diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2007-10-11 18:46:10 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-10-11 18:46:10 -0400 |
commit | 9b9ea2202f3396790f635c62f7498ad75f08f62c (patch) | |
tree | 60f5dd7cdf7f8dba47a89aabbd48d91fdc067395 /arch/mips/sibyte/sb1250/time.c | |
parent | d865bea4dace1d42995a6cf552bc4863842623f4 (diff) |
[MIPS] Clockevent driver for BCM1250
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/sibyte/sb1250/time.c')
-rw-r--r-- | arch/mips/sibyte/sb1250/time.c | 181 |
1 files changed, 160 insertions, 21 deletions
diff --git a/arch/mips/sibyte/sb1250/time.c b/arch/mips/sibyte/sb1250/time.c index eb177075e9c..38199ad8fc5 100644 --- a/arch/mips/sibyte/sb1250/time.c +++ b/arch/mips/sibyte/sb1250/time.c | |||
@@ -25,6 +25,7 @@ | |||
25 | * code to do general bookkeeping (e.g. update jiffies, run | 25 | * code to do general bookkeeping (e.g. update jiffies, run |
26 | * bottom halves, etc.) | 26 | * bottom halves, etc.) |
27 | */ | 27 | */ |
28 | #include <linux/clockchips.h> | ||
28 | #include <linux/interrupt.h> | 29 | #include <linux/interrupt.h> |
29 | #include <linux/sched.h> | 30 | #include <linux/sched.h> |
30 | #include <linux/spinlock.h> | 31 | #include <linux/spinlock.h> |
@@ -71,16 +72,158 @@ void __init sb1250_hpt_setup(void) | |||
71 | } | 72 | } |
72 | } | 73 | } |
73 | 74 | ||
75 | /* | ||
76 | * The general purpose timer ticks at 1 Mhz independent if | ||
77 | * the rest of the system | ||
78 | */ | ||
79 | static void sibyte_set_mode(enum clock_event_mode mode, | ||
80 | struct clock_event_device *evt) | ||
81 | { | ||
82 | unsigned int cpu = smp_processor_id(); | ||
83 | void __iomem *timer_cfg, *timer_init; | ||
84 | |||
85 | timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); | ||
86 | timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); | ||
87 | |||
88 | switch(mode) { | ||
89 | case CLOCK_EVT_MODE_PERIODIC: | ||
90 | __raw_writeq(0, timer_cfg); | ||
91 | __raw_writeq((V_SCD_TIMER_FREQ / HZ) - 1, timer_init); | ||
92 | __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, | ||
93 | timer_cfg); | ||
94 | break; | ||
95 | |||
96 | case CLOCK_EVT_MODE_ONESHOT: | ||
97 | /* Stop the timer until we actually program a shot */ | ||
98 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
99 | __raw_writeq(0, timer_cfg); | ||
100 | break; | ||
74 | 101 | ||
75 | void sb1250_time_init(void) | 102 | case CLOCK_EVT_MODE_UNUSED: /* shuddup gcc */ |
103 | ; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | static int | ||
108 | sibyte_next_event(unsigned long delta, struct clock_event_device *evt) | ||
76 | { | 109 | { |
77 | int cpu = smp_processor_id(); | 110 | unsigned int cpu = smp_processor_id(); |
78 | int irq = K_INT_TIMER_0+cpu; | 111 | void __iomem *timer_cfg, *timer_init; |
79 | 112 | ||
80 | /* Only have 4 general purpose timers, and we use last one as hpt */ | 113 | timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); |
81 | if (cpu > 2) { | 114 | timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); |
82 | BUG(); | 115 | |
116 | __raw_writeq(0, timer_cfg); | ||
117 | __raw_writeq(delta, timer_init); | ||
118 | __raw_writeq(M_SCD_TIMER_ENABLE, timer_cfg); | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | struct clock_event_device sibyte_hpt_clockevent = { | ||
124 | .name = "sb1250-counter", | ||
125 | .features = CLOCK_EVT_FEAT_PERIODIC, | ||
126 | .set_mode = sibyte_set_mode, | ||
127 | .set_next_event = sibyte_next_event, | ||
128 | .shift = 32, | ||
129 | .irq = 0, | ||
130 | }; | ||
131 | |||
132 | static irqreturn_t sibyte_counter_handler(int irq, void *dev_id) | ||
133 | { | ||
134 | struct clock_event_device *cd = &sibyte_hpt_clockevent; | ||
135 | |||
136 | cd->event_handler(cd); | ||
137 | |||
138 | return IRQ_HANDLED; | ||
139 | } | ||
140 | |||
141 | static struct irqaction sibyte_irqaction = { | ||
142 | .handler = sibyte_counter_handler, | ||
143 | .flags = IRQF_DISABLED | IRQF_PERCPU, | ||
144 | .name = "timer", | ||
145 | }; | ||
146 | |||
147 | /* | ||
148 | * The general purpose timer ticks at 1 Mhz independent if | ||
149 | * the rest of the system | ||
150 | */ | ||
151 | static void sibyte_set_mode(enum clock_event_mode mode, | ||
152 | struct clock_event_device *evt) | ||
153 | { | ||
154 | unsigned int cpu = smp_processor_id(); | ||
155 | void __iomem *timer_cfg, *timer_init; | ||
156 | |||
157 | timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); | ||
158 | timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); | ||
159 | |||
160 | switch (mode) { | ||
161 | case CLOCK_EVT_MODE_PERIODIC: | ||
162 | __raw_writeq(0, timer_cfg); | ||
163 | __raw_writeq((V_SCD_TIMER_FREQ / HZ) - 1, timer_init); | ||
164 | __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, | ||
165 | timer_cfg); | ||
166 | break; | ||
167 | |||
168 | case CLOCK_EVT_MODE_ONESHOT: | ||
169 | /* Stop the timer until we actually program a shot */ | ||
170 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
171 | __raw_writeq(0, timer_cfg); | ||
172 | break; | ||
173 | |||
174 | case CLOCK_EVT_MODE_UNUSED: /* shuddup gcc */ | ||
175 | ; | ||
83 | } | 176 | } |
177 | } | ||
178 | |||
179 | static int | ||
180 | sibyte_next_event(unsigned long delta, struct clock_event_device *evt) | ||
181 | { | ||
182 | unsigned int cpu = smp_processor_id(); | ||
183 | void __iomem *timer_cfg, *timer_init; | ||
184 | |||
185 | timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); | ||
186 | timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); | ||
187 | |||
188 | __raw_writeq(0, timer_cfg); | ||
189 | __raw_writeq(delta, timer_init); | ||
190 | __raw_writeq(M_SCD_TIMER_ENABLE, timer_cfg); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | struct clock_event_device sibyte_hpt_clockevent = { | ||
196 | .name = "sb1250-counter", | ||
197 | .features = CLOCK_EVT_FEAT_PERIODIC, | ||
198 | .set_mode = sibyte_set_mode, | ||
199 | .set_next_event = sibyte_next_event, | ||
200 | .shift = 32, | ||
201 | .irq = 0, | ||
202 | }; | ||
203 | |||
204 | static irqreturn_t sibyte_counter_handler(int irq, void *dev_id) | ||
205 | { | ||
206 | struct clock_event_device *cd = &sibyte_hpt_clockevent; | ||
207 | |||
208 | cd->event_handler(cd); | ||
209 | |||
210 | return IRQ_HANDLED; | ||
211 | } | ||
212 | |||
213 | static struct irqaction sibyte_irqaction = { | ||
214 | .handler = sibyte_counter_handler, | ||
215 | .flags = IRQF_DISABLED | IRQF_PERCPU, | ||
216 | .name = "timer", | ||
217 | }; | ||
218 | |||
219 | static void __init sb1250_clockevent_init(void) | ||
220 | { | ||
221 | struct clock_event_device *cd = &sibyte_hpt_clockevent; | ||
222 | unsigned int cpu = smp_processor_id(); | ||
223 | int irq = K_INT_TIMER_0 + cpu; | ||
224 | |||
225 | /* Only have 4 general purpose timers, and we use last one as hpt */ | ||
226 | BUG_ON(cpu > 2); | ||
84 | 227 | ||
85 | sb1250_mask_irq(cpu, irq); | 228 | sb1250_mask_irq(cpu, irq); |
86 | 229 | ||
@@ -88,24 +231,11 @@ void sb1250_time_init(void) | |||
88 | __raw_writeq(IMR_IP4_VAL, | 231 | __raw_writeq(IMR_IP4_VAL, |
89 | IOADDR(A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MAP_BASE) + | 232 | IOADDR(A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MAP_BASE) + |
90 | (irq << 3))); | 233 | (irq << 3))); |
91 | 234 | cd->cpumask = cpumask_of_cpu(0); | |
92 | /* the general purpose timer ticks at 1 Mhz independent if the rest of the system */ | ||
93 | /* Disable the timer and set up the count */ | ||
94 | __raw_writeq(0, IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG))); | ||
95 | #ifdef CONFIG_SIMULATION | ||
96 | __raw_writeq((50000 / HZ) - 1, | ||
97 | IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT))); | ||
98 | #else | ||
99 | __raw_writeq((V_SCD_TIMER_FREQ / HZ) - 1, | ||
100 | IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT))); | ||
101 | #endif | ||
102 | |||
103 | /* Set the timer running */ | ||
104 | __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, | ||
105 | IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG))); | ||
106 | 235 | ||
107 | sb1250_unmask_irq(cpu, irq); | 236 | sb1250_unmask_irq(cpu, irq); |
108 | sb1250_steal_irq(irq); | 237 | sb1250_steal_irq(irq); |
238 | |||
109 | /* | 239 | /* |
110 | * This interrupt is "special" in that it doesn't use the request_irq | 240 | * This interrupt is "special" in that it doesn't use the request_irq |
111 | * way to hook the irq line. The timer interrupt is initialized early | 241 | * way to hook the irq line. The timer interrupt is initialized early |
@@ -114,6 +244,15 @@ void sb1250_time_init(void) | |||
114 | * called directly from irq_handler.S when IP[4] is set during an | 244 | * called directly from irq_handler.S when IP[4] is set during an |
115 | * interrupt | 245 | * interrupt |
116 | */ | 246 | */ |
247 | setup_irq(irq, &sibyte_irqaction); | ||
248 | |||
249 | clockevents_register_device(cd); | ||
250 | } | ||
251 | |||
252 | void __init plat_time_init(void) | ||
253 | { | ||
254 | sb1250_clocksource_init(); | ||
255 | sb1250_clockevent_init(); | ||
117 | } | 256 | } |
118 | 257 | ||
119 | /* | 258 | /* |