aboutsummaryrefslogtreecommitdiffstats
path: root/arch/avr32
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
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')
-rw-r--r--arch/avr32/kernel/time.c150
-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
4 files changed, 313 insertions, 73 deletions
diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c
index c10833f2ee0c..7014a3571ec0 100644
--- a/arch/avr32/kernel/time.c
+++ b/arch/avr32/kernel/time.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2004-2006 Atmel Corporation 2 * Copyright (C) 2004-2007 Atmel Corporation
3 * 3 *
4 * Based on MIPS implementation arch/mips/kernel/time.c 4 * Based on MIPS implementation arch/mips/kernel/time.c
5 * Copyright 2001 MontaVista Software Inc. 5 * Copyright 2001 MontaVista Software Inc.
@@ -20,18 +20,25 @@
20#include <linux/init.h> 20#include <linux/init.h>
21#include <linux/profile.h> 21#include <linux/profile.h>
22#include <linux/sysdev.h> 22#include <linux/sysdev.h>
23#include <linux/err.h>
23 24
24#include <asm/div64.h> 25#include <asm/div64.h>
25#include <asm/sysreg.h> 26#include <asm/sysreg.h>
26#include <asm/io.h> 27#include <asm/io.h>
27#include <asm/sections.h> 28#include <asm/sections.h>
28 29
29static cycle_t read_cycle_count(void) 30/* how many counter cycles in a jiffy? */
31static u32 cycles_per_jiffy;
32
33/* the count value for the next timer interrupt */
34static u32 expirelo;
35
36cycle_t __weak read_cycle_count(void)
30{ 37{
31 return (cycle_t)sysreg_read(COUNT); 38 return (cycle_t)sysreg_read(COUNT);
32} 39}
33 40
34static struct clocksource clocksource_avr32 = { 41struct clocksource __weak clocksource_avr32 = {
35 .name = "avr32", 42 .name = "avr32",
36 .rating = 350, 43 .rating = 350,
37 .read = read_cycle_count, 44 .read = read_cycle_count,
@@ -40,12 +47,20 @@ static struct clocksource clocksource_avr32 = {
40 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 47 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
41}; 48};
42 49
50irqreturn_t __weak timer_interrupt(int irq, void *dev_id);
51
52struct irqaction timer_irqaction = {
53 .handler = timer_interrupt,
54 .flags = IRQF_DISABLED,
55 .name = "timer",
56};
57
43/* 58/*
44 * By default we provide the null RTC ops 59 * By default we provide the null RTC ops
45 */ 60 */
46static unsigned long null_rtc_get_time(void) 61static unsigned long null_rtc_get_time(void)
47{ 62{
48 return mktime(2004, 1, 1, 0, 0, 0); 63 return mktime(2007, 1, 1, 0, 0, 0);
49} 64}
50 65
51static int null_rtc_set_time(unsigned long sec) 66static int null_rtc_set_time(unsigned long sec)
@@ -56,23 +71,14 @@ static int null_rtc_set_time(unsigned long sec)
56static unsigned long (*rtc_get_time)(void) = null_rtc_get_time; 71static unsigned long (*rtc_get_time)(void) = null_rtc_get_time;
57static int (*rtc_set_time)(unsigned long) = null_rtc_set_time; 72static int (*rtc_set_time)(unsigned long) = null_rtc_set_time;
58 73
59/* how many counter cycles in a jiffy? */
60static unsigned long cycles_per_jiffy;
61
62/* cycle counter value at the previous timer interrupt */
63static unsigned int timerhi, timerlo;
64
65/* the count value for the next timer interrupt */
66static unsigned int expirelo;
67
68static void avr32_timer_ack(void) 74static void avr32_timer_ack(void)
69{ 75{
70 unsigned int count; 76 u32 count;
71 77
72 /* Ack this timer interrupt and set the next one */ 78 /* Ack this timer interrupt and set the next one */
73 expirelo += cycles_per_jiffy; 79 expirelo += cycles_per_jiffy;
80 /* setting COMPARE to 0 stops the COUNT-COMPARE */
74 if (expirelo == 0) { 81 if (expirelo == 0) {
75 printk(KERN_DEBUG "expirelo == 0\n");
76 sysreg_write(COMPARE, expirelo + 1); 82 sysreg_write(COMPARE, expirelo + 1);
77 } else { 83 } else {
78 sysreg_write(COMPARE, expirelo); 84 sysreg_write(COMPARE, expirelo);
@@ -86,27 +92,56 @@ static void avr32_timer_ack(void)
86 } 92 }
87} 93}
88 94
89static unsigned int avr32_hpt_read(void) 95int __weak avr32_hpt_init(void)
90{ 96{
91 return sysreg_read(COUNT); 97 int ret;
98 unsigned long mult, shift, count_hz;
99
100 count_hz = clk_get_rate(boot_cpu_data.clk);
101 shift = clocksource_avr32.shift;
102 mult = clocksource_hz2mult(count_hz, shift);
103 clocksource_avr32.mult = mult;
104
105 {
106 u64 tmp;
107
108 tmp = TICK_NSEC;
109 tmp <<= shift;
110 tmp += mult / 2;
111 do_div(tmp, mult);
112
113 cycles_per_jiffy = tmp;
114 }
115
116 ret = setup_irq(0, &timer_irqaction);
117 if (ret) {
118 pr_debug("timer: could not request IRQ 0: %d\n", ret);
119 return -ENODEV;
120 }
121
122 printk(KERN_INFO "timer: AT32AP COUNT-COMPARE at irq 0, "
123 "%lu.%03lu MHz\n",
124 ((count_hz + 500) / 1000) / 1000,
125 ((count_hz + 500) / 1000) % 1000);
126
127 return 0;
92} 128}
93 129
94/* 130/*
95 * Taken from MIPS c0_hpt_timer_init(). 131 * Taken from MIPS c0_hpt_timer_init().
96 * 132 *
97 * Why is it so complicated, and what is "count"? My assumption is 133 * The reason COUNT is written twice is probably to make sure we don't get any
98 * that `count' specifies the "reference cycle", i.e. the cycle since 134 * timer interrupts while we are messing with the counter.
99 * reset that should mean "zero". The reason COUNT is written twice is
100 * probably to make sure we don't get any timer interrupts while we
101 * are messing with the counter.
102 */ 135 */
103static void avr32_hpt_init(unsigned int count) 136int __weak avr32_hpt_start(void)
104{ 137{
105 count = sysreg_read(COUNT) - count; 138 u32 count = sysreg_read(COUNT);
106 expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy; 139 expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy;
107 sysreg_write(COUNT, expirelo - cycles_per_jiffy); 140 sysreg_write(COUNT, expirelo - cycles_per_jiffy);
108 sysreg_write(COMPARE, expirelo); 141 sysreg_write(COMPARE, expirelo);
109 sysreg_write(COUNT, count); 142 sysreg_write(COUNT, count);
143
144 return 0;
110} 145}
111 146
112/* 147/*
@@ -115,26 +150,18 @@ static void avr32_hpt_init(unsigned int count)
115 * 150 *
116 * In UP mode, it is invoked from the (global) timer_interrupt. 151 * In UP mode, it is invoked from the (global) timer_interrupt.
117 */ 152 */
118static void local_timer_interrupt(int irq, void *dev_id) 153void local_timer_interrupt(int irq, void *dev_id)
119{ 154{
120 if (current->pid) 155 if (current->pid)
121 profile_tick(CPU_PROFILING); 156 profile_tick(CPU_PROFILING);
122 update_process_times(user_mode(get_irq_regs())); 157 update_process_times(user_mode(get_irq_regs()));
123} 158}
124 159
125static irqreturn_t 160irqreturn_t __weak timer_interrupt(int irq, void *dev_id)
126timer_interrupt(int irq, void *dev_id)
127{ 161{
128 unsigned int count;
129
130 /* ack timer interrupt and try to set next interrupt */ 162 /* ack timer interrupt and try to set next interrupt */
131 count = avr32_hpt_read();
132 avr32_timer_ack(); 163 avr32_timer_ack();
133 164
134 /* Update timerhi/timerlo for intra-jiffy calibration */
135 timerhi += count < timerlo; /* Wrap around */
136 timerlo = count;
137
138 /* 165 /*
139 * Call the generic timer interrupt handler 166 * Call the generic timer interrupt handler
140 */ 167 */
@@ -153,60 +180,37 @@ timer_interrupt(int irq, void *dev_id)
153 return IRQ_HANDLED; 180 return IRQ_HANDLED;
154} 181}
155 182
156static struct irqaction timer_irqaction = {
157 .handler = timer_interrupt,
158 .flags = IRQF_DISABLED,
159 .name = "timer",
160};
161
162void __init time_init(void) 183void __init time_init(void)
163{ 184{
164 unsigned long mult, shift, count_hz;
165 int ret; 185 int ret;
166 186
187 /*
188 * Make sure we don't get any COMPARE interrupts before we can
189 * handle them.
190 */
191 sysreg_write(COMPARE, 0);
192
167 xtime.tv_sec = rtc_get_time(); 193 xtime.tv_sec = rtc_get_time();
168 xtime.tv_nsec = 0; 194 xtime.tv_nsec = 0;
169 195
170 set_normalized_timespec(&wall_to_monotonic, 196 set_normalized_timespec(&wall_to_monotonic,
171 -xtime.tv_sec, -xtime.tv_nsec); 197 -xtime.tv_sec, -xtime.tv_nsec);
172 198
173 printk("Before time_init: count=%08lx, compare=%08lx\n", 199 ret = avr32_hpt_init();
174 (unsigned long)sysreg_read(COUNT), 200 if (ret) {
175 (unsigned long)sysreg_read(COMPARE)); 201 pr_debug("timer: failed setup: %d\n", ret);
176 202 return;
177 count_hz = clk_get_rate(boot_cpu_data.clk);
178 shift = clocksource_avr32.shift;
179 mult = clocksource_hz2mult(count_hz, shift);
180 clocksource_avr32.mult = mult;
181
182 printk("Cycle counter: mult=%lu, shift=%lu\n", mult, shift);
183
184 {
185 u64 tmp;
186
187 tmp = TICK_NSEC;
188 tmp <<= shift;
189 tmp += mult / 2;
190 do_div(tmp, mult);
191
192 cycles_per_jiffy = tmp;
193 } 203 }
194 204
195 /* This sets up the high precision timer for the first interrupt. */
196 avr32_hpt_init(avr32_hpt_read());
197
198 printk("After time_init: count=%08lx, compare=%08lx\n",
199 (unsigned long)sysreg_read(COUNT),
200 (unsigned long)sysreg_read(COMPARE));
201
202 ret = clocksource_register(&clocksource_avr32); 205 ret = clocksource_register(&clocksource_avr32);
203 if (ret) 206 if (ret)
204 printk(KERN_ERR 207 pr_debug("timer: could not register clocksource: %d\n", ret);
205 "timer: could not register clocksource: %d\n", ret);
206 208
207 ret = setup_irq(0, &timer_irqaction); 209 ret = avr32_hpt_start();
208 if (ret) 210 if (ret) {
209 printk("timer: could not request IRQ 0: %d\n", ret); 211 pr_debug("timer: failed starting: %d\n", ret);
212 return;
213 }
210} 214}
211 215
212static struct sysdev_class timer_class = { 216static struct sysdev_class timer_class = {
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}