diff options
author | David Brownell <david-b@pacbell.net> | 2008-02-14 11:24:02 -0800 |
---|---|---|
committer | Haavard Skinnemoen <haavard.skinnemoen@atmel.com> | 2008-04-19 20:40:08 -0400 |
commit | e723ff666a5da8f7fda4e36ebfeafac2175a5c6e (patch) | |
tree | 53d2b1a0190795977ddccbda2085598d7ea14e43 /arch/avr32/mach-at32ap | |
parent | 7e59128f31e0c57d52e86d57730d4c9281494dda (diff) |
avr32: Generic clockevents support
This combines three patches from David Brownell:
* avr32: tclib support
* avr32: simplify clocksources
* avr32: Turn count/compare into a oneshot clockevent device
Register both TC blocks (instead of just the first one) so that
the AT32/AT91 tclib code will pick them up (instead of just the
avr32-only PIT-style clocksource).
Rename the first one and its resources appropriately.
More cleanups to the cycle counter clocksource code
- Disable all the weak symbol magic; remove the AVR32-only TCB-based
clocksource code (source and header).
- Mark the __init code properly.
- Don't forget to report IRQF_TIMER.
- Make the system work properly with this clocksource, by preventing
use of the CPU "idle" sleep state in the idle loop when it's used.
Package the avr32 count/compare timekeeping support as a oneshot
clockevent device, so it supports NO_HZ and high res timers.
This means it also supports plugging in other clockevent devices
and clocksources.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Diffstat (limited to 'arch/avr32/mach-at32ap')
-rw-r--r-- | arch/avr32/mach-at32ap/Makefile | 1 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap700x.c | 33 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/time-tc.c | 218 |
3 files changed, 24 insertions, 228 deletions
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index 83cab2abb6c..e89009439e4 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile @@ -1,4 +1,3 @@ obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o obj-$(CONFIG_CPU_AT32AP700X) += at32ap700x.o pm-at32ap700x.o -obj-$(CONFIG_CPU_AT32AP700X) += time-tc.o obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 6302bfd5851..22c302ad9b3 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -606,19 +606,32 @@ static inline void set_ebi_sfr_bits(u32 mask) } /* -------------------------------------------------------------------- - * System Timer/Counter (TC) + * Timer/Counter (TC) * -------------------------------------------------------------------- */ -static struct resource at32_systc0_resource[] = { + +static struct resource at32_tcb0_resource[] = { PBMEM(0xfff00c00), IRQ(22), }; -struct platform_device at32_systc0_device = { - .name = "systc", +static struct platform_device at32_tcb0_device = { + .name = "atmel_tcb", .id = 0, - .resource = at32_systc0_resource, - .num_resources = ARRAY_SIZE(at32_systc0_resource), + .resource = at32_tcb0_resource, + .num_resources = ARRAY_SIZE(at32_tcb0_resource), +}; +DEV_CLK(t0_clk, at32_tcb0, pbb, 3); + +static struct resource at32_tcb1_resource[] = { + PBMEM(0xfff01000), + IRQ(23), +}; +static struct platform_device at32_tcb1_device = { + .name = "atmel_tcb", + .id = 1, + .resource = at32_tcb1_resource, + .num_resources = ARRAY_SIZE(at32_tcb1_resource), }; -DEV_CLK(pclk, at32_systc0, pbb, 3); +DEV_CLK(t0_clk, at32_tcb1, pbb, 4); /* -------------------------------------------------------------------- * PIO @@ -670,7 +683,8 @@ void __init at32_add_system_devices(void) platform_device_register(&pdc_device); platform_device_register(&dmaca0_device); - platform_device_register(&at32_systc0_device); + platform_device_register(&at32_tcb0_device); + platform_device_register(&at32_tcb1_device); platform_device_register(&pio0_device); platform_device_register(&pio1_device); @@ -1737,7 +1751,8 @@ struct clk *at32_clock_list[] = { &pio2_mck, &pio3_mck, &pio4_mck, - &at32_systc0_pclk, + &at32_tcb0_t0_clk, + &at32_tcb1_t0_clk, &atmel_usart0_usart, &atmel_usart1_usart, &atmel_usart2_usart, diff --git a/arch/avr32/mach-at32ap/time-tc.c b/arch/avr32/mach-at32ap/time-tc.c deleted file mode 100644 index 10265863c98..00000000000 --- a/arch/avr32/mach-at32ap/time-tc.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2004-2007 Atmel Corporation - * - * Based on MIPS implementation arch/mips/kernel/time.c - * Copyright 2001 MontaVista Software Inc. - * - * 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/clk.h> -#include <linux/clocksource.h> -#include <linux/time.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/profile.h> -#include <linux/sysdev.h> -#include <linux/err.h> - -#include <asm/div64.h> -#include <asm/sysreg.h> -#include <asm/io.h> -#include <asm/sections.h> - -#include <asm/arch/time.h> - -/* how many counter cycles in a jiffy? */ -static u32 cycles_per_jiffy; - -/* the count value for the next timer interrupt */ -static u32 expirelo; - -/* the I/O registers of the TC module */ -static void __iomem *ioregs; - -cycle_t read_cycle_count(void) -{ - return (cycle_t)timer_read(ioregs, 0, CV); -} - -struct clocksource clocksource_avr32 = { - .name = "avr32", - .rating = 342, - .read = read_cycle_count, - .mask = CLOCKSOURCE_MASK(16), - .shift = 16, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; - -static void avr32_timer_ack(void) -{ - u16 count = expirelo; - - /* Ack this timer interrupt and set the next one, use a u16 - * variable so it will wrap around correctly */ - count += cycles_per_jiffy; - expirelo = count; - timer_write(ioregs, 0, RC, expirelo); - - /* Check to see if we have missed any timer interrupts */ - count = timer_read(ioregs, 0, CV); - if ((count - expirelo) < 0x7fff) { - expirelo = count + cycles_per_jiffy; - timer_write(ioregs, 0, RC, expirelo); - } -} - -u32 avr32_hpt_read(void) -{ - return timer_read(ioregs, 0, CV); -} - -static int avr32_timer_calc_div_and_set_jiffies(struct clk *pclk) -{ - unsigned int cycles_max = (clocksource_avr32.mask + 1) / 2; - unsigned int divs[] = { 4, 8, 16, 32 }; - int divs_size = ARRAY_SIZE(divs); - int i = 0; - unsigned long count_hz; - unsigned long shift; - unsigned long mult; - int clock_div = -1; - u64 tmp; - - shift = clocksource_avr32.shift; - - do { - count_hz = clk_get_rate(pclk) / divs[i]; - mult = clocksource_hz2mult(count_hz, shift); - clocksource_avr32.mult = mult; - - tmp = TICK_NSEC; - tmp <<= shift; - tmp += mult / 2; - do_div(tmp, mult); - - cycles_per_jiffy = tmp; - } while (cycles_per_jiffy > cycles_max && ++i < divs_size); - - clock_div = i + 1; - - if (clock_div > divs_size) { - pr_debug("timer: could not calculate clock divider\n"); - return -EFAULT; - } - - /* Set the clock divider */ - timer_write(ioregs, 0, CMR, TIMER_BF(CMR_TCCLKS, clock_div)); - - return 0; -} - -int avr32_hpt_init(unsigned int count) -{ - struct resource *regs; - struct clk *pclk; - int irq = -1; - int ret = 0; - - ret = -ENXIO; - - irq = platform_get_irq(&at32_systc0_device, 0); - if (irq < 0) { - pr_debug("timer: could not get irq\n"); - goto out_error; - } - - pclk = clk_get(&at32_systc0_device.dev, "pclk"); - if (IS_ERR(pclk)) { - pr_debug("timer: could not get clk: %ld\n", PTR_ERR(pclk)); - goto out_error; - } - clk_enable(pclk); - - regs = platform_get_resource(&at32_systc0_device, IORESOURCE_MEM, 0); - if (!regs) { - pr_debug("timer: could not get resource\n"); - goto out_error_clk; - } - - ioregs = ioremap(regs->start, regs->end - regs->start + 1); - if (!ioregs) { - pr_debug("timer: could not get ioregs\n"); - goto out_error_clk; - } - - ret = avr32_timer_calc_div_and_set_jiffies(pclk); - if (ret) - goto out_error_io; - - ret = setup_irq(irq, &timer_irqaction); - if (ret) { - pr_debug("timer: could not request irq %d: %d\n", - irq, ret); - goto out_error_io; - } - - expirelo = (timer_read(ioregs, 0, CV) / cycles_per_jiffy + 1) - * cycles_per_jiffy; - - /* Enable clock and interrupts on RC compare */ - timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_CLKEN)); - timer_write(ioregs, 0, IER, TIMER_BIT(IER_CPCS)); - /* Set cycles to first interrupt */ - timer_write(ioregs, 0, RC, expirelo); - - printk(KERN_INFO "timer: AT32AP system timer/counter at 0x%p irq %d\n", - ioregs, irq); - - return 0; - -out_error_io: - iounmap(ioregs); -out_error_clk: - clk_put(pclk); -out_error: - return ret; -} - -int avr32_hpt_start(void) -{ - timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_SWTRG)); - return 0; -} - -irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - unsigned int sr = timer_read(ioregs, 0, SR); - - if (sr & TIMER_BIT(SR_CPCS)) { - /* ack timer interrupt and try to set next interrupt */ - avr32_timer_ack(); - - /* - * Call the generic timer interrupt handler - */ - write_seqlock(&xtime_lock); - do_timer(1); - write_sequnlock(&xtime_lock); - - /* - * In UP mode, we call local_timer_interrupt() to do profiling - * and process accounting. - * - * SMP is not supported yet. - */ - local_timer_interrupt(irq, dev_id); - - return IRQ_HANDLED; - } - - return IRQ_NONE; -} |