Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next

John W. Linville says:

====================
This pull request is intended for the 3.11 stream...

One big highlight is the cw1200 driver the ST-E CW1100 & CW1200
WLAN chipsets.  This one has been lingering for a while, lacking
some review comments.  Once started getting pulled into linux-next,
it got a bit more attention and a number of improvements were made
over the initial cut.  No doubt there will be more changes ahead,
but I think it is looking alright at this point.

Along with that, there is the usual flurry of updates to the mac80211
core and the iwlwifi, mwifiex, ath9k, rt2x00, wil6210, and other
drivers.  A few of the highlights are some rt2x00 refactoring/cleanup
by Gabor Juhos, some rt2800 hardware support enhancements by Stanislaw
Gruszka, some iwlwifi power management updates from Alexander Bondar,
some enhanced bcma SPROM support from Rafał Miłecki, and a variety
of other things here and there.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2013-06-12 14:23:41 -07:00
commit 4a2e667ac1
206 changed files with 19930 additions and 3398 deletions

View File

@ -132,9 +132,7 @@
!Finclude/net/cfg80211.h cfg80211_send_rx_assoc !Finclude/net/cfg80211.h cfg80211_send_rx_assoc
!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout !Finclude/net/cfg80211.h cfg80211_send_assoc_timeout
!Finclude/net/cfg80211.h cfg80211_send_deauth !Finclude/net/cfg80211.h cfg80211_send_deauth
!Finclude/net/cfg80211.h __cfg80211_send_deauth
!Finclude/net/cfg80211.h cfg80211_send_disassoc !Finclude/net/cfg80211.h cfg80211_send_disassoc
!Finclude/net/cfg80211.h __cfg80211_send_disassoc
!Finclude/net/cfg80211.h cfg80211_ibss_joined !Finclude/net/cfg80211.h cfg80211_ibss_joined
!Finclude/net/cfg80211.h cfg80211_connect_result !Finclude/net/cfg80211.h cfg80211_connect_result
!Finclude/net/cfg80211.h cfg80211_roamed !Finclude/net/cfg80211.h cfg80211_roamed

View File

@ -2299,6 +2299,11 @@ M: Jaya Kumar <jayakumar.alsa@gmail.com>
S: Maintained S: Maintained
F: sound/pci/cs5535audio/ F: sound/pci/cs5535audio/
CW1200 WLAN driver
M: Solomon Peachy <pizza@shaftnet.org>
S: Maintained
F: drivers/net/wireless/cw1200/
CX18 VIDEO4LINUX DRIVER CX18 VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net> M: Andy Walls <awalls@md.metrocast.net>
L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers) L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers)

View File

@ -72,12 +72,12 @@ fail:
* R/W ops. * R/W ops.
**************************************************/ **************************************************/
static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom) static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom,
size_t words)
{ {
int i; int i;
for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++) for (i = 0; i < words; i++)
sprom[i] = bcma_read16(bus->drv_cc.core, sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2));
offset + (i * 2));
} }
/************************************************** /**************************************************
@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data)
return t[crc ^ data]; return t[crc ^ data];
} }
static u8 bcma_sprom_crc(const u16 *sprom) static u8 bcma_sprom_crc(const u16 *sprom, size_t words)
{ {
int word; int word;
u8 crc = 0xFF; u8 crc = 0xFF;
for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) { for (word = 0; word < words - 1; word++) {
crc = bcma_crc8(crc, sprom[word] & 0x00FF); crc = bcma_crc8(crc, sprom[word] & 0x00FF);
crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8); crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
} }
crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF); crc = bcma_crc8(crc, sprom[words - 1] & 0x00FF);
crc ^= 0xFF; crc ^= 0xFF;
return crc; return crc;
} }
static int bcma_sprom_check_crc(const u16 *sprom) static int bcma_sprom_check_crc(const u16 *sprom, size_t words)
{ {
u8 crc; u8 crc;
u8 expected_crc; u8 expected_crc;
u16 tmp; u16 tmp;
crc = bcma_sprom_crc(sprom); crc = bcma_sprom_crc(sprom, words);
tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC; tmp = sprom[words - 1] & SSB_SPROM_REVISION_CRC;
expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
if (crc != expected_crc) if (crc != expected_crc)
return -EPROTO; return -EPROTO;
@ -154,21 +154,25 @@ static int bcma_sprom_check_crc(const u16 *sprom)
return 0; return 0;
} }
static int bcma_sprom_valid(const u16 *sprom) static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
size_t words)
{ {
u16 revision; u16 revision;
int err; int err;
err = bcma_sprom_check_crc(sprom); err = bcma_sprom_check_crc(sprom, words);
if (err) if (err)
return err; return err;
revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV; revision = sprom[words - 1] & SSB_SPROM_REVISION_REV;
if (revision != 8 && revision != 9) { if (revision != 8 && revision != 9 && revision != 10) {
pr_err("Unsupported SPROM revision: %d\n", revision); pr_err("Unsupported SPROM revision: %d\n", revision);
return -ENOENT; return -ENOENT;
} }
bus->sprom.revision = revision;
bcma_debug(bus, "Found SPROM revision %d\n", revision);
return 0; return 0;
} }
@ -208,9 +212,6 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) != BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
ARRAY_SIZE(bus->sprom.core_pwr_info)); ARRAY_SIZE(bus->sprom.core_pwr_info));
bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
SSB_SPROM_REVISION_REV;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i]; v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v); *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
@ -502,7 +503,6 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
case BCMA_CHIP_ID_BCM4331: case BCMA_CHIP_ID_BCM4331:
present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT; present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
break; break;
case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43224:
case BCMA_CHIP_ID_BCM43225: case BCMA_CHIP_ID_BCM43225:
/* for these chips OTP is always available */ /* for these chips OTP is always available */
@ -550,7 +550,9 @@ int bcma_sprom_get(struct bcma_bus *bus)
{ {
u16 offset = BCMA_CC_SPROM; u16 offset = BCMA_CC_SPROM;
u16 *sprom; u16 *sprom;
int err = 0; size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
SSB_SPROMSIZE_WORDS_R10, };
int i, err = 0;
if (!bus->drv_cc.core) if (!bus->drv_cc.core)
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -579,32 +581,37 @@ int bcma_sprom_get(struct bcma_bus *bus)
} }
} }
sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
GFP_KERNEL);
if (!sprom)
return -ENOMEM;
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
bcma_debug(bus, "SPROM offset 0x%x\n", offset); bcma_debug(bus, "SPROM offset 0x%x\n", offset);
bcma_sprom_read(bus, offset, sprom); for (i = 0; i < ARRAY_SIZE(sprom_sizes); i++) {
size_t words = sprom_sizes[i];
sprom = kcalloc(words, sizeof(u16), GFP_KERNEL);
if (!sprom)
return -ENOMEM;
bcma_sprom_read(bus, offset, sprom, words);
err = bcma_sprom_valid(bus, sprom, words);
if (!err)
break;
kfree(sprom);
}
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true); bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
err = bcma_sprom_valid(sprom);
if (err) { if (err) {
bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n"); bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
goto out; } else {
bcma_sprom_extract_r8(bus, sprom);
kfree(sprom);
} }
bcma_sprom_extract_r8(bus, sprom);
out:
kfree(sprom);
return err; return err;
} }

View File

@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/mwifiex/Kconfig" source "drivers/net/wireless/mwifiex/Kconfig"
source "drivers/net/wireless/cw1200/Kconfig"
endif # WLAN endif # WLAN

View File

@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/
obj-$(CONFIG_BRCMFMAC) += brcm80211/ obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/ obj-$(CONFIG_BRCMSMAC) += brcm80211/
obj-$(CONFIG_CW1200) += cw1200/

View File

@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED
developed. At this point enabling this option won't do anything developed. At this point enabling this option won't do anything
except increase code size. except increase code size.
config ATH9K_MAC_DEBUG
bool "Atheros MAC statistics"
depends on ATH9K_DEBUGFS
default y
---help---
This option enables collection of statistics for Rx/Tx status
data and some other MAC related statistics
config ATH9K_RATE_CONTROL config ATH9K_RATE_CONTROL
bool "Atheros ath9k rate control" bool "Atheros ath9k rate control"
depends on ATH9K depends on ATH9K

View File

@ -118,10 +118,10 @@ static void ath9k_ani_restart(struct ath_hw *ah)
{ {
struct ar5416AniState *aniState; struct ar5416AniState *aniState;
if (!DO_ANI(ah)) if (!ah->curchan)
return; return;
aniState = &ah->curchan->ani; aniState = &ah->ani;
aniState->listenTime = 0; aniState->listenTime = 0;
ENABLE_REGWRITE_BUFFER(ah); ENABLE_REGWRITE_BUFFER(ah);
@ -143,7 +143,7 @@ static void ath9k_ani_restart(struct ath_hw *ah)
static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
bool scan) bool scan)
{ {
struct ar5416AniState *aniState = &ah->curchan->ani; struct ar5416AniState *aniState = &ah->ani;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_ofdm_level_entry *entry_ofdm;
const struct ani_cck_level_entry *entry_cck; const struct ani_cck_level_entry *entry_cck;
@ -195,10 +195,10 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
{ {
struct ar5416AniState *aniState; struct ar5416AniState *aniState;
if (!DO_ANI(ah)) if (!ah->curchan)
return; return;
aniState = &ah->curchan->ani; aniState = &ah->ani;
if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL) if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false); ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);
@ -210,7 +210,7 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel, static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel,
bool scan) bool scan)
{ {
struct ar5416AniState *aniState = &ah->curchan->ani; struct ar5416AniState *aniState = &ah->ani;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_ofdm_level_entry *entry_ofdm;
const struct ani_cck_level_entry *entry_cck; const struct ani_cck_level_entry *entry_cck;
@ -251,10 +251,10 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
{ {
struct ar5416AniState *aniState; struct ar5416AniState *aniState;
if (!DO_ANI(ah)) if (!ah->curchan)
return; return;
aniState = &ah->curchan->ani; aniState = &ah->ani;
if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL) if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1, ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,
@ -269,7 +269,7 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
{ {
struct ar5416AniState *aniState; struct ar5416AniState *aniState;
aniState = &ah->curchan->ani; aniState = &ah->ani;
/* lower OFDM noise immunity */ /* lower OFDM noise immunity */
if (aniState->ofdmNoiseImmunityLevel > 0 && if (aniState->ofdmNoiseImmunityLevel > 0 &&
@ -292,12 +292,12 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
*/ */
void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
{ {
struct ar5416AniState *aniState = &ah->curchan->ani; struct ar5416AniState *aniState = &ah->ani;
struct ath9k_channel *chan = ah->curchan; struct ath9k_channel *chan = ah->curchan;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
int ofdm_nil, cck_nil; int ofdm_nil, cck_nil;
if (!DO_ANI(ah)) if (!ah->curchan)
return; return;
BUG_ON(aniState == NULL); BUG_ON(aniState == NULL);
@ -380,7 +380,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ar5416AniState *aniState = &ah->curchan->ani; struct ar5416AniState *aniState = &ah->ani;
u32 phyCnt1, phyCnt2; u32 phyCnt1, phyCnt2;
int32_t listenTime; int32_t listenTime;
@ -415,10 +415,10 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
u32 ofdmPhyErrRate, cckPhyErrRate; u32 ofdmPhyErrRate, cckPhyErrRate;
if (!DO_ANI(ah)) if (!ah->curchan)
return; return;
aniState = &ah->curchan->ani; aniState = &ah->ani;
if (!ath9k_hw_ani_read_counters(ah)) if (!ath9k_hw_ani_read_counters(ah))
return; return;
@ -490,32 +490,22 @@ EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
void ath9k_hw_ani_init(struct ath_hw *ah) void ath9k_hw_ani_init(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
int i; struct ar5416AniState *ani = &ah->ani;
ath_dbg(common, ANI, "Initialize ANI\n"); ath_dbg(common, ANI, "Initialize ANI\n");
ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH; ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW; ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
for (i = 0; i < ARRAY_SIZE(ah->channels); i++) { ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
struct ath9k_channel *chan = &ah->channels[i]; ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
struct ar5416AniState *ani = &chan->ani; ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
ani->ofdmsTurn = true;
ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; ani->ofdmWeakSigDetect = true;
ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
ani->ofdmsTurn = true;
ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
}
/* /*
* since we expect some ongoing maintenance on the tables, let's sanity * since we expect some ongoing maintenance on the tables, let's sanity
@ -524,9 +514,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
ah->aniperiod = ATH9K_ANI_PERIOD; ah->aniperiod = ATH9K_ANI_PERIOD;
ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL; ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL;
if (ah->config.enable_ani)
ah->proc_phyerr |= HAL_PROCESS_ANI;
ath9k_ani_restart(ah); ath9k_ani_restart(ah);
ath9k_enable_mib_counters(ah); ath9k_enable_mib_counters(ah);
} }

View File

@ -17,10 +17,6 @@
#ifndef ANI_H #ifndef ANI_H
#define ANI_H #define ANI_H
#define HAL_PROCESS_ANI 0x00000001
#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan)
#define BEACON_RSSI(ahp) (ahp->stats.avgbrssi) #define BEACON_RSSI(ahp) (ahp->stats.avgbrssi)
/* units are errors per second */ /* units are errors per second */
@ -38,11 +34,7 @@
#define ATH9K_ANI_CCK_TRIG_LOW 300 #define ATH9K_ANI_CCK_TRIG_LOW 300
#define ATH9K_ANI_NOISE_IMMUNE_LVL 4 #define ATH9K_ANI_NOISE_IMMUNE_LVL 4
#define ATH9K_ANI_USE_OFDM_WEAK_SIG true
#define ATH9K_ANI_CCK_WEAK_SIG_THR false
#define ATH9K_ANI_SPUR_IMMUNE_LVL 3 #define ATH9K_ANI_SPUR_IMMUNE_LVL 3
#define ATH9K_ANI_FIRSTEP_LVL 2 #define ATH9K_ANI_FIRSTEP_LVL 2
#define ATH9K_ANI_RSSI_THR_HIGH 40 #define ATH9K_ANI_RSSI_THR_HIGH 40
@ -111,7 +103,7 @@ struct ar5416AniState {
u8 mrcCCK; u8 mrcCCK;
u8 spurImmunityLevel; u8 spurImmunityLevel;
u8 firstepLevel; u8 firstepLevel;
u8 ofdmWeakSigDetect; bool ofdmWeakSigDetect;
u32 listenTime; u32 listenTime;
u32 ofdmPhyErrCount; u32 ofdmPhyErrCount;
u32 cckPhyErrCount; u32 cckPhyErrCount;
@ -119,8 +111,6 @@ struct ar5416AniState {
}; };
struct ar5416Stats { struct ar5416Stats {
u32 ast_ani_niup;
u32 ast_ani_nidown;
u32 ast_ani_spurup; u32 ast_ani_spurup;
u32 ast_ani_spurdown; u32 ast_ani_spurdown;
u32 ast_ani_ofdmon; u32 ast_ani_ofdmon;

View File

@ -931,7 +931,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan; struct ath9k_channel *chan = ah->curchan;
struct ar5416AniState *aniState = &chan->ani; struct ar5416AniState *aniState = &ah->ani;
s32 value, value2; s32 value, value2;
switch (cmd & ah->ani_function) { switch (cmd & ah->ani_function) {
@ -1207,7 +1207,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan; struct ath9k_channel *chan = ah->curchan;
struct ar5416AniState *aniState = &chan->ani; struct ar5416AniState *aniState = &ah->ani;
struct ath9k_ani_default *iniDef; struct ath9k_ani_default *iniDef;
u32 val; u32 val;
@ -1251,7 +1251,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
/* these levels just got reset to defaults by the INI */ /* these levels just got reset to defaults by the INI */
aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->ofdmWeakSigDetect = true;
aniState->mrcCCK = false; /* not available on pre AR9003 */ aniState->mrcCCK = false; /* not available on pre AR9003 */
} }

View File

@ -38,10 +38,6 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
else else
INIT_INI_ARRAY(&ah->iniPcieSerdes, INIT_INI_ARRAY(&ah->iniPcieSerdes,
ar9280PciePhy_clkreq_always_on_L1_9280); ar9280PciePhy_clkreq_always_on_L1_9280);
#ifdef CONFIG_PM_SLEEP
INIT_INI_ARRAY(&ah->iniPcieSerdesWow,
ar9280PciePhy_awow);
#endif
if (AR_SREV_9287_11_OR_LATER(ah)) { if (AR_SREV_9287_11_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1); INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1);

View File

@ -925,20 +925,6 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
{0x00004044, 0x00000000}, {0x00004044, 0x00000000},
}; };
static const u32 ar9280PciePhy_awow[][2] = {
/* Addr allmodes */
{0x00004040, 0x9248fd00},
{0x00004040, 0x24924924},
{0x00004040, 0xa8000019},
{0x00004040, 0x13160820},
{0x00004040, 0xe5980560},
{0x00004040, 0xc01dcffd},
{0x00004040, 0x1aaabe41},
{0x00004040, 0xbe105554},
{0x00004040, 0x00043007},
{0x00004044, 0x00000000},
};
static const u32 ar9285Modes_9285_1_2[][5] = { static const u32 ar9285Modes_9285_1_2[][5] = {
/* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
{0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},

View File

@ -469,6 +469,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
rxs->rs_status = 0; rxs->rs_status = 0;
rxs->rs_flags = 0; rxs->rs_flags = 0;
rxs->flag = 0;
rxs->rs_datalen = rxsp->status2 & AR_DataLen; rxs->rs_datalen = rxsp->status2 & AR_DataLen;
rxs->rs_tstamp = rxsp->status3; rxs->rs_tstamp = rxsp->status3;
@ -493,8 +494,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0; rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0;
rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0; rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0;
rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7); rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7);
rxs->rs_flags = (rxsp->status4 & AR_GI) ? ATH9K_RX_GI : 0; rxs->flag |= (rxsp->status4 & AR_GI) ? RX_FLAG_SHORT_GI : 0;
rxs->rs_flags |= (rxsp->status4 & AR_2040) ? ATH9K_RX_2040 : 0; rxs->flag |= (rxsp->status4 & AR_2040) ? RX_FLAG_40MHZ : 0;
rxs->evm0 = rxsp->status6; rxs->evm0 = rxsp->status6;
rxs->evm1 = rxsp->status7; rxs->evm1 = rxsp->status7;

View File

@ -454,6 +454,8 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
if (accum_cnt <= thresh_accum_cnt) if (accum_cnt <= thresh_accum_cnt)
continue; continue;
max_index++;
/* sum(tx amplitude) */ /* sum(tx amplitude) */
accum_tx = ((data_L[i] >> 16) & 0xffff) | accum_tx = ((data_L[i] >> 16) & 0xffff) |
((data_U[i] & 0x7ff) << 16); ((data_U[i] & 0x7ff) << 16);
@ -468,20 +470,21 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
accum_tx <<= scale_factor; accum_tx <<= scale_factor;
accum_rx <<= scale_factor; accum_rx <<= scale_factor;
x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >> x_est[max_index] =
scale_factor; (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
scale_factor;
Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >> Y[max_index] =
((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
scale_factor) + scale_factor) +
(1 << scale_factor) * max_index + 16; (1 << scale_factor) * i + 16;
if (accum_ang >= (1 << 26)) if (accum_ang >= (1 << 26))
accum_ang -= 1 << 27; accum_ang -= 1 << 27;
theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) / theta[max_index] =
accum_cnt; ((accum_ang * (1 << scale_factor)) + accum_cnt) /
accum_cnt;
max_index++;
} }
/* /*

View File

@ -905,7 +905,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan; struct ath9k_channel *chan = ah->curchan;
struct ar5416AniState *aniState = &chan->ani; struct ar5416AniState *aniState = &ah->ani;
s32 value, value2; s32 value, value2;
switch (cmd & ah->ani_function) { switch (cmd & ah->ani_function) {
@ -1173,7 +1173,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
struct ath9k_ani_default *iniDef; struct ath9k_ani_default *iniDef;
u32 val; u32 val;
aniState = &ah->curchan->ani; aniState = &ah->ani;
iniDef = &aniState->iniDef; iniDef = &aniState->iniDef;
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
@ -1214,7 +1214,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
/* these levels just got reset to defaults by the INI */ /* these levels just got reset to defaults by the INI */
aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->ofdmWeakSigDetect = true;
aniState->mrcCCK = true; aniState->mrcCCK = true;
} }

View File

@ -642,6 +642,7 @@ enum sc_op_flags {
SC_OP_ANI_RUN, SC_OP_ANI_RUN,
SC_OP_PRIM_STA_VIF, SC_OP_PRIM_STA_VIF,
SC_OP_HW_RESET, SC_OP_HW_RESET,
SC_OP_SCANNING,
}; };
/* Powersave flags */ /* Powersave flags */
@ -755,7 +756,6 @@ struct ath_softc {
struct rchan *rfs_chan_spec_scan; struct rchan *rfs_chan_spec_scan;
enum spectral_mode spectral_mode; enum spectral_mode spectral_mode;
struct ath_spec_scan spec_config; struct ath_spec_scan spec_config;
int scanning;
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
atomic_t wow_got_bmiss_intr; atomic_t wow_got_bmiss_intr;

View File

@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc)
ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
/* Always burst out beacon and CAB traffic. */ /* Always burst out beacon and CAB traffic. */
qi.tqi_aifs = 1; qi.tqi_aifs = 1;
qi.tqi_cwmin = 0; qi.tqi_cwmin = 0;
@ -273,7 +274,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
u64 tsf; u64 tsf;
int slot; int slot;
if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) { if (sc->sc_ah->opmode != NL80211_IFTYPE_AP &&
sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) {
ath_dbg(common, BEACON, "slot 0, tsf: %llu\n", ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
ath9k_hw_gettsf64(sc->sc_ah)); ath9k_hw_gettsf64(sc->sc_ah));
return 0; return 0;
@ -765,10 +767,10 @@ void ath9k_set_beacon(struct ath_softc *sc)
switch (sc->sc_ah->opmode) { switch (sc->sc_ah->opmode) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_ap(sc, cur_conf); ath9k_beacon_config_ap(sc, cur_conf);
break; break;
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_adhoc(sc, cur_conf); ath9k_beacon_config_adhoc(sc, cur_conf);
break; break;
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:

View File

@ -173,25 +173,69 @@ static const struct file_operations fops_rx_chainmask = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
static ssize_t read_file_disable_ani(struct file *file, char __user *user_buf, static ssize_t read_file_ani(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct ath_softc *sc = file->private_data; struct ath_softc *sc = file->private_data;
struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_common *common = ath9k_hw_common(sc->sc_ah);
char buf[32]; struct ath_hw *ah = sc->sc_ah;
unsigned int len; unsigned int len = 0, size = 1024;
ssize_t retval = 0;
char *buf;
len = sprintf(buf, "%d\n", common->disable_ani); buf = kzalloc(size, GFP_KERNEL);
return simple_read_from_buffer(user_buf, count, ppos, buf, len); if (buf == NULL)
return -ENOMEM;
if (common->disable_ani) {
len += snprintf(buf + len, size - len, "%s: %s\n",
"ANI", "DISABLED");
goto exit;
}
len += snprintf(buf + len, size - len, "%15s: %s\n",
"ANI", "ENABLED");
len += snprintf(buf + len, size - len, "%15s: %u\n",
"ANI RESET", ah->stats.ast_ani_reset);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"SPUR UP", ah->stats.ast_ani_spurup);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"SPUR DOWN", ah->stats.ast_ani_spurup);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"OFDM WS-DET ON", ah->stats.ast_ani_ofdmon);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"MRC-CCK ON", ah->stats.ast_ani_ccklow);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"MRC-CCK OFF", ah->stats.ast_ani_cckhigh);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"FIR-STEP UP", ah->stats.ast_ani_stepup);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"FIR-STEP DOWN", ah->stats.ast_ani_stepdown);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"OFDM ERRORS", ah->stats.ast_ani_ofdmerrs);
len += snprintf(buf + len, size - len, "%15s: %u\n",
"CCK ERRORS", ah->stats.ast_ani_cckerrs);
exit:
if (len > size)
len = size;
retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
return retval;
} }
static ssize_t write_file_disable_ani(struct file *file, static ssize_t write_file_ani(struct file *file,
const char __user *user_buf, const char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct ath_softc *sc = file->private_data; struct ath_softc *sc = file->private_data;
struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_common *common = ath9k_hw_common(sc->sc_ah);
unsigned long disable_ani; unsigned long ani;
char buf[32]; char buf[32];
ssize_t len; ssize_t len;
@ -200,12 +244,15 @@ static ssize_t write_file_disable_ani(struct file *file,
return -EFAULT; return -EFAULT;
buf[len] = '\0'; buf[len] = '\0';
if (kstrtoul(buf, 0, &disable_ani)) if (kstrtoul(buf, 0, &ani))
return -EINVAL; return -EINVAL;
common->disable_ani = !!disable_ani; if (ani < 0 || ani > 1)
return -EINVAL;
if (disable_ani) { common->disable_ani = !ani;
if (common->disable_ani) {
clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
ath_stop_ani(sc); ath_stop_ani(sc);
} else { } else {
@ -215,9 +262,9 @@ static ssize_t write_file_disable_ani(struct file *file,
return count; return count;
} }
static const struct file_operations fops_disable_ani = { static const struct file_operations fops_ani = {
.read = read_file_disable_ani, .read = read_file_ani,
.write = write_file_disable_ani, .write = write_file_ani,
.open = simple_open, .open = simple_open,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.llseek = default_llseek, .llseek = default_llseek,
@ -738,8 +785,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_txq *txq,
unsigned int flags) unsigned int flags)
{ {
#define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\
[sc->debug.tsidx].c)
int qnum = txq->axq_qnum; int qnum = txq->axq_qnum;
TX_STAT_INC(qnum, tx_pkts_all); TX_STAT_INC(qnum, tx_pkts_all);
@ -771,37 +816,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
TX_STAT_INC(qnum, data_underrun); TX_STAT_INC(qnum, data_underrun);
if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN) if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
TX_STAT_INC(qnum, delim_underrun); TX_STAT_INC(qnum, delim_underrun);
#ifdef CONFIG_ATH9K_MAC_DEBUG
spin_lock(&sc->debug.samp_lock);
TX_SAMP_DBG(jiffies) = jiffies;
TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0;
TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1;
TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2;
TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0;
TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1;
TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2;
TX_SAMP_DBG(rateindex) = ts->ts_rateindex;
TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK);
TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry;
TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry;
TX_SAMP_DBG(rssi) = ts->ts_rssi;
TX_SAMP_DBG(tid) = ts->tid;
TX_SAMP_DBG(qid) = ts->qid;
if (ts->ts_flags & ATH9K_TX_BA) {
TX_SAMP_DBG(ba_low) = ts->ba_low;
TX_SAMP_DBG(ba_high) = ts->ba_high;
} else {
TX_SAMP_DBG(ba_low) = 0;
TX_SAMP_DBG(ba_high) = 0;
}
sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES;
spin_unlock(&sc->debug.samp_lock);
#endif
#undef TX_SAMP_DBG
} }
static const struct file_operations fops_xmit = { static const struct file_operations fops_xmit = {
@ -915,8 +929,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
{ {
#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ #define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\
[sc->debug.rsidx].c)
RX_STAT_INC(rx_pkts_all); RX_STAT_INC(rx_pkts_all);
sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen;
@ -940,27 +952,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
RX_PHY_ERR_INC(rs->rs_phyerr); RX_PHY_ERR_INC(rs->rs_phyerr);
} }
#ifdef CONFIG_ATH9K_MAC_DEBUG
spin_lock(&sc->debug.samp_lock);
RX_SAMP_DBG(jiffies) = jiffies;
RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1;
RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2;
RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0;
RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1;
RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2;
RX_SAMP_DBG(antenna) = rs->rs_antenna;
RX_SAMP_DBG(rssi) = rs->rs_rssi;
RX_SAMP_DBG(rate) = rs->rs_rate;
RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon;
sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES;
spin_unlock(&sc->debug.samp_lock);
#endif
#undef RX_PHY_ERR_INC #undef RX_PHY_ERR_INC
#undef RX_SAMP_DBG
} }
static const struct file_operations fops_recv = { static const struct file_operations fops_recv = {
@ -1485,283 +1477,6 @@ static const struct file_operations fops_modal_eeprom = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
#ifdef CONFIG_ATH9K_MAC_DEBUG
void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
{
#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
unsigned long flags;
int i;
ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->debug.samp_lock);
spin_lock_irqsave(&common->cc_lock, flags);
ath_hw_cycle_counters_update(common);
ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles;
ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy;
ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame;
ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame;
spin_unlock_irqrestore(&common->cc_lock, flags);
ATH_SAMP_DBG(noise) = ah->noise;
REG_WRITE_D(ah, AR_MACMISC,
((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
(AR_MACMISC_MISC_OBS_BUS_1 <<
AR_MACMISC_MISC_OBS_BUS_MSB_S)));
for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah,
AR_DMADBG_0 + (i * sizeof(u32)));
ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1);
ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR);
memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist,
sizeof(ATH_SAMP_DBG(nfCalHist)));
sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES;
spin_unlock_bh(&sc->debug.samp_lock);
ath9k_ps_restore(sc);
#undef ATH_SAMP_DBG
}
static int open_file_bb_mac_samps(struct inode *inode, struct file *file)
{
#define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c
struct ath_softc *sc = inode->i_private;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &common->hw->conf;
struct ath_dbg_bb_mac_samp *bb_mac_samp;
struct ath9k_nfcal_hist *h;
int i, j, qcuOffset = 0, dcuOffset = 0;
u32 *qcuBase, *dcuBase, size = 30000, len = 0;
u32 sampidx = 0;
u8 *buf;
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
u8 nread;
if (test_bit(SC_OP_INVALID, &sc->sc_flags))
return -EAGAIN;
buf = vmalloc(size);
if (!buf)
return -ENOMEM;
bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
if (!bb_mac_samp) {
vfree(buf);
return -ENOMEM;
}
/* Account the current state too */
ath9k_debug_samp_bb_mac(sc);
spin_lock_bh(&sc->debug.samp_lock);
memcpy(bb_mac_samp, sc->debug.bb_mac_samp,
sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
len += snprintf(buf + len, size - len,
"Current Sample Index: %d\n", sc->debug.sampidx);
spin_unlock_bh(&sc->debug.samp_lock);
len += snprintf(buf + len, size - len,
"Raw DMA Debug Dump:\n");
len += snprintf(buf + len, size - len, "Sample |\t");
for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i);
len += snprintf(buf + len, size - len, "\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
len += snprintf(buf + len, size - len, "%d\t", sampidx);
for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
len += snprintf(buf + len, size - len, " %08x\t",
ATH_SAMP_DBG(dma_dbg_reg_vals[i]));
len += snprintf(buf + len, size - len, "\n");
}
len += snprintf(buf + len, size - len, "\n");
len += snprintf(buf + len, size - len,
"Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
for (i = 0; i < ATH9K_NUM_QUEUES; i++,
qcuOffset += 4, dcuOffset += 5) {
if (i == 8) {
qcuOffset = 0;
qcuBase++;
}
if (i == 6) {
dcuOffset = 0;
dcuBase++;
}
if (!sc->debug.stats.txstats[i].queued)
continue;
len += snprintf(buf + len, size - len,
"%4d %7d %2x %1x %2x %2x\n",
sampidx, i,
(*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
(*qcuBase & (0x8 << qcuOffset)) >>
(qcuOffset + 3),
ATH_SAMP_DBG(dma_dbg_reg_vals[2]) &
(0x7 << (i * 3)) >> (i * 3),
(*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
}
len += snprintf(buf + len, size - len, "\n");
}
len += snprintf(buf + len, size - len,
"samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp "
"ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 "
"txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx,
(ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18,
(ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22);
len += snprintf(buf + len, size - len, "%7x %8x ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26,
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3));
len += snprintf(buf + len, size - len, "%7x %7x ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25,
(ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27);
len += snprintf(buf + len, size - len, "%7d %12d ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2,
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10);
len += snprintf(buf + len, size - len, "%12d %12d ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11,
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12);
len += snprintf(buf + len, size - len, "%12d %12d ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13,
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17);
len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n",
ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr));
}
len += snprintf(buf + len, size - len,
"Sample ChNoise Chain privNF #Reading Readings\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
h = ATH_SAMP_DBG(nfCalHist);
if (!ATH_SAMP_DBG(noise))
continue;
for (i = 0; i < NUM_NF_READINGS; i++) {
if (!(chainmask & (1 << i)) ||
((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
continue;
nread = AR_PHY_CCA_FILTERWINDOW_LENGTH -
h[i].invalidNFcount;
len += snprintf(buf + len, size - len,
"%4d %5d %4d\t %d\t %d\t",
sampidx, ATH_SAMP_DBG(noise),
i, h[i].privNF, nread);
for (j = 0; j < nread; j++)
len += snprintf(buf + len, size - len,
" %d", h[i].nfCalBuffer[j]);
len += snprintf(buf + len, size - len, "\n");
}
}
len += snprintf(buf + len, size - len, "\nCycle counters:\n"
"Sample Total Rxbusy Rxframes Txframes\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
if (!ATH_SAMP_DBG(cc.cycles))
continue;
len += snprintf(buf + len, size - len,
"%4d %08x %08x %08x %08x\n",
sampidx, ATH_SAMP_DBG(cc.cycles),
ATH_SAMP_DBG(cc.rx_busy),
ATH_SAMP_DBG(cc.rx_frame),
ATH_SAMP_DBG(cc.tx_frame));
}
len += snprintf(buf + len, size - len, "Tx status Dump :\n");
len += snprintf(buf + len, size - len,
"Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb "
"isok rts_fail data_fail rate tid qid "
"ba_low ba_high tx_before(ms)\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
if (!ATH_SAMP_DBG(ts[i].jiffies))
continue;
len += snprintf(buf + len, size - len, "%-14d"
"%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d "
"%-9d %-4d %-3d %-3d %08x %08x %-11d\n",
sampidx,
ATH_SAMP_DBG(ts[i].rssi_ctl0),
ATH_SAMP_DBG(ts[i].rssi_ctl1),
ATH_SAMP_DBG(ts[i].rssi_ctl2),
ATH_SAMP_DBG(ts[i].rssi_ext0),
ATH_SAMP_DBG(ts[i].rssi_ext1),
ATH_SAMP_DBG(ts[i].rssi_ext2),
ATH_SAMP_DBG(ts[i].rssi),
ATH_SAMP_DBG(ts[i].isok),
ATH_SAMP_DBG(ts[i].rts_fail_cnt),
ATH_SAMP_DBG(ts[i].data_fail_cnt),
ATH_SAMP_DBG(ts[i].rateindex),
ATH_SAMP_DBG(ts[i].tid),
ATH_SAMP_DBG(ts[i].qid),
ATH_SAMP_DBG(ts[i].ba_low),
ATH_SAMP_DBG(ts[i].ba_high),
jiffies_to_msecs(jiffies -
ATH_SAMP_DBG(ts[i].jiffies)));
}
}
len += snprintf(buf + len, size - len, "Rx status Dump :\n");
len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 "
"ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
if (!ATH_SAMP_DBG(rs[i].jiffies))
continue;
len += snprintf(buf + len, size - len, "%-14d"
"%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n",
sampidx,
ATH_SAMP_DBG(rs[i].rssi_ctl0),
ATH_SAMP_DBG(rs[i].rssi_ctl1),
ATH_SAMP_DBG(rs[i].rssi_ctl2),
ATH_SAMP_DBG(rs[i].rssi_ext0),
ATH_SAMP_DBG(rs[i].rssi_ext1),
ATH_SAMP_DBG(rs[i].rssi_ext2),
ATH_SAMP_DBG(rs[i].rssi),
ATH_SAMP_DBG(rs[i].is_mybeacon) ?
"True" : "False",
ATH_SAMP_DBG(rs[i].antenna),
ATH_SAMP_DBG(rs[i].rate),
jiffies_to_msecs(jiffies -
ATH_SAMP_DBG(rs[i].jiffies)));
}
}
vfree(bb_mac_samp);
file->private_data = buf;
return 0;
#undef ATH_SAMP_DBG
}
static const struct file_operations fops_samps = {
.open = open_file_bb_mac_samps,
.read = ath9k_debugfs_read_buf,
.release = ath9k_debugfs_release_buf,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
#endif
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
@ -2059,8 +1774,8 @@ int ath9k_init_debug(struct ath_hw *ah)
sc->debug.debugfs_phy, sc, &fops_rx_chainmask); sc->debug.debugfs_phy, sc, &fops_rx_chainmask);
debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR, debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_tx_chainmask); sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR, debugfs_create_file("ani", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_disable_ani); sc->debug.debugfs_phy, sc, &fops_ani);
debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
&sc->sc_ah->config.enable_paprd); &sc->sc_ah->config.enable_paprd);
debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
@ -2095,11 +1810,6 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR, debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, sc->debug.debugfs_phy, sc,
&fops_spectral_fft_period); &fops_spectral_fft_period);
#ifdef CONFIG_ATH9K_MAC_DEBUG
debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_samps);
#endif
debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,

View File

@ -251,56 +251,10 @@ struct ath_stats {
u32 reset[__RESET_TYPE_MAX]; u32 reset[__RESET_TYPE_MAX];
}; };
#define ATH_DBG_MAX_SAMPLES 10
struct ath_dbg_bb_mac_samp {
u32 dma_dbg_reg_vals[ATH9K_NUM_DMA_DEBUG_REGS];
u32 pcu_obs, pcu_cr, noise;
struct {
u64 jiffies;
int8_t rssi_ctl0;
int8_t rssi_ctl1;
int8_t rssi_ctl2;
int8_t rssi_ext0;
int8_t rssi_ext1;
int8_t rssi_ext2;
int8_t rssi;
bool isok;
u8 rts_fail_cnt;
u8 data_fail_cnt;
u8 rateindex;
u8 qid;
u8 tid;
u32 ba_low;
u32 ba_high;
} ts[ATH_DBG_MAX_SAMPLES];
struct {
u64 jiffies;
int8_t rssi_ctl0;
int8_t rssi_ctl1;
int8_t rssi_ctl2;
int8_t rssi_ext0;
int8_t rssi_ext1;
int8_t rssi_ext2;
int8_t rssi;
bool is_mybeacon;
u8 antenna;
u8 rate;
} rs[ATH_DBG_MAX_SAMPLES];
struct ath_cycle_counters cc;
struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
};
struct ath9k_debug { struct ath9k_debug {
struct dentry *debugfs_phy; struct dentry *debugfs_phy;
u32 regidx; u32 regidx;
struct ath_stats stats; struct ath_stats stats;
#ifdef CONFIG_ATH9K_MAC_DEBUG
spinlock_t samp_lock;
struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES];
u8 sampidx;
u8 tsidx;
u8 rsidx;
#endif
}; };
int ath9k_init_debug(struct ath_hw *ah); int ath9k_init_debug(struct ath_hw *ah);
@ -364,17 +318,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc,
#endif /* CONFIG_ATH9K_DEBUGFS */ #endif /* CONFIG_ATH9K_DEBUGFS */
#ifdef CONFIG_ATH9K_MAC_DEBUG
void ath9k_debug_samp_bb_mac(struct ath_softc *sc);
#else
static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
{
}
#endif
#endif /* DEBUG_H */ #endif /* DEBUG_H */

