aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2015-06-01 05:04:26 -0400
committerArnd Bergmann <arnd@arndb.de>2015-06-01 11:57:34 -0400
commit4af34b572a85c44c55491a10693535a79627c478 (patch)
treec27f6bf9fe4d2c39e989704d888386c73932eebd
parent72275b4c08e7536ed5fe21c8899d99fc9f1fce7b (diff)
drivers: soc: sunxi: Introduce SoC driver to map SRAMs
The Allwinner SoCs have a handful of SRAM that can be either mapped to be accessible by devices or the CPU. That mapping is controlled by an SRAM controller, and that mapping might not be set by the bootloader, for example if the device wasn't used at all, or if we're using solutions like the U-Boot's Falcon Boot. We could also imagine changing this at runtime for example to change the mapping of these SRAMs to use them for suspend/resume or runtime memory rate change, if that ever happens. These use cases require some API in the kernel to control that mapping, exported through a drivers/soc driver. This driver also implement a debugfs file that shows the SRAM found in the system, the current mapping and the SRAM that have been claimed by some drivers in the kernel. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Hans de Goede <hdegoede@redhat.com> Tested-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--Documentation/devicetree/bindings/soc/sunxi/sram.txt72
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-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--include/linux/soc/sunxi/sunxi_sram.h19
7 files changed, 388 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/soc/sunxi/sram.txt b/Documentation/devicetree/bindings/soc/sunxi/sram.txt
new file mode 100644
index 000000000000..067698112f5f
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/sunxi/sram.txt
@@ -0,0 +1,72 @@
1Allwinnner SoC SRAM controllers
2-----------------------------------------------------
3
4The SRAM controller found on most Allwinner devices is represented by
5a regular node for the SRAM controller itself, with sub-nodes
6reprensenting the SRAM handled by the SRAM controller.
7
8Controller Node
9---------------
10
11Required properties:
12- compatible : "allwinner,sun4i-a10-sram-controller"
13- reg : sram controller register offset + length
14
15SRAM nodes
16----------
17
18Each SRAM is described using the mmio-sram bindings documented in
19Documentation/devicetree/bindings/misc/sram.txt
20
21Each SRAM will have SRAM sections that are going to be handled by the
22SRAM controller as subnodes. These sections are represented following
23once again the representation described in the mmio-sram binding.
24
25The valid sections compatible are:
26 - allwinner,sun4i-a10-sram-a3-a4
27 - allwinner,sun4i-a10-sram-d
28
29Devices using SRAM sections
30---------------------------
31
32Some devices need to request to the SRAM controller to map an SRAM for
33their exclusive use.
34
35The relationship between such a device and an SRAM section is
36expressed through the allwinner,sram property, that will take a
37phandle and an argument.
38
39This valid values for this argument are:
40 - 0: CPU
41 - 1: Device
42
43Example
44-------
45sram-controller@01c00000 {
46 compatible = "allwinner,sun4i-a10-sram-controller";
47 reg = <0x01c00000 0x30>;
48 #address-cells = <1>;
49 #size-cells = <1>;
50 ranges;
51
52 sram_a: sram@00000000 {
53 compatible = "mmio-sram";
54 reg = <0x00000000 0xc000>;
55 #address-cells = <1>;
56 #size-cells = <1>;
57 ranges = <0 0x00000000 0xc000>;
58
59 emac_sram: sram-section@8000 {
60 compatible = "allwinner,sun4i-a10-sram-a3-a4";
61 reg = <0x8000 0x4000>;
62 status = "disabled";
63 };
64 };
65};
66
67emac: ethernet@01c0b000 {
68 compatible = "allwinner,sun4i-a10-emac";
69 ...
70
71 allwinner,sram = <&emac_sram 1>;
72};
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/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/include/linux/soc/sunxi/sunxi_sram.h b/include/linux/soc/sunxi/sunxi_sram.h
new file mode 100644
index 000000000000..c5f663bba9c2
--- /dev/null
+++ b/include/linux/soc/sunxi/sunxi_sram.h
@@ -0,0 +1,19 @@
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#ifndef _SUNXI_SRAM_H_
14#define _SUNXI_SRAM_H_
15
16int sunxi_sram_claim(struct device *dev);
17int sunxi_sram_release(struct device *dev);
18
19#endif /* _SUNXI_SRAM_H_ */