1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Versatile Express Serial Power Controller (SPC) support |
4 | * |
5 | * Copyright (C) 2013 ARM Ltd. |
6 | * |
7 | * Authors: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com> |
8 | * Achin Gupta <achin.gupta@arm.com> |
9 | * Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> |
10 | */ |
11 | |
12 | #include <linux/clk-provider.h> |
13 | #include <linux/clkdev.h> |
14 | #include <linux/cpu.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/err.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/io.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/pm_opp.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/semaphore.h> |
23 | |
24 | #include <asm/cacheflush.h> |
25 | |
26 | #include "spc.h" |
27 | |
28 | #define SPCLOG "vexpress-spc: " |
29 | |
30 | #define PERF_LVL_A15 0x00 |
31 | #define PERF_REQ_A15 0x04 |
32 | #define PERF_LVL_A7 0x08 |
33 | #define PERF_REQ_A7 0x0c |
34 | #define COMMS 0x10 |
35 | #define COMMS_REQ 0x14 |
36 | #define PWC_STATUS 0x18 |
37 | #define PWC_FLAG 0x1c |
38 | |
39 | /* SPC wake-up IRQs status and mask */ |
40 | #define WAKE_INT_MASK 0x24 |
41 | #define WAKE_INT_RAW 0x28 |
42 | #define WAKE_INT_STAT 0x2c |
43 | /* SPC power down registers */ |
44 | #define A15_PWRDN_EN 0x30 |
45 | #define A7_PWRDN_EN 0x34 |
46 | /* SPC per-CPU mailboxes */ |
47 | #define A15_BX_ADDR0 0x68 |
48 | #define A7_BX_ADDR0 0x78 |
49 | |
50 | /* SPC CPU/cluster reset statue */ |
51 | #define STANDBYWFI_STAT 0x3c |
52 | #define STANDBYWFI_STAT_A15_CPU_MASK(cpu) (1 << (cpu)) |
53 | #define STANDBYWFI_STAT_A7_CPU_MASK(cpu) (1 << (3 + (cpu))) |
54 | |
55 | /* SPC system config interface registers */ |
56 | #define SYSCFG_WDATA 0x70 |
57 | #define SYSCFG_RDATA 0x74 |
58 | |
59 | /* A15/A7 OPP virtual register base */ |
60 | #define A15_PERFVAL_BASE 0xC10 |
61 | #define A7_PERFVAL_BASE 0xC30 |
62 | |
63 | /* Config interface control bits */ |
64 | #define SYSCFG_START BIT(31) |
65 | #define SYSCFG_SCC (6 << 20) |
66 | #define SYSCFG_STAT (14 << 20) |
67 | |
68 | /* wake-up interrupt masks */ |
69 | #define GBL_WAKEUP_INT_MSK (0x3 << 10) |
70 | |
71 | /* TC2 static dual-cluster configuration */ |
72 | #define MAX_CLUSTERS 2 |
73 | |
74 | /* |
75 | * Even though the SPC takes max 3-5 ms to complete any OPP/COMMS |
76 | * operation, the operation could start just before jiffie is about |
77 | * to be incremented. So setting timeout value of 20ms = 2jiffies@100Hz |
78 | */ |
79 | #define TIMEOUT_US 20000 |
80 | |
81 | #define MAX_OPPS 8 |
82 | #define CA15_DVFS 0 |
83 | #define CA7_DVFS 1 |
84 | #define SPC_SYS_CFG 2 |
85 | #define STAT_COMPLETE(type) ((1 << 0) << (type << 2)) |
86 | #define STAT_ERR(type) ((1 << 1) << (type << 2)) |
87 | #define RESPONSE_MASK(type) (STAT_COMPLETE(type) | STAT_ERR(type)) |
88 | |
89 | struct ve_spc_opp { |
90 | unsigned long freq; |
91 | unsigned long u_volt; |
92 | }; |
93 | |
94 | struct ve_spc_drvdata { |
95 | void __iomem *baseaddr; |
96 | /* |
97 | * A15s cluster identifier |
98 | * It corresponds to A15 processors MPIDR[15:8] bitfield |
99 | */ |
100 | u32 a15_clusid; |
101 | uint32_t cur_rsp_mask; |
102 | uint32_t cur_rsp_stat; |
103 | struct semaphore sem; |
104 | struct completion done; |
105 | struct ve_spc_opp *opps[MAX_CLUSTERS]; |
106 | int num_opps[MAX_CLUSTERS]; |
107 | }; |
108 | |
109 | static struct ve_spc_drvdata *info; |
110 | |
111 | static inline bool cluster_is_a15(u32 cluster) |
112 | { |
113 | return cluster == info->a15_clusid; |
114 | } |
115 | |
116 | /** |
117 | * ve_spc_global_wakeup_irq() - sets/clears global wakeup IRQs |
118 | * |
119 | * @set: if true, global wake-up IRQs are set, if false they are cleared |
120 | * |
121 | * Function to set/clear global wakeup IRQs. Not protected by locking since |
122 | * it might be used in code paths where normal cacheable locks are not |
123 | * working. Locking must be provided by the caller to ensure atomicity. |
124 | */ |
125 | void ve_spc_global_wakeup_irq(bool set) |
126 | { |
127 | u32 reg; |
128 | |
129 | reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK); |
130 | |
131 | if (set) |
132 | reg |= GBL_WAKEUP_INT_MSK; |
133 | else |
134 | reg &= ~GBL_WAKEUP_INT_MSK; |
135 | |
136 | writel_relaxed(reg, info->baseaddr + WAKE_INT_MASK); |
137 | } |
138 | |
139 | /** |
140 | * ve_spc_cpu_wakeup_irq() - sets/clears per-CPU wake-up IRQs |
141 | * |
142 | * @cluster: mpidr[15:8] bitfield describing cluster affinity level |
143 | * @cpu: mpidr[7:0] bitfield describing cpu affinity level |
144 | * @set: if true, wake-up IRQs are set, if false they are cleared |
145 | * |
146 | * Function to set/clear per-CPU wake-up IRQs. Not protected by locking since |
147 | * it might be used in code paths where normal cacheable locks are not |
148 | * working. Locking must be provided by the caller to ensure atomicity. |
149 | */ |
150 | void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set) |
151 | { |
152 | u32 mask, reg; |
153 | |
154 | if (cluster >= MAX_CLUSTERS) |
155 | return; |
156 | |
157 | mask = BIT(cpu); |
158 | |
159 | if (!cluster_is_a15(cluster)) |
160 | mask <<= 4; |
161 | |
162 | reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK); |
163 | |
164 | if (set) |
165 | reg |= mask; |
166 | else |
167 | reg &= ~mask; |
168 | |
169 | writel_relaxed(reg, info->baseaddr + WAKE_INT_MASK); |
170 | } |
171 | |
172 | /** |
173 | * ve_spc_set_resume_addr() - set the jump address used for warm boot |
174 | * |
175 | * @cluster: mpidr[15:8] bitfield describing cluster affinity level |
176 | * @cpu: mpidr[7:0] bitfield describing cpu affinity level |
177 | * @addr: physical resume address |
178 | */ |
179 | void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr) |
180 | { |
181 | void __iomem *baseaddr; |
182 | |
183 | if (cluster >= MAX_CLUSTERS) |
184 | return; |
185 | |
186 | if (cluster_is_a15(cluster)) |
187 | baseaddr = info->baseaddr + A15_BX_ADDR0 + (cpu << 2); |
188 | else |
189 | baseaddr = info->baseaddr + A7_BX_ADDR0 + (cpu << 2); |
190 | |
191 | writel_relaxed(addr, baseaddr); |
192 | } |
193 | |
194 | /** |
195 | * ve_spc_powerdown() - enables/disables cluster powerdown |
196 | * |
197 | * @cluster: mpidr[15:8] bitfield describing cluster affinity level |
198 | * @enable: if true enables powerdown, if false disables it |
199 | * |
200 | * Function to enable/disable cluster powerdown. Not protected by locking |
201 | * since it might be used in code paths where normal cacheable locks are not |
202 | * working. Locking must be provided by the caller to ensure atomicity. |
203 | */ |
204 | void ve_spc_powerdown(u32 cluster, bool enable) |
205 | { |
206 | u32 pwdrn_reg; |
207 | |
208 | if (cluster >= MAX_CLUSTERS) |
209 | return; |
210 | |
211 | pwdrn_reg = cluster_is_a15(cluster) ? A15_PWRDN_EN : A7_PWRDN_EN; |
212 | writel_relaxed(enable, info->baseaddr + pwdrn_reg); |
213 | } |
214 | |
215 | static u32 standbywfi_cpu_mask(u32 cpu, u32 cluster) |
216 | { |
217 | return cluster_is_a15(cluster) ? |
218 | STANDBYWFI_STAT_A15_CPU_MASK(cpu) |
219 | : STANDBYWFI_STAT_A7_CPU_MASK(cpu); |
220 | } |
221 | |
222 | /** |
223 | * ve_spc_cpu_in_wfi() - Checks if the specified CPU is in WFI or not |
224 | * |
225 | * @cpu: mpidr[7:0] bitfield describing CPU affinity level within cluster |
226 | * @cluster: mpidr[15:8] bitfield describing cluster affinity level |
227 | * |
228 | * @return: non-zero if and only if the specified CPU is in WFI |
229 | * |
230 | * Take care when interpreting the result of this function: a CPU might |
231 | * be in WFI temporarily due to idle, and is not necessarily safely |
232 | * parked. |
233 | */ |
234 | int ve_spc_cpu_in_wfi(u32 cpu, u32 cluster) |
235 | { |
236 | int ret; |
237 | u32 mask = standbywfi_cpu_mask(cpu, cluster); |
238 | |
239 | if (cluster >= MAX_CLUSTERS) |
240 | return 1; |
241 | |
242 | ret = readl_relaxed(info->baseaddr + STANDBYWFI_STAT); |
243 | |
244 | pr_debug("%s: PCFGREG[0x%X] = 0x%08X, mask = 0x%X\n" , |
245 | __func__, STANDBYWFI_STAT, ret, mask); |
246 | |
247 | return ret & mask; |
248 | } |
249 | |
250 | static int ve_spc_get_performance(int cluster, u32 *freq) |
251 | { |
252 | struct ve_spc_opp *opps = info->opps[cluster]; |
253 | u32 perf_cfg_reg = 0; |
254 | u32 perf; |
255 | |
256 | perf_cfg_reg = cluster_is_a15(cluster) ? PERF_LVL_A15 : PERF_LVL_A7; |
257 | |
258 | perf = readl_relaxed(info->baseaddr + perf_cfg_reg); |
259 | if (perf >= info->num_opps[cluster]) |
260 | return -EINVAL; |
261 | |
262 | opps += perf; |
263 | *freq = opps->freq; |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | /* find closest match to given frequency in OPP table */ |
269 | static int ve_spc_round_performance(int cluster, u32 freq) |
270 | { |
271 | int idx, max_opp = info->num_opps[cluster]; |
272 | struct ve_spc_opp *opps = info->opps[cluster]; |
273 | u32 fmin = 0, fmax = ~0, ftmp; |
274 | |
275 | freq /= 1000; /* OPP entries in kHz */ |
276 | for (idx = 0; idx < max_opp; idx++, opps++) { |
277 | ftmp = opps->freq; |
278 | if (ftmp >= freq) { |
279 | if (ftmp <= fmax) |
280 | fmax = ftmp; |
281 | } else { |
282 | if (ftmp >= fmin) |
283 | fmin = ftmp; |
284 | } |
285 | } |
286 | if (fmax != ~0) |
287 | return fmax * 1000; |
288 | else |
289 | return fmin * 1000; |
290 | } |
291 | |
292 | static int ve_spc_find_performance_index(int cluster, u32 freq) |
293 | { |
294 | int idx, max_opp = info->num_opps[cluster]; |
295 | struct ve_spc_opp *opps = info->opps[cluster]; |
296 | |
297 | for (idx = 0; idx < max_opp; idx++, opps++) |
298 | if (opps->freq == freq) |
299 | break; |
300 | return (idx == max_opp) ? -EINVAL : idx; |
301 | } |
302 | |
303 | static int ve_spc_waitforcompletion(int req_type) |
304 | { |
305 | int ret = wait_for_completion_interruptible_timeout( |
306 | x: &info->done, timeout: usecs_to_jiffies(TIMEOUT_US)); |
307 | if (ret == 0) |
308 | ret = -ETIMEDOUT; |
309 | else if (ret > 0) |
310 | ret = info->cur_rsp_stat & STAT_COMPLETE(req_type) ? 0 : -EIO; |
311 | return ret; |
312 | } |
313 | |
314 | static int ve_spc_set_performance(int cluster, u32 freq) |
315 | { |
316 | u32 perf_cfg_reg; |
317 | int ret, perf, req_type; |
318 | |
319 | if (cluster_is_a15(cluster)) { |
320 | req_type = CA15_DVFS; |
321 | perf_cfg_reg = PERF_LVL_A15; |
322 | } else { |
323 | req_type = CA7_DVFS; |
324 | perf_cfg_reg = PERF_LVL_A7; |
325 | } |
326 | |
327 | perf = ve_spc_find_performance_index(cluster, freq); |
328 | |
329 | if (perf < 0) |
330 | return perf; |
331 | |
332 | if (down_timeout(sem: &info->sem, jiffies: usecs_to_jiffies(TIMEOUT_US))) |
333 | return -ETIME; |
334 | |
335 | init_completion(x: &info->done); |
336 | info->cur_rsp_mask = RESPONSE_MASK(req_type); |
337 | |
338 | writel(val: perf, addr: info->baseaddr + perf_cfg_reg); |
339 | ret = ve_spc_waitforcompletion(req_type); |
340 | |
341 | info->cur_rsp_mask = 0; |
342 | up(sem: &info->sem); |
343 | |
344 | return ret; |
345 | } |
346 | |
347 | static int ve_spc_read_sys_cfg(int func, int offset, uint32_t *data) |
348 | { |
349 | int ret; |
350 | |
351 | if (down_timeout(sem: &info->sem, jiffies: usecs_to_jiffies(TIMEOUT_US))) |
352 | return -ETIME; |
353 | |
354 | init_completion(x: &info->done); |
355 | info->cur_rsp_mask = RESPONSE_MASK(SPC_SYS_CFG); |
356 | |
357 | /* Set the control value */ |
358 | writel(SYSCFG_START | func | offset >> 2, addr: info->baseaddr + COMMS); |
359 | ret = ve_spc_waitforcompletion(SPC_SYS_CFG); |
360 | |
361 | if (ret == 0) |
362 | *data = readl(addr: info->baseaddr + SYSCFG_RDATA); |
363 | |
364 | info->cur_rsp_mask = 0; |
365 | up(sem: &info->sem); |
366 | |
367 | return ret; |
368 | } |
369 | |
370 | static irqreturn_t ve_spc_irq_handler(int irq, void *data) |
371 | { |
372 | struct ve_spc_drvdata *drv_data = data; |
373 | uint32_t status = readl_relaxed(drv_data->baseaddr + PWC_STATUS); |
374 | |
375 | if (info->cur_rsp_mask & status) { |
376 | info->cur_rsp_stat = status; |
377 | complete(&drv_data->done); |
378 | } |
379 | |
380 | return IRQ_HANDLED; |
381 | } |
382 | |
383 | /* |
384 | * +--------------------------+ |
385 | * | 31 20 | 19 0 | |
386 | * +--------------------------+ |
387 | * | m_volt | freq(kHz) | |
388 | * +--------------------------+ |
389 | */ |
390 | #define MULT_FACTOR 20 |
391 | #define VOLT_SHIFT 20 |
392 | #define FREQ_MASK (0xFFFFF) |
393 | static int ve_spc_populate_opps(uint32_t cluster) |
394 | { |
395 | uint32_t data = 0, off, ret, idx; |
396 | struct ve_spc_opp *opps; |
397 | |
398 | opps = kcalloc(MAX_OPPS, size: sizeof(*opps), GFP_KERNEL); |
399 | if (!opps) |
400 | return -ENOMEM; |
401 | |
402 | info->opps[cluster] = opps; |
403 | |
404 | off = cluster_is_a15(cluster) ? A15_PERFVAL_BASE : A7_PERFVAL_BASE; |
405 | for (idx = 0; idx < MAX_OPPS; idx++, off += 4, opps++) { |
406 | ret = ve_spc_read_sys_cfg(SYSCFG_SCC, offset: off, data: &data); |
407 | if (!ret) { |
408 | opps->freq = (data & FREQ_MASK) * MULT_FACTOR; |
409 | opps->u_volt = (data >> VOLT_SHIFT) * 1000; |
410 | } else { |
411 | break; |
412 | } |
413 | } |
414 | info->num_opps[cluster] = idx; |
415 | |
416 | return ret; |
417 | } |
418 | |
419 | static int ve_init_opp_table(struct device *cpu_dev) |
420 | { |
421 | int cluster; |
422 | int idx, ret = 0, max_opp; |
423 | struct ve_spc_opp *opps; |
424 | |
425 | cluster = topology_physical_package_id(cpu_dev->id); |
426 | cluster = cluster < 0 ? 0 : cluster; |
427 | |
428 | max_opp = info->num_opps[cluster]; |
429 | opps = info->opps[cluster]; |
430 | |
431 | for (idx = 0; idx < max_opp; idx++, opps++) { |
432 | ret = dev_pm_opp_add(dev: cpu_dev, freq: opps->freq * 1000, u_volt: opps->u_volt); |
433 | if (ret) { |
434 | dev_warn(cpu_dev, "failed to add opp %lu %lu\n" , |
435 | opps->freq, opps->u_volt); |
436 | return ret; |
437 | } |
438 | } |
439 | return ret; |
440 | } |
441 | |
442 | int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq) |
443 | { |
444 | int ret; |
445 | info = kzalloc(size: sizeof(*info), GFP_KERNEL); |
446 | if (!info) |
447 | return -ENOMEM; |
448 | |
449 | info->baseaddr = baseaddr; |
450 | info->a15_clusid = a15_clusid; |
451 | |
452 | if (irq <= 0) { |
453 | pr_err(SPCLOG "Invalid IRQ %d\n" , irq); |
454 | kfree(objp: info); |
455 | return -EINVAL; |
456 | } |
457 | |
458 | init_completion(x: &info->done); |
459 | |
460 | readl_relaxed(info->baseaddr + PWC_STATUS); |
461 | |
462 | ret = request_irq(irq, handler: ve_spc_irq_handler, IRQF_TRIGGER_HIGH |
463 | | IRQF_ONESHOT, name: "vexpress-spc" , dev: info); |
464 | if (ret) { |
465 | pr_err(SPCLOG "IRQ %d request failed\n" , irq); |
466 | kfree(objp: info); |
467 | return -ENODEV; |
468 | } |
469 | |
470 | sema_init(sem: &info->sem, val: 1); |
471 | /* |
472 | * Multi-cluster systems may need this data when non-coherent, during |
473 | * cluster power-up/power-down. Make sure driver info reaches main |
474 | * memory. |
475 | */ |
476 | sync_cache_w(info); |
477 | sync_cache_w(&info); |
478 | |
479 | return 0; |
480 | } |
481 | |
482 | struct clk_spc { |
483 | struct clk_hw hw; |
484 | int cluster; |
485 | }; |
486 | |
487 | #define to_clk_spc(spc) container_of(spc, struct clk_spc, hw) |
488 | static unsigned long spc_recalc_rate(struct clk_hw *hw, |
489 | unsigned long parent_rate) |
490 | { |
491 | struct clk_spc *spc = to_clk_spc(hw); |
492 | u32 freq; |
493 | |
494 | if (ve_spc_get_performance(cluster: spc->cluster, freq: &freq)) |
495 | return -EIO; |
496 | |
497 | return freq * 1000; |
498 | } |
499 | |
500 | static long spc_round_rate(struct clk_hw *hw, unsigned long drate, |
501 | unsigned long *parent_rate) |
502 | { |
503 | struct clk_spc *spc = to_clk_spc(hw); |
504 | |
505 | return ve_spc_round_performance(cluster: spc->cluster, freq: drate); |
506 | } |
507 | |
508 | static int spc_set_rate(struct clk_hw *hw, unsigned long rate, |
509 | unsigned long parent_rate) |
510 | { |
511 | struct clk_spc *spc = to_clk_spc(hw); |
512 | |
513 | return ve_spc_set_performance(cluster: spc->cluster, freq: rate / 1000); |
514 | } |
515 | |
516 | static struct clk_ops clk_spc_ops = { |
517 | .recalc_rate = spc_recalc_rate, |
518 | .round_rate = spc_round_rate, |
519 | .set_rate = spc_set_rate, |
520 | }; |
521 | |
522 | static struct clk *ve_spc_clk_register(struct device *cpu_dev) |
523 | { |
524 | struct clk_init_data init; |
525 | struct clk_spc *spc; |
526 | |
527 | spc = kzalloc(size: sizeof(*spc), GFP_KERNEL); |
528 | if (!spc) |
529 | return ERR_PTR(error: -ENOMEM); |
530 | |
531 | spc->hw.init = &init; |
532 | spc->cluster = topology_physical_package_id(cpu_dev->id); |
533 | |
534 | spc->cluster = spc->cluster < 0 ? 0 : spc->cluster; |
535 | |
536 | init.name = dev_name(dev: cpu_dev); |
537 | init.ops = &clk_spc_ops; |
538 | init.flags = CLK_GET_RATE_NOCACHE; |
539 | init.num_parents = 0; |
540 | |
541 | return devm_clk_register(dev: cpu_dev, hw: &spc->hw); |
542 | } |
543 | |
544 | static int __init ve_spc_clk_init(void) |
545 | { |
546 | int cpu, cluster; |
547 | struct clk *clk; |
548 | bool init_opp_table[MAX_CLUSTERS] = { false }; |
549 | |
550 | if (!info) |
551 | return 0; /* Continue only if SPC is initialised */ |
552 | |
553 | if (ve_spc_populate_opps(cluster: 0) || ve_spc_populate_opps(cluster: 1)) { |
554 | pr_err("failed to build OPP table\n" ); |
555 | return -ENODEV; |
556 | } |
557 | |
558 | for_each_possible_cpu(cpu) { |
559 | struct device *cpu_dev = get_cpu_device(cpu); |
560 | if (!cpu_dev) { |
561 | pr_warn("failed to get cpu%d device\n" , cpu); |
562 | continue; |
563 | } |
564 | clk = ve_spc_clk_register(cpu_dev); |
565 | if (IS_ERR(ptr: clk)) { |
566 | pr_warn("failed to register cpu%d clock\n" , cpu); |
567 | continue; |
568 | } |
569 | if (clk_register_clkdev(clk, NULL, dev_name(dev: cpu_dev))) { |
570 | pr_warn("failed to register cpu%d clock lookup\n" , cpu); |
571 | continue; |
572 | } |
573 | |
574 | cluster = topology_physical_package_id(cpu_dev->id); |
575 | if (cluster < 0 || init_opp_table[cluster]) |
576 | continue; |
577 | |
578 | if (ve_init_opp_table(cpu_dev)) |
579 | pr_warn("failed to initialise cpu%d opp table\n" , cpu); |
580 | else if (dev_pm_opp_set_sharing_cpus(cpu_dev, |
581 | topology_core_cpumask(cpu_dev->id))) |
582 | pr_warn("failed to mark OPPs shared for cpu%d\n" , cpu); |
583 | else |
584 | init_opp_table[cluster] = true; |
585 | } |
586 | |
587 | platform_device_register_simple(name: "vexpress-spc-cpufreq" , id: -1, NULL, num: 0); |
588 | return 0; |
589 | } |
590 | device_initcall(ve_spc_clk_init); |
591 | |