a53c8fab3f
Remove the file name from the comment at top of many files. In most cases the file name was wrong anyway, so it's rather pointless. Also unify the IBM copyright statement. We did have a lot of sightly different statements and wanted to change them one after another whenever a file gets touched. However that never happened. Instead people start to take the old/"wrong" statements to use as a template for new files. So unify all of them in one go. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
151 lines
3.5 KiB
C
151 lines
3.5 KiB
C
/*
|
|
* Support for adapter interruptions
|
|
*
|
|
* Copyright IBM Corp. 1999, 2007
|
|
* Author(s): Ingo Adlung <adlung@de.ibm.com>
|
|
* Cornelia Huck <cornelia.huck@de.ibm.com>
|
|
* Arnd Bergmann <arndb@de.ibm.com>
|
|
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/rcupdate.h>
|
|
|
|
#include <asm/airq.h>
|
|
#include <asm/isc.h>
|
|
|
|
#include "cio.h"
|
|
#include "cio_debug.h"
|
|
|
|
#define NR_AIRQS 32
|
|
#define NR_AIRQS_PER_WORD sizeof(unsigned long)
|
|
#define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD)
|
|
|
|
union indicator_t {
|
|
unsigned long word[NR_AIRQ_WORDS];
|
|
unsigned char byte[NR_AIRQS];
|
|
} __attribute__((packed));
|
|
|
|
struct airq_t {
|
|
adapter_int_handler_t handler;
|
|
void *drv_data;
|
|
};
|
|
|
|
static union indicator_t indicators[MAX_ISC+1];
|
|
static struct airq_t *airqs[MAX_ISC+1][NR_AIRQS];
|
|
|
|
static int register_airq(struct airq_t *airq, u8 isc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NR_AIRQS; i++)
|
|
if (!cmpxchg(&airqs[isc][i], NULL, airq))
|
|
return i;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/**
|
|
* s390_register_adapter_interrupt() - register adapter interrupt handler
|
|
* @handler: adapter handler to be registered
|
|
* @drv_data: driver data passed with each call to the handler
|
|
* @isc: isc for which the handler should be called
|
|
*
|
|
* Returns:
|
|
* Pointer to the indicator to be used on success
|
|
* ERR_PTR() if registration failed
|
|
*/
|
|
void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
|
|
void *drv_data, u8 isc)
|
|
{
|
|
struct airq_t *airq;
|
|
char dbf_txt[16];
|
|
int ret;
|
|
|
|
if (isc > MAX_ISC)
|
|
return ERR_PTR(-EINVAL);
|
|
airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
|
|
if (!airq) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
airq->handler = handler;
|
|
airq->drv_data = drv_data;
|
|
|
|
ret = register_airq(airq, isc);
|
|
out:
|
|
snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
|
|
CIO_TRACE_EVENT(4, dbf_txt);
|
|
if (ret < 0) {
|
|
kfree(airq);
|
|
return ERR_PTR(ret);
|
|
} else
|
|
return &indicators[isc].byte[ret];
|
|
}
|
|
EXPORT_SYMBOL(s390_register_adapter_interrupt);
|
|
|
|
/**
|
|
* s390_unregister_adapter_interrupt - unregister adapter interrupt handler
|
|
* @ind: indicator for which the handler is to be unregistered
|
|
* @isc: interruption subclass
|
|
*/
|
|
void s390_unregister_adapter_interrupt(void *ind, u8 isc)
|
|
{
|
|
struct airq_t *airq;
|
|
char dbf_txt[16];
|
|
int i;
|
|
|
|
i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]);
|
|
snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
|
|
CIO_TRACE_EVENT(4, dbf_txt);
|
|
indicators[isc].byte[i] = 0;
|
|
airq = xchg(&airqs[isc][i], NULL);
|
|
/*
|
|
* Allow interrupts to complete. This will ensure that the airq handle
|
|
* is no longer referenced by any interrupt handler.
|
|
*/
|
|
synchronize_sched();
|
|
kfree(airq);
|
|
}
|
|
EXPORT_SYMBOL(s390_unregister_adapter_interrupt);
|
|
|
|
#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
|
|
|
|
void do_adapter_IO(u8 isc)
|
|
{
|
|
int w;
|
|
int i;
|
|
unsigned long word;
|
|
struct airq_t *airq;
|
|
|
|
/*
|
|
* Access indicator array in word-sized chunks to minimize storage
|
|
* fetch operations.
|
|
*/
|
|
for (w = 0; w < NR_AIRQ_WORDS; w++) {
|
|
word = indicators[isc].word[w];
|
|
i = w * NR_AIRQS_PER_WORD;
|
|
/*
|
|
* Check bytes within word for active indicators.
|
|
*/
|
|
while (word) {
|
|
if (word & INDICATOR_MASK) {
|
|
airq = airqs[isc][i];
|
|
/* Make sure gcc reads from airqs only once. */
|
|
barrier();
|
|
if (likely(airq))
|
|
airq->handler(&indicators[isc].byte[i],
|
|
airq->drv_data);
|
|
else
|
|
/*
|
|
* Reset ill-behaved indicator.
|
|
*/
|
|
indicators[isc].byte[i] = 0;
|
|
}
|
|
word <<= 8;
|
|
i++;
|
|
}
|
|
}
|
|
}
|