aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/sun4m_irq.c
diff options
context:
space:
mode:
authorSam Ravnborg <sam@ravnborg.org>2011-04-18 07:25:44 -0400
committerDavid S. Miller <davem@davemloft.net>2011-04-20 01:11:40 -0400
commit6baa9b20a68a88c2fd751cbe8d7652009379351b (patch)
tree999283be451c020ff35110c336292e9d698d2464 /arch/sparc/kernel/sun4m_irq.c
parent06010fb588700e7fcb29e720c56884e3de5fd327 (diff)
sparc32: genirq support
The conversion of sparc32 to genirq is based on original work done by David S. Miller. Daniel Hellstrom has helped in the conversion and implemented the shutdowm functionality. Marcel van Nies <morcles@gmail.com> has tested this on Sparc Station 20 Test status: sun4c - not tested sun4m,pci - not tested sun4m,sbus - tested (Sparc Classic, Sparc Station 5, Sparc Station 20) sun4d - not tested leon - tested on various combinations of leon boards, including SMP variants generic Introduce use of GENERIC_HARDIRQS and GENERIC_IRQ_SHOW Allocate 64 IRQs - which is enough even for SS2000 Use a table of irq_bucket to maintain uses IRQs irq_bucket is also used to chain several irq's that must be called when the same intrrupt is asserted Use irq_link to link a interrupt source to the irq All plafforms must now supply their own build_device_irq method handler_irq rewriten to use generic irq support floppy Read FLOPPY_IRQ from platform device Use generic request_irq to register the floppy interrupt Rewrote sparc_floppy_irq to use the generic irq support pcic: Introduce irq_chip Store mask in chip_data for use in mask/unmask functions Add build_device_irq for pcic Use pcic_build_device_irq in pci_time_init allocate virtual irqs in pcic_fill_irq sun4c: Introduce irq_chip Store mask in chip_data for use in mask/unmask functions Add build_device_irq for sun4c Use sun4c_build_device_irq in sun4c_init_timers sun4m: Introduce irq_chip Introduce dedicated mask/unmask methods Introduce sun4m_handler_data that allow easy access to necessary data in the mask/unmask functions Add a helper method to enable profile_timer (used from smp) Added sun4m_build_device_irq Use sun4m_build_device_irq in sun4m_init_timers TODO: There is no replacement for smp_rotate that always scheduled next CPU as interrupt target upon an interrupt sun4d: Introduce irq_chip Introduce dedicated mask/unmask methods Introduce sun4d_handler_data that allow easy access to necessary data in mask/unmask fuctions Rewrote sun4d_handler_irq to use generic irq support TODO: The original implmentation of enable/disable had: if (irq < NR_IRQS) return; The new implmentation does not distingush between SBUS and cpu interrupts. I am no sure what is right here. I assume we need to do something for the cpu interrupts. I have not succeeded booting my sun4d box (with or without this patch) and my understanding of this platfrom is limited. So I would be a bit suprised if this works. leon: Introduce irq_chip Store mask in chip_data for use in mask/unmask functions Add build_device_irq for leon Use leon_build_device_irq in leon_init_timers Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Acked-by: Daniel Hellstrom <daniel@gaisler.com> Tested-by: Daniel Hellstrom <daniel@gaisler.com> Tested-by: Marcel van Nies <morcles@gmail.com> Cc: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/kernel/sun4m_irq.c')
-rw-r--r--arch/sparc/kernel/sun4m_irq.c179
1 files changed, 98 insertions, 81 deletions
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c
index 69df6257a32e..422c16dad1f6 100644
--- a/arch/sparc/kernel/sun4m_irq.c
+++ b/arch/sparc/kernel/sun4m_irq.c
@@ -100,6 +100,11 @@
100struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; 100struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS];
101struct sun4m_irq_global __iomem *sun4m_irq_global; 101struct sun4m_irq_global __iomem *sun4m_irq_global;
102 102
103struct sun4m_handler_data {
104 bool percpu;
105 long mask;
106};
107
103/* Dave Redman (djhr@tadpole.co.uk) 108/* Dave Redman (djhr@tadpole.co.uk)
104 * The sun4m interrupt registers. 109 * The sun4m interrupt registers.
105 */ 110 */
@@ -142,9 +147,9 @@ struct sun4m_irq_global __iomem *sun4m_irq_global;
142#define OBP_INT_LEVEL_VME 0x40 147#define OBP_INT_LEVEL_VME 0x40
143 148
144#define SUN4M_TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10) 149#define SUN4M_TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10)
145#define SUM4M_PROFILE_IRQ (OBP_INT_LEVEL_ONBOARD | 14) 150#define SUN4M_PROFILE_IRQ (OBP_INT_LEVEL_ONBOARD | 14)
146 151
147static unsigned long irq_mask[0x50] = { 152static unsigned long sun4m_imask[0x50] = {
148 /* 0x00 - SMP */ 153 /* 0x00 - SMP */
149 0, SUN4M_SOFT_INT(1), 154 0, SUN4M_SOFT_INT(1),
150 SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), 155 SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3),
@@ -169,7 +174,7 @@ static unsigned long irq_mask[0x50] = {
169 SUN4M_INT_VIDEO, SUN4M_INT_MODULE, 174 SUN4M_INT_VIDEO, SUN4M_INT_MODULE,
170 SUN4M_INT_REALTIME, SUN4M_INT_FLOPPY, 175 SUN4M_INT_REALTIME, SUN4M_INT_FLOPPY,
171 (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), 176 (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS),
172 SUN4M_INT_AUDIO, 0, SUN4M_INT_MODULE_ERR, 177 SUN4M_INT_AUDIO, SUN4M_INT_E14, SUN4M_INT_MODULE_ERR,
173 /* 0x30 - sbus */ 178 /* 0x30 - sbus */
174 0, 0, SUN4M_INT_SBUS(0), SUN4M_INT_SBUS(1), 179 0, 0, SUN4M_INT_SBUS(0), SUN4M_INT_SBUS(1),
175 0, SUN4M_INT_SBUS(2), 0, SUN4M_INT_SBUS(3), 180 0, SUN4M_INT_SBUS(2), 0, SUN4M_INT_SBUS(3),
@@ -182,105 +187,110 @@ static unsigned long irq_mask[0x50] = {
182 0, SUN4M_INT_VME(6), 0, 0 187 0, SUN4M_INT_VME(6), 0, 0
183}; 188};
184 189
185static unsigned long sun4m_get_irqmask(unsigned int irq) 190static void sun4m_mask_irq(struct irq_data *data)
186{ 191{
187 unsigned long mask; 192 struct sun4m_handler_data *handler_data = data->handler_data;
188 193 int cpu = smp_processor_id();
189 if (irq < 0x50)
190 mask = irq_mask[irq];
191 else
192 mask = 0;
193 194
194 if (!mask) 195 if (handler_data->mask) {
195 printk(KERN_ERR "sun4m_get_irqmask: IRQ%d has no valid mask!\n", 196 unsigned long flags;
196 irq);
197 197
198 return mask; 198 local_irq_save(flags);
199 if (handler_data->percpu) {
200 sbus_writel(handler_data->mask, &sun4m_irq_percpu[cpu]->set);
201 } else {
202 sbus_writel(handler_data->mask, &sun4m_irq_global->mask_set);
203 }
204 local_irq_restore(flags);
205 }
199} 206}
200 207
201static void sun4m_disable_irq(unsigned int irq_nr) 208static void sun4m_unmask_irq(struct irq_data *data)
202{ 209{
203 unsigned long mask, flags; 210 struct sun4m_handler_data *handler_data = data->handler_data;
204 int cpu = smp_processor_id(); 211 int cpu = smp_processor_id();
205 212
206 mask = sun4m_get_irqmask(irq_nr); 213 if (handler_data->mask) {
207 local_irq_save(flags); 214 unsigned long flags;
208 if (irq_nr > 15)
209 sbus_writel(mask, &sun4m_irq_global->mask_set);
210 else
211 sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
212 local_irq_restore(flags);
213}
214
215static void sun4m_enable_irq(unsigned int irq_nr)
216{
217 unsigned long mask, flags;
218 int cpu = smp_processor_id();
219 215
220 /* Dreadful floppy hack. When we use 0x2b instead of
221 * 0x0b the system blows (it starts to whistle!).
222 * So we continue to use 0x0b. Fixme ASAP. --P3
223 */
224 if (irq_nr != 0x0b) {
225 mask = sun4m_get_irqmask(irq_nr);
226 local_irq_save(flags);
227 if (irq_nr > 15)
228 sbus_writel(mask, &sun4m_irq_global->mask_clear);
229 else
230 sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
231 local_irq_restore(flags);
232 } else {
233 local_irq_save(flags); 216 local_irq_save(flags);
234 sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear); 217 if (handler_data->percpu) {
218 sbus_writel(handler_data->mask, &sun4m_irq_percpu[cpu]->clear);
219 } else {
220 sbus_writel(handler_data->mask, &sun4m_irq_global->mask_clear);
221 }
235 local_irq_restore(flags); 222 local_irq_restore(flags);
236 } 223 }
237} 224}
238 225
239static unsigned long cpu_pil_to_imask[16] = { 226static unsigned int sun4m_startup_irq(struct irq_data *data)
240/*0*/ 0x00000000, 227{
241/*1*/ 0x00000000, 228 irq_link(data->irq);
242/*2*/ SUN4M_INT_SBUS(0) | SUN4M_INT_VME(0), 229 sun4m_unmask_irq(data);
243/*3*/ SUN4M_INT_SBUS(1) | SUN4M_INT_VME(1), 230 return 0;
244/*4*/ SUN4M_INT_SCSI, 231}
245/*5*/ SUN4M_INT_SBUS(2) | SUN4M_INT_VME(2),
246/*6*/ SUN4M_INT_ETHERNET,
247/*7*/ SUN4M_INT_SBUS(3) | SUN4M_INT_VME(3),
248/*8*/ SUN4M_INT_VIDEO,
249/*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR,
250/*10*/ SUN4M_INT_REALTIME,
251/*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY,
252/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
253/*13*/ SUN4M_INT_SBUS(6) | SUN4M_INT_VME(6) | SUN4M_INT_AUDIO,
254/*14*/ SUN4M_INT_E14,
255/*15*/ SUN4M_INT_ERROR,
256};
257 232
258/* We assume the caller has disabled local interrupts when these are called, 233static void sun4m_shutdown_irq(struct irq_data *data)
259 * or else very bizarre behavior will result.
260 */
261static void sun4m_disable_pil_irq(unsigned int pil)
262{ 234{
263 sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set); 235 sun4m_mask_irq(data);
236 irq_unlink(data->irq);
264} 237}
265 238
266static void sun4m_enable_pil_irq(unsigned int pil) 239static struct irq_chip sun4m_irq = {
240 .name = "sun4m",
241 .irq_startup = sun4m_startup_irq,
242 .irq_shutdown = sun4m_shutdown_irq,
243 .irq_mask = sun4m_mask_irq,
244 .irq_unmask = sun4m_unmask_irq,
245};
246
247
248static unsigned int sun4m_build_device_irq(struct platform_device *op,
249 unsigned int real_irq)
267{ 250{
268 sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear); 251 struct sun4m_handler_data *handler_data;
252 unsigned int irq;
253 unsigned int pil;
254
255 if (real_irq >= OBP_INT_LEVEL_VME) {
256 prom_printf("Bogus sun4m IRQ %u\n", real_irq);
257 prom_halt();
258 }
259 pil = (real_irq & 0xf);
260 irq = irq_alloc(real_irq, pil);
261
262 if (irq == 0)
263 goto out;
264
265 handler_data = irq_get_handler_data(irq);
266 if (unlikely(handler_data))
267 goto out;
268
269 handler_data = kzalloc(sizeof(struct sun4m_handler_data), GFP_ATOMIC);
270 if (unlikely(!handler_data)) {
271 prom_printf("IRQ: kzalloc(sun4m_handler_data) failed.\n");
272 prom_halt();
273 }
274
275 handler_data->mask = sun4m_imask[real_irq];
276 handler_data->percpu = real_irq < OBP_INT_LEVEL_ONBOARD;
277 irq_set_chip_and_handler_name(irq, &sun4m_irq,
278 handle_level_irq, "level");
279 irq_set_handler_data(irq, handler_data);
280
281out:
282 return irq;
269} 283}
270 284
271#ifdef CONFIG_SMP 285#ifdef CONFIG_SMP
272static void sun4m_send_ipi(int cpu, int level) 286static void sun4m_send_ipi(int cpu, int level)
273{ 287{
274 unsigned long mask = sun4m_get_irqmask(level); 288 sbus_writel(SUN4M_SOFT_INT(level), &sun4m_irq_percpu[cpu]->set);
275
276 sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
277} 289}
278 290
279static void sun4m_clear_ipi(int cpu, int level) 291static void sun4m_clear_ipi(int cpu, int level)
280{ 292{
281 unsigned long mask = sun4m_get_irqmask(level); 293 sbus_writel(SUN4M_SOFT_INT(level), &sun4m_irq_percpu[cpu]->clear);
282
283 sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
284} 294}
285 295
286static void sun4m_set_udt(int cpu) 296static void sun4m_set_udt(int cpu)
@@ -343,7 +353,15 @@ void sun4m_nmi(struct pt_regs *regs)
343 prom_halt(); 353 prom_halt();
344} 354}
345 355
346/* Exported for sun4m_smp.c */ 356void sun4m_unmask_profile_irq(void)
357{
358 unsigned long flags;
359
360 local_irq_save(flags);
361 sbus_writel(sun4m_imask[SUN4M_PROFILE_IRQ], &sun4m_irq_global->mask_clear);
362 local_irq_restore(flags);
363}
364
347void sun4m_clear_profile_irq(int cpu) 365void sun4m_clear_profile_irq(int cpu)
348{ 366{
349 sbus_readl(&timers_percpu[cpu]->l14_limit); 367 sbus_readl(&timers_percpu[cpu]->l14_limit);
@@ -358,6 +376,7 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn)
358{ 376{
359 struct device_node *dp = of_find_node_by_name(NULL, "counter"); 377 struct device_node *dp = of_find_node_by_name(NULL, "counter");
360 int i, err, len, num_cpu_timers; 378 int i, err, len, num_cpu_timers;
379 unsigned int irq;
361 const u32 *addr; 380 const u32 *addr;
362 381
363 if (!dp) { 382 if (!dp) {
@@ -384,8 +403,9 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn)
384 403
385 master_l10_counter = &timers_global->l10_count; 404 master_l10_counter = &timers_global->l10_count;
386 405
387 err = request_irq(SUN4M_TIMER_IRQ, counter_fn, 406 irq = sun4m_build_device_irq(NULL, SUN4M_TIMER_IRQ);
388 (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); 407
408 err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL);
389 if (err) { 409 if (err) {
390 printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", 410 printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n",
391 err); 411 err);
@@ -452,14 +472,11 @@ void __init sun4m_init_IRQ(void)
452 if (num_cpu_iregs == 4) 472 if (num_cpu_iregs == 4)
453 sbus_writel(0, &sun4m_irq_global->interrupt_target); 473 sbus_writel(0, &sun4m_irq_global->interrupt_target);
454 474
455 BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
456 BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
457 BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
458 BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
459 BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM); 475 BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
460 BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM); 476 BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
461 477
462 sparc_irq_config.init_timers = sun4m_init_timers; 478 sparc_irq_config.init_timers = sun4m_init_timers;
479 sparc_irq_config.build_device_irq = sun4m_build_device_irq;
463 480
464#ifdef CONFIG_SMP 481#ifdef CONFIG_SMP
465 BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM); 482 BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);