dect
/
linux-2.6
Archived
13
0
Fork 0

MMC fixes for 3.4-rc2:

The major fixes here are:
   * Disable use of MSI in sdhci-pci, which caused multiple chipsets to
     stop working in 3.4-rc1.  I'll wait to turn this on again until we
     have a chipset whitelist for it.
   * Fix a libertas SDIO powered-resume regression introduced in 3.3;
     thanks to Neil Brown and Rafael Wysocki for this fix.
   * Fix module reloading on omap_hsmmc.
   * Stop trusting the spec/card's specified maximum data timeout length,
     and use three seconds instead.  Previously we used 300ms.
 
  Also cleanups and fixes for s3c, atmel, sh_mmcif and omap_hsmmc.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJPfzfxAAoJEHNBYZ7TNxYMGBoP/02Drw+n4c7xykXdB7SSloq8
 SGIAmh25EBFyZhcEmQ8Mm5qn4voHG8+Jm7ZXA3OipJ4xucu5fdoCIu/n8arGYa2u
 GRB+GYOz92maCNBwLGZIMm7A64m2rkrRdwTw4pkjOZuJLh28GCshK2E+ZWoMKVNU
 flaUna7NQc/p2eXulCNirry+4l1xxGW00lYaSOq78MTOKUMwWLCJZw+TUvfvxjX8
 5BFc3b2+5fQKc1M0qh23oEcneRdOUUK6dWzvZHuQso1SkDeB1cetN9fzrhnys9or
 tooIqd+GQRPJ6oGqe+VD8MT8HGMPb6oETZaFM7h7bt8nFcScu3iVjmJQLIbnsNBY
 ho8AUkMbZtDtRBWDtk11zM9qrOWGufRdB0qW4R+4Zik1JohNp6SAeV7YSllIT4sT
 lT5gdzAxAiZ992iwYKuwAn0SQOlevN2BjHMFGg8abZtlOWEWE2YIQ0nTJS2SCIyj
 qbM9wmLZ2Ymo9KabRY+huRgrbViNHDzAQOLB/LEyvbPwD97arJE1R/OVNEasiv52
 rh5pCsIa5oeUeX1fsfysfug+cO96Xm1oL/cGyLRj8WZB++nurHRm7ZAo36r8FLK1
 4LpMokEEwpoPcbNqsfFvOy3aRV4sGNTDj+U8B4zyq9GigSoUwSAK7Nn2qbY9zIWj
 W+oh3jfoyMbyVE5+00X7
 =CCKD
 -----END PGP SIGNATURE-----

Merge tag 'mmc-fixes-for-3.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc

Pull MMC fixes from Chris Ball:
 - Disable use of MSI in sdhci-pci, which caused multiple chipsets to
   stop working in 3.4-rc1.  I'll wait to turn this on again until we
   have a chipset whitelist for it.
 - Fix a libertas SDIO powered-resume regression introduced in 3.3;
   thanks to Neil Brown and Rafael Wysocki for this fix.
 - Fix module reloading on omap_hsmmc.
 - Stop trusting the spec/card's specified maximum data timeout length,
   and use three seconds instead.  Previously we used 300ms.

Also cleanups and fixes for s3c, atmel, sh_mmcif and omap_hsmmc.

* tag 'mmc-fixes-for-3.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (28 commits)
  mmc: use really long write timeout to deal with crappy cards
  mmc: sdhci-dove: Fix compile error by including module.h
  mmc: Prevent 1.8V switch for SD hosts that don't support UHS modes.
  Revert "mmc: sdhci-pci: Add MSI support"
  Revert "mmc: sdhci-pci: add quirks for broken MSI on O2Micro controllers"
  mmc: core: fix power class selection
  mmc: omap_hsmmc: fix module re-insertion
  mmc: omap_hsmmc: convert to module_platform_driver
  mmc: omap_hsmmc: make it behave well as a module
  mmc: omap_hsmmc: trivial cleanups
  mmc: omap_hsmmc: context save after enabling runtime pm
  mmc: omap_hsmmc: use runtime put sync in probe error patch
  mmc: sdio: Use empty system suspend/resume callbacks at the bus level
  mmc: bus: print bus speed mode of UHS-I card
  mmc: sdhci-pci: add quirks for broken MSI on O2Micro controllers
  mmc: sh_mmcif: Simplify calculation of mmc->f_min
  mmc: sh_mmcif: mmc->f_max should be half of the bus clock
  mmc: sh_mmcif: double clock speed
  mmc: block: Remove use of mmc_blk_set_blksize
  mmc: atmel-mci: add support for odd clock dividers
  ...
