Skip to content

Commit

Permalink
Merge pull request #36 from grigorig/sdhci-perf-cleanup
Browse files Browse the repository at this point in the history
sdhci-bcm2708 performance, compatibility and reliability improvements
  • Loading branch information
popcornmix committed Jun 13, 2012
2 parents 958eacf + f1a14ae commit 801f610
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 83 deletions.
8 changes: 8 additions & 0 deletions drivers/mmc/card/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -1492,6 +1492,14 @@ static const struct mmc_fixup blk_fixups[] =
MMC_QUIRK_BLK_NO_CMD23),
MMC_FIXUP("MMC32G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_BLK_NO_CMD23),

/*
* Some Micron MMC cards needs longer data read timeout than
* indicated in CSD.
*/
MMC_FIXUP(CID_NAME_ANY, 0x13, 0x200, add_quirk_mmc,
MMC_QUIRK_LONG_READ_TIME),

END_FIXUP
};

Expand Down
30 changes: 22 additions & 8 deletions drivers/mmc/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,14 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)

if (data->flags & MMC_DATA_WRITE)
/*
* The limit is really 250 ms, but that is
* insufficient for some crappy cards.
* The MMC spec "It is strongly recommended
* for hosts to implement more than 500ms
* timeout value even if the card indicates
* the 250ms maximum busy length." Even the
* previous value of 300ms is known to be
* insufficient for some cards.
*/
limit_us = 300000;
limit_us = 3000000;
else
limit_us = 100000;

Expand All @@ -411,6 +415,18 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
data->timeout_clks = 0;
}
}

/*
* Some cards require longer data read timeout than indicated in CSD.
* Address this by setting the read timeout to a "reasonably high"
* value. For the cards tested, 300ms has proven enough. If necessary,
* this value can be increased if other problematic cards require this.
*/
if (mmc_card_long_read_time(card) && data->flags & MMC_DATA_READ) {
data->timeout_ns = 300000000;
data->timeout_clks = 0;
}

/*
* Some cards need very high timeouts if driven in SPI mode.
* The worst observed timeout was 900ms after writing a
Expand Down Expand Up @@ -1119,13 +1135,11 @@ static void mmc_power_up(struct mmc_host *host)
bit = fls(host->ocr_avail) - 1;

host->ios.vdd = bit;
if (mmc_host_is_spi(host)) {
if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH;
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
} else {
else
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
}
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
Expand Down
8 changes: 8 additions & 0 deletions drivers/mmc/core/mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
BUG_ON(!host);
WARN_ON(!host->claimed);

/* Set correct bus mode for MMC before attempting init */
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);

/*
* Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle
Expand Down Expand Up @@ -1019,6 +1023,10 @@ int mmc_attach_mmc(struct mmc_host *host)
BUG_ON(!host);
WARN_ON(!host->claimed);

/* Set correct bus mode for MMC before attempting attach */
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);

err = mmc_send_op_cond(host, 0, &ocr);
if (err)
return err;
Expand Down
8 changes: 3 additions & 5 deletions drivers/mmc/core/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ static int mmc_read_switch(struct mmc_card *card)
goto out;
}

if (status[13] & UHS_SDR50_BUS_SPEED)
card->sw_caps.hs_max_dtr = 50000000;

if (card->scr.sda_spec3) {
card->sw_caps.sd3_bus_mode = status[13];

Expand Down Expand Up @@ -348,9 +351,6 @@ static int mmc_read_switch(struct mmc_card *card)
}

card->sw_caps.sd3_curr_limit = status[7];
} else {
if (status[13] & 0x02)
card->sw_caps.hs_max_dtr = 50000000;
}

out:
Expand Down Expand Up @@ -929,8 +929,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
err = mmc_send_relative_addr(host, &card->rca);
if (err)
return err;

mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}

if (!oldcard) {
Expand Down
2 changes: 0 additions & 2 deletions drivers/mmc/core/sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
*/
if (oldcard)
oldcard->rca = card->rca;

mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}

/*
Expand Down
82 changes: 14 additions & 68 deletions drivers/mmc/host/sdhci-bcm2708.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
#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 BCM2708_EMMC_CLOCK_FREQ 50000000

#define POWER_OFF 0
#define POWER_LAZY_OFF 1
Expand Down Expand Up @@ -135,6 +135,8 @@ static inline unsigned long int since_ns(hptime_t t)
return (unsigned long)((hptime() - t) * HPTIME_CLK_NS);
}

static bool allow_highspeed = 1;

#if 0
static void hptime_test(void)
{
Expand Down Expand Up @@ -359,68 +361,9 @@ 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 20000000; // this value is in Hz (20MHz)
}

static unsigned int sdhci_bcm2708_get_timeout_clock(struct sdhci_host *host)
{
if(host->clock)
return (host->clock / 1000); // this value is in kHz (100MHz)
else
return (sdhci_bcm2708_get_max_clock(host) / 1000);
return BCM2708_EMMC_CLOCK_FREQ;
}

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 *
Expand Down Expand Up @@ -907,7 +850,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host,
while (0 != (sdhci_bcm2708_raw_readl(host, SDHCI_PRESENT_STATE)
& state_mask) && --timeout > 0)
{
udelay(100);
udelay(30);
continue;
}
if (timeout <= 0)
Expand Down Expand Up @@ -1307,11 +1250,7 @@ static struct sdhci_ops sdhci_bcm2708_ops = {
#else
#error The BCM2708 SDHCI driver needs CONFIG_MMC_SDHCI_IO_ACCESSORS to be set
#endif
//.enable_dma = 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,

.enable = sdhci_bcm2708_enable,
.disable = sdhci_bcm2708_disable,
Expand Down Expand Up @@ -1374,7 +1313,9 @@ static int __devinit sdhci_bcm2708_probe(struct platform_device *pdev)
host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_NONSTANDARD_CLOCK;
SDHCI_QUIRK_MISSING_CAPS |
SDHCI_QUIRK_NO_HISPD_BIT;

#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA
host->flags = SDHCI_USE_PLATDMA;
#endif
Expand Down Expand Up @@ -1442,7 +1383,8 @@ static int __devinit sdhci_bcm2708_probe(struct platform_device *pdev)
host_priv->dma_chan, host_priv->dma_chan_base,
host_priv->dma_irq);

host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
if (allow_highspeed)
host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
#endif

ret = sdhci_add_host(host);
Expand Down Expand Up @@ -1548,8 +1490,12 @@ static void __exit sdhci_drv_exit(void)
module_init(sdhci_drv_init);
module_exit(sdhci_drv_exit);

module_param(allow_highspeed, bool, 0444);

MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
MODULE_AUTHOR("Broadcom <info@broadcom.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:"DRIVER_NAME);

MODULE_PARM_DESC(allow_highspeed, "Allow high speed transfers modes");

6 changes: 6 additions & 0 deletions include/linux/mmc/card.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ struct mmc_card {
#define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */
#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */

unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */
Expand Down Expand Up @@ -377,6 +378,11 @@ static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
}

static inline int mmc_card_long_read_time(const struct mmc_card *c)
{
return c->quirks & MMC_QUIRK_LONG_READ_TIME;
}

#define mmc_card_name(c) ((c)->cid.prod_name)
#define mmc_card_id(c) (dev_name(&(c)->dev))

Expand Down

0 comments on commit 801f610

Please sign in to comment.