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 static 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
,
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
;
158 err
= vexpress_config_get_topo(dev
->of_node
, &site
,
163 prop
= of_find_property(dev
->of_node
,
164 "arm,vexpress-sysreg,func", NULL
);
166 return ERR_PTR(-EINVAL
);
168 num
= prop
->length
/ sizeof(u32
) / 2;
172 * "arm,vexpress-energy" function used to be described
173 * by its first device only, now it requires both
175 if (num
== 1 && of_device_is_compatible(dev
->of_node
,
176 "arm,vexpress-energy")) {
178 energy_quirk
[0] = *val
;
179 energy_quirk
[2] = *val
++;
180 energy_quirk
[1] = *val
;
181 energy_quirk
[3] = cpu_to_be32(be32_to_cpup(val
) + 1);
185 func
= kzalloc(sizeof(*func
) + sizeof(*func
->template) * num
,
188 return ERR_PTR(-ENOMEM
);
190 func
->syscfg
= syscfg
;
191 func
->num_templates
= num
;
193 for (i
= 0; i
< num
; i
++) {
194 u32 function
, device
;
196 function
= be32_to_cpup(val
++);
197 device
= be32_to_cpup(val
++);
199 dev_dbg(dev
, "func %p: %u/%u/%u/%u/%u\n",
200 func
, site
, position
, dcc
,
203 func
->template[i
] = SYS_CFGCTRL_DCC(dcc
);
204 func
->template[i
] |= SYS_CFGCTRL_SITE(site
);
205 func
->template[i
] |= SYS_CFGCTRL_POSITION(position
);
206 func
->template[i
] |= SYS_CFGCTRL_FUNC(function
);
207 func
->template[i
] |= SYS_CFGCTRL_DEVICE(device
);
210 vexpress_syscfg_regmap_config
.max_register
= num
- 1;
212 func
->regmap
= regmap_init(dev
, NULL
, func
,
213 &vexpress_syscfg_regmap_config
);
215 if (IS_ERR(func
->regmap
)) {
216 void *err
= func
->regmap
;
222 list_add(&func
->list
, &syscfg
->funcs
);
227 static void vexpress_syscfg_regmap_exit(struct regmap
*regmap
, void *context
)
229 struct vexpress_syscfg
*syscfg
= context
;
230 struct vexpress_syscfg_func
*func
, *tmp
;
234 list_for_each_entry_safe(func
, tmp
, &syscfg
->funcs
, list
) {
235 if (func
->regmap
== regmap
) {
236 list_del(&syscfg
->funcs
);
243 static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops
= {
244 .regmap_init
= vexpress_syscfg_regmap_init
,
245 .regmap_exit
= vexpress_syscfg_regmap_exit
,
249 static int vexpress_syscfg_probe(struct platform_device
*pdev
)
251 struct vexpress_syscfg
*syscfg
;
252 struct resource
*res
;
253 struct device
*bridge
;
255 syscfg
= devm_kzalloc(&pdev
->dev
, sizeof(*syscfg
), GFP_KERNEL
);
258 syscfg
->dev
= &pdev
->dev
;
259 INIT_LIST_HEAD(&syscfg
->funcs
);
261 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
262 if (!devm_request_mem_region(&pdev
->dev
, res
->start
,
263 resource_size(res
), pdev
->name
))
266 syscfg
->base
= devm_ioremap(&pdev
->dev
, res
->start
, resource_size(res
));
270 /* Must use dev.parent (MFD), as that's where DT phandle points at... */
271 bridge
= vexpress_config_bridge_register(pdev
->dev
.parent
,
272 &vexpress_syscfg_bridge_ops
, syscfg
);
274 return PTR_ERR(bridge
);
279 static const struct platform_device_id vexpress_syscfg_id_table
[] = {
280 { "vexpress-syscfg", },
284 static struct platform_driver vexpress_syscfg_driver
= {
285 .driver
.name
= "vexpress-syscfg",
286 .id_table
= vexpress_syscfg_id_table
,
287 .probe
= vexpress_syscfg_probe
,
290 static int __init
vexpress_syscfg_init(void)
292 return platform_driver_register(&vexpress_syscfg_driver
);
294 core_initcall(vexpress_syscfg_init
);