This commit is contained in:
Linus Torvalds 2012-04-06 17:22:23 -07:00
commit 1ddca05743
13 changed files with 304 additions and 223 deletions

View File

@ -1623,24 +1623,6 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
return ret;
}
static int
mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
{
int err;
mmc_claim_host(card->host);
err = mmc_set_blocklen(card, 512);
mmc_release_host(card->host);
if (err) {
pr_err("%s: unable to set block size to 512: %d\n",
md->disk->disk_name, err);
return -EINVAL;
}
return 0;
}
static void mmc_blk_remove_req(struct mmc_blk_data *md)
{
struct mmc_card *card;
@ -1768,7 +1750,6 @@ static const struct mmc_fixup blk_fixups[] =
static int mmc_blk_probe(struct mmc_card *card)
{
struct mmc_blk_data *md, *part_md;
int err;
char cap_str[10];
/*
@ -1781,10 +1762,6 @@ static int mmc_blk_probe(struct mmc_card *card)
if (IS_ERR(md))
return PTR_ERR(md);
err = mmc_blk_set_blksize(md, card);
if (err)
goto out;
string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
cap_str, sizeof(cap_str));
pr_info("%s: %s %s %s %s\n",
@ -1809,7 +1786,7 @@ static int mmc_blk_probe(struct mmc_card *card)
out:
mmc_blk_remove_parts(card, md);
mmc_blk_remove_req(md);
return err;
return 0;
}
static void mmc_blk_remove(struct mmc_card *card)
@ -1845,8 +1822,6 @@ static int mmc_blk_resume(struct mmc_card *card)
struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) {
mmc_blk_set_blksize(md, card);
/*
* Resume involves the card going into idle state,
* so current partition is always the main one.

View File

@ -267,6 +267,15 @@ int mmc_add_card(struct mmc_card *card)
{
int ret;
const char *type;
const char *uhs_bus_speed_mode = "";
static const char *const uhs_speeds[] = {
[UHS_SDR12_BUS_SPEED] = "SDR12 ",
[UHS_SDR25_BUS_SPEED] = "SDR25 ",
[UHS_SDR50_BUS_SPEED] = "SDR50 ",
[UHS_SDR104_BUS_SPEED] = "SDR104 ",
[UHS_DDR50_BUS_SPEED] = "DDR50 ",
};
dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
@ -296,6 +305,10 @@ int mmc_add_card(struct mmc_card *card)
break;
}
if (mmc_sd_card_uhs(card) &&
(card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
if (mmc_host_is_spi(card->host)) {
pr_info("%s: new %s%s%s card on SPI\n",
mmc_hostname(card->host),
@ -303,13 +316,13 @@ int mmc_add_card(struct mmc_card *card)
mmc_card_ddr_mode(card) ? "DDR " : "",
type);
} else {
pr_info("%s: new %s%s%s%s card at address %04x\n",
pr_info("%s: new %s%s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""),
(mmc_card_hs200(card) ? "HS200 " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "",
type, card->rca);
uhs_bus_speed_mode, type, card->rca);
}
#ifdef CONFIG_DEBUG_FS

View File

@ -527,10 +527,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;

View File

@ -695,6 +695,11 @@ static int mmc_select_powerclass(struct mmc_card *card,
else if (host->ios.clock <= 200000000)
index = EXT_CSD_PWR_CL_200_195;
break;
case MMC_VDD_27_28:
case MMC_VDD_28_29:
case MMC_VDD_29_30:
case MMC_VDD_30_31:
case MMC_VDD_31_32:
case MMC_VDD_32_33:
case MMC_VDD_33_34:
case MMC_VDD_34_35:
@ -1111,11 +1116,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
if (err) {
pr_err("%s: power class selection to bus width %d failed\n",
mmc_hostname(card->host), 1 << bus_width);
goto err;
}
if (err)
pr_warning("%s: power class selection to bus width %d"
" failed\n", mmc_hostname(card->host),
1 << bus_width);
}
/*
@ -1147,10 +1151,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
ext_csd);
if (err)
pr_err("%s: power class selection to "
"bus width %d failed\n",
mmc_hostname(card->host),
1 << bus_width);
pr_warning("%s: power class selection to "
"bus width %d failed\n",
mmc_hostname(card->host),
1 << bus_width);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
@ -1178,10 +1182,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
ext_csd);
if (err)
pr_err("%s: power class selection to "
"bus width %d ddr %d failed\n",
mmc_hostname(card->host),
1 << bus_width, ddr);
pr_warning("%s: power class selection to "
"bus width %d ddr %d failed\n",
mmc_hostname(card->host),
1 << bus_width, ddr);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,

View File

@ -192,9 +192,15 @@ static int sdio_bus_remove(struct device *dev)
return ret;
}
#ifdef CONFIG_PM_RUNTIME
#ifdef CONFIG_PM
static int pm_no_operation(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops sdio_bus_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_no_operation, pm_no_operation)
SET_RUNTIME_PM_OPS(
pm_generic_runtime_suspend,
pm_generic_runtime_resume,
@ -204,11 +210,11 @@ static const struct dev_pm_ops sdio_bus_pm_ops = {
#define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops)
#else /* !CONFIG_PM_RUNTIME */
#else /* !CONFIG_PM */
#define SDIO_PM_OPS_PTR NULL
#endif /* !CONFIG_PM_RUNTIME */
#endif /* !CONFIG_PM */
static struct bus_type sdio_bus_type = {
.name = "sdio",

View File

@ -31,6 +31,7 @@
# define ATMCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */
# define ATMCI_MR_PDCPADV ( 1 << 14) /* Padding Value */
# define ATMCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */
# define ATMCI_MR_CLKODD(x) ((x) << 16) /* LSB of Clock Divider */
#define ATMCI_DTOR 0x0008 /* Data Timeout */
# define ATMCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
# define ATMCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */

View File

@ -77,6 +77,7 @@ struct atmel_mci_caps {
bool has_cstor_reg;
bool has_highspeed;
bool has_rwproof;
bool has_odd_clk_div;
};
struct atmel_mci_dma {
@ -482,7 +483,14 @@ err:
static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
unsigned int ns)
{
return (ns * (host->bus_hz / 1000000) + 999) / 1000;
/*
* It is easier here to use us instead of ns for the timeout,
* it prevents from overflows during calculation.
*/
unsigned int us = DIV_ROUND_UP(ns, 1000);
/* Maximum clock frequency is host->bus_hz/2 */
return us * (DIV_ROUND_UP(host->bus_hz, 2000000));
}
static void atmci_set_timeout(struct atmel_mci *host,
@ -1127,16 +1135,27 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
/* Calculate clock divider */
clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
if (clkdiv > 255) {
dev_warn(&mmc->class_dev,
"clock %u too slow; using %lu\n",
clock_min, host->bus_hz / (2 * 256));
clkdiv = 255;
if (host->caps.has_odd_clk_div) {
clkdiv = DIV_ROUND_UP(host->bus_hz, clock_min) - 2;
if (clkdiv > 511) {
dev_warn(&mmc->class_dev,
"clock %u too slow; using %lu\n",
clock_min, host->bus_hz / (511 + 2));
clkdiv = 511;
}
host->mode_reg = ATMCI_MR_CLKDIV(clkdiv >> 1)
| ATMCI_MR_CLKODD(clkdiv & 1);
} else {
clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
if (clkdiv > 255) {
dev_warn(&mmc->class_dev,
"clock %u too slow; using %lu\n",
clock_min, host->bus_hz / (2 * 256));
clkdiv = 255;
}
host->mode_reg = ATMCI_MR_CLKDIV(clkdiv);
}
host->mode_reg = ATMCI_MR_CLKDIV(clkdiv);
/*
* WRPROOF and RDPROOF prevent overruns/underruns by
* stopping the clock when the FIFO is full/empty.
@ -2007,35 +2026,35 @@ static void __init atmci_get_cap(struct atmel_mci *host)
"version: 0x%x\n", version);
host->caps.has_dma = 0;
host->caps.has_pdc = 0;
host->caps.has_pdc = 1;
host->caps.has_cfg_reg = 0;
host->caps.has_cstor_reg = 0;
host->caps.has_highspeed = 0;
host->caps.has_rwproof = 0;
host->caps.has_odd_clk_div = 0;
/* keep only major version number */
switch (version & 0xf00) {
case 0x100:
case 0x200:
host->caps.has_pdc = 1;
host->caps.has_rwproof = 1;
break;
case 0x300:
case 0x400:
case 0x500:
host->caps.has_odd_clk_div = 1;
case 0x400:
case 0x300:
#ifdef CONFIG_AT_HDMAC
host->caps.has_dma = 1;
#else
host->caps.has_dma = 0;
dev_info(&host->pdev->dev,
"has dma capability but dma engine is not selected, then use pio\n");
#endif
host->caps.has_pdc = 0;
host->caps.has_cfg_reg = 1;
host->caps.has_cstor_reg = 1;
host->caps.has_highspeed = 1;
case 0x200:
host->caps.has_rwproof = 1;
case 0x100:
break;
default:
host->caps.has_pdc = 0;
dev_warn(&host->pdev->dev,
"Unmanaged mci version, set minimum capabilities\n");
break;

View File

@ -1785,7 +1785,7 @@ static inline struct omap_mmc_platform_data
}
#endif
static int __init omap_hsmmc_probe(struct platform_device *pdev)
static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
{
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_host *mmc;
@ -1818,8 +1818,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if (res == NULL || irq < 0)
return -ENXIO;
res->start += pdata->reg_offset;
res->end += pdata->reg_offset;
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (res == NULL)
return -EBUSY;
@ -1843,7 +1841,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
host->dma_ch = -1;
host->irq = irq;
host->slot_id = 0;
host->mapbase = res->start;
host->mapbase = res->start + pdata->reg_offset;
host->base = ioremap(host->mapbase, SZ_4K);
host->power_mode = MMC_POWER_OFF;
host->next_data.cookie = 1;
@ -1875,8 +1873,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
goto err1;
}
omap_hsmmc_context_save(host);
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
@ -1887,6 +1883,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(host->dev);
omap_hsmmc_context_save(host);
if (cpu_is_omap2430()) {
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
/*
@ -2018,8 +2016,7 @@ err_reg:
err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
if (host->got_dbclk) {
@ -2037,35 +2034,33 @@ err:
return ret;
}
static int omap_hsmmc_remove(struct platform_device *pdev)
static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
{
struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
struct resource *res;
if (host) {
pm_runtime_get_sync(host->dev);
mmc_remove_host(host->mmc);
if (host->use_reg)
omap_hsmmc_reg_put(host);
if (host->pdata->cleanup)
host->pdata->cleanup(&pdev->dev);
free_irq(host->irq, host);
if (mmc_slot(host).card_detect_irq)
free_irq(mmc_slot(host).card_detect_irq, host);
pm_runtime_get_sync(host->dev);
mmc_remove_host(host->mmc);
if (host->use_reg)
omap_hsmmc_reg_put(host);
if (host->pdata->cleanup)
host->pdata->cleanup(&pdev->dev);
free_irq(host->irq, host);
if (mmc_slot(host).card_detect_irq)
free_irq(mmc_slot(host).card_detect_irq, host);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
if (host->got_dbclk) {
clk_disable(host->dbclk);
clk_put(host->dbclk);
}
mmc_free_host(host->mmc);
iounmap(host->base);
omap_hsmmc_gpio_free(pdev->dev.platform_data);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
if (host->got_dbclk) {
clk_disable(host->dbclk);
clk_put(host->dbclk);
}
mmc_free_host(host->mmc);
iounmap(host->base);
omap_hsmmc_gpio_free(pdev->dev.platform_data);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, resource_size(res));
@ -2078,49 +2073,45 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
static int omap_hsmmc_suspend(struct device *dev)
{
int ret = 0;
struct platform_device *pdev = to_platform_device(dev);
struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
if (!host)
return 0;
if (host && host->suspended)
return 0;
if (host) {
pm_runtime_get_sync(host->dev);
host->suspended = 1;
if (host->pdata->suspend) {
ret = host->pdata->suspend(&pdev->dev,
host->slot_id);
if (ret) {
dev_dbg(mmc_dev(host->mmc),
"Unable to handle MMC board"
" level suspend\n");
host->suspended = 0;
return ret;
}
}
ret = mmc_suspend_host(host->mmc);
pm_runtime_get_sync(host->dev);
host->suspended = 1;
if (host->pdata->suspend) {
ret = host->pdata->suspend(dev, host->slot_id);
if (ret) {
dev_dbg(dev, "Unable to handle MMC board"
" level suspend\n");
host->suspended = 0;
if (host->pdata->resume) {
ret = host->pdata->resume(&pdev->dev,
host->slot_id);
if (ret)
dev_dbg(mmc_dev(host->mmc),
"Unmask interrupt failed\n");
}
goto err;
return ret;
}
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
omap_hsmmc_disable_irq(host);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
}
if (host->got_dbclk)
clk_disable(host->dbclk);
}
ret = mmc_suspend_host(host->mmc);
if (ret) {
host->suspended = 0;
if (host->pdata->resume) {
ret = host->pdata->resume(dev, host->slot_id);
if (ret)
dev_dbg(dev, "Unmask interrupt failed\n");
}
goto err;
}
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
omap_hsmmc_disable_irq(host);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
}
if (host->got_dbclk)
clk_disable(host->dbclk);
err:
pm_runtime_put_sync(host->dev);
return ret;
@ -2130,39 +2121,38 @@ err:
static int omap_hsmmc_resume(struct device *dev)
{
int ret = 0;
struct platform_device *pdev = to_platform_device(dev);
struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
if (!host)
return 0;
if (host && !host->suspended)
return 0;
if (host) {
pm_runtime_get_sync(host->dev);
pm_runtime_get_sync(host->dev);
if (host->got_dbclk)
clk_enable(host->dbclk);
if (host->got_dbclk)
clk_enable(host->dbclk);
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
omap_hsmmc_conf_bus_power(host);
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
omap_hsmmc_conf_bus_power(host);
if (host->pdata->resume) {
ret = host->pdata->resume(&pdev->dev, host->slot_id);
if (ret)
dev_dbg(mmc_dev(host->mmc),
"Unmask interrupt failed\n");
}
omap_hsmmc_protect_card(host);
/* Notify the core to resume the host */
ret = mmc_resume_host(host->mmc);
if (ret == 0)
host->suspended = 0;
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
if (host->pdata->resume) {
ret = host->pdata->resume(dev, host->slot_id);
if (ret)
dev_dbg(dev, "Unmask interrupt failed\n");
}
omap_hsmmc_protect_card(host);
/* Notify the core to resume the host */
ret = mmc_resume_host(host->mmc);
if (ret == 0)
host->suspended = 0;
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return ret;
}
@ -2178,7 +2168,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_save(host);
dev_dbg(mmc_dev(host->mmc), "disabled\n");
dev_dbg(dev, "disabled\n");
return 0;
}
@ -2189,7 +2179,7 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_restore(host);
dev_dbg(mmc_dev(host->mmc), "enabled\n");
dev_dbg(dev, "enabled\n");
return 0;
}
@ -2202,7 +2192,8 @@ static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
};
static struct platform_driver omap_hsmmc_driver = {
.remove = omap_hsmmc_remove,
.probe = omap_hsmmc_probe,
.remove = __devexit_p(omap_hsmmc_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
@ -2211,21 +2202,7 @@ static struct platform_driver omap_hsmmc_driver = {
},
};
static int __init omap_hsmmc_init(void)
{
/* Register the MMC driver */
return platform_driver_probe(&omap_hsmmc_driver, omap_hsmmc_probe);
}
static void __exit omap_hsmmc_cleanup(void)
{
/* Unregister MMC driver */
platform_driver_unregister(&omap_hsmmc_driver);
}
module_init(omap_hsmmc_init);
module_exit(omap_hsmmc_cleanup);
module_platform_driver(omap_hsmmc_driver);
MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRIVER_NAME);

View File

@ -20,6 +20,7 @@
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mmc/host.h>
#include "sdhci-pltfm.h"

View File

@ -1418,8 +1418,6 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
slots = chip->num_slots; /* Quirk may have changed this */
pci_enable_msi(pdev);
for (i = 0; i < slots; i++) {
slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i);
if (IS_ERR(slot)) {
@ -1438,8 +1436,6 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
return 0;
free:
pci_disable_msi(pdev);
pci_set_drvdata(pdev, NULL);
kfree(chip);
@ -1462,8 +1458,6 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
for (i = 0; i < chip->num_slots; i++)
sdhci_pci_remove_slot(chip->slots[i]);
pci_disable_msi(pdev);
pci_set_drvdata(pdev, NULL);
kfree(chip);
}

View File

@ -20,6 +20,10 @@
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/host.h>
@ -53,6 +57,18 @@ struct sdhci_s3c {
struct clk *clk_bus[MAX_BUS_CLK];
};
/**
* struct sdhci_s3c_driver_data - S3C SDHCI platform specific driver data
* @sdhci_quirks: sdhci host specific quirks.
*
* Specifies platform specific configuration of sdhci controller.
* Note: A structure for driver specific platform data is used for future
* expansion of its usage.
*/
struct sdhci_s3c_drv_data {
unsigned int sdhci_quirks;
};
static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
{
return sdhci_priv(host);
@ -132,10 +148,10 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
return UINT_MAX;
/*
* Clock divider's step is different as 1 from that of host controller
* when 'clk_type' is S3C_SDHCI_CLK_DIV_EXTERNAL.
* If controller uses a non-standard clock division, find the best clock
* speed possible with selected clock source and skip the division.
*/
if (ourhost->pdata->clk_type) {
if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
rate = clk_round_rate(clksrc, wanted);
return wanted - rate;
}
@ -272,6 +288,8 @@ static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_s3c *ourhost = to_s3c(host);
unsigned long timeout;
u16 clk = 0;
/* don't bother if the clock is going off */
if (clock == 0)
@ -282,6 +300,25 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
host->clock = clock;
clk = SDHCI_CLOCK_INT_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
/* Wait max 20 ms */
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);
}
/**
@ -382,16 +419,24 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
}
}
static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data(
struct platform_device *pdev)
{
return (struct sdhci_s3c_drv_data *)
platform_get_device_id(pdev)->driver_data;
}
static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
{
struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
struct s3c_sdhci_platdata *pdata;
struct sdhci_s3c_drv_data *drv_data;
struct device *dev = &pdev->dev;
struct sdhci_host *host;
struct sdhci_s3c *sc;
struct resource *res;
int ret, irq, ptr, clks;
if (!pdata) {
if (!pdev->dev.platform_data) {
dev_err(dev, "no device data specified\n");
return -ENOENT;
}
@ -402,18 +447,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
return irq;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no memory specified\n");
return -ENOENT;
}
host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c));
if (IS_ERR(host)) {
dev_err(dev, "sdhci_alloc_host() failed\n");
return PTR_ERR(host);
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
ret = -ENOMEM;
goto err_io_clk;
}
memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
drv_data = sdhci_s3c_get_driver_data(pdev);
sc = sdhci_priv(host);
sc->host = host;
@ -464,15 +511,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
goto err_no_busclks;
}
sc->ioarea = request_mem_region(res->start, resource_size(res),
mmc_hostname(host->mmc));
if (!sc->ioarea) {
dev_err(dev, "failed to reserve register area\n");
ret = -ENXIO;
goto err_req_regs;
}
host->ioaddr = ioremap_nocache(res->start, resource_size(res));
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->ioaddr = devm_request_and_ioremap(&pdev->dev, res);
if (!host->ioaddr) {
dev_err(dev, "failed to map registers\n");
ret = -ENXIO;
@ -491,6 +531,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
/* Setup quirks for the controller */
host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
if (drv_data)
host->quirks |= drv_data->sdhci_quirks;
#ifndef CONFIG_MMC_SDHCI_S3C_DMA
@ -518,6 +560,14 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
host->mmc->caps = MMC_CAP_NONREMOVABLE;
switch (pdata->max_width) {
case 8:
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
case 4:
host->mmc->caps |= MMC_CAP_4_BIT_DATA;
break;
}
if (pdata->pm_caps)
host->mmc->pm_caps |= pdata->pm_caps;
@ -531,7 +581,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
* If controller does not have internal clock divider,
* we can use overriding functions instead of default.
*/
if (pdata->clk_type) {
if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;
sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;
sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock;
@ -544,10 +594,17 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
if (pdata->host_caps2)
host->mmc->caps2 |= pdata->host_caps2;
pm_runtime_enable(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1);
ret = sdhci_add_host(host);
if (ret) {
dev_err(dev, "sdhci_add_host() failed\n");
goto err_add_host;
pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
goto err_req_regs;
}
/* The following two methods of card detection might call
@ -561,10 +618,6 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
return 0;
err_add_host:
release_resource(sc->ioarea);
kfree(sc->ioarea);
err_req_regs:
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
if (sc->clk_bus[ptr]) {
@ -601,6 +654,8 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
sdhci_remove_host(host, 1);
pm_runtime_disable(&pdev->dev);
for (ptr = 0; ptr < 3; ptr++) {
if (sc->clk_bus[ptr]) {
clk_disable(sc->clk_bus[ptr]);
@ -610,18 +665,13 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
clk_disable(sc->clk_io);
clk_put(sc->clk_io);
iounmap(host->ioaddr);
release_resource(sc->ioarea);
kfree(sc->ioarea);
sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int sdhci_s3c_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -635,10 +685,29 @@ static int sdhci_s3c_resume(struct device *dev)
return sdhci_resume_host(host);
}
#endif
#ifdef CONFIG_PM_RUNTIME
static int sdhci_s3c_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
return sdhci_runtime_suspend_host(host);
}
static int sdhci_s3c_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
return sdhci_runtime_resume_host(host);
}
#endif
#ifdef CONFIG_PM
static const struct dev_pm_ops sdhci_s3c_pmops = {
.suspend = sdhci_s3c_suspend,
.resume = sdhci_s3c_resume,
SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume,
NULL)
};
#define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops)
@ -647,9 +716,31 @@ static const struct dev_pm_ops sdhci_s3c_pmops = {
#define SDHCI_S3C_PMOPS NULL
#endif
#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
.sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK,
};
#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data)
#else
#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL)
#endif
static struct platform_device_id sdhci_s3c_driver_ids[] = {
{
.name = "s3c-sdhci",
.driver_data = (kernel_ulong_t)NULL,
}, {
.name = "exynos4-sdhci",
.driver_data = EXYNOS4_SDHCI_DRV_DATA,
},
{ }
};
MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids);
static struct platform_driver sdhci_s3c_driver = {
.probe = sdhci_s3c_probe,
.remove = __devexit_p(sdhci_s3c_remove),
.id_table = sdhci_s3c_driver_ids,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-sdhci",

View File

@ -2782,8 +2782,9 @@ int sdhci_add_host(struct sdhci_host *host)
mmc_card_is_removable(mmc))
mmc->caps |= MMC_CAP_NEEDS_POLL;
/* UHS-I mode(s) supported by the host controller. */
if (host->version >= SDHCI_SPEC_300)
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50))
mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
/* SDR104 supports also implies SDR50 support */

View File

@ -454,7 +454,8 @@ static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK);
else
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR &
((fls(host->clk / clk) - 1) << 16));
((fls(DIV_ROUND_UP(host->clk,
clk) - 1) - 1) << 16));
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
}
@ -1297,14 +1298,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
spin_lock_init(&host->lock);
mmc->ops = &sh_mmcif_ops;
mmc->f_max = host->clk;
/* close to 400KHz */
if (mmc->f_max < 51200000)
mmc->f_min = mmc->f_max / 128;
else if (mmc->f_max < 102400000)
mmc->f_min = mmc->f_max / 256;
else
mmc->f_min = mmc->f_max / 512;
mmc->f_max = host->clk / 2;
mmc->f_min = host->clk / 512;
if (pd->ocr)
mmc->ocr_avail = pd->ocr;
mmc->caps = MMC_CAP_MMC_HIGHSPEED;