blob: 934720af98a391e2274b83af924882f33e09d56b [file] [log] [blame]
/**
*
* Name: skisr.c
* Project: Wireless LAN, Bus driver for SDIO interface
* Version: $Revision: 1.1 $
* Date: $Date: 2007/01/18 09:21:35 $
* Purpose: This module handles the interrupts.
*
* Copyright (C) 2003-2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
/******************************************************************************
*
* History:
*
* $Log: skisr.c,v $
* Revision 1.1 2007/01/18 09:21:35 pweber
* Put under CVS control
*
*
******************************************************************************/
#include "h/skdrv1st.h"
#include "h/skdrv2nd.h"
static void (*gpio_int_callback) (void *) = NULL;
static void *gpio_int_callback_arg = NULL;
int
request_gpio_irq_callback(void (*callback) (void *), void *arg)
{
if (!gpio_int_callback && !gpio_int_callback_arg) {
gpio_int_callback = callback;
gpio_int_callback_arg = arg;
return 0;
}
return -1;
}
EXPORT_SYMBOL(request_gpio_irq_callback);
int
release_gpio_irq_callback(void (*callback) (void *), void *arg)
{
if ((callback == gpio_int_callback)
&& (arg == gpio_int_callback_arg)) {
gpio_int_callback = NULL;
gpio_int_callback_arg = NULL;
return 0;
}
return -1;
}
EXPORT_SYMBOL(release_gpio_irq_callback);
DECLARE_TASKLET(SDIOBus_tasklet, SDIOBus_Dpc, 0);
void
SDIOBus_Dpc(unsigned long arg)
{
SK_U32 irq_status, status;
SK_U16 errorIRQ_status = 0;
PSD_DEVICE_DATA pDev;
DBGPRINT(DBG_IRQ, (KERN_DEBUG "SDIOBUS_Dpc startet ....\n"));
if (arg == 0) {
DBGPRINT(DBG_ERROR, (KERN_DEBUG "SDIOBUS_Dpc : arg == NULL !\n"));
return;
}
pDev = (PSD_DEVICE_DATA) arg;
if (pDev == NULL) {
DBGPRINT(DBG_ERROR, (KERN_DEBUG "SDIOBUS_Dpc : pDev == NULL !\n"));
return;
}
irq_status = pDev->lastIntStatus;
if ((irq_status & STDHOST_NORMAL_IRQ_ERROR) != 0) {
MEM_READ_USHORT(pDev, SK_SLOT_0, STDHOST_ERROR_IRQ_STATUS,
&errorIRQ_status);
if ((errorIRQ_status & STDHOST_ERROR_IRQ_GPI_0) != 0) {
if ((pDev->lastErrorIRQSignalMask & STDHOST_ERROR_IRQ_GPI_0) != 0) {
// clear irq bit
MEM_WRITE_USHORT(pDev, SK_SLOT_0, STDHOST_ERROR_IRQ_STATUS,
STDHOST_ERROR_IRQ_GPI_0);
// Fake a SDIO card interrupt
irq_status |= STDHOST_NORMAL_IRQ_CARD;
}
}
if ((errorIRQ_status & STDHOST_ERROR_IRQ_GPI_1) != 0) {
if ((pDev->lastErrorIRQSignalMask & STDHOST_ERROR_IRQ_GPI_1) != 0) {
printk("GPI-1 interrupt fired (0x%4.04X)\n", errorIRQ_status);
/*--- mmoser 3/12/2007 ---*/
// clear irq bit
MEM_WRITE_USHORT(pDev, SK_SLOT_0, STDHOST_ERROR_IRQ_STATUS,
STDHOST_ERROR_IRQ_GPI_1);
SDHost_SetClock(pDev, 1);
if (gpio_int_callback && gpio_int_callback_arg)
gpio_int_callback(gpio_int_callback_arg);
}
}
}
if ((irq_status & STDHOST_NORMAL_IRQ_CARD_OUT) ==
STDHOST_NORMAL_IRQ_CARD_OUT) {
// clear irq bit
MEM_WRITE_USHORT(pDev, SK_SLOT_0, STDHOST_NORMAL_IRQ_STATUS,
STDHOST_NORMAL_IRQ_CARD_OUT);
/*--- mmoser 14.09.2005 ---*/
if (pDev->initialized) {
DBGPRINT(DBG_IRQ | DBG_W528D,
(KERN_DEBUG "SDIOBUS_Dpc : NORMAL_IRQ_CARD_OUT\n"));
/*--- mmoser 10.08.2005 ---*/
DBGPRINT(DBG_IRQ, (KERN_DEBUG "CARD REMOVED\n"));
SDIOBus_CardRemoved(pDev);
/*--- mmoser 3/31/2006 ---*/
pDev->SurpriseRemoved = SK_TRUE;
}
SDHost_EnableInterrupt(pDev, pDev->lastIRQSignalMask);
DBGPRINT(DBG_IRQ,
(KERN_DEBUG "SDIOBUS_Dpc finished.(line=%d)\n", __LINE__));
return;
}
if ((irq_status & STDHOST_NORMAL_IRQ_CARD_IN) == STDHOST_NORMAL_IRQ_CARD_IN) {
// clear irq bit
MEM_WRITE_USHORT(pDev, SK_SLOT_0, STDHOST_NORMAL_IRQ_STATUS,
STDHOST_NORMAL_IRQ_CARD_IN);
DBGPRINT(DBG_IRQ, (KERN_DEBUG "SDIOBUS_Dpc : NORMAL_IRQ_CARD_IN\n"));
DBGPRINT(DBG_IRQ, (KERN_DEBUG "CARD PLUGGED IN\n"));
SDIOBus_CardPluggedIn(pDev);
SDHost_EnableInterrupt(pDev, pDev->lastIRQSignalMask);
DBGPRINT(DBG_IRQ,
(KERN_DEBUG "SDIOBUS_Dpc finished.(line=%d)\n", __LINE__));
return;
}
if ((irq_status & STDHOST_NORMAL_IRQ_CARD) == STDHOST_NORMAL_IRQ_CARD) {
status = (irq_status << 16);
if (pDev->number_of_functions > 1) {
queue_work(pDev->workqueue, &pDev->irq_work);
} else {
if (pDev->irq_device_cache[1]->functions[1].int_handler != NULL) {
pDev->irq_device_cache[1]->functions[1].int_handler(pDev->
irq_device_cache
[1],
pDev->
irq_device_cache
[1]->
pCurrent_Ids,
pDev->
irq_device_cache
[1]->
functions
[1].
context);
} else {
DBGPRINT(DBG_IRQ,
(KERN_DEBUG "No interrupt handler registered.\n"));
SDHost_EnableInterrupt(pDev, STDHOST_NORMAL_IRQ_CARD_ALL_ENA);
DBGPRINT(DBG_IRQ, (KERN_DEBUG "SDIOBUS_Dpc finished.\n"));
return;
}
}
DBGPRINT(DBG_IRQ,
(KERN_DEBUG "SDIOBUS_Dpc finished.(line=%d)\n", __LINE__));
return;
}
if (irq_status & (STDHOST_NORMAL_IRQ_ERROR |
STDHOST_NORMAL_IRQ_TRANS_COMPLETE |
STDHOST_NORMAL_IRQ_CMD_COMPLETE)) {
if (((irq_status & STDHOST_NORMAL_IRQ_TRANS_COMPLETE) &&
(pDev->lastIRQSignalMask & STDHOST_NORMAL_IRQ_TRANS_COMPLETE)) ||
((irq_status & STDHOST_NORMAL_IRQ_CMD_COMPLETE) &&
(pDev->lastIRQSignalMask & STDHOST_NORMAL_IRQ_CMD_COMPLETE)) ||
((errorIRQ_status & STDHOST_ERROR_IRQ_GPI_1) &&
(pDev->lastErrorIRQSignalMask & STDHOST_ERROR_IRQ_GPI_1))
)
{
SDHost_EnableInterrupt(pDev, pDev->lastIRQSignalMask);
}
DBGPRINT(DBG_IRQ,
(KERN_DEBUG "SDIOBUS_Dpc finished.(line=%d)\n", __LINE__));
return;
} else {
MEM_WRITE_USHORT(pDev, SK_SLOT_0, STDHOST_NORMAL_IRQ_STATUS, 0xFFFF);
MEM_WRITE_USHORT(pDev, SK_SLOT_0, STDHOST_ERROR_IRQ_STATUS, 0xFFFF);
DBGPRINT(DBG_ERROR,
(KERN_DEBUG "SDIOBus_Dpc(): Unhandled IRQ : 0x%8.08X.\n",
irq_status));
}
SDHost_EnableInterrupt(pDev, pDev->lastIRQSignalMask);
DBGPRINT(DBG_IRQ,
(KERN_DEBUG "SDIOBUS_Dpc finished.(line=%d)\n", __LINE__));
}
irqreturn_t
SDIOBus_Isr(int irq, void *dev_id, struct pt_regs *regs)
{
SK_U32 tmp;
SK_U16 tmpsig;
PSD_DEVICE_DATA pDev;
if (dev_id == NULL) {
DBGPRINT(DBG_IRQ, (KERN_DEBUG "SDIOBUS_Isr : dev_id == NULL !\n"));
return IRQ_HANDLED;
}
pDev = (PSD_DEVICE_DATA) dev_id;
if (pDev == NULL) {
DBGPRINT(DBG_IRQ, (KERN_DEBUG "SDIOBUS_Isr : pDev == NULL !\n"));
return IRQ_HANDLED;
}
MEM_READ_ULONG(pDev, SK_SLOT_0, STDHOST_NORMAL_IRQ_STATUS, &tmp);
MEM_READ_USHORT(pDev, SK_SLOT_0, STDHOST_ERROR_IRQ_SIGNAL_ENABLE, &tmpsig);
// Check if interrupt came from our device
if (((((tmp & STDHOST_NORMAL_IRQ_ERROR) != 0) &&
((tmpsig & (STDHOST_ERROR_IRQ_GPI_1 | STDHOST_ERROR_IRQ_GPI_0)) != 0))
|| ((tmp & pDev->currentIRQSignalMask) != 0)) && tmp != 0xFFFFFFFF)
{
/*--- mmoser 8/29/2006 ---*/
if (tmp & STDHOST_NORMAL_IRQ_TRANS_COMPLETE) {
if (pDev->currentIRQSignalMask & STDHOST_NORMAL_IRQ_TRANS_COMPLETE) {
// clear irq bit
MEM_WRITE_USHORT(pDev, SK_SLOT_0, STDHOST_NORMAL_IRQ_STATUS,
STDHOST_NORMAL_IRQ_TRANS_COMPLETE);
if (!test_and_set_bit(0, &pDev->trans_complete_evt.event)) {
wake_up(&pDev->trans_complete_evt.wq);
}
}
}
if (tmp & STDHOST_NORMAL_IRQ_CMD_COMPLETE) {
if (pDev->currentIRQSignalMask & STDHOST_NORMAL_IRQ_CMD_COMPLETE) {
// clear irq bit
MEM_WRITE_USHORT(pDev, SK_SLOT_0, STDHOST_NORMAL_IRQ_STATUS,
STDHOST_NORMAL_IRQ_CMD_COMPLETE);
if (!test_and_set_bit(0, &pDev->cmd_complete_evt.event)) {
wake_up(&pDev->cmd_complete_evt.wq);
}
}
}
MEM_READ_USHORT(pDev, SK_SLOT_0, STDHOST_ERROR_IRQ_SIGNAL_ENABLE,
&pDev->lastErrorIRQSignalMask);
pDev->lastIntStatus = tmp;
DBGPRINT(DBG_IRQ,
(KERN_DEBUG "SDIOBUS_Isr (0x%4.04X)\n", pDev->lastIntStatus));
SDHost_DisableInterrupt(pDev, STDHOST_NORMAL_IRQ_ALL_SIG_ENA);
SDIOBus_tasklet.data = (unsigned long) pDev;
tasklet_schedule(&SDIOBus_tasklet);
} else {
if (tmp == 0xFFFFFF)
pDev->SurpriseRemoved = SK_TRUE;
return IRQ_NONE;
}
return IRQ_HANDLED;
}