[PATCH 1/3] add a quirk for sdhci host which cannot erase too many sectors each time

From: Chuanxiao Dong
Date: Thu Dec 02 2010 - 02:19:54 EST


The original max_discard_sectors value which used to limit the number of
discard sectors is UINT_MAX, which means kernel block layer can pass down
any sectors to host for erasing. But for some sdhci host controller like
MFLD platform controller cannot handle too many sectors at one time. Maybe
during the erasing, host controller will generate a timeout interrupt since
erasing too many sectors may need longer time than the maximum time host can
wait.

This patch adds a new quirk for such sdhci host controller since this should
be an architecture issue. SDHCI host controller with this quirk can set the
max_discard_sectors to be one erase block size. With the set value, sdhci
host controller can operate erase command without error.

Signed-off-by: Chuanxiao Dong <chuanxiao.dong@xxxxxxxxx>
---
drivers/mmc/card/queue.c | 5 ++++-
drivers/mmc/core/core.c | 33 +++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.c | 4 ++++
include/linux/mmc/core.h | 1 +
include/linux/mmc/host.h | 2 ++
include/linux/mmc/sdhci.h | 2 ++
6 files changed, 46 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 4e42d03..f557788 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -131,7 +131,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
if (mmc_can_erase(card)) {
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mq->queue);
- mq->queue->limits.max_discard_sectors = UINT_MAX;
+ /* get a suitable max_discard_sectors limitation */
+ mq->queue->limits.max_discard_sectors =
+ mmc_set_discard_limit(card);
+
if (card->erased_byte == 0)
mq->queue->limits.discard_zeroes_data = 1;
if (!mmc_can_trim(card) && is_power_of_2(card->erase_size)) {
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 6286898..9fe16c2 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1469,6 +1469,39 @@ int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
}
EXPORT_SYMBOL(mmc_erase_group_aligned);

+/**
+ * mmc_set_discard_limit: set the max_discard_sectors according
+ * to host controller erase capability.
+ * @card: card need to be erased
+ *
+ * Return value:
+ * positive value: one erase block size
+ * UINT_MAX: unlimitied value which means no limitation for
+ * max_discard_sectors
+ */
+int mmc_set_discard_limit(struct mmc_card *card)
+{
+ struct mmc_host *host;
+ unsigned int nr = UINT_MAX;
+ host = card->host;
+ if (host->caps & MMC_CAP_ERASE_SINGLE) {
+ /*
+ * Have to set a small limitation for request queue
+ * to ensure that host controller won't generate a
+ * timeout interrupt during erasing, here let limitation
+ * to be one erase block size.
+ * And this will let TRIM/ERASE performance lower.
+ */
+ nr = 1;
+ if (card->erase_shift)
+ nr <<= card->erase_shift;
+ else
+ nr *= card->erase_size;
+ }
+ return nr;
+}
+EXPORT_SYMBOL(mmc_set_discard_limit);
+
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
{
struct mmc_command cmd;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 8a74fcb..b4448a9 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1875,6 +1875,10 @@ int sdhci_add_host(struct sdhci_host *host)

mmc->f_max = host->max_clk;
mmc->caps |= MMC_CAP_SDIO_IRQ;
+ mmc->caps |= MMC_CAP_ERASE;
+
+ if (host->quirks & SDHCI_QUIRK_FORCE_ERASE_SINGLE)
+ mmc->caps |= MMC_CAP_ERASE_SINGLE;

/*
* A controller may support 8-bit width, but the board itself
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 64e013f..ffddd1f 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -152,6 +152,7 @@ extern int mmc_can_trim(struct mmc_card *card);
extern int mmc_can_secure_erase_trim(struct mmc_card *card);
extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
unsigned int nr);
+extern int mmc_set_discard_limit(struct mmc_card *card);

extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);

diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 381c77f..c3d2ade 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -169,6 +169,8 @@ struct mmc_host {
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
/* DDR mode at 1.2V */
#define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */
+#define MMC_CAP_ERASE_SINGLE (1 << 14)
+ /* Erase signle erase block each time */

mmc_pm_flag_t pm_caps; /* supported pm features */

diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 1fdc673..e7bdfc8 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -83,6 +83,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
+/* Controller has an issue with erase/trim the whole device at one time */
+#define SDHCI_QUIRK_FORCE_ERASE_SINGLE (1<<30)

int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
--
1.6.6.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/