[PATCH V2] ARM: bcm281xx: Add timer driver

Olof Johansson olof at lixom.net
Sat Dec 8 18:44:53 EST 2012


John,

On Sat, Dec 8, 2012 at 12:15 AM, Christian Daudt <csd at broadcom.com> wrote:
> This adds support for the Broadcom timer, used in the following SoCs:
> BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
>
> This patch needs the arm-soc/soc/next branch
>

Given the dependencies on the new platform here, should we just merge
this through arm-soc? If so, an Acked-by would be appreciated.

Alternatively, we can merge the driver and the device tree updates
separately if that's easier.


Thanks!


-Olof

> Updates from V1:
>  - Rename bcm_timer.c to bcm_kona_timer.c
>  - Pull .h into bcm_kona_timer.c
>  - Make timers static
>  - Clean up comment block
>  - Switched to using clockevents_config_and_register
>  - Added an error to the get_timer loop if it repeats too much
>  - Added to Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt
>  - Added missing readl to timer_disable_and_clear
>
> Note: bcm,kona-timer was kept as the 'compatible' field to make it
> specific enough for when there are multiple bcm timers (bcm,timer is
> too generic).
>
> Signed-off-by: Christian Daudt <csd at broadcom.com>
> Acked-by: Arnd Bergmann <arnd at arndb.de>
>
> diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt b/Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt
> new file mode 100644
> index 0000000..59fa6e6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt
> @@ -0,0 +1,19 @@
> +Broadcom Kona Family timer
> +-----------------------------------------------------
> +This timer is used in the following Broadcom SoCs:
> + BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
> +
> +Required properties:
> +- compatible : "bcm,kona-timer"
> +- reg : Register range for the timer
> +- interrupts : interrupt for the timer
> +- clock-frequency: frequency that the clock operates
> +
> +Example:
> +       timer at 35006000 {
> +               compatible = "bcm,kona-timer";
> +               reg = <0x35006000 0x1000>;
> +               interrupts = <0x0 7 0x4>;
> +               clock-frequency = <32768>;
> +       };
> +
> diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi
> index ad13588..8f71f40 100644
> --- a/arch/arm/boot/dts/bcm11351.dtsi
> +++ b/arch/arm/boot/dts/bcm11351.dtsi
> @@ -47,4 +47,12 @@
>                     cache-unified;
>                     cache-level = <2>;
>         };
> +
> +       timer at 35006000 {
> +               compatible = "bcm,kona-timer";
> +               reg = <0x35006000 0x1000>;
> +               interrupts = <0x0 7 0x4>;
> +               clock-frequency = <32768>;
> +       };
> +
>  };
> diff --git a/arch/arm/mach-bcm/board_bcm.c b/arch/arm/mach-bcm/board_bcm.c
> index 3a62f1b..2457010 100644
> --- a/arch/arm/mach-bcm/board_bcm.c
> +++ b/arch/arm/mach-bcm/board_bcm.c
> @@ -27,12 +27,10 @@ static const struct of_device_id irq_match[] = {
>         {}
>  };
>
> -static void timer_init(void)
> -{
> -}
> +extern void bcm_timer_init(void);
>
>  static struct sys_timer timer = {
> -       .init = timer_init,
> +       .init = bcm_timer_init,
>  };
>
>  static void __init init_irq(void)
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 603be36..885afc5 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -14,5 +14,6 @@ obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
>  obj-$(CONFIG_CLKSRC_DBX500_PRCMU)      += clksrc-dbx500-prcmu.o
>  obj-$(CONFIG_ARMADA_370_XP_TIMER)      += time-armada-370-xp.o
>  obj-$(CONFIG_ARCH_BCM2835)     += bcm2835_timer.o
> +obj-$(CONFIG_ARCH_BCM)         += bcm_kona_timer.o
>
>  obj-$(CONFIG_CLKSRC_ARM_GENERIC)       += arm_generic.o
> diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c
> new file mode 100644
> index 0000000..dbc54c2
> --- /dev/null
> +++ b/drivers/clocksource/bcm_kona_timer.c
> @@ -0,0 +1,206 @@
> +/*
> + * Copyright (C) 2012 Broadcom Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/clockchips.h>
> +#include <linux/types.h>
> +
> +#include <linux/io.h>
> +#include <asm/mach/time.h>
> +
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +
> +
> +#define KONA_GPTIMER_STCS_OFFSET                       0x00000000
> +#define KONA_GPTIMER_STCLO_OFFSET                      0x00000004
> +#define KONA_GPTIMER_STCHI_OFFSET                      0x00000008
> +#define KONA_GPTIMER_STCM0_OFFSET                      0x0000000C
> +
> +#define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT            0
> +#define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT         4
> +
> +struct bcm_timers {
> +       int tmr_irq;
> +       void __iomem *tmr_regs;
> +};
> +
> +static struct bcm_timers timers;
> +
> +static u32 arch_timer_rate;
> +
> +/*
> + * We use the peripheral timers for system tick, the cpu global timer for
> + * profile tick
> + */
> +static void timer_disable_and_clear(void __iomem *base)
> +{
> +       uint32_t reg;
> +
> +       /*
> +        * clear and disable interrupts
> +        * We are using compare/match register 0 for our system interrupts
> +        */
> +       reg = readl(base + KONA_GPTIMER_STCS_OFFSET);
> +
> +       /* Clear compare (0) interrupt */
> +       reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT;
> +       /* disable compare */
> +       reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
> +
> +       writel(reg, base + KONA_GPTIMER_STCS_OFFSET);
> +
> +}
> +
> +static void
> +timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw)
> +{
> +       void __iomem *base = IOMEM(timer_base);
> +       int loop_limit = 4;
> +
> +       /* Read 64-bit free running counter
> +        * 1. Read hi-word
> +        * 2. Read low-word
> +        * 3. Read hi-word again
> +        * 4.1
> +        *      if new hi-word is not equal to previously read hi-word, then
> +        *      start from #1
> +        * 4.2
> +        *      if new hi-word is equal to previously read hi-word then stop.
> +        */
> +
> +       while (--loop_limit) {
> +               *msw = readl(base + KONA_GPTIMER_STCHI_OFFSET);
> +               *lsw = readl(base + KONA_GPTIMER_STCLO_OFFSET);
> +               if (*msw == readl(base + KONA_GPTIMER_STCHI_OFFSET))
> +                       break;
> +       }
> +       if (!loop_limit) {
> +               pr_err("bcm_kona_timer: getting counter failed.\n");
> +               pr_err(" Timer will be impacted\n");
> +       }
> +
> +       return;
> +}
> +
> +static const struct of_device_id bcm_timer_ids[] __initconst = {
> +       {.compatible = "bcm,kona-timer"},
> +       {},
> +};
> +
> +static void __init timers_init(void)
> +{
> +       struct device_node *node;
> +       u32 freq;
> +
> +       node = of_find_matching_node(NULL, bcm_timer_ids);
> +
> +       if (!node)
> +               panic("No timer");
> +
> +       if (!of_property_read_u32(node, "clock-frequency", &freq))
> +               arch_timer_rate = freq;
> +       else
> +               panic("clock-frequency not set in the .dts file");
> +
> +       /* Setup IRQ numbers */
> +       timers.tmr_irq = irq_of_parse_and_map(node, 0);
> +
> +       /* Setup IO addresses */
> +       timers.tmr_regs = of_iomap(node, 0);
> +
> +       timer_disable_and_clear(timers.tmr_regs);
> +}
> +
> +static int timer_set_next_event(unsigned long clc,
> +                                 struct clock_event_device *unused)
> +{
> +       /* timer (0) is disabled by the timer interrupt already
> +        * so, here we reload the next event value and re-enable
> +        * the timer
> +        *
> +        * This way, we are potentially losing the time between
> +        * timer-interrupt->set_next_event. CPU local timers, when
> +        * they come in should get rid of skew
> +        */
> +
> +       uint32_t lsw, msw;
> +       uint32_t reg;
> +
> +       timer_get_counter(timers.tmr_regs, &msw, &lsw);
> +
> +       /* Load the "next" event tick value */
> +       writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET);
> +
> +       /* Enable compare */
> +       reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
> +       reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
> +       writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
> +
> +       return 0;
> +}
> +
> +static void timer_set_mode(enum clock_event_mode mode,
> +                            struct clock_event_device *unused)
> +{
> +       switch (mode) {
> +       case CLOCK_EVT_MODE_ONESHOT:
> +               /* by default mode is one shot don't do any thing */
> +               break;
> +       case CLOCK_EVT_MODE_UNUSED:
> +       case CLOCK_EVT_MODE_SHUTDOWN:
> +       default:
> +               timer_disable_and_clear(timers.tmr_regs);
> +       }
> +}
> +
> +static struct clock_event_device clockevent_timer = {
> +       .name = "timer 1",
> +       .features = CLOCK_EVT_FEAT_ONESHOT,
> +       .set_next_event = timer_set_next_event,
> +       .set_mode = timer_set_mode
> +};
> +
> +static void __init timer_clockevents_init(void)
> +{
> +       clockevent_timer.cpumask = cpumask_of(0);
> +       clockevents_config_and_register(&clockevent_timer,
> +               arch_timer_rate, 6, 0xffffffff);
> +}
> +
> +static irqreturn_t timer_interrupt(int irq, void *dev_id)
> +{
> +       struct clock_event_device *evt = &clockevent_timer;
> +
> +       timer_disable_and_clear(timers.tmr_regs);
> +       evt->event_handler(evt);
> +       return IRQ_HANDLED;
> +}
> +
> +static struct irqaction timer_irq = {
> +       .name = "Kona Timer Tick",
> +       .flags = IRQF_TIMER,
> +       .handler = timer_interrupt,
> +};
> +
> +void __init bcm_timer_init(void)
> +{
> +       timers_init();
> +       timer_clockevents_init();
> +       setup_irq(timers.tmr_irq, &timer_irq);
> +       timer_set_next_event((arch_timer_rate / HZ), NULL);
> +}
> --
> 1.7.1
>
>



More information about the linux-arm-kernel mailing list