diff options
-rw-r--r-- | arch/s390/Kconfig | 11 | ||||
-rw-r--r-- | arch/s390/include/asm/hardirq.h | 5 | ||||
-rw-r--r-- | arch/s390/include/asm/hw_irq.h | 17 | ||||
-rw-r--r-- | arch/s390/include/asm/irq.h | 35 | ||||
-rw-r--r-- | arch/s390/include/asm/pci.h | 16 | ||||
-rw-r--r-- | arch/s390/include/asm/serial.h | 6 | ||||
-rw-r--r-- | arch/s390/kernel/entry.S | 12 | ||||
-rw-r--r-- | arch/s390/kernel/entry64.S | 9 | ||||
-rw-r--r-- | arch/s390/kernel/irq.c | 160 | ||||
-rw-r--r-- | arch/s390/pci/Makefile | 2 | ||||
-rw-r--r-- | arch/s390/pci/pci.c | 255 | ||||
-rw-r--r-- | arch/s390/pci/pci_msi.c | 134 | ||||
-rw-r--r-- | drivers/s390/cio/airq.c | 21 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 46 | ||||
-rw-r--r-- | drivers/s390/cio/cio.h | 3 |
15 files changed, 249 insertions, 483 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 8a4cae78f03c..8b7892bf6d8b 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
@@ -116,6 +116,7 @@ config S390 | |||
116 | select HAVE_FUNCTION_GRAPH_TRACER | 116 | select HAVE_FUNCTION_GRAPH_TRACER |
117 | select HAVE_FUNCTION_TRACER | 117 | select HAVE_FUNCTION_TRACER |
118 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST | 118 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST |
119 | select HAVE_GENERIC_HARDIRQS | ||
119 | select HAVE_KERNEL_BZIP2 | 120 | select HAVE_KERNEL_BZIP2 |
120 | select HAVE_KERNEL_GZIP | 121 | select HAVE_KERNEL_GZIP |
121 | select HAVE_KERNEL_LZ4 | 122 | select HAVE_KERNEL_LZ4 |
@@ -445,6 +446,16 @@ config PCI_NR_FUNCTIONS | |||
445 | This allows you to specify the maximum number of PCI functions which | 446 | This allows you to specify the maximum number of PCI functions which |
446 | this kernel will support. | 447 | this kernel will support. |
447 | 448 | ||
449 | config PCI_NR_MSI | ||
450 | int "Maximum number of MSI interrupts (64-32768)" | ||
451 | range 64 32768 | ||
452 | default "256" | ||
453 | help | ||
454 | This defines the number of virtual interrupts the kernel will | ||
455 | provide for MSI interrupts. If you configure your system to have | ||
456 | too few drivers will fail to allocate MSI interrupts for all | ||
457 | PCI devices. | ||
458 | |||
448 | source "drivers/pci/Kconfig" | 459 | source "drivers/pci/Kconfig" |
449 | source "drivers/pci/pcie/Kconfig" | 460 | source "drivers/pci/pcie/Kconfig" |
450 | source "drivers/pci/hotplug/Kconfig" | 461 | source "drivers/pci/hotplug/Kconfig" |
diff --git a/arch/s390/include/asm/hardirq.h b/arch/s390/include/asm/hardirq.h index 0c82ba86e997..a908d2941c5d 100644 --- a/arch/s390/include/asm/hardirq.h +++ b/arch/s390/include/asm/hardirq.h | |||
@@ -20,4 +20,9 @@ | |||
20 | 20 | ||
21 | #define HARDIRQ_BITS 8 | 21 | #define HARDIRQ_BITS 8 |
22 | 22 | ||
23 | static inline void ack_bad_irq(unsigned int irq) | ||
24 | { | ||
25 | printk(KERN_CRIT "unexpected IRQ trap at vector %02x\n", irq); | ||
26 | } | ||
27 | |||
23 | #endif /* __ASM_HARDIRQ_H */ | 28 | #endif /* __ASM_HARDIRQ_H */ |
diff --git a/arch/s390/include/asm/hw_irq.h b/arch/s390/include/asm/hw_irq.h index 7e3d2586c1ff..ee96a8b697f9 100644 --- a/arch/s390/include/asm/hw_irq.h +++ b/arch/s390/include/asm/hw_irq.h | |||
@@ -4,19 +4,8 @@ | |||
4 | #include <linux/msi.h> | 4 | #include <linux/msi.h> |
5 | #include <linux/pci.h> | 5 | #include <linux/pci.h> |
6 | 6 | ||
7 | static inline struct msi_desc *irq_get_msi_desc(unsigned int irq) | 7 | void __init init_airq_interrupts(void); |
8 | { | 8 | void __init init_cio_interrupts(void); |
9 | return __irq_get_msi_desc(irq); | 9 | void __init init_ext_interrupts(void); |
10 | } | ||
11 | |||
12 | /* Must be called with msi map lock held */ | ||
13 | static inline int irq_set_msi_desc(unsigned int irq, struct msi_desc *msi) | ||
14 | { | ||
15 | if (!msi) | ||
16 | return -EINVAL; | ||
17 | |||
18 | msi->irq = irq; | ||
19 | return 0; | ||
20 | } | ||
21 | 10 | ||
22 | #endif | 11 | #endif |
diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 87c17bfb2968..1eaa3625803c 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h | |||
@@ -1,17 +1,28 @@ | |||
1 | #ifndef _ASM_IRQ_H | 1 | #ifndef _ASM_IRQ_H |
2 | #define _ASM_IRQ_H | 2 | #define _ASM_IRQ_H |
3 | 3 | ||
4 | #define EXT_INTERRUPT 1 | ||
5 | #define IO_INTERRUPT 2 | ||
6 | #define THIN_INTERRUPT 3 | ||
7 | |||
8 | #define NR_IRQS_BASE 4 | ||
9 | |||
10 | #ifdef CONFIG_PCI_NR_MSI | ||
11 | # define NR_IRQS (NR_IRQS_BASE + CONFIG_PCI_NR_MSI) | ||
12 | #else | ||
13 | # define NR_IRQS NR_IRQS_BASE | ||
14 | #endif | ||
15 | |||
16 | /* This number is used when no interrupt has been assigned */ | ||
17 | #define NO_IRQ 0 | ||
18 | |||
19 | #ifndef __ASSEMBLY__ | ||
20 | |||
4 | #include <linux/hardirq.h> | 21 | #include <linux/hardirq.h> |
5 | #include <linux/percpu.h> | 22 | #include <linux/percpu.h> |
6 | #include <linux/cache.h> | 23 | #include <linux/cache.h> |
7 | #include <linux/types.h> | 24 | #include <linux/types.h> |
8 | 25 | ||
9 | enum interruption_main_class { | ||
10 | EXTERNAL_INTERRUPT, | ||
11 | IO_INTERRUPT, | ||
12 | NR_IRQS | ||
13 | }; | ||
14 | |||
15 | enum interruption_class { | 26 | enum interruption_class { |
16 | IRQEXT_CLK, | 27 | IRQEXT_CLK, |
17 | IRQEXT_EXC, | 28 | IRQEXT_EXC, |
@@ -72,14 +83,8 @@ void service_subclass_irq_unregister(void); | |||
72 | void measurement_alert_subclass_register(void); | 83 | void measurement_alert_subclass_register(void); |
73 | void measurement_alert_subclass_unregister(void); | 84 | void measurement_alert_subclass_unregister(void); |
74 | 85 | ||
75 | #ifdef CONFIG_LOCKDEP | 86 | #define irq_canonicalize(irq) (irq) |
76 | # define disable_irq_nosync_lockdep(irq) disable_irq_nosync(irq) | 87 | |
77 | # define disable_irq_nosync_lockdep_irqsave(irq, flags) \ | 88 | #endif /* __ASSEMBLY__ */ |
78 | disable_irq_nosync(irq) | ||
79 | # define disable_irq_lockdep(irq) disable_irq(irq) | ||
80 | # define enable_irq_lockdep(irq) enable_irq(irq) | ||
81 | # define enable_irq_lockdep_irqrestore(irq, flags) \ | ||
82 | enable_irq(irq) | ||
83 | #endif | ||
84 | 89 | ||
85 | #endif /* _ASM_IRQ_H */ | 90 | #endif /* _ASM_IRQ_H */ |
diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 7d3295087aa4..d0872769d44e 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h | |||
@@ -53,12 +53,6 @@ struct zpci_fmb { | |||
53 | atomic64_t unmapped_pages; | 53 | atomic64_t unmapped_pages; |
54 | } __packed __aligned(16); | 54 | } __packed __aligned(16); |
55 | 55 | ||
56 | struct msi_map { | ||
57 | unsigned long irq; | ||
58 | struct msi_desc *msi; | ||
59 | struct hlist_node msi_chain; | ||
60 | }; | ||
61 | |||
62 | #define ZPCI_MSI_VEC_BITS 11 | 56 | #define ZPCI_MSI_VEC_BITS 11 |
63 | #define ZPCI_MSI_VEC_MAX (1 << ZPCI_MSI_VEC_BITS) | 57 | #define ZPCI_MSI_VEC_MAX (1 << ZPCI_MSI_VEC_BITS) |
64 | #define ZPCI_MSI_VEC_MASK (ZPCI_MSI_VEC_MAX - 1) | 58 | #define ZPCI_MSI_VEC_MASK (ZPCI_MSI_VEC_MAX - 1) |
@@ -92,8 +86,6 @@ struct zpci_dev { | |||
92 | 86 | ||
93 | /* IRQ stuff */ | 87 | /* IRQ stuff */ |
94 | u64 msi_addr; /* MSI address */ | 88 | u64 msi_addr; /* MSI address */ |
95 | struct zdev_irq_map *irq_map; | ||
96 | struct msi_map *msi_map; | ||
97 | struct airq_iv *aibv; /* adapter interrupt bit vector */ | 89 | struct airq_iv *aibv; /* adapter interrupt bit vector */ |
98 | unsigned int aisb; /* number of the summary bit */ | 90 | unsigned int aisb; /* number of the summary bit */ |
99 | 91 | ||
@@ -153,14 +145,6 @@ int clp_add_pci_device(u32, u32, int); | |||
153 | int clp_enable_fh(struct zpci_dev *, u8); | 145 | int clp_enable_fh(struct zpci_dev *, u8); |
154 | int clp_disable_fh(struct zpci_dev *); | 146 | int clp_disable_fh(struct zpci_dev *); |
155 | 147 | ||
156 | /* MSI */ | ||
157 | struct msi_desc *__irq_get_msi_desc(unsigned int); | ||
158 | int zpci_msi_set_mask_bits(struct msi_desc *, u32, u32); | ||
159 | int zpci_setup_msi_irq(struct zpci_dev *, struct msi_desc *, unsigned int, int); | ||
160 | void zpci_teardown_msi_irq(struct zpci_dev *, struct msi_desc *); | ||
161 | int zpci_msihash_init(void); | ||
162 | void zpci_msihash_exit(void); | ||
163 | |||
164 | #ifdef CONFIG_PCI | 148 | #ifdef CONFIG_PCI |
165 | /* Error handling and recovery */ | 149 | /* Error handling and recovery */ |
166 | void zpci_event_error(void *); | 150 | void zpci_event_error(void *); |
diff --git a/arch/s390/include/asm/serial.h b/arch/s390/include/asm/serial.h new file mode 100644 index 000000000000..5b3e48ef534b --- /dev/null +++ b/arch/s390/include/asm/serial.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef _ASM_S390_SERIAL_H | ||
2 | #define _ASM_S390_SERIAL_H | ||
3 | |||
4 | #define BASE_BAUD 0 | ||
5 | |||
6 | #endif /* _ASM_S390_SERIAL_H */ | ||
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index be7a408be7a1..5ca70b4b72cb 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <asm/unistd.h> | 18 | #include <asm/unistd.h> |
19 | #include <asm/page.h> | 19 | #include <asm/page.h> |
20 | #include <asm/sigp.h> | 20 | #include <asm/sigp.h> |
21 | #include <asm/irq.h> | ||
21 | 22 | ||
22 | __PT_R0 = __PT_GPRS | 23 | __PT_R0 = __PT_GPRS |
23 | __PT_R1 = __PT_GPRS + 4 | 24 | __PT_R1 = __PT_GPRS + 4 |
@@ -435,6 +436,11 @@ io_skip: | |||
435 | io_loop: | 436 | io_loop: |
436 | l %r1,BASED(.Ldo_IRQ) | 437 | l %r1,BASED(.Ldo_IRQ) |
437 | lr %r2,%r11 # pass pointer to pt_regs | 438 | lr %r2,%r11 # pass pointer to pt_regs |
439 | lhi %r3,IO_INTERRUPT | ||
440 | tm __PT_INT_CODE+8(%r11),0x80 # adapter interrupt ? | ||
441 | jz io_call | ||
442 | lhi %r3,THIN_INTERRUPT | ||
443 | io_call: | ||
438 | basr %r14,%r1 # call do_IRQ | 444 | basr %r14,%r1 # call do_IRQ |
439 | tm __LC_MACHINE_FLAGS+2,0x10 # MACHINE_FLAG_LPAR | 445 | tm __LC_MACHINE_FLAGS+2,0x10 # MACHINE_FLAG_LPAR |
440 | jz io_return | 446 | jz io_return |
@@ -584,9 +590,10 @@ ext_skip: | |||
584 | mvc __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR | 590 | mvc __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR |
585 | mvc __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS | 591 | mvc __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS |
586 | TRACE_IRQS_OFF | 592 | TRACE_IRQS_OFF |
593 | l %r1,BASED(.Ldo_IRQ) | ||
587 | lr %r2,%r11 # pass pointer to pt_regs | 594 | lr %r2,%r11 # pass pointer to pt_regs |
588 | l %r1,BASED(.Ldo_extint) | 595 | lhi %r3,EXT_INTERRUPT |
589 | basr %r14,%r1 # call do_extint | 596 | basr %r14,%r1 # call do_IRQ |
590 | j io_return | 597 | j io_return |
591 | 598 | ||
592 | /* | 599 | /* |
@@ -902,7 +909,6 @@ cleanup_idle_wait: | |||
902 | .Ldo_machine_check: .long s390_do_machine_check | 909 | .Ldo_machine_check: .long s390_do_machine_check |
903 | .Lhandle_mcck: .long s390_handle_mcck | 910 | .Lhandle_mcck: .long s390_handle_mcck |
904 | .Ldo_IRQ: .long do_IRQ | 911 | .Ldo_IRQ: .long do_IRQ |
905 | .Ldo_extint: .long do_extint | ||
906 | .Ldo_signal: .long do_signal | 912 | .Ldo_signal: .long do_signal |
907 | .Ldo_notify_resume: .long do_notify_resume | 913 | .Ldo_notify_resume: .long do_notify_resume |
908 | .Ldo_per_trap: .long do_per_trap | 914 | .Ldo_per_trap: .long do_per_trap |
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 1c039d0c24c7..980c7aa1cc5c 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <asm/unistd.h> | 19 | #include <asm/unistd.h> |
20 | #include <asm/page.h> | 20 | #include <asm/page.h> |
21 | #include <asm/sigp.h> | 21 | #include <asm/sigp.h> |
22 | #include <asm/irq.h> | ||
22 | 23 | ||
23 | __PT_R0 = __PT_GPRS | 24 | __PT_R0 = __PT_GPRS |
24 | __PT_R1 = __PT_GPRS + 8 | 25 | __PT_R1 = __PT_GPRS + 8 |
@@ -468,6 +469,11 @@ io_skip: | |||
468 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) | 469 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) |
469 | io_loop: | 470 | io_loop: |
470 | lgr %r2,%r11 # pass pointer to pt_regs | 471 | lgr %r2,%r11 # pass pointer to pt_regs |
472 | lghi %r3,IO_INTERRUPT | ||
473 | tm __PT_INT_CODE+8(%r11),0x80 # adapter interrupt ? | ||
474 | jz io_call | ||
475 | lghi %r3,THIN_INTERRUPT | ||
476 | io_call: | ||
471 | brasl %r14,do_IRQ | 477 | brasl %r14,do_IRQ |
472 | tm __LC_MACHINE_FLAGS+6,0x10 # MACHINE_FLAG_LPAR | 478 | tm __LC_MACHINE_FLAGS+6,0x10 # MACHINE_FLAG_LPAR |
473 | jz io_return | 479 | jz io_return |
@@ -623,7 +629,8 @@ ext_skip: | |||
623 | TRACE_IRQS_OFF | 629 | TRACE_IRQS_OFF |
624 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) | 630 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) |
625 | lgr %r2,%r11 # pass pointer to pt_regs | 631 | lgr %r2,%r11 # pass pointer to pt_regs |
626 | brasl %r14,do_extint | 632 | lghi %r3,EXT_INTERRUPT |
633 | brasl %r14,do_IRQ | ||
627 | j io_return | 634 | j io_return |
628 | 635 | ||
629 | /* | 636 | /* |
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 54b0995514e8..b34ba0ea96a9 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <asm/cputime.h> | 22 | #include <asm/cputime.h> |
23 | #include <asm/lowcore.h> | 23 | #include <asm/lowcore.h> |
24 | #include <asm/irq.h> | 24 | #include <asm/irq.h> |
25 | #include <asm/hw_irq.h> | ||
25 | #include "entry.h" | 26 | #include "entry.h" |
26 | 27 | ||
27 | DEFINE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat); | 28 | DEFINE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat); |
@@ -42,9 +43,10 @@ struct irq_class { | |||
42 | * Since the external and I/O interrupt fields are already sums we would end | 43 | * Since the external and I/O interrupt fields are already sums we would end |
43 | * up with having a sum which accounts each interrupt twice. | 44 | * up with having a sum which accounts each interrupt twice. |
44 | */ | 45 | */ |
45 | static const struct irq_class irqclass_main_desc[NR_IRQS] = { | 46 | static const struct irq_class irqclass_main_desc[NR_IRQS_BASE] = { |
46 | [EXTERNAL_INTERRUPT] = {.name = "EXT"}, | 47 | [EXT_INTERRUPT] = {.name = "EXT"}, |
47 | [IO_INTERRUPT] = {.name = "I/O"} | 48 | [IO_INTERRUPT] = {.name = "I/O"}, |
49 | [THIN_INTERRUPT] = {.name = "AIO"}, | ||
48 | }; | 50 | }; |
49 | 51 | ||
50 | /* | 52 | /* |
@@ -86,6 +88,28 @@ static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = { | |||
86 | [CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"}, | 88 | [CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"}, |
87 | }; | 89 | }; |
88 | 90 | ||
91 | void __init init_IRQ(void) | ||
92 | { | ||
93 | irq_reserve_irqs(0, THIN_INTERRUPT); | ||
94 | init_cio_interrupts(); | ||
95 | init_airq_interrupts(); | ||
96 | init_ext_interrupts(); | ||
97 | } | ||
98 | |||
99 | void do_IRQ(struct pt_regs *regs, int irq) | ||
100 | { | ||
101 | struct pt_regs *old_regs; | ||
102 | |||
103 | old_regs = set_irq_regs(regs); | ||
104 | irq_enter(); | ||
105 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) | ||
106 | /* Serve timer interrupts first. */ | ||
107 | clock_comparator_work(); | ||
108 | generic_handle_irq(irq); | ||
109 | irq_exit(); | ||
110 | set_irq_regs(old_regs); | ||
111 | } | ||
112 | |||
89 | /* | 113 | /* |
90 | * show_interrupts is needed by /proc/interrupts. | 114 | * show_interrupts is needed by /proc/interrupts. |
91 | */ | 115 | */ |
@@ -100,27 +124,36 @@ int show_interrupts(struct seq_file *p, void *v) | |||
100 | for_each_online_cpu(cpu) | 124 | for_each_online_cpu(cpu) |
101 | seq_printf(p, "CPU%d ", cpu); | 125 | seq_printf(p, "CPU%d ", cpu); |
102 | seq_putc(p, '\n'); | 126 | seq_putc(p, '\n'); |
127 | goto out; | ||
103 | } | 128 | } |
104 | if (irq < NR_IRQS) { | 129 | if (irq < NR_IRQS) { |
130 | if (irq >= NR_IRQS_BASE) | ||
131 | goto out; | ||
105 | seq_printf(p, "%s: ", irqclass_main_desc[irq].name); | 132 | seq_printf(p, "%s: ", irqclass_main_desc[irq].name); |
106 | for_each_online_cpu(cpu) | 133 | for_each_online_cpu(cpu) |
107 | seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[irq]); | 134 | seq_printf(p, "%10u ", kstat_irqs_cpu(irq, cpu)); |
108 | seq_putc(p, '\n'); | 135 | seq_putc(p, '\n'); |
109 | goto skip_arch_irqs; | 136 | goto out; |
110 | } | 137 | } |
111 | for (irq = 0; irq < NR_ARCH_IRQS; irq++) { | 138 | for (irq = 0; irq < NR_ARCH_IRQS; irq++) { |
112 | seq_printf(p, "%s: ", irqclass_sub_desc[irq].name); | 139 | seq_printf(p, "%s: ", irqclass_sub_desc[irq].name); |
113 | for_each_online_cpu(cpu) | 140 | for_each_online_cpu(cpu) |
114 | seq_printf(p, "%10u ", per_cpu(irq_stat, cpu).irqs[irq]); | 141 | seq_printf(p, "%10u ", |
142 | per_cpu(irq_stat, cpu).irqs[irq]); | ||
115 | if (irqclass_sub_desc[irq].desc) | 143 | if (irqclass_sub_desc[irq].desc) |
116 | seq_printf(p, " %s", irqclass_sub_desc[irq].desc); | 144 | seq_printf(p, " %s", irqclass_sub_desc[irq].desc); |
117 | seq_putc(p, '\n'); | 145 | seq_putc(p, '\n'); |
118 | } | 146 | } |
119 | skip_arch_irqs: | 147 | out: |
120 | put_online_cpus(); | 148 | put_online_cpus(); |
121 | return 0; | 149 | return 0; |
122 | } | 150 | } |
123 | 151 | ||
152 | int arch_show_interrupts(struct seq_file *p, int prec) | ||
153 | { | ||
154 | return 0; | ||
155 | } | ||
156 | |||
124 | /* | 157 | /* |
125 | * Switch to the asynchronous interrupt stack for softirq execution. | 158 | * Switch to the asynchronous interrupt stack for softirq execution. |
126 | */ | 159 | */ |
@@ -159,14 +192,6 @@ asmlinkage void do_softirq(void) | |||
159 | local_irq_restore(flags); | 192 | local_irq_restore(flags); |
160 | } | 193 | } |
161 | 194 | ||
162 | #ifdef CONFIG_PROC_FS | ||
163 | void init_irq_proc(void) | ||
164 | { | ||
165 | if (proc_mkdir("irq", NULL)) | ||
166 | create_prof_cpu_mask(); | ||
167 | } | ||
168 | #endif | ||
169 | |||
170 | /* | 195 | /* |
171 | * ext_int_hash[index] is the list head for all external interrupts that hash | 196 | * ext_int_hash[index] is the list head for all external interrupts that hash |
172 | * to this index. | 197 | * to this index. |
@@ -183,14 +208,6 @@ struct ext_int_info { | |||
183 | /* ext_int_hash_lock protects the handler lists for external interrupts */ | 208 | /* ext_int_hash_lock protects the handler lists for external interrupts */ |
184 | DEFINE_SPINLOCK(ext_int_hash_lock); | 209 | DEFINE_SPINLOCK(ext_int_hash_lock); |
185 | 210 | ||
186 | static void __init init_external_interrupts(void) | ||
187 | { | ||
188 | int idx; | ||
189 | |||
190 | for (idx = 0; idx < ARRAY_SIZE(ext_int_hash); idx++) | ||
191 | INIT_LIST_HEAD(&ext_int_hash[idx]); | ||
192 | } | ||
193 | |||
194 | static inline int ext_hash(u16 code) | 211 | static inline int ext_hash(u16 code) |
195 | { | 212 | { |
196 | return (code + (code >> 9)) & 0xff; | 213 | return (code + (code >> 9)) & 0xff; |
@@ -234,20 +251,13 @@ int unregister_external_interrupt(u16 code, ext_int_handler_t handler) | |||
234 | } | 251 | } |
235 | EXPORT_SYMBOL(unregister_external_interrupt); | 252 | EXPORT_SYMBOL(unregister_external_interrupt); |
236 | 253 | ||
237 | void __irq_entry do_extint(struct pt_regs *regs) | 254 | static irqreturn_t do_ext_interrupt(int irq, void *dummy) |
238 | { | 255 | { |
256 | struct pt_regs *regs = get_irq_regs(); | ||
239 | struct ext_code ext_code; | 257 | struct ext_code ext_code; |
240 | struct pt_regs *old_regs; | ||
241 | struct ext_int_info *p; | 258 | struct ext_int_info *p; |
242 | int index; | 259 | int index; |
243 | 260 | ||
244 | old_regs = set_irq_regs(regs); | ||
245 | irq_enter(); | ||
246 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) { | ||
247 | /* Serve timer interrupts first. */ | ||
248 | clock_comparator_work(); | ||
249 | } | ||
250 | kstat_incr_irqs_this_cpu(EXTERNAL_INTERRUPT, NULL); | ||
251 | ext_code = *(struct ext_code *) ®s->int_code; | 261 | ext_code = *(struct ext_code *) ®s->int_code; |
252 | if (ext_code.code != 0x1004) | 262 | if (ext_code.code != 0x1004) |
253 | __get_cpu_var(s390_idle).nohz_delay = 1; | 263 | __get_cpu_var(s390_idle).nohz_delay = 1; |
@@ -259,13 +269,25 @@ void __irq_entry do_extint(struct pt_regs *regs) | |||
259 | p->handler(ext_code, regs->int_parm, | 269 | p->handler(ext_code, regs->int_parm, |
260 | regs->int_parm_long); | 270 | regs->int_parm_long); |
261 | rcu_read_unlock(); | 271 | rcu_read_unlock(); |
262 | irq_exit(); | 272 | |
263 | set_irq_regs(old_regs); | 273 | return IRQ_HANDLED; |
264 | } | 274 | } |
265 | 275 | ||
266 | void __init init_IRQ(void) | 276 | static struct irqaction external_interrupt = { |
277 | .name = "EXT", | ||
278 | .handler = do_ext_interrupt, | ||
279 | }; | ||
280 | |||
281 | void __init init_ext_interrupts(void) | ||
267 | { | 282 | { |
268 | init_external_interrupts(); | 283 | int idx; |
284 | |||
285 | for (idx = 0; idx < ARRAY_SIZE(ext_int_hash); idx++) | ||
286 | INIT_LIST_HEAD(&ext_int_hash[idx]); | ||
287 | |||
288 | irq_set_chip_and_handler(EXT_INTERRUPT, | ||
289 | &dummy_irq_chip, handle_percpu_irq); | ||
290 | setup_irq(EXT_INTERRUPT, &external_interrupt); | ||
269 | } | 291 | } |
270 | 292 | ||
271 | static DEFINE_SPINLOCK(sc_irq_lock); | 293 | static DEFINE_SPINLOCK(sc_irq_lock); |
@@ -313,69 +335,3 @@ void measurement_alert_subclass_unregister(void) | |||
313 | spin_unlock(&ma_subclass_lock); | 335 | spin_unlock(&ma_subclass_lock); |
314 | } | 336 | } |
315 | EXPORT_SYMBOL(measurement_alert_subclass_unregister); | 337 | EXPORT_SYMBOL(measurement_alert_subclass_unregister); |
316 | |||
317 | #ifdef CONFIG_SMP | ||
318 | void synchronize_irq(unsigned int irq) | ||
319 | { | ||
320 | /* | ||
321 | * Not needed, the handler is protected by a lock and IRQs that occur | ||
322 | * after the handler is deleted are just NOPs. | ||
323 | */ | ||
324 | } | ||
325 | EXPORT_SYMBOL_GPL(synchronize_irq); | ||
326 | #endif | ||
327 | |||
328 | #ifndef CONFIG_PCI | ||
329 | |||
330 | /* Only PCI devices have dynamically-defined IRQ handlers */ | ||
331 | |||
332 | int request_irq(unsigned int irq, irq_handler_t handler, | ||
333 | unsigned long irqflags, const char *devname, void *dev_id) | ||
334 | { | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | EXPORT_SYMBOL_GPL(request_irq); | ||
338 | |||
339 | void free_irq(unsigned int irq, void *dev_id) | ||
340 | { | ||
341 | WARN_ON(1); | ||
342 | } | ||
343 | EXPORT_SYMBOL_GPL(free_irq); | ||
344 | |||
345 | void enable_irq(unsigned int irq) | ||
346 | { | ||
347 | WARN_ON(1); | ||
348 | } | ||
349 | EXPORT_SYMBOL_GPL(enable_irq); | ||
350 | |||
351 | void disable_irq(unsigned int irq) | ||
352 | { | ||
353 | WARN_ON(1); | ||
354 | } | ||
355 | EXPORT_SYMBOL_GPL(disable_irq); | ||
356 | |||
357 | #endif /* !CONFIG_PCI */ | ||
358 | |||
359 | void disable_irq_nosync(unsigned int irq) | ||
360 | { | ||
361 | disable_irq(irq); | ||
362 | } | ||
363 | EXPORT_SYMBOL_GPL(disable_irq_nosync); | ||
364 | |||
365 | unsigned long probe_irq_on(void) | ||
366 | { | ||
367 | return 0; | ||
368 | } | ||
369 | EXPORT_SYMBOL_GPL(probe_irq_on); | ||
370 | |||
371 | int probe_irq_off(unsigned long val) | ||
372 | { | ||
373 | return 0; | ||
374 | } | ||
375 | EXPORT_SYMBOL_GPL(probe_irq_off); | ||
376 | |||
377 | unsigned int probe_irq_mask(unsigned long val) | ||
378 | { | ||
379 | return val; | ||
380 | } | ||
381 | EXPORT_SYMBOL_GPL(probe_irq_mask); | ||
diff --git a/arch/s390/pci/Makefile b/arch/s390/pci/Makefile index 086a2e37935d..a9e1dc4ae442 100644 --- a/arch/s390/pci/Makefile +++ b/arch/s390/pci/Makefile | |||
@@ -2,5 +2,5 @@ | |||
2 | # Makefile for the s390 PCI subsystem. | 2 | # Makefile for the s390 PCI subsystem. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_PCI) += pci.o pci_dma.o pci_clp.o pci_msi.o pci_sysfs.o \ | 5 | obj-$(CONFIG_PCI) += pci.o pci_dma.o pci_clp.o pci_sysfs.o \ |
6 | pci_event.o pci_debug.o pci_insn.o | 6 | pci_event.o pci_debug.o pci_insn.o |
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 27e7fed3707d..d65dc4f50e2a 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c | |||
@@ -50,24 +50,23 @@ EXPORT_SYMBOL_GPL(zpci_list); | |||
50 | DEFINE_MUTEX(zpci_list_lock); | 50 | DEFINE_MUTEX(zpci_list_lock); |
51 | EXPORT_SYMBOL_GPL(zpci_list_lock); | 51 | EXPORT_SYMBOL_GPL(zpci_list_lock); |
52 | 52 | ||
53 | static struct pci_hp_callback_ops *hotplug_ops; | ||
54 | 53 | ||
55 | static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES); | 54 | static void zpci_enable_irq(struct irq_data *data); |
56 | static DEFINE_SPINLOCK(zpci_domain_lock); | 55 | static void zpci_disable_irq(struct irq_data *data); |
57 | 56 | ||
58 | struct callback { | 57 | static struct irq_chip zpci_irq_chip = { |
59 | irq_handler_t handler; | 58 | .name = "zPCI", |
60 | void *data; | 59 | .irq_unmask = zpci_enable_irq, |
60 | .irq_mask = zpci_disable_irq, | ||
61 | }; | 61 | }; |
62 | 62 | ||
63 | struct zdev_irq_map { | 63 | static struct pci_hp_callback_ops *hotplug_ops; |
64 | struct airq_iv *aibv; /* Adapter interrupt bit vector */ | 64 | |
65 | struct callback *cb; /* callback handler array */ | 65 | static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES); |
66 | int msi_vecs; /* consecutive MSI-vectors used */ | 66 | static DEFINE_SPINLOCK(zpci_domain_lock); |
67 | }; | ||
68 | 67 | ||
69 | static struct airq_iv *zpci_aisb_iv; | 68 | static struct airq_iv *zpci_aisb_iv; |
70 | static struct zdev_irq_map *zpci_imap[ZPCI_NR_DEVICES]; | 69 | static struct airq_iv *zpci_aibv[ZPCI_NR_DEVICES]; |
71 | 70 | ||
72 | /* Adapter interrupt definitions */ | 71 | /* Adapter interrupt definitions */ |
73 | static void zpci_irq_handler(struct airq_struct *airq); | 72 | static void zpci_irq_handler(struct airq_struct *airq); |
@@ -83,19 +82,8 @@ static DECLARE_BITMAP(zpci_iomap, ZPCI_IOMAP_MAX_ENTRIES); | |||
83 | struct zpci_iomap_entry *zpci_iomap_start; | 82 | struct zpci_iomap_entry *zpci_iomap_start; |
84 | EXPORT_SYMBOL_GPL(zpci_iomap_start); | 83 | EXPORT_SYMBOL_GPL(zpci_iomap_start); |
85 | 84 | ||
86 | static struct kmem_cache *zdev_irq_cache; | ||
87 | static struct kmem_cache *zdev_fmb_cache; | 85 | static struct kmem_cache *zdev_fmb_cache; |
88 | 86 | ||
89 | static inline int irq_to_msi_nr(unsigned int irq) | ||
90 | { | ||
91 | return irq & ZPCI_MSI_VEC_MASK; | ||
92 | } | ||
93 | |||
94 | static inline int irq_to_dev_nr(unsigned int irq) | ||
95 | { | ||
96 | return irq >> ZPCI_MSI_VEC_BITS; | ||
97 | } | ||
98 | |||
99 | struct zpci_dev *get_zdev(struct pci_dev *pdev) | 87 | struct zpci_dev *get_zdev(struct pci_dev *pdev) |
100 | { | 88 | { |
101 | return (struct zpci_dev *) pdev->sysdata; | 89 | return (struct zpci_dev *) pdev->sysdata; |
@@ -283,21 +271,42 @@ static int zpci_cfg_store(struct zpci_dev *zdev, int offset, u32 val, u8 len) | |||
283 | return rc; | 271 | return rc; |
284 | } | 272 | } |
285 | 273 | ||
286 | void enable_irq(unsigned int irq) | 274 | static int zpci_msi_set_mask_bits(struct msi_desc *msi, u32 mask, u32 flag) |
275 | { | ||
276 | int offset, pos; | ||
277 | u32 mask_bits; | ||
278 | |||
279 | if (msi->msi_attrib.is_msix) { | ||
280 | offset = msi->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + | ||
281 | PCI_MSIX_ENTRY_VECTOR_CTRL; | ||
282 | msi->masked = readl(msi->mask_base + offset); | ||
283 | writel(flag, msi->mask_base + offset); | ||
284 | } else if (msi->msi_attrib.maskbit) { | ||
285 | pos = (long) msi->mask_base; | ||
286 | pci_read_config_dword(msi->dev, pos, &mask_bits); | ||
287 | mask_bits &= ~(mask); | ||
288 | mask_bits |= flag & mask; | ||
289 | pci_write_config_dword(msi->dev, pos, mask_bits); | ||
290 | } else | ||
291 | return 0; | ||
292 | |||
293 | msi->msi_attrib.maskbit = !!flag; | ||
294 | return 1; | ||
295 | } | ||
296 | |||
297 | static void zpci_enable_irq(struct irq_data *data) | ||
287 | { | 298 | { |
288 | struct msi_desc *msi = irq_get_msi_desc(irq); | 299 | struct msi_desc *msi = irq_get_msi_desc(data->irq); |
289 | 300 | ||
290 | zpci_msi_set_mask_bits(msi, 1, 0); | 301 | zpci_msi_set_mask_bits(msi, 1, 0); |
291 | } | 302 | } |
292 | EXPORT_SYMBOL_GPL(enable_irq); | ||
293 | 303 | ||
294 | void disable_irq(unsigned int irq) | 304 | static void zpci_disable_irq(struct irq_data *data) |
295 | { | 305 | { |
296 | struct msi_desc *msi = irq_get_msi_desc(irq); | 306 | struct msi_desc *msi = irq_get_msi_desc(data->irq); |
297 | 307 | ||
298 | zpci_msi_set_mask_bits(msi, 1, 1); | 308 | zpci_msi_set_mask_bits(msi, 1, 1); |
299 | } | 309 | } |
300 | EXPORT_SYMBOL_GPL(disable_irq); | ||
301 | 310 | ||
302 | void pcibios_fixup_bus(struct pci_bus *bus) | 311 | void pcibios_fixup_bus(struct pci_bus *bus) |
303 | { | 312 | { |
@@ -385,7 +394,7 @@ static struct pci_ops pci_root_ops = { | |||
385 | static void zpci_irq_handler(struct airq_struct *airq) | 394 | static void zpci_irq_handler(struct airq_struct *airq) |
386 | { | 395 | { |
387 | unsigned long si, ai; | 396 | unsigned long si, ai; |
388 | struct zdev_irq_map *imap; | 397 | struct airq_iv *aibv; |
389 | int irqs_on = 0; | 398 | int irqs_on = 0; |
390 | 399 | ||
391 | inc_irq_stat(IRQIO_PCI); | 400 | inc_irq_stat(IRQIO_PCI); |
@@ -403,69 +412,33 @@ static void zpci_irq_handler(struct airq_struct *airq) | |||
403 | } | 412 | } |
404 | 413 | ||
405 | /* Scan the adapter interrupt vector for this device. */ | 414 | /* Scan the adapter interrupt vector for this device. */ |
406 | imap = zpci_imap[si]; | 415 | aibv = zpci_aibv[si]; |
407 | for (ai = 0;;) { | 416 | for (ai = 0;;) { |
408 | ai = airq_iv_scan(imap->aibv, ai, imap->msi_vecs); | 417 | ai = airq_iv_scan(aibv, ai, airq_iv_end(aibv)); |
409 | if (ai == -1UL) | 418 | if (ai == -1UL) |
410 | break; | 419 | break; |
411 | inc_irq_stat(IRQIO_MSI); | 420 | inc_irq_stat(IRQIO_MSI); |
412 | airq_iv_lock(imap->aibv, ai); | 421 | airq_iv_lock(aibv, ai); |
413 | if (imap->cb[ai].handler) | 422 | generic_handle_irq(airq_iv_get_data(aibv, ai)); |
414 | imap->cb[ai].handler(ai, imap->cb[ai].data); | 423 | airq_iv_unlock(aibv, ai); |
415 | airq_iv_unlock(imap->aibv, ai); | ||
416 | } | 424 | } |
417 | } | 425 | } |
418 | } | 426 | } |
419 | 427 | ||
420 | static int zpci_alloc_msi(struct zpci_dev *zdev, int msi_vecs) | ||
421 | { | ||
422 | unsigned long size; | ||
423 | |||
424 | /* Alloc aibv & callback space */ | ||
425 | zdev->irq_map = kmem_cache_zalloc(zdev_irq_cache, GFP_KERNEL); | ||
426 | if (!zdev->irq_map) | ||
427 | goto out; | ||
428 | /* Store the number of used MSI vectors */ | ||
429 | zdev->irq_map->msi_vecs = msi_vecs; | ||
430 | /* Allocate callback array */ | ||
431 | size = sizeof(struct callback) * msi_vecs; | ||
432 | zdev->irq_map->cb = kzalloc(size, GFP_KERNEL); | ||
433 | if (!zdev->irq_map->cb) | ||
434 | goto out_map; | ||
435 | /* Allocate msi_map array */ | ||
436 | size = sizeof(struct msi_map) * msi_vecs; | ||
437 | zdev->msi_map = kzalloc(size, GFP_KERNEL); | ||
438 | if (!zdev->msi_map) | ||
439 | goto out_cb; | ||
440 | return 0; | ||
441 | |||
442 | out_cb: | ||
443 | kfree(zdev->irq_map->cb); | ||
444 | out_map: | ||
445 | kmem_cache_free(zdev_irq_cache, zdev->irq_map); | ||
446 | out: | ||
447 | return -ENOMEM; | ||
448 | } | ||
449 | |||
450 | static void zpci_free_msi(struct zpci_dev *zdev) | ||
451 | { | ||
452 | kfree(zdev->msi_map); | ||
453 | kfree(zdev->irq_map->cb); | ||
454 | kmem_cache_free(zdev_irq_cache, zdev->irq_map); | ||
455 | } | ||
456 | |||
457 | int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) | 428 | int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) |
458 | { | 429 | { |
459 | struct zpci_dev *zdev = get_zdev(pdev); | 430 | struct zpci_dev *zdev = get_zdev(pdev); |
460 | unsigned int msi_nr, msi_vecs; | 431 | unsigned int hwirq, irq, msi_vecs; |
461 | unsigned long aisb; | 432 | unsigned long aisb; |
462 | struct msi_desc *msi; | 433 | struct msi_desc *msi; |
434 | struct msi_msg msg; | ||
463 | int rc; | 435 | int rc; |
464 | 436 | ||
465 | pr_debug("%s: requesting %d MSI-X interrupts...", __func__, nvec); | 437 | pr_debug("%s: requesting %d MSI-X interrupts...", __func__, nvec); |
466 | if (type != PCI_CAP_ID_MSIX && type != PCI_CAP_ID_MSI) | 438 | if (type != PCI_CAP_ID_MSIX && type != PCI_CAP_ID_MSI) |
467 | return -EINVAL; | 439 | return -EINVAL; |
468 | msi_vecs = min(nvec, ZPCI_MSI_VEC_MAX); | 440 | msi_vecs = min(nvec, ZPCI_MSI_VEC_MAX); |
441 | msi_vecs = min_t(unsigned int, msi_vecs, CONFIG_PCI_NR_MSI); | ||
469 | 442 | ||
470 | /* Allocate adapter summary indicator bit */ | 443 | /* Allocate adapter summary indicator bit */ |
471 | rc = -EIO; | 444 | rc = -EIO; |
@@ -476,30 +449,31 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) | |||
476 | 449 | ||
477 | /* Create adapter interrupt vector */ | 450 | /* Create adapter interrupt vector */ |
478 | rc = -ENOMEM; | 451 | rc = -ENOMEM; |
479 | zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_BITLOCK); | 452 | zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK); |
480 | if (!zdev->aibv) | 453 | if (!zdev->aibv) |
481 | goto out_si; | 454 | goto out_si; |
482 | 455 | ||
483 | /* Allocate data structures for msi interrupts */ | ||
484 | rc = zpci_alloc_msi(zdev, msi_vecs); | ||
485 | if (rc) | ||
486 | goto out_iv; | ||
487 | |||
488 | /* Wire up shortcut pointer */ | 456 | /* Wire up shortcut pointer */ |
489 | zpci_imap[aisb] = zdev->irq_map; | 457 | zpci_aibv[aisb] = zdev->aibv; |
490 | zdev->irq_map->aibv = zdev->aibv; | ||
491 | 458 | ||
492 | /* | 459 | /* Request MSI interrupts */ |
493 | * TODO: irq number 0 wont be found if we return less than the | 460 | hwirq = 0; |
494 | * requested MSIs. Ignore it for now and fix in common code. | ||
495 | */ | ||
496 | msi_nr = aisb << ZPCI_MSI_VEC_BITS; | ||
497 | list_for_each_entry(msi, &pdev->msi_list, list) { | 461 | list_for_each_entry(msi, &pdev->msi_list, list) { |
498 | rc = zpci_setup_msi_irq(zdev, msi, msi_nr, | 462 | rc = -EIO; |
499 | aisb << ZPCI_MSI_VEC_BITS); | 463 | irq = irq_alloc_desc(0); /* Alloc irq on node 0 */ |
464 | if (irq == NO_IRQ) | ||
465 | goto out_msi; | ||
466 | rc = irq_set_msi_desc(irq, msi); | ||
500 | if (rc) | 467 | if (rc) |
501 | goto out_msi; | 468 | goto out_msi; |
502 | msi_nr++; | 469 | irq_set_chip_and_handler(irq, &zpci_irq_chip, |
470 | handle_simple_irq); | ||
471 | msg.data = hwirq; | ||
472 | msg.address_lo = zdev->msi_addr & 0xffffffff; | ||
473 | msg.address_hi = zdev->msi_addr >> 32; | ||
474 | write_msi_msg(irq, &msg); | ||
475 | airq_iv_set_data(zdev->aibv, hwirq, irq); | ||
476 | hwirq++; | ||
503 | } | 477 | } |
504 | 478 | ||
505 | /* Enable adapter interrupts */ | 479 | /* Enable adapter interrupts */ |
@@ -510,14 +484,17 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) | |||
510 | return (msi_vecs == nvec) ? 0 : msi_vecs; | 484 | return (msi_vecs == nvec) ? 0 : msi_vecs; |
511 | 485 | ||
512 | out_msi: | 486 | out_msi: |
513 | msi_nr -= aisb << ZPCI_MSI_VEC_BITS; | ||
514 | list_for_each_entry(msi, &pdev->msi_list, list) { | 487 | list_for_each_entry(msi, &pdev->msi_list, list) { |
515 | if (msi_nr-- == 0) | 488 | if (hwirq-- == 0) |
516 | break; | 489 | break; |
517 | zpci_teardown_msi_irq(zdev, msi); | 490 | irq_set_msi_desc(msi->irq, NULL); |
491 | irq_free_desc(msi->irq); | ||
492 | msi->msg.address_lo = 0; | ||
493 | msi->msg.address_hi = 0; | ||
494 | msi->msg.data = 0; | ||
495 | msi->irq = 0; | ||
518 | } | 496 | } |
519 | zpci_free_msi(zdev); | 497 | zpci_aibv[aisb] = NULL; |
520 | out_iv: | ||
521 | airq_iv_release(zdev->aibv); | 498 | airq_iv_release(zdev->aibv); |
522 | out_si: | 499 | out_si: |
523 | airq_iv_free_bit(zpci_aisb_iv, aisb); | 500 | airq_iv_free_bit(zpci_aisb_iv, aisb); |
@@ -541,10 +518,18 @@ void arch_teardown_msi_irqs(struct pci_dev *pdev) | |||
541 | return; | 518 | return; |
542 | } | 519 | } |
543 | 520 | ||
544 | list_for_each_entry(msi, &pdev->msi_list, list) | 521 | /* Release MSI interrupts */ |
545 | zpci_teardown_msi_irq(zdev, msi); | 522 | list_for_each_entry(msi, &pdev->msi_list, list) { |
523 | zpci_msi_set_mask_bits(msi, 1, 1); | ||
524 | irq_set_msi_desc(msi->irq, NULL); | ||
525 | irq_free_desc(msi->irq); | ||
526 | msi->msg.address_lo = 0; | ||
527 | msi->msg.address_hi = 0; | ||
528 | msi->msg.data = 0; | ||
529 | msi->irq = 0; | ||
530 | } | ||
546 | 531 | ||
547 | zpci_free_msi(zdev); | 532 | zpci_aibv[zdev->aisb] = NULL; |
548 | airq_iv_release(zdev->aibv); | 533 | airq_iv_release(zdev->aibv); |
549 | airq_iv_free_bit(zpci_aisb_iv, zdev->aisb); | 534 | airq_iv_free_bit(zpci_aisb_iv, zdev->aisb); |
550 | } | 535 | } |
@@ -625,61 +610,6 @@ int pcibios_add_platform_entries(struct pci_dev *pdev) | |||
625 | return zpci_sysfs_add_device(&pdev->dev); | 610 | return zpci_sysfs_add_device(&pdev->dev); |
626 | } | 611 | } |
627 | 612 | ||
628 | int zpci_request_irq(unsigned int irq, irq_handler_t handler, void *data) | ||
629 | { | ||
630 | unsigned int msi_nr = irq_to_msi_nr(irq); | ||
631 | unsigned int dev_nr = irq_to_dev_nr(irq); | ||
632 | struct zdev_irq_map *imap; | ||
633 | struct msi_desc *msi; | ||
634 | |||
635 | msi = irq_get_msi_desc(irq); | ||
636 | if (!msi) | ||
637 | return -EIO; | ||
638 | |||
639 | imap = zpci_imap[dev_nr]; | ||
640 | imap->cb[msi_nr].handler = handler; | ||
641 | imap->cb[msi_nr].data = data; | ||
642 | |||
643 | /* | ||
644 | * The generic MSI code returns with the interrupt disabled on the | ||
645 | * card, using the MSI mask bits. Firmware doesn't appear to unmask | ||
646 | * at that level, so we do it here by hand. | ||
647 | */ | ||
648 | zpci_msi_set_mask_bits(msi, 1, 0); | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | void zpci_free_irq(unsigned int irq) | ||
653 | { | ||
654 | unsigned int msi_nr = irq_to_msi_nr(irq); | ||
655 | unsigned int dev_nr = irq_to_dev_nr(irq); | ||
656 | struct zdev_irq_map *imap; | ||
657 | struct msi_desc *msi; | ||
658 | |||
659 | /* Disable interrupt */ | ||
660 | msi = irq_get_msi_desc(irq); | ||
661 | if (!msi) | ||
662 | return; | ||
663 | zpci_msi_set_mask_bits(msi, 1, 1); | ||
664 | imap = zpci_imap[dev_nr]; | ||
665 | imap->cb[msi_nr].handler = NULL; | ||
666 | imap->cb[msi_nr].data = NULL; | ||
667 | synchronize_rcu(); | ||
668 | } | ||
669 | |||
670 | int request_irq(unsigned int irq, irq_handler_t handler, | ||
671 | unsigned long irqflags, const char *devname, void *dev_id) | ||
672 | { | ||
673 | return zpci_request_irq(irq, handler, dev_id); | ||
674 | } | ||
675 | EXPORT_SYMBOL_GPL(request_irq); | ||
676 | |||
677 | void free_irq(unsigned int irq, void *dev_id) | ||
678 | { | ||
679 | zpci_free_irq(irq); | ||
680 | } | ||
681 | EXPORT_SYMBOL_GPL(free_irq); | ||
682 | |||
683 | static int __init zpci_irq_init(void) | 613 | static int __init zpci_irq_init(void) |
684 | { | 614 | { |
685 | int rc; | 615 | int rc; |
@@ -930,15 +860,10 @@ static inline int barsize(u8 size) | |||
930 | 860 | ||
931 | static int zpci_mem_init(void) | 861 | static int zpci_mem_init(void) |
932 | { | 862 | { |
933 | zdev_irq_cache = kmem_cache_create("PCI_IRQ_cache", sizeof(struct zdev_irq_map), | ||
934 | L1_CACHE_BYTES, SLAB_HWCACHE_ALIGN, NULL); | ||
935 | if (!zdev_irq_cache) | ||
936 | goto error_zdev; | ||
937 | |||
938 | zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb), | 863 | zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb), |
939 | 16, 0, NULL); | 864 | 16, 0, NULL); |
940 | if (!zdev_fmb_cache) | 865 | if (!zdev_fmb_cache) |
941 | goto error_fmb; | 866 | goto error_zdev; |
942 | 867 | ||
943 | /* TODO: use realloc */ | 868 | /* TODO: use realloc */ |
944 | zpci_iomap_start = kzalloc(ZPCI_IOMAP_MAX_ENTRIES * sizeof(*zpci_iomap_start), | 869 | zpci_iomap_start = kzalloc(ZPCI_IOMAP_MAX_ENTRIES * sizeof(*zpci_iomap_start), |
@@ -949,8 +874,6 @@ static int zpci_mem_init(void) | |||
949 | 874 | ||
950 | error_iomap: | 875 | error_iomap: |
951 | kmem_cache_destroy(zdev_fmb_cache); | 876 | kmem_cache_destroy(zdev_fmb_cache); |
952 | error_fmb: | ||
953 | kmem_cache_destroy(zdev_irq_cache); | ||
954 | error_zdev: | 877 | error_zdev: |
955 | return -ENOMEM; | 878 | return -ENOMEM; |
956 | } | 879 | } |
@@ -958,7 +881,6 @@ error_zdev: | |||
958 | static void zpci_mem_exit(void) | 881 | static void zpci_mem_exit(void) |
959 | { | 882 | { |
960 | kfree(zpci_iomap_start); | 883 | kfree(zpci_iomap_start); |
961 | kmem_cache_destroy(zdev_irq_cache); | ||
962 | kmem_cache_destroy(zdev_fmb_cache); | 884 | kmem_cache_destroy(zdev_fmb_cache); |
963 | } | 885 | } |
964 | 886 | ||
@@ -1007,16 +929,12 @@ static int __init pci_base_init(void) | |||
1007 | 929 | ||
1008 | rc = zpci_debug_init(); | 930 | rc = zpci_debug_init(); |
1009 | if (rc) | 931 | if (rc) |
1010 | return rc; | 932 | goto out; |
1011 | 933 | ||
1012 | rc = zpci_mem_init(); | 934 | rc = zpci_mem_init(); |
1013 | if (rc) | 935 | if (rc) |
1014 | goto out_mem; | 936 | goto out_mem; |
1015 | 937 | ||
1016 | rc = zpci_msihash_init(); | ||
1017 | if (rc) | ||
1018 | goto out_hash; | ||
1019 | |||
1020 | rc = zpci_irq_init(); | 938 | rc = zpci_irq_init(); |
1021 | if (rc) | 939 | if (rc) |
1022 | goto out_irq; | 940 | goto out_irq; |
@@ -1036,11 +954,10 @@ out_find: | |||
1036 | out_dma: | 954 | out_dma: |
1037 | zpci_irq_exit(); | 955 | zpci_irq_exit(); |
1038 | out_irq: | 956 | out_irq: |
1039 | zpci_msihash_exit(); | ||
1040 | out_hash: | ||
1041 | zpci_mem_exit(); | 957 | zpci_mem_exit(); |
1042 | out_mem: | 958 | out_mem: |
1043 | zpci_debug_exit(); | 959 | zpci_debug_exit(); |
960 | out: | ||
1044 | return rc; | 961 | return rc; |
1045 | } | 962 | } |
1046 | subsys_initcall(pci_base_init); | 963 | subsys_initcall(pci_base_init); |
diff --git a/arch/s390/pci/pci_msi.c b/arch/s390/pci/pci_msi.c deleted file mode 100644 index d4b480a18b0f..000000000000 --- a/arch/s390/pci/pci_msi.c +++ /dev/null | |||
@@ -1,134 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corp. 2012 | ||
3 | * | ||
4 | * Author(s): | ||
5 | * Jan Glauber <jang@linux.vnet.ibm.com> | ||
6 | */ | ||
7 | |||
8 | #define COMPONENT "zPCI" | ||
9 | #define pr_fmt(fmt) COMPONENT ": " fmt | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/err.h> | ||
13 | #include <linux/rculist.h> | ||
14 | #include <linux/hash.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/msi.h> | ||
17 | #include <asm/hw_irq.h> | ||
18 | |||
19 | /* mapping of irq numbers to msi_desc */ | ||
20 | static struct hlist_head *msi_hash; | ||
21 | static const unsigned int msi_hash_bits = 8; | ||
22 | #define MSI_HASH_BUCKETS (1U << msi_hash_bits) | ||
23 | #define msi_hashfn(nr) hash_long(nr, msi_hash_bits) | ||
24 | |||
25 | static DEFINE_SPINLOCK(msi_map_lock); | ||
26 | |||
27 | struct msi_desc *__irq_get_msi_desc(unsigned int irq) | ||
28 | { | ||
29 | struct msi_map *map; | ||
30 | |||
31 | hlist_for_each_entry_rcu(map, | ||
32 | &msi_hash[msi_hashfn(irq)], msi_chain) | ||
33 | if (map->irq == irq) | ||
34 | return map->msi; | ||
35 | return NULL; | ||
36 | } | ||
37 | |||
38 | int zpci_msi_set_mask_bits(struct msi_desc *msi, u32 mask, u32 flag) | ||
39 | { | ||
40 | if (msi->msi_attrib.is_msix) { | ||
41 | int offset = msi->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + | ||
42 | PCI_MSIX_ENTRY_VECTOR_CTRL; | ||
43 | msi->masked = readl(msi->mask_base + offset); | ||
44 | writel(flag, msi->mask_base + offset); | ||
45 | } else { | ||
46 | if (msi->msi_attrib.maskbit) { | ||
47 | int pos; | ||
48 | u32 mask_bits; | ||
49 | |||
50 | pos = (long) msi->mask_base; | ||
51 | pci_read_config_dword(msi->dev, pos, &mask_bits); | ||
52 | mask_bits &= ~(mask); | ||
53 | mask_bits |= flag & mask; | ||
54 | pci_write_config_dword(msi->dev, pos, mask_bits); | ||
55 | } else { | ||
56 | return 0; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | msi->msi_attrib.maskbit = !!flag; | ||
61 | return 1; | ||
62 | } | ||
63 | |||
64 | int zpci_setup_msi_irq(struct zpci_dev *zdev, struct msi_desc *msi, | ||
65 | unsigned int nr, int offset) | ||
66 | { | ||
67 | struct msi_map *map; | ||
68 | struct msi_msg msg; | ||
69 | int rc; | ||
70 | |||
71 | map = zdev->msi_map + (nr & ZPCI_MSI_VEC_MASK); | ||
72 | map->irq = nr; | ||
73 | map->msi = msi; | ||
74 | INIT_HLIST_NODE(&map->msi_chain); | ||
75 | |||
76 | pr_debug("%s hashing irq: %u to bucket nr: %llu\n", | ||
77 | __func__, nr, msi_hashfn(nr)); | ||
78 | hlist_add_head_rcu(&map->msi_chain, &msi_hash[msi_hashfn(nr)]); | ||
79 | |||
80 | spin_lock(&msi_map_lock); | ||
81 | rc = irq_set_msi_desc(nr, msi); | ||
82 | if (rc) { | ||
83 | spin_unlock(&msi_map_lock); | ||
84 | hlist_del_rcu(&map->msi_chain); | ||
85 | return rc; | ||
86 | } | ||
87 | spin_unlock(&msi_map_lock); | ||
88 | |||
89 | msg.data = nr - offset; | ||
90 | msg.address_lo = zdev->msi_addr & 0xffffffff; | ||
91 | msg.address_hi = zdev->msi_addr >> 32; | ||
92 | write_msi_msg(nr, &msg); | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | void zpci_teardown_msi_irq(struct zpci_dev *zdev, struct msi_desc *msi) | ||
97 | { | ||
98 | int nr = msi->irq & ZPCI_MSI_VEC_MASK; | ||
99 | struct msi_map *map; | ||
100 | |||
101 | zpci_msi_set_mask_bits(msi, 1, 1); | ||
102 | msi->msg.address_lo = 0; | ||
103 | msi->msg.address_hi = 0; | ||
104 | msi->msg.data = 0; | ||
105 | msi->irq = 0; | ||
106 | |||
107 | spin_lock(&msi_map_lock); | ||
108 | map = zdev->msi_map + nr; | ||
109 | hlist_del_rcu(&map->msi_chain); | ||
110 | spin_unlock(&msi_map_lock); | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * The msi hash table has 256 entries which is good for 4..20 | ||
115 | * devices (a typical device allocates 10 + CPUs MSI's). Maybe make | ||
116 | * the hash table size adjustable later. | ||
117 | */ | ||
118 | int __init zpci_msihash_init(void) | ||
119 | { | ||
120 | unsigned int i; | ||
121 | |||
122 | msi_hash = kmalloc(MSI_HASH_BUCKETS * sizeof(*msi_hash), GFP_KERNEL); | ||
123 | if (!msi_hash) | ||
124 | return -ENOMEM; | ||
125 | |||
126 | for (i = 0; i < MSI_HASH_BUCKETS; i++) | ||
127 | INIT_HLIST_HEAD(&msi_hash[i]); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | void __init zpci_msihash_exit(void) | ||
132 | { | ||
133 | kfree(msi_hash); | ||
134 | } | ||
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 6ead6d076445..d028fd800c9c 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c | |||
@@ -81,17 +81,34 @@ void unregister_adapter_interrupt(struct airq_struct *airq) | |||
81 | } | 81 | } |
82 | EXPORT_SYMBOL(unregister_adapter_interrupt); | 82 | EXPORT_SYMBOL(unregister_adapter_interrupt); |
83 | 83 | ||
84 | void do_adapter_IO(u8 isc) | 84 | static irqreturn_t do_airq_interrupt(int irq, void *dummy) |
85 | { | 85 | { |
86 | struct tpi_info *tpi_info; | ||
86 | struct airq_struct *airq; | 87 | struct airq_struct *airq; |
87 | struct hlist_head *head; | 88 | struct hlist_head *head; |
88 | 89 | ||
89 | head = &airq_lists[isc]; | 90 | __this_cpu_write(s390_idle.nohz_delay, 1); |
91 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; | ||
92 | head = &airq_lists[tpi_info->isc]; | ||
90 | rcu_read_lock(); | 93 | rcu_read_lock(); |
91 | hlist_for_each_entry_rcu(airq, head, list) | 94 | hlist_for_each_entry_rcu(airq, head, list) |
92 | if ((*airq->lsi_ptr & airq->lsi_mask) != 0) | 95 | if ((*airq->lsi_ptr & airq->lsi_mask) != 0) |
93 | airq->handler(airq); | 96 | airq->handler(airq); |
94 | rcu_read_unlock(); | 97 | rcu_read_unlock(); |
98 | |||
99 | return IRQ_HANDLED; | ||
100 | } | ||
101 | |||
102 | static struct irqaction airq_interrupt = { | ||
103 | .name = "AIO", | ||
104 | .handler = do_airq_interrupt, | ||
105 | }; | ||
106 | |||
107 | void __init init_airq_interrupts(void) | ||
108 | { | ||
109 | irq_set_chip_and_handler(THIN_INTERRUPT, | ||
110 | &dummy_irq_chip, handle_percpu_irq); | ||
111 | setup_irq(THIN_INTERRUPT, &airq_interrupt); | ||
95 | } | 112 | } |
96 | 113 | ||
97 | /** | 114 | /** |
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 4eeb4a6bf207..d7da67a31c77 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
@@ -561,37 +561,23 @@ out: | |||
561 | } | 561 | } |
562 | 562 | ||
563 | /* | 563 | /* |
564 | * do_IRQ() handles all normal I/O device IRQ's (the special | 564 | * do_cio_interrupt() handles all normal I/O device IRQ's |
565 | * SMP cross-CPU interrupts have their own specific | ||
566 | * handlers). | ||
567 | * | ||
568 | */ | 565 | */ |
569 | void __irq_entry do_IRQ(struct pt_regs *regs) | 566 | static irqreturn_t do_cio_interrupt(int irq, void *dummy) |
570 | { | 567 | { |
571 | struct tpi_info *tpi_info = (struct tpi_info *) ®s->int_code; | 568 | struct tpi_info *tpi_info; |
572 | struct subchannel *sch; | 569 | struct subchannel *sch; |
573 | struct irb *irb; | 570 | struct irb *irb; |
574 | struct pt_regs *old_regs; | ||
575 | 571 | ||
576 | old_regs = set_irq_regs(regs); | ||
577 | irq_enter(); | ||
578 | __this_cpu_write(s390_idle.nohz_delay, 1); | 572 | __this_cpu_write(s390_idle.nohz_delay, 1); |
579 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) | 573 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; |
580 | /* Serve timer interrupts first. */ | ||
581 | clock_comparator_work(); | ||
582 | |||
583 | kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); | ||
584 | irb = (struct irb *) &S390_lowcore.irb; | 574 | irb = (struct irb *) &S390_lowcore.irb; |
585 | if (tpi_info->adapter_IO) { | ||
586 | do_adapter_IO(tpi_info->isc); | ||
587 | goto out; | ||
588 | } | ||
589 | sch = (struct subchannel *)(unsigned long) tpi_info->intparm; | 575 | sch = (struct subchannel *)(unsigned long) tpi_info->intparm; |
590 | if (!sch) { | 576 | if (!sch) { |
591 | /* Clear pending interrupt condition. */ | 577 | /* Clear pending interrupt condition. */ |
592 | inc_irq_stat(IRQIO_CIO); | 578 | inc_irq_stat(IRQIO_CIO); |
593 | tsch(tpi_info->schid, irb); | 579 | tsch(tpi_info->schid, irb); |
594 | goto out; | 580 | return IRQ_HANDLED; |
595 | } | 581 | } |
596 | spin_lock(sch->lock); | 582 | spin_lock(sch->lock); |
597 | /* Store interrupt response block to lowcore. */ | 583 | /* Store interrupt response block to lowcore. */ |
@@ -606,9 +592,23 @@ void __irq_entry do_IRQ(struct pt_regs *regs) | |||
606 | } else | 592 | } else |
607 | inc_irq_stat(IRQIO_CIO); | 593 | inc_irq_stat(IRQIO_CIO); |
608 | spin_unlock(sch->lock); | 594 | spin_unlock(sch->lock); |
609 | out: | 595 | |
610 | irq_exit(); | 596 | return IRQ_HANDLED; |
611 | set_irq_regs(old_regs); | 597 | } |
598 | |||
599 | static struct irq_desc *irq_desc_io; | ||
600 | |||
601 | static struct irqaction io_interrupt = { | ||
602 | .name = "IO", | ||
603 | .handler = do_cio_interrupt, | ||
604 | }; | ||
605 | |||
606 | void __init init_cio_interrupts(void) | ||
607 | { | ||
608 | irq_set_chip_and_handler(IO_INTERRUPT, | ||
609 | &dummy_irq_chip, handle_percpu_irq); | ||
610 | setup_irq(IO_INTERRUPT, &io_interrupt); | ||
611 | irq_desc_io = irq_to_desc(IO_INTERRUPT); | ||
612 | } | 612 | } |
613 | 613 | ||
614 | #ifdef CONFIG_CCW_CONSOLE | 614 | #ifdef CONFIG_CCW_CONSOLE |
@@ -635,7 +635,7 @@ void cio_tsch(struct subchannel *sch) | |||
635 | local_bh_disable(); | 635 | local_bh_disable(); |
636 | irq_enter(); | 636 | irq_enter(); |
637 | } | 637 | } |
638 | kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); | 638 | kstat_incr_irqs_this_cpu(IO_INTERRUPT, irq_desc_io); |
639 | if (sch->driver && sch->driver->irq) | 639 | if (sch->driver && sch->driver->irq) |
640 | sch->driver->irq(sch); | 640 | sch->driver->irq(sch); |
641 | else | 641 | else |
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index d62f5e7f3cf1..d42f67412bd8 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h | |||
@@ -121,9 +121,6 @@ extern int cio_commit_config(struct subchannel *sch); | |||
121 | int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key); | 121 | int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key); |
122 | int cio_tm_intrg(struct subchannel *sch); | 122 | int cio_tm_intrg(struct subchannel *sch); |
123 | 123 | ||
124 | void do_adapter_IO(u8 isc); | ||
125 | void do_IRQ(struct pt_regs *); | ||
126 | |||
127 | /* Use with care. */ | 124 | /* Use with care. */ |
128 | #ifdef CONFIG_CCW_CONSOLE | 125 | #ifdef CONFIG_CCW_CONSOLE |
129 | extern struct subchannel *cio_probe_console(void); | 126 | extern struct subchannel *cio_probe_console(void); |