static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size) { int i, j; u32 v1, v2, rng_last = sc->rng_last; struct ath_hw *ah = sc->sc_ah; ath9k_ps_wakeup(sc); REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1); REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5); REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, AR_PHY_TEST_CTL_RX_OBS_SEL, 0); for (i = 0, j = 0; i < buf_size; i++) { v1 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff; v2 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff; /* wait for data ready */ if (v1 && v2 && rng_last != v1 && v1 != v2 && v1 != 0xffff && v2 != 0xffff) buf[j++] = (v1 << 16) | v2; rng_last = v2; } ath9k_ps_restore(sc); sc->rng_last = rng_last; return j << 2; }
/* * This is the master bt coex timer which runs for every * 45ms, bt traffic will be given priority during 55% of this * period while wlan gets remaining 45% */ static void ath_btcoex_period_timer(unsigned long data) { struct ath_softc *sc = (struct ath_softc *) data; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; u32 timer_period; bool is_btscan; ath9k_ps_wakeup(sc); ath_detect_bt_priority(sc); is_btscan = sc->sc_flags & SC_OP_BT_SCAN; spin_lock_bh(&btcoex->btcoex_lock); ath9k_hw_btcoex_bt_stomp(ah, is_btscan ? ATH_BTCOEX_STOMP_ALL : btcoex->bt_stomp_type); spin_unlock_bh(&btcoex->btcoex_lock); if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) { if (btcoex->hw_timer_enabled) ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); timer_period = is_btscan ? btcoex->btscan_no_stomp : btcoex->btcoex_no_stomp; ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period, timer_period * 10); btcoex->hw_timer_enabled = true; } ath9k_ps_restore(sc); mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); }
void ath_hw_pll_work(struct work_struct *work) { u32 pll_sqsum; struct ath_softc *sc = container_of(work, struct ath_softc, hw_pll_work.work); /* * ensure that the PLL WAR is executed only * after the STA is associated (or) if the * beaconing had started in interfaces that * uses beacons. */ if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) return; if (sc->tx99_state) return; ath9k_ps_wakeup(sc); pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); ath9k_ps_restore(sc); if (ath_hw_pll_rx_hang_check(sc, pll_sqsum)) return; ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); }
static int ath_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_softc *sc = hw->priv; u32 val; /* * Suspend/Resume resets the PCI configuration space, so we have to * re-disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_read_config_dword(pdev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); ath9k_ps_wakeup(sc); /* Enable LED */ ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); /* * Reset key cache to sane defaults (all entries cleared) instead of * semi-random values after suspend/resume. */ ath9k_cmn_init_crypto(sc->sc_ah); ath9k_ps_restore(sc); sc->ps_idle = true; ath_radio_disable(sc, hw); return 0; }
void ath_hw_check(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; int busy; ath9k_ps_wakeup(sc); if (ath9k_hw_check_alive(sc->sc_ah)) goto out; spin_lock_irqsave(&common->cc_lock, flags); busy = ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, " "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1); if (busy >= 99) { if (++sc->hw_busy_count >= 3) { spin_lock_bh(&sc->sc_pcu_lock); ath_reset(sc, true); spin_unlock_bh(&sc->sc_pcu_lock); } } else if (busy >= 0) sc->hw_busy_count = 0; out: ath9k_ps_restore(sc); }
static ssize_t write_file_bt_ant_diversity(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath9k_hw_capabilities *pCap = &sc->sc_ah->caps; unsigned long bt_ant_diversity; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; if (!(pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV)) goto exit; buf[len] = '\0'; if (kstrtoul(buf, 0, &bt_ant_diversity)) return -EINVAL; common->bt_ant_diversity = !!bt_ant_diversity; ath9k_ps_wakeup(sc); ath9k_hw_set_bt_ant_diversity(sc->sc_ah, common->bt_ant_diversity); ath_dbg(common, CONFIG, "Enable WLAN/BT RX Antenna diversity: %d\n", common->bt_ant_diversity); ath9k_ps_restore(sc); exit: return count; }
static int open_file_regdump(struct inode *inode, struct file *file) { struct ath_softc *sc = inode->i_private; unsigned int len = 0; u8 *buf; int i; unsigned long num_regs, regdump_len, max_reg_offset; max_reg_offset = AR_SREV_9300_20_OR_LATER(sc->sc_ah) ? 0x16bd4 : 0xb500; num_regs = max_reg_offset / 4 + 1; regdump_len = num_regs * REGDUMP_LINE_SIZE + 1; buf = vmalloc(regdump_len); if (!buf) return -ENOMEM; ath9k_ps_wakeup(sc); for (i = 0; i < num_regs; i++) len += scnprintf(buf + len, regdump_len - len, "0x%06x 0x%08x\n", i << 2, REG_READ(sc->sc_ah, i << 2)); ath9k_ps_restore(sc); file->private_data = buf; return 0; }
static void ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_regulatory *reg = ath9k_hw_regulatory(ah); ath_reg_notifier_apply(wiphy, request, reg); /* synchronize DFS detector if regulatory domain changed */ if (sc->dfs_detector != NULL) sc->dfs_detector->set_dfs_domain(sc->dfs_detector, request->dfs_region); /* Set tx power */ if (!ah->curchan) return; sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power; ath9k_ps_wakeup(sc); ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower, sc->cur_chan->txpower, &sc->cur_chan->cur_txpower); ath9k_ps_restore(sc); }
static void ath9k_tx99_deinit(struct ath_softc *sc) { ath_reset(sc, NULL); ath9k_ps_wakeup(sc); ath9k_tx99_stop(sc); ath9k_ps_restore(sc); }
/* * This is the master bt coex timer which runs for every * 45ms, bt traffic will be given priority during 55% of this * period while wlan gets remaining 45% */ static void ath_btcoex_period_timer(unsigned long data) { struct ath_softc *sc = (struct ath_softc *) data; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; u32 timer_period; bool is_btscan; unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) { spin_unlock_irqrestore(&sc->sc_pm_lock, flags); goto skip_hw_wakeup; } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_ps_wakeup(sc); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) ath_detect_bt_priority(sc); is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags); btcoex->bt_wait_time += btcoex->btcoex_period; if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) { if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) && (mci->num_pan || mci->num_other_acl)) ah->btcoex_hw.mci.stomp_ftp = (sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH); else ah->btcoex_hw.mci.stomp_ftp = false; btcoex->bt_wait_time = 0; sc->rx.num_pkts = 0; } spin_lock_bh(&btcoex->btcoex_lock); ath9k_hw_btcoex_bt_stomp(ah, is_btscan ? ATH_BTCOEX_STOMP_ALL : btcoex->bt_stomp_type); ath9k_hw_btcoex_enable(ah); spin_unlock_bh(&btcoex->btcoex_lock); if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) { if (btcoex->hw_timer_enabled) ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); timer_period = is_btscan ? btcoex->btscan_no_stomp : btcoex->btcoex_no_stomp; ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period, timer_period * 10); btcoex->hw_timer_enabled = true; } ath9k_ps_restore(sc); skip_hw_wakeup: timer_period = btcoex->btcoex_period; mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(timer_period)); }
/* * This is the master bt coex timer which runs for every * 45ms, bt traffic will be given priority during 55% of this * period while wlan gets remaining 45% */ static void ath_btcoex_period_timer(unsigned long data) { struct ath_softc *sc = (struct ath_softc *) data; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; enum ath_stomp_type stomp_type; u32 timer_period; unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) { btcoex->bt_wait_time += btcoex->btcoex_period; spin_unlock_irqrestore(&sc->sc_pm_lock, flags); goto skip_hw_wakeup; } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_mci_update_rssi(sc); ath9k_ps_wakeup(sc); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) ath_detect_bt_priority(sc); if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) ath_mci_ftp_adjust(sc); spin_lock_bh(&btcoex->btcoex_lock); stomp_type = btcoex->bt_stomp_type; timer_period = btcoex->btcoex_no_stomp; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) { if (test_bit(BT_OP_SCAN, &btcoex->op_flags)) { stomp_type = ATH_BTCOEX_STOMP_ALL; timer_period = btcoex->btscan_no_stomp; } } else if (btcoex->stomp_audio >= 5) { stomp_type = ATH_BTCOEX_STOMP_AUDIO; btcoex->stomp_audio = 0; } ath9k_hw_btcoex_bt_stomp(ah, stomp_type); ath9k_hw_btcoex_enable(ah); spin_unlock_bh(&btcoex->btcoex_lock); if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) mod_timer(&btcoex->no_stomp_timer, jiffies + msecs_to_jiffies(timer_period)); ath9k_ps_restore(sc); skip_hw_wakeup: mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(btcoex->btcoex_period)); }
static bool ath_is_rfkill_set(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; bool is_blocked; ath9k_ps_wakeup(sc); is_blocked = ath9k_btcoex_ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == ah->rfkill_polarity; ath9k_ps_restore(sc); return is_blocked; }
static ssize_t read_file_regval(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; char buf[32]; unsigned int len; u32 regval; ath9k_ps_wakeup(sc); regval = REG_READ_D(ah, sc->debug.regidx); ath9k_ps_restore(sc); len = sprintf(buf, "0x%08x\n", regval); return simple_read_from_buffer(user_buf, count, ppos, buf, len); }
void ath9k_deinit_device(struct ath_softc *sc) { struct ieee80211_hw *hw = sc->hw; ath9k_ps_wakeup(sc); wiphy_rfkill_stop_polling(sc->hw->wiphy); ath_deinit_leds(sc); ath9k_ps_restore(sc); ieee80211_unregister_hw(hw); ath_rx_cleanup(sc); ath_tx_cleanup(sc); ath9k_deinit_softc(sc); }
static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *channel = hw->conf.channel; int r; ath9k_ps_wakeup(sc); spin_lock_bh(&sc->sc_pcu_lock); ath9k_hw_configpcipowersave(ah, 0, 0); if (!ah->curchan) ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah); r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); if (r) { ath_err(common, "Unable to reset channel (%u MHz), reset status %d\n", channel->center_freq, r); } ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); if (ath_startrecv(sc) != 0) { ath_err(common, "Unable to restart recv logic\n"); goto out; } if (sc->sc_flags & SC_OP_BEACONS) ath_set_beacon(sc); /* restart beacons */ /* Re-Enable interrupts */ ath9k_hw_set_interrupts(ah, ah->imask); /* Enable LED */ ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ath9k_hw_set_gpio(ah, ah->led_pin, 0); ieee80211_wake_queues(hw); ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2); out: spin_unlock_bh(&sc->sc_pcu_lock); ath9k_ps_restore(sc); }
void ath_hw_pll_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, hw_pll_work.work); u32 pll_sqsum; if (AR_SREV_9485(sc->sc_ah)) { ath9k_ps_wakeup(sc); pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); ath9k_ps_restore(sc); ath_hw_pll_rx_hang_check(sc, pll_sqsum); ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5); } }
/* * Checks if the BB/MAC is hung. */ void ath_hw_check(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; int busy; u8 is_alive, nbeacon = 1; enum ath_reset_type type; ath9k_ps_wakeup(sc); is_alive = ath9k_hw_check_alive(sc->sc_ah); if (is_alive && !AR_SREV_9300(sc->sc_ah)) goto out; else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { ath_dbg(common, RESET, "DCU stuck is detected. Schedule chip reset\n"); type = RESET_TYPE_MAC_HANG; goto sched_reset; } spin_lock_irqsave(&common->cc_lock, flags); busy = ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n", busy, sc->hw_busy_count + 1); if (busy >= 99) { if (++sc->hw_busy_count >= 3) { type = RESET_TYPE_BB_HANG; goto sched_reset; } } else if (busy >= 0) { sc->hw_busy_count = 0; nbeacon = 3; } ath_start_rx_poll(sc, nbeacon); goto out; sched_reset: ath9k_queue_reset(sc, type); out: ath9k_ps_restore(sc); }
/* * Generic tsf based hw timer which configures weight * registers to time slice between wlan and bt traffic */ static void ath_btcoex_no_stomp_timer(unsigned long arg) { struct ath_softc *sc = (struct ath_softc *)arg; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; ath9k_ps_wakeup(sc); spin_lock_bh(&btcoex->btcoex_lock); if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && test_bit(BT_OP_SCAN, &btcoex->op_flags))) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); ath9k_hw_btcoex_enable(ah); spin_unlock_bh(&btcoex->btcoex_lock); ath9k_ps_restore(sc); }
/* * Checks if the BB/MAC is hung. */ bool ath_hw_check(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); enum ath_reset_type type; bool is_alive; ath9k_ps_wakeup(sc); is_alive = ath9k_hw_check_alive(sc->sc_ah); if (!is_alive) { ath_dbg(common, RESET, "HW hang detected, schedule chip reset\n"); type = RESET_TYPE_MAC_HANG; ath9k_queue_reset(sc, type); } ath9k_ps_restore(sc); return is_alive; }
/* * PA Pre-distortion. */ static void ath_paprd_activate(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath9k_hw_cal_data *caldata = ah->caldata; int chain; if (!caldata || !caldata->paprd_done) return; ath9k_ps_wakeup(sc); ar9003_paprd_enable(ah, false); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if (!(ah->txchainmask & BIT(chain))) continue; ar9003_paprd_populate_single_table(ah, caldata, chain); } ar9003_paprd_enable(ah, true); ath9k_ps_restore(sc); }
static int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_regulatory *reg = ath9k_hw_regulatory(ah); int ret; ret = ath_reg_notifier_apply(wiphy, request, reg); /* Set tx power */ if (ah->curchan) { sc->config.txpowlimit = 2 * ah->curchan->chan->max_power; ath9k_ps_wakeup(sc); ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit; ath9k_ps_restore(sc); } return ret; }
static ssize_t write_file_regval(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; unsigned long regval; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (kstrtoul(buf, 0, ®val)) return -EINVAL; ath9k_ps_wakeup(sc); REG_WRITE_D(ah, sc->debug.regidx, regval); ath9k_ps_restore(sc); return count; }
/* * Generic tsf based hw timer which configures weight * registers to time slice between wlan and bt traffic */ static void ath_btcoex_no_stomp_timer(void *arg) { struct ath_softc *sc = (struct ath_softc *)arg; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; struct ath_common *common = ath9k_hw_common(ah); bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN; ath_dbg(common, ATH_DBG_BTCOEX, "no stomp timer running\n"); ath9k_ps_wakeup(sc); spin_lock_bh(&btcoex->btcoex_lock); if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); spin_unlock_bh(&btcoex->btcoex_lock); ath9k_ps_restore(sc); }
/* * Generic tsf based hw timer which configures weight * registers to time slice between wlan and bt traffic */ static void ath_btcoex_no_stomp_timer(void *arg) { struct ath_softc *sc = (struct ath_softc *)arg; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; struct ath_common *common = ath9k_hw_common(ah); ath_dbg(common, BTCOEX, "no stomp timer running\n"); ath9k_ps_wakeup(sc); spin_lock_bh(&btcoex->btcoex_lock); if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || test_bit(BT_OP_SCAN, &btcoex->op_flags)) ath9k_btcoex_ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_btcoex_ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); ath9k_btcoex_ath9k_hw_btcoex_enable(ah); spin_unlock_bh(&btcoex->btcoex_lock); ath9k_ps_restore(sc); }
static ssize_t write_file_tx99_power(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; int r; u8 tx_power; r = kstrtou8_from_user(user_buf, count, 0, &tx_power); if (r) return r; if (tx_power > MAX_RATE_POWER) return -EINVAL; sc->tx99_power = tx_power; ath9k_ps_wakeup(sc); ath9k_hw_tx99_set_txpower(sc->sc_ah, sc->tx99_power); ath9k_ps_restore(sc); return count; }
void ath9k_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); u32 status = sc->intrstatus; u32 rxmask; if ((status & ATH9K_INT_FATAL) || (status & ATH9K_INT_BB_WATCHDOG)) { spin_lock(&sc->sc_pcu_lock); ath_reset(sc, true); spin_unlock(&sc->sc_pcu_lock); return; } ath9k_ps_wakeup(sc); spin_lock(&sc->sc_pcu_lock); /* * Only run the baseband hang check if beacons stop working in AP or * IBSS mode, because it has a high false positive rate. For station * mode it should not be necessary, since the upper layers will detect * this through a beacon miss automatically and the following channel * change will trigger a hardware reset anyway */ if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0 && !ath9k_hw_check_alive(ah)) ieee80211_queue_work(sc->hw, &sc->hw_check_work); if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) { /* * TSF sync does not look correct; remain awake to sync with * the next Beacon. */ ath_dbg(common, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n"); sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC | PS_TSFOOR_SYNC; } if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); else rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); if (status & rxmask) { /* Check for high priority Rx first */ if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && (status & ATH9K_INT_RXHP)) ath_rx_tasklet(sc, 0, true); ath_rx_tasklet(sc, 0, false); } if (status & ATH9K_INT_TX) { if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_tx_edma_tasklet(sc); else ath_tx_tasklet(sc); } if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) if (status & ATH9K_INT_GENTIMER) ath_gen_timer_isr(sc->sc_ah); /* re-enable hardware interrupt */ ath9k_hw_enable_interrupts(ah); spin_unlock(&sc->sc_pcu_lock); ath9k_ps_restore(sc); }
/* * This routine performs the periodic noise floor calibration function * that is used to adjust and optimize the chip performance. This * takes environmental changes (location, temperature) into account. * When the task is complete, it reschedules itself depending on the * appropriate interval that was calculated. */ void ath_ani_calibrate(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); bool longcal = false; bool shortcal = false; bool aniflag = false; unsigned int timestamp = jiffies_to_msecs(jiffies); u32 cal_interval, short_cal_interval, long_cal_interval; unsigned long flags; if (ah->caldata && ah->caldata->nfcal_interference) long_cal_interval = ATH_LONG_CALINTERVAL_INT; else long_cal_interval = ATH_LONG_CALINTERVAL; short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; /* Only calibrate if awake */ if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) goto set_timer; ath9k_ps_wakeup(sc); /* Long calibration runs independently of short calibration. */ if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { longcal = true; ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); common->ani.longcal_timer = timestamp; } /* Short calibration applies only while caldone is false */ if (!common->ani.caldone) { if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { shortcal = true; ath_dbg(common, ATH_DBG_ANI, "shortcal @%lu\n", jiffies); common->ani.shortcal_timer = timestamp; common->ani.resetcal_timer = timestamp; } } else { if ((timestamp - common->ani.resetcal_timer) >= ATH_RESTART_CALINTERVAL) { common->ani.caldone = ath9k_hw_reset_calvalid(ah); if (common->ani.caldone) common->ani.resetcal_timer = timestamp; } } /* Verify whether we must check ANI */ if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) { aniflag = true; common->ani.checkani_timer = timestamp; } /* Call ANI routine if necessary */ if (aniflag) { spin_lock_irqsave(&common->cc_lock, flags); ath9k_hw_ani_monitor(ah, ah->curchan); ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); } /* Perform calibration if necessary */ if (longcal || shortcal) { common->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, common->rx_chainmask, longcal); } ath9k_ps_restore(sc); set_timer: /* * Set timer interval based on previous results. * The interval must be the shortest necessary to satisfy ANI, * short calibration and long calibration. */ cal_interval = ATH_LONG_CALINTERVAL; if (sc->sc_ah->config.enable_ani) cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval); if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) { if (!ah->caldata->paprd_done) ieee80211_queue_work(sc->hw, &sc->paprd_work); else if (!ah->paprd_table_write_done) ath_paprd_activate(sc); } }
void ath_paprd_calibrate(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); struct ieee80211_hw *hw = sc->hw; struct ath_hw *ah = sc->sc_ah; struct ieee80211_hdr *hdr; struct sk_buff *skb = NULL; struct ath9k_hw_cal_data *caldata = ah->caldata; struct ath_common *common = ath9k_hw_common(ah); int ftype; int chain_ok = 0; int chain; int len = 1800; if (!caldata) return; ath9k_ps_wakeup(sc); if (ar9003_paprd_init_table(ah) < 0) goto fail_paprd; skb = alloc_skb(len, GFP_KERNEL); if (!skb) goto fail_paprd; skb_put(skb, len); memset(skb->data, 0, len); hdr = (struct ieee80211_hdr *)skb->data; ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; hdr->frame_control = cpu_to_le16(ftype); hdr->duration_id = cpu_to_le16(10); memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if (!(common->tx_chainmask & BIT(chain))) continue; chain_ok = 0; ath_dbg(common, ATH_DBG_CALIBRATE, "Sending PAPRD frame for thermal measurement " "on chain %d\n", chain); if (!ath_paprd_send_frame(sc, skb, chain)) goto fail_paprd; ar9003_paprd_setup_gain_table(ah, chain); ath_dbg(common, ATH_DBG_CALIBRATE, "Sending PAPRD training frame on chain %d\n", chain); if (!ath_paprd_send_frame(sc, skb, chain)) goto fail_paprd; if (!ar9003_paprd_is_done(ah)) { ath_dbg(common, ATH_DBG_CALIBRATE, "PAPRD not yet done on chain %d\n", chain); break; } if (ar9003_paprd_create_curve(ah, caldata, chain)) { ath_dbg(common, ATH_DBG_CALIBRATE, "PAPRD create curve failed on chain %d\n", chain); break; } chain_ok = 1; } kfree_skb(skb); if (chain_ok) { caldata->paprd_done = true; ath_paprd_activate(sc); } fail_paprd: ath9k_ps_restore(sc); }
/* * Set/change channels. If the channel is really being changed, it's done * by reseting the chip. To accomplish this we must first cleanup any pending * DMA, then restart stuff. */ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; bool fastcc = true, stopped; struct ieee80211_channel *channel = hw->conf.channel; struct ath9k_hw_cal_data *caldata = NULL; int r; if (sc->sc_flags & SC_OP_INVALID) return -EIO; sc->hw_busy_count = 0; del_timer_sync(&common->ani.timer); cancel_work_sync(&sc->paprd_work); cancel_work_sync(&sc->hw_check_work); cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); ath9k_ps_wakeup(sc); spin_lock_bh(&sc->sc_pcu_lock); /* * This is only performed if the channel settings have * actually changed. * * To switch channels clear any pending DMA operations; * wait long enough for the RX fifo to drain, reset the * hardware at the new frequency, and then re-enable * the relevant bits of the h/w. */ ath9k_hw_disable_interrupts(ah); stopped = ath_drain_all_txq(sc, false); if (!ath_stoprecv(sc)) stopped = false; if (!ath9k_hw_check_alive(ah)) stopped = false; /* XXX: do not flush receive queue here. We don't want * to flush data frames already in queue because of * changing channel. */ if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL)) fastcc = false; if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) caldata = &sc->caldata; ath_dbg(common, ATH_DBG_CONFIG, "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n", sc->sc_ah->curchan->channel, channel->center_freq, conf_is_ht40(conf), fastcc); r = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (r) { ath_err(common, "Unable to reset channel (%u MHz), reset status %d\n", channel->center_freq, r); goto ps_restore; } if (ath_startrecv(sc) != 0) { ath_err(common, "Unable to restart recv logic\n"); r = -EIO; goto ps_restore; } ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); ath9k_hw_set_interrupts(ah, ah->imask); if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) { if (sc->sc_flags & SC_OP_BEACONS) ath_set_beacon(sc); ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2); if (!common->disable_ani) ath_start_ani(common); } ps_restore: ieee80211_wake_queues(hw); spin_unlock_bh(&sc->sc_pcu_lock); ath9k_ps_restore(sc); return r; }
/* * This is the master bt coex timer which runs for every * 45ms, bt traffic will be given priority during 55% of this * period while wlan gets remaining 45% */ static void ath_btcoex_period_timer(unsigned long data) { struct ath_softc *sc = (struct ath_softc *) data; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; enum ath_stomp_type stomp_type; u32 timer_period; unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) { btcoex->bt_wait_time += btcoex->btcoex_period; spin_unlock_irqrestore(&sc->sc_pm_lock, flags); goto skip_hw_wakeup; } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_mci_update_rssi(sc); ath9k_ps_wakeup(sc); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) ath_detect_bt_priority(sc); if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) ath_mci_ftp_adjust(sc); spin_lock_bh(&btcoex->btcoex_lock); stomp_type = btcoex->bt_stomp_type; timer_period = btcoex->btcoex_no_stomp; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) { if (test_bit(BT_OP_SCAN, &btcoex->op_flags)) { stomp_type = ATH_BTCOEX_STOMP_ALL; timer_period = btcoex->btscan_no_stomp; } } else if (btcoex->stomp_audio >= 5) { stomp_type = ATH_BTCOEX_STOMP_AUDIO; btcoex->stomp_audio = 0; } ath9k_hw_btcoex_bt_stomp(ah, stomp_type); ath9k_hw_btcoex_enable(ah); spin_unlock_bh(&btcoex->btcoex_lock); /* * btcoex_period is in msec while (btocex/btscan_)no_stomp are in usec, * ensure that we properly convert btcoex_period to usec * for any comparision with (btcoex/btscan_)no_stomp. */ if (btcoex->btcoex_period * 1000 != btcoex->btcoex_no_stomp) { if (btcoex->hw_timer_enabled) ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period, timer_period * 10); btcoex->hw_timer_enabled = true; } ath9k_ps_restore(sc); skip_hw_wakeup: mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(btcoex->btcoex_period)); }