diff options
Diffstat (limited to 'arch/arm/mach-omap2/timer-gp.c')
-rw-r--r-- | arch/arm/mach-omap2/timer-gp.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c new file mode 100644 index 000000000000..9ec11443200f --- /dev/null +++ b/arch/arm/mach-omap2/timer-gp.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap2/timer-gp.c | ||
3 | * | ||
4 | * OMAP2 GP timer support. | ||
5 | * | ||
6 | * Copyright (C) 2005 Nokia Corporation | ||
7 | * Author: Paul Mundt <paul.mundt@nokia.com> | ||
8 | * Juha Yrjölä <juha.yrjola@nokia.com> | ||
9 | * | ||
10 | * Some parts based off of TI's 24xx code: | ||
11 | * | ||
12 | * Copyright (C) 2004 Texas Instruments, Inc. | ||
13 | * | ||
14 | * Roughly modelled after the OMAP1 MPU timer code. | ||
15 | * | ||
16 | * This file is subject to the terms and conditions of the GNU General Public | ||
17 | * License. See the file "COPYING" in the main directory of this archive | ||
18 | * for more details. | ||
19 | */ | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/time.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <asm/mach/time.h> | ||
25 | #include <asm/delay.h> | ||
26 | #include <asm/io.h> | ||
27 | #include <asm/hardware/clock.h> | ||
28 | |||
29 | #define OMAP2_GP_TIMER1_BASE 0x48028000 | ||
30 | #define OMAP2_GP_TIMER2_BASE 0x4802a000 | ||
31 | #define OMAP2_GP_TIMER3_BASE 0x48078000 | ||
32 | #define OMAP2_GP_TIMER4_BASE 0x4807a000 | ||
33 | |||
34 | #define GP_TIMER_TIDR 0x00 | ||
35 | #define GP_TIMER_TISR 0x18 | ||
36 | #define GP_TIMER_TIER 0x1c | ||
37 | #define GP_TIMER_TCLR 0x24 | ||
38 | #define GP_TIMER_TCRR 0x28 | ||
39 | #define GP_TIMER_TLDR 0x2c | ||
40 | #define GP_TIMER_TSICR 0x40 | ||
41 | |||
42 | #define OS_TIMER_NR 1 /* GP timer 2 */ | ||
43 | |||
44 | static unsigned long timer_base[] = { | ||
45 | IO_ADDRESS(OMAP2_GP_TIMER1_BASE), | ||
46 | IO_ADDRESS(OMAP2_GP_TIMER2_BASE), | ||
47 | IO_ADDRESS(OMAP2_GP_TIMER3_BASE), | ||
48 | IO_ADDRESS(OMAP2_GP_TIMER4_BASE), | ||
49 | }; | ||
50 | |||
51 | static inline unsigned int timer_read_reg(int nr, unsigned int reg) | ||
52 | { | ||
53 | return __raw_readl(timer_base[nr] + reg); | ||
54 | } | ||
55 | |||
56 | static inline void timer_write_reg(int nr, unsigned int reg, unsigned int val) | ||
57 | { | ||
58 | __raw_writel(val, timer_base[nr] + reg); | ||
59 | } | ||
60 | |||
61 | /* Note that we always enable the clock prescale divider bit */ | ||
62 | static inline void omap2_gp_timer_start(int nr, unsigned long load_val) | ||
63 | { | ||
64 | unsigned int tmp; | ||
65 | |||
66 | tmp = 0xffffffff - load_val; | ||
67 | |||
68 | timer_write_reg(nr, GP_TIMER_TLDR, tmp); | ||
69 | timer_write_reg(nr, GP_TIMER_TCRR, tmp); | ||
70 | timer_write_reg(nr, GP_TIMER_TIER, 1 << 1); | ||
71 | timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1); | ||
72 | } | ||
73 | |||
74 | static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id, | ||
75 | struct pt_regs *regs) | ||
76 | { | ||
77 | write_seqlock(&xtime_lock); | ||
78 | |||
79 | timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1); | ||
80 | timer_tick(regs); | ||
81 | |||
82 | write_sequnlock(&xtime_lock); | ||
83 | |||
84 | return IRQ_HANDLED; | ||
85 | } | ||
86 | |||
87 | static struct irqaction omap2_gp_timer_irq = { | ||
88 | .name = "gp timer", | ||
89 | .flags = SA_INTERRUPT, | ||
90 | .handler = omap2_gp_timer_interrupt, | ||
91 | }; | ||
92 | |||
93 | static void __init omap2_gp_timer_init(void) | ||
94 | { | ||
95 | struct clk * sys_ck; | ||
96 | u32 tick_period = 120000; | ||
97 | u32 l; | ||
98 | |||
99 | /* Reset clock and prescale value */ | ||
100 | timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0); | ||
101 | |||
102 | sys_ck = clk_get(NULL, "sys_ck"); | ||
103 | if (IS_ERR(sys_ck)) | ||
104 | printk(KERN_ERR "Could not get sys_ck\n"); | ||
105 | else { | ||
106 | clk_use(sys_ck); | ||
107 | tick_period = clk_get_rate(sys_ck) / 100; | ||
108 | clk_put(sys_ck); | ||
109 | } | ||
110 | |||
111 | tick_period /= 2; /* Minimum prescale divider is 2 */ | ||
112 | tick_period -= 1; | ||
113 | |||
114 | l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR); | ||
115 | printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n", | ||
116 | (l >> 4) & 0x0f, l & 0x0f); | ||
117 | |||
118 | setup_irq(38, &omap2_gp_timer_irq); | ||
119 | |||
120 | omap2_gp_timer_start(OS_TIMER_NR, tick_period); | ||
121 | } | ||
122 | |||
123 | struct sys_timer omap_timer = { | ||
124 | .init = omap2_gp_timer_init, | ||
125 | }; | ||
126 | |||