summaryrefslogtreecommitdiff
path: root/arch/arm/mach-highbank
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-highbank')
-rw-r--r--arch/arm/mach-highbank/Kconfig7
-rw-r--r--arch/arm/mach-highbank/Makefile2
-rw-r--r--arch/arm/mach-highbank/core.h4
-rw-r--r--arch/arm/mach-highbank/highbank.c47
-rw-r--r--arch/arm/mach-highbank/hotplug.c37
-rw-r--r--arch/arm/mach-highbank/platsmp.c68
-rw-r--r--arch/arm/mach-highbank/pm.c27
7 files changed, 163 insertions, 29 deletions
diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig
index 0aded64..8e8437d 100644
--- a/arch/arm/mach-highbank/Kconfig
+++ b/arch/arm/mach-highbank/Kconfig
@@ -4,16 +4,15 @@ config ARCH_HIGHBANK
select ARCH_HAS_CPUFREQ
select ARCH_HAS_HOLES_MEMORYMODEL
select ARCH_HAS_OPP
- select ARCH_SUPPORTS_BIG_ENDIAN
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_AMBA
- select ARM_ERRATA_764369 if SMP
+ select ARM_ERRATA_764369
select ARM_ERRATA_775420
- select ARM_ERRATA_798181 if SMP
+ select ARM_ERRATA_798181
select ARM_GIC
- select ARM_PSCI
select ARM_TIMER_SP804
select CACHE_L2X0
+ select CLKDEV_LOOKUP
select COMMON_CLK
select CPU_V7
select GENERIC_CLOCKEVENTS
diff --git a/arch/arm/mach-highbank/Makefile b/arch/arm/mach-highbank/Makefile
index 55840f4..8a1ef57 100644
--- a/arch/arm/mach-highbank/Makefile
+++ b/arch/arm/mach-highbank/Makefile
@@ -3,4 +3,6 @@ obj-y := highbank.o system.o smc.o
plus_sec := $(call as-instr,.arch_extension sec,+sec)
AFLAGS_smc.o :=-Wa,-march=armv7-a$(plus_sec)
+obj-$(CONFIG_SMP) += platsmp.o
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_PM_SLEEP) += pm.o
diff --git a/arch/arm/mach-highbank/core.h b/arch/arm/mach-highbank/core.h
index 7ec5edc..aea1ec5 100644
--- a/arch/arm/mach-highbank/core.h
+++ b/arch/arm/mach-highbank/core.h
@@ -3,6 +3,7 @@
#include <linux/reboot.h>
+extern void highbank_set_cpu_jump(int cpu, void *jump_addr);
extern void highbank_restart(enum reboot_mode, const char *);
extern void __iomem *scu_base_addr;
@@ -13,5 +14,8 @@ static inline void highbank_pm_init(void) {}
#endif
extern void highbank_smc1(int fn, int arg);
+extern void highbank_cpu_die(unsigned int cpu);
+
+extern struct smp_operations highbank_smp_ops;
#endif
diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c
index b3d7e56..8e63ccd 100644
--- a/arch/arm/mach-highbank/highbank.c
+++ b/arch/arm/mach-highbank/highbank.c
@@ -24,9 +24,11 @@
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/amba/bus.h>
-#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
-#include <asm/psci.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -47,6 +49,17 @@ static void __init highbank_scu_map_io(void)
scu_base_addr = ioremap(base, SZ_4K);
}
+#define HB_JUMP_TABLE_PHYS(cpu) (0x40 + (0x10 * (cpu)))
+#define HB_JUMP_TABLE_VIRT(cpu) phys_to_virt(HB_JUMP_TABLE_PHYS(cpu))
+
+void highbank_set_cpu_jump(int cpu, void *jump_addr)
+{
+ cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
+ writel(virt_to_phys(jump_addr), HB_JUMP_TABLE_VIRT(cpu));
+ __cpuc_flush_dcache_area(HB_JUMP_TABLE_VIRT(cpu), 16);
+ outer_clean_range(HB_JUMP_TABLE_PHYS(cpu),
+ HB_JUMP_TABLE_PHYS(cpu) + 15);
+}
static void highbank_l2x0_disable(void)
{
@@ -70,6 +83,20 @@ static void __init highbank_init_irq(void)
}
}
+static void __init highbank_timer_init(void)
+{
+ struct device_node *np;
+
+ /* Map system registers */
+ np = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs");
+ sregs_base = of_iomap(np, 0);
+ WARN_ON(!sregs_base);
+
+ of_clk_init(NULL);
+
+ clocksource_of_init();
+}
+
static void highbank_power_off(void)
{
highbank_set_pwr_shutdown();
@@ -126,19 +153,8 @@ static struct notifier_block highbank_platform_nb = {
.notifier_call = highbank_platform_notifier,
};
-static struct platform_device highbank_cpuidle_device = {
- .name = "cpuidle-calxeda",
-};
-
static void __init highbank_init(void)
{
- struct device_node *np;
-
- /* Map system registers */
- np = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs");
- sregs_base = of_iomap(np, 0);
- WARN_ON(!sregs_base);
-
pm_power_off = highbank_power_off;
highbank_pm_init();
@@ -146,9 +162,6 @@ static void __init highbank_init(void)
bus_register_notifier(&amba_bustype, &highbank_amba_nb);
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
-
- if (psci_ops.cpu_suspend)
- platform_device_register(&highbank_cpuidle_device);
}
static const char *highbank_match[] __initconst = {
@@ -161,7 +174,9 @@ DT_MACHINE_START(HIGHBANK, "Highbank")
#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE)
.dma_zone_size = (4ULL * SZ_1G),
#endif
+ .smp = smp_ops(highbank_smp_ops),
.init_irq = highbank_init_irq,
+ .init_time = highbank_timer_init,
.init_machine = highbank_init,
.dt_compat = highbank_match,
.restart = highbank_restart,
diff --git a/arch/arm/mach-highbank/hotplug.c b/arch/arm/mach-highbank/hotplug.c
new file mode 100644
index 0000000..a019e4e
--- /dev/null
+++ b/arch/arm/mach-highbank/hotplug.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2011 Calxeda, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <asm/cacheflush.h>
+
+#include "core.h"
+#include "sysregs.h"
+
+extern void secondary_startup(void);
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ */
+void __ref highbank_cpu_die(unsigned int cpu)
+{
+ highbank_set_cpu_jump(cpu, phys_to_virt(0));
+
+ flush_cache_louis();
+ highbank_set_core_pwr();
+
+ while (1)
+ cpu_do_idle();
+}
diff --git a/arch/arm/mach-highbank/platsmp.c b/arch/arm/mach-highbank/platsmp.c
new file mode 100644
index 0000000..32d75cf5
--- /dev/null
+++ b/arch/arm/mach-highbank/platsmp.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010-2011 Calxeda, Inc.
+ * Based on platsmp.c, Copyright (C) 2002 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+
+#include <asm/smp_scu.h>
+
+#include "core.h"
+
+extern void secondary_startup(void);
+
+static int highbank_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ highbank_set_cpu_jump(cpu, secondary_startup);
+ arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+ return 0;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+static void __init highbank_smp_init_cpus(void)
+{
+ unsigned int i, ncores = 4;
+
+ /* sanity check */
+ if (ncores > NR_CPUS) {
+ printk(KERN_WARNING
+ "highbank: no. of cores (%d) greater than configured "
+ "maximum of %d - clipping\n",
+ ncores, NR_CPUS);
+ ncores = NR_CPUS;
+ }
+
+ for (i = 0; i < ncores; i++)
+ set_cpu_possible(i, true);
+}
+
+static void __init highbank_smp_prepare_cpus(unsigned int max_cpus)
+{
+ if (scu_base_addr)
+ scu_enable(scu_base_addr);
+}
+
+struct smp_operations highbank_smp_ops __initdata = {
+ .smp_init_cpus = highbank_smp_init_cpus,
+ .smp_prepare_cpus = highbank_smp_prepare_cpus,
+ .smp_boot_secondary = highbank_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = highbank_cpu_die,
+#endif
+};
diff --git a/arch/arm/mach-highbank/pm.c b/arch/arm/mach-highbank/pm.c
index 7f2bd85..04eddb4 100644
--- a/arch/arm/mach-highbank/pm.c
+++ b/arch/arm/mach-highbank/pm.c
@@ -16,19 +16,27 @@
#include <linux/cpu_pm.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/suspend.h>
+#include <asm/cacheflush.h>
+#include <asm/proc-fns.h>
#include <asm/suspend.h>
-#include <asm/psci.h>
+
+#include "core.h"
+#include "sysregs.h"
static int highbank_suspend_finish(unsigned long val)
{
- const struct psci_power_state ps = {
- .type = PSCI_POWER_STATE_TYPE_POWER_DOWN,
- .affinity_level = 1,
- };
+ outer_flush_all();
+ outer_disable();
+
+ highbank_set_pwr_suspend();
+
+ cpu_do_idle();
- return psci_ops.cpu_suspend(ps, __pa(cpu_resume));
+ highbank_clear_pwr_request();
+ return 0;
}
static int highbank_pm_enter(suspend_state_t state)
@@ -36,11 +44,15 @@ static int highbank_pm_enter(suspend_state_t state)
cpu_pm_enter();
cpu_cluster_pm_enter();
+ highbank_set_cpu_jump(0, cpu_resume);
cpu_suspend(0, highbank_suspend_finish);
cpu_cluster_pm_exit();
cpu_pm_exit();
+ highbank_smc1(0x102, 0x1);
+ if (scu_base_addr)
+ scu_enable(scu_base_addr);
return 0;
}
@@ -51,8 +63,5 @@ static const struct platform_suspend_ops highbank_pm_ops = {
void __init highbank_pm_init(void)
{
- if (!psci_ops.cpu_suspend)
- return;
-
suspend_set_ops(&highbank_pm_ops);
}