--- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1428,6 +1428,50 @@ void ath9k_deinit_debug(struct ath_softc ath9k_cmn_spectral_deinit_debug(&sc->spec_priv); } +static ssize_t read_file_diag(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "0x%08lx\n", ah->diag); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_diag(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + unsigned long diag; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &diag)) + return -EINVAL; + + ah->diag = diag; + ath9k_hw_update_diag(ah); + + return count; +} + +static const struct file_operations fops_diag = { + .read = read_file_diag, + .write = write_file_diag, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + + int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -1451,6 +1495,8 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("gpio_led", S_IWUSR, sc->debug.debugfs_phy, sc, &fops_gpio_led); #endif + debugfs_create_file("diag", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, + sc, &fops_diag); debugfs_create_devm_seqfile(sc->dev, "dma", sc->debug.debugfs_phy, read_file_dma); debugfs_create_devm_seqfile(sc->dev, "interrupt", sc->debug.debugfs_phy, --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -522,6 +522,12 @@ enum { ATH9K_RESET_COLD, }; +enum { + ATH_DIAG_DISABLE_RX, + ATH_DIAG_DISABLE_TX, + ATH_DIAG_TRIGGER_ERROR, +}; + struct ath9k_hw_version { u32 magic; u16 devid; @@ -810,6 +816,8 @@ struct ath_hw { u32 ah_flags; s16 nf_override; + unsigned long diag; + bool reset_power_on; bool htc_reset_init; @@ -1079,6 +1087,7 @@ void ath9k_hw_check_nav(struct ath_hw *a bool ath9k_hw_check_alive(struct ath_hw *ah); bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); +void ath9k_hw_update_diag(struct ath_hw *ah); /* Generic hw timer primitives */ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1881,6 +1881,20 @@ u32 ath9k_hw_get_tsf_offset(struct times } EXPORT_SYMBOL(ath9k_hw_get_tsf_offset); +void ath9k_hw_update_diag(struct ath_hw *ah) +{ + if (test_bit(ATH_DIAG_DISABLE_RX, &ah->diag)) + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + else + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + + if (test_bit(ATH_DIAG_DISABLE_TX, &ah->diag)) + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK); + else + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK); +} +EXPORT_SYMBOL(ath9k_hw_update_diag); + int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata, bool fastcc) { @@ -2089,6 +2103,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st ar9003_hw_disable_phy_restart(ah); ath9k_hw_apply_gpio_override(ah); + ath9k_hw_update_diag(ah); if (AR_SREV_9565(ah) && common->bt_ant_diversity) REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -537,6 +537,11 @@ irqreturn_t ath_isr(int irq, void *dev) return IRQ_HANDLED; } + if (test_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag)) { + status |= ATH9K_INT_FATAL; + clear_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag); + } + /* * If there are no status bits set, then this interrupt was not * for me (should have been caught above).