1/*
2 *    Support for adapter interruptions
3 *
4 *    Copyright IBM Corp. 1999, 2007
5 *    Author(s): Ingo Adlung <adlung@de.ibm.com>
6 *		 Cornelia Huck <cornelia.huck@de.ibm.com>
7 *		 Arnd Bergmann <arndb@de.ibm.com>
8 *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
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
19#include <asm/airq.h>
20#include <asm/isc.h>
21
22#include "cio.h"
23#include "cio_debug.h"
24#include "ioasm.h"
25
26static DEFINE_SPINLOCK(airq_lists_lock);
27static struct hlist_head airq_lists[MAX_ISC+1];
28
29/**
30 * register_adapter_interrupt() - register adapter interrupt handler
31 * @airq: pointer to adapter interrupt descriptor
32 *
33 * Returns 0 on success, or -EINVAL.
34 */
35int register_adapter_interrupt(struct airq_struct *airq)
36{
37	char dbf_txt[32];
38
39	if (!airq->handler || airq->isc > MAX_ISC)
40		return -EINVAL;
41	if (!airq->lsi_ptr) {
42		airq->lsi_ptr = kzalloc(1, GFP_KERNEL);
43		if (!airq->lsi_ptr)
44			return -ENOMEM;
45		airq->flags |= AIRQ_PTR_ALLOCATED;
46	}
47	if (!airq->lsi_mask)
48		airq->lsi_mask = 0xff;
49	snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq);
50	CIO_TRACE_EVENT(4, dbf_txt);
51	isc_register(airq->isc);
52	spin_lock(&airq_lists_lock);
53	hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]);
54	spin_unlock(&airq_lists_lock);
55	return 0;
56}
57EXPORT_SYMBOL(register_adapter_interrupt);
58
59/**
60 * unregister_adapter_interrupt - unregister adapter interrupt handler
61 * @airq: pointer to adapter interrupt descriptor
62 */
63void unregister_adapter_interrupt(struct airq_struct *airq)
64{
65	char dbf_txt[32];
66
67	if (hlist_unhashed(&airq->list))
68		return;
69	snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq);
70	CIO_TRACE_EVENT(4, dbf_txt);
71	spin_lock(&airq_lists_lock);
72	hlist_del_rcu(&airq->list);
73	spin_unlock(&airq_lists_lock);
74	synchronize_rcu();
75	isc_unregister(airq->isc);
76	if (airq->flags & AIRQ_PTR_ALLOCATED) {
77		kfree(airq->lsi_ptr);
78		airq->lsi_ptr = NULL;
79		airq->flags &= ~AIRQ_PTR_ALLOCATED;
80	}
81}
82EXPORT_SYMBOL(unregister_adapter_interrupt);
83
84static irqreturn_t do_airq_interrupt(int irq, void *dummy)
85{
86	struct tpi_info *tpi_info;
87	struct airq_struct *airq;
88	struct hlist_head *head;
89
90	set_cpu_flag(CIF_NOHZ_DELAY);
91	tpi_info = (struct tpi_info *) &get_irq_regs()->int_code;
92	head = &airq_lists[tpi_info->isc];
93	rcu_read_lock();
94	hlist_for_each_entry_rcu(airq, head, list)
95		if ((*airq->lsi_ptr & airq->lsi_mask) != 0)
96			airq->handler(airq);
97	rcu_read_unlock();
98
99	return IRQ_HANDLED;
100}
101
102static struct irqaction airq_interrupt = {
103	.name	 = "AIO",
104	.handler = do_airq_interrupt,
105};
106
107void __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);
112}
113
114/**
115 * airq_iv_create - create an interrupt vector
116 * @bits: number of bits in the interrupt vector
117 * @flags: allocation flags
118 *
119 * Returns a pointer to an interrupt vector structure
120 */
121struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags)
122{
123	struct airq_iv *iv;
124	unsigned long size;
125
126	iv = kzalloc(sizeof(*iv), GFP_KERNEL);
127	if (!iv)
128		goto out;
129	iv->bits = bits;
130	size = BITS_TO_LONGS(bits) * sizeof(unsigned long);
131	iv->vector = kzalloc(size, GFP_KERNEL);
132	if (!iv->vector)
133		goto out_free;
134	if (flags & AIRQ_IV_ALLOC) {
135		iv->avail = kmalloc(size, GFP_KERNEL);
136		if (!iv->avail)
137			goto out_free;
138		memset(iv->avail, 0xff, size);
139		iv->end = 0;
140	} else
141		iv->end = bits;
142	if (flags & AIRQ_IV_BITLOCK) {
143		iv->bitlock = kzalloc(size, GFP_KERNEL);
144		if (!iv->bitlock)
145			goto out_free;
146	}
147	if (flags & AIRQ_IV_PTR) {
148		size = bits * sizeof(unsigned long);
149		iv->ptr = kzalloc(size, GFP_KERNEL);
150		if (!iv->ptr)
151			goto out_free;
152	}
153	if (flags & AIRQ_IV_DATA) {
154		size = bits * sizeof(unsigned int);
155		iv->data = kzalloc(size, GFP_KERNEL);
156		if (!iv->data)
157			goto out_free;
158	}
159	spin_lock_init(&iv->lock);
160	return iv;
161
162out_free:
163	kfree(iv->ptr);
164	kfree(iv->bitlock);
165	kfree(iv->avail);
166	kfree(iv->vector);
167	kfree(iv);
168out:
169	return NULL;
170}
171EXPORT_SYMBOL(airq_iv_create);
172
173/**
174 * airq_iv_release - release an interrupt vector
175 * @iv: pointer to interrupt vector structure
176 */
177void airq_iv_release(struct airq_iv *iv)
178{
179	kfree(iv->data);
180	kfree(iv->ptr);
181	kfree(iv->bitlock);
182	kfree(iv->vector);
183	kfree(iv->avail);
184	kfree(iv);
185}
186EXPORT_SYMBOL(airq_iv_release);
187
188/**
189 * airq_iv_alloc - allocate irq bits from an interrupt vector
190 * @iv: pointer to an interrupt vector structure
191 * @num: number of consecutive irq bits to allocate
192 *
193 * Returns the bit number of the first irq in the allocated block of irqs,
194 * or -1UL if no bit is available or the AIRQ_IV_ALLOC flag has not been
195 * specified
196 */
197unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num)
198{
199	unsigned long bit, i, flags;
200
201	if (!iv->avail || num == 0)
202		return -1UL;
203	spin_lock_irqsave(&iv->lock, flags);
204	bit = find_first_bit_inv(iv->avail, iv->bits);
205	while (bit + num <= iv->bits) {
206		for (i = 1; i < num; i++)
207			if (!test_bit_inv(bit + i, iv->avail))
208				break;
209		if (i >= num) {
210			/* Found a suitable block of irqs */
211			for (i = 0; i < num; i++)
212				clear_bit_inv(bit + i, iv->avail);
213			if (bit + num >= iv->end)
214				iv->end = bit + num + 1;
215			break;
216		}
217		bit = find_next_bit_inv(iv->avail, iv->bits, bit + i + 1);
218	}
219	if (bit + num > iv->bits)
220		bit = -1UL;
221	spin_unlock_irqrestore(&iv->lock, flags);
222	return bit;
223}
224EXPORT_SYMBOL(airq_iv_alloc);
225
226/**
227 * airq_iv_free - free irq bits of an interrupt vector
228 * @iv: pointer to interrupt vector structure
229 * @bit: number of the first irq bit to free
230 * @num: number of consecutive irq bits to free
231 */
232void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num)
233{
234	unsigned long i, flags;
235
236	if (!iv->avail || num == 0)
237		return;
238	spin_lock_irqsave(&iv->lock, flags);
239	for (i = 0; i < num; i++) {
240		/* Clear (possibly left over) interrupt bit */
241		clear_bit_inv(bit + i, iv->vector);
242		/* Make the bit positions available again */
243		set_bit_inv(bit + i, iv->avail);
244	}
245	if (bit + num >= iv->end) {
246		/* Find new end of bit-field */
247		while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail))
248			iv->end--;
249	}
250	spin_unlock_irqrestore(&iv->lock, flags);
251}
252EXPORT_SYMBOL(airq_iv_free);
253
254/**
255 * airq_iv_scan - scan interrupt vector for non-zero bits
256 * @iv: pointer to interrupt vector structure
257 * @start: bit number to start the search
258 * @end: bit number to end the search
259 *
260 * Returns the bit number of the next non-zero interrupt bit, or
261 * -1UL if the scan completed without finding any more any non-zero bits.
262 */
263unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start,
264			   unsigned long end)
265{
266	unsigned long bit;
267
268	/* Find non-zero bit starting from 'ivs->next'. */
269	bit = find_next_bit_inv(iv->vector, end, start);
270	if (bit >= end)
271		return -1UL;
272	clear_bit_inv(bit, iv->vector);
273	return bit;
274}
275EXPORT_SYMBOL(airq_iv_scan);
276