View File

@ -208,6 +208,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \ case NL80211_IFTYPE_AP: \
_priv->num_ap_vif++; \ _priv->num_ap_vif++; \
break; \ break; \
case NL80211_IFTYPE_MESH_POINT: \
_priv->num_mbss_vif++; \
break; \
default: \ default: \
break; \ break; \
} \ } \
@ -224,6 +227,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \ case NL80211_IFTYPE_AP: \
_priv->num_ap_vif--; \ _priv->num_ap_vif--; \
break; \ break; \
case NL80211_IFTYPE_MESH_POINT: \
_priv->num_mbss_vif--; \
break; \
default: \ default: \
break; \ break; \
} \ } \
@ -450,6 +456,7 @@ struct ath9k_htc_priv {
u8 sta_slot; u8 sta_slot;
u8 vif_sta_pos[ATH9K_HTC_MAX_VIF]; u8 vif_sta_pos[ATH9K_HTC_MAX_VIF];
u8 num_ibss_vif; u8 num_ibss_vif;
u8 num_mbss_vif;
u8 num_sta_vif; u8 num_sta_vif;
u8 num_sta_assoc_vif; u8 num_sta_assoc_vif;
u8 num_ap_vif; u8 num_ap_vif;

View File

@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
ath9k_hw_get_txq_props(ah, priv->beaconq, &qi); ath9k_hw_get_txq_props(ah, priv->beaconq, &qi);
if (priv->ah->opmode == NL80211_IFTYPE_AP) { if (priv->ah->opmode == NL80211_IFTYPE_AP ||
priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) {
qi.tqi_aifs = 1; qi.tqi_aifs = 1;
qi.tqi_cwmin = 0; qi.tqi_cwmin = 0;
qi.tqi_cwmax = 0; qi.tqi_cwmax = 0;
@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf); ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break; break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf); ath9k_htc_beacon_config_ap(priv, cur_conf);
break; break;
@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf); ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break; break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf); ath9k_htc_beacon_config_ap(priv, cur_conf);
break; break;

View File

@ -698,7 +698,8 @@ static const struct ieee80211_iface_limit if_limits[] = {
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) }, BIT(NL80211_IFTYPE_P2P_CLIENT) },
{ .max = 2, .types = BIT(NL80211_IFTYPE_AP) | { .max = 2, .types = BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) }, BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_MESH_POINT) },
}; };
static const struct ieee80211_iface_combination if_comb = { static const struct ieee80211_iface_combination if_comb = {
@ -721,6 +722,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
hw->wiphy->interface_modes = hw->wiphy->interface_modes =
@ -728,7 +730,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT); BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_MESH_POINT);
hw->wiphy->iface_combinations = &if_comb; hw->wiphy->iface_combinations = &if_comb;
hw->wiphy->n_iface_combinations = 1; hw->wiphy->n_iface_combinations = 1;

View File

@ -113,7 +113,9 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
struct ath9k_htc_priv *priv = data; struct ath9k_htc_priv *priv = data;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon) if ((vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_MESH_POINT) &&
bss_conf->enable_beacon)
priv->reconfig_beacon = true; priv->reconfig_beacon = true;
if (bss_conf->assoc) { if (bss_conf->assoc) {
@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
priv->ah->opmode = NL80211_IFTYPE_ADHOC; priv->ah->opmode = NL80211_IFTYPE_ADHOC;
else if (priv->num_ap_vif) else if (priv->num_ap_vif)
priv->ah->opmode = NL80211_IFTYPE_AP; priv->ah->opmode = NL80211_IFTYPE_AP;
else if (priv->num_mbss_vif)
priv->ah->opmode = NL80211_IFTYPE_MESH_POINT;
else else
priv->ah->opmode = NL80211_IFTYPE_STATION; priv->ah->opmode = NL80211_IFTYPE_STATION;
@ -810,8 +814,7 @@ void ath9k_htc_ani_work(struct work_struct *work)
} }
/* Verify whether we must check ANI */ /* Verify whether we must check ANI */
if (ah->config.enable_ani && if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
(timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
aniflag = true; aniflag = true;
common->ani.checkani_timer = timestamp; common->ani.checkani_timer = timestamp;
} }
@ -841,8 +844,7 @@ set_timer:
* short calibration and long calibration. * short calibration and long calibration.
*/ */
cal_interval = ATH_LONG_CALINTERVAL; cal_interval = ATH_LONG_CALINTERVAL;
if (ah->config.enable_ani) cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
if (!common->ani.caldone) if (!common->ani.caldone)
cal_interval = min(cal_interval, (u32)short_cal_interval); cal_interval = min(cal_interval, (u32)short_cal_interval);
@ -1052,6 +1054,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
hvif.opmode = HTC_M_HOSTAP; hvif.opmode = HTC_M_HOSTAP;
break; break;
case NL80211_IFTYPE_MESH_POINT:
hvif.opmode = HTC_M_WDS; /* close enough */
break;
default: default:
ath_err(common, ath_err(common,
"Interface type %d not yet supported\n", vif->type); "Interface type %d not yet supported\n", vif->type);
@ -1084,6 +1089,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
INC_VIF(priv, vif->type); INC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) || if ((vif->type == NL80211_IFTYPE_AP) ||
(vif->type == NL80211_IFTYPE_MESH_POINT) ||
(vif->type == NL80211_IFTYPE_ADHOC)) (vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_assign_bslot(priv, vif); ath9k_htc_assign_bslot(priv, vif);
@ -1134,6 +1140,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
DEC_VIF(priv, vif->type); DEC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) || if ((vif->type == NL80211_IFTYPE_AP) ||
vif->type == NL80211_IFTYPE_MESH_POINT ||
(vif->type == NL80211_IFTYPE_ADHOC)) (vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_remove_bslot(priv, vif); ath9k_htc_remove_bslot(priv, vif);
@ -1525,9 +1532,10 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
/* /*
* Disable SWBA interrupt only if there are no * Disable SWBA interrupt only if there are no
* AP/IBSS interfaces. * concurrent AP/mesh or IBSS interfaces.
*/ */
if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) { if ((priv->num_ap_vif + priv->num_mbss_vif <= 1) ||
priv->num_ibss_vif) {
ath_dbg(common, CONFIG, ath_dbg(common, CONFIG,
"Beacon disabled for BSS: %pM\n", "Beacon disabled for BSS: %pM\n",
bss_conf->bssid); bss_conf->bssid);
@ -1538,12 +1546,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BEACON_INT) { if (changed & BSS_CHANGED_BEACON_INT) {
/* /*
* Reset the HW TSF for the first AP interface. * Reset the HW TSF for the first AP or mesh interface.
*/ */
if ((priv->ah->opmode == NL80211_IFTYPE_AP) && if (priv->nvifs == 1 &&
(priv->nvifs == 1) && ((priv->ah->opmode == NL80211_IFTYPE_AP &&
(priv->num_ap_vif == 1) && vif->type == NL80211_IFTYPE_AP &&
(vif->type == NL80211_IFTYPE_AP)) { priv->num_ap_vif == 1) ||
(priv->ah->opmode == NL80211_IFTYPE_MESH_POINT &&
vif->type == NL80211_IFTYPE_MESH_POINT &&
priv->num_mbss_vif == 1))) {
set_bit(OP_TSF_RESET, &priv->op_flags); set_bit(OP_TSF_RESET, &priv->op_flags);
} }
ath_dbg(common, CONFIG, ath_dbg(common, CONFIG,

View File

@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv)
if (priv->rxfilter & FIF_PSPOLL) if (priv->rxfilter & FIF_PSPOLL)
rfilt |= ATH9K_RX_FILTER_PSPOLL; rfilt |= ATH9K_RX_FILTER_PSPOLL;
if (priv->nvifs > 1) if (priv->nvifs > 1 || priv->rxfilter & FIF_OTHER_BSS)
rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
return rfilt; return rfilt;

View File

@ -452,7 +452,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
ah->config.pcie_clock_req = 0; ah->config.pcie_clock_req = 0;
ah->config.pcie_waen = 0; ah->config.pcie_waen = 0;
ah->config.analog_shiftreg = 1; ah->config.analog_shiftreg = 1;
ah->config.enable_ani = true;
for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
ah->config.spurchans[i][0] = AR_NO_SPUR; ah->config.spurchans[i][0] = AR_NO_SPUR;
@ -549,8 +548,7 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_ver(ah),
ah->eep_ops->get_eeprom_rev(ah)); ah->eep_ops->get_eeprom_rev(ah));
if (ah->config.enable_ani) ath9k_hw_ani_init(ah);
ath9k_hw_ani_init(ah);
return 0; return 0;
} }
@ -1250,10 +1248,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
switch (opmode) { switch (opmode) {
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
set |= AR_STA_ID1_ADHOC; set |= AR_STA_ID1_ADHOC;
REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
break; break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
set |= AR_STA_ID1_STA_AP; set |= AR_STA_ID1_STA_AP;
/* fall through */ /* fall through */
@ -2255,12 +2253,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
switch (ah->opmode) { switch (ah->opmode) {
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
REG_SET_BIT(ah, AR_TXCFG, REG_SET_BIT(ah, AR_TXCFG,
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon + REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
TU_TO_USEC(ah->atim_window ? ah->atim_window : 1)); TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
flags |= AR_NDP_TIMER_EN; flags |= AR_NDP_TIMER_EN;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon); REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon - REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -
@ -2604,13 +2602,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->hw_caps |= ATH9K_HW_CAP_RTT; pCap->hw_caps |= ATH9K_HW_CAP_RTT;
} }
if (AR_SREV_9280_20_OR_LATER(ah)) { if (AR_SREV_9462(ah))
pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE | pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE;
ATH9K_HW_WOW_PATTERN_MATCH_EXACT;
if (AR_SREV_9280(ah))
pCap->hw_caps |= ATH9K_HW_WOW_PATTERN_MATCH_DWORD;
}
if (AR_SREV_9300_20_OR_LATER(ah) && if (AR_SREV_9300_20_OR_LATER(ah) &&
ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) ah->eep_ops->get_eeprom(ah, EEP_PAPRD))

View File

@ -246,9 +246,7 @@ enum ath9k_hw_caps {
ATH9K_HW_CAP_MCI = BIT(15), ATH9K_HW_CAP_MCI = BIT(15),
ATH9K_HW_CAP_DFS = BIT(16), ATH9K_HW_CAP_DFS = BIT(16),
ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17), ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17),
ATH9K_HW_WOW_PATTERN_MATCH_EXACT = BIT(18), ATH9K_HW_CAP_PAPRD = BIT(18),
ATH9K_HW_WOW_PATTERN_MATCH_DWORD = BIT(19),
ATH9K_HW_CAP_PAPRD = BIT(20),
}; };
/* /*
@ -291,7 +289,6 @@ struct ath9k_ops_config {
u32 ofdm_trig_high; u32 ofdm_trig_high;
u32 cck_trig_high; u32 cck_trig_high;
u32 cck_trig_low; u32 cck_trig_low;
u32 enable_ani;
u32 enable_paprd; u32 enable_paprd;
int serialize_regmode; int serialize_regmode;
bool rx_intr_mitigation; bool rx_intr_mitigation;
@ -423,7 +420,6 @@ struct ath9k_hw_cal_data {
struct ath9k_channel { struct ath9k_channel {
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
struct ar5416AniState ani;
u16 channel; u16 channel;
u32 channelFlags; u32 channelFlags;
u32 chanmode; u32 chanmode;
@ -854,10 +850,10 @@ struct ath_hw {
u32 globaltxtimeout; u32 globaltxtimeout;
/* ANI */ /* ANI */
u32 proc_phyerr;
u32 aniperiod; u32 aniperiod;
enum ath9k_ani_cmd ani_function; enum ath9k_ani_cmd ani_function;
u32 ani_skip_count; u32 ani_skip_count;
struct ar5416AniState ani;
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
struct ath_btcoex_hw btcoex_hw; struct ath_btcoex_hw btcoex_hw;
@ -882,9 +878,6 @@ struct ath_hw {
struct ar5416IniArray iniBank6; struct ar5416IniArray iniBank6;
struct ar5416IniArray iniAddac; struct ar5416IniArray iniAddac;
struct ar5416IniArray iniPcieSerdes; struct ar5416IniArray iniPcieSerdes;
#ifdef CONFIG_PM_SLEEP
struct ar5416IniArray iniPcieSerdesWow;
#endif
struct ar5416IniArray iniPcieSerdesLowPower; struct ar5416IniArray iniPcieSerdesLowPower;
struct ar5416IniArray iniModesFastClock; struct ar5416IniArray iniModesFastClock;
struct ar5416IniArray iniAdditional; struct ar5416IniArray iniAdditional;
@ -1165,8 +1158,6 @@ static inline void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
} }
#endif #endif
#define ATH9K_CLOCK_RATE_CCK 22 #define ATH9K_CLOCK_RATE_CCK 22
#define ATH9K_CLOCK_RATE_5GHZ_OFDM 40 #define ATH9K_CLOCK_RATE_5GHZ_OFDM 40
#define ATH9K_CLOCK_RATE_2GHZ_OFDM 44 #define ATH9K_CLOCK_RATE_2GHZ_OFDM 44

View File

@ -21,6 +21,7 @@
#include <linux/ath9k_platform.h> #include <linux/ath9k_platform.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/relay.h> #include <linux/relay.h>
#include <net/ieee80211_radiotap.h>
#include "ath9k.h" #include "ath9k.h"
@ -613,9 +614,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock); spin_lock_init(&sc->sc_pm_lock);
mutex_init(&sc->mutex); mutex_init(&sc->mutex);
#ifdef CONFIG_ATH9K_MAC_DEBUG
spin_lock_init(&sc->debug.samp_lock);
#endif
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
(unsigned long)sc); (unsigned long)sc);
@ -769,12 +767,19 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SUPPORTS_RC_TABLE; IEEE80211_HW_SUPPORTS_RC_TABLE;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
if (AR_SREV_9280_20_OR_LATER(ah))
hw->radiotap_mcs_details |=
IEEE80211_RADIOTAP_MCS_HAVE_STBC;
}
if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt) if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
hw->flags |= IEEE80211_HW_MFP_CAPABLE; hw->flags |= IEEE80211_HW_MFP_CAPABLE;
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
hw->wiphy->interface_modes = hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_CLIENT) |
@ -795,21 +800,17 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
device_can_wakeup(sc->dev)) { device_can_wakeup(sc->dev)) {
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_DISCONNECT; WIPHY_WOWLAN_DISCONNECT;
hw->wiphy->wowlan.n_patterns = MAX_NUM_USER_PATTERN; hw->wiphy->wowlan.n_patterns = MAX_NUM_USER_PATTERN;
hw->wiphy->wowlan.pattern_min_len = 1; hw->wiphy->wowlan.pattern_min_len = 1;
hw->wiphy->wowlan.pattern_max_len = MAX_PATTERN_SIZE; hw->wiphy->wowlan.pattern_max_len = MAX_PATTERN_SIZE;
} }
atomic_set(&sc->wow_sleep_proc_intr, -1); atomic_set(&sc->wow_sleep_proc_intr, -1);
atomic_set(&sc->wow_got_bmiss_intr, -1); atomic_set(&sc->wow_got_bmiss_intr, -1);
#endif #endif
hw->queues = 4; hw->queues = 4;

View File

@ -390,9 +390,7 @@ void ath_ani_calibrate(unsigned long data)
} }
/* Verify whether we must check ANI */ /* Verify whether we must check ANI */
if (sc->sc_ah->config.enable_ani if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) {
&& (timestamp - common->ani.checkani_timer) >=
ah->config.ani_poll_interval) {
aniflag = true; aniflag = true;
common->ani.checkani_timer = timestamp; common->ani.checkani_timer = timestamp;
} }
@ -418,7 +416,6 @@ void ath_ani_calibrate(unsigned long data)
longcal ? "long" : "", shortcal ? "short" : "", longcal ? "long" : "", shortcal ? "short" : "",
aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
ath9k_debug_samp_bb_mac(sc);
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
set_timer: set_timer:
@ -428,9 +425,7 @@ set_timer:
* short calibration and long calibration. * short calibration and long calibration.
*/ */
cal_interval = ATH_LONG_CALINTERVAL; cal_interval = ATH_LONG_CALINTERVAL;
if (sc->sc_ah->config.enable_ani) cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval);
cal_interval = min(cal_interval,
(u32)ah->config.ani_poll_interval);
if (!common->ani.caldone) if (!common->ani.caldone)
cal_interval = min(cal_interval, (u32)short_cal_interval); cal_interval = min(cal_interval, (u32)short_cal_interval);

View File

@ -547,6 +547,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
rs->rs_status = 0; rs->rs_status = 0;
rs->rs_flags = 0; rs->rs_flags = 0;
rs->flag = 0;
rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen; rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen;
rs->rs_tstamp = ads.AR_RcvTimestamp; rs->rs_tstamp = ads.AR_RcvTimestamp;
@ -586,10 +587,17 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
rs->rs_moreaggr = rs->rs_moreaggr =
(ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
rs->rs_flags =
(ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0; /* directly mapped flags for ieee80211_rx_status */
rs->rs_flags |= rs->flag |=
(ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0; (ads.ds_rxstatus3 & AR_GI) ? RX_FLAG_SHORT_GI : 0;
rs->flag |=
(ads.ds_rxstatus3 & AR_2040) ? RX_FLAG_40MHZ : 0;
if (AR_SREV_9280_20_OR_LATER(ah))
rs->flag |=
(ads.ds_rxstatus3 & AR_STBC) ?
/* we can only Nss=1 STBC */
(1 << RX_FLAG_STBC_SHIFT) : 0;
if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) if (ads.ds_rxstatus8 & AR_PreDelimCRCErr)
rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE; rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE;

View File

@ -149,6 +149,7 @@ struct ath_rx_status {
u32 evm2; u32 evm2;
u32 evm3; u32 evm3;
u32 evm4; u32 evm4;
u32 flag; /* see enum mac80211_rx_flags */
}; };
struct ath_htc_rx_status { struct ath_htc_rx_status {
@ -533,7 +534,8 @@ struct ar5416_desc {
#define AR_2040 0x00000002 #define AR_2040 0x00000002
#define AR_Parallel40 0x00000004 #define AR_Parallel40 0x00000004
#define AR_Parallel40_S 2 #define AR_Parallel40_S 2
#define AR_RxStatusRsvd30 0x000000f8 #define AR_STBC 0x00000008 /* on ar9280 and later */
#define AR_RxStatusRsvd30 0x000000f0
#define AR_RxAntenna 0xffffff00 #define AR_RxAntenna 0xffffff00
#define AR_RxAntenna_S 8 #define AR_RxAntenna_S 8

View File

@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc)
ath_stop_ani(sc); ath_stop_ani(sc);
del_timer_sync(&sc->rx_poll_timer); del_timer_sync(&sc->rx_poll_timer);
ath9k_debug_samp_bb_mac(sc);
ath9k_hw_disable_interrupts(ah); ath9k_hw_disable_interrupts(ah);
if (!ath_drain_all_txq(sc)) if (!ath_drain_all_txq(sc))
@ -1273,7 +1272,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
curchan->center_freq); curchan->center_freq);
} else { } else {
/* perform spectral scan if requested. */ /* perform spectral scan if requested. */
if (sc->scanning && if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN) sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw); ath9k_spectral_scan_trigger(hw);
} }
@ -1690,7 +1689,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
bool flush = false; bool flush = false;
int ret = 0; int ret = 0;
local_bh_disable(); mutex_lock(&sc->mutex);
switch (action) { switch (action) {
case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_START:
@ -1723,7 +1722,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n"); ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
} }
local_bh_enable(); mutex_unlock(&sc->mutex);
return ret; return ret;
} }
@ -2007,7 +2006,6 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
{ {
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_capabilities *pcaps = &ah->caps;
int pattern_count = 0; int pattern_count = 0;
int i, byte_cnt; int i, byte_cnt;
u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
@ -2077,36 +2075,9 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
/* Create Disassociate pattern mask */ /* Create Disassociate pattern mask */
if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_EXACT) { dis_deauth_mask[0] = 0xfe;
dis_deauth_mask[1] = 0x03;
if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_DWORD) { dis_deauth_mask[2] = 0xc0;
/*
* for AR9280, because of hardware limitation, the
* first 4 bytes have to be matched for all patterns.
* the mask for disassociation and de-auth pattern
* matching need to enable the first 4 bytes.
* also the duration field needs to be filled.
*/
dis_deauth_mask[0] = 0xf0;
/*
* fill in duration field
FIXME: what is the exact value ?
*/
dis_deauth_pattern[2] = 0xff;
dis_deauth_pattern[3] = 0xff;
} else {
dis_deauth_mask[0] = 0xfe;
}
dis_deauth_mask[1] = 0x03;
dis_deauth_mask[2] = 0xc0;
} else {
dis_deauth_mask[0] = 0xef;
dis_deauth_mask[1] = 0x3f;
dis_deauth_mask[2] = 0x00;
dis_deauth_mask[3] = 0xfc;
}
ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n"); ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
@ -2342,15 +2313,13 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
static void ath9k_sw_scan_start(struct ieee80211_hw *hw) static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
{ {
struct ath_softc *sc = hw->priv; struct ath_softc *sc = hw->priv;
set_bit(SC_OP_SCANNING, &sc->sc_flags);
sc->scanning = 1;
} }
static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
{ {
struct ath_softc *sc = hw->priv; struct ath_softc *sc = hw->priv;
clear_bit(SC_OP_SCANNING, &sc->sc_flags);
sc->scanning = 0;
} }
struct ieee80211_ops ath9k_ops = { struct ieee80211_ops ath9k_ops = {

View File

@ -868,10 +868,7 @@ static int ath9k_process_rate(struct ath_common *common,
if (rx_stats->rs_rate & 0x80) { if (rx_stats->rs_rate & 0x80) {
/* HT rate */ /* HT rate */
rxs->flag |= RX_FLAG_HT; rxs->flag |= RX_FLAG_HT;
if (rx_stats->rs_flags & ATH9K_RX_2040) rxs->flag |= rx_stats->flag;
rxs->flag |= RX_FLAG_40MHZ;
if (rx_stats->rs_flags & ATH9K_RX_GI)
rxs->flag |= RX_FLAG_SHORT_GI;
rxs->rate_idx = rx_stats->rs_rate & 0x7f; rxs->rate_idx = rx_stats->rs_rate & 0x7f;
return 0; return 0;
} }
@ -958,11 +955,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
if (rx_stats->rs_more) if (rx_stats->rs_more)
return 0; return 0;
ath9k_process_rssi(common, hw, hdr, rx_stats);
if (ath9k_process_rate(common, hw, rx_stats, rx_status)) if (ath9k_process_rate(common, hw, rx_stats, rx_status))
return -EINVAL; return -EINVAL;
ath9k_process_rssi(common, hw, hdr, rx_stats);
rx_status->band = hw->conf.chandef.chan->band; rx_status->band = hw->conf.chandef.chan->band;
rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->freq = hw->conf.chandef.chan->center_freq;
rx_status->signal = ah->noise + rx_stats->rs_rssi; rx_status->signal = ah->noise + rx_stats->rs_rssi;

View File

@ -34,17 +34,6 @@ const char *ath9k_hw_wow_event_to_string(u32 wow_event)
} }
EXPORT_SYMBOL(ath9k_hw_wow_event_to_string); EXPORT_SYMBOL(ath9k_hw_wow_event_to_string);
static void ath9k_hw_config_serdes_wow_sleep(struct ath_hw *ah)
{
int i;
for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++)
REG_WRITE(ah, INI_RA(&ah->iniPcieSerdesWow, i, 0),
INI_RA(&ah->iniPcieSerdesWow, i, 1));
usleep_range(1000, 1500);
}
static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah) static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
@ -58,15 +47,8 @@ static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW)); REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
return; return;
} else {
if (!AR_SREV_9300_20_OR_LATER(ah))
REG_WRITE(ah, AR_RXDP, 0x0);
} }
/* AR9280 WoW has sleep issue, do not set it to sleep */
if (AR_SREV_9280_20(ah))
return;
REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
} }
@ -84,27 +66,16 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
/* set the transmit buffer */ /* set the transmit buffer */
ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16)); ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
if (!(AR_SREV_9300_20_OR_LATER(ah)))
ctl[0] += (KAL_ANTENNA_MODE << 25);
ctl[1] = 0; ctl[1] = 0;
ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */ ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */
ctl[4] = 0; ctl[4] = 0;
ctl[7] = (ah->txchainmask) << 2; ctl[7] = (ah->txchainmask) << 2;
ctl[2] = 0xf << 16; /* tx_tries 0 */
if (AR_SREV_9300_20_OR_LATER(ah))
ctl[2] = 0xf << 16; /* tx_tries 0 */
else
ctl[2] = 0x7 << 16; /* tx_tries 0 */
for (i = 0; i < KAL_NUM_DESC_WORDS; i++) for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
/* for AR9300 family 13 descriptor words */ REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
if (AR_SREV_9300_20_OR_LATER(ah))
REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) | data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) |
(KAL_TO_DS << 8) | (KAL_DURATION_ID << 16); (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16);
@ -183,9 +154,6 @@ void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT); ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
if (!AR_SREV_9285_12_OR_LATER(ah))
return;
if (pattern_count < 4) { if (pattern_count < 4) {
/* Pattern 0-3 uses AR_WOW_LENGTH1 register */ /* Pattern 0-3 uses AR_WOW_LENGTH1 register */
set = (pattern_len & AR_WOW_LENGTH_MAX) << set = (pattern_len & AR_WOW_LENGTH_MAX) <<
@ -207,6 +175,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
{ {
u32 wow_status = 0; u32 wow_status = 0;
u32 val = 0, rval; u32 val = 0, rval;
/* /*
* read the WoW status register to know * read the WoW status register to know
* the wakeup reason * the wakeup reason
@ -223,19 +192,14 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
val &= ah->wow_event_mask; val &= ah->wow_event_mask;
if (val) { if (val) {
if (val & AR_WOW_MAGIC_PAT_FOUND) if (val & AR_WOW_MAGIC_PAT_FOUND)
wow_status |= AH_WOW_MAGIC_PATTERN_EN; wow_status |= AH_WOW_MAGIC_PATTERN_EN;
if (AR_WOW_PATTERN_FOUND(val)) if (AR_WOW_PATTERN_FOUND(val))
wow_status |= AH_WOW_USER_PATTERN_EN; wow_status |= AH_WOW_USER_PATTERN_EN;
if (val & AR_WOW_KEEP_ALIVE_FAIL) if (val & AR_WOW_KEEP_ALIVE_FAIL)
wow_status |= AH_WOW_LINK_CHANGE; wow_status |= AH_WOW_LINK_CHANGE;
if (val & AR_WOW_BEACON_FAIL) if (val & AR_WOW_BEACON_FAIL)
wow_status |= AH_WOW_BEACON_MISS; wow_status |= AH_WOW_BEACON_MISS;
} }
/* /*
@ -254,17 +218,6 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
REG_WRITE(ah, AR_WOW_PATTERN, REG_WRITE(ah, AR_WOW_PATTERN,
AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN))); AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN)));
/*
* tie reset register for AR9002 family of chipsets
* NB: not tieing it back might have some repurcussions.
*/
if (!AR_SREV_9300_20_OR_LATER(ah)) {
REG_SET_BIT(ah, AR_WA, AR_WA_UNTIE_RESET_EN |
AR_WA_POR_SHORT | AR_WA_RESET_EN);
}
/* /*
* restore the beacon threshold to init value * restore the beacon threshold to init value
*/ */
@ -277,8 +230,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
* reset to our Chip's Power On Reset so that any PCI-E * reset to our Chip's Power On Reset so that any PCI-E
* reset from the bus will not reset our chip * reset from the bus will not reset our chip
*/ */
if (ah->is_pciexpress)
if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress)
ath9k_hw_configpcipowersave(ah, false); ath9k_hw_configpcipowersave(ah, false);
ah->wow_event_mask = 0; ah->wow_event_mask = 0;
@ -298,7 +250,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
* are from the 'pattern_enable' in this function and * are from the 'pattern_enable' in this function and
* 'pattern_count' of ath9k_hw_wow_apply_pattern() * 'pattern_count' of ath9k_hw_wow_apply_pattern()
*/ */
wow_event_mask = ah->wow_event_mask; wow_event_mask = ah->wow_event_mask;
/* /*
@ -306,50 +257,15 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
* WOW sleep, we do want the Reset from the PCI-E to disturb * WOW sleep, we do want the Reset from the PCI-E to disturb
* our hw state * our hw state
*/ */
if (ah->is_pciexpress) { if (ah->is_pciexpress) {
/* /*
* we need to untie the internal POR (power-on-reset) * we need to untie the internal POR (power-on-reset)
* to the external PCI-E reset. We also need to tie * to the external PCI-E reset. We also need to tie
* the PCI-E Phy reset to the PCI-E reset. * the PCI-E Phy reset to the PCI-E reset.
*/ */
set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
if (AR_SREV_9300_20_OR_LATER(ah)) { clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
set = AR_WA_RESET_EN | AR_WA_POR_SHORT; REG_RMW(ah, AR_WA, set, clr);
clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
REG_RMW(ah, AR_WA, set, clr);
} else {
if (AR_SREV_9285(ah) || AR_SREV_9287(ah))
set = AR9285_WA_DEFAULT;
else
set = AR9280_WA_DEFAULT;
/*
* In AR9280 and AR9285, bit 14 in WA register
* (disable L1) should only be set when device
* enters D3 state and be cleared when device
* comes back to D0
*/
if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
set |= AR_WA_D3_L1_DISABLE;
clr = AR_WA_UNTIE_RESET_EN;
set |= AR_WA_RESET_EN | AR_WA_POR_SHORT;
REG_RMW(ah, AR_WA, set, clr);
/*
* for WoW sleep, we reprogram the SerDes so that the
* PLL and CLK REQ are both enabled. This uses more
* power but otherwise WoW sleep is unstable and the
* chip may disappear.
*/
if (AR_SREV_9285_12_OR_LATER(ah))
ath9k_hw_config_serdes_wow_sleep(ah);
}
} }
/* /*
@ -378,7 +294,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
* Program default values for pattern backoff, aifs/slot/KAL count, * Program default values for pattern backoff, aifs/slot/KAL count,
* beacon miss timeout, KAL timeout, etc. * beacon miss timeout, KAL timeout, etc.
*/ */
set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF); set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF);
REG_SET_BIT(ah, AR_WOW_PATTERN, set); REG_SET_BIT(ah, AR_WOW_PATTERN, set);
@ -398,7 +313,7 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
/* /*
* Keep alive timo in ms except AR9280 * Keep alive timo in ms except AR9280
*/ */
if (!pattern_enable || AR_SREV_9280(ah)) if (!pattern_enable)
set = AR_WOW_KEEP_ALIVE_NEVER; set = AR_WOW_KEEP_ALIVE_NEVER;
else else
set = KAL_TIMEOUT * 32; set = KAL_TIMEOUT * 32;
@ -420,7 +335,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
/* /*
* Configure MAC WoW Registers * Configure MAC WoW Registers
*/ */
set = 0; set = 0;
/* Send keep alive timeouts anyway */ /* Send keep alive timeouts anyway */
clr = AR_WOW_KEEP_ALIVE_AUTO_DIS; clr = AR_WOW_KEEP_ALIVE_AUTO_DIS;
@ -430,16 +344,9 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
else else
set = AR_WOW_KEEP_ALIVE_FAIL_DIS; set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
/*
* FIXME: For now disable keep alive frame
* failure. This seems to sometimes trigger
* unnecessary wake up with AR9485 chipsets.
*/
set = AR_WOW_KEEP_ALIVE_FAIL_DIS; set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr); REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr);
/* /*
* we are relying on a bmiss failure. ensure we have * we are relying on a bmiss failure. ensure we have
* enough threshold to prevent false positives * enough threshold to prevent false positives
@ -473,14 +380,8 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
set |= AR_WOW_MAC_INTR_EN; set |= AR_WOW_MAC_INTR_EN;
REG_RMW(ah, AR_WOW_PATTERN, set, clr); REG_RMW(ah, AR_WOW_PATTERN, set, clr);
/* REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
* For AR9285 and later version of chipsets AR_WOW_PATTERN_SUPPORTED);
* enable WoW pattern match for packets less
* than 256 bytes for all patterns
*/
if (AR_SREV_9285_12_OR_LATER(ah))
REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
AR_WOW_PATTERN_SUPPORTED);
/* /*
* Set the power states appropriately and enable PME * Set the power states appropriately and enable PME
@ -488,43 +389,32 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
clr = 0; clr = 0;
set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN | set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN |
AR_PMCTRL_PWR_PM_CTRL_ENA; AR_PMCTRL_PWR_PM_CTRL_ENA;
/*
* This is needed for AR9300 chipsets to wake-up
* the host.
*/
if (AR_SREV_9300_20_OR_LATER(ah))
clr = AR_PCIE_PM_CTRL_ENA;
clr = AR_PCIE_PM_CTRL_ENA;
REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr); REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr);
if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { /*
/* * this is needed to prevent the chip waking up
* this is needed to prevent the chip waking up * the host within 3-4 seconds with certain
* the host within 3-4 seconds with certain * platform/BIOS. The fix is to enable
* platform/BIOS. The fix is to enable * D1 & D3 to match original definition and
* D1 & D3 to match original definition and * also match the OTP value. Anyway this
* also match the OTP value. Anyway this * is more related to SW WOW.
* is more related to SW WOW. */
*/ clr = AR_PMCTRL_PWR_STATE_D1D3;
clr = AR_PMCTRL_PWR_STATE_D1D3; REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
}
set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM); REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
if (AR_SREV_9300_20_OR_LATER(ah)) { /* to bring down WOW power low margin */
/* to bring down WOW power low margin */ set = BIT(13);
set = BIT(13); REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set); /* HW WoW */
/* HW WoW */ clr = BIT(5);
clr = BIT(5); REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
}
ath9k_hw_set_powermode_wow_sleep(ah); ath9k_hw_set_powermode_wow_sleep(ah);
ah->wow_event_mask = wow_event_mask; ah->wow_event_mask = wow_event_mask;

View File

@ -27,3 +27,15 @@ config WIL6210_ISR_COR
self-clear when accessed for debug purposes, it makes self-clear when accessed for debug purposes, it makes
such monitoring impossible. such monitoring impossible.
Say y unless you debug interrupts Say y unless you debug interrupts
config ATH6KL_TRACING
bool "wil6210 tracing support"
depends on WIL6210
depends on EVENT_TRACING
default y
---help---
Say Y here to enable tracepoints for the wil6210 driver
using the kernel tracing infrastructure. Select this
option if you are interested in debugging the driver.
If unsure, say Y to make it easier to debug problems.

View File

@ -1,15 +1,20 @@
obj-$(CONFIG_WIL6210) += wil6210.o obj-$(CONFIG_WIL6210) += wil6210.o
wil6210-objs := main.o wil6210-y := main.o
wil6210-objs += netdev.o wil6210-y += netdev.o
wil6210-objs += cfg80211.o wil6210-y += cfg80211.o
wil6210-objs += pcie_bus.o wil6210-y += pcie_bus.o
wil6210-objs += debugfs.o wil6210-y += debugfs.o
wil6210-objs += wmi.o wil6210-y += wmi.o
wil6210-objs += interrupt.o wil6210-y += interrupt.o
wil6210-objs += txrx.o wil6210-y += txrx.o
wil6210-y += debug.o
wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
subdir-ccflags-y += -Werror subdir-ccflags-y += -Werror
endif endif
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
subdir-ccflags-y += -D__CHECK_ENDIAN__ subdir-ccflags-y += -D__CHECK_ENDIAN__

View File

@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
* FW don't support scan after connection attempt * FW don't support scan after connection attempt
*/ */
set_bit(wil_status_dontscan, &wil->status); set_bit(wil_status_dontscan, &wil->status);
set_bit(wil_status_fwconnecting, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
if (rc == 0) { if (rc == 0) {
/* Connect can take lots of time */ /* Connect can take lots of time */
mod_timer(&wil->connect_timer, mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000)); jiffies + msecs_to_jiffies(2000));
} else {
clear_bit(wil_status_dontscan, &wil->status);
clear_bit(wil_status_fwconnecting, &wil->status);
} }
out: out:

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "wil6210.h"
#include "trace.h"
int wil_err(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
int ret;
va_start(args, fmt);
vaf.va = &args;
ret = netdev_err(ndev, "%pV", &vaf);
trace_wil6210_log_err(&vaf);
va_end(args);
return ret;
}
int wil_info(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
int ret;
va_start(args, fmt);
vaf.va = &args;
ret = netdev_info(ndev, "%pV", &vaf);
trace_wil6210_log_info(&vaf);
va_end(args);
return ret;
}
int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
va_start(args, fmt);
vaf.va = &args;
trace_wil6210_log_dbg(&vaf);
va_end(args);
return 0;
}

View File

