mt76: mt7615: introduce PM support
authorLorenzo Bianconi <lorenzo@kernel.org>
Fri, 1 May 2020 10:36:15 +0000 (12:36 +0200)
committerFelix Fietkau <nbd@nbd.name>
Tue, 12 May 2020 17:52:34 +0000 (19:52 +0200)
Introduce suspend/resume to mt7615e driver

Co-developed-by: Wan-Feng Jiang <Wan-Feng.Jiang@mediatek.com>
Signed-off-by: Wan-Feng Jiang <Wan-Feng.Jiang@mediatek.com>
Co-developed-by: Soul Huang <Soul.Huang@mediatek.com>
Signed-off-by: Soul Huang <Soul.Huang@mediatek.com>
Co-developed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7615/dma.c
drivers/net/wireless/mediatek/mt76/mt7615/mac.c
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
drivers/net/wireless/mediatek/mt76/mt7615/mmio.c
drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
drivers/net/wireless/mediatek/mt76/mt7615/pci.c
drivers/net/wireless/mediatek/mt76/mt7615/regs.h

index 0b1fbddd1c3fdabfaa9c7cd3b21a6199fb90c426..5a124610d4af02d5bd5084ccee5c2c2f59bd473a 100644 (file)
@@ -130,6 +130,43 @@ static int mt7615_poll_tx(struct napi_struct *napi, int budget)
        return 0;
 }
 
+int mt7615_wait_pdma_busy(struct mt7615_dev *dev)
+{
+       struct mt76_dev *mdev = &dev->mt76;
+
+       if (!is_mt7663(mdev)) {
+               u32 mask = MT_PDMA_TX_BUSY | MT_PDMA_RX_BUSY;
+               u32 reg = mt7615_reg_map(dev, MT_PDMA_BUSY);
+
+               if (!mt76_poll_msec(dev, reg, mask, 0, 1000)) {
+                       dev_err(mdev->dev, "PDMA engine busy\n");
+                       return -EIO;
+               }
+
+               return 0;
+       }
+
+       if (!mt76_poll_msec(dev, MT_PDMA_BUSY_STATUS,
+                           MT_PDMA_TX_IDX_BUSY, 0, 1000)) {
+               dev_err(mdev->dev, "PDMA engine tx busy\n");
+               return -EIO;
+       }
+
+       if (!mt76_poll_msec(dev, MT_PSE_PG_INFO,
+                           MT_PSE_SRC_CNT, 0, 1000)) {
+               dev_err(mdev->dev, "PSE engine busy\n");
+               return -EIO;
+       }
+
+       if (!mt76_poll_msec(dev, MT_PDMA_BUSY_STATUS,
+                           MT_PDMA_BUSY_IDX, 0, 1000)) {
+               dev_err(mdev->dev, "PDMA engine busy\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
 static void mt7622_dma_sched_init(struct mt7615_dev *dev)
 {
        u32 reg = mt7615_reg_map(dev, MT_DMASHDL_BASE);
index 5c09787b0d761443261a38caebbcacd79bcbf14a..7d65a3fb0c239c75894bac2936d1361db8072ba6 100644 (file)
@@ -1807,8 +1807,7 @@ mt7615_update_beacons(struct mt7615_dev *dev)
                mt7615_update_vif_beacon, dev->mt76.phy2->hw);
 }
 
-static void
-mt7615_dma_reset(struct mt7615_dev *dev)
+void mt7615_dma_reset(struct mt7615_dev *dev)
 {
        int i;
 
@@ -1827,6 +1826,7 @@ mt7615_dma_reset(struct mt7615_dev *dev)
                 MT_WPDMA_GLO_CFG_RX_DMA_EN | MT_WPDMA_GLO_CFG_TX_DMA_EN |
                 MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE);
 }
+EXPORT_SYMBOL_GPL(mt7615_dma_reset);
 
 void mt7615_mac_reset_work(struct work_struct *work)
 {
index 72e2e1cbab59eacd8ff9496ec0b8f37cdee72354..9b13402244489b0e7d5dc0691c186e59ff4a1515 100644 (file)
@@ -1683,7 +1683,7 @@ static void mt7622_trigger_hif_int(struct mt7615_dev *dev, bool en)
                           !en * MT_INFRACFG_MISC_AP2CONN_WAKE);
 }
 
