[PATCH v2 8/9] ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures
Barry Song
21cnbao at gmail.com
Fri Jan 18 23:08:55 EST 2013
Hi Mark,
Thanks very much for reviewing.
2013/1/16 Mark Rutland <mark.rutland at arm.com>:
> Hello,
>
> On Wed, Jan 16, 2013 at 05:56:23AM +0000, Barry Song wrote:
>> From: Barry Song <Baohua.Song at csr.com>
>>
>> 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 at csr.com>
>> ---
>> 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 <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,7 +46,7 @@ static const char *prima2_dt_match[] __initdata = {
>>
>> DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
>> /* Maintainer: Barry Song <baohua.song at csr.com> */
>> - .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 <baohua.song at 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 a4f91a6..b7c26b6 100644
>> --- a/arch/arm/mach-prima2/common.h
>> +++ b/arch/arm/mach-prima2/common.h
>> @@ -14,6 +14,11 @@
>> #include <asm/exception.h>
>>
>> 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 <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 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 <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/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 <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)
>> +{
>> + 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
>> +
>> +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 <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);
>> +}
>> +
>> +/* 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.
>
>> +
>> + 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.
>
>> +
>> +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.
>
>> + 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
More information about the linux-arm-kernel
mailing list