0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/io.h>
0009 #include <linux/slab.h>
0010 #include <linux/kernel_stat.h>
0011 #include <linux/atomic.h>
0012 #include <linux/rculist.h>
0013
0014 #include <asm/debug.h>
0015 #include <asm/qdio.h>
0016 #include <asm/airq.h>
0017 #include <asm/isc.h>
0018 #include <asm/tpi.h>
0019
0020 #include "cio.h"
0021 #include "ioasm.h"
0022 #include "qdio.h"
0023 #include "qdio_debug.h"
0024
0025
0026
0027
0028
0029 #define TIQDIO_NR_NONSHARED_IND 63
0030 #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1)
0031 #define TIQDIO_SHARED_IND 63
0032
0033
0034 struct indicator_t {
0035 u32 ind;
0036 atomic_t count;
0037 };
0038
0039
0040 static LIST_HEAD(tiq_list);
0041 static DEFINE_MUTEX(tiq_list_lock);
0042
0043 static struct indicator_t *q_indicators;
0044
0045 u64 last_ai_time;
0046
0047
0048 static u32 *get_indicator(void)
0049 {
0050 int i;
0051
0052 for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++)
0053 if (!atomic_cmpxchg(&q_indicators[i].count, 0, 1))
0054 return &q_indicators[i].ind;
0055
0056
0057 atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count);
0058 return &q_indicators[TIQDIO_SHARED_IND].ind;
0059 }
0060
0061 static void put_indicator(u32 *addr)
0062 {
0063 struct indicator_t *ind = container_of(addr, struct indicator_t, ind);
0064
0065 if (!addr)
0066 return;
0067 atomic_dec(&ind->count);
0068 }
0069
0070 static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
0071 {
0072 return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
0073 }
0074
0075 int test_nonshared_ind(struct qdio_irq *irq_ptr)
0076 {
0077 if (!is_thinint_irq(irq_ptr))
0078 return 0;
0079 if (references_shared_dsci(irq_ptr))
0080 return 0;
0081 if (*irq_ptr->dsci)
0082 return 1;
0083 else
0084 return 0;
0085 }
0086
0087 static inline u32 clear_shared_ind(void)
0088 {
0089 if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
0090 return 0;
0091 return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
0092 }
0093
0094
0095
0096
0097
0098
0099 static void tiqdio_thinint_handler(struct airq_struct *airq,
0100 struct tpi_info *tpi_info)
0101 {
0102 u64 irq_time = S390_lowcore.int_clock;
0103 u32 si_used = clear_shared_ind();
0104 struct qdio_irq *irq;
0105
0106 last_ai_time = irq_time;
0107 inc_irq_stat(IRQIO_QAI);
0108
0109
0110 rcu_read_lock();
0111
0112 list_for_each_entry_rcu(irq, &tiq_list, entry) {
0113
0114 if (unlikely(references_shared_dsci(irq))) {
0115 if (!si_used)
0116 continue;
0117 } else {
0118 if (!*irq->dsci)
0119 continue;
0120
0121 xchg(irq->dsci, 0);
0122 }
0123
0124 qdio_deliver_irq(irq);
0125 irq->last_data_irq_time = irq_time;
0126
0127 QDIO_PERF_STAT_INC(irq, adapter_int);
0128 }
0129 rcu_read_unlock();
0130 }
0131
0132 static struct airq_struct tiqdio_airq = {
0133 .handler = tiqdio_thinint_handler,
0134 .isc = QDIO_AIRQ_ISC,
0135 };
0136
0137 static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
0138 {
0139 struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page;
0140 u64 summary_indicator_addr, subchannel_indicator_addr;
0141 int rc;
0142
0143 if (reset) {
0144 summary_indicator_addr = 0;
0145 subchannel_indicator_addr = 0;
0146 } else {
0147 summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr);
0148 subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci);
0149 }
0150
0151 rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr,
0152 subchannel_indicator_addr, tiqdio_airq.isc);
0153 if (rc) {
0154 DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
0155 scssc->response.code);
0156 goto out;
0157 }
0158
0159 DBF_EVENT("setscind");
0160 DBF_HEX(&summary_indicator_addr, sizeof(summary_indicator_addr));
0161 DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr));
0162 out:
0163 return rc;
0164 }
0165
0166 int qdio_establish_thinint(struct qdio_irq *irq_ptr)
0167 {
0168 int rc;
0169
0170 if (!is_thinint_irq(irq_ptr))
0171 return 0;
0172
0173 irq_ptr->dsci = get_indicator();
0174 DBF_HEX(&irq_ptr->dsci, sizeof(void *));
0175
0176 rc = set_subchannel_ind(irq_ptr, 0);
0177 if (rc) {
0178 put_indicator(irq_ptr->dsci);
0179 return rc;
0180 }
0181
0182 mutex_lock(&tiq_list_lock);
0183 list_add_rcu(&irq_ptr->entry, &tiq_list);
0184 mutex_unlock(&tiq_list_lock);
0185 return 0;
0186 }
0187
0188 void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
0189 {
0190 if (!is_thinint_irq(irq_ptr))
0191 return;
0192
0193 mutex_lock(&tiq_list_lock);
0194 list_del_rcu(&irq_ptr->entry);
0195 mutex_unlock(&tiq_list_lock);
0196 synchronize_rcu();
0197
0198
0199 set_subchannel_ind(irq_ptr, 1);
0200 put_indicator(irq_ptr->dsci);
0201 }
0202
0203 int __init qdio_thinint_init(void)
0204 {
0205 int rc;
0206
0207 q_indicators = kcalloc(TIQDIO_NR_INDICATORS, sizeof(struct indicator_t),
0208 GFP_KERNEL);
0209 if (!q_indicators)
0210 return -ENOMEM;
0211
0212 rc = register_adapter_interrupt(&tiqdio_airq);
0213 if (rc) {
0214 DBF_EVENT("RTI:%x", rc);
0215 kfree(q_indicators);
0216 return rc;
0217 }
0218 return 0;
0219 }
0220
0221 void __exit qdio_thinint_exit(void)
0222 {
0223 WARN_ON(!list_empty(&tiq_list));
0224 unregister_adapter_interrupt(&tiqdio_airq);
0225 kfree(q_indicators);
0226 }