aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-06-26 14:54:29 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-26 14:54:29 -0400
commitf5dcb68086ba2e033b2af32b0da0c7a7c7872a09 (patch)
tree89c41089b492f8d8d411185bd7cb07538802e837 /drivers/soc
parent3d9f96d850e4bbfae24dc9aee03033dd77c81596 (diff)
parent4af34b572a85c44c55491a10693535a79627c478 (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/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-rw-r--r--drivers/soc/qcom/Kconfig7
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/spm.c385
-rw-r--r--drivers/soc/sunxi/Kconfig10
-rw-r--r--drivers/soc/sunxi/Makefile1
-rw-r--r--drivers/soc/sunxi/sunxi_sram.c284
-rw-r--r--drivers/soc/tegra/fuse/tegra-apbmisc.c21
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
3source "drivers/soc/mediatek/Kconfig" 3source "drivers/soc/mediatek/Kconfig"
4source "drivers/soc/qcom/Kconfig" 4source "drivers/soc/qcom/Kconfig"
5source "drivers/soc/sunxi/Kconfig"
5source "drivers/soc/ti/Kconfig" 6source "drivers/soc/ti/Kconfig"
6source "drivers/soc/versatile/Kconfig" 7source "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
5obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ 5obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
6obj-$(CONFIG_ARCH_QCOM) += qcom/ 6obj-$(CONFIG_ARCH_QCOM) += qcom/
7obj-$(CONFIG_ARCH_SUNXI) += sunxi/
7obj-$(CONFIG_ARCH_TEGRA) += tegra/ 8obj-$(CONFIG_ARCH_TEGRA) += tegra/
8obj-$(CONFIG_SOC_TI) += ti/ 9obj-$(CONFIG_SOC_TI) += ti/
9obj-$(CONFIG_PLAT_VERSATILE) += versatile/ 10obj-$(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
13config 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 @@
1obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o 1obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
2obj-$(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
39enum 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
47enum 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
61struct 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
71struct spm_driver_data {
72 void __iomem *reg_base;
73 const struct spm_reg_data *reg_data;
74};
75
76static 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 */
84static 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
95static 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 */
105static 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
117static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv);
118
119typedef int (*idle_fn)(int);
120static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops);
121
122static 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 */
131static 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
150static 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
156static 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
171static 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
182static 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
200static int qcom_idle_enter(int cpu, unsigned long index)
201{
202 return per_cpu(qcom_idle_ops, cpu)[index](cpu);
203}
204
205static const struct of_device_id qcom_idle_state_match[] __initconst = {
206 { .compatible = "qcom,idle-state-spc", .data = qcom_cpu_spc },
207 { },
208};
209
210static 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 */
273check_spm:
274 return per_cpu(cpu_spm_drv, cpu) ? 0 : -ENXIO;
275}
276
277static struct cpuidle_ops qcom_cpuidle_ops __initdata = {
278 .suspend = qcom_idle_enter,
279 .init = qcom_cpuidle_init,
280};
281
282CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v1, "qcom,kpss-acc-v1", &qcom_cpuidle_ops);
283CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v2, "qcom,kpss-acc-v2", &qcom_cpuidle_ops);
284
285static 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
314static 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
324static 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
374static struct platform_driver spm_driver = {
375 .probe = spm_dev_probe,
376 .driver = {
377 .name = "saw",
378 .of_match_table = spm_match_table,
379 },
380};
381module_platform_driver(spm_driver);
382
383MODULE_LICENSE("GPL v2");
384MODULE_DESCRIPTION("SAW power controller driver");
385MODULE_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#
4config 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
23struct sunxi_sram_func {
24 char *func;
25 u8 val;
26};
27
28struct 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
37struct 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
58static 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
64static 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
70static 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
82static struct device *sram_dev;
83static LIST_HEAD(claimed_sram);
84static DEFINE_SPINLOCK(sram_lock);
85static void __iomem *base;
86
87static 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
134static int sunxi_sram_open(struct inode *inode, struct file *file)
135{
136 return single_open(file, sunxi_sram_show, inode->i_private);
137}
138
139static 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
146static 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
151static 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
180err:
181 of_node_put(args.np);
182 return ERR_PTR(ret);
183}
184
185int 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}
221EXPORT_SYMBOL(sunxi_sram_claim);
222
223int 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}
243EXPORT_SYMBOL(sunxi_sram_release);
244
245static 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
267static const struct of_device_id sunxi_sram_dt_match[] = {
268 { .compatible = "allwinner,sun4i-a10-sram-controller" },
269 { },
270};
271MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match);
272
273static 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};
280module_platform_driver(sunxi_sram_driver);
281
282MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
283MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver");
284MODULE_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
31static void __iomem *apbmisc_base; 37static void __iomem *apbmisc_base;
32static void __iomem *strapping_base; 38static void __iomem *strapping_base;
39static bool long_ram_code;
33 40
34u32 tegra_read_chipid(void) 41u32 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
64u32 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
57static const struct of_device_id apbmisc_match[] __initconst = { 76static 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}