aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius Strobl <marius@FreeBSD.org>2017-03-16 22:23:04 +0000
committerMarius Strobl <marius@FreeBSD.org>2017-03-16 22:23:04 +0000
commit72dec0792a09bb5f03d341642657bd6115d99c9e (patch)
tree4bbd8d23bc107fe38b0cdaefbb7caf715dff4100
parent2d6acb22fecdcf352070106f8d49bbd6e508a992 (diff)
Notes
-rw-r--r--UPDATING7
-rw-r--r--etc/mtree/BSD.include.dist2
-rw-r--r--include/Makefile2
-rw-r--r--sys/arm/ti/ti_sdhci.c5
-rw-r--r--sys/conf/files3
-rw-r--r--sys/dev/mmc/bridge.h6
-rw-r--r--sys/dev/mmc/mmc.c327
-rw-r--r--sys/dev/mmc/mmc_ioctl.h64
-rw-r--r--sys/dev/mmc/mmc_private.h69
-rw-r--r--sys/dev/mmc/mmc_subr.c252
-rw-r--r--sys/dev/mmc/mmc_subr.h72
-rw-r--r--sys/dev/mmc/mmcbrvar.h11
-rw-r--r--sys/dev/mmc/mmcreg.h70
-rw-r--r--sys/dev/mmc/mmcsd.c1015
-rw-r--r--sys/dev/mmc/mmcvar.h4
-rw-r--r--sys/dev/sdhci/sdhci.c16
-rw-r--r--sys/dev/sdhci/sdhci.h4
-rw-r--r--sys/dev/sdhci/sdhci_acpi.c13
-rw-r--r--sys/dev/sdhci/sdhci_pci.c18
-rw-r--r--sys/modules/mmc/Makefile2
-rw-r--r--sys/modules/mmcsd/Makefile2
-rw-r--r--sys/sys/param.h2
22 files changed, 1574 insertions, 392 deletions
diff --git a/UPDATING b/UPDATING
index bf613334daa7..d495ba4fc817 100644
--- a/UPDATING
+++ b/UPDATING
@@ -51,6 +51,13 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 12.x IS SLOW:
****************************** SPECIAL WARNING: ******************************
+20170316:
+ The mmcsd.ko module now additionally depends on geom_flashmap.ko.
+ Also, mmc.ko and mmcsd.ko need to be a matching pair built from the
+ same source (previously, the dependency of mmcsd.ko on mmc.ko was
+ missing, but mmcsd.ko now will refuse to load if it is incompatible
+ with mmc.ko).
+
20170315:
The syntax of ipfw(8) named states was changed to avoid ambiguity.
If you have used named states in the firewall rules, you need to modify
diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
index 8a9bbeabdb99..15e9807ea73e 100644
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -130,6 +130,8 @@
..
mfi
..
+ mmc
+ ..
mpt
mpilib
..
diff --git a/include/Makefile b/include/Makefile
index 3f2816a94651..e8ad0030a7c7 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -45,7 +45,7 @@ LDIRS= bsm cam geom net net80211 netgraph netinet netinet6 \
LSUBDIRS= cam/ata cam/nvme cam/scsi \
dev/acpica dev/agp dev/an dev/bktr dev/ciss dev/filemon dev/firewire \
dev/hwpmc dev/hyperv \
- dev/ic dev/iicbus dev/io dev/lmc dev/mfi dev/nvme \
+ dev/ic dev/iicbus dev/io dev/lmc dev/mfi dev/mmc dev/nvme \
dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/smbus \
dev/speaker dev/utopia dev/vkbd dev/wi \
fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/nullfs \
diff --git a/sys/arm/ti/ti_sdhci.c b/sys/arm/ti/ti_sdhci.c
index 7bd7c5ccbdd5..6b188ddd0728 100644
--- a/sys/arm/ti/ti_sdhci.c
+++ b/sys/arm/ti/ti_sdhci.c
@@ -606,6 +606,11 @@ ti_sdhci_attach(device_t dev)
* before waiting to see them de-asserted.
*/
sc->slot.quirks |= SDHCI_QUIRK_WAITFOR_RESET_ASSERTED;
+
+ /*
+ * The controller waits for busy responses.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_WAIT_WHILE_BUSY;
/*
* DMA is not really broken, I just haven't implemented it yet.
diff --git a/sys/conf/files b/sys/conf/files
index dbf0fbde290a..7032ecbddd7e 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2218,6 +2218,7 @@ dev/mlx/mlx.c optional mlx
dev/mlx/mlx_disk.c optional mlx
dev/mlx/mlx_pci.c optional mlx pci
dev/mly/mly.c optional mly
+dev/mmc/mmc_subr.c optional mmc | mmcsd
dev/mmc/mmc.c optional mmc
dev/mmc/mmcbr_if.m standard
dev/mmc/mmcbus_if.m standard
@@ -3429,7 +3430,7 @@ geom/geom_disk.c standard
geom/geom_dump.c standard
geom/geom_event.c standard
geom/geom_fox.c optional geom_fox
-geom/geom_flashmap.c optional fdt cfi | fdt nand | fdt mx25l
+geom/geom_flashmap.c optional fdt cfi | fdt nand | fdt mx25l | mmcsd
geom/geom_io.c standard
geom/geom_kern.c standard
geom/geom_map.c optional geom_map
diff --git a/sys/dev/mmc/bridge.h b/sys/dev/mmc/bridge.h
index 1095626227d3..ad1a17bcbf86 100644
--- a/sys/dev/mmc/bridge.h
+++ b/sys/dev/mmc/bridge.h
@@ -132,6 +132,8 @@ struct mmc_host {
#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 */
enum mmc_card_mode mode;
struct mmc_ios ios; /* Current state of the host */
};
@@ -139,10 +141,12 @@ struct mmc_host {
extern driver_t mmc_driver;
extern devclass_t mmc_devclass;
-#define MMC_VERSION 1
+#define MMC_VERSION 2
#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/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c
index da7431b07ca3..a3a68a01e635 100644
--- a/sys/dev/mmc/mmc.c
+++ b/sys/dev/mmc/mmc.c
@@ -65,25 +65,16 @@ __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 "mmcbr_if.h"
#include "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. */
-
/*
* Per-card data
*/
@@ -91,7 +82,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;
@@ -107,6 +98,7 @@ struct mmc_ivars {
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 */
};
@@ -156,7 +148,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);
@@ -182,25 +175,17 @@ 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_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,
+ int 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 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);
@@ -299,19 +284,19 @@ 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);
sc->last_rca = rca;
/* 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",
(ivar->bus_width == bus_width_4) ? 4 :
(ivar->bus_width == bus_width_8) ? 8 : 1);
}
- mmc_set_card_bus_width(sc, rca, ivar->bus_width);
+ mmc_set_card_bus_width(sc, ivar);
mmcbr_set_bus_width(busdev, ivar->bus_width);
mmcbr_update_ios(busdev);
}
@@ -417,74 +402,6 @@ mmc_wait_for_request(device_t brdev, device_t reqdev __unused,
}
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 +413,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 +441,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 +462,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 +490,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 +516,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);
}
@@ -649,24 +567,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 +590,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 +606,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,9 +623,10 @@ 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;
@@ -737,18 +639,19 @@ mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width)
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_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, int timing)
{
u_char switch_res[64];
- int err;
uint8_t value;
+ int err;
switch (timing) {
case bus_timing_normal:
@@ -760,12 +663,26 @@ mmc_set_timing(struct mmc_softc *sc, int timing)
default:
return (MMC_ERR_INVALID);
}
- if (mmcbr_get_mode(sc->dev) == mode_sd)
+ if (mmcbr_get_mode(sc->dev) == mode_sd) {
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 {
+ 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);
}
@@ -808,7 +725,7 @@ mmc_test_bus_width(struct mmc_softc *sc)
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));
@@ -820,7 +737,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);
@@ -845,7 +762,7 @@ mmc_test_bus_width(struct mmc_softc *sc)
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));
@@ -857,7 +774,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);
@@ -899,7 +816,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;
@@ -913,7 +830,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
@@ -1125,7 +1046,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);
}
@@ -1141,7 +1062,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);
}
@@ -1166,37 +1087,13 @@ 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)
-{
- struct mmc_command cmd;
- struct mmc_data data;
- int err;
-
- 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)
{
struct mmc_command cmd;
@@ -1216,7 +1113,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);
@@ -1233,7 +1130,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);
}
@@ -1248,28 +1145,12 @@ 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)
-{
- struct mmc_command cmd;
- int err;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = MMC_SEND_STATUS;
- cmd.arg = rca << 16;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
- *status = cmd.resp[0];
- return (err);
-}
-
-static int
mmc_set_blocklen(struct mmc_softc *sc, uint32_t len)
{
struct mmc_command cmd;
@@ -1280,13 +1161,14 @@ mmc_set_blocklen(struct mmc_softc *sc, uint32_t len)
cmd.arg = len;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
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);
}
static void
mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard)
{
+
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);
@@ -1374,7 +1256,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);
@@ -1386,7 +1269,7 @@ mmc_discover_cards(struct mmc_softc *sc)
break;
}
- /* Get card SCR. Card must be selected to fetch it. */
+ /* 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);
mmc_app_decode_scr(ivar->raw_scr, &ivar->scr);
@@ -1396,7 +1279,7 @@ mmc_discover_cards(struct mmc_softc *sc)
mmc_sd_switch(sc, SD_SWITCH_MODE_CHECK,
SD_SWITCH_GROUP1, SD_SWITCH_NOCHANGE,
switch_res);
- if (switch_res[13] & 2) {
+ if (switch_res[13] & (1 << SD_SWITCH_HS_MODE)) {
ivar->timing = bus_timing_hs;
ivar->hs_tran_speed = SD_MAX_HS;
}
@@ -1453,7 +1336,6 @@ 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);
/* Get card CSD. */
@@ -1471,7 +1353,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);
@@ -1485,9 +1367,15 @@ mmc_discover_cards(struct mmc_softc *sc)
mmc_select_card(sc, ivar->rca);
- /* 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) +
@@ -1507,14 +1395,31 @@ mmc_discover_cards(struct mmc_softc *sc)
ivar->hs_tran_speed = MMC_TYPE_26_MAX_HS;
else
ivar->hs_tran_speed = ivar->tran_speed;
+ /*
+ * 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;
@@ -1533,6 +1438,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)
@@ -1672,8 +1579,6 @@ 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
@@ -1700,27 +1605,26 @@ mmc_calculate_clock(struct mmc_softc *sc)
if (ivar->hs_tran_speed < max_hs_dtr)
max_hs_dtr = ivar->hs_tran_speed;
}
+ 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)" : "");
+ }
for (i = 0; i < nkid; i++) {
ivar = device_get_ivars(kids[i]);
if (ivar->timing == bus_timing_normal)
continue;
mmc_select_card(sc, ivar->rca);
- mmc_set_timing(sc, max_timing);
+ mmc_set_timing(sc, ivar, max_timing);
}
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
@@ -1731,6 +1635,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
@@ -1741,6 +1647,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;
@@ -1840,4 +1749,4 @@ driver_t mmc_driver = {
};
devclass_t mmc_devclass;
-MODULE_VERSION(mmc, 1);
+MODULE_VERSION(mmc, MMC_VERSION);
diff --git a/sys/dev/mmc/mmc_ioctl.h b/sys/dev/mmc/mmc_ioctl.h
new file mode 100644
index 000000000000..97cff06838ce
--- /dev/null
+++ b/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/sys/dev/mmc/mmc_private.h b/sys/dev/mmc/mmc_private.h
new file mode 100644
index 000000000000..bbca0c6019f2
--- /dev/null
+++ b/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/sys/dev/mmc/mmc_subr.c b/sys/dev/mmc/mmc_subr.c
new file mode 100644
index 000000000000..7f0317a5cf51
--- /dev/null
+++ b/sys/dev/mmc/mmc_subr.c
@@ -0,0 +1,252 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <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 "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/sys/dev/mmc/mmc_subr.h b/sys/dev/mmc/mmc_subr.h
new file mode 100644
index 000000000000..6e300d2fa268
--- /dev/null
+++ b/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/sys/dev/mmc/mmcbrvar.h b/sys/dev/mmc/mmcbrvar.h
index c81f3f1f35ea..f7066c7d2b14 100644
--- a/sys/dev/mmc/mmcbrvar.h
+++ b/sys/dev/mmc/mmcbrvar.h
@@ -53,9 +53,8 @@
*/
#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 "mmcbr_if.h"
@@ -74,13 +73,14 @@ enum mmcbr_device_ivars {
MMCBR_IVAR_VDD,
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)
@@ -97,6 +97,7 @@ MMCBR_ACCESSOR(vdd, VDD, 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)
diff --git a/sys/dev/mmc/mmcreg.h b/sys/dev/mmc/mmcreg.h
index 66034896aa8b..202652f44429 100644
--- a/sys/dev/mmc/mmcreg.h
+++ b/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
@@ -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
@@ -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,18 +292,73 @@ struct mmc_request {
/*
* EXT_CSD fields
*/
+#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_HS_TIMING 185 /* R/W */
#define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_REV 192 /* RO */
+#define EXT_CSD_PART_SWITCH_TO 199 /* 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_GEN_CMD6_TIME 248 /* RO */
/*
* EXT_CSD field definitions
*/
+#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
@@ -438,6 +494,16 @@ 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
diff --git a/sys/dev/mmc/mmcsd.c b/sys/dev/mmc/mmcsd.c
index 1204044f69a3..4b2e29f11814 100644
--- a/sys/dev/mmc/mmcsd.c
+++ b/sys/dev/mmc/mmcsd.c
@@ -1,6 +1,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
@@ -58,16 +59,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 <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>
@@ -79,17 +87,46 @@ __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;
struct disk *disk;
struct proc *p;
struct bio_queue_head bio_queue;
daddr_t eblock, eend; /* Range remaining after the last erase. */
+ 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;
+ struct cdev *rpmb_dev;
};
static const char *errmsg[] =
@@ -114,22 +151,42 @@ static int mmcsd_probe(device_t dev);
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);
+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);
+/* 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);
-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);
+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);
+static daddr_t mmcsd_rw(struct mmcsd_part *part, struct bio *bp);
+static int mmcsd_set_blockcount(struct mmcsd_softc *sc, u_int count, bool rel);
+static int mmcsd_slicer(device_t dev, const char *provider,
+ struct flash_slice *slices, int *nslices);
+static int mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca,
+ u_int part);
-#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);
+#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)
@@ -143,74 +200,353 @@ mmcsd_probe(device_t dev)
static int
mmcsd_attach(device_t dev)
{
+ device_t mmcbr;
struct mmcsd_softc *sc;
- struct disk *d;
- 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);
- 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;
- 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;
+ /* 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;
/*
- * 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;
- 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");
}
+
/*
- * 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.
+ * Add default partition. This may be the only one or the user
+ * data area in case partitions are supported.
*/
- 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);
- disk_create(d, DISK_VERSION);
- bioq_init(&sc->bio_queue);
+ 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;
+
+ /*
+ * 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.
+ */
+ 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;
+ struct disk *d;
+ 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));
- 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));
+ /* 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);
+
+ 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);
+ }
+
+ 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));
+ }
+}
+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);
}
@@ -218,26 +554,44 @@ static int
mmcsd_detach(device_t dev)
{
struct mmcsd_softc *sc = device_get_softc(dev);
+ struct mmcsd_part *part;
+ int i;
- 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);
+ 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);
+ }
}
- 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);
- MMCSD_LOCK_DESTROY(sc);
+ 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);
+ }
+ }
return (0);
}
@@ -245,18 +599,26 @@ static int
mmcsd_suspend(device_t dev)
{
struct mmcsd_softc *sc = device_get_softc(dev);
+ struct mmcsd_part *part;
+ int i;
- 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);
+ 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);
+ }
}
- MMCSD_UNLOCK(sc);
return (0);
}
@@ -264,16 +626,23 @@ static int
mmcsd_resume(device_t dev)
{
struct mmcsd_softc *sc = device_get_softc(dev);
+ struct mmcsd_part *part;
+ int i;
- 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);
+ 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);
+ }
+ }
return (0);
}
@@ -295,19 +664,299 @@ static void
mmcsd_strategy(struct bio *bp)
{
struct mmcsd_softc *sc;
+ struct mmcsd_part *part;
- 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);
+ 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);
}
}
+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));
+}
+
+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));
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
static const char *
mmcsd_errmsg(int e)
{
@@ -318,20 +967,24 @@ mmcsd_errmsg(int 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;
+ struct mmcsd_softc *sc;
+ device_t dev, mmcbr;
int numblocks, sz;
- device_t mmcbr = device_get_parent(dev);
char *vaddr;
+ sc = part->sc;
+ dev = sc->dev;
+ mmcbr = sc->mmcbr;
+
block = bp->bio_pblkno;
- sz = sc->disk->d_sectorsize;
+ sz = part->disk->d_sectorsize;
end = bp->bio_pblkno + (bp->bio_bcount / sz);
while (block < end) {
vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz;
@@ -388,33 +1041,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. */
+ /* 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);
}
@@ -467,12 +1124,12 @@ 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);
}
@@ -481,26 +1138,40 @@ static int
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);
}
@@ -508,24 +1179,30 @@ mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset,
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;
@@ -533,17 +1210,25 @@ 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;
@@ -556,9 +1241,9 @@ mmcsd_task(void *arg)
}
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);
}
@@ -590,4 +1275,22 @@ 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)
+{
+
+ 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);
+ }
+ 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/sys/dev/mmc/mmcvar.h b/sys/dev/mmc/mmcvar.h
index 7e7808d68d66..9f62b1126fa3 100644
--- a/sys/dev/mmc/mmcvar.h
+++ b/sys/dev/mmc/mmcvar.h
@@ -55,9 +55,8 @@
#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)
diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c
index 7ea25d73553b..a94bfabfaef9 100644
--- a/sys/dev/sdhci/sdhci.c
+++ b/sys/dev/sdhci/sdhci.c
@@ -686,6 +686,10 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
slot->host.caps |= MMC_CAP_8_BIT_DATA;
if (caps & SDHCI_CAN_DO_HISPD)
slot->host.caps |= MMC_CAP_HSPEED;
+ if (slot->quirks & SDHCI_QUIRK_BOOT_NOACC)
+ slot->host.caps |= MMC_CAP_BOOT_NOACC;
+ if (slot->quirks & SDHCI_QUIRK_WAIT_WHILE_BUSY)
+ slot->host.caps |= MMC_CAP_WAIT_WHILE_BUSY;
/* Decide if we have usable DMA. */
if (caps & SDHCI_CAN_DO_DMA)
slot->opt |= SDHCI_HAVE_DMA;
@@ -1013,9 +1017,11 @@ sdhci_finish_command(struct sdhci_slot *slot)
uint8_t extra;
slot->cmd_done = 1;
- /* Interrupt aggregation: Restore command interrupt.
+ /*
+ * Interrupt aggregation: Restore command interrupt.
* Main restore point for the case when command interrupt
- * happened first. */
+ * happened first.
+ */
WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask |= SDHCI_INT_RESPONSE);
/* In case of error - reset host and return. */
if (slot->curcmd->error) {
@@ -1523,6 +1529,12 @@ sdhci_generic_read_ivar(device_t bus, device_t child, int which,
case MMCBR_IVAR_MAX_DATA:
*result = 65535;
break;
+ case MMCBR_IVAR_MAX_BUSY_TIMEOUT:
+ /*
+ * Currently, sdhci_start_data() hardcodes 1 s for all CMDs.
+ */
+ *result = 1000000;
+ break;
}
return (0);
}
diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h
index e88d8217dbd7..28fd3c020da2 100644
--- a/sys/dev/sdhci/sdhci.h
+++ b/sys/dev/sdhci/sdhci.h
@@ -73,6 +73,10 @@
#define SDHCI_QUIRK_INTEL_POWER_UP_RESET (1 << 19)
/* Data timeout is invalid, use 1 MHz clock instead. */
#define SDHCI_QUIRK_DATA_TIMEOUT_1MHZ (1 << 20)
+/* Controller doesn't allow access boot partitions. */
+#define SDHCI_QUIRK_BOOT_NOACC (1 << 21)
+/* Controller waits for busy responses. */
+#define SDHCI_QUIRK_WAIT_WHILE_BUSY (1 << 22)
/*
* Controller registers
diff --git a/sys/dev/sdhci/sdhci_acpi.c b/sys/dev/sdhci/sdhci_acpi.c
index 517be666af99..8ee635667922 100644
--- a/sys/dev/sdhci/sdhci_acpi.c
+++ b/sys/dev/sdhci/sdhci_acpi.c
@@ -57,13 +57,14 @@ static const struct sdhci_acpi_device {
const char *desc;
u_int quirks;
} sdhci_acpi_devices[] = {
- { "80860F14", 1, "Intel Bay Trail SD Host Controller",
+ { "80860F14", 1, "Intel Bay Trail eMMC 4.5 Controller",
SDHCI_QUIRK_ALL_SLOTS_NON_REMOVABLE |
- SDHCI_QUIRK_INTEL_POWER_UP_RESET },
- { "80860F14", 3, "Intel Bay Trail SD Host Controller",
- SDHCI_QUIRK_INTEL_POWER_UP_RESET },
- { "80860F16", 0, "Intel Bay Trail SD Host Controller",
- 0 },
+ SDHCI_QUIRK_INTEL_POWER_UP_RESET |
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
+ { "80860F14", 3, "Intel Bay Trail SDXC Controller",
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
+ { "80860F16", 0, "Intel Bay Trail SDXC Controller",
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
{ NULL, 0, NULL, 0}
};
diff --git a/sys/dev/sdhci/sdhci_pci.c b/sys/dev/sdhci/sdhci_pci.c
index de916023bb08..3288a18826f8 100644
--- a/sys/dev/sdhci/sdhci_pci.c
+++ b/sys/dev/sdhci/sdhci_pci.c
@@ -105,17 +105,27 @@ static const struct sdhci_device {
SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC },
{ 0x0f148086, 0xffff, "Intel Bay Trail eMMC 4.5 Controller",
SDHCI_QUIRK_ALL_SLOTS_NON_REMOVABLE |
- SDHCI_QUIRK_INTEL_POWER_UP_RESET },
+ SDHCI_QUIRK_INTEL_POWER_UP_RESET |
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
+ { 0x0f158086, 0xffff, "Intel Bay Trail SDXC Controller",
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
{ 0x0f508086, 0xffff, "Intel Bay Trail eMMC 4.5 Controller",
SDHCI_QUIRK_ALL_SLOTS_NON_REMOVABLE |
- SDHCI_QUIRK_INTEL_POWER_UP_RESET },
+ SDHCI_QUIRK_INTEL_POWER_UP_RESET |
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
{ 0x22948086, 0xffff, "Intel Braswell eMMC 4.5.1 Controller",
SDHCI_QUIRK_ALL_SLOTS_NON_REMOVABLE |
SDHCI_QUIRK_DATA_TIMEOUT_1MHZ |
- SDHCI_QUIRK_INTEL_POWER_UP_RESET },
+ SDHCI_QUIRK_INTEL_POWER_UP_RESET |
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
+ { 0x22968086, 0xffff, "Intel Braswell SDXC Controller",
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
+ { 0x5aca8086, 0xffff, "Intel Apollo Lake SDXC Controller",
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
{ 0x5acc8086, 0xffff, "Intel Apollo Lake eMMC 5.0 Controller",
SDHCI_QUIRK_ALL_SLOTS_NON_REMOVABLE |
- SDHCI_QUIRK_INTEL_POWER_UP_RESET },
+ SDHCI_QUIRK_INTEL_POWER_UP_RESET |
+ SDHCI_QUIRK_WAIT_WHILE_BUSY },
{ 0, 0xffff, NULL,
0 }
};
diff --git a/sys/modules/mmc/Makefile b/sys/modules/mmc/Makefile
index 70ae22d778fe..3fee6e6393b7 100644
--- a/sys/modules/mmc/Makefile
+++ b/sys/modules/mmc/Makefile
@@ -3,6 +3,6 @@
.PATH: ${SRCTOP}/sys/dev/mmc
KMOD= mmc
-SRCS= mmc.c mmcbr_if.h mmcbus_if.h device_if.h bus_if.h
+SRCS= bus_if.h device_if.h mmc_subr.c mmc.c mmcbr_if.h mmcbus_if.h
.include <bsd.kmod.mk>
diff --git a/sys/modules/mmcsd/Makefile b/sys/modules/mmcsd/Makefile
index 4294c593b755..4573fc37dc22 100644
--- a/sys/modules/mmcsd/Makefile
+++ b/sys/modules/mmcsd/Makefile
@@ -3,6 +3,6 @@
.PATH: ${SRCTOP}/sys/dev/mmc
KMOD= mmcsd
-SRCS= bus_if.h device_if.h mmcbr_if.h mmcbus_if.h mmcsd.c
+SRCS= bus_if.h device_if.h mmc_subr.c mmcbr_if.h mmcbus_if.h mmcsd.c
.include <bsd.kmod.mk>
diff --git a/sys/sys/param.h b/sys/sys/param.h
index d162ad96706a..3298e75b8bf5 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -58,7 +58,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
-#define __FreeBSD_version 1200024 /* Master, propagated to newvers */
+#define __FreeBSD_version 1200025 /* Master, propagated to newvers */
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,