diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /arch/arm/mach-prima2 | |
parent | Initial commit. (diff) | |
download | linux-upstream/5.10.209.tar.xz linux-upstream/5.10.209.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/arm/mach-prima2')
-rw-r--r-- | arch/arm/mach-prima2/Kconfig | 48 | ||||
-rw-r--r-- | arch/arm/mach-prima2/Makefile | 9 | ||||
-rw-r--r-- | arch/arm/mach-prima2/common.c | 64 | ||||
-rw-r--r-- | arch/arm/mach-prima2/common.h | 32 | ||||
-rw-r--r-- | arch/arm/mach-prima2/headsmp.S | 36 | ||||
-rw-r--r-- | arch/arm/mach-prima2/hotplug.c | 38 | ||||
-rw-r--r-- | arch/arm/mach-prima2/platsmp.c | 123 | ||||
-rw-r--r-- | arch/arm/mach-prima2/pm.c | 153 | ||||
-rw-r--r-- | arch/arm/mach-prima2/pm.h | 28 | ||||
-rw-r--r-- | arch/arm/mach-prima2/rstc.c | 107 | ||||
-rw-r--r-- | arch/arm/mach-prima2/rtciobrg.c | 179 | ||||
-rw-r--r-- | arch/arm/mach-prima2/sleep.S | 63 |
12 files changed, 880 insertions, 0 deletions
diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig new file mode 100644 index 000000000..ea077f663 --- /dev/null +++ b/arch/arm/mach-prima2/Kconfig @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig ARCH_SIRF + bool "CSR SiRF" + depends on ARCH_MULTI_V7 + select ARCH_HAS_RESET_CONTROLLER + select RESET_CONTROLLER + select GENERIC_IRQ_CHIP + select GPIOLIB + select NO_IOPORT_MAP + select REGMAP + select PINCTRL + select PINCTRL_SIRF + help + Support for CSR SiRFprimaII/Marco/Polo platforms + +if ARCH_SIRF + +comment "CSR SiRF atlas6/primaII/Atlas7 Specific Features" + +config ARCH_ATLAS6 + bool "CSR SiRFSoC ATLAS6 ARM Cortex A9 Platform" + default y + select SIRF_IRQ + help + Support for CSR SiRFSoC ARM Cortex A9 Platform + +config ARCH_ATLAS7 + bool "CSR SiRFSoC ATLAS7 ARM Cortex A7 Platform" + default y + select ARM_GIC + select ATLAS7_TIMER + select HAVE_ARM_SCU if SMP + help + Support for CSR SiRFSoC ARM Cortex A7 Platform + +config ARCH_PRIMA2 + bool "CSR SiRFSoC PRIMA2 ARM Cortex A9 Platform" + default y + select SIRF_IRQ + select ZONE_DMA + select PRIMA2_TIMER + help + Support for CSR SiRFSoC ARM Cortex A9 Platform + +config SIRF_IRQ + bool + +endif diff --git a/arch/arm/mach-prima2/Makefile b/arch/arm/mach-prima2/Makefile new file mode 100644 index 000000000..0fd276303 --- /dev/null +++ b/arch/arm/mach-prima2/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-y += rstc.o +obj-y += common.o +obj-y += rtciobrg.o +obj-$(CONFIG_SUSPEND) += pm.o sleep.o +obj-$(CONFIG_SMP) += platsmp.o headsmp.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o + +CFLAGS_hotplug.o += -march=armv7-a diff --git a/arch/arm/mach-prima2/common.c b/arch/arm/mach-prima2/common.c new file mode 100644 index 000000000..e2d158e33 --- /dev/null +++ b/arch/arm/mach-prima2/common.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Defines machines for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sizes.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include "common.h" + +static void __init __maybe_unused sirfsoc_init_late(void) +{ + sirfsoc_pm_init(); +} + +#ifdef CONFIG_ARCH_ATLAS6 +static const char *const atlas6_dt_match[] __initconst = { + "sirf,atlas6", + NULL +}; + +DT_MACHINE_START(ATLAS6_DT, "Generic ATLAS6 (Flattened Device Tree)") + /* Maintainer: Barry Song <baohua.song@csr.com> */ + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .init_late = sirfsoc_init_late, + .dt_compat = atlas6_dt_match, +MACHINE_END +#endif + +#ifdef CONFIG_ARCH_PRIMA2 +static const char *const prima2_dt_match[] __initconst = { + "sirf,prima2", + NULL +}; + +DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)") + /* Maintainer: Barry Song <baohua.song@csr.com> */ + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .dma_zone_size = SZ_256M, + .init_late = sirfsoc_init_late, + .dt_compat = prima2_dt_match, +MACHINE_END +#endif + +#ifdef CONFIG_ARCH_ATLAS7 +static const char *const atlas7_dt_match[] __initconst = { + "sirf,atlas7", + NULL +}; + +DT_MACHINE_START(ATLAS7_DT, "Generic ATLAS7 (Flattened Device Tree)") + /* Maintainer: Barry Song <baohua.song@csr.com> */ + .smp = smp_ops(sirfsoc_smp_ops), + .dt_compat = atlas7_dt_match, +MACHINE_END +#endif diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h new file mode 100644 index 000000000..3bab7e571 --- /dev/null +++ b/arch/arm/mach-prima2/common.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file contains common function prototypes to avoid externs in the c files. + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#ifndef __MACH_PRIMA2_COMMON_H__ +#define __MACH_PRIMA2_COMMON_H__ + +#include <linux/init.h> +#include <linux/reboot.h> + +#include <asm/mach/time.h> +#include <asm/exception.h> + +extern volatile int prima2_pen_release; + +extern const struct smp_operations sirfsoc_smp_ops; +extern void sirfsoc_secondary_startup(void); +extern void sirfsoc_cpu_die(unsigned int cpu); + +extern void __init sirfsoc_of_irq_init(void); +extern asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs); + +#ifdef CONFIG_SUSPEND +extern int sirfsoc_pm_init(void); +#else +static inline int sirfsoc_pm_init(void) { return 0; } +#endif + +#endif diff --git a/arch/arm/mach-prima2/headsmp.S b/arch/arm/mach-prima2/headsmp.S new file mode 100644 index 000000000..88ea12439 --- /dev/null +++ b/arch/arm/mach-prima2/headsmp.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Entry of the second core for CSR Marco dual-core SMP SoCs + * + * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/linkage.h> +#include <linux/init.h> + +/* + * SIRFSOC specific entry point for secondary CPUs. This provides + * a "holding pen" into which all secondary cores are held until we're + * ready for them to initialise. + */ +ENTRY(sirfsoc_secondary_startup) + mrc p15, 0, r0, c0, c0, 5 + and r0, r0, #15 + adr r4, 1f + ldmia r4, {r5, r6} + sub r4, r4, r5 + add r6, r6, r4 +pen: ldr r7, [r6] + cmp r7, r0 + bne pen + + /* + * we've been released from the holding pen: secondary_stack + * should now contain the SVC stack for this core + */ + b secondary_startup +ENDPROC(sirfsoc_secondary_startup) + + .align +1: .long . + .long prima2_pen_release diff --git a/arch/arm/mach-prima2/hotplug.c b/arch/arm/mach-prima2/hotplug.c new file mode 100644 index 000000000..bc0d957e8 --- /dev/null +++ b/arch/arm/mach-prima2/hotplug.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CPU hotplug support for CSR Marco dual-core SMP SoCs + * + * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/smp.h> + +#include <asm/smp_plat.h> +#include "common.h" + +static inline void platform_do_lowpower(unsigned int cpu) +{ + /* we put the platform to just WFI */ + for (;;) { + __asm__ __volatile__("dsb\n\t" "wfi\n\t" + : : : "memory"); + if (prima2_pen_release == cpu_logical_map(cpu)) { + /* + * OK, proper wakeup, we're done + */ + break; + } + } +} + +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +void sirfsoc_cpu_die(unsigned int cpu) +{ + platform_do_lowpower(cpu); +} diff --git a/arch/arm/mach-prima2/platsmp.c b/arch/arm/mach-prima2/platsmp.c new file mode 100644 index 000000000..8f7bbb57f --- /dev/null +++ b/arch/arm/mach-prima2/platsmp.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * plat smp support for CSR Marco dual-core SMP SoCs + * + * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <asm/page.h> +#include <asm/mach/map.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> +#include <asm/cacheflush.h> +#include <asm/cputype.h> + +#include "common.h" + +static void __iomem *clk_base; + +static DEFINE_SPINLOCK(boot_lock); + +/* XXX prima2_pen_release is cargo culted code - DO NOT COPY XXX */ +volatile int prima2_pen_release = -1; + +static void sirfsoc_secondary_init(unsigned int cpu) +{ + /* + * let the primary processor know we're out of the + * pen, then head off into the C entry point + */ + prima2_pen_release = -1; + smp_wmb(); + + /* + * Synchronise with the boot thread. + */ + spin_lock(&boot_lock); + spin_unlock(&boot_lock); +} + +static const struct of_device_id clk_ids[] = { + { .compatible = "sirf,atlas7-clkc" }, + {}, +}; + +static int sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + unsigned long timeout; + struct device_node *np; + + np = of_find_matching_node(NULL, clk_ids); + if (!np) + return -ENODEV; + + clk_base = of_iomap(np, 0); + if (!clk_base) + return -ENOMEM; + + /* + * write the address of secondary startup into the clkc register + * at offset 0x2bC, then write the magic number 0x3CAF5D62 to the + * clkc register at offset 0x2b8, which is what boot rom code is + * waiting for. This would wake up the secondary core from WFE + */ +#define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x2bc + __raw_writel(__pa_symbol(sirfsoc_secondary_startup), + clk_base + SIRFSOC_CPU1_JUMPADDR_OFFSET); + +#define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x2b8 + __raw_writel(0x3CAF5D62, + clk_base + SIRFSOC_CPU1_WAKEMAGIC_OFFSET); + + /* make sure write buffer is drained */ + mb(); + + spin_lock(&boot_lock); + + /* + * The secondary processor is waiting to be released from + * the holding pen - release it, then wait for it to flag + * that it has been released by resetting prima2_pen_release. + * + * Note that "prima2_pen_release" is the hardware CPU ID, whereas + * "cpu" is Linux's internal ID. + */ + prima2_pen_release = cpu_logical_map(cpu); + sync_cache_w(&prima2_pen_release); + + /* + * Send the secondary CPU SEV, thereby causing the boot monitor to read + * the JUMPADDR and WAKEMAGIC, and branch to the address found there. + */ + dsb_sev(); + + timeout = jiffies + (1 * HZ); + while (time_before(jiffies, timeout)) { + smp_rmb(); + if (prima2_pen_release == -1) + break; + + udelay(10); + } + + /* + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ + spin_unlock(&boot_lock); + + return prima2_pen_release != -1 ? -ENOSYS : 0; +} + +const struct smp_operations sirfsoc_smp_ops __initconst = { + .smp_secondary_init = sirfsoc_secondary_init, + .smp_boot_secondary = sirfsoc_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = sirfsoc_cpu_die, +#endif +}; diff --git a/arch/arm/mach-prima2/pm.c b/arch/arm/mach-prima2/pm.c new file mode 100644 index 000000000..c24bc89f3 --- /dev/null +++ b/arch/arm/mach-prima2/pm.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * power management entry for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/kernel.h> +#include <linux/suspend.h> +#include <linux/slab.h> +#include <linux/export.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/io.h> +#include <linux/rtc/sirfsoc_rtciobrg.h> +#include <asm/outercache.h> +#include <asm/suspend.h> +#include <asm/hardware/cache-l2x0.h> + +#include "pm.h" + +/* + * suspend asm codes will access these to make DRAM become self-refresh and + * system sleep + */ +u32 sirfsoc_pwrc_base; +void __iomem *sirfsoc_memc_base; + +static void sirfsoc_set_wakeup_source(void) +{ + u32 pwr_trigger_en_reg; + pwr_trigger_en_reg = sirfsoc_rtc_iobrg_readl(sirfsoc_pwrc_base + + SIRFSOC_PWRC_TRIGGER_EN); +#define X_ON_KEY_B (1 << 0) +#define RTC_ALARM0_B (1 << 2) +#define RTC_ALARM1_B (1 << 3) + sirfsoc_rtc_iobrg_writel(pwr_trigger_en_reg | X_ON_KEY_B | + RTC_ALARM0_B | RTC_ALARM1_B, + sirfsoc_pwrc_base + SIRFSOC_PWRC_TRIGGER_EN); +} + +static void sirfsoc_set_sleep_mode(u32 mode) +{ + u32 sleep_mode = sirfsoc_rtc_iobrg_readl(sirfsoc_pwrc_base + + SIRFSOC_PWRC_PDN_CTRL); + sleep_mode &= ~(SIRFSOC_SLEEP_MODE_MASK << 1); + sleep_mode |= mode << 1; + sirfsoc_rtc_iobrg_writel(sleep_mode, sirfsoc_pwrc_base + + SIRFSOC_PWRC_PDN_CTRL); +} + +static int sirfsoc_pre_suspend_power_off(void) +{ + u32 wakeup_entry = __pa_symbol(cpu_resume); + + sirfsoc_rtc_iobrg_writel(wakeup_entry, sirfsoc_pwrc_base + + SIRFSOC_PWRC_SCRATCH_PAD1); + + sirfsoc_set_wakeup_source(); + + sirfsoc_set_sleep_mode(SIRFSOC_DEEP_SLEEP_MODE); + + return 0; +} + +static int sirfsoc_pm_enter(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_MEM: + sirfsoc_pre_suspend_power_off(); + + outer_disable(); + /* go zzz */ + cpu_suspend(0, sirfsoc_finish_suspend); + outer_resume(); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct platform_suspend_ops sirfsoc_pm_ops = { + .enter = sirfsoc_pm_enter, + .valid = suspend_valid_only_mem, +}; + +static const struct of_device_id pwrc_ids[] = { + { .compatible = "sirf,prima2-pwrc" }, + {} +}; + +static int __init sirfsoc_of_pwrc_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, pwrc_ids); + if (!np) { + pr_err("unable to find compatible sirf pwrc node in dtb\n"); + return -ENOENT; + } + + /* + * pwrc behind rtciobrg is not located in memory space + * though the property is named reg. reg only means base + * offset for pwrc. then of_iomap is not suitable here. + */ + if (of_property_read_u32(np, "reg", &sirfsoc_pwrc_base)) + panic("unable to find base address of pwrc node in dtb\n"); + + of_node_put(np); + + return 0; +} + +static const struct of_device_id memc_ids[] = { + { .compatible = "sirf,prima2-memc" }, + {} +}; + +static int sirfsoc_memc_probe(struct platform_device *op) +{ + struct device_node *np = op->dev.of_node; + + sirfsoc_memc_base = of_iomap(np, 0); + if (!sirfsoc_memc_base) + panic("unable to map memc registers\n"); + + return 0; +} + +static struct platform_driver sirfsoc_memc_driver = { + .probe = sirfsoc_memc_probe, + .driver = { + .name = "sirfsoc-memc", + .of_match_table = memc_ids, + }, +}; + +static int __init sirfsoc_memc_init(void) +{ + return platform_driver_register(&sirfsoc_memc_driver); +} + +int __init sirfsoc_pm_init(void) +{ + sirfsoc_of_pwrc_init(); + sirfsoc_memc_init(); + suspend_set_ops(&sirfsoc_pm_ops); + return 0; +} diff --git a/arch/arm/mach-prima2/pm.h b/arch/arm/mach-prima2/pm.h new file mode 100644 index 000000000..0aff6cb87 --- /dev/null +++ b/arch/arm/mach-prima2/pm.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * arch/arm/mach-prima2/pm.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#ifndef _MACH_PRIMA2_PM_H_ +#define _MACH_PRIMA2_PM_H_ + +#define SIRFSOC_PWR_SLEEPFORCE 0x01 + +#define SIRFSOC_SLEEP_MODE_MASK 0x3 +#define SIRFSOC_DEEP_SLEEP_MODE 0x1 + +#define SIRFSOC_PWRC_PDN_CTRL 0x0 +#define SIRFSOC_PWRC_PON_OFF 0x4 +#define SIRFSOC_PWRC_TRIGGER_EN 0x8 +#define SIRFSOC_PWRC_PIN_STATUS 0x14 +#define SIRFSOC_PWRC_SCRATCH_PAD1 0x18 +#define SIRFSOC_PWRC_SCRATCH_PAD2 0x1C + +#ifndef __ASSEMBLY__ +extern int sirfsoc_finish_suspend(unsigned long); +#endif + +#endif + diff --git a/arch/arm/mach-prima2/rstc.c b/arch/arm/mach-prima2/rstc.c new file mode 100644 index 000000000..9d56606ac --- /dev/null +++ b/arch/arm/mach-prima2/rstc.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * reset controller for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/reset-controller.h> + +#include <asm/system_misc.h> + +#define SIRFSOC_RSTBIT_NUM 64 + +static void __iomem *sirfsoc_rstc_base; +static DEFINE_MUTEX(rstc_lock); + +static int sirfsoc_reset_module(struct reset_controller_dev *rcdev, + unsigned long sw_reset_idx) +{ + u32 reset_bit = sw_reset_idx; + + if (reset_bit >= SIRFSOC_RSTBIT_NUM) + return -EINVAL; + + mutex_lock(&rstc_lock); + + /* + * Writing 1 to this bit resets corresponding block. + * Writing 0 to this bit de-asserts reset signal of the + * corresponding block. datasheet doesn't require explicit + * delay between the set and clear of reset bit. it could + * be shorter if tests pass. + */ + writel(readl(sirfsoc_rstc_base + + (reset_bit / 32) * 4) | (1 << reset_bit), + sirfsoc_rstc_base + (reset_bit / 32) * 4); + msleep(20); + writel(readl(sirfsoc_rstc_base + + (reset_bit / 32) * 4) & ~(1 << reset_bit), + sirfsoc_rstc_base + (reset_bit / 32) * 4); + + mutex_unlock(&rstc_lock); + + return 0; +} + +static struct reset_control_ops sirfsoc_rstc_ops = { + .reset = sirfsoc_reset_module, +}; + +static struct reset_controller_dev sirfsoc_reset_controller = { + .ops = &sirfsoc_rstc_ops, + .nr_resets = SIRFSOC_RSTBIT_NUM, +}; + +#define SIRFSOC_SYS_RST_BIT BIT(31) + +static void sirfsoc_restart(enum reboot_mode mode, const char *cmd) +{ + writel(SIRFSOC_SYS_RST_BIT, sirfsoc_rstc_base); +} + +static int sirfsoc_rstc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + sirfsoc_rstc_base = of_iomap(np, 0); + if (!sirfsoc_rstc_base) { + dev_err(&pdev->dev, "unable to map rstc cpu registers\n"); + return -ENOMEM; + } + + sirfsoc_reset_controller.of_node = np; + arm_pm_restart = sirfsoc_restart; + + if (IS_ENABLED(CONFIG_RESET_CONTROLLER)) + reset_controller_register(&sirfsoc_reset_controller); + + return 0; +} + +static const struct of_device_id rstc_ids[] = { + { .compatible = "sirf,prima2-rstc" }, + {}, +}; + +static struct platform_driver sirfsoc_rstc_driver = { + .probe = sirfsoc_rstc_probe, + .driver = { + .name = "sirfsoc_rstc", + .of_match_table = rstc_ids, + }, +}; + +static int __init sirfsoc_rstc_init(void) +{ + return platform_driver_register(&sirfsoc_rstc_driver); +} +subsys_initcall(sirfsoc_rstc_init); diff --git a/arch/arm/mach-prima2/rtciobrg.c b/arch/arm/mach-prima2/rtciobrg.c new file mode 100644 index 000000000..97c0e333e --- /dev/null +++ b/arch/arm/mach-prima2/rtciobrg.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RTC I/O Bridge interfaces for CSR SiRFprimaII/atlas7 + * ARM access the registers of SYSRTC, GPSRTC and PWRC through this module + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> + +#define SIRFSOC_CPUIOBRG_CTRL 0x00 +#define SIRFSOC_CPUIOBRG_WRBE 0x04 +#define SIRFSOC_CPUIOBRG_ADDR 0x08 +#define SIRFSOC_CPUIOBRG_DATA 0x0c + +/* + * suspend asm codes will access this address to make system deepsleep + * after DRAM becomes self-refresh + */ +void __iomem *sirfsoc_rtciobrg_base; +static DEFINE_SPINLOCK(rtciobrg_lock); + +/* + * symbols without lock are only used by suspend asm codes + * and these symbols are not exported too + */ +void sirfsoc_rtc_iobrg_wait_sync(void) +{ + while (readl_relaxed(sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_CTRL)) + cpu_relax(); +} + +void sirfsoc_rtc_iobrg_besyncing(void) +{ + unsigned long flags; + + spin_lock_irqsave(&rtciobrg_lock, flags); + + sirfsoc_rtc_iobrg_wait_sync(); + + spin_unlock_irqrestore(&rtciobrg_lock, flags); +} +EXPORT_SYMBOL_GPL(sirfsoc_rtc_iobrg_besyncing); + +u32 __sirfsoc_rtc_iobrg_readl(u32 addr) +{ + sirfsoc_rtc_iobrg_wait_sync(); + + writel_relaxed(0x00, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_WRBE); + writel_relaxed(addr, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_ADDR); + writel_relaxed(0x01, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_CTRL); + + sirfsoc_rtc_iobrg_wait_sync(); + + return readl_relaxed(sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_DATA); +} + +u32 sirfsoc_rtc_iobrg_readl(u32 addr) +{ + unsigned long flags, val; + + /* TODO: add hwspinlock to sync with M3 */ + spin_lock_irqsave(&rtciobrg_lock, flags); + + val = __sirfsoc_rtc_iobrg_readl(addr); + + spin_unlock_irqrestore(&rtciobrg_lock, flags); + + return val; +} +EXPORT_SYMBOL_GPL(sirfsoc_rtc_iobrg_readl); + +void sirfsoc_rtc_iobrg_pre_writel(u32 val, u32 addr) +{ + sirfsoc_rtc_iobrg_wait_sync(); + + writel_relaxed(0xf1, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_WRBE); + writel_relaxed(addr, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_ADDR); + + writel_relaxed(val, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_DATA); +} + +void sirfsoc_rtc_iobrg_writel(u32 val, u32 addr) +{ + unsigned long flags; + + /* TODO: add hwspinlock to sync with M3 */ + spin_lock_irqsave(&rtciobrg_lock, flags); + + sirfsoc_rtc_iobrg_pre_writel(val, addr); + + writel_relaxed(0x01, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_CTRL); + + sirfsoc_rtc_iobrg_wait_sync(); + + spin_unlock_irqrestore(&rtciobrg_lock, flags); +} +EXPORT_SYMBOL_GPL(sirfsoc_rtc_iobrg_writel); + + +static int regmap_iobg_regwrite(void *context, unsigned int reg, + unsigned int val) +{ + sirfsoc_rtc_iobrg_writel(val, reg); + return 0; +} + +static int regmap_iobg_regread(void *context, unsigned int reg, + unsigned int *val) +{ + *val = (u32)sirfsoc_rtc_iobrg_readl(reg); + return 0; +} + +static struct regmap_bus regmap_iobg = { + .reg_write = regmap_iobg_regwrite, + .reg_read = regmap_iobg_regread, +}; + +/** + * devm_regmap_init_iobg(): Initialise managed register map + * + * @iobg: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_iobg(struct device *dev, + const struct regmap_config *config) +{ + const struct regmap_bus *bus = ®map_iobg; + + return devm_regmap_init(dev, bus, dev, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_iobg); + +static const struct of_device_id rtciobrg_ids[] = { + { .compatible = "sirf,prima2-rtciobg" }, + {} +}; + +static int sirfsoc_rtciobrg_probe(struct platform_device *op) +{ + struct device_node *np = op->dev.of_node; + + sirfsoc_rtciobrg_base = of_iomap(np, 0); + if (!sirfsoc_rtciobrg_base) + panic("unable to map rtc iobrg registers\n"); + + return 0; +} + +static struct platform_driver sirfsoc_rtciobrg_driver = { + .probe = sirfsoc_rtciobrg_probe, + .driver = { + .name = "sirfsoc-rtciobrg", + .of_match_table = rtciobrg_ids, + }, +}; + +static int __init sirfsoc_rtciobrg_init(void) +{ + return platform_driver_register(&sirfsoc_rtciobrg_driver); +} +postcore_initcall(sirfsoc_rtciobrg_init); + +MODULE_AUTHOR("Zhiwu Song <zhiwu.song@csr.com>"); +MODULE_AUTHOR("Barry Song <baohua.song@csr.com>"); +MODULE_DESCRIPTION("CSR SiRFprimaII rtc io bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-prima2/sleep.S b/arch/arm/mach-prima2/sleep.S new file mode 100644 index 000000000..d9bbc5ca3 --- /dev/null +++ b/arch/arm/mach-prima2/sleep.S @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * sleep mode for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/linkage.h> +#include <asm/ptrace.h> +#include <asm/assembler.h> + +#include "pm.h" + +#define DENALI_CTL_22_OFF 0x58 +#define DENALI_CTL_112_OFF 0x1c0 + + .text + +ENTRY(sirfsoc_finish_suspend) + @ r5: mem controller + ldr r0, =sirfsoc_memc_base + ldr r5, [r0] + @ r6: pwrc base offset + ldr r0, =sirfsoc_pwrc_base + ldr r6, [r0] + @ r7: rtc iobrg controller + ldr r0, =sirfsoc_rtciobrg_base + ldr r7, [r0] + + @ Read the power control register and set the + @ sleep force bit. + add r0, r6, #SIRFSOC_PWRC_PDN_CTRL + bl __sirfsoc_rtc_iobrg_readl + orr r0,r0,#SIRFSOC_PWR_SLEEPFORCE + add r1, r6, #SIRFSOC_PWRC_PDN_CTRL + bl sirfsoc_rtc_iobrg_pre_writel + mov r1, #0x1 + + @ read the MEM ctl register and set the self + @ refresh bit + + ldr r2, [r5, #DENALI_CTL_22_OFF] + orr r2, r2, #0x1 + + @ Following code has to run from cache since + @ the RAM is going to self refresh mode + .align 5 + str r2, [r5, #DENALI_CTL_22_OFF] + +1: + ldr r4, [r5, #DENALI_CTL_112_OFF] + tst r4, #0x1 + bne 1b + + @ write SLEEPFORCE through rtc iobridge + + str r1, [r7] + @ wait rtc io bridge sync +1: + ldr r3, [r7] + tst r3, #0x01 + bne 1b + b . |