Commit 27015b6f authored by Bo Jiao's avatar Bo Jiao Committed by Felix Fietkau
Browse files

wifi: mt76: mt7996: enable full system reset support



Add mt7996_reset() and refactor mt7996_mac_reset_work() to support
full system recovery.

Signed-off-by: default avatarBo Jiao <Bo.Jiao@mediatek.com>
Signed-off-by: default avatarRyder Lee <ryder.lee@mediatek.com>
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent 1b83d17c
Loading
Loading
Loading
Loading
+64 −0
Original line number Diff line number Diff line
@@ -352,6 +352,70 @@ int mt7996_dma_init(struct mt7996_dev *dev)
	return 0;
}

void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
{
	struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1];
	struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2];
	u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
	int i;

	mt76_clear(dev, MT_WFDMA0_GLO_CFG,
		   MT_WFDMA0_GLO_CFG_TX_DMA_EN |
		   MT_WFDMA0_GLO_CFG_RX_DMA_EN);

	if (dev->hif2)
		mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
			   MT_WFDMA0_GLO_CFG_TX_DMA_EN |
			   MT_WFDMA0_GLO_CFG_RX_DMA_EN);

	usleep_range(1000, 2000);

	for (i = 0; i < __MT_TXQ_MAX; i++) {
		mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
		if (phy2)
			mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true);
		if (phy3)
			mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true);
	}

	for (i = 0; i < __MT_MCUQ_MAX; i++)
		mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);

	mt76_for_each_q_rx(&dev->mt76, i)
		mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);

	mt76_tx_status_check(&dev->mt76, true);

	/* reset wfsys */
	if (force)
		mt7996_wfsys_reset(dev);

	mt7996_dma_disable(dev, force);

	/* reset hw queues */
	for (i = 0; i < __MT_TXQ_MAX; i++) {
		mt76_queue_reset(dev, dev->mphy.q_tx[i]);
		if (phy2)
			mt76_queue_reset(dev, phy2->q_tx[i]);
		if (phy3)
			mt76_queue_reset(dev, phy3->q_tx[i]);
	}

	for (i = 0; i < __MT_MCUQ_MAX; i++)
		mt76_queue_reset(dev, dev->mt76.q_mcu[i]);

	mt76_for_each_q_rx(&dev->mt76, i) {
		mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
	}

	mt76_tx_status_check(&dev->mt76, true);

	mt76_for_each_q_rx(&dev->mt76, i)
		mt76_queue_rx_reset(dev, i);

	mt7996_dma_enable(dev);
}

void mt7996_dma_cleanup(struct mt7996_dev *dev)
{
	mt7996_dma_disable(dev, true);
+6 −5
Original line number Diff line number Diff line
@@ -99,8 +99,7 @@ static void mt7996_led_set_brightness(struct led_classdev *led_cdev,
		mt7996_led_set_config(led_cdev, 0xff, 0);
}

static void
mt7996_init_txpower(struct mt7996_dev *dev,
void mt7996_init_txpower(struct mt7996_dev *dev,
			 struct ieee80211_supported_band *sband)
{
	int i, nss = hweight8(dev->mphy.antenna_mask);
@@ -255,7 +254,7 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band)
	mt76_rmw(dev, MT_WTBLOFF_RSCR(band), mask, set);
}

static void mt7996_mac_init(struct mt7996_dev *dev)
void mt7996_mac_init(struct mt7996_dev *dev)
{
#define HIF_TXD_V2_1	4
	int i;
@@ -289,7 +288,7 @@ static void mt7996_mac_init(struct mt7996_dev *dev)
		mt7996_mac_init_band(dev, i);
}

static int mt7996_txbf_init(struct mt7996_dev *dev)
int mt7996_txbf_init(struct mt7996_dev *dev)
{
	int ret;

@@ -870,6 +869,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
	if (ret)
		return ret;

	dev->recovery.hw_init_done = true;

	return mt7996_init_debugfs(&dev->phy);
}

+238 −46
Original line number Diff line number Diff line
@@ -1704,7 +1704,7 @@ mt7996_wait_reset_state(struct mt7996_dev *dev, u32 state)
	bool ret;

	ret = wait_event_timeout(dev->reset_wait,
				 (READ_ONCE(dev->reset_state) & state),
				 (READ_ONCE(dev->recovery.state) & state),
				 MT7996_RESET_TIMEOUT);

	WARN(!ret, "Timeout waiting for MCU reset state %x\n", state);
@@ -1753,68 +1753,207 @@ mt7996_update_beacons(struct mt7996_dev *dev)
					    mt7996_update_vif_beacon, phy3->hw);
}

