/* * Copyright (C) 2006-2016, 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. * * 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. */ /* Description: This file implements interrupt related functions. */ #include "sysadpt.h" #include "dev.h" #include "fwcmd.h" #include "isr.h" #define INVALID_WATCHDOG 0xAA #ifdef BG4CT_A0_WORKAROUND #define MAX_ISR_ITERATION 2 #endif irqreturn_t mwl_isr(int irq, void *dev_id) { struct ieee80211_hw *hw = dev_id; struct mwl_priv *priv = hw->priv; void __iomem *int_status_mask; #ifdef BG4CT_A0_WORKAROUND unsigned int currIteration = 0; #endif u32 int_status, clr_status; u32 status; #ifdef BG4CT_A0_WORKAROUND do { #endif 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 unplugged?\n"); } else { clr_status = int_status; if (int_status & MACREG_A2HRIC_BIT_TX_DONE) { int_status &= ~MACREG_A2HRIC_BIT_TX_DONE; if (!priv->is_tx_done_schedule) { status = readl(int_status_mask); writel((status & ~MACREG_A2HRIC_BIT_TX_DONE), int_status_mask); tasklet_schedule(&priv->tx_done_task); priv->is_tx_done_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_BIT_RADAR_DETECT) { int_status &= ~MACREG_A2HRIC_BIT_RADAR_DETECT; wiphy_info(hw->wiphy, "radar detected by firmware\n"); ieee80211_radar_detected(hw); } if (int_status & MACREG_A2HRIC_BIT_QUE_EMPTY) { int_status &= ~MACREG_A2HRIC_BIT_QUE_EMPTY; if (!priv->is_qe_schedule) { if (time_after(jiffies, (priv->qe_trigger_time + 1))) { status = readl(int_status_mask); writel((status & ~MACREG_A2HRIC_BIT_QUE_EMPTY), int_status_mask); tasklet_schedule(&priv->qe_task); priv->qe_trigger_num++; priv->is_qe_schedule = true; priv->qe_trigger_time = jiffies; } } } if (int_status & MACREG_A2HRIC_BIT_CHAN_SWITCH) { int_status &= ~MACREG_A2HRIC_BIT_CHAN_SWITCH; ieee80211_queue_work(hw, &priv->chnl_switch_handle); } if (int_status & MACREG_A2HRIC_BA_WATCHDOG) ieee80211_queue_work(hw, &priv->watchdog_ba_handle); writel(~clr_status, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); } #ifdef BG4CT_A0_WORKAROUND } while (currIteration++ < MAX_ISR_ITERATION); #endif return IRQ_HANDLED; } void mwl_chnl_switch_event(struct work_struct *work) { struct mwl_priv *priv = container_of(work, struct mwl_priv, chnl_switch_handle); struct mwl_vif *mwl_vif; struct ieee80211_vif *vif; if (!priv->csa_active) { wiphy_err(priv->hw->wiphy, "csa is not active (got channel switch event)\n"); return; } spin_lock_bh(&priv->vif_lock); list_for_each_entry(mwl_vif, &priv->vif_list, list) { vif = container_of((char *)mwl_vif, struct ieee80211_vif, drv_priv[0]); if (vif->csa_active) ieee80211_csa_finish(vif); } spin_unlock_bh(&priv->vif_lock); wiphy_info(priv->hw->wiphy, "channel switch is done\n"); priv->csa_active = false; } 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); rc = mwl_fwcmd_get_watchdog_bitmap(priv->hw, &bitmap); if (rc) return; spin_lock_bh(&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); } 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_bh(&priv->stream_lock); }