diff options
author | Jonas Aaberg <jonas.aberg@stericsson.com> | 2011-04-15 07:53:20 +0200 |
---|---|---|
committer | Jonas ABERG <jonas.aberg@stericsson.com> | 2011-04-19 18:10:25 +0200 |
commit | 78c6544aa035c6698989aab91512cda135efe21f (patch) | |
tree | d7fa4eaec03cfbb77fafeed6be3471101b54b4b3 /arch | |
parent | c97227d29d3287d95bab3f2ea03ee83c8f438fcf (diff) |
ARM: ux500: timer-mtu: Add oneshot and broadcast support
The mtu timer can now be programmed as oneshot clockevent
and as broadcast clock.
ST-Ericsson Linux next: Not tested, ask SSM for ER
ST-Ericsson ID: ER332789
ST-Ericsson FOSS-OUT ID: Trivial
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Change-Id: I2b391503dc4ed94364dabcdd720a649af6c5f864
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21037
Reviewed-by: Mattias WALLIN <mattias.wallin@stericsson.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-ux500/timer-mtu.c | 178 |
1 files changed, 107 insertions, 71 deletions
diff --git a/arch/arm/mach-ux500/timer-mtu.c b/arch/arm/mach-ux500/timer-mtu.c index dd083af05fd..3cdf3ca3c20 100644 --- a/arch/arm/mach-ux500/timer-mtu.c +++ b/arch/arm/mach-ux500/timer-mtu.c @@ -36,14 +36,18 @@ #define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */ /* bits for the control register */ -#define MTU_CRn_ENA 0x80 -#define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */ -#define MTU_CRn_PRESCALE_MASK 0x0c -#define MTU_CRn_PRESCALE_1 0x00 -#define MTU_CRn_PRESCALE_16 0x04 -#define MTU_CRn_PRESCALE_256 0x08 -#define MTU_CRn_32BITS 0x02 -#define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/ +#define MTU_CRn_ENA (0x01 << 7) +#define MTU_CRn_DIS (0x00 << 7) +#define MTU_CRn_PERIODIC (0x01 << 6) /* if 0 = free-running */ +#define MTU_CRn_FREERUNNING (0x00 << 6) +#define MTU_CRn_PRESCALE_MASK (0x03 << 2) +#define MTU_CRn_PRESCALE_1 (0x00 << 2) +#define MTU_CRn_PRESCALE_16 (0x01 << 2) +#define MTU_CRn_PRESCALE_256 (0x02 << 2) +#define MTU_CRn_32BITS (0x01 << 1) + +/* if 0 = wraps reloading from BGLR*/ +#define MTU_CRn_ONESHOT (0x01 << 0) /* Other registers are usual amba/primecell registers, currently not used */ #define MTU_ITCR 0xff0 @@ -59,9 +63,9 @@ #define MTU_PCELL2 0xff8 #define MTU_PCELL3 0xffC -static u32 u8500_count; /* accumulated count */ -static u32 u8500_cycle; /* write-once */ +static u32 u8500_cycle; /* write-once */ static __iomem void *mtu0_base; +static bool mtu_periodic = true; /* * U8500 sched_clock implementation. It has a resolution of @@ -141,10 +145,26 @@ static cycle_t u8500_read_timer_dummy(struct clocksource *cs) { return 0; } +static void mtu_clockevent_reset(void); +static void mtu_clocksource_reset(void) +{ + writel(MTU_CRn_DIS, mtu0_base + MTU_CR(1)); -void mtu_timer_reset(void); + /* ClockSource: configure load and background-load, and fire it up */ + writel(u8500_cycle, mtu0_base + MTU_LR(1)); + writel(u8500_cycle, mtu0_base + MTU_BGLR(1)); -static void u8500_clocksource_resume(struct clocksource *cs) + writel(MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS | MTU_CRn_ENA | + MTU_CRn_FREERUNNING, mtu0_base + MTU_CR(1)); +} + +void mtu_timer_reset(void) +{ + mtu_clocksource_reset(); + mtu_clockevent_reset(); +} + +static void u8500_mtu_clocksource_resume(struct clocksource *cs) { mtu_timer_reset(); } @@ -154,9 +174,9 @@ static struct clocksource u8500_clksrc = { .rating = 120, .read = u8500_read_timer_dummy, .shift = 20, - .mask = CLOCKSOURCE_MASK(32), + .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .resume = u8500_clocksource_resume, + .resume = u8500_mtu_clocksource_resume, }; #ifdef ARCH_HAS_READ_CURRENT_TIMER @@ -179,98 +199,111 @@ int read_current_timer(unsigned long *timer_val) *timer_val = u8500_read_timer(&u8500_clksrc); return 0; } - #endif /* * Clockevent device: currently only periodic mode is supported */ -static void u8500_clkevt_mode(enum clock_event_mode mode, - struct clock_event_device *dev) + +static void mtu_clockevent_reset(void) { - unsigned long flags; + if (mtu_periodic) { + + /* Timer: configure load and background-load, and fire it up */ + writel(u8500_cycle, mtu0_base + MTU_LR(0)); + writel(u8500_cycle, mtu0_base + MTU_BGLR(0)); + writel(MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | + MTU_CRn_32BITS | MTU_CRn_ENA, + mtu0_base + MTU_CR(0)); + writel(1 << 0, mtu0_base + MTU_IMSC); + } +} + +static void u8500_mtu_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *dev) +{ switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - /* enable interrupts -- and count current value? */ - raw_local_irq_save(flags); - writel(1, mtu0_base + MTU_IMSC); - raw_local_irq_restore(flags); + mtu_periodic = true; + mtu_clockevent_reset(); break; case CLOCK_EVT_MODE_ONESHOT: - BUG(); /* Not yet supported */ - /* FALLTHROUGH */ + mtu_periodic = false; + break; case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_UNUSED: - /* disable irq */ - raw_local_irq_save(flags); + writel(MTU_CRn_DIS, mtu0_base + MTU_CR(0)); writel(0, mtu0_base + MTU_IMSC); - raw_local_irq_restore(flags); break; case CLOCK_EVT_MODE_RESUME: break; } } -static struct clock_event_device u8500_clkevt = { - .name = "mtu_0", - .features = CLOCK_EVT_FEAT_PERIODIC, - .shift = 32, - .rating = 100, - .set_mode = u8500_clkevt_mode, - .irq = IRQ_MTU0, -}; +static int u8500_mtu_clkevt_next(unsigned long evt, struct clock_event_device *ev) +{ + writel(1 << 0, mtu0_base + MTU_IMSC); + writel(evt, mtu0_base + MTU_LR(0)); + /* Load highest value, enable device, enable interrupts */ + writel(MTU_CRn_ONESHOT | MTU_CRn_PRESCALE_1 | + MTU_CRn_32BITS | MTU_CRn_ENA, + mtu0_base + MTU_CR(0)); + return 0; +} /* * IRQ Handler for the timer 0 of the MTU block. The irq is not shared * as we are the only users of mtu0 by now. */ -static irqreturn_t u8500_timer_interrupt(int irq, void *dev_id) +static irqreturn_t u8500_timer_interrupt(int irq, void *dev) { + struct clock_event_device *clkevt = dev; /* ack: "interrupt clear register" */ writel(1 << 0, mtu0_base + MTU_ICR); - u8500_clkevt.event_handler(&u8500_clkevt); + clkevt->event_handler(clkevt); return IRQ_HANDLED; } /* + * Added here as asm/smp.h is removed in v2.6.34 and + * this funcitons is needed for current PM setup. + */ +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST +void smp_timer_broadcast(const struct cpumask *mask); +#endif + +static struct clock_event_device u8500_mtu_clkevt = { + .name = "mtu_0", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + /* Must be of higher rating the timer-rtt at boot */ + .rating = 100, + .set_mode = u8500_mtu_clkevt_mode, + .set_next_event = u8500_mtu_clkevt_next, + .irq = IRQ_MTU0, +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST + .broadcast = smp_timer_broadcast, +#endif + .cpumask = cpu_all_mask, +}; + +/* * Set up timer interrupt, and return the current time in seconds. */ static struct irqaction u8500_timer_irq = { .name = "MTU Timer Tick", .flags = IRQF_DISABLED | IRQF_TIMER, .handler = u8500_timer_interrupt, + .dev_id = &u8500_mtu_clkevt, }; -void mtu_timer_reset(void) -{ - u32 cr; - - writel(0, mtu0_base + MTU_CR(0)); /* off */ - writel(0, mtu0_base + MTU_CR(1)); /* off */ - - /* Timer: configure load and background-load, and fire it up */ - writel(u8500_cycle, mtu0_base + MTU_LR(0)); - writel(u8500_cycle, mtu0_base + MTU_BGLR(0)); - cr = MTU_CRn_PERIODIC | (MTU_CRn_PRESCALE_1 << 2) | MTU_CRn_32BITS; - writel(cr, mtu0_base + MTU_CR(0)); - writel(cr | MTU_CRn_ENA, mtu0_base + MTU_CR(0)); - - /* CS: configure load and background-load, and fire it up */ - writel(u8500_cycle, mtu0_base + MTU_LR(1)); - writel(u8500_cycle, mtu0_base + MTU_BGLR(1)); - cr = (MTU_CRn_PRESCALE_1 << 2) | MTU_CRn_32BITS; - writel(cr, mtu0_base + MTU_CR(1)); - writel(cr | MTU_CRn_ENA, mtu0_base + MTU_CR(1)); -} - void __init mtu_timer_init(void) { unsigned long rate; struct clk *clk0; - int bits; clk0 = clk_get_sys("mtu0", NULL); BUG_ON(IS_ERR(clk0)); @@ -287,29 +320,32 @@ void __init mtu_timer_init(void) /* Save global pointer to mtu, used by functions above */ if (cpu_is_u5500()) { - mtu0_base = __io_address(U5500_MTU0_BASE); - } else if (cpu_is_u8500ed()) { - mtu0_base = __io_address(U8500_MTU0_BASE_ED); + mtu0_base = ioremap(U5500_MTU0_BASE, SZ_4K); } else if (cpu_is_u8500()) { - mtu0_base = __io_address(U8500_MTU0_BASE); - } else + mtu0_base = ioremap(U8500_MTU0_BASE, SZ_4K); + } else { ux500_unknown_soc(); + } - /* Init the timer and register clocksource */ - mtu_timer_reset(); + /* Restart clock source */ + mtu_clocksource_reset(); /* Now the scheduling clock is ready */ u8500_clksrc.read = u8500_read_timer; u8500_clksrc.mult = clocksource_hz2mult(rate, u8500_clksrc.shift); - bits = 8*sizeof(u8500_count); clocksource_register(&u8500_clksrc); /* Register irq and clockevents */ + u8500_mtu_clkevt.mult = div_sc(rate, NSEC_PER_SEC, + u8500_mtu_clkevt.shift); + u8500_mtu_clkevt.max_delta_ns = clockevent_delta2ns(0xffffffff, + &u8500_mtu_clkevt); + u8500_mtu_clkevt.min_delta_ns = clockevent_delta2ns(0xff, + &u8500_mtu_clkevt); + setup_irq(IRQ_MTU0, &u8500_timer_irq); - u8500_clkevt.mult = div_sc(rate, NSEC_PER_SEC, u8500_clkevt.shift); - u8500_clkevt.cpumask = cpumask_of(0); - clockevents_register_device(&u8500_clkevt); + clockevents_register_device(&u8500_mtu_clkevt); #ifdef ARCH_HAS_READ_CURRENT_TIMER set_delay_fn(mtu_timer_delay_loop); #endif |