From: Magnus Karlsson <magnus.karls...@intel.com> This is a port of commit da6bb36cec919273d92d00b9d49f26723fde32e1 from linux-yocto-3.10, branch standard/axxia/base, that reverts b8dd1bdee59fd5dd8cdc038d802a3a68400066a6 from the same branch.
Signed-off-by: Magnus Karlsson <magnus.karls...@intel.com> --- arch/arm/Kconfig | 37 -- arch/arm/mach-axxia/axxia-gic.c | 327 +++---------- arch/arm/mach-axxia/axxia_circular_queue.c | 63 --- arch/arm/mach-axxia/axxia_circular_queue.h | 30 -- arch/arm/mach-axxia/hotplug.c | 238 ++------- arch/arm/mach-axxia/include/mach/axxia-gic.h | 3 +- arch/arm/mach-axxia/lsi_power_management.c | 673 ++++++++++++++++---------- arch/arm/mach-axxia/lsi_power_management.h | 6 +- arch/arm/mach-axxia/platsmp.c | 27 +- 9 files changed, 537 insertions(+), 867 deletions(-) delete mode 100644 arch/arm/mach-axxia/axxia_circular_queue.c delete mode 100644 arch/arm/mach-axxia/axxia_circular_queue.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 99ff039..fdfe745 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1651,8 +1651,6 @@ 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 @@ -1660,41 +1658,6 @@ 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-gic.c b/arch/arm/mach-axxia/axxia-gic.c index b2a459d..9d3ac12 100644 --- a/arch/arm/mach-axxia/axxia-gic.c +++ b/arch/arm/mach-axxia/axxia-gic.c @@ -32,15 +32,13 @@ * As such, the enable set/clear, pending set/clear and active bit * registers are banked per-cpu for these sources. */ + #include <linux/module.h> #include <linux/io.h> #include <linux/of_address.h> #include <linux/cpu_pm.h> #include <linux/irqdomain.h> #include <linux/irqchip/arm-gic.h> -#include <linux/slab.h> -#include <linux/workqueue.h> -#include <linux/delay.h> #include <asm/exception.h> #include <asm/smp_plat.h> @@ -48,7 +46,6 @@ #include <mach/axxia-gic.h> #include "lsi_power_management.h" -#include "axxia_circular_queue.h" #define MAX_GIC_INTERRUPTS 1020 @@ -168,7 +165,6 @@ struct gic_notifier_data { struct notifier_block *self; unsigned long cmd; void *v; - }; #endif @@ -177,16 +173,13 @@ struct gic_rpc_data { u32 func_mask; u32 cpu, oldcpu; u32 type; - bool update_enable; const struct cpumask *mask_val; #ifdef CONFIG_CPU_PM struct gic_notifier_data gn_data; #endif }; - static DEFINE_RAW_SPINLOCK(irq_controller_lock); - static DEFINE_MUTEX(irq_bus_lock); static struct gic_chip_data gic_data __read_mostly; @@ -213,47 +206,6 @@ static inline unsigned int gic_irq(struct irq_data *d) return d->hwirq; } - -/*************************** CIRCULAR QUEUE **************************************/ -struct circular_queue_t axxia_circ_q; -static void axxia_gic_flush_affinity_queue(struct work_struct *dummy); -static void gic_set_affinity_remote(void *info); -static void gic_clr_affinity_remote(void *info); - -static DECLARE_WORK(axxia_gic_affinity_work, axxia_gic_flush_affinity_queue); -static DEFINE_MUTEX(affinity_lock); - -enum axxia_affinity_mode { - AFFINITY_CLEAR_LOCAL = 1, - AFFINITY_CLEAR_OTHER_CLUSTER, - AFFINITY_SET_LOCAL, - AFFINITY_SET_OTHER_CLUSTER -}; - -static void axxia_gic_flush_affinity_queue(struct work_struct *dummy) -{ - - void *qdata; - struct gic_rpc_data *rpc_data; - - - while (axxia_get_item(&axxia_circ_q, &qdata) != -1) { - rpc_data = (struct gic_rpc_data *) qdata; - if (rpc_data->func_mask == SET_AFFINITY) { - smp_call_function_single(rpc_data->cpu, - gic_set_affinity_remote, - qdata, 1); - - } else if (rpc_data->func_mask == CLR_AFFINITY) { - - smp_call_function_single(rpc_data->cpu, - gic_clr_affinity_remote, - qdata, 1); - } - kfree(qdata); - } -} - /* * This GIC driver implements IRQ management routines (e.g., gic_mask_irq, * etc.) that work across multiple clusters. Since a core cannot directly @@ -266,7 +218,6 @@ static void axxia_gic_flush_affinity_queue(struct work_struct *dummy) * The Linux interrupt code has a mechanism, which is called bus lock/unlock, * which was created for irq chips hanging off slow busses like i2c/spi. The * bus lock is mutex that is used to serialize bus accesses. We take advantage - * * of this feature here, because we can think of IRQ management routines having * to remotely execute on other clusters as a "slow bus" action. Doing this * here serializes all IRQ management interfaces and guarantees that different @@ -490,52 +441,15 @@ static int gic_retrigger(struct irq_data *d) return -ENXIO; } -static int _gic_clear_affinity(struct irq_data *d, u32 cpu, bool update_enable) -{ - - void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); - unsigned int shift = (gic_irq(d) % 4) * 8; - u32 val; - u32 mask = 0; - u32 enable_mask, enable_offset; - - mask = 0xff << shift; - - enable_mask = 1 << (gic_irq(d) % 32); - enable_offset = 4 * (gic_irq(d) / 32); - - raw_spin_lock(&irq_controller_lock); - - val = readl_relaxed(reg) & ~mask; - /* Clear affinity, mask IRQ. */ - writel_relaxed(val, reg); - - if (update_enable) { - - writel_relaxed(enable_mask, - gic_data_dist_base(&gic_data) + GIC_DIST_PENDING_CLEAR + enable_offset); - writel_relaxed(enable_mask, - gic_data_dist_base(&gic_data) + GIC_DIST_ACTIVE_CLEAR + enable_offset); - writel_relaxed(enable_mask, - gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_CLEAR + enable_offset); - - } - - raw_spin_unlock(&irq_controller_lock); - - return IRQ_SET_MASK_OK; - -} - static int _gic_set_affinity(struct irq_data *d, - u32 cpu, - bool update_enable) + const struct cpumask *mask_val, + bool do_clear) { - void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); + void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + + (gic_irq(d) & ~3); unsigned int shift = (gic_irq(d) % 4) * 8; - u32 val; - u32 mask = 0; - u32 bit; + unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); + u32 val, mask, bit; u32 enable_mask, enable_offset; /* @@ -549,17 +463,18 @@ static int _gic_set_affinity(struct irq_data *d, enable_offset = 4 * (gic_irq(d) / 32); raw_spin_lock(&irq_controller_lock); - val = readl_relaxed(reg) & ~mask; - /* Set affinity, mask IRQ. */ - writel_relaxed(val | bit, reg); - - if (update_enable) { - writel_relaxed(enable_mask, - gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_SET - + enable_offset); + if (do_clear == true) { + /* Clear affinity, mask IRQ. */ + writel_relaxed(val, reg); + writel_relaxed(enable_mask, gic_data_dist_base(&gic_data) + + GIC_DIST_ENABLE_CLEAR + enable_offset); + } else { + /* Set affinity, unmask IRQ. */ + writel_relaxed(val | bit, reg); + writel_relaxed(enable_mask, gic_data_dist_base(&gic_data) + + GIC_DIST_ENABLE_SET + enable_offset); } - raw_spin_unlock(&irq_controller_lock); return IRQ_SET_MASK_OK; @@ -574,37 +489,27 @@ static int _gic_set_affinity(struct irq_data *d, static void gic_set_affinity_remote(void *info) { struct gic_rpc_data *rpc = (struct gic_rpc_data *)info; - _gic_set_affinity(rpc->d, rpc->cpu, rpc->update_enable); - + _gic_set_affinity(rpc->d, rpc->mask_val, false); } static void gic_clr_affinity_remote(void *info) { struct gic_rpc_data *rpc = (struct gic_rpc_data *)info; - _gic_clear_affinity(rpc->d, rpc->oldcpu, rpc->update_enable); - + _gic_set_affinity(rpc->d, rpc->mask_val, true); } static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { + unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); u32 pcpu = cpu_logical_map(smp_processor_id()); unsigned int irqid = gic_irq(d); - struct cpumask *affinity_mask = (struct cpumask *)mask_val; - u32 mask; - u32 oldcpu = irq_cpuid[irqid]; - struct gic_rpc_data *gic_rpc_ptr; - int rval; - bool new_same_core = false; - bool old_same_core = false; - bool update_enable = false; - u32 clear_needed = 0; - u32 set_needed = 0; - u32 add_cpu; - u32 del_cpu; BUG_ON(!irqs_disabled()); + if (cpu >= nr_cpu_ids) + return -EINVAL; + if (irqid >= MAX_GIC_INTERRUPTS) return -EINVAL; @@ -612,131 +517,53 @@ static int gic_set_affinity(struct irq_data *d, if ((irqid >= IPI0_CPU0) && (irqid < MAX_AXM_IPI_NUM)) return IRQ_SET_MASK_OK; - - if (force) - add_cpu = cpumask_any(cpu_online_mask); - else - add_cpu = cpumask_any_and(affinity_mask, cpu_online_mask); - - if (add_cpu >= nr_cpu_ids) { - pr_err("ERROR: no cpus left\n"); - return -EINVAL; - } - - del_cpu = oldcpu; - - if (add_cpu == del_cpu) + /* + * If the new IRQ affinity is the same as current, then + * there's no need to update anything. + */ + if (cpu_logical_map(cpu) == irq_cpuid[irqid]) return IRQ_SET_MASK_OK; - new_same_core = - ((add_cpu / CORES_PER_CLUSTER) == (pcpu / CORES_PER_CLUSTER)) ? - true : false; - old_same_core = - ((del_cpu / CORES_PER_CLUSTER) == (pcpu / CORES_PER_CLUSTER)) ? - true : false; - - update_enable = ((add_cpu / CORES_PER_CLUSTER) == (del_cpu / CORES_PER_CLUSTER)) ? false : true; - - if (new_same_core) { - - if (old_same_core) { - clear_needed = AFFINITY_CLEAR_LOCAL; - set_needed = AFFINITY_SET_LOCAL; - } else { - set_needed = AFFINITY_SET_LOCAL; - clear_needed = AFFINITY_CLEAR_OTHER_CLUSTER; - } - + /* + * If the new physical cpu assignment falls within the same + * cluster as the cpu we're currently running on, set the IRQ + * affinity directly. Otherwise, use the RPC mechanism. + */ + if ((cpu_logical_map(cpu) / CORES_PER_CLUSTER) == + (pcpu / CORES_PER_CLUSTER)) { + _gic_set_affinity(d, mask_val, false); } else { - - if (old_same_core) { - set_needed = AFFINITY_SET_OTHER_CLUSTER; - clear_needed = AFFINITY_CLEAR_LOCAL; - } else { - set_needed = AFFINITY_SET_OTHER_CLUSTER; - clear_needed = AFFINITY_CLEAR_OTHER_CLUSTER; - } + gic_rpc_data.func_mask |= SET_AFFINITY; + gic_rpc_data.cpu = cpu; + gic_rpc_data.d = d; + gic_rpc_data.mask_val = mask_val; } - mutex_lock(&affinity_lock); - - /* Update Axxia IRQ affinity table with the new physical CPU number. */ - irq_cpuid[irqid] = cpu_logical_map(add_cpu); - /* - * We clear first to make sure the affinity mask always has a bit set, - * especially when the two cpus are in the same cluster. + * If the new physical cpu assignment is on a cluster that's + * different than the prior cluster, clear the IRQ affinity + * on the old cluster. */ - if (irqid != IRQ_PMU) { - - if (clear_needed == AFFINITY_CLEAR_LOCAL) { - - _gic_clear_affinity(d, del_cpu, update_enable); - - } else if (clear_needed == AFFINITY_CLEAR_OTHER_CLUSTER) { - - mask = 0xf << (oldcpu / CORES_PER_CLUSTER); - del_cpu = cpumask_any_and((struct cpumask *)&mask, cpu_online_mask); - - if (del_cpu < nr_cpu_ids) { - - gic_rpc_ptr = kmalloc(sizeof(struct gic_rpc_data), GFP_KERNEL); - if (!gic_rpc_ptr) { - pr_err( - "ERROR: failed to get memory for workqueue to set affinity false\n"); - mutex_unlock(&affinity_lock); - return -EFAULT; - } - - gic_rpc_ptr->func_mask = CLR_AFFINITY; - gic_rpc_ptr->cpu = del_cpu; - gic_rpc_ptr->oldcpu = oldcpu; - gic_rpc_ptr->d = d; - gic_rpc_ptr->update_enable = update_enable; - rval = axxia_put_item(&axxia_circ_q, (void *) gic_rpc_ptr); - if (rval) { - pr_err( - "ERROR: failed to add CLR_AFFINITY request for cpu: %d\n", - del_cpu); - kfree((void *) gic_rpc_ptr); - } - schedule_work_on(0, &axxia_gic_affinity_work); - } - } - } - - - if (set_needed == AFFINITY_SET_LOCAL) { - - _gic_set_affinity(d, add_cpu, update_enable); - - } else if (set_needed == AFFINITY_SET_OTHER_CLUSTER) { - - gic_rpc_ptr = kmalloc(sizeof(struct gic_rpc_data), GFP_KERNEL); - if (!gic_rpc_ptr) { - pr_err( - "ERROR: failed to get memory for workqueue to set affinity false\n"); - mutex_unlock(&affinity_lock); - return -EFAULT; - } - - gic_rpc_ptr->func_mask = SET_AFFINITY; - gic_rpc_ptr->cpu = add_cpu; - gic_rpc_ptr->update_enable = update_enable; - gic_rpc_ptr->d = d; - rval = axxia_put_item(&axxia_circ_q, (void *) gic_rpc_ptr); - if (rval) { - pr_err("ERROR: failed to add SET_AFFINITY request for cpu: %d\n", - add_cpu); - kfree((void *) gic_rpc_ptr); + if ((irqid != IRQ_PMU) && ((cpu_logical_map(cpu) / CORES_PER_CLUSTER) != + (irq_cpuid[irqid] / CORES_PER_CLUSTER))) { + /* + * If old cpu assignment falls within the same cluster as + * the cpu we're currently running on, clear the IRQ affinity + * directly. Otherwise, use RPC mechanism. + */ + if ((irq_cpuid[irqid] / CORES_PER_CLUSTER) == + (pcpu / CORES_PER_CLUSTER)) { + _gic_set_affinity(d, mask_val, true); + } else { + gic_rpc_data.func_mask |= CLR_AFFINITY; + gic_rpc_data.oldcpu = irq_cpuid[irqid]; + gic_rpc_data.d = d; + gic_rpc_data.mask_val = mask_val; } - schedule_work_on(0, &axxia_gic_affinity_work); - } - - mutex_unlock(&affinity_lock); - + /* Update Axxia IRQ affinity table with the new physical CPU number. */ + irq_cpuid[irqid] = cpu_logical_map(cpu); return IRQ_SET_MASK_OK; } @@ -1008,7 +835,6 @@ static void gic_irq_sync_unlock(struct irq_data *d) int i, j, cpu; int nr_cluster_ids = ((nr_cpu_ids - 1) / CORES_PER_CLUSTER) + 1; - if (gic_rpc_data.func_mask & IRQ_MASK) { smp_call_function_single(gic_rpc_data.cpu, gic_mask_remote, @@ -1044,6 +870,18 @@ static void gic_irq_sync_unlock(struct irq_data *d) } } + if (gic_rpc_data.func_mask & SET_AFFINITY) { + smp_call_function_single(gic_rpc_data.cpu, + gic_set_affinity_remote, + &gic_rpc_data, 1); + } + + if (gic_rpc_data.func_mask & CLR_AFFINITY) { + smp_call_function_single(gic_rpc_data.oldcpu, + gic_clr_affinity_remote, + &gic_rpc_data, 1); + } + #ifdef CONFIG_CPU_PM if (gic_rpc_data.func_mask & GIC_NOTIFIER) { for (i = 0; i < nr_cluster_ids; i++) { @@ -1073,7 +911,6 @@ static void gic_irq_sync_unlock(struct irq_data *d) /* Give the bus lock. */ mutex_unlock(&irq_bus_lock); - } static @@ -1275,14 +1112,9 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic) * Disable all interrupts. Leave the PPI and SGIs alone * as these enables are banked registers. */ - for (i = 32; i < gic_irqs; i += 32) { - writel_relaxed(0xffffffff, - base + GIC_DIST_ACTIVE_CLEAR + i * 4 / 32); - writel_relaxed(0xffffffff, - base + GIC_DIST_PENDING_CLEAR + i * 4 / 32); + for (i = 32; i < gic_irqs; i += 32) writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); - } /* * Set Axxia IPI interrupts for all CPUs in this cluster. @@ -1318,7 +1150,8 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic) for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) { enablemask = 1 << (i % 32); enableoff = (i / 32) * 4; - writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff); + writel_relaxed(enablemask, + base + GIC_DIST_ENABLE_SET + enableoff); } /* @@ -1573,9 +1406,6 @@ void __init axxia_gic_init_bases(int irq_start, gic_dist_init(gic); gic_cpu_init(gic); gic_pm_init(gic); - - axxia_initialize_queue(&axxia_circ_q); - } #ifdef CONFIG_SMP @@ -1588,6 +1418,11 @@ void __cpuinit axxia_gic_secondary_init(void) } #endif +void __cpuinit axxia_hotplug_gic_secondary_init(void) +{ + gic_cpu_init(&gic_data); +} + #ifdef CONFIG_OF int __init axxia_gic_of_init(struct device_node *node, @@ -1613,8 +1448,6 @@ int __init axxia_gic_of_init(struct device_node *node, axxia_gic_init_bases(-1, dist_base, cpu_base, node); - - return 0; } #endif diff --git a/arch/arm/mach-axxia/axxia_circular_queue.c b/arch/arm/mach-axxia/axxia_circular_queue.c deleted file mode 100644 index 971aead..0000000 --- a/arch/arm/mach-axxia/axxia_circular_queue.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * axxia_circular_queue.c - * - * Created on: Sep 30, 2014 - * Author: z8cpaul - */ - - -#include <asm/exception.h> -#include "axxia_circular_queue.h" - - -void axxia_initialize_queue(struct circular_queue_t *queue) -{ - int i; - - queue->valid_items = 0; - queue->first = 0; - queue->last = 0; - - for (i = 0; i < MAX_ITEMS; i++) - queue->data[i] = NULL; - - return; -} - -bool axxia_is_empty(struct circular_queue_t *queue) -{ - - if (queue->valid_items == 0) - return true; - else - return false; -} - -int axxia_put_item(struct circular_queue_t *queue, void *item_value) - -{ - if (queue->valid_items >= MAX_ITEMS) { - pr_err("ERROR: queue is full\n"); - return -EINVAL; - } else { - queue->valid_items++; - queue->data[queue->last] = item_value; - queue->last = (queue->last + 1) % MAX_ITEMS; - } - return 0; -} - - -int axxia_get_item(struct circular_queue_t *queue, void **item_value) -{ - - if (axxia_is_empty(queue)) { - return -1; - } else { - *item_value = queue->data[queue->first]; - queue->first = (queue->first + 1) % MAX_ITEMS; - queue->valid_items--; - } - return 0; - -} diff --git a/arch/arm/mach-axxia/axxia_circular_queue.h b/arch/arm/mach-axxia/axxia_circular_queue.h deleted file mode 100644 index 0fe88a0..0000000 --- a/arch/arm/mach-axxia/axxia_circular_queue.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * axxia_circular_queue.h - * - * Created on: Sep 30, 2014 - * Author: z8cpaul - */ - -#ifndef AXXIA_CIRCULAR_QUEUE_H_ -#define AXXIA_CIRCULAR_QUEUE_H_ - -#define MAX_ITEMS 1020 - -struct circular_queue_t - -{ - int first; - int last; - int valid_items; - void *data[MAX_ITEMS]; -}; - -void axxia_initialize_queue(struct circular_queue_t *queue); - -bool axxia_is_empty(struct circular_queue_t *queue); - -int axxia_put_item(struct circular_queue_t *queue, void *item_value); - -int axxia_get_item(struct circular_queue_t *queue, void **item_value); - -#endif diff --git a/arch/arm/mach-axxia/hotplug.c b/arch/arm/mach-axxia/hotplug.c index 8878346..a484ea1 100644 --- a/arch/arm/mach-axxia/hotplug.c +++ b/arch/arm/mach-axxia/hotplug.c @@ -11,140 +11,40 @@ #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" -#include "axxia_circular_queue.h" -extern struct circular_queue_t axxia_circ_q; + extern volatile int pen_release; -static inline void pm_cpu_logical_shutdown(u32 cpu) +static inline void cpu_enter_lowpower_a15(void) { - u32 val; - - asm volatile( - " mrc p15, 1, %0, c9, c0, 2\n" - : "=&r" (val) - : "Ir" (0x1) - : "cc"); + 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) + : "=&r" (v) : "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( - " 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" + /* + * Turn off coherency + */ + " mrc p15, 0, %0, c1, c0, 1\n" " bic %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - : "=&r" (val) - : "Ir" (CR_C) + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (0x40) : "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(); -} - -#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(); } @@ -154,65 +54,21 @@ 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"); } -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 pm_cpu_shutdown(cpu); -#endif return 1; } @@ -224,65 +80,33 @@ 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; - - int timeout; - timeout = 30; - /* make sure no migrations are happening */ - while (!axxia_is_empty(&axxia_circ_q)) { + pm_data pm_request; + int rVal = 0; + bool lastCpu; - if (timeout-- == 0) - break; + pm_request.cpu = cpu; + pm_request.cluster = 0; - mdelay(1); - } - if (timeout == 0) - pr_err("ERROR: tried to shut down and Q was still full\n"); - - last_cpu = pm_cpu_last_of_cluster(cpu); - if (last_cpu) - pm_L2_logical_shutdown(cpu); + lastCpu = pm_cpu_last_of_cluster(cpu); + if (lastCpu) + rVal = pm_cpul2_logical_die(&pm_request); else - pm_cpu_logical_shutdown(cpu); + rVal = pm_cpu_logical_die(&pm_request); + if (rVal) + pr_err("CPU %d failed to die\n", 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/include/mach/axxia-gic.h b/arch/arm/mach-axxia/include/mach/axxia-gic.h index b9e5574..af7fbdf 100644 --- a/arch/arm/mach-axxia/include/mach/axxia-gic.h +++ b/arch/arm/mach-axxia/include/mach/axxia-gic.h @@ -12,6 +12,5 @@ void axxia_gic_raise_softirq(const struct cpumask *mask, unsigned int irq); void axxia_gic_secondary_init(void); int __init axxia_gic_of_init(struct device_node *node, struct device_node *parent); -void axxia_gic_dump_mask(char *tmp, const struct cpumask *mask); -void axxia_gic_kill_cpu(u32 rcpu); + #endif diff --git a/arch/arm/mach-axxia/lsi_power_management.c b/arch/arm/mach-axxia/lsi_power_management.c index 93e8ac9..521c341 100644 --- a/arch/arm/mach-axxia/lsi_power_management.c +++ b/arch/arm/mach-axxia/lsi_power_management.c @@ -18,14 +18,12 @@ #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 @@ -35,7 +33,6 @@ #define PM_WAIT_TIME (10000) #define MAX_CLUSTER (4) -#define IPI_IRQ_MASK (0xFFFF) #define CHECK_BIT(var, pos) ((var) & (1 << (pos))) @@ -53,60 +50,25 @@ PORESET_CLUSTER1, PORESET_CLUSTER2, PORESET_CLUSTER3 }; -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 -}; -static void __iomem *syscon; - -u32 pm_cpu_powered_down; - +static u32 pm_cpu_powered_down; /*======================= LOCAL FUNCTIONS ==============================*/ -static void pm_set_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, +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, 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): @@ -165,14 +127,11 @@ 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): @@ -231,11 +190,10 @@ bool pm_cpu_last_of_cluster(u32 cpu) __LINE__); break; } -#endif return false; } -static void pm_set_bits_syscon_register(u32 reg, u32 data) +static void pm_set_bits_syscon_register(void __iomem *syscon, u32 reg, u32 data) { u32 tmp; @@ -244,7 +202,7 @@ static void pm_set_bits_syscon_register(u32 reg, u32 data) writel(tmp, syscon + reg); } -static void pm_clear_bits_syscon_register(u32 reg, u32 data) +static void pm_clear_bits_syscon_register(void __iomem *syscon, u32 reg, u32 data) { u32 tmp; @@ -253,7 +211,7 @@ static void pm_clear_bits_syscon_register(u32 reg, u32 data) writel(tmp, syscon + reg); } -static bool pm_test_for_bit_with_timeout(u32 reg, u32 bit) +static bool pm_test_for_bit_with_timeout(void __iomem *syscon, u32 reg, u32 bit) { u32 tmp = 0; @@ -272,7 +230,8 @@ static bool pm_test_for_bit_with_timeout(u32 reg, u32 bit) return true; } -static bool pm_wait_for_bit_clear_with_timeout(u32 reg, u32 bit) +static bool pm_wait_for_bit_clear_with_timeout(void __iomem *syscon, u32 reg, + u32 bit) { u32 cnt = 0; u32 tmp = 0; @@ -366,7 +325,7 @@ static int pm_dickens_logical_powerup(u32 cluster) void __iomem *dickens = ioremap(DICKENS_PHYS_ADDR, SZ_4M); if (dickens == NULL) { pr_err("Failed to map dickens registers\n"); - return -PM_ERR_DICKENS_IOREMAP; + return -EINVAL; } bit = (0x01 << cluster_to_node[cluster]); @@ -388,7 +347,7 @@ static int pm_dickens_logical_powerup(u32 cluster) if (0 == retries) { pr_err("DICKENS: Failed on the SNOOP DONAIN\n"); - rval = -PM_ERR_DICKENS_SNOOP_DOMAIN; + rval = -EINVAL; goto dickens_power_up; } @@ -408,8 +367,8 @@ 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 CTL SET\n"); - rval = -PM_ERR_DICKENS_SNOOP_DOMAIN; + pr_err("DICKENS: Failed on the SNOOP DONAIN\n"); + rval = -EINVAL; goto dickens_power_up; } @@ -419,155 +378,230 @@ dickens_power_up: return rval; } -static int pm_enable_ipi_interrupts(u32 cpu) -{ - - u32 i; - u32 cpumask = 1 << cpu; - u32 powered_on_cpu = (~(pm_cpu_powered_down) & IPI_IRQ_MASK); - - /* Enable the CPU IPI */ - pm_set_bits_syscon_register(ipi_register[cpu], powered_on_cpu); - - for (i = 0; i < MAX_IPI; i++) { - if ((1 << i) & powered_on_cpu) - pm_set_bits_syscon_register(ipi_register[i], cpumask); - } - - return 0; - -} - -void pm_init_syscon(void) -{ - syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); -} - -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) +static void __pm_cpu_shutdown(void *data) { + pm_data *pm_request = (pm_data *)data; + void __iomem *syscon; bool success; - u32 reqcpu = cpu_logical_map(cpu); - u32 cluster = reqcpu / CORES_PER_CLUSTER; - u32 cluster_mask = (0x01 << cluster); + u32 cluster_mask = (0x01 << pm_request->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; - } - - pm_init_syscon(); - /* * 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(reqcpu); + last_cpu = pm_cpu_last_of_cluster(pm_request->cpu); if (last_cpu) { - /* Disable all the interrupts to the cluster gic */ - pm_set_bits_syscon_register(NCP_SYSCON_GIC_DISABLE, cluster_mask); - /* Remove the cluster from the Dickens coherency domain */ - pm_dickens_logical_shutdown(cluster); + pm_dickens_logical_shutdown(pm_request->cluster); /* Power down the cpu */ - pm_cpu_physical_isolation_and_power_down(reqcpu); + pm_cpu_physical_isolation_and_power_down(pm_request->cpu); + + syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); + if (WARN_ON(!syscon)) + return; - 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 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_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); if (!success) { pr_err( "Failed to keep other cluster count going on cluster %d: %s-%d\n", - cluster, __FILE__, __LINE__); - goto pm_shutdown_exit; + pm_request->cluster, __FILE__, __LINE__); + iounmap(syscon); + return; } /* Turn off the ACE */ - pm_set_bits_syscon_register(NCP_SYSCON_PWR_ACEPWRDNRQ, cluster_mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_ACEPWRDNRQ, cluster_mask); /* Wait for ACE to complete power off */ - success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NACEPWRDNACK, cluster); + success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NACEPWRDNACK, pm_request->cluster); if (!success) { pr_err("Failed to power off ACE on cluster %d: %s-%d\n", - cluster, __FILE__, __LINE__); - goto pm_shutdown_exit; + pm_request->cluster, __FILE__, __LINE__); + iounmap(syscon); + return; } /* Isolate the cluster */ - pm_set_bits_syscon_register(NCP_SYSCON_PWR_ISOLATEL2MISC, cluster_mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_ISOLATEL2MISC, cluster_mask); /* Wait for WFI L2 to go to standby */ - success = pm_test_for_bit_with_timeout(NCP_SYSCON_PWR_STANDBYWFIL2, cluster); + success = pm_test_for_bit_with_timeout(syscon, NCP_SYSCON_PWR_STANDBYWFIL2, pm_request->cluster); if (!success) { pr_err("Failed to enter L2 WFI on cluster %d: %s-%d\n", - cluster, __FILE__, __LINE__); - goto pm_shutdown_exit; + pm_request->cluster, __FILE__, __LINE__); + iounmap(syscon); + return; } + iounmap(syscon); + /* Power off the L2 */ - pm_L2_isolation_and_power_down(cluster); + pm_L2_isolation_and_power_down(pm_request->cluster); if (rval == 0) { - pr_info("CPU %d is powered down with cluster: %d\n", reqcpu, cluster); - pm_cpu_powered_down |= (1 << reqcpu); + 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); } else - pr_err("CPU %d failed to power down\n", reqcpu); + pr_err("CPU %d failed to power down\n", pm_request->cpu); } else { - rval = pm_cpu_physical_isolation_and_power_down(reqcpu); + rval = pm_cpu_physical_isolation_and_power_down(pm_request->cpu); if (rval == 0) - pm_cpu_powered_down |= (1 << reqcpu); + pm_cpu_powered_down |= (1 << pm_request->cpu); else - pr_err("CPU %d failed to power down\n", reqcpu); + pr_err("CPU %d failed to power down\n", pm_request->cpu); } -pm_shutdown_exit: - iounmap(syscon); 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; - u32 cluster_mask = (0x01 << cluster); - u32 timeout; - pm_init_syscon(); + /* 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(NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_set_bits_syscon_register(NCP_SYSCON_PWRUP_CPU_RST, cpu_mask); + 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); - /* Hold the CPU in reset */ - pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_set_bits_syscon_register(NCP_SYSCON_HOLD_CPU, cpu_mask); + iounmap(syscon); /* * Is this the first cpu of a cluster to come back on? @@ -576,15 +610,16 @@ 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) { pr_err("CPU: Failed the logical L2 power up\n"); - goto pm_power_up; + return rval; } - pm_clear_bits_syscon_register(NCP_SYSCON_GIC_DISABLE, cluster_mask); cluster_power_up[cluster] = true; } + /* * Power up the CPU */ @@ -594,44 +629,32 @@ int pm_cpu_powerup(u32 cpu) goto pm_power_up; } - timeout = 30; - - /* wait max 10 ms until cpuX is on */ - while (!pm_cpu_active(cpu)) { - - if (timeout-- == 0) - break; - - mdelay(1); - } + udelay(16); - if (timeout == 0) { - rval = -ETIMEDOUT; - goto pm_power_up; - } + /* 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(NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_clear_bits_syscon_register(NCP_SYSCON_PWRUP_CPU_RST, cpu_mask); + 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(NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_clear_bits_syscon_register(NCP_SYSCON_HOLD_CPU, cpu_mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_HOLD_CPU, 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; } @@ -641,8 +664,46 @@ unsigned long pm_get_powered_down_cpu(void) return pm_cpu_powered_down; } +void pm_cpu_logical_shutdown(void *data) +{ + u32 val; -inline void pm_cpu_logical_powerup(void) + 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" (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( + " 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; + +} + +void pm_cpu_logical_powerup(void) { unsigned int v; @@ -670,104 +731,174 @@ inline void pm_cpu_logical_powerup(void) static int pm_cpu_physical_isolation_and_power_down(int cpu) { - + void __iomem *syscon; int rval = 0; bool success; u32 mask = (0x01 << cpu); - /* Disable the CPU IPI */ - pm_clear_bits_syscon_register(ipi_register[cpu], IPI_IRQ_MASK); + syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); + if (WARN_ON(!syscon)) + return -EINVAL; /* Initiate power down of the CPU's HS Rams */ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPURAM, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPURAM, mask); /* Wait until the RAM power down is complete */ - success = pm_test_for_bit_with_timeout(NCP_SYSCON_PWR_NPWRUPCPURAM_ACK, cpu); + success = pm_test_for_bit_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPCPURAM_ACK, cpu); if (!success) { - rval = -PM_ERR_FAILED_PWR_DWN_RAM; + rval = -EINVAL; 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(NCP_SYSCON_PWR_ISOLATECPU, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_ISOLATECPU, mask); /* Initiate power down of the CPU logic */ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPUSTG2, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPUSTG2, mask); udelay(10); /* Continue power down of the CPU logic */ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPUSTG1, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPUSTG1, mask); - success = pm_test_for_bit_with_timeout(NCP_SYSCON_PWR_NPWRUPCPUSTG1_ACK, cpu); + success = pm_test_for_bit_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPCPUSTG1_ACK, cpu); if (!success) { - rval = -PM_ERR_FAILED_STAGE_1; + rval = -EINVAL; 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(NCP_SYSCON_PWR_PWRUPCPUSTG1, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPUSTG1, mask); /* Wait until CPU logic power is compete */ - success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NPWRUPCPUSTG1_ACK, cpu); + success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPCPUSTG1_ACK, cpu); if (!success) { - rval = -PM_ERR_ACK1_FAIL; + rval = -EINVAL; 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(NCP_SYSCON_PWR_PWRUPCPUSTG2, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPUSTG2, mask); - udelay(20); + udelay(10); /* Initiate power up of HS Rams */ - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPCPURAM, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPCPURAM, mask); /* Wait until the RAM power up is complete */ - success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NPWRUPCPURAM_ACK, cpu); + success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPCPURAM_ACK, cpu); if (!success) { - rval = -PM_ERR_RAM_ACK_FAIL; + rval = -EINVAL; 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(NCP_SYSCON_PWR_ISOLATECPU, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_ISOLATECPU, mask); power_up_cleanup: - + iounmap(syscon); return rval; } /*========================================== L2 FUNCTIONS ========================================*/ -static void pm_L2_isolation_and_power_down(int cluster) +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; + /* Enable the chip select for the cluster */ - pm_set_bits_syscon_register(NCP_SYSCON_PWR_CHIPSELECTEN, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_CHIPSELECTEN, mask); /* Disable the hsram */ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2HSRAM, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2HSRAM, mask); switch (cluster) { case (0): @@ -788,11 +919,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(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif @@ -816,11 +947,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(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -843,11 +974,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(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -870,11 +1001,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(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -884,45 +1015,51 @@ static void pm_L2_isolation_and_power_down(int cluster) } /* Power down stage 2 */ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2LGCSTG2, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2LGCSTG2, mask); /* Power down stage 1 */ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2LGCSTG1, mask); + pm_clear_bits_syscon_register(syscon, 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(NCP_SYSCON_PWR_PWRUPL2LGCSTG1, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2LGCSTG1, mask); /* Wait for the stage 1 power up to complete */ - success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NPWRUPL2LGCSTG1_ACK, cluster); + success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPL2LGCSTG1_ACK, cluster); if (!success) { pr_err("CPU: Failed to ack the L2 Stage 1 Power up\n"); - rval = -PM_ERR_FAIL_L2ACK; + rval = -EINVAL; goto power_up_l2_cleanup; } /* Power on stage 2 */ - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2LGCSTG2, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2LGCSTG2, mask); /* Set the chip select */ - pm_set_bits_syscon_register(NCP_SYSCON_PWR_CHIPSELECTEN, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_CHIPSELECTEN, mask); - /* Power up the snoop ram */ - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL2HSRAM, mask); + /* Power up the snoop ramram */ + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL2HSRAM, mask); /* Wait for the stage 1 power up to complete */ - success = pm_wait_for_bit_clear_with_timeout(NCP_SYSCON_PWR_NPWRUPL2HSRAM_ACK, cluster); + success = pm_wait_for_bit_clear_with_timeout(syscon, NCP_SYSCON_PWR_NPWRUPL2HSRAM_ACK, cluster); if (!success) { pr_err("CPU: failed to get the HSRAM power up ACK\n"); - rval = -PM_ERR_FAIL_L2HSRAM; + rval = -EINVAL; goto power_up_l2_cleanup; } @@ -945,11 +1082,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(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL20RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif @@ -973,11 +1110,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(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL21RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -1000,11 +1137,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(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL22RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -1027,11 +1164,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(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM2, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM1, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM1, RAM_ALL_MASK); udelay(20); - pm_set_bits_syscon_register(NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0, RAM_ALL_MASK); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_PWRUPL23RAM_PWRUPL2RAM0, RAM_ALL_MASK); udelay(20); #endif break; @@ -1041,78 +1178,86 @@ static int pm_L2_physical_connection_and_power_up(u32 cluster) } /* Clear the chip select */ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_CHIPSELECTEN, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_CHIPSELECTEN, mask); /* Release the isolation clamps */ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_ISOLATEL2MISC, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_ISOLATEL2MISC, mask); /* Turn the ACE bridge power on*/ - pm_clear_bits_syscon_register(NCP_SYSCON_PWR_ACEPWRDNRQ, mask); + pm_clear_bits_syscon_register(syscon, 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; + syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K); + if (WARN_ON(!syscon)) + return -EINVAL; + /* put the cluster into a cpu hold */ - pm_set_bits_syscon_register(NCP_SYSCON_RESET_AXIS, + pm_set_bits_syscon_register(syscon, NCP_SYSCON_RESET_AXIS, cluster_to_poreset[cluster]); - /* Allow the L2 to be reset */ - pm_clear_bits_syscon_register(NCP_SYSCON_LRSTDISABLE, mask); + /* + * Write the key so the reset cpu register can be written to. + */ + 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); /* Hold the chip debug cluster */ - pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_set_bits_syscon_register(NCP_SYSCON_HOLD_DBG, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_HOLD_DBG, mask); /* Hold the L2 cluster */ - pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_set_bits_syscon_register(NCP_SYSCON_HOLD_L2, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_set_bits_syscon_register(syscon, 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(NCP_SYSCON_RESET_AXIS, + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_RESET_AXIS, cluster_to_poreset[cluster]); udelay(64); /* Enable the system counter */ - pm_set_bits_syscon_register(NCP_SYSCON_PWR_CSYSREQ_CNT, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_PWR_CSYSREQ_CNT, mask); /* Release the L2 cluster */ - pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_clear_bits_syscon_register(NCP_SYSCON_HOLD_L2, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_HOLD_L2, mask); /* Release the chip debug cluster */ - pm_set_bits_syscon_register(NCP_SYSCON_KEY, VALID_KEY_VALUE); - pm_clear_bits_syscon_register(NCP_SYSCON_HOLD_DBG, mask); + pm_set_bits_syscon_register(syscon, NCP_SYSCON_KEY, VALID_KEY_VALUE); + pm_clear_bits_syscon_register(syscon, 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(NCP_SYSCON_PWR_ACINACTM, mask); - - /* Disable the L2 reset */ - pm_set_bits_syscon_register(NCP_SYSCON_LRSTDISABLE, mask); + pm_clear_bits_syscon_register(syscon, NCP_SYSCON_PWR_ACINACTM, mask); - udelay(64); - -exit_pm_L2_logical_powerup: + iounmap(syscon); return rval; @@ -1124,6 +1269,12 @@ 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); @@ -1292,6 +1443,7 @@ void pm_debug_read_pwr_registers(void) #endif + iounmap(syscon); } @@ -1299,7 +1451,11 @@ 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); @@ -1325,7 +1481,8 @@ 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) @@ -1356,6 +1513,8 @@ void pm_dump_dickens(void) pr_err("DKN_MN_DVM_DOMAIN_CTL: 0x%x\n", status); + iounmap(dickens); + } diff --git a/arch/arm/mach-axxia/lsi_power_management.h b/arch/arm/mach-axxia/lsi_power_management.h index 4de6bd7..4cb6d1f 100644 --- a/arch/arm/mach-axxia/lsi_power_management.h +++ b/arch/arm/mach-axxia/lsi_power_management.h @@ -155,7 +155,6 @@ #define MAX_NUM_CLUSTERS (4) #define CORES_PER_CLUSTER (4) -#define MAX_IPI (19) typedef struct { u32 cpu; @@ -167,19 +166,18 @@ 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); -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 4bbcbed..009b3df 100644 --- a/arch/arm/mach-axxia/platsmp.c +++ b/arch/arm/mach-axxia/platsmp.c @@ -27,8 +27,6 @@ #include <mach/axxia-gic.h> extern void axxia_secondary_startup(void); -extern void axxia_cpu_power_management_gic_control(u32 cpu, bool enable); -extern void axxia_dist_power_management_gic_control(bool enable); #define SYSCON_PHYS_ADDR 0x002010030000ULL @@ -92,11 +90,10 @@ static DEFINE_RAW_SPINLOCK(boot_lock); void __cpuinit axxia_secondary_init(unsigned int cpu) { - int phys_cpu; - int phys_cluster; + int phys_cpu, cluster; phys_cpu = cpu_logical_map(cpu); - phys_cluster = phys_cpu / 4; + cluster = (phys_cpu / 4) << 8; /* * Only execute this when powering up a cpu for hotplug. @@ -107,20 +104,10 @@ void __cpuinit axxia_secondary_init(unsigned int cpu) axxia_gic_secondary_init(); } else { - -#ifdef CONFIG_HOTPLUG_CPU_COMPLETE_POWER_DOWN - pm_cpu_logical_powerup(); - mdelay(16); -#endif - axxia_gic_secondary_init(); - -#ifdef CONFIG_HOTPLUG_CPU_COMPLETE_POWER_DOWN pm_cpu_logical_powerup(); - if (cluster_power_up[phys_cluster]) - cluster_power_up[phys_cluster] = false; - pm_in_progress[phys_cpu] = false; -#endif + pm_in_progress[cpu] = false; + cluster_power_up[cluster] = false; } /* @@ -156,13 +143,14 @@ 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[phys_cpu] = true; + pm_in_progress[cpu] = true; rVal = pm_cpu_powerup(phys_cpu); if (rVal) { _raw_spin_unlock(&boot_lock); return rVal; } + } /* @@ -183,6 +171,7 @@ 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); @@ -192,7 +181,6 @@ 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)) { @@ -303,7 +291,6 @@ static void __init axxia_smp_prepare_cpus(unsigned int max_cpus) } 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