u8 rxchainmask,
                                      struct ath9k_cal_list *currCal)
 {
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
        bool iscaldone = false;
 
        if (currCal->calState == CAL_RUNNING) {
                                }
 
                                currCal->calData->calPostProc(ah, numChains);
-                               ichan->CalValid |= currCal->calData->calType;
+                               caldata->CalValid |= currCal->calData->calType;
                                currCal->calState = CAL_DONE;
                                iscaldone = true;
                        } else {
                                ar9002_hw_setup_calibration(ah, currCal);
                        }
                }
-       } else if (!(ichan->CalValid & currCal->calData->calType)) {
+       } else if (!(caldata->CalValid & currCal->calData->calType)) {
                ath9k_hw_reset_calibration(ah, currCal);
        }
 
                        ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
        }
 
-       chan->CalValid = 0;
+       if (ah->caldata)
+               ah->caldata->CalValid = 0;
 
        return true;
 }
 
                                      u8 rxchainmask,
                                      struct ath9k_cal_list *currCal)
 {
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
        /* Cal is assumed not done until explicitly set below */
        bool iscaldone = false;
 
                                currCal->calData->calPostProc(ah, numChains);
 
                                /* Calibration has finished. */
-                               ichan->CalValid |= currCal->calData->calType;
+                               caldata->CalValid |= currCal->calData->calType;
                                currCal->calState = CAL_DONE;
                                iscaldone = true;
                        } else {
                        ar9003_hw_setup_calibration(ah, currCal);
                        }
                }
-       } else if (!(ichan->CalValid & currCal->calData->calType)) {
+       } else if (!(caldata->CalValid & currCal->calData->calType)) {
                /* If current cal is marked invalid in channel, kick it off */
                ath9k_hw_reset_calibration(ah, currCal);
        }
        if (ah->cal_list_curr)
                ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
 
-       chan->CalValid = 0;
+       if (ah->caldata)
+               ah->caldata->CalValid = 0;
 
        return true;
 }
 
 }
 
 void ar9003_paprd_populate_single_table(struct ath_hw *ah,
-                                       struct ath9k_channel *chan, int chain)
+                                       struct ath9k_hw_cal_data *caldata,
+                                       int chain)
 {
-       u32 *paprd_table_val = chan->pa_table[chain];
-       u32 small_signal_gain = chan->small_signal_gain[chain];
+       u32 *paprd_table_val = caldata->pa_table[chain];
+       u32 small_signal_gain = caldata->small_signal_gain[chain];
        u32 training_power;
        u32 reg = 0;
        int i;
 }
 EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);
 
-int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_channel *chan,
-                             int chain)
+int ar9003_paprd_create_curve(struct ath_hw *ah,
+                             struct ath9k_hw_cal_data *caldata, int chain)
 {
-       u16 *small_signal_gain = &chan->small_signal_gain[chain];
-       u32 *pa_table = chan->pa_table[chain];
+       u16 *small_signal_gain = &caldata->small_signal_gain[chain];
+       u32 *pa_table = caldata->pa_table[chain];
        u32 *data_L, *data_U;
        int i, status = 0;
        u32 *buf;
        u32 reg;
 
-       memset(chan->pa_table[chain], 0, sizeof(chan->pa_table[chain]));
+       memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain]));
 
        buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC);
        if (!buf)
 
 struct ath_wiphy {
        struct ath_softc *sc; /* shared for all virtual wiphys */
        struct ieee80211_hw *hw;
+       struct ath9k_hw_cal_data caldata;
        enum ath_wiphy_state {
                ATH_WIPHY_INACTIVE,
                ATH_WIPHY_ACTIVE,
 
 /* We can tune this as we go by monitoring really low values */
 #define ATH9K_NF_TOO_LOW       -60
 
-/* AR5416 may return very high value (like -31 dBm), in those cases the nf
- * is incorrect and we should use the static NF value. Later we can try to
- * find out why they are reporting these values */
-
-static bool ath9k_hw_nf_in_range(struct ath_hw *ah, s16 nf)
-{
-       if (nf > ATH9K_NF_TOO_LOW) {
-               ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
-                         "noise floor value detected (%d) is "
-                         "lower than what we think is a "
-                         "reasonable value (%d)\n",
-                         nf, ATH9K_NF_TOO_LOW);
-               return false;
-       }
-       return true;
-}
-
 static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
 {
        int16_t nfval;
        ah->cal_samples = 0;
 }
 
+static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
+                                  struct ath9k_channel *chan)
+{
+       struct ath_nf_limits *limit;
+
+       if (!chan || IS_CHAN_2GHZ(chan))
+               limit = &ah->nf_2g;
+       else
+               limit = &ah->nf_5g;
+
+       return limit->nominal;
+}
+
 /* This is done for the currently configured channel */
 bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
 {
        struct ieee80211_conf *conf = &common->hw->conf;
        struct ath9k_cal_list *currCal = ah->cal_list_curr;
 
-       if (!ah->curchan)
+       if (!ah->caldata)
                return true;
 
        if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
                  "Resetting Cal %d state for channel %u\n",
                  currCal->calData->calType, conf->channel->center_freq);
 