-static int mt7615_driver_own(struct mt7615_dev *dev)
+int mt7615_driver_own(struct mt7615_dev *dev)
 {
        struct mt76_dev *mdev = &dev->mt76;
        u32 addr;
@@ -1703,8 +1703,9 @@ static int mt7615_driver_own(struct mt7615_dev *dev)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(mt7615_driver_own);
 
-static int mt7615_firmware_own(struct mt7615_dev *dev)
+int mt7615_firmware_own(struct mt7615_dev *dev)
 {
        u32 addr;
 
@@ -1723,6 +1724,7 @@ static int mt7615_firmware_own(struct mt7615_dev *dev)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(mt7615_firmware_own);
 
 static int mt7615_load_patch(struct mt7615_dev *dev, u32 addr, const char *name)
 {
index c9f5b1ce70aeb2648baefc12e26dc3116338e8f7..e670393506f0b4dbe6a66646519e30f4db3e07b0 100644 (file)
@@ -15,6 +15,8 @@ const u32 mt7615e_reg_map[] = {
        [MT_ARB_BASE]           = 0x20c00,
        [MT_HIF_BASE]           = 0x04000,
        [MT_CSR_BASE]           = 0x07000,
+       [MT_PLE_BASE]           = 0x08000,
+       [MT_PSE_BASE]           = 0x0c000,
        [MT_PHY_BASE]           = 0x10000,
        [MT_CFG_BASE]           = 0x20200,
        [MT_AGG_BASE]           = 0x20a00,
@@ -40,6 +42,8 @@ const u32 mt7663e_reg_map[] = {
        [MT_ARB_BASE]           = 0x20c00,
        [MT_HIF_BASE]           = 0x04000,
        [MT_CSR_BASE]           = 0x07000,
+       [MT_PLE_BASE]           = 0x08000,
+       [MT_PSE_BASE]           = 0x0c000,
        [MT_PHY_BASE]           = 0x10000,
        [MT_CFG_BASE]           = 0x20000,
        [MT_AGG_BASE]           = 0x22000,
index 3e6bc3ce914a7028d17141d6ea156b603dcf248b..be9188e40259a6558354413869d65fa9ebb1b122 100644 (file)
@@ -380,6 +380,7 @@ int mt7615_eeprom_init(struct mt7615_dev *dev, u32 addr);
 int mt7615_eeprom_get_power_index(struct mt7615_dev *dev,
                                  struct ieee80211_channel *chan,
                                  u8 chain_idx);
+int mt7615_wait_pdma_busy(struct mt7615_dev *dev);
 int mt7615_dma_init(struct mt7615_dev *dev);
 void mt7615_dma_cleanup(struct mt7615_dev *dev);
 int mt7615_mcu_init(struct mt7615_dev *dev);
@@ -436,6 +437,7 @@ static inline u16 mt7615_wtbl_size(struct mt7615_dev *dev)
                return MT7615_WTBL_SIZE;
 }
 
+void mt7615_dma_reset(struct mt7615_dev *dev);
 void mt7615_scan_work(struct work_struct *work);
 void mt7615_ps_work(struct work_struct *work);
 void mt7615_init_txpower(struct mt7615_dev *dev,
@@ -526,6 +528,9 @@ int mt7615_mcu_apply_tx_dpd(struct mt7615_phy *phy);
 void m7615_mcu_set_ps_iter(void *priv, u8 *mac, struct ieee80211_vif *vif);
 int mt7615_dfs_init_radar_detector(struct mt7615_phy *phy);
 
+int mt7615_firmware_own(struct mt7615_dev *dev);
+int mt7615_driver_own(struct mt7615_dev *dev);
+
 int mt7615_init_debugfs(struct mt7615_dev *dev);
 int mt7615_mcu_wait_response(struct mt7615_dev *dev, int cmd, int seq);
 
index 0605c908059e56d789b823a8af9758628c52b694..88ff1456452178033c70af0bd7b22d56758f2f96 100644 (file)
@@ -66,11 +66,119 @@ static void mt7615_pci_remove(struct pci_dev *pdev)
        pci_free_irq_vectors(pdev);
 }
 
+#ifdef CONFIG_PM
+static int mt7615_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct mt76_dev *mdev = pci_get_drvdata(pdev);
+       struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
+       bool hif_suspend;
+       int i, err;
+
+       hif_suspend = !test_bit(MT76_STATE_SUSPEND, &dev->mphy.state) &&
+                     mt7615_firmware_offload(dev);
+       if (hif_suspend) {
+               err = mt7615_mcu_set_hif_suspend(dev, true);
+               if (err)
+                       return err;
+       }
+
+       napi_disable(&mdev->tx_napi);
+       tasklet_kill(&mdev->tx_tasklet);
+
+       for (i = 0; i < ARRAY_SIZE(mdev->q_rx); i++)
+               napi_disable(&mdev->napi[i]);
+       tasklet_kill(&dev->irq_tasklet);
+
+       mt7615_dma_reset(dev);
+
+       err = mt7615_wait_pdma_busy(dev);
+       if (err)
+               goto restore;
+
+       if (is_mt7663(mdev)) {
+               mt76_set(dev, MT_PDMA_SLP_PROT, MT_PDMA_AXI_SLPPROT_ENABLE);
+               if (!mt76_poll_msec(dev, MT_PDMA_SLP_PROT,
+                                   MT_PDMA_AXI_SLPPROT_RDY,
+                                   MT_PDMA_AXI_SLPPROT_RDY, 1000)) {
+                       dev_err(mdev->dev, "PDMA sleep protection failed\n");
+                       err = -EIO;
+                       goto restore;
+               }
+       }
+
+       pci_enable_wake(pdev, pci_choose_state(pdev, state), true);
+       pci_save_state(pdev);
+       err = pci_set_power_state(pdev, pci_choose_state(pdev, state));
+       if (err)
+               goto restore;
+
+       err = mt7615_firmware_own(dev);
+       if (err)
+               goto restore;
+
+       return 0;
+
+restore:
+       for (i = 0; i < ARRAY_SIZE(mdev->q_rx); i++)
+               napi_enable(&mdev->napi[i]);
+       napi_enable(&mdev->tx_napi);
+       if (hif_suspend)
+               mt7615_mcu_set_hif_suspend(dev, false);
+
+       return err;
+}
+
+static int mt7615_pci_resume(struct pci_dev *pdev)
+{
+       struct mt76_dev *mdev = pci_get_drvdata(pdev);
+       struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
+       bool pdma_reset;
+       int i, err;
+
+       err = mt7615_driver_own(dev);
+       if (err < 0)
+               return err;
+
+       err = pci_set_power_state(pdev, PCI_D0);
+       if (err)
+               return err;
+
+       pci_restore_state(pdev);
+
+       if (is_mt7663(&dev->mt76)) {
+               mt76_clear(dev, MT_PDMA_SLP_PROT, MT_PDMA_AXI_SLPPROT_ENABLE);
+               mt76_wr(dev, MT_PCIE_IRQ_ENABLE, 1);
+       }
+
+       pdma_reset = !mt76_rr(dev, MT_WPDMA_TX_RING0_CTRL0) &&
+                    !mt76_rr(dev, MT_WPDMA_TX_RING0_CTRL1);
+       if (pdma_reset)
+               dev_err(mdev->dev, "PDMA engine must be reinitialized\n");
+
+       for (i = 0; i < ARRAY_SIZE(mdev->q_rx); i++) {
+               napi_enable(&mdev->napi[i]);
+               napi_schedule(&mdev->napi[i]);
+       }
+       napi_enable(&mdev->tx_napi);
+       napi_schedule(&mdev->tx_napi);
+
+       if (!test_bit(MT76_STATE_SUSPEND, &dev->mphy.state) &&
+           mt7615_firmware_offload(dev))
+               err = mt7615_mcu_set_hif_suspend(dev, false);
+
+       return err;
+}
+#endif /* CONFIG_PM */
+
 struct pci_driver mt7615_pci_driver = {
        .name           = KBUILD_MODNAME,
        .id_table       = mt7615_pci_device_table,
        .probe          = mt7615_pci_probe,
        .remove         = mt7615_pci_remove,
+#ifdef CONFIG_PM
+       .suspend        = mt7615_pci_suspend,
+       .resume         = mt7615_pci_resume,
+#endif /* CONFIG_PM */
 };
 
 MODULE_DEVICE_TABLE(pci, mt7615_pci_device_table);
index 68d30bcc087a6406b6db2341288507939a6ae4b9..aee433a9eff6818ec1a4d7a127bf0ef1bb921630 100644 (file)
@@ -12,6 +12,8 @@ enum mt7615_reg_base {
        MT_ARB_BASE,
        MT_HIF_BASE,
        MT_CSR_BASE,
+       MT_PLE_BASE,
+       MT_PSE_BASE,
        MT_PHY_BASE,
        MT_CFG_BASE,
        MT_AGG_BASE,
@@ -63,6 +65,17 @@ enum mt7615_reg_base {
 #define MT_HIF_RST                     MT_HIF(0x100)
 #define MT_HIF_LOGIC_RST_N             BIT(4)
 
+#define MT_PDMA_SLP_PROT               MT_HIF(0x154)
+#define MT_PDMA_AXI_SLPPROT_ENABLE     BIT(0)
+#define MT_PDMA_AXI_SLPPROT_RDY                BIT(16)
+
+#define MT_PDMA_BUSY_STATUS            MT_HIF(0x168)
+#define MT_PDMA_TX_IDX_BUSY            BIT(2)
+#define MT_PDMA_BUSY_IDX               BIT(31)
+
+#define MT_WPDMA_TX_RING0_CTRL0                MT_HIF(0x300)
+#define MT_WPDMA_TX_RING0_CTRL1                MT_HIF(0x304)
+
 #define MT7663_MCU_PCIE_REMAP_2_OFFSET GENMASK(15, 0)
 #define MT7663_MCU_PCIE_REMAP_2_BASE   GENMASK(31, 16)
 
@@ -138,8 +151,7 @@ enum mt7615_reg_base {
 #define MT_CSR(ofs)                    ((dev)->reg_map[MT_CSR_BASE] + (ofs))
 #define MT_CONN_HIF_ON_LPCTL           MT_CSR(0x000)
 
-#define MT_PLE_BASE                    0x8000
-#define MT_PLE(ofs)                    (MT_PLE_BASE + (ofs))
+#define MT_PLE(ofs)                    ((dev)->reg_map[MT_PLE_BASE] + (ofs))
 
 #define MT_PLE_FL_Q0_CTRL              MT_PLE(0x1b0)
 #define MT_PLE_FL_Q1_CTRL              MT_PLE(0x1b4)
@@ -149,6 +161,14 @@ enum mt7615_reg_base {
 #define MT_PLE_AC_QEMPTY(ac, n)                MT_PLE(0x300 + 0x10 * (ac) + \
                                               ((n) << 2))
 
+#define MT_PSE(ofs)                    ((dev)->reg_map[MT_PSE_BASE] + (ofs))
+#define MT_PSE_QUEUE_EMPTY             MT_PSE(0x0b4)
+#define MT_HIF_0_EMPTY_MASK            BIT(16)
+#define MT_HIF_1_EMPTY_MASK            BIT(17)
+#define MT_HIF_ALL_EMPTY_MASK          GENMASK(17, 16)
+#define MT_PSE_PG_INFO                 MT_PSE(0x194)
+#define MT_PSE_SRC_CNT                 GENMASK(27, 16)
+
 #define MT_WF_PHY_BASE                 ((dev)->reg_map[MT_PHY_BASE])
 #define MT_WF_PHY(ofs)                 (MT_WF_PHY_BASE + (ofs))
 
@@ -482,6 +502,10 @@ enum mt7615_reg_base {
 #define MT_LED_STATUS_ON               GENMASK(23, 16)
 #define MT_LED_STATUS_DURATION         GENMASK(15, 0)
 
+#define MT_PDMA_BUSY                   0x82000504
+#define MT_PDMA_TX_BUSY                        BIT(0)
+#define MT_PDMA_RX_BUSY                        BIT(1)
+
 #define MT_EFUSE_BASE                  ((dev)->reg_map[MT_EFUSE_ADDR_BASE])
 #define MT_EFUSE_BASE_CTRL             0x000
 #define MT_EFUSE_BASE_CTRL_EMPTY       BIT(30)