xref: /onnv-gate/usr/src/uts/common/io/sdcard/impl/sda_slot.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 /*
267302Sgdamore@opensolaris.org  * SD card slot support.
277302Sgdamore@opensolaris.org  */
287302Sgdamore@opensolaris.org 
297302Sgdamore@opensolaris.org #include <sys/types.h>
307302Sgdamore@opensolaris.org #include <sys/cmn_err.h>
317302Sgdamore@opensolaris.org #include <sys/varargs.h>
327302Sgdamore@opensolaris.org #include <sys/ddi.h>
337302Sgdamore@opensolaris.org #include <sys/sunddi.h>
347302Sgdamore@opensolaris.org #include <sys/sdcard/sda_impl.h>
357302Sgdamore@opensolaris.org 
367302Sgdamore@opensolaris.org 
377302Sgdamore@opensolaris.org /*
387302Sgdamore@opensolaris.org  * Prototypes.
397302Sgdamore@opensolaris.org  */
407302Sgdamore@opensolaris.org 
417302Sgdamore@opensolaris.org static void sda_slot_insert(void *);
427302Sgdamore@opensolaris.org static sda_err_t sda_slot_check_response(sda_cmd_t *);
437302Sgdamore@opensolaris.org static void sda_slot_handle_detect(sda_slot_t *);
447302Sgdamore@opensolaris.org static void sda_slot_handle_transfer(sda_slot_t *, sda_err_t);
457302Sgdamore@opensolaris.org static void sda_slot_handle_fault(sda_slot_t *, sda_fault_t);
467302Sgdamore@opensolaris.org static void sda_slot_abort(sda_slot_t *, sda_err_t);
477302Sgdamore@opensolaris.org static void sda_slot_halt(sda_slot_t *);
487302Sgdamore@opensolaris.org static void sda_slot_thread(void *);
497302Sgdamore@opensolaris.org static void sda_slot_vprintf(sda_slot_t *, int, const char *, va_list);
507302Sgdamore@opensolaris.org 
517302Sgdamore@opensolaris.org /*
527302Sgdamore@opensolaris.org  * Static Variables.
537302Sgdamore@opensolaris.org  */
547302Sgdamore@opensolaris.org 
557302Sgdamore@opensolaris.org static struct {
567302Sgdamore@opensolaris.org 	sda_fault_t	fault;
577302Sgdamore@opensolaris.org 	const char	*msg;
587302Sgdamore@opensolaris.org } sda_slot_faults[] = {
597302Sgdamore@opensolaris.org 	{ SDA_FAULT_TIMEOUT,	"Data transfer timed out" },
607302Sgdamore@opensolaris.org 	{ SDA_FAULT_ACMD12,	"Auto CMD12 failure" },
617302Sgdamore@opensolaris.org 	{ SDA_FAULT_CRC7,	"CRC7 failure on CMD/DAT line" },
627302Sgdamore@opensolaris.org 	{ SDA_FAULT_PROTO,	"SD/MMC protocol signaling error" },
637302Sgdamore@opensolaris.org 	{ SDA_FAULT_INIT,	"Card initialization failure" },
647302Sgdamore@opensolaris.org 	{ SDA_FAULT_HOST,	"Internal host or slot failure" },
657302Sgdamore@opensolaris.org 	{ SDA_FAULT_CURRENT,	"Current overlimit detected" },
667302Sgdamore@opensolaris.org 	{ SDA_FAULT_RESET,	"Failed to reset slot" },
677302Sgdamore@opensolaris.org 	{ SDA_FAULT_NONE,	NULL },	/* sentinel, must be last! */
687302Sgdamore@opensolaris.org };
697302Sgdamore@opensolaris.org 
707302Sgdamore@opensolaris.org /*
717302Sgdamore@opensolaris.org  * Internal implementation.
727302Sgdamore@opensolaris.org  */
737302Sgdamore@opensolaris.org 
747302Sgdamore@opensolaris.org /*
757302Sgdamore@opensolaris.org  * These allow for recursive entry.  This is necessary to facilitate
767302Sgdamore@opensolaris.org  * simpler locking with things like the fault handler, where a caller
777302Sgdamore@opensolaris.org  * might already be "holding" the slot.
787302Sgdamore@opensolaris.org  *
797302Sgdamore@opensolaris.org  * This is modeled in part after ndi_devi_enter and ndi_devi_exit.
807302Sgdamore@opensolaris.org  */
817302Sgdamore@opensolaris.org void
sda_slot_enter(sda_slot_t * slot)827302Sgdamore@opensolaris.org sda_slot_enter(sda_slot_t *slot)
837302Sgdamore@opensolaris.org {
847302Sgdamore@opensolaris.org 	kt_did_t	self = ddi_get_kt_did();
857302Sgdamore@opensolaris.org 	mutex_enter(&slot->s_lock);
867302Sgdamore@opensolaris.org 	if (slot->s_owner == self) {
877302Sgdamore@opensolaris.org 		slot->s_circular++;
887302Sgdamore@opensolaris.org 	} else {
897302Sgdamore@opensolaris.org 		while ((slot->s_owner != 0) && (slot->s_owner != self)) {
907302Sgdamore@opensolaris.org 			cv_wait(&slot->s_cv, &slot->s_lock);
917302Sgdamore@opensolaris.org 		}
927302Sgdamore@opensolaris.org 		slot->s_owner = self;
937302Sgdamore@opensolaris.org 		slot->s_circular++;
947302Sgdamore@opensolaris.org 	}
957302Sgdamore@opensolaris.org 	mutex_exit(&slot->s_lock);
967302Sgdamore@opensolaris.org }
977302Sgdamore@opensolaris.org 
987302Sgdamore@opensolaris.org void
sda_slot_exit(sda_slot_t * slot)997302Sgdamore@opensolaris.org sda_slot_exit(sda_slot_t *slot)
1007302Sgdamore@opensolaris.org {
1017302Sgdamore@opensolaris.org 	ASSERT(sda_slot_owned(slot));
1027302Sgdamore@opensolaris.org 
1037302Sgdamore@opensolaris.org 	mutex_enter(&slot->s_lock);
1047302Sgdamore@opensolaris.org 	slot->s_circular--;
1057302Sgdamore@opensolaris.org 	if (slot->s_circular == 0) {
1067302Sgdamore@opensolaris.org 		slot->s_owner = 0;
1077302Sgdamore@opensolaris.org 		cv_broadcast(&slot->s_cv);
1087302Sgdamore@opensolaris.org 	}
1097302Sgdamore@opensolaris.org 	mutex_exit(&slot->s_lock);
1107302Sgdamore@opensolaris.org }
1117302Sgdamore@opensolaris.org 
1127302Sgdamore@opensolaris.org boolean_t
sda_slot_owned(sda_slot_t * slot)1137302Sgdamore@opensolaris.org sda_slot_owned(sda_slot_t *slot)
1147302Sgdamore@opensolaris.org {
1157302Sgdamore@opensolaris.org 	return (slot->s_owner == ddi_get_kt_did());
1167302Sgdamore@opensolaris.org }
1177302Sgdamore@opensolaris.org 
1187302Sgdamore@opensolaris.org sda_err_t
sda_slot_check_response(sda_cmd_t * cmdp)1197302Sgdamore@opensolaris.org sda_slot_check_response(sda_cmd_t *cmdp)
1207302Sgdamore@opensolaris.org {
1217302Sgdamore@opensolaris.org 	uint32_t	errs;
1227302Sgdamore@opensolaris.org 	switch (cmdp->sc_rtype & 0xf) {
1237302Sgdamore@opensolaris.org 	case R1:
1247302Sgdamore@opensolaris.org 		if ((errs = (cmdp->sc_response[0] & R1_ERRS)) != 0) {
1257302Sgdamore@opensolaris.org 			if (errs & (R1_WP_VIOLATION | R1_CSD_OVERWRITE)) {
1267302Sgdamore@opensolaris.org 				return (SDA_EWPROTECT);
1277302Sgdamore@opensolaris.org 			}
1287302Sgdamore@opensolaris.org 			if (errs & (R1_ADDRESS_ERROR | R1_BLOCK_LEN_ERROR |
1297302Sgdamore@opensolaris.org 			    R1_OUT_OF_RANGE | R1_ERASE_PARAM)) {
1307302Sgdamore@opensolaris.org 				return (SDA_EINVAL);
1317302Sgdamore@opensolaris.org 			}
1327302Sgdamore@opensolaris.org 			return (SDA_EIO);
1337302Sgdamore@opensolaris.org 		}
1347302Sgdamore@opensolaris.org 		break;
1357302Sgdamore@opensolaris.org 	case R5:
1367302Sgdamore@opensolaris.org 		if ((errs = (cmdp->sc_response[0] & R5_ERRS)) != 0) {
1377302Sgdamore@opensolaris.org 			return (SDA_EIO);
1387302Sgdamore@opensolaris.org 		}
1397302Sgdamore@opensolaris.org 		break;
1407302Sgdamore@opensolaris.org 	}
1417302Sgdamore@opensolaris.org 	return (SDA_EOK);
1427302Sgdamore@opensolaris.org }
1437302Sgdamore@opensolaris.org 
1447302Sgdamore@opensolaris.org void
sda_slot_halt(sda_slot_t * slot)1457302Sgdamore@opensolaris.org sda_slot_halt(sda_slot_t *slot)
1467302Sgdamore@opensolaris.org {
1477302Sgdamore@opensolaris.org 	sda_slot_enter(slot);
1487302Sgdamore@opensolaris.org 	slot->s_ops.so_halt(slot->s_prv);
1498289Sgdamore@opensolaris.org 	/* We need to wait 1 msec for power down. */
1508289Sgdamore@opensolaris.org 	drv_usecwait(1000);
1517302Sgdamore@opensolaris.org 	sda_slot_exit(slot);
1527302Sgdamore@opensolaris.org }
1537302Sgdamore@opensolaris.org 
1547302Sgdamore@opensolaris.org void
sda_slot_reset(sda_slot_t * slot)1557302Sgdamore@opensolaris.org sda_slot_reset(sda_slot_t *slot)
1567302Sgdamore@opensolaris.org {
1577302Sgdamore@opensolaris.org 	sda_slot_enter(slot);
1587302Sgdamore@opensolaris.org 	if (slot->s_ops.so_reset(slot->s_prv) != 0) {
1597302Sgdamore@opensolaris.org 		sda_slot_fault(slot, SDA_FAULT_RESET);
1607302Sgdamore@opensolaris.org 	}
1617302Sgdamore@opensolaris.org 	sda_slot_exit(slot);
1627302Sgdamore@opensolaris.org }
1637302Sgdamore@opensolaris.org 
1647302Sgdamore@opensolaris.org int
sda_slot_power_on(sda_slot_t * slot)1657302Sgdamore@opensolaris.org sda_slot_power_on(sda_slot_t *slot)
1667302Sgdamore@opensolaris.org {
1677302Sgdamore@opensolaris.org 	int		rv;
1687302Sgdamore@opensolaris.org 	uint32_t	ocr;
1697302Sgdamore@opensolaris.org 
1707302Sgdamore@opensolaris.org 	sda_slot_enter(slot);
1717302Sgdamore@opensolaris.org 
1727302Sgdamore@opensolaris.org 	/*
1737302Sgdamore@opensolaris.org 	 * Get the voltage supplied by the host.  Note that we expect
1747302Sgdamore@opensolaris.org 	 * hosts will include a range of 2.7-3.7 in their supported
1757302Sgdamore@opensolaris.org 	 * voltage ranges.  The spec does not allow for hosts that
1767302Sgdamore@opensolaris.org 	 * cannot supply a voltage in this range, yet.
1777302Sgdamore@opensolaris.org 	 */
1787302Sgdamore@opensolaris.org 	if ((rv = sda_getprop(slot, SDA_PROP_OCR, &ocr)) != 0) {
1797302Sgdamore@opensolaris.org 		sda_slot_err(slot, "Failed to get host OCR (%d)", rv);
1807302Sgdamore@opensolaris.org 		goto done;
1817302Sgdamore@opensolaris.org 	}
1827302Sgdamore@opensolaris.org 	if ((ocr & OCR_HI_MASK) == 0) {
1837302Sgdamore@opensolaris.org 		sda_slot_err(slot, "Host does not support standard voltages.");
1847302Sgdamore@opensolaris.org 		rv = ENOTSUP;
1857302Sgdamore@opensolaris.org 		goto done;
1867302Sgdamore@opensolaris.org 	}
1877302Sgdamore@opensolaris.org 
1887302Sgdamore@opensolaris.org 	/*
1897302Sgdamore@opensolaris.org 	 * We prefer 3.3V, 3.0V, and failing that, just use the
1907302Sgdamore@opensolaris.org 	 * maximum that the host supports.  3.3V is preferable,
1917302Sgdamore@opensolaris.org 	 * because it is the typical common voltage that just about
1927302Sgdamore@opensolaris.org 	 * everything supports.  Otherwise we just pick the highest
1937302Sgdamore@opensolaris.org 	 * supported voltage.  This facilitates initial power up.
1947302Sgdamore@opensolaris.org 	 */
1957302Sgdamore@opensolaris.org 	if (ocr & OCR_32_33V) {
1967302Sgdamore@opensolaris.org 		slot->s_cur_ocr = OCR_32_33V;
1977302Sgdamore@opensolaris.org 	} else if (ocr & OCR_29_30V) {
1987302Sgdamore@opensolaris.org 		slot->s_cur_ocr = OCR_29_30V;
1997302Sgdamore@opensolaris.org 	} else {
2007302Sgdamore@opensolaris.org 		slot->s_cur_ocr = (1U << (ddi_fls(ocr) - 1));
2017302Sgdamore@opensolaris.org 	}
2027302Sgdamore@opensolaris.org 
2037302Sgdamore@opensolaris.org 	/*
2047302Sgdamore@opensolaris.org 	 * Turn on the power.
2057302Sgdamore@opensolaris.org 	 */
2067302Sgdamore@opensolaris.org 	if ((rv = sda_setprop(slot, SDA_PROP_OCR, slot->s_cur_ocr)) != 0) {
2077302Sgdamore@opensolaris.org 		sda_slot_err(slot, "Failed to set OCR %x (%d)",
2087302Sgdamore@opensolaris.org 		    slot->s_cur_ocr, rv);
2097302Sgdamore@opensolaris.org 		goto done;
2107302Sgdamore@opensolaris.org 	}
2117302Sgdamore@opensolaris.org 
2127302Sgdamore@opensolaris.org 	sda_slot_exit(slot);
2137302Sgdamore@opensolaris.org 
2147302Sgdamore@opensolaris.org 	/*
2157302Sgdamore@opensolaris.org 	 * Wait 250 msec (per spec) for power ramp to complete.
2167302Sgdamore@opensolaris.org 	 */
2177302Sgdamore@opensolaris.org 	delay(drv_usectohz(250000));
2187302Sgdamore@opensolaris.org 	return (0);
2197302Sgdamore@opensolaris.org 
2207302Sgdamore@opensolaris.org done:
2217302Sgdamore@opensolaris.org 	sda_slot_exit(slot);
2227302Sgdamore@opensolaris.org 	return (rv);
2237302Sgdamore@opensolaris.org }
2247302Sgdamore@opensolaris.org 
2257302Sgdamore@opensolaris.org void
sda_slot_power_off(sda_slot_t * slot)2267302Sgdamore@opensolaris.org sda_slot_power_off(sda_slot_t *slot)
2277302Sgdamore@opensolaris.org {
2287302Sgdamore@opensolaris.org 	sda_slot_enter(slot);
2297302Sgdamore@opensolaris.org 	(void) sda_setprop(slot, SDA_PROP_OCR, 0);
2307302Sgdamore@opensolaris.org 	/* XXX: FMA: on failure this should cause a fault to be generated */
2317302Sgdamore@opensolaris.org 	/* spec requires voltage to stay low for at least 1 msec */
2327302Sgdamore@opensolaris.org 	drv_usecwait(1000);
2337302Sgdamore@opensolaris.org 	sda_slot_exit(slot);
2347302Sgdamore@opensolaris.org }
2357302Sgdamore@opensolaris.org 
2367302Sgdamore@opensolaris.org void
sda_slot_insert(void * arg)2377302Sgdamore@opensolaris.org sda_slot_insert(void *arg)
2387302Sgdamore@opensolaris.org {
2397302Sgdamore@opensolaris.org 	sda_slot_t	*slot = arg;
2407302Sgdamore@opensolaris.org 
2417302Sgdamore@opensolaris.org 	if (sda_init_card(slot) != SDA_EOK) {
2427302Sgdamore@opensolaris.org 		/*
2437302Sgdamore@opensolaris.org 		 * Remove power from the slot.  If a more severe fault
2447302Sgdamore@opensolaris.org 		 * occurred, then a manual reset with cfgadm will be needed.
2457302Sgdamore@opensolaris.org 		 */
2467302Sgdamore@opensolaris.org 		sda_slot_err(slot, "Unable to initialize card!");
2477302Sgdamore@opensolaris.org 		sda_slot_enter(slot);
2487302Sgdamore@opensolaris.org 		sda_slot_power_off(slot);
2497302Sgdamore@opensolaris.org 		sda_slot_abort(slot, SDA_ENODEV);
2507302Sgdamore@opensolaris.org 		sda_slot_exit(slot);
2517302Sgdamore@opensolaris.org 
252*12426Sgdamore@opensolaris.org 	} else if ((slot->s_flags & SLOTF_MEMORY) == 0) {
253*12426Sgdamore@opensolaris.org 		/*
254*12426Sgdamore@opensolaris.org 		 * SDIO: For SDIO, we can write the card's
255*12426Sgdamore@opensolaris.org 		 * MANFID tuple in CIS to the UUID.  Until we
256*12426Sgdamore@opensolaris.org 		 * support SDIO, we just suppress creating
257*12426Sgdamore@opensolaris.org 		 * devinfo nodes.
258*12426Sgdamore@opensolaris.org 		 */
259*12426Sgdamore@opensolaris.org 		sda_slot_err(slot, "Non-memory target not supported");
2607302Sgdamore@opensolaris.org 	} else {
261*12426Sgdamore@opensolaris.org 
262*12426Sgdamore@opensolaris.org 		sda_slot_enter(slot);
263*12426Sgdamore@opensolaris.org 		if (sda_mem_parse_cid_csd(slot) != DDI_SUCCESS) {
264*12426Sgdamore@opensolaris.org 			sda_slot_err(slot,
265*12426Sgdamore@opensolaris.org 			    "Unable to parse card identification");
266*12426Sgdamore@opensolaris.org 		} else {
267*12426Sgdamore@opensolaris.org 			slot->s_warn = B_FALSE;
268*12426Sgdamore@opensolaris.org 			slot->s_ready = B_TRUE;
269*12426Sgdamore@opensolaris.org 		}
270*12426Sgdamore@opensolaris.org 		sda_slot_exit(slot);
2717302Sgdamore@opensolaris.org 	}
2727302Sgdamore@opensolaris.org 
2737302Sgdamore@opensolaris.org 	slot->s_stamp = ddi_get_time();
2747302Sgdamore@opensolaris.org 	slot->s_intransit = 0;
275*12426Sgdamore@opensolaris.org 	bd_state_change(slot->s_bdh);
2767302Sgdamore@opensolaris.org }
2777302Sgdamore@opensolaris.org 
2787302Sgdamore@opensolaris.org void
sda_slot_abort(sda_slot_t * slot,sda_err_t errno)2797302Sgdamore@opensolaris.org sda_slot_abort(sda_slot_t *slot, sda_err_t errno)
2807302Sgdamore@opensolaris.org {
2817302Sgdamore@opensolaris.org 	sda_cmd_t	*cmdp;
2827302Sgdamore@opensolaris.org 
2837302Sgdamore@opensolaris.org 	ASSERT(sda_slot_owned(slot));
2847302Sgdamore@opensolaris.org 
2857302Sgdamore@opensolaris.org 	if ((cmdp = slot->s_xfrp) != NULL) {
2867302Sgdamore@opensolaris.org 		slot->s_xfrp = NULL;
287*12426Sgdamore@opensolaris.org 		sda_cmd_notify(cmdp, 0, errno);
288*12426Sgdamore@opensolaris.org 		list_insert_tail(&slot->s_abortlist, cmdp);
2897302Sgdamore@opensolaris.org 	}
2907302Sgdamore@opensolaris.org 	while ((cmdp = list_head(&slot->s_cmdlist)) != NULL) {
2917302Sgdamore@opensolaris.org 		list_remove(&slot->s_cmdlist, cmdp);
2927302Sgdamore@opensolaris.org 		sda_cmd_notify(cmdp, 0, errno);
2937302Sgdamore@opensolaris.org 		list_insert_tail(&slot->s_abortlist, cmdp);
2947302Sgdamore@opensolaris.org 	}
2957302Sgdamore@opensolaris.org 
2967302Sgdamore@opensolaris.org 	sda_slot_wakeup(slot);
2977302Sgdamore@opensolaris.org }
2987302Sgdamore@opensolaris.org 
2997302Sgdamore@opensolaris.org void
sda_slot_handle_transfer(sda_slot_t * slot,sda_err_t errno)3007302Sgdamore@opensolaris.org sda_slot_handle_transfer(sda_slot_t *slot, sda_err_t errno)
3017302Sgdamore@opensolaris.org {
3027302Sgdamore@opensolaris.org 	sda_cmd_t	*cmdp;
3037302Sgdamore@opensolaris.org 
3047302Sgdamore@opensolaris.org 	sda_slot_enter(slot);
3057302Sgdamore@opensolaris.org 
3067302Sgdamore@opensolaris.org 	if ((cmdp = slot->s_xfrp) != NULL) {
3077302Sgdamore@opensolaris.org 
3087302Sgdamore@opensolaris.org 		slot->s_xfrp = NULL;
3097302Sgdamore@opensolaris.org 		slot->s_xfrtmo = 0;
3107302Sgdamore@opensolaris.org 		(void) sda_setprop(slot, SDA_PROP_LED, 0);
3117302Sgdamore@opensolaris.org 		sda_slot_exit(slot);
3127302Sgdamore@opensolaris.org 
3137302Sgdamore@opensolaris.org 		sda_slot_wakeup(slot);
3147302Sgdamore@opensolaris.org 
3157302Sgdamore@opensolaris.org 		sda_cmd_notify(cmdp, SDA_CMDF_DAT, errno);
3167302Sgdamore@opensolaris.org 	} else {
3177302Sgdamore@opensolaris.org 		sda_slot_exit(slot);
3187302Sgdamore@opensolaris.org 	}
3197302Sgdamore@opensolaris.org }
3207302Sgdamore@opensolaris.org 
3217302Sgdamore@opensolaris.org void
sda_slot_handle_fault(sda_slot_t * slot,sda_fault_t fault)3227302Sgdamore@opensolaris.org sda_slot_handle_fault(sda_slot_t *slot, sda_fault_t fault)
3237302Sgdamore@opensolaris.org {
3247302Sgdamore@opensolaris.org 	const char	*msg;
3257302Sgdamore@opensolaris.org 	int		i;
3267302Sgdamore@opensolaris.org 
3277302Sgdamore@opensolaris.org 	sda_slot_enter(slot);
3287302Sgdamore@opensolaris.org 
3297302Sgdamore@opensolaris.org 	if ((fault == SDA_FAULT_TIMEOUT) && (slot->s_init)) {
3307302Sgdamore@opensolaris.org 		/*
3317302Sgdamore@opensolaris.org 		 * Timeouts during initialization are quite normal.
3327302Sgdamore@opensolaris.org 		 */
3337302Sgdamore@opensolaris.org 		sda_slot_exit(slot);
3347302Sgdamore@opensolaris.org 		return;
3357302Sgdamore@opensolaris.org 	}
3367302Sgdamore@opensolaris.org 
3377302Sgdamore@opensolaris.org 	slot->s_failed = B_TRUE;
3387302Sgdamore@opensolaris.org 	sda_slot_abort(slot, SDA_EFAULT);
3397302Sgdamore@opensolaris.org 
3407302Sgdamore@opensolaris.org 	msg = "Unknown fault (%d)";
3417302Sgdamore@opensolaris.org 	for (i = 0; sda_slot_faults[i].msg != NULL; i++) {
3427302Sgdamore@opensolaris.org 		if (sda_slot_faults[i].fault == fault) {
3437302Sgdamore@opensolaris.org 			msg = sda_slot_faults[i].msg;
3447302Sgdamore@opensolaris.org 			break;
3457302Sgdamore@opensolaris.org 		}
3467302Sgdamore@opensolaris.org 	}
3477302Sgdamore@opensolaris.org 
3487302Sgdamore@opensolaris.org 	/*
3497302Sgdamore@opensolaris.org 	 * FMA would be a better choice here.
3507302Sgdamore@opensolaris.org 	 */
3517302Sgdamore@opensolaris.org 	sda_slot_err(slot, msg, fault);
3527302Sgdamore@opensolaris.org 
3537302Sgdamore@opensolaris.org 	/*
3547302Sgdamore@opensolaris.org 	 * Shut down the slot.  Interaction from userland via cfgadm
3557302Sgdamore@opensolaris.org 	 * can revive it.
3567302Sgdamore@opensolaris.org 	 *
3577302Sgdamore@opensolaris.org 	 * FMA can help here.
3587302Sgdamore@opensolaris.org 	 */
3597302Sgdamore@opensolaris.org 	sda_slot_halt(slot);
3607302Sgdamore@opensolaris.org 
3617302Sgdamore@opensolaris.org 	sda_slot_exit(slot);
3627302Sgdamore@opensolaris.org }
3637302Sgdamore@opensolaris.org 
3647302Sgdamore@opensolaris.org void
sda_slot_handle_detect(sda_slot_t * slot)3657302Sgdamore@opensolaris.org sda_slot_handle_detect(sda_slot_t *slot)
3667302Sgdamore@opensolaris.org {
3677302Sgdamore@opensolaris.org 	uint32_t	inserted;
3687302Sgdamore@opensolaris.org 
3697302Sgdamore@opensolaris.org 	sda_slot_enter(slot);
3707302Sgdamore@opensolaris.org 
3717302Sgdamore@opensolaris.org 	slot->s_stamp = ddi_get_time();
3727302Sgdamore@opensolaris.org 	slot->s_intransit = 1;
3737302Sgdamore@opensolaris.org 	slot->s_flags = 0;
3747302Sgdamore@opensolaris.org 	slot->s_rca = 0;
3757302Sgdamore@opensolaris.org 	slot->s_ready = B_FALSE;
3767302Sgdamore@opensolaris.org 
3777302Sgdamore@opensolaris.org 	sda_getprop(slot, SDA_PROP_INSERTED, &inserted);
3787302Sgdamore@opensolaris.org 	slot->s_inserted = (inserted != 0);
3797302Sgdamore@opensolaris.org 
3807302Sgdamore@opensolaris.org 	if (slot->s_inserted && !slot->s_failed) {
3817302Sgdamore@opensolaris.org 		/*
3827302Sgdamore@opensolaris.org 		 * We need to initialize the card, so we only support
3837302Sgdamore@opensolaris.org 		 * hipri commands for now.
3847302Sgdamore@opensolaris.org 		 */
3857302Sgdamore@opensolaris.org 		slot->s_init = B_TRUE;
3867302Sgdamore@opensolaris.org 
3877302Sgdamore@opensolaris.org 		/*
3887302Sgdamore@opensolaris.org 		 * Card insertion occurred.  We have to run this on
3897302Sgdamore@opensolaris.org 		 * another task, to avoid deadlock as the task may
3907302Sgdamore@opensolaris.org 		 * need to dispatch commands.
3917302Sgdamore@opensolaris.org 		 */
3928289Sgdamore@opensolaris.org 		(void) ddi_taskq_dispatch(slot->s_hp_tq, sda_slot_insert, slot,
3937302Sgdamore@opensolaris.org 		    DDI_SLEEP);
3947302Sgdamore@opensolaris.org 	} else {
3957302Sgdamore@opensolaris.org 
3967302Sgdamore@opensolaris.org 		/*
3977302Sgdamore@opensolaris.org 		 * Nuke in-flight commands.
3987302Sgdamore@opensolaris.org 		 */
3997302Sgdamore@opensolaris.org 		sda_slot_abort(slot, SDA_ENODEV);
4007302Sgdamore@opensolaris.org 
4017302Sgdamore@opensolaris.org 		/*
4027302Sgdamore@opensolaris.org 		 * Restart the slot (incl. power cycle).  This gets the
4037302Sgdamore@opensolaris.org 		 * slot to a known good state.
4047302Sgdamore@opensolaris.org 		 */
4057302Sgdamore@opensolaris.org 		sda_slot_reset(slot);
4067302Sgdamore@opensolaris.org 
4077302Sgdamore@opensolaris.org 		slot->s_intransit = 0;
408*12426Sgdamore@opensolaris.org 		bd_state_change(slot->s_bdh);
4097302Sgdamore@opensolaris.org 	}
4107302Sgdamore@opensolaris.org 	sda_slot_exit(slot);
4117302Sgdamore@opensolaris.org 
4127302Sgdamore@opensolaris.org 	sda_slot_wakeup(slot);
4137302Sgdamore@opensolaris.org }
4147302Sgdamore@opensolaris.org 
4157302Sgdamore@opensolaris.org void
sda_slot_transfer(sda_slot_t * slot,sda_err_t errno)4167302Sgdamore@opensolaris.org sda_slot_transfer(sda_slot_t *slot, sda_err_t errno)
4177302Sgdamore@opensolaris.org {
4187302Sgdamore@opensolaris.org 	mutex_enter(&slot->s_evlock);
4197302Sgdamore@opensolaris.org 	slot->s_errno = errno;
4207302Sgdamore@opensolaris.org 	slot->s_xfrdone = B_TRUE;
4217302Sgdamore@opensolaris.org 	cv_broadcast(&slot->s_evcv);
4227302Sgdamore@opensolaris.org 	mutex_exit(&slot->s_evlock);
4237302Sgdamore@opensolaris.org }
4247302Sgdamore@opensolaris.org 
4257302Sgdamore@opensolaris.org void
sda_slot_detect(sda_slot_t * slot)4267302Sgdamore@opensolaris.org sda_slot_detect(sda_slot_t *slot)
4277302Sgdamore@opensolaris.org {
4287302Sgdamore@opensolaris.org 	mutex_enter(&slot->s_evlock);
4297302Sgdamore@opensolaris.org 	slot->s_detect = B_TRUE;
4307302Sgdamore@opensolaris.org 	cv_broadcast(&slot->s_evcv);
4317302Sgdamore@opensolaris.org 	mutex_exit(&slot->s_evlock);
4327302Sgdamore@opensolaris.org }
4337302Sgdamore@opensolaris.org 
4347302Sgdamore@opensolaris.org void
sda_slot_fault(sda_slot_t * slot,sda_fault_t fault)4357302Sgdamore@opensolaris.org sda_slot_fault(sda_slot_t *slot, sda_fault_t fault)
4367302Sgdamore@opensolaris.org {
4377302Sgdamore@opensolaris.org 	mutex_enter(&slot->s_evlock);
4387302Sgdamore@opensolaris.org 	slot->s_fault = fault;
4397302Sgdamore@opensolaris.org 	cv_broadcast(&slot->s_evcv);
4407302Sgdamore@opensolaris.org 	mutex_exit(&slot->s_evlock);
4417302Sgdamore@opensolaris.org }
4427302Sgdamore@opensolaris.org 
4437302Sgdamore@opensolaris.org void
sda_slot_wakeup(sda_slot_t * slot)4447302Sgdamore@opensolaris.org sda_slot_wakeup(sda_slot_t *slot)
4457302Sgdamore@opensolaris.org {
4467302Sgdamore@opensolaris.org 	mutex_enter(&slot->s_evlock);
4477302Sgdamore@opensolaris.org 	slot->s_wake = B_TRUE;
4487302Sgdamore@opensolaris.org 	cv_broadcast(&slot->s_evcv);
4497302Sgdamore@opensolaris.org 	mutex_exit(&slot->s_evlock);
4507302Sgdamore@opensolaris.org }
4517302Sgdamore@opensolaris.org 
4527302Sgdamore@opensolaris.org void
sda_slot_init(sda_slot_t * slot)4537302Sgdamore@opensolaris.org sda_slot_init(sda_slot_t *slot)
4547302Sgdamore@opensolaris.org {
4557302Sgdamore@opensolaris.org 	mutex_init(&slot->s_lock, NULL, MUTEX_DRIVER, NULL);
4567302Sgdamore@opensolaris.org 	cv_init(&slot->s_cv, NULL, CV_DRIVER, NULL);
4577302Sgdamore@opensolaris.org 	mutex_init(&slot->s_evlock, NULL, MUTEX_DRIVER, NULL);
4587302Sgdamore@opensolaris.org 	cv_init(&slot->s_evcv, NULL, CV_DRIVER, NULL);
4597302Sgdamore@opensolaris.org 
4607302Sgdamore@opensolaris.org 	sda_cmd_list_init(&slot->s_cmdlist);
4617302Sgdamore@opensolaris.org 	sda_cmd_list_init(&slot->s_abortlist);
4627302Sgdamore@opensolaris.org }
4637302Sgdamore@opensolaris.org 
4647302Sgdamore@opensolaris.org void
sda_slot_fini(sda_slot_t * slot)4657302Sgdamore@opensolaris.org sda_slot_fini(sda_slot_t *slot)
4667302Sgdamore@opensolaris.org {
4677302Sgdamore@opensolaris.org 	sda_cmd_list_fini(&slot->s_cmdlist);
4687302Sgdamore@opensolaris.org 	sda_cmd_list_fini(&slot->s_abortlist);
4697302Sgdamore@opensolaris.org 	mutex_destroy(&slot->s_lock);
4707302Sgdamore@opensolaris.org 	mutex_destroy(&slot->s_evlock);
4717302Sgdamore@opensolaris.org 	cv_destroy(&slot->s_cv);
4727302Sgdamore@opensolaris.org 	cv_destroy(&slot->s_evcv);
4737302Sgdamore@opensolaris.org }
4747302Sgdamore@opensolaris.org 
475*12426Sgdamore@opensolaris.org static bd_ops_t sda_bd_ops = {
476*12426Sgdamore@opensolaris.org 	BD_OPS_VERSION_0,
477*12426Sgdamore@opensolaris.org 	sda_mem_bd_driveinfo,
478*12426Sgdamore@opensolaris.org 	sda_mem_bd_mediainfo,
479*12426Sgdamore@opensolaris.org 	NULL,			/* devid_init */
480*12426Sgdamore@opensolaris.org 	NULL,			/* sync_cache */
481*12426Sgdamore@opensolaris.org 	sda_mem_bd_read,
482*12426Sgdamore@opensolaris.org 	sda_mem_bd_write,
483*12426Sgdamore@opensolaris.org 	NULL			/* dump */
484*12426Sgdamore@opensolaris.org };
485*12426Sgdamore@opensolaris.org 
4867302Sgdamore@opensolaris.org void
sda_slot_attach(sda_slot_t * slot)4877302Sgdamore@opensolaris.org sda_slot_attach(sda_slot_t *slot)
4887302Sgdamore@opensolaris.org {
4897686Sgdamore@opensolaris.org 	sda_host_t	*h = slot->s_hostp;
4907302Sgdamore@opensolaris.org 	char		name[16];
4917302Sgdamore@opensolaris.org 	uint32_t	cap;
4927302Sgdamore@opensolaris.org 
4937302Sgdamore@opensolaris.org 	/*
4948289Sgdamore@opensolaris.org 	 * We have two taskqs.  The first taskq is used for
4957302Sgdamore@opensolaris.org 	 * card initialization.
4967302Sgdamore@opensolaris.org 	 *
4978289Sgdamore@opensolaris.org 	 * The second is used for the main processing loop.
4987302Sgdamore@opensolaris.org 	 *
4997302Sgdamore@opensolaris.org 	 * The reason for a separate taskq is that initialization
5007302Sgdamore@opensolaris.org 	 * needs to acquire locks which may be held by the slot
5017302Sgdamore@opensolaris.org 	 * thread, or by device driver context... use of the separate
5027302Sgdamore@opensolaris.org 	 * taskq breaks the deadlock.  Additionally, the
5037302Sgdamore@opensolaris.org 	 * initialization task may need to sleep quite a while during
5047302Sgdamore@opensolaris.org 	 * card initialization.
5057302Sgdamore@opensolaris.org 	 */
5067302Sgdamore@opensolaris.org 
507*12426Sgdamore@opensolaris.org 	slot->s_bdh = bd_alloc_handle(slot, &sda_bd_ops, h->h_dma, KM_SLEEP);
508*12426Sgdamore@opensolaris.org 	ASSERT(slot->s_bdh);
509*12426Sgdamore@opensolaris.org 
5107302Sgdamore@opensolaris.org 	sda_slot_enter(slot);
5117302Sgdamore@opensolaris.org 
5128289Sgdamore@opensolaris.org 	(void) snprintf(name, sizeof (name), "slot_%d_hp_tq",
5138289Sgdamore@opensolaris.org 	    slot->s_slot_num);
5148289Sgdamore@opensolaris.org 	slot->s_hp_tq = ddi_taskq_create(h->h_dip, name, 1,
5158289Sgdamore@opensolaris.org 	    TASKQ_DEFAULTPRI, 0);
5168289Sgdamore@opensolaris.org 	if (slot->s_hp_tq == NULL) {
5177302Sgdamore@opensolaris.org 		/* Generally, this failure should never occur */
5188289Sgdamore@opensolaris.org 		sda_slot_err(slot, "Unable to create hotplug slot taskq");
5197302Sgdamore@opensolaris.org 		sda_slot_exit(slot);
520*12426Sgdamore@opensolaris.org 		bd_free_handle(slot->s_bdh);
521*12426Sgdamore@opensolaris.org 		slot->s_bdh = NULL;
5227302Sgdamore@opensolaris.org 		return;
5237302Sgdamore@opensolaris.org 	}
5247302Sgdamore@opensolaris.org 
5257302Sgdamore@opensolaris.org 	/* create the main processing thread */
5268289Sgdamore@opensolaris.org 	(void) snprintf(name, sizeof (name), "slot_%d_main_tq",
5278289Sgdamore@opensolaris.org 	    slot->s_slot_num);
5288289Sgdamore@opensolaris.org 	slot->s_main_tq = ddi_taskq_create(h->h_dip, name, 1,
5298289Sgdamore@opensolaris.org 	    TASKQ_DEFAULTPRI, 0);
5308289Sgdamore@opensolaris.org 	if (slot->s_main_tq == NULL) {
5318289Sgdamore@opensolaris.org 		/* Generally, this failure should never occur */
5328289Sgdamore@opensolaris.org 		sda_slot_err(slot, "Unable to create main slot taskq");
5338289Sgdamore@opensolaris.org 		sda_slot_exit(slot);
534*12426Sgdamore@opensolaris.org 		bd_free_handle(slot->s_bdh);
535*12426Sgdamore@opensolaris.org 		slot->s_bdh = NULL;
5368289Sgdamore@opensolaris.org 		return;
5378289Sgdamore@opensolaris.org 	}
5388289Sgdamore@opensolaris.org 	(void) ddi_taskq_dispatch(slot->s_main_tq, sda_slot_thread, slot,
5398289Sgdamore@opensolaris.org 	    DDI_SLEEP);
5407302Sgdamore@opensolaris.org 
5417302Sgdamore@opensolaris.org 	/*
5427302Sgdamore@opensolaris.org 	 * Determine slot capabilities.
5437302Sgdamore@opensolaris.org 	 */
5447302Sgdamore@opensolaris.org 	slot->s_caps = 0;
5457302Sgdamore@opensolaris.org 
5467302Sgdamore@opensolaris.org 	if ((sda_getprop(slot, SDA_PROP_CAP_NOPIO, &cap) == 0) && (cap != 0)) {
5477302Sgdamore@opensolaris.org 		slot->s_caps |= SLOT_CAP_NOPIO;
5487302Sgdamore@opensolaris.org 	}
5497302Sgdamore@opensolaris.org 	if ((sda_getprop(slot, SDA_PROP_CAP_4BITS, &cap) == 0) && (cap != 0)) {
5507302Sgdamore@opensolaris.org 		slot->s_caps |= SLOT_CAP_4BITS;
5517302Sgdamore@opensolaris.org 	}
5527302Sgdamore@opensolaris.org 	if ((sda_getprop(slot, SDA_PROP_CAP_HISPEED, &cap) == 0) &&
5537302Sgdamore@opensolaris.org 	    (cap != 0)) {
5547302Sgdamore@opensolaris.org 		slot->s_caps |= SLOT_CAP_HISPEED;
5557302Sgdamore@opensolaris.org 	}
5567302Sgdamore@opensolaris.org 
5577302Sgdamore@opensolaris.org 	/* make sure that the host is started up */
5587302Sgdamore@opensolaris.org 	if (slot->s_ops.so_reset(slot->s_prv) != 0) {
5597302Sgdamore@opensolaris.org 		sda_slot_fault(slot, SDA_FAULT_RESET);
5607302Sgdamore@opensolaris.org 	}
5617302Sgdamore@opensolaris.org 
5627302Sgdamore@opensolaris.org 	sda_slot_exit(slot);
563*12426Sgdamore@opensolaris.org 
564*12426Sgdamore@opensolaris.org 	(void) bd_attach_handle(h->h_dip, slot->s_bdh);
5657302Sgdamore@opensolaris.org }
5667302Sgdamore@opensolaris.org 
5677302Sgdamore@opensolaris.org void
sda_slot_detach(sda_slot_t * slot)5687302Sgdamore@opensolaris.org sda_slot_detach(sda_slot_t *slot)
5697302Sgdamore@opensolaris.org {
5707302Sgdamore@opensolaris.org 	/*
5717302Sgdamore@opensolaris.org 	 * Shut down the thread.
5727302Sgdamore@opensolaris.org 	 */
573*12426Sgdamore@opensolaris.org 	(void) bd_detach_handle(slot->s_bdh);
574*12426Sgdamore@opensolaris.org 
5758289Sgdamore@opensolaris.org 	mutex_enter(&slot->s_evlock);
5768289Sgdamore@opensolaris.org 	slot->s_detach = B_TRUE;
5778289Sgdamore@opensolaris.org 	cv_broadcast(&slot->s_evcv);
5788289Sgdamore@opensolaris.org 	mutex_exit(&slot->s_evlock);
5797302Sgdamore@opensolaris.org 
5807302Sgdamore@opensolaris.org 	/*
5818289Sgdamore@opensolaris.org 	 * Nuke the taskqs. We do this after stopping the background
5828289Sgdamore@opensolaris.org 	 * thread to avoid deadlock.
5837302Sgdamore@opensolaris.org 	 */
5848289Sgdamore@opensolaris.org 	if (slot->s_main_tq)
5858289Sgdamore@opensolaris.org 		ddi_taskq_destroy(slot->s_main_tq);
5868289Sgdamore@opensolaris.org 	if (slot->s_hp_tq)
5878289Sgdamore@opensolaris.org 		ddi_taskq_destroy(slot->s_hp_tq);
588*12426Sgdamore@opensolaris.org 
589*12426Sgdamore@opensolaris.org 	bd_free_handle(slot->s_bdh);
5908289Sgdamore@opensolaris.org }
5918289Sgdamore@opensolaris.org 
5928289Sgdamore@opensolaris.org void
sda_slot_suspend(sda_slot_t * slot)5938289Sgdamore@opensolaris.org sda_slot_suspend(sda_slot_t *slot)
5948289Sgdamore@opensolaris.org {
5958289Sgdamore@opensolaris.org 	mutex_enter(&slot->s_evlock);
5968289Sgdamore@opensolaris.org 	slot->s_suspend = B_TRUE;
5978289Sgdamore@opensolaris.org 	cv_broadcast(&slot->s_evcv);
5988289Sgdamore@opensolaris.org 	mutex_exit(&slot->s_evlock);
5998289Sgdamore@opensolaris.org 	ddi_taskq_wait(slot->s_main_tq);
6008289Sgdamore@opensolaris.org }
6018289Sgdamore@opensolaris.org 
6028289Sgdamore@opensolaris.org void
sda_slot_resume(sda_slot_t * slot)6038289Sgdamore@opensolaris.org sda_slot_resume(sda_slot_t *slot)
6048289Sgdamore@opensolaris.org {
6058289Sgdamore@opensolaris.org 	mutex_enter(&slot->s_evlock);
6068289Sgdamore@opensolaris.org 	slot->s_suspend = B_FALSE;
6078289Sgdamore@opensolaris.org 	/*
6088289Sgdamore@opensolaris.org 	 * A card change event may have occurred, and in any case we need
6098289Sgdamore@opensolaris.org 	 * to reinitialize the card.
6108289Sgdamore@opensolaris.org 	 */
6118289Sgdamore@opensolaris.org 	slot->s_detect = B_TRUE;
6128289Sgdamore@opensolaris.org 	mutex_exit(&slot->s_evlock);
6138289Sgdamore@opensolaris.org 
6148289Sgdamore@opensolaris.org 	/* Start up a new instance of the main processing task. */
6158289Sgdamore@opensolaris.org 	(void) ddi_taskq_dispatch(slot->s_main_tq, sda_slot_thread, slot,
6168289Sgdamore@opensolaris.org 	    DDI_SLEEP);
6177302Sgdamore@opensolaris.org }
6187302Sgdamore@opensolaris.org 
6197302Sgdamore@opensolaris.org void
sda_slot_thread(void * arg)6207302Sgdamore@opensolaris.org sda_slot_thread(void *arg)
6217302Sgdamore@opensolaris.org {
6227302Sgdamore@opensolaris.org 	sda_slot_t	*slot = arg;
6237302Sgdamore@opensolaris.org 
6247302Sgdamore@opensolaris.org 	for (;;) {
6257302Sgdamore@opensolaris.org 		sda_cmd_t	*cmdp;
6267302Sgdamore@opensolaris.org 		boolean_t	datline;
6277302Sgdamore@opensolaris.org 		sda_err_t	rv;
6287302Sgdamore@opensolaris.org 
6297302Sgdamore@opensolaris.org 		mutex_enter(&slot->s_evlock);
6307302Sgdamore@opensolaris.org 
6317302Sgdamore@opensolaris.org 		/*
6327302Sgdamore@opensolaris.org 		 * Process any abort list first.
6337302Sgdamore@opensolaris.org 		 */
6347302Sgdamore@opensolaris.org 		if ((cmdp = list_head(&slot->s_abortlist)) != NULL) {
6357302Sgdamore@opensolaris.org 			list_remove(&slot->s_abortlist, cmdp);
6367302Sgdamore@opensolaris.org 			mutex_exit(&slot->s_evlock);
6377302Sgdamore@opensolaris.org 			/*
6387302Sgdamore@opensolaris.org 			 * EOK used here, to avoid clobbering previous
6397302Sgdamore@opensolaris.org 			 * error code.
6407302Sgdamore@opensolaris.org 			 */
6417302Sgdamore@opensolaris.org 			sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT,
6427302Sgdamore@opensolaris.org 			    SDA_EOK);
6437302Sgdamore@opensolaris.org 			continue;
6447302Sgdamore@opensolaris.org 		}
6457302Sgdamore@opensolaris.org 
6467302Sgdamore@opensolaris.org 		if (slot->s_detach) {
6478289Sgdamore@opensolaris.org 			/* Parent is detaching the slot, bail out. */
6488289Sgdamore@opensolaris.org 			break;
6498289Sgdamore@opensolaris.org 		}
6508289Sgdamore@opensolaris.org 
6518289Sgdamore@opensolaris.org 		if ((slot->s_suspend) && (slot->s_xfrp == NULL)) {
6528289Sgdamore@opensolaris.org 			/*
6538289Sgdamore@opensolaris.org 			 * Host wants to suspend, but don't do it if
6548289Sgdamore@opensolaris.org 			 * we have a transfer outstanding.
6558289Sgdamore@opensolaris.org 			 */
6567302Sgdamore@opensolaris.org 			break;
6577302Sgdamore@opensolaris.org 		}
6587302Sgdamore@opensolaris.org 
6597302Sgdamore@opensolaris.org 		if (slot->s_detect) {
6607302Sgdamore@opensolaris.org 			slot->s_detect = B_FALSE;
6617302Sgdamore@opensolaris.org 			mutex_exit(&slot->s_evlock);
6627302Sgdamore@opensolaris.org 
6637302Sgdamore@opensolaris.org 			sda_slot_handle_detect(slot);
6647302Sgdamore@opensolaris.org 			continue;
6657302Sgdamore@opensolaris.org 		}
6667302Sgdamore@opensolaris.org 
6677302Sgdamore@opensolaris.org 		if (slot->s_xfrdone) {
6687302Sgdamore@opensolaris.org 			sda_err_t	errno;
6697302Sgdamore@opensolaris.org 
6707302Sgdamore@opensolaris.org 			errno = slot->s_errno;
6717302Sgdamore@opensolaris.org 			slot->s_errno = SDA_EOK;
6727302Sgdamore@opensolaris.org 			slot->s_xfrdone = B_FALSE;
6737302Sgdamore@opensolaris.org 			mutex_exit(&slot->s_evlock);
6747302Sgdamore@opensolaris.org 
6757302Sgdamore@opensolaris.org 			sda_slot_handle_transfer(slot, errno);
6767302Sgdamore@opensolaris.org 			continue;
6777302Sgdamore@opensolaris.org 		}
6787302Sgdamore@opensolaris.org 
6797302Sgdamore@opensolaris.org 		if (slot->s_fault != SDA_FAULT_NONE) {
6807302Sgdamore@opensolaris.org 			sda_fault_t	fault;
6817302Sgdamore@opensolaris.org 
6827302Sgdamore@opensolaris.org 			fault = slot->s_fault;
6837302Sgdamore@opensolaris.org 			slot->s_fault = SDA_FAULT_NONE;
6847302Sgdamore@opensolaris.org 			mutex_exit(&slot->s_evlock);
6857302Sgdamore@opensolaris.org 
6867302Sgdamore@opensolaris.org 			sda_slot_handle_fault(slot, fault);
6877302Sgdamore@opensolaris.org 			continue;
6887302Sgdamore@opensolaris.org 		}
6897302Sgdamore@opensolaris.org 
6907302Sgdamore@opensolaris.org 		if ((slot->s_xfrp != NULL) && (gethrtime() > slot->s_xfrtmo)) {
6917302Sgdamore@opensolaris.org 			/*
6927302Sgdamore@opensolaris.org 			 * The device stalled processing the data request.
6937302Sgdamore@opensolaris.org 			 * At this point, we really have no choice but to
6947302Sgdamore@opensolaris.org 			 * nuke the request, and flag a fault.
6957302Sgdamore@opensolaris.org 			 */
6967302Sgdamore@opensolaris.org 			mutex_exit(&slot->s_evlock);
6977302Sgdamore@opensolaris.org 			sda_slot_handle_transfer(slot, SDA_ETIME);
6987302Sgdamore@opensolaris.org 			sda_slot_fault(slot, SDA_FAULT_TIMEOUT);
6997302Sgdamore@opensolaris.org 			continue;
7007302Sgdamore@opensolaris.org 		}
7017302Sgdamore@opensolaris.org 
7028289Sgdamore@opensolaris.org 		/*
7038289Sgdamore@opensolaris.org 		 * If the slot has suspended, then we can't process
7048289Sgdamore@opensolaris.org 		 * any new commands yet.
7058289Sgdamore@opensolaris.org 		 */
7068289Sgdamore@opensolaris.org 		if ((slot->s_suspend) || (!slot->s_wake)) {
7077302Sgdamore@opensolaris.org 
7087302Sgdamore@opensolaris.org 			/*
7097302Sgdamore@opensolaris.org 			 * We use a timed wait if we are waiting for a
710*12426Sgdamore@opensolaris.org 			 * data transfer to complete.  Otherwise we
7117302Sgdamore@opensolaris.org 			 * avoid the timed wait to avoid waking CPU
7127302Sgdamore@opensolaris.org 			 * (power savings.)
7137302Sgdamore@opensolaris.org 			 */
7147302Sgdamore@opensolaris.org 
7157302Sgdamore@opensolaris.org 			if ((slot->s_xfrp != NULL) || (slot->s_reap)) {
7168289Sgdamore@opensolaris.org 				/* Wait 3 sec (reap attempts). */
71711066Srafael.vanoni@sun.com 				(void) cv_reltimedwait(&slot->s_evcv,
71811066Srafael.vanoni@sun.com 				    &slot->s_evlock, drv_usectohz(3000000),
71911066Srafael.vanoni@sun.com 				    TR_CLOCK_TICK);
7207302Sgdamore@opensolaris.org 			} else {
7217302Sgdamore@opensolaris.org 				(void) cv_wait(&slot->s_evcv, &slot->s_evlock);
7227302Sgdamore@opensolaris.org 			}
7237302Sgdamore@opensolaris.org 
7247302Sgdamore@opensolaris.org 			mutex_exit(&slot->s_evlock);
7257302Sgdamore@opensolaris.org 			continue;
7267302Sgdamore@opensolaris.org 		}
7277302Sgdamore@opensolaris.org 
7287302Sgdamore@opensolaris.org 		slot->s_wake = B_FALSE;
7297302Sgdamore@opensolaris.org 
730*12426Sgdamore@opensolaris.org 		mutex_exit(&slot->s_evlock);
7317302Sgdamore@opensolaris.org 
7327302Sgdamore@opensolaris.org 		/*
7337302Sgdamore@opensolaris.org 		 * We're awake now, so look for work to do.  First
7347302Sgdamore@opensolaris.org 		 * acquire access to the slot.
7357302Sgdamore@opensolaris.org 		 */
7368289Sgdamore@opensolaris.org 		sda_slot_enter(slot);
7377302Sgdamore@opensolaris.org 
7387302Sgdamore@opensolaris.org 
7397302Sgdamore@opensolaris.org 		/*
7407302Sgdamore@opensolaris.org 		 * If no more commands to process, go back to sleep.
7417302Sgdamore@opensolaris.org 		 */
7427302Sgdamore@opensolaris.org 		if ((cmdp = list_head(&slot->s_cmdlist)) == NULL) {
7437302Sgdamore@opensolaris.org 			sda_slot_exit(slot);
7447302Sgdamore@opensolaris.org 			continue;
7457302Sgdamore@opensolaris.org 		}
7467302Sgdamore@opensolaris.org 
7478289Sgdamore@opensolaris.org 		/*
7488289Sgdamore@opensolaris.org 		 * If the current command is not an initialization
7498289Sgdamore@opensolaris.org 		 * command, but we are initializing, go back to sleep.
7508289Sgdamore@opensolaris.org 		 * (This happens potentially during a card reset or
7518289Sgdamore@opensolaris.org 		 * suspend/resume cycle, where the card has not been
7528289Sgdamore@opensolaris.org 		 * removed, but a reset is in progress.)
7538289Sgdamore@opensolaris.org 		 */
7548289Sgdamore@opensolaris.org 		if (slot->s_init && !(cmdp->sc_flags & SDA_CMDF_INIT)) {
7558289Sgdamore@opensolaris.org 			sda_slot_exit(slot);
7568289Sgdamore@opensolaris.org 			continue;
7578289Sgdamore@opensolaris.org 		}
7588289Sgdamore@opensolaris.org 
7597302Sgdamore@opensolaris.org 		datline = ((cmdp->sc_flags & SDA_CMDF_DAT) != 0);
7607302Sgdamore@opensolaris.org 
7617302Sgdamore@opensolaris.org 		if (datline) {
7627302Sgdamore@opensolaris.org 			/*
7637302Sgdamore@opensolaris.org 			 * If the current command has a data phase
7647302Sgdamore@opensolaris.org 			 * while a transfer is in progress, then go
7657302Sgdamore@opensolaris.org 			 * back to sleep.
7667302Sgdamore@opensolaris.org 			 */
7677302Sgdamore@opensolaris.org 			if (slot->s_xfrp != NULL) {
7687302Sgdamore@opensolaris.org 				sda_slot_exit(slot);
7697302Sgdamore@opensolaris.org 				continue;
7707302Sgdamore@opensolaris.org 			}
7717302Sgdamore@opensolaris.org 
7727302Sgdamore@opensolaris.org 			/*
7737302Sgdamore@opensolaris.org 			 * Note that APP_CMD doesn't have a data phase,
7747302Sgdamore@opensolaris.org 			 * although the associated ACMD might.
7757302Sgdamore@opensolaris.org 			 */
7767302Sgdamore@opensolaris.org 			if (cmdp->sc_index != CMD_APP_CMD) {
7777302Sgdamore@opensolaris.org 				slot->s_xfrp = cmdp;
7787302Sgdamore@opensolaris.org 				/*
7797302Sgdamore@opensolaris.org 				 * All commands should complete in
7807302Sgdamore@opensolaris.org 				 * less than 5 seconds.  The worst
7817302Sgdamore@opensolaris.org 				 * case is actually somewhere around 4
7827302Sgdamore@opensolaris.org 				 * seconds, but that is when the clock
7837302Sgdamore@opensolaris.org 				 * is only 100 kHz.
7847302Sgdamore@opensolaris.org 				 */
7857302Sgdamore@opensolaris.org 				slot->s_xfrtmo = gethrtime() +
7867302Sgdamore@opensolaris.org 				    5000000000ULL;
7877302Sgdamore@opensolaris.org 				(void) sda_setprop(slot, SDA_PROP_LED, 1);
7887302Sgdamore@opensolaris.org 			}
7897302Sgdamore@opensolaris.org 		}
7907302Sgdamore@opensolaris.org 
7917302Sgdamore@opensolaris.org 		/*
7927302Sgdamore@opensolaris.org 		 * We're committed to dispatching this command now,
7937302Sgdamore@opensolaris.org 		 * so remove it from the list.
7947302Sgdamore@opensolaris.org 		 */
7957302Sgdamore@opensolaris.org 		list_remove(&slot->s_cmdlist, cmdp);
7967302Sgdamore@opensolaris.org 
7977302Sgdamore@opensolaris.org 		/*
7987302Sgdamore@opensolaris.org 		 * There could be more commands after this one, so we
7997302Sgdamore@opensolaris.org 		 * mark ourself so we stay awake for another cycle.
8007302Sgdamore@opensolaris.org 		 */
8017302Sgdamore@opensolaris.org 		sda_slot_wakeup(slot);
8027302Sgdamore@opensolaris.org 
8037302Sgdamore@opensolaris.org 		/*
8047302Sgdamore@opensolaris.org 		 * Submit the command.  Note that we are holding the
8057302Sgdamore@opensolaris.org 		 * slot lock here, so it is critical that the caller
8067302Sgdamore@opensolaris.org 		 * *not* call back up into the framework.  The caller
8077302Sgdamore@opensolaris.org 		 * must break context.  But doing it this way prevents
8087302Sgdamore@opensolaris.org 		 * a critical race on card removal.
8097302Sgdamore@opensolaris.org 		 *
8107302Sgdamore@opensolaris.org 		 * Note that we don't resubmit memory to the device if
8117302Sgdamore@opensolaris.org 		 * it isn't flagged as ready (e.g. if the wrong device
8127302Sgdamore@opensolaris.org 		 * was inserted!)
8137302Sgdamore@opensolaris.org 		 */
8148289Sgdamore@opensolaris.org 		if ((!slot->s_ready) && (cmdp->sc_flags & SDA_CMDF_MEM)) {
8157302Sgdamore@opensolaris.org 			rv = SDA_ENODEV;
8167302Sgdamore@opensolaris.org 		} else {
8177302Sgdamore@opensolaris.org 			rv = slot->s_ops.so_cmd(slot->s_prv, cmdp);
8187302Sgdamore@opensolaris.org 		}
8197302Sgdamore@opensolaris.org 		if (rv == SDA_EOK)
8207302Sgdamore@opensolaris.org 			rv = sda_slot_check_response(cmdp);
8217302Sgdamore@opensolaris.org 
8227302Sgdamore@opensolaris.org 		if (rv == SDA_EOK) {
8237302Sgdamore@opensolaris.org 			/*
8247302Sgdamore@opensolaris.org 			 * If APP_CMD completed properly, then
8257302Sgdamore@opensolaris.org 			 * resubmit with ACMD index.  Note wake was
8267302Sgdamore@opensolaris.org 			 * already set above.
8277302Sgdamore@opensolaris.org 			 */
8287302Sgdamore@opensolaris.org 			if (cmdp->sc_index == CMD_APP_CMD) {
8297302Sgdamore@opensolaris.org 				if ((cmdp->sc_response[0] & R1_APP_CMD) == 0) {
8307302Sgdamore@opensolaris.org 					sda_slot_log(slot, "APP_CMD not set!");
8317302Sgdamore@opensolaris.org 				}
8327302Sgdamore@opensolaris.org 				sda_cmd_resubmit_acmd(slot, cmdp);
8337302Sgdamore@opensolaris.org 				sda_slot_exit(slot);
8347302Sgdamore@opensolaris.org 
8357302Sgdamore@opensolaris.org 				continue;
8367302Sgdamore@opensolaris.org 			}
8377302Sgdamore@opensolaris.org 
8387302Sgdamore@opensolaris.org 		} else if (datline) {
8397302Sgdamore@opensolaris.org 			/*
8407302Sgdamore@opensolaris.org 			 * If an error occurred and we were expecting
8417302Sgdamore@opensolaris.org 			 * a transfer phase, we have to clean up.
8427302Sgdamore@opensolaris.org 			 */
8437302Sgdamore@opensolaris.org 			(void) sda_setprop(slot, SDA_PROP_LED, 0);
8447302Sgdamore@opensolaris.org 			slot->s_xfrp = NULL;
8457302Sgdamore@opensolaris.org 			slot->s_xfrtmo = 0;
8467302Sgdamore@opensolaris.org 
8477302Sgdamore@opensolaris.org 			/*
8487302Sgdamore@opensolaris.org 			 * And notify any waiter.
8497302Sgdamore@opensolaris.org 			 */
8507302Sgdamore@opensolaris.org 			sda_slot_exit(slot);
8517302Sgdamore@opensolaris.org 			sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT, rv);
8527302Sgdamore@opensolaris.org 			continue;
8537302Sgdamore@opensolaris.org 		}
8547302Sgdamore@opensolaris.org 
8557302Sgdamore@opensolaris.org 		/*
8567302Sgdamore@opensolaris.org 		 * Wake any waiter.
8577302Sgdamore@opensolaris.org 		 */
8587302Sgdamore@opensolaris.org 		sda_slot_exit(slot);
8597302Sgdamore@opensolaris.org 		sda_cmd_notify(cmdp, SDA_CMDF_BUSY, rv);
8607302Sgdamore@opensolaris.org 	}
8617302Sgdamore@opensolaris.org 
8627302Sgdamore@opensolaris.org 	mutex_exit(&slot->s_evlock);
8637302Sgdamore@opensolaris.org }
8647302Sgdamore@opensolaris.org 
8657302Sgdamore@opensolaris.org void
sda_slot_vprintf(sda_slot_t * s,int level,const char * fmt,va_list ap)8667302Sgdamore@opensolaris.org sda_slot_vprintf(sda_slot_t *s, int level, const char *fmt, va_list ap)
8677302Sgdamore@opensolaris.org {
8687302Sgdamore@opensolaris.org 	char		msgbuf[256];
8697302Sgdamore@opensolaris.org 	const char	*pfx, *sfx;
8707302Sgdamore@opensolaris.org 
8717302Sgdamore@opensolaris.org 	if (level == CE_CONT) {
8727302Sgdamore@opensolaris.org 		pfx = "!";
8737302Sgdamore@opensolaris.org 		sfx = "\n";
8747302Sgdamore@opensolaris.org 	} else {
8757302Sgdamore@opensolaris.org 		pfx = sfx = "";
8767302Sgdamore@opensolaris.org 	}
8777302Sgdamore@opensolaris.org 
8787302Sgdamore@opensolaris.org 	if (s != NULL) {
8797686Sgdamore@opensolaris.org 		dev_info_t	*dip = s->s_hostp->h_dip;
8807302Sgdamore@opensolaris.org 
8817302Sgdamore@opensolaris.org 		(void) snprintf(msgbuf, sizeof (msgbuf),
8827302Sgdamore@opensolaris.org 		    "%s%s%d: slot %d: %s%s", pfx,
8837302Sgdamore@opensolaris.org 		    ddi_driver_name(dip), ddi_get_instance(dip),
8847302Sgdamore@opensolaris.org 		    s->s_slot_num, fmt, sfx);
8857302Sgdamore@opensolaris.org 	} else {
8867302Sgdamore@opensolaris.org 		(void) snprintf(msgbuf, sizeof (msgbuf), "%ssda: %s%s",
8877302Sgdamore@opensolaris.org 		    pfx, fmt, sfx);
8887302Sgdamore@opensolaris.org 	}
8897302Sgdamore@opensolaris.org 	vcmn_err(level, msgbuf, ap);
8907302Sgdamore@opensolaris.org }
8917302Sgdamore@opensolaris.org 
8927302Sgdamore@opensolaris.org void
sda_slot_err(sda_slot_t * s,const char * fmt,...)8937302Sgdamore@opensolaris.org sda_slot_err(sda_slot_t *s, const char *fmt, ...)
8947302Sgdamore@opensolaris.org {
8957302Sgdamore@opensolaris.org 	va_list	ap;
8967302Sgdamore@opensolaris.org 
8977302Sgdamore@opensolaris.org 	va_start(ap, fmt);
8987302Sgdamore@opensolaris.org 	sda_slot_vprintf(s, CE_WARN, fmt, ap);
8997302Sgdamore@opensolaris.org 	va_end(ap);
9007302Sgdamore@opensolaris.org }
9017302Sgdamore@opensolaris.org 
9027302Sgdamore@opensolaris.org void
sda_slot_log(sda_slot_t * s,const char * fmt,...)9037302Sgdamore@opensolaris.org sda_slot_log(sda_slot_t *s, const char *fmt, ...)
9047302Sgdamore@opensolaris.org {
9057302Sgdamore@opensolaris.org 	va_list	ap;
9067302Sgdamore@opensolaris.org 
9077302Sgdamore@opensolaris.org 	va_start(ap, fmt);
9087302Sgdamore@opensolaris.org 	sda_slot_vprintf(s, CE_CONT, fmt, ap);
9097302Sgdamore@opensolaris.org 	va_end(ap);
9107302Sgdamore@opensolaris.org }
911