aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorBarry Song <Baohua.Song@csr.com>2012-12-20 06:37:32 -0500
committerBarry Song <Barry.Song@csr.com>2013-01-22 06:53:27 -0500
commit4898de3d15d8ba34aa7a1b0f753a476d52ebdf92 (patch)
treee2606fb82bab7bcbe0503404ecd67fd0d3d47427 /arch
parentf2a94192d953990c5c928f52dd4122a67f93b980 (diff)
ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures
this patch adds tick timer, smp entries and generic DT machine for SiRFmarco dual-core SMP chips. with the added marco, we change the defconfig, using the same defconfig, we get a zImage which can work on both prima2 and marco. Signed-off-by: Barry Song <Baohua.Song@csr.com> Cc: Mark Rutland <mark.rutland@arm.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/boot/dts/Makefile1
-rw-r--r--arch/arm/configs/prima2_defconfig3
-rw-r--r--arch/arm/mach-prima2/Kconfig10
-rw-r--r--arch/arm/mach-prima2/Makefile3
-rw-r--r--arch/arm/mach-prima2/common.c40
-rw-r--r--arch/arm/mach-prima2/common.h11
-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/platsmp.c163
-rw-r--r--arch/arm/mach-prima2/timer-marco.c316
10 files changed, 666 insertions, 1 deletions
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index e44da40d984f..6af9901d3d2f 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -73,6 +73,7 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-dns320.dtb \
73 kirkwood-ts219-6281.dtb \ 73 kirkwood-ts219-6281.dtb \
74 kirkwood-ts219-6282.dtb \ 74 kirkwood-ts219-6282.dtb \
75 kirkwood-openblocks_a6.dtb 75 kirkwood-openblocks_a6.dtb
76dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb
76dtb-$(CONFIG_ARCH_MSM) += msm8660-surf.dtb \ 77dtb-$(CONFIG_ARCH_MSM) += msm8660-surf.dtb \
77 msm8960-cdp.dtb 78 msm8960-cdp.dtb
78dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ 79dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \
diff --git a/arch/arm/configs/prima2_defconfig b/arch/arm/configs/prima2_defconfig
index 6a936c7c078a..002a1ceadceb 100644
--- a/arch/arm/configs/prima2_defconfig
+++ b/arch/arm/configs/prima2_defconfig
@@ -11,6 +11,9 @@ CONFIG_PARTITION_ADVANCED=y
11CONFIG_BSD_DISKLABEL=y 11CONFIG_BSD_DISKLABEL=y
12CONFIG_SOLARIS_X86_PARTITION=y 12CONFIG_SOLARIS_X86_PARTITION=y
13CONFIG_ARCH_SIRF=y 13CONFIG_ARCH_SIRF=y
14# CONFIG_SWP_EMULATE is not set
15CONFIG_SMP=y
16CONFIG_SCHED_MC=y
14CONFIG_PREEMPT=y 17CONFIG_PREEMPT=y
15CONFIG_AEABI=y 18CONFIG_AEABI=y
16CONFIG_KEXEC=y 19CONFIG_KEXEC=y
diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig
index 558ccfb8d458..4f7379fe01e2 100644
--- a/arch/arm/mach-prima2/Kconfig
+++ b/arch/arm/mach-prima2/Kconfig
@@ -11,6 +11,16 @@ config ARCH_PRIMA2
11 help 11 help
12 Support for CSR SiRFSoC ARM Cortex A9 Platform 12 Support for CSR SiRFSoC ARM Cortex A9 Platform
13 13
14config ARCH_MARCO
15 bool "CSR SiRFSoC MARCO ARM Cortex A9 Platform"
16 default y
17 select ARM_GIC
18 select CPU_V7
19 select HAVE_SMP
20 select SMP_ON_UP
21 help
22 Support for CSR SiRFSoC ARM Cortex A9 Platform
23
14endmenu 24endmenu
15 25
16config SIRF_IRQ 26config SIRF_IRQ
diff --git a/arch/arm/mach-prima2/Makefile b/arch/arm/mach-prima2/Makefile
index 0007a6ec78f2..bfe360cbd177 100644
--- a/arch/arm/mach-prima2/Makefile
+++ b/arch/arm/mach-prima2/Makefile
@@ -5,4 +5,7 @@ obj-$(CONFIG_DEBUG_LL) += lluart.o
5obj-$(CONFIG_CACHE_L2X0) += l2x0.o 5obj-$(CONFIG_CACHE_L2X0) += l2x0.o
6obj-$(CONFIG_SUSPEND) += pm.o sleep.o 6obj-$(CONFIG_SUSPEND) += pm.o sleep.o
7obj-$(CONFIG_SIRF_IRQ) += irq.o 7obj-$(CONFIG_SIRF_IRQ) += irq.o
8obj-$(CONFIG_SMP) += platsmp.o headsmp.o
9obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
8obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o 10obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
11obj-$(CONFIG_ARCH_MARCO) += timer-marco.o
diff --git a/arch/arm/mach-prima2/common.c b/arch/arm/mach-prima2/common.c
index 99f9c7e391f5..00a65649a7e2 100644
--- a/arch/arm/mach-prima2/common.c
+++ b/arch/arm/mach-prima2/common.c
@@ -8,9 +8,11 @@
8 8
9#include <linux/init.h> 9#include <linux/init.h>
10#include <linux/kernel.h> 10#include <linux/kernel.h>
11#include <linux/of_irq.h>
11#include <asm/sizes.h> 12#include <asm/sizes.h>
12#include <asm/mach-types.h> 13#include <asm/mach-types.h>
13#include <asm/mach/arch.h> 14#include <asm/mach/arch.h>
15#include <asm/hardware/gic.h>
14#include <linux/of.h> 16#include <linux/of.h>
15#include <linux/of_platform.h> 17#include <linux/of_platform.h>
16#include "common.h" 18#include "common.h"
@@ -30,6 +32,12 @@ void __init sirfsoc_init_late(void)
30 sirfsoc_pm_init(); 32 sirfsoc_pm_init();
31} 33}
32 34
35static __init void sirfsoc_map_io(void)
36{
37 sirfsoc_map_lluart();
38 sirfsoc_map_scu();
39}
40
33#ifdef CONFIG_ARCH_PRIMA2 41#ifdef CONFIG_ARCH_PRIMA2
34static const char *prima2_dt_match[] __initdata = { 42static const char *prima2_dt_match[] __initdata = {
35 "sirf,prima2", 43 "sirf,prima2",
@@ -38,7 +46,7 @@ static const char *prima2_dt_match[] __initdata = {
38 46
39DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)") 47DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
40 /* Maintainer: Barry Song <baohua.song@csr.com> */ 48 /* Maintainer: Barry Song <baohua.song@csr.com> */
41 .map_io = sirfsoc_map_lluart, 49 .map_io = sirfsoc_map_io,
42 .init_irq = sirfsoc_of_irq_init, 50 .init_irq = sirfsoc_of_irq_init,
43 .init_time = sirfsoc_prima2_timer_init, 51 .init_time = sirfsoc_prima2_timer_init,
44#ifdef CONFIG_MULTI_IRQ_HANDLER 52#ifdef CONFIG_MULTI_IRQ_HANDLER
@@ -51,3 +59,33 @@ DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
51 .restart = sirfsoc_restart, 59 .restart = sirfsoc_restart,
52MACHINE_END 60MACHINE_END
53#endif 61#endif
62
63#ifdef CONFIG_ARCH_MARCO
64static const struct of_device_id marco_irq_match[] __initconst = {
65 { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, },
66 { /* sentinel */ }
67};
68
69static void __init marco_init_irq(void)
70{
71 of_irq_init(marco_irq_match);
72}
73
74static const char *marco_dt_match[] __initdata = {
75 "sirf,marco",
76 NULL
77};
78
79DT_MACHINE_START(MARCO_DT, "Generic MARCO (Flattened Device Tree)")
80 /* Maintainer: Barry Song <baohua.song@csr.com> */
81 .smp = smp_ops(sirfsoc_smp_ops),
82 .map_io = sirfsoc_map_io,
83 .init_irq = marco_init_irq,
84 .init_time = sirfsoc_marco_timer_init,
85 .handle_irq = gic_handle_irq,
86 .init_machine = sirfsoc_mach_init,
87 .init_late = sirfsoc_init_late,
88 .dt_compat = marco_dt_match,
89 .restart = sirfsoc_restart,
90MACHINE_END
91#endif
diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h
index a4f91a6de55e..b7c26b62e4a7 100644
--- a/arch/arm/mach-prima2/common.h
+++ b/arch/arm/mach-prima2/common.h
@@ -14,6 +14,11 @@
14#include <asm/exception.h> 14#include <asm/exception.h>
15 15
16extern void sirfsoc_prima2_timer_init(void); 16extern void sirfsoc_prima2_timer_init(void);
17extern void sirfsoc_marco_timer_init(void);
18
19extern struct smp_operations sirfsoc_smp_ops;
20extern void sirfsoc_secondary_startup(void);
21extern void sirfsoc_cpu_die(unsigned int cpu);
17 22
18extern void __init sirfsoc_of_irq_init(void); 23extern void __init sirfsoc_of_irq_init(void);
19extern void __init sirfsoc_of_clk_init(void); 24extern void __init sirfsoc_of_clk_init(void);
@@ -26,6 +31,12 @@ static inline void sirfsoc_map_lluart(void) {}
26extern void __init sirfsoc_map_lluart(void); 31extern void __init sirfsoc_map_lluart(void);
27#endif 32#endif
28 33
34#ifndef CONFIG_SMP
35static inline void sirfsoc_map_scu(void) {}
36#else
37extern void sirfsoc_map_scu(void);
38#endif
39
29#ifdef CONFIG_SUSPEND 40#ifdef CONFIG_SUSPEND
30extern int sirfsoc_pm_init(void); 41extern int sirfsoc_pm_init(void);
31#else 42#else
diff --git a/arch/arm/mach-prima2/headsmp.S b/arch/arm/mach-prima2/headsmp.S
new file mode 100644
index 000000000000..6ec19d51a271
--- /dev/null
+++ b/arch/arm/mach-prima2/headsmp.S
@@ -0,0 +1,79 @@
1/*
2 * Entry of the second core for CSR Marco dual-core SMP SoCs
3 *
4 * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
5 *
6 * Licensed under GPLv2 or later.
7 */
8
9#include <linux/linkage.h>
10#include <linux/init.h>
11
12 __INIT
13/*
14 * Cold boot and hardware reset show different behaviour,
15 * system will be always panic if we warm-reset the board
16 * Here we invalidate L1 of CPU1 to make sure there isn't
17 * uninitialized data written into memory later
18 */
19ENTRY(v7_invalidate_l1)
20 mov r0, #0
21 mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache
22 mcr p15, 2, r0, c0, c0, 0
23 mrc p15, 1, r0, c0, c0, 0
24
25 ldr r1, =0x7fff
26 and r2, r1, r0, lsr #13
27
28 ldr r1, =0x3ff
29
30 and r3, r1, r0, lsr #3 @ NumWays - 1
31 add r2, r2, #1 @ NumSets
32
33 and r0, r0, #0x7
34 add r0, r0, #4 @ SetShift
35
36 clz r1, r3 @ WayShift
37 add r4, r3, #1 @ NumWays
381: sub r2, r2, #1 @ NumSets--
39 mov r3, r4 @ Temp = NumWays
402: subs r3, r3, #1 @ Temp--
41 mov r5, r3, lsl r1
42 mov r6, r2, lsl r0
43 orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
44 mcr p15, 0, r5, c7, c6, 2
45 bgt 2b
46 cmp r2, #0
47 bgt 1b
48 dsb
49 isb
50 mov pc, lr
51ENDPROC(v7_invalidate_l1)
52
53/*
54 * SIRFSOC specific entry point for secondary CPUs. This provides
55 * a "holding pen" into which all secondary cores are held until we're
56 * ready for them to initialise.
57 */
58ENTRY(sirfsoc_secondary_startup)
59 bl v7_invalidate_l1
60 mrc p15, 0, r0, c0, c0, 5
61 and r0, r0, #15
62 adr r4, 1f
63 ldmia r4, {r5, r6}
64 sub r4, r4, r5
65 add r6, r6, r4
66pen: ldr r7, [r6]
67 cmp r7, r0
68 bne pen
69
70 /*
71 * we've been released from the holding pen: secondary_stack
72 * should now contain the SVC stack for this core
73 */
74 b secondary_startup
75ENDPROC(sirfsoc_secondary_startup)
76
77 .align
781: .long .
79 .long pen_release
diff --git a/arch/arm/mach-prima2/hotplug.c b/arch/arm/mach-prima2/hotplug.c
new file mode 100644
index 000000000000..97c1ee586442
--- /dev/null
+++ b/arch/arm/mach-prima2/hotplug.c
@@ -0,0 +1,41 @@
1/*
2 * CPU hotplug support for CSR Marco dual-core SMP SoCs
3 *
4 * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
5 *
6 * Licensed under GPLv2 or later.
7 */
8
9#include <linux/kernel.h>
10#include <linux/errno.h>
11#include <linux/smp.h>
12
13#include <asm/cacheflush.h>
14#include <asm/smp_plat.h>
15
16static inline void platform_do_lowpower(unsigned int cpu)
17{
18 flush_cache_all();
19
20 /* we put the platform to just WFI */
21 for (;;) {
22 __asm__ __volatile__("dsb\n\t" "wfi\n\t"
23 : : : "memory");
24 if (pen_release == cpu_logical_map(cpu)) {
25 /*
26 * OK, proper wakeup, we're done
27 */
28 break;
29 }
30 }
31}
32
33/*
34 * platform-specific code to shutdown a CPU
35 *
36 * Called with IRQs disabled
37 */
38void sirfsoc_cpu_die(unsigned int cpu)
39{
40 platform_do_lowpower(cpu);
41}
diff --git a/arch/arm/mach-prima2/platsmp.c b/arch/arm/mach-prima2/platsmp.c
new file mode 100644
index 000000000000..2395022bc733
--- /dev/null
+++ b/arch/arm/mach-prima2/platsmp.c
@@ -0,0 +1,163 @@
1/*
2 * plat smp support for CSR Marco dual-core SMP SoCs
3 *
4 * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
5 *
6 * Licensed under GPLv2 or later.
7 */
8
9#include <linux/init.h>
10#include <linux/smp.h>
11#include <linux/delay.h>
12#include <linux/of.h>
13#include <linux/of_address.h>
14#include <asm/page.h>
15#include <asm/mach/map.h>
16#include <asm/smp_plat.h>
17#include <asm/smp_scu.h>
18#include <asm/cacheflush.h>
19#include <asm/cputype.h>
20#include <asm/hardware/gic.h>
21#include <mach/map.h>
22
23#include "common.h"
24
25static void __iomem *scu_base;
26static void __iomem *rsc_base;
27
28static DEFINE_SPINLOCK(boot_lock);
29
30static struct map_desc scu_io_desc __initdata = {
31 .length = SZ_4K,
32 .type = MT_DEVICE,
33};
34
35void __init sirfsoc_map_scu(void)
36{
37 unsigned long base;
38
39 /* Get SCU base */
40 asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base));
41
42 scu_io_desc.virtual = SIRFSOC_VA(base);
43 scu_io_desc.pfn = __phys_to_pfn(base);
44 iotable_init(&scu_io_desc, 1);
45
46 scu_base = (void __iomem *)SIRFSOC_VA(base);
47}
48
49static void __cpuinit sirfsoc_secondary_init(unsigned int cpu)
50{
51 /*
52 * if any interrupts are already enabled for the primary
53 * core (e.g. timer irq), then they will not have been enabled
54 * for us: do so
55 */
56 gic_secondary_init(0);
57
58 /*
59 * let the primary processor know we're out of the
60 * pen, then head off into the C entry point
61 */
62 pen_release = -1;
63 smp_wmb();
64
65 /*
66 * Synchronise with the boot thread.
67 */
68 spin_lock(&boot_lock);
69 spin_unlock(&boot_lock);
70}
71
72static struct of_device_id rsc_ids[] = {
73 { .compatible = "sirf,marco-rsc" },
74 {},
75};
76
77static int __cpuinit sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle)
78{
79 unsigned long timeout;
80 struct device_node *np;
81
82 np = of_find_matching_node(NULL, rsc_ids);
83 if (!np)
84 return -ENODEV;
85
86 rsc_base = of_iomap(np, 0);
87 if (!rsc_base)
88 return -ENOMEM;
89
90 /*
91 * write the address of secondary startup into the sram register
92 * at offset 0x2C, then write the magic number 0x3CAF5D62 to the
93 * RSC register at offset 0x28, which is what boot rom code is
94 * waiting for. This would wake up the secondary core from WFE
95 */
96#define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x2C
97 __raw_writel(virt_to_phys(sirfsoc_secondary_startup),
98 rsc_base + SIRFSOC_CPU1_JUMPADDR_OFFSET);
99
100#define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x28
101 __raw_writel(0x3CAF5D62,
102 rsc_base + SIRFSOC_CPU1_WAKEMAGIC_OFFSET);
103
104 /* make sure write buffer is drained */
105 mb();
106
107 spin_lock(&boot_lock);
108
109 /*
110 * The secondary processor is waiting to be released from
111 * the holding pen - release it, then wait for it to flag
112 * that it has been released by resetting pen_release.
113 *
114 * Note that "pen_release" is the hardware CPU ID, whereas
115 * "cpu" is Linux's internal ID.
116 */
117 pen_release = cpu_logical_map(cpu);
118 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
119 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
120
121 /*
122 * Send the secondary CPU SEV, thereby causing the boot monitor to read
123 * the JUMPADDR and WAKEMAGIC, and branch to the address found there.
124 */
125 dsb_sev();
126
127 timeout = jiffies + (1 * HZ);
128 while (time_before(jiffies, timeout)) {
129 smp_rmb();
130 if (pen_release == -1)
131 break;
132
133 udelay(10);
134 }
135
136 /*
137 * now the secondary core is starting up let it run its
138 * calibrations, then wait for it to finish
139 */
140 spin_unlock(&boot_lock);
141
142 return pen_release != -1 ? -ENOSYS : 0;
143}
144
145static void __init sirfsoc_smp_init_cpus(void)
146{
147 set_smp_cross_call(gic_raise_softirq);
148}
149
150static void __init sirfsoc_smp_prepare_cpus(unsigned int max_cpus)
151{
152 scu_enable(scu_base);
153}
154
155struct smp_operations sirfsoc_smp_ops __initdata = {
156 .smp_init_cpus = sirfsoc_smp_init_cpus,
157 .smp_prepare_cpus = sirfsoc_smp_prepare_cpus,
158 .smp_secondary_init = sirfsoc_secondary_init,
159 .smp_boot_secondary = sirfsoc_boot_secondary,
160#ifdef CONFIG_HOTPLUG_CPU
161 .cpu_die = sirfsoc_cpu_die,
162#endif
163};
diff --git a/arch/arm/mach-prima2/timer-marco.c b/arch/arm/mach-prima2/timer-marco.c
new file mode 100644
index 000000000000..f4eea2e97eb0
--- /dev/null
+++ b/arch/arm/mach-prima2/timer-marco.c
@@ -0,0 +1,316 @@
1/*
2 * System timer for CSR SiRFprimaII
3 *
4 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
5 *
6 * Licensed under GPLv2 or later.
7 */
8
9#include <linux/kernel.h>
10#include <linux/interrupt.h>
11#include <linux/clockchips.h>
12#include <linux/clocksource.h>
13#include <linux/bitops.h>
14#include <linux/irq.h>
15#include <linux/clk.h>
16#include <linux/slab.h>
17#include <linux/of.h>
18#include <linux/of_irq.h>
19#include <linux/of_address.h>
20#include <asm/sched_clock.h>
21#include <asm/localtimer.h>
22#include <asm/mach/time.h>
23
24#include "common.h"
25
26#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
27#define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004
28#define SIRFSOC_TIMER_MATCH_0 0x0018
29#define SIRFSOC_TIMER_MATCH_1 0x001c
30#define SIRFSOC_TIMER_COUNTER_0 0x0048
31#define SIRFSOC_TIMER_COUNTER_1 0x004c
32#define SIRFSOC_TIMER_INTR_STATUS 0x0060
33#define SIRFSOC_TIMER_WATCHDOG_EN 0x0064
34#define SIRFSOC_TIMER_64COUNTER_CTRL 0x0068
35#define SIRFSOC_TIMER_64COUNTER_LO 0x006c
36#define SIRFSOC_TIMER_64COUNTER_HI 0x0070
37#define SIRFSOC_TIMER_64COUNTER_LOAD_LO 0x0074
38#define SIRFSOC_TIMER_64COUNTER_LOAD_HI 0x0078
39#define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO 0x007c
40#define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI 0x0080
41
42#define SIRFSOC_TIMER_REG_CNT 6
43
44static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
45 SIRFSOC_TIMER_WATCHDOG_EN,
46 SIRFSOC_TIMER_32COUNTER_0_CTRL,
47 SIRFSOC_TIMER_32COUNTER_1_CTRL,
48 SIRFSOC_TIMER_64COUNTER_CTRL,
49 SIRFSOC_TIMER_64COUNTER_RLATCHED_LO,
50 SIRFSOC_TIMER_64COUNTER_RLATCHED_HI,
51};
52
53static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
54
55static void __iomem *sirfsoc_timer_base;
56static void __init sirfsoc_of_timer_map(void);
57
58/* disable count and interrupt */
59static inline void sirfsoc_timer_count_disable(int idx)
60{
61 writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7,
62 sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
63}
64
65/* enable count and interrupt */
66static inline void sirfsoc_timer_count_enable(int idx)
67{
68 writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7,
69 sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
70}
71
72/* timer interrupt handler */
73static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
74{
75 struct clock_event_device *ce = dev_id;
76 int cpu = smp_processor_id();
77
78 /* clear timer interrupt */
79 writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
80
81 if (ce->mode == CLOCK_EVT_MODE_ONESHOT)
82 sirfsoc_timer_count_disable(cpu);
83
84 ce->event_handler(ce);
85
86 return IRQ_HANDLED;
87}
88
89/* read 64-bit timer counter */
90static cycle_t sirfsoc_timer_read(struct clocksource *cs)
91{
92 u64 cycles;
93
94 writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
95 BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
96
97 cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI);
98 cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO);
99
100 return cycles;
101}
102
103static int sirfsoc_timer_set_next_event(unsigned long delta,
104 struct clock_event_device *ce)
105{
106 int cpu = smp_processor_id();
107
108 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
109 4 * cpu);
110 writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
111 4 * cpu);
112
113 /* enable the tick */
114 sirfsoc_timer_count_enable(cpu);
115
116 return 0;
117}
118
119static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
120 struct clock_event_device *ce)
121{
122 switch (mode) {
123 case CLOCK_EVT_MODE_ONESHOT:
124 /* enable in set_next_event */
125 break;
126 default:
127 break;
128 }
129
130 sirfsoc_timer_count_disable(smp_processor_id());
131}
132
133static void sirfsoc_clocksource_suspend(struct clocksource *cs)
134{
135 int i;
136
137 for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
138 sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
139}
140
141static void sirfsoc_clocksource_resume(struct clocksource *cs)
142{
143 int i;
144
145 for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
146 writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
147
148 writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
149 sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
150 writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
151 sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
152
153 writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
154 BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
155}
156
157static struct clock_event_device sirfsoc_clockevent = {
158 .name = "sirfsoc_clockevent",
159 .rating = 200,
160 .features = CLOCK_EVT_FEAT_ONESHOT,
161 .set_mode = sirfsoc_timer_set_mode,
162 .set_next_event = sirfsoc_timer_set_next_event,
163};
164
165static struct clocksource sirfsoc_clocksource = {
166 .name = "sirfsoc_clocksource",
167 .rating = 200,
168 .mask = CLOCKSOURCE_MASK(64),
169 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
170 .read = sirfsoc_timer_read,
171 .suspend = sirfsoc_clocksource_suspend,
172 .resume = sirfsoc_clocksource_resume,
173};
174
175static struct irqaction sirfsoc_timer_irq = {
176 .name = "sirfsoc_timer0",
177 .flags = IRQF_TIMER | IRQF_NOBALANCING,
178 .handler = sirfsoc_timer_interrupt,
179 .dev_id = &sirfsoc_clockevent,
180};
181
182#ifdef CONFIG_LOCAL_TIMERS
183
184static struct irqaction sirfsoc_timer1_irq = {
185 .name = "sirfsoc_timer1",
186 .flags = IRQF_TIMER | IRQF_NOBALANCING,
187 .handler = sirfsoc_timer_interrupt,
188};
189
190static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce)
191{
192 /* Use existing clock_event for cpu 0 */
193 if (!smp_processor_id())
194 return 0;
195
196 ce->irq = sirfsoc_timer1_irq.irq;
197 ce->name = "local_timer";
198 ce->features = sirfsoc_clockevent.features;
199 ce->rating = sirfsoc_clockevent.rating;
200 ce->set_mode = sirfsoc_timer_set_mode;
201 ce->set_next_event = sirfsoc_timer_set_next_event;
202 ce->shift = sirfsoc_clockevent.shift;
203 ce->mult = sirfsoc_clockevent.mult;
204 ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns;
205 ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns;
206
207 sirfsoc_timer1_irq.dev_id = ce;
208 BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq));
209 irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1));
210
211 clockevents_register_device(ce);
212 return 0;
213}
214
215static void sirfsoc_local_timer_stop(struct clock_event_device *ce)
216{
217 sirfsoc_timer_count_disable(1);
218
219 remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
220}
221
222static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = {
223 .setup = sirfsoc_local_timer_setup,
224 .stop = sirfsoc_local_timer_stop,
225};
226#endif /* CONFIG_LOCAL_TIMERS */
227
228static void __init sirfsoc_clockevent_init(void)
229{
230 clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60);
231
232 sirfsoc_clockevent.max_delta_ns =
233 clockevent_delta2ns(-2, &sirfsoc_clockevent);
234 sirfsoc_clockevent.min_delta_ns =
235 clockevent_delta2ns(2, &sirfsoc_clockevent);
236
237 sirfsoc_clockevent.cpumask = cpumask_of(0);
238 clockevents_register_device(&sirfsoc_clockevent);
239#ifdef CONFIG_LOCAL_TIMERS
240 local_timer_register(&sirfsoc_local_timer_ops);
241#endif
242}
243
244/* initialize the kernel jiffy timer source */
245void __init sirfsoc_marco_timer_init(void)
246{
247 unsigned long rate;
248 u32 timer_div;
249 struct clk *clk;
250
251 /* initialize clocking early, we want to set the OS timer */
252 sirfsoc_of_clk_init();
253
254 /* timer's input clock is io clock */
255 clk = clk_get_sys("io", NULL);
256
257 BUG_ON(IS_ERR(clk));
258 rate = clk_get_rate(clk);
259
260 BUG_ON(rate < CLOCK_TICK_RATE);
261 BUG_ON(rate % CLOCK_TICK_RATE);
262
263 sirfsoc_of_timer_map();
264
265 /* Initialize the timer dividers */
266 timer_div = rate / CLOCK_TICK_RATE - 1;
267 writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
268 writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
269 writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
270
271 /* Initialize timer counters to 0 */
272 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
273 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
274 writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
275 BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
276 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
277 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
278
279 /* Clear all interrupts */
280 writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
281
282 BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
283
284 BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
285
286 sirfsoc_clockevent_init();
287}
288
289static struct of_device_id timer_ids[] = {
290 { .compatible = "sirf,marco-tick" },
291 {},
292};
293
294static void __init sirfsoc_of_timer_map(void)
295{
296 struct device_node *np;
297
298 np = of_find_matching_node(NULL, timer_ids);
299 if (!np)
300 return;
301 sirfsoc_timer_base = of_iomap(np, 0);
302 if (!sirfsoc_timer_base)
303 panic("unable to map timer cpu registers\n");
304
305 sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
306 if (!sirfsoc_timer_irq.irq)
307 panic("No irq passed for timer0 via DT\n");
308
309#ifdef CONFIG_LOCAL_TIMERS
310 sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1);
311 if (!sirfsoc_timer1_irq.irq)
312 panic("No irq passed for timer1 via DT\n");
313#endif
314
315 of_node_put(np);
316}