@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
if (skb) { if (skb) {
unsigned char printbuf[16 * 3 + 2]; unsigned char printbuf[16 * 3 + 2];
int i = 0; int i = 0;
int len = skb_headlen(skb); int len = le16_to_cpu(d->dma.length);
void *p = skb->data; void *p = skb->data;
if (len != skb_headlen(skb)) {
seq_printf(s, "!!! len: desc = %d skb = %d\n",
len, skb_headlen(skb));
len = min_t(int, len, skb_headlen(skb));
}
seq_printf(s, " len = %d\n", len); seq_printf(s, " len = %d\n", len);
while (i < len) { while (i < len) {

View File

@ -17,6 +17,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include "wil6210.h" #include "wil6210.h"
#include "trace.h"
/** /**
* Theory of operation: * Theory of operation:
@ -103,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
clear_bit(wil_status_irqen, &wil->status); clear_bit(wil_status_irqen, &wil->status);
} }
static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
{ {
iowrite32(WIL6210_IMC_TX, wil->csr + iowrite32(WIL6210_IMC_TX, wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) + HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMC)); offsetof(struct RGF_ICR, IMC));
} }
static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{ {
iowrite32(WIL6210_IMC_RX, wil->csr + iowrite32(WIL6210_IMC_RX, wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) + HOSTADDR(RGF_DMA_EP_RX_ICR) +
@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_RX_ICR) + HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR)); offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_rx(isr);
wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
if (!isr) { if (!isr) {
@ -180,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
wil_dbg_irq(wil, "RX done\n"); wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
wil_rx_handle(wil); wil_dbg_txrx(wil, "NAPI schedule\n");
napi_schedule(&wil->napi_rx);
} }
if (isr) if (isr)
wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
wil6210_unmask_irq_rx(wil); /* Rx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -198,6 +201,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_TX_ICR) + HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR)); offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_tx(isr);
wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
if (!isr) { if (!isr) {
@ -208,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
wil6210_mask_irq_tx(wil); wil6210_mask_irq_tx(wil);
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
uint i;
wil_dbg_irq(wil, "TX done\n"); wil_dbg_irq(wil, "TX done\n");
napi_schedule(&wil->napi_tx);
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
for (i = 0; i < 24; i++) { /* clear also all VRING interrupts */
u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); isr &= ~(BIT(25) - 1UL);
if (isr & mask) {
isr &= ~mask;
wil_dbg_irq(wil, "TX done(%i)\n", i);
wil_tx_complete(wil, i);
}
}
} }
if (isr) if (isr)
wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
wil6210_unmask_irq_tx(wil); /* Tx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -256,6 +254,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_MISC_ICR) + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR)); offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_misc(isr);
wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr); wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
if (!isr) { if (!isr) {
@ -301,6 +300,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
struct wil6210_priv *wil = cookie; struct wil6210_priv *wil = cookie;
u32 isr = wil->isr_misc; u32 isr = wil->isr_misc;
trace_wil6210_irq_misc_thread(isr);
wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
if (isr & ISR_MISC_FW_ERROR) { if (isr & ISR_MISC_FW_ERROR) {
@ -408,6 +408,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
if (wil6210_debug_irq_mask(wil, pseudo_cause)) if (wil6210_debug_irq_mask(wil, pseudo_cause))
return IRQ_NONE; return IRQ_NONE;
trace_wil6210_irq_pseudo(pseudo_cause);
wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
wil6210_mask_irq_pseudo(wil); wil6210_mask_irq_pseudo(wil);

View File

@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
{ {
uint i; uint i;
struct net_device *ndev = wil_to_ndev(wil); struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
wil_dbg_misc(wil, "%s()\n", __func__); wil_dbg_misc(wil, "%s()\n", __func__);
wil_link_off(wil); wil_link_off(wil);
clear_bit(wil_status_fwconnected, &wil->status); if (test_bit(wil_status_fwconnected, &wil->status)) {
clear_bit(wil_status_fwconnected, &wil->status);
switch (wdev->sme_state) { cfg80211_disconnected(ndev,
case CFG80211_SME_CONNECTED: WLAN_STATUS_UNSPECIFIED_FAILURE,
cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
NULL, 0, GFP_KERNEL); NULL, 0, GFP_KERNEL);
break; } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
case CFG80211_SME_CONNECTING:
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL); GFP_KERNEL);
break;
default:
break;
} }
clear_bit(wil_status_fwconnecting, &wil->status);
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
wil_vring_fini_tx(wil, i); wil_vring_fini_tx(wil, i);
@ -365,6 +359,9 @@ static int __wil_up(struct wil6210_priv *wil)
/* Rx VRING. After MAC and beacon */ /* Rx VRING. After MAC and beacon */
wil_rx_init(wil); wil_rx_init(wil);
napi_enable(&wil->napi_rx);
napi_enable(&wil->napi_tx);
return 0; return 0;
} }
@ -381,6 +378,9 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(struct wil6210_priv *wil) static int __wil_down(struct wil6210_priv *wil)
{ {
napi_disable(&wil->napi_rx);
napi_disable(&wil->napi_tx);
if (wil->scan_request) { if (wil->scan_request) {
cfg80211_scan_done(wil->scan_request, true); cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL; wil->scan_request = NULL;

View File

@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = {
.ndo_validate_addr = eth_validate_addr, .ndo_validate_addr = eth_validate_addr,
}; };
static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
{
struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
napi_rx);
int quota = budget;
int done;
wil_rx_handle(wil, &quota);
done = budget - quota;
if (done <= 1) { /* burst ends - only one packet processed */
napi_complete(napi);
wil6210_unmask_irq_rx(wil);
wil_dbg_txrx(wil, "NAPI RX complete\n");
}
wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
return done;
}
static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
{
struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
napi_tx);
int tx_done = 0;
uint i;
/* always process ALL Tx complete, regardless budget - it is fast */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
struct vring *vring = &wil->vring_tx[i];
if (!vring->va)
continue;
tx_done += wil_tx_complete(wil, i);
}
if (tx_done <= 1) { /* burst ends - only one packet processed */
napi_complete(napi);
wil6210_unmask_irq_tx(wil);
wil_dbg_txrx(wil, "NAPI TX complete\n");
}
wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
return min(tx_done, budget);
}
void *wil_if_alloc(struct device *dev, void __iomem *csr) void *wil_if_alloc(struct device *dev, void __iomem *csr)
{ {
struct net_device *ndev; struct net_device *ndev;
@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev; wdev->netdev = ndev;
netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
WIL6210_NAPI_BUDGET);
netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
WIL6210_NAPI_BUDGET);
wil_link_off(wil); wil_link_off(wil);
return wil; return wil;

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#define CREATE_TRACE_POINTS
#include "trace.h"

View File

@ -0,0 +1,235 @@
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM wil6210
#if !defined(WIL6210_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define WIL6210_TRACE_H
#include <linux/tracepoint.h>
#include "wil6210.h"
#include "txrx.h"
/* create empty functions when tracing is disabled */
#if !defined(CONFIG_WIL6210_TRACING) || defined(__CHECKER__)
#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, ...) \
static inline void trace_ ## name(proto) {}
#undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(...)
#undef DEFINE_EVENT
#define DEFINE_EVENT(evt_class, name, proto, ...) \
static inline void trace_ ## name(proto) {}
#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
DECLARE_EVENT_CLASS(wil6210_wmi,
TP_PROTO(u16 id, void *buf, u16 buf_len),
TP_ARGS(id, buf, buf_len),
TP_STRUCT__entry(
__field(u16, id)
__field(u16, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__entry->id = id;
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"id 0x%04x len %d",
__entry->id, __entry->buf_len
)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
TP_PROTO(u16 id, void *buf, u16 buf_len),
TP_ARGS(id, buf, buf_len)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
TP_PROTO(u16 id, void *buf, u16 buf_len),
TP_ARGS(id, buf, buf_len)
);
#define WIL6210_MSG_MAX (200)
DECLARE_EVENT_CLASS(wil6210_log_event,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf),
TP_STRUCT__entry(
__dynamic_array(char, msg, WIL6210_MSG_MAX)
),
TP_fast_assign(
WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
WIL6210_MSG_MAX,
vaf->fmt,
*vaf->va) >= WIL6210_MSG_MAX);
),
TP_printk("%s", __get_str(msg))
);
DEFINE_EVENT(wil6210_log_event, wil6210_log_err,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DEFINE_EVENT(wil6210_log_event, wil6210_log_info,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DEFINE_EVENT(wil6210_log_event, wil6210_log_dbg,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
#define wil_pseudo_irq_cause(x) __print_flags(x, "|", \
{BIT_DMA_PSEUDO_CAUSE_RX, "Rx" }, \
{BIT_DMA_PSEUDO_CAUSE_TX, "Tx" }, \
{BIT_DMA_PSEUDO_CAUSE_MISC, "Misc" })
TRACE_EVENT(wil6210_irq_pseudo,
TP_PROTO(u32 x),
TP_ARGS(x),
TP_STRUCT__entry(
__field(u32, x)
),
TP_fast_assign(
__entry->x = x;
),
TP_printk("cause 0x%08x : %s", __entry->x,
wil_pseudo_irq_cause(__entry->x))
);
DECLARE_EVENT_CLASS(wil6210_irq,
TP_PROTO(u32 x),
TP_ARGS(x),
TP_STRUCT__entry(
__field(u32, x)
),
TP_fast_assign(
__entry->x = x;
),
TP_printk("cause 0x%08x", __entry->x)
);
DEFINE_EVENT(wil6210_irq, wil6210_irq_rx,
TP_PROTO(u32 x),
TP_ARGS(x)
);
DEFINE_EVENT(wil6210_irq, wil6210_irq_tx,
TP_PROTO(u32 x),
TP_ARGS(x)
);
DEFINE_EVENT(wil6210_irq, wil6210_irq_misc,
TP_PROTO(u32 x),
TP_ARGS(x)
);
DEFINE_EVENT(wil6210_irq, wil6210_irq_misc_thread,
TP_PROTO(u32 x),
TP_ARGS(x)
);
TRACE_EVENT(wil6210_rx,
TP_PROTO(u16 index, struct vring_rx_desc *d),
TP_ARGS(index, d),
TP_STRUCT__entry(
__field(u16, index)
__field(unsigned int, len)
__field(u8, mid)
__field(u8, cid)
__field(u8, tid)
__field(u8, type)
__field(u8, subtype)
__field(u16, seq)
__field(u8, mcs)
),
TP_fast_assign(
__entry->index = index;
__entry->len = d->dma.length;
__entry->mid = wil_rxdesc_mid(d);
__entry->cid = wil_rxdesc_cid(d);
__entry->tid = wil_rxdesc_tid(d);
__entry->type = wil_rxdesc_ftype(d);
__entry->subtype = wil_rxdesc_subtype(d);
__entry->seq = wil_rxdesc_seq(d);
__entry->mcs = wil_rxdesc_mcs(d);
),
TP_printk("index %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x"
" type 0x%1x subtype 0x%1x", __entry->index, __entry->len,
__entry->mid, __entry->cid, __entry->tid, __entry->mcs,
__entry->seq, __entry->type, __entry->subtype)
);
TRACE_EVENT(wil6210_tx,
TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags),
TP_ARGS(vring, index, len, frags),
TP_STRUCT__entry(
__field(u8, vring)
__field(u8, frags)
__field(u16, index)
__field(unsigned int, len)
),
TP_fast_assign(
__entry->vring = vring;
__entry->frags = frags;
__entry->index = index;
__entry->len = len;
),
TP_printk("vring %d index %d len %d frags %d",
__entry->vring, __entry->index, __entry->len, __entry->frags)
);
TRACE_EVENT(wil6210_tx_done,
TP_PROTO(u8 vring, u16 index, unsigned int len, u8 err),
TP_ARGS(vring, index, len, err),
TP_STRUCT__entry(
__field(u8, vring)
__field(u8, err)
__field(u16, index)
__field(unsigned int, len)
),
TP_fast_assign(
__entry->vring = vring;
__entry->index = index;
__entry->len = len;
__entry->err = err;
),
TP_printk("vring %d index %d len %d err 0x%02x",
__entry->vring, __entry->index, __entry->len,
__entry->err)
);
#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/
#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__)
/* we don't want to use include/trace/events */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
/* This part must be outside protection */
#include <trace/define_trace.h>
#endif /* defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) */

View File

@ -22,6 +22,7 @@
#include "wil6210.h" #include "wil6210.h"
#include "wmi.h" #include "wmi.h"
#include "txrx.h" #include "txrx.h"
#include "trace.h"
static bool rtap_include_phy_info; static bool rtap_include_phy_info;
module_param(rtap_include_phy_info, bool, S_IRUGO); module_param(rtap_include_phy_info, bool, S_IRUGO);
@ -89,8 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
* we can use any * we can use any
*/ */
for (i = 0; i < vring->size; i++) { for (i = 0; i < vring->size; i++) {
volatile struct vring_tx_desc *d = &(vring->va[i].tx); volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
d->dma.status = TX_DMA_STATUS_DU; _d->dma.status = TX_DMA_STATUS_DU;
} }
wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
@ -106,30 +107,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
size_t sz = vring->size * sizeof(vring->va[0]); size_t sz = vring->size * sizeof(vring->va[0]);
while (!wil_vring_is_empty(vring)) { while (!wil_vring_is_empty(vring)) {
dma_addr_t pa;
struct sk_buff *skb;
u16 dmalen;
if (tx) { if (tx) {
volatile struct vring_tx_desc *d = struct vring_tx_desc dd, *d = &dd;
volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx; &vring->va[vring->swtail].tx;
dma_addr_t pa = d->dma.addr_low |
((u64)d->dma.addr_high << 32); *d = *_d;
struct sk_buff *skb = vring->ctx[vring->swtail]; pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
skb = vring->ctx[vring->swtail];
if (skb) { if (skb) {
dma_unmap_single(dev, pa, d->dma.length, dma_unmap_single(dev, pa, dmalen,
DMA_TO_DEVICE); DMA_TO_DEVICE);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL; vring->ctx[vring->swtail] = NULL;
} else { } else {
dma_unmap_page(dev, pa, d->dma.length, dma_unmap_page(dev, pa, dmalen,
DMA_TO_DEVICE); DMA_TO_DEVICE);
} }
vring->swtail = wil_vring_next_tail(vring); vring->swtail = wil_vring_next_tail(vring);
} else { /* rx */ } else { /* rx */
volatile struct vring_rx_desc *d = struct vring_rx_desc dd, *d = &dd;
volatile struct vring_rx_desc *_d =
&vring->va[vring->swtail].rx; &vring->va[vring->swtail].rx;
dma_addr_t pa = d->dma.addr_low |
((u64)d->dma.addr_high << 32); *d = *_d;
struct sk_buff *skb = vring->ctx[vring->swhead]; pa = wil_desc_addr(&d->dma.addr);
dma_unmap_single(dev, pa, d->dma.length, dmalen = le16_to_cpu(d->dma.length);
DMA_FROM_DEVICE); skb = vring->ctx[vring->swhead];
dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
kfree_skb(skb); kfree_skb(skb);
wil_vring_advance_head(vring, 1); wil_vring_advance_head(vring, 1);
} }
@ -151,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
{ {
struct device *dev = wil_to_dev(wil); struct device *dev = wil_to_dev(wil);
unsigned int sz = RX_BUF_LEN; unsigned int sz = RX_BUF_LEN;
volatile struct vring_rx_desc *d = &(vring->va[i].rx); struct vring_rx_desc dd, *d = &dd;
volatile struct vring_rx_desc *_d = &(vring->va[i].rx);
dma_addr_t pa; dma_addr_t pa;
/* TODO align */ /* TODO align */
@ -169,13 +180,13 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
} }
d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
d->dma.addr_low = lower_32_bits(pa); wil_desc_addr_set(&d->dma.addr, pa);
d->dma.addr_high = (u16)upper_32_bits(pa);
/* ip_length don't care */ /* ip_length don't care */
/* b11 don't care */ /* b11 don't care */
/* error don't care */ /* error don't care */
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
d->dma.length = sz; d->dma.length = cpu_to_le16(sz);
*_d = *d;
vring->ctx[i] = skb; vring->ctx[i] = skb;
return 0; return 0;
@ -321,11 +332,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
{ {
struct device *dev = wil_to_dev(wil); struct device *dev = wil_to_dev(wil);
struct net_device *ndev = wil_to_ndev(wil); struct net_device *ndev = wil_to_ndev(wil);
volatile struct vring_rx_desc *d; volatile struct vring_rx_desc *_d;
struct vring_rx_desc *d1; struct vring_rx_desc *d;
struct sk_buff *skb; struct sk_buff *skb;
dma_addr_t pa; dma_addr_t pa;
unsigned int sz = RX_BUF_LEN; unsigned int sz = RX_BUF_LEN;
u16 dmalen;
u8 ftype; u8 ftype;
u8 ds_bits; u8 ds_bits;
@ -334,32 +346,44 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
if (wil_vring_is_empty(vring)) if (wil_vring_is_empty(vring))
return NULL; return NULL;
d = &(vring->va[vring->swhead].rx); _d = &(vring->va[vring->swhead].rx);
if (!(d->dma.status & RX_DMA_STATUS_DU)) { if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
/* it is not error, we just reached end of Rx done area */ /* it is not error, we just reached end of Rx done area */
return NULL; return NULL;
} }
pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
skb = vring->ctx[vring->swhead]; skb = vring->ctx[vring->swhead];
d = wil_skb_rxdesc(skb);
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
vring->ctx[vring->swhead] = NULL;
wil_vring_advance_head(vring, 1);
dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
skb_trim(skb, d->dma.length); dmalen = le16_to_cpu(d->dma.length);
d1 = wil_skb_rxdesc(skb); trace_wil6210_rx(vring->swhead, d);
*d1 = *d; wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1); if (dmalen > sz) {
wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
kfree_skb(skb);
return NULL;
}
skb_trim(skb, dmalen);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
wil->stats.last_mcs_rx = wil_rxdesc_mcs(d);
/* use radiotap header only if required */ /* use radiotap header only if required */
if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
wil_rx_add_radiotap_header(wil, skb); wil_rx_add_radiotap_header(wil, skb);
wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
wil_vring_advance_head(vring, 1);
/* no extra checks if in sniffer mode */ /* no extra checks if in sniffer mode */
if (ndev->type != ARPHRD_ETHER) if (ndev->type != ARPHRD_ETHER)
return skb; return skb;
@ -368,7 +392,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
* Driver should recognize it by frame type, that is found * Driver should recognize it by frame type, that is found
* in Rx descriptor. If type is not data, it is 802.11 frame as is * in Rx descriptor. If type is not data, it is 802.11 frame as is
*/ */
ftype = wil_rxdesc_ftype(d1) << 2; ftype = wil_rxdesc_ftype(d) << 2;
if (ftype != IEEE80211_FTYPE_DATA) { if (ftype != IEEE80211_FTYPE_DATA) {
wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype); wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
/* TODO: process it */ /* TODO: process it */
@ -383,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
return NULL; return NULL;
} }
ds_bits = wil_rxdesc_ds_bits(d1); ds_bits = wil_rxdesc_ds_bits(d);
if (ds_bits == 1) { if (ds_bits == 1) {
/* /*
* HW bug - in ToDS mode, i.e. Rx on AP side, * HW bug - in ToDS mode, i.e. Rx on AP side,
@ -425,6 +449,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
/* /*
* Pass Rx packet to the netif. Update statistics. * Pass Rx packet to the netif. Update statistics.
* Called in softirq context (NAPI poll).
*/ */
static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{ {
@ -433,10 +458,7 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
skb_orphan(skb); skb_orphan(skb);
if (in_interrupt()) rc = netif_receive_skb(skb);
rc = netif_rx(skb);
else
rc = netif_rx_ni(skb);
if (likely(rc == NET_RX_SUCCESS)) { if (likely(rc == NET_RX_SUCCESS)) {
ndev->stats.rx_packets++; ndev->stats.rx_packets++;
@ -450,9 +472,9 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
/** /**
* Proceed all completed skb's from Rx VRING * Proceed all completed skb's from Rx VRING
* *
* Safe to call from IRQ * Safe to call from NAPI poll, i.e. softirq with interrupts enabled
*/ */
void wil_rx_handle(struct wil6210_priv *wil) void wil_rx_handle(struct wil6210_priv *wil, int *quota)
{ {
struct net_device *ndev = wil_to_ndev(wil); struct net_device *ndev = wil_to_ndev(wil);
struct vring *v = &wil->vring_rx; struct vring *v = &wil->vring_rx;
@ -463,9 +485,8 @@ void wil_rx_handle(struct wil6210_priv *wil)
return; return;
} }
wil_dbg_txrx(wil, "%s()\n", __func__); wil_dbg_txrx(wil, "%s()\n", __func__);
while (NULL != (skb = wil_vring_reap_rx(wil, v))) { while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, (*quota)--;
skb->data, skb_headlen(skb), false);
if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
skb->dev = ndev; skb->dev = ndev;
@ -600,17 +621,15 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
return NULL; return NULL;
} }
static int wil_tx_desc_map(volatile struct vring_tx_desc *d, static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len)
dma_addr_t pa, u32 len)
{ {
d->dma.addr_low = lower_32_bits(pa); wil_desc_addr_set(&d->dma.addr, pa);
d->dma.addr_high = (u16)upper_32_bits(pa);
d->dma.ip_length = 0; d->dma.ip_length = 0;
/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
d->dma.b11 = 0/*14 | BIT(7)*/; d->dma.b11 = 0/*14 | BIT(7)*/;
d->dma.error = 0; d->dma.error = 0;
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
d->dma.length = len; d->dma.length = cpu_to_le16((u16)len);
d->dma.d0 = 0; d->dma.d0 = 0;
d->mac.d[0] = 0; d->mac.d[0] = 0;
d->mac.d[1] = 0; d->mac.d[1] = 0;
@ -630,7 +649,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct device *dev = wil_to_dev(wil); struct device *dev = wil_to_dev(wil);
volatile struct vring_tx_desc *d; struct vring_tx_desc dd, *d = &dd;
volatile struct vring_tx_desc *_d;
u32 swhead = vring->swhead; u32 swhead = vring->swhead;
int avail = wil_vring_avail_tx(vring); int avail = wil_vring_avail_tx(vring);
int nr_frags = skb_shinfo(skb)->nr_frags; int nr_frags = skb_shinfo(skb)->nr_frags;
@ -648,7 +668,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
1 + nr_frags); 1 + nr_frags);
return -ENOMEM; return -ENOMEM;
} }
d = &(vring->va[i].tx); _d = &(vring->va[i].tx);
/* FIXME FW can accept only unicast frames for the peer */ /* FIXME FW can accept only unicast frames for the peer */
memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
@ -667,25 +687,30 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_tx_desc_map(d, pa, skb_headlen(skb)); wil_tx_desc_map(d, pa, skb_headlen(skb));
d->mac.d[2] |= ((nr_frags + 1) << d->mac.d[2] |= ((nr_frags + 1) <<
MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
if (nr_frags)
*_d = *d;
/* middle segments */ /* middle segments */
for (f = 0; f < nr_frags; f++) { for (f = 0; f < nr_frags; f++) {
const struct skb_frag_struct *frag = const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f]; &skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag); int len = skb_frag_size(frag);
i = (swhead + f + 1) % vring->size; i = (swhead + f + 1) % vring->size;
d = &(vring->va[i].tx); _d = &(vring->va[i].tx);
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa))) if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error; goto dma_error;
wil_tx_desc_map(d, pa, len); wil_tx_desc_map(d, pa, len);
vring->ctx[i] = NULL; vring->ctx[i] = NULL;
*_d = *d;
} }
/* for the last seg only */ /* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ d->dma.d0 |= BIT(9); /* BUG: undocumented bit */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
*_d = *d;
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4, wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false); (const void *)d, sizeof(*d), false);
@ -693,6 +718,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* advance swhead */ /* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1); wil_vring_advance_head(vring, nr_frags + 1);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
/* hold reference to skb /* hold reference to skb
* to prevent skb release before accounting * to prevent skb release before accounting
@ -705,14 +731,18 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* unmap what we have mapped */ /* unmap what we have mapped */
/* Note: increment @f to operate with positive index */ /* Note: increment @f to operate with positive index */
for (f++; f > 0; f--) { for (f++; f > 0; f--) {
u16 dmalen;
i = (swhead + f) % vring->size; i = (swhead + f) % vring->size;
d = &(vring->va[i].tx); _d = &(vring->va[i].tx);
d->dma.status = TX_DMA_STATUS_DU; *d = *_d;
pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); _d->dma.status = TX_DMA_STATUS_DU;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
if (vring->ctx[i]) if (vring->ctx[i])
dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
else else
dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
} }
return -EINVAL; return -EINVAL;
@ -761,7 +791,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
break; /* goto drop; */ break; /* goto drop; */
} }
drop: drop:
netif_tx_stop_all_queues(ndev);
ndev->stats.tx_dropped++; ndev->stats.tx_dropped++;
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
@ -771,41 +800,48 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/** /**
* Clean up transmitted skb's from the Tx VRING * Clean up transmitted skb's from the Tx VRING
* *
* Return number of descriptors cleared
*
* Safe to call from IRQ * Safe to call from IRQ
*/ */
void wil_tx_complete(struct wil6210_priv *wil, int ringid) int wil_tx_complete(struct wil6210_priv *wil, int ringid)
{ {
struct net_device *ndev = wil_to_ndev(wil); struct net_device *ndev = wil_to_ndev(wil);
struct device *dev = wil_to_dev(wil); struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid]; struct vring *vring = &wil->vring_tx[ringid];
int done = 0;
if (!vring->va) { if (!vring->va) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
return; return 0;
} }
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid); wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) { while (!wil_vring_is_empty(vring)) {
volatile struct vring_tx_desc *d1 = volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx; &vring->va[vring->swtail].tx;
struct vring_tx_desc dd, *d = &dd; struct vring_tx_desc dd, *d = &dd;
dma_addr_t pa; dma_addr_t pa;
struct sk_buff *skb; struct sk_buff *skb;
u16 dmalen;
dd = *d1; *d = *_d;
if (!(d->dma.status & TX_DMA_STATUS_DU)) if (!(d->dma.status & TX_DMA_STATUS_DU))
break; break;
dmalen = le16_to_cpu(d->dma.length);
trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
d->dma.error);
wil_dbg_txrx(wil, wil_dbg_txrx(wil,
"Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
vring->swtail, d->dma.length, d->dma.status, vring->swtail, dmalen, d->dma.status,
d->dma.error); d->dma.error);
wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false); (const void *)d, sizeof(*d), false);
pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); pa = wil_desc_addr(&d->dma.addr);
skb = vring->ctx[vring->swtail]; skb = vring->ctx[vring->swtail];
if (skb) { if (skb) {
if (d->dma.error == 0) { if (d->dma.error == 0) {
@ -815,18 +851,21 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
ndev->stats.tx_errors++; ndev->stats.tx_errors++;
} }
dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL; vring->ctx[vring->swtail] = NULL;
} else { } else {
dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
} }
d->dma.addr_low = 0; d->dma.addr.addr_low = 0;
d->dma.addr_high = 0; d->dma.addr.addr_high = 0;
d->dma.length = 0; d->dma.length = 0;
d->dma.status = TX_DMA_STATUS_DU; d->dma.status = TX_DMA_STATUS_DU;
vring->swtail = wil_vring_next_tail(vring); vring->swtail = wil_vring_next_tail(vring);
done++;
} }
if (wil_vring_avail_tx(vring) > vring->size/4) if (wil_vring_avail_tx(vring) > vring->size/4)
netif_tx_wake_all_queues(wil_to_ndev(wil)); netif_tx_wake_all_queues(wil_to_ndev(wil));
return done;
} }

View File

