summaryrefslogtreecommitdiffstats
path: root/sys/dev/mmc/mmc.c
diff options
context:
space:
mode:
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