-       ah->curchan->CalValid &= ~currCal->calData->calType;
+       ah->caldata->CalValid &= ~currCal->calData->calType;
        currCal->calState = CAL_WAITING;
 
        return false;
 
 void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
 {
-       struct ath9k_nfcal_hist *h;
+       struct ath9k_nfcal_hist *h = NULL;
        unsigned i, j;
        int32_t val;
        u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
        struct ath_common *common = ath9k_hw_common(ah);
+       s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
 
-       h = ah->nfCalHist;
+       if (ah->caldata)
+               h = ah->caldata->nfCalHist;
 
        for (i = 0; i < NUM_NF_READINGS; i++) {
                if (chainmask & (1 << i)) {
+                       s16 nfval;
+
+                       if (h)
+                               nfval = h[i].privNF;
+                       else
+                               nfval = default_nf;
+
                        val = REG_READ(ah, ah->nf_regs[i]);
                        val &= 0xFFFFFE00;
-                       val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
+                       val |= (((u32) nfval << 1) & 0x1ff);
                        REG_WRITE(ah, ah->nf_regs[i], val);
                }
        }
        int16_t nfarray[NUM_NF_READINGS] = { 0 };
        struct ath9k_nfcal_hist *h;
        struct ieee80211_channel *c = chan->chan;
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
+
+       if (!caldata)
+               return ath9k_hw_get_default_nf(ah, chan);
 
        chan->channelFlags &= (~CHANNEL_CW_INT);
        if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
                ath_print(common, ATH_DBG_CALIBRATE,
                          "NF did not complete in calibration window\n");
                nf = 0;
-               chan->rawNoiseFloor = nf;
-               return chan->rawNoiseFloor;
+               caldata->rawNoiseFloor = nf;
+               return caldata->rawNoiseFloor;
        } else {
                ath9k_hw_do_getnf(ah, nfarray);
                ath9k_hw_nf_sanitize(ah, nfarray);
                }
        }
 
-       h = ah->nfCalHist;
+       h = caldata->nfCalHist;
 
        ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
-       chan->rawNoiseFloor = h[0].privNF;
+       caldata->rawNoiseFloor = h[0].privNF;
 
-       return chan->rawNoiseFloor;
+       return ah->caldata->rawNoiseFloor;
 }
 
-void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah)
+void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
+                                 struct ath9k_channel *chan)
 {
-       struct ath_nf_limits *limit;
+       struct ath9k_nfcal_hist *h;
+       s16 default_nf;
        int i, j;
 
-       if (!ah->curchan || IS_CHAN_2GHZ(ah->curchan))
-               limit = &ah->nf_2g;
-       else
-               limit = &ah->nf_5g;
+       if (!ah->caldata)
+               return;
 
+       h = ah->caldata->nfCalHist;
+       default_nf = ath9k_hw_get_default_nf(ah, chan);
        for (i = 0; i < NUM_NF_READINGS; i++) {
-               ah->nfCalHist[i].currIndex = 0;
-               ah->nfCalHist[i].privNF = limit->nominal;
-               ah->nfCalHist[i].invalidNFcount =
-                       AR_PHY_CCA_FILTERWINDOW_LENGTH;
+               h[i].currIndex = 0;
+               h[i].privNF = default_nf;
+               h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
                for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
-                       ah->nfCalHist[i].nfCalBuffer[j] = limit->nominal;
+                       h[i].nfCalBuffer[j] = default_nf;
                }
        }
 }
 
 s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
 {
-       s16 nf;
-
-       if (chan->rawNoiseFloor == 0)
-               nf = -96;
-       else
-               nf = chan->rawNoiseFloor;
-
-       if (!ath9k_hw_nf_in_range(ah, nf))
-               nf = ATH_DEFAULT_NOISE_FLOOR;
+       if (!ah->caldata || !ah->caldata->rawNoiseFloor)
+               return ath9k_hw_get_default_nf(ah, chan);
 
-       return nf;
+       return ah->caldata->rawNoiseFloor;
 }
 EXPORT_SYMBOL(ath9k_hw_getchan_noise);
 
 void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
 int16_t ath9k_hw_getnf(struct ath_hw *ah,
                       struct ath9k_channel *chan);
