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