summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <gdamore@opensolaris.org>2008-12-05 20:57:52 -0800
committerGarrett D'Amore <gdamore@opensolaris.org>2008-12-05 20:57:52 -0800
commitf2b90c3c415ff04d4adb3a54242822b41d74bfd9 (patch)
tree9ebff4d2800a4221771d42782eeacbdfb20e4203
parent0373a3df1f0034861e6d4551f43396675f5784f2 (diff)
downloadillumos-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.c88
-rw-r--r--usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c21
-rw-r--r--usr/src/uts/common/io/sdcard/impl/mapfile2
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_cmd.c7
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_host.c31
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_init.c10
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_mem.c9
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_nexus.c20
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_slot.c149
-rw-r--r--usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c1
-rw-r--r--usr/src/uts/common/sys/sdcard/sda.h2
-rw-r--r--usr/src/uts/common/sys/sdcard/sda_impl.h9
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 *);