summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-prima2
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2013-01-27 23:03:42 -0800
committerOlof Johansson <olof@lixom.net>2013-01-27 23:03:42 -0800
commitaf70fdc947dbe835acc26c6ee9e8e930f38935f8 (patch)
tree9924e9f2732e3cf3c7a882cb11377247729891ab /arch/arm/mach-prima2
parent66eae035dc59755c51ad3b3c718a5ed7535322e3 (diff)
parent7f46a10724e0d31c83029436e54298be2cc558fa (diff)
Merge branch 'marco-timer-cleanup-rebase' of git://gitorious.org/sirfprima2-kernel/sirfprima2-kernel into next/soc
From Barry Song, this adds support for a new SoC from CSR; marco. It's SMP, uses GIC instead of VIC and in general needs a bit of rework of the platform code for setup, which this branch contains. * 'marco-timer-cleanup-rebase' of git://gitorious.org/sirfprima2-kernel/sirfprima2-kernel: ARM: PRIMA2: provide two DEBUG_LL ports for prima2 and marco ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures ARM: PRIMA2: irq: make prima2 irq can work even we enable GIC for Marco ARM: PRIMA2: rtciobg: it is also compatible with marco ARM: PRIMA2: rstc: enable the support for Marco ARM: PRIMA2: mv timer to timer-prima2 as we will add timer-marco ARM: PRIMA2: initialize l2x0 according to mach from DT ARM: PRIMA2: enable AUTO_ZRELADDR for SIRF in Kconfig ARM: PRIMA2: add CSR SiRFmarco device tree .dts Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'arch/arm/mach-prima2')
-rw-r--r--arch/arm/mach-prima2/Kconfig10
-rw-r--r--arch/arm/mach-prima2/Makefile5
-rw-r--r--arch/arm/mach-prima2/common.c45
-rw-r--r--arch/arm/mach-prima2/common.h15
-rw-r--r--arch/arm/mach-prima2/headsmp.S79
-rw-r--r--arch/arm/mach-prima2/hotplug.c41
-rw-r--r--arch/arm/mach-prima2/include/mach/irqs.h4
-rw-r--r--arch/arm/mach-prima2/include/mach/uart.h6
-rw-r--r--arch/arm/mach-prima2/include/mach/uncompress.h3
-rw-r--r--arch/arm/mach-prima2/irq.c16
-rw-r--r--arch/arm/mach-prima2/l2x0.c29
-rw-r--r--arch/arm/mach-prima2/platsmp.c163
-rw-r--r--arch/arm/mach-prima2/rstc.c45
-rw-r--r--arch/arm/mach-prima2/rtciobrg.c1
-rw-r--r--arch/arm/mach-prima2/timer-marco.c316
-rw-r--r--arch/arm/mach-prima2/timer-prima2.c (renamed from arch/arm/mach-prima2/timer.c)6
16 files changed, 751 insertions, 33 deletions
diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig
index 558ccfb8d45..4f7379fe01e 100644
--- a/arch/arm/mach-prima2/Kconfig
+++ b/arch/arm/mach-prima2/Kconfig
@@ -11,6 +11,16 @@ config ARCH_PRIMA2
help
Support for CSR SiRFSoC ARM Cortex A9 Platform
+config ARCH_MARCO
+ bool "CSR SiRFSoC MARCO ARM Cortex A9 Platform"
+ default y
+ select ARM_GIC
+ select CPU_V7
+ select HAVE_SMP
+ select SMP_ON_UP
+ help
+ Support for CSR SiRFSoC ARM Cortex A9 Platform
+
endmenu
config SIRF_IRQ
diff --git a/arch/arm/mach-prima2/Makefile b/arch/arm/mach-prima2/Makefile
index fc9ce22e2b5..bfe360cbd17 100644
--- a/arch/arm/mach-prima2/Makefile
+++ b/arch/arm/mach-prima2/Makefile
@@ -1,4 +1,3 @@
-obj-y := timer.o
obj-y += rstc.o
obj-y += common.o
obj-y += rtciobrg.o
@@ -6,3 +5,7 @@ obj-$(CONFIG_DEBUG_LL) += lluart.o
obj-$(CONFIG_CACHE_L2X0) += l2x0.o
obj-$(CONFIG_SUSPEND) += pm.o sleep.o
obj-$(CONFIG_SIRF_IRQ) += irq.o
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
+obj-$(CONFIG_ARCH_MARCO) += timer-marco.o
diff --git a/arch/arm/mach-prima2/common.c b/arch/arm/mach-prima2/common.c
index ed3570e5eb8..00a65649a7e 100644
--- a/arch/arm/mach-prima2/common.c
+++ b/arch/arm/mach-prima2/common.c
@@ -8,9 +8,11 @@
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/of_irq.h>
#include <asm/sizes.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+#include <asm/hardware/gic.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include "common.h"
@@ -30,6 +32,12 @@ void __init sirfsoc_init_late(void)
sirfsoc_pm_init();
}
+static __init void sirfsoc_map_io(void)
+{
+ sirfsoc_map_lluart();
+ sirfsoc_map_scu();
+}
+
#ifdef CONFIG_ARCH_PRIMA2
static const char *prima2_dt_match[] __initdata = {
"sirf,prima2",
@@ -38,9 +46,12 @@ static const char *prima2_dt_match[] __initdata = {
DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
/* Maintainer: Barry Song <baohua.song@csr.com> */
- .map_io = sirfsoc_map_lluart,
+ .map_io = sirfsoc_map_io,
.init_irq = sirfsoc_of_irq_init,
- .init_time = sirfsoc_timer_init,
+ .init_time = sirfsoc_prima2_timer_init,
+#ifdef CONFIG_MULTI_IRQ_HANDLER
+ .handle_irq = sirfsoc_handle_irq,
+#endif
.dma_zone_size = SZ_256M,
.init_machine = sirfsoc_mach_init,
.init_late = sirfsoc_init_late,
@@ -48,3 +59,33 @@ DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
.restart = sirfsoc_restart,
MACHINE_END
#endif
+
+#ifdef CONFIG_ARCH_MARCO
+static const struct of_device_id marco_irq_match[] __initconst = {
+ { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, },
+ { /* sentinel */ }
+};
+
+static void __init marco_init_irq(void)
+{
+ of_irq_init(marco_irq_match);
+}
+
+static const char *marco_dt_match[] __initdata = {
+ "sirf,marco",
+ NULL
+};
+
+DT_MACHINE_START(MARCO_DT, "Generic MARCO (Flattened Device Tree)")
+ /* Maintainer: Barry Song <baohua.song@csr.com> */
+ .smp = smp_ops(sirfsoc_smp_ops),
+ .map_io = sirfsoc_map_io,
+ .init_irq = marco_init_irq,
+ .init_time = sirfsoc_marco_timer_init,
+ .handle_irq = gic_handle_irq,
+ .init_machine = sirfsoc_mach_init,
+ .init_late = sirfsoc_init_late,
+ .dt_compat = marco_dt_match,
+ .restart = sirfsoc_restart,
+MACHINE_END
+#endif
diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h
index 9c75f124e3c..b7c26b62e4a 100644
--- a/arch/arm/mach-prima2/common.h
+++ b/arch/arm/mach-prima2/common.h
@@ -11,12 +11,19 @@
#include <linux/init.h>
#include <asm/mach/time.h>
+#include <asm/exception.h>
-extern void sirfsoc_timer_init(void);
+extern void sirfsoc_prima2_timer_init(void);
+extern void sirfsoc_marco_timer_init(void);
+
+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 void __init sirfsoc_of_clk_init(void);
extern void sirfsoc_restart(char, const char *);
+extern asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs);
#ifndef CONFIG_DEBUG_LL
static inline void sirfsoc_map_lluart(void) {}
@@ -24,6 +31,12 @@ static inline void sirfsoc_map_lluart(void) {}
extern void __init sirfsoc_map_lluart(void);
#endif
+#ifndef CONFIG_SMP
+static inline void sirfsoc_map_scu(void) {}
+#else
+extern void sirfsoc_map_scu(void);
+#endif
+
#ifdef CONFIG_SUSPEND
extern int sirfsoc_pm_init(void);
#else
diff --git a/arch/arm/mach-prima2/headsmp.S b/arch/arm/mach-prima2/headsmp.S
new file mode 100644
index 00000000000..6ec19d51a27
--- /dev/null
+++ b/arch/arm/mach-prima2/headsmp.S
@@ -0,0 +1,79 @@
+/*
+ * 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>
+
+ __INIT
+/*
+ * Cold boot and hardware reset show different behaviour,
+ * system will be always panic if we warm-reset the board
+ * Here we invalidate L1 of CPU1 to make sure there isn't
+ * uninitialized data written into memory later
+ */
+ENTRY(v7_invalidate_l1)
+ mov r0, #0
+ mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache
+ mcr p15, 2, r0, c0, c0, 0
+ mrc p15, 1, r0, c0, c0, 0
+
+ ldr r1, =0x7fff
+ and r2, r1, r0, lsr #13
+
+ ldr r1, =0x3ff
+
+ and r3, r1, r0, lsr #3 @ NumWays - 1
+ add r2, r2, #1 @ NumSets
+
+ and r0, r0, #0x7
+ add r0, r0, #4 @ SetShift
+
+ clz r1, r3 @ WayShift
+ add r4, r3, #1 @ NumWays
+1: sub r2, r2, #1 @ NumSets--
+ mov r3, r4 @ Temp = NumWays
+2: subs r3, r3, #1 @ Temp--
+ mov r5, r3, lsl r1
+ mov r6, r2, lsl r0
+ orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
+ mcr p15, 0, r5, c7, c6, 2
+ bgt 2b
+ cmp r2, #0
+ bgt 1b
+ dsb
+ isb
+ mov pc, lr
+ENDPROC(v7_invalidate_l1)
+
+/*
+ * 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/arch/arm/mach-prima2/hotplug.c b/arch/arm/mach-prima2/hotplug.c
new file mode 100644
index 00000000000..97c1ee58644
--- /dev/null
+++ b/arch/arm/mach-prima2/hotplug.c
@@ -0,0 +1,41 @@
+/*
+ * 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/cacheflush.h>
+#include <asm/smp_plat.h>
+
+static inline void platform_do_lowpower(unsigned int cpu)
+{
+ flush_cache_all();
+
+ /* 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 sirfsoc_cpu_die(unsigned int cpu)
+{
+ platform_do_lowpower(cpu);
+}
diff --git a/arch/arm/mach-prima2/include/mach/irqs.h b/arch/arm/mach-prima2/include/mach/irqs.h
index f6014a07541..b778a0f248e 100644
--- a/arch/arm/mach-prima2/include/mach/irqs.h
+++ b/arch/arm/mach-prima2/include/mach/irqs.h
@@ -10,8 +10,8 @@
#define __ASM_ARCH_IRQS_H
#define SIRFSOC_INTENAL_IRQ_START 0
-#define SIRFSOC_INTENAL_IRQ_END 59
+#define SIRFSOC_INTENAL_IRQ_END 127
#define SIRFSOC_GPIO_IRQ_START (SIRFSOC_INTENAL_IRQ_END + 1)
-#define NR_IRQS 220
+#define NR_IRQS 288
#endif
diff --git a/arch/arm/mach-prima2/include/mach/uart.h b/arch/arm/mach-prima2/include/mach/uart.h
index c98b4d5ac24..c10510d01a4 100644
--- a/arch/arm/mach-prima2/include/mach/uart.h
+++ b/arch/arm/mach-prima2/include/mach/uart.h
@@ -10,7 +10,13 @@
#define __MACH_PRIMA2_SIRFSOC_UART_H
/* UART-1: used as serial debug port */
+#if defined(CONFIG_DEBUG_SIRFPRIMA2_UART1)
#define SIRFSOC_UART1_PA_BASE 0xb0060000
+#elif defined(CONFIG_DEBUG_SIRFMARCO_UART1)
+#define SIRFSOC_UART1_PA_BASE 0xcc060000
+#else
+#define SIRFSOC_UART1_PA_BASE 0
+#endif
#define SIRFSOC_UART1_VA_BASE SIRFSOC_VA(0x060000)
#define SIRFSOC_UART1_SIZE SZ_4K
diff --git a/arch/arm/mach-prima2/include/mach/uncompress.h b/arch/arm/mach-prima2/include/mach/uncompress.h
index 0c898fcf909..15f3edcfbb4 100644
--- a/arch/arm/mach-prima2/include/mach/uncompress.h
+++ b/arch/arm/mach-prima2/include/mach/uncompress.h
@@ -25,6 +25,9 @@ static __inline__ void putc(char c)
* during kernel decompression, all mappings are flat:
* virt_addr == phys_addr
*/
+ if (!SIRFSOC_UART1_PA_BASE)
+ return;
+
while (__raw_readl((void __iomem *)SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_STATUS)
& SIRFSOC_UART1_TXFIFO_FULL)
barrier();
diff --git a/arch/arm/mach-prima2/irq.c b/arch/arm/mach-prima2/irq.c
index 7dee9176e77..6c0f3e9c43f 100644
--- a/arch/arm/mach-prima2/irq.c
+++ b/arch/arm/mach-prima2/irq.c
@@ -9,17 +9,19 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <mach/hardware.h>
-#include <asm/mach/irq.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/irqdomain.h>
#include <linux/syscore_ops.h>
+#include <asm/mach/irq.h>
+#include <asm/exception.h>
+#include <mach/hardware.h>
#define SIRFSOC_INT_RISC_MASK0 0x0018
#define SIRFSOC_INT_RISC_MASK1 0x001C
#define SIRFSOC_INT_RISC_LEVEL0 0x0020
#define SIRFSOC_INT_RISC_LEVEL1 0x0024
+#define SIRFSOC_INIT_IRQ_ID 0x0038
void __iomem *sirfsoc_intc_base;
@@ -52,6 +54,16 @@ static __init void sirfsoc_irq_init(void)
writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK1);
}
+asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat, irqnr;
+
+ irqstat = readl_relaxed(sirfsoc_intc_base + SIRFSOC_INIT_IRQ_ID);
+ irqnr = irqstat & 0xff;
+
+ handle_IRQ(irqnr, regs);
+}
+
static struct of_device_id intc_ids[] = {
{ .compatible = "sirf,prima2-intc" },
{},
diff --git a/arch/arm/mach-prima2/l2x0.c b/arch/arm/mach-prima2/l2x0.c
index c99837797d7..cbcbe9cb094 100644
--- a/arch/arm/mach-prima2/l2x0.c
+++ b/arch/arm/mach-prima2/l2x0.c
@@ -11,19 +11,38 @@
#include <linux/of.h>
#include <asm/hardware/cache-l2x0.h>
-static struct of_device_id prima2_l2x0_ids[] = {
- { .compatible = "sirf,prima2-pl310-cache" },
+struct l2x0_aux
+{
+ u32 val;
+ u32 mask;
+};
+
+static struct l2x0_aux prima2_l2x0_aux __initconst = {
+ .val = 2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT,
+ .mask = 0,
+};
+
+static struct l2x0_aux marco_l2x0_aux __initconst = {
+ .val = (2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) |
+ (1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT),
+ .mask = L2X0_AUX_CTRL_MASK,
+};
+
+static struct of_device_id sirf_l2x0_ids[] __initconst = {
+ { .compatible = "sirf,prima2-pl310-cache", .data = &prima2_l2x0_aux, },
+ { .compatible = "sirf,marco-pl310-cache", .data = &marco_l2x0_aux, },
{},
};
static int __init sirfsoc_l2x0_init(void)
{
struct device_node *np;
+ const struct l2x0_aux *aux;
- np = of_find_matching_node(NULL, prima2_l2x0_ids);
+ np = of_find_matching_node(NULL, sirf_l2x0_ids);
if (np) {
- pr_info("Initializing prima2 L2 cache\n");
- return l2x0_of_init(0x40000, 0);
+ aux = of_match_node(sirf_l2x0_ids, np)->data;
+ return l2x0_of_init(aux->val, aux->mask);
}
return 0;
diff --git a/arch/arm/mach-prima2/platsmp.c b/arch/arm/mach-prima2/platsmp.c
new file mode 100644
index 00000000000..2395022bc73
--- /dev/null
+++ b/arch/arm/mach-prima2/platsmp.c
@@ -0,0 +1,163 @@
+/*
+ * 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 <asm/hardware/gic.h>
+#include <mach/map.h>
+
+#include "common.h"
+
+static void __iomem *scu_base;
+static void __iomem *rsc_base;
+
+static DEFINE_SPINLOCK(boot_lock);
+
+static struct map_desc scu_io_desc __initdata = {
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+};
+
+void __init sirfsoc_map_scu(void)
+{
+ unsigned long base;
+
+ /* Get SCU base */
+ asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base));
+
+ scu_io_desc.virtual = SIRFSOC_VA(base);
+ scu_io_desc.pfn = __phys_to_pfn(base);
+ iotable_init(&scu_io_desc, 1);
+
+ scu_base = (void __iomem *)SIRFSOC_VA(base);
+}
+
+static void __cpuinit sirfsoc_secondary_init(unsigned int cpu)
+{
+ /*
+ * if any interrupts are already enabled for the primary
+ * core (e.g. timer irq), then they will not have been enabled
+ * for us: do so
+ */
+ gic_secondary_init(0);
+
+ /*
+ * 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.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+static struct of_device_id rsc_ids[] = {
+ { .compatible = "sirf,marco-rsc" },
+ {},
+};
+
+static int __cpuinit sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, rsc_ids);
+ if (!np)
+ return -ENODEV;
+
+ rsc_base = of_iomap(np, 0);
+ if (!rsc_base)
+ return -ENOMEM;
+
+ /*
+ * write the address of secondary startup into the sram register
+ * at offset 0x2C, then write the magic number 0x3CAF5D62 to the
+ * RSC register at offset 0x28, which is what boot rom code is
+ * waiting for. This would wake up the secondary core from WFE
+ */
+#define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x2C
+ __raw_writel(virt_to_phys(sirfsoc_secondary_startup),
+ rsc_base + SIRFSOC_CPU1_JUMPADDR_OFFSET);
+
+#define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x28
+ __raw_writel(0x3CAF5D62,
+ rsc_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 pen_release.
+ *
+ * Note that "pen_release" is the hardware CPU ID, whereas
+ * "cpu" is Linux's internal ID.
+ */
+ pen_release = cpu_logical_map(cpu);
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+
+ /*
+ * 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
+ */
+ spin_unlock(&boot_lock);
+
+ return pen_release != -1 ? -ENOSYS : 0;
+}
+
+static void __init sirfsoc_smp_init_cpus(void)
+{
+ set_smp_cross_call(gic_raise_softirq);
+}
+
+static void __init sirfsoc_smp_prepare_cpus(unsigned int max_cpus)
+{
+ scu_enable(scu_base);
+}
+
+struct smp_operations sirfsoc_smp_ops __initdata = {
+ .smp_init_cpus = sirfsoc_smp_init_cpus,
+ .smp_prepare_cpus = sirfsoc_smp_prepare_cpus,
+ .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/rstc.c b/arch/arm/mach-prima2/rstc.c
index 762adb73ab7..435019ca0a4 100644
--- a/arch/arm/mach-prima2/rstc.c
+++ b/arch/arm/mach-prima2/rstc.c
@@ -19,6 +19,7 @@ static DEFINE_MUTEX(rstc_lock);
static struct of_device_id rstc_ids[] = {
{ .compatible = "sirf,prima2-rstc" },
+ { .compatible = "sirf,marco-rstc" },
{},
};
@@ -42,27 +43,37 @@ early_initcall(sirfsoc_of_rstc_init);
int sirfsoc_reset_device(struct device *dev)
{
- const unsigned int *prop = of_get_property(dev->of_node, "reset-bit", NULL);
- unsigned int reset_bit;
+ u32 reset_bit;
- if (!prop)
- return -ENODEV;
-
- reset_bit = be32_to_cpup(prop);
+ if (of_property_read_u32(dev->of_node, "reset-bit", &reset_bit))
+ 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) | reset_bit,
- sirfsoc_rstc_base + (reset_bit / 32) * 4);
- msleep(10);
- writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) & ~reset_bit,
- sirfsoc_rstc_base + (reset_bit / 32) * 4);
+ if (of_device_is_compatible(dev->of_node, "sirf,prima2-rstc")) {
+ /*
+ * 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) | reset_bit,
+ sirfsoc_rstc_base + (reset_bit / 32) * 4);
+ msleep(10);
+ writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) & ~reset_bit,
+ sirfsoc_rstc_base + (reset_bit / 32) * 4);
+ } else {
+ /*
+ * For MARCO and POLO
+ * Writing 1 to SET register resets corresponding block. Writing 1 to CLEAR
+ * register 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(reset_bit, sirfsoc_rstc_base + (reset_bit / 32) * 8);
+ msleep(10);
+ writel(reset_bit, sirfsoc_rstc_base + (reset_bit / 32) * 8 + 4);
+ }
mutex_unlock(&rstc_lock);
diff --git a/arch/arm/mach-prima2/rtciobrg.c b/arch/arm/mach-prima2/rtciobrg.c
index 55735360213..9f2da2eec4d 100644
--- a/arch/arm/mach-prima2/rtciobrg.c
+++ b/arch/arm/mach-prima2/rtciobrg.c
@@ -104,6 +104,7 @@ EXPORT_SYMBOL_GPL(sirfsoc_rtc_iobrg_writel);
static const struct of_device_id rtciobrg_ids[] = {
{ .compatible = "sirf,prima2-rtciobg" },
+ { .compatible = "sirf,marco-rtciobg" },
{}
};
diff --git a/arch/arm/mach-prima2/timer-marco.c b/arch/arm/mach-prima2/timer-marco.c
new file mode 100644
index 00000000000..f4eea2e97eb
--- /dev/null
+++ b/arch/arm/mach-prima2/timer-marco.c
@@ -0,0 +1,316 @@
+/*
+ * System timer 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/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <asm/sched_clock.h>
+#include <asm/localtimer.h>
+#include <asm/mach/time.h>
+
+#include "common.h"
+
+#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
+#define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004
+#define SIRFSOC_TIMER_MATCH_0 0x0018
+#define SIRFSOC_TIMER_MATCH_1 0x001c
+#define SIRFSOC_TIMER_COUNTER_0 0x0048
+#define SIRFSOC_TIMER_COUNTER_1 0x004c
+#define SIRFSOC_TIMER_INTR_STATUS 0x0060
+#define SIRFSOC_TIMER_WATCHDOG_EN 0x0064
+#define SIRFSOC_TIMER_64COUNTER_CTRL 0x0068
+#define SIRFSOC_TIMER_64COUNTER_LO 0x006c
+#define SIRFSOC_TIMER_64COUNTER_HI 0x0070
+#define SIRFSOC_TIMER_64COUNTER_LOAD_LO 0x0074
+#define SIRFSOC_TIMER_64COUNTER_LOAD_HI 0x0078
+#define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO 0x007c
+#define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI 0x0080
+
+#define SIRFSOC_TIMER_REG_CNT 6
+
+static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
+ SIRFSOC_TIMER_WATCHDOG_EN,
+ SIRFSOC_TIMER_32COUNTER_0_CTRL,
+ SIRFSOC_TIMER_32COUNTER_1_CTRL,
+ SIRFSOC_TIMER_64COUNTER_CTRL,
+ SIRFSOC_TIMER_64COUNTER_RLATCHED_LO,
+ SIRFSOC_TIMER_64COUNTER_RLATCHED_HI,
+};
+
+static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
+
+static void __iomem *sirfsoc_timer_base;
+static void __init sirfsoc_of_timer_map(void);
+
+/* disable count and interrupt */
+static inline void sirfsoc_timer_count_disable(int idx)
+{
+ writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7,
+ sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
+}
+
+/* enable count and interrupt */
+static inline void sirfsoc_timer_count_enable(int idx)
+{
+ writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7,
+ sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
+}
+
+/* timer interrupt handler */
+static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *ce = dev_id;
+ int cpu = smp_processor_id();
+
+ /* clear timer interrupt */
+ writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
+
+ if (ce->mode == CLOCK_EVT_MODE_ONESHOT)
+ sirfsoc_timer_count_disable(cpu);
+
+ ce->event_handler(ce);
+
+ return IRQ_HANDLED;
+}
+
+/* read 64-bit timer counter */
+static cycle_t sirfsoc_timer_read(struct clocksource *cs)
+{
+ u64 cycles;
+
+ writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
+ BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
+
+ cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI);
+ cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO);
+
+ return cycles;
+}
+
+static int sirfsoc_timer_set_next_event(unsigned long delta,
+ struct clock_event_device *ce)
+{
+ int cpu = smp_processor_id();
+
+ writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
+ 4 * cpu);
+ writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
+ 4 * cpu);
+
+ /* enable the tick */
+ sirfsoc_timer_count_enable(cpu);
+
+ return 0;
+}
+
+static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *ce)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_ONESHOT:
+ /* enable in set_next_event */
+ break;
+ default:
+ break;
+ }
+
+ sirfsoc_timer_count_disable(smp_processor_id());
+}
+
+static void sirfsoc_clocksource_suspend(struct clocksource *cs)
+{
+ int i;
+
+ for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
+ sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
+}
+
+static void sirfsoc_clocksource_resume(struct clocksource *cs)
+{
+ int i;
+
+ for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
+ writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
+
+ writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
+ sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
+ writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
+ sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
+
+ writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
+ BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
+}
+
+static struct clock_event_device sirfsoc_clockevent = {
+ .name = "sirfsoc_clockevent",
+ .rating = 200,
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = sirfsoc_timer_set_mode,
+ .set_next_event = sirfsoc_timer_set_next_event,
+};
+
+static struct clocksource sirfsoc_clocksource = {
+ .name = "sirfsoc_clocksource",
+ .rating = 200,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .read = sirfsoc_timer_read,
+ .suspend = sirfsoc_clocksource_suspend,
+ .resume = sirfsoc_clocksource_resume,
+};
+
+static struct irqaction sirfsoc_timer_irq = {
+ .name = "sirfsoc_timer0",
+ .flags = IRQF_TIMER | IRQF_NOBALANCING,
+ .handler = sirfsoc_timer_interrupt,
+ .dev_id = &sirfsoc_clockevent,
+};
+
+#ifdef CONFIG_LOCAL_TIMERS
+
+static struct irqaction sirfsoc_timer1_irq = {
+ .name = "sirfsoc_timer1",
+ .flags = IRQF_TIMER | IRQF_NOBALANCING,
+ .handler = sirfsoc_timer_interrupt,
+};
+
+static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce)
+{
+ /* Use existing clock_event for cpu 0 */
+ if (!smp_processor_id())
+ return 0;
+
+ ce->irq = sirfsoc_timer1_irq.irq;
+ ce->name = "local_timer";
+ ce->features = sirfsoc_clockevent.features;
+ ce->rating = sirfsoc_clockevent.rating;
+ ce->set_mode = sirfsoc_timer_set_mode;
+ ce->set_next_event = sirfsoc_timer_set_next_event;
+ ce->shift = sirfsoc_clockevent.shift;
+ ce->mult = sirfsoc_clockevent.mult;
+ ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns;
+ ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns;
+
+ sirfsoc_timer1_irq.dev_id = ce;
+ BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq));
+ irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1));
+
+ clockevents_register_device(ce);
+ return 0;
+}
+
+static void sirfsoc_local_timer_stop(struct clock_event_device *ce)
+{
+ sirfsoc_timer_count_disable(1);
+
+ remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
+}
+
+static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = {
+ .setup = sirfsoc_local_timer_setup,
+ .stop = sirfsoc_local_timer_stop,
+};
+#endif /* CONFIG_LOCAL_TIMERS */
+
+static void __init sirfsoc_clockevent_init(void)
+{
+ clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60);
+
+ sirfsoc_clockevent.max_delta_ns =
+ clockevent_delta2ns(-2, &sirfsoc_clockevent);
+ sirfsoc_clockevent.min_delta_ns =
+ clockevent_delta2ns(2, &sirfsoc_clockevent);
+
+ sirfsoc_clockevent.cpumask = cpumask_of(0);
+ clockevents_register_device(&sirfsoc_clockevent);
+#ifdef CONFIG_LOCAL_TIMERS
+ local_timer_register(&sirfsoc_local_timer_ops);
+#endif
+}
+
+/* initialize the kernel jiffy timer source */
+void __init sirfsoc_marco_timer_init(void)
+{
+ unsigned long rate;
+ u32 timer_div;
+ struct clk *clk;
+
+ /* initialize clocking early, we want to set the OS timer */
+ sirfsoc_of_clk_init();
+
+ /* timer's input clock is io clock */
+ clk = clk_get_sys("io", NULL);
+
+ BUG_ON(IS_ERR(clk));
+ rate = clk_get_rate(clk);
+
+ BUG_ON(rate < CLOCK_TICK_RATE);
+ BUG_ON(rate % CLOCK_TICK_RATE);
+
+ sirfsoc_of_timer_map();
+
+ /* Initialize the timer dividers */
+ timer_div = rate / CLOCK_TICK_RATE - 1;
+ writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
+ writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
+ writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
+
+ /* Initialize timer counters to 0 */
+ writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
+ writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
+ writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
+ BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
+ writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
+ writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
+
+ /* Clear all interrupts */
+ writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
+
+ BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
+
+ BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
+
+ sirfsoc_clockevent_init();
+}
+
+static struct of_device_id timer_ids[] = {
+ { .compatible = "sirf,marco-tick" },
+ {},
+};
+
+static void __init sirfsoc_of_timer_map(void)
+{
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, timer_ids);
+ if (!np)
+ return;
+ sirfsoc_timer_base = of_iomap(np, 0);
+ if (!sirfsoc_timer_base)
+ panic("unable to map timer cpu registers\n");
+
+ sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
+ if (!sirfsoc_timer_irq.irq)
+ panic("No irq passed for timer0 via DT\n");
+
+#ifdef CONFIG_LOCAL_TIMERS
+ sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1);
+ if (!sirfsoc_timer1_irq.irq)
+ panic("No irq passed for timer1 via DT\n");
+#endif
+
+ of_node_put(np);
+}
diff --git a/arch/arm/mach-prima2/timer.c b/arch/arm/mach-prima2/timer-prima2.c
index a7a2c199c3e..6da584f8a94 100644
--- a/arch/arm/mach-prima2/timer.c
+++ b/arch/arm/mach-prima2/timer-prima2.c
@@ -181,7 +181,7 @@ static void __init sirfsoc_clockevent_init(void)
}
/* initialize the kernel jiffy timer source */
-void __init sirfsoc_timer_init(void)
+void __init sirfsoc_prima2_timer_init(void)
{
unsigned long rate;
struct clk *clk;
@@ -220,14 +220,14 @@ static struct of_device_id timer_ids[] = {
{},
};
-void __init sirfsoc_of_timer_map(void)
+static void __init sirfsoc_of_timer_map(void)
{
struct device_node *np;
const unsigned int *intspec;
np = of_find_matching_node(NULL, timer_ids);
if (!np)
- panic("unable to find compatible timer node in dtb\n");
+ return;
sirfsoc_timer_base = of_iomap(np, 0);
if (!sirfsoc_timer_base)
panic("unable to map timer cpu registers\n");