1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2779e6e1cSJan Glauber /* 3a53c8fabSHeiko Carstens * Copyright IBM Corp. 2000, 2009 4779e6e1cSJan Glauber * Author(s): Utz Bacher <utz.bacher@de.ibm.com> 5779e6e1cSJan Glauber * Cornelia Huck <cornelia.huck@de.ibm.com> 6779e6e1cSJan Glauber * Jan Glauber <jang@linux.vnet.ibm.com> 7779e6e1cSJan Glauber */ 8779e6e1cSJan Glauber #include <linux/io.h> 95a0e3ad6STejun Heo #include <linux/slab.h> 1030d77c3eSJan Glauber #include <linux/kernel_stat.h> 1160063497SArun Sharma #include <linux/atomic.h> 12b2d09103SIngo Molnar #include <linux/rculist.h> 13b2d09103SIngo Molnar 14779e6e1cSJan Glauber #include <asm/debug.h> 15779e6e1cSJan Glauber #include <asm/qdio.h> 16779e6e1cSJan Glauber #include <asm/airq.h> 17779e6e1cSJan Glauber #include <asm/isc.h> 18779e6e1cSJan Glauber 19779e6e1cSJan Glauber #include "cio.h" 20779e6e1cSJan Glauber #include "ioasm.h" 21779e6e1cSJan Glauber #include "qdio.h" 22779e6e1cSJan Glauber #include "qdio_debug.h" 23779e6e1cSJan Glauber 24779e6e1cSJan Glauber /* 25779e6e1cSJan Glauber * Restriction: only 63 iqdio subchannels would have its own indicator, 26779e6e1cSJan Glauber * after that, subsequent subchannels share one indicator 27779e6e1cSJan Glauber */ 28779e6e1cSJan Glauber #define TIQDIO_NR_NONSHARED_IND 63 29779e6e1cSJan Glauber #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) 305f4026f8SJan Glauber #define TIQDIO_SHARED_IND 63 315f4026f8SJan Glauber 325f4026f8SJan Glauber /* device state change indicators */ 335f4026f8SJan Glauber struct indicator_t { 345f4026f8SJan Glauber u32 ind; /* u32 because of compare-and-swap performance */ 355f4026f8SJan Glauber atomic_t count; /* use count, 0 or 1 for non-shared indicators */ 365f4026f8SJan Glauber }; 37779e6e1cSJan Glauber 38779e6e1cSJan Glauber /* list of thin interrupt input queues */ 39779e6e1cSJan Glauber static LIST_HEAD(tiq_list); 40c4736d96SMartin Schwidefsky static DEFINE_MUTEX(tiq_list_lock); 41779e6e1cSJan Glauber 425f4026f8SJan Glauber static struct indicator_t *q_indicators; 43779e6e1cSJan Glauber 44a2b86019SJan Glauber u64 last_ai_time; 45d36deae7SJan Glauber 46779e6e1cSJan Glauber /* returns addr for the device state change indicator */ 47779e6e1cSJan Glauber static u32 *get_indicator(void) 48779e6e1cSJan Glauber { 49779e6e1cSJan Glauber int i; 50779e6e1cSJan Glauber 51779e6e1cSJan Glauber for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++) 52648a6f44SSebastian Ott if (!atomic_cmpxchg(&q_indicators[i].count, 0, 1)) 53779e6e1cSJan Glauber return &q_indicators[i].ind; 54779e6e1cSJan Glauber 55779e6e1cSJan Glauber /* use the shared indicator */ 56779e6e1cSJan Glauber atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count); 57779e6e1cSJan Glauber return &q_indicators[TIQDIO_SHARED_IND].ind; 58779e6e1cSJan Glauber } 59779e6e1cSJan Glauber 60779e6e1cSJan Glauber static void put_indicator(u32 *addr) 61779e6e1cSJan Glauber { 6230e8eb86SSebastian Ott struct indicator_t *ind = container_of(addr, struct indicator_t, ind); 63779e6e1cSJan Glauber 64779e6e1cSJan Glauber if (!addr) 65779e6e1cSJan Glauber return; 6630e8eb86SSebastian Ott atomic_dec(&ind->count); 67779e6e1cSJan Glauber } 68779e6e1cSJan Glauber 695f4026f8SJan Glauber static inline int references_shared_dsci(struct qdio_irq *irq_ptr) 705f4026f8SJan Glauber { 715f4026f8SJan Glauber return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; 725f4026f8SJan Glauber } 735f4026f8SJan Glauber 745f4026f8SJan Glauber int test_nonshared_ind(struct qdio_irq *irq_ptr) 755f4026f8SJan Glauber { 765f4026f8SJan Glauber if (!is_thinint_irq(irq_ptr)) 775f4026f8SJan Glauber return 0; 789c159bbcSJulian Wiedmann if (references_shared_dsci(irq_ptr)) 795f4026f8SJan Glauber return 0; 805f4026f8SJan Glauber if (*irq_ptr->dsci) 815f4026f8SJan Glauber return 1; 825f4026f8SJan Glauber else 835f4026f8SJan Glauber return 0; 845f4026f8SJan Glauber } 855f4026f8SJan Glauber 86b02f0c2eSJan Glauber static inline u32 clear_shared_ind(void) 87779e6e1cSJan Glauber { 88b02f0c2eSJan Glauber if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) 89b02f0c2eSJan Glauber return 0; 90b02f0c2eSJan Glauber return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); 91779e6e1cSJan Glauber } 92779e6e1cSJan Glauber 93cf9a031cSJan Glauber /** 94cf9a031cSJan Glauber * tiqdio_thinint_handler - thin interrupt handler for qdio 9572a01d0bSJulian Wiedmann * @airq: pointer to adapter interrupt descriptor 9634c636a0SSebastian Ott * @floating: flag to recognize floating vs. directed interrupts (unused) 97cf9a031cSJan Glauber */ 9830e63ef2SSebastian Ott static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) 99779e6e1cSJan Glauber { 100*bd839171SJulian Wiedmann u64 irq_time = S390_lowcore.int_clock; 101b02f0c2eSJan Glauber u32 si_used = clear_shared_ind(); 10294c43bdaSJulian Wiedmann struct qdio_irq *irq; 103779e6e1cSJan Glauber 104*bd839171SJulian Wiedmann last_ai_time = irq_time; 105420f42ecSHeiko Carstens inc_irq_stat(IRQIO_QAI); 106d36deae7SJan Glauber 107779e6e1cSJan Glauber /* protect tiq_list entries, only changed in activate or shutdown */ 108779e6e1cSJan Glauber rcu_read_lock(); 109779e6e1cSJan Glauber 11094c43bdaSJulian Wiedmann list_for_each_entry_rcu(irq, &tiq_list, entry) { 111d36deae7SJan Glauber /* only process queues from changed sets */ 112104ea556Sfrank.blaschka@de.ibm.com if (unlikely(references_shared_dsci(irq))) { 1134f325184SJan Glauber if (!si_used) 1144f325184SJan Glauber continue; 1151ecbcfd5SJulian Wiedmann } else { 1161ecbcfd5SJulian Wiedmann if (!*irq->dsci) 117d36deae7SJan Glauber continue; 118d36deae7SJan Glauber 1191ecbcfd5SJulian Wiedmann xchg(irq->dsci, 0); 1201ecbcfd5SJulian Wiedmann } 1211ecbcfd5SJulian Wiedmann 1221ecbcfd5SJulian Wiedmann qdio_deliver_irq(irq); 123*bd839171SJulian Wiedmann irq->last_data_irq_time = irq_time; 124d36deae7SJan Glauber 12546112810SJulian Wiedmann QDIO_PERF_STAT_INC(irq, adapter_int); 126d36deae7SJan Glauber } 127779e6e1cSJan Glauber rcu_read_unlock(); 128779e6e1cSJan Glauber } 129779e6e1cSJan Glauber 130d86f71fdSJulian Wiedmann static struct airq_struct tiqdio_airq = { 131d86f71fdSJulian Wiedmann .handler = tiqdio_thinint_handler, 132d86f71fdSJulian Wiedmann .isc = QDIO_AIRQ_ISC, 133d86f71fdSJulian Wiedmann }; 134d86f71fdSJulian Wiedmann 135779e6e1cSJan Glauber static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) 136779e6e1cSJan Glauber { 137ca4ba153SSebastian Ott struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page; 138ca4ba153SSebastian Ott u64 summary_indicator_addr, subchannel_indicator_addr; 139779e6e1cSJan Glauber int rc; 140779e6e1cSJan Glauber 141779e6e1cSJan Glauber if (reset) { 142ca4ba153SSebastian Ott summary_indicator_addr = 0; 143ca4ba153SSebastian Ott subchannel_indicator_addr = 0; 144779e6e1cSJan Glauber } else { 145f4eae94fSMartin Schwidefsky summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr); 146ca4ba153SSebastian Ott subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci); 147779e6e1cSJan Glauber } 148779e6e1cSJan Glauber 149ca4ba153SSebastian Ott rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr, 15092892240SJulian Wiedmann subchannel_indicator_addr, tiqdio_airq.isc); 151779e6e1cSJan Glauber if (rc) { 15222f99347SJan Glauber DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no, 153ca4ba153SSebastian Ott scssc->response.code); 154ca4ba153SSebastian Ott goto out; 155779e6e1cSJan Glauber } 156779e6e1cSJan Glauber 15722f99347SJan Glauber DBF_EVENT("setscind"); 158ca4ba153SSebastian Ott DBF_HEX(&summary_indicator_addr, sizeof(summary_indicator_addr)); 159ca4ba153SSebastian Ott DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr)); 160ca4ba153SSebastian Ott out: 161ca4ba153SSebastian Ott return rc; 162779e6e1cSJan Glauber } 163779e6e1cSJan Glauber 164779e6e1cSJan Glauber int qdio_establish_thinint(struct qdio_irq *irq_ptr) 165779e6e1cSJan Glauber { 16675e82becSJulian Wiedmann int rc; 16775e82becSJulian Wiedmann 168779e6e1cSJan Glauber if (!is_thinint_irq(irq_ptr)) 169779e6e1cSJan Glauber return 0; 170779e6e1cSJan Glauber 171779e6e1cSJan Glauber irq_ptr->dsci = get_indicator(); 17222f99347SJan Glauber DBF_HEX(&irq_ptr->dsci, sizeof(void *)); 17375e82becSJulian Wiedmann 17475e82becSJulian Wiedmann rc = set_subchannel_ind(irq_ptr, 0); 175954d6235SJulian Wiedmann if (rc) { 17675e82becSJulian Wiedmann put_indicator(irq_ptr->dsci); 17775e82becSJulian Wiedmann return rc; 178779e6e1cSJan Glauber } 179779e6e1cSJan Glauber 180954d6235SJulian Wiedmann mutex_lock(&tiq_list_lock); 181954d6235SJulian Wiedmann list_add_rcu(&irq_ptr->entry, &tiq_list); 182954d6235SJulian Wiedmann mutex_unlock(&tiq_list_lock); 183954d6235SJulian Wiedmann return 0; 184954d6235SJulian Wiedmann } 185954d6235SJulian Wiedmann 186779e6e1cSJan Glauber void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) 187779e6e1cSJan Glauber { 188779e6e1cSJan Glauber if (!is_thinint_irq(irq_ptr)) 189779e6e1cSJan Glauber return; 190779e6e1cSJan Glauber 191954d6235SJulian Wiedmann mutex_lock(&tiq_list_lock); 192954d6235SJulian Wiedmann list_del_rcu(&irq_ptr->entry); 193954d6235SJulian Wiedmann mutex_unlock(&tiq_list_lock); 194954d6235SJulian Wiedmann synchronize_rcu(); 195954d6235SJulian Wiedmann 196779e6e1cSJan Glauber /* reset adapter interrupt indicators */ 197779e6e1cSJan Glauber set_subchannel_ind(irq_ptr, 1); 1984814a2b3SJan Glauber put_indicator(irq_ptr->dsci); 199779e6e1cSJan Glauber } 200779e6e1cSJan Glauber 2013050f022SJulian Wiedmann int __init qdio_thinint_init(void) 2023050f022SJulian Wiedmann { 2033050f022SJulian Wiedmann int rc; 2043050f022SJulian Wiedmann 2053050f022SJulian Wiedmann q_indicators = kcalloc(TIQDIO_NR_INDICATORS, sizeof(struct indicator_t), 2063050f022SJulian Wiedmann GFP_KERNEL); 2073050f022SJulian Wiedmann if (!q_indicators) 2083050f022SJulian Wiedmann return -ENOMEM; 2093050f022SJulian Wiedmann 2103050f022SJulian Wiedmann rc = register_adapter_interrupt(&tiqdio_airq); 2113050f022SJulian Wiedmann if (rc) { 2123050f022SJulian Wiedmann DBF_EVENT("RTI:%x", rc); 2133050f022SJulian Wiedmann kfree(q_indicators); 2143050f022SJulian Wiedmann return rc; 2153050f022SJulian Wiedmann } 2163050f022SJulian Wiedmann return 0; 2173050f022SJulian Wiedmann } 2183050f022SJulian Wiedmann 2193050f022SJulian Wiedmann void __exit qdio_thinint_exit(void) 220779e6e1cSJan Glauber { 2219e890ad8SJan Glauber WARN_ON(!list_empty(&tiq_list)); 222f4eae94fSMartin Schwidefsky unregister_adapter_interrupt(&tiqdio_airq); 2233050f022SJulian Wiedmann kfree(q_indicators); 224779e6e1cSJan Glauber } 225