14bb7efa7SGarrett D'Amore /*
24bb7efa7SGarrett D'Amore  * CDDL HEADER START
34bb7efa7SGarrett D'Amore  *
44bb7efa7SGarrett D'Amore  * The contents of this file are subject to the terms of the
54bb7efa7SGarrett D'Amore  * Common Development and Distribution License (the "License").
64bb7efa7SGarrett D'Amore  * You may not use this file except in compliance with the License.
74bb7efa7SGarrett D'Amore  *
84bb7efa7SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94bb7efa7SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
104bb7efa7SGarrett D'Amore  * See the License for the specific language governing permissions
114bb7efa7SGarrett D'Amore  * and limitations under the License.
124bb7efa7SGarrett D'Amore  *
134bb7efa7SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
144bb7efa7SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154bb7efa7SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
164bb7efa7SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
174bb7efa7SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
184bb7efa7SGarrett D'Amore  *
194bb7efa7SGarrett D'Amore  * CDDL HEADER END
204bb7efa7SGarrett D'Amore  */
214bb7efa7SGarrett D'Amore /*
22*28e61230SGarrett 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 
284bb7efa7SGarrett D'Amore typedef	struct sdslot	sdslot_t;
294bb7efa7SGarrett D'Amore typedef	struct sdhost	sdhost_t;
304bb7efa7SGarrett D'Amore 
314bb7efa7SGarrett D'Amore /*
324bb7efa7SGarrett D'Amore  * Per slot state.
334bb7efa7SGarrett D'Amore  */
344bb7efa7SGarrett D'Amore struct sdslot {
354bb7efa7SGarrett D'Amore 	sda_host_t		*ss_host;
364bb7efa7SGarrett D'Amore 	int			ss_num;
374bb7efa7SGarrett D'Amore 	ddi_acc_handle_t	ss_acch;
384bb7efa7SGarrett D'Amore 	caddr_t 		ss_regva;
394bb7efa7SGarrett D'Amore 	kmutex_t		ss_lock;
404bb7efa7SGarrett D'Amore 	uint32_t		ss_capab;
414bb7efa7SGarrett D'Amore 	uint32_t		ss_baseclk;	/* Hz */
424bb7efa7SGarrett D'Amore 	uint32_t		ss_cardclk;	/* Hz */
434bb7efa7SGarrett D'Amore 	uint8_t			ss_tmoutclk;
444bb7efa7SGarrett D'Amore 	uint32_t		ss_tmusecs;	/* timeout units in usecs */
454bb7efa7SGarrett D'Amore 	uint32_t		ss_ocr;		/* OCR formatted voltages */
464bb7efa7SGarrett D'Amore 	uint16_t		ss_mode;
474bb7efa7SGarrett D'Amore 	boolean_t		ss_suspended;
484bb7efa7SGarrett D'Amore 
494bb7efa7SGarrett D'Amore 	/*
504bb7efa7SGarrett D'Amore 	 * Command in progress
514bb7efa7SGarrett D'Amore 	 */
524bb7efa7SGarrett D'Amore 	uint8_t			*ss_kvaddr;
534bb7efa7SGarrett D'Amore 	ddi_dma_cookie_t	*ss_dmacs;
544bb7efa7SGarrett D'Amore 	uint_t			ss_ndmac;
554bb7efa7SGarrett D'Amore 	int			ss_blksz;
564bb7efa7SGarrett D'Amore 	uint16_t		ss_resid;	/* in blocks */
574bb7efa7SGarrett D'Amore 
584bb7efa7SGarrett D'Amore 	/* scratch buffer, to receive extra PIO data */
594bb7efa7SGarrett D'Amore 	uint32_t		ss_bounce[2048 / 4];
604bb7efa7SGarrett D'Amore };
614bb7efa7SGarrett D'Amore 
624bb7efa7SGarrett D'Amore /*
634bb7efa7SGarrett D'Amore  * Per controller state.
644bb7efa7SGarrett D'Amore  */
654bb7efa7SGarrett D'Amore struct sdhost {
664bb7efa7SGarrett D'Amore 	int			sh_numslots;
674bb7efa7SGarrett D'Amore 	ddi_dma_attr_t		sh_dmaattr;
684bb7efa7SGarrett D'Amore 	sdslot_t		sh_slots[SDHOST_MAXSLOTS];
694bb7efa7SGarrett D'Amore 	sda_host_t		*sh_host;
704bb7efa7SGarrett D'Amore 
714bb7efa7SGarrett D'Amore 	/*
724bb7efa7SGarrett D'Amore 	 * Interrupt related information.
734bb7efa7SGarrett D'Amore 	 */
744bb7efa7SGarrett D'Amore 	ddi_intr_handle_t	sh_ihandle;
754bb7efa7SGarrett D'Amore 	int			sh_icap;
764bb7efa7SGarrett D'Amore 	uint_t			sh_ipri;
774bb7efa7SGarrett D'Amore };
784bb7efa7SGarrett D'Amore 
794bb7efa7SGarrett D'Amore 
804bb7efa7SGarrett D'Amore static int sdhost_attach(dev_info_t *, ddi_attach_cmd_t);
814bb7efa7SGarrett D'Amore static int sdhost_detach(dev_info_t *, ddi_detach_cmd_t);
82f2b90c3cSGarrett D'Amore static int sdhost_quiesce(dev_info_t *);
834bb7efa7SGarrett D'Amore static int sdhost_suspend(dev_info_t *);
844bb7efa7SGarrett D'Amore static int sdhost_resume(dev_info_t *);
854bb7efa7SGarrett D'Amore 
864bb7efa7SGarrett D'Amore static void sdhost_enable_interrupts(sdslot_t *);
874bb7efa7SGarrett D'Amore static void sdhost_disable_interrupts(sdslot_t *);
884bb7efa7SGarrett D'Amore static int sdhost_setup_intr(dev_info_t *, sdhost_t *);
894bb7efa7SGarrett D'Amore static uint_t sdhost_intr(caddr_t, caddr_t);
904bb7efa7SGarrett D'Amore static int sdhost_init_slot(dev_info_t *, sdhost_t *, int, int);
914bb7efa7SGarrett D'Amore static void sdhost_uninit_slot(sdhost_t *, int);
924bb7efa7SGarrett D'Amore static sda_err_t sdhost_soft_reset(sdslot_t *, uint8_t);
934bb7efa7SGarrett D'Amore static sda_err_t sdhost_set_clock(sdslot_t *, uint32_t);
944bb7efa7SGarrett D'Amore static void sdhost_xfer_done(sdslot_t *, sda_err_t);
954bb7efa7SGarrett D'Amore static sda_err_t sdhost_wait_cmd(sdslot_t *, sda_cmd_t *);
964bb7efa7SGarrett D'Amore static uint_t sdhost_slot_intr(sdslot_t *);
974bb7efa7SGarrett D'Amore 
984bb7efa7SGarrett D'Amore static sda_err_t sdhost_cmd(void *, sda_cmd_t *);
994bb7efa7SGarrett D'Amore static sda_err_t sdhost_getprop(void *, sda_prop_t, uint32_t *);
1004bb7efa7SGarrett D'Amore static sda_err_t sdhost_setprop(void *, sda_prop_t, uint32_t);
1014bb7efa7SGarrett D'Amore static sda_err_t sdhost_poll(void *);
1024bb7efa7SGarrett D'Amore static sda_err_t sdhost_reset(void *);
1034bb7efa7SGarrett D'Amore static sda_err_t sdhost_halt(void *);
1044bb7efa7SGarrett D'Amore 
1054bb7efa7SGarrett D'Amore static struct dev_ops sdhost_dev_ops = {
1064bb7efa7SGarrett D'Amore 	DEVO_REV,			/* devo_rev */
1074bb7efa7SGarrett D'Amore 	0,				/* devo_refcnt */
1084bb7efa7SGarrett D'Amore 	ddi_no_info,			/* devo_getinfo */
1094bb7efa7SGarrett D'Amore 	nulldev,			/* devo_identify */
1104bb7efa7SGarrett D'Amore 	nulldev,			/* devo_probe */
1114bb7efa7SGarrett D'Amore 	sdhost_attach,			/* devo_attach */
1124bb7efa7SGarrett D'Amore 	sdhost_detach,			/* devo_detach */
1134bb7efa7SGarrett D'Amore 	nodev,				/* devo_reset */
1144bb7efa7SGarrett D'Amore 	NULL,				/* devo_cb_ops */
1154bb7efa7SGarrett D'Amore 	NULL,				/* devo_bus_ops */
11619397407SSherry Moore 	NULL,				/* devo_power */
117f2b90c3cSGarrett D'Amore 	sdhost_quiesce,			/* devo_quiesce */
1184bb7efa7SGarrett D'Amore };
1194bb7efa7SGarrett D'Amore 
1204bb7efa7SGarrett D'Amore static struct modldrv sdhost_modldrv = {
1214bb7efa7SGarrett D'Amore 	&mod_driverops,			/* drv_modops */
1224bb7efa7SGarrett D'Amore 	"Standard SD Host Controller",	/* drv_linkinfo */
1234bb7efa7SGarrett D'Amore 	&sdhost_dev_ops			/* drv_dev_ops */
1244bb7efa7SGarrett D'Amore };
1254bb7efa7SGarrett D'Amore 
1264bb7efa7SGarrett D'Amore static struct modlinkage modlinkage = {
1274bb7efa7SGarrett D'Amore 	MODREV_1,			/* ml_rev */
1284bb7efa7SGarrett D'Amore 	{ &sdhost_modldrv, NULL }	/* ml_linkage */
1294bb7efa7SGarrett D'Amore };
1304bb7efa7SGarrett D'Amore 
1314bb7efa7SGarrett D'Amore static struct sda_ops sdhost_ops = {
1324bb7efa7SGarrett D'Amore 	SDA_OPS_VERSION,
1334bb7efa7SGarrett D'Amore 	sdhost_cmd,			/* so_cmd */
1344bb7efa7SGarrett D'Amore 	sdhost_getprop,			/* so_getprop */
1354bb7efa7SGarrett D'Amore 	sdhost_setprop,			/* so_setprop */
1364bb7efa7SGarrett D'Amore 	sdhost_poll,			/* so_poll */
1374bb7efa7SGarrett D'Amore 	sdhost_reset,			/* so_reset */
1384bb7efa7SGarrett D'Amore 	sdhost_halt,			/* so_halt */
1394bb7efa7SGarrett D'Amore };
1404bb7efa7SGarrett D'Amore 
1414bb7efa7SGarrett D'Amore static ddi_device_acc_attr_t sdhost_regattr = {
1424bb7efa7SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
1434bb7efa7SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,	/* devacc_attr_endian_flags */
1444bb7efa7SGarrett D'Amore 	DDI_STRICTORDER_ACC,	/* devacc_attr_dataorder */
1454bb7efa7SGarrett D'Amore 	DDI_DEFAULT_ACC,	/* devacc_attr_access */
1464bb7efa7SGarrett D'Amore };
1474bb7efa7SGarrett D'Amore 
1484bb7efa7SGarrett D'Amore #define	GET16(ss, reg)	\
1494bb7efa7SGarrett D'Amore 	ddi_get16(ss->ss_acch, (void *)(ss->ss_regva + reg))
1504bb7efa7SGarrett D'Amore #define	PUT16(ss, reg, val)	\
1514bb7efa7SGarrett D'Amore 	ddi_put16(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
1524bb7efa7SGarrett D'Amore #define	GET32(ss, reg)	\
1534bb7efa7SGarrett D'Amore 	ddi_get32(ss->ss_acch, (void *)(ss->ss_regva + reg))
1544bb7efa7SGarrett D'Amore #define	PUT32(ss, reg, val)	\
1554bb7efa7SGarrett D'Amore 	ddi_put32(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
1564bb7efa7SGarrett D'Amore #define	GET64(ss, reg)	\
1574bb7efa7SGarrett D'Amore 	ddi_get64(ss->ss_acch, (void *)(ss->ss_regva + reg))
1584bb7efa7SGarrett D'Amore 
1594bb7efa7SGarrett D'Amore #define	GET8(ss, reg)	\
1604bb7efa7SGarrett D'Amore 	ddi_get8(ss->ss_acch, (void *)(ss->ss_regva + reg))
1614bb7efa7SGarrett D'Amore #define	PUT8(ss, reg, val)	\
1624bb7efa7SGarrett D'Amore 	ddi_put8(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
1634bb7efa7SGarrett D'Amore 
1644bb7efa7SGarrett D'Amore #define	CLR8(ss, reg, mask)	PUT8(ss, reg, GET8(ss, reg) & ~(mask))
1654bb7efa7SGarrett D'Amore #define	SET8(ss, reg, mask)	PUT8(ss, reg, GET8(ss, reg) | (mask))
1664bb7efa7SGarrett D'Amore 
1674bb7efa7SGarrett D'Amore /*
1684bb7efa7SGarrett D'Amore  * If ever anyone uses PIO on SPARC, we have to endian-swap.  But we
1694bb7efa7SGarrett D'Amore  * think that SD Host Controllers are likely to be uncommon on SPARC,
1704bb7efa7SGarrett D'Amore  * and hopefully when they exist at all they will be able to use DMA.
1714bb7efa7SGarrett D'Amore  */
1724bb7efa7SGarrett D'Amore #ifdef	_BIG_ENDIAN
1734bb7efa7SGarrett D'Amore #define	sw32(x)		ddi_swap32(x)
1744bb7efa7SGarrett D'Amore #define	sw16(x)		ddi_swap16(x)
1754bb7efa7SGarrett D'Amore #else
1764bb7efa7SGarrett D'Amore #define	sw32(x)		(x)
1774bb7efa7SGarrett D'Amore #define	sw16(x)		(x)
1784bb7efa7SGarrett D'Amore #endif
1794bb7efa7SGarrett D'Amore 
1804bb7efa7SGarrett D'Amore #define	GETDATA32(ss)		sw32(GET32(ss, REG_DATA))
1814bb7efa7SGarrett D'Amore #define	GETDATA16(ss)		sw16(GET16(ss, REG_DATA))
1824bb7efa7SGarrett D'Amore #define	GETDATA8(ss)		GET8(ss, REG_DATA)
1834bb7efa7SGarrett D'Amore 
1844bb7efa7SGarrett D'Amore #define	PUTDATA32(ss, val)	PUT32(ss, REG_DATA, sw32(val))
1854bb7efa7SGarrett D'Amore #define	PUTDATA16(ss, val)	PUT16(ss, REG_DATA, sw16(val))
1864bb7efa7SGarrett D'Amore #define	PUTDATA8(ss, val)	PUT8(ss, REG_DATA, val)
1874bb7efa7SGarrett D'Amore 
1884bb7efa7SGarrett D'Amore #define	CHECK_STATE(ss, nm)	\
1894bb7efa7SGarrett D'Amore 	((GET32(ss, REG_PRS) & PRS_ ## nm) != 0)
1904bb7efa7SGarrett D'Amore 
1914bb7efa7SGarrett D'Amore int
1924bb7efa7SGarrett D'Amore _init(void)
1934bb7efa7SGarrett D'Amore {
1944bb7efa7SGarrett D'Amore 	int	rv;
1954bb7efa7SGarrett D'Amore 
1964bb7efa7SGarrett D'Amore 	sda_host_init_ops(&sdhost_dev_ops);
1974bb7efa7SGarrett D'Amore 
1984bb7efa7SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
1994bb7efa7SGarrett D'Amore 		sda_host_fini_ops(&sdhost_dev_ops);
2004bb7efa7SGarrett D'Amore 	}
2014bb7efa7SGarrett D'Amore 
2024bb7efa7SGarrett D'Amore 	return (rv);
2034bb7efa7SGarrett D'Amore }
2044bb7efa7SGarrett D'Amore 
2054bb7efa7SGarrett D'Amore int
2064bb7efa7SGarrett D'Amore _fini(void)
2074bb7efa7SGarrett D'Amore {
2084bb7efa7SGarrett D'Amore 	int	rv;
2094bb7efa7SGarrett D'Amore 
2104bb7efa7SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) == 0) {
2114bb7efa7SGarrett D'Amore 		sda_host_fini_ops(&sdhost_dev_ops);
2124bb7efa7SGarrett D'Amore 	}
2134bb7efa7SGarrett D'Amore 	return (rv);
2144bb7efa7SGarrett D'Amore }
2154bb7efa7SGarrett D'Amore 
2164bb7efa7SGarrett D'Amore int
2174bb7efa7SGarrett D'Amore _info(struct modinfo *modinfop)
2184bb7efa7SGarrett D'Amore {
2194bb7efa7SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
2204bb7efa7SGarrett D'Amore }
2214bb7efa7SGarrett D'Amore 
2224bb7efa7SGarrett D'Amore int
2234bb7efa7SGarrett D'Amore sdhost_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2244bb7efa7SGarrett D'Amore {
2254bb7efa7SGarrett D'Amore 	sdhost_t		*shp;
2264bb7efa7SGarrett D'Amore 	ddi_acc_handle_t	pcih;
2274bb7efa7SGarrett D'Amore 	uint8_t			slotinfo;
2284bb7efa7SGarrett D'Amore 	uint8_t			bar;
2294bb7efa7SGarrett D'Amore 	int			i;
230f2b90c3cSGarrett D'Amore 	int			rv;
2314bb7efa7SGarrett D'Amore 
2324bb7efa7SGarrett D'Amore 	switch (cmd) {
2334bb7efa7SGarrett D'Amore 	case DDI_ATTACH:
2344bb7efa7SGarrett D'Amore 		break;
2354bb7efa7SGarrett D'Amore 
2364bb7efa7SGarrett D'Amore 	case DDI_RESUME:
2374bb7efa7SGarrett D'Amore 		return (sdhost_resume(dip));
2384bb7efa7SGarrett D'Amore 
2394bb7efa7SGarrett D'Amore 	default:
2404bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
2414bb7efa7SGarrett D'Amore 	}
2424bb7efa7SGarrett D'Amore 
2434bb7efa7SGarrett D'Amore 	/*
2444bb7efa7SGarrett D'Amore 	 * Soft state allocation.
2454bb7efa7SGarrett D'Amore 	 */
2464bb7efa7SGarrett D'Amore 	shp = kmem_zalloc(sizeof (*shp), KM_SLEEP);
2474bb7efa7SGarrett D'Amore 	ddi_set_driver_private(dip, shp);
2484bb7efa7SGarrett D'Amore 
2494bb7efa7SGarrett D'Amore 	/*
2504bb7efa7SGarrett D'Amore 	 * Initialize DMA attributes.  For now we initialize as for
2514bb7efa7SGarrett D'Amore 	 * SDMA.  If we add ADMA support we can improve this.
2524bb7efa7SGarrett D'Amore 	 */
2534bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_version = DMA_ATTR_V0;
2544bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_addr_lo = 0;
2554bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_addr_hi = 0xffffffffU;
2564bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_count_max = 0xffffffffU;
2574bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_align = 1;
2584bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_burstsizes = 0;	/* for now! */
2594bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_minxfer = 1;
2604bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_maxxfer = 0xffffffffU;
2614bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_sgllen = -1;		/* unlimited! */
2624bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_seg = 0xfff;		/* 4K segments */
2634bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_granular = 1;
2644bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_flags = 0;
2654bb7efa7SGarrett D'Amore 
2664bb7efa7SGarrett D'Amore 	/*
2674bb7efa7SGarrett D'Amore 	 * PCI configuration access to figure out number of slots present.
2684bb7efa7SGarrett D'Amore 	 */
2694bb7efa7SGarrett D'Amore 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
2704bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "pci_config_setup failed");
2714bb7efa7SGarrett D'Amore 		goto failed;
2724bb7efa7SGarrett D'Amore 	}
2734bb7efa7SGarrett D'Amore 
2744bb7efa7SGarrett D'Amore 	slotinfo = pci_config_get8(pcih, SLOTINFO);
2754bb7efa7SGarrett D'Amore 	shp->sh_numslots = SLOTINFO_NSLOT(slotinfo);
2764bb7efa7SGarrett D'Amore 
2774bb7efa7SGarrett D'Amore 	if (shp->sh_numslots > SDHOST_MAXSLOTS) {
2784bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Host reports to have too many slots: %d",
2794bb7efa7SGarrett D'Amore 		    shp->sh_numslots);
2804bb7efa7SGarrett D'Amore 		goto failed;
2814bb7efa7SGarrett D'Amore 	}
2824bb7efa7SGarrett D'Amore 
2834bb7efa7SGarrett D'Amore 	/*
2844bb7efa7SGarrett D'Amore 	 * Enable master accesses and DMA.
2854bb7efa7SGarrett D'Amore 	 */
2864bb7efa7SGarrett D'Amore 	pci_config_put16(pcih, PCI_CONF_COMM,
2874bb7efa7SGarrett D'Amore 	    pci_config_get16(pcih, PCI_CONF_COMM) |
2884bb7efa7SGarrett D'Amore 	    PCI_COMM_MAE | PCI_COMM_ME);
2894bb7efa7SGarrett D'Amore 
2904bb7efa7SGarrett D'Amore 	/*
2914bb7efa7SGarrett D'Amore 	 * Figure out which BAR to use.  Note that we number BARs from
2924bb7efa7SGarrett D'Amore 	 * 1, although PCI and SD Host numbers from 0.  (We number
2934bb7efa7SGarrett D'Amore 	 * from 1, because register number 0 means PCI configuration
2944bb7efa7SGarrett D'Amore 	 * space in Solaris.)
2954bb7efa7SGarrett D'Amore 	 */
2964bb7efa7SGarrett D'Amore 	bar = SLOTINFO_BAR(slotinfo) + 1;
2974bb7efa7SGarrett D'Amore 
2984bb7efa7SGarrett D'Amore 	pci_config_teardown(&pcih);
2994bb7efa7SGarrett D'Amore 
3004bb7efa7SGarrett D'Amore 	/*
3014bb7efa7SGarrett D'Amore 	 * Setup interrupts ... supports the new DDI interrupt API.  This
3024bb7efa7SGarrett D'Amore 	 * will support MSI or MSI-X interrupts if a device is found to
3034bb7efa7SGarrett D'Amore 	 * support it.
3044bb7efa7SGarrett D'Amore 	 */
3054bb7efa7SGarrett D'Amore 	if (sdhost_setup_intr(dip, shp) != DDI_SUCCESS) {
3064bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Failed to setup interrupts");
3074bb7efa7SGarrett D'Amore 		goto failed;
3084bb7efa7SGarrett D'Amore 	}
3094bb7efa7SGarrett D'Amore 
3104bb7efa7SGarrett D'Amore 	shp->sh_host = sda_host_alloc(dip, shp->sh_numslots, &sdhost_ops,
3114bb7efa7SGarrett D'Amore 	    &shp->sh_dmaattr);
3124bb7efa7SGarrett D'Amore 	if (shp->sh_host == NULL) {
3134bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Failed allocating SD host structure");
3144bb7efa7SGarrett D'Amore 		goto failed;
3154bb7efa7SGarrett D'Amore 	}
3164bb7efa7SGarrett D'Amore 
3174bb7efa7SGarrett D'Amore 	/*
3184bb7efa7SGarrett D'Amore 	 * Configure slots, this also maps registers, enables
3194bb7efa7SGarrett D'Amore 	 * interrupts, etc.  Most of the hardware setup is done here.
3204bb7efa7SGarrett D'Amore 	 */
3214bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++) {
3224bb7efa7SGarrett D'Amore 		if (sdhost_init_slot(dip, shp, i, bar + i) != DDI_SUCCESS) {
3234bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "Failed initializing slot %d", i);
3244bb7efa7SGarrett D'Amore 			goto failed;
3254bb7efa7SGarrett D'Amore 		}
3264bb7efa7SGarrett D'Amore 	}
3274bb7efa7SGarrett D'Amore 
3284bb7efa7SGarrett D'Amore 	ddi_report_dev(dip);
3294bb7efa7SGarrett D'Amore 
3304bb7efa7SGarrett D'Amore 	/*
3314bb7efa7SGarrett D'Amore 	 * Enable device interrupts at the DDI layer.
3324bb7efa7SGarrett D'Amore 	 */
333f2b90c3cSGarrett D'Amore 	if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
334f2b90c3cSGarrett D'Amore 		rv = ddi_intr_block_enable(&shp->sh_ihandle, 1);
335f2b90c3cSGarrett D'Amore 	} else {
336f2b90c3cSGarrett D'Amore 		rv = ddi_intr_enable(shp->sh_ihandle);
337f2b90c3cSGarrett D'Amore 	}
338f2b90c3cSGarrett D'Amore 	if (rv != DDI_SUCCESS) {
339f2b90c3cSGarrett D'Amore 		cmn_err(CE_WARN, "Failed enabling interrupts");
340f2b90c3cSGarrett D'Amore 		goto failed;
341f2b90c3cSGarrett D'Amore 	}
3424bb7efa7SGarrett D'Amore 
3434bb7efa7SGarrett D'Amore 	/*
3444bb7efa7SGarrett D'Amore 	 * Mark the slots online with the framework.  This will cause
3454bb7efa7SGarrett D'Amore 	 * the framework to probe them for the presence of cards.
3464bb7efa7SGarrett D'Amore 	 */
3474bb7efa7SGarrett D'Amore 	if (sda_host_attach(shp->sh_host) != DDI_SUCCESS) {
3484bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Failed attaching to SDA framework");
349f2b90c3cSGarrett D'Amore 		if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
350f2b90c3cSGarrett D'Amore 			(void) ddi_intr_block_disable(&shp->sh_ihandle, 1);
351f2b90c3cSGarrett D'Amore 		} else {
352f2b90c3cSGarrett D'Amore 			(void) ddi_intr_disable(shp->sh_ihandle);
353f2b90c3cSGarrett D'Amore 		}
3544bb7efa7SGarrett D'Amore 		goto failed;
3554bb7efa7SGarrett D'Amore 	}
3564bb7efa7SGarrett D'Amore 
3574bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
3584bb7efa7SGarrett D'Amore 
3594bb7efa7SGarrett D'Amore failed:
3604bb7efa7SGarrett D'Amore 	if (shp->sh_ihandle != NULL) {
3614bb7efa7SGarrett D'Amore 		(void) ddi_intr_remove_handler(shp->sh_ihandle);
3624bb7efa7SGarrett D'Amore 		(void) ddi_intr_free(shp->sh_ihandle);
3634bb7efa7SGarrett D'Amore 	}
3644bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++)
3654bb7efa7SGarrett D'Amore 		sdhost_uninit_slot(shp, i);
3664bb7efa7SGarrett D'Amore 	kmem_free(shp, sizeof (*shp));
3674bb7efa7SGarrett D'Amore 
3684bb7efa7SGarrett D'Amore 	return (DDI_FAILURE);
3694bb7efa7SGarrett D'Amore }
3704bb7efa7SGarrett D'Amore 
3714bb7efa7SGarrett D'Amore int
3724bb7efa7SGarrett D'Amore sdhost_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3734bb7efa7SGarrett D'Amore {
3744bb7efa7SGarrett D'Amore 	sdhost_t	*shp;
3754bb7efa7SGarrett D'Amore 	int		i;
3764bb7efa7SGarrett D'Amore 
3774bb7efa7SGarrett D'Amore 	switch (cmd) {
3784bb7efa7SGarrett D'Amore 	case DDI_DETACH:
3794bb7efa7SGarrett D'Amore 		break;
3804bb7efa7SGarrett D'Amore 
3814bb7efa7SGarrett D'Amore 	case DDI_SUSPEND:
3824bb7efa7SGarrett D'Amore 		return (sdhost_suspend(dip));
3834bb7efa7SGarrett D'Amore 
3844bb7efa7SGarrett D'Amore 	default:
3854bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
3864bb7efa7SGarrett D'Amore 	}
3874bb7efa7SGarrett D'Amore 
3884bb7efa7SGarrett D'Amore 	shp = ddi_get_driver_private(dip);
3894bb7efa7SGarrett D'Amore 
3904bb7efa7SGarrett D'Amore 	/*
3914bb7efa7SGarrett D'Amore 	 * Take host offline with the framework.
3924bb7efa7SGarrett D'Amore 	 */
3934bb7efa7SGarrett D'Amore 	sda_host_detach(shp->sh_host);
3944bb7efa7SGarrett D'Amore 
3954bb7efa7SGarrett D'Amore 	/*
3964bb7efa7SGarrett D'Amore 	 * Tear down interrupts.
3974bb7efa7SGarrett D'Amore 	 */
3984bb7efa7SGarrett D'Amore 	if (shp->sh_ihandle != NULL) {
399f2b90c3cSGarrett D'Amore 		if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
400f2b90c3cSGarrett D'Amore 			(void) ddi_intr_block_disable(&shp->sh_ihandle, 1);
401f2b90c3cSGarrett D'Amore 		} else {
402f2b90c3cSGarrett D'Amore 			(void) ddi_intr_disable(shp->sh_ihandle);
403f2b90c3cSGarrett D'Amore 		}
4044bb7efa7SGarrett D'Amore 		(void) ddi_intr_remove_handler(shp->sh_ihandle);
4054bb7efa7SGarrett D'Amore 		(void) ddi_intr_free(shp->sh_ihandle);
4064bb7efa7SGarrett D'Amore 	}
4074bb7efa7SGarrett D'Amore 
4084bb7efa7SGarrett D'Amore 	/*
4094bb7efa7SGarrett D'Amore 	 * Tear down register mappings, etc.
4104bb7efa7SGarrett D'Amore 	 */
4114bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++)
4124bb7efa7SGarrett D'Amore 		sdhost_uninit_slot(shp, i);
4134bb7efa7SGarrett D'Amore 	kmem_free(shp, sizeof (*shp));
4144bb7efa7SGarrett D'Amore 
4154bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
4164bb7efa7SGarrett D'Amore }
4174bb7efa7SGarrett D'Amore 
418f2b90c3cSGarrett D'Amore int
419f2b90c3cSGarrett D'Amore sdhost_quiesce(dev_info_t *dip)
420f2b90c3cSGarrett D'Amore {
421f2b90c3cSGarrett D'Amore 	sdhost_t	*shp;
422f2b90c3cSGarrett D'Amore 	sdslot_t	*ss;
423f2b90c3cSGarrett D'Amore 
424f2b90c3cSGarrett D'Amore 	shp = ddi_get_driver_private(dip);
425f2b90c3cSGarrett D'Amore 
426f2b90c3cSGarrett D'Amore 	/* reset each slot separately */
427f2b90c3cSGarrett D'Amore 	for (int i = 0; i < shp->sh_numslots; i++) {
428f2b90c3cSGarrett D'Amore 		ss = &shp->sh_slots[i];
429f2b90c3cSGarrett D'Amore 		if (ss->ss_acch == NULL)
430f2b90c3cSGarrett D'Amore 			continue;
431f2b90c3cSGarrett D'Amore 
432f2b90c3cSGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
433f2b90c3cSGarrett D'Amore 	}
434f2b90c3cSGarrett D'Amore 	return (DDI_SUCCESS);
435f2b90c3cSGarrett D'Amore }
436f2b90c3cSGarrett D'Amore 
4374bb7efa7SGarrett D'Amore int
4384bb7efa7SGarrett D'Amore sdhost_suspend(dev_info_t *dip)
4394bb7efa7SGarrett D'Amore {
4404bb7efa7SGarrett D'Amore 	sdhost_t	*shp;
4414bb7efa7SGarrett D'Amore 	sdslot_t	*ss;
4424bb7efa7SGarrett D'Amore 	int		i;
4434bb7efa7SGarrett D'Amore 
4444bb7efa7SGarrett D'Amore 	shp = ddi_get_driver_private(dip);
4454bb7efa7SGarrett D'Amore 
446f2b90c3cSGarrett D'Amore 	sda_host_suspend(shp->sh_host);
4474bb7efa7SGarrett D'Amore 
4484bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++) {
4494bb7efa7SGarrett D'Amore 		ss = &shp->sh_slots[i];
4504bb7efa7SGarrett D'Amore 		mutex_enter(&ss->ss_lock);
4514bb7efa7SGarrett D'Amore 		ss->ss_suspended = B_TRUE;
4524bb7efa7SGarrett D'Amore 		sdhost_disable_interrupts(ss);
4534bb7efa7SGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
4544bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
4554bb7efa7SGarrett D'Amore 	}
4564bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
4574bb7efa7SGarrett D'Amore }
4584bb7efa7SGarrett D'Amore 
4594bb7efa7SGarrett D'Amore int
4604bb7efa7SGarrett D'Amore sdhost_resume(dev_info_t *dip)
4614bb7efa7SGarrett D'Amore {
4624bb7efa7SGarrett D'Amore 	sdhost_t	*shp;
4634bb7efa7SGarrett D'Amore 	sdslot_t	*ss;
4644bb7efa7SGarrett D'Amore 	int		i;
4654bb7efa7SGarrett D'Amore 
4664bb7efa7SGarrett D'Amore 	shp = ddi_get_driver_private(dip);
4674bb7efa7SGarrett D'Amore 
4684bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++) {
4694bb7efa7SGarrett D'Amore 		ss = &shp->sh_slots[i];
4704bb7efa7SGarrett D'Amore 		mutex_enter(&ss->ss_lock);
4714bb7efa7SGarrett D'Amore 		ss->ss_suspended = B_FALSE;
4724bb7efa7SGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
4734bb7efa7SGarrett D'Amore 		sdhost_enable_interrupts(ss);
4744bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
4754bb7efa7SGarrett D'Amore 	}
4764bb7efa7SGarrett D'Amore 
477f2b90c3cSGarrett D'Amore 	sda_host_resume(shp->sh_host);
4784bb7efa7SGarrett D'Amore 
4794bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
4804bb7efa7SGarrett D'Amore }
4814bb7efa7SGarrett D'Amore 
4824bb7efa7SGarrett D'Amore sda_err_t
4834bb7efa7SGarrett D'Amore sdhost_set_clock(sdslot_t *ss, uint32_t hz)
4844bb7efa7SGarrett D'Amore {
4854bb7efa7SGarrett D'Amore 	uint16_t	div;
4864bb7efa7SGarrett D'Amore 	uint32_t	val;
4874bb7efa7SGarrett D'Amore 	uint32_t	clk;
4884bb7efa7SGarrett D'Amore 	int		count;
4894bb7efa7SGarrett D'Amore 
4904bb7efa7SGarrett D'Amore 	/*
4914bb7efa7SGarrett D'Amore 	 * Shut off the clock to begin.
4924bb7efa7SGarrett D'Amore 	 */
4934bb7efa7SGarrett D'Amore 	ss->ss_cardclk = 0;
4944bb7efa7SGarrett D'Amore 	PUT16(ss, REG_CLOCK_CONTROL, 0);
4954bb7efa7SGarrett D'Amore 	if (hz == 0) {
4964bb7efa7SGarrett D'Amore 		return (SDA_EOK);
4974bb7efa7SGarrett D'Amore 	}
4984bb7efa7SGarrett D'Amore 
4994bb7efa7SGarrett D'Amore 	if (ss->ss_baseclk == 0) {
5004bb7efa7SGarrett D'Amore 		sda_host_log(ss->ss_host, ss->ss_num,
5014bb7efa7SGarrett D'Amore 		    "Base clock frequency not established.");
5024bb7efa7SGarrett D'Amore 		return (SDA_EINVAL);
5034bb7efa7SGarrett D'Amore 	}
5044bb7efa7SGarrett D'Amore 
5054bb7efa7SGarrett D'Amore 	if ((hz > 25000000) && ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0)) {
5064bb7efa7SGarrett D'Amore 		/* this clock requires high speed timings! */
5074bb7efa7SGarrett D'Amore 		SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
5084bb7efa7SGarrett D'Amore 	} else {
5094bb7efa7SGarrett D'Amore 		/* don't allow clock to run faster than 25MHz */
5104bb7efa7SGarrett D'Amore 		hz = min(hz, 25000000);
5114bb7efa7SGarrett D'Amore 		CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
5124bb7efa7SGarrett D'Amore 	}
5134bb7efa7SGarrett D'Amore 
5144bb7efa7SGarrett D'Amore 	/* figure out the divider */
5154bb7efa7SGarrett D'Amore 	clk = ss->ss_baseclk;
5164bb7efa7SGarrett D'Amore 	div  = 1;
5174bb7efa7SGarrett D'Amore 	while (clk > hz) {
5184bb7efa7SGarrett D'Amore 		if (div > 0x80)
5194bb7efa7SGarrett D'Amore 			break;
5204bb7efa7SGarrett D'Amore 		clk >>= 1;	/* divide clock by two */
5214bb7efa7SGarrett D'Amore 		div <<= 1;	/* divider goes up by one */
5224bb7efa7SGarrett D'Amore 	}
5234bb7efa7SGarrett D'Amore 	div >>= 1;	/* 0 == divide by 1, 1 = divide by 2 */
5244bb7efa7SGarrett D'Amore 
5254bb7efa7SGarrett D'Amore 	/*
5264bb7efa7SGarrett D'Amore 	 * Set the internal clock divider first, without enabling the
5274bb7efa7SGarrett D'Amore 	 * card clock yet.
5284bb7efa7SGarrett D'Amore 	 */
5294bb7efa7SGarrett D'Amore 	PUT16(ss, REG_CLOCK_CONTROL,
5304bb7efa7SGarrett D'Amore 	    (div << CLOCK_CONTROL_FREQ_SHIFT) | CLOCK_CONTROL_INT_CLOCK_EN);
5314bb7efa7SGarrett D'Amore 
5324bb7efa7SGarrett D'Amore 	/*
5334bb7efa7SGarrett D'Amore 	 * Wait up to 100 msec for the internal clock to stabilize.
5344bb7efa7SGarrett D'Amore 	 * (The spec does not seem to indicate a maximum timeout, but
5354bb7efa7SGarrett D'Amore 	 * it also suggests that an infinite loop be used, which is
5364bb7efa7SGarrett D'Amore 	 * not appropriate for hardened Solaris drivers.)
5374bb7efa7SGarrett D'Amore 	 */
5384bb7efa7SGarrett D'Amore 	for (count = 100000; count; count -= 10) {
5394bb7efa7SGarrett D'Amore 
5404bb7efa7SGarrett D'Amore 		val = GET16(ss, REG_CLOCK_CONTROL);
5414bb7efa7SGarrett D'Amore 
5424bb7efa7SGarrett D'Amore 		if (val & CLOCK_CONTROL_INT_CLOCK_STABLE) {
5434bb7efa7SGarrett D'Amore 			/* if clock is stable, enable the SD clock pin */
5444bb7efa7SGarrett D'Amore 			PUT16(ss, REG_CLOCK_CONTROL, val |
5454bb7efa7SGarrett D'Amore 			    CLOCK_CONTROL_SD_CLOCK_EN);
5464bb7efa7SGarrett D'Amore 
5474bb7efa7SGarrett D'Amore 			ss->ss_cardclk = clk;
5484bb7efa7SGarrett D'Amore 			return (SDA_EOK);
5494bb7efa7SGarrett D'Amore 		}
5504bb7efa7SGarrett D'Amore 
5514bb7efa7SGarrett D'Amore 		drv_usecwait(10);
5524bb7efa7SGarrett D'Amore 	}
5534bb7efa7SGarrett D'Amore 
5544bb7efa7SGarrett D'Amore 	return (SDA_ETIME);
5554bb7efa7SGarrett D'Amore }
5564bb7efa7SGarrett D'Amore 
5574bb7efa7SGarrett D'Amore sda_err_t
5584bb7efa7SGarrett D'Amore sdhost_soft_reset(sdslot_t *ss, uint8_t bits)
5594bb7efa7SGarrett D'Amore {
5604bb7efa7SGarrett D'Amore 	int	count;
5614bb7efa7SGarrett D'Amore 
5624bb7efa7SGarrett D'Amore 	/*
5634bb7efa7SGarrett D'Amore 	 * There appears to be a bug where Ricoh hosts might have a
5644bb7efa7SGarrett D'Amore 	 * problem if the host frequency is not set.  If the card
5654bb7efa7SGarrett D'Amore 	 * isn't present, or we are doing a master reset, just enable
5664bb7efa7SGarrett D'Amore 	 * the internal clock at its native speed.  (No dividers, and
5674bb7efa7SGarrett D'Amore 	 * not exposed to card.).
5684bb7efa7SGarrett D'Amore 	 */
5694bb7efa7SGarrett D'Amore 	if ((bits == SOFT_RESET_ALL) || !(CHECK_STATE(ss, CARD_INSERTED))) {
5704bb7efa7SGarrett D'Amore 		PUT16(ss, REG_CLOCK_CONTROL, CLOCK_CONTROL_INT_CLOCK_EN);
5714bb7efa7SGarrett D'Amore 		/* simple 1msec wait, don't wait for clock to stabilize */
5724bb7efa7SGarrett D'Amore 		drv_usecwait(1000);
5734bb7efa7SGarrett D'Amore 	}
5744bb7efa7SGarrett D'Amore 
5754bb7efa7SGarrett D'Amore 	PUT8(ss, REG_SOFT_RESET, bits);
5764bb7efa7SGarrett D'Amore 	for (count = 100000; count != 0; count -= 10) {
5774bb7efa7SGarrett D'Amore 		if ((GET8(ss, REG_SOFT_RESET) & bits) == 0) {
5784bb7efa7SGarrett D'Amore 			return (SDA_EOK);
5794bb7efa7SGarrett D'Amore 		}
5804bb7efa7SGarrett D'Amore 		drv_usecwait(10);
5814bb7efa7SGarrett D'Amore 	}
5824bb7efa7SGarrett D'Amore 
5834bb7efa7SGarrett D'Amore 	return (SDA_ETIME);
5844bb7efa7SGarrett D'Amore }
5854bb7efa7SGarrett D'Amore 
5864bb7efa7SGarrett D'Amore void
5874bb7efa7SGarrett D'Amore sdhost_disable_interrupts(sdslot_t *ss)
5884bb7efa7SGarrett D'Amore {
5894bb7efa7SGarrett D'Amore 	/* disable slot interrupts for card insert and remove */
5904bb7efa7SGarrett D'Amore 	PUT16(ss, REG_INT_MASK, 0);
5914bb7efa7SGarrett D'Amore 	PUT16(ss, REG_INT_EN, 0);
5924bb7efa7SGarrett D'Amore 
5934bb7efa7SGarrett D'Amore 	/* disable error interrupts */
5944bb7efa7SGarrett D'Amore 	PUT16(ss, REG_ERR_MASK, 0);
5954bb7efa7SGarrett D'Amore 	PUT16(ss, REG_ERR_EN, 0);
5964bb7efa7SGarrett D'Amore }
5974bb7efa7SGarrett D'Amore 
5984bb7efa7SGarrett D'Amore void
5994bb7efa7SGarrett D'Amore sdhost_enable_interrupts(sdslot_t *ss)
6004bb7efa7SGarrett D'Amore {
6014bb7efa7SGarrett D'Amore 	/*
6024bb7efa7SGarrett D'Amore 	 * Note that we want to enable reading of the CMD related
6034bb7efa7SGarrett D'Amore 	 * bits, but we do not want them to generate an interrupt.
6044bb7efa7SGarrett D'Amore 	 * (The busy wait for typical CMD stuff will normally be less
6054bb7efa7SGarrett D'Amore 	 * than 10usec, so its simpler/easier to just poll.  Even in
6064bb7efa7SGarrett D'Amore 	 * the worst case of 100 kHz, the poll is at worst 2 msec.)
6074bb7efa7SGarrett D'Amore 	 */
6084bb7efa7SGarrett D'Amore 
6094bb7efa7SGarrett D'Amore 	/* enable slot interrupts for card insert and remove */
6104bb7efa7SGarrett D'Amore 	PUT16(ss, REG_INT_MASK, INT_MASK);
6114bb7efa7SGarrett D'Amore 	PUT16(ss, REG_INT_EN, INT_ENAB);
6124bb7efa7SGarrett D'Amore 
6134bb7efa7SGarrett D'Amore 	/* enable error interrupts */
6144bb7efa7SGarrett D'Amore 	PUT16(ss, REG_ERR_MASK, ERR_MASK);
6154bb7efa7SGarrett D'Amore 	PUT16(ss, REG_ERR_EN, ERR_ENAB);
6164bb7efa7SGarrett D'Amore }
6174bb7efa7SGarrett D'Amore 
6184bb7efa7SGarrett D'Amore int
6194bb7efa7SGarrett D'Amore sdhost_setup_intr(dev_info_t *dip, sdhost_t *shp)
6204bb7efa7SGarrett D'Amore {
6214bb7efa7SGarrett D'Amore 	int		itypes;
6224bb7efa7SGarrett D'Amore 	int		itype;
6234bb7efa7SGarrett D'Amore 
6244bb7efa7SGarrett D'Amore 	/*
6254bb7efa7SGarrett D'Amore 	 * Set up interrupt handler.
6264bb7efa7SGarrett D'Amore 	 */
6274bb7efa7SGarrett D'Amore 	if (ddi_intr_get_supported_types(dip, &itypes) != DDI_SUCCESS) {
6284bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "ddi_intr_get_supported_types failed");
6294bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
6304bb7efa7SGarrett D'Amore 	}
6314bb7efa7SGarrett D'Amore 
632*28e61230SGarrett D'Amore 	/*
633*28e61230SGarrett D'Amore 	 * It turns out that some controllers don't properly implement MSI,
634*28e61230SGarrett D'Amore 	 * but advertise MSI capability in their  PCI config space.
635*28e61230SGarrett D'Amore 	 *
636*28e61230SGarrett D'Amore 	 * While this is really a chip-specific bug, the simplest solution
637*28e61230SGarrett D'Amore 	 * is to just suppress MSI for now by default -- every device seen
638*28e61230SGarrett D'Amore 	 * so far can use FIXED interrupts.
639*28e61230SGarrett D'Amore 	 *
640*28e61230SGarrett D'Amore 	 * We offer an override property, though, just in case someone really
641*28e61230SGarrett D'Amore 	 * wants to force it.
642*28e61230SGarrett D'Amore 	 *
643*28e61230SGarrett D'Amore 	 * We don't do this if the FIXED type isn't supported though!
644*28e61230SGarrett D'Amore 	 */
645*28e61230SGarrett D'Amore 	if ((ddi_prop_get_int(DDI_DEV_T_ANY, dip,
646*28e61230SGarrett D'Amore 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "enable-msi", 0) == 0) &&
647*28e61230SGarrett D'Amore 	    (itypes & DDI_INTR_TYPE_FIXED)) {
648*28e61230SGarrett D'Amore 		itypes &= ~DDI_INTR_TYPE_MSI;
649*28e61230SGarrett D'Amore 	}
650*28e61230SGarrett D'Amore 	if ((ddi_prop_get_int(DDI_DEV_T_ANY, dip,
651*28e61230SGarrett D'Amore 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "enable-msix", 0) == 0) &&
652*28e61230SGarrett D'Amore 	    (itypes & DDI_INTR_TYPE_FIXED)) {
653*28e61230SGarrett D'Amore 		itypes &= ~DDI_INTR_TYPE_MSIX;
654*28e61230SGarrett D'Amore 	}
655*28e61230SGarrett D'Amore 
6564bb7efa7SGarrett D'Amore 	/*
6574bb7efa7SGarrett D'Amore 	 * Interrupt types are bits in a mask.  We know about these ones:
6584bb7efa7SGarrett D'Amore 	 * FIXED = 1
6594bb7efa7SGarrett D'Amore 	 * MSI = 2
6604bb7efa7SGarrett D'Amore 	 * MSIX = 4
6614bb7efa7SGarrett D'Amore 	 */
6624bb7efa7SGarrett D'Amore 	for (itype = DDI_INTR_TYPE_MSIX; itype != 0; itype >>= 1) {
6634bb7efa7SGarrett D'Amore 
6644bb7efa7SGarrett D'Amore 		int			count;
6654bb7efa7SGarrett D'Amore 
6664bb7efa7SGarrett D'Amore 		if ((itypes & itype) == 0) {
6674bb7efa7SGarrett D'Amore 			/* this type is not supported on this device! */
6684bb7efa7SGarrett D'Amore 			continue;
6694bb7efa7SGarrett D'Amore 		}
6704bb7efa7SGarrett D'Amore 
6714bb7efa7SGarrett D'Amore 		if ((ddi_intr_get_nintrs(dip, itype, &count) != DDI_SUCCESS) ||
6724bb7efa7SGarrett D'Amore 		    (count == 0)) {
6734bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "ddi_intr_get_nintrs failed");
6744bb7efa7SGarrett D'Amore 			continue;
6754bb7efa7SGarrett D'Amore 		}
6764bb7efa7SGarrett D'Amore 
6774bb7efa7SGarrett D'Amore 		/*
6784bb7efa7SGarrett D'Amore 		 * We have not seen a host device with multiple
6794bb7efa7SGarrett D'Amore 		 * interrupts (one per slot?), and the spec does not
6804bb7efa7SGarrett D'Amore 		 * indicate that they exist.  But if one ever occurs,
6814bb7efa7SGarrett D'Amore 		 * we spew a warning to help future debugging/support
6824bb7efa7SGarrett D'Amore 		 * efforts.
6834bb7efa7SGarrett D'Amore 		 */
6844bb7efa7SGarrett D'Amore 		if (count > 1) {
6854bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "Controller offers %d interrupts, "
6864bb7efa7SGarrett D'Amore 			    "but driver only supports one", count);
6874bb7efa7SGarrett D'Amore 			continue;
6884bb7efa7SGarrett D'Amore 		}
6894bb7efa7SGarrett D'Amore 
6904bb7efa7SGarrett D'Amore 		if ((ddi_intr_alloc(dip, &shp->sh_ihandle, itype, 0, 1,
6914bb7efa7SGarrett D'Amore 		    &count, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
6924bb7efa7SGarrett D'Amore 		    (count != 1)) {
6934bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "ddi_intr_alloc failed");
6944bb7efa7SGarrett D'Amore 			continue;
6954bb7efa7SGarrett D'Amore 		}
6964bb7efa7SGarrett D'Amore 
6974bb7efa7SGarrett D'Amore 		if (ddi_intr_get_pri(shp->sh_ihandle, &shp->sh_ipri) !=
6984bb7efa7SGarrett D'Amore 		    DDI_SUCCESS) {
6994bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "ddi_intr_get_pri failed");
7004bb7efa7SGarrett D'Amore 			(void) ddi_intr_free(shp->sh_ihandle);
7014bb7efa7SGarrett D'Amore 			shp->sh_ihandle = NULL;
7024bb7efa7SGarrett D'Amore 			continue;
7034bb7efa7SGarrett D'Amore 		}
7044bb7efa7SGarrett D'Amore 
7054bb7efa7SGarrett D'Amore 		if (shp->sh_ipri >= ddi_intr_get_hilevel_pri()) {
7064bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "Hi level interrupt not supported");
7074bb7efa7SGarrett D'Amore 			(void) ddi_intr_free(shp->sh_ihandle);
7084bb7efa7SGarrett D'Amore 			shp->sh_ihandle = NULL;
7094bb7efa7SGarrett D'Amore 			continue;
7104bb7efa7SGarrett D'Amore 		}
7114bb7efa7SGarrett D'Amore 
7124bb7efa7SGarrett D'Amore 		if (ddi_intr_get_cap(shp->sh_ihandle, &shp->sh_icap) !=
7134bb7efa7SGarrett D'Amore 		    DDI_SUCCESS) {
7144bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "ddi_intr_get_cap failed");
7154bb7efa7SGarrett D'Amore 			(void) ddi_intr_free(shp->sh_ihandle);
7164bb7efa7SGarrett D'Amore 			shp->sh_ihandle = NULL;
7174bb7efa7SGarrett D'Amore 			continue;
7184bb7efa7SGarrett D'Amore 		}
7194bb7efa7SGarrett D'Amore 
7204bb7efa7SGarrett D'Amore 		if (ddi_intr_add_handler(shp->sh_ihandle, sdhost_intr,
7214bb7efa7SGarrett D'Amore 		    shp, NULL) != DDI_SUCCESS) {
7224bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "ddi_intr_add_handler failed");
7234bb7efa7SGarrett D'Amore 			(void) ddi_intr_free(shp->sh_ihandle);
7244bb7efa7SGarrett D'Amore 			shp->sh_ihandle = NULL;
7254bb7efa7SGarrett D'Amore 			continue;
7264bb7efa7SGarrett D'Amore 		}
7274bb7efa7SGarrett D'Amore 
7284bb7efa7SGarrett D'Amore 		return (DDI_SUCCESS);
7294bb7efa7SGarrett D'Amore 	}
7304bb7efa7SGarrett D'Amore 
7314bb7efa7SGarrett D'Amore 	return (DDI_FAILURE);
7324bb7efa7SGarrett D'Amore }
7334bb7efa7SGarrett D'Amore 
7344bb7efa7SGarrett D'Amore void
7354bb7efa7SGarrett D'Amore sdhost_xfer_done(sdslot_t *ss, sda_err_t errno)
7364bb7efa7SGarrett D'Amore {
7374bb7efa7SGarrett D'Amore 	if ((errno == SDA_EOK) && (ss->ss_resid != 0)) {
7384bb7efa7SGarrett D'Amore 		/* an unexpected partial transfer was found */
7394bb7efa7SGarrett D'Amore 		errno = SDA_ERESID;
7404bb7efa7SGarrett D'Amore 	}
7414bb7efa7SGarrett D'Amore 	ss->ss_blksz = 0;
7424bb7efa7SGarrett D'Amore 	ss->ss_resid = 0;
7434bb7efa7SGarrett D'Amore 
7444bb7efa7SGarrett D'Amore 	if (errno != SDA_EOK) {
7454bb7efa7SGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
7464bb7efa7SGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
7474bb7efa7SGarrett D'Amore 
7484bb7efa7SGarrett D'Amore 		/* send a STOP command if necessary */
7494bb7efa7SGarrett D'Amore 		if (ss->ss_mode & XFR_MODE_AUTO_CMD12) {
7504bb7efa7SGarrett D'Amore 			PUT32(ss, REG_ARGUMENT, 0);
7514bb7efa7SGarrett D'Amore 			PUT16(ss, REG_COMMAND,
7524bb7efa7SGarrett D'Amore 			    (CMD_STOP_TRANSMIT << 8) |
7534bb7efa7SGarrett D'Amore 			    COMMAND_TYPE_NORM | COMMAND_INDEX_CHECK_EN |
7544bb7efa7SGarrett D'Amore 			    COMMAND_CRC_CHECK_EN | COMMAND_RESP_48_BUSY);
7554bb7efa7SGarrett D'Amore 		}
7564bb7efa7SGarrett D'Amore 	}
7574bb7efa7SGarrett D'Amore 
7584bb7efa7SGarrett D'Amore 	sda_host_transfer(ss->ss_host, ss->ss_num, errno);
7594bb7efa7SGarrett D'Amore }
7604bb7efa7SGarrett D'Amore 
7614bb7efa7SGarrett D'Amore uint_t
7624bb7efa7SGarrett D'Amore sdhost_slot_intr(sdslot_t *ss)
7634bb7efa7SGarrett D'Amore {
7644bb7efa7SGarrett D'Amore 	uint16_t	intr;
7654bb7efa7SGarrett D'Amore 	uint16_t	errs;
7664bb7efa7SGarrett D'Amore 	uint8_t		*data;
7674bb7efa7SGarrett D'Amore 	int		count;
7684bb7efa7SGarrett D'Amore 
7694bb7efa7SGarrett D'Amore 	mutex_enter(&ss->ss_lock);
7704bb7efa7SGarrett D'Amore 
7714bb7efa7SGarrett D'Amore 	if (ss->ss_suspended) {
7724bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
7734bb7efa7SGarrett D'Amore 		return (DDI_INTR_UNCLAIMED);
7744bb7efa7SGarrett D'Amore 	}
7754bb7efa7SGarrett D'Amore 
7764bb7efa7SGarrett D'Amore 	intr = GET16(ss, REG_INT_STAT);
7774bb7efa7SGarrett D'Amore 	if (intr == 0) {
7784bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
7794bb7efa7SGarrett D'Amore 		return (DDI_INTR_UNCLAIMED);
7804bb7efa7SGarrett D'Amore 	}
7814bb7efa7SGarrett D'Amore 	errs = GET16(ss, REG_ERR_STAT);
7824bb7efa7SGarrett D'Amore 
7834bb7efa7SGarrett D'Amore 	if (intr & (INT_REM | INT_INS)) {
7844bb7efa7SGarrett D'Amore 
7854bb7efa7SGarrett D'Amore 		PUT16(ss, REG_INT_STAT, intr);
7864bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
7874bb7efa7SGarrett D'Amore 
7884bb7efa7SGarrett D'Amore 		sda_host_detect(ss->ss_host, ss->ss_num);
7894bb7efa7SGarrett D'Amore 		/* no further interrupt processing this cycle */
7904bb7efa7SGarrett D'Amore 		return (DDI_INTR_CLAIMED);
7914bb7efa7SGarrett D'Amore 	}
7924bb7efa7SGarrett D'Amore 
7934bb7efa7SGarrett D'Amore 	if (intr & INT_DMA) {
7944bb7efa7SGarrett D'Amore 		/*
7954bb7efa7SGarrett D'Amore 		 * We have crossed a DMA/page boundary.  Cope with it.
7964bb7efa7SGarrett D'Amore 		 */
7974bb7efa7SGarrett D'Amore 		if (ss->ss_ndmac) {
7984bb7efa7SGarrett D'Amore 			ss->ss_ndmac--;
7994bb7efa7SGarrett D'Amore 			ss->ss_dmacs++;
8004bb7efa7SGarrett D'Amore 			PUT16(ss, REG_INT_STAT, INT_DMA);
8014bb7efa7SGarrett D'Amore 			PUT32(ss, REG_SDMA_ADDR, ss->ss_dmacs->dmac_address);
8024bb7efa7SGarrett D'Amore 
8034bb7efa7SGarrett D'Amore 		} else {
8044bb7efa7SGarrett D'Amore 			/*
8054bb7efa7SGarrett D'Amore 			 * Apparently some sdhost controllers issue a
8064bb7efa7SGarrett D'Amore 			 * final DMA interrupt if the DMA completes on
8074bb7efa7SGarrett D'Amore 			 * a boundary, even though there is no further
8084bb7efa7SGarrett D'Amore 			 * data to transfer.
8094bb7efa7SGarrett D'Amore 			 *
8104bb7efa7SGarrett D'Amore 			 * There might be a risk here of the
8114bb7efa7SGarrett D'Amore 			 * controller continuing to access the same
8124bb7efa7SGarrett D'Amore 			 * data over and over again, but we accept the
8134bb7efa7SGarrett D'Amore 			 * risk.
8144bb7efa7SGarrett D'Amore 			 */
8154bb7efa7SGarrett D'Amore 			PUT16(ss, REG_INT_STAT, INT_DMA);
8164bb7efa7SGarrett D'Amore 		}
8174bb7efa7SGarrett D'Amore 	}
8184bb7efa7SGarrett D'Amore 
8194bb7efa7SGarrett D'Amore 	if (intr & INT_RD) {
8204bb7efa7SGarrett D'Amore 		/*
8214bb7efa7SGarrett D'Amore 		 * PIO read!  PIO is quite suboptimal, but we expect
8224bb7efa7SGarrett D'Amore 		 * performance critical applications to use DMA
8234bb7efa7SGarrett D'Amore 		 * whenever possible.  We have to stage this through
8244bb7efa7SGarrett D'Amore 		 * the bounce buffer to meet alignment considerations.
8254bb7efa7SGarrett D'Amore 		 */
8264bb7efa7SGarrett D'Amore 
8274bb7efa7SGarrett D'Amore 		PUT16(ss, REG_INT_STAT, INT_RD);
8284bb7efa7SGarrett D'Amore 
8294bb7efa7SGarrett D'Amore 		while ((ss->ss_resid > 0) && CHECK_STATE(ss, BUF_RD_EN)) {
8304bb7efa7SGarrett D'Amore 
8314bb7efa7SGarrett D'Amore 			data = (void *)ss->ss_bounce;
8324bb7efa7SGarrett D'Amore 			count = ss->ss_blksz;
8334bb7efa7SGarrett D'Amore 
8344bb7efa7SGarrett D'Amore 			ASSERT(count > 0);
8354bb7efa7SGarrett D'Amore 			ASSERT(ss->ss_kvaddr != NULL);
8364bb7efa7SGarrett D'Amore 
8374bb7efa7SGarrett D'Amore 			while (count >= sizeof (uint32_t)) {
8384bb7efa7SGarrett D'Amore 				*(uint32_t *)(void *)data = GETDATA32(ss);
8394bb7efa7SGarrett D'Amore 				data += sizeof (uint32_t);
8404bb7efa7SGarrett D'Amore 				count -= sizeof (uint32_t);
8414bb7efa7SGarrett D'Amore 			}
8424bb7efa7SGarrett D'Amore 			while (count >= sizeof (uint16_t)) {
8434bb7efa7SGarrett D'Amore 				*(uint16_t *)(void *)data = GETDATA16(ss);
8444bb7efa7SGarrett D'Amore 				data += sizeof (uint16_t);
8454bb7efa7SGarrett D'Amore 				count -= sizeof (uint16_t);
8464bb7efa7SGarrett D'Amore 			}
8474bb7efa7SGarrett D'Amore 			while (count >= sizeof (uint8_t)) {
8484bb7efa7SGarrett D'Amore 				*(uint8_t *)data = GETDATA8(ss);
8494bb7efa7SGarrett D'Amore 				data += sizeof (uint8_t);
8504bb7efa7SGarrett D'Amore 				count -= sizeof (uint8_t);
8514bb7efa7SGarrett D'Amore 			}
8524bb7efa7SGarrett D'Amore 
8534bb7efa7SGarrett D'Amore 			bcopy(ss->ss_bounce, ss->ss_kvaddr, ss->ss_blksz);
8544bb7efa7SGarrett D'Amore 			ss->ss_kvaddr += ss->ss_blksz;
8554bb7efa7SGarrett D'Amore 			ss->ss_resid--;
8564bb7efa7SGarrett D'Amore 		}
8574bb7efa7SGarrett D'Amore 	}
8584bb7efa7SGarrett D'Amore 
8594bb7efa7SGarrett D'Amore 	if (intr & INT_WR) {
8604bb7efa7SGarrett D'Amore 		/*
8614bb7efa7SGarrett D'Amore 		 * PIO write!  PIO is quite suboptimal, but we expect
8624bb7efa7SGarrett D'Amore 		 * performance critical applications to use DMA
8634bb7efa7SGarrett D'Amore 		 * whenever possible.  We have to stage this trhough
8644bb7efa7SGarrett D'Amore 		 * the bounce buffer to meet alignment considerations.
8654bb7efa7SGarrett D'Amore 		 */
8664bb7efa7SGarrett D'Amore 
8674bb7efa7SGarrett D'Amore 		PUT16(ss, REG_INT_STAT, INT_WR);
8684bb7efa7SGarrett D'Amore 
8694bb7efa7SGarrett D'Amore 		while ((ss->ss_resid > 0) && CHECK_STATE(ss, BUF_WR_EN)) {
8704bb7efa7SGarrett D'Amore 
8714bb7efa7SGarrett D'Amore 			data = (void *)ss->ss_bounce;
8724bb7efa7SGarrett D'Amore 			count = ss->ss_blksz;
8734bb7efa7SGarrett D'Amore 
8744bb7efa7SGarrett D'Amore 			ASSERT(count > 0);
8754bb7efa7SGarrett D'Amore 			ASSERT(ss->ss_kvaddr != NULL);
8764bb7efa7SGarrett D'Amore 
8774bb7efa7SGarrett D'Amore 			bcopy(ss->ss_kvaddr, data, count);
8784bb7efa7SGarrett D'Amore 			while (count >= sizeof (uint32_t)) {
8794bb7efa7SGarrett D'Amore 				PUTDATA32(ss, *(uint32_t *)(void *)data);
8804bb7efa7SGarrett D'Amore 				data += sizeof (uint32_t);
8814bb7efa7SGarrett D'Amore 				count -= sizeof (uint32_t);
8824bb7efa7SGarrett D'Amore 			}
8834bb7efa7SGarrett D'Amore 			while (count >= sizeof (uint16_t)) {
8844bb7efa7SGarrett D'Amore 				PUTDATA16(ss, *(uint16_t *)(void *)data);
8854bb7efa7SGarrett D'Amore 				data += sizeof (uint16_t);
8864bb7efa7SGarrett D'Amore 				count -= sizeof (uint16_t);
8874bb7efa7SGarrett D'Amore 			}
8884bb7efa7SGarrett D'Amore 			while (count >= sizeof (uint8_t)) {
8894bb7efa7SGarrett D'Amore 				PUTDATA8(ss, *(uint8_t *)data);
8904bb7efa7SGarrett D'Amore 				data += sizeof (uint8_t);
8914bb7efa7SGarrett D'Amore 				count -= sizeof (uint8_t);
8924bb7efa7SGarrett D'Amore 			}
8934bb7efa7SGarrett D'Amore 
8944bb7efa7SGarrett D'Amore 			ss->ss_kvaddr += ss->ss_blksz;
8954bb7efa7SGarrett D'Amore 			ss->ss_resid--;
8964bb7efa7SGarrett D'Amore 		}
8974bb7efa7SGarrett D'Amore 	}
8984bb7efa7SGarrett D'Amore 
8994bb7efa7SGarrett D'Amore 	if (intr & INT_XFR) {
9004bb7efa7SGarrett D'Amore 		PUT16(ss, REG_INT_STAT, INT_XFR);
9014bb7efa7SGarrett D'Amore 
9024bb7efa7SGarrett D'Amore 		sdhost_xfer_done(ss, SDA_EOK);
9034bb7efa7SGarrett D'Amore 	}
9044bb7efa7SGarrett D'Amore 
9054bb7efa7SGarrett D'Amore 	if (intr & INT_ERR) {
9064bb7efa7SGarrett D'Amore 		PUT16(ss, REG_ERR_STAT, errs);
9074bb7efa7SGarrett D'Amore 		PUT16(ss, REG_INT_STAT, INT_ERR);
9084bb7efa7SGarrett D'Amore 
9094bb7efa7SGarrett D'Amore 		if (errs & ERR_DAT) {
9104bb7efa7SGarrett D'Amore 			if ((errs & ERR_DAT_END) == ERR_DAT_END) {
9114bb7efa7SGarrett D'Amore 				sdhost_xfer_done(ss, SDA_EPROTO);
9124bb7efa7SGarrett D'Amore 			} else if ((errs & ERR_DAT_CRC) == ERR_DAT_CRC) {
9134bb7efa7SGarrett D'Amore 				sdhost_xfer_done(ss, SDA_ECRC7);
9144bb7efa7SGarrett D'Amore 			} else {
9154bb7efa7SGarrett D'Amore 				sdhost_xfer_done(ss, SDA_ETIME);
9164bb7efa7SGarrett D'Amore 			}
9174bb7efa7SGarrett D'Amore 
9184bb7efa7SGarrett D'Amore 		} else if (errs & ERR_ACMD12) {
9194bb7efa7SGarrett D'Amore 			/*
9204bb7efa7SGarrett D'Amore 			 * Generally, this is bad news.  we need a full
9214bb7efa7SGarrett D'Amore 			 * reset to recover properly.
9224bb7efa7SGarrett D'Amore 			 */
9234bb7efa7SGarrett D'Amore 			sdhost_xfer_done(ss, SDA_ECMD12);
9244bb7efa7SGarrett D'Amore 		}
9254bb7efa7SGarrett D'Amore 
9264bb7efa7SGarrett D'Amore 		/*
9274bb7efa7SGarrett D'Amore 		 * This asynchronous error leaves the slot more or less
9284bb7efa7SGarrett D'Amore 		 * useless.  Report it to the framework.
9294bb7efa7SGarrett D'Amore 		 */
9304bb7efa7SGarrett D'Amore 		if (errs & ERR_CURRENT) {
9314bb7efa7SGarrett D'Amore 			sda_host_fault(ss->ss_host, ss->ss_num,
9324bb7efa7SGarrett D'Amore 			    SDA_FAULT_CURRENT);
9334bb7efa7SGarrett D'Amore 		}
9344bb7efa7SGarrett D'Amore 	}
9354bb7efa7SGarrett D'Amore 
9364bb7efa7SGarrett D'Amore 	mutex_exit(&ss->ss_lock);
9374bb7efa7SGarrett D'Amore 
9384bb7efa7SGarrett D'Amore 	return (DDI_INTR_CLAIMED);
9394bb7efa7SGarrett D'Amore }
9404bb7efa7SGarrett D'Amore 
9414bb7efa7SGarrett D'Amore /*ARGSUSED1*/
9424bb7efa7SGarrett D'Amore uint_t
9434bb7efa7SGarrett D'Amore sdhost_intr(caddr_t arg1, caddr_t arg2)
9444bb7efa7SGarrett D'Amore {
9454bb7efa7SGarrett D'Amore 	sdhost_t	*shp = (void *)arg1;
9464bb7efa7SGarrett D'Amore 	int		rv = DDI_INTR_UNCLAIMED;
9474bb7efa7SGarrett D'Amore 	int		num;
9484bb7efa7SGarrett D'Amore 
9494bb7efa7SGarrett D'Amore 	/* interrupt for each of the slots present in the system */
9504bb7efa7SGarrett D'Amore 	for (num = 0; num < shp->sh_numslots; num++) {
9514bb7efa7SGarrett D'Amore 		if (sdhost_slot_intr(&shp->sh_slots[num]) ==
9524bb7efa7SGarrett D'Amore 		    DDI_INTR_CLAIMED) {
9534bb7efa7SGarrett D'Amore 			rv = DDI_INTR_CLAIMED;
9544bb7efa7SGarrett D'Amore 		}
9554bb7efa7SGarrett D'Amore 	}
9564bb7efa7SGarrett D'Amore 	return (rv);
9574bb7efa7SGarrett D'Amore }
9584bb7efa7SGarrett D'Amore 
9594bb7efa7SGarrett D'Amore int
9604bb7efa7SGarrett D'Amore sdhost_init_slot(dev_info_t *dip, sdhost_t *shp, int num, int bar)
9614bb7efa7SGarrett D'Amore {
9624bb7efa7SGarrett D'Amore 	sdslot_t	*ss;
9634bb7efa7SGarrett D'Amore 	uint32_t	capab;
9644bb7efa7SGarrett D'Amore 	uint32_t	clk;
9654bb7efa7SGarrett D'Amore 
9664bb7efa7SGarrett D'Amore 	/*
9674bb7efa7SGarrett D'Amore 	 * Register the private state.
9684bb7efa7SGarrett D'Amore 	 */
9694bb7efa7SGarrett D'Amore 	ss = &shp->sh_slots[num];
9704bb7efa7SGarrett D'Amore 	ss->ss_host = shp->sh_host;
9714bb7efa7SGarrett D'Amore 	ss->ss_num = num;
9724bb7efa7SGarrett D'Amore 	sda_host_set_private(shp->sh_host, num, ss);
9734bb7efa7SGarrett D'Amore 
9744bb7efa7SGarrett D'Amore 	/*
9754bb7efa7SGarrett D'Amore 	 * Initialize core data structure, locks, etc.
9764bb7efa7SGarrett D'Amore 	 */
9774bb7efa7SGarrett D'Amore 	mutex_init(&ss->ss_lock, NULL, MUTEX_DRIVER,
9784bb7efa7SGarrett D'Amore 	    DDI_INTR_PRI(shp->sh_ipri));
9794bb7efa7SGarrett D'Amore 
9804bb7efa7SGarrett D'Amore 	if (ddi_regs_map_setup(dip, bar, &ss->ss_regva, 0, 0, &sdhost_regattr,
9814bb7efa7SGarrett D'Amore 	    &ss->ss_acch) != DDI_SUCCESS) {
982f2b90c3cSGarrett D'Amore 		cmn_err(CE_WARN, "Failed to map registers!");
9834bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
9844bb7efa7SGarrett D'Amore 	}
9854bb7efa7SGarrett D'Amore 
9864bb7efa7SGarrett D'Amore 	/* reset before reading capabilities */
9874bb7efa7SGarrett D'Amore 	if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK)
9884bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
9894bb7efa7SGarrett D'Amore 
9904bb7efa7SGarrett D'Amore 	capab = GET64(ss, REG_CAPAB) & 0xffffffffU; /* upper bits reserved */
9914bb7efa7SGarrett D'Amore 	ss->ss_capab = capab;
9924bb7efa7SGarrett D'Amore 
9934bb7efa7SGarrett D'Amore 	/* host voltages in OCR format */
9944bb7efa7SGarrett D'Amore 	ss->ss_ocr = 0;
9954bb7efa7SGarrett D'Amore 	if (capab & CAPAB_18V)
9964bb7efa7SGarrett D'Amore 		ss->ss_ocr |= OCR_18_19V;	/* 1.8V */
9974bb7efa7SGarrett D'Amore 	if (capab & CAPAB_30V)
9984bb7efa7SGarrett D'Amore 		ss->ss_ocr |= OCR_30_31V;
9994bb7efa7SGarrett D'Amore 	if (capab & CAPAB_33V)
10004bb7efa7SGarrett D'Amore 		ss->ss_ocr |= OCR_32_33V;
10014bb7efa7SGarrett D'Amore 
10024bb7efa7SGarrett D'Amore 	/* base clock */
10034bb7efa7SGarrett D'Amore 	ss->ss_baseclk =
10044bb7efa7SGarrett D'Amore 	    ((capab & CAPAB_BASE_FREQ_MASK) >> CAPAB_BASE_FREQ_SHIFT);
10054bb7efa7SGarrett D'Amore 	ss->ss_baseclk *= 1000000;
10064bb7efa7SGarrett D'Amore 
10074bb7efa7SGarrett D'Amore 	/*
10084bb7efa7SGarrett D'Amore 	 * Timeout clock.  We can calculate this using the following
10094bb7efa7SGarrett D'Amore 	 * formula:
10104bb7efa7SGarrett D'Amore 	 *
10114bb7efa7SGarrett D'Amore 	 * (1000000 usec/1sec) * (1sec/tmoutclk) * base factor = clock time
10124bb7efa7SGarrett D'Amore 	 *
10134bb7efa7SGarrett D'Amore 	 * Clock time is the length of the base clock in usecs.
10144bb7efa7SGarrett D'Amore 	 *
10154bb7efa7SGarrett D'Amore 	 * Our base factor is 2^13, which is the shortest clock we
10164bb7efa7SGarrett D'Amore 	 * can count.
10174bb7efa7SGarrett D'Amore 	 *
10184bb7efa7SGarrett D'Amore 	 * To simplify the math and avoid overflow, we cancel out the
10194bb7efa7SGarrett D'Amore 	 * zeros for kHz or MHz.  Since we want to wait more clocks, not
10204bb7efa7SGarrett D'Amore 	 * less, on error, we truncate the result rather than rounding
10214bb7efa7SGarrett D'Amore 	 * up.
10224bb7efa7SGarrett D'Amore 	 */
10234bb7efa7SGarrett D'Amore 	clk = ((capab & CAPAB_TIMEOUT_FREQ_MASK) >> CAPAB_TIMEOUT_FREQ_SHIFT);
10244bb7efa7SGarrett D'Amore 	if ((ss->ss_baseclk == 0) || (clk == 0)) {
10254bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Unable to determine clock frequencies");
10264bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
10274bb7efa7SGarrett D'Amore 	}
10284bb7efa7SGarrett D'Amore 
10294bb7efa7SGarrett D'Amore 	if (capab & CAPAB_TIMEOUT_UNITS) {
10304bb7efa7SGarrett D'Amore 		/* MHz */
10314bb7efa7SGarrett D'Amore 		ss->ss_tmusecs = (1 << 13) / clk;
10324bb7efa7SGarrett D'Amore 		clk *= 1000000;
10334bb7efa7SGarrett D'Amore 	} else {
10344bb7efa7SGarrett D'Amore 		/* kHz */
10354bb7efa7SGarrett D'Amore 		ss->ss_tmusecs = (1000 * (1 << 13)) / clk;
10364bb7efa7SGarrett D'Amore 		clk *= 1000;
10374bb7efa7SGarrett D'Amore 	}
10384bb7efa7SGarrett D'Amore 
10394bb7efa7SGarrett D'Amore 	/*
10404bb7efa7SGarrett D'Amore 	 * Calculation of the timeout.
10414bb7efa7SGarrett D'Amore 	 *
10424bb7efa7SGarrett D'Amore 	 * SDIO cards use a 1sec timeout, and SDHC cards use fixed
10434bb7efa7SGarrett D'Amore 	 * 100msec for read and 250 msec for write.
10444bb7efa7SGarrett D'Amore 	 *
10454bb7efa7SGarrett D'Amore 	 * Legacy cards running at 375kHz have a worst case of about
10464bb7efa7SGarrett D'Amore 	 * 15 seconds.  Running at 25MHz (the standard speed) it is
10474bb7efa7SGarrett D'Amore 	 * about 100msec for read, and about 3.2 sec for write.
10484bb7efa7SGarrett D'Amore 	 * Typical values are 1/100th that, or about 1msec for read,
10494bb7efa7SGarrett D'Amore 	 * and 32 msec for write.
10504bb7efa7SGarrett D'Amore 	 *
10514bb7efa7SGarrett D'Amore 	 * No transaction at full speed should ever take more than 4
10524bb7efa7SGarrett D'Amore 	 * seconds.  (Some slow legacy cards might have trouble, but
10534bb7efa7SGarrett D'Amore 	 * we'll worry about them if they ever are seen.  Nobody wants
10544bb7efa7SGarrett D'Amore 	 * to wait 4 seconds to access a single block anyway!)
10554bb7efa7SGarrett D'Amore 	 *
10564bb7efa7SGarrett D'Amore 	 * To get to 4 seconds, we continuously double usec until we
10574bb7efa7SGarrett D'Amore 	 * get to the maximum value, or a timeout greater than 4
10584bb7efa7SGarrett D'Amore 	 * seconds.
10594bb7efa7SGarrett D'Amore 	 *
10604bb7efa7SGarrett D'Amore 	 * Note that for high-speed timeout clocks, we might not be
10614bb7efa7SGarrett D'Amore 	 * able to get to the full 4 seconds.  E.g. with a 48MHz
10624bb7efa7SGarrett D'Amore 	 * timeout clock, we can only get to about 2.8 seconds.  Its
10634bb7efa7SGarrett D'Amore 	 * possible that there could be some slow MMC cards that will
10644bb7efa7SGarrett D'Amore 	 * timeout at this clock rate, but it seems unlikely.  (The
10654bb7efa7SGarrett D'Amore 	 * device would have to be pressing the very worst times,
10664bb7efa7SGarrett D'Amore 	 * against the 100-fold "permissive" window allowed, and
10674bb7efa7SGarrett D'Amore 	 * running at only 12.5MHz.)
10684bb7efa7SGarrett D'Amore 	 *
10694bb7efa7SGarrett D'Amore 	 * XXX: this could easily be a tunable.  Someone dealing with only
10704bb7efa7SGarrett D'Amore 	 * reasonable cards could set this to just 1 second.
10714bb7efa7SGarrett D'Amore 	 */
10724bb7efa7SGarrett D'Amore 	for (ss->ss_tmoutclk = 0; ss->ss_tmoutclk < 14; ss->ss_tmoutclk++) {
10734bb7efa7SGarrett D'Amore 		if ((ss->ss_tmusecs * (1 << ss->ss_tmoutclk)) >= 4000000) {
10744bb7efa7SGarrett D'Amore 			break;
10754bb7efa7SGarrett D'Amore 		}
10764bb7efa7SGarrett D'Amore 	}
10774bb7efa7SGarrett D'Amore 
10784bb7efa7SGarrett D'Amore 	/*
10794bb7efa7SGarrett D'Amore 	 * Enable slot interrupts.
10804bb7efa7SGarrett D'Amore 	 */
10814bb7efa7SGarrett D'Amore 	sdhost_enable_interrupts(ss);
10824bb7efa7SGarrett D'Amore 
10834bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
10844bb7efa7SGarrett D'Amore }
10854bb7efa7SGarrett D'Amore 
10864bb7efa7SGarrett D'Amore void
10874bb7efa7SGarrett D'Amore sdhost_uninit_slot(sdhost_t *shp, int num)
10884bb7efa7SGarrett D'Amore {
10894bb7efa7SGarrett D'Amore 	sdslot_t	*ss;
10904bb7efa7SGarrett D'Amore 
10914bb7efa7SGarrett D'Amore 	ss = &shp->sh_slots[num];
10924bb7efa7SGarrett D'Amore 	if (ss->ss_acch == NULL)
10934bb7efa7SGarrett D'Amore 		return;
10944bb7efa7SGarrett D'Amore 
10954bb7efa7SGarrett D'Amore 	(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
10964bb7efa7SGarrett D'Amore 
10974bb7efa7SGarrett D'Amore 	ddi_regs_map_free(&ss->ss_acch);
10984bb7efa7SGarrett D'Amore 	mutex_destroy(&ss->ss_lock);
10994bb7efa7SGarrett D'Amore }
11004bb7efa7SGarrett D'Amore 
11014bb7efa7SGarrett D'Amore void
11024bb7efa7SGarrett D'Amore sdhost_get_response(sdslot_t *ss, sda_cmd_t *cmdp)
11034bb7efa7SGarrett D'Amore {
11044bb7efa7SGarrett D'Amore 	uint32_t	*resp = cmdp->sc_response;
11054bb7efa7SGarrett D'Amore 	int		i;
11064bb7efa7SGarrett D'Amore 
11074bb7efa7SGarrett D'Amore 	resp[0] = GET32(ss, REG_RESP1);
11084bb7efa7SGarrett D'Amore 	resp[1] = GET32(ss, REG_RESP2);
11094bb7efa7SGarrett D'Amore 	resp[2] = GET32(ss, REG_RESP3);
11104bb7efa7SGarrett D'Amore 	resp[3] = GET32(ss, REG_RESP4);
11114bb7efa7SGarrett D'Amore 
11124bb7efa7SGarrett D'Amore 	/*
11134bb7efa7SGarrett D'Amore 	 * Response 2 is goofy because the host drops the low
11144bb7efa7SGarrett D'Amore 	 * order CRC bits.  This makes it a bit awkward, so we
11154bb7efa7SGarrett D'Amore 	 * have to shift the bits to make it work out right.
11164bb7efa7SGarrett D'Amore 	 *
11174bb7efa7SGarrett D'Amore 	 * Note that the framework expects the 32 bit
11184bb7efa7SGarrett D'Amore 	 * words to be ordered in LE fashion.  (The
11194bb7efa7SGarrett D'Amore 	 * bits within the words are in native order).
11204bb7efa7SGarrett D'Amore 	 */
11214bb7efa7SGarrett D'Amore 	if (cmdp->sc_rtype == R2) {
11224bb7efa7SGarrett D'Amore 		for (i = 3; i > 0; i--) {
11234bb7efa7SGarrett D'Amore 			resp[i] <<= 8;
11244bb7efa7SGarrett D'Amore 			resp[i] |= (resp[i - 1] >> 24);
11254bb7efa7SGarrett D'Amore 		}
11264bb7efa7SGarrett D'Amore 		resp[0] <<= 8;
11274bb7efa7SGarrett D'Amore 	}
11284bb7efa7SGarrett D'Amore }
11294bb7efa7SGarrett D'Amore 
11304bb7efa7SGarrett D'Amore sda_err_t
11314bb7efa7SGarrett D'Amore sdhost_wait_cmd(sdslot_t *ss, sda_cmd_t *cmdp)
11324bb7efa7SGarrett D'Amore {
11334bb7efa7SGarrett D'Amore 	int		i;
11344bb7efa7SGarrett D'Amore 	uint16_t	errs;
1135f2b90c3cSGarrett D'Amore 	sda_err_t	rv;
11364bb7efa7SGarrett D'Amore 
11374bb7efa7SGarrett D'Amore 	/*
11384bb7efa7SGarrett D'Amore 	 * Worst case for 100kHz timeout is 2msec (200 clocks), we add
11394bb7efa7SGarrett D'Amore 	 * a tiny bit for safety.  (Generally timeout will be far, far
11404bb7efa7SGarrett D'Amore 	 * less than that.)
11414bb7efa7SGarrett D'Amore 	 *
11424bb7efa7SGarrett D'Amore 	 * Note that at more typical 12MHz (and normally it will be
11434bb7efa7SGarrett D'Amore 	 * even faster than that!) that the device timeout is only
11444bb7efa7SGarrett D'Amore 	 * 16.67 usec.  We could be smarter and reduce the delay time,
11454bb7efa7SGarrett D'Amore 	 * but that would require putting more intelligence into the
11464bb7efa7SGarrett D'Amore 	 * code, and we don't expect CMD timeout to normally occur
11474bb7efa7SGarrett D'Amore 	 * except during initialization.  (At which time we need the
11484bb7efa7SGarrett D'Amore 	 * full timeout anyway.)
11494bb7efa7SGarrett D'Amore 	 *
11504bb7efa7SGarrett D'Amore 	 * Checking the ERR_STAT will normally cause the timeout to
11514bb7efa7SGarrett D'Amore 	 * terminate to finish early if the device is healthy, anyway.
11524bb7efa7SGarrett D'Amore 	 */
11534bb7efa7SGarrett D'Amore 
11544bb7efa7SGarrett D'Amore 	for (i = 3000; i > 0; i -= 5) {
11554bb7efa7SGarrett D'Amore 		if (GET16(ss, REG_INT_STAT) & INT_CMD) {
11564bb7efa7SGarrett D'Amore 
11574bb7efa7SGarrett D'Amore 			PUT16(ss, REG_INT_STAT, INT_CMD);
11584bb7efa7SGarrett D'Amore 
11594bb7efa7SGarrett D'Amore 			/* command completed */
11604bb7efa7SGarrett D'Amore 			sdhost_get_response(ss, cmdp);
11614bb7efa7SGarrett D'Amore 			return (SDA_EOK);
11624bb7efa7SGarrett D'Amore 		}
11634bb7efa7SGarrett D'Amore 
11644bb7efa7SGarrett D'Amore 		if ((errs = (GET16(ss, REG_ERR_STAT) & ERR_CMD)) != 0) {
11654bb7efa7SGarrett D'Amore 			PUT16(ss, REG_ERR_STAT, errs);
11664bb7efa7SGarrett D'Amore 
11674bb7efa7SGarrett D'Amore 			/* command timeout isn't a host failure */
11684bb7efa7SGarrett D'Amore 			if ((errs & ERR_CMD_TMO) == ERR_CMD_TMO) {
1169f2b90c3cSGarrett D'Amore 				rv = SDA_ETIME;
1170f2b90c3cSGarrett D'Amore 			} else if ((errs & ERR_CMD_CRC) == ERR_CMD_CRC) {
1171f2b90c3cSGarrett D'Amore 				rv = SDA_ECRC7;
11724bb7efa7SGarrett D'Amore 			} else {
1173f2b90c3cSGarrett D'Amore 				rv = SDA_EPROTO;
11744bb7efa7SGarrett D'Amore 			}
1175f2b90c3cSGarrett D'Amore 			goto error;
11764bb7efa7SGarrett D'Amore 		}
11774bb7efa7SGarrett D'Amore 
11784bb7efa7SGarrett D'Amore 		drv_usecwait(5);
11794bb7efa7SGarrett D'Amore 	}
11804bb7efa7SGarrett D'Amore 
1181f2b90c3cSGarrett D'Amore 	rv = SDA_ETIME;
1182f2b90c3cSGarrett D'Amore 
1183f2b90c3cSGarrett D'Amore error:
1184f2b90c3cSGarrett D'Amore 	/*
1185f2b90c3cSGarrett D'Amore 	 * NB: We need to soft reset the CMD and DAT
1186f2b90c3cSGarrett D'Amore 	 * lines after a failure of this sort.
1187f2b90c3cSGarrett D'Amore 	 */
1188f2b90c3cSGarrett D'Amore 	(void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
1189f2b90c3cSGarrett D'Amore 	(void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
1190f2b90c3cSGarrett D'Amore 
1191f2b90c3cSGarrett D'Amore 	return (rv);
11924bb7efa7SGarrett D'Amore }
11934bb7efa7SGarrett D'Amore 
11944bb7efa7SGarrett D'Amore sda_err_t
11954bb7efa7SGarrett D'Amore sdhost_poll(void *arg)
11964bb7efa7SGarrett D'Amore {
11974bb7efa7SGarrett D'Amore 	sdslot_t	*ss = arg;
11984bb7efa7SGarrett D'Amore 
11994bb7efa7SGarrett D'Amore 	(void) sdhost_slot_intr(ss);
12004bb7efa7SGarrett D'Amore 	return (SDA_EOK);
12014bb7efa7SGarrett D'Amore }
12024bb7efa7SGarrett D'Amore 
12034bb7efa7SGarrett D'Amore sda_err_t
12044bb7efa7SGarrett D'Amore sdhost_cmd(void *arg, sda_cmd_t *cmdp)
12054bb7efa7SGarrett D'Amore {
12064bb7efa7SGarrett D'Amore 	sdslot_t	*ss = arg;
12074bb7efa7SGarrett D'Amore 	uint16_t	command;
12084bb7efa7SGarrett D'Amore 	uint16_t	mode;
12094bb7efa7SGarrett D'Amore 	sda_err_t	rv;
12104bb7efa7SGarrett D'Amore 
12114bb7efa7SGarrett D'Amore 	/*
12124bb7efa7SGarrett D'Amore 	 * Command register:
12134bb7efa7SGarrett D'Amore 	 * bit 13-8	= command index
12144bb7efa7SGarrett D'Amore 	 * bit 7-6	= command type (always zero for us!)
12154bb7efa7SGarrett D'Amore 	 * bit 5	= data present select
12164bb7efa7SGarrett D'Amore 	 * bit 4	= command index check (always on!)
12174bb7efa7SGarrett D'Amore 	 * bit 3	= command CRC check enable
12184bb7efa7SGarrett D'Amore 	 * bit 2	= reserved
12194bb7efa7SGarrett D'Amore 	 * bit 1-0	= response type
12204bb7efa7SGarrett D'Amore 	 */
12214bb7efa7SGarrett D'Amore 
12224bb7efa7SGarrett D'Amore 	command = ((uint16_t)cmdp->sc_index << 8);
12234bb7efa7SGarrett D'Amore 	command |= COMMAND_TYPE_NORM |
12244bb7efa7SGarrett D'Amore 	    COMMAND_INDEX_CHECK_EN | COMMAND_CRC_CHECK_EN;
12254bb7efa7SGarrett D'Amore 
12264bb7efa7SGarrett D'Amore 	switch (cmdp->sc_rtype) {
12274bb7efa7SGarrett D'Amore 	case R0:
12284bb7efa7SGarrett D'Amore 		command |= COMMAND_RESP_NONE;
12294bb7efa7SGarrett D'Amore 		break;
12304bb7efa7SGarrett D'Amore 	case R1:
12314bb7efa7SGarrett D'Amore 	case R5:
12324bb7efa7SGarrett D'Amore 	case R6:
12334bb7efa7SGarrett D'Amore 	case R7:
12344bb7efa7SGarrett D'Amore 		command |= COMMAND_RESP_48;
12354bb7efa7SGarrett D'Amore 		break;
12364bb7efa7SGarrett D'Amore 	case R1b:
12374bb7efa7SGarrett D'Amore 	case R5b:
12384bb7efa7SGarrett D'Amore 		command |= COMMAND_RESP_48_BUSY;
12394bb7efa7SGarrett D'Amore 		break;
12404bb7efa7SGarrett D'Amore 	case R2:
12414bb7efa7SGarrett D'Amore 		command |= COMMAND_RESP_136;
12424bb7efa7SGarrett D'Amore 		command &= ~(COMMAND_INDEX_CHECK_EN | COMMAND_CRC_CHECK_EN);
12434bb7efa7SGarrett D'Amore 		break;
12444bb7efa7SGarrett D'Amore 	case R3:
12454bb7efa7SGarrett D'Amore 	case R4:
12464bb7efa7SGarrett D'Amore 		command |= COMMAND_RESP_48;
12474bb7efa7SGarrett D'Amore 		command &= ~COMMAND_CRC_CHECK_EN;
12484bb7efa7SGarrett D'Amore 		command &= ~COMMAND_INDEX_CHECK_EN;
12494bb7efa7SGarrett D'Amore 		break;
12504bb7efa7SGarrett D'Amore 	default:
12514bb7efa7SGarrett D'Amore 		return (SDA_EINVAL);
12524bb7efa7SGarrett D'Amore 	}
12534bb7efa7SGarrett D'Amore 
12544bb7efa7SGarrett D'Amore 	mutex_enter(&ss->ss_lock);
12554bb7efa7SGarrett D'Amore 	if (ss->ss_suspended) {
12564bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
12574bb7efa7SGarrett D'Amore 		return (SDA_ESUSPENDED);
12584bb7efa7SGarrett D'Amore 	}
12594bb7efa7SGarrett D'Amore 
12604bb7efa7SGarrett D'Amore 	if (cmdp->sc_nblks != 0) {
12614bb7efa7SGarrett D'Amore 		uint16_t	blksz;
12624bb7efa7SGarrett D'Amore 		uint16_t	nblks;
12634bb7efa7SGarrett D'Amore 
12644bb7efa7SGarrett D'Amore 		blksz = cmdp->sc_blksz;
12654bb7efa7SGarrett D'Amore 		nblks = cmdp->sc_nblks;
12664bb7efa7SGarrett D'Amore 
12674bb7efa7SGarrett D'Amore 		/*
12684bb7efa7SGarrett D'Amore 		 * Ensure that we have good data.
12694bb7efa7SGarrett D'Amore 		 */
12704bb7efa7SGarrett D'Amore 		if ((blksz < 1) || (blksz > 2048)) {
12714bb7efa7SGarrett D'Amore 			mutex_exit(&ss->ss_lock);
12724bb7efa7SGarrett D'Amore 			return (SDA_EINVAL);
12734bb7efa7SGarrett D'Amore 		}
12744bb7efa7SGarrett D'Amore 		command |= COMMAND_DATA_PRESENT;
12754bb7efa7SGarrett D'Amore 
12764bb7efa7SGarrett D'Amore 		ss->ss_blksz = blksz;
12774bb7efa7SGarrett D'Amore 
12784bb7efa7SGarrett D'Amore 		/*
12794bb7efa7SGarrett D'Amore 		 * Only SDMA for now.  We can investigate ADMA2 later.
12804bb7efa7SGarrett D'Amore 		 * (Right now we don't have ADMA2 capable hardware.)
12814bb7efa7SGarrett D'Amore 		 */
12824bb7efa7SGarrett D'Amore 		if (((ss->ss_capab & CAPAB_SDMA) != 0) &&
12834bb7efa7SGarrett D'Amore 		    (cmdp->sc_ndmac != 0)) {
12844bb7efa7SGarrett D'Amore 			ddi_dma_cookie_t	*dmacs = cmdp->sc_dmacs;
12854bb7efa7SGarrett D'Amore 
12864bb7efa7SGarrett D'Amore 			ASSERT(dmacs != NULL);
12874bb7efa7SGarrett D'Amore 
12884bb7efa7SGarrett D'Amore 			ss->ss_kvaddr = NULL;
12894bb7efa7SGarrett D'Amore 			ss->ss_resid = 0;
12904bb7efa7SGarrett D'Amore 			ss->ss_dmacs = dmacs;
12914bb7efa7SGarrett D'Amore 			ss->ss_ndmac = cmdp->sc_ndmac - 1;
12924bb7efa7SGarrett D'Amore 
12934bb7efa7SGarrett D'Amore 			PUT32(ss, REG_SDMA_ADDR, dmacs->dmac_address);
12944bb7efa7SGarrett D'Amore 			mode = XFR_MODE_DMA_EN;
12954bb7efa7SGarrett D'Amore 			PUT16(ss, REG_BLKSZ, blksz);
12964bb7efa7SGarrett D'Amore 
12974bb7efa7SGarrett D'Amore 		} else {
12984bb7efa7SGarrett D'Amore 			ss->ss_kvaddr = (void *)cmdp->sc_kvaddr;
12994bb7efa7SGarrett D'Amore 			ss->ss_resid = nblks;
13004bb7efa7SGarrett D'Amore 			ss->ss_dmacs = NULL;
13014bb7efa7SGarrett D'Amore 			ss->ss_ndmac = 0;
13024bb7efa7SGarrett D'Amore 			mode = 0;
13034bb7efa7SGarrett D'Amore 			PUT16(ss, REG_BLKSZ, blksz);
13044bb7efa7SGarrett D'Amore 		}
13054bb7efa7SGarrett D'Amore 
13064bb7efa7SGarrett D'Amore 		if (nblks > 1) {
13074bb7efa7SGarrett D'Amore 			mode |= XFR_MODE_MULTI | XFR_MODE_COUNT;
13084bb7efa7SGarrett D'Amore 			if (cmdp->sc_flags & SDA_CMDF_AUTO_CMD12)
13094bb7efa7SGarrett D'Amore 				mode |= XFR_MODE_AUTO_CMD12;
13104bb7efa7SGarrett D'Amore 		}
13114bb7efa7SGarrett D'Amore 		if ((cmdp->sc_flags & SDA_CMDF_READ) != 0) {
13124bb7efa7SGarrett D'Amore 			mode |= XFR_MODE_READ;
13134bb7efa7SGarrett D'Amore 		}
13144bb7efa7SGarrett D'Amore 
13154bb7efa7SGarrett D'Amore 		ss->ss_mode = mode;
13164bb7efa7SGarrett D'Amore 
13174bb7efa7SGarrett D'Amore 		PUT8(ss, REG_TIMEOUT_CONTROL, ss->ss_tmoutclk);
13184bb7efa7SGarrett D'Amore 		PUT16(ss, REG_BLOCK_COUNT, nblks);
13194bb7efa7SGarrett D'Amore 		PUT16(ss, REG_XFR_MODE, mode);
13204bb7efa7SGarrett D'Amore 	}
13214bb7efa7SGarrett D'Amore 
13224bb7efa7SGarrett D'Amore 	PUT32(ss, REG_ARGUMENT, cmdp->sc_argument);
13234bb7efa7SGarrett D'Amore 	PUT16(ss, REG_COMMAND, command);
13244bb7efa7SGarrett D'Amore 
13254bb7efa7SGarrett D'Amore 	rv = sdhost_wait_cmd(ss, cmdp);
13264bb7efa7SGarrett D'Amore 
13274bb7efa7SGarrett D'Amore 	mutex_exit(&ss->ss_lock);
13284bb7efa7SGarrett D'Amore 
13294bb7efa7SGarrett D'Amore 	return (rv);
13304bb7efa7SGarrett D'Amore }
13314bb7efa7SGarrett D'Amore 
13324bb7efa7SGarrett D'Amore sda_err_t
13334bb7efa7SGarrett D'Amore sdhost_getprop(void *arg, sda_prop_t prop, uint32_t *val)
13344bb7efa7SGarrett D'Amore {
13354bb7efa7SGarrett D'Amore 	sdslot_t	*ss = arg;
13364bb7efa7SGarrett D'Amore 	sda_err_t	rv = 0;
13374bb7efa7SGarrett D'Amore 
13384bb7efa7SGarrett D'Amore 	mutex_enter(&ss->ss_lock);
13394bb7efa7SGarrett D'Amore 
13404bb7efa7SGarrett D'Amore 	if (ss->ss_suspended) {
13414bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
13424bb7efa7SGarrett D'Amore 		return (SDA_ESUSPENDED);
13434bb7efa7SGarrett D'Amore 	}
13444bb7efa7SGarrett D'Amore 	switch (prop) {
13454bb7efa7SGarrett D'Amore 	case SDA_PROP_INSERTED:
13464bb7efa7SGarrett D'Amore 		if (CHECK_STATE(ss, CARD_INSERTED)) {
13474bb7efa7SGarrett D'Amore 			*val = B_TRUE;
13484bb7efa7SGarrett D'Amore 		} else {
13494bb7efa7SGarrett D'Amore 			*val = B_FALSE;
13504bb7efa7SGarrett D'Amore 		}
13514bb7efa7SGarrett D'Amore 		break;
13524bb7efa7SGarrett D'Amore 
13534bb7efa7SGarrett D'Amore 	case SDA_PROP_WPROTECT:
13544bb7efa7SGarrett D'Amore 		if (CHECK_STATE(ss, WRITE_ENABLE)) {
13554bb7efa7SGarrett D'Amore 			*val = B_FALSE;
13564bb7efa7SGarrett D'Amore 		} else {
13574bb7efa7SGarrett D'Amore 			*val = B_TRUE;
13584bb7efa7SGarrett D'Amore 		}
13594bb7efa7SGarrett D'Amore 		break;
13604bb7efa7SGarrett D'Amore 
13614bb7efa7SGarrett D'Amore 	case SDA_PROP_OCR:
13624bb7efa7SGarrett D'Amore 		*val = ss->ss_ocr;
13634bb7efa7SGarrett D'Amore 		break;
13644bb7efa7SGarrett D'Amore 
13654bb7efa7SGarrett D'Amore 	case SDA_PROP_CLOCK:
13664bb7efa7SGarrett D'Amore 		*val = ss->ss_cardclk;
13674bb7efa7SGarrett D'Amore 		break;
13684bb7efa7SGarrett D'Amore 
13694bb7efa7SGarrett D'Amore 	case SDA_PROP_CAP_HISPEED:
13704bb7efa7SGarrett D'Amore 		if ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0) {
13714bb7efa7SGarrett D'Amore 			*val = B_TRUE;
13724bb7efa7SGarrett D'Amore 		} else {
13734bb7efa7SGarrett D'Amore 			*val = B_FALSE;
13744bb7efa7SGarrett D'Amore 		}
13754bb7efa7SGarrett D'Amore 		break;
13764bb7efa7SGarrett D'Amore 
13774bb7efa7SGarrett D'Amore 	case SDA_PROP_CAP_4BITS:
13784bb7efa7SGarrett D'Amore 		*val = B_TRUE;
13794bb7efa7SGarrett D'Amore 		break;
13804bb7efa7SGarrett D'Amore 
13814bb7efa7SGarrett D'Amore 	case SDA_PROP_CAP_NOPIO:
13824bb7efa7SGarrett D'Amore 		if ((ss->ss_capab & CAPAB_SDMA) != 0) {
13834bb7efa7SGarrett D'Amore 			*val = B_TRUE;
13844bb7efa7SGarrett D'Amore 		} else {
13854bb7efa7SGarrett D'Amore 			*val = B_FALSE;
13864bb7efa7SGarrett D'Amore 		}
13874bb7efa7SGarrett D'Amore 		break;
13884bb7efa7SGarrett D'Amore 
13894bb7efa7SGarrett D'Amore 	case SDA_PROP_CAP_INTR:
13904bb7efa7SGarrett D'Amore 	case SDA_PROP_CAP_8BITS:
13914bb7efa7SGarrett D'Amore 		*val = B_FALSE;
13924bb7efa7SGarrett D'Amore 		break;
13934bb7efa7SGarrett D'Amore 
13944bb7efa7SGarrett D'Amore 	default:
13954bb7efa7SGarrett D'Amore 		rv = SDA_ENOTSUP;
13964bb7efa7SGarrett D'Amore 		break;
13974bb7efa7SGarrett D'Amore 	}
13984bb7efa7SGarrett D'Amore 	mutex_exit(&ss->ss_lock);
13994bb7efa7SGarrett D'Amore 
14004bb7efa7SGarrett D'Amore 	return (rv);
14014bb7efa7SGarrett D'Amore }
14024bb7efa7SGarrett D'Amore 
14034bb7efa7SGarrett D'Amore sda_err_t
14044bb7efa7SGarrett D'Amore sdhost_setprop(void *arg, sda_prop_t prop, uint32_t val)
14054bb7efa7SGarrett D'Amore {
14064bb7efa7SGarrett D'Amore 	sdslot_t	*ss = arg;
14074bb7efa7SGarrett D'Amore 	sda_err_t	rv = SDA_EOK;
14084bb7efa7SGarrett D'Amore 
14094bb7efa7SGarrett D'Amore 	mutex_enter(&ss->ss_lock);
14104bb7efa7SGarrett D'Amore 
14114bb7efa7SGarrett D'Amore 	if (ss->ss_suspended) {
14124bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
14134bb7efa7SGarrett D'Amore 		return (SDA_ESUSPENDED);
14144bb7efa7SGarrett D'Amore 	}
14154bb7efa7SGarrett D'Amore 
14164bb7efa7SGarrett D'Amore 	switch (prop) {
14174bb7efa7SGarrett D'Amore 	case SDA_PROP_LED:
14184bb7efa7SGarrett D'Amore 		if (val) {
14194bb7efa7SGarrett D'Amore 			SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_LED_ON);
14204bb7efa7SGarrett D'Amore 		} else {
14214bb7efa7SGarrett D'Amore 			CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_LED_ON);
14224bb7efa7SGarrett D'Amore 		}
14234bb7efa7SGarrett D'Amore 		break;
14244bb7efa7SGarrett D'Amore 
14254bb7efa7SGarrett D'Amore 	case SDA_PROP_CLOCK:
14264bb7efa7SGarrett D'Amore 		rv = sdhost_set_clock(arg, val);
14274bb7efa7SGarrett D'Amore 		break;
14284bb7efa7SGarrett D'Amore 
14294bb7efa7SGarrett D'Amore 	case SDA_PROP_BUSWIDTH:
14304bb7efa7SGarrett D'Amore 		switch (val) {
14314bb7efa7SGarrett D'Amore 		case 1:
14324bb7efa7SGarrett D'Amore 			CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_DATA_WIDTH);
14334bb7efa7SGarrett D'Amore 			break;
14344bb7efa7SGarrett D'Amore 		case 4:
14354bb7efa7SGarrett D'Amore 			SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_DATA_WIDTH);
14364bb7efa7SGarrett D'Amore 			break;
14374bb7efa7SGarrett D'Amore 		default:
14384bb7efa7SGarrett D'Amore 			rv = SDA_EINVAL;
14394bb7efa7SGarrett D'Amore 		}
14404bb7efa7SGarrett D'Amore 		break;
14414bb7efa7SGarrett D'Amore 
14424bb7efa7SGarrett D'Amore 	case SDA_PROP_OCR:
14434bb7efa7SGarrett D'Amore 		val &= ss->ss_ocr;
14444bb7efa7SGarrett D'Amore 
14454bb7efa7SGarrett D'Amore 		if (val & OCR_17_18V) {
14464bb7efa7SGarrett D'Amore 			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_18V);
14474bb7efa7SGarrett D'Amore 			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_18V |
14484bb7efa7SGarrett D'Amore 			    POWER_CONTROL_BUS_POWER);
14494bb7efa7SGarrett D'Amore 		} else if (val & OCR_29_30V) {
14504bb7efa7SGarrett D'Amore 			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_30V);
14514bb7efa7SGarrett D'Amore 			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_30V |
14524bb7efa7SGarrett D'Amore 			    POWER_CONTROL_BUS_POWER);
14534bb7efa7SGarrett D'Amore 		} else if (val & OCR_32_33V) {
14544bb7efa7SGarrett D'Amore 			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_33V);
14554bb7efa7SGarrett D'Amore 			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_33V |
14564bb7efa7SGarrett D'Amore 			    POWER_CONTROL_BUS_POWER);
14574bb7efa7SGarrett D'Amore 		} else if (val == 0) {
14584bb7efa7SGarrett D'Amore 			/* turn off power */
14594bb7efa7SGarrett D'Amore 			PUT8(ss, REG_POWER_CONTROL, 0);
14604bb7efa7SGarrett D'Amore 		} else {
14614bb7efa7SGarrett D'Amore 			rv = SDA_EINVAL;
14624bb7efa7SGarrett D'Amore 		}
14634bb7efa7SGarrett D'Amore 		break;
14644bb7efa7SGarrett D'Amore 
14654bb7efa7SGarrett D'Amore 	case SDA_PROP_HISPEED:
14664bb7efa7SGarrett D'Amore 		if (val) {
14674bb7efa7SGarrett D'Amore 			SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
14684bb7efa7SGarrett D'Amore 		} else {
14694bb7efa7SGarrett D'Amore 			CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
14704bb7efa7SGarrett D'Amore 		}
14714bb7efa7SGarrett D'Amore 		/* give clocks time to settle */
14724bb7efa7SGarrett D'Amore 		drv_usecwait(10);
14734bb7efa7SGarrett D'Amore 		break;
14744bb7efa7SGarrett D'Amore 
14754bb7efa7SGarrett D'Amore 	default:
14764bb7efa7SGarrett D'Amore 		rv = SDA_ENOTSUP;
14774bb7efa7SGarrett D'Amore 		break;
14784bb7efa7SGarrett D'Amore 	}
14794bb7efa7SGarrett D'Amore 
14804bb7efa7SGarrett D'Amore 	/*
14814bb7efa7SGarrett D'Amore 	 * Apparently some controllers (ENE) have issues with changing
14824bb7efa7SGarrett D'Amore 	 * certain parameters (bus width seems to be one), requiring
14834bb7efa7SGarrett D'Amore 	 * a reset of the DAT and CMD lines.
14844bb7efa7SGarrett D'Amore 	 */
14854bb7efa7SGarrett D'Amore 	if (rv == SDA_EOK) {
14864bb7efa7SGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
14874bb7efa7SGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
14884bb7efa7SGarrett D'Amore 	}
14894bb7efa7SGarrett D'Amore 	mutex_exit(&ss->ss_lock);
14904bb7efa7SGarrett D'Amore 	return (rv);
14914bb7efa7SGarrett D'Amore }
14924bb7efa7SGarrett D'Amore 
14934bb7efa7SGarrett D'Amore sda_err_t
14944bb7efa7SGarrett D'Amore sdhost_reset(void *arg)
14954bb7efa7SGarrett D'Amore {
14964bb7efa7SGarrett D'Amore 	sdslot_t	*ss = arg;
14974bb7efa7SGarrett D'Amore 
14984bb7efa7SGarrett D'Amore 	mutex_enter(&ss->ss_lock);
14994bb7efa7SGarrett D'Amore 	if (!ss->ss_suspended) {
15004bb7efa7SGarrett D'Amore 		if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK) {
15014bb7efa7SGarrett D'Amore 			mutex_exit(&ss->ss_lock);
15024bb7efa7SGarrett D'Amore 			return (SDA_ETIME);
15034bb7efa7SGarrett D'Amore 		}
15044bb7efa7SGarrett D'Amore 		sdhost_enable_interrupts(ss);
15054bb7efa7SGarrett D'Amore 	}
15064bb7efa7SGarrett D'Amore 	mutex_exit(&ss->ss_lock);
15074bb7efa7SGarrett D'Amore 	return (SDA_EOK);
15084bb7efa7SGarrett D'Amore }
15094bb7efa7SGarrett D'Amore 
15104bb7efa7SGarrett D'Amore sda_err_t
15114bb7efa7SGarrett D'Amore sdhost_halt(void *arg)
15124bb7efa7SGarrett D'Amore {
15134bb7efa7SGarrett D'Amore 	sdslot_t	*ss = arg;
15144bb7efa7SGarrett D'Amore 
15154bb7efa7SGarrett D'Amore 	mutex_enter(&ss->ss_lock);
15164bb7efa7SGarrett D'Amore 	if (!ss->ss_suspended) {
15174bb7efa7SGarrett D'Amore 		sdhost_disable_interrupts(ss);
15184bb7efa7SGarrett D'Amore 		/* this has the side effect of removing power from the card */
15194bb7efa7SGarrett D'Amore 		if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK) {
15204bb7efa7SGarrett D'Amore 			mutex_exit(&ss->ss_lock);
15214bb7efa7SGarrett D'Amore 			return (SDA_ETIME);
15224bb7efa7SGarrett D'Amore 		}
15234bb7efa7SGarrett D'Amore 	}
15244bb7efa7SGarrett D'Amore 	mutex_exit(&ss->ss_lock);
15254bb7efa7SGarrett D'Amore 	return (SDA_EOK);
15264bb7efa7SGarrett D'Amore }
1527