From: Magnus Karlsson <magnus.karls...@intel.com> Adds a choice to hotplug. There are two ways to power down the cpu, either to a low power mode or completely off. If the power off mode is selected then the a new option is available to power off the L2 cache as well.
This code was originally written by Charlie Paul <cpaul.windri...@gmail.com> and John Jacques <john.jacq...@lsi.com>. Only minor modifications performed by the author of this patch. Signed-off-by: Charlie Paul <charlie.p...@windriver.com> Signed-off-by: John Jacques <john.jacq...@lsi.com> Signed-off-by: Magnus Karlsson <magnus.karls...@intel.com> --- arch/arm/Kconfig | 37 ++ arch/arm/mach-axxia/axxia.c | 2 - arch/arm/mach-axxia/axxia.h | 5 + arch/arm/mach-axxia/ddr_retention.c | 3 +- arch/arm/mach-axxia/hotplug.c | 236 +++++++-- arch/arm/mach-axxia/lsi_power_management.c | 765 +++++++++++----------------- arch/arm/mach-axxia/lsi_power_management.h | 15 +- arch/arm/mach-axxia/platsmp.c | 64 ++- 8 files changed, 593 insertions(+), 534 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c15c6fa..c766c5f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1593,6 +1593,8 @@ config NR_CPUS depends on SMP default "4" +menu "Support for hot-pluggable CPUs" + config HOTPLUG_CPU bool "Support for hot-pluggable CPUs" depends on SMP && HOTPLUG @@ -1600,6 +1602,41 @@ config HOTPLUG_CPU Say Y here to experiment with turning CPUs off and on. CPUs can be controlled through /sys/devices/system/cpu. +choice + prompt "CPU Power Down Mode" + default HOTPLUG_CPU_COMPLETE_POWER_DOWN + help + This is used to select how the CPU is going to be powered down. If LOW POWER + is selected then the CPU enters a WFI state and waits for an interrupt to + wake up. If COMPLETE POWER down is selected the CPU power is turned off. The only + way to power on the CPU is to execute a command. + +config HOTPLUG_CPU_COMPLETE_POWER_DOWN + bool "Power off the CPU" + help + This will power off the CPU completely. The irqs are migrated + to another CPU. + +config HOTPLUG_CPU_LOW_POWER + bool "Low Power CPU (wfi)" + help + This will put the CPU into a low power mode wfi mode. When an interrupt + is received the CPU will power on again. + +endchoice + +config HOTPLUG_CPU_L2_POWER_DOWN + bool "Power Off L2 Cache" + depends on HOTPLUG_CPU_COMPLETE_POWER_DOWN + default n if HOTPLUG_CPU_LOW_POWER + help + Select this if you want to power down the L2 cache when + all CPUS of a cluster have been powered off. + +endmenu + + + config ARM_PSCI bool "Support for the ARM Power State Coordination Interface (PSCI)" depends on CPU_V7 diff --git a/arch/arm/mach-axxia/axxia.c b/arch/arm/mach-axxia/axxia.c index 4847e1d..0753d03 100644 --- a/arch/arm/mach-axxia/axxia.c +++ b/arch/arm/mach-axxia/axxia.c @@ -63,7 +63,6 @@ static const char *axxia_dt_match[] __initconst = { }; static void __iomem *base; -static void __iomem *dickens; static void set_l3_pstate(u32 newstate) { @@ -199,7 +198,6 @@ void __init axxia_dt_init(void) { base = ioremap(0x2010000000, 0x40000); if (!of_find_compatible_node(NULL, NULL, "lsi,axm5500-sim")) { - dickens = ioremap(0x2000000000, SZ_4M); #ifdef CONFIG_KEXEC kexec_reinit = flush_l3; #endif diff --git a/arch/arm/mach-axxia/axxia.h b/arch/arm/mach-axxia/axxia.h index 000adc8..c46b76d 100644 --- a/arch/arm/mach-axxia/axxia.h +++ b/arch/arm/mach-axxia/axxia.h @@ -5,6 +5,11 @@ void axxia_ddr_retention_init(void); void axxia_platform_cpu_die(unsigned int cpu); int axxia_platform_cpu_kill(unsigned int cpu); +extern void axxia_secondary_startup(void); + extern struct smp_operations axxia_smp_ops; +extern void __iomem *syscon; +extern void __iomem *dickens; + #endif diff --git a/arch/arm/mach-axxia/ddr_retention.c b/arch/arm/mach-axxia/ddr_retention.c index 7430dec..48732e9 100644 --- a/arch/arm/mach-axxia/ddr_retention.c +++ b/arch/arm/mach-axxia/ddr_retention.c @@ -32,10 +32,10 @@ #include <asm/io.h> #include <asm/cacheflush.h> #include <mach/ncr.h> +#include "axxia.h" static void __iomem *nca; static void __iomem *apb; -static void __iomem *dickens; static int ddr_retention_enabled; extern int ncr_read_nolock(unsigned long, unsigned long, int, void *); extern int ncr_write_nolock(unsigned long, unsigned long, int, void *); @@ -350,7 +350,6 @@ axxia_ddr_retention_init(void) } else { apb = ioremap(0x2010000000, 0x80000); nca = ioremap(0x002020100000ULL, 0x20000); - dickens = ioremap(0x2000000000, 0x1000000); ddr_retention_enabled = 1; pr_info("DDR Retention Reset Initialized\n"); } diff --git a/arch/arm/mach-axxia/hotplug.c b/arch/arm/mach-axxia/hotplug.c index 9e82bdc..e8754dc 100644 --- a/arch/arm/mach-axxia/hotplug.c +++ b/arch/arm/mach-axxia/hotplug.c @@ -11,40 +11,144 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/smp.h> +#include <linux/of_address.h> +#include <linux/delay.h> +#include <mach/axxia-gic.h> #include <asm/cacheflush.h> #include <asm/smp_plat.h> #include <asm/cp15.h> #include "lsi_power_management.h" - extern volatile int pen_release; -static inline void cpu_enter_lowpower_a15(void) +static inline void pm_cpu_logical_shutdown(u32 cpu) { - unsigned int v; + u32 val; + + asm volatile( + " mrc p15, 1, %0, c9, c0, 2\n" + : "=&r" (val) + : "Ir" (0x1) + : "cc"); asm volatile( " mrc p15, 0, %0, c1, c0, 0\n" " bic %0, %0, %1\n" " mcr p15, 0, %0, c1, c0, 0\n" - : "=&r" (v) + : "=&r" (val) : "Ir" (CR_C) : "cc"); + /* Clear and invalidate all date from L1 data cache */ flush_cache_all(); + /* Switch the processor over to AMP mode out of SMP */ asm volatile( - /* - * Turn off coherency - */ - " mrc p15, 0, %0, c1, c0, 1\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (val) + : "Ir" (0x40) + : "cc"); + + isb(); + dsb(); + + wfi(); + +} + +static inline void pm_L2_logical_shutdown(u32 cpu) +{ + u32 val; + + asm volatile( + " mrc p15, 0, %0, c1, c0, 0\n" " bic %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 1\n" - : "=&r" (v) - : "Ir" (0x40) + " mcr p15, 0, %0, c1, c0, 0\n" + : "=&r" (val) + : "Ir" (CR_C) : "cc"); + + asm volatile( + /* + * Disable L2 prefetch + */ + " mrc p15, 1, %0, c15, c0, 3\n" + " orr %0, %0, %1\n" + " mcr p15, 1, %0, c15, c0, 3\n" + : "=&r" (val) + : "Ir" (0x400) + : "cc"); + + asm volatile( + " mrc p15, 1, %0, c15, c0, 4\n" + " orr %0, %0, %1\n" + " mcr p15, 1, %0, c15, c0, 4\n" + : "=&r" (val) + : "Ir" (0x1) + : "cc"); + + isb(); + dsb(); + + flush_cache_all(); + + /* Turn the DBG Double Lock quiet */ + asm volatile( + /* + * Turn Off the DBGOSDLR.DLK bit + */ + " mrc p14, 0, %0, c1, c3, 4\n" + " orr %0, %0, %1\n" + " mcr p14, 0, %0, c1, c3, 4\n" + : "=&r" (val) + : "Ir" (0x1) + : "cc"); + + /* Switch the processor over to AMP mode out of SMP */ + asm volatile( + " mrc p15, 0, %0, c1, c0, 1\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (val) + : "Ir" (0x40) + : "cc"); + + isb(); + dsb(); + + wfi(); +} + +#ifdef CONFIG_HOTPLUG_CPU_LOW_POWER +static inline void cpu_enter_lowpower_a15(void) +{ + unsigned int v; + + asm volatile( + " mrc p15, 0, %0, c1, c0, 0\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + : "=&r" (v) + : "Ir" (CR_C) + : "cc"); + + flush_cache_all(); + + asm volatile( + /* + * Turn off coherency + */ + " mrc p15, 0, %0, c1, c0, 1\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (0x40) + : "cc"); + isb(); dsb(); } @@ -54,21 +158,69 @@ static inline void cpu_leave_lowpower(void) unsigned int v; asm volatile( - "mrc p15, 0, %0, c1, c0, 0\n" - " orr %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - " mrc p15, 0, %0, c1, c0, 1\n" - " orr %0, %0, %2\n" - " mcr p15, 0, %0, c1, c0, 1\n" - : "=&r" (v) - : "Ir" (CR_C), "Ir" (0x40) - : "cc"); + "mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " orr %0, %0, %2\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (CR_C), "Ir" (0x40) + : "cc"); + isb(); + dsb(); } +static void __ref platform_do_lowpower(unsigned int cpu, int *spurious) +{ + int phys_cpu, cluster; + + /* + * there is no power-control hardware on this platform, so all + * we can do is put the core into WFI; this is safe as the calling + * code will have already disabled interrupts + */ + for (;;) { + wfi(); + + /* + * Convert the "cpu" variable to be compatible with the + * ARM MPIDR register format (CLUSTERID and CPUID): + * + * Bits: |11 10 9 8|7 6 5 4 3 2|1 0 + * | CLUSTER | Reserved |CPU + */ + phys_cpu = cpu_logical_map(cpu); + cluster = (phys_cpu / 4) << 8; + phys_cpu = cluster + (phys_cpu % 4); + + if (pen_release == phys_cpu) { + /* + * OK, proper wakeup, we're done + */ + break; + } + + /* + * Getting here, means that we have come out of WFI without + * having been woken up - this shouldn't happen + * + * Just note it happening - when we're woken, we can report + * its occurrence. + */ + (*spurious)++; + } +} +#endif int axxia_platform_cpu_kill(unsigned int cpu) { + +#ifdef CONFIG_HOTPLUG_CPU_COMPLETE_POWER_DOWN + get_cpu(); pm_cpu_shutdown(cpu); + put_cpu(); +#endif return 1; } @@ -80,33 +232,49 @@ int axxia_platform_cpu_kill(unsigned int cpu) void axxia_platform_cpu_die(unsigned int cpu) { +#ifdef CONFIG_HOTPLUG_CPU_COMPLETE_POWER_DOWN + bool last_cpu; - pm_data pm_request; - int rVal = 0; - bool lastCpu; - - pm_request.cpu = cpu; - pm_request.cluster = 0; - - - lastCpu = pm_cpu_last_of_cluster(cpu); - if (lastCpu) - rVal = pm_cpul2_logical_die(&pm_request); + last_cpu = pm_cpu_last_of_cluster(cpu); + if (last_cpu) + pm_L2_logical_shutdown(cpu); else - rVal = pm_cpu_logical_die(&pm_request); - if (rVal) - pr_err("CPU %d failed to die\n", cpu); + pm_cpu_logical_shutdown(cpu); for (;;) wfi(); +#else /* CPU low power mode */ + + int spurious = 0; + + /* + * we're ready for shutdown now, so do it + */ + cpu_enter_lowpower_a15(); + pm_in_progress[cpu] = true; + + platform_do_lowpower(cpu, &spurious); + + /* + * bring this CPU back into the world of cache + * coherency, and then restore interrupts + */ + cpu_leave_lowpower(); + + if (spurious) + pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); +#endif + } int platform_cpu_disable(unsigned int cpu) { + /* * we don't allow CPU 0 to be shutdown (it is still too special * e.g. clock tick interrupts) */ + return cpu == 0 ? -EPERM : 0; } diff --git a/arch/arm/mach-axxia/lsi_power_management.c b/arch/arm/mach-axxia/lsi_power_management.c index 9723054..ddc3c98 100644 --- a/arch/arm/mach-axxia/lsi_power_management.c +++ b/arch/arm/mach-axxia/lsi_power_management.c @@ -18,21 +18,21 @@ #include <linux/errno.h> #include <linux/smp.h> #include <linux/delay.h> +#include <linux/of_address.h> #include <asm/exception.h> #include <asm/cacheflush.h> #include <asm/smp_plat.h> #include <asm/cp15.h> #include "axxia.h" +#include <mach/axxia-gic.h> #include "lsi_power_management.h" #undef DEBUG_CPU_PM -#define SYSCON_PHYS_ADDR 0x002010030000ULL -#define DICKENS_PHYS_ADDR 0x2000000000 - #define PM_WAIT_TIME (10000) #define MAX_CLUSTER (4) +#define IPI_IRQ_MASK (0xFFFF) #define CHECK_BIT(var, pos) ((var) & (1 << (pos))) @@ -50,25 +50,67 @@ PORESET_CLUSTER1, PORESET_CLUSTER2, PORESET_CLUSTER3 }; -static u32 pm_cpu_powered_down; +static const u32 cluster_to_mask[MAX_CLUSTER] = { + IPI0_MASK, + IPI1_MASK, + IPI2_MASK, + IPI3_MASK +}; + +static const u32 ipi_register[MAX_IPI] = { + NCP_SYSCON_MASK_IPI0, + NCP_SYSCON_MASK_IPI1, + NCP_SYSCON_MASK_IPI2, + NCP_SYSCON_MASK_IPI3, + NCP_SYSCON_MASK_IPI4, + NCP_SYSCON_MASK_IPI5, + NCP_SYSCON_MASK_IPI6, + NCP_SYSCON_MASK_IPI7, + NCP_SYSCON_MASK_IPI8, + NCP_SYSCON_MASK_IPI9, + NCP_SYSCON_MASK_IPI10, + NCP_SYSCON_MASK_IPI11, + NCP_SYSCON_MASK_IPI12, + NCP_SYSCON_MASK_IPI13, + NCP_SYSCON_MASK_IPI14, + NCP_SYSCON_MASK_IPI15, + NCP_SYSCON_MASK_IPI16, + NCP_SYSCON_MASK_IPI17, + NCP_SYSCON_MASK_IPI18 +}; + +enum pm_error_code { + PM_ERR_DICKENS_IOREMAP = 200, + PM_ERR_DICKENS_SNOOP_DOMAIN, + PM_ERR_FAILED_PWR_DWN_RAM, + PM_ERR_FAILED_STAGE_1, + PM_ERR_ACK1_FAIL, + PM_ERR_RAM_ACK_FAIL, + PM_ERR_FAIL_L2ACK, + PM_ERR_FAIL_L2HSRAM +}; + +u32 pm_cpu_powered_down; + /*======================= LOCAL FUNCTIONS ==============================*/ -static void pm_set_bits_syscon_register(void __iomem *syscon, u32 reg, u32 data); -static void pm_clear_bits_syscon_register(void __iomem *syscon, u32 reg, u32 data); -static bool pm_test_for_bit_with_timeout(void __iomem *syscon, u32 reg, u32 bit); -static bool pm_wait_for_bit_clear_with_timeout(void __iomem *syscon, u32 reg, +static void pm_set_bits_syscon_register(u32 reg, u32 data); +static void pm_or_bits_syscon_register(u32 reg, u32 data); +static void pm_clear_bits_syscon_register(u32 reg, u32 data); +static bool pm_test_for_bit_with_timeout(u32 reg, u32 bit); +static bool pm_wait_for_bit_clear_with_timeout(u32 reg, u32 bit); static void pm_dickens_logical_shutdown(u32 cluster); static int pm_dickens_logical_powerup(u32 cluster); static int pm_cpu_physical_isolation_and_power_down(int cpu); static void pm_L2_isolation_and_power_down(int cluster); -static void __pm_cpu_shutdown(void *data); static int pm_cpu_physical_connection_and_power_up(int cpu); static int pm_L2_physical_connection_and_power_up(u32 cluster); static int pm_L2_logical_powerup(u32 cluster, u32 cpu); static bool pm_first_cpu_of_cluster(u32 cpu) { +#ifdef CONFIG_HOTPLUG_CPU_L2_POWER_DOWN u32 count = 0; switch (cpu) { case (0): @@ -127,11 +169,14 @@ static bool pm_first_cpu_of_cluster(u32 cpu) __LINE__); break; } +#endif return false; } bool pm_cpu_last_of_cluster(u32 cpu) { +#ifdef CONFIG_HOTPLUG_CPU_L2_POWER_DOWN + u32 count = 0; switch (cpu) { case (0): @@ -190,10 +235,16 @@ bool pm_cpu_last_of_cluster(u32 cpu) __LINE__); break; } +#endif return false; } -static void pm_set_bits_syscon_register(void __iomem *syscon, u32 reg, u32 data) +static void pm_set_bits_syscon_register(u32 reg, u32 data) +{ + writel(data, syscon + reg); +} + +static void pm_or_bits_syscon_register(u32 reg, u32 data) { u32 tmp; @@ -202,7 +253,8 @@ static void pm_set_bits_syscon_register(void __iomem *syscon, u32 reg, u32 data) writel(tmp, syscon + reg); } -static void pm_clear_bits_syscon_register(void __iomem *syscon, u32 reg, u32 data) + +static void pm_clear_bits_syscon_register(u32 reg, u32 data) { u32 tmp; @@ -211,7 +263,7 @@ static void pm_clear_bits_syscon_register(void __iomem *syscon, u32 reg, u32 dat writel(tmp, syscon + reg); } -static bool pm_test_for_bit_with_timeout(void __iomem *syscon, u32 reg, u32 bit) +static bool pm_test_for_bit_with_timeout(u32 reg, u32 bit) { u32 tmp = 0; @@ -230,8 +282,7 @@ static bool pm_test_for_bit_with_timeout(void __iomem *syscon, u32 reg, u32 bit) return true; } -static bool pm_wait_for_bit_clear_with_timeout(void __iomem *syscon, u32 reg, - u32 bit) +static bool pm_wait_for_bit_clear_with_timeout(u32 reg, u32 bit) { u32 cnt = 0; u32 tmp = 0; @@ -256,13 +307,6 @@ static void pm_dickens_logical_shutdown(u32 cluster) u32 bit; u32 bit_pos; int retries; - void __iomem *dickens; - - dickens = ioremap(DICKENS_PHYS_ADDR, SZ_4M); - if (dickens == NULL) { - pr_err("DICKENS: Failed to map the dickens registers\n"); - return; - } bit = (0x01 << cluster_to_node[cluster]); bit_pos = cluster_to_node[cluster]; @@ -283,7 +327,7 @@ static void pm_dickens_logical_shutdown(u32 cluster) if (0 == retries) { pr_err("DICKENS: Failed to clear the SNOOP main control. LOOP:%d reg: 0x%x\n", i, status); - goto dickens_power_down; + return; } @@ -303,14 +347,8 @@ static void pm_dickens_logical_shutdown(u32 cluster) if (0 == retries) { pr_err("DICKENS: failed to set DOMAIN OFFSET Reg=0x%x\n", status); - goto dickens_power_down; - + return; } - -dickens_power_down: - iounmap(dickens); - - return; } static int pm_dickens_logical_powerup(u32 cluster) @@ -322,12 +360,6 @@ static int pm_dickens_logical_powerup(u32 cluster) int retries; int rval = 0; - void __iomem *dickens = ioremap(DICKENS_PHYS_ADDR, SZ_4M); - if (dickens == NULL) { - pr_err("Failed to map dickens registers\n"); - return -EINVAL; - } - bit = (0x01 << cluster_to_node[cluster]); bit_pos = cluster_to_node[cluster]; @@ -347,8 +379,7 @@ static int pm_dickens_logical_powerup(u32 cluster) if (0 == retries) { pr_err("DICKENS: Failed on the SNOOP DONAIN\n"); - rval = -EINVAL; - goto dickens_power_up; + return -PM_ERR_DICKENS_SNOOP_DOMAIN; } } @@ -367,241 +398,143 @@ static int pm_dickens_logical_powerup(u32 cluster) } while ((0 < --retries) && !CHECK_BIT(status, bit_pos)); if (0 == retries) { - pr_err("DICKENS: Failed on the SNOOP DONAIN\n"); - rval = -EINVAL; - goto dickens_power_up; + pr_err("DICKENS: Failed on the SNOOP DONAIN CTL SET\n"); + return -PM_ERR_DICKENS_SNOOP_DOMAIN; } -dickens_power_up: - iounmap(dickens); - return rval; } -static void __pm_cpu_shutdown(void *data) +static void pm_disable_ipi_interrupts(u32 cpu) +{ + pm_clear_bits_syscon_register(ipi_register[cpu], IPI_IRQ_MASK); +} + +static void pm_enable_ipi_interrupts(u32 cpu) +{ + + u32 i; + u32 powered_on_cpu = (~(pm_cpu_powered_down) & IPI_IRQ_MASK); + + pm_set_bits_syscon_register(ipi_register[cpu], powered_on_cpu); + + for (i = 0; i < MAX_CPUS; i++) { + if ((1 << i) & powered_on_cpu) + pm_or_bits_syscon_register(ipi_register[i], (1 << cpu)); + } + + return; +} + +bool pm_cpu_active(u32 cpu) +{ + + bool success = false; + u32 reg; + + reg = readl(syscon + NCP_SYSCON_PWR_QACTIVE); + if (reg & (1 << cpu)) + success = true; + + return success; + +} + +void pm_cpu_shutdown(u32 cpu) { - pm_data *pm_request = (pm_data *)data; - void __iomem *syscon; bool success; - u32 cluster_mask = (0x01 << pm_request->cluster); + u32 reqcpu = cpu_logical_map(cpu); + u32 cluster = reqcpu / CORES_PER_CLUSTER; + u32 cluster_mask = (0x01 << cluster); bool last_cpu; int rval = 0; + /* Check to see if the cpu is powered up */ + if (pm_cpu_powered_down & (1 << reqcpu)) { + pr_err("CPU %d is already powered off - %s:%d\n", cpu, __FILE__, __LINE__); + return; + } + /* * Is this the last cpu of a cluster then turn off the L2 cache * along with the CPU. */ - last_cpu = pm_cpu_last_of_cluster(pm_request->cpu); + last_cpu = pm_cpu_last_of_cluster(reqcpu); if (last_cpu) { + /* Disable all the interrupts to the cluster gic */ + pm_or_bits_syscon_register(NCP_SYSCON_GIC_DISABLE, cluster_mask); + /* Remove the cluster from the Dickens coherency domain */ - pm_dickens_logical_shutdown(pm_request->cluster); + pm_dickens_logical_shutdown(cluster); /* Power down the cpu */ - pm_cpu_physical_isolation_and_power_down(pm_request->cpu); - - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return; - -#if 0 - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_CSYSREQ_TS, cluster_mask); - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_CACTIVE_TS, pm_request->cluster); - if (!success) { - pr_err( - "Failed to keep other cluster TS going on cluster %d: %s-%d\n", - pm_request->cluster, __FILE__, __LINE__); - iounmap(syscon); - return; - } + pm_cpu_physical_isolation_and_power_down(reqcpu); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_CSYSREQ_ATB, cluster_mask); - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_CACTIVE_ATB, pm_request->cluster); - if (!success) { - pr_err( - "Failed to keep other cluster ATB going on cluster %d: %s-%d\n", - pm_request->cluster, __FILE__, __LINE__); - iounmap(syscon); - return; - } - - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_CSYSREQ_APB, cluster_mask); - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_CACTIVE_APB, pm_request->cluster); - if (!success) { - pr_err( - "Failed to keep other cluster APB going on cluster %d: %s-%d\n", - pm_request->cluster, __FILE__, __LINE__); - iounmap(syscon); - return; - } -#endif - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_CSYSREQ_CNT, cluster_mask); - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_CACTIVE_CNT, pm_request->cluster); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_CSYSREQ_CNT, cluster_mask); + success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_CACTIVE_CNT, cluster); if (!success) { pr_err( "Failed to keep other cluster count going on cluster %d: %s-%d\n", - pm_request->cluster, __FILE__, __LINE__); - iounmap(syscon); - return; + cluster, __FILE__, __LINE__); + goto pm_shutdown_exit; } /* Turn off the ACE */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_ACEPWRDNRQ, cluster_mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_ACEPWRDNRQ, cluster_mask); /* Wait for ACE to complete power off */ - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NACEPWRDNACK, pm_request->cluster); + success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NACEPWRDNACK, cluster); if (!success) { pr_err("Failed to power off ACE on cluster %d: %s-%d\n", - pm_request->cluster, __FILE__, __LINE__); - iounmap(syscon); - return; + cluster, __FILE__, __LINE__); + goto pm_shutdown_exit; } /* Isolate the cluster */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_ISOLATEL2MISC, cluster_mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_ISOLATEL2MISC, cluster_mask); /* Wait for WFI L2 to go to standby */ - success = pm_test_for_bit_with_timeout(syscon, NCP_SYSCON_PWR_STANDBYWFIL2, pm_request->cluster); + success = pm_test_for_bit_with_timeout(NCP_SYSCON_PWR_STANDBYWFIL2, cluster); if (!success) { pr_err("Failed to enter L2 WFI on cluster %d: %s-%d\n", - pm_request->cluster, __FILE__, __LINE__); - iounmap(syscon); - return; + cluster, __FILE__, __LINE__); + goto pm_shutdown_exit; } - iounmap(syscon); - /* Power off the L2 */ - pm_L2_isolation_and_power_down(pm_request->cluster); + pm_L2_isolation_and_power_down(cluster); if (rval == 0) { - pr_info("CPU %d is powered down with cluster: %d\n", pm_request->cpu, pm_request->cluster); - pm_cpu_powered_down |= (1 << pm_request->cpu); + pr_info("CPU %d is powered down with cluster: %d\n", reqcpu, cluster); + pm_cpu_powered_down |= (1 << reqcpu); } else - pr_err("CPU %d failed to power down\n", pm_request->cpu); + pr_err("CPU %d failed to power down\n", reqcpu); } else { - rval = pm_cpu_physical_isolation_and_power_down(pm_request->cpu); + rval = pm_cpu_physical_isolation_and_power_down(reqcpu); if (rval == 0) - pm_cpu_powered_down |= (1 << pm_request->cpu); + pm_cpu_powered_down |= (1 << reqcpu); else - pr_err("CPU %d failed to power down\n", pm_request->cpu); + pr_err("CPU %d failed to power down\n", reqcpu); } +pm_shutdown_exit: return; } - -int pm_cpu_logical_die(pm_data *pm_request) -{ - void __iomem *syscon; - bool success; - - smp_call_function_single(pm_request->cpu, pm_cpu_logical_shutdown, (void *)pm_request, 1); - - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; - - /* Wait for the cpu to enter wfi */ - success = pm_test_for_bit_with_timeout(syscon, NCP_SYSCON_PWR_STANDBYWFI, pm_request->cpu); - if (!success) { - pr_err("Failed to enter WFI mode on cpu %d: %s-%d\n", - pm_request->cpu, __FILE__, __LINE__); - iounmap(syscon); - return -EINVAL; - } - - iounmap(syscon); - return 0; -} - -int pm_cpul2_logical_die(pm_data *pm_request) -{ - void __iomem *syscon; - bool success; - - smp_call_function_single(pm_request->cpu, pm_L2_logical_shutdown, (void *)pm_request, 1); - - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; - - /* Wait for the cpu to enter wfi */ - success = pm_test_for_bit_with_timeout(syscon, NCP_SYSCON_PWR_STANDBYWFI, pm_request->cpu); - if (!success) { - pr_err("Failed to enter WFI mode on cpu %d: %s-%d\n", - pm_request->cpu, __FILE__, __LINE__); - iounmap(syscon); - return -EINVAL; - } - - iounmap(syscon); - return 0; -} - -void pm_cpu_shutdown(u32 cpu) -{ - - pm_data pm_request; - - u32 pcpu = cpu_logical_map(smp_processor_id()); - u32 rcpu = cpumask_any_and(cpu_present_mask, cpu_online_mask); - u32 reqcpu = cpu_logical_map(cpu); - - /* Check to see if the cpu is powered up */ - if (pm_cpu_powered_down & (1 << cpu)) { - pr_err("CPU %d is already powered off - %s:%d\n", cpu, __FILE__, __LINE__); - return; - } - /* - * Is this the last cpu to be powered off, then don't - * allow the power to be shut off. - */ - if (cpu == 0) { - pr_err("Cannot turn off cpu 0 - %s:%d\n", __FILE__, __LINE__); - return; - } - - /* - * Is this process on the requested cpu to power down - * then send it to another cpu for processing - */ - pm_request.cpu = cpu; - pm_request.cluster = reqcpu / CORES_PER_CLUSTER; - - if (pcpu == cpu) - smp_call_function_single(rcpu, __pm_cpu_shutdown, (void *)&pm_request, 0); - else - __pm_cpu_shutdown(&pm_request); - -} - int pm_cpu_powerup(u32 cpu) { bool first_cpu; int rval = 0; - void __iomem *syscon = NULL; u32 cpu_mask = (0x01 << cpu); u32 reqcpu = cpu_logical_map(cpu); u32 cluster = reqcpu / CORES_PER_CLUSTER; - - /* Hold the CPU in reset */ - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; - - /* - * The key value has to be written before the CPU RST can be written. - */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWRUP_CPU_RST, cpu_mask); - - iounmap(syscon); + u32 cluster_mask = (0x01 << cluster); /* * Is this the first cpu of a cluster to come back on? @@ -610,13 +543,21 @@ int pm_cpu_powerup(u32 cpu) first_cpu = pm_first_cpu_of_cluster(cpu); if (first_cpu) { - rval = pm_L2_logical_powerup(cluster, cpu); - if (rval) { + if (rval) + pr_info("CPU %d is powered up with cluster: %d\n", reqcpu, cluster); + else { pr_err("CPU: Failed the logical L2 power up\n"); - return rval; + goto pm_power_up; } cluster_power_up[cluster] = true; + pm_clear_bits_syscon_register(NCP_SYSCON_GIC_DISABLE, cluster_mask); + + + } else { + /* Set the CPU into reset */ + pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_or_bits_syscon_register(NCP_SYSCON_PWRUP_CPU_RST, cpu_mask); } @@ -630,33 +571,21 @@ int pm_cpu_powerup(u32 cpu) goto pm_power_up; } - udelay(16); - - /* Clear the CPU from reset and let it go */ - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; - /* * The key value must be written before the CPU RST can be written. */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWRUP_CPU_RST, cpu_mask); - - /* - * The key value must be written before HOLD CPU can be written. - */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_HOLD_CPU, cpu_mask); + pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_clear_bits_syscon_register(NCP_SYSCON_PWRUP_CPU_RST, cpu_mask); /* * Clear the powered down mask */ pm_cpu_powered_down &= ~(1 << cpu); + /* Enable the CPU IPI */ + pm_enable_ipi_interrupts(cpu); pm_power_up: - iounmap(syscon); return rval; } @@ -665,241 +594,177 @@ unsigned long pm_get_powered_down_cpu(void) return pm_cpu_powered_down; } -void pm_cpu_logical_shutdown(void *data) -{ - u32 val; - asm volatile( - " mrc p15, 1, %0, c9, c0, 2\n" - : "=&r" (val) - : "Ir" (0x1) - : "cc"); +inline void pm_cpu_logical_powerup(void) +{ + unsigned int v; asm volatile( - " mrc p15, 0, %0, c1, c0, 0\n" - " bic %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - : "=&r" (val) - : "Ir" (CR_C) - : "cc"); + " mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %2\n" + " mcr p15, 0, %0, c1, c0, 0\n" + : "=&r" (v) + : "Ir" (CR_C), "Ir" (CR_I) + : "cc"); - /* Clear and invalidate all date from L1 data cache */ - flush_cache_all(); + /* + * Iniitalize the ACTLR2 register (all cores). + */ - /* Switch the processor over to AMP mode out of SMP */ asm volatile( - " mrc p15, 0, %0, c1, c0, 1\n" - " bic %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 1\n" - : "=&r" (val) - : "Ir" (0x40) - : "cc"); + " mrc p15, 1, %0, c15, c0, 4\n" + " bic %0, %0, %1\n" + " mcr p15, 1, %0, c15, c0, 4\n" + : "=&r" (v) + : "Ir" (0x1) + : "cc"); isb(); dsb(); - - wfi(); - - return; - } -void pm_cpu_logical_powerup(void) +inline void pm_cluster_logical_powerup(void) { unsigned int v; + /* + * Initialize the L2CTLR register (primary core in each cluster). + */ asm volatile( - " mrc p15, 0, %0, c1, c0, 0\n" + " mrc p15, 1, %0, c9, c0, 2\n" " orr %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - " mrc p15, 0, %0, c1, c0, 0\n" " orr %0, %0, %2\n" - " mcr p15, 0, %0, c1, c0, 0\n" - " mrc p15, 0, %0, c1, c0, 1\n" - " orr %0, %0, %3\n" - " mcr p15, 0, %0, c1, c0, 1\n" + " mcr p15, 1, %0, c9, c0, 2" : "=&r" (v) - : "Ir" (CR_C), "Ir" (CR_I), "Ir" (0x40) + : "Ir" (0x01), "Ir" (0x1 << 21) : "cc"); + isb(); + dsb(); + /* + * Initialize the L2ACTLR register (primary core in each cluster). + */ asm volatile( - " mrc p15, 1, %0, c9, c0, 2\n" - : "=&r" (v) - : "Ir" (0x1) - : "cc"); + " mrc p15, 1, r0, c15, c0, 0\n" + " orr %0, %0, %1\n" + " orr %0, %0, %2\n" + " orr %0, %0, %3\n" + " orr %0, %0, %4\n" + " orr %0, %0, %5\n" + " mcr p15, 1, %0, c15, c0, 0" + : "=&r" (v) + : "Ir" (0x1 << 3), "Ir" (0x1 << 7), "Ir" (0x1 << 12), "Ir" (0x1 << 13), "Ir" (0x1 << 14) + : "cc"); + isb(); + dsb(); } static int pm_cpu_physical_isolation_and_power_down(int cpu) { - void __iomem *syscon; + int rval = 0; bool success; u32 mask = (0x01 << cpu); - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; + /* Disable the CPU IPI */ + pm_disable_ipi_interrupts(cpu); /* Initiate power down of the CPU's HS Rams */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPURAM, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPURAM, mask); /* Wait until the RAM power down is complete */ - success = pm_test_for_bit_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPCPURAM_ACK, cpu); + success = pm_test_for_bit_with_timeout(NCP_SYSCON_PWR_NPWRUPCPURAM_ACK, cpu); if (!success) { - rval = -EINVAL; + rval = -PM_ERR_FAILED_PWR_DWN_RAM; pr_err("CPU: Failed to power down CPU RAM\n"); goto power_down_cleanup; } /* Activate the CPU's isolation clamps */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_ISOLATECPU, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_ISOLATECPU, mask); /* Initiate power down of the CPU logic */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPUSTG2, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPUSTG2, mask); - udelay(10); + udelay(16); /* Continue power down of the CPU logic */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPUSTG1, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPUSTG1, mask); - success = pm_test_for_bit_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPCPUSTG1_ACK, cpu); + success = pm_test_for_bit_with_timeout(NCP_SYSCON_PWR_NPWRUPCPUSTG1_ACK, cpu); if (!success) { - rval = -EINVAL; + rval = -PM_ERR_FAILED_STAGE_1; pr_err("CPU: Failed to power down stage 1 cpu\n"); goto power_down_cleanup; } power_down_cleanup: - iounmap(syscon); + return rval; } static int pm_cpu_physical_connection_and_power_up(int cpu) { int rval = 0; - void __iomem *syscon; + bool success; u32 mask = (0x01 << cpu); - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; - /* Initiate power up of the CPU */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPUSTG1, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPUSTG1, mask); /* Wait until CPU logic power is compete */ - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPCPUSTG1_ACK, cpu); + success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NPWRUPCPUSTG1_ACK, cpu); if (!success) { - rval = -EINVAL; + rval = -PM_ERR_ACK1_FAIL; pr_err("CPU: Failed to get ACK from power down stage 1\n"); goto power_up_cleanup; } /* Continue stage 2 power up of the CPU*/ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPUSTG2, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPUSTG2, mask); - udelay(10); + udelay(16); /* Initiate power up of HS Rams */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPURAM, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPURAM, mask); /* Wait until the RAM power up is complete */ - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPCPURAM_ACK, cpu); + success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NPWRUPCPURAM_ACK, cpu); if (!success) { - rval = -EINVAL; + rval = -PM_ERR_RAM_ACK_FAIL; pr_err("CPU: Failed to get ACK of power power up\n"); goto power_up_cleanup; } /* Release the CPU's isolation clamps */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_ISOLATECPU, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_ISOLATECPU, mask); + + udelay(16); power_up_cleanup: - iounmap(syscon); + return rval; } /*========================================== L2 FUNCTIONS ========================================*/ -void pm_L2_logical_shutdown(void *data) -{ - u32 val; - - - asm volatile( - " mrc p15, 0, %0, c1, c0, 0\n" - " bic %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - : "=&r" (val) - : "Ir" (CR_C) - : "cc"); - - - asm volatile( - /* - * Disable L2 prefetch - */ - " mrc p15, 1, %0, c15, c0, 3\n" - " orr %0, %0, %1\n" - " mcr p15, 1, %0, c15, c0, 3\n" - : "=&r" (val) - : "Ir" (0x400) - : "cc"); - - isb(); - dsb(); - - /* Clear and invalidate all L1 and L2 data cache */ - flush_cache_all(); - - - /* Turn the DBG Double Lock quiet */ - asm volatile( - /* - * Turn Off the DBGOSDLR.DLK bit - */ - " mrc p14, 0, %0, c1, c3, 4\n" - " orr %0, %0, %1\n" - " mcr p14, 0, %0, c1, c3, 4\n" - : "=&r" (val) - : "Ir" (0x1) - : "cc"); - - /* Switch the processor over to AMP mode out of SMP */ - asm volatile( - " mrc p15, 0, %0, c1, c0, 1\n" - " bic %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 1\n" - : "=&r" (val) - : "Ir" (0x40) - : "cc"); - - isb(); - dsb(); - - wfi(); - return; -} - static void pm_L2_isolation_and_power_down(int cluster) { - void __iomem *syscon; - u32 mask = (0x1 << cluster); - - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return; + u32 mask = (0x1 << cluster); /* Enable the chip select for the cluster */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_CHIPSELECTEN, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_CHIPSELECTEN, mask); /* Disable the hsram */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2HSRAM, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2HSRAM, mask); switch (cluster) { case (0): @@ -920,11 +785,11 @@ static void pm_L2_isolation_and_power_down(int cluster) NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_BANK3_MASK); udelay(20); #else - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif @@ -948,11 +813,11 @@ static void pm_L2_isolation_and_power_down(int cluster) NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_BANK3_MASK); udelay(20); #else - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -975,11 +840,11 @@ static void pm_L2_isolation_and_power_down(int cluster) NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_BANK3_MASK); udelay(20); #else - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -1002,11 +867,11 @@ static void pm_L2_isolation_and_power_down(int cluster) NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_BANK3_MASK); udelay(20); #else - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -1016,51 +881,45 @@ static void pm_L2_isolation_and_power_down(int cluster) } /* Power down stage 2 */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2LGCSTG2, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2LGCSTG2, mask); /* Power down stage 1 */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2LGCSTG1, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2LGCSTG1, mask); - - iounmap(syscon); } static int pm_L2_physical_connection_and_power_up(u32 cluster) { - void __iomem *syscon; + bool success; u32 mask = (0x1 << cluster); int rval = 0; - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; - /* Power up stage 1 */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2LGCSTG1, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2LGCSTG1, mask); /* Wait for the stage 1 power up to complete */ - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPL2LGCSTG1_ACK, cluster); + success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NPWRUPL2LGCSTG1_ACK, cluster); if (!success) { pr_err("CPU: Failed to ack the L2 Stage 1 Power up\n"); - rval = -EINVAL; + rval = -PM_ERR_FAIL_L2ACK; goto power_up_l2_cleanup; } /* Power on stage 2 */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2LGCSTG2, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2LGCSTG2, mask); /* Set the chip select */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_CHIPSELECTEN, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_CHIPSELECTEN, mask); - /* Power up the snoop ramram */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2HSRAM, mask); + /* Power up the snoop ram */ + pm_or_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2HSRAM, mask); /* Wait for the stage 1 power up to complete */ - success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPL2HSRAM_ACK, cluster); + success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NPWRUPL2HSRAM_ACK, cluster); if (!success) { pr_err("CPU: failed to get the HSRAM power up ACK\n"); - rval = -EINVAL; + rval = -PM_ERR_FAIL_L2HSRAM; goto power_up_l2_cleanup; } @@ -1083,11 +942,11 @@ static int pm_L2_physical_connection_and_power_up(u32 cluster) NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_BANK3_MASK); udelay(20); #else - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif @@ -1098,7 +957,7 @@ static int pm_L2_physical_connection_and_power_up(u32 cluster) pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_BANK0_MASK); - udelay(20); + udelay(20); pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_BANK1_LS_MASK); pm_set_bits_syscon_register(syscon, @@ -1111,11 +970,11 @@ static int pm_L2_physical_connection_and_power_up(u32 cluster) NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_BANK3_MASK); udelay(20); #else - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -1138,11 +997,11 @@ static int pm_L2_physical_connection_and_power_up(u32 cluster) NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_BANK3_MASK); udelay(20); #else - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -1165,11 +1024,11 @@ static int pm_L2_physical_connection_and_power_up(u32 cluster) NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_BANK3_MASK); udelay(20); #else - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -1179,86 +1038,82 @@ static int pm_L2_physical_connection_and_power_up(u32 cluster) } /* Clear the chip select */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_CHIPSELECTEN, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_CHIPSELECTEN, mask); /* Release the isolation clamps */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_ISOLATEL2MISC, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_ISOLATEL2MISC, mask); /* Turn the ACE bridge power on*/ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_ACEPWRDNRQ, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_ACEPWRDNRQ, mask); power_up_l2_cleanup: - - iounmap(syscon); - return rval; } static int pm_L2_logical_powerup(u32 cluster, u32 cpu) { - void __iomem *syscon; u32 mask = (0x1 << cluster); - u32 cpu_mask = (0x1 << cpu); int rval = 0; + u32 cluster_mask; - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; + if (cluster == 0) + cluster_mask = 0xe; + else + cluster_mask = 0xf << (cluster * 4); /* put the cluster into a cpu hold */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_RESET_AXIS, + pm_or_bits_syscon_register(NCP_SYSCON_RESET_AXIS, cluster_to_poreset[cluster]); /* - * Write the key so the reset cpu register can be written to. + * The key value has to be written before the CPU RST can be written. */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWRUP_CPU_RST, cpu_mask); + pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_or_bits_syscon_register(NCP_SYSCON_PWRUP_CPU_RST, cluster_mask); /* Hold the chip debug cluster */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_HOLD_DBG, mask); + pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_or_bits_syscon_register(NCP_SYSCON_HOLD_DBG, mask); /* Hold the L2 cluster */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_set_bits_syscon_register(syscon, NCP_SYSCON_HOLD_L2, mask); + pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_or_bits_syscon_register(NCP_SYSCON_HOLD_L2, mask); - iounmap(syscon); /* Cluster physical power up */ rval = pm_L2_physical_connection_and_power_up(cluster); + if (rval) + goto exit_pm_L2_logical_powerup; udelay(16); - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return -EINVAL; - /* take the cluster out of a cpu hold */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_RESET_AXIS, + pm_clear_bits_syscon_register(NCP_SYSCON_RESET_AXIS, cluster_to_poreset[cluster]); udelay(64); /* Enable the system counter */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_CSYSREQ_CNT, mask); + pm_or_bits_syscon_register(NCP_SYSCON_PWR_CSYSREQ_CNT, mask); /* Release the L2 cluster */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_HOLD_L2, mask); + pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_clear_bits_syscon_register(NCP_SYSCON_HOLD_L2, mask); /* Release the chip debug cluster */ - pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_HOLD_DBG, mask); - + pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_clear_bits_syscon_register(NCP_SYSCON_HOLD_DBG, mask); + /* Power up the dickens */ rval = pm_dickens_logical_powerup(cluster); + if (rval) + goto exit_pm_L2_logical_powerup; /* start L2 */ - pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_ACINACTM, mask); + pm_clear_bits_syscon_register(NCP_SYSCON_PWR_ACINACTM, mask); - iounmap(syscon); +exit_pm_L2_logical_powerup: return rval; @@ -1270,12 +1125,6 @@ void pm_debug_read_pwr_registers(void) { u32 reg; - void __iomem *syscon; - - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return; - reg = readl(syscon + 0x1400); pr_err("NCP_SYSCON_PWR_CLKEN: 0x%x\n", reg); reg = readl(syscon + NCP_SYSCON_PWR_ACINACTM); @@ -1444,7 +1293,6 @@ void pm_debug_read_pwr_registers(void) #endif - iounmap(syscon); } @@ -1452,11 +1300,7 @@ void pm_dump_L2_registers(void) { u32 reg; - void __iomem *syscon; - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); - if (WARN_ON(!syscon)) - return; reg = readl(syscon + 0x1580); pr_err("NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2: 0x%x\n", reg); reg = readl(syscon + 0x1584); @@ -1482,23 +1326,15 @@ void pm_dump_L2_registers(void) reg = readl(syscon + 0x15ac); pr_err("NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0: 0x%x\n", reg); - iounmap(syscon); -} + void pm_dump_dickens(void) { - void __iomem *dickens; u32 status; u32 i; - dickens = ioremap(DICKENS_PHYS_ADDR, SZ_4M); - if (dickens == NULL) { - pr_err("DICKENS: Failed to map the dickens registers\n"); - return; - } - for (i = 0; i < DKN_HNF_TOTAL_NODES; ++i) { status = readl( dickens + (0x10000 * (DKN_HNF_NODE_ID + i)) @@ -1512,11 +1348,6 @@ void pm_dump_dickens(void) + DKN_MN_DVM_DOMAIN_CTL); pr_err("DKN_MN_DVM_DOMAIN_CTL: 0x%x\n", status); - - - iounmap(dickens); - - } #endif diff --git a/arch/arm/mach-axxia/lsi_power_management.h b/arch/arm/mach-axxia/lsi_power_management.h index 4cb6d1f..ef70af3 100644 --- a/arch/arm/mach-axxia/lsi_power_management.h +++ b/arch/arm/mach-axxia/lsi_power_management.h @@ -150,11 +150,19 @@ #define PORESET_CLUSTER2 (0x40000) #define PORESET_CLUSTER3 (0x80000) +/* IPI Masks */ +#define IPI0_MASK (0x1111) +#define IPI1_MASK (0x2222) +#define IPI2_MASK (0x4444) +#define IPI3_MASK (0x8888) + /* SYSCON KEY Value */ #define VALID_KEY_VALUE (0xAB) #define MAX_NUM_CLUSTERS (4) #define CORES_PER_CLUSTER (4) +#define MAX_IPI (19) +#define MAX_CPUS (MAX_NUM_CLUSTERS * CORES_PER_CLUSTER) typedef struct { u32 cpu; @@ -166,18 +174,19 @@ void pm_cpu_shutdown(u32 cpu); int pm_cpu_powerup(u32 cpu); void pm_debug_read_pwr_registers(void); void pm_dump_L2_registers(void); -void pm_cpu_logical_shutdown(void *data); int pm_cpu_logical_die(pm_data *pm_request); int pm_cpul2_logical_die(pm_data *pm_request); unsigned long pm_get_powered_down_cpu(void); bool pm_cpu_last_of_cluster(u32 cpu); -void pm_L2_logical_shutdown(void *data); void pm_dump_dickens(void); void pm_init_cpu(u32 cpu); void pm_cpu_logical_powerup(void); - +void pm_cluster_logical_powerup(void); +bool pm_cpu_active(u32 cpu); +void pm_init_syscon(void); extern bool pm_in_progress[]; extern bool cluster_power_up[]; +extern u32 pm_cpu_powered_down; #endif /* LSI_POWER_MANAGEMENT_H_ */ diff --git a/arch/arm/mach-axxia/platsmp.c b/arch/arm/mach-axxia/platsmp.c index d453cb9..6472734 100644 --- a/arch/arm/mach-axxia/platsmp.c +++ b/arch/arm/mach-axxia/platsmp.c @@ -26,13 +26,15 @@ #include "lsi_power_management.h" #include <mach/axxia-gic.h> -extern void axxia_secondary_startup(void); - #define SYSCON_PHYS_ADDR 0x002010030000ULL +#define DICKENS_PHYS_ADDR 0x2000000000 static int __cpuinitdata wfe_fixup; static int wfe_available; +void __iomem *syscon; +void __iomem *dickens; + inline void __axxia_arch_wfe(void) { @@ -60,7 +62,7 @@ static void __init check_fixup_sev(void __iomem *syscon) pr_info("axxia: Cross-cluster SEV fixup: %s\n", wfe_fixup ? "yes" : "no"); } -static void __init do_fixup_sev(void) +static void do_fixup_sev(void) { u32 tmp; @@ -77,7 +79,7 @@ static void __init do_fixup_sev(void) * observers, irrespective of whether they're taking part in coherency * or not. This is necessary for the hotplug code to work reliably. */ -static void __cpuinit write_pen_release(int val) +static void write_pen_release(int val) { pen_release = val; smp_wmb(); @@ -87,26 +89,37 @@ static void __cpuinit write_pen_release(int val) static DEFINE_RAW_SPINLOCK(boot_lock); -void __cpuinit axxia_secondary_init(unsigned int cpu) +void axxia_secondary_init(unsigned int cpu) { - int phys_cpu, cluster; + int phys_cpu; + int phys_cluster; phys_cpu = cpu_logical_map(cpu); - cluster = (phys_cpu / 4) << 8; + phys_cluster = phys_cpu / 4; /* * Only execute this when powering up a cpu for hotplug. */ - if (!pm_in_progress[cpu]) { + if (!pm_in_progress[phys_cpu]) { /* Fixup for cross-cluster SEV */ do_fixup_sev(); axxia_gic_secondary_init(); } else { - axxia_gic_secondary_init(); + +#ifdef CONFIG_HOTPLUG_CPU_COMPLETE_POWER_DOWN + if (cluster_power_up[phys_cluster]) + pm_cluster_logical_powerup(); pm_cpu_logical_powerup(); - pm_in_progress[cpu] = false; - cluster_power_up[cluster] = false; +#endif + get_cpu(); + axxia_gic_secondary_init(); + put_cpu(); + +#ifdef CONFIG_HOTPLUG_CPU_COMPLETE_POWER_DOWN + cluster_power_up[phys_cluster] = false; + pm_in_progress[phys_cpu] = false; +#endif } /* @@ -122,13 +135,14 @@ void __cpuinit axxia_secondary_init(unsigned int cpu) _raw_spin_unlock(&boot_lock); } -int __cpuinit axxia_boot_secondary(unsigned int cpu, struct task_struct *idle) +int axxia_boot_secondary(unsigned int cpu, struct task_struct *idle) { int phys_cpu, cluster; unsigned long timeout; unsigned long powered_down_cpu; - int rVal = 0; + u32 i; + u32 dummy; /* @@ -142,14 +156,8 @@ int __cpuinit axxia_boot_secondary(unsigned int cpu, struct task_struct *idle) powered_down_cpu = pm_get_powered_down_cpu(); if (powered_down_cpu & (1 << phys_cpu)) { - pm_in_progress[cpu] = true; - - rVal = pm_cpu_powerup(phys_cpu); - if (rVal) { - _raw_spin_unlock(&boot_lock); - return rVal; - } - + pm_in_progress[phys_cpu] = true; + pm_cpu_powerup(phys_cpu); } /* @@ -170,7 +178,6 @@ int __cpuinit axxia_boot_secondary(unsigned int cpu, struct task_struct *idle) * Bits: |11 10 9 8|7 6 5 4 3 2|1 0 * | CLUSTER | Reserved |CPU */ - phys_cpu = cpu_logical_map(cpu); cluster = (phys_cpu / 4) << 8; phys_cpu = cluster + (phys_cpu % 4); @@ -180,14 +187,18 @@ int __cpuinit axxia_boot_secondary(unsigned int cpu, struct task_struct *idle) /* Send a wakeup IPI to get the idled cpu out of WFI state */ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + /* Wait for so long, then give up if nothing happens ... */ timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { smp_rmb(); + if (pen_release == -1) break; - udelay(10); + /* Wait 10 cycles */ + for (i = 0; i < 10; i++) + dummy = i; } /* @@ -216,7 +227,6 @@ static __init struct device_node *get_cpu_node(int cpu) static void __init axxia_smp_prepare_cpus(unsigned int max_cpus) { - void __iomem *syscon; int cpu_count = 0; int cpu; @@ -224,6 +234,10 @@ static void __init axxia_smp_prepare_cpus(unsigned int max_cpus) if (WARN_ON(!syscon)) return; + dickens = ioremap(DICKENS_PHYS_ADDR, SZ_4M); + if (WARN_ON(!dickens)) + return; + check_fixup_sev(syscon); do_fixup_sev(); @@ -289,8 +303,6 @@ static void __init axxia_smp_prepare_cpus(unsigned int max_cpus) iounmap(release_virt); } } - - iounmap(syscon); } struct smp_operations axxia_smp_ops __initdata = { -- 1.7.9.5 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto