diff options
author | Stefan Agner <stefan.agner@toradex.com> | 2015-08-04 18:19:42 +0200 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2015-08-04 18:19:42 +0200 |
commit | 8f4fb1c1d6e4c7804d6d3db69986642199d801e9 (patch) | |
tree | e4f2b00217d04a6c2c7b14ca3fada76e8e10c1e6 /drivers | |
parent | 49662c2e2fcede96ed0a55c7838265e3ed781a52 (diff) | |
parent | aaa847711ebdb33592f7e4136be2eeac14b83137 (diff) |
Merge branch 'vf610-suspend-4.1-lpstop2' into toradex_vf_4.1-next
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/dma/fsl-edma.c | 83 | ||||
-rw-r--r-- | drivers/gpio/gpio-vf610.c | 188 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-vf610-gpc.c | 123 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 2 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pltfm.c | 36 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pltfm.h | 2 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251x.c | 13 | ||||
-rw-r--r-- | drivers/pinctrl/freescale/pinctrl-imx.c | 64 | ||||
-rw-r--r-- | drivers/pinctrl/freescale/pinctrl-imx.h | 3 | ||||
-rw-r--r-- | drivers/pinctrl/freescale/pinctrl-vf610.c | 6 | ||||
-rw-r--r-- | drivers/pwm/pwm-fsl-ftm.c | 58 | ||||
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 25 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 6 |
14 files changed, 558 insertions, 52 deletions
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 915eec3cc279..cf8b06cd0c83 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -116,6 +116,10 @@ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) +enum fsl_edma_pm_state { + RUNNING = 0, + SUSPENDED, +}; struct fsl_edma_hw_tcd { __le32 saddr; @@ -147,6 +151,9 @@ struct fsl_edma_slave_config { struct fsl_edma_chan { struct virt_dma_chan vchan; enum dma_status status; + enum fsl_edma_pm_state pm_state; + bool idle; + u32 slave_id; struct fsl_edma_engine *edma; struct fsl_edma_desc *edesc; struct fsl_edma_slave_config fsc; @@ -298,6 +305,7 @@ static int fsl_edma_terminate_all(struct dma_chan *chan) spin_lock_irqsave(&fsl_chan->vchan.lock, flags); fsl_edma_disable_request(fsl_chan); fsl_chan->edesc = NULL; + fsl_chan->idle = true; vchan_get_all_descriptors(&fsl_chan->vchan, &head); spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); vchan_dma_desc_free_list(&fsl_chan->vchan, &head); @@ -313,6 +321,7 @@ static int fsl_edma_pause(struct dma_chan *chan) if (fsl_chan->edesc) { fsl_edma_disable_request(fsl_chan); fsl_chan->status = DMA_PAUSED; + fsl_chan->idle = true; } spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); return 0; @@ -327,6 +336,7 @@ static int fsl_edma_resume(struct dma_chan *chan) if (fsl_chan->edesc) { fsl_edma_enable_request(fsl_chan); fsl_chan->status = DMA_IN_PROGRESS; + fsl_chan->idle = false; } spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); return 0; @@ -648,6 +658,7 @@ static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd); fsl_edma_enable_request(fsl_chan); fsl_chan->status = DMA_IN_PROGRESS; + fsl_chan->idle = false; } static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) @@ -676,6 +687,7 @@ static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) vchan_cookie_complete(&fsl_chan->edesc->vdesc); fsl_chan->edesc = NULL; fsl_chan->status = DMA_COMPLETE; + fsl_chan->idle = true; } else { vchan_cyclic_callback(&fsl_chan->edesc->vdesc); } @@ -704,6 +716,7 @@ static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id) edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), fsl_edma->membase + EDMA_CERR); fsl_edma->chans[ch].status = DMA_ERROR; + fsl_edma->chans[ch].idle = true; } } return IRQ_HANDLED; @@ -724,6 +737,12 @@ static void fsl_edma_issue_pending(struct dma_chan *chan) spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (unlikely(fsl_chan->pm_state != RUNNING)) { + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + /* cannot submit due to suspend */ + return; + } + if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc) fsl_edma_xfer_desc(fsl_chan); @@ -735,6 +754,7 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, { struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data; struct dma_chan *chan, *_chan; + struct fsl_edma_chan *fsl_chan; unsigned long chans_per_mux = fsl_edma->n_chans / DMAMUX_NR; if (dma_spec->args_count != 2) @@ -748,8 +768,10 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, chan = dma_get_slave_channel(chan); if (chan) { chan->device->privatecnt++; - fsl_edma_chan_mux(to_fsl_edma_chan(chan), - dma_spec->args[1], true); + fsl_chan = to_fsl_edma_chan(chan); + fsl_chan->slave_id = dma_spec->args[1]; + fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, + true); mutex_unlock(&fsl_edma->fsl_edma_mutex); return chan; } @@ -888,7 +910,9 @@ static int fsl_edma_probe(struct platform_device *pdev) struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i]; fsl_chan->edma = fsl_edma; - + fsl_chan->pm_state = RUNNING; + fsl_chan->slave_id = 0; + fsl_chan->idle = true; fsl_chan->vchan.desc_free = fsl_edma_free_desc; vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); @@ -959,6 +983,58 @@ static int fsl_edma_remove(struct platform_device *pdev) return 0; } +static int fsl_edma_suspend_late(struct device *dev) +{ + struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); + struct fsl_edma_chan *fsl_chan; + unsigned long flags; + int i; + + for (i = 0; i < fsl_edma->n_chans; i++) { + fsl_chan = &fsl_edma->chans[i]; + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + /* Make sure chan is idle or can not into suspend. */ + if (unlikely(!fsl_chan->idle)) + goto out; + fsl_chan->pm_state = SUSPENDED; + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + } + return 0; + +out: + for (; i >= 0; i--) { + fsl_chan = &fsl_edma->chans[i]; + fsl_chan->pm_state = RUNNING; + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + } + return -EBUSY; +} + +static int fsl_edma_resume_early(struct device *dev) +{ + struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); + struct fsl_edma_chan *fsl_chan; + int i; + + for (i = 0; i < fsl_edma->n_chans; i++) { + fsl_chan = &fsl_edma->chans[i]; + fsl_chan->pm_state = RUNNING; + edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i)); + if (fsl_chan->slave_id != 0) + fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true); + } + + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, + fsl_edma->membase + EDMA_CR); + + return 0; +} + +static const struct dev_pm_ops fsl_edma_pm_ops = { + .suspend_late = fsl_edma_suspend_late, + .resume_early = fsl_edma_resume_early, +}; + static const struct of_device_id fsl_edma_dt_ids[] = { { .compatible = "fsl,vf610-edma", }, { /* sentinel */ } @@ -969,6 +1045,7 @@ static struct platform_driver fsl_edma_driver = { .driver = { .name = "fsl-edma", .of_match_table = fsl_edma_dt_ids, + .pm = &fsl_edma_pm_ops, }, .probe = fsl_edma_probe, .remove = fsl_edma_remove, diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index b6a08879a797..bd6b30d181f0 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -36,7 +36,10 @@ struct vf610_gpio_port { void __iomem *base; void __iomem *gpio_base; u8 irqc[VF610_GPIO_PER_PORT]; + struct platform_device *pdev_wkpu; + s8 fsl_wakeup[VF610_GPIO_PER_PORT]; int irq; + u32 state; }; #define GPIO_PDOR 0x00 @@ -60,6 +63,14 @@ struct vf610_gpio_port { #define PORT_INT_EITHER_EDGE 0xb #define PORT_INT_LOGIC_ONE 0xc +#define WKPU_WISR 0x14 +#define WKPU_IRER 0x18 +#define WKPU_WRER 0x1c +#define WKPU_WIREER 0x28 +#define WKPU_WIFEER 0x2c +#define WKPU_WIFER 0x30 +#define WKPU_WIPUER 0x34 + static struct irq_chip vf610_gpio_irq_chip; static const struct of_device_id vf610_gpio_dt_ids[] = { @@ -67,6 +78,11 @@ static const struct of_device_id vf610_gpio_dt_ids[] = { { /* sentinel */ } }; +static const struct of_device_id vf610_wkpu_dt_ids[] = { + { .compatible = "fsl,vf610-wkpu" }, + { /* sentinel */ } +}; + static inline void vf610_gpio_writel(u32 val, void __iomem *reg) { writel_relaxed(val, reg); @@ -151,8 +167,26 @@ static void vf610_gpio_irq_ack(struct irq_data *d) static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type) { struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d); + s8 wkpu_gpio = port->fsl_wakeup[d->hwirq]; u8 irqc; + if (wkpu_gpio >= 0) { + void __iomem *base = platform_get_drvdata(port->pdev_wkpu); + u32 wireer, wifeer; + u32 mask = 1 << wkpu_gpio; + + wireer = vf610_gpio_readl(base + WKPU_WIREER) & ~mask; + wifeer = vf610_gpio_readl(base + WKPU_WIFEER) & ~mask; + + if (type & IRQ_TYPE_EDGE_RISING) + wireer |= mask; + if (type & IRQ_TYPE_EDGE_FALLING) + wifeer |= mask; + + vf610_gpio_writel(wireer, base + WKPU_WIREER); + vf610_gpio_writel(wifeer, base + WKPU_WIFEER); + } + switch (type) { case IRQ_TYPE_EDGE_RISING: irqc = PORT_INT_RISING_EDGE; @@ -203,6 +237,29 @@ static void vf610_gpio_irq_unmask(struct irq_data *d) static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable) { struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d); + s8 wkpu_gpio = port->fsl_wakeup[d->hwirq]; + + if (wkpu_gpio >= 0) { + void __iomem *base = NULL; + u32 wrer, irer; + + base = platform_get_drvdata(port->pdev_wkpu); + + /* WKPU wakeup flag for LPSTOPx modes... */ + wrer = vf610_gpio_readl(base + WKPU_WRER); + irer = vf610_gpio_readl(base + WKPU_IRER); + + if (enable) { + wrer |= 1 << wkpu_gpio; + irer |= 1 << wkpu_gpio; + } else { + wrer &= ~(1 << wkpu_gpio); + irer &= ~(1 << wkpu_gpio); + } + + vf610_gpio_writel(wrer, base + WKPU_WRER); + vf610_gpio_writel(irer, base + WKPU_IRER); + } if (enable) enable_irq_wake(port->irq); @@ -221,6 +278,77 @@ static struct irq_chip vf610_gpio_irq_chip = { .irq_set_wake = vf610_gpio_irq_set_wake, }; +static int __maybe_unused vf610_gpio_suspend(struct device *dev) +{ + struct vf610_gpio_port *port = dev_get_drvdata(dev); + + port->state = vf610_gpio_readl(port->gpio_base + GPIO_PDOR); + + /* + * There is no need to store Port state since we maintain the state + * alread in the irqc array + */ + + return 0; +} + +static int __maybe_unused vf610_gpio_resume(struct device *dev) +{ + struct vf610_gpio_port *port = dev_get_drvdata(dev); + int i; + + vf610_gpio_writel(port->state, port->gpio_base + GPIO_PDOR); + + for (i = 0; i < port->gc.ngpio; i++) { + u32 irqc = port->irqc[i] << PORT_PCR_IRQC_OFFSET; + + vf610_gpio_writel(irqc, port->base + PORT_PCR(i)); + } + + return 0; +} + +static const struct dev_pm_ops vf610_gpio_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(vf610_gpio_suspend, vf610_gpio_resume) +}; + +static int vf610_gpio_wkpu(struct device_node *np, struct vf610_gpio_port *port) +{ + struct platform_device *pdev = NULL; + struct of_phandle_args arg; + int i, ret; + + for (i = 0; i < VF610_GPIO_PER_PORT; i++) + port->fsl_wakeup[i] = -1; + + for (i = 0;;i++) { + int gpioid, wakeupid, cnt; + + ret = of_parse_phandle_with_fixed_args(np, "fsl,gpio-wakeup", + 3, i, &arg); + + if (ret == -ENOENT) + break; + + if (!pdev) + pdev = of_find_device_by_node(arg.np); + of_node_put(arg.np); + if (!pdev) + return -EPROBE_DEFER; + + gpioid = arg.args[0]; + wakeupid = arg.args[1]; + cnt = arg.args[2]; + + while (cnt-- && gpioid < VF610_GPIO_PER_PORT) + port->fsl_wakeup[gpioid++] = wakeupid++; + } + + port->pdev_wkpu = pdev; + + return 0; +} + static int vf610_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -248,6 +376,10 @@ static int vf610_gpio_probe(struct platform_device *pdev) if (port->irq < 0) return port->irq; + ret = vf610_gpio_wkpu(np, port); + if (ret < 0) + return ret; + gc = &port->gc; gc->of_node = np; gc->dev = dev; @@ -278,6 +410,7 @@ static int vf610_gpio_probe(struct platform_device *pdev) } gpiochip_set_chained_irqchip(gc, &vf610_gpio_irq_chip, port->irq, vf610_gpio_irq_handler); + platform_set_drvdata(pdev, port); return 0; } @@ -285,6 +418,7 @@ static int vf610_gpio_probe(struct platform_device *pdev) static struct platform_driver vf610_gpio_driver = { .driver = { .name = "gpio-vf610", + .pm = &vf610_gpio_pm_ops, .of_match_table = vf610_gpio_dt_ids, }, .probe = vf610_gpio_probe, @@ -296,6 +430,60 @@ static int __init gpio_vf610_init(void) } device_initcall(gpio_vf610_init); +static irqreturn_t vf610_wkpu_irq(int irq, void *data) +{ + void __iomem *base = data; + u32 wisr; + + wisr = vf610_gpio_readl(base + WKPU_WISR); + vf610_gpio_writel(wisr, base + WKPU_WISR); + pr_debug("%s, WKPU interrupt received, flags %08x\n", __func__, wisr); + + return 0; +} + +static int vf610_wkpu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *iores; + void __iomem *base; + int irq, err; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, iores); + + if (IS_ERR(base)) + return PTR_ERR(base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + err = devm_request_irq(dev, irq, vf610_wkpu_irq, 0, "wkpu-vf610", base); + if (err) { + dev_err(dev, "Error requesting IRQ!\n"); + return err; + } + + platform_set_drvdata(pdev, base); + + return 0; +} + +static struct platform_driver vf610_wkpu_driver = { + .driver = { + .name = "wkpu-vf610", + .of_match_table = vf610_wkpu_dt_ids, + }, + .probe = vf610_wkpu_probe, +}; + +static int __init vf610_wkpu_init(void) +{ + return platform_driver_register(&vf610_wkpu_driver); +} +device_initcall(vf610_wkpu_init); + MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>"); MODULE_DESCRIPTION("Freescale VF610 GPIO"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index dda4927e47a6..e17ec6c715cb 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o +obj-$(CONFIG_SOC_VF610) += irq-vf610-gpc.o obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o diff --git a/drivers/irqchip/irq-vf610-gpc.c b/drivers/irqchip/irq-vf610-gpc.c new file mode 100644 index 000000000000..5527e103f9b0 --- /dev/null +++ b/drivers/irqchip/irq-vf610-gpc.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2015 Toradex AG + * Author: Stefan Agner <stefan@agner.ch> + * + * 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. + * + * + * The GPC (General Power Controller) irqchip driver takes care of the + * interrupt wakeup functionality. + * + * o All peripheral interrupts of the Vybrid SoC can be used as wakeup + * source from STOP mode. In LPSTOP mode however, the GPC is unpowered + * too and cannot be used to as a wakeup source. The WKPU (Wakeup Unit) + * is responsible for wakeups from LPSTOP modes. + */ + +#include <linux/cpu_pm.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/mfd/syscon.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/regmap.h> + +#include "irqchip.h" + +#define IMR_NUM 4 +#define VF610_GPC_IMR1 0x044 +#define VF610_GPC_MAX_IRQS (IMR_NUM * 32) + +static void __iomem *gpc_base; + +static int vf610_gpc_irq_set_wake(struct irq_data *d, unsigned int on) +{ + unsigned int idx = d->hwirq / 32; + void __iomem *reg_imr = gpc_base + VF610_GPC_IMR1 + (idx * 4); + u32 mask = 1 << d->hwirq % 32; + + if (on) + writel_relaxed(readl_relaxed(reg_imr) & ~mask, reg_imr); + else + writel_relaxed(readl_relaxed(reg_imr) | mask, reg_imr); + + /* + * Do *not* call into the parent, as the GIC doesn't have any + * wake-up facility... + */ + return 0; +} + +static struct irq_chip vf610_gpc_chip = { + .name = "vf610-gpc", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_enable = irq_chip_enable_parent, + .irq_disable = irq_chip_disable_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_wake = vf610_gpc_irq_set_wake, +}; + +static int vf610_gpc_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct of_phandle_args *args = data; + struct of_phandle_args parent_args; + irq_hw_number_t hwirq; + int i; + + if (args->args_count != 2) + return -EINVAL; + + hwirq = args->args[0]; + for (i = 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &vf610_gpc_chip, NULL); + + parent_args = *args; + parent_args.np = domain->parent->of_node; + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args); +} + +static const struct irq_domain_ops gpc_irq_domain_ops = { + .xlate = irq_domain_xlate_twocell, + .alloc = vf610_gpc_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int __init vf610_gpc_of_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *domain, *domain_parent; + int i; + + domain_parent = irq_find_host(parent); + if (!domain_parent) { + pr_err("vf610_gpc: interrupt-parent not found\n"); + return -EINVAL; + } + + gpc_base = of_io_request_and_map(node, 0, "gpc"); + if (WARN_ON(!gpc_base)) + return -ENOMEM; + + domain = irq_domain_add_hierarchy(domain_parent, 0, VF610_GPC_MAX_IRQS, + node, &gpc_irq_domain_ops, NULL); + if (!domain) { + iounmap(gpc_base); + return -ENOMEM; + } + + /* Initially mask all interrupts for wakeup */ + for (i = 0; i < IMR_NUM; i++) + writel_relaxed(~0, gpc_base + VF610_GPC_IMR1 + i * 4); + + return 0; +} +IRQCHIP_DECLARE(vf610_gpc, "fsl,vf610-gpc", vf610_gpc_of_init); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 82f512d87cb8..7b7b3a319d8a 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1132,7 +1132,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) #endif static const struct dev_pm_ops sdhci_esdhc_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume) + SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_rpm_suspend, sdhci_pltfm_rpm_resume) SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend, sdhci_esdhc_runtime_resume, NULL) }; diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index a207f5aaf62f..38c03cd66052 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -31,6 +31,7 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #ifdef CONFIG_PPC #include <asm/machdep.h> #endif @@ -256,6 +257,41 @@ const struct dev_pm_ops sdhci_pltfm_pmops = { .resume = sdhci_pltfm_resume, }; EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops); + +int sdhci_pltfm_rpm_suspend(struct device *dev) +{ + int ret; + struct sdhci_host *host = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + ret = sdhci_suspend_host(host); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + if (ret) + return ret; + + return pm_runtime_force_suspend(dev); +} +EXPORT_SYMBOL_GPL(sdhci_pltfm_rpm_suspend); + +int sdhci_pltfm_rpm_resume(struct device *dev) +{ + int ret; + struct sdhci_host *host = dev_get_drvdata(dev); + + ret = pm_runtime_force_resume(dev); + + if (ret) + return ret; + + pm_runtime_get_sync(dev); + ret = sdhci_resume_host(host); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} +EXPORT_SYMBOL_GPL(sdhci_pltfm_rpm_resume); #endif /* CONFIG_PM */ static int __init sdhci_pltfm_drv_init(void) diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 04bc2481e5c3..ac5f6ea9b55f 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -114,6 +114,8 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host) extern int sdhci_pltfm_suspend(struct device *dev); extern int sdhci_pltfm_resume(struct device *dev); extern const struct dev_pm_ops sdhci_pltfm_pmops; +extern int sdhci_pltfm_rpm_suspend(struct device *dev); +extern int sdhci_pltfm_rpm_resume(struct device *dev); #define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops) #else #define SDHCI_PLTFM_PMOPS NULL diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index bf63fee4e743..34c625ea2801 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -1221,17 +1221,16 @@ static int __maybe_unused mcp251x_can_resume(struct device *dev) struct spi_device *spi = to_spi_device(dev); struct mcp251x_priv *priv = spi_get_drvdata(spi); - if (priv->after_suspend & AFTER_SUSPEND_POWER) { + if (priv->after_suspend & AFTER_SUSPEND_POWER) mcp251x_power_enable(priv->power, 1); + + if (priv->after_suspend & AFTER_SUSPEND_UP) { + mcp251x_power_enable(priv->transceiver, 1); queue_work(priv->wq, &priv->restart_work); } else { - if (priv->after_suspend & AFTER_SUSPEND_UP) { - mcp251x_power_enable(priv->transceiver, 1); - queue_work(priv->wq, &priv->restart_work); - } else { - priv->after_suspend = 0; - } + priv->after_suspend = 0; } + priv->force_quit = 0; enable_irq(spi->irq); return 0; diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index e261f1cf85c6..2c11aefc8a45 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -23,6 +23,7 @@ #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/slab.h> +#include <linux/syscore_ops.h> #include "../core.h" #include "pinctrl-imx.h" @@ -40,6 +41,10 @@ struct imx_pinctrl { struct pinctrl_dev *pctl; void __iomem *base; const struct imx_pinctrl_soc_info *info; +#ifdef CONFIG_PM_SLEEP + u32 *mux_regs; + u32 *input_regs; +#endif }; static const inline struct imx_pin_group *imx_pinctrl_find_group_by_name( @@ -643,6 +648,53 @@ static int imx_pinctrl_probe_dt(struct platform_device *pdev, return 0; } +static int __maybe_unused imx_pinctrl_suspend(struct device *dev) +{ + struct imx_pinctrl *ipctl = dev_get_drvdata(dev); + const struct imx_pinctrl_soc_info *info = ipctl->info; + int i; + + for (i = 0; i < info->npins; i++) { + const struct imx_pin_reg *pin_reg = &info->pin_regs[i]; + if (pin_reg->mux_reg == -1) + continue; + + ipctl->mux_regs[i] = readl(ipctl->base + pin_reg->mux_reg); + } + + for (i = 0; i < info->ninput_regs; i++) + ipctl->input_regs[i] = readl(ipctl->base + + info->input_regs_offset + i * sizeof(u32 *)); + + return 0; +} + +static int __maybe_unused imx_pinctrl_resume(struct device *dev) +{ + struct imx_pinctrl *ipctl = dev_get_drvdata(dev); + const struct imx_pinctrl_soc_info *info = ipctl->info; + const struct imx_pin_reg *pin_reg; + int i; + + for (i = 0; i < info->npins; i++) { + pin_reg = &info->pin_regs[i]; + if (pin_reg->mux_reg == -1) + continue; + + writel(ipctl->mux_regs[i], ipctl->base + pin_reg->mux_reg); + } + + for (i = 0; i < info->ninput_regs; i++) + writel(ipctl->input_regs[i], ipctl->base + + info->input_regs_offset + i * sizeof(u32 *)); + + return 0; +} + +const struct dev_pm_ops imx_pinctrl_dev_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(imx_pinctrl_suspend, imx_pinctrl_resume) +}; + int imx_pinctrl_probe(struct platform_device *pdev, struct imx_pinctrl_soc_info *info) { @@ -671,6 +723,18 @@ int imx_pinctrl_probe(struct platform_device *pdev, info->pin_regs[i].conf_reg = -1; } +#ifdef CONFIG_PM_SLEEP + ipctl->mux_regs = devm_kzalloc(&pdev->dev, sizeof(u32 *) * + info->npins, GFP_KERNEL); + if (!ipctl->mux_regs) + return -ENOMEM; + + ipctl->input_regs = devm_kzalloc(&pdev->dev, sizeof(u32 *) * + info->ninput_regs, GFP_KERNEL); + if (!ipctl->input_regs) + return -ENOMEM; +#endif + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ipctl->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ipctl->base)) diff --git a/drivers/pinctrl/freescale/pinctrl-imx.h b/drivers/pinctrl/freescale/pinctrl-imx.h index 49e55d39f7c8..6da680675bd9 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.h +++ b/drivers/pinctrl/freescale/pinctrl-imx.h @@ -80,6 +80,8 @@ struct imx_pinctrl_soc_info { unsigned int ngroups; struct imx_pmx_func *functions; unsigned int nfunctions; + unsigned int input_regs_offset; + unsigned int ninput_regs; unsigned int flags; }; @@ -97,4 +99,5 @@ struct imx_pinctrl_soc_info { int imx_pinctrl_probe(struct platform_device *pdev, struct imx_pinctrl_soc_info *info); int imx_pinctrl_remove(struct platform_device *pdev); +extern const struct dev_pm_ops imx_pinctrl_dev_pm_ops; #endif /* __DRIVERS_PINCTRL_IMX_H */ diff --git a/drivers/pinctrl/freescale/pinctrl-vf610.c b/drivers/pinctrl/freescale/pinctrl-vf610.c index 37a037543d29..3d5148e267fa 100644 --- a/drivers/pinctrl/freescale/pinctrl-vf610.c +++ b/drivers/pinctrl/freescale/pinctrl-vf610.c @@ -19,6 +19,9 @@ #include "pinctrl-imx.h" +#define VF610_INPUT_REG_CNT 49 +#define VF610_INPUT_REG_BASE 0x2ec + enum vf610_pads { VF610_PAD_PTA6 = 0, VF610_PAD_PTA8 = 1, @@ -299,6 +302,8 @@ static const struct pinctrl_pin_desc vf610_pinctrl_pads[] = { static struct imx_pinctrl_soc_info vf610_pinctrl_info = { .pins = vf610_pinctrl_pads, .npins = ARRAY_SIZE(vf610_pinctrl_pads), + .input_regs_offset = VF610_INPUT_REG_BASE, + .ninput_regs = VF610_INPUT_REG_CNT, .flags = SHARE_MUX_CONF_REG, }; @@ -316,6 +321,7 @@ static struct platform_driver vf610_pinctrl_driver = { .driver = { .name = "vf610-pinctrl", .of_match_table = vf610_pinctrl_of_match, + .pm = &imx_pinctrl_dev_pm_ops, }, .probe = vf610_pinctrl_probe, .remove = imx_pinctrl_remove, diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index f9dfc8b6407a..5e1dd9605070 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -80,7 +80,6 @@ struct fsl_pwm_chip { struct mutex lock; - unsigned int use_count; unsigned int cnt_select; unsigned int clk_ps; @@ -300,9 +299,6 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) { int ret; - if (fpc->use_count++ != 0) - return 0; - /* select counter clock source */ regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, FTM_SC_CLK(fpc->cnt_select)); @@ -334,25 +330,6 @@ static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return ret; } -static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) -{ - /* - * already disabled, do nothing - */ - if (fpc->use_count == 0) - return; - - /* there are still users, so can't disable yet */ - if (--fpc->use_count > 0) - return; - - /* no users left, disable PWM counter clock */ - regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0); - - clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); - clk_disable_unprepare(fpc->clk[fpc->cnt_select]); -} - static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct fsl_pwm_chip *fpc = to_fsl_chip(chip); @@ -362,7 +339,8 @@ static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), BIT(pwm->hwpwm)); - fsl_counter_clock_disable(fpc); + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); + clk_disable_unprepare(fpc->clk[fpc->cnt_select]); regmap_read(fpc->regmap, FTM_OUTMASK, &val); if ((val & 0xFF) == 0xFF) @@ -492,17 +470,24 @@ static int fsl_pwm_remove(struct platform_device *pdev) static int fsl_pwm_suspend(struct device *dev) { struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); - u32 val; + int i; regcache_cache_only(fpc->regmap, true); regcache_mark_dirty(fpc->regmap); - /* read from cache */ - regmap_read(fpc->regmap, FTM_OUTMASK, &val); - if ((val & 0xFF) != 0xFF) { + for (i = 0; i < fpc->chip.npwm; i++) { + struct pwm_device *pwm = &fpc->chip.pwms[i]; + + if (!test_bit(PWMF_REQUESTED, &pwm->flags)) + continue; + + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + continue; + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); clk_disable_unprepare(fpc->clk[fpc->cnt_select]); - clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); } return 0; @@ -511,12 +496,19 @@ static int fsl_pwm_suspend(struct device *dev) static int fsl_pwm_resume(struct device *dev) { struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); - u32 val; + int i; + + for (i = 0; i < fpc->chip.npwm; i++) { + struct pwm_device *pwm = &fpc->chip.pwms[i]; + + if (!test_bit(PWMF_REQUESTED, &pwm->flags)) + continue; - /* read from cache */ - regmap_read(fpc->regmap, FTM_OUTMASK, &val); - if ((val & 0xFF) != 0xFF) { clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + continue; + clk_prepare_enable(fpc->clk[fpc->cnt_select]); clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); } diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 08ce76f4f261..921f68a85b5c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -483,9 +483,8 @@ static void lpuart_dma_rx_complete(void *arg) spin_unlock_irqrestore(&sport->port.lock, flags); } -static void lpuart_timer_func(unsigned long data) +static void lpuart_dma_rx_terminate(struct lpuart_port *sport) { - struct lpuart_port *sport = (struct lpuart_port *)data; struct tty_port *port = &sport->port.state->port; struct dma_tx_state state; unsigned long flags; @@ -510,6 +509,11 @@ static void lpuart_timer_func(unsigned long data) spin_unlock_irqrestore(&sport->port.lock, flags); } +static void lpuart_timer_func(unsigned long data) +{ + lpuart_dma_rx_terminate((struct lpuart_port *)data); +} + static inline void lpuart_prepare_rx(struct lpuart_port *sport) { unsigned long flags; @@ -921,13 +925,16 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, sport->port.membase + UARTPFIFO); - /* explicitly clear RDRF */ - readb(sport->port.membase + UARTSR1); - /* flush Tx and Rx FIFO */ writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO); + /* explicitly clear RDRF */ + if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) { + readb(sport->port.membase + UARTDR); + writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); + } + writeb(0, sport->port.membase + UARTTWFIFO); writeb(1, sport->port.membase + UARTRWFIFO); @@ -1876,7 +1883,12 @@ static int lpuart_suspend(struct device *dev) writeb(temp, sport->port.membase + UARTCR2); } + if (sport->dma_rx_in_progress) + lpuart_dma_rx_terminate(sport); + uart_suspend_port(&lpuart_reg, &sport->port); + if (sport->port.suspended && !sport->port.irq_wake) + clk_disable_unprepare(sport->clk); return 0; } @@ -1886,6 +1898,9 @@ static int lpuart_resume(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; + if (sport->port.suspended && !sport->port.irq_wake) + clk_prepare_enable(sport->clk); + if (sport->lpuart32) { lpuart32_setup_watermark(sport); temp = lpuart32_read(sport->port.membase + UARTCTRL); diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 389f0e034259..6728859e8c3c 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -184,9 +184,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) &pdata); if (IS_ERR(data->ci_pdev)) { ret = PTR_ERR(data->ci_pdev); - dev_err(&pdev->dev, - "Can't register ci_hdrc platform device, err=%d\n", - ret); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "ci_hdrc_add_device failed, err=%d\n", ret); goto err_clk; } |