@ -27,6 +27,28 @@
#define WIL6210_RTAP_SIZE (128) #define WIL6210_RTAP_SIZE (128)
/* Tx/Rx path */ /* Tx/Rx path */
/*
* Common representation of physical address in Vring
*/
struct vring_dma_addr {
__le32 addr_low;
__le16 addr_high;
} __packed;
static inline dma_addr_t wil_desc_addr(struct vring_dma_addr *addr)
{
return le32_to_cpu(addr->addr_low) |
((u64)le16_to_cpu(addr->addr_high) << 32);
}
static inline void wil_desc_addr_set(struct vring_dma_addr *addr,
dma_addr_t pa)
{
addr->addr_low = cpu_to_le32(lower_32_bits(pa));
addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa));
}
/* /*
* Tx descriptor - MAC part * Tx descriptor - MAC part
* [dword 0] * [dword 0]
@ -216,13 +238,12 @@ struct vring_tx_mac {
struct vring_tx_dma { struct vring_tx_dma {
u32 d0; u32 d0;
u32 addr_low; struct vring_dma_addr addr;
u16 addr_high;
u8 ip_length; u8 ip_length;
u8 b11; /* 0..6: mac_length; 7:ip_version */ u8 b11; /* 0..6: mac_length; 7:ip_version */
u8 error; /* 0..2: err; 3..7: reserved; */ u8 error; /* 0..2: err; 3..7: reserved; */
u8 status; /* 0: used; 1..7; reserved */ u8 status; /* 0: used; 1..7; reserved */
u16 length; __le16 length;
} __packed; } __packed;
/* /*
@ -315,13 +336,12 @@ struct vring_rx_mac {
struct vring_rx_dma { struct vring_rx_dma {
u32 d0; u32 d0;
u32 addr_low; struct vring_dma_addr addr;
u16 addr_high;
u8 ip_length; u8 ip_length;
u8 b11; u8 b11;
u8 error; u8 error;
u8 status; u8 status;
u16 length; __le16 length;
} __packed; } __packed;
struct vring_tx_desc { struct vring_tx_desc {

View File

@ -34,9 +34,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
#define WIL6210_MEM_SIZE (2*1024*1024UL) #define WIL6210_MEM_SIZE (2*1024*1024UL)
#define WIL6210_RX_RING_SIZE (128) #define WIL6210_RX_RING_SIZE (128)
#define WIL6210_TX_RING_SIZE (128) #define WIL6210_TX_RING_SIZE (128)
#define WIL6210_MAX_TX_RINGS (24) #define WIL6210_MAX_TX_RINGS (24) /* HW limit */
#define WIL6210_MAX_CID (8) /* HW limit */
#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
/* Hardware definitions begin */ /* Hardware definitions begin */
@ -184,6 +186,7 @@ struct vring {
enum { /* for wil6210_priv.status */ enum { /* for wil6210_priv.status */
wil_status_fwready = 0, wil_status_fwready = 0,
wil_status_fwconnecting,
wil_status_fwconnected, wil_status_fwconnected,
wil_status_dontscan, wil_status_dontscan,
wil_status_reset_done, wil_status_reset_done,
@ -239,6 +242,8 @@ struct wil6210_priv {
* - consumed in thread by wmi_event_worker * - consumed in thread by wmi_event_worker
*/ */
spinlock_t wmi_ev_lock; spinlock_t wmi_ev_lock;
struct napi_struct napi_rx;
struct napi_struct napi_tx;
/* DMA related */ /* DMA related */
struct vring vring_rx; struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS]; struct vring vring_tx[WIL6210_MAX_TX_RINGS];
@ -267,9 +272,13 @@ struct wil6210_priv {
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) #define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg(wil, fmt, arg...) do { \
netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
wil_dbg_trace(wil, fmt, ##arg); \
} while (0)
#define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) #define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
#define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) #define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
@ -356,10 +365,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
void wil_vring_fini_tx(struct wil6210_priv *wil, int id); void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
void wil_tx_complete(struct wil6210_priv *wil, int ringid); int wil_tx_complete(struct wil6210_priv *wil, int ringid);
void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
/* RX API */ /* RX API */
void wil_rx_handle(struct wil6210_priv *wil); void wil_rx_handle(struct wil6210_priv *wil, int *quota);
void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type); int wil_iftype_nl2wmi(enum nl80211_iftype type);

View File

@ -20,6 +20,7 @@
#include "wil6210.h" #include "wil6210.h"
#include "txrx.h" #include "txrx.h"
#include "wmi.h" #include "wmi.h"
#include "trace.h"
/** /**
* WMI event receiving - theory of operations * WMI event receiving - theory of operations
@ -246,6 +247,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
iowrite32(r->head = next_head, wil->csr + HOST_MBOX + iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.head)); offsetof(struct wil6210_mbox_ctl, tx.head));
trace_wil6210_wmi_cmd(cmdid, buf, len);
/* interrupt to FW */ /* interrupt to FW */
iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
@ -406,7 +409,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
if ((wdev->iftype == NL80211_IFTYPE_STATION) || if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
if (wdev->sme_state != CFG80211_SME_CONNECTING) { if (!test_bit(wil_status_fwconnecting, &wil->status)) {
wil_err(wil, "Not in connecting state\n"); wil_err(wil, "Not in connecting state\n");
return; return;
} }
@ -430,6 +433,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
} }
clear_bit(wil_status_fwconnecting, &wil->status);
set_bit(wil_status_fwconnected, &wil->status); set_bit(wil_status_fwconnected, &wil->status);
/* FIXME FW can transmit only ucast frames to peer */ /* FIXME FW can transmit only ucast frames to peer */
@ -635,8 +639,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
hdr.flags); hdr.flags);
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) { (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
wil_dbg_wmi(wil, "WMI event 0x%04x\n", u16 id = le16_to_cpu(evt->event.wmi.id);
evt->event.wmi.id); wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
trace_wil6210_wmi_event(id, &evt->event.wmi, len);
} }
wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1, wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
&evt->event.hdr, sizeof(hdr) + len, true); &evt->event.hdr, sizeof(hdr) + len, true);
@ -724,7 +729,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
.bcon_interval = cpu_to_le16(bi), .bcon_interval = cpu_to_le16(bi),
.network_type = wmi_nettype, .network_type = wmi_nettype,
.disable_sec_offload = 1, .disable_sec_offload = 1,
.channel = chan, .channel = chan - 1,
}; };
struct { struct {
struct wil6210_mbox_hdr_wmi wmi; struct wil6210_mbox_hdr_wmi wmi;

View File

@ -606,7 +606,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev)
static struct platform_driver brcmf_sdio_pd = { static struct platform_driver brcmf_sdio_pd = {
.remove = brcmf_sdio_pd_remove, .remove = brcmf_sdio_pd_remove,
.driver = { .driver = {
.name = BRCMFMAC_SDIO_PDATA_NAME .name = BRCMFMAC_SDIO_PDATA_NAME,
.owner = THIS_MODULE,
} }
}; };

View File

@ -900,7 +900,7 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
if (supr_status) { if (supr_status) {
update_rate = false; update_rate = false;
if (supr_status == TX_STATUS_SUPR_BADCH) { if (supr_status == TX_STATUS_SUPR_BADCH) {
brcms_err(wlc->hw->d11core, brcms_dbg_ht(wlc->hw->d11core,
"%s: Pkt tx suppressed, illegal channel possibly %d\n", "%s: Pkt tx suppressed, illegal channel possibly %d\n",
__func__, CHSPEC_CHANNEL( __func__, CHSPEC_CHANNEL(
wlc->default_bss->chanspec)); wlc->default_bss->chanspec));

View File

@ -0,0 +1,30 @@
config CW1200
tristate "CW1200 WLAN support"
depends on MAC80211 && CFG80211
help
This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets.
This option just enables the driver core, see below for
specific bus support.
if CW1200
config CW1200_WLAN_SDIO
tristate "Support SDIO platforms"
depends on CW1200 && MMC
help
Enable support for the CW1200 connected via an SDIO bus.
By default this driver only supports the Sagrad SG901-1091/1098 EVK
and similar designs that utilize a hardware reset circuit. To
support different CW1200 SDIO designs you will need to override
the default platform data by calling cw1200_sdio_set_platform_data()
in your board setup file.
config CW1200_WLAN_SPI
tristate "Support SPI platforms"
depends on CW1200 && SPI
help
Enables support for the CW1200 connected via a SPI bus. You will
need to add appropriate platform data glue in your board setup
file.
endif

View File

@ -0,0 +1,21 @@
cw1200_core-y := \
fwio.o \
txrx.o \
main.o \
queue.o \
hwio.o \
bh.o \
wsm.o \
sta.o \
scan.o \
debug.o
cw1200_core-$(CONFIG_PM) += pm.o
# CFLAGS_sta.o += -DDEBUG
cw1200_wlan_sdio-y := cw1200_sdio.o
cw1200_wlan_spi-y := cw1200_spi.o
obj-$(CONFIG_CW1200) += cw1200_core.o
obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o
obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o

View File

@ -0,0 +1,619 @@
/*
* Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver, which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <net/mac80211.h>
#include <linux/kthread.h>
#include <linux/timer.h>
#include "cw1200.h"
#include "bh.h"
#include "hwio.h"
#include "wsm.h"
#include "hwbus.h"
#include "debug.h"
#include "fwio.h"
static int cw1200_bh(void *arg);
#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4)
/* an SPI message cannot be bigger than (2"12-1)*2 bytes
* "*2" to cvt to bytes
*/
#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2)
#define PIGGYBACK_CTRL_REG (2)
#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
/* Suspend state privates */
enum cw1200_bh_pm_state {
CW1200_BH_RESUMED = 0,
CW1200_BH_SUSPEND,
CW1200_BH_SUSPENDED,
CW1200_BH_RESUME,
};
typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
u8 *data, size_t size);
static void cw1200_bh_work(struct work_struct *work)
{
struct cw1200_common *priv =
container_of(work, struct cw1200_common, bh_work);
cw1200_bh(priv);
}
int cw1200_register_bh(struct cw1200_common *priv)
{
int err = 0;
/* Realtime workqueue */
priv->bh_workqueue = alloc_workqueue("cw1200_bh",
WQ_MEM_RECLAIM | WQ_HIGHPRI
| WQ_CPU_INTENSIVE, 1);
if (!priv->bh_workqueue)
return -ENOMEM;
INIT_WORK(&priv->bh_work, cw1200_bh_work);
pr_debug("[BH] register.\n");
atomic_set(&priv->bh_rx, 0);
atomic_set(&priv->bh_tx, 0);
atomic_set(&priv->bh_term, 0);
atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
priv->bh_error = 0;
priv->hw_bufs_used = 0;
priv->buf_id_tx = 0;
priv->buf_id_rx = 0;
init_waitqueue_head(&priv->bh_wq);
init_waitqueue_head(&priv->bh_evt_wq);
err = !queue_work(priv->bh_workqueue, &priv->bh_work);
WARN_ON(err);
return err;
}
void cw1200_unregister_bh(struct cw1200_common *priv)
{
atomic_add(1, &priv->bh_term);
wake_up(&priv->bh_wq);
flush_workqueue(priv->bh_workqueue);
destroy_workqueue(priv->bh_workqueue);
priv->bh_workqueue = NULL;
pr_debug("[BH] unregistered.\n");
}
void cw1200_irq_handler(struct cw1200_common *priv)
{
pr_debug("[BH] irq.\n");
/* Disable Interrupts! */
/* NOTE: hwbus_ops->lock already held */
__cw1200_irq_enable(priv, 0);
if (/* WARN_ON */(priv->bh_error))
return;
if (atomic_add_return(1, &priv->bh_rx) == 1)
wake_up(&priv->bh_wq);
}
EXPORT_SYMBOL_GPL(cw1200_irq_handler);
void cw1200_bh_wakeup(struct cw1200_common *priv)
{
pr_debug("[BH] wakeup.\n");
if (priv->bh_error) {
pr_err("[BH] wakeup failed (BH error)\n");
return;
}
if (atomic_add_return(1, &priv->bh_tx) == 1)
wake_up(&priv->bh_wq);
}
int cw1200_bh_suspend(struct cw1200_common *priv)
{
pr_debug("[BH] suspend.\n");
if (priv->bh_error) {
wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n");
return -EINVAL;
}
atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
wake_up(&priv->bh_wq);
return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
(CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)),
1 * HZ) ? 0 : -ETIMEDOUT;
}
int cw1200_bh_resume(struct cw1200_common *priv)
{
pr_debug("[BH] resume.\n");
if (priv->bh_error) {
wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n");
return -EINVAL;
}
atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
wake_up(&priv->bh_wq);
return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
(CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)),
1 * HZ) ? 0 : -ETIMEDOUT;
}
static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
{
++priv->hw_bufs_used;
}
int wsm_release_tx_buffer(struct cw1200_common *priv, int count)
{
int ret = 0;
int hw_bufs_used = priv->hw_bufs_used;
priv->hw_bufs_used -= count;
if (WARN_ON(priv->hw_bufs_used < 0))
ret = -1;
else if (hw_bufs_used >= priv->wsm_caps.input_buffers)
ret = 1;
if (!priv->hw_bufs_used)
wake_up(&priv->bh_evt_wq);
return ret;
}
static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv,
u16 *ctrl_reg)
{
int ret;
ret = cw1200_reg_read_16(priv,
ST90TDS_CONTROL_REG_ID, ctrl_reg);
if (ret) {
ret = cw1200_reg_read_16(priv,
ST90TDS_CONTROL_REG_ID, ctrl_reg);
if (ret)
pr_err("[BH] Failed to read control register.\n");
}
return ret;
}
static int cw1200_device_wakeup(struct cw1200_common *priv)
{
u16 ctrl_reg;
int ret;
pr_debug("[BH] Device wakeup.\n");
/* First, set the dpll register */
ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
cw1200_dpll_from_clk(priv->hw_refclk));
if (WARN_ON(ret))
return ret;
/* To force the device to be always-on, the host sets WLAN_UP to 1 */
ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
ST90TDS_CONT_WUP_BIT);
if (WARN_ON(ret))
return ret;
ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg);
if (WARN_ON(ret))
return ret;
/* If the device returns WLAN_RDY as 1, the device is active and will
* remain active.
*/
if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
pr_debug("[BH] Device awake.\n");
return 1;
}
return 0;
}
/* Must be called from BH thraed. */
void cw1200_enable_powersave(struct cw1200_common *priv,
bool enable)
{
pr_debug("[BH] Powerave is %s.\n",
enable ? "enabled" : "disabled");
priv->powersave_enabled = enable;
}
static int cw1200_bh_rx_helper(struct cw1200_common *priv,
uint16_t *ctrl_reg,
int *tx)
{
size_t read_len = 0;
struct sk_buff *skb_rx = NULL;
struct wsm_hdr *wsm;
size_t wsm_len;
u16 wsm_id;
u8 wsm_seq;
int rx_resync = 1;
size_t alloc_len;
u8 *data;
read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
if (!read_len)
return 0; /* No more work */
if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
(read_len > EFFECTIVE_BUF_SIZE))) {
pr_debug("Invalid read len: %zu (%04x)",
read_len, *ctrl_reg);
goto err;
}
/* Add SIZE of PIGGYBACK reg (CONTROL Reg)
* to the NEXT Message length + 2 Bytes for SKB
*/
read_len = read_len + 2;
alloc_len = priv->hwbus_ops->align_size(
priv->hwbus_priv, read_len);
/* Check if not exceeding CW1200 capabilities */
if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
pr_debug("Read aligned len: %zu\n",
alloc_len);
}
skb_rx = dev_alloc_skb(alloc_len);
if (WARN_ON(!skb_rx))
goto err;
skb_trim(skb_rx, 0);
skb_put(skb_rx, read_len);
data = skb_rx->data;
if (WARN_ON(!data))
goto err;
if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) {
pr_err("rx blew up, len %zu\n", alloc_len);
goto err;
}
/* Piggyback */
*ctrl_reg = __le16_to_cpu(
((__le16 *)data)[alloc_len / 2 - 1]);
wsm = (struct wsm_hdr *)data;
wsm_len = __le16_to_cpu(wsm->len);
if (WARN_ON(wsm_len > read_len))
goto err;
if (priv->wsm_enable_wsm_dumps)
print_hex_dump_bytes("<-- ",
DUMP_PREFIX_NONE,
data, wsm_len);
wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
skb_trim(skb_rx, wsm_len);
if (wsm_id == 0x0800) {
wsm_handle_exception(priv,
&data[sizeof(*wsm)],
wsm_len - sizeof(*wsm));
goto err;
} else if (!rx_resync) {
if (WARN_ON(wsm_seq != priv->wsm_rx_seq))
goto err;
}
priv->wsm_rx_seq = (wsm_seq + 1) & 7;
rx_resync = 0;
if (wsm_id & 0x0400) {
int rc = wsm_release_tx_buffer(priv, 1);
if (WARN_ON(rc < 0))
return rc;
else if (rc > 0)
*tx = 1;
}
/* cw1200_wsm_rx takes care on SKB livetime */
if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx)))
goto err;
if (skb_rx) {
dev_kfree_skb(skb_rx);
skb_rx = NULL;
}
return 0;
err:
if (skb_rx) {
dev_kfree_skb(skb_rx);
skb_rx = NULL;
}
return -1;
}
static int cw1200_bh_tx_helper(struct cw1200_common *priv,
int *pending_tx,
int *tx_burst)
{
size_t tx_len;
u8 *data;
int ret;
struct wsm_hdr *wsm;
if (priv->device_can_sleep) {
ret = cw1200_device_wakeup(priv);
if (WARN_ON(ret < 0)) { /* Error in wakeup */
*pending_tx = 1;
return 0;
} else if (ret) { /* Woke up */
priv->device_can_sleep = false;
} else { /* Did not awake */
*pending_tx = 1;
return 0;
}
}
wsm_alloc_tx_buffer(priv);
ret = wsm_get_tx(priv, &data, &tx_len, tx_burst);
if (ret <= 0) {
wsm_release_tx_buffer(priv, 1);
if (WARN_ON(ret < 0))
return ret; /* Error */
return 0; /* No work */
}
wsm = (struct wsm_hdr *)data;
BUG_ON(tx_len < sizeof(*wsm));
BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
atomic_add(1, &priv->bh_tx);
tx_len = priv->hwbus_ops->align_size(
priv->hwbus_priv, tx_len);
/* Check if not exceeding CW1200 capabilities */
if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
pr_debug("Write aligned len: %zu\n", tx_len);
wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq));
if (WARN_ON(cw1200_data_write(priv, data, tx_len))) {
pr_err("tx blew up, len %zu\n", tx_len);
wsm_release_tx_buffer(priv, 1);
return -1; /* Error */
}
if (priv->wsm_enable_wsm_dumps)
print_hex_dump_bytes("--> ",
DUMP_PREFIX_NONE,
data,
__le16_to_cpu(wsm->len));
wsm_txed(priv, data);
priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
if (*tx_burst > 1) {
cw1200_debug_tx_burst(priv);
return 1; /* Work remains */
}
return 0;
}
static int cw1200_bh(void *arg)
{
struct cw1200_common *priv = arg;
int rx, tx, term, suspend;
u16 ctrl_reg = 0;
int tx_allowed;
int pending_tx = 0;
int tx_burst;
long status;
u32 dummy;
int ret;
for (;;) {
if (!priv->hw_bufs_used &&
priv->powersave_enabled &&
!priv->device_can_sleep &&
!atomic_read(&priv->recent_scan)) {
status = 1 * HZ;
pr_debug("[BH] Device wakedown. No data.\n");
cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0);
priv->device_can_sleep = true;
} else if (priv->hw_bufs_used) {
/* Interrupt loss detection */
status = 1 * HZ;
} else {
status = MAX_SCHEDULE_TIMEOUT;
}
/* Dummy Read for SDIO retry mechanism*/
if ((priv->hw_type != -1) &&
(atomic_read(&priv->bh_rx) == 0) &&
(atomic_read(&priv->bh_tx) == 0))
cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID,
&dummy, sizeof(dummy));
pr_debug("[BH] waiting ...\n");
status = wait_event_interruptible_timeout(priv->bh_wq, ({
rx = atomic_xchg(&priv->bh_rx, 0);
tx = atomic_xchg(&priv->bh_tx, 0);
term = atomic_xchg(&priv->bh_term, 0);
suspend = pending_tx ?
0 : atomic_read(&priv->bh_suspend);
(rx || tx || term || suspend || priv->bh_error);
}), status);
pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n",
rx, tx, term, suspend, status);
/* Did an error occur? */
if ((status < 0 && status != -ERESTARTSYS) ||
term || priv->bh_error) {
break;
}
if (!status) { /* wait_event timed out */
unsigned long timestamp = jiffies;
long timeout;
int pending = 0;
int i;
/* Check to see if we have any outstanding frames */
if (priv->hw_bufs_used && (!rx || !tx)) {
wiphy_warn(priv->hw->wiphy,
"Missed interrupt? (%d frames outstanding)\n",
priv->hw_bufs_used);
rx = 1;
/* Get a timestamp of "oldest" frame */
for (i = 0; i < 4; ++i)
pending += cw1200_queue_get_xmit_timestamp(
&priv->tx_queue[i],
&timestamp,
priv->pending_frame_id);
/* Check if frame transmission is timed out.
* Add an extra second with respect to possible
* interrupt loss.
*/
timeout = timestamp +
WSM_CMD_LAST_CHANCE_TIMEOUT +
1 * HZ -
jiffies;
/* And terminate BH thread if the frame is "stuck" */
if (pending && timeout < 0) {
wiphy_warn(priv->hw->wiphy,
"Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
priv->hw_bufs_used, pending,
timestamp, jiffies);
break;
}
} else if (!priv->device_can_sleep &&
!atomic_read(&priv->recent_scan)) {
pr_debug("[BH] Device wakedown. Timeout.\n");
cw1200_reg_write_16(priv,
ST90TDS_CONTROL_REG_ID, 0);
priv->device_can_sleep = true;
}
goto done;
} else if (suspend) {
pr_debug("[BH] Device suspend.\n");
if (priv->powersave_enabled) {
pr_debug("[BH] Device wakedown. Suspend.\n");
cw1200_reg_write_16(priv,
ST90TDS_CONTROL_REG_ID, 0);
priv->device_can_sleep = true;
}
atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
wake_up(&priv->bh_evt_wq);
status = wait_event_interruptible(priv->bh_wq,
CW1200_BH_RESUME == atomic_read(&priv->bh_suspend));
if (status < 0) {
wiphy_err(priv->hw->wiphy,
"Failed to wait for resume: %ld.\n",
status);
break;
}
pr_debug("[BH] Device resume.\n");
atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
wake_up(&priv->bh_evt_wq);
atomic_add(1, &priv->bh_rx);
goto done;
}
rx:
tx += pending_tx;
pending_tx = 0;
if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
break;
/* Don't bother trying to rx unless we have data to read */
if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
if (ret < 0)
break;
/* Double up here if there's more data.. */
if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
if (ret < 0)
break;
}
}
tx:
if (tx) {
tx = 0;
BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers);
tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used;
tx_allowed = tx_burst > 0;
if (!tx_allowed) {
/* Buffers full. Ensure we process tx
* after we handle rx..
*/
pending_tx = tx;
goto done_rx;
}
ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst);
if (ret < 0)
break;
if (ret > 0) /* More to transmit */
tx = ret;
/* Re-read ctrl reg */
if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
break;
}
done_rx:
if (priv->bh_error)
break;
if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
goto rx;
if (tx)
goto tx;
done:
/* Re-enable device interrupts */
priv->hwbus_ops->lock(priv->hwbus_priv);
__cw1200_irq_enable(priv, 1);
priv->hwbus_ops->unlock(priv->hwbus_priv);
}
/* Explicitly disable device interrupts */
priv->hwbus_ops->lock(priv->hwbus_priv);
__cw1200_irq_enable(priv, 0);
priv->hwbus_ops->unlock(priv->hwbus_priv);
if (!term) {
pr_err("[BH] Fatal error, exiting.\n");
priv->bh_error = 1;
/* TODO: schedule_work(recovery) */
}
return 0;
}

View File

@ -0,0 +1,28 @@
/*
* Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_BH_H
#define CW1200_BH_H
/* extern */ struct cw1200_common;
int cw1200_register_bh(struct cw1200_common *priv);
void cw1200_unregister_bh(struct cw1200_common *priv);
void cw1200_irq_handler(struct cw1200_common *priv);
void cw1200_bh_wakeup(struct cw1200_common *priv);
int cw1200_bh_suspend(struct cw1200_common *priv);
int cw1200_bh_resume(struct cw1200_common *priv);
/* Must be called from BH thread. */
void cw1200_enable_powersave(struct cw1200_common *priv,
bool enable);
int wsm_release_tx_buffer(struct cw1200_common *priv, int count);
#endif /* CW1200_BH_H */

View File

@ -0,0 +1,323 @@
/*
* Common private data for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on the mac80211 Prism54 code, which is
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
*
* Based on the islsm (softmac prism54) driver, which is:
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_H
#define CW1200_H
#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <net/mac80211.h>
#include "queue.h"
#include "wsm.h"
#include "scan.h"
#include "txrx.h"
#include "pm.h"
/* Forward declarations */
struct hwbus_ops;
struct task_struct;
struct cw1200_debug_priv;
struct firmware;
#define CW1200_MAX_CTRL_FRAME_LEN (0x1000)
#define CW1200_MAX_STA_IN_AP_MODE (5)
#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1)
#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2)
#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3)
#define CW1200_MAX_REQUEUE_ATTEMPTS (5)
#define CW1200_MAX_TID (8)
#define CW1200_BLOCK_ACK_CNT (30)
#define CW1200_BLOCK_ACK_THLD (800)
#define CW1200_BLOCK_ACK_HIST (3)
#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST)
#define CW1200_JOIN_TIMEOUT (1 * HZ)
#define CW1200_AUTH_TIMEOUT (5 * HZ)
struct cw1200_ht_info {
struct ieee80211_sta_ht_cap ht_cap;
enum nl80211_channel_type channel_type;
u16 operation_mode;
};
/* Please keep order */
enum cw1200_join_status {
CW1200_JOIN_STATUS_PASSIVE = 0,
CW1200_JOIN_STATUS_MONITOR,
CW1200_JOIN_STATUS_JOINING,
CW1200_JOIN_STATUS_PRE_STA,
CW1200_JOIN_STATUS_STA,
CW1200_JOIN_STATUS_IBSS,
CW1200_JOIN_STATUS_AP,
};
enum cw1200_link_status {
CW1200_LINK_OFF,
CW1200_LINK_RESERVE,
CW1200_LINK_SOFT,
CW1200_LINK_HARD,
CW1200_LINK_RESET,
CW1200_LINK_RESET_REMAP,
};
extern int cw1200_power_mode;
extern const char * const cw1200_fw_types[];
struct cw1200_link_entry {
unsigned long timestamp;
enum cw1200_link_status status;
enum cw1200_link_status prev_status;
u8 mac[ETH_ALEN];
u8 buffered[CW1200_MAX_TID];
struct sk_buff_head rx_queue;
};
struct cw1200_common {
/* interfaces to the rest of the stack */
struct ieee80211_hw *hw;
struct ieee80211_vif *vif;
struct device *pdev;
/* Statistics */
struct ieee80211_low_level_stats stats;
/* Our macaddr */
u8 mac_addr[ETH_ALEN];
/* Hardware interface */
const struct hwbus_ops *hwbus_ops;
struct hwbus_priv *hwbus_priv;
/* Hardware information */
enum {
HIF_9000_SILICON_VERSATILE = 0,
HIF_8601_VERSATILE,
HIF_8601_SILICON,
} hw_type;
enum {
CW1200_HW_REV_CUT10 = 10,
CW1200_HW_REV_CUT11 = 11,
CW1200_HW_REV_CUT20 = 20,
CW1200_HW_REV_CUT22 = 22,
CW1X60_HW_REV = 40,
} hw_revision;
int hw_refclk;
bool hw_have_5ghz;
const struct firmware *sdd;
char *sdd_path;
struct cw1200_debug_priv *debug;
struct workqueue_struct *workqueue;
struct mutex conf_mutex;
struct cw1200_queue tx_queue[4];
struct cw1200_queue_stats tx_queue_stats;
int tx_burst_idx;
/* firmware/hardware info */
unsigned int tx_hdr_len;
/* Radio data */
int output_power;
/* BBP/MAC state */
struct ieee80211_rate *rates;
struct ieee80211_rate *mcs_rates;
struct ieee80211_channel *channel;
struct wsm_edca_params edca;
struct wsm_tx_queue_params tx_queue_params;
struct wsm_mib_association_mode association_mode;
struct wsm_set_bss_params bss_params;
struct cw1200_ht_info ht_info;
struct wsm_set_pm powersave_mode;
struct wsm_set_pm firmware_ps_mode;
int cqm_rssi_thold;
unsigned cqm_rssi_hyst;
bool cqm_use_rssi;
int cqm_beacon_loss_count;
int channel_switch_in_progress;
wait_queue_head_t channel_switch_done;
u8 long_frame_max_tx_count;
u8 short_frame_max_tx_count;
int mode;
bool enable_beacon;
int beacon_int;
bool listening;
struct wsm_rx_filter rx_filter;
struct wsm_mib_multicast_filter multicast_filter;
bool has_multicast_subscription;
bool disable_beacon_filter;
struct work_struct update_filtering_work;
struct work_struct set_beacon_wakeup_period_work;
u8 ba_rx_tid_mask;
u8 ba_tx_tid_mask;
struct cw1200_pm_state pm_state;
struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo;
struct wsm_uapsd_info uapsd_info;
bool setbssparams_done;
bool bt_present;
u8 conf_listen_interval;
u32 listen_interval;
u32 erp_info;
u32 rts_threshold;
/* BH */
atomic_t bh_rx;
atomic_t bh_tx;
atomic_t bh_term;
atomic_t bh_suspend;
struct workqueue_struct *bh_workqueue;
struct work_struct bh_work;
int bh_error;
wait_queue_head_t bh_wq;
wait_queue_head_t bh_evt_wq;
u8 buf_id_tx;
u8 buf_id_rx;
u8 wsm_rx_seq;
u8 wsm_tx_seq;
int hw_bufs_used;
bool powersave_enabled;
bool device_can_sleep;
/* Scan status */
struct cw1200_scan scan;
/* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid
* FW issue with sleeping/waking up.
*/
atomic_t recent_scan;
struct delayed_work clear_recent_scan_work;
/* WSM */
struct wsm_startup_ind wsm_caps;
struct mutex wsm_cmd_mux;
struct wsm_buf wsm_cmd_buf;
struct wsm_cmd wsm_cmd;
wait_queue_head_t wsm_cmd_wq;
wait_queue_head_t wsm_startup_done;
int firmware_ready;
atomic_t tx_lock;
/* WSM debug */
int wsm_enable_wsm_dumps;
/* WSM Join */
enum cw1200_join_status join_status;
u32 pending_frame_id;
bool join_pending;
struct delayed_work join_timeout;
struct work_struct unjoin_work;
struct work_struct join_complete_work;
int join_complete_status;
int join_dtim_period;
bool delayed_unjoin;
/* TX/RX and security */
s8 wep_default_key_id;
struct work_struct wep_key_work;
u32 key_map;
struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1];
/* AP powersave */
u32 link_id_map;
struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE];
struct work_struct link_id_work;
struct delayed_work link_id_gc_work;
u32 sta_asleep_mask;
u32 pspoll_mask;
bool aid0_bit_set;
spinlock_t ps_state_lock; /* Protect power save state */
bool buffered_multicasts;
bool tx_multicast;
struct work_struct set_tim_work;
struct work_struct set_cts_work;
struct work_struct multicast_start_work;
struct work_struct multicast_stop_work;
struct timer_list mcast_timeout;
/* WSM events and CQM implementation */
spinlock_t event_queue_lock; /* Protect event queue */
struct list_head event_queue;
struct work_struct event_handler;
struct delayed_work bss_loss_work;
spinlock_t bss_loss_lock; /* Protect BSS loss state */
int bss_loss_state;
int bss_loss_confirm_id;
int delayed_link_loss;
struct work_struct bss_params_work;
/* TX rate policy cache */
struct tx_policy_cache tx_policy_cache;
struct work_struct tx_policy_upload_work;
/* legacy PS mode switch in suspend */
int ps_mode_switch_in_progress;
wait_queue_head_t ps_mode_switch_done;
/* Workaround for WFD testcase 6.1.10*/
struct work_struct linkid_reset_work;
u8 action_frame_sa[ETH_ALEN];
u8 action_linkid;
};
struct cw1200_sta_priv {
int link_id;
};
/* interfaces for the drivers */
int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
struct hwbus_priv *hwbus,
struct device *pdev,
struct cw1200_common **pself,
int ref_clk, const u8 *macaddr,
const char *sdd_path, bool have_5ghz);
void cw1200_core_release(struct cw1200_common *self);
#define FWLOAD_BLOCK_SIZE (1024)
static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info)
{
return ht_info->channel_type != NL80211_CHAN_NO_HT;
}
static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info)
{
return cw1200_is_ht(ht_info) &&
(ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
!(ht_info->operation_mode &
IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
}
static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info)
{
if (!cw1200_is_ht(ht_info))
return 0;
return ht_info->ht_cap.ampdu_density;
}
#endif /* CW1200_H */

View File

@ -0,0 +1,425 @@
/*
* Mac80211 SDIO driver for ST-Ericsson CW1200 device
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <net/mac80211.h>
#include "cw1200.h"
#include "hwbus.h"
#include <linux/platform_data/net-cw1200.h>
#include "hwio.h"
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
MODULE_LICENSE("GPL");
#define SDIO_BLOCK_SIZE (512)
/* Default platform data for Sagrad modules */
static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = {
.ref_clk = 38400,
.have_5ghz = false,
.sdd_file = "sdd_sagrad_1091_1098.bin",
};
/* Allow platform data to be overridden */
static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data;
void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata)
{
global_plat_data = pdata;
}
struct hwbus_priv {
struct sdio_func *func;
struct cw1200_common *core;
const struct cw1200_platform_data_sdio *pdata;
};
#ifndef SDIO_VENDOR_ID_STE
#define SDIO_VENDOR_ID_STE 0x0020
#endif
#ifndef SDIO_DEVICE_ID_STE_CW1200
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
#endif
static const struct sdio_device_id cw1200_sdio_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
{ /* end: all zeroes */ },
};
/* hwbus_ops implemetation */
static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self,
unsigned int addr,
void *dst, int count)
{
return sdio_memcpy_fromio(self->func, dst, addr, count);
}
static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self,
unsigned int addr,
const void *src, int count)
{
return sdio_memcpy_toio(self->func, addr, (void *)src, count);
}
static void cw1200_sdio_lock(struct hwbus_priv *self)
{
sdio_claim_host(self->func);
}
static void cw1200_sdio_unlock(struct hwbus_priv *self)
{
sdio_release_host(self->func);
}
static void cw1200_sdio_irq_handler(struct sdio_func *func)
{
struct hwbus_priv *self = sdio_get_drvdata(func);
/* note: sdio_host already claimed here. */
if (self->core)
cw1200_irq_handler(self->core);
}
static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
{
return IRQ_WAKE_THREAD;
}
static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
{
struct hwbus_priv *self = dev_id;
if (self->core) {
sdio_claim_host(self->func);
cw1200_irq_handler(self->core);
sdio_release_host(self->func);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
}
static int cw1200_request_irq(struct hwbus_priv *self)
{
int ret;
u8 cccr;
cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
if (WARN_ON(ret))
goto err;
/* Master interrupt enable ... */
cccr |= BIT(0);
/* ... for our function */
cccr |= BIT(self->func->num);
sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
if (WARN_ON(ret))
goto err;
ret = enable_irq_wake(self->pdata->irq);
if (WARN_ON(ret))
goto err;
/* Request the IRQ */
ret = request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq,
cw1200_gpio_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"cw1200_wlan_irq", self);
if (WARN_ON(ret))
goto err;
return 0;
err:
return ret;
}
static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ subscribe\n");
sdio_claim_host(self->func);
if (self->pdata->irq)
ret = cw1200_request_irq(self);
else
ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
sdio_release_host(self->func);
return ret;
}
static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ unsubscribe\n");
if (self->pdata->irq) {
disable_irq_wake(self->pdata->irq);
free_irq(self->pdata->irq, self);
} else {
sdio_claim_host(self->func);
ret = sdio_release_irq(self->func);
sdio_release_host(self->func);
}
return ret;
}
static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
{
if (pdata->reset) {
gpio_set_value(pdata->reset, 0);
msleep(30); /* Min is 2 * CLK32K cycles */
gpio_free(pdata->reset);
}
if (pdata->power_ctrl)
pdata->power_ctrl(pdata, false);
if (pdata->clk_ctrl)
pdata->clk_ctrl(pdata, false);
return 0;
}
static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
{
/* Ensure I/Os are pulled low */
if (pdata->reset) {
gpio_request(pdata->reset, "cw1200_wlan_reset");
gpio_direction_output(pdata->reset, 0);
}
if (pdata->powerup) {
gpio_request(pdata->powerup, "cw1200_wlan_powerup");
gpio_direction_output(pdata->powerup, 0);
}
if (pdata->reset || pdata->powerup)
msleep(10); /* Settle time? */
/* Enable 3v3 and 1v8 to hardware */
if (pdata->power_ctrl) {
if (pdata->power_ctrl(pdata, true)) {
pr_err("power_ctrl() failed!\n");
return -1;
}
}
/* Enable CLK32K */
if (pdata->clk_ctrl) {
if (pdata->clk_ctrl(pdata, true)) {
pr_err("clk_ctrl() failed!\n");
return -1;
}
msleep(10); /* Delay until clock is stable for 2 cycles */
}
/* Enable POWERUP signal */
if (pdata->powerup) {
gpio_set_value(pdata->powerup, 1);
msleep(250); /* or more..? */
}
/* Enable RSTn signal */
if (pdata->reset) {
gpio_set_value(pdata->reset, 1);
msleep(50); /* Or more..? */
}
return 0;
}
static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size)
{
if (self->pdata->no_nptb)
size = round_up(size, SDIO_BLOCK_SIZE);
else
size = sdio_align_size(self->func, size);
return size;
}
static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend)
{
int ret = 0;
if (self->pdata->irq)
ret = irq_set_irq_wake(self->pdata->irq, suspend);
return ret;
}
static struct hwbus_ops cw1200_sdio_hwbus_ops = {
.hwbus_memcpy_fromio = cw1200_sdio_memcpy_fromio,
.hwbus_memcpy_toio = cw1200_sdio_memcpy_toio,
.lock = cw1200_sdio_lock,
.unlock = cw1200_sdio_unlock,
.align_size = cw1200_sdio_align_size,
.power_mgmt = cw1200_sdio_pm,
};
/* Probe Function to be called by SDIO stack when device is discovered */
static int cw1200_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct hwbus_priv *self;
int status;
pr_info("cw1200_wlan_sdio: Probe called\n");
/* We are only able to handle the wlan function */
if (func->num != 0x01)
return -ENODEV;
self = kzalloc(sizeof(*self), GFP_KERNEL);
if (!self) {
pr_err("Can't allocate SDIO hwbus_priv.\n");
return -ENOMEM;
}
func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
self->pdata = global_plat_data; /* FIXME */
self->func = func;
sdio_set_drvdata(func, self);
sdio_claim_host(func);
sdio_enable_func(func);
sdio_release_host(func);
status = cw1200_sdio_irq_subscribe(self);
status = cw1200_core_probe(&cw1200_sdio_hwbus_ops,
self, &func->dev, &self->core,
self->pdata->ref_clk,
self->pdata->macaddr,
self->pdata->sdd_file,
self->pdata->have_5ghz);
if (status) {
cw1200_sdio_irq_unsubscribe(self);
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
sdio_set_drvdata(func, NULL);
kfree(self);
}
return status;
}
/* Disconnect Function to be called by SDIO stack when
* device is disconnected
*/
static void cw1200_sdio_disconnect(struct sdio_func *func)
{
struct hwbus_priv *self = sdio_get_drvdata(func);
if (self) {
cw1200_sdio_irq_unsubscribe(self);
if (self->core) {
cw1200_core_release(self->core);
self->core = NULL;
}
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
sdio_set_drvdata(func, NULL);
kfree(self);
}
}
#ifdef CONFIG_PM
static int cw1200_sdio_suspend(struct device *dev)
{
int ret;
struct sdio_func *func = dev_to_sdio_func(dev);
struct hwbus_priv *self = sdio_get_drvdata(func);
if (!cw1200_can_suspend(self->core))
return -EAGAIN;
/* Notify SDIO that CW1200 will remain powered during suspend */
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret)
pr_err("Error setting SDIO pm flags: %i\n", ret);
return ret;
}
static int cw1200_sdio_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops cw1200_pm_ops = {
.suspend = cw1200_sdio_suspend,
.resume = cw1200_sdio_resume,
};
#endif
static struct sdio_driver sdio_driver = {
.name = "cw1200_wlan_sdio",
.id_table = cw1200_sdio_ids,
.probe = cw1200_sdio_probe,
.remove = cw1200_sdio_disconnect,
#ifdef CONFIG_PM
.drv = {
.pm = &cw1200_pm_ops,
}
#endif
};
/* Init Module function -> Called by insmod */
static int __init cw1200_sdio_init(void)
{
const struct cw1200_platform_data_sdio *pdata;
int ret;
/* FIXME -- this won't support multiple devices */
pdata = global_plat_data;
if (cw1200_sdio_on(pdata)) {
ret = -1;
goto err;
}
ret = sdio_register_driver(&sdio_driver);
if (ret)
goto err;
return 0;
err:
cw1200_sdio_off(pdata);
return ret;
}
/* Called at Driver Unloading */
static void __exit cw1200_sdio_exit(void)
{
const struct cw1200_platform_data_sdio *pdata;
/* FIXME -- this won't support multiple devices */
pdata = global_plat_data;
sdio_unregister_driver(&sdio_driver);
cw1200_sdio_off(pdata);
}
module_init(cw1200_sdio_init);
module_exit(cw1200_sdio_exit);

View File

@ -0,0 +1,463 @@
/*
* Mac80211 SPI driver for ST-Ericsson CW1200 device
*
* Copyright (c) 2011, Sagrad Inc.
* Author: Solomon Peachy <speachy@sagrad.com>
*
* Based on cw1200_sdio.c
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <net/mac80211.h>
#include <linux/spi/spi.h>
#include <linux/device.h>
#include "cw1200.h"
#include "hwbus.h"
#include <linux/platform_data/net-cw1200.h>
#include "hwio.h"
MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:cw1200_wlan_spi");
/* #define SPI_DEBUG */
struct hwbus_priv {
struct spi_device *func;
struct cw1200_common *core;
const struct cw1200_platform_data_spi *pdata;
spinlock_t lock; /* Serialize all bus operations */
int claimed;
};
#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
#define SET_WRITE 0x7FFF /* usage: and operation */
#define SET_READ 0x8000 /* usage: or operation */
/* Notes on byte ordering:
LE: B0 B1 B2 B3
BE: B3 B2 B1 B0
Hardware expects 32-bit data to be written as 16-bit BE words:
B1 B0 B3 B2
*/
static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self,
unsigned int addr,
void *dst, int count)
{
int ret, i;
uint16_t regaddr;
struct spi_message m;
struct spi_transfer t_addr = {
.tx_buf = &regaddr,
.len = sizeof(regaddr),
};
struct spi_transfer t_msg = {
.rx_buf = dst,
.len = count,
};
regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
regaddr |= SET_READ;
regaddr |= (count>>1);
regaddr = cpu_to_le16(regaddr);
#ifdef SPI_DEBUG
pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr,
le16_to_cpu(regaddr));
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
regaddr = swab16(regaddr);
spi_message_init(&m);
spi_message_add_tail(&t_addr, &m);
spi_message_add_tail(&t_msg, &m);
ret = spi_sync(self->func, &m);
#ifdef SPI_DEBUG
pr_info("READ : ");
for (i = 0; i < t_addr.len; i++)
printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
printk(" : ");
for (i = 0; i < t_msg.len; i++)
printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
printk("\n");
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)dst;
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
return ret;
}
static int cw1200_spi_memcpy_toio(struct hwbus_priv *self,
unsigned int addr,
const void *src, int count)
{
int rval, i;
uint16_t regaddr;
struct spi_transfer t_addr = {
.tx_buf = &regaddr,
.len = sizeof(regaddr),
};
struct spi_transfer t_msg = {
.tx_buf = src,
.len = count,
};
struct spi_message m;
regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
regaddr &= SET_WRITE;
regaddr |= (count>>1);
regaddr = cpu_to_le16(regaddr);
#ifdef SPI_DEBUG
pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr,
le16_to_cpu(regaddr));
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)src;
regaddr = swab16(regaddr);
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
#ifdef SPI_DEBUG
pr_info("WRITE: ");
for (i = 0; i < t_addr.len; i++)
printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
printk(" : ");
for (i = 0; i < t_msg.len; i++)
printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
printk("\n");
#endif
spi_message_init(&m);
spi_message_add_tail(&t_addr, &m);
spi_message_add_tail(&t_msg, &m);
rval = spi_sync(self->func, &m);
#ifdef SPI_DEBUG
pr_info("WROTE: %d\n", m.actual_length);
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)src;
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
return rval;
}
static void cw1200_spi_lock(struct hwbus_priv *self)
{
unsigned long flags;
might_sleep();
spin_lock_irqsave(&self->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (!self->claimed)
break;
spin_unlock_irqrestore(&self->lock, flags);
schedule();
spin_lock_irqsave(&self->lock, flags);
}
set_current_state(TASK_RUNNING);
self->claimed = 1;
spin_unlock_irqrestore(&self->lock, flags);
return;
}
static void cw1200_spi_unlock(struct hwbus_priv *self)
{
unsigned long flags;
spin_lock_irqsave(&self->lock, flags);
self->claimed = 0;
spin_unlock_irqrestore(&self->lock, flags);
return;
}
static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
{
struct hwbus_priv *self = dev_id;
if (self->core) {
cw1200_irq_handler(self->core);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
}
static int cw1200_spi_irq_subscribe(struct hwbus_priv *self)
{
int ret;
pr_debug("SW IRQ subscribe\n");
ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler,
IRQF_TRIGGER_HIGH,
"cw1200_wlan_irq", self);
if (WARN_ON(ret < 0))
goto exit;
ret = enable_irq_wake(self->func->irq);
if (WARN_ON(ret))
goto free_irq;
return 0;
free_irq:
free_irq(self->func->irq, self);
exit:
return ret;
}
static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ unsubscribe\n");
disable_irq_wake(self->func->irq);
free_irq(self->func->irq, self);
return ret;
}
static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
{
if (pdata->reset) {
gpio_set_value(pdata->reset, 0);
msleep(30); /* Min is 2 * CLK32K cycles */
gpio_free(pdata->reset);
}
if (pdata->power_ctrl)
pdata->power_ctrl(pdata, false);
if (pdata->clk_ctrl)
pdata->clk_ctrl(pdata, false);
return 0;
}
static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
{
/* Ensure I/Os are pulled low */
if (pdata->reset) {
gpio_request(pdata->reset, "cw1200_wlan_reset");
gpio_direction_output(pdata->reset, 0);
}
if (pdata->powerup) {
gpio_request(pdata->powerup, "cw1200_wlan_powerup");
gpio_direction_output(pdata->powerup, 0);
}
if (pdata->reset || pdata->powerup)
msleep(10); /* Settle time? */
/* Enable 3v3 and 1v8 to hardware */
if (pdata->power_ctrl) {
if (pdata->power_ctrl(pdata, true)) {
pr_err("power_ctrl() failed!\n");
return -1;
}
}
/* Enable CLK32K */
if (pdata->clk_ctrl) {
if (pdata->clk_ctrl(pdata, true)) {
pr_err("clk_ctrl() failed!\n");
return -1;
}
msleep(10); /* Delay until clock is stable for 2 cycles */
}
/* Enable POWERUP signal */
if (pdata->powerup) {
gpio_set_value(pdata->powerup, 1);
msleep(250); /* or more..? */
}
/* Enable RSTn signal */
if (pdata->reset) {
gpio_set_value(pdata->reset, 1);
msleep(50); /* Or more..? */
}
return 0;
}
static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size)
{
return size & 1 ? size + 1 : size;
}
static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend)
{
return irq_set_irq_wake(self->func->irq, suspend);
}
static struct hwbus_ops cw1200_spi_hwbus_ops = {
.hwbus_memcpy_fromio = cw1200_spi_memcpy_fromio,
.hwbus_memcpy_toio = cw1200_spi_memcpy_toio,
.lock = cw1200_spi_lock,
.unlock = cw1200_spi_unlock,
.align_size = cw1200_spi_align_size,
.power_mgmt = cw1200_spi_pm,
};
/* Probe Function to be called by SPI stack when device is discovered */
static int cw1200_spi_probe(struct spi_device *func)
{
const struct cw1200_platform_data_spi *plat_data =
func->dev.platform_data;
struct hwbus_priv *self;
int status;
/* Sanity check speed */
if (func->max_speed_hz > 52000000)
func->max_speed_hz = 52000000;
if (func->max_speed_hz < 1000000)
func->max_speed_hz = 1000000;
/* Fix up transfer size */
if (plat_data->spi_bits_per_word)
func->bits_per_word = plat_data->spi_bits_per_word;
if (!func->bits_per_word)
func->bits_per_word = 16;
/* And finally.. */
func->mode = SPI_MODE_0;
pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
func->chip_select, func->mode, func->bits_per_word,
func->max_speed_hz);
if (cw1200_spi_on(plat_data)) {
pr_err("spi_on() failed!\n");
return -1;
}
if (spi_setup(func)) {
pr_err("spi_setup() failed!\n");
return -1;
}
self = kzalloc(sizeof(*self), GFP_KERNEL);
if (!self) {
pr_err("Can't allocate SPI hwbus_priv.");
return -ENOMEM;
}
self->pdata = plat_data;
self->func = func;
spin_lock_init(&self->lock);
spi_set_drvdata(func, self);
status = cw1200_spi_irq_subscribe(self);
status = cw1200_core_probe(&cw1200_spi_hwbus_ops,
self, &func->dev, &self->core,
self->pdata->ref_clk,
self->pdata->macaddr,
self->pdata->sdd_file,
self->pdata->have_5ghz);
if (status) {
cw1200_spi_irq_unsubscribe(self);
cw1200_spi_off(plat_data);
kfree(self);
}
return status;
}
/* Disconnect Function to be called by SPI stack when device is disconnected */
static int cw1200_spi_disconnect(struct spi_device *func)
{
struct hwbus_priv *self = spi_get_drvdata(func);
if (self) {
cw1200_spi_irq_unsubscribe(self);
if (self->core) {
cw1200_core_release(self->core);
self->core = NULL;
}
kfree(self);
}
cw1200_spi_off(func->dev.platform_data);
return 0;
}
#ifdef CONFIG_PM
static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
{
struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
if (!cw1200_can_suspend(self->core))
return -EAGAIN;
/* XXX notify host that we have to keep CW1200 powered on? */
return 0;
}
static int cw1200_spi_resume(struct device *dev)
{
return 0;
}
#endif
static struct spi_driver spi_driver = {
.probe = cw1200_spi_probe,
.remove = cw1200_spi_disconnect,
.driver = {
.name = "cw1200_wlan_spi",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.suspend = cw1200_spi_suspend,
.resume = cw1200_spi_resume,
#endif
},
};
module_spi_driver(spi_driver);

