[PATCH] ARM: OMAP: Add support for dmtimer v2 ip (Re: [PATCH v15 06/12] OMAP: dmtimer: switch-over to platform device driver)
DebBarma, Tarun Kanti
tarun.kanti at ti.com
Sun Sep 18 08:27:03 EDT 2011
[...]
> Well here's what I came up with to deal with the different timer
> registers. We can't use the context registers as those are for
> the value naturally..
>
> But we can map the interrupt registers separately and then have
> the rest start from func_base that is different based on the timer
> version. Rebasing the rest of the dmtimer hwmod patches on this
> should be fairly easy, mostly just need to pass timer instead of
> timer->io_base and use __raw_read/write for the interrupt registers.
I went through the patch. It definitely looks much more simplified now.
I will rebase on top of this change.
>
> Also, I ended up checking the timer revision with if (!(tidr >> 16))
> as it seems that those bits are zero for v1 timers? If that works,
> then we don't need patch 02/12 for the revision number.
Right.
--
Tarun
>
> Afzal, care to check if that works for AM335X/TI816X/TI814X?
> It tried it briefly with omap4 gptimer3 as the clockevent and
> CONFIG_LOCAL_TIMER disabled.
>
> The patch is against the current cleanup branch in linux-omap
> tree.
>
> Regards,
>
> Tony
>
>
> From: Tony Lindgren <tony at atomide.com>
> Date: Fri, 16 Sep 2011 15:44:20 -0700
> Subject: [PATCH] ARM: OMAP: Add support for dmtimer v2 ip
>
> The registers are slightly different between v1 and v2 ip that
> is available in omap4 and later for some timers.
>
> Add support for v2 ip by mapping the interrupt related registers
> separately and adding func_base for the functional registers.
>
> Also disable dmtimer driver features on omap4 for now as
> those need the hwmod conversion series to deal with enabling
> the timers properly in omap_dm_timer_init.
>
> Signed-off-by: Tony Lindgren <tony at atomide.com>
>
> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
> index cf1de7d..21d34fb 100644
> --- a/arch/arm/mach-omap2/timer.c
> +++ b/arch/arm/mach-omap2/timer.c
> @@ -78,7 +78,7 @@ static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id)
> {
> struct clock_event_device *evt = &clockevent_gpt;
>
> - __omap_dm_timer_write_status(clkev.io_base, OMAP_TIMER_INT_OVERFLOW);
> + __omap_dm_timer_write_status(&clkev, OMAP_TIMER_INT_OVERFLOW);
>
> evt->event_handler(evt);
> return IRQ_HANDLED;
> @@ -93,7 +93,7 @@ static struct irqaction omap2_gp_timer_irq = {
> static int omap2_gp_timer_set_next_event(unsigned long cycles,
> struct clock_event_device *evt)
> {
> - __omap_dm_timer_load_start(clkev.io_base, OMAP_TIMER_CTRL_ST,
> + __omap_dm_timer_load_start(&clkev, OMAP_TIMER_CTRL_ST,
> 0xffffffff - cycles, 1);
>
> return 0;
> @@ -104,16 +104,16 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
> {
> u32 period;
>
> - __omap_dm_timer_stop(clkev.io_base, 1, clkev.rate);
> + __omap_dm_timer_stop(&clkev, 1, clkev.rate);
>
> switch (mode) {
> case CLOCK_EVT_MODE_PERIODIC:
> period = clkev.rate / HZ;
> period -= 1;
> /* Looks like we need to first set the load value separately */
> - __omap_dm_timer_write(clkev.io_base, OMAP_TIMER_LOAD_REG,
> + __omap_dm_timer_write(&clkev, OMAP_TIMER_LOAD_REG,
> 0xffffffff - period, 1);
> - __omap_dm_timer_load_start(clkev.io_base,
> + __omap_dm_timer_load_start(&clkev,
> OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST,
> 0xffffffff - period, 1);
> break;
> @@ -172,6 +172,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
> }
>
> omap_hwmod_enable(oh);
> + __omap_dm_timer_init_regs(timer);
>
> sys_timer_reserved |= (1 << (gptimer_id - 1));
>
> @@ -189,7 +190,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
> clk_put(src);
> }
> }
> - __omap_dm_timer_reset(timer->io_base, 1, 1);
> + __omap_dm_timer_reset(timer, 1, 1);
> timer->posted = 1;
>
> timer->rate = clk_get_rate(timer->fclk);
> @@ -210,7 +211,7 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
> omap2_gp_timer_irq.dev_id = (void *)&clkev;
> setup_irq(clkev.irq, &omap2_gp_timer_irq);
>
> - __omap_dm_timer_int_enable(clkev.io_base, OMAP_TIMER_INT_OVERFLOW);
> + __omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>
> clockevent_gpt.mult = div_sc(clkev.rate, NSEC_PER_SEC,
> clockevent_gpt.shift);
> @@ -251,7 +252,7 @@ static struct omap_dm_timer clksrc;
> static DEFINE_CLOCK_DATA(cd);
> static cycle_t clocksource_read_cycles(struct clocksource *cs)
> {
> - return (cycle_t)__omap_dm_timer_read_counter(clksrc.io_base, 1);
> + return (cycle_t)__omap_dm_timer_read_counter(&clksrc, 1);
> }
>
> static struct clocksource clocksource_gpt = {
> @@ -266,7 +267,7 @@ static void notrace dmtimer_update_sched_clock(void)
> {
> u32 cyc;
>
> - cyc = __omap_dm_timer_read_counter(clksrc.io_base, 1);
> + cyc = __omap_dm_timer_read_counter(&clksrc, 1);
>
> update_sched_clock(&cd, cyc, (u32)~0);
> }
> @@ -276,7 +277,7 @@ unsigned long long notrace sched_clock(void)
> u32 cyc = 0;
>
> if (clksrc.reserved)
> - cyc = __omap_dm_timer_read_counter(clksrc.io_base, 1);
> + cyc = __omap_dm_timer_read_counter(&clksrc, 1);
>
> return cyc_to_sched_clock(&cd, cyc, (u32)~0);
> }
> @@ -293,7 +294,7 @@ static void __init omap2_gp_clocksource_init(int gptimer_id,
> pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n",
> gptimer_id, clksrc.rate);
>
> - __omap_dm_timer_load_start(clksrc.io_base,
> + __omap_dm_timer_load_start(&clksrc,
> OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
> init_sched_clock(&cd, dmtimer_update_sched_clock, 32, clksrc.rate);
>
> diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
> index 75a847d..e23b7cf 100644
> --- a/arch/arm/plat-omap/dmtimer.c
> +++ b/arch/arm/plat-omap/dmtimer.c
> @@ -170,7 +170,8 @@ static spinlock_t dm_timer_lock;
> */
> static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
> {
> - return __omap_dm_timer_read(timer->io_base, reg, timer->posted);
> + WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
> + return __omap_dm_timer_read(timer, reg, timer->posted);
> }
>
> /*
> @@ -182,15 +183,19 @@ static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
> static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
> u32 value)
> {
> - __omap_dm_timer_write(timer->io_base, reg, value, timer->posted);
> + WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
> + __omap_dm_timer_write(timer, reg, value, timer->posted);
> }
>
> static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
> {
> int c;
>
> + if (!timer->sys_stat)
> + return;
> +
> c = 0;
> - while (!(omap_dm_timer_read_reg(timer, OMAP_TIMER_SYS_STAT_REG) & 1)) {
> + while (!(__raw_readl(timer->sys_stat) & 1)) {
> c++;
> if (c > 100000) {
> printk(KERN_ERR "Timer failed to reset\n");
> @@ -219,7 +224,7 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
> if (cpu_class_is_omap2())
> wakeup = 1;
>
> - __omap_dm_timer_reset(timer->io_base, autoidle, wakeup);
> + __omap_dm_timer_reset(timer, autoidle, wakeup);
> timer->posted = 1;
> }
>
> @@ -401,7 +406,7 @@ void omap_dm_timer_stop(struct omap_dm_timer *timer)
> rate = clk_get_rate(timer->fclk);
> #endif
>
> - __omap_dm_timer_stop(timer->io_base, timer->posted, rate);
> + __omap_dm_timer_stop(timer, timer->posted, rate);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
>
> @@ -466,7 +471,7 @@ void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
> }
> l |= OMAP_TIMER_CTRL_ST;
>
> - __omap_dm_timer_load_start(timer->io_base, l, load, timer->posted);
> + __omap_dm_timer_load_start(timer, l, load, timer->posted);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
>
> @@ -519,7 +524,7 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
> void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
> unsigned int value)
> {
> - __omap_dm_timer_int_enable(timer->io_base, value);
> + __omap_dm_timer_int_enable(timer, value);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
>
> @@ -527,7 +532,7 @@ unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
> {
> unsigned int l;
>
> - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
> + l = __raw_readl(timer->irq_stat);
>
> return l;
> }
> @@ -535,13 +540,13 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
>
> void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
> {
> - __omap_dm_timer_write_status(timer->io_base, value);
> + __omap_dm_timer_write_status(timer, value);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
>
> unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
> {
> - return __omap_dm_timer_read_counter(timer->io_base, timer->posted);
> + return __omap_dm_timer_read_counter(timer, timer->posted);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
>
> @@ -601,6 +606,9 @@ static int __init omap_dm_timer_init(void)
> dm_timer_count = omap4_dm_timer_count;
> dm_source_names = omap4_dm_source_names;
> dm_source_clocks = omap4_dm_source_clocks;
> +
> + pr_err("dmtimers disabled for omap4 until hwmod conversion\n");
> + return -ENODEV;
> }
>
> if (cpu_class_is_omap2())
> @@ -630,8 +638,12 @@ static int __init omap_dm_timer_init(void)
> if (sys_timer_reserved & (1 << i)) {
> timer->reserved = 1;
> timer->posted = 1;
> + continue;
> }
> #endif
> + omap_dm_timer_enable(timer);
> + __omap_dm_timer_init_regs(timer);
> + omap_dm_timer_disable(timer);
> }
>
> return 0;
> diff --git a/arch/arm/plat-omap/include/plat/dmtimer.h b/arch/arm/plat-omap/include/plat/dmtimer.h
> index eb5d16c..a11d0c0 100644
> --- a/arch/arm/plat-omap/include/plat/dmtimer.h
> +++ b/arch/arm/plat-omap/include/plat/dmtimer.h
> @@ -98,12 +98,30 @@ int omap_dm_timers_active(void);
> * used by dmtimer.c and sys_timer related code.
> */
>
> -/* register offsets */
> -#define _OMAP_TIMER_ID_OFFSET 0x00
> -#define _OMAP_TIMER_OCP_CFG_OFFSET 0x10
> -#define _OMAP_TIMER_SYS_STAT_OFFSET 0x14
> -#define _OMAP_TIMER_STAT_OFFSET 0x18
> -#define _OMAP_TIMER_INT_EN_OFFSET 0x1c
> +/*
> + * The interrupt registers are different between v1 and v2 ip.
> + * These registers are offsets from timer->iobase.
> + */
> +#define OMAP_TIMER_ID_OFFSET 0x00
> +#define OMAP_TIMER_OCP_CFG_OFFSET 0x10
> +
> +#define OMAP_TIMER_V1_SYS_STAT_OFFSET 0x14
> +#define OMAP_TIMER_V1_STAT_OFFSET 0x18
> +#define OMAP_TIMER_V1_INT_EN_OFFSET 0x1c
> +
> +#define OMAP_TIMER_V2_IRQSTATUS_RAW 0x24
> +#define OMAP_TIMER_V2_IRQSTATUS 0x28
> +#define OMAP_TIMER_V2_IRQENABLE_SET 0x2c
> +#define OMAP_TIMER_V2_IRQENABLE_CLR 0x30
> +
> +/*
> + * The functional registers have a different base on v1 and v2 ip.
> + * These registers are offsets from timer->func_base. The func_base
> + * is samae as io_base for v1 and io_base + 0x14 for v2 ip.
> + *
> + */
> +#define OMAP_TIMER_V2_FUNC_OFFSET 0x14
> +
> #define _OMAP_TIMER_WAKEUP_EN_OFFSET 0x20
> #define _OMAP_TIMER_CTRL_OFFSET 0x24
> #define OMAP_TIMER_CTRL_GPOCFG (1 << 14)
> @@ -147,21 +165,6 @@ int omap_dm_timers_active(void);
> /* register offsets with the write pending bit encoded */
> #define WPSHIFT 16
>
> -#define OMAP_TIMER_ID_REG (_OMAP_TIMER_ID_OFFSET \
> - | (WP_NONE << WPSHIFT))
> -
> -#define OMAP_TIMER_OCP_CFG_REG (_OMAP_TIMER_OCP_CFG_OFFSET \
> - | (WP_NONE << WPSHIFT))
> -
> -#define OMAP_TIMER_SYS_STAT_REG (_OMAP_TIMER_SYS_STAT_OFFSET \
> - | (WP_NONE << WPSHIFT))
> -
> -#define OMAP_TIMER_STAT_REG (_OMAP_TIMER_STAT_OFFSET \
> - | (WP_NONE << WPSHIFT))
> -
> -#define OMAP_TIMER_INT_EN_REG (_OMAP_TIMER_INT_EN_OFFSET \
> - | (WP_NONE << WPSHIFT))
> -
> #define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \
> | (WP_NONE << WPSHIFT))
>
> @@ -213,7 +216,14 @@ struct omap_dm_timer {
> #ifdef CONFIG_ARCH_OMAP2PLUS
> struct clk *iclk, *fclk;
> #endif
> - void __iomem *io_base;
> + void __iomem *io_base;
> + void __iomem *sys_stat; /* TISTAT timer status */
> + void __iomem *irq_stat; /* TISR/IRQSTATUS interrupt status */
> + void __iomem *irq_ena; /* irq enable */
> + void __iomem *irq_dis; /* irq disable, only on v2 ip */
> + void __iomem *pend; /* write pending */
> + void __iomem *func_base; /* function register base */
> +
> unsigned long rate;
> unsigned reserved:1;
> unsigned enabled:1;
> @@ -223,35 +233,57 @@ struct omap_dm_timer {
> extern u32 sys_timer_reserved;
> void omap_dm_timer_prepare(struct omap_dm_timer *timer);
>
> -static inline u32 __omap_dm_timer_read(void __iomem *base, u32 reg,
> +static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg,
> int posted)
> {
> if (posted)
> - while (__raw_readl(base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))
> - & (reg >> WPSHIFT))
> + while (__raw_readl(timer->pend) & (reg >> WPSHIFT))
> cpu_relax();
>
> - return __raw_readl(base + (reg & 0xff));
> + return __raw_readl(timer->func_base + (reg & 0xff));
> }
>
> -static inline void __omap_dm_timer_write(void __iomem *base, u32 reg, u32 val,
> - int posted)
> +static inline void __omap_dm_timer_write(struct omap_dm_timer *timer,
> + u32 reg, u32 val, int posted)
> {
> if (posted)
> - while (__raw_readl(base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))
> - & (reg >> WPSHIFT))
> + while (__raw_readl(timer->pend) & (reg >> WPSHIFT))
> cpu_relax();
>
> - __raw_writel(val, base + (reg & 0xff));
> + __raw_writel(val, timer->func_base + (reg & 0xff));
> +}
> +
> +static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
> +{
> + u32 tidr;
> +
> + /* Assume v1 ip if bits [31:16] are zero */
> + tidr = __raw_readl(timer->io_base);
> + if (!(tidr >> 16)) {
> + timer->sys_stat = timer->io_base + OMAP_TIMER_V1_SYS_STAT_OFFSET;
> + timer->irq_stat = timer->io_base + OMAP_TIMER_V1_STAT_OFFSET;
> + timer->irq_ena = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
> + timer->irq_dis = 0;
> + timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET;
> + timer->func_base = timer->io_base;
> + } else {
> + timer->sys_stat = 0;
> + timer->irq_stat = timer->io_base + OMAP_TIMER_V2_IRQSTATUS;
> + timer->irq_ena = timer->io_base + OMAP_TIMER_V2_IRQENABLE_SET;
> + timer->irq_dis = timer->io_base + OMAP_TIMER_V2_IRQENABLE_CLR;
> + timer->pend = timer->io_base +
> + _OMAP_TIMER_WRITE_PEND_OFFSET + OMAP_TIMER_V2_FUNC_OFFSET;
> + timer->func_base = timer->io_base + OMAP_TIMER_V2_FUNC_OFFSET;
> + }
> }
>
> /* Assumes the source clock has been set by caller */
> -static inline void __omap_dm_timer_reset(void __iomem *base, int autoidle,
> - int wakeup)
> +static inline void __omap_dm_timer_reset(struct omap_dm_timer *timer,
> + int autoidle, int wakeup)
> {
> u32 l;
>
> - l = __omap_dm_timer_read(base, OMAP_TIMER_OCP_CFG_REG, 0);
> + l = __raw_readl(timer->io_base + OMAP_TIMER_OCP_CFG_OFFSET);
> l |= 0x02 << 3; /* Set to smart-idle mode */
> l |= 0x2 << 8; /* Set clock activity to perserve f-clock on idle */
>
> @@ -261,10 +293,10 @@ static inline void __omap_dm_timer_reset(void __iomem *base, int autoidle,
> if (wakeup)
> l |= 1 << 2;
>
> - __omap_dm_timer_write(base, OMAP_TIMER_OCP_CFG_REG, l, 0);
> + __raw_writel(l, timer->io_base + OMAP_TIMER_OCP_CFG_OFFSET);
>
> /* Match hardware reset default of posted mode */
> - __omap_dm_timer_write(base, OMAP_TIMER_IF_CTRL_REG,
> + __omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG,
> OMAP_TIMER_CTRL_POSTED, 0);
> }
>
> @@ -286,18 +318,18 @@ static inline int __omap_dm_timer_set_source(struct clk *timer_fck,
> return ret;
> }
>
> -static inline void __omap_dm_timer_stop(void __iomem *base, int posted,
> - unsigned long rate)
> +static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer,
> + int posted, unsigned long rate)
> {
> u32 l;
>
> - l = __omap_dm_timer_read(base, OMAP_TIMER_CTRL_REG, posted);
> + l = __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
> if (l & OMAP_TIMER_CTRL_ST) {
> l &= ~0x1;
> - __omap_dm_timer_write(base, OMAP_TIMER_CTRL_REG, l, posted);
> + __omap_dm_timer_write(timer, OMAP_TIMER_CTRL_REG, l, posted);
> #ifdef CONFIG_ARCH_OMAP2PLUS
> /* Readback to make sure write has completed */
> - __omap_dm_timer_read(base, OMAP_TIMER_CTRL_REG, posted);
> + __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
> /*
> * Wait for functional clock period x 3.5 to make sure that
> * timer is stopped
> @@ -307,34 +339,34 @@ static inline void __omap_dm_timer_stop(void __iomem *base, int posted,
> }
>
> /* Ack possibly pending interrupt */
> - __omap_dm_timer_write(base, OMAP_TIMER_STAT_REG,
> - OMAP_TIMER_INT_OVERFLOW, 0);
> + __raw_writel(OMAP_TIMER_INT_OVERFLOW, timer->irq_stat);
> }
>
> -static inline void __omap_dm_timer_load_start(void __iomem *base, u32 ctrl,
> - unsigned int load, int posted)
> +static inline void __omap_dm_timer_load_start(struct omap_dm_timer *timer,
> + u32 ctrl, unsigned int load,
> + int posted)
> {
> - __omap_dm_timer_write(base, OMAP_TIMER_COUNTER_REG, load, posted);
> - __omap_dm_timer_write(base, OMAP_TIMER_CTRL_REG, ctrl, posted);
> + __omap_dm_timer_write(timer, OMAP_TIMER_COUNTER_REG, load, posted);
> + __omap_dm_timer_write(timer, OMAP_TIMER_CTRL_REG, ctrl, posted);
> }
>
> -static inline void __omap_dm_timer_int_enable(void __iomem *base,
> +static inline void __omap_dm_timer_int_enable(struct omap_dm_timer *timer,
> unsigned int value)
> {
> - __omap_dm_timer_write(base, OMAP_TIMER_INT_EN_REG, value, 0);
> - __omap_dm_timer_write(base, OMAP_TIMER_WAKEUP_EN_REG, value, 0);
> + __raw_writel(value, timer->irq_ena);
> + __omap_dm_timer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value, 0);
> }
>
> -static inline unsigned int __omap_dm_timer_read_counter(void __iomem *base,
> - int posted)
> +static inline unsigned int
> +__omap_dm_timer_read_counter(struct omap_dm_timer *timer, int posted)
> {
> - return __omap_dm_timer_read(base, OMAP_TIMER_COUNTER_REG, posted);
> + return __omap_dm_timer_read(timer, OMAP_TIMER_COUNTER_REG, posted);
> }
>
> -static inline void __omap_dm_timer_write_status(void __iomem *base,
> +static inline void __omap_dm_timer_write_status(struct omap_dm_timer *timer,
> unsigned int value)
> {
> - __omap_dm_timer_write(base, OMAP_TIMER_STAT_REG, value, 0);
> + __raw_writel(value, timer->irq_stat);
> }
>
> #endif /* __ASM_ARCH_DMTIMER_H */
>
More information about the linux-arm-kernel
mailing list