-void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah);
+void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
+                                 struct ath9k_channel *chan);
 s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
 void ath9k_hw_reset_calibration(struct ath_hw *ah,
                                struct ath9k_cal_list *currCal);
 
        u16 seq_no;
        u32 bmiss_cnt;
 
+       struct ath9k_hw_cal_data caldata[38];
+
        spinlock_t beacon_lock;
 
        bool tx_queues_stop;
 
        struct ieee80211_conf *conf = &common->hw->conf;
        bool fastcc = true;
        struct ieee80211_channel *channel = hw->conf.channel;
+       struct ath9k_hw_cal_data *caldata;
        enum htc_phymode mode;
        __be16 htc_mode;
        u8 cmd_rsp;
                  priv->ah->curchan->channel,
                  channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf));
 
-       ret = ath9k_hw_reset(ah, hchan, fastcc);
+       caldata = &priv->caldata[channel->hw_value];
+       ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
        if (ret) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset channel (%u Mhz) "
                ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
 
        /* Reset the HW */
-       ret = ath9k_hw_reset(ah, ah->curchan, false);
+       ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        if (ret) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset hardware; reset status %d "
                ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
 
        /* Reset the HW */
-       ret = ath9k_hw_reset(ah, ah->curchan, false);
+       ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        if (ret) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset hardware; reset status %d "
        ath9k_hw_configpcipowersave(ah, 0, 0);
 
        ath9k_hw_htc_resetinit(ah);
-       ret = ath9k_hw_reset(ah, init_channel, false);
+       ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
        if (ret) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset hardware; reset status %d "
 
        else
                ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S);
 
-       ath9k_init_nfcal_hist_buffer(ah);
        ah->bb_watchdog_timeout_ms = 25;
 
        common->state = ATH_HW_INITIALIZED;
 
        ath9k_hw_spur_mitigate_freq(ah, chan);
 
-       if (!chan->oneTimeCalsDone)
-               chan->oneTimeCalsDone = true;
-
        return true;
 }
 
 EXPORT_SYMBOL(ath9k_hw_check_alive);
 
 int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
-                   bool bChannelChange)
+                  struct ath9k_hw_cal_data *caldata, bool bChannelChange)
 {
        struct ath_common *common = ath9k_hw_common(ah);
        u32 saveLedState;
        if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
                return -EIO;
 
-       if (curchan && !ah->chip_fullsleep)
+       if (curchan && !ah->chip_fullsleep && ah->caldata)
                ath9k_hw_getnf(ah, curchan);
 
+       ah->caldata = caldata;
+       if (caldata &&
+           (chan->channel != caldata->channel ||
+            (chan->channelFlags & ~CHANNEL_CW_INT) !=
+            (caldata->channelFlags & ~CHANNEL_CW_INT))) {
+               /* Operating channel changed, reset channel calibration data */
+               memset(caldata, 0, sizeof(*caldata));
+               ath9k_init_nfcal_hist_buffer(ah, chan);
+       }
+
        if (bChannelChange &&
            (ah->chip_fullsleep != true) &&
            (ah->curchan != NULL) &&
 
         CHANNEL_HT40PLUS |                     \
         CHANNEL_HT40MINUS)
 
-struct ath9k_channel {
-       struct ieee80211_channel *chan;
+struct ath9k_hw_cal_data {
        u16 channel;
        u32 channelFlags;
-       u32 chanmode;
        int32_t CalValid;
-       bool oneTimeCalsDone;
        int8_t iCoff;
        int8_t qCoff;
        int16_t rawNoiseFloor;
        bool paprd_done;
        u16 small_signal_gain[AR9300_MAX_CHAINS];
        u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ];
+       struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
+};
+
+struct ath9k_channel {
+       struct ieee80211_channel *chan;
+       u16 channel;
+       u32 channelFlags;
+       u32 chanmode;
 };
 
 #define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \
        enum nl80211_iftype opmode;
        enum ath9k_power_mode power_mode;
 
-       struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
+       struct ath9k_hw_cal_data *caldata;
        struct ath9k_pacal_info pacal_info;
        struct ar5416Stats stats;
        struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES];
 void ath9k_hw_deinit(struct ath_hw *ah);
 int ath9k_hw_init(struct ath_hw *ah);
 int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
