diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-04-04 09:36:57 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-04-04 14:46:23 +0200 |
commit | de8a76da2f374792594ce03a203b3f30e4889f6f (patch) | |
tree | 12b5e1e59358005c3c522955c08aee4795e4829c /freebsd/sys/dev/mmc | |
parent | Enable bridging by default (diff) | |
download | rtems-libbsd-de8a76da2f374792594ce03a203b3f30e4889f6f.tar.bz2 |
Update to FreeBSD head 2017-04-04
Git mirror commit 642b174daddbd0efd9bb5f242c43f4ab4db6869f.
Diffstat (limited to 'freebsd/sys/dev/mmc')
-rw-r--r-- | freebsd/sys/dev/mmc/bridge.h | 63 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc.c | 837 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc_ioctl.h | 64 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc_private.h | 69 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc_subr.c | 254 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc_subr.h | 72 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcbrvar.h | 25 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcreg.h | 196 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcsd.c | 1134 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcvar.h | 6 |
10 files changed, 2135 insertions, 585 deletions
diff --git a/freebsd/sys/dev/mmc/bridge.h b/freebsd/sys/dev/mmc/bridge.h index a26c31ec..a780ffae 100644 --- a/freebsd/sys/dev/mmc/bridge.h +++ b/freebsd/sys/dev/mmc/bridge.h @@ -52,7 +52,7 @@ */ #ifndef DEV_MMC_BRIDGE_H -#define DEV_MMC_BRIDGE_H +#define DEV_MMC_BRIDGE_H #include <sys/bus.h> @@ -60,7 +60,7 @@ * This file defines interfaces for the mmc bridge. The names chosen * are similar to or the same as the names used in Linux to allow for * easy porting of what Linux calls mmc host drivers. I use the - * FreeBSD terminology of bridge and bus for consistancy with other + * FreeBSD terminology of bridge and bus for consistency with other * drivers in the system. This file corresponds roughly to the Linux * linux/mmc/host.h file. * @@ -73,10 +73,9 @@ * to be added to the mmcbus file). * * Attached to the mmc bridge is an mmcbus. The mmcbus is described - * in dev/mmc/bus.h. + * in dev/mmc/mmcbus_if.m. */ - /* * mmc_ios is a structure that is used to store the state of the mmc/sd * bus configuration. This include the bus' clock speed, its voltage, @@ -90,6 +89,10 @@ enum mmc_vdd { vdd_330, vdd_340, vdd_350, vdd_360 }; +enum mmc_vccq { + vccq_120 = 0, vccq_180, vccq_330 +}; + enum mmc_power_mode { power_off = 0, power_up, power_on }; @@ -106,18 +109,28 @@ enum mmc_bus_width { bus_width_1 = 0, bus_width_4 = 2, bus_width_8 = 3 }; +enum mmc_drv_type { + drv_type_b = 0, drv_type_a, drv_type_c, drv_type_d +}; + enum mmc_bus_timing { - bus_timing_normal = 0, bus_timing_hs + bus_timing_normal = 0, bus_timing_hs, bus_timing_uhs_sdr12, + bus_timing_uhs_sdr25, bus_timing_uhs_sdr50, bus_timing_uhs_ddr50, + bus_timing_uhs_sdr104, bus_timing_mmc_ddr52, bus_timing_mmc_hs200, + bus_timing_mmc_hs400, bus_timing_mmc_hs400es, bus_timing_max = + bus_timing_mmc_hs400es }; struct mmc_ios { uint32_t clock; /* Speed of the clock in Hz to move data */ - enum mmc_vdd vdd; /* Voltage to apply to the power pins/ */ + enum mmc_vdd vdd; /* Voltage to apply to the power pins */ + enum mmc_vccq vccq; /* Voltage to use for signaling */ enum mmc_bus_mode bus_mode; enum mmc_chip_select chip_select; enum mmc_bus_width bus_width; enum mmc_power_mode power_mode; enum mmc_bus_timing timing; + enum mmc_drv_type drv_type; }; enum mmc_card_mode { @@ -130,9 +143,33 @@ struct mmc_host { uint32_t host_ocr; uint32_t ocr; uint32_t caps; -#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */ -#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */ -#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */ +#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */ +#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */ +#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */ +#define MMC_CAP_BOOT_NOACC (1 << 4) /* Cannot access boot partitions */ +#define MMC_CAP_WAIT_WHILE_BUSY (1 << 5) /* Host waits for busy responses */ +#define MMC_CAP_UHS_SDR12 (1 << 6) /* Can do UHS SDR12 */ +#define MMC_CAP_UHS_SDR25 (1 << 7) /* Can do UHS SDR25 */ +#define MMC_CAP_UHS_SDR50 (1 << 8) /* Can do UHS SDR50 */ +#define MMC_CAP_UHS_SDR104 (1 << 9) /* Can do UHS SDR104 */ +#define MMC_CAP_UHS_DDR50 (1 << 10) /* Can do UHS DDR50 */ +#define MMC_CAP_MMC_DDR52_120 (1 << 11) /* Can do eMMC DDR52 at 1.2 V */ +#define MMC_CAP_MMC_DDR52_180 (1 << 12) /* Can do eMMC DDR52 at 1.8 V */ +#define MMC_CAP_MMC_DDR52 (MMC_CAP_MMC_DDR52_120 | MMC_CAP_MMC_DDR52_180) +#define MMC_CAP_MMC_HS200_120 (1 << 13) /* Can do eMMC HS200 at 1.2 V */ +#define MMC_CAP_MMC_HS200_180 (1 << 14) /* Can do eMMC HS200 at 1.8 V */ +#define MMC_CAP_MMC_HS200 (MMC_CAP_MMC_HS200_120| MMC_CAP_MMC_HS200_180) +#define MMC_CAP_MMC_HS400_120 (1 << 15) /* Can do eMMC HS400 at 1.2 V */ +#define MMC_CAP_MMC_HS400_180 (1 << 16) /* Can do eMMC HS400 at 1.8 V */ +#define MMC_CAP_MMC_HS400 (MMC_CAP_MMC_HS400_120 | MMC_CAP_MMC_HS400_180) +#define MMC_CAP_MMC_HSX00_120 (MMC_CAP_MMC_HS200_120 | MMC_CAP_MMC_HS400_120) +#define MMC_CAP_MMC_ENH_STROBE (1 << 17) /* Can do eMMC Enhanced Strobe */ +#define MMC_CAP_SIGNALING_120 (1 << 18) /* Can do signaling at 1.2 V */ +#define MMC_CAP_SIGNALING_180 (1 << 19) /* Can do signaling at 1.8 V */ +#define MMC_CAP_SIGNALING_330 (1 << 20) /* Can do signaling at 3.3 V */ +#define MMC_CAP_DRIVER_TYPE_A (1 << 21) /* Can do Driver Type A */ +#define MMC_CAP_DRIVER_TYPE_C (1 << 22) /* Can do Driver Type C */ +#define MMC_CAP_DRIVER_TYPE_D (1 << 23) /* Can do Driver Type D */ enum mmc_card_mode mode; struct mmc_ios ios; /* Current state of the host */ }; @@ -140,4 +177,12 @@ struct mmc_host { extern driver_t mmc_driver; extern devclass_t mmc_devclass; +#define MMC_VERSION 3 + +#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 /* DEV_MMC_BRIDGE_H */ diff --git a/freebsd/sys/dev/mmc/mmc.c b/freebsd/sys/dev/mmc/mmc.c index a3232248..ab494804 100644 --- a/freebsd/sys/dev/mmc/mmc.c +++ b/freebsd/sys/dev/mmc/mmc.c @@ -3,6 +3,7 @@ /*- * Copyright (c) 2006 Bernd Walter. All rights reserved. * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -67,24 +68,17 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/time.h> +#include <dev/mmc/bridge.h> +#include <dev/mmc/mmc_private.h> +#include <dev/mmc/mmc_subr.h> #include <dev/mmc/mmcreg.h> #include <dev/mmc/mmcbrvar.h> #include <dev/mmc/mmcvar.h> + #include <rtems/bsd/local/mmcbr_if.h> #include <rtems/bsd/local/mmcbus_if.h> -struct mmc_softc { - device_t dev; - 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; - struct timeval log_time; -}; - -#define LOG_PPS 5 /* Log no more than 5 errors per second. */ +CTASSERT(bus_timing_max <= sizeof(uint32_t) * NBBY); /* * Per-card data @@ -93,7 +87,7 @@ struct mmc_ivars { uint32_t raw_cid[4]; /* Raw bits of the CID */ uint32_t raw_csd[4]; /* Raw bits of the CSD */ uint32_t raw_scr[2]; /* Raw bits of the SCR */ - uint8_t raw_ext_csd[512]; /* Raw bits of the EXT_CSD */ + 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; enum mmc_card_mode mode; @@ -103,24 +97,26 @@ struct mmc_ivars { 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 timing; /* Bus timing support */ 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 */ + uint32_t vccq_180; /* Mask of bus timings at VCCQ of 1.8 V */ uint32_t tran_speed; /* Max speed in normal mode */ uint32_t hs_tran_speed; /* Max speed in high speed mode */ uint32_t erase_sector; /* Card native erase sector size */ + uint32_t cmd6_time; /* Generic switch timeout [us] */ char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */ char card_sn_string[16];/* Formatted serial # for disk->d_ident */ }; -#define CMD_RETRIES 3 - -#define CARD_ID_FREQUENCY 400000 /* Spec requires 400kHz max during ID phase. */ +#define CMD_RETRIES 3 static SYSCTL_NODE(_hw, OID_AUTO, mmc, CTLFLAG_RD, NULL, "mmc driver"); static int mmc_debug; -SYSCTL_INT(_hw_mmc, OID_AUTO, debug, CTLFLAG_RWTUN, &mmc_debug, 0, "Debug level"); +SYSCTL_INT(_hw_mmc, OID_AUTO, debug, CTLFLAG_RWTUN, &mmc_debug, 0, + "Debug level"); /* bus entry points */ static int mmc_acquire_bus(device_t busdev, device_t dev); @@ -139,14 +135,14 @@ static int mmc_wait_for_request(device_t brdev, device_t reqdev, static int mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value); -#define MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define MMC_LOCK_INIT(_sc) \ - mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ +#define MMC_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->dev), \ "mmc", MTX_DEF) -#define MMC_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); -#define MMC_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); -#define MMC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); +#define MMC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx); +#define MMC_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED); +#define MMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED); static int mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid); static void mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr); @@ -157,7 +153,8 @@ static int mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, static int mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr); static int mmc_calculate_clock(struct mmc_softc *sc); -static void mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid); +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); @@ -183,25 +180,20 @@ static uint32_t mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr); static int mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr); static int mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd); -static int mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd); static int mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs); 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_send_status(struct mmc_softc *sc, uint16_t rca, - uint32_t *status); static int mmc_set_blocklen(struct mmc_softc *sc, uint32_t len); -static int mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, - int width); +static int mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar); +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, int timing); -static int mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, - uint8_t value); +static int mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing); static int mmc_test_bus_width(struct mmc_softc *sc); -static int mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca, - struct mmc_command *cmd, int retries); -static int mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd, - int retries); +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 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); @@ -261,7 +253,7 @@ mmc_suspend(device_t dev) err = bus_generic_suspend(dev); if (err) - return (err); + return (err); mmc_power_down(sc); return (0); } @@ -280,8 +272,8 @@ mmc_acquire_bus(device_t busdev, device_t dev) { struct mmc_softc *sc; struct mmc_ivars *ivar; - int err; - int rca; + int err, rca; + enum mmc_bus_timing timing; err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), busdev); if (err) @@ -300,19 +292,47 @@ mmc_acquire_bus(device_t busdev, device_t dev) * unselect unless the bus code itself wants the mmc * bus, and constantly reselecting causes problems. */ - rca = mmc_get_rca(dev); + ivar = device_get_ivars(dev); + rca = ivar->rca; if (sc->last_rca != rca) { - mmc_select_card(sc, rca); + if (mmc_select_card(sc, rca) != MMC_ERR_NONE) { + device_printf(sc->dev, "Card at relative " + "address %d failed to select.\n", rca); + return (ENXIO); + } sc->last_rca = rca; + timing = mmcbr_get_timing(busdev); /* Prepare bus width for the new card. */ - ivar = device_get_ivars(dev); if (bootverbose || mmc_debug) { device_printf(busdev, - "setting bus width to %d bits\n", + "setting bus width to %d bits %s timing\n", (ivar->bus_width == bus_width_4) ? 4 : - (ivar->bus_width == bus_width_8) ? 8 : 1); + (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", + 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); + 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); + return (ENXIO); } - mmc_set_card_bus_width(sc, rca, ivar->bus_width); mmcbr_set_bus_width(busdev, ivar->bus_width); mmcbr_update_ios(busdev); } @@ -409,7 +429,8 @@ mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) } static int -mmc_wait_for_request(device_t brdev, device_t reqdev, struct mmc_request *req) +mmc_wait_for_request(device_t brdev, device_t reqdev __unused, + struct mmc_request *req) { struct mmc_softc *sc = device_get_softc(brdev); @@ -417,74 +438,6 @@ mmc_wait_for_request(device_t brdev, device_t reqdev, struct mmc_request *req) } static int -mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd, int retries) -{ - struct mmc_request mreq; - int err; - - do { - memset(&mreq, 0, sizeof(mreq)); - memset(cmd->resp, 0, sizeof(cmd->resp)); - cmd->retries = 0; /* Retries done here, not in hardware. */ - cmd->mrq = &mreq; - mreq.cmd = cmd; - if (mmc_wait_for_req(sc, &mreq) != 0) - err = MMC_ERR_FAILED; - else - err = cmd->error; - } while (err != MMC_ERR_NONE && retries-- > 0); - - if (err != MMC_ERR_NONE && sc->squelched == 0) { - if (ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS)) { - device_printf(sc->dev, "CMD%d failed, RESULT: %d\n", - cmd->opcode, err); - } - } - - return (err); -} - -static int -mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca, - struct mmc_command *cmd, int retries) -{ - struct mmc_command appcmd; - int err; - - /* Squelch error reporting at lower levels, we report below. */ - sc->squelched++; - do { - memset(&appcmd, 0, sizeof(appcmd)); - appcmd.opcode = MMC_APP_CMD; - appcmd.arg = rca << 16; - appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - appcmd.data = NULL; - if (mmc_wait_for_cmd(sc, &appcmd, 0) != 0) - err = MMC_ERR_FAILED; - else - err = appcmd.error; - if (err == MMC_ERR_NONE) { - if (!(appcmd.resp[0] & R1_APP_CMD)) - err = MMC_ERR_FAILED; - else if (mmc_wait_for_cmd(sc, cmd, 0) != 0) - err = MMC_ERR_FAILED; - else - err = cmd->error; - } - } while (err != MMC_ERR_NONE && retries-- > 0); - sc->squelched--; - - if (err != MMC_ERR_NONE && sc->squelched == 0) { - if (ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS)) { - device_printf(sc->dev, "ACMD%d failed, RESULT: %d\n", - cmd->opcode, err); - } - } - - return (err); -} - -static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, uint32_t arg, uint32_t flags, uint32_t *resp, int retries) { @@ -496,7 +449,7 @@ mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, cmd.arg = arg; cmd.flags = flags; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, retries); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, retries); if (err) return (err); if (resp) { @@ -524,7 +477,7 @@ mmc_idle_cards(struct mmc_softc *sc) cmd.arg = 0; cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; cmd.data = NULL; - mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); mmc_ms_delay(1); mmcbr_set_chip_select(dev, cs_dontcare); @@ -545,7 +498,8 @@ mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr) cmd.data = NULL; for (i = 0; i < 1000; i++) { - err = mmc_wait_for_app_cmd(sc, 0, &cmd, CMD_RETRIES); + err = mmc_wait_for_app_cmd(sc->dev, sc->dev, 0, &cmd, + CMD_RETRIES); if (err != MMC_ERR_NONE) break; if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) || @@ -572,7 +526,7 @@ mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr) cmd.data = NULL; for (i = 0; i < 1000; i++) { - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) break; if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) || @@ -598,7 +552,7 @@ mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs) cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); return (err); } @@ -606,6 +560,7 @@ static void mmc_power_up(struct mmc_softc *sc) { device_t dev; + enum mmc_vccq vccq; dev = sc->dev; mmcbr_set_vdd(dev, mmc_highest_voltage(mmcbr_get_host_ocr(dev))); @@ -615,9 +570,14 @@ mmc_power_up(struct mmc_softc *sc) mmcbr_set_power_mode(dev, power_up); mmcbr_set_clock(dev, 0); mmcbr_update_ios(dev); + for (vccq = vccq_330; ; vccq--) { + mmcbr_set_vccq(dev, vccq); + if (mmcbr_switch_vccq(dev) == 0 || vccq == vccq_120) + break; + } mmc_ms_delay(1); - mmcbr_set_clock(dev, CARD_ID_FREQUENCY); + mmcbr_set_clock(dev, SD_MMC_CARD_ID_FREQUENCY); mmcbr_set_timing(dev, bus_timing_normal); mmcbr_set_power_mode(dev, power_on); mmcbr_update_ios(dev); @@ -649,24 +609,6 @@ mmc_select_card(struct mmc_softc *sc, uint16_t rca) } static int -mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, uint8_t value) -{ - struct mmc_command cmd; - int err; - - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = MMC_SWITCH_FUNC; - cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | - (index << 16) | - (value << 8) | - set; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); - return (err); -} - -static int mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, uint8_t *res) { @@ -690,12 +632,12 @@ mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, data.len = 64; data.flags = MMC_DATA_READ; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); return (err); } static int -mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width) +mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar) { struct mmc_command cmd; int err; @@ -706,13 +648,14 @@ mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width) cmd.opcode = ACMD_SET_CLR_CARD_DETECT; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.arg = SD_CLR_CARD_DETECT; - err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + err = mmc_wait_for_app_cmd(sc->dev, sc->dev, ivar->rca, &cmd, + CMD_RETRIES); if (err != 0) return (err); memset(&cmd, 0, sizeof(cmd)); cmd.opcode = ACMD_SET_BUS_WIDTH; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - switch (width) { + switch (ivar->bus_width) { case bus_width_1: cmd.arg = SD_BUS_WIDTH_1; break; @@ -722,64 +665,196 @@ mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width) default: return (MMC_ERR_INVALID); } - err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + err = mmc_wait_for_app_cmd(sc->dev, sc->dev, ivar->rca, &cmd, + CMD_RETRIES); } else { - switch (width) { + switch (ivar->bus_width) { case bus_width_1: value = EXT_CSD_BUS_WIDTH_1; break; case bus_width_4: - value = EXT_CSD_BUS_WIDTH_4; + switch (mmcbr_get_timing(sc->dev)) { + 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; + default: + value = EXT_CSD_BUS_WIDTH_4; + break; + } break; case bus_width_8: - value = EXT_CSD_BUS_WIDTH_8; + switch (mmcbr_get_timing(sc->dev)) { + 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; + break; + default: + value = EXT_CSD_BUS_WIDTH_8; + break; + } break; default: return (MMC_ERR_INVALID); } - err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, - value); + err = mmc_switch(sc->dev, sc->dev, ivar->rca, + EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, value, + ivar->cmd6_time, true); } return (err); } static int -mmc_set_timing(struct mmc_softc *sc, int timing) +mmc_set_power_class(struct mmc_softc *sc, struct mmc_ivars *ivar) { - int err; - uint8_t value; - u_char switch_res[64]; + device_t dev; + const uint8_t *ext_csd; + uint32_t clock; + uint8_t value; - switch (timing) { - case bus_timing_normal: - value = 0; + dev = sc->dev; + if (mmcbr_get_mode(dev) != mode_mmc || ivar->csd.spec_vers < 4) + return (MMC_ERR_NONE); + + value = 0; + ext_csd = ivar->raw_ext_csd; + clock = mmcbr_get_clock(dev); + switch (1 << mmcbr_get_vdd(dev)) { + case MMC_OCR_LOW_VOLTAGE: + if (clock <= MMC_TYPE_HS_26_MAX) + value = ext_csd[EXT_CSD_PWR_CL_26_195]; + else if (clock <= MMC_TYPE_HS_52_MAX) { + if (mmcbr_get_timing(dev) >= bus_timing_mmc_ddr52 && + ivar->bus_width >= bus_width_4) + value = ext_csd[EXT_CSD_PWR_CL_52_195_DDR]; + else + value = ext_csd[EXT_CSD_PWR_CL_52_195]; + } else if (clock <= MMC_TYPE_HS200_HS400ES_MAX) + value = ext_csd[EXT_CSD_PWR_CL_200_195]; break; - case bus_timing_hs: - value = 1; + case MMC_OCR_270_280: + case MMC_OCR_280_290: + case MMC_OCR_290_300: + case MMC_OCR_300_310: + case MMC_OCR_310_320: + case MMC_OCR_320_330: + case MMC_OCR_330_340: + case MMC_OCR_340_350: + case MMC_OCR_350_360: + if (clock <= MMC_TYPE_HS_26_MAX) + value = ext_csd[EXT_CSD_PWR_CL_26_360]; + else if (clock <= MMC_TYPE_HS_52_MAX) { + if (mmcbr_get_timing(dev) == bus_timing_mmc_ddr52 && + ivar->bus_width >= bus_width_4) + value = ext_csd[EXT_CSD_PWR_CL_52_360_DDR]; + else + value = ext_csd[EXT_CSD_PWR_CL_52_360]; + } else if (clock <= MMC_TYPE_HS200_HS400ES_MAX) { + if (ivar->bus_width == bus_width_8) + value = ext_csd[EXT_CSD_PWR_CL_200_360_DDR]; + else + value = ext_csd[EXT_CSD_PWR_CL_200_360]; + } break; default: + device_printf(dev, "No power class support for VDD 0x%x\n", + 1 << mmcbr_get_vdd(dev)); return (MMC_ERR_INVALID); } - if (mmcbr_get_mode(sc->dev) == mode_sd) + + if (ivar->bus_width == bus_width_8) + value = (value & EXT_CSD_POWER_CLASS_8BIT_MASK) >> + EXT_CSD_POWER_CLASS_8BIT_SHIFT; + else + value = (value & EXT_CSD_POWER_CLASS_4BIT_MASK) >> + EXT_CSD_POWER_CLASS_4BIT_SHIFT; + + if (value == 0) + return (MMC_ERR_NONE); + + return (mmc_switch(dev, dev, ivar->rca, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_CLASS, value, ivar->cmd6_time, true)); +} + +static int +mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing) +{ + u_char switch_res[64]; + uint8_t value; + int err; + + if (mmcbr_get_mode(sc->dev) == mode_sd) { + switch (timing) { + case bus_timing_normal: + value = SD_SWITCH_NORMAL_MODE; + break; + case bus_timing_hs: + value = SD_SWITCH_HS_MODE; + break; + default: + return (MMC_ERR_INVALID); + } err = mmc_sd_switch(sc, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1, value, switch_res); - else - err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, value); + if (err != MMC_ERR_NONE) + return (err); + if ((switch_res[16] & 0xf) != value) + return (MMC_ERR_FAILED); + mmcbr_set_timing(sc->dev, timing); + mmcbr_update_ios(sc->dev); + } else { + switch (timing) { + case bus_timing_normal: + value = EXT_CSD_HS_TIMING_BC; + break; + case bus_timing_hs: + case bus_timing_mmc_ddr52: + value = EXT_CSD_HS_TIMING_HS; + break; + default: + return (MMC_ERR_INVALID); + } + err = mmc_switch(sc->dev, sc->dev, ivar->rca, + EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, value, + ivar->cmd6_time, false); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_timing(sc->dev, timing); + mmcbr_update_ios(sc->dev); + err = mmc_switch_status(sc->dev, sc->dev, ivar->rca, + ivar->cmd6_time); + } return (err); } +static const uint8_t p8[8] = { + 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t p8ok[8] = { + 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t p4[4] = { + 0x5A, 0x00, 0x00, 0x00 +}; + +static const uint8_t p4ok[4] = { + 0xA5, 0x00, 0x00, 0x00 +}; + static int mmc_test_bus_width(struct mmc_softc *sc) { struct mmc_command cmd; struct mmc_data data; - int err; uint8_t buf[8]; - uint8_t p8[8] = { 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t p8ok[8] = { 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t p4[4] = { 0x5A, 0x00, 0x00, 0x00, }; - uint8_t p4ok[4] = { 0xA5, 0x00, 0x00, 0x00, }; + int err; if (mmcbr_get_caps(sc->dev) & MMC_CAP_8_BIT_DATA) { mmcbr_set_bus_width(sc->dev, bus_width_8); @@ -793,10 +868,10 @@ mmc_test_bus_width(struct mmc_softc *sc) cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.data = &data; - data.data = p8; + data.data = __DECONST(void *, p8); data.len = 8; data.flags = MMC_DATA_WRITE; - mmc_wait_for_cmd(sc, &cmd, 0); + mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, 0); memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); @@ -808,7 +883,7 @@ mmc_test_bus_width(struct mmc_softc *sc) data.data = buf; data.len = 8; data.flags = MMC_DATA_READ; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, 0); sc->squelched--; mmcbr_set_bus_width(sc->dev, bus_width_1); @@ -830,10 +905,10 @@ mmc_test_bus_width(struct mmc_softc *sc) cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.data = &data; - data.data = p4; + data.data = __DECONST(void *, p4); data.len = 4; data.flags = MMC_DATA_WRITE; - mmc_wait_for_cmd(sc, &cmd, 0); + mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, 0); memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); @@ -845,7 +920,7 @@ mmc_test_bus_width(struct mmc_softc *sc) data.data = buf; data.len = 4; data.flags = MMC_DATA_READ; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, 0); sc->squelched--; mmcbr_set_bus_width(sc->dev, bus_width_1); @@ -863,6 +938,7 @@ mmc_get_bits(uint32_t *bits, int bit_len, int start, int size) const int i = (bit_len / 32) - (start / 32) - 1; const int shift = start & 31; uint32_t retval = bits[i] >> shift; + if (size + shift > 32) retval |= bits[i - 1] << (32 - shift); return (retval & ((1llu << size) - 1)); @@ -887,7 +963,7 @@ mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid) } static void -mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid) +mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid, bool is_4_41p) { int i; @@ -901,7 +977,11 @@ mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid) cid->prv = mmc_get_bits(raw_cid, 128, 48, 8); cid->psn = mmc_get_bits(raw_cid, 128, 16, 32); cid->mdt_month = mmc_get_bits(raw_cid, 128, 12, 4); - cid->mdt_year = mmc_get_bits(raw_cid, 128, 8, 4) + 1997; + cid->mdt_year = mmc_get_bits(raw_cid, 128, 8, 4); + if (is_4_41p) + cid->mdt_year += 2013; + else + cid->mdt_year += 1997; } static void @@ -982,10 +1062,14 @@ mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1); csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1); csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1); - csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)]; - csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)]; - csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)]; - csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)]; + csd->vdd_r_curr_min = + cur_min[mmc_get_bits(raw_csd, 128, 59, 3)]; + csd->vdd_r_curr_max = + cur_max[mmc_get_bits(raw_csd, 128, 56, 3)]; + csd->vdd_w_curr_min = + cur_min[mmc_get_bits(raw_csd, 128, 53, 3)]; + csd->vdd_w_curr_max = + cur_max[mmc_get_bits(raw_csd, 128, 50, 3)]; m = mmc_get_bits(raw_csd, 128, 62, 12); e = mmc_get_bits(raw_csd, 128, 47, 3); csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len; @@ -1010,8 +1094,8 @@ mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1); csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1); csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1); - csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 128, 48, 22) + 1) * - 512 * 1024; + csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 128, 48, 22) + + 1) * 512 * 1024; csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1); csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1; csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7); @@ -1109,7 +1193,7 @@ mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid) cmd.arg = 0; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t)); return (err); } @@ -1125,7 +1209,7 @@ mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd) cmd.arg = rca << 16; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); memcpy(rawcsd, cmd.resp, 4 * sizeof(uint32_t)); return (err); } @@ -1150,42 +1234,18 @@ mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr) data.len = 8; data.flags = MMC_DATA_READ; - err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + err = mmc_wait_for_app_cmd(sc->dev, sc->dev, rca, &cmd, CMD_RETRIES); rawscr[0] = be32toh(rawscr[0]); rawscr[1] = be32toh(rawscr[1]); return (err); } static int -mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd) -{ - int err; - struct mmc_command cmd; - struct mmc_data data; - - memset(&cmd, 0, sizeof(cmd)); - memset(&data, 0, sizeof(data)); - - memset(rawextcsd, 0, 512); - cmd.opcode = MMC_SEND_EXT_CSD; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - cmd.arg = 0; - cmd.data = &data; - - data.data = rawextcsd; - data.len = 512; - data.flags = MMC_DATA_READ; - - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); - return (err); -} - -static int mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus) { - int err, i; struct mmc_command cmd; struct mmc_data data; + int err, i; memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); @@ -1200,7 +1260,7 @@ mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus) data.len = 64; data.flags = MMC_DATA_READ; - err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + err = mmc_wait_for_app_cmd(sc->dev, sc->dev, rca, &cmd, CMD_RETRIES); for (i = 0; i < 16; i++) rawsdstatus[i] = be32toh(rawsdstatus[i]); return (err); @@ -1217,7 +1277,7 @@ mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp) cmd.arg = resp << 16; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); return (err); } @@ -1232,54 +1292,102 @@ mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp) cmd.arg = 0; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); *resp = cmd.resp[0]; return (err); } static int -mmc_send_status(struct mmc_softc *sc, uint16_t rca, uint32_t *status) +mmc_set_blocklen(struct mmc_softc *sc, uint32_t len) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = rca << 16; + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = len; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); - *status = cmd.resp[0]; + err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES); return (err); } -static int -mmc_set_blocklen(struct mmc_softc *sc, uint32_t len) +static uint32_t +mmc_timing_to_dtr(struct mmc_ivars *ivar, enum mmc_bus_timing timing) { - struct mmc_command cmd; - int err; - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = MMC_SET_BLOCKLEN; - cmd.arg = len; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); - return (err); + switch (timing) { + case bus_timing_normal: + return (ivar->tran_speed); + case bus_timing_hs: + return (ivar->hs_tran_speed); + case bus_timing_uhs_sdr12: + return (SD_SDR12_MAX); + case bus_timing_uhs_sdr25: + return (SD_SDR25_MAX); + case bus_timing_uhs_ddr50: + return (SD_DDR50_MAX); + case bus_timing_uhs_sdr50: + return (SD_SDR50_MAX); + case bus_timing_uhs_sdr104: + return (SD_SDR104_MAX); + case bus_timing_mmc_ddr52: + return (MMC_TYPE_DDR52_MAX); + case bus_timing_mmc_hs200: + case bus_timing_mmc_hs400: + case bus_timing_mmc_hs400es: + return (MMC_TYPE_HS200_HS400ES_MAX); + } + return (0); +} + +static const char * +mmc_timing_to_string(enum mmc_bus_timing timing) +{ + + switch (timing) { + case bus_timing_normal: + return ("normal speed"); + case bus_timing_hs: + return ("high speed"); + case bus_timing_uhs_sdr12: + case bus_timing_uhs_sdr25: + case bus_timing_uhs_sdr50: + case bus_timing_uhs_sdr104: + return ("single data rate"); + case bus_timing_uhs_ddr50: + case bus_timing_mmc_ddr52: + return ("dual data rate"); + case bus_timing_mmc_hs200: + return ("HS200"); + case bus_timing_mmc_hs400: + return ("HS400"); + case bus_timing_mmc_hs400es: + return ("HS400 with enhanced strobe"); + } + return (""); } static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard) { + enum mmc_bus_timing max_timing, timing; + device_printf(dev, "Card at relative address 0x%04x%s:\n", ivar->rca, newcard ? " added" : ""); device_printf(dev, " card: %s\n", ivar->card_id_string); - device_printf(dev, " bus: %ubit, %uMHz%s\n", + max_timing = bus_timing_normal; + for (timing = bus_timing_max; timing > bus_timing_normal; timing--) { + if (isset(&ivar->timings, timing)) { + max_timing = timing; + break; + } + } + device_printf(dev, " bus: %ubit, %uMHz (%s timing)\n", (ivar->bus_width == bus_width_1 ? 1 : (ivar->bus_width == bus_width_4 ? 4 : 8)), - (ivar->timing == bus_timing_hs ? - ivar->hs_tran_speed : ivar->tran_speed) / 1000000, - ivar->timing == bus_timing_hs ? ", high speed timing" : ""); + mmc_timing_to_dtr(ivar, timing) / 1000000, + mmc_timing_to_string(timing)); device_printf(dev, " memory: %u blocks, erase sector %u blocks%s\n", ivar->sec_count, ivar->erase_sector, ivar->read_only ? ", read-only" : ""); @@ -1288,14 +1396,16 @@ mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard) static void 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; - int err, i, devcount, newcard; - uint32_t raw_cid[4], resp, sec_count, status; device_t child; + int devcount, err, host_caps, i, newcard; + uint32_t resp, sec_count, status; uint16_t rca = 2; - u_char switch_res[64]; + host_caps = mmcbr_get_caps(sc->dev); if (bootverbose || mmc_debug) device_printf(sc->dev, "Probing cards\n"); while (1) { @@ -1309,18 +1419,21 @@ mmc_discover_cards(struct mmc_softc *sc) break; } newcard = 1; - if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) + 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) { + 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", + device_printf(sc->dev, + "%sard detected (CID %08x%08x%08x%08x)\n", newcard ? "New c" : "C", raw_cid[0], raw_cid[1], raw_cid[2], raw_cid[3]); } @@ -1332,14 +1445,24 @@ mmc_discover_cards(struct mmc_softc *sc) if (mmcbr_get_ro(sc->dev)) ivar->read_only = 1; ivar->bus_width = bus_width_1; - ivar->timing = bus_timing_normal; + setbit(&ivar->timings, bus_timing_normal); ivar->mode = mmcbr_get_mode(sc->dev); if (ivar->mode == mode_sd) { mmc_decode_cid_sd(ivar->raw_cid, &ivar->cid); - mmc_send_relative_addr(sc, &resp); + err = mmc_send_relative_addr(sc, &resp); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, + "Error getting RCA %d\n", err); + break; + } ivar->rca = resp >> 16; /* Get card CSD. */ - mmc_send_csd(sc, ivar->rca, ivar->raw_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; + } if (bootverbose || mmc_debug) device_printf(sc->dev, "%sard detected (CSD %08x%08x%08x%08x)\n", @@ -1354,7 +1477,8 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->erase_sector = ivar->csd.erase_sector * ivar->csd.write_bl_len / MMC_SECTOR_SIZE; - err = mmc_send_status(sc, ivar->rca, &status); + err = mmc_send_status(sc->dev, sc->dev, ivar->rca, + &status); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading card status %d\n", err); @@ -1366,19 +1490,30 @@ mmc_discover_cards(struct mmc_softc *sc) break; } - /* Get card SCR. Card must be selected to fetch it. */ - mmc_select_card(sc, ivar->rca); - mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr); + /* Get card SCR. Card must be selected to fetch it. */ + err = mmc_select_card(sc, ivar->rca); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, + "Error selecting card %d\n", err); + break; + } + 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; + } mmc_app_decode_scr(ivar->raw_scr, &ivar->scr); /* Get card switch capabilities (command class 10). */ if ((ivar->scr.sda_vsn >= 1) && - (ivar->csd.ccc & (1<<10))) { - mmc_sd_switch(sc, SD_SWITCH_MODE_CHECK, + (ivar->csd.ccc & (1 << 10))) { + err = mmc_sd_switch(sc, SD_SWITCH_MODE_CHECK, SD_SWITCH_GROUP1, SD_SWITCH_NOCHANGE, switch_res); - if (switch_res[13] & 2) { - ivar->timing = bus_timing_hs; - ivar->hs_tran_speed = SD_MAX_HS; + if (err == MMC_ERR_NONE && + switch_res[13] & (1 << SD_SWITCH_HS_MODE)) { + setbit(&ivar->timings, bus_timing_hs); + ivar->hs_tran_speed = SD_HS_MAX; } } @@ -1388,15 +1523,16 @@ mmc_discover_cards(struct mmc_softc *sc) * commands, although the state tables / diagrams in the * standard suggest they go back to the transfer state. * Other cards don't become deselected, and if we - * atttempt to blindly re-select them, we get timeout + * attempt to blindly re-select them, we get timeout * errors from some controllers. So we deselect then * reselect to handle all situations. The only thing we * use from the sd_status is the erase sector size, but * it is still nice to get that right. */ mmc_select_card(sc, 0); - mmc_select_card(sc, ivar->rca); - mmc_app_sd_status(sc, ivar->rca, ivar->raw_sd_status); + (void)mmc_select_card(sc, ivar->rca); + (void)mmc_app_sd_status(sc, ivar->rca, + ivar->raw_sd_status); mmc_app_decode_sd_status(ivar->raw_sd_status, &ivar->sd_status); if (ivar->sd_status.au_size != 0) { @@ -1404,7 +1540,7 @@ mmc_discover_cards(struct mmc_softc *sc) 16 << ivar->sd_status.au_size; } /* Find max supported bus width. */ - if ((mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) && + if ((host_caps & MMC_CAP_4_BIT_DATA) && (ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) ivar->bus_width = bus_width_4; @@ -1433,11 +1569,18 @@ mmc_discover_cards(struct mmc_softc *sc) mmc_select_card(sc, 0); return; } - mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid); ivar->rca = rca++; - mmc_set_relative_addr(sc, ivar->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; + } /* Get card CSD. */ - mmc_send_csd(sc, ivar->rca, ivar->raw_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; + } if (bootverbose || mmc_debug) device_printf(sc->dev, "%sard detected (CSD %08x%08x%08x%08x)\n", @@ -1451,7 +1594,7 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->erase_sector = ivar->csd.erase_sector * ivar->csd.write_bl_len / MMC_SECTOR_SIZE; - err = mmc_send_status(sc, ivar->rca, &status); + err = mmc_send_status(sc->dev, sc->dev, ivar->rca, &status); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading card status %d\n", err); @@ -1463,11 +1606,22 @@ mmc_discover_cards(struct mmc_softc *sc) break; } - mmc_select_card(sc, ivar->rca); + err = mmc_select_card(sc, ivar->rca); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, "Error selecting card %d\n", + err); + break; + } - /* Only MMC >= 4.x cards support EXT_CSD. */ + /* Only MMC >= 4.x devices support EXT_CSD. */ if (ivar->csd.spec_vers >= 4) { - mmc_send_ext_csd(sc, ivar->raw_ext_csd); + err = mmc_send_ext_csd(sc->dev, sc->dev, + ivar->raw_ext_csd); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, + "Error reading EXT_CSD %d\n", err); + break; + } /* Handle extended capacity from EXT_CSD */ sec_count = ivar->raw_ext_csd[EXT_CSD_SEC_CNT] + (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 1] << 8) + @@ -1477,28 +1631,54 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->sec_count = sec_count; ivar->high_cap = 1; } - /* Get card speed in high speed mode. */ - ivar->timing = bus_timing_hs; - if (ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] - & EXT_CSD_CARD_TYPE_52) - ivar->hs_tran_speed = MMC_TYPE_52_MAX_HS; - else if (ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] - & EXT_CSD_CARD_TYPE_26) - ivar->hs_tran_speed = MMC_TYPE_26_MAX_HS; - else - ivar->hs_tran_speed = ivar->tran_speed; + /* Get device speeds beyond normal mode. */ + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS_52) != 0) { + setbit(&ivar->timings, bus_timing_hs); + ivar->hs_tran_speed = MMC_TYPE_HS_52_MAX; + } else if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS_26) != 0) { + setbit(&ivar->timings, bus_timing_hs); + ivar->hs_tran_speed = MMC_TYPE_HS_26_MAX; + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_DDR_52_1_2V) != 0 && + (host_caps & MMC_CAP_SIGNALING_120) != 0) { + setbit(&ivar->timings, bus_timing_mmc_ddr52); + setbit(&ivar->vccq_120, bus_timing_mmc_ddr52); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_DDR_52_1_8V) != 0 && + (host_caps & MMC_CAP_SIGNALING_180) != 0) { + setbit(&ivar->timings, bus_timing_mmc_ddr52); + setbit(&ivar->vccq_180, bus_timing_mmc_ddr52); + } + /* + * Determine generic switch timeout (provided in + * units of 10 ms), defaulting to 500 ms. + */ + ivar->cmd6_time = 500 * 1000; + 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 * ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE]; - mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_ERASE_GRP_DEF, 1); + err = mmc_switch(sc->dev, sc->dev, ivar->rca, + EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GRP_DEF, + EXT_CSD_ERASE_GRP_DEF_EN, + ivar->cmd6_time, true); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, + "Error setting erase group %d\n", + err); + break; + } } - } else { - ivar->bus_width = bus_width_1; - ivar->timing = bus_timing_normal; } /* @@ -1513,6 +1693,8 @@ 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) @@ -1529,7 +1711,7 @@ mmc_discover_cards(struct mmc_softc *sc) static void mmc_rescan_cards(struct mmc_softc *sc) { - struct mmc_ivars *ivar = NULL; + struct mmc_ivars *ivar; device_t *devlist; int err, i, devcount; @@ -1537,9 +1719,10 @@ mmc_rescan_cards(struct mmc_softc *sc) return; for (i = 0; i < devcount; i++) { ivar = device_get_ivars(devlist[i]); - if (mmc_select_card(sc, ivar->rca)) { + 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", + device_printf(sc->dev, + "Card at relative address %d lost.\n", ivar->rca); device_delete_child(sc->dev, devlist[i]); free(ivar, M_DEVBUF); @@ -1561,7 +1744,8 @@ mmc_delete_cards(struct mmc_softc *sc) for (i = 0; i < devcount; i++) { ivar = device_get_ivars(devlist[i]); if (bootverbose || mmc_debug) - device_printf(sc->dev, "Card at relative address %d deleted.\n", + device_printf(sc->dev, + "Card at relative address %d deleted.\n", ivar->rca); device_delete_child(sc->dev, devlist[i]); free(ivar, M_DEVBUF); @@ -1591,7 +1775,8 @@ mmc_go_discovery(struct mmc_softc *sc) mmc_idle_cards(sc); err = mmc_send_if_cond(sc, 1); if ((bootverbose || mmc_debug) && err == 0) - device_printf(sc->dev, "SD 2.0 interface conditions: OK\n"); + device_printf(sc->dev, + "SD 2.0 interface conditions: OK\n"); if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { if (bootverbose || mmc_debug) device_printf(sc->dev, "SD probe: failed\n"); @@ -1601,13 +1786,15 @@ mmc_go_discovery(struct mmc_softc *sc) mmcbr_set_mode(dev, mode_mmc); if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { if (bootverbose || mmc_debug) - device_printf(sc->dev, "MMC probe: failed\n"); + device_printf(sc->dev, + "MMC probe: failed\n"); ocr = 0; /* Failed both, powerdown. */ } else if (bootverbose || mmc_debug) device_printf(sc->dev, "MMC probe: OK (OCR: 0x%08x)\n", ocr); } else if (bootverbose || mmc_debug) - device_printf(sc->dev, "SD probe: OK (OCR: 0x%08x)\n", ocr); + device_printf(sc->dev, "SD probe: OK (OCR: 0x%08x)\n", + ocr); sc->squelched--; mmcbr_set_ocr(dev, mmc_select_vdd(sc, ocr)); @@ -1615,7 +1802,7 @@ mmc_go_discovery(struct mmc_softc *sc) mmc_idle_cards(sc); } else { mmcbr_set_bus_mode(dev, opendrain); - mmcbr_set_clock(dev, CARD_ID_FREQUENCY); + mmcbr_set_clock(dev, SD_MMC_CARD_ID_FREQUENCY); mmcbr_update_ios(dev); /* XXX recompute vdd based on new cards? */ } @@ -1624,7 +1811,8 @@ mmc_go_discovery(struct mmc_softc *sc) * one card on the bus. */ if (bootverbose || mmc_debug) - device_printf(sc->dev, "Current OCR: 0x%08x\n", mmcbr_get_ocr(dev)); + device_printf(sc->dev, "Current OCR: 0x%08x\n", + 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); @@ -1646,56 +1834,69 @@ mmc_go_discovery(struct mmc_softc *sc) mmcbr_set_bus_mode(dev, pushpull); mmcbr_update_ios(dev); mmc_calculate_clock(sc); - bus_generic_attach(dev); -/* mmc_update_children_sysctl(dev);*/ } static int mmc_calculate_clock(struct mmc_softc *sc) { - int max_dtr, max_hs_dtr, max_timing; - int nkid, i, f_max; device_t *kids; struct mmc_ivars *ivar; - - f_max = mmcbr_get_f_max(sc->dev); - max_dtr = max_hs_dtr = f_max; - if ((mmcbr_get_caps(sc->dev) & MMC_CAP_HSPEED)) + int host_caps, i, nkid; + uint32_t dtr, max_dtr; + enum mmc_bus_timing max_timing, timing; + bool changed; + + 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"); - for (i = 0; i < nkid; i++) { - ivar = device_get_ivars(kids[i]); - if (ivar->timing < max_timing) - max_timing = ivar->timing; - if (ivar->tran_speed < max_dtr) - max_dtr = ivar->tran_speed; - if (ivar->hs_tran_speed < max_hs_dtr) - max_hs_dtr = ivar->hs_tran_speed; + 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 >= + bus_timing_normal; timing--) { + if (isset(&ivar->timings, timing)) { + max_timing = timing; + break; + } + } + changed = true; + } + dtr = mmc_timing_to_dtr(ivar, max_timing); + if (dtr < max_dtr) { + max_dtr = dtr; + changed = true; + } + } + } while (changed == true); + if (bootverbose || mmc_debug) { + device_printf(sc->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]); - if (ivar->timing == bus_timing_normal) + if ((ivar->timings & ~(1 << bus_timing_normal)) == 0) continue; - mmc_select_card(sc, ivar->rca); - mmc_set_timing(sc, max_timing); + 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); } mmc_select_card(sc, 0); free(kids, M_TEMP); - if (max_timing == bus_timing_hs) - max_dtr = max_hs_dtr; - if (bootverbose || mmc_debug) { - device_printf(sc->dev, - "setting transfer rate to %d.%03dMHz%s\n", - max_dtr / 1000000, (max_dtr / 1000) % 1000, - max_timing == bus_timing_hs ? " (high speed timing)" : ""); - } - mmcbr_set_timing(sc->dev, max_timing); mmcbr_set_clock(sc->dev, max_dtr); mmcbr_update_ios(sc->dev); - return max_dtr; + return (max_dtr); } static void @@ -1706,6 +1907,8 @@ mmc_scan(struct mmc_softc *sc) mmc_acquire_bus(dev, dev); mmc_go_discovery(sc); mmc_release_bus(dev, dev); + + bus_generic_attach(dev); } static int @@ -1716,6 +1919,9 @@ mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) switch (which) { default: return (EINVAL); + case MMC_IVAR_SPEC_VERS: + *result = ivar->csd.spec_vers; + break; case MMC_IVAR_DSR_IMP: *result = ivar->csd.dsr_imp; break; @@ -1762,6 +1968,7 @@ mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) static int mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) { + /* * None are writable ATM */ @@ -1814,4 +2021,4 @@ driver_t mmc_driver = { }; devclass_t mmc_devclass; -MODULE_VERSION(mmc, 1); +MODULE_VERSION(mmc, MMC_VERSION); diff --git a/freebsd/sys/dev/mmc/mmc_ioctl.h b/freebsd/sys/dev/mmc/mmc_ioctl.h new file mode 100644 index 00000000..97cff068 --- /dev/null +++ b/freebsd/sys/dev/mmc/mmc_ioctl.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_MMC_MMC_IOCTL_H_ +#define _DEV_MMC_MMC_IOCTL_H_ + +struct mmc_ioc_cmd { + int write_flag; /* 0: RD, 1: WR, (1 << 31): reliable WR */ + int is_acmd; /* 0: normal, 1: use CMD55 */ + uint32_t opcode; + uint32_t arg; + uint32_t response[4]; + u_int flags; + u_int blksz; + u_int blocks; + u_int __spare[4]; + uint32_t __pad; + uint64_t data_ptr; +}; + +#define mmc_ioc_cmd_set_data(mic, ptr) \ + (mic).data_ptr = (uint64_t)(uintptr_t)(ptr) + +struct mmc_ioc_multi_cmd { + uint64_t num_of_cmds; + struct mmc_ioc_cmd cmds[0]; +}; + +#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) + +/* Maximum accepted data transfer size */ +#define MMC_IOC_MAX_BYTES (512 * 256) +/* Maximum accepted number of commands */ +#define MMC_IOC_MAX_CMDS 255 + +#endif /* _DEV_MMC_MMC_IOCTL_H_ */ diff --git a/freebsd/sys/dev/mmc/mmc_private.h b/freebsd/sys/dev/mmc/mmc_private.h new file mode 100644 index 00000000..bbca0c60 --- /dev/null +++ b/freebsd/sys/dev/mmc/mmc_private.h @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * $FreeBSD$ + */ + +#ifndef DEV_MMC_PRIVATE_H +#define DEV_MMC_PRIVATE_H + +struct mmc_softc { + device_t dev; + 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; + struct timeval log_time; +}; + +#endif /* DEV_MMC_PRIVATE_H */ diff --git a/freebsd/sys/dev/mmc/mmc_subr.c b/freebsd/sys/dev/mmc/mmc_subr.c new file mode 100644 index 00000000..294fd9c0 --- /dev/null +++ b/freebsd/sys/dev/mmc/mmc_subr.c @@ -0,0 +1,254 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/time.h> + +#include <dev/mmc/bridge.h> +#include <dev/mmc/mmc_private.h> +#include <dev/mmc/mmc_subr.h> +#include <dev/mmc/mmcreg.h> +#include <dev/mmc/mmcbrvar.h> + +#include <rtems/bsd/local/mmcbus_if.h> + +#define CMD_RETRIES 3 +#define LOG_PPS 5 /* Log no more than 5 errors per second. */ + +int +mmc_wait_for_cmd(device_t brdev, device_t reqdev, struct mmc_command *cmd, + int retries) +{ + struct mmc_request mreq; + struct mmc_softc *sc; + int err; + + do { + memset(&mreq, 0, sizeof(mreq)); + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = 0; /* Retries done here, not in hardware. */ + cmd->mrq = &mreq; + if (cmd->data != NULL) + cmd->data->mrq = &mreq; + mreq.cmd = cmd; + if (MMCBUS_WAIT_FOR_REQUEST(brdev, reqdev, &mreq) != 0) + err = MMC_ERR_FAILED; + else + err = cmd->error; + } while (err != MMC_ERR_NONE && retries-- > 0); + + 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, "CMD%d failed, RESULT: %d\n", + cmd->opcode, err); + } + } + + return (err); +} + +int +mmc_wait_for_app_cmd(device_t brdev, device_t reqdev, uint16_t rca, + struct mmc_command *cmd, int retries) +{ + struct mmc_command appcmd; + struct mmc_softc *sc; + int err; + + sc = device_get_softc(brdev); + + /* Squelch error reporting at lower levels, we report below. */ + sc->squelched++; + do { + memset(&appcmd, 0, sizeof(appcmd)); + appcmd.opcode = MMC_APP_CMD; + appcmd.arg = (uint32_t)rca << 16; + appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + if (mmc_wait_for_cmd(brdev, reqdev, &appcmd, 0) != 0) + err = MMC_ERR_FAILED; + else + err = appcmd.error; + if (err == MMC_ERR_NONE) { + if (!(appcmd.resp[0] & R1_APP_CMD)) + err = MMC_ERR_FAILED; + else if (mmc_wait_for_cmd(brdev, reqdev, cmd, 0) != 0) + err = MMC_ERR_FAILED; + else + err = cmd->error; + } + } while (err != MMC_ERR_NONE && retries-- > 0); + 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", + cmd->opcode, err); + } + } + + return (err); +} + +int +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; + int err; + + KASSERT(timeout != 0, ("%s: no timeout", __func__)); + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = MMC_SWITCH_FUNC; + cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | (index << 16) | (value << 8) | + set; + /* + * If the hardware supports busy detection but the switch timeout + * exceeds the maximum host timeout, use a R1 instead of a R1B + * response in order to keep the hardware from timing out. + */ + if (mmcbr_get_caps(brdev) & MMC_CAP_WAIT_WHILE_BUSY && + timeout > mmcbr_get_max_busy_timeout(brdev)) + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + else + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + 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)); +} + +int +mmc_switch_status(device_t brdev, device_t reqdev, uint16_t rca, u_int timeout) +{ + struct timeval cur, end; + int err; + uint32_t status; + + KASSERT(timeout != 0, ("%s: no timeout", __func__)); + + /* + * Note that when using a R1B response in mmc_switch(), bridges of + * type MMC_CAP_WAIT_WHILE_BUSY will issue mmc_send_status() only + * once and then exit the loop. + */ + for (;;) { + err = mmc_send_status(brdev, reqdev, rca, &status); + if (err != MMC_ERR_NONE) + break; + if (R1_CURRENT_STATE(status) == R1_STATE_TRAN) + break; + getmicrouptime(&cur); + if (end.tv_sec == 0 && end.tv_usec == 0) { + end.tv_usec = timeout; + timevaladd(&end, &cur); + } + if (timevalcmp(&cur, &end, >)) { + err = MMC_ERR_TIMEOUT; + break; + } + } + if (err == MMC_ERR_NONE && R1_CURRENT_STATE(status) == R1_SWITCH_ERROR) + return (MMC_ERR_FAILED); + return (err); +} + +int +mmc_send_ext_csd(device_t brdev, device_t reqdev, uint8_t *rawextcsd) +{ + struct mmc_command cmd; + struct mmc_data data; + int err; + + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); + + memset(rawextcsd, 0, MMC_EXTCSD_SIZE); + cmd.opcode = MMC_SEND_EXT_CSD; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.data = &data; + + data.data = rawextcsd; + data.len = MMC_EXTCSD_SIZE; + data.flags = MMC_DATA_READ; + + err = mmc_wait_for_cmd(brdev, reqdev, &cmd, CMD_RETRIES); + return (err); +} + +int +mmc_send_status(device_t brdev, device_t reqdev, uint16_t rca, uint32_t *status) +{ + struct mmc_command cmd; + int err; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = (uint32_t)rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(brdev, reqdev, &cmd, CMD_RETRIES); + *status = cmd.resp[0]; + return (err); +} diff --git a/freebsd/sys/dev/mmc/mmc_subr.h b/freebsd/sys/dev/mmc/mmc_subr.h new file mode 100644 index 00000000..6e300d2f --- /dev/null +++ b/freebsd/sys/dev/mmc/mmc_subr.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * $FreeBSD$ + */ + +#ifndef DEV_MMC_SUBR_H +#define DEV_MMC_SUBR_H + +struct mmc_command; + +int mmc_send_ext_csd(device_t brdev, device_t reqdev, uint8_t *rawextcsd); +int mmc_send_status(device_t brdev, device_t reqdev, uint16_t rca, + uint32_t *status); +int mmc_switch(device_t brdev, device_t reqdev, uint16_t rca, uint8_t set, + uint8_t index, uint8_t value, u_int timeout, bool send_status); +int mmc_switch_status(device_t brdev, device_t reqdev, uint16_t rca, + u_int timeout); +int mmc_wait_for_app_cmd(device_t brdev, device_t reqdev, uint16_t rca, + struct mmc_command *cmd, int retries); +int mmc_wait_for_cmd(device_t brdev, device_t reqdev, struct mmc_command *cmd, + int retries); + +#endif /* DEV_MMC_SUBR_H */ diff --git a/freebsd/sys/dev/mmc/mmcbrvar.h b/freebsd/sys/dev/mmc/mmcbrvar.h index 1f0a5714..77c304b4 100644 --- a/freebsd/sys/dev/mmc/mmcbrvar.h +++ b/freebsd/sys/dev/mmc/mmcbrvar.h @@ -49,14 +49,14 @@ * or the SD Card Association to disclose or distribute any technical * information, know-how or other confidential information to any third party. * - * "$FreeBSD$" + * $FreeBSD$ */ #ifndef DEV_MMC_MMCBRVAR_H -#define DEV_MMC_MMCBRVAR_H +#define DEV_MMC_MMCBRVAR_H -#include <dev/mmc/bridge.h> #include <dev/mmc/mmcreg.h> + #include <rtems/bsd/local/mmcbr_if.h> enum mmcbr_device_ivars { @@ -71,15 +71,17 @@ enum mmcbr_device_ivars { MMCBR_IVAR_OCR, MMCBR_IVAR_POWER_MODE, MMCBR_IVAR_VDD, + MMCBR_IVAR_VCCQ, MMCBR_IVAR_CAPS, MMCBR_IVAR_TIMING, - MMCBR_IVAR_MAX_DATA + MMCBR_IVAR_MAX_DATA, + MMCBR_IVAR_MAX_BUSY_TIMEOUT }; /* - * Simplified accessors for pci devices + * Simplified accessors for bridge devices */ -#define MMCBR_ACCESSOR(var, ivar, type) \ +#define MMCBR_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(mmcbr, var, MMCBR, ivar, type) MMCBR_ACCESSOR(bus_mode, BUS_MODE, int) @@ -93,19 +95,30 @@ MMCBR_ACCESSOR(mode, MODE, int) MMCBR_ACCESSOR(ocr, OCR, int) MMCBR_ACCESSOR(power_mode, POWER_MODE, int) MMCBR_ACCESSOR(vdd, VDD, int) +MMCBR_ACCESSOR(vccq, VCCQ, int) MMCBR_ACCESSOR(caps, CAPS, int) MMCBR_ACCESSOR(timing, TIMING, int) MMCBR_ACCESSOR(max_data, MAX_DATA, int) +MMCBR_ACCESSOR(max_busy_timeout, MAX_BUSY_TIMEOUT, u_int) static int __inline mmcbr_update_ios(device_t dev) { + return (MMCBR_UPDATE_IOS(device_get_parent(dev), dev)); } static int __inline +mmcbr_switch_vccq(device_t dev) +{ + + return (MMCBR_SWITCH_VCCQ(device_get_parent(dev), dev)); +} + +static int __inline mmcbr_get_ro(device_t dev) { + return (MMCBR_GET_RO(device_get_parent(dev), dev)); } diff --git a/freebsd/sys/dev/mmc/mmcreg.h b/freebsd/sys/dev/mmc/mmcreg.h index ba4ca93a..359f31d5 100644 --- a/freebsd/sys/dev/mmc/mmcreg.h +++ b/freebsd/sys/dev/mmc/mmcreg.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -55,7 +56,7 @@ #define DEV_MMC_MMCREG_H /* - * This file contains the register definitions for the mmc and sd busses. + * This file contains the register definitions for the mmc and sd buses. * They are taken from publicly available sources. */ @@ -100,7 +101,7 @@ struct mmc_command { #define MMC_ERR_FAILED 4 #define MMC_ERR_INVALID 5 #define MMC_ERR_NO_MEMORY 6 -#define MMC_ERR_MAX 6 +#define MMC_ERR_MAX 6 struct mmc_data *data; /* Data segment with cmd */ struct mmc_request *mrq; /* backpointer to request */ }; @@ -140,6 +141,7 @@ struct mmc_command { #define R1_ERASE_RESET (1u << 13) /* sr, c */ #define R1_CURRENT_STATE_MASK (0xfu << 9) /* sx, b */ #define R1_READY_FOR_DATA (1u << 8) /* sx, a */ +#define R1_SWITCH_ERROR (1u << 7) /* sx, c */ #define R1_APP_CMD (1u << 5) /* sr, c */ #define R1_AKE_SEQ_ERROR (1u << 3) /* er, c */ #define R1_STATUS(x) ((x) & 0xFFFFE000) @@ -184,7 +186,7 @@ struct mmc_request { #define MMC_SET_RELATIVE_ADDR 3 #define SD_SEND_RELATIVE_ADDR 3 #define MMC_SET_DSR 4 - /* reserved: 5 */ +#define MMC_SLEEP_AWAKE 5 #define MMC_SWITCH_FUNC 6 #define MMC_SWITCH_FUNC_CMDS 0 #define MMC_SWITCH_FUNC_SET 1 @@ -207,11 +209,11 @@ struct mmc_request { #define MMC_SET_BLOCKLEN 16 #define MMC_READ_SINGLE_BLOCK 17 #define MMC_READ_MULTIPLE_BLOCK 18 - /* reserved: 19 */ +#define MMC_SEND_TUNING_BLOCK 19 +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* Class 3: Stream write commands */ #define MMC_WRITE_DAT_UNTIL_STOP 20 - /* reserved: 21 */ /* reserved: 22 */ /* Class 4: Block oriented write commands */ @@ -278,7 +280,6 @@ struct mmc_request { /* reserved: 50 */ /* reserved: 57 */ - /* Application specific commands for SD */ #define ACMD_SET_BUS_WIDTH 6 #define ACMD_SD_STATUS 13 @@ -291,52 +292,153 @@ struct mmc_request { /* * EXT_CSD fields */ -#define EXT_CSD_ERASE_GRP_DEF 175 /* R/W */ -#define EXT_CSD_BUS_WIDTH 183 /* R/W */ -#define EXT_CSD_HS_TIMING 185 /* R/W */ -#define EXT_CSD_CARD_TYPE 196 /* RO */ -#define EXT_CSD_REV 192 /* RO */ -#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ -#define EXT_CSD_ERASE_TO_MULT 223 /* RO */ -#define EXT_CSD_ERASE_GRP_SIZE 224 /* RO */ +#define EXT_CSD_EXT_PART_ATTR 52 /* R/W, 2 bytes */ +#define EXT_CSD_ENH_START_ADDR 136 /* R/W, 4 bytes */ +#define EXT_CSD_ENH_SIZE_MULT 140 /* R/W, 3 bytes */ +#define EXT_CSD_GP_SIZE_MULT 143 /* R/W, 12 bytes */ +#define EXT_CSD_PART_SET 155 /* R/W */ +#define EXT_CSD_PART_ATTR 156 /* R/W */ +#define EXT_CSD_PART_SUPPORT 160 /* RO */ +#define EXT_CSD_RPMB_MULT 168 /* RO */ +#define EXT_CSD_BOOT_WP_STATUS 174 /* RO */ +#define EXT_CSD_ERASE_GRP_DEF 175 /* R/W */ +#define EXT_CSD_PART_CONFIG 179 /* R/W */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_STROBE_SUPPORT 184 /* RO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_DRIVER_STRENGTH 197 /* RO */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_PART_SWITCH_TO 199 /* RO */ +#define EXT_CSD_PWR_CL_52_195 200 /* RO */ +#define EXT_CSD_PWR_CL_26_195 201 /* RO */ +#define EXT_CSD_PWR_CL_52_360 202 /* RO */ +#define EXT_CSD_PWR_CL_26_360 203 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ +#define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */ +#define EXT_CSD_ERASE_TO_MULT 223 /* RO */ +#define EXT_CSD_ERASE_GRP_SIZE 224 /* RO */ +#define EXT_CSD_BOOT_SIZE_MULT 226 /* RO */ +#define EXT_CSD_PWR_CL_200_195 236 /* RO */ +#define EXT_CSD_PWR_CL_200_360 237 /* RO */ +#define EXT_CSD_PWR_CL_52_195_DDR 238 /* RO */ +#define EXT_CSD_PWR_CL_52_360_DDR 239 /* RO */ +#define EXT_CSD_GEN_CMD6_TIME 248 /* RO */ +#define EXT_CSD_PWR_CL_200_360_DDR 253 /* RO */ /* * EXT_CSD field definitions */ -#define EXT_CSD_CMD_SET_NORMAL 1 -#define EXT_CSD_CMD_SET_SECURE 2 -#define EXT_CSD_CMD_SET_CPSECURE 4 - -#define EXT_CSD_CARD_TYPE_26 1 -#define EXT_CSD_CARD_TYPE_52 2 - -#define EXT_CSD_BUS_WIDTH_1 0 -#define EXT_CSD_BUS_WIDTH_4 1 -#define EXT_CSD_BUS_WIDTH_8 2 - -#define MMC_TYPE_26_MAX_HS 26000000 -#define MMC_TYPE_52_MAX_HS 52000000 +#define EXT_CSD_EXT_PART_ATTR_DEFAULT 0x0 +#define EXT_CSD_EXT_PART_ATTR_SYSTEMCODE 0x1 +#define EXT_CSD_EXT_PART_ATTR_NPERSISTENT 0x2 + +#define EXT_CSD_PART_SET_COMPLETED 0x01 + +#define EXT_CSD_PART_ATTR_ENH_USR 0x01 +#define EXT_CSD_PART_ATTR_ENH_GP0 0x02 +#define EXT_CSD_PART_ATTR_ENH_GP1 0x04 +#define EXT_CSD_PART_ATTR_ENH_GP2 0x08 +#define EXT_CSD_PART_ATTR_ENH_GP3 0x10 +#define EXT_CSD_PART_ATTR_ENH_MASK 0x1f + +#define EXT_CSD_PART_SUPPORT_EN 0x01 +#define EXT_CSD_PART_SUPPORT_ENH_ATTR_EN 0x02 +#define EXT_CSD_PART_SUPPORT_EXT_ATTR_EN 0x04 + +#define EXT_CSD_BOOT_WP_STATUS_BOOT0_PWR 0x01 +#define EXT_CSD_BOOT_WP_STATUS_BOOT0_PERM 0x02 +#define EXT_CSD_BOOT_WP_STATUS_BOOT0_MASK 0x03 +#define EXT_CSD_BOOT_WP_STATUS_BOOT1_PWR 0x04 +#define EXT_CSD_BOOT_WP_STATUS_BOOT1_PERM 0x08 +#define EXT_CSD_BOOT_WP_STATUS_BOOT1_MASK 0x0c + +#define EXT_CSD_ERASE_GRP_DEF_EN 0x01 + +#define EXT_CSD_PART_CONFIG_ACC_DEFAULT 0x00 +#define EXT_CSD_PART_CONFIG_ACC_BOOT0 0x01 +#define EXT_CSD_PART_CONFIG_ACC_BOOT1 0x02 +#define EXT_CSD_PART_CONFIG_ACC_RPMB 0x03 +#define EXT_CSD_PART_CONFIG_ACC_GP0 0x04 +#define EXT_CSD_PART_CONFIG_ACC_GP1 0x05 +#define EXT_CSD_PART_CONFIG_ACC_GP2 0x06 +#define EXT_CSD_PART_CONFIG_ACC_GP3 0x07 +#define EXT_CSD_PART_CONFIG_ACC_MASK 0x07 +#define EXT_CSD_PART_CONFIG_BOOT0 0x08 +#define EXT_CSD_PART_CONFIG_BOOT1 0x10 +#define EXT_CSD_PART_CONFIG_BOOT_USR 0x38 +#define EXT_CSD_PART_CONFIG_BOOT_MASK 0x38 +#define EXT_CSD_PART_CONFIG_BOOT_ACK 0x40 + +#define EXT_CSD_CMD_SET_NORMAL 1 +#define EXT_CSD_CMD_SET_SECURE 2 +#define EXT_CSD_CMD_SET_CPSECURE 4 + +#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_DRV_STR_SHIFT 4 + +#define EXT_CSD_POWER_CLASS_8BIT_MASK 0xf0 +#define EXT_CSD_POWER_CLASS_8BIT_SHIFT 4 +#define EXT_CSD_POWER_CLASS_4BIT_MASK 0x0f +#define EXT_CSD_POWER_CLASS_4BIT_SHIFT 0 + +#define EXT_CSD_CARD_TYPE_HS_26 0x0001 +#define EXT_CSD_CARD_TYPE_HS_52 0x0002 +#define EXT_CSD_CARD_TYPE_DDR_52_1_8V 0x0004 +#define EXT_CSD_CARD_TYPE_DDR_52_1_2V 0x0008 +#define EXT_CSD_CARD_TYPE_HS200_1_8V 0x0010 +#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 +#define EXT_CSD_BUS_WIDTH_8 2 +#define EXT_CSD_BUS_WIDTH_4_DDR 5 +#define EXT_CSD_BUS_WIDTH_8_DDR 6 +#define EXT_CSD_BUS_WIDTH_ES 0x80 + +#define MMC_TYPE_HS_26_MAX 26000000 +#define MMC_TYPE_HS_52_MAX 52000000 +#define MMC_TYPE_DDR52_MAX 52000000 +#define MMC_TYPE_HS200_HS400ES_MAX 200000000 /* * SD bus widths */ -#define SD_BUS_WIDTH_1 0 -#define SD_BUS_WIDTH_4 2 +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 /* * SD Switch */ -#define SD_SWITCH_MODE_CHECK 0 -#define SD_SWITCH_MODE_SET 1 -#define SD_SWITCH_GROUP1 0 -#define SD_SWITCH_NORMAL_MODE 0 -#define SD_SWITCH_HS_MODE 1 -#define SD_SWITCH_NOCHANGE 0xF +#define SD_SWITCH_MODE_CHECK 0 +#define SD_SWITCH_MODE_SET 1 +#define SD_SWITCH_GROUP1 0 +#define SD_SWITCH_NORMAL_MODE 0 +#define SD_SWITCH_HS_MODE 1 +#define SD_SWITCH_SDR50_MODE 2 +#define SD_SWITCH_SDR104_MODE 3 +#define SD_SWITCH_DDR50 4 +#define SD_SWITCH_NOCHANGE 0xF #define SD_CLR_CARD_DETECT 0 #define SD_SET_CARD_DETECT 1 -#define SD_MAX_HS 50000000 +#define SD_HS_MAX 50000000 +#define SD_DDR50_MAX 50000000 +#define SD_SDR12_MAX 25000000 +#define SD_SDR25_MAX 50000000 +#define SD_SDR50_MAX 100000000 +#define SD_SDR104_MAX 208000000 + +/* Specifications require 400 kHz max. during ID phase. */ +#define SD_MMC_CARD_ID_FREQUENCY 400000 /* OCR bits */ @@ -373,6 +475,12 @@ struct mmc_request { #define MMC_OCR_340_350 (1U << 22) /* Vdd voltage 3.40 ~ 3.50 */ #define MMC_OCR_350_360 (1U << 23) /* Vdd voltage 3.50 ~ 3.60 */ #define MMC_OCR_MAX_VOLTAGE_SHIFT 23 +#define MMC_OCR_S18R (1U << 24) /* Switching to 1.8 V requested (SD) */ +#define MMC_OCR_S18A MMC_OCR_S18R /* Switching to 1.8 V accepted (SD) */ +#define MMC_OCR_XPC (1U << 28) /* SDXC Power Control */ +#define MMC_OCR_ACCESS_MODE_BYTE (0U << 29) /* Access Mode Byte (MMC) */ +#define MMC_OCR_ACCESS_MODE_SECT (1U << 29) /* Access Mode Sector (MMC) */ +#define MMC_OCR_ACCESS_MODE_MASK (3U << 29) #define MMC_OCR_CCS (1u << 30) /* Card Capacity status (SD vs SDHC) */ #define MMC_OCR_CARD_BUSY (1U << 31) /* Card Power up status */ @@ -419,8 +527,8 @@ struct mmc_scr { unsigned char sda_vsn; unsigned char bus_widths; -#define SD_SCR_BUS_WIDTH_1 (1<<0) -#define SD_SCR_BUS_WIDTH_4 (1<<2) +#define SD_SCR_BUS_WIDTH_1 (1 << 0) +#define SD_SCR_BUS_WIDTH_4 (1 << 2) }; struct mmc_sd_status @@ -438,11 +546,21 @@ struct mmc_sd_status }; /* + * Various MMC/SD constants + */ +#define MMC_BOOT_RPMB_BLOCK_SIZE (128 * 1024) + +#define MMC_EXTCSD_SIZE 512 + +#define MMC_PART_GP_MAX 4 +#define MMC_PART_MAX 8 + +/* * 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 * byte sector size anywhere, so we assume that such cards are very rare * and only note their existence in passing here... */ -#define MMC_SECTOR_SIZE 512 +#define MMC_SECTOR_SIZE 512 #endif /* DEV_MMCREG_H */ diff --git a/freebsd/sys/dev/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c index a39d51fe..c1cfbe8b 100644 --- a/freebsd/sys/dev/mmc/mmcsd.c +++ b/freebsd/sys/dev/mmc/mmcsd.c @@ -3,6 +3,7 @@ /*- * Copyright (c) 2006 Bernd Walter. All rights reserved. * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -60,16 +61,23 @@ __FBSDID("$FreeBSD$"); #include <sys/bio.h> #include <sys/bus.h> #include <sys/conf.h> +#include <sys/fcntl.h> +#include <sys/ioccom.h> #include <sys/kernel.h> #include <sys/kthread.h> #include <rtems/bsd/sys/lock.h> #include <sys/malloc.h> #include <sys/module.h> #include <sys/mutex.h> +#include <sys/slicer.h> #include <sys/time.h> + #include <geom/geom.h> #include <geom/geom_disk.h> +#include <dev/mmc/bridge.h> +#include <dev/mmc/mmc_ioctl.h> +#include <dev/mmc/mmc_subr.h> #include <dev/mmc/mmcbrvar.h> #include <dev/mmc/mmcreg.h> #include <dev/mmc/mmcvar.h> @@ -88,19 +96,48 @@ __FBSDID("$FreeBSD$"); #define kproc_exit kthread_exit #endif -struct mmcsd_softc { - device_t dev; - struct mtx sc_mtx; +#define MMCSD_CMD_RETRIES 5 + +#define MMCSD_FMT_BOOT "mmcsd%dboot" +#define MMCSD_FMT_GP "mmcsd%dgp" +#define MMCSD_FMT_RPMB "mmcsd%drpmb" +#define MMCSD_LABEL_ENH "enh" + +#define MMCSD_PART_NAMELEN (16 + 1) + +struct mmcsd_softc; + +struct mmcsd_part { + struct mtx part_mtx; + struct mmcsd_softc *sc; #ifndef __rtems__ struct disk *disk; struct proc *p; struct bio_queue_head bio_queue; daddr_t eblock, eend; /* Range remaining after the last erase. */ +#endif /* __rtems__ */ + u_int cnt; + u_int type; int running; int suspend; + bool ro; + char name[MMCSD_PART_NAMELEN]; +}; + +struct mmcsd_softc { + device_t dev; + device_t mmcbr; + struct mmcsd_part *part[MMC_PART_MAX]; + enum mmc_card_mode mode; + uint8_t part_curr; /* Partition currently switched to */ + uint8_t ext_csd[MMC_EXTCSD_SIZE]; + uint16_t rca; + uint32_t part_time; /* Partition switch timeout [us] */ + off_t enh_base; /* Enhanced user data area slice base ... */ + off_t enh_size; /* ... and size [bytes] */ int log_count; struct timeval log_time; -#endif /* __rtems__ */ + struct cdev *rpmb_dev; }; #ifndef __rtems__ @@ -127,26 +164,50 @@ static int mmcsd_probe(device_t dev); /* disk routines */ static int mmcsd_close(struct disk *dp); static int mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, - off_t offset, size_t length); + off_t offset, size_t length); +static int mmcsd_getattr(struct bio *); +static int mmcsd_ioctl_disk(struct disk *disk, u_long cmd, void *data, + int fflag, struct thread *td); static int mmcsd_open(struct disk *dp); static void mmcsd_strategy(struct bio *bp); static void mmcsd_task(void *arg); #endif /* __rtems__ */ +/* RMPB cdev interface */ +static int mmcsd_ioctl_rpmb(struct cdev *dev, u_long cmd, caddr_t data, + int fflag, struct thread *td); + +static void mmcsd_add_part(struct mmcsd_softc *sc, u_int type, + const char *name, u_int cnt, off_t media_size, off_t erase_size, bool ro); static int mmcsd_bus_bit_width(device_t dev); #ifndef __rtems__ -static daddr_t mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp); -static daddr_t mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp); +static daddr_t mmcsd_delete(struct mmcsd_part *part, struct bio *bp); #endif /* __rtems__ */ - -#define MMCSD_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define MMCSD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define MMCSD_LOCK_INIT(_sc) \ - mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ - "mmcsd", MTX_DEF) -#define MMCSD_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); -#define MMCSD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); -#define MMCSD_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); +static int mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, + int fflag); +static int mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, + int fflag); +static uintmax_t mmcsd_pretty_size(off_t size, char *unit); +#ifndef __rtems__ +static daddr_t mmcsd_rw(struct mmcsd_part *part, struct bio *bp); +#endif /* __rtems__ */ +static int mmcsd_set_blockcount(struct mmcsd_softc *sc, u_int count, bool rel); +#ifndef __rtems__ +static int mmcsd_slicer(device_t dev, const char *provider, + struct flash_slice *slices, int *nslices); +#endif /* __rtems__ */ +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); static int mmcsd_probe(device_t dev) @@ -159,10 +220,9 @@ mmcsd_probe(device_t dev) #ifdef __rtems__ static rtems_status_code -rtems_bsd_mmcsd_set_block_size(struct mmcsd_softc *self, uint32_t block_size) +rtems_bsd_mmcsd_set_block_size(device_t dev, uint32_t block_size) { rtems_status_code status_code = RTEMS_SUCCESSFUL; - device_t dev = self->dev; struct mmc_command cmd; struct mmc_request req; @@ -183,10 +243,11 @@ rtems_bsd_mmcsd_set_block_size(struct mmcsd_softc *self, uint32_t block_size) } static int -rtems_bsd_mmcsd_disk_read_write(struct mmcsd_softc *self, rtems_blkdev_request *blkreq) +rtems_bsd_mmcsd_disk_read_write(struct mmcsd_part *part, rtems_blkdev_request *blkreq) { rtems_status_code status_code = RTEMS_SUCCESSFUL; - device_t dev = self->dev; + struct mmcsd_softc *sc = part->sc; + device_t dev = sc->dev; int shift = mmc_get_high_cap(dev) ? 0 : 9; int rca = mmc_get_rca(dev); uint32_t buffer_count = blkreq->bufnum; @@ -216,7 +277,7 @@ rtems_bsd_mmcsd_disk_read_write(struct mmcsd_softc *self, rtems_blkdev_request * data_flags = MMC_DATA_READ; } - MMCSD_LOCK(self); + MMCSD_PART_LOCK(part); for (i = 0; i < buffer_count; ++i) { rtems_blkdev_sg_buffer *sg = &blkreq->bufs [i]; @@ -293,7 +354,7 @@ rtems_bsd_mmcsd_disk_read_write(struct mmcsd_softc *self, rtems_blkdev_request * error: - MMCSD_UNLOCK(self); + MMCSD_PART_UNLOCK(part); rtems_blkdev_request_done(blkreq, status_code); @@ -303,12 +364,12 @@ error: static int rtems_bsd_mmcsd_disk_ioctl(rtems_disk_device *dd, uint32_t req, void *arg) { - struct mmcsd_softc *self = rtems_disk_get_driver_data(dd); if (req == RTEMS_BLKIO_REQUEST) { + struct mmcsd_part *part = rtems_disk_get_driver_data(dd); rtems_blkdev_request *blkreq = arg; - return rtems_bsd_mmcsd_disk_read_write(self, blkreq); + return rtems_bsd_mmcsd_disk_read_write(part, blkreq); } else if (req == RTEMS_BLKIO_CAPABILITIES) { *(uint32_t *) arg = RTEMS_BLKDEV_CAP_MULTISECTOR_CONT; return 0; @@ -321,11 +382,12 @@ static rtems_status_code rtems_bsd_mmcsd_attach_worker(rtems_media_state state, const char *src, char **dest, void *arg) { rtems_status_code status_code = RTEMS_SUCCESSFUL; - struct mmcsd_softc *self = arg; + struct mmcsd_part *part = arg; char *disk = NULL; if (state == RTEMS_MEDIA_STATE_READY) { - device_t dev = self->dev; + struct mmcsd_softc *sc = part->sc; + device_t dev = sc->dev; uint32_t block_count = mmc_get_media_size(dev); uint32_t block_size = MMC_SECTOR_SIZE; @@ -337,14 +399,14 @@ rtems_bsd_mmcsd_attach_worker(rtems_media_state state, const char *src, char **d MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev); - status_code = rtems_bsd_mmcsd_set_block_size(self, block_size); + status_code = rtems_bsd_mmcsd_set_block_size(dev, block_size); if (status_code != RTEMS_SUCCESSFUL) { printf("OOPS: set block size failed\n"); goto error; } status_code = rtems_blkdev_create(disk, block_size, - block_count, rtems_bsd_mmcsd_disk_ioctl, self); + block_count, rtems_bsd_mmcsd_disk_ioctl, part); if (status_code != RTEMS_SUCCESSFUL) { goto error; } @@ -363,128 +425,412 @@ error: static int mmcsd_attach(device_t dev) { + device_t mmcbr; struct mmcsd_softc *sc; -#ifndef __rtems__ - struct disk *d; -#else /* __rtems__ */ - struct { - char d_ident[16]; - char d_descr[64]; - } x, *d = &x; -#endif /* __rtems__ */ - intmax_t mb; - uint32_t speed; - uint32_t maxblocks; - char unit; + const uint8_t *ext_csd; + off_t erase_size, sector_size, size, wp_size; + uintmax_t bytes; + int err, i; + uint8_t rev; + bool comp, ro; + char unit[2]; sc = device_get_softc(dev); sc->dev = dev; - MMCSD_LOCK_INIT(sc); + sc->mmcbr = mmcbr = device_get_parent(dev); + sc->mode = mmcbr_get_mode(mmcbr); + sc->rca = mmc_get_rca(dev); -#ifndef __rtems__ - d = sc->disk = disk_alloc(); - d->d_open = mmcsd_open; - d->d_close = mmcsd_close; - d->d_strategy = mmcsd_strategy; - d->d_dump = mmcsd_dump; - d->d_name = "mmcsd"; - d->d_drv1 = sc; - d->d_sectorsize = mmc_get_sector_size(dev); - d->d_maxsize = mmc_get_max_data(dev) * d->d_sectorsize; - d->d_mediasize = (off_t)mmc_get_media_size(dev) * d->d_sectorsize; - d->d_stripesize = mmc_get_erase_sector(dev) * d->d_sectorsize; - d->d_unit = device_get_unit(dev); - d->d_flags = DISKFLAG_CANDELETE; - d->d_delmaxsize = mmc_get_erase_sector(dev) * d->d_sectorsize; -#endif /* __rtems__ */ - strlcpy(d->d_ident, mmc_get_card_sn_string(dev), sizeof(d->d_ident)); - strlcpy(d->d_descr, mmc_get_card_id_string(dev), sizeof(d->d_descr)); + /* Only MMC >= 4.x devices support EXT_CSD. */ + if (mmc_get_spec_vers(dev) >= 4) { + MMCBUS_ACQUIRE_BUS(mmcbr, dev); + err = mmc_send_ext_csd(mmcbr, dev, sc->ext_csd); + MMCBUS_RELEASE_BUS(mmcbr, dev); + if (err != MMC_ERR_NONE) + bzero(sc->ext_csd, sizeof(sc->ext_csd)); + } + ext_csd = sc->ext_csd; -#ifndef __rtems__ /* - * Display in most natural units. There's no cards < 1MB. The SD - * standard goes to 2GiB due to its reliance on FAT, but the data - * format supports up to 4GiB and some card makers push it up to this - * limit. The SDHC standard only goes to 32GiB due to FAT32, but the - * data format supports up to 2TiB however. 2048GB isn't too ugly, so - * we note it in passing here and don't add the code to print - * TB). Since these cards are sold in terms of MB and GB not MiB and - * GiB, report them like that. We also round to the nearest unit, since - * many cards are a few percent short, even of the power of 10 size. + * Enhanced user data area and general purpose partitions are only + * supported in revision 1.4 (EXT_CSD_REV == 4) and later, the RPMB + * partition in revision 1.5 (MMC v4.41, EXT_CSD_REV == 5) and later. */ - mb = (d->d_mediasize + 1000000 / 2 - 1) / 1000000; -#else /* __rtems__ */ - mb = mmc_get_media_size(dev); - mb *= mmc_get_sector_size(dev); - mb = (mb + 1000000 / 2 - 1) / 1000000; -#endif /* __rtems__ */ - unit = 'M'; - if (mb >= 1000) { - unit = 'G'; - mb = (mb + 1000 / 2 - 1) / 1000; + rev = ext_csd[EXT_CSD_REV]; + + /* + * Ignore user-creatable enhanced user data area and general purpose + * partitions partitions as long as partitioning hasn't been finished. + */ + comp = (ext_csd[EXT_CSD_PART_SET] & EXT_CSD_PART_SET_COMPLETED) != 0; + + /* + * Add enhanced user data area slice, unless it spans the entirety of + * the user data area. The enhanced area is of a multiple of high + * capacity write protect groups ((ERASE_GRP_SIZE + HC_WP_GRP_SIZE) * + * 512 KB) and its offset given in either sectors or bytes, depending + * on whether it's a high capacity device or not. + * NB: The slicer and its slices need to be registered before adding + * the disk for the corresponding user data area as re-tasting is + * racy. + */ + sector_size = mmc_get_sector_size(dev); + size = ext_csd[EXT_CSD_ENH_SIZE_MULT] + + (ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) + + (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16); + if (rev >= 4 && comp == TRUE && size > 0 && + (ext_csd[EXT_CSD_PART_SUPPORT] & + EXT_CSD_PART_SUPPORT_ENH_ATTR_EN) != 0 && + (ext_csd[EXT_CSD_PART_ATTR] & (EXT_CSD_PART_ATTR_ENH_USR)) != 0) { + erase_size = ext_csd[EXT_CSD_ERASE_GRP_SIZE] * 1024 * + MMC_SECTOR_SIZE; + wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + size *= erase_size * wp_size; + if (size != mmc_get_media_size(dev) * sector_size) { + sc->enh_size = size; + sc->enh_base = (ext_csd[EXT_CSD_ENH_START_ADDR] + + (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); + } else if (bootverbose) + device_printf(dev, + "enhanced user data area spans entire device\n"); } + + /* + * Add default partition. This may be the only one or the user + * data area in case partitions are supported. + */ + 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); + + if (mmc_get_spec_vers(dev) < 3) + return (0); + + /* Belatedly announce enhanced user data slice. */ + if (sc->enh_size != 0) { + bytes = mmcsd_pretty_size(size, unit); + printf(FLASH_SLICES_FMT ": %ju%sB enhanced user data area " + "slice offset 0x%jx at %s\n", device_get_nameunit(dev), + MMCSD_LABEL_ENH, bytes, unit, (uintmax_t)sc->enh_base, + device_get_nameunit(dev)); + } + + /* + * Determine partition switch timeout (provided in units of 10 ms) + * and ensure it's at least 300 ms as some eMMC chips lie. + */ + sc->part_time = max(ext_csd[EXT_CSD_PART_SWITCH_TO] * 10 * 1000, + 300 * 1000); + + /* Add boot partitions, which are of a fixed multiple of 128 KB. */ + size = ext_csd[EXT_CSD_BOOT_SIZE_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE; + if (size > 0 && (mmcbr_get_caps(mmcbr) & MMC_CAP_BOOT_NOACC) == 0) { + mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_BOOT0, + MMCSD_FMT_BOOT, 0, size, MMC_BOOT_RPMB_BLOCK_SIZE, + ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] & + EXT_CSD_BOOT_WP_STATUS_BOOT0_MASK) != 0)); + mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_BOOT1, + MMCSD_FMT_BOOT, 1, size, MMC_BOOT_RPMB_BLOCK_SIZE, + ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] & + EXT_CSD_BOOT_WP_STATUS_BOOT1_MASK) != 0)); + } + + /* Add RPMB partition, which also is of a fixed multiple of 128 KB. */ + size = ext_csd[EXT_CSD_RPMB_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE; + if (rev >= 5 && size > 0) + mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_RPMB, + MMCSD_FMT_RPMB, 0, size, MMC_BOOT_RPMB_BLOCK_SIZE, ro); + + if (rev <= 3 || comp == FALSE) + return (0); + + /* + * Add general purpose partitions, which are of a multiple of high + * capacity write protect groups, too. + */ + if ((ext_csd[EXT_CSD_PART_SUPPORT] & EXT_CSD_PART_SUPPORT_EN) != 0) { + erase_size = ext_csd[EXT_CSD_ERASE_GRP_SIZE] * 1024 * + MMC_SECTOR_SIZE; + wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + for (i = 0; i < MMC_PART_GP_MAX; i++) { + size = ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3] + + (ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3 + 1] << 8) + + (ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3 + 2] << 16); + if (size == 0) + continue; + mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_GP0 + i, + MMCSD_FMT_GP, i, size * erase_size * wp_size, + erase_size, ro); + } + } + return (0); +} + +static uintmax_t +mmcsd_pretty_size(off_t size, char *unit) +{ + uintmax_t bytes; + int i; + /* - * Report the clock speed of the underlying hardware, which might be - * different than what the card reports due to hardware limitations. - * Report how many blocks the hardware transfers at once. + * Display in most natural units. There's no card < 1MB. However, + * RPMB partitions occasionally are smaller than that, though. The + * SD standard goes to 2 GiB due to its reliance on FAT, but the data + * format supports up to 4 GiB and some card makers push it up to this + * limit. The SDHC standard only goes to 32 GiB due to FAT32, but the + * data format supports up to 2 TiB however. 2048 GB isn't too ugly, + * so we note it in passing here and don't add the code to print TB). + * Since these cards are sold in terms of MB and GB not MiB and GiB, + * report them like that. We also round to the nearest unit, since + * many cards are a few percent short, even of the power of 10 size. */ - speed = mmcbr_get_clock(device_get_parent(dev)); - maxblocks = mmc_get_max_data(dev); - device_printf(dev, "%ju%cB <%s>%s at %s %d.%01dMHz/%dbit/%d-block\n", - mb, unit, d->d_descr, - mmc_get_read_only(dev) ? " (read-only)" : "", - device_get_nameunit(device_get_parent(dev)), - speed / 1000000, (speed / 100000) % 10, - mmcsd_bus_bit_width(dev), maxblocks); + bytes = size; + unit[0] = unit[1] = '\0'; + for (i = 0; i <= 2 && bytes >= 1000; i++) { + bytes = (bytes + 1000 / 2 - 1) / 1000; + switch (i) { + case 0: + unit[0] = 'k'; + break; + case 1: + unit[0] = 'M'; + break; + case 2: + unit[0] = 'G'; + break; + default: + break; + } + } + return (bytes); +} + +static struct cdevsw mmcsd_rpmb_cdevsw = { + .d_version = D_VERSION, + .d_name = "mmcsdrpmb", + .d_ioctl = mmcsd_ioctl_rpmb +}; + +static void +mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, + off_t media_size, off_t erase_size, bool ro) +{ + struct make_dev_args args; + device_t dev, mmcbr; + const char *ext; + const uint8_t *ext_csd; + struct mmcsd_part *part; #ifndef __rtems__ - disk_create(d, DISK_VERSION); - 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, "%s: mmc/sd card", - device_get_nameunit(dev)); + struct disk *d; +#endif /* __rtems__ */ + uintmax_t bytes; + u_int gp; + uint32_t speed; + uint8_t extattr; + bool enh; + char unit[2]; + + dev = sc->dev; + mmcbr = sc->mmcbr; + part = sc->part[type] = malloc(sizeof(*part), M_DEVBUF, + M_WAITOK | M_ZERO); + part->sc = sc; + part->cnt = cnt; + part->type = type; + part->ro = ro; + snprintf(part->name, sizeof(part->name), name, device_get_unit(dev)); + + /* For the RPMB partition, allow IOCTL access only. */ + if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) { + make_dev_args_init(&args); + args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; + args.mda_devsw = &mmcsd_rpmb_cdevsw; + args.mda_uid = UID_ROOT; + args.mda_gid = GID_OPERATOR; + args.mda_mode = 0640; + args.mda_si_drv1 = part; + if (make_dev_s(&args, &sc->rpmb_dev, "%s", part->name) != 0) { + device_printf(dev, "Failed to make RPMB device\n"); + free(part, M_DEVBUF); + return; + } + } else { + MMCSD_PART_LOCK_INIT(part); + +#ifndef __rtems__ + d = part->disk = disk_alloc(); + d->d_open = mmcsd_open; + d->d_close = mmcsd_close; + d->d_strategy = mmcsd_strategy; + d->d_ioctl = mmcsd_ioctl_disk; + d->d_dump = mmcsd_dump; + d->d_getattr = mmcsd_getattr; + 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_mediasize = media_size; + d->d_stripesize = erase_size; + d->d_unit = cnt; + d->d_flags = DISKFLAG_CANDELETE; + d->d_delmaxsize = erase_size; + strlcpy(d->d_ident, mmc_get_card_sn_string(dev), + sizeof(d->d_ident)); + strlcpy(d->d_descr, mmc_get_card_id_string(dev), + sizeof(d->d_descr)); + d->d_rotation_rate = DISK_RR_NON_ROTATING; + + disk_create(d, DISK_VERSION); + bioq_init(&part->bio_queue); + + part->running = 1; + kproc_create(&mmcsd_task, part, &part->p, 0, 0, + "%s%d: mmc/sd card", part->name, cnt); #else /* __rtems__ */ - rtems_status_code status_code = rtems_media_server_disk_attach( - device_get_name(dev), - rtems_bsd_mmcsd_attach_worker, - sc - ); - BSD_ASSERT(status_code == RTEMS_SUCCESSFUL); + rtems_status_code status_code = rtems_media_server_disk_attach( + part->name, rtems_bsd_mmcsd_attach_worker, part); + BSD_ASSERT(status_code == RTEMS_SUCCESSFUL); #endif /* __rtems__ */ + } + + bytes = mmcsd_pretty_size(media_size, unit); + if (type == EXT_CSD_PART_CONFIG_ACC_DEFAULT) { + speed = mmcbr_get_clock(mmcbr); + printf("%s%d: %ju%sB <%s>%s at %s %d.%01dMHz/%dbit/%d-block\n", + 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)); + } 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)" : "", + device_get_nameunit(dev)); + } else { + enh = false; + ext = NULL; + extattr = 0; + if (type >= EXT_CSD_PART_CONFIG_ACC_GP0 && + type <= EXT_CSD_PART_CONFIG_ACC_GP3) { + ext_csd = sc->ext_csd; + gp = type - EXT_CSD_PART_CONFIG_ACC_GP0; + if ((ext_csd[EXT_CSD_PART_SUPPORT] & + EXT_CSD_PART_SUPPORT_ENH_ATTR_EN) != 0 && + (ext_csd[EXT_CSD_PART_ATTR] & + (EXT_CSD_PART_ATTR_ENH_GP0 << gp)) != 0) + enh = true; + else if ((ext_csd[EXT_CSD_PART_SUPPORT] & + EXT_CSD_PART_SUPPORT_EXT_ATTR_EN) != 0) { + extattr = (ext_csd[EXT_CSD_EXT_PART_ATTR + + (gp / 2)] >> (4 * (gp % 2))) & 0xF; + switch (extattr) { + case EXT_CSD_EXT_PART_ATTR_DEFAULT: + break; + case EXT_CSD_EXT_PART_ATTR_SYSTEMCODE: + ext = "system code"; + break; + case EXT_CSD_EXT_PART_ATTR_NPERSISTENT: + ext = "non-persistent"; + break; + default: + ext = "reserved"; + break; + } + } + } + if (ext == NULL) + printf("%s%d: %ju%sB partion %d%s%s at %s\n", + part->name, cnt, bytes, unit, type, enh ? + " enhanced" : "", ro ? " (read-only)" : "", + device_get_nameunit(dev)); + else + printf("%s%d: %ju%sB partion %d extended 0x%x " + "(%s)%s at %s\n", part->name, cnt, bytes, unit, + type, extattr, ext, ro ? " (read-only)" : "", + device_get_nameunit(dev)); + } +} +#ifndef __rtems__ +static int +mmcsd_slicer(device_t dev, const char *provider, + struct flash_slice *slices, int *nslices) +{ + char name[MMCSD_PART_NAMELEN]; + struct mmcsd_softc *sc; + struct mmcsd_part *part; + + *nslices = 0; + if (slices == NULL) + return (ENOMEM); + + sc = device_get_softc(dev); + if (sc->enh_size == 0) + return (ENXIO); + + part = sc->part[EXT_CSD_PART_CONFIG_ACC_DEFAULT]; + snprintf(name, sizeof(name), "%s%d", part->disk->d_name, + part->disk->d_unit); + if (strcmp(name, provider) != 0) + return (ENXIO); + + *nslices = 1; + slices[0].base = sc->enh_base; + slices[0].size = sc->enh_size; + slices[0].label = MMCSD_LABEL_ENH; return (0); } +#endif /* __rtems__ */ static int mmcsd_detach(device_t dev) { +#ifndef __rtems__ struct mmcsd_softc *sc = device_get_softc(dev); + struct mmcsd_part *part; + int i; + + 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); + } + MMCSD_PART_UNLOCK(part); + } + } -#ifndef __rtems__ - MMCSD_LOCK(sc); - 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. */ - bioq_flush(&sc->bio_queue, NULL, ENXIO); - /* kill disk */ - disk_destroy(sc->disk); + if (sc->rpmb_dev != NULL) + destroy_dev(sc->rpmb_dev); + + for (i = 0; i < MMC_PART_MAX; i++) { + part = sc->part[i]; + if (part != NULL) { + if (part->disk != NULL) { + /* Flush the request queue. */ + bioq_flush(&part->bio_queue, NULL, ENXIO); + /* kill disk */ + disk_destroy(part->disk); + + MMCSD_PART_LOCK_DESTROY(part); + } + free(part, M_DEVBUF); + } + } #else /* __rtems__ */ BSD_PANIC("FIXME"); #endif /* __rtems__ */ - - MMCSD_LOCK_DESTROY(sc); - return (0); } @@ -493,18 +839,26 @@ mmcsd_suspend(device_t dev) { #ifndef __rtems__ 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); + struct mmcsd_part *part; + int i; + + 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); + } + MMCSD_PART_UNLOCK(part); + } + } #else /* __rtems__ */ BSD_PANIC("FIXME"); #endif /* __rtems__ */ @@ -516,16 +870,23 @@ mmcsd_resume(device_t dev) { #ifndef __rtems__ 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, "%s: mmc/sd card", - device_get_nameunit(dev)); - } else - MMCSD_UNLOCK(sc); + struct mmcsd_part *part; + int i; + + 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); + } + } #else /* __rtems__ */ BSD_PANIC("FIXME"); #endif /* __rtems__ */ @@ -534,14 +895,14 @@ mmcsd_resume(device_t dev) #ifndef __rtems__ static int -mmcsd_open(struct disk *dp) +mmcsd_open(struct disk *dp __unused) { return (0); } static int -mmcsd_close(struct disk *dp) +mmcsd_close(struct disk *dp __unused) { return (0); @@ -551,47 +912,339 @@ static void mmcsd_strategy(struct bio *bp) { struct mmcsd_softc *sc; - - sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1; - MMCSD_LOCK(sc); - if (sc->running > 0 || sc->suspend > 0) { - bioq_disksort(&sc->bio_queue, bp); - MMCSD_UNLOCK(sc); - wakeup(sc); + struct mmcsd_part *part; + + part = bp->bio_disk->d_drv1; + sc = part->sc; + MMCSD_PART_LOCK(part); + if (part->running > 0 || part->suspend > 0) { + bioq_disksort(&part->bio_queue, bp); + MMCSD_PART_UNLOCK(part); + wakeup(part); } else { - MMCSD_UNLOCK(sc); + MMCSD_PART_UNLOCK(part); biofinish(bp, NULL, ENXIO); } } +#endif /* __rtems__ */ + +static int +mmcsd_ioctl_rpmb(struct cdev *dev, u_long cmd, caddr_t data, + int fflag, struct thread *td __unused) +{ + + return (mmcsd_ioctl(dev->si_drv1, cmd, data, fflag)); +} + +#ifndef __rtems__ +static int +mmcsd_ioctl_disk(struct disk *disk, u_long cmd, void *data, int fflag, + struct thread *td __unused) +{ + + return (mmcsd_ioctl(disk->d_drv1, cmd, data, fflag)); +} +#endif /* __rtems__ */ + +static int +mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, int fflag) +{ + struct mmc_ioc_cmd *mic; + struct mmc_ioc_multi_cmd *mimc; + int i, err; + u_long cnt, size; + + if ((fflag & FREAD) == 0) + return (EBADF); + + err = 0; + switch (cmd) { + case MMC_IOC_CMD: + mic = data; + err = mmcsd_ioctl_cmd(part, data, fflag); + break; + case MMC_IOC_CMD_MULTI: + mimc = data; + if (mimc->num_of_cmds == 0) + break; + if (mimc->num_of_cmds > MMC_IOC_MAX_CMDS) + return (EINVAL); + cnt = mimc->num_of_cmds; + 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; + } + free(mic, M_TEMP); + break; + default: + return (ENOIOCTL); + } + return (err); +} + +static int +mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag) +{ + struct mmc_command cmd; + struct mmc_data data; + struct mmcsd_softc *sc; + device_t dev, mmcbr; + void *dp; + u_long len; + int err, retries; + uint32_t status; + uint16_t rca; + + if ((fflag & FWRITE) == 0 && mic->write_flag != 0) + return (EBADF); + + if (part->ro == TRUE && mic->write_flag != 0) + return (EROFS); + + err = 0; + dp = NULL; + len = mic->blksz * mic->blocks; + if (len > MMC_IOC_MAX_BYTES) + return (EOVERFLOW); + if (len != 0) { + dp = malloc(len, M_TEMP, M_WAITOK); + err = copyin((void *)(uintptr_t)mic->data_ptr, dp, len); + if (err != 0) + goto out; + } + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); + cmd.opcode = mic->opcode; + cmd.arg = mic->arg; + cmd.flags = mic->flags; + if (len != 0) { + data.len = len; + data.data = dp; + data.flags = mic->write_flag != 0 ? MMC_DATA_WRITE : + MMC_DATA_READ; + cmd.data = &data; + } + sc = part->sc; + rca = sc->rca; + if (mic->is_acmd == 0) { + /* Enforce/patch/restrict RCA-based commands */ + switch (cmd.opcode) { + case MMC_SET_RELATIVE_ADDR: + case MMC_SELECT_CARD: + err = EPERM; + goto out; + case MMC_STOP_TRANSMISSION: + if ((cmd.arg & 0x1) == 0) + break; + /* FALLTHROUGH */ + case MMC_SLEEP_AWAKE: + case MMC_SEND_CSD: + case MMC_SEND_CID: + case MMC_SEND_STATUS: + case MMC_GO_INACTIVE_STATE: + case MMC_FAST_IO: + case MMC_APP_CMD: + cmd.arg = (cmd.arg & 0x0000FFFF) | (rca << 16); + break; + default: + break; + } + } + dev = sc->dev; + mmcbr = sc->mmcbr; + MMCBUS_ACQUIRE_BUS(mmcbr, dev); + err = mmcsd_switch_part(mmcbr, dev, rca, part->type); + if (err != MMC_ERR_NONE) + goto release; + if (part->type == EXT_CSD_PART_CONFIG_ACC_RPMB) { + err = mmcsd_set_blockcount(sc, mic->blocks, + mic->write_flag & (1 << 31)); + if (err != MMC_ERR_NONE) + goto release; + } + if (mic->is_acmd != 0) + (void)mmc_wait_for_app_cmd(mmcbr, dev, rca, &cmd, 0); + else + (void)mmc_wait_for_cmd(mmcbr, dev, &cmd, 0); + if (part->type == EXT_CSD_PART_CONFIG_ACC_RPMB) { + /* + * If the request went to the RPMB partition, try to ensure + * that the command actually has completed ... + */ + retries = MMCSD_CMD_RETRIES; + do { + err = mmc_send_status(mmcbr, dev, rca, &status); + if (err != MMC_ERR_NONE) + break; + if (R1_STATUS(status) == 0 && + R1_CURRENT_STATE(status) != R1_STATE_PRG) + break; + DELAY(1000); + } while (retries-- > 0); + + /* ... and always switch back to the default partition. */ + err = mmcsd_switch_part(mmcbr, dev, rca, + EXT_CSD_PART_CONFIG_ACC_DEFAULT); + if (err != MMC_ERR_NONE) + goto release; + } + /* + * If EXT_CSD was changed, our copy is outdated now. Specifically, + * the upper bits of EXT_CSD_PART_CONFIG used in mmcsd_switch_part(), + * so retrieve EXT_CSD again. + */ + if (cmd.opcode == MMC_SWITCH_FUNC) { + err = mmc_send_ext_csd(mmcbr, dev, sc->ext_csd); + if (err != MMC_ERR_NONE) + goto release; + } + MMCBUS_RELEASE_BUS(mmcbr, dev); + if (cmd.error != MMC_ERR_NONE) { + switch (cmd.error) { + case MMC_ERR_TIMEOUT: + err = ETIMEDOUT; + break; + case MMC_ERR_BADCRC: + err = EILSEQ; + break; + case MMC_ERR_INVALID: + err = EINVAL; + break; + case MMC_ERR_NO_MEMORY: + err = ENOMEM; + break; + default: + err = EIO; + break; + } + goto out; + } + memcpy(mic->response, cmd.resp, 4 * sizeof(uint32_t)); + if (mic->write_flag == 0 && len != 0) { + err = copyout(dp, (void *)(uintptr_t)mic->data_ptr, len); + if (err != 0) + goto out; + } + goto out; + +release: + MMCBUS_RELEASE_BUS(mmcbr, dev); + err = EIO; + +out: + if (dp != NULL) + free(dp, M_TEMP); + return (err); +} + +#ifndef __rtems__ +static int +mmcsd_getattr(struct bio *bp) +{ + struct mmcsd_part *part; + device_t dev; + + if (strcmp(bp->bio_attribute, "MMC::device") == 0) { + if (bp->bio_length != sizeof(dev)) + return (EFAULT); + part = bp->bio_disk->d_drv1; + dev = part->sc->dev; + bcopy(&dev, bp->bio_data, sizeof(dev)); + bp->bio_completed = bp->bio_length; + return (0); + } + return (-1); +} +#endif /* __rtems__ */ + +static int +mmcsd_set_blockcount(struct mmcsd_softc *sc, u_int count, bool reliable) +{ + struct mmc_command cmd; + struct mmc_request req; + + memset(&req, 0, sizeof(req)); + memset(&cmd, 0, sizeof(cmd)); + cmd.mrq = &req; + req.cmd = &cmd; + cmd.opcode = MMC_SET_BLOCK_COUNT; + cmd.arg = count & 0x0000FFFF; + if (reliable) + cmd.arg |= 1 << 31; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + MMCBUS_WAIT_FOR_REQUEST(sc->mmcbr, sc->dev, &req); + return (cmd.error); +} + +static int +mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca, u_int part) +{ + struct mmcsd_softc *sc; + int err; + uint8_t value; + + sc = device_get_softc(dev); + + if (sc->part_curr == part) + return (MMC_ERR_NONE); + + if (sc->mode == mode_sd) + return (MMC_ERR_NONE); + + value = (sc->ext_csd[EXT_CSD_PART_CONFIG] & + ~EXT_CSD_PART_CONFIG_ACC_MASK) | 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) + return (err); + + sc->ext_csd[EXT_CSD_PART_CONFIG] = value; + sc->part_curr = part; + return (MMC_ERR_NONE); +} +#ifndef __rtems__ static const char * mmcsd_errmsg(int e) { + if (e < 0 || e > MMC_ERR_MAX) return "Bad error code"; return errmsg[e]; } static daddr_t -mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp) +mmcsd_rw(struct mmcsd_part *part, struct bio *bp) { daddr_t block, end; struct mmc_command cmd; struct mmc_command stop; struct mmc_request req; struct mmc_data data; - device_t dev = sc->dev; - int sz = sc->disk->d_sectorsize; - device_t mmcbr = device_get_parent(dev); + struct mmcsd_softc *sc; + device_t dev, mmcbr; + int numblocks, sz; + char *vaddr; + + sc = part->sc; + dev = sc->dev; + mmcbr = sc->mmcbr; block = bp->bio_pblkno; + sz = part->disk->d_sectorsize; end = bp->bio_pblkno + (bp->bio_bcount / sz); while (block < end) { - char *vaddr = bp->bio_data + - (block - bp->bio_pblkno) * sz; - int numblocks = min(end - block, mmc_get_max_data(dev)); + vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; + numblocks = min(end - block, mmc_get_max_data(dev)); memset(&req, 0, sizeof(req)); - memset(&cmd, 0, sizeof(cmd)); + memset(&cmd, 0, sizeof(cmd)); memset(&stop, 0, sizeof(stop)); memset(&data, 0, sizeof(data)); cmd.mrq = &req; @@ -629,10 +1282,11 @@ mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp) } MMCBUS_WAIT_FOR_REQUEST(mmcbr, dev, &req); if (req.cmd->error != MMC_ERR_NONE) { - if (ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS)) { + if (ppsratecheck(&sc->log_time, &sc->log_count, + LOG_PPS)) device_printf(dev, "Error indicated: %d %s\n", - req.cmd->error, mmcsd_errmsg(req.cmd->error)); - } + req.cmd->error, + mmcsd_errmsg(req.cmd->error)); break; } block += numblocks; @@ -641,33 +1295,37 @@ mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp) } static daddr_t -mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp) +mmcsd_delete(struct mmcsd_part *part, struct bio *bp) { daddr_t block, end, start, stop; struct mmc_command cmd; struct mmc_request req; - device_t dev = sc->dev; - int sz = sc->disk->d_sectorsize; - int erase_sector; - device_t mmcbr = device_get_parent(dev); + struct mmcsd_softc *sc; + device_t dev, mmcbr; + int erase_sector, sz; + + sc = part->sc; + dev = sc->dev; + mmcbr = sc->mmcbr; block = bp->bio_pblkno; + sz = part->disk->d_sectorsize; end = bp->bio_pblkno + (bp->bio_bcount / sz); /* Coalesce with part remaining from previous request. */ - if (block > sc->eblock && block <= sc->eend) - block = sc->eblock; - if (end >= sc->eblock && end < sc->eend) - end = sc->eend; + if (block > part->eblock && block <= part->eend) + block = part->eblock; + if (end >= part->eblock && end < part->eend) + end = part->eend; /* Safe round to the erase sector boundaries. */ erase_sector = mmc_get_erase_sector(dev); start = block + erase_sector - 1; /* Round up. */ start -= start % erase_sector; stop = end; /* Round down. */ - stop -= end % erase_sector; - /* We can't erase area smaller then sector, store it for later. */ + stop -= end % erase_sector; + /* We can't erase an area smaller than a sector, store it for later. */ if (start >= stop) { - sc->eblock = block; - sc->eend = end; + part->eblock = block; + part->eend = end; return (end); } @@ -720,40 +1378,54 @@ mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp) return (block); } /* Store one of remaining parts for the next call. */ - if (bp->bio_pblkno >= sc->eblock || block == start) { - sc->eblock = stop; /* Predict next forward. */ - sc->eend = end; + if (bp->bio_pblkno >= part->eblock || block == start) { + part->eblock = stop; /* Predict next forward. */ + part->eend = end; } else { - sc->eblock = block; /* Predict next backward. */ - sc->eend = start; + part->eblock = block; /* Predict next backward. */ + part->eend = start; } return (end); } static int -mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, - off_t offset, size_t length) +mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, + size_t length) { - struct disk *disk = arg; - struct mmcsd_softc *sc = (struct mmcsd_softc *)disk->d_drv1; - device_t dev = sc->dev; struct bio bp; daddr_t block, end; - device_t mmcbr = device_get_parent(dev); + struct disk *disk; + struct mmcsd_softc *sc; + struct mmcsd_part *part; + device_t dev, mmcbr; + int err; /* length zero is special and really means flush buffers to media */ if (!length) return (0); + disk = arg; + part = disk->d_drv1; + sc = part->sc; + dev = sc->dev; + mmcbr = sc->mmcbr; + g_reset_bio(&bp); bp.bio_disk = disk; bp.bio_pblkno = offset / disk->d_sectorsize; bp.bio_bcount = length; bp.bio_data = virtual; bp.bio_cmd = BIO_WRITE; - end = bp.bio_pblkno + bp.bio_bcount / sc->disk->d_sectorsize; + end = bp.bio_pblkno + bp.bio_bcount / disk->d_sectorsize; MMCBUS_ACQUIRE_BUS(mmcbr, dev); - block = mmcsd_rw(sc, &bp); + err = mmcsd_switch_part(mmcbr, dev, sc->rca, part->type); + if (err != MMC_ERR_NONE) { + if (ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS)) + device_printf(dev, "Partition switch error\n"); + MMCBUS_RELEASE_BUS(mmcbr, dev); + return (EIO); + } + block = mmcsd_rw(part, &bp); MMCBUS_RELEASE_BUS(mmcbr, dev); return ((end < block) ? EIO : 0); } @@ -761,24 +1433,30 @@ mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, static void mmcsd_task(void *arg) { - struct mmcsd_softc *sc = (struct mmcsd_softc*)arg; - struct bio *bp; - int sz; daddr_t block, end; - device_t dev = sc->dev; - device_t mmcbr = device_get_parent(sc->dev); + struct mmcsd_part *part; + struct mmcsd_softc *sc; + struct bio *bp; + device_t dev, mmcbr; + int err, sz; + + part = arg; + sc = part->sc; + dev = sc->dev; + mmcbr = sc->mmcbr; while (1) { - MMCSD_LOCK(sc); + MMCSD_PART_LOCK(part); do { - if (sc->running == 0) + if (part->running == 0) goto out; - bp = bioq_takefirst(&sc->bio_queue); + bp = bioq_takefirst(&part->bio_queue); if (bp == NULL) - msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); + msleep(part, &part->part_mtx, PRIBIO, + "jobqueue", 0); } while (bp == NULL); - MMCSD_UNLOCK(sc); - if (bp->bio_cmd != BIO_READ && mmc_get_read_only(dev)) { + MMCSD_PART_UNLOCK(part); + if (bp->bio_cmd != BIO_READ && part->ro) { bp->bio_error = EROFS; bp->bio_resid = bp->bio_bcount; bp->bio_flags |= BIO_ERROR; @@ -786,30 +1464,40 @@ mmcsd_task(void *arg) continue; } MMCBUS_ACQUIRE_BUS(mmcbr, dev); - sz = sc->disk->d_sectorsize; + sz = part->disk->d_sectorsize; block = bp->bio_pblkno; end = bp->bio_pblkno + (bp->bio_bcount / sz); + err = mmcsd_switch_part(mmcbr, dev, sc->rca, part->type); + if (err != MMC_ERR_NONE) { + if (ppsratecheck(&sc->log_time, &sc->log_count, + LOG_PPS)) + device_printf(dev, "Partition switch error\n"); + goto release; + } if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { /* Access to the remaining erase block obsoletes it. */ - if (block < sc->eend && end > sc->eblock) - sc->eblock = sc->eend = 0; - block = mmcsd_rw(sc, bp); + if (block < part->eend && end > part->eblock) + part->eblock = part->eend = 0; + block = mmcsd_rw(part, bp); } else if (bp->bio_cmd == BIO_DELETE) { - block = mmcsd_delete(sc, bp); + block = mmcsd_delete(part, bp); } +release: MMCBUS_RELEASE_BUS(mmcbr, dev); if (block < end) { bp->bio_error = EIO; bp->bio_resid = (end - block) * sz; bp->bio_flags |= BIO_ERROR; + } else { + bp->bio_resid = 0; } biodone(bp); } out: /* tell parent we're done */ - sc->running = -1; - MMCSD_UNLOCK(sc); - wakeup(sc); + part->running = -1; + MMCSD_PART_UNLOCK(part); + wakeup(part); kproc_exit(0); } @@ -842,4 +1530,24 @@ static driver_t mmcsd_driver = { }; static devclass_t mmcsd_devclass; -DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, NULL, NULL); +static int +mmcsd_handler(module_t mod __unused, int what, void *arg __unused) +{ + +#ifndef __rtems__ + switch (what) { + case MOD_LOAD: + flash_register_slicer(mmcsd_slicer, FLASH_SLICES_TYPE_MMC, + TRUE); + return (0); + case MOD_UNLOAD: + flash_register_slicer(NULL, FLASH_SLICES_TYPE_MMC, TRUE); + return (0); + } +#endif /* __rtems__ */ + return (0); +} + +DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, mmcsd_handler, NULL); +MODULE_DEPEND(mmcsd, g_flashmap, 0, 0, 0); +MMC_DEPEND(mmcsd); diff --git a/freebsd/sys/dev/mmc/mmcvar.h b/freebsd/sys/dev/mmc/mmcvar.h index c7a4af99..9f62b112 100644 --- a/freebsd/sys/dev/mmc/mmcvar.h +++ b/freebsd/sys/dev/mmc/mmcvar.h @@ -49,15 +49,14 @@ * or the SD Card Association to disclose or distribute any technical * information, know-how or other confidential information to any third party. * - * "$FreeBSD$" + * $FreeBSD$ */ #ifndef DEV_MMC_MMCVAR_H #define DEV_MMC_MMCVAR_H -#include <dev/mmc/bridge.h> - enum mmc_device_ivars { + MMC_IVAR_SPEC_VERS, MMC_IVAR_DSR_IMP, MMC_IVAR_MEDIA_SIZE, MMC_IVAR_RCA, @@ -79,6 +78,7 @@ enum mmc_device_ivars { #define MMC_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(mmc, var, MMC, ivar, type) +MMC_ACCESSOR(spec_vers, SPEC_VERS, uint8_t) MMC_ACCESSOR(dsr_imp, DSR_IMP, int) MMC_ACCESSOR(media_size, MEDIA_SIZE, long) MMC_ACCESSOR(rca, RCA, int) |