static void
mt7996_dma_reset(struct mt7996_dev *dev)
void mt7996_tx_token_put(struct mt7996_dev *dev)
{
	struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1];
	struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2];
	u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
	int i;
	struct mt76_txwi_cache *txwi;
	int id;

	spin_lock_bh(&dev->mt76.token_lock);
	idr_for_each_entry(&dev->mt76.token, txwi, id) {
		mt7996_txwi_free(dev, txwi, NULL, NULL);
		dev->mt76.token_count--;
	}
	spin_unlock_bh(&dev->mt76.token_lock);
	idr_destroy(&dev->mt76.token);
}

static int
mt7996_mac_restart(struct mt7996_dev *dev)
{
	struct mt7996_phy *phy2, *phy3;
	struct mt76_dev *mdev = &dev->mt76;
	int i, ret;

	mt76_clear(dev, MT_WFDMA0_GLO_CFG,
		   MT_WFDMA0_GLO_CFG_TX_DMA_EN |
		   MT_WFDMA0_GLO_CFG_RX_DMA_EN);
	phy2 = mt7996_phy2(dev);
	phy3 = mt7996_phy3(dev);

	if (dev->hif2) {
		mt76_wr(dev, MT_INT1_MASK_CSR, 0x0);
		mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
	}

	if (dev_is_pci(mdev->dev)) {
		mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
		if (dev->hif2)
		mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
			   MT_WFDMA0_GLO_CFG_TX_DMA_EN |
			   MT_WFDMA0_GLO_CFG_RX_DMA_EN);
			mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0x0);
	}

	usleep_range(1000, 2000);
	set_bit(MT76_RESET, &dev->mphy.state);
	set_bit(MT76_MCU_RESET, &dev->mphy.state);
	wake_up(&dev->mt76.mcu.wait);
	if (phy2) {
		set_bit(MT76_RESET, &phy2->mt76->state);
		set_bit(MT76_MCU_RESET, &phy2->mt76->state);
	}
	if (phy3) {
		set_bit(MT76_RESET, &phy3->mt76->state);
		set_bit(MT76_MCU_RESET, &phy3->mt76->state);
	}

	for (i = 0; i < __MT_TXQ_MAX; i++) {
		mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
	/* lock/unlock all queues to ensure that no tx is pending */
	mt76_txq_schedule_all(&dev->mphy);
	if (phy2)
			mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true);
		mt76_txq_schedule_all(phy2->mt76);
	if (phy3)
			mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true);
		mt76_txq_schedule_all(phy3->mt76);

	/* disable all tx/rx napi */
	mt76_worker_disable(&dev->mt76.tx_worker);
	mt76_for_each_q_rx(mdev, i) {
		if (mdev->q_rx[i].ndesc)
			napi_disable(&dev->mt76.napi[i]);
	}
	napi_disable(&dev->mt76.tx_napi);

	for (i = 0; i < __MT_MCUQ_MAX; i++)
		mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
	/* token reinit */
	mt7996_tx_token_put(dev);
	idr_init(&dev->mt76.token);

	mt76_for_each_q_rx(&dev->mt76, i)
		mt76_queue_rx_reset(dev, i);
	mt7996_dma_reset(dev, true);

	mt76_tx_status_check(&dev->mt76, true);
	local_bh_disable();
	mt76_for_each_q_rx(mdev, i) {
		if (mdev->q_rx[i].ndesc) {
			napi_enable(&dev->mt76.napi[i]);
			napi_schedule(&dev->mt76.napi[i]);
		}
	}
	local_bh_enable();
	clear_bit(MT76_MCU_RESET, &dev->mphy.state);
	clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);

	/* re-init prefetch settings after reset */
	mt7996_dma_prefetch(dev);
	mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask);
	mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
	if (dev->hif2) {
		mt76_wr(dev, MT_INT1_MASK_CSR, dev->mt76.mmio.irqmask);
		mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
	}
	if (dev_is_pci(mdev->dev)) {
		mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
		if (dev->hif2)
			mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff);
	}

	mt76_set(dev, MT_WFDMA0_GLO_CFG,
		 MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
	/* load firmware */
	ret = mt7996_mcu_init_firmware(dev);
	if (ret)
		goto out;

	if (dev->hif2)
		mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
			 MT_WFDMA0_GLO_CFG_TX_DMA_EN |
			 MT_WFDMA0_GLO_CFG_RX_DMA_EN);
	/* set the necessary init items */
	ret = mt7996_mcu_set_eeprom(dev);
	if (ret)
		goto out;

	mt7996_mac_init(dev);
	mt7996_init_txpower(dev, &dev->mphy.sband_2g.sband);
	mt7996_init_txpower(dev, &dev->mphy.sband_5g.sband);
	mt7996_init_txpower(dev, &dev->mphy.sband_6g.sband);
	ret = mt7996_txbf_init(dev);

	if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) {
		ret = mt7996_run(dev->mphy.hw);
		if (ret)
			goto out;
	}

