diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/sh/Kconfig | 11 | ||||
-rw-r--r-- | drivers/sh/intc.c | 200 |
2 files changed, 159 insertions, 52 deletions
diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index 22c3cdaf22fe..a54de0b9b3df 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig | |||
@@ -11,3 +11,14 @@ config INTC_USERIMASK | |||
11 | drivers that are using special priority levels. | 11 | drivers that are using special priority levels. |
12 | 12 | ||
13 | If in doubt, say N. | 13 | If in doubt, say N. |
14 | |||
15 | config INTC_BALANCING | ||
16 | bool "Hardware IRQ balancing support" | ||
17 | depends on SMP && SUPERH && CPU_SUBTYPE_SH7786 | ||
18 | help | ||
19 | This enables support for IRQ auto-distribution mode on SH-X3 | ||
20 | SMP parts. All of the balancing and CPU wakeup decisions are | ||
21 | taken care of automatically by hardware for distributed | ||
22 | vectors. | ||
23 | |||
24 | If in doubt, say N. | ||
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); | |||
98 | static unsigned int intc_prio_level[NR_IRQS]; /* for now */ | 98 | static unsigned int intc_prio_level[NR_IRQS]; /* for now */ |
99 | static unsigned int default_prio_level = 2; /* 2 - 16 */ | 99 | static unsigned int default_prio_level = 2; /* 2 - 16 */ |
100 | static unsigned long ack_handle[NR_IRQS]; | 100 | static unsigned long ack_handle[NR_IRQS]; |
101 | #ifdef CONFIG_INTC_BALANCING | ||
102 | static unsigned long dist_handle[NR_IRQS]; | ||
103 | #endif | ||
101 | 104 | ||
102 | static inline struct intc_desc_int *get_intc_desc(unsigned int irq) | 105 | static 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 | ||
111 | static 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 | |||
137 | static 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 | |||
108 | static inline unsigned int set_field(unsigned int value, | 152 | static 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 | ||
286 | static 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 | |||
299 | static 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 | |||
312 | static 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 | ||
355 | static inline void intc_balancing_enable(unsigned int irq) | ||
356 | { | ||
357 | } | ||
358 | |||
359 | static inline void intc_balancing_disable(unsigned int irq) | ||
360 | { | ||
361 | } | ||
362 | #endif | ||
363 | |||
241 | static inline void _intc_enable(unsigned int irq, unsigned long handle) | 364 | static 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 | ||
258 | static void intc_enable(unsigned int irq) | 383 | static void intc_enable(unsigned int irq) |
@@ -263,10 +388,12 @@ static void intc_enable(unsigned int irq) | |||
263 | static void intc_disable(unsigned int irq) | 388 | static 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 | ||
459 | static 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 | |||
485 | static 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 | |||
501 | static intc_enum __init intc_grp_id(struct intc_desc *desc, | 584 | static 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 | ||