--- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -283,4 +283,8 @@ void _ath_dbg(struct ath_common *common, /** Returns string describing opmode, or NULL if unknown mode. */ const char *ath_opmode_to_string(enum nl80211_iftype opmode); +void debug_reg_access_add(const char *file, size_t line, const char *function, + const char *op, void *ah, u32 reg, u32 val); +void debug_reg_access_print(void); + #endif /* ATH_H */ --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -182,6 +182,7 @@ static bool ar9003_hw_get_isr(struct ath struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_common *common = ath9k_hw_common(ah); u32 sync_cause = 0, async_cause, async_mask = AR_INTR_MAC_IRQ; + bool fatal_int; if (ath9k_hw_mci_is_enabled(ah)) async_mask |= AR_INTR_ASYNC_MASK_MCI; @@ -308,6 +309,21 @@ static bool ar9003_hw_get_isr(struct ath if (sync_cause) { ath9k_debug_sync_cause(common, sync_cause); + fatal_int = (sync_cause & (BIT(5) | BIT(6))) ? true : false; + + if (fatal_int) { + if (sync_cause & BIT(5)) { + printk( + "received MAC_TXC_CORRUPTION_FLAG_SYNC\n"); + debug_reg_access_print(); + *masked |= ATH9K_INT_FATAL; + } + if (sync_cause & BIT(6)) { + printk( + "received INVALID_ADDRESS_ACCESS\n"); + debug_reg_access_print(); + } + } if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -22,9 +22,24 @@ #include "ath9k.h" #define REG_WRITE_D(_ah, _reg, _val) \ - ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg)) + do { \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + typeof(_val) _t_val = (_val); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "W", _t_ah, _t_reg, _t_val); \ + ath9k_hw_common(_t_ah)->ops->write((_t_ah), (_t_val), \ + (_t_reg)); \ + } while (0) + #define REG_READ_D(_ah, _reg) \ - ath9k_hw_common(_ah)->ops->read((_ah), (_reg)) + ({ \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "R", _t_ah, _t_reg, 0); \ + ath9k_hw_common(_t_ah)->ops->read((_t_ah), (_t_reg)); \ + }) static ssize_t ath9k_debugfs_read_buf(struct file *file, char __user *user_buf, --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -70,28 +70,59 @@ #define ATH9K_NUM_CHANNELS 38 /* Register read/write primitives */ + #define REG_WRITE(_ah, _reg, _val) \ - (_ah)->reg_ops.write((_ah), (_val), (_reg)) + do { \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + typeof(_val) _t_val = (_val); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "W", _t_ah, _t_reg, _t_val); \ + (_t_ah)->reg_ops.write((_t_ah), (_t_val), (_t_reg)); \ + } while (0) #define REG_READ(_ah, _reg) \ - (_ah)->reg_ops.read((_ah), (_reg)) + ({ \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "R", _t_ah, _t_reg, 0); \ + (_t_ah)->reg_ops.read((_t_ah), (_t_reg)); \ + }) #define REG_READ_MULTI(_ah, _addr, _val, _cnt) \ - (_ah)->reg_ops.multi_read((_ah), (_addr), (_val), (_cnt)) + do { \ + typeof(_ah) _t_ah = (_ah); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "REG_READ_MULTI", _t_ah, 0, 0); \ + (_t_ah)->reg_ops.multi_read((_t_ah), (_addr), (_val), (_cnt)); \ + } while (0) #define REG_RMW(_ah, _reg, _set, _clr) \ - (_ah)->reg_ops.rmw((_ah), (_reg), (_set), (_clr)) + ({ \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "RMW", _t_ah, _t_reg, 0); \ + (_t_ah)->reg_ops.rmw((_t_ah), (_reg), (_set), (_clr)); \ + }) #define ENABLE_REGWRITE_BUFFER(_ah) \ do { \ - if ((_ah)->reg_ops.enable_write_buffer) \ - (_ah)->reg_ops.enable_write_buffer((_ah)); \ + typeof(_ah) _t_ah = (_ah); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "ENABLE_REGWRITE_BUFFER", _t_ah, 0, 0); \ + if ((_t_ah)->reg_ops.enable_write_buffer) \ + (_t_ah)->reg_ops.enable_write_buffer((_t_ah)); \ } while (0) #define REGWRITE_BUFFER_FLUSH(_ah) \ do { \ - if ((_ah)->reg_ops.write_flush) \ - (_ah)->reg_ops.write_flush((_ah)); \ + typeof(_ah) _t_ah = (_ah); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "REGWRITE_BUFFER_FLUSH", _t_ah, 0, 0); \ + if ((_t_ah)->reg_ops.write_flush) \ + (_t_ah)->reg_ops.write_flush((_t_ah)); \ } while (0) #define PR_EEP(_s, _val) \ --- a/drivers/net/wireless/ath/hw.c +++ b/drivers/net/wireless/ath/hw.c @@ -20,8 +20,99 @@ #include "ath.h" #include "reg.h" -#define REG_READ (common->ops->read) -#define REG_WRITE (common->ops->write) +#define REG_WRITE(_ah, _val, _reg) \ + do { \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + typeof(_val) _t_val = (_val); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "W", _t_ah, _t_reg, _t_val); \ + (common->ops->write)((_t_ah), (_t_val), (_t_reg)); \ + } while (0) + +#define REG_READ(_ah, _reg) \ + ({ \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "R", _t_ah, _t_reg, 0); \ + (common->ops->read)((_t_ah), (_t_reg)); \ + }) + +struct debug_reg_access { + const char *file; + size_t line; + const char *function; + const char *op; + void *ah; + u32 reg; + u32 val; +}; + +static size_t debug_reg_access_pos; +static struct debug_reg_access debug_reg_access_db[128]; +static spinlock_t debug_reg_access_lock; + +static int __init debug_reg_access_init(void) +{ + printk("Init debug_reg_access\n"); + memset(debug_reg_access_db, 0, sizeof(debug_reg_access_db)); + debug_reg_access_pos = 0; + spin_lock_init(&debug_reg_access_lock); + + return 0; +} + +EXPORT_SYMBOL(debug_reg_access_add); +void debug_reg_access_add(const char *file, size_t line, const char *function, + const char *op, void *ah, u32 reg, u32 val) +{ + unsigned long flags; + struct debug_reg_access *entry; + + spin_lock_irqsave(&debug_reg_access_lock, flags); + + entry = &debug_reg_access_db[debug_reg_access_pos]; + entry->file = file; + entry->line = line; + entry->function = function; + entry->op = op; + entry->ah = ah; + entry->reg = reg; + entry->val = val; + + debug_reg_access_pos++; + debug_reg_access_pos %= ARRAY_SIZE(debug_reg_access_db); + + spin_unlock_irqrestore(&debug_reg_access_lock, flags); +} + +EXPORT_SYMBOL(debug_reg_access_print); +void debug_reg_access_print(void) +{ + unsigned long flags; + size_t i, pos; + struct debug_reg_access *entry; + + spin_lock_irqsave(&debug_reg_access_lock, flags); + printk("=================== cut here (start) ===================\n"); + for (i = 0; i < ARRAY_SIZE(debug_reg_access_db); i++) { + pos = (i + debug_reg_access_pos); + pos %= ARRAY_SIZE(debug_reg_access_db); + entry = &debug_reg_access_db[pos]; + + if (!entry->file) + continue; + + printk("%s:%zu (%s); %s, ah %p, reg %zu, val %zu\n", + entry->file, entry->line, entry->function, entry->op, + entry->ah, entry->reg, entry->val); + } + printk("==================== cut here (end) ====================\n"); + spin_unlock_irqrestore(&debug_reg_access_lock, flags); +} + +module_init(debug_reg_access_init); /** * ath_hw_set_bssid_mask - filter out bssids we listen --- a/drivers/net/wireless/ath/key.c +++ b/drivers/net/wireless/ath/key.c @@ -22,16 +22,42 @@ #include "ath.h" #include "reg.h" -#define REG_READ (common->ops->read) -#define REG_WRITE(_ah, _reg, _val) (common->ops->write)(_ah, _val, _reg) +#define REG_WRITE(_ah, _reg, _val) \ + do { \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + typeof(_val) _t_val = (_val); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "W", _t_ah, _t_reg, _t_val); \ + (common->ops->write)((_t_ah), (_t_val), (_t_reg)); \ + } while (0) + +#define REG_READ(_ah, _reg) \ + ({ \ + typeof(_ah) _t_ah = (_ah); \ + typeof(_reg) _t_reg = (_reg); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "R", _t_ah, _t_reg, 0); \ + (common->ops->read)((_t_ah), (_reg)); \ + }) + #define ENABLE_REGWRITE_BUFFER(_ah) \ - if (common->ops->enable_write_buffer) \ - common->ops->enable_write_buffer((_ah)); + do {\ + typeof(_ah) _t_ah = (_ah); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "ENABLE_REGWRITE_BUFFER", _t_ah, 0, 0); \ + if (common->ops->enable_write_buffer) \ + common->ops->enable_write_buffer((_t_ah)); \ + } while (0) #define REGWRITE_BUFFER_FLUSH(_ah) \ - if (common->ops->write_flush) \ - common->ops->write_flush((_ah)); - + do {\ + typeof(_ah) _t_ah = (_ah); \ + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, \ + "REGWRITE_BUFFER_FLUSH", _t_ah, 0, 0); \ + if (common->ops->write_flush) \ + common->ops->write_flush((_t_ah)); \ + } while (0) #define IEEE80211_WEP_NKID 4 /* number of key ids */ --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -387,10 +387,18 @@ void ath_ani_calibrate(unsigned long dat /* Call ANI routine if necessary */ if (aniflag) { + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, + "PRE_IRQLOCK", NULL, 0, 0); spin_lock_irqsave(&common->cc_lock, flags); + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, + "POST_IRQLOCK", NULL, 0, 0); ath9k_hw_ani_monitor(ah, ah->curchan); ath_update_survey_stats(sc); + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, + "PRE_IRQUNLOCK", NULL, 0, 0); spin_unlock_irqrestore(&common->cc_lock, flags); + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, + "POST_IRQUNLOCK", NULL, 0, 0); } /* Perform calibration if necessary */ --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -866,6 +866,7 @@ static bool ar9003_hw_ani_control(struct struct ath9k_channel *chan = ah->curchan; struct ar5416AniState *aniState = &chan->ani; s32 value, value2; + u32 sync_cause = 0; switch (cmd & ah->ani_function) { case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{ @@ -1035,10 +1036,19 @@ static bool ar9003_hw_ani_control(struct * is_on == 0 means MRC CCK is OFF (more noise imm) */ bool is_on = param ? 1 : 0; + sync_cause = (ah)->reg_ops.read((ah), (AR_INTR_SYNC_CAUSE)) & AR_INTR_SYNC_DEFAULT; + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, + "sync_cause 0", NULL, 0, sync_cause); REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_ENABLE, is_on); + sync_cause = (ah)->reg_ops.read((ah), (AR_INTR_SYNC_CAUSE)) & AR_INTR_SYNC_DEFAULT; + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, + "sync_cause 1", NULL, 0, sync_cause); REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_MUX_REG, is_on); + sync_cause = (ah)->reg_ops.read((ah), (AR_INTR_SYNC_CAUSE)) & AR_INTR_SYNC_DEFAULT; + debug_reg_access_add(__FILE__, __LINE__, __FUNCTION__, + "sync_cause 2", NULL, 0, sync_cause); if (is_on != aniState->mrcCCK) { ath_dbg(common, ANI, "** ch %d: MRC CCK: %s=>%s\n", chan->channel,