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