1/*
2 * linux/drivers/s390/cio/thinint_qdio.c
3 *
4 * Copyright 2000,2009 IBM Corp.
5 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
6 *	      Cornelia Huck <cornelia.huck@de.ibm.com>
7 *	      Jan Glauber <jang@linux.vnet.ibm.com>
8 */
9#include <linux/io.h>
10#include <linux/slab.h>
11#include <linux/kernel_stat.h>
12#include <linux/atomic.h>
13#include <asm/debug.h>
14#include <asm/qdio.h>
15#include <asm/airq.h>
16#include <asm/isc.h>
17
18#include "cio.h"
19#include "ioasm.h"
20#include "qdio.h"
21#include "qdio_debug.h"
22
23/*
24 * Restriction: only 63 iqdio subchannels would have its own indicator,
25 * after that, subsequent subchannels share one indicator
26 */
27#define TIQDIO_NR_NONSHARED_IND		63
28#define TIQDIO_NR_INDICATORS		(TIQDIO_NR_NONSHARED_IND + 1)
29#define TIQDIO_SHARED_IND		63
30
31/* device state change indicators */
32struct indicator_t {
33	u32 ind;	/* u32 because of compare-and-swap performance */
34	atomic_t count; /* use count, 0 or 1 for non-shared indicators */
35};
36
37/* list of thin interrupt input queues */
38static LIST_HEAD(tiq_list);
39static DEFINE_MUTEX(tiq_list_lock);
40
41/* adapter local summary indicator */
42static u8 *tiqdio_alsi;
43
44static struct indicator_t *q_indicators;
45
46u64 last_ai_time;
47
48/* returns addr for the device state change indicator */
49static u32 *get_indicator(void)
50{
51	int i;
52
53	for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++)
54		if (!atomic_read(&q_indicators[i].count)) {
55			atomic_set(&q_indicators[i].count, 1);
56			return &q_indicators[i].ind;
57		}
58
59	/* use the shared indicator */
60	atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count);
61	return &q_indicators[TIQDIO_SHARED_IND].ind;
62}
63
64static void put_indicator(u32 *addr)
65{
66	int i;
67
68	if (!addr)
69		return;
70	i = ((unsigned long)addr - (unsigned long)q_indicators) /
71		sizeof(struct indicator_t);
72	atomic_dec(&q_indicators[i].count);
73}
74
75void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
76{
77	mutex_lock(&tiq_list_lock);
78	BUG_ON(irq_ptr->nr_input_qs < 1);
79	list_add_rcu(&irq_ptr->input_qs[0]->entry, &tiq_list);
80	mutex_unlock(&tiq_list_lock);
81	xchg(irq_ptr->dsci, 1 << 7);
82}
83
84void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
85{
86	struct qdio_q *q;
87
88	BUG_ON(irq_ptr->nr_input_qs < 1);
89	q = irq_ptr->input_qs[0];
90	/* if establish triggered an error */
91	if (!q || !q->entry.prev || !q->entry.next)
92		return;
93
94	mutex_lock(&tiq_list_lock);
95	list_del_rcu(&q->entry);
96	mutex_unlock(&tiq_list_lock);
97	synchronize_rcu();
98}
99
100static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr)
101{
102	return irq_ptr->nr_input_qs > 1;
103}
104
105static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
106{
107	return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
108}
109
110static inline int shared_ind(struct qdio_irq *irq_ptr)
111{
112	return references_shared_dsci(irq_ptr) ||
113		has_multiple_inq_on_dsci(irq_ptr);
114}
115
116void clear_nonshared_ind(struct qdio_irq *irq_ptr)
117{
118	if (!is_thinint_irq(irq_ptr))
119		return;
120	if (shared_ind(irq_ptr))
121		return;
122	xchg(irq_ptr->dsci, 0);
123}
124
125int test_nonshared_ind(struct qdio_irq *irq_ptr)
126{
127	if (!is_thinint_irq(irq_ptr))
128		return 0;
129	if (shared_ind(irq_ptr))
130		return 0;
131	if (*irq_ptr->dsci)
132		return 1;
133	else
134		return 0;
135}
136
137static inline u32 clear_shared_ind(void)
138{
139	if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
140		return 0;
141	return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
142}
143
144static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
145{
146	struct qdio_q *q;
147	int i;
148
149	for_each_input_queue(irq, q, i) {
150		if (!references_shared_dsci(irq) &&
151		    has_multiple_inq_on_dsci(irq))
152			xchg(q->irq_ptr->dsci, 0);
153
154		if (q->u.in.queue_start_poll) {
155			/* skip if polling is enabled or already in work */
156			if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
157					     &q->u.in.queue_irq_state)) {
158				qperf_inc(q, int_discarded);
159				continue;
160			}
161
162			/* avoid dsci clear here, done after processing */
163			q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
164						 q->irq_ptr->int_parm);
165		} else {
166			if (!shared_ind(q->irq_ptr))
167				xchg(q->irq_ptr->dsci, 0);
168
169			/*
170			 * Call inbound processing but not directly
171			 * since that could starve other thinint queues.
172			 */
173			tasklet_schedule(&q->tasklet);
174		}
175	}
176}
177
178/**
179 * tiqdio_thinint_handler - thin interrupt handler for qdio
180 * @alsi: pointer to adapter local summary indicator
181 * @data: NULL
182 */
183static void tiqdio_thinint_handler(void *alsi, void *data)
184{
185	u32 si_used = clear_shared_ind();
186	struct qdio_q *q;
187
188	last_ai_time = S390_lowcore.int_clock;
189	kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++;
190
191	/* protect tiq_list entries, only changed in activate or shutdown */
192	rcu_read_lock();
193
194	/* check for work on all inbound thinint queues */
195	list_for_each_entry_rcu(q, &tiq_list, entry) {
196		struct qdio_irq *irq;
197
198		/* only process queues from changed sets */
199		irq = q->irq_ptr;
200		if (unlikely(references_shared_dsci(irq))) {
201			if (!si_used)
202				continue;
203		} else if (!*irq->dsci)
204			continue;
205
206		tiqdio_call_inq_handlers(irq);
207
208		qperf_inc(q, adapter_int);
209	}
210	rcu_read_unlock();
211}
212
213static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
214{
215	struct scssc_area *scssc_area;
216	int rc;
217
218	scssc_area = (struct scssc_area *)irq_ptr->chsc_page;
219	memset(scssc_area, 0, PAGE_SIZE);
220
221	if (reset) {
222		scssc_area->summary_indicator_addr = 0;
223		scssc_area->subchannel_indicator_addr = 0;
224	} else {
225		scssc_area->summary_indicator_addr = virt_to_phys(tiqdio_alsi);
226		scssc_area->subchannel_indicator_addr =
227			virt_to_phys(irq_ptr->dsci);
228	}
229
230	scssc_area->request = (struct chsc_header) {
231		.length = 0x0fe0,
232		.code	= 0x0021,
233	};
234	scssc_area->operation_code = 0;
235	scssc_area->ks = PAGE_DEFAULT_KEY >> 4;
236	scssc_area->kc = PAGE_DEFAULT_KEY >> 4;
237	scssc_area->isc = QDIO_AIRQ_ISC;
238	scssc_area->schid = irq_ptr->schid;
239
240	/* enable the time delay disablement facility */
241	if (css_general_characteristics.aif_tdd)
242		scssc_area->word_with_d_bit = 0x10000000;
243
244	rc = chsc(scssc_area);
245	if (rc)
246		return -EIO;
247
248	rc = chsc_error_from_response(scssc_area->response.code);
249	if (rc) {
250		DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
251			  scssc_area->response.code);
252		DBF_ERROR_HEX(&scssc_area->response, sizeof(void *));
253		return rc;
254	}
255
256	DBF_EVENT("setscind");
257	DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long));
258	DBF_HEX(&scssc_area->subchannel_indicator_addr,	sizeof(unsigned long));
259	return 0;
260}
261
262/* allocate non-shared indicators and shared indicator */
263int __init tiqdio_allocate_memory(void)
264{
265	q_indicators = kzalloc(sizeof(struct indicator_t) * TIQDIO_NR_INDICATORS,
266			     GFP_KERNEL);
267	if (!q_indicators)
268		return -ENOMEM;
269	return 0;
270}
271
272void tiqdio_free_memory(void)
273{
274	kfree(q_indicators);
275}
276
277int __init tiqdio_register_thinints(void)
278{
279	isc_register(QDIO_AIRQ_ISC);
280	tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler,
281						      NULL, QDIO_AIRQ_ISC);
282	if (IS_ERR(tiqdio_alsi)) {
283		DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi));
284		tiqdio_alsi = NULL;
285		isc_unregister(QDIO_AIRQ_ISC);
286		return -ENOMEM;
287	}
288	return 0;
289}
290
291int qdio_establish_thinint(struct qdio_irq *irq_ptr)
292{
293	if (!is_thinint_irq(irq_ptr))
294		return 0;
295	return set_subchannel_ind(irq_ptr, 0);
296}
297
298void qdio_setup_thinint(struct qdio_irq *irq_ptr)
299{
300	if (!is_thinint_irq(irq_ptr))
301		return;
302	irq_ptr->dsci = get_indicator();
303	DBF_HEX(&irq_ptr->dsci, sizeof(void *));
304}
305
306void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
307{
308	if (!is_thinint_irq(irq_ptr))
309		return;
310
311	/* reset adapter interrupt indicators */
312	set_subchannel_ind(irq_ptr, 1);
313	put_indicator(irq_ptr->dsci);
314}
315
316void __exit tiqdio_unregister_thinints(void)
317{
318	WARN_ON(!list_empty(&tiq_list));
319
320	if (tiqdio_alsi) {
321		s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC);
322		isc_unregister(QDIO_AIRQ_ISC);
323	}
324}
325