View File

@ -0,0 +1,428 @@
/*
* mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
* DebugFS code
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "cw1200.h"
#include "debug.h"
#include "fwio.h"
/* join_status */
static const char * const cw1200_debug_join_status[] = {
"passive",
"monitor",
"station (joining)",
"station (not authenticated yet)",
"station",
"adhoc",
"access point",
};
/* WSM_JOIN_PREAMBLE_... */
static const char * const cw1200_debug_preamble[] = {
"long",
"short",
"long on 1 and 2 Mbps",
};
static const char * const cw1200_debug_link_id[] = {
"OFF",
"REQ",
"SOFT",
"HARD",
};
static const char *cw1200_debug_mode(int mode)
{
switch (mode) {
case NL80211_IFTYPE_UNSPECIFIED:
return "unspecified";
case NL80211_IFTYPE_MONITOR:
return "monitor";
case NL80211_IFTYPE_STATION:
return "station";
case NL80211_IFTYPE_ADHOC:
return "adhoc";
case NL80211_IFTYPE_MESH_POINT:
return "mesh point";
case NL80211_IFTYPE_AP:
return "access point";
case NL80211_IFTYPE_P2P_CLIENT:
return "p2p client";
case NL80211_IFTYPE_P2P_GO:
return "p2p go";
default:
return "unsupported";
}
}
static void cw1200_queue_status_show(struct seq_file *seq,
struct cw1200_queue *q)
{
int i;
seq_printf(seq, "Queue %d:\n", q->queue_id);
seq_printf(seq, " capacity: %zu\n", q->capacity);
seq_printf(seq, " queued: %zu\n", q->num_queued);
seq_printf(seq, " pending: %zu\n", q->num_pending);
seq_printf(seq, " sent: %zu\n", q->num_sent);
seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no");
seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no");
seq_puts(seq, " link map: 0-> ");
for (i = 0; i < q->stats->map_capacity; ++i)
seq_printf(seq, "%.2d ", q->link_map_cache[i]);
seq_printf(seq, "<-%zu\n", q->stats->map_capacity);
}
static void cw1200_debug_print_map(struct seq_file *seq,
struct cw1200_common *priv,
const char *label,
u32 map)
{
int i;
seq_printf(seq, "%s0-> ", label);
for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i)
seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1);
}
static int cw1200_status_show(struct seq_file *seq, void *v)
{
int i;
struct list_head *item;
struct cw1200_common *priv = seq->private;
struct cw1200_debug_priv *d = priv->debug;
seq_puts(seq, "CW1200 Wireless LAN driver status\n");
seq_printf(seq, "Hardware: %d.%d\n",
priv->wsm_caps.hw_id,
priv->wsm_caps.hw_subid);
seq_printf(seq, "Firmware: %s %d.%d\n",
cw1200_fw_types[priv->wsm_caps.fw_type],
priv->wsm_caps.fw_ver,
priv->wsm_caps.fw_build);
seq_printf(seq, "FW API: %d\n",
priv->wsm_caps.fw_api);
seq_printf(seq, "FW caps: 0x%.4X\n",
priv->wsm_caps.fw_cap);
seq_printf(seq, "FW label: '%s'\n",
priv->wsm_caps.fw_label);
seq_printf(seq, "Mode: %s%s\n",
cw1200_debug_mode(priv->mode),
priv->listening ? " (listening)" : "");
seq_printf(seq, "Join state: %s\n",
cw1200_debug_join_status[priv->join_status]);
if (priv->channel)
seq_printf(seq, "Channel: %d%s\n",
priv->channel->hw_value,
priv->channel_switch_in_progress ?
" (switching)" : "");
if (priv->rx_filter.promiscuous)
seq_puts(seq, "Filter: promisc\n");
else if (priv->rx_filter.fcs)
seq_puts(seq, "Filter: fcs\n");
if (priv->rx_filter.bssid)
seq_puts(seq, "Filter: bssid\n");
if (!priv->disable_beacon_filter)
seq_puts(seq, "Filter: beacons\n");
if (priv->enable_beacon ||
priv->mode == NL80211_IFTYPE_AP ||
priv->mode == NL80211_IFTYPE_ADHOC ||
priv->mode == NL80211_IFTYPE_MESH_POINT ||
priv->mode == NL80211_IFTYPE_P2P_GO)
seq_printf(seq, "Beaconing: %s\n",
priv->enable_beacon ?
"enabled" : "disabled");
for (i = 0; i < 4; ++i)
seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i,
priv->edca.params[i].cwmin,
priv->edca.params[i].cwmax,
priv->edca.params[i].aifns,
priv->edca.params[i].txop_limit,
priv->edca.params[i].max_rx_lifetime);
if (priv->join_status == CW1200_JOIN_STATUS_STA) {
static const char *pm_mode = "unknown";
switch (priv->powersave_mode.mode) {
case WSM_PSM_ACTIVE:
pm_mode = "off";
break;
case WSM_PSM_PS:
pm_mode = "on";
break;
case WSM_PSM_FAST_PS:
pm_mode = "dynamic";
break;
}
seq_printf(seq, "Preamble: %s\n",
cw1200_debug_preamble[priv->association_mode.preamble]);
seq_printf(seq, "AMPDU spcn: %d\n",
priv->association_mode.mpdu_start_spacing);
seq_printf(seq, "Basic rate: 0x%.8X\n",
le32_to_cpu(priv->association_mode.basic_rate_set));
seq_printf(seq, "Bss lost: %d beacons\n",
priv->bss_params.beacon_lost_count);
seq_printf(seq, "AID: %d\n",
priv->bss_params.aid);
seq_printf(seq, "Rates: 0x%.8X\n",
priv->bss_params.operational_rate_set);
seq_printf(seq, "Powersave: %s\n", pm_mode);
}
seq_printf(seq, "HT: %s\n",
cw1200_is_ht(&priv->ht_info) ? "on" : "off");
if (cw1200_is_ht(&priv->ht_info)) {
seq_printf(seq, "Greenfield: %s\n",
cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no");
seq_printf(seq, "AMPDU dens: %d\n",
cw1200_ht_ampdu_density(&priv->ht_info));
}
seq_printf(seq, "RSSI thold: %d\n",
priv->cqm_rssi_thold);
seq_printf(seq, "RSSI hyst: %d\n",
priv->cqm_rssi_hyst);
seq_printf(seq, "Long retr: %d\n",
priv->long_frame_max_tx_count);
seq_printf(seq, "Short retr: %d\n",
priv->short_frame_max_tx_count);
spin_lock_bh(&priv->tx_policy_cache.lock);
i = 0;
list_for_each(item, &priv->tx_policy_cache.used)
++i;
spin_unlock_bh(&priv->tx_policy_cache.lock);
seq_printf(seq, "RC in use: %d\n", i);
seq_puts(seq, "\n");
for (i = 0; i < 4; ++i) {
cw1200_queue_status_show(seq, &priv->tx_queue[i]);
seq_puts(seq, "\n");
}
cw1200_debug_print_map(seq, priv, "Link map: ",
priv->link_id_map);
cw1200_debug_print_map(seq, priv, "Asleep map: ",
priv->sta_asleep_mask);
cw1200_debug_print_map(seq, priv, "PSPOLL map: ",
priv->pspoll_mask);
seq_puts(seq, "\n");
for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
if (priv->link_id_db[i].status) {
seq_printf(seq, "Link %d: %s, %pM\n",
i + 1,
cw1200_debug_link_id[priv->link_id_db[i].status],
priv->link_id_db[i].mac);
}
}
seq_puts(seq, "\n");
seq_printf(seq, "BH status: %s\n",
atomic_read(&priv->bh_term) ? "terminated" : "alive");
seq_printf(seq, "Pending RX: %d\n",
atomic_read(&priv->bh_rx));
seq_printf(seq, "Pending TX: %d\n",
atomic_read(&priv->bh_tx));
if (priv->bh_error)
seq_printf(seq, "BH errcode: %d\n",
priv->bh_error);
seq_printf(seq, "TX bufs: %d x %d bytes\n",
priv->wsm_caps.input_buffers,
priv->wsm_caps.input_buffer_size);
seq_printf(seq, "Used bufs: %d\n",
priv->hw_bufs_used);
seq_printf(seq, "Powermgmt: %s\n",
priv->powersave_enabled ? "on" : "off");
seq_printf(seq, "Device: %s\n",
priv->device_can_sleep ? "asleep" : "awake");
spin_lock(&priv->wsm_cmd.lock);
seq_printf(seq, "WSM status: %s\n",
priv->wsm_cmd.done ? "idle" : "active");
seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n",
priv->wsm_cmd.cmd, priv->wsm_cmd.len);
seq_printf(seq, "WSM retval: %d\n",
priv->wsm_cmd.ret);
spin_unlock(&priv->wsm_cmd.lock);
seq_printf(seq, "Datapath: %s\n",
atomic_read(&priv->tx_lock) ? "locked" : "unlocked");
if (atomic_read(&priv->tx_lock))
seq_printf(seq, "TXlock cnt: %d\n",
atomic_read(&priv->tx_lock));
seq_printf(seq, "TXed: %d\n",
d->tx);
seq_printf(seq, "AGG TXed: %d\n",
d->tx_agg);
seq_printf(seq, "MULTI TXed: %d (%d)\n",
d->tx_multi, d->tx_multi_frames);
seq_printf(seq, "RXed: %d\n",
d->rx);
seq_printf(seq, "AGG RXed: %d\n",
d->rx_agg);
seq_printf(seq, "TX miss: %d\n",
d->tx_cache_miss);
seq_printf(seq, "TX align: %d\n",
d->tx_align);
seq_printf(seq, "TX burst: %d\n",
d->tx_burst);
seq_printf(seq, "TX TTL: %d\n",
d->tx_ttl);
seq_printf(seq, "Scan: %s\n",
atomic_read(&priv->scan.in_progress) ? "active" : "idle");
return 0;
}
static int cw1200_status_open(struct inode *inode, struct file *file)
{
return single_open(file, &cw1200_status_show,
inode->i_private);
}
static const struct file_operations fops_status = {
.open = cw1200_status_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int cw1200_counters_show(struct seq_file *seq, void *v)
{
int ret;
struct cw1200_common *priv = seq->private;
struct wsm_mib_counters_table counters;
ret = wsm_get_counters_table(priv, &counters);
if (ret)
return ret;
#define PUT_COUNTER(tab, name) \
seq_printf(seq, "%s:" tab "%d\n", #name, \
__le32_to_cpu(counters.name))
PUT_COUNTER("\t\t", plcp_errors);
PUT_COUNTER("\t\t", fcs_errors);
PUT_COUNTER("\t\t", tx_packets);
PUT_COUNTER("\t\t", rx_packets);
PUT_COUNTER("\t\t", rx_packet_errors);
PUT_COUNTER("\t", rx_decryption_failures);
PUT_COUNTER("\t\t", rx_mic_failures);
PUT_COUNTER("\t", rx_no_key_failures);
PUT_COUNTER("\t", tx_multicast_frames);
PUT_COUNTER("\t", tx_frames_success);
PUT_COUNTER("\t", tx_frame_failures);
PUT_COUNTER("\t", tx_frames_retried);
PUT_COUNTER("\t", tx_frames_multi_retried);
PUT_COUNTER("\t", rx_frame_duplicates);
PUT_COUNTER("\t\t", rts_success);
PUT_COUNTER("\t\t", rts_failures);
PUT_COUNTER("\t\t", ack_failures);
PUT_COUNTER("\t", rx_multicast_frames);
PUT_COUNTER("\t", rx_frames_success);
PUT_COUNTER("\t", rx_cmac_icv_errors);
PUT_COUNTER("\t\t", rx_cmac_replays);
PUT_COUNTER("\t", rx_mgmt_ccmp_replays);
#undef PUT_COUNTER
return 0;
}
static int cw1200_counters_open(struct inode *inode, struct file *file)
{
return single_open(file, &cw1200_counters_show,
inode->i_private);
}
static const struct file_operations fops_counters = {
.open = cw1200_counters_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static ssize_t cw1200_wsm_dumps(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct cw1200_common *priv = file->private_data;
char buf[1];
if (!count)
return -EINVAL;
if (copy_from_user(buf, user_buf, 1))
return -EFAULT;
if (buf[0] == '1')
priv->wsm_enable_wsm_dumps = 1;
else
priv->wsm_enable_wsm_dumps = 0;
return count;
}
static const struct file_operations fops_wsm_dumps = {
.open = simple_open,
.write = cw1200_wsm_dumps,
.llseek = default_llseek,
};
int cw1200_debug_init(struct cw1200_common *priv)
{
int ret = -ENOMEM;
struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv),
GFP_KERNEL);
priv->debug = d;
if (!d)
return ret;
d->debugfs_phy = debugfs_create_dir("cw1200",
priv->hw->wiphy->debugfsdir);
if (!d->debugfs_phy)
goto err;
if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
priv, &fops_status))
goto err;
if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
priv, &fops_counters))
goto err;
if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
priv, &fops_wsm_dumps))
goto err;
return 0;
err:
priv->debug = NULL;
debugfs_remove_recursive(d->debugfs_phy);
kfree(d);
return ret;
}
void cw1200_debug_release(struct cw1200_common *priv)
{
struct cw1200_debug_priv *d = priv->debug;
if (d) {
debugfs_remove_recursive(d->debugfs_phy);
priv->debug = NULL;
kfree(d);
}
}

View File

@ -0,0 +1,93 @@
/*
* DebugFS code for ST-Ericsson CW1200 mac80211 driver
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_DEBUG_H_INCLUDED
#define CW1200_DEBUG_H_INCLUDED
struct cw1200_debug_priv {
struct dentry *debugfs_phy;
int tx;
int tx_agg;
int rx;
int rx_agg;
int tx_multi;
int tx_multi_frames;
int tx_cache_miss;
int tx_align;
int tx_ttl;
int tx_burst;
int ba_cnt;
int ba_acc;
int ba_cnt_rx;
int ba_acc_rx;
};
int cw1200_debug_init(struct cw1200_common *priv);
void cw1200_debug_release(struct cw1200_common *priv);
static inline void cw1200_debug_txed(struct cw1200_common *priv)
{
++priv->debug->tx;
}
static inline void cw1200_debug_txed_agg(struct cw1200_common *priv)
{
++priv->debug->tx_agg;
}
static inline void cw1200_debug_txed_multi(struct cw1200_common *priv,
int count)
{
++priv->debug->tx_multi;
priv->debug->tx_multi_frames += count;
}
static inline void cw1200_debug_rxed(struct cw1200_common *priv)
{
++priv->debug->rx;
}
static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv)
{
++priv->debug->rx_agg;
}
static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv)
{
++priv->debug->tx_cache_miss;
}
static inline void cw1200_debug_tx_align(struct cw1200_common *priv)
{
++priv->debug->tx_align;
}
static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv)
{
++priv->debug->tx_ttl;
}
static inline void cw1200_debug_tx_burst(struct cw1200_common *priv)
{
++priv->debug->tx_burst;
}
static inline void cw1200_debug_ba(struct cw1200_common *priv,
int ba_cnt, int ba_acc,
int ba_cnt_rx, int ba_acc_rx)
{
priv->debug->ba_cnt = ba_cnt;
priv->debug->ba_acc = ba_acc;
priv->debug->ba_cnt_rx = ba_cnt_rx;
priv->debug->ba_acc_rx = ba_acc_rx;
}
#endif /* CW1200_DEBUG_H_INCLUDED */

View File

@ -0,0 +1,520 @@
/*
* Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/firmware.h>
#include "cw1200.h"
#include "fwio.h"
#include "hwio.h"
#include "hwbus.h"
#include "bh.h"
static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision)
{
int hw_type = -1;
u32 silicon_type = (config_reg_val >> 24) & 0x7;
u32 silicon_vers = (config_reg_val >> 31) & 0x1;
switch (silicon_type) {
case 0x00:
*major_revision = 1;
hw_type = HIF_9000_SILICON_VERSATILE;
break;
case 0x01:
case 0x02: /* CW1x00 */
case 0x04: /* CW1x60 */
*major_revision = silicon_type;
if (silicon_vers)
hw_type = HIF_8601_VERSATILE;
else
hw_type = HIF_8601_SILICON;
break;
default:
break;
}
return hw_type;
}
static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
{
int ret, block, num_blocks;
unsigned i;
u32 val32;
u32 put = 0, get = 0;
u8 *buf = NULL;
const char *fw_path;
const struct firmware *firmware = NULL;
/* Macroses are local. */
#define APB_WRITE(reg, val) \
do { \
ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
if (ret < 0) \
goto error; \
} while (0)
#define APB_READ(reg, val) \
do { \
ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
if (ret < 0) \
goto error; \
} while (0)
#define REG_WRITE(reg, val) \
do { \
ret = cw1200_reg_write_32(priv, (reg), (val)); \
if (ret < 0) \
goto error; \
} while (0)
#define REG_READ(reg, val) \
do { \
ret = cw1200_reg_read_32(priv, (reg), &(val)); \
if (ret < 0) \
goto error; \
} while (0)
switch (priv->hw_revision) {
case CW1200_HW_REV_CUT10:
fw_path = FIRMWARE_CUT10;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_10;
break;
case CW1200_HW_REV_CUT11:
fw_path = FIRMWARE_CUT11;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_11;
break;
case CW1200_HW_REV_CUT20:
fw_path = FIRMWARE_CUT20;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_20;
break;
case CW1200_HW_REV_CUT22:
fw_path = FIRMWARE_CUT22;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_22;
break;
case CW1X60_HW_REV:
fw_path = FIRMWARE_CW1X60;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_CW1X60;
break;
default:
pr_err("Invalid silicon revision %d.\n", priv->hw_revision);
return -EINVAL;
}
/* Initialize common registers */
APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE);
APB_WRITE(DOWNLOAD_PUT_REG, 0);
APB_WRITE(DOWNLOAD_GET_REG, 0);
APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING);
APB_WRITE(DOWNLOAD_FLAGS_REG, 0);
/* Write the NOP Instruction */
REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000);
REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE);
/* Release CPU from RESET */
REG_READ(ST90TDS_CONFIG_REG_ID, val32);
val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT;
REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
/* Enable Clock */
val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT;
REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
/* Load a firmware file */
ret = request_firmware(&firmware, fw_path, priv->pdev);
if (ret) {
pr_err("Can't load firmware file %s.\n", fw_path);
goto error;
}
buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
if (!buf) {
pr_err("Can't allocate firmware load buffer.\n");
ret = -ENOMEM;
goto error;
}
/* Check if the bootloader is ready */
for (i = 0; i < 100; i += 1 + i / 2) {
APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32);
if (val32 == DOWNLOAD_I_AM_HERE)
break;
mdelay(i);
} /* End of for loop */
if (val32 != DOWNLOAD_I_AM_HERE) {
pr_err("Bootloader is not ready.\n");
ret = -ETIMEDOUT;
goto error;
}
/* Calculcate number of download blocks */
num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1;
/* Updating the length in Download Ctrl Area */
val32 = firmware->size; /* Explicit cast from size_t to u32 */
APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32);
/* Firmware downloading loop */
for (block = 0; block < num_blocks; block++) {
size_t tx_size;
size_t block_size;
/* check the download status */
APB_READ(DOWNLOAD_STATUS_REG, val32);
if (val32 != DOWNLOAD_PENDING) {
pr_err("Bootloader reported error %d.\n", val32);
ret = -EIO;
goto error;
}
/* loop until put - get <= 24K */
for (i = 0; i < 100; i++) {
APB_READ(DOWNLOAD_GET_REG, get);
if ((put - get) <=
(DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE))
break;
mdelay(i);
}
if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
pr_err("Timeout waiting for FIFO.\n");
ret = -ETIMEDOUT;
goto error;
}
/* calculate the block size */
tx_size = block_size = min((size_t)(firmware->size - put),
(size_t)DOWNLOAD_BLOCK_SIZE);
memcpy(buf, &firmware->data[put], block_size);
if (block_size < DOWNLOAD_BLOCK_SIZE) {
memset(&buf[block_size], 0,
DOWNLOAD_BLOCK_SIZE - block_size);
tx_size = DOWNLOAD_BLOCK_SIZE;
}
/* send the block to sram */
ret = cw1200_apb_write(priv,
CW1200_APB(DOWNLOAD_FIFO_OFFSET +
(put & (DOWNLOAD_FIFO_SIZE - 1))),
buf, tx_size);
if (ret < 0) {
pr_err("Can't write firmware block @ %d!\n",
put & (DOWNLOAD_FIFO_SIZE - 1));
goto error;
}
/* update the put register */
put += block_size;
APB_WRITE(DOWNLOAD_PUT_REG, put);
} /* End of firmware download loop */
/* Wait for the download completion */
for (i = 0; i < 300; i += 1 + i / 2) {
APB_READ(DOWNLOAD_STATUS_REG, val32);
if (val32 != DOWNLOAD_PENDING)
break;
mdelay(i);
}
if (val32 != DOWNLOAD_SUCCESS) {
pr_err("Wait for download completion failed: 0x%.8X\n", val32);
ret = -ETIMEDOUT;
goto error;
} else {
pr_info("Firmware download completed.\n");
ret = 0;
}
error:
kfree(buf);
if (firmware)
release_firmware(firmware);
return ret;
#undef APB_WRITE
#undef APB_READ
#undef REG_WRITE
#undef REG_READ
}
static int config_reg_read(struct cw1200_common *priv, u32 *val)
{
switch (priv->hw_type) {
case HIF_9000_SILICON_VERSATILE: {
u16 val16;
int ret = cw1200_reg_read_16(priv,
ST90TDS_CONFIG_REG_ID,
&val16);
if (ret < 0)
return ret;
*val = val16;
return 0;
}
case HIF_8601_VERSATILE:
case HIF_8601_SILICON:
default:
cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val);
break;
}
return 0;
}
static int config_reg_write(struct cw1200_common *priv, u32 val)
{
switch (priv->hw_type) {
case HIF_9000_SILICON_VERSATILE:
return cw1200_reg_write_16(priv,
ST90TDS_CONFIG_REG_ID,
(u16)val);
case HIF_8601_VERSATILE:
case HIF_8601_SILICON:
default:
return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val);
break;
}
return 0;
}
int cw1200_load_firmware(struct cw1200_common *priv)
{
int ret;
int i;
u32 val32;
u16 val16;
int major_revision = -1;
/* Read CONFIG Register */
ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto out;
}
if (val32 == 0 || val32 == 0xffffffff) {
pr_err("Bad config register value (0x%08x)\n", val32);
ret = -EIO;
goto out;
}
priv->hw_type = cw1200_get_hw_type(val32, &major_revision);
if (priv->hw_type < 0) {
pr_err("Can't deduce hardware type.\n");
ret = -ENOTSUPP;
goto out;
}
/* Set DPLL Reg value, and read back to confirm writes work */
ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
cw1200_dpll_from_clk(priv->hw_refclk));
if (ret < 0) {
pr_err("Can't write DPLL register.\n");
goto out;
}
msleep(20);
ret = cw1200_reg_read_32(priv,
ST90TDS_TSET_GEN_R_W_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read DPLL register.\n");
goto out;
}
if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) {
pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n",
cw1200_dpll_from_clk(priv->hw_refclk), val32);
ret = -EIO;
goto out;
}
/* Set wakeup bit in device */
ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16);
if (ret < 0) {
pr_err("set_wakeup: can't read control register.\n");
goto out;
}
ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
val16 | ST90TDS_CONT_WUP_BIT);
if (ret < 0) {
pr_err("set_wakeup: can't write control register.\n");
goto out;
}
/* Wait for wakeup */
for (i = 0; i < 300; i += (1 + i / 2)) {
ret = cw1200_reg_read_16(priv,
ST90TDS_CONTROL_REG_ID, &val16);
if (ret < 0) {
pr_err("wait_for_wakeup: can't read control register.\n");
goto out;
}
if (val16 & ST90TDS_CONT_RDY_BIT)
break;
msleep(i);
}
if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) {
pr_err("wait_for_wakeup: device is not responding.\n");
ret = -ETIMEDOUT;
goto out;
}
switch (major_revision) {
case 1:
/* CW1200 Hardware detection logic : Check for CUT1.1 */
ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32);
if (ret) {
pr_err("HW detection: can't read CUT ID.\n");
goto out;
}
switch (val32) {
case CW1200_CUT_11_ID_STR:
pr_info("CW1x00 Cut 1.1 silicon detected.\n");
priv->hw_revision = CW1200_HW_REV_CUT11;
break;
default:
pr_info("CW1x00 Cut 1.0 silicon detected.\n");
priv->hw_revision = CW1200_HW_REV_CUT10;
break;
}
/* According to ST-E, CUT<2.0 has busted BA TID0-3.
Just disable it entirely...
*/
priv->ba_rx_tid_mask = 0;
priv->ba_tx_tid_mask = 0;
break;
case 2: {
u32 ar1, ar2, ar3;
ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1);
if (ret) {
pr_err("(1) HW detection: can't read CUT ID\n");
goto out;
}
ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2);
if (ret) {
pr_err("(2) HW detection: can't read CUT ID.\n");
goto out;
}
ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3);
if (ret) {
pr_err("(3) HW detection: can't read CUT ID.\n");
goto out;
}
if (ar1 == CW1200_CUT_22_ID_STR1 &&
ar2 == CW1200_CUT_22_ID_STR2 &&
ar3 == CW1200_CUT_22_ID_STR3) {
pr_info("CW1x00 Cut 2.2 silicon detected.\n");
priv->hw_revision = CW1200_HW_REV_CUT22;
} else {
pr_info("CW1x00 Cut 2.0 silicon detected.\n");
priv->hw_revision = CW1200_HW_REV_CUT20;
}
break;
}
case 4:
pr_info("CW1x60 silicon detected.\n");
priv->hw_revision = CW1X60_HW_REV;
break;
default:
pr_err("Unsupported silicon major revision %d.\n",
major_revision);
ret = -ENOTSUPP;
goto out;
}
/* Checking for access mode */
ret = config_reg_read(priv, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto out;
}
if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) {
pr_err("Device is already in QUEUE mode!\n");
ret = -EINVAL;
goto out;
}
switch (priv->hw_type) {
case HIF_8601_SILICON:
if (priv->hw_revision == CW1X60_HW_REV) {
pr_err("Can't handle CW1160/1260 firmware load yet.\n");
ret = -ENOTSUPP;
goto out;
}
ret = cw1200_load_firmware_cw1200(priv);
break;
default:
pr_err("Can't perform firmware load for hw type %d.\n",
priv->hw_type);
ret = -ENOTSUPP;
goto out;
}
if (ret < 0) {
pr_err("Firmware load error.\n");
goto out;
}
/* Enable interrupt signalling */
priv->hwbus_ops->lock(priv->hwbus_priv);
ret = __cw1200_irq_enable(priv, 1);
priv->hwbus_ops->unlock(priv->hwbus_priv);
if (ret < 0)
goto unsubscribe;
/* Configure device for MESSSAGE MODE */
ret = config_reg_read(priv, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto unsubscribe;
}
ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT);
if (ret < 0) {
pr_err("Can't write config register.\n");
goto unsubscribe;
}
/* Unless we read the CONFIG Register we are
* not able to get an interrupt
*/
mdelay(10);
config_reg_read(priv, &val32);
out:
return ret;
unsubscribe:
/* Disable interrupt signalling */
priv->hwbus_ops->lock(priv->hwbus_priv);
ret = __cw1200_irq_enable(priv, 0);
priv->hwbus_ops->unlock(priv->hwbus_priv);
return ret;
}

View File

@ -0,0 +1,39 @@
/*
* Firmware API for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FWIO_H_INCLUDED
#define FWIO_H_INCLUDED
#define BOOTLOADER_CW1X60 "boot_cw1x60.bin"
#define FIRMWARE_CW1X60 "wsm_cw1x60.bin"
#define FIRMWARE_CUT22 "wsm_22.bin"
#define FIRMWARE_CUT20 "wsm_20.bin"
#define FIRMWARE_CUT11 "wsm_11.bin"
#define FIRMWARE_CUT10 "wsm_10.bin"
#define SDD_FILE_CW1X60 "sdd_cw1x60.bin"
#define SDD_FILE_22 "sdd_22.bin"
#define SDD_FILE_20 "sdd_20.bin"
#define SDD_FILE_11 "sdd_11.bin"
#define SDD_FILE_10 "sdd_10.bin"
int cw1200_load_firmware(struct cw1200_common *priv);
/* SDD definitions */
#define SDD_PTA_CFG_ELT_ID 0xEB
#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5
u32 cw1200_dpll_from_clk(u16 clk);
#endif

View File

@ -0,0 +1,33 @@
/*
* Common hwbus abstraction layer interface for cw1200 wireless driver
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_HWBUS_H
#define CW1200_HWBUS_H
struct hwbus_priv;
void cw1200_irq_handler(struct cw1200_common *priv);
/* This MUST be wrapped with hwbus_ops->lock/unlock! */
int __cw1200_irq_enable(struct cw1200_common *priv, int enable);
struct hwbus_ops {
int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr,
void *dst, int count);
int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr,
const void *src, int count);
void (*lock)(struct hwbus_priv *self);
void (*unlock)(struct hwbus_priv *self);
size_t (*align_size)(struct hwbus_priv *self, size_t size);
int (*power_mgmt)(struct hwbus_priv *self, bool suspend);
};
#endif /* CW1200_HWBUS_H */

View File

@ -0,0 +1,310 @@
/*
* Low-level device IO routines for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver, which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include "cw1200.h"
#include "hwio.h"
#include "hwbus.h"
/* Sdio addr is 4*spi_addr */
#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
((((buf_id) & 0x1F) << 7) \
| (((mpf) & 1) << 6) \
| (((rfu) & 1) << 5) \
| (((reg_id_ofs) & 0x1F) << 0))
#define MAX_RETRY 3
static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr,
void *buf, size_t buf_len, int buf_id)
{
u16 addr_sdio;
u32 sdio_reg_addr_17bit;
/* Check if buffer is aligned to 4 byte boundary */
if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
pr_err("buffer is not aligned.\n");
return -EINVAL;
}
/* Convert to SDIO Register Address */
addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv,
sdio_reg_addr_17bit,
buf, buf_len);
}
static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr,
const void *buf, size_t buf_len, int buf_id)
{
u16 addr_sdio;
u32 sdio_reg_addr_17bit;
/* Convert to SDIO Register Address */
addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv,
sdio_reg_addr_17bit,
buf, buf_len);
}
static inline int __cw1200_reg_read_32(struct cw1200_common *priv,
u16 addr, u32 *val)
{
int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
*val = le32_to_cpu(*val);
return i;
}
static inline int __cw1200_reg_write_32(struct cw1200_common *priv,
u16 addr, u32 val)
{
val = cpu_to_le32(val);
return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
}
static inline int __cw1200_reg_read_16(struct cw1200_common *priv,
u16 addr, u16 *val)
{
int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
*val = le16_to_cpu(*val);
return i;
}
static inline int __cw1200_reg_write_16(struct cw1200_common *priv,
u16 addr, u16 val)
{
val = cpu_to_le16(val);
return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
}
int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf,
size_t buf_len)
{
int ret;
priv->hwbus_ops->lock(priv->hwbus_priv);
ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
priv->hwbus_ops->unlock(priv->hwbus_priv);
return ret;
}
int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
size_t buf_len)
{
int ret;
priv->hwbus_ops->lock(priv->hwbus_priv);
ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
priv->hwbus_ops->unlock(priv->hwbus_priv);
return ret;
}
int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len)
{
int ret, retry = 1;
int buf_id_rx = priv->buf_id_rx;
priv->hwbus_ops->lock(priv->hwbus_priv);
while (retry <= MAX_RETRY) {
ret = __cw1200_reg_read(priv,
ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
buf_len, buf_id_rx + 1);
if (!ret) {
buf_id_rx = (buf_id_rx + 1) & 3;
priv->buf_id_rx = buf_id_rx;
break;
} else {
retry++;
mdelay(1);
pr_err("error :[%d]\n", ret);
}
}
priv->hwbus_ops->unlock(priv->hwbus_priv);
return ret;
}
int cw1200_data_write(struct cw1200_common *priv, const void *buf,
size_t buf_len)
{
int ret, retry = 1;
int buf_id_tx = priv->buf_id_tx;
priv->hwbus_ops->lock(priv->hwbus_priv);
while (retry <= MAX_RETRY) {
ret = __cw1200_reg_write(priv,
ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
buf_len, buf_id_tx);
if (!ret) {
buf_id_tx = (buf_id_tx + 1) & 31;
priv->buf_id_tx = buf_id_tx;
break;
} else {
retry++;
mdelay(1);
pr_err("error :[%d]\n", ret);
}
}
priv->hwbus_ops->unlock(priv->hwbus_priv);
return ret;
}
int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
size_t buf_len, u32 prefetch, u16 port_addr)
{
u32 val32 = 0;
int i, ret;
if ((buf_len / 2) >= 0x1000) {
pr_err("Can't read more than 0xfff words.\n");
return -EINVAL;
}
priv->hwbus_ops->lock(priv->hwbus_priv);
/* Write address */
ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
if (ret < 0) {
pr_err("Can't write address register.\n");
goto out;
}
/* Read CONFIG Register Value - We will read 32 bits */
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto out;
}
/* Set PREFETCH bit */
ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID,
val32 | prefetch);
if (ret < 0) {
pr_err("Can't write prefetch bit.\n");
goto out;
}
/* Check for PRE-FETCH bit to be cleared */
for (i = 0; i < 20; i++) {
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't check prefetch bit.\n");
goto out;
}
if (!(val32 & prefetch))
break;
mdelay(i);
}
if (val32 & prefetch) {
pr_err("Prefetch bit is not cleared.\n");
goto out;
}
/* Read data port */
ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0);
if (ret < 0) {
pr_err("Can't read data port.\n");
goto out;
}
out:
priv->hwbus_ops->unlock(priv->hwbus_priv);
return ret;
}
int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
size_t buf_len)
{
int ret;
if ((buf_len / 2) >= 0x1000) {
pr_err("Can't write more than 0xfff words.\n");
return -EINVAL;
}
priv->hwbus_ops->lock(priv->hwbus_priv);
/* Write address */
ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
if (ret < 0) {
pr_err("Can't write address register.\n");
goto out;
}
/* Write data port */
ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID,
buf, buf_len, 0);
if (ret < 0) {
pr_err("Can't write data port.\n");
goto out;
}
out:
priv->hwbus_ops->unlock(priv->hwbus_priv);
return ret;
}
int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
{
u32 val32;
u16 val16;
int ret;
if (HIF_8601_SILICON == priv->hw_type) {
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
return ret;
}
if (enable)
val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE;
else
val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE;
ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32);
if (ret < 0) {
pr_err("Can't write config register.\n");
return ret;
}
} else {
ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16);
if (ret < 0) {
pr_err("Can't read control register.\n");
return ret;
}
if (enable)
val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE;
else
val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE;
ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16);
if (ret < 0) {
pr_err("Can't write control register.\n");
return ret;
}
}
return 0;
}

View File

