Merge branch 'for-4.14-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[sfrench/cifs-2.6.git] / drivers / soc / sunxi / sunxi_sram.c
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         u32     reg_val;
27 };
28
29 struct sunxi_sram_data {
30         char                    *name;
31         u8                      reg;
32         u8                      offset;
33         u8                      width;
34         struct sunxi_sram_func  *func;
35         struct list_head        list;
36 };
37
38 struct sunxi_sram_desc {
39         struct sunxi_sram_data  data;
40         bool                    claimed;
41 };
42
43 #define SUNXI_SRAM_MAP(_reg_val, _val, _func)                   \
44         {                                                       \
45                 .func = _func,                                  \
46                 .val = _val,                                    \
47                 .reg_val = _reg_val,                            \
48         }
49
50 #define SUNXI_SRAM_DATA(_name, _reg, _off, _width, ...)         \
51         {                                                       \
52                 .name = _name,                                  \
53                 .reg = _reg,                                    \
54                 .offset = _off,                                 \
55                 .width = _width,                                \
56                 .func = (struct sunxi_sram_func[]){             \
57                         __VA_ARGS__, { } },                     \
58         }
59
60 static struct sunxi_sram_desc sun4i_a10_sram_a3_a4 = {
61         .data   = SUNXI_SRAM_DATA("A3-A4", 0x4, 0x4, 2,
62                                   SUNXI_SRAM_MAP(0, 0, "cpu"),
63                                   SUNXI_SRAM_MAP(1, 1, "emac")),
64 };
65
66 static struct sunxi_sram_desc sun4i_a10_sram_d = {
67         .data   = SUNXI_SRAM_DATA("D", 0x4, 0x0, 1,
68                                   SUNXI_SRAM_MAP(0, 0, "cpu"),
69                                   SUNXI_SRAM_MAP(1, 1, "usb-otg")),
70 };
71
72 static struct sunxi_sram_desc sun50i_a64_sram_c = {
73         .data   = SUNXI_SRAM_DATA("C", 0x4, 24, 1,
74                                   SUNXI_SRAM_MAP(0, 1, "cpu"),
75                                   SUNXI_SRAM_MAP(1, 0, "de2")),
76 };
77
78 static const struct of_device_id sunxi_sram_dt_ids[] = {
79         {
80                 .compatible     = "allwinner,sun4i-a10-sram-a3-a4",
81                 .data           = &sun4i_a10_sram_a3_a4.data,
82         },
83         {
84                 .compatible     = "allwinner,sun4i-a10-sram-d",
85                 .data           = &sun4i_a10_sram_d.data,
86         },
87         {
88                 .compatible     = "allwinner,sun50i-a64-sram-c",
89                 .data           = &sun50i_a64_sram_c.data,
90         },
91         {}
92 };
93
94 static struct device *sram_dev;
95 static LIST_HEAD(claimed_sram);
96 static DEFINE_SPINLOCK(sram_lock);
97 static void __iomem *base;
98
99 static int sunxi_sram_show(struct seq_file *s, void *data)
100 {
101         struct device_node *sram_node, *section_node;
102         const struct sunxi_sram_data *sram_data;
103         const struct of_device_id *match;
104         struct sunxi_sram_func *func;
105         const __be32 *sram_addr_p, *section_addr_p;
106         u32 val;
107
108         seq_puts(s, "Allwinner sunXi SRAM\n");
109         seq_puts(s, "--------------------\n\n");
110
111         for_each_child_of_node(sram_dev->of_node, sram_node) {
112                 sram_addr_p = of_get_address(sram_node, 0, NULL, NULL);
113
114                 seq_printf(s, "sram@%08x\n",
115                            be32_to_cpu(*sram_addr_p));
116
117                 for_each_child_of_node(sram_node, section_node) {
118                         match = of_match_node(sunxi_sram_dt_ids, section_node);
119                         if (!match)
120                                 continue;
121                         sram_data = match->data;
122
123                         section_addr_p = of_get_address(section_node, 0,
124                                                         NULL, NULL);
125
126                         seq_printf(s, "\tsection@%04x\t(%s)\n",
127                                    be32_to_cpu(*section_addr_p),
128                                    sram_data->name);
129
130                         val = readl(base + sram_data->reg);
131                         val >>= sram_data->offset;
132                         val &= GENMASK(sram_data->width - 1, 0);
133
134                         for (func = sram_data->func; func->func; func++) {
135                                 seq_printf(s, "\t\t%s%c\n", func->func,
136                                            func->reg_val == val ?
137                                            '*' : ' ');
138                         }
139                 }
140
141                 seq_puts(s, "\n");
142         }
143
144         return 0;
145 }
146
147 static int sunxi_sram_open(struct inode *inode, struct file *file)
148 {
149         return single_open(file, sunxi_sram_show, inode->i_private);
150 }
151
152 static const struct file_operations sunxi_sram_fops = {
153         .open = sunxi_sram_open,
154         .read = seq_read,
155         .llseek = seq_lseek,
156         .release = single_release,
157 };
158
159 static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data)
160 {
161         return container_of(data, struct sunxi_sram_desc, data);
162 }
163
164 static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *node,
165                                                          unsigned int *reg_value)
166 {
167         const struct of_device_id *match;
168         const struct sunxi_sram_data *data;
169         struct sunxi_sram_func *func;
170         struct of_phandle_args args;
171         u8 val;
172         int ret;
173
174         ret = of_parse_phandle_with_fixed_args(node, "allwinner,sram", 1, 0,
175                                                &args);
176         if (ret)
177                 return ERR_PTR(ret);
178
179         if (!of_device_is_available(args.np)) {
180                 ret = -EBUSY;
181                 goto err;
182         }
183
184         val = args.args[0];
185
186         match = of_match_node(sunxi_sram_dt_ids, args.np);
187         if (!match) {
188                 ret = -EINVAL;
189                 goto err;
190         }
191
192         data = match->data;
193         if (!data) {
194                 ret = -EINVAL;
195                 goto err;
196         };
197
198         for (func = data->func; func->func; func++) {
199                 if (val == func->val) {
200                         if (reg_value)
201                                 *reg_value = func->reg_val;
202
203                         break;
204                 }
205         }
206
207         if (!func->func) {
208                 ret = -EINVAL;
209                 goto err;
210         }
211
212         of_node_put(args.np);
213         return match->data;
214
215 err:
216         of_node_put(args.np);
217         return ERR_PTR(ret);
218 }
219
220 int sunxi_sram_claim(struct device *dev)
221 {
222         const struct sunxi_sram_data *sram_data;
223         struct sunxi_sram_desc *sram_desc;
224         unsigned int device;
225         u32 val, mask;
226
227         if (IS_ERR(base))
228                 return PTR_ERR(base);
229
230         if (!base)
231                 return -EPROBE_DEFER;
232
233         if (!dev || !dev->of_node)
234                 return -EINVAL;
235
236         sram_data = sunxi_sram_of_parse(dev->of_node, &device);
237         if (IS_ERR(sram_data))
238                 return PTR_ERR(sram_data);
239
240         sram_desc = to_sram_desc(sram_data);
241
242         spin_lock(&sram_lock);
243
244         if (sram_desc->claimed) {
245                 spin_unlock(&sram_lock);
246                 return -EBUSY;
247         }
248
249         mask = GENMASK(sram_data->offset + sram_data->width - 1,
250                        sram_data->offset);
251         val = readl(base + sram_data->reg);
252         val &= ~mask;
253         writel(val | ((device << sram_data->offset) & mask),
254                base + sram_data->reg);
255
256         spin_unlock(&sram_lock);
257
258         return 0;
259 }
260 EXPORT_SYMBOL(sunxi_sram_claim);
261
262 int sunxi_sram_release(struct device *dev)
263 {
264         const struct sunxi_sram_data *sram_data;
265         struct sunxi_sram_desc *sram_desc;
266
267         if (!dev || !dev->of_node)
268                 return -EINVAL;
269
270         sram_data = sunxi_sram_of_parse(dev->of_node, NULL);
271         if (IS_ERR(sram_data))
272                 return -EINVAL;
273
274         sram_desc = to_sram_desc(sram_data);
275
276         spin_lock(&sram_lock);
277         sram_desc->claimed = false;
278         spin_unlock(&sram_lock);
279
280         return 0;
281 }
282 EXPORT_SYMBOL(sunxi_sram_release);
283
284 static int sunxi_sram_probe(struct platform_device *pdev)
285 {
286         struct resource *res;
287         struct dentry *d;
288
289         sram_dev = &pdev->dev;
290
291         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
292         base = devm_ioremap_resource(&pdev->dev, res);
293         if (IS_ERR(base))
294                 return PTR_ERR(base);
295
296         of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
297
298         d = debugfs_create_file("sram", S_IRUGO, NULL, NULL,
299                                 &sunxi_sram_fops);
300         if (!d)
301                 return -ENOMEM;
302
303         return 0;
304 }
305
306 static const struct of_device_id sunxi_sram_dt_match[] = {
307         { .compatible = "allwinner,sun4i-a10-sram-controller" },
308         { .compatible = "allwinner,sun50i-a64-sram-controller" },
309         { },
310 };
311 MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match);
312
313 static struct platform_driver sunxi_sram_driver = {
314         .driver = {
315                 .name           = "sunxi-sram",
316                 .of_match_table = sunxi_sram_dt_match,
317         },
318         .probe  = sunxi_sram_probe,
319 };
320 module_platform_driver(sunxi_sram_driver);
321
322 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
323 MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver");
324 MODULE_LICENSE("GPL");