xref: /linux/drivers/s390/cio/qdio_setup.c (revision 4f6b838c378a52ea3ae0b15f12ca8a20849072fa)
1724117b7SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2779e6e1cSJan Glauber /*
3779e6e1cSJan Glauber  * qdio queue initialization
4779e6e1cSJan Glauber  *
5a53c8fabSHeiko Carstens  * Copyright IBM Corp. 2008
6779e6e1cSJan Glauber  * Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
7779e6e1cSJan Glauber  */
8779e6e1cSJan Glauber #include <linux/kernel.h>
9779e6e1cSJan Glauber #include <linux/slab.h>
103a4c5d59SHeiko Carstens #include <linux/export.h>
11e9091ffdSJulian Wiedmann #include <linux/io.h>
12*180a4c42SJulian Wiedmann 
13*180a4c42SJulian Wiedmann #include <asm/ebcdic.h>
14779e6e1cSJan Glauber #include <asm/qdio.h>
15779e6e1cSJan Glauber 
16779e6e1cSJan Glauber #include "cio.h"
17779e6e1cSJan Glauber #include "css.h"
18779e6e1cSJan Glauber #include "device.h"
19779e6e1cSJan Glauber #include "ioasm.h"
20779e6e1cSJan Glauber #include "chsc.h"
21779e6e1cSJan Glauber #include "qdio.h"
22779e6e1cSJan Glauber #include "qdio_debug.h"
23779e6e1cSJan Glauber 
245245c924SSebastian Ott #define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
255245c924SSebastian Ott 
26779e6e1cSJan Glauber static struct kmem_cache *qdio_q_cache;
27104ea556Sfrank.blaschka@de.ibm.com static struct kmem_cache *qdio_aob_cache;
28104ea556Sfrank.blaschka@de.ibm.com 
29400d8291SHeiko Carstens struct qaob *qdio_allocate_aob(void)
30104ea556Sfrank.blaschka@de.ibm.com {
31400d8291SHeiko Carstens 	return kmem_cache_zalloc(qdio_aob_cache, GFP_ATOMIC);
32104ea556Sfrank.blaschka@de.ibm.com }
33104ea556Sfrank.blaschka@de.ibm.com 
34104ea556Sfrank.blaschka@de.ibm.com void qdio_release_aob(struct qaob *aob)
35104ea556Sfrank.blaschka@de.ibm.com {
36104ea556Sfrank.blaschka@de.ibm.com 	kmem_cache_free(qdio_aob_cache, aob);
37104ea556Sfrank.blaschka@de.ibm.com }
38104ea556Sfrank.blaschka@de.ibm.com EXPORT_SYMBOL_GPL(qdio_release_aob);
39779e6e1cSJan Glauber 
405245c924SSebastian Ott /**
415245c924SSebastian Ott  * qdio_free_buffers() - free qdio buffers
425245c924SSebastian Ott  * @buf: array of pointers to qdio buffers
435245c924SSebastian Ott  * @count: number of qdio buffers to free
445245c924SSebastian Ott  */
455245c924SSebastian Ott void qdio_free_buffers(struct qdio_buffer **buf, unsigned int count)
465245c924SSebastian Ott {
475245c924SSebastian Ott 	int pos;
485245c924SSebastian Ott 
495245c924SSebastian Ott 	for (pos = 0; pos < count; pos += QBUFF_PER_PAGE)
505245c924SSebastian Ott 		free_page((unsigned long) buf[pos]);
515245c924SSebastian Ott }
525245c924SSebastian Ott EXPORT_SYMBOL_GPL(qdio_free_buffers);
535245c924SSebastian Ott 
545245c924SSebastian Ott /**
555245c924SSebastian Ott  * qdio_alloc_buffers() - allocate qdio buffers
565245c924SSebastian Ott  * @buf: array of pointers to qdio buffers
575245c924SSebastian Ott  * @count: number of qdio buffers to allocate
585245c924SSebastian Ott  */
595245c924SSebastian Ott int qdio_alloc_buffers(struct qdio_buffer **buf, unsigned int count)
605245c924SSebastian Ott {
615245c924SSebastian Ott 	int pos;
625245c924SSebastian Ott 
635245c924SSebastian Ott 	for (pos = 0; pos < count; pos += QBUFF_PER_PAGE) {
645245c924SSebastian Ott 		buf[pos] = (void *) get_zeroed_page(GFP_KERNEL);
655245c924SSebastian Ott 		if (!buf[pos]) {
665245c924SSebastian Ott 			qdio_free_buffers(buf, count);
675245c924SSebastian Ott 			return -ENOMEM;
685245c924SSebastian Ott 		}
695245c924SSebastian Ott 	}
705245c924SSebastian Ott 	for (pos = 0; pos < count; pos++)
715245c924SSebastian Ott 		if (pos % QBUFF_PER_PAGE)
725245c924SSebastian Ott 			buf[pos] = buf[pos - 1] + 1;
735245c924SSebastian Ott 	return 0;
745245c924SSebastian Ott }
755245c924SSebastian Ott EXPORT_SYMBOL_GPL(qdio_alloc_buffers);
765245c924SSebastian Ott 
775245c924SSebastian Ott /**
785245c924SSebastian Ott  * qdio_reset_buffers() - reset qdio buffers
795245c924SSebastian Ott  * @buf: array of pointers to qdio buffers
805245c924SSebastian Ott  * @count: number of qdio buffers that will be zeroed
815245c924SSebastian Ott  */
825245c924SSebastian Ott void qdio_reset_buffers(struct qdio_buffer **buf, unsigned int count)
835245c924SSebastian Ott {
845245c924SSebastian Ott 	int pos;
855245c924SSebastian Ott 
865245c924SSebastian Ott 	for (pos = 0; pos < count; pos++)
875245c924SSebastian Ott 		memset(buf[pos], 0, sizeof(struct qdio_buffer));
885245c924SSebastian Ott }
895245c924SSebastian Ott EXPORT_SYMBOL_GPL(qdio_reset_buffers);
905245c924SSebastian Ott 
91779e6e1cSJan Glauber /*
92779e6e1cSJan Glauber  * qebsm is only available under 64bit but the adapter sets the feature
93779e6e1cSJan Glauber  * flag anyway, so we manually override it.
94779e6e1cSJan Glauber  */
95779e6e1cSJan Glauber static inline int qebsm_possible(void)
96779e6e1cSJan Glauber {
97779e6e1cSJan Glauber 	return css_general_characteristics.qebsm;
98779e6e1cSJan Glauber }
99779e6e1cSJan Glauber 
100779e6e1cSJan Glauber /*
101779e6e1cSJan Glauber  * qib_param_field: pointer to 128 bytes or NULL, if no param field
102779e6e1cSJan Glauber  * nr_input_qs: pointer to nr_queues*128 words of data or NULL
103779e6e1cSJan Glauber  */
104779e6e1cSJan Glauber static void set_impl_params(struct qdio_irq *irq_ptr,
105779e6e1cSJan Glauber 			    unsigned int qib_param_field_format,
106779e6e1cSJan Glauber 			    unsigned char *qib_param_field,
107779e6e1cSJan Glauber 			    unsigned long *input_slib_elements,
108779e6e1cSJan Glauber 			    unsigned long *output_slib_elements)
109779e6e1cSJan Glauber {
110779e6e1cSJan Glauber 	struct qdio_q *q;
111779e6e1cSJan Glauber 	int i, j;
112779e6e1cSJan Glauber 
113779e6e1cSJan Glauber 	if (!irq_ptr)
114779e6e1cSJan Glauber 		return;
115779e6e1cSJan Glauber 
116779e6e1cSJan Glauber 	irq_ptr->qib.pfmt = qib_param_field_format;
117779e6e1cSJan Glauber 	if (qib_param_field)
118779e6e1cSJan Glauber 		memcpy(irq_ptr->qib.parm, qib_param_field,
1199f494438SJulian Wiedmann 		       sizeof(irq_ptr->qib.parm));
120779e6e1cSJan Glauber 
121779e6e1cSJan Glauber 	if (!input_slib_elements)
122779e6e1cSJan Glauber 		goto output;
123779e6e1cSJan Glauber 
124779e6e1cSJan Glauber 	for_each_input_queue(irq_ptr, q, i) {
125779e6e1cSJan Glauber 		for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
126779e6e1cSJan Glauber 			q->slib->slibe[j].parms =
127779e6e1cSJan Glauber 				input_slib_elements[i * QDIO_MAX_BUFFERS_PER_Q + j];
128779e6e1cSJan Glauber 	}
129779e6e1cSJan Glauber output:
130779e6e1cSJan Glauber 	if (!output_slib_elements)
131779e6e1cSJan Glauber 		return;
132779e6e1cSJan Glauber 
133779e6e1cSJan Glauber 	for_each_output_queue(irq_ptr, q, i) {
134779e6e1cSJan Glauber 		for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
135779e6e1cSJan Glauber 			q->slib->slibe[j].parms =
136779e6e1cSJan Glauber 				output_slib_elements[i * QDIO_MAX_BUFFERS_PER_Q + j];
137779e6e1cSJan Glauber 	}
138779e6e1cSJan Glauber }
139779e6e1cSJan Glauber 
1402a7cf35cSJulian Wiedmann static void __qdio_free_queues(struct qdio_q **queues, unsigned int count)
1412a7cf35cSJulian Wiedmann {
1422a7cf35cSJulian Wiedmann 	struct qdio_q *q;
1432a7cf35cSJulian Wiedmann 	unsigned int i;
1442a7cf35cSJulian Wiedmann 
1452a7cf35cSJulian Wiedmann 	for (i = 0; i < count; i++) {
1462a7cf35cSJulian Wiedmann 		q = queues[i];
1472a7cf35cSJulian Wiedmann 		free_page((unsigned long) q->slib);
1482a7cf35cSJulian Wiedmann 		kmem_cache_free(qdio_q_cache, q);
1492a7cf35cSJulian Wiedmann 	}
1502a7cf35cSJulian Wiedmann }
1512a7cf35cSJulian Wiedmann 
152d188cac3SJulian Wiedmann void qdio_free_queues(struct qdio_irq *irq_ptr)
153d188cac3SJulian Wiedmann {
154d188cac3SJulian Wiedmann 	__qdio_free_queues(irq_ptr->input_qs, irq_ptr->max_input_qs);
155d188cac3SJulian Wiedmann 	irq_ptr->max_input_qs = 0;
156d188cac3SJulian Wiedmann 
157d188cac3SJulian Wiedmann 	__qdio_free_queues(irq_ptr->output_qs, irq_ptr->max_output_qs);
158d188cac3SJulian Wiedmann 	irq_ptr->max_output_qs = 0;
159d188cac3SJulian Wiedmann }
160d188cac3SJulian Wiedmann 
161779e6e1cSJan Glauber static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
162779e6e1cSJan Glauber {
163779e6e1cSJan Glauber 	struct qdio_q *q;
164779e6e1cSJan Glauber 	int i;
165779e6e1cSJan Glauber 
166779e6e1cSJan Glauber 	for (i = 0; i < nr_queues; i++) {
167e5218134SJulian Wiedmann 		q = kmem_cache_zalloc(qdio_q_cache, GFP_KERNEL);
1682a7cf35cSJulian Wiedmann 		if (!q) {
1692a7cf35cSJulian Wiedmann 			__qdio_free_queues(irq_ptr_qs, i);
170779e6e1cSJan Glauber 			return -ENOMEM;
1712a7cf35cSJulian Wiedmann 		}
172779e6e1cSJan Glauber 
173779e6e1cSJan Glauber 		q->slib = (struct slib *) __get_free_page(GFP_KERNEL);
174779e6e1cSJan Glauber 		if (!q->slib) {
175779e6e1cSJan Glauber 			kmem_cache_free(qdio_q_cache, q);
1762a7cf35cSJulian Wiedmann 			__qdio_free_queues(irq_ptr_qs, i);
177779e6e1cSJan Glauber 			return -ENOMEM;
178779e6e1cSJan Glauber 		}
179779e6e1cSJan Glauber 		irq_ptr_qs[i] = q;
180779e6e1cSJan Glauber 	}
181779e6e1cSJan Glauber 	return 0;
182779e6e1cSJan Glauber }
183779e6e1cSJan Glauber 
184779e6e1cSJan Glauber int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs, int nr_output_qs)
185779e6e1cSJan Glauber {
186779e6e1cSJan Glauber 	int rc;
187779e6e1cSJan Glauber 
188779e6e1cSJan Glauber 	rc = __qdio_allocate_qs(irq_ptr->input_qs, nr_input_qs);
189779e6e1cSJan Glauber 	if (rc)
190779e6e1cSJan Glauber 		return rc;
1912a7cf35cSJulian Wiedmann 
192779e6e1cSJan Glauber 	rc = __qdio_allocate_qs(irq_ptr->output_qs, nr_output_qs);
193d188cac3SJulian Wiedmann 	if (rc) {
1942a7cf35cSJulian Wiedmann 		__qdio_free_queues(irq_ptr->input_qs, nr_input_qs);
195779e6e1cSJan Glauber 		return rc;
196779e6e1cSJan Glauber 	}
197779e6e1cSJan Glauber 
198d188cac3SJulian Wiedmann 	irq_ptr->max_input_qs = nr_input_qs;
199d188cac3SJulian Wiedmann 	irq_ptr->max_output_qs = nr_output_qs;
200d188cac3SJulian Wiedmann 	return 0;
201d188cac3SJulian Wiedmann }
202d188cac3SJulian Wiedmann 
203779e6e1cSJan Glauber static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
204779e6e1cSJan Glauber 			      qdio_handler_t *handler, int i)
205779e6e1cSJan Glauber {
2065382fe11SJan Glauber 	struct slib *slib = q->slib;
207779e6e1cSJan Glauber 
2085382fe11SJan Glauber 	/* queue must be cleared for qdio_establish */
2095382fe11SJan Glauber 	memset(q, 0, sizeof(*q));
2105382fe11SJan Glauber 	memset(slib, 0, PAGE_SIZE);
2115382fe11SJan Glauber 	q->slib = slib;
212779e6e1cSJan Glauber 	q->irq_ptr = irq_ptr;
213779e6e1cSJan Glauber 	q->mask = 1 << (31 - i);
214779e6e1cSJan Glauber 	q->nr = i;
215779e6e1cSJan Glauber 	q->handler = handler;
216779e6e1cSJan Glauber }
217779e6e1cSJan Glauber 
218779e6e1cSJan Glauber static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
219bdf11767SJulian Wiedmann 				struct qdio_buffer **sbals_array, int i)
220779e6e1cSJan Glauber {
221779e6e1cSJan Glauber 	struct qdio_q *prev;
222779e6e1cSJan Glauber 	int j;
223779e6e1cSJan Glauber 
22422f99347SJan Glauber 	DBF_HEX(&q, sizeof(void *));
225779e6e1cSJan Glauber 	q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2);
226779e6e1cSJan Glauber 
227779e6e1cSJan Glauber 	/* fill in sbal */
228ce1d8014SJan Glauber 	for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
229779e6e1cSJan Glauber 		q->sbal[j] = *sbals_array++;
230779e6e1cSJan Glauber 
231779e6e1cSJan Glauber 	/* fill in slib */
232779e6e1cSJan Glauber 	if (i > 0) {
233779e6e1cSJan Glauber 		prev = (q->is_input_q) ? irq_ptr->input_qs[i - 1]
234779e6e1cSJan Glauber 			: irq_ptr->output_qs[i - 1];
235779e6e1cSJan Glauber 		prev->slib->nsliba = (unsigned long)q->slib;
236779e6e1cSJan Glauber 	}
237779e6e1cSJan Glauber 
238779e6e1cSJan Glauber 	q->slib->sla = (unsigned long)q->sl;
239779e6e1cSJan Glauber 	q->slib->slsba = (unsigned long)&q->slsb.val[0];
240779e6e1cSJan Glauber 
241779e6e1cSJan Glauber 	/* fill in sl */
242779e6e1cSJan Glauber 	for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
243e9091ffdSJulian Wiedmann 		q->sl->element[j].sbal = virt_to_phys(q->sbal[j]);
244779e6e1cSJan Glauber }
245779e6e1cSJan Glauber 
246779e6e1cSJan Glauber static void setup_queues(struct qdio_irq *irq_ptr,
247779e6e1cSJan Glauber 			 struct qdio_initialize *qdio_init)
248779e6e1cSJan Glauber {
249779e6e1cSJan Glauber 	struct qdio_q *q;
250104ea556Sfrank.blaschka@de.ibm.com 	struct qdio_outbuf_state *output_sbal_state_array =
251104ea556Sfrank.blaschka@de.ibm.com 				  qdio_init->output_sbal_state_array;
252779e6e1cSJan Glauber 	int i;
253779e6e1cSJan Glauber 
254779e6e1cSJan Glauber 	for_each_input_queue(irq_ptr, q, i) {
255104ea556Sfrank.blaschka@de.ibm.com 		DBF_EVENT("inq:%1d", i);
256779e6e1cSJan Glauber 		setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i);
257779e6e1cSJan Glauber 
258779e6e1cSJan Glauber 		q->is_input_q = 1;
259104ea556Sfrank.blaschka@de.ibm.com 
260d8564e19SJulian Wiedmann 		setup_storage_lists(q, irq_ptr,
261d8564e19SJulian Wiedmann 				    qdio_init->input_sbal_addr_array[i], i);
262779e6e1cSJan Glauber 
263104ea556Sfrank.blaschka@de.ibm.com 		if (is_thinint_irq(irq_ptr)) {
264779e6e1cSJan Glauber 			tasklet_init(&q->tasklet, tiqdio_inbound_processing,
265779e6e1cSJan Glauber 				     (unsigned long) q);
266104ea556Sfrank.blaschka@de.ibm.com 		} else {
267779e6e1cSJan Glauber 			tasklet_init(&q->tasklet, qdio_inbound_processing,
268779e6e1cSJan Glauber 				     (unsigned long) q);
269779e6e1cSJan Glauber 		}
270104ea556Sfrank.blaschka@de.ibm.com 	}
271779e6e1cSJan Glauber 
272779e6e1cSJan Glauber 	for_each_output_queue(irq_ptr, q, i) {
27322f99347SJan Glauber 		DBF_EVENT("outq:%1d", i);
274779e6e1cSJan Glauber 		setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i);
275779e6e1cSJan Glauber 
276104ea556Sfrank.blaschka@de.ibm.com 		q->u.out.sbal_state = output_sbal_state_array;
277104ea556Sfrank.blaschka@de.ibm.com 		output_sbal_state_array += QDIO_MAX_BUFFERS_PER_Q;
278104ea556Sfrank.blaschka@de.ibm.com 
279779e6e1cSJan Glauber 		q->is_input_q = 0;
280d8564e19SJulian Wiedmann 		setup_storage_lists(q, irq_ptr,
281d8564e19SJulian Wiedmann 				    qdio_init->output_sbal_addr_array[i], i);
282779e6e1cSJan Glauber 
283779e6e1cSJan Glauber 		tasklet_init(&q->tasklet, qdio_outbound_processing,
284779e6e1cSJan Glauber 			     (unsigned long) q);
285cb9f780aSKees Cook 		timer_setup(&q->u.out.timer, qdio_outbound_timer, 0);
286779e6e1cSJan Glauber 	}
287779e6e1cSJan Glauber }
288779e6e1cSJan Glauber 
289779e6e1cSJan Glauber static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac)
290779e6e1cSJan Glauber {
291779e6e1cSJan Glauber 	if (qdioac & AC1_SIGA_INPUT_NEEDED)
292779e6e1cSJan Glauber 		irq_ptr->siga_flag.input = 1;
293779e6e1cSJan Glauber 	if (qdioac & AC1_SIGA_OUTPUT_NEEDED)
294779e6e1cSJan Glauber 		irq_ptr->siga_flag.output = 1;
295779e6e1cSJan Glauber 	if (qdioac & AC1_SIGA_SYNC_NEEDED)
296779e6e1cSJan Glauber 		irq_ptr->siga_flag.sync = 1;
29790adac58SJan Glauber 	if (!(qdioac & AC1_AUTOMATIC_SYNC_ON_THININT))
29890adac58SJan Glauber 		irq_ptr->siga_flag.sync_after_ai = 1;
29990adac58SJan Glauber 	if (!(qdioac & AC1_AUTOMATIC_SYNC_ON_OUT_PCI))
30090adac58SJan Glauber 		irq_ptr->siga_flag.sync_out_after_pci = 1;
301779e6e1cSJan Glauber }
302779e6e1cSJan Glauber 
303779e6e1cSJan Glauber static void check_and_setup_qebsm(struct qdio_irq *irq_ptr,
304779e6e1cSJan Glauber 				  unsigned char qdioac, unsigned long token)
305779e6e1cSJan Glauber {
306779e6e1cSJan Glauber 	if (!(irq_ptr->qib.rflags & QIB_RFLAGS_ENABLE_QEBSM))
307779e6e1cSJan Glauber 		goto no_qebsm;
308779e6e1cSJan Glauber 	if (!(qdioac & AC1_SC_QEBSM_AVAILABLE) ||
309779e6e1cSJan Glauber 	    (!(qdioac & AC1_SC_QEBSM_ENABLED)))
310779e6e1cSJan Glauber 		goto no_qebsm;
311779e6e1cSJan Glauber 
312779e6e1cSJan Glauber 	irq_ptr->sch_token = token;
313779e6e1cSJan Glauber 
31422f99347SJan Glauber 	DBF_EVENT("V=V:1");
31522f99347SJan Glauber 	DBF_EVENT("%8lx", irq_ptr->sch_token);
316779e6e1cSJan Glauber 	return;
317779e6e1cSJan Glauber 
318779e6e1cSJan Glauber no_qebsm:
319779e6e1cSJan Glauber 	irq_ptr->sch_token = 0;
320779e6e1cSJan Glauber 	irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
32122f99347SJan Glauber 	DBF_EVENT("noV=V");
322779e6e1cSJan Glauber }
323779e6e1cSJan Glauber 
324bbd50e17SJan Glauber /*
325bbd50e17SJan Glauber  * If there is a qdio_irq we use the chsc_page and store the information
326bbd50e17SJan Glauber  * in the qdio_irq, otherwise we copy it to the specified structure.
327bbd50e17SJan Glauber  */
328bbd50e17SJan Glauber int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
329bbd50e17SJan Glauber 			struct subchannel_id *schid,
330bbd50e17SJan Glauber 			struct qdio_ssqd_desc *data)
331779e6e1cSJan Glauber {
332779e6e1cSJan Glauber 	struct chsc_ssqd_area *ssqd;
333779e6e1cSJan Glauber 	int rc;
334779e6e1cSJan Glauber 
33522f99347SJan Glauber 	DBF_EVENT("getssqd:%4x", schid->sch_no);
336da5b6cb1SSebastian Ott 	if (!irq_ptr) {
337bbd50e17SJan Glauber 		ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
338da5b6cb1SSebastian Ott 		if (!ssqd)
339da5b6cb1SSebastian Ott 			return -ENOMEM;
340da5b6cb1SSebastian Ott 	} else {
341da5b6cb1SSebastian Ott 		ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
342da5b6cb1SSebastian Ott 	}
343779e6e1cSJan Glauber 
344da5b6cb1SSebastian Ott 	rc = chsc_ssqd(*schid, ssqd);
345779e6e1cSJan Glauber 	if (rc)
346da5b6cb1SSebastian Ott 		goto out;
347779e6e1cSJan Glauber 
348779e6e1cSJan Glauber 	if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) ||
349779e6e1cSJan Glauber 	    !(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) ||
350bbd50e17SJan Glauber 	    (ssqd->qdio_ssqd.sch != schid->sch_no))
351da5b6cb1SSebastian Ott 		rc = -EINVAL;
352779e6e1cSJan Glauber 
353da5b6cb1SSebastian Ott 	if (!rc)
354da5b6cb1SSebastian Ott 		memcpy(data, &ssqd->qdio_ssqd, sizeof(*data));
355da5b6cb1SSebastian Ott 
356da5b6cb1SSebastian Ott out:
357da5b6cb1SSebastian Ott 	if (!irq_ptr)
358bbd50e17SJan Glauber 		free_page((unsigned long)ssqd);
359da5b6cb1SSebastian Ott 
360da5b6cb1SSebastian Ott 	return rc;
361779e6e1cSJan Glauber }
362779e6e1cSJan Glauber 
363779e6e1cSJan Glauber void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
364779e6e1cSJan Glauber {
365779e6e1cSJan Glauber 	unsigned char qdioac;
366779e6e1cSJan Glauber 	int rc;
367779e6e1cSJan Glauber 
368da5b6cb1SSebastian Ott 	rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, &irq_ptr->ssqd_desc);
369779e6e1cSJan Glauber 	if (rc) {
37022f99347SJan Glauber 		DBF_ERROR("%4x ssqd ERR", irq_ptr->schid.sch_no);
37122f99347SJan Glauber 		DBF_ERROR("rc:%x", rc);
372779e6e1cSJan Glauber 		/* all flags set, worst case */
373779e6e1cSJan Glauber 		qdioac = AC1_SIGA_INPUT_NEEDED | AC1_SIGA_OUTPUT_NEEDED |
374779e6e1cSJan Glauber 			 AC1_SIGA_SYNC_NEEDED;
375779e6e1cSJan Glauber 	} else
376779e6e1cSJan Glauber 		qdioac = irq_ptr->ssqd_desc.qdioac1;
377779e6e1cSJan Glauber 
378779e6e1cSJan Glauber 	check_and_setup_qebsm(irq_ptr, qdioac, irq_ptr->ssqd_desc.sch_token);
379779e6e1cSJan Glauber 	process_ac_flags(irq_ptr, qdioac);
38061d84979SJan Glauber 	DBF_EVENT("ac 1:%2x 2:%4x", qdioac, irq_ptr->ssqd_desc.qdioac2);
38161d84979SJan Glauber 	DBF_EVENT("3:%4x qib:%4x", irq_ptr->ssqd_desc.qdioac3, irq_ptr->qib.ac);
382779e6e1cSJan Glauber }
383779e6e1cSJan Glauber 
384d188cac3SJulian Wiedmann void qdio_free_async_data(struct qdio_irq *irq_ptr)
385779e6e1cSJan Glauber {
386779e6e1cSJan Glauber 	struct qdio_q *q;
387779e6e1cSJan Glauber 	int i;
388779e6e1cSJan Glauber 
389d188cac3SJulian Wiedmann 	for (i = 0; i < irq_ptr->max_output_qs; i++) {
390779e6e1cSJan Glauber 		q = irq_ptr->output_qs[i];
391104ea556Sfrank.blaschka@de.ibm.com 		if (q->u.out.use_cq) {
392d188cac3SJulian Wiedmann 			unsigned int n;
393104ea556Sfrank.blaschka@de.ibm.com 
394d188cac3SJulian Wiedmann 			for (n = 0; n < QDIO_MAX_BUFFERS_PER_Q; n++) {
395104ea556Sfrank.blaschka@de.ibm.com 				struct qaob *aob = q->u.out.aobs[n];
396d188cac3SJulian Wiedmann 
397104ea556Sfrank.blaschka@de.ibm.com 				if (aob) {
398104ea556Sfrank.blaschka@de.ibm.com 					qdio_release_aob(aob);
399104ea556Sfrank.blaschka@de.ibm.com 					q->u.out.aobs[n] = NULL;
400104ea556Sfrank.blaschka@de.ibm.com 				}
401104ea556Sfrank.blaschka@de.ibm.com 			}
402104ea556Sfrank.blaschka@de.ibm.com 
403104ea556Sfrank.blaschka@de.ibm.com 			qdio_disable_async_operation(&q->u.out);
404104ea556Sfrank.blaschka@de.ibm.com 		}
405779e6e1cSJan Glauber 	}
406779e6e1cSJan Glauber }
407779e6e1cSJan Glauber 
4084d4a3caaSJulian Wiedmann static void qdio_fill_qdr_desc(struct qdesfmt0 *desc, struct qdio_q *queue)
409779e6e1cSJan Glauber {
4104d4a3caaSJulian Wiedmann 	desc->sliba = virt_to_phys(queue->slib);
4114d4a3caaSJulian Wiedmann 	desc->sla = virt_to_phys(queue->sl);
4124d4a3caaSJulian Wiedmann 	desc->slsba = virt_to_phys(&queue->slsb);
413779e6e1cSJan Glauber 
4144d4a3caaSJulian Wiedmann 	desc->akey = PAGE_DEFAULT_KEY >> 4;
4154d4a3caaSJulian Wiedmann 	desc->bkey = PAGE_DEFAULT_KEY >> 4;
4164d4a3caaSJulian Wiedmann 	desc->ckey = PAGE_DEFAULT_KEY >> 4;
4174d4a3caaSJulian Wiedmann 	desc->dkey = PAGE_DEFAULT_KEY >> 4;
418779e6e1cSJan Glauber }
419779e6e1cSJan Glauber 
420779e6e1cSJan Glauber static void setup_qdr(struct qdio_irq *irq_ptr,
421779e6e1cSJan Glauber 		      struct qdio_initialize *qdio_init)
422779e6e1cSJan Glauber {
4234d4a3caaSJulian Wiedmann 	struct qdesfmt0 *desc = &irq_ptr->qdr->qdf0[0];
424779e6e1cSJan Glauber 	int i;
425779e6e1cSJan Glauber 
426779e6e1cSJan Glauber 	irq_ptr->qdr->qfmt = qdio_init->q_format;
427dfe5bb50SSwen Schillig 	irq_ptr->qdr->ac = qdio_init->qdr_ac;
428779e6e1cSJan Glauber 	irq_ptr->qdr->iqdcnt = qdio_init->no_input_qs;
429779e6e1cSJan Glauber 	irq_ptr->qdr->oqdcnt = qdio_init->no_output_qs;
430779e6e1cSJan Glauber 	irq_ptr->qdr->iqdsz = sizeof(struct qdesfmt0) / 4; /* size in words */
431779e6e1cSJan Glauber 	irq_ptr->qdr->oqdsz = sizeof(struct qdesfmt0) / 4;
4324d4a3caaSJulian Wiedmann 	irq_ptr->qdr->qiba = virt_to_phys(&irq_ptr->qib);
433d1bf8590SHeiko Carstens 	irq_ptr->qdr->qkey = PAGE_DEFAULT_KEY >> 4;
434779e6e1cSJan Glauber 
435779e6e1cSJan Glauber 	for (i = 0; i < qdio_init->no_input_qs; i++)
4364d4a3caaSJulian Wiedmann 		qdio_fill_qdr_desc(desc++, irq_ptr->input_qs[i]);
437779e6e1cSJan Glauber 
438779e6e1cSJan Glauber 	for (i = 0; i < qdio_init->no_output_qs; i++)
4394d4a3caaSJulian Wiedmann 		qdio_fill_qdr_desc(desc++, irq_ptr->output_qs[i]);
440779e6e1cSJan Glauber }
441779e6e1cSJan Glauber 
442779e6e1cSJan Glauber static void setup_qib(struct qdio_irq *irq_ptr,
443779e6e1cSJan Glauber 		      struct qdio_initialize *init_data)
444779e6e1cSJan Glauber {
445779e6e1cSJan Glauber 	if (qebsm_possible())
446779e6e1cSJan Glauber 		irq_ptr->qib.rflags |= QIB_RFLAGS_ENABLE_QEBSM;
447779e6e1cSJan Glauber 
448dcc18f48SChristof Schmitt 	irq_ptr->qib.rflags |= init_data->qib_rflags;
449dcc18f48SChristof Schmitt 
450779e6e1cSJan Glauber 	irq_ptr->qib.qfmt = init_data->q_format;
451779e6e1cSJan Glauber 	if (init_data->no_input_qs)
452779e6e1cSJan Glauber 		irq_ptr->qib.isliba =
453779e6e1cSJan Glauber 			(unsigned long)(irq_ptr->input_qs[0]->slib);
454779e6e1cSJan Glauber 	if (init_data->no_output_qs)
455779e6e1cSJan Glauber 		irq_ptr->qib.osliba =
456779e6e1cSJan Glauber 			(unsigned long)(irq_ptr->output_qs[0]->slib);
457*180a4c42SJulian Wiedmann 	memcpy(irq_ptr->qib.ebcnam, dev_name(&irq_ptr->cdev->dev), 8);
458*180a4c42SJulian Wiedmann 	ASCEBC(irq_ptr->qib.ebcnam, 8);
459779e6e1cSJan Glauber }
460779e6e1cSJan Glauber 
461014816b6SJulian Wiedmann int qdio_setup_irq(struct qdio_irq *irq_ptr, struct qdio_initialize *init_data)
462779e6e1cSJan Glauber {
463b2745655SJulian Wiedmann 	struct ccw_device *cdev = irq_ptr->cdev;
464779e6e1cSJan Glauber 	struct ciw *ciw;
465779e6e1cSJan Glauber 
466432ac5e0SJan Glauber 	memset(&irq_ptr->qib, 0, sizeof(irq_ptr->qib));
467432ac5e0SJan Glauber 	memset(&irq_ptr->siga_flag, 0, sizeof(irq_ptr->siga_flag));
468432ac5e0SJan Glauber 	memset(&irq_ptr->ccw, 0, sizeof(irq_ptr->ccw));
469432ac5e0SJan Glauber 	memset(&irq_ptr->ssqd_desc, 0, sizeof(irq_ptr->ssqd_desc));
470432ac5e0SJan Glauber 	memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat));
471432ac5e0SJan Glauber 
472d5d006faSJulian Wiedmann 	irq_ptr->debugfs_dev = NULL;
4736e2a7b51SJulian Wiedmann 	irq_ptr->sch_token = irq_ptr->perf_stat_enabled = 0;
4746e2a7b51SJulian Wiedmann 	irq_ptr->state = QDIO_IRQ_STATE_INACTIVE;
475432ac5e0SJan Glauber 
476779e6e1cSJan Glauber 	/* wipes qib.ac, required by ar7063 */
477779e6e1cSJan Glauber 	memset(irq_ptr->qdr, 0, sizeof(struct qdr));
478779e6e1cSJan Glauber 
479779e6e1cSJan Glauber 	irq_ptr->int_parm = init_data->int_parm;
480779e6e1cSJan Glauber 	irq_ptr->nr_input_qs = init_data->no_input_qs;
481779e6e1cSJan Glauber 	irq_ptr->nr_output_qs = init_data->no_output_qs;
482313dc689SJulian Wiedmann 	irq_ptr->scan_threshold = init_data->scan_threshold;
483dd62abd2SJulian Wiedmann 	ccw_device_get_schid(cdev, &irq_ptr->schid);
484779e6e1cSJan Glauber 	setup_queues(irq_ptr, init_data);
485779e6e1cSJan Glauber 
4860a6e6345SJulian Wiedmann 	if (init_data->irq_poll) {
4870a6e6345SJulian Wiedmann 		irq_ptr->irq_poll = init_data->irq_poll;
4880a6e6345SJulian Wiedmann 		set_bit(QDIO_IRQ_DISABLED, &irq_ptr->poll_state);
4890a6e6345SJulian Wiedmann 	} else {
4900a6e6345SJulian Wiedmann 		irq_ptr->irq_poll = NULL;
4910a6e6345SJulian Wiedmann 	}
4920a6e6345SJulian Wiedmann 
493779e6e1cSJan Glauber 	setup_qib(irq_ptr, init_data);
494779e6e1cSJan Glauber 	set_impl_params(irq_ptr, init_data->qib_param_field_format,
495779e6e1cSJan Glauber 			init_data->qib_param_field,
496779e6e1cSJan Glauber 			init_data->input_slib_elements,
497779e6e1cSJan Glauber 			init_data->output_slib_elements);
498779e6e1cSJan Glauber 
499779e6e1cSJan Glauber 	/* fill input and output descriptors */
500779e6e1cSJan Glauber 	setup_qdr(irq_ptr, init_data);
501779e6e1cSJan Glauber 
502779e6e1cSJan Glauber 	/* qdr, qib, sls, slsbs, slibs, sbales are filled now */
503779e6e1cSJan Glauber 
5047b942b4bSJulian Wiedmann 	/* set our IRQ handler */
5057b942b4bSJulian Wiedmann 	spin_lock_irq(get_ccwdev_lock(cdev));
5067b942b4bSJulian Wiedmann 	irq_ptr->orig_handler = cdev->handler;
5077b942b4bSJulian Wiedmann 	cdev->handler = qdio_int_handler;
5087b942b4bSJulian Wiedmann 	spin_unlock_irq(get_ccwdev_lock(cdev));
5097b942b4bSJulian Wiedmann 
510779e6e1cSJan Glauber 	/* get qdio commands */
511dd62abd2SJulian Wiedmann 	ciw = ccw_device_get_ciw(cdev, CIW_TYPE_EQUEUE);
512779e6e1cSJan Glauber 	if (!ciw) {
51322f99347SJan Glauber 		DBF_ERROR("%4x NO EQ", irq_ptr->schid.sch_no);
5142e68adcdSJulian Wiedmann 		return -EINVAL;
515779e6e1cSJan Glauber 	}
516779e6e1cSJan Glauber 	irq_ptr->equeue = *ciw;
517779e6e1cSJan Glauber 
518dd62abd2SJulian Wiedmann 	ciw = ccw_device_get_ciw(cdev, CIW_TYPE_AQUEUE);
519779e6e1cSJan Glauber 	if (!ciw) {
52022f99347SJan Glauber 		DBF_ERROR("%4x NO AQ", irq_ptr->schid.sch_no);
5212e68adcdSJulian Wiedmann 		return -EINVAL;
522779e6e1cSJan Glauber 	}
523779e6e1cSJan Glauber 	irq_ptr->aqueue = *ciw;
524779e6e1cSJan Glauber 
525779e6e1cSJan Glauber 	return 0;
526779e6e1cSJan Glauber }
527779e6e1cSJan Glauber 
5287b942b4bSJulian Wiedmann void qdio_shutdown_irq(struct qdio_irq *irq)
5297b942b4bSJulian Wiedmann {
5307b942b4bSJulian Wiedmann 	struct ccw_device *cdev = irq->cdev;
5317b942b4bSJulian Wiedmann 
5327b942b4bSJulian Wiedmann 	/* restore IRQ handler */
5337b942b4bSJulian Wiedmann 	spin_lock_irq(get_ccwdev_lock(cdev));
5347b942b4bSJulian Wiedmann 	cdev->handler = irq->orig_handler;
5357b942b4bSJulian Wiedmann 	cdev->private->intparm = 0;
5367b942b4bSJulian Wiedmann 	spin_unlock_irq(get_ccwdev_lock(cdev));
5377b942b4bSJulian Wiedmann }
5387b942b4bSJulian Wiedmann 
539b2745655SJulian Wiedmann void qdio_print_subchannel_info(struct qdio_irq *irq_ptr)
540779e6e1cSJan Glauber {
541779e6e1cSJan Glauber 	char s[80];
542779e6e1cSJan Glauber 
54322f99347SJan Glauber 	snprintf(s, 80, "qdio: %s %s on SC %x using "
5446726a807SJan Glauber 		 "AI:%d QEBSM:%d PRI:%d TDD:%d SIGA:%s%s%s%s%s\n",
545b2745655SJulian Wiedmann 		 dev_name(&irq_ptr->cdev->dev),
54622f99347SJan Glauber 		 (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" :
54722f99347SJan Glauber 			((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"),
54822f99347SJan Glauber 		 irq_ptr->schid.sch_no,
54922f99347SJan Glauber 		 is_thinint_irq(irq_ptr),
55022f99347SJan Glauber 		 (irq_ptr->sch_token) ? 1 : 0,
551f85b2b29SJulian Wiedmann 		 pci_out_supported(irq_ptr) ? 1 : 0,
55222f99347SJan Glauber 		 css_general_characteristics.aif_tdd,
55322f99347SJan Glauber 		 (irq_ptr->siga_flag.input) ? "R" : " ",
55422f99347SJan Glauber 		 (irq_ptr->siga_flag.output) ? "W" : " ",
55522f99347SJan Glauber 		 (irq_ptr->siga_flag.sync) ? "S" : " ",
55690adac58SJan Glauber 		 (irq_ptr->siga_flag.sync_after_ai) ? "A" : " ",
55790adac58SJan Glauber 		 (irq_ptr->siga_flag.sync_out_after_pci) ? "P" : " ");
55875f62761SJan Glauber 	printk(KERN_INFO "%s", s);
559779e6e1cSJan Glauber }
560779e6e1cSJan Glauber 
561104ea556Sfrank.blaschka@de.ibm.com int qdio_enable_async_operation(struct qdio_output_q *outq)
562104ea556Sfrank.blaschka@de.ibm.com {
5636396bb22SKees Cook 	outq->aobs = kcalloc(QDIO_MAX_BUFFERS_PER_Q, sizeof(struct qaob *),
56411f04465SJulian Wiedmann 			     GFP_KERNEL);
565104ea556Sfrank.blaschka@de.ibm.com 	if (!outq->aobs) {
566104ea556Sfrank.blaschka@de.ibm.com 		outq->use_cq = 0;
567104ea556Sfrank.blaschka@de.ibm.com 		return -ENOMEM;
568104ea556Sfrank.blaschka@de.ibm.com 	}
569104ea556Sfrank.blaschka@de.ibm.com 	outq->use_cq = 1;
570104ea556Sfrank.blaschka@de.ibm.com 	return 0;
571104ea556Sfrank.blaschka@de.ibm.com }
572104ea556Sfrank.blaschka@de.ibm.com 
573104ea556Sfrank.blaschka@de.ibm.com void qdio_disable_async_operation(struct qdio_output_q *q)
574104ea556Sfrank.blaschka@de.ibm.com {
575104ea556Sfrank.blaschka@de.ibm.com 	kfree(q->aobs);
576104ea556Sfrank.blaschka@de.ibm.com 	q->aobs = NULL;
577104ea556Sfrank.blaschka@de.ibm.com 	q->use_cq = 0;
578104ea556Sfrank.blaschka@de.ibm.com }
579104ea556Sfrank.blaschka@de.ibm.com 
580779e6e1cSJan Glauber int __init qdio_setup_init(void)
581779e6e1cSJan Glauber {
582104ea556Sfrank.blaschka@de.ibm.com 	int rc;
583104ea556Sfrank.blaschka@de.ibm.com 
584779e6e1cSJan Glauber 	qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q),
585779e6e1cSJan Glauber 					 256, 0, NULL);
586779e6e1cSJan Glauber 	if (!qdio_q_cache)
587779e6e1cSJan Glauber 		return -ENOMEM;
588779e6e1cSJan Glauber 
589104ea556Sfrank.blaschka@de.ibm.com 	qdio_aob_cache = kmem_cache_create("qdio_aob",
590104ea556Sfrank.blaschka@de.ibm.com 					sizeof(struct qaob),
591104ea556Sfrank.blaschka@de.ibm.com 					sizeof(struct qaob),
592104ea556Sfrank.blaschka@de.ibm.com 					0,
593104ea556Sfrank.blaschka@de.ibm.com 					NULL);
594104ea556Sfrank.blaschka@de.ibm.com 	if (!qdio_aob_cache) {
595104ea556Sfrank.blaschka@de.ibm.com 		rc = -ENOMEM;
596104ea556Sfrank.blaschka@de.ibm.com 		goto free_qdio_q_cache;
597104ea556Sfrank.blaschka@de.ibm.com 	}
598104ea556Sfrank.blaschka@de.ibm.com 
599779e6e1cSJan Glauber 	/* Check for OSA/FCP thin interrupts (bit 67). */
60022f99347SJan Glauber 	DBF_EVENT("thinint:%1d",
601779e6e1cSJan Glauber 		  (css_general_characteristics.aif_osa) ? 1 : 0);
602779e6e1cSJan Glauber 
603779e6e1cSJan Glauber 	/* Check for QEBSM support in general (bit 58). */
60422f99347SJan Glauber 	DBF_EVENT("cssQEBSM:%1d", (qebsm_possible()) ? 1 : 0);
605104ea556Sfrank.blaschka@de.ibm.com 	rc = 0;
606104ea556Sfrank.blaschka@de.ibm.com out:
607104ea556Sfrank.blaschka@de.ibm.com 	return rc;
608104ea556Sfrank.blaschka@de.ibm.com free_qdio_q_cache:
609104ea556Sfrank.blaschka@de.ibm.com 	kmem_cache_destroy(qdio_q_cache);
610104ea556Sfrank.blaschka@de.ibm.com 	goto out;
611779e6e1cSJan Glauber }
612779e6e1cSJan Glauber 
6133f1934bcSHeiko Carstens void qdio_setup_exit(void)
614779e6e1cSJan Glauber {
615104ea556Sfrank.blaschka@de.ibm.com 	kmem_cache_destroy(qdio_aob_cache);
616779e6e1cSJan Glauber 	kmem_cache_destroy(qdio_q_cache);
617779e6e1cSJan Glauber }
618