@ -0,0 +1,246 @@
/*
* Low-level API for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_HWIO_H_INCLUDED
#define CW1200_HWIO_H_INCLUDED
/* extern */ struct cw1200_common;
#define CW1200_CUT_11_ID_STR (0x302E3830)
#define CW1200_CUT_22_ID_STR1 (0x302e3132)
#define CW1200_CUT_22_ID_STR2 (0x32302e30)
#define CW1200_CUT_22_ID_STR3 (0x3335)
#define CW1200_CUT_ID_ADDR (0xFFF17F90)
#define CW1200_CUT2_ID_ADDR (0xFFF1FF90)
/* Download control area */
/* boot loader start address in SRAM */
#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000)
/* 32K, 0x4000 to 0xDFFF */
#define DOWNLOAD_FIFO_OFFSET (0x00004000)
/* 32K */
#define DOWNLOAD_FIFO_SIZE (0x00008000)
/* 128 bytes, 0xFF80 to 0xFFFF */
#define DOWNLOAD_CTRL_OFFSET (0x0000FF80)
#define DOWNLOAD_CTRL_DATA_DWORDS (32-6)
struct download_cntl_t {
/* size of whole firmware file (including Cheksum), host init */
u32 image_size;
/* downloading flags */
u32 flags;
/* No. of bytes put into the download, init & updated by host */
u32 put;
/* last traced program counter, last ARM reg_pc */
u32 trace_pc;
/* No. of bytes read from the download, host init, device updates */
u32 get;
/* r0, boot losader status, host init to pending, device updates */
u32 status;
/* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS];
};
#define DOWNLOAD_IMAGE_SIZE_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size))
#define DOWNLOAD_FLAGS_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags))
#define DOWNLOAD_PUT_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put))
#define DOWNLOAD_TRACE_PC_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc))
#define DOWNLOAD_GET_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get))
#define DOWNLOAD_STATUS_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status))
#define DOWNLOAD_DEBUG_DATA_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data))
#define DOWNLOAD_DEBUG_DATA_LEN (108)
#define DOWNLOAD_BLOCK_SIZE (1024)
/* For boot loader detection */
#define DOWNLOAD_ARE_YOU_HERE (0x87654321)
#define DOWNLOAD_I_AM_HERE (0x12345678)
/* Download error code */
#define DOWNLOAD_PENDING (0xFFFFFFFF)
#define DOWNLOAD_SUCCESS (0)
#define DOWNLOAD_EXCEPTION (1)
#define DOWNLOAD_ERR_MEM_1 (2)
#define DOWNLOAD_ERR_MEM_2 (3)
#define DOWNLOAD_ERR_SOFTWARE (4)
#define DOWNLOAD_ERR_FILE_SIZE (5)
#define DOWNLOAD_ERR_CHECKSUM (6)
#define DOWNLOAD_ERR_OVERFLOW (7)
#define DOWNLOAD_ERR_IMAGE (8)
#define DOWNLOAD_ERR_HOST (9)
#define DOWNLOAD_ERR_ABORT (10)
#define SYS_BASE_ADDR_SILICON (0)
#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000)
#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON)
#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr))
/* Device register definitions */
/* WBF - SPI Register Addresses */
#define ST90TDS_ADDR_ID_BASE (0x0000)
/* 16/32 bits */
#define ST90TDS_CONFIG_REG_ID (0x0000)
/* 16/32 bits */
#define ST90TDS_CONTROL_REG_ID (0x0001)
/* 16 bits, Q mode W/R */
#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002)
/* 32 bits, AHB bus R/W */
#define ST90TDS_AHB_DPORT_REG_ID (0x0003)
/* 16/32 bits */
#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004)
/* 32 bits, APB bus R/W */
#define ST90TDS_SRAM_DPORT_REG_ID (0x0005)
/* 32 bits, t_settle/general */
#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006)
/* 16 bits, Q mode read, no length */
#define ST90TDS_FRAME_OUT_REG_ID (0x0007)
#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID)
/* WBF - Control register bit set */
/* next o/p length, bit 11 to 0 */
#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF)
#define ST90TDS_CONT_WUP_BIT (BIT(12))
#define ST90TDS_CONT_RDY_BIT (BIT(13))
#define ST90TDS_CONT_IRQ_ENABLE (BIT(14))
#define ST90TDS_CONT_RDY_ENABLE (BIT(15))
#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15))
/* SPI Config register bit set */
#define ST90TDS_CONFIG_FRAME_BIT (BIT(2))
#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4))
#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3))
#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4))
#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5))
#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6))
#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7))
/* TBD: Sure??? */
#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7))
#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8))
#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9))
/* QueueM */
#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
/* AHB bus */
#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11))
#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
/* APB bus */
#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13))
/* cpu reset */
#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14))
#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15))
/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */
#define ST90TDS_CONF_IRQ_ENABLE (BIT(16))
#define ST90TDS_CONF_RDY_ENABLE (BIT(17))
#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17))
int cw1200_data_read(struct cw1200_common *priv,
void *buf, size_t buf_len);
int cw1200_data_write(struct cw1200_common *priv,
const void *buf, size_t buf_len);
int cw1200_reg_read(struct cw1200_common *priv, u16 addr,
void *buf, size_t buf_len);
int cw1200_reg_write(struct cw1200_common *priv, u16 addr,
const void *buf, size_t buf_len);
static inline int cw1200_reg_read_16(struct cw1200_common *priv,
u16 addr, u16 *val)
{
u32 tmp;
int i;
i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
tmp = le32_to_cpu(tmp);
*val = tmp & 0xffff;
return i;
}
static inline int cw1200_reg_write_16(struct cw1200_common *priv,
u16 addr, u16 val)
{
u32 tmp = val;
tmp = cpu_to_le32(tmp);
return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp));
}
static inline int cw1200_reg_read_32(struct cw1200_common *priv,
u16 addr, u32 *val)
{
int i = cw1200_reg_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
static inline int cw1200_reg_write_32(struct cw1200_common *priv,
u16 addr, u32 val)
{
val = cpu_to_le32(val);
return cw1200_reg_write(priv, addr, &val, sizeof(val));
}
int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
size_t buf_len, u32 prefetch, u16 port_addr);
int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
size_t buf_len);
static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr,
void *buf, size_t buf_len)
{
return cw1200_indirect_read(priv, addr, buf, buf_len,
ST90TDS_CONFIG_PRFETCH_BIT,
ST90TDS_SRAM_DPORT_REG_ID);
}
static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr,
void *buf, size_t buf_len)
{
return cw1200_indirect_read(priv, addr, buf, buf_len,
ST90TDS_CONFIG_AHB_PRFETCH_BIT,
ST90TDS_AHB_DPORT_REG_ID);
}
static inline int cw1200_apb_read_32(struct cw1200_common *priv,
u32 addr, u32 *val)
{
int i = cw1200_apb_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
static inline int cw1200_apb_write_32(struct cw1200_common *priv,
u32 addr, u32 val)
{
val = cpu_to_le32(val);
return cw1200_apb_write(priv, addr, &val, sizeof(val));
}
static inline int cw1200_ahb_read_32(struct cw1200_common *priv,
u32 addr, u32 *val)
{
int i = cw1200_ahb_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
#endif /* CW1200_HWIO_H_INCLUDED */

View File

@ -0,0 +1,600 @@
/*
* mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* Based on:
* - the islsm (softmac prism54) driver, which is:
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
* - stlc45xx driver
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <linux/vmalloc.h>
#include <linux/random.h>
#include <linux/sched.h>
#include <net/mac80211.h>
#include "cw1200.h"
#include "txrx.h"
#include "hwbus.h"
#include "fwio.h"
#include "hwio.h"
#include "bh.h"
#include "sta.h"
#include "scan.h"
#include "debug.h"
#include "pm.h"
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
MODULE_LICENSE("GPL");
MODULE_ALIAS("cw1200_core");
/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
static char *cw1200_sdd_path;
module_param(cw1200_sdd_path, charp, 0644);
MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
static int cw1200_refclk;
module_param(cw1200_refclk, int, 0644);
MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
int cw1200_power_mode = wsm_power_mode_quiescent;
module_param(cw1200_power_mode, int, 0644);
MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)");
#define RATETAB_ENT(_rate, _rateid, _flags) \
{ \
.bitrate = (_rate), \
.hw_value = (_rateid), \
.flags = (_flags), \
}
static struct ieee80211_rate cw1200_rates[] = {
RATETAB_ENT(10, 0, 0),
RATETAB_ENT(20, 1, 0),
RATETAB_ENT(55, 2, 0),
RATETAB_ENT(110, 3, 0),
RATETAB_ENT(60, 6, 0),
RATETAB_ENT(90, 7, 0),
RATETAB_ENT(120, 8, 0),
RATETAB_ENT(180, 9, 0),
RATETAB_ENT(240, 10, 0),
RATETAB_ENT(360, 11, 0),
RATETAB_ENT(480, 12, 0),
RATETAB_ENT(540, 13, 0),
};
static struct ieee80211_rate cw1200_mcs_rates[] = {
RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS),
RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
};
#define cw1200_a_rates (cw1200_rates + 4)
#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4)
#define cw1200_g_rates (cw1200_rates + 0)
#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates))
#define cw1200_n_rates (cw1200_mcs_rates)
#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates))
#define CHAN2G(_channel, _freq, _flags) { \
.band = IEEE80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
#define CHAN5G(_channel, _flags) { \
.band = IEEE80211_BAND_5GHZ, \
.center_freq = 5000 + (5 * (_channel)), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
static struct ieee80211_channel cw1200_2ghz_chantable[] = {
CHAN2G(1, 2412, 0),
CHAN2G(2, 2417, 0),
CHAN2G(3, 2422, 0),
CHAN2G(4, 2427, 0),
CHAN2G(5, 2432, 0),
CHAN2G(6, 2437, 0),
CHAN2G(7, 2442, 0),
CHAN2G(8, 2447, 0),
CHAN2G(9, 2452, 0),
CHAN2G(10, 2457, 0),
CHAN2G(11, 2462, 0),
CHAN2G(12, 2467, 0),
CHAN2G(13, 2472, 0),
CHAN2G(14, 2484, 0),
};
static struct ieee80211_channel cw1200_5ghz_chantable[] = {
CHAN5G(34, 0), CHAN5G(36, 0),
CHAN5G(38, 0), CHAN5G(40, 0),
CHAN5G(42, 0), CHAN5G(44, 0),
CHAN5G(46, 0), CHAN5G(48, 0),
CHAN5G(52, 0), CHAN5G(56, 0),
CHAN5G(60, 0), CHAN5G(64, 0),
CHAN5G(100, 0), CHAN5G(104, 0),
CHAN5G(108, 0), CHAN5G(112, 0),
CHAN5G(116, 0), CHAN5G(120, 0),
CHAN5G(124, 0), CHAN5G(128, 0),
CHAN5G(132, 0), CHAN5G(136, 0),
CHAN5G(140, 0), CHAN5G(149, 0),
CHAN5G(153, 0), CHAN5G(157, 0),
CHAN5G(161, 0), CHAN5G(165, 0),
CHAN5G(184, 0), CHAN5G(188, 0),
CHAN5G(192, 0), CHAN5G(196, 0),
CHAN5G(200, 0), CHAN5G(204, 0),
CHAN5G(208, 0), CHAN5G(212, 0),
CHAN5G(216, 0),
};
static struct ieee80211_supported_band cw1200_band_2ghz = {
.channels = cw1200_2ghz_chantable,
.n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
.bitrates = cw1200_g_rates,
.n_bitrates = cw1200_g_rates_size,
.ht_cap = {
.cap = IEEE80211_HT_CAP_GRN_FLD |
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
IEEE80211_HT_CAP_MAX_AMSDU,
.ht_supported = 1,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
.mcs = {
.rx_mask[0] = 0xFF,
.rx_highest = __cpu_to_le16(0x41),
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
},
};
static struct ieee80211_supported_band cw1200_band_5ghz = {
.channels = cw1200_5ghz_chantable,
.n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
.bitrates = cw1200_a_rates,
.n_bitrates = cw1200_a_rates_size,
.ht_cap = {
.cap = IEEE80211_HT_CAP_GRN_FLD |
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
IEEE80211_HT_CAP_MAX_AMSDU,
.ht_supported = 1,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
.mcs = {
.rx_mask[0] = 0xFF,
.rx_highest = __cpu_to_le16(0x41),
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
},
};
static const unsigned long cw1200_ttl[] = {
1 * HZ, /* VO */
2 * HZ, /* VI */
5 * HZ, /* BE */
10 * HZ /* BK */
};
static const struct ieee80211_ops cw1200_ops = {
.start = cw1200_start,
.stop = cw1200_stop,
.add_interface = cw1200_add_interface,
.remove_interface = cw1200_remove_interface,
.change_interface = cw1200_change_interface,
.tx = cw1200_tx,
.hw_scan = cw1200_hw_scan,
.set_tim = cw1200_set_tim,
.sta_notify = cw1200_sta_notify,
.sta_add = cw1200_sta_add,
.sta_remove = cw1200_sta_remove,
.set_key = cw1200_set_key,
.set_rts_threshold = cw1200_set_rts_threshold,
.config = cw1200_config,
.bss_info_changed = cw1200_bss_info_changed,
.prepare_multicast = cw1200_prepare_multicast,
.configure_filter = cw1200_configure_filter,
.conf_tx = cw1200_conf_tx,
.get_stats = cw1200_get_stats,
.ampdu_action = cw1200_ampdu_action,
.flush = cw1200_flush,
#ifdef CONFIG_PM
.suspend = cw1200_wow_suspend,
.resume = cw1200_wow_resume,
#endif
/* Intentionally not offloaded: */
/*.channel_switch = cw1200_channel_switch, */
/*.remain_on_channel = cw1200_remain_on_channel, */
/*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */
};
int cw1200_ba_rx_tids = -1;
int cw1200_ba_tx_tids = -1;
module_param(cw1200_ba_rx_tids, int, 0644);
module_param(cw1200_ba_tx_tids, int, 0644);
MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
const bool have_5ghz)
{
int i, band;
struct ieee80211_hw *hw;
struct cw1200_common *priv;
hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
if (!hw)
return NULL;
priv = hw->priv;
priv->hw = hw;
priv->hw_type = -1;
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
priv->rates = cw1200_rates; /* TODO: fetch from FW */
priv->mcs_rates = cw1200_n_rates;
if (cw1200_ba_rx_tids != -1)
priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
else
priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */
if (cw1200_ba_tx_tids != -1)
priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
else
priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */
hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SUPPORTS_UAPSD |
IEEE80211_HW_CONNECTION_MONITOR |
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC;
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MESH_POINT) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO);
#ifdef CONFIG_PM
/* Support only for limited wowlan functionalities */
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
WIPHY_WOWLAN_DISCONNECT;
hw->wiphy->wowlan.n_patterns = 0;
#endif
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
hw->channel_change_time = 1000; /* TODO: find actual value */
hw->queues = 4;
priv->rts_threshold = -1;
hw->max_rates = 8;
hw->max_rate_tries = 15;
hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
8; /* TKIP IV */
hw->sta_data_size = sizeof(struct cw1200_sta_priv);
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz;
if (have_5ghz)
hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz;
/* Channel params have to be cleared before registering wiphy again */
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
sband->channels[i].flags = 0;
sband->channels[i].max_antenna_gain = 0;
sband->channels[i].max_power = 30;
}
}
hw->wiphy->max_scan_ssids = 2;
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
if (macaddr)
SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
else
SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
/* Fix up mac address if necessary */
if (hw->wiphy->perm_addr[3] == 0 &&
hw->wiphy->perm_addr[4] == 0 &&
hw->wiphy->perm_addr[5] == 0) {
get_random_bytes(&hw->wiphy->perm_addr[3], 3);
}
mutex_init(&priv->wsm_cmd_mux);
mutex_init(&priv->conf_mutex);
priv->workqueue = create_singlethread_workqueue("cw1200_wq");
sema_init(&priv->scan.lock, 1);
INIT_WORK(&priv->scan.work, cw1200_scan_work);
INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
cw1200_clear_recent_scan_work);
INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
spin_lock_init(&priv->event_queue_lock);
INIT_LIST_HEAD(&priv->event_queue);
INIT_WORK(&priv->event_handler, cw1200_event_handler);
INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
spin_lock_init(&priv->bss_loss_lock);
spin_lock_init(&priv->ps_state_lock);
INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
INIT_WORK(&priv->set_beacon_wakeup_period_work,
cw1200_set_beacon_wakeup_period_work);
init_timer(&priv->mcast_timeout);
priv->mcast_timeout.data = (unsigned long)priv;
priv->mcast_timeout.function = cw1200_mcast_timeout;
if (cw1200_queue_stats_init(&priv->tx_queue_stats,
CW1200_LINK_ID_MAX,
cw1200_skb_dtor,
priv)) {
ieee80211_free_hw(hw);
return NULL;
}
for (i = 0; i < 4; ++i) {
if (cw1200_queue_init(&priv->tx_queue[i],
&priv->tx_queue_stats, i, 16,
cw1200_ttl[i])) {
for (; i > 0; i--)
cw1200_queue_deinit(&priv->tx_queue[i - 1]);
cw1200_queue_stats_deinit(&priv->tx_queue_stats);
ieee80211_free_hw(hw);
return NULL;
}
}
init_waitqueue_head(&priv->channel_switch_done);
init_waitqueue_head(&priv->wsm_cmd_wq);
init_waitqueue_head(&priv->wsm_startup_done);
init_waitqueue_head(&priv->ps_mode_switch_done);
wsm_buf_init(&priv->wsm_cmd_buf);
spin_lock_init(&priv->wsm_cmd.lock);
priv->wsm_cmd.done = 1;
tx_policy_init(priv);
return hw;
}
static int cw1200_register_common(struct ieee80211_hw *dev)
{
struct cw1200_common *priv = dev->priv;
int err;
#ifdef CONFIG_PM
err = cw1200_pm_init(&priv->pm_state, priv);
if (err) {
pr_err("Cannot init PM. (%d).\n",
err);
return err;
}
#endif
err = ieee80211_register_hw(dev);
if (err) {
pr_err("Cannot register device (%d).\n",
err);
#ifdef CONFIG_PM
cw1200_pm_deinit(&priv->pm_state);
#endif
return err;
}
cw1200_debug_init(priv);
pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
return 0;
}
static void cw1200_free_common(struct ieee80211_hw *dev)
{
ieee80211_free_hw(dev);
}
static void cw1200_unregister_common(struct ieee80211_hw *dev)
{
struct cw1200_common *priv = dev->priv;
int i;
ieee80211_unregister_hw(dev);
del_timer_sync(&priv->mcast_timeout);
cw1200_unregister_bh(priv);
cw1200_debug_release(priv);
mutex_destroy(&priv->conf_mutex);
wsm_buf_deinit(&priv->wsm_cmd_buf);
destroy_workqueue(priv->workqueue);
priv->workqueue = NULL;
if (priv->sdd) {
release_firmware(priv->sdd);
priv->sdd = NULL;
}
for (i = 0; i < 4; ++i)
cw1200_queue_deinit(&priv->tx_queue[i]);
cw1200_queue_stats_deinit(&priv->tx_queue_stats);
#ifdef CONFIG_PM
cw1200_pm_deinit(&priv->pm_state);
#endif
}
/* Clock is in KHz */
u32 cw1200_dpll_from_clk(u16 clk_khz)
{
switch (clk_khz) {
case 0x32C8: /* 13000 KHz */
return 0x1D89D241;
case 0x3E80: /* 16000 KHz */
return 0x000001E1;
case 0x41A0: /* 16800 KHz */
return 0x124931C1;
case 0x4B00: /* 19200 KHz */
return 0x00000191;
case 0x5DC0: /* 24000 KHz */
return 0x00000141;
case 0x6590: /* 26000 KHz */
return 0x0EC4F121;
case 0x8340: /* 33600 KHz */
return 0x092490E1;
case 0x9600: /* 38400 KHz */
return 0x100010C1;
case 0x9C40: /* 40000 KHz */
return 0x000000C1;
case 0xBB80: /* 48000 KHz */
return 0x000000A1;
case 0xCB20: /* 52000 KHz */
return 0x07627091;
default:
pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n",
clk_khz);
return 0x0EC4F121;
}
}
int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
struct hwbus_priv *hwbus,
struct device *pdev,
struct cw1200_common **core,
int ref_clk, const u8 *macaddr,
const char *sdd_path, bool have_5ghz)
{
int err = -EINVAL;
struct ieee80211_hw *dev;
struct cw1200_common *priv;
struct wsm_operational_mode mode = {
.power_mode = cw1200_power_mode,
.disable_more_flag_usage = true,
};
dev = cw1200_init_common(macaddr, have_5ghz);
if (!dev)
goto err;
priv = dev->priv;
priv->hw_refclk = ref_clk;
if (cw1200_refclk)
priv->hw_refclk = cw1200_refclk;
priv->sdd_path = (char *)sdd_path;
if (cw1200_sdd_path)
priv->sdd_path = cw1200_sdd_path;
priv->hwbus_ops = hwbus_ops;
priv->hwbus_priv = hwbus;
priv->pdev = pdev;
SET_IEEE80211_DEV(priv->hw, pdev);
/* Pass struct cw1200_common back up */
*core = priv;
err = cw1200_register_bh(priv);
if (err)
goto err1;
err = cw1200_load_firmware(priv);
if (err)
goto err2;
if (wait_event_interruptible_timeout(priv->wsm_startup_done,
priv->firmware_ready,
3*HZ) <= 0) {
/* TODO: Need to find how to reset device
in QUEUE mode properly.
*/
pr_err("Timeout waiting on device startup\n");
err = -ETIMEDOUT;
goto err2;
}
/* Set low-power mode. */
wsm_set_operational_mode(priv, &mode);
/* Enable multi-TX confirmation */
wsm_use_multi_tx_conf(priv, true);
err = cw1200_register_common(dev);
if (err)
goto err2;
return err;
err2:
cw1200_unregister_bh(priv);
err1:
cw1200_free_common(dev);
err:
*core = NULL;
return err;
}
EXPORT_SYMBOL_GPL(cw1200_core_probe);
void cw1200_core_release(struct cw1200_common *self)
{
/* Disable device interrupts */
self->hwbus_ops->lock(self->hwbus_priv);
__cw1200_irq_enable(self, 0);
self->hwbus_ops->unlock(self->hwbus_priv);
/* And then clean up */
cw1200_unregister_common(self->hw);
cw1200_free_common(self->hw);
return;
}
EXPORT_SYMBOL_GPL(cw1200_core_release);

View File

@ -0,0 +1,367 @@
/*
* Mac80211 power management API for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/if_ether.h>
#include "cw1200.h"
#include "pm.h"
#include "sta.h"
#include "bh.h"
#include "hwbus.h"
#define CW1200_BEACON_SKIPPING_MULTIPLIER 3
struct cw1200_udp_port_filter {
struct wsm_udp_port_filter_hdr hdr;
/* Up to 4 filters are allowed. */
struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS];
} __packed;
struct cw1200_ether_type_filter {
struct wsm_ether_type_filter_hdr hdr;
/* Up to 4 filters are allowed. */
struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS];
} __packed;
static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = {
.hdr.num = 2,
.filters = {
[0] = {
.action = WSM_FILTER_ACTION_FILTER_OUT,
.type = WSM_FILTER_PORT_TYPE_DST,
.port = __cpu_to_le16(67), /* DHCP Bootps */
},
[1] = {
.action = WSM_FILTER_ACTION_FILTER_OUT,
.type = WSM_FILTER_PORT_TYPE_DST,
.port = __cpu_to_le16(68), /* DHCP Bootpc */
},
}
};
static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = {
.num = 0,
};
#ifndef ETH_P_WAPI
#define ETH_P_WAPI 0x88B4
#endif
static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = {
.hdr.num = 4,
.filters = {
[0] = {
.action = WSM_FILTER_ACTION_FILTER_IN,
.type = __cpu_to_le16(ETH_P_IP),
},
[1] = {
.action = WSM_FILTER_ACTION_FILTER_IN,
.type = __cpu_to_le16(ETH_P_PAE),
},
[2] = {
.action = WSM_FILTER_ACTION_FILTER_IN,
.type = __cpu_to_le16(ETH_P_WAPI),
},
[3] = {
.action = WSM_FILTER_ACTION_FILTER_IN,
.type = __cpu_to_le16(ETH_P_ARP),
},
},
};
static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = {
.num = 0,
};
/* private */
struct cw1200_suspend_state {
unsigned long bss_loss_tmo;
unsigned long join_tmo;
unsigned long direct_probe;
unsigned long link_id_gc;
bool beacon_skipping;
u8 prev_ps_mode;
};
static void cw1200_pm_stay_awake_tmo(unsigned long arg)
{
/* XXX what's the point of this ? */
}
int cw1200_pm_init(struct cw1200_pm_state *pm,
struct cw1200_common *priv)
{
spin_lock_init(&pm->lock);
init_timer(&pm->stay_awake);
pm->stay_awake.data = (unsigned long)pm;
pm->stay_awake.function = cw1200_pm_stay_awake_tmo;
return 0;
}
void cw1200_pm_deinit(struct cw1200_pm_state *pm)
{
del_timer_sync(&pm->stay_awake);
}
void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
unsigned long tmo)
{
long cur_tmo;
spin_lock_bh(&pm->lock);
cur_tmo = pm->stay_awake.expires - jiffies;
if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
mod_timer(&pm->stay_awake, jiffies + tmo);
spin_unlock_bh(&pm->lock);
}
static long cw1200_suspend_work(struct delayed_work *work)
{
int ret = cancel_delayed_work(work);
long tmo;
if (ret > 0) {
/* Timer is pending */
tmo = work->timer.expires - jiffies;
if (tmo < 0)
tmo = 0;
} else {
tmo = -1;
}
return tmo;
}
static int cw1200_resume_work(struct cw1200_common *priv,
struct delayed_work *work,
unsigned long tmo)
{
if ((long)tmo < 0)
return 1;
return queue_delayed_work(priv->workqueue, work, tmo);
}
int cw1200_can_suspend(struct cw1200_common *priv)
{
if (atomic_read(&priv->bh_rx)) {
wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
return 0;
}
return 1;
}
EXPORT_SYMBOL_GPL(cw1200_can_suspend);
int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct cw1200_common *priv = hw->priv;
struct cw1200_pm_state *pm_state = &priv->pm_state;
struct cw1200_suspend_state *state;
int ret;
spin_lock_bh(&pm_state->lock);
ret = timer_pending(&pm_state->stay_awake);
spin_unlock_bh(&pm_state->lock);
if (ret)
return -EAGAIN;
/* Do not suspend when datapath is not idle */
if (priv->tx_queue_stats.num_queued)
return -EBUSY;
/* Make sure there is no configuration requests in progress. */
if (!mutex_trylock(&priv->conf_mutex))
return -EBUSY;
/* Ensure pending operations are done.
* Note also that wow_suspend must return in ~2.5sec, before
* watchdog is triggered.
*/
if (priv->channel_switch_in_progress)
goto revert1;
/* Do not suspend when join is pending */
if (priv->join_pending)
goto revert1;
/* Do not suspend when scanning */
if (down_trylock(&priv->scan.lock))
goto revert1;
/* Lock TX. */
wsm_lock_tx_async(priv);
/* Wait to avoid possible race with bh code.
* But do not wait too long...
*/
if (wait_event_timeout(priv->bh_evt_wq,
!priv->hw_bufs_used, HZ / 10) <= 0)
goto revert2;
/* Set UDP filter */
wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr);
/* Set ethernet frame type filter */
wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr);
/* Allocate state */
state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
if (!state)
goto revert3;
/* Change to legacy PS while going to suspend */
if (!priv->vif->p2p &&
priv->join_status == CW1200_JOIN_STATUS_STA &&
priv->powersave_mode.mode != WSM_PSM_PS) {
state->prev_ps_mode = priv->powersave_mode.mode;
priv->powersave_mode.mode = WSM_PSM_PS;
cw1200_set_pm(priv, &priv->powersave_mode);
if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
!priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
goto revert3;
}
}
/* Store delayed work states. */
state->bss_loss_tmo =
cw1200_suspend_work(&priv->bss_loss_work);
state->join_tmo =
cw1200_suspend_work(&priv->join_timeout);
state->direct_probe =
cw1200_suspend_work(&priv->scan.probe_work);
state->link_id_gc =
cw1200_suspend_work(&priv->link_id_gc_work);
cancel_delayed_work_sync(&priv->clear_recent_scan_work);
atomic_set(&priv->recent_scan, 0);
/* Enable beacon skipping */
if (priv->join_status == CW1200_JOIN_STATUS_STA &&
priv->join_dtim_period &&
!priv->has_multicast_subscription) {
state->beacon_skipping = true;
wsm_set_beacon_wakeup_period(priv,
priv->join_dtim_period,
CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period);
}
/* Stop serving thread */
if (cw1200_bh_suspend(priv))
goto revert4;
ret = timer_pending(&priv->mcast_timeout);
if (ret)
goto revert5;
/* Store suspend state */
pm_state->suspend_state = state;
/* Enable IRQ wake */
ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true);
if (ret) {
wiphy_err(priv->hw->wiphy,
"PM request failed: %d. WoW is disabled.\n", ret);
cw1200_wow_resume(hw);
return -EBUSY;
}
/* Force resume if event is coming from the device. */
if (atomic_read(&priv->bh_rx)) {
cw1200_wow_resume(hw);
return -EAGAIN;
}
return 0;
revert5:
WARN_ON(cw1200_bh_resume(priv));
revert4:
cw1200_resume_work(priv, &priv->bss_loss_work,
state->bss_loss_tmo);
cw1200_resume_work(priv, &priv->join_timeout,
state->join_tmo);
cw1200_resume_work(priv, &priv->scan.probe_work,
state->direct_probe);
cw1200_resume_work(priv, &priv->link_id_gc_work,
state->link_id_gc);
kfree(state);
revert3:
wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
revert2:
wsm_unlock_tx(priv);
up(&priv->scan.lock);
revert1:
mutex_unlock(&priv->conf_mutex);
return -EBUSY;
}
int cw1200_wow_resume(struct ieee80211_hw *hw)
{
struct cw1200_common *priv = hw->priv;
struct cw1200_pm_state *pm_state = &priv->pm_state;
struct cw1200_suspend_state *state;
state = pm_state->suspend_state;
pm_state->suspend_state = NULL;
/* Disable IRQ wake */
priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false);
/* Scan.lock must be released before BH is resumed other way
* in case when BSS_LOST command arrived the processing of the
* command will be delayed.
*/
up(&priv->scan.lock);
/* Resume BH thread */
WARN_ON(cw1200_bh_resume(priv));
/* Restores previous PS mode */
if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) {
priv->powersave_mode.mode = state->prev_ps_mode;
cw1200_set_pm(priv, &priv->powersave_mode);
}
if (state->beacon_skipping) {
wsm_set_beacon_wakeup_period(priv, priv->beacon_int *
priv->join_dtim_period >
MAX_BEACON_SKIP_TIME_MS ? 1 :
priv->join_dtim_period, 0);
state->beacon_skipping = false;
}
/* Resume delayed work */
cw1200_resume_work(priv, &priv->bss_loss_work,
state->bss_loss_tmo);
cw1200_resume_work(priv, &priv->join_timeout,
state->join_tmo);
cw1200_resume_work(priv, &priv->scan.probe_work,
state->direct_probe);
cw1200_resume_work(priv, &priv->link_id_gc_work,
state->link_id_gc);
/* Remove UDP port filter */
wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
/* Remove ethernet frame type filter */
wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
/* Unlock datapath */
wsm_unlock_tx(priv);
/* Unlock configuration mutex */
mutex_unlock(&priv->conf_mutex);
/* Free memory */
kfree(state);
return 0;
}

View File

@ -0,0 +1,43 @@
/*
* Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef PM_H_INCLUDED
#define PM_H_INCLUDED
/* ******************************************************************** */
/* mac80211 API */
/* extern */ struct cw1200_common;
/* private */ struct cw1200_suspend_state;
struct cw1200_pm_state {
struct cw1200_suspend_state *suspend_state;
struct timer_list stay_awake;
struct platform_device *pm_dev;
spinlock_t lock; /* Protect access */
};
#ifdef CONFIG_PM
int cw1200_pm_init(struct cw1200_pm_state *pm,
struct cw1200_common *priv);
void cw1200_pm_deinit(struct cw1200_pm_state *pm);
int cw1200_wow_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan);
int cw1200_wow_resume(struct ieee80211_hw *hw);
int cw1200_can_suspend(struct cw1200_common *priv);
void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
unsigned long tmo);
#else
static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
unsigned long tmo) {
}
#endif
#endif

View File

@ -0,0 +1,583 @@
/*
* O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <net/mac80211.h>
#include <linux/sched.h>
#include "queue.h"
#include "cw1200.h"
#include "debug.h"
/* private */ struct cw1200_queue_item
{
struct list_head head;
struct sk_buff *skb;
u32 packet_id;
unsigned long queue_timestamp;
unsigned long xmit_timestamp;
struct cw1200_txpriv txpriv;
u8 generation;
};
static inline void __cw1200_queue_lock(struct cw1200_queue *queue)
{
struct cw1200_queue_stats *stats = queue->stats;
if (queue->tx_locked_cnt++ == 0) {
pr_debug("[TX] Queue %d is locked.\n",
queue->queue_id);
ieee80211_stop_queue(stats->priv->hw, queue->queue_id);
}
}
static inline void __cw1200_queue_unlock(struct cw1200_queue *queue)
{
struct cw1200_queue_stats *stats = queue->stats;
BUG_ON(!queue->tx_locked_cnt);
if (--queue->tx_locked_cnt == 0) {
pr_debug("[TX] Queue %d is unlocked.\n",
queue->queue_id);
ieee80211_wake_queue(stats->priv->hw, queue->queue_id);
}
}
static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation,
u8 *queue_id, u8 *item_generation,
u8 *item_id)
{
*item_id = (packet_id >> 0) & 0xFF;
*item_generation = (packet_id >> 8) & 0xFF;
*queue_id = (packet_id >> 16) & 0xFF;
*queue_generation = (packet_id >> 24) & 0xFF;
}
static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id,
u8 item_generation, u8 item_id)
{
return ((u32)item_id << 0) |
((u32)item_generation << 8) |
((u32)queue_id << 16) |
((u32)queue_generation << 24);
}
static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats,
struct list_head *gc_list)
{
struct cw1200_queue_item *item, *tmp;
list_for_each_entry_safe(item, tmp, gc_list, head) {
list_del(&item->head);
stats->skb_dtor(stats->priv, item->skb, &item->txpriv);
kfree(item);
}
}
static void cw1200_queue_register_post_gc(struct list_head *gc_list,
struct cw1200_queue_item *item)
{
struct cw1200_queue_item *gc_item;
gc_item = kmalloc(sizeof(struct cw1200_queue_item),
GFP_ATOMIC);
BUG_ON(!gc_item);
memcpy(gc_item, item, sizeof(struct cw1200_queue_item));
list_add_tail(&gc_item->head, gc_list);
}
static void __cw1200_queue_gc(struct cw1200_queue *queue,
struct list_head *head,
bool unlock)
{
struct cw1200_queue_stats *stats = queue->stats;
struct cw1200_queue_item *item = NULL, *tmp;
bool wakeup_stats = false;
list_for_each_entry_safe(item, tmp, &queue->queue, head) {
if (jiffies - item->queue_timestamp < queue->ttl)
break;
--queue->num_queued;
--queue->link_map_cache[item->txpriv.link_id];
spin_lock_bh(&stats->lock);
--stats->num_queued;
if (!--stats->link_map_cache[item->txpriv.link_id])
wakeup_stats = true;
spin_unlock_bh(&stats->lock);
cw1200_debug_tx_ttl(stats->priv);
cw1200_queue_register_post_gc(head, item);
item->skb = NULL;
list_move_tail(&item->head, &queue->free_pool);
}
if (wakeup_stats)
wake_up(&stats->wait_link_id_empty);
if (queue->overfull) {
if (queue->num_queued <= (queue->capacity >> 1)) {
queue->overfull = false;
if (unlock)
__cw1200_queue_unlock(queue);
} else if (item) {
unsigned long tmo = item->queue_timestamp + queue->ttl;
mod_timer(&queue->gc, tmo);
cw1200_pm_stay_awake(&stats->priv->pm_state,
tmo - jiffies);
}
}
}
static void cw1200_queue_gc(unsigned long arg)
{
LIST_HEAD(list);
struct cw1200_queue *queue =
(struct cw1200_queue *)arg;
spin_lock_bh(&queue->lock);
__cw1200_queue_gc(queue, &list, true);
spin_unlock_bh(&queue->lock);
cw1200_queue_post_gc(queue->stats, &list);
}
int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
size_t map_capacity,
cw1200_queue_skb_dtor_t skb_dtor,
struct cw1200_common *priv)
{
memset(stats, 0, sizeof(*stats));
stats->map_capacity = map_capacity;
stats->skb_dtor = skb_dtor;
stats->priv = priv;
spin_lock_init(&stats->lock);
init_waitqueue_head(&stats->wait_link_id_empty);
stats->link_map_cache = kzalloc(sizeof(int) * map_capacity,
GFP_KERNEL);
if (!stats->link_map_cache)
return -ENOMEM;
return 0;
}
int cw1200_queue_init(struct cw1200_queue *queue,
struct cw1200_queue_stats *stats,
u8 queue_id,
size_t capacity,
unsigned long ttl)
{
size_t i;
memset(queue, 0, sizeof(*queue));
queue->stats = stats;
queue->capacity = capacity;
queue->queue_id = queue_id;
queue->ttl = ttl;
INIT_LIST_HEAD(&queue->queue);
INIT_LIST_HEAD(&queue->pending);
INIT_LIST_HEAD(&queue->free_pool);
spin_lock_init(&queue->lock);
init_timer(&queue->gc);
queue->gc.data = (unsigned long)queue;
queue->gc.function = cw1200_queue_gc;
queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
GFP_KERNEL);
if (!queue->pool)
return -ENOMEM;
queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity,
GFP_KERNEL);
if (!queue->link_map_cache) {
kfree(queue->pool);
queue->pool = NULL;
return -ENOMEM;
}
for (i = 0; i < capacity; ++i)
list_add_tail(&queue->pool[i].head, &queue->free_pool);
return 0;
}
int cw1200_queue_clear(struct cw1200_queue *queue)
{
int i;
LIST_HEAD(gc_list);
struct cw1200_queue_stats *stats = queue->stats;
struct cw1200_queue_item *item, *tmp;
spin_lock_bh(&queue->lock);
queue->generation++;
list_splice_tail_init(&queue->queue, &queue->pending);
list_for_each_entry_safe(item, tmp, &queue->pending, head) {
WARN_ON(!item->skb);
cw1200_queue_register_post_gc(&gc_list, item);
item->skb = NULL;
list_move_tail(&item->head, &queue->free_pool);
}
queue->num_queued = 0;
queue->num_pending = 0;
spin_lock_bh(&stats->lock);
for (i = 0; i < stats->map_capacity; ++i) {
stats->num_queued -= queue->link_map_cache[i];
stats->link_map_cache[i] -= queue->link_map_cache[i];
queue->link_map_cache[i] = 0;
}
spin_unlock_bh(&stats->lock);
if (queue->overfull) {
queue->overfull = false;
__cw1200_queue_unlock(queue);
}
spin_unlock_bh(&queue->lock);
wake_up(&stats->wait_link_id_empty);
cw1200_queue_post_gc(stats, &gc_list);
return 0;
}
void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
{
kfree(stats->link_map_cache);
stats->link_map_cache = NULL;
}
void cw1200_queue_deinit(struct cw1200_queue *queue)
{
cw1200_queue_clear(queue);
del_timer_sync(&queue->gc);
INIT_LIST_HEAD(&queue->free_pool);
kfree(queue->pool);
kfree(queue->link_map_cache);
queue->pool = NULL;
queue->link_map_cache = NULL;
queue->capacity = 0;
}
size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
u32 link_id_map)
{
size_t ret;
int i, bit;
size_t map_capacity = queue->stats->map_capacity;
if (!link_id_map)
return 0;
spin_lock_bh(&queue->lock);
if (link_id_map == (u32)-1) {
ret = queue->num_queued - queue->num_pending;
} else {
ret = 0;
for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
if (link_id_map & bit)
ret += queue->link_map_cache[i];
}
}
spin_unlock_bh(&queue->lock);
return ret;
}
int cw1200_queue_put(struct cw1200_queue *queue,
struct sk_buff *skb,
struct cw1200_txpriv *txpriv)
{
int ret = 0;
LIST_HEAD(gc_list);
struct cw1200_queue_stats *stats = queue->stats;
if (txpriv->link_id >= queue->stats->map_capacity)
return -EINVAL;
spin_lock_bh(&queue->lock);
if (!WARN_ON(list_empty(&queue->free_pool))) {
struct cw1200_queue_item *item = list_first_entry(
&queue->free_pool, struct cw1200_queue_item, head);
BUG_ON(item->skb);
list_move_tail(&item->head, &queue->queue);
item->skb = skb;
item->txpriv = *txpriv;
item->generation = 0;
item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
queue->queue_id,
item->generation,
item - queue->pool);
item->queue_timestamp = jiffies;
++queue->num_queued;
++queue->link_map_cache[txpriv->link_id];
spin_lock_bh(&stats->lock);
++stats->num_queued;
++stats->link_map_cache[txpriv->link_id];
spin_unlock_bh(&stats->lock);
/* TX may happen in parallel sometimes.
* Leave extra queue slots so we don't overflow.
*/
if (queue->overfull == false &&
queue->num_queued >=
(queue->capacity - (num_present_cpus() - 1))) {
queue->overfull = true;
__cw1200_queue_lock(queue);
mod_timer(&queue->gc, jiffies);
}
} else {
ret = -ENOENT;
}
spin_unlock_bh(&queue->lock);
return ret;
}
int cw1200_queue_get(struct cw1200_queue *queue,
u32 link_id_map,
struct wsm_tx **tx,
struct ieee80211_tx_info **tx_info,
const struct cw1200_txpriv **txpriv)
{
int ret = -ENOENT;
struct cw1200_queue_item *item;
struct cw1200_queue_stats *stats = queue->stats;
bool wakeup_stats = false;
spin_lock_bh(&queue->lock);
list_for_each_entry(item, &queue->queue, head) {
if (link_id_map & BIT(item->txpriv.link_id)) {
ret = 0;
break;
}
}
if (!WARN_ON(ret)) {
*tx = (struct wsm_tx *)item->skb->data;
*tx_info = IEEE80211_SKB_CB(item->skb);
*txpriv = &item->txpriv;
(*tx)->packet_id = __cpu_to_le32(item->packet_id);
list_move_tail(&item->head, &queue->pending);
++queue->num_pending;
--queue->link_map_cache[item->txpriv.link_id];
item->xmit_timestamp = jiffies;
spin_lock_bh(&stats->lock);
--stats->num_queued;
if (!--stats->link_map_cache[item->txpriv.link_id])
wakeup_stats = true;
spin_unlock_bh(&stats->lock);
}
spin_unlock_bh(&queue->lock);
if (wakeup_stats)
wake_up(&stats->wait_link_id_empty);
return ret;
}
int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id)
{
int ret = 0;
u8 queue_generation, queue_id, item_generation, item_id;
struct cw1200_queue_item *item;
struct cw1200_queue_stats *stats = queue->stats;
cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
&item_generation, &item_id);
item = &queue->pool[item_id];
spin_lock_bh(&queue->lock);
BUG_ON(queue_id != queue->queue_id);
if (queue_generation != queue->generation) {
ret = -ENOENT;
} else if (item_id >= (unsigned) queue->capacity) {
WARN_ON(1);
ret = -EINVAL;
} else if (item->generation != item_generation) {
WARN_ON(1);
ret = -ENOENT;
} else {
--queue->num_pending;
++queue->link_map_cache[item->txpriv.link_id];
spin_lock_bh(&stats->lock);
++stats->num_queued;
++stats->link_map_cache[item->txpriv.link_id];
spin_unlock_bh(&stats->lock);
item->generation = ++item_generation;
item->packet_id = cw1200_queue_mk_packet_id(queue_generation,
queue_id,
item_generation,
item_id);
list_move(&item->head, &queue->queue);
}
spin_unlock_bh(&queue->lock);
return ret;
}
int cw1200_queue_requeue_all(struct cw1200_queue *queue)
{
struct cw1200_queue_item *item, *tmp;
struct cw1200_queue_stats *stats = queue->stats;
spin_lock_bh(&queue->lock);
list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) {
--queue->num_pending;
++queue->link_map_cache[item->txpriv.link_id];
spin_lock_bh(&stats->lock);
++stats->num_queued;
++stats->link_map_cache[item->txpriv.link_id];
spin_unlock_bh(&stats->lock);
++item->generation;
item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
queue->queue_id,
item->generation,
item - queue->pool);
list_move(&item->head, &queue->queue);
}
spin_unlock_bh(&queue->lock);
return 0;
}
int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id)
{
int ret = 0;
u8 queue_generation, queue_id, item_generation, item_id;
struct cw1200_queue_item *item;
struct cw1200_queue_stats *stats = queue->stats;
struct sk_buff *gc_skb = NULL;
struct cw1200_txpriv gc_txpriv;
cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
&item_generation, &item_id);
item = &queue->pool[item_id];
spin_lock_bh(&queue->lock);
BUG_ON(queue_id != queue->queue_id);
if (queue_generation != queue->generation) {
ret = -ENOENT;
} else if (item_id >= (unsigned) queue->capacity) {
WARN_ON(1);
ret = -EINVAL;
} else if (item->generation != item_generation) {
WARN_ON(1);
ret = -ENOENT;
} else {
gc_txpriv = item->txpriv;
gc_skb = item->skb;
item->skb = NULL;
--queue->num_pending;
--queue->num_queued;
++queue->num_sent;
++item->generation;
/* Do not use list_move_tail here, but list_move:
* try to utilize cache row.
*/
list_move(&item->head, &queue->free_pool);
if (queue->overfull &&
(queue->num_queued <= (queue->capacity >> 1))) {
queue->overfull = false;
__cw1200_queue_unlock(queue);
}
}
spin_unlock_bh(&queue->lock);
if (gc_skb)
stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv);
return ret;
}
int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
struct sk_buff **skb,
const struct cw1200_txpriv **txpriv)
{
int ret = 0;
u8 queue_generation, queue_id, item_generation, item_id;
struct cw1200_queue_item *item;
cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
&item_generation, &item_id);
item = &queue->pool[item_id];
spin_lock_bh(&queue->lock);
BUG_ON(queue_id != queue->queue_id);
if (queue_generation != queue->generation) {
ret = -ENOENT;
} else if (item_id >= (unsigned) queue->capacity) {
WARN_ON(1);
ret = -EINVAL;
} else if (item->generation != item_generation) {
WARN_ON(1);
ret = -ENOENT;
} else {
*skb = item->skb;
*txpriv = &item->txpriv;
}
spin_unlock_bh(&queue->lock);
return ret;
}
void cw1200_queue_lock(struct cw1200_queue *queue)
{
spin_lock_bh(&queue->lock);
__cw1200_queue_lock(queue);
spin_unlock_bh(&queue->lock);
}
void cw1200_queue_unlock(struct cw1200_queue *queue)
{
spin_lock_bh(&queue->lock);
__cw1200_queue_unlock(queue);
spin_unlock_bh(&queue->lock);
}
bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
unsigned long *timestamp,
u32 pending_frame_id)
{
struct cw1200_queue_item *item;
bool ret;
spin_lock_bh(&queue->lock);
ret = !list_empty(&queue->pending);
if (ret) {
list_for_each_entry(item, &queue->pending, head) {
if (item->packet_id != pending_frame_id)
if (time_before(item->xmit_timestamp,
*timestamp))
*timestamp = item->xmit_timestamp;
}
}
spin_unlock_bh(&queue->lock);
return ret;
}
bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
u32 link_id_map)
{
bool empty = true;
spin_lock_bh(&stats->lock);
if (link_id_map == (u32)-1) {
empty = stats->num_queued == 0;
} else {
int i;
for (i = 0; i < stats->map_capacity; ++i) {
if (link_id_map & BIT(i)) {
if (stats->link_map_cache[i]) {
empty = false;
break;
}
}
}
}
spin_unlock_bh(&stats->lock);
return empty;
}

