2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2008-01-26 21:10:44 +08:00
|
|
|
* Support for adapter interruptions
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2012-07-20 17:15:04 +08:00
|
|
|
* Copyright IBM Corp. 1999, 2007
|
2008-01-26 21:10:44 +08:00
|
|
|
* 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>
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/init.h>
|
2013-06-24 16:30:41 +08:00
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/kernel_stat.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/module.h>
|
2013-06-24 16:30:41 +08:00
|
|
|
#include <linux/mutex.h>
|
|
|
|
#include <linux/rculist.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/slab.h>
|
|
|
|
|
2008-01-26 21:10:44 +08:00
|
|
|
#include <asm/airq.h>
|
2008-07-14 15:58:59 +08:00
|
|
|
#include <asm/isc.h>
|
2008-01-26 21:10:44 +08:00
|
|
|
|
|
|
|
#include "cio.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "cio_debug.h"
|
2013-06-24 16:30:41 +08:00
|
|
|
#include "ioasm.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-06-24 16:30:41 +08:00
|
|
|
static DEFINE_SPINLOCK(airq_lists_lock);
|
|
|
|
static struct hlist_head airq_lists[MAX_ISC+1];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-01-26 21:10:44 +08:00
|
|
|
/**
|
2013-06-24 16:30:41 +08:00
|
|
|
* register_adapter_interrupt() - register adapter interrupt handler
|
|
|
|
* @airq: pointer to adapter interrupt descriptor
|
2008-01-26 21:10:44 +08:00
|
|
|
*
|
2013-06-24 16:30:41 +08:00
|
|
|
* Returns 0 on success, or -EINVAL.
|
2008-01-26 21:10:44 +08:00
|
|
|
*/
|
2013-06-24 16:30:41 +08:00
|
|
|
int register_adapter_interrupt(struct airq_struct *airq)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-06-24 16:30:41 +08:00
|
|
|
char dbf_txt[32];
|
|
|
|
|
|
|
|
if (!airq->handler || airq->isc > MAX_ISC)
|
|
|
|
return -EINVAL;
|
|
|
|
if (!airq->lsi_ptr) {
|
|
|
|
airq->lsi_ptr = kzalloc(1, GFP_KERNEL);
|
|
|
|
if (!airq->lsi_ptr)
|
|
|
|
return -ENOMEM;
|
|
|
|
airq->flags |= AIRQ_PTR_ALLOCATED;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-06-24 16:30:41 +08:00
|
|
|
if (!airq->lsi_mask)
|
|
|
|
airq->lsi_mask = 0xff;
|
|
|
|
snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq);
|
2008-01-26 21:10:44 +08:00
|
|
|
CIO_TRACE_EVENT(4, dbf_txt);
|
2013-06-24 16:30:41 +08:00
|
|
|
isc_register(airq->isc);
|
|
|
|
spin_lock(&airq_lists_lock);
|
|
|
|
hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]);
|
|
|
|
spin_unlock(&airq_lists_lock);
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-06-24 16:30:41 +08:00
|
|
|
EXPORT_SYMBOL(register_adapter_interrupt);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-01-26 21:10:44 +08:00
|
|
|
/**
|
2013-06-24 16:30:41 +08:00
|
|
|
* unregister_adapter_interrupt - unregister adapter interrupt handler
|
|
|
|
* @airq: pointer to adapter interrupt descriptor
|
2008-01-26 21:10:44 +08:00
|
|
|
*/
|
2013-06-24 16:30:41 +08:00
|
|
|
void unregister_adapter_interrupt(struct airq_struct *airq)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-06-24 16:30:41 +08:00
|
|
|
char dbf_txt[32];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-06-24 16:30:41 +08:00
|
|
|
if (hlist_unhashed(&airq->list))
|
|
|
|
return;
|
|
|
|
snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq);
|
2008-01-26 21:10:44 +08:00
|
|
|
CIO_TRACE_EVENT(4, dbf_txt);
|
2013-06-24 16:30:41 +08:00
|
|
|
spin_lock(&airq_lists_lock);
|
|
|
|
hlist_del_rcu(&airq->list);
|
|
|
|
spin_unlock(&airq_lists_lock);
|
|
|
|
synchronize_rcu();
|
|
|
|
isc_unregister(airq->isc);
|
|
|
|
if (airq->flags & AIRQ_PTR_ALLOCATED) {
|
|
|
|
kfree(airq->lsi_ptr);
|
|
|
|
airq->lsi_ptr = NULL;
|
|
|
|
airq->flags &= ~AIRQ_PTR_ALLOCATED;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-06-24 16:30:41 +08:00
|
|
|
EXPORT_SYMBOL(unregister_adapter_interrupt);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-14 15:58:59 +08:00
|
|
|
void do_adapter_IO(u8 isc)
|
2008-01-26 21:10:44 +08:00
|
|
|
{
|
2013-06-24 16:30:41 +08:00
|
|
|
struct airq_struct *airq;
|
|
|
|
struct hlist_head *head;
|
|
|
|
|
|
|
|
head = &airq_lists[isc];
|
|
|
|
rcu_read_lock();
|
|
|
|
hlist_for_each_entry_rcu(airq, head, list)
|
|
|
|
if ((*airq->lsi_ptr & airq->lsi_mask) != 0)
|
|
|
|
airq->handler(airq);
|
|
|
|
rcu_read_unlock();
|
2008-01-26 21:10:44 +08:00
|
|
|
}
|