summaryrefslogtreecommitdiffstats
path: root/sys/dev/mmc/mmc.c
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2008-12-06 21:41:27 +0000
committermav <mav@FreeBSD.org>2008-12-06 21:41:27 +0000
commite989bd475bb853f0505a5db0a6c7ec983d11319b (patch)
treefadd036504cd550412e09f34244d4ea3a9758718 /sys/dev/mmc/mmc.c
parent2a6c909faeab000884e36118f00ef41b0b6bb2f9 (diff)
downloadFreeBSD-src-e989bd475bb853f0505a5db0a6c7ec983d11319b.zip
FreeBSD-src-e989bd475bb853f0505a5db0a6c7ec983d11319b.tar.gz
Implement suspend/resume for mmc and mmcsd drivers.
Now it is possible to suspend/resume with inserted and active card. To reinitialize card on resume and to detect card change while suspended, implement bus rescan routines. It can also be used by controllers without card presence detection signals or with multiple cards per slot support. While there, cleanup msleep() usage. We have no any rights to exit without "request done" signal from driver as it could lead to modify after free.
Diffstat (limited to 'sys/dev/mmc/mmc.c')
-rw-r--r--sys/dev/mmc/mmc.c167
1 files changed, 113 insertions, 54 deletions
diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c
index bea8c3b..2b2d6ab 100644
--- a/sys/dev/mmc/mmc.c
+++ b/sys/dev/mmc/mmc.c
@@ -108,6 +108,8 @@ struct mmc_ivars {
static int mmc_probe(device_t dev);
static int mmc_attach(device_t dev);
static int mmc_detach(device_t dev);
+static int mmc_suspend(device_t dev);
+static int mmc_resume(device_t dev);
#define MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
@@ -130,6 +132,8 @@ static int mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width)
static int mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr);
static void mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr);
static int mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd);
+static void mmc_scan(struct mmc_softc *sc);
+static int mmc_delete_cards(struct mmc_softc *sc);
static void
mmc_ms_delay(int ms)
@@ -166,29 +170,39 @@ static int
mmc_detach(device_t dev)
{
struct mmc_softc *sc = device_get_softc(dev);
- device_t *kids;
- int i, nkid;
+ int err;
- /* kill children [ph33r]. -sorbo */
- if (device_get_children(sc->dev, &kids, &nkid) != 0)
- return (0);
- for (i = 0; i < nkid; i++) {
- device_t kid = kids[i];
- void *ivar = device_get_ivars(kid);
-
- device_detach(kid);
- device_delete_child(sc->dev, kid);
- free(ivar, M_DEVBUF);
- }
- free(kids, M_TEMP);
+ if ((err = mmc_delete_cards(sc)) != 0)
+ return (err);
mmc_power_down(sc);
-
MMC_LOCK_DESTROY(sc);
return (0);
}
static int
+mmc_suspend(device_t dev)
+{
+ struct mmc_softc *sc = device_get_softc(dev);
+ int err;
+
+ err = bus_generic_suspend(dev);
+ if (err)
+ return (err);
+ mmc_power_down(sc);
+ return (0);
+}
+
+static int
+mmc_resume(device_t dev)
+{
+ struct mmc_softc *sc = device_get_softc(dev);
+
+ mmc_scan(sc);
+ return (bus_generic_resume(dev));
+}
+
+static int
mmc_acquire_bus(device_t busdev, device_t dev)
{
struct mmc_softc *sc;
@@ -265,12 +279,6 @@ mmc_release_bus(device_t busdev, device_t dev)
return (0);
}
-static void
-mmc_rescan_cards(struct mmc_softc *sc)
-{
- /* XXX: Look at the children and see if they respond to status */
-}
-
static uint32_t
mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr)
{
@@ -294,31 +302,25 @@ mmc_wakeup(struct mmc_request *req)
{
struct mmc_softc *sc;
-/* printf("Wakeup for req %p done_data %p\n", req, req->done_data); */
sc = (struct mmc_softc *)req->done_data;
MMC_LOCK(sc);
req->flags |= MMC_REQ_DONE;
- wakeup(req);
MMC_UNLOCK(sc);
+ wakeup(req);
}
static int
mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req)
{
- int err;
req->done = mmc_wakeup;
req->done_data = sc;
-/* printf("Submitting request %p sc %p\n", req, sc); */
MMCBR_REQUEST(device_get_parent(sc->dev), sc->dev, req);
MMC_LOCK(sc);
- do {
- err = msleep(req, &sc->sc_mtx, PZERO | PCATCH, "mmcreq",
- hz / 10);
- } while (!(req->flags & MMC_REQ_DONE) && err == EAGAIN);
-/* printf("Request %p done with error %d\n", req, err); */
+ while ((req->flags & MMC_REQ_DONE) == 0)
+ msleep(req, &sc->sc_mtx, 0, "mmcreq", 0);
MMC_UNLOCK(sc);
- return (err);
+ return (0);
}
static int
@@ -1060,25 +1062,41 @@ mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp)
static void
mmc_discover_cards(struct mmc_softc *sc)
{
- struct mmc_ivars *ivar;
- int err;
+ struct mmc_ivars *ivar = NULL;
+ device_t *devlist;
+ int err, i, devcount, newcard;
+ uint32_t raw_cid[4];
uint32_t resp, sec_count;
device_t child;
uint16_t rca = 2;
u_char switch_res[64];
while (1) {
- ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF,
- M_WAITOK | M_ZERO);
- if (!ivar)
- return;
- err = mmc_all_send_cid(sc, ivar->raw_cid);
+ err = mmc_all_send_cid(sc, raw_cid);
if (err == MMC_ERR_TIMEOUT)
break;
if (err != MMC_ERR_NONE) {
device_printf(sc->dev, "Error reading CID %d\n", err);
break;
}
+ newcard = 1;
+ if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
+ return;
+ for (i = 0; i < devcount; i++) {
+ ivar = device_get_ivars(devlist[i]);
+ if (memcmp(ivar->raw_cid, raw_cid, sizeof(raw_cid)) == 0) {
+ newcard = 0;
+ break;
+ }
+ }
+ free(devlist, M_TEMP);
+ if (newcard) {
+ ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+ if (!ivar)
+ return;
+ memcpy(ivar->raw_cid, raw_cid, sizeof(raw_cid));
+ }
if (mmcbr_get_ro(sc->dev))
ivar->read_only = 1;
ivar->bus_width = bus_width_1;
@@ -1121,9 +1139,11 @@ mmc_discover_cards(struct mmc_softc *sc)
if ((mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) &&
(ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4))
ivar->bus_width = bus_width_4;
- /* Add device. */
- child = device_add_child(sc->dev, NULL, -1);
- device_set_ivars(child, ivar);
+ if (newcard) {
+ /* Add device. */
+ child = device_add_child(sc->dev, NULL, -1);
+ device_set_ivars(child, ivar);
+ }
return;
}
mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid);
@@ -1174,11 +1194,50 @@ mmc_discover_cards(struct mmc_softc *sc)
ivar->bus_width = bus_width_1;
ivar->timing = bus_timing_normal;
}
- /* Add device. */
- child = device_add_child(sc->dev, NULL, -1);
- device_set_ivars(child, ivar);
+ if (newcard) {
+ /* Add device. */
+ child = device_add_child(sc->dev, NULL, -1);
+ device_set_ivars(child, ivar);
+ }
}
- free(ivar, M_DEVBUF);
+}
+
+static void
+mmc_rescan_cards(struct mmc_softc *sc)
+{
+ struct mmc_ivars *ivar = NULL;
+ device_t *devlist;
+ int err, i, devcount;
+
+ if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
+ return;
+ for (i = 0; i < devcount; i++) {
+ ivar = device_get_ivars(devlist[i]);
+ if (mmc_select_card(sc, ivar->rca)) {
+ device_delete_child(sc->dev, devlist[i]);
+ free(ivar, M_DEVBUF);
+ }
+ }
+ free(devlist, M_TEMP);
+ mmc_select_card(sc, 0);
+}
+
+static int
+mmc_delete_cards(struct mmc_softc *sc)
+{
+ struct mmc_ivars *ivar;
+ device_t *devlist;
+ int err, i, devcount;
+
+ if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
+ return (err);
+ for (i = 0; i < devcount; i++) {
+ ivar = device_get_ivars(devlist[i]);
+ device_delete_child(sc->dev, devlist[i]);
+ free(ivar, M_DEVBUF);
+ }
+ free(devlist, M_TEMP);
+ return (0);
}
static void
@@ -1205,7 +1264,7 @@ mmc_go_discovery(struct mmc_softc *sc)
*/
mmcbr_set_mode(dev, mode_mmc);
if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE)
- return; /* Failed both, punt! XXX powerdown? */
+ ocr = 0; /* Failed both, powerdown. */
}
mmcbr_set_ocr(dev, mmc_select_vdd(sc, ocr));
if (mmcbr_get_ocr(dev) != 0)
@@ -1220,8 +1279,11 @@ mmc_go_discovery(struct mmc_softc *sc)
* Make sure that we have a mutually agreeable voltage to at least
* one card on the bus.
*/
- if (mmcbr_get_ocr(dev) == 0)
+ if (mmcbr_get_ocr(dev) == 0) {
+ mmc_delete_cards(sc);
+ mmc_power_down(sc);
return;
+ }
/*
* Reselect the cards after we've idled them above.
*/
@@ -1232,6 +1294,7 @@ mmc_go_discovery(struct mmc_softc *sc)
} else
mmc_send_op_cond(sc, mmcbr_get_ocr(dev), NULL);
mmc_discover_cards(sc);
+ mmc_rescan_cards(sc);
mmcbr_set_bus_mode(dev, pushpull);
mmcbr_update_ios(dev);
@@ -1292,17 +1355,11 @@ mmc_calculate_clock(struct mmc_softc *sc)
static void
mmc_scan(struct mmc_softc *sc)
{
- device_t dev;
+ device_t dev = sc->dev;
- dev = sc->dev;
mmc_acquire_bus(dev, dev);
-
- if (mmcbr_get_power_mode(dev) == power_on)
- mmc_rescan_cards(sc);
mmc_go_discovery(sc);
-
mmc_release_bus(dev, dev);
- /* XXX probe/attach/detach children? */
}
static int
@@ -1374,6 +1431,8 @@ static device_method_t mmc_methods[] = {
DEVMETHOD(device_probe, mmc_probe),
DEVMETHOD(device_attach, mmc_attach),
DEVMETHOD(device_detach, mmc_detach),
+ DEVMETHOD(device_suspend, mmc_suspend),
+ DEVMETHOD(device_resume, mmc_resume),
/* Bus interface */
DEVMETHOD(bus_read_ivar, mmc_read_ivar),
OpenPOWER on IntegriCloud