diff options
Diffstat (limited to 'kernel/arch/arm/mach-prima2')
-rw-r--r-- | kernel/arch/arm/mach-prima2/Kconfig | 44 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/Makefile | 8 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/Makefile.boot | 3 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/common.c | 65 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/common.h | 31 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/headsmp.S | 38 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/hotplug.c | 38 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/platsmp.c | 121 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/pm.c | 153 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/pm.h | 29 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/rstc.c | 108 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/rtciobrg.c | 138 | ||||
-rw-r--r-- | kernel/arch/arm/mach-prima2/sleep.S | 64 |
13 files changed, 840 insertions, 0 deletions
diff --git a/kernel/arch/arm/mach-prima2/Kconfig b/kernel/arch/arm/mach-prima2/Kconfig new file mode 100644 index 000000000..e03d8b5c9 --- /dev/null +++ b/kernel/arch/arm/mach-prima2/Kconfig @@ -0,0 +1,44 @@ +menuconfig ARCH_SIRF + bool "CSR SiRF" if ARCH_MULTI_V7 + select ARCH_HAS_RESET_CONTROLLER + select ARCH_REQUIRE_GPIOLIB + select GENERIC_IRQ_CHIP + select NO_IOPORT_MAP + 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 CPU_V7 + select HAVE_ARM_SCU if SMP + select HAVE_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 + help + Support for CSR SiRFSoC ARM Cortex A9 Platform + +config SIRF_IRQ + bool + +endif diff --git a/kernel/arch/arm/mach-prima2/Makefile b/kernel/arch/arm/mach-prima2/Makefile new file mode 100644 index 000000000..d7d02b043 --- /dev/null +++ b/kernel/arch/arm/mach-prima2/Makefile @@ -0,0 +1,8 @@ +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/kernel/arch/arm/mach-prima2/Makefile.boot b/kernel/arch/arm/mach-prima2/Makefile.boot new file mode 100644 index 000000000..c77a4883a --- /dev/null +++ b/kernel/arch/arm/mach-prima2/Makefile.boot @@ -0,0 +1,3 @@ +zreladdr-y += 0x00008000 +params_phys-y := 0x00000100 +initrd_phys-y := 0x00800000 diff --git a/kernel/arch/arm/mach-prima2/common.c b/kernel/arch/arm/mach-prima2/common.c new file mode 100644 index 000000000..8cadb302a --- /dev/null +++ b/kernel/arch/arm/mach-prima2/common.c @@ -0,0 +1,65 @@ +/* + * Defines machines for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <asm/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 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/kernel/arch/arm/mach-prima2/common.h b/kernel/arch/arm/mach-prima2/common.h new file mode 100644 index 000000000..3916a6665 --- /dev/null +++ b/kernel/arch/arm/mach-prima2/common.h @@ -0,0 +1,31 @@ +/* + * 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. + * + * Licensed under GPLv2 or later. + */ + +#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 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/kernel/arch/arm/mach-prima2/headsmp.S b/kernel/arch/arm/mach-prima2/headsmp.S new file mode 100644 index 000000000..d86fe33c5 --- /dev/null +++ b/kernel/arch/arm/mach-prima2/headsmp.S @@ -0,0 +1,38 @@ +/* + * Entry of the second core for CSR Marco dual-core SMP SoCs + * + * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#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) + bl v7_invalidate_l1 + 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 pen_release diff --git a/kernel/arch/arm/mach-prima2/hotplug.c b/kernel/arch/arm/mach-prima2/hotplug.c new file mode 100644 index 000000000..0ab2f8bae --- /dev/null +++ b/kernel/arch/arm/mach-prima2/hotplug.c @@ -0,0 +1,38 @@ +/* + * CPU hotplug support for CSR Marco dual-core SMP SoCs + * + * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/smp.h> + +#include <asm/smp_plat.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 (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 __ref sirfsoc_cpu_die(unsigned int cpu) +{ + platform_do_lowpower(cpu); +} diff --git a/kernel/arch/arm/mach-prima2/platsmp.c b/kernel/arch/arm/mach-prima2/platsmp.c new file mode 100644 index 000000000..dcb3ed0c2 --- /dev/null +++ b/kernel/arch/arm/mach-prima2/platsmp.c @@ -0,0 +1,121 @@ +/* + * plat smp support for CSR Marco dual-core SMP SoCs + * + * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#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_RAW_SPINLOCK(boot_lock); + +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 + */ + pen_release = -1; + smp_wmb(); + + /* + * Synchronise with the boot thread. + */ + raw_spin_lock(&boot_lock); + raw_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(virt_to_phys(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(); + + raw_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 pen_release. + * + * Note that "pen_release" is the hardware CPU ID, whereas + * "cpu" is Linux's internal ID. + */ + pen_release = cpu_logical_map(cpu); + sync_cache_w(&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 (pen_release == -1) + break; + + udelay(10); + } + + /* + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ + raw_spin_unlock(&boot_lock); + + return pen_release != -1 ? -ENOSYS : 0; +} + +struct smp_operations sirfsoc_smp_ops __initdata = { + .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/kernel/arch/arm/mach-prima2/pm.c b/kernel/arch/arm/mach-prima2/pm.c new file mode 100644 index 000000000..d99d08eeb --- /dev/null +++ b/kernel/arch/arm/mach-prima2/pm.c @@ -0,0 +1,153 @@ +/* + * power management entry for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#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/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 = virt_to_phys(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/kernel/arch/arm/mach-prima2/pm.h b/kernel/arch/arm/mach-prima2/pm.h new file mode 100644 index 000000000..bae6d77e0 --- /dev/null +++ b/kernel/arch/arm/mach-prima2/pm.h @@ -0,0 +1,29 @@ +/* + * arch/arm/mach-prima2/pm.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#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/kernel/arch/arm/mach-prima2/rstc.c b/kernel/arch/arm/mach-prima2/rstc.c new file mode 100644 index 000000000..7c251eb11 --- /dev/null +++ b/kernel/arch/arm/mach-prima2/rstc.c @@ -0,0 +1,108 @@ +/* + * reset controller for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#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/kernel/arch/arm/mach-prima2/rtciobrg.c b/kernel/arch/arm/mach-prima2/rtciobrg.c new file mode 100644 index 000000000..8f66d8f7c --- /dev/null +++ b/kernel/arch/arm/mach-prima2/rtciobrg.c @@ -0,0 +1,138 @@ +/* + * RTC I/O Bridge interfaces for CSR SiRFprimaII + * ARM access the registers of SYSRTC, GPSRTC and PWRC through this module + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.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; + + 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; + + 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 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>, " + "Barry Song <baohua.song@csr.com>"); +MODULE_DESCRIPTION("CSR SiRFprimaII rtc io bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/arch/arm/mach-prima2/sleep.S b/kernel/arch/arm/mach-prima2/sleep.S new file mode 100644 index 000000000..0745abc36 --- /dev/null +++ b/kernel/arch/arm/mach-prima2/sleep.S @@ -0,0 +1,64 @@ +/* + * sleep mode for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#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 . |