-                  bool bChannelChange);
+                  struct ath9k_hw_cal_data *caldata, bool bChannelChange);
 int ath9k_hw_fill_cap_info(struct ath_hw *ah);
 u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan);
 
 void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah);
 void ar9003_paprd_enable(struct ath_hw *ah, bool val);
 void ar9003_paprd_populate_single_table(struct ath_hw *ah,
-                                       struct ath9k_channel *chan, int chain);
-int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_channel *chan,
-                             int chain);
+                                       struct ath9k_hw_cal_data *caldata,
+                                       int chain);
+int ar9003_paprd_create_curve(struct ath_hw *ah,
+                             struct ath9k_hw_cal_data *caldata, int chain);
 int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain);
 int ar9003_paprd_init_table(struct ath_hw *ah);
 bool ar9003_paprd_is_done(struct ath_hw *ah);
 
 int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
                    struct ath9k_channel *hchan)
 {
+       struct ath_wiphy *aphy = hw->priv;
        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)
        if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
                fastcc = false;
 
+       if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
+               caldata = &aphy->caldata;
+
        ath_print(common, ATH_DBG_CONFIG,
                  "(%u MHz) -> (%u MHz), conf_is_ht40: %d\n",
                  sc->sc_ah->curchan->channel,
 
        spin_lock_bh(&sc->sc_resetlock);
 
-       r = ath9k_hw_reset(ah, hchan, fastcc);
+       r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
        if (r) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset channel (%u MHz), "
 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 (!ah->curchan->paprd_done)
+       if (!caldata || !caldata->paprd_done)
                return;
 
        ath9k_ps_wakeup(sc);
                if (!(ah->caps.tx_chainmask & BIT(chain)))
                        continue;
 
-               ar9003_paprd_populate_single_table(ah, ah->curchan, chain);
+               ar9003_paprd_populate_single_table(ah, caldata, chain);
        }
 
        ar9003_paprd_enable(ah, true);
        int band = hw->conf.channel->band;
        struct ieee80211_supported_band *sband = &sc->sbands[band];
        struct ath_tx_control txctl;
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
        int qnum, ftype;
        int chain_ok = 0;
        int chain;
        int time_left;
        int i;
 
+       if (!caldata)
+               return;
+
        skb = alloc_skb(len, GFP_KERNEL);
        if (!skb)
                return;
                if (!ar9003_paprd_is_done(ah))
                        break;
 
-               if (ar9003_paprd_create_curve(ah, ah->curchan, chain) != 0)
+               if (ar9003_paprd_create_curve(ah, caldata, chain) != 0)
                        break;
 
                chain_ok = 1;
        kfree_skb(skb);
 
        if (chain_ok) {
-               ah->curchan->paprd_done = true;
+               caldata->paprd_done = true;
                ath_paprd_activate(sc);
        }
 
                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) {
-               if (!sc->sc_ah->curchan->paprd_done)
+       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
                        ath_paprd_activate(sc);
                ah->curchan = ath_get_curchannel(sc, sc->hw);
 
        spin_lock_bh(&sc->sc_resetlock);
-       r = ath9k_hw_reset(ah, ah->curchan, false);
+       r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        if (r) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset channel (%u MHz), "
                ah->curchan = ath_get_curchannel(sc, hw);
 
        spin_lock_bh(&sc->sc_resetlock);
-       r = ath9k_hw_reset(ah, ah->curchan, false);
+       r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        if (r) {
                ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
                          "Unable to reset channel (%u MHz), "
        ath_flushrecv(sc);
 
        spin_lock_bh(&sc->sc_resetlock);
-       r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
+       r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
        if (r)
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset hardware; reset status %d\n", r);
         * and then setup of the interrupt mask.
         */
        spin_lock_bh(&sc->sc_resetlock);
-       r = ath9k_hw_reset(ah, init_channel, false);
+       r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
        if (r) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset hardware; reset status %d "
 
                          "Failed to stop TX DMA. Resetting hardware!\n");
 
                spin_lock_bh(&sc->sc_resetlock);
-               r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
+               r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
                if (r)
                        ath_print(common, ATH_DBG_FATAL,
                                  "Unable to reset hardware; reset status %d\n",