aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-vexpress/spc.c
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/arm/mach-vexpress/spc.c
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/arm/mach-vexpress/spc.c')
-rw-r--r--arch/arm/mach-vexpress/spc.c262
1 files changed, 261 insertions, 1 deletions
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