diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-09 10:01:15 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-09 10:01:15 -0700 |
commit | 0160e00ae8e987be8822745fb166aa76451c9bcc (patch) | |
tree | deca2d09a729155ed0cb631f2bc8f557e634ab06 | |
parent | c81ee18e97e4e3162169a749eb7f2b79b3510c7a (diff) | |
parent | b6942b68f85ed3161c91741791ec6f1779574919 (diff) | |
download | op-kernel-dev-0160e00ae8e987be8822745fb166aa76451c9bcc.zip op-kernel-dev-0160e00ae8e987be8822745fb166aa76451c9bcc.tar.gz |
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
...
88 files changed, 2937 insertions, 428 deletions
diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.txt new file mode 100644 index 0000000..078a58b --- /dev/null +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.txt @@ -0,0 +1,34 @@ +NVIDIA Tegra Power Management Controller (PMC) + +Required properties: +- compatible: Should contain one of the following: + - "nvidia,tegra186-pmc": for Tegra186 +- reg: Must contain an (offset, length) pair of the register set for each + entry in reg-names. +- reg-names: Must include the following entries: + - "pmc" + - "wake" + - "aotag" + - "scratch" + +Optional properties: +- nvidia,invert-interrupt: If present, inverts the PMU interrupt signal. + +Example: + +SoC DTSI: + + pmc@c3600000 { + compatible = "nvidia,tegra186-pmc"; + reg = <0 0x0c360000 0 0x10000>, + <0 0x0c370000 0 0x10000>, + <0 0x0c380000 0 0x10000>, + <0 0x0c390000 0 0x10000>; + reg-names = "pmc", "wake", "aotag", "scratch"; + }; + +Board DTS: + + pmc@c360000 { + nvidia,invert-interrupt; + }; diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt b/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt index 65cc034..6c14989 100644 --- a/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt +++ b/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt @@ -1,22 +1,42 @@ Freescale i.MX General Power Controller ======================================= -The i.MX6Q General Power Control (GPC) block contains DVFS load tracking -counters and Power Gating Control (PGC) for the CPU and PU (GPU/VPU) power -domains. +The i.MX6 General Power Control (GPC) block contains DVFS load tracking +counters and Power Gating Control (PGC). Required properties: -- compatible: Should be "fsl,imx6q-gpc" or "fsl,imx6sl-gpc" +- compatible: Should be one of the following: + - fsl,imx6q-gpc + - fsl,imx6qp-gpc + - fsl,imx6sl-gpc - reg: should be register base and length as documented in the datasheet -- interrupts: Should contain GPC interrupt request 1 -- pu-supply: Link to the LDO regulator powering the PU power domain -- clocks: Clock phandles to devices in the PU power domain that need - to be enabled during domain power-up for reset propagation. -- #power-domain-cells: Should be 1, see below: +- interrupts: Should contain one interrupt specifier for the GPC interrupt +- clocks: Must contain an entry for each entry in clock-names. + See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details. +- clock-names: Must include the following entries: + - ipg -The gpc node is a power-controller as documented by the generic power domain -bindings in Documentation/devicetree/bindings/power/power_domain.txt. +The power domains are generic power domain providers as documented in +Documentation/devicetree/bindings/power/power_domain.txt. They are described as +subnodes of the power gating controller 'pgc' node of the GPC and should +contain the following: + +Required properties: +- reg: Must contain the DOMAIN_INDEX of this power domain + The following DOMAIN_INDEX values are valid for i.MX6Q: + ARM_DOMAIN 0 + PU_DOMAIN 1 + The following additional DOMAIN_INDEX value is valid for i.MX6SL: + DISPLAY_DOMAIN 2 + +- #power-domain-cells: Should be 0 + +Optional properties: +- clocks: a number of phandles to clocks that need to be enabled during domain + power-up sequencing to ensure reset propagation into devices located inside + this power domain +- power-supply: a phandle to the regulator powering this domain Example: @@ -25,14 +45,30 @@ Example: reg = <0x020dc000 0x4000>; interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>, <0 90 IRQ_TYPE_LEVEL_HIGH>; - pu-supply = <®_pu>; - clocks = <&clks IMX6QDL_CLK_GPU3D_CORE>, - <&clks IMX6QDL_CLK_GPU3D_SHADER>, - <&clks IMX6QDL_CLK_GPU2D_CORE>, - <&clks IMX6QDL_CLK_GPU2D_AXI>, - <&clks IMX6QDL_CLK_OPENVG_AXI>, - <&clks IMX6QDL_CLK_VPU_AXI>; - #power-domain-cells = <1>; + clocks = <&clks IMX6QDL_CLK_IPG>; + clock-names = "ipg"; + + pgc { + #address-cells = <1>; + #size-cells = <0>; + + power-domain@0 { + reg = <0>; + #power-domain-cells = <0>; + }; + + pd_pu: power-domain@1 { + reg = <1>; + #power-domain-cells = <0>; + power-supply = <®_pu>; + clocks = <&clks IMX6QDL_CLK_GPU3D_CORE>, + <&clks IMX6QDL_CLK_GPU3D_SHADER>, + <&clks IMX6QDL_CLK_GPU2D_CORE>, + <&clks IMX6QDL_CLK_GPU2D_AXI>, + <&clks IMX6QDL_CLK_OPENVG_AXI>, + <&clks IMX6QDL_CLK_VPU_AXI>; + }; + }; }; @@ -40,20 +76,13 @@ Specifying power domain for IP modules ====================================== IP cores belonging to a power domain should contain a 'power-domains' property -that is a phandle pointing to the gpc device node and a DOMAIN_INDEX specifying -the power domain the device belongs to. +that is a phandle pointing to the power domain the device belongs to. Example of a device that is part of the PU power domain: vpu: vpu@02040000 { reg = <0x02040000 0x3c000>; /* ... */ - power-domains = <&gpc 1>; + power-domains = <&pd_pu>; /* ... */ }; - -The following DOMAIN_INDEX values are valid for i.MX6Q: -ARM_DOMAIN 0 -PU_DOMAIN 1 -The following additional DOMAIN_INDEX value is valid for i.MX6SL: -DISPLAY_DOMAIN 2 diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt new file mode 100644 index 0000000..02f45c6 --- /dev/null +++ b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt @@ -0,0 +1,71 @@ +Freescale i.MX General Power Controller v2 +========================================== + +The i.MX7S/D General Power Control (GPC) block contains Power Gating +Control (PGC) for various power domains. + +Required properties: + +- compatible: Should be "fsl,imx7d-gpc" + +- reg: should be register base and length as documented in the + datasheet + +- interrupts: Should contain GPC interrupt request 1 + +Power domains contained within GPC node are generic power domain +providers, documented in +Documentation/devicetree/bindings/power/power_domain.txt, which are +described as subnodes of the power gating controller 'pgc' node, +which, in turn, is expected to contain the following: + +Required properties: + +- reg: Power domain index. Valid values are defined in + include/dt-bindings/power/imx7-power.h + +- #power-domain-cells: Should be 0 + +Optional properties: + +- power-supply: Power supply used to power the domain + +Example: + + gpc: gpc@303a0000 { + compatible = "fsl,imx7d-gpc"; + reg = <0x303a0000 0x1000>; + interrupt-controller; + interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <3>; + interrupt-parent = <&intc>; + + pgc { + #address-cells = <1>; + #size-cells = <0>; + + pgc_pcie_phy: power-domain@3 { + #power-domain-cells = <0>; + + reg = <IMX7_POWER_DOMAIN_PCIE_PHY>; + power-supply = <®_1p0d>; + }; + }; + }; + + +Specifying power domain for IP modules +====================================== + +IP cores belonging to a power domain should contain a 'power-domains' +property that is a phandle for PGC node representing the domain. + +Example of a device that is part of the PCIE_PHY power domain: + + pcie: pcie@33800000 { + reg = <0x33800000 0x4000>, + <0x4ff00000 0x80000>; + /* ... */ + power-domains = <&pgc_pcie_phy>; + /* ... */ + }; diff --git a/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt b/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt new file mode 100644 index 0000000..5e1afc3 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt @@ -0,0 +1,47 @@ +Freescale i.MX7 System Reset Controller +====================================== + +Please also refer to reset.txt in this directory for common reset +controller binding usage. + +Required properties: +- compatible: Should be "fsl,imx7-src", "syscon" +- reg: should be register base and length as documented in the + datasheet +- interrupts: Should contain SRC interrupt +- #reset-cells: 1, see below + +example: + +src: reset-controller@30390000 { + compatible = "fsl,imx7d-src", "syscon"; + reg = <0x30390000 0x2000>; + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; + #reset-cells = <1>; +}; + + +Specifying reset lines connected to IP modules +============================================== + +The system reset controller can be used to reset various set of +peripherals. Device nodes that need access to reset lines should +specify them as a reset phandle in their corresponding node as +specified in reset.txt. + +Example: + + pcie: pcie@33800000 { + + ... + + resets = <&src IMX7_RESET_PCIEPHY>, + <&src IMX7_RESET_PCIE_CTRL_APPS_EN>; + reset-names = "pciephy", "apps"; + + ... + }; + + +For list of all valid reset indicies see +<dt-bindings/reset/imx7-reset.h> diff --git a/Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt b/Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt new file mode 100644 index 0000000..c705db0 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt @@ -0,0 +1,57 @@ +Texas Instruments TI-SCI Generic Power Domain +--------------------------------------------- + +Some TI SoCs contain a system controller (like the PMMC, etc...) that is +responsible for controlling the state of the IPs that are present. +Communication between the host processor running an OS and the system +controller happens through a protocol known as TI-SCI [1]. + +[1] Documentation/devicetree/bindings/arm/keystone/ti,sci.txt + +PM Domain Node +============== +The PM domain node represents the global PM domain managed by the PMMC, which +in this case is the implementation as documented by the generic PM domain +bindings in Documentation/devicetree/bindings/power/power_domain.txt. Because +this relies on the TI SCI protocol to communicate with the PMMC it must be a +child of the pmmc node. + +Required Properties: +-------------------- +- compatible: should be "ti,sci-pm-domain" +- #power-domain-cells: Must be 1 so that an id can be provided in each + device node. + +Example (K2G): +------------- + pmmc: pmmc { + compatible = "ti,k2g-sci"; + ... + + k2g_pds: power-controller { + compatible = "ti,sci-pm-domain"; + #power-domain-cells = <1>; + }; + }; + +PM Domain Consumers +=================== +Hardware blocks belonging to a PM domain should contain a "power-domains" +property that is a phandle pointing to the corresponding PM domain node +along with an index representing the device id to be passed to the PMMC +for device control. + +Required Properties: +-------------------- +- power-domains: phandle pointing to the corresponding PM domain node + and an ID representing the device. + +See dt-bindings/genpd/k2g.h for the list of valid identifiers for k2g. + +Example (K2G): +-------------------- + uart0: serial@02530c00 { + compatible = "ns16550a"; + ... + power-domains = <&k2g_pds K2G_DEV_UART0>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 9d58e9f..41590d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -653,7 +653,9 @@ M: Thor Thayer <thor.thayer@linux.intel.com> S: Maintained F: drivers/gpio/gpio-altera-a10sr.c F: drivers/mfd/altera-a10sr.c +F: drivers/reset/reset-a10sr.c F: include/linux/mfd/altera-a10sr.h +F: include/dt-bindings/reset/altr,rst-mgr-a10sr.h ALTERA TRIPLE SPEED ETHERNET DRIVER M: Vince Bridgers <vbridger@opensource.altera.com> @@ -1282,6 +1284,7 @@ F: arch/arm/mach-mxs/ F: arch/arm/boot/dts/imx* F: arch/arm/configs/imx*_defconfig F: drivers/clk/imx/ +F: drivers/soc/imx/ F: include/soc/imx/ ARM/FREESCALE VYBRID ARM ARCHITECTURE @@ -12604,6 +12607,9 @@ S: Maintained F: Documentation/devicetree/bindings/arm/keystone/ti,sci.txt F: drivers/firmware/ti_sci* F: include/linux/soc/ti/ti_sci_protocol.h +F: Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt +F: include/dt-bindings/genpd/k2g.h +F: drivers/soc/ti/ti_sci_pm_domains.c THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER M: Hans Verkuil <hverkuil@xs4all.nl> diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 9353184..1181053 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -1,6 +1,3 @@ -config ICST - bool - config SA1111 bool select DMABOUNCE if !ARCH_PXA diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 27f23b1..29fdf6a 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -4,7 +4,6 @@ obj-y += firmware.o -obj-$(CONFIG_ICST) += icst.o obj-$(CONFIG_SA1111) += sa1111.o obj-$(CONFIG_DMABOUNCE) += dmabounce.o obj-$(CONFIG_SHARP_LOCOMO) += locomo.o diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c index 1dc2a34..93f584b 100644 --- a/arch/arm/mach-imx/gpc.c +++ b/arch/arm/mach-imx/gpc.c @@ -10,26 +10,17 @@ * http://www.gnu.org/copyleft/gpl.html */ -#include <linux/clk.h> -#include <linux/delay.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/irqchip.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/platform_device.h> -#include <linux/pm_domain.h> -#include <linux/regulator/consumer.h> #include <linux/irqchip/arm-gic.h> #include "common.h" #include "hardware.h" -#define GPC_CNTR 0x000 #define GPC_IMR1 0x008 -#define GPC_PGC_GPU_PDN 0x260 -#define GPC_PGC_GPU_PUPSCR 0x264 -#define GPC_PGC_GPU_PDNSCR 0x268 #define GPC_PGC_CPU_PDN 0x2a0 #define GPC_PGC_CPU_PUPSCR 0x2a4 #define GPC_PGC_CPU_PDNSCR 0x2a8 @@ -39,18 +30,6 @@ #define IMR_NUM 4 #define GPC_MAX_IRQS (IMR_NUM * 32) -#define GPU_VPU_PUP_REQ BIT(1) -#define GPU_VPU_PDN_REQ BIT(0) - -#define GPC_CLK_MAX 6 - -struct pu_domain { - struct generic_pm_domain base; - struct regulator *reg; - struct clk *clk[GPC_CLK_MAX]; - int num_clks; -}; - static void __iomem *gpc_base; static u32 gpc_wake_irqs[IMR_NUM]; static u32 gpc_saved_imrs[IMR_NUM]; @@ -296,199 +275,3 @@ void __init imx_gpc_check_dt(void) gpc_base = of_iomap(np, 0); } } - -static void _imx6q_pm_pu_power_off(struct generic_pm_domain *genpd) -{ - int iso, iso2sw; - u32 val; - - /* Read ISO and ISO2SW power down delays */ - val = readl_relaxed(gpc_base + GPC_PGC_GPU_PDNSCR); - iso = val & 0x3f; - iso2sw = (val >> 8) & 0x3f; - - /* Gate off PU domain when GPU/VPU when powered down */ - writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN); - - /* Request GPC to power down GPU/VPU */ - val = readl_relaxed(gpc_base + GPC_CNTR); - val |= GPU_VPU_PDN_REQ; - writel_relaxed(val, gpc_base + GPC_CNTR); - - /* Wait ISO + ISO2SW IPG clock cycles */ - ndelay((iso + iso2sw) * 1000 / 66); -} - -static int imx6q_pm_pu_power_off(struct generic_pm_domain *genpd) -{ - struct pu_domain *pu = container_of(genpd, struct pu_domain, base); - - _imx6q_pm_pu_power_off(genpd); - - if (pu->reg) - regulator_disable(pu->reg); - - return 0; -} - -static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd) -{ - struct pu_domain *pu = container_of(genpd, struct pu_domain, base); - int i, ret, sw, sw2iso; - u32 val; - - if (pu->reg) - ret = regulator_enable(pu->reg); - if (pu->reg && ret) { - pr_err("%s: failed to enable regulator: %d\n", __func__, ret); - return ret; - } - - /* Enable reset clocks for all devices in the PU domain */ - for (i = 0; i < pu->num_clks; i++) - clk_prepare_enable(pu->clk[i]); - - /* Gate off PU domain when GPU/VPU when powered down */ - writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN); - - /* Read ISO and ISO2SW power down delays */ - val = readl_relaxed(gpc_base + GPC_PGC_GPU_PUPSCR); - sw = val & 0x3f; - sw2iso = (val >> 8) & 0x3f; - - /* Request GPC to power up GPU/VPU */ - val = readl_relaxed(gpc_base + GPC_CNTR); - val |= GPU_VPU_PUP_REQ; - writel_relaxed(val, gpc_base + GPC_CNTR); - - /* Wait ISO + ISO2SW IPG clock cycles */ - ndelay((sw + sw2iso) * 1000 / 66); - - /* Disable reset clocks for all devices in the PU domain */ - for (i = 0; i < pu->num_clks; i++) - clk_disable_unprepare(pu->clk[i]); - - return 0; -} - -static struct generic_pm_domain imx6q_arm_domain = { - .name = "ARM", -}; - -static struct pu_domain imx6q_pu_domain = { - .base = { - .name = "PU", - .power_off = imx6q_pm_pu_power_off, - .power_on = imx6q_pm_pu_power_on, - }, -}; - -static struct generic_pm_domain imx6sl_display_domain = { - .name = "DISPLAY", -}; - -static struct generic_pm_domain *imx_gpc_domains[] = { - &imx6q_arm_domain, - &imx6q_pu_domain.base, - &imx6sl_display_domain, -}; - -static struct genpd_onecell_data imx_gpc_onecell_data = { - .domains = imx_gpc_domains, - .num_domains = ARRAY_SIZE(imx_gpc_domains), -}; - -static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg) -{ - struct clk *clk; - int i, ret; - - imx6q_pu_domain.reg = pu_reg; - - for (i = 0; ; i++) { - 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); - goto clk_err; - } - imx6q_pu_domain.clk[i] = clk; - } - imx6q_pu_domain.num_clks = i; - - /* Enable power always in case bootloader disabled it. */ - imx6q_pm_pu_power_on(&imx6q_pu_domain.base); - - if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) - return 0; - - imx6q_pu_domain.base.states = devm_kzalloc(dev, - sizeof(*imx6q_pu_domain.base.states), - GFP_KERNEL); - if (!imx6q_pu_domain.base.states) - return -ENOMEM; - - imx6q_pu_domain.base.states[0].power_off_latency_ns = 25000; - imx6q_pu_domain.base.states[0].power_on_latency_ns = 2000000; - imx6q_pu_domain.base.state_count = 1; - - for (i = 0; i < ARRAY_SIZE(imx_gpc_domains); i++) - pm_genpd_init(imx_gpc_domains[i], NULL, false); - - ret = of_genpd_add_provider_onecell(dev->of_node, - &imx_gpc_onecell_data); - if (ret) - goto power_off; - - return 0; - -power_off: - imx6q_pm_pu_power_off(&imx6q_pu_domain.base); -clk_err: - while (i--) - clk_put(imx6q_pu_domain.clk[i]); - imx6q_pu_domain.reg = NULL; - return -EINVAL; -} - -static int imx_gpc_probe(struct platform_device *pdev) -{ - struct regulator *pu_reg; - int ret; - - /* 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")) - return 0; - - pu_reg = devm_regulator_get_optional(&pdev->dev, "pu"); - if (PTR_ERR(pu_reg) == -ENODEV) - pu_reg = NULL; - if (IS_ERR(pu_reg)) { - ret = PTR_ERR(pu_reg); - dev_err(&pdev->dev, "failed to get pu regulator: %d\n", ret); - return ret; - } - - return imx_gpc_genpd_init(&pdev->dev, pu_reg); -} - -static const struct of_device_id imx_gpc_dt_ids[] = { - { .compatible = "fsl,imx6q-gpc" }, - { .compatible = "fsl,imx6sl-gpc" }, - { } -}; - -static struct platform_driver imx_gpc_driver = { - .driver = { - .name = "imx-gpc", - .of_match_table = imx_gpc_dt_ids, - }, - .probe = imx_gpc_probe, -}; - -static int __init imx_pgc_init(void) -{ - return platform_driver_register(&imx_gpc_driver); -} -subsys_initcall(imx_pgc_init); diff --git a/arch/arm/mach-keystone/Kconfig b/arch/arm/mach-keystone/Kconfig index 5543570..db12235 100644 --- a/arch/arm/mach-keystone/Kconfig +++ b/arch/arm/mach-keystone/Kconfig @@ -10,6 +10,7 @@ config ARCH_KEYSTONE select ARCH_SUPPORTS_BIG_ENDIAN select ZONE_DMA if ARM_LPAE select PINCTRL + select PM_GENERIC_DOMAINS if PM help Support for boards based on the Texas Instruments Keystone family of SoCs. diff --git a/arch/arm/mach-keystone/pm_domain.c b/arch/arm/mach-keystone/pm_domain.c index 8cbb357..fe57e26 100644 --- a/arch/arm/mach-keystone/pm_domain.c +++ b/arch/arm/mach-keystone/pm_domain.c @@ -32,7 +32,9 @@ static struct pm_clk_notifier_block platform_domain_notifier = { }; static const struct of_device_id of_keystone_table[] = { - {.compatible = "ti,keystone"}, + {.compatible = "ti,k2hk"}, + {.compatible = "ti,k2e"}, + {.compatible = "ti,k2l"}, { /* end of list */ }, }; diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index fffad24..3b33f0bb 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -2,7 +2,6 @@ asflags-y += -march=armv7-a obj-y += io.o obj-y += irq.o -obj-y += flowctrl.o obj-y += pm.o obj-y += reset.o obj-y += reset-handler.o diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index afcee04..76e4c83 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -26,12 +26,13 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <soc/tegra/flowctrl.h> + #include <asm/cpuidle.h> #include <asm/smp_plat.h> #include <asm/suspend.h> #include "cpuidle.h" -#include "flowctrl.h" #include "iomap.h" #include "irq.h" #include "pm.h" diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 75620ae..b5a2afe 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -21,6 +21,7 @@ #include <linux/jiffies.h> #include <linux/smp.h> +#include <soc/tegra/flowctrl.h> #include <soc/tegra/fuse.h> #include <soc/tegra/pmc.h> @@ -30,7 +31,6 @@ #include <asm/smp_scu.h> #include "common.h" -#include "flowctrl.h" #include "iomap.h" #include "reset.h" diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index b0f48a3..1ad5719 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -27,6 +27,7 @@ #include <linux/spinlock.h> #include <linux/suspend.h> +#include <soc/tegra/flowctrl.h> #include <soc/tegra/fuse.h> #include <soc/tegra/pm.h> #include <soc/tegra/pmc.h> @@ -38,7 +39,6 @@ #include <asm/suspend.h> #include <asm/tlbflush.h> -#include "flowctrl.h" #include "iomap.h" #include "pm.h" #include "reset.h" diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S index e3070fda..805f306 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S @@ -17,12 +17,12 @@ #include <linux/init.h> #include <linux/linkage.h> +#include <soc/tegra/flowctrl.h> #include <soc/tegra/fuse.h> #include <asm/asm-offsets.h> #include <asm/cache.h> -#include "flowctrl.h" #include "iomap.h" #include "reset.h" #include "sleep.h" diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S index f5d1966..5c8e638 100644 --- a/arch/arm/mach-tegra/sleep-tegra20.S +++ b/arch/arm/mach-tegra/sleep-tegra20.S @@ -20,6 +20,8 @@ #include <linux/linkage.h> +#include <soc/tegra/flowctrl.h> + #include <asm/assembler.h> #include <asm/proc-fns.h> #include <asm/cp15.h> @@ -27,7 +29,6 @@ #include "irammap.h" #include "sleep.h" -#include "flowctrl.h" #define EMC_CFG 0xc #define EMC_ADR_CFG 0x10 diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S index 16e5ff0..dd4a67d 100644 --- a/arch/arm/mach-tegra/sleep-tegra30.S +++ b/arch/arm/mach-tegra/sleep-tegra30.S @@ -16,13 +16,13 @@ #include <linux/linkage.h> +#include <soc/tegra/flowctrl.h> #include <soc/tegra/fuse.h> #include <asm/asm-offsets.h> #include <asm/assembler.h> #include <asm/cache.h> -#include "flowctrl.h" #include "irammap.h" #include "sleep.h" diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S index f024a51..5e34967 100644 --- a/arch/arm/mach-tegra/sleep.S +++ b/arch/arm/mach-tegra/sleep.S @@ -30,8 +30,6 @@ #include <asm/hardware/cache-l2x0.h> #include "iomap.h" - -#include "flowctrl.h" #include "sleep.h" #define CLK_RESET_CCLK_BURST 0x20 diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index e01cbca..649e9e8 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -48,7 +48,6 @@ #include "board.h" #include "common.h" #include "cpuidle.h" -#include "flowctrl.h" #include "iomap.h" #include "irq.h" #include "pm.h" @@ -75,7 +74,6 @@ static void __init tegra_init_early(void) { of_register_trusted_foundations(); tegra_cpu_reset_handler_init(); - tegra_flowctrl_init(); } static void __init tegra_dt_init_irq(void) diff --git a/arch/arm/plat-versatile/include/plat/clock.h b/arch/arm/plat-versatile/include/plat/clock.h deleted file mode 100644 index 3cfb024..0000000 --- a/arch/arm/plat-versatile/include/plat/clock.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef PLAT_CLOCK_H -#define PLAT_CLOCK_H - -#include <asm/hardware/icst.h> - -struct clk_ops { - long (*round)(struct clk *, unsigned long); - int (*set)(struct clk *, unsigned long); - void (*setvco)(struct clk *, struct icst_vco); -}; - -int icst_clk_set(struct clk *, unsigned long); -long icst_clk_round(struct clk *, unsigned long); - -#endif diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index ff6cb9e..de3eaf0 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 3048cc1..cd931a5 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 0000000..6c3bd5f --- /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 ad19642..da49a83 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 dc26e59..909deda 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 a6da2aa..8aa875f 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 8ff0374..79413040 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 4faa944..09fbe66 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 04e6f0a..5add02e 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 74c3216..401558b 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 c56efc7..6fdfee3 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 a89a927..d6960de 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/arch/arm/common/icst.c b/drivers/clk/versatile/icst.c index d7ed252..de2af63 100644 --- a/arch/arm/common/icst.c +++ b/drivers/clk/versatile/icst.c @@ -17,7 +17,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <asm/div64.h> -#include <asm/hardware/icst.h> +#include "icst.h" /* * Divisors for each OD setting. diff --git a/arch/arm/include/asm/hardware/icst.h b/drivers/clk/versatile/icst.h index 794220b..7519bba 100644 --- a/arch/arm/include/asm/hardware/icst.h +++ b/drivers/clk/versatile/icst.h @@ -1,6 +1,4 @@ /* - * arch/arm/include/asm/hardware/icst.h - * * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -11,8 +9,8 @@ * clock generators. See http://www.idt.com/ for more information * on these devices. */ -#ifndef ASMARM_HARDWARE_ICST_H -#define ASMARM_HARDWARE_ICST_H +#ifndef ICST_H +#define ICST_H struct icst_params { unsigned long ref; diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 9ad0b19..f6cfc31 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 b0d2549..ff20442 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 8ad226c..93e3b96 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 c933259..6e6d561 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 d987bcc..bb16510 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 6a0f154..9bea691 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 f207c3b..70bfc98 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 f4cdfe9..d21c07c 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 2cd3f6c..02a74db 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 0000000..37496bd --- /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 6b97631..2674880 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 0000000..4db177b --- /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 c32f11a..a8b915e 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 0d9036d..cf5b974 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 bbc4c06..11d651b 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 43e4a9f..07224c0 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 b44f6b5..cd585cd 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 9c11be3..c4ba898 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 45e5b13..309643f 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 3467de7..824b442 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 94e7335..b6195fd 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 0000000..357a5d8 --- /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 0000000..5b6e396 --- /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 0000000..47e7aa9 --- /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 0000000..3039072 --- /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, ®map_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 5e7537c..7412666 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 225c35c..528a137 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 f6e842e..07edb04 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 3309603..ca26f13 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 2455339..8b25bd5 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 3619f2e..4d7694a 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 56d9244..bd4a76f 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 a469e36..40d4229a 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 e5e124c..dcf088d 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 ae857ff..4f81dd5 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/arch/arm/mach-tegra/flowctrl.c b/drivers/soc/tegra/flowctrl.c index 475e783..0e345c0 100644 --- a/arch/arm/mach-tegra/flowctrl.c +++ b/drivers/soc/tegra/flowctrl.c @@ -1,7 +1,7 @@ /* - * arch/arm/mach-tegra/flowctrl.c + * drivers/soc/tegra/flowctrl.c * - * functions and macros to control the flowcontroller + * Functions and macros to control the flowcontroller * * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. * @@ -24,11 +24,12 @@ #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> -#include "flowctrl.h" - static u8 flowctrl_offset_halt_cpu[] = { FLOW_CTRL_HALT_CPU0_EVENTS, FLOW_CTRL_HALT_CPU1_EVENTS, @@ -47,6 +48,10 @@ 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 */ @@ -58,6 +63,10 @@ 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); } @@ -140,7 +149,23 @@ void flowctrl_cpu_suspend_exit(unsigned int cpuid) flowctrl_write_cpu_csr(cpuid, reg); } -static const struct of_device_id matches[] __initconst = { +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" }, @@ -148,24 +173,52 @@ static const struct of_device_id matches[] __initconst = { { } }; -void __init tegra_flowctrl_init(void) +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) { - /* hardcoded fallback if device tree node is missing */ - unsigned long base = 0x60007000; - unsigned long size = SZ_4K; + struct resource res; struct device_node *np; - np = of_find_matching_node(NULL, matches); - if (np) { - struct resource res; + if (!soc_is_tegra()) + return 0; - if (of_address_to_resource(np, 0, &res) == 0) { - size = resource_size(&res); - base = res.start; + 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(base, size); + 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 de2c1bf..7413f60 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 0000000..6f5c6f9 --- /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 3557c5e..39e152a 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 48ff3a7..7d57273 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 0000000..b0b2838 --- /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 5ed924f..4dc5d62 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 61c8d84..c42aeaa 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; diff --git a/include/dt-bindings/genpd/k2g.h b/include/dt-bindings/genpd/k2g.h new file mode 100644 index 0000000..1f31f17 --- /dev/null +++ b/include/dt-bindings/genpd/k2g.h @@ -0,0 +1,90 @@ +/* + * TI K2G SoC Device definitions + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef _DT_BINDINGS_GENPD_K2G_H +#define _DT_BINDINGS_GENPD_K2G_H + +/* Documented in http://processors.wiki.ti.com/index.php/TISCI */ + +#define K2G_DEV_PMMC0 0x0000 +#define K2G_DEV_MLB0 0x0001 +#define K2G_DEV_DSS0 0x0002 +#define K2G_DEV_MCBSP0 0x0003 +#define K2G_DEV_MCASP0 0x0004 +#define K2G_DEV_MCASP1 0x0005 +#define K2G_DEV_MCASP2 0x0006 +#define K2G_DEV_DCAN0 0x0008 +#define K2G_DEV_DCAN1 0x0009 +#define K2G_DEV_EMIF0 0x000a +#define K2G_DEV_MMCHS0 0x000b +#define K2G_DEV_MMCHS1 0x000c +#define K2G_DEV_GPMC0 0x000d +#define K2G_DEV_ELM0 0x000e +#define K2G_DEV_SPI0 0x0010 +#define K2G_DEV_SPI1 0x0011 +#define K2G_DEV_SPI2 0x0012 +#define K2G_DEV_SPI3 0x0013 +#define K2G_DEV_ICSS0 0x0014 +#define K2G_DEV_ICSS1 0x0015 +#define K2G_DEV_USB0 0x0016 +#define K2G_DEV_USB1 0x0017 +#define K2G_DEV_NSS0 0x0018 +#define K2G_DEV_PCIE0 0x0019 +#define K2G_DEV_GPIO0 0x001b +#define K2G_DEV_GPIO1 0x001c +#define K2G_DEV_TIMER64_0 0x001d +#define K2G_DEV_TIMER64_1 0x001e +#define K2G_DEV_TIMER64_2 0x001f +#define K2G_DEV_TIMER64_3 0x0020 +#define K2G_DEV_TIMER64_4 0x0021 +#define K2G_DEV_TIMER64_5 0x0022 +#define K2G_DEV_TIMER64_6 0x0023 +#define K2G_DEV_MSGMGR0 0x0025 +#define K2G_DEV_BOOTCFG0 0x0026 +#define K2G_DEV_ARM_BOOTROM0 0x0027 +#define K2G_DEV_DSP_BOOTROM0 0x0029 +#define K2G_DEV_DEBUGSS0 0x002b +#define K2G_DEV_UART0 0x002c +#define K2G_DEV_UART1 0x002d +#define K2G_DEV_UART2 0x002e +#define K2G_DEV_EHRPWM0 0x002f +#define K2G_DEV_EHRPWM1 0x0030 +#define K2G_DEV_EHRPWM2 0x0031 +#define K2G_DEV_EHRPWM3 0x0032 +#define K2G_DEV_EHRPWM4 0x0033 +#define K2G_DEV_EHRPWM5 0x0034 +#define K2G_DEV_EQEP0 0x0035 +#define K2G_DEV_EQEP1 0x0036 +#define K2G_DEV_EQEP2 0x0037 +#define K2G_DEV_ECAP0 0x0038 +#define K2G_DEV_ECAP1 0x0039 +#define K2G_DEV_I2C0 0x003a +#define K2G_DEV_I2C1 0x003b +#define K2G_DEV_I2C2 0x003c +#define K2G_DEV_EDMA0 0x003f +#define K2G_DEV_SEMAPHORE0 0x0040 +#define K2G_DEV_INTC0 0x0041 +#define K2G_DEV_GIC0 0x0042 +#define K2G_DEV_QSPI0 0x0043 +#define K2G_DEV_ARM_64B_COUNTER0 0x0044 +#define K2G_DEV_TETRIS0 0x0045 +#define K2G_DEV_CGEM0 0x0046 +#define K2G_DEV_MSMC0 0x0047 +#define K2G_DEV_CBASS0 0x0049 +#define K2G_DEV_BOARD0 0x004c +#define K2G_DEV_EDMA1 0x004f + +#endif diff --git a/include/dt-bindings/power/imx7-power.h b/include/dt-bindings/power/imx7-power.h new file mode 100644 index 0000000..3a181e4 --- /dev/null +++ b/include/dt-bindings/power/imx7-power.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2017 Impinj + * + * 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. + */ + +#ifndef __DT_BINDINGS_IMX7_POWER_H__ +#define __DT_BINDINGS_IMX7_POWER_H__ + +#define IMX7_POWER_DOMAIN_MIPI_PHY 0 +#define IMX7_POWER_DOMAIN_PCIE_PHY 1 +#define IMX7_POWER_DOMAIN_USB_HSIC_PHY 2 + +#endif diff --git a/include/dt-bindings/power/r8a7795-sysc.h b/include/dt-bindings/power/r8a7795-sysc.h index ee2e26b..ad679ee 100644 --- a/include/dt-bindings/power/r8a7795-sysc.h +++ b/include/dt-bindings/power/r8a7795-sysc.h @@ -33,7 +33,7 @@ #define R8A7795_PD_CA53_SCU 21 #define R8A7795_PD_3DG_E 22 #define R8A7795_PD_A3IR 24 -#define R8A7795_PD_A2VC0 25 +#define R8A7795_PD_A2VC0 25 /* ES1.x only */ #define R8A7795_PD_A2VC1 26 /* Always-on power area */ diff --git a/include/dt-bindings/reset/altr,rst-mgr-a10sr.h b/include/dt-bindings/reset/altr,rst-mgr-a10sr.h new file mode 100644 index 0000000..9855925 --- /dev/null +++ b/include/dt-bindings/reset/altr,rst-mgr-a10sr.h @@ -0,0 +1,33 @@ +/* + * 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 binding definitions for Altera Arria10 MAX5 System Resource Chip + * + * Adapted from altr,rst-mgr-a10.h + */ + +#ifndef _DT_BINDINGS_RESET_ALTR_RST_MGR_A10SR_H +#define _DT_BINDINGS_RESET_ALTR_RST_MGR_A10SR_H + +/* Peripheral PHY resets */ +#define A10SR_RESET_ENET_HPS 0 +#define A10SR_RESET_PCIE 1 +#define A10SR_RESET_FILE 2 +#define A10SR_RESET_BQSPI 3 +#define A10SR_RESET_USB 4 + +#define A10SR_RESET_NUM 5 + +#endif diff --git a/include/dt-bindings/reset/imx7-reset.h b/include/dt-bindings/reset/imx7-reset.h new file mode 100644 index 0000000..6394817 --- /dev/null +++ b/include/dt-bindings/reset/imx7-reset.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 Impinj, Inc. + * + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * 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/>. + */ + +#ifndef DT_BINDING_RESET_IMX7_H +#define DT_BINDING_RESET_IMX7_H + +#define IMX7_RESET_A7_CORE_POR_RESET0 0 +#define IMX7_RESET_A7_CORE_POR_RESET1 1 +#define IMX7_RESET_A7_CORE_RESET0 2 +#define IMX7_RESET_A7_CORE_RESET1 3 +#define IMX7_RESET_A7_DBG_RESET0 4 +#define IMX7_RESET_A7_DBG_RESET1 5 +#define IMX7_RESET_A7_ETM_RESET0 6 +#define IMX7_RESET_A7_ETM_RESET1 7 +#define IMX7_RESET_A7_SOC_DBG_RESET 8 +#define IMX7_RESET_A7_L2RESET 9 +#define IMX7_RESET_SW_M4C_RST 10 +#define IMX7_RESET_SW_M4P_RST 11 +#define IMX7_RESET_EIM_RST 12 +#define IMX7_RESET_HSICPHY_PORT_RST 13 +#define IMX7_RESET_USBPHY1_POR 14 +#define IMX7_RESET_USBPHY1_PORT_RST 15 +#define IMX7_RESET_USBPHY2_POR 16 +#define IMX7_RESET_USBPHY2_PORT_RST 17 +#define IMX7_RESET_MIPI_PHY_MRST 18 +#define IMX7_RESET_MIPI_PHY_SRST 19 + +/* + * IMX7_RESET_PCIEPHY is a logical reset line combining PCIEPHY_BTN + * and PCIEPHY_G_RST + */ +#define IMX7_RESET_PCIEPHY 20 +#define IMX7_RESET_PCIEPHY_PERST 21 + +/* + * IMX7_RESET_PCIE_CTRL_APPS_EN is not strictly a reset line, but it + * can be used to inhibit PCIe LTTSM, so, in a way, it can be thoguht + * of as one + */ +#define IMX7_RESET_PCIE_CTRL_APPS_EN 22 +#define IMX7_RESET_DDRC_PRST 23 +#define IMX7_RESET_DDRC_CORE_RST 24 + +#define IMX7_RESET_NUM 25 + +#endif + diff --git a/include/linux/firmware/meson/meson_sm.h b/include/linux/firmware/meson/meson_sm.h index 8e953c6..37a5eae 100644 --- a/include/linux/firmware/meson/meson_sm.h +++ b/include/linux/firmware/meson/meson_sm.h @@ -25,7 +25,7 @@ int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); int meson_sm_call_write(void *buffer, unsigned int b_size, unsigned int cmd_index, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); -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); #endif /* _MESON_SM_FW_H_ */ diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 9b6abe6..b7803a2 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -118,6 +118,7 @@ struct generic_pm_domain_data { struct pm_domain_data base; struct gpd_timing_data td; struct notifier_block nb; + void *data; }; #ifdef CONFIG_PM_GENERIC_DOMAINS diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index d32f6f1..e538047 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -40,6 +40,9 @@ extern int qcom_scm_pas_shutdown(u32 peripheral); extern void qcom_scm_cpu_power_down(u32 flags); extern u32 qcom_scm_get_version(void); extern int qcom_scm_set_remote_state(u32 state, u32 id); +extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); +extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); +extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare); #else static inline int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) @@ -67,5 +70,8 @@ static inline void qcom_scm_cpu_power_down(u32 flags) {} static inline u32 qcom_scm_get_version(void) { return 0; } static inline u32 qcom_scm_set_remote_state(u32 state,u32 id) { return -ENODEV; } +static inline int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) { return -ENODEV; } +static inline int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) { return -ENODEV; } +static inline int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) { return -ENODEV; } #endif #endif diff --git a/arch/arm/mach-tegra/flowctrl.h b/include/soc/tegra/flowctrl.h index 73a9c50..8f86aea 100644 --- a/arch/arm/mach-tegra/flowctrl.h +++ b/include/soc/tegra/flowctrl.h @@ -1,7 +1,5 @@ /* - * arch/arm/mach-tegra/flowctrl.h - * - * functions and macros to control the flowcontroller + * Functions and macros to control the flowcontroller * * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. * @@ -18,8 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __MACH_TEGRA_FLOWCTRL_H -#define __MACH_TEGRA_FLOWCTRL_H +#ifndef __SOC_TEGRA_FLOWCTRL_H__ +#define __SOC_TEGRA_FLOWCTRL_H__ #define FLOW_CTRL_HALT_CPU0_EVENTS 0x0 #define FLOW_CTRL_WAITEVENT (2 << 29) @@ -53,14 +51,32 @@ #define TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8) #ifndef __ASSEMBLY__ +#ifdef CONFIG_SOC_TEGRA_FLOWCTRL u32 flowctrl_read_cpu_csr(unsigned int cpuid); void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value); void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value); void flowctrl_cpu_suspend_enter(unsigned int cpuid); void flowctrl_cpu_suspend_exit(unsigned int cpuid); +#else +static inline u32 flowctrl_read_cpu_csr(unsigned int cpuid) +{ + return 0; +} + +static inline void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value) +{ +} + +static inline void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value) {} -void tegra_flowctrl_init(void); -#endif +static inline void flowctrl_cpu_suspend_enter(unsigned int cpuid) +{ +} -#endif +static inline void flowctrl_cpu_suspend_exit(unsigned int cpuid) +{ +} +#endif /* CONFIG_SOC_TEGRA_FLOWCTRL */ +#endif /* __ASSEMBLY */ +#endif /* __SOC_TEGRA_FLOWCTRL_H__ */ diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index 2f271d1..1c3982b 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h @@ -26,12 +26,6 @@ struct clk; struct reset_control; -#ifdef CONFIG_PM_SLEEP -enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); -void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); -void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); -#endif /* CONFIG_PM_SLEEP */ - #ifdef CONFIG_SMP bool tegra_pmc_cpu_is_powered(unsigned int cpuid); int tegra_pmc_cpu_power_on(unsigned int cpuid); @@ -144,7 +138,7 @@ enum tegra_io_pad_voltage { TEGRA_IO_PAD_3300000UV, }; -#ifdef CONFIG_ARCH_TEGRA +#ifdef CONFIG_SOC_TEGRA_PMC int tegra_powergate_is_powered(unsigned int id); int tegra_powergate_power_on(unsigned int id); int tegra_powergate_power_off(unsigned int id); @@ -163,6 +157,11 @@ int tegra_io_pad_get_voltage(enum tegra_io_pad id); /* deprecated, use tegra_io_pad_power_{enable,disable}() instead */ int tegra_io_rail_power_on(unsigned int id); int tegra_io_rail_power_off(unsigned int id); + +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); +void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); +void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); + #else static inline int tegra_powergate_is_powered(unsigned int id) { @@ -221,6 +220,20 @@ static inline int tegra_io_rail_power_off(unsigned int id) { return -ENOSYS; } -#endif /* CONFIG_ARCH_TEGRA */ + +static inline enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) +{ + return TEGRA_SUSPEND_NONE; +} + +static inline void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode) +{ +} + +static inline void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) +{ +} + +#endif /* CONFIG_SOC_TEGRA_PMC */ #endif /* __SOC_TEGRA_PMC_H__ */ |