void mt7996_tx_token_put(struct mt7996_dev *dev)
	if (phy2 && test_bit(MT76_STATE_RUNNING, &phy2->mt76->state)) {
		ret = mt7996_run(phy2->mt76->hw);
		if (ret)
			goto out;
	}

	if (phy3 && test_bit(MT76_STATE_RUNNING, &phy3->mt76->state)) {
		ret = mt7996_run(phy3->mt76->hw);
		if (ret)
			goto out;
	}

out:
	/* reset done */
	clear_bit(MT76_RESET, &dev->mphy.state);
	if (phy2)
		clear_bit(MT76_RESET, &phy2->mt76->state);
	if (phy3)
		clear_bit(MT76_RESET, &phy3->mt76->state);

	local_bh_disable();
	napi_enable(&dev->mt76.tx_napi);
	napi_schedule(&dev->mt76.tx_napi);
	local_bh_enable();

	mt76_worker_enable(&dev->mt76.tx_worker);
	return ret;
}

static void
mt7996_mac_full_reset(struct mt7996_dev *dev)
{
	struct mt76_txwi_cache *txwi;
	int id;
	struct mt7996_phy *phy2, *phy3;
	int i;

	spin_lock_bh(&dev->mt76.token_lock);
	idr_for_each_entry(&dev->mt76.token, txwi, id) {
		mt7996_txwi_free(dev, txwi, NULL, NULL);
		dev->mt76.token_count--;
	phy2 = mt7996_phy2(dev);
	phy3 = mt7996_phy3(dev);
	dev->recovery.hw_full_reset = true;

	wake_up(&dev->mt76.mcu.wait);
	ieee80211_stop_queues(mt76_hw(dev));
	if (phy2)
		ieee80211_stop_queues(phy2->mt76->hw);
	if (phy3)
		ieee80211_stop_queues(phy3->mt76->hw);

	cancel_delayed_work_sync(&dev->mphy.mac_work);
	if (phy2)
		cancel_delayed_work_sync(&phy2->mt76->mac_work);
	if (phy3)
		cancel_delayed_work_sync(&phy3->mt76->mac_work);

	mutex_lock(&dev->mt76.mutex);
	for (i = 0; i < 10; i++) {
		if (!mt7996_mac_restart(dev))
			break;
	}
	spin_unlock_bh(&dev->mt76.token_lock);
	idr_destroy(&dev->mt76.token);
	mutex_unlock(&dev->mt76.mutex);

	if (i == 10)
		dev_err(dev->mt76.dev, "chip full reset failed\n");

	ieee80211_restart_hw(mt76_hw(dev));
	if (phy2)
		ieee80211_restart_hw(phy2->mt76->hw);
	if (phy3)
		ieee80211_restart_hw(phy3->mt76->hw);

	ieee80211_wake_queues(mt76_hw(dev));
	if (phy2)
		ieee80211_wake_queues(phy2->mt76->hw);
	if (phy3)
		ieee80211_wake_queues(phy3->mt76->hw);

	dev->recovery.hw_full_reset = false;
	ieee80211_queue_delayed_work(mt76_hw(dev),
				     &dev->mphy.mac_work,
				     MT7996_WATCHDOG_TIME);
	if (phy2)
		ieee80211_queue_delayed_work(phy2->mt76->hw,
					     &phy2->mt76->mac_work,
					     MT7996_WATCHDOG_TIME);
	if (phy3)
		ieee80211_queue_delayed_work(phy3->mt76->hw,
					     &phy3->mt76->mac_work,
					     MT7996_WATCHDOG_TIME);
}

/* system error recovery */
void mt7996_mac_reset_work(struct work_struct *work)
{
	struct mt7996_phy *phy2, *phy3;
@@ -1825,9 +1964,36 @@ void mt7996_mac_reset_work(struct work_struct *work)
	phy2 = mt7996_phy2(dev);
	phy3 = mt7996_phy3(dev);

	if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA))
	/* chip full reset */
	if (dev->recovery.restart) {
		/* disable WA/WM WDT */
		mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA,
			   MT_MCU_CMD_WDT_MASK);

		if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
			dev->recovery.wa_reset_count++;
		else
			dev->recovery.wm_reset_count++;

		mt7996_mac_full_reset(dev);

		/* enable mcu irq */
		mt7996_irq_enable(dev, MT_INT_MCU_CMD);
		mt7996_irq_disable(dev, 0);

		/* enable WA/WM WDT */
		mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);

		dev->recovery.state = MT_MCU_CMD_NORMAL_STATE;
		dev->recovery.restart = false;
		return;
	}

	if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
		return;

	dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
		 wiphy_name(dev->mt76.hw->wiphy));
	ieee80211_stop_queues(mt76_hw(dev));
	if (phy2)
		ieee80211_stop_queues(phy2->mt76->hw);
