diff options
-rw-r--r-- | arch/sh/include/asm/irq.h | 16 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 36 | ||||
-rw-r--r-- | arch/sh/kernel/irq.c | 49 | ||||
-rw-r--r-- | drivers/sh/Kconfig | 11 | ||||
-rw-r--r-- | drivers/sh/intc.c | 200 | ||||
-rw-r--r-- | include/linux/sh_intc.h | 11 |
6 files changed, 244 insertions, 79 deletions
diff --git a/arch/sh/include/asm/irq.h b/arch/sh/include/asm/irq.h index df8e1500527c..99c593b3a827 100644 --- a/arch/sh/include/asm/irq.h +++ b/arch/sh/include/asm/irq.h | |||
@@ -12,6 +12,14 @@ | |||
12 | #define NR_IRQS_LEGACY 8 /* Legacy external IRQ0-7 */ | 12 | #define NR_IRQS_LEGACY 8 /* Legacy external IRQ0-7 */ |
13 | 13 | ||
14 | /* | 14 | /* |
15 | * This is a special IRQ number for indicating that no IRQ has been | ||
16 | * triggered and to simply ignore the IRQ dispatch. This is a special | ||
17 | * case that can happen with IRQ auto-distribution when multiple CPUs | ||
18 | * are woken up and signalled in parallel. | ||
19 | */ | ||
20 | #define NO_IRQ_IGNORE ((unsigned int)-1) | ||
21 | |||
22 | /* | ||
15 | * Convert back and forth between INTEVT and IRQ values. | 23 | * Convert back and forth between INTEVT and IRQ values. |
16 | */ | 24 | */ |
17 | #ifdef CONFIG_CPU_HAS_INTEVT | 25 | #ifdef CONFIG_CPU_HAS_INTEVT |
@@ -53,6 +61,14 @@ extern void irq_ctx_exit(int cpu); | |||
53 | # define irq_ctx_exit(cpu) do { } while (0) | 61 | # define irq_ctx_exit(cpu) do { } while (0) |
54 | #endif | 62 | #endif |
55 | 63 | ||
64 | #ifdef CONFIG_INTC_BALANCING | ||
65 | extern unsigned int irq_lookup(unsigned int irq); | ||
66 | extern void irq_finish(unsigned int irq); | ||
67 | #else | ||
68 | #define irq_lookup(irq) (irq) | ||
69 | #define irq_finish(irq) do { } while (0) | ||
70 | #endif | ||
71 | |||
56 | #include <asm-generic/irq.h> | 72 | #include <asm-generic/irq.h> |
57 | #ifdef CONFIG_CPU_SH5 | 73 | #ifdef CONFIG_CPU_SH5 |
58 | #include <cpu/irq.h> | 74 | #include <cpu/irq.h> |
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index 235edf8065df..d7336036d04d 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c | |||
@@ -573,7 +573,6 @@ static struct platform_device *sh7786_devices[] __initdata = { | |||
573 | &usb_ohci_device, | 573 | &usb_ohci_device, |
574 | }; | 574 | }; |
575 | 575 | ||
576 | |||
577 | /* | 576 | /* |
578 | * Please call this function if your platform board | 577 | * Please call this function if your platform board |
579 | * use external clock for USB | 578 | * use external clock for USB |
@@ -581,6 +580,7 @@ static struct platform_device *sh7786_devices[] __initdata = { | |||
581 | #define USBCTL0 0xffe70858 | 580 | #define USBCTL0 0xffe70858 |
582 | #define CLOCK_MODE_MASK 0xffffff7f | 581 | #define CLOCK_MODE_MASK 0xffffff7f |
583 | #define EXT_CLOCK_MODE 0x00000080 | 582 | #define EXT_CLOCK_MODE 0x00000080 |
583 | |||
584 | void __init sh7786_usb_use_exclock(void) | 584 | void __init sh7786_usb_use_exclock(void) |
585 | { | 585 | { |
586 | u32 val = __raw_readl(USBCTL0) & CLOCK_MODE_MASK; | 586 | u32 val = __raw_readl(USBCTL0) & CLOCK_MODE_MASK; |
@@ -598,6 +598,7 @@ void __init sh7786_usb_use_exclock(void) | |||
598 | #define PLL_ENB 0x00000002 | 598 | #define PLL_ENB 0x00000002 |
599 | #define PHY_RST 0x00000004 | 599 | #define PHY_RST 0x00000004 |
600 | #define ACT_PLL_STATUS 0xc0000000 | 600 | #define ACT_PLL_STATUS 0xc0000000 |
601 | |||
601 | static void __init sh7786_usb_setup(void) | 602 | static void __init sh7786_usb_setup(void) |
602 | { | 603 | { |
603 | int i = 1000000; | 604 | int i = 1000000; |
@@ -753,9 +754,19 @@ static struct intc_vect vectors[] __initdata = { | |||
753 | #define INTMSK2 0xfe410068 | 754 | #define INTMSK2 0xfe410068 |
754 | #define INTMSKCLR2 0xfe41006c | 755 | #define INTMSKCLR2 0xfe41006c |
755 | 756 | ||
757 | #define INTDISTCR0 0xfe4100b0 | ||
758 | #define INTDISTCR1 0xfe4100b4 | ||
759 | #define INTACK 0xfe4100b8 | ||
760 | #define INTACKCLR 0xfe4100bc | ||
761 | #define INT2DISTCR0 0xfe410900 | ||
762 | #define INT2DISTCR1 0xfe410904 | ||
763 | #define INT2DISTCR2 0xfe410908 | ||
764 | #define INT2DISTCR3 0xfe41090c | ||
765 | |||
756 | static struct intc_mask_reg mask_registers[] __initdata = { | 766 | static struct intc_mask_reg mask_registers[] __initdata = { |
757 | { CnINTMSK0, CnINTMSKCLR0, 32, | 767 | { CnINTMSK0, CnINTMSKCLR0, 32, |
758 | { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, | 768 | { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 }, |
769 | INTC_SMP_BALANCING(INTDISTCR0) }, | ||
759 | { INTMSK2, INTMSKCLR2, 32, | 770 | { INTMSK2, INTMSKCLR2, 32, |
760 | { IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, | 771 | { IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, |
761 | IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, | 772 | IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, |
@@ -767,7 +778,8 @@ static struct intc_mask_reg mask_registers[] __initdata = { | |||
767 | IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, 0, } }, | 778 | IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, 0, } }, |
768 | { CnINT2MSKR0, CnINT2MSKCR0 , 32, | 779 | { CnINT2MSKR0, CnINT2MSKCR0 , 32, |
769 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | 780 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
770 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WDT } }, | 781 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WDT }, |
782 | INTC_SMP_BALANCING(INT2DISTCR0) }, | ||
771 | { CnINT2MSKR1, CnINT2MSKCR1, 32, | 783 | { CnINT2MSKR1, CnINT2MSKCR1, 32, |
772 | { TMU0_0, TMU0_1, TMU0_2, TMU0_3, TMU1_0, TMU1_1, TMU1_2, 0, | 784 | { TMU0_0, TMU0_1, TMU0_2, TMU0_3, TMU1_0, TMU1_1, TMU1_2, 0, |
773 | DMAC0_0, DMAC0_1, DMAC0_2, DMAC0_3, DMAC0_4, DMAC0_5, DMAC0_6, | 785 | DMAC0_0, DMAC0_1, DMAC0_2, DMAC0_3, DMAC0_4, DMAC0_5, DMAC0_6, |
@@ -776,14 +788,14 @@ static struct intc_mask_reg mask_registers[] __initdata = { | |||
776 | HPB_0, HPB_1, HPB_2, | 788 | HPB_0, HPB_1, HPB_2, |
777 | SCIF0_0, SCIF0_1, SCIF0_2, SCIF0_3, | 789 | SCIF0_0, SCIF0_1, SCIF0_2, SCIF0_3, |
778 | SCIF1, | 790 | SCIF1, |
779 | TMU2, TMU3, 0, } }, | 791 | TMU2, TMU3, 0, }, INTC_SMP_BALANCING(INT2DISTCR1) }, |
780 | { CnINT2MSKR2, CnINT2MSKCR2, 32, | 792 | { CnINT2MSKR2, CnINT2MSKCR2, 32, |
781 | { 0, 0, SCIF2, SCIF3, SCIF4, SCIF5, | 793 | { 0, 0, SCIF2, SCIF3, SCIF4, SCIF5, |
782 | Eth_0, Eth_1, | 794 | Eth_0, Eth_1, |
783 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | 795 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
784 | PCIeC0_0, PCIeC0_1, PCIeC0_2, | 796 | PCIeC0_0, PCIeC0_1, PCIeC0_2, |
785 | PCIeC1_0, PCIeC1_1, PCIeC1_2, | 797 | PCIeC1_0, PCIeC1_1, PCIeC1_2, |
786 | USB, 0, 0 } }, | 798 | USB, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR2) }, |
787 | { CnINT2MSKR3, CnINT2MSKCR3, 32, | 799 | { CnINT2MSKR3, CnINT2MSKCR3, 32, |
788 | { 0, 0, 0, 0, 0, 0, | 800 | { 0, 0, 0, 0, 0, 0, |
789 | I2C0, I2C1, | 801 | I2C0, I2C1, |
@@ -792,7 +804,7 @@ static struct intc_mask_reg mask_registers[] __initdata = { | |||
792 | HAC0, HAC1, | 804 | HAC0, HAC1, |
793 | FLCTL, 0, | 805 | FLCTL, 0, |
794 | HSPI, GPIO0, GPIO1, Thermal, | 806 | HSPI, GPIO0, GPIO1, Thermal, |
795 | 0, 0, 0, 0, 0, 0, 0, 0 } }, | 807 | 0, 0, 0, 0, 0, 0, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR3) }, |
796 | }; | 808 | }; |
797 | 809 | ||
798 | static struct intc_prio_reg prio_registers[] __initdata = { | 810 | static struct intc_prio_reg prio_registers[] __initdata = { |
@@ -910,6 +922,18 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567, | |||
910 | #define INTC_INTMSKCLR2 INTMSKCLR2 | 922 | #define INTC_INTMSKCLR2 INTMSKCLR2 |
911 | #define INTC_USERIMASK 0xfe411000 | 923 | #define INTC_USERIMASK 0xfe411000 |
912 | 924 | ||
925 | #ifdef CONFIG_INTC_BALANCING | ||
926 | unsigned int irq_lookup(unsigned int irq) | ||
927 | { | ||
928 | return __raw_readl(INTACK) & 1 ? irq : NO_IRQ_IGNORE; | ||
929 | } | ||
930 | |||
931 | void irq_finish(unsigned int irq) | ||
932 | { | ||
933 | __raw_writel(irq2evt(irq), INTACKCLR); | ||
934 | } | ||
935 | #endif | ||
936 | |||
913 | void __init plat_irq_setup(void) | 937 | void __init plat_irq_setup(void) |
914 | { | 938 | { |
915 | /* disable IRQ3-0 + IRQ7-4 */ | 939 | /* disable IRQ3-0 + IRQ7-4 */ |
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index d2d41d046657..f6a9319c28e2 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c | |||
@@ -113,19 +113,14 @@ union irq_ctx { | |||
113 | 113 | ||
114 | static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly; | 114 | static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly; |
115 | static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly; | 115 | static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly; |
116 | #endif | ||
117 | 116 | ||
118 | asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs) | 117 | static char softirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; |
118 | static char hardirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; | ||
119 | |||
120 | static inline void handle_one_irq(unsigned int irq) | ||
119 | { | 121 | { |
120 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
121 | #ifdef CONFIG_IRQSTACKS | ||
122 | union irq_ctx *curctx, *irqctx; | 122 | union irq_ctx *curctx, *irqctx; |
123 | #endif | ||
124 | |||
125 | irq_enter(); | ||
126 | irq = irq_demux(irq); | ||
127 | 123 | ||
128 | #ifdef CONFIG_IRQSTACKS | ||
129 | curctx = (union irq_ctx *)current_thread_info(); | 124 | curctx = (union irq_ctx *)current_thread_info(); |
130 | irqctx = hardirq_ctx[smp_processor_id()]; | 125 | irqctx = hardirq_ctx[smp_processor_id()]; |
131 | 126 | ||
@@ -164,20 +159,9 @@ asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs) | |||
164 | "r5", "r6", "r7", "r8", "t", "pr" | 159 | "r5", "r6", "r7", "r8", "t", "pr" |
165 | ); | 160 | ); |
166 | } else | 161 | } else |
167 | #endif | ||
168 | generic_handle_irq(irq); | 162 | generic_handle_irq(irq); |
169 | |||
170 | irq_exit(); | ||
171 | |||
172 | set_irq_regs(old_regs); | ||
173 | return 1; | ||
174 | } | 163 | } |
175 | 164 | ||
176 | #ifdef CONFIG_IRQSTACKS | ||
177 | static char softirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; | ||
178 | |||
179 | static char hardirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; | ||
180 | |||
181 | /* | 165 | /* |
182 | * allocate per-cpu stacks for hardirq and for softirq processing | 166 | * allocate per-cpu stacks for hardirq and for softirq processing |
183 | */ | 167 | */ |
@@ -257,8 +241,33 @@ asmlinkage void do_softirq(void) | |||
257 | 241 | ||
258 | local_irq_restore(flags); | 242 | local_irq_restore(flags); |
259 | } | 243 | } |
244 | #else | ||
245 | static inline void handle_one_irq(unsigned int irq) | ||
246 | { | ||
247 | generic_handle_irq(irq); | ||
248 | } | ||
260 | #endif | 249 | #endif |
261 | 250 | ||
251 | asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs) | ||
252 | { | ||
253 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
254 | |||
255 | irq_enter(); | ||
256 | |||
257 | irq = irq_demux(irq_lookup(irq)); | ||
258 | |||
259 | if (irq != NO_IRQ_IGNORE) { | ||
260 | handle_one_irq(irq); | ||
261 | irq_finish(irq); | ||
262 | } | ||
263 | |||
264 | irq_exit(); | ||
265 | |||
266 | set_irq_regs(old_regs); | ||
267 | |||
268 | return IRQ_HANDLED; | ||
269 | } | ||
270 | |||
262 | void __init init_IRQ(void) | 271 | void __init init_IRQ(void) |
263 | { | 272 | { |
264 | plat_irq_setup(); | 273 | plat_irq_setup(); |
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 | ||
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index f0e8cca199c7..0d6cd38e673d 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h | |||
@@ -23,6 +23,9 @@ struct intc_group { | |||
23 | struct intc_mask_reg { | 23 | struct intc_mask_reg { |
24 | unsigned long set_reg, clr_reg, reg_width; | 24 | unsigned long set_reg, clr_reg, reg_width; |
25 | intc_enum enum_ids[32]; | 25 | intc_enum enum_ids[32]; |
26 | #ifdef CONFIG_INTC_BALANCING | ||
27 | unsigned long dist_reg; | ||
28 | #endif | ||
26 | #ifdef CONFIG_SMP | 29 | #ifdef CONFIG_SMP |
27 | unsigned long smp; | 30 | unsigned long smp; |
28 | #endif | 31 | #endif |
@@ -41,8 +44,14 @@ struct intc_sense_reg { | |||
41 | intc_enum enum_ids[16]; | 44 | intc_enum enum_ids[16]; |
42 | }; | 45 | }; |
43 | 46 | ||
47 | #ifdef CONFIG_INTC_BALANCING | ||
48 | #define INTC_SMP_BALANCING(reg) .dist_reg = (reg) | ||
49 | #else | ||
50 | #define INTC_SMP_BALANCING(reg) | ||
51 | #endif | ||
52 | |||
44 | #ifdef CONFIG_SMP | 53 | #ifdef CONFIG_SMP |
45 | #define INTC_SMP(stride, nr) .smp = (stride) | ((nr) << 8) | 54 | #define INTC_SMP(stride, nr) .smp = (stride) | ((nr) << 8) |
46 | #else | 55 | #else |
47 | #define INTC_SMP(stride, nr) | 56 | #define INTC_SMP(stride, nr) |
48 | #endif | 57 | #endif |