From 09eeff52bf20d485bcafc441f01c142c59c3da16 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Fri, 1 Jun 2012 10:39:45 -0400 Subject: mmc: sdhci: Use DBG() instead of pr_warning() on large timeout 3bdc9ba892d6 ("mmc: use really long write timeout to deal with crappy cards") in 3.4 increased the write timeout that the core sends to host drivers to 3 seconds. This makes sdhci's "requested timeout too large" warning trigger on every write; so, change this pr_warning() to a DBG(). Reported-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e626732aff77..f4b8b4db3a9a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -680,8 +680,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) } if (count >= 0xF) { - pr_warning("%s: Too large timeout 0x%x requested for CMD%d!\n", - mmc_hostname(host->mmc), count, cmd->opcode); + DBG("%s: Too large timeout 0x%x requested for CMD%d!\n", + mmc_hostname(host->mmc), count, cmd->opcode); count = 0xE; } -- cgit From b87cc1b5d3a96ef9f1b3a4f8ce7aaff18e96c994 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Wed, 23 May 2012 15:52:15 +0200 Subject: mmc: atmel-mci: fix data timeout issue The data timeout timer was configured after mmc_add_host call. So, with bad timings, it was possible to have a mmc request causing mod_timer call on a non setup timer. Signed-off-by: Ludovic Desroches Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 420aca642b14..456c077455cb 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2314,6 +2314,8 @@ static int __init atmci_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); + setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host); + /* We need at least one slot to succeed */ nr_slots = 0; ret = -ENODEV; @@ -2352,8 +2354,6 @@ static int __init atmci_probe(struct platform_device *pdev) } } - setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host); - dev_info(&pdev->dev, "Atmel MCI controller at 0x%08lx irq %d, %u slots\n", host->mapbase, irq, nr_slots); -- cgit From 693e5e2025278d90e1427f037e5ec8ea1ec7d5c4 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 6 Jun 2012 12:19:44 +0200 Subject: mmc: atmel-mci: fix burst/chunk size modification The use of DMA slave config operation requires that the burst size (aka chunk size) is specified through this interface. Modify atmel-mci slave driver to use this specification on its side. Signed-off-by: Nicolas Ferre Signed-off-by: Ludovic Desroches Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci-regs.h | 14 ++++++++++++++ drivers/mmc/host/atmel-mci.c | 8 +++++--- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h index 787aba1682bb..ab56f7db5315 100644 --- a/drivers/mmc/host/atmel-mci-regs.h +++ b/drivers/mmc/host/atmel-mci-regs.h @@ -140,4 +140,18 @@ #define atmci_writel(port,reg,value) \ __raw_writel((value), (port)->regs + reg) +/* + * Fix sconfig's burst size according to atmel MCI. We need to convert them as: + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. + * + * This can be done by finding most significant bit set. + */ +static inline unsigned int atmci_convert_chksize(unsigned int maxburst) +{ + if (maxburst > 1) + return fls(maxburst) - 2; + else + return 0; +} + #endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */ diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 456c077455cb..f2c115e06438 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -910,6 +910,7 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) enum dma_data_direction direction; enum dma_transfer_direction slave_dirn; unsigned int sglen; + u32 maxburst; u32 iflags; data->error = -EINPROGRESS; @@ -943,17 +944,18 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) if (!chan) return -ENODEV; - if (host->caps.has_dma) - atmci_writel(host, ATMCI_DMA, ATMCI_DMA_CHKSIZE(3) | ATMCI_DMAEN); - if (data->flags & MMC_DATA_READ) { direction = DMA_FROM_DEVICE; host->dma_conf.direction = slave_dirn = DMA_DEV_TO_MEM; + maxburst = atmci_convert_chksize(host->dma_conf.src_maxburst); } else { direction = DMA_TO_DEVICE; host->dma_conf.direction = slave_dirn = DMA_MEM_TO_DEV; + maxburst = atmci_convert_chksize(host->dma_conf.dst_maxburst); } + atmci_writel(host, ATMCI_DMA, ATMCI_DMA_CHKSIZE(maxburst) | ATMCI_DMAEN); + sglen = dma_map_sg(chan->device->dev, data->sg, data->sg_len, direction); -- cgit From eed6c63cefaf935e6fb28c4dd9977a280ae544a8 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sun, 20 May 2012 13:27:21 +0900 Subject: mmc: dw_mmc: fix the transmission handling in IDMAC DTO interrupt can be later than transmit interrupt(IDMAC) in case of write. Current handling of IDMAC interrupt sets EVENT_DATA_COMPLETE as well as EVENT_XFER_COMPLETE regardless of DTO rising. This makes the current request finish in tasklet and permits the next request even though current data transfer is still in progress. As a result, sequence is broken and lock-up happens. Setting EVENT_DATA_COMPLETE is not proper after IDMAC interrupt. It should be taken after DTO interrupt is generated. Reported-by: Dmitry Shmidt Signed-off-by: Seungwon Jeon Signed-off-by: Hyeonsu Kim Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 9bbf45f8c538..b46faf0cfb27 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1623,7 +1623,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); - set_bit(EVENT_DATA_COMPLETE, &host->pending_events); host->dma_ops->complete(host); } #endif -- cgit From 141a712a4eb09639dd4973a7c5e6999e3b8ae04a Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Tue, 22 May 2012 13:01:03 +0900 Subject: mmc: dw_mmc: fix the IDMAC sw reset IDMAC may not be cleaned in driver probe if it has been already used in boot time. So IDMAC needs sw reset newly. And DMA interface reset precedes the internal DMAC reset. Additionally SDMMC_IDMAC_SWRESET is replaced with magic code. Signed-off-by: Seungwon Jeon Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b46faf0cfb27..98fe02347d59 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -418,6 +418,8 @@ static int dw_mci_idmac_init(struct dw_mci *host) p->des3 = host->sg_dma; p->des0 = IDMAC_DES0_ER; + mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET); + /* Mask out interrupts - get Tx & Rx complete only */ mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); @@ -1724,7 +1726,8 @@ static void dw_mci_work_routine_card(struct work_struct *work) #ifdef CONFIG_MMC_DW_IDMAC ctrl = mci_readl(host, BMOD); - ctrl |= 0x01; /* Software reset of DMA */ + /* Software reset of DMA */ + ctrl |= SDMMC_IDMAC_SWRESET; mci_writel(host, BMOD, ctrl); #endif @@ -1949,10 +1952,6 @@ int dw_mci_probe(struct dw_mci *host) spin_lock_init(&host->lock); INIT_LIST_HEAD(&host->queue); - - host->dma_ops = host->pdata->dma_ops; - dw_mci_init_dma(host); - /* * Get the host data width - this assumes that HCON has been set with * the correct values. @@ -1980,10 +1979,11 @@ int dw_mci_probe(struct dw_mci *host) } /* Reset all blocks */ - if (!mci_wait_reset(&host->dev, host)) { - ret = -ENODEV; - goto err_dmaunmap; - } + if (!mci_wait_reset(&host->dev, host)) + return -ENODEV; + + host->dma_ops = host->pdata->dma_ops; + dw_mci_init_dma(host); /* Clear the interrupts for the host controller */ mci_writel(host, RINTSTS, 0xFFFFFFFF); @@ -2169,14 +2169,14 @@ int dw_mci_resume(struct dw_mci *host) if (host->vmmc) regulator_enable(host->vmmc); - if (host->dma_ops->init) - host->dma_ops->init(host); - if (!mci_wait_reset(&host->dev, host)) { ret = -ENODEV; return ret; } + if (host->dma_ops->init) + host->dma_ops->init(host); + /* Restore the old value at FIFOTH register */ mci_writel(host, FIFOTH, host->fifoth_val); -- cgit From fda5f736864c46324dbc50246ef1ca0e84ebf4ae Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Tue, 22 May 2012 13:01:13 +0900 Subject: mmc: dw_mmc: fix incorrect setting of host->data of NULL Setting host->data to NULL is incorrect sequence in dw_mci_command_complete. This early setting makes the skip of dma_unmap_sg in dw_mci_dma_cleanup. Signed-off-by: Seungwon Jeon Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 98fe02347d59..b070ee542c8e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -941,8 +941,8 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd mdelay(20); if (cmd->data) { - host->data = NULL; dw_mci_stop_dma(host); + host->data = NULL; } } } -- cgit From e419990b5e811027b1552cbc5b76a6cc180f7f48 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Tue, 22 May 2012 13:01:21 +0900 Subject: mmc: dw_mmc: correct the calculation for CLKDIV In case of "host->bus_hz < slot->clock", divider value is miscalculated. And clock divider register value is multiple of 2. If calculated divider value is odd number, result can be over-clocking. Signed-off-by: Seungwon Jeon Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b070ee542c8e..1ca5e72ceb65 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -617,14 +617,15 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) u32 div; if (slot->clock != host->current_speed) { - if (host->bus_hz % slot->clock) + div = host->bus_hz / slot->clock; + if (host->bus_hz % slot->clock && host->bus_hz > slot->clock) /* * move the + 1 after the divide to prevent * over-clocking the card. */ - div = ((host->bus_hz / slot->clock) >> 1) + 1; - else - div = (host->bus_hz / slot->clock) >> 1; + div += 1; + + div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0; dev_info(&slot->mmc->class_dev, "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ" -- cgit From 4f837791d90bca76384b665eb2649feab169cc13 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 6 Jun 2012 09:44:09 -0400 Subject: mmc: omap: Fix a section warning regression Commit b6e0703b (mmc: omap: make it behave well as a module) made some __devinit changes but missed one function causing a section warning: WARNING: vmlinux.o(.devinit.text+0x8604): Section mismatch in reference from the function mmc_omap_probe) The function __devinit mmc_omap_probe() references a function __init mmc_omap_new_slot(). Signed-off-by: Tony Lindgren Acked-by: Felipe Balbi Signed-off-by: Chris Ball --- drivers/mmc/host/omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 552196c764d4..feda3064b2cd 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1300,7 +1300,7 @@ static const struct mmc_host_ops mmc_omap_ops = { .set_ios = mmc_omap_set_ios, }; -static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) +static int __devinit mmc_omap_new_slot(struct mmc_omap_host *host, int id) { struct mmc_omap_slot *slot = NULL; struct mmc_host *mmc; -- cgit From 3caf41406dd67b412abae7df86cc2a09bef9621f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 6 Jun 2012 09:45:50 -0400 Subject: mmc: omap: Fix NULL pointer dereference if mmc_omap_new_slot() fails Commit b01a4f1c (mmc: omap: convert to per instance workqueue) initializes the workqueue too late causing the following: Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 [00000000] *pgd=00000000 Internal error: Oops: 5 [#1] SMP ARM Modules linked in: CPU: 0 Not tainted (3.4.0-08218-gb48b2c3 #158) PC is at __queue_work+0x8/0x46c LR is at queue_work_on+0x38/0x40 pc : [] lr : [] psr: 60000193 sp : c0691e1c ip : 00000000 fp : c07374ac r10: c7aae400 r9 : c0395700 r8 : 00000100 r7 : c0691e70 r6 : 00000000 r5 : 00000000 r4 : c7aae440 r3 : 00000001 r2 : c7aae440 r1 : 00000000 r0 : 00000000 Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 00c5387d Table: 80004000 DAC: 00000017 Process swapper/0 (pid: 0, stack limit = 0xc06902f8) Stack: (0xc0691e1c to 0xc0692000) Fix this by initializing the workqueue before mmc_omap_remove_slot() get called. Tested on n770, looks like n800 at least still has some other issue with MMC. Signed-off-by: Tony Lindgren Signed-off-by: Chris Ball --- drivers/mmc/host/omap.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index feda3064b2cd..6b07730598da 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1485,24 +1485,27 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) } host->nr_slots = pdata->nr_slots; + + host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0); + if (!host->mmc_omap_wq) + goto err_plat_cleanup; + for (i = 0; i < pdata->nr_slots; i++) { ret = mmc_omap_new_slot(host, i); if (ret < 0) { while (--i >= 0) mmc_omap_remove_slot(host->slots[i]); - goto err_plat_cleanup; + goto err_destroy_wq; } } host->reg_shift = (cpu_is_omap7xx() ? 1 : 2); - host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0); - if (!host->mmc_omap_wq) - goto err_plat_cleanup; - return 0; +err_destroy_wq: + destroy_workqueue(host->mmc_omap_wq); err_plat_cleanup: if (pdata->cleanup) pdata->cleanup(&pdev->dev); -- cgit From ebbe6f889f36d35f4d1757ba2ecc0af6150bf12b Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 6 Jun 2012 09:47:49 -0400 Subject: mmc: omap: Fix broken reg_shift initialization Commit fa550189 (mmc: core: Prevent eMMC VCC supply to be cut from late init) slightly affected timings for how things are done for the omap MMC driver causing the MMC cards not getting detected any longer. Turns out this was caused by buggy reg_shift initialization in the omap MMC driver that was happening after mmc_add_host() was being called. Fix this by initializing reg_shift before mmc_add_host() is called. Signed-off-by: Tony Lindgren Cc: Signed-off-by: Chris Ball --- drivers/mmc/host/omap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 6b07730598da..3e8dcf8d2e05 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1485,6 +1485,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) } host->nr_slots = pdata->nr_slots; + host->reg_shift = (cpu_is_omap7xx() ? 1 : 2); host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0); if (!host->mmc_omap_wq) @@ -1500,8 +1501,6 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) } } - host->reg_shift = (cpu_is_omap7xx() ? 1 : 2); - return 0; err_destroy_wq: -- cgit From 81ec1daa118ec9a04c0a907883872ae2996372bd Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Sun, 3 Jun 2012 15:29:55 +0200 Subject: mmc: sdhci-s3c: pass IRQF ONESHOT to request threaded irq Fix a boot regression in existing kernels causing: genirq: Threaded irq requested with handler=NULL and !ONESHOT for irq XXX caused by 1c6c6952 (genirq: Reject bogus threaded irq requests). Signed-off-by: Heiko Stuebner Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-s3c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 55a164fcaa15..a50c205ea208 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -404,7 +404,7 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) if (sc->ext_cd_irq && request_threaded_irq(sc->ext_cd_irq, NULL, sdhci_s3c_gpio_card_detect_thread, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, dev_name(dev), sc) == 0) { int status = gpio_get_value(sc->ext_cd_gpio); if (pdata->ext_cd_gpio_invert) -- cgit From a3e545e9ab26892641ecac7cee30ea4b4e87977e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 May 2012 06:33:27 +0200 Subject: mmc: mxs-mmc: Move of_match_table out of CONFIG_PM Signed-off-by: Marek Vasut Acked-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 34a90266ab11..277161d279b8 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -894,8 +894,8 @@ static struct platform_driver mxs_mmc_driver = { .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &mxs_mmc_pm_ops, - .of_match_table = mxs_mmc_dt_ids, #endif + .of_match_table = mxs_mmc_dt_ids, }, }; -- cgit From c11bd557903395f436d63bb6dfb0f4b86ea365d5 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 21 May 2012 16:43:42 -0600 Subject: mmc: tegra: use bus-width property instead of support-8bit Update the driver to parse the new unified bus-width property introduced in commit 7f21779 "mmc: dt: Consolidate DT bindings", instead of the legacy support-8bit property. Signed-off-by: Stephen Warren Acked-by: Chris Ball --- drivers/mmc/host/sdhci-tegra.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b38d8a78f6a0..6e5338a071ce 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -223,6 +223,7 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata( { struct tegra_sdhci_platform_data *plat; struct device_node *np = pdev->dev.of_node; + u32 bus_width; if (!np) return NULL; @@ -236,7 +237,9 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata( plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0); - if (of_find_property(np, "support-8bit", NULL)) + + if (of_property_read_u32(np, "bus-width", &bus_width) == 0 && + bus_width == 8) plat->is_8bit = 1; return plat; -- cgit From 1e674bc6689e9aca5bd4355d1f87093636ed1c02 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:37 +0530 Subject: mmc: tegra: add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Cc: cjb@laptop.org Cc: linux-mmc@vger.kernel.org Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren --- drivers/mmc/host/sdhci-tegra.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b38d8a78f6a0..278965695f00 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -334,7 +334,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) rc = PTR_ERR(clk); goto err_clk_get; } - clk_enable(clk); + clk_prepare_enable(clk); pltfm_host->clk = clk; host->mmc->pm_caps = plat->pm_flags; @@ -349,7 +349,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) return 0; err_add_host: - clk_disable(pltfm_host->clk); + clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); err_clk_get: if (gpio_is_valid(plat->wp_gpio)) @@ -390,7 +390,7 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev) if (gpio_is_valid(plat->power_gpio)) gpio_free(plat->power_gpio); - clk_disable(pltfm_host->clk); + clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); sdhci_pltfm_free(pdev); -- cgit From b9b52918abe88d9135dd570fb394b9c432eb1cfd Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 Jun 2012 10:49:51 +0100 Subject: ARM: 7422/1: mmc: mmci: Allocate platform memory during Device Tree boot When booting with Device Tree enabled, platform specific information is gathered by parsing the DT binary. Platform data is subsequently populated with the result. The memory required for this is not automatically allocated during Device Tree boot, so we'll do it here instead. Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index f0fcce40cd8d..0045ee001ec7 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1276,6 +1276,12 @@ static int __devinit mmci_probe(struct amba_device *dev, return -EINVAL; } + if (!plat) { + plat = devm_kzalloc(&dev->dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return -ENOMEM; + } + if (np) mmci_dt_populate_generic_pdata(np, plat); -- cgit From f43380981716c9b48bf2809e4cd1cb0c6b71429d Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Sun, 17 Jun 2012 21:13:34 +0100 Subject: ARM: 7426/1: mmc: mmci: Remove wrong error handling of gpio 0 Zero is a valid GPIO and shouldn't be handled as an error return code from of_get_named_gpio(). It was a leftover from old code before getting pdata->gpio_*() was modified. Signed-off-by: Roland Stigge Acked-by: Linus Walleij Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 0045ee001ec7..78d3e4845997 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1216,12 +1216,7 @@ static void mmci_dt_populate_generic_pdata(struct device_node *np, int bus_width = 0; pdata->gpio_wp = of_get_named_gpio(np, "wp-gpios", 0); - if (!pdata->gpio_wp) - pdata->gpio_wp = -1; - pdata->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0); - if (!pdata->gpio_cd) - pdata->gpio_cd = -1; if (of_get_property(np, "cd-inverted", NULL)) pdata->cd_invert = true; -- cgit From 2805b9ab7c836cfbe01e86129983e36205078cb2 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Sun, 17 Jun 2012 21:14:27 +0100 Subject: ARM: 7427/1: mmc: mmci: Defer probe() in case of yet uninitialized GPIOs If the GPIOs used by the MMCI driver are not registered yet when the driver is probe()d, they can't be used. This happens if the mmci driver is probed before the respective GPIO controller (e.g. on the LPC32xx EA3250 board, the PCA9532 GPIO controller would be initialized via DT after mmci). Therefore, we defer mmci in this case. Signed-off-by: Roland Stigge Acked-by: Linus Walleij Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 78d3e4845997..50ff19a62368 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1425,6 +1425,10 @@ static int __devinit mmci_probe(struct amba_device *dev, writel(0, host->base + MMCIMASK1); writel(0xfff, host->base + MMCICLEAR); + if (plat->gpio_cd == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_gpio_cd; + } if (gpio_is_valid(plat->gpio_cd)) { ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)"); if (ret == 0) @@ -1448,6 +1452,10 @@ static int __devinit mmci_probe(struct amba_device *dev, if (ret >= 0) host->gpio_cd_irq = gpio_to_irq(plat->gpio_cd); } + if (plat->gpio_wp == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_gpio_wp; + } if (gpio_is_valid(plat->gpio_wp)) { ret = gpio_request(plat->gpio_wp, DRIVER_NAME " (wp)"); if (ret == 0) -- cgit From 10d8935f46e5028847b179757ecbf9238b13d129 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 Jun 2012 12:53:02 -0700 Subject: Viresh has moved viresh.kumar@st.com email-id doesn't exist anymore as I have left the company. Replace ST's id with viresh.linux@gmail.com. It also updates .mailmap file to fix address for 'git shortlog' Signed-off-by: Viresh Kumar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/host/sdhci-spear.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 1fe32dfa7cd4..423da8194cd8 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -4,7 +4,7 @@ * Support of SDHCI platform devices for spear soc family * * Copyright (C) 2010 ST Microelectronics - * Viresh Kumar + * Viresh Kumar * * Inspired by sdhci-pltfm.c * @@ -289,5 +289,5 @@ static struct platform_driver sdhci_driver = { module_platform_driver(sdhci_driver); MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); -MODULE_AUTHOR("Viresh Kumar "); +MODULE_AUTHOR("Viresh Kumar "); MODULE_LICENSE("GPL v2"); -- cgit From fe85227347738eb9b871bc163e7fb0db8b6cd2a0 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 22 Jun 2012 18:49:35 +0800 Subject: Revert "mmc: omap_hsmmc: Enable Auto CMD12" This patch reverts the commit dba3c29ea4a1d5d544. After bisecting, this commit dba3c29 is found to ruin micro-SD card data (writing incorrect file, or fs is corrupt after several times mount) on the beagle-xm revB, and reverting the commit will fix the problem. Also from TRM of OMAP3/OMAP4/DM37x, the below is mentioned about the Auto CMD12 Enable bit. - SDIO does not support this feature. - SD card only. Looks it is not suitable to always enable Auto CMD12 in host controller driver. Considered that the commit is not mature enough, so ask to revert it first. Cc: Balaji T K Cc: Venkatraman S Buglink: https://lkml.org/lkml/2012/6/10/225 Reported-by: Paolo Pisati Reported-bisected-and-tested-by: Ming Lei Signed-off-by: Ming Lei Acked-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 9a7a60aeb19e..389a3eedfc24 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -85,7 +85,6 @@ #define BRR_ENABLE (1 << 5) #define DTO_ENABLE (1 << 20) #define INIT_STREAM (1 << 1) -#define ACEN_ACMD12 (1 << 2) #define DP_SELECT (1 << 21) #define DDIR (1 << 4) #define DMA_EN 0x1 @@ -117,7 +116,6 @@ #define OMAP_MMC_MAX_CLOCK 52000000 #define DRIVER_NAME "omap_hsmmc" -#define AUTO_CMD12 (1 << 0) /* Auto CMD12 support */ /* * One controller can have multiple slots, like on some omap boards using * omap.c controller driver. Luckily this is not currently done on any known @@ -177,7 +175,6 @@ struct omap_hsmmc_host { int reqs_blocked; int use_reg; int req_in_progress; - unsigned int flags; struct omap_hsmmc_next next_data; struct omap_mmc_platform_data *pdata; @@ -773,8 +770,6 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, cmdtype = 0x3; cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22); - if ((host->flags & AUTO_CMD12) && mmc_op_multi(cmd->opcode)) - cmdreg |= ACEN_ACMD12; if (data) { cmdreg |= DP_SELECT | MSBS | BCE; @@ -847,14 +842,11 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data) else data->bytes_xfered = 0; - if (data->stop && ((!(host->flags & AUTO_CMD12)) || data->error)) { - omap_hsmmc_start_command(host, data->stop, NULL); - } else { - if (data->stop) - data->stop->resp[0] = OMAP_HSMMC_READ(host->base, - RSP76); + if (!data->stop) { omap_hsmmc_request_done(host, data->mrq); + return; } + omap_hsmmc_start_command(host, data->stop, NULL); } /* @@ -1859,7 +1851,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) host->mapbase = res->start + pdata->reg_offset; host->base = ioremap(host->mapbase, SZ_4K); host->power_mode = MMC_POWER_OFF; - host->flags = AUTO_CMD12; host->next_data.cookie = 1; platform_set_drvdata(pdev, host); -- cgit From 0f506a96693d8ad4aeccbd98dbd54a3c7357cfa8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:35 +0200 Subject: mmc: tmio: use MMC opcode defines instead of numbers mmc.h defines macros for most frequently used MMC opcodes. Use them instead of hard-coded numbers. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 9a7996ade58e..0ad3917cafd1 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -305,8 +306,8 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command int c = cmd->opcode; u32 irq_mask = TMIO_MASK_CMD; - /* Command 12 is handled by hardware */ - if (cmd->opcode == 12 && !cmd->arg) { + /* CMD12 is handled by hardware */ + if (cmd->opcode == MMC_STOP_TRANSMISSION && !cmd->arg) { sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x001); return 0; } @@ -449,7 +450,7 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host) } if (stop) { - if (stop->opcode == 12 && !stop->arg) + if (stop->opcode == MMC_STOP_TRANSMISSION && !stop->arg) sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x000); else BUG(); -- cgit From fd0ea65d3e675e479e022b6cfc9ebe1864c76afc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 30 Apr 2012 23:31:57 +0200 Subject: mmc: extend and rename cd-gpio helpers to handle more slot GPIO functions GPIOs can be used in MMC/SD-card slots not only for hotplug detection, but also to implement the write-protection pin. Rename cd-gpio helpers to slot-gpio to make addition of further slot GPIO functions possible. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 0ad3917cafd1..7ffc489bed35 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -34,9 +34,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -977,7 +977,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, tmio_mmc_enable_mmc_irqs(_host, irq_mask); if (pdata->flags & TMIO_MMC_USE_GPIO_CD) { - ret = mmc_cd_gpio_request(mmc, pdata->cd_gpio); + ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio); if (ret < 0) { tmio_mmc_host_remove(_host); return ret; @@ -1009,7 +1009,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) * This means we can miss a card-eject, but this is anyway * possible, because of delayed processing of hotplug events. */ - mmc_cd_gpio_free(mmc); + mmc_gpio_free_cd(mmc); if (!host->native_hotplug) pm_runtime_get_sync(&pdev->dev); -- cgit From 916001fe33b7b4dc797f7b29ec8bc346c4369fa6 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 9 May 2012 17:09:15 +0200 Subject: mmc: sh_mmcif: remove unneeded struct sh_mmcif_dma, prepare to shdma conversion Now that all users have been updated to use the embedded in struct sh_mmcif_plat_data DMA slave IDs, struct sh_mmcif_dma is no longer needed and can be removed. This also makes preparation to the shdma base library conversion easier. Signed-off-by: Guennadi Liakhovetski Cc: Chris Ball Signed-off-by: Vinod Koul --- drivers/mmc/host/sh_mmcif.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 724b35e85a26..9e3b9b1c3637 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -385,31 +385,27 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host, host->dma_active = false; /* We can only either use DMA for both Tx and Rx or not use it at all */ - if (pdata->dma) { - dev_warn(&host->pd->dev, - "Update your platform to use embedded DMA slave IDs\n"); - tx = &pdata->dma->chan_priv_tx; - rx = &pdata->dma->chan_priv_rx; - } else { - tx = &host->dma_slave_tx; - tx->slave_id = pdata->slave_id_tx; - rx = &host->dma_slave_rx; - rx->slave_id = pdata->slave_id_rx; - } - if (tx->slave_id > 0 && rx->slave_id > 0) { + tx = &host->dma_slave_tx; + tx->shdma_slave.slave_id = pdata->slave_id_tx; + rx = &host->dma_slave_rx; + rx->shdma_slave.slave_id = pdata->slave_id_rx; + + if (tx->shdma_slave.slave_id > 0 && rx->shdma_slave.slave_id > 0) { dma_cap_mask_t mask; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, tx); + host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, + &tx->shdma_slave); dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__, host->chan_tx); if (!host->chan_tx) return; - host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, rx); + host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, + &rx->shdma_slave); dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__, host->chan_rx); -- cgit From c471270d7a4161a8f1794202ad8338c0ec55759e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 9 May 2012 17:09:16 +0200 Subject: mmc: sh_mobile_sdhi: prepare for conversion to the shdma base library Signed-off-by: Guennadi Liakhovetski Cc: Chris Ball Signed-off-by: Vinod Koul --- drivers/mmc/host/sh_mobile_sdhi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 934b68e9efc3..b8d7004cf04a 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -145,10 +145,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->cd_gpio = p->cd_gpio; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { - priv->param_tx.slave_id = p->dma_slave_tx; - priv->param_rx.slave_id = p->dma_slave_rx; - priv->dma_priv.chan_priv_tx = &priv->param_tx; - priv->dma_priv.chan_priv_rx = &priv->param_rx; + priv->param_tx.shdma_slave.slave_id = p->dma_slave_tx; + priv->param_rx.shdma_slave.slave_id = p->dma_slave_rx; + priv->dma_priv.chan_priv_tx = &priv->param_tx.shdma_slave; + priv->dma_priv.chan_priv_rx = &priv->param_rx.shdma_slave; priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */ mmc_data->dma = &priv->dma_priv; } -- cgit From 0e79f9ae1610c15f5e5959c39d7c39071619de97 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 5 Jul 2012 12:29:43 +0200 Subject: mmc: sh_mmcif: switch to the new DMA channel allocation and configuration Using the "private" field from struct dma_chan is deprecated. The sh dmaengine driver now also supports the preferred DMA channel allocation and configuration method, using a standard filter function and a channel configuration operation. This patch updates sh_mmcif to use this new method. Signed-off-by: Guennadi Liakhovetski Cc: Chris Ball Signed-off-by: Vinod Koul --- drivers/mmc/host/sh_mmcif.c | 82 ++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 35 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 9e3b9b1c3637..0f07d2878c49 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -211,8 +211,6 @@ struct sh_mmcif_host { struct mmc_host *mmc; struct mmc_request *mrq; struct platform_device *pd; - struct sh_dmae_slave dma_slave_tx; - struct sh_dmae_slave dma_slave_rx; struct clk *hclk; unsigned int clk; int bus_width; @@ -371,52 +369,66 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) desc, cookie); } -static bool sh_mmcif_filter(struct dma_chan *chan, void *arg) -{ - dev_dbg(chan->device->dev, "%s: slave data %p\n", __func__, arg); - chan->private = arg; - return true; -} - static void sh_mmcif_request_dma(struct sh_mmcif_host *host, struct sh_mmcif_plat_data *pdata) { - struct sh_dmae_slave *tx, *rx; + struct resource *res = platform_get_resource(host->pd, IORESOURCE_MEM, 0); + struct dma_slave_config cfg; + dma_cap_mask_t mask; + int ret; + host->dma_active = false; + if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) + return; + /* We can only either use DMA for both Tx and Rx or not use it at all */ - tx = &host->dma_slave_tx; - tx->shdma_slave.slave_id = pdata->slave_id_tx; - rx = &host->dma_slave_rx; - rx->shdma_slave.slave_id = pdata->slave_id_rx; + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + host->chan_tx = dma_request_channel(mask, shdma_chan_filter, + (void *)pdata->slave_id_tx); + dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__, + host->chan_tx); - if (tx->shdma_slave.slave_id > 0 && rx->shdma_slave.slave_id > 0) { - dma_cap_mask_t mask; + if (!host->chan_tx) + return; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); + cfg.slave_id = pdata->slave_id_tx; + cfg.direction = DMA_MEM_TO_DEV; + cfg.dst_addr = res->start + MMCIF_CE_DATA; + cfg.src_addr = 0; + ret = dmaengine_slave_config(host->chan_tx, &cfg); + if (ret < 0) + goto ecfgtx; - host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, - &tx->shdma_slave); - dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__, - host->chan_tx); + host->chan_rx = dma_request_channel(mask, shdma_chan_filter, + (void *)pdata->slave_id_rx); + dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__, + host->chan_rx); - if (!host->chan_tx) - return; + if (!host->chan_rx) + goto erqrx; - host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, - &rx->shdma_slave); - dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__, - host->chan_rx); + cfg.slave_id = pdata->slave_id_rx; + cfg.direction = DMA_DEV_TO_MEM; + cfg.dst_addr = 0; + cfg.src_addr = res->start + MMCIF_CE_DATA; + ret = dmaengine_slave_config(host->chan_rx, &cfg); + if (ret < 0) + goto ecfgrx; - if (!host->chan_rx) { - dma_release_channel(host->chan_tx); - host->chan_tx = NULL; - return; - } + init_completion(&host->dma_complete); - init_completion(&host->dma_complete); - } + return; + +ecfgrx: + dma_release_channel(host->chan_rx); + host->chan_rx = NULL; +erqrx: +ecfgtx: + dma_release_channel(host->chan_tx); + host->chan_tx = NULL; } static void sh_mmcif_release_dma(struct sh_mmcif_host *host) -- cgit From b650352dd3df36164e3427bff3f33bc06ac47642 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Tue, 10 Apr 2012 22:34:33 -0400 Subject: mmc: sdhci-pxa: Add device tree support Tested on an OLPC XO-1.75. (MMP2, sdhci-pxav3, CONFIG_MACH_MMP2_DT=y) Signed-off-by: Chris Ball Acked-by: Haojian Zhuang --- drivers/mmc/host/sdhci-pxav2.c | 54 ++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-pxav3.c | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index dbb75bfbcffb..b6ee8857e226 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include + #include "sdhci.h" #include "sdhci-pltfm.h" @@ -121,6 +124,48 @@ static struct sdhci_ops pxav2_sdhci_ops = { .platform_8bit_width = pxav2_mmc_set_width, }; +#ifdef CONFIG_OF +static const struct of_device_id sdhci_pxav2_of_match[] = { + { + .compatible = "mrvl,pxav2-mmc", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_pxav2_of_match); + +static struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev) +{ + struct sdhci_pxa_platdata *pdata; + struct device_node *np = dev->of_node; + u32 bus_width; + u32 clk_delay_cycles; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_find_property(np, "non-removable", NULL)) + pdata->flags |= PXA_FLAG_CARD_PERMANENT; + + of_property_read_u32(np, "bus-width", &bus_width); + if (bus_width == 8) + pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT; + + of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles); + if (clk_delay_cycles > 0) { + pdata->clk_delay_sel = 1; + pdata->clk_delay_cycles = clk_delay_cycles; + } + + return pdata; +} +#else +static inline struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev) +{ + return NULL; +} +#endif + static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; @@ -128,6 +173,8 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; + const struct of_device_id *match; + int ret; struct clk *clk; @@ -156,6 +203,10 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; + match = of_match_device(of_match_ptr(sdhci_pxav2_of_match), &pdev->dev); + if (match) { + pdata = pxav2_get_mmc_pdata(dev); + } if (pdata) { if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { /* on-chip device */ @@ -218,6 +269,9 @@ static struct platform_driver sdhci_pxav2_driver = { .driver = { .name = "sdhci-pxav2", .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = sdhci_pxav2_of_match, +#endif .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_pxav2_probe, diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index f29695683556..07fe3834fe0b 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include + #include "sdhci.h" #include "sdhci-pltfm.h" @@ -164,6 +167,46 @@ static struct sdhci_ops pxav3_sdhci_ops = { .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, }; +#ifdef CONFIG_OF +static const struct of_device_id sdhci_pxav3_of_match[] = { + { + .compatible = "mrvl,pxav3-mmc", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match); + +static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) +{ + struct sdhci_pxa_platdata *pdata; + struct device_node *np = dev->of_node; + u32 bus_width; + u32 clk_delay_cycles; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_find_property(np, "non-removable", NULL)) + pdata->flags |= PXA_FLAG_CARD_PERMANENT; + + of_property_read_u32(np, "bus-width", &bus_width); + if (bus_width == 8) + pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT; + + of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles); + if (clk_delay_cycles > 0) + pdata->clk_delay_cycles = clk_delay_cycles; + + return pdata; +} +#else +static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) +{ + return NULL; +} +#endif + static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; @@ -171,6 +214,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; + const struct of_device_id *match; + int ret; struct clk *clk; @@ -202,6 +247,10 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; + match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev); + if (match) + pdata = pxav3_get_mmc_pdata(dev); + if (pdata) { if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { /* on-chip device */ @@ -263,6 +312,9 @@ static int __devexit sdhci_pxav3_remove(struct platform_device *pdev) static struct platform_driver sdhci_pxav3_driver = { .driver = { .name = "sdhci-pxav3", +#ifdef CONFIG_OF + .of_match_table = sdhci_pxav3_of_match, +#endif .owner = THIS_MODULE, .pm = SDHCI_PLTFM_PMOPS, }, -- cgit From 94c6cee91bebfc17596243b6a5f4fe910feec426 Mon Sep 17 00:00:00 2001 From: Girish K S Date: Tue, 12 Jun 2012 15:28:22 +0530 Subject: mmc: dw_mmc: Add check for IDMAC configuration In the current dwmmc driver there is support for selecting IDMAC from the menu config option. If the support for IDMAC is enabled in the menu config and the hardware configuration register's DMA_INTERFACE field is 0, the driver will still try to do the DMA initialization. The dw_mci_idmac_init function currently implemented returns only success indicating that the DMA initialization is always successful. This patch adds a check for existence of the DMA IP to allow the DMA initialization. Signed-off-by: Girish K S Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1ca5e72ceb65..5a600b3b3199 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -405,11 +405,23 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) static int dw_mci_idmac_init(struct dw_mci *host) { struct idmac_desc *p; - int i; + int i, dma_support; /* Number of descriptors in the ring buffer */ host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); + /* Check if Hardware Configuration Register has support for DMA */ + dma_support = (mci_readl(host, HCON) >> 16) & 0x3; + + if (!dma_support || dma_support > 2) { + dev_err(&host->dev, + "Host Controller does not support IDMA Tx.\n"); + host->dma_ops = NULL; + return -ENODEV; + } + + dev_info(&host->dev, "Using internal DMA controller.\n"); + /* Forward link the descriptor list */ for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1)); @@ -1876,7 +1888,6 @@ static void dw_mci_init_dma(struct dw_mci *host) /* Determine which DMA interface to use */ #ifdef CONFIG_MMC_DW_IDMAC host->dma_ops = &dw_mci_idmac_ops; - dev_info(&host->dev, "Using internal DMA controller.\n"); #endif if (!host->dma_ops) -- cgit From 7541ca98477862e2e9988c6c2ceadbdccefa9d77 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 Jun 2012 22:56:09 +0200 Subject: mmc: sh_mmcif: Support MMC_SLEEP_AWAKE command The MMC_SLEEP_AWAKE and SD_IO_SEND_OP_COND commands share the same opcode. SD_IO_SEND_OP_COND isn't supported by the SH MMCIF, but MMC_SLEEP_AWAKE is. Discriminate between the two commands using the command flags, and reject SD_IO_SEND_OP_COND only. Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 724b35e85a26..e32da1126e8d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -892,21 +892,15 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) switch (mrq->cmd->opcode) { /* MMCIF does not support SD/SDIO command */ - case SD_IO_SEND_OP_COND: + case MMC_SLEEP_AWAKE: /* = SD_IO_SEND_OP_COND (5) */ + case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ + if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR) + break; case MMC_APP_CMD: host->state = STATE_IDLE; mrq->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, mrq); return; - case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ - if (!mrq->data) { - /* send_if_cond cmd (not support) */ - host->state = STATE_IDLE; - mrq->cmd->error = -ETIMEDOUT; - mmc_request_done(mmc, mrq); - return; - } - break; default: break; } -- cgit From 6de707f200f73af7a58b58b3a5b956cff7b6e228 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 Jun 2012 23:29:35 +0200 Subject: mmc: tmio: Don't access hardware registers after stopping clocks The tmio_mmc_set_ios() function configures the MMC power, clock and bus width. When the mmc core requests the driver to power off the card, we inform runtime PM, that the controller can be suspended. This can lead to the MSTP clock being turned off. Writing to any 16-bit hardware registers with the MSTP clock off leads to timeouts and errors being printed to the kernel log. This can occur both when stopping the MMC clock and when configuring the bus width. To fix this, stop the MMC clock before calling put_runtime_pm(), and skip bus width configuration when power is off. Signed-off-by: Laurent Pinchart Tested-by: Kuninori Morimoto Tested-by: Simon Horman Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 7ffc489bed35..4f26edf07204 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -811,19 +811,21 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->set_pwr && ios->power_mode == MMC_POWER_OFF) host->set_pwr(host->pdev, 0); if (host->power) { + tmio_mmc_clk_stop(host); host->power = false; pm_runtime_put(dev); } - tmio_mmc_clk_stop(host); } - switch (ios->bus_width) { - case MMC_BUS_WIDTH_1: - sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); - break; - case MMC_BUS_WIDTH_4: - sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); - break; + if (host->power) { + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); + break; + case MMC_BUS_WIDTH_4: + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); + break; + } } /* Let things settle. delay taken from winCE driver */ -- cgit From 3bfe619dc365e439936ed6dae36506944c44c18c Mon Sep 17 00:00:00 2001 From: Jonathan Kliegman Date: Thu, 14 Jun 2012 13:31:55 -0400 Subject: mmc: dw_mmc: Fix null dma_ops access when use_dma is false host->dma_ops is not valid if host->usa dma is 0 so protect host->dma_ops reference in dw_mci_resume Signed-off-by: Jonathan Kliegman Acked-by: Jaehoon Chung Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 5a600b3b3199..72dc3cde646d 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2186,7 +2186,7 @@ int dw_mci_resume(struct dw_mci *host) return ret; } - if (host->dma_ops->init) + if (host->use_dma && host->dma_ops->init) host->dma_ops->init(host); /* Restore the old value at FIFOTH register */ -- cgit From e0337cc8b02fcb3f725746735db84d6d1b6b9196 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:30 +0200 Subject: mmc: tmio: don't needlessly enable interrupts during probing We don't have to force-enable MMC interrupts in our .probe() method, mmc_add_host() will trigger card detection, that will enable all the necessary interrupts. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 4f26edf07204..4318c1a74ba0 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -951,6 +951,17 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, _host->sdcard_irq_mask = sd_ctrl_read32(_host, CTL_IRQ_MASK); tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL); + + /* Unmask the IRQs we want to know about */ + if (!_host->chan_rx) + irq_mask |= TMIO_MASK_READOP; + if (!_host->chan_tx) + irq_mask |= TMIO_MASK_WRITEOP; + if (!_host->native_hotplug) + irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT); + + _host->sdcard_irq_mask &= ~irq_mask; + if (pdata->flags & TMIO_MMC_SDIO_IRQ) tmio_mmc_enable_sdio_irq(mmc, 0); @@ -968,16 +979,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, dev_pm_qos_expose_latency_limit(&pdev->dev, 100); - /* Unmask the IRQs we want to know about */ - if (!_host->chan_rx) - irq_mask |= TMIO_MASK_READOP; - if (!_host->chan_tx) - irq_mask |= TMIO_MASK_WRITEOP; - if (!_host->native_hotplug) - irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT); - - tmio_mmc_enable_mmc_irqs(_host, irq_mask); - if (pdata->flags & TMIO_MMC_USE_GPIO_CD) { ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio); if (ret < 0) { -- cgit From 8c102a964655b1a8df41b1f9e2355657471a45e3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:31 +0200 Subject: mmc: tmio: add callbacks to enable-update and disable the interface clock Every time the clock is enabled after possibly being disabled, we have to re-read its frequency and update our configuration. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 4318c1a74ba0..c6c0334a20e1 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -752,6 +752,22 @@ fail: mmc_request_done(mmc, mrq); } +static int tmio_mmc_clk_update(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + struct tmio_mmc_data *pdata = host->pdata; + int ret; + + if (!pdata->clk_enable) + return -ENOTSUPP; + + ret = pdata->clk_enable(host->pdev, &mmc->f_max); + if (!ret) + mmc->f_min = mmc->f_max / 512; + + return ret; +} + /* Set MMC clock / power. * Note: This controller uses a simple divider scheme therefore it cannot * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as @@ -798,6 +814,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) */ if (ios->power_mode == MMC_POWER_ON && ios->clock) { if (!host->power) { + tmio_mmc_clk_update(mmc); pm_runtime_get_sync(dev); host->power = true; } @@ -811,9 +828,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->set_pwr && ios->power_mode == MMC_POWER_OFF) host->set_pwr(host->pdev, 0); if (host->power) { + struct tmio_mmc_data *pdata = host->pdata; tmio_mmc_clk_stop(host); host->power = false; pm_runtime_put(dev); + if (pdata->clk_disable) + pdata->clk_disable(host->pdev); } } @@ -907,8 +927,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->ops = &tmio_mmc_ops; mmc->caps = MMC_CAP_4_BIT_DATA | pdata->capabilities; - mmc->f_max = pdata->hclk; - mmc->f_min = mmc->f_max / 512; mmc->max_segs = 32; mmc->max_blk_size = 512; mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) * @@ -930,6 +948,11 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, if (ret < 0) goto pm_disable; + if (tmio_mmc_clk_update(mmc) < 0) { + mmc->f_max = pdata->hclk; + mmc->f_min = mmc->f_max / 512; + } + /* * There are 4 different scenarios for the card detection: * 1) an external gpio irq handles the cd (best for power savings) @@ -975,7 +998,13 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, /* See if we also get DMA */ tmio_mmc_request_dma(_host, pdata); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (pdata->clk_disable) + pdata->clk_disable(pdev); + if (ret < 0) { + tmio_mmc_host_remove(_host); + return ret; + } dev_pm_qos_expose_latency_limit(&pdev->dev, 100); -- cgit From 56c492879318f43b1a7911675ff30fb0ede123b5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:32 +0200 Subject: mmc: sdhi: implement tmio-mmc clock enable-update and disable callbacks Instead of delivering one static clock frequency value, read from the hardware during probing, enable the tmio-mmc driver to re-read the frequency dynamically. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 934b68e9efc3..d8b167c8493a 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -39,6 +39,27 @@ struct sh_mobile_sdhi { struct tmio_mmc_dma dma_priv; }; +static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f) +{ + struct mmc_host *mmc = dev_get_drvdata(&pdev->dev); + struct tmio_mmc_host *host = mmc_priv(mmc); + struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); + int ret = clk_enable(priv->clk); + if (ret < 0) + return ret; + + *f = clk_get_rate(priv->clk); + return 0; +} + +static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev) +{ + struct mmc_host *mmc = dev_get_drvdata(&pdev->dev); + struct tmio_mmc_host *host = mmc_priv(mmc); + struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); + clk_disable(priv->clk); +} + static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state) { struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; @@ -132,9 +153,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) goto eclkget; } - mmc_data->hclk = clk_get_rate(priv->clk); mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; mmc_data->get_cd = sh_mobile_sdhi_get_cd; + mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; + mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; if (p) { mmc_data->flags = p->tmio_flags; -- cgit From b958a67c6bbe0a886bebc618aac7a059cdbab418 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:33 +0200 Subject: mmc: tmio: add regulator support Currently the TMIO MMC driver derives the OCR mask from the platform data and uses a platform callback to turn card power on and off. This patch adds regulator support to the driver. Signed-off-by: Guennadi Liakhovetski Cc: Liam Girdwood Cc: Mark Brown Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index c6c0334a20e1..b5c32b40ebc1 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -768,6 +768,18 @@ static int tmio_mmc_clk_update(struct mmc_host *mmc) return ret; } +static void tmio_mmc_set_power(struct tmio_mmc_host *host, struct mmc_ios *ios) +{ + struct mmc_host *mmc = host->mmc; + + if (host->set_pwr) + host->set_pwr(host->pdev, ios->power_mode != MMC_POWER_OFF); + if (!IS_ERR(mmc->supply.vmmc)) + /* Errors ignored... */ + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + ios->power_mode ? ios->vdd : 0); +} + /* Set MMC clock / power. * Note: This controller uses a simple divider scheme therefore it cannot * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as @@ -820,13 +832,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } tmio_mmc_set_clock(host, ios->clock); /* power up SD bus */ - if (host->set_pwr) - host->set_pwr(host->pdev, 1); + tmio_mmc_set_power(host, ios); /* start bus clock */ tmio_mmc_clk_start(host); } else if (ios->power_mode != MMC_POWER_UP) { - if (host->set_pwr && ios->power_mode == MMC_POWER_OFF) - host->set_pwr(host->pdev, 0); + if (ios->power_mode == MMC_POWER_OFF) + tmio_mmc_set_power(host, ios); if (host->power) { struct tmio_mmc_data *pdata = host->pdata; tmio_mmc_clk_stop(host); @@ -888,6 +899,19 @@ static const struct mmc_host_ops tmio_mmc_ops = { .enable_sdio_irq = tmio_mmc_enable_sdio_irq, }; +static void tmio_mmc_init_ocr(struct tmio_mmc_host *host) +{ + struct tmio_mmc_data *pdata = host->pdata; + struct mmc_host *mmc = host->mmc; + + mmc_regulator_get_supply(mmc); + + if (!mmc->ocr_avail) + mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34; + else if (pdata->ocr_mask) + dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); +} + int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, struct platform_device *pdev, struct tmio_mmc_data *pdata) @@ -933,10 +957,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->max_segs; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - if (pdata->ocr_mask) - mmc->ocr_avail = pdata->ocr_mask; - else - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + tmio_mmc_init_ocr(_host); _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD || mmc->caps & MMC_CAP_NEEDS_POLL || -- cgit From 71da63e767c393c0ebbd3c65df428df0ce42a16c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:34 +0200 Subject: mmc: sdhi: do not install dummy callbacks Currently the SDHI glue for the TMIO MMC driver installs dummy .get_cd() and .set_pwr() callbacks even if the platform didn't supply them. This is not necessary, since the TMIO MMC driver itself checks for NULL callbacks. This is also dubious if the platform provides a regulator for SD-card power switching. It is better to only install those callbacks, if they are really provided by the platform. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index d8b167c8493a..42f07fa6e043 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -64,18 +64,14 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state) { struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - if (p && p->set_pwr) - p->set_pwr(pdev, state); + p->set_pwr(pdev, state); } static int sh_mobile_sdhi_get_cd(struct platform_device *pdev) { struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - if (p && p->get_cd) - return p->get_cd(pdev); - else - return -ENOSYS; + return p->get_cd(pdev); } static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) @@ -153,8 +149,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) goto eclkget; } - mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; - mmc_data->get_cd = sh_mobile_sdhi_get_cd; + if (p->set_pwr) + mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; + if (p->get_cd) + mmc_data->get_cd = sh_mobile_sdhi_get_cd; mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; -- cgit From 944640d0fffb2ee2961a24c3747b1fc87c4157a8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:36 +0200 Subject: mmc: tmio: remove a duplicated comment line Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b5c32b40ebc1..b204012fe7a9 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -984,7 +984,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, * While we increment the runtime PM counter for all scenarios when * the mmc core activates us by calling an appropriate set_ios(), we * must additionally ensure that in case 2) the tmio mmc hardware stays - * additionally ensure that in case 2) the tmio mmc hardware stays * powered on during runtime for the card detection to work. */ if (_host->native_hotplug) -- cgit From e1aae2eb3f7c446a2680a3a0ccd05aa50521b4e2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 19 Apr 2012 16:15:52 +0200 Subject: mmc: sh_mmcif: simplify and use meaningful label names in error-handling A check for NULL platform data can be conveniently made in the very beginning of probing. Replace numbered error-handling labels in .probe() with meaningful names to make any future reorganisation simpler. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index e32da1126e8d..d6ffb0557537 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1241,11 +1241,16 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) int ret = 0, irq[2]; struct mmc_host *mmc; struct sh_mmcif_host *host; - struct sh_mmcif_plat_data *pd; + struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; struct resource *res; void __iomem *reg; char clk_name[8]; + if (!pd) { + dev_err(&pdev->dev, "sh_mmcif plat data error.\n"); + return -ENXIO; + } + irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); if (irq[0] < 0 || irq[1] < 0) { @@ -1262,16 +1267,11 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) dev_err(&pdev->dev, "ioremap error.\n"); return -ENOMEM; } - pd = pdev->dev.platform_data; - if (!pd) { - dev_err(&pdev->dev, "sh_mmcif plat data error.\n"); - ret = -ENXIO; - goto clean_up; - } + mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; - goto clean_up; + goto ealloch; } host = mmc_priv(mmc); host->mmc = mmc; @@ -1283,7 +1283,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) if (IS_ERR(host->hclk)) { dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); ret = PTR_ERR(host->hclk); - goto clean_up1; + goto eclkget; } clk_enable(host->hclk); host->clk = clk_get_rate(host->hclk); @@ -1313,7 +1313,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) ret = pm_runtime_resume(&pdev->dev); if (ret < 0) - goto clean_up2; + goto eresume; INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); @@ -1322,17 +1322,17 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); - goto clean_up3; + goto ereqirq0; } ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto clean_up4; + goto ereqirq1; } ret = mmc_add_host(mmc); if (ret < 0) - goto clean_up5; + goto emmcaddh; dev_pm_qos_expose_latency_limit(&pdev->dev, 100); @@ -1341,20 +1341,19 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); return ret; -clean_up5: +emmcaddh: free_irq(irq[1], host); -clean_up4: +ereqirq1: free_irq(irq[0], host); -clean_up3: +ereqirq0: pm_runtime_suspend(&pdev->dev); -clean_up2: +eresume: pm_runtime_disable(&pdev->dev); clk_disable(host->hclk); -clean_up1: +eclkget: mmc_free_host(mmc); -clean_up: - if (reg) - iounmap(reg); +ealloch: + iounmap(reg); return ret; } -- cgit From b289174ff70a591545a054d52ae081a75a73f085 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 19 Apr 2012 18:02:05 +0200 Subject: mmc: sh_mmcif: fix clock management Regardless of whether the MMC bus clock is the same as the PM clock on this specific interface, it has to be managed separately. Its proper management should also include enabling and disabling of the clock, whenever the interface is becoming active or going idle respectively. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 46 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index d6ffb0557537..6a93b0466854 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -942,6 +942,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->power) { pm_runtime_put(&host->pd->dev); + clk_disable(host->hclk); host->power = false; if (p->down_pwr && ios->power_mode == MMC_POWER_OFF) p->down_pwr(host->pd); @@ -954,6 +955,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!host->power) { if (p->set_pwr) p->set_pwr(host->pd, ios->power_mode); + clk_enable(host->hclk); pm_runtime_get_sync(&host->pd->dev); host->power = true; sh_mmcif_sync_reset(host); @@ -1278,22 +1280,11 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) host->addr = reg; host->timeout = 1000; - snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id); - host->hclk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(host->hclk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - ret = PTR_ERR(host->hclk); - goto eclkget; - } - clk_enable(host->hclk); - host->clk = clk_get_rate(host->hclk); host->pd = pdev; spin_lock_init(&host->lock); mmc->ops = &sh_mmcif_ops; - 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; @@ -1305,18 +1296,30 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size; mmc->max_seg_size = mmc->max_req_size; - sh_mmcif_sync_reset(host); platform_set_drvdata(pdev, host); pm_runtime_enable(&pdev->dev); host->power = false; + snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id); + host->hclk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(host->hclk)) { + ret = PTR_ERR(host->hclk); + dev_err(&pdev->dev, "cannot get clock \"%s\": %d\n", clk_name, ret); + goto eclkget; + } + clk_enable(host->hclk); + host->clk = clk_get_rate(host->hclk); + mmc->f_max = host->clk / 2; + mmc->f_min = host->clk / 512; + ret = pm_runtime_resume(&pdev->dev); if (ret < 0) goto eresume; INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); + sh_mmcif_sync_reset(host); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host); @@ -1330,6 +1333,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) goto ereqirq1; } + clk_disable(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) goto emmcaddh; @@ -1348,9 +1352,10 @@ ereqirq1: ereqirq0: pm_runtime_suspend(&pdev->dev); eresume: - pm_runtime_disable(&pdev->dev); clk_disable(host->hclk); + clk_put(host->hclk); eclkget: + pm_runtime_disable(&pdev->dev); mmc_free_host(mmc); ealloch: iounmap(reg); @@ -1363,6 +1368,7 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) int irq[2]; host->dying = true; + clk_enable(host->hclk); pm_runtime_get_sync(&pdev->dev); dev_pm_qos_hide_latency_limit(&pdev->dev); @@ -1388,9 +1394,9 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - clk_disable(host->hclk); mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); + clk_disable(host->hclk); pm_runtime_disable(&pdev->dev); return 0; @@ -1399,24 +1405,18 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int sh_mmcif_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sh_mmcif_host *host = platform_get_drvdata(pdev); + struct sh_mmcif_host *host = dev_get_drvdata(dev); int ret = mmc_suspend_host(host->mmc); - if (!ret) { + if (!ret) sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - clk_disable(host->hclk); - } return ret; } static int sh_mmcif_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sh_mmcif_host *host = platform_get_drvdata(pdev); - - clk_enable(host->hclk); + struct sh_mmcif_host *host = dev_get_drvdata(dev); return mmc_resume_host(host->mmc); } -- cgit From a6609267107ecc5598b79aa353036c1f57e7257e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 19 Apr 2012 18:02:50 +0200 Subject: mmc: sh_mmcif: re-read the clock frequency every time it is turned on With aggressive clock gating the clock can be disabled during interface inactivity. During this time its frequency can be changed by another its user. Therefore when the interface is activated again and the clock is re-enabled, its frequency has to be re-read. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6a93b0466854..3ffb92fc084e 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -910,6 +910,19 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) sh_mmcif_start_cmd(host, mrq); } +static int sh_mmcif_clk_update(struct sh_mmcif_host *host) +{ + int ret = clk_enable(host->hclk); + + if (!ret) { + host->clk = clk_get_rate(host->hclk); + host->mmc->f_max = host->clk / 2; + host->mmc->f_min = host->clk / 512; + } + + return ret; +} + static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sh_mmcif_host *host = mmc_priv(mmc); @@ -955,7 +968,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!host->power) { if (p->set_pwr) p->set_pwr(host->pd, ios->power_mode); - clk_enable(host->hclk); + sh_mmcif_clk_update(host); pm_runtime_get_sync(&host->pd->dev); host->power = true; sh_mmcif_sync_reset(host); @@ -1308,10 +1321,9 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) dev_err(&pdev->dev, "cannot get clock \"%s\": %d\n", clk_name, ret); goto eclkget; } - clk_enable(host->hclk); - host->clk = clk_get_rate(host->hclk); - mmc->f_max = host->clk / 2; - mmc->f_min = host->clk / 512; + ret = sh_mmcif_clk_update(host); + if (ret < 0) + goto eclkupdate; ret = pm_runtime_resume(&pdev->dev); if (ret < 0) @@ -1353,6 +1365,7 @@ ereqirq0: pm_runtime_suspend(&pdev->dev); eresume: clk_disable(host->hclk); +eclkupdate: clk_put(host->hclk); eclkget: pm_runtime_disable(&pdev->dev); -- cgit From e2ee996eaad64c149cb7cfd344789c7e0c1577dc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 20 Apr 2012 09:01:05 +0200 Subject: mmc: sh_mmcif: remove redundant .down_pwr() callback From the original version of sh_mmcif the .set_pwr() callback has only been used to turn the card's power on, and the .down_pwr() callback has been used to turn it off. .set_pwr() can be used for both these tasks, which is also how it is implemented by the only user of this API: the SH7724 ecovec board. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 3ffb92fc084e..468027665ede 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -957,8 +957,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) pm_runtime_put(&host->pd->dev); clk_disable(host->hclk); host->power = false; - if (p->down_pwr && ios->power_mode == MMC_POWER_OFF) - p->down_pwr(host->pd); + if (p->set_pwr && ios->power_mode == MMC_POWER_OFF) + p->set_pwr(host->pd, 0); } host->state = STATE_IDLE; return; -- cgit From 7d17baa05da6a2e64ee15011cdf4319bd3e0ff61 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 20 Apr 2012 18:27:13 +0200 Subject: mmc: sh_mmcif: add regulator support Add regulator support to the sh_mmcif driver, but also preserve the current power-callback. Also note, that the card power is not switched off during clock gating periods, hence there's no need to power it on every time the card is re-activated. Signed-off-by: Guennadi Liakhovetski Reviwed-by: Mark Brown Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 468027665ede..204bcedc2164 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -923,10 +923,22 @@ static int sh_mmcif_clk_update(struct sh_mmcif_host *host) return ret; } +static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) +{ + struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; + struct mmc_host *mmc = host->mmc; + + if (pd->set_pwr) + pd->set_pwr(host->pd, ios->power_mode != MMC_POWER_OFF); + if (!IS_ERR(mmc->supply.vmmc)) + /* Errors ignored... */ + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + ios->power_mode ? ios->vdd : 0); +} + static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sh_mmcif_host *host = mmc_priv(mmc); - struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; unsigned long flags; spin_lock_irqsave(&host->lock, flags); @@ -944,6 +956,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sh_mmcif_request_dma(host, host->pd->dev.platform_data); host->card_present = true; } + sh_mmcif_set_power(host, ios); } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) { /* clock stop */ sh_mmcif_clock_control(host, 0); @@ -957,8 +970,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) pm_runtime_put(&host->pd->dev); clk_disable(host->hclk); host->power = false; - if (p->set_pwr && ios->power_mode == MMC_POWER_OFF) - p->set_pwr(host->pd, 0); + if (ios->power_mode == MMC_POWER_OFF) + sh_mmcif_set_power(host, ios); } host->state = STATE_IDLE; return; @@ -966,8 +979,6 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->clock) { if (!host->power) { - if (p->set_pwr) - p->set_pwr(host->pd, ios->power_mode); sh_mmcif_clk_update(host); pm_runtime_get_sync(&host->pd->dev); host->power = true; @@ -1251,6 +1262,19 @@ static void mmcif_timeout_work(struct work_struct *work) mmc_request_done(host->mmc, mrq); } +static void sh_mmcif_init_ocr(struct sh_mmcif_host *host) +{ + struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; + struct mmc_host *mmc = host->mmc; + + mmc_regulator_get_supply(mmc); + + if (!mmc->ocr_avail) + mmc->ocr_avail = pd->ocr; + else if (pd->ocr) + dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); +} + static int __devinit sh_mmcif_probe(struct platform_device *pdev) { int ret = 0, irq[2]; @@ -1298,8 +1322,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) spin_lock_init(&host->lock); mmc->ops = &sh_mmcif_ops; - if (pd->ocr) - mmc->ocr_avail = pd->ocr; + sh_mmcif_init_ocr(host); + mmc->caps = MMC_CAP_MMC_HIGHSPEED; if (pd->caps) mmc->caps |= pd->caps; -- cgit From 02cb3221d5bb351ad9f7469453dcca7594a0fabf Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 23 May 2012 10:44:37 +0200 Subject: mmc: tmio: support caps2 flags Allow tmio mmc glue drivers to pass mmc_host::caps2 flags down to the mmc layer. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b204012fe7a9..f8df02111794 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -951,6 +951,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->ops = &tmio_mmc_ops; mmc->caps = MMC_CAP_4_BIT_DATA | pdata->capabilities; + mmc->caps2 = pdata->capabilities2; mmc->max_segs = 32; mmc->max_blk_size = 512; mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) * -- cgit From d7d8d500bc03891c4a86da49858c46e2db256581 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 23 May 2012 11:05:33 +0200 Subject: mmc: sh_mobile_sdhi: support caps2 flags Let SDHI platforms specify mmc_host::caps2 flags in their platform data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 42f07fa6e043..1e7c5c46201d 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -162,6 +162,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->write16_hook = sh_mobile_sdhi_write16_hook; mmc_data->ocr_mask = p->tmio_ocr_mask; mmc_data->capabilities |= p->tmio_caps; + mmc_data->capabilities2 |= p->tmio_caps2; mmc_data->cd_gpio = p->cd_gpio; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { -- cgit From 3071cafb7f6f9cbb52b1b7eb308c8b45cae0dcf8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 17:11:56 +0200 Subject: mmc: tmio: use generic GPIO CD and WP handlers The tmio-mmc driver is already using the generic GPIO CD handler in IRQ mode. This patch adds support for CD polling mode and also checks for availability of a WP GPIO. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index f8df02111794..0d8a9bbe30be 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -875,6 +875,9 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_data *pdata = host->pdata; + int ret = mmc_gpio_get_ro(mmc); + if (ret >= 0) + return ret; return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) || (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)); @@ -884,6 +887,9 @@ static int tmio_mmc_get_cd(struct mmc_host *mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_data *pdata = host->pdata; + int ret = mmc_gpio_get_cd(mmc); + if (ret >= 0) + return ret; if (!pdata->get_cd) return -ENOSYS; -- cgit From c7bb4487a3474c03986758595fcae1cfb771b3b0 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 18:03:43 +0200 Subject: mmc: sdhi: add OF support, make platform data optional Add primitive support for OF to the SDHI TMIO glue, which also makes it necessary to be able to run without platform data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 1e7c5c46201d..a842939e4655 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -133,12 +134,14 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) } mmc_data = &priv->mmc_data; - p->pdata = mmc_data; - if (p->init) { - ret = p->init(pdev, &sdhi_ops); - if (ret) - goto einit; + if (p) { + p->pdata = mmc_data; + if (p->init) { + ret = p->init(pdev, &sdhi_ops); + if (ret) + goto einit; + } } snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); @@ -149,10 +152,6 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) goto eclkget; } - if (p->set_pwr) - mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; - if (p->get_cd) - mmc_data->get_cd = sh_mobile_sdhi_get_cd; mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; @@ -164,6 +163,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->capabilities |= p->tmio_caps; mmc_data->capabilities2 |= p->tmio_caps2; mmc_data->cd_gpio = p->cd_gpio; + if (p->set_pwr) + mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; + if (p->get_cd) + mmc_data->get_cd = sh_mobile_sdhi_get_cd; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { priv->param_tx.slave_id = p->dma_slave_tx; @@ -269,7 +272,7 @@ eirq_card_detect: eprobe: clk_put(priv->clk); eclkget: - if (p->cleanup) + if (p && p->cleanup) p->cleanup(pdev); einit: kfree(priv); @@ -284,7 +287,8 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; int i = 0, irq; - p->pdata = NULL; + if (p) + p->pdata = NULL; tmio_mmc_host_remove(host); @@ -297,7 +301,7 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) clk_put(priv->clk); - if (p->cleanup) + if (p && p->cleanup) p->cleanup(pdev); kfree(priv); @@ -312,11 +316,18 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { .runtime_resume = tmio_mmc_host_runtime_resume, }; +static const struct of_device_id sh_mobile_sdhi_of_match[] = { + { .compatible = "renesas,shmobile-sdhi" }, + { } +}; +MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); + static struct platform_driver sh_mobile_sdhi_driver = { .driver = { .name = "sh_mobile_sdhi", .owner = THIS_MODULE, .pm = &tmio_mmc_dev_pm_ops, + .of_match_table = sh_mobile_sdhi_of_match, }, .probe = sh_mobile_sdhi_probe, .remove = __devexit_p(sh_mobile_sdhi_remove), -- cgit From bf68a812f06ca40bccfa2e792055141f2c3948c7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 18:18:16 +0200 Subject: mmc: sh-mmcif: add OF support, make platform data optional Add primitive OF support to the sh-mmcif driver, which also makes it necessary to be able to run without platform data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 204bcedc2164..68b31f7c290b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -384,6 +385,9 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host, struct sh_dmae_slave *tx, *rx; host->dma_active = false; + if (!pdata) + return; + /* We can only either use DMA for both Tx and Rx or not use it at all */ if (pdata->dma) { dev_warn(&host->pd->dev, @@ -444,13 +448,14 @@ static void sh_mmcif_release_dma(struct sh_mmcif_host *host) static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) { struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + bool sup_pclk = p ? p->sup_pclk : false; sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR); if (!clk) return; - if (p->sup_pclk && clk == host->clk) + if (sup_pclk && clk == host->clk) sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); else sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & @@ -928,7 +933,7 @@ static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; struct mmc_host *mmc = host->mmc; - if (pd->set_pwr) + if (pd && pd->set_pwr) pd->set_pwr(host->pd, ios->power_mode != MMC_POWER_OFF); if (!IS_ERR(mmc->supply.vmmc)) /* Errors ignored... */ @@ -996,7 +1001,7 @@ static int sh_mmcif_get_cd(struct mmc_host *mmc) struct sh_mmcif_host *host = mmc_priv(mmc); struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; - if (!p->get_cd) + if (!p || !p->get_cd) return -ENOSYS; else return p->get_cd(host->pd); @@ -1269,6 +1274,9 @@ static void sh_mmcif_init_ocr(struct sh_mmcif_host *host) mmc_regulator_get_supply(mmc); + if (!pd) + return; + if (!mmc->ocr_avail) mmc->ocr_avail = pd->ocr; else if (pd->ocr) @@ -1285,11 +1293,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) void __iomem *reg; char clk_name[8]; - if (!pd) { - dev_err(&pdev->dev, "sh_mmcif plat data error.\n"); - return -ENXIO; - } - irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); if (irq[0] < 0 || irq[1] < 0) { @@ -1325,7 +1328,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_init_ocr(host); mmc->caps = MMC_CAP_MMC_HIGHSPEED; - if (pd->caps) + if (pd && pd->caps) mmc->caps |= pd->caps; mmc->max_segs = 32; mmc->max_blk_size = 512; @@ -1462,6 +1465,12 @@ static int sh_mmcif_resume(struct device *dev) #define sh_mmcif_resume NULL #endif /* CONFIG_PM */ +static const struct of_device_id mmcif_of_match[] = { + { .compatible = "renesas,sh-mmcif" }, + { } +}; +MODULE_DEVICE_TABLE(of, mmcif_of_match); + static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { .suspend = sh_mmcif_suspend, .resume = sh_mmcif_resume, @@ -1473,6 +1482,8 @@ static struct platform_driver sh_mmcif_driver = { .driver = { .name = DRIVER_NAME, .pm = &sh_mmcif_dev_pm_ops, + .owner = THIS_MODULE, + .of_match_table = mmcif_of_match, }, }; -- cgit From e480606ad43bb72fd82a9bd99cdcf21829a6e9c0 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 14 Jun 2012 14:24:35 +0200 Subject: mmc: sh_mmcif: support generic card-detection Extend the sh_mmcif driver to support GPIO card detection, provided by the slot function module. The original .get_cd() platform callback is also preserved for now. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 68b31f7c290b..b2af7136cd27 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -1000,6 +1001,10 @@ static int sh_mmcif_get_cd(struct mmc_host *mmc) { struct sh_mmcif_host *host = mmc_priv(mmc); struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + int ret = mmc_gpio_get_cd(mmc); + + if (ret >= 0) + return ret; if (!p || !p->get_cd) return -ENOSYS; @@ -1372,6 +1377,12 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) goto ereqirq1; } + if (pd && pd->use_cd_gpio) { + ret = mmc_gpio_request_cd(mmc, pd->cd_gpio); + if (ret < 0) + goto erqcd; + } + clk_disable(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) @@ -1385,6 +1396,9 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) return ret; emmcaddh: + if (pd && pd->use_cd_gpio) + mmc_gpio_free_cd(mmc); +erqcd: free_irq(irq[1], host); ereqirq1: free_irq(irq[0], host); @@ -1405,6 +1419,7 @@ ealloch: static int __devexit sh_mmcif_remove(struct platform_device *pdev) { struct sh_mmcif_host *host = platform_get_drvdata(pdev); + struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; int irq[2]; host->dying = true; @@ -1413,6 +1428,9 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) dev_pm_qos_hide_latency_limit(&pdev->dev); + if (pd && pd->use_cd_gpio) + mmc_gpio_free_cd(host->mmc); + mmc_remove_host(host->mmc); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); -- cgit From bad37e1ac6b1a73ed30702b24a45c27c4f53aada Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Sun, 27 May 2012 18:36:44 -0700 Subject: mmc: sdhci: if MAX_CURRENT is 0, try getting current from regulator The sd host controller spec indicates the the MAX_CURRENT value may be returned as 0. In this case other methods need to be used to return the current. If 0 is returned and there is a regulator, ask the regulator for how much current is available. Signed-off-by: Philip Rakity Signed-off-by: Mark F. Brown Reviewed-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 28 ++++++++++++++++++++++------ drivers/mmc/host/sdhci.h | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f4b8b4db3a9a..a0853d03b330 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2837,6 +2837,13 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_RETUNING_MODE_SHIFT; ocr_avail = 0; + + host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); + if (IS_ERR(host->vmmc)) { + pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); + host->vmmc = NULL; + } + /* * According to SD Host Controller spec v3.00, if the Host System * can afford more than 150mA, Host Driver should set XPC to 1. Also @@ -2845,6 +2852,21 @@ int sdhci_add_host(struct sdhci_host *host) * value. */ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT); + if (!max_current_caps && host->vmmc) { + u32 curr = regulator_get_current_limit(host->vmmc); + if (curr > 0) { + + /* convert to SDHCI_MAX_CURRENT format */ + curr = curr/1000; /* convert to mA */ + curr = curr/SDHCI_MAX_CURRENT_MULTIPLIER; + + curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT); + max_current_caps = + (curr << SDHCI_MAX_CURRENT_330_SHIFT) | + (curr << SDHCI_MAX_CURRENT_300_SHIFT) | + (curr << SDHCI_MAX_CURRENT_180_SHIFT); + } + } if (caps[0] & SDHCI_CAN_VDD_330) { int max_current_330; @@ -2995,12 +3017,6 @@ int sdhci_add_host(struct sdhci_host *host) if (ret) goto untasklet; - host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); - if (IS_ERR(host->vmmc)) { - pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); - host->vmmc = NULL; - } - sdhci_init(host, 0); #ifdef CONFIG_MMC_DEBUG diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index f761f23d2a28..97653ea8942b 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -205,6 +205,7 @@ #define SDHCI_CAPABILITIES_1 0x44 #define SDHCI_MAX_CURRENT 0x48 +#define SDHCI_MAX_CURRENT_LIMIT 0xFF #define SDHCI_MAX_CURRENT_330_MASK 0x0000FF #define SDHCI_MAX_CURRENT_330_SHIFT 0 #define SDHCI_MAX_CURRENT_300_MASK 0x00FF00 -- cgit From 0aa6770000bafa65c17cf44b6619d328d4fc79b3 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Sun, 27 May 2012 18:36:33 -0700 Subject: mmc: sdhci: only set 200mA support for 1.8v if 200mA is available max_current_caps can return 0 if not available from the sd controller. If no regulator is present or the regulator specifies a current less then 200ma, we no longer still set the 200mA caps bit anyway. Signed-off-by: Philip Rakity Reviewed-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a0853d03b330..8f61f8d6e0ca 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2914,7 +2914,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_MAX_CURRENT_600; else if (max_current_180 >= 400) mmc->caps |= MMC_CAP_MAX_CURRENT_400; - else + else if (max_current_180 >= 200) mmc->caps |= MMC_CAP_MAX_CURRENT_200; } -- cgit From 68737043bb155b42d54943d25153b901fd16e535 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 8 Jun 2012 12:26:13 -0700 Subject: mmc: only support voltage (vdd) that regulator agrees with If we are using a regulator the SD Host Controller and the regulator should agree about the voltages supported. Use the common subset that is supported. Signed-off-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8f61f8d6e0ca..f76736b50bc7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2844,6 +2844,23 @@ int sdhci_add_host(struct sdhci_host *host) host->vmmc = NULL; } +#ifdef CONFIG_REGULATOR + if (host->vmmc) { + ret = regulator_is_supported_voltage(host->vmmc, 3300000, + 3300000); + if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330))) + caps[0] &= ~SDHCI_CAN_VDD_330; + ret = regulator_is_supported_voltage(host->vmmc, 3000000, + 3000000); + if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300))) + caps[0] &= ~SDHCI_CAN_VDD_300; + ret = regulator_is_supported_voltage(host->vmmc, 1800000, + 1800000); + if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180))) + caps[0] &= ~SDHCI_CAN_VDD_180; + } +#endif /* CONFIG_REGULATOR */ + /* * According to SD Host Controller spec v3.00, if the Host System * can afford more than 150mA, Host Driver should set XPC to 1. Also -- cgit From 4dc5a79f1350718ac31e4ab87eebce112b117a34 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 26 Jun 2012 16:38:57 +0800 Subject: mmc: mxs-mmc: enable regulator for mmc slot If bootloader or platform initialization code does not enable the power supply to mmc slot, we need to do it in mmc driver. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 277161d279b8..2247c50f9869 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -707,6 +707,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) struct pinctrl *pinctrl; int ret = 0, irq_err, irq_dma; dma_cap_mask_t mask; + struct regulator *reg_vmmc; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -747,6 +748,16 @@ static int mxs_mmc_probe(struct platform_device *pdev) host->mmc = mmc; host->sdio_irq_en = 0; + reg_vmmc = devm_regulator_get(&pdev->dev, "vmmc"); + if (!IS_ERR(reg_vmmc)) { + ret = regulator_enable(reg_vmmc); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable vmmc regulator: %d\n", ret); + goto out_mmc_free; + } + } + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(pinctrl)) { ret = PTR_ERR(pinctrl); -- cgit From bd6a8c30faf19b4e7abace93fb89efd6d72607ec Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Wed, 27 Jun 2012 21:49:27 -0700 Subject: mmc: sdhci: Allow caps[1] to be set via SDHCI_QUIRK_MISSING_CAPS Currently only the capability_0 register can be set if SDHCI_QUIRK_MISSING_CAPS is defined. This is a problem when the capability_1 register also needs changing. Use the quirk SDHCI_QUIRK_MISSING_CAPS to allow both registers to be set. Redefining caps[1] is useful when the board design does not support 1.8v vccq so UHS modes are not available. The code that calls sdhci_add_host can then detect this condition and adjust the caps so the UHS mode will not be attempted on UHS cards. Signed-off-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f76736b50bc7..206b52fec443 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2584,7 +2584,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host); int sdhci_add_host(struct sdhci_host *host) { struct mmc_host *mmc; - u32 caps[2]; + u32 caps[2] = {0, 0}; u32 max_current_caps; unsigned int ocr_avail; int ret; @@ -2614,8 +2614,10 @@ int sdhci_add_host(struct sdhci_host *host) caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : sdhci_readl(host, SDHCI_CAPABILITIES); - caps[1] = (host->version >= SDHCI_SPEC_300) ? - sdhci_readl(host, SDHCI_CAPABILITIES_1) : 0; + if (host->version >= SDHCI_SPEC_300) + caps[1] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? + host->caps1 : + sdhci_readl(host, SDHCI_CAPABILITIES_1); if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; -- cgit From b9929f0eb466089e14389a7d467196c4e179dc6a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 30 Jun 2012 14:24:22 +0200 Subject: mmc: s3cmci: Convert s3cmci driver to gpiolib API The s3c2410_gpio* calls are obsolete and have been scheduled for removal since several kernel releases. Remove them and use common gpiolib API. This patch is a prerequisite for removal of the S3C24XX SoC specific arch/arm/plat-samsung/include/gpio-fns.h header. Tested on Micro2440-SDK. Signed-off-by: Sylwester Nawrocki Signed-off-by: Chris Ball --- drivers/mmc/host/s3cmci.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index c3622a69f432..bd5a5cce122c 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -26,7 +26,6 @@ #include #include -#include #include @@ -1237,12 +1236,9 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_ON: case MMC_POWER_UP: - s3c2410_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPE5_SDCLK); - s3c2410_gpio_cfgpin(S3C2410_GPE(6), S3C2410_GPE6_SDCMD); - s3c2410_gpio_cfgpin(S3C2410_GPE(7), S3C2410_GPE7_SDDAT0); - s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1); - s3c2410_gpio_cfgpin(S3C2410_GPE(9), S3C2410_GPE9_SDDAT2); - s3c2410_gpio_cfgpin(S3C2410_GPE(10), S3C2410_GPE10_SDDAT3); + /* Configure GPE5...GPE10 pins in SD mode */ + s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2), + S3C_GPIO_PULL_NONE); if (host->pdata->set_power) host->pdata->set_power(ios->power_mode, ios->vdd); -- cgit From 0fc81ee3102efedd4571e28372d2242c694c67ec Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 2 Jul 2012 14:26:15 +0100 Subject: mmc: sdhci: Report failure reasons for all cases in sdhci_add_host() For most error conditions sdhci_add_host() will print a diagnostic message indicating why it failed but there are a few cases where this does not happen. Add error messages in these cases to aid diagnosis. Signed-off-by: Mark Brown Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 206b52fec443..101f9d43196f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3033,8 +3033,11 @@ int sdhci_add_host(struct sdhci_host *host) ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, mmc_hostname(mmc), host); - if (ret) + if (ret) { + pr_err("%s: Failed to request IRQ %d: %d\n", + mmc_hostname(mmc), host->irq, ret); goto untasklet; + } sdhci_init(host, 0); @@ -3051,8 +3054,11 @@ int sdhci_add_host(struct sdhci_host *host) host->led.brightness_set = sdhci_led_control; ret = led_classdev_register(mmc_dev(mmc), &host->led); - if (ret) + if (ret) { + pr_err("%s: Failed to register LED device: %d\n", + mmc_hostname(mmc), ret); goto reset; + } #endif mmiowb(); -- cgit From 55fc05b7414274f17795cd0e8a3b1546f3649d5e Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 3 Jul 2012 23:13:39 +0100 Subject: mmc: sdhci-pci: CaFe has broken card detection At http://dev.laptop.org/ticket/11980 we have determined that the Marvell CaFe SDHCI controller reports bad card presence during resume. It reports that no card is present even when it is. This is a regression -- resume worked back around 2.6.37. Around 400ms after resuming, a "card inserted" interrupt is generated, at which point it starts reporting presence. Work around this hardware oddity by setting the SDHCI_QUIRK_BROKEN_CARD_DETECTION flag. Thanks to Chris Ball for helping with diagnosis. Signed-off-by: Daniel Drake [stable@: please apply to 3.0+] Cc: Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 69ef0beae104..504da715a41a 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -157,6 +157,7 @@ static const struct sdhci_pci_fixes sdhci_ene_714 = { static const struct sdhci_pci_fixes sdhci_cafe = { .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | SDHCI_QUIRK_NO_BUSY_IRQ | + SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, }; -- cgit From 473b095a72a95ba719905b1f2e82cd18d099a427 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 3 Jul 2012 17:27:49 +0800 Subject: mmc: sdhci: fix incorrect command used in tuning For SD hosts using retuning mode 1, when retuning timer expired, it will need to do retuning in sdhci_request before processing the actual request. But the retuning command is fixed: cmd19 for SD card and cmd21 for eMMC card, so we can't use the original request's command to do the tuning. And since the tuning command depends on the card type attached to the host, we will need to know the card type to use the correct tuning command. Signed-off-by: Aaron Lu Reviewed-by: Philip Rakity Cc: stable [3.3+] Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 101f9d43196f..12015151fab9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -27,6 +27,7 @@ #include #include +#include #include "sdhci.h" @@ -1245,6 +1246,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) struct sdhci_host *host; bool present; unsigned long flags; + u32 tuning_opcode; host = mmc_priv(mmc); @@ -1292,8 +1294,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) */ if ((host->flags & SDHCI_NEEDS_RETUNING) && !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { + /* eMMC uses cmd21 while sd and sdio use cmd19 */ + tuning_opcode = mmc->card->type == MMC_TYPE_MMC ? + MMC_SEND_TUNING_BLOCK_HS200 : + MMC_SEND_TUNING_BLOCK; spin_unlock_irqrestore(&host->lock, flags); - sdhci_execute_tuning(mmc, mrq->cmd->opcode); + sdhci_execute_tuning(mmc, tuning_opcode); spin_lock_irqsave(&host->lock, flags); /* Restore original mmc_request structure */ -- cgit From b67c6b411dcf17199e1dfdf18da8b1c6caf73e6e Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Fri, 29 Jun 2012 16:17:31 +0800 Subject: mmc: sdhci: restore host settings when card is removed Some of the host settings are affected by different cards inserted, e.g. when an UHS-I card is inserted, the SDHCI_NEEDS_RETUING flag might be set when the tuning timer expired and host's max_blk_count will be reduced to make sure the data transfer for a command does not exceed 4MiB to meet the retuning mode 1's requirement. When the card is removed, we should restore the original setting of the host since we can't be sure the next card being inserted will still be an UHS-I card that needs tuning. The original setting include its max_blk_count and no set of the flag of SDHCI_NEEDS_RETUNING. Signed-off-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 12015151fab9..b0a5629dea3e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -245,6 +245,18 @@ static void sdhci_init(struct sdhci_host *host, int soft) static void sdhci_reinit(struct sdhci_host *host) { sdhci_init(host, 0); + /* + * Retuning stuffs are affected by different cards inserted and only + * applicable to UHS-I cards. So reset these fields to their initial + * value when card is removed. + */ + if (host->version >= SDHCI_SPEC_300 && host->tuning_count && + host->tuning_mode == SDHCI_TUNING_MODE_1) { + del_timer_sync(&host->tuning_timer); + host->flags &= ~SDHCI_NEEDS_RETUNING; + host->mmc->max_blk_count = + (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; + } sdhci_enable_card_detection(host); } -- cgit From 973905feab85416784f36cc94b868392fd465ef4 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 4 Jul 2012 13:29:09 +0800 Subject: mmc: sdhci: Introduce new flag SDHCI_USING_RETUNING_TIMER Add a new flag of SDHCI_USING_RETUNING_TIMER to represent if the host is using a retuning timer for the card inserted. This flag is set when the host does tuning the first time for the card and the host's retuning mode is 1. This flag is used afterwards whenever needs to decide if the host is currently using a retuning timer. This flag is cleared when the card is removed in sdhci_reinit. The set/clear of the flag and the start/stop of the retuning timer is associated with the card's init/remove time, so there is no need to touch it when the host is to be removed as at that time the card should have already been removed. Signed-off-by: Aaron Lu Reviewed-by: Girish K S Reviewed-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b0a5629dea3e..3ec418212894 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -250,8 +250,9 @@ static void sdhci_reinit(struct sdhci_host *host) * applicable to UHS-I cards. So reset these fields to their initial * value when card is removed. */ - if (host->version >= SDHCI_SPEC_300 && host->tuning_count && - host->tuning_mode == SDHCI_TUNING_MODE_1) { + if (host->flags & SDHCI_USING_RETUNING_TIMER) { + host->flags &= ~SDHCI_USING_RETUNING_TIMER; + del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; host->mmc->max_blk_count = @@ -1873,6 +1874,7 @@ out: */ if (!(host->flags & SDHCI_NEEDS_RETUNING) && host->tuning_count && (host->tuning_mode == SDHCI_TUNING_MODE_1)) { + host->flags |= SDHCI_USING_RETUNING_TIMER; mod_timer(&host->tuning_timer, jiffies + host->tuning_count * HZ); /* Tuning mode 1 limits the maximum data length to 4MB */ @@ -1890,10 +1892,10 @@ out: * try tuning again at a later time, when the re-tuning timer expires. * So for these controllers, we return 0. Since there might be other * controllers who do not have this capability, we return error for - * them. + * them. SDHCI_USING_RETUNING_TIMER means the host is currently using + * a retuning timer to do the retuning for the card. */ - if (err && host->tuning_count && - host->tuning_mode == SDHCI_TUNING_MODE_1) + if (err && (host->flags & SDHCI_USING_RETUNING_TIMER)) err = 0; sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); @@ -2400,7 +2402,6 @@ out: int sdhci_suspend_host(struct sdhci_host *host) { int ret; - bool has_tuning_timer; if (host->ops->platform_suspend) host->ops->platform_suspend(host); @@ -2408,16 +2409,14 @@ int sdhci_suspend_host(struct sdhci_host *host) sdhci_disable_card_detection(host); /* Disable tuning since we are suspending */ - has_tuning_timer = host->version >= SDHCI_SPEC_300 && - host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1; - if (has_tuning_timer) { + if (host->flags & SDHCI_USING_RETUNING_TIMER) { del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; } ret = mmc_suspend_host(host->mmc); if (ret) { - if (has_tuning_timer) { + if (host->flags & SDHCI_USING_RETUNING_TIMER) { host->flags |= SDHCI_NEEDS_RETUNING; mod_timer(&host->tuning_timer, jiffies + host->tuning_count * HZ); @@ -2468,8 +2467,7 @@ int sdhci_resume_host(struct sdhci_host *host) host->ops->platform_resume(host); /* Set the re-tuning expiration flag */ - if ((host->version >= SDHCI_SPEC_300) && host->tuning_count && - (host->tuning_mode == SDHCI_TUNING_MODE_1)) + if (host->flags & SDHCI_USING_RETUNING_TIMER) host->flags |= SDHCI_NEEDS_RETUNING; return ret; @@ -2508,8 +2506,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) int ret = 0; /* Disable tuning since we are suspending */ - if (host->version >= SDHCI_SPEC_300 && - host->tuning_mode == SDHCI_TUNING_MODE_1) { + if (host->flags & SDHCI_USING_RETUNING_TIMER) { del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; } @@ -2550,8 +2547,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) sdhci_do_enable_preset_value(host, true); /* Set the re-tuning expiration flag */ - if ((host->version >= SDHCI_SPEC_300) && host->tuning_count && - (host->tuning_mode == SDHCI_TUNING_MODE_1)) + if (host->flags & SDHCI_USING_RETUNING_TIMER) host->flags |= SDHCI_NEEDS_RETUNING; spin_lock_irqsave(&host->lock, flags); @@ -3140,8 +3136,6 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) free_irq(host->irq, host); del_timer_sync(&host->timer); - if (host->version >= SDHCI_SPEC_300) - del_timer_sync(&host->tuning_timer); tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); -- cgit From eb6d5ae1c918cefbfe06f6f96e1a75f2179d82ce Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 5 Jul 2012 22:06:13 +0100 Subject: mmc: sdhci: poll for card even when card is logically unremovable The Marvell CaFe is now marked as having bad card detection to fix a problem during system resume. Now on the OLPC XO-1 we are facing the issue that the card is marked as logically unremovable (via MMC_UNSAFE_RESUME), which means that mmc_card_is_removable considers the card non-removable. The existing code logic decides not to poll for card presence in this case, and card detection is also disabled because of the quirk being set. This means that no SD cards are detected when inserted after boot. Refine the logic to enable card presence polling in the case when a card is logically unremovable, only avoiding the poll in the case when the card is physically non-removable (denoted with MMC_CAP_NONREMOVABLE). Signed-off-by: Daniel Drake Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3ec418212894..bc1de70360c8 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2795,7 +2795,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && - mmc_card_is_removable(mmc)) + !(host->mmc->caps & MMC_CAP_NONREMOVABLE)) mmc->caps |= MMC_CAP_NEEDS_POLL; /* Any UHS-I mode in caps implies SDR12 and SDR25 support. */ -- cgit From 41b4e9a194f69b1c945038c559ea407a6b383e81 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 6 Jul 2012 11:58:33 +0200 Subject: mmc: atmel-mci: fix incorrect setting of host->data to NULL Setting host->data to NULL is incorrect sequence in STATE_SENDING_STOP state of FSM: This early setting leads to the skip of dma_unmap_sg() in atmci_dma_cleanup() which is a bug. Idea taken from dw_mmc by Seungwon Jeon. Signed-off-by: Nicolas Ferre Cc: Seungwon Jeon Acked-by: Ludovic Desroches Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index f2c115e06438..84c4ac4d5192 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1685,7 +1685,6 @@ static void atmci_tasklet_func(unsigned long priv) dev_dbg(&host->pdev->dev, "FSM: cmd ready\n"); host->cmd = NULL; - host->data = NULL; data->bytes_xfered = data->blocks * data->blksz; data->error = 0; atmci_command_complete(host, mrq->stop); @@ -1699,6 +1698,7 @@ static void atmci_tasklet_func(unsigned long priv) atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY); state = STATE_WAITING_NOTBUSY; } + host->data = NULL; break; case STATE_END_REQUEST: -- cgit From 8a4de07e05e7bedc894c2de3b3b04673d6d840ec Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 6 Jul 2012 12:11:51 +0200 Subject: mmc: atmel-mci: modify CLKDIV displaying in debugfs Modify clock division displaying in debugfs for matching the new CLKDIV,CLKODD user interface arrangement. Is using the has_odd_clk_div property to choose the proper format. Signed-off-by: Nicolas Ferre Acked-by: Ludovic Desroches Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 84c4ac4d5192..322412cec4ee 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -391,11 +391,17 @@ static int atmci_regs_show(struct seq_file *s, void *v) clk_disable(host->mck); spin_unlock_bh(&host->lock); - seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n", + seq_printf(s, "MR:\t0x%08x%s%s ", buf[ATMCI_MR / 4], buf[ATMCI_MR / 4] & ATMCI_MR_RDPROOF ? " RDPROOF" : "", - buf[ATMCI_MR / 4] & ATMCI_MR_WRPROOF ? " WRPROOF" : "", - buf[ATMCI_MR / 4] & 0xff); + buf[ATMCI_MR / 4] & ATMCI_MR_WRPROOF ? " WRPROOF" : ""); + if (host->caps.has_odd_clk_div) + seq_printf(s, "{CLKDIV,CLKODD}=%u\n", + ((buf[ATMCI_MR / 4] & 0xff) << 1) + | ((buf[ATMCI_MR / 4] >> 16) & 1)); + else + seq_printf(s, "CLKDIV=%u\n", + (buf[ATMCI_MR / 4] & 0xff)); seq_printf(s, "DTOR:\t0x%08x\n", buf[ATMCI_DTOR / 4]); seq_printf(s, "SDCR:\t0x%08x\n", buf[ATMCI_SDCR / 4]); seq_printf(s, "ARGR:\t0x%08x\n", buf[ATMCI_ARGR / 4]); -- cgit From 38cfc2f71cec9508bdd3a11e576738c2182418d0 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Wed, 27 Jun 2012 09:15:00 -0700 Subject: mmc: sdhci: When a UHS switch fails, cycle power if regulator is used Power needs to be removed from the card when switching to 1.8v fails. If a regulator is used to control vmmc we need to turn the regulator off and then back on otherwise power will not be removed from the card. Signed-off-by: Philip Rakity Reviewed-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index bc1de70360c8..d9966568143b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1682,11 +1682,15 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, pwr = sdhci_readb(host, SDHCI_POWER_CONTROL); pwr &= ~SDHCI_POWER_ON; sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + if (host->vmmc) + regulator_disable(host->vmmc); /* Wait for 1ms as per the spec */ usleep_range(1000, 1500); pwr |= SDHCI_POWER_ON; sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + if (host->vmmc) + regulator_enable(host->vmmc); pr_info(DRIVER_NAME ": Switching to 1.8V signalling " "voltage failed, retrying with S18R set to 0\n"); -- cgit From 94c18149451e9ae91a669bc33370273327d7da81 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 27 Jun 2012 14:19:54 +0530 Subject: mmc: omap_hsmmc: add clk_prepare and clk_unprepare In preparation for OMAP moving to the Common Clock Framework (CCF) add clk_prepare() and clk_unprepare() for the hsmmc clocks. Signed-off-by: Rajendra Nayak Acked-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 389a3eedfc24..a9227a7a9d69 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1089,7 +1089,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) /* Disable the clocks */ pm_runtime_put_sync(host->dev); if (host->dbclk) - clk_disable(host->dbclk); + clk_disable_unprepare(host->dbclk); /* Turn the power off */ ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); @@ -1100,7 +1100,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) vdd); pm_runtime_get_sync(host->dev); if (host->dbclk) - clk_enable(host->dbclk); + clk_prepare_enable(host->dbclk); if (ret != 0) goto err; @@ -1899,7 +1899,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) if (IS_ERR(host->dbclk)) { dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n"); host->dbclk = NULL; - } else if (clk_enable(host->dbclk) != 0) { + } else if (clk_prepare_enable(host->dbclk) != 0) { dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n"); clk_put(host->dbclk); host->dbclk = NULL; @@ -2023,7 +2023,7 @@ err_irq: pm_runtime_disable(host->dev); clk_put(host->fclk); if (host->dbclk) { - clk_disable(host->dbclk); + clk_disable_unprepare(host->dbclk); clk_put(host->dbclk); } err1: @@ -2058,7 +2058,7 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_disable(host->dev); clk_put(host->fclk); if (host->dbclk) { - clk_disable(host->dbclk); + clk_disable_unprepare(host->dbclk); clk_put(host->dbclk); } @@ -2116,7 +2116,7 @@ static int omap_hsmmc_suspend(struct device *dev) } if (host->dbclk) - clk_disable(host->dbclk); + clk_disable_unprepare(host->dbclk); err: pm_runtime_put_sync(host->dev); return ret; @@ -2137,7 +2137,7 @@ static int omap_hsmmc_resume(struct device *dev) pm_runtime_get_sync(host->dev); if (host->dbclk) - clk_enable(host->dbclk); + clk_prepare_enable(host->dbclk); if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) omap_hsmmc_conf_bus_power(host); -- cgit From 55c4665ea0a42fd6427826bfce96eb4b0389262a Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 4 Jul 2012 13:31:48 +0800 Subject: mmc: sd: Fix sd current limit setting Host has different current capabilities at different voltages, we need to record these settings seperately. The defined voltages are 1.8/3.0/3.3. For other voltages, we do not touch current limit setting. Before we set the current limit for the sd card, find out the host's operating voltage first and then find out the current capabilities of the host at that voltage to set the current limit. Signed-off-by: Aaron Lu Reviewed-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d9966568143b..9a11dc39921c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2906,53 +2906,28 @@ int sdhci_add_host(struct sdhci_host *host) } if (caps[0] & SDHCI_CAN_VDD_330) { - int max_current_330; - ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; - max_current_330 = ((max_current_caps & + mmc->max_current_330 = ((max_current_caps & SDHCI_MAX_CURRENT_330_MASK) >> SDHCI_MAX_CURRENT_330_SHIFT) * SDHCI_MAX_CURRENT_MULTIPLIER; - - if (max_current_330 > 150) - mmc->caps |= MMC_CAP_SET_XPC_330; } if (caps[0] & SDHCI_CAN_VDD_300) { - int max_current_300; - ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; - max_current_300 = ((max_current_caps & + mmc->max_current_300 = ((max_current_caps & SDHCI_MAX_CURRENT_300_MASK) >> SDHCI_MAX_CURRENT_300_SHIFT) * SDHCI_MAX_CURRENT_MULTIPLIER; - - if (max_current_300 > 150) - mmc->caps |= MMC_CAP_SET_XPC_300; } if (caps[0] & SDHCI_CAN_VDD_180) { - int max_current_180; - ocr_avail |= MMC_VDD_165_195; - max_current_180 = ((max_current_caps & + mmc->max_current_180 = ((max_current_caps & SDHCI_MAX_CURRENT_180_MASK) >> SDHCI_MAX_CURRENT_180_SHIFT) * SDHCI_MAX_CURRENT_MULTIPLIER; - - if (max_current_180 > 150) - mmc->caps |= MMC_CAP_SET_XPC_180; - - /* Maximum current capabilities of the host at 1.8V */ - if (max_current_180 >= 800) - mmc->caps |= MMC_CAP_MAX_CURRENT_800; - else if (max_current_180 >= 600) - mmc->caps |= MMC_CAP_MAX_CURRENT_600; - else if (max_current_180 >= 400) - mmc->caps |= MMC_CAP_MAX_CURRENT_400; - else if (max_current_180 >= 200) - mmc->caps |= MMC_CAP_MAX_CURRENT_200; } mmc->ocr_avail = ocr_avail; -- cgit From 9a0985b78d70209db1e0d870f7be80272a0e7642 Mon Sep 17 00:00:00 2001 From: Wilson Callan Date: Thu, 19 Jul 2012 02:49:16 -0400 Subject: mmc: esdhc: Fix DMA_MASK to not break mx25 DMA access Patch to not set reserved bits in i.MX25 PROCTL register. DMA stops working if those bits get set. Signed-off-by: Wilson Callan Acked-by: Sascha Hauer Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ebbe984e5d00..e23f8134591c 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -299,6 +299,8 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; u32 new_val; switch (reg) { @@ -315,8 +317,11 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) SDHCI_CTRL_D3CD); /* ensure the endianess */ new_val |= ESDHC_HOST_CONTROL_LE; - /* DMA mode bits are shifted */ - new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; + /* bits 8&9 are reserved on mx25 */ + if (!is_imx25_esdhc(imx_data)) { + /* DMA mode bits are shifted */ + new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; + } esdhc_clrset_le(host, 0xffff, new_val, reg); return; -- cgit From b6e76f10afbdc510df9288ab4df3e94a89c20909 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 19 Jul 2012 11:11:39 -0400 Subject: mmc: mxs-mmc: Add wp-inverted property The write-protect GPIO is inverted on some boards. Handle such case. Signed-off-by: Marek Vasut Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 2247c50f9869..a51f9309ffbb 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -164,16 +164,23 @@ struct mxs_mmc_host { spinlock_t lock; int sdio_irq_en; int wp_gpio; + bool wp_inverted; }; static int mxs_mmc_get_ro(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); + int ret; if (!gpio_is_valid(host->wp_gpio)) return -EINVAL; - return gpio_get_value(host->wp_gpio); + ret = gpio_get_value(host->wp_gpio); + + if (host->wp_inverted) + ret = !ret; + + return ret; } static int mxs_mmc_get_cd(struct mmc_host *mmc) @@ -708,6 +715,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) int ret = 0, irq_err, irq_dma; dma_cap_mask_t mask; struct regulator *reg_vmmc; + enum of_gpio_flags flags; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -796,7 +804,10 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_4_BIT_DATA; else if (bus_width == 8) mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; - host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); + host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, + &flags); + if (flags & OF_GPIO_ACTIVE_LOW) + host->wp_inverted = 1; } else { if (pdata->flags & SLOTF_8_BIT_CAPABLE) mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; -- cgit From 9c17d08ca183b92c9cfab6537069914a5e0e7e81 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 10 Jul 2012 16:40:56 -0700 Subject: mmc: omap_hsmmc: ensure probe returns error upon resource failure If platform_get_resource_by_name() fails, driver probe is aborted an should return an error so the driver is not bound to the device. However, in the current error path of platform_get_resource_by_name(), probe returns zero since the return value (ret) is not properly set. With a zero return value, the driver core assumes probe was successful and will bind the driver to the device. Fix this by ensuring that probe returns an error code in this failure path. Signed-off-by: Kevin Hilman Acked-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index a9227a7a9d69..bc28627af66b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1931,6 +1931,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); if (!res) { dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n"); + ret = -ENXIO; goto err_irq; } host->dma_line_tx = res->start; @@ -1938,6 +1939,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); if (!res) { dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n"); + ret = -ENXIO; goto err_irq; } host->dma_line_rx = res->start; -- cgit From a9ca1d5477a89c1833214a01463190f7bd77d8a9 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Sat, 21 Jul 2012 19:26:19 -0400 Subject: mmc: sdhci-dove: Add SDHCI_QUIRK_NO_HISPD_BIT The sdio controller on dove doesn't have a bit to indicate high-speed. With the quirk set it fixes accessing high-speed sdcards. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-dove.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 177f697b5835..9c8536881796 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -66,7 +66,8 @@ static struct sdhci_pltfm_data sdhci_dove_pdata = { .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | - SDHCI_QUIRK_FORCE_DMA, + SDHCI_QUIRK_FORCE_DMA | + SDHCI_QUIRK_NO_HISPD_BIT, }; static int __devinit sdhci_dove_probe(struct platform_device *pdev) -- cgit From 30b87c60e9cbfcdfce22aa652fc1e5d061fcb733 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Thu, 5 Jul 2012 12:14:01 +0200 Subject: mmc: sdhci-dove: Prepare for common clock framework As mach-dove is moving towards common clock framework prepare the sdhci driver to grab its clock. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-dove.c | 48 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 9c8536881796..a6e53a1ebb08 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -20,11 +20,17 @@ */ #include +#include +#include #include #include #include "sdhci-pltfm.h" +struct sdhci_dove_priv { + struct clk *clk; +}; + static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) { u16 ret; @@ -72,11 +78,51 @@ static struct sdhci_pltfm_data sdhci_dove_pdata = { static int __devinit sdhci_dove_probe(struct platform_device *pdev) { - return sdhci_pltfm_register(pdev, &sdhci_dove_pdata); + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_dove_priv *priv; + int ret; + + ret = sdhci_pltfm_register(pdev, &sdhci_dove_pdata); + if (ret) + goto sdhci_dove_register_fail; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv), + GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "unable to allocate private data"); + ret = -ENOMEM; + goto sdhci_dove_allocate_fail; + } + + host = platform_get_drvdata(pdev); + pltfm_host = sdhci_priv(host); + pltfm_host->priv = priv; + + priv->clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(priv->clk)) + clk_prepare_enable(priv->clk); + return 0; + +sdhci_dove_allocate_fail: + sdhci_pltfm_unregister(pdev); +sdhci_dove_register_fail: + return ret; } static int __devexit sdhci_dove_remove(struct platform_device *pdev) { + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_dove_priv *priv = pltfm_host->priv; + + if (priv->clk) { + if (!IS_ERR(priv->clk)) { + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + } + devm_kfree(&pdev->dev, priv->clk); + } return sdhci_pltfm_unregister(pdev); } -- cgit