@@ -1856,7 +2022,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
	mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);

	if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
		mt7996_dma_reset(dev);
		mt7996_dma_reset(dev, false);

		mt7996_tx_token_put(dev);
		idr_init(&dev->mt76.token);
@@ -1911,6 +2077,32 @@ void mt7996_mac_reset_work(struct work_struct *work)
		ieee80211_queue_delayed_work(phy3->mt76->hw,
					     &phy3->mt76->mac_work,
					     MT7996_WATCHDOG_TIME);
	dev_info(dev->mt76.dev,"\n%s L1 SER recovery completed.",
		 wiphy_name(dev->mt76.hw->wiphy));
}

void mt7996_reset(struct mt7996_dev *dev)
{
	if (!dev->recovery.hw_init_done)
		return;

	if (dev->recovery.hw_full_reset)
		return;

	/* wm/wa exception: do full recovery */
	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK) {
		dev->recovery.restart = true;
		dev_info(dev->mt76.dev,
			 "%s indicated firmware crash, attempting recovery\n",
			 wiphy_name(dev->mt76.hw->wiphy));

		mt7996_irq_disable(dev, MT_INT_MCU_CMD);
		queue_work(dev->mt76.wq, &dev->reset_work);
		return;
	}

	queue_work(dev->mt76.wq, &dev->reset_work);
	wake_up(&dev->reset_wait);
}

void mt7996_mac_update_stats(struct mt7996_phy *phy)
+13 −5
Original line number Diff line number Diff line
@@ -22,17 +22,13 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
	return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
}

static int mt7996_start(struct ieee80211_hw *hw)
int mt7996_run(struct ieee80211_hw *hw)
{
	struct mt7996_dev *dev = mt7996_hw_dev(hw);
	struct mt7996_phy *phy = mt7996_hw_phy(hw);
	bool running;
	int ret;

	flush_work(&dev->init_work);

	mutex_lock(&dev->mt76.mutex);

	running = mt7996_dev_running(dev);
	if (!running) {
		ret = mt7996_mcu_set_hdr_trans(dev, true);
@@ -59,6 +55,18 @@ static int mt7996_start(struct ieee80211_hw *hw)
		mt7996_mac_reset_counters(phy);

out:
	return ret;
}

static int mt7996_start(struct ieee80211_hw *hw)
{
	struct mt7996_dev *dev = mt7996_hw_dev(hw);
	int ret;

	flush_work(&dev->init_work);

	mutex_lock(&dev->mt76.mutex);
	ret = mt7996_run(hw);
	mutex_unlock(&dev->mt76.mutex);

	return ret;
+14 −8
Original line number Diff line number Diff line
@@ -2425,17 +2425,10 @@ mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev)
				     MCU_WM_UNI_CMD(VOW), true);
}

int mt7996_mcu_init(struct mt7996_dev *dev)
int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
{
	static const struct mt76_mcu_ops mt7996_mcu_ops = {
		.headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */
		.mcu_skb_send_msg = mt7996_mcu_send_message,
		.mcu_parse_response = mt7996_mcu_parse_response,
	};
	int ret;

	dev->mt76.mcu_ops = &mt7996_mcu_ops;

	/* force firmware operation mode into normal state,
	 * which should be set before firmware download stage.
	 */
@@ -2476,6 +2469,19 @@ int mt7996_mcu_init(struct mt7996_dev *dev)
				 MCU_WA_PARAM_RED, 0, 0);
}

int mt7996_mcu_init(struct mt7996_dev *dev)
{
	static const struct mt76_mcu_ops mt7996_mcu_ops = {
		.headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */
		.mcu_skb_send_msg = mt7996_mcu_send_message,
		.mcu_parse_response = mt7996_mcu_parse_response,
	};

	dev->mt76.mcu_ops = &mt7996_mcu_ops;

	return mt7996_mcu_init_firmware(dev);
}

void mt7996_mcu_exit(struct mt7996_dev *dev)
{
	mt7996_mcu_restart(&dev->mt76);
Loading