diff options
Diffstat (limited to 'isr.c')
-rw-r--r-- | isr.c | 142 |
1 files changed, 142 insertions, 0 deletions
@@ -0,0 +1,142 @@ +/* + * Copyright (C) 2006-2015, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Description: This file implements interrupt related functions. + */ + +#include "sysadpt.h" +#include "dev.h" +#include "fwcmd.h" +#include "isr.h" + +#define INVALID_WATCHDOG 0xAA + +irqreturn_t mwl_isr(int irq, void *dev_id) +{ + struct ieee80211_hw *hw = dev_id; + struct mwl_priv *priv; + void *int_status_mask; + unsigned int int_status, clr_status; + u32 status; + + priv = hw->priv; + + int_status_mask = priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK; + + int_status = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); + + if (int_status == 0x00000000) + return IRQ_NONE; + + if (int_status == 0xffffffff) { + wiphy_warn(hw->wiphy, "card plugged out???"); + } else { + clr_status = int_status; + + if (int_status & MACREG_A2HRIC_BIT_TX_DONE) { + int_status &= ~MACREG_A2HRIC_BIT_TX_DONE; + + if (!priv->is_tx_schedule) { + status = readl(int_status_mask); + writel((status & ~MACREG_A2HRIC_BIT_TX_DONE), + int_status_mask); + tasklet_schedule(&priv->tx_task); + priv->is_tx_schedule = true; + } + } + + if (int_status & MACREG_A2HRIC_BIT_RX_RDY) { + int_status &= ~MACREG_A2HRIC_BIT_RX_RDY; + + if (!priv->is_rx_schedule) { + status = readl(int_status_mask); + writel((status & ~MACREG_A2HRIC_BIT_RX_RDY), + int_status_mask); + tasklet_schedule(&priv->rx_task); + priv->is_rx_schedule = true; + } + } + + if (int_status & MACREG_A2HRIC_BA_WATCHDOG) { + status = readl(int_status_mask); + writel((status & ~MACREG_A2HRIC_BA_WATCHDOG), + int_status_mask); + int_status &= ~MACREG_A2HRIC_BA_WATCHDOG; + ieee80211_queue_work(hw, &priv->watchdog_ba_handle); + } + + writel(~clr_status, + priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); + } + + return IRQ_HANDLED; +} + +void mwl_watchdog_ba_events(struct work_struct *work) +{ + int rc; + u8 bitmap = 0, stream_index; + struct mwl_ampdu_stream *streams; + struct mwl_priv *priv = + container_of(work, struct mwl_priv, watchdog_ba_handle); + struct ieee80211_hw *hw = priv->hw; + u32 status; + + rc = mwl_fwcmd_get_watchdog_bitmap(priv->hw, &bitmap); + + if (rc) + goto done; + + spin_lock(&priv->stream_lock); + + /* the bitmap is the hw queue number. Map it to the ampdu queue. */ + if (bitmap != INVALID_WATCHDOG) { + if (bitmap == SYSADPT_TX_AMPDU_QUEUES) + stream_index = 0; + else if (bitmap > SYSADPT_TX_AMPDU_QUEUES) + stream_index = bitmap - SYSADPT_TX_AMPDU_QUEUES; + else + stream_index = bitmap + 3; /** queue 0 is stream 3*/ + + if (bitmap != 0xFF) { + /* Check if the stream is in use before disabling it */ + streams = &priv->ampdu[stream_index]; + + if (streams->state == AMPDU_STREAM_ACTIVE) { + ieee80211_stop_tx_ba_session(streams->sta, + streams->tid); + spin_unlock(&priv->stream_lock); + mwl_fwcmd_destroy_ba(hw, stream_index); + spin_lock(&priv->stream_lock); + } + } else { + for (stream_index = 0; + stream_index < SYSADPT_TX_AMPDU_QUEUES; + stream_index++) { + streams = &priv->ampdu[stream_index]; + + if (streams->state != AMPDU_STREAM_ACTIVE) + continue; + + ieee80211_stop_tx_ba_session(streams->sta, + streams->tid); + spin_unlock(&priv->stream_lock); + mwl_fwcmd_destroy_ba(hw, stream_index); + spin_lock(&priv->stream_lock); + } + } + } + + spin_unlock(&priv->stream_lock); + +done: + + status = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); + writel(status | MACREG_A2HRIC_BA_WATCHDOG, + priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); +} |