--- zzzz-none-000/linux-3.10.107/drivers/mmc/host/atmel-mci.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/mmc/host/atmel-mci.c 2021-02-04 17:41:59.000000000 +0000 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,22 +29,25 @@ #include #include #include -#include +#include #include #include -#include #include #include +#include +#include +#include +#include #include #include -#include - #include "atmel-mci-regs.h" +#define AUTOSUSPEND_DELAY 50 + #define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE) #define ATMCI_DMA_THRESHOLD 16 @@ -257,7 +261,6 @@ #define ATMCI_CARD_PRESENT 0 #define ATMCI_CARD_NEED_INIT 1 #define ATMCI_SHUTDOWN 2 -#define ATMCI_SUSPENDED 3 int detect_pin; int wp_pin; @@ -380,22 +383,27 @@ { struct atmel_mci *host = s->private; u32 *buf; + int ret = 0; + buf = kmalloc(ATMCI_REGS_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; + pm_runtime_get_sync(&host->pdev->dev); + /* * Grab a more or less consistent snapshot. Note that we're * not disabling interrupts, so IMR and SR may not be * consistent. */ spin_lock_bh(&host->lock); - clk_enable(host->mck); memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE); - clk_disable(host->mck); spin_unlock_bh(&host->lock); + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); + seq_printf(s, "MR:\t0x%08x%s%s ", buf[ATMCI_MR / 4], buf[ATMCI_MR / 4] & ATMCI_MR_RDPROOF ? " RDPROOF" : "", @@ -446,7 +454,7 @@ kfree(buf); - return 0; + return ret; } static int atmci_regs_open(struct inode *inode, struct file *file) @@ -554,6 +562,9 @@ pdata->slot[slot_id].detect_is_active_high = of_property_read_bool(cnp, "cd-inverted"); + pdata->slot[slot_id].non_removable = + of_property_read_bool(cnp, "non-removable"); + pdata->slot[slot_id].wp_pin = of_get_named_gpio(cnp, "wp-gpios", 0); } @@ -816,16 +827,9 @@ atmci_pdc_cleanup(host); - /* - * If the card was removed, data will be NULL. No point trying - * to send the stop command or waiting for NBUSY in this case. - */ - if (host->data) { - dev_dbg(&host->pdev->dev, - "(%s) set pending xfer complete\n", __func__); - atmci_set_pending(host, EVENT_XFER_COMPLETE); - tasklet_schedule(&host->tasklet); - } + dev_dbg(&host->pdev->dev, "(%s) set pending xfer complete\n", __func__); + atmci_set_pending(host, EVENT_XFER_COMPLETE); + tasklet_schedule(&host->tasklet); } static void atmci_dma_cleanup(struct atmel_mci *host) @@ -1253,6 +1257,8 @@ WARN_ON(slot->mrq); dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode); + pm_runtime_get_sync(&host->pdev->dev); + /* * We may "know" the card is gone even though there's still an * electrical connection. If so, we really need to communicate @@ -1283,6 +1289,8 @@ struct atmel_mci *host = slot->host; unsigned int i; + pm_runtime_get_sync(&host->pdev->dev); + slot->sdc_reg &= ~ATMCI_SDCBUS_MASK; switch (ios->bus_width) { case MMC_BUS_WIDTH_1: @@ -1299,7 +1307,6 @@ spin_lock_bh(&host->lock); if (!host->mode_reg) { - clk_enable(host->mck); atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN); if (host->caps.has_cfg_reg) @@ -1384,7 +1391,6 @@ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS); if (host->mode_reg) { atmci_readl(host, ATMCI_MR); - clk_disable(host->mck); } host->mode_reg = 0; } @@ -1392,8 +1398,14 @@ } switch (ios->power_mode) { + case MMC_POWER_OFF: + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + break; case MMC_POWER_UP: set_bit(ATMCI_CARD_NEED_INIT, &slot->flags); + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); break; default: /* @@ -1410,6 +1422,9 @@ */ break; } + + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); } static int atmci_get_ro(struct mmc_host *mmc) @@ -1501,6 +1516,9 @@ spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); spin_lock(&host->lock); + + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); } static void atmci_command_complete(struct atmel_mci *host, @@ -2126,7 +2144,7 @@ return IRQ_HANDLED; } -static int __init atmci_init_slot(struct atmel_mci *host, +static int atmci_init_slot(struct atmel_mci *host, struct mci_slot_pdata *slot_data, unsigned int id, u32 sdc_reg, u32 sdio_irq) { @@ -2185,7 +2203,8 @@ /* Assume card is present initially */ set_bit(ATMCI_CARD_PRESENT, &slot->flags); if (gpio_is_valid(slot->detect_pin)) { - if (gpio_request(slot->detect_pin, "mmc_detect")) { + if (devm_gpio_request(&host->pdev->dev, slot->detect_pin, + "mmc_detect")) { dev_dbg(&mmc->class_dev, "no detect pin available\n"); slot->detect_pin = -EBUSY; } else if (gpio_get_value(slot->detect_pin) ^ @@ -2194,17 +2213,23 @@ } } - if (!gpio_is_valid(slot->detect_pin)) - mmc->caps |= MMC_CAP_NEEDS_POLL; + if (!gpio_is_valid(slot->detect_pin)) { + if (slot_data->non_removable) + mmc->caps |= MMC_CAP_NONREMOVABLE; + else + mmc->caps |= MMC_CAP_NEEDS_POLL; + } if (gpio_is_valid(slot->wp_pin)) { - if (gpio_request(slot->wp_pin, "mmc_wp")) { + if (devm_gpio_request(&host->pdev->dev, slot->wp_pin, + "mmc_wp")) { dev_dbg(&mmc->class_dev, "no WP pin available\n"); slot->wp_pin = -EBUSY; } } host->slot[id] = slot; + mmc_regulator_get_supply(mmc); mmc_add_host(mmc); if (gpio_is_valid(slot->detect_pin)) { @@ -2221,7 +2246,6 @@ dev_dbg(&mmc->class_dev, "could not request IRQ %d for detect pin\n", gpio_to_irq(slot->detect_pin)); - gpio_free(slot->detect_pin); slot->detect_pin = -EBUSY; } } @@ -2231,7 +2255,7 @@ return 0; } -static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot, +static void atmci_cleanup_slot(struct atmel_mci_slot *slot, unsigned int id) { /* Debugfs stuff is cleaned up by mmc core */ @@ -2246,64 +2270,31 @@ free_irq(gpio_to_irq(pin), slot); del_timer_sync(&slot->detect_timer); - gpio_free(pin); } - if (gpio_is_valid(slot->wp_pin)) - gpio_free(slot->wp_pin); slot->host->slot[id] = NULL; mmc_free_host(slot->mmc); } -static bool atmci_filter(struct dma_chan *chan, void *pdata) -{ - struct mci_platform_data *sl_pdata = pdata; - struct mci_dma_data *sl; - - if (!sl_pdata) - return false; - - sl = sl_pdata->dma_slave; - if (sl && find_slave_dev(sl) == chan->device->dev) { - chan->private = slave_data_ptr(sl); - return true; - } else { - return false; - } -} - -static bool atmci_configure_dma(struct atmel_mci *host) +static int atmci_configure_dma(struct atmel_mci *host) { - struct mci_platform_data *pdata; - dma_cap_mask_t mask; - - if (host == NULL) - return false; - - pdata = host->pdev->dev.platform_data; + host->dma.chan = dma_request_slave_channel_reason(&host->pdev->dev, + "rxtx"); + if (IS_ERR(host->dma.chan)) + return PTR_ERR(host->dma.chan); + + dev_info(&host->pdev->dev, "using %s for DMA transfers\n", + dma_chan_name(host->dma.chan)); + + host->dma_conf.src_addr = host->mapbase + ATMCI_RDR; + host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.src_maxburst = 1; + host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR; + host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.dst_maxburst = 1; + host->dma_conf.device_fc = false; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - host->dma.chan = dma_request_slave_channel_compat(mask, atmci_filter, pdata, - &host->pdev->dev, "rxtx"); - if (!host->dma.chan) { - dev_warn(&host->pdev->dev, "no DMA channel available\n"); - return false; - } else { - dev_info(&host->pdev->dev, - "using %s for DMA transfers\n", - dma_chan_name(host->dma.chan)); - - host->dma_conf.src_addr = host->mapbase + ATMCI_RDR; - host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_conf.src_maxburst = 1; - host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR; - host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_conf.dst_maxburst = 1; - host->dma_conf.device_fc = false; - return true; - } + return 0; } /* @@ -2311,7 +2302,7 @@ * HSMCI provides DMA support and a new config register but no more supports * PDC. */ -static void __init atmci_get_cap(struct atmel_mci *host) +static void atmci_get_cap(struct atmel_mci *host) { unsigned int version; @@ -2333,6 +2324,7 @@ /* keep only major version number */ switch (version & 0xf00) { + case 0x600: case 0x500: host->caps.has_odd_clk_div = 1; case 0x400: @@ -2359,14 +2351,14 @@ } } -static int __init atmci_probe(struct platform_device *pdev) +static int atmci_probe(struct platform_device *pdev) { struct mci_platform_data *pdata; struct atmel_mci *host; struct resource *regs; unsigned int nr_slots; int irq; - int ret; + int ret, i; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) @@ -2384,7 +2376,7 @@ if (irq < 0) return irq; - host = kzalloc(sizeof(struct atmel_mci), GFP_KERNEL); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) return -ENOMEM; @@ -2392,33 +2384,37 @@ spin_lock_init(&host->lock); INIT_LIST_HEAD(&host->queue); - host->mck = clk_get(&pdev->dev, "mci_clk"); - if (IS_ERR(host->mck)) { - ret = PTR_ERR(host->mck); - goto err_clk_get; - } + host->mck = devm_clk_get(&pdev->dev, "mci_clk"); + if (IS_ERR(host->mck)) + return PTR_ERR(host->mck); - ret = -ENOMEM; - host->regs = ioremap(regs->start, resource_size(regs)); + host->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); if (!host->regs) - goto err_ioremap; + return -ENOMEM; + + ret = clk_prepare_enable(host->mck); + if (ret) + return ret; - clk_enable(host->mck); atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); host->bus_hz = clk_get_rate(host->mck); - clk_disable(host->mck); host->mapbase = regs->start; tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host); ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); - if (ret) - goto err_request_irq; + if (ret) { + clk_disable_unprepare(host->mck); + return ret; + } /* Get MCI capabilities and set operations according to it */ atmci_get_cap(host); - if (atmci_configure_dma(host)) { + ret = atmci_configure_dma(host); + if (ret == -EPROBE_DEFER) + goto err_dma_probe_defer; + if (ret == 0) { host->prepare_data = &atmci_prepare_data_dma; host->submit_data = &atmci_submit_data_dma; host->stop_transfer = &atmci_stop_transfer_dma; @@ -2438,6 +2434,12 @@ setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + /* We need at least one slot to succeed */ nr_slots = 0; ret = -ENODEV; @@ -2472,7 +2474,7 @@ if (!host->buffer) { ret = -ENOMEM; dev_err(&pdev->dev, "buffer allocation failed\n"); - goto err_init_slot; + goto err_dma_alloc; } } @@ -2480,27 +2482,36 @@ "Atmel MCI controller at 0x%08lx irq %d, %u slots\n", host->mapbase, irq, nr_slots); + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + return 0; +err_dma_alloc: + for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { + if (host->slot[i]) + atmci_cleanup_slot(host->slot[i], i); + } err_init_slot: - if (host->dma.chan) + clk_disable_unprepare(host->mck); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + del_timer_sync(&host->timer); + if (!IS_ERR(host->dma.chan)) dma_release_channel(host->dma.chan); +err_dma_probe_defer: free_irq(irq, host); -err_request_irq: - iounmap(host->regs); -err_ioremap: - clk_put(host->mck); -err_clk_get: - kfree(host); return ret; } -static int __exit atmci_remove(struct platform_device *pdev) +static int atmci_remove(struct platform_device *pdev) { struct atmel_mci *host = platform_get_drvdata(pdev); unsigned int i; - platform_set_drvdata(pdev, NULL); + pm_runtime_get_sync(&pdev->dev); if (host->buffer) dma_free_coherent(&pdev->dev, host->buf_size, @@ -2511,106 +2522,62 @@ atmci_cleanup_slot(host->slot[i], i); } - clk_enable(host->mck); atmci_writel(host, ATMCI_IDR, ~0UL); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS); atmci_readl(host, ATMCI_SR); - clk_disable(host->mck); - if (host->dma.chan) + del_timer_sync(&host->timer); + if (!IS_ERR(host->dma.chan)) dma_release_channel(host->dma.chan); free_irq(platform_get_irq(pdev, 0), host); - iounmap(host->regs); - clk_put(host->mck); - kfree(host); + clk_disable_unprepare(host->mck); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); return 0; } #ifdef CONFIG_PM -static int atmci_suspend(struct device *dev) +static int atmci_runtime_suspend(struct device *dev) { struct atmel_mci *host = dev_get_drvdata(dev); - int i; - for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { - struct atmel_mci_slot *slot = host->slot[i]; - int ret; + clk_disable_unprepare(host->mck); - if (!slot) - continue; - ret = mmc_suspend_host(slot->mmc); - if (ret < 0) { - while (--i >= 0) { - slot = host->slot[i]; - if (slot - && test_bit(ATMCI_SUSPENDED, &slot->flags)) { - mmc_resume_host(host->slot[i]->mmc); - clear_bit(ATMCI_SUSPENDED, &slot->flags); - } - } - return ret; - } else { - set_bit(ATMCI_SUSPENDED, &slot->flags); - } - } + pinctrl_pm_select_sleep_state(dev); return 0; } -static int atmci_resume(struct device *dev) +static int atmci_runtime_resume(struct device *dev) { struct atmel_mci *host = dev_get_drvdata(dev); - int i; - int ret = 0; - - for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { - struct atmel_mci_slot *slot = host->slot[i]; - int err; - slot = host->slot[i]; - if (!slot) - continue; - if (!test_bit(ATMCI_SUSPENDED, &slot->flags)) - continue; - err = mmc_resume_host(slot->mmc); - if (err < 0) - ret = err; - else - clear_bit(ATMCI_SUSPENDED, &slot->flags); - } + pinctrl_pm_select_default_state(dev); - return ret; + return clk_prepare_enable(host->mck); } -static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume); -#define ATMCI_PM_OPS (&atmci_pm) -#else -#define ATMCI_PM_OPS NULL #endif +static const struct dev_pm_ops atmci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL) +}; + static struct platform_driver atmci_driver = { - .remove = __exit_p(atmci_remove), + .probe = atmci_probe, + .remove = atmci_remove, .driver = { .name = "atmel_mci", - .pm = ATMCI_PM_OPS, .of_match_table = of_match_ptr(atmci_dt_ids), + .pm = &atmci_dev_pm_ops, }, }; - -static int __init atmci_init(void) -{ - return platform_driver_probe(&atmci_driver, atmci_probe); -} - -static void __exit atmci_exit(void) -{ - platform_driver_unregister(&atmci_driver); -} - -late_initcall(atmci_init); /* try to load after dma driver when built-in */ -module_exit(atmci_exit); +module_platform_driver(atmci_driver); MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver"); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");