[PATCH v2 8/9] ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures
Mark Rutland
mark.rutland at arm.com
Mon Jan 21 04:48:55 EST 2013
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 <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
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 <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.
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.
More information about the linux-arm-kernel
mailing list