1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright IBM Corp. 2000, 2009
4 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
5 * Cornelia Huck <cornelia.huck@de.ibm.com>
6 * Jan Glauber <jang@linux.vnet.ibm.com>
9 #include <linux/slab.h>
10 #include <linux/kernel_stat.h>
11 #include <linux/atomic.h>
12 #include <linux/rculist.h>
14 #include <asm/debug.h>
23 #include "qdio_debug.h"
26 * Restriction: only 63 iqdio subchannels would have its own indicator,
27 * after that, subsequent subchannels share one indicator
29 #define TIQDIO_NR_NONSHARED_IND 63
30 #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1)
31 #define TIQDIO_SHARED_IND 63
33 /* device state change indicators */
35 u32 ind
; /* u32 because of compare-and-swap performance */
36 atomic_t count
; /* use count, 0 or 1 for non-shared indicators */
39 /* list of thin interrupt input queues */
40 static LIST_HEAD(tiq_list
);
41 static DEFINE_MUTEX(tiq_list_lock
);
43 static struct indicator_t
*q_indicators
;
47 /* returns addr for the device state change indicator */
48 static u32
*get_indicator(void)
52 for (i
= 0; i
< TIQDIO_NR_NONSHARED_IND
; i
++)
53 if (!atomic_cmpxchg(&q_indicators
[i
].count
, 0, 1))
54 return &q_indicators
[i
].ind
;
56 /* use the shared indicator */
57 atomic_inc(&q_indicators
[TIQDIO_SHARED_IND
].count
);
58 return &q_indicators
[TIQDIO_SHARED_IND
].ind
;
61 static void put_indicator(u32
*addr
)
63 struct indicator_t
*ind
= container_of(addr
, struct indicator_t
, ind
);
67 atomic_dec(&ind
->count
);
70 static inline int references_shared_dsci(struct qdio_irq
*irq_ptr
)
72 return irq_ptr
->dsci
== &q_indicators
[TIQDIO_SHARED_IND
].ind
;
75 int test_nonshared_ind(struct qdio_irq
*irq_ptr
)
77 if (!is_thinint_irq(irq_ptr
))
79 if (references_shared_dsci(irq_ptr
))
87 static inline u32
clear_shared_ind(void)
89 if (!atomic_read(&q_indicators
[TIQDIO_SHARED_IND
].count
))
91 return xchg(&q_indicators
[TIQDIO_SHARED_IND
].ind
, 0);
95 * tiqdio_thinint_handler - thin interrupt handler for qdio
96 * @airq: pointer to adapter interrupt descriptor
97 * @tpi_info: interrupt information (e.g. floating vs directed -- unused)
99 static void tiqdio_thinint_handler(struct airq_struct
*airq
,
100 struct tpi_info
*tpi_info
)
102 u64 irq_time
= get_lowcore()->int_clock
;
103 u32 si_used
= clear_shared_ind();
104 struct qdio_irq
*irq
;
106 last_ai_time
= irq_time
;
107 inc_irq_stat(IRQIO_QAI
);
109 /* protect tiq_list entries, only changed in activate or shutdown */
112 list_for_each_entry_rcu(irq
, &tiq_list
, entry
) {
113 /* only process queues from changed sets */
114 if (unlikely(references_shared_dsci(irq
))) {
124 qdio_deliver_irq(irq
);
125 irq
->last_data_irq_time
= irq_time
;
127 QDIO_PERF_STAT_INC(irq
, adapter_int
);
132 static struct airq_struct tiqdio_airq
= {
133 .handler
= tiqdio_thinint_handler
,
134 .isc
= QDIO_AIRQ_ISC
,
137 static int set_subchannel_ind(struct qdio_irq
*irq_ptr
, int reset
)
139 struct chsc_scssc_area
*scssc
= (void *)irq_ptr
->chsc_page
;
140 dma64_t summary_indicator_addr
, subchannel_indicator_addr
;
144 summary_indicator_addr
= 0;
145 subchannel_indicator_addr
= 0;
147 summary_indicator_addr
= virt_to_dma64(tiqdio_airq
.lsi_ptr
);
148 subchannel_indicator_addr
= virt_to_dma64(irq_ptr
->dsci
);
151 rc
= chsc_sadc(irq_ptr
->schid
, scssc
, summary_indicator_addr
,
152 subchannel_indicator_addr
, tiqdio_airq
.isc
);
154 DBF_ERROR("%4x SSI r:%4x", irq_ptr
->schid
.sch_no
,
155 scssc
->response
.code
);
159 DBF_EVENT("setscind");
160 DBF_HEX(&summary_indicator_addr
, sizeof(summary_indicator_addr
));
161 DBF_HEX(&subchannel_indicator_addr
, sizeof(subchannel_indicator_addr
));
166 int qdio_establish_thinint(struct qdio_irq
*irq_ptr
)
170 if (!is_thinint_irq(irq_ptr
))
173 irq_ptr
->dsci
= get_indicator();
174 DBF_HEX(&irq_ptr
->dsci
, sizeof(void *));
176 rc
= set_subchannel_ind(irq_ptr
, 0);
178 put_indicator(irq_ptr
->dsci
);
182 mutex_lock(&tiq_list_lock
);
183 list_add_rcu(&irq_ptr
->entry
, &tiq_list
);
184 mutex_unlock(&tiq_list_lock
);
188 void qdio_shutdown_thinint(struct qdio_irq
*irq_ptr
)
190 if (!is_thinint_irq(irq_ptr
))
193 mutex_lock(&tiq_list_lock
);
194 list_del_rcu(&irq_ptr
->entry
);
195 mutex_unlock(&tiq_list_lock
);
198 /* reset adapter interrupt indicators */
199 set_subchannel_ind(irq_ptr
, 1);
200 put_indicator(irq_ptr
->dsci
);
203 int __init
qdio_thinint_init(void)
207 q_indicators
= kcalloc(TIQDIO_NR_INDICATORS
, sizeof(struct indicator_t
),
212 rc
= register_adapter_interrupt(&tiqdio_airq
);
214 DBF_EVENT("RTI:%x", rc
);
221 void __exit
qdio_thinint_exit(void)
223 WARN_ON(!list_empty(&tiq_list
));
224 unregister_adapter_interrupt(&tiqdio_airq
);