diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-08 08:12:43 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-08 08:12:43 -0800 |
commit | 79c9601c2e0dbbe69895d302de4d19f3a31fbd30 (patch) | |
tree | 78d4be2df851b2b4106adcfd736622a90cecf9e9 /arch/arm/plat-nomadik/timer.c | |
parent | 41440ffe21f29bdb985cab76b2d0b06d83e63b19 (diff) | |
parent | 3d14b5beba35250c548d3851a2b84fce742d8311 (diff) |
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
* 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm: (272 commits)
Fix soc_common PCMCIA configuration
ARM: 5827/1: SA1100: h3100/h3600: emit messages on failed gpio_request
ARM: 5826/1: SA1100: h3100/h3600: always build htc-egpio driver
ARM: 5825/1: SA1100: h3600: update defconfig
ARM: 5824/1: SA1100: reuse h3600 PCMCIA driver on h3100
ARM: 5823/1: SA1100: h3100/h3600: add support for gpio-keys
ARM: 5822/1: SA1100: h3100/h3600: clean up #includes
ARM: 5821/1: SA1100: h3100/h3600: revise copyright boilerplates
ARM: 5820/1: SA1100: h3100/h3600: split h3600.c
ARM: 5819/1: SA1100: h3100/h3600: merge h3600.h and h3600_gpio.h into h3xxx.h
ARM: 5818/1: SA1100: h3100/h3600: drop old GPIO definitions
ARM: 5817/1: SA1100: h3100/h3600: configure all unused gpios as inputs
ARM: 5816/1: SA1100: h3600: remove IRQ_GPIO_* definitions
ARM: 5815/1: SA1100: h3100/h3600: remove now unused assign_h3600_egpio handlers
ARM: 5814/1: SA1100: h3100/h3600: convert all users of assign_h3600_egpio to gpiolib
ARM: 5813/1: SA1100: h3100/h3600: add htc-egpio driver
ARM: 5812/1: SA1100: h3100/h3600: separate machine-specific LCD helpers
ARM: 5811/2: pcmcia: convert sa1100_h3600 driver to gpiolib
ARM: 5799/1: SA1100: h3600: stop setting direction for LCD pins
ARM: 5798/1: SA1100: h3600: remove unused cruft from h3600.h
...
Diffstat (limited to 'arch/arm/plat-nomadik/timer.c')
-rw-r--r-- | arch/arm/plat-nomadik/timer.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c new file mode 100644 index 00000000000..62f18ad43a2 --- /dev/null +++ b/arch/arm/plat-nomadik/timer.c @@ -0,0 +1,147 @@ +/* + * linux/arch/arm/mach-nomadik/timer.c + * + * Copyright (C) 2008 STMicroelectronics + * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/clockchips.h> +#include <linux/jiffies.h> +#include <asm/mach/time.h> + +#include <plat/mtu.h> + +static u32 nmdk_count; /* accumulated count */ +static u32 nmdk_cycle; /* write-once */ + +/* setup by the platform code */ +void __iomem *mtu_base; + +/* + * clocksource: the MTU device is a decrementing counters, so we negate + * the value being read. + */ +static cycle_t nmdk_read_timer(struct clocksource *cs) +{ + u32 count = readl(mtu_base + MTU_VAL(0)); + return nmdk_count + nmdk_cycle - count; + +} + +static struct clocksource nmdk_clksrc = { + .name = "mtu_0", + .rating = 120, + .read = nmdk_read_timer, + .shift = 20, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +/* + * Clockevent device: currently only periodic mode is supported + */ +static void nmdk_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *dev) +{ + unsigned long flags; + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* enable interrupts -- and count current value? */ + raw_local_irq_save(flags); + writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC); + raw_local_irq_restore(flags); + break; + case CLOCK_EVT_MODE_ONESHOT: + BUG(); /* Not supported, yet */ + /* FALLTHROUGH */ + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + /* disable irq */ + raw_local_irq_save(flags); + writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC); + raw_local_irq_restore(flags); + break; + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static struct clock_event_device nmdk_clkevt = { + .name = "mtu_0", + .features = CLOCK_EVT_FEAT_PERIODIC, + .shift = 32, + .rating = 100, + .set_mode = nmdk_clkevt_mode, +}; + +/* + * 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 nmdk_timer_interrupt(int irq, void *dev_id) +{ + /* ack: "interrupt clear register" */ + writel(1 << 0, mtu_base + MTU_ICR); + + /* we can't count lost ticks, unfortunately */ + nmdk_count += nmdk_cycle; + nmdk_clkevt.event_handler(&nmdk_clkevt); + + return IRQ_HANDLED; +} + +/* + * Set up timer interrupt, and return the current time in seconds. + */ +static struct irqaction nmdk_timer_irq = { + .name = "Nomadik Timer Tick", + .flags = IRQF_DISABLED | IRQF_TIMER, + .handler = nmdk_timer_interrupt, +}; + +static void nmdk_timer_reset(void) +{ + u32 cr; + + writel(0, mtu_base + MTU_CR(0)); /* off */ + + /* configure load and background-load, and fire it up */ + writel(nmdk_cycle, mtu_base + MTU_LR(0)); + writel(nmdk_cycle, mtu_base + MTU_BGLR(0)); + cr = MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS; + writel(cr, mtu_base + MTU_CR(0)); + writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0)); +} + +void __init nmdk_timer_init(void) +{ + unsigned long rate; + int bits; + + rate = CLOCK_TICK_RATE; /* 2.4MHz */ + nmdk_cycle = (rate + HZ/2) / HZ; + + /* Init the timer and register clocksource */ + nmdk_timer_reset(); + + nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift); + bits = 8*sizeof(nmdk_count); + nmdk_clksrc.mask = CLOCKSOURCE_MASK(bits); + + if (clocksource_register(&nmdk_clksrc)) + printk(KERN_ERR "timer: failed to initialize clock " + "source %s\n", nmdk_clksrc.name); + + /* Register irq and clockevents */ + setup_irq(IRQ_MTU0, &nmdk_timer_irq); + nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift); + nmdk_clkevt.cpumask = cpumask_of(0); + clockevents_register_device(&nmdk_clkevt); +} |