Permalink
Browse files

Fix for mmc timeouts when erasing multiple blocks

  • Loading branch information...
popcornmix authored and Chris Boot committed Mar 17, 2012
1 parent 5389e5f commit 84045e4d40677ec9322357bdb0fbeaa16c6d25fa
Showing with 102 additions and 12 deletions.
  1. +6 −4 arch/arm/mach-bcm2708/power.c
  2. +91 −6 drivers/mmc/host/sdhci-bcm2708.c
  3. +5 −2 drivers/mmc/host/sdhci.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/bug.h>
#include <linux/delay.h>
#include <mach/power.h>
#include <mach/vcio.h>
#include <mach/arm_power.h>
@@ -97,6 +98,7 @@ int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request)
global_request << 4);
/* Wait for a response during power-up */
mdelay(10);
if (global_request & ~g_state.global_request) {
rc = bcm_mailbox_read(MBOX_CHAN_POWER,
&actual);
@@ -111,14 +113,14 @@ int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request)
if (rc == 0) {
if (actual != global_request) {
printk(KERN_ERR
"%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n",
printk(KERN_INFO
"%s: Fail: prev global %x, new global %x, actual %x request %x, others_request %x\n",
__func__,
g_state.global_request,
global_request, actual, request, others_request);
/* A failure */
BUG_ON((others_request & actual)
!= others_request);
// BUG_ON((others_request & actual)
// != others_request);
request &= actual;
rc = -EIO;
}
@@ -26,7 +26,9 @@
#include <linux/highmem.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sd.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
@@ -68,6 +70,9 @@
#define BCM2708_SDHCI_SLEEP_TIMEOUT 1000 /* msecs */
/* Mhz clock that the EMMC core is running at. Should match the platform clockman settings */
#define BCM2708_EMMC_CLOCK_FREQ 80000000
#define POWER_OFF 0
#define POWER_LAZY_OFF 1
#define POWER_ON 2
@@ -222,6 +227,10 @@ u8 sdhci_bcm2708_readb(struct sdhci_host *host, int reg)
static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg)
{
u32 ier;
static bool timeout_disabled = false;
unsigned int ns_2clk = 0;
/* The Arasan has a bugette whereby it may lose the content of
* successive writes to registers that are within two SD-card clock
* cycles of each other (a clock domain crossing problem).
@@ -234,7 +243,7 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg)
/* host->clock is the clock freq in Hz */
static hptime_t last_write_hpt;
hptime_t now = hptime();
unsigned int ns_2clk = 2000000000/host->clock;
ns_2clk = 2000000000/host->clock;
if (now == last_write_hpt || now == last_write_hpt+1) {
/* we can't guarantee any significant time has
@@ -250,6 +259,24 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg)
}
last_write_hpt = now;
}
#if 1
/* The Arasan is clocked for timeouts using the SD clock which is too fast
* for ERASE commands and causes issues. So we disable timeouts for ERASE */
if (host->cmd != NULL && host->cmd->opcode == MMC_ERASE && reg == (SDHCI_COMMAND & ~3)) {
mod_timer(&host->timer, jiffies + 30 * HZ);
ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE);
ier &= ~SDHCI_INT_DATA_TIMEOUT;
writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
timeout_disabled = true;
udelay((ns_2clk+1000-1)/1000);
} else if (timeout_disabled) {
ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE);
ier |= SDHCI_INT_DATA_TIMEOUT;
writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
timeout_disabled = false;
udelay((ns_2clk+1000-1)/1000);
}
#endif
writel(val, host->ioaddr + reg);
#else
void __iomem * regaddr = host->ioaddr + reg;
@@ -325,14 +352,68 @@ void sdhci_bcm2708_writeb(struct sdhci_host *host, u8 val, int reg)
static unsigned int sdhci_bcm2708_get_max_clock(struct sdhci_host *host)
{
return 100000000; // this value is in Hz (100MHz/4)
return 20000000; // this value is in Hz (20MHz)
}
static unsigned int sdhci_bcm2708_get_timeout_clock(struct sdhci_host *host)
{
return 100000; // this value is in kHz (100MHz/4)
if(host->clock)
return (host->clock / 1000); // this value is in kHz (100MHz)
else
return (sdhci_bcm2708_get_max_clock(host) / 1000);
}
static void sdhci_bcm2708_set_clock(struct sdhci_host *host, unsigned int clock)
{
int div = 0;
u16 clk = 0;
unsigned long timeout;
if (clock == host->clock)
return;
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
if (clock == 0)
goto out;
if (BCM2708_EMMC_CLOCK_FREQ <= clock)
div = 1;
else {
for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
if ((BCM2708_EMMC_CLOCK_FREQ / div) <= clock)
break;
}
}
DBG( "desired SD clock: %d, actual: %d\n",
clock, BCM2708_EMMC_CLOCK_FREQ / div);
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);
timeout = 20;
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
printk(KERN_ERR "%s: Internal clock never "
"stabilised.\n", mmc_hostname(host->mmc));
return;
}
timeout--;
mdelay(1);
}
clk |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
out:
host->clock = clock;
}
/*****************************************************************************\
* *
* DMA Operation *
@@ -821,7 +902,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host,
continue;
if (1000000-timeout > 4000) /*ave. is about 3250*/
printk(KERN_INFO "%s: note - long %s sync %luns - "
DBG("%s: note - long %s sync %luns - "
"%d its.\n",
mmc_hostname(host->mmc),
data->flags & MMC_DATA_READ? "read": "write",
@@ -1219,7 +1300,7 @@ static struct sdhci_ops sdhci_bcm2708_ops = {
#error The BCM2708 SDHCI driver needs CONFIG_MMC_SDHCI_IO_ACCESSORS to be set
#endif
//.enable_dma = NULL,
//.set_clock = NULL,
.set_clock = sdhci_bcm2708_set_clock,
.get_max_clock = sdhci_bcm2708_get_max_clock,
//.get_min_clock = NULL,
.get_timeout_clock = sdhci_bcm2708_get_timeout_clock,
@@ -1282,7 +1363,9 @@ static int __devinit sdhci_bcm2708_probe(struct platform_device *pdev)
host->irq = platform_get_irq(pdev, 0);
host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_NONSTANDARD_CLOCK;
#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA
host->flags = SDHCI_USE_PLATDMA;
#endif
@@ -1349,6 +1432,8 @@ static int __devinit sdhci_bcm2708_probe(struct platform_device *pdev)
host_priv->cb_base, (unsigned)host_priv->cb_handle,
host_priv->dma_chan, host_priv->dma_chan_base,
host_priv->dma_irq);
host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
#endif
ret = sdhci_add_host(host);
View
@@ -2921,8 +2921,11 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->caps |= MMC_CAP_MAX_CURRENT_200;
}
if(host->ops->voltage_broken)
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
if(host->ops->voltage_broken) {
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
// Cannot support UHS modes is we are stuck at 3.3V;
mmc->caps &= ~(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50);
}
mmc->ocr_avail = ocr_avail;
mmc->ocr_avail_sdio = ocr_avail;

0 comments on commit 84045e4

Please sign in to comment.