diff options
Diffstat (limited to 'drivers/mmc/host/pxamci.c')
| -rw-r--r-- | drivers/mmc/host/pxamci.c | 312 |
1 files changed, 110 insertions, 202 deletions
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 59ab194cb009..b5ea058ed467 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/mmc/host/pxa.c - PXA MMCI driver * * Copyright (C) 2003 Russell King, All Rights Reserved. * - * 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. - * * This hardware is really sick: * - No way to clear interrupts. * - Have to turn off the clock whenever we touch the device. @@ -24,22 +21,19 @@ #include <linux/interrupt.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> -#include <linux/dma/pxa-dma.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/mmc/host.h> #include <linux/mmc/slot-gpio.h> #include <linux/io.h> #include <linux/regulator/consumer.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/gfp.h> #include <linux/of.h> -#include <linux/of_gpio.h> -#include <linux/of_device.h> +#include <linux/soc/pxa/cpu.h> -#include <asm/sizes.h> +#include <linux/sizes.h> -#include <mach/hardware.h> #include <linux/platform_data/mmc-pxamci.h> #include "pxamci.h" @@ -59,11 +53,13 @@ struct pxamci_host { void __iomem *base; struct clk *clk; unsigned long clkrate; - int irq; unsigned int clkrt; unsigned int cmdat; unsigned int imask; unsigned int power_mode; + unsigned long detect_delay_ms; + bool use_ro_gpio; + struct gpio_desc *power; struct pxamci_platform_data *pdata; struct mmc_request *mrq; @@ -73,64 +69,45 @@ struct pxamci_host { struct dma_chan *dma_chan_rx; struct dma_chan *dma_chan_tx; dma_cookie_t dma_cookie; - dma_addr_t sg_dma; unsigned int dma_len; - unsigned int dma_dir; - unsigned int dma_drcmrrx; - unsigned int dma_drcmrtx; - - struct regulator *vcc; }; -static inline void pxamci_init_ocr(struct pxamci_host *host) +static int pxamci_init_ocr(struct pxamci_host *host) { -#ifdef CONFIG_REGULATOR - host->vcc = devm_regulator_get_optional(mmc_dev(host->mmc), "vmmc"); - - if (IS_ERR(host->vcc)) - host->vcc = NULL; - else { - host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc); - if (host->pdata && host->pdata->ocr_mask) - dev_warn(mmc_dev(host->mmc), - "ocr_mask/setpower will not be used\n"); - } -#endif - if (host->vcc == NULL) { + struct mmc_host *mmc = host->mmc; + int ret; + + ret = mmc_regulator_get_supply(mmc); + if (ret < 0) + return ret; + + if (IS_ERR(mmc->supply.vmmc)) { /* fall-back to platform data */ - host->mmc->ocr_avail = host->pdata ? + mmc->ocr_avail = host->pdata ? host->pdata->ocr_mask : MMC_VDD_32_33 | MMC_VDD_33_34; } + + return 0; } static inline int pxamci_set_power(struct pxamci_host *host, unsigned char power_mode, unsigned int vdd) { - int on; + struct mmc_host *mmc = host->mmc; + struct regulator *supply = mmc->supply.vmmc; - if (host->vcc) { - int ret; + if (!IS_ERR(supply)) + return mmc_regulator_set_ocr(mmc, supply, vdd); - if (power_mode == MMC_POWER_UP) { - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); - if (ret) - return ret; - } else if (power_mode == MMC_POWER_OFF) { - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); - if (ret) - return ret; - } - } - if (!host->vcc && host->pdata && - gpio_is_valid(host->pdata->gpio_power)) { - on = ((1 << vdd) & host->pdata->ocr_mask); - gpio_set_value(host->pdata->gpio_power, - !!on ^ host->pdata->gpio_power_invert); + if (host->power) { + bool on = !!((1 << vdd) & host->pdata->ocr_mask); + gpiod_set_value(host->power, on); } - if (!host->vcc && host->pdata && host->pdata->setpower) + + if (host->pdata && host->pdata->setpower) return host->pdata->setpower(mmc_dev(host->mmc), vdd); return 0; @@ -181,7 +158,7 @@ static void pxamci_dma_irq(void *param); static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) { struct dma_async_tx_descriptor *tx; - enum dma_data_direction direction; + enum dma_transfer_direction direction; struct dma_slave_config config; struct dma_chan *chan; unsigned int nob = data->blocks; @@ -449,7 +426,7 @@ static int pxamci_get_ro(struct mmc_host *mmc) { struct pxamci_host *host = mmc_priv(mmc); - if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro)) + if (host->use_ro_gpio) return mmc_gpio_get_ro(mmc); if (host->pdata && host->pdata->get_ro) return !!host->pdata->get_ro(mmc_dev(mmc)); @@ -585,7 +562,7 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid) { struct pxamci_host *host = mmc_priv(devid); - mmc_detect_change(devid, msecs_to_jiffies(host->pdata->detect_delay_ms)); + mmc_detect_change(devid, msecs_to_jiffies(host->detect_delay_ms)); return IRQ_HANDLED; } @@ -597,37 +574,30 @@ static const struct of_device_id pxa_mmc_dt_ids[] = { MODULE_DEVICE_TABLE(of, pxa_mmc_dt_ids); -static int pxamci_of_init(struct platform_device *pdev) +static int pxamci_of_init(struct platform_device *pdev, + struct mmc_host *mmc) { - struct device_node *np = pdev->dev.of_node; - struct pxamci_platform_data *pdata; - u32 tmp; - - if (!np) - return 0; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; + struct device_node *np = pdev->dev.of_node; + struct pxamci_host *host = mmc_priv(mmc); + u32 tmp; + int ret; - pdata->gpio_card_detect = - of_get_named_gpio(np, "cd-gpios", 0); - pdata->gpio_card_ro = - of_get_named_gpio(np, "wp-gpios", 0); + if (!np) + return 0; /* pxa-mmc specific */ - pdata->gpio_power = - of_get_named_gpio(np, "pxa-mmc,gpio-power", 0); - if (of_property_read_u32(np, "pxa-mmc,detect-delay-ms", &tmp) == 0) - pdata->detect_delay_ms = tmp; + host->detect_delay_ms = tmp; - pdev->dev.platform_data = pdata; + ret = mmc_of_parse(mmc); + if (ret < 0) + return ret; - return 0; + return 0; } #else -static int pxamci_of_init(struct platform_device *pdev) +static int pxamci_of_init(struct platform_device *pdev, + struct mmc_host *mmc) { return 0; } @@ -637,25 +607,17 @@ static int pxamci_probe(struct platform_device *pdev) { struct mmc_host *mmc; struct pxamci_host *host = NULL; - struct resource *r, *dmarx, *dmatx; - struct pxad_param param_rx, param_tx; - int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; - dma_cap_mask_t mask; + struct device *dev = &pdev->dev; + struct resource *r; + int ret, irq; - ret = pxamci_of_init(pdev); - if (ret) - return ret; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto out; - } + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; mmc->ops = &pxamci_ops; @@ -680,17 +642,19 @@ static int pxamci_probe(struct platform_device *pdev) */ mmc->max_blk_count = 65535; + ret = pxamci_of_init(pdev, mmc); + if (ret) + return ret; + host = mmc_priv(mmc); host->mmc = mmc; host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF; - host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - host->clk = NULL; - goto out; - } + host->clk = devm_clk_get(dev, NULL); + if (IS_ERR(host->clk)) + return dev_err_probe(dev, PTR_ERR(host->clk), + "Failed to acquire clock\n"); host->clkrate = clk_get_rate(host->clk); @@ -700,13 +664,11 @@ static int pxamci_probe(struct platform_device *pdev) mmc->f_min = (host->clkrate + 63) / 64; mmc->f_max = (mmc_has_26MHz()) ? 26000000 : host->clkrate; - pxamci_init_ocr(host); + ret = pxamci_init_ocr(host); + if (ret < 0) + return ret; - /* - * This architecture used to disable bounce buffers through its - * defconfig, now it is done at runtime as a host property. - */ - mmc->caps = MMC_CAP_NO_BOUNCE_BUFF; + mmc->caps = 0; host->cmdat = 0; if (!cpu_is_pxa25x()) { mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; @@ -717,15 +679,12 @@ static int pxamci_probe(struct platform_device *pdev) } spin_lock_init(&host->lock); - host->res = r; - host->irq = irq; host->imask = MMC_I_MASK_ALL; - host->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto out; - } + host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); + if (IS_ERR(host->base)) + return PTR_ERR(host->base); + host->res = r; /* * Ensure that the host controller is shut down, and setup @@ -736,121 +695,74 @@ static int pxamci_probe(struct platform_device *pdev) writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); - ret = devm_request_irq(&pdev->dev, host->irq, pxamci_irq, 0, + ret = devm_request_irq(dev, irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) - goto out; + return ret; platform_set_drvdata(pdev, mmc); - if (!pdev->dev.of_node) { - dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0); - dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!dmarx || !dmatx) { - ret = -ENXIO; - goto out; - } - param_rx.prio = PXAD_PRIO_LOWEST; - param_rx.drcmr = dmarx->start; - param_tx.prio = PXAD_PRIO_LOWEST; - param_tx.drcmr = dmatx->start; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); + host->dma_chan_rx = devm_dma_request_chan(dev, "rx"); + if (IS_ERR(host->dma_chan_rx)) + return dev_err_probe(dev, PTR_ERR(host->dma_chan_rx), + "unable to request rx dma channel\n"); - host->dma_chan_rx = - dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m_rx, &pdev->dev, "rx"); - if (host->dma_chan_rx == NULL) { - dev_err(&pdev->dev, "unable to request rx dma channel\n"); - ret = -ENODEV; - goto out; - } - host->dma_chan_tx = - dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m_tx, &pdev->dev, "tx"); - if (host->dma_chan_tx == NULL) { - dev_err(&pdev->dev, "unable to request tx dma channel\n"); - ret = -ENODEV; - goto out; - } + host->dma_chan_tx = devm_dma_request_chan(dev, "tx"); + if (IS_ERR(host->dma_chan_tx)) + return dev_err_probe(dev, PTR_ERR(host->dma_chan_tx), + "unable to request tx dma channel\n"); if (host->pdata) { - gpio_cd = host->pdata->gpio_card_detect; - gpio_ro = host->pdata->gpio_card_ro; - gpio_power = host->pdata->gpio_power; - } - if (gpio_is_valid(gpio_power)) { - ret = devm_gpio_request(&pdev->dev, gpio_power, - "mmc card power"); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", - gpio_power); - goto out; - } - gpio_direction_output(gpio_power, - host->pdata->gpio_power_invert); - } - if (gpio_is_valid(gpio_ro)) { - ret = mmc_gpio_request_ro(mmc, gpio_ro); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", - gpio_ro); - goto out; - } else { - mmc->caps2 |= host->pdata->gpio_card_ro_invert ? - 0 : MMC_CAP2_RO_ACTIVE_HIGH; - } - } + host->detect_delay_ms = host->pdata->detect_delay_ms; - if (gpio_is_valid(gpio_cd)) - ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_cd); - goto out; - } + host->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(host->power)) + return dev_err_probe(dev, PTR_ERR(host->power), + "Failed requesting gpio_power\n"); - if (host->pdata && host->pdata->init) - host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); + /* FIXME: should we pass detection delay to debounce? */ + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0); + if (ret && ret != -ENOENT) + return dev_err_probe(dev, ret, "Failed requesting gpio_cd\n"); - if (gpio_is_valid(gpio_power) && host->pdata->setpower) - dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n"); - if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) - dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n"); + if (!host->pdata->gpio_card_ro_invert) + mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - mmc_add_host(mmc); + ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0); + if (ret && ret != -ENOENT) + return dev_err_probe(dev, ret, "Failed requesting gpio_ro\n"); - return 0; + if (!ret) + host->use_ro_gpio = true; + + if (host->pdata->init) + host->pdata->init(dev, pxamci_detect_irq, mmc); -out: - if (host) { - if (host->dma_chan_rx) - dma_release_channel(host->dma_chan_rx); - if (host->dma_chan_tx) - dma_release_channel(host->dma_chan_tx); + if (host->power && host->pdata->setpower) + dev_warn(dev, "gpio_power and setpower() both defined\n"); + if (host->use_ro_gpio && host->pdata->get_ro) + dev_warn(dev, "gpio_ro and get_ro() both defined\n"); } - if (mmc) - mmc_free_host(mmc); + + ret = mmc_add_host(mmc); + if (ret) { + if (host->pdata && host->pdata->exit) + host->pdata->exit(dev, mmc); + } + return ret; } -static int pxamci_remove(struct platform_device *pdev) +static void pxamci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); - int gpio_cd = -1, gpio_ro = -1, gpio_power = -1; if (mmc) { struct pxamci_host *host = mmc_priv(mmc); mmc_remove_host(mmc); - if (host->pdata) { - gpio_cd = host->pdata->gpio_card_detect; - gpio_ro = host->pdata->gpio_card_ro; - gpio_power = host->pdata->gpio_power; - } if (host->pdata && host->pdata->exit) host->pdata->exit(&pdev->dev, mmc); @@ -861,12 +773,7 @@ static int pxamci_remove(struct platform_device *pdev) dmaengine_terminate_all(host->dma_chan_rx); dmaengine_terminate_all(host->dma_chan_tx); - dma_release_channel(host->dma_chan_rx); - dma_release_channel(host->dma_chan_tx); - - mmc_free_host(mmc); } - return 0; } static struct platform_driver pxamci_driver = { @@ -874,6 +781,7 @@ static struct platform_driver pxamci_driver = { .remove = pxamci_remove, .driver = { .name = DRIVER_NAME, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = of_match_ptr(pxa_mmc_dt_ids), }, }; |
