1
2
3
4
5
6
7
8
9
10
11#include <linux/init.h>
12#include <linux/irq.h>
13#include <linux/kernel_stat.h>
14#include <linux/module.h>
15#include <linux/mutex.h>
16#include <linux/rculist.h>
17#include <linux/slab.h>
18#include <linux/dma-mapping.h>
19
20#include <asm/airq.h>
21#include <asm/isc.h>
22#include <asm/cio.h>
23
24#include "cio.h"
25#include "cio_debug.h"
26#include "ioasm.h"
27
28static DEFINE_SPINLOCK(airq_lists_lock);
29static struct hlist_head airq_lists[MAX_ISC+1];
30
31
32
33
34
35
36
37int register_adapter_interrupt(struct airq_struct *airq)
38{
39 char dbf_txt[32];
40
41 if (!airq->handler || airq->isc > MAX_ISC)
42 return -EINVAL;
43 if (!airq->lsi_ptr) {
44 airq->lsi_ptr = kzalloc(1, GFP_KERNEL);
45 if (!airq->lsi_ptr)
46 return -ENOMEM;
47 airq->flags |= AIRQ_PTR_ALLOCATED;
48 }
49 if (!airq->lsi_mask)
50 airq->lsi_mask = 0xff;
51 snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq);
52 CIO_TRACE_EVENT(4, dbf_txt);
53 isc_register(airq->isc);
54 spin_lock(&airq_lists_lock);
55 hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]);
56 spin_unlock(&airq_lists_lock);
57 return 0;
58}
59EXPORT_SYMBOL(register_adapter_interrupt);
60
61
62
63
64
65void unregister_adapter_interrupt(struct airq_struct *airq)
66{
67 char dbf_txt[32];
68
69 if (hlist_unhashed(&airq->list))
70 return;
71 snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq);
72 CIO_TRACE_EVENT(4, dbf_txt);
73 spin_lock(&airq_lists_lock);
74 hlist_del_rcu(&airq->list);
75 spin_unlock(&airq_lists_lock);
76 synchronize_rcu();
77 isc_unregister(airq->isc);
78 if (airq->flags & AIRQ_PTR_ALLOCATED) {
79 kfree(airq->lsi_ptr);
80 airq->lsi_ptr = NULL;
81 airq->flags &= ~AIRQ_PTR_ALLOCATED;
82 }
83}
84EXPORT_SYMBOL(unregister_adapter_interrupt);
85
86void do_adapter_IO(u8 isc)
87{
88 struct airq_struct *airq;
89 struct hlist_head *head;
90
91 head = &airq_lists[isc];
92 rcu_read_lock();
93 hlist_for_each_entry_rcu(airq, head, list)
94 if ((*airq->lsi_ptr & airq->lsi_mask) != 0)
95 airq->handler(airq);
96 rcu_read_unlock();
97}
98
99static inline unsigned long iv_size(unsigned long bits)
100{
101 return BITS_TO_LONGS(bits) * sizeof(unsigned long);
102}
103
104
105
106
107
108
109
110
111struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags)
112{
113 struct airq_iv *iv;
114 unsigned long size;
115
116 iv = kzalloc(sizeof(*iv), GFP_KERNEL);
117 if (!iv)
118 goto out;
119 iv->bits = bits;
120 size = iv_size(bits);
121 iv->vector = cio_dma_zalloc(size);
122 if (!iv->vector)
123 goto out_free;
124 if (flags & AIRQ_IV_ALLOC) {
125 iv->avail = kmalloc(size, GFP_KERNEL);
126 if (!iv->avail)
127 goto out_free;
128 memset(iv->avail, 0xff, size);
129 iv->end = 0;
130 } else
131 iv->end = bits;
132 if (flags & AIRQ_IV_BITLOCK) {
133 iv->bitlock = kzalloc(size, GFP_KERNEL);
134 if (!iv->bitlock)
135 goto out_free;
136 }
137 if (flags & AIRQ_IV_PTR) {
138 size = bits * sizeof(unsigned long);
139 iv->ptr = kzalloc(size, GFP_KERNEL);
140 if (!iv->ptr)
141 goto out_free;
142 }
143 if (flags & AIRQ_IV_DATA) {
144 size = bits * sizeof(unsigned int);
145 iv->data = kzalloc(size, GFP_KERNEL);
146 if (!iv->data)
147 goto out_free;
148 }
149 spin_lock_init(&iv->lock);
150 return iv;
151
152out_free:
153 kfree(iv->ptr);
154 kfree(iv->bitlock);
155 kfree(iv->avail);
156 cio_dma_free(iv->vector, size);
157 kfree(iv);
158out:
159 return NULL;
160}
161EXPORT_SYMBOL(airq_iv_create);
162
163
164
165
166
167void airq_iv_release(struct airq_iv *iv)
168{
169 kfree(iv->data);
170 kfree(iv->ptr);
171 kfree(iv->bitlock);
172 cio_dma_free(iv->vector, iv_size(iv->bits));
173 kfree(iv->avail);
174 kfree(iv);
175}
176EXPORT_SYMBOL(airq_iv_release);
177
178
179
180
181
182
183
184
185
186
187unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num)
188{
189 unsigned long bit, i, flags;
190
191 if (!iv->avail || num == 0)
192 return -1UL;
193 spin_lock_irqsave(&iv->lock, flags);
194 bit = find_first_bit_inv(iv->avail, iv->bits);
195 while (bit + num <= iv->bits) {
196 for (i = 1; i < num; i++)
197 if (!test_bit_inv(bit + i, iv->avail))
198 break;
199 if (i >= num) {
200
201 for (i = 0; i < num; i++)
202 clear_bit_inv(bit + i, iv->avail);
203 if (bit + num >= iv->end)
204 iv->end = bit + num + 1;
205 break;
206 }
207 bit = find_next_bit_inv(iv->avail, iv->bits, bit + i + 1);
208 }
209 if (bit + num > iv->bits)
210 bit = -1UL;
211 spin_unlock_irqrestore(&iv->lock, flags);
212 return bit;
213}
214EXPORT_SYMBOL(airq_iv_alloc);
215
216
217
218
219
220
221
222void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num)
223{
224 unsigned long i, flags;
225
226 if (!iv->avail || num == 0)
227 return;
228 spin_lock_irqsave(&iv->lock, flags);
229 for (i = 0; i < num; i++) {
230
231 clear_bit_inv(bit + i, iv->vector);
232
233 set_bit_inv(bit + i, iv->avail);
234 }
235 if (bit + num >= iv->end) {
236
237 while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail))
238 iv->end--;
239 }
240 spin_unlock_irqrestore(&iv->lock, flags);
241}
242EXPORT_SYMBOL(airq_iv_free);
243
244
245
246
247
248
249
250
251
252
253unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start,
254 unsigned long end)
255{
256 unsigned long bit;
257
258
259 bit = find_next_bit_inv(iv->vector, end, start);
260 if (bit >= end)
261 return -1UL;
262 clear_bit_inv(bit, iv->vector);
263 return bit;
264}
265EXPORT_SYMBOL(airq_iv_scan);
266