From mboxrd@z Thu Jan 1 00:00:00 1970 From: mark.rutland@arm.com (Mark Rutland) Date: Mon, 21 Jan 2013 09:48:55 +0000 Subject: [PATCH v2 8/9] ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures In-Reply-To: References: <1358315784-25104-1-git-send-email-Barry.Song@csr.com> <1358315784-25104-4-git-send-email-Barry.Song@csr.com> <20130116153739.GE10218@e106331-lin.cambridge.arm.com> Message-ID: <20130121094855.GE15707@e106331-lin.cambridge.arm.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org On Sat, Jan 19, 2013 at 04:08:55AM +0000, Barry Song wrote: > Hi Mark, > Thanks very much for reviewing. > > 2013/1/16 Mark Rutland : > > Hello, > > > > On Wed, Jan 16, 2013 at 05:56:23AM +0000, Barry Song wrote: > >> From: Barry Song > >> > >> 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 > >> --- > >> arch/arm/boot/dts/Makefile | 1 + > >> arch/arm/configs/prima2_defconfig | 3 + > >> arch/arm/mach-prima2/Kconfig | 10 + > >> arch/arm/mach-prima2/Makefile | 3 + > >> arch/arm/mach-prima2/common.c | 40 ++++- > >> arch/arm/mach-prima2/common.h | 11 + > >> arch/arm/mach-prima2/headsmp.S | 79 ++++++++ > >> arch/arm/mach-prima2/hotplug.c | 41 ++++ > >> arch/arm/mach-prima2/platsmp.c | 170 +++++++++++++++++ > >> arch/arm/mach-prima2/timer-marco.c | 355 ++++++++++++++++++++++++++++++++++++ > >> 10 files changed, 712 insertions(+), 1 deletions(-) > >> create mode 100644 arch/arm/mach-prima2/headsmp.S > >> create mode 100644 arch/arm/mach-prima2/hotplug.c > >> create mode 100644 arch/arm/mach-prima2/platsmp.c > >> create mode 100644 arch/arm/mach-prima2/timer-marco.c > >> > >> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile > >> index e44da40..6af9901 100644 > >> --- a/arch/arm/boot/dts/Makefile > >> +++ b/arch/arm/boot/dts/Makefile > >> @@ -73,6 +73,7 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-dns320.dtb \ > >> kirkwood-ts219-6281.dtb \ > >> kirkwood-ts219-6282.dtb \ > >> kirkwood-openblocks_a6.dtb > >> +dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb > >> dtb-$(CONFIG_ARCH_MSM) += msm8660-surf.dtb \ > >> msm8960-cdp.dtb > >> dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ > >> diff --git a/arch/arm/configs/prima2_defconfig b/arch/arm/configs/prima2_defconfig > >> index 6a936c7..002a1ce 100644 > >> --- a/arch/arm/configs/prima2_defconfig > >> +++ b/arch/arm/configs/prima2_defconfig > >> @@ -11,6 +11,9 @@ CONFIG_PARTITION_ADVANCED=y > >> CONFIG_BSD_DISKLABEL=y > >> CONFIG_SOLARIS_X86_PARTITION=y > >> CONFIG_ARCH_SIRF=y > >> +# CONFIG_SWP_EMULATE is not set > >> +CONFIG_SMP=y > >> +CONFIG_SCHED_MC=y > >> CONFIG_PREEMPT=y > >> CONFIG_AEABI=y > >> CONFIG_KEXEC=y > >> diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig > >> index 558ccfb..4f7379f 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 0007a6e..bfe360c 100644 > >> --- a/arch/arm/mach-prima2/Makefile > >> +++ b/arch/arm/mach-prima2/Makefile > >> @@ -5,4 +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 99f9c7e..00a6564 100644 > >> --- a/arch/arm/mach-prima2/common.c > >> +++ b/arch/arm/mach-prima2/common.c > >> @@ -8,9 +8,11 @@ > >> > >> #include > >> #include > >> +#include > >> #include > >> #include > >> #include > >> +#include > >> #include > >> #include > >> #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,7 +46,7 @@ static const char *prima2_dt_match[] __initdata = { > >> > >> DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)") > >> /* Maintainer: Barry Song */ > >> - .map_io = sirfsoc_map_lluart, > >> + .map_io = sirfsoc_map_io, > >> .init_irq = sirfsoc_of_irq_init, > >> .init_time = sirfsoc_prima2_timer_init, > >> #ifdef CONFIG_MULTI_IRQ_HANDLER > >> @@ -51,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 */ > >> + .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 a4f91a6..b7c26b6 100644 > >> --- a/arch/arm/mach-prima2/common.h > >> +++ b/arch/arm/mach-prima2/common.h > >> @@ -14,6 +14,11 @@ > >> #include > >> > >> 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); > >> @@ -26,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 0000000..6ec19d5 > >> --- /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 > >> +#include > >> + > >> + __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< >> + 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 0000000..97c1ee5 > >> --- /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 > >> +#include > >> +#include > >> + > >> +#include > >> +#include > >> + > >> +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/platsmp.c b/arch/arm/mach-prima2/platsmp.c > >> new file mode 100644 > >> index 0000000..b939e9b4 > >> --- /dev/null > >> +++ b/arch/arm/mach-prima2/platsmp.c > >> @@ -0,0 +1,170 @@ > >> +/* > >> + * 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 > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> + > >> +#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) > >> +{ > >> + int i, ncores; > >> + > >> + ncores = scu_get_core_count(scu_base); > >> + > >> + for (i = 0; i < ncores; i++) > >> + set_cpu_possible(i, true); > >> + > >> + set_smp_cross_call(gic_raise_softirq); > >> +} > > > > You don't need to use scu_get_core_count to figure out which cpus to set > > possible. It duplicates work already done by arm_dt_init_cpu_maps, and doesn't > > initialise the logical map (as arm_dt_init_cpu_maps does). > > > > You're already relying on the arm_dt_init_cpu_maps to set up the logical map > > for the holding pen release, so you may as well rely on it to set_cpu_possible > > for each of the cpus described in the dt. > > > > Tegra is already on its way to doing this: > > > > http://lists.infradead.org/pipermail/linux-arm-kernel/2012-December/138319.html > > http://lists.infradead.org/pipermail/linux-arm-kernel/2013-January/140219.html > > http://lists.infradead.org/pipermail/linux-arm-kernel/2013-January/141596.html > > > what if there are 4 nodes in dts but there are actually only one core > in soc? for example, using qemu to run vexpress without --smp=4? > SiRFmarco will have 2 versions, one is UP, the other one is dual core. > if using dt, i might need to take cpus node out of marco.dtsi If the dt doesn't match the hardware, then the dt is wrong. It's probably worth having a separate dtsi for the UP and SMP versions (with all the core stuff in another shared dsti). > > >> + > >> +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 > >> +}; > > > > The timer below is a big chunk of code, it'd be nicer if it had a patch to > > itself. > ok. > > > > >> diff --git a/arch/arm/mach-prima2/timer-marco.c b/arch/arm/mach-prima2/timer-marco.c > >> new file mode 100644 > >> index 0000000..07b3a6b > >> --- /dev/null > >> +++ b/arch/arm/mach-prima2/timer-marco.c > >> @@ -0,0 +1,355 @@ > >> +/* > >> + * System timer for CSR SiRFprimaII > >> + * > >> + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. > >> + * > >> + * Licensed under GPLv2 or later. > >> + */ > >> + > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> + > >> +#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); > >> +} > >> + > >> +/* timer0 interrupt handler */ > >> +static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id) > >> +{ > >> + struct clock_event_device *ce = dev_id; > >> + > >> + /* clear timer0 interrupt */ > >> + writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); > >> + > >> + if (ce->mode == CLOCK_EVT_MODE_ONESHOT) > >> + sirfsoc_timer_count_disable(0); > >> + > >> + 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) > >> +{ > >> + > >> + writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0); > >> + writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0); > >> + > >> + /* enable the tick */ > >> + sirfsoc_timer_count_enable(0); > >> + > >> + 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(0); > >> +} > >> + > >> +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 > >> + > >> +/* timer1 interrupt handler */ > >> +static irqreturn_t sirfsoc_timer1_interrupt(int irq, void *dev_id) > >> +{ > >> + struct clock_event_device *ce = dev_id; > >> + > >> + /* clear timer1 interrupt */ > >> + writel_relaxed(BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); > >> + > >> + if (ce->mode == CLOCK_EVT_MODE_ONESHOT) > >> + sirfsoc_timer_count_disable(1); > >> + > >> + ce->event_handler(ce); > >> + > >> + return IRQ_HANDLED; > >> +} > >> + > >> +static struct irqaction sirfsoc_timer1_irq = { > >> + .name = "sirfsoc_timer1", > >> + .flags = IRQF_TIMER | IRQF_NOBALANCING, > >> + .handler = sirfsoc_timer1_interrupt, > >> +}; > >> + > >> +static int sirfsoc_timer1_set_next_event(unsigned long delta, > >> + struct clock_event_device *ce) > >> +{ > >> + writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1); > >> + writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_1); > >> + > >> + /* enable the tick */ > >> + sirfsoc_timer_count_enable(1); > >> + > >> + return 0; > >> +} > >> + > >> +static void sirfsoc_timer1_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(1); > >> +} > >> + > >> +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; > > > > This seems a little scary. Does the timer only exist for 1 core, or is it not > > actually a local timer? > > there are multiple timers, everyone can be avaliable to all cores. > here we actually use timer0 as cpu0's tick, timer1 as cpu1's tick. > each one uses a seperate timers. Ok. >>From a glance over the code, it looks like the timer0 and timer1 are pretty much identical. Is there no way to have the logic unified, figuring out whether to use SIRFSOC_TIMER_*_{0,1} automatically? > > > > >> + > >> + ce->irq = sirfsoc_timer1_irq.irq; > >> + ce->name = "local_timer"; > >> + ce->features = sirfsoc_clockevent.features; > >> + ce->rating = sirfsoc_clockevent.rating; > >> + ce->cpumask = cpumask_of(1); > > > > The local_timer api sets the cpumask, and you've already rejected setups from > > cpu0, so this isn't technically necessary. > > right. > > > >> + ce->set_mode = sirfsoc_timer1_set_mode; > >> + ce->set_next_event = sirfsoc_timer1_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); > >> +} > > > > Presumably you need to balance what you've done in sirfsoc_local_timer_setup, > > and return early for cpu0. If you don't unify the code for the two timers, I really think you should have an early return here for cpu0. > > > >> + > >> +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); > > > > I assume this is a typo. For one thing, clockevent_delta2ns takes an unsigned > > long. > > grep and get: > http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git&a=search&h=HEAD&st=grep&s=clockevent_delta2ns > almost all platforms don't really take it seriously if we think -2 is > very big unsigned long and number like 0xfffffffe is almost not > unsigned long without UL. That's a bit opaque, but as you say, everyone's doing it (or something equivalent). > > > > >> + 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 / 2 - 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); > >> +} > >> -- > >> 1.7.5.4 > > > Thanks, > > Mark. > > -barry > Thanks, Mark.