Merge with /home/shaggy/git/linus-clean/
[linux-2.6] / arch / arm / mach-integrator / core.c
1 /*
2  *  linux/arch/arm/mach-integrator/core.c
3  *
4  *  Copyright (C) 2000-2003 Deep Blue Solutions Ltd
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2, as
8  * published by the Free Software Foundation.
9  */
10 #include <linux/types.h>
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/device.h>
14 #include <linux/spinlock.h>
15 #include <linux/interrupt.h>
16 #include <linux/sched.h>
17 #include <linux/smp.h>
18 #include <linux/amba/bus.h>
19
20 #include <asm/hardware.h>
21 #include <asm/irq.h>
22 #include <asm/io.h>
23 #include <asm/hardware/arm_timer.h>
24 #include <asm/arch/cm.h>
25 #include <asm/system.h>
26 #include <asm/leds.h>
27 #include <asm/mach/time.h>
28
29 #include "common.h"
30
31 static struct amba_device rtc_device = {
32         .dev            = {
33                 .bus_id = "mb:15",
34         },
35         .res            = {
36                 .start  = INTEGRATOR_RTC_BASE,
37                 .end    = INTEGRATOR_RTC_BASE + SZ_4K - 1,
38                 .flags  = IORESOURCE_MEM,
39         },
40         .irq            = { IRQ_RTCINT, NO_IRQ },
41         .periphid       = 0x00041030,
42 };
43
44 static struct amba_device uart0_device = {
45         .dev            = {
46                 .bus_id = "mb:16",
47         },
48         .res            = {
49                 .start  = INTEGRATOR_UART0_BASE,
50                 .end    = INTEGRATOR_UART0_BASE + SZ_4K - 1,
51                 .flags  = IORESOURCE_MEM,
52         },
53         .irq            = { IRQ_UARTINT0, NO_IRQ },
54         .periphid       = 0x0041010,
55 };
56
57 static struct amba_device uart1_device = {
58         .dev            = {
59                 .bus_id = "mb:17",
60         },
61         .res            = {
62                 .start  = INTEGRATOR_UART1_BASE,
63                 .end    = INTEGRATOR_UART1_BASE + SZ_4K - 1,
64                 .flags  = IORESOURCE_MEM,
65         },
66         .irq            = { IRQ_UARTINT1, NO_IRQ },
67         .periphid       = 0x0041010,
68 };
69
70 static struct amba_device kmi0_device = {
71         .dev            = {
72                 .bus_id = "mb:18",
73         },
74         .res            = {
75                 .start  = KMI0_BASE,
76                 .end    = KMI0_BASE + SZ_4K - 1,
77                 .flags  = IORESOURCE_MEM,
78         },
79         .irq            = { IRQ_KMIINT0, NO_IRQ },
80         .periphid       = 0x00041050,
81 };
82
83 static struct amba_device kmi1_device = {
84         .dev            = {
85                 .bus_id = "mb:19",
86         },
87         .res            = {
88                 .start  = KMI1_BASE,
89                 .end    = KMI1_BASE + SZ_4K - 1,
90                 .flags  = IORESOURCE_MEM,
91         },
92         .irq            = { IRQ_KMIINT1, NO_IRQ },
93         .periphid       = 0x00041050,
94 };
95
96 static struct amba_device *amba_devs[] __initdata = {
97         &rtc_device,
98         &uart0_device,
99         &uart1_device,
100         &kmi0_device,
101         &kmi1_device,
102 };
103
104 static int __init integrator_init(void)
105 {
106         int i;
107
108         for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
109                 struct amba_device *d = amba_devs[i];
110                 amba_device_register(d, &iomem_resource);
111         }
112
113         return 0;
114 }
115
116 arch_initcall(integrator_init);
117
118 #define CM_CTRL IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_CTRL_OFFSET
119
120 static DEFINE_SPINLOCK(cm_lock);
121
122 /**
123  * cm_control - update the CM_CTRL register.
124  * @mask: bits to change
125  * @set: bits to set
126  */
127 void cm_control(u32 mask, u32 set)
128 {
129         unsigned long flags;
130         u32 val;
131
132         spin_lock_irqsave(&cm_lock, flags);
133         val = readl(CM_CTRL) & ~mask;
134         writel(val | set, CM_CTRL);
135         spin_unlock_irqrestore(&cm_lock, flags);
136 }
137
138 EXPORT_SYMBOL(cm_control);
139
140 /*
141  * Where is the timer (VA)?
142  */
143 #define TIMER0_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000000)
144 #define TIMER1_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000100)
145 #define TIMER2_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000200)
146 #define VA_IC_BASE     IO_ADDRESS(INTEGRATOR_IC_BASE) 
147
148 /*
149  * How long is the timer interval?
150  */
151 #define TIMER_INTERVAL  (TICKS_PER_uSEC * mSEC_10)
152 #if TIMER_INTERVAL >= 0x100000
153 #define TICKS2USECS(x)  (256 * (x) / TICKS_PER_uSEC)
154 #elif TIMER_INTERVAL >= 0x10000
155 #define TICKS2USECS(x)  (16 * (x) / TICKS_PER_uSEC)
156 #else
157 #define TICKS2USECS(x)  ((x) / TICKS_PER_uSEC)
158 #endif
159
160 static unsigned long timer_reload;
161
162 /*
163  * Returns number of ms since last clock interrupt.  Note that interrupts
164  * will have been disabled by do_gettimeoffset()
165  */
166 unsigned long integrator_gettimeoffset(void)
167 {
168         unsigned long ticks1, ticks2, status;
169
170         /*
171          * Get the current number of ticks.  Note that there is a race
172          * condition between us reading the timer and checking for
173          * an interrupt.  We get around this by ensuring that the
174          * counter has not reloaded between our two reads.
175          */
176         ticks2 = readl(TIMER1_VA_BASE + TIMER_VALUE) & 0xffff;
177         do {
178                 ticks1 = ticks2;
179                 status = __raw_readl(VA_IC_BASE + IRQ_RAW_STATUS);
180                 ticks2 = readl(TIMER1_VA_BASE + TIMER_VALUE) & 0xffff;
181         } while (ticks2 > ticks1);
182
183         /*
184          * Number of ticks since last interrupt.
185          */
186         ticks1 = timer_reload - ticks2;
187
188         /*
189          * Interrupt pending?  If so, we've reloaded once already.
190          */
191         if (status & (1 << IRQ_TIMERINT1))
192                 ticks1 += timer_reload;
193
194         /*
195          * Convert the ticks to usecs
196          */
197         return TICKS2USECS(ticks1);
198 }
199
200 /*
201  * IRQ handler for the timer
202  */
203 static irqreturn_t
204 integrator_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
205 {
206         write_seqlock(&xtime_lock);
207
208         /*
209          * clear the interrupt
210          */
211         writel(1, TIMER1_VA_BASE + TIMER_INTCLR);
212
213         /*
214          * the clock tick routines are only processed on the
215          * primary CPU
216          */
217         if (hard_smp_processor_id() == 0) {
218                 timer_tick(regs);
219 #ifdef CONFIG_SMP
220                 smp_send_timer();
221 #endif
222         }
223
224 #ifdef CONFIG_SMP
225         /*
226          * this is the ARM equivalent of the APIC timer interrupt
227          */
228         update_process_times(user_mode(regs));
229 #endif /* CONFIG_SMP */
230
231         write_sequnlock(&xtime_lock);
232
233         return IRQ_HANDLED;
234 }
235
236 static struct irqaction integrator_timer_irq = {
237         .name           = "Integrator Timer Tick",
238         .flags          = SA_INTERRUPT | SA_TIMER,
239         .handler        = integrator_timer_interrupt,
240 };
241
242 /*
243  * Set up timer interrupt, and return the current time in seconds.
244  */
245 void __init integrator_time_init(unsigned long reload, unsigned int ctrl)
246 {
247         unsigned int timer_ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC;
248
249         timer_reload = reload;
250         timer_ctrl |= ctrl;
251
252         if (timer_reload > 0x100000) {
253                 timer_reload >>= 8;
254                 timer_ctrl |= TIMER_CTRL_DIV256;
255         } else if (timer_reload > 0x010000) {
256                 timer_reload >>= 4;
257                 timer_ctrl |= TIMER_CTRL_DIV16;
258         }
259
260         /*
261          * Initialise to a known state (all timers off)
262          */
263         writel(0, TIMER0_VA_BASE + TIMER_CTRL);
264         writel(0, TIMER1_VA_BASE + TIMER_CTRL);
265         writel(0, TIMER2_VA_BASE + TIMER_CTRL);
266
267         writel(timer_reload, TIMER1_VA_BASE + TIMER_LOAD);
268         writel(timer_reload, TIMER1_VA_BASE + TIMER_VALUE);
269         writel(timer_ctrl, TIMER1_VA_BASE + TIMER_CTRL);
270
271         /*
272          * Make irqs happen for the system timer
273          */
274         setup_irq(IRQ_TIMERINT1, &integrator_timer_irq);
275 }