diff options
author | Pawel Moll <pawel.moll@arm.com> | 2014-04-23 05:49:31 -0400 |
---|---|---|
committer | Pawel Moll <pawel.moll@arm.com> | 2014-05-15 12:02:19 -0400 |
commit | 974cc7b93441a0e78f030495436d1be7eb7c208d (patch) | |
tree | bc59d8bdfbb3c8bc25282495a40247eef810a415 /drivers | |
parent | 29f9b6cf7bff6a118130163c848811e14f8022da (diff) |
mfd: vexpress: Define the device as MFD cells
This patch - finally, after over 6 months! :-( - addresses
Samuel's request to split the vexpress-sysreg driver into
smaller portions and define the device in a form of MFD
cells:
* LEDs code has been completely removed and replaced with
"gpio-leds" nodes in the tree (referencing dedicated
GPIO subnodes in sysreg - bindings documentation updated);
this also better fits the reality as some variants of the
motherboard don't have all the LEDs populated
* syscfg bridge code has been extracted into a separate
driver (placed in drivers/misc for no better place)
* all the ID & MISC registers are defined as sysconf
making them available for other drivers should they need
to use them (and also to the user via /sys/kernel/debug/regmap
which can be helpful in platform debugging)
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/Kconfig | 15 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 2 | ||||
-rw-r--r-- | drivers/mfd/vexpress-sysreg.c | 533 | ||||
-rw-r--r-- | drivers/misc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/vexpress-syscfg.c | 324 |
6 files changed, 479 insertions, 405 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 33834120d057..490fd48a9541 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -1227,12 +1227,17 @@ config MCP_UCB1200_TS | |||
1227 | 1227 | ||
1228 | endmenu | 1228 | endmenu |
1229 | 1229 | ||
1230 | config VEXPRESS_CONFIG | 1230 | config MFD_VEXPRESS_SYSREG |
1231 | bool "ARM Versatile Express platform infrastructure" | 1231 | bool "Versatile Express System Registers" |
1232 | depends on ARM || ARM64 | 1232 | depends on VEXPRESS_CONFIG |
1233 | default y | ||
1234 | select CLKSRC_MMIO | ||
1235 | select GPIO_GENERIC_PLATFORM | ||
1236 | select MFD_CORE | ||
1237 | select MFD_SYSCON | ||
1233 | help | 1238 | help |
1234 | Platform configuration infrastructure for the ARM Ltd. | 1239 | System Registers are the platform configuration block |
1235 | Versatile Express. | 1240 | on the ARM Ltd. Versatile Express board. |
1236 | 1241 | ||
1237 | endmenu | 1242 | endmenu |
1238 | endif | 1243 | endif |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9ba838eb5131..cec3487b539e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
@@ -161,7 +161,7 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o | |||
161 | obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o | 161 | obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o |
162 | obj-$(CONFIG_MFD_SYSCON) += syscon.o | 162 | obj-$(CONFIG_MFD_SYSCON) += syscon.o |
163 | obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o | 163 | obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o |
164 | obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-sysreg.o | 164 | obj-$(CONFIG_MFD_VEXPRESS_SYSREG) += vexpress-sysreg.o |
165 | obj-$(CONFIG_MFD_RETU) += retu-mfd.o | 165 | obj-$(CONFIG_MFD_RETU) += retu-mfd.o |
166 | obj-$(CONFIG_MFD_AS3711) += as3711.o | 166 | obj-$(CONFIG_MFD_AS3711) += as3711.o |
167 | obj-$(CONFIG_MFD_AS3722) += as3722.o | 167 | obj-$(CONFIG_MFD_AS3722) += as3722.o |
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index b4138a7168db..952df843b6be 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c | |||
@@ -11,25 +11,22 @@ | |||
11 | * Copyright (C) 2012 ARM Limited | 11 | * Copyright (C) 2012 ARM Limited |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/basic_mmio_gpio.h> | ||
14 | #include <linux/err.h> | 15 | #include <linux/err.h> |
15 | #include <linux/gpio.h> | ||
16 | #include <linux/io.h> | 16 | #include <linux/io.h> |
17 | #include <linux/leds.h> | 17 | #include <linux/mfd/core.h> |
18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
19 | #include <linux/of_platform.h> | 19 | #include <linux/of_platform.h> |
20 | #include <linux/platform_data/syscon.h> | ||
20 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
21 | #include <linux/regulator/driver.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
24 | #include <linux/stat.h> | 23 | #include <linux/stat.h> |
25 | #include <linux/timer.h> | ||
26 | #include <linux/vexpress.h> | 24 | #include <linux/vexpress.h> |
27 | 25 | ||
28 | #define SYS_ID 0x000 | 26 | #define SYS_ID 0x000 |
29 | #define SYS_SW 0x004 | 27 | #define SYS_SW 0x004 |
30 | #define SYS_LED 0x008 | 28 | #define SYS_LED 0x008 |
31 | #define SYS_100HZ 0x024 | 29 | #define SYS_100HZ 0x024 |
32 | #define SYS_FLAGS 0x030 | ||
33 | #define SYS_FLAGSSET 0x030 | 30 | #define SYS_FLAGSSET 0x030 |
34 | #define SYS_FLAGSCLR 0x034 | 31 | #define SYS_FLAGSCLR 0x034 |
35 | #define SYS_NVFLAGS 0x038 | 32 | #define SYS_NVFLAGS 0x038 |
@@ -51,36 +48,32 @@ | |||
51 | #define SYS_ID_HBI_SHIFT 16 | 48 | #define SYS_ID_HBI_SHIFT 16 |
52 | #define SYS_PROCIDx_HBI_SHIFT 0 | 49 | #define SYS_PROCIDx_HBI_SHIFT 0 |
53 | 50 | ||
54 | #define SYS_LED_LED(n) (1 << (n)) | ||
55 | |||
56 | #define SYS_MCI_CARDIN (1 << 0) | 51 | #define SYS_MCI_CARDIN (1 << 0) |
57 | #define SYS_MCI_WPROT (1 << 1) | 52 | #define SYS_MCI_WPROT (1 << 1) |
58 | 53 | ||
59 | #define SYS_FLASH_WPn (1 << 0) | ||
60 | |||
61 | #define SYS_MISC_MASTERSITE (1 << 14) | 54 | #define SYS_MISC_MASTERSITE (1 << 14) |
62 | 55 | ||
63 | #define SYS_CFGCTRL_START (1 << 31) | ||
64 | #define SYS_CFGCTRL_WRITE (1 << 30) | ||
65 | #define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) | ||
66 | #define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) | ||
67 | #define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) | ||
68 | #define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) | ||
69 | #define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) | ||
70 | 56 | ||
71 | #define SYS_CFGSTAT_ERR (1 << 1) | 57 | static void __iomem *__vexpress_sysreg_base; |
72 | #define SYS_CFGSTAT_COMPLETE (1 << 0) | ||
73 | 58 | ||
59 | static void __iomem *vexpress_sysreg_base(void) | ||
60 | { | ||
61 | if (!__vexpress_sysreg_base) { | ||
62 | struct device_node *node = of_find_compatible_node(NULL, NULL, | ||
63 | "arm,vexpress-sysreg"); | ||
74 | 64 | ||
75 | static void __iomem *vexpress_sysreg_base; | 65 | __vexpress_sysreg_base = of_iomap(node, 0); |
76 | static struct device *vexpress_sysreg_dev; | 66 | } |
77 | static LIST_HEAD(vexpress_sysreg_config_funcs); | 67 | |
78 | static struct device *vexpress_sysreg_config_bridge; | 68 | WARN_ON(!__vexpress_sysreg_base); |
69 | |||
70 | return __vexpress_sysreg_base; | ||
71 | } | ||
79 | 72 | ||
80 | 73 | ||
81 | static int vexpress_sysreg_get_master(void) | 74 | static int vexpress_sysreg_get_master(void) |
82 | { | 75 | { |
83 | if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) | 76 | if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE) |
84 | return VEXPRESS_SITE_DB2; | 77 | return VEXPRESS_SITE_DB2; |
85 | 78 | ||
86 | return VEXPRESS_SITE_DB1; | 79 | return VEXPRESS_SITE_DB1; |
@@ -88,8 +81,13 @@ static int vexpress_sysreg_get_master(void) | |||
88 | 81 | ||
89 | void vexpress_flags_set(u32 data) | 82 | void vexpress_flags_set(u32 data) |
90 | { | 83 | { |
91 | writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); | 84 | writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR); |
92 | writel(data, vexpress_sysreg_base + SYS_FLAGSSET); | 85 | writel(data, vexpress_sysreg_base() + SYS_FLAGSSET); |
86 | } | ||
87 | |||
88 | unsigned int vexpress_get_mci_cardin(struct device *dev) | ||
89 | { | ||
90 | return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN; | ||
93 | } | 91 | } |
94 | 92 | ||
95 | u32 vexpress_get_procid(int site) | 93 | u32 vexpress_get_procid(int site) |
@@ -97,7 +95,7 @@ u32 vexpress_get_procid(int site) | |||
97 | if (site == VEXPRESS_SITE_MASTER) | 95 | if (site == VEXPRESS_SITE_MASTER) |
98 | site = vexpress_sysreg_get_master(); | 96 | site = vexpress_sysreg_get_master(); |
99 | 97 | ||
100 | return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? | 98 | return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ? |
101 | SYS_PROCID0 : SYS_PROCID1)); | 99 | SYS_PROCID0 : SYS_PROCID1)); |
102 | } | 100 | } |
103 | 101 | ||
@@ -107,7 +105,7 @@ u32 vexpress_get_hbi(int site) | |||
107 | 105 | ||
108 | switch (site) { | 106 | switch (site) { |
109 | case VEXPRESS_SITE_MB: | 107 | case VEXPRESS_SITE_MB: |
110 | id = readl(vexpress_sysreg_base + SYS_ID); | 108 | id = readl(vexpress_sysreg_base() + SYS_ID); |
111 | return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; | 109 | return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; |
112 | case VEXPRESS_SITE_MASTER: | 110 | case VEXPRESS_SITE_MASTER: |
113 | case VEXPRESS_SITE_DB1: | 111 | case VEXPRESS_SITE_DB1: |
@@ -121,406 +119,143 @@ u32 vexpress_get_hbi(int site) | |||
121 | 119 | ||
122 | void __iomem *vexpress_get_24mhz_clock_base(void) | 120 | void __iomem *vexpress_get_24mhz_clock_base(void) |
123 | { | 121 | { |
124 | return vexpress_sysreg_base + SYS_24MHZ; | 122 | return vexpress_sysreg_base() + SYS_24MHZ; |
125 | } | ||
126 | |||
127 | |||
128 | struct vexpress_sysreg_config_func { | ||
129 | struct list_head list; | ||
130 | struct regmap *regmap; | ||
131 | int num_templates; | ||
132 | u32 template[0]; /* Keep this last */ | ||
133 | }; | ||
134 | |||
135 | static int vexpress_sysreg_config_exec(struct vexpress_sysreg_config_func *func, | ||
136 | int index, bool write, u32 *data) | ||
137 | { | ||
138 | u32 command, status; | ||
139 | int tries; | ||
140 | long timeout; | ||
141 | |||
142 | if (WARN_ON(!vexpress_sysreg_base)) | ||
143 | return -ENOENT; | ||
144 | |||
145 | if (WARN_ON(index > func->num_templates)) | ||
146 | return -EINVAL; | ||
147 | |||
148 | command = readl(vexpress_sysreg_base + SYS_CFGCTRL); | ||
149 | if (WARN_ON(command & SYS_CFGCTRL_START)) | ||
150 | return -EBUSY; | ||
151 | |||
152 | command = func->template[index]; | ||
153 | command |= SYS_CFGCTRL_START; | ||
154 | command |= write ? SYS_CFGCTRL_WRITE : 0; | ||
155 | |||
156 | /* Use a canary for reads */ | ||
157 | if (!write) | ||
158 | *data = 0xdeadbeef; | ||
159 | |||
160 | dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n", | ||
161 | command, *data); | ||
162 | writel(*data, vexpress_sysreg_base + SYS_CFGDATA); | ||
163 | writel(0, vexpress_sysreg_base + SYS_CFGSTAT); | ||
164 | writel(command, vexpress_sysreg_base + SYS_CFGCTRL); | ||
165 | mb(); | ||
166 | |||
167 | /* The operation can take ages... Go to sleep, 100us initially */ | ||
168 | tries = 100; | ||
169 | timeout = 100; | ||
170 | do { | ||
171 | set_current_state(TASK_INTERRUPTIBLE); | ||
172 | schedule_timeout(usecs_to_jiffies(timeout)); | ||
173 | if (signal_pending(current)) | ||
174 | return -EINTR; | ||
175 | |||
176 | status = readl(vexpress_sysreg_base + SYS_CFGSTAT); | ||
177 | if (status & SYS_CFGSTAT_ERR) | ||
178 | return -EFAULT; | ||
179 | |||
180 | if (timeout > 20) | ||
181 | timeout -= 20; | ||
182 | } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); | ||
183 | if (WARN_ON_ONCE(!tries)) | ||
184 | return -ETIMEDOUT; | ||
185 | |||
186 | if (!write) { | ||
187 | *data = readl(vexpress_sysreg_base + SYS_CFGDATA); | ||
188 | dev_dbg(vexpress_sysreg_dev, "func %p, read data %x\n", | ||
189 | func, *data); | ||
190 | } | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int vexpress_sysreg_config_read(void *context, unsigned int index, | ||
196 | unsigned int *val) | ||
197 | { | ||
198 | struct vexpress_sysreg_config_func *func = context; | ||
199 | |||
200 | return vexpress_sysreg_config_exec(func, index, false, val); | ||
201 | } | ||
202 | |||
203 | static int vexpress_sysreg_config_write(void *context, unsigned int index, | ||
204 | unsigned int val) | ||
205 | { | ||
206 | struct vexpress_sysreg_config_func *func = context; | ||
207 | |||
208 | return vexpress_sysreg_config_exec(func, index, true, &val); | ||
209 | } | ||
210 | |||
211 | struct regmap_config vexpress_sysreg_regmap_config = { | ||
212 | .lock = vexpress_config_lock, | ||
213 | .unlock = vexpress_config_unlock, | ||
214 | .reg_bits = 32, | ||
215 | .val_bits = 32, | ||
216 | .reg_read = vexpress_sysreg_config_read, | ||
217 | .reg_write = vexpress_sysreg_config_write, | ||
218 | .reg_format_endian = REGMAP_ENDIAN_LITTLE, | ||
219 | .val_format_endian = REGMAP_ENDIAN_LITTLE, | ||
220 | }; | ||
221 | |||
222 | static struct regmap *vexpress_sysreg_config_regmap_init(struct device *dev, | ||
223 | void *context) | ||
224 | { | ||
225 | struct platform_device *pdev = to_platform_device(dev); | ||
226 | struct vexpress_sysreg_config_func *func; | ||
227 | struct property *prop; | ||
228 | const __be32 *val = NULL; | ||
229 | __be32 energy_quirk[4]; | ||
230 | int num; | ||
231 | u32 site, position, dcc; | ||
232 | int err; | ||
233 | int i; | ||
234 | |||
235 | if (dev->of_node) { | ||
236 | err = vexpress_config_get_topo(dev->of_node, &site, &position, | ||
237 | &dcc); | ||
238 | if (err) | ||
239 | return ERR_PTR(err); | ||
240 | |||
241 | prop = of_find_property(dev->of_node, | ||
242 | "arm,vexpress-sysreg,func", NULL); | ||
243 | if (!prop) | ||
244 | return ERR_PTR(-EINVAL); | ||
245 | |||
246 | num = prop->length / sizeof(u32) / 2; | ||
247 | val = prop->value; | ||
248 | } else { | ||
249 | if (pdev->num_resources != 1 || | ||
250 | pdev->resource[0].flags != IORESOURCE_BUS) | ||
251 | return ERR_PTR(-EFAULT); | ||
252 | |||
253 | site = pdev->resource[0].start; | ||
254 | if (site == VEXPRESS_SITE_MASTER) | ||
255 | site = vexpress_sysreg_get_master(); | ||
256 | position = 0; | ||
257 | dcc = 0; | ||
258 | num = 1; | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * "arm,vexpress-energy" function used to be described | ||
263 | * by its first device only, now it requires both | ||
264 | */ | ||
265 | if (num == 1 && of_device_is_compatible(dev->of_node, | ||
266 | "arm,vexpress-energy")) { | ||
267 | num = 2; | ||
268 | energy_quirk[0] = *val; | ||
269 | energy_quirk[2] = *val++; | ||
270 | energy_quirk[1] = *val; | ||
271 | energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); | ||
272 | val = energy_quirk; | ||
273 | } | ||
274 | |||
275 | func = kzalloc(sizeof(*func) + sizeof(*func->template) * num, | ||
276 | GFP_KERNEL); | ||
277 | if (!func) | ||
278 | return NULL; | ||
279 | |||
280 | func->num_templates = num; | ||
281 | |||
282 | for (i = 0; i < num; i++) { | ||
283 | u32 function, device; | ||
284 | |||
285 | if (dev->of_node) { | ||
286 | function = be32_to_cpup(val++); | ||
287 | device = be32_to_cpup(val++); | ||
288 | } else { | ||
289 | function = pdev->resource[0].end; | ||
290 | device = pdev->id; | ||
291 | } | ||
292 | |||
293 | dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", | ||
294 | func, site, position, dcc, | ||
295 | function, device); | ||
296 | |||
297 | func->template[i] = SYS_CFGCTRL_DCC(dcc); | ||
298 | func->template[i] |= SYS_CFGCTRL_SITE(site); | ||
299 | func->template[i] |= SYS_CFGCTRL_POSITION(position); | ||
300 | func->template[i] |= SYS_CFGCTRL_FUNC(function); | ||
301 | func->template[i] |= SYS_CFGCTRL_DEVICE(device); | ||
302 | } | ||
303 | |||
304 | vexpress_sysreg_regmap_config.max_register = num - 1; | ||
305 | |||
306 | func->regmap = regmap_init(dev, NULL, func, | ||
307 | &vexpress_sysreg_regmap_config); | ||
308 | |||
309 | if (IS_ERR(func->regmap)) | ||
310 | kfree(func); | ||
311 | else | ||
312 | list_add(&func->list, &vexpress_sysreg_config_funcs); | ||
313 | |||
314 | return func->regmap; | ||
315 | } | ||
316 | |||
317 | static void vexpress_sysreg_config_regmap_exit(struct regmap *regmap, | ||
318 | void *context) | ||
319 | { | ||
320 | struct vexpress_sysreg_config_func *func, *tmp; | ||
321 | |||
322 | regmap_exit(regmap); | ||
323 | |||
324 | list_for_each_entry_safe(func, tmp, &vexpress_sysreg_config_funcs, | ||
325 | list) { | ||
326 | if (func->regmap == regmap) { | ||
327 | list_del(&vexpress_sysreg_config_funcs); | ||
328 | kfree(func); | ||
329 | break; | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | |||
334 | static struct vexpress_config_bridge_ops vexpress_sysreg_config_bridge_ops = { | ||
335 | .regmap_init = vexpress_sysreg_config_regmap_init, | ||
336 | .regmap_exit = vexpress_sysreg_config_regmap_exit, | ||
337 | }; | ||
338 | |||
339 | int vexpress_sysreg_config_device_register(struct platform_device *pdev) | ||
340 | { | ||
341 | pdev->dev.parent = vexpress_sysreg_config_bridge; | ||
342 | |||
343 | return platform_device_register(pdev); | ||
344 | } | 123 | } |
345 | 124 | ||
346 | 125 | ||
347 | void __init vexpress_sysreg_early_init(void __iomem *base) | 126 | void __init vexpress_sysreg_early_init(void __iomem *base) |
348 | { | 127 | { |
349 | vexpress_sysreg_base = base; | 128 | __vexpress_sysreg_base = base; |
350 | vexpress_config_set_master(vexpress_sysreg_get_master()); | ||
351 | } | ||
352 | |||
353 | void __init vexpress_sysreg_of_early_init(void) | ||
354 | { | ||
355 | struct device_node *node; | ||
356 | |||
357 | if (vexpress_sysreg_base) | ||
358 | return; | ||
359 | |||
360 | node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); | ||
361 | if (WARN_ON(!node)) | ||
362 | return; | ||
363 | |||
364 | vexpress_sysreg_base = of_iomap(node, 0); | ||
365 | if (WARN_ON(!vexpress_sysreg_base)) | ||
366 | return; | ||
367 | 129 | ||
368 | vexpress_config_set_master(vexpress_sysreg_get_master()); | 130 | vexpress_config_set_master(vexpress_sysreg_get_master()); |
369 | } | 131 | } |
370 | 132 | ||
371 | 133 | ||
372 | #ifdef CONFIG_GPIOLIB | 134 | /* The sysreg block is just a random collection of various functions... */ |
373 | |||
374 | #define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \ | ||
375 | [VEXPRESS_GPIO_##_name] = { \ | ||
376 | .reg = _reg, \ | ||
377 | .value = _reg##_##_value, \ | ||
378 | } | ||
379 | 135 | ||
380 | static struct vexpress_sysreg_gpio { | 136 | static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = { |
381 | unsigned long reg; | 137 | .label = "sys_id", |
382 | u32 value; | ||
383 | } vexpress_sysreg_gpios[] = { | ||
384 | VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN), | ||
385 | VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT), | ||
386 | VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn), | ||
387 | VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)), | ||
388 | VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)), | ||
389 | VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)), | ||
390 | VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)), | ||
391 | VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)), | ||
392 | VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)), | ||
393 | VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)), | ||
394 | VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)), | ||
395 | }; | 138 | }; |
396 | 139 | ||
397 | static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, | 140 | static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = { |
398 | unsigned offset) | 141 | .label = "sys_led", |
399 | { | 142 | .base = -1, |
400 | return 0; | 143 | .ngpio = 8, |
401 | } | ||
402 | |||
403 | static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, | ||
404 | unsigned offset) | ||
405 | { | ||
406 | struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; | ||
407 | u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); | ||
408 | |||
409 | return !!(reg_value & gpio->value); | ||
410 | } | ||
411 | |||
412 | static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, | ||
413 | unsigned offset, int value) | ||
414 | { | ||
415 | struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; | ||
416 | u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); | ||
417 | |||
418 | if (value) | ||
419 | reg_value |= gpio->value; | ||
420 | else | ||
421 | reg_value &= ~gpio->value; | ||
422 | |||
423 | writel(reg_value, vexpress_sysreg_base + gpio->reg); | ||
424 | } | ||
425 | |||
426 | static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, | ||
427 | unsigned offset, int value) | ||
428 | { | ||
429 | vexpress_sysreg_gpio_set(chip, offset, value); | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | static struct gpio_chip vexpress_sysreg_gpio_chip = { | ||
435 | .label = "vexpress-sysreg", | ||
436 | .direction_input = vexpress_sysreg_gpio_direction_input, | ||
437 | .direction_output = vexpress_sysreg_gpio_direction_output, | ||
438 | .get = vexpress_sysreg_gpio_get, | ||
439 | .set = vexpress_sysreg_gpio_set, | ||
440 | .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios), | ||
441 | .base = 0, | ||
442 | }; | 144 | }; |
443 | 145 | ||
444 | 146 | static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = { | |
445 | #define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \ | 147 | .label = "sys_mci", |
446 | { \ | 148 | .base = -1, |
447 | .name = "v2m:green:"_name, \ | 149 | .ngpio = 2, |
448 | .default_trigger = _default_trigger, \ | ||
449 | .gpio = VEXPRESS_GPIO_##_gpio, \ | ||
450 | } | ||
451 | |||
452 | struct gpio_led vexpress_sysreg_leds[] = { | ||
453 | VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0), | ||
454 | VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1), | ||
455 | VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2), | ||
456 | VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3), | ||
457 | VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4), | ||
458 | VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5), | ||
459 | VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6), | ||
460 | VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7), | ||
461 | }; | 150 | }; |
462 | 151 | ||
463 | struct gpio_led_platform_data vexpress_sysreg_leds_pdata = { | 152 | static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = { |
464 | .num_leds = ARRAY_SIZE(vexpress_sysreg_leds), | 153 | .label = "sys_flash", |
465 | .leds = vexpress_sysreg_leds, | 154 | .base = -1, |
155 | .ngpio = 1, | ||
466 | }; | 156 | }; |
467 | 157 | ||
468 | #endif | 158 | static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = { |
469 | 159 | .label = "sys_misc", | |
160 | }; | ||
470 | 161 | ||
471 | static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, | 162 | static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = { |
472 | struct device_attribute *attr, char *buf) | 163 | .label = "sys_procid", |
473 | { | 164 | }; |
474 | return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID)); | ||
475 | } | ||
476 | 165 | ||
477 | DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL); | 166 | static struct mfd_cell vexpress_sysreg_cells[] = { |
167 | { | ||
168 | .name = "syscon", | ||
169 | .num_resources = 1, | ||
170 | .resources = (struct resource []) { | ||
171 | DEFINE_RES_MEM(SYS_ID, 0x4), | ||
172 | }, | ||
173 | .platform_data = &vexpress_sysreg_sys_id_pdata, | ||
174 | .pdata_size = sizeof(vexpress_sysreg_sys_id_pdata), | ||
175 | }, { | ||
176 | .name = "basic-mmio-gpio", | ||
177 | .of_compatible = "arm,vexpress-sysreg,sys_led", | ||
178 | .num_resources = 1, | ||
179 | .resources = (struct resource []) { | ||
180 | DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"), | ||
181 | }, | ||
182 | .platform_data = &vexpress_sysreg_sys_led_pdata, | ||
183 | .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata), | ||
184 | }, { | ||
185 | .name = "basic-mmio-gpio", | ||
186 | .of_compatible = "arm,vexpress-sysreg,sys_mci", | ||
187 | .num_resources = 1, | ||
188 | .resources = (struct resource []) { | ||
189 | DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"), | ||
190 | }, | ||
191 | .platform_data = &vexpress_sysreg_sys_mci_pdata, | ||
192 | .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata), | ||
193 | }, { | ||
194 | .name = "basic-mmio-gpio", | ||
195 | .of_compatible = "arm,vexpress-sysreg,sys_flash", | ||
196 | .num_resources = 1, | ||
197 | .resources = (struct resource []) { | ||
198 | DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"), | ||
199 | }, | ||
200 | .platform_data = &vexpress_sysreg_sys_flash_pdata, | ||
201 | .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata), | ||
202 | }, { | ||
203 | .name = "syscon", | ||
204 | .num_resources = 1, | ||
205 | .resources = (struct resource []) { | ||
206 | DEFINE_RES_MEM(SYS_MISC, 0x4), | ||
207 | }, | ||
208 | .platform_data = &vexpress_sysreg_sys_misc_pdata, | ||
209 | .pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata), | ||
210 | }, { | ||
211 | .name = "syscon", | ||
212 | .num_resources = 1, | ||
213 | .resources = (struct resource []) { | ||
214 | DEFINE_RES_MEM(SYS_PROCID0, 0x8), | ||
215 | }, | ||
216 | .platform_data = &vexpress_sysreg_sys_procid_pdata, | ||
217 | .pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata), | ||
218 | }, { | ||
219 | .name = "vexpress-syscfg", | ||
220 | .num_resources = 1, | ||
221 | .resources = (struct resource []) { | ||
222 | DEFINE_RES_MEM(SYS_CFGDATA, 0xc), | ||
223 | }, | ||
224 | } | ||
225 | }; | ||
478 | 226 | ||
479 | static int vexpress_sysreg_probe(struct platform_device *pdev) | 227 | static int vexpress_sysreg_probe(struct platform_device *pdev) |
480 | { | 228 | { |
481 | int err; | 229 | struct resource *mem; |
482 | struct resource *res = platform_get_resource(pdev, | 230 | void __iomem *base; |
483 | IORESOURCE_MEM, 0); | 231 | struct bgpio_chip *mmc_gpio_chip; |
484 | |||
485 | if (!devm_request_mem_region(&pdev->dev, res->start, | ||
486 | resource_size(res), pdev->name)) { | ||
487 | dev_err(&pdev->dev, "Failed to request memory region!\n"); | ||
488 | return -EBUSY; | ||
489 | } | ||
490 | 232 | ||
491 | if (!vexpress_sysreg_base) | 233 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
492 | vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, | 234 | if (!mem) |
493 | resource_size(res)); | 235 | return -EINVAL; |
494 | 236 | ||
495 | if (!vexpress_sysreg_base) { | 237 | base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); |
496 | dev_err(&pdev->dev, "Failed to obtain base address!\n"); | 238 | if (!base) |
497 | return -EFAULT; | 239 | return -ENOMEM; |
498 | } | ||
499 | 240 | ||
500 | vexpress_config_set_master(vexpress_sysreg_get_master()); | 241 | vexpress_config_set_master(vexpress_sysreg_get_master()); |
501 | vexpress_sysreg_dev = &pdev->dev; | ||
502 | |||
503 | #ifdef CONFIG_GPIOLIB | ||
504 | vexpress_sysreg_gpio_chip.dev = &pdev->dev; | ||
505 | err = gpiochip_add(&vexpress_sysreg_gpio_chip); | ||
506 | if (err) { | ||
507 | dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", | ||
508 | err); | ||
509 | return err; | ||
510 | } | ||
511 | 242 | ||
512 | platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", | 243 | /* |
513 | PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, | 244 | * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with |
514 | sizeof(vexpress_sysreg_leds_pdata)); | 245 | * older trees using sysreg node for MMC control lines. |
515 | #endif | 246 | */ |
516 | 247 | mmc_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*mmc_gpio_chip), | |
517 | vexpress_sysreg_config_bridge = vexpress_config_bridge_register( | 248 | GFP_KERNEL); |
518 | &pdev->dev, &vexpress_sysreg_config_bridge_ops, NULL); | 249 | if (!mmc_gpio_chip) |
519 | WARN_ON(!vexpress_sysreg_config_bridge); | 250 | return -ENOMEM; |
520 | 251 | bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI, | |
521 | device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); | 252 | NULL, NULL, NULL, NULL, 0); |
522 | 253 | mmc_gpio_chip->gc.ngpio = 2; | |
523 | return 0; | 254 | gpiochip_add(&mmc_gpio_chip->gc); |
255 | |||
256 | return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, | ||
257 | vexpress_sysreg_cells, | ||
258 | ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL); | ||
524 | } | 259 | } |
525 | 260 | ||
526 | static const struct of_device_id vexpress_sysreg_match[] = { | 261 | static const struct of_device_id vexpress_sysreg_match[] = { |
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8baff0effc7d..d9663ef90ce8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -515,6 +515,15 @@ config SRAM | |||
515 | the genalloc API. It is supposed to be used for small on-chip SRAM | 515 | the genalloc API. It is supposed to be used for small on-chip SRAM |
516 | areas found on many SoCs. | 516 | areas found on many SoCs. |
517 | 517 | ||
518 | config VEXPRESS_SYSCFG | ||
519 | bool "Versatile Express System Configuration driver" | ||
520 | depends on VEXPRESS_CONFIG | ||
521 | default y | ||
522 | help | ||
523 | ARM Ltd. Versatile Express uses specialised platform configuration | ||
524 | bus. System Configuration interface is one of the possible means | ||
525 | of generating transactions on this bus. | ||
526 | |||
518 | source "drivers/misc/c2port/Kconfig" | 527 | source "drivers/misc/c2port/Kconfig" |
519 | source "drivers/misc/eeprom/Kconfig" | 528 | source "drivers/misc/eeprom/Kconfig" |
520 | source "drivers/misc/cb710/Kconfig" | 529 | source "drivers/misc/cb710/Kconfig" |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7eb4b69580c0..d59ce1261b38 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -55,3 +55,4 @@ obj-$(CONFIG_SRAM) += sram.o | |||
55 | obj-y += mic/ | 55 | obj-y += mic/ |
56 | obj-$(CONFIG_GENWQE) += genwqe/ | 56 | obj-$(CONFIG_GENWQE) += genwqe/ |
57 | obj-$(CONFIG_ECHO) += echo/ | 57 | obj-$(CONFIG_ECHO) += echo/ |
58 | obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o | ||
diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c new file mode 100644 index 000000000000..73068e50e56d --- /dev/null +++ b/drivers/misc/vexpress-syscfg.c | |||
@@ -0,0 +1,324 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License version 2 as | ||
4 | * published by the Free Software Foundation. | ||
5 | * | ||
6 | * This program is distributed in the hope that it will be useful, | ||
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9 | * GNU General Public License for more details. | ||
10 | * | ||
11 | * Copyright (C) 2014 ARM Limited | ||
12 | */ | ||
13 | |||
14 | #include <linux/delay.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/syscore_ops.h> | ||
22 | #include <linux/vexpress.h> | ||
23 | |||
24 | |||
25 | #define SYS_CFGDATA 0x0 | ||
26 | |||
27 | #define SYS_CFGCTRL 0x4 | ||
28 | #define SYS_CFGCTRL_START (1 << 31) | ||
29 | #define SYS_CFGCTRL_WRITE (1 << 30) | ||
30 | #define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) | ||
31 | #define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) | ||
32 | #define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) | ||
33 | #define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) | ||
34 | #define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) | ||
35 | |||
36 | #define SYS_CFGSTAT 0x8 | ||
37 | #define SYS_CFGSTAT_ERR (1 << 1) | ||
38 | #define SYS_CFGSTAT_COMPLETE (1 << 0) | ||
39 | |||
40 | |||
41 | struct vexpress_syscfg { | ||
42 | struct device *dev; | ||
43 | void __iomem *base; | ||
44 | struct list_head funcs; | ||
45 | }; | ||
46 | |||
47 | struct vexpress_syscfg_func { | ||
48 | struct list_head list; | ||
49 | struct vexpress_syscfg *syscfg; | ||
50 | struct regmap *regmap; | ||
51 | int num_templates; | ||
52 | u32 template[0]; /* Keep it last! */ | ||
53 | }; | ||
54 | |||
55 | |||
56 | static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func, | ||
57 | int index, bool write, u32 *data) | ||
58 | { | ||
59 | struct vexpress_syscfg *syscfg = func->syscfg; | ||
60 | u32 command, status; | ||
61 | int tries; | ||
62 | long timeout; | ||
63 | |||
64 | if (WARN_ON(index > func->num_templates)) | ||
65 | return -EINVAL; | ||
66 | |||
67 | command = readl(syscfg->base + SYS_CFGCTRL); | ||
68 | if (WARN_ON(command & SYS_CFGCTRL_START)) | ||
69 | return -EBUSY; | ||
70 | |||
71 | command = func->template[index]; | ||
72 | command |= SYS_CFGCTRL_START; | ||
73 | command |= write ? SYS_CFGCTRL_WRITE : 0; | ||
74 | |||
75 | /* Use a canary for reads */ | ||
76 | if (!write) | ||
77 | *data = 0xdeadbeef; | ||
78 | |||
79 | dev_dbg(syscfg->dev, "func %p, command %x, data %x\n", | ||
80 | func, command, *data); | ||
81 | writel(*data, syscfg->base + SYS_CFGDATA); | ||
82 | writel(0, syscfg->base + SYS_CFGSTAT); | ||
83 | writel(command, syscfg->base + SYS_CFGCTRL); | ||
84 | mb(); | ||
85 | |||
86 | /* The operation can take ages... Go to sleep, 100us initially */ | ||
87 | tries = 100; | ||
88 | timeout = 100; | ||
89 | do { | ||
90 | if (!irqs_disabled()) { | ||
91 | set_current_state(TASK_INTERRUPTIBLE); | ||
92 | schedule_timeout(usecs_to_jiffies(timeout)); | ||
93 | if (signal_pending(current)) | ||
94 | return -EINTR; | ||
95 | } else { | ||
96 | udelay(timeout); | ||
97 | } | ||
98 | |||
99 | status = readl(syscfg->base + SYS_CFGSTAT); | ||
100 | if (status & SYS_CFGSTAT_ERR) | ||
101 | return -EFAULT; | ||
102 | |||
103 | if (timeout > 20) | ||
104 | timeout -= 20; | ||
105 | } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); | ||
106 | if (WARN_ON_ONCE(!tries)) | ||
107 | return -ETIMEDOUT; | ||
108 | |||
109 | if (!write) { | ||
110 | *data = readl(syscfg->base + SYS_CFGDATA); | ||
111 | dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data); | ||
112 | } | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int vexpress_syscfg_read(void *context, unsigned int index, | ||
118 | unsigned int *val) | ||
119 | { | ||
120 | struct vexpress_syscfg_func *func = context; | ||
121 | |||
122 | return vexpress_syscfg_exec(func, index, false, val); | ||
123 | } | ||
124 | |||
125 | static int vexpress_syscfg_write(void *context, unsigned int index, | ||
126 | unsigned int val) | ||
127 | { | ||
128 | struct vexpress_syscfg_func *func = context; | ||
129 | |||
130 | return vexpress_syscfg_exec(func, index, true, &val); | ||
131 | } | ||
132 | |||
133 | struct regmap_config vexpress_syscfg_regmap_config = { | ||
134 | .lock = vexpress_config_lock, | ||
135 | .unlock = vexpress_config_unlock, | ||
136 | .reg_bits = 32, | ||
137 | .val_bits = 32, | ||
138 | .reg_read = vexpress_syscfg_read, | ||
139 | .reg_write = vexpress_syscfg_write, | ||
140 | .reg_format_endian = REGMAP_ENDIAN_LITTLE, | ||
141 | .val_format_endian = REGMAP_ENDIAN_LITTLE, | ||
142 | }; | ||
143 | |||
144 | |||
145 | static struct regmap *vexpress_syscfg_regmap_init(struct device *dev, | ||
146 | void *context) | ||
147 | { | ||
148 | struct platform_device *pdev = to_platform_device(dev); | ||
149 | struct vexpress_syscfg *syscfg = context; | ||
150 | struct vexpress_syscfg_func *func; | ||
151 | struct property *prop; | ||
152 | const __be32 *val = NULL; | ||
153 | __be32 energy_quirk[4]; | ||
154 | int num; | ||
155 | u32 site, position, dcc; | ||
156 | int i; | ||
157 | |||
158 | if (dev->of_node) { | ||
159 | int err = vexpress_config_get_topo(dev->of_node, &site, | ||
160 | &position, &dcc); | ||
161 | |||
162 | if (err) | ||
163 | return ERR_PTR(err); | ||
164 | |||
165 | prop = of_find_property(dev->of_node, | ||
166 | "arm,vexpress-sysreg,func", NULL); | ||
167 | if (!prop) | ||
168 | return ERR_PTR(-EINVAL); | ||
169 | |||
170 | num = prop->length / sizeof(u32) / 2; | ||
171 | val = prop->value; | ||
172 | } else { | ||
173 | if (pdev->num_resources != 1 || | ||
174 | pdev->resource[0].flags != IORESOURCE_BUS) | ||
175 | return ERR_PTR(-EFAULT); | ||
176 | |||
177 | site = pdev->resource[0].start; | ||
178 | if (site == VEXPRESS_SITE_MASTER) | ||
179 | site = vexpress_config_get_master(); | ||
180 | position = 0; | ||
181 | dcc = 0; | ||
182 | num = 1; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * "arm,vexpress-energy" function used to be described | ||
187 | * by its first device only, now it requires both | ||
188 | */ | ||
189 | if (num == 1 && of_device_is_compatible(dev->of_node, | ||
190 | "arm,vexpress-energy")) { | ||
191 | num = 2; | ||
192 | energy_quirk[0] = *val; | ||
193 | energy_quirk[2] = *val++; | ||
194 | energy_quirk[1] = *val; | ||
195 | energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); | ||
196 | val = energy_quirk; | ||
197 | } | ||
198 | |||
199 | func = kzalloc(sizeof(*func) + sizeof(*func->template) * num, | ||
200 | GFP_KERNEL); | ||
201 | if (!func) | ||
202 | return NULL; | ||
203 | |||
204 | func->syscfg = syscfg; | ||
205 | func->num_templates = num; | ||
206 | |||
207 | for (i = 0; i < num; i++) { | ||
208 | u32 function, device; | ||
209 | |||
210 | if (dev->of_node) { | ||
211 | function = be32_to_cpup(val++); | ||
212 | device = be32_to_cpup(val++); | ||
213 | } else { | ||
214 | function = pdev->resource[0].end; | ||
215 | device = pdev->id; | ||
216 | } | ||
217 | |||
218 | dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", | ||
219 | func, site, position, dcc, | ||
220 | function, device); | ||
221 | |||
222 | func->template[i] = SYS_CFGCTRL_DCC(dcc); | ||
223 | func->template[i] |= SYS_CFGCTRL_SITE(site); | ||
224 | func->template[i] |= SYS_CFGCTRL_POSITION(position); | ||
225 | func->template[i] |= SYS_CFGCTRL_FUNC(function); | ||
226 | func->template[i] |= SYS_CFGCTRL_DEVICE(device); | ||
227 | } | ||
228 | |||
229 | vexpress_syscfg_regmap_config.max_register = num - 1; | ||
230 | |||
231 | func->regmap = regmap_init(dev, NULL, func, | ||
232 | &vexpress_syscfg_regmap_config); | ||
233 | |||
234 | if (IS_ERR(func->regmap)) | ||
235 | kfree(func); | ||
236 | else | ||
237 | list_add(&func->list, &syscfg->funcs); | ||
238 | |||
239 | return func->regmap; | ||
240 | } | ||
241 | |||
242 | static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context) | ||
243 | { | ||
244 | struct vexpress_syscfg *syscfg = context; | ||
245 | struct vexpress_syscfg_func *func, *tmp; | ||
246 | |||
247 | regmap_exit(regmap); | ||
248 | |||
249 | list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) { | ||
250 | if (func->regmap == regmap) { | ||
251 | list_del(&syscfg->funcs); | ||
252 | kfree(func); | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = { | ||
259 | .regmap_init = vexpress_syscfg_regmap_init, | ||
260 | .regmap_exit = vexpress_syscfg_regmap_exit, | ||
261 | }; | ||
262 | |||
263 | |||
264 | /* Non-DT hack, to be gone... */ | ||
265 | static struct device *vexpress_syscfg_bridge; | ||
266 | |||
267 | int vexpress_syscfg_device_register(struct platform_device *pdev) | ||
268 | { | ||
269 | pdev->dev.parent = vexpress_syscfg_bridge; | ||
270 | |||
271 | return platform_device_register(pdev); | ||
272 | } | ||
273 | |||
274 | |||
275 | int vexpress_syscfg_probe(struct platform_device *pdev) | ||
276 | { | ||
277 | struct vexpress_syscfg *syscfg; | ||
278 | struct resource *res; | ||
279 | struct device *bridge; | ||
280 | |||
281 | syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL); | ||
282 | if (!syscfg) | ||
283 | return -ENOMEM; | ||
284 | syscfg->dev = &pdev->dev; | ||
285 | INIT_LIST_HEAD(&syscfg->funcs); | ||
286 | |||
287 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
288 | if (!devm_request_mem_region(&pdev->dev, res->start, | ||
289 | resource_size(res), pdev->name)) | ||
290 | return -EBUSY; | ||
291 | |||
292 | syscfg->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | ||
293 | if (!syscfg->base) | ||
294 | return -EFAULT; | ||
295 | |||
296 | /* Must use dev.parent (MFD), as that's where DT phandle points at... */ | ||
297 | bridge = vexpress_config_bridge_register(pdev->dev.parent, | ||
298 | &vexpress_syscfg_bridge_ops, syscfg); | ||
299 | if (IS_ERR(bridge)) | ||
300 | return PTR_ERR(bridge); | ||
301 | |||
302 | /* Non-DT case */ | ||
303 | if (!pdev->dev.of_node) | ||
304 | vexpress_syscfg_bridge = bridge; | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static const struct platform_device_id vexpress_syscfg_id_table[] = { | ||
310 | { "vexpress-syscfg", }, | ||
311 | {}, | ||
312 | }; | ||
313 | |||
314 | static struct platform_driver vexpress_syscfg_driver = { | ||
315 | .driver.name = "vexpress-syscfg", | ||
316 | .id_table = vexpress_syscfg_id_table, | ||
317 | .probe = vexpress_syscfg_probe, | ||
318 | }; | ||
319 | |||
320 | static int __init vexpress_syscfg_init(void) | ||
321 | { | ||
322 | return platform_driver_register(&vexpress_syscfg_driver); | ||
323 | } | ||
324 | core_initcall(vexpress_syscfg_init); | ||