aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh/intc.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-04-15 00:13:52 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-04-15 00:13:52 -0400
commitdc825b17904a06bbd2f79d720b23156e4c01a22f (patch)
tree8f1e13b850a06264530f1f1bb680a541e73cef34 /drivers/sh/intc.c
parentfecf066c2d2fbc7e6a7e7e3a5af772a165bdd7b0 (diff)
sh: intc: IRQ auto-distribution support.
This implements support for hardware-managed IRQ balancing as implemented by SH-X3 cores (presently only hooked up for SH7786, but can probably be carried over to other SH-X3 cores, too). CPUs need to specify their distribution register along with the mask definitions, as these follow the same format. Peripheral IRQs that don't opt out of balancing will be automatically distributed at the whim of the hardware block, while each CPU needs to verify whether it is handling the IRQ or not, especially before clearing the mask. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/sh/intc.c')
-rw-r--r--drivers/sh/intc.c200
1 files changed, 148 insertions, 52 deletions
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index 77d10acf1884..dcb4c833820b 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -98,6 +98,9 @@ static DEFINE_SPINLOCK(vector_lock);
98static unsigned int intc_prio_level[NR_IRQS]; /* for now */ 98static unsigned int intc_prio_level[NR_IRQS]; /* for now */
99static unsigned int default_prio_level = 2; /* 2 - 16 */ 99static unsigned int default_prio_level = 2; /* 2 - 16 */
100static unsigned long ack_handle[NR_IRQS]; 100static unsigned long ack_handle[NR_IRQS];
101#ifdef CONFIG_INTC_BALANCING
102static unsigned long dist_handle[NR_IRQS];
103#endif
101 104
102static inline struct intc_desc_int *get_intc_desc(unsigned int irq) 105static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
103{ 106{
@@ -105,6 +108,47 @@ static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
105 return container_of(chip, struct intc_desc_int, chip); 108 return container_of(chip, struct intc_desc_int, chip);
106} 109}
107 110
111static unsigned long intc_phys_to_virt(struct intc_desc_int *d,
112 unsigned long address)
113{
114 struct intc_window *window;
115 int k;
116
117 /* scan through physical windows and convert address */
118 for (k = 0; k < d->nr_windows; k++) {
119 window = d->window + k;
120
121 if (address < window->phys)
122 continue;
123
124 if (address >= (window->phys + window->size))
125 continue;
126
127 address -= window->phys;
128 address += (unsigned long)window->virt;
129
130 return address;
131 }
132
133 /* no windows defined, register must be 1:1 mapped virt:phys */
134 return address;
135}
136
137static unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address)
138{
139 unsigned int k;
140
141 address = intc_phys_to_virt(d, address);
142
143 for (k = 0; k < d->nr_reg; k++) {
144 if (d->reg[k] == address)
145 return k;
146 }
147
148 BUG();
149 return 0;
150}
151
108static inline unsigned int set_field(unsigned int value, 152static inline unsigned int set_field(unsigned int value,
109 unsigned int field_value, 153 unsigned int field_value,
110 unsigned int handle) 154 unsigned int handle)
@@ -238,6 +282,85 @@ static void (*intc_disable_fns[])(unsigned long addr,
238 [MODE_PCLR_REG] = intc_mode_field, 282 [MODE_PCLR_REG] = intc_mode_field,
239}; 283};
240 284
285#ifdef CONFIG_INTC_BALANCING
286static inline void intc_balancing_enable(unsigned int irq)
287{
288 struct intc_desc_int *d = get_intc_desc(irq);
289 unsigned long handle = dist_handle[irq];
290 unsigned long addr;
291
292 if (irq_balancing_disabled(irq) || !handle)
293 return;
294
295 addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
296 intc_reg_fns[_INTC_FN(handle)](addr, handle, 1);
297}
298
299static inline void intc_balancing_disable(unsigned int irq)
300{
301 struct intc_desc_int *d = get_intc_desc(irq);
302 unsigned long handle = dist_handle[irq];
303 unsigned long addr;
304
305 if (irq_balancing_disabled(irq) || !handle)
306 return;
307
308 addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
309 intc_reg_fns[_INTC_FN(handle)](addr, handle, 0);
310}
311
312static unsigned int intc_dist_data(struct intc_desc *desc,
313 struct intc_desc_int *d,
314 intc_enum enum_id)
315{
316 struct intc_mask_reg *mr = desc->hw.mask_regs;
317 unsigned int i, j, fn, mode;
318 unsigned long reg_e, reg_d;
319
320 for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) {
321 mr = desc->hw.mask_regs + i;
322
323 /*
324 * Skip this entry if there's no auto-distribution
325 * register associated with it.
326 */
327 if (!mr->dist_reg)
328 continue;
329
330 for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
331 if (mr->enum_ids[j] != enum_id)
332 continue;
333
334 fn = REG_FN_MODIFY_BASE;
335 mode = MODE_ENABLE_REG;
336 reg_e = mr->dist_reg;
337 reg_d = mr->dist_reg;
338
339 fn += (mr->reg_width >> 3) - 1;
340 return _INTC_MK(fn, mode,
341 intc_get_reg(d, reg_e),
342 intc_get_reg(d, reg_d),
343 1,
344 (mr->reg_width - 1) - j);
345 }
346 }
347
348 /*
349 * It's possible we've gotten here with no distribution options
350 * available for the IRQ in question, so we just skip over those.
351 */
352 return 0;
353}
354#else
355static inline void intc_balancing_enable(unsigned int irq)
356{
357}
358
359static inline void intc_balancing_disable(unsigned int irq)
360{
361}
362#endif
363
241static inline void _intc_enable(unsigned int irq, unsigned long handle) 364static inline void _intc_enable(unsigned int irq, unsigned long handle)
242{ 365{
243 struct intc_desc_int *d = get_intc_desc(irq); 366 struct intc_desc_int *d = get_intc_desc(irq);
@@ -253,6 +376,8 @@ static inline void _intc_enable(unsigned int irq, unsigned long handle)
253 intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ 376 intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
254 [_INTC_FN(handle)], irq); 377 [_INTC_FN(handle)], irq);
255 } 378 }
379
380 intc_balancing_enable(irq);
256} 381}
257 382
258static void intc_enable(unsigned int irq) 383static void intc_enable(unsigned int irq)
@@ -263,10 +388,12 @@ static void intc_enable(unsigned int irq)
263static void intc_disable(unsigned int irq) 388static void intc_disable(unsigned int irq)
264{ 389{
265 struct intc_desc_int *d = get_intc_desc(irq); 390 struct intc_desc_int *d = get_intc_desc(irq);
266 unsigned long handle = (unsigned long) get_irq_chip_data(irq); 391 unsigned long handle = (unsigned long)get_irq_chip_data(irq);
267 unsigned long addr; 392 unsigned long addr;
268 unsigned int cpu; 393 unsigned int cpu;
269 394
395 intc_balancing_disable(irq);
396
270 for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { 397 for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
271#ifdef CONFIG_SMP 398#ifdef CONFIG_SMP
272 if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) 399 if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
@@ -345,8 +472,7 @@ static void intc_mask_ack(unsigned int irq)
345 472
346 intc_disable(irq); 473 intc_disable(irq);
347 474
348 /* read register and write zero only to the assocaited bit */ 475 /* read register and write zero only to the associated bit */
349
350 if (handle) { 476 if (handle) {
351 addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); 477 addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
352 switch (_INTC_FN(handle)) { 478 switch (_INTC_FN(handle)) {
@@ -375,7 +501,8 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
375{ 501{
376 int i; 502 int i;
377 503
378 /* this doesn't scale well, but... 504 /*
505 * this doesn't scale well, but...
379 * 506 *
380 * this function should only be used for cerain uncommon 507 * this function should only be used for cerain uncommon
381 * operations such as intc_set_priority() and intc_set_sense() 508 * operations such as intc_set_priority() and intc_set_sense()
@@ -386,7 +513,6 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
386 * memory footprint down is to make sure the array is sorted 513 * memory footprint down is to make sure the array is sorted
387 * and then perform a bisect to lookup the irq. 514 * and then perform a bisect to lookup the irq.
388 */ 515 */
389
390 for (i = 0; i < nr_hp; i++) { 516 for (i = 0; i < nr_hp; i++) {
391 if ((hp + i)->irq != irq) 517 if ((hp + i)->irq != irq)
392 continue; 518 continue;
@@ -417,7 +543,6 @@ int intc_set_priority(unsigned int irq, unsigned int prio)
417 * primary masking method is using intc_prio_level[irq] 543 * primary masking method is using intc_prio_level[irq]
418 * priority level will be set during next enable() 544 * priority level will be set during next enable()
419 */ 545 */
420
421 if (_INTC_FN(ihp->handle) != REG_FN_ERR) 546 if (_INTC_FN(ihp->handle) != REG_FN_ERR)
422 _intc_enable(irq, ihp->handle); 547 _intc_enable(irq, ihp->handle);
423 } 548 }
@@ -456,48 +581,6 @@ static int intc_set_sense(unsigned int irq, unsigned int type)
456 return 0; 581 return 0;
457} 582}
458 583
459static unsigned long intc_phys_to_virt(struct intc_desc_int *d,
460 unsigned long address)
461{
462 struct intc_window *window;
463 int k;
464
465 /* scan through physical windows and convert address */
466 for (k = 0; k < d->nr_windows; k++) {
467 window = d->window + k;
468
469 if (address < window->phys)
470 continue;
471
472 if (address >= (window->phys + window->size))
473 continue;
474
475 address -= window->phys;
476 address += (unsigned long)window->virt;
477
478 return address;
479 }
480
481 /* no windows defined, register must be 1:1 mapped virt:phys */
482 return address;
483}
484
485static unsigned int __init intc_get_reg(struct intc_desc_int *d,
486 unsigned long address)
487{
488 unsigned int k;
489
490 address = intc_phys_to_virt(d, address);
491
492 for (k = 0; k < d->nr_reg; k++) {
493 if (d->reg[k] == address)
494 return k;
495 }
496
497 BUG();
498 return 0;
499}
500
501static intc_enum __init intc_grp_id(struct intc_desc *desc, 584static intc_enum __init intc_grp_id(struct intc_desc *desc,
502 intc_enum enum_id) 585 intc_enum enum_id)
503{ 586{
@@ -755,13 +838,14 @@ static void __init intc_register_irq(struct intc_desc *desc,
755 */ 838 */
756 set_bit(irq, intc_irq_map); 839 set_bit(irq, intc_irq_map);
757 840
758 /* Prefer single interrupt source bitmap over other combinations: 841 /*
842 * Prefer single interrupt source bitmap over other combinations:
843 *
759 * 1. bitmap, single interrupt source 844 * 1. bitmap, single interrupt source
760 * 2. priority, single interrupt source 845 * 2. priority, single interrupt source
761 * 3. bitmap, multiple interrupt sources (groups) 846 * 3. bitmap, multiple interrupt sources (groups)
762 * 4. priority, multiple interrupt sources (groups) 847 * 4. priority, multiple interrupt sources (groups)
763 */ 848 */
764
765 data[0] = intc_mask_data(desc, d, enum_id, 0); 849 data[0] = intc_mask_data(desc, d, enum_id, 0);
766 data[1] = intc_prio_data(desc, d, enum_id, 0); 850 data[1] = intc_prio_data(desc, d, enum_id, 0);
767 851
@@ -786,7 +870,8 @@ static void __init intc_register_irq(struct intc_desc *desc,
786 handle_level_irq, "level"); 870 handle_level_irq, "level");
787 set_irq_chip_data(irq, (void *)data[primary]); 871 set_irq_chip_data(irq, (void *)data[primary]);
788 872
789 /* set priority level 873 /*
874 * set priority level
790 * - this needs to be at least 2 for 5-bit priorities on 7780 875 * - this needs to be at least 2 for 5-bit priorities on 7780
791 */ 876 */
792 intc_prio_level[irq] = default_prio_level; 877 intc_prio_level[irq] = default_prio_level;
@@ -806,7 +891,6 @@ static void __init intc_register_irq(struct intc_desc *desc,
806 * only secondary priority should access registers, so 891 * only secondary priority should access registers, so
807 * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() 892 * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority()
808 */ 893 */
809
810 hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); 894 hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0);
811 hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); 895 hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0);
812 } 896 }
@@ -827,6 +911,11 @@ static void __init intc_register_irq(struct intc_desc *desc,
827 if (desc->hw.ack_regs) 911 if (desc->hw.ack_regs)
828 ack_handle[irq] = intc_ack_data(desc, d, enum_id); 912 ack_handle[irq] = intc_ack_data(desc, d, enum_id);
829 913
914#ifdef CONFIG_INTC_BALANCING
915 if (desc->hw.mask_regs)
916 dist_handle[irq] = intc_dist_data(desc, d, enum_id);
917#endif
918
830#ifdef CONFIG_ARM 919#ifdef CONFIG_ARM
831 set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ 920 set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */
832#endif 921#endif
@@ -892,6 +981,10 @@ int __init register_intc_controller(struct intc_desc *desc)
892 } 981 }
893 982
894 d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; 983 d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0;
984#ifdef CONFIG_INTC_BALANCING
985 if (d->nr_reg)
986 d->nr_reg += hw->nr_mask_regs;
987#endif
895 d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; 988 d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
896 d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; 989 d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
897 d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; 990 d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0;
@@ -912,6 +1005,9 @@ int __init register_intc_controller(struct intc_desc *desc)
912 smp = IS_SMP(hw->mask_regs[i]); 1005 smp = IS_SMP(hw->mask_regs[i]);
913 k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); 1006 k += save_reg(d, k, hw->mask_regs[i].set_reg, smp);
914 k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); 1007 k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp);
1008#ifdef CONFIG_INTC_BALANCING
1009 k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0);
1010#endif
915 } 1011 }
916 } 1012 }
917 1013