1*779e6e1cSJan Glauber /* 2*779e6e1cSJan Glauber * linux/drivers/s390/cio/thinint_qdio.c 3*779e6e1cSJan Glauber * 4*779e6e1cSJan Glauber * thin interrupt support for qdio 5*779e6e1cSJan Glauber * 6*779e6e1cSJan Glauber * Copyright 2000-2008 IBM Corp. 7*779e6e1cSJan Glauber * Author(s): Utz Bacher <utz.bacher@de.ibm.com> 8*779e6e1cSJan Glauber * Cornelia Huck <cornelia.huck@de.ibm.com> 9*779e6e1cSJan Glauber * Jan Glauber <jang@linux.vnet.ibm.com> 10*779e6e1cSJan Glauber */ 11*779e6e1cSJan Glauber #include <linux/io.h> 12*779e6e1cSJan Glauber #include <asm/atomic.h> 13*779e6e1cSJan Glauber #include <asm/debug.h> 14*779e6e1cSJan Glauber #include <asm/qdio.h> 15*779e6e1cSJan Glauber #include <asm/airq.h> 16*779e6e1cSJan Glauber #include <asm/isc.h> 17*779e6e1cSJan Glauber 18*779e6e1cSJan Glauber #include "cio.h" 19*779e6e1cSJan Glauber #include "ioasm.h" 20*779e6e1cSJan Glauber #include "qdio.h" 21*779e6e1cSJan Glauber #include "qdio_debug.h" 22*779e6e1cSJan Glauber #include "qdio_perf.h" 23*779e6e1cSJan Glauber 24*779e6e1cSJan Glauber /* 25*779e6e1cSJan Glauber * Restriction: only 63 iqdio subchannels would have its own indicator, 26*779e6e1cSJan Glauber * after that, subsequent subchannels share one indicator 27*779e6e1cSJan Glauber */ 28*779e6e1cSJan Glauber #define TIQDIO_NR_NONSHARED_IND 63 29*779e6e1cSJan Glauber #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) 30*779e6e1cSJan Glauber #define TIQDIO_SHARED_IND 63 31*779e6e1cSJan Glauber 32*779e6e1cSJan Glauber /* list of thin interrupt input queues */ 33*779e6e1cSJan Glauber static LIST_HEAD(tiq_list); 34*779e6e1cSJan Glauber 35*779e6e1cSJan Glauber /* adapter local summary indicator */ 36*779e6e1cSJan Glauber static unsigned char *tiqdio_alsi; 37*779e6e1cSJan Glauber 38*779e6e1cSJan Glauber /* device state change indicators */ 39*779e6e1cSJan Glauber struct indicator_t { 40*779e6e1cSJan Glauber u32 ind; /* u32 because of compare-and-swap performance */ 41*779e6e1cSJan Glauber atomic_t count; /* use count, 0 or 1 for non-shared indicators */ 42*779e6e1cSJan Glauber }; 43*779e6e1cSJan Glauber static struct indicator_t *q_indicators; 44*779e6e1cSJan Glauber 45*779e6e1cSJan Glauber static void tiqdio_tasklet_fn(unsigned long data); 46*779e6e1cSJan Glauber static DECLARE_TASKLET(tiqdio_tasklet, tiqdio_tasklet_fn, 0); 47*779e6e1cSJan Glauber 48*779e6e1cSJan Glauber static int css_qdio_omit_svs; 49*779e6e1cSJan Glauber 50*779e6e1cSJan Glauber static inline unsigned long do_clear_global_summary(void) 51*779e6e1cSJan Glauber { 52*779e6e1cSJan Glauber register unsigned long __fn asm("1") = 3; 53*779e6e1cSJan Glauber register unsigned long __tmp asm("2"); 54*779e6e1cSJan Glauber register unsigned long __time asm("3"); 55*779e6e1cSJan Glauber 56*779e6e1cSJan Glauber asm volatile( 57*779e6e1cSJan Glauber " .insn rre,0xb2650000,2,0" 58*779e6e1cSJan Glauber : "+d" (__fn), "=d" (__tmp), "=d" (__time)); 59*779e6e1cSJan Glauber return __time; 60*779e6e1cSJan Glauber } 61*779e6e1cSJan Glauber 62*779e6e1cSJan Glauber /* returns addr for the device state change indicator */ 63*779e6e1cSJan Glauber static u32 *get_indicator(void) 64*779e6e1cSJan Glauber { 65*779e6e1cSJan Glauber int i; 66*779e6e1cSJan Glauber 67*779e6e1cSJan Glauber for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++) 68*779e6e1cSJan Glauber if (!atomic_read(&q_indicators[i].count)) { 69*779e6e1cSJan Glauber atomic_set(&q_indicators[i].count, 1); 70*779e6e1cSJan Glauber return &q_indicators[i].ind; 71*779e6e1cSJan Glauber } 72*779e6e1cSJan Glauber 73*779e6e1cSJan Glauber /* use the shared indicator */ 74*779e6e1cSJan Glauber atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count); 75*779e6e1cSJan Glauber return &q_indicators[TIQDIO_SHARED_IND].ind; 76*779e6e1cSJan Glauber } 77*779e6e1cSJan Glauber 78*779e6e1cSJan Glauber static void put_indicator(u32 *addr) 79*779e6e1cSJan Glauber { 80*779e6e1cSJan Glauber int i; 81*779e6e1cSJan Glauber 82*779e6e1cSJan Glauber if (!addr) 83*779e6e1cSJan Glauber return; 84*779e6e1cSJan Glauber i = ((unsigned long)addr - (unsigned long)q_indicators) / 85*779e6e1cSJan Glauber sizeof(struct indicator_t); 86*779e6e1cSJan Glauber atomic_dec(&q_indicators[i].count); 87*779e6e1cSJan Glauber } 88*779e6e1cSJan Glauber 89*779e6e1cSJan Glauber void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) 90*779e6e1cSJan Glauber { 91*779e6e1cSJan Glauber struct qdio_q *q; 92*779e6e1cSJan Glauber int i; 93*779e6e1cSJan Glauber 94*779e6e1cSJan Glauber /* No TDD facility? If we must use SIGA-s we can also omit SVS. */ 95*779e6e1cSJan Glauber if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync) 96*779e6e1cSJan Glauber css_qdio_omit_svs = 1; 97*779e6e1cSJan Glauber 98*779e6e1cSJan Glauber for_each_input_queue(irq_ptr, q, i) { 99*779e6e1cSJan Glauber list_add_rcu(&q->entry, &tiq_list); 100*779e6e1cSJan Glauber synchronize_rcu(); 101*779e6e1cSJan Glauber } 102*779e6e1cSJan Glauber xchg(irq_ptr->dsci, 1); 103*779e6e1cSJan Glauber tasklet_schedule(&tiqdio_tasklet); 104*779e6e1cSJan Glauber } 105*779e6e1cSJan Glauber 106*779e6e1cSJan Glauber /* 107*779e6e1cSJan Glauber * we cannot stop the tiqdio tasklet here since it is for all 108*779e6e1cSJan Glauber * thinint qdio devices and it must run as long as there is a 109*779e6e1cSJan Glauber * thinint device left 110*779e6e1cSJan Glauber */ 111*779e6e1cSJan Glauber void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) 112*779e6e1cSJan Glauber { 113*779e6e1cSJan Glauber struct qdio_q *q; 114*779e6e1cSJan Glauber int i; 115*779e6e1cSJan Glauber 116*779e6e1cSJan Glauber for_each_input_queue(irq_ptr, q, i) { 117*779e6e1cSJan Glauber list_del_rcu(&q->entry); 118*779e6e1cSJan Glauber synchronize_rcu(); 119*779e6e1cSJan Glauber } 120*779e6e1cSJan Glauber } 121*779e6e1cSJan Glauber 122*779e6e1cSJan Glauber static inline int tiqdio_inbound_q_done(struct qdio_q *q) 123*779e6e1cSJan Glauber { 124*779e6e1cSJan Glauber unsigned char state; 125*779e6e1cSJan Glauber 126*779e6e1cSJan Glauber if (!atomic_read(&q->nr_buf_used)) 127*779e6e1cSJan Glauber return 1; 128*779e6e1cSJan Glauber 129*779e6e1cSJan Glauber qdio_siga_sync_q(q); 130*779e6e1cSJan Glauber get_buf_state(q, q->first_to_check, &state); 131*779e6e1cSJan Glauber 132*779e6e1cSJan Glauber if (state == SLSB_P_INPUT_PRIMED) 133*779e6e1cSJan Glauber /* more work coming */ 134*779e6e1cSJan Glauber return 0; 135*779e6e1cSJan Glauber return 1; 136*779e6e1cSJan Glauber } 137*779e6e1cSJan Glauber 138*779e6e1cSJan Glauber static inline int shared_ind(struct qdio_irq *irq_ptr) 139*779e6e1cSJan Glauber { 140*779e6e1cSJan Glauber return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; 141*779e6e1cSJan Glauber } 142*779e6e1cSJan Glauber 143*779e6e1cSJan Glauber static void __tiqdio_inbound_processing(struct qdio_q *q) 144*779e6e1cSJan Glauber { 145*779e6e1cSJan Glauber qdio_perf_stat_inc(&perf_stats.thinint_inbound); 146*779e6e1cSJan Glauber qdio_sync_after_thinint(q); 147*779e6e1cSJan Glauber 148*779e6e1cSJan Glauber /* 149*779e6e1cSJan Glauber * Maybe we have work on our outbound queues... at least 150*779e6e1cSJan Glauber * we have to check the PCI capable queues. 151*779e6e1cSJan Glauber */ 152*779e6e1cSJan Glauber qdio_check_outbound_after_thinint(q); 153*779e6e1cSJan Glauber 154*779e6e1cSJan Glauber again: 155*779e6e1cSJan Glauber if (!qdio_inbound_q_moved(q)) 156*779e6e1cSJan Glauber return; 157*779e6e1cSJan Glauber 158*779e6e1cSJan Glauber qdio_kick_inbound_handler(q); 159*779e6e1cSJan Glauber 160*779e6e1cSJan Glauber if (!tiqdio_inbound_q_done(q)) { 161*779e6e1cSJan Glauber qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop); 162*779e6e1cSJan Glauber goto again; 163*779e6e1cSJan Glauber } 164*779e6e1cSJan Glauber 165*779e6e1cSJan Glauber qdio_stop_polling(q); 166*779e6e1cSJan Glauber /* 167*779e6e1cSJan Glauber * We need to check again to not lose initiative after 168*779e6e1cSJan Glauber * resetting the ACK state. 169*779e6e1cSJan Glauber */ 170*779e6e1cSJan Glauber if (!tiqdio_inbound_q_done(q)) { 171*779e6e1cSJan Glauber qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2); 172*779e6e1cSJan Glauber goto again; 173*779e6e1cSJan Glauber } 174*779e6e1cSJan Glauber } 175*779e6e1cSJan Glauber 176*779e6e1cSJan Glauber void tiqdio_inbound_processing(unsigned long data) 177*779e6e1cSJan Glauber { 178*779e6e1cSJan Glauber struct qdio_q *q = (struct qdio_q *)data; 179*779e6e1cSJan Glauber 180*779e6e1cSJan Glauber __tiqdio_inbound_processing(q); 181*779e6e1cSJan Glauber } 182*779e6e1cSJan Glauber 183*779e6e1cSJan Glauber /* check for work on all inbound thinint queues */ 184*779e6e1cSJan Glauber static void tiqdio_tasklet_fn(unsigned long data) 185*779e6e1cSJan Glauber { 186*779e6e1cSJan Glauber struct qdio_q *q; 187*779e6e1cSJan Glauber 188*779e6e1cSJan Glauber qdio_perf_stat_inc(&perf_stats.tasklet_thinint); 189*779e6e1cSJan Glauber again: 190*779e6e1cSJan Glauber 191*779e6e1cSJan Glauber /* protect tiq_list entries, only changed in activate or shutdown */ 192*779e6e1cSJan Glauber rcu_read_lock(); 193*779e6e1cSJan Glauber 194*779e6e1cSJan Glauber list_for_each_entry_rcu(q, &tiq_list, entry) 195*779e6e1cSJan Glauber /* only process queues from changed sets */ 196*779e6e1cSJan Glauber if (*q->irq_ptr->dsci) { 197*779e6e1cSJan Glauber 198*779e6e1cSJan Glauber /* only clear it if the indicator is non-shared */ 199*779e6e1cSJan Glauber if (!shared_ind(q->irq_ptr)) 200*779e6e1cSJan Glauber xchg(q->irq_ptr->dsci, 0); 201*779e6e1cSJan Glauber /* 202*779e6e1cSJan Glauber * don't call inbound processing directly since 203*779e6e1cSJan Glauber * that could starve other thinint queues 204*779e6e1cSJan Glauber */ 205*779e6e1cSJan Glauber tasklet_schedule(&q->tasklet); 206*779e6e1cSJan Glauber } 207*779e6e1cSJan Glauber 208*779e6e1cSJan Glauber rcu_read_unlock(); 209*779e6e1cSJan Glauber 210*779e6e1cSJan Glauber /* 211*779e6e1cSJan Glauber * if we used the shared indicator clear it now after all queues 212*779e6e1cSJan Glauber * were processed 213*779e6e1cSJan Glauber */ 214*779e6e1cSJan Glauber if (atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) { 215*779e6e1cSJan Glauber xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); 216*779e6e1cSJan Glauber 217*779e6e1cSJan Glauber /* prevent racing */ 218*779e6e1cSJan Glauber if (*tiqdio_alsi) 219*779e6e1cSJan Glauber xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1); 220*779e6e1cSJan Glauber } 221*779e6e1cSJan Glauber 222*779e6e1cSJan Glauber /* check for more work */ 223*779e6e1cSJan Glauber if (*tiqdio_alsi) { 224*779e6e1cSJan Glauber xchg(tiqdio_alsi, 0); 225*779e6e1cSJan Glauber qdio_perf_stat_inc(&perf_stats.tasklet_thinint_loop); 226*779e6e1cSJan Glauber goto again; 227*779e6e1cSJan Glauber } 228*779e6e1cSJan Glauber } 229*779e6e1cSJan Glauber 230*779e6e1cSJan Glauber /** 231*779e6e1cSJan Glauber * tiqdio_thinint_handler - thin interrupt handler for qdio 232*779e6e1cSJan Glauber * @ind: pointer to adapter local summary indicator 233*779e6e1cSJan Glauber * @drv_data: NULL 234*779e6e1cSJan Glauber */ 235*779e6e1cSJan Glauber static void tiqdio_thinint_handler(void *ind, void *drv_data) 236*779e6e1cSJan Glauber { 237*779e6e1cSJan Glauber qdio_perf_stat_inc(&perf_stats.thin_int); 238*779e6e1cSJan Glauber 239*779e6e1cSJan Glauber /* 240*779e6e1cSJan Glauber * SVS only when needed: issue SVS to benefit from iqdio interrupt 241*779e6e1cSJan Glauber * avoidance (SVS clears adapter interrupt suppression overwrite) 242*779e6e1cSJan Glauber */ 243*779e6e1cSJan Glauber if (!css_qdio_omit_svs) 244*779e6e1cSJan Glauber do_clear_global_summary(); 245*779e6e1cSJan Glauber 246*779e6e1cSJan Glauber /* 247*779e6e1cSJan Glauber * reset local summary indicator (tiqdio_alsi) to stop adapter 248*779e6e1cSJan Glauber * interrupts for now, the tasklet will clean all dsci's 249*779e6e1cSJan Glauber */ 250*779e6e1cSJan Glauber xchg((u8 *)ind, 0); 251*779e6e1cSJan Glauber tasklet_hi_schedule(&tiqdio_tasklet); 252*779e6e1cSJan Glauber } 253*779e6e1cSJan Glauber 254*779e6e1cSJan Glauber static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) 255*779e6e1cSJan Glauber { 256*779e6e1cSJan Glauber struct scssc_area *scssc_area; 257*779e6e1cSJan Glauber char dbf_text[15]; 258*779e6e1cSJan Glauber void *ptr; 259*779e6e1cSJan Glauber int rc; 260*779e6e1cSJan Glauber 261*779e6e1cSJan Glauber scssc_area = (struct scssc_area *)irq_ptr->chsc_page; 262*779e6e1cSJan Glauber memset(scssc_area, 0, PAGE_SIZE); 263*779e6e1cSJan Glauber 264*779e6e1cSJan Glauber if (reset) { 265*779e6e1cSJan Glauber scssc_area->summary_indicator_addr = 0; 266*779e6e1cSJan Glauber scssc_area->subchannel_indicator_addr = 0; 267*779e6e1cSJan Glauber } else { 268*779e6e1cSJan Glauber scssc_area->summary_indicator_addr = virt_to_phys(tiqdio_alsi); 269*779e6e1cSJan Glauber scssc_area->subchannel_indicator_addr = 270*779e6e1cSJan Glauber virt_to_phys(irq_ptr->dsci); 271*779e6e1cSJan Glauber } 272*779e6e1cSJan Glauber 273*779e6e1cSJan Glauber scssc_area->request = (struct chsc_header) { 274*779e6e1cSJan Glauber .length = 0x0fe0, 275*779e6e1cSJan Glauber .code = 0x0021, 276*779e6e1cSJan Glauber }; 277*779e6e1cSJan Glauber scssc_area->operation_code = 0; 278*779e6e1cSJan Glauber scssc_area->ks = PAGE_DEFAULT_KEY; 279*779e6e1cSJan Glauber scssc_area->kc = PAGE_DEFAULT_KEY; 280*779e6e1cSJan Glauber scssc_area->isc = QDIO_AIRQ_ISC; 281*779e6e1cSJan Glauber scssc_area->schid = irq_ptr->schid; 282*779e6e1cSJan Glauber 283*779e6e1cSJan Glauber /* enable the time delay disablement facility */ 284*779e6e1cSJan Glauber if (css_general_characteristics.aif_tdd) 285*779e6e1cSJan Glauber scssc_area->word_with_d_bit = 0x10000000; 286*779e6e1cSJan Glauber 287*779e6e1cSJan Glauber rc = chsc(scssc_area); 288*779e6e1cSJan Glauber if (rc) 289*779e6e1cSJan Glauber return -EIO; 290*779e6e1cSJan Glauber 291*779e6e1cSJan Glauber rc = chsc_error_from_response(scssc_area->response.code); 292*779e6e1cSJan Glauber if (rc) { 293*779e6e1cSJan Glauber sprintf(dbf_text, "sidR%4x", scssc_area->response.code); 294*779e6e1cSJan Glauber QDIO_DBF_TEXT1(0, trace, dbf_text); 295*779e6e1cSJan Glauber QDIO_DBF_TEXT1(0, setup, dbf_text); 296*779e6e1cSJan Glauber ptr = &scssc_area->response; 297*779e6e1cSJan Glauber QDIO_DBF_HEX2(1, setup, &ptr, QDIO_DBF_SETUP_LEN); 298*779e6e1cSJan Glauber return rc; 299*779e6e1cSJan Glauber } 300*779e6e1cSJan Glauber 301*779e6e1cSJan Glauber QDIO_DBF_TEXT2(0, setup, "setscind"); 302*779e6e1cSJan Glauber QDIO_DBF_HEX2(0, setup, &scssc_area->summary_indicator_addr, 303*779e6e1cSJan Glauber sizeof(unsigned long)); 304*779e6e1cSJan Glauber QDIO_DBF_HEX2(0, setup, &scssc_area->subchannel_indicator_addr, 305*779e6e1cSJan Glauber sizeof(unsigned long)); 306*779e6e1cSJan Glauber return 0; 307*779e6e1cSJan Glauber } 308*779e6e1cSJan Glauber 309*779e6e1cSJan Glauber /* allocate non-shared indicators and shared indicator */ 310*779e6e1cSJan Glauber int __init tiqdio_allocate_memory(void) 311*779e6e1cSJan Glauber { 312*779e6e1cSJan Glauber q_indicators = kzalloc(sizeof(struct indicator_t) * TIQDIO_NR_INDICATORS, 313*779e6e1cSJan Glauber GFP_KERNEL); 314*779e6e1cSJan Glauber if (!q_indicators) 315*779e6e1cSJan Glauber return -ENOMEM; 316*779e6e1cSJan Glauber return 0; 317*779e6e1cSJan Glauber } 318*779e6e1cSJan Glauber 319*779e6e1cSJan Glauber void tiqdio_free_memory(void) 320*779e6e1cSJan Glauber { 321*779e6e1cSJan Glauber kfree(q_indicators); 322*779e6e1cSJan Glauber } 323*779e6e1cSJan Glauber 324*779e6e1cSJan Glauber int __init tiqdio_register_thinints(void) 325*779e6e1cSJan Glauber { 326*779e6e1cSJan Glauber char dbf_text[20]; 327*779e6e1cSJan Glauber 328*779e6e1cSJan Glauber isc_register(QDIO_AIRQ_ISC); 329*779e6e1cSJan Glauber tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler, 330*779e6e1cSJan Glauber NULL, QDIO_AIRQ_ISC); 331*779e6e1cSJan Glauber if (IS_ERR(tiqdio_alsi)) { 332*779e6e1cSJan Glauber sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_alsi)); 333*779e6e1cSJan Glauber QDIO_DBF_TEXT0(0, setup, dbf_text); 334*779e6e1cSJan Glauber tiqdio_alsi = NULL; 335*779e6e1cSJan Glauber isc_unregister(QDIO_AIRQ_ISC); 336*779e6e1cSJan Glauber return -ENOMEM; 337*779e6e1cSJan Glauber } 338*779e6e1cSJan Glauber return 0; 339*779e6e1cSJan Glauber } 340*779e6e1cSJan Glauber 341*779e6e1cSJan Glauber int qdio_establish_thinint(struct qdio_irq *irq_ptr) 342*779e6e1cSJan Glauber { 343*779e6e1cSJan Glauber if (!is_thinint_irq(irq_ptr)) 344*779e6e1cSJan Glauber return 0; 345*779e6e1cSJan Glauber 346*779e6e1cSJan Glauber /* Check for aif time delay disablement. If installed, 347*779e6e1cSJan Glauber * omit SVS even under LPAR 348*779e6e1cSJan Glauber */ 349*779e6e1cSJan Glauber if (css_general_characteristics.aif_tdd) 350*779e6e1cSJan Glauber css_qdio_omit_svs = 1; 351*779e6e1cSJan Glauber return set_subchannel_ind(irq_ptr, 0); 352*779e6e1cSJan Glauber } 353*779e6e1cSJan Glauber 354*779e6e1cSJan Glauber void qdio_setup_thinint(struct qdio_irq *irq_ptr) 355*779e6e1cSJan Glauber { 356*779e6e1cSJan Glauber if (!is_thinint_irq(irq_ptr)) 357*779e6e1cSJan Glauber return; 358*779e6e1cSJan Glauber irq_ptr->dsci = get_indicator(); 359*779e6e1cSJan Glauber QDIO_DBF_HEX1(0, setup, &irq_ptr->dsci, sizeof(void *)); 360*779e6e1cSJan Glauber } 361*779e6e1cSJan Glauber 362*779e6e1cSJan Glauber void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) 363*779e6e1cSJan Glauber { 364*779e6e1cSJan Glauber if (!is_thinint_irq(irq_ptr)) 365*779e6e1cSJan Glauber return; 366*779e6e1cSJan Glauber 367*779e6e1cSJan Glauber /* reset adapter interrupt indicators */ 368*779e6e1cSJan Glauber put_indicator(irq_ptr->dsci); 369*779e6e1cSJan Glauber set_subchannel_ind(irq_ptr, 1); 370*779e6e1cSJan Glauber } 371*779e6e1cSJan Glauber 372*779e6e1cSJan Glauber void __exit tiqdio_unregister_thinints(void) 373*779e6e1cSJan Glauber { 374*779e6e1cSJan Glauber tasklet_disable(&tiqdio_tasklet); 375*779e6e1cSJan Glauber 376*779e6e1cSJan Glauber if (tiqdio_alsi) { 377*779e6e1cSJan Glauber s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC); 378*779e6e1cSJan Glauber isc_unregister(QDIO_AIRQ_ISC); 379*779e6e1cSJan Glauber } 380*779e6e1cSJan Glauber } 381