summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/Kconfig12
-rw-r--r--drivers/mmc/core/mmc.c15
-rw-r--r--drivers/mmc/core/sd.c11
-rw-r--r--drivers/mmc/host/sdhci.c28
-rw-r--r--drivers/mmc/host/sdhci.h7
-rw-r--r--include/linux/mmc/host.h3
6 files changed, 75 insertions, 1 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index f2eeb38efa65..1f8494e86d34 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -12,6 +12,18 @@ menuconfig MMC
If you want MMC/SD/SDIO support, you should say Y here and
also to your specific host controller driver.
+config EMBEDDED_MMC_START_OFFSET
+ bool "MMC start sector offset"
+ depends on MMC != n
+ help
+ This enables a per-controller fixed offset in sectors / bytes to
+ the location of the master boot record in the device, and reduces
+ the capacity of the device by a corresponding amount. This is
+ primarily used by embedded systems with embedded MMC storage where
+ the initial sectors are used by boot firmware. If you do not know
+ if this applies to your system, say N here.
+
+
config MMC_DEBUG
bool "MMC debugging"
depends on MMC != n
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3e35075b7a13..c49b21decbf1 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -141,6 +141,10 @@ static int mmc_decode_csd(struct mmc_card *card)
e = UNSTUFF_BITS(resp, 47, 3);
m = UNSTUFF_BITS(resp, 62, 12);
csd->capacity = (1 + m) << (e + 2);
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ BUG_ON(card->host->ops->get_host_offset(card->host) >= csd->capacity);
+ csd->capacity -= card->host->ops->get_host_offset(card->host);
+#endif
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
@@ -222,8 +226,17 @@ static int mmc_read_ext_csd(struct mmc_card *card)
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
- if (card->ext_csd.sectors)
+ if (card->ext_csd.sectors) {
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ unsigned offs;
+ offs = card->host->ops->get_host_offset(card->host);
+ offs >>= 9;
+ BUG_ON(offs >= card->ext_csd.sectors);
+ card->ext_csd.sectors -= offs;
+#endif
mmc_card_set_blockaddr(card);
+ }
+
}
switch (ext_csd[EXT_CSD_CARD_TYPE]) {
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 9486936f7ed6..b5ee533f0e29 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -110,6 +110,11 @@ static int mmc_decode_csd(struct mmc_card *card)
e = UNSTUFF_BITS(resp, 47, 3);
m = UNSTUFF_BITS(resp, 62, 12);
csd->capacity = (1 + m) << (e + 2);
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ BUG_ON(card->host->ops->get_host_offset(card->host) >=
+ csd->capacity);
+ csd->capacity -= card->host->ops->get_host_offset(card->host);
+#endif
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
@@ -138,6 +143,12 @@ static int mmc_decode_csd(struct mmc_card *card)
m = UNSTUFF_BITS(resp, 48, 22);
csd->capacity = (1 + m) << 10;
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ BUG_ON((card->host->ops->get_host_offset(card->host) >> 9) >=
+ csd->capacity);
+ csd->capacity -=
+ (card->host->ops->get_host_offset(card->host) >> 9);
+#endif
csd->read_blkbits = 9;
csd->read_partial = 0;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index accb592764ed..09a5c6d8e168 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -821,7 +821,17 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_prepare_data(host, cmd->data);
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ if (cmd->data) {
+ /* It is assumed that the device is block addressed. */
+ writel(cmd->arg + (host->start_offset >> 9),
+ host->ioaddr + SDHCI_ARGUMENT);
+ } else {
+ writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+ }
+#else
writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+#endif
sdhci_set_transfer_mode(host, cmd->data);
@@ -1120,11 +1130,22 @@ out:
spin_unlock_irqrestore(&host->lock, flags);
}
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+static unsigned int sdhci_get_host_offset(struct mmc_host *mmc) {
+ struct sdhci_host *host;
+ host = mmc_priv(mmc);
+ return host->start_offset;
+}
+#endif
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro,
.enable_sdio_irq = sdhci_enable_sdio_irq,
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ .get_host_offset = sdhci_get_host_offset,
+#endif
};
/*****************************************************************************\
@@ -1612,6 +1633,13 @@ int sdhci_add_host(struct sdhci_host *host)
mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
}
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ if (host->ops->get_startoffset)
+ host->start_offset = host->ops->get_startoffset(host);
+ else
+ host->start_offset = 0;
+#endif
+
host->max_clk =
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
if (host->max_clk == 0) {
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 43c37c68d07a..7dea10fb06a8 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -262,12 +262,19 @@ struct sdhci_host {
struct timer_list timer; /* Timer for timeouts */
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ unsigned int start_offset; /* Zero-offset for MBR */
+#endif
+
unsigned long private[0] ____cacheline_aligned;
};
struct sdhci_ops {
int (*enable_dma)(struct sdhci_host *host);
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ unsigned int (*get_startoffset)(struct sdhci_host *host);
+#endif
};
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 7d41efcd7c33..62bfc01ed2d7 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -77,6 +77,9 @@ struct mmc_host_ops {
int (*get_cd)(struct mmc_host *host);
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
+#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
+ unsigned int (*get_host_offset)(struct mmc_host *host);
+#endif
};
struct mmc_card;