From e8975581f63870be42ff4662b293d1b0c8c21350 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 9 Oct 2008 12:18:51 +0200 Subject: mac80211: introduce hw config change flags This makes mac80211 notify the driver which configuration actually changed, e.g. channel etc. No driver changes, this is just plumbing, driver authors are expected to act on this if they want to. Also remove the HW CONFIG debug printk, it's incorrect, often we configure something else. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 9e47d727e220..fcd688765d04 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -219,8 +219,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); static void ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); -static int ath5k_config(struct ieee80211_hw *hw, - struct ieee80211_conf *conf); +static int ath5k_config(struct ieee80211_hw *hw, u32 changed); static int ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_if_conf *conf); @@ -2780,10 +2779,10 @@ end: * TODO: Phy disable/diversity etc */ static int -ath5k_config(struct ieee80211_hw *hw, - struct ieee80211_conf *conf) +ath5k_config(struct ieee80211_hw *hw, u32 changed) { struct ath5k_softc *sc = hw->priv; + struct ieee80211_conf *conf = &hw->conf; sc->bintval = conf->beacon_int; sc->power_level = conf->power_level; -- cgit v1.2.1 From da966bcae70e4012b7d999820b728dd6502047e0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 12 Oct 2008 22:54:10 +0200 Subject: Ath5k: add AP mode Add support for AP mode. This involves: - enablement in ath5k_beacon_config -- initialize beacon timer - add AP to the supported modes in ath5k_add_interface - handle beacon change even for AP in ath5k_config_interface - remove useless test for IBSS in ath5k_beacon_update Note that it doesn't enable the AP mode for the driver. It must be enabled by NL80211_IFTYPE_AP bit added to interface_modes. v2: Fixed opmode constant (IEEE80211_ to NL80211_) Signed-off-by: Jiri Slaby Cc: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 42 ++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 25 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index fcd688765d04..c98380845fa7 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -237,8 +237,7 @@ static int ath5k_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats); static u64 ath5k_get_tsf(struct ieee80211_hw *hw); static void ath5k_reset_tsf(struct ieee80211_hw *hw); -static int ath5k_beacon_update(struct ieee80211_hw *hw, - struct sk_buff *skb); +static int ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb); static struct ieee80211_ops ath5k_hw_ops = { .tx = ath5k_tx, @@ -2137,8 +2136,6 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf) * * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA * interrupts to detect TSF updates only. - * - * AP mode is missing. */ static void ath5k_beacon_config(struct ath5k_softc *sc) @@ -2151,7 +2148,8 @@ ath5k_beacon_config(struct ath5k_softc *sc) if (sc->opmode == NL80211_IFTYPE_STATION) { sc->imask |= AR5K_INT_BMISS; - } else if (sc->opmode == NL80211_IFTYPE_ADHOC) { + } else if (sc->opmode == NL80211_IFTYPE_ADHOC || + sc->opmode == NL80211_IFTYPE_AP) { /* * In IBSS mode we use a self-linked tx descriptor and let the * hardware send the beacons automatically. We have to load it @@ -2163,13 +2161,15 @@ ath5k_beacon_config(struct ath5k_softc *sc) sc->imask |= AR5K_INT_SWBA; - if (ath5k_hw_hasveol(ah)) { - spin_lock(&sc->block); - ath5k_beacon_send(sc); - spin_unlock(&sc->block); - } + if (sc->opmode == NL80211_IFTYPE_ADHOC) { + if (ath5k_hw_hasveol(ah)) { + spin_lock(&sc->block); + ath5k_beacon_send(sc); + spin_unlock(&sc->block); + } + } else + ath5k_beacon_update_timers(sc, -1); } - /* TODO else AP */ ath5k_hw_set_imr(ah, sc->imask); } @@ -2740,6 +2740,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, sc->vif = conf->vif; switch (conf->type) { + case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MONITOR: @@ -2803,7 +2804,7 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ret = -EIO; goto unlock; } - if (conf->bssid) { + if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) { /* Cache for later use during resets */ memcpy(ah->ah_bssid, conf->bssid, ETH_ALEN); /* XXX: assoc id is set to 0 for now, mac80211 doesn't have @@ -2811,18 +2812,16 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ath5k_hw_set_associd(ah, ah->ah_bssid, 0); mmiowb(); } - if (conf->changed & IEEE80211_IFCC_BEACON && - vif->type == NL80211_IFTYPE_ADHOC) { + (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_AP)) { struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); if (!beacon) { ret = -ENOMEM; goto unlock; } - /* call old handler for now */ - ath5k_beacon_update(hw, beacon); + ath5k_beacon_update(sc, beacon); } - mutex_unlock(&sc->lock); return ath5k_reset_wake(sc); @@ -3052,19 +3051,13 @@ ath5k_reset_tsf(struct ieee80211_hw *hw) } static int -ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) +ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb) { - struct ath5k_softc *sc = hw->priv; unsigned long flags; int ret; ath5k_debug_dump_skb(sc, skb, "BC ", 1); - if (sc->opmode != NL80211_IFTYPE_ADHOC) { - ret = -EIO; - goto end; - } - spin_lock_irqsave(&sc->block, flags); ath5k_txbuf_free(sc, sc->bbuf); sc->bbuf->skb = skb; @@ -3077,7 +3070,6 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) mmiowb(); } -end: return ret; } -- cgit v1.2.1 From 6e0e0bf80c963d83f511e1cda652630cf31ebf85 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 13 Oct 2008 14:08:10 -0700 Subject: ath5k: correct ath5k signal quality This adjusts ath5k's signal quality reporting taking into consideration received beacons at an RSSI of 35 should indicate you can use of 54 Mbps on a single stream card (all ath5k cards) reliably. Signed-off-by: Luis R. Rodriguez Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index c98380845fa7..b1e9a47c1c55 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1778,7 +1778,17 @@ accept: rxs.noise = sc->ah->ah_noise_floor; rxs.signal = rxs.noise + rs.rs_rssi; - rxs.qual = rs.rs_rssi * 100 / 64; + + /* An rssi of 35 indicates you should be able use + * 54 Mbps reliably. A more elaborate scheme can be used + * here but it requires a map of SNR/throughput for each + * possible mode used */ + rxs.qual = rs.rs_rssi * 100 / 35; + + /* rssi can be more than 35 though, anything above that + * should be considered at 100% */ + if (rxs.qual > 100) + rxs.qual = 100; rxs.antenna = rs.rs_antenna; rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate); -- cgit v1.2.1 From b706e65b40417e03c2451bb3f92488f3736843fa Mon Sep 17 00:00:00 2001 From: Andrey Yurovsky Date: Mon, 13 Oct 2008 18:23:07 -0700 Subject: ath5k: fix mesh point operation This patch fixes mesh point operation (thanks to YanBo for pointing out the problem): make mesh point interfaces start beaconing when they come up and configure the RX filter in mesh mode so that mesh beacons and action frames are received. Add mesh point to the check in ath5k_add_interface. Tested with multiple AR5211 cards. Signed-off-by: Andrey Yurovsky Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index b1e9a47c1c55..44401f6f578d 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2159,6 +2159,7 @@ ath5k_beacon_config(struct ath5k_softc *sc) if (sc->opmode == NL80211_IFTYPE_STATION) { sc->imask |= AR5K_INT_BMISS; } else if (sc->opmode == NL80211_IFTYPE_ADHOC || + sc->opmode == NL80211_IFTYPE_MESH_POINT || sc->opmode == NL80211_IFTYPE_AP) { /* * In IBSS mode we use a self-linked tx descriptor and let the @@ -2753,6 +2754,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MONITOR: sc->opmode = conf->type; break; @@ -2824,6 +2826,7 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } if (conf->changed & IEEE80211_IFCC_BEACON && (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_AP)) { struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); if (!beacon) { @@ -2952,6 +2955,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, rfilt |= AR5K_RX_FILTER_PROM; if (sc->opmode == NL80211_IFTYPE_ADHOC) rfilt |= AR5K_RX_FILTER_BEACON; + if (sc->opmode == NL80211_IFTYPE_MESH_POINT) + rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | + AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; /* Set filters */ ath5k_hw_set_rx_filter(ah,rfilt); -- cgit v1.2.1 From e6a9854b05c1a6af1308fe2b8c68f35abf28a3ee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 21 Oct 2008 12:40:02 +0200 Subject: mac80211/drivers: rewrite the rate control API So after the previous changes we were still unhappy with how convoluted the API is and decided to make things simpler for everybody. This completely changes the rate control API, now taking into account 802.11n with MCS rates and more control, most drivers don't support that though. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 44401f6f578d..3773d983ea66 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -541,8 +541,8 @@ ath5k_pci_probe(struct pci_dev *pdev, /* set up multi-rate retry capabilities */ if (sc->ah->ah_version == AR5K_AR5212) { - hw->max_altrates = 3; - hw->max_altrate_tries = 11; + hw->max_rates = 4; + hw->max_rate_tries = 11; } /* Finish private driver data initialization */ @@ -1181,7 +1181,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, (sc->power_level * 2), ieee80211_get_tx_rate(sc->hw, info)->hw_value, - info->control.retry_limit, keyidx, 0, flags, 0, 0); + info->control.rates[0].count, keyidx, 0, flags, 0, 0); if (ret) goto err_unmap; @@ -1193,7 +1193,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) break; mrr_rate[i] = rate->hw_value; - mrr_tries[i] = info->control.retries[i].limit; + mrr_tries[i] = info->control.rates[i + 1].count; } ah->ah_setup_mrr_tx_desc(ah, ds, @@ -1849,30 +1849,26 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); - memset(&info->status, 0, sizeof(info->status)); - info->tx_rate_idx = ath5k_hw_to_driver_rix(sc, - ts.ts_rate[ts.ts_final_idx]); - info->status.retry_count = ts.ts_longretry; - + ieee80211_tx_info_clear_status(info); for (i = 0; i < 4; i++) { - struct ieee80211_tx_altrate *r = - &info->status.retries[i]; + struct ieee80211_tx_rate *r = + &info->status.rates[i]; if (ts.ts_rate[i]) { - r->rate_idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]); - r->limit = ts.ts_retry[i]; + r->idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]); + r->count = ts.ts_retry[i]; } else { - r->rate_idx = -1; - r->limit = 0; + r->idx = -1; + r->count = 0; } } - info->status.excessive_retries = 0; + /* count the successful attempt as well */ + info->status.rates[ts.ts_final_idx].count++; + if (unlikely(ts.ts_status)) { sc->ll_stats.dot11ACKFailureCount++; - if (ts.ts_status & AR5K_TXERR_XRETRY) - info->status.excessive_retries = 1; - else if (ts.ts_status & AR5K_TXERR_FILT) + if (ts.ts_status & AR5K_TXERR_FILT) info->flags |= IEEE80211_TX_STAT_TX_FILTERED; } else { info->flags |= IEEE80211_TX_STAT_ACK; -- cgit v1.2.1 From 0bbac08f0a23898787978c0b405474e453e9824c Mon Sep 17 00:00:00 2001 From: John Daiker Date: Fri, 17 Oct 2008 12:16:00 -0700 Subject: ath5k: reduce checkpatch.pl errors A few changes to reduce checkpatch.pl errors in the ath5k driver. For the most part, I only fixed cosmetic things, and left the actual 'code flow' untouched (hopefully)! Diff is against wireless-testing HEAD. Signed-off-by: John Daiker Reviewed-by: Bob Copeland Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 11 +++++------ drivers/net/wireless/ath5k/initvals.c | 8 ++++---- drivers/net/wireless/ath5k/phy.c | 5 +++-- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 3773d983ea66..6caabebc4c66 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2528,8 +2528,7 @@ ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led, led->led_dev.brightness_set = ath5k_led_brightness_set; err = led_classdev_register(&sc->pdev->dev, &led->led_dev); - if (err) - { + if (err) { ATH5K_WARN(sc, "could not register LED %s\n", name); led->sc = NULL; } @@ -2890,9 +2889,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, if (*new_flags & FIF_PROMISC_IN_BSS) { rfilt |= AR5K_RX_FILTER_PROM; __set_bit(ATH_STAT_PROMISC, sc->status); - } - else + } else { __clear_bit(ATH_STAT_PROMISC, sc->status); + } } /* Note, AR5K_RX_FILTER_MCAST is already enabled */ @@ -2956,7 +2955,7 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; /* Set filters */ - ath5k_hw_set_rx_filter(ah,rfilt); + ath5k_hw_set_rx_filter(ah, rfilt); /* Set multicast bits */ ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]); @@ -2973,7 +2972,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ath5k_softc *sc = hw->priv; int ret = 0; - switch(key->alg) { + switch (key->alg) { case ALG_WEP: /* XXX: fix hardware encryption, its not working. For now * allow software encryption */ diff --git a/drivers/net/wireless/ath5k/initvals.c b/drivers/net/wireless/ath5k/initvals.c index ceaa6c475c06..450bd6e945ff 100644 --- a/drivers/net/wireless/ath5k/initvals.c +++ b/drivers/net/wireless/ath5k/initvals.c @@ -1681,7 +1681,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel) */ /* For AR5212 and combatible */ - if (ah->ah_version == AR5K_AR5212){ + if (ah->ah_version == AR5K_AR5212) { /* First set of mode-specific settings */ ath5k_hw_ini_mode_registers(ah, @@ -1695,7 +1695,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel) ar5212_ini, change_channel); /* Second set of mode-specific settings */ - if (ah->ah_radio == AR5K_RF5111){ + if (ah->ah_radio == AR5K_RF5111) { ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(ar5212_rf5111_ini_mode_end), @@ -1706,7 +1706,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel) ARRAY_SIZE(rf5111_ini_bbgain), rf5111_ini_bbgain, change_channel); - } else if (ah->ah_radio == AR5K_RF5112){ + } else if (ah->ah_radio == AR5K_RF5112) { ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(ar5212_rf5112_ini_mode_end), @@ -1716,7 +1716,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel) ARRAY_SIZE(rf5112_ini_bbgain), rf5112_ini_bbgain, change_channel); - } else if (ah->ah_radio == AR5K_RF5413){ + } else if (ah->ah_radio == AR5K_RF5413) { ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(rf5413_ini_mode_end), diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c index e43f6563e61a..69625bf4d11c 100644 --- a/drivers/net/wireless/ath5k/phy.c +++ b/drivers/net/wireless/ath5k/phy.c @@ -1412,7 +1412,8 @@ static int ath5k_hw_rf5112_rfregs(struct ath5k_hw *ah, rf_ini = rfregs_2112a; rf_size = ARRAY_SIZE(rfregs_5112a); if (mode < 2) { - ATH5K_ERR(ah->ah_sc,"invalid channel mode: %i\n",mode); + ATH5K_ERR(ah->ah_sc, "invalid channel mode: %i\n", + mode); return -EINVAL; } mode = mode - 2; /*no a/turboa modes for 2112*/ @@ -1708,7 +1709,7 @@ enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath5k_hw *ah) if (ah->ah_radio >= AR5K_RF5112) { ath5k_hw_rfregs_gainf_corr(ah); ah->ah_gain.g_current = - ah->ah_gain.g_current>=ah->ah_gain.g_f_corr ? + ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ? (ah->ah_gain.g_current-ah->ah_gain.g_f_corr) : 0; } -- cgit v1.2.1 From 4c674c60bd567597f1224973712b352f4f474890 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 26 Oct 2008 20:40:25 +0200 Subject: ath5k: Update interrupt masking code *Properly get/set all available ISR/IMR values and review common/uncommon bits *Better handling of per-txq interrupts (we can now resolve what q is generating each interrupt -this will help in debuging wme later) *Some minor updates from legacy-hal *Properly handle RXNOFRM and TXNOFRM interrupt masking (even when we don't set them on IMR they keep showing up, so we disable them by zeroing AR5K_RXNOFRM and AR5K_TXNOFRM registers). This doesn't exist on legacy-hal but i've tested it on various cards and it works fine. Changes-Licensed-under: ISC Signed-Off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/ath5k.h | 86 ++++++++++++----- drivers/net/wireless/ath5k/base.c | 7 +- drivers/net/wireless/ath5k/dma.c | 190 ++++++++++++++++++++++++++++--------- drivers/net/wireless/ath5k/qcu.c | 37 +++++++- drivers/net/wireless/ath5k/reg.h | 3 +- drivers/net/wireless/ath5k/reset.c | 3 +- 6 files changed, 247 insertions(+), 79 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h index 53ea439aff48..b11792039911 100644 --- a/drivers/net/wireless/ath5k/ath5k.h +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -507,11 +507,15 @@ enum ath5k_tx_queue_id { #define AR5K_TXQ_FLAG_TXEOLINT_ENABLE 0x0004 /* Enable TXEOL interrupt -not used- */ #define AR5K_TXQ_FLAG_TXDESCINT_ENABLE 0x0008 /* Enable TXDESC interrupt -not used- */ #define AR5K_TXQ_FLAG_TXURNINT_ENABLE 0x0010 /* Enable TXURN interrupt */ -#define AR5K_TXQ_FLAG_BACKOFF_DISABLE 0x0020 /* Disable random post-backoff */ -#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE 0x0040 /* Enable ready time expiry policy (?)*/ -#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE 0x0080 /* Enable backoff while bursting */ -#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x0100 /* Disable backoff while bursting */ -#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x0200 /* Enable hw compression -not implemented-*/ +#define AR5K_TXQ_FLAG_CBRORNINT_ENABLE 0x0020 /* Enable CBRORN interrupt */ +#define AR5K_TXQ_FLAG_CBRURNINT_ENABLE 0x0040 /* Enable CBRURN interrupt */ +#define AR5K_TXQ_FLAG_QTRIGINT_ENABLE 0x0080 /* Enable QTRIG interrupt */ +#define AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE 0x0100 /* Enable TXNOFRM interrupt */ +#define AR5K_TXQ_FLAG_BACKOFF_DISABLE 0x0200 /* Disable random post-backoff */ +#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE 0x0300 /* Enable ready time expiry policy (?)*/ +#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE 0x0800 /* Enable backoff while bursting */ +#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x1000 /* Disable backoff while bursting */ +#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x2000 /* Enable hw compression -not implemented-*/ /* * A struct to hold tx queue's parameters @@ -853,7 +857,7 @@ enum ath5k_ant_setting { * checked. We should do this with ath5k_hw_update_mib_counters() but * it seems we should also then do some noise immunity work. * @AR5K_INT_RXPHY: RX PHY Error - * @AR5K_INT_RXKCM: ?? + * @AR5K_INT_RXKCM: RX Key cache miss * @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a * beacon that must be handled in software. The alternative is if you * have VEOL support, in that case you let the hardware deal with things. @@ -869,7 +873,7 @@ enum ath5k_ant_setting { * @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by DMA * errors. These types of errors we can enable seem to be of type * AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR. - * @AR5K_INT_GLOBAL: Seems to be used to clear and set the IER + * @AR5K_INT_GLOBAL: Used to clear and set the IER * @AR5K_INT_NOCARD: signals the card has been removed * @AR5K_INT_COMMON: common interrupts shared amogst MACs with the same * bit value @@ -881,36 +885,61 @@ enum ath5k_ant_setting { * MACs. */ enum ath5k_int { - AR5K_INT_RX = 0x00000001, /* Not common */ + AR5K_INT_RXOK = 0x00000001, AR5K_INT_RXDESC = 0x00000002, + AR5K_INT_RXERR = 0x00000004, AR5K_INT_RXNOFRM = 0x00000008, AR5K_INT_RXEOL = 0x00000010, AR5K_INT_RXORN = 0x00000020, - AR5K_INT_TX = 0x00000040, /* Not common */ + AR5K_INT_TXOK = 0x00000040, AR5K_INT_TXDESC = 0x00000080, + AR5K_INT_TXERR = 0x00000100, + AR5K_INT_TXNOFRM = 0x00000200, + AR5K_INT_TXEOL = 0x00000400, AR5K_INT_TXURN = 0x00000800, AR5K_INT_MIB = 0x00001000, + AR5K_INT_SWI = 0x00002000, AR5K_INT_RXPHY = 0x00004000, AR5K_INT_RXKCM = 0x00008000, AR5K_INT_SWBA = 0x00010000, + AR5K_INT_BRSSI = 0x00020000, AR5K_INT_BMISS = 0x00040000, - AR5K_INT_BNR = 0x00100000, /* Not common */ - AR5K_INT_GPIO = 0x01000000, - AR5K_INT_FATAL = 0x40000000, /* Not common */ - AR5K_INT_GLOBAL = 0x80000000, - - AR5K_INT_COMMON = AR5K_INT_RXNOFRM - | AR5K_INT_RXDESC - | AR5K_INT_RXEOL - | AR5K_INT_RXORN - | AR5K_INT_TXURN - | AR5K_INT_TXDESC - | AR5K_INT_MIB - | AR5K_INT_RXPHY - | AR5K_INT_RXKCM - | AR5K_INT_SWBA - | AR5K_INT_BMISS - | AR5K_INT_GPIO, + AR5K_INT_FATAL = 0x00080000, /* Non common */ + AR5K_INT_BNR = 0x00100000, /* Non common */ + AR5K_INT_TIM = 0x00200000, /* Non common */ + AR5K_INT_DTIM = 0x00400000, /* Non common */ + AR5K_INT_DTIM_SYNC = 0x00800000, /* Non common */ + AR5K_INT_GPIO = 0x01000000, + AR5K_INT_BCN_TIMEOUT = 0x02000000, /* Non common */ + AR5K_INT_CAB_TIMEOUT = 0x04000000, /* Non common */ + AR5K_INT_RX_DOPPLER = 0x08000000, /* Non common */ + AR5K_INT_QCBRORN = 0x10000000, /* Non common */ + AR5K_INT_QCBRURN = 0x20000000, /* Non common */ + AR5K_INT_QTRIG = 0x40000000, /* Non common */ + AR5K_INT_GLOBAL = 0x80000000, + + AR5K_INT_COMMON = AR5K_INT_RXOK + | AR5K_INT_RXDESC + | AR5K_INT_RXERR + | AR5K_INT_RXNOFRM + | AR5K_INT_RXEOL + | AR5K_INT_RXORN + | AR5K_INT_TXOK + | AR5K_INT_TXDESC + | AR5K_INT_TXERR + | AR5K_INT_TXNOFRM + | AR5K_INT_TXEOL + | AR5K_INT_TXURN + | AR5K_INT_MIB + | AR5K_INT_SWI + | AR5K_INT_RXPHY + | AR5K_INT_RXKCM + | AR5K_INT_SWBA + | AR5K_INT_BRSSI + | AR5K_INT_BMISS + | AR5K_INT_GPIO + | AR5K_INT_GLOBAL, + AR5K_INT_NOCARD = 0xffffffff }; @@ -1081,6 +1110,11 @@ struct ath5k_hw { u32 ah_txq_imr_txurn; u32 ah_txq_imr_txdesc; u32 ah_txq_imr_txeol; + u32 ah_txq_imr_cbrorn; + u32 ah_txq_imr_cbrurn; + u32 ah_txq_imr_qtrig; + u32 ah_txq_imr_nofrm; + u32 ah_txq_isr; u32 *ah_rf_banks; size_t ah_rf_banks_size; struct ath5k_gain ah_gain; diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 6caabebc4c66..7ac5f2847da5 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2216,7 +2216,7 @@ ath5k_init(struct ath5k_softc *sc, bool is_resume) */ sc->curchan = sc->hw->conf.channel; sc->curband = &sc->sbands[sc->curchan->band]; - sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL | + sc->imask = AR5K_INT_RXOK | AR5K_INT_TXOK | AR5K_INT_RXEOL | AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; ret = ath5k_reset(sc, false, false); @@ -2410,9 +2410,10 @@ ath5k_intr(int irq, void *dev_id) /* bump tx trigger level */ ath5k_hw_update_tx_triglevel(ah, true); } - if (status & AR5K_INT_RX) + if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR)) tasklet_schedule(&sc->rxtq); - if (status & AR5K_INT_TX) + if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC + | AR5K_INT_TXERR | AR5K_INT_TXEOL)) tasklet_schedule(&sc->txtq); if (status & AR5K_INT_BMISS) { } diff --git a/drivers/net/wireless/ath5k/dma.c b/drivers/net/wireless/ath5k/dma.c index 7adceb2c7fab..7e2b1a67e5da 100644 --- a/drivers/net/wireless/ath5k/dma.c +++ b/drivers/net/wireless/ath5k/dma.c @@ -472,9 +472,6 @@ bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah) * * NOTE: We use read-and-clear register, so after this function is called ISR * is zeroed. - * - * XXX: Why filter interrupts in sw with interrupt_mask ? No benefit at all - * plus it can be misleading (one might thing that we save interrupts this way) */ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) { @@ -494,11 +491,16 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) } } else { /* - * Read interrupt status from the Read-And-Clear - * shadow register. + * Read interrupt status from Interrupt + * Status Register shadow copy (Read And Clear) + * * Note: PISR/SISR Not available on 5210 */ data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR); + if (unlikely(data == AR5K_INT_NOCARD)) { + *interrupt_mask = data; + return -ENODEV; + } } /* @@ -506,17 +508,9 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) */ *interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr; - if (unlikely(data == AR5K_INT_NOCARD)) - return -ENODEV; - - if (data & (AR5K_ISR_RXOK | AR5K_ISR_RXERR)) - *interrupt_mask |= AR5K_INT_RX; - - if (data & (AR5K_ISR_TXOK | AR5K_ISR_TXERR - | AR5K_ISR_TXDESC | AR5K_ISR_TXEOL)) - *interrupt_mask |= AR5K_INT_TX; - if (ah->ah_version != AR5K_AR5210) { + u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2); + /*HIU = Host Interface Unit (PCI etc)*/ if (unlikely(data & (AR5K_ISR_HIUERR))) *interrupt_mask |= AR5K_INT_FATAL; @@ -524,24 +518,93 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) /*Beacon Not Ready*/ if (unlikely(data & (AR5K_ISR_BNR))) *interrupt_mask |= AR5K_INT_BNR; - } - /* - * XXX: BMISS interrupts may occur after association. - * I found this on 5210 code but it needs testing. If this is - * true we should disable them before assoc and re-enable them - * after a successfull assoc + some jiffies. - */ -#if 0 - interrupt_mask &= ~AR5K_INT_BMISS; -#endif + if (unlikely(sisr2 & (AR5K_SISR2_SSERR | + AR5K_SISR2_DPERR | + AR5K_SISR2_MCABT))) + *interrupt_mask |= AR5K_INT_FATAL; + + if (data & AR5K_ISR_TIM) + *interrupt_mask |= AR5K_INT_TIM; + + if (data & AR5K_ISR_BCNMISC) { + if (sisr2 & AR5K_SISR2_TIM) + *interrupt_mask |= AR5K_INT_TIM; + if (sisr2 & AR5K_SISR2_DTIM) + *interrupt_mask |= AR5K_INT_DTIM; + if (sisr2 & AR5K_SISR2_DTIM_SYNC) + *interrupt_mask |= AR5K_INT_DTIM_SYNC; + if (sisr2 & AR5K_SISR2_BCN_TIMEOUT) + *interrupt_mask |= AR5K_INT_BCN_TIMEOUT; + if (sisr2 & AR5K_SISR2_CAB_TIMEOUT) + *interrupt_mask |= AR5K_INT_CAB_TIMEOUT; + } + + if (data & AR5K_ISR_RXDOPPLER) + *interrupt_mask |= AR5K_INT_RX_DOPPLER; + if (data & AR5K_ISR_QCBRORN) { + *interrupt_mask |= AR5K_INT_QCBRORN; + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR3), + AR5K_SISR3_QCBRORN); + } + if (data & AR5K_ISR_QCBRURN) { + *interrupt_mask |= AR5K_INT_QCBRURN; + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR3), + AR5K_SISR3_QCBRURN); + } + if (data & AR5K_ISR_QTRIG) { + *interrupt_mask |= AR5K_INT_QTRIG; + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR4), + AR5K_SISR4_QTRIG); + } + + if (data & AR5K_ISR_TXOK) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR0), + AR5K_SISR0_QCU_TXOK); + + if (data & AR5K_ISR_TXDESC) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR0), + AR5K_SISR0_QCU_TXDESC); + + if (data & AR5K_ISR_TXERR) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR1), + AR5K_SISR1_QCU_TXERR); + + if (data & AR5K_ISR_TXEOL) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR1), + AR5K_SISR1_QCU_TXEOL); + + if (data & AR5K_ISR_TXURN) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR2), + AR5K_SISR2_QCU_TXURN); + } else { + if (unlikely(data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT + | AR5K_ISR_HIUERR | AR5K_ISR_DPERR))) + *interrupt_mask |= AR5K_INT_FATAL; + + /* + * XXX: BMISS interrupts may occur after association. + * I found this on 5210 code but it needs testing. If this is + * true we should disable them before assoc and re-enable them + * after a successfull assoc + some jiffies. + interrupt_mask &= ~AR5K_INT_BMISS; + */ + } /* * In case we didn't handle anything, * print the register value. */ if (unlikely(*interrupt_mask == 0 && net_ratelimit())) - ATH5K_PRINTF("0x%08x\n", data); + ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr); return 0; } @@ -560,14 +623,17 @@ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) { enum ath5k_int old_mask, int_mask; + old_mask = ah->ah_imr; + /* * Disable card interrupts to prevent any race conditions - * (they will be re-enabled afterwards). + * (they will be re-enabled afterwards if AR5K_INT GLOBAL + * is set again on the new mask). */ - ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); - ath5k_hw_reg_read(ah, AR5K_IER); - - old_mask = ah->ah_imr; + if (old_mask & AR5K_INT_GLOBAL) { + ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); + ath5k_hw_reg_read(ah, AR5K_IER); + } /* * Add additional, chipset-dependent interrupt mask flags @@ -575,30 +641,64 @@ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) */ int_mask = new_mask & AR5K_INT_COMMON; - if (new_mask & AR5K_INT_RX) - int_mask |= AR5K_IMR_RXOK | AR5K_IMR_RXERR | AR5K_IMR_RXORN | - AR5K_IMR_RXDESC; - - if (new_mask & AR5K_INT_TX) - int_mask |= AR5K_IMR_TXOK | AR5K_IMR_TXERR | AR5K_IMR_TXDESC | - AR5K_IMR_TXURN; - if (ah->ah_version != AR5K_AR5210) { + /* Preserve per queue TXURN interrupt mask */ + u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2) + & AR5K_SIMR2_QCU_TXURN; + if (new_mask & AR5K_INT_FATAL) { int_mask |= AR5K_IMR_HIUERR; - AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_MCABT | - AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR); + simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR + | AR5K_SIMR2_DPERR); } + + /*Beacon Not Ready*/ + if (new_mask & AR5K_INT_BNR) + int_mask |= AR5K_INT_BNR; + + if (new_mask & AR5K_INT_TIM) + int_mask |= AR5K_IMR_TIM; + + if (new_mask & AR5K_INT_TIM) + simr2 |= AR5K_SISR2_TIM; + if (new_mask & AR5K_INT_DTIM) + simr2 |= AR5K_SISR2_DTIM; + if (new_mask & AR5K_INT_DTIM_SYNC) + simr2 |= AR5K_SISR2_DTIM_SYNC; + if (new_mask & AR5K_INT_BCN_TIMEOUT) + simr2 |= AR5K_SISR2_BCN_TIMEOUT; + if (new_mask & AR5K_INT_CAB_TIMEOUT) + simr2 |= AR5K_SISR2_CAB_TIMEOUT; + + if (new_mask & AR5K_INT_RX_DOPPLER) + int_mask |= AR5K_IMR_RXDOPPLER; + + /* Note: Per queue interrupt masks + * are set via reset_tx_queue (qcu.c) */ + ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); + ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2); + + } else { + if (new_mask & AR5K_INT_FATAL) + int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT + | AR5K_IMR_HIUERR | AR5K_IMR_DPERR); + + ath5k_hw_reg_write(ah, int_mask, AR5K_IMR); } - ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); + /* If RXNOFRM interrupt is masked disable it + * by setting AR5K_RXNOFRM to zero */ + if (!(new_mask & AR5K_INT_RXNOFRM)) + ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM); /* Store new interrupt mask */ ah->ah_imr = new_mask; - /* ..re-enable interrupts */ - ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); - ath5k_hw_reg_read(ah, AR5K_IER); + /* ..re-enable interrupts if AR5K_INT_GLOBAL is set */ + if (new_mask & AR5K_INT_GLOBAL) { + ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); + ath5k_hw_reg_read(ah, AR5K_IER); + } return old_mask; } diff --git a/drivers/net/wireless/ath5k/qcu.c b/drivers/net/wireless/ath5k/qcu.c index 01bf09176d23..1b7bc50ea8eb 100644 --- a/drivers/net/wireless/ath5k/qcu.c +++ b/drivers/net/wireless/ath5k/qcu.c @@ -432,13 +432,30 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue); + if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue); /* Update secondary interrupt mask registers */ + + /* Filter out inactive queues */ ah->ah_txq_imr_txok &= ah->ah_txq_status; ah->ah_txq_imr_txerr &= ah->ah_txq_status; ah->ah_txq_imr_txurn &= ah->ah_txq_status; ah->ah_txq_imr_txdesc &= ah->ah_txq_status; ah->ah_txq_imr_txeol &= ah->ah_txq_status; + ah->ah_txq_imr_cbrorn &= ah->ah_txq_status; + ah->ah_txq_imr_cbrurn &= ah->ah_txq_status; + ah->ah_txq_imr_qtrig &= ah->ah_txq_status; + ah->ah_txq_imr_nofrm &= ah->ah_txq_status; ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok, AR5K_SIMR0_QCU_TXOK) | @@ -448,8 +465,24 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) AR5K_SIMR1_QCU_TXERR) | AR5K_REG_SM(ah->ah_txq_imr_txeol, AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1); - ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txurn, - AR5K_SIMR2_QCU_TXURN), AR5K_SIMR2); + /* Update simr2 but don't overwrite rest simr2 settings */ + AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN); + AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, + AR5K_REG_SM(ah->ah_txq_imr_txurn, + AR5K_SIMR2_QCU_TXURN)); + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn, + AR5K_SIMR3_QCBRORN) | + AR5K_REG_SM(ah->ah_txq_imr_cbrurn, + AR5K_SIMR3_QCBRURN), AR5K_SIMR3); + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig, + AR5K_SIMR4_QTRIG), AR5K_SIMR4); + /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */ + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm, + AR5K_TXNOFRM_QCU), AR5K_TXNOFRM); + /* No queue has TXNOFRM enabled, disable the interrupt + * by setting AR5K_TXNOFRM to zero */ + if (ah->ah_txq_imr_nofrm == 0) + ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM); } return 0; diff --git a/drivers/net/wireless/ath5k/reg.h b/drivers/net/wireless/ath5k/reg.h index e557fe178bbf..0dae54d00f03 100644 --- a/drivers/net/wireless/ath5k/reg.h +++ b/drivers/net/wireless/ath5k/reg.h @@ -234,6 +234,7 @@ #define AR5K_TXNOFRM 0x004c #define AR5K_TXNOFRM_M 0x000003ff #define AR5K_TXNOFRM_QCU 0x000ffc00 +#define AR5K_TXNOFRM_QCU_S 10 /* * Receive frame gap timeout register @@ -350,7 +351,7 @@ #define AR5K_SISR3 0x0090 /* Register Address [5211+] */ #define AR5K_SISR3_QCBRORN 0x000003ff /* Mask for QCBRORN */ -#define AR5K_SISR3_QCBORN_S 0 +#define AR5K_SISR3_QCBRORN_S 0 #define AR5K_SISR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */ #define AR5K_SISR3_QCBRURN_S 16 diff --git a/drivers/net/wireless/ath5k/reset.c b/drivers/net/wireless/ath5k/reset.c index 1b6d45b6772d..b51bc030da02 100644 --- a/drivers/net/wireless/ath5k/reset.c +++ b/drivers/net/wireless/ath5k/reset.c @@ -864,8 +864,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, /* Pre-enable interrupts on 5211/5212*/ if (ah->ah_version != AR5K_AR5210) - ath5k_hw_set_imr(ah, AR5K_INT_RX | AR5K_INT_TX | - AR5K_INT_FATAL); + ath5k_hw_set_imr(ah, ah->ah_imr); /* * Set RF kill flags if supported by the device (read from the EEPROM) -- cgit v1.2.1 From f07a6c495e88ae0143e3c20b1ed666314197277f Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Wed, 29 Oct 2008 04:28:28 +0200 Subject: ath5k: Update PCU code * In set_opmode don't handle antenna settings and preserve other STA_ID1 settings (shouldn't matter because we call it during reset but it makes things cleaner) Also set properly AP/ADHOC indicator flag on CFG while setting AP/ADHOC modes and always enable key search mode. * Properly set BSSID Mask during reset (cache it and reuse it durring set_associd) * Update beacon_init to flush pending BMISS interrupts and handle setting of adhoc beacon ATIM policy flag for ad-hoc mode. Also set TSF to 0 to start TSF increment on AP mode. We need to handle sleep timers for AR5212 there + add support for PCF. * Properly clean MIC key from keytable when TKIP is used (Bob is working on set_key function etc so i leave it for now). Tested on AR5212 (Hainan) and AR5413 and works fine v2 Set PISR on AR5211+ and ISR on AR5210, got to sleep more ;-) Changes-Licensed-under: ISC Signed-Off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/ath5k.h | 3 +- drivers/net/wireless/ath5k/pcu.c | 111 ++++++++++++++++++++++++++++++------- drivers/net/wireless/ath5k/reg.h | 12 +++- 3 files changed, 102 insertions(+), 24 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h index b11792039911..a725bb94a52d 100644 --- a/drivers/net/wireless/ath5k/ath5k.h +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -1093,10 +1093,11 @@ struct ath5k_hw { u8 ah_sta_id[ETH_ALEN]; - /* Current BSSID we are trying to assoc to / creating. + /* Current BSSID we are trying to assoc to / create. * This is passed by mac80211 on config_interface() and cached here for * use in resets */ u8 ah_bssid[ETH_ALEN]; + u8 ah_bssid_mask[ETH_ALEN]; u32 ah_gpio[AR5K_MAX_GPIO]; int ah_gpio_npins; diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index a47df9a24aa1..5674639f7bf8 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -46,34 +46,45 @@ int ath5k_hw_set_opmode(struct ath5k_hw *ah) { u32 pcu_reg, beacon_reg, low_id, high_id; - pcu_reg = 0; + + /* Preserve rest settings */ + pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; + pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP + | AR5K_STA_ID1_KEYSRCH_MODE + | (ah->ah_version == AR5K_AR5210 ? + (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0)); + beacon_reg = 0; ATH5K_TRACE(ah->ah_sc); switch (ah->ah_op_mode) { case NL80211_IFTYPE_ADHOC: - pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA | - (ah->ah_version == AR5K_AR5210 ? - AR5K_STA_ID1_NO_PSPOLL : 0); + pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_KEYSRCH_MODE; beacon_reg |= AR5K_BCR_ADHOC; + if (ah->ah_version == AR5K_AR5210) + pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; + else + AR5K_REG_DISABLE_BITS(ah, AR5K_CFG, AR5K_CFG_ADHOC); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: - pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA | - (ah->ah_version == AR5K_AR5210 ? - AR5K_STA_ID1_NO_PSPOLL : 0); + pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE; beacon_reg |= AR5K_BCR_AP; + if (ah->ah_version == AR5K_AR5210) + pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; + else + AR5K_REG_ENABLE_BITS(ah, AR5K_CFG, AR5K_CFG_ADHOC); break; case NL80211_IFTYPE_STATION: - pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | - (ah->ah_version == AR5K_AR5210 ? + pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE + | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_PWR_SV : 0); case NL80211_IFTYPE_MONITOR: - pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | - (ah->ah_version == AR5K_AR5210 ? + pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE + | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_NO_PSPOLL : 0); break; @@ -130,6 +141,8 @@ void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); } + + /* TODO: Handle ANI stats */ } /** @@ -254,6 +267,10 @@ void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) * @mac: The card's mac address * * Set station id on hw using the provided mac address + * + * NOTE: This is only called during attach, don't call it + * on reset because it overwrites all AR5K_STA_ID1 settings. + * We have set_opmode (above) for reset. */ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) { @@ -290,8 +307,10 @@ void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) * Set simple BSSID mask on 5212 */ if (ah->ah_version == AR5K_AR5212) { - ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM0); - ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM1); + ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask), + AR5K_BSS_IDM0); + ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask), + AR5K_BSS_IDM1); } /* @@ -415,6 +434,9 @@ int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) u32 low_id, high_id; ATH5K_TRACE(ah->ah_sc); + /* Cache bssid mask so that we can restore it + * on reset */ + memcpy(ah->ah_bssid_mask, mask, ETH_ALEN); if (ah->ah_version == AR5K_AR5212) { low_id = AR5K_LOW_ID(mask); high_id = AR5K_HIGH_ID(mask); @@ -576,7 +598,7 @@ void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) filter |= AR5K_RX_FILTER_PROM; } - /*Zero length DMA*/ + /*Zero length DMA (phy error reporting) */ if (data) AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); else @@ -661,7 +683,12 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) * Set the additional timers by mode */ switch (ah->ah_op_mode) { + case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_STATION: + /* In STA mode timer1 is used as next wakeup + * timer and timer2 as next CFP duration start + * timer. Both in 1/8TUs. */ + /* TODO: PCF handling */ if (ah->ah_version == AR5K_AR5210) { timer1 = 0xffffffff; timer2 = 0xffffffff; @@ -669,27 +696,60 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) timer1 = 0x0000ffff; timer2 = 0x0007ffff; } + /* Mark associated AP as PCF incapable for now */ + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF); break; - + case NL80211_IFTYPE_ADHOC: + AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM); default: + /* On non-STA modes timer1 is used as next DMA + * beacon alert (DBA) timer and timer2 as next + * software beacon alert. Both in 1/8TUs. */ timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3; timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3; + break; } + /* Timer3 marks the end of our ATIM window + * a zero length window is not allowed because + * we 'll get no beacons */ timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); /* * Set the beacon register and enable all timers. - * (next beacon, DMA beacon, software beacon, ATIM window time) */ - ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); + /* When in AP mode zero timer0 to start TSF */ + if (ah->ah_op_mode == NL80211_IFTYPE_AP) + ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); + else + ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); + /* Force a TSF reset if requested and enable beacons */ + if (interval & AR5K_BEACON_RESET_TSF) + ath5k_hw_reset_tsf(ah); + ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD | - AR5K_BEACON_RESET_TSF | AR5K_BEACON_ENABLE), - AR5K_BEACON); + AR5K_BEACON_ENABLE), + AR5K_BEACON); + + /* Flush any pending BMISS interrupts on ISR by + * performing a clear-on-write operation on PISR + * register for the BMISS bit (writing a bit on + * ISR togles a reset for that bit and leaves + * the rest bits intact) */ + if (ah->ah_version == AR5K_AR5210) + ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR); + else + ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR); + + /* TODO: Set enchanced sleep registers on AR5212 + * based on vif->bss_conf params, until then + * disable power save reporting.*/ + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV); + } #if 0 @@ -899,14 +959,25 @@ int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr) */ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) { - unsigned int i; + unsigned int i, type; ATH5K_TRACE(ah->ah_sc); AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry)); + for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); + /* Reset associated MIC entry if TKIP + * is enabled located at offset (entry + 64) */ + if (type == AR5K_KEYTABLE_TYPE_TKIP) { + entry = entry + AR5K_KEYTABLE_MIC_OFFSET; + AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++) + ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); + } + /* * Set NULL encryption on AR5212+ * diff --git a/drivers/net/wireless/ath5k/reg.h b/drivers/net/wireless/ath5k/reg.h index 0dae54d00f03..69755fc2f9be 100644 --- a/drivers/net/wireless/ath5k/reg.h +++ b/drivers/net/wireless/ath5k/reg.h @@ -1114,14 +1114,16 @@ #define AR5K_PCU_MAX 0x8fff /* - * First station id register (MAC address in lower 32 bits) + * First station id register (Lower 32 bits of MAC address) */ -#define AR5K_STA_ID0 0x8000 +#define AR5K_STA_ID0 0x8000 +#define AR5K_STA_ID0_ARRD_L32 0xffffffff /* - * Second station id register (MAC address in upper 16 bits) + * Second station id register (Upper 16 bits of MAC address + PCU settings) */ #define AR5K_STA_ID1 0x8004 /* Register Address */ +#define AR5K_STA_ID1_ADDR_U16 0x0000ffff /* Upper 16 bits of MAC addres */ #define AR5K_STA_ID1_AP 0x00010000 /* Set AP mode */ #define AR5K_STA_ID1_ADHOC 0x00020000 /* Set Ad-Hoc mode */ #define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting */ @@ -1811,6 +1813,10 @@ #define AR5K_KEYTABLE_MAC1(_n) AR5K_KEYTABLE_OFF(_n, 7) #define AR5K_KEYTABLE_VALID 0x00008000 +/* If key type is TKIP and MIC is enabled + * MIC key goes in offset entry + 64 */ +#define AR5K_KEYTABLE_MIC_OFFSET 64 + /* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit * WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit * WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit -- cgit v1.2.1 From 17683c65c8a5f3f29f5408334992986b996d8205 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 29 Oct 2008 23:24:26 -0400 Subject: ath5k: fix keytable type buglet in ath5k_hw_reset_key Be sure we clear out both the mic (if applicable) and the encryption key type. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/pcu.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index 5674639f7bf8..9c6698520edc 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -960,6 +960,7 @@ int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr) int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) { unsigned int i, type; + u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; ATH5K_TRACE(ah->ah_sc); AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); @@ -972,10 +973,10 @@ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) /* Reset associated MIC entry if TKIP * is enabled located at offset (entry + 64) */ if (type == AR5K_KEYTABLE_TYPE_TKIP) { - entry = entry + AR5K_KEYTABLE_MIC_OFFSET; - AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + AR5K_ASSERT_ENTRY(micentry, AR5K_KEYTABLE_SIZE); for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++) - ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); + ath5k_hw_reg_write(ah, 0, + AR5K_KEYTABLE_OFF(micentry, i)); } /* @@ -987,10 +988,16 @@ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) * Note2: Windows driver (ndiswrapper) sets this to * 0x00000714 instead of 0x00000007 */ - if (ah->ah_version > AR5K_AR5211) + if (ah->ah_version > AR5K_AR5211) { ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, AR5K_KEYTABLE_TYPE(entry)); + if (type == AR5K_KEYTABLE_TYPE_TKIP) { + ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, + AR5K_KEYTABLE_TYPE(micentry)); + } + } + return 0; } -- cgit v1.2.1 From 9ad9a26e7b7b6671a9aefbb1edb2e8a9ed50ee8d Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 29 Oct 2008 08:30:54 -0400 Subject: ath5k: enable hardware encryption for WEP This change re-enables hardware encryption for ath5k after setting up mac80211 to handle the initialization vectors which happens to make it work. Add a module param (nohwcrypt) to optionally turn it off. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 6f368e8cdf17..4bf3678529b8 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -60,6 +60,9 @@ #include "debug.h" static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */ +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); /******************\ @@ -2975,11 +2978,12 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ath5k_softc *sc = hw->priv; int ret = 0; + if (modparam_nohwcrypt) + return -EOPNOTSUPP; + switch (key->alg) { case ALG_WEP: - /* XXX: fix hardware encryption, its not working. For now - * allow software encryption */ - /* break; */ + break; case ALG_TKIP: case ALG_CCMP: return -EOPNOTSUPP; @@ -2999,6 +3003,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } __set_bit(key->keyidx, sc->keymap); key->hw_key_idx = key->keyidx; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; break; case DISABLE_KEY: ath5k_hw_reset_key(sc->ah, key->keyidx); -- cgit v1.2.1 From 3f64b435ab76e79bfb3b4d36f043e6f892093b71 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 29 Oct 2008 23:19:14 -0400 Subject: ath5k: update keycache to support TKIP handling Newer parts have slots at entry+64 for michael mic and can do WPA-TKIP in hardware. The open-sourced Atheros HAL has code for accessing this portion so now we know how where to put the key material. Signed-off-by: Bob Copeland Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 5 +-- drivers/net/wireless/ath5k/pcu.c | 64 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 4bf3678529b8..c7ffcbb9062d 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2983,8 +2983,8 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, switch (key->alg) { case ALG_WEP: - break; case ALG_TKIP: + break; case ALG_CCMP: return -EOPNOTSUPP; default: @@ -3003,7 +3003,8 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } __set_bit(key->keyidx, sc->keymap); key->hw_key_idx = key->keyidx; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV | + IEEE80211_KEY_FLAG_GENERATE_MMIC); break; case DISABLE_KEY: ath5k_hw_reset_key(sc->ah, key->keyidx); diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index 9c6698520edc..d7f0c1017bda 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -1021,17 +1021,29 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, const struct ieee80211_key_conf *key, const u8 *mac) { unsigned int i; + int keylen; __le32 key_v[5] = {}; + __le32 key0 = 0, key1 = 0; + __le32 *rxmic, *txmic; u32 keytype; + u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; + bool is_tkip; ATH5K_TRACE(ah->ah_sc); - /* key->keylen comes in from mac80211 in bytes */ + is_tkip = (key->alg == ALG_TKIP); + + /* + * key->keylen comes in from mac80211 in bytes. + * TKIP is 128 bit + 128 bit mic + */ + keylen = (is_tkip) ? (128 / 8) : key->keylen; - if (key->keylen > AR5K_KEYTABLE_SIZE / 8) + if (entry > AR5K_KEYTABLE_SIZE || + (is_tkip && micentry > AR5K_KEYTABLE_SIZE)) return -EOPNOTSUPP; - switch (key->keylen) { + switch (keylen) { /* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit */ case 40 / 8: memcpy(&key_v[0], key->key, 5); @@ -1045,24 +1057,66 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, memcpy(&key_v[4], &key->key[12], 1); keytype = AR5K_KEYTABLE_TYPE_104; break; - /* WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit */ + /* WEP/TKIP 128-bit = 128-bit entered key + 24 bit IV = 152-bit */ case 128 / 8: memcpy(&key_v[0], &key->key[0], 6); memcpy(&key_v[2], &key->key[6], 6); memcpy(&key_v[4], &key->key[12], 4); - keytype = AR5K_KEYTABLE_TYPE_128; + keytype = is_tkip ? + AR5K_KEYTABLE_TYPE_TKIP : + AR5K_KEYTABLE_TYPE_128; break; default: return -EINVAL; /* shouldn't happen */ } + /* intentionally corrupt key until mic is installed */ + if (is_tkip) { + key0 = key_v[0] = ~key_v[0]; + key1 = key_v[1] = ~key_v[1]; + } + for (i = 0; i < ARRAY_SIZE(key_v); i++) ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), AR5K_KEYTABLE_OFF(entry, i)); ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry)); + if (is_tkip) { + /* Install rx/tx MIC */ + rxmic = (__le32 *) &key->key[16]; + txmic = (__le32 *) &key->key[24]; +#if 0 + /* MISC_MODE register & 0x04 - for mac srev >= griffin */ + key_v[0] = rxmic[0]; + key_v[1] = (txmic[0] >> 16) & 0xffff; + key_v[2] = rxmic[1]; + key_v[3] = txmic[0] & 0xffff; + key_v[4] = txmic[1]; +#else + key_v[0] = rxmic[0]; + key_v[1] = 0; + key_v[2] = rxmic[1]; + key_v[3] = 0; + key_v[4] = 0; +#endif + for (i = 0; i < ARRAY_SIZE(key_v); i++) + ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), + AR5K_KEYTABLE_OFF(micentry, i)); + + ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, + AR5K_KEYTABLE_TYPE(micentry)); + ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC0(micentry)); + ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC1(micentry)); + + /* restore first 2 words of key */ + ath5k_hw_reg_write(ah, le32_to_cpu(~key0), + AR5K_KEYTABLE_OFF(entry, 0)); + ath5k_hw_reg_write(ah, le32_to_cpu(~key1), + AR5K_KEYTABLE_OFF(entry, 1)); + } + return ath5k_hw_set_key_lladdr(ah, entry, mac); } -- cgit v1.2.1 From 8b0162a3dc5c30e862b7a73da29e32de3170f5e4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 3 Nov 2008 11:27:38 +0100 Subject: ath5k: ignore the return value of ath5k_hw_noise_floor_calibration Noise floor calibration occasionally fails on Atheros hardware. This is not fatal and can happen if there's simply too much noise on the air. Ignoring the calibration error is the right thing to do here, because when the error is ignored, the hardware will still work, whereas if the error causes the driver to bail out of a bigger configuration function and does not configure the tx queues or the IMR (as is the case in reset.c), the hw no longer works properly until the next reset. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/phy.c | 4 +--- drivers/net/wireless/ath5k/reset.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c index 69625bf4d11c..7ba18e09463b 100644 --- a/drivers/net/wireless/ath5k/phy.c +++ b/drivers/net/wireless/ath5k/phy.c @@ -2196,9 +2196,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, return ret; } - ret = ath5k_hw_noise_floor_calibration(ah, channel->center_freq); - if (ret) - return ret; + ath5k_hw_noise_floor_calibration(ah, channel->center_freq); /* * Re-enable RX/TX and beacons diff --git a/drivers/net/wireless/ath5k/reset.c b/drivers/net/wireless/ath5k/reset.c index b51bc030da02..5003263c9ea4 100644 --- a/drivers/net/wireless/ath5k/reset.c +++ b/drivers/net/wireless/ath5k/reset.c @@ -842,9 +842,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, * * XXX: Find an interval that's OK for all cards... */ - ret = ath5k_hw_noise_floor_calibration(ah, channel->center_freq); - if (ret) - return ret; + ath5k_hw_noise_floor_calibration(ah, channel->center_freq); /* * Reset queues and start beacon timers at the end of the reset routine -- cgit v1.2.1 From 6a53a8a99cb044e1873648ba184440db6d9c65b8 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Tue, 4 Nov 2008 00:25:54 +0200 Subject: ath5k: Fix IMR init *On a previous patch i splitted AR5K_INT_TX to multiple different TX interrupt flags for better handling but i forgot to unmask the new TXDESC and TXEOL interrupts on ath5k_init and only left TXOK. However for each queue we enable TXDESC and TXEOL interrupts, not TXOK so we don't handle TX interrupts at all (because these interrupts remain masked on PISR) and under load it results packet loss. Fix the problem by unmasking TXDESC and TXEOL on ath5k_init. Signed-Off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index c7ffcbb9062d..1ed152f2dec3 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2219,9 +2219,9 @@ ath5k_init(struct ath5k_softc *sc, bool is_resume) */ sc->curchan = sc->hw->conf.channel; sc->curband = &sc->sbands[sc->curchan->band]; - sc->imask = AR5K_INT_RXOK | AR5K_INT_TXOK | AR5K_INT_RXEOL | - AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL | - AR5K_INT_MIB; + sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | + AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | + AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; ret = ath5k_reset(sc, false, false); if (ret) goto done; -- cgit v1.2.1 From 296bf2aefdf7b4b1bf093a7b42a86ee21b46d07f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 3 Nov 2008 14:43:00 -0800 Subject: ath5k: allow APs to receive beacons Allow APs to receive beacons to detect when it needs to use protection to update the NAV correctly on 11b stations. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 1ed152f2dec3..34cd1a4a297f 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2953,9 +2953,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, test_bit(ATH_STAT_PROMISC, sc->status)) rfilt |= AR5K_RX_FILTER_PROM; if (sc->opmode == NL80211_IFTYPE_STATION || - sc->opmode == NL80211_IFTYPE_ADHOC) { + sc->opmode == NL80211_IFTYPE_ADHOC || + sc->opmode == NL80211_IFTYPE_AP) rfilt |= AR5K_RX_FILTER_BEACON; - } if (sc->opmode == NL80211_IFTYPE_MESH_POINT) rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; -- cgit v1.2.1 From 9764f3f9c3700620f9f8a1f9af57f58758e835da Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Nov 2008 18:56:59 +0100 Subject: ath5k: name pci driver "ath5k" too Call the ath5k pci driver struct "ath5k" too to be less confusing in sysfs. Signed-off-by: Johannes Berg Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 34cd1a4a297f..7fdb59c813ca 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -200,7 +200,7 @@ static int ath5k_pci_resume(struct pci_dev *pdev); #endif /* CONFIG_PM */ static struct pci_driver ath5k_pci_driver = { - .name = "ath5k_pci", + .name = KBUILD_MODNAME, .id_table = ath5k_pci_id_table, .probe = ath5k_pci_probe, .remove = __devexit_p(ath5k_pci_remove), -- cgit v1.2.1 From 0e149cf5a1b1900484f4b808d59a5f246cd720be Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Mon, 17 Nov 2008 23:40:38 -0500 Subject: ath5k: set mac address in add_interface Configure the mac address in add_interface and clear it in remove_interface so that users can change the mac address to something other than the one in the eeprom. Also avoid setting it at attach time so that we won't ack packets until fully set up. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/attach.c | 12 +++--------- drivers/net/wireless/ath5k/base.c | 13 +++++++++++-- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/attach.c b/drivers/net/wireless/ath5k/attach.c index 51d569883cdd..49d82d79d3fc 100644 --- a/drivers/net/wireless/ath5k/attach.c +++ b/drivers/net/wireless/ath5k/attach.c @@ -106,7 +106,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) { struct ath5k_hw *ah; struct pci_dev *pdev = sc->pdev; - u8 mac[ETH_ALEN]; + u8 mac[ETH_ALEN] = {}; int ret; u32 srev; @@ -317,15 +317,9 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) goto err_free; } - /* Set MAC address */ - ret = ath5k_eeprom_read_mac(ah, mac); - if (ret) { - ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n", - sc->pdev->device); - goto err_free; - } - + /* MAC address is cleared until add_interface */ ath5k_hw_set_lladdr(ah, mac); + /* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */ memset(ah->ah_bssid, 0xff, ETH_ALEN); ath5k_hw_set_associd(ah, ah->ah_bssid, 0); diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 7fdb59c813ca..4bcea5a9d70b 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -707,7 +707,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; - u8 mac[ETH_ALEN]; + u8 mac[ETH_ALEN] = {}; int ret; ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device); @@ -777,7 +777,13 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc); setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc); - ath5k_hw_get_lladdr(ah, mac); + ret = ath5k_eeprom_read_mac(ah, mac); + if (ret) { + ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n", + sc->pdev->device); + goto err_queues; + } + SET_IEEE80211_PERM_ADDR(hw, mac); /* All MAC address bits matter for ACKs */ memset(sc->bssidmask, 0xff, ETH_ALEN); @@ -2765,6 +2771,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, /* Set to a reasonable value. Note that this will * be set to mac80211's value at ath5k_config(). */ sc->bintval = 1000; + ath5k_hw_set_lladdr(sc->ah, conf->mac_addr); ret = 0; end: @@ -2777,11 +2784,13 @@ ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) { struct ath5k_softc *sc = hw->priv; + u8 mac[ETH_ALEN] = {}; mutex_lock(&sc->lock); if (sc->vif != conf->vif) goto end; + ath5k_hw_set_lladdr(sc->ah, mac); sc->vif = NULL; end: mutex_unlock(&sc->lock); -- cgit v1.2.1 From 1048643ea94d742bd409f343e284af430656fd6c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 20 Nov 2008 15:16:22 +0100 Subject: ath5k: Clean up eeprom parsing and add missing calibration data This patch brings the ath5k eeprom parsing code in sync with the work done on ath_info by Nick Kossifidis and integrates the missing parts based on the code of the Atheros Legacy HAL release. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/ath5k.h | 7 - drivers/net/wireless/ath5k/eeprom.c | 1194 ++++++++++++++++++++++++++++++----- drivers/net/wireless/ath5k/eeprom.h | 253 ++++++-- drivers/net/wireless/ath5k/reset.c | 2 +- 4 files changed, 1264 insertions(+), 192 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h index a725bb94a52d..5ee2dd1814c2 100644 --- a/drivers/net/wireless/ath5k/ath5k.h +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -821,13 +821,6 @@ struct ath5k_athchan_2ghz { return (false); \ } while (0) -enum ath5k_ant_setting { - AR5K_ANT_VARIABLE = 0, /* variable by programming */ - AR5K_ANT_FIXED_A = 1, /* fixed to 11a frequencies */ - AR5K_ANT_FIXED_B = 2, /* fixed to 11b frequencies */ - AR5K_ANT_MAX = 3, -}; - /* * Hardware interrupt abstraction */ diff --git a/drivers/net/wireless/ath5k/eeprom.c b/drivers/net/wireless/ath5k/eeprom.c index a883839b6a9f..1cb7edfae625 100644 --- a/drivers/net/wireless/ath5k/eeprom.c +++ b/drivers/net/wireless/ath5k/eeprom.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis + * Copyright (c) 2008 Felix Fietkau * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -63,8 +64,8 @@ static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data) /* * Translate binary channel representation in EEPROM to frequency */ -static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin, - unsigned int mode) +static u16 ath5k_eeprom_bin2freq(struct ath5k_eeprom_info *ee, u16 bin, + unsigned int mode) { u16 val; @@ -72,13 +73,13 @@ static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin, return bin; if (mode == AR5K_EEPROM_MODE_11A) { - if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2) + if (ee->ee_version > AR5K_EEPROM_VERSION_3_2) val = (5 * bin) + 4800; else val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 : (bin * 10) + 5100; } else { - if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2) + if (ee->ee_version > AR5K_EEPROM_VERSION_3_2) val = bin + 2300; else val = bin + 2400; @@ -87,6 +88,71 @@ static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin, return val; } +/* + * Initialize eeprom & capabilities structs + */ +static int +ath5k_eeprom_init_header(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + int ret; + u16 val; + + /* Initial TX thermal adjustment values */ + ee->ee_tx_clip = 4; + ee->ee_pwd_84 = ee->ee_pwd_90 = 1; + ee->ee_gain_select = 1; + + /* + * Read values from EEPROM and store them in the capability structure + */ + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); + + /* Return if we have an old EEPROM */ + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0) + return 0; + +#ifdef notyet + /* + * Validate the checksum of the EEPROM date. There are some + * devices with invalid EEPROMs. + */ + for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) { + AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); + cksum ^= val; + } + if (cksum != AR5K_EEPROM_INFO_CKSUM) { + ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum); + return -EIO; + } +#endif + + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version), + ee_ant_gain); + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); + } + + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { + AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val); + ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; + ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; + + AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val); + ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; + ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; + } + + return 0; +} + + /* * Read antenna infos from eeprom */ @@ -100,7 +166,7 @@ static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset, AR5K_EEPROM_READ(o++, val); ee->ee_switch_settling[mode] = (val >> 8) & 0x7f; - ee->ee_ant_tx_rx[mode] = (val >> 2) & 0x3f; + ee->ee_atn_tx_rx[mode] = (val >> 2) & 0x3f; ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; AR5K_EEPROM_READ(o++, val); @@ -157,6 +223,30 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, u16 val; int ret; + ee->ee_n_piers[mode] = 0; + AR5K_EEPROM_READ(o++, val); + ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); + switch(mode) { + case AR5K_EEPROM_MODE_11A: + ee->ee_ob[mode][3] = (val >> 5) & 0x7; + ee->ee_db[mode][3] = (val >> 2) & 0x7; + ee->ee_ob[mode][2] = (val << 1) & 0x7; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ob[mode][2] |= (val >> 15) & 0x1; + ee->ee_db[mode][2] = (val >> 12) & 0x7; + ee->ee_ob[mode][1] = (val >> 9) & 0x7; + ee->ee_db[mode][1] = (val >> 6) & 0x7; + ee->ee_ob[mode][0] = (val >> 3) & 0x7; + ee->ee_db[mode][0] = val & 0x7; + break; + case AR5K_EEPROM_MODE_11G: + case AR5K_EEPROM_MODE_11B: + ee->ee_ob[mode][1] = (val >> 4) & 0x7; + ee->ee_db[mode][1] = val & 0x7; + break; + } + AR5K_EEPROM_READ(o++, val); ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff; ee->ee_thr_62[mode] = val & 0xff; @@ -209,8 +299,11 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, AR5K_EEPROM_READ(o++, val); ee->ee_i_gain[mode] |= (val << 3) & 0x38; - if (mode == AR5K_EEPROM_MODE_11G) + if (mode == AR5K_EEPROM_MODE_11G) { ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff; + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6) + ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; + } } if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && @@ -219,10 +312,77 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, ee->ee_q_cal[mode] = (val >> 3) & 0x1f; } - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6 && - mode == AR5K_EEPROM_MODE_11G) - ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0) + goto done; + + switch(mode) { + case AR5K_EEPROM_MODE_11A: + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1) + break; + + AR5K_EEPROM_READ(o++, val); + ee->ee_margin_tx_rx[mode] = val & 0x3f; + break; + case AR5K_EEPROM_MODE_11B: + AR5K_EEPROM_READ(o++, val); + + ee->ee_pwr_cal_b[0].freq = + ath5k_eeprom_bin2freq(ee, val & 0xff, mode); + if (ee->ee_pwr_cal_b[0].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + ee->ee_pwr_cal_b[1].freq = + ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); + if (ee->ee_pwr_cal_b[1].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + AR5K_EEPROM_READ(o++, val); + ee->ee_pwr_cal_b[2].freq = + ath5k_eeprom_bin2freq(ee, val & 0xff, mode); + if (ee->ee_pwr_cal_b[2].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) + ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + break; + case AR5K_EEPROM_MODE_11G: + AR5K_EEPROM_READ(o++, val); + + ee->ee_pwr_cal_g[0].freq = + ath5k_eeprom_bin2freq(ee, val & 0xff, mode); + if (ee->ee_pwr_cal_g[0].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + ee->ee_pwr_cal_g[1].freq = + ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); + if (ee->ee_pwr_cal_g[1].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + AR5K_EEPROM_READ(o++, val); + ee->ee_turbo_max_power[mode] = val & 0x7f; + ee->ee_xr_power[mode] = (val >> 7) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_pwr_cal_g[2].freq = + ath5k_eeprom_bin2freq(ee, val & 0xff, mode); + if (ee->ee_pwr_cal_g[2].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) + ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_i_cal[mode] = (val >> 8) & 0x3f; + ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { + AR5K_EEPROM_READ(o++, val); + ee->ee_cck_ofdm_gain_delta = val & 0xff; + } + break; + } + +done: /* return new offset */ *offset = o; @@ -230,204 +390,944 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, } /* - * Initialize eeprom & capabilities structs + * Read turbo mode information on newer EEPROM versions */ -int ath5k_eeprom_init(struct ath5k_hw *ah) +static int +ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah, + u32 *offset, unsigned int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; - unsigned int mode, i; - int ret; - u32 offset; + u32 o = *offset; u16 val; + int ret; - /* Initial TX thermal adjustment values */ - ee->ee_tx_clip = 4; - ee->ee_pwd_84 = ee->ee_pwd_90 = 1; - ee->ee_gain_select = 1; + if (ee->ee_version < AR5K_EEPROM_VERSION_5_0) + return 0; - /* - * Read values from EEPROM and store them in the capability structure - */ - AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); - AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); - AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); - AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); - AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); + switch (mode){ + case AR5K_EEPROM_MODE_11A: + ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f; - /* Return if we have an old EEPROM */ - if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0) - return 0; + ee->ee_atn_tx_rx_turbo[mode] = (val >> 13) & 0x7; + AR5K_EEPROM_READ(o++, val); + ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x7) << 3; + ee->ee_margin_tx_rx_turbo[mode] = (val >> 3) & 0x3f; + + ee->ee_adc_desired_size_turbo[mode] = (val >> 9) & 0x7f; + AR5K_EEPROM_READ(o++, val); + ee->ee_adc_desired_size_turbo[mode] |= (val & 0x1) << 7; + ee->ee_pga_desired_size_turbo[mode] = (val >> 1) & 0xff; + + if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >=2) + ee->ee_pd_gain_overlap = (val >> 9) & 0xf; + break; + case AR5K_EEPROM_MODE_11G: + ee->ee_switch_settling_turbo[mode] = (val >> 8) & 0x7f; + + ee->ee_atn_tx_rx_turbo[mode] = (val >> 15) & 0x7; + AR5K_EEPROM_READ(o++, val); + ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x1f) << 1; + ee->ee_margin_tx_rx_turbo[mode] = (val >> 5) & 0x3f; + + ee->ee_adc_desired_size_turbo[mode] = (val >> 11) & 0x7f; + AR5K_EEPROM_READ(o++, val); + ee->ee_adc_desired_size_turbo[mode] |= (val & 0x7) << 5; + ee->ee_pga_desired_size_turbo[mode] = (val >> 3) & 0xff; + break; + } + + /* return new offset */ + *offset = o; + + return 0; +} + + +static int +ath5k_eeprom_init_modes(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 mode_offset[3]; + unsigned int mode; + u32 offset; + int ret; -#ifdef notyet /* - * Validate the checksum of the EEPROM date. There are some - * devices with invalid EEPROMs. + * Get values for all modes */ - for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) { - AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); - cksum ^= val; + mode_offset[AR5K_EEPROM_MODE_11A] = AR5K_EEPROM_MODES_11A(ah->ah_ee_version); + mode_offset[AR5K_EEPROM_MODE_11B] = AR5K_EEPROM_MODES_11B(ah->ah_ee_version); + mode_offset[AR5K_EEPROM_MODE_11G] = AR5K_EEPROM_MODES_11G(ah->ah_ee_version); + + ee->ee_turbo_max_power[AR5K_EEPROM_MODE_11A] = + AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); + + for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) { + offset = mode_offset[mode]; + + ret = ath5k_eeprom_read_ants(ah, &offset, mode); + if (ret) + return ret; + + ret = ath5k_eeprom_read_modes(ah, &offset, mode); + if (ret) + return ret; + + ret = ath5k_eeprom_read_turbo_modes(ah, &offset, mode); + if (ret) + return ret; } - if (cksum != AR5K_EEPROM_INFO_CKSUM) { - ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum); - return -EIO; + + /* override for older eeprom versions for better performance */ + if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) { + ee->ee_thr_62[AR5K_EEPROM_MODE_11A] = 15; + ee->ee_thr_62[AR5K_EEPROM_MODE_11B] = 28; + ee->ee_thr_62[AR5K_EEPROM_MODE_11G] = 28; } -#endif - AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version), - ee_ant_gain); + return 0; +} - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { - AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); - AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); - } +static inline void +ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) +{ + const static u16 intercepts3[] = + { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 }; + const static u16 intercepts3_2[] = + { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; + const u16 *ip; + int i; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2) + ip = intercepts3_2; + else + ip = intercepts3; - if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { - AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val); - ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; - ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; + for (i = 0; i < ARRAY_SIZE(intercepts3); i++) + *vp++ = (ip[i] * max + (100 - ip[i]) * min) / 100; +} - AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val); - ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; - ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; +static inline int +ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max, + struct ath5k_chan_pcal_info *pc, u8 *count) +{ + int o = *offset; + int i = 0; + u8 f1, f2; + int ret; + u16 val; + + while(i < max) { + AR5K_EEPROM_READ(o++, val); + + f1 = (val >> 8) & 0xff; + f2 = val & 0xff; + + if (f1) + pc[i++].freq = f1; + + if (f2) + pc[i++].freq = f2; + + if (!f1 || !f2) + break; } + *offset = o; + *count = i; - /* - * Get conformance test limit values - */ - offset = AR5K_EEPROM_CTL(ah->ah_ee_version); - ee->ee_ctls = AR5K_EEPROM_N_CTLS(ah->ah_ee_version); + return 0; +} + +static int +ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *pcal = ee->ee_pwr_cal_a; + int i, ret; + u16 val; + u8 mask; + + if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { + ath5k_eeprom_read_freq_list(ah, &offset, + AR5K_EEPROM_N_5GHZ_CHAN, pcal, + &ee->ee_n_piers[AR5K_EEPROM_MODE_11A]); + } else { + mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version); - for (i = 0; i < ee->ee_ctls; i++) { AR5K_EEPROM_READ(offset++, val); - ee->ee_ctl[i] = (val >> 8) & 0xff; - ee->ee_ctl[i + 1] = val & 0xff; + pcal[0].freq = (val >> 9) & mask; + pcal[1].freq = (val >> 2) & mask; + pcal[2].freq = (val << 5) & mask; + + AR5K_EEPROM_READ(offset++, val); + pcal[2].freq |= (val >> 11) & 0x1f; + pcal[3].freq = (val >> 4) & mask; + pcal[4].freq = (val << 3) & mask; + + AR5K_EEPROM_READ(offset++, val); + pcal[4].freq |= (val >> 13) & 0x7; + pcal[5].freq = (val >> 6) & mask; + pcal[6].freq = (val << 1) & mask; + + AR5K_EEPROM_READ(offset++, val); + pcal[6].freq |= (val >> 15) & 0x1; + pcal[7].freq = (val >> 8) & mask; + pcal[8].freq = (val >> 1) & mask; + pcal[9].freq = (val << 6) & mask; + + AR5K_EEPROM_READ(offset++, val); + pcal[9].freq |= (val >> 10) & 0x3f; + ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10; } - /* - * Get values for 802.11a (5GHz) - */ - mode = AR5K_EEPROM_MODE_11A; + for(i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i += 1) { + pcal[i].freq = ath5k_eeprom_bin2freq(ee, + pcal[i].freq, AR5K_EEPROM_MODE_11A); + } - ee->ee_turbo_max_power[mode] = - AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); + return 0; +} - offset = AR5K_EEPROM_MODES_11A(ah->ah_ee_version); +static inline int +ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *pcal; + int i; + + switch(mode) { + case AR5K_EEPROM_MODE_11B: + pcal = ee->ee_pwr_cal_b; + break; + case AR5K_EEPROM_MODE_11G: + pcal = ee->ee_pwr_cal_g; + break; + default: + return -EINVAL; + } - ret = ath5k_eeprom_read_ants(ah, &offset, mode); - if (ret) - return ret; + ath5k_eeprom_read_freq_list(ah, &offset, + AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal, + &ee->ee_n_piers[mode]); + for(i = 0; i < AR5K_EEPROM_N_2GHZ_CHAN_2413; i += 1) { + pcal[i].freq = ath5k_eeprom_bin2freq(ee, + pcal[i].freq, mode); + } - AR5K_EEPROM_READ(offset++, val); - ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); - ee->ee_ob[mode][3] = (val >> 5) & 0x7; - ee->ee_db[mode][3] = (val >> 2) & 0x7; - ee->ee_ob[mode][2] = (val << 1) & 0x7; - - AR5K_EEPROM_READ(offset++, val); - ee->ee_ob[mode][2] |= (val >> 15) & 0x1; - ee->ee_db[mode][2] = (val >> 12) & 0x7; - ee->ee_ob[mode][1] = (val >> 9) & 0x7; - ee->ee_db[mode][1] = (val >> 6) & 0x7; - ee->ee_ob[mode][0] = (val >> 3) & 0x7; - ee->ee_db[mode][0] = val & 0x7; - - ret = ath5k_eeprom_read_modes(ah, &offset, mode); - if (ret) - return ret; + return 0; +} - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) { - AR5K_EEPROM_READ(offset++, val); - ee->ee_margin_tx_rx[mode] = val & 0x3f; + +static int +ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *pcal; + int offset, ret; + int i, j; + u16 val; + + offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); + switch(mode) { + case AR5K_EEPROM_MODE_11A: + if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) + return 0; + + ret = ath5k_eeprom_init_11a_pcal_freq(ah, + offset + AR5K_EEPROM_GROUP1_OFFSET); + if (ret < 0) + return ret; + + offset += AR5K_EEPROM_GROUP2_OFFSET; + pcal = ee->ee_pwr_cal_a; + break; + case AR5K_EEPROM_MODE_11B: + if (!AR5K_EEPROM_HDR_11B(ee->ee_header) && + !AR5K_EEPROM_HDR_11G(ee->ee_header)) + return 0; + + pcal = ee->ee_pwr_cal_b; + offset += AR5K_EEPROM_GROUP3_OFFSET; + + /* fixed piers */ + pcal[0].freq = 2412; + pcal[1].freq = 2447; + pcal[2].freq = 2484; + ee->ee_n_piers[mode] = 3; + break; + case AR5K_EEPROM_MODE_11G: + if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) + return 0; + + pcal = ee->ee_pwr_cal_g; + offset += AR5K_EEPROM_GROUP4_OFFSET; + + /* fixed piers */ + pcal[0].freq = 2312; + pcal[1].freq = 2412; + pcal[2].freq = 2484; + ee->ee_n_piers[mode] = 3; + break; + default: + return -EINVAL; } - /* - * Get values for 802.11b (2.4GHz) - */ - mode = AR5K_EEPROM_MODE_11B; - offset = AR5K_EEPROM_MODES_11B(ah->ah_ee_version); + for (i = 0; i < ee->ee_n_piers[mode]; i++) { + struct ath5k_chan_pcal_info_rf5111 *cdata = + &pcal[i].rf5111_info; - ret = ath5k_eeprom_read_ants(ah, &offset, mode); - if (ret) - return ret; + AR5K_EEPROM_READ(offset++, val); + cdata->pcdac_max = ((val >> 10) & AR5K_EEPROM_PCDAC_M); + cdata->pcdac_min = ((val >> 4) & AR5K_EEPROM_PCDAC_M); + cdata->pwr[0] = ((val << 2) & AR5K_EEPROM_POWER_M); - AR5K_EEPROM_READ(offset++, val); - ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); - ee->ee_ob[mode][1] = (val >> 4) & 0x7; - ee->ee_db[mode][1] = val & 0x7; + AR5K_EEPROM_READ(offset++, val); + cdata->pwr[0] |= ((val >> 14) & 0x3); + cdata->pwr[1] = ((val >> 8) & AR5K_EEPROM_POWER_M); + cdata->pwr[2] = ((val >> 2) & AR5K_EEPROM_POWER_M); + cdata->pwr[3] = ((val << 4) & AR5K_EEPROM_POWER_M); - ret = ath5k_eeprom_read_modes(ah, &offset, mode); - if (ret) - return ret; + AR5K_EEPROM_READ(offset++, val); + cdata->pwr[3] |= ((val >> 12) & 0xf); + cdata->pwr[4] = ((val >> 6) & AR5K_EEPROM_POWER_M); + cdata->pwr[5] = (val & AR5K_EEPROM_POWER_M); - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { AR5K_EEPROM_READ(offset++, val); - ee->ee_cal_pier[mode][0] = - ath5k_eeprom_bin2freq(ah, val & 0xff, mode); - ee->ee_cal_pier[mode][1] = - ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode); + cdata->pwr[6] = ((val >> 10) & AR5K_EEPROM_POWER_M); + cdata->pwr[7] = ((val >> 4) & AR5K_EEPROM_POWER_M); + cdata->pwr[8] = ((val << 2) & AR5K_EEPROM_POWER_M); AR5K_EEPROM_READ(offset++, val); - ee->ee_cal_pier[mode][2] = - ath5k_eeprom_bin2freq(ah, val & 0xff, mode); + cdata->pwr[8] |= ((val >> 14) & 0x3); + cdata->pwr[9] = ((val >> 8) & AR5K_EEPROM_POWER_M); + cdata->pwr[10] = ((val >> 2) & AR5K_EEPROM_POWER_M); + + ath5k_get_pcdac_intercepts(ah, cdata->pcdac_min, + cdata->pcdac_max, cdata->pcdac); + + for (j = 0; j < AR5K_EEPROM_N_PCDAC; j++) { + cdata->pwr[j] = (u16) + (AR5K_EEPROM_POWER_STEP * cdata->pwr[j]); + } } - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) - ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + return 0; +} - /* - * Get values for 802.11g (2.4GHz) - */ - mode = AR5K_EEPROM_MODE_11G; - offset = AR5K_EEPROM_MODES_11G(ah->ah_ee_version); +static int +ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info; + struct ath5k_chan_pcal_info *gen_chan_info; + u32 offset; + unsigned int i, c; + u16 val; + int ret; - ret = ath5k_eeprom_read_ants(ah, &offset, mode); - if (ret) - return ret; + switch (mode) { + case AR5K_EEPROM_MODE_11A: + /* + * Read 5GHz EEPROM channels + */ + offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); + ath5k_eeprom_init_11a_pcal_freq(ah, offset); + + offset += AR5K_EEPROM_GROUP2_OFFSET; + gen_chan_info = ee->ee_pwr_cal_a; + break; + case AR5K_EEPROM_MODE_11B: + offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); + if (AR5K_EEPROM_HDR_11A(ee->ee_header)) + offset += AR5K_EEPROM_GROUP3_OFFSET; + + /* NB: frequency piers parsed during mode init */ + gen_chan_info = ee->ee_pwr_cal_b; + break; + case AR5K_EEPROM_MODE_11G: + offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); + if (AR5K_EEPROM_HDR_11A(ee->ee_header)) + offset += AR5K_EEPROM_GROUP4_OFFSET; + else if (AR5K_EEPROM_HDR_11B(ee->ee_header)) + offset += AR5K_EEPROM_GROUP2_OFFSET; + + /* NB: frequency piers parsed during mode init */ + gen_chan_info = ee->ee_pwr_cal_g; + break; + default: + return -EINVAL; + } - AR5K_EEPROM_READ(offset++, val); - ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); - ee->ee_ob[mode][1] = (val >> 4) & 0x7; - ee->ee_db[mode][1] = val & 0x7; + for (i = 0; i < ee->ee_n_piers[mode]; i++) { + chan_pcal_info = &gen_chan_info[i].rf5112_info; - ret = ath5k_eeprom_read_modes(ah, &offset, mode); - if (ret) - return ret; + /* Power values in dBm * 4 + * for the lower xpd gain curve + * (0 dBm -> higher output power) */ + for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) { + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr_x0[c] = (val & 0xff); + chan_pcal_info->pwr_x0[++c] = ((val >> 8) & 0xff); + } - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + /* PCDAC steps + * corresponding to the above power + * measurements */ AR5K_EEPROM_READ(offset++, val); - ee->ee_cal_pier[mode][0] = - ath5k_eeprom_bin2freq(ah, val & 0xff, mode); - ee->ee_cal_pier[mode][1] = - ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode); + chan_pcal_info->pcdac_x0[1] = (val & 0x1f); + chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f); + chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f); + /* Power values in dBm * 4 + * for the higher xpd gain curve + * (18 dBm -> lower output power) */ AR5K_EEPROM_READ(offset++, val); - ee->ee_turbo_max_power[mode] = val & 0x7f; - ee->ee_xr_power[mode] = (val >> 7) & 0x3f; + chan_pcal_info->pwr_x3[0] = (val & 0xff); + chan_pcal_info->pwr_x3[1] = ((val >> 8) & 0xff); AR5K_EEPROM_READ(offset++, val); - ee->ee_cal_pier[mode][2] = - ath5k_eeprom_bin2freq(ah, val & 0xff, mode); + chan_pcal_info->pwr_x3[2] = (val & 0xff); + + /* PCDAC steps + * corresponding to the above power + * measurements (static) */ + chan_pcal_info->pcdac_x3[0] = 20; + chan_pcal_info->pcdac_x3[1] = 35; + chan_pcal_info->pcdac_x3[2] = 63; + + if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) { + chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0xff); + + /* Last xpd0 power level is also channel maximum */ + gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3]; + } else { + chan_pcal_info->pcdac_x0[0] = 1; + gen_chan_info[i].max_pwr = ((val >> 8) & 0xff); + } - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) - ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + /* Recreate pcdac_x0 table for this channel using pcdac steps */ + chan_pcal_info->pcdac_x0[1] += chan_pcal_info->pcdac_x0[0]; + chan_pcal_info->pcdac_x0[2] += chan_pcal_info->pcdac_x0[1]; + chan_pcal_info->pcdac_x0[3] += chan_pcal_info->pcdac_x0[2]; + } + + return 0; +} + +static inline unsigned int +ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode) +{ + static const unsigned int pdgains_size[] = { 4, 6, 9, 12 }; + unsigned int sz; + + sz = pdgains_size[ee->ee_pd_gains[mode] - 1]; + sz *= ee->ee_n_piers[mode]; + + return sz; +} + +static unsigned int +ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) +{ + u32 offset = AR5K_EEPROM_CAL_DATA_START(ee->ee_misc4); + + switch(mode) { + case AR5K_EEPROM_MODE_11G: + if (AR5K_EEPROM_HDR_11B(ee->ee_header)) + offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) + 2; + /* fall through */ + case AR5K_EEPROM_MODE_11B: + if (AR5K_EEPROM_HDR_11A(ee->ee_header)) + offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) + 5; + /* fall through */ + case AR5K_EEPROM_MODE_11A: + break; + default: + break; + } + + return offset; +} + +static int +ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf2413 *chan_pcal_info; + struct ath5k_chan_pcal_info *gen_chan_info; + unsigned int i, c; + u32 offset; + int ret; + u16 val; + u8 pd_gains = 0; + + if (ee->ee_x_gain[mode] & 0x1) pd_gains++; + if ((ee->ee_x_gain[mode] >> 1) & 0x1) pd_gains++; + if ((ee->ee_x_gain[mode] >> 2) & 0x1) pd_gains++; + if ((ee->ee_x_gain[mode] >> 3) & 0x1) pd_gains++; + ee->ee_pd_gains[mode] = pd_gains; + + offset = ath5k_cal_data_offset_2413(ee, mode); + switch (mode) { + case AR5K_EEPROM_MODE_11A: + if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) + return 0; + + ath5k_eeprom_init_11a_pcal_freq(ah, offset); + offset += AR5K_EEPROM_N_5GHZ_CHAN / 2; + gen_chan_info = ee->ee_pwr_cal_a; + break; + case AR5K_EEPROM_MODE_11B: + if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) + return 0; + ath5k_eeprom_init_11bg_2413(ah, mode, offset); + offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; + gen_chan_info = ee->ee_pwr_cal_b; + break; + case AR5K_EEPROM_MODE_11G: + if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) + return 0; + + ath5k_eeprom_init_11bg_2413(ah, mode, offset); + offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; + gen_chan_info = ee->ee_pwr_cal_g; + break; + default: + return -EINVAL; + } + + if (pd_gains == 0) + return 0; + + for (i = 0; i < ee->ee_n_piers[mode]; i++) { + chan_pcal_info = &gen_chan_info[i].rf2413_info; + + /* + * Read pwr_i, pddac_i and the first + * 2 pd points (pwr, pddac) + */ AR5K_EEPROM_READ(offset++, val); - ee->ee_i_cal[mode] = (val >> 8) & 0x3f; - ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + chan_pcal_info->pwr_i[0] = val & 0x1f; + chan_pcal_info->pddac_i[0] = (val >> 5) & 0x7f; + chan_pcal_info->pwr[0][0] = + (val >> 12) & 0xf; - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pddac[0][0] = val & 0x3f; + chan_pcal_info->pwr[0][1] = (val >> 6) & 0xf; + chan_pcal_info->pddac[0][1] = + (val >> 10) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr[0][2] = val & 0xf; + chan_pcal_info->pddac[0][2] = + (val >> 4) & 0x3f; + + chan_pcal_info->pwr[0][3] = 0; + chan_pcal_info->pddac[0][3] = 0; + + if (pd_gains > 1) { + /* + * Pd gain 0 is not the last pd gain + * so it only has 2 pd points. + * Continue wih pd gain 1. + */ + chan_pcal_info->pwr_i[1] = (val >> 10) & 0x1f; + + chan_pcal_info->pddac_i[1] = (val >> 15) & 0x1; AR5K_EEPROM_READ(offset++, val); - ee->ee_cck_ofdm_gain_delta = val & 0xff; + chan_pcal_info->pddac_i[1] |= (val & 0x3F) << 1; + + chan_pcal_info->pwr[1][0] = (val >> 6) & 0xf; + chan_pcal_info->pddac[1][0] = + (val >> 10) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr[1][1] = val & 0xf; + chan_pcal_info->pddac[1][1] = + (val >> 4) & 0x3f; + chan_pcal_info->pwr[1][2] = + (val >> 10) & 0xf; + + chan_pcal_info->pddac[1][2] = + (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pddac[1][2] |= + (val & 0xF) << 2; + + chan_pcal_info->pwr[1][3] = 0; + chan_pcal_info->pddac[1][3] = 0; + } else if (pd_gains == 1) { + /* + * Pd gain 0 is the last one so + * read the extra point. + */ + chan_pcal_info->pwr[0][3] = + (val >> 10) & 0xf; + + chan_pcal_info->pddac[0][3] = + (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pddac[0][3] |= + (val & 0xF) << 2; + } + + /* + * Proceed with the other pd_gains + * as above. + */ + if (pd_gains > 2) { + chan_pcal_info->pwr_i[2] = (val >> 4) & 0x1f; + chan_pcal_info->pddac_i[2] = (val >> 9) & 0x7f; + + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr[2][0] = + (val >> 0) & 0xf; + chan_pcal_info->pddac[2][0] = + (val >> 4) & 0x3f; + chan_pcal_info->pwr[2][1] = + (val >> 10) & 0xf; + + chan_pcal_info->pddac[2][1] = + (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pddac[2][1] |= + (val & 0xF) << 2; + + chan_pcal_info->pwr[2][2] = + (val >> 4) & 0xf; + chan_pcal_info->pddac[2][2] = + (val >> 8) & 0x3f; + + chan_pcal_info->pwr[2][3] = 0; + chan_pcal_info->pddac[2][3] = 0; + } else if (pd_gains == 2) { + chan_pcal_info->pwr[1][3] = + (val >> 4) & 0xf; + chan_pcal_info->pddac[1][3] = + (val >> 8) & 0x3f; + } + + if (pd_gains > 3) { + chan_pcal_info->pwr_i[3] = (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr_i[3] |= ((val >> 0) & 0x7) << 2; + + chan_pcal_info->pddac_i[3] = (val >> 3) & 0x7f; + chan_pcal_info->pwr[3][0] = + (val >> 10) & 0xf; + chan_pcal_info->pddac[3][0] = + (val >> 14) & 0x3; + + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pddac[3][0] |= + (val & 0xF) << 2; + chan_pcal_info->pwr[3][1] = + (val >> 4) & 0xf; + chan_pcal_info->pddac[3][1] = + (val >> 8) & 0x3f; + + chan_pcal_info->pwr[3][2] = + (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr[3][2] |= + ((val >> 0) & 0x3) << 2; + + chan_pcal_info->pddac[3][2] = + (val >> 2) & 0x3f; + chan_pcal_info->pwr[3][3] = + (val >> 8) & 0xf; + + chan_pcal_info->pddac[3][3] = + (val >> 12) & 0xF; + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pddac[3][3] |= + ((val >> 0) & 0x3) << 4; + } else if (pd_gains == 3) { + chan_pcal_info->pwr[2][3] = + (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr[2][3] |= + ((val >> 0) & 0x3) << 2; + + chan_pcal_info->pddac[2][3] = + (val >> 2) & 0x3f; + } + + for (c = 0; c < pd_gains; c++) { + /* Recreate pwr table for this channel using pwr steps */ + chan_pcal_info->pwr[c][0] += chan_pcal_info->pwr_i[c] * 2; + chan_pcal_info->pwr[c][1] += chan_pcal_info->pwr[c][0]; + chan_pcal_info->pwr[c][2] += chan_pcal_info->pwr[c][1]; + chan_pcal_info->pwr[c][3] += chan_pcal_info->pwr[c][2]; + if (chan_pcal_info->pwr[c][3] == chan_pcal_info->pwr[c][2]) + chan_pcal_info->pwr[c][3] = 0; + + /* Recreate pddac table for this channel using pddac steps */ + chan_pcal_info->pddac[c][0] += chan_pcal_info->pddac_i[c]; + chan_pcal_info->pddac[c][1] += chan_pcal_info->pddac[c][0]; + chan_pcal_info->pddac[c][2] += chan_pcal_info->pddac[c][1]; + chan_pcal_info->pddac[c][3] += chan_pcal_info->pddac[c][2]; + if (chan_pcal_info->pddac[c][3] == chan_pcal_info->pddac[c][2]) + chan_pcal_info->pddac[c][3] = 0; } } - /* - * Read 5GHz EEPROM channels - */ + return 0; +} + +/* + * Read per rate target power (this is the maximum tx power + * supported by the card). This info is used when setting + * tx power, no matter the channel. + * + * This also works for v5 EEPROMs. + */ +static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_rate_pcal_info *rate_pcal_info; + u16 *rate_target_pwr_num; + u32 offset; + u16 val; + int ret, i; + + offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1); + rate_target_pwr_num = &ee->ee_rate_target_pwr_num[mode]; + switch (mode) { + case AR5K_EEPROM_MODE_11A: + offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version); + rate_pcal_info = ee->ee_rate_tpwr_a; + ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_CHAN; + break; + case AR5K_EEPROM_MODE_11B: + offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version); + rate_pcal_info = ee->ee_rate_tpwr_b; + ee->ee_rate_target_pwr_num[mode] = 2; /* 3rd is g mode's 1st */ + break; + case AR5K_EEPROM_MODE_11G: + offset += AR5K_EEPROM_TARGET_PWR_OFF_11G(ee->ee_version); + rate_pcal_info = ee->ee_rate_tpwr_g; + ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_2GHZ_CHAN; + break; + default: + return -EINVAL; + } + + /* Different freq mask for older eeproms (<= v3.2) */ + if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2) { + for (i = 0; i < (*rate_target_pwr_num); i++) { + AR5K_EEPROM_READ(offset++, val); + rate_pcal_info[i].freq = + ath5k_eeprom_bin2freq(ee, (val >> 9) & 0x7f, mode); + + rate_pcal_info[i].target_power_6to24 = ((val >> 3) & 0x3f); + rate_pcal_info[i].target_power_36 = (val << 3) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + + if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS || + val == 0) { + (*rate_target_pwr_num) = i; + break; + } + + rate_pcal_info[i].target_power_36 |= ((val >> 13) & 0x7); + rate_pcal_info[i].target_power_48 = ((val >> 7) & 0x3f); + rate_pcal_info[i].target_power_54 = ((val >> 1) & 0x3f); + } + } else { + for (i = 0; i < (*rate_target_pwr_num); i++) { + AR5K_EEPROM_READ(offset++, val); + rate_pcal_info[i].freq = + ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); + + rate_pcal_info[i].target_power_6to24 = ((val >> 2) & 0x3f); + rate_pcal_info[i].target_power_36 = (val << 4) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + + if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS || + val == 0) { + (*rate_target_pwr_num) = i; + break; + } + + rate_pcal_info[i].target_power_36 |= (val >> 12) & 0xf; + rate_pcal_info[i].target_power_48 = ((val >> 6) & 0x3f); + rate_pcal_info[i].target_power_54 = (val & 0x3f); + } + } + + return 0; +} + +static int +ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + int (*read_pcal)(struct ath5k_hw *hw, int mode); + int mode; + int err; + + if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) && + (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 1)) + read_pcal = ath5k_eeprom_read_pcal_info_5112; + else if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0) && + (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 2)) + read_pcal = ath5k_eeprom_read_pcal_info_2413; + else + read_pcal = ath5k_eeprom_read_pcal_info_5111; + + for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) { + err = read_pcal(ah, mode); + if (err) + return err; + + err = ath5k_eeprom_read_target_rate_pwr_info(ah, mode); + if (err < 0) + return err; + } + + return 0; +} + +/* Read conformance test limits */ +static int +ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_edge_power *rep; + unsigned int fmask, pmask; + unsigned int ctl_mode; + int ret, i, j; + u32 offset; + u16 val; + + pmask = AR5K_EEPROM_POWER_M; + fmask = AR5K_EEPROM_FREQ_M(ee->ee_version); + offset = AR5K_EEPROM_CTL(ee->ee_version); + ee->ee_ctls = AR5K_EEPROM_N_CTLS(ee->ee_version); + for (i = 0; i < ee->ee_ctls; i += 2) { + AR5K_EEPROM_READ(offset++, val); + ee->ee_ctl[i] = (val >> 8) & 0xff; + ee->ee_ctl[i + 1] = val & 0xff; + } + + offset = AR5K_EEPROM_GROUP8_OFFSET; + if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0) + offset += AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) - + AR5K_EEPROM_GROUP5_OFFSET; + else + offset += AR5K_EEPROM_GROUPS_START(ee->ee_version); + + rep = ee->ee_ctl_pwr; + for(i = 0; i < ee->ee_ctls; i++) { + switch(ee->ee_ctl[i] & AR5K_CTL_MODE_M) { + case AR5K_CTL_11A: + case AR5K_CTL_TURBO: + ctl_mode = AR5K_EEPROM_MODE_11A; + break; + default: + ctl_mode = AR5K_EEPROM_MODE_11G; + break; + } + if (ee->ee_ctl[i] == 0) { + if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) + offset += 8; + else + offset += 7; + rep += AR5K_EEPROM_N_EDGES; + continue; + } + if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { + for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) { + AR5K_EEPROM_READ(offset++, val); + rep[j].freq = (val >> 8) & fmask; + rep[j + 1].freq = val & fmask; + } + for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) { + AR5K_EEPROM_READ(offset++, val); + rep[j].edge = (val >> 8) & pmask; + rep[j].flag = (val >> 14) & 1; + rep[j + 1].edge = val & pmask; + rep[j + 1].flag = (val >> 6) & 1; + } + } else { + AR5K_EEPROM_READ(offset++, val); + rep[0].freq = (val >> 9) & fmask; + rep[1].freq = (val >> 2) & fmask; + rep[2].freq = (val << 5) & fmask; + + AR5K_EEPROM_READ(offset++, val); + rep[2].freq |= (val >> 11) & 0x1f; + rep[3].freq = (val >> 4) & fmask; + rep[4].freq = (val << 3) & fmask; + + AR5K_EEPROM_READ(offset++, val); + rep[4].freq |= (val >> 13) & 0x7; + rep[5].freq = (val >> 6) & fmask; + rep[6].freq = (val << 1) & fmask; + + AR5K_EEPROM_READ(offset++, val); + rep[6].freq |= (val >> 15) & 0x1; + rep[7].freq = (val >> 8) & fmask; + + rep[0].edge = (val >> 2) & pmask; + rep[1].edge = (val << 4) & pmask; + + AR5K_EEPROM_READ(offset++, val); + rep[1].edge |= (val >> 12) & 0xf; + rep[2].edge = (val >> 6) & pmask; + rep[3].edge = val & pmask; + + AR5K_EEPROM_READ(offset++, val); + rep[4].edge = (val >> 10) & pmask; + rep[5].edge = (val >> 4) & pmask; + rep[6].edge = (val << 2) & pmask; + + AR5K_EEPROM_READ(offset++, val); + rep[6].edge |= (val >> 14) & 0x3; + rep[7].edge = (val >> 8) & pmask; + } + for (j = 0; j < AR5K_EEPROM_N_EDGES; j++) { + rep[j].freq = ath5k_eeprom_bin2freq(ee, + rep[j].freq, ctl_mode); + } + rep += AR5K_EEPROM_N_EDGES; + } return 0; } + +/* + * Initialize eeprom power tables + */ +int +ath5k_eeprom_init(struct ath5k_hw *ah) +{ + int err; + + err = ath5k_eeprom_init_header(ah); + if (err < 0) + return err; + + err = ath5k_eeprom_init_modes(ah); + if (err < 0) + return err; + + err = ath5k_eeprom_read_pcal_info(ah); + if (err < 0) + return err; + + err = ath5k_eeprom_read_ctl_info(ah); + if (err < 0) + return err; + + return 0; +} /* * Read the MAC address from eeprom */ diff --git a/drivers/net/wireless/ath5k/eeprom.h b/drivers/net/wireless/ath5k/eeprom.h index a468ecfbb18a..09eb7d0176a4 100644 --- a/drivers/net/wireless/ath5k/eeprom.h +++ b/drivers/net/wireless/ath5k/eeprom.h @@ -25,24 +25,8 @@ #define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */ #define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */ -#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */ -#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */ -#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */ -#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */ -#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008 -#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */ -#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020 -#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */ -#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080 -#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */ -#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200 -#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */ -#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800 -#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */ -#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000 -#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */ -#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000 #define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */ +#define AR5K_EEPROM_CHECKSUM 0x00c0 /* EEPROM checksum */ #define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */ #define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE) #define AR5K_EEPROM_INFO_CKSUM 0xffff @@ -53,15 +37,19 @@ #define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2Ghz (ar5211_rfregs) */ #define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */ #define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */ -#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain ee_cck_ofdm_power_delta (eeprom_read_modes) */ -#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc*, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */ +#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */ +#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */ #define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */ #define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */ -#define AR5K_EEPROM_VERSION_4_3 0x4003 +#define AR5K_EEPROM_VERSION_4_3 0x4003 /* power calibration changes */ #define AR5K_EEPROM_VERSION_4_4 0x4004 #define AR5K_EEPROM_VERSION_4_5 0x4005 #define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */ -#define AR5K_EEPROM_VERSION_4_7 0x4007 +#define AR5K_EEPROM_VERSION_4_7 0x3007 /* 4007 ? */ +#define AR5K_EEPROM_VERSION_4_9 0x4009 /* EAR futureproofing */ +#define AR5K_EEPROM_VERSION_5_0 0x5000 /* Has 2413 PDADC calibration etc */ +#define AR5K_EEPROM_VERSION_5_1 0x5001 /* Has capability values */ +#define AR5K_EEPROM_VERSION_5_3 0x5003 /* Has spur mitigation tables */ #define AR5K_EEPROM_MODE_11A 0 #define AR5K_EEPROM_MODE_11B 1 @@ -74,8 +62,8 @@ #define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz (?) */ #define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */ #define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7) -#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz (?) */ #define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */ +#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz */ #define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c #define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2 @@ -87,27 +75,95 @@ (((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0) #define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3) -#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((int8_t)(((_v) >> 8) & 0xff)) -#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((int8_t)((_v) & 0xff)) +#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((s8)(((_v) >> 8) & 0xff)) +#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((s8)((_v) & 0xff)) + +/* Misc values available since EEPROM 4.0 */ +#define AR5K_EEPROM_MISC0 AR5K_EEPROM_INFO(4) +#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff) +#define AR5K_EEPROM_HDR_XR2_DIS(_v) (((_v) >> 12) & 0x1) +#define AR5K_EEPROM_HDR_XR5_DIS(_v) (((_v) >> 13) & 0x1) +#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3) + +#define AR5K_EEPROM_MISC1 AR5K_EEPROM_INFO(5) +#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff) +#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1) +#define AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(_v) (((_v) >> 15) & 0x1) + +#define AR5K_EEPROM_MISC2 AR5K_EEPROM_INFO(6) +#define AR5K_EEPROM_EEP_FILE_VERSION(_v) (((_v) >> 8) & 0xff) +#define AR5K_EEPROM_EAR_FILE_VERSION(_v) ((_v) & 0xff) + +#define AR5K_EEPROM_MISC3 AR5K_EEPROM_INFO(7) +#define AR5K_EEPROM_ART_BUILD_NUM(_v) (((_v) >> 10) & 0x3f) +#define AR5K_EEPROM_EAR_FILE_ID(_v) ((_v) & 0xff) + +#define AR5K_EEPROM_MISC4 AR5K_EEPROM_INFO(8) +#define AR5K_EEPROM_CAL_DATA_START(_v) (((_v) >> 4) & 0xfff) +#define AR5K_EEPROM_MASK_R0(_v) (((_v) >> 2) & 0x3) +#define AR5K_EEPROM_MASK_R1(_v) ((_v) & 0x3) + +#define AR5K_EEPROM_MISC5 AR5K_EEPROM_INFO(9) +#define AR5K_EEPROM_COMP_DIS(_v) ((_v) & 0x1) +#define AR5K_EEPROM_AES_DIS(_v) (((_v) >> 1) & 0x1) +#define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1) +#define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1) +#define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf) +#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1) +#define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf) + +#define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10) +#define AR5K_EEPROM_TX_CHAIN_DIS ((_v) & 0x8) +#define AR5K_EEPROM_RX_CHAIN_DIS (((_v) >> 3) & 0x8) +#define AR5K_EEPROM_FCC_MID_EN (((_v) >> 6) & 0x1) +#define AR5K_EEPROM_JAP_U1EVEN_EN (((_v) >> 7) & 0x1) +#define AR5K_EEPROM_JAP_U2_EN (((_v) >> 8) & 0x1) +#define AR5K_EEPROM_JAP_U1ODD_EN (((_v) >> 9) & 0x1) +#define AR5K_EEPROM_JAP_11A_NEW_EN (((_v) >> 10) & 0x1) /* calibration settings */ #define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4) #define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2) #define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d) #define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */ +#define AR5K_EEPROM_GROUPS_START(_v) AR5K_EEPROM_OFF(_v, 0x0100, 0x0150) /* Start of Groups */ +#define AR5K_EEPROM_GROUP1_OFFSET 0x0 +#define AR5K_EEPROM_GROUP2_OFFSET 0x5 +#define AR5K_EEPROM_GROUP3_OFFSET 0x37 +#define AR5K_EEPROM_GROUP4_OFFSET 0x46 +#define AR5K_EEPROM_GROUP5_OFFSET 0x55 +#define AR5K_EEPROM_GROUP6_OFFSET 0x65 +#define AR5K_EEPROM_GROUP7_OFFSET 0x69 +#define AR5K_EEPROM_GROUP8_OFFSET 0x6f + +#define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ + AR5K_EEPROM_GROUP5_OFFSET, 0x0000) +#define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ + AR5K_EEPROM_GROUP6_OFFSET, 0x0010) +#define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ + AR5K_EEPROM_GROUP7_OFFSET, 0x0014) /* [3.1 - 3.3] */ #define AR5K_EEPROM_OBDB0_2GHZ 0x00ec #define AR5K_EEPROM_OBDB1_2GHZ 0x00ed -/* Misc values available since EEPROM 4.0 */ -#define AR5K_EEPROM_MISC0 0x00c4 -#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff) -#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3) -#define AR5K_EEPROM_MISC1 0x00c5 -#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff) -#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1) - +#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */ +#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */ +#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */ +#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */ +#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008 +#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */ +#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020 +#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */ +#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080 +#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */ +#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200 +#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */ +#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800 +#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */ +#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000 +#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */ +#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000 /* Some EEPROM defines */ #define AR5K_EEPROM_EEP_SCALE 100 @@ -115,8 +171,11 @@ #define AR5K_EEPROM_N_MODES 3 #define AR5K_EEPROM_N_5GHZ_CHAN 10 #define AR5K_EEPROM_N_2GHZ_CHAN 3 +#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4 #define AR5K_EEPROM_MAX_CHAN 10 +#define AR5K_EEPROM_N_PWR_POINTS_5111 11 #define AR5K_EEPROM_N_PCDAC 11 +#define AR5K_EEPROM_N_PHASE_CAL 5 #define AR5K_EEPROM_N_TEST_FREQ 8 #define AR5K_EEPROM_N_EDGES 8 #define AR5K_EEPROM_N_INTERCEPTS 11 @@ -136,6 +195,8 @@ #define AR5K_EEPROM_N_XPD_PER_CHANNEL 4 #define AR5K_EEPROM_N_XPD0_POINTS 4 #define AR5K_EEPROM_N_XPD3_POINTS 3 +#define AR5K_EEPROM_N_PD_GAINS 4 +#define AR5K_EEPROM_N_PD_POINTS 5 #define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35 #define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55 #define AR5K_EEPROM_POWER_M 0x3f @@ -158,8 +219,99 @@ #define AR5K_EEPROM_READ_HDR(_o, _v) \ AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v); \ -/* Struct to hold EEPROM calibration data */ +enum ath5k_ant_setting { + AR5K_ANT_VARIABLE = 0, /* variable by programming */ + AR5K_ANT_FIXED_A = 1, /* fixed to 11a frequencies */ + AR5K_ANT_FIXED_B = 2, /* fixed to 11b frequencies */ + AR5K_ANT_MAX = 3, +}; + +enum ath5k_ctl_mode { + AR5K_CTL_11A = 0, + AR5K_CTL_11B = 1, + AR5K_CTL_11G = 2, + AR5K_CTL_TURBO = 3, + AR5K_CTL_108G = 4, + AR5K_CTL_2GHT20 = 5, + AR5K_CTL_5GHT20 = 6, + AR5K_CTL_2GHT40 = 7, + AR5K_CTL_5GHT40 = 8, + AR5K_CTL_MODE_M = 15, +}; + +/* Per channel calibration data, used for power table setup */ +struct ath5k_chan_pcal_info_rf5111 { + /* Power levels in half dbm units + * for one power curve. */ + u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111]; + /* PCDAC table steps + * for the above values */ + u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111]; + /* Starting PCDAC step */ + u8 pcdac_min; + /* Final PCDAC step */ + u8 pcdac_max; +}; + +struct ath5k_chan_pcal_info_rf5112 { + /* Power levels in quarter dBm units + * for lower (0) and higher (3) + * level curves */ + s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS]; + s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS]; + /* PCDAC table steps + * for the above values */ + u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS]; + u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS]; +}; + +struct ath5k_chan_pcal_info_rf2413 { + /* Starting pwr/pddac values */ + s8 pwr_i[AR5K_EEPROM_N_PD_GAINS]; + u8 pddac_i[AR5K_EEPROM_N_PD_GAINS]; + /* (pwr,pddac) points */ + s8 pwr[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_N_PD_POINTS]; + u8 pddac[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_N_PD_POINTS]; +}; + +struct ath5k_chan_pcal_info { + /* Frequency */ + u16 freq; + /* Max available power */ + s8 max_pwr; + union { + struct ath5k_chan_pcal_info_rf5111 rf5111_info; + struct ath5k_chan_pcal_info_rf5112 rf5112_info; + struct ath5k_chan_pcal_info_rf2413 rf2413_info; + }; +}; + +/* Per rate calibration data for each mode, used for power table setup */ +struct ath5k_rate_pcal_info { + u16 freq; /* Frequency */ + /* Power level for 6-24Mbit/s rates */ + u16 target_power_6to24; + /* Power level for 36Mbit rate */ + u16 target_power_36; + /* Power level for 48Mbit rate */ + u16 target_power_48; + /* Power level for 54Mbit rate */ + u16 target_power_54; +}; + +/* Power edges for conformance test limits */ +struct ath5k_edge_power { + u16 freq; + u16 edge; /* in half dBm */ + bool flag; +}; + +/* EEPROM calibration data */ struct ath5k_eeprom_info { + + /* Header information */ u16 ee_magic; u16 ee_protect; u16 ee_regdomain; @@ -168,6 +320,11 @@ struct ath5k_eeprom_info { u16 ee_ant_gain; u16 ee_misc0; u16 ee_misc1; + u16 ee_misc2; + u16 ee_misc3; + u16 ee_misc4; + u16 ee_misc5; + u16 ee_misc6; u16 ee_cck_ofdm_gain_delta; u16 ee_cck_ofdm_power_delta; u16 ee_scaled_cck_delta; @@ -185,7 +342,7 @@ struct ath5k_eeprom_info { u16 ee_turbo_max_power[AR5K_EEPROM_N_MODES]; u16 ee_xr_power[AR5K_EEPROM_N_MODES]; u16 ee_switch_settling[AR5K_EEPROM_N_MODES]; - u16 ee_ant_tx_rx[AR5K_EEPROM_N_MODES]; + u16 ee_atn_tx_rx[AR5K_EEPROM_N_MODES]; u16 ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC]; u16 ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB]; u16 ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB]; @@ -198,18 +355,40 @@ struct ath5k_eeprom_info { u16 ee_x_gain[AR5K_EEPROM_N_MODES]; u16 ee_i_gain[AR5K_EEPROM_N_MODES]; u16 ee_margin_tx_rx[AR5K_EEPROM_N_MODES]; + u16 ee_switch_settling_turbo[AR5K_EEPROM_N_MODES]; + u16 ee_margin_tx_rx_turbo[AR5K_EEPROM_N_MODES]; + u16 ee_atn_tx_rx_turbo[AR5K_EEPROM_N_MODES]; - /* Unused */ + /* Power calibration data */ u16 ee_false_detect[AR5K_EEPROM_N_MODES]; - u16 ee_cal_pier[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_2GHZ_CHAN]; - u16 ee_channel[AR5K_EEPROM_N_MODES][AR5K_EEPROM_MAX_CHAN]; /*empty*/ + + /* Number of pd gain curves per mode (RF2413) */ + u8 ee_pd_gains[AR5K_EEPROM_N_MODES]; + + u8 ee_n_piers[AR5K_EEPROM_N_MODES]; + struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN]; + struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN]; + struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN]; + + /* Per rate target power levels */ + u16 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES]; + struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN]; + struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN]; + struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN]; /* Conformance test limits (Unused) */ u16 ee_ctls; u16 ee_ctl[AR5K_EEPROM_MAX_CTLS]; + struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS]; /* Noise Floor Calibration settings */ s16 ee_noise_floor_thr[AR5K_EEPROM_N_MODES]; s8 ee_adc_desired_size[AR5K_EEPROM_N_MODES]; s8 ee_pga_desired_size[AR5K_EEPROM_N_MODES]; + s8 ee_adc_desired_size_turbo[AR5K_EEPROM_N_MODES]; + s8 ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES]; + s8 ee_pd_gain_overlap; + + u32 ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; }; + diff --git a/drivers/net/wireless/ath5k/reset.c b/drivers/net/wireless/ath5k/reset.c index 5003263c9ea4..dc2d7d8bdb7a 100644 --- a/drivers/net/wireless/ath5k/reset.c +++ b/drivers/net/wireless/ath5k/reset.c @@ -674,7 +674,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, (ee->ee_switch_settling[ee_mode] << 7) & 0x3f80, 0xffffc07f); AR5K_REG_MASKED_BITS(ah, AR5K_PHY_GAIN, - (ee->ee_ant_tx_rx[ee_mode] << 12) & 0x3f000, + (ee->ee_atn_tx_rx[ee_mode] << 12) & 0x3f000, 0xfffc0fff); AR5K_REG_MASKED_BITS(ah, AR5K_PHY_DESIRED_SIZE, (ee->ee_adc_desired_size[ee_mode] & 0x00ff) | -- cgit v1.2.1 From f6bac3ea5990653765700b2c3778b08782abebe5 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 26 Nov 2008 16:17:11 -0500 Subject: ath5k: preserve higher order bits when setting mac address In some cases we would like to set the mac address without changing the operating mode. However, Atheros cards store PCU data in the high 16 bits of the mac address register. Change ath5k_hw_set_lladdr() to not clobber the PCU settings. Changes-licensed-under: ISC Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/pcu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index d7f0c1017bda..79879f2d426c 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -267,24 +267,23 @@ void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) * @mac: The card's mac address * * Set station id on hw using the provided mac address - * - * NOTE: This is only called during attach, don't call it - * on reset because it overwrites all AR5K_STA_ID1 settings. - * We have set_opmode (above) for reset. */ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) { u32 low_id, high_id; + u32 pcu_reg; ATH5K_TRACE(ah->ah_sc); /* Set new station ID */ memcpy(ah->ah_sta_id, mac, ETH_ALEN); + pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; + low_id = AR5K_LOW_ID(mac); high_id = AR5K_HIGH_ID(mac); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); - ath5k_hw_reg_write(ah, high_id, AR5K_STA_ID1); + ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); return 0; } -- cgit v1.2.1 From 671434904633876f89be70af415c35c89fb90115 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Tue, 25 Nov 2008 20:55:21 -0500 Subject: ath5k: clean up ath5k_hw_set_key Status: O With the addition of TKIP (and soon CCMP), key->alg is a more useful guide to key type than the key length. This patch cleans up key type assignment in ath5k_hw_set_key by extracting it into its own function. It also replaces the separate memcpy() calls for extracting key material into the hardware format with a loop that works regardless of key size. Finally, the patch removes support for WEP-128 since it is a non-standard key length that mac80211 also doesn't use. Changes-licensed-under: ISC Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/pcu.c | 58 +++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 24 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index 79879f2d426c..ad7ea36502bd 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -1013,6 +1013,23 @@ int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry) AR5K_KEYTABLE_VALID; } +static +int ath5k_keycache_type(const struct ieee80211_key_conf *key) +{ + switch (key->alg) { + case ALG_TKIP: + return AR5K_KEYTABLE_TYPE_TKIP; + case ALG_CCMP: + return AR5K_KEYTABLE_TYPE_CCM; + case ALG_WEP: + if (key->keylen == LEN_WEP40) + return AR5K_KEYTABLE_TYPE_40; + else if (key->keylen == LEN_WEP104) + return AR5K_KEYTABLE_TYPE_104; + } + return -EINVAL; +} + /* * Set a key entry on the table */ @@ -1027,6 +1044,7 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, u32 keytype; u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; bool is_tkip; + const u8 *key_ptr; ATH5K_TRACE(ah->ah_sc); @@ -1042,33 +1060,25 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, (is_tkip && micentry > AR5K_KEYTABLE_SIZE)) return -EOPNOTSUPP; - switch (keylen) { - /* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit */ - case 40 / 8: - memcpy(&key_v[0], key->key, 5); - keytype = AR5K_KEYTABLE_TYPE_40; - break; + if (unlikely(keylen > 16)) + return -EOPNOTSUPP; - /* WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit */ - case 104 / 8: - memcpy(&key_v[0], &key->key[0], 6); - memcpy(&key_v[2], &key->key[6], 6); - memcpy(&key_v[4], &key->key[12], 1); - keytype = AR5K_KEYTABLE_TYPE_104; - break; - /* WEP/TKIP 128-bit = 128-bit entered key + 24 bit IV = 152-bit */ - case 128 / 8: - memcpy(&key_v[0], &key->key[0], 6); - memcpy(&key_v[2], &key->key[6], 6); - memcpy(&key_v[4], &key->key[12], 4); - keytype = is_tkip ? - AR5K_KEYTABLE_TYPE_TKIP : - AR5K_KEYTABLE_TYPE_128; - break; + keytype = ath5k_keycache_type(key); + if (keytype < 0) + return keytype; - default: - return -EINVAL; /* shouldn't happen */ + /* + * each key block is 6 bytes wide, written as pairs of + * alternating 32 and 16 bit le values. + */ + key_ptr = key->key; + for (i = 0; keylen >= 6; keylen -= 6) { + memcpy(&key_v[i], key_ptr, 6); + i += 2; + key_ptr += 6; } + if (keylen) + memcpy(&key_v[i], key_ptr, keylen); /* intentionally corrupt key until mic is installed */ if (is_tkip) { -- cgit v1.2.1 From f650470a8f506bc33a15778432ebb8cdcf89175b Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 26 Nov 2008 16:17:25 -0500 Subject: ath5k: enable combined michael mic in key cache For mac revisions >= "Griffin," the hardware allows the mic tx and rx authenticator keys to share the same cache line, whereas earlier hardware can only store the rx. Enable the combined mic on hardware that supports it. Changes to ath5k.h Changes-licensed-under: 3-Clause-BSD Changes to attach.c, pcu.c, reg.h Changes-licensed-under: ISC Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/ath5k.h | 1 + drivers/net/wireless/ath5k/attach.c | 6 ++++++ drivers/net/wireless/ath5k/pcu.c | 28 ++++++++++++++-------------- drivers/net/wireless/ath5k/reg.h | 1 + 4 files changed, 22 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h index 5ee2dd1814c2..13df1191b070 100644 --- a/drivers/net/wireless/ath5k/ath5k.h +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -1052,6 +1052,7 @@ struct ath5k_hw { bool ah_calibration; bool ah_running; bool ah_single_chip; + bool ah_combined_mic; enum ath5k_rfgain ah_rf_gain; u32 ah_mac_srev; diff --git a/drivers/net/wireless/ath5k/attach.c b/drivers/net/wireless/ath5k/attach.c index 49d82d79d3fc..dea378f76731 100644 --- a/drivers/net/wireless/ath5k/attach.c +++ b/drivers/net/wireless/ath5k/attach.c @@ -317,6 +317,12 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) goto err_free; } + if (srev >= AR5K_SREV_AR2414) { + ah->ah_combined_mic = true; + AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE, + AR5K_MISC_MODE_COMBINED_MIC); + } + /* MAC address is cleared until add_interface */ ath5k_hw_set_lladdr(ah, mac); diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index ad7ea36502bd..dabe42219e2a 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -1096,20 +1096,20 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, /* Install rx/tx MIC */ rxmic = (__le32 *) &key->key[16]; txmic = (__le32 *) &key->key[24]; -#if 0 - /* MISC_MODE register & 0x04 - for mac srev >= griffin */ - key_v[0] = rxmic[0]; - key_v[1] = (txmic[0] >> 16) & 0xffff; - key_v[2] = rxmic[1]; - key_v[3] = txmic[0] & 0xffff; - key_v[4] = txmic[1]; -#else - key_v[0] = rxmic[0]; - key_v[1] = 0; - key_v[2] = rxmic[1]; - key_v[3] = 0; - key_v[4] = 0; -#endif + + if (ah->ah_combined_mic) { + key_v[0] = rxmic[0]; + key_v[1] = (txmic[0] >> 16) & 0xffff; + key_v[2] = rxmic[1]; + key_v[3] = txmic[0] & 0xffff; + key_v[4] = txmic[1]; + } else { + key_v[0] = rxmic[0]; + key_v[1] = 0; + key_v[2] = rxmic[1]; + key_v[3] = 0; + key_v[4] = 0; + } for (i = 0; i < ARRAY_SIZE(key_v); i++) ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), AR5K_KEYTABLE_OFF(micentry, i)); diff --git a/drivers/net/wireless/ath5k/reg.h b/drivers/net/wireless/ath5k/reg.h index 69755fc2f9be..91aaeaf88199 100644 --- a/drivers/net/wireless/ath5k/reg.h +++ b/drivers/net/wireless/ath5k/reg.h @@ -1729,6 +1729,7 @@ #define AR5K_MISC_MODE 0x8120 /* Register Address */ #define AR5K_MISC_MODE_FBSSID_MATCH 0x00000001 /* Force BSSID match */ #define AR5K_MISC_MODE_ACKSIFS_MEM 0x00000002 /* ACK SIFS memory (?) */ +#define AR5K_MISC_MODE_COMBINED_MIC 0x00000004 /* use rx/tx MIC key */ /* more bits */ /* -- cgit v1.2.1 From 388cdf31db6dfc3d175786a76989266380e12c26 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Tue, 9 Dec 2008 23:05:38 -0500 Subject: ath5k: fix endianness of bitwise ops when installing mic Fix these bugs found by sparse: ath5k/pcu.c:1102:21: warning: restricted __le32 degrades to integer ath5k/pcu.c:1102:13: warning: incorrect type in assignment (different base types) ath5k/pcu.c:1102:13: expected restricted __le32 ath5k/pcu.c:1102:13: got unsigned int ath5k/pcu.c:1104:20: warning: restricted __le32 degrades to integer ath5k/pcu.c:1104:13: warning: incorrect type in assignment (different base types) ath5k/pcu.c:1104:13: expected restricted __le32 ath5k/pcu.c:1104:13: got unsigned int Changes-licensed-under: ISC Reported-by: Johannes Berg Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/pcu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index dabe42219e2a..0cac05c6a9ce 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -1099,9 +1099,9 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, if (ah->ah_combined_mic) { key_v[0] = rxmic[0]; - key_v[1] = (txmic[0] >> 16) & 0xffff; + key_v[1] = cpu_to_le32(le32_to_cpu(txmic[0]) >> 16); key_v[2] = rxmic[1]; - key_v[3] = txmic[0] & 0xffff; + key_v[3] = cpu_to_le32(le32_to_cpu(txmic[0]) & 0xffff); key_v[4] = txmic[1]; } else { key_v[0] = rxmic[0]; -- cgit v1.2.1 From 0fe45b1debba7302155b62f3829119a1185a4f5a Mon Sep 17 00:00:00 2001 From: Benoit PAPILLAULT Date: Fri, 12 Dec 2008 15:29:58 +0100 Subject: ath5k: fix 802.11 header padding on RX, unpadding on TX Padding the 802.11 header to a multiple of 4 bytes needs to be done only for frames with a body. This fixes a bug where 2 bytes were missing in monitor mode for ACK frames. Inspired by a patch from Jouni Malinen on ath9k. Ref: http://bugzilla.kernel.org/show_bug.cgi?id=12101 : Signed-off-by: Benoit Papillault Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 0e4317010ed0..9b5f31aab574 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1668,7 +1668,7 @@ ath5k_tasklet_rx(unsigned long data) struct ath5k_desc *ds; int ret; int hdrlen; - int pad; + int padsize; spin_lock(&sc->rxbuflock); if (list_empty(&sc->rxbuf)) { @@ -1753,16 +1753,19 @@ accept: skb_put(skb, rs.rs_datalen); - /* - * the hardware adds a padding to 4 byte boundaries between - * the header and the payload data if the header length is - * not multiples of 4 - remove it - */ + /* The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. The general calculation for + * padsize would take into account odd header lengths: + * padsize = (4 - hdrlen % 4) % 4; However, since only + * even-length headers are used, padding can only be 0 or 2 + * bytes and we can optimize this a bit. In addition, we must + * not try to remove padding from short control frames that do + * not have payload. */ hdrlen = ieee80211_get_hdrlen_from_skb(skb); - if (hdrlen & 3) { - pad = hdrlen % 4; - memmove(skb->data + pad, skb->data, hdrlen); - skb_pull(skb, pad); + padsize = hdrlen & 3; + if (padsize && hdrlen >= 24) { + memmove(skb->data + padsize, skb->data, hdrlen); + skb_pull(skb, padsize); } /* @@ -2623,7 +2626,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) struct ath5k_buf *bf; unsigned long flags; int hdrlen; - int pad; + int padsize; ath5k_debug_dump_skb(sc, skb, "TX ", 1); @@ -2635,15 +2638,16 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * if this is not the case we add the padding after the header */ hdrlen = ieee80211_get_hdrlen_from_skb(skb); - if (hdrlen & 3) { - pad = hdrlen % 4; - if (skb_headroom(skb) < pad) { + padsize = hdrlen & 3; + if (padsize && hdrlen >= 24) { + + if (skb_headroom(skb) < padsize) { ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough" - " headroom to pad %d\n", hdrlen, pad); + " headroom to pad %d\n", hdrlen, padsize); return -1; } - skb_push(skb, pad); - memmove(skb->data, skb->data+pad, hdrlen); + skb_push(skb, padsize); + memmove(skb->data, skb->data+padsize, hdrlen); } spin_lock_irqsave(&sc->txbuflock, flags); -- cgit v1.2.1 From fd6effcaf8a894c0a0f602b943dbc54a170d4418 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 18 Dec 2008 23:23:05 -0500 Subject: ath5k: correct packet length in tx descriptors Packet length calculation (which includes frame check sequence) should take into account whether we add a pad field or not. Extract the calculation into a helper and use it in both places. Changes to desc.c Changes-licensed-under: ISC Changes to ath5k.h, base.c Changes-licensed-under: 3-Clause-BSD Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/ath5k.h | 5 +++++ drivers/net/wireless/ath5k/base.c | 8 ++++---- drivers/net/wireless/ath5k/desc.c | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath5k') diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h index 13df1191b070..183ffc8e62ca 100644 --- a/drivers/net/wireless/ath5k/ath5k.h +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -1350,4 +1350,9 @@ static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits) return retval; } +static inline int ath5k_pad_size(int hdrlen) +{ + return (hdrlen < 24) ? 0 : hdrlen & 3; +} + #endif diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 9b5f31aab574..4af2607deec0 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1762,8 +1762,8 @@ accept: * not try to remove padding from short control frames that do * not have payload. */ hdrlen = ieee80211_get_hdrlen_from_skb(skb); - padsize = hdrlen & 3; - if (padsize && hdrlen >= 24) { + padsize = ath5k_pad_size(hdrlen); + if (padsize) { memmove(skb->data + padsize, skb->data, hdrlen); skb_pull(skb, padsize); } @@ -2638,8 +2638,8 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * if this is not the case we add the padding after the header */ hdrlen = ieee80211_get_hdrlen_from_skb(skb); - padsize = hdrlen & 3; - if (padsize && hdrlen >= 24) { + padsize = ath5k_pad_size(hdrlen); + if (padsize) { if (skb_headroom(skb) < padsize) { ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough" diff --git a/drivers/net/wireless/ath5k/desc.c b/drivers/net/wireless/ath5k/desc.c index 5e362a7a3620..b40a9287a39a 100644 --- a/drivers/net/wireless/ath5k/desc.c +++ b/drivers/net/wireless/ath5k/desc.c @@ -71,7 +71,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, /* Verify and set frame length */ /* remove padding we might have added before */ - frame_len = pkt_len - (hdr_len & 3) + FCS_LEN; + frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN; if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN) return -EINVAL; @@ -202,7 +202,7 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, /* Verify and set frame length */ /* remove padding we might have added before */ - frame_len = pkt_len - (hdr_len & 3) + FCS_LEN; + frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN; if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN) return -EINVAL; -- cgit v1.2.1