diff options
| -rw-r--r-- | arch/arm/mach-vexpress/Kconfig | 12 | ||||
| -rw-r--r-- | arch/arm/mach-vexpress/Makefile | 3 | ||||
| -rw-r--r-- | arch/arm/mach-vexpress/spc.c | 262 | ||||
| -rw-r--r-- | arch/arm/mach-vexpress/spc.h | 2 | ||||
| -rw-r--r-- | arch/arm/mach-vexpress/tc2_pm.c | 7 |
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 | ||
| 69 | config 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 | |||
| 69 | config ARCH_VEXPRESS_TC2_PM | 80 | config 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 | |||
| 8 | obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o | 8 | obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o |
| 9 | obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o | 9 | obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o |
| 10 | CFLAGS_dcscb.o += -march=armv7-a | 10 | CFLAGS_dcscb.o += -march=armv7-a |
| 11 | obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += tc2_pm.o spc.o | 11 | obj-$(CONFIG_ARCH_VEXPRESS_SPC) += spc.o |
| 12 | obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += tc2_pm.o | ||
| 12 | CFLAGS_tc2_pm.o += -march=armv7-a | 13 | CFLAGS_tc2_pm.o += -march=armv7-a |
| 13 | obj-$(CONFIG_SMP) += platsmp.o | 14 | obj-$(CONFIG_SMP) += platsmp.o |
| 14 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o | 15 | obj-$(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 | |||
| 86 | struct ve_spc_opp { | ||
| 87 | unsigned long freq; | ||
| 88 | unsigned long u_volt; | ||
| 89 | }; | ||
| 90 | |||
| 45 | struct ve_spc_drvdata { | 91 | struct 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 | ||
| 54 | static struct ve_spc_drvdata *info; | 106 | static 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 | ||
| 160 | int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid) | 212 | static 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 */ | ||
| 231 | static 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 | |||
| 254 | static 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 | |||
| 265 | static 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 | |||
| 276 | static 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 | |||
| 311 | static 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 | |||
| 334 | static 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) | ||
| 357 | static 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 | |||
| 383 | static 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 | |||
| 400 | int __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 | ||
| 18 | int __init ve_spc_init(void __iomem *base, u32 a15_clusid); | 18 | int __init ve_spc_init(void __iomem *base, u32 a15_clusid, int irq); |
| 19 | void ve_spc_global_wakeup_irq(bool set); | 19 | void ve_spc_global_wakeup_irq(bool set); |
| 20 | void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set); | 20 | void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set); |
| 21 | void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr); | 21 | void 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 | ||
| 312 | static int __init tc2_pm_init(void) | 313 | static 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 | ||
