1 /* 2 * drivers/s390/cio/airq.c 3 * Support for adapter interruptions 4 * 5 * Copyright IBM Corp. 1999,2007 6 * Author(s): Ingo Adlung <adlung@de.ibm.com> 7 * Cornelia Huck <cornelia.huck@de.ibm.com> 8 * Arnd Bergmann <arndb@de.ibm.com> 9 * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> 10 */ 11 12 #include <linux/init.h> 13 #include <linux/module.h> 14 #include <linux/slab.h> 15 #include <linux/rcupdate.h> 16 17 #include <asm/airq.h> 18 19 #include "cio.h" 20 #include "cio_debug.h" 21 22 #define NR_AIRQS 32 23 #define NR_AIRQS_PER_WORD sizeof(unsigned long) 24 #define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) 25 26 union indicator_t { 27 unsigned long word[NR_AIRQ_WORDS]; 28 unsigned char byte[NR_AIRQS]; 29 } __attribute__((packed)); 30 31 struct airq_t { 32 adapter_int_handler_t handler; 33 void *drv_data; 34 }; 35 36 static union indicator_t indicators; 37 static struct airq_t *airqs[NR_AIRQS]; 38 39 static int register_airq(struct airq_t *airq) 40 { 41 int i; 42 43 for (i = 0; i < NR_AIRQS; i++) 44 if (!cmpxchg(&airqs[i], NULL, airq)) 45 return i; 46 return -ENOMEM; 47 } 48 49 /** 50 * s390_register_adapter_interrupt() - register adapter interrupt handler 51 * @handler: adapter handler to be registered 52 * @drv_data: driver data passed with each call to the handler 53 * 54 * Returns: 55 * Pointer to the indicator to be used on success 56 * ERR_PTR() if registration failed 57 */ 58 void *s390_register_adapter_interrupt(adapter_int_handler_t handler, 59 void *drv_data) 60 { 61 struct airq_t *airq; 62 char dbf_txt[16]; 63 int ret; 64 65 airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); 66 if (!airq) { 67 ret = -ENOMEM; 68 goto out; 69 } 70 airq->handler = handler; 71 airq->drv_data = drv_data; 72 ret = register_airq(airq); 73 if (ret < 0) 74 kfree(airq); 75 out: 76 snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); 77 CIO_TRACE_EVENT(4, dbf_txt); 78 if (ret < 0) 79 return ERR_PTR(ret); 80 else 81 return &indicators.byte[ret]; 82 } 83 EXPORT_SYMBOL(s390_register_adapter_interrupt); 84 85 /** 86 * s390_unregister_adapter_interrupt - unregister adapter interrupt handler 87 * @ind: indicator for which the handler is to be unregistered 88 */ 89 void s390_unregister_adapter_interrupt(void *ind) 90 { 91 struct airq_t *airq; 92 char dbf_txt[16]; 93 int i; 94 95 i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]); 96 snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); 97 CIO_TRACE_EVENT(4, dbf_txt); 98 indicators.byte[i] = 0; 99 airq = xchg(&airqs[i], NULL); 100 /* 101 * Allow interrupts to complete. This will ensure that the airq handle 102 * is no longer referenced by any interrupt handler. 103 */ 104 synchronize_sched(); 105 kfree(airq); 106 } 107 EXPORT_SYMBOL(s390_unregister_adapter_interrupt); 108 109 #define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) 110 111 void do_adapter_IO(void) 112 { 113 int w; 114 int i; 115 unsigned long word; 116 struct airq_t *airq; 117 118 /* 119 * Access indicator array in word-sized chunks to minimize storage 120 * fetch operations. 121 */ 122 for (w = 0; w < NR_AIRQ_WORDS; w++) { 123 word = indicators.word[w]; 124 i = w * NR_AIRQS_PER_WORD; 125 /* 126 * Check bytes within word for active indicators. 127 */ 128 while (word) { 129 if (word & INDICATOR_MASK) { 130 airq = airqs[i]; 131 if (likely(airq)) 132 airq->handler(&indicators.byte[i], 133 airq->drv_data); 134 else 135 /* 136 * Reset ill-behaved indicator. 137 */ 138 indicators.byte[i] = 0; 139 } 140 word <<= 8; 141 i++; 142 } 143 } 144 } 145