summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-09 10:01:15 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-09 10:01:15 -0700
commit0160e00ae8e987be8822745fb166aa76451c9bcc (patch)
treedeca2d09a729155ed0cb631f2bc8f557e634ab06 /drivers
parentc81ee18e97e4e3162169a749eb7f2b79b3510c7a (diff)
parentb6942b68f85ed3161c91741791ec6f1779574919 (diff)
downloadlinux-0160e00ae8e987be8822745fb166aa76451c9bcc.tar.bz2
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Olof Johansson: "Driver updates for ARM SoCs: Reset subsystem, merged through arm-soc by tradition: - Make bool drivers explicitly non-modular - New support for i.MX7 and Arria10 reset controllers PATA driver for Palmchip BK371 (acked by Tejun) Power domain drivers for i.MX (GPC, GPCv2) - Moved out of mach-imx for GPC - Bunch of tweaks, fixes, etc PMC support for Tegra186 SoC detection support for Renesas RZ/G1H and RZ/G1N Move Tegra flow controller driver from mach directory to drivers/soc - (Power management / CPU power driver) Misc smaller tweaks for other platforms" * tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (60 commits) soc: pm-domain: Fix the mangled urls soc: renesas: rcar-sysc: Add support for R-Car H3 ES2.0 soc: renesas: rcar-sysc: Add support for fixing up power area tables soc: renesas: Register SoC device early soc: imx: gpc: add workaround for i.MX6QP to the GPC PD driver dt-bindings: imx-gpc: add i.MX6 QuadPlus compatible soc: imx: gpc: add defines for domain index soc: imx: Add GPCv2 power gating driver dt-bindings: Add GPCv2 power gating driver ARM/clk: move the ICST library to drivers/clk ARM: plat-versatile: remove stale clock header ARM: keystone: Drop PM domain support for k2g soc: ti: Add ti_sci_pm_domains driver dt-bindings: Add TI SCI PM Domains PM / Domains: Do not check if simple providers have phandle cells PM / Domains: Add generic data pointer to genpd data struct soc/tegra: Add initial flowctrl support for Tegra132/210 soc/tegra: flowctrl: Add basic platform driver soc/tegra: Move Tegra flowctrl driver ARM: tegra: Remove unnecessary inclusion of flowctrl header ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/Kconfig9
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/pata_bk3710.c382
-rw-r--r--drivers/base/power/domain.c2
-rw-r--r--drivers/base/soc.c52
-rw-r--r--drivers/clk/versatile/Kconfig3
-rw-r--r--drivers/clk/versatile/Makefile2
-rw-r--r--drivers/clk/versatile/clk-icst.c1
-rw-r--r--drivers/clk/versatile/clk-icst.h2
-rw-r--r--drivers/clk/versatile/clk-impd1.c1
-rw-r--r--drivers/clk/versatile/clk-realview.c1
-rw-r--r--drivers/clk/versatile/clk-versatile.c1
-rw-r--r--drivers/clk/versatile/icst.c105
-rw-r--r--drivers/clk/versatile/icst.h57
-rw-r--r--drivers/firmware/arm_scpi.c7
-rw-r--r--drivers/firmware/meson/meson_sm.c20
-rw-r--r--drivers/firmware/qcom_scm-32.c18
-rw-r--r--drivers/firmware/qcom_scm-64.c58
-rw-r--r--drivers/firmware/qcom_scm.c18
-rw-r--r--drivers/firmware/qcom_scm.h11
-rw-r--r--drivers/nvmem/meson-efuse.c2
-rw-r--r--drivers/reset/Kconfig14
-rw-r--r--drivers/reset/Makefile3
-rw-r--r--drivers/reset/reset-a10sr.c138
-rw-r--r--drivers/reset/reset-ath79.c27
-rw-r--r--drivers/reset/reset-imx7.c158
-rw-r--r--drivers/reset/reset-meson.c12
-rw-r--r--drivers/reset/reset-oxnas.c6
-rw-r--r--drivers/reset/reset-pistachio.c9
-rw-r--r--drivers/reset/reset-socfpga.c13
-rw-r--r--drivers/reset/reset-sunxi.c18
-rw-r--r--drivers/reset/reset-uniphier.c37
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-rw-r--r--drivers/soc/bcm/brcmstb/common.c9
-rw-r--r--drivers/soc/imx/Kconfig9
-rw-r--r--drivers/soc/imx/Makefile2
-rw-r--r--drivers/soc/imx/gpc.c489
-rw-r--r--drivers/soc/imx/gpcv2.c363
-rw-r--r--drivers/soc/renesas/r8a7795-sysc.c26
-rw-r--r--drivers/soc/renesas/rcar-sysc.c25
-rw-r--r--drivers/soc/renesas/rcar-sysc.h10
-rw-r--r--drivers/soc/renesas/renesas-soc.c18
-rw-r--r--drivers/soc/samsung/Kconfig8
-rw-r--r--drivers/soc/samsung/Makefile4
-rw-r--r--drivers/soc/samsung/exynos-pmu.c22
-rw-r--r--drivers/soc/samsung/exynos-pmu.h3
-rw-r--r--drivers/soc/tegra/Kconfig22
-rw-r--r--drivers/soc/tegra/Makefile4
-rw-r--r--drivers/soc/tegra/flowctrl.c224
-rw-r--r--drivers/soc/tegra/fuse/fuse-tegra.c4
-rw-r--r--drivers/soc/tegra/pmc-tegra186.c169
-rw-r--r--drivers/soc/ti/Kconfig12
-rw-r--r--drivers/soc/ti/Makefile1
-rw-r--r--drivers/soc/ti/ti_sci_pm_domains.c202
-rw-r--r--drivers/soc/zte/zx296718_pm_domains.c1
-rw-r--r--drivers/soc/zte/zx2967_pm_domains.c4
57 files changed, 2710 insertions, 111 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index ff6cb9e4c381..de3eaf051697 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -518,6 +518,15 @@ config PATA_BF54X
If unsure, say N.
+config PATA_BK3710
+ tristate "Palmchip BK3710 PATA support"
+ depends on ARCH_DAVINCI
+ help
+ This option enables support for the integrated IDE controller on
+ the TI DaVinci SoC.
+
+ If unsure, say N.
+
config PATA_CMD64X
tristate "CMD64x PATA support"
depends on PCI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 3048cc100a46..cd931a5eba92 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_PATA_ARTOP) += pata_artop.o
obj-$(CONFIG_PATA_ATIIXP) += pata_atiixp.o
obj-$(CONFIG_PATA_ATP867X) += pata_atp867x.o
obj-$(CONFIG_PATA_BF54X) += pata_bf54x.o
+obj-$(CONFIG_PATA_BK3710) += pata_bk3710.o
obj-$(CONFIG_PATA_CMD64X) += pata_cmd64x.o
obj-$(CONFIG_PATA_CS5520) += pata_cs5520.o
obj-$(CONFIG_PATA_CS5530) += pata_cs5530.o
diff --git a/drivers/ata/pata_bk3710.c b/drivers/ata/pata_bk3710.c
new file mode 100644
index 000000000000..6c3bd5fae3e4
--- /dev/null
+++ b/drivers/ata/pata_bk3710.c
@@ -0,0 +1,382 @@
+/*
+ * Palmchip BK3710 PATA controller driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Based on palm_bk3710.c:
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/ata.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/libata.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#define DRV_NAME "pata_bk3710"
+
+#define BK3710_TF_OFFSET 0x1F0
+#define BK3710_CTL_OFFSET 0x3F6
+
+#define BK3710_BMISP 0x02
+#define BK3710_IDETIMP 0x40
+#define BK3710_UDMACTL 0x48
+#define BK3710_MISCCTL 0x50
+#define BK3710_REGSTB 0x54
+#define BK3710_REGRCVR 0x58
+#define BK3710_DATSTB 0x5C
+#define BK3710_DATRCVR 0x60
+#define BK3710_DMASTB 0x64
+#define BK3710_DMARCVR 0x68
+#define BK3710_UDMASTB 0x6C
+#define BK3710_UDMATRP 0x70
+#define BK3710_UDMAENV 0x74
+#define BK3710_IORDYTMP 0x78
+
+static struct scsi_host_template pata_bk3710_sht = {
+ ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static unsigned int ideclk_period; /* in nanoseconds */
+
+struct pata_bk3710_udmatiming {
+ unsigned int rptime; /* tRP -- Ready to pause time (nsec) */
+ unsigned int cycletime; /* tCYCTYP2/2 -- avg Cycle Time (nsec) */
+ /* tENV is always a minimum of 20 nsec */
+};
+
+static const struct pata_bk3710_udmatiming pata_bk3710_udmatimings[6] = {
+ { 160, 240 / 2 }, /* UDMA Mode 0 */
+ { 125, 160 / 2 }, /* UDMA Mode 1 */
+ { 100, 120 / 2 }, /* UDMA Mode 2 */
+ { 100, 90 / 2 }, /* UDMA Mode 3 */
+ { 100, 60 / 2 }, /* UDMA Mode 4 */
+ { 85, 40 / 2 }, /* UDMA Mode 5 */
+};
+
+static void pata_bk3710_setudmamode(void __iomem *base, unsigned int dev,
+ unsigned int mode)
+{
+ u32 val32;
+ u16 val16;
+ u8 tenv, trp, t0;
+
+ /* DMA Data Setup */
+ t0 = DIV_ROUND_UP(pata_bk3710_udmatimings[mode].cycletime,
+ ideclk_period) - 1;
+ tenv = DIV_ROUND_UP(20, ideclk_period) - 1;
+ trp = DIV_ROUND_UP(pata_bk3710_udmatimings[mode].rptime,
+ ideclk_period) - 1;
+
+ /* udmastb Ultra DMA Access Strobe Width */
+ val32 = ioread32(base + BK3710_UDMASTB) & (0xFF << (dev ? 0 : 8));
+ val32 |= t0 << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_UDMASTB);
+
+ /* udmatrp Ultra DMA Ready to Pause Time */
+ val32 = ioread32(base + BK3710_UDMATRP) & (0xFF << (dev ? 0 : 8));
+ val32 |= trp << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_UDMATRP);
+
+ /* udmaenv Ultra DMA envelop Time */
+ val32 = ioread32(base + BK3710_UDMAENV) & (0xFF << (dev ? 0 : 8));
+ val32 |= tenv << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_UDMAENV);
+
+ /* Enable UDMA for Device */
+ val16 = ioread16(base + BK3710_UDMACTL) | (1 << dev);
+ iowrite16(val16, base + BK3710_UDMACTL);
+}
+
+static void pata_bk3710_setmwdmamode(void __iomem *base, unsigned int dev,
+ unsigned short min_cycle,
+ unsigned int mode)
+{
+ const struct ata_timing *t;
+ int cycletime;
+ u32 val32;
+ u16 val16;
+ u8 td, tkw, t0;
+
+ t = ata_timing_find_mode(mode);
+ cycletime = max_t(int, t->cycle, min_cycle);
+
+ /* DMA Data Setup */
+ t0 = DIV_ROUND_UP(cycletime, ideclk_period);
+ td = DIV_ROUND_UP(t->active, ideclk_period);
+ tkw = t0 - td - 1;
+ td--;
+
+ val32 = ioread32(base + BK3710_DMASTB) & (0xFF << (dev ? 0 : 8));
+ val32 |= td << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_DMASTB);
+
+ val32 = ioread32(base + BK3710_DMARCVR) & (0xFF << (dev ? 0 : 8));
+ val32 |= tkw << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_DMARCVR);
+
+ /* Disable UDMA for Device */
+ val16 = ioread16(base + BK3710_UDMACTL) & ~(1 << dev);
+ iowrite16(val16, base + BK3710_UDMACTL);
+}
+
+static void pata_bk3710_set_dmamode(struct ata_port *ap,
+ struct ata_device *adev)
+{
+ void __iomem *base = (void __iomem *)ap->ioaddr.bmdma_addr;
+ int is_slave = adev->devno;
+ const u8 xferspeed = adev->dma_mode;
+
+ if (xferspeed >= XFER_UDMA_0)
+ pata_bk3710_setudmamode(base, is_slave,
+ xferspeed - XFER_UDMA_0);
+ else
+ pata_bk3710_setmwdmamode(base, is_slave,
+ adev->id[ATA_ID_EIDE_DMA_MIN],
+ xferspeed);
+}
+
+static void pata_bk3710_setpiomode(void __iomem *base, struct ata_device *pair,
+ unsigned int dev, unsigned int cycletime,
+ unsigned int mode)
+{
+ const struct ata_timing *t;
+ u32 val32;
+ u8 t2, t2i, t0;
+
+ t = ata_timing_find_mode(XFER_PIO_0 + mode);
+
+ /* PIO Data Setup */
+ t0 = DIV_ROUND_UP(cycletime, ideclk_period);
+ t2 = DIV_ROUND_UP(t->active, ideclk_period);
+
+ t2i = t0 - t2 - 1;
+ t2--;
+
+ val32 = ioread32(base + BK3710_DATSTB) & (0xFF << (dev ? 0 : 8));
+ val32 |= t2 << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_DATSTB);
+
+ val32 = ioread32(base + BK3710_DATRCVR) & (0xFF << (dev ? 0 : 8));
+ val32 |= t2i << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_DATRCVR);
+
+ /* FIXME: this is broken also in the old driver */
+ if (pair) {
+ u8 mode2 = pair->pio_mode - XFER_PIO_0;
+
+ if (mode2 < mode)
+ mode = mode2;
+ }
+
+ /* TASKFILE Setup */
+ t0 = DIV_ROUND_UP(t->cyc8b, ideclk_period);
+ t2 = DIV_ROUND_UP(t->act8b, ideclk_period);
+
+ t2i = t0 - t2 - 1;
+ t2--;
+
+ val32 = ioread32(base + BK3710_REGSTB) & (0xFF << (dev ? 0 : 8));
+ val32 |= t2 << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_REGSTB);
+
+ val32 = ioread32(base + BK3710_REGRCVR) & (0xFF << (dev ? 0 : 8));
+ val32 |= t2i << (dev ? 8 : 0);
+ iowrite32(val32, base + BK3710_REGRCVR);
+}
+
+static void pata_bk3710_set_piomode(struct ata_port *ap,
+ struct ata_device *adev)
+{
+ void __iomem *base = (void __iomem *)ap->ioaddr.bmdma_addr;
+ struct ata_device *pair = ata_dev_pair(adev);
+ const struct ata_timing *t = ata_timing_find_mode(adev->pio_mode);
+ const u16 *id = adev->id;
+ unsigned int cycle_time = 0;
+ int is_slave = adev->devno;
+ const u8 pio = adev->pio_mode - XFER_PIO_0;
+
+ if (id[ATA_ID_FIELD_VALID] & 2) {
+ if (ata_id_has_iordy(id))
+ cycle_time = id[ATA_ID_EIDE_PIO_IORDY];
+ else
+ cycle_time = id[ATA_ID_EIDE_PIO];
+
+ /* conservative "downgrade" for all pre-ATA2 drives */
+ if (pio < 3 && cycle_time < t->cycle)
+ cycle_time = 0; /* use standard timing */
+ }
+
+ if (!cycle_time)
+ cycle_time = t->cycle;
+
+ pata_bk3710_setpiomode(base, pair, is_slave, cycle_time, pio);
+}
+
+static void pata_bk3710_chipinit(void __iomem *base)
+{
+ /*
+ * REVISIT: the ATA reset signal needs to be managed through a
+ * GPIO, which means it should come from platform_data. Until
+ * we get and use such information, we have to trust that things
+ * have been reset before we get here.
+ */
+
+ /*
+ * Program the IDETIMP Register Value based on the following assumptions
+ *
+ * (ATA_IDETIMP_IDEEN , ENABLE ) |
+ * (ATA_IDETIMP_PREPOST1 , DISABLE) |
+ * (ATA_IDETIMP_PREPOST0 , DISABLE) |
+ *
+ * DM6446 silicon rev 2.1 and earlier have no observed net benefit
+ * from enabling prefetch/postwrite.
+ */
+ iowrite16(BIT(15), base + BK3710_IDETIMP);
+
+ /*
+ * UDMACTL Ultra-ATA DMA Control
+ * (ATA_UDMACTL_UDMAP1 , 0 ) |
+ * (ATA_UDMACTL_UDMAP0 , 0 )
+ *
+ */
+ iowrite16(0, base + BK3710_UDMACTL);
+
+ /*
+ * MISCCTL Miscellaneous Conrol Register
+ * (ATA_MISCCTL_HWNHLD1P , 1 cycle)
+ * (ATA_MISCCTL_HWNHLD0P , 1 cycle)
+ * (ATA_MISCCTL_TIMORIDE , 1)
+ */
+ iowrite32(0x001, base + BK3710_MISCCTL);
+
+ /*
+ * IORDYTMP IORDY Timer for Primary Register
+ * (ATA_IORDYTMP_IORDYTMP , DISABLE)
+ */
+ iowrite32(0, base + BK3710_IORDYTMP);
+
+ /*
+ * Configure BMISP Register
+ * (ATA_BMISP_DMAEN1 , DISABLE ) |
+ * (ATA_BMISP_DMAEN0 , DISABLE ) |
+ * (ATA_BMISP_IORDYINT , CLEAR) |
+ * (ATA_BMISP_INTRSTAT , CLEAR) |
+ * (ATA_BMISP_DMAERROR , CLEAR)
+ */
+ iowrite16(0xE, base + BK3710_BMISP);
+
+ pata_bk3710_setpiomode(base, NULL, 0, 600, 0);
+ pata_bk3710_setpiomode(base, NULL, 1, 600, 0);
+}
+
+static struct ata_port_operations pata_bk3710_ports_ops = {
+ .inherits = &ata_bmdma_port_ops,
+ .cable_detect = ata_cable_80wire,
+
+ .set_piomode = pata_bk3710_set_piomode,
+ .set_dmamode = pata_bk3710_set_dmamode,
+};
+
+static int __init pata_bk3710_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+ struct resource *mem;
+ struct ata_host *host;
+ struct ata_port *ap;
+ void __iomem *base;
+ unsigned long rate;
+ int irq;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return -ENODEV;
+
+ clk_enable(clk);
+ rate = clk_get_rate(clk);
+ if (!rate)
+ return -EINVAL;
+
+ /* NOTE: round *down* to meet minimum timings; we count in clocks */
+ ideclk_period = 1000000000UL / rate;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ pr_err(DRV_NAME ": failed to get IRQ resource\n");
+ return irq;
+ }
+
+ base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* configure the Palmchip controller */
+ pata_bk3710_chipinit(base);
+
+ /* allocate host */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+ ap = host->ports[0];
+
+ ap->ops = &pata_bk3710_ports_ops;
+ ap->pio_mask = ATA_PIO4;
+ ap->mwdma_mask = ATA_MWDMA2;
+ ap->udma_mask = rate < 100000000 ? ATA_UDMA4 : ATA_UDMA5;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+
+ ap->ioaddr.data_addr = base + BK3710_TF_OFFSET;
+ ap->ioaddr.error_addr = base + BK3710_TF_OFFSET + 1;
+ ap->ioaddr.feature_addr = base + BK3710_TF_OFFSET + 1;
+ ap->ioaddr.nsect_addr = base + BK3710_TF_OFFSET + 2;
+ ap->ioaddr.lbal_addr = base + BK3710_TF_OFFSET + 3;
+ ap->ioaddr.lbam_addr = base + BK3710_TF_OFFSET + 4;
+ ap->ioaddr.lbah_addr = base + BK3710_TF_OFFSET + 5;
+ ap->ioaddr.device_addr = base + BK3710_TF_OFFSET + 6;
+ ap->ioaddr.status_addr = base + BK3710_TF_OFFSET + 7;
+ ap->ioaddr.command_addr = base + BK3710_TF_OFFSET + 7;
+
+ ap->ioaddr.altstatus_addr = base + BK3710_CTL_OFFSET;
+ ap->ioaddr.ctl_addr = base + BK3710_CTL_OFFSET;
+
+ ap->ioaddr.bmdma_addr = base;
+
+ ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx",
+ (unsigned long)base + BK3710_TF_OFFSET,
+ (unsigned long)base + BK3710_CTL_OFFSET);
+
+ /* activate */
+ return ata_host_activate(host, irq, ata_sff_interrupt, 0,
+ &pata_bk3710_sht);
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:palm_bk3710");
+
+static struct platform_driver pata_bk3710_driver = {
+ .driver = {
+ .name = "palm_bk3710",
+ },
+};
+
+static int __init pata_bk3710_init(void)
+{
+ return platform_driver_probe(&pata_bk3710_driver, pata_bk3710_probe);
+}
+
+module_init(pata_bk3710_init);
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index ad196427b4f2..da49a8383dc3 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1636,8 +1636,6 @@ static struct generic_pm_domain *genpd_xlate_simple(
struct of_phandle_args *genpdspec,
void *data)
{
- if (genpdspec->args_count != 0)
- return ERR_PTR(-EINVAL);
return data;
}
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index dc26e5949a32..909dedae4c4e 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -109,15 +109,18 @@ static void soc_release(struct device *dev)
kfree(soc_dev);
}
+static struct soc_device_attribute *early_soc_dev_attr;
+
struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
{
struct soc_device *soc_dev;
int ret;
if (!soc_bus_type.p) {
- ret = bus_register(&soc_bus_type);
- if (ret)
- goto out1;
+ if (early_soc_dev_attr)
+ return ERR_PTR(-EBUSY);
+ early_soc_dev_attr = soc_dev_attr;
+ return NULL;
}
soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
@@ -159,45 +162,53 @@ void soc_device_unregister(struct soc_device *soc_dev)
ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
device_unregister(&soc_dev->dev);
+ early_soc_dev_attr = NULL;
}
static int __init soc_bus_register(void)
{
- if (soc_bus_type.p)
- return 0;
+ int ret;
- return bus_register(&soc_bus_type);
+ ret = bus_register(&soc_bus_type);
+ if (ret)
+ return ret;
+
+ if (early_soc_dev_attr)
+ return PTR_ERR(soc_device_register(early_soc_dev_attr));
+
+ return 0;
}
core_initcall(soc_bus_register);
-static int soc_device_match_one(struct device *dev, void *arg)
+static int soc_device_match_attr(const struct soc_device_attribute *attr,
+ const struct soc_device_attribute *match)
{
- struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
- const struct soc_device_attribute *match = arg;
-
if (match->machine &&
- (!soc_dev->attr->machine ||
- !glob_match(match->machine, soc_dev->attr->machine)))
+ (!attr->machine || !glob_match(match->machine, attr->machine)))
return 0;
if (match->family &&
- (!soc_dev->attr->family ||
- !glob_match(match->family, soc_dev->attr->family)))
+ (!attr->family || !glob_match(match->family, attr->family)))
return 0;
if (match->revision &&
- (!soc_dev->attr->revision ||
- !glob_match(match->revision, soc_dev->attr->revision)))
+ (!attr->revision || !glob_match(match->revision, attr->revision)))
return 0;
if (match->soc_id &&
- (!soc_dev->attr->soc_id ||
- !glob_match(match->soc_id, soc_dev->attr->soc_id)))
+ (!attr->soc_id || !glob_match(match->soc_id, attr->soc_id)))
return 0;
return 1;
}
+static int soc_device_match_one(struct device *dev, void *arg)
+{
+ struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
+
+ return soc_device_match_attr(soc_dev->attr, arg);
+}
+
/*
* soc_device_match - identify the SoC in the machine
* @matches: zero-terminated array of possible matches
@@ -230,6 +241,11 @@ const struct soc_device_attribute *soc_device_match(
break;
ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches,
soc_device_match_one);
+ if (ret < 0 && early_soc_dev_attr)
+ ret = soc_device_match_attr(early_soc_dev_attr,
+ matches);
+ if (ret < 0)
+ return NULL;
if (!ret)
matches++;
else
diff --git a/drivers/clk/versatile/Kconfig b/drivers/clk/versatile/Kconfig
index a6da2aa09f83..8aa875f25239 100644
--- a/drivers/clk/versatile/Kconfig
+++ b/drivers/clk/versatile/Kconfig
@@ -1,3 +1,6 @@
+config ICST
+ bool
+
config COMMON_CLK_VERSATILE
bool "Clock driver for ARM Reference designs"
depends on ARCH_INTEGRATOR || ARCH_REALVIEW || \
diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile
index 8ff03744fe98..794130402c8d 100644
--- a/drivers/clk/versatile/Makefile
+++ b/drivers/clk/versatile/Makefile
@@ -1,5 +1,5 @@
# Makefile for Versatile-specific clocks
-obj-$(CONFIG_ICST) += clk-icst.o clk-versatile.o
+obj-$(CONFIG_ICST) += icst.o clk-icst.o clk-versatile.o
obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o
obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
obj-$(CONFIG_CLK_SP810) += clk-sp810.o
diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c
index 4faa94440779..09fbe66f1f11 100644
--- a/drivers/clk/versatile/clk-icst.c
+++ b/drivers/clk/versatile/clk-icst.c
@@ -22,6 +22,7 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
+#include "icst.h"
#include "clk-icst.h"
/* Magic unlocking token used on all Versatile boards */
diff --git a/drivers/clk/versatile/clk-icst.h b/drivers/clk/versatile/clk-icst.h
index 04e6f0aef588..5add02ebec5d 100644
--- a/drivers/clk/versatile/clk-icst.h
+++ b/drivers/clk/versatile/clk-icst.h
@@ -1,5 +1,3 @@
-#include <asm/hardware/icst.h>
-
/**
* struct clk_icst_desc - descriptor for the ICST VCO
* @params: ICST parameters
diff --git a/drivers/clk/versatile/clk-impd1.c b/drivers/clk/versatile/clk-impd1.c
index 74c3216dbb00..401558bfc409 100644
--- a/drivers/clk/versatile/clk-impd1.c
+++ b/drivers/clk/versatile/clk-impd1.c
@@ -12,6 +12,7 @@
#include <linux/io.h>
#include <linux/platform_data/clk-integrator.h>
+#include "icst.h"
#include "clk-icst.h"
#define IMPD1_OSC1 0x00
diff --git a/drivers/clk/versatile/clk-realview.c b/drivers/clk/versatile/clk-realview.c
index c56efc70ac16..6fdfee3232f4 100644
--- a/drivers/clk/versatile/clk-realview.c
+++ b/drivers/clk/versatile/clk-realview.c
@@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/clk-provider.h>
+#include "icst.h"
#include "clk-icst.h"
#define REALVIEW_SYS_OSC0_OFFSET 0x0C
diff --git a/drivers/clk/versatile/clk-versatile.c b/drivers/clk/versatile/clk-versatile.c
index a89a927567e0..d6960de64d4a 100644
--- a/drivers/clk/versatile/clk-versatile.c
+++ b/drivers/clk/versatile/clk-versatile.c
@@ -12,6 +12,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
+#include "icst.h"
#include "clk-icst.h"
#define INTEGRATOR_HDR_LOCK_OFFSET 0x14
diff --git a/drivers/clk/versatile/icst.c b/drivers/clk/versatile/icst.c
new file mode 100644
index 000000000000..de2af63a3aad
--- /dev/null
+++ b/drivers/clk/versatile/icst.c
@@ -0,0 +1,105 @@
+/*
+ * linux/arch/arm/common/icst307.c
+ *
+ * Copyright (C) 2003 Deep Blue Solutions, Ltd, 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.
+ *
+ * Support functions for calculating clocks/divisors for the ICST307
+ * clock generators. See http://www.idt.com/ for more information
+ * on these devices.
+ *
+ * This is an almost identical implementation to the ICST525 clock generator.
+ * The s2div and idx2s files are different
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <asm/div64.h>
+#include "icst.h"
+
+/*
+ * Divisors for each OD setting.
+ */
+const unsigned char icst307_s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 };
+const unsigned char icst525_s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 };
+EXPORT_SYMBOL(icst307_s2div);
+EXPORT_SYMBOL(icst525_s2div);
+
+unsigned long icst_hz(const struct icst_params *p, struct icst_vco vco)
+{
+ u64 dividend = p->ref * 2 * (u64)(vco.v + 8);
+ u32 divisor = (vco.r + 2) * p->s2div[vco.s];
+
+ do_div(dividend, divisor);
+ return (unsigned long)dividend;
+}
+
+EXPORT_SYMBOL(icst_hz);
+
+/*
+ * Ascending divisor S values.
+ */
+const unsigned char icst307_idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 };
+const unsigned char icst525_idx2s[8] = { 1, 3, 4, 7, 5, 2, 6, 0 };
+EXPORT_SYMBOL(icst307_idx2s);
+EXPORT_SYMBOL(icst525_idx2s);
+
+struct icst_vco
+icst_hz_to_vco(const struct icst_params *p, unsigned long freq)
+{
+ struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+ unsigned long f;
+ unsigned int i = 0, rd, best = (unsigned int)-1;
+
+ /*
+ * First, find the PLL output divisor such
+ * that the PLL output is within spec.
+ */
+ do {
+ f = freq * p->s2div[p->idx2s[i]];
+
+ if (f > p->vco_min && f <= p->vco_max)
+ break;
+ i++;
+ } while (i < 8);
+
+ if (i >= 8)
+ return vco;
+
+ vco.s = p->idx2s[i];
+
+ /*
+ * Now find the closest divisor combination
+ * which gives a PLL output of 'f'.
+ */
+ for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+ unsigned long fref_div, f_pll;
+ unsigned int vd;
+ int f_diff;
+
+ fref_div = (2 * p->ref) / rd;
+
+ vd = (f + fref_div / 2) / fref_div;
+ if (vd < p->vd_min || vd > p->vd_max)
+ continue;
+
+ f_pll = fref_div * vd;
+ f_diff = f_pll - f;
+ if (f_diff < 0)
+ f_diff = -f_diff;
+
+ if ((unsigned)f_diff < best) {
+ vco.v = vd - 8;
+ vco.r = rd - 2;
+ if (f_diff == 0)
+ break;
+ best = f_diff;
+ }
+ }
+
+ return vco;
+}
+
+EXPORT_SYMBOL(icst_hz_to_vco);
diff --git a/drivers/clk/versatile/icst.h b/drivers/clk/versatile/icst.h
new file mode 100644
index 000000000000..7519bba03b04
--- /dev/null
+++ b/drivers/clk/versatile/icst.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2003 Deep Blue Solutions, Ltd, 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.
+ *
+ * Support functions for calculating clocks/divisors for the ICST
+ * clock generators. See http://www.idt.com/ for more information
+ * on these devices.
+ */
+#ifndef ICST_H
+#define ICST_H
+
+struct icst_params {
+ unsigned long ref;
+ unsigned long vco_max; /* inclusive */
+ unsigned long vco_min; /* exclusive */
+ unsigned short vd_min; /* inclusive */
+ unsigned short vd_max; /* inclusive */
+ unsigned char rd_min; /* inclusive */
+ unsigned char rd_max; /* inclusive */
+ const unsigned char *s2div; /* chip specific s2div array */
+ const unsigned char *idx2s; /* chip specific idx2s array */
+};
+
+struct icst_vco {
+ unsigned short v;
+ unsigned char r;
+ unsigned char s;
+};
+
+unsigned long icst_hz(const struct icst_params *p, struct icst_vco vco);
+struct icst_vco icst_hz_to_vco(const struct icst_params *p, unsigned long freq);
+
+/*
+ * ICST307 VCO frequency must be between 6MHz and 200MHz (3.3 or 5V).
+ * This frequency is pre-output divider.
+ */
+#define ICST307_VCO_MIN 6000000
+#define ICST307_VCO_MAX 200000000
+
+extern const unsigned char icst307_s2div[];
+extern const unsigned char icst307_idx2s[];
+
+/*
+ * ICST525 VCO frequency must be between 10MHz and 200MHz (3V) or 320MHz (5V).
+ * This frequency is pre-output divider.
+ */
+#define ICST525_VCO_MIN 10000000
+#define ICST525_VCO_MAX_3V 200000000
+#define ICST525_VCO_MAX_5V 320000000
+
+extern const unsigned char icst525_s2div[];
+extern const unsigned char icst525_idx2s[];
+
+#endif
diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
index 9ad0b1934be9..f6cfc31d34c7 100644
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -538,7 +538,7 @@ static int scpi_send_message(u8 idx, void *tx_buf, unsigned int tx_len,
msg->tx_len = tx_len;
msg->rx_buf = rx_buf;
msg->rx_len = rx_len;
- init_completion(&msg->done);
+ reinit_completion(&msg->done);
ret = mbox_send_message(scpi_chan->chan, msg);
if (ret < 0 || !rx_buf)
@@ -872,8 +872,11 @@ static int scpi_alloc_xfer_list(struct device *dev, struct scpi_chan *ch)
return -ENOMEM;
ch->xfers = xfers;
- for (i = 0; i < MAX_SCPI_XFERS; i++, xfers++)
+ for (i = 0; i < MAX_SCPI_XFERS; i++, xfers++) {
+ init_completion(&xfers->done);
list_add_tail(&xfers->node, &ch->xfers_list);
+ }
+
return 0;
}
diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c
index b0d254930ed3..ff204421117b 100644
--- a/drivers/firmware/meson/meson_sm.c
+++ b/drivers/firmware/meson/meson_sm.c
@@ -127,6 +127,7 @@ EXPORT_SYMBOL(meson_sm_call);
* meson_sm_call_read - retrieve data from secure-monitor
*
* @buffer: Buffer to store the retrieved data
+ * @bsize: Size of the buffer
* @cmd_index: Index of the SMC32 function ID
* @arg0: SMC32 Argument 0
* @arg1: SMC32 Argument 1
@@ -135,11 +136,14 @@ EXPORT_SYMBOL(meson_sm_call);
* @arg4: SMC32 Argument 4
*
* Return: size of read data on success, a negative value on error
+ * When 0 is returned there is no guarantee about the amount of
+ * data read and bsize bytes are copied in buffer.
*/
-int meson_sm_call_read(void *buffer, unsigned int cmd_index, u32 arg0,
- u32 arg1, u32 arg2, u32 arg3, u32 arg4)
+int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index,
+ u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
{
u32 size;
+ int ret;
if (!fw.chip)
return -ENOENT;
@@ -147,16 +151,24 @@ int meson_sm_call_read(void *buffer, unsigned int cmd_index, u32 arg0,
if (!fw.chip->cmd_shmem_out_base)
return -EINVAL;
+ if (bsize > fw.chip->shmem_size)
+ return -EINVAL;
+
if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
return -EINVAL;
- if (!size || size > fw.chip->shmem_size)
+ if (size > bsize)
return -EINVAL;
+ ret = size;
+
+ if (!size)
+ size = bsize;
+
if (buffer)
memcpy(buffer, fw.sm_shmem_out_base, size);
- return size;
+ return ret;
}
EXPORT_SYMBOL(meson_sm_call_read);
diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c
index 8ad226c60374..93e3b96b6dfa 100644
--- a/drivers/firmware/qcom_scm-32.c
+++ b/drivers/firmware/qcom_scm-32.c
@@ -578,3 +578,21 @@ int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
return ret ? : le32_to_cpu(scm_ret);
}
+
+int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id,
+ u32 spare)
+{
+ return -ENODEV;
+}
+
+int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare,
+ size_t *size)
+{
+ return -ENODEV;
+}
+
+int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size,
+ u32 spare)
+{
+ return -ENODEV;
+}
diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c
index c9332590e8c6..6e6d561708e2 100644
--- a/drivers/firmware/qcom_scm-64.c
+++ b/drivers/firmware/qcom_scm-64.c
@@ -381,3 +381,61 @@ int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
return ret ? : res.a1;
}
+
+int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare)
+{
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+ int ret;
+
+ desc.args[0] = device_id;
+ desc.args[1] = spare;
+ desc.arginfo = QCOM_SCM_ARGS(2);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, QCOM_SCM_RESTORE_SEC_CFG,
+ &desc, &res);
+
+ return ret ? : res.a1;
+}
+
+int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare,
+ size_t *size)
+{
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+ int ret;
+
+ desc.args[0] = spare;
+ desc.arginfo = QCOM_SCM_ARGS(1);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP,
+ QCOM_SCM_IOMMU_SECURE_PTBL_SIZE, &desc, &res);
+
+ if (size)
+ *size = res.a1;
+
+ return ret ? : res.a2;
+}
+
+int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size,
+ u32 spare)
+{
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+ int ret;
+
+ desc.args[0] = addr;
+ desc.args[1] = size;
+ desc.args[2] = spare;
+ desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL,
+ QCOM_SCM_VAL);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP,
+ QCOM_SCM_IOMMU_SECURE_PTBL_INIT, &desc, &res);
+
+ /* the pg table has been initialized already, ignore the error */
+ if (ret == -EPERM)
+ ret = 0;
+
+ return ret;
+}
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index d987bcc7489d..bb16510d75ba 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -315,6 +315,24 @@ static const struct reset_control_ops qcom_scm_pas_reset_ops = {
.deassert = qcom_scm_pas_reset_deassert,
};
+int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare)
+{
+ return __qcom_scm_restore_sec_cfg(__scm->dev, device_id, spare);
+}
+EXPORT_SYMBOL(qcom_scm_restore_sec_cfg);
+
+int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size)
+{
+ return __qcom_scm_iommu_secure_ptbl_size(__scm->dev, spare, size);
+}
+EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size);
+
+int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare)
+{
+ return __qcom_scm_iommu_secure_ptbl_init(__scm->dev, addr, size, spare);
+}
+EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init);
+
/**
* qcom_scm_is_available() - Checks if SCM is available
*/
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h
index 6a0f15469344..9bea691f30fb 100644
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -85,4 +85,15 @@ static inline int qcom_scm_remap_error(int err)
return -EINVAL;
}
+#define QCOM_SCM_SVC_MP 0xc
+#define QCOM_SCM_RESTORE_SEC_CFG 2
+extern int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id,
+ u32 spare);
+#define QCOM_SCM_IOMMU_SECURE_PTBL_SIZE 3
+#define QCOM_SCM_IOMMU_SECURE_PTBL_INIT 4
+extern int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare,
+ size_t *size);
+extern int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr,
+ u32 size, u32 spare);
+
#endif
diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c
index f207c3b10482..70bfc9839bb2 100644
--- a/drivers/nvmem/meson-efuse.c
+++ b/drivers/nvmem/meson-efuse.c
@@ -27,7 +27,7 @@ static int meson_efuse_read(void *context, unsigned int offset,
u8 *buf = val;
int ret;
- ret = meson_sm_call_read(buf, SM_EFUSE_READ, offset,
+ ret = meson_sm_call_read(buf, bytes, SM_EFUSE_READ, offset,
bytes, 0, 0, 0);
if (ret < 0)
return ret;
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index f4cdfe94b9ec..d21c07ccc94e 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -14,6 +14,13 @@ menuconfig RESET_CONTROLLER
if RESET_CONTROLLER
+config RESET_A10SR
+ tristate "Altera Arria10 System Resource Reset"
+ depends on MFD_ALTERA_A10SR
+ help
+ This option enables support for the external reset functions for
+ peripheral PHYs on the Altera Arria10 System Resource Chip.
+
config RESET_ATH79
bool "AR71xx Reset Driver" if COMPILE_TEST
default ATH79
@@ -27,6 +34,13 @@ config RESET_BERLIN
help
This enables the reset controller driver for Marvell Berlin SoCs.
+config RESET_IMX7
+ bool "i.MX7 Reset Driver" if COMPILE_TEST
+ default SOC_IMX7D
+ select MFD_SYSCON
+ help
+ This enables the reset controller driver for i.MX7 SoCs.
+
config RESET_LPC18XX
bool "LPC18xx/43xx Reset Driver" if COMPILE_TEST
default ARCH_LPC18XX
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 2cd3f6c45165..02a74db94339 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -2,8 +2,10 @@ obj-y += core.o
obj-y += hisilicon/
obj-$(CONFIG_ARCH_STI) += sti/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
+obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o
obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
+obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
@@ -15,3 +17,4 @@ obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
obj-$(CONFIG_RESET_ZX2967) += reset-zx2967.o
obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
+
diff --git a/drivers/reset/reset-a10sr.c b/drivers/reset/reset-a10sr.c
new file mode 100644
index 000000000000..37496bd27fa2
--- /dev/null
+++ b/drivers/reset/reset-a10sr.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright Intel Corporation (C) 2017. All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Reset driver for Altera Arria10 MAX5 System Resource Chip
+ *
+ * Adapted from reset-socfpga.c
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/altera-a10sr.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/reset/altr,rst-mgr-a10sr.h>
+
+struct a10sr_reset {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+};
+
+static inline struct a10sr_reset *to_a10sr_rst(struct reset_controller_dev *rc)
+{
+ return container_of(rc, struct a10sr_reset, rcdev);
+}
+
+static inline int a10sr_reset_shift(unsigned long id)
+{
+ switch (id) {
+ case A10SR_RESET_ENET_HPS:
+ return 1;
+ case A10SR_RESET_PCIE:
+ case A10SR_RESET_FILE:
+ case A10SR_RESET_BQSPI:
+ case A10SR_RESET_USB:
+ return id + 11;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int a10sr_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct a10sr_reset *a10r = to_a10sr_rst(rcdev);
+ int offset = a10sr_reset_shift(id);
+ u8 mask = ALTR_A10SR_REG_BIT_MASK(offset);
+ int index = ALTR_A10SR_HPS_RST_REG + ALTR_A10SR_REG_OFFSET(offset);
+
+ return regmap_update_bits(a10r->regmap, index, mask, assert ? 0 : mask);
+}
+
+static int a10sr_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return a10sr_reset_update(rcdev, id, true);
+}
+
+static int a10sr_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return a10sr_reset_update(rcdev, id, false);
+}
+
+static int a10sr_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+ struct a10sr_reset *a10r = to_a10sr_rst(rcdev);
+ int offset = a10sr_reset_shift(id);
+ u8 mask = ALTR_A10SR_REG_BIT_MASK(offset);
+ int index = ALTR_A10SR_HPS_RST_REG + ALTR_A10SR_REG_OFFSET(offset);
+ unsigned int value;
+
+ ret = regmap_read(a10r->regmap, index, &value);
+ if (ret < 0)
+ return ret;
+
+ return !!(value & mask);
+}
+
+static const struct reset_control_ops a10sr_reset_ops = {
+ .assert = a10sr_reset_assert,
+ .deassert = a10sr_reset_deassert,
+ .status = a10sr_reset_status,
+};
+
+static int a10sr_reset_probe(struct platform_device *pdev)
+{
+ struct altr_a10sr *a10sr = dev_get_drvdata(pdev->dev.parent);
+ struct a10sr_reset *a10r;
+
+ a10r = devm_kzalloc(&pdev->dev, sizeof(struct a10sr_reset),
+ GFP_KERNEL);
+ if (!a10r)
+ return -ENOMEM;
+
+ a10r->rcdev.owner = THIS_MODULE;
+ a10r->rcdev.nr_resets = A10SR_RESET_NUM;
+ a10r->rcdev.ops = &a10sr_reset_ops;
+ a10r->rcdev.of_node = pdev->dev.of_node;
+ a10r->regmap = a10sr->regmap;
+
+ platform_set_drvdata(pdev, a10r);
+
+ return devm_reset_controller_register(&pdev->dev, &a10r->rcdev);
+}
+
+static const struct of_device_id a10sr_reset_of_match[] = {
+ { .compatible = "altr,a10sr-reset" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, a10sr_reset_of_match);
+
+static struct platform_driver a10sr_reset_driver = {
+ .probe = a10sr_reset_probe,
+ .driver = {
+ .name = "altr_a10sr_reset",
+ },
+};
+module_platform_driver(a10sr_reset_driver);
+
+MODULE_AUTHOR("Thor Thayer <thor.thayer@linux.intel.com>");
+MODULE_DESCRIPTION("Altera Arria10 System Resource Reset Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c
index 6b97631f5489..2674880e5492 100644
--- a/drivers/reset/reset-ath79.c
+++ b/drivers/reset/reset-ath79.c
@@ -1,4 +1,7 @@
/*
+ * AR71xx Reset Controller Driver
+ * Author: Alban Bedel
+ *
* Copyright (C) 2015 Alban Bedel <albeu@free.fr>
*
* This program is free software; you can redistribute it and/or modify
@@ -13,7 +16,7 @@
*/
#include <linux/io.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/reboot.h>
@@ -127,31 +130,17 @@ static int ath79_reset_probe(struct platform_device *pdev)
return 0;
}
-static int ath79_reset_remove(struct platform_device *pdev)
-{
- struct ath79_reset *ath79_reset = platform_get_drvdata(pdev);
-
- unregister_restart_handler(&ath79_reset->restart_nb);
-
- return 0;
-}
-
static const struct of_device_id ath79_reset_dt_ids[] = {
{ .compatible = "qca,ar7100-reset", },
{ },
};
-MODULE_DEVICE_TABLE(of, ath79_reset_dt_ids);
static struct platform_driver ath79_reset_driver = {
.probe = ath79_reset_probe,
- .remove = ath79_reset_remove,
.driver = {
- .name = "ath79-reset",
- .of_match_table = ath79_reset_dt_ids,
+ .name = "ath79-reset",
+ .of_match_table = ath79_reset_dt_ids,
+ .suppress_bind_attrs = true,
},
};
-module_platform_driver(ath79_reset_driver);
-
-MODULE_AUTHOR("Alban Bedel <albeu@free.fr>");
-MODULE_DESCRIPTION("AR71xx Reset Controller Driver");
-MODULE_LICENSE("GPL");
+builtin_platform_driver(ath79_reset_driver);
diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c
new file mode 100644
index 000000000000..4db177bc89bc
--- /dev/null
+++ b/drivers/reset/reset-imx7.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * i.MX7 System Reset Controller (SRC) driver
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/regmap.h>
+#include <dt-bindings/reset/imx7-reset.h>
+
+struct imx7_src {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+};
+
+enum imx7_src_registers {
+ SRC_A7RCR0 = 0x0004,
+ SRC_M4RCR = 0x000c,
+ SRC_ERCR = 0x0014,
+ SRC_HSICPHY_RCR = 0x001c,
+ SRC_USBOPHY1_RCR = 0x0020,
+ SRC_USBOPHY2_RCR = 0x0024,
+ SRC_MIPIPHY_RCR = 0x0028,
+ SRC_PCIEPHY_RCR = 0x002c,
+ SRC_DDRC_RCR = 0x1000,
+};
+
+struct imx7_src_signal {
+ unsigned int offset, bit;
+};
+
+static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = {
+ [IMX7_RESET_A7_CORE_POR_RESET0] = { SRC_A7RCR0, BIT(0) },
+ [IMX7_RESET_A7_CORE_POR_RESET1] = { SRC_A7RCR0, BIT(1) },
+ [IMX7_RESET_A7_CORE_RESET0] = { SRC_A7RCR0, BIT(4) },
+ [IMX7_RESET_A7_CORE_RESET1] = { SRC_A7RCR0, BIT(5) },
+ [IMX7_RESET_A7_DBG_RESET0] = { SRC_A7RCR0, BIT(8) },
+ [IMX7_RESET_A7_DBG_RESET1] = { SRC_A7RCR0, BIT(9) },
+ [IMX7_RESET_A7_ETM_RESET0] = { SRC_A7RCR0, BIT(12) },
+ [IMX7_RESET_A7_ETM_RESET1] = { SRC_A7RCR0, BIT(13) },
+ [IMX7_RESET_A7_SOC_DBG_RESET] = { SRC_A7RCR0, BIT(20) },
+ [IMX7_RESET_A7_L2RESET] = { SRC_A7RCR0, BIT(21) },
+ [IMX7_RESET_SW_M4C_RST] = { SRC_M4RCR, BIT(1) },
+ [IMX7_RESET_SW_M4P_RST] = { SRC_M4RCR, BIT(2) },
+ [IMX7_RESET_EIM_RST] = { SRC_ERCR, BIT(0) },
+ [IMX7_RESET_HSICPHY_PORT_RST] = { SRC_HSICPHY_RCR, BIT(1) },
+ [IMX7_RESET_USBPHY1_POR] = { SRC_USBOPHY1_RCR, BIT(0) },
+ [IMX7_RESET_USBPHY1_PORT_RST] = { SRC_USBOPHY1_RCR, BIT(1) },
+ [IMX7_RESET_USBPHY2_POR] = { SRC_USBOPHY2_RCR, BIT(0) },
+ [IMX7_RESET_USBPHY2_PORT_RST] = { SRC_USBOPHY2_RCR, BIT(1) },
+ [IMX7_RESET_MIPI_PHY_MRST] = { SRC_MIPIPHY_RCR, BIT(1) },
+ [IMX7_RESET_MIPI_PHY_SRST] = { SRC_MIPIPHY_RCR, BIT(2) },
+ [IMX7_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, BIT(2) | BIT(1) },
+ [IMX7_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) },
+ [IMX7_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) },
+ [IMX7_RESET_DDRC_PRST] = { SRC_DDRC_RCR, BIT(0) },
+ [IMX7_RESET_DDRC_CORE_RST] = { SRC_DDRC_RCR, BIT(1) },
+};
+
+static struct imx7_src *to_imx7_src(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct imx7_src, rcdev);
+}
+
+static int imx7_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx7_src *imx7src = to_imx7_src(rcdev);
+ const struct imx7_src_signal *signal = &imx7_src_signals[id];
+ unsigned int value = 0;
+
+ switch (id) {
+ case IMX7_RESET_PCIEPHY:
+ /*
+ * wait for more than 10us to release phy g_rst and
+ * btnrst
+ */
+ if (!assert)
+ udelay(10);
+ break;
+
+ case IMX7_RESET_PCIE_CTRL_APPS_EN:
+ value = (assert) ? 0 : signal->bit;
+ break;
+ }
+
+ return regmap_update_bits(imx7src->regmap,
+ signal->offset, signal->bit, value);
+}
+
+static int imx7_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx7_reset_set(rcdev, id, true);
+}
+
+static int imx7_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx7_reset_set(rcdev, id, false);
+}
+
+static const struct reset_control_ops imx7_reset_ops = {
+ .assert = imx7_reset_assert,
+ .deassert = imx7_reset_deassert,
+};
+
+static int imx7_reset_probe(struct platform_device *pdev)
+{
+ struct imx7_src *imx7src;
+ struct device *dev = &pdev->dev;
+ struct regmap_config config = { .name = "src" };
+
+ imx7src = devm_kzalloc(dev, sizeof(*imx7src), GFP_KERNEL);
+ if (!imx7src)
+ return -ENOMEM;
+
+ imx7src->regmap = syscon_node_to_regmap(dev->of_node);
+ if (IS_ERR(imx7src->regmap)) {
+ dev_err(dev, "Unable to get imx7-src regmap");
+ return PTR_ERR(imx7src->regmap);
+ }
+ regmap_attach_dev(dev, imx7src->regmap, &config);
+
+ imx7src->rcdev.owner = THIS_MODULE;
+ imx7src->rcdev.nr_resets = IMX7_RESET_NUM;
+ imx7src->rcdev.ops = &imx7_reset_ops;
+ imx7src->rcdev.of_node = dev->of_node;
+
+ return devm_reset_controller_register(dev, &imx7src->rcdev);
+}
+
+static const struct of_device_id imx7_reset_dt_ids[] = {
+ { .compatible = "fsl,imx7d-src", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver imx7_reset_driver = {
+ .probe = imx7_reset_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = imx7_reset_dt_ids,
+ },
+};
+builtin_platform_driver(imx7_reset_driver);
diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c
index c32f11a30c5f..a8b915eb8b58 100644
--- a/drivers/reset/reset-meson.c
+++ b/drivers/reset/reset-meson.c
@@ -1,4 +1,6 @@
/*
+ * Amlogic Meson Reset Controller driver
+ *
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
@@ -53,7 +55,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/err.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -95,7 +97,6 @@ static const struct of_device_id meson_reset_dt_ids[] = {
{ .compatible = "amlogic,meson-gxbb-reset", },
{ /* sentinel */ },
};
-MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
static int meson_reset_probe(struct platform_device *pdev)
{
@@ -128,9 +129,4 @@ static struct platform_driver meson_reset_driver = {
.of_match_table = meson_reset_dt_ids,
},
};
-
-module_platform_driver(meson_reset_driver);
-
-MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver");
-MODULE_LICENSE("Dual BSD/GPL");
+builtin_platform_driver(meson_reset_driver);
diff --git a/drivers/reset/reset-oxnas.c b/drivers/reset/reset-oxnas.c
index 0d9036dea010..cf5b9742b86e 100644
--- a/drivers/reset/reset-oxnas.c
+++ b/drivers/reset/reset-oxnas.c
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/err.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
@@ -83,7 +83,6 @@ static const struct of_device_id oxnas_reset_dt_ids[] = {
{ .compatible = "oxsemi,ox820-reset", },
{ /* sentinel */ },
};
-MODULE_DEVICE_TABLE(of, oxnas_reset_dt_ids);
static int oxnas_reset_probe(struct platform_device *pdev)
{
@@ -123,5 +122,4 @@ static struct platform_driver oxnas_reset_driver = {
.of_match_table = oxnas_reset_dt_ids,
},
};
-
-module_platform_driver(oxnas_reset_driver);
+builtin_platform_driver(oxnas_reset_driver);
diff --git a/drivers/reset/reset-pistachio.c b/drivers/reset/reset-pistachio.c
index bbc4c06dd33b..11d651b44e81 100644
--- a/drivers/reset/reset-pistachio.c
+++ b/drivers/reset/reset-pistachio.c
@@ -10,7 +10,7 @@
* version 2, as published by the Free Software Foundation.
*/
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -128,7 +128,6 @@ static const struct of_device_id pistachio_reset_dt_ids[] = {
{ .compatible = "img,pistachio-reset", },
{ /* sentinel */ },
};
-MODULE_DEVICE_TABLE(of, pistachio_reset_dt_ids);
static struct platform_driver pistachio_reset_driver = {
.probe = pistachio_reset_probe,
@@ -137,8 +136,4 @@ static struct platform_driver pistachio_reset_driver = {
.of_match_table = pistachio_reset_dt_ids,
},
};
-module_platform_driver(pistachio_reset_driver);
-
-MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
-MODULE_DESCRIPTION("Pistacho Reset Controller Driver");
-MODULE_LICENSE("GPL v2");
+builtin_platform_driver(pistachio_reset_driver);
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c
index 43e4a9f39b9b..07224c019892 100644
--- a/drivers/reset/reset-socfpga.c
+++ b/drivers/reset/reset-socfpga.c
@@ -25,7 +25,8 @@
#include <linux/spinlock.h>
#include <linux/types.h>
-#define NR_BANKS 4
+#define BANK_INCREMENT 4
+#define NR_BANKS 8
struct socfpga_reset_data {
spinlock_t lock;
@@ -46,8 +47,8 @@ static int socfpga_reset_assert(struct reset_controller_dev *rcdev,
spin_lock_irqsave(&data->lock, flags);
- reg = readl(data->membase + (bank * NR_BANKS));
- writel(reg | BIT(offset), data->membase + (bank * NR_BANKS));
+ reg = readl(data->membase + (bank * BANK_INCREMENT));
+ writel(reg | BIT(offset), data->membase + (bank * BANK_INCREMENT));
spin_unlock_irqrestore(&data->lock, flags);
return 0;
@@ -67,8 +68,8 @@ static int socfpga_reset_deassert(struct reset_controller_dev *rcdev,
spin_lock_irqsave(&data->lock, flags);
- reg = readl(data->membase + (bank * NR_BANKS));
- writel(reg & ~BIT(offset), data->membase + (bank * NR_BANKS));
+ reg = readl(data->membase + (bank * BANK_INCREMENT));
+ writel(reg & ~BIT(offset), data->membase + (bank * BANK_INCREMENT));
spin_unlock_irqrestore(&data->lock, flags);
@@ -84,7 +85,7 @@ static int socfpga_reset_status(struct reset_controller_dev *rcdev,
int offset = id % BITS_PER_LONG;
u32 reg;
- reg = readl(data->membase + (bank * NR_BANKS));
+ reg = readl(data->membase + (bank * BANK_INCREMENT));
return !(reg & BIT(offset));
}
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c
index b44f6b5f87b6..cd585cd2f04d 100644
--- a/drivers/reset/reset-sunxi.c
+++ b/drivers/reset/reset-sunxi.c
@@ -34,15 +34,16 @@ static int sunxi_reset_assert(struct reset_controller_dev *rcdev,
struct sunxi_reset_data *data = container_of(rcdev,
struct sunxi_reset_data,
rcdev);
- int bank = id / BITS_PER_LONG;
- int offset = id % BITS_PER_LONG;
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
- reg = readl(data->membase + (bank * 4));
- writel(reg & ~BIT(offset), data->membase + (bank * 4));
+ reg = readl(data->membase + (bank * reg_width));
+ writel(reg & ~BIT(offset), data->membase + (bank * reg_width));
spin_unlock_irqrestore(&data->lock, flags);
@@ -55,15 +56,16 @@ static int sunxi_reset_deassert(struct reset_controller_dev *rcdev,
struct sunxi_reset_data *data = container_of(rcdev,
struct sunxi_reset_data,
rcdev);
- int bank = id / BITS_PER_LONG;
- int offset = id % BITS_PER_LONG;
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
- reg = readl(data->membase + (bank * 4));
- writel(reg | BIT(offset), data->membase + (bank * 4));
+ reg = readl(data->membase + (bank * reg_width));
+ writel(reg | BIT(offset), data->membase + (bank * reg_width));
spin_unlock_irqrestore(&data->lock, flags);
diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c
index 9c11be3d3450..c4ba89832796 100644
--- a/drivers/reset/reset-uniphier.c
+++ b/drivers/reset/reset-uniphier.c
@@ -50,6 +50,15 @@ struct uniphier_reset_data {
}
/* System reset data */
+#define UNIPHIER_SLD3_SYS_RESET_NAND(id) \
+ UNIPHIER_RESETX((id), 0x2004, 2)
+
+#define UNIPHIER_LD11_SYS_RESET_NAND(id) \
+ UNIPHIER_RESETX((id), 0x200c, 0)
+
+#define UNIPHIER_LD11_SYS_RESET_EMMC(id) \
+ UNIPHIER_RESETX((id), 0x200c, 2)
+
#define UNIPHIER_SLD3_SYS_RESET_STDMAC(id) \
UNIPHIER_RESETX((id), 0x2000, 10)
@@ -65,12 +74,14 @@ struct uniphier_reset_data {
#define UNIPHIER_PRO4_SYS_RESET_USB3(id, ch) \
UNIPHIER_RESETX((id), 0x2000 + 0x4 * (ch), 17)
-const struct uniphier_reset_data uniphier_sld3_sys_reset_data[] = {
+static const struct uniphier_reset_data uniphier_sld3_sys_reset_data[] = {
+ UNIPHIER_SLD3_SYS_RESET_NAND(2),
UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* Ether, HSC, MIO */
UNIPHIER_RESET_END,
};
-const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = {
+static const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = {
+ UNIPHIER_SLD3_SYS_RESET_NAND(2),
UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC, MIO, RLE */
UNIPHIER_PRO4_SYS_RESET_GIO(12), /* Ether, SATA, USB3 */
UNIPHIER_PRO4_SYS_RESET_USB3(14, 0),
@@ -78,7 +89,8 @@ const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = {
UNIPHIER_RESET_END,
};
-const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = {
+static const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = {
+ UNIPHIER_SLD3_SYS_RESET_NAND(2),
UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC */
UNIPHIER_PRO4_SYS_RESET_GIO(12), /* PCIe, USB3 */
UNIPHIER_PRO4_SYS_RESET_USB3(14, 0),
@@ -86,7 +98,8 @@ const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = {
UNIPHIER_RESET_END,
};
-const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = {
+static const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = {
+ UNIPHIER_SLD3_SYS_RESET_NAND(2),
UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC, RLE */
UNIPHIER_PRO4_SYS_RESET_USB3(14, 0),
UNIPHIER_PRO4_SYS_RESET_USB3(15, 1),
@@ -100,12 +113,16 @@ const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = {
UNIPHIER_RESET_END,
};
-const struct uniphier_reset_data uniphier_ld11_sys_reset_data[] = {
+static const struct uniphier_reset_data uniphier_ld11_sys_reset_data[] = {
+ UNIPHIER_LD11_SYS_RESET_NAND(2),
+ UNIPHIER_LD11_SYS_RESET_EMMC(4),
UNIPHIER_LD11_SYS_RESET_STDMAC(8), /* HSC, MIO */
UNIPHIER_RESET_END,
};
-const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
+static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
+ UNIPHIER_LD11_SYS_RESET_NAND(2),
+ UNIPHIER_LD11_SYS_RESET_EMMC(4),
UNIPHIER_LD11_SYS_RESET_STDMAC(8), /* HSC */
UNIPHIER_LD20_SYS_RESET_GIO(12), /* PCIe, USB3 */
UNIPHIER_RESETX(16, 0x200c, 12), /* USB30-PHY0 */
@@ -134,7 +151,7 @@ const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
#define UNIPHIER_MIO_RESET_DMAC(id) \
UNIPHIER_RESETX((id), 0x110, 17)
-const struct uniphier_reset_data uniphier_sld3_mio_reset_data[] = {
+static const struct uniphier_reset_data uniphier_sld3_mio_reset_data[] = {
UNIPHIER_MIO_RESET_SD(0, 0),
UNIPHIER_MIO_RESET_SD(1, 1),
UNIPHIER_MIO_RESET_SD(2, 2),
@@ -154,7 +171,7 @@ const struct uniphier_reset_data uniphier_sld3_mio_reset_data[] = {
UNIPHIER_RESET_END,
};
-const struct uniphier_reset_data uniphier_pro5_sd_reset_data[] = {
+static const struct uniphier_reset_data uniphier_pro5_sd_reset_data[] = {
UNIPHIER_MIO_RESET_SD(0, 0),
UNIPHIER_MIO_RESET_SD(1, 1),
UNIPHIER_MIO_RESET_EMMC_HW_RESET(6, 1),
@@ -171,7 +188,7 @@ const struct uniphier_reset_data uniphier_pro5_sd_reset_data[] = {
#define UNIPHIER_PERI_RESET_FI2C(id, ch) \
UNIPHIER_RESETX((id), 0x114, 24 + (ch))
-const struct uniphier_reset_data uniphier_ld4_peri_reset_data[] = {
+static const struct uniphier_reset_data uniphier_ld4_peri_reset_data[] = {
UNIPHIER_PERI_RESET_UART(0, 0),
UNIPHIER_PERI_RESET_UART(1, 1),
UNIPHIER_PERI_RESET_UART(2, 2),
@@ -184,7 +201,7 @@ const struct uniphier_reset_data uniphier_ld4_peri_reset_data[] = {
UNIPHIER_RESET_END,
};
-const struct uniphier_reset_data uniphier_pro4_peri_reset_data[] = {
+static const struct uniphier_reset_data uniphier_pro4_peri_reset_data[] = {
UNIPHIER_PERI_RESET_UART(0, 0),
UNIPHIER_PERI_RESET_UART(1, 1),
UNIPHIER_PERI_RESET_UART(2, 2),
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 45e5b13a3c02..309643fe35f9 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -3,6 +3,7 @@ menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/atmel/Kconfig"
source "drivers/soc/bcm/Kconfig"
source "drivers/soc/fsl/Kconfig"
+source "drivers/soc/imx/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/rockchip/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 3467de7d3890..824b44281efa 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -7,6 +7,7 @@ obj-y += bcm/
obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/
+obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_ARCH_RENESAS) += renesas/
diff --git a/drivers/soc/bcm/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c
index 94e7335553f4..b6195fdf0d00 100644
--- a/drivers/soc/bcm/brcmstb/common.c
+++ b/drivers/soc/bcm/brcmstb/common.c
@@ -41,6 +41,15 @@ bool soc_is_brcmstb(void)
}
static const struct of_device_id sun_top_ctrl_match[] = {
+ { .compatible = "brcm,bcm7125-sun-top-ctrl", },
+ { .compatible = "brcm,bcm7346-sun-top-ctrl", },
+ { .compatible = "brcm,bcm7358-sun-top-ctrl", },
+ { .compatible = "brcm,bcm7360-sun-top-ctrl", },
+ { .compatible = "brcm,bcm7362-sun-top-ctrl", },
+ { .compatible = "brcm,bcm7420-sun-top-ctrl", },
+ { .compatible = "brcm,bcm7425-sun-top-ctrl", },
+ { .compatible = "brcm,bcm7429-sun-top-ctrl", },
+ { .compatible = "brcm,bcm7425-sun-top-ctrl", },
{ .compatible = "brcm,brcmstb-sun-top-ctrl", },
{ }
};
diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig
new file mode 100644
index 000000000000..357a5d8f8da0
--- /dev/null
+++ b/drivers/soc/imx/Kconfig
@@ -0,0 +1,9 @@
+menu "i.MX SoC drivers"
+
+config IMX7_PM_DOMAINS
+ bool "i.MX7 PM domains"
+ select PM_GENERIC_DOMAINS
+ depends on SOC_IMX7D || (COMPILE_TEST && OF)
+ default y if SOC_IMX7D
+
+endmenu
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
new file mode 100644
index 000000000000..5b6e396c1121
--- /dev/null
+++ b/drivers/soc/imx/Makefile
@@ -0,0 +1,2 @@
+obj-y += gpc.o
+obj-$(CONFIG_IMX7_PM_DOMAINS) += gpcv2.o
diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c
new file mode 100644
index 000000000000..47e7aa963dbb
--- /dev/null
+++ b/drivers/soc/imx/gpc.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
+ * Copyright 2011-2013 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define GPC_CNTR 0x000
+
+#define GPC_PGC_CTRL_OFFS 0x0
+#define GPC_PGC_PUPSCR_OFFS 0x4
+#define GPC_PGC_PDNSCR_OFFS 0x8
+#define GPC_PGC_SW2ISO_SHIFT 0x8
+#define GPC_PGC_SW_SHIFT 0x0
+
+#define GPC_PGC_GPU_PDN 0x260
+#define GPC_PGC_GPU_PUPSCR 0x264
+#define GPC_PGC_GPU_PDNSCR 0x268
+
+#define GPU_VPU_PUP_REQ BIT(1)
+#define GPU_VPU_PDN_REQ BIT(0)
+
+#define GPC_CLK_MAX 6
+
+#define PGC_DOMAIN_FLAG_NO_PD BIT(0)
+
+struct imx_pm_domain {
+ struct generic_pm_domain base;
+ struct regmap *regmap;
+ struct regulator *supply;
+ struct clk *clk[GPC_CLK_MAX];
+ int num_clks;
+ unsigned int reg_offs;
+ signed char cntr_pdn_bit;
+ unsigned int ipg_rate_mhz;
+ unsigned int flags;
+};
+
+static inline struct imx_pm_domain *
+to_imx_pm_domain(struct generic_pm_domain *genpd)
+{
+ return container_of(genpd, struct imx_pm_domain, base);
+}
+
+static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd)
+{
+ struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
+ int iso, iso2sw;
+ u32 val;
+
+ if (pd->flags & PGC_DOMAIN_FLAG_NO_PD)
+ return -EBUSY;
+
+ /* Read ISO and ISO2SW power down delays */
+ regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val);
+ iso = val & 0x3f;
+ iso2sw = (val >> 8) & 0x3f;
+
+ /* Gate off domain when powered down */
+ regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
+ 0x1, 0x1);
+
+ /* Request GPC to power down domain */
+ val = BIT(pd->cntr_pdn_bit);
+ regmap_update_bits(pd->regmap, GPC_CNTR, val, val);
+
+ /* Wait ISO + ISO2SW IPG clock cycles */
+ udelay(DIV_ROUND_UP(iso + iso2sw, pd->ipg_rate_mhz));
+
+ if (pd->supply)
+ regulator_disable(pd->supply);
+
+ return 0;
+}
+
+static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd)
+{
+ struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
+ int i, ret, sw, sw2iso;
+ u32 val;
+
+ if (pd->supply) {
+ ret = regulator_enable(pd->supply);
+ if (ret) {
+ pr_err("%s: failed to enable regulator: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ /* Enable reset clocks for all devices in the domain */
+ for (i = 0; i < pd->num_clks; i++)
+ clk_prepare_enable(pd->clk[i]);
+
+ /* Gate off domain when powered down */
+ regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
+ 0x1, 0x1);
+
+ /* Read ISO and ISO2SW power up delays */
+ regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val);
+ sw = val & 0x3f;
+ sw2iso = (val >> 8) & 0x3f;
+
+ /* Request GPC to power up domain */
+ val = BIT(pd->cntr_pdn_bit + 1);
+ regmap_update_bits(pd->regmap, GPC_CNTR, val, val);
+
+ /* Wait ISO + ISO2SW IPG clock cycles */
+ udelay(DIV_ROUND_UP(sw + sw2iso, pd->ipg_rate_mhz));
+
+ /* Disable reset clocks for all devices in the domain */
+ for (i = 0; i < pd->num_clks; i++)
+ clk_disable_unprepare(pd->clk[i]);
+
+ return 0;
+}
+
+static int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain)
+{
+ int i, ret;
+
+ for (i = 0; ; i++) {
+ struct clk *clk = of_clk_get(dev->of_node, i);
+ if (IS_ERR(clk))
+ break;
+ if (i >= GPC_CLK_MAX) {
+ dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX);
+ ret = -EINVAL;
+ goto clk_err;
+ }
+ domain->clk[i] = clk;
+ }
+ domain->num_clks = i;
+
+ return 0;
+
+clk_err:
+ while (i--)
+ clk_put(domain->clk[i]);
+
+ return ret;
+}
+
+static void imx_pgc_put_clocks(struct imx_pm_domain *domain)
+{
+ int i;
+
+ for (i = domain->num_clks - 1; i >= 0; i--)
+ clk_put(domain->clk[i]);
+}
+
+static int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain)
+{
+ /* try to get the domain supply regulator */
+ domain->supply = devm_regulator_get_optional(dev, "power");
+ if (IS_ERR(domain->supply)) {
+ if (PTR_ERR(domain->supply) == -ENODEV)
+ domain->supply = NULL;
+ else
+ return PTR_ERR(domain->supply);
+ }
+
+ /* try to get all clocks needed for reset propagation */
+ return imx_pgc_get_clocks(dev, domain);
+}
+
+static int imx_pgc_power_domain_probe(struct platform_device *pdev)
+{
+ struct imx_pm_domain *domain = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ /* if this PD is associated with a DT node try to parse it */
+ if (dev->of_node) {
+ ret = imx_pgc_parse_dt(dev, domain);
+ if (ret)
+ return ret;
+ }
+
+ /* initially power on the domain */
+ if (domain->base.power_on)
+ domain->base.power_on(&domain->base);
+
+ if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
+ pm_genpd_init(&domain->base, NULL, false);
+ ret = of_genpd_add_provider_simple(dev->of_node, &domain->base);
+ if (ret)
+ goto genpd_err;
+ }
+
+ device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE);
+
+ return 0;
+
+genpd_err:
+ pm_genpd_remove(&domain->base);
+ imx_pgc_put_clocks(domain);
+
+ return ret;
+}
+
+static int imx_pgc_power_domain_remove(struct platform_device *pdev)
+{
+ struct imx_pm_domain *domain = pdev->dev.platform_data;
+
+ if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
+ of_genpd_del_provider(pdev->dev.of_node);
+ pm_genpd_remove(&domain->base);
+ imx_pgc_put_clocks(domain);
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id imx_pgc_power_domain_id[] = {
+ { "imx-pgc-power-domain"},
+ { },
+};
+
+static struct platform_driver imx_pgc_power_domain_driver = {
+ .driver = {
+ .name = "imx-pgc-pd",
+ },
+ .probe = imx_pgc_power_domain_probe,
+ .remove = imx_pgc_power_domain_remove,
+ .id_table = imx_pgc_power_domain_id,
+};
+builtin_platform_driver(imx_pgc_power_domain_driver)
+
+#define GPC_PGC_DOMAIN_ARM 0
+#define GPC_PGC_DOMAIN_PU 1
+#define GPC_PGC_DOMAIN_DISPLAY 2
+
+static struct genpd_power_state imx6_pm_domain_pu_state = {
+ .power_off_latency_ns = 25000,
+ .power_on_latency_ns = 2000000,
+};
+
+static struct imx_pm_domain imx_gpc_domains[] = {
+ {
+ .base = {
+ .name = "ARM",
+ },
+ }, {
+ .base = {
+ .name = "PU",
+ .power_off = imx6_pm_domain_power_off,
+ .power_on = imx6_pm_domain_power_on,
+ .states = &imx6_pm_domain_pu_state,
+ .state_count = 1,
+ },
+ .reg_offs = 0x260,
+ .cntr_pdn_bit = 0,
+ }, {
+ .base = {
+ .name = "DISPLAY",
+ .power_off = imx6_pm_domain_power_off,
+ .power_on = imx6_pm_domain_power_on,
+ },
+ .reg_offs = 0x240,
+ .cntr_pdn_bit = 4,
+ }
+};
+
+struct imx_gpc_dt_data {
+ int num_domains;
+ bool err009619_present;
+};
+
+static const struct imx_gpc_dt_data imx6q_dt_data = {
+ .num_domains = 2,
+ .err009619_present = false,
+};
+
+static const struct imx_gpc_dt_data imx6qp_dt_data = {
+ .num_domains = 2,
+ .err009619_present = true,
+};
+
+static const struct imx_gpc_dt_data imx6sl_dt_data = {
+ .num_domains = 3,
+ .err009619_present = false,
+};
+
+static const struct of_device_id imx_gpc_dt_ids[] = {
+ { .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data },
+ { .compatible = "fsl,imx6qp-gpc", .data = &imx6qp_dt_data },
+ { .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data },
+ { }
+};
+
+static const struct regmap_config imx_gpc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x2ac,
+};
+
+static struct generic_pm_domain *imx_gpc_onecell_domains[] = {
+ &imx_gpc_domains[0].base,
+ &imx_gpc_domains[1].base,
+};
+
+static struct genpd_onecell_data imx_gpc_onecell_data = {
+ .domains = imx_gpc_onecell_domains,
+ .num_domains = 2,
+};
+
+static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap,
+ unsigned int num_domains)
+{
+ struct imx_pm_domain *domain;
+ int i, ret;
+
+ for (i = 0; i < num_domains; i++) {
+ domain = &imx_gpc_domains[i];
+ domain->regmap = regmap;
+ domain->ipg_rate_mhz = 66;
+
+ if (i == 1) {
+ domain->supply = devm_regulator_get(dev, "pu");
+ if (IS_ERR(domain->supply))
+ return PTR_ERR(domain->supply);;
+
+ ret = imx_pgc_get_clocks(dev, domain);
+ if (ret)
+ goto clk_err;
+
+ domain->base.power_on(&domain->base);
+ }
+ }
+
+ for (i = 0; i < num_domains; i++)
+ pm_genpd_init(&imx_gpc_domains[i].base, NULL, false);
+
+ if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
+ ret = of_genpd_add_provider_onecell(dev->of_node,
+ &imx_gpc_onecell_data);
+ if (ret)
+ goto genpd_err;
+ }
+
+ return 0;
+
+genpd_err:
+ for (i = 0; i < num_domains; i++)
+ pm_genpd_remove(&imx_gpc_domains[i].base);
+ imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
+clk_err:
+ return ret;
+}
+
+static int imx_gpc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(imx_gpc_dt_ids, &pdev->dev);
+ const struct imx_gpc_dt_data *of_id_data = of_id->data;
+ struct device_node *pgc_node;
+ struct regmap *regmap;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
+
+ /* bail out if DT too old and doesn't provide the necessary info */
+ if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
+ !pgc_node)
+ return 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
+ &imx_gpc_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&pdev->dev, "failed to init regmap: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* Disable PU power down in normal operation if ERR009619 is present */
+ if (of_id_data->err009619_present)
+ imx_gpc_domains[GPC_PGC_DOMAIN_PU].flags |=
+ PGC_DOMAIN_FLAG_NO_PD;
+
+ if (!pgc_node) {
+ ret = imx_gpc_old_dt_init(&pdev->dev, regmap,
+ of_id_data->num_domains);
+ if (ret)
+ return ret;
+ } else {
+ struct imx_pm_domain *domain;
+ struct platform_device *pd_pdev;
+ struct device_node *np;
+ struct clk *ipg_clk;
+ unsigned int ipg_rate_mhz;
+ int domain_index;
+
+ ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(ipg_clk))
+ return PTR_ERR(ipg_clk);
+ ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000;
+
+ for_each_child_of_node(pgc_node, np) {
+ ret = of_property_read_u32(np, "reg", &domain_index);
+ if (ret) {
+ of_node_put(np);
+ return ret;
+ }
+ if (domain_index >= of_id_data->num_domains)
+ continue;
+
+ domain = &imx_gpc_domains[domain_index];
+ domain->regmap = regmap;
+ domain->ipg_rate_mhz = ipg_rate_mhz;
+
+ pd_pdev = platform_device_alloc("imx-pgc-power-domain",
+ domain_index);
+ if (!pd_pdev) {
+ of_node_put(np);
+ return -ENOMEM;
+ }
+ pd_pdev->dev.platform_data = domain;
+ pd_pdev->dev.parent = &pdev->dev;
+ pd_pdev->dev.of_node = np;
+
+ ret = platform_device_add(pd_pdev);
+ if (ret) {
+ platform_device_put(pd_pdev);
+ of_node_put(np);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int imx_gpc_remove(struct platform_device *pdev)
+{
+ int ret;
+
+ /*
+ * If the old DT binding is used the toplevel driver needs to
+ * de-register the power domains
+ */
+ if (!of_get_child_by_name(pdev->dev.of_node, "pgc")) {
+ of_genpd_del_provider(pdev->dev.of_node);
+
+ ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base);
+ if (ret)
+ return ret;
+ imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
+
+ ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver imx_gpc_driver = {
+ .driver = {
+ .name = "imx-gpc",
+ .of_match_table = imx_gpc_dt_ids,
+ },
+ .probe = imx_gpc_probe,
+ .remove = imx_gpc_remove,
+};
+builtin_platform_driver(imx_gpc_driver)
diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c
new file mode 100644
index 000000000000..3039072911a5
--- /dev/null
+++ b/drivers/soc/imx/gpcv2.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2017 Impinj, Inc
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * Based on the code of analogus driver:
+ *
+ * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <dt-bindings/power/imx7-power.h>
+
+#define GPC_LPCR_A7_BSC 0x000
+
+#define GPC_PGC_CPU_MAPPING 0x0ec
+#define USB_HSIC_PHY_A7_DOMAIN BIT(6)
+#define USB_OTG2_PHY_A7_DOMAIN BIT(5)
+#define USB_OTG1_PHY_A7_DOMAIN BIT(4)
+#define PCIE_PHY_A7_DOMAIN BIT(3)
+#define MIPI_PHY_A7_DOMAIN BIT(2)
+
+#define GPC_PU_PGC_SW_PUP_REQ 0x0f8
+#define GPC_PU_PGC_SW_PDN_REQ 0x104
+#define USB_HSIC_PHY_SW_Pxx_REQ BIT(4)
+#define USB_OTG2_PHY_SW_Pxx_REQ BIT(3)
+#define USB_OTG1_PHY_SW_Pxx_REQ BIT(2)
+#define PCIE_PHY_SW_Pxx_REQ BIT(1)
+#define MIPI_PHY_SW_Pxx_REQ BIT(0)
+
+#define GPC_M4_PU_PDN_FLG 0x1bc
+
+
+#define PGC_MIPI 4
+#define PGC_PCIE 5
+#define PGC_USB_HSIC 8
+#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40)
+#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc)
+
+#define GPC_PGC_CTRL_PCR BIT(0)
+
+struct imx7_pgc_domain {
+ struct generic_pm_domain genpd;
+ struct regmap *regmap;
+ struct regulator *regulator;
+
+ unsigned int pgc;
+
+ const struct {
+ u32 pxx;
+ u32 map;
+ } bits;
+
+ const int voltage;
+ struct device *dev;
+};
+
+static int imx7_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
+ bool on)
+{
+ struct imx7_pgc_domain *domain = container_of(genpd,
+ struct imx7_pgc_domain,
+ genpd);
+ unsigned int offset = on ?
+ GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ;
+ const bool enable_power_control = !on;
+ const bool has_regulator = !IS_ERR(domain->regulator);
+ unsigned long deadline;
+ int ret = 0;
+
+ regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING,
+ domain->bits.map, domain->bits.map);
+
+ if (has_regulator && on) {
+ ret = regulator_enable(domain->regulator);
+ if (ret) {
+ dev_err(domain->dev, "failed to enable regulator\n");
+ goto unmap;
+ }
+ }
+
+ if (enable_power_control)
+ regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
+ GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
+
+ regmap_update_bits(domain->regmap, offset,
+ domain->bits.pxx, domain->bits.pxx);
+
+ /*
+ * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
+ * for PUP_REQ/PDN_REQ bit to be cleared
+ */
+ deadline = jiffies + msecs_to_jiffies(1);
+ while (true) {
+ u32 pxx_req;
+
+ regmap_read(domain->regmap, offset, &pxx_req);
+
+ if (!(pxx_req & domain->bits.pxx))
+ break;
+
+ if (time_after(jiffies, deadline)) {
+ dev_err(domain->dev, "falied to command PGC\n");
+ ret = -ETIMEDOUT;
+ /*
+ * If we were in a process of enabling a
+ * domain and failed we might as well disable
+ * the regulator we just enabled. And if it
+ * was the opposite situation and we failed to
+ * power down -- keep the regulator on
+ */
+ on = !on;
+ break;
+ }
+
+ cpu_relax();
+ }
+
+ if (enable_power_control)
+ regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
+ GPC_PGC_CTRL_PCR, 0);
+
+ if (has_regulator && !on) {
+ int err;
+
+ err = regulator_disable(domain->regulator);
+ if (err)
+ dev_err(domain->dev,
+ "failed to disable regulator: %d\n", ret);
+ /* Preserve earlier error code */
+ ret = ret ?: err;
+ }
+unmap:
+ regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING,
+ domain->bits.map, 0);
+ return ret;
+}
+
+static int imx7_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd)
+{
+ return imx7_gpc_pu_pgc_sw_pxx_req(genpd, true);
+}
+
+static int imx7_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd)
+{
+ return imx7_gpc_pu_pgc_sw_pxx_req(genpd, false);
+}
+
+static struct imx7_pgc_domain imx7_pgc_domains[] = {
+ [IMX7_POWER_DOMAIN_MIPI_PHY] = {
+ .genpd = {
+ .name = "mipi-phy",
+ },
+ .bits = {
+ .pxx = MIPI_PHY_SW_Pxx_REQ,
+ .map = MIPI_PHY_A7_DOMAIN,
+ },
+ .voltage = 1000000,
+ .pgc = PGC_MIPI,
+ },
+
+ [IMX7_POWER_DOMAIN_PCIE_PHY] = {
+ .genpd = {
+ .name = "pcie-phy",
+ },
+ .bits = {
+ .pxx = PCIE_PHY_SW_Pxx_REQ,
+ .map = PCIE_PHY_A7_DOMAIN,
+ },
+ .voltage = 1000000,
+ .pgc = PGC_PCIE,
+ },
+
+ [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = {
+ .genpd = {
+ .name = "usb-hsic-phy",
+ },
+ .bits = {
+ .pxx = USB_HSIC_PHY_SW_Pxx_REQ,
+ .map = USB_HSIC_PHY_A7_DOMAIN,
+ },
+ .voltage = 1200000,
+ .pgc = PGC_USB_HSIC,
+ },
+};
+
+static int imx7_pgc_domain_probe(struct platform_device *pdev)
+{
+ struct imx7_pgc_domain *domain = pdev->dev.platform_data;
+ int ret;
+
+ domain->dev = &pdev->dev;
+
+ ret = pm_genpd_init(&domain->genpd, NULL, true);
+ if (ret) {
+ dev_err(domain->dev, "Failed to init power domain\n");
+ return ret;
+ }
+
+ domain->regulator = devm_regulator_get_optional(domain->dev, "power");
+ if (IS_ERR(domain->regulator)) {
+ if (PTR_ERR(domain->regulator) != -ENODEV) {
+ dev_err(domain->dev, "Failed to get domain's regulator\n");
+ return PTR_ERR(domain->regulator);
+ }
+ } else {
+ regulator_set_voltage(domain->regulator,
+ domain->voltage, domain->voltage);
+ }
+
+ ret = of_genpd_add_provider_simple(domain->dev->of_node,
+ &domain->genpd);
+ if (ret) {
+ dev_err(domain->dev, "Failed to add genpd provider\n");
+ pm_genpd_remove(&domain->genpd);
+ }
+
+ return ret;
+}
+
+static int imx7_pgc_domain_remove(struct platform_device *pdev)
+{
+ struct imx7_pgc_domain *domain = pdev->dev.platform_data;
+
+ of_genpd_del_provider(domain->dev->of_node);
+ pm_genpd_remove(&domain->genpd);
+
+ return 0;
+}
+
+static const struct platform_device_id imx7_pgc_domain_id[] = {
+ { "imx7-pgc-domain", },
+ { },
+};
+
+static struct platform_driver imx7_pgc_domain_driver = {
+ .driver = {
+ .name = "imx7-pgc",
+ },
+ .probe = imx7_pgc_domain_probe,
+ .remove = imx7_pgc_domain_remove,
+ .id_table = imx7_pgc_domain_id,
+};
+builtin_platform_driver(imx7_pgc_domain_driver)
+
+static int imx_gpcv2_probe(struct platform_device *pdev)
+{
+ static const struct regmap_range yes_ranges[] = {
+ regmap_reg_range(GPC_LPCR_A7_BSC,
+ GPC_M4_PU_PDN_FLG),
+ regmap_reg_range(GPC_PGC_CTRL(PGC_MIPI),
+ GPC_PGC_SR(PGC_MIPI)),
+ regmap_reg_range(GPC_PGC_CTRL(PGC_PCIE),
+ GPC_PGC_SR(PGC_PCIE)),
+ regmap_reg_range(GPC_PGC_CTRL(PGC_USB_HSIC),
+ GPC_PGC_SR(PGC_USB_HSIC)),
+ };
+ static const struct regmap_access_table access_table = {
+ .yes_ranges = yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(yes_ranges),
+ };
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .rd_table = &access_table,
+ .wr_table = &access_table,
+ .max_register = SZ_4K,
+ };
+ struct device *dev = &pdev->dev;
+ struct device_node *pgc_np, *np;
+ struct regmap *regmap;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ pgc_np = of_get_child_by_name(dev->of_node, "pgc");
+ if (!pgc_np) {
+ dev_err(dev, "No power domains specified in DT\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "failed to init regmap (%d)\n", ret);
+ return ret;
+ }
+
+ for_each_child_of_node(pgc_np, np) {
+ struct platform_device *pd_pdev;
+ struct imx7_pgc_domain *domain;
+ u32 domain_index;
+
+ ret = of_property_read_u32(np, "reg", &domain_index);
+ if (ret) {
+ dev_err(dev, "Failed to read 'reg' property\n");
+ of_node_put(np);
+ return ret;
+ }
+
+ if (domain_index >= ARRAY_SIZE(imx7_pgc_domains)) {
+ dev_warn(dev,
+ "Domain index %d is out of bounds\n",
+ domain_index);
+ continue;
+ }
+
+ domain = &imx7_pgc_domains[domain_index];
+ domain->regmap = regmap;
+ domain->genpd.power_on = imx7_gpc_pu_pgc_sw_pup_req;
+ domain->genpd.power_off = imx7_gpc_pu_pgc_sw_pdn_req;
+
+ pd_pdev = platform_device_alloc("imx7-pgc-domain",
+ domain_index);
+ if (!pd_pdev) {
+ dev_err(dev, "Failed to allocate platform device\n");
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ pd_pdev->dev.platform_data = domain;
+ pd_pdev->dev.parent = dev;
+ pd_pdev->dev.of_node = np;
+
+ ret = platform_device_add(pd_pdev);
+ if (ret) {
+ platform_device_put(pd_pdev);
+ of_node_put(np);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id imx_gpcv2_dt_ids[] = {
+ { .compatible = "fsl,imx7d-gpc" },
+ { }
+};
+
+static struct platform_driver imx_gpc_driver = {
+ .driver = {
+ .name = "imx-gpcv2",
+ .of_match_table = imx_gpcv2_dt_ids,
+ },
+ .probe = imx_gpcv2_probe,
+};
+builtin_platform_driver(imx_gpc_driver)
diff --git a/drivers/soc/renesas/r8a7795-sysc.c b/drivers/soc/renesas/r8a7795-sysc.c
index 5e7537c96f7b..7412666187b3 100644
--- a/drivers/soc/renesas/r8a7795-sysc.c
+++ b/drivers/soc/renesas/r8a7795-sysc.c
@@ -1,7 +1,7 @@
/*
* Renesas R-Car H3 System Controller
*
- * Copyright (C) 2016 Glider bvba
+ * Copyright (C) 2016-2017 Glider bvba
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -10,12 +10,13 @@
#include <linux/bug.h>
#include <linux/kernel.h>
+#include <linux/sys_soc.h>
#include <dt-bindings/power/r8a7795-sysc.h>
#include "rcar-sysc.h"
-static const struct rcar_sysc_area r8a7795_areas[] __initconst = {
+static struct rcar_sysc_area r8a7795_areas[] __initdata = {
{ "always-on", 0, 0, R8A7795_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "ca57-scu", 0x1c0, 0, R8A7795_PD_CA57_SCU, R8A7795_PD_ALWAYS_ON,
PD_SCU },
@@ -40,6 +41,7 @@ static const struct rcar_sysc_area r8a7795_areas[] __initconst = {
{ "a3vp", 0x340, 0, R8A7795_PD_A3VP, R8A7795_PD_ALWAYS_ON },
{ "cr7", 0x240, 0, R8A7795_PD_CR7, R8A7795_PD_ALWAYS_ON },
{ "a3vc", 0x380, 0, R8A7795_PD_A3VC, R8A7795_PD_ALWAYS_ON },
+ /* A2VC0 exists on ES1.x only */
{ "a2vc0", 0x3c0, 0, R8A7795_PD_A2VC0, R8A7795_PD_A3VC },
{ "a2vc1", 0x3c0, 1, R8A7795_PD_A2VC1, R8A7795_PD_A3VC },
{ "3dg-a", 0x100, 0, R8A7795_PD_3DG_A, R8A7795_PD_ALWAYS_ON },
@@ -50,7 +52,27 @@ static const struct rcar_sysc_area r8a7795_areas[] __initconst = {
{ "a3ir", 0x180, 0, R8A7795_PD_A3IR, R8A7795_PD_ALWAYS_ON },
};
+
+ /*
+ * Fixups for R-Car H3 revisions after ES1.x
+ */
+
+static const struct soc_device_attribute r8a7795es1[] __initconst = {
+ { .soc_id = "r8a7795", .revision = "ES1.*" },
+ { /* sentinel */ }
+};
+
+static int __init r8a7795_sysc_init(void)
+{
+ if (!soc_device_match(r8a7795es1))
+ rcar_sysc_nullify(r8a7795_areas, ARRAY_SIZE(r8a7795_areas),
+ R8A7795_PD_A2VC0);
+
+ return 0;
+}
+
const struct rcar_sysc_info r8a7795_sysc_info __initconst = {
+ .init = r8a7795_sysc_init,
.areas = r8a7795_areas,
.num_areas = ARRAY_SIZE(r8a7795_areas),
};
diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c
index 225c35c79d9a..528a13742aeb 100644
--- a/drivers/soc/renesas/rcar-sysc.c
+++ b/drivers/soc/renesas/rcar-sysc.c
@@ -2,7 +2,7 @@
* R-Car SYSC Power management support
*
* Copyright (C) 2014 Magnus Damm
- * Copyright (C) 2015-2016 Glider bvba
+ * Copyright (C) 2015-2017 Glider bvba
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -334,6 +334,12 @@ static int __init rcar_sysc_pd_init(void)
info = match->data;
+ if (info->init) {
+ error = info->init();
+ if (error)
+ return error;
+ }
+
has_cpg_mstp = of_find_compatible_node(NULL, NULL,
"renesas,cpg-mstp-clocks");
@@ -377,6 +383,11 @@ static int __init rcar_sysc_pd_init(void)
const struct rcar_sysc_area *area = &info->areas[i];
struct rcar_sysc_pd *pd;
+ if (!area->name) {
+ /* Skip NULLified area */
+ continue;
+ }
+
pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL);
if (!pd) {
error = -ENOMEM;
@@ -406,6 +417,18 @@ out_put:
}
early_initcall(rcar_sysc_pd_init);
+void __init rcar_sysc_nullify(struct rcar_sysc_area *areas,
+ unsigned int num_areas, u8 id)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_areas; i++)
+ if (areas[i].isr_bit == id) {
+ areas[i].name = NULL;
+ return;
+ }
+}
+
void __init rcar_sysc_init(phys_addr_t base, u32 syscier)
{
u32 syscimr;
diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h
index f6e842e2976e..07edb049a401 100644
--- a/drivers/soc/renesas/rcar-sysc.h
+++ b/drivers/soc/renesas/rcar-sysc.h
@@ -46,6 +46,7 @@ struct rcar_sysc_area {
*/
struct rcar_sysc_info {
+ int (*init)(void); /* Optional */
const struct rcar_sysc_area *areas;
unsigned int num_areas;
};
@@ -59,4 +60,13 @@ extern const struct rcar_sysc_info r8a7792_sysc_info;
extern const struct rcar_sysc_info r8a7794_sysc_info;
extern const struct rcar_sysc_info r8a7795_sysc_info;
extern const struct rcar_sysc_info r8a7796_sysc_info;
+
+
+ /*
+ * Helpers for fixing up power area tables depending on SoC revision
+ */
+
+extern void rcar_sysc_nullify(struct rcar_sysc_area *areas,
+ unsigned int num_areas, u8 id);
+
#endif /* __SOC_RENESAS_RCAR_SYSC_H__ */
diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c
index 330960312296..ca26f13d399c 100644
--- a/drivers/soc/renesas/renesas-soc.c
+++ b/drivers/soc/renesas/renesas-soc.c
@@ -80,11 +80,21 @@ static const struct renesas_soc soc_rmobile_a1 __initconst __maybe_unused = {
.id = 0x40,
};
+static const struct renesas_soc soc_rz_g1h __initconst __maybe_unused = {
+ .family = &fam_rzg,
+ .id = 0x45,
+};
+
static const struct renesas_soc soc_rz_g1m __initconst __maybe_unused = {
.family = &fam_rzg,
.id = 0x47,
};
+static const struct renesas_soc soc_rz_g1n __initconst __maybe_unused = {
+ .family = &fam_rzg,
+ .id = 0x4b,
+};
+
static const struct renesas_soc soc_rz_g1e __initconst __maybe_unused = {
.family = &fam_rzg,
.id = 0x4c,
@@ -150,9 +160,15 @@ static const struct of_device_id renesas_socs[] __initconst = {
#ifdef CONFIG_ARCH_R8A7740
{ .compatible = "renesas,r8a7740", .data = &soc_rmobile_a1 },
#endif
+#ifdef CONFIG_ARCH_R8A7742
+ { .compatible = "renesas,r8a7742", .data = &soc_rz_g1h },
+#endif
#ifdef CONFIG_ARCH_R8A7743
{ .compatible = "renesas,r8a7743", .data = &soc_rz_g1m },
#endif
+#ifdef CONFIG_ARCH_R8A7744
+ { .compatible = "renesas,r8a7744", .data = &soc_rz_g1n },
+#endif
#ifdef CONFIG_ARCH_R8A7745
{ .compatible = "renesas,r8a7745", .data = &soc_rz_g1e },
#endif
@@ -254,4 +270,4 @@ static int __init renesas_soc_init(void)
return 0;
}
-core_initcall(renesas_soc_init);
+early_initcall(renesas_soc_init);
diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index 245533907d1b..8b25bd55e648 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -8,7 +8,13 @@ if SOC_SAMSUNG
config EXYNOS_PMU
bool "Exynos PMU controller driver" if COMPILE_TEST
- depends on (ARM && ARCH_EXYNOS) || ((ARM || ARM64) && COMPILE_TEST)
+ depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
+ select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS
+
+# There is no need to enable these drivers for ARMv8
+config EXYNOS_PMU_ARM_DRIVERS
+ bool "Exynos PMU ARMv7-specific driver extensions" if COMPILE_TEST
+ depends on EXYNOS_PMU
config EXYNOS_PM_DOMAINS
bool "Exynos PM domains" if COMPILE_TEST
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index 3619f2ecddaa..4d7694a4e7a4 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -1,3 +1,5 @@
-obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \
+obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
+
+obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
exynos5250-pmu.o exynos5420-pmu.o
obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index 56d9244ff981..bd4a76f27bc2 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -69,27 +69,37 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode)
}
/*
+ * Split the data between ARM architectures because it is relatively big
+ * and useless on other arch.
+ */
+#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
+#define exynos_pmu_data_arm_ptr(data) (&data)
+#else
+#define exynos_pmu_data_arm_ptr(data) NULL
+#endif
+
+/*
* PMU platform driver and devicetree bindings.
*/
static const struct of_device_id exynos_pmu_of_device_ids[] = {
{
.compatible = "samsung,exynos3250-pmu",
- .data = &exynos3250_pmu_data,
+ .data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data),
}, {
.compatible = "samsung,exynos4210-pmu",
- .data = &exynos4210_pmu_data,
+ .data = exynos_pmu_data_arm_ptr(exynos4210_pmu_data),
}, {
.compatible = "samsung,exynos4212-pmu",
- .data = &exynos4212_pmu_data,
+ .data = exynos_pmu_data_arm_ptr(exynos4212_pmu_data),
}, {
.compatible = "samsung,exynos4412-pmu",
- .data = &exynos4412_pmu_data,
+ .data = exynos_pmu_data_arm_ptr(exynos4412_pmu_data),
}, {
.compatible = "samsung,exynos5250-pmu",
- .data = &exynos5250_pmu_data,
+ .data = exynos_pmu_data_arm_ptr(exynos5250_pmu_data),
}, {
.compatible = "samsung,exynos5420-pmu",
- .data = &exynos5420_pmu_data,
+ .data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data),
}, {
.compatible = "samsung,exynos5433-pmu",
},
diff --git a/drivers/soc/samsung/exynos-pmu.h b/drivers/soc/samsung/exynos-pmu.h
index a469e366fead..40d4229abfb5 100644
--- a/drivers/soc/samsung/exynos-pmu.h
+++ b/drivers/soc/samsung/exynos-pmu.h
@@ -31,6 +31,8 @@ struct exynos_pmu_data {
};
extern void __iomem *pmu_base_addr;
+
+#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
/* list of all exported SoC specific data */
extern const struct exynos_pmu_data exynos3250_pmu_data;
extern const struct exynos_pmu_data exynos4210_pmu_data;
@@ -38,6 +40,7 @@ extern const struct exynos_pmu_data exynos4212_pmu_data;
extern const struct exynos_pmu_data exynos4412_pmu_data;
extern const struct exynos_pmu_data exynos5250_pmu_data;
extern const struct exynos_pmu_data exynos5420_pmu_data;
+#endif
extern void pmu_raw_writel(u32 val, u32 offset);
extern u32 pmu_raw_readl(u32 offset);
diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index e5e124c07066..dcf088db40b6 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -12,6 +12,8 @@ config ARCH_TEGRA_2x_SOC
select PINCTRL_TEGRA20
select PL310_ERRATA_727915 if CACHE_L2X0
select PL310_ERRATA_769419 if CACHE_L2X0
+ select SOC_TEGRA_FLOWCTRL
+ select SOC_TEGRA_PMC
select TEGRA_TIMER
help
Support for NVIDIA Tegra AP20 and T20 processors, based on the
@@ -23,6 +25,8 @@ config ARCH_TEGRA_3x_SOC
select ARM_ERRATA_764369 if SMP
select PINCTRL_TEGRA30
select PL310_ERRATA_769419 if CACHE_L2X0
+ select SOC_TEGRA_FLOWCTRL
+ select SOC_TEGRA_PMC
select TEGRA_TIMER
help
Support for NVIDIA Tegra T30 processor family, based on the
@@ -33,6 +37,8 @@ config ARCH_TEGRA_114_SOC
select ARM_ERRATA_798181 if SMP
select HAVE_ARM_ARCH_TIMER
select PINCTRL_TEGRA114
+ select SOC_TEGRA_FLOWCTRL
+ select SOC_TEGRA_PMC
select TEGRA_TIMER
help
Support for NVIDIA Tegra T114 processor family, based on the
@@ -42,6 +48,8 @@ config ARCH_TEGRA_124_SOC
bool "Enable support for Tegra124 family"
select HAVE_ARM_ARCH_TIMER
select PINCTRL_TEGRA124
+ select SOC_TEGRA_FLOWCTRL
+ select SOC_TEGRA_PMC
select TEGRA_TIMER
help
Support for NVIDIA Tegra T124 processor family, based on the
@@ -55,6 +63,8 @@ if ARM64
config ARCH_TEGRA_132_SOC
bool "NVIDIA Tegra132 SoC"
select PINCTRL_TEGRA124
+ select SOC_TEGRA_FLOWCTRL
+ select SOC_TEGRA_PMC
help
Enable support for NVIDIA Tegra132 SoC, based on the Denver
ARMv8 CPU. The Tegra132 SoC is similar to the Tegra124 SoC,
@@ -64,6 +74,8 @@ config ARCH_TEGRA_132_SOC
config ARCH_TEGRA_210_SOC
bool "NVIDIA Tegra210 SoC"
select PINCTRL_TEGRA210
+ select SOC_TEGRA_FLOWCTRL
+ select SOC_TEGRA_PMC
help
Enable support for the NVIDIA Tegra210 SoC. Also known as Tegra X1,
the Tegra210 has four Cortex-A57 cores paired with four Cortex-A53
@@ -83,6 +95,7 @@ config ARCH_TEGRA_186_SOC
select TEGRA_BPMP
select TEGRA_HSP_MBOX
select TEGRA_IVC
+ select SOC_TEGRA_PMC_TEGRA186
help
Enable support for the NVIDIA Tegar186 SoC. The Tegra186 features a
combination of Denver and Cortex-A57 CPU cores and a GPU based on
@@ -93,3 +106,12 @@ config ARCH_TEGRA_186_SOC
endif
endif
+
+config SOC_TEGRA_FLOWCTRL
+ bool
+
+config SOC_TEGRA_PMC
+ bool
+
+config SOC_TEGRA_PMC_TEGRA186
+ bool
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
index ae857ff7d53d..4f81dd55e5d1 100644
--- a/drivers/soc/tegra/Makefile
+++ b/drivers/soc/tegra/Makefile
@@ -1,4 +1,6 @@
obj-y += fuse/
obj-y += common.o
-obj-y += pmc.o
+obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
+obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
+obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o
diff --git a/drivers/soc/tegra/flowctrl.c b/drivers/soc/tegra/flowctrl.c
new file mode 100644
index 000000000000..0e345c05fc65
--- /dev/null
+++ b/drivers/soc/tegra/flowctrl.c
@@ -0,0 +1,224 @@
+/*
+ * drivers/soc/tegra/flowctrl.c
+ *
+ * Functions and macros to control the flowcontroller
+ *
+ * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include <soc/tegra/common.h>
+#include <soc/tegra/flowctrl.h>
+#include <soc/tegra/fuse.h>
+
+static u8 flowctrl_offset_halt_cpu[] = {
+ FLOW_CTRL_HALT_CPU0_EVENTS,
+ FLOW_CTRL_HALT_CPU1_EVENTS,
+ FLOW_CTRL_HALT_CPU1_EVENTS + 8,
+ FLOW_CTRL_HALT_CPU1_EVENTS + 16,
+};
+
+static u8 flowctrl_offset_cpu_csr[] = {
+ FLOW_CTRL_CPU0_CSR,
+ FLOW_CTRL_CPU1_CSR,
+ FLOW_CTRL_CPU1_CSR + 8,
+ FLOW_CTRL_CPU1_CSR + 16,
+};
+
+static void __iomem *tegra_flowctrl_base;
+
+static void flowctrl_update(u8 offset, u32 value)
+{
+ if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base),
+ "Tegra flowctrl not initialised!\n"))
+ return;
+
+ writel(value, tegra_flowctrl_base + offset);
+
+ /* ensure the update has reached the flow controller */
+ wmb();
+ readl_relaxed(tegra_flowctrl_base + offset);
+}
+
+u32 flowctrl_read_cpu_csr(unsigned int cpuid)
+{
+ u8 offset = flowctrl_offset_cpu_csr[cpuid];
+
+ if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base),
+ "Tegra flowctrl not initialised!\n"))
+ return 0;
+
+ return readl(tegra_flowctrl_base + offset);
+}
+
+void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value)
+{
+ return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value);
+}
+
+void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value)
+{
+ return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value);
+}
+
+void flowctrl_cpu_suspend_enter(unsigned int cpuid)
+{
+ unsigned int reg;
+ int i;
+
+ reg = flowctrl_read_cpu_csr(cpuid);
+ switch (tegra_get_chip_id()) {
+ case TEGRA20:
+ /* clear wfe bitmap */
+ reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;
+ /* clear wfi bitmap */
+ reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;
+ /* pwr gating on wfe */
+ reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid;
+ break;
+ case TEGRA30:
+ case TEGRA114:
+ case TEGRA124:
+ /* clear wfe bitmap */
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP;
+ /* clear wfi bitmap */
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;
+ /* pwr gating on wfi */
+ reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid;
+ break;
+ }
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */
+ reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */
+ flowctrl_write_cpu_csr(cpuid, reg);
+
+ for (i = 0; i < num_possible_cpus(); i++) {
+ if (i == cpuid)
+ continue;
+ reg = flowctrl_read_cpu_csr(i);
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG;
+ reg |= FLOW_CTRL_CSR_INTR_FLAG;
+ flowctrl_write_cpu_csr(i, reg);
+ }
+}
+
+void flowctrl_cpu_suspend_exit(unsigned int cpuid)
+{
+ unsigned int reg;
+
+ /* Disable powergating via flow controller for CPU0 */
+ reg = flowctrl_read_cpu_csr(cpuid);
+ switch (tegra_get_chip_id()) {
+ case TEGRA20:
+ /* clear wfe bitmap */
+ reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;
+ /* clear wfi bitmap */
+ reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;
+ break;
+ case TEGRA30:
+ case TEGRA114:
+ case TEGRA124:
+ /* clear wfe bitmap */
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP;
+ /* clear wfi bitmap */
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;
+ break;
+ }
+ reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
+ flowctrl_write_cpu_csr(cpuid, reg);
+}
+
+static int tegra_flowctrl_probe(struct platform_device *pdev)
+{
+ void __iomem *base = tegra_flowctrl_base;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tegra_flowctrl_base))
+ return PTR_ERR(base);
+
+ iounmap(base);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_flowctrl_match[] = {
+ { .compatible = "nvidia,tegra210-flowctrl" },
+ { .compatible = "nvidia,tegra124-flowctrl" },
+ { .compatible = "nvidia,tegra114-flowctrl" },
+ { .compatible = "nvidia,tegra30-flowctrl" },
+ { .compatible = "nvidia,tegra20-flowctrl" },
+ { }
+};
+
+static struct platform_driver tegra_flowctrl_driver = {
+ .driver = {
+ .name = "tegra-flowctrl",
+ .suppress_bind_attrs = true,
+ .of_match_table = tegra_flowctrl_match,
+ },
+ .probe = tegra_flowctrl_probe,
+};
+builtin_platform_driver(tegra_flowctrl_driver);
+
+static int __init tegra_flowctrl_init(void)
+{
+ struct resource res;
+ struct device_node *np;
+
+ if (!soc_is_tegra())
+ return 0;
+
+ np = of_find_matching_node(NULL, tegra_flowctrl_match);
+ if (np) {
+ if (of_address_to_resource(np, 0, &res) < 0) {
+ pr_err("failed to get flowctrl register\n");
+ return -ENXIO;
+ }
+ of_node_put(np);
+ } else if (IS_ENABLED(CONFIG_ARM)) {
+ /*
+ * Hardcoded fallback for 32-bit Tegra
+ * devices if device tree node is missing.
+ */
+ res.start = 0x60007000;
+ res.end = 0x60007fff;
+ res.flags = IORESOURCE_MEM;
+ } else {
+ /*
+ * At this point we're running on a Tegra,
+ * that doesn't support the flow controller
+ * (eg. Tegra186), so just return.
+ */
+ return 0;
+ }
+
+ tegra_flowctrl_base = ioremap_nocache(res.start, resource_size(&res));
+ if (!tegra_flowctrl_base)
+ return -ENXIO;
+
+ return 0;
+}
+early_initcall(tegra_flowctrl_init);
diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c
index de2c1bfe28b5..7413f60fa855 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra.c
@@ -18,7 +18,7 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/kobject.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -168,7 +168,7 @@ static struct platform_driver tegra_fuse_driver = {
},
.probe = tegra_fuse_probe,
};
-module_platform_driver(tegra_fuse_driver);
+builtin_platform_driver(tegra_fuse_driver);
bool __init tegra_fuse_read_spare(unsigned int spare)
{
diff --git a/drivers/soc/tegra/pmc-tegra186.c b/drivers/soc/tegra/pmc-tegra186.c
new file mode 100644
index 000000000000..6f5c6f98ba92
--- /dev/null
+++ b/drivers/soc/tegra/pmc-tegra186.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#define pr_fmt(fmt) "tegra-pmc: " fmt
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+
+#include <asm/system_misc.h>
+
+#define PMC_CNTRL 0x000
+#define PMC_CNTRL_MAIN_RST BIT(4)
+
+#define PMC_RST_STATUS 0x070
+
+#define WAKE_AOWAKE_CTRL 0x4f4
+#define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0)
+
+#define SCRATCH_SCRATCH0 0x2000
+#define SCRATCH_SCRATCH0_MODE_RECOVERY BIT(31)
+#define SCRATCH_SCRATCH0_MODE_BOOTLOADER BIT(30)
+#define SCRATCH_SCRATCH0_MODE_RCM BIT(1)
+#define SCRATCH_SCRATCH0_MODE_MASK (SCRATCH_SCRATCH0_MODE_RECOVERY | \
+ SCRATCH_SCRATCH0_MODE_BOOTLOADER | \
+ SCRATCH_SCRATCH0_MODE_RCM)
+
+struct tegra_pmc {
+ struct device *dev;
+ void __iomem *regs;
+ void __iomem *wake;
+ void __iomem *aotag;
+ void __iomem *scratch;
+
+ void (*system_restart)(enum reboot_mode mode, const char *cmd);
+ struct notifier_block restart;
+};
+
+static int tegra186_pmc_restart_notify(struct notifier_block *nb,
+ unsigned long action,
+ void *data)
+{
+ struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, restart);
+ const char *cmd = data;
+ u32 value;
+
+ value = readl(pmc->scratch + SCRATCH_SCRATCH0);
+ value &= ~SCRATCH_SCRATCH0_MODE_MASK;
+
+ if (cmd) {
+ if (strcmp(cmd, "recovery") == 0)
+ value |= SCRATCH_SCRATCH0_MODE_RECOVERY;
+
+ if (strcmp(cmd, "bootloader") == 0)
+ value |= SCRATCH_SCRATCH0_MODE_BOOTLOADER;
+
+ if (strcmp(cmd, "forced-recovery") == 0)
+ value |= SCRATCH_SCRATCH0_MODE_RCM;
+ }
+
+ writel(value, pmc->scratch + SCRATCH_SCRATCH0);
+
+ /*
+ * If available, call the system restart implementation that was
+ * registered earlier (typically PSCI).
+ */
+ if (pmc->system_restart) {
+ pmc->system_restart(reboot_mode, cmd);
+ return NOTIFY_DONE;
+ }
+
+ /* reset everything but SCRATCH0_SCRATCH0 and PMC_RST_STATUS */
+ value = readl(pmc->regs + PMC_CNTRL);
+ value |= PMC_CNTRL_MAIN_RST;
+ writel(value, pmc->regs + PMC_CNTRL);
+
+ return NOTIFY_DONE;
+}
+
+static int tegra186_pmc_setup(struct tegra_pmc *pmc)
+{
+ struct device_node *np = pmc->dev->of_node;
+ bool invert;
+ u32 value;
+
+ invert = of_property_read_bool(np, "nvidia,invert-interrupt");
+
+ value = readl(pmc->wake + WAKE_AOWAKE_CTRL);
+
+ if (invert)
+ value |= WAKE_AOWAKE_CTRL_INTR_POLARITY;
+ else
+ value &= ~WAKE_AOWAKE_CTRL_INTR_POLARITY;
+
+ writel(value, pmc->wake + WAKE_AOWAKE_CTRL);
+
+ /*
+ * We need to hook any system restart implementation registered
+ * previously so we can write SCRATCH_SCRATCH0 before reset.
+ */
+ pmc->system_restart = arm_pm_restart;
+ arm_pm_restart = NULL;
+
+ pmc->restart.notifier_call = tegra186_pmc_restart_notify;
+ pmc->restart.priority = 128;
+
+ return register_restart_handler(&pmc->restart);
+}
+
+static int tegra186_pmc_probe(struct platform_device *pdev)
+{
+ struct tegra_pmc *pmc;
+ struct resource *res;
+
+ pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
+ if (!pmc)
+ return -ENOMEM;
+
+ pmc->dev = &pdev->dev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmc");
+ pmc->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pmc->regs))
+ return PTR_ERR(pmc->regs);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake");
+ pmc->wake = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pmc->wake))
+ return PTR_ERR(pmc->wake);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag");
+ pmc->aotag = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pmc->aotag))
+ return PTR_ERR(pmc->aotag);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch");
+ pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pmc->scratch))
+ return PTR_ERR(pmc->scratch);
+
+ return tegra186_pmc_setup(pmc);
+}
+
+static const struct of_device_id tegra186_pmc_of_match[] = {
+ { .compatible = "nvidia,tegra186-pmc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tegra186_pmc_of_match);
+
+static struct platform_driver tegra186_pmc_driver = {
+ .driver = {
+ .name = "tegra186-pmc",
+ .of_match_table = tegra186_pmc_of_match,
+ },
+ .probe = tegra186_pmc_probe,
+};
+builtin_platform_driver(tegra186_pmc_driver);
diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index 3557c5e32a93..39e152abe6b9 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -38,4 +38,16 @@ config WKUP_M3_IPC
to communicate and use the Wakeup M3 for PM features like suspend
resume and boots it using wkup_m3_rproc driver.
+config TI_SCI_PM_DOMAINS
+ tristate "TI SCI PM Domains Driver"
+ depends on TI_SCI_PROTOCOL
+ depends on PM_GENERIC_DOMAINS
+ help
+ Generic power domain implementation for TI device implementing
+ the TI SCI protocol.
+
+ To compile this as a module, choose M here. The module will be
+ called ti_sci_pm_domains. Note this is needed early in boot before
+ rootfs may be available.
+
endif # SOC_TI
diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
index 48ff3a79634f..7d572736c86e 100644
--- a/drivers/soc/ti/Makefile
+++ b/drivers/soc/ti/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o
knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o
obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o
obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o
+obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o
diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c
new file mode 100644
index 000000000000..b0b283810e72
--- /dev/null
+++ b/drivers/soc/ti/ti_sci_pm_domains.c
@@ -0,0 +1,202 @@
+/*
+ * TI SCI Generic Power Domain Driver
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
+ * J Keerthy <j-keerthy@ti.com>
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/soc/ti/ti_sci_protocol.h>
+
+/**
+ * struct ti_sci_genpd_dev_data: holds data needed for every device attached
+ * to this genpd
+ * @idx: index of the device that identifies it with the system
+ * control processor.
+ */
+struct ti_sci_genpd_dev_data {
+ int idx;
+};
+
+/**
+ * struct ti_sci_pm_domain: TI specific data needed for power domain
+ * @ti_sci: handle to TI SCI protocol driver that provides ops to
+ * communicate with system control processor.
+ * @dev: pointer to dev for the driver for devm allocs
+ * @pd: generic_pm_domain for use with the genpd framework
+ */
+struct ti_sci_pm_domain {
+ const struct ti_sci_handle *ti_sci;
+ struct device *dev;
+ struct generic_pm_domain pd;
+};
+
+#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
+
+/**
+ * ti_sci_dev_id(): get prepopulated ti_sci id from struct dev
+ * @dev: pointer to device associated with this genpd
+ *
+ * Returns device_id stored from ti,sci_id property
+ */
+static int ti_sci_dev_id(struct device *dev)
+{
+ struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
+ struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
+
+ return sci_dev_data->idx;
+}
+
+/**
+ * ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle
+ * @dev: pointer to device associated with this genpd
+ *
+ * Returns ti_sci_handle to be used to communicate with system
+ * control processor.
+ */
+static const struct ti_sci_handle *ti_sci_dev_to_sci_handle(struct device *dev)
+{
+ struct generic_pm_domain *pd = pd_to_genpd(dev->pm_domain);
+ struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(pd);
+
+ return ti_sci_genpd->ti_sci;
+}
+
+/**
+ * ti_sci_dev_start(): genpd device start hook called to turn device on
+ * @dev: pointer to device associated with this genpd to be powered on
+ */
+static int ti_sci_dev_start(struct device *dev)
+{
+ const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
+ int idx = ti_sci_dev_id(dev);
+
+ return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
+}
+
+/**
+ * ti_sci_dev_stop(): genpd device stop hook called to turn device off
+ * @dev: pointer to device associated with this genpd to be powered off
+ */
+static int ti_sci_dev_stop(struct device *dev)
+{
+ const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
+ int idx = ti_sci_dev_id(dev);
+
+ return ti_sci->ops.dev_ops.put_device(ti_sci, idx);
+}
+
+static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct of_phandle_args pd_args;
+ struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(domain);
+ const struct ti_sci_handle *ti_sci = ti_sci_genpd->ti_sci;
+ struct ti_sci_genpd_dev_data *sci_dev_data;
+ struct generic_pm_domain_data *genpd_data;
+ int idx, ret = 0;
+
+ ret = of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells", 0, &pd_args);
+ if (ret < 0)
+ return ret;
+
+ if (pd_args.args_count != 1)
+ return -EINVAL;
+
+ idx = pd_args.args[0];
+
+ /*
+ * Check the validity of the requested idx, if the index is not valid
+ * the PMMC will return a NAK here and we will not allocate it.
+ */
+ ret = ti_sci->ops.dev_ops.is_valid(ti_sci, idx);
+ if (ret)
+ return -EINVAL;
+
+ sci_dev_data = kzalloc(sizeof(*sci_dev_data), GFP_KERNEL);
+ if (!sci_dev_data)
+ return -ENOMEM;
+
+ sci_dev_data->idx = idx;
+
+ genpd_data = dev_gpd_data(dev);
+ genpd_data->data = sci_dev_data;
+
+ return 0;
+}
+
+static void ti_sci_pd_detach_dev(struct generic_pm_domain *domain,
+ struct device *dev)
+{
+ struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
+ struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
+
+ kfree(sci_dev_data);
+ genpd_data->data = NULL;
+}
+
+static const struct of_device_id ti_sci_pm_domain_matches[] = {
+ { .compatible = "ti,sci-pm-domain", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
+
+static int ti_sci_pm_domain_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct ti_sci_pm_domain *ti_sci_pd;
+ int ret;
+
+ ti_sci_pd = devm_kzalloc(dev, sizeof(*ti_sci_pd), GFP_KERNEL);
+ if (!ti_sci_pd)
+ return -ENOMEM;
+
+ ti_sci_pd->ti_sci = devm_ti_sci_get_handle(dev);
+ if (IS_ERR(ti_sci_pd->ti_sci))
+ return PTR_ERR(ti_sci_pd->ti_sci);
+
+ ti_sci_pd->dev = dev;
+
+ ti_sci_pd->pd.attach_dev = ti_sci_pd_attach_dev;
+ ti_sci_pd->pd.detach_dev = ti_sci_pd_detach_dev;
+
+ ti_sci_pd->pd.dev_ops.start = ti_sci_dev_start;
+ ti_sci_pd->pd.dev_ops.stop = ti_sci_dev_stop;
+
+ pm_genpd_init(&ti_sci_pd->pd, NULL, true);
+
+ ret = of_genpd_add_provider_simple(np, &ti_sci_pd->pd);
+
+ return ret;
+}
+
+static struct platform_driver ti_sci_pm_domains_driver = {
+ .probe = ti_sci_pm_domain_probe,
+ .driver = {
+ .name = "ti_sci_pm_domains",
+ .of_match_table = ti_sci_pm_domain_matches,
+ },
+};
+module_platform_driver(ti_sci_pm_domains_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver");
+MODULE_AUTHOR("Dave Gerlach");
diff --git a/drivers/soc/zte/zx296718_pm_domains.c b/drivers/soc/zte/zx296718_pm_domains.c
index 5ed924fee855..4dc5d62ee81b 100644
--- a/drivers/soc/zte/zx296718_pm_domains.c
+++ b/drivers/soc/zte/zx296718_pm_domains.c
@@ -169,7 +169,6 @@ static const struct of_device_id zx296718_pm_domain_matches[] = {
static struct platform_driver zx296718_pd_driver = {
.driver = {
.name = "zx296718-powerdomain",
- .owner = THIS_MODULE,
.of_match_table = zx296718_pm_domain_matches,
},
.probe = zx296718_pd_probe,
diff --git a/drivers/soc/zte/zx2967_pm_domains.c b/drivers/soc/zte/zx2967_pm_domains.c
index 61c8d84bf315..c42aeaaa34ba 100644
--- a/drivers/soc/zte/zx2967_pm_domains.c
+++ b/drivers/soc/zte/zx2967_pm_domains.c
@@ -125,10 +125,8 @@ int zx2967_pd_probe(struct platform_device *pdev,
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pcubase = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(pcubase)) {
- dev_err(&pdev->dev, "ioremap fail.\n");
+ if (IS_ERR(pcubase))
return PTR_ERR(pcubase);
- }
for (i = 0; i < domain_num; ++i) {
zx_pm_domains[i]->power_on = zx2967_power_on;