summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r--drivers/mmc/host/sdhci.c86
1 files changed, 57 insertions, 29 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 401527d273b5..782c0ee3c925 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -47,7 +47,8 @@ static void sdhci_finish_command(struct sdhci_host *);
static void sdhci_dumpregs(struct sdhci_host *host)
{
- printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n");
+ printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
+ mmc_hostname(host->mmc));
printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
sdhci_readl(host, SDHCI_DMA_ADDRESS),
@@ -1001,13 +1002,28 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if (clock == 0)
goto out;
- for (div = 1;div < 256;div *= 2) {
- if ((host->max_clk / div) <= clock)
- break;
+ if (host->version >= SDHCI_SPEC_300) {
+ /* Version 3.00 divisors must be a multiple of 2. */
+ if (host->max_clk <= clock)
+ div = 1;
+ else {
+ for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
+ if ((host->max_clk / div) <= clock)
+ break;
+ }
+ }
+ } else {
+ /* Version 2.00 divisors must be a power of 2. */
+ for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
+ if ((host->max_clk / div) <= clock)
+ break;
+ }
}
div >>= 1;
- clk = div << SDHCI_DIVIDER_SHIFT;
+ clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+ clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+ << SDHCI_DIVIDER_HI_SHIFT;
clk |= SDHCI_CLOCK_INT_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
@@ -1034,11 +1050,9 @@ out:
static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
{
- u8 pwr;
+ u8 pwr = 0;
- if (power == (unsigned short)-1)
- pwr = 0;
- else {
+ if (power != (unsigned short)-1) {
switch (1 << power) {
case MMC_VDD_165_195:
pwr = SDHCI_POWER_180;
@@ -1168,6 +1182,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
sdhci_set_power(host, ios->vdd);
+ if (host->ops->platform_send_init_74_clocks)
+ host->ops->platform_send_init_74_clocks(host, ios->power_mode);
+
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (ios->bus_width == MMC_BUS_WIDTH_8)
@@ -1180,8 +1197,9 @@ 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 &&
- !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
+ if ((ios->timing == MMC_TIMING_SD_HS ||
+ ios->timing == MMC_TIMING_MMC_HS)
+ && !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
ctrl |= SDHCI_CTRL_HISPD;
else
ctrl &= ~SDHCI_CTRL_HISPD;
@@ -1205,22 +1223,25 @@ static int sdhci_get_ro(struct mmc_host *mmc)
{
struct sdhci_host *host;
unsigned long flags;
- int present;
+ int is_readonly;
host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
if (host->flags & SDHCI_DEVICE_DEAD)
- present = 0;
+ is_readonly = 0;
+ else if (host->ops->get_ro)
+ is_readonly = host->ops->get_ro(host);
else
- present = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE)
+ & SDHCI_WRITE_PROTECT);
spin_unlock_irqrestore(&host->lock, flags);
- if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT)
- return !!(present & SDHCI_WRITE_PROTECT);
- return !(present & SDHCI_WRITE_PROTECT);
+ /* This quirk needs to be replaced by a callback-function later */
+ return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ?
+ !is_readonly : is_readonly;
}
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
@@ -1427,7 +1448,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
sdhci_finish_command(host);
}
-#ifdef DEBUG
+#ifdef CONFIG_MMC_DEBUG
static void sdhci_show_adma_error(struct sdhci_host *host)
{
const char *name = mmc_hostname(host->mmc);
@@ -1708,7 +1729,7 @@ int sdhci_add_host(struct sdhci_host *host)
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) {
+ if (host->version > SDHCI_SPEC_300) {
printk(KERN_ERR "%s: Unknown controller version (%d). "
"You may experience problems.\n", mmc_hostname(mmc),
host->version);
@@ -1779,8 +1800,13 @@ int sdhci_add_host(struct sdhci_host *host)
mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
}
- host->max_clk =
- (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+ if (host->version >= SDHCI_SPEC_300)
+ host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK)
+ >> SDHCI_CLOCK_BASE_SHIFT;
+ else
+ host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK)
+ >> SDHCI_CLOCK_BASE_SHIFT;
+
host->max_clk *= 1000000;
if (host->max_clk == 0 || host->quirks &
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
@@ -1815,18 +1841,21 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->ops = &sdhci_ops;
if (host->ops->get_min_clock)
mmc->f_min = host->ops->get_min_clock(host);
+ else if (host->version >= SDHCI_SPEC_300)
+ mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300;
else
- mmc->f_min = host->max_clk / 256;
+ mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
mmc->f_max = host->max_clk;
mmc->caps |= MMC_CAP_SDIO_IRQ;
if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
- mmc->caps |= MMC_CAP_4_BIT_DATA;
+ mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
if (caps & SDHCI_CAN_DO_HISPD)
- mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+ mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
- if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
+ mmc_card_is_removable(mmc))
mmc->caps |= MMC_CAP_NEEDS_POLL;
mmc->ocr_avail = 0;
@@ -1850,12 +1879,11 @@ int sdhci_add_host(struct sdhci_host *host)
* can do scatter/gather or not.
*/
if (host->flags & SDHCI_USE_ADMA)
- mmc->max_hw_segs = 128;
+ mmc->max_segs = 128;
else if (host->flags & SDHCI_USE_SDMA)
- mmc->max_hw_segs = 1;
+ mmc->max_segs = 1;
else /* PIO */
- mmc->max_hw_segs = 128;
- mmc->max_phys_segs = 128;
+ mmc->max_segs = 128;
/*
* Maximum number of sectors in one transfer. Limited by DMA boundary