summaryrefslogtreecommitdiffstats
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/Kconfig18
-rw-r--r--arch/arm/mach-highbank/Makefile4
-rw-r--r--arch/arm/mach-highbank/core.h18
-rw-r--r--arch/arm/mach-highbank/highbank.c175
-rw-r--r--arch/arm/mach-highbank/pm.c49
-rw-r--r--arch/arm/mach-highbank/smc.S25
-rw-r--r--arch/arm/mach-highbank/sysregs.h75
-rw-r--r--arch/arm/mach-highbank/system.c22
8 files changed, 386 insertions, 0 deletions
diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig
new file mode 100644
index 000000000..9de38ce81
--- /dev/null
+++ b/arch/arm/mach-highbank/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config ARCH_HIGHBANK
+ bool "Calxeda ECX-1000/2000 (Highbank/Midway)"
+ depends on ARCH_MULTI_V7
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARM_AMBA
+ select ARM_ERRATA_764369 if SMP
+ select ARM_ERRATA_775420
+ select ARM_ERRATA_798181 if SMP
+ select ARM_GIC
+ select ARM_PSCI
+ select ARM_TIMER_SP804
+ select CACHE_L2X0
+ select HAVE_ARM_SCU
+ select HAVE_ARM_TWD if SMP
+ select MAILBOX
+ select PL320_MBOX
+ select ZONE_DMA if ARM_LPAE
diff --git a/arch/arm/mach-highbank/Makefile b/arch/arm/mach-highbank/Makefile
new file mode 100644
index 000000000..71cc68041
--- /dev/null
+++ b/arch/arm/mach-highbank/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y := highbank.o system.o smc.o
+
+obj-$(CONFIG_PM_SLEEP) += pm.o
diff --git a/arch/arm/mach-highbank/core.h b/arch/arm/mach-highbank/core.h
new file mode 100644
index 000000000..3991a6594
--- /dev/null
+++ b/arch/arm/mach-highbank/core.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __HIGHBANK_CORE_H
+#define __HIGHBANK_CORE_H
+
+#include <linux/reboot.h>
+
+extern void highbank_restart(enum reboot_mode, const char *);
+extern void __iomem *scu_base_addr;
+
+#ifdef CONFIG_PM_SLEEP
+extern void highbank_pm_init(void);
+#else
+static inline void highbank_pm_init(void) {}
+#endif
+
+extern void highbank_smc1(int fn, int arg);
+
+#endif
diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c
new file mode 100644
index 000000000..db607955a
--- /dev/null
+++ b/arch/arm/mach-highbank/highbank.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2010-2011 Calxeda, Inc.
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clocksource.h>
+#include <linux/dma-map-ops.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/pl320-ipc.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/reboot.h>
+#include <linux/amba/bus.h>
+#include <linux/platform_device.h>
+#include <linux/psci.h>
+
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include "core.h"
+#include "sysregs.h"
+
+void __iomem *sregs_base;
+void __iomem *scu_base_addr;
+
+static void __init highbank_scu_map_io(void)
+{
+ unsigned long base;
+
+ /* Get SCU base */
+ asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base));
+
+ scu_base_addr = ioremap(base, SZ_4K);
+}
+
+
+static void highbank_l2c310_write_sec(unsigned long val, unsigned reg)
+{
+ if (reg == L2X0_CTRL)
+ highbank_smc1(0x102, val);
+ else
+ WARN_ONCE(1, "Highbank L2C310: ignoring write to reg 0x%x\n",
+ reg);
+}
+
+static void __init highbank_init_irq(void)
+{
+ irqchip_init();
+
+ if (of_find_compatible_node(NULL, NULL, "arm,cortex-a9"))
+ highbank_scu_map_io();
+}
+
+static void highbank_power_off(void)
+{
+ highbank_set_pwr_shutdown();
+
+ while (1)
+ cpu_do_idle();
+}
+
+static int highbank_platform_notifier(struct notifier_block *nb,
+ unsigned long event, void *__dev)
+{
+ struct resource *res;
+ int reg = -1;
+ u32 val;
+ struct device *dev = __dev;
+
+ if (event != BUS_NOTIFY_ADD_DEVICE)
+ return NOTIFY_DONE;
+
+ if (of_device_is_compatible(dev->of_node, "calxeda,hb-ahci"))
+ reg = 0xc;
+ else if (of_device_is_compatible(dev->of_node, "calxeda,hb-sdhci"))
+ reg = 0x18;
+ else if (of_device_is_compatible(dev->of_node, "arm,pl330"))
+ reg = 0x20;
+ else if (of_device_is_compatible(dev->of_node, "calxeda,hb-xgmac")) {
+ res = platform_get_resource(to_platform_device(dev),
+ IORESOURCE_MEM, 0);
+ if (res) {
+ if (res->start == 0xfff50000)
+ reg = 0;
+ else if (res->start == 0xfff51000)
+ reg = 4;
+ }
+ }
+
+ if (reg < 0)
+ return NOTIFY_DONE;
+
+ if (of_property_read_bool(dev->of_node, "dma-coherent")) {
+ val = readl(sregs_base + reg);
+ writel(val | 0xff01, sregs_base + reg);
+ set_dma_ops(dev, &arm_coherent_dma_ops);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block highbank_amba_nb = {
+ .notifier_call = highbank_platform_notifier,
+};
+
+static struct notifier_block highbank_platform_nb = {
+ .notifier_call = highbank_platform_notifier,
+};
+
+static struct platform_device highbank_cpuidle_device = {
+ .name = "cpuidle-calxeda",
+};
+
+static int hb_keys_notifier(struct notifier_block *nb, unsigned long event, void *data)
+{
+ u32 key = *(u32 *)data;
+
+ if (event != 0x1000)
+ return 0;
+
+ if (key == KEY_POWER)
+ orderly_poweroff(false);
+ else if (key == 0xffff)
+ ctrl_alt_del();
+
+ return 0;
+}
+static struct notifier_block hb_keys_nb = {
+ .notifier_call = hb_keys_notifier,
+};
+
+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();
+
+ bus_register_notifier(&platform_bus_type, &highbank_platform_nb);
+ bus_register_notifier(&amba_bustype, &highbank_amba_nb);
+
+ pl320_ipc_register_notifier(&hb_keys_nb);
+
+ if (psci_ops.cpu_suspend)
+ platform_device_register(&highbank_cpuidle_device);
+}
+
+static const char *const highbank_match[] __initconst = {
+ "calxeda,highbank",
+ "calxeda,ecx-2000",
+ NULL,
+};
+
+DT_MACHINE_START(HIGHBANK, "Highbank")
+#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE)
+ .dma_zone_size = (4ULL * SZ_1G),
+#endif
+ .l2c_aux_val = 0,
+ .l2c_aux_mask = ~0,
+ .l2c_write_sec = highbank_l2c310_write_sec,
+ .init_irq = highbank_init_irq,
+ .init_machine = highbank_init,
+ .dt_compat = highbank_match,
+ .restart = highbank_restart,
+MACHINE_END
diff --git a/arch/arm/mach-highbank/pm.c b/arch/arm/mach-highbank/pm.c
new file mode 100644
index 000000000..561941bae
--- /dev/null
+++ b/arch/arm/mach-highbank/pm.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2011 Calxeda, Inc.
+ */
+
+#include <linux/cpu_pm.h>
+#include <linux/init.h>
+#include <linux/psci.h>
+#include <linux/suspend.h>
+
+#include <asm/suspend.h>
+
+#include <uapi/linux/psci.h>
+
+#define HIGHBANK_SUSPEND_PARAM \
+ ((0 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
+ (1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
+ (PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
+
+static int highbank_suspend_finish(unsigned long val)
+{
+ return psci_ops.cpu_suspend(HIGHBANK_SUSPEND_PARAM, __pa(cpu_resume));
+}
+
+static int highbank_pm_enter(suspend_state_t state)
+{
+ cpu_pm_enter();
+ cpu_cluster_pm_enter();
+
+ cpu_suspend(0, highbank_suspend_finish);
+
+ cpu_cluster_pm_exit();
+ cpu_pm_exit();
+
+ return 0;
+}
+
+static const struct platform_suspend_ops highbank_pm_ops = {
+ .enter = highbank_pm_enter,
+ .valid = suspend_valid_only_mem,
+};
+
+void __init highbank_pm_init(void)
+{
+ if (!psci_ops.cpu_suspend)
+ return;
+
+ suspend_set_ops(&highbank_pm_ops);
+}
diff --git a/arch/arm/mach-highbank/smc.S b/arch/arm/mach-highbank/smc.S
new file mode 100644
index 000000000..78b3f19e7
--- /dev/null
+++ b/arch/arm/mach-highbank/smc.S
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copied from omap44xx-smc.S Copyright (C) 2010 Texas Instruments, Inc.
+ * Copyright 2012 Calxeda, Inc.
+ */
+
+#include <linux/linkage.h>
+
+/*
+ * This is common routine to manage secure monitor API
+ * used to modify the PL310 secure registers.
+ * 'r0' contains the value to be modified and 'r12' contains
+ * the monitor API number.
+ * Function signature : void highbank_smc1(u32 fn, u32 arg)
+ */
+ .arch armv7-a
+ .arch_extension sec
+ENTRY(highbank_smc1)
+ stmfd sp!, {r4-r11, lr}
+ mov r12, r0
+ mov r0, r1
+ dsb
+ smc #0
+ ldmfd sp!, {r4-r11, pc}
+ENDPROC(highbank_smc1)
diff --git a/arch/arm/mach-highbank/sysregs.h b/arch/arm/mach-highbank/sysregs.h
new file mode 100644
index 000000000..3c13fdcaf
--- /dev/null
+++ b/arch/arm/mach-highbank/sysregs.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2011 Calxeda, Inc.
+ */
+#ifndef _MACH_HIGHBANK__SYSREGS_H_
+#define _MACH_HIGHBANK__SYSREGS_H_
+
+#include <linux/io.h>
+#include <linux/smp.h>
+#include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
+#include "core.h"
+
+extern void __iomem *sregs_base;
+
+#define HB_SREG_A9_PWR_REQ 0xf00
+#define HB_SREG_A9_BOOT_STAT 0xf04
+#define HB_SREG_A9_BOOT_DATA 0xf08
+
+#define HB_PWR_SUSPEND 0
+#define HB_PWR_SOFT_RESET 1
+#define HB_PWR_HARD_RESET 2
+#define HB_PWR_SHUTDOWN 3
+
+#define SREG_CPU_PWR_CTRL(c) (0x200 + ((c) * 4))
+
+static inline void highbank_set_core_pwr(void)
+{
+ int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(smp_processor_id()), 0);
+ if (scu_base_addr)
+ scu_power_mode(scu_base_addr, SCU_PM_POWEROFF);
+ else
+ writel_relaxed(1, sregs_base + SREG_CPU_PWR_CTRL(cpu));
+}
+
+static inline void highbank_clear_core_pwr(void)
+{
+ int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(smp_processor_id()), 0);
+ if (scu_base_addr)
+ scu_power_mode(scu_base_addr, SCU_PM_NORMAL);
+ else
+ writel_relaxed(0, sregs_base + SREG_CPU_PWR_CTRL(cpu));
+}
+
+static inline void highbank_set_pwr_suspend(void)
+{
+ writel(HB_PWR_SUSPEND, sregs_base + HB_SREG_A9_PWR_REQ);
+ highbank_set_core_pwr();
+}
+
+static inline void highbank_set_pwr_shutdown(void)
+{
+ writel(HB_PWR_SHUTDOWN, sregs_base + HB_SREG_A9_PWR_REQ);
+ highbank_set_core_pwr();
+}
+
+static inline void highbank_set_pwr_soft_reset(void)
+{
+ writel(HB_PWR_SOFT_RESET, sregs_base + HB_SREG_A9_PWR_REQ);
+ highbank_set_core_pwr();
+}
+
+static inline void highbank_set_pwr_hard_reset(void)
+{
+ writel(HB_PWR_HARD_RESET, sregs_base + HB_SREG_A9_PWR_REQ);
+ highbank_set_core_pwr();
+}
+
+static inline void highbank_clear_pwr_request(void)
+{
+ writel(~0UL, sregs_base + HB_SREG_A9_PWR_REQ);
+ highbank_clear_core_pwr();
+}
+
+#endif
diff --git a/arch/arm/mach-highbank/system.c b/arch/arm/mach-highbank/system.c
new file mode 100644
index 000000000..b749c4a6d
--- /dev/null
+++ b/arch/arm/mach-highbank/system.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2011 Calxeda, Inc.
+ */
+#include <linux/io.h>
+#include <asm/proc-fns.h>
+#include <linux/reboot.h>
+
+#include "core.h"
+#include "sysregs.h"
+
+void highbank_restart(enum reboot_mode mode, const char *cmd)
+{
+ if (mode == REBOOT_HARD)
+ highbank_set_pwr_hard_reset();
+ else
+ highbank_set_pwr_soft_reset();
+
+ while (1)
+ cpu_do_idle();
+}
+