Message ID | 20130814221237.695587612@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 08/15, Domenico Andreoli wrote: > + > +static inline void __iomem *to_load(struct bcm4760_timer *timer) > +{ > + return timer->base + TIMER_LOAD_OFFSET; > +} > + > +static inline void __iomem *to_control(struct bcm4760_timer *timer) > +{ > + return timer->base + TIMER_CONTROL_OFFSET; > +} > + > +static inline void __iomem *to_intclr(struct bcm4760_timer *timer) > +{ > + return timer->base + TIMER_INTCLR_OFFSET; > +} > + > +static inline void __iomem *to_ris(struct bcm4760_timer *timer) > +{ > + return timer->base + TIMER_RIS_OFFSET; > +} > + > +static inline void __iomem *to_mis(struct bcm4760_timer *timer) > +{ > + return timer->base + TIMER_MIS_OFFSET; > +} Style Nit: This is new. Usually people either make a <my_driver>_{readl,writel}() function that takes the struct and an offset or they just add the offset directly in their readl/writel calls. Can you do that? Probably save some lines of code. > +static irqreturn_t bcm4760_timer_interrupt(int irq, void *dev_id) > +{ > + struct bcm4760_timer *timer = dev_id; > + void (*event_handler)(struct clock_event_device *); > + > + /* check the (masked) interrupt status */ > + if (!readl_relaxed(to_mis(timer))) > + return IRQ_NONE; > + > + /* clear the timer interrupt */ > + writel_relaxed(1, to_intclr(timer)); > + > + event_handler = ACCESS_ONCE(timer->evt.event_handler); > + if (event_handler) > + event_handler(&timer->evt); This is unfortunate. Do you have a pending timer interrupt left by the bootloader? > + > + return IRQ_HANDLED; > +} > + > +static void __init bcm4760_init_time(struct device_node *node) > +{ > + void __iomem *base; > + u32 freq = 24000000; Why have freq in the DT binding at all then? > + int irq; > + struct bcm4760_timer *timer; > + > + base = of_iomap(node, 0); > + if (!base) > + panic("Can't remap timer registers"); > + > + timer = kzalloc(sizeof(*timer), GFP_KERNEL); > + if (!timer) > + panic("Can't allocate timer struct\n"); > + > + irq = irq_of_parse_and_map(node, 0); > + if (irq <= 0) > + panic("Can't parse timer IRQ"); > + > + timer->base = base; > + timer->evt.name = node->name; > + timer->evt.rating = 300; > + timer->evt.features = CLOCK_EVT_FEAT_ONESHOT; > + timer->evt.set_mode = bcm4760_timer_set_mode; > + timer->evt.set_next_event = bcm4760_timer_set_next_event; > + timer->evt.cpumask = cpumask_of(0); > + timer->act.name = node->name; > + timer->act.flags = IRQF_TIMER | IRQF_SHARED; > + timer->act.dev_id = timer; > + timer->act.handler = bcm4760_timer_interrupt; > + > + if (setup_irq(irq, &timer->act)) > + panic("Can't set up timer IRQ\n"); > + > + clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff); If you switch this registration and the setup_irq() call you shouldn't need the ACCESS_ONCE() and that check in the irq handler. Please switch the order and or clear the interrupt before registering the clockevent and remove the checks in the interrupt handler.
On Wed, Aug 14, 2013 at 05:30:38PM -0700, Stephen Boyd wrote: > On 08/15, Domenico Andreoli wrote: > > + > > +static inline void __iomem *to_load(struct bcm4760_timer *timer) > > +{ > > + return timer->base + TIMER_LOAD_OFFSET; > > +} > > + > > +static inline void __iomem *to_control(struct bcm4760_timer *timer) > > +{ > > + return timer->base + TIMER_CONTROL_OFFSET; > > +} > > + > > +static inline void __iomem *to_intclr(struct bcm4760_timer *timer) > > +{ > > + return timer->base + TIMER_INTCLR_OFFSET; > > +} > > + > > +static inline void __iomem *to_ris(struct bcm4760_timer *timer) > > +{ > > + return timer->base + TIMER_RIS_OFFSET; > > +} > > + > > +static inline void __iomem *to_mis(struct bcm4760_timer *timer) > > +{ > > + return timer->base + TIMER_MIS_OFFSET; > > +} > > Style Nit: This is new. Usually people either make a > <my_driver>_{readl,writel}() function that takes the struct and > an offset or they just add the offset directly in their > readl/writel calls. Can you do that? Probably save some lines of > code. yes, sure. > > > +static irqreturn_t bcm4760_timer_interrupt(int irq, void *dev_id) > > +{ > > + struct bcm4760_timer *timer = dev_id; > > + void (*event_handler)(struct clock_event_device *); > > + > > + /* check the (masked) interrupt status */ > > + if (!readl_relaxed(to_mis(timer))) > > + return IRQ_NONE; > > + > > + /* clear the timer interrupt */ > > + writel_relaxed(1, to_intclr(timer)); > > + > > + event_handler = ACCESS_ONCE(timer->evt.event_handler); > > + if (event_handler) > > + event_handler(&timer->evt); > > This is unfortunate. Do you have a pending timer interrupt left > by the bootloader? Do you mean that if no interrupts are expected beween the irq request and the call clockevents_config_and_register(), I can assume event_handler is always set? > > > + > > + return IRQ_HANDLED; > > +} > > + > > +static void __init bcm4760_init_time(struct device_node *node) > > +{ > > + void __iomem *base; > > + u32 freq = 24000000; > > Why have freq in the DT binding at all then? To get an HW address and remap it at runtime, I guess. I copied it but would also prefer to use DT only where really needed. > > > + int irq; > > + struct bcm4760_timer *timer; > > + > > + base = of_iomap(node, 0); > > + if (!base) > > + panic("Can't remap timer registers"); > > + > > + timer = kzalloc(sizeof(*timer), GFP_KERNEL); > > + if (!timer) > > + panic("Can't allocate timer struct\n"); > > + > > + irq = irq_of_parse_and_map(node, 0); > > + if (irq <= 0) > > + panic("Can't parse timer IRQ"); > > + > > + timer->base = base; > > + timer->evt.name = node->name; > > + timer->evt.rating = 300; > > + timer->evt.features = CLOCK_EVT_FEAT_ONESHOT; > > + timer->evt.set_mode = bcm4760_timer_set_mode; > > + timer->evt.set_next_event = bcm4760_timer_set_next_event; > > + timer->evt.cpumask = cpumask_of(0); > > + timer->act.name = node->name; > > + timer->act.flags = IRQF_TIMER | IRQF_SHARED; > > + timer->act.dev_id = timer; > > + timer->act.handler = bcm4760_timer_interrupt; > > + > > + if (setup_irq(irq, &timer->act)) > > + panic("Can't set up timer IRQ\n"); > > + > > + clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff); > > If you switch this registration and the setup_irq() call you > shouldn't need the ACCESS_ONCE() and that check in the irq handler. > Please switch the order and or clear the interrupt before > registering the clockevent and remove the checks in the interrupt > handler. got it. Thank you. Domenico
On Thu, Aug 15, 2013 at 12:10:46AM +0200, Domenico Andreoli wrote: > From: Domenico Andreoli <domenico.andreoli@linux.com> > > System timer implementation for the BCM4760 based SoCs. > > v3: > * unchanged > > v2: > * dropped clock-frequency property, its allowed value was anyway fixed > to 24MHz and not clearly related to any known clock > > v1: > * initial release > > Cc: John Stultz <john.stultz@linaro.org> > Cc: Thomas Gleixner <tglx@linutronix.de> > Cc: devicetree@vger.kernel.org > Cc: linux-arm-kernel@lists.infradead.org > Signed-off-by: Domenico Andreoli <domenico.andreoli@linux.com> Acked-by: Olof Johansson <olof@lixom.net>
On Thu, Aug 29, 2013 at 4:20 PM, Olof Johansson <olof@lixom.net> wrote:
> Acked-by: Olof Johansson <olof@lixom.net>
Sorry, should have clarified: Acked if the changes requested by
Stephen are done.
-Olof
On Thu, Aug 29, 2013 at 04:21:51PM -0700, Olof Johansson wrote: > On Thu, Aug 29, 2013 at 4:20 PM, Olof Johansson <olof@lixom.net> wrote: > > > Acked-by: Olof Johansson <olof@lixom.net> > > Sorry, should have clarified: Acked if the changes requested by > Stephen are done. I've already prepared them. Will repost later. > > > -Olof Domenico
Index: b/Documentation/devicetree/bindings/timer/brcm,bcm4760-system-timer.txt =================================================================== --- /dev/null +++ b/Documentation/devicetree/bindings/timer/brcm,bcm4760-system-timer.txt @@ -0,0 +1,21 @@ +Broadcom BCM4760 System Timer device tree bindings +-------------------------------------------------- + +The BCM4760 Timer peripheral provides either two or four 32-bit timer +channels. Three timer blocks are available at 0xba000, 0xbb000 and +0xd1000. The first two provide four channels, the last (in the AON - +Always ON power domain) provides only two. + +Required properties: + +- compatible : should be "brcm,bcm4760-system-timer" +- reg : Specifies base physical address and size of the registers. +- interrupts : A list of 2 or 4 interrupt sinks; one per timer channel. + +Example: + +timer@ba000 { + compatible = "brcm,bcm4760-system-timer"; + reg = <0xba000 0x1000>; + interrupts = <4>, <11>; +}; Index: b/arch/arm/boot/dts/bcm4760.dtsi =================================================================== --- a/arch/arm/boot/dts/bcm4760.dtsi +++ b/arch/arm/boot/dts/bcm4760.dtsi @@ -27,6 +27,14 @@ #size-cells = <1>; ranges; + timer@ba000 { + compatible = "brcm,bcm4760-system-timer"; + reg = <0xba000 0x1000>; + interrupt-parent = <&vic0>; + interrupts = <4>, <11>; + clock-frequency = <24000000>; + }; + vic0: interrupt-controller@80000 { compatible = "brcm,bcm4760-pl192", "arm,pl192-vic", "arm,primecell"; reg = <0x80000 0x1000>; Index: b/drivers/clocksource/Makefile =================================================================== --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clk obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ORION_TIMER) += time-orion.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o +obj-$(CONFIG_ARCH_BCM4760) += bcm4760_timer.o obj-$(CONFIG_ARCH_MARCO) += timer-marco.o obj-$(CONFIG_ARCH_MXS) += mxs_timer.o obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o Index: b/drivers/clocksource/bcm4760_timer.c =================================================================== --- /dev/null +++ b/drivers/clocksource/bcm4760_timer.c @@ -0,0 +1,163 @@ +/* + * Broadcom BCM4760 based ARM11 SoCs system timer + * + * Copyright (C) 2012 Domenico Andreoli <domenico.andreoli@linux.com> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/clockchips.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> + +#define TIMER_LOAD_OFFSET 0x00 /* load */ +#define TIMER_VALUE_OFFSET 0x04 /* value */ +#define TIMER_CONTROL_OFFSET 0x08 /* control */ +#define TIMER_INTCLR_OFFSET 0x0c /* interrupt clear */ +#define TIMER_RIS_OFFSET 0x10 /* raw interrupt */ +#define TIMER_MIS_OFFSET 0x14 /* masked interrupt status */ +#define TIMER_BGLOAD_OFFSET 0x18 /* background load */ + +#define TIMER_CTRL_ONESHOTMODE BIT(0) /* One shot mode */ +#define TIMER_CTRL_32BIT BIT(1) /* 32-bit counter mode */ +#define TIMER_CTRL_IE BIT(5) /* Interrupt enable */ +#define TIMER_CTRL_PERIODIC BIT(6) /* Periodic mode */ +#define TIMER_CTRL_EN BIT(7) /* Timer enable */ +#define TIMER_CTRL_CLK2 BIT(9) /* Clock 2 selected */ +#define TIMER_CTRL_PREBY16 (1 << 2) /* prescale divide by 16 */ +#define TIMER_CTRL_PREBY256 (2 << 2) /* prescale divide by 256 */ + +struct bcm4760_timer { + void __iomem *base; + struct clock_event_device evt; + struct irqaction act; +}; + +static inline void __iomem *to_load(struct bcm4760_timer *timer) +{ + return timer->base + TIMER_LOAD_OFFSET; +} + +static inline void __iomem *to_control(struct bcm4760_timer *timer) +{ + return timer->base + TIMER_CONTROL_OFFSET; +} + +static inline void __iomem *to_intclr(struct bcm4760_timer *timer) +{ + return timer->base + TIMER_INTCLR_OFFSET; +} + +static inline void __iomem *to_ris(struct bcm4760_timer *timer) +{ + return timer->base + TIMER_RIS_OFFSET; +} + +static inline void __iomem *to_mis(struct bcm4760_timer *timer) +{ + return timer->base + TIMER_MIS_OFFSET; +} + +static void bcm4760_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt_dev) +{ + struct bcm4760_timer *timer; + u32 val; + + timer = container_of(evt_dev, struct bcm4760_timer, evt); + val = TIMER_CTRL_CLK2 | TIMER_CTRL_32BIT | + TIMER_CTRL_IE | TIMER_CTRL_EN; + + switch (mode) { + case CLOCK_EVT_MODE_ONESHOT: + writel(val | TIMER_CTRL_ONESHOTMODE, to_control(timer)); + break; + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + default: + WARN(1, "%s: unhandled event mode %d\n", __func__, mode); + break; + } +} + +static int bcm4760_timer_set_next_event(unsigned long event, + struct clock_event_device *evt_dev) +{ + struct bcm4760_timer *timer; + + timer = container_of(evt_dev, struct bcm4760_timer, evt); + writel(event, to_load(timer)); + return 0; +} + +static irqreturn_t bcm4760_timer_interrupt(int irq, void *dev_id) +{ + struct bcm4760_timer *timer = dev_id; + void (*event_handler)(struct clock_event_device *); + + /* check the (masked) interrupt status */ + if (!readl_relaxed(to_mis(timer))) + return IRQ_NONE; + + /* clear the timer interrupt */ + writel_relaxed(1, to_intclr(timer)); + + event_handler = ACCESS_ONCE(timer->evt.event_handler); + if (event_handler) + event_handler(&timer->evt); + + return IRQ_HANDLED; +} + +static void __init bcm4760_init_time(struct device_node *node) +{ + void __iomem *base; + u32 freq = 24000000; + int irq; + struct bcm4760_timer *timer; + + base = of_iomap(node, 0); + if (!base) + panic("Can't remap timer registers"); + + timer = kzalloc(sizeof(*timer), GFP_KERNEL); + if (!timer) + panic("Can't allocate timer struct\n"); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("Can't parse timer IRQ"); + + timer->base = base; + timer->evt.name = node->name; + timer->evt.rating = 300; + timer->evt.features = CLOCK_EVT_FEAT_ONESHOT; + timer->evt.set_mode = bcm4760_timer_set_mode; + timer->evt.set_next_event = bcm4760_timer_set_next_event; + timer->evt.cpumask = cpumask_of(0); + timer->act.name = node->name; + timer->act.flags = IRQF_TIMER | IRQF_SHARED; + timer->act.dev_id = timer; + timer->act.handler = bcm4760_timer_interrupt; + + if (setup_irq(irq, &timer->act)) + panic("Can't set up timer IRQ\n"); + + clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff); +} + +CLOCKSOURCE_OF_DECLARE(bcm4760, "brcm,bcm4760-system-timer", bcm4760_init_time); Index: b/arch/arm/mach-bcm/Kconfig =================================================================== --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -23,4 +23,5 @@ config ARCH_BCM4760 select ARM_AMBA select ARM_VIC select CLKSRC_OF + select GENERIC_CLOCKEVENTS select SOC_BUS