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