summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2015-08-04 18:19:42 +0200
committerStefan Agner <stefan.agner@toradex.com>2015-08-04 18:19:42 +0200
commit8f4fb1c1d6e4c7804d6d3db69986642199d801e9 (patch)
treee4f2b00217d04a6c2c7b14ca3fada76e8e10c1e6 /drivers
parent49662c2e2fcede96ed0a55c7838265e3ed781a52 (diff)
parentaaa847711ebdb33592f7e4136be2eeac14b83137 (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.c83
-rw-r--r--drivers/gpio/gpio-vf610.c188
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-vf610-gpc.c123
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c2
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c36
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h2
-rw-r--r--drivers/net/can/spi/mcp251x.c13
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx.c64
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx.h3
-rw-r--r--drivers/pinctrl/freescale/pinctrl-vf610.c6
-rw-r--r--drivers/pwm/pwm-fsl-ftm.c58
-rw-r--r--drivers/tty/serial/fsl_lpuart.c25
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c6
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;
}