diff options
Diffstat (limited to 'arch/arm/mach-highbank')
-rw-r--r-- | arch/arm/mach-highbank/Kconfig | 18 | ||||
-rw-r--r-- | arch/arm/mach-highbank/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/mach-highbank/core.h | 18 | ||||
-rw-r--r-- | arch/arm/mach-highbank/highbank.c | 175 | ||||
-rw-r--r-- | arch/arm/mach-highbank/pm.c | 49 | ||||
-rw-r--r-- | arch/arm/mach-highbank/smc.S | 25 | ||||
-rw-r--r-- | arch/arm/mach-highbank/sysregs.h | 75 | ||||
-rw-r--r-- | arch/arm/mach-highbank/system.c | 22 |
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(); +} + |