Date: Sat, 6 Dec 2008 21:41:27 +0000 (UTC) From: Alexander Motin <mav@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r185721 - head/sys/dev/mmc Message-ID: <200812062141.mB6LfRLm005830@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mav Date: Sat Dec 6 21:41:27 2008 New Revision: 185721 URL: http://svn.freebsd.org/changeset/base/185721 Log: 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. Modified: head/sys/dev/mmc/mmc.c head/sys/dev/mmc/mmcsd.c Modified: head/sys/dev/mmc/mmc.c ============================================================================== --- head/sys/dev/mmc/mmc.c Sat Dec 6 21:33:44 2008 (r185720) +++ head/sys/dev/mmc/mmc.c Sat Dec 6 21:41:27 2008 (r185721) @@ -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 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_ 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 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), Modified: head/sys/dev/mmc/mmcsd.c ============================================================================== --- head/sys/dev/mmc/mmcsd.c Sat Dec 6 21:33:44 2008 (r185720) +++ head/sys/dev/mmc/mmcsd.c Sat Dec 6 21:41:27 2008 (r185721) @@ -79,6 +79,7 @@ struct mmcsd_softc { struct bio_queue_head bio_queue; daddr_t eblock, eend; /* Range remaining after the last erase. */ int running; + int suspend; }; /* bus entry points */ @@ -163,6 +164,7 @@ mmcsd_attach(device_t dev) bioq_init(&sc->bio_queue); sc->running = 1; + sc->suspend = 0; sc->eblock = sc->eend = 0; kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card"); @@ -174,16 +176,16 @@ mmcsd_detach(device_t dev) { struct mmcsd_softc *sc = device_get_softc(dev); - /* kill thread */ MMCSD_LOCK(sc); - sc->running = 0; - wakeup(sc); - MMCSD_UNLOCK(sc); - - /* wait for thread to finish. XXX probably want timeout. -sorbo */ - MMCSD_LOCK(sc); - while (sc->running != -1) - msleep(sc, &sc->sc_mtx, PRIBIO, "detach", 0); + sc->suspend = 0; + if (sc->running > 0) { + /* kill thread */ + sc->running = 0; + wakeup(sc); + /* wait for thread to finish. */ + while (sc->running != -1) + msleep(sc, &sc->sc_mtx, 0, "detach", 0); + } MMCSD_UNLOCK(sc); /* Flush the request queue. */ @@ -197,6 +199,41 @@ mmcsd_detach(device_t dev) } static int +mmcsd_suspend(device_t dev) +{ + struct mmcsd_softc *sc = device_get_softc(dev); + + MMCSD_LOCK(sc); + sc->suspend = 1; + if (sc->running > 0) { + /* kill thread */ + sc->running = 0; + wakeup(sc); + /* wait for thread to finish. */ + while (sc->running != -1) + msleep(sc, &sc->sc_mtx, 0, "detach", 0); + } + MMCSD_UNLOCK(sc); + return (0); +} + +static int +mmcsd_resume(device_t dev) +{ + struct mmcsd_softc *sc = device_get_softc(dev); + + MMCSD_LOCK(sc); + sc->suspend = 0; + if (sc->running <= 0) { + sc->running = 1; + MMCSD_UNLOCK(sc); + kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card"); + } else + MMCSD_UNLOCK(sc); + return (0); +} + +static int mmcsd_open(struct disk *dp) { return (0); @@ -215,10 +252,10 @@ mmcsd_strategy(struct bio *bp) sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1; MMCSD_LOCK(sc); - if (sc->running > 0) { + if (sc->running > 0 || sc->suspend > 0) { bioq_disksort(&sc->bio_queue, bp); - wakeup(sc); MMCSD_UNLOCK(sc); + wakeup(sc); } else { MMCSD_UNLOCK(sc); biofinish(bp, NULL, ENXIO); @@ -428,8 +465,8 @@ mmcsd_task(void *arg) out: /* tell parent we're done */ sc->running = -1; - wakeup(sc); MMCSD_UNLOCK(sc); + wakeup(sc); kproc_exit(0); } @@ -458,6 +495,8 @@ static device_method_t mmcsd_methods[] = DEVMETHOD(device_probe, mmcsd_probe), DEVMETHOD(device_attach, mmcsd_attach), DEVMETHOD(device_detach, mmcsd_detach), + DEVMETHOD(device_suspend, mmcsd_suspend), + DEVMETHOD(device_resume, mmcsd_resume), {0, 0}, };
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200812062141.mB6LfRLm005830>