diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 14:54:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 14:54:29 -0400 |
commit | f5dcb68086ba2e033b2af32b0da0c7a7c7872a09 (patch) | |
tree | 89c41089b492f8d8d411185bd7cb07538802e837 /drivers/soc | |
parent | 3d9f96d850e4bbfae24dc9aee03033dd77c81596 (diff) | |
parent | 4af34b572a85c44c55491a10693535a79627c478 (diff) |
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Kevin Hilman:
"Some of these are for drivers/soc, where we're now putting
SoC-specific drivers these days. Some are for other driver subsystems
where we have received acks from the appropriate maintainers.
Some highlights:
- simple-mfd: document DT bindings and misc updates
- migrate mach-berlin to simple-mfd for clock, pinctrl and reset
- memory: support for Tegra132 SoC
- memory: introduce tegra EMC driver for scaling memory frequency
- misc. updates for ARM CCI and CCN busses"
* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (48 commits)
drivers: soc: sunxi: Introduce SoC driver to map SRAMs
arm-cci: Add aliases for PMU events
arm-cci: Add CCI-500 PMU support
arm-cci: Sanitise CCI400 PMU driver specific code
arm-cci: Abstract handling for CCI events
arm-cci: Abstract out the PMU counter details
arm-cci: Cleanup PMU driver code
arm-cci: Do not enable CCI-400 PMU by default
firmware: qcom: scm: Add HDCP Support
ARM: berlin: add an ADC node for the BG2Q
ARM: berlin: remove useless chip and system ctrl compatibles
clk: berlin: drop direct of_iomap of nodes reg property
ARM: berlin: move BG2Q clock node
ARM: berlin: move BG2CD clock node
ARM: berlin: move BG2 clock node
clk: berlin: prepare simple-mfd conversion
pinctrl: berlin: drop SoC stub provided regmap
ARM: berlin: move pinctrl to simple-mfd nodes
pinctrl: berlin: prepare to use regmap provided by syscon
reset: berlin: drop arch_initcall initialization
...
Diffstat (limited to 'drivers/soc')
-rw-r--r-- | drivers/soc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/soc/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/qcom/Kconfig | 7 | ||||
-rw-r--r-- | drivers/soc/qcom/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/qcom/spm.c | 385 | ||||
-rw-r--r-- | drivers/soc/sunxi/Kconfig | 10 | ||||
-rw-r--r-- | drivers/soc/sunxi/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/sunxi/sunxi_sram.c | 284 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/tegra-apbmisc.c | 21 |
9 files changed, 711 insertions, 0 deletions
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index d8bde82f0370..96ddecb92254 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig | |||
@@ -2,6 +2,7 @@ menu "SOC (System On Chip) specific Drivers" | |||
2 | 2 | ||
3 | source "drivers/soc/mediatek/Kconfig" | 3 | source "drivers/soc/mediatek/Kconfig" |
4 | source "drivers/soc/qcom/Kconfig" | 4 | source "drivers/soc/qcom/Kconfig" |
5 | source "drivers/soc/sunxi/Kconfig" | ||
5 | source "drivers/soc/ti/Kconfig" | 6 | source "drivers/soc/ti/Kconfig" |
6 | source "drivers/soc/versatile/Kconfig" | 7 | source "drivers/soc/versatile/Kconfig" |
7 | 8 | ||
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 70042b259744..7dc7c0d8a2c1 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ | 5 | obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ |
6 | obj-$(CONFIG_ARCH_QCOM) += qcom/ | 6 | obj-$(CONFIG_ARCH_QCOM) += qcom/ |
7 | obj-$(CONFIG_ARCH_SUNXI) += sunxi/ | ||
7 | obj-$(CONFIG_ARCH_TEGRA) += tegra/ | 8 | obj-$(CONFIG_ARCH_TEGRA) += tegra/ |
8 | obj-$(CONFIG_SOC_TI) += ti/ | 9 | obj-$(CONFIG_SOC_TI) += ti/ |
9 | obj-$(CONFIG_PLAT_VERSATILE) += versatile/ | 10 | obj-$(CONFIG_PLAT_VERSATILE) += versatile/ |
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 460b2dba109c..5eea374c8fa6 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig | |||
@@ -10,3 +10,10 @@ config QCOM_GSBI | |||
10 | functions for connecting the underlying serial UART, SPI, and I2C | 10 | functions for connecting the underlying serial UART, SPI, and I2C |
11 | devices to the output pins. | 11 | devices to the output pins. |
12 | 12 | ||
13 | config QCOM_PM | ||
14 | bool "Qualcomm Power Management" | ||
15 | depends on ARCH_QCOM && !ARM64 | ||
16 | help | ||
17 | QCOM Platform specific power driver to manage cores and L2 low power | ||
18 | modes. It interface with various system drivers to put the cores in | ||
19 | low power modes. | ||
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 438901257ac1..931d385386c5 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o | 1 | obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o |
2 | obj-$(CONFIG_QCOM_PM) += spm.o | ||
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c new file mode 100644 index 000000000000..b562af816c0a --- /dev/null +++ b/drivers/soc/qcom/spm.c | |||
@@ -0,0 +1,385 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. | ||
3 | * Copyright (c) 2014,2015, Linaro Ltd. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 and | ||
7 | * only version 2 as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/of_address.h> | ||
22 | #include <linux/of_device.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/cpuidle.h> | ||
26 | #include <linux/cpu_pm.h> | ||
27 | #include <linux/qcom_scm.h> | ||
28 | |||
29 | #include <asm/cpuidle.h> | ||
30 | #include <asm/proc-fns.h> | ||
31 | #include <asm/suspend.h> | ||
32 | |||
33 | #define MAX_PMIC_DATA 2 | ||
34 | #define MAX_SEQ_DATA 64 | ||
35 | #define SPM_CTL_INDEX 0x7f | ||
36 | #define SPM_CTL_INDEX_SHIFT 4 | ||
37 | #define SPM_CTL_EN BIT(0) | ||
38 | |||
39 | enum pm_sleep_mode { | ||
40 | PM_SLEEP_MODE_STBY, | ||
41 | PM_SLEEP_MODE_RET, | ||
42 | PM_SLEEP_MODE_SPC, | ||
43 | PM_SLEEP_MODE_PC, | ||
44 | PM_SLEEP_MODE_NR, | ||
45 | }; | ||
46 | |||
47 | enum spm_reg { | ||
48 | SPM_REG_CFG, | ||
49 | SPM_REG_SPM_CTL, | ||
50 | SPM_REG_DLY, | ||
51 | SPM_REG_PMIC_DLY, | ||
52 | SPM_REG_PMIC_DATA_0, | ||
53 | SPM_REG_PMIC_DATA_1, | ||
54 | SPM_REG_VCTL, | ||
55 | SPM_REG_SEQ_ENTRY, | ||
56 | SPM_REG_SPM_STS, | ||
57 | SPM_REG_PMIC_STS, | ||
58 | SPM_REG_NR, | ||
59 | }; | ||
60 | |||
61 | struct spm_reg_data { | ||
62 | const u8 *reg_offset; | ||
63 | u32 spm_cfg; | ||
64 | u32 spm_dly; | ||
65 | u32 pmic_dly; | ||
66 | u32 pmic_data[MAX_PMIC_DATA]; | ||
67 | u8 seq[MAX_SEQ_DATA]; | ||
68 | u8 start_index[PM_SLEEP_MODE_NR]; | ||
69 | }; | ||
70 | |||
71 | struct spm_driver_data { | ||
72 | void __iomem *reg_base; | ||
73 | const struct spm_reg_data *reg_data; | ||
74 | }; | ||
75 | |||
76 | static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = { | ||
77 | [SPM_REG_CFG] = 0x08, | ||
78 | [SPM_REG_SPM_CTL] = 0x30, | ||
79 | [SPM_REG_DLY] = 0x34, | ||
80 | [SPM_REG_SEQ_ENTRY] = 0x80, | ||
81 | }; | ||
82 | |||
83 | /* SPM register data for 8974, 8084 */ | ||
84 | static const struct spm_reg_data spm_reg_8974_8084_cpu = { | ||
85 | .reg_offset = spm_reg_offset_v2_1, | ||
86 | .spm_cfg = 0x1, | ||
87 | .spm_dly = 0x3C102800, | ||
88 | .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03, | ||
89 | 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30, | ||
90 | 0x0F }, | ||
91 | .start_index[PM_SLEEP_MODE_STBY] = 0, | ||
92 | .start_index[PM_SLEEP_MODE_SPC] = 3, | ||
93 | }; | ||
94 | |||
95 | static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = { | ||
96 | [SPM_REG_CFG] = 0x08, | ||
97 | [SPM_REG_SPM_CTL] = 0x20, | ||
98 | [SPM_REG_PMIC_DLY] = 0x24, | ||
99 | [SPM_REG_PMIC_DATA_0] = 0x28, | ||
100 | [SPM_REG_PMIC_DATA_1] = 0x2C, | ||
101 | [SPM_REG_SEQ_ENTRY] = 0x80, | ||
102 | }; | ||
103 | |||
104 | /* SPM register data for 8064 */ | ||
105 | static const struct spm_reg_data spm_reg_8064_cpu = { | ||
106 | .reg_offset = spm_reg_offset_v1_1, | ||
107 | .spm_cfg = 0x1F, | ||
108 | .pmic_dly = 0x02020004, | ||
109 | .pmic_data[0] = 0x0084009C, | ||
110 | .pmic_data[1] = 0x00A4001C, | ||
111 | .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01, | ||
112 | 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, | ||
113 | .start_index[PM_SLEEP_MODE_STBY] = 0, | ||
114 | .start_index[PM_SLEEP_MODE_SPC] = 2, | ||
115 | }; | ||
116 | |||
117 | static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv); | ||
118 | |||
119 | typedef int (*idle_fn)(int); | ||
120 | static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops); | ||
121 | |||
122 | static inline void spm_register_write(struct spm_driver_data *drv, | ||
123 | enum spm_reg reg, u32 val) | ||
124 | { | ||
125 | if (drv->reg_data->reg_offset[reg]) | ||
126 | writel_relaxed(val, drv->reg_base + | ||
127 | drv->reg_data->reg_offset[reg]); | ||
128 | } | ||
129 | |||
130 | /* Ensure a guaranteed write, before return */ | ||
131 | static inline void spm_register_write_sync(struct spm_driver_data *drv, | ||
132 | enum spm_reg reg, u32 val) | ||
133 | { | ||
134 | u32 ret; | ||
135 | |||
136 | if (!drv->reg_data->reg_offset[reg]) | ||
137 | return; | ||
138 | |||
139 | do { | ||
140 | writel_relaxed(val, drv->reg_base + | ||
141 | drv->reg_data->reg_offset[reg]); | ||
142 | ret = readl_relaxed(drv->reg_base + | ||
143 | drv->reg_data->reg_offset[reg]); | ||
144 | if (ret == val) | ||
145 | break; | ||
146 | cpu_relax(); | ||
147 | } while (1); | ||
148 | } | ||
149 | |||
150 | static inline u32 spm_register_read(struct spm_driver_data *drv, | ||
151 | enum spm_reg reg) | ||
152 | { | ||
153 | return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]); | ||
154 | } | ||
155 | |||
156 | static void spm_set_low_power_mode(struct spm_driver_data *drv, | ||
157 | enum pm_sleep_mode mode) | ||
158 | { | ||
159 | u32 start_index; | ||
160 | u32 ctl_val; | ||
161 | |||
162 | start_index = drv->reg_data->start_index[mode]; | ||
163 | |||
164 | ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL); | ||
165 | ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT); | ||
166 | ctl_val |= start_index << SPM_CTL_INDEX_SHIFT; | ||
167 | ctl_val |= SPM_CTL_EN; | ||
168 | spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val); | ||
169 | } | ||
170 | |||
171 | static int qcom_pm_collapse(unsigned long int unused) | ||
172 | { | ||
173 | qcom_scm_cpu_power_down(QCOM_SCM_CPU_PWR_DOWN_L2_ON); | ||
174 | |||
175 | /* | ||
176 | * Returns here only if there was a pending interrupt and we did not | ||
177 | * power down as a result. | ||
178 | */ | ||
179 | return -1; | ||
180 | } | ||
181 | |||
182 | static int qcom_cpu_spc(int cpu) | ||
183 | { | ||
184 | int ret; | ||
185 | struct spm_driver_data *drv = per_cpu(cpu_spm_drv, cpu); | ||
186 | |||
187 | spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC); | ||
188 | ret = cpu_suspend(0, qcom_pm_collapse); | ||
189 | /* | ||
190 | * ARM common code executes WFI without calling into our driver and | ||
191 | * if the SPM mode is not reset, then we may accidently power down the | ||
192 | * cpu when we intended only to gate the cpu clock. | ||
193 | * Ensure the state is set to standby before returning. | ||
194 | */ | ||
195 | spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); | ||
196 | |||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | static int qcom_idle_enter(int cpu, unsigned long index) | ||
201 | { | ||
202 | return per_cpu(qcom_idle_ops, cpu)[index](cpu); | ||
203 | } | ||
204 | |||
205 | static const struct of_device_id qcom_idle_state_match[] __initconst = { | ||
206 | { .compatible = "qcom,idle-state-spc", .data = qcom_cpu_spc }, | ||
207 | { }, | ||
208 | }; | ||
209 | |||
210 | static int __init qcom_cpuidle_init(struct device_node *cpu_node, int cpu) | ||
211 | { | ||
212 | const struct of_device_id *match_id; | ||
213 | struct device_node *state_node; | ||
214 | int i; | ||
215 | int state_count = 1; | ||
216 | idle_fn idle_fns[CPUIDLE_STATE_MAX]; | ||
217 | idle_fn *fns; | ||
218 | cpumask_t mask; | ||
219 | bool use_scm_power_down = false; | ||
220 | |||
221 | for (i = 0; ; i++) { | ||
222 | state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | ||
223 | if (!state_node) | ||
224 | break; | ||
225 | |||
226 | if (!of_device_is_available(state_node)) | ||
227 | continue; | ||
228 | |||
229 | if (i == CPUIDLE_STATE_MAX) { | ||
230 | pr_warn("%s: cpuidle states reached max possible\n", | ||
231 | __func__); | ||
232 | break; | ||
233 | } | ||
234 | |||
235 | match_id = of_match_node(qcom_idle_state_match, state_node); | ||
236 | if (!match_id) | ||
237 | return -ENODEV; | ||
238 | |||
239 | idle_fns[state_count] = match_id->data; | ||
240 | |||
241 | /* Check if any of the states allow power down */ | ||
242 | if (match_id->data == qcom_cpu_spc) | ||
243 | use_scm_power_down = true; | ||
244 | |||
245 | state_count++; | ||
246 | } | ||
247 | |||
248 | if (state_count == 1) | ||
249 | goto check_spm; | ||
250 | |||
251 | fns = devm_kcalloc(get_cpu_device(cpu), state_count, sizeof(*fns), | ||
252 | GFP_KERNEL); | ||
253 | if (!fns) | ||
254 | return -ENOMEM; | ||
255 | |||
256 | for (i = 1; i < state_count; i++) | ||
257 | fns[i] = idle_fns[i]; | ||
258 | |||
259 | if (use_scm_power_down) { | ||
260 | /* We have atleast one power down mode */ | ||
261 | cpumask_clear(&mask); | ||
262 | cpumask_set_cpu(cpu, &mask); | ||
263 | qcom_scm_set_warm_boot_addr(cpu_resume, &mask); | ||
264 | } | ||
265 | |||
266 | per_cpu(qcom_idle_ops, cpu) = fns; | ||
267 | |||
268 | /* | ||
269 | * SPM probe for the cpu should have happened by now, if the | ||
270 | * SPM device does not exist, return -ENXIO to indicate that the | ||
271 | * cpu does not support idle states. | ||
272 | */ | ||
273 | check_spm: | ||
274 | return per_cpu(cpu_spm_drv, cpu) ? 0 : -ENXIO; | ||
275 | } | ||
276 | |||
277 | static struct cpuidle_ops qcom_cpuidle_ops __initdata = { | ||
278 | .suspend = qcom_idle_enter, | ||
279 | .init = qcom_cpuidle_init, | ||
280 | }; | ||
281 | |||
282 | CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v1, "qcom,kpss-acc-v1", &qcom_cpuidle_ops); | ||
283 | CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v2, "qcom,kpss-acc-v2", &qcom_cpuidle_ops); | ||
284 | |||
285 | static struct spm_driver_data *spm_get_drv(struct platform_device *pdev, | ||
286 | int *spm_cpu) | ||
287 | { | ||
288 | struct spm_driver_data *drv = NULL; | ||
289 | struct device_node *cpu_node, *saw_node; | ||
290 | int cpu; | ||
291 | bool found; | ||
292 | |||
293 | for_each_possible_cpu(cpu) { | ||
294 | cpu_node = of_cpu_device_node_get(cpu); | ||
295 | if (!cpu_node) | ||
296 | continue; | ||
297 | saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); | ||
298 | found = (saw_node == pdev->dev.of_node); | ||
299 | of_node_put(saw_node); | ||
300 | of_node_put(cpu_node); | ||
301 | if (found) | ||
302 | break; | ||
303 | } | ||
304 | |||
305 | if (found) { | ||
306 | drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); | ||
307 | if (drv) | ||
308 | *spm_cpu = cpu; | ||
309 | } | ||
310 | |||
311 | return drv; | ||
312 | } | ||
313 | |||
314 | static const struct of_device_id spm_match_table[] = { | ||
315 | { .compatible = "qcom,msm8974-saw2-v2.1-cpu", | ||
316 | .data = &spm_reg_8974_8084_cpu }, | ||
317 | { .compatible = "qcom,apq8084-saw2-v2.1-cpu", | ||
318 | .data = &spm_reg_8974_8084_cpu }, | ||
319 | { .compatible = "qcom,apq8064-saw2-v1.1-cpu", | ||
320 | .data = &spm_reg_8064_cpu }, | ||
321 | { }, | ||
322 | }; | ||
323 | |||
324 | static int spm_dev_probe(struct platform_device *pdev) | ||
325 | { | ||
326 | struct spm_driver_data *drv; | ||
327 | struct resource *res; | ||
328 | const struct of_device_id *match_id; | ||
329 | void __iomem *addr; | ||
330 | int cpu; | ||
331 | |||
332 | drv = spm_get_drv(pdev, &cpu); | ||
333 | if (!drv) | ||
334 | return -EINVAL; | ||
335 | |||
336 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
337 | drv->reg_base = devm_ioremap_resource(&pdev->dev, res); | ||
338 | if (IS_ERR(drv->reg_base)) | ||
339 | return PTR_ERR(drv->reg_base); | ||
340 | |||
341 | match_id = of_match_node(spm_match_table, pdev->dev.of_node); | ||
342 | if (!match_id) | ||
343 | return -ENODEV; | ||
344 | |||
345 | drv->reg_data = match_id->data; | ||
346 | |||
347 | /* Write the SPM sequences first.. */ | ||
348 | addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY]; | ||
349 | __iowrite32_copy(addr, drv->reg_data->seq, | ||
350 | ARRAY_SIZE(drv->reg_data->seq) / 4); | ||
351 | |||
352 | /* | ||
353 | * ..and then the control registers. | ||
354 | * On some SoC if the control registers are written first and if the | ||
355 | * CPU was held in reset, the reset signal could trigger the SPM state | ||
356 | * machine, before the sequences are completely written. | ||
357 | */ | ||
358 | spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg); | ||
359 | spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly); | ||
360 | spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly); | ||
361 | spm_register_write(drv, SPM_REG_PMIC_DATA_0, | ||
362 | drv->reg_data->pmic_data[0]); | ||
363 | spm_register_write(drv, SPM_REG_PMIC_DATA_1, | ||
364 | drv->reg_data->pmic_data[1]); | ||
365 | |||
366 | /* Set up Standby as the default low power mode */ | ||
367 | spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); | ||
368 | |||
369 | per_cpu(cpu_spm_drv, cpu) = drv; | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static struct platform_driver spm_driver = { | ||
375 | .probe = spm_dev_probe, | ||
376 | .driver = { | ||
377 | .name = "saw", | ||
378 | .of_match_table = spm_match_table, | ||
379 | }, | ||
380 | }; | ||
381 | module_platform_driver(spm_driver); | ||
382 | |||
383 | MODULE_LICENSE("GPL v2"); | ||
384 | MODULE_DESCRIPTION("SAW power controller driver"); | ||
385 | MODULE_ALIAS("platform:saw"); | ||
diff --git a/drivers/soc/sunxi/Kconfig b/drivers/soc/sunxi/Kconfig new file mode 100644 index 000000000000..353b07e40176 --- /dev/null +++ b/drivers/soc/sunxi/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Allwinner sunXi SoC drivers | ||
3 | # | ||
4 | config SUNXI_SRAM | ||
5 | bool | ||
6 | default ARCH_SUNXI | ||
7 | help | ||
8 | Say y here to enable the SRAM controller support. This | ||
9 | device is responsible on mapping the SRAM in the sunXi SoCs | ||
10 | whether to the CPU/DMA, or to the devices. | ||
diff --git a/drivers/soc/sunxi/Makefile b/drivers/soc/sunxi/Makefile new file mode 100644 index 000000000000..4cf9dbdf346e --- /dev/null +++ b/drivers/soc/sunxi/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-$(CONFIG_SUNXI_SRAM) += sunxi_sram.o | |||
diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c new file mode 100644 index 000000000000..bc52670c8f4b --- /dev/null +++ b/drivers/soc/sunxi/sunxi_sram.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* | ||
2 | * Allwinner SoCs SRAM Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 2015 Maxime Ripard | ||
5 | * | ||
6 | * Author: Maxime Ripard <maxime.ripard@free-electrons.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public | ||
9 | * License version 2. This program is licensed "as is" without any | ||
10 | * warranty of any kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/debugfs.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/of.h> | ||
17 | #include <linux/of_address.h> | ||
18 | #include <linux/of_device.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | #include <linux/soc/sunxi/sunxi_sram.h> | ||
22 | |||
23 | struct sunxi_sram_func { | ||
24 | char *func; | ||
25 | u8 val; | ||
26 | }; | ||
27 | |||
28 | struct sunxi_sram_data { | ||
29 | char *name; | ||
30 | u8 reg; | ||
31 | u8 offset; | ||
32 | u8 width; | ||
33 | struct sunxi_sram_func *func; | ||
34 | struct list_head list; | ||
35 | }; | ||
36 | |||
37 | struct sunxi_sram_desc { | ||
38 | struct sunxi_sram_data data; | ||
39 | bool claimed; | ||
40 | }; | ||
41 | |||
42 | #define SUNXI_SRAM_MAP(_val, _func) \ | ||
43 | { \ | ||
44 | .func = _func, \ | ||
45 | .val = _val, \ | ||
46 | } | ||
47 | |||
48 | #define SUNXI_SRAM_DATA(_name, _reg, _off, _width, ...) \ | ||
49 | { \ | ||
50 | .name = _name, \ | ||
51 | .reg = _reg, \ | ||
52 | .offset = _off, \ | ||
53 | .width = _width, \ | ||
54 | .func = (struct sunxi_sram_func[]){ \ | ||
55 | __VA_ARGS__, { } }, \ | ||
56 | } | ||
57 | |||
58 | static struct sunxi_sram_desc sun4i_a10_sram_a3_a4 = { | ||
59 | .data = SUNXI_SRAM_DATA("A3-A4", 0x4, 0x4, 2, | ||
60 | SUNXI_SRAM_MAP(0, "cpu"), | ||
61 | SUNXI_SRAM_MAP(1, "emac")), | ||
62 | }; | ||
63 | |||
64 | static struct sunxi_sram_desc sun4i_a10_sram_d = { | ||
65 | .data = SUNXI_SRAM_DATA("D", 0x4, 0x0, 1, | ||
66 | SUNXI_SRAM_MAP(0, "cpu"), | ||
67 | SUNXI_SRAM_MAP(1, "usb-otg")), | ||
68 | }; | ||
69 | |||
70 | static const struct of_device_id sunxi_sram_dt_ids[] = { | ||
71 | { | ||
72 | .compatible = "allwinner,sun4i-a10-sram-a3-a4", | ||
73 | .data = &sun4i_a10_sram_a3_a4.data, | ||
74 | }, | ||
75 | { | ||
76 | .compatible = "allwinner,sun4i-a10-sram-d", | ||
77 | .data = &sun4i_a10_sram_d.data, | ||
78 | }, | ||
79 | {} | ||
80 | }; | ||
81 | |||
82 | static struct device *sram_dev; | ||
83 | static LIST_HEAD(claimed_sram); | ||
84 | static DEFINE_SPINLOCK(sram_lock); | ||
85 | static void __iomem *base; | ||
86 | |||
87 | static int sunxi_sram_show(struct seq_file *s, void *data) | ||
88 | { | ||
89 | struct device_node *sram_node, *section_node; | ||
90 | const struct sunxi_sram_data *sram_data; | ||
91 | const struct of_device_id *match; | ||
92 | struct sunxi_sram_func *func; | ||
93 | const __be32 *sram_addr_p, *section_addr_p; | ||
94 | u32 val; | ||
95 | |||
96 | seq_puts(s, "Allwinner sunXi SRAM\n"); | ||
97 | seq_puts(s, "--------------------\n\n"); | ||
98 | |||
99 | for_each_child_of_node(sram_dev->of_node, sram_node) { | ||
100 | sram_addr_p = of_get_address(sram_node, 0, NULL, NULL); | ||
101 | |||
102 | seq_printf(s, "sram@%08x\n", | ||
103 | be32_to_cpu(*sram_addr_p)); | ||
104 | |||
105 | for_each_child_of_node(sram_node, section_node) { | ||
106 | match = of_match_node(sunxi_sram_dt_ids, section_node); | ||
107 | if (!match) | ||
108 | continue; | ||
109 | sram_data = match->data; | ||
110 | |||
111 | section_addr_p = of_get_address(section_node, 0, | ||
112 | NULL, NULL); | ||
113 | |||
114 | seq_printf(s, "\tsection@%04x\t(%s)\n", | ||
115 | be32_to_cpu(*section_addr_p), | ||
116 | sram_data->name); | ||
117 | |||
118 | val = readl(base + sram_data->reg); | ||
119 | val >>= sram_data->offset; | ||
120 | val &= sram_data->width; | ||
121 | |||
122 | for (func = sram_data->func; func->func; func++) { | ||
123 | seq_printf(s, "\t\t%s%c\n", func->func, | ||
124 | func->val == val ? '*' : ' '); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | seq_puts(s, "\n"); | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int sunxi_sram_open(struct inode *inode, struct file *file) | ||
135 | { | ||
136 | return single_open(file, sunxi_sram_show, inode->i_private); | ||
137 | } | ||
138 | |||
139 | static const struct file_operations sunxi_sram_fops = { | ||
140 | .open = sunxi_sram_open, | ||
141 | .read = seq_read, | ||
142 | .llseek = seq_lseek, | ||
143 | .release = single_release, | ||
144 | }; | ||
145 | |||
146 | static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data) | ||
147 | { | ||
148 | return container_of(data, struct sunxi_sram_desc, data); | ||
149 | } | ||
150 | |||
151 | static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *node, | ||
152 | unsigned int *value) | ||
153 | { | ||
154 | const struct of_device_id *match; | ||
155 | struct of_phandle_args args; | ||
156 | int ret; | ||
157 | |||
158 | ret = of_parse_phandle_with_fixed_args(node, "allwinner,sram", 1, 0, | ||
159 | &args); | ||
160 | if (ret) | ||
161 | return ERR_PTR(ret); | ||
162 | |||
163 | if (!of_device_is_available(args.np)) { | ||
164 | ret = -EBUSY; | ||
165 | goto err; | ||
166 | } | ||
167 | |||
168 | if (value) | ||
169 | *value = args.args[0]; | ||
170 | |||
171 | match = of_match_node(sunxi_sram_dt_ids, args.np); | ||
172 | if (!match) { | ||
173 | ret = -EINVAL; | ||
174 | goto err; | ||
175 | } | ||
176 | |||
177 | of_node_put(args.np); | ||
178 | return match->data; | ||
179 | |||
180 | err: | ||
181 | of_node_put(args.np); | ||
182 | return ERR_PTR(ret); | ||
183 | } | ||
184 | |||
185 | int sunxi_sram_claim(struct device *dev) | ||
186 | { | ||
187 | const struct sunxi_sram_data *sram_data; | ||
188 | struct sunxi_sram_desc *sram_desc; | ||
189 | unsigned int device; | ||
190 | u32 val, mask; | ||
191 | |||
192 | if (IS_ERR(base)) | ||
193 | return -EPROBE_DEFER; | ||
194 | |||
195 | if (!dev || !dev->of_node) | ||
196 | return -EINVAL; | ||
197 | |||
198 | sram_data = sunxi_sram_of_parse(dev->of_node, &device); | ||
199 | if (IS_ERR(sram_data)) | ||
200 | return PTR_ERR(sram_data); | ||
201 | |||
202 | sram_desc = to_sram_desc(sram_data); | ||
203 | |||
204 | spin_lock(&sram_lock); | ||
205 | |||
206 | if (sram_desc->claimed) { | ||
207 | spin_unlock(&sram_lock); | ||
208 | return -EBUSY; | ||
209 | } | ||
210 | |||
211 | mask = GENMASK(sram_data->offset + sram_data->width, sram_data->offset); | ||
212 | val = readl(base + sram_data->reg); | ||
213 | val &= ~mask; | ||
214 | writel(val | ((device << sram_data->offset) & mask), | ||
215 | base + sram_data->reg); | ||
216 | |||
217 | spin_unlock(&sram_lock); | ||
218 | |||
219 | return 0; | ||
220 | } | ||
221 | EXPORT_SYMBOL(sunxi_sram_claim); | ||
222 | |||
223 | int sunxi_sram_release(struct device *dev) | ||
224 | { | ||
225 | const struct sunxi_sram_data *sram_data; | ||
226 | struct sunxi_sram_desc *sram_desc; | ||
227 | |||
228 | if (!dev || !dev->of_node) | ||
229 | return -EINVAL; | ||
230 | |||
231 | sram_data = sunxi_sram_of_parse(dev->of_node, NULL); | ||
232 | if (IS_ERR(sram_data)) | ||
233 | return -EINVAL; | ||
234 | |||
235 | sram_desc = to_sram_desc(sram_data); | ||
236 | |||
237 | spin_lock(&sram_lock); | ||
238 | sram_desc->claimed = false; | ||
239 | spin_unlock(&sram_lock); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | EXPORT_SYMBOL(sunxi_sram_release); | ||
244 | |||
245 | static int sunxi_sram_probe(struct platform_device *pdev) | ||
246 | { | ||
247 | struct resource *res; | ||
248 | struct dentry *d; | ||
249 | |||
250 | sram_dev = &pdev->dev; | ||
251 | |||
252 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
253 | base = devm_ioremap_resource(&pdev->dev, res); | ||
254 | if (IS_ERR(base)) | ||
255 | return PTR_ERR(base); | ||
256 | |||
257 | of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); | ||
258 | |||
259 | d = debugfs_create_file("sram", S_IRUGO, NULL, NULL, | ||
260 | &sunxi_sram_fops); | ||
261 | if (!d) | ||
262 | return -ENOMEM; | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static const struct of_device_id sunxi_sram_dt_match[] = { | ||
268 | { .compatible = "allwinner,sun4i-a10-sram-controller" }, | ||
269 | { }, | ||
270 | }; | ||
271 | MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); | ||
272 | |||
273 | static struct platform_driver sunxi_sram_driver = { | ||
274 | .driver = { | ||
275 | .name = "sunxi-sram", | ||
276 | .of_match_table = sunxi_sram_dt_match, | ||
277 | }, | ||
278 | .probe = sunxi_sram_probe, | ||
279 | }; | ||
280 | module_platform_driver(sunxi_sram_driver); | ||
281 | |||
282 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); | ||
283 | MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver"); | ||
284 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index 3bf5aba4caaa..73fad05d8f2c 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c | |||
@@ -28,8 +28,15 @@ | |||
28 | #define APBMISC_SIZE 0x64 | 28 | #define APBMISC_SIZE 0x64 |
29 | #define FUSE_SKU_INFO 0x10 | 29 | #define FUSE_SKU_INFO 0x10 |
30 | 30 | ||
31 | #define PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT 4 | ||
32 | #define PMC_STRAPPING_OPT_A_RAM_CODE_MASK_LONG \ | ||
33 | (0xf << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT) | ||
34 | #define PMC_STRAPPING_OPT_A_RAM_CODE_MASK_SHORT \ | ||
35 | (0x3 << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT) | ||
36 | |||
31 | static void __iomem *apbmisc_base; | 37 | static void __iomem *apbmisc_base; |
32 | static void __iomem *strapping_base; | 38 | static void __iomem *strapping_base; |
39 | static bool long_ram_code; | ||
33 | 40 | ||
34 | u32 tegra_read_chipid(void) | 41 | u32 tegra_read_chipid(void) |
35 | { | 42 | { |
@@ -54,6 +61,18 @@ u32 tegra_read_straps(void) | |||
54 | return 0; | 61 | return 0; |
55 | } | 62 | } |
56 | 63 | ||
64 | u32 tegra_read_ram_code(void) | ||
65 | { | ||
66 | u32 straps = tegra_read_straps(); | ||
67 | |||
68 | if (long_ram_code) | ||
69 | straps &= PMC_STRAPPING_OPT_A_RAM_CODE_MASK_LONG; | ||
70 | else | ||
71 | straps &= PMC_STRAPPING_OPT_A_RAM_CODE_MASK_SHORT; | ||
72 | |||
73 | return straps >> PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT; | ||
74 | } | ||
75 | |||
57 | static const struct of_device_id apbmisc_match[] __initconst = { | 76 | static const struct of_device_id apbmisc_match[] __initconst = { |
58 | { .compatible = "nvidia,tegra20-apbmisc", }, | 77 | { .compatible = "nvidia,tegra20-apbmisc", }, |
59 | {}, | 78 | {}, |
@@ -112,4 +131,6 @@ void __init tegra_init_apbmisc(void) | |||
112 | strapping_base = of_iomap(np, 1); | 131 | strapping_base = of_iomap(np, 1); |
113 | if (!strapping_base) | 132 | if (!strapping_base) |
114 | pr_err("ioremap tegra strapping_base failed\n"); | 133 | pr_err("ioremap tegra strapping_base failed\n"); |
134 | |||
135 | long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code"); | ||
115 | } | 136 | } |