diff options
Diffstat (limited to 'arch/arm/mach-at91/at91sam926x_time.c')
-rw-r--r-- | arch/arm/mach-at91/at91sam926x_time.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c new file mode 100644 index 000000000000..59cbbe1e8161 --- /dev/null +++ b/arch/arm/mach-at91/at91sam926x_time.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-at91/at91sam926x_time.c | ||
3 | * | ||
4 | * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France | ||
5 | * Revision 2005 M. Nicolas Diremdjian, ATMEL Rousset, France | ||
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/init.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/irq.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/time.h> | ||
18 | |||
19 | #include <asm/hardware.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/mach/time.h> | ||
22 | |||
23 | #include <asm/arch/at91_pit.h> | ||
24 | |||
25 | |||
26 | #define PIT_CPIV(x) ((x) & AT91_PIT_CPIV) | ||
27 | #define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20) | ||
28 | |||
29 | /* | ||
30 | * Returns number of microseconds since last timer interrupt. Note that interrupts | ||
31 | * will have been disabled by do_gettimeofday() | ||
32 | * 'LATCH' is hwclock ticks (see CLOCK_TICK_RATE in timex.h) per jiffy. | ||
33 | * 'tick' is usecs per jiffy (linux/timex.h). | ||
34 | */ | ||
35 | static unsigned long at91sam926x_gettimeoffset(void) | ||
36 | { | ||
37 | unsigned long elapsed; | ||
38 | unsigned long t = at91_sys_read(AT91_PIT_PIIR); | ||
39 | |||
40 | elapsed = (PIT_PICNT(t) * LATCH) + PIT_CPIV(t); /* hardware clock cycles */ | ||
41 | |||
42 | return (unsigned long)(elapsed * 1000000) / LATCH; | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * IRQ handler for the timer. | ||
47 | */ | ||
48 | static irqreturn_t at91sam926x_timer_interrupt(int irq, void *dev_id) | ||
49 | { | ||
50 | volatile long nr_ticks; | ||
51 | |||
52 | if (at91_sys_read(AT91_PIT_SR) & AT91_PIT_PITS) { /* This is a shared interrupt */ | ||
53 | write_seqlock(&xtime_lock); | ||
54 | |||
55 | /* Get number to ticks performed before interrupt and clear PIT interrupt */ | ||
56 | nr_ticks = PIT_PICNT(at91_sys_read(AT91_PIT_PIVR)); | ||
57 | do { | ||
58 | timer_tick(); | ||
59 | nr_ticks--; | ||
60 | } while (nr_ticks); | ||
61 | |||
62 | write_sequnlock(&xtime_lock); | ||
63 | return IRQ_HANDLED; | ||
64 | } else | ||
65 | return IRQ_NONE; /* not handled */ | ||
66 | } | ||
67 | |||
68 | static struct irqaction at91sam926x_timer_irq = { | ||
69 | .name = "at91_tick", | ||
70 | .flags = IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER, | ||
71 | .handler = at91sam926x_timer_interrupt | ||
72 | }; | ||
73 | |||
74 | void at91sam926x_timer_reset(void) | ||
75 | { | ||
76 | /* Disable timer */ | ||
77 | at91_sys_write(AT91_PIT_MR, 0); | ||
78 | |||
79 | /* Clear any pending interrupts */ | ||
80 | (void) at91_sys_read(AT91_PIT_PIVR); | ||
81 | |||
82 | /* Set Period Interval timer and enable its interrupt */ | ||
83 | at91_sys_write(AT91_PIT_MR, (LATCH & AT91_PIT_PIV) | AT91_PIT_PITIEN | AT91_PIT_PITEN); | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * Set up timer interrupt. | ||
88 | */ | ||
89 | void __init at91sam926x_timer_init(void) | ||
90 | { | ||
91 | /* Initialize and enable the timer */ | ||
92 | at91sam926x_timer_reset(); | ||
93 | |||
94 | /* Make IRQs happen for the system timer. */ | ||
95 | setup_irq(AT91_ID_SYS, &at91sam926x_timer_irq); | ||
96 | } | ||
97 | |||
98 | #ifdef CONFIG_PM | ||
99 | static void at91sam926x_timer_suspend(void) | ||
100 | { | ||
101 | /* Disable timer */ | ||
102 | at91_sys_write(AT91_PIT_MR, 0); | ||
103 | } | ||
104 | #else | ||
105 | #define at91sam926x_timer_suspend NULL | ||
106 | #endif | ||
107 | |||
108 | struct sys_timer at91sam926x_timer = { | ||
109 | .init = at91sam926x_timer_init, | ||
110 | .offset = at91sam926x_gettimeoffset, | ||
111 | .suspend = at91sam926x_timer_suspend, | ||
112 | .resume = at91sam926x_timer_reset, | ||
113 | }; | ||
114 | |||