diff options
author | Gary King <gking@nvidia.com> | 2010-05-20 21:25:28 -0700 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-05-21 19:34:59 -0700 |
commit | 3f5d769df6006af515cd7a77ea56414d83a2e6a6 (patch) | |
tree | 49420f4a9f439dc6a7e1e06d080ca3893b6d33c2 /drivers/mmc | |
parent | c7edc161906cbdf0598dcf7f3cc7845c9328baaa (diff) |
sdhci: add Tegra 2 quirks
add BROKEN_WRITE_PROTECT quirk which calls a get_ro callback function
to detect the card read-only status for hosts which do not detect
the write protect flag correctly
add ENABLE_INTERRUPT_AT_BLOCK_GAP quick for hosts which do not detect SDIO
card interrupts unless INTERRUPT_AT_BLOCK_GAP is enabled
add BROKEN_CTRL_HISPD quirk for hosts which should not have CTRL_HISPD
bit set after switching to high-speed mode
add NO_64KB_ADMA quirk for hosts which need to split a 64KB (max) ADMA
transfer into 2 smaller transfers
add BROKEN_SPEC_VERSION quirk to bypass reading the HOST_VERSION register
for controllers which report incorrect values from this register
add NO_SDIO_IRQ quirk, for controllers which should use SDIO IRQs
Change-Id: I0f09f5a25c0d4048d8c7139a3d68ee3705f95589
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 66 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 15 |
2 files changed, 69 insertions, 12 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2d47bf53a88c..f81f35d6ee5b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -465,6 +465,25 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, len -= offset; } + if ((len == 0x10000) && + (host->quirks & SDHCI_QUIRK_NO_64KB_ADMA)) { + len = 1 << 15; + + desc[7] = (addr >> 24) & 0xff; + desc[6] = (addr >> 16) & 0xff; + desc[5] = (addr >> 8) & 0xff; + desc[4] = (addr >> 0) & 0xff; + + desc[3] = (len >> 8) & 0xff; + desc[2] = (len >> 0) & 0xff; + + desc[1] = 0x00; + desc[0] = 0x21; /* tran, valid */ + + desc += 8; + addr += len; + } + desc[7] = (addr >> 24) & 0xff; desc[6] = (addr >> 16) & 0xff; desc[5] = (addr >> 8) & 0xff; @@ -1162,10 +1181,15 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else ctrl &= ~SDHCI_CTRL_4BITBUS; - if (ios->timing == MMC_TIMING_SD_HS) - ctrl |= SDHCI_CTRL_HISPD; - else - ctrl &= ~SDHCI_CTRL_HISPD; + /* Tegra controllers often fail to detect high-speed cards when + * CTRL_HISPD is programmed + */ + if (!(host->quirks & SDHCI_QUIRK_BROKEN_CTRL_HISPD)) { + if (ios->timing == MMC_TIMING_SD_HS) + ctrl |= SDHCI_CTRL_HISPD; + else + ctrl &= ~SDHCI_CTRL_HISPD; + } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -1194,14 +1218,17 @@ static int sdhci_get_ro(struct mmc_host *mmc) if (host->flags & SDHCI_DEVICE_DEAD) present = 0; - else + else if (!(host->quirks & SDHCI_QUIRK_BROKEN_WRITE_PROTECT)) { present = sdhci_readl(host, SDHCI_PRESENT_STATE); + present = !(present & SDHCI_WRITE_PROTECT); + } else if (host->ops->get_ro) + present = host->ops->get_ro(host); + else + present = 0; spin_unlock_irqrestore(&host->lock, flags); - if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT) - return !!(present & SDHCI_WRITE_PROTECT); - return !(present & SDHCI_WRITE_PROTECT); + return present; } static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -1220,6 +1247,16 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); else sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); + + if (host->quirks & SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP) { + u8 gap_ctrl = sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL); + if (enable) + gap_ctrl |= 0x8; + else + gap_ctrl &= ~0x8; + sdhci_writeb(host, gap_ctrl, SDHCI_BLOCK_GAP_CONTROL); + } + out: mmiowb(); @@ -1690,9 +1727,11 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_reset(host, SDHCI_RESET_ALL); - host->version = sdhci_readw(host, SDHCI_HOST_VERSION); - host->version = (host->version & SDHCI_SPEC_VER_MASK) - >> SDHCI_SPEC_VER_SHIFT; + if (!(host->quirks & SDHCI_QUIRK_BROKEN_SPEC_VERSION)) { + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + host->version = (host->version & SDHCI_SPEC_VER_MASK) + >> SDHCI_SPEC_VER_SHIFT; + } if (host->version > SDHCI_SPEC_200) { printk(KERN_ERR "%s: Unknown controller version (%d). " "You may experience problems.\n", mmc_hostname(mmc), @@ -1802,11 +1841,14 @@ int sdhci_add_host(struct sdhci_host *host) else mmc->f_min = host->max_clk / 256; mmc->f_max = host->max_clk; - mmc->caps = MMC_CAP_SDIO_IRQ; + mmc->caps = 0; if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA)) mmc->caps |= MMC_CAP_4_BIT_DATA; + if (!(host->quirks & SDHCI_QUIRK_NO_SDIO_IRQ)) + mmc->caps |= MMC_CAP_SDIO_IRQ; + if (caps & SDHCI_CAN_DO_HISPD) mmc->caps |= MMC_CAP_SD_HIGHSPEED; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 98aa621fd22b..30e77e2ee262 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -234,6 +234,18 @@ struct sdhci_host { #define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23) /* Controller uses SDCLK instead of TMCLK for data timeouts */ #define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24) +/* Controller write protect bit is broken. Assume no write protection */ +#define SDHCI_QUIRK_BROKEN_WRITE_PROTECT (1<<25) +/* Controller needs INTERRUPT_AT_BLOCK_GAP enabled to detect card interrupts */ +#define SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP (1<<26) +/* Controller should not program HIGH_SPEED_EN after switching to high speed */ +#define SDHCI_QUIRK_BROKEN_CTRL_HISPD (1<<27) +/* Controller handles ADMA descriptor with length 0000h incorrectly */ +#define SDHCI_QUIRK_NO_64KB_ADMA (1<<28) +/* Version reported in HOST_VERSION is incorrect */ +#define SDHCI_QUIRK_BROKEN_SPEC_VERSION (1<<29) +/* Controller should not use SDIO IRQ */ +#define SDHCI_QUIRK_NO_SDIO_IRQ (1<<30) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ @@ -310,6 +322,9 @@ struct sdhci_ops { unsigned int (*get_max_clock)(struct sdhci_host *host); unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host); + /* returns card read-only status in a host-specific way if + * SDHCI_QUIRK_BROKEN_WRITE_PROTECT is set */ + int (*get_ro)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS |