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/sh_mmcif.c') 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 v1.2.3 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/sh_mmcif.c') 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 v1.2.3 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/sh_mmcif.c') 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 v1.2.3 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/sh_mmcif.c') 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 v1.2.3 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/sh_mmcif.c') 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 v1.2.3 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/sh_mmcif.c') 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 v1.2.3 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/sh_mmcif.c') 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 v1.2.3 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/sh_mmcif.c') 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 v1.2.3 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/sh_mmcif.c') 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 v1.2.3 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 ++++++++++++++++++ include/linux/mmc/sh_mmcif.h | 2 ++ 2 files changed, 20 insertions(+) (limited to 'drivers/mmc/host/sh_mmcif.c') 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); diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index 05f0e3db1c12..c2f73cbb4d5c 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -44,6 +44,8 @@ struct sh_mmcif_plat_data { struct sh_mmcif_dma *dma; /* Deprecated. Instead */ unsigned int slave_id_tx; /* use embedded slave_id_[tr]x */ unsigned int slave_id_rx; + bool use_cd_gpio : 1; + unsigned int cd_gpio; u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ unsigned long caps; u32 ocr; -- cgit v1.2.3