14bb7efa7SGarrett D'Amore /*
24bb7efa7SGarrett D'Amore * CDDL HEADER START
34bb7efa7SGarrett D'Amore *
44bb7efa7SGarrett D'Amore * The contents of this file are subject to the terms of the
54bb7efa7SGarrett D'Amore * Common Development and Distribution License (the "License").
64bb7efa7SGarrett D'Amore * You may not use this file except in compliance with the License.
74bb7efa7SGarrett D'Amore *
84bb7efa7SGarrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94bb7efa7SGarrett D'Amore * or http://www.opensolaris.org/os/licensing.
104bb7efa7SGarrett D'Amore * See the License for the specific language governing permissions
114bb7efa7SGarrett D'Amore * and limitations under the License.
124bb7efa7SGarrett D'Amore *
134bb7efa7SGarrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
144bb7efa7SGarrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154bb7efa7SGarrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
164bb7efa7SGarrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
174bb7efa7SGarrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
184bb7efa7SGarrett D'Amore *
194bb7efa7SGarrett D'Amore * CDDL HEADER END
204bb7efa7SGarrett D'Amore */
214bb7efa7SGarrett D'Amore /*
22*3f7d54a6SGarrett D'Amore * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
234bb7efa7SGarrett D'Amore */
244bb7efa7SGarrett D'Amore
254bb7efa7SGarrett D'Amore #include "sdhost.h"
264bb7efa7SGarrett D'Amore
279a5113a6SGarrett D'Amore typedef struct sdstats sdstats_t;
284bb7efa7SGarrett D'Amore typedef struct sdslot sdslot_t;
294bb7efa7SGarrett D'Amore typedef struct sdhost sdhost_t;
304bb7efa7SGarrett D'Amore
319a5113a6SGarrett D'Amore struct sdstats {
329a5113a6SGarrett D'Amore kstat_named_t ks_ncmd;
339a5113a6SGarrett D'Amore kstat_named_t ks_ixfr;
349a5113a6SGarrett D'Amore kstat_named_t ks_oxfr;
359a5113a6SGarrett D'Amore kstat_named_t ks_ibytes;
369a5113a6SGarrett D'Amore kstat_named_t ks_obytes;
379a5113a6SGarrett D'Amore kstat_named_t ks_npio;
389a5113a6SGarrett D'Amore kstat_named_t ks_ndma;
399a5113a6SGarrett D'Amore kstat_named_t ks_nmulti;
409a5113a6SGarrett D'Amore kstat_named_t ks_baseclk;
419a5113a6SGarrett D'Amore kstat_named_t ks_cardclk;
429a5113a6SGarrett D'Amore kstat_named_t ks_tmusecs;
439a5113a6SGarrett D'Amore kstat_named_t ks_width;
449a5113a6SGarrett D'Amore kstat_named_t ks_flags;
459a5113a6SGarrett D'Amore kstat_named_t ks_capab;
469a5113a6SGarrett D'Amore };
479a5113a6SGarrett D'Amore
489a5113a6SGarrett D'Amore #define SDFLAG_FORCE_PIO (1U << 0)
499a5113a6SGarrett D'Amore #define SDFLAG_FORCE_DMA (1U << 1)
509a5113a6SGarrett D'Amore
514bb7efa7SGarrett D'Amore /*
524bb7efa7SGarrett D'Amore * Per slot state.
534bb7efa7SGarrett D'Amore */
544bb7efa7SGarrett D'Amore struct sdslot {
554bb7efa7SGarrett D'Amore sda_host_t *ss_host;
564bb7efa7SGarrett D'Amore int ss_num;
574bb7efa7SGarrett D'Amore ddi_acc_handle_t ss_acch;
584bb7efa7SGarrett D'Amore caddr_t ss_regva;
594bb7efa7SGarrett D'Amore kmutex_t ss_lock;
604bb7efa7SGarrett D'Amore uint8_t ss_tmoutclk;
614bb7efa7SGarrett D'Amore uint32_t ss_ocr; /* OCR formatted voltages */
624bb7efa7SGarrett D'Amore uint16_t ss_mode;
634bb7efa7SGarrett D'Amore boolean_t ss_suspended;
649a5113a6SGarrett D'Amore sdstats_t ss_stats;
659a5113a6SGarrett D'Amore #define ss_ncmd ss_stats.ks_ncmd.value.ui64
669a5113a6SGarrett D'Amore #define ss_ixfr ss_stats.ks_ixfr.value.ui64
679a5113a6SGarrett D'Amore #define ss_oxfr ss_stats.ks_oxfr.value.ui64
689a5113a6SGarrett D'Amore #define ss_ibytes ss_stats.ks_ibytes.value.ui64
699a5113a6SGarrett D'Amore #define ss_obytes ss_stats.ks_obytes.value.ui64
709a5113a6SGarrett D'Amore #define ss_ndma ss_stats.ks_ndma.value.ui64
719a5113a6SGarrett D'Amore #define ss_npio ss_stats.ks_npio.value.ui64
729a5113a6SGarrett D'Amore #define ss_nmulti ss_stats.ks_nmulti.value.ui64
739a5113a6SGarrett D'Amore
749a5113a6SGarrett D'Amore #define ss_baseclk ss_stats.ks_baseclk.value.ui32
759a5113a6SGarrett D'Amore #define ss_cardclk ss_stats.ks_cardclk.value.ui32
769a5113a6SGarrett D'Amore #define ss_tmusecs ss_stats.ks_tmusecs.value.ui32
779a5113a6SGarrett D'Amore #define ss_width ss_stats.ks_width.value.ui32
789a5113a6SGarrett D'Amore #define ss_flags ss_stats.ks_flags.value.ui32
799a5113a6SGarrett D'Amore #define ss_capab ss_stats.ks_capab.value.ui32
809a5113a6SGarrett D'Amore kstat_t *ss_ksp;
814bb7efa7SGarrett D'Amore
824bb7efa7SGarrett D'Amore /*
834bb7efa7SGarrett D'Amore * Command in progress
844bb7efa7SGarrett D'Amore */
854bb7efa7SGarrett D'Amore uint8_t *ss_kvaddr;
864bb7efa7SGarrett D'Amore int ss_blksz;
874bb7efa7SGarrett D'Amore uint16_t ss_resid; /* in blocks */
889a5113a6SGarrett D'Amore int ss_rcnt;
894bb7efa7SGarrett D'Amore
904bb7efa7SGarrett D'Amore /* scratch buffer, to receive extra PIO data */
919a5113a6SGarrett D'Amore caddr_t ss_bounce;
929a5113a6SGarrett D'Amore ddi_dma_handle_t ss_bufdmah;
939a5113a6SGarrett D'Amore ddi_acc_handle_t ss_bufacch;
949a5113a6SGarrett D'Amore ddi_dma_cookie_t ss_bufdmac;
954bb7efa7SGarrett D'Amore };
964bb7efa7SGarrett D'Amore
979a5113a6SGarrett D'Amore /*
989a5113a6SGarrett D'Amore * This allocates a rather large chunk of contiguous memory for DMA.
999a5113a6SGarrett D'Amore * But doing so means that we'll almost never have to resort to PIO.
1009a5113a6SGarrett D'Amore */
1019a5113a6SGarrett D'Amore #define SDHOST_BOUNCESZ 65536
1029a5113a6SGarrett D'Amore
1034bb7efa7SGarrett D'Amore /*
1044bb7efa7SGarrett D'Amore * Per controller state.
1054bb7efa7SGarrett D'Amore */
1064bb7efa7SGarrett D'Amore struct sdhost {
1074bb7efa7SGarrett D'Amore int sh_numslots;
1084bb7efa7SGarrett D'Amore ddi_dma_attr_t sh_dmaattr;
1094bb7efa7SGarrett D'Amore sdslot_t sh_slots[SDHOST_MAXSLOTS];
1104bb7efa7SGarrett D'Amore sda_host_t *sh_host;
1114bb7efa7SGarrett D'Amore
1124bb7efa7SGarrett D'Amore /*
1134bb7efa7SGarrett D'Amore * Interrupt related information.
1144bb7efa7SGarrett D'Amore */
1154bb7efa7SGarrett D'Amore ddi_intr_handle_t sh_ihandle;
1164bb7efa7SGarrett D'Amore int sh_icap;
1174bb7efa7SGarrett D'Amore uint_t sh_ipri;
1184bb7efa7SGarrett D'Amore };
1194bb7efa7SGarrett D'Amore
1209a5113a6SGarrett D'Amore #define PROPSET(x) \
1219a5113a6SGarrett D'Amore (ddi_prop_get_int(DDI_DEV_T_ANY, dip, \
1229a5113a6SGarrett D'Amore DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, x, 0) != 0)
1239a5113a6SGarrett D'Amore
1244bb7efa7SGarrett D'Amore
1254bb7efa7SGarrett D'Amore static int sdhost_attach(dev_info_t *, ddi_attach_cmd_t);
1264bb7efa7SGarrett D'Amore static int sdhost_detach(dev_info_t *, ddi_detach_cmd_t);
127f2b90c3cSGarrett D'Amore static int sdhost_quiesce(dev_info_t *);
1284bb7efa7SGarrett D'Amore static int sdhost_suspend(dev_info_t *);
1294bb7efa7SGarrett D'Amore static int sdhost_resume(dev_info_t *);
1304bb7efa7SGarrett D'Amore
1314bb7efa7SGarrett D'Amore static void sdhost_enable_interrupts(sdslot_t *);
1324bb7efa7SGarrett D'Amore static void sdhost_disable_interrupts(sdslot_t *);
1334bb7efa7SGarrett D'Amore static int sdhost_setup_intr(dev_info_t *, sdhost_t *);
1344bb7efa7SGarrett D'Amore static uint_t sdhost_intr(caddr_t, caddr_t);
1354bb7efa7SGarrett D'Amore static int sdhost_init_slot(dev_info_t *, sdhost_t *, int, int);
1364bb7efa7SGarrett D'Amore static void sdhost_uninit_slot(sdhost_t *, int);
1374bb7efa7SGarrett D'Amore static sda_err_t sdhost_soft_reset(sdslot_t *, uint8_t);
1384bb7efa7SGarrett D'Amore static sda_err_t sdhost_set_clock(sdslot_t *, uint32_t);
1394bb7efa7SGarrett D'Amore static void sdhost_xfer_done(sdslot_t *, sda_err_t);
1404bb7efa7SGarrett D'Amore static sda_err_t sdhost_wait_cmd(sdslot_t *, sda_cmd_t *);
1414bb7efa7SGarrett D'Amore static uint_t sdhost_slot_intr(sdslot_t *);
1424bb7efa7SGarrett D'Amore
1434bb7efa7SGarrett D'Amore static sda_err_t sdhost_cmd(void *, sda_cmd_t *);
1444bb7efa7SGarrett D'Amore static sda_err_t sdhost_getprop(void *, sda_prop_t, uint32_t *);
1454bb7efa7SGarrett D'Amore static sda_err_t sdhost_setprop(void *, sda_prop_t, uint32_t);
1464bb7efa7SGarrett D'Amore static sda_err_t sdhost_poll(void *);
1474bb7efa7SGarrett D'Amore static sda_err_t sdhost_reset(void *);
1484bb7efa7SGarrett D'Amore static sda_err_t sdhost_halt(void *);
1494bb7efa7SGarrett D'Amore
1504bb7efa7SGarrett D'Amore static struct dev_ops sdhost_dev_ops = {
1514bb7efa7SGarrett D'Amore DEVO_REV, /* devo_rev */
1524bb7efa7SGarrett D'Amore 0, /* devo_refcnt */
1534bb7efa7SGarrett D'Amore ddi_no_info, /* devo_getinfo */
1544bb7efa7SGarrett D'Amore nulldev, /* devo_identify */
1554bb7efa7SGarrett D'Amore nulldev, /* devo_probe */
1564bb7efa7SGarrett D'Amore sdhost_attach, /* devo_attach */
1574bb7efa7SGarrett D'Amore sdhost_detach, /* devo_detach */
1584bb7efa7SGarrett D'Amore nodev, /* devo_reset */
1594bb7efa7SGarrett D'Amore NULL, /* devo_cb_ops */
1604bb7efa7SGarrett D'Amore NULL, /* devo_bus_ops */
16119397407SSherry Moore NULL, /* devo_power */
162f2b90c3cSGarrett D'Amore sdhost_quiesce, /* devo_quiesce */
1634bb7efa7SGarrett D'Amore };
1644bb7efa7SGarrett D'Amore
1654bb7efa7SGarrett D'Amore static struct modldrv sdhost_modldrv = {
1664bb7efa7SGarrett D'Amore &mod_driverops, /* drv_modops */
1674bb7efa7SGarrett D'Amore "Standard SD Host Controller", /* drv_linkinfo */
1684bb7efa7SGarrett D'Amore &sdhost_dev_ops /* drv_dev_ops */
1694bb7efa7SGarrett D'Amore };
1704bb7efa7SGarrett D'Amore
1714bb7efa7SGarrett D'Amore static struct modlinkage modlinkage = {
1724bb7efa7SGarrett D'Amore MODREV_1, /* ml_rev */
1734bb7efa7SGarrett D'Amore { &sdhost_modldrv, NULL } /* ml_linkage */
1744bb7efa7SGarrett D'Amore };
1754bb7efa7SGarrett D'Amore
1764bb7efa7SGarrett D'Amore static struct sda_ops sdhost_ops = {
1774bb7efa7SGarrett D'Amore SDA_OPS_VERSION,
1784bb7efa7SGarrett D'Amore sdhost_cmd, /* so_cmd */
1794bb7efa7SGarrett D'Amore sdhost_getprop, /* so_getprop */
1804bb7efa7SGarrett D'Amore sdhost_setprop, /* so_setprop */
1814bb7efa7SGarrett D'Amore sdhost_poll, /* so_poll */
1824bb7efa7SGarrett D'Amore sdhost_reset, /* so_reset */
1834bb7efa7SGarrett D'Amore sdhost_halt, /* so_halt */
1844bb7efa7SGarrett D'Amore };
1854bb7efa7SGarrett D'Amore
1864bb7efa7SGarrett D'Amore static ddi_device_acc_attr_t sdhost_regattr = {
1874bb7efa7SGarrett D'Amore DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
1884bb7efa7SGarrett D'Amore DDI_STRUCTURE_LE_ACC, /* devacc_attr_endian_flags */
1894bb7efa7SGarrett D'Amore DDI_STRICTORDER_ACC, /* devacc_attr_dataorder */
1904bb7efa7SGarrett D'Amore DDI_DEFAULT_ACC, /* devacc_attr_access */
1914bb7efa7SGarrett D'Amore };
1929a5113a6SGarrett D'Amore static ddi_device_acc_attr_t sdhost_bufattr = {
1939a5113a6SGarrett D'Amore DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
1949a5113a6SGarrett D'Amore DDI_NEVERSWAP_ACC, /* devacc_attr_endian_flags */
1959a5113a6SGarrett D'Amore DDI_STRICTORDER_ACC, /* devacc_attr_dataorder */
1969a5113a6SGarrett D'Amore DDI_DEFAULT_ACC, /* devacc_attr_access */
1979a5113a6SGarrett D'Amore };
1984bb7efa7SGarrett D'Amore
1994bb7efa7SGarrett D'Amore #define GET16(ss, reg) \
2004bb7efa7SGarrett D'Amore ddi_get16(ss->ss_acch, (void *)(ss->ss_regva + reg))
2014bb7efa7SGarrett D'Amore #define PUT16(ss, reg, val) \
2024bb7efa7SGarrett D'Amore ddi_put16(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2034bb7efa7SGarrett D'Amore #define GET32(ss, reg) \
2044bb7efa7SGarrett D'Amore ddi_get32(ss->ss_acch, (void *)(ss->ss_regva + reg))
2054bb7efa7SGarrett D'Amore #define PUT32(ss, reg, val) \
2064bb7efa7SGarrett D'Amore ddi_put32(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2074bb7efa7SGarrett D'Amore #define GET64(ss, reg) \
2084bb7efa7SGarrett D'Amore ddi_get64(ss->ss_acch, (void *)(ss->ss_regva + reg))
2094bb7efa7SGarrett D'Amore
2104bb7efa7SGarrett D'Amore #define GET8(ss, reg) \
2114bb7efa7SGarrett D'Amore ddi_get8(ss->ss_acch, (void *)(ss->ss_regva + reg))
2124bb7efa7SGarrett D'Amore #define PUT8(ss, reg, val) \
2134bb7efa7SGarrett D'Amore ddi_put8(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2144bb7efa7SGarrett D'Amore
2154bb7efa7SGarrett D'Amore #define CLR8(ss, reg, mask) PUT8(ss, reg, GET8(ss, reg) & ~(mask))
2164bb7efa7SGarrett D'Amore #define SET8(ss, reg, mask) PUT8(ss, reg, GET8(ss, reg) | (mask))
2174bb7efa7SGarrett D'Amore
2184bb7efa7SGarrett D'Amore /*
2194bb7efa7SGarrett D'Amore * If ever anyone uses PIO on SPARC, we have to endian-swap. But we
2204bb7efa7SGarrett D'Amore * think that SD Host Controllers are likely to be uncommon on SPARC,
2214bb7efa7SGarrett D'Amore * and hopefully when they exist at all they will be able to use DMA.
2224bb7efa7SGarrett D'Amore */
2234bb7efa7SGarrett D'Amore #ifdef _BIG_ENDIAN
2244bb7efa7SGarrett D'Amore #define sw32(x) ddi_swap32(x)
2254bb7efa7SGarrett D'Amore #define sw16(x) ddi_swap16(x)
2264bb7efa7SGarrett D'Amore #else
2274bb7efa7SGarrett D'Amore #define sw32(x) (x)
2284bb7efa7SGarrett D'Amore #define sw16(x) (x)
2294bb7efa7SGarrett D'Amore #endif
2304bb7efa7SGarrett D'Amore
2314bb7efa7SGarrett D'Amore #define GETDATA32(ss) sw32(GET32(ss, REG_DATA))
2324bb7efa7SGarrett D'Amore #define GETDATA16(ss) sw16(GET16(ss, REG_DATA))
2334bb7efa7SGarrett D'Amore #define GETDATA8(ss) GET8(ss, REG_DATA)
2344bb7efa7SGarrett D'Amore
2354bb7efa7SGarrett D'Amore #define PUTDATA32(ss, val) PUT32(ss, REG_DATA, sw32(val))
2364bb7efa7SGarrett D'Amore #define PUTDATA16(ss, val) PUT16(ss, REG_DATA, sw16(val))
2374bb7efa7SGarrett D'Amore #define PUTDATA8(ss, val) PUT8(ss, REG_DATA, val)
2384bb7efa7SGarrett D'Amore
2394bb7efa7SGarrett D'Amore #define CHECK_STATE(ss, nm) \
2404bb7efa7SGarrett D'Amore ((GET32(ss, REG_PRS) & PRS_ ## nm) != 0)
2414bb7efa7SGarrett D'Amore
2424bb7efa7SGarrett D'Amore int
_init(void)2434bb7efa7SGarrett D'Amore _init(void)
2444bb7efa7SGarrett D'Amore {
2454bb7efa7SGarrett D'Amore int rv;
2464bb7efa7SGarrett D'Amore
2474bb7efa7SGarrett D'Amore sda_host_init_ops(&sdhost_dev_ops);
2484bb7efa7SGarrett D'Amore
2494bb7efa7SGarrett D'Amore if ((rv = mod_install(&modlinkage)) != 0) {
2504bb7efa7SGarrett D'Amore sda_host_fini_ops(&sdhost_dev_ops);
2514bb7efa7SGarrett D'Amore }
2524bb7efa7SGarrett D'Amore
2534bb7efa7SGarrett D'Amore return (rv);
2544bb7efa7SGarrett D'Amore }
2554bb7efa7SGarrett D'Amore
2564bb7efa7SGarrett D'Amore int
_fini(void)2574bb7efa7SGarrett D'Amore _fini(void)
2584bb7efa7SGarrett D'Amore {
2594bb7efa7SGarrett D'Amore int rv;
2604bb7efa7SGarrett D'Amore
2614bb7efa7SGarrett D'Amore if ((rv = mod_remove(&modlinkage)) == 0) {
2624bb7efa7SGarrett D'Amore sda_host_fini_ops(&sdhost_dev_ops);
2634bb7efa7SGarrett D'Amore }
2644bb7efa7SGarrett D'Amore return (rv);
2654bb7efa7SGarrett D'Amore }
2664bb7efa7SGarrett D'Amore
2674bb7efa7SGarrett D'Amore int
_info(struct modinfo * modinfop)2684bb7efa7SGarrett D'Amore _info(struct modinfo *modinfop)
2694bb7efa7SGarrett D'Amore {
2704bb7efa7SGarrett D'Amore return (mod_info(&modlinkage, modinfop));
2714bb7efa7SGarrett D'Amore }
2724bb7efa7SGarrett D'Amore
2734bb7efa7SGarrett D'Amore int
sdhost_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2744bb7efa7SGarrett D'Amore sdhost_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2754bb7efa7SGarrett D'Amore {
2764bb7efa7SGarrett D'Amore sdhost_t *shp;
2774bb7efa7SGarrett D'Amore ddi_acc_handle_t pcih;
2784bb7efa7SGarrett D'Amore uint8_t slotinfo;
2794bb7efa7SGarrett D'Amore uint8_t bar;
2804bb7efa7SGarrett D'Amore int i;
281f2b90c3cSGarrett D'Amore int rv;
2824bb7efa7SGarrett D'Amore
2834bb7efa7SGarrett D'Amore switch (cmd) {
2844bb7efa7SGarrett D'Amore case DDI_ATTACH:
2854bb7efa7SGarrett D'Amore break;
2864bb7efa7SGarrett D'Amore
2874bb7efa7SGarrett D'Amore case DDI_RESUME:
2884bb7efa7SGarrett D'Amore return (sdhost_resume(dip));
2894bb7efa7SGarrett D'Amore
2904bb7efa7SGarrett D'Amore default:
2914bb7efa7SGarrett D'Amore return (DDI_FAILURE);
2924bb7efa7SGarrett D'Amore }
2934bb7efa7SGarrett D'Amore
2944bb7efa7SGarrett D'Amore /*
2954bb7efa7SGarrett D'Amore * Soft state allocation.
2964bb7efa7SGarrett D'Amore */
2974bb7efa7SGarrett D'Amore shp = kmem_zalloc(sizeof (*shp), KM_SLEEP);
2984bb7efa7SGarrett D'Amore ddi_set_driver_private(dip, shp);
2994bb7efa7SGarrett D'Amore
300c8ca7eb8SGarrett D'Amore /*
301c8ca7eb8SGarrett D'Amore * Reset the "slot number", so uninit slot works properly.
302c8ca7eb8SGarrett D'Amore */
303c8ca7eb8SGarrett D'Amore for (i = 0; i < SDHOST_MAXSLOTS; i++) {
304c8ca7eb8SGarrett D'Amore shp->sh_slots[i].ss_num = -1;
305c8ca7eb8SGarrett D'Amore }
306c8ca7eb8SGarrett D'Amore
3074bb7efa7SGarrett D'Amore /*
3084bb7efa7SGarrett D'Amore * Initialize DMA attributes. For now we initialize as for
3094bb7efa7SGarrett D'Amore * SDMA. If we add ADMA support we can improve this.
3104bb7efa7SGarrett D'Amore */
3114bb7efa7SGarrett D'Amore shp->sh_dmaattr.dma_attr_version = DMA_ATTR_V0;
3124bb7efa7SGarrett D'Amore shp->sh_dmaattr.dma_attr_addr_lo = 0;
3134bb7efa7SGarrett D'Amore shp->sh_dmaattr.dma_attr_addr_hi = 0xffffffffU;
3144bb7efa7SGarrett D'Amore shp->sh_dmaattr.dma_attr_count_max = 0xffffffffU;
3159a5113a6SGarrett D'Amore shp->sh_dmaattr.dma_attr_align = 4096; /* Ricoh needs it */
3164bb7efa7SGarrett D'Amore shp->sh_dmaattr.dma_attr_burstsizes = 0; /* for now! */
3174bb7efa7SGarrett D'Amore shp->sh_dmaattr.dma_attr_minxfer = 1;
3189a5113a6SGarrett D'Amore shp->sh_dmaattr.dma_attr_maxxfer = 0x7ffffU;
3199a5113a6SGarrett D'Amore shp->sh_dmaattr.dma_attr_sgllen = 1; /* no scatter/gather */
3209a5113a6SGarrett D'Amore shp->sh_dmaattr.dma_attr_seg = 0x7ffffU; /* not to cross 512K */
3214bb7efa7SGarrett D'Amore shp->sh_dmaattr.dma_attr_granular = 1;
3224bb7efa7SGarrett D'Amore shp->sh_dmaattr.dma_attr_flags = 0;
3234bb7efa7SGarrett D'Amore
3244bb7efa7SGarrett D'Amore /*
3254bb7efa7SGarrett D'Amore * PCI configuration access to figure out number of slots present.
3264bb7efa7SGarrett D'Amore */
3274bb7efa7SGarrett D'Amore if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
3284bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "pci_config_setup failed");
3294bb7efa7SGarrett D'Amore goto failed;
3304bb7efa7SGarrett D'Amore }
3314bb7efa7SGarrett D'Amore
3324bb7efa7SGarrett D'Amore slotinfo = pci_config_get8(pcih, SLOTINFO);
3334bb7efa7SGarrett D'Amore shp->sh_numslots = SLOTINFO_NSLOT(slotinfo);
3344bb7efa7SGarrett D'Amore
3354bb7efa7SGarrett D'Amore if (shp->sh_numslots > SDHOST_MAXSLOTS) {
3364bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "Host reports to have too many slots: %d",
3374bb7efa7SGarrett D'Amore shp->sh_numslots);
338c8ca7eb8SGarrett D'Amore pci_config_teardown(&pcih);
3394bb7efa7SGarrett D'Amore goto failed;
3404bb7efa7SGarrett D'Amore }
3414bb7efa7SGarrett D'Amore
3424bb7efa7SGarrett D'Amore /*
3434bb7efa7SGarrett D'Amore * Enable master accesses and DMA.
3444bb7efa7SGarrett D'Amore */
3454bb7efa7SGarrett D'Amore pci_config_put16(pcih, PCI_CONF_COMM,
3464bb7efa7SGarrett D'Amore pci_config_get16(pcih, PCI_CONF_COMM) |
3474bb7efa7SGarrett D'Amore PCI_COMM_MAE | PCI_COMM_ME);
3484bb7efa7SGarrett D'Amore
3494bb7efa7SGarrett D'Amore /*
3504bb7efa7SGarrett D'Amore * Figure out which BAR to use. Note that we number BARs from
3514bb7efa7SGarrett D'Amore * 1, although PCI and SD Host numbers from 0. (We number
3524bb7efa7SGarrett D'Amore * from 1, because register number 0 means PCI configuration
3534bb7efa7SGarrett D'Amore * space in Solaris.)
3544bb7efa7SGarrett D'Amore */
3554bb7efa7SGarrett D'Amore bar = SLOTINFO_BAR(slotinfo) + 1;
3564bb7efa7SGarrett D'Amore
3574bb7efa7SGarrett D'Amore pci_config_teardown(&pcih);
3584bb7efa7SGarrett D'Amore
3594bb7efa7SGarrett D'Amore /*
3604bb7efa7SGarrett D'Amore * Setup interrupts ... supports the new DDI interrupt API. This
3614bb7efa7SGarrett D'Amore * will support MSI or MSI-X interrupts if a device is found to
3624bb7efa7SGarrett D'Amore * support it.
3634bb7efa7SGarrett D'Amore */
3644bb7efa7SGarrett D'Amore if (sdhost_setup_intr(dip, shp) != DDI_SUCCESS) {
3654bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "Failed to setup interrupts");
3664bb7efa7SGarrett D'Amore goto failed;
3674bb7efa7SGarrett D'Amore }
3684bb7efa7SGarrett D'Amore
3694bb7efa7SGarrett D'Amore shp->sh_host = sda_host_alloc(dip, shp->sh_numslots, &sdhost_ops,
370*3f7d54a6SGarrett D'Amore NULL);
3714bb7efa7SGarrett D'Amore if (shp->sh_host == NULL) {
3724bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "Failed allocating SD host structure");
3734bb7efa7SGarrett D'Amore goto failed;
3744bb7efa7SGarrett D'Amore }
3754bb7efa7SGarrett D'Amore
3764bb7efa7SGarrett D'Amore /*
3774bb7efa7SGarrett D'Amore * Configure slots, this also maps registers, enables
3784bb7efa7SGarrett D'Amore * interrupts, etc. Most of the hardware setup is done here.
3794bb7efa7SGarrett D'Amore */
3804bb7efa7SGarrett D'Amore for (i = 0; i < shp->sh_numslots; i++) {
3814bb7efa7SGarrett D'Amore if (sdhost_init_slot(dip, shp, i, bar + i) != DDI_SUCCESS) {
3824bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "Failed initializing slot %d", i);
3834bb7efa7SGarrett D'Amore goto failed;
3844bb7efa7SGarrett D'Amore }
3854bb7efa7SGarrett D'Amore }
3864bb7efa7SGarrett D'Amore
3874bb7efa7SGarrett D'Amore ddi_report_dev(dip);
3884bb7efa7SGarrett D'Amore
3894bb7efa7SGarrett D'Amore /*
3904bb7efa7SGarrett D'Amore * Enable device interrupts at the DDI layer.
3914bb7efa7SGarrett D'Amore */
392f2b90c3cSGarrett D'Amore if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
393f2b90c3cSGarrett D'Amore rv = ddi_intr_block_enable(&shp->sh_ihandle, 1);
394f2b90c3cSGarrett D'Amore } else {
395f2b90c3cSGarrett D'Amore rv = ddi_intr_enable(shp->sh_ihandle);
396f2b90c3cSGarrett D'Amore }
397f2b90c3cSGarrett D'Amore if (rv != DDI_SUCCESS) {
398f2b90c3cSGarrett D'Amore cmn_err(CE_WARN, "Failed enabling interrupts");
399f2b90c3cSGarrett D'Amore goto failed;
400f2b90c3cSGarrett D'Amore }
4014bb7efa7SGarrett D'Amore
4024bb7efa7SGarrett D'Amore /*
4034bb7efa7SGarrett D'Amore * Mark the slots online with the framework. This will cause
4044bb7efa7SGarrett D'Amore * the framework to probe them for the presence of cards.
4054bb7efa7SGarrett D'Amore */
4064bb7efa7SGarrett D'Amore if (sda_host_attach(shp->sh_host) != DDI_SUCCESS) {
4074bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "Failed attaching to SDA framework");
408f2b90c3cSGarrett D'Amore if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
409f2b90c3cSGarrett D'Amore (void) ddi_intr_block_disable(&shp->sh_ihandle, 1);
410f2b90c3cSGarrett D'Amore } else {
411f2b90c3cSGarrett D'Amore (void) ddi_intr_disable(shp->sh_ihandle);
412f2b90c3cSGarrett D'Amore }
4134bb7efa7SGarrett D'Amore goto failed;
4144bb7efa7SGarrett D'Amore }
4154bb7efa7SGarrett D'Amore
4164bb7efa7SGarrett D'Amore return (DDI_SUCCESS);
4174bb7efa7SGarrett D'Amore
4184bb7efa7SGarrett D'Amore failed:
4194bb7efa7SGarrett D'Amore if (shp->sh_ihandle != NULL) {
4204bb7efa7SGarrett D'Amore (void) ddi_intr_remove_handler(shp->sh_ihandle);
4214bb7efa7SGarrett D'Amore (void) ddi_intr_free(shp->sh_ihandle);
4224bb7efa7SGarrett D'Amore }
4234bb7efa7SGarrett D'Amore for (i = 0; i < shp->sh_numslots; i++)
4244bb7efa7SGarrett D'Amore sdhost_uninit_slot(shp, i);
425c8ca7eb8SGarrett D'Amore if (shp->sh_host != NULL)
426c8ca7eb8SGarrett D'Amore sda_host_free(shp->sh_host);
4274bb7efa7SGarrett D'Amore kmem_free(shp, sizeof (*shp));
4284bb7efa7SGarrett D'Amore
4294bb7efa7SGarrett D'Amore return (DDI_FAILURE);
4304bb7efa7SGarrett D'Amore }
4314bb7efa7SGarrett D'Amore
4324bb7efa7SGarrett D'Amore int
sdhost_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4334bb7efa7SGarrett D'Amore sdhost_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4344bb7efa7SGarrett D'Amore {
4354bb7efa7SGarrett D'Amore sdhost_t *shp;
4364bb7efa7SGarrett D'Amore int i;
4374bb7efa7SGarrett D'Amore
4384bb7efa7SGarrett D'Amore switch (cmd) {
4394bb7efa7SGarrett D'Amore case DDI_DETACH:
4404bb7efa7SGarrett D'Amore break;
4414bb7efa7SGarrett D'Amore
4424bb7efa7SGarrett D'Amore case DDI_SUSPEND:
4434bb7efa7SGarrett D'Amore return (sdhost_suspend(dip));
4444bb7efa7SGarrett D'Amore
4454bb7efa7SGarrett D'Amore default:
4464bb7efa7SGarrett D'Amore return (DDI_FAILURE);
4474bb7efa7SGarrett D'Amore }
4484bb7efa7SGarrett D'Amore
4494bb7efa7SGarrett D'Amore shp = ddi_get_driver_private(dip);
4504bb7efa7SGarrett D'Amore
4514bb7efa7SGarrett D'Amore /*
4524bb7efa7SGarrett D'Amore * Take host offline with the framework.
4534bb7efa7SGarrett D'Amore */
4544bb7efa7SGarrett D'Amore sda_host_detach(shp->sh_host);
4554bb7efa7SGarrett D'Amore
4564bb7efa7SGarrett D'Amore /*
4574bb7efa7SGarrett D'Amore * Tear down interrupts.
4584bb7efa7SGarrett D'Amore */
4594bb7efa7SGarrett D'Amore if (shp->sh_ihandle != NULL) {
460f2b90c3cSGarrett D'Amore if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
461f2b90c3cSGarrett D'Amore (void) ddi_intr_block_disable(&shp->sh_ihandle, 1);
462f2b90c3cSGarrett D'Amore } else {
463f2b90c3cSGarrett D'Amore (void) ddi_intr_disable(shp->sh_ihandle);
464f2b90c3cSGarrett D'Amore }
4654bb7efa7SGarrett D'Amore (void) ddi_intr_remove_handler(shp->sh_ihandle);
4664bb7efa7SGarrett D'Amore (void) ddi_intr_free(shp->sh_ihandle);
4674bb7efa7SGarrett D'Amore }
4684bb7efa7SGarrett D'Amore
4694bb7efa7SGarrett D'Amore /*
4704bb7efa7SGarrett D'Amore * Tear down register mappings, etc.
4714bb7efa7SGarrett D'Amore */
4724bb7efa7SGarrett D'Amore for (i = 0; i < shp->sh_numslots; i++)
4734bb7efa7SGarrett D'Amore sdhost_uninit_slot(shp, i);
474c8ca7eb8SGarrett D'Amore sda_host_free(shp->sh_host);
4754bb7efa7SGarrett D'Amore kmem_free(shp, sizeof (*shp));
4764bb7efa7SGarrett D'Amore
4774bb7efa7SGarrett D'Amore return (DDI_SUCCESS);
4784bb7efa7SGarrett D'Amore }
4794bb7efa7SGarrett D'Amore
480f2b90c3cSGarrett D'Amore int
sdhost_quiesce(dev_info_t * dip)481f2b90c3cSGarrett D'Amore sdhost_quiesce(dev_info_t *dip)
482f2b90c3cSGarrett D'Amore {
483f2b90c3cSGarrett D'Amore sdhost_t *shp;
484f2b90c3cSGarrett D'Amore sdslot_t *ss;
485f2b90c3cSGarrett D'Amore
486f2b90c3cSGarrett D'Amore shp = ddi_get_driver_private(dip);
487f2b90c3cSGarrett D'Amore
488f2b90c3cSGarrett D'Amore /* reset each slot separately */
489f2b90c3cSGarrett D'Amore for (int i = 0; i < shp->sh_numslots; i++) {
490f2b90c3cSGarrett D'Amore ss = &shp->sh_slots[i];
491f2b90c3cSGarrett D'Amore if (ss->ss_acch == NULL)
492f2b90c3cSGarrett D'Amore continue;
493f2b90c3cSGarrett D'Amore
494f2b90c3cSGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
495f2b90c3cSGarrett D'Amore }
496f2b90c3cSGarrett D'Amore return (DDI_SUCCESS);
497f2b90c3cSGarrett D'Amore }
498f2b90c3cSGarrett D'Amore
4994bb7efa7SGarrett D'Amore int
sdhost_suspend(dev_info_t * dip)5004bb7efa7SGarrett D'Amore sdhost_suspend(dev_info_t *dip)
5014bb7efa7SGarrett D'Amore {
5024bb7efa7SGarrett D'Amore sdhost_t *shp;
5034bb7efa7SGarrett D'Amore sdslot_t *ss;
5044bb7efa7SGarrett D'Amore int i;
5054bb7efa7SGarrett D'Amore
5064bb7efa7SGarrett D'Amore shp = ddi_get_driver_private(dip);
5074bb7efa7SGarrett D'Amore
508f2b90c3cSGarrett D'Amore sda_host_suspend(shp->sh_host);
5094bb7efa7SGarrett D'Amore
5104bb7efa7SGarrett D'Amore for (i = 0; i < shp->sh_numslots; i++) {
5114bb7efa7SGarrett D'Amore ss = &shp->sh_slots[i];
5124bb7efa7SGarrett D'Amore mutex_enter(&ss->ss_lock);
5134bb7efa7SGarrett D'Amore ss->ss_suspended = B_TRUE;
5144bb7efa7SGarrett D'Amore sdhost_disable_interrupts(ss);
5154bb7efa7SGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
5164bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
5174bb7efa7SGarrett D'Amore }
5184bb7efa7SGarrett D'Amore return (DDI_SUCCESS);
5194bb7efa7SGarrett D'Amore }
5204bb7efa7SGarrett D'Amore
5214bb7efa7SGarrett D'Amore int
sdhost_resume(dev_info_t * dip)5224bb7efa7SGarrett D'Amore sdhost_resume(dev_info_t *dip)
5234bb7efa7SGarrett D'Amore {
5244bb7efa7SGarrett D'Amore sdhost_t *shp;
5254bb7efa7SGarrett D'Amore sdslot_t *ss;
5264bb7efa7SGarrett D'Amore int i;
5274bb7efa7SGarrett D'Amore
5284bb7efa7SGarrett D'Amore shp = ddi_get_driver_private(dip);
5294bb7efa7SGarrett D'Amore
5304bb7efa7SGarrett D'Amore for (i = 0; i < shp->sh_numslots; i++) {
5314bb7efa7SGarrett D'Amore ss = &shp->sh_slots[i];
5324bb7efa7SGarrett D'Amore mutex_enter(&ss->ss_lock);
5334bb7efa7SGarrett D'Amore ss->ss_suspended = B_FALSE;
5344bb7efa7SGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
5354bb7efa7SGarrett D'Amore sdhost_enable_interrupts(ss);
5364bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
5374bb7efa7SGarrett D'Amore }
5384bb7efa7SGarrett D'Amore
539f2b90c3cSGarrett D'Amore sda_host_resume(shp->sh_host);
5404bb7efa7SGarrett D'Amore
5414bb7efa7SGarrett D'Amore return (DDI_SUCCESS);
5424bb7efa7SGarrett D'Amore }
5434bb7efa7SGarrett D'Amore
5444bb7efa7SGarrett D'Amore sda_err_t
sdhost_set_clock(sdslot_t * ss,uint32_t hz)5454bb7efa7SGarrett D'Amore sdhost_set_clock(sdslot_t *ss, uint32_t hz)
5464bb7efa7SGarrett D'Amore {
5474bb7efa7SGarrett D'Amore uint16_t div;
5484bb7efa7SGarrett D'Amore uint32_t val;
5494bb7efa7SGarrett D'Amore uint32_t clk;
5504bb7efa7SGarrett D'Amore int count;
5514bb7efa7SGarrett D'Amore
5524bb7efa7SGarrett D'Amore /*
5534bb7efa7SGarrett D'Amore * Shut off the clock to begin.
5544bb7efa7SGarrett D'Amore */
5554bb7efa7SGarrett D'Amore ss->ss_cardclk = 0;
5564bb7efa7SGarrett D'Amore PUT16(ss, REG_CLOCK_CONTROL, 0);
5574bb7efa7SGarrett D'Amore if (hz == 0) {
5584bb7efa7SGarrett D'Amore return (SDA_EOK);
5594bb7efa7SGarrett D'Amore }
5604bb7efa7SGarrett D'Amore
5614bb7efa7SGarrett D'Amore if (ss->ss_baseclk == 0) {
5624bb7efa7SGarrett D'Amore sda_host_log(ss->ss_host, ss->ss_num,
5634bb7efa7SGarrett D'Amore "Base clock frequency not established.");
5644bb7efa7SGarrett D'Amore return (SDA_EINVAL);
5654bb7efa7SGarrett D'Amore }
5664bb7efa7SGarrett D'Amore
5674bb7efa7SGarrett D'Amore if ((hz > 25000000) && ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0)) {
5684bb7efa7SGarrett D'Amore /* this clock requires high speed timings! */
5694bb7efa7SGarrett D'Amore SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
5704bb7efa7SGarrett D'Amore } else {
5714bb7efa7SGarrett D'Amore /* don't allow clock to run faster than 25MHz */
5724bb7efa7SGarrett D'Amore hz = min(hz, 25000000);
5734bb7efa7SGarrett D'Amore CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
5744bb7efa7SGarrett D'Amore }
5754bb7efa7SGarrett D'Amore
5764bb7efa7SGarrett D'Amore /* figure out the divider */
5774bb7efa7SGarrett D'Amore clk = ss->ss_baseclk;
5784bb7efa7SGarrett D'Amore div = 1;
5794bb7efa7SGarrett D'Amore while (clk > hz) {
5804bb7efa7SGarrett D'Amore if (div > 0x80)
5814bb7efa7SGarrett D'Amore break;
5824bb7efa7SGarrett D'Amore clk >>= 1; /* divide clock by two */
5834bb7efa7SGarrett D'Amore div <<= 1; /* divider goes up by one */
5844bb7efa7SGarrett D'Amore }
5854bb7efa7SGarrett D'Amore div >>= 1; /* 0 == divide by 1, 1 = divide by 2 */
5864bb7efa7SGarrett D'Amore
5874bb7efa7SGarrett D'Amore /*
5884bb7efa7SGarrett D'Amore * Set the internal clock divider first, without enabling the
5894bb7efa7SGarrett D'Amore * card clock yet.
5904bb7efa7SGarrett D'Amore */
5914bb7efa7SGarrett D'Amore PUT16(ss, REG_CLOCK_CONTROL,
5924bb7efa7SGarrett D'Amore (div << CLOCK_CONTROL_FREQ_SHIFT) | CLOCK_CONTROL_INT_CLOCK_EN);
5934bb7efa7SGarrett D'Amore
5944bb7efa7SGarrett D'Amore /*
5954bb7efa7SGarrett D'Amore * Wait up to 100 msec for the internal clock to stabilize.
5964bb7efa7SGarrett D'Amore * (The spec does not seem to indicate a maximum timeout, but
5974bb7efa7SGarrett D'Amore * it also suggests that an infinite loop be used, which is
5984bb7efa7SGarrett D'Amore * not appropriate for hardened Solaris drivers.)
5994bb7efa7SGarrett D'Amore */
6004bb7efa7SGarrett D'Amore for (count = 100000; count; count -= 10) {
6014bb7efa7SGarrett D'Amore
6024bb7efa7SGarrett D'Amore val = GET16(ss, REG_CLOCK_CONTROL);
6034bb7efa7SGarrett D'Amore
6044bb7efa7SGarrett D'Amore if (val & CLOCK_CONTROL_INT_CLOCK_STABLE) {
6054bb7efa7SGarrett D'Amore /* if clock is stable, enable the SD clock pin */
6064bb7efa7SGarrett D'Amore PUT16(ss, REG_CLOCK_CONTROL, val |
6074bb7efa7SGarrett D'Amore CLOCK_CONTROL_SD_CLOCK_EN);
6084bb7efa7SGarrett D'Amore
6094bb7efa7SGarrett D'Amore ss->ss_cardclk = clk;
6104bb7efa7SGarrett D'Amore return (SDA_EOK);
6114bb7efa7SGarrett D'Amore }
6124bb7efa7SGarrett D'Amore
6134bb7efa7SGarrett D'Amore drv_usecwait(10);
6144bb7efa7SGarrett D'Amore }
6154bb7efa7SGarrett D'Amore
6164bb7efa7SGarrett D'Amore return (SDA_ETIME);
6174bb7efa7SGarrett D'Amore }
6184bb7efa7SGarrett D'Amore
6194bb7efa7SGarrett D'Amore sda_err_t
sdhost_soft_reset(sdslot_t * ss,uint8_t bits)6204bb7efa7SGarrett D'Amore sdhost_soft_reset(sdslot_t *ss, uint8_t bits)
6214bb7efa7SGarrett D'Amore {
6224bb7efa7SGarrett D'Amore int count;
6234bb7efa7SGarrett D'Amore
6244bb7efa7SGarrett D'Amore /*
6254bb7efa7SGarrett D'Amore * There appears to be a bug where Ricoh hosts might have a
6264bb7efa7SGarrett D'Amore * problem if the host frequency is not set. If the card
6274bb7efa7SGarrett D'Amore * isn't present, or we are doing a master reset, just enable
6284bb7efa7SGarrett D'Amore * the internal clock at its native speed. (No dividers, and
6294bb7efa7SGarrett D'Amore * not exposed to card.).
6304bb7efa7SGarrett D'Amore */
6314bb7efa7SGarrett D'Amore if ((bits == SOFT_RESET_ALL) || !(CHECK_STATE(ss, CARD_INSERTED))) {
6324bb7efa7SGarrett D'Amore PUT16(ss, REG_CLOCK_CONTROL, CLOCK_CONTROL_INT_CLOCK_EN);
6334bb7efa7SGarrett D'Amore /* simple 1msec wait, don't wait for clock to stabilize */
6344bb7efa7SGarrett D'Amore drv_usecwait(1000);
6359a5113a6SGarrett D'Amore /*
6369a5113a6SGarrett D'Amore * reset the card clock & width -- master reset also
6379a5113a6SGarrett D'Amore * resets these
6389a5113a6SGarrett D'Amore */
6399a5113a6SGarrett D'Amore ss->ss_cardclk = 0;
6409a5113a6SGarrett D'Amore ss->ss_width = 1;
6414bb7efa7SGarrett D'Amore }
6424bb7efa7SGarrett D'Amore
6439a5113a6SGarrett D'Amore
6444bb7efa7SGarrett D'Amore PUT8(ss, REG_SOFT_RESET, bits);
6454bb7efa7SGarrett D'Amore for (count = 100000; count != 0; count -= 10) {
6464bb7efa7SGarrett D'Amore if ((GET8(ss, REG_SOFT_RESET) & bits) == 0) {
6474bb7efa7SGarrett D'Amore return (SDA_EOK);
6484bb7efa7SGarrett D'Amore }
6494bb7efa7SGarrett D'Amore drv_usecwait(10);
6504bb7efa7SGarrett D'Amore }
6514bb7efa7SGarrett D'Amore
6524bb7efa7SGarrett D'Amore return (SDA_ETIME);
6534bb7efa7SGarrett D'Amore }
6544bb7efa7SGarrett D'Amore
6554bb7efa7SGarrett D'Amore void
sdhost_disable_interrupts(sdslot_t * ss)6564bb7efa7SGarrett D'Amore sdhost_disable_interrupts(sdslot_t *ss)
6574bb7efa7SGarrett D'Amore {
6584bb7efa7SGarrett D'Amore /* disable slot interrupts for card insert and remove */
6594bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_MASK, 0);
6604bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_EN, 0);
6614bb7efa7SGarrett D'Amore
6624bb7efa7SGarrett D'Amore /* disable error interrupts */
6634bb7efa7SGarrett D'Amore PUT16(ss, REG_ERR_MASK, 0);
6644bb7efa7SGarrett D'Amore PUT16(ss, REG_ERR_EN, 0);
6654bb7efa7SGarrett D'Amore }
6664bb7efa7SGarrett D'Amore
6674bb7efa7SGarrett D'Amore void
sdhost_enable_interrupts(sdslot_t * ss)6684bb7efa7SGarrett D'Amore sdhost_enable_interrupts(sdslot_t *ss)
6694bb7efa7SGarrett D'Amore {
6704bb7efa7SGarrett D'Amore /*
6714bb7efa7SGarrett D'Amore * Note that we want to enable reading of the CMD related
6724bb7efa7SGarrett D'Amore * bits, but we do not want them to generate an interrupt.
6734bb7efa7SGarrett D'Amore * (The busy wait for typical CMD stuff will normally be less
6744bb7efa7SGarrett D'Amore * than 10usec, so its simpler/easier to just poll. Even in
6754bb7efa7SGarrett D'Amore * the worst case of 100 kHz, the poll is at worst 2 msec.)
6764bb7efa7SGarrett D'Amore */
6774bb7efa7SGarrett D'Amore
6784bb7efa7SGarrett D'Amore /* enable slot interrupts for card insert and remove */
6794bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_MASK, INT_MASK);
6804bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_EN, INT_ENAB);
6814bb7efa7SGarrett D'Amore
6824bb7efa7SGarrett D'Amore /* enable error interrupts */
6834bb7efa7SGarrett D'Amore PUT16(ss, REG_ERR_MASK, ERR_MASK);
6844bb7efa7SGarrett D'Amore PUT16(ss, REG_ERR_EN, ERR_ENAB);
6854bb7efa7SGarrett D'Amore }
6864bb7efa7SGarrett D'Amore
6874bb7efa7SGarrett D'Amore int
sdhost_setup_intr(dev_info_t * dip,sdhost_t * shp)6884bb7efa7SGarrett D'Amore sdhost_setup_intr(dev_info_t *dip, sdhost_t *shp)
6894bb7efa7SGarrett D'Amore {
6904bb7efa7SGarrett D'Amore int itypes;
6914bb7efa7SGarrett D'Amore int itype;
6924bb7efa7SGarrett D'Amore
6934bb7efa7SGarrett D'Amore /*
6944bb7efa7SGarrett D'Amore * Set up interrupt handler.
6954bb7efa7SGarrett D'Amore */
6964bb7efa7SGarrett D'Amore if (ddi_intr_get_supported_types(dip, &itypes) != DDI_SUCCESS) {
6974bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "ddi_intr_get_supported_types failed");
6984bb7efa7SGarrett D'Amore return (DDI_FAILURE);
6994bb7efa7SGarrett D'Amore }
7004bb7efa7SGarrett D'Amore
70128e61230SGarrett D'Amore /*
70228e61230SGarrett D'Amore * It turns out that some controllers don't properly implement MSI,
70328e61230SGarrett D'Amore * but advertise MSI capability in their PCI config space.
70428e61230SGarrett D'Amore *
70528e61230SGarrett D'Amore * While this is really a chip-specific bug, the simplest solution
70628e61230SGarrett D'Amore * is to just suppress MSI for now by default -- every device seen
70728e61230SGarrett D'Amore * so far can use FIXED interrupts.
70828e61230SGarrett D'Amore *
70928e61230SGarrett D'Amore * We offer an override property, though, just in case someone really
71028e61230SGarrett D'Amore * wants to force it.
71128e61230SGarrett D'Amore *
71228e61230SGarrett D'Amore * We don't do this if the FIXED type isn't supported though!
71328e61230SGarrett D'Amore */
7149a5113a6SGarrett D'Amore if (itypes & DDI_INTR_TYPE_FIXED) {
7159a5113a6SGarrett D'Amore if (!PROPSET(SDHOST_PROP_ENABLE_MSI)) {
7169a5113a6SGarrett D'Amore itypes &= ~DDI_INTR_TYPE_MSI;
7179a5113a6SGarrett D'Amore }
7189a5113a6SGarrett D'Amore if (!PROPSET(SDHOST_PROP_ENABLE_MSIX)) {
7199a5113a6SGarrett D'Amore itypes &= ~DDI_INTR_TYPE_MSIX;
7209a5113a6SGarrett D'Amore }
72128e61230SGarrett D'Amore }
72228e61230SGarrett D'Amore
7234bb7efa7SGarrett D'Amore /*
7244bb7efa7SGarrett D'Amore * Interrupt types are bits in a mask. We know about these ones:
7254bb7efa7SGarrett D'Amore * FIXED = 1
7264bb7efa7SGarrett D'Amore * MSI = 2
7274bb7efa7SGarrett D'Amore * MSIX = 4
7284bb7efa7SGarrett D'Amore */
7294bb7efa7SGarrett D'Amore for (itype = DDI_INTR_TYPE_MSIX; itype != 0; itype >>= 1) {
7304bb7efa7SGarrett D'Amore
7314bb7efa7SGarrett D'Amore int count;
7324bb7efa7SGarrett D'Amore
7334bb7efa7SGarrett D'Amore if ((itypes & itype) == 0) {
7344bb7efa7SGarrett D'Amore /* this type is not supported on this device! */
7354bb7efa7SGarrett D'Amore continue;
7364bb7efa7SGarrett D'Amore }
7374bb7efa7SGarrett D'Amore
7384bb7efa7SGarrett D'Amore if ((ddi_intr_get_nintrs(dip, itype, &count) != DDI_SUCCESS) ||
7394bb7efa7SGarrett D'Amore (count == 0)) {
7404bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "ddi_intr_get_nintrs failed");
7414bb7efa7SGarrett D'Amore continue;
7424bb7efa7SGarrett D'Amore }
7434bb7efa7SGarrett D'Amore
7444bb7efa7SGarrett D'Amore /*
7454bb7efa7SGarrett D'Amore * We have not seen a host device with multiple
7464bb7efa7SGarrett D'Amore * interrupts (one per slot?), and the spec does not
7474bb7efa7SGarrett D'Amore * indicate that they exist. But if one ever occurs,
7484bb7efa7SGarrett D'Amore * we spew a warning to help future debugging/support
7494bb7efa7SGarrett D'Amore * efforts.
7504bb7efa7SGarrett D'Amore */
7514bb7efa7SGarrett D'Amore if (count > 1) {
7524bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "Controller offers %d interrupts, "
7534bb7efa7SGarrett D'Amore "but driver only supports one", count);
7544bb7efa7SGarrett D'Amore continue;
7554bb7efa7SGarrett D'Amore }
7564bb7efa7SGarrett D'Amore
7574bb7efa7SGarrett D'Amore if ((ddi_intr_alloc(dip, &shp->sh_ihandle, itype, 0, 1,
7584bb7efa7SGarrett D'Amore &count, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
7594bb7efa7SGarrett D'Amore (count != 1)) {
7604bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "ddi_intr_alloc failed");
7614bb7efa7SGarrett D'Amore continue;
7624bb7efa7SGarrett D'Amore }
7634bb7efa7SGarrett D'Amore
7644bb7efa7SGarrett D'Amore if (ddi_intr_get_pri(shp->sh_ihandle, &shp->sh_ipri) !=
7654bb7efa7SGarrett D'Amore DDI_SUCCESS) {
7664bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "ddi_intr_get_pri failed");
7674bb7efa7SGarrett D'Amore (void) ddi_intr_free(shp->sh_ihandle);
7684bb7efa7SGarrett D'Amore shp->sh_ihandle = NULL;
7694bb7efa7SGarrett D'Amore continue;
7704bb7efa7SGarrett D'Amore }
7714bb7efa7SGarrett D'Amore
7724bb7efa7SGarrett D'Amore if (shp->sh_ipri >= ddi_intr_get_hilevel_pri()) {
7734bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "Hi level interrupt not supported");
7744bb7efa7SGarrett D'Amore (void) ddi_intr_free(shp->sh_ihandle);
7754bb7efa7SGarrett D'Amore shp->sh_ihandle = NULL;
7764bb7efa7SGarrett D'Amore continue;
7774bb7efa7SGarrett D'Amore }
7784bb7efa7SGarrett D'Amore
7794bb7efa7SGarrett D'Amore if (ddi_intr_get_cap(shp->sh_ihandle, &shp->sh_icap) !=
7804bb7efa7SGarrett D'Amore DDI_SUCCESS) {
7814bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "ddi_intr_get_cap failed");
7824bb7efa7SGarrett D'Amore (void) ddi_intr_free(shp->sh_ihandle);
7834bb7efa7SGarrett D'Amore shp->sh_ihandle = NULL;
7844bb7efa7SGarrett D'Amore continue;
7854bb7efa7SGarrett D'Amore }
7864bb7efa7SGarrett D'Amore
7874bb7efa7SGarrett D'Amore if (ddi_intr_add_handler(shp->sh_ihandle, sdhost_intr,
7884bb7efa7SGarrett D'Amore shp, NULL) != DDI_SUCCESS) {
7894bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "ddi_intr_add_handler failed");
7904bb7efa7SGarrett D'Amore (void) ddi_intr_free(shp->sh_ihandle);
7914bb7efa7SGarrett D'Amore shp->sh_ihandle = NULL;
7924bb7efa7SGarrett D'Amore continue;
7934bb7efa7SGarrett D'Amore }
7944bb7efa7SGarrett D'Amore
7954bb7efa7SGarrett D'Amore return (DDI_SUCCESS);
7964bb7efa7SGarrett D'Amore }
7974bb7efa7SGarrett D'Amore
7984bb7efa7SGarrett D'Amore return (DDI_FAILURE);
7994bb7efa7SGarrett D'Amore }
8004bb7efa7SGarrett D'Amore
8014bb7efa7SGarrett D'Amore void
sdhost_xfer_done(sdslot_t * ss,sda_err_t errno)8024bb7efa7SGarrett D'Amore sdhost_xfer_done(sdslot_t *ss, sda_err_t errno)
8034bb7efa7SGarrett D'Amore {
8044bb7efa7SGarrett D'Amore if ((errno == SDA_EOK) && (ss->ss_resid != 0)) {
8054bb7efa7SGarrett D'Amore /* an unexpected partial transfer was found */
8064bb7efa7SGarrett D'Amore errno = SDA_ERESID;
8074bb7efa7SGarrett D'Amore }
8084bb7efa7SGarrett D'Amore ss->ss_blksz = 0;
8094bb7efa7SGarrett D'Amore ss->ss_resid = 0;
8104bb7efa7SGarrett D'Amore
8114bb7efa7SGarrett D'Amore if (errno != SDA_EOK) {
8124bb7efa7SGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
8134bb7efa7SGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
8144bb7efa7SGarrett D'Amore
8154bb7efa7SGarrett D'Amore /* send a STOP command if necessary */
8164bb7efa7SGarrett D'Amore if (ss->ss_mode & XFR_MODE_AUTO_CMD12) {
8174bb7efa7SGarrett D'Amore PUT32(ss, REG_ARGUMENT, 0);
8184bb7efa7SGarrett D'Amore PUT16(ss, REG_COMMAND,
8194bb7efa7SGarrett D'Amore (CMD_STOP_TRANSMIT << 8) |
8204bb7efa7SGarrett D'Amore COMMAND_TYPE_NORM | COMMAND_INDEX_CHECK_EN |
8214bb7efa7SGarrett D'Amore COMMAND_CRC_CHECK_EN | COMMAND_RESP_48_BUSY);
8224bb7efa7SGarrett D'Amore }
8234bb7efa7SGarrett D'Amore }
8244bb7efa7SGarrett D'Amore
8254bb7efa7SGarrett D'Amore sda_host_transfer(ss->ss_host, ss->ss_num, errno);
8264bb7efa7SGarrett D'Amore }
8274bb7efa7SGarrett D'Amore
8284bb7efa7SGarrett D'Amore uint_t
sdhost_slot_intr(sdslot_t * ss)8294bb7efa7SGarrett D'Amore sdhost_slot_intr(sdslot_t *ss)
8304bb7efa7SGarrett D'Amore {
8314bb7efa7SGarrett D'Amore uint16_t intr;
8324bb7efa7SGarrett D'Amore uint16_t errs;
8339a5113a6SGarrett D'Amore caddr_t data;
8344bb7efa7SGarrett D'Amore int count;
8354bb7efa7SGarrett D'Amore
8364bb7efa7SGarrett D'Amore mutex_enter(&ss->ss_lock);
8374bb7efa7SGarrett D'Amore
8384bb7efa7SGarrett D'Amore if (ss->ss_suspended) {
8394bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
8404bb7efa7SGarrett D'Amore return (DDI_INTR_UNCLAIMED);
8414bb7efa7SGarrett D'Amore }
8424bb7efa7SGarrett D'Amore
8434bb7efa7SGarrett D'Amore intr = GET16(ss, REG_INT_STAT);
8444bb7efa7SGarrett D'Amore if (intr == 0) {
8454bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
8464bb7efa7SGarrett D'Amore return (DDI_INTR_UNCLAIMED);
8474bb7efa7SGarrett D'Amore }
8484bb7efa7SGarrett D'Amore errs = GET16(ss, REG_ERR_STAT);
8494bb7efa7SGarrett D'Amore
8504bb7efa7SGarrett D'Amore if (intr & (INT_REM | INT_INS)) {
8514bb7efa7SGarrett D'Amore
8524bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_STAT, intr);
8534bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
8544bb7efa7SGarrett D'Amore
8554bb7efa7SGarrett D'Amore sda_host_detect(ss->ss_host, ss->ss_num);
8564bb7efa7SGarrett D'Amore /* no further interrupt processing this cycle */
8574bb7efa7SGarrett D'Amore return (DDI_INTR_CLAIMED);
8584bb7efa7SGarrett D'Amore }
8594bb7efa7SGarrett D'Amore
8604bb7efa7SGarrett D'Amore if (intr & INT_DMA) {
8614bb7efa7SGarrett D'Amore /*
8624bb7efa7SGarrett D'Amore * We have crossed a DMA/page boundary. Cope with it.
8634bb7efa7SGarrett D'Amore */
8649a5113a6SGarrett D'Amore /*
8659a5113a6SGarrett D'Amore * Apparently some sdhost controllers issue a final
8669a5113a6SGarrett D'Amore * DMA interrupt if the DMA completes on a boundary,
8679a5113a6SGarrett D'Amore * even though there is no further data to transfer.
8689a5113a6SGarrett D'Amore *
8699a5113a6SGarrett D'Amore * There might be a risk here of the controller
8709a5113a6SGarrett D'Amore * continuing to access the same data over and over
8719a5113a6SGarrett D'Amore * again, but we accept the risk.
8729a5113a6SGarrett D'Amore */
8739a5113a6SGarrett D'Amore PUT16(ss, REG_INT_STAT, INT_DMA);
8744bb7efa7SGarrett D'Amore }
8754bb7efa7SGarrett D'Amore
8764bb7efa7SGarrett D'Amore if (intr & INT_RD) {
8774bb7efa7SGarrett D'Amore /*
8784bb7efa7SGarrett D'Amore * PIO read! PIO is quite suboptimal, but we expect
8794bb7efa7SGarrett D'Amore * performance critical applications to use DMA
8804bb7efa7SGarrett D'Amore * whenever possible. We have to stage this through
8814bb7efa7SGarrett D'Amore * the bounce buffer to meet alignment considerations.
8824bb7efa7SGarrett D'Amore */
8834bb7efa7SGarrett D'Amore
8844bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_STAT, INT_RD);
8854bb7efa7SGarrett D'Amore
8864bb7efa7SGarrett D'Amore while ((ss->ss_resid > 0) && CHECK_STATE(ss, BUF_RD_EN)) {
8874bb7efa7SGarrett D'Amore
8889a5113a6SGarrett D'Amore data = ss->ss_bounce;
8894bb7efa7SGarrett D'Amore count = ss->ss_blksz;
8904bb7efa7SGarrett D'Amore
8914bb7efa7SGarrett D'Amore ASSERT(count > 0);
8924bb7efa7SGarrett D'Amore ASSERT(ss->ss_kvaddr != NULL);
8934bb7efa7SGarrett D'Amore
8944bb7efa7SGarrett D'Amore while (count >= sizeof (uint32_t)) {
8954bb7efa7SGarrett D'Amore *(uint32_t *)(void *)data = GETDATA32(ss);
8964bb7efa7SGarrett D'Amore data += sizeof (uint32_t);
8974bb7efa7SGarrett D'Amore count -= sizeof (uint32_t);
8984bb7efa7SGarrett D'Amore }
8994bb7efa7SGarrett D'Amore while (count >= sizeof (uint16_t)) {
9004bb7efa7SGarrett D'Amore *(uint16_t *)(void *)data = GETDATA16(ss);
9014bb7efa7SGarrett D'Amore data += sizeof (uint16_t);
9024bb7efa7SGarrett D'Amore count -= sizeof (uint16_t);
9034bb7efa7SGarrett D'Amore }
9044bb7efa7SGarrett D'Amore while (count >= sizeof (uint8_t)) {
9054bb7efa7SGarrett D'Amore *(uint8_t *)data = GETDATA8(ss);
9064bb7efa7SGarrett D'Amore data += sizeof (uint8_t);
9074bb7efa7SGarrett D'Amore count -= sizeof (uint8_t);
9084bb7efa7SGarrett D'Amore }
9094bb7efa7SGarrett D'Amore
9104bb7efa7SGarrett D'Amore bcopy(ss->ss_bounce, ss->ss_kvaddr, ss->ss_blksz);
9114bb7efa7SGarrett D'Amore ss->ss_kvaddr += ss->ss_blksz;
9124bb7efa7SGarrett D'Amore ss->ss_resid--;
9134bb7efa7SGarrett D'Amore }
9144bb7efa7SGarrett D'Amore }
9154bb7efa7SGarrett D'Amore
9164bb7efa7SGarrett D'Amore if (intr & INT_WR) {
9174bb7efa7SGarrett D'Amore /*
9184bb7efa7SGarrett D'Amore * PIO write! PIO is quite suboptimal, but we expect
9194bb7efa7SGarrett D'Amore * performance critical applications to use DMA
9209a5113a6SGarrett D'Amore * whenever possible. We have to stage this through
9214bb7efa7SGarrett D'Amore * the bounce buffer to meet alignment considerations.
9224bb7efa7SGarrett D'Amore */
9234bb7efa7SGarrett D'Amore
9244bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_STAT, INT_WR);
9254bb7efa7SGarrett D'Amore
9264bb7efa7SGarrett D'Amore while ((ss->ss_resid > 0) && CHECK_STATE(ss, BUF_WR_EN)) {
9274bb7efa7SGarrett D'Amore
9289a5113a6SGarrett D'Amore data = ss->ss_bounce;
9294bb7efa7SGarrett D'Amore count = ss->ss_blksz;
9304bb7efa7SGarrett D'Amore
9314bb7efa7SGarrett D'Amore ASSERT(count > 0);
9324bb7efa7SGarrett D'Amore ASSERT(ss->ss_kvaddr != NULL);
9334bb7efa7SGarrett D'Amore
9344bb7efa7SGarrett D'Amore bcopy(ss->ss_kvaddr, data, count);
9354bb7efa7SGarrett D'Amore while (count >= sizeof (uint32_t)) {
9364bb7efa7SGarrett D'Amore PUTDATA32(ss, *(uint32_t *)(void *)data);
9374bb7efa7SGarrett D'Amore data += sizeof (uint32_t);
9384bb7efa7SGarrett D'Amore count -= sizeof (uint32_t);
9394bb7efa7SGarrett D'Amore }
9404bb7efa7SGarrett D'Amore while (count >= sizeof (uint16_t)) {
9414bb7efa7SGarrett D'Amore PUTDATA16(ss, *(uint16_t *)(void *)data);
9424bb7efa7SGarrett D'Amore data += sizeof (uint16_t);
9434bb7efa7SGarrett D'Amore count -= sizeof (uint16_t);
9444bb7efa7SGarrett D'Amore }
9454bb7efa7SGarrett D'Amore while (count >= sizeof (uint8_t)) {
9464bb7efa7SGarrett D'Amore PUTDATA8(ss, *(uint8_t *)data);
9474bb7efa7SGarrett D'Amore data += sizeof (uint8_t);
9484bb7efa7SGarrett D'Amore count -= sizeof (uint8_t);
9494bb7efa7SGarrett D'Amore }
9504bb7efa7SGarrett D'Amore
9514bb7efa7SGarrett D'Amore ss->ss_kvaddr += ss->ss_blksz;
9524bb7efa7SGarrett D'Amore ss->ss_resid--;
9534bb7efa7SGarrett D'Amore }
9544bb7efa7SGarrett D'Amore }
9554bb7efa7SGarrett D'Amore
9564bb7efa7SGarrett D'Amore if (intr & INT_XFR) {
9579a5113a6SGarrett D'Amore if ((ss->ss_mode & (XFR_MODE_READ | XFR_MODE_DMA_EN)) ==
9589a5113a6SGarrett D'Amore (XFR_MODE_READ | XFR_MODE_DMA_EN)) {
9599a5113a6SGarrett D'Amore (void) ddi_dma_sync(ss->ss_bufdmah, 0, 0,
9609a5113a6SGarrett D'Amore DDI_DMA_SYNC_FORKERNEL);
9619a5113a6SGarrett D'Amore bcopy(ss->ss_bounce, ss->ss_kvaddr, ss->ss_rcnt);
9629a5113a6SGarrett D'Amore ss->ss_rcnt = 0;
9639a5113a6SGarrett D'Amore }
9644bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_STAT, INT_XFR);
9654bb7efa7SGarrett D'Amore
9664bb7efa7SGarrett D'Amore sdhost_xfer_done(ss, SDA_EOK);
9674bb7efa7SGarrett D'Amore }
9684bb7efa7SGarrett D'Amore
9694bb7efa7SGarrett D'Amore if (intr & INT_ERR) {
9704bb7efa7SGarrett D'Amore PUT16(ss, REG_ERR_STAT, errs);
9714bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_STAT, INT_ERR);
9724bb7efa7SGarrett D'Amore
9734bb7efa7SGarrett D'Amore if (errs & ERR_DAT) {
9744bb7efa7SGarrett D'Amore if ((errs & ERR_DAT_END) == ERR_DAT_END) {
9754bb7efa7SGarrett D'Amore sdhost_xfer_done(ss, SDA_EPROTO);
9764bb7efa7SGarrett D'Amore } else if ((errs & ERR_DAT_CRC) == ERR_DAT_CRC) {
9774bb7efa7SGarrett D'Amore sdhost_xfer_done(ss, SDA_ECRC7);
9784bb7efa7SGarrett D'Amore } else {
9794bb7efa7SGarrett D'Amore sdhost_xfer_done(ss, SDA_ETIME);
9804bb7efa7SGarrett D'Amore }
9814bb7efa7SGarrett D'Amore
9824bb7efa7SGarrett D'Amore } else if (errs & ERR_ACMD12) {
9834bb7efa7SGarrett D'Amore /*
9844bb7efa7SGarrett D'Amore * Generally, this is bad news. we need a full
9854bb7efa7SGarrett D'Amore * reset to recover properly.
9864bb7efa7SGarrett D'Amore */
9874bb7efa7SGarrett D'Amore sdhost_xfer_done(ss, SDA_ECMD12);
9884bb7efa7SGarrett D'Amore }
9894bb7efa7SGarrett D'Amore
9904bb7efa7SGarrett D'Amore /*
9914bb7efa7SGarrett D'Amore * This asynchronous error leaves the slot more or less
9924bb7efa7SGarrett D'Amore * useless. Report it to the framework.
9934bb7efa7SGarrett D'Amore */
9944bb7efa7SGarrett D'Amore if (errs & ERR_CURRENT) {
9954bb7efa7SGarrett D'Amore sda_host_fault(ss->ss_host, ss->ss_num,
9964bb7efa7SGarrett D'Amore SDA_FAULT_CURRENT);
9974bb7efa7SGarrett D'Amore }
9984bb7efa7SGarrett D'Amore }
9994bb7efa7SGarrett D'Amore
10004bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
10014bb7efa7SGarrett D'Amore
10024bb7efa7SGarrett D'Amore return (DDI_INTR_CLAIMED);
10034bb7efa7SGarrett D'Amore }
10044bb7efa7SGarrett D'Amore
10054bb7efa7SGarrett D'Amore /*ARGSUSED1*/
10064bb7efa7SGarrett D'Amore uint_t
sdhost_intr(caddr_t arg1,caddr_t arg2)10074bb7efa7SGarrett D'Amore sdhost_intr(caddr_t arg1, caddr_t arg2)
10084bb7efa7SGarrett D'Amore {
10094bb7efa7SGarrett D'Amore sdhost_t *shp = (void *)arg1;
10104bb7efa7SGarrett D'Amore int rv = DDI_INTR_UNCLAIMED;
10114bb7efa7SGarrett D'Amore int num;
10124bb7efa7SGarrett D'Amore
10134bb7efa7SGarrett D'Amore /* interrupt for each of the slots present in the system */
10144bb7efa7SGarrett D'Amore for (num = 0; num < shp->sh_numslots; num++) {
10154bb7efa7SGarrett D'Amore if (sdhost_slot_intr(&shp->sh_slots[num]) ==
10164bb7efa7SGarrett D'Amore DDI_INTR_CLAIMED) {
10174bb7efa7SGarrett D'Amore rv = DDI_INTR_CLAIMED;
10184bb7efa7SGarrett D'Amore }
10194bb7efa7SGarrett D'Amore }
10204bb7efa7SGarrett D'Amore return (rv);
10214bb7efa7SGarrett D'Amore }
10224bb7efa7SGarrett D'Amore
10234bb7efa7SGarrett D'Amore int
sdhost_init_slot(dev_info_t * dip,sdhost_t * shp,int num,int bar)10244bb7efa7SGarrett D'Amore sdhost_init_slot(dev_info_t *dip, sdhost_t *shp, int num, int bar)
10254bb7efa7SGarrett D'Amore {
10264bb7efa7SGarrett D'Amore sdslot_t *ss;
10274bb7efa7SGarrett D'Amore uint32_t capab;
10284bb7efa7SGarrett D'Amore uint32_t clk;
10299a5113a6SGarrett D'Amore char ksname[16];
10309a5113a6SGarrett D'Amore size_t blen;
10319a5113a6SGarrett D'Amore unsigned ndmac;
10329a5113a6SGarrett D'Amore int rv;
10334bb7efa7SGarrett D'Amore
10344bb7efa7SGarrett D'Amore /*
10354bb7efa7SGarrett D'Amore * Register the private state.
10364bb7efa7SGarrett D'Amore */
10374bb7efa7SGarrett D'Amore ss = &shp->sh_slots[num];
10384bb7efa7SGarrett D'Amore ss->ss_host = shp->sh_host;
10394bb7efa7SGarrett D'Amore ss->ss_num = num;
10404bb7efa7SGarrett D'Amore sda_host_set_private(shp->sh_host, num, ss);
10414bb7efa7SGarrett D'Amore /*
10424bb7efa7SGarrett D'Amore * Initialize core data structure, locks, etc.
10434bb7efa7SGarrett D'Amore */
10444bb7efa7SGarrett D'Amore mutex_init(&ss->ss_lock, NULL, MUTEX_DRIVER,
10454bb7efa7SGarrett D'Amore DDI_INTR_PRI(shp->sh_ipri));
10464bb7efa7SGarrett D'Amore
10479a5113a6SGarrett D'Amore /*
10489a5113a6SGarrett D'Amore * Set up DMA.
10499a5113a6SGarrett D'Amore */
10509a5113a6SGarrett D'Amore rv = ddi_dma_alloc_handle(dip, &shp->sh_dmaattr,
10519a5113a6SGarrett D'Amore DDI_DMA_SLEEP, NULL, &ss->ss_bufdmah);
10529a5113a6SGarrett D'Amore if (rv != DDI_SUCCESS) {
10539a5113a6SGarrett D'Amore cmn_err(CE_WARN, "Failed to alloc dma handle (%d)!", rv);
10549a5113a6SGarrett D'Amore return (DDI_FAILURE);
10559a5113a6SGarrett D'Amore }
10569a5113a6SGarrett D'Amore
10579a5113a6SGarrett D'Amore rv = ddi_dma_mem_alloc(ss->ss_bufdmah, SDHOST_BOUNCESZ,
10589a5113a6SGarrett D'Amore &sdhost_bufattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
10599a5113a6SGarrett D'Amore &ss->ss_bounce, &blen, &ss->ss_bufacch);
10609a5113a6SGarrett D'Amore if (rv != DDI_SUCCESS) {
10619a5113a6SGarrett D'Amore cmn_err(CE_WARN, "Failed to alloc bounce buffer (%d)!", rv);
10629a5113a6SGarrett D'Amore return (DDI_FAILURE);
10639a5113a6SGarrett D'Amore }
10649a5113a6SGarrett D'Amore
10659a5113a6SGarrett D'Amore rv = ddi_dma_addr_bind_handle(ss->ss_bufdmah, NULL, ss->ss_bounce,
10669a5113a6SGarrett D'Amore blen, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
10679a5113a6SGarrett D'Amore &ss->ss_bufdmac, &ndmac);
10689a5113a6SGarrett D'Amore if ((rv != DDI_DMA_MAPPED) || (ndmac != 1)) {
10699a5113a6SGarrett D'Amore cmn_err(CE_WARN, "Failed to bind DMA bounce buffer (%d, %u)!",
10709a5113a6SGarrett D'Amore rv, ndmac);
10719a5113a6SGarrett D'Amore return (DDI_FAILURE);
10729a5113a6SGarrett D'Amore }
10739a5113a6SGarrett D'Amore
10749a5113a6SGarrett D'Amore /*
10759a5113a6SGarrett D'Amore * Set up virtual kstats.
10769a5113a6SGarrett D'Amore */
10779a5113a6SGarrett D'Amore (void) snprintf(ksname, sizeof (ksname), "slot%d", num);
10789a5113a6SGarrett D'Amore ss->ss_ksp = kstat_create(ddi_driver_name(dip), ddi_get_instance(dip),
10799a5113a6SGarrett D'Amore ksname, "misc", KSTAT_TYPE_NAMED,
10809a5113a6SGarrett D'Amore sizeof (sdstats_t) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL);
10819a5113a6SGarrett D'Amore if (ss->ss_ksp != NULL) {
10829a5113a6SGarrett D'Amore sdstats_t *sp = &ss->ss_stats;
10839a5113a6SGarrett D'Amore ss->ss_ksp->ks_data = sp;
10849a5113a6SGarrett D'Amore ss->ss_ksp->ks_private = ss;
10859a5113a6SGarrett D'Amore ss->ss_ksp->ks_lock = &ss->ss_lock;
10869a5113a6SGarrett D'Amore /* counters are 64 bits wide */
10879a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_ncmd, "ncmd", KSTAT_DATA_UINT64);
10889a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_ixfr, "ixfr", KSTAT_DATA_UINT64);
10899a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_oxfr, "oxfr", KSTAT_DATA_UINT64);
10909a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_ibytes, "ibytes", KSTAT_DATA_UINT64);
10919a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_obytes, "obytes", KSTAT_DATA_UINT64);
10929a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_npio, "npio", KSTAT_DATA_UINT64);
10939a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_ndma, "ndma", KSTAT_DATA_UINT64);
10949a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_nmulti, "nmulti", KSTAT_DATA_UINT64);
10959a5113a6SGarrett D'Amore /* these aren't counters -- leave them at 32 bits */
10969a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_baseclk, "baseclk", KSTAT_DATA_UINT32);
10979a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_cardclk, "cardclk", KSTAT_DATA_UINT32);
10989a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_tmusecs, "tmusecs", KSTAT_DATA_UINT32);
10999a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_width, "width", KSTAT_DATA_UINT32);
11009a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_flags, "flags", KSTAT_DATA_UINT32);
11019a5113a6SGarrett D'Amore kstat_named_init(&sp->ks_capab, "capab", KSTAT_DATA_UINT32);
11029a5113a6SGarrett D'Amore kstat_install(ss->ss_ksp);
11039a5113a6SGarrett D'Amore }
11049a5113a6SGarrett D'Amore
11059a5113a6SGarrett D'Amore if (PROPSET(SDHOST_PROP_FORCE_PIO)) {
11069a5113a6SGarrett D'Amore ss->ss_flags |= SDFLAG_FORCE_PIO;
11079a5113a6SGarrett D'Amore }
11089a5113a6SGarrett D'Amore if (PROPSET(SDHOST_PROP_FORCE_DMA)) {
11099a5113a6SGarrett D'Amore ss->ss_flags |= SDFLAG_FORCE_DMA;
11109a5113a6SGarrett D'Amore }
11119a5113a6SGarrett D'Amore
11124bb7efa7SGarrett D'Amore if (ddi_regs_map_setup(dip, bar, &ss->ss_regva, 0, 0, &sdhost_regattr,
11134bb7efa7SGarrett D'Amore &ss->ss_acch) != DDI_SUCCESS) {
1114f2b90c3cSGarrett D'Amore cmn_err(CE_WARN, "Failed to map registers!");
11154bb7efa7SGarrett D'Amore return (DDI_FAILURE);
11164bb7efa7SGarrett D'Amore }
11174bb7efa7SGarrett D'Amore
11184bb7efa7SGarrett D'Amore /* reset before reading capabilities */
11194bb7efa7SGarrett D'Amore if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK)
11204bb7efa7SGarrett D'Amore return (DDI_FAILURE);
11214bb7efa7SGarrett D'Amore
11224bb7efa7SGarrett D'Amore capab = GET64(ss, REG_CAPAB) & 0xffffffffU; /* upper bits reserved */
11234bb7efa7SGarrett D'Amore ss->ss_capab = capab;
11244bb7efa7SGarrett D'Amore
11254bb7efa7SGarrett D'Amore /* host voltages in OCR format */
11264bb7efa7SGarrett D'Amore ss->ss_ocr = 0;
11274bb7efa7SGarrett D'Amore if (capab & CAPAB_18V)
11284bb7efa7SGarrett D'Amore ss->ss_ocr |= OCR_18_19V; /* 1.8V */
11294bb7efa7SGarrett D'Amore if (capab & CAPAB_30V)
11304bb7efa7SGarrett D'Amore ss->ss_ocr |= OCR_30_31V;
11314bb7efa7SGarrett D'Amore if (capab & CAPAB_33V)
11324bb7efa7SGarrett D'Amore ss->ss_ocr |= OCR_32_33V;
11334bb7efa7SGarrett D'Amore
11344bb7efa7SGarrett D'Amore /* base clock */
11354bb7efa7SGarrett D'Amore ss->ss_baseclk =
11364bb7efa7SGarrett D'Amore ((capab & CAPAB_BASE_FREQ_MASK) >> CAPAB_BASE_FREQ_SHIFT);
11374bb7efa7SGarrett D'Amore ss->ss_baseclk *= 1000000;
11384bb7efa7SGarrett D'Amore
11394bb7efa7SGarrett D'Amore /*
11404bb7efa7SGarrett D'Amore * Timeout clock. We can calculate this using the following
11414bb7efa7SGarrett D'Amore * formula:
11424bb7efa7SGarrett D'Amore *
11434bb7efa7SGarrett D'Amore * (1000000 usec/1sec) * (1sec/tmoutclk) * base factor = clock time
11444bb7efa7SGarrett D'Amore *
11454bb7efa7SGarrett D'Amore * Clock time is the length of the base clock in usecs.
11464bb7efa7SGarrett D'Amore *
11474bb7efa7SGarrett D'Amore * Our base factor is 2^13, which is the shortest clock we
11484bb7efa7SGarrett D'Amore * can count.
11494bb7efa7SGarrett D'Amore *
11504bb7efa7SGarrett D'Amore * To simplify the math and avoid overflow, we cancel out the
11514bb7efa7SGarrett D'Amore * zeros for kHz or MHz. Since we want to wait more clocks, not
11524bb7efa7SGarrett D'Amore * less, on error, we truncate the result rather than rounding
11534bb7efa7SGarrett D'Amore * up.
11544bb7efa7SGarrett D'Amore */
11554bb7efa7SGarrett D'Amore clk = ((capab & CAPAB_TIMEOUT_FREQ_MASK) >> CAPAB_TIMEOUT_FREQ_SHIFT);
11564bb7efa7SGarrett D'Amore if ((ss->ss_baseclk == 0) || (clk == 0)) {
11574bb7efa7SGarrett D'Amore cmn_err(CE_WARN, "Unable to determine clock frequencies");
11584bb7efa7SGarrett D'Amore return (DDI_FAILURE);
11594bb7efa7SGarrett D'Amore }
11604bb7efa7SGarrett D'Amore
11614bb7efa7SGarrett D'Amore if (capab & CAPAB_TIMEOUT_UNITS) {
11624bb7efa7SGarrett D'Amore /* MHz */
11634bb7efa7SGarrett D'Amore ss->ss_tmusecs = (1 << 13) / clk;
11644bb7efa7SGarrett D'Amore clk *= 1000000;
11654bb7efa7SGarrett D'Amore } else {
11664bb7efa7SGarrett D'Amore /* kHz */
11674bb7efa7SGarrett D'Amore ss->ss_tmusecs = (1000 * (1 << 13)) / clk;
11684bb7efa7SGarrett D'Amore clk *= 1000;
11694bb7efa7SGarrett D'Amore }
11704bb7efa7SGarrett D'Amore
11714bb7efa7SGarrett D'Amore /*
11724bb7efa7SGarrett D'Amore * Calculation of the timeout.
11734bb7efa7SGarrett D'Amore *
11744bb7efa7SGarrett D'Amore * SDIO cards use a 1sec timeout, and SDHC cards use fixed
11754bb7efa7SGarrett D'Amore * 100msec for read and 250 msec for write.
11764bb7efa7SGarrett D'Amore *
11774bb7efa7SGarrett D'Amore * Legacy cards running at 375kHz have a worst case of about
11784bb7efa7SGarrett D'Amore * 15 seconds. Running at 25MHz (the standard speed) it is
11794bb7efa7SGarrett D'Amore * about 100msec for read, and about 3.2 sec for write.
11804bb7efa7SGarrett D'Amore * Typical values are 1/100th that, or about 1msec for read,
11814bb7efa7SGarrett D'Amore * and 32 msec for write.
11824bb7efa7SGarrett D'Amore *
11834bb7efa7SGarrett D'Amore * No transaction at full speed should ever take more than 4
11844bb7efa7SGarrett D'Amore * seconds. (Some slow legacy cards might have trouble, but
11854bb7efa7SGarrett D'Amore * we'll worry about them if they ever are seen. Nobody wants
11864bb7efa7SGarrett D'Amore * to wait 4 seconds to access a single block anyway!)
11874bb7efa7SGarrett D'Amore *
11884bb7efa7SGarrett D'Amore * To get to 4 seconds, we continuously double usec until we
11894bb7efa7SGarrett D'Amore * get to the maximum value, or a timeout greater than 4
11904bb7efa7SGarrett D'Amore * seconds.
11914bb7efa7SGarrett D'Amore *
11924bb7efa7SGarrett D'Amore * Note that for high-speed timeout clocks, we might not be
11934bb7efa7SGarrett D'Amore * able to get to the full 4 seconds. E.g. with a 48MHz
11944bb7efa7SGarrett D'Amore * timeout clock, we can only get to about 2.8 seconds. Its
11954bb7efa7SGarrett D'Amore * possible that there could be some slow MMC cards that will
11964bb7efa7SGarrett D'Amore * timeout at this clock rate, but it seems unlikely. (The
11974bb7efa7SGarrett D'Amore * device would have to be pressing the very worst times,
11984bb7efa7SGarrett D'Amore * against the 100-fold "permissive" window allowed, and
11994bb7efa7SGarrett D'Amore * running at only 12.5MHz.)
12004bb7efa7SGarrett D'Amore *
12014bb7efa7SGarrett D'Amore * XXX: this could easily be a tunable. Someone dealing with only
12024bb7efa7SGarrett D'Amore * reasonable cards could set this to just 1 second.
12034bb7efa7SGarrett D'Amore */
12044bb7efa7SGarrett D'Amore for (ss->ss_tmoutclk = 0; ss->ss_tmoutclk < 14; ss->ss_tmoutclk++) {
12054bb7efa7SGarrett D'Amore if ((ss->ss_tmusecs * (1 << ss->ss_tmoutclk)) >= 4000000) {
12064bb7efa7SGarrett D'Amore break;
12074bb7efa7SGarrett D'Amore }
12084bb7efa7SGarrett D'Amore }
12094bb7efa7SGarrett D'Amore
12104bb7efa7SGarrett D'Amore /*
12114bb7efa7SGarrett D'Amore * Enable slot interrupts.
12124bb7efa7SGarrett D'Amore */
12134bb7efa7SGarrett D'Amore sdhost_enable_interrupts(ss);
12144bb7efa7SGarrett D'Amore
12154bb7efa7SGarrett D'Amore return (DDI_SUCCESS);
12164bb7efa7SGarrett D'Amore }
12174bb7efa7SGarrett D'Amore
12184bb7efa7SGarrett D'Amore void
sdhost_uninit_slot(sdhost_t * shp,int num)12194bb7efa7SGarrett D'Amore sdhost_uninit_slot(sdhost_t *shp, int num)
12204bb7efa7SGarrett D'Amore {
12214bb7efa7SGarrett D'Amore sdslot_t *ss;
12224bb7efa7SGarrett D'Amore
12234bb7efa7SGarrett D'Amore ss = &shp->sh_slots[num];
12244bb7efa7SGarrett D'Amore
1225c8ca7eb8SGarrett D'Amore if (ss->ss_acch != NULL)
1226c8ca7eb8SGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
12274bb7efa7SGarrett D'Amore
1228c8ca7eb8SGarrett D'Amore if (ss->ss_bufdmac.dmac_address)
12299a5113a6SGarrett D'Amore (void) ddi_dma_unbind_handle(ss->ss_bufdmah);
1230c8ca7eb8SGarrett D'Amore
1231c8ca7eb8SGarrett D'Amore if (ss->ss_bufacch != NULL)
12329a5113a6SGarrett D'Amore ddi_dma_mem_free(&ss->ss_bufacch);
1233c8ca7eb8SGarrett D'Amore
1234c8ca7eb8SGarrett D'Amore if (ss->ss_bufdmah != NULL)
12359a5113a6SGarrett D'Amore ddi_dma_free_handle(&ss->ss_bufdmah);
1236c8ca7eb8SGarrett D'Amore
1237c8ca7eb8SGarrett D'Amore if (ss->ss_ksp != NULL)
12389a5113a6SGarrett D'Amore kstat_delete(ss->ss_ksp);
12399a5113a6SGarrett D'Amore
1240c8ca7eb8SGarrett D'Amore if (ss->ss_acch != NULL)
1241c8ca7eb8SGarrett D'Amore ddi_regs_map_free(&ss->ss_acch);
1242c8ca7eb8SGarrett D'Amore
1243c8ca7eb8SGarrett D'Amore if (ss->ss_num != -1)
1244c8ca7eb8SGarrett D'Amore mutex_destroy(&ss->ss_lock);
12454bb7efa7SGarrett D'Amore }
12464bb7efa7SGarrett D'Amore
12474bb7efa7SGarrett D'Amore void
sdhost_get_response(sdslot_t * ss,sda_cmd_t * cmdp)12484bb7efa7SGarrett D'Amore sdhost_get_response(sdslot_t *ss, sda_cmd_t *cmdp)
12494bb7efa7SGarrett D'Amore {
12504bb7efa7SGarrett D'Amore uint32_t *resp = cmdp->sc_response;
12514bb7efa7SGarrett D'Amore int i;
12524bb7efa7SGarrett D'Amore
12534bb7efa7SGarrett D'Amore resp[0] = GET32(ss, REG_RESP1);
12544bb7efa7SGarrett D'Amore resp[1] = GET32(ss, REG_RESP2);
12554bb7efa7SGarrett D'Amore resp[2] = GET32(ss, REG_RESP3);
12564bb7efa7SGarrett D'Amore resp[3] = GET32(ss, REG_RESP4);
12574bb7efa7SGarrett D'Amore
12584bb7efa7SGarrett D'Amore /*
12594bb7efa7SGarrett D'Amore * Response 2 is goofy because the host drops the low
12604bb7efa7SGarrett D'Amore * order CRC bits. This makes it a bit awkward, so we
12614bb7efa7SGarrett D'Amore * have to shift the bits to make it work out right.
12624bb7efa7SGarrett D'Amore *
12634bb7efa7SGarrett D'Amore * Note that the framework expects the 32 bit
12644bb7efa7SGarrett D'Amore * words to be ordered in LE fashion. (The
12654bb7efa7SGarrett D'Amore * bits within the words are in native order).
12664bb7efa7SGarrett D'Amore */
12674bb7efa7SGarrett D'Amore if (cmdp->sc_rtype == R2) {
12684bb7efa7SGarrett D'Amore for (i = 3; i > 0; i--) {
12694bb7efa7SGarrett D'Amore resp[i] <<= 8;
12704bb7efa7SGarrett D'Amore resp[i] |= (resp[i - 1] >> 24);
12714bb7efa7SGarrett D'Amore }
12724bb7efa7SGarrett D'Amore resp[0] <<= 8;
12734bb7efa7SGarrett D'Amore }
12744bb7efa7SGarrett D'Amore }
12754bb7efa7SGarrett D'Amore
12764bb7efa7SGarrett D'Amore sda_err_t
sdhost_wait_cmd(sdslot_t * ss,sda_cmd_t * cmdp)12774bb7efa7SGarrett D'Amore sdhost_wait_cmd(sdslot_t *ss, sda_cmd_t *cmdp)
12784bb7efa7SGarrett D'Amore {
12794bb7efa7SGarrett D'Amore int i;
12804bb7efa7SGarrett D'Amore uint16_t errs;
1281f2b90c3cSGarrett D'Amore sda_err_t rv;
12824bb7efa7SGarrett D'Amore
12834bb7efa7SGarrett D'Amore /*
12844bb7efa7SGarrett D'Amore * Worst case for 100kHz timeout is 2msec (200 clocks), we add
12854bb7efa7SGarrett D'Amore * a tiny bit for safety. (Generally timeout will be far, far
12864bb7efa7SGarrett D'Amore * less than that.)
12874bb7efa7SGarrett D'Amore *
12884bb7efa7SGarrett D'Amore * Note that at more typical 12MHz (and normally it will be
12894bb7efa7SGarrett D'Amore * even faster than that!) that the device timeout is only
12904bb7efa7SGarrett D'Amore * 16.67 usec. We could be smarter and reduce the delay time,
12914bb7efa7SGarrett D'Amore * but that would require putting more intelligence into the
12924bb7efa7SGarrett D'Amore * code, and we don't expect CMD timeout to normally occur
12934bb7efa7SGarrett D'Amore * except during initialization. (At which time we need the
12944bb7efa7SGarrett D'Amore * full timeout anyway.)
12954bb7efa7SGarrett D'Amore *
12964bb7efa7SGarrett D'Amore * Checking the ERR_STAT will normally cause the timeout to
12974bb7efa7SGarrett D'Amore * terminate to finish early if the device is healthy, anyway.
12984bb7efa7SGarrett D'Amore */
12994bb7efa7SGarrett D'Amore
13004bb7efa7SGarrett D'Amore for (i = 3000; i > 0; i -= 5) {
13014bb7efa7SGarrett D'Amore if (GET16(ss, REG_INT_STAT) & INT_CMD) {
13024bb7efa7SGarrett D'Amore
13034bb7efa7SGarrett D'Amore PUT16(ss, REG_INT_STAT, INT_CMD);
13044bb7efa7SGarrett D'Amore
13054bb7efa7SGarrett D'Amore /* command completed */
13064bb7efa7SGarrett D'Amore sdhost_get_response(ss, cmdp);
13074bb7efa7SGarrett D'Amore return (SDA_EOK);
13084bb7efa7SGarrett D'Amore }
13094bb7efa7SGarrett D'Amore
13104bb7efa7SGarrett D'Amore if ((errs = (GET16(ss, REG_ERR_STAT) & ERR_CMD)) != 0) {
13114bb7efa7SGarrett D'Amore PUT16(ss, REG_ERR_STAT, errs);
13124bb7efa7SGarrett D'Amore
13134bb7efa7SGarrett D'Amore /* command timeout isn't a host failure */
13144bb7efa7SGarrett D'Amore if ((errs & ERR_CMD_TMO) == ERR_CMD_TMO) {
1315f2b90c3cSGarrett D'Amore rv = SDA_ETIME;
1316f2b90c3cSGarrett D'Amore } else if ((errs & ERR_CMD_CRC) == ERR_CMD_CRC) {
1317f2b90c3cSGarrett D'Amore rv = SDA_ECRC7;
13184bb7efa7SGarrett D'Amore } else {
1319f2b90c3cSGarrett D'Amore rv = SDA_EPROTO;
13204bb7efa7SGarrett D'Amore }
1321f2b90c3cSGarrett D'Amore goto error;
13224bb7efa7SGarrett D'Amore }
13234bb7efa7SGarrett D'Amore
13244bb7efa7SGarrett D'Amore drv_usecwait(5);
13254bb7efa7SGarrett D'Amore }
13264bb7efa7SGarrett D'Amore
1327f2b90c3cSGarrett D'Amore rv = SDA_ETIME;
1328f2b90c3cSGarrett D'Amore
1329f2b90c3cSGarrett D'Amore error:
1330f2b90c3cSGarrett D'Amore /*
1331f2b90c3cSGarrett D'Amore * NB: We need to soft reset the CMD and DAT
1332f2b90c3cSGarrett D'Amore * lines after a failure of this sort.
1333f2b90c3cSGarrett D'Amore */
1334f2b90c3cSGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
1335f2b90c3cSGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
1336f2b90c3cSGarrett D'Amore
1337f2b90c3cSGarrett D'Amore return (rv);
13384bb7efa7SGarrett D'Amore }
13394bb7efa7SGarrett D'Amore
13404bb7efa7SGarrett D'Amore sda_err_t
sdhost_poll(void * arg)13414bb7efa7SGarrett D'Amore sdhost_poll(void *arg)
13424bb7efa7SGarrett D'Amore {
13434bb7efa7SGarrett D'Amore sdslot_t *ss = arg;
13444bb7efa7SGarrett D'Amore
13454bb7efa7SGarrett D'Amore (void) sdhost_slot_intr(ss);
13464bb7efa7SGarrett D'Amore return (SDA_EOK);
13474bb7efa7SGarrett D'Amore }
13484bb7efa7SGarrett D'Amore
13494bb7efa7SGarrett D'Amore sda_err_t
sdhost_cmd(void * arg,sda_cmd_t * cmdp)13504bb7efa7SGarrett D'Amore sdhost_cmd(void *arg, sda_cmd_t *cmdp)
13514bb7efa7SGarrett D'Amore {
13524bb7efa7SGarrett D'Amore sdslot_t *ss = arg;
13534bb7efa7SGarrett D'Amore uint16_t command;
13544bb7efa7SGarrett D'Amore uint16_t mode;
13554bb7efa7SGarrett D'Amore sda_err_t rv;
13564bb7efa7SGarrett D'Amore
13574bb7efa7SGarrett D'Amore /*
13584bb7efa7SGarrett D'Amore * Command register:
13594bb7efa7SGarrett D'Amore * bit 13-8 = command index
13604bb7efa7SGarrett D'Amore * bit 7-6 = command type (always zero for us!)
13614bb7efa7SGarrett D'Amore * bit 5 = data present select
13624bb7efa7SGarrett D'Amore * bit 4 = command index check (always on!)
13634bb7efa7SGarrett D'Amore * bit 3 = command CRC check enable
13644bb7efa7SGarrett D'Amore * bit 2 = reserved
13654bb7efa7SGarrett D'Amore * bit 1-0 = response type
13664bb7efa7SGarrett D'Amore */
13674bb7efa7SGarrett D'Amore
13684bb7efa7SGarrett D'Amore command = ((uint16_t)cmdp->sc_index << 8);
13694bb7efa7SGarrett D'Amore command |= COMMAND_TYPE_NORM |
13704bb7efa7SGarrett D'Amore COMMAND_INDEX_CHECK_EN | COMMAND_CRC_CHECK_EN;
13714bb7efa7SGarrett D'Amore
13724bb7efa7SGarrett D'Amore switch (cmdp->sc_rtype) {
13734bb7efa7SGarrett D'Amore case R0:
13744bb7efa7SGarrett D'Amore command |= COMMAND_RESP_NONE;
13754bb7efa7SGarrett D'Amore break;
13764bb7efa7SGarrett D'Amore case R1:
13774bb7efa7SGarrett D'Amore case R5:
13784bb7efa7SGarrett D'Amore case R6:
13794bb7efa7SGarrett D'Amore case R7:
13804bb7efa7SGarrett D'Amore command |= COMMAND_RESP_48;
13814bb7efa7SGarrett D'Amore break;
13824bb7efa7SGarrett D'Amore case R1b:
13834bb7efa7SGarrett D'Amore case R5b:
13844bb7efa7SGarrett D'Amore command |= COMMAND_RESP_48_BUSY;
13854bb7efa7SGarrett D'Amore break;
13864bb7efa7SGarrett D'Amore case R2:
13874bb7efa7SGarrett D'Amore command |= COMMAND_RESP_136;
13884bb7efa7SGarrett D'Amore command &= ~(COMMAND_INDEX_CHECK_EN | COMMAND_CRC_CHECK_EN);
13894bb7efa7SGarrett D'Amore break;
13904bb7efa7SGarrett D'Amore case R3:
13914bb7efa7SGarrett D'Amore case R4:
13924bb7efa7SGarrett D'Amore command |= COMMAND_RESP_48;
13934bb7efa7SGarrett D'Amore command &= ~COMMAND_CRC_CHECK_EN;
13944bb7efa7SGarrett D'Amore command &= ~COMMAND_INDEX_CHECK_EN;
13954bb7efa7SGarrett D'Amore break;
13964bb7efa7SGarrett D'Amore default:
13974bb7efa7SGarrett D'Amore return (SDA_EINVAL);
13984bb7efa7SGarrett D'Amore }
13994bb7efa7SGarrett D'Amore
14004bb7efa7SGarrett D'Amore mutex_enter(&ss->ss_lock);
14014bb7efa7SGarrett D'Amore if (ss->ss_suspended) {
14024bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
14034bb7efa7SGarrett D'Amore return (SDA_ESUSPENDED);
14044bb7efa7SGarrett D'Amore }
14054bb7efa7SGarrett D'Amore
14064bb7efa7SGarrett D'Amore if (cmdp->sc_nblks != 0) {
14074bb7efa7SGarrett D'Amore uint16_t blksz;
14084bb7efa7SGarrett D'Amore uint16_t nblks;
14094bb7efa7SGarrett D'Amore
14104bb7efa7SGarrett D'Amore blksz = cmdp->sc_blksz;
14114bb7efa7SGarrett D'Amore nblks = cmdp->sc_nblks;
14124bb7efa7SGarrett D'Amore
14134bb7efa7SGarrett D'Amore /*
14144bb7efa7SGarrett D'Amore * Ensure that we have good data.
14154bb7efa7SGarrett D'Amore */
14164bb7efa7SGarrett D'Amore if ((blksz < 1) || (blksz > 2048)) {
14174bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
14184bb7efa7SGarrett D'Amore return (SDA_EINVAL);
14194bb7efa7SGarrett D'Amore }
14204bb7efa7SGarrett D'Amore command |= COMMAND_DATA_PRESENT;
14214bb7efa7SGarrett D'Amore
14224bb7efa7SGarrett D'Amore ss->ss_blksz = blksz;
14234bb7efa7SGarrett D'Amore
14249a5113a6SGarrett D'Amore ss->ss_kvaddr = (void *)cmdp->sc_kvaddr;
14259a5113a6SGarrett D'Amore ss->ss_rcnt = 0;
14269a5113a6SGarrett D'Amore ss->ss_resid = 0;
14279a5113a6SGarrett D'Amore
14284bb7efa7SGarrett D'Amore /*
14294bb7efa7SGarrett D'Amore * Only SDMA for now. We can investigate ADMA2 later.
14304bb7efa7SGarrett D'Amore * (Right now we don't have ADMA2 capable hardware.)
14319a5113a6SGarrett D'Amore * We always use a bounce buffer, which solves weird
14329a5113a6SGarrett D'Amore * problems with certain controllers. Doing this with
14339a5113a6SGarrett D'Amore * a large contiguous buffer may be faster than
14349a5113a6SGarrett D'Amore * servicing all the little per-page interrupts
14359a5113a6SGarrett D'Amore * anyway. (Bcopy of 64 K vs. 16 interrupts.)
14364bb7efa7SGarrett D'Amore */
14374bb7efa7SGarrett D'Amore if (((ss->ss_capab & CAPAB_SDMA) != 0) &&
14389a5113a6SGarrett D'Amore ((ss->ss_flags & SDFLAG_FORCE_PIO) == 0) &&
14399a5113a6SGarrett D'Amore ((blksz * nblks) <= SDHOST_BOUNCESZ)) {
14409a5113a6SGarrett D'Amore
14419a5113a6SGarrett D'Amore if (cmdp->sc_flags & SDA_CMDF_WRITE) {
14429a5113a6SGarrett D'Amore /*
14439a5113a6SGarrett D'Amore * if we're writing, prepare initial round
14449a5113a6SGarrett D'Amore * of data
14459a5113a6SGarrett D'Amore */
14469a5113a6SGarrett D'Amore bcopy(cmdp->sc_kvaddr, ss->ss_bounce,
14479a5113a6SGarrett D'Amore nblks * blksz);
14489a5113a6SGarrett D'Amore (void) ddi_dma_sync(ss->ss_bufdmah, 0, 0,
14499a5113a6SGarrett D'Amore DDI_DMA_SYNC_FORDEV);
14509a5113a6SGarrett D'Amore } else {
14519a5113a6SGarrett D'Amore ss->ss_rcnt = nblks * blksz;
14529a5113a6SGarrett D'Amore }
14539a5113a6SGarrett D'Amore PUT32(ss, REG_SDMA_ADDR, ss->ss_bufdmac.dmac_address);
14544bb7efa7SGarrett D'Amore mode = XFR_MODE_DMA_EN;
14559a5113a6SGarrett D'Amore PUT16(ss, REG_BLKSZ, BLKSZ_BOUNDARY_512K | blksz);
14569a5113a6SGarrett D'Amore ss->ss_ndma++;
14574bb7efa7SGarrett D'Amore
14584bb7efa7SGarrett D'Amore } else {
14594bb7efa7SGarrett D'Amore mode = 0;
14609a5113a6SGarrett D'Amore ss->ss_npio++;
14619a5113a6SGarrett D'Amore ss->ss_resid = nblks;
14624bb7efa7SGarrett D'Amore PUT16(ss, REG_BLKSZ, blksz);
14634bb7efa7SGarrett D'Amore }
14644bb7efa7SGarrett D'Amore
14654bb7efa7SGarrett D'Amore if (nblks > 1) {
14664bb7efa7SGarrett D'Amore mode |= XFR_MODE_MULTI | XFR_MODE_COUNT;
14674bb7efa7SGarrett D'Amore if (cmdp->sc_flags & SDA_CMDF_AUTO_CMD12)
14684bb7efa7SGarrett D'Amore mode |= XFR_MODE_AUTO_CMD12;
14699a5113a6SGarrett D'Amore ss->ss_nmulti++;
14704bb7efa7SGarrett D'Amore }
14714bb7efa7SGarrett D'Amore if ((cmdp->sc_flags & SDA_CMDF_READ) != 0) {
14724bb7efa7SGarrett D'Amore mode |= XFR_MODE_READ;
14739a5113a6SGarrett D'Amore ss->ss_ixfr++;
14749a5113a6SGarrett D'Amore ss->ss_ibytes += nblks * blksz;
14759a5113a6SGarrett D'Amore } else {
14769a5113a6SGarrett D'Amore ss->ss_oxfr++;
14779a5113a6SGarrett D'Amore ss->ss_obytes += nblks * blksz;
14784bb7efa7SGarrett D'Amore }
14794bb7efa7SGarrett D'Amore
14804bb7efa7SGarrett D'Amore ss->ss_mode = mode;
14814bb7efa7SGarrett D'Amore
14824bb7efa7SGarrett D'Amore PUT8(ss, REG_TIMEOUT_CONTROL, ss->ss_tmoutclk);
14834bb7efa7SGarrett D'Amore PUT16(ss, REG_BLOCK_COUNT, nblks);
14844bb7efa7SGarrett D'Amore PUT16(ss, REG_XFR_MODE, mode);
14854bb7efa7SGarrett D'Amore }
14864bb7efa7SGarrett D'Amore
14874bb7efa7SGarrett D'Amore PUT32(ss, REG_ARGUMENT, cmdp->sc_argument);
14884bb7efa7SGarrett D'Amore PUT16(ss, REG_COMMAND, command);
14894bb7efa7SGarrett D'Amore
14909a5113a6SGarrett D'Amore ss->ss_ncmd++;
14914bb7efa7SGarrett D'Amore rv = sdhost_wait_cmd(ss, cmdp);
14924bb7efa7SGarrett D'Amore
14934bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
14944bb7efa7SGarrett D'Amore
14954bb7efa7SGarrett D'Amore return (rv);
14964bb7efa7SGarrett D'Amore }
14974bb7efa7SGarrett D'Amore
14984bb7efa7SGarrett D'Amore sda_err_t
sdhost_getprop(void * arg,sda_prop_t prop,uint32_t * val)14994bb7efa7SGarrett D'Amore sdhost_getprop(void *arg, sda_prop_t prop, uint32_t *val)
15004bb7efa7SGarrett D'Amore {
15014bb7efa7SGarrett D'Amore sdslot_t *ss = arg;
15024bb7efa7SGarrett D'Amore sda_err_t rv = 0;
15034bb7efa7SGarrett D'Amore
15044bb7efa7SGarrett D'Amore mutex_enter(&ss->ss_lock);
15054bb7efa7SGarrett D'Amore
15064bb7efa7SGarrett D'Amore if (ss->ss_suspended) {
15074bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
15084bb7efa7SGarrett D'Amore return (SDA_ESUSPENDED);
15094bb7efa7SGarrett D'Amore }
15104bb7efa7SGarrett D'Amore switch (prop) {
15114bb7efa7SGarrett D'Amore case SDA_PROP_INSERTED:
15124bb7efa7SGarrett D'Amore if (CHECK_STATE(ss, CARD_INSERTED)) {
15134bb7efa7SGarrett D'Amore *val = B_TRUE;
15144bb7efa7SGarrett D'Amore } else {
15154bb7efa7SGarrett D'Amore *val = B_FALSE;
15164bb7efa7SGarrett D'Amore }
15174bb7efa7SGarrett D'Amore break;
15184bb7efa7SGarrett D'Amore
15194bb7efa7SGarrett D'Amore case SDA_PROP_WPROTECT:
15204bb7efa7SGarrett D'Amore if (CHECK_STATE(ss, WRITE_ENABLE)) {
15214bb7efa7SGarrett D'Amore *val = B_FALSE;
15224bb7efa7SGarrett D'Amore } else {
15234bb7efa7SGarrett D'Amore *val = B_TRUE;
15244bb7efa7SGarrett D'Amore }
15254bb7efa7SGarrett D'Amore break;
15264bb7efa7SGarrett D'Amore
15274bb7efa7SGarrett D'Amore case SDA_PROP_OCR:
15284bb7efa7SGarrett D'Amore *val = ss->ss_ocr;
15294bb7efa7SGarrett D'Amore break;
15304bb7efa7SGarrett D'Amore
15314bb7efa7SGarrett D'Amore case SDA_PROP_CLOCK:
15324bb7efa7SGarrett D'Amore *val = ss->ss_cardclk;
15334bb7efa7SGarrett D'Amore break;
15344bb7efa7SGarrett D'Amore
15354bb7efa7SGarrett D'Amore case SDA_PROP_CAP_HISPEED:
15364bb7efa7SGarrett D'Amore if ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0) {
15374bb7efa7SGarrett D'Amore *val = B_TRUE;
15384bb7efa7SGarrett D'Amore } else {
15394bb7efa7SGarrett D'Amore *val = B_FALSE;
15404bb7efa7SGarrett D'Amore }
15414bb7efa7SGarrett D'Amore break;
15424bb7efa7SGarrett D'Amore
15434bb7efa7SGarrett D'Amore case SDA_PROP_CAP_4BITS:
15444bb7efa7SGarrett D'Amore *val = B_TRUE;
15454bb7efa7SGarrett D'Amore break;
15464bb7efa7SGarrett D'Amore
15474bb7efa7SGarrett D'Amore case SDA_PROP_CAP_NOPIO:
15489a5113a6SGarrett D'Amore /*
15499a5113a6SGarrett D'Amore * We might have to use PIO for buffers that don't
15509a5113a6SGarrett D'Amore * have reasonable alignments. A few controllers seem
15519a5113a6SGarrett D'Amore * not to deal with granularity or alignments of
15529a5113a6SGarrett D'Amore * something other 32-bits.
15539a5113a6SGarrett D'Amore */
15549a5113a6SGarrett D'Amore *val = B_FALSE;
15554bb7efa7SGarrett D'Amore break;
15564bb7efa7SGarrett D'Amore
15574bb7efa7SGarrett D'Amore case SDA_PROP_CAP_INTR:
15584bb7efa7SGarrett D'Amore case SDA_PROP_CAP_8BITS:
15594bb7efa7SGarrett D'Amore *val = B_FALSE;
15604bb7efa7SGarrett D'Amore break;
15614bb7efa7SGarrett D'Amore
15624bb7efa7SGarrett D'Amore default:
15634bb7efa7SGarrett D'Amore rv = SDA_ENOTSUP;
15644bb7efa7SGarrett D'Amore break;
15654bb7efa7SGarrett D'Amore }
15664bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
15674bb7efa7SGarrett D'Amore
15684bb7efa7SGarrett D'Amore return (rv);
15694bb7efa7SGarrett D'Amore }
15704bb7efa7SGarrett D'Amore
15714bb7efa7SGarrett D'Amore sda_err_t
sdhost_setprop(void * arg,sda_prop_t prop,uint32_t val)15724bb7efa7SGarrett D'Amore sdhost_setprop(void *arg, sda_prop_t prop, uint32_t val)
15734bb7efa7SGarrett D'Amore {
15744bb7efa7SGarrett D'Amore sdslot_t *ss = arg;
15754bb7efa7SGarrett D'Amore sda_err_t rv = SDA_EOK;
15764bb7efa7SGarrett D'Amore
15774bb7efa7SGarrett D'Amore mutex_enter(&ss->ss_lock);
15784bb7efa7SGarrett D'Amore
15794bb7efa7SGarrett D'Amore if (ss->ss_suspended) {
15804bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
15814bb7efa7SGarrett D'Amore return (SDA_ESUSPENDED);
15824bb7efa7SGarrett D'Amore }
15834bb7efa7SGarrett D'Amore
15844bb7efa7SGarrett D'Amore switch (prop) {
15854bb7efa7SGarrett D'Amore case SDA_PROP_LED:
15864bb7efa7SGarrett D'Amore if (val) {
15874bb7efa7SGarrett D'Amore SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_LED_ON);
15884bb7efa7SGarrett D'Amore } else {
15894bb7efa7SGarrett D'Amore CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_LED_ON);
15904bb7efa7SGarrett D'Amore }
15914bb7efa7SGarrett D'Amore break;
15924bb7efa7SGarrett D'Amore
15934bb7efa7SGarrett D'Amore case SDA_PROP_CLOCK:
15944bb7efa7SGarrett D'Amore rv = sdhost_set_clock(arg, val);
15954bb7efa7SGarrett D'Amore break;
15964bb7efa7SGarrett D'Amore
15974bb7efa7SGarrett D'Amore case SDA_PROP_BUSWIDTH:
15984bb7efa7SGarrett D'Amore switch (val) {
15994bb7efa7SGarrett D'Amore case 1:
16009a5113a6SGarrett D'Amore ss->ss_width = val;
16014bb7efa7SGarrett D'Amore CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_DATA_WIDTH);
16024bb7efa7SGarrett D'Amore break;
16034bb7efa7SGarrett D'Amore case 4:
16049a5113a6SGarrett D'Amore ss->ss_width = val;
16054bb7efa7SGarrett D'Amore SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_DATA_WIDTH);
16064bb7efa7SGarrett D'Amore break;
16074bb7efa7SGarrett D'Amore default:
16084bb7efa7SGarrett D'Amore rv = SDA_EINVAL;
16094bb7efa7SGarrett D'Amore }
16104bb7efa7SGarrett D'Amore break;
16114bb7efa7SGarrett D'Amore
16124bb7efa7SGarrett D'Amore case SDA_PROP_OCR:
16134bb7efa7SGarrett D'Amore val &= ss->ss_ocr;
16144bb7efa7SGarrett D'Amore
16154bb7efa7SGarrett D'Amore if (val & OCR_17_18V) {
16164bb7efa7SGarrett D'Amore PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_18V);
16174bb7efa7SGarrett D'Amore PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_18V |
16184bb7efa7SGarrett D'Amore POWER_CONTROL_BUS_POWER);
16194bb7efa7SGarrett D'Amore } else if (val & OCR_29_30V) {
16204bb7efa7SGarrett D'Amore PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_30V);
16214bb7efa7SGarrett D'Amore PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_30V |
16224bb7efa7SGarrett D'Amore POWER_CONTROL_BUS_POWER);
16234bb7efa7SGarrett D'Amore } else if (val & OCR_32_33V) {
16244bb7efa7SGarrett D'Amore PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_33V);
16254bb7efa7SGarrett D'Amore PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_33V |
16264bb7efa7SGarrett D'Amore POWER_CONTROL_BUS_POWER);
16274bb7efa7SGarrett D'Amore } else if (val == 0) {
16284bb7efa7SGarrett D'Amore /* turn off power */
16294bb7efa7SGarrett D'Amore PUT8(ss, REG_POWER_CONTROL, 0);
16304bb7efa7SGarrett D'Amore } else {
16314bb7efa7SGarrett D'Amore rv = SDA_EINVAL;
16324bb7efa7SGarrett D'Amore }
16334bb7efa7SGarrett D'Amore break;
16344bb7efa7SGarrett D'Amore
16354bb7efa7SGarrett D'Amore case SDA_PROP_HISPEED:
16364bb7efa7SGarrett D'Amore if (val) {
16374bb7efa7SGarrett D'Amore SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
16384bb7efa7SGarrett D'Amore } else {
16394bb7efa7SGarrett D'Amore CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
16404bb7efa7SGarrett D'Amore }
16414bb7efa7SGarrett D'Amore /* give clocks time to settle */
16424bb7efa7SGarrett D'Amore drv_usecwait(10);
16434bb7efa7SGarrett D'Amore break;
16444bb7efa7SGarrett D'Amore
16454bb7efa7SGarrett D'Amore default:
16464bb7efa7SGarrett D'Amore rv = SDA_ENOTSUP;
16474bb7efa7SGarrett D'Amore break;
16484bb7efa7SGarrett D'Amore }
16494bb7efa7SGarrett D'Amore
16504bb7efa7SGarrett D'Amore /*
16514bb7efa7SGarrett D'Amore * Apparently some controllers (ENE) have issues with changing
16524bb7efa7SGarrett D'Amore * certain parameters (bus width seems to be one), requiring
16534bb7efa7SGarrett D'Amore * a reset of the DAT and CMD lines.
16544bb7efa7SGarrett D'Amore */
16554bb7efa7SGarrett D'Amore if (rv == SDA_EOK) {
16564bb7efa7SGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
16574bb7efa7SGarrett D'Amore (void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
16584bb7efa7SGarrett D'Amore }
16594bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
16604bb7efa7SGarrett D'Amore return (rv);
16614bb7efa7SGarrett D'Amore }
16624bb7efa7SGarrett D'Amore
16634bb7efa7SGarrett D'Amore sda_err_t
sdhost_reset(void * arg)16644bb7efa7SGarrett D'Amore sdhost_reset(void *arg)
16654bb7efa7SGarrett D'Amore {
16664bb7efa7SGarrett D'Amore sdslot_t *ss = arg;
16674bb7efa7SGarrett D'Amore
16684bb7efa7SGarrett D'Amore mutex_enter(&ss->ss_lock);
16694bb7efa7SGarrett D'Amore if (!ss->ss_suspended) {
16704bb7efa7SGarrett D'Amore if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK) {
16714bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
16724bb7efa7SGarrett D'Amore return (SDA_ETIME);
16734bb7efa7SGarrett D'Amore }
16744bb7efa7SGarrett D'Amore sdhost_enable_interrupts(ss);
16754bb7efa7SGarrett D'Amore }
16764bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
16774bb7efa7SGarrett D'Amore return (SDA_EOK);
16784bb7efa7SGarrett D'Amore }
16794bb7efa7SGarrett D'Amore
16804bb7efa7SGarrett D'Amore sda_err_t
sdhost_halt(void * arg)16814bb7efa7SGarrett D'Amore sdhost_halt(void *arg)
16824bb7efa7SGarrett D'Amore {
16834bb7efa7SGarrett D'Amore sdslot_t *ss = arg;
16844bb7efa7SGarrett D'Amore
16854bb7efa7SGarrett D'Amore mutex_enter(&ss->ss_lock);
16864bb7efa7SGarrett D'Amore if (!ss->ss_suspended) {
16874bb7efa7SGarrett D'Amore sdhost_disable_interrupts(ss);
16884bb7efa7SGarrett D'Amore /* this has the side effect of removing power from the card */
16894bb7efa7SGarrett D'Amore if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK) {
16904bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
16914bb7efa7SGarrett D'Amore return (SDA_ETIME);
16924bb7efa7SGarrett D'Amore }
16934bb7efa7SGarrett D'Amore }
16944bb7efa7SGarrett D'Amore mutex_exit(&ss->ss_lock);
16954bb7efa7SGarrett D'Amore return (SDA_EOK);
16964bb7efa7SGarrett D'Amore }
1697