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