1779e6e1cSJan Glauber /* 2779e6e1cSJan Glauber * linux/drivers/s390/cio/thinint_qdio.c 3779e6e1cSJan Glauber * 46486cda6SJan Glauber * Copyright 2000,2009 IBM Corp. 5779e6e1cSJan Glauber * Author(s): Utz Bacher <utz.bacher@de.ibm.com> 6779e6e1cSJan Glauber * Cornelia Huck <cornelia.huck@de.ibm.com> 7779e6e1cSJan Glauber * Jan Glauber <jang@linux.vnet.ibm.com> 8779e6e1cSJan Glauber */ 9779e6e1cSJan Glauber #include <linux/io.h> 105a0e3ad6STejun Heo #include <linux/slab.h> 11779e6e1cSJan Glauber #include <asm/atomic.h> 12779e6e1cSJan Glauber #include <asm/debug.h> 13779e6e1cSJan Glauber #include <asm/qdio.h> 14779e6e1cSJan Glauber #include <asm/airq.h> 15779e6e1cSJan Glauber #include <asm/isc.h> 16779e6e1cSJan Glauber 17779e6e1cSJan Glauber #include "cio.h" 18779e6e1cSJan Glauber #include "ioasm.h" 19779e6e1cSJan Glauber #include "qdio.h" 20779e6e1cSJan Glauber #include "qdio_debug.h" 21779e6e1cSJan Glauber 22779e6e1cSJan Glauber /* 23779e6e1cSJan Glauber * Restriction: only 63 iqdio subchannels would have its own indicator, 24779e6e1cSJan Glauber * after that, subsequent subchannels share one indicator 25779e6e1cSJan Glauber */ 26779e6e1cSJan Glauber #define TIQDIO_NR_NONSHARED_IND 63 27779e6e1cSJan Glauber #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) 28779e6e1cSJan Glauber 29779e6e1cSJan Glauber /* list of thin interrupt input queues */ 30779e6e1cSJan Glauber static LIST_HEAD(tiq_list); 31b4547402SJan Glauber DEFINE_MUTEX(tiq_list_lock); 32779e6e1cSJan Glauber 33779e6e1cSJan Glauber /* adapter local summary indicator */ 34d36deae7SJan Glauber static u8 *tiqdio_alsi; 35779e6e1cSJan Glauber 36d36deae7SJan Glauber struct indicator_t *q_indicators; 37779e6e1cSJan Glauber 38779e6e1cSJan Glauber static int css_qdio_omit_svs; 39779e6e1cSJan Glauber 40d36deae7SJan Glauber static u64 last_ai_time; 41d36deae7SJan Glauber 42779e6e1cSJan Glauber static inline unsigned long do_clear_global_summary(void) 43779e6e1cSJan Glauber { 44779e6e1cSJan Glauber register unsigned long __fn asm("1") = 3; 45779e6e1cSJan Glauber register unsigned long __tmp asm("2"); 46779e6e1cSJan Glauber register unsigned long __time asm("3"); 47779e6e1cSJan Glauber 48779e6e1cSJan Glauber asm volatile( 49779e6e1cSJan Glauber " .insn rre,0xb2650000,2,0" 50779e6e1cSJan Glauber : "+d" (__fn), "=d" (__tmp), "=d" (__time)); 51779e6e1cSJan Glauber return __time; 52779e6e1cSJan Glauber } 53779e6e1cSJan Glauber 54779e6e1cSJan Glauber /* returns addr for the device state change indicator */ 55779e6e1cSJan Glauber static u32 *get_indicator(void) 56779e6e1cSJan Glauber { 57779e6e1cSJan Glauber int i; 58779e6e1cSJan Glauber 59779e6e1cSJan Glauber for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++) 60779e6e1cSJan Glauber if (!atomic_read(&q_indicators[i].count)) { 61779e6e1cSJan Glauber atomic_set(&q_indicators[i].count, 1); 62779e6e1cSJan Glauber return &q_indicators[i].ind; 63779e6e1cSJan Glauber } 64779e6e1cSJan Glauber 65779e6e1cSJan Glauber /* use the shared indicator */ 66779e6e1cSJan Glauber atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count); 67779e6e1cSJan Glauber return &q_indicators[TIQDIO_SHARED_IND].ind; 68779e6e1cSJan Glauber } 69779e6e1cSJan Glauber 70779e6e1cSJan Glauber static void put_indicator(u32 *addr) 71779e6e1cSJan Glauber { 72779e6e1cSJan Glauber int i; 73779e6e1cSJan Glauber 74779e6e1cSJan Glauber if (!addr) 75779e6e1cSJan Glauber return; 76779e6e1cSJan Glauber i = ((unsigned long)addr - (unsigned long)q_indicators) / 77779e6e1cSJan Glauber sizeof(struct indicator_t); 78779e6e1cSJan Glauber atomic_dec(&q_indicators[i].count); 79779e6e1cSJan Glauber } 80779e6e1cSJan Glauber 81779e6e1cSJan Glauber void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) 82779e6e1cSJan Glauber { 83779e6e1cSJan Glauber struct qdio_q *q; 84779e6e1cSJan Glauber int i; 85779e6e1cSJan Glauber 86779e6e1cSJan Glauber /* No TDD facility? If we must use SIGA-s we can also omit SVS. */ 87779e6e1cSJan Glauber if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync) 88779e6e1cSJan Glauber css_qdio_omit_svs = 1; 89779e6e1cSJan Glauber 90b4547402SJan Glauber mutex_lock(&tiq_list_lock); 91b4547402SJan Glauber for_each_input_queue(irq_ptr, q, i) 92779e6e1cSJan Glauber list_add_rcu(&q->entry, &tiq_list); 93b4547402SJan Glauber mutex_unlock(&tiq_list_lock); 94d0c9d4a8SJan Glauber xchg(irq_ptr->dsci, 1 << 7); 95779e6e1cSJan Glauber } 96779e6e1cSJan Glauber 97779e6e1cSJan Glauber void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) 98779e6e1cSJan Glauber { 99779e6e1cSJan Glauber struct qdio_q *q; 100779e6e1cSJan Glauber int i; 101779e6e1cSJan Glauber 10253b41ba7SJan Glauber for (i = 0; i < irq_ptr->nr_input_qs; i++) { 10353b41ba7SJan Glauber q = irq_ptr->input_qs[i]; 10453b41ba7SJan Glauber /* if establish triggered an error */ 10553b41ba7SJan Glauber if (!q || !q->entry.prev || !q->entry.next) 10653b41ba7SJan Glauber continue; 107b4547402SJan Glauber 108b4547402SJan Glauber mutex_lock(&tiq_list_lock); 109779e6e1cSJan Glauber list_del_rcu(&q->entry); 110b4547402SJan Glauber mutex_unlock(&tiq_list_lock); 111779e6e1cSJan Glauber synchronize_rcu(); 112779e6e1cSJan Glauber } 113779e6e1cSJan Glauber } 114779e6e1cSJan Glauber 115d36deae7SJan Glauber static inline int shared_ind_used(void) 116779e6e1cSJan Glauber { 117d36deae7SJan Glauber return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count); 118779e6e1cSJan Glauber } 119779e6e1cSJan Glauber 120cf9a031cSJan Glauber /** 121cf9a031cSJan Glauber * tiqdio_thinint_handler - thin interrupt handler for qdio 122d36deae7SJan Glauber * @alsi: pointer to adapter local summary indicator 123d36deae7SJan Glauber * @data: NULL 124cf9a031cSJan Glauber */ 125d36deae7SJan Glauber static void tiqdio_thinint_handler(void *alsi, void *data) 126779e6e1cSJan Glauber { 127779e6e1cSJan Glauber struct qdio_q *q; 128779e6e1cSJan Glauber 129d36deae7SJan Glauber last_ai_time = S390_lowcore.int_clock; 130d36deae7SJan Glauber 131cf9a031cSJan Glauber /* 132cf9a031cSJan Glauber * SVS only when needed: issue SVS to benefit from iqdio interrupt 133d36deae7SJan Glauber * avoidance (SVS clears adapter interrupt suppression overwrite). 134cf9a031cSJan Glauber */ 135cf9a031cSJan Glauber if (!css_qdio_omit_svs) 136cf9a031cSJan Glauber do_clear_global_summary(); 137cf9a031cSJan Glauber 138d36deae7SJan Glauber /* reset local summary indicator */ 139d36deae7SJan Glauber if (shared_ind_used()) 140d36deae7SJan Glauber xchg(tiqdio_alsi, 0); 141779e6e1cSJan Glauber 142779e6e1cSJan Glauber /* protect tiq_list entries, only changed in activate or shutdown */ 143779e6e1cSJan Glauber rcu_read_lock(); 144779e6e1cSJan Glauber 145cf9a031cSJan Glauber /* check for work on all inbound thinint queues */ 146d36deae7SJan Glauber list_for_each_entry_rcu(q, &tiq_list, entry) { 147779e6e1cSJan Glauber 148d36deae7SJan Glauber /* only process queues from changed sets */ 149d36deae7SJan Glauber if (!*q->irq_ptr->dsci) 150d36deae7SJan Glauber continue; 151d36deae7SJan Glauber 152d36deae7SJan Glauber if (q->u.in.queue_start_poll) { 153d36deae7SJan Glauber /* skip if polling is enabled or already in work */ 154d36deae7SJan Glauber if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, 155d36deae7SJan Glauber &q->u.in.queue_irq_state)) { 156d36deae7SJan Glauber qperf_inc(q, int_discarded); 157d36deae7SJan Glauber continue; 158d36deae7SJan Glauber } 159d36deae7SJan Glauber 160d36deae7SJan Glauber /* avoid dsci clear here, done after processing */ 161d36deae7SJan Glauber q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr, 162d36deae7SJan Glauber q->irq_ptr->int_parm); 163d36deae7SJan Glauber } else { 164779e6e1cSJan Glauber /* only clear it if the indicator is non-shared */ 165779e6e1cSJan Glauber if (!shared_ind(q->irq_ptr)) 166779e6e1cSJan Glauber xchg(q->irq_ptr->dsci, 0); 167779e6e1cSJan Glauber /* 168d36deae7SJan Glauber * Call inbound processing but not directly 169d36deae7SJan Glauber * since that could starve other thinint queues. 170779e6e1cSJan Glauber */ 171779e6e1cSJan Glauber tasklet_schedule(&q->tasklet); 172779e6e1cSJan Glauber } 173d36deae7SJan Glauber qperf_inc(q, adapter_int); 174d36deae7SJan Glauber } 175779e6e1cSJan Glauber rcu_read_unlock(); 176779e6e1cSJan Glauber 177779e6e1cSJan Glauber /* 178d36deae7SJan Glauber * If the shared indicator was used clear it now after all queues 179d36deae7SJan Glauber * were processed. 180779e6e1cSJan Glauber */ 181d36deae7SJan Glauber if (shared_ind_used()) { 182779e6e1cSJan Glauber xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); 183779e6e1cSJan Glauber 184779e6e1cSJan Glauber /* prevent racing */ 185779e6e1cSJan Glauber if (*tiqdio_alsi) 186d0c9d4a8SJan Glauber xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7); 187779e6e1cSJan Glauber } 188779e6e1cSJan Glauber } 189779e6e1cSJan Glauber 190779e6e1cSJan Glauber static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) 191779e6e1cSJan Glauber { 192779e6e1cSJan Glauber struct scssc_area *scssc_area; 193779e6e1cSJan Glauber int rc; 194779e6e1cSJan Glauber 195779e6e1cSJan Glauber scssc_area = (struct scssc_area *)irq_ptr->chsc_page; 196779e6e1cSJan Glauber memset(scssc_area, 0, PAGE_SIZE); 197779e6e1cSJan Glauber 198779e6e1cSJan Glauber if (reset) { 199779e6e1cSJan Glauber scssc_area->summary_indicator_addr = 0; 200779e6e1cSJan Glauber scssc_area->subchannel_indicator_addr = 0; 201779e6e1cSJan Glauber } else { 202779e6e1cSJan Glauber scssc_area->summary_indicator_addr = virt_to_phys(tiqdio_alsi); 203779e6e1cSJan Glauber scssc_area->subchannel_indicator_addr = 204779e6e1cSJan Glauber virt_to_phys(irq_ptr->dsci); 205779e6e1cSJan Glauber } 206779e6e1cSJan Glauber 207779e6e1cSJan Glauber scssc_area->request = (struct chsc_header) { 208779e6e1cSJan Glauber .length = 0x0fe0, 209779e6e1cSJan Glauber .code = 0x0021, 210779e6e1cSJan Glauber }; 211779e6e1cSJan Glauber scssc_area->operation_code = 0; 212d1bf8590SHeiko Carstens scssc_area->ks = PAGE_DEFAULT_KEY >> 4; 213d1bf8590SHeiko Carstens scssc_area->kc = PAGE_DEFAULT_KEY >> 4; 214779e6e1cSJan Glauber scssc_area->isc = QDIO_AIRQ_ISC; 215779e6e1cSJan Glauber scssc_area->schid = irq_ptr->schid; 216779e6e1cSJan Glauber 217779e6e1cSJan Glauber /* enable the time delay disablement facility */ 218779e6e1cSJan Glauber if (css_general_characteristics.aif_tdd) 219779e6e1cSJan Glauber scssc_area->word_with_d_bit = 0x10000000; 220779e6e1cSJan Glauber 221779e6e1cSJan Glauber rc = chsc(scssc_area); 222779e6e1cSJan Glauber if (rc) 223779e6e1cSJan Glauber return -EIO; 224779e6e1cSJan Glauber 225779e6e1cSJan Glauber rc = chsc_error_from_response(scssc_area->response.code); 226779e6e1cSJan Glauber if (rc) { 22722f99347SJan Glauber DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no, 22822f99347SJan Glauber scssc_area->response.code); 22922f99347SJan Glauber DBF_ERROR_HEX(&scssc_area->response, sizeof(void *)); 230779e6e1cSJan Glauber return rc; 231779e6e1cSJan Glauber } 232779e6e1cSJan Glauber 23322f99347SJan Glauber DBF_EVENT("setscind"); 23422f99347SJan Glauber DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long)); 23522f99347SJan Glauber DBF_HEX(&scssc_area->subchannel_indicator_addr, sizeof(unsigned long)); 236779e6e1cSJan Glauber return 0; 237779e6e1cSJan Glauber } 238779e6e1cSJan Glauber 239779e6e1cSJan Glauber /* allocate non-shared indicators and shared indicator */ 240779e6e1cSJan Glauber int __init tiqdio_allocate_memory(void) 241779e6e1cSJan Glauber { 242779e6e1cSJan Glauber q_indicators = kzalloc(sizeof(struct indicator_t) * TIQDIO_NR_INDICATORS, 243779e6e1cSJan Glauber GFP_KERNEL); 244779e6e1cSJan Glauber if (!q_indicators) 245779e6e1cSJan Glauber return -ENOMEM; 246779e6e1cSJan Glauber return 0; 247779e6e1cSJan Glauber } 248779e6e1cSJan Glauber 249779e6e1cSJan Glauber void tiqdio_free_memory(void) 250779e6e1cSJan Glauber { 251779e6e1cSJan Glauber kfree(q_indicators); 252779e6e1cSJan Glauber } 253779e6e1cSJan Glauber 254779e6e1cSJan Glauber int __init tiqdio_register_thinints(void) 255779e6e1cSJan Glauber { 256779e6e1cSJan Glauber isc_register(QDIO_AIRQ_ISC); 257779e6e1cSJan Glauber tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler, 258779e6e1cSJan Glauber NULL, QDIO_AIRQ_ISC); 259779e6e1cSJan Glauber if (IS_ERR(tiqdio_alsi)) { 26022f99347SJan Glauber DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi)); 261779e6e1cSJan Glauber tiqdio_alsi = NULL; 262779e6e1cSJan Glauber isc_unregister(QDIO_AIRQ_ISC); 263779e6e1cSJan Glauber return -ENOMEM; 264779e6e1cSJan Glauber } 265779e6e1cSJan Glauber return 0; 266779e6e1cSJan Glauber } 267779e6e1cSJan Glauber 268779e6e1cSJan Glauber int qdio_establish_thinint(struct qdio_irq *irq_ptr) 269779e6e1cSJan Glauber { 270779e6e1cSJan Glauber if (!is_thinint_irq(irq_ptr)) 271779e6e1cSJan Glauber return 0; 272779e6e1cSJan Glauber 273779e6e1cSJan Glauber /* Check for aif time delay disablement. If installed, 274779e6e1cSJan Glauber * omit SVS even under LPAR 275779e6e1cSJan Glauber */ 276779e6e1cSJan Glauber if (css_general_characteristics.aif_tdd) 277779e6e1cSJan Glauber css_qdio_omit_svs = 1; 278779e6e1cSJan Glauber return set_subchannel_ind(irq_ptr, 0); 279779e6e1cSJan Glauber } 280779e6e1cSJan Glauber 281779e6e1cSJan Glauber void qdio_setup_thinint(struct qdio_irq *irq_ptr) 282779e6e1cSJan Glauber { 283779e6e1cSJan Glauber if (!is_thinint_irq(irq_ptr)) 284779e6e1cSJan Glauber return; 285779e6e1cSJan Glauber irq_ptr->dsci = get_indicator(); 28622f99347SJan Glauber DBF_HEX(&irq_ptr->dsci, sizeof(void *)); 287779e6e1cSJan Glauber } 288779e6e1cSJan Glauber 289779e6e1cSJan Glauber void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) 290779e6e1cSJan Glauber { 291779e6e1cSJan Glauber if (!is_thinint_irq(irq_ptr)) 292779e6e1cSJan Glauber return; 293779e6e1cSJan Glauber 294779e6e1cSJan Glauber /* reset adapter interrupt indicators */ 295779e6e1cSJan Glauber set_subchannel_ind(irq_ptr, 1); 296*4814a2b3SJan Glauber put_indicator(irq_ptr->dsci); 297779e6e1cSJan Glauber } 298779e6e1cSJan Glauber 299779e6e1cSJan Glauber void __exit tiqdio_unregister_thinints(void) 300779e6e1cSJan Glauber { 3019e890ad8SJan Glauber WARN_ON(!list_empty(&tiq_list)); 302779e6e1cSJan Glauber 303779e6e1cSJan Glauber if (tiqdio_alsi) { 304779e6e1cSJan Glauber s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC); 305779e6e1cSJan Glauber isc_unregister(QDIO_AIRQ_ISC); 306779e6e1cSJan Glauber } 307779e6e1cSJan Glauber } 308