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.
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.
11 * Copyright (C) 2014 ARM Limited
14 #include <linux/delay.h>
15 #include <linux/err.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>
25 #define SYS_CFGDATA 0x0
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)
36 #define SYS_CFGSTAT 0x8
37 #define SYS_CFGSTAT_ERR (1 << 1)
38 #define SYS_CFGSTAT_COMPLETE (1 << 0)
41 struct vexpress_syscfg
{
44 struct list_head funcs
;
47 struct vexpress_syscfg_func
{
48 struct list_head list
;
49 struct vexpress_syscfg
*syscfg
;
50 struct regmap
*regmap
;
52 u32
template[0]; /* Keep it last! */
56 static int vexpress_syscfg_exec(struct vexpress_syscfg_func
*func
,
57 int index
, bool write
, u32
*data
)
59 struct vexpress_syscfg
*syscfg
= func
->syscfg
;
64 if (WARN_ON(index
> func
->num_templates
))
67 command
= readl(syscfg
->base
+ SYS_CFGCTRL
);
68 if (WARN_ON(command
& SYS_CFGCTRL_START
))
71 command
= func
->template[index
];
72 command
|= SYS_CFGCTRL_START
;
73 command
|= write
? SYS_CFGCTRL_WRITE
: 0;
75 /* Use a canary for reads */
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
);
86 /* The operation can take ages... Go to sleep, 100us initially */
90 if (!irqs_disabled()) {
91 set_current_state(TASK_INTERRUPTIBLE
);
92 schedule_timeout(usecs_to_jiffies(timeout
));
93 if (signal_pending(current
))
99 status
= readl(syscfg
->base
+ SYS_CFGSTAT
);
100 if (status
& SYS_CFGSTAT_ERR
)
105 } while (--tries
&& !(status
& SYS_CFGSTAT_COMPLETE
));
106 if (WARN_ON_ONCE(!tries
))
110 *data
= readl(syscfg
->base
+ SYS_CFGDATA
);
111 dev_dbg(syscfg
->dev
, "func %p, read data %x\n", func
, *data
);
117 static int vexpress_syscfg_read(void *context
, unsigned int index
,
120 struct vexpress_syscfg_func
*func
= context
;
122 return vexpress_syscfg_exec(func
, index
, false, val
);
125 static int vexpress_syscfg_write(void *context
, unsigned int index
,
128 struct vexpress_syscfg_func
*func
= context
;
130 return vexpress_syscfg_exec(func
, index
, true, &val
);
133 struct regmap_config vexpress_syscfg_regmap_config
= {
134 .lock
= vexpress_config_lock
,
135 .unlock
= vexpress_config_unlock
,
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
,
145 static struct regmap
*vexpress_syscfg_regmap_init(struct device
*dev
,
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];
155 u32 site
, position
, dcc
;
159 int err
= vexpress_config_get_topo(dev
->of_node
, &site
,
165 prop
= of_find_property(dev
->of_node
,
166 "arm,vexpress-sysreg,func", NULL
);
168 return ERR_PTR(-EINVAL
);
170 num
= prop
->length
/ sizeof(u32
) / 2;
173 if (pdev
->num_resources
!= 1 ||
174 pdev
->resource
[0].flags
!= IORESOURCE_BUS
)
175 return ERR_PTR(-EFAULT
);
177 site
= pdev
->resource
[0].start
;
178 if (site
== VEXPRESS_SITE_MASTER
)
179 site
= vexpress_config_get_master();
186 * "arm,vexpress-energy" function used to be described
187 * by its first device only, now it requires both
189 if (num
== 1 && of_device_is_compatible(dev
->of_node
,
190 "arm,vexpress-energy")) {
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);
199 func
= kzalloc(sizeof(*func
) + sizeof(*func
->template) * num
,
202 return ERR_PTR(-ENOMEM
);
204 func
->syscfg
= syscfg
;
205 func
->num_templates
= num
;
207 for (i
= 0; i
< num
; i
++) {
208 u32 function
, device
;
211 function
= be32_to_cpup(val
++);
212 device
= be32_to_cpup(val
++);
214 function
= pdev
->resource
[0].end
;
218 dev_dbg(dev
, "func %p: %u/%u/%u/%u/%u\n",
219 func
, site
, position
, dcc
,
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
);
229 vexpress_syscfg_regmap_config
.max_register
= num
- 1;
231 func
->regmap
= regmap_init(dev
, NULL
, func
,
232 &vexpress_syscfg_regmap_config
);
234 if (IS_ERR(func
->regmap
)) {
235 void *err
= func
->regmap
;
241 list_add(&func
->list
, &syscfg
->funcs
);
246 static void vexpress_syscfg_regmap_exit(struct regmap
*regmap
, void *context
)
248 struct vexpress_syscfg
*syscfg
= context
;
249 struct vexpress_syscfg_func
*func
, *tmp
;
253 list_for_each_entry_safe(func
, tmp
, &syscfg
->funcs
, list
) {
254 if (func
->regmap
== regmap
) {
255 list_del(&syscfg
->funcs
);
262 static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops
= {
263 .regmap_init
= vexpress_syscfg_regmap_init
,
264 .regmap_exit
= vexpress_syscfg_regmap_exit
,
268 /* Non-DT hack, to be gone... */
269 static struct device
*vexpress_syscfg_bridge
;
271 int vexpress_syscfg_device_register(struct platform_device
*pdev
)
273 pdev
->dev
.parent
= vexpress_syscfg_bridge
;
275 return platform_device_register(pdev
);
279 int vexpress_syscfg_probe(struct platform_device
*pdev
)
281 struct vexpress_syscfg
*syscfg
;
282 struct resource
*res
;
283 struct device
*bridge
;
285 syscfg
= devm_kzalloc(&pdev
->dev
, sizeof(*syscfg
), GFP_KERNEL
);
288 syscfg
->dev
= &pdev
->dev
;
289 INIT_LIST_HEAD(&syscfg
->funcs
);
291 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
292 if (!devm_request_mem_region(&pdev
->dev
, res
->start
,
293 resource_size(res
), pdev
->name
))
296 syscfg
->base
= devm_ioremap(&pdev
->dev
, res
->start
, resource_size(res
));
300 /* Must use dev.parent (MFD), as that's where DT phandle points at... */
301 bridge
= vexpress_config_bridge_register(pdev
->dev
.parent
,
302 &vexpress_syscfg_bridge_ops
, syscfg
);
304 return PTR_ERR(bridge
);
307 if (!pdev
->dev
.of_node
)
308 vexpress_syscfg_bridge
= bridge
;
313 static const struct platform_device_id vexpress_syscfg_id_table
[] = {
314 { "vexpress-syscfg", },
318 static struct platform_driver vexpress_syscfg_driver
= {
319 .driver
.name
= "vexpress-syscfg",
320 .id_table
= vexpress_syscfg_id_table
,
321 .probe
= vexpress_syscfg_probe
,
324 static int __init
vexpress_syscfg_init(void)
326 return platform_driver_register(&vexpress_syscfg_driver
);
328 core_initcall(vexpress_syscfg_init
);