diff options
author | Garrett D'Amore <gdamore@opensolaris.org> | 2008-12-05 20:57:52 -0800 |
---|---|---|
committer | Garrett D'Amore <gdamore@opensolaris.org> | 2008-12-05 20:57:52 -0800 |
commit | f2b90c3c415ff04d4adb3a54242822b41d74bfd9 (patch) | |
tree | 9ebff4d2800a4221771d42782eeacbdfb20e4203 | |
parent | 0373a3df1f0034861e6d4551f43396675f5784f2 (diff) | |
download | illumos-joyent-f2b90c3c415ff04d4adb3a54242822b41d74bfd9.tar.gz |
PSARC 2008/742 SDcard Framework Suspend & Resume
6769845 Toshiba Tecra M10 can't read SD cards
6774949 sdcard does not handle block interrupts properly
6775711 sdcard poor interaction with suspend/resume
6760177 sdhost has no quiesce()
-rw-r--r-- | usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c | 88 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c | 21 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/impl/mapfile | 2 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/impl/sda_cmd.c | 7 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/impl/sda_host.c | 31 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/impl/sda_init.c | 10 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/impl/sda_mem.c | 9 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/impl/sda_nexus.c | 20 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/impl/sda_slot.c | 149 | ||||
-rw-r--r-- | usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c | 1 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sdcard/sda.h | 2 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sdcard/sda_impl.h | 9 |
12 files changed, 222 insertions, 127 deletions
diff --git a/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c b/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c index af20fa7d6f..348a345bdc 100644 --- a/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c +++ b/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c @@ -79,6 +79,7 @@ struct sdhost { static int sdhost_attach(dev_info_t *, ddi_attach_cmd_t); static int sdhost_detach(dev_info_t *, ddi_detach_cmd_t); +static int sdhost_quiesce(dev_info_t *); static int sdhost_suspend(dev_info_t *); static int sdhost_resume(dev_info_t *); @@ -113,7 +114,7 @@ static struct dev_ops sdhost_dev_ops = { NULL, /* devo_cb_ops */ NULL, /* devo_bus_ops */ NULL, /* devo_power */ - ddi_quiesce_not_supported, /* devo_quiesce */ + sdhost_quiesce, /* devo_quiesce */ }; static struct modldrv sdhost_modldrv = { @@ -226,6 +227,7 @@ sdhost_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) uint8_t slotinfo; uint8_t bar; int i; + int rv; switch (cmd) { case DDI_ATTACH: @@ -328,7 +330,15 @@ sdhost_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) /* * Enable device interrupts at the DDI layer. */ - (void) ddi_intr_enable(shp->sh_ihandle); + if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) { + rv = ddi_intr_block_enable(&shp->sh_ihandle, 1); + } else { + rv = ddi_intr_enable(shp->sh_ihandle); + } + if (rv != DDI_SUCCESS) { + cmn_err(CE_WARN, "Failed enabling interrupts"); + goto failed; + } /* * Mark the slots online with the framework. This will cause @@ -336,7 +346,11 @@ sdhost_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) */ if (sda_host_attach(shp->sh_host) != DDI_SUCCESS) { cmn_err(CE_WARN, "Failed attaching to SDA framework"); - (void) ddi_intr_disable(shp->sh_ihandle); + if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) { + (void) ddi_intr_block_disable(&shp->sh_ihandle, 1); + } else { + (void) ddi_intr_disable(shp->sh_ihandle); + } goto failed; } @@ -372,8 +386,6 @@ sdhost_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) } shp = ddi_get_driver_private(dip); - if (shp == NULL) - return (DDI_FAILURE); /* * Take host offline with the framework. @@ -384,7 +396,11 @@ sdhost_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) * Tear down interrupts. */ if (shp->sh_ihandle != NULL) { - (void) ddi_intr_disable(shp->sh_ihandle); + if (shp->sh_icap & DDI_INTR_FLAG_BLOCK) { + (void) ddi_intr_block_disable(&shp->sh_ihandle, 1); + } else { + (void) ddi_intr_disable(shp->sh_ihandle); + } (void) ddi_intr_remove_handler(shp->sh_ihandle); (void) ddi_intr_free(shp->sh_ihandle); } @@ -400,6 +416,25 @@ sdhost_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) } int +sdhost_quiesce(dev_info_t *dip) +{ + sdhost_t *shp; + sdslot_t *ss; + + shp = ddi_get_driver_private(dip); + + /* reset each slot separately */ + for (int i = 0; i < shp->sh_numslots; i++) { + ss = &shp->sh_slots[i]; + if (ss->ss_acch == NULL) + continue; + + (void) sdhost_soft_reset(ss, SOFT_RESET_ALL); + } + return (DDI_SUCCESS); +} + +int sdhost_suspend(dev_info_t *dip) { sdhost_t *shp; @@ -407,11 +442,8 @@ sdhost_suspend(dev_info_t *dip) int i; shp = ddi_get_driver_private(dip); - if (shp == NULL) - return (DDI_FAILURE); - /* disable the interrupts */ - (void) ddi_intr_disable(shp->sh_ihandle); + sda_host_suspend(shp->sh_host); for (i = 0; i < shp->sh_numslots; i++) { ss = &shp->sh_slots[i]; @@ -432,8 +464,6 @@ sdhost_resume(dev_info_t *dip) int i; shp = ddi_get_driver_private(dip); - if (shp == NULL) - return (DDI_FAILURE); for (i = 0; i < shp->sh_numslots; i++) { ss = &shp->sh_slots[i]; @@ -444,14 +474,8 @@ sdhost_resume(dev_info_t *dip) mutex_exit(&ss->ss_lock); } - /* re-enable the interrupts */ - (void) ddi_intr_enable(shp->sh_ihandle); + sda_host_resume(shp->sh_host); - /* kick off a new card detect task */ - for (i = 0; i < shp->sh_numslots; i++) { - ss = &shp->sh_slots[i]; - sda_host_detect(ss->ss_host, ss->ss_num); - } return (DDI_SUCCESS); } @@ -931,6 +955,7 @@ sdhost_init_slot(dev_info_t *dip, sdhost_t *shp, int num, int bar) if (ddi_regs_map_setup(dip, bar, &ss->ss_regva, 0, 0, &sdhost_regattr, &ss->ss_acch) != DDI_SUCCESS) { + cmn_err(CE_WARN, "Failed to map registers!"); return (DDI_FAILURE); } @@ -1083,6 +1108,7 @@ sdhost_wait_cmd(sdslot_t *ss, sda_cmd_t *cmdp) { int i; uint16_t errs; + sda_err_t rv; /* * Worst case for 100kHz timeout is 2msec (200 clocks), we add @@ -1116,20 +1142,29 @@ sdhost_wait_cmd(sdslot_t *ss, sda_cmd_t *cmdp) /* command timeout isn't a host failure */ if ((errs & ERR_CMD_TMO) == ERR_CMD_TMO) { - return (SDA_ETIME); - } - - if ((errs & ERR_CMD_CRC) == ERR_CMD_CRC) { - return (SDA_ECRC7); + rv = SDA_ETIME; + } else if ((errs & ERR_CMD_CRC) == ERR_CMD_CRC) { + rv = SDA_ECRC7; } else { - return (SDA_EPROTO); + rv = SDA_EPROTO; } + goto error; } drv_usecwait(5); } - return (SDA_ETIME); + rv = SDA_ETIME; + +error: + /* + * NB: We need to soft reset the CMD and DAT + * lines after a failure of this sort. + */ + (void) sdhost_soft_reset(ss, SOFT_RESET_CMD); + (void) sdhost_soft_reset(ss, SOFT_RESET_DAT); + + return (rv); } sda_err_t @@ -1282,7 +1317,6 @@ sdhost_getprop(void *arg, sda_prop_t prop, uint32_t *val) mutex_exit(&ss->ss_lock); return (SDA_ESUSPENDED); } - switch (prop) { case SDA_PROP_INSERTED: if (CHECK_STATE(ss, CARD_INSERTED)) { diff --git a/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c b/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c index 0fe9e7d402..c5c51606e3 100644 --- a/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c +++ b/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c @@ -271,11 +271,7 @@ wbsd_detach(dev_info_t *dip) { wbsd_t *wp; - if ((wp = ddi_get_driver_private(dip)) == NULL) { - cmn_err(CE_WARN, "Unable to get soft state"); - return (DDI_FAILURE); - } - + wp = ddi_get_driver_private(dip); sda_host_detach(wp->w_host); @@ -297,10 +293,10 @@ wbsd_suspend(dev_info_t *dip) { wbsd_t *wp; - if ((wp = ddi_get_driver_private(dip)) == NULL) { - cmn_err(CE_WARN, "Unable to get soft state"); - return (DDI_FAILURE); - } + wp = ddi_get_driver_private(dip); + + sda_host_suspend(wp->w_host); + mutex_enter(&wp->w_lock); wp->w_suspended = B_TRUE; wbsd_halt_hw(wp); @@ -314,17 +310,14 @@ wbsd_resume(dev_info_t *dip) { wbsd_t *wp; - if ((wp = ddi_get_driver_private(dip)) == NULL) { - cmn_err(CE_WARN, "Unable to get soft state"); - return (DDI_FAILURE); - } + wp = ddi_get_driver_private(dip); mutex_enter(&wp->w_lock); wp->w_suspended = B_FALSE; wbsd_reset_hw(wp); mutex_exit(&wp->w_lock); - sda_host_detect(wp->w_host, 0); + sda_host_resume(wp->w_host); return (DDI_SUCCESS); } diff --git a/usr/src/uts/common/io/sdcard/impl/mapfile b/usr/src/uts/common/io/sdcard/impl/mapfile index 62bc1454e9..13895f7503 100644 --- a/usr/src/uts/common/io/sdcard/impl/mapfile +++ b/usr/src/uts/common/io/sdcard/impl/mapfile @@ -34,6 +34,8 @@ sda_host_attach; sda_host_detach; sda_host_detect; + sda_host_suspend; + sda_host_resume; sda_host_set_private; sda_host_transfer; sda_host_fault; diff --git a/usr/src/uts/common/io/sdcard/impl/sda_cmd.c b/usr/src/uts/common/io/sdcard/impl/sda_cmd.c index 4524e3f03c..f84eac242c 100644 --- a/usr/src/uts/common/io/sdcard/impl/sda_cmd.c +++ b/usr/src/uts/common/io/sdcard/impl/sda_cmd.c @@ -230,7 +230,12 @@ sda_cmd_submit(sda_slot_t *slot, sda_cmd_t *cmdp, void (*done)(sda_cmd_t *)) return; } - list_insert_tail(&slot->s_cmdlist, c); + /* Initialization commands go to the head of the class */ + if (c->c_flags & SDA_CMDF_INIT) { + list_insert_head(&slot->s_cmdlist, c); + } else { + list_insert_tail(&slot->s_cmdlist, c); + } sda_slot_exit(slot); sda_slot_wakeup(slot); diff --git a/usr/src/uts/common/io/sdcard/impl/sda_host.c b/usr/src/uts/common/io/sdcard/impl/sda_host.c index b11fb651c6..ce2864c4dd 100644 --- a/usr/src/uts/common/io/sdcard/impl/sda_host.c +++ b/usr/src/uts/common/io/sdcard/impl/sda_host.c @@ -115,7 +115,6 @@ sda_host_t * sda_host_alloc(dev_info_t *dip, int nslot, sda_ops_t *ops, ddi_dma_attr_t *dma) { sda_host_t *h; - int i; if (ops->so_version != SDA_OPS_VERSION) { return (NULL); @@ -128,7 +127,7 @@ sda_host_alloc(dev_info_t *dip, int nslot, sda_ops_t *ops, ddi_dma_attr_t *dma) h->h_dip = dip; /* initialize each slot */ - for (i = 0; i < nslot; i++) { + for (int i = 0; i < nslot; i++) { sda_slot_t *slot = &h->h_slots[i]; slot->s_hostp = h; @@ -144,9 +143,7 @@ sda_host_alloc(dev_info_t *dip, int nslot, sda_ops_t *ops, ddi_dma_attr_t *dma) void sda_host_free(sda_host_t *h) { - int i; - - for (i = 0; i < h->h_nslot; i++) { + for (int i = 0; i < h->h_nslot; i++) { sda_slot_fini(&h->h_slots[i]); } @@ -163,12 +160,10 @@ sda_host_set_private(sda_host_t *h, int num, void *private) int sda_host_attach(sda_host_t *h) { - int i; - /* * Attach slots. */ - for (i = 0; i < h->h_nslot; i++) { + for (int i = 0; i < h->h_nslot; i++) { sda_slot_attach(&h->h_slots[i]); @@ -189,8 +184,6 @@ sda_host_attach(sda_host_t *h) void sda_host_detach(sda_host_t *h) { - int i; - /* * Unregister nexus minor nodes. */ @@ -199,12 +192,28 @@ sda_host_detach(sda_host_t *h) /* * Detach slots. */ - for (i = 0; i < h->h_nslot; i++) { + for (int i = 0; i < h->h_nslot; i++) { sda_slot_detach(&h->h_slots[i]); } } void +sda_host_suspend(sda_host_t *h) +{ + for (int i = 0; i < h->h_nslot; i++) { + sda_slot_suspend(&h->h_slots[i]); + } +} + +void +sda_host_resume(sda_host_t *h) +{ + for (int i = 0; i < h->h_nslot; i++) { + sda_slot_resume(&h->h_slots[i]); + } +} + +void sda_host_transfer(sda_host_t *h, int num, sda_err_t errno) { sda_slot_transfer(&h->h_slots[num], errno); diff --git a/usr/src/uts/common/io/sdcard/impl/sda_init.c b/usr/src/uts/common/io/sdcard/impl/sda_init.c index e6cfce429d..3e08c9e2cd 100644 --- a/usr/src/uts/common/io/sdcard/impl/sda_init.c +++ b/usr/src/uts/common/io/sdcard/impl/sda_init.c @@ -414,12 +414,10 @@ sda_init_clock(sda_slot_t *slot, uint32_t hz) return; } - if ((rv = sda_getprop(slot, SDA_PROP_CLOCK, &act)) == SDA_EOK) { - sda_slot_debug(slot, "Clock set to %u Hz (requested %u Hz)", - act, hz); - } else { - sda_slot_debug(slot, "Clock frequency unknown (good luck)."); - } + rv = sda_getprop(slot, SDA_PROP_CLOCK, &act); + sda_slot_debug(slot, + rv == SDA_EOK ? "Clock set to %u Hz (requested %u Hz)" : + "Clock frequency unknown (good luck).", act, hz); /* * For now, just wait 10msec for clocks to stabilize to the diff --git a/usr/src/uts/common/io/sdcard/impl/sda_mem.c b/usr/src/uts/common/io/sdcard/impl/sda_mem.c index a9e78431af..f470e38687 100644 --- a/usr/src/uts/common/io/sdcard/impl/sda_mem.c +++ b/usr/src/uts/common/io/sdcard/impl/sda_mem.c @@ -38,6 +38,7 @@ static int sda_mem_attach(dev_info_t *, ddi_attach_cmd_t); static int sda_mem_detach(dev_info_t *, ddi_detach_cmd_t); +static int sda_mem_quiesce(dev_info_t *); static b2s_err_t sda_mem_b2s_errno(sda_err_t); static boolean_t sda_mem_b2s_request(void *, b2s_request_t *); static boolean_t sda_mem_b2s_rw(sda_slot_t *, b2s_request_t *); @@ -94,6 +95,7 @@ sda_mem_init(struct modlinkage *modlp) devo = ((struct modldrv *)(modlp->ml_linkage[0]))->drv_dev_ops; devo->devo_attach = sda_mem_attach; devo->devo_detach = sda_mem_detach; + devo->devo_quiesce = sda_mem_quiesce; devo->devo_cb_ops = &sda_mem_ops; @@ -457,6 +459,13 @@ sda_mem_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) } } +int +sda_mem_quiesce(dev_info_t *dip) +{ + /* no work to do */ + return (DDI_SUCCESS); +} + uint32_t sda_mem_getbits(uint32_t *resp, int hibit, int len) { diff --git a/usr/src/uts/common/io/sdcard/impl/sda_nexus.c b/usr/src/uts/common/io/sdcard/impl/sda_nexus.c index faa5aca1f7..569948b510 100644 --- a/usr/src/uts/common/io/sdcard/impl/sda_nexus.c +++ b/usr/src/uts/common/io/sdcard/impl/sda_nexus.c @@ -130,7 +130,7 @@ sda_nexus_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, if ((slot = ddi_get_parent_data(child_dip)) == NULL) { sda_slot_err(NULL, "Parent data struct missing!"); - return (DDI_FAILURE); + return (DDI_NOT_WELL_FORMED); } /* @@ -146,7 +146,8 @@ sda_nexus_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, */ ndip = ndi_devi_find(dip, ddi_node_name(child_dip), addr); if (ndip && (ndip != child_dip)) { - return (DDI_NOT_WELL_FORMED); + sda_slot_err(slot, "Duplicate device node found " + "(%s@%d)", ddi_node_name(ndip), addr); } /* @@ -410,6 +411,7 @@ sda_nexus_remove(sda_slot_t *slot) ndi_devi_enter(pdip, &circ); if ((cdip = slot->s_dip) != NULL) { reap = B_TRUE; + mutex_enter(&(DEVI(cdip))->devi_lock); DEVI_SET_DEVICE_REMOVED(cdip); mutex_exit(&(DEVI(cdip))->devi_lock); @@ -439,15 +441,17 @@ sda_nexus_reap(void *arg) ndi_devi_enter(pdip, &circ); sda_slot_enter(slot); - if (((cdip = slot->s_dip) != NULL) && DEVI_IS_DEVICE_REMOVED(cdip)) { + cdip = slot->s_dip; + + if ((cdip != NULL) && DEVI_IS_DEVICE_REMOVED(cdip)) { sda_slot_exit(slot); - (void) ddi_deviname(cdip, devnm); - (void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE); if (i_ddi_node_state(cdip) < DS_INITIALIZED) { rv = ddi_remove_child(cdip, 0); } else { + (void) ddi_deviname(cdip, devnm); + (void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE); rv = ndi_devi_unconfig_one(pdip, devnm + 1, NULL, NDI_DEVI_REMOVE | NDI_UNCONFIG); } @@ -461,10 +465,10 @@ sda_nexus_reap(void *arg) return; } sda_slot_enter(slot); - } - if (slot->s_dip == cdip) { - slot->s_dip = NULL; + if (slot->s_dip == cdip) { + slot->s_dip = NULL; + } } sda_slot_exit(slot); diff --git a/usr/src/uts/common/io/sdcard/impl/sda_slot.c b/usr/src/uts/common/io/sdcard/impl/sda_slot.c index 61362162c8..0e908ecd26 100644 --- a/usr/src/uts/common/io/sdcard/impl/sda_slot.c +++ b/usr/src/uts/common/io/sdcard/impl/sda_slot.c @@ -32,9 +32,6 @@ */ #include <sys/types.h> -#include <sys/thread.h> -#include <sys/proc.h> -#include <sys/callb.h> #include <sys/sysmacros.h> #include <sys/cpuvar.h> #include <sys/cmn_err.h> @@ -157,7 +154,8 @@ sda_slot_halt(sda_slot_t *slot) { sda_slot_enter(slot); slot->s_ops.so_halt(slot->s_prv); - drv_usecwait(1000); /* we need to wait 1 msec for power down */ + /* We need to wait 1 msec for power down. */ + drv_usecwait(1000); sda_slot_exit(slot); } @@ -408,7 +406,7 @@ sda_slot_handle_detect(sda_slot_t *slot) * another task, to avoid deadlock as the task may * need to dispatch commands. */ - (void) ddi_taskq_dispatch(slot->s_tq, sda_slot_insert, slot, + (void) ddi_taskq_dispatch(slot->s_hp_tq, sda_slot_insert, slot, DDI_SLEEP); } else { @@ -497,14 +495,13 @@ sda_slot_attach(sda_slot_t *slot) { sda_host_t *h = slot->s_hostp; char name[16]; - kthread_t *thr; uint32_t cap; /* - * We have both a thread and a taskq. The taskq is used for + * We have two taskqs. The first taskq is used for * card initialization. * - * The thread is used for the main processing loop. + * The second is used for the main processing loop. * * The reason for a separate taskq is that initialization * needs to acquire locks which may be held by the slot @@ -516,19 +513,30 @@ sda_slot_attach(sda_slot_t *slot) sda_slot_enter(slot); - (void) snprintf(name, sizeof (name), "slot_%d_tq", slot->s_slot_num); - slot->s_tq = ddi_taskq_create(h->h_dip, name, 1, TASKQ_DEFAULTPRI, 0); - if (slot->s_tq == NULL) { + (void) snprintf(name, sizeof (name), "slot_%d_hp_tq", + slot->s_slot_num); + slot->s_hp_tq = ddi_taskq_create(h->h_dip, name, 1, + TASKQ_DEFAULTPRI, 0); + if (slot->s_hp_tq == NULL) { /* Generally, this failure should never occur */ - sda_slot_err(slot, "Unable to create slot taskq"); + sda_slot_err(slot, "Unable to create hotplug slot taskq"); sda_slot_exit(slot); return; } /* create the main processing thread */ - thr = thread_create(NULL, 0, sda_slot_thread, slot, 0, &p0, TS_RUN, - minclsyspri); - slot->s_thrid = thr->t_did; + (void) snprintf(name, sizeof (name), "slot_%d_main_tq", + slot->s_slot_num); + slot->s_main_tq = ddi_taskq_create(h->h_dip, name, 1, + TASKQ_DEFAULTPRI, 0); + if (slot->s_main_tq == NULL) { + /* Generally, this failure should never occur */ + sda_slot_err(slot, "Unable to create main slot taskq"); + sda_slot_exit(slot); + return; + } + (void) ddi_taskq_dispatch(slot->s_main_tq, sda_slot_thread, slot, + DDI_SLEEP); /* * Determine slot capabilities. @@ -560,33 +568,52 @@ sda_slot_detach(sda_slot_t *slot) /* * Shut down the thread. */ - if (slot->s_thrid) { - mutex_enter(&slot->s_evlock); - slot->s_detach = B_TRUE; - cv_broadcast(&slot->s_evcv); - mutex_exit(&slot->s_evlock); - } - thread_join(slot->s_thrid); + mutex_enter(&slot->s_evlock); + slot->s_detach = B_TRUE; + cv_broadcast(&slot->s_evcv); + mutex_exit(&slot->s_evlock); /* - * Nuke the taskq. We do this after killing the - * thread, to ensure that the thread doesn't try to - * dispatch to it. + * Nuke the taskqs. We do this after stopping the background + * thread to avoid deadlock. */ - if (slot->s_tq) - ddi_taskq_destroy(slot->s_tq); + if (slot->s_main_tq) + ddi_taskq_destroy(slot->s_main_tq); + if (slot->s_hp_tq) + ddi_taskq_destroy(slot->s_hp_tq); +} + +void +sda_slot_suspend(sda_slot_t *slot) +{ + mutex_enter(&slot->s_evlock); + slot->s_suspend = B_TRUE; + cv_broadcast(&slot->s_evcv); + mutex_exit(&slot->s_evlock); + ddi_taskq_wait(slot->s_main_tq); +} + +void +sda_slot_resume(sda_slot_t *slot) +{ + mutex_enter(&slot->s_evlock); + slot->s_suspend = B_FALSE; + /* + * A card change event may have occurred, and in any case we need + * to reinitialize the card. + */ + slot->s_detect = B_TRUE; + mutex_exit(&slot->s_evlock); + + /* Start up a new instance of the main processing task. */ + (void) ddi_taskq_dispatch(slot->s_main_tq, sda_slot_thread, slot, + DDI_SLEEP); } void sda_slot_thread(void *arg) { sda_slot_t *slot = arg; -#ifndef __lock_lint - callb_cpr_t cprinfo; - - CALLB_CPR_INIT(&cprinfo, &slot->s_evlock, callb_generic_cpr, - "sda_slot_thread"); -#endif for (;;) { sda_cmd_t *cmdp; @@ -611,7 +638,15 @@ sda_slot_thread(void *arg) } if (slot->s_detach) { - /* parent is detaching the slot, bail out */ + /* Parent is detaching the slot, bail out. */ + break; + } + + if ((slot->s_suspend) && (slot->s_xfrp == NULL)) { + /* + * Host wants to suspend, but don't do it if + * we have a transfer outstanding. + */ break; } @@ -651,8 +686,8 @@ sda_slot_thread(void *arg) * Do not sleep while holding the evlock. If this * fails, we'll just try again the next cycle. */ - (void) ddi_taskq_dispatch(slot->s_tq, sda_nexus_reap, - slot, DDI_NOSLEEP); + (void) ddi_taskq_dispatch(slot->s_hp_tq, + sda_nexus_reap, slot, DDI_NOSLEEP); } if ((slot->s_xfrp != NULL) && (gethrtime() > slot->s_xfrtmo)) { @@ -667,7 +702,11 @@ sda_slot_thread(void *arg) continue; } - if (!slot->s_wake) { + /* + * If the slot has suspended, then we can't process + * any new commands yet. + */ + if ((slot->s_suspend) || (!slot->s_wake)) { /* * We use a timed wait if we are waiting for a @@ -676,22 +715,15 @@ sda_slot_thread(void *arg) * avoid the timed wait to avoid waking CPU * (power savings.) */ -#ifndef __lock_lint - CALLB_CPR_SAFE_BEGIN(&cprinfo); -#endif if ((slot->s_xfrp != NULL) || (slot->s_reap)) { - /* wait 3 sec (reap attempts) */ - + /* Wait 3 sec (reap attempts). */ (void) cv_timedwait(&slot->s_evcv, &slot->s_evlock, ddi_get_lbolt() + drv_usectohz(3000000)); } else { (void) cv_wait(&slot->s_evcv, &slot->s_evlock); } -#ifndef __lock_lint - CALLB_CPR_SAFE_END(&cprinfo, &slot->s_evlock); -#endif mutex_exit(&slot->s_evlock); continue; @@ -714,9 +746,9 @@ sda_slot_thread(void *arg) * We're awake now, so look for work to do. First * acquire access to the slot. */ - sda_slot_enter(slot); + /* * If no more commands to process, go back to sleep. */ @@ -725,6 +757,18 @@ sda_slot_thread(void *arg) continue; } + /* + * If the current command is not an initialization + * command, but we are initializing, go back to sleep. + * (This happens potentially during a card reset or + * suspend/resume cycle, where the card has not been + * removed, but a reset is in progress.) + */ + if (slot->s_init && !(cmdp->sc_flags & SDA_CMDF_INIT)) { + sda_slot_exit(slot); + continue; + } + datline = ((cmdp->sc_flags & SDA_CMDF_DAT) != 0); if (datline) { @@ -776,16 +820,11 @@ sda_slot_thread(void *arg) * must break context. But doing it this way prevents * a critical race on card removal. * - * During initialization, we reject any commands that - * are not from the initialization code. This does - * have the side effect of removing them. - * * Note that we don't resubmit memory to the device if * it isn't flagged as ready (e.g. if the wrong device * was inserted!) */ - if (((!slot->s_ready) && (cmdp->sc_flags & SDA_CMDF_MEM)) || - (slot->s_init && !(cmdp->sc_flags & SDA_CMDF_INIT))) { + if ((!slot->s_ready) && (cmdp->sc_flags & SDA_CMDF_MEM)) { rv = SDA_ENODEV; if (!slot->s_warn) { sda_slot_err(slot, @@ -839,13 +878,7 @@ sda_slot_thread(void *arg) sda_cmd_notify(cmdp, SDA_CMDF_BUSY, rv); } -#ifdef __lock_lint mutex_exit(&slot->s_evlock); -#else - CALLB_CPR_EXIT(&cprinfo); -#endif - - thread_exit(); } void diff --git a/usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c b/usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c index 4b5d7356c0..65a80b4578 100644 --- a/usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c +++ b/usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c @@ -60,6 +60,7 @@ static struct dev_ops sdcard_devops = { NULL, /* cb_ops */ NULL, /* bus_ops */ NULL, /* power */ + NULL, /* quiesce */ }; static struct modldrv modldrv = { diff --git a/usr/src/uts/common/sys/sdcard/sda.h b/usr/src/uts/common/sys/sdcard/sda.h index 5f48cf8403..a087b57b82 100644 --- a/usr/src/uts/common/sys/sdcard/sda.h +++ b/usr/src/uts/common/sys/sdcard/sda.h @@ -314,6 +314,8 @@ void sda_host_free(sda_host_t *); void sda_host_set_private(sda_host_t *, int, void *); int sda_host_attach(sda_host_t *); void sda_host_detach(sda_host_t *); +void sda_host_suspend(sda_host_t *); +void sda_host_resume(sda_host_t *); void sda_host_detect(sda_host_t *, int); void sda_host_fault(sda_host_t *, int, sda_fault_t); void sda_host_transfer(sda_host_t *, int, sda_err_t); diff --git a/usr/src/uts/common/sys/sdcard/sda_impl.h b/usr/src/uts/common/sys/sdcard/sda_impl.h index c1b48db950..cb4a18e4a7 100644 --- a/usr/src/uts/common/sys/sdcard/sda_impl.h +++ b/usr/src/uts/common/sys/sdcard/sda_impl.h @@ -53,6 +53,7 @@ struct sda_slot { int s_slot_num; boolean_t s_inserted; boolean_t s_failed; + uint8_t s_num_io; uint32_t s_cur_ocr; /* current ocr */ @@ -70,6 +71,7 @@ struct sda_slot { /* these are protected by the evlock */ boolean_t s_wake; /* wake up thread */ boolean_t s_detach; /* detach in progress */ + boolean_t s_suspend; /* host has DDI_SUSPENDed */ boolean_t s_detect; /* detect event occurred */ sda_fault_t s_fault; boolean_t s_xfrdone; /* transfer event occurred */ @@ -116,8 +118,8 @@ struct sda_slot { /* * Asynch. threads. */ - kt_did_t s_thrid; /* processing thread id */ - ddi_taskq_t *s_tq; /* insert taskq */ + ddi_taskq_t *s_hp_tq; /* insert taskq */ + ddi_taskq_t *s_main_tq; /* main processing taskq */ /* * Timestamping for cfgadm benefit. @@ -161,6 +163,7 @@ _NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_lock, sda_slot::s_circular)) _NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_wake)) _NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_detach)) _NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_detect)) +_NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_suspend)) _NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_fault)) _NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_xfrdone)) _NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_errno)) @@ -256,6 +259,8 @@ void sda_slot_exit(sda_slot_t *); boolean_t sda_slot_owned(sda_slot_t *); void sda_slot_attach(sda_slot_t *); void sda_slot_detach(sda_slot_t *); +void sda_slot_suspend(sda_slot_t *); +void sda_slot_resume(sda_slot_t *); void sda_slot_reset(sda_slot_t *); void sda_slot_wakeup(sda_slot_t *); void sda_slot_detect(sda_slot_t *); |