diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-08-07 14:56:50 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-09-21 10:29:37 +0200 |
commit | c37f9fba70085fedc8eede7559489d2321393005 (patch) | |
tree | 042455ebf1fa89a277a825f72e1ed805d0b4d296 /freebsd/sys/dev/mmc | |
parent | Update to FreeBSD head 2017-06-01 (diff) | |
download | rtems-libbsd-c37f9fba70085fedc8eede7559489d2321393005.tar.bz2 |
Update to FreeBSD head 2017-08-01
Git mirror commit f5002f5e5f78cae9f0269d812dc0aedb0339312c.
Update #3472.
Diffstat (limited to 'freebsd/sys/dev/mmc')
-rw-r--r-- | freebsd/sys/dev/mmc/bridge.h | 8 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc.c | 848 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc_ioctl.h | 2 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc_private.h | 11 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc_subr.c | 20 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcbrvar.h | 16 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcreg.h | 119 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcsd.c | 298 |
8 files changed, 1054 insertions, 268 deletions
diff --git a/freebsd/sys/dev/mmc/bridge.h b/freebsd/sys/dev/mmc/bridge.h index a780ffae..53e61b48 100644 --- a/freebsd/sys/dev/mmc/bridge.h +++ b/freebsd/sys/dev/mmc/bridge.h @@ -137,6 +137,10 @@ enum mmc_card_mode { mode_mmc, mode_sd }; +enum mmc_retune_req { + retune_req_none = 0, retune_req_normal, retune_req_reset +}; + struct mmc_host { int f_min; int f_max; @@ -174,15 +178,17 @@ struct mmc_host { struct mmc_ios ios; /* Current state of the host */ }; +#ifdef _KERNEL extern driver_t mmc_driver; extern devclass_t mmc_devclass; -#define MMC_VERSION 3 +#define MMC_VERSION 4 #define MMC_DECLARE_BRIDGE(name) \ DRIVER_MODULE(mmc, name, mmc_driver, mmc_devclass, NULL, NULL); \ MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION); #define MMC_DEPEND(name) \ MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION); +#endif /* _KERNEL */ #endif /* DEV_MMC_BRIDGE_H */ diff --git a/freebsd/sys/dev/mmc/mmc.c b/freebsd/sys/dev/mmc/mmc.c index 023091eb..74e26332 100644 --- a/freebsd/sys/dev/mmc/mmc.c +++ b/freebsd/sys/dev/mmc/mmc.c @@ -90,14 +90,14 @@ struct mmc_ivars { uint8_t raw_ext_csd[MMC_EXTCSD_SIZE]; /* Raw bits of the EXT_CSD */ uint32_t raw_sd_status[16]; /* Raw bits of the SD_STATUS */ uint16_t rca; + u_char read_only; /* True when the device is read-only */ + u_char high_cap; /* High Capacity device (block addressed) */ enum mmc_card_mode mode; + enum mmc_bus_width bus_width; /* Bus width to use */ struct mmc_cid cid; /* cid decoded */ struct mmc_csd csd; /* csd decoded */ struct mmc_scr scr; /* scr decoded */ struct mmc_sd_status sd_status; /* SD_STATUS decoded */ - u_char read_only; /* True when the device is read-only */ - u_char bus_width; /* Bus width to use */ - u_char high_cap; /* High Capacity card (block addressed) */ uint32_t sec_count; /* Card capacity in 512byte blocks */ uint32_t timings; /* Mask of bus timings supported */ uint32_t vccq_120; /* Mask of bus timings at VCCQ of 1.2 V */ @@ -129,8 +129,10 @@ static int mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result); static int mmc_release_bus(device_t busdev, device_t dev); static int mmc_resume(device_t dev); +static void mmc_retune_pause(device_t busdev, device_t dev, bool retune); +static void mmc_retune_unpause(device_t busdev, device_t dev); static int mmc_suspend(device_t dev); -static int mmc_wait_for_request(device_t brdev, device_t reqdev, +static int mmc_wait_for_request(device_t busdev, device_t dev, struct mmc_request *req); static int mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value); @@ -157,21 +159,23 @@ static void mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid, bool is_4_41p); static void mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid); static void mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd); -static void mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd); +static int mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd); static void mmc_delayed_attach(void *xsc); -static int mmc_delete_cards(struct mmc_softc *sc); +static int mmc_delete_cards(struct mmc_softc *sc, bool final); static void mmc_discover_cards(struct mmc_softc *sc); static void mmc_format_card_id_string(struct mmc_ivars *ivar); static void mmc_go_discovery(struct mmc_softc *sc); static uint32_t mmc_get_bits(uint32_t *bits, int bit_len, int start, int size); static int mmc_highest_voltage(uint32_t ocr); +static bool mmc_host_timing(device_t dev, enum mmc_bus_timing timing); static void mmc_idle_cards(struct mmc_softc *sc); static void mmc_ms_delay(int ms); static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard); static void mmc_power_down(struct mmc_softc *sc); static void mmc_power_up(struct mmc_softc *sc); static void mmc_rescan_cards(struct mmc_softc *sc); +static int mmc_retune(device_t busdev, device_t dev, bool reset); static void mmc_scan(struct mmc_softc *sc); static int mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, uint8_t *res); @@ -185,15 +189,23 @@ static int mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr); static int mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp); static int mmc_set_blocklen(struct mmc_softc *sc, uint32_t len); -static int mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar); +static int mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing); static int mmc_set_power_class(struct mmc_softc *sc, struct mmc_ivars *ivar); static int mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp); static int mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, enum mmc_bus_timing timing); +static int mmc_set_vccq(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing); +static int mmc_switch_to_hs200(struct mmc_softc *sc, struct mmc_ivars *ivar, + uint32_t clock); +static int mmc_switch_to_hs400(struct mmc_softc *sc, struct mmc_ivars *ivar, + uint32_t max_dtr, enum mmc_bus_timing max_timing); static int mmc_test_bus_width(struct mmc_softc *sc); static uint32_t mmc_timing_to_dtr(struct mmc_ivars *ivar, enum mmc_bus_timing timing); static const char *mmc_timing_to_string(enum mmc_bus_timing timing); +static void mmc_update_child_list(struct mmc_softc *sc); static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, uint32_t arg, uint32_t flags, uint32_t *resp, int retries); static int mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req); @@ -237,7 +249,8 @@ mmc_detach(device_t dev) struct mmc_softc *sc = device_get_softc(dev); int err; - if ((err = mmc_delete_cards(sc)) != 0) + err = mmc_delete_cards(sc, true); + if (err != 0) return (err); mmc_power_down(sc); MMC_LOCK_DESTROY(sc); @@ -252,10 +265,21 @@ mmc_suspend(device_t dev) int err; err = bus_generic_suspend(dev); - if (err) + if (err != 0) + return (err); + /* + * We power down with the bus acquired here, mainly so that no device + * is selected any longer and sc->last_rca gets set to 0. Otherwise, + * the deselect as part of the bus acquisition in mmc_scan() may fail + * during resume, as the bus isn't powered up again before later in + * mmc_go_discovery(). + */ + err = mmc_acquire_bus(dev, dev); + if (err != 0) return (err); mmc_power_down(sc); - return (0); + err = mmc_release_bus(dev, dev); + return (err); } static int @@ -272,7 +296,8 @@ mmc_acquire_bus(device_t busdev, device_t dev) { struct mmc_softc *sc; struct mmc_ivars *ivar; - int err, rca; + int err; + uint16_t rca; enum mmc_bus_timing timing; err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), busdev); @@ -296,12 +321,27 @@ mmc_acquire_bus(device_t busdev, device_t dev) rca = ivar->rca; if (sc->last_rca != rca) { if (mmc_select_card(sc, rca) != MMC_ERR_NONE) { - device_printf(sc->dev, "Card at relative " - "address %d failed to select.\n", rca); + device_printf(busdev, "Card at relative " + "address %d failed to select\n", rca); return (ENXIO); } sc->last_rca = rca; timing = mmcbr_get_timing(busdev); + /* + * For eMMC modes, setting/updating bus width and VCCQ + * only really is necessary if there actually is more + * than one device on the bus as generally that already + * had to be done by mmc_calculate_clock() or one of + * its calees. Moreover, setting the bus width anew + * can trigger re-tuning (via a CRC error on the next + * CMD), even if not switching between devices an the + * previously selected one is still tuned. Obviously, + * we need to re-tune the host controller if devices + * are actually switched, though. + */ + if (timing >= bus_timing_mmc_ddr52 && + sc->child_count == 1) + return (0); /* Prepare bus width for the new card. */ if (bootverbose || mmc_debug) { device_printf(busdev, @@ -310,38 +350,34 @@ mmc_acquire_bus(device_t busdev, device_t dev) (ivar->bus_width == bus_width_8) ? 8 : 1, mmc_timing_to_string(timing)); } - if (mmc_set_card_bus_width(sc, ivar) != MMC_ERR_NONE) { - device_printf(sc->dev, "Card at relative " - "address %d failed to set bus width.\n", + if (mmc_set_card_bus_width(sc, ivar, timing) != + MMC_ERR_NONE) { + device_printf(busdev, "Card at relative " + "address %d failed to set bus width\n", rca); return (ENXIO); } - if (isset(&ivar->vccq_120, timing)) - mmcbr_set_vccq(busdev, vccq_120); - else if (isset(&ivar->vccq_180, timing)) - mmcbr_set_vccq(busdev, vccq_180); - else - mmcbr_set_vccq(busdev, vccq_330); - if (mmcbr_switch_vccq(busdev) != 0) { - device_printf(sc->dev, "Failed to set VCCQ " - "for card at relative address %d.\n", rca); + mmcbr_set_bus_width(busdev, ivar->bus_width); + mmcbr_update_ios(busdev); + if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) { + device_printf(busdev, "Failed to set VCCQ " + "for card at relative address %d\n", rca); return (ENXIO); } - if (mmc_set_power_class(sc, ivar) != MMC_ERR_NONE) { - device_printf(sc->dev, "Card at relative " - "address %d failed to set power class.\n", - rca); + if (timing >= bus_timing_mmc_hs200 && + mmc_retune(busdev, dev, true) != 0) { + device_printf(busdev, "Card at relative " + "address %d failed to re-tune\n", rca); return (ENXIO); } - mmcbr_set_bus_width(busdev, ivar->bus_width); - mmcbr_update_ios(busdev); } } else { /* * If there's a card selected, stand down. */ if (sc->last_rca != 0) { - mmc_select_card(sc, 0); + if (mmc_select_card(sc, 0) != MMC_ERR_NONE) + return (ENXIO); sc->last_rca = 0; } } @@ -416,7 +452,7 @@ mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) #endif /* __rtems__ */ req->done = mmc_wakeup; req->done_data = sc; - if (mmc_debug > 1) { + if (__predict_false(mmc_debug > 1)) { device_printf(sc->dev, "REQUEST: CMD%d arg %#x flags %#x", req->cmd->opcode, req->cmd->arg, req->cmd->flags); if (req->cmd->data) { @@ -434,18 +470,66 @@ mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) rtems_binary_semaphore_wait(&req->req_done); rtems_binary_semaphore_destroy(&req->req_done); #endif /* __rtems__ */ - if (mmc_debug > 2 || (mmc_debug > 0 && req->cmd->error != MMC_ERR_NONE)) + if (__predict_false(mmc_debug > 2 || (mmc_debug > 0 && + req->cmd->error != MMC_ERR_NONE))) device_printf(sc->dev, "CMD%d RESULT: %d\n", req->cmd->opcode, req->cmd->error); return (0); } static int -mmc_wait_for_request(device_t brdev, device_t reqdev __unused, - struct mmc_request *req) +mmc_wait_for_request(device_t busdev, device_t dev, struct mmc_request *req) { - struct mmc_softc *sc = device_get_softc(brdev); + struct mmc_softc *sc; + struct mmc_ivars *ivar; + int err, i; + enum mmc_retune_req retune_req; + + sc = device_get_softc(busdev); + KASSERT(sc->owner != NULL, + ("%s: Request from %s without bus being acquired.", __func__, + device_get_nameunit(dev))); + /* + * Unless no device is selected or re-tuning is already ongoing, + * execute re-tuning if a) the bridge is requesting to do so and + * re-tuning hasn't been otherwise paused, or b) if a child asked + * to be re-tuned prior to pausing (see also mmc_retune_pause()). + */ + if (__predict_false(sc->last_rca != 0 && sc->retune_ongoing == 0 && + (((retune_req = mmcbr_get_retune_req(busdev)) != retune_req_none && + sc->retune_paused == 0) || sc->retune_needed == 1))) { + if (__predict_false(mmc_debug > 1)) { + device_printf(busdev, + "Re-tuning with%s circuit reset required\n", + retune_req == retune_req_reset ? "" : "out"); + } + if (device_get_parent(dev) == busdev) + ivar = device_get_ivars(dev); + else { + for (i = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); + if (ivar->rca == sc->last_rca) + break; + } + if (ivar->rca != sc->last_rca) + return (EINVAL); + } + sc->retune_ongoing = 1; + err = mmc_retune(busdev, dev, retune_req == retune_req_reset); + sc->retune_ongoing = 0; + switch (err) { + case MMC_ERR_NONE: + case MMC_ERR_FAILED: /* Re-tune error but still might work */ + break; + case MMC_ERR_BADCRC: /* Switch failure on HS400 recovery */ + return (ENXIO); + case MMC_ERR_INVALID: /* Driver implementation b0rken */ + default: /* Unknown error, should not happen */ + return (EINVAL); + } + sc->retune_needed = 0; + } return (mmc_wait_for_req(sc, req)); } @@ -613,11 +697,14 @@ mmc_power_down(struct mmc_softc *sc) static int mmc_select_card(struct mmc_softc *sc, uint16_t rca) { - int flags; + int err, flags; flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC; - return (mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16, - flags, NULL, CMD_RETRIES)); + sc->retune_paused++; + err = mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16, + flags, NULL, CMD_RETRIES); + sc->retune_paused--; + return (err); } static int @@ -649,7 +736,8 @@ mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, } static int -mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar) +mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing) { struct mmc_command cmd; int err; @@ -682,28 +770,33 @@ mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar) } else { switch (ivar->bus_width) { case bus_width_1: + if (timing == bus_timing_mmc_hs400 || + timing == bus_timing_mmc_hs400es) + return (MMC_ERR_INVALID); value = EXT_CSD_BUS_WIDTH_1; break; case bus_width_4: - switch (mmcbr_get_timing(sc->dev)) { + switch (timing) { case bus_timing_mmc_ddr52: - case bus_timing_mmc_hs200: - case bus_timing_mmc_hs400: - case bus_timing_mmc_hs400es: value = EXT_CSD_BUS_WIDTH_4_DDR; break; + case bus_timing_mmc_hs400: + case bus_timing_mmc_hs400es: + return (MMC_ERR_INVALID); default: value = EXT_CSD_BUS_WIDTH_4; break; } break; case bus_width_8: - switch (mmcbr_get_timing(sc->dev)) { + value = 0; + switch (timing) { + case bus_timing_mmc_hs400es: + value = EXT_CSD_BUS_WIDTH_ES; + /* FALLTHROUGH */ case bus_timing_mmc_ddr52: - case bus_timing_mmc_hs200: case bus_timing_mmc_hs400: - case bus_timing_mmc_hs400es: - value = EXT_CSD_BUS_WIDTH_8_DDR; + value |= EXT_CSD_BUS_WIDTH_8_DDR; break; default: value = EXT_CSD_BUS_WIDTH_8; @@ -828,6 +921,13 @@ mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, case bus_timing_mmc_ddr52: value = EXT_CSD_HS_TIMING_HS; break; + case bus_timing_mmc_hs200: + value = EXT_CSD_HS_TIMING_HS200; + break; + case bus_timing_mmc_hs400: + case bus_timing_mmc_hs400es: + value = EXT_CSD_HS_TIMING_HS400; + break; default: return (MMC_ERR_INVALID); } @@ -844,6 +944,23 @@ mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, return (err); } +static int +mmc_set_vccq(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing) +{ + + if (isset(&ivar->vccq_120, timing)) + mmcbr_set_vccq(sc->dev, vccq_120); + else if (isset(&ivar->vccq_180, timing)) + mmcbr_set_vccq(sc->dev, vccq_180); + else + mmcbr_set_vccq(sc->dev, vccq_330); + if (mmcbr_switch_vccq(sc->dev) != 0) + return (MMC_ERR_INVALID); + else + return (MMC_ERR_NONE); +} + static const uint8_t p8[8] = { 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -1051,7 +1168,7 @@ static const int cur_max[8] = { 1000, 5000, 10000, 25000, 35000, 45000, 800000, 200000 }; -static void +static int mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) { int v; @@ -1092,6 +1209,7 @@ mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); + return (MMC_ERR_NONE); } else if (v == 1) { m = mmc_get_bits(raw_csd, 128, 115, 4); e = mmc_get_bits(raw_csd, 128, 112, 3); @@ -1115,8 +1233,9 @@ mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); - } else - panic("unknown SD CSD version"); + return (MMC_ERR_NONE); + } + return (MMC_ERR_INVALID); } static void @@ -1380,6 +1499,53 @@ mmc_timing_to_string(enum mmc_bus_timing timing) return (""); } +static bool +mmc_host_timing(device_t dev, enum mmc_bus_timing timing) +{ + int host_caps; + + host_caps = mmcbr_get_caps(dev); + +#define HOST_TIMING_CAP(host_caps, cap) ({ \ + bool retval; \ + if (((host_caps) & (cap)) == (cap)) \ + retval = true; \ + else \ + retval = false; \ + retval; \ +}) + + switch (timing) { + case bus_timing_normal: + return (true); + case bus_timing_hs: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_HSPEED)); + case bus_timing_uhs_sdr12: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR12)); + case bus_timing_uhs_sdr25: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR25)); + case bus_timing_uhs_ddr50: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_DDR50)); + case bus_timing_uhs_sdr50: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR50)); + case bus_timing_uhs_sdr104: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR104)); + case bus_timing_mmc_ddr52: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_DDR52)); + case bus_timing_mmc_hs200: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS200)); + case bus_timing_mmc_hs400: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400)); + case bus_timing_mmc_hs400es: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400 | + MMC_CAP_MMC_ENH_STROBE)); + } + +#undef HOST_TIMING_CAP + + return (false); +} + static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard) { @@ -1411,9 +1577,8 @@ mmc_discover_cards(struct mmc_softc *sc) u_char switch_res[64]; uint32_t raw_cid[4]; struct mmc_ivars *ivar = NULL; - device_t *devlist; device_t child; - int devcount, err, host_caps, i, newcard; + int err, host_caps, i, newcard; uint32_t resp, sec_count, status; uint16_t rca = 2; @@ -1421,6 +1586,7 @@ mmc_discover_cards(struct mmc_softc *sc) if (bootverbose || mmc_debug) device_printf(sc->dev, "Probing cards\n"); while (1) { + child = NULL; sc->squelched++; /* Errors are expected, squelch reporting. */ err = mmc_all_send_cid(sc, raw_cid); sc->squelched--; @@ -1431,18 +1597,14 @@ mmc_discover_cards(struct mmc_softc *sc) 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]); + for (i = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); if (memcmp(ivar->raw_cid, raw_cid, sizeof(raw_cid)) == 0) { newcard = 0; break; } } - free(devlist, M_TEMP); if (bootverbose || mmc_debug) { device_printf(sc->dev, "%sard detected (CID %08x%08x%08x%08x)\n", @@ -1465,7 +1627,7 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error getting RCA %d\n", err); - break; + goto free_ivar; } ivar->rca = resp >> 16; /* Get card CSD. */ @@ -1473,7 +1635,7 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error getting CSD %d\n", err); - break; + goto free_ivar; } if (bootverbose || mmc_debug) device_printf(sc->dev, @@ -1481,7 +1643,11 @@ mmc_discover_cards(struct mmc_softc *sc) newcard ? "New c" : "C", ivar->raw_csd[0], ivar->raw_csd[1], ivar->raw_csd[2], ivar->raw_csd[3]); - mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd); + err = mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, "Error decoding CSD\n"); + goto free_ivar; + } ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE; if (ivar->csd.csd_structure > 0) ivar->high_cap = 1; @@ -1494,12 +1660,12 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading card status %d\n", err); - break; + goto free_ivar; } if ((status & R1_CARD_IS_LOCKED) != 0) { device_printf(sc->dev, - "Card is password protected, skipping.\n"); - break; + "Card is password protected, skipping\n"); + goto free_ivar; } /* Get card SCR. Card must be selected to fetch it. */ @@ -1507,13 +1673,13 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error selecting card %d\n", err); - break; + goto free_ivar; } err = mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading SCR %d\n", err); - break; + goto free_ivar; } mmc_app_decode_scr(ivar->raw_scr, &ivar->scr); /* Get card switch capabilities (command class 10). */ @@ -1541,7 +1707,7 @@ mmc_discover_cards(struct mmc_softc *sc) * use from the sd_status is the erase sector size, but * it is still nice to get that right. */ - mmc_select_card(sc, 0); + (void)mmc_select_card(sc, 0); (void)mmc_select_card(sc, ivar->rca); (void)mmc_app_sd_status(sc, ivar->rca, ivar->raw_sd_status); @@ -1551,47 +1717,24 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->erase_sector = 16 << ivar->sd_status.au_size; } - /* Find max supported bus width. */ + /* Find maximum supported bus width. */ if ((host_caps & MMC_CAP_4_BIT_DATA) && (ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) ivar->bus_width = bus_width_4; - /* - * Some cards that report maximum I/O block sizes - * greater than 512 require the block length to be - * set to 512, even though that is supposed to be - * the default. Example: - * - * Transcend 2GB SDSC card, CID: - * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 - */ - if (ivar->csd.read_bl_len != MMC_SECTOR_SIZE || - ivar->csd.write_bl_len != MMC_SECTOR_SIZE) - mmc_set_blocklen(sc, MMC_SECTOR_SIZE); - - mmc_format_card_id_string(ivar); - - if (bootverbose || mmc_debug) - mmc_log_card(sc->dev, ivar, newcard); - if (newcard) { - /* Add device. */ - child = device_add_child(sc->dev, NULL, -1); - device_set_ivars(child, ivar); - } - mmc_select_card(sc, 0); - return; + goto child_common; } ivar->rca = rca++; err = mmc_set_relative_addr(sc, ivar->rca); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error setting RCA %d\n", err); - break; + goto free_ivar; } /* Get card CSD. */ err = mmc_send_csd(sc, ivar->rca, ivar->raw_csd); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error getting CSD %d\n", err); - break; + goto free_ivar; } if (bootverbose || mmc_debug) device_printf(sc->dev, @@ -1610,19 +1753,19 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading card status %d\n", err); - break; + goto free_ivar; } if ((status & R1_CARD_IS_LOCKED) != 0) { device_printf(sc->dev, - "Card is password protected, skipping.\n"); - break; + "Card is password protected, skipping\n"); + goto free_ivar; } err = mmc_select_card(sc, ivar->rca); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error selecting card %d\n", err); - break; + goto free_ivar; } /* Only MMC >= 4.x devices support EXT_CSD. */ @@ -1632,7 +1775,7 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading EXT_CSD %d\n", err); - break; + goto free_ivar; } /* Handle extended capacity from EXT_CSD */ sec_count = ivar->raw_ext_csd[EXT_CSD_SEC_CNT] + @@ -1643,6 +1786,8 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->sec_count = sec_count; ivar->high_cap = 1; } + /* Find maximum supported bus width. */ + ivar->bus_width = mmc_test_bus_width(sc); /* Get device speeds beyond normal mode. */ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_HS_52) != 0) { @@ -1665,6 +1810,50 @@ mmc_discover_cards(struct mmc_softc *sc) setbit(&ivar->timings, bus_timing_mmc_ddr52); setbit(&ivar->vccq_180, bus_timing_mmc_ddr52); } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS200_1_2V) != 0 && + (host_caps & MMC_CAP_SIGNALING_120) != 0) { + setbit(&ivar->timings, bus_timing_mmc_hs200); + setbit(&ivar->vccq_120, bus_timing_mmc_hs200); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS200_1_8V) != 0 && + (host_caps & MMC_CAP_SIGNALING_180) != 0) { + setbit(&ivar->timings, bus_timing_mmc_hs200); + setbit(&ivar->vccq_180, bus_timing_mmc_hs200); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 && + (host_caps & MMC_CAP_SIGNALING_120) != 0 && + ivar->bus_width == bus_width_8) { + setbit(&ivar->timings, bus_timing_mmc_hs400); + setbit(&ivar->vccq_120, bus_timing_mmc_hs400); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 && + (host_caps & MMC_CAP_SIGNALING_180) != 0 && + ivar->bus_width == bus_width_8) { + setbit(&ivar->timings, bus_timing_mmc_hs400); + setbit(&ivar->vccq_180, bus_timing_mmc_hs400); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 && + (ivar->raw_ext_csd[EXT_CSD_STROBE_SUPPORT] & + EXT_CSD_STROBE_SUPPORT_EN) != 0 && + (host_caps & MMC_CAP_SIGNALING_120) != 0 && + ivar->bus_width == bus_width_8) { + setbit(&ivar->timings, bus_timing_mmc_hs400es); + setbit(&ivar->vccq_120, bus_timing_mmc_hs400es); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 && + (ivar->raw_ext_csd[EXT_CSD_STROBE_SUPPORT] & + EXT_CSD_STROBE_SUPPORT_EN) != 0 && + (host_caps & MMC_CAP_SIGNALING_180) != 0 && + ivar->bus_width == bus_width_8) { + setbit(&ivar->timings, bus_timing_mmc_hs400es); + setbit(&ivar->vccq_180, bus_timing_mmc_hs400es); + } /* * Determine generic switch timeout (provided in * units of 10 ms), defaulting to 500 ms. @@ -1673,8 +1862,6 @@ mmc_discover_cards(struct mmc_softc *sc) if (ivar->csd.spec_vers >= 6) ivar->cmd6_time = 10 * ivar->raw_ext_csd[EXT_CSD_GEN_CMD6_TIME]; - /* Find max supported bus width. */ - ivar->bus_width = mmc_test_bus_width(sc); /* Handle HC erase sector size. */ if (ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE] != 0) { ivar->erase_sector = 1024 * @@ -1688,11 +1875,15 @@ mmc_discover_cards(struct mmc_softc *sc) device_printf(sc->dev, "Error setting erase group %d\n", err); - break; + goto free_ivar; } } } + mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid, + ivar->raw_ext_csd[EXT_CSD_REV] >= 5); + +child_common: /* * Some cards that report maximum I/O block sizes greater * than 512 require the block length to be set to 512, even @@ -1705,8 +1896,6 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->csd.write_bl_len != MMC_SECTOR_SIZE) mmc_set_blocklen(sc, MMC_SECTOR_SIZE); - mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid, - ivar->raw_ext_csd[EXT_CSD_REV] >= 5); mmc_format_card_id_string(ivar); if (bootverbose || mmc_debug) @@ -1714,56 +1903,111 @@ mmc_discover_cards(struct mmc_softc *sc) if (newcard) { /* Add device. */ child = device_add_child(sc->dev, NULL, -1); - device_set_ivars(child, ivar); + if (child != NULL) { + device_set_ivars(child, ivar); + sc->child_list = realloc(sc->child_list, + sizeof(device_t) * sc->child_count + 1, + M_DEVBUF, M_WAITOK); + sc->child_list[sc->child_count++] = child; + } else + device_printf(sc->dev, "Error adding child\n"); } - mmc_select_card(sc, 0); + +free_ivar: + if (newcard && child == NULL) + free(ivar, M_DEVBUF); + (void)mmc_select_card(sc, 0); + /* + * Not returning here when one MMC device could no be added + * potentially would mean looping forever when that device + * is broken (in which case it also may impact the remainder + * of the bus anyway, though). + */ + if ((newcard && child == NULL) || + mmcbr_get_mode(sc->dev) == mode_sd) + return; } } static void +mmc_update_child_list(struct mmc_softc *sc) +{ + device_t child; + int i, j; + + if (sc->child_count == 0) { + free(sc->child_list, M_DEVBUF); + return; + } + for (i = j = 0; i < sc->child_count; i++) { + for (;;) { + child = sc->child_list[j++]; + if (child != NULL) + break; + } + if (i != j) + sc->child_list[i] = child; + } + sc->child_list = realloc(sc->child_list, sizeof(device_t) * + sc->child_count, M_DEVBUF, M_WAITOK); +} + +static void mmc_rescan_cards(struct mmc_softc *sc) { struct mmc_ivars *ivar; - device_t *devlist; - int err, i, devcount; + int err, i, j; - if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) - return; - for (i = 0; i < devcount; i++) { - ivar = device_get_ivars(devlist[i]); + for (i = j = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); if (mmc_select_card(sc, ivar->rca) != MMC_ERR_NONE) { if (bootverbose || mmc_debug) device_printf(sc->dev, - "Card at relative address %d lost.\n", + "Card at relative address %d lost\n", ivar->rca); - device_delete_child(sc->dev, devlist[i]); + err = device_delete_child(sc->dev, sc->child_list[i]); + if (err != 0) { + j++; + continue; + } free(ivar, M_DEVBUF); - } + } else + j++; } - free(devlist, M_TEMP); - mmc_select_card(sc, 0); + if (sc->child_count == j) + goto out; + sc->child_count = j; + mmc_update_child_list(sc); +out: + (void)mmc_select_card(sc, 0); } static int -mmc_delete_cards(struct mmc_softc *sc) +mmc_delete_cards(struct mmc_softc *sc, bool final) { struct mmc_ivars *ivar; - device_t *devlist; - int err, i, devcount; + int err, i, j; - if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) - return (err); - for (i = 0; i < devcount; i++) { - ivar = device_get_ivars(devlist[i]); + err = 0; + for (i = j = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); if (bootverbose || mmc_debug) device_printf(sc->dev, - "Card at relative address %d deleted.\n", + "Card at relative address %d deleted\n", ivar->rca); - device_delete_child(sc->dev, devlist[i]); + err = device_delete_child(sc->dev, sc->child_list[i]); + if (err != 0) { + j++; + if (final == false) + continue; + else + break; + } free(ivar, M_DEVBUF); } - free(devlist, M_TEMP); - return (0); + sc->child_count = j; + mmc_update_child_list(sc); + return (err); } static void @@ -1827,7 +2071,7 @@ mmc_go_discovery(struct mmc_softc *sc) mmcbr_get_ocr(dev)); if (mmcbr_get_ocr(dev) == 0) { device_printf(sc->dev, "No compatible cards found on bus\n"); - mmc_delete_cards(sc); + (void)mmc_delete_cards(sc, false); mmc_power_down(sc); return; } @@ -1851,31 +2095,27 @@ mmc_go_discovery(struct mmc_softc *sc) static int mmc_calculate_clock(struct mmc_softc *sc) { - device_t *kids; + device_t dev; struct mmc_ivars *ivar; - int host_caps, i, nkid; + int i; uint32_t dtr, max_dtr; + uint16_t rca; enum mmc_bus_timing max_timing, timing; - bool changed; + bool changed, hs400; - max_dtr = mmcbr_get_f_max(sc->dev); - host_caps = mmcbr_get_caps(sc->dev); - if ((host_caps & MMC_CAP_MMC_DDR52) != 0) - max_timing = bus_timing_mmc_ddr52; - else if ((host_caps & MMC_CAP_HSPEED) != 0) - max_timing = bus_timing_hs; - else - max_timing = bus_timing_normal; - if (device_get_children(sc->dev, &kids, &nkid) != 0) - panic("can't get children"); + dev = sc->dev; + max_dtr = mmcbr_get_f_max(dev); + max_timing = bus_timing_max; do { changed = false; - for (i = 0; i < nkid; i++) { - ivar = device_get_ivars(kids[i]); - if (isclr(&ivar->timings, max_timing)) { - for (timing = max_timing; timing >= + for (i = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); + if (isclr(&ivar->timings, max_timing) || + !mmc_host_timing(dev, max_timing)) { + for (timing = max_timing - 1; timing >= bus_timing_normal; timing--) { - if (isset(&ivar->timings, timing)) { + if (isset(&ivar->timings, timing) && + mmc_host_timing(dev, timing)) { max_timing = timing; break; } @@ -1889,38 +2129,316 @@ mmc_calculate_clock(struct mmc_softc *sc) } } } while (changed == true); + if (bootverbose || mmc_debug) { - device_printf(sc->dev, + device_printf(dev, "setting transfer rate to %d.%03dMHz (%s timing)\n", max_dtr / 1000000, (max_dtr / 1000) % 1000, mmc_timing_to_string(max_timing)); } - for (i = 0; i < nkid; i++) { - ivar = device_get_ivars(kids[i]); + + /* + * HS400 must be tuned in HS200 mode, so in case of HS400 we begin + * with HS200 following the sequence as described in "6.6.2.2 HS200 + * timing mode selection" of the eMMC specification v5.1, too, and + * switch to max_timing later. HS400ES requires no tuning and, thus, + * can be switch to directly, but requires the same detour via high + * speed mode as does HS400 (see mmc_switch_to_hs400()). + */ + hs400 = max_timing == bus_timing_mmc_hs400; + timing = hs400 == true ? bus_timing_mmc_hs200 : max_timing; + for (i = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); if ((ivar->timings & ~(1 << bus_timing_normal)) == 0) continue; - if (mmc_select_card(sc, ivar->rca) != MMC_ERR_NONE || - mmc_set_timing(sc, ivar, max_timing) != MMC_ERR_NONE) - device_printf(sc->dev, "Card at relative address %d " - "failed to set timing.\n", ivar->rca); + + rca = ivar->rca; + if (mmc_select_card(sc, rca) != MMC_ERR_NONE) { + device_printf(dev, "Card at relative address %d " + "failed to select\n", rca); + continue; + } + + if (timing == bus_timing_mmc_hs200 || /* includes HS400 */ + timing == bus_timing_mmc_hs400es) { + if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) { + device_printf(dev, "Failed to set VCCQ for " + "card at relative address %d\n", rca); + continue; + } + } + + if (timing == bus_timing_mmc_hs200) { /* includes HS400 */ + /* Set bus width (required for initial tuning). */ + if (mmc_set_card_bus_width(sc, ivar, timing) != + MMC_ERR_NONE) { + device_printf(dev, "Card at relative address " + "%d failed to set bus width\n", rca); + continue; + } + mmcbr_set_bus_width(dev, ivar->bus_width); + mmcbr_update_ios(dev); + } else if (timing == bus_timing_mmc_hs400es) { + if (mmc_switch_to_hs400(sc, ivar, max_dtr, timing) != + MMC_ERR_NONE) { + device_printf(dev, "Card at relative address " + "%d failed to set %s timing\n", rca, + mmc_timing_to_string(timing)); + continue; + } + goto power_class; + } + + if (mmc_set_timing(sc, ivar, timing) != MMC_ERR_NONE) { + device_printf(dev, "Card at relative address %d " + "failed to set %s timing\n", rca, + mmc_timing_to_string(timing)); + continue; + } + + if (timing == bus_timing_mmc_ddr52) { + /* + * Set EXT_CSD_BUS_WIDTH_n_DDR in EXT_CSD_BUS_WIDTH + * (must be done after switching to EXT_CSD_HS_TIMING). + */ + if (mmc_set_card_bus_width(sc, ivar, timing) != + MMC_ERR_NONE) { + device_printf(dev, "Card at relative address " + "%d failed to set bus width\n", rca); + continue; + } + mmcbr_set_bus_width(dev, ivar->bus_width); + mmcbr_update_ios(dev); + if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) { + device_printf(dev, "Failed to set VCCQ for " + "card at relative address %d\n", rca); + continue; + } + } + + /* Set clock (must be done before initial tuning). */ + mmcbr_set_clock(dev, max_dtr); + mmcbr_update_ios(dev); + + if (mmcbr_tune(dev, hs400) != 0) { + device_printf(dev, "Card at relative address %d " + "failed to execute initial tuning\n", rca); + continue; + } + + if (hs400 == true && mmc_switch_to_hs400(sc, ivar, max_dtr, + max_timing) != MMC_ERR_NONE) { + device_printf(dev, "Card at relative address %d " + "failed to set %s timing\n", rca, + mmc_timing_to_string(max_timing)); + continue; + } + +power_class: + if (mmc_set_power_class(sc, ivar) != MMC_ERR_NONE) { + device_printf(dev, "Card at relative address %d " + "failed to set power class\n", rca); + } } - mmc_select_card(sc, 0); - free(kids, M_TEMP); - mmcbr_set_clock(sc->dev, max_dtr); - mmcbr_update_ios(sc->dev); + (void)mmc_select_card(sc, 0); return (max_dtr); } +/* + * Switch from HS200 to HS400 (either initially or for re-tuning) or directly + * to HS400ES. This follows the sequences described in "6.6.2.3 HS400 timing + * mode selection" of the eMMC specification v5.1. + */ +static int +mmc_switch_to_hs400(struct mmc_softc *sc, struct mmc_ivars *ivar, + uint32_t clock, enum mmc_bus_timing max_timing) +{ + device_t dev; + int err; + uint16_t rca; + + dev = sc->dev; + rca = ivar->rca; + + /* + * Both clock and timing must be set as appropriate for high speed + * before eventually switching to HS400/HS400ES; mmc_set_timing() + * will issue mmcbr_update_ios(). + */ + mmcbr_set_clock(dev, ivar->hs_tran_speed); + err = mmc_set_timing(sc, ivar, bus_timing_hs); + if (err != MMC_ERR_NONE) + return (err); + + /* + * Set EXT_CSD_BUS_WIDTH_8_DDR in EXT_CSD_BUS_WIDTH (and additionally + * EXT_CSD_BUS_WIDTH_ES for HS400ES). + */ + err = mmc_set_card_bus_width(sc, ivar, max_timing); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_bus_width(dev, ivar->bus_width); + mmcbr_update_ios(dev); + + /* Finally, switch to HS400/HS400ES mode. */ + err = mmc_set_timing(sc, ivar, max_timing); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_clock(dev, clock); + mmcbr_update_ios(dev); + return (MMC_ERR_NONE); +} + +/* + * Switch from HS400 to HS200 (for re-tuning). + */ +static int +mmc_switch_to_hs200(struct mmc_softc *sc, struct mmc_ivars *ivar, + uint32_t clock) +{ + device_t dev; + int err; + uint16_t rca; + + dev = sc->dev; + rca = ivar->rca; + + /* + * Both clock and timing must initially be set as appropriate for + * DDR52 before eventually switching to HS200; mmc_set_timing() + * will issue mmcbr_update_ios(). + */ + mmcbr_set_clock(dev, ivar->hs_tran_speed); + err = mmc_set_timing(sc, ivar, bus_timing_mmc_ddr52); + if (err != MMC_ERR_NONE) + return (err); + + /* + * Next, switch to high speed. Thus, clear EXT_CSD_BUS_WIDTH_n_DDR + * in EXT_CSD_BUS_WIDTH and update bus width and timing in ios. + */ + err = mmc_set_card_bus_width(sc, ivar, bus_timing_hs); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_bus_width(dev, ivar->bus_width); + mmcbr_set_timing(sc->dev, bus_timing_hs); + mmcbr_update_ios(dev); + + /* Finally, switch to HS200 mode. */ + err = mmc_set_timing(sc, ivar, bus_timing_mmc_hs200); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_clock(dev, clock); + mmcbr_update_ios(dev); + return (MMC_ERR_NONE); +} + +static int +mmc_retune(device_t busdev, device_t dev, bool reset) +{ + struct mmc_softc *sc; + struct mmc_ivars *ivar; + int err; + uint32_t clock; + enum mmc_bus_timing timing; + + if (device_get_parent(dev) != busdev) + return (MMC_ERR_INVALID); + + sc = device_get_softc(busdev); + if (sc->retune_needed != 1 && sc->retune_paused != 0) + return (MMC_ERR_INVALID); + + timing = mmcbr_get_timing(busdev); + if (timing == bus_timing_mmc_hs400) { + /* + * Controllers use the data strobe line to latch data from + * the devices in HS400 mode so periodic re-tuning isn't + * expected to be required, i. e. only if a CRC or tuning + * error is signaled to the bridge. In these latter cases + * we are asked to reset the tuning circuit and need to do + * the switch timing dance. + */ + if (reset == false) + return (0); + ivar = device_get_ivars(dev); + clock = mmcbr_get_clock(busdev); + if (mmc_switch_to_hs200(sc, ivar, clock) != MMC_ERR_NONE) + return (MMC_ERR_BADCRC); + } + err = mmcbr_retune(busdev, reset); + if (err != 0 && timing == bus_timing_mmc_hs400) + return (MMC_ERR_BADCRC); + switch (err) { + case 0: + break; + case EIO: + return (MMC_ERR_FAILED); + default: + return (MMC_ERR_INVALID); + } + if (timing == bus_timing_mmc_hs400) { + if (mmc_switch_to_hs400(sc, ivar, clock, timing) != + MMC_ERR_NONE) + return (MMC_ERR_BADCRC); + } + return (MMC_ERR_NONE); +} + +static void +mmc_retune_pause(device_t busdev, device_t dev, bool retune) +{ + struct mmc_softc *sc; + + sc = device_get_softc(busdev); + KASSERT(device_get_parent(dev) == busdev, + ("%s: %s is not a child of %s", __func__, device_get_nameunit(dev), + device_get_nameunit(busdev))); + KASSERT(sc->owner != NULL, + ("%s: Request from %s without bus being acquired.", __func__, + device_get_nameunit(dev))); + + if (retune == true && sc->retune_paused == 0) + sc->retune_needed = 1; + sc->retune_paused++; +} + +static void +mmc_retune_unpause(device_t busdev, device_t dev) +{ + struct mmc_softc *sc; + + sc = device_get_softc(busdev); + KASSERT(device_get_parent(dev) == busdev, + ("%s: %s is not a child of %s", __func__, device_get_nameunit(dev), + device_get_nameunit(busdev))); + KASSERT(sc->owner != NULL, + ("%s: Request from %s without bus being acquired.", __func__, + device_get_nameunit(dev))); + KASSERT(sc->retune_paused != 0, + ("%s: Re-tune pause count already at 0", __func__)); + + sc->retune_paused--; +} + static void mmc_scan(struct mmc_softc *sc) { device_t dev = sc->dev; + int err; - mmc_acquire_bus(dev, dev); + err = mmc_acquire_bus(dev, dev); + if (err != 0) { + device_printf(dev, "Failed to acquire bus for scanning\n"); + return; + } mmc_go_discovery(sc); - mmc_release_bus(dev, dev); - - bus_generic_attach(dev); + err = mmc_release_bus(dev, dev); + if (err != 0) { + device_printf(dev, "Failed to release bus after scanning\n"); + return; + } + (void)bus_generic_attach(dev); } static int @@ -2019,6 +2537,8 @@ static device_method_t mmc_methods[] = { DEVMETHOD(bus_child_location_str, mmc_child_location_str), /* MMC Bus interface */ + DEVMETHOD(mmcbus_retune_pause, mmc_retune_pause), + DEVMETHOD(mmcbus_retune_unpause, mmc_retune_unpause), DEVMETHOD(mmcbus_wait_for_request, mmc_wait_for_request), DEVMETHOD(mmcbus_acquire_bus, mmc_acquire_bus), DEVMETHOD(mmcbus_release_bus, mmc_release_bus), diff --git a/freebsd/sys/dev/mmc/mmc_ioctl.h b/freebsd/sys/dev/mmc/mmc_ioctl.h index 97cff068..e633fec9 100644 --- a/freebsd/sys/dev/mmc/mmc_ioctl.h +++ b/freebsd/sys/dev/mmc/mmc_ioctl.h @@ -54,7 +54,7 @@ struct mmc_ioc_multi_cmd { #define MMC_IOC_BASE 'M' #define MMC_IOC_CMD _IOWR(MMC_IOC_BASE, 0, struct mmc_ioc_cmd) -#define MMC_IOC_CMD_MULTI _IOWR(MMC_IOC_BASE, 1, struct mmc_ioc_multi_cmd) +#define MMC_IOC_MULTI_CMD _IOWR(MMC_IOC_BASE, 1, struct mmc_ioc_multi_cmd) /* Maximum accepted data transfer size */ #define MMC_IOC_MAX_BYTES (512 * 256) diff --git a/freebsd/sys/dev/mmc/mmc_private.h b/freebsd/sys/dev/mmc/mmc_private.h index bbca0c60..633d0784 100644 --- a/freebsd/sys/dev/mmc/mmc_private.h +++ b/freebsd/sys/dev/mmc/mmc_private.h @@ -60,9 +60,14 @@ struct mmc_softc { struct mtx sc_mtx; struct intr_config_hook config_intrhook; device_t owner; - uint32_t last_rca; - int squelched; /* suppress reporting of (expected) errors */ - int log_count; + device_t *child_list; + int child_count; + uint16_t last_rca; + uint16_t retune_paused; + uint8_t retune_needed; + uint8_t retune_ongoing; + uint16_t squelched; /* suppress reporting of (expected) errors */ + int log_count; struct timeval log_time; }; diff --git a/freebsd/sys/dev/mmc/mmc_subr.c b/freebsd/sys/dev/mmc/mmc_subr.c index f76e9637..006354ba 100644 --- a/freebsd/sys/dev/mmc/mmc_subr.c +++ b/freebsd/sys/dev/mmc/mmc_subr.c @@ -140,7 +140,6 @@ mmc_wait_for_app_cmd(device_t brdev, device_t reqdev, uint16_t rca, sc->squelched--; if (err != MMC_ERR_NONE && brdev == reqdev) { - sc = device_get_softc(brdev); if (sc->squelched == 0 && ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS)) { device_printf(sc->dev, "ACMD%d failed, RESULT: %d\n", @@ -156,10 +155,13 @@ mmc_switch(device_t brdev, device_t reqdev, uint16_t rca, uint8_t set, uint8_t index, uint8_t value, u_int timeout, bool status) { struct mmc_command cmd; + struct mmc_softc *sc; int err; KASSERT(timeout != 0, ("%s: no timeout", __func__)); + sc = device_get_softc(brdev); + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SWITCH_FUNC; cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | (index << 16) | (value << 8) | @@ -174,10 +176,19 @@ mmc_switch(device_t brdev, device_t reqdev, uint16_t rca, uint8_t set, cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; else cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + /* + * Pause re-tuning so it won't interfere with the busy state and also + * so that the result of CMD13 will always refer to switching rather + * than to a tuning command that may have snuck in between. + */ + sc->retune_paused++; err = mmc_wait_for_cmd(brdev, reqdev, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE || status == false) - return (err); - return (mmc_switch_status(brdev, reqdev, rca, timeout)); + goto out; + err = mmc_switch_status(brdev, reqdev, rca, timeout); +out: + sc->retune_paused--; + return (err); } int @@ -194,6 +205,7 @@ mmc_switch_status(device_t brdev, device_t reqdev, uint16_t rca, u_int timeout) * type MMC_CAP_WAIT_WHILE_BUSY will issue mmc_send_status() only * once and then exit the loop. */ + end.tv_sec = end.tv_usec = 0; for (;;) { err = mmc_send_status(brdev, reqdev, rca, &status); if (err != MMC_ERR_NONE) @@ -210,7 +222,7 @@ mmc_switch_status(device_t brdev, device_t reqdev, uint16_t rca, u_int timeout) break; } } - if (err == MMC_ERR_NONE && R1_CURRENT_STATE(status) == R1_SWITCH_ERROR) + if (err == MMC_ERR_NONE && (status & R1_SWITCH_ERROR) != 0) return (MMC_ERR_FAILED); return (err); } diff --git a/freebsd/sys/dev/mmc/mmcbrvar.h b/freebsd/sys/dev/mmc/mmcbrvar.h index 77c304b4..c70af92a 100644 --- a/freebsd/sys/dev/mmc/mmcbrvar.h +++ b/freebsd/sys/dev/mmc/mmcbrvar.h @@ -70,6 +70,7 @@ enum mmcbr_device_ivars { MMCBR_IVAR_MODE, MMCBR_IVAR_OCR, MMCBR_IVAR_POWER_MODE, + MMCBR_IVAR_RETUNE_REQ, MMCBR_IVAR_VDD, MMCBR_IVAR_VCCQ, MMCBR_IVAR_CAPS, @@ -94,6 +95,7 @@ MMCBR_ACCESSOR(host_ocr, HOST_OCR, int) MMCBR_ACCESSOR(mode, MODE, int) MMCBR_ACCESSOR(ocr, OCR, int) MMCBR_ACCESSOR(power_mode, POWER_MODE, int) +MMCBR_ACCESSOR(retune_req, RETUNE_REQ, int) MMCBR_ACCESSOR(vdd, VDD, int) MMCBR_ACCESSOR(vccq, VCCQ, int) MMCBR_ACCESSOR(caps, CAPS, int) @@ -109,6 +111,20 @@ mmcbr_update_ios(device_t dev) } static int __inline +mmcbr_tune(device_t dev, bool hs400) +{ + + return (MMCBR_TUNE(device_get_parent(dev), dev, hs400)); +} + +static int __inline +mmcbr_retune(device_t dev, bool reset) +{ + + return (MMCBR_RETUNE(device_get_parent(dev), dev, reset)); +} + +static int __inline mmcbr_switch_vccq(device_t dev) { diff --git a/freebsd/sys/dev/mmc/mmcreg.h b/freebsd/sys/dev/mmc/mmcreg.h index 39680ad6..80f433c1 100644 --- a/freebsd/sys/dev/mmc/mmcreg.h +++ b/freebsd/sys/dev/mmc/mmcreg.h @@ -1,6 +1,7 @@ /*- * Copyright (c) 2006 M. Warner Losh. All rights reserved. * Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org> + * Copyright (c) 2015-2016 Ilya Bakulin <kibab@FreeBSD.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -159,6 +160,34 @@ struct mmc_command { #define R1_STATE_PRG 7 #define R1_STATE_DIS 8 +/* R4 response (SDIO) */ +#define R4_IO_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x3) +#define R4_IO_MEM_PRESENT (0x1<<27) +#define R4_IO_OCR_MASK 0x00fffff0 + +/* + * R5 responses + * + * Types (per SD 2.0 standard) + *e : error bit + *s : status bit + *r : detected and set for the actual command response + *x : Detected and set during command execution. The host can get + * the status by issuing a command with R1 response. + * + * Clear Condition (per SD 2.0 standard) + *a : according to the card current state. + *b : always related to the previous command. reception of a valid + * command will clear it (with a delay of one command). + *c : clear by read + */ +#define R5_COM_CRC_ERROR (1u << 15)/* er, b */ +#define R5_ILLEGAL_COMMAND (1u << 14)/* er, b */ +#define R5_IO_CURRENT_STATE_MASK (3u << 12)/* s, b */ +#define R5_IO_CURRENT_STATE(x) (((x) & R5_IO_CURRENT_STATE_MASK) >> 12) +#define R5_ERROR (1u << 11)/* erx, c */ +#define R5_FUNCTION_NUMBER (1u << 9)/* er, c */ +#define R5_OUT_OF_RANGE (1u << 8)/* er, c */ struct mmc_data { size_t len; /* size of the data */ size_t xfer_len; @@ -176,10 +205,12 @@ struct mmc_request { struct mmc_command *stop; void (*done)(struct mmc_request *); /* Completion function */ void *done_data; /* requestor set data */ -#ifndef __rtems__ uint32_t flags; +#ifndef __rtems__ #define MMC_REQ_DONE 1 -#else /* __rtems__ */ +#endif /* __rtems__ */ +#define MMC_TUNE_DONE 2 +#ifdef __rtems__ rtems_binary_semaphore req_done; #endif /* __rtems__ */ }; @@ -194,6 +225,7 @@ struct mmc_request { #define SD_SEND_RELATIVE_ADDR 3 #define MMC_SET_DSR 4 #define MMC_SLEEP_AWAKE 5 +#define IO_SEND_OP_COND 5 #define MMC_SWITCH_FUNC 6 #define MMC_SWITCH_FUNC_CMDS 0 #define MMC_SWITCH_FUNC_SET 1 @@ -276,7 +308,31 @@ struct mmc_request { /* Class 9: I/O cards (sd) */ #define SD_IO_RW_DIRECT 52 +/* CMD52 arguments */ +#define SD_ARG_CMD52_READ (0<<31) +#define SD_ARG_CMD52_WRITE (1<<31) +#define SD_ARG_CMD52_FUNC_SHIFT 28 +#define SD_ARG_CMD52_FUNC_MASK 0x7 +#define SD_ARG_CMD52_EXCHANGE (1<<27) +#define SD_ARG_CMD52_REG_SHIFT 9 +#define SD_ARG_CMD52_REG_MASK 0x1ffff +#define SD_ARG_CMD52_DATA_SHIFT 0 +#define SD_ARG_CMD52_DATA_MASK 0xff +#define SD_R5_DATA(resp) ((resp)[0] & 0xff) + #define SD_IO_RW_EXTENDED 53 +/* CMD53 arguments */ +#define SD_ARG_CMD53_READ (0<<31) +#define SD_ARG_CMD53_WRITE (1<<31) +#define SD_ARG_CMD53_FUNC_SHIFT 28 +#define SD_ARG_CMD53_FUNC_MASK 0x7 +#define SD_ARG_CMD53_BLOCK_MODE (1<<27) +#define SD_ARG_CMD53_INCREMENT (1<<26) +#define SD_ARG_CMD53_REG_SHIFT 9 +#define SD_ARG_CMD53_REG_MASK 0x1ffff +#define SD_ARG_CMD53_LENGTH_SHIFT 0 +#define SD_ARG_CMD53_LENGTH_MASK 0x1ff +#define SD_ARG_CMD53_LENGTH_MAX 64 /* XXX should be 511? */ /* Class 10: Switch function commands */ #define SD_SWITCH_FUNC 6 @@ -384,8 +440,8 @@ struct mmc_request { #define EXT_CSD_HS_TIMING_BC 0 #define EXT_CSD_HS_TIMING_HS 1 -#define EXT_CSD_HS_TIMING_DDR200 2 -#define EXT_CSD_HS_TIMING_DDR400 3 +#define EXT_CSD_HS_TIMING_HS200 2 +#define EXT_CSD_HS_TIMING_HS400 3 #define EXT_CSD_HS_TIMING_DRV_STR_SHIFT 4 #define EXT_CSD_POWER_CLASS_8BIT_MASK 0xf0 @@ -401,7 +457,6 @@ struct mmc_request { #define EXT_CSD_CARD_TYPE_HS200_1_2V 0x0020 #define EXT_CSD_CARD_TYPE_HS400_1_8V 0x0040 #define EXT_CSD_CARD_TYPE_HS400_1_2V 0x0080 -#define EXT_CSD_CARD_TYPE_HS400ES 0x0100 #define EXT_CSD_BUS_WIDTH_1 0 #define EXT_CSD_BUS_WIDTH_4 1 @@ -410,6 +465,8 @@ struct mmc_request { #define EXT_CSD_BUS_WIDTH_8_DDR 6 #define EXT_CSD_BUS_WIDTH_ES 0x80 +#define EXT_CSD_STROBE_SUPPORT_EN 0x01 + #define MMC_TYPE_HS_26_MAX 26000000 #define MMC_TYPE_HS_52_MAX 52000000 #define MMC_TYPE_DDR52_MAX 52000000 @@ -447,6 +504,54 @@ struct mmc_request { /* Specifications require 400 kHz max. during ID phase. */ #define SD_MMC_CARD_ID_FREQUENCY 400000 +/* + * SDIO Direct & Extended I/O + */ +#define SD_IO_RW_WR (1u << 31) +#define SD_IO_RW_FUNC(x) (((x) & 0x7) << 28) +#define SD_IO_RW_RAW (1u << 27) +#define SD_IO_RW_INCR (1u << 26) +#define SD_IO_RW_ADR(x) (((x) & 0x1FFFF) << 9) +#define SD_IO_RW_DAT(x) (((x) & 0xFF) << 0) +#define SD_IO_RW_LEN(x) (((x) & 0xFF) << 0) + +#define SD_IOE_RW_LEN(x) (((x) & 0x1FF) << 0) +#define SD_IOE_RW_BLK (1u << 27) + +/* Card Common Control Registers (CCCR) */ +#define SD_IO_CCCR_START 0x00000 +#define SD_IO_CCCR_SIZE 0x100 +#define SD_IO_CCCR_FN_ENABLE 0x02 +#define SD_IO_CCCR_FN_READY 0x03 +#define SD_IO_CCCR_INT_ENABLE 0x04 +#define SD_IO_CCCR_INT_PENDING 0x05 +#define SD_IO_CCCR_CTL 0x06 +#define CCCR_CTL_RES (1<<3) +#define SD_IO_CCCR_BUS_WIDTH 0x07 +#define CCCR_BUS_WIDTH_4 (1<<1) +#define CCCR_BUS_WIDTH_1 (1<<0) +#define SD_IO_CCCR_CARDCAP 0x08 +#define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */ + +/* Function Basic Registers (FBR) */ +#define SD_IO_FBR_START 0x00100 +#define SD_IO_FBR_SIZE 0x00700 + +/* Card Information Structure (CIS) */ +#define SD_IO_CIS_START 0x01000 +#define SD_IO_CIS_SIZE 0x17000 + +/* CIS tuple codes (based on PC Card 16) */ +#define SD_IO_CISTPL_VERS_1 0x15 +#define SD_IO_CISTPL_MANFID 0x20 +#define SD_IO_CISTPL_FUNCID 0x21 +#define SD_IO_CISTPL_FUNCE 0x22 +#define SD_IO_CISTPL_END 0xff + +/* CISTPL_FUNCID codes */ +/* OpenBSD incorrectly defines 0x0c as FUNCTION_WLAN */ +/* #define SDMMC_FUNCTION_WLAN 0x0c */ + /* OCR bits */ /* @@ -562,6 +667,10 @@ struct mmc_sd_status #define MMC_PART_GP_MAX 4 #define MMC_PART_MAX 8 +#define MMC_TUNING_MAX 64 /* Maximum tuning iterations */ +#define MMC_TUNING_LEN 64 /* Size of tuning data */ +#define MMC_TUNING_LEN_HS200 128 /* Size of tuning data in HS200 mode */ + /* * Older versions of the MMC standard had a variable sector size. However, * I've been able to find no old MMC or SD cards that have a non 512 diff --git a/freebsd/sys/dev/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c index 5066e250..195feae2 100644 --- a/freebsd/sys/dev/mmc/mmcsd.c +++ b/freebsd/sys/dev/mmc/mmcsd.c @@ -108,7 +108,8 @@ __FBSDID("$FreeBSD$"); struct mmcsd_softc; struct mmcsd_part { - struct mtx part_mtx; + struct mtx disk_mtx; + struct mtx ioctl_mtx; struct mmcsd_softc *sc; #ifndef __rtems__ struct disk *disk; @@ -120,6 +121,7 @@ struct mmcsd_part { u_int type; int running; int suspend; + int ioctl; bool ro; char name[MMCSD_PART_NAMELEN]; }; @@ -129,6 +131,9 @@ struct mmcsd_softc { device_t mmcbr; struct mmcsd_part *part[MMC_PART_MAX]; enum mmc_card_mode mode; + u_int max_data; /* Maximum data size [blocks] */ + u_int erase_sector; /* Device native erase sector size [blocks] */ + uint8_t high_cap; /* High Capacity device (block addressed) */ uint8_t part_curr; /* Partition currently switched to */ uint8_t ext_csd[MMC_EXTCSD_SIZE]; uint16_t rca; @@ -199,15 +204,25 @@ static int mmcsd_slicer(device_t dev, const char *provider, static int mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca, u_int part); -#define MMCSD_PART_LOCK(_part) mtx_lock(&(_part)->part_mtx) -#define MMCSD_PART_UNLOCK(_part) mtx_unlock(&(_part)->part_mtx) -#define MMCSD_PART_LOCK_INIT(_part) \ - mtx_init(&(_part)->part_mtx, (_part)->name, "mmcsd part", MTX_DEF) -#define MMCSD_PART_LOCK_DESTROY(_part) mtx_destroy(&(_part)->part_mtx); -#define MMCSD_PART_ASSERT_LOCKED(_part) \ - mtx_assert(&(_part)->part_mtx, MA_OWNED); -#define MMCSD_PART_ASSERT_UNLOCKED(_part) \ - mtx_assert(&(_part)->part_mtx, MA_NOTOWNED); +#define MMCSD_DISK_LOCK(_part) mtx_lock(&(_part)->disk_mtx) +#define MMCSD_DISK_UNLOCK(_part) mtx_unlock(&(_part)->disk_mtx) +#define MMCSD_DISK_LOCK_INIT(_part) \ + mtx_init(&(_part)->disk_mtx, (_part)->name, "mmcsd disk", MTX_DEF) +#define MMCSD_DISK_LOCK_DESTROY(_part) mtx_destroy(&(_part)->disk_mtx); +#define MMCSD_DISK_ASSERT_LOCKED(_part) \ + mtx_assert(&(_part)->disk_mtx, MA_OWNED); +#define MMCSD_DISK_ASSERT_UNLOCKED(_part) \ + mtx_assert(&(_part)->disk_mtx, MA_NOTOWNED); + +#define MMCSD_IOCTL_LOCK(_part) mtx_lock(&(_part)->ioctl_mtx) +#define MMCSD_IOCTL_UNLOCK(_part) mtx_unlock(&(_part)->ioctl_mtx) +#define MMCSD_IOCTL_LOCK_INIT(_part) \ + mtx_init(&(_part)->ioctl_mtx, (_part)->name, "mmcsd IOCTL", MTX_DEF) +#define MMCSD_IOCTL_LOCK_DESTROY(_part) mtx_destroy(&(_part)->ioctl_mtx); +#define MMCSD_IOCTL_ASSERT_LOCKED(_part) \ + mtx_assert(&(_part)->ioctl_mtx, MA_OWNED); +#define MMCSD_IOCLT_ASSERT_UNLOCKED(_part) \ + mtx_assert(&(_part)->ioctl_mtx, MA_NOTOWNED); static int mmcsd_probe(device_t dev) @@ -277,7 +292,7 @@ rtems_bsd_mmcsd_disk_read_write(struct mmcsd_part *part, rtems_blkdev_request *b data_flags = MMC_DATA_READ; } - MMCSD_PART_LOCK(part); + MMCSD_DISK_LOCK(part); for (i = 0; i < buffer_count; ++i) { rtems_blkdev_sg_buffer *sg = &blkreq->bufs [i]; @@ -354,7 +369,7 @@ rtems_bsd_mmcsd_disk_read_write(struct mmcsd_part *part, rtems_blkdev_request *b error: - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); rtems_blkdev_request_done(blkreq, status_code); @@ -439,6 +454,18 @@ mmcsd_attach(device_t dev) sc->dev = dev; sc->mmcbr = mmcbr = device_get_parent(dev); sc->mode = mmcbr_get_mode(mmcbr); + /* + * Note that in principle with an SDHCI-like re-tuning implementation, + * the maximum data size can change at runtime due to a device removal/ + * insertion that results in switches to/from a transfer mode involving + * re-tuning, iff there are multiple devices on a given bus. Until now + * mmc(4) lacks support for rescanning already attached buses, however, + * and sdhci(4) to date has no support for shared buses in the first + * place either. + */ + sc->max_data = mmc_get_max_data(dev); + sc->erase_sector = mmc_get_erase_sector(dev); + sc->high_cap = mmc_get_high_cap(dev); sc->rca = mmc_get_rca(dev); /* Only MMC >= 4.x devices support EXT_CSD. */ @@ -492,7 +519,7 @@ mmcsd_attach(device_t dev) (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) + (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) + (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24)) * - (mmc_get_high_cap(dev) ? MMC_SECTOR_SIZE : 1); + (sc->high_cap != 0 ? MMC_SECTOR_SIZE : 1); } else if (bootverbose) device_printf(dev, "enhanced user data area spans entire device\n"); @@ -505,7 +532,7 @@ mmcsd_attach(device_t dev) ro = mmc_get_read_only(dev); mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_DEFAULT, "mmcsd", device_get_unit(dev), mmc_get_media_size(dev) * sector_size, - mmc_get_erase_sector(dev) * sector_size, ro); + sc->erase_sector * sector_size, ro); if (mmc_get_spec_vers(dev) < 3) return (0); @@ -644,7 +671,16 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, part->ro = ro; snprintf(part->name, sizeof(part->name), name, device_get_unit(dev)); - /* For the RPMB partition, allow IOCTL access only. */ + MMCSD_IOCTL_LOCK_INIT(part); + + /* + * For the RPMB partition, allow IOCTL access only. + * NB: If ever attaching RPMB partitions to disk(9), the re-tuning + * implementation and especially its pausing need to be revisited, + * because then re-tuning requests may be issued by the IOCTL half + * of this driver while re-tuning is already paused by the disk(9) + * one and vice versa. + */ if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) { make_dev_args_init(&args); args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; @@ -659,7 +695,7 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, return; } } else { - MMCSD_PART_LOCK_INIT(part); + MMCSD_DISK_LOCK_INIT(part); #ifndef __rtems__ d = part->disk = disk_alloc(); @@ -672,7 +708,7 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, d->d_name = part->name; d->d_drv1 = part; d->d_sectorsize = mmc_get_sector_size(dev); - d->d_maxsize = mmc_get_max_data(dev) * d->d_sectorsize; + d->d_maxsize = sc->max_data * d->d_sectorsize; d->d_mediasize = media_size; d->d_stripesize = erase_size; d->d_unit = cnt; @@ -704,7 +740,7 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, part->name, cnt, bytes, unit, mmc_get_card_id_string(dev), ro ? " (read-only)" : "", device_get_nameunit(mmcbr), speed / 1000000, (speed / 100000) % 10, - mmcsd_bus_bit_width(dev), mmc_get_max_data(dev)); + mmcsd_bus_bit_width(dev), sc->max_data); } else if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) { printf("%s: %ju%sB partion %d%s at %s\n", part->name, bytes, unit, type, ro ? " (read-only)" : "", @@ -795,19 +831,27 @@ mmcsd_detach(device_t dev) for (i = 0; i < MMC_PART_MAX; i++) { part = sc->part[i]; - if (part != NULL && part->disk != NULL) { - MMCSD_PART_LOCK(part); - part->suspend = 0; - if (part->running > 0) { - /* kill thread */ - part->running = 0; - wakeup(part); - /* wait for thread to finish. */ - while (part->running != -1) - msleep(part, &part->part_mtx, 0, - "detach", 0); + if (part != NULL) { + if (part->disk != NULL) { + MMCSD_DISK_LOCK(part); + part->suspend = 0; + if (part->running > 0) { + /* kill thread */ + part->running = 0; + wakeup(part); + /* wait for thread to finish. */ + while (part->running != -1) + msleep(part, &part->disk_mtx, 0, + "mmcsd disk detach", 0); + } + MMCSD_DISK_UNLOCK(part); } - MMCSD_PART_UNLOCK(part); + MMCSD_IOCTL_LOCK(part); + while (part->ioctl > 0) + msleep(part, &part->ioctl_mtx, 0, + "mmcsd IOCTL detach", 0); + part->ioctl = -1; + MMCSD_IOCTL_UNLOCK(part); } } @@ -823,8 +867,9 @@ mmcsd_detach(device_t dev) /* kill disk */ disk_destroy(part->disk); - MMCSD_PART_LOCK_DESTROY(part); + MMCSD_DISK_LOCK_DESTROY(part); } + MMCSD_IOCTL_LOCK_DESTROY(part); free(part, M_DEVBUF); } } @@ -844,19 +889,27 @@ mmcsd_suspend(device_t dev) for (i = 0; i < MMC_PART_MAX; i++) { part = sc->part[i]; - if (part != NULL && part->disk != NULL) { - MMCSD_PART_LOCK(part); - part->suspend = 1; - if (part->running > 0) { - /* kill thread */ - part->running = 0; - wakeup(part); - /* wait for thread to finish. */ - while (part->running != -1) - msleep(part, &part->part_mtx, 0, - "detach", 0); + if (part != NULL) { + if (part->disk != NULL) { + MMCSD_DISK_LOCK(part); + part->suspend = 1; + if (part->running > 0) { + /* kill thread */ + part->running = 0; + wakeup(part); + /* wait for thread to finish. */ + while (part->running != -1) + msleep(part, &part->disk_mtx, 0, + "mmcsd disk suspension", 0); + } + MMCSD_DISK_UNLOCK(part); } - MMCSD_PART_UNLOCK(part); + MMCSD_IOCTL_LOCK(part); + while (part->ioctl > 0) + msleep(part, &part->ioctl_mtx, 0, + "mmcsd IOCTL suspension", 0); + part->ioctl = -1; + MMCSD_IOCTL_UNLOCK(part); } } #else /* __rtems__ */ @@ -875,16 +928,22 @@ mmcsd_resume(device_t dev) for (i = 0; i < MMC_PART_MAX; i++) { part = sc->part[i]; - if (part != NULL && part->disk != NULL) { - MMCSD_PART_LOCK(part); - part->suspend = 0; - if (part->running <= 0) { - part->running = 1; - kproc_create(&mmcsd_task, part, &part->p, 0, 0, - "%s%d: mmc/sd card", part->name, part->cnt); - MMCSD_PART_UNLOCK(part); - } else - MMCSD_PART_UNLOCK(part); + if (part != NULL) { + if (part->disk != NULL) { + MMCSD_DISK_LOCK(part); + part->suspend = 0; + if (part->running <= 0) { + part->running = 1; + MMCSD_DISK_UNLOCK(part); + kproc_create(&mmcsd_task, part, + &part->p, 0, 0, "%s%d: mmc/sd card", + part->name, part->cnt); + } else + MMCSD_DISK_UNLOCK(part); + } + MMCSD_IOCTL_LOCK(part); + part->ioctl = 0; + MMCSD_IOCTL_UNLOCK(part); } } #else /* __rtems__ */ @@ -916,13 +975,13 @@ mmcsd_strategy(struct bio *bp) part = bp->bio_disk->d_drv1; sc = part->sc; - MMCSD_PART_LOCK(part); + MMCSD_DISK_LOCK(part); if (part->running > 0 || part->suspend > 0) { bioq_disksort(&part->bio_queue, bp); - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); wakeup(part); } else { - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); biofinish(bp, NULL, ENXIO); } } @@ -961,9 +1020,9 @@ mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, int fflag) switch (cmd) { case MMC_IOC_CMD: mic = data; - err = mmcsd_ioctl_cmd(part, data, fflag); + err = mmcsd_ioctl_cmd(part, mic, fflag); break; - case MMC_IOC_CMD_MULTI: + case MMC_IOC_MULTI_CMD: mimc = data; if (mimc->num_of_cmds == 0) break; @@ -973,12 +1032,12 @@ mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, int fflag) size = sizeof(*mic) * cnt; mic = malloc(size, M_TEMP, M_WAITOK); err = copyin((const void *)mimc->cmds, mic, size); - if (err != 0) - break; - for (i = 0; i < cnt; i++) { - err = mmcsd_ioctl_cmd(part, &mic[i], fflag); - if (err != 0) - break; + if (err == 0) { + for (i = 0; i < cnt; i++) { + err = mmcsd_ioctl_cmd(part, &mic[i], fflag); + if (err != 0) + break; + } } free(mic, M_TEMP); break; @@ -1007,11 +1066,31 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag) if (part->ro == TRUE && mic->write_flag != 0) return (EROFS); + /* + * We don't need to explicitly lock against the disk(9) half of this + * driver as MMCBUS_ACQUIRE_BUS() will serialize us. However, it's + * necessary to protect against races with detachment and suspension, + * especially since it's required to switch away from RPMB partitions + * again after an access (see mmcsd_switch_part()). + */ + MMCSD_IOCTL_LOCK(part); + while (part->ioctl != 0) { + if (part->ioctl < 0) { + MMCSD_IOCTL_UNLOCK(part); + return (ENXIO); + } + msleep(part, &part->ioctl_mtx, 0, "mmcsd IOCTL", 0); + } + part->ioctl = 1; + MMCSD_IOCTL_UNLOCK(part); + err = 0; dp = NULL; len = mic->blksz * mic->blocks; - if (len > MMC_IOC_MAX_BYTES) - return (EOVERFLOW); + if (len > MMC_IOC_MAX_BYTES) { + err = EOVERFLOW; + goto out; + } if (len != 0) { dp = malloc(len, M_TEMP, M_WAITOK); err = copyin((void *)(uintptr_t)mic->data_ptr, dp, len); @@ -1066,7 +1145,7 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag) err = mmcsd_set_blockcount(sc, mic->blocks, mic->write_flag & (1 << 31)); if (err != MMC_ERR_NONE) - goto release; + goto switch_back; } if (mic->is_acmd != 0) (void)mmc_wait_for_app_cmd(mmcbr, dev, rca, &cmd, 0); @@ -1088,6 +1167,7 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag) DELAY(1000); } while (retries-- > 0); +switch_back: /* ... and always switch back to the default partition. */ err = mmcsd_switch_part(mmcbr, dev, rca, EXT_CSD_PART_CONFIG_ACC_DEFAULT); @@ -1138,6 +1218,10 @@ release: err = EIO; out: + MMCSD_IOCTL_LOCK(part); + part->ioctl = 0; + MMCSD_IOCTL_UNLOCK(part); + wakeup(part); if (dp != NULL) free(dp, M_TEMP); return (err); @@ -1191,10 +1275,23 @@ mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca, u_int part) sc = device_get_softc(dev); - if (sc->part_curr == part) + if (sc->mode == mode_sd) return (MMC_ERR_NONE); - if (sc->mode == mode_sd) + /* + * According to section "6.2.2 Command restrictions" of the eMMC + * specification v5.1, CMD19/CMD21 aren't allowed to be used with + * RPMB partitions. So we pause re-tuning along with triggering + * it up-front to decrease the likelihood of re-tuning becoming + * necessary while accessing an RPMB partition. Consequently, an + * RPMB partition should immediately be switched away from again + * after an access in order to allow for re-tuning to take place + * anew. + */ + if (part == EXT_CSD_PART_CONFIG_ACC_RPMB) + MMCBUS_RETUNE_PAUSE(sc->mmcbr, sc->dev, true); + + if (sc->part_curr == part) return (MMC_ERR_NONE); value = (sc->ext_csd[EXT_CSD_PART_CONFIG] & @@ -1202,10 +1299,15 @@ mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca, u_int part) /* Jump! */ err = mmc_switch(bus, dev, rca, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG, value, sc->part_time, true); - if (err != MMC_ERR_NONE) + if (err != MMC_ERR_NONE) { + if (part == EXT_CSD_PART_CONFIG_ACC_RPMB) + MMCBUS_RETUNE_UNPAUSE(sc->mmcbr, sc->dev); return (err); + } sc->ext_csd[EXT_CSD_PART_CONFIG] = value; + if (sc->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB) + MMCBUS_RETUNE_UNPAUSE(sc->mmcbr, sc->dev); sc->part_curr = part; return (MMC_ERR_NONE); } @@ -1217,7 +1319,7 @@ mmcsd_errmsg(int e) if (e < 0 || e > MMC_ERR_MAX) return "Bad error code"; - return errmsg[e]; + return (errmsg[e]); } static daddr_t @@ -1230,7 +1332,7 @@ mmcsd_rw(struct mmcsd_part *part, struct bio *bp) struct mmc_data data; struct mmcsd_softc *sc; device_t dev, mmcbr; - int numblocks, sz; + u_int numblocks, sz; char *vaddr; sc = part->sc; @@ -1242,7 +1344,7 @@ mmcsd_rw(struct mmcsd_part *part, struct bio *bp) end = bp->bio_pblkno + (bp->bio_bcount / sz); while (block < end) { vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; - numblocks = min(end - block, mmc_get_max_data(dev)); + numblocks = min(end - block, sc->max_data); memset(&req, 0, sizeof(req)); memset(&cmd, 0, sizeof(cmd)); memset(&stop, 0, sizeof(stop)); @@ -1262,7 +1364,7 @@ mmcsd_rw(struct mmcsd_part *part, struct bio *bp) cmd.opcode = MMC_WRITE_BLOCK; } cmd.arg = block; - if (!mmc_get_high_cap(dev)) + if (sc->high_cap == 0) cmd.arg <<= 9; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; data.data = vaddr; @@ -1302,7 +1404,7 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) struct mmc_request req; struct mmcsd_softc *sc; device_t dev, mmcbr; - int erase_sector, sz; + u_int erase_sector, sz; sc = part->sc; dev = sc->dev; @@ -1317,7 +1419,7 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) if (end >= part->eblock && end < part->eend) end = part->eend; /* Safe round to the erase sector boundaries. */ - erase_sector = mmc_get_erase_sector(dev); + erase_sector = sc->erase_sector; start = block + erase_sector - 1; /* Round up. */ start -= start % erase_sector; stop = end; /* Round down. */ @@ -1329,6 +1431,12 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) return (end); } + /* + * Pause re-tuning so it won't interfere with the order of erase + * commands. Note that these latter don't use the data lines, so + * re-tuning shouldn't actually become necessary during erase. + */ + MMCBUS_RETUNE_PAUSE(mmcbr, dev, false); /* Set erase start position. */ memset(&req, 0, sizeof(req)); memset(&cmd, 0, sizeof(cmd)); @@ -1339,13 +1447,15 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) else cmd.opcode = MMC_ERASE_GROUP_START; cmd.arg = start; - if (!mmc_get_high_cap(dev)) + if (sc->high_cap == 0) cmd.arg <<= 9; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; MMCBUS_WAIT_FOR_REQUEST(mmcbr, dev, &req); if (req.cmd->error != MMC_ERR_NONE) { - printf("erase err1: %d\n", req.cmd->error); - return (block); + device_printf(dev, "Setting erase start position failed %d\n", + req.cmd->error); + block = bp->bio_pblkno; + goto unpause; } /* Set erase stop position. */ memset(&req, 0, sizeof(req)); @@ -1356,14 +1466,16 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) else cmd.opcode = MMC_ERASE_GROUP_END; cmd.arg = stop; - if (!mmc_get_high_cap(dev)) + if (sc->high_cap == 0) cmd.arg <<= 9; cmd.arg--; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; MMCBUS_WAIT_FOR_REQUEST(mmcbr, dev, &req); if (req.cmd->error != MMC_ERR_NONE) { - printf("erase err2: %d\n", req.cmd->error); - return (block); + device_printf(dev, "Setting erase stop position failed %d\n", + req.cmd->error); + block = bp->bio_pblkno; + goto unpause; } /* Erase range. */ memset(&req, 0, sizeof(req)); @@ -1374,8 +1486,11 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; MMCBUS_WAIT_FOR_REQUEST(mmcbr, dev, &req); if (req.cmd->error != MMC_ERR_NONE) { - printf("erase err3 %d\n", req.cmd->error); - return (block); + device_printf(dev, "erase err3: %d\n", req.cmd->error); + device_printf(dev, "Issuing erase command failed %d\n", + req.cmd->error); + block = bp->bio_pblkno; + goto unpause; } /* Store one of remaining parts for the next call. */ if (bp->bio_pblkno >= part->eblock || block == start) { @@ -1385,7 +1500,10 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) part->eblock = block; /* Predict next backward. */ part->eend = start; } - return (end); + block = end; +unpause: + MMCBUS_RETUNE_UNPAUSE(mmcbr, dev); + return (block); } static int @@ -1446,16 +1564,16 @@ mmcsd_task(void *arg) mmcbr = sc->mmcbr; while (1) { - MMCSD_PART_LOCK(part); + MMCSD_DISK_LOCK(part); do { if (part->running == 0) goto out; bp = bioq_takefirst(&part->bio_queue); if (bp == NULL) - msleep(part, &part->part_mtx, PRIBIO, - "jobqueue", 0); + msleep(part, &part->disk_mtx, PRIBIO, + "mmcsd disk jobqueue", 0); } while (bp == NULL); - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); if (bp->bio_cmd != BIO_READ && part->ro) { bp->bio_error = EROFS; bp->bio_resid = bp->bio_bcount; @@ -1496,7 +1614,7 @@ release: out: /* tell parent we're done */ part->running = -1; - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); wakeup(part); kproc_exit(0); |