diff options
author | Paul Mundt <lethal@linux-sh.org> | 2010-04-15 00:13:52 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-04-15 00:13:52 -0400 |
commit | dc825b17904a06bbd2f79d720b23156e4c01a22f (patch) | |
tree | 8f1e13b850a06264530f1f1bb680a541e73cef34 /arch/sh | |
parent | fecf066c2d2fbc7e6a7e7e3a5af772a165bdd7b0 (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 'arch/sh')
-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 |
3 files changed, 75 insertions, 26 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(); |