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