]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * drivers/s390/cio/airq.c | |
4e8e56c6 | 3 | * Support for adapter interruptions |
1da177e4 | 4 | * |
4e8e56c6 PO |
5 | * Copyright IBM Corp. 1999,2007 |
6 | * Author(s): Ingo Adlung <adlung@de.ibm.com> | |
7 | * Cornelia Huck <cornelia.huck@de.ibm.com> | |
8 | * Arnd Bergmann <arndb@de.ibm.com> | |
9 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | |
1da177e4 LT |
10 | */ |
11 | ||
12 | #include <linux/init.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/rcupdate.h> | |
16 | ||
4e8e56c6 PO |
17 | #include <asm/airq.h> |
18 | ||
19 | #include "cio.h" | |
1da177e4 | 20 | #include "cio_debug.h" |
1da177e4 | 21 | |
4e8e56c6 PO |
22 | #define NR_AIRQS 32 |
23 | #define NR_AIRQS_PER_WORD sizeof(unsigned long) | |
24 | #define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) | |
1da177e4 | 25 | |
4e8e56c6 PO |
26 | union indicator_t { |
27 | unsigned long word[NR_AIRQ_WORDS]; | |
28 | unsigned char byte[NR_AIRQS]; | |
29 | } __attribute__((packed)); | |
1da177e4 | 30 | |
4e8e56c6 PO |
31 | struct airq_t { |
32 | adapter_int_handler_t handler; | |
33 | void *drv_data; | |
34 | }; | |
1da177e4 | 35 | |
4e8e56c6 PO |
36 | static union indicator_t indicators; |
37 | static struct airq_t *airqs[NR_AIRQS]; | |
1da177e4 | 38 | |
4e8e56c6 PO |
39 | static int register_airq(struct airq_t *airq) |
40 | { | |
41 | int i; | |
1da177e4 | 42 | |
4e8e56c6 PO |
43 | for (i = 0; i < NR_AIRQS; i++) |
44 | if (!cmpxchg(&airqs[i], NULL, airq)) | |
45 | return i; | |
46 | return -ENOMEM; | |
1da177e4 LT |
47 | } |
48 | ||
4e8e56c6 PO |
49 | /** |
50 | * s390_register_adapter_interrupt() - register adapter interrupt handler | |
51 | * @handler: adapter handler to be registered | |
52 | * @drv_data: driver data passed with each call to the handler | |
53 | * | |
54 | * Returns: | |
55 | * Pointer to the indicator to be used on success | |
56 | * ERR_PTR() if registration failed | |
57 | */ | |
58 | void *s390_register_adapter_interrupt(adapter_int_handler_t handler, | |
59 | void *drv_data) | |
1da177e4 | 60 | { |
4e8e56c6 PO |
61 | struct airq_t *airq; |
62 | char dbf_txt[16]; | |
1da177e4 | 63 | int ret; |
1da177e4 | 64 | |
4e8e56c6 PO |
65 | airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); |
66 | if (!airq) { | |
67 | ret = -ENOMEM; | |
68 | goto out; | |
1da177e4 | 69 | } |
4e8e56c6 PO |
70 | airq->handler = handler; |
71 | airq->drv_data = drv_data; | |
72 | ret = register_airq(airq); | |
73 | if (ret < 0) | |
74 | kfree(airq); | |
75 | out: | |
76 | snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); | |
77 | CIO_TRACE_EVENT(4, dbf_txt); | |
78 | if (ret < 0) | |
79 | return ERR_PTR(ret); | |
80 | else | |
81 | return &indicators.byte[ret]; | |
1da177e4 | 82 | } |
4e8e56c6 | 83 | EXPORT_SYMBOL(s390_register_adapter_interrupt); |
1da177e4 | 84 | |
4e8e56c6 PO |
85 | /** |
86 | * s390_unregister_adapter_interrupt - unregister adapter interrupt handler | |
87 | * @ind: indicator for which the handler is to be unregistered | |
88 | */ | |
89 | void s390_unregister_adapter_interrupt(void *ind) | |
1da177e4 | 90 | { |
4e8e56c6 PO |
91 | struct airq_t *airq; |
92 | char dbf_txt[16]; | |
93 | int i; | |
1da177e4 | 94 | |
4e8e56c6 PO |
95 | i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]); |
96 | snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); | |
97 | CIO_TRACE_EVENT(4, dbf_txt); | |
98 | indicators.byte[i] = 0; | |
99 | airq = xchg(&airqs[i], NULL); | |
100 | /* | |
101 | * Allow interrupts to complete. This will ensure that the airq handle | |
102 | * is no longer referenced by any interrupt handler. | |
103 | */ | |
104 | synchronize_sched(); | |
105 | kfree(airq); | |
1da177e4 | 106 | } |
4e8e56c6 PO |
107 | EXPORT_SYMBOL(s390_unregister_adapter_interrupt); |
108 | ||
109 | #define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) | |
1da177e4 | 110 | |
4e8e56c6 PO |
111 | void do_adapter_IO(void) |
112 | { | |
113 | int w; | |
114 | int i; | |
115 | unsigned long word; | |
116 | struct airq_t *airq; | |
117 | ||
118 | /* | |
119 | * Access indicator array in word-sized chunks to minimize storage | |
120 | * fetch operations. | |
121 | */ | |
122 | for (w = 0; w < NR_AIRQ_WORDS; w++) { | |
123 | word = indicators.word[w]; | |
124 | i = w * NR_AIRQS_PER_WORD; | |
125 | /* | |
126 | * Check bytes within word for active indicators. | |
127 | */ | |
128 | while (word) { | |
129 | if (word & INDICATOR_MASK) { | |
130 | airq = airqs[i]; | |
131 | if (likely(airq)) | |
132 | airq->handler(&indicators.byte[i], | |
133 | airq->drv_data); | |
134 | else | |
135 | /* | |
136 | * Reset ill-behaved indicator. | |
137 | */ | |
138 | indicators.byte[i] = 0; | |
139 | } | |
140 | word <<= 8; | |
141 | i++; | |
142 | } | |
143 | } | |
144 | } |