11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  drivers/s390/cio/airq.c
34e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *    Support for adapter interruptions
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
54e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *    Copyright IBM Corp. 1999,2007
64e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *    Author(s): Ingo Adlung <adlung@de.ibm.com>
74e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *		 Cornelia Huck <cornelia.huck@de.ibm.com>
84e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *		 Arnd Bergmann <arndb@de.ibm.com>
94e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/rcupdate.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
174e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter#include <asm/airq.h>
18da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck#include <asm/isc.h>
194e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter
204e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter#include "cio.h"
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cio_debug.h"
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
234e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter#define NR_AIRQS		32
244e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter#define NR_AIRQS_PER_WORD	sizeof(unsigned long)
254e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter#define NR_AIRQ_WORDS		(NR_AIRQS / NR_AIRQS_PER_WORD)
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
274e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiterunion indicator_t {
284e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	unsigned long word[NR_AIRQ_WORDS];
294e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	unsigned char byte[NR_AIRQS];
304e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter} __attribute__((packed));
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
324e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiterstruct airq_t {
334e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	adapter_int_handler_t handler;
344e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	void *drv_data;
354e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter};
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
37a1f640734ab57d548a3fdadad6b869da534d4ecbSebastian Ottstatic union indicator_t indicators[MAX_ISC+1];
38a1f640734ab57d548a3fdadad6b869da534d4ecbSebastian Ottstatic struct airq_t *airqs[MAX_ISC+1][NR_AIRQS];
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
40da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huckstatic int register_airq(struct airq_t *airq, u8 isc)
414e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter{
424e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	int i;
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
444e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	for (i = 0; i < NR_AIRQS; i++)
45da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck		if (!cmpxchg(&airqs[isc][i], NULL, airq))
464e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter			return i;
474e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	return -ENOMEM;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
504e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter/**
514e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter * s390_register_adapter_interrupt() - register adapter interrupt handler
524e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter * @handler: adapter handler to be registered
534e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter * @drv_data: driver data passed with each call to the handler
54da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck * @isc: isc for which the handler should be called
554e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *
564e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter * Returns:
574e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *  Pointer to the indicator to be used on success
584e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter *  ERR_PTR() if registration failed
594e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter */
604e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleitervoid *s390_register_adapter_interrupt(adapter_int_handler_t handler,
61da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck				      void *drv_data, u8 isc)
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
634e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	struct airq_t *airq;
644e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	char dbf_txt[16];
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
67da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck	if (isc > MAX_ISC)
68da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck		return ERR_PTR(-EINVAL);
694e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
704e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	if (!airq) {
714e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		ret = -ENOMEM;
724e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		goto out;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
744e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	airq->handler = handler;
754e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	airq->drv_data = drv_data;
76da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck
77da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck	ret = register_airq(airq, isc);
784e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiterout:
794e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
804e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	CIO_TRACE_EVENT(4, dbf_txt);
81da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck	if (ret < 0) {
82da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck		kfree(airq);
834e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		return ERR_PTR(ret);
84da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck	} else
85da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck		return &indicators[isc].byte[ret];
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
874e8e56c6713398f417317d449f50c08bf2756c66Peter OberparleiterEXPORT_SYMBOL(s390_register_adapter_interrupt);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
894e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter/**
904e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter * s390_unregister_adapter_interrupt - unregister adapter interrupt handler
914e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter * @ind: indicator for which the handler is to be unregistered
92da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck * @isc: interruption subclass
934e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter */
94da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huckvoid s390_unregister_adapter_interrupt(void *ind, u8 isc)
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
964e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	struct airq_t *airq;
974e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	char dbf_txt[16];
984e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	int i;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
100da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck	i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]);
1014e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
1024e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	CIO_TRACE_EVENT(4, dbf_txt);
103da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck	indicators[isc].byte[i] = 0;
104da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck	airq = xchg(&airqs[isc][i], NULL);
1054e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	/*
1064e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	 * Allow interrupts to complete. This will ensure that the airq handle
1074e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	 * is no longer referenced by any interrupt handler.
1084e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	 */
1094e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	synchronize_sched();
1104e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	kfree(airq);
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1124e8e56c6713398f417317d449f50c08bf2756c66Peter OberparleiterEXPORT_SYMBOL(s390_unregister_adapter_interrupt);
1134e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter
1144e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter#define INDICATOR_MASK	(0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
116da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huckvoid do_adapter_IO(u8 isc)
1174e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter{
1184e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	int w;
1194e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	int i;
1204e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	unsigned long word;
1214e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	struct airq_t *airq;
1224e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter
1234e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	/*
1244e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	 * Access indicator array in word-sized chunks to minimize storage
1254e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	 * fetch operations.
1264e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	 */
1274e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	for (w = 0; w < NR_AIRQ_WORDS; w++) {
128da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck		word = indicators[isc].word[w];
1294e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		i = w * NR_AIRQS_PER_WORD;
1304e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		/*
1314e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		 * Check bytes within word for active indicators.
1324e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		 */
1334e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		while (word) {
1344e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter			if (word & INDICATOR_MASK) {
135da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck				airq = airqs[isc][i];
13687fa5af80cdd5053b27a546725948c2b74ec82b2Heiko Carstens				/* Make sure gcc reads from airqs only once. */
13787fa5af80cdd5053b27a546725948c2b74ec82b2Heiko Carstens				barrier();
1384e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter				if (likely(airq))
139da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck					airq->handler(&indicators[isc].byte[i],
1404e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter						      airq->drv_data);
1414e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter				else
1424e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter					/*
1434e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter					 * Reset ill-behaved indicator.
1444e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter					 */
145da7c5af82879828409f6b81431ac2f9f353ab04eCornelia Huck					indicators[isc].byte[i] = 0;
1464e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter			}
1474e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter			word <<= 8;
1484e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter			i++;
1494e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter		}
1504e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter	}
1514e8e56c6713398f417317d449f50c08bf2756c66Peter Oberparleiter}
152