14bb7efa7SGarrett D'Amore /*
24bb7efa7SGarrett D'Amore  * CDDL HEADER START
34bb7efa7SGarrett D'Amore  *
44bb7efa7SGarrett D'Amore  * The contents of this file are subject to the terms of the
54bb7efa7SGarrett D'Amore  * Common Development and Distribution License (the "License").
64bb7efa7SGarrett D'Amore  * You may not use this file except in compliance with the License.
74bb7efa7SGarrett D'Amore  *
84bb7efa7SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94bb7efa7SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
104bb7efa7SGarrett D'Amore  * See the License for the specific language governing permissions
114bb7efa7SGarrett D'Amore  * and limitations under the License.
124bb7efa7SGarrett D'Amore  *
134bb7efa7SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
144bb7efa7SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154bb7efa7SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
164bb7efa7SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
174bb7efa7SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
184bb7efa7SGarrett D'Amore  *
194bb7efa7SGarrett D'Amore  * CDDL HEADER END
204bb7efa7SGarrett D'Amore  */
214bb7efa7SGarrett D'Amore /*
22*3f7d54a6SGarrett D'Amore  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
234bb7efa7SGarrett D'Amore  */
244bb7efa7SGarrett D'Amore 
254bb7efa7SGarrett D'Amore #include "sdhost.h"
264bb7efa7SGarrett D'Amore 
279a5113a6SGarrett D'Amore typedef	struct sdstats	sdstats_t;
284bb7efa7SGarrett D'Amore typedef	struct sdslot	sdslot_t;
294bb7efa7SGarrett D'Amore typedef	struct sdhost	sdhost_t;
304bb7efa7SGarrett D'Amore 
319a5113a6SGarrett D'Amore struct sdstats {
329a5113a6SGarrett D'Amore 	kstat_named_t	ks_ncmd;
339a5113a6SGarrett D'Amore 	kstat_named_t	ks_ixfr;
349a5113a6SGarrett D'Amore 	kstat_named_t	ks_oxfr;
359a5113a6SGarrett D'Amore 	kstat_named_t	ks_ibytes;
369a5113a6SGarrett D'Amore 	kstat_named_t	ks_obytes;
379a5113a6SGarrett D'Amore 	kstat_named_t	ks_npio;
389a5113a6SGarrett D'Amore 	kstat_named_t	ks_ndma;
399a5113a6SGarrett D'Amore 	kstat_named_t	ks_nmulti;
409a5113a6SGarrett D'Amore 	kstat_named_t	ks_baseclk;
419a5113a6SGarrett D'Amore 	kstat_named_t	ks_cardclk;
429a5113a6SGarrett D'Amore 	kstat_named_t	ks_tmusecs;
439a5113a6SGarrett D'Amore 	kstat_named_t	ks_width;
449a5113a6SGarrett D'Amore 	kstat_named_t	ks_flags;
459a5113a6SGarrett D'Amore 	kstat_named_t	ks_capab;
469a5113a6SGarrett D'Amore };
479a5113a6SGarrett D'Amore 
489a5113a6SGarrett D'Amore #define	SDFLAG_FORCE_PIO		(1U << 0)
499a5113a6SGarrett D'Amore #define	SDFLAG_FORCE_DMA		(1U << 1)
509a5113a6SGarrett D'Amore 
514bb7efa7SGarrett D'Amore /*
524bb7efa7SGarrett D'Amore  * Per slot state.
534bb7efa7SGarrett D'Amore  */
544bb7efa7SGarrett D'Amore struct sdslot {
554bb7efa7SGarrett D'Amore 	sda_host_t		*ss_host;
564bb7efa7SGarrett D'Amore 	int			ss_num;
574bb7efa7SGarrett D'Amore 	ddi_acc_handle_t	ss_acch;
584bb7efa7SGarrett D'Amore 	caddr_t 		ss_regva;
594bb7efa7SGarrett D'Amore 	kmutex_t		ss_lock;
604bb7efa7SGarrett D'Amore 	uint8_t			ss_tmoutclk;
614bb7efa7SGarrett D'Amore 	uint32_t		ss_ocr;		/* OCR formatted voltages */
624bb7efa7SGarrett D'Amore 	uint16_t		ss_mode;
634bb7efa7SGarrett D'Amore 	boolean_t		ss_suspended;
649a5113a6SGarrett D'Amore 	sdstats_t		ss_stats;
659a5113a6SGarrett D'Amore #define	ss_ncmd			ss_stats.ks_ncmd.value.ui64
669a5113a6SGarrett D'Amore #define	ss_ixfr			ss_stats.ks_ixfr.value.ui64
679a5113a6SGarrett D'Amore #define	ss_oxfr			ss_stats.ks_oxfr.value.ui64
689a5113a6SGarrett D'Amore #define	ss_ibytes		ss_stats.ks_ibytes.value.ui64
699a5113a6SGarrett D'Amore #define	ss_obytes		ss_stats.ks_obytes.value.ui64
709a5113a6SGarrett D'Amore #define	ss_ndma			ss_stats.ks_ndma.value.ui64
719a5113a6SGarrett D'Amore #define	ss_npio			ss_stats.ks_npio.value.ui64
729a5113a6SGarrett D'Amore #define	ss_nmulti		ss_stats.ks_nmulti.value.ui64
739a5113a6SGarrett D'Amore 
749a5113a6SGarrett D'Amore #define	ss_baseclk		ss_stats.ks_baseclk.value.ui32
759a5113a6SGarrett D'Amore #define	ss_cardclk		ss_stats.ks_cardclk.value.ui32
769a5113a6SGarrett D'Amore #define	ss_tmusecs		ss_stats.ks_tmusecs.value.ui32
779a5113a6SGarrett D'Amore #define	ss_width		ss_stats.ks_width.value.ui32
789a5113a6SGarrett D'Amore #define	ss_flags		ss_stats.ks_flags.value.ui32
799a5113a6SGarrett D'Amore #define	ss_capab		ss_stats.ks_capab.value.ui32
809a5113a6SGarrett D'Amore 	kstat_t			*ss_ksp;
814bb7efa7SGarrett D'Amore 
824bb7efa7SGarrett D'Amore 	/*
834bb7efa7SGarrett D'Amore 	 * Command in progress
844bb7efa7SGarrett D'Amore 	 */
854bb7efa7SGarrett D'Amore 	uint8_t			*ss_kvaddr;
864bb7efa7SGarrett D'Amore 	int			ss_blksz;
874bb7efa7SGarrett D'Amore 	uint16_t		ss_resid;	/* in blocks */
889a5113a6SGarrett D'Amore 	int			ss_rcnt;
894bb7efa7SGarrett D'Amore 
904bb7efa7SGarrett D'Amore 	/* scratch buffer, to receive extra PIO data */
919a5113a6SGarrett D'Amore 	caddr_t			ss_bounce;
929a5113a6SGarrett D'Amore 	ddi_dma_handle_t	ss_bufdmah;
939a5113a6SGarrett D'Amore 	ddi_acc_handle_t	ss_bufacch;
949a5113a6SGarrett D'Amore 	ddi_dma_cookie_t	ss_bufdmac;
954bb7efa7SGarrett D'Amore };
964bb7efa7SGarrett D'Amore 
979a5113a6SGarrett D'Amore /*
989a5113a6SGarrett D'Amore  * This allocates a rather large chunk of contiguous memory for DMA.
999a5113a6SGarrett D'Amore  * But doing so means that we'll almost never have to resort to PIO.
1009a5113a6SGarrett D'Amore  */
1019a5113a6SGarrett D'Amore #define	SDHOST_BOUNCESZ		65536
1029a5113a6SGarrett D'Amore 
1034bb7efa7SGarrett D'Amore /*
1044bb7efa7SGarrett D'Amore  * Per controller state.
1054bb7efa7SGarrett D'Amore  */
1064bb7efa7SGarrett D'Amore struct sdhost {
1074bb7efa7SGarrett D'Amore 	int			sh_numslots;
1084bb7efa7SGarrett D'Amore 	ddi_dma_attr_t		sh_dmaattr;
1094bb7efa7SGarrett D'Amore 	sdslot_t		sh_slots[SDHOST_MAXSLOTS];
1104bb7efa7SGarrett D'Amore 	sda_host_t		*sh_host;
1114bb7efa7SGarrett D'Amore 
1124bb7efa7SGarrett D'Amore 	/*
1134bb7efa7SGarrett D'Amore 	 * Interrupt related information.
1144bb7efa7SGarrett D'Amore 	 */
1154bb7efa7SGarrett D'Amore 	ddi_intr_handle_t	sh_ihandle;
1164bb7efa7SGarrett D'Amore 	int			sh_icap;
1174bb7efa7SGarrett D'Amore 	uint_t			sh_ipri;
1184bb7efa7SGarrett D'Amore };
1194bb7efa7SGarrett D'Amore 
1209a5113a6SGarrett D'Amore #define	PROPSET(x)							\
1219a5113a6SGarrett D'Amore 	(ddi_prop_get_int(DDI_DEV_T_ANY, dip,				\
1229a5113a6SGarrett D'Amore 	DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, x, 0) != 0)
1239a5113a6SGarrett D'Amore 
1244bb7efa7SGarrett D'Amore 
1254bb7efa7SGarrett D'Amore static int sdhost_attach(dev_info_t *, ddi_attach_cmd_t);
1264bb7efa7SGarrett D'Amore static int sdhost_detach(dev_info_t *, ddi_detach_cmd_t);
127f2b90c3cSGarrett D'Amore static int sdhost_quiesce(dev_info_t *);
1284bb7efa7SGarrett D'Amore static int sdhost_suspend(dev_info_t *);
1294bb7efa7SGarrett D'Amore static int sdhost_resume(dev_info_t *);
1304bb7efa7SGarrett D'Amore 
1314bb7efa7SGarrett D'Amore static void sdhost_enable_interrupts(sdslot_t *);
1324bb7efa7SGarrett D'Amore static void sdhost_disable_interrupts(sdslot_t *);
1334bb7efa7SGarrett D'Amore static int sdhost_setup_intr(dev_info_t *, sdhost_t *);
1344bb7efa7SGarrett D'Amore static uint_t sdhost_intr(caddr_t, caddr_t);
1354bb7efa7SGarrett D'Amore static int sdhost_init_slot(dev_info_t *, sdhost_t *, int, int);
1364bb7efa7SGarrett D'Amore static void sdhost_uninit_slot(sdhost_t *, int);
1374bb7efa7SGarrett D'Amore static sda_err_t sdhost_soft_reset(sdslot_t *, uint8_t);
1384bb7efa7SGarrett D'Amore static sda_err_t sdhost_set_clock(sdslot_t *, uint32_t);
1394bb7efa7SGarrett D'Amore static void sdhost_xfer_done(sdslot_t *, sda_err_t);
1404bb7efa7SGarrett D'Amore static sda_err_t sdhost_wait_cmd(sdslot_t *, sda_cmd_t *);
1414bb7efa7SGarrett D'Amore static uint_t sdhost_slot_intr(sdslot_t *);
1424bb7efa7SGarrett D'Amore 
1434bb7efa7SGarrett D'Amore static sda_err_t sdhost_cmd(void *, sda_cmd_t *);
1444bb7efa7SGarrett D'Amore static sda_err_t sdhost_getprop(void *, sda_prop_t, uint32_t *);
1454bb7efa7SGarrett D'Amore static sda_err_t sdhost_setprop(void *, sda_prop_t, uint32_t);
1464bb7efa7SGarrett D'Amore static sda_err_t sdhost_poll(void *);
1474bb7efa7SGarrett D'Amore static sda_err_t sdhost_reset(void *);
1484bb7efa7SGarrett D'Amore static sda_err_t sdhost_halt(void *);
1494bb7efa7SGarrett D'Amore 
1504bb7efa7SGarrett D'Amore static struct dev_ops sdhost_dev_ops = {
1514bb7efa7SGarrett D'Amore 	DEVO_REV,			/* devo_rev */
1524bb7efa7SGarrett D'Amore 	0,				/* devo_refcnt */
1534bb7efa7SGarrett D'Amore 	ddi_no_info,			/* devo_getinfo */
1544bb7efa7SGarrett D'Amore 	nulldev,			/* devo_identify */
1554bb7efa7SGarrett D'Amore 	nulldev,			/* devo_probe */
1564bb7efa7SGarrett D'Amore 	sdhost_attach,			/* devo_attach */
1574bb7efa7SGarrett D'Amore 	sdhost_detach,			/* devo_detach */
1584bb7efa7SGarrett D'Amore 	nodev,				/* devo_reset */
1594bb7efa7SGarrett D'Amore 	NULL,				/* devo_cb_ops */
1604bb7efa7SGarrett D'Amore 	NULL,				/* devo_bus_ops */
16119397407SSherry Moore 	NULL,				/* devo_power */
162f2b90c3cSGarrett D'Amore 	sdhost_quiesce,			/* devo_quiesce */
1634bb7efa7SGarrett D'Amore };
1644bb7efa7SGarrett D'Amore 
1654bb7efa7SGarrett D'Amore static struct modldrv sdhost_modldrv = {
1664bb7efa7SGarrett D'Amore 	&mod_driverops,			/* drv_modops */
1674bb7efa7SGarrett D'Amore 	"Standard SD Host Controller",	/* drv_linkinfo */
1684bb7efa7SGarrett D'Amore 	&sdhost_dev_ops			/* drv_dev_ops */
1694bb7efa7SGarrett D'Amore };
1704bb7efa7SGarrett D'Amore 
1714bb7efa7SGarrett D'Amore static struct modlinkage modlinkage = {
1724bb7efa7SGarrett D'Amore 	MODREV_1,			/* ml_rev */
1734bb7efa7SGarrett D'Amore 	{ &sdhost_modldrv, NULL }	/* ml_linkage */
1744bb7efa7SGarrett D'Amore };
1754bb7efa7SGarrett D'Amore 
1764bb7efa7SGarrett D'Amore static struct sda_ops sdhost_ops = {
1774bb7efa7SGarrett D'Amore 	SDA_OPS_VERSION,
1784bb7efa7SGarrett D'Amore 	sdhost_cmd,			/* so_cmd */
1794bb7efa7SGarrett D'Amore 	sdhost_getprop,			/* so_getprop */
1804bb7efa7SGarrett D'Amore 	sdhost_setprop,			/* so_setprop */
1814bb7efa7SGarrett D'Amore 	sdhost_poll,			/* so_poll */
1824bb7efa7SGarrett D'Amore 	sdhost_reset,			/* so_reset */
1834bb7efa7SGarrett D'Amore 	sdhost_halt,			/* so_halt */
1844bb7efa7SGarrett D'Amore };
1854bb7efa7SGarrett D'Amore 
1864bb7efa7SGarrett D'Amore static ddi_device_acc_attr_t sdhost_regattr = {
1874bb7efa7SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
1884bb7efa7SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,	/* devacc_attr_endian_flags */
1894bb7efa7SGarrett D'Amore 	DDI_STRICTORDER_ACC,	/* devacc_attr_dataorder */
1904bb7efa7SGarrett D'Amore 	DDI_DEFAULT_ACC,	/* devacc_attr_access */
1914bb7efa7SGarrett D'Amore };
1929a5113a6SGarrett D'Amore static ddi_device_acc_attr_t sdhost_bufattr = {
1939a5113a6SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
1949a5113a6SGarrett D'Amore 	DDI_NEVERSWAP_ACC,	/* devacc_attr_endian_flags */
1959a5113a6SGarrett D'Amore 	DDI_STRICTORDER_ACC,	/* devacc_attr_dataorder */
1969a5113a6SGarrett D'Amore 	DDI_DEFAULT_ACC,	/* devacc_attr_access */
1979a5113a6SGarrett D'Amore };
1984bb7efa7SGarrett D'Amore 
1994bb7efa7SGarrett D'Amore #define	GET16(ss, reg)	\
2004bb7efa7SGarrett D'Amore 	ddi_get16(ss->ss_acch, (void *)(ss->ss_regva + reg))
2014bb7efa7SGarrett D'Amore #define	PUT16(ss, reg, val)	\
2024bb7efa7SGarrett D'Amore 	ddi_put16(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2034bb7efa7SGarrett D'Amore #define	GET32(ss, reg)	\
2044bb7efa7SGarrett D'Amore 	ddi_get32(ss->ss_acch, (void *)(ss->ss_regva + reg))
2054bb7efa7SGarrett D'Amore #define	PUT32(ss, reg, val)	\
2064bb7efa7SGarrett D'Amore 	ddi_put32(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2074bb7efa7SGarrett D'Amore #define	GET64(ss, reg)	\
2084bb7efa7SGarrett D'Amore 	ddi_get64(ss->ss_acch, (void *)(ss->ss_regva + reg))
2094bb7efa7SGarrett D'Amore 
2104bb7efa7SGarrett D'Amore #define	GET8(ss, reg)	\
2114bb7efa7SGarrett D'Amore 	ddi_get8(ss->ss_acch, (void *)(ss->ss_regva + reg))
2124bb7efa7SGarrett D'Amore #define	PUT8(ss, reg, val)	\
2134bb7efa7SGarrett D'Amore 	ddi_put8(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
2144bb7efa7SGarrett D'Amore 
2154bb7efa7SGarrett D'Amore #define	CLR8(ss, reg, mask)	PUT8(ss, reg, GET8(ss, reg) & ~(mask))
2164bb7efa7SGarrett D'Amore #define	SET8(ss, reg, mask)	PUT8(ss, reg, GET8(ss, reg) | (mask))
2174bb7efa7SGarrett D'Amore 
2184bb7efa7SGarrett D'Amore /*
2194bb7efa7SGarrett D'Amore  * If ever anyone uses PIO on SPARC, we have to endian-swap.  But we
2204bb7efa7SGarrett D'Amore  * think that SD Host Controllers are likely to be uncommon on SPARC,
2214bb7efa7SGarrett D'Amore  * and hopefully when they exist at all they will be able to use DMA.
2224bb7efa7SGarrett D'Amore  */
2234bb7efa7SGarrett D'Amore #ifdef	_BIG_ENDIAN
2244bb7efa7SGarrett D'Amore #define	sw32(x)		ddi_swap32(x)
2254bb7efa7SGarrett D'Amore #define	sw16(x)		ddi_swap16(x)
2264bb7efa7SGarrett D'Amore #else
2274bb7efa7SGarrett D'Amore #define	sw32(x)		(x)
2284bb7efa7SGarrett D'Amore #define	sw16(x)		(x)
2294bb7efa7SGarrett D'Amore #endif
2304bb7efa7SGarrett D'Amore 
2314bb7efa7SGarrett D'Amore #define	GETDATA32(ss)		sw32(GET32(ss, REG_DATA))
2324bb7efa7SGarrett D'Amore #define	GETDATA16(ss)		sw16(GET16(ss, REG_DATA))
2334bb7efa7SGarrett D'Amore #define	GETDATA8(ss)		GET8(ss, REG_DATA)
2344bb7efa7SGarrett D'Amore 
2354bb7efa7SGarrett D'Amore #define	PUTDATA32(ss, val)	PUT32(ss, REG_DATA, sw32(val))
2364bb7efa7SGarrett D'Amore #define	PUTDATA16(ss, val)	PUT16(ss, REG_DATA, sw16(val))
2374bb7efa7SGarrett D'Amore #define	PUTDATA8(ss, val)	PUT8(ss, REG_DATA, val)
2384bb7efa7SGarrett D'Amore 
2394bb7efa7SGarrett D'Amore #define	CHECK_STATE(ss, nm)	\
2404bb7efa7SGarrett D'Amore 	((GET32(ss, REG_PRS) & PRS_ ## nm) != 0)
2414bb7efa7SGarrett D'Amore 
2424bb7efa7SGarrett D'Amore int
_init(void)2434bb7efa7SGarrett D'Amore _init(void)
2444bb7efa7SGarrett D'Amore {
2454bb7efa7SGarrett D'Amore 	int	rv;
2464bb7efa7SGarrett D'Amore 
2474bb7efa7SGarrett D'Amore 	sda_host_init_ops(&sdhost_dev_ops);
2484bb7efa7SGarrett D'Amore 
2494bb7efa7SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
2504bb7efa7SGarrett D'Amore 		sda_host_fini_ops(&sdhost_dev_ops);
2514bb7efa7SGarrett D'Amore 	}
2524bb7efa7SGarrett D'Amore 
2534bb7efa7SGarrett D'Amore 	return (rv);
2544bb7efa7SGarrett D'Amore }
2554bb7efa7SGarrett D'Amore 
2564bb7efa7SGarrett D'Amore int
_fini(void)2574bb7efa7SGarrett D'Amore _fini(void)
2584bb7efa7SGarrett D'Amore {
2594bb7efa7SGarrett D'Amore 	int	rv;
2604bb7efa7SGarrett D'Amore 
2614bb7efa7SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) == 0) {
2624bb7efa7SGarrett D'Amore 		sda_host_fini_ops(&sdhost_dev_ops);
2634bb7efa7SGarrett D'Amore 	}
2644bb7efa7SGarrett D'Amore 	return (rv);
2654bb7efa7SGarrett D'Amore }
2664bb7efa7SGarrett D'Amore 
2674bb7efa7SGarrett D'Amore int
_info(struct modinfo * modinfop)2684bb7efa7SGarrett D'Amore _info(struct modinfo *modinfop)
2694bb7efa7SGarrett D'Amore {
2704bb7efa7SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
2714bb7efa7SGarrett D'Amore }
2724bb7efa7SGarrett D'Amore 
2734bb7efa7SGarrett D'Amore int
sdhost_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2744bb7efa7SGarrett D'Amore sdhost_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2754bb7efa7SGarrett D'Amore {
2764bb7efa7SGarrett D'Amore 	sdhost_t		*shp;
2774bb7efa7SGarrett D'Amore 	ddi_acc_handle_t	pcih;
2784bb7efa7SGarrett D'Amore 	uint8_t			slotinfo;
2794bb7efa7SGarrett D'Amore 	uint8_t			bar;
2804bb7efa7SGarrett D'Amore 	int			i;
281f2b90c3cSGarrett D'Amore 	int			rv;
2824bb7efa7SGarrett D'Amore 
2834bb7efa7SGarrett D'Amore 	switch (cmd) {
2844bb7efa7SGarrett D'Amore 	case DDI_ATTACH:
2854bb7efa7SGarrett D'Amore 		break;
2864bb7efa7SGarrett D'Amore 
2874bb7efa7SGarrett D'Amore 	case DDI_RESUME:
2884bb7efa7SGarrett D'Amore 		return (sdhost_resume(dip));
2894bb7efa7SGarrett D'Amore 
2904bb7efa7SGarrett D'Amore 	default:
2914bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
2924bb7efa7SGarrett D'Amore 	}
2934bb7efa7SGarrett D'Amore 
2944bb7efa7SGarrett D'Amore 	/*
2954bb7efa7SGarrett D'Amore 	 * Soft state allocation.
2964bb7efa7SGarrett D'Amore 	 */
2974bb7efa7SGarrett D'Amore 	shp = kmem_zalloc(sizeof (*shp), KM_SLEEP);
2984bb7efa7SGarrett D'Amore 	ddi_set_driver_private(dip, shp);
2994bb7efa7SGarrett D'Amore 
300c8ca7eb8SGarrett D'Amore 	/*
301c8ca7eb8SGarrett D'Amore 	 * Reset the "slot number", so uninit slot works properly.
302c8ca7eb8SGarrett D'Amore 	 */
303c8ca7eb8SGarrett D'Amore 	for (i = 0; i < SDHOST_MAXSLOTS; i++) {
304c8ca7eb8SGarrett D'Amore 		shp->sh_slots[i].ss_num = -1;
305c8ca7eb8SGarrett D'Amore 	}
306c8ca7eb8SGarrett D'Amore 
3074bb7efa7SGarrett D'Amore 	/*
3084bb7efa7SGarrett D'Amore 	 * Initialize DMA attributes.  For now we initialize as for
3094bb7efa7SGarrett D'Amore 	 * SDMA.  If we add ADMA support we can improve this.
3104bb7efa7SGarrett D'Amore 	 */
3114bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_version = DMA_ATTR_V0;
3124bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_addr_lo = 0;
3134bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_addr_hi = 0xffffffffU;
3144bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_count_max = 0xffffffffU;
3159a5113a6SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_align = 4096;		/* Ricoh needs it */
3164bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_burstsizes = 0;	/* for now! */
3174bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_minxfer = 1;
3189a5113a6SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_maxxfer = 0x7ffffU;
3199a5113a6SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_sgllen = 1;		/* no scatter/gather */
3209a5113a6SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_seg = 0x7ffffU;	/* not to cross 512K */
3214bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_granular = 1;
3224bb7efa7SGarrett D'Amore 	shp->sh_dmaattr.dma_attr_flags = 0;
3234bb7efa7SGarrett D'Amore 
3244bb7efa7SGarrett D'Amore 	/*
3254bb7efa7SGarrett D'Amore 	 * PCI configuration access to figure out number of slots present.
3264bb7efa7SGarrett D'Amore 	 */
3274bb7efa7SGarrett D'Amore 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
3284bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "pci_config_setup failed");
3294bb7efa7SGarrett D'Amore 		goto failed;
3304bb7efa7SGarrett D'Amore 	}
3314bb7efa7SGarrett D'Amore 
3324bb7efa7SGarrett D'Amore 	slotinfo = pci_config_get8(pcih, SLOTINFO);
3334bb7efa7SGarrett D'Amore 	shp->sh_numslots = SLOTINFO_NSLOT(slotinfo);
3344bb7efa7SGarrett D'Amore 
3354bb7efa7SGarrett D'Amore 	if (shp->sh_numslots > SDHOST_MAXSLOTS) {
3364bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Host reports to have too many slots: %d",
3374bb7efa7SGarrett D'Amore 		    shp->sh_numslots);
338c8ca7eb8SGarrett D'Amore 		pci_config_teardown(&pcih);
3394bb7efa7SGarrett D'Amore 		goto failed;
3404bb7efa7SGarrett D'Amore 	}
3414bb7efa7SGarrett D'Amore 
3424bb7efa7SGarrett D'Amore 	/*
3434bb7efa7SGarrett D'Amore 	 * Enable master accesses and DMA.
3444bb7efa7SGarrett D'Amore 	 */
3454bb7efa7SGarrett D'Amore 	pci_config_put16(pcih, PCI_CONF_COMM,
3464bb7efa7SGarrett D'Amore 	    pci_config_get16(pcih, PCI_CONF_COMM) |
3474bb7efa7SGarrett D'Amore 	    PCI_COMM_MAE | PCI_COMM_ME);
3484bb7efa7SGarrett D'Amore 
3494bb7efa7SGarrett D'Amore 	/*
3504bb7efa7SGarrett D'Amore 	 * Figure out which BAR to use.  Note that we number BARs from
3514bb7efa7SGarrett D'Amore 	 * 1, although PCI and SD Host numbers from 0.  (We number
3524bb7efa7SGarrett D'Amore 	 * from 1, because register number 0 means PCI configuration
3534bb7efa7SGarrett D'Amore 	 * space in Solaris.)
3544bb7efa7SGarrett D'Amore 	 */
3554bb7efa7SGarrett D'Amore 	bar = SLOTINFO_BAR(slotinfo) + 1;
3564bb7efa7SGarrett D'Amore 
3574bb7efa7SGarrett D'Amore 	pci_config_teardown(&pcih);
3584bb7efa7SGarrett D'Amore 
3594bb7efa7SGarrett D'Amore 	/*
3604bb7efa7SGarrett D'Amore 	 * Setup interrupts ... supports the new DDI interrupt API.  This
3614bb7efa7SGarrett D'Amore 	 * will support MSI or MSI-X interrupts if a device is found to
3624bb7efa7SGarrett D'Amore 	 * support it.
3634bb7efa7SGarrett D'Amore 	 */
3644bb7efa7SGarrett D'Amore 	if (sdhost_setup_intr(dip, shp) != DDI_SUCCESS) {
3654bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Failed to setup interrupts");
3664bb7efa7SGarrett D'Amore 		goto failed;
3674bb7efa7SGarrett D'Amore 	}
3684bb7efa7SGarrett D'Amore 
3694bb7efa7SGarrett D'Amore 	shp->sh_host = sda_host_alloc(dip, shp->sh_numslots, &sdhost_ops,
370*3f7d54a6SGarrett D'Amore 	    NULL);
3714bb7efa7SGarrett D'Amore 	if (shp->sh_host == NULL) {
3724bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Failed allocating SD host structure");
3734bb7efa7SGarrett D'Amore 		goto failed;
3744bb7efa7SGarrett D'Amore 	}
3754bb7efa7SGarrett D'Amore 
3764bb7efa7SGarrett D'Amore 	/*
3774bb7efa7SGarrett D'Amore 	 * Configure slots, this also maps registers, enables
3784bb7efa7SGarrett D'Amore 	 * interrupts, etc.  Most of the hardware setup is done here.
3794bb7efa7SGarrett D'Amore 	 */
3804bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++) {
3814bb7efa7SGarrett D'Amore 		if (sdhost_init_slot(dip, shp, i, bar + i) != DDI_SUCCESS) {
3824bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "Failed initializing slot %d", i);
3834bb7efa7SGarrett D'Amore 			goto failed;
3844bb7efa7SGarrett D'Amore 		}
3854bb7efa7SGarrett D'Amore 	}
3864bb7efa7SGarrett D'Amore 
3874bb7efa7SGarrett D'Amore 	ddi_report_dev(dip);
3884bb7efa7SGarrett D'Amore 
3894bb7efa7SGarrett D'Amore 	/*
3904bb7efa7SGarrett D'Amore 	 * Enable device interrupts at the DDI layer.
3914bb7efa7SGarrett D'Amore 	 */
392f2b90c3cSGarrett D'Amore 	if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
393f2b90c3cSGarrett D'Amore 		rv = ddi_intr_block_enable(&shp->sh_ihandle, 1);
394f2b90c3cSGarrett D'Amore 	} else {
395f2b90c3cSGarrett D'Amore 		rv = ddi_intr_enable(shp->sh_ihandle);
396f2b90c3cSGarrett D'Amore 	}
397f2b90c3cSGarrett D'Amore 	if (rv != DDI_SUCCESS) {
398f2b90c3cSGarrett D'Amore 		cmn_err(CE_WARN, "Failed enabling interrupts");
399f2b90c3cSGarrett D'Amore 		goto failed;
400f2b90c3cSGarrett D'Amore 	}
4014bb7efa7SGarrett D'Amore 
4024bb7efa7SGarrett D'Amore 	/*
4034bb7efa7SGarrett D'Amore 	 * Mark the slots online with the framework.  This will cause
4044bb7efa7SGarrett D'Amore 	 * the framework to probe them for the presence of cards.
4054bb7efa7SGarrett D'Amore 	 */
4064bb7efa7SGarrett D'Amore 	if (sda_host_attach(shp->sh_host) != DDI_SUCCESS) {
4074bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "Failed attaching to SDA framework");
408f2b90c3cSGarrett D'Amore 		if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
409f2b90c3cSGarrett D'Amore 			(void) ddi_intr_block_disable(&shp->sh_ihandle, 1);
410f2b90c3cSGarrett D'Amore 		} else {
411f2b90c3cSGarrett D'Amore 			(void) ddi_intr_disable(shp->sh_ihandle);
412f2b90c3cSGarrett D'Amore 		}
4134bb7efa7SGarrett D'Amore 		goto failed;
4144bb7efa7SGarrett D'Amore 	}
4154bb7efa7SGarrett D'Amore 
4164bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
4174bb7efa7SGarrett D'Amore 
4184bb7efa7SGarrett D'Amore failed:
4194bb7efa7SGarrett D'Amore 	if (shp->sh_ihandle != NULL) {
4204bb7efa7SGarrett D'Amore 		(void) ddi_intr_remove_handler(shp->sh_ihandle);
4214bb7efa7SGarrett D'Amore 		(void) ddi_intr_free(shp->sh_ihandle);
4224bb7efa7SGarrett D'Amore 	}
4234bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++)
4244bb7efa7SGarrett D'Amore 		sdhost_uninit_slot(shp, i);
425c8ca7eb8SGarrett D'Amore 	if (shp->sh_host != NULL)
426c8ca7eb8SGarrett D'Amore 		sda_host_free(shp->sh_host);
4274bb7efa7SGarrett D'Amore 	kmem_free(shp, sizeof (*shp));
4284bb7efa7SGarrett D'Amore 
4294bb7efa7SGarrett D'Amore 	return (DDI_FAILURE);
4304bb7efa7SGarrett D'Amore }
4314bb7efa7SGarrett D'Amore 
4324bb7efa7SGarrett D'Amore int
sdhost_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4334bb7efa7SGarrett D'Amore sdhost_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4344bb7efa7SGarrett D'Amore {
4354bb7efa7SGarrett D'Amore 	sdhost_t	*shp;
4364bb7efa7SGarrett D'Amore 	int		i;
4374bb7efa7SGarrett D'Amore 
4384bb7efa7SGarrett D'Amore 	switch (cmd) {
4394bb7efa7SGarrett D'Amore 	case DDI_DETACH:
4404bb7efa7SGarrett D'Amore 		break;
4414bb7efa7SGarrett D'Amore 
4424bb7efa7SGarrett D'Amore 	case DDI_SUSPEND:
4434bb7efa7SGarrett D'Amore 		return (sdhost_suspend(dip));
4444bb7efa7SGarrett D'Amore 
4454bb7efa7SGarrett D'Amore 	default:
4464bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
4474bb7efa7SGarrett D'Amore 	}
4484bb7efa7SGarrett D'Amore 
4494bb7efa7SGarrett D'Amore 	shp = ddi_get_driver_private(dip);
4504bb7efa7SGarrett D'Amore 
4514bb7efa7SGarrett D'Amore 	/*
4524bb7efa7SGarrett D'Amore 	 * Take host offline with the framework.
4534bb7efa7SGarrett D'Amore 	 */
4544bb7efa7SGarrett D'Amore 	sda_host_detach(shp->sh_host);
4554bb7efa7SGarrett D'Amore 
4564bb7efa7SGarrett D'Amore 	/*
4574bb7efa7SGarrett D'Amore 	 * Tear down interrupts.
4584bb7efa7SGarrett D'Amore 	 */
4594bb7efa7SGarrett D'Amore 	if (shp->sh_ihandle != NULL) {
460f2b90c3cSGarrett D'Amore 		if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) {
461f2b90c3cSGarrett D'Amore 			(void) ddi_intr_block_disable(&shp->sh_ihandle, 1);
462f2b90c3cSGarrett D'Amore 		} else {
463f2b90c3cSGarrett D'Amore 			(void) ddi_intr_disable(shp->sh_ihandle);
464f2b90c3cSGarrett D'Amore 		}
4654bb7efa7SGarrett D'Amore 		(void) ddi_intr_remove_handler(shp->sh_ihandle);
4664bb7efa7SGarrett D'Amore 		(void) ddi_intr_free(shp->sh_ihandle);
4674bb7efa7SGarrett D'Amore 	}
4684bb7efa7SGarrett D'Amore 
4694bb7efa7SGarrett D'Amore 	/*
4704bb7efa7SGarrett D'Amore 	 * Tear down register mappings, etc.
4714bb7efa7SGarrett D'Amore 	 */
4724bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++)
4734bb7efa7SGarrett D'Amore 		sdhost_uninit_slot(shp, i);
474c8ca7eb8SGarrett D'Amore 	sda_host_free(shp->sh_host);
4754bb7efa7SGarrett D'Amore 	kmem_free(shp, sizeof (*shp));
4764bb7efa7SGarrett D'Amore 
4774bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
4784bb7efa7SGarrett D'Amore }
4794bb7efa7SGarrett D'Amore 
480f2b90c3cSGarrett D'Amore int
sdhost_quiesce(dev_info_t * dip)481f2b90c3cSGarrett D'Amore sdhost_quiesce(dev_info_t *dip)
482f2b90c3cSGarrett D'Amore {
483f2b90c3cSGarrett D'Amore 	sdhost_t	*shp;
484f2b90c3cSGarrett D'Amore 	sdslot_t	*ss;
485f2b90c3cSGarrett D'Amore 
486f2b90c3cSGarrett D'Amore 	shp = ddi_get_driver_private(dip);
487f2b90c3cSGarrett D'Amore 
488f2b90c3cSGarrett D'Amore 	/* reset each slot separately */
489f2b90c3cSGarrett D'Amore 	for (int i = 0; i < shp->sh_numslots; i++) {
490f2b90c3cSGarrett D'Amore 		ss = &shp->sh_slots[i];
491f2b90c3cSGarrett D'Amore 		if (ss->ss_acch == NULL)
492f2b90c3cSGarrett D'Amore 			continue;
493f2b90c3cSGarrett D'Amore 
494f2b90c3cSGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
495f2b90c3cSGarrett D'Amore 	}
496f2b90c3cSGarrett D'Amore 	return (DDI_SUCCESS);
497f2b90c3cSGarrett D'Amore }
498f2b90c3cSGarrett D'Amore 
4994bb7efa7SGarrett D'Amore int
sdhost_suspend(dev_info_t * dip)5004bb7efa7SGarrett D'Amore sdhost_suspend(dev_info_t *dip)
5014bb7efa7SGarrett D'Amore {
5024bb7efa7SGarrett D'Amore 	sdhost_t	*shp;
5034bb7efa7SGarrett D'Amore 	sdslot_t	*ss;
5044bb7efa7SGarrett D'Amore 	int		i;
5054bb7efa7SGarrett D'Amore 
5064bb7efa7SGarrett D'Amore 	shp = ddi_get_driver_private(dip);
5074bb7efa7SGarrett D'Amore 
508f2b90c3cSGarrett D'Amore 	sda_host_suspend(shp->sh_host);
5094bb7efa7SGarrett D'Amore 
5104bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++) {
5114bb7efa7SGarrett D'Amore 		ss = &shp->sh_slots[i];
5124bb7efa7SGarrett D'Amore 		mutex_enter(&ss->ss_lock);
5134bb7efa7SGarrett D'Amore 		ss->ss_suspended = B_TRUE;
5144bb7efa7SGarrett D'Amore 		sdhost_disable_interrupts(ss);
5154bb7efa7SGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
5164bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
5174bb7efa7SGarrett D'Amore 	}
5184bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
5194bb7efa7SGarrett D'Amore }
5204bb7efa7SGarrett D'Amore 
5214bb7efa7SGarrett D'Amore int
sdhost_resume(dev_info_t * dip)5224bb7efa7SGarrett D'Amore sdhost_resume(dev_info_t *dip)
5234bb7efa7SGarrett D'Amore {
5244bb7efa7SGarrett D'Amore 	sdhost_t	*shp;
5254bb7efa7SGarrett D'Amore 	sdslot_t	*ss;
5264bb7efa7SGarrett D'Amore 	int		i;
5274bb7efa7SGarrett D'Amore 
5284bb7efa7SGarrett D'Amore 	shp = ddi_get_driver_private(dip);
5294bb7efa7SGarrett D'Amore 
5304bb7efa7SGarrett D'Amore 	for (i = 0; i < shp->sh_numslots; i++) {
5314bb7efa7SGarrett D'Amore 		ss = &shp->sh_slots[i];
5324bb7efa7SGarrett D'Amore 		mutex_enter(&ss->ss_lock);
5334bb7efa7SGarrett D'Amore 		ss->ss_suspended = B_FALSE;
5344bb7efa7SGarrett D'Amore 		(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
5354bb7efa7SGarrett D'Amore 		sdhost_enable_interrupts(ss);
5364bb7efa7SGarrett D'Amore 		mutex_exit(&ss->ss_lock);
5374bb7efa7SGarrett D'Amore 	}
5384bb7efa7SGarrett D'Amore 
539f2b90c3cSGarrett D'Amore 	sda_host_resume(shp->sh_host);
5404bb7efa7SGarrett D'Amore 
5414bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
5424bb7efa7SGarrett D'Amore }
5434bb7efa7SGarrett D'Amore 
5444bb7efa7SGarrett D'Amore sda_err_t
sdhost_set_clock(sdslot_t * ss,uint32_t hz)5454bb7efa7SGarrett D'Amore sdhost_set_clock(sdslot_t *ss, uint32_t hz)
5464bb7efa7SGarrett D'Amore {
5474bb7efa7SGarrett D'Amore 	uint16_t	div;
5484bb7efa7SGarrett D'Amore 	uint32_t	val;
5494bb7efa7SGarrett D'Amore 	uint32_t	clk;
5504bb7efa7SGarrett D'Amore 	int		count;
5514bb7efa7SGarrett D'Amore 
5524bb7efa7SGarrett D'Amore 	/*
5534bb7efa7SGarrett D'Amore 	 * Shut off the clock to begin.
5544bb7efa7SGarrett D'Amore 	 */
5554bb7efa7SGarrett D'Amore 	ss->ss_cardclk = 0;
5564bb7efa7SGarrett D'Amore 	PUT16(ss, REG_CLOCK_CONTROL, 0);
5574bb7efa7SGarrett D'Amore 	if (hz == 0) {
5584bb7efa7SGarrett D'Amore 		return (SDA_EOK);
5594bb7efa7SGarrett D'Amore 	}
5604bb7efa7SGarrett D'Amore 
5614bb7efa7SGarrett D'Amore 	if (ss->ss_baseclk == 0) {
5624bb7efa7SGarrett D'Amore 		sda_host_log(ss->ss_host, ss->ss_num,
5634bb7efa7SGarrett D'Amore 		    "Base clock frequency not established.");
5644bb7efa7SGarrett D'Amore 		return (SDA_EINVAL);
5654bb7efa7SGarrett D'Amore 	}
5664bb7efa7SGarrett D'Amore 
5674bb7efa7SGarrett D'Amore 	if ((hz > 25000000) && ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0)) {
5684bb7efa7SGarrett D'Amore 		/* this clock requires high speed timings! */
5694bb7efa7SGarrett D'Amore 		SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
5704bb7efa7SGarrett D'Amore 	} else {
5714bb7efa7SGarrett D'Amore 		/* don't allow clock to run faster than 25MHz */
5724bb7efa7SGarrett D'Amore 		hz = min(hz, 25000000);
5734bb7efa7SGarrett D'Amore 		CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
5744bb7efa7SGarrett D'Amore 	}
5754bb7efa7SGarrett D'Amore 
5764bb7efa7SGarrett D'Amore 	/* figure out the divider */
5774bb7efa7SGarrett D'Amore 	clk = ss->ss_baseclk;
5784bb7efa7SGarrett D'Amore 	div  = 1;
5794bb7efa7SGarrett D'Amore 	while (clk > hz) {
5804bb7efa7SGarrett D'Amore 		if (div > 0x80)
5814bb7efa7SGarrett D'Amore 			break;
5824bb7efa7SGarrett D'Amore 		clk >>= 1;	/* divide clock by two */
5834bb7efa7SGarrett D'Amore 		div <<= 1;	/* divider goes up by one */
5844bb7efa7SGarrett D'Amore 	}
5854bb7efa7SGarrett D'Amore 	div >>= 1;	/* 0 == divide by 1, 1 = divide by 2 */
5864bb7efa7SGarrett D'Amore 
5874bb7efa7SGarrett D'Amore 	/*
5884bb7efa7SGarrett D'Amore 	 * Set the internal clock divider first, without enabling the
5894bb7efa7SGarrett D'Amore 	 * card clock yet.
5904bb7efa7SGarrett D'Amore 	 */
5914bb7efa7SGarrett D'Amore 	PUT16(ss, REG_CLOCK_CONTROL,
5924bb7efa7SGarrett D'Amore 	    (div << CLOCK_CONTROL_FREQ_SHIFT) | CLOCK_CONTROL_INT_CLOCK_EN);
5934bb7efa7SGarrett D'Amore 
5944bb7efa7SGarrett D'Amore 	/*
5954bb7efa7SGarrett D'Amore 	 * Wait up to 100 msec for the internal clock to stabilize.
5964bb7efa7SGarrett D'Amore 	 * (The spec does not seem to indicate a maximum timeout, but
5974bb7efa7SGarrett D'Amore 	 * it also suggests that an infinite loop be used, which is
5984bb7efa7SGarrett D'Amore 	 * not appropriate for hardened Solaris drivers.)
5994bb7efa7SGarrett D'Amore 	 */
6004bb7efa7SGarrett D'Amore 	for (count = 100000; count; count -= 10) {
6014bb7efa7SGarrett D'Amore 
6024bb7efa7SGarrett D'Amore 		val = GET16(ss, REG_CLOCK_CONTROL);
6034bb7efa7SGarrett D'Amore 
6044bb7efa7SGarrett D'Amore 		if (val & CLOCK_CONTROL_INT_CLOCK_STABLE) {
6054bb7efa7SGarrett D'Amore 			/* if clock is stable, enable the SD clock pin */
6064bb7efa7SGarrett D'Amore 			PUT16(ss, REG_CLOCK_CONTROL, val |
6074bb7efa7SGarrett D'Amore 			    CLOCK_CONTROL_SD_CLOCK_EN);
6084bb7efa7SGarrett D'Amore 
6094bb7efa7SGarrett D'Amore 			ss->ss_cardclk = clk;
6104bb7efa7SGarrett D'Amore 			return (SDA_EOK);
6114bb7efa7SGarrett D'Amore 		}
6124bb7efa7SGarrett D'Amore 
6134bb7efa7SGarrett D'Amore 		drv_usecwait(10);
6144bb7efa7SGarrett D'Amore 	}
6154bb7efa7SGarrett D'Amore 
6164bb7efa7SGarrett D'Amore 	return (SDA_ETIME);
6174bb7efa7SGarrett D'Amore }
6184bb7efa7SGarrett D'Amore 
6194bb7efa7SGarrett D'Amore sda_err_t
sdhost_soft_reset(sdslot_t * ss,uint8_t bits)6204bb7efa7SGarrett D'Amore sdhost_soft_reset(sdslot_t *ss, uint8_t bits)
6214bb7efa7SGarrett D'Amore {
6224bb7efa7SGarrett D'Amore 	int	count;
6234bb7efa7SGarrett D'Amore 
6244bb7efa7SGarrett D'Amore 	/*
6254bb7efa7SGarrett D'Amore 	 * There appears to be a bug where Ricoh hosts might have a
6264bb7efa7SGarrett D'Amore 	 * problem if the host frequency is not set.  If the card
6274bb7efa7SGarrett D'Amore 	 * isn't present, or we are doing a master reset, just enable
6284bb7efa7SGarrett D'Amore 	 * the internal clock at its native speed.  (No dividers, and
6294bb7efa7SGarrett D'Amore 	 * not exposed to card.).
6304bb7efa7SGarrett D'Amore 	 */
6314bb7efa7SGarrett D'Amore 	if ((bits == SOFT_RESET_ALL) || !(CHECK_STATE(ss, CARD_INSERTED))) {
6324bb7efa7SGarrett D'Amore 		PUT16(ss, REG_CLOCK_CONTROL, CLOCK_CONTROL_INT_CLOCK_EN);
6334bb7efa7SGarrett D'Amore 		/* simple 1msec wait, don't wait for clock to stabilize */
6344bb7efa7SGarrett D'Amore 		drv_usecwait(1000);
6359a5113a6SGarrett D'Amore 		/*
6369a5113a6SGarrett D'Amore 		 * reset the card clock & width -- master reset also
6379a5113a6SGarrett D'Amore 		 * resets these
6389a5113a6SGarrett D'Amore 		 */
6399a5113a6SGarrett D'Amore 		ss->ss_cardclk = 0;
6409a5113a6SGarrett D'Amore 		ss->ss_width = 1;
6414bb7efa7SGarrett D'Amore 	}
6424bb7efa7SGarrett D'Amore 
6439a5113a6SGarrett D'Amore 
6444bb7efa7SGarrett D'Amore 	PUT8(ss, REG_SOFT_RESET, bits);
6454bb7efa7SGarrett D'Amore 	for (count = 100000; count != 0; count -= 10) {
6464bb7efa7SGarrett D'Amore 		if ((GET8(ss, REG_SOFT_RESET) & bits) == 0) {
6474bb7efa7SGarrett D'Amore 			return (SDA_EOK);
6484bb7efa7SGarrett D'Amore 		}
6494bb7efa7SGarrett D'Amore 		drv_usecwait(10);
6504bb7efa7SGarrett D'Amore 	}
6514bb7efa7SGarrett D'Amore 
6524bb7efa7SGarrett D'Amore 	return (SDA_ETIME);
6534bb7efa7SGarrett D'Amore }
6544bb7efa7SGarrett D'Amore 
6554bb7efa7SGarrett D'Amore void
sdhost_disable_interrupts(sdslot_t * ss)6564bb7efa7SGarrett D'Amore sdhost_disable_interrupts(sdslot_t *ss)
6574bb7efa7SGarrett D'Amore {
6584bb7efa7SGarrett D'Amore 	/* disable slot interrupts for card insert and remove */
6594bb7efa7SGarrett D'Amore 	PUT16(ss, REG_INT_MASK, 0);
6604bb7efa7SGarrett D'Amore 	PUT16(ss, REG_INT_EN, 0);
6614bb7efa7SGarrett D'Amore 
6624bb7efa7SGarrett D'Amore 	/* disable error interrupts */
6634bb7efa7SGarrett D'Amore 	PUT16(ss, REG_ERR_MASK, 0);
6644bb7efa7SGarrett D'Amore 	PUT16(ss, REG_ERR_EN, 0);
6654bb7efa7SGarrett D'Amore }
6664bb7efa7SGarrett D'Amore 
6674bb7efa7SGarrett D'Amore void
sdhost_enable_interrupts(sdslot_t * ss)6684bb7efa7SGarrett D'Amore sdhost_enable_interrupts(sdslot_t *ss)
6694bb7efa7SGarrett D'Amore {
6704bb7efa7SGarrett D'Amore 	/*
6714bb7efa7SGarrett D'Amore 	 * Note that we want to enable reading of the CMD related
6724bb7efa7SGarrett D'Amore 	 * bits, but we do not want them to generate an interrupt.
6734bb7efa7SGarrett D'Amore 	 * (The busy wait for typical CMD stuff will normally be less
6744bb7efa7SGarrett D'Amore 	 * than 10usec, so its simpler/easier to just poll.  Even in
6754bb7efa7SGarrett D'Amore 	 * the worst case of 100 kHz, the poll is at worst 2 msec.)
6764bb7efa7SGarrett D'Amore 	 */
6774bb7efa7SGarrett D'Amore 
6784bb7efa7SGarrett D'Amore 	/* enable slot interrupts for card insert and remove */
6794bb7efa7SGarrett D'Amore 	PUT16(ss, REG_INT_MASK, INT_MASK);
6804bb7efa7SGarrett D'Amore 	PUT16(ss, REG_INT_EN, INT_ENAB);
6814bb7efa7SGarrett D'Amore 
6824bb7efa7SGarrett D'Amore 	/* enable error interrupts */
6834bb7efa7SGarrett D'Amore 	PUT16(ss, REG_ERR_MASK, ERR_MASK);
6844bb7efa7SGarrett D'Amore 	PUT16(ss, REG_ERR_EN, ERR_ENAB);
6854bb7efa7SGarrett D'Amore }
6864bb7efa7SGarrett D'Amore 
6874bb7efa7SGarrett D'Amore int
sdhost_setup_intr(dev_info_t * dip,sdhost_t * shp)6884bb7efa7SGarrett D'Amore sdhost_setup_intr(dev_info_t *dip, sdhost_t *shp)
6894bb7efa7SGarrett D'Amore {
6904bb7efa7SGarrett D'Amore 	int		itypes;
6914bb7efa7SGarrett D'Amore 	int		itype;
6924bb7efa7SGarrett D'Amore 
6934bb7efa7SGarrett D'Amore 	/*
6944bb7efa7SGarrett D'Amore 	 * Set up interrupt handler.
6954bb7efa7SGarrett D'Amore 	 */
6964bb7efa7SGarrett D'Amore 	if (ddi_intr_get_supported_types(dip, &itypes) != DDI_SUCCESS) {
6974bb7efa7SGarrett D'Amore 		cmn_err(CE_WARN, "ddi_intr_get_supported_types failed");
6984bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
6994bb7efa7SGarrett D'Amore 	}
7004bb7efa7SGarrett D'Amore 
70128e61230SGarrett D'Amore 	/*
70228e61230SGarrett D'Amore 	 * It turns out that some controllers don't properly implement MSI,
70328e61230SGarrett D'Amore 	 * but advertise MSI capability in their  PCI config space.
70428e61230SGarrett D'Amore 	 *
70528e61230SGarrett D'Amore 	 * While this is really a chip-specific bug, the simplest solution
70628e61230SGarrett D'Amore 	 * is to just suppress MSI for now by default -- every device seen
70728e61230SGarrett D'Amore 	 * so far can use FIXED interrupts.
70828e61230SGarrett D'Amore 	 *
70928e61230SGarrett D'Amore 	 * We offer an override property, though, just in case someone really
71028e61230SGarrett D'Amore 	 * wants to force it.
71128e61230SGarrett D'Amore 	 *
71228e61230SGarrett D'Amore 	 * We don't do this if the FIXED type isn't supported though!
71328e61230SGarrett D'Amore 	 */
7149a5113a6SGarrett D'Amore 	if (itypes & DDI_INTR_TYPE_FIXED) {
7159a5113a6SGarrett D'Amore 		if (!PROPSET(SDHOST_PROP_ENABLE_MSI)) {
7169a5113a6SGarrett D'Amore 			itypes &= ~DDI_INTR_TYPE_MSI;
7179a5113a6SGarrett D'Amore 		}
7189a5113a6SGarrett D'Amore 		if (!PROPSET(SDHOST_PROP_ENABLE_MSIX)) {
7199a5113a6SGarrett D'Amore 			itypes &= ~DDI_INTR_TYPE_MSIX;
7209a5113a6SGarrett D'Amore 		}
72128e61230SGarrett D'Amore 	}
72228e61230SGarrett D'Amore 
7234bb7efa7SGarrett D'Amore 	/*
7244bb7efa7SGarrett D'Amore 	 * Interrupt types are bits in a mask.  We know about these ones:
7254bb7efa7SGarrett D'Amore 	 * FIXED = 1
7264bb7efa7SGarrett D'Amore 	 * MSI = 2
7274bb7efa7SGarrett D'Amore 	 * MSIX = 4
7284bb7efa7SGarrett D'Amore 	 */
7294bb7efa7SGarrett D'Amore 	for (itype = DDI_INTR_TYPE_MSIX; itype != 0; itype >>= 1) {
7304bb7efa7SGarrett D'Amore 
7314bb7efa7SGarrett D'Amore 		int			count;
7324bb7efa7SGarrett D'Amore 
7334bb7efa7SGarrett D'Amore 		if ((itypes & itype) == 0) {
7344bb7efa7SGarrett D'Amore 			/* this type is not supported on this device! */
7354bb7efa7SGarrett D'Amore 			continue;
7364bb7efa7SGarrett D'Amore 		}
7374bb7efa7SGarrett D'Amore 
7384bb7efa7SGarrett D'Amore 		if ((ddi_intr_get_nintrs(dip, itype, &count) != DDI_SUCCESS) ||
7394bb7efa7SGarrett D'Amore 		    (count == 0)) {
7404bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "ddi_intr_get_nintrs failed");
7414bb7efa7SGarrett D'Amore 			continue;
7424bb7efa7SGarrett D'Amore 		}
7434bb7efa7SGarrett D'Amore 
7444bb7efa7SGarrett D'Amore 		/*
7454bb7efa7SGarrett D'Amore 		 * We have not seen a host device with multiple
7464bb7efa7SGarrett D'Amore 		 * interrupts (one per slot?), and the spec does not
7474bb7efa7SGarrett D'Amore 		 * indicate that they exist.  But if one ever occurs,
7484bb7efa7SGarrett D'Amore 		 * we spew a warning to help future debugging/support
7494bb7efa7SGarrett D'Amore 		 * efforts.
7504bb7efa7SGarrett D'Amore 		 */
7514bb7efa7SGarrett D'Amore 		if (count > 1) {
7524bb7efa7SGarrett D'Amore 			cmn_err(CE_WARN, "Controller offers %d interrupts, "
7534bb7efa7SGarrett D'Amore 			    "but driver only supports one", count);
7544bb7efa7SGarrett D'Amore 			continue;
7554bb7efa7SGarrett D'Amore 		}
7564bb7efa7SGarrett D'Amore 
7574bb7efa7SGarrett D'Amore 		if ((ddi_intr_alloc(dip, &shp->sh_ihandle, itype, 0, 1,
7584bb7efa7SGarrett D'Amore 		    &count, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
759