diff options
Diffstat (limited to 'drivers/mmc/host/davinci_mmc.c')
| -rw-r--r-- | drivers/mmc/host/davinci_mmc.c | 190 |
1 files changed, 80 insertions, 110 deletions
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 9e68c3645e22..42b0118a45a8 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1,44 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * davinci_mmc.c - TI DaVinci MMC/SD/SDIO driver * * Copyright (C) 2006 Texas Instruments. * Original author: Purushotam Kumar * Copyright (C) 2009 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/platform_device.h> #include <linux/clk.h> -#include <linux/err.h> #include <linux/cpufreq.h> -#include <linux/mmc/host.h> -#include <linux/io.h> -#include <linux/irq.h> #include <linux/delay.h> -#include <linux/dmaengine.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/mmc/host.h> #include <linux/mmc/mmc.h> -#include <linux/of.h> -#include <linux/of_device.h> #include <linux/mmc/slot-gpio.h> -#include <linux/interrupt.h> - +#include <linux/module.h> #include <linux/platform_data/mmc-davinci.h> +#include <linux/platform_device.h> +#include <linux/property.h> /* * Register Definitions @@ -160,17 +145,17 @@ #define MAX_NR_SG 16 static unsigned rw_threshold = 32; -module_param(rw_threshold, uint, S_IRUGO); +module_param(rw_threshold, uint, 0444); MODULE_PARM_DESC(rw_threshold, "Read/Write threshold. Default = 32"); static unsigned poll_threshold = 128; -module_param(poll_threshold, uint, S_IRUGO); +module_param(poll_threshold, uint, 0444); MODULE_PARM_DESC(poll_threshold, "Polling transaction size threshold. Default = 128"); static unsigned poll_loopcount = 32; -module_param(poll_loopcount, uint, S_IRUGO); +module_param(poll_loopcount, uint, 0444); MODULE_PARM_DESC(poll_loopcount, "Maximum polling loop count. Default = 32"); @@ -194,12 +179,6 @@ struct mmc_davinci_host { #define DAVINCI_MMC_DATADIR_WRITE 2 unsigned char data_dir; - /* buffer is used during PIO of one scatterlist segment, and - * is updated along with buffer_bytes_left. bytes_left applies - * to all N blocks of the PIO transfer. - */ - u8 *buffer; - u32 buffer_bytes_left; u32 bytes_left; struct dma_chan *dma_tx; @@ -210,8 +189,8 @@ struct mmc_davinci_host { bool active_request; /* For PIO we walk scatterlists one segment at a time. */ + struct sg_mapping_iter sg_miter; unsigned int sg_len; - struct scatterlist *sg; /* Version of the MMC/SD controller */ u8 version; @@ -227,30 +206,25 @@ struct mmc_davinci_host { static irqreturn_t mmc_davinci_irq(int irq, void *dev_id); /* PIO only */ -static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host) -{ - host->buffer_bytes_left = sg_dma_len(host->sg); - host->buffer = sg_virt(host->sg); - if (host->buffer_bytes_left > host->bytes_left) - host->buffer_bytes_left = host->bytes_left; -} - static void davinci_fifo_data_trans(struct mmc_davinci_host *host, unsigned int n) { + struct sg_mapping_iter *sgm = &host->sg_miter; u8 *p; unsigned int i; - if (host->buffer_bytes_left == 0) { - host->sg = sg_next(host->data->sg); - mmc_davinci_sg_to_buf(host); + /* + * By adjusting sgm->consumed this will give a pointer to the + * current index into the sgm. + */ + if (!sg_miter_next(sgm)) { + dev_err(mmc_dev(host->mmc), "ran out of sglist prematurely\n"); + return; } + p = sgm->addr; - p = host->buffer; - if (n > host->buffer_bytes_left) - n = host->buffer_bytes_left; - host->buffer_bytes_left -= n; - host->bytes_left -= n; + if (n > sgm->length) + n = sgm->length; /* NOTE: we never transfer more than rw_threshold bytes * to/from the fifo here; there's no I/O overlap. @@ -275,7 +249,9 @@ static void davinci_fifo_data_trans(struct mmc_davinci_host *host, p = p + (n & 3); } } - host->buffer = p; + + sgm->consumed = n; + host->bytes_left -= n; } static void mmc_davinci_start_command(struct mmc_davinci_host *host, @@ -303,7 +279,7 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host, default: s = ", (R? response)"; break; - }; s; })); + } s; })); host->cmd = cmd; switch (mmc_resp_type(cmd)) { @@ -313,7 +289,7 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host, * then it's harmless for us to allow it. */ cmd_reg |= MMCCMD_BSYEXP; - /* FALLTHROUGH */ + fallthrough; case MMC_RSP_R1: /* 48 bits, CRC */ cmd_reg |= MMCCMD_RSPFMT_R1456; break; @@ -531,6 +507,7 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) int fifo_lev = (rw_threshold == 32) ? MMCFIFOCTL_FIFOLEV : 0; int timeout; struct mmc_data *data = req->data; + unsigned int flags = SG_MITER_ATOMIC; /* Used from IRQ */ if (host->version == MMC_CTLR_VERSION_2) fifo_lev = (rw_threshold == 64) ? MMCFIFOCTL_FIFOLEV : 0; @@ -559,12 +536,14 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) /* Configure the FIFO */ if (data->flags & MMC_DATA_WRITE) { + flags |= SG_MITER_FROM_SG; host->data_dir = DAVINCI_MMC_DATADIR_WRITE; writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST, host->base + DAVINCI_MMCFIFOCTL); writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR, host->base + DAVINCI_MMCFIFOCTL); } else { + flags |= SG_MITER_TO_SG; host->data_dir = DAVINCI_MMC_DATADIR_READ; writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST, host->base + DAVINCI_MMCFIFOCTL); @@ -572,7 +551,6 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) host->base + DAVINCI_MMCFIFOCTL); } - host->buffer = NULL; host->bytes_left = data->blocks * data->blksz; /* For now we try to use DMA whenever we won't need partial FIFO @@ -590,8 +568,7 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) } else { /* Revert to CPU Copy */ host->sg_len = data->sg_len; - host->sg = host->data->sg; - mmc_davinci_sg_to_buf(host); + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); } } @@ -611,7 +588,7 @@ static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req) cpu_relax(); } if (mmcst1 & MMCST1_BUSY) { - dev_err(mmc_dev(host->mmc), "still BUSY? bad ... \n"); + dev_err(mmc_dev(host->mmc), "still BUSY? bad ...\n"); req->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, req); return; @@ -857,6 +834,8 @@ davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data) { mmc_davinci_reset_ctrl(host, 1); mmc_davinci_reset_ctrl(host, 0); + if (!host->do_dma) + sg_miter_stop(&host->sg_miter); } static irqreturn_t mmc_davinci_sdio_irq(int irq, void *dev_id) @@ -933,11 +912,13 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) if (qstatus & MMCST0_DATDNE) { /* All blocks sent/received, and CRC checks passed */ if (data != NULL) { - if ((host->do_dma == 0) && (host->bytes_left > 0)) { - /* if datasize < rw_threshold - * no RX ints are generated - */ - davinci_fifo_data_trans(host, host->bytes_left); + if (!host->do_dma) { + if (host->bytes_left > 0) + /* if datasize < rw_threshold + * no RX ints are generated + */ + davinci_fifo_data_trans(host, host->bytes_left); + sg_miter_stop(&host->sg_miter); } end_transfer = 1; data->bytes_xfered = data->blocks * data->blksz; @@ -1009,7 +990,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) if (qstatus & MMCST0_RSPDNE) { /* End of command phase */ - end_command = (int) host->cmd; + end_command = host->cmd ? 1 : 0; } if (end_command) @@ -1117,7 +1098,7 @@ static inline void mmc_davinci_cpufreq_deregister(struct mmc_davinci_host *host) { } #endif -static void __init init_mmcsd_host(struct mmc_davinci_host *host) +static void init_mmcsd_host(struct mmc_davinci_host *host) { mmc_davinci_reset_ctrl(host, 1); @@ -1187,13 +1168,13 @@ static int mmc_davinci_parse_pdata(struct mmc_host *mmc) mmc->caps |= pdata->caps; /* Register a cd gpio, if there is not one, enable polling */ - ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL); + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0); if (ret == -EPROBE_DEFER) return ret; else if (ret) mmc->caps |= MMC_CAP_NEEDS_POLL; - ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL); + ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0); if (ret == -EPROBE_DEFER) return ret; @@ -1202,11 +1183,10 @@ static int mmc_davinci_parse_pdata(struct mmc_host *mmc) static int davinci_mmcsd_probe(struct platform_device *pdev) { - const struct of_device_id *match; struct mmc_davinci_host *host = NULL; struct mmc_host *mmc = NULL; struct resource *r, *mem = NULL; - int ret, irq; + int ret, irq, bus_width; size_t mem_size; const struct platform_device_id *id_entry; @@ -1223,7 +1203,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) if (!mem) return -EBUSY; - mmc = mmc_alloc_host(sizeof(struct mmc_davinci_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1232,30 +1212,25 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) host->mem_res = mem; host->base = devm_ioremap(&pdev->dev, mem->start, mem_size); - if (!host->base) { - ret = -ENOMEM; - goto ioremap_fail; - } + if (!host->base) + return -ENOMEM; host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - goto clk_get_fail; - } + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + ret = clk_prepare_enable(host->clk); if (ret) - goto clk_prepare_enable_fail; + return ret; host->mmc_input_clk = clk_get_rate(host->clk); - match = of_match_device(davinci_mmc_dt_ids, &pdev->dev); - if (match) { - pdev->id_entry = match->data; + pdev->id_entry = device_get_match_data(&pdev->dev); + if (pdev->id_entry) { ret = mmc_of_parse(mmc); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "could not parse of data: %d\n", ret); + dev_err_probe(&pdev->dev, ret, + "could not parse of data\n"); goto parse_fail; } } else { @@ -1273,7 +1248,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) host->use_dma = use_dma; host->mmc_irq = irq; - host->sdio_irq = platform_get_irq(pdev, 1); + host->sdio_irq = platform_get_irq_optional(pdev, 1); if (host->use_dma) { ret = davinci_acquire_dma_channels(host); @@ -1338,9 +1313,14 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) rename_region(mem, mmc_hostname(mmc)); + if (mmc->caps & MMC_CAP_8_BIT_DATA) + bus_width = 8; + else if (mmc->caps & MMC_CAP_4_BIT_DATA) + bus_width = 4; + else + bus_width = 1; dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n", - host->use_dma ? "DMA" : "PIO", - (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1); + host->use_dma ? "DMA" : "PIO", bus_width); return 0; @@ -1353,15 +1333,11 @@ cpu_freq_fail: parse_fail: dma_probe_defer: clk_disable_unprepare(host->clk); -clk_prepare_enable_fail: -clk_get_fail: -ioremap_fail: - mmc_free_host(mmc); return ret; } -static int __exit davinci_mmcsd_remove(struct platform_device *pdev) +static void davinci_mmcsd_remove(struct platform_device *pdev) { struct mmc_davinci_host *host = platform_get_drvdata(pdev); @@ -1369,12 +1345,8 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev) mmc_davinci_cpufreq_deregister(host); davinci_release_dma_channels(host); clk_disable_unprepare(host->clk); - mmc_free_host(host->mmc); - - return 0; } -#ifdef CONFIG_PM static int davinci_mmcsd_suspend(struct device *dev) { struct mmc_davinci_host *host = dev_get_drvdata(dev); @@ -1389,31 +1361,29 @@ static int davinci_mmcsd_suspend(struct device *dev) static int davinci_mmcsd_resume(struct device *dev) { struct mmc_davinci_host *host = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(host->clk); + if (ret) + return ret; - clk_enable(host->clk); mmc_davinci_reset_ctrl(host, 0); return 0; } -static const struct dev_pm_ops davinci_mmcsd_pm = { - .suspend = davinci_mmcsd_suspend, - .resume = davinci_mmcsd_resume, -}; - -#define davinci_mmcsd_pm_ops (&davinci_mmcsd_pm) -#else -#define davinci_mmcsd_pm_ops NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(davinci_mmcsd_pm_ops, + davinci_mmcsd_suspend, davinci_mmcsd_resume); static struct platform_driver davinci_mmcsd_driver = { .driver = { .name = "davinci_mmc", - .pm = davinci_mmcsd_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .pm = pm_sleep_ptr(&davinci_mmcsd_pm_ops), .of_match_table = davinci_mmc_dt_ids, }, .probe = davinci_mmcsd_probe, - .remove = __exit_p(davinci_mmcsd_remove), + .remove = davinci_mmcsd_remove, .id_table = davinci_mmc_devtype, }; |
