PageRenderTime 77ms CodeModel.GetById 29ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/s390/cio/qdio_thinint.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 268 lines | 192 code | 41 blank | 35 comment | 27 complexity | 81ab51344b0c4f2e386d1ba07c0e3fce MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1/*
  2 * linux/drivers/s390/cio/thinint_qdio.c
  3 *
  4 * Copyright 2000,2009 IBM Corp.
  5 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
  6 *	      Cornelia Huck <cornelia.huck@de.ibm.com>
  7 *	      Jan Glauber <jang@linux.vnet.ibm.com>
  8 */
  9#include <linux/io.h>
 10#include <linux/slab.h>
 11#include <linux/kernel_stat.h>
 12#include <linux/atomic.h>
 13#include <asm/debug.h>
 14#include <asm/qdio.h>
 15#include <asm/airq.h>
 16#include <asm/isc.h>
 17
 18#include "cio.h"
 19#include "ioasm.h"
 20#include "qdio.h"
 21#include "qdio_debug.h"
 22
 23/*
 24 * Restriction: only 63 iqdio subchannels would have its own indicator,
 25 * after that, subsequent subchannels share one indicator
 26 */
 27#define TIQDIO_NR_NONSHARED_IND		63
 28#define TIQDIO_NR_INDICATORS		(TIQDIO_NR_NONSHARED_IND + 1)
 29
 30/* list of thin interrupt input queues */
 31static LIST_HEAD(tiq_list);
 32DEFINE_MUTEX(tiq_list_lock);
 33
 34/* adapter local summary indicator */
 35static u8 *tiqdio_alsi;
 36
 37struct indicator_t *q_indicators;
 38
 39static u64 last_ai_time;
 40
 41/* returns addr for the device state change indicator */
 42static u32 *get_indicator(void)
 43{
 44	int i;
 45
 46	for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++)
 47		if (!atomic_read(&q_indicators[i].count)) {
 48			atomic_set(&q_indicators[i].count, 1);
 49			return &q_indicators[i].ind;
 50		}
 51
 52	/* use the shared indicator */
 53	atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count);
 54	return &q_indicators[TIQDIO_SHARED_IND].ind;
 55}
 56
 57static void put_indicator(u32 *addr)
 58{
 59	int i;
 60
 61	if (!addr)
 62		return;
 63	i = ((unsigned long)addr - (unsigned long)q_indicators) /
 64		sizeof(struct indicator_t);
 65	atomic_dec(&q_indicators[i].count);
 66}
 67
 68void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
 69{
 70	struct qdio_q *q;
 71	int i;
 72
 73	mutex_lock(&tiq_list_lock);
 74	for_each_input_queue(irq_ptr, q, i)
 75		list_add_rcu(&q->entry, &tiq_list);
 76	mutex_unlock(&tiq_list_lock);
 77	xchg(irq_ptr->dsci, 1 << 7);
 78}
 79
 80void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
 81{
 82	struct qdio_q *q;
 83	int i;
 84
 85	for (i = 0; i < irq_ptr->nr_input_qs; i++) {
 86		q = irq_ptr->input_qs[i];
 87		/* if establish triggered an error */
 88		if (!q || !q->entry.prev || !q->entry.next)
 89			continue;
 90
 91		mutex_lock(&tiq_list_lock);
 92		list_del_rcu(&q->entry);
 93		mutex_unlock(&tiq_list_lock);
 94		synchronize_rcu();
 95	}
 96}
 97
 98static inline u32 clear_shared_ind(void)
 99{
100	if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
101		return 0;
102	return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
103}
104
105/**
106 * tiqdio_thinint_handler - thin interrupt handler for qdio
107 * @alsi: pointer to adapter local summary indicator
108 * @data: NULL
109 */
110static void tiqdio_thinint_handler(void *alsi, void *data)
111{
112	u32 si_used = clear_shared_ind();
113	struct qdio_q *q;
114
115	last_ai_time = S390_lowcore.int_clock;
116	kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++;
117
118	/* protect tiq_list entries, only changed in activate or shutdown */
119	rcu_read_lock();
120
121	/* check for work on all inbound thinint queues */
122	list_for_each_entry_rcu(q, &tiq_list, entry) {
123
124		/* only process queues from changed sets */
125		if (unlikely(shared_ind(q->irq_ptr->dsci))) {
126			if (!si_used)
127				continue;
128		} else if (!*q->irq_ptr->dsci)
129			continue;
130
131		if (q->u.in.queue_start_poll) {
132			/* skip if polling is enabled or already in work */
133			if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
134					     &q->u.in.queue_irq_state)) {
135				qperf_inc(q, int_discarded);
136				continue;
137			}
138
139			/* avoid dsci clear here, done after processing */
140			q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
141						 q->irq_ptr->int_parm);
142		} else {
143			/* only clear it if the indicator is non-shared */
144			if (!shared_ind(q->irq_ptr->dsci))
145				xchg(q->irq_ptr->dsci, 0);
146			/*
147			 * Call inbound processing but not directly
148			 * since that could starve other thinint queues.
149			 */
150			tasklet_schedule(&q->tasklet);
151		}
152		qperf_inc(q, adapter_int);
153	}
154	rcu_read_unlock();
155}
156
157static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
158{
159	struct scssc_area *scssc_area;
160	int rc;
161
162	scssc_area = (struct scssc_area *)irq_ptr->chsc_page;
163	memset(scssc_area, 0, PAGE_SIZE);
164
165	if (reset) {
166		scssc_area->summary_indicator_addr = 0;
167		scssc_area->subchannel_indicator_addr = 0;
168	} else {
169		scssc_area->summary_indicator_addr = virt_to_phys(tiqdio_alsi);
170		scssc_area->subchannel_indicator_addr =
171			virt_to_phys(irq_ptr->dsci);
172	}
173
174	scssc_area->request = (struct chsc_header) {
175		.length = 0x0fe0,
176		.code	= 0x0021,
177	};
178	scssc_area->operation_code = 0;
179	scssc_area->ks = PAGE_DEFAULT_KEY >> 4;
180	scssc_area->kc = PAGE_DEFAULT_KEY >> 4;
181	scssc_area->isc = QDIO_AIRQ_ISC;
182	scssc_area->schid = irq_ptr->schid;
183
184	/* enable the time delay disablement facility */
185	if (css_general_characteristics.aif_tdd)
186		scssc_area->word_with_d_bit = 0x10000000;
187
188	rc = chsc(scssc_area);
189	if (rc)
190		return -EIO;
191
192	rc = chsc_error_from_response(scssc_area->response.code);
193	if (rc) {
194		DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
195			  scssc_area->response.code);
196		DBF_ERROR_HEX(&scssc_area->response, sizeof(void *));
197		return rc;
198	}
199
200	DBF_EVENT("setscind");
201	DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long));
202	DBF_HEX(&scssc_area->subchannel_indicator_addr,	sizeof(unsigned long));
203	return 0;
204}
205
206/* allocate non-shared indicators and shared indicator */
207int __init tiqdio_allocate_memory(void)
208{
209	q_indicators = kzalloc(sizeof(struct indicator_t) * TIQDIO_NR_INDICATORS,
210			     GFP_KERNEL);
211	if (!q_indicators)
212		return -ENOMEM;
213	return 0;
214}
215
216void tiqdio_free_memory(void)
217{
218	kfree(q_indicators);
219}
220
221int __init tiqdio_register_thinints(void)
222{
223	isc_register(QDIO_AIRQ_ISC);
224	tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler,
225						      NULL, QDIO_AIRQ_ISC);
226	if (IS_ERR(tiqdio_alsi)) {
227		DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi));
228		tiqdio_alsi = NULL;
229		isc_unregister(QDIO_AIRQ_ISC);
230		return -ENOMEM;
231	}
232	return 0;
233}
234
235int qdio_establish_thinint(struct qdio_irq *irq_ptr)
236{
237	if (!is_thinint_irq(irq_ptr))
238		return 0;
239	return set_subchannel_ind(irq_ptr, 0);
240}
241
242void qdio_setup_thinint(struct qdio_irq *irq_ptr)
243{
244	if (!is_thinint_irq(irq_ptr))
245		return;
246	irq_ptr->dsci = get_indicator();
247	DBF_HEX(&irq_ptr->dsci, sizeof(void *));
248}
249
250void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
251{
252	if (!is_thinint_irq(irq_ptr))
253		return;
254
255	/* reset adapter interrupt indicators */
256	set_subchannel_ind(irq_ptr, 1);
257	put_indicator(irq_ptr->dsci);
258}
259
260void __exit tiqdio_unregister_thinints(void)
261{
262	WARN_ON(!list_empty(&tiq_list));
263
264	if (tiqdio_alsi) {
265		s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC);
266		isc_unregister(QDIO_AIRQ_ISC);
267	}
268}