1 /* 2 * Support for adapter interruptions 3 * 4 * Copyright IBM Corp. 1999, 2007 5 * Author(s): Ingo Adlung <adlung@de.ibm.com> 6 * Cornelia Huck <cornelia.huck@de.ibm.com> 7 * Arnd Bergmann <arndb@de.ibm.com> 8 * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> 9 */ 10 11 #include <linux/init.h> 12 #include <linux/module.h> 13 #include <linux/slab.h> 14 #include <linux/rcupdate.h> 15 16 #include <asm/airq.h> 17 #include <asm/isc.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[MAX_ISC+1]; 37 static struct airq_t *airqs[MAX_ISC+1][NR_AIRQS]; 38 39 static int register_airq(struct airq_t *airq, u8 isc) 40 { 41 int i; 42 43 for (i = 0; i < NR_AIRQS; i++) 44 if (!cmpxchg(&airqs[isc][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 * @isc: isc for which the handler should be called 54 * 55 * Returns: 56 * Pointer to the indicator to be used on success 57 * ERR_PTR() if registration failed 58 */ 59 void *s390_register_adapter_interrupt(adapter_int_handler_t handler, 60 void *drv_data, u8 isc) 61 { 62 struct airq_t *airq; 63 char dbf_txt[16]; 64 int ret; 65 66 if (isc > MAX_ISC) 67 return ERR_PTR(-EINVAL); 68 airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); 69 if (!airq) { 70 ret = -ENOMEM; 71 goto out; 72 } 73 airq->handler = handler; 74 airq->drv_data = drv_data; 75 76 ret = register_airq(airq, isc); 77 out: 78 snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); 79 CIO_TRACE_EVENT(4, dbf_txt); 80 if (ret < 0) { 81 kfree(airq); 82 return ERR_PTR(ret); 83 } else 84 return &indicators[isc].byte[ret]; 85 } 86 EXPORT_SYMBOL(s390_register_adapter_interrupt); 87 88 /** 89 * s390_unregister_adapter_interrupt - unregister adapter interrupt handler 90 * @ind: indicator for which the handler is to be unregistered 91 * @isc: interruption subclass 92 */ 93 void s390_unregister_adapter_interrupt(void *ind, u8 isc) 94 { 95 struct airq_t *airq; 96 char dbf_txt[16]; 97 int i; 98 99 i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]); 100 snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); 101 CIO_TRACE_EVENT(4, dbf_txt); 102 indicators[isc].byte[i] = 0; 103 airq = xchg(&airqs[isc][i], NULL); 104 /* 105 * Allow interrupts to complete. This will ensure that the airq handle 106 * is no longer referenced by any interrupt handler. 107 */ 108 synchronize_sched(); 109 kfree(airq); 110 } 111 EXPORT_SYMBOL(s390_unregister_adapter_interrupt); 112 113 #define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) 114 115 void do_adapter_IO(u8 isc) 116 { 117 int w; 118 int i; 119 unsigned long word; 120 struct airq_t *airq; 121 122 /* 123 * Access indicator array in word-sized chunks to minimize storage 124 * fetch operations. 125 */ 126 for (w = 0; w < NR_AIRQ_WORDS; w++) { 127 word = indicators[isc].word[w]; 128 i = w * NR_AIRQS_PER_WORD; 129 /* 130 * Check bytes within word for active indicators. 131 */ 132 while (word) { 133 if (word & INDICATOR_MASK) { 134 airq = airqs[isc][i]; 135 /* Make sure gcc reads from airqs only once. */ 136 barrier(); 137 if (likely(airq)) 138 airq->handler(&indicators[isc].byte[i], 139 airq->drv_data); 140 else 141 /* 142 * Reset ill-behaved indicator. 143 */ 144 indicators[isc].byte[i] = 0; 145 } 146 word <<= 8; 147 i++; 148 } 149 } 150 } 151