View File

@ -0,0 +1,116 @@
/*
* O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_QUEUE_H_INCLUDED
#define CW1200_QUEUE_H_INCLUDED
/* private */ struct cw1200_queue_item;
/* extern */ struct sk_buff;
/* extern */ struct wsm_tx;
/* extern */ struct cw1200_common;
/* extern */ struct ieee80211_tx_queue_stats;
/* extern */ struct cw1200_txpriv;
/* forward */ struct cw1200_queue_stats;
typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv,
struct sk_buff *skb,
const struct cw1200_txpriv *txpriv);
struct cw1200_queue {
struct cw1200_queue_stats *stats;
size_t capacity;
size_t num_queued;
size_t num_pending;
size_t num_sent;
struct cw1200_queue_item *pool;
struct list_head queue;
struct list_head free_pool;
struct list_head pending;
int tx_locked_cnt;
int *link_map_cache;
bool overfull;
spinlock_t lock; /* Protect queue entry */
u8 queue_id;
u8 generation;
struct timer_list gc;
unsigned long ttl;
};
struct cw1200_queue_stats {
spinlock_t lock; /* Protect stats entry */
int *link_map_cache;
int num_queued;
size_t map_capacity;
wait_queue_head_t wait_link_id_empty;
cw1200_queue_skb_dtor_t skb_dtor;
struct cw1200_common *priv;
};
struct cw1200_txpriv {
u8 link_id;
u8 raw_link_id;
u8 tid;
u8 rate_id;
u8 offset;
};
int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
size_t map_capacity,
cw1200_queue_skb_dtor_t skb_dtor,
struct cw1200_common *priv);
int cw1200_queue_init(struct cw1200_queue *queue,
struct cw1200_queue_stats *stats,
u8 queue_id,
size_t capacity,
unsigned long ttl);
int cw1200_queue_clear(struct cw1200_queue *queue);
void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats);
void cw1200_queue_deinit(struct cw1200_queue *queue);
size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
u32 link_id_map);
int cw1200_queue_put(struct cw1200_queue *queue,
struct sk_buff *skb,
struct cw1200_txpriv *txpriv);
int cw1200_queue_get(struct cw1200_queue *queue,
u32 link_id_map,
struct wsm_tx **tx,
struct ieee80211_tx_info **tx_info,
const struct cw1200_txpriv **txpriv);
int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id);
int cw1200_queue_requeue_all(struct cw1200_queue *queue);
int cw1200_queue_remove(struct cw1200_queue *queue,
u32 packet_id);
int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
struct sk_buff **skb,
const struct cw1200_txpriv **txpriv);
void cw1200_queue_lock(struct cw1200_queue *queue);
void cw1200_queue_unlock(struct cw1200_queue *queue);
bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
unsigned long *timestamp,
u32 pending_frame_id);
bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
u32 link_id_map);
static inline u8 cw1200_queue_get_queue_id(u32 packet_id)
{
return (packet_id >> 16) & 0xFF;
}
static inline u8 cw1200_queue_get_generation(u32 packet_id)
{
return (packet_id >> 8) & 0xFF;
}
#endif /* CW1200_QUEUE_H_INCLUDED */

View File

@ -0,0 +1,461 @@
/*
* Scan implementation for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/sched.h>
#include "cw1200.h"
#include "scan.h"
#include "sta.h"
#include "pm.h"
static void cw1200_scan_restart_delayed(struct cw1200_common *priv);
static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
{
int ret, i;
int tmo = 2000;
switch (priv->join_status) {
case CW1200_JOIN_STATUS_PRE_STA:
case CW1200_JOIN_STATUS_JOINING:
return -EBUSY;
default:
break;
}
wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
scan->type, scan->num_channels, scan->flags);
for (i = 0; i < scan->num_channels; ++i)
tmo += scan->ch[i].max_chan_time + 10;
cancel_delayed_work_sync(&priv->clear_recent_scan_work);
atomic_set(&priv->scan.in_progress, 1);
atomic_set(&priv->recent_scan, 1);
cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000);
queue_delayed_work(priv->workqueue, &priv->scan.timeout,
tmo * HZ / 1000);
ret = wsm_scan(priv, scan);
if (ret) {
atomic_set(&priv->scan.in_progress, 0);
cancel_delayed_work_sync(&priv->scan.timeout);
cw1200_scan_restart_delayed(priv);
}
return ret;
}
int cw1200_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
{
struct cw1200_common *priv = hw->priv;
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};
int i, ret;
if (!priv->vif)
return -EINVAL;
/* Scan when P2P_GO corrupt firmware MiniAP mode */
if (priv->join_status == CW1200_JOIN_STATUS_AP)
return -EOPNOTSUPP;
if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
req->n_ssids = 0;
wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
req->n_ssids);
if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
return -EINVAL;
frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0,
req->ie_len);
if (!frame.skb)
return -ENOMEM;
if (req->ie_len)
memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len);
/* will be unlocked in cw1200_scan_work() */
down(&priv->scan.lock);
mutex_lock(&priv->conf_mutex);
ret = wsm_set_template_frame(priv, &frame);
if (!ret) {
/* Host want to be the probe responder. */
ret = wsm_set_probe_responder(priv, true);
}
if (ret) {
mutex_unlock(&priv->conf_mutex);
up(&priv->scan.lock);
dev_kfree_skb(frame.skb);
return ret;
}
wsm_lock_tx(priv);
BUG_ON(priv->scan.req);
priv->scan.req = req;
priv->scan.n_ssids = 0;
priv->scan.status = 0;
priv->scan.begin = &req->channels[0];
priv->scan.curr = priv->scan.begin;
priv->scan.end = &req->channels[req->n_channels];
priv->scan.output_power = priv->output_power;
for (i = 0; i < req->n_ssids; ++i) {
struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
dst->length = req->ssids[i].ssid_len;
++priv->scan.n_ssids;
}
mutex_unlock(&priv->conf_mutex);
if (frame.skb)
dev_kfree_skb(frame.skb);
queue_work(priv->workqueue, &priv->scan.work);
return 0;
}
void cw1200_scan_work(struct work_struct *work)
{
struct cw1200_common *priv = container_of(work, struct cw1200_common,
scan.work);
struct ieee80211_channel **it;
struct wsm_scan scan = {
.type = WSM_SCAN_TYPE_FOREGROUND,
.flags = WSM_SCAN_FLAG_SPLIT_METHOD,
};
bool first_run = (priv->scan.begin == priv->scan.curr &&
priv->scan.begin != priv->scan.end);
int i;
if (first_run) {
/* Firmware gets crazy if scan request is sent
* when STA is joined but not yet associated.
* Force unjoin in this case.
*/
if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
cw1200_join_timeout(&priv->join_timeout.work);
}
mutex_lock(&priv->conf_mutex);
if (first_run) {
if (priv->join_status == CW1200_JOIN_STATUS_STA &&
!(priv->powersave_mode.mode & WSM_PSM_PS)) {
struct wsm_set_pm pm = priv->powersave_mode;
pm.mode = WSM_PSM_PS;
cw1200_set_pm(priv, &pm);
} else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
/* FW bug: driver has to restart p2p-dev mode
* after scan
*/
cw1200_disable_listening(priv);
}
}
if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
if (priv->scan.output_power != priv->output_power)
wsm_set_output_power(priv, priv->output_power * 10);
if (priv->join_status == CW1200_JOIN_STATUS_STA &&
!(priv->powersave_mode.mode & WSM_PSM_PS))
cw1200_set_pm(priv, &priv->powersave_mode);
if (priv->scan.status < 0)
wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n",
priv->scan.status);
else if (priv->scan.req)
wiphy_dbg(priv->hw->wiphy,
"[SCAN] Scan completed.\n");
else
wiphy_dbg(priv->hw->wiphy,
"[SCAN] Scan canceled.\n");
priv->scan.req = NULL;
cw1200_scan_restart_delayed(priv);
wsm_unlock_tx(priv);
mutex_unlock(&priv->conf_mutex);
ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0);
up(&priv->scan.lock);
return;
} else {
struct ieee80211_channel *first = *priv->scan.curr;
for (it = priv->scan.curr + 1, i = 1;
it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
++it, ++i) {
if ((*it)->band != first->band)
break;
if (((*it)->flags ^ first->flags) &
IEEE80211_CHAN_PASSIVE_SCAN)
break;
if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
(*it)->max_power != first->max_power)
break;
}
scan.band = first->band;
if (priv->scan.req->no_cck)
scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
else
scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
scan.num_probes =
(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2;
scan.num_ssids = priv->scan.n_ssids;
scan.ssids = &priv->scan.ssids[0];
scan.num_channels = it - priv->scan.curr;
/* TODO: Is it optimal? */
scan.probe_delay = 100;
/* It is not stated in WSM specification, however
* FW team says that driver may not use FG scan
* when joined.
*/
if (priv->join_status == CW1200_JOIN_STATUS_STA) {
scan.type = WSM_SCAN_TYPE_BACKGROUND;
scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
}
scan.ch = kzalloc(
sizeof(struct wsm_scan_ch) * (it - priv->scan.curr),
GFP_KERNEL);
if (!scan.ch) {
priv->scan.status = -ENOMEM;
goto fail;
}
for (i = 0; i < scan.num_channels; ++i) {
scan.ch[i].number = priv->scan.curr[i]->hw_value;
if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
scan.ch[i].min_chan_time = 50;
scan.ch[i].max_chan_time = 100;
} else {
scan.ch[i].min_chan_time = 10;
scan.ch[i].max_chan_time = 25;
}
}
if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
priv->scan.output_power != first->max_power) {
priv->scan.output_power = first->max_power;
wsm_set_output_power(priv,
priv->scan.output_power * 10);
}
priv->scan.status = cw1200_scan_start(priv, &scan);
kfree(scan.ch);
if (priv->scan.status)
goto fail;
priv->scan.curr = it;
}
mutex_unlock(&priv->conf_mutex);
return;
fail:
priv->scan.curr = priv->scan.end;
mutex_unlock(&priv->conf_mutex);
queue_work(priv->workqueue, &priv->scan.work);
return;
}
static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
{
/* FW bug: driver has to restart p2p-dev mode after scan. */
if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
cw1200_enable_listening(priv);
cw1200_update_filtering(priv);
}
if (priv->delayed_unjoin) {
priv->delayed_unjoin = false;
if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
wsm_unlock_tx(priv);
} else if (priv->delayed_link_loss) {
wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
priv->delayed_link_loss = 0;
cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
}
}
static void cw1200_scan_complete(struct cw1200_common *priv)
{
queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
if (priv->scan.direct_probe) {
wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
cw1200_scan_restart_delayed(priv);
priv->scan.direct_probe = 0;
up(&priv->scan.lock);
wsm_unlock_tx(priv);
} else {
cw1200_scan_work(&priv->scan.work);
}
}
void cw1200_scan_failed_cb(struct cw1200_common *priv)
{
if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
/* STA is stopped. */
return;
if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
priv->scan.status = -EIO;
queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
}
}
void cw1200_scan_complete_cb(struct cw1200_common *priv,
struct wsm_scan_complete *arg)
{
if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
/* STA is stopped. */
return;
if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
priv->scan.status = 1;
queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
}
}
void cw1200_clear_recent_scan_work(struct work_struct *work)
{
struct cw1200_common *priv =
container_of(work, struct cw1200_common,
clear_recent_scan_work.work);
atomic_xchg(&priv->recent_scan, 0);
}
void cw1200_scan_timeout(struct work_struct *work)
{
struct cw1200_common *priv =
container_of(work, struct cw1200_common, scan.timeout.work);
if (atomic_xchg(&priv->scan.in_progress, 0)) {
if (priv->scan.status > 0) {
priv->scan.status = 0;
} else if (!priv->scan.status) {
wiphy_warn(priv->hw->wiphy,
"Timeout waiting for scan complete notification.\n");
priv->scan.status = -ETIMEDOUT;
priv->scan.curr = priv->scan.end;
wsm_stop_scan(priv);
}
cw1200_scan_complete(priv);
}
}
void cw1200_probe_work(struct work_struct *work)
{
struct cw1200_common *priv =
container_of(work, struct cw1200_common, scan.probe_work.work);
u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
struct cw1200_queue *queue = &priv->tx_queue[queue_id];
const struct cw1200_txpriv *txpriv;
struct wsm_tx *wsm;
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};
struct wsm_ssid ssids[1] = {{
.length = 0,
} };
struct wsm_scan_ch ch[1] = {{
.min_chan_time = 0,
.max_chan_time = 10,
} };
struct wsm_scan scan = {
.type = WSM_SCAN_TYPE_FOREGROUND,
.num_probes = 1,
.probe_delay = 0,
.num_channels = 1,
.ssids = ssids,
.ch = ch,
};
u8 *ies;
size_t ies_len;
int ret;
wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
mutex_lock(&priv->conf_mutex);
if (down_trylock(&priv->scan.lock)) {
/* Scan is already in progress. Requeue self. */
schedule();
queue_delayed_work(priv->workqueue,
&priv->scan.probe_work, HZ / 10);
mutex_unlock(&priv->conf_mutex);
return;
}
/* Make sure we still have a pending probe req */
if (cw1200_queue_get_skb(queue, priv->pending_frame_id,
&frame.skb, &txpriv)) {
up(&priv->scan.lock);
mutex_unlock(&priv->conf_mutex);
wsm_unlock_tx(priv);
return;
}
wsm = (struct wsm_tx *)frame.skb->data;
scan.max_tx_rate = wsm->max_tx_rate;
scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
if (priv->join_status == CW1200_JOIN_STATUS_STA ||
priv->join_status == CW1200_JOIN_STATUS_IBSS) {
scan.type = WSM_SCAN_TYPE_BACKGROUND;
scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
}
ch[0].number = priv->channel->hw_value;
skb_pull(frame.skb, txpriv->offset);
ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
if (ies_len) {
u8 *ssidie =
(u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
u8 *nextie = &ssidie[2 + ssidie[1]];
/* Remove SSID from the IE list. It has to be provided
* as a separate argument in cw1200_scan_start call
*/
/* Store SSID localy */
ssids[0].length = ssidie[1];
memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
scan.num_ssids = 1;
/* Remove SSID from IE list */
ssidie[1] = 0;
memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
skb_trim(frame.skb, frame.skb->len - ssids[0].length);
}
}
/* FW bug: driver has to restart p2p-dev mode after scan */
if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
cw1200_disable_listening(priv);
ret = wsm_set_template_frame(priv, &frame);
priv->scan.direct_probe = 1;
if (!ret) {
wsm_flush_tx(priv);
ret = cw1200_scan_start(priv, &scan);
}
mutex_unlock(&priv->conf_mutex);
skb_push(frame.skb, txpriv->offset);
if (!ret)
IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
if (ret) {
priv->scan.direct_probe = 0;
up(&priv->scan.lock);
wsm_unlock_tx(priv);
}
return;
}

View File

@ -0,0 +1,56 @@
/*
* Scan interface for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef SCAN_H_INCLUDED
#define SCAN_H_INCLUDED
#include <linux/semaphore.h>
#include "wsm.h"
/* external */ struct sk_buff;
/* external */ struct cfg80211_scan_request;
/* external */ struct ieee80211_channel;
/* external */ struct ieee80211_hw;
/* external */ struct work_struct;
struct cw1200_scan {
struct semaphore lock;
struct work_struct work;
struct delayed_work timeout;
struct cfg80211_scan_request *req;
struct ieee80211_channel **begin;
struct ieee80211_channel **curr;
struct ieee80211_channel **end;
struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
int output_power;
int n_ssids;
int status;
atomic_t in_progress;
/* Direct probe requests workaround */
struct delayed_work probe_work;
int direct_probe;
};
int cw1200_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
void cw1200_scan_work(struct work_struct *work);
void cw1200_scan_timeout(struct work_struct *work);
void cw1200_clear_recent_scan_work(struct work_struct *work);
void cw1200_scan_complete_cb(struct cw1200_common *priv,
struct wsm_scan_complete *arg);
void cw1200_scan_failed_cb(struct cw1200_common *priv);
/* ******************************************************************** */
/* Raw probe requests TX workaround */
void cw1200_probe_work(struct work_struct *work);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
/*
* Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef STA_H_INCLUDED
#define STA_H_INCLUDED
/* ******************************************************************** */
/* mac80211 API */
int cw1200_start(struct ieee80211_hw *dev);
void cw1200_stop(struct ieee80211_hw *dev);
int cw1200_add_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif);
void cw1200_remove_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif);
int cw1200_change_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
enum nl80211_iftype new_type,
bool p2p);
int cw1200_config(struct ieee80211_hw *dev, u32 changed);
void cw1200_configure_filter(struct ieee80211_hw *dev,
unsigned int changed_flags,
unsigned int *total_flags,
u64 multicast);
int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
u16 queue, const struct ieee80211_tx_queue_params *params);
int cw1200_get_stats(struct ieee80211_hw *dev,
struct ieee80211_low_level_stats *stats);
int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key);
int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
struct netdev_hw_addr_list *mc_list);
int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
/* ******************************************************************** */
/* WSM callbacks */
void cw1200_join_complete_cb(struct cw1200_common *priv,
struct wsm_join_complete *arg);
/* ******************************************************************** */
/* WSM events */
void cw1200_free_event_queue(struct cw1200_common *priv);
void cw1200_event_handler(struct work_struct *work);
void cw1200_bss_loss_work(struct work_struct *work);
void cw1200_bss_params_work(struct work_struct *work);
void cw1200_keep_alive_work(struct work_struct *work);
void cw1200_tx_failure_work(struct work_struct *work);
void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good,
int bad);
static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
int init, int good, int bad)
{
spin_lock(&priv->bss_loss_lock);
__cw1200_cqm_bssloss_sm(priv, init, good, bad);
spin_unlock(&priv->bss_loss_lock);
}
/* ******************************************************************** */
/* Internal API */
int cw1200_setup_mac(struct cw1200_common *priv);
void cw1200_join_timeout(struct work_struct *work);
void cw1200_unjoin_work(struct work_struct *work);
void cw1200_join_complete_work(struct work_struct *work);
void cw1200_wep_key_work(struct work_struct *work);
void cw1200_update_listening(struct cw1200_common *priv, bool enabled);
void cw1200_update_filtering(struct cw1200_common *priv);
void cw1200_update_filtering_work(struct work_struct *work);
void cw1200_set_beacon_wakeup_period_work(struct work_struct *work);
int cw1200_enable_listening(struct cw1200_common *priv);
int cw1200_disable_listening(struct cw1200_common *priv);
int cw1200_set_uapsd_param(struct cw1200_common *priv,
const struct wsm_edca_params *arg);
void cw1200_ba_work(struct work_struct *work);
void cw1200_ba_timer(unsigned long arg);
/* AP stuffs */
int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
bool set);
int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
enum sta_notify_cmd notify_cmd,
struct ieee80211_sta *sta);
void cw1200_bss_info_changed(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
u32 changed);
int cw1200_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn,
u8 buf_size);
void cw1200_suspend_resume(struct cw1200_common *priv,
struct wsm_suspend_resume *arg);
void cw1200_set_tim_work(struct work_struct *work);
void cw1200_set_cts_work(struct work_struct *work);
void cw1200_multicast_start_work(struct work_struct *work);
void cw1200_multicast_stop_work(struct work_struct *work);
void cw1200_mcast_timeout(unsigned long arg);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
/*
* Datapath interface for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_TXRX_H
#define CW1200_TXRX_H
#include <linux/list.h>
/* extern */ struct ieee80211_hw;
/* extern */ struct sk_buff;
/* extern */ struct wsm_tx;
/* extern */ struct wsm_rx;
/* extern */ struct wsm_tx_confirm;
/* extern */ struct cw1200_txpriv;
struct tx_policy {
union {
__le32 tbl[3];
u8 raw[12];
};
u8 defined;
u8 usage_count;
u8 retry_count;
u8 uploaded;
};
struct tx_policy_cache_entry {
struct tx_policy policy;
struct list_head link;
};
#define TX_POLICY_CACHE_SIZE (8)
struct tx_policy_cache {
struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
struct list_head used;
struct list_head free;
spinlock_t lock; /* Protect policy cache */
};
/* ******************************************************************** */
/* TX policy cache */
/* Intention of TX policy cache is an overcomplicated WSM API.
* Device does not accept per-PDU tx retry sequence.
* It uses "tx retry policy id" instead, so driver code has to sync
* linux tx retry sequences with a retry policy table in the device.
*/
void tx_policy_init(struct cw1200_common *priv);
void tx_policy_upload_work(struct work_struct *work);
void tx_policy_clean(struct cw1200_common *priv);
/* ******************************************************************** */
/* TX implementation */
u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv,
u32 rates);
void cw1200_tx(struct ieee80211_hw *dev,
struct ieee80211_tx_control *control,
struct sk_buff *skb);
void cw1200_skb_dtor(struct cw1200_common *priv,
struct sk_buff *skb,
const struct cw1200_txpriv *txpriv);
/* ******************************************************************** */
/* WSM callbacks */
void cw1200_tx_confirm_cb(struct cw1200_common *priv,
int link_id,
struct wsm_tx_confirm *arg);
void cw1200_rx_cb(struct cw1200_common *priv,
struct wsm_rx *arg,
int link_id,
struct sk_buff **skb_p);
/* ******************************************************************** */
/* Timeout */
void cw1200_tx_timeout(struct work_struct *work);
/* ******************************************************************** */
/* Security */
int cw1200_alloc_key(struct cw1200_common *priv);
void cw1200_free_key(struct cw1200_common *priv, int idx);
void cw1200_free_keys(struct cw1200_common *priv);
int cw1200_upload_keys(struct cw1200_common *priv);
/* ******************************************************************** */
/* Workaround for WFD test case 6.1.10 */
void cw1200_link_id_reset(struct work_struct *work);
#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac);
int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac);
void cw1200_link_id_work(struct work_struct *work);
void cw1200_link_id_gc_work(struct work_struct *work);
#endif /* CW1200_TXRX_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6242,8 +6242,6 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
if ((val & 0x0000ff00) != 0) if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff); pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
pci_set_power_state(pci_dev, PCI_D0);
if (!ipw2100_hw_is_adapter_in_system(dev)) { if (!ipw2100_hw_is_adapter_in_system(dev)) {
printk(KERN_WARNING DRV_NAME printk(KERN_WARNING DRV_NAME
"Device not found via register read.\n"); "Device not found via register read.\n");

View File

@ -1347,14 +1347,6 @@ struct il_rx_mpdu_res_start {
#define TX_CMD_SEC_SHIFT 6 #define TX_CMD_SEC_SHIFT 6
#define TX_CMD_SEC_KEY128 0x08 #define TX_CMD_SEC_KEY128 0x08
/*
* security overhead sizes
*/
#define WEP_IV_LEN 4
#define WEP_ICV_LEN 4
#define CCMP_MIC_LEN 8
#define TKIP_ICV_LEN 4
/* /*
* C_TX = 0x1c (command) * C_TX = 0x1c (command)
*/ */

View File

@ -76,13 +76,16 @@
#define IWL_INVALID_STATION 255 #define IWL_INVALID_STATION 255
/* device operations */ /* device operations */
extern struct iwl_lib_ops iwl1000_lib; extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg;
extern struct iwl_lib_ops iwl2000_lib; extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg;
extern struct iwl_lib_ops iwl2030_lib; extern const struct iwl_dvm_cfg iwl_dvm_105_cfg;
extern struct iwl_lib_ops iwl5000_lib; extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg;
extern struct iwl_lib_ops iwl5150_lib; extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg;
extern struct iwl_lib_ops iwl6000_lib; extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg;
extern struct iwl_lib_ops iwl6030_lib; extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg;
extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg;
extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg;
extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
#define TIME_UNIT 1024 #define TIME_UNIT 1024
@ -291,8 +294,8 @@ void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena);
static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv)
{ {
return priv->cfg->bt_params && return priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist; priv->lib->bt_params->advanced_bt_coexist;
} }
#ifdef CONFIG_IWLWIFI_DEBUG #ifdef CONFIG_IWLWIFI_DEBUG

View File

@ -521,7 +521,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
if (priv->cfg->base_params->hd_v2) { if (priv->lib->hd_v2) {
cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
@ -895,7 +895,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv,
continue; continue;
} }
delta_g = (priv->cfg->base_params->chain_noise_scale * delta_g = (priv->lib->chain_noise_scale *
((s32)average_noise[default_chain] - ((s32)average_noise[default_chain] -
(s32)average_noise[i])) / 1500; (s32)average_noise[i])) / 1500;
@ -1051,8 +1051,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
return; return;
/* Analyze signal for disconnected antenna */ /* Analyze signal for disconnected antenna */
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) { priv->lib->bt_params->advanced_bt_coexist) {
/* Disable disconnected antenna algorithm for advanced /* Disable disconnected antenna algorithm for advanced
bt coex, assuming valid antennas are connected */ bt coex, assuming valid antennas are connected */
data->active_chains = priv->nvm_data->valid_rx_ant; data->active_chains = priv->nvm_data->valid_rx_ant;

View File

@ -838,10 +838,6 @@ struct iwl_qosparam_cmd {
#define STA_MODIFY_DELBA_TID_MSK 0x10 #define STA_MODIFY_DELBA_TID_MSK 0x10
#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 #define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20
/* Receiver address (actually, Rx station's index into station table),
* combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid))
/* agn */ /* agn */
struct iwl_keyinfo { struct iwl_keyinfo {
__le16 key_flags; __le16 key_flags;
@ -1224,14 +1220,6 @@ struct iwl_rx_mpdu_res_start {
#define TX_CMD_SEC_SHIFT 6 #define TX_CMD_SEC_SHIFT 6
#define TX_CMD_SEC_KEY128 0x08 #define TX_CMD_SEC_KEY128 0x08
/*
* security overhead sizes
*/
#define WEP_IV_LEN 4
#define WEP_ICV_LEN 4
#define CCMP_MIC_LEN 8
#define TKIP_ICV_LEN 4
/* /*
* REPLY_TX = 0x1c (command) * REPLY_TX = 0x1c (command)
*/ */

View File

@ -568,16 +568,61 @@ struct iwl_hw_params {
const struct iwl_sensitivity_ranges *sens; const struct iwl_sensitivity_ranges *sens;
}; };
struct iwl_lib_ops { /**
/* set hw dependent parameters */ * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters
* @advanced_bt_coexist: support advanced bt coexist
* @bt_init_traffic_load: specify initial bt traffic load
* @bt_prio_boost: default bt priority boost value
* @agg_time_limit: maximum number of uSec in aggregation
* @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
*/
struct iwl_dvm_bt_params {
bool advanced_bt_coexist;
u8 bt_init_traffic_load;
u32 bt_prio_boost;
u16 agg_time_limit;
bool bt_sco_disable;
bool bt_session_2;
};
/**
* struct iwl_dvm_cfg - DVM firmware specific device configuration
* @set_hw_params: set hardware parameters
* @set_channel_switch: send channel switch command
* @nic_config: apply device specific configuration
* @temperature: read temperature
* @adv_thermal_throttle: support advance thermal throttle
* @support_ct_kill_exit: support ct kill exit condition
* @plcp_delta_threshold: plcp error rate threshold used to trigger
* radio tuning when there is a high receiving plcp error rate
* @chain_noise_scale: default chain noise scale used for gain computation
* @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
* @no_idle_support: do not support idle mode
* @bt_params: pointer to BT parameters
* @need_temp_offset_calib: need to perform temperature offset calibration
* @no_xtal_calib: some devices do not need crystal calibration data,
* don't send it to those
* @temp_offset_v2: support v2 of temperature offset calibration
* @adv_pm: advanced power management
*/
struct iwl_dvm_cfg {
void (*set_hw_params)(struct iwl_priv *priv); void (*set_hw_params)(struct iwl_priv *priv);
int (*set_channel_switch)(struct iwl_priv *priv, int (*set_channel_switch)(struct iwl_priv *priv,
struct ieee80211_channel_switch *ch_switch); struct ieee80211_channel_switch *ch_switch);
/* device specific configuration */
void (*nic_config)(struct iwl_priv *priv); void (*nic_config)(struct iwl_priv *priv);
/* temperature */
void (*temperature)(struct iwl_priv *priv); void (*temperature)(struct iwl_priv *priv);
const struct iwl_dvm_bt_params *bt_params;
s32 chain_noise_scale;
u8 plcp_delta_threshold;
bool adv_thermal_throttle;
bool support_ct_kill_exit;
bool hd_v2;
bool no_idle_support;
bool need_temp_offset_calib;
bool no_xtal_calib;
bool temp_offset_v2;
bool adv_pm;
}; };
struct iwl_wipan_noa_data { struct iwl_wipan_noa_data {
@ -610,7 +655,7 @@ struct iwl_priv {
struct device *dev; /* for debug prints only */ struct device *dev; /* for debug prints only */
const struct iwl_cfg *cfg; const struct iwl_cfg *cfg;
const struct iwl_fw *fw; const struct iwl_fw *fw;
const struct iwl_lib_ops *lib; const struct iwl_dvm_cfg *lib;
unsigned long status; unsigned long status;
spinlock_t sta_lock; spinlock_t sta_lock;

View File

@ -174,10 +174,13 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv)
priv->hw_params.sens = &iwl1000_sensitivity; priv->hw_params.sens = &iwl1000_sensitivity;
} }
struct iwl_lib_ops iwl1000_lib = { const struct iwl_dvm_cfg iwl_dvm_1000_cfg = {
.set_hw_params = iwl1000_hw_set_hw_params, .set_hw_params = iwl1000_hw_set_hw_params,
.nic_config = iwl1000_nic_config, .nic_config = iwl1000_nic_config,
.temperature = iwlagn_temperature, .temperature = iwlagn_temperature,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
}; };
@ -232,16 +235,56 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv)
priv->hw_params.sens = &iwl2000_sensitivity; priv->hw_params.sens = &iwl2000_sensitivity;
} }
struct iwl_lib_ops iwl2000_lib = { const struct iwl_dvm_cfg iwl_dvm_2000_cfg = {
.set_hw_params = iwl2000_hw_set_hw_params, .set_hw_params = iwl2000_hw_set_hw_params,
.nic_config = iwl2000_nic_config, .nic_config = iwl2000_nic_config,
.temperature = iwlagn_temperature, .temperature = iwlagn_temperature,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.hd_v2 = true,
.need_temp_offset_calib = true,
.temp_offset_v2 = true,
}; };
struct iwl_lib_ops iwl2030_lib = { const struct iwl_dvm_cfg iwl_dvm_105_cfg = {
.set_hw_params = iwl2000_hw_set_hw_params, .set_hw_params = iwl2000_hw_set_hw_params,
.nic_config = iwl2000_nic_config, .nic_config = iwl2000_nic_config,
.temperature = iwlagn_temperature, .temperature = iwlagn_temperature,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.hd_v2 = true,
.need_temp_offset_calib = true,
.temp_offset_v2 = true,
.adv_pm = true,
};
static const struct iwl_dvm_bt_params iwl2030_bt_params = {
/* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
.advanced_bt_coexist = true,
.agg_time_limit = BT_AGG_THRESHOLD_DEF,
.bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
.bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
.bt_sco_disable = true,
.bt_session_2 = true,
};
const struct iwl_dvm_cfg iwl_dvm_2030_cfg = {
.set_hw_params = iwl2000_hw_set_hw_params,
.nic_config = iwl2000_nic_config,
.temperature = iwlagn_temperature,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.hd_v2 = true,
.bt_params = &iwl2030_bt_params,
.need_temp_offset_calib = true,
.temp_offset_v2 = true,
.adv_pm = true,
}; };
/* /*
@ -420,16 +463,23 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
return iwl_dvm_send_cmd(priv, &hcmd); return iwl_dvm_send_cmd(priv, &hcmd);
} }
struct iwl_lib_ops iwl5000_lib = { const struct iwl_dvm_cfg iwl_dvm_5000_cfg = {
.set_hw_params = iwl5000_hw_set_hw_params, .set_hw_params = iwl5000_hw_set_hw_params,
.set_channel_switch = iwl5000_hw_channel_switch, .set_channel_switch = iwl5000_hw_channel_switch,
.temperature = iwlagn_temperature, .temperature = iwlagn_temperature,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.no_idle_support = true,
}; };
struct iwl_lib_ops iwl5150_lib = { const struct iwl_dvm_cfg iwl_dvm_5150_cfg = {
.set_hw_params = iwl5150_hw_set_hw_params, .set_hw_params = iwl5150_hw_set_hw_params,
.set_channel_switch = iwl5000_hw_channel_switch, .set_channel_switch = iwl5000_hw_channel_switch,
.temperature = iwl5150_temperature, .temperature = iwl5150_temperature,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.no_idle_support = true,
.no_xtal_calib = true,
}; };
@ -584,16 +634,59 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
return err; return err;
} }
struct iwl_lib_ops iwl6000_lib = { const struct iwl_dvm_cfg iwl_dvm_6000_cfg = {
.set_hw_params = iwl6000_hw_set_hw_params, .set_hw_params = iwl6000_hw_set_hw_params,
.set_channel_switch = iwl6000_hw_channel_switch, .set_channel_switch = iwl6000_hw_channel_switch,
.nic_config = iwl6000_nic_config, .nic_config = iwl6000_nic_config,
.temperature = iwlagn_temperature, .temperature = iwlagn_temperature,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
}; };
struct iwl_lib_ops iwl6030_lib = { const struct iwl_dvm_cfg iwl_dvm_6005_cfg = {
.set_hw_params = iwl6000_hw_set_hw_params, .set_hw_params = iwl6000_hw_set_hw_params,
.set_channel_switch = iwl6000_hw_channel_switch, .set_channel_switch = iwl6000_hw_channel_switch,
.nic_config = iwl6000_nic_config, .nic_config = iwl6000_nic_config,
.temperature = iwlagn_temperature, .temperature = iwlagn_temperature,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.need_temp_offset_calib = true,
};
const struct iwl_dvm_cfg iwl_dvm_6050_cfg = {
.set_hw_params = iwl6000_hw_set_hw_params,
.set_channel_switch = iwl6000_hw_channel_switch,
.nic_config = iwl6000_nic_config,
.temperature = iwlagn_temperature,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1500,
};
static const struct iwl_dvm_bt_params iwl6000_bt_params = {
/* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
.advanced_bt_coexist = true,
.agg_time_limit = BT_AGG_THRESHOLD_DEF,
.bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
.bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
.bt_sco_disable = true,
};
const struct iwl_dvm_cfg iwl_dvm_6030_cfg = {
.set_hw_params = iwl6000_hw_set_hw_params,
.set_channel_switch = iwl6000_hw_channel_switch,
.nic_config = iwl6000_nic_config,
.temperature = iwlagn_temperature,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.bt_params = &iwl6000_bt_params,
.need_temp_offset_calib = true,
.adv_pm = true,
}; };

View File

@ -254,23 +254,23 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) !=
sizeof(basic.bt3_lookup_table)); sizeof(basic.bt3_lookup_table));
if (priv->cfg->bt_params) { if (priv->lib->bt_params) {
/* /*
* newer generation of devices (2000 series and newer) * newer generation of devices (2000 series and newer)
* use the version 2 of the bt command * use the version 2 of the bt command
* we need to make sure sending the host command * we need to make sure sending the host command
* with correct data structure to avoid uCode assert * with correct data structure to avoid uCode assert
*/ */
if (priv->cfg->bt_params->bt_session_2) { if (priv->lib->bt_params->bt_session_2) {
bt_cmd_v2.prio_boost = cpu_to_le32( bt_cmd_v2.prio_boost = cpu_to_le32(
priv->cfg->bt_params->bt_prio_boost); priv->lib->bt_params->bt_prio_boost);
bt_cmd_v2.tx_prio_boost = 0; bt_cmd_v2.tx_prio_boost = 0;
bt_cmd_v2.rx_prio_boost = 0; bt_cmd_v2.rx_prio_boost = 0;
} else { } else {
/* older version only has 8 bits */ /* older version only has 8 bits */
WARN_ON(priv->cfg->bt_params->bt_prio_boost & ~0xFF); WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF);
bt_cmd_v1.prio_boost = bt_cmd_v1.prio_boost =
priv->cfg->bt_params->bt_prio_boost; priv->lib->bt_params->bt_prio_boost;
bt_cmd_v1.tx_prio_boost = 0; bt_cmd_v1.tx_prio_boost = 0;
bt_cmd_v1.rx_prio_boost = 0; bt_cmd_v1.rx_prio_boost = 0;
} }
@ -330,7 +330,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
priv->bt_full_concurrent ? priv->bt_full_concurrent ?
"full concurrency" : "3-wire"); "full concurrency" : "3-wire");
if (priv->cfg->bt_params->bt_session_2) { if (priv->lib->bt_params->bt_session_2) {
memcpy(&bt_cmd_v2.basic, &basic, memcpy(&bt_cmd_v2.basic, &basic,
sizeof(basic)); sizeof(basic));
ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
@ -758,8 +758,8 @@ static bool is_single_rx_stream(struct iwl_priv *priv)
*/ */
static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
{ {
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist && priv->lib->bt_params->advanced_bt_coexist &&
(priv->bt_full_concurrent || (priv->bt_full_concurrent ||
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
/* /*
@ -830,8 +830,8 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
else else
active_chains = priv->nvm_data->valid_rx_ant; active_chains = priv->nvm_data->valid_rx_ant;
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist && priv->lib->bt_params->advanced_bt_coexist &&
(priv->bt_full_concurrent || (priv->bt_full_concurrent ||
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
/* /*

View File

@ -426,7 +426,11 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
if (ret) if (ret)
goto error; goto error;
iwl_trans_d3_suspend(priv->trans); /* let the ucode operate on its own */
iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
iwl_trans_d3_suspend(priv->trans, false);
goto out; goto out;
@ -500,7 +504,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
/* we'll clear ctx->vif during iwlagn_prepare_restart() */ /* we'll clear ctx->vif during iwlagn_prepare_restart() */
vif = ctx->vif; vif = ctx->vif;
ret = iwl_trans_d3_resume(priv->trans, &d3_status); ret = iwl_trans_d3_resume(priv->trans, &d3_status, false);
if (ret) if (ret)
goto out_unlock; goto out_unlock;
@ -509,6 +513,10 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
goto out_unlock; goto out_unlock;
} }
/* uCode is no longer operating by itself */
iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
base = priv->device_pointers.error_event_table; base = priv->device_pointers.error_event_table;
if (!iwlagn_hw_valid_rtc_data_addr(base)) { if (!iwlagn_hw_valid_rtc_data_addr(base)) {
IWL_WARN(priv, "Invalid error table during resume!\n"); IWL_WARN(priv, "Invalid error table during resume!\n");
@ -1276,8 +1284,8 @@ static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
IWL_DEBUG_MAC80211(priv, "enter\n"); IWL_DEBUG_MAC80211(priv, "enter\n");
mutex_lock(&priv->mutex); mutex_lock(&priv->mutex);
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) { priv->lib->bt_params->advanced_bt_coexist) {
if (rssi_event == RSSI_EVENT_LOW) if (rssi_event == RSSI_EVENT_LOW)
priv->bt_enable_pspoll = true; priv->bt_enable_pspoll = true;
else if (rssi_event == RSSI_EVENT_HIGH) else if (rssi_event == RSSI_EVENT_HIGH)
@ -1387,7 +1395,7 @@ static int iwl_setup_interface(struct iwl_priv *priv,
return err; return err;
} }
if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist &&
vif->type == NL80211_IFTYPE_ADHOC) { vif->type == NL80211_IFTYPE_ADHOC) {
/* /*
* pretend to have high BT traffic as long as we * pretend to have high BT traffic as long as we

View File

@ -615,7 +615,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
priv->thermal_throttle.ct_kill_toggle = false; priv->thermal_throttle.ct_kill_toggle = false;
if (priv->cfg->base_params->support_ct_kill_exit) { if (priv->lib->support_ct_kill_exit) {
adv_cmd.critical_temperature_enter = adv_cmd.critical_temperature_enter =
cpu_to_le32(priv->hw_params.ct_kill_threshold); cpu_to_le32(priv->hw_params.ct_kill_threshold);
adv_cmd.critical_temperature_exit = adv_cmd.critical_temperature_exit =
@ -732,10 +732,10 @@ int iwl_alive_start(struct iwl_priv *priv)
} }
/* download priority table before any calibration request */ /* download priority table before any calibration request */
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) { priv->lib->bt_params->advanced_bt_coexist) {
/* Configure Bluetooth device coexistence support */ /* Configure Bluetooth device coexistence support */
if (priv->cfg->bt_params->bt_sco_disable) if (priv->lib->bt_params->bt_sco_disable)
priv->bt_enable_pspoll = false; priv->bt_enable_pspoll = false;
else else
priv->bt_enable_pspoll = true; priv->bt_enable_pspoll = true;
@ -873,9 +873,9 @@ void iwl_down(struct iwl_priv *priv)
priv->bt_status = 0; priv->bt_status = 0;
priv->cur_rssi_ctx = NULL; priv->cur_rssi_ctx = NULL;
priv->bt_is_sco = 0; priv->bt_is_sco = 0;
if (priv->cfg->bt_params) if (priv->lib->bt_params)
priv->bt_traffic_load = priv->bt_traffic_load =
priv->cfg->bt_params->bt_init_traffic_load; priv->lib->bt_params->bt_init_traffic_load;
else else
priv->bt_traffic_load = 0; priv->bt_traffic_load = 0;
priv->bt_full_concurrent = false; priv->bt_full_concurrent = false;
@ -1058,7 +1058,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
iwl_setup_scan_deferred_work(priv); iwl_setup_scan_deferred_work(priv);
if (priv->cfg->bt_params) if (priv->lib->bt_params)
iwlagn_bt_setup_deferred_work(priv); iwlagn_bt_setup_deferred_work(priv);
init_timer(&priv->statistics_periodic); init_timer(&priv->statistics_periodic);
@ -1072,7 +1072,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
void iwl_cancel_deferred_work(struct iwl_priv *priv) void iwl_cancel_deferred_work(struct iwl_priv *priv)
{ {
if (priv->cfg->bt_params) if (priv->lib->bt_params)
iwlagn_bt_cancel_deferred_work(priv); iwlagn_bt_cancel_deferred_work(priv);
cancel_work_sync(&priv->run_time_calib_work); cancel_work_sync(&priv->run_time_calib_work);
@ -1098,8 +1098,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
priv->band = IEEE80211_BAND_2GHZ; priv->band = IEEE80211_BAND_2GHZ;
priv->plcp_delta_threshold = priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold;
priv->cfg->base_params->plcp_delta_threshold;
priv->iw_mode = NL80211_IFTYPE_STATION; priv->iw_mode = NL80211_IFTYPE_STATION;
priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
@ -1116,8 +1115,8 @@ static int iwl_init_drv(struct iwl_priv *priv)
iwl_init_scan_params(priv); iwl_init_scan_params(priv);
/* init bt coex */ /* init bt coex */
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) { priv->lib->bt_params->advanced_bt_coexist) {
priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
@ -1264,31 +1263,37 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
switch (priv->cfg->device_family) { switch (priv->cfg->device_family) {
case IWL_DEVICE_FAMILY_1000: case IWL_DEVICE_FAMILY_1000:
case IWL_DEVICE_FAMILY_100: case IWL_DEVICE_FAMILY_100:
priv->lib = &iwl1000_lib; priv->lib = &iwl_dvm_1000_cfg;
break; break;
case IWL_DEVICE_FAMILY_2000: case IWL_DEVICE_FAMILY_2000:
priv->lib = &iwl_dvm_2000_cfg;
break;
case IWL_DEVICE_FAMILY_105: case IWL_DEVICE_FAMILY_105:
priv->lib = &iwl2000_lib; priv->lib = &iwl_dvm_105_cfg;
break; break;
case IWL_DEVICE_FAMILY_2030: case IWL_DEVICE_FAMILY_2030:
case IWL_DEVICE_FAMILY_135: case IWL_DEVICE_FAMILY_135:
priv->lib = &iwl2030_lib; priv->lib = &iwl_dvm_2030_cfg;
break; break;
case IWL_DEVICE_FAMILY_5000: case IWL_DEVICE_FAMILY_5000:
priv->lib = &iwl5000_lib; priv->lib = &iwl_dvm_5000_cfg;
break; break;
case IWL_DEVICE_FAMILY_5150: case IWL_DEVICE_FAMILY_5150:
priv->lib = &iwl5150_lib; priv->lib = &iwl_dvm_5150_cfg;
break; break;
case IWL_DEVICE_FAMILY_6000: case IWL_DEVICE_FAMILY_6000:
case IWL_DEVICE_FAMILY_6005:
case IWL_DEVICE_FAMILY_6000i: case IWL_DEVICE_FAMILY_6000i:
priv->lib = &iwl_dvm_6000_cfg;
break;
case IWL_DEVICE_FAMILY_6005:
priv->lib = &iwl_dvm_6005_cfg;
break;
case IWL_DEVICE_FAMILY_6050: case IWL_DEVICE_FAMILY_6050:
case IWL_DEVICE_FAMILY_6150: case IWL_DEVICE_FAMILY_6150:
priv->lib = &iwl6000_lib; priv->lib = &iwl_dvm_6050_cfg;
break; break;
case IWL_DEVICE_FAMILY_6030: case IWL_DEVICE_FAMILY_6030:
priv->lib = &iwl6030_lib; priv->lib = &iwl_dvm_6030_cfg;
break; break;
default: default:
break; break;

View File

@ -163,7 +163,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
u8 skip; u8 skip;
u32 slp_itrvl; u32 slp_itrvl;
if (priv->cfg->adv_pm) { if (priv->lib->adv_pm) {
table = apm_range_2; table = apm_range_2;
if (period <= IWL_DTIM_RANGE_1_MAX) if (period <= IWL_DTIM_RANGE_1_MAX)
table = apm_range_1; table = apm_range_1;
@ -217,7 +217,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
if (iwl_advanced_bt_coexist(priv)) { if (iwl_advanced_bt_coexist(priv)) {
if (!priv->cfg->bt_params->bt_sco_disable) if (!priv->lib->bt_params->bt_sco_disable)
cmd->flags |= IWL_POWER_BT_SCO_ENA; cmd->flags |= IWL_POWER_BT_SCO_ENA;
else else
cmd->flags &= ~IWL_POWER_BT_SCO_ENA; cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
@ -293,7 +293,7 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
if (priv->wowlan) if (priv->wowlan)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
else if (!priv->cfg->base_params->no_idle_support && else if (!priv->lib->no_idle_support &&
priv->hw->conf.flags & IEEE80211_CONF_IDLE) priv->hw->conf.flags & IEEE80211_CONF_IDLE)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
else if (iwl_tt_is_low_power_state(priv)) { else if (iwl_tt_is_low_power_state(priv)) {

View File

@ -1088,7 +1088,7 @@ done:
(priv->tm_fixed_rate != lq_sta->dbg_fixed_rate)) (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate))
rs_program_fix_rate(priv, lq_sta); rs_program_fix_rate(priv, lq_sta);
#endif #endif
if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist)
rs_bt_update_lq(priv, ctx, lq_sta); rs_bt_update_lq(priv, ctx, lq_sta);
} }
@ -3064,11 +3064,11 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
* overwrite if needed, pass aggregation time limit * overwrite if needed, pass aggregation time limit
* to uCode in uSec * to uCode in uSec
*/ */
if (priv && priv->cfg->bt_params && if (priv && priv->lib->bt_params &&
priv->cfg->bt_params->agg_time_limit && priv->lib->bt_params->agg_time_limit &&
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
lq_cmd->agg_params.agg_time_limit = lq_cmd->agg_params.agg_time_limit =
cpu_to_le16(priv->cfg->bt_params->agg_time_limit); cpu_to_le16(priv->lib->bt_params->agg_time_limit);
} }
static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)

View File

@ -1102,7 +1102,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv)
iwl_notification_wait_init(&priv->notif_wait); iwl_notification_wait_init(&priv->notif_wait);
/* Set up BT Rx handlers */ /* Set up BT Rx handlers */
if (priv->cfg->bt_params) if (priv->lib->bt_params)
iwlagn_bt_rx_handler_setup(priv); iwlagn_bt_rx_handler_setup(priv);
} }

View File

@ -801,8 +801,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
* Internal scans are passive, so we can indiscriminately set * Internal scans are passive, so we can indiscriminately set
* the BT ignore flag on 2.4 GHz since it applies to TX only. * the BT ignore flag on 2.4 GHz since it applies to TX only.
*/ */
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) priv->lib->bt_params->advanced_bt_coexist)
scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
break; break;
case IEEE80211_BAND_5GHZ: case IEEE80211_BAND_5GHZ:
@ -844,8 +844,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
band = priv->scan_band; band = priv->scan_band;
if (band == IEEE80211_BAND_2GHZ && if (band == IEEE80211_BAND_2GHZ &&
priv->cfg->bt_params && priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) { priv->lib->bt_params->advanced_bt_coexist) {
/* transmit 2.4 GHz probes only on first antenna */ /* transmit 2.4 GHz probes only on first antenna */
scan_tx_antennas = first_antenna(scan_tx_antennas); scan_tx_antennas = first_antenna(scan_tx_antennas);
} }
@ -873,8 +873,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
rx_ant = first_antenna(active_chains); rx_ant = first_antenna(active_chains);
} }
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist && priv->lib->bt_params->advanced_bt_coexist &&
priv->bt_full_concurrent) { priv->bt_full_concurrent) {
/* operated as 1x1 in full concurrency mode */ /* operated as 1x1 in full concurrency mode */
rx_ant = first_antenna(rx_ant); rx_ant = first_antenna(rx_ant);

View File

@ -627,7 +627,7 @@ void iwl_tt_initialize(struct iwl_priv *priv)
INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
if (priv->cfg->base_params->adv_thermal_throttle) { if (priv->lib->adv_thermal_throttle) {
IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n");
tt->restriction = kcalloc(IWL_TI_STATE_MAX, tt->restriction = kcalloc(IWL_TI_STATE_MAX,
sizeof(struct iwl_tt_restriction), sizeof(struct iwl_tt_restriction),

View File

@ -83,8 +83,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
else if (ieee80211_is_back_req(fc)) else if (ieee80211_is_back_req(fc))
tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
else if (info->band == IEEE80211_BAND_2GHZ && else if (info->band == IEEE80211_BAND_2GHZ &&
priv->cfg->bt_params && priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist && priv->lib->bt_params->advanced_bt_coexist &&
(ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
ieee80211_is_reassoc_req(fc) || ieee80211_is_reassoc_req(fc) ||
skb->protocol == cpu_to_be16(ETH_P_PAE))) skb->protocol == cpu_to_be16(ETH_P_PAE)))
@ -202,8 +202,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
rate_flags |= RATE_MCS_CCK_MSK; rate_flags |= RATE_MCS_CCK_MSK;
/* Set up antennas */ /* Set up antennas */
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist && priv->lib->bt_params->advanced_bt_coexist &&
priv->bt_full_concurrent) { priv->bt_full_concurrent) {
/* operated as 1x1 in full concurrency mode */ /* operated as 1x1 in full concurrency mode */
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
@ -986,8 +986,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
* notification again. * notification again.
*/ */
if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
priv->cfg->bt_params && priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) { priv->lib->bt_params->advanced_bt_coexist) {
IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n");
} }

View File

@ -132,8 +132,8 @@ int iwl_init_alive_start(struct iwl_priv *priv)
{ {
int ret; int ret;
if (priv->cfg->bt_params && if (priv->lib->bt_params &&
priv->cfg->bt_params->advanced_bt_coexist) { priv->lib->bt_params->advanced_bt_coexist) {
/* /*
* Tell uCode we are ready to perform calibration * Tell uCode we are ready to perform calibration
* need to perform this before any calibration * need to perform this before any calibration
@ -155,8 +155,8 @@ int iwl_init_alive_start(struct iwl_priv *priv)
* temperature offset calibration is only needed for runtime ucode, * temperature offset calibration is only needed for runtime ucode,
* so prepare the value now. * so prepare the value now.
*/ */
if (priv->cfg->need_temp_offset_calib) { if (priv->lib->need_temp_offset_calib) {
if (priv->cfg->temp_offset_v2) if (priv->lib->temp_offset_v2)
return iwl_set_temperature_offset_calib_v2(priv); return iwl_set_temperature_offset_calib_v2(priv);
else else
return iwl_set_temperature_offset_calib(priv); return iwl_set_temperature_offset_calib(priv);
@ -277,7 +277,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
if (ret) if (ret)
return ret; return ret;
if (!priv->cfg->no_xtal_calib) { if (!priv->lib->no_xtal_calib) {
ret = iwl_set_Xtal_calib(priv); ret = iwl_set_Xtal_calib(priv);
if (ret) if (ret)
return ret; return ret;

View File

@ -60,9 +60,6 @@ static const struct iwl_base_params iwl1000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_1000, .max_ll_items = OTP_MAX_LL_ITEMS_1000,
.shadow_ram_support = false, .shadow_ram_support = false,
.led_compensation = 51, .led_compensation = 51,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_WATCHDOG_DISABLED, .wd_timeout = IWL_WATCHDOG_DISABLED,
.max_event_log_size = 128, .max_event_log_size = 128,
}; };

View File

@ -72,14 +72,9 @@ static const struct iwl_base_params iwl2000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_2x00, .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
.shadow_ram_support = true, .shadow_ram_support = true,
.led_compensation = 51, .led_compensation = 51,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_DEF_WD_TIMEOUT, .wd_timeout = IWL_DEF_WD_TIMEOUT,
.max_event_log_size = 512, .max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
.hd_v2 = true,
}; };
@ -90,14 +85,9 @@ static const struct iwl_base_params iwl2030_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_2x00, .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
.shadow_ram_support = true, .shadow_ram_support = true,
.led_compensation = 57, .led_compensation = 57,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT, .wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512, .max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
.hd_v2 = true,
}; };
static const struct iwl_ht_params iwl2000_ht_params = { static const struct iwl_ht_params iwl2000_ht_params = {
@ -106,16 +96,6 @@ static const struct iwl_ht_params iwl2000_ht_params = {
.ht40_bands = BIT(IEEE80211_BAND_2GHZ), .ht40_bands = BIT(IEEE80211_BAND_2GHZ),
}; };
static const struct iwl_bt_params iwl2030_bt_params = {
/* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
.advanced_bt_coexist = true,
.agg_time_limit = BT_AGG_THRESHOLD_DEF,
.bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
.bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
.bt_sco_disable = true,
.bt_session_2 = true,
};
static const struct iwl_eeprom_params iwl20x0_eeprom_params = { static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
.regulatory_bands = { .regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS, EEPROM_REG_BAND_1_CHANNELS,
@ -137,12 +117,10 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
.device_family = IWL_DEVICE_FAMILY_2000, \ .device_family = IWL_DEVICE_FAMILY_2000, \
.max_inst_size = IWL60_RTC_INST_SIZE, \ .max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2000_base_params, \ .base_params = &iwl2000_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \ .eeprom_params = &iwl20x0_eeprom_params, \
.need_temp_offset_calib = true, \
.temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE .led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl2000_2bgn_cfg = { const struct iwl_cfg iwl2000_2bgn_cfg = {
@ -168,12 +146,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = {
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2030_base_params, \ .base_params = &iwl2030_base_params, \
.bt_params = &iwl2030_bt_params, \
.eeprom_params = &iwl20x0_eeprom_params, \ .eeprom_params = &iwl20x0_eeprom_params, \
.need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE
.temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true
const struct iwl_cfg iwl2030_2bgn_cfg = { const struct iwl_cfg iwl2030_2bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
@ -193,10 +167,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = {
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2000_base_params, \ .base_params = &iwl2000_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \ .eeprom_params = &iwl20x0_eeprom_params, \
.need_temp_offset_calib = true, \
.temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \ .led_mode = IWL_LED_RF_STATE, \
.adv_pm = true, \
.rx_with_siso_diversity = true .rx_with_siso_diversity = true
const struct iwl_cfg iwl105_bgn_cfg = { const struct iwl_cfg iwl105_bgn_cfg = {
@ -222,12 +193,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = {
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2030_base_params, \ .base_params = &iwl2030_base_params, \
.bt_params = &iwl2030_bt_params, \
.eeprom_params = &iwl20x0_eeprom_params, \ .eeprom_params = &iwl20x0_eeprom_params, \
.need_temp_offset_calib = true, \
.temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \ .led_mode = IWL_LED_RF_STATE, \
.adv_pm = true, \
.rx_with_siso_diversity = true .rx_with_siso_diversity = true
const struct iwl_cfg iwl135_bgn_cfg = { const struct iwl_cfg iwl135_bgn_cfg = {

View File

@ -59,11 +59,8 @@ static const struct iwl_base_params iwl5000_base_params = {
.num_of_queues = IWLAGN_NUM_QUEUES, .num_of_queues = IWLAGN_NUM_QUEUES,
.pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
.led_compensation = 51, .led_compensation = 51,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_WATCHDOG_DISABLED, .wd_timeout = IWL_WATCHDOG_DISABLED,
.max_event_log_size = 512, .max_event_log_size = 512,
.no_idle_support = true,
}; };
static const struct iwl_ht_params iwl5000_ht_params = { static const struct iwl_ht_params iwl5000_ht_params = {
@ -159,7 +156,6 @@ const struct iwl_cfg iwl5350_agn_cfg = {
.nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \
.base_params = &iwl5000_base_params, \ .base_params = &iwl5000_base_params, \
.eeprom_params = &iwl5000_eeprom_params, \ .eeprom_params = &iwl5000_eeprom_params, \
.no_xtal_calib = true, \
.led_mode = IWL_LED_BLINK, \ .led_mode = IWL_LED_BLINK, \
.internal_wimax_coex = true .internal_wimax_coex = true

View File

@ -82,10 +82,6 @@ static const struct iwl_base_params iwl6000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x00, .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true, .shadow_ram_support = true,
.led_compensation = 51, .led_compensation = 51,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_DEF_WD_TIMEOUT, .wd_timeout = IWL_DEF_WD_TIMEOUT,
.max_event_log_size = 512, .max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@ -98,10 +94,6 @@ static const struct iwl_base_params iwl6050_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x50, .max_ll_items = OTP_MAX_LL_ITEMS_6x50,
.shadow_ram_support = true, .shadow_ram_support = true,
.led_compensation = 51, .led_compensation = 51,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1500,
.wd_timeout = IWL_DEF_WD_TIMEOUT, .wd_timeout = IWL_DEF_WD_TIMEOUT,
.max_event_log_size = 1024, .max_event_log_size = 1024,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@ -114,10 +106,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x00, .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true, .shadow_ram_support = true,
.led_compensation = 57, .led_compensation = 57,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT, .wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512, .max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@ -129,15 +117,6 @@ static const struct iwl_ht_params iwl6000_ht_params = {
.ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
}; };
static const struct iwl_bt_params iwl6000_bt_params = {
/* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
.advanced_bt_coexist = true,
.agg_time_limit = BT_AGG_THRESHOLD_DEF,
.bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
.bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
.bt_sco_disable = true,
};
static const struct iwl_eeprom_params iwl6000_eeprom_params = { static const struct iwl_eeprom_params iwl6000_eeprom_params = {
.regulatory_bands = { .regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS, EEPROM_REG_BAND_1_CHANNELS,
@ -163,7 +142,6 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = {
.nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \ .base_params = &iwl6000_g2_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \ .eeprom_params = &iwl6000_eeprom_params, \
.need_temp_offset_calib = true, \
.led_mode = IWL_LED_RF_STATE .led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl6005_2agn_cfg = { const struct iwl_cfg iwl6005_2agn_cfg = {
@ -217,11 +195,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = {
.nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_ver = EEPROM_6030_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \ .base_params = &iwl6000_g2_base_params, \
.bt_params = &iwl6000_bt_params, \
.eeprom_params = &iwl6000_eeprom_params, \ .eeprom_params = &iwl6000_eeprom_params, \
.need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true \
const struct iwl_cfg iwl6030_2agn_cfg = { const struct iwl_cfg iwl6030_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
@ -256,11 +231,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = {
.nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_ver = EEPROM_6030_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \ .base_params = &iwl6000_g2_base_params, \
.bt_params = &iwl6000_bt_params, \
.eeprom_params = &iwl6000_eeprom_params, \ .eeprom_params = &iwl6000_eeprom_params, \
.need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true
const struct iwl_cfg iwl6035_2agn_cfg = { const struct iwl_cfg iwl6035_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",

View File

@ -96,13 +96,9 @@ static const struct iwl_base_params iwl7000_base_params = {
.pll_cfg_val = 0, .pll_cfg_val = 0,
.shadow_ram_support = true, .shadow_ram_support = true,
.led_compensation = 57, .led_compensation = 57,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT, .wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512, .max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */ .shadow_reg_enable = true,
}; };
static const struct iwl_ht_params iwl7000_ht_params = { static const struct iwl_ht_params iwl7000_ht_params = {
@ -118,14 +114,11 @@ static const struct iwl_ht_params iwl7000_ht_params = {
.max_inst_size = IWL60_RTC_INST_SIZE, \ .max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \
.base_params = &iwl7000_base_params, \ .base_params = &iwl7000_base_params, \
/* TODO: .bt_params? */ \ .led_mode = IWL_LED_RF_STATE
.need_temp_offset_calib = true, \
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true \
const struct iwl_cfg iwl7260_2ac_cfg = { const struct iwl_cfg iwl7260_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC7260", .name = "Intel(R) Dual Band Wireless AC 7260",
.fw_name_pre = IWL7260_FW_PRE, .fw_name_pre = IWL7260_FW_PRE,
IWL_DEVICE_7000, IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params, .ht_params = &iwl7000_ht_params,
@ -133,8 +126,44 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
.nvm_calib_ver = IWL7260_TX_POWER_VERSION, .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
}; };
const struct iwl_cfg iwl3160_ac_cfg = { const struct iwl_cfg iwl7260_2n_cfg = {
.name = "Intel(R) Dual Band Wireless AC3160", .name = "Intel(R) Dual Band Wireless N 7260",
.fw_name_pre = IWL7260_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL7260_NVM_VERSION,
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
};
const struct iwl_cfg iwl7260_n_cfg = {
.name = "Intel(R) Wireless N 7260",
.fw_name_pre = IWL7260_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL7260_NVM_VERSION,
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
};
const struct iwl_cfg iwl3160_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 3160",
.fw_name_pre = IWL3160_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL3160_NVM_VERSION,
.nvm_calib_ver = IWL3160_TX_POWER_VERSION,
};
const struct iwl_cfg iwl3160_2n_cfg = {
.name = "Intel(R) Dual Band Wireless N 3160",
.fw_name_pre = IWL3160_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL3160_NVM_VERSION,
.nvm_calib_ver = IWL3160_TX_POWER_VERSION,
};
const struct iwl_cfg iwl3160_n_cfg = {
.name = "Intel(R) Wireless N 3160",
.fw_name_pre = IWL3160_FW_PRE, .fw_name_pre = IWL3160_FW_PRE,
IWL_DEVICE_7000, IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params, .ht_params = &iwl7000_ht_params,

View File

@ -136,17 +136,9 @@ enum iwl_led_mode {
* @led_compensation: compensate on the led on/off time per HW according * @led_compensation: compensate on the led on/off time per HW according
* to the deviation to achieve the desired led frequency. * to the deviation to achieve the desired led frequency.
* The detail algorithm is described in iwl-led.c * The detail algorithm is described in iwl-led.c
* @chain_noise_num_beacons: number of beacons used to compute chain noise
* @adv_thermal_throttle: support advance thermal throttle
* @support_ct_kill_exit: support ct kill exit condition
* @plcp_delta_threshold: plcp error rate threshold used to trigger
* radio tuning when there is a high receiving plcp error rate
* @chain_noise_scale: default chain noise scale used for gain computation
* @wd_timeout: TX queues watchdog timeout * @wd_timeout: TX queues watchdog timeout
* @max_event_log_size: size of event log buffer size for ucode event logging * @max_event_log_size: size of event log buffer size for ucode event logging
* @shadow_reg_enable: HW shadow register support * @shadow_reg_enable: HW shadow register support
* @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
* @no_idle_support: do not support idle mode
*/ */
struct iwl_base_params { struct iwl_base_params {
int eeprom_size; int eeprom_size;
@ -157,31 +149,9 @@ struct iwl_base_params {
const u16 max_ll_items; const u16 max_ll_items;
const bool shadow_ram_support; const bool shadow_ram_support;
u16 led_compensation; u16 led_compensation;
bool adv_thermal_throttle;
bool support_ct_kill_exit;
u8 plcp_delta_threshold;
s32 chain_noise_scale;
unsigned int wd_timeout; unsigned int wd_timeout;
u32 max_event_log_size; u32 max_event_log_size;
const bool shadow_reg_enable; const bool shadow_reg_enable;
const bool hd_v2;
const bool no_idle_support;
};
/*
* @advanced_bt_coexist: support advanced bt coexist
* @bt_init_traffic_load: specify initial bt traffic load
* @bt_prio_boost: default bt priority boost value
* @agg_time_limit: maximum number of uSec in aggregation
* @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
*/
struct iwl_bt_params {
bool advanced_bt_coexist;
u8 bt_init_traffic_load;
u32 bt_prio_boost;
u16 agg_time_limit;
bool bt_sco_disable;
bool bt_session_2;
}; };
/* /*
@ -231,16 +201,10 @@ struct iwl_eeprom_params {
* @nvm_calib_ver: NVM calibration version * @nvm_calib_ver: NVM calibration version
* @lib: pointer to the lib ops * @lib: pointer to the lib ops
* @base_params: pointer to basic parameters * @base_params: pointer to basic parameters
* @ht_params: point to ht patameters * @ht_params: point to ht parameters
* @bt_params: pointer to bt parameters
* @need_temp_offset_calib: need to perform temperature offset calibration
* @no_xtal_calib: some devices do not need crystal calibration data,
* don't send it to those
* @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
* @adv_pm: advance power management
* @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @rx_with_siso_diversity: 1x1 device with rx antenna diversity
* @internal_wimax_coex: internal wifi/wimax combo device * @internal_wimax_coex: internal wifi/wimax combo device
* @temp_offset_v2: support v2 of temperature offset calibration
* *
* We enable the driver to be backward compatible wrt. hardware features. * We enable the driver to be backward compatible wrt. hardware features.
* API differences in uCode shouldn't be handled here but through TLVs * API differences in uCode shouldn't be handled here but through TLVs
@ -264,15 +228,10 @@ struct iwl_cfg {
const struct iwl_base_params *base_params; const struct iwl_base_params *base_params;
/* params likely to change within a device family */ /* params likely to change within a device family */
const struct iwl_ht_params *ht_params; const struct iwl_ht_params *ht_params;
const struct iwl_bt_params *bt_params;
const struct iwl_eeprom_params *eeprom_params; const struct iwl_eeprom_params *eeprom_params;
const bool need_temp_offset_calib; /* if used set to true */
const bool no_xtal_calib;
enum iwl_led_mode led_mode; enum iwl_led_mode led_mode;
const bool adv_pm;
const bool rx_with_siso_diversity; const bool rx_with_siso_diversity;
const bool internal_wimax_coex; const bool internal_wimax_coex;
const bool temp_offset_v2;
}; };
/* /*
@ -320,6 +279,10 @@ extern const struct iwl_cfg iwl105_bgn_cfg;
extern const struct iwl_cfg iwl105_bgn_d_cfg; extern const struct iwl_cfg iwl105_bgn_d_cfg;
extern const struct iwl_cfg iwl135_bgn_cfg; extern const struct iwl_cfg iwl135_bgn_cfg;
extern const struct iwl_cfg iwl7260_2ac_cfg; extern const struct iwl_cfg iwl7260_2ac_cfg;
extern const struct iwl_cfg iwl3160_ac_cfg; extern const struct iwl_cfg iwl7260_2n_cfg;
extern const struct iwl_cfg iwl7260_n_cfg;
extern const struct iwl_cfg iwl3160_2ac_cfg;
extern const struct iwl_cfg iwl3160_2n_cfg;
extern const struct iwl_cfg iwl3160_n_cfg;
#endif /* __IWL_CONFIG_H__ */ #endif /* __IWL_CONFIG_H__ */

View File

@ -472,4 +472,23 @@
#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) #define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10)
#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) #define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0)
/*****************************************************************************
* 7000/3000 series SHR DTS addresses *
*****************************************************************************/
/* Diode Results Register Structure: */
enum dtd_diode_reg {
DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */
DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */
DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */
DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */
DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */
DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */
/* Those are the masks INSIDE the flags bit-field: */
DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0,
DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */
DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7,
DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */
};
#endif /* !__iwl_csr_h__ */ #endif /* !__iwl_csr_h__ */

View File

@ -1234,6 +1234,9 @@ MODULE_PARM_DESC(wd_disable,
"Disable stuck queue watchdog timer 0=system default, " "Disable stuck queue watchdog timer 0=system default, "
"1=disable, 2=enable (default: 0)"); "1=disable, 2=enable (default: 0)");
module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
MODULE_PARM_DESC(nvm_file, "NVM file name");
/* /*
* set bt_coex_active to true, uCode will do kill/defer * set bt_coex_active to true, uCode will do kill/defer
* every time the priority line is asserted (BT is sending signals on the * every time the priority line is asserted (BT is sending signals on the

Some files were not shown because too many files have changed in this diff Show More