aboutsummaryrefslogtreecommitdiffstats
path: root/arch/avr32/mach-at32ap
diff options
context:
space:
mode:
authorHans-Christian Egtvedt <hcegtvedt@atmel.com>2007-03-12 13:15:16 -0400
committerHaavard Skinnemoen <hskinnemoen@atmel.com>2007-04-27 07:44:12 -0400
commit7760989e5e2900e484e9115e6e690c6ce0b0221c (patch)
tree465791d8a5d0360a6d41fc592c09abd4932aafe1 /arch/avr32/mach-at32ap
parent228e845fd243bf42033998afab792357444e9e4a (diff)
[AVR32] Change system timer from count-compare to Timer/Counter 0
Due to limitation of the count-compare system timer (not able to count when CPU is in sleep), the system timer had to be changed to use a peripheral timer/counter. The old COUNT-COMPARE code is still present in time.c as weak functions. The new timer is added to the architecture directory. This patch sets up TC0 as system timer The new timer has been tested on AT32AP7000/ATSTK1000 at 100 Hz, 250 Hz, 300 Hz and 1000 Hz. For more details about the timer/counter see the datasheet for AT32AP700x available at http://www.atmel.com/dyn/products/product_card.asp?part_id=3903 Signed-off-by: Hans-Christian Egtvedt <hcegtvedt@atmel.com> Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Diffstat (limited to 'arch/avr32/mach-at32ap')
-rw-r--r--arch/avr32/mach-at32ap/Makefile1
-rw-r--r--arch/avr32/mach-at32ap/at32ap7000.c18
-rw-r--r--arch/avr32/mach-at32ap/time-tc.c217
3 files changed, 236 insertions, 0 deletions
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile
index b21bea9af8b1..f1d395724ac6 100644
--- a/arch/avr32/mach-at32ap/Makefile
+++ b/arch/avr32/mach-at32ap/Makefile
@@ -1,2 +1,3 @@
1obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o 1obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o
2obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o 2obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o
3obj-$(CONFIG_CPU_AT32AP7000) += time-tc.o
diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c
index 32c7045141c2..6eeda60b8288 100644
--- a/arch/avr32/mach-at32ap/at32ap7000.c
+++ b/arch/avr32/mach-at32ap/at32ap7000.c
@@ -504,6 +504,21 @@ static inline void set_ebi_sfr_bits(u32 mask)
504} 504}
505 505
506/* -------------------------------------------------------------------- 506/* --------------------------------------------------------------------
507 * System Timer/Counter (TC)
508 * -------------------------------------------------------------------- */
509static struct resource at32_systc0_resource[] = {
510 PBMEM(0xfff00c00),
511 IRQ(22),
512};
513struct platform_device at32_systc0_device = {
514 .name = "systc",
515 .id = 0,
516 .resource = at32_systc0_resource,
517 .num_resources = ARRAY_SIZE(at32_systc0_resource),
518};
519DEV_CLK(pclk, at32_systc0, pbb, 3);
520
521/* --------------------------------------------------------------------
507 * PIO 522 * PIO
508 * -------------------------------------------------------------------- */ 523 * -------------------------------------------------------------------- */
509 524
@@ -551,6 +566,8 @@ void __init at32_add_system_devices(void)
551 platform_device_register(&smc0_device); 566 platform_device_register(&smc0_device);
552 platform_device_register(&pdc_device); 567 platform_device_register(&pdc_device);
553 568
569 platform_device_register(&at32_systc0_device);
570
554 platform_device_register(&pio0_device); 571 platform_device_register(&pio0_device);
555 platform_device_register(&pio1_device); 572 platform_device_register(&pio1_device);
556 platform_device_register(&pio2_device); 573 platform_device_register(&pio2_device);
@@ -1000,6 +1017,7 @@ struct clk *at32_clock_list[] = {
1000 &pio2_mck, 1017 &pio2_mck,
1001 &pio3_mck, 1018 &pio3_mck,
1002 &pio4_mck, 1019 &pio4_mck,
1020 &at32_systc0_pclk,
1003 &atmel_usart0_usart, 1021 &atmel_usart0_usart,
1004 &atmel_usart1_usart, 1022 &atmel_usart1_usart,
1005 &atmel_usart2_usart, 1023 &atmel_usart2_usart,
diff --git a/arch/avr32/mach-at32ap/time-tc.c b/arch/avr32/mach-at32ap/time-tc.c
new file mode 100644
index 000000000000..7ac0e94d8ed5
--- /dev/null
+++ b/arch/avr32/mach-at32ap/time-tc.c
@@ -0,0 +1,217 @@
1/*
2 * Copyright (C) 2004-2007 Atmel Corporation
3 *
4 * Based on MIPS implementation arch/mips/kernel/time.c
5 * Copyright 2001 MontaVista Software Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/clk.h>
13#include <linux/clocksource.h>
14#include <linux/time.h>
15#include <linux/module.h>
16#include <linux/interrupt.h>
17#include <linux/irq.h>
18#include <linux/kernel_stat.h>
19#include <linux/errno.h>
20#include <linux/init.h>
21#include <linux/profile.h>
22#include <linux/sysdev.h>
23#include <linux/err.h>
24
25#include <asm/div64.h>
26#include <asm/sysreg.h>
27#include <asm/io.h>
28#include <asm/sections.h>
29
30#include <asm/arch/time.h>
31
32/* how many counter cycles in a jiffy? */
33static u32 cycles_per_jiffy;
34
35/* the count value for the next timer interrupt */
36static u32 expirelo;
37
38/* the I/O registers of the TC module */
39static void __iomem *ioregs;
40
41cycle_t read_cycle_count(void)
42{
43 return (cycle_t)timer_read(ioregs, 0, CV);
44}
45
46struct clocksource clocksource_avr32 = {
47 .name = "avr32",
48 .rating = 342,
49 .read = read_cycle_count,
50 .mask = CLOCKSOURCE_MASK(16),
51 .shift = 16,
52 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
53};
54
55static void avr32_timer_ack(void)
56{
57 u16 count = expirelo;
58
59 /* Ack this timer interrupt and set the next one, use a u16
60 * variable so it will wrap around correctly */
61 count += cycles_per_jiffy;
62 expirelo = count;
63 timer_write(ioregs, 0, RC, expirelo);
64
65 /* Check to see if we have missed any timer interrupts */
66 count = timer_read(ioregs, 0, CV);
67 if ((count - expirelo) < 0x7fff) {
68 expirelo = count + cycles_per_jiffy;
69 timer_write(ioregs, 0, RC, expirelo);
70 }
71}
72
73u32 avr32_hpt_read(void)
74{
75 return timer_read(ioregs, 0, CV);
76}
77
78static int avr32_timer_calc_div_and_set_jiffies(struct clk *pclk)
79{
80 unsigned int cycles_max = (clocksource_avr32.mask + 1) / 2;
81 unsigned int divs[] = { 4, 8, 16, 32 };
82 int divs_size = sizeof(divs) / sizeof(*divs);
83 int i = 0;
84 unsigned long count_hz;
85 unsigned long shift;
86 unsigned long mult;
87 int clock_div = -1;
88 u64 tmp;
89
90 shift = clocksource_avr32.shift;
91
92 do {
93 count_hz = clk_get_rate(pclk) / divs[i];
94 mult = clocksource_hz2mult(count_hz, shift);
95 clocksource_avr32.mult = mult;
96
97 tmp = TICK_NSEC;
98 tmp <<= shift;
99 tmp += mult / 2;
100 do_div(tmp, mult);
101
102 cycles_per_jiffy = tmp;
103 } while (cycles_per_jiffy > cycles_max && ++i < divs_size);
104
105 clock_div = i + 1;
106
107 if (clock_div > divs_size) {
108 pr_debug("timer: could not calculate clock divider\n");
109 return -EFAULT;
110 }
111
112 /* Set the clock divider */
113 timer_write(ioregs, 0, CMR, TIMER_BF(CMR_TCCLKS, clock_div));
114
115 return 0;
116}
117
118int avr32_hpt_init(unsigned int count)
119{
120 struct resource *regs;
121 struct clk *pclk;
122 int irq = -1;
123 int ret = 0;
124
125 ret = -ENXIO;
126
127 irq = platform_get_irq(&at32_systc0_device, 0);
128 if (irq < 0) {
129 pr_debug("timer: could not get irq\n");
130 goto out_error;
131 }
132
133 pclk = clk_get(&at32_systc0_device.dev, "pclk");
134 if (IS_ERR(pclk)) {
135 pr_debug("timer: could not get clk: %ld\n", PTR_ERR(pclk));
136 goto out_error;
137 }
138
139 regs = platform_get_resource(&at32_systc0_device, IORESOURCE_MEM, 0);
140 if (!regs) {
141 pr_debug("timer: could not get resource\n");
142 goto out_error_clk;
143 }
144
145 ioregs = ioremap(regs->start, regs->end - regs->start + 1);
146 if (!ioregs) {
147 pr_debug("timer: could not get ioregs\n");
148 goto out_error_clk;
149 }
150
151 ret = avr32_timer_calc_div_and_set_jiffies(pclk);
152 if (ret)
153 goto out_error_io;
154
155 ret = setup_irq(irq, &timer_irqaction);
156 if (ret) {
157 pr_debug("timer: could not request irq %d: %d\n",
158 irq, ret);
159 goto out_error_io;
160 }
161
162 expirelo = (timer_read(ioregs, 0, CV) / cycles_per_jiffy + 1)
163 * cycles_per_jiffy;
164
165 /* Enable clock and interrupts on RC compare */
166 timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_CLKEN));
167 timer_write(ioregs, 0, IER, TIMER_BIT(IER_CPCS));
168 /* Set cycles to first interrupt */
169 timer_write(ioregs, 0, RC, expirelo);
170
171 printk(KERN_INFO "timer: AT32AP system timer/counter at 0x%p irq %d\n",
172 ioregs, irq);
173
174 return 0;
175
176out_error_io:
177 iounmap(ioregs);
178out_error_clk:
179 clk_put(pclk);
180out_error:
181 return ret;
182}
183
184int avr32_hpt_start(void)
185{
186 timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_SWTRG));
187 return 0;
188}
189
190irqreturn_t timer_interrupt(int irq, void *dev_id)
191{
192 unsigned int sr = timer_read(ioregs, 0, SR);
193
194 if (sr & TIMER_BIT(SR_CPCS)) {
195 /* ack timer interrupt and try to set next interrupt */
196 avr32_timer_ack();
197
198 /*
199 * Call the generic timer interrupt handler
200 */
201 write_seqlock(&xtime_lock);
202 do_timer(1);
203 write_sequnlock(&xtime_lock);
204
205 /*
206 * In UP mode, we call local_timer_interrupt() to do profiling
207 * and process accounting.
208 *
209 * SMP is not supported yet.
210 */
211 local_timer_interrupt(irq, dev_id);
212
213 return IRQ_HANDLED;
214 }
215
216 return IRQ_NONE;
217}