aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>2013-10-29 08:18:37 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-10-29 19:48:25 -0400
commitf7cd2d835e0f17cde2e5cead92be0099d7e92a7c (patch)
tree99fdd746006029a5683c4e671ea134ad01cdba3a /arch
parentad7722dab7292dbc1c4586d701ac226b68122d39 (diff)
ARM: vexpress/TC2: add support for CPU DVFS
SPC(Serial Power Controller) on TC2 also controls the CPU performance operating points which is essential to provide CPU DVFS. The M3 microcontroller provides two sets of eight performance values, one set for each cluster (CA15 or CA7). Each of this value contains the frequency(kHz) and voltage(mV) at that performance level. It expects these performance level to be passed through the SPC PERF_LVL registers. This patch adds support to populate these performance levels from M3, build the mapping to CPU OPPs at the boot and then use it to get and set the CPU performance level runtime. Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com> Acked-by: Nicolas Pitre <nico@linaro.org> Acked-by: Pawel Moll <Pawel.Moll@arm.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-vexpress/Kconfig12
-rw-r--r--arch/arm/mach-vexpress/Makefile3
-rw-r--r--arch/arm/mach-vexpress/spc.c262
-rw-r--r--arch/arm/mach-vexpress/spc.h2
-rw-r--r--arch/arm/mach-vexpress/tc2_pm.c7
5 files changed, 281 insertions, 5 deletions
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
index 365795447804..c77170c04fd0 100644
--- a/arch/arm/mach-vexpress/Kconfig
+++ b/arch/arm/mach-vexpress/Kconfig
@@ -66,10 +66,22 @@ config ARCH_VEXPRESS_DCSCB
66 This is needed to provide CPU and cluster power management 66 This is needed to provide CPU and cluster power management
67 on RTSM implementing big.LITTLE. 67 on RTSM implementing big.LITTLE.
68 68
69config ARCH_VEXPRESS_SPC
70 bool "Versatile Express Serial Power Controller (SPC)"
71 select ARCH_HAS_CPUFREQ
72 select ARCH_HAS_OPP
73 select PM_OPP
74 help
75 The TC2 (A15x2 A7x3) versatile express core tile integrates a logic
76 block called Serial Power Controller (SPC) that provides the interface
77 between the dual cluster test-chip and the M3 microcontroller that
78 carries out power management.
79
69config ARCH_VEXPRESS_TC2_PM 80config ARCH_VEXPRESS_TC2_PM
70 bool "Versatile Express TC2 power management" 81 bool "Versatile Express TC2 power management"
71 depends on MCPM 82 depends on MCPM
72 select ARM_CCI 83 select ARM_CCI
84 select ARCH_VEXPRESS_SPC
73 help 85 help
74 Support for CPU and cluster power management on Versatile Express 86 Support for CPU and cluster power management on Versatile Express
75 with a TC2 (A15x2 A7x3) big.LITTLE core tile. 87 with a TC2 (A15x2 A7x3) big.LITTLE core tile.
diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile
index 505e64ab3eae..0997e0b7494c 100644
--- a/arch/arm/mach-vexpress/Makefile
+++ b/arch/arm/mach-vexpress/Makefile
@@ -8,7 +8,8 @@ obj-y := v2m.o
8obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o 8obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o
9obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o 9obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o
10CFLAGS_dcscb.o += -march=armv7-a 10CFLAGS_dcscb.o += -march=armv7-a
11obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += tc2_pm.o spc.o 11obj-$(CONFIG_ARCH_VEXPRESS_SPC) += spc.o
12obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += tc2_pm.o
12CFLAGS_tc2_pm.o += -march=armv7-a 13CFLAGS_tc2_pm.o += -march=armv7-a
13obj-$(CONFIG_SMP) += platsmp.o 14obj-$(CONFIG_SMP) += platsmp.o
14obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o 15obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-vexpress/spc.c b/arch/arm/mach-vexpress/spc.c
index eefb029197ca..de0ca3615c36 100644
--- a/arch/arm/mach-vexpress/spc.c
+++ b/arch/arm/mach-vexpress/spc.c
@@ -17,14 +17,27 @@
17 * GNU General Public License for more details. 17 * GNU General Public License for more details.
18 */ 18 */
19 19
20#include <linux/delay.h>
20#include <linux/err.h> 21#include <linux/err.h>
22#include <linux/interrupt.h>
21#include <linux/io.h> 23#include <linux/io.h>
24#include <linux/pm_opp.h>
22#include <linux/slab.h> 25#include <linux/slab.h>
26#include <linux/semaphore.h>
23 27
24#include <asm/cacheflush.h> 28#include <asm/cacheflush.h>
25 29
26#define SPCLOG "vexpress-spc: " 30#define SPCLOG "vexpress-spc: "
27 31
32#define PERF_LVL_A15 0x00
33#define PERF_REQ_A15 0x04
34#define PERF_LVL_A7 0x08
35#define PERF_REQ_A7 0x0c
36#define COMMS 0x10
37#define COMMS_REQ 0x14
38#define PWC_STATUS 0x18
39#define PWC_FLAG 0x1c
40
28/* SPC wake-up IRQs status and mask */ 41/* SPC wake-up IRQs status and mask */
29#define WAKE_INT_MASK 0x24 42#define WAKE_INT_MASK 0x24
30#define WAKE_INT_RAW 0x28 43#define WAKE_INT_RAW 0x28
@@ -36,12 +49,45 @@
36#define A15_BX_ADDR0 0x68 49#define A15_BX_ADDR0 0x68
37#define A7_BX_ADDR0 0x78 50#define A7_BX_ADDR0 0x78
38 51
52/* SPC system config interface registers */
53#define SYSCFG_WDATA 0x70
54#define SYSCFG_RDATA 0x74
55
56/* A15/A7 OPP virtual register base */
57#define A15_PERFVAL_BASE 0xC10
58#define A7_PERFVAL_BASE 0xC30
59
60/* Config interface control bits */
61#define SYSCFG_START (1 << 31)
62#define SYSCFG_SCC (6 << 20)
63#define SYSCFG_STAT (14 << 20)
64
39/* wake-up interrupt masks */ 65/* wake-up interrupt masks */
40#define GBL_WAKEUP_INT_MSK (0x3 << 10) 66#define GBL_WAKEUP_INT_MSK (0x3 << 10)
41 67
42/* TC2 static dual-cluster configuration */ 68/* TC2 static dual-cluster configuration */
43#define MAX_CLUSTERS 2 69#define MAX_CLUSTERS 2
44 70
71/*
72 * Even though the SPC takes max 3-5 ms to complete any OPP/COMMS
73 * operation, the operation could start just before jiffie is about
74 * to be incremented. So setting timeout value of 20ms = 2jiffies@100Hz
75 */
76#define TIMEOUT_US 20000
77
78#define MAX_OPPS 8
79#define CA15_DVFS 0
80#define CA7_DVFS 1
81#define SPC_SYS_CFG 2
82#define STAT_COMPLETE(type) ((1 << 0) << (type << 2))
83#define STAT_ERR(type) ((1 << 1) << (type << 2))
84#define RESPONSE_MASK(type) (STAT_COMPLETE(type) | STAT_ERR(type))
85
86struct ve_spc_opp {
87 unsigned long freq;
88 unsigned long u_volt;
89};
90
45struct ve_spc_drvdata { 91struct ve_spc_drvdata {
46 void __iomem *baseaddr; 92 void __iomem *baseaddr;
47 /* 93 /*
@@ -49,6 +95,12 @@ struct ve_spc_drvdata {
49 * It corresponds to A15 processors MPIDR[15:8] bitfield 95 * It corresponds to A15 processors MPIDR[15:8] bitfield
50 */ 96 */
51 u32 a15_clusid; 97 u32 a15_clusid;
98 uint32_t cur_rsp_mask;
99 uint32_t cur_rsp_stat;
100 struct semaphore sem;
101 struct completion done;
102 struct ve_spc_opp *opps[MAX_CLUSTERS];
103 int num_opps[MAX_CLUSTERS];
52}; 104};
53 105
54static struct ve_spc_drvdata *info; 106static struct ve_spc_drvdata *info;
@@ -157,8 +209,197 @@ void ve_spc_powerdown(u32 cluster, bool enable)
157 writel_relaxed(enable, info->baseaddr + pwdrn_reg); 209 writel_relaxed(enable, info->baseaddr + pwdrn_reg);
158} 210}
159 211
160int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid) 212static int ve_spc_get_performance(int cluster, u32 *freq)
213{
214 struct ve_spc_opp *opps = info->opps[cluster];
215 u32 perf_cfg_reg = 0;
216 u32 perf;
217
218 perf_cfg_reg = cluster_is_a15(cluster) ? PERF_LVL_A15 : PERF_LVL_A7;
219
220 perf = readl_relaxed(info->baseaddr + perf_cfg_reg);
221 if (perf >= info->num_opps[cluster])
222 return -EINVAL;
223
224 opps += perf;
225 *freq = opps->freq;
226
227 return 0;
228}
229
230/* find closest match to given frequency in OPP table */
231static int ve_spc_round_performance(int cluster, u32 freq)
232{
233 int idx, max_opp = info->num_opps[cluster];
234 struct ve_spc_opp *opps = info->opps[cluster];
235 u32 fmin = 0, fmax = ~0, ftmp;
236
237 freq /= 1000; /* OPP entries in kHz */
238 for (idx = 0; idx < max_opp; idx++, opps++) {
239 ftmp = opps->freq;
240 if (ftmp >= freq) {
241 if (ftmp <= fmax)
242 fmax = ftmp;
243 } else {
244 if (ftmp >= fmin)
245 fmin = ftmp;
246 }
247 }
248 if (fmax != ~0)
249 return fmax * 1000;
250 else
251 return fmin * 1000;
252}
253
254static int ve_spc_find_performance_index(int cluster, u32 freq)
255{
256 int idx, max_opp = info->num_opps[cluster];
257 struct ve_spc_opp *opps = info->opps[cluster];
258
259 for (idx = 0; idx < max_opp; idx++, opps++)
260 if (opps->freq == freq)
261 break;
262 return (idx == max_opp) ? -EINVAL : idx;
263}
264
265static int ve_spc_waitforcompletion(int req_type)
266{
267 int ret = wait_for_completion_interruptible_timeout(
268 &info->done, usecs_to_jiffies(TIMEOUT_US));
269 if (ret == 0)
270 ret = -ETIMEDOUT;
271 else if (ret > 0)
272 ret = info->cur_rsp_stat & STAT_COMPLETE(req_type) ? 0 : -EIO;
273 return ret;
274}
275
276static int ve_spc_set_performance(int cluster, u32 freq)
277{
278 u32 perf_cfg_reg, perf_stat_reg;
279 int ret, perf, req_type;
280
281 if (cluster_is_a15(cluster)) {
282 req_type = CA15_DVFS;
283 perf_cfg_reg = PERF_LVL_A15;
284 perf_stat_reg = PERF_REQ_A15;
285 } else {
286 req_type = CA7_DVFS;
287 perf_cfg_reg = PERF_LVL_A7;
288 perf_stat_reg = PERF_REQ_A7;
289 }
290
291 perf = ve_spc_find_performance_index(cluster, freq);
292
293 if (perf < 0)
294 return perf;
295
296 if (down_timeout(&info->sem, usecs_to_jiffies(TIMEOUT_US)))
297 return -ETIME;
298
299 init_completion(&info->done);
300 info->cur_rsp_mask = RESPONSE_MASK(req_type);
301
302 writel(perf, info->baseaddr + perf_cfg_reg);
303 ret = ve_spc_waitforcompletion(req_type);
304
305 info->cur_rsp_mask = 0;
306 up(&info->sem);
307
308 return ret;
309}
310
311static int ve_spc_read_sys_cfg(int func, int offset, uint32_t *data)
312{
313 int ret;
314
315 if (down_timeout(&info->sem, usecs_to_jiffies(TIMEOUT_US)))
316 return -ETIME;
317
318 init_completion(&info->done);
319 info->cur_rsp_mask = RESPONSE_MASK(SPC_SYS_CFG);
320
321 /* Set the control value */
322 writel(SYSCFG_START | func | offset >> 2, info->baseaddr + COMMS);
323 ret = ve_spc_waitforcompletion(SPC_SYS_CFG);
324
325 if (ret == 0)
326 *data = readl(info->baseaddr + SYSCFG_RDATA);
327
328 info->cur_rsp_mask = 0;
329 up(&info->sem);
330
331 return ret;
332}
333
334static irqreturn_t ve_spc_irq_handler(int irq, void *data)
335{
336 struct ve_spc_drvdata *drv_data = data;
337 uint32_t status = readl_relaxed(drv_data->baseaddr + PWC_STATUS);
338
339 if (info->cur_rsp_mask & status) {
340 info->cur_rsp_stat = status;
341 complete(&drv_data->done);
342 }
343
344 return IRQ_HANDLED;
345}
346
347/*
348 * +--------------------------+
349 * | 31 20 | 19 0 |
350 * +--------------------------+
351 * | u_volt | freq(kHz) |
352 * +--------------------------+
353 */
354#define MULT_FACTOR 20
355#define VOLT_SHIFT 20
356#define FREQ_MASK (0xFFFFF)
357static int ve_spc_populate_opps(uint32_t cluster)
358{
359 uint32_t data = 0, off, ret, idx;
360 struct ve_spc_opp *opps;
361
362 opps = kzalloc(sizeof(*opps) * MAX_OPPS, GFP_KERNEL);
363 if (!opps)
364 return -ENOMEM;
365
366 info->opps[cluster] = opps;
367
368 off = cluster_is_a15(cluster) ? A15_PERFVAL_BASE : A7_PERFVAL_BASE;
369 for (idx = 0; idx < MAX_OPPS; idx++, off += 4, opps++) {
370 ret = ve_spc_read_sys_cfg(SYSCFG_SCC, off, &data);
371 if (!ret) {
372 opps->freq = (data & FREQ_MASK) * MULT_FACTOR;
373 opps->u_volt = data >> VOLT_SHIFT;
374 } else {
375 break;
376 }
377 }
378 info->num_opps[cluster] = idx;
379
380 return ret;
381}
382
383static int ve_init_opp_table(struct device *cpu_dev)
384{
385 int cluster = topology_physical_package_id(cpu_dev->id);
386 int idx, ret = 0, max_opp = info->num_opps[cluster];
387 struct ve_spc_opp *opps = info->opps[cluster];
388
389 for (idx = 0; idx < max_opp; idx++, opps++) {
390 ret = dev_pm_opp_add(cpu_dev, opps->freq * 1000, opps->u_volt);
391 if (ret) {
392 dev_warn(cpu_dev, "failed to add opp %lu %lu\n",
393 opps->freq, opps->u_volt);
394 return ret;
395 }
396 }
397 return ret;
398}
399
400int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq)
161{ 401{
402 int ret;
162 info = kzalloc(sizeof(*info), GFP_KERNEL); 403 info = kzalloc(sizeof(*info), GFP_KERNEL);
163 if (!info) { 404 if (!info) {
164 pr_err(SPCLOG "unable to allocate mem\n"); 405 pr_err(SPCLOG "unable to allocate mem\n");
@@ -168,6 +409,25 @@ int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid)
168 info->baseaddr = baseaddr; 409 info->baseaddr = baseaddr;
169 info->a15_clusid = a15_clusid; 410 info->a15_clusid = a15_clusid;
170 411
412 if (irq <= 0) {
413 pr_err(SPCLOG "Invalid IRQ %d\n", irq);
414 kfree(info);
415 return -EINVAL;
416 }
417
418 init_completion(&info->done);
419
420 readl_relaxed(info->baseaddr + PWC_STATUS);
421
422 ret = request_irq(irq, ve_spc_irq_handler, IRQF_TRIGGER_HIGH
423 | IRQF_ONESHOT, "vexpress-spc", info);
424 if (ret) {
425 pr_err(SPCLOG "IRQ %d request failed\n", irq);
426 kfree(info);
427 return -ENODEV;
428 }
429
430 sema_init(&info->sem, 1);
171 /* 431 /*
172 * Multi-cluster systems may need this data when non-coherent, during 432 * Multi-cluster systems may need this data when non-coherent, during
173 * cluster power-up/power-down. Make sure driver info reaches main 433 * cluster power-up/power-down. Make sure driver info reaches main
diff --git a/arch/arm/mach-vexpress/spc.h b/arch/arm/mach-vexpress/spc.h
index 5f7e4a446a17..dbd44c3720f9 100644
--- a/arch/arm/mach-vexpress/spc.h
+++ b/arch/arm/mach-vexpress/spc.h
@@ -15,7 +15,7 @@
15#ifndef __SPC_H_ 15#ifndef __SPC_H_
16#define __SPC_H_ 16#define __SPC_H_
17 17
18int __init ve_spc_init(void __iomem *base, u32 a15_clusid); 18int __init ve_spc_init(void __iomem *base, u32 a15_clusid, int irq);
19void ve_spc_global_wakeup_irq(bool set); 19void ve_spc_global_wakeup_irq(bool set);
20void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set); 20void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set);
21void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr); 21void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr);
diff --git a/arch/arm/mach-vexpress/tc2_pm.c b/arch/arm/mach-vexpress/tc2_pm.c
index e6eb48192912..d38130aba464 100644
--- a/arch/arm/mach-vexpress/tc2_pm.c
+++ b/arch/arm/mach-vexpress/tc2_pm.c
@@ -16,6 +16,7 @@
16#include <linux/io.h> 16#include <linux/io.h>
17#include <linux/kernel.h> 17#include <linux/kernel.h>
18#include <linux/of_address.h> 18#include <linux/of_address.h>
19#include <linux/of_irq.h>
19#include <linux/spinlock.h> 20#include <linux/spinlock.h>
20#include <linux/errno.h> 21#include <linux/errno.h>
21#include <linux/irqchip/arm-gic.h> 22#include <linux/irqchip/arm-gic.h>
@@ -311,7 +312,7 @@ static void __naked tc2_pm_power_up_setup(unsigned int affinity_level)
311 312
312static int __init tc2_pm_init(void) 313static int __init tc2_pm_init(void)
313{ 314{
314 int ret; 315 int ret, irq;
315 void __iomem *scc; 316 void __iomem *scc;
316 u32 a15_cluster_id, a7_cluster_id, sys_info; 317 u32 a15_cluster_id, a7_cluster_id, sys_info;
317 struct device_node *np; 318 struct device_node *np;
@@ -336,13 +337,15 @@ static int __init tc2_pm_init(void)
336 tc2_nr_cpus[a15_cluster_id] = (sys_info >> 16) & 0xf; 337 tc2_nr_cpus[a15_cluster_id] = (sys_info >> 16) & 0xf;
337 tc2_nr_cpus[a7_cluster_id] = (sys_info >> 20) & 0xf; 338 tc2_nr_cpus[a7_cluster_id] = (sys_info >> 20) & 0xf;
338 339
340 irq = irq_of_parse_and_map(np, 0);
341
339 /* 342 /*
340 * A subset of the SCC registers is also used to communicate 343 * A subset of the SCC registers is also used to communicate
341 * with the SPC (power controller). We need to be able to 344 * with the SPC (power controller). We need to be able to
342 * drive it very early in the boot process to power up 345 * drive it very early in the boot process to power up
343 * processors, so we initialize the SPC driver here. 346 * processors, so we initialize the SPC driver here.
344 */ 347 */
345 ret = ve_spc_init(scc + SPC_BASE, a15_cluster_id); 348 ret = ve_spc_init(scc + SPC_BASE, a15_cluster_id, irq);
346 if (ret) 349 if (ret)
347 return ret; 350 return ret;
348 351