diff options
author | Thierry Reding <treding@nvidia.com> | 2017-02-23 12:11:57 -0500 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2017-04-04 09:43:50 -0400 |
commit | 5e7d4c65294174d6f58fe36df3edd55cd3b859d6 (patch) | |
tree | ebdf2ddfd0f345b671a3d803155de6ad81657987 | |
parent | c1ae3cfa0e89fa1a7ecc4c99031f5e9ae99d9201 (diff) |
soc/tegra: Implement Tegra186 PMC support
The power management controller on Tegra186 has changed in backwards-
incompatible ways with respect to earlier generations. This implements a
new driver that supports inversion of the PMU interrupt as well as the
"recovery", "bootloader" and "forced-recovery" reboot commands.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r-- | Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.txt | 34 | ||||
-rw-r--r-- | drivers/soc/tegra/Kconfig | 13 | ||||
-rw-r--r-- | drivers/soc/tegra/Makefile | 3 | ||||
-rw-r--r-- | drivers/soc/tegra/pmc-tegra186.c | 169 |
4 files changed, 218 insertions, 1 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 000000000000..078a58b0302f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.txt | |||
@@ -0,0 +1,34 @@ | |||
1 | NVIDIA Tegra Power Management Controller (PMC) | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: Should contain one of the following: | ||
5 | - "nvidia,tegra186-pmc": for Tegra186 | ||
6 | - reg: Must contain an (offset, length) pair of the register set for each | ||
7 | entry in reg-names. | ||
8 | - reg-names: Must include the following entries: | ||
9 | - "pmc" | ||
10 | - "wake" | ||
11 | - "aotag" | ||
12 | - "scratch" | ||
13 | |||
14 | Optional properties: | ||
15 | - nvidia,invert-interrupt: If present, inverts the PMU interrupt signal. | ||
16 | |||
17 | Example: | ||
18 | |||
19 | SoC DTSI: | ||
20 | |||
21 | pmc@c3600000 { | ||
22 | compatible = "nvidia,tegra186-pmc"; | ||
23 | reg = <0 0x0c360000 0 0x10000>, | ||
24 | <0 0x0c370000 0 0x10000>, | ||
25 | <0 0x0c380000 0 0x10000>, | ||
26 | <0 0x0c390000 0 0x10000>; | ||
27 | reg-names = "pmc", "wake", "aotag", "scratch"; | ||
28 | }; | ||
29 | |||
30 | Board DTS: | ||
31 | |||
32 | pmc@c360000 { | ||
33 | nvidia,invert-interrupt; | ||
34 | }; | ||
diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index e5e124c07066..208d6edb3fdb 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig | |||
@@ -12,6 +12,7 @@ config ARCH_TEGRA_2x_SOC | |||
12 | select PINCTRL_TEGRA20 | 12 | select PINCTRL_TEGRA20 |
13 | select PL310_ERRATA_727915 if CACHE_L2X0 | 13 | select PL310_ERRATA_727915 if CACHE_L2X0 |
14 | select PL310_ERRATA_769419 if CACHE_L2X0 | 14 | select PL310_ERRATA_769419 if CACHE_L2X0 |
15 | select SOC_TEGRA_PMC | ||
15 | select TEGRA_TIMER | 16 | select TEGRA_TIMER |
16 | help | 17 | help |
17 | Support for NVIDIA Tegra AP20 and T20 processors, based on the | 18 | Support for NVIDIA Tegra AP20 and T20 processors, based on the |
@@ -23,6 +24,7 @@ config ARCH_TEGRA_3x_SOC | |||
23 | select ARM_ERRATA_764369 if SMP | 24 | select ARM_ERRATA_764369 if SMP |
24 | select PINCTRL_TEGRA30 | 25 | select PINCTRL_TEGRA30 |
25 | select PL310_ERRATA_769419 if CACHE_L2X0 | 26 | select PL310_ERRATA_769419 if CACHE_L2X0 |
27 | select SOC_TEGRA_PMC | ||
26 | select TEGRA_TIMER | 28 | select TEGRA_TIMER |
27 | help | 29 | help |
28 | Support for NVIDIA Tegra T30 processor family, based on the | 30 | Support for NVIDIA Tegra T30 processor family, based on the |
@@ -33,6 +35,7 @@ config ARCH_TEGRA_114_SOC | |||
33 | select ARM_ERRATA_798181 if SMP | 35 | select ARM_ERRATA_798181 if SMP |
34 | select HAVE_ARM_ARCH_TIMER | 36 | select HAVE_ARM_ARCH_TIMER |
35 | select PINCTRL_TEGRA114 | 37 | select PINCTRL_TEGRA114 |
38 | select SOC_TEGRA_PMC | ||
36 | select TEGRA_TIMER | 39 | select TEGRA_TIMER |
37 | help | 40 | help |
38 | Support for NVIDIA Tegra T114 processor family, based on the | 41 | Support for NVIDIA Tegra T114 processor family, based on the |
@@ -42,6 +45,7 @@ config ARCH_TEGRA_124_SOC | |||
42 | bool "Enable support for Tegra124 family" | 45 | bool "Enable support for Tegra124 family" |
43 | select HAVE_ARM_ARCH_TIMER | 46 | select HAVE_ARM_ARCH_TIMER |
44 | select PINCTRL_TEGRA124 | 47 | select PINCTRL_TEGRA124 |
48 | select SOC_TEGRA_PMC | ||
45 | select TEGRA_TIMER | 49 | select TEGRA_TIMER |
46 | help | 50 | help |
47 | Support for NVIDIA Tegra T124 processor family, based on the | 51 | Support for NVIDIA Tegra T124 processor family, based on the |
@@ -55,6 +59,7 @@ if ARM64 | |||
55 | config ARCH_TEGRA_132_SOC | 59 | config ARCH_TEGRA_132_SOC |
56 | bool "NVIDIA Tegra132 SoC" | 60 | bool "NVIDIA Tegra132 SoC" |
57 | select PINCTRL_TEGRA124 | 61 | select PINCTRL_TEGRA124 |
62 | select SOC_TEGRA_PMC | ||
58 | help | 63 | help |
59 | Enable support for NVIDIA Tegra132 SoC, based on the Denver | 64 | Enable support for NVIDIA Tegra132 SoC, based on the Denver |
60 | ARMv8 CPU. The Tegra132 SoC is similar to the Tegra124 SoC, | 65 | ARMv8 CPU. The Tegra132 SoC is similar to the Tegra124 SoC, |
@@ -64,6 +69,7 @@ config ARCH_TEGRA_132_SOC | |||
64 | config ARCH_TEGRA_210_SOC | 69 | config ARCH_TEGRA_210_SOC |
65 | bool "NVIDIA Tegra210 SoC" | 70 | bool "NVIDIA Tegra210 SoC" |
66 | select PINCTRL_TEGRA210 | 71 | select PINCTRL_TEGRA210 |
72 | select SOC_TEGRA_PMC | ||
67 | help | 73 | help |
68 | Enable support for the NVIDIA Tegra210 SoC. Also known as Tegra X1, | 74 | Enable support for the NVIDIA Tegra210 SoC. Also known as Tegra X1, |
69 | the Tegra210 has four Cortex-A57 cores paired with four Cortex-A53 | 75 | the Tegra210 has four Cortex-A57 cores paired with four Cortex-A53 |
@@ -83,6 +89,7 @@ config ARCH_TEGRA_186_SOC | |||
83 | select TEGRA_BPMP | 89 | select TEGRA_BPMP |
84 | select TEGRA_HSP_MBOX | 90 | select TEGRA_HSP_MBOX |
85 | select TEGRA_IVC | 91 | select TEGRA_IVC |
92 | select SOC_TEGRA_PMC_TEGRA186 | ||
86 | help | 93 | help |
87 | Enable support for the NVIDIA Tegar186 SoC. The Tegra186 features a | 94 | Enable support for the NVIDIA Tegar186 SoC. The Tegra186 features a |
88 | combination of Denver and Cortex-A57 CPU cores and a GPU based on | 95 | combination of Denver and Cortex-A57 CPU cores and a GPU based on |
@@ -93,3 +100,9 @@ config ARCH_TEGRA_186_SOC | |||
93 | 100 | ||
94 | endif | 101 | endif |
95 | endif | 102 | endif |
103 | |||
104 | config SOC_TEGRA_PMC | ||
105 | bool | ||
106 | |||
107 | config SOC_TEGRA_PMC_TEGRA186 | ||
108 | bool | ||
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index ae857ff7d53d..b4425e4319ff 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-y += fuse/ | 1 | obj-y += fuse/ |
2 | 2 | ||
3 | obj-y += common.o | 3 | obj-y += common.o |
4 | obj-y += pmc.o | 4 | obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o |
5 | obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o | ||
diff --git a/drivers/soc/tegra/pmc-tegra186.c b/drivers/soc/tegra/pmc-tegra186.c new file mode 100644 index 000000000000..6f5c6f98ba92 --- /dev/null +++ b/drivers/soc/tegra/pmc-tegra186.c | |||
@@ -0,0 +1,169 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | */ | ||
13 | |||
14 | #define pr_fmt(fmt) "tegra-pmc: " fmt | ||
15 | |||
16 | #include <linux/io.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/reboot.h> | ||
21 | |||
22 | #include <asm/system_misc.h> | ||
23 | |||
24 | #define PMC_CNTRL 0x000 | ||
25 | #define PMC_CNTRL_MAIN_RST BIT(4) | ||
26 | |||
27 | #define PMC_RST_STATUS 0x070 | ||
28 | |||
29 | #define WAKE_AOWAKE_CTRL 0x4f4 | ||
30 | #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) | ||
31 | |||
32 | #define SCRATCH_SCRATCH0 0x2000 | ||
33 | #define SCRATCH_SCRATCH0_MODE_RECOVERY BIT(31) | ||
34 | #define SCRATCH_SCRATCH0_MODE_BOOTLOADER BIT(30) | ||
35 | #define SCRATCH_SCRATCH0_MODE_RCM BIT(1) | ||
36 | #define SCRATCH_SCRATCH0_MODE_MASK (SCRATCH_SCRATCH0_MODE_RECOVERY | \ | ||
37 | SCRATCH_SCRATCH0_MODE_BOOTLOADER | \ | ||
38 | SCRATCH_SCRATCH0_MODE_RCM) | ||
39 | |||
40 | struct tegra_pmc { | ||
41 | struct device *dev; | ||
42 | void __iomem *regs; | ||
43 | void __iomem *wake; | ||
44 | void __iomem *aotag; | ||
45 | void __iomem *scratch; | ||
46 | |||
47 | void (*system_restart)(enum reboot_mode mode, const char *cmd); | ||
48 | struct notifier_block restart; | ||
49 | }; | ||
50 | |||
51 | static int tegra186_pmc_restart_notify(struct notifier_block *nb, | ||
52 | unsigned long action, | ||
53 | void *data) | ||
54 | { | ||
55 | struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, restart); | ||
56 | const char *cmd = data; | ||
57 | u32 value; | ||
58 | |||
59 | value = readl(pmc->scratch + SCRATCH_SCRATCH0); | ||
60 | value &= ~SCRATCH_SCRATCH0_MODE_MASK; | ||
61 | |||
62 | if (cmd) { | ||
63 | if (strcmp(cmd, "recovery") == 0) | ||
64 | value |= SCRATCH_SCRATCH0_MODE_RECOVERY; | ||
65 | |||
66 | if (strcmp(cmd, "bootloader") == 0) | ||
67 | value |= SCRATCH_SCRATCH0_MODE_BOOTLOADER; | ||
68 | |||
69 | if (strcmp(cmd, "forced-recovery") == 0) | ||
70 | value |= SCRATCH_SCRATCH0_MODE_RCM; | ||
71 | } | ||
72 | |||
73 | writel(value, pmc->scratch + SCRATCH_SCRATCH0); | ||
74 | |||
75 | /* | ||
76 | * If available, call the system restart implementation that was | ||
77 | * registered earlier (typically PSCI). | ||
78 | */ | ||
79 | if (pmc->system_restart) { | ||
80 | pmc->system_restart(reboot_mode, cmd); | ||
81 | return NOTIFY_DONE; | ||
82 | } | ||
83 | |||
84 | /* reset everything but SCRATCH0_SCRATCH0 and PMC_RST_STATUS */ | ||
85 | value = readl(pmc->regs + PMC_CNTRL); | ||
86 | value |= PMC_CNTRL_MAIN_RST; | ||
87 | writel(value, pmc->regs + PMC_CNTRL); | ||
88 | |||
89 | return NOTIFY_DONE; | ||
90 | } | ||
91 | |||
92 | static int tegra186_pmc_setup(struct tegra_pmc *pmc) | ||
93 | { | ||
94 | struct device_node *np = pmc->dev->of_node; | ||
95 | bool invert; | ||
96 | u32 value; | ||
97 | |||
98 | invert = of_property_read_bool(np, "nvidia,invert-interrupt"); | ||
99 | |||
100 | value = readl(pmc->wake + WAKE_AOWAKE_CTRL); | ||
101 | |||
102 | if (invert) | ||
103 | value |= WAKE_AOWAKE_CTRL_INTR_POLARITY; | ||
104 | else | ||
105 | value &= ~WAKE_AOWAKE_CTRL_INTR_POLARITY; | ||
106 | |||
107 | writel(value, pmc->wake + WAKE_AOWAKE_CTRL); | ||
108 | |||
109 | /* | ||
110 | * We need to hook any system restart implementation registered | ||
111 | * previously so we can write SCRATCH_SCRATCH0 before reset. | ||
112 | */ | ||
113 | pmc->system_restart = arm_pm_restart; | ||
114 | arm_pm_restart = NULL; | ||
115 | |||
116 | pmc->restart.notifier_call = tegra186_pmc_restart_notify; | ||
117 | pmc->restart.priority = 128; | ||
118 | |||
119 | return register_restart_handler(&pmc->restart); | ||
120 | } | ||
121 | |||
122 | static int tegra186_pmc_probe(struct platform_device *pdev) | ||
123 | { | ||
124 | struct tegra_pmc *pmc; | ||
125 | struct resource *res; | ||
126 | |||
127 | pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); | ||
128 | if (!pmc) | ||
129 | return -ENOMEM; | ||
130 | |||
131 | pmc->dev = &pdev->dev; | ||
132 | |||
133 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmc"); | ||
134 | pmc->regs = devm_ioremap_resource(&pdev->dev, res); | ||
135 | if (IS_ERR(pmc->regs)) | ||
136 | return PTR_ERR(pmc->regs); | ||
137 | |||
138 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake"); | ||
139 | pmc->wake = devm_ioremap_resource(&pdev->dev, res); | ||
140 | if (IS_ERR(pmc->wake)) | ||
141 | return PTR_ERR(pmc->wake); | ||
142 | |||
143 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag"); | ||
144 | pmc->aotag = devm_ioremap_resource(&pdev->dev, res); | ||
145 | if (IS_ERR(pmc->aotag)) | ||
146 | return PTR_ERR(pmc->aotag); | ||
147 | |||
148 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch"); | ||
149 | pmc->scratch = devm_ioremap_resource(&pdev->dev, res); | ||
150 | if (IS_ERR(pmc->scratch)) | ||
151 | return PTR_ERR(pmc->scratch); | ||
152 | |||
153 | return tegra186_pmc_setup(pmc); | ||
154 | } | ||
155 | |||
156 | static const struct of_device_id tegra186_pmc_of_match[] = { | ||
157 | { .compatible = "nvidia,tegra186-pmc" }, | ||
158 | { /* sentinel */ } | ||
159 | }; | ||
160 | MODULE_DEVICE_TABLE(of, tegra186_pmc_of_match); | ||
161 | |||
162 | static struct platform_driver tegra186_pmc_driver = { | ||
163 | .driver = { | ||
164 | .name = "tegra186-pmc", | ||
165 | .of_match_table = tegra186_pmc_of_match, | ||
166 | }, | ||
167 | .probe = tegra186_pmc_probe, | ||
168 | }; | ||
169 | builtin_platform_driver(tegra186_pmc_driver); | ||