1 // SPDX-License-Identifier: GPL-2.0-only
4 * Copyright (C) 2014 ARM Limited
7 #include <linux/delay.h>
11 #include <linux/platform_device.h>
12 #include <linux/sched/signal.h>
13 #include <linux/slab.h>
14 #include <linux/syscore_ops.h>
15 #include <linux/vexpress.h>
18 #define SYS_CFGDATA 0x0
20 #define SYS_CFGCTRL 0x4
21 #define SYS_CFGCTRL_START (1 << 31)
22 #define SYS_CFGCTRL_WRITE (1 << 30)
23 #define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26)
24 #define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20)
25 #define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16)
26 #define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12)
27 #define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0)
29 #define SYS_CFGSTAT 0x8
30 #define SYS_CFGSTAT_ERR (1 << 1)
31 #define SYS_CFGSTAT_COMPLETE (1 << 0)
34 struct vexpress_syscfg
{
37 struct list_head funcs
;
40 struct vexpress_syscfg_func
{
41 struct list_head list
;
42 struct vexpress_syscfg
*syscfg
;
43 struct regmap
*regmap
;
45 u32
template[0]; /* Keep it last! */
49 static int vexpress_syscfg_exec(struct vexpress_syscfg_func
*func
,
50 int index
, bool write
, u32
*data
)
52 struct vexpress_syscfg
*syscfg
= func
->syscfg
;
57 if (WARN_ON(index
>= func
->num_templates
))
60 command
= readl(syscfg
->base
+ SYS_CFGCTRL
);
61 if (WARN_ON(command
& SYS_CFGCTRL_START
))
64 command
= func
->template[index
];
65 command
|= SYS_CFGCTRL_START
;
66 command
|= write
? SYS_CFGCTRL_WRITE
: 0;
68 /* Use a canary for reads */
72 dev_dbg(syscfg
->dev
, "func %p, command %x, data %x\n",
73 func
, command
, *data
);
74 writel(*data
, syscfg
->base
+ SYS_CFGDATA
);
75 writel(0, syscfg
->base
+ SYS_CFGSTAT
);
76 writel(command
, syscfg
->base
+ SYS_CFGCTRL
);
79 /* The operation can take ages... Go to sleep, 100us initially */
83 if (!irqs_disabled()) {
84 set_current_state(TASK_INTERRUPTIBLE
);
85 schedule_timeout(usecs_to_jiffies(timeout
));
86 if (signal_pending(current
))
92 status
= readl(syscfg
->base
+ SYS_CFGSTAT
);
93 if (status
& SYS_CFGSTAT_ERR
)
98 } while (--tries
&& !(status
& SYS_CFGSTAT_COMPLETE
));
99 if (WARN_ON_ONCE(!tries
))
103 *data
= readl(syscfg
->base
+ SYS_CFGDATA
);
104 dev_dbg(syscfg
->dev
, "func %p, read data %x\n", func
, *data
);
110 static int vexpress_syscfg_read(void *context
, unsigned int index
,
113 struct vexpress_syscfg_func
*func
= context
;
115 return vexpress_syscfg_exec(func
, index
, false, val
);
118 static int vexpress_syscfg_write(void *context
, unsigned int index
,
121 struct vexpress_syscfg_func
*func
= context
;
123 return vexpress_syscfg_exec(func
, index
, true, &val
);
126 static struct regmap_config vexpress_syscfg_regmap_config
= {
127 .lock
= vexpress_config_lock
,
128 .unlock
= vexpress_config_unlock
,
131 .reg_read
= vexpress_syscfg_read
,
132 .reg_write
= vexpress_syscfg_write
,
133 .reg_format_endian
= REGMAP_ENDIAN_LITTLE
,
134 .val_format_endian
= REGMAP_ENDIAN_LITTLE
,
138 static struct regmap
*vexpress_syscfg_regmap_init(struct device
*dev
,
142 struct vexpress_syscfg
*syscfg
= context
;
143 struct vexpress_syscfg_func
*func
;
144 struct property
*prop
;
145 const __be32
*val
= NULL
;
146 __be32 energy_quirk
[4];
148 u32 site
, position
, dcc
;
151 err
= vexpress_config_get_topo(dev
->of_node
, &site
,
156 prop
= of_find_property(dev
->of_node
,
157 "arm,vexpress-sysreg,func", NULL
);
159 return ERR_PTR(-EINVAL
);
161 num
= prop
->length
/ sizeof(u32
) / 2;
165 * "arm,vexpress-energy" function used to be described
166 * by its first device only, now it requires both
168 if (num
== 1 && of_device_is_compatible(dev
->of_node
,
169 "arm,vexpress-energy")) {
171 energy_quirk
[0] = *val
;
172 energy_quirk
[2] = *val
++;
173 energy_quirk
[1] = *val
;
174 energy_quirk
[3] = cpu_to_be32(be32_to_cpup(val
) + 1);
178 func
= kzalloc(struct_size(func
, template, num
), GFP_KERNEL
);
180 return ERR_PTR(-ENOMEM
);
182 func
->syscfg
= syscfg
;
183 func
->num_templates
= num
;
185 for (i
= 0; i
< num
; i
++) {
186 u32 function
, device
;
188 function
= be32_to_cpup(val
++);
189 device
= be32_to_cpup(val
++);
191 dev_dbg(dev
, "func %p: %u/%u/%u/%u/%u\n",
192 func
, site
, position
, dcc
,
195 func
->template[i
] = SYS_CFGCTRL_DCC(dcc
);
196 func
->template[i
] |= SYS_CFGCTRL_SITE(site
);
197 func
->template[i
] |= SYS_CFGCTRL_POSITION(position
);
198 func
->template[i
] |= SYS_CFGCTRL_FUNC(function
);
199 func
->template[i
] |= SYS_CFGCTRL_DEVICE(device
);
202 vexpress_syscfg_regmap_config
.max_register
= num
- 1;
204 func
->regmap
= regmap_init(dev
, NULL
, func
,
205 &vexpress_syscfg_regmap_config
);
207 if (IS_ERR(func
->regmap
)) {
208 void *err
= func
->regmap
;
214 list_add(&func
->list
, &syscfg
->funcs
);
219 static void vexpress_syscfg_regmap_exit(struct regmap
*regmap
, void *context
)
221 struct vexpress_syscfg
*syscfg
= context
;
222 struct vexpress_syscfg_func
*func
, *tmp
;
226 list_for_each_entry_safe(func
, tmp
, &syscfg
->funcs
, list
) {
227 if (func
->regmap
== regmap
) {
228 list_del(&syscfg
->funcs
);
235 static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops
= {
236 .regmap_init
= vexpress_syscfg_regmap_init
,
237 .regmap_exit
= vexpress_syscfg_regmap_exit
,
241 static int vexpress_syscfg_probe(struct platform_device
*pdev
)
243 struct vexpress_syscfg
*syscfg
;
244 struct resource
*res
;
245 struct device
*bridge
;
247 syscfg
= devm_kzalloc(&pdev
->dev
, sizeof(*syscfg
), GFP_KERNEL
);
250 syscfg
->dev
= &pdev
->dev
;
251 INIT_LIST_HEAD(&syscfg
->funcs
);
253 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
254 syscfg
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
255 if (IS_ERR(syscfg
->base
))
256 return PTR_ERR(syscfg
->base
);
258 /* Must use dev.parent (MFD), as that's where DT phandle points at... */
259 bridge
= vexpress_config_bridge_register(pdev
->dev
.parent
,
260 &vexpress_syscfg_bridge_ops
, syscfg
);
262 return PTR_ERR_OR_ZERO(bridge
);
265 static const struct platform_device_id vexpress_syscfg_id_table
[] = {
266 { "vexpress-syscfg", },
270 static struct platform_driver vexpress_syscfg_driver
= {
271 .driver
.name
= "vexpress-syscfg",
272 .id_table
= vexpress_syscfg_id_table
,
273 .probe
= vexpress_syscfg_probe
,
276 static int __init
vexpress_syscfg_init(void)
278 return platform_driver_register(&vexpress_syscfg_driver
);
280 core_initcall(vexpress_syscfg_init
);