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