17302Sgdamore@opensolaris.org /*
27302Sgdamore@opensolaris.org * CDDL HEADER START
37302Sgdamore@opensolaris.org *
47302Sgdamore@opensolaris.org * The contents of this file are subject to the terms of the
57302Sgdamore@opensolaris.org * Common Development and Distribution License (the "License").
67302Sgdamore@opensolaris.org * You may not use this file except in compliance with the License.
77302Sgdamore@opensolaris.org *
87302Sgdamore@opensolaris.org * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97302Sgdamore@opensolaris.org * or http://www.opensolaris.org/os/licensing.
107302Sgdamore@opensolaris.org * See the License for the specific language governing permissions
117302Sgdamore@opensolaris.org * and limitations under the License.
127302Sgdamore@opensolaris.org *
137302Sgdamore@opensolaris.org * When distributing Covered Code, include this CDDL HEADER in each
147302Sgdamore@opensolaris.org * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157302Sgdamore@opensolaris.org * If applicable, add the following below this CDDL HEADER, with the
167302Sgdamore@opensolaris.org * fields enclosed by brackets "[]" replaced with your own identifying
177302Sgdamore@opensolaris.org * information: Portions Copyright [yyyy] [name of copyright owner]
187302Sgdamore@opensolaris.org *
197302Sgdamore@opensolaris.org * CDDL HEADER END
207302Sgdamore@opensolaris.org */
217302Sgdamore@opensolaris.org /*
22*12426Sgdamore@opensolaris.org * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
237302Sgdamore@opensolaris.org */
247302Sgdamore@opensolaris.org
257302Sgdamore@opensolaris.org #include "sdhost.h"
267302Sgdamore@opensolaris.org
278708Sgdamore@opensolaris.org typedef struct sdstats sdstats_t;
287302Sgdamore@opensolaris.org typedef struct sdslot sdslot_t;
297302Sgdamore@opensolaris.org typedef struct sdhost sdhost_t;
307302Sgdamore@opensolaris.org
318708Sgdamore@opensolaris.org struct sdstats {
328708Sgdamore@opensolaris.org kstat_named_t ks_ncmd;
338708Sgdamore@opensolaris.org kstat_named_t ks_ixfr;
348708Sgdamore@opensolaris.org kstat_named_t ks_oxfr;
358708Sgdamore@opensolaris.org kstat_named_t ks_ibytes;
368708Sgdamore@opensolaris.org kstat_named_t ks_obytes;
378708Sgdamore@opensolaris.org kstat_named_t ks_npio;
388708Sgdamore@opensolaris.org kstat_named_t ks_ndma;
398708Sgdamore@opensolaris.org kstat_named_t ks_nmulti;
408708Sgdamore@opensolaris.org kstat_named_t ks_baseclk;
418708Sgdamore@opensolaris.org kstat_named_t ks_cardclk;
428708Sgdamore@opensolaris.org kstat_named_t ks_tmusecs;
438708Sgdamore@opensolaris.org kstat_named_t ks_width;
448708Sgdamore@opensolaris.org kstat_named_t ks_flags;
458708Sgdamore@opensolaris.org kstat_named_t ks_capab;
468708Sgdamore@opensolaris.org };
478708Sgdamore@opensolaris.org
488708Sgdamore@opensolaris.org #define SDFLAG_FORCE_PIO (1U << 0)
498708Sgdamore@opensolaris.org #define SDFLAG_FORCE_DMA (1U << 1)
508708Sgdamore@opensolaris.org
517302Sgdamore@opensolaris.org /*
527302Sgdamore@opensolaris.org * Per slot state.
537302Sgdamore@opensolaris.org */
547302Sgdamore@opensolaris.org struct sdslot {
557302Sgdamore@opensolaris.org sda_host_t *ss_host;
567302Sgdamore@opensolaris.org int ss_num;
577302Sgdamore@opensolaris.org ddi_acc_handle_t ss_acch;
587302Sgdamore@opensolaris.org caddr_t ss_regva;
597302Sgdamore@opensolaris.org kmutex_t ss_lock;
607302Sgdamore@opensolaris.org uint8_t ss_tmoutclk;
617302Sgdamore@opensolaris.org uint32_t ss_ocr; /* OCR formatted voltages */
627302Sgdamore@opensolaris.org uint16_t ss_mode;
637302Sgdamore@opensolaris.org boolean_t ss_suspended;
648708Sgdamore@opensolaris.org sdstats_t ss_stats;
658708Sgdamore@opensolaris.org #define ss_ncmd ss_stats.ks_ncmd.value.ui64
668708Sgdamore@opensolaris.org #define ss_ixfr ss_stats.ks_ixfr.value.ui64
678708Sgdamore@opensolaris.org #define ss_oxfr ss_stats.ks_oxfr.value.ui64
688708Sgdamore@opensolaris.org #define ss_ibytes ss_stats.ks_ibytes.value.ui64
698708Sgdamore@opensolaris.org #define ss_obytes ss_stats.ks_obytes.value.ui64
708708Sgdamore@opensolaris.org #define ss_ndma ss_stats.ks_ndma.value.ui64
718708Sgdamore@opensolaris.org #define ss_npio ss_stats.ks_npio.value.ui64
728708Sgdamore@opensolaris.org #define ss_nmulti ss_stats.ks_nmulti.value.ui64
738708Sgdamore@opensolaris.org
748708Sgdamore@opensolaris.org #define ss_baseclk ss_stats.ks_baseclk.value.ui32
758708Sgdamore@opensolaris.org #define ss_cardclk ss_stats.ks_cardclk.value.ui32
768708Sgdamore@opensolaris.org #define ss_tmusecs ss_stats.ks_tmusecs.value.ui32
778708Sgdamore@opensolaris.org #define ss_width ss_stats.ks_width.value.ui32
788708Sgdamore@opensolaris.org #define ss_flags ss_stats.ks_flags.value.ui32
798708Sgdamore@opensolaris.org #define ss_capab ss_stats.ks_capab.value.ui32
808708Sgdamore@opensolaris.org kstat_t *ss_ksp;
817302Sgdamore@opensolaris.org
827302Sgdamore@opensolaris.org /*
837302Sgdamore@opensolaris.org * Command in progress
847302Sgdamore@opensolaris.org */
857302Sgdamore@opensolaris.org uint8_t *ss_kvaddr;
867302Sgdamore@opensolaris.org int ss_blksz;
877302Sgdamore@opensolaris.org uint16_t ss_resid; /* in blocks */
888708Sgdamore@opensolaris.org int ss_rcnt;
897302Sgdamore@opensolaris.org
907302Sgdamore@opensolaris.org /* scratch buffer, to receive extra PIO data */
918708Sgdamore@opensolaris.org caddr_t ss_bounce;
928708Sgdamore@opensolaris.org ddi_dma_handle_t ss_bufdmah;
938708Sgdamore@opensolaris.org ddi_acc_handle_t ss_bufacch;
948708Sgdamore@opensolaris.org ddi_dma_cookie_t ss_bufdmac;
957302Sgdamore@opensolaris.org };
967302Sgdamore@opensolaris.org
977302Sgdamore@opensolaris.org /*
988708Sgdamore@opensolaris.org * This allocates a rather large chunk of contiguous memory for DMA.
998708Sgdamore@opensolaris.org * But doing so means that we'll almost never have to resort to PIO.
1008708Sgdamore@opensolaris.org */
1018708Sgdamore@opensolaris.org #define SDHOST_BOUNCESZ 65536
1028708Sgdamore@opensolaris.org
1038708Sgdamore@opensolaris.org /*
1047302Sgdamore@opensolaris.org * Per controller state.
1057302Sgdamore@opensolaris.org */
1067302Sgdamore@opensolaris.org struct sdhost {
1077302Sgdamore@opensolaris.org int sh_numslots;
1087302Sgdamore@opensolaris.org ddi_dma_attr_t sh_dmaattr;
1097302Sgdamore@opensolaris.org sdslot_t sh_slots[SDHOST_MAXSLOTS];
1107302Sgdamore@opensolaris.org sda_host_t *sh_host;
1117302Sgdamore@opensolaris.org
1127302Sgdamore@opensolaris.org /*
1137302Sgdamore@opensolaris.org * Interrupt related information.
1147302Sgdamore@opensolaris.org */
1157302Sgdamore@opensolaris.org ddi_intr_handle_t sh_ihandle;
1167302Sgdamore@opensolaris.org int sh_icap;
1177302Sgdamore@opensolaris.org uint_t sh_ipri;
1187302Sgdamore@opensolaris.org };
1197302Sgdamore@opensolaris.org
1208708Sgdamore@opensolaris.org #define PROPSET(x) \
1218708Sgdamore@opensolaris.org (ddi_prop_get_int(DDI_DEV_T_ANY, dip, \
1228708Sgdamore@opensolaris.org DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, x, 0) != 0)
1238708Sgdamore@opensolaris.org
1247302Sgdamore@opensolaris.org
1257302Sgdamore@opensolaris.org static int sdhost_attach(dev_info_t *, ddi_attach_cmd_t);
1267302Sgdamore@opensolaris.org static int sdhost_detach(dev_info_t *, ddi_detach_cmd_t);
1278289Sgdamore@opensolaris.org static int sdhost_quiesce(dev_info_t *);
1287302Sgdamore@opensolaris.org static int sdhost_suspend(dev_info_t *);
1297302Sgdamore@opensolaris.org static int sdhost_resume(dev_info_t *);
1307302Sgdamore@opensolaris.org
1317302Sgdamore@opensolaris.org static void sdhost_enable_interrupts(sdslot_t *);
1327302Sgdamore@opensolaris.org static void sdhost_disable_interrupts(sdslot_t *);
1337302Sgdamore@opensolaris.org static int sdhost_setup_intr(dev_info_t *, sdhost_t *);
1347302Sgdamore@opensolaris.org static uint_t sdhost_intr(caddr_t, caddr_t);
1357302Sgdamore@opensolaris.org static int sdhost_init_slot(dev_info_t *, sdhost_t *, int, int);
1367302Sgdamore@opensolaris.org static void sdhost_uninit_slot(sdhost_t *, int);
1377302Sgdamore@opensolaris.org static sda_err_t sdhost_soft_reset(sdslot_t *, uint8_t);
1387302Sgdamore@opensolaris.org static sda_err_t sdhost_set_clock(sdslot_t *, uint32_t);
1397302Sgdamore@opensolaris.org static void sdhost_xfer_done(sdslot_t *, sda_err_t);
1407302Sgdamore@opensolaris.org static sda_err_t sdhost_wait_cmd(sdslot_t *, sda_cmd_t *);
1417302Sgdamore@opensolaris.org static uint_t sdhost_slot_intr(sdslot_t *);
1427302Sgdamore@opensolaris.org
1437302Sgdamore@opensolaris.org static sda_err_t sdhost_cmd(void *, sda_cmd_t *);
1447302Sgdamore@opensolaris.org static sda_err_t sdhost_getprop(void *, sda_prop_t, uint32_t *);
1457302Sgdamore@opensolaris.org static sda_err_t sdhost_setprop(void *, sda_prop_t, uint32_t);
1467302Sgdamore@opensolaris.org static sda_err_t sdhost_poll(void *);
1477302Sgdamore@opensolaris.org static sda_err_t sdhost_reset(void *);
1487302Sgdamore@opensolaris.org static sda_err_t sdhost_halt(void *);
1497302Sgdamore@opensolaris.org
1507302Sgdamore@opensolaris.org static struct dev_ops sdhost_dev_ops = {
1517302Sgdamore@opensolaris.org DEVO_REV, /* devo_rev */
1527302Sgdamore@opensolaris.org 0, /* devo_refcnt */
1537302Sgdamore@opensolaris.org ddi_no_info, /* devo_getinfo */
1547302Sgdamore@opensolaris.org nulldev, /* devo_identify */
1557302Sgdamore@opensolaris.org nulldev, /* devo_probe */
1567302Sgdamore@opensolaris.org sdhost_attach, /* devo_attach */
1577302Sgdamore@opensolaris.org sdhost_detach, /* devo_detach */
1587302Sgdamore@opensolaris.org nodev, /* devo_reset */
1597302Sgdamore@opensolaris.org NULL, /* devo_cb_ops */
1607302Sgdamore@opensolaris.org NULL, /* devo_bus_ops */
1617656SSherry.Moore@Sun.COM NULL, /* devo_power */
1628289Sgdamore@opensolaris.org sdhost_quiesce, /* devo_quiesce */
1637302Sgdamore@opensolaris.org };
1647302Sgdamore@opensolaris.org
1657302Sgdamore@opensolaris.org static struct modldrv sdhost_modldrv = {
1667302Sgdamore@opensolaris.org &mod_driverops, /* drv_modops */
1677302Sgdamore@opensolaris.org "Standard SD Host Controller", /* drv_linkinfo */
1687302Sgdamore@opensolaris.org &sdhost_dev_ops /* drv_dev_ops */
1697302Sgdamore@opensolaris.org };
1707302Sgdamore@opensolaris.org
1717302Sgdamore@opensolaris.org static struct modlinkage modlinkage = {
1727302Sgdamore@opensolaris.org MODREV_1, /* ml_rev */
1737302Sgdamore@opensolaris.org { &sdhost_modldrv, NULL } /* ml_linkage */
1747302Sgdamore@opensolaris.org };
1757302Sgdamore@opensolaris.org
1767302Sgdamore@opensolaris.org static struct sda_ops sdhost_ops = {
1777302Sgdamore@opensolaris.org SDA_OPS_VERSION,
1787302Sgdamore@opensolaris.org sdhost_cmd, /* so_cmd */
1797302Sgdamore@opensolaris.org sdhost_getprop, /* so_getprop */
1807302Sgdamore@opensolaris.org sdhost_setprop, /* so_setprop */
1817302Sgdamore@opensolaris.org sdhost_poll, /* so_poll */
1827302Sgdamore@opensolaris.org sdhost_reset, /* so_reset */
1837302Sgdamore@opensolaris.org sdhost_halt, /* so_halt */
1847302Sgdamore@opensolaris.org };
1857302Sgdamore@opensolaris.org
1867302Sgdamore@opensolaris.org static ddi_device_acc_attr_t sdhost_regattr = {
1877302Sgdamore@opensolaris.org DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
1887302Sgdamore@opensolaris.org DDI_STRUCTURE_LE_ACC, /* devacc_attr_endian_flags */
1897302Sgdamore@opensolaris.org DDI_STRICTORDER_ACC, /* devacc_attr_dataorder */
1907302Sgdamore@opensolaris.org DDI_DEFAULT_ACC, /* devacc_attr_access */
1917302Sgdamore@opensolaris.org };
1928708Sgdamore@opensolaris.org static ddi_device_acc_attr_t sdhost_bufattr = {
1938708Sgdamore@opensolaris.org DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
1948708Sgdamore@opensolaris.org DDI_NEVERSWAP_ACC, /* devacc_attr_endian_flags */
1958708Sgdamore@opensolaris.org DDI_STRICTORDER_ACC, /* devacc_attr_dataorder */
1968708Sgdamore@opensolaris.org DDI_DEFAULT_ACC, /* devacc_attr_access */
1978708Sgdamore@opensolaris.org };
1987302Sgdamore@opensolaris.org
1997302Sgdamore@opensolaris.org #define GET16(ss, reg) \
2007302Sgdamore@opensolaris.org ddi_get16(ss->ss_acch, (void *)(ss->ss_regva + reg))
2017302Sgdamore@opensolaris.org #define PUT16(ss, reg, val) \
2027302Sgdamore@opensolaris.org ddi_put16(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2037302Sgdamore@opensolaris.org #define GET32(ss, reg) \
2047302Sgdamore@opensolaris.org ddi_get32(ss->ss_acch, (void *)(ss->ss_regva + reg))
2057302Sgdamore@opensolaris.org #define PUT32(ss, reg, val) \
2067302Sgdamore@opensolaris.org ddi_put32(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2077302Sgdamore@opensolaris.org #define GET64(ss, reg) \
2087302Sgdamore@opensolaris.org ddi_get64(ss->ss_acch, (void *)(ss->ss_regva + reg))
2097302Sgdamore@opensolaris.org
2107302Sgdamore@opensolaris.org #define GET8(ss, reg) \
2117302Sgdamore@opensolaris.org ddi_get8(ss->ss_acch, (void *)(ss->ss_regva + reg))
2127302Sgdamore@opensolaris.org #define PUT8(ss, reg, val) \
2137302Sgdamore@opensolaris.org ddi_put8(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2147302Sgdamore@opensolaris.org
2157302Sgdamore@opensolaris.org #define CLR8(ss, reg, mask) PUT8(ss, reg, GET8(ss, reg) & ~(mask))
2167302Sgdamore@opensolaris.org #define SET8(ss, reg, mask) PUT8(ss, reg, GET8(ss, reg) | (mask))
2177302Sgdamore@opensolaris.org
2187302Sgdamore@opensolaris.org /*
2197302Sgdamore@opensolaris.org * If ever anyone uses PIO on SPARC, we have to endian-swap. But we
2207302Sgdamore@opensolaris.org * think that SD Host Controllers are likely to be uncommon on SPARC,
2217302Sgdamore@opensolaris.org * and hopefully when they exist at all they will be able to use DMA.
2227302Sgdamore@opensolaris.org */
2237302Sgdamore@opensolaris.org #ifdef _BIG_ENDIAN
2247302Sgdamore@opensolaris.org #define sw32(x) ddi_swap32(x)
2257302Sgdamore@opensolaris.org #define sw16(x) ddi_swap16(x)
2267302Sgdamore@opensolaris.org #else
2277302Sgdamore@opensolaris.org #define sw32(x) (x)
2287302Sgdamore@opensolaris.org #define sw16(x) (x)
2297302Sgdamore@opensolaris.org #endif
2307302Sgdamore@opensolaris.org
2317302Sgdamore@opensolaris.org #define GETDATA32(ss) sw32(GET32(ss, REG_DATA))
2327302Sgdamore@opensolaris.org #define GETDATA16(ss) sw16(GET16(ss, REG_DATA))
2337302Sgdamore@opensolaris.org #define GETDATA8(ss) GET8(ss, REG_DATA)
2347302Sgdamore@opensolaris.org
2357302Sgdamore@opensolaris.org #define PUTDATA32(ss, val) PUT32(ss, REG_DATA, sw32(val))
2367302Sgdamore@opensolaris.org #define PUTDATA16(ss, val) PUT16(ss, REG_DATA, sw16(val))
2377302Sgdamore@opensolaris.org #define PUTDATA8(ss, val) PUT8(ss, REG_DATA, val)
2387302Sgdamore@opensolaris.org
2397302Sgdamore@opensolaris.org #define CHECK_STATE(ss, nm) \
2407302Sgdamore@opensolaris.org ((GET32(ss, REG_PRS) & PRS_ ## nm) != 0)
2417302Sgdamore@opensolaris.org
2427302Sgdamore@opensolaris.org int
_init(void)2437302Sgdamore@opensolaris.org _init(void)
2447302Sgdamore@opensolaris.org {
2457302Sgdamore@opensolaris.org int rv;
2467302Sgdamore@opensolaris.org
2477302Sgdamore@opensolaris.org sda_host_init_ops(&sdhost_dev_ops);
2487302Sgdamore@opensolaris.org
2497302Sgdamore@opensolaris.org if ((rv = mod_install(&modlinkage)) != 0) {
2507302Sgdamore@opensolaris.org sda_host_fini_ops(&sdhost_dev_ops);
2517302Sgdamore@opensolaris.org }
2527302Sgdamore@opensolaris.org
2537302Sgdamore@opensolaris.org return (rv);
2547302Sgdamore@opensolaris.org }
2557302Sgdamore@opensolaris.org
2567302Sgdamore@opensolaris.org int
_fini(void)2577302Sgdamore@opensolaris.org _fini(void)
2587302Sgdamore@opensolaris.org {
2597302Sgdamore@opensolaris.org int rv;
2607302Sgdamore@opensolaris.org
2617302Sgdamore@opensolaris.org if ((rv = mod_remove(&modlinkage)) == 0) {
2627302Sgdamore@opensolaris.org sda_host_fini_ops(&sdhost_dev_ops);
2637302Sgdamore@opensolaris.org }
2647302Sgdamore@opensolaris.org return (rv);
2657302Sgdamore@opensolaris.org }
2667302Sgdamore@opensolaris.org
2677302Sgdamore@opensolaris.org int
_info(struct modinfo * modinfop)2687302Sgdamore@opensolaris.org _info(struct modinfo *modinfop)
2697302Sgdamore@opensolaris.org {
2707302Sgdamore@opensolaris.org return (mod_info(&modlinkage, modinfop));
2717302Sgdamore@opensolaris.org }
2727302Sgdamore@opensolaris.org
2737302Sgdamore@opensolaris.org int
sdhost_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2747302Sgdamore@opensolaris.org sdhost_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2757302Sgdamore@opensolaris.org {
2767302Sgdamore@opensolaris.org sdhost_t *shp;
2777302Sgdamore@opensolaris.org ddi_acc_handle_t pcih;
2787302Sgdamore@opensolaris.org uint8_t slotinfo;
2797302Sgdamore@opensolaris.org uint8_t bar;
2807302Sgdamore@opensolaris.org int i;
2818289Sgdamore@opensolaris.org int rv;
2827302Sgdamore@opensolaris.org
2837302Sgdamore@opensolaris.org switch (cmd) {
2847302Sgdamore@opensolaris.org case DDI_ATTACH:
2857302Sgdamore@opensolaris.org break;
2867302Sgdamore@opensolaris.org
2877302Sgdamore@opensolaris.org case DDI_RESUME:
2887302Sgdamore@opensolaris.org return (sdhost_resume(dip));
2897302Sgdamore@opensolaris.org
2907302Sgdamore@opensolaris.org default:
2917302Sgdamore@opensolaris.org return (DDI_FAILURE);
2927302Sgdamore@opensolaris.org }
2937302Sgdamore@opensolaris.org
2947302Sgdamore@opensolaris.org /*
2957302Sgdamore@opensolaris.org * Soft state allocation.
2967302Sgdamore@opensolaris.org */
2977302Sgdamore@opensolaris.org shp = kmem_zalloc(sizeof (*shp), KM_SLEEP);
2987302Sgdamore@opensolaris.org ddi_set_driver_private(dip, shp);
2997302Sgdamore@opensolaris.org
3007302Sgdamore@opensolaris.org /*
3019495Sgdamore@opensolaris.org * Reset the "slot number", so uninit slot works properly.
3029495Sgdamore@opensolaris.org */
3039495Sgdamore@opensolaris.org for (i = 0; i < SDHOST_MAXSLOTS; i++) {
3049495Sgdamore@opensolaris.org shp->sh_slots[i].ss_num = -1;
3059495Sgdamore@opensolaris.org }
3069495Sgdamore@opensolaris.org
3079495Sgdamore@opensolaris.org /*
3087302Sgdamore@opensolaris.org * Initialize DMA attributes. For now we initialize as for
3097302Sgdamore@opensolaris.org * SDMA. If we add ADMA support we can improve this.
3107302Sgdamore@opensolaris.org */
3117302Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_version = DMA_ATTR_V0;
3127302Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_addr_lo = 0;
3137302Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_addr_hi = 0xffffffffU;
3147302Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_count_max = 0xffffffffU;
3158708Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_align = 4096; /* Ricoh needs it */
3167302Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_burstsizes = 0; /* for now! */
3177302Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_minxfer = 1;
3188708Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_maxxfer = 0x7ffffU;
3198708Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_sgllen = 1; /* no scatter/gather */
3208708Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_seg = 0x7ffffU; /* not to cross 512K */
3217302Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_granular = 1;
3227302Sgdamore@opensolaris.org shp->sh_dmaattr.dma_attr_flags = 0;
3237302Sgdamore@opensolaris.org
3247302Sgdamore@opensolaris.org /*
3257302Sgdamore@opensolaris.org * PCI configuration access to figure out number of slots present.
3267302Sgdamore@opensolaris.org */
3277302Sgdamore@opensolaris.org if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
3287302Sgdamore@opensolaris.org cmn_err(CE_WARN, "pci_config_setup failed");
3297302Sgdamore@opensolaris.org goto failed;
3307302Sgdamore@opensolaris.org }
3317302Sgdamore@opensolaris.org
3327302Sgdamore@opensolaris.org slotinfo = pci_config_get8(pcih, SLOTINFO);
3337302Sgdamore@opensolaris.org shp->sh_numslots = SLOTINFO_NSLOT(slotinfo);
3347302Sgdamore@opensolaris.org
3357302Sgdamore@opensolaris.org if (shp->sh_numslots > SDHOST_MAXSLOTS) {
3367302Sgdamore@opensolaris.org cmn_err(CE_WARN, "Host reports to have too many slots: %d",
3377302Sgdamore@opensolaris.org shp->sh_numslots);
3389495Sgdamore@opensolaris.org pci_config_teardown(&pcih);
3397302Sgdamore@opensolaris.org goto failed;
3407302Sgdamore@opensolaris.org }
3417302Sgdamore@opensolaris.org
3427302Sgdamore@opensolaris.org /*
3437302Sgdamore@opensolaris.org * Enable master accesses and DMA.
3447302Sgdamore@opensolaris.org */
3457302Sgdamore@opensolaris.org pci_config_put16(pcih, PCI_CONF_COMM,
3467302Sgdamore@opensolaris.org pci_config_get16(pcih, PCI_CONF_COMM) |
3477302Sgdamore@opensolaris.org PCI_COMM_MAE | PCI_COMM_ME);
3487302Sgdamore@opensolaris.org
3497302Sgdamore@opensolaris.org /*
3507302Sgdamore@opensolaris.org * Figure out which BAR to use. Note that we number BARs from
3517302Sgdamore@opensolaris.org * 1, although PCI and SD Host numbers from 0. (We number
3527302Sgdamore@opensolaris.org * from 1, because register number 0 means PCI configuration
3537302Sgdamore@opensolaris.org * space in Solaris.)
3547302Sgdamore@opensolaris.org */
3557302Sgdamore@opensolaris.org bar = SLOTINFO_BAR(slotinfo) + 1;
3567302Sgdamore@opensolaris.org
3577302Sgdamore@opensolaris.org pci_config_teardown(&pcih);
3587302Sgdamore@opensolaris.org
3597302Sgdamore@opensolaris.org /*
3607302Sgdamore@opensolaris.org * Setup interrupts ... supports the new DDI interrupt API. This
3617302Sgdamore@opensolaris.org * will support MSI or MSI-X interrupts if a device is found to
3627302Sgdamore@opensolaris.org * support it.
3637302Sgdamore@opensolaris.org */
3647302Sgdamore@opensolaris.org if (sdhost_setup_intr(dip, shp) != DDI_SUCCESS) {
3657302Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed to setup interrupts");
3667302Sgdamore@opensolaris.org goto failed;
3677302Sgdamore@opensolaris.org }
3687302Sgdamore@opensolaris.org
3697302Sgdamore@opensolaris.org shp->sh_host = sda_host_alloc(dip, shp->sh_numslots, &sdhost_ops,
370*12426Sgdamore@opensolaris.org NULL);
3717302Sgdamore@opensolaris.org if (shp->sh_host == NULL) {
3727302Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed allocating SD host structure");
3737302Sgdamore@opensolaris.org goto failed;
3747302Sgdamore@opensolaris.org }
3757302Sgdamore@opensolaris.org
3767302Sgdamore@opensolaris.org /*
3777302Sgdamore@opensolaris.org * Configure slots, this also maps registers, enables
3787302Sgdamore@opensolaris.org * interrupts, etc. Most of the hardware setup is done here.
3797302Sgdamore@opensolaris.org */
3807302Sgdamore@opensolaris.org for (i = 0; i < shp->sh_numslots; i++) {
3817302Sgdamore@opensolaris.org if (sdhost_init_slot(dip, shp, i, bar + i) != DDI_SUCCESS) {
3827302Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed initializing slot %d", i);
3837302Sgdamore@opensolaris.org goto failed;
3847302Sgdamore@opensolaris.org }
3857302Sgdamore@opensolaris.org }
3867302Sgdamore@opensolaris.org
3877302Sgdamore@opensolaris.org ddi_report_dev(dip);
3887302Sgdamore@opensolaris.org
3897302Sgdamore@opensolaris.org /*
3907302Sgdamore@opensolaris.org * Enable device interrupts at the DDI layer.
3917302Sgdamore@opensolaris.org */
3928289Sgdamore@opensolaris.org if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
3938289Sgdamore@opensolaris.org rv = ddi_intr_block_enable(&shp->sh_ihandle, 1);
3948289Sgdamore@opensolaris.org } else {
3958289Sgdamore@opensolaris.org rv = ddi_intr_enable(shp->sh_ihandle);
3968289Sgdamore@opensolaris.org }
3978289Sgdamore@opensolaris.org if (rv != DDI_SUCCESS) {
3988289Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed enabling interrupts");
3998289Sgdamore@opensolaris.org goto failed;
4008289Sgdamore@opensolaris.org }
4017302Sgdamore@opensolaris.org
4027302Sgdamore@opensolaris.org /*
4037302Sgdamore@opensolaris.org * Mark the slots online with the framework. This will cause
4047302Sgdamore@opensolaris.org * the framework to probe them for the presence of cards.
4057302Sgdamore@opensolaris.org */
4067302Sgdamore@opensolaris.org if (sda_host_attach(shp->sh_host) != DDI_SUCCESS) {
4077302Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed attaching to SDA framework");
4088289Sgdamore@opensolaris.org if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
4098289Sgdamore@opensolaris.org (void) ddi_intr_block_disable(&shp->sh_ihandle, 1);
4108289Sgdamore@opensolaris.org } else {
4118289Sgdamore@opensolaris.org (void) ddi_intr_disable(shp->sh_ihandle);
4128289Sgdamore@opensolaris.org }
4137302Sgdamore@opensolaris.org goto failed;
4147302Sgdamore@opensolaris.org }
4157302Sgdamore@opensolaris.org
4167302Sgdamore@opensolaris.org return (DDI_SUCCESS);
4177302Sgdamore@opensolaris.org
4187302Sgdamore@opensolaris.org failed:
4197302Sgdamore@opensolaris.org if (shp->sh_ihandle != NULL) {
4207302Sgdamore@opensolaris.org (void) ddi_intr_remove_handler(shp->sh_ihandle);
4217302Sgdamore@opensolaris.org (void) ddi_intr_free(shp->sh_ihandle);
4227302Sgdamore@opensolaris.org }
4237302Sgdamore@opensolaris.org for (i = 0; i < shp->sh_numslots; i++)
4247302Sgdamore@opensolaris.org sdhost_uninit_slot(shp, i);
4259495Sgdamore@opensolaris.org if (shp->sh_host != NULL)
4269495Sgdamore@opensolaris.org sda_host_free(shp->sh_host);
4277302Sgdamore@opensolaris.org kmem_free(shp, sizeof (*shp));
4287302Sgdamore@opensolaris.org
4297302Sgdamore@opensolaris.org return (DDI_FAILURE);
4307302Sgdamore@opensolaris.org }
4317302Sgdamore@opensolaris.org
4327302Sgdamore@opensolaris.org int
sdhost_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4337302Sgdamore@opensolaris.org sdhost_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4347302Sgdamore@opensolaris.org {
4357302Sgdamore@opensolaris.org sdhost_t *shp;
4367302Sgdamore@opensolaris.org int i;
4377302Sgdamore@opensolaris.org
4387302Sgdamore@opensolaris.org switch (cmd) {
4397302Sgdamore@opensolaris.org case DDI_DETACH:
4407302Sgdamore@opensolaris.org break;
4417302Sgdamore@opensolaris.org
4427302Sgdamore@opensolaris.org case DDI_SUSPEND:
4437302Sgdamore@opensolaris.org return (sdhost_suspend(dip));
4447302Sgdamore@opensolaris.org
4457302Sgdamore@opensolaris.org default:
4467302Sgdamore@opensolaris.org return (DDI_FAILURE);
4477302Sgdamore@opensolaris.org }
4487302Sgdamore@opensolaris.org
4497302Sgdamore@opensolaris.org shp = ddi_get_driver_private(dip);
4507302Sgdamore@opensolaris.org
4517302Sgdamore@opensolaris.org /*
4527302Sgdamore@opensolaris.org * Take host offline with the framework.
4537302Sgdamore@opensolaris.org */
4547302Sgdamore@opensolaris.org sda_host_detach(shp->sh_host);
4557302Sgdamore@opensolaris.org
4567302Sgdamore@opensolaris.org /*
4577302Sgdamore@opensolaris.org * Tear down interrupts.
4587302Sgdamore@opensolaris.org */
4597302Sgdamore@opensolaris.org if (shp->sh_ihandle != NULL) {
4608289Sgdamore@opensolaris.org if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
4618289Sgdamore@opensolaris.org (void) ddi_intr_block_disable(&shp->sh_ihandle, 1);
4628289Sgdamore@opensolaris.org } else {
4638289Sgdamore@opensolaris.org (void) ddi_intr_disable(shp->sh_ihandle);
4648289Sgdamore@opensolaris.org }
4657302Sgdamore@opensolaris.org (void) ddi_intr_remove_handler(shp->sh_ihandle);
4667302Sgdamore@opensolaris.org (void) ddi_intr_free(shp->sh_ihandle);
4677302Sgdamore@opensolaris.org }
4687302Sgdamore@opensolaris.org
4697302Sgdamore@opensolaris.org /*
4707302Sgdamore@opensolaris.org * Tear down register mappings, etc.
4717302Sgdamore@opensolaris.org */
4727302Sgdamore@opensolaris.org for (i = 0; i < shp->sh_numslots; i++)
4737302Sgdamore@opensolaris.org sdhost_uninit_slot(shp, i);
4749495Sgdamore@opensolaris.org sda_host_free(shp->sh_host);
4757302Sgdamore@opensolaris.org kmem_free(shp, sizeof (*shp));
4767302Sgdamore@opensolaris.org
4777302Sgdamore@opensolaris.org return (DDI_SUCCESS);
4787302Sgdamore@opensolaris.org }
4797302Sgdamore@opensolaris.org
4807302Sgdamore@opensolaris.org int
sdhost_quiesce(dev_info_t * dip)4818289Sgdamore@opensolaris.org sdhost_quiesce(dev_info_t *dip)
4828289Sgdamore@opensolaris.org {
4838289Sgdamore@opensolaris.org sdhost_t *shp;
4848289Sgdamore@opensolaris.org sdslot_t *ss;
4858289Sgdamore@opensolaris.org
4868289Sgdamore@opensolaris.org shp = ddi_get_driver_private(dip);
4878289Sgdamore@opensolaris.org
4888289Sgdamore@opensolaris.org /* reset each slot separately */
4898289Sgdamore@opensolaris.org for (int i = 0; i < shp->sh_numslots; i++) {
4908289Sgdamore@opensolaris.org ss = &shp->sh_slots[i];
4918289Sgdamore@opensolaris.org if (ss->ss_acch == NULL)
4928289Sgdamore@opensolaris.org continue;
4938289Sgdamore@opensolaris.org
4948289Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
4958289Sgdamore@opensolaris.org }
4968289Sgdamore@opensolaris.org return (DDI_SUCCESS);
4978289Sgdamore@opensolaris.org }
4988289Sgdamore@opensolaris.org
4998289Sgdamore@opensolaris.org int
sdhost_suspend(dev_info_t * dip)5007302Sgdamore@opensolaris.org sdhost_suspend(dev_info_t *dip)
5017302Sgdamore@opensolaris.org {
5027302Sgdamore@opensolaris.org sdhost_t *shp;
5037302Sgdamore@opensolaris.org sdslot_t *ss;
5047302Sgdamore@opensolaris.org int i;
5057302Sgdamore@opensolaris.org
5067302Sgdamore@opensolaris.org shp = ddi_get_driver_private(dip);
5077302Sgdamore@opensolaris.org
5088289Sgdamore@opensolaris.org sda_host_suspend(shp->sh_host);
5097302Sgdamore@opensolaris.org
5107302Sgdamore@opensolaris.org for (i = 0; i < shp->sh_numslots; i++) {
5117302Sgdamore@opensolaris.org ss = &shp->sh_slots[i];
5127302Sgdamore@opensolaris.org mutex_enter(&ss->ss_lock);
5137302Sgdamore@opensolaris.org ss->ss_suspended = B_TRUE;
5147302Sgdamore@opensolaris.org sdhost_disable_interrupts(ss);
5157302Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
5167302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
5177302Sgdamore@opensolaris.org }
5187302Sgdamore@opensolaris.org return (DDI_SUCCESS);
5197302Sgdamore@opensolaris.org }
5207302Sgdamore@opensolaris.org
5217302Sgdamore@opensolaris.org int
sdhost_resume(dev_info_t * dip)5227302Sgdamore@opensolaris.org sdhost_resume(dev_info_t *dip)
5237302Sgdamore@opensolaris.org {
5247302Sgdamore@opensolaris.org sdhost_t *shp;
5257302Sgdamore@opensolaris.org sdslot_t *ss;
5267302Sgdamore@opensolaris.org int i;
5277302Sgdamore@opensolaris.org
5287302Sgdamore@opensolaris.org shp = ddi_get_driver_private(dip);
5297302Sgdamore@opensolaris.org
5307302Sgdamore@opensolaris.org for (i = 0; i < shp->sh_numslots; i++) {
5317302Sgdamore@opensolaris.org ss = &shp->sh_slots[i];
5327302Sgdamore@opensolaris.org mutex_enter(&ss->ss_lock);
5337302Sgdamore@opensolaris.org ss->ss_suspended = B_FALSE;
5347302Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
5357302Sgdamore@opensolaris.org sdhost_enable_interrupts(ss);
5367302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
5377302Sgdamore@opensolaris.org }
5387302Sgdamore@opensolaris.org
5398289Sgdamore@opensolaris.org sda_host_resume(shp->sh_host);
5407302Sgdamore@opensolaris.org
5417302Sgdamore@opensolaris.org return (DDI_SUCCESS);
5427302Sgdamore@opensolaris.org }
5437302Sgdamore@opensolaris.org
5447302Sgdamore@opensolaris.org sda_err_t
sdhost_set_clock(sdslot_t * ss,uint32_t hz)5457302Sgdamore@opensolaris.org sdhost_set_clock(sdslot_t *ss, uint32_t hz)
5467302Sgdamore@opensolaris.org {
5477302Sgdamore@opensolaris.org uint16_t div;
5487302Sgdamore@opensolaris.org uint32_t val;
5497302Sgdamore@opensolaris.org uint32_t clk;
5507302Sgdamore@opensolaris.org int count;
5517302Sgdamore@opensolaris.org
5527302Sgdamore@opensolaris.org /*
5537302Sgdamore@opensolaris.org * Shut off the clock to begin.
5547302Sgdamore@opensolaris.org */
5557302Sgdamore@opensolaris.org ss->ss_cardclk = 0;
5567302Sgdamore@opensolaris.org PUT16(ss, REG_CLOCK_CONTROL, 0);
5577302Sgdamore@opensolaris.org if (hz == 0) {
5587302Sgdamore@opensolaris.org return (SDA_EOK);
5597302Sgdamore@opensolaris.org }
5607302Sgdamore@opensolaris.org
5617302Sgdamore@opensolaris.org if (ss->ss_baseclk == 0) {
5627302Sgdamore@opensolaris.org sda_host_log(ss->ss_host, ss->ss_num,
5637302Sgdamore@opensolaris.org "Base clock frequency not established.");
5647302Sgdamore@opensolaris.org return (SDA_EINVAL);
5657302Sgdamore@opensolaris.org }
5667302Sgdamore@opensolaris.org
5677302Sgdamore@opensolaris.org if ((hz > 25000000) && ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0)) {
5687302Sgdamore@opensolaris.org /* this clock requires high speed timings! */
5697302Sgdamore@opensolaris.org SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
5707302Sgdamore@opensolaris.org } else {
5717302Sgdamore@opensolaris.org /* don't allow clock to run faster than 25MHz */
5727302Sgdamore@opensolaris.org hz = min(hz, 25000000);
5737302Sgdamore@opensolaris.org CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
5747302Sgdamore@opensolaris.org }
5757302Sgdamore@opensolaris.org
5767302Sgdamore@opensolaris.org /* figure out the divider */
5777302Sgdamore@opensolaris.org clk = ss->ss_baseclk;
5787302Sgdamore@opensolaris.org div = 1;
5797302Sgdamore@opensolaris.org while (clk > hz) {
5807302Sgdamore@opensolaris.org if (div > 0x80)
5817302Sgdamore@opensolaris.org break;
5827302Sgdamore@opensolaris.org clk >>= 1; /* divide clock by two */
5837302Sgdamore@opensolaris.org div <<= 1; /* divider goes up by one */
5847302Sgdamore@opensolaris.org }
5857302Sgdamore@opensolaris.org div >>= 1; /* 0 == divide by 1, 1 = divide by 2 */
5867302Sgdamore@opensolaris.org
5877302Sgdamore@opensolaris.org /*
5887302Sgdamore@opensolaris.org * Set the internal clock divider first, without enabling the
5897302Sgdamore@opensolaris.org * card clock yet.
5907302Sgdamore@opensolaris.org */
5917302Sgdamore@opensolaris.org PUT16(ss, REG_CLOCK_CONTROL,
5927302Sgdamore@opensolaris.org (div << CLOCK_CONTROL_FREQ_SHIFT) | CLOCK_CONTROL_INT_CLOCK_EN);
5937302Sgdamore@opensolaris.org
5947302Sgdamore@opensolaris.org /*
5957302Sgdamore@opensolaris.org * Wait up to 100 msec for the internal clock to stabilize.
5967302Sgdamore@opensolaris.org * (The spec does not seem to indicate a maximum timeout, but
5977302Sgdamore@opensolaris.org * it also suggests that an infinite loop be used, which is
5987302Sgdamore@opensolaris.org * not appropriate for hardened Solaris drivers.)
5997302Sgdamore@opensolaris.org */
6007302Sgdamore@opensolaris.org for (count = 100000; count; count -= 10) {
6017302Sgdamore@opensolaris.org
6027302Sgdamore@opensolaris.org val = GET16(ss, REG_CLOCK_CONTROL);
6037302Sgdamore@opensolaris.org
6047302Sgdamore@opensolaris.org if (val & CLOCK_CONTROL_INT_CLOCK_STABLE) {
6057302Sgdamore@opensolaris.org /* if clock is stable, enable the SD clock pin */
6067302Sgdamore@opensolaris.org PUT16(ss, REG_CLOCK_CONTROL, val |
6077302Sgdamore@opensolaris.org CLOCK_CONTROL_SD_CLOCK_EN);
6087302Sgdamore@opensolaris.org
6097302Sgdamore@opensolaris.org ss->ss_cardclk = clk;
6107302Sgdamore@opensolaris.org return (SDA_EOK);
6117302Sgdamore@opensolaris.org }
6127302Sgdamore@opensolaris.org
6137302Sgdamore@opensolaris.org drv_usecwait(10);
6147302Sgdamore@opensolaris.org }
6157302Sgdamore@opensolaris.org
6167302Sgdamore@opensolaris.org return (SDA_ETIME);
6177302Sgdamore@opensolaris.org }
6187302Sgdamore@opensolaris.org
6197302Sgdamore@opensolaris.org sda_err_t
sdhost_soft_reset(sdslot_t * ss,uint8_t bits)6207302Sgdamore@opensolaris.org sdhost_soft_reset(sdslot_t *ss, uint8_t bits)
6217302Sgdamore@opensolaris.org {
6227302Sgdamore@opensolaris.org int count;
6237302Sgdamore@opensolaris.org
6247302Sgdamore@opensolaris.org /*
6257302Sgdamore@opensolaris.org * There appears to be a bug where Ricoh hosts might have a
6267302Sgdamore@opensolaris.org * problem if the host frequency is not set. If the card
6277302Sgdamore@opensolaris.org * isn't present, or we are doing a master reset, just enable
6287302Sgdamore@opensolaris.org * the internal clock at its native speed. (No dividers, and
6297302Sgdamore@opensolaris.org * not exposed to card.).
6307302Sgdamore@opensolaris.org */
6317302Sgdamore@opensolaris.org if ((bits == SOFT_RESET_ALL) || !(CHECK_STATE(ss, CARD_INSERTED))) {
6327302Sgdamore@opensolaris.org PUT16(ss, REG_CLOCK_CONTROL, CLOCK_CONTROL_INT_CLOCK_EN);
6337302Sgdamore@opensolaris.org /* simple 1msec wait, don't wait for clock to stabilize */
6347302Sgdamore@opensolaris.org drv_usecwait(1000);
6358708Sgdamore@opensolaris.org /*
6368708Sgdamore@opensolaris.org * reset the card clock & width -- master reset also
6378708Sgdamore@opensolaris.org * resets these
6388708Sgdamore@opensolaris.org */
6398708Sgdamore@opensolaris.org ss->ss_cardclk = 0;
6408708Sgdamore@opensolaris.org ss->ss_width = 1;
6417302Sgdamore@opensolaris.org }
6427302Sgdamore@opensolaris.org
6438708Sgdamore@opensolaris.org
6447302Sgdamore@opensolaris.org PUT8(ss, REG_SOFT_RESET, bits);
6457302Sgdamore@opensolaris.org for (count = 100000; count != 0; count -= 10) {
6467302Sgdamore@opensolaris.org if ((GET8(ss, REG_SOFT_RESET) & bits) == 0) {
6477302Sgdamore@opensolaris.org return (SDA_EOK);
6487302Sgdamore@opensolaris.org }
6497302Sgdamore@opensolaris.org drv_usecwait(10);
6507302Sgdamore@opensolaris.org }
6517302Sgdamore@opensolaris.org
6527302Sgdamore@opensolaris.org return (SDA_ETIME);
6537302Sgdamore@opensolaris.org }
6547302Sgdamore@opensolaris.org
6557302Sgdamore@opensolaris.org void
sdhost_disable_interrupts(sdslot_t * ss)6567302Sgdamore@opensolaris.org sdhost_disable_interrupts(sdslot_t *ss)
6577302Sgdamore@opensolaris.org {
6587302Sgdamore@opensolaris.org /* disable slot interrupts for card insert and remove */
6597302Sgdamore@opensolaris.org PUT16(ss, REG_INT_MASK, 0);
6607302Sgdamore@opensolaris.org PUT16(ss, REG_INT_EN, 0);
6617302Sgdamore@opensolaris.org
6627302Sgdamore@opensolaris.org /* disable error interrupts */
6637302Sgdamore@opensolaris.org PUT16(ss, REG_ERR_MASK, 0);
6647302Sgdamore@opensolaris.org PUT16(ss, REG_ERR_EN, 0);
6657302Sgdamore@opensolaris.org }
6667302Sgdamore@opensolaris.org
6677302Sgdamore@opensolaris.org void
sdhost_enable_interrupts(sdslot_t * ss)6687302Sgdamore@opensolaris.org sdhost_enable_interrupts(sdslot_t *ss)
6697302Sgdamore@opensolaris.org {
6707302Sgdamore@opensolaris.org /*
6717302Sgdamore@opensolaris.org * Note that we want to enable reading of the CMD related
6727302Sgdamore@opensolaris.org * bits, but we do not want them to generate an interrupt.
6737302Sgdamore@opensolaris.org * (The busy wait for typical CMD stuff will normally be less
6747302Sgdamore@opensolaris.org * than 10usec, so its simpler/easier to just poll. Even in
6757302Sgdamore@opensolaris.org * the worst case of 100 kHz, the poll is at worst 2 msec.)
6767302Sgdamore@opensolaris.org */
6777302Sgdamore@opensolaris.org
6787302Sgdamore@opensolaris.org /* enable slot interrupts for card insert and remove */
6797302Sgdamore@opensolaris.org PUT16(ss, REG_INT_MASK, INT_MASK);
6807302Sgdamore@opensolaris.org PUT16(ss, REG_INT_EN, INT_ENAB);
6817302Sgdamore@opensolaris.org
6827302Sgdamore@opensolaris.org /* enable error interrupts */
6837302Sgdamore@opensolaris.org PUT16(ss, REG_ERR_MASK, ERR_MASK);
6847302Sgdamore@opensolaris.org PUT16(ss, REG_ERR_EN, ERR_ENAB);
6857302Sgdamore@opensolaris.org }
6867302Sgdamore@opensolaris.org
6877302Sgdamore@opensolaris.org int
sdhost_setup_intr(dev_info_t * dip,sdhost_t * shp)6887302Sgdamore@opensolaris.org sdhost_setup_intr(dev_info_t *dip, sdhost_t *shp)
6897302Sgdamore@opensolaris.org {
6907302Sgdamore@opensolaris.org int itypes;
6917302Sgdamore@opensolaris.org int itype;
6927302Sgdamore@opensolaris.org
6937302Sgdamore@opensolaris.org /*
6947302Sgdamore@opensolaris.org * Set up interrupt handler.
6957302Sgdamore@opensolaris.org */
6967302Sgdamore@opensolaris.org if (ddi_intr_get_supported_types(dip, &itypes) != DDI_SUCCESS) {
6977302Sgdamore@opensolaris.org cmn_err(CE_WARN, "ddi_intr_get_supported_types failed");
6987302Sgdamore@opensolaris.org return (DDI_FAILURE);
6997302Sgdamore@opensolaris.org }
7007302Sgdamore@opensolaris.org
7017302Sgdamore@opensolaris.org /*
7028471Sgdamore@opensolaris.org * It turns out that some controllers don't properly implement MSI,
7038471Sgdamore@opensolaris.org * but advertise MSI capability in their PCI config space.
7048471Sgdamore@opensolaris.org *
7058471Sgdamore@opensolaris.org * While this is really a chip-specific bug, the simplest solution
7068471Sgdamore@opensolaris.org * is to just suppress MSI for now by default -- every device seen
7078471Sgdamore@opensolaris.org * so far can use FIXED interrupts.
7088471Sgdamore@opensolaris.org *
7098471Sgdamore@opensolaris.org * We offer an override property, though, just in case someone really
7108471Sgdamore@opensolaris.org * wants to force it.
7118471Sgdamore@opensolaris.org *
7128471Sgdamore@opensolaris.org * We don't do this if the FIXED type isn't supported though!
7138471Sgdamore@opensolaris.org */
7148708Sgdamore@opensolaris.org if (itypes & DDI_INTR_TYPE_FIXED) {
7158708Sgdamore@opensolaris.org if (!PROPSET(SDHOST_PROP_ENABLE_MSI)) {
7168708Sgdamore@opensolaris.org itypes &= ~DDI_INTR_TYPE_MSI;
7178708Sgdamore@opensolaris.org }
7188708Sgdamore@opensolaris.org if (!PROPSET(SDHOST_PROP_ENABLE_MSIX)) {
7198708Sgdamore@opensolaris.org itypes &= ~DDI_INTR_TYPE_MSIX;
7208708Sgdamore@opensolaris.org }
7218471Sgdamore@opensolaris.org }
7228471Sgdamore@opensolaris.org
7238471Sgdamore@opensolaris.org /*
7247302Sgdamore@opensolaris.org * Interrupt types are bits in a mask. We know about these ones:
7257302Sgdamore@opensolaris.org * FIXED = 1
7267302Sgdamore@opensolaris.org * MSI = 2
7277302Sgdamore@opensolaris.org * MSIX = 4
7287302Sgdamore@opensolaris.org */
7297302Sgdamore@opensolaris.org for (itype = DDI_INTR_TYPE_MSIX; itype != 0; itype >>= 1) {
7307302Sgdamore@opensolaris.org
7317302Sgdamore@opensolaris.org int count;
7327302Sgdamore@opensolaris.org
7337302Sgdamore@opensolaris.org if ((itypes & itype) == 0) {
7347302Sgdamore@opensolaris.org /* this type is not supported on this device! */
7357302Sgdamore@opensolaris.org continue;
7367302Sgdamore@opensolaris.org }
7377302Sgdamore@opensolaris.org
7387302Sgdamore@opensolaris.org if ((ddi_intr_get_nintrs(dip, itype, &count) != DDI_SUCCESS) ||
7397302Sgdamore@opensolaris.org (count == 0)) {
7407302Sgdamore@opensolaris.org cmn_err(CE_WARN, "ddi_intr_get_nintrs failed");
7417302Sgdamore@opensolaris.org continue;
7427302Sgdamore@opensolaris.org }
7437302Sgdamore@opensolaris.org
7447302Sgdamore@opensolaris.org /*
7457302Sgdamore@opensolaris.org * We have not seen a host device with multiple
7467302Sgdamore@opensolaris.org * interrupts (one per slot?), and the spec does not
7477302Sgdamore@opensolaris.org * indicate that they exist. But if one ever occurs,
7487302Sgdamore@opensolaris.org * we spew a warning to help future debugging/support
7497302Sgdamore@opensolaris.org * efforts.
7507302Sgdamore@opensolaris.org */
7517302Sgdamore@opensolaris.org if (count > 1) {
7527302Sgdamore@opensolaris.org cmn_err(CE_WARN, "Controller offers %d interrupts, "
7537302Sgdamore@opensolaris.org "but driver only supports one", count);
7547302Sgdamore@opensolaris.org continue;
7557302Sgdamore@opensolaris.org }
7567302Sgdamore@opensolaris.org
7577302Sgdamore@opensolaris.org if ((ddi_intr_alloc(dip, &shp->sh_ihandle, itype, 0, 1,
7587302Sgdamore@opensolaris.org &count, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
7597302Sgdamore@opensolaris.org (count != 1)) {
7607302Sgdamore@opensolaris.org cmn_err(CE_WARN, "ddi_intr_alloc failed");
7617302Sgdamore@opensolaris.org continue;
7627302Sgdamore@opensolaris.org }
7637302Sgdamore@opensolaris.org
7647302Sgdamore@opensolaris.org if (ddi_intr_get_pri(shp->sh_ihandle, &shp->sh_ipri) !=
7657302Sgdamore@opensolaris.org DDI_SUCCESS) {
7667302Sgdamore@opensolaris.org cmn_err(CE_WARN, "ddi_intr_get_pri failed");
7677302Sgdamore@opensolaris.org (void) ddi_intr_free(shp->sh_ihandle);
7687302Sgdamore@opensolaris.org shp->sh_ihandle = NULL;
7697302Sgdamore@opensolaris.org continue;
7707302Sgdamore@opensolaris.org }
7717302Sgdamore@opensolaris.org
7727302Sgdamore@opensolaris.org if (shp->sh_ipri >= ddi_intr_get_hilevel_pri()) {
7737302Sgdamore@opensolaris.org cmn_err(CE_WARN, "Hi level interrupt not supported");
7747302Sgdamore@opensolaris.org (void) ddi_intr_free(shp->sh_ihandle);
7757302Sgdamore@opensolaris.org shp->sh_ihandle = NULL;
7767302Sgdamore@opensolaris.org continue;
7777302Sgdamore@opensolaris.org }
7787302Sgdamore@opensolaris.org
7797302Sgdamore@opensolaris.org if (ddi_intr_get_cap(shp->sh_ihandle, &shp->sh_icap) !=
7807302Sgdamore@opensolaris.org DDI_SUCCESS) {
7817302Sgdamore@opensolaris.org cmn_err(CE_WARN, "ddi_intr_get_cap failed");
7827302Sgdamore@opensolaris.org (void) ddi_intr_free(shp->sh_ihandle);
7837302Sgdamore@opensolaris.org shp->sh_ihandle = NULL;
7847302Sgdamore@opensolaris.org continue;
7857302Sgdamore@opensolaris.org }
7867302Sgdamore@opensolaris.org
7877302Sgdamore@opensolaris.org if (ddi_intr_add_handler(shp->sh_ihandle, sdhost_intr,
7887302Sgdamore@opensolaris.org shp, NULL) != DDI_SUCCESS) {
7897302Sgdamore@opensolaris.org cmn_err(CE_WARN, "ddi_intr_add_handler failed");
7907302Sgdamore@opensolaris.org (void) ddi_intr_free(shp->sh_ihandle);
7917302Sgdamore@opensolaris.org shp->sh_ihandle = NULL;
7927302Sgdamore@opensolaris.org continue;
7937302Sgdamore@opensolaris.org }
7947302Sgdamore@opensolaris.org
7957302Sgdamore@opensolaris.org return (DDI_SUCCESS);
7967302Sgdamore@opensolaris.org }
7977302Sgdamore@opensolaris.org
7987302Sgdamore@opensolaris.org return (DDI_FAILURE);
7997302Sgdamore@opensolaris.org }
8007302Sgdamore@opensolaris.org
8017302Sgdamore@opensolaris.org void
sdhost_xfer_done(sdslot_t * ss,sda_err_t errno)8027302Sgdamore@opensolaris.org sdhost_xfer_done(sdslot_t *ss, sda_err_t errno)
8037302Sgdamore@opensolaris.org {
8047302Sgdamore@opensolaris.org if ((errno == SDA_EOK) && (ss->ss_resid != 0)) {
8057302Sgdamore@opensolaris.org /* an unexpected partial transfer was found */
8067302Sgdamore@opensolaris.org errno = SDA_ERESID;
8077302Sgdamore@opensolaris.org }
8087302Sgdamore@opensolaris.org ss->ss_blksz = 0;
8097302Sgdamore@opensolaris.org ss->ss_resid = 0;
8107302Sgdamore@opensolaris.org
8117302Sgdamore@opensolaris.org if (errno != SDA_EOK) {
8127302Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
8137302Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
8147302Sgdamore@opensolaris.org
8157302Sgdamore@opensolaris.org /* send a STOP command if necessary */
8167302Sgdamore@opensolaris.org if (ss->ss_mode & XFR_MODE_AUTO_CMD12) {
8177302Sgdamore@opensolaris.org PUT32(ss, REG_ARGUMENT, 0);
8187302Sgdamore@opensolaris.org PUT16(ss, REG_COMMAND,
8197302Sgdamore@opensolaris.org (CMD_STOP_TRANSMIT << 8) |
8207302Sgdamore@opensolaris.org COMMAND_TYPE_NORM | COMMAND_INDEX_CHECK_EN |
8217302Sgdamore@opensolaris.org COMMAND_CRC_CHECK_EN | COMMAND_RESP_48_BUSY);
8227302Sgdamore@opensolaris.org }
8237302Sgdamore@opensolaris.org }
8247302Sgdamore@opensolaris.org
8257302Sgdamore@opensolaris.org sda_host_transfer(ss->ss_host, ss->ss_num, errno);
8267302Sgdamore@opensolaris.org }
8277302Sgdamore@opensolaris.org
8287302Sgdamore@opensolaris.org uint_t
sdhost_slot_intr(sdslot_t * ss)8297302Sgdamore@opensolaris.org sdhost_slot_intr(sdslot_t *ss)
8307302Sgdamore@opensolaris.org {
8317302Sgdamore@opensolaris.org uint16_t intr;
8327302Sgdamore@opensolaris.org uint16_t errs;
8338708Sgdamore@opensolaris.org caddr_t data;
8347302Sgdamore@opensolaris.org int count;
8357302Sgdamore@opensolaris.org
8367302Sgdamore@opensolaris.org mutex_enter(&ss->ss_lock);
8377302Sgdamore@opensolaris.org
8387302Sgdamore@opensolaris.org if (ss->ss_suspended) {
8397302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
8407302Sgdamore@opensolaris.org return (DDI_INTR_UNCLAIMED);
8417302Sgdamore@opensolaris.org }
8427302Sgdamore@opensolaris.org
8437302Sgdamore@opensolaris.org intr = GET16(ss, REG_INT_STAT);
8447302Sgdamore@opensolaris.org if (intr == 0) {
8457302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
8467302Sgdamore@opensolaris.org return (DDI_INTR_UNCLAIMED);
8477302Sgdamore@opensolaris.org }
8487302Sgdamore@opensolaris.org errs = GET16(ss, REG_ERR_STAT);
8497302Sgdamore@opensolaris.org
8507302Sgdamore@opensolaris.org if (intr & (INT_REM | INT_INS)) {
8517302Sgdamore@opensolaris.org
8527302Sgdamore@opensolaris.org PUT16(ss, REG_INT_STAT, intr);
8537302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
8547302Sgdamore@opensolaris.org
8557302Sgdamore@opensolaris.org sda_host_detect(ss->ss_host, ss->ss_num);
8567302Sgdamore@opensolaris.org /* no further interrupt processing this cycle */
8577302Sgdamore@opensolaris.org return (DDI_INTR_CLAIMED);
8587302Sgdamore@opensolaris.org }
8597302Sgdamore@opensolaris.org
8607302Sgdamore@opensolaris.org if (intr & INT_DMA) {
8617302Sgdamore@opensolaris.org /*
8627302Sgdamore@opensolaris.org * We have crossed a DMA/page boundary. Cope with it.
8637302Sgdamore@opensolaris.org */
8648708Sgdamore@opensolaris.org /*
8658708Sgdamore@opensolaris.org * Apparently some sdhost controllers issue a final
8668708Sgdamore@opensolaris.org * DMA interrupt if the DMA completes on a boundary,
8678708Sgdamore@opensolaris.org * even though there is no further data to transfer.
8688708Sgdamore@opensolaris.org *
8698708Sgdamore@opensolaris.org * There might be a risk here of the controller
8708708Sgdamore@opensolaris.org * continuing to access the same data over and over
8718708Sgdamore@opensolaris.org * again, but we accept the risk.
8728708Sgdamore@opensolaris.org */
8738708Sgdamore@opensolaris.org PUT16(ss, REG_INT_STAT, INT_DMA);
8747302Sgdamore@opensolaris.org }
8757302Sgdamore@opensolaris.org
8767302Sgdamore@opensolaris.org if (intr & INT_RD) {
8777302Sgdamore@opensolaris.org /*
8787302Sgdamore@opensolaris.org * PIO read! PIO is quite suboptimal, but we expect
8797302Sgdamore@opensolaris.org * performance critical applications to use DMA
8807302Sgdamore@opensolaris.org * whenever possible. We have to stage this through
8817302Sgdamore@opensolaris.org * the bounce buffer to meet alignment considerations.
8827302Sgdamore@opensolaris.org */
8837302Sgdamore@opensolaris.org
8847302Sgdamore@opensolaris.org PUT16(ss, REG_INT_STAT, INT_RD);
8857302Sgdamore@opensolaris.org
8867302Sgdamore@opensolaris.org while ((ss->ss_resid > 0) && CHECK_STATE(ss, BUF_RD_EN)) {
8877302Sgdamore@opensolaris.org
8888708Sgdamore@opensolaris.org data = ss->ss_bounce;
8897302Sgdamore@opensolaris.org count = ss->ss_blksz;
8907302Sgdamore@opensolaris.org
8917302Sgdamore@opensolaris.org ASSERT(count > 0);
8927302Sgdamore@opensolaris.org ASSERT(ss->ss_kvaddr != NULL);
8937302Sgdamore@opensolaris.org
8947302Sgdamore@opensolaris.org while (count >= sizeof (uint32_t)) {
8957302Sgdamore@opensolaris.org *(uint32_t *)(void *)data = GETDATA32(ss);
8967302Sgdamore@opensolaris.org data += sizeof (uint32_t);
8977302Sgdamore@opensolaris.org count -= sizeof (uint32_t);
8987302Sgdamore@opensolaris.org }
8997302Sgdamore@opensolaris.org while (count >= sizeof (uint16_t)) {
9007302Sgdamore@opensolaris.org *(uint16_t *)(void *)data = GETDATA16(ss);
9017302Sgdamore@opensolaris.org data += sizeof (uint16_t);
9027302Sgdamore@opensolaris.org count -= sizeof (uint16_t);
9037302Sgdamore@opensolaris.org }
9047302Sgdamore@opensolaris.org while (count >= sizeof (uint8_t)) {
9057302Sgdamore@opensolaris.org *(uint8_t *)data = GETDATA8(ss);
9067302Sgdamore@opensolaris.org data += sizeof (uint8_t);
9077302Sgdamore@opensolaris.org count -= sizeof (uint8_t);
9087302Sgdamore@opensolaris.org }
9097302Sgdamore@opensolaris.org
9107302Sgdamore@opensolaris.org bcopy(ss->ss_bounce, ss->ss_kvaddr, ss->ss_blksz);
9117302Sgdamore@opensolaris.org ss->ss_kvaddr += ss->ss_blksz;
9127302Sgdamore@opensolaris.org ss->ss_resid--;
9137302Sgdamore@opensolaris.org }
9147302Sgdamore@opensolaris.org }
9157302Sgdamore@opensolaris.org
9167302Sgdamore@opensolaris.org if (intr & INT_WR) {
9177302Sgdamore@opensolaris.org /*
9187302Sgdamore@opensolaris.org * PIO write! PIO is quite suboptimal, but we expect
9197302Sgdamore@opensolaris.org * performance critical applications to use DMA
9208708Sgdamore@opensolaris.org * whenever possible. We have to stage this through
9217302Sgdamore@opensolaris.org * the bounce buffer to meet alignment considerations.
9227302Sgdamore@opensolaris.org */
9237302Sgdamore@opensolaris.org
9247302Sgdamore@opensolaris.org PUT16(ss, REG_INT_STAT, INT_WR);
9257302Sgdamore@opensolaris.org
9267302Sgdamore@opensolaris.org while ((ss->ss_resid > 0) && CHECK_STATE(ss, BUF_WR_EN)) {
9277302Sgdamore@opensolaris.org
9288708Sgdamore@opensolaris.org data = ss->ss_bounce;
9297302Sgdamore@opensolaris.org count = ss->ss_blksz;
9307302Sgdamore@opensolaris.org
9317302Sgdamore@opensolaris.org ASSERT(count > 0);
9327302Sgdamore@opensolaris.org ASSERT(ss->ss_kvaddr != NULL);
9337302Sgdamore@opensolaris.org
9347302Sgdamore@opensolaris.org bcopy(ss->ss_kvaddr, data, count);
9357302Sgdamore@opensolaris.org while (count >= sizeof (uint32_t)) {
9367302Sgdamore@opensolaris.org PUTDATA32(ss, *(uint32_t *)(void *)data);
9377302Sgdamore@opensolaris.org data += sizeof (uint32_t);
9387302Sgdamore@opensolaris.org count -= sizeof (uint32_t);
9397302Sgdamore@opensolaris.org }
9407302Sgdamore@opensolaris.org while (count >= sizeof (uint16_t)) {
9417302Sgdamore@opensolaris.org PUTDATA16(ss, *(uint16_t *)(void *)data);
9427302Sgdamore@opensolaris.org data += sizeof (uint16_t);
9437302Sgdamore@opensolaris.org count -= sizeof (uint16_t);
9447302Sgdamore@opensolaris.org }
9457302Sgdamore@opensolaris.org while (count >= sizeof (uint8_t)) {
9467302Sgdamore@opensolaris.org PUTDATA8(ss, *(uint8_t *)data);
9477302Sgdamore@opensolaris.org data += sizeof (uint8_t);
9487302Sgdamore@opensolaris.org count -= sizeof (uint8_t);
9497302Sgdamore@opensolaris.org }
9507302Sgdamore@opensolaris.org
9517302Sgdamore@opensolaris.org ss->ss_kvaddr += ss->ss_blksz;
9527302Sgdamore@opensolaris.org ss->ss_resid--;
9537302Sgdamore@opensolaris.org }
9547302Sgdamore@opensolaris.org }
9557302Sgdamore@opensolaris.org
9567302Sgdamore@opensolaris.org if (intr & INT_XFR) {
9578708Sgdamore@opensolaris.org if ((ss->ss_mode & (XFR_MODE_READ | XFR_MODE_DMA_EN)) ==
9588708Sgdamore@opensolaris.org (XFR_MODE_READ | XFR_MODE_DMA_EN)) {
9598708Sgdamore@opensolaris.org (void) ddi_dma_sync(ss->ss_bufdmah, 0, 0,
9608708Sgdamore@opensolaris.org DDI_DMA_SYNC_FORKERNEL);
9618708Sgdamore@opensolaris.org bcopy(ss->ss_bounce, ss->ss_kvaddr, ss->ss_rcnt);
9628708Sgdamore@opensolaris.org ss->ss_rcnt = 0;
9638708Sgdamore@opensolaris.org }
9647302Sgdamore@opensolaris.org PUT16(ss, REG_INT_STAT, INT_XFR);
9657302Sgdamore@opensolaris.org
9667302Sgdamore@opensolaris.org sdhost_xfer_done(ss, SDA_EOK);
9677302Sgdamore@opensolaris.org }
9687302Sgdamore@opensolaris.org
9697302Sgdamore@opensolaris.org if (intr & INT_ERR) {
9707302Sgdamore@opensolaris.org PUT16(ss, REG_ERR_STAT, errs);
9717302Sgdamore@opensolaris.org PUT16(ss, REG_INT_STAT, INT_ERR);
9727302Sgdamore@opensolaris.org
9737302Sgdamore@opensolaris.org if (errs & ERR_DAT) {
9747302Sgdamore@opensolaris.org if ((errs & ERR_DAT_END) == ERR_DAT_END) {
9757302Sgdamore@opensolaris.org sdhost_xfer_done(ss, SDA_EPROTO);
9767302Sgdamore@opensolaris.org } else if ((errs & ERR_DAT_CRC) == ERR_DAT_CRC) {
9777302Sgdamore@opensolaris.org sdhost_xfer_done(ss, SDA_ECRC7);
9787302Sgdamore@opensolaris.org } else {
9797302Sgdamore@opensolaris.org sdhost_xfer_done(ss, SDA_ETIME);
9807302Sgdamore@opensolaris.org }
9817302Sgdamore@opensolaris.org
9827302Sgdamore@opensolaris.org } else if (errs & ERR_ACMD12) {
9837302Sgdamore@opensolaris.org /*
9847302Sgdamore@opensolaris.org * Generally, this is bad news. we need a full
9857302Sgdamore@opensolaris.org * reset to recover properly.
9867302Sgdamore@opensolaris.org */
9877302Sgdamore@opensolaris.org sdhost_xfer_done(ss, SDA_ECMD12);
9887302Sgdamore@opensolaris.org }
9897302Sgdamore@opensolaris.org
9907302Sgdamore@opensolaris.org /*
9917302Sgdamore@opensolaris.org * This asynchronous error leaves the slot more or less
9927302Sgdamore@opensolaris.org * useless. Report it to the framework.
9937302Sgdamore@opensolaris.org */
9947302Sgdamore@opensolaris.org if (errs & ERR_CURRENT) {
9957302Sgdamore@opensolaris.org sda_host_fault(ss->ss_host, ss->ss_num,
9967302Sgdamore@opensolaris.org SDA_FAULT_CURRENT);
9977302Sgdamore@opensolaris.org }
9987302Sgdamore@opensolaris.org }
9997302Sgdamore@opensolaris.org
10007302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
10017302Sgdamore@opensolaris.org
10027302Sgdamore@opensolaris.org return (DDI_INTR_CLAIMED);
10037302Sgdamore@opensolaris.org }
10047302Sgdamore@opensolaris.org
10057302Sgdamore@opensolaris.org /*ARGSUSED1*/
10067302Sgdamore@opensolaris.org uint_t
sdhost_intr(caddr_t arg1,caddr_t arg2)10077302Sgdamore@opensolaris.org sdhost_intr(caddr_t arg1, caddr_t arg2)
10087302Sgdamore@opensolaris.org {
10097302Sgdamore@opensolaris.org sdhost_t *shp = (void *)arg1;
10107302Sgdamore@opensolaris.org int rv = DDI_INTR_UNCLAIMED;
10117302Sgdamore@opensolaris.org int num;
10127302Sgdamore@opensolaris.org
10137302Sgdamore@opensolaris.org /* interrupt for each of the slots present in the system */
10147302Sgdamore@opensolaris.org for (num = 0; num < shp->sh_numslots; num++) {
10157302Sgdamore@opensolaris.org if (sdhost_slot_intr(&shp->sh_slots[num]) ==
10167302Sgdamore@opensolaris.org DDI_INTR_CLAIMED) {
10177302Sgdamore@opensolaris.org rv = DDI_INTR_CLAIMED;
10187302Sgdamore@opensolaris.org }
10197302Sgdamore@opensolaris.org }
10207302Sgdamore@opensolaris.org return (rv);
10217302Sgdamore@opensolaris.org }
10227302Sgdamore@opensolaris.org
10237302Sgdamore@opensolaris.org int
sdhost_init_slot(dev_info_t * dip,sdhost_t * shp,int num,int bar)10247302Sgdamore@opensolaris.org sdhost_init_slot(dev_info_t *dip, sdhost_t *shp, int num, int bar)
10257302Sgdamore@opensolaris.org {
10267302Sgdamore@opensolaris.org sdslot_t *ss;
10277302Sgdamore@opensolaris.org uint32_t capab;
10287302Sgdamore@opensolaris.org uint32_t clk;
10298708Sgdamore@opensolaris.org char ksname[16];
10308708Sgdamore@opensolaris.org size_t blen;
10318708Sgdamore@opensolaris.org unsigned ndmac;
10328708Sgdamore@opensolaris.org int rv;
10337302Sgdamore@opensolaris.org
10347302Sgdamore@opensolaris.org /*
10357302Sgdamore@opensolaris.org * Register the private state.
10367302Sgdamore@opensolaris.org */
10377302Sgdamore@opensolaris.org ss = &shp->sh_slots[num];
10387302Sgdamore@opensolaris.org ss->ss_host = shp->sh_host;
10397302Sgdamore@opensolaris.org ss->ss_num = num;
10407302Sgdamore@opensolaris.org sda_host_set_private(shp->sh_host, num, ss);
10417302Sgdamore@opensolaris.org /*
10427302Sgdamore@opensolaris.org * Initialize core data structure, locks, etc.
10437302Sgdamore@opensolaris.org */
10447302Sgdamore@opensolaris.org mutex_init(&ss->ss_lock, NULL, MUTEX_DRIVER,
10457302Sgdamore@opensolaris.org DDI_INTR_PRI(shp->sh_ipri));
10467302Sgdamore@opensolaris.org
10478708Sgdamore@opensolaris.org /*
10488708Sgdamore@opensolaris.org * Set up DMA.
10498708Sgdamore@opensolaris.org */
10508708Sgdamore@opensolaris.org rv = ddi_dma_alloc_handle(dip, &shp->sh_dmaattr,
10518708Sgdamore@opensolaris.org DDI_DMA_SLEEP, NULL, &ss->ss_bufdmah);
10528708Sgdamore@opensolaris.org if (rv != DDI_SUCCESS) {
10538708Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed to alloc dma handle (%d)!", rv);
10548708Sgdamore@opensolaris.org return (DDI_FAILURE);
10558708Sgdamore@opensolaris.org }
10568708Sgdamore@opensolaris.org
10578708Sgdamore@opensolaris.org rv = ddi_dma_mem_alloc(ss->ss_bufdmah, SDHOST_BOUNCESZ,
10588708Sgdamore@opensolaris.org &sdhost_bufattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
10598708Sgdamore@opensolaris.org &ss->ss_bounce, &blen, &ss->ss_bufacch);
10608708Sgdamore@opensolaris.org if (rv != DDI_SUCCESS) {
10618708Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed to alloc bounce buffer (%d)!", rv);
10628708Sgdamore@opensolaris.org return (DDI_FAILURE);
10638708Sgdamore@opensolaris.org }
10648708Sgdamore@opensolaris.org
10658708Sgdamore@opensolaris.org rv = ddi_dma_addr_bind_handle(ss->ss_bufdmah, NULL, ss->ss_bounce,
10668708Sgdamore@opensolaris.org blen, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
10678708Sgdamore@opensolaris.org &ss->ss_bufdmac, &ndmac);
10688708Sgdamore@opensolaris.org if ((rv != DDI_DMA_MAPPED) || (ndmac != 1)) {
10698708Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed to bind DMA bounce buffer (%d, %u)!",
10708708Sgdamore@opensolaris.org rv, ndmac);
10718708Sgdamore@opensolaris.org return (DDI_FAILURE);
10728708Sgdamore@opensolaris.org }
10738708Sgdamore@opensolaris.org
10748708Sgdamore@opensolaris.org /*
10758708Sgdamore@opensolaris.org * Set up virtual kstats.
10768708Sgdamore@opensolaris.org */
10778708Sgdamore@opensolaris.org (void) snprintf(ksname, sizeof (ksname), "slot%d", num);
10788708Sgdamore@opensolaris.org ss->ss_ksp = kstat_create(ddi_driver_name(dip), ddi_get_instance(dip),
10798708Sgdamore@opensolaris.org ksname, "misc", KSTAT_TYPE_NAMED,
10808708Sgdamore@opensolaris.org sizeof (sdstats_t) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL);
10818708Sgdamore@opensolaris.org if (ss->ss_ksp != NULL) {
10828708Sgdamore@opensolaris.org sdstats_t *sp = &ss->ss_stats;
10838708Sgdamore@opensolaris.org ss->ss_ksp->ks_data = sp;
10848708Sgdamore@opensolaris.org ss->ss_ksp->ks_private = ss;
10858708Sgdamore@opensolaris.org ss->ss_ksp->ks_lock = &ss->ss_lock;
10868708Sgdamore@opensolaris.org /* counters are 64 bits wide */
10878708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_ncmd, "ncmd", KSTAT_DATA_UINT64);
10888708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_ixfr, "ixfr", KSTAT_DATA_UINT64);
10898708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_oxfr, "oxfr", KSTAT_DATA_UINT64);
10908708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_ibytes, "ibytes", KSTAT_DATA_UINT64);
10918708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_obytes, "obytes", KSTAT_DATA_UINT64);
10928708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_npio, "npio", KSTAT_DATA_UINT64);
10938708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_ndma, "ndma", KSTAT_DATA_UINT64);
10948708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_nmulti, "nmulti", KSTAT_DATA_UINT64);
10958708Sgdamore@opensolaris.org /* these aren't counters -- leave them at 32 bits */
10968708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_baseclk, "baseclk", KSTAT_DATA_UINT32);
10978708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_cardclk, "cardclk", KSTAT_DATA_UINT32);
10988708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_tmusecs, "tmusecs", KSTAT_DATA_UINT32);
10998708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_width, "width", KSTAT_DATA_UINT32);
11008708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_flags, "flags", KSTAT_DATA_UINT32);
11018708Sgdamore@opensolaris.org kstat_named_init(&sp->ks_capab, "capab", KSTAT_DATA_UINT32);
11028708Sgdamore@opensolaris.org kstat_install(ss->ss_ksp);
11038708Sgdamore@opensolaris.org }
11048708Sgdamore@opensolaris.org
11058708Sgdamore@opensolaris.org if (PROPSET(SDHOST_PROP_FORCE_PIO)) {
11068708Sgdamore@opensolaris.org ss->ss_flags |= SDFLAG_FORCE_PIO;
11078708Sgdamore@opensolaris.org }
11088708Sgdamore@opensolaris.org if (PROPSET(SDHOST_PROP_FORCE_DMA)) {
11098708Sgdamore@opensolaris.org ss->ss_flags |= SDFLAG_FORCE_DMA;
11108708Sgdamore@opensolaris.org }
11118708Sgdamore@opensolaris.org
11127302Sgdamore@opensolaris.org if (ddi_regs_map_setup(dip, bar, &ss->ss_regva, 0, 0, &sdhost_regattr,
11137302Sgdamore@opensolaris.org &ss->ss_acch) != DDI_SUCCESS) {
11148289Sgdamore@opensolaris.org cmn_err(CE_WARN, "Failed to map registers!");
11157302Sgdamore@opensolaris.org return (DDI_FAILURE);
11167302Sgdamore@opensolaris.org }
11177302Sgdamore@opensolaris.org
11187302Sgdamore@opensolaris.org /* reset before reading capabilities */
11197302Sgdamore@opensolaris.org if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK)
11207302Sgdamore@opensolaris.org return (DDI_FAILURE);
11217302Sgdamore@opensolaris.org
11227302Sgdamore@opensolaris.org capab = GET64(ss, REG_CAPAB) & 0xffffffffU; /* upper bits reserved */
11237302Sgdamore@opensolaris.org ss->ss_capab = capab;
11247302Sgdamore@opensolaris.org
11257302Sgdamore@opensolaris.org /* host voltages in OCR format */
11267302Sgdamore@opensolaris.org ss->ss_ocr = 0;
11277302Sgdamore@opensolaris.org if (capab & CAPAB_18V)
11287302Sgdamore@opensolaris.org ss->ss_ocr |= OCR_18_19V; /* 1.8V */
11297302Sgdamore@opensolaris.org if (capab & CAPAB_30V)
11307302Sgdamore@opensolaris.org ss->ss_ocr |= OCR_30_31V;
11317302Sgdamore@opensolaris.org if (capab & CAPAB_33V)
11327302Sgdamore@opensolaris.org ss->ss_ocr |= OCR_32_33V;
11337302Sgdamore@opensolaris.org
11347302Sgdamore@opensolaris.org /* base clock */
11357302Sgdamore@opensolaris.org ss->ss_baseclk =
11367302Sgdamore@opensolaris.org ((capab & CAPAB_BASE_FREQ_MASK) >> CAPAB_BASE_FREQ_SHIFT);
11377302Sgdamore@opensolaris.org ss->ss_baseclk *= 1000000;
11387302Sgdamore@opensolaris.org
11397302Sgdamore@opensolaris.org /*
11407302Sgdamore@opensolaris.org * Timeout clock. We can calculate this using the following
11417302Sgdamore@opensolaris.org * formula:
11427302Sgdamore@opensolaris.org *
11437302Sgdamore@opensolaris.org * (1000000 usec/1sec) * (1sec/tmoutclk) * base factor = clock time
11447302Sgdamore@opensolaris.org *
11457302Sgdamore@opensolaris.org * Clock time is the length of the base clock in usecs.
11467302Sgdamore@opensolaris.org *
11477302Sgdamore@opensolaris.org * Our base factor is 2^13, which is the shortest clock we
11487302Sgdamore@opensolaris.org * can count.
11497302Sgdamore@opensolaris.org *
11507302Sgdamore@opensolaris.org * To simplify the math and avoid overflow, we cancel out the
11517302Sgdamore@opensolaris.org * zeros for kHz or MHz. Since we want to wait more clocks, not
11527302Sgdamore@opensolaris.org * less, on error, we truncate the result rather than rounding
11537302Sgdamore@opensolaris.org * up.
11547302Sgdamore@opensolaris.org */
11557302Sgdamore@opensolaris.org clk = ((capab & CAPAB_TIMEOUT_FREQ_MASK) >> CAPAB_TIMEOUT_FREQ_SHIFT);
11567302Sgdamore@opensolaris.org if ((ss->ss_baseclk == 0) || (clk == 0)) {
11577302Sgdamore@opensolaris.org cmn_err(CE_WARN, "Unable to determine clock frequencies");
11587302Sgdamore@opensolaris.org return (DDI_FAILURE);
11597302Sgdamore@opensolaris.org }
11607302Sgdamore@opensolaris.org
11617302Sgdamore@opensolaris.org if (capab & CAPAB_TIMEOUT_UNITS) {
11627302Sgdamore@opensolaris.org /* MHz */
11637302Sgdamore@opensolaris.org ss->ss_tmusecs = (1 << 13) / clk;
11647302Sgdamore@opensolaris.org clk *= 1000000;
11657302Sgdamore@opensolaris.org } else {
11667302Sgdamore@opensolaris.org /* kHz */
11677302Sgdamore@opensolaris.org ss->ss_tmusecs = (1000 * (1 << 13)) / clk;
11687302Sgdamore@opensolaris.org clk *= 1000;
11697302Sgdamore@opensolaris.org }
11707302Sgdamore@opensolaris.org
11717302Sgdamore@opensolaris.org /*
11727302Sgdamore@opensolaris.org * Calculation of the timeout.
11737302Sgdamore@opensolaris.org *
11747302Sgdamore@opensolaris.org * SDIO cards use a 1sec timeout, and SDHC cards use fixed
11757302Sgdamore@opensolaris.org * 100msec for read and 250 msec for write.
11767302Sgdamore@opensolaris.org *
11777302Sgdamore@opensolaris.org * Legacy cards running at 375kHz have a worst case of about
11787302Sgdamore@opensolaris.org * 15 seconds. Running at 25MHz (the standard speed) it is
11797302Sgdamore@opensolaris.org * about 100msec for read, and about 3.2 sec for write.
11807302Sgdamore@opensolaris.org * Typical values are 1/100th that, or about 1msec for read,
11817302Sgdamore@opensolaris.org * and 32 msec for write.
11827302Sgdamore@opensolaris.org *
11837302Sgdamore@opensolaris.org * No transaction at full speed should ever take more than 4
11847302Sgdamore@opensolaris.org * seconds. (Some slow legacy cards might have trouble, but
11857302Sgdamore@opensolaris.org * we'll worry about them if they ever are seen. Nobody wants
11867302Sgdamore@opensolaris.org * to wait 4 seconds to access a single block anyway!)
11877302Sgdamore@opensolaris.org *
11887302Sgdamore@opensolaris.org * To get to 4 seconds, we continuously double usec until we
11897302Sgdamore@opensolaris.org * get to the maximum value, or a timeout greater than 4
11907302Sgdamore@opensolaris.org * seconds.
11917302Sgdamore@opensolaris.org *
11927302Sgdamore@opensolaris.org * Note that for high-speed timeout clocks, we might not be
11937302Sgdamore@opensolaris.org * able to get to the full 4 seconds. E.g. with a 48MHz
11947302Sgdamore@opensolaris.org * timeout clock, we can only get to about 2.8 seconds. Its
11957302Sgdamore@opensolaris.org * possible that there could be some slow MMC cards that will
11967302Sgdamore@opensolaris.org * timeout at this clock rate, but it seems unlikely. (The
11977302Sgdamore@opensolaris.org * device would have to be pressing the very worst times,
11987302Sgdamore@opensolaris.org * against the 100-fold "permissive" window allowed, and
11997302Sgdamore@opensolaris.org * running at only 12.5MHz.)
12007302Sgdamore@opensolaris.org *
12017302Sgdamore@opensolaris.org * XXX: this could easily be a tunable. Someone dealing with only
12027302Sgdamore@opensolaris.org * reasonable cards could set this to just 1 second.
12037302Sgdamore@opensolaris.org */
12047302Sgdamore@opensolaris.org for (ss->ss_tmoutclk = 0; ss->ss_tmoutclk < 14; ss->ss_tmoutclk++) {
12057302Sgdamore@opensolaris.org if ((ss->ss_tmusecs * (1 << ss->ss_tmoutclk)) >= 4000000) {
12067302Sgdamore@opensolaris.org break;
12077302Sgdamore@opensolaris.org }
12087302Sgdamore@opensolaris.org }
12097302Sgdamore@opensolaris.org
12107302Sgdamore@opensolaris.org /*
12117302Sgdamore@opensolaris.org * Enable slot interrupts.
12127302Sgdamore@opensolaris.org */
12137302Sgdamore@opensolaris.org sdhost_enable_interrupts(ss);
12147302Sgdamore@opensolaris.org
12157302Sgdamore@opensolaris.org return (DDI_SUCCESS);
12167302Sgdamore@opensolaris.org }
12177302Sgdamore@opensolaris.org
12187302Sgdamore@opensolaris.org void
sdhost_uninit_slot(sdhost_t * shp,int num)12197302Sgdamore@opensolaris.org sdhost_uninit_slot(sdhost_t *shp, int num)
12207302Sgdamore@opensolaris.org {
12217302Sgdamore@opensolaris.org sdslot_t *ss;
12227302Sgdamore@opensolaris.org
12237302Sgdamore@opensolaris.org ss = &shp->sh_slots[num];
12247302Sgdamore@opensolaris.org
12259495Sgdamore@opensolaris.org if (ss->ss_acch != NULL)
12269495Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
12277302Sgdamore@opensolaris.org
12289495Sgdamore@opensolaris.org if (ss->ss_bufdmac.dmac_address)
12298708Sgdamore@opensolaris.org (void) ddi_dma_unbind_handle(ss->ss_bufdmah);
12309495Sgdamore@opensolaris.org
12319495Sgdamore@opensolaris.org if (ss->ss_bufacch != NULL)
12328708Sgdamore@opensolaris.org ddi_dma_mem_free(&ss->ss_bufacch);
12339495Sgdamore@opensolaris.org
12349495Sgdamore@opensolaris.org if (ss->ss_bufdmah != NULL)
12358708Sgdamore@opensolaris.org ddi_dma_free_handle(&ss->ss_bufdmah);
12369495Sgdamore@opensolaris.org
12379495Sgdamore@opensolaris.org if (ss->ss_ksp != NULL)
12388708Sgdamore@opensolaris.org kstat_delete(ss->ss_ksp);
12398708Sgdamore@opensolaris.org
12409495Sgdamore@opensolaris.org if (ss->ss_acch != NULL)
12419495Sgdamore@opensolaris.org ddi_regs_map_free(&ss->ss_acch);
12429495Sgdamore@opensolaris.org
12439495Sgdamore@opensolaris.org if (ss->ss_num != -1)
12449495Sgdamore@opensolaris.org mutex_destroy(&ss->ss_lock);
12457302Sgdamore@opensolaris.org }
12467302Sgdamore@opensolaris.org
12477302Sgdamore@opensolaris.org void
sdhost_get_response(sdslot_t * ss,sda_cmd_t * cmdp)12487302Sgdamore@opensolaris.org sdhost_get_response(sdslot_t *ss, sda_cmd_t *cmdp)
12497302Sgdamore@opensolaris.org {
12507302Sgdamore@opensolaris.org uint32_t *resp = cmdp->sc_response;
12517302Sgdamore@opensolaris.org int i;
12527302Sgdamore@opensolaris.org
12537302Sgdamore@opensolaris.org resp[0] = GET32(ss, REG_RESP1);
12547302Sgdamore@opensolaris.org resp[1] = GET32(ss, REG_RESP2);
12557302Sgdamore@opensolaris.org resp[2] = GET32(ss, REG_RESP3);
12567302Sgdamore@opensolaris.org resp[3] = GET32(ss, REG_RESP4);
12577302Sgdamore@opensolaris.org
12587302Sgdamore@opensolaris.org /*
12597302Sgdamore@opensolaris.org * Response 2 is goofy because the host drops the low
12607302Sgdamore@opensolaris.org * order CRC bits. This makes it a bit awkward, so we
12617302Sgdamore@opensolaris.org * have to shift the bits to make it work out right.
12627302Sgdamore@opensolaris.org *
12637302Sgdamore@opensolaris.org * Note that the framework expects the 32 bit
12647302Sgdamore@opensolaris.org * words to be ordered in LE fashion. (The
12657302Sgdamore@opensolaris.org * bits within the words are in native order).
12667302Sgdamore@opensolaris.org */
12677302Sgdamore@opensolaris.org if (cmdp->sc_rtype == R2) {
12687302Sgdamore@opensolaris.org for (i = 3; i > 0; i--) {
12697302Sgdamore@opensolaris.org resp[i] <<= 8;
12707302Sgdamore@opensolaris.org resp[i] |= (resp[i - 1] >> 24);
12717302Sgdamore@opensolaris.org }
12727302Sgdamore@opensolaris.org resp[0] <<= 8;
12737302Sgdamore@opensolaris.org }
12747302Sgdamore@opensolaris.org }
12757302Sgdamore@opensolaris.org
12767302Sgdamore@opensolaris.org sda_err_t
sdhost_wait_cmd(sdslot_t * ss,sda_cmd_t * cmdp)12777302Sgdamore@opensolaris.org sdhost_wait_cmd(sdslot_t *ss, sda_cmd_t *cmdp)
12787302Sgdamore@opensolaris.org {
12797302Sgdamore@opensolaris.org int i;
12807302Sgdamore@opensolaris.org uint16_t errs;
12818289Sgdamore@opensolaris.org sda_err_t rv;
12827302Sgdamore@opensolaris.org
12837302Sgdamore@opensolaris.org /*
12847302Sgdamore@opensolaris.org * Worst case for 100kHz timeout is 2msec (200 clocks), we add
12857302Sgdamore@opensolaris.org * a tiny bit for safety. (Generally timeout will be far, far
12867302Sgdamore@opensolaris.org * less than that.)
12877302Sgdamore@opensolaris.org *
12887302Sgdamore@opensolaris.org * Note that at more typical 12MHz (and normally it will be
12897302Sgdamore@opensolaris.org * even faster than that!) that the device timeout is only
12907302Sgdamore@opensolaris.org * 16.67 usec. We could be smarter and reduce the delay time,
12917302Sgdamore@opensolaris.org * but that would require putting more intelligence into the
12927302Sgdamore@opensolaris.org * code, and we don't expect CMD timeout to normally occur
12937302Sgdamore@opensolaris.org * except during initialization. (At which time we need the
12947302Sgdamore@opensolaris.org * full timeout anyway.)
12957302Sgdamore@opensolaris.org *
12967302Sgdamore@opensolaris.org * Checking the ERR_STAT will normally cause the timeout to
12977302Sgdamore@opensolaris.org * terminate to finish early if the device is healthy, anyway.
12987302Sgdamore@opensolaris.org */
12997302Sgdamore@opensolaris.org
13007302Sgdamore@opensolaris.org for (i = 3000; i > 0; i -= 5) {
13017302Sgdamore@opensolaris.org if (GET16(ss, REG_INT_STAT) & INT_CMD) {
13027302Sgdamore@opensolaris.org
13037302Sgdamore@opensolaris.org PUT16(ss, REG_INT_STAT, INT_CMD);
13047302Sgdamore@opensolaris.org
13057302Sgdamore@opensolaris.org /* command completed */
13067302Sgdamore@opensolaris.org sdhost_get_response(ss, cmdp);
13077302Sgdamore@opensolaris.org return (SDA_EOK);
13087302Sgdamore@opensolaris.org }
13097302Sgdamore@opensolaris.org
13107302Sgdamore@opensolaris.org if ((errs = (GET16(ss, REG_ERR_STAT) & ERR_CMD)) != 0) {
13117302Sgdamore@opensolaris.org PUT16(ss, REG_ERR_STAT, errs);
13127302Sgdamore@opensolaris.org
13137302Sgdamore@opensolaris.org /* command timeout isn't a host failure */
13147302Sgdamore@opensolaris.org if ((errs & ERR_CMD_TMO) == ERR_CMD_TMO) {
13158289Sgdamore@opensolaris.org rv = SDA_ETIME;
13168289Sgdamore@opensolaris.org } else if ((errs & ERR_CMD_CRC) == ERR_CMD_CRC) {
13178289Sgdamore@opensolaris.org rv = SDA_ECRC7;
13188289Sgdamore@opensolaris.org } else {
13198289Sgdamore@opensolaris.org rv = SDA_EPROTO;
13207302Sgdamore@opensolaris.org }
13218289Sgdamore@opensolaris.org goto error;
13227302Sgdamore@opensolaris.org }
13237302Sgdamore@opensolaris.org
13247302Sgdamore@opensolaris.org drv_usecwait(5);
13257302Sgdamore@opensolaris.org }
13267302Sgdamore@opensolaris.org
13278289Sgdamore@opensolaris.org rv = SDA_ETIME;
13288289Sgdamore@opensolaris.org
13298289Sgdamore@opensolaris.org error:
13308289Sgdamore@opensolaris.org /*
13318289Sgdamore@opensolaris.org * NB: We need to soft reset the CMD and DAT
13328289Sgdamore@opensolaris.org * lines after a failure of this sort.
13338289Sgdamore@opensolaris.org */
13348289Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
13358289Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
13368289Sgdamore@opensolaris.org
13378289Sgdamore@opensolaris.org return (rv);
13387302Sgdamore@opensolaris.org }
13397302Sgdamore@opensolaris.org
13407302Sgdamore@opensolaris.org sda_err_t
sdhost_poll(void * arg)13417302Sgdamore@opensolaris.org sdhost_poll(void *arg)
13427302Sgdamore@opensolaris.org {
13437302Sgdamore@opensolaris.org sdslot_t *ss = arg;
13447302Sgdamore@opensolaris.org
13457302Sgdamore@opensolaris.org (void) sdhost_slot_intr(ss);
13467302Sgdamore@opensolaris.org return (SDA_EOK);
13477302Sgdamore@opensolaris.org }
13487302Sgdamore@opensolaris.org
13497302Sgdamore@opensolaris.org sda_err_t
sdhost_cmd(void * arg,sda_cmd_t * cmdp)13507302Sgdamore@opensolaris.org sdhost_cmd(void *arg, sda_cmd_t *cmdp)
13517302Sgdamore@opensolaris.org {
13527302Sgdamore@opensolaris.org sdslot_t *ss = arg;
13537302Sgdamore@opensolaris.org uint16_t command;
13547302Sgdamore@opensolaris.org uint16_t mode;
13557302Sgdamore@opensolaris.org sda_err_t rv;
13567302Sgdamore@opensolaris.org
13577302Sgdamore@opensolaris.org /*
13587302Sgdamore@opensolaris.org * Command register:
13597302Sgdamore@opensolaris.org * bit 13-8 = command index
13607302Sgdamore@opensolaris.org * bit 7-6 = command type (always zero for us!)
13617302Sgdamore@opensolaris.org * bit 5 = data present select
13627302Sgdamore@opensolaris.org * bit 4 = command index check (always on!)
13637302Sgdamore@opensolaris.org * bit 3 = command CRC check enable
13647302Sgdamore@opensolaris.org * bit 2 = reserved
13657302Sgdamore@opensolaris.org * bit 1-0 = response type
13667302Sgdamore@opensolaris.org */
13677302Sgdamore@opensolaris.org
13687302Sgdamore@opensolaris.org command = ((uint16_t)cmdp->sc_index << 8);
13697302Sgdamore@opensolaris.org command |= COMMAND_TYPE_NORM |
13707302Sgdamore@opensolaris.org COMMAND_INDEX_CHECK_EN | COMMAND_CRC_CHECK_EN;
13717302Sgdamore@opensolaris.org
13727302Sgdamore@opensolaris.org switch (cmdp->sc_rtype) {
13737302Sgdamore@opensolaris.org case R0:
13747302Sgdamore@opensolaris.org command |= COMMAND_RESP_NONE;
13757302Sgdamore@opensolaris.org break;
13767302Sgdamore@opensolaris.org case R1:
13777302Sgdamore@opensolaris.org case R5:
13787302Sgdamore@opensolaris.org case R6:
13797302Sgdamore@opensolaris.org case R7:
13807302Sgdamore@opensolaris.org command |= COMMAND_RESP_48;
13817302Sgdamore@opensolaris.org break;
13827302Sgdamore@opensolaris.org case R1b:
13837302Sgdamore@opensolaris.org case R5b:
13847302Sgdamore@opensolaris.org command |= COMMAND_RESP_48_BUSY;
13857302Sgdamore@opensolaris.org break;
13867302Sgdamore@opensolaris.org case R2:
13877302Sgdamore@opensolaris.org command |= COMMAND_RESP_136;
13887302Sgdamore@opensolaris.org command &= ~(COMMAND_INDEX_CHECK_EN | COMMAND_CRC_CHECK_EN);
13897302Sgdamore@opensolaris.org break;
13907302Sgdamore@opensolaris.org case R3:
13917302Sgdamore@opensolaris.org case R4:
13927302Sgdamore@opensolaris.org command |= COMMAND_RESP_48;
13937302Sgdamore@opensolaris.org command &= ~COMMAND_CRC_CHECK_EN;
13947302Sgdamore@opensolaris.org command &= ~COMMAND_INDEX_CHECK_EN;
13957302Sgdamore@opensolaris.org break;
13967302Sgdamore@opensolaris.org default:
13977302Sgdamore@opensolaris.org return (SDA_EINVAL);
13987302Sgdamore@opensolaris.org }
13997302Sgdamore@opensolaris.org
14007302Sgdamore@opensolaris.org mutex_enter(&ss->ss_lock);
14017302Sgdamore@opensolaris.org if (ss->ss_suspended) {
14027302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
14037302Sgdamore@opensolaris.org return (SDA_ESUSPENDED);
14047302Sgdamore@opensolaris.org }
14057302Sgdamore@opensolaris.org
14067302Sgdamore@opensolaris.org if (cmdp->sc_nblks != 0) {
14077302Sgdamore@opensolaris.org uint16_t blksz;
14087302Sgdamore@opensolaris.org uint16_t nblks;
14097302Sgdamore@opensolaris.org
14107302Sgdamore@opensolaris.org blksz = cmdp->sc_blksz;
14117302Sgdamore@opensolaris.org nblks = cmdp->sc_nblks;
14127302Sgdamore@opensolaris.org
14137302Sgdamore@opensolaris.org /*
14147302Sgdamore@opensolaris.org * Ensure that we have good data.
14157302Sgdamore@opensolaris.org */
14167302Sgdamore@opensolaris.org if ((blksz < 1) || (blksz > 2048)) {
14177302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
14187302Sgdamore@opensolaris.org return (SDA_EINVAL);
14197302Sgdamore@opensolaris.org }
14207302Sgdamore@opensolaris.org command |= COMMAND_DATA_PRESENT;
14217302Sgdamore@opensolaris.org
14227302Sgdamore@opensolaris.org ss->ss_blksz = blksz;
14237302Sgdamore@opensolaris.org
14248708Sgdamore@opensolaris.org ss->ss_kvaddr = (void *)cmdp->sc_kvaddr;
14258708Sgdamore@opensolaris.org ss->ss_rcnt = 0;
14268708Sgdamore@opensolaris.org ss->ss_resid = 0;
14278708Sgdamore@opensolaris.org
14287302Sgdamore@opensolaris.org /*
14297302Sgdamore@opensolaris.org * Only SDMA for now. We can investigate ADMA2 later.
14307302Sgdamore@opensolaris.org * (Right now we don't have ADMA2 capable hardware.)
14318708Sgdamore@opensolaris.org * We always use a bounce buffer, which solves weird
14328708Sgdamore@opensolaris.org * problems with certain controllers. Doing this with
14338708Sgdamore@opensolaris.org * a large contiguous buffer may be faster than
14348708Sgdamore@opensolaris.org * servicing all the little per-page interrupts
14358708Sgdamore@opensolaris.org * anyway. (Bcopy of 64 K vs. 16 interrupts.)
14367302Sgdamore@opensolaris.org */
14377302Sgdamore@opensolaris.org if (((ss->ss_capab & CAPAB_SDMA) != 0) &&
14388708Sgdamore@opensolaris.org ((ss->ss_flags & SDFLAG_FORCE_PIO) == 0) &&
14398708Sgdamore@opensolaris.org ((blksz * nblks) <= SDHOST_BOUNCESZ)) {
14407302Sgdamore@opensolaris.org
14418708Sgdamore@opensolaris.org if (cmdp->sc_flags & SDA_CMDF_WRITE) {
14428708Sgdamore@opensolaris.org /*
14438708Sgdamore@opensolaris.org * if we're writing, prepare initial round
14448708Sgdamore@opensolaris.org * of data
14458708Sgdamore@opensolaris.org */
14468708Sgdamore@opensolaris.org bcopy(cmdp->sc_kvaddr, ss->ss_bounce,
14478708Sgdamore@opensolaris.org nblks * blksz);
14488708Sgdamore@opensolaris.org (void) ddi_dma_sync(ss->ss_bufdmah, 0, 0,
14498708Sgdamore@opensolaris.org DDI_DMA_SYNC_FORDEV);
14508708Sgdamore@opensolaris.org } else {
14518708Sgdamore@opensolaris.org ss->ss_rcnt = nblks * blksz;
14528708Sgdamore@opensolaris.org }
14538708Sgdamore@opensolaris.org PUT32(ss, REG_SDMA_ADDR, ss->ss_bufdmac.dmac_address);
14547302Sgdamore@opensolaris.org mode = XFR_MODE_DMA_EN;
14558708Sgdamore@opensolaris.org PUT16(ss, REG_BLKSZ, BLKSZ_BOUNDARY_512K | blksz);
14568708Sgdamore@opensolaris.org ss->ss_ndma++;
14577302Sgdamore@opensolaris.org
14587302Sgdamore@opensolaris.org } else {
14598708Sgdamore@opensolaris.org mode = 0;
14608708Sgdamore@opensolaris.org ss->ss_npio++;
14617302Sgdamore@opensolaris.org ss->ss_resid = nblks;
14627302Sgdamore@opensolaris.org PUT16(ss, REG_BLKSZ, blksz);
14637302Sgdamore@opensolaris.org }
14647302Sgdamore@opensolaris.org
14657302Sgdamore@opensolaris.org if (nblks > 1) {
14667302Sgdamore@opensolaris.org mode |= XFR_MODE_MULTI | XFR_MODE_COUNT;
14677302Sgdamore@opensolaris.org if (cmdp->sc_flags & SDA_CMDF_AUTO_CMD12)
14687302Sgdamore@opensolaris.org mode |= XFR_MODE_AUTO_CMD12;
14698708Sgdamore@opensolaris.org ss->ss_nmulti++;
14707302Sgdamore@opensolaris.org }
14717302Sgdamore@opensolaris.org if ((cmdp->sc_flags & SDA_CMDF_READ) != 0) {
14727302Sgdamore@opensolaris.org mode |= XFR_MODE_READ;
14738708Sgdamore@opensolaris.org ss->ss_ixfr++;
14748708Sgdamore@opensolaris.org ss->ss_ibytes += nblks * blksz;
14758708Sgdamore@opensolaris.org } else {
14768708Sgdamore@opensolaris.org ss->ss_oxfr++;
14778708Sgdamore@opensolaris.org ss->ss_obytes += nblks * blksz;
14787302Sgdamore@opensolaris.org }
14797302Sgdamore@opensolaris.org
14807302Sgdamore@opensolaris.org ss->ss_mode = mode;
14817302Sgdamore@opensolaris.org
14827302Sgdamore@opensolaris.org PUT8(ss, REG_TIMEOUT_CONTROL, ss->ss_tmoutclk);
14837302Sgdamore@opensolaris.org PUT16(ss, REG_BLOCK_COUNT, nblks);
14847302Sgdamore@opensolaris.org PUT16(ss, REG_XFR_MODE, mode);
14857302Sgdamore@opensolaris.org }
14867302Sgdamore@opensolaris.org
14877302Sgdamore@opensolaris.org PUT32(ss, REG_ARGUMENT, cmdp->sc_argument);
14887302Sgdamore@opensolaris.org PUT16(ss, REG_COMMAND, command);
14897302Sgdamore@opensolaris.org
14908708Sgdamore@opensolaris.org ss->ss_ncmd++;
14917302Sgdamore@opensolaris.org rv = sdhost_wait_cmd(ss, cmdp);
14927302Sgdamore@opensolaris.org
14937302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
14947302Sgdamore@opensolaris.org
14957302Sgdamore@opensolaris.org return (rv);
14967302Sgdamore@opensolaris.org }
14977302Sgdamore@opensolaris.org
14987302Sgdamore@opensolaris.org sda_err_t
sdhost_getprop(void * arg,sda_prop_t prop,uint32_t * val)14997302Sgdamore@opensolaris.org sdhost_getprop(void *arg, sda_prop_t prop, uint32_t *val)
15007302Sgdamore@opensolaris.org {
15017302Sgdamore@opensolaris.org sdslot_t *ss = arg;
15027302Sgdamore@opensolaris.org sda_err_t rv = 0;
15037302Sgdamore@opensolaris.org
15047302Sgdamore@opensolaris.org mutex_enter(&ss->ss_lock);
15057302Sgdamore@opensolaris.org
15067302Sgdamore@opensolaris.org if (ss->ss_suspended) {
15077302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
15087302Sgdamore@opensolaris.org return (SDA_ESUSPENDED);
15097302Sgdamore@opensolaris.org }
15107302Sgdamore@opensolaris.org switch (prop) {
15117302Sgdamore@opensolaris.org case SDA_PROP_INSERTED:
15127302Sgdamore@opensolaris.org if (CHECK_STATE(ss, CARD_INSERTED)) {
15137302Sgdamore@opensolaris.org *val = B_TRUE;
15147302Sgdamore@opensolaris.org } else {
15157302Sgdamore@opensolaris.org *val = B_FALSE;
15167302Sgdamore@opensolaris.org }
15177302Sgdamore@opensolaris.org break;
15187302Sgdamore@opensolaris.org
15197302Sgdamore@opensolaris.org case SDA_PROP_WPROTECT:
15207302Sgdamore@opensolaris.org if (CHECK_STATE(ss, WRITE_ENABLE)) {
15217302Sgdamore@opensolaris.org *val = B_FALSE;
15227302Sgdamore@opensolaris.org } else {
15237302Sgdamore@opensolaris.org *val = B_TRUE;
15247302Sgdamore@opensolaris.org }
15257302Sgdamore@opensolaris.org break;
15267302Sgdamore@opensolaris.org
15277302Sgdamore@opensolaris.org case SDA_PROP_OCR:
15287302Sgdamore@opensolaris.org *val = ss->ss_ocr;
15297302Sgdamore@opensolaris.org break;
15307302Sgdamore@opensolaris.org
15317302Sgdamore@opensolaris.org case SDA_PROP_CLOCK:
15327302Sgdamore@opensolaris.org *val = ss->ss_cardclk;
15337302Sgdamore@opensolaris.org break;
15347302Sgdamore@opensolaris.org
15357302Sgdamore@opensolaris.org case SDA_PROP_CAP_HISPEED:
15367302Sgdamore@opensolaris.org if ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0) {
15377302Sgdamore@opensolaris.org *val = B_TRUE;
15387302Sgdamore@opensolaris.org } else {
15397302Sgdamore@opensolaris.org *val = B_FALSE;
15407302Sgdamore@opensolaris.org }
15417302Sgdamore@opensolaris.org break;
15427302Sgdamore@opensolaris.org
15437302Sgdamore@opensolaris.org case SDA_PROP_CAP_4BITS:
15447302Sgdamore@opensolaris.org *val = B_TRUE;
15457302Sgdamore@opensolaris.org break;
15467302Sgdamore@opensolaris.org
15477302Sgdamore@opensolaris.org case SDA_PROP_CAP_NOPIO:
15488708Sgdamore@opensolaris.org /*
15498708Sgdamore@opensolaris.org * We might have to use PIO for buffers that don't
15508708Sgdamore@opensolaris.org * have reasonable alignments. A few controllers seem
15518708Sgdamore@opensolaris.org * not to deal with granularity or alignments of
15528708Sgdamore@opensolaris.org * something other 32-bits.
15538708Sgdamore@opensolaris.org */
15548708Sgdamore@opensolaris.org *val = B_FALSE;
15557302Sgdamore@opensolaris.org break;
15567302Sgdamore@opensolaris.org
15577302Sgdamore@opensolaris.org case SDA_PROP_CAP_INTR:
15587302Sgdamore@opensolaris.org case SDA_PROP_CAP_8BITS:
15597302Sgdamore@opensolaris.org *val = B_FALSE;
15607302Sgdamore@opensolaris.org break;
15617302Sgdamore@opensolaris.org
15627302Sgdamore@opensolaris.org default:
15637302Sgdamore@opensolaris.org rv = SDA_ENOTSUP;
15647302Sgdamore@opensolaris.org break;
15657302Sgdamore@opensolaris.org }
15667302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
15677302Sgdamore@opensolaris.org
15687302Sgdamore@opensolaris.org return (rv);
15697302Sgdamore@opensolaris.org }
15707302Sgdamore@opensolaris.org
15717302Sgdamore@opensolaris.org sda_err_t
sdhost_setprop(void * arg,sda_prop_t prop,uint32_t val)15727302Sgdamore@opensolaris.org sdhost_setprop(void *arg, sda_prop_t prop, uint32_t val)
15737302Sgdamore@opensolaris.org {
15747302Sgdamore@opensolaris.org sdslot_t *ss = arg;
15757302Sgdamore@opensolaris.org sda_err_t rv = SDA_EOK;
15767302Sgdamore@opensolaris.org
15777302Sgdamore@opensolaris.org mutex_enter(&ss->ss_lock);
15787302Sgdamore@opensolaris.org
15797302Sgdamore@opensolaris.org if (ss->ss_suspended) {
15807302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
15817302Sgdamore@opensolaris.org return (SDA_ESUSPENDED);
15827302Sgdamore@opensolaris.org }
15837302Sgdamore@opensolaris.org
15847302Sgdamore@opensolaris.org switch (prop) {
15857302Sgdamore@opensolaris.org case SDA_PROP_LED:
15867302Sgdamore@opensolaris.org if (val) {
15877302Sgdamore@opensolaris.org SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_LED_ON);
15887302Sgdamore@opensolaris.org } else {
15897302Sgdamore@opensolaris.org CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_LED_ON);
15907302Sgdamore@opensolaris.org }
15917302Sgdamore@opensolaris.org break;
15927302Sgdamore@opensolaris.org
15937302Sgdamore@opensolaris.org case SDA_PROP_CLOCK:
15947302Sgdamore@opensolaris.org rv = sdhost_set_clock(arg, val);
15957302Sgdamore@opensolaris.org break;
15967302Sgdamore@opensolaris.org
15977302Sgdamore@opensolaris.org case SDA_PROP_BUSWIDTH:
15987302Sgdamore@opensolaris.org switch (val) {
15997302Sgdamore@opensolaris.org case 1:
16008708Sgdamore@opensolaris.org ss->ss_width = val;
16017302Sgdamore@opensolaris.org CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_DATA_WIDTH);
16027302Sgdamore@opensolaris.org break;
16037302Sgdamore@opensolaris.org case 4:
16048708Sgdamore@opensolaris.org ss->ss_width = val;
16057302Sgdamore@opensolaris.org SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_DATA_WIDTH);
16067302Sgdamore@opensolaris.org break;
16077302Sgdamore@opensolaris.org default:
16087302Sgdamore@opensolaris.org rv = SDA_EINVAL;
16097302Sgdamore@opensolaris.org }
16107302Sgdamore@opensolaris.org break;
16117302Sgdamore@opensolaris.org
16127302Sgdamore@opensolaris.org case SDA_PROP_OCR:
16137302Sgdamore@opensolaris.org val &= ss->ss_ocr;
16147302Sgdamore@opensolaris.org
16157302Sgdamore@opensolaris.org if (val & OCR_17_18V) {
16167302Sgdamore@opensolaris.org PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_18V);
16177302Sgdamore@opensolaris.org PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_18V |
16187302Sgdamore@opensolaris.org POWER_CONTROL_BUS_POWER);
16197302Sgdamore@opensolaris.org } else if (val & OCR_29_30V) {
16207302Sgdamore@opensolaris.org PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_30V);
16217302Sgdamore@opensolaris.org PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_30V |
16227302Sgdamore@opensolaris.org POWER_CONTROL_BUS_POWER);
16237302Sgdamore@opensolaris.org } else if (val & OCR_32_33V) {
16247302Sgdamore@opensolaris.org PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_33V);
16257302Sgdamore@opensolaris.org PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_33V |
16267302Sgdamore@opensolaris.org POWER_CONTROL_BUS_POWER);
16277302Sgdamore@opensolaris.org } else if (val == 0) {
16287302Sgdamore@opensolaris.org /* turn off power */
16297302Sgdamore@opensolaris.org PUT8(ss, REG_POWER_CONTROL, 0);
16307302Sgdamore@opensolaris.org } else {
16317302Sgdamore@opensolaris.org rv = SDA_EINVAL;
16327302Sgdamore@opensolaris.org }
16337302Sgdamore@opensolaris.org break;
16347302Sgdamore@opensolaris.org
16357302Sgdamore@opensolaris.org case SDA_PROP_HISPEED:
16367302Sgdamore@opensolaris.org if (val) {
16377302Sgdamore@opensolaris.org SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
16387302Sgdamore@opensolaris.org } else {
16397302Sgdamore@opensolaris.org CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
16407302Sgdamore@opensolaris.org }
16417302Sgdamore@opensolaris.org /* give clocks time to settle */
16427302Sgdamore@opensolaris.org drv_usecwait(10);
16437302Sgdamore@opensolaris.org break;
16447302Sgdamore@opensolaris.org
16457302Sgdamore@opensolaris.org default:
16467302Sgdamore@opensolaris.org rv = SDA_ENOTSUP;
16477302Sgdamore@opensolaris.org break;
16487302Sgdamore@opensolaris.org }
16497302Sgdamore@opensolaris.org
16507302Sgdamore@opensolaris.org /*
16517302Sgdamore@opensolaris.org * Apparently some controllers (ENE) have issues with changing
16527302Sgdamore@opensolaris.org * certain parameters (bus width seems to be one), requiring
16537302Sgdamore@opensolaris.org * a reset of the DAT and CMD lines.
16547302Sgdamore@opensolaris.org */
16557302Sgdamore@opensolaris.org if (rv == SDA_EOK) {
16567302Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
16577302Sgdamore@opensolaris.org (void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
16587302Sgdamore@opensolaris.org }
16597302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
16607302Sgdamore@opensolaris.org return (rv);
16617302Sgdamore@opensolaris.org }
16627302Sgdamore@opensolaris.org
16637302Sgdamore@opensolaris.org sda_err_t
sdhost_reset(void * arg)16647302Sgdamore@opensolaris.org sdhost_reset(void *arg)
16657302Sgdamore@opensolaris.org {
16667302Sgdamore@opensolaris.org sdslot_t *ss = arg;
16677302Sgdamore@opensolaris.org
16687302Sgdamore@opensolaris.org mutex_enter(&ss->ss_lock);
16697302Sgdamore@opensolaris.org if (!ss->ss_suspended) {
16707302Sgdamore@opensolaris.org if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK) {
16717302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
16727302Sgdamore@opensolaris.org return (SDA_ETIME);
16737302Sgdamore@opensolaris.org }
16747302Sgdamore@opensolaris.org sdhost_enable_interrupts(ss);
16757302Sgdamore@opensolaris.org }
16767302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
16777302Sgdamore@opensolaris.org return (SDA_EOK);
16787302Sgdamore@opensolaris.org }
16797302Sgdamore@opensolaris.org
16807302Sgdamore@opensolaris.org sda_err_t
sdhost_halt(void * arg)16817302Sgdamore@opensolaris.org sdhost_halt(void *arg)
16827302Sgdamore@opensolaris.org {
16837302Sgdamore@opensolaris.org sdslot_t *ss = arg;
16847302Sgdamore@opensolaris.org
16857302Sgdamore@opensolaris.org mutex_enter(&ss->ss_lock);
16867302Sgdamore@opensolaris.org if (!ss->ss_suspended) {
16877302Sgdamore@opensolaris.org sdhost_disable_interrupts(ss);
16887302Sgdamore@opensolaris.org /* this has the side effect of removing power from the card */
16897302Sgdamore@opensolaris.org if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK) {
16907302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
16917302Sgdamore@opensolaris.org return (SDA_ETIME);
16927302Sgdamore@opensolaris.org }
16937302Sgdamore@opensolaris.org }
16947302Sgdamore@opensolaris.org mutex_exit(&ss->ss_lock);
16957302Sgdamore@opensolaris.org return (SDA_EOK);
16967302Sgdamore@opensolaris.org }
1697