diff options
Diffstat (limited to 'drivers/mmc/host/mxcmmc.c')
| -rw-r--r-- | drivers/mmc/host/mxcmmc.c | 218 |
1 files changed, 105 insertions, 113 deletions
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index fb3ca8296273..c405cfb8b269 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/mmc/host/mxcmmc.c - Freescale i.MX MMCI driver * @@ -10,17 +11,13 @@ * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com> * * derived from pxamci.c by Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include <linux/module.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/platform_device.h> +#include <linux/highmem.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/blkdev.h> @@ -30,21 +27,18 @@ #include <linux/delay.h> #include <linux/clk.h> #include <linux/io.h> -#include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/dmaengine.h> #include <linux/types.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_dma.h> -#include <linux/of_gpio.h> #include <linux/mmc/slot-gpio.h> #include <asm/dma.h> #include <asm/irq.h> #include <linux/platform_data/mmc-mxcmmc.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #define DRIVER_NAME "mxc-mmc" #define MXCMCI_TIMEOUT_MS 10000 @@ -162,32 +156,16 @@ struct mxcmci_host { enum mxcmci_type devtype; }; -static const struct platform_device_id mxcmci_devtype[] = { - { - .name = "imx21-mmc", - .driver_data = IMX21_MMC, - }, { - .name = "imx31-mmc", - .driver_data = IMX31_MMC, - }, { - .name = "mpc512x-sdhc", - .driver_data = MPC512X_MMC, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, mxcmci_devtype); - static const struct of_device_id mxcmci_of_match[] = { { .compatible = "fsl,imx21-mmc", - .data = &mxcmci_devtype[IMX21_MMC], + .data = (void *) IMX21_MMC, }, { .compatible = "fsl,imx31-mmc", - .data = &mxcmci_devtype[IMX31_MMC], + .data = (void *) IMX31_MMC, }, { .compatible = "fsl,mpc5121-sdhc", - .data = &mxcmci_devtype[MPC512X_MMC], + .data = (void *) MPC512X_MMC, }, { /* sentinel */ } @@ -288,11 +266,18 @@ static inline void buffer_swap32(u32 *buf, int len) static void mxcmci_swap_buffers(struct mmc_data *data) { - struct scatterlist *sg; - int i; + struct sg_mapping_iter sgm; + u32 *buf; + + sg_miter_start(&sgm, data->sg, data->sg_len, + SG_MITER_TO_SG | SG_MITER_FROM_SG); + + while (sg_miter_next(&sgm)) { + buf = sgm.addr; + buffer_swap32(buf, sgm.length); + } - for_each_sg(data->sg, sg, data->sg_len, i) - buffer_swap32(sg_virt(sg), sg->length); + sg_miter_stop(&sgm); } #else static inline void mxcmci_swap_buffers(struct mmc_data *data) {} @@ -367,7 +352,7 @@ static void mxcmci_dma_callback(void *data) struct mxcmci_host *host = data; u32 stat; - del_timer(&host->watchdog); + timer_delete(&host->watchdog); stat = mxcmci_readl(host, MMC_REG_STATUS); @@ -548,10 +533,9 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask) } while (1); } -static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes) +static int mxcmci_pull(struct mxcmci_host *host, u32 *buf, int bytes) { unsigned int stat; - u32 *buf = _buf; while (bytes > 3) { stat = mxcmci_poll_status(host, @@ -577,10 +561,9 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes) return 0; } -static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes) +static int mxcmci_push(struct mxcmci_host *host, u32 *buf, int bytes) { unsigned int stat; - u32 *buf = _buf; while (bytes > 3) { stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); @@ -608,31 +591,39 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes) static int mxcmci_transfer_data(struct mxcmci_host *host) { struct mmc_data *data = host->req->data; - struct scatterlist *sg; - int stat, i; + struct sg_mapping_iter sgm; + int stat; + u32 *buf; host->data = data; host->datasize = 0; + sg_miter_start(&sgm, data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? SG_MITER_TO_SG : SG_MITER_FROM_SG); if (data->flags & MMC_DATA_READ) { - for_each_sg(data->sg, sg, data->sg_len, i) { - stat = mxcmci_pull(host, sg_virt(sg), sg->length); + while (sg_miter_next(&sgm)) { + buf = sgm.addr; + stat = mxcmci_pull(host, buf, sgm.length); if (stat) - return stat; - host->datasize += sg->length; + goto transfer_error; + host->datasize += sgm.length; } } else { - for_each_sg(data->sg, sg, data->sg_len, i) { - stat = mxcmci_push(host, sg_virt(sg), sg->length); + while (sg_miter_next(&sgm)) { + buf = sgm.addr; + stat = mxcmci_push(host, buf, sgm.length); if (stat) - return stat; - host->datasize += sg->length; + goto transfer_error; + host->datasize += sgm.length; } stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE); if (stat) - return stat; + goto transfer_error; } - return 0; + +transfer_error: + sg_miter_stop(&sgm); + return stat; } static void mxcmci_datawork(struct work_struct *work) @@ -681,6 +672,9 @@ static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat) spin_unlock_irqrestore(&host->lock, flags); + if (data_error) + return; + mxcmci_read_response(host, stat); host->cmd = NULL; @@ -716,7 +710,6 @@ static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat) static irqreturn_t mxcmci_irq(int irq, void *devid) { struct mxcmci_host *host = devid; - unsigned long flags; bool sdio_irq; u32 stat; @@ -728,9 +721,9 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat); - spin_lock_irqsave(&host->lock, flags); + spin_lock(&host->lock); sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio; - spin_unlock_irqrestore(&host->lock, flags); + spin_unlock(&host->lock); if (mxcmci_use_dma(host) && (stat & (STATUS_WRITE_OP_DONE))) mxcmci_writel(host, STATUS_WRITE_OP_DONE, MMC_REG_STATUS); @@ -744,7 +737,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) mxcmci_cmd_done(host, stat); if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) { - del_timer(&host->watchdog); + timer_delete(&host->watchdog); mxcmci_data_done(host, stat); } @@ -942,7 +935,7 @@ static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card) * One way to prevent this is to only allow 1-bit transfers. */ - if (is_imx31_mmc(mxcmci) && card->type == MMC_TYPE_SDIO) + if (is_imx31_mmc(mxcmci) && mmc_card_sdio(card)) host->caps &= ~MMC_CAP_4_BIT_DATA; else host->caps |= MMC_CAP_4_BIT_DATA; @@ -960,10 +953,9 @@ static bool filter(struct dma_chan *chan, void *param) return true; } -static void mxcmci_watchdog(unsigned long data) +static void mxcmci_watchdog(struct timer_list *t) { - struct mmc_host *mmc = (struct mmc_host *)data; - struct mxcmci_host *host = mmc_priv(mmc); + struct mxcmci_host *host = timer_container_of(host, t, watchdog); struct mmc_request *req = host->req; unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS); @@ -1003,37 +995,31 @@ static int mxcmci_probe(struct platform_device *pdev) struct mxcmci_host *host; struct resource *res; int ret = 0, irq; - bool dat3_card_detect = false; + bool dat3_card_detect; dma_cap_mask_t mask; - const struct of_device_id *of_id; struct imxmmc_platform_data *pdata = pdev->dev.platform_data; pr_info("i.MX/MPC512x SDHC driver\n"); - of_id = of_match_device(mxcmci_of_match, &pdev->dev); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (irq < 0) - return -EINVAL; + return irq; - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; host = mmc_priv(mmc); - host->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto out_free; - } + host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(host->base)) + return PTR_ERR(host->base); host->phys_base = res->start; ret = mmc_of_parse(mmc); if (ret) - goto out_free; + return ret; mmc->ops = &mxcmci_ops; /* For devicetree parsing, the bus width is read from devicetree */ @@ -1048,12 +1034,7 @@ static int mxcmci_probe(struct platform_device *pdev) mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - if (of_id) { - const struct platform_device_id *id_entry = of_id->data; - host->devtype = id_entry->driver_data; - } else { - host->devtype = pdev->id_entry->driver_data; - } + host->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev); /* adjust max_segs after devtype detection */ if (!is_mpc512x_mmc(host)) @@ -1065,13 +1046,13 @@ static int mxcmci_probe(struct platform_device *pdev) if (pdata) dat3_card_detect = pdata->dat3_card_detect; - else if (mmc_card_is_removable(mmc) - && !of_property_read_bool(pdev->dev.of_node, "cd-gpios")) - dat3_card_detect = true; + else + dat3_card_detect = mmc_card_is_removable(mmc) && + !of_property_present(pdev->dev.of_node, "cd-gpios"); ret = mmc_regulator_get_supply(mmc); - if (ret == -EPROBE_DEFER) - goto out_free; + if (ret) + return ret; if (!mmc->ocr_avail) { if (pdata && pdata->ocr_avail) @@ -1087,19 +1068,20 @@ static int mxcmci_probe(struct platform_device *pdev) host->default_irq_mask = 0; host->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(host->clk_ipg)) { - ret = PTR_ERR(host->clk_ipg); - goto out_free; - } + if (IS_ERR(host->clk_ipg)) + return PTR_ERR(host->clk_ipg); host->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(host->clk_per)) { - ret = PTR_ERR(host->clk_per); - goto out_free; - } + if (IS_ERR(host->clk_per)) + return PTR_ERR(host->clk_per); + + ret = clk_prepare_enable(host->clk_per); + if (ret) + return ret; - clk_prepare_enable(host->clk_per); - clk_prepare_enable(host->clk_ipg); + ret = clk_prepare_enable(host->clk_ipg); + if (ret) + goto out_clk_per_put; mxcmci_softreset(host); @@ -1120,7 +1102,16 @@ static int mxcmci_probe(struct platform_device *pdev) mxcmci_writel(host, host->default_irq_mask, MMC_REG_INT_CNTR); if (!host->pdata) { - host->dma = dma_request_slave_channel(&pdev->dev, "rx-tx"); + host->dma = dma_request_chan(&pdev->dev, "rx-tx"); + if (IS_ERR(host->dma)) { + if (PTR_ERR(host->dma) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto out_clk_put; + } + + /* Ignore errors to fall back to PIO mode */ + host->dma = NULL; + } } else { res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res) { @@ -1155,11 +1146,11 @@ static int mxcmci_probe(struct platform_device *pdev) goto out_free_dma; } - init_timer(&host->watchdog); - host->watchdog.function = &mxcmci_watchdog; - host->watchdog.data = (unsigned long)mmc; + timer_setup(&host->watchdog, mxcmci_watchdog, 0); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto out_free_dma; return 0; @@ -1168,16 +1159,14 @@ out_free_dma: dma_release_channel(host->dma); out_clk_put: - clk_disable_unprepare(host->clk_per); clk_disable_unprepare(host->clk_ipg); - -out_free: - mmc_free_host(mmc); +out_clk_per_put: + clk_disable_unprepare(host->clk_per); return ret; } -static int mxcmci_remove(struct platform_device *pdev) +static void mxcmci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct mxcmci_host *host = mmc_priv(mmc); @@ -1192,13 +1181,9 @@ static int mxcmci_remove(struct platform_device *pdev) clk_disable_unprepare(host->clk_per); clk_disable_unprepare(host->clk_ipg); - - mmc_free_host(mmc); - - return 0; } -static int __maybe_unused mxcmci_suspend(struct device *dev) +static int mxcmci_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); @@ -1208,25 +1193,32 @@ static int __maybe_unused mxcmci_suspend(struct device *dev) return 0; } -static int __maybe_unused mxcmci_resume(struct device *dev) +static int mxcmci_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); + int ret; - clk_prepare_enable(host->clk_per); - clk_prepare_enable(host->clk_ipg); - return 0; + ret = clk_prepare_enable(host->clk_per); + if (ret) + return ret; + + ret = clk_prepare_enable(host->clk_ipg); + if (ret) + clk_disable_unprepare(host->clk_per); + + return ret; } -static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume); static struct platform_driver mxcmci_driver = { .probe = mxcmci_probe, .remove = mxcmci_remove, - .id_table = mxcmci_devtype, .driver = { .name = DRIVER_NAME, - .pm = &mxcmci_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .pm = pm_sleep_ptr(&mxcmci_pm_ops), .of_match_table = mxcmci_of_match, } }; |
