RE: [PATCH 10/11] net: wwan: t9xx: Add power management support
flat view
From: Jagielski, Jedrzej <hidden>
Date: 2026-06-01 12:26:51
Also in:
linux-arm-kernel, linux-doc, linux-mediatek, lkml
From: Jack Wu via B4 Relay <devnull+jackbb_wu.compal.com@kernel.org> Sent: Friday, May 29, 2026 12:32 PM
quoted hunk ↗ jump to hunk
From: Jack Wu <redacted> Add s2idle (S0ix) power management support for the t9xx WWAN driver. In s2idle the modem remains powered. The driver must quiesce host-side DMA engines and service threads before the platform enters low-power state, then restore them on resume. - Suspend: park TRB service threads, stop CLDMA TX/RX queues, disable DPMAIF data path, mask MHCCIF and MSIX interrupts, save PCIe state - Resume: restore PCIe state, re-initialize ATR, unmask MHCCIF, resume CLDMA queues, re-enable DPMAIF data path, unpark TRB service threads Signed-off-by: Jack Wu <redacted> --- drivers/net/wwan/t9xx/pcie/mtk_cldma.c | 94 +++++++++++++++++++++++++++++++++ drivers/net/wwan/t9xx/pcie/mtk_cldma.h | 3 ++ drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c | 35 +++++++++++- drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h | 2 + drivers/net/wwan/t9xx/pcie/mtk_pci.c | 85 ++++++++++++++++++++++++++++- 5 files changed, 216 insertions(+), 3 deletions(-)diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.c b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c index aacb4177d914..a5227eb546f4 100644 --- a/drivers/net/wwan/t9xx/pcie/mtk_cldma.c +++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c@@ -1113,6 +1113,100 @@ int mtk_cldma_exit(struct mtk_ctrl_trans *trans)return 0; } +void mtk_cldma_pm_suspend(struct mtk_md_dev *mdev) +{ + struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv; + struct cldma_dev *cd = trans->dev; + struct cldma_drv_info *drv_info; + struct cldma_drv_ops *drv_ops; + struct rxq *rxq; + struct txq *txq; + int i, q; + + for (i = 0; i < NR_CLDMA; i++) { + drv_info = cd->cldma_drv_info[i]; + if (!drv_info) + continue; + + drv_ops = drv_info->drv_ops; + + /* Stop TX queues and flush pending tx_done_work (suspend phase) */ + drv_ops->cldma_stop_queue(drv_info, DIR_TX, ALLQ); + for (q = 0; q < HW_QUEUE_NUM; q++) { + txq = drv_info->txq[q]; + if (txq) + flush_work(&txq->tx_done_work); + } + + for (q = 0; q < HW_QUEUE_NUM; q++) { + rxq = drv_info->rxq[q]; + if (!rxq) + continue; + atomic_set(&rxq->need_exit, 1); + drv_ops->cldma_stop_queue(drv_info, DIR_RX, q); + flush_work(&rxq->rx_done_work); + } + mtk_pci_mask_irq(mdev, drv_info->pci_ext_irq_id); + } +} + +void mtk_cldma_pm_resume_early(struct mtk_md_dev *mdev) +{ + struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv; + struct cldma_dev *cd = trans->dev; + struct cldma_drv_info *drv_info; + struct cldma_drv_ops *drv_ops; + struct rxq *rxq; + int i, q; + + for (i = 0; i < NR_CLDMA; i++) { + drv_info = cd->cldma_drv_info[i]; + if (!drv_info) + continue; + + drv_ops = drv_info->drv_ops; + + /* Resume RX queues from current HW ring position (no addr reset) */ + for (q = 0; q < HW_QUEUE_NUM; q++) { + rxq = drv_info->rxq[q]; + if (!rxq) + continue; + atomic_set(&rxq->need_exit, 0); + drv_ops->cldma_resume_queue(drv_info, DIR_RX, q); + } + + /* Unmask CLDMA L1 interrupt */ + mtk_pci_unmask_irq(mdev, drv_info->pci_ext_irq_id); + } +} + +void mtk_cldma_pm_resume(struct mtk_md_dev *mdev) +{ + struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv; + struct cldma_dev *cd = trans->dev; + struct cldma_drv_info *drv_info; + struct cldma_drv_ops *drv_ops; + struct txq *txq; + int i, q; + + for (i = 0; i < NR_CLDMA; i++) { + drv_info = cd->cldma_drv_info[i]; + if (!drv_info) + continue; + + drv_ops = drv_info->drv_ops; + + /* Restart TX queues that have pending descriptors */ + for (q = 0; q < HW_QUEUE_NUM; q++) { + txq = drv_info->txq[q]; + if (!txq) + continue; + if (atomic_read(&txq->req_budget) < txq->nr_gpds) + mtk_cldma_start_xfer(drv_info, q); + } + } +} + static int mtk_cldma_open(struct cldma_dev *cd, struct sk_buff *skb) { struct trb_open_priv *trb_open_priv = (struct trb_open_priv *)skb->data;diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.h b/drivers/net/wwan/t9xx/pcie/mtk_cldma.h index 04f83ff0e37d..fd39985f75e7 100644 --- a/drivers/net/wwan/t9xx/pcie/mtk_cldma.h +++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma.h@@ -163,6 +163,9 @@ int mtk_cldma_get_tx_budget(void *dev, enum mtk_hif_id hif_id, u32 qno);int mtk_cldma_trb_process(void *dev, struct sk_buff *skb); void mtk_cldma_fsm_state_listener(struct mtk_fsm_param *param, struct mtk_ctrl_trans *trans); int mtk_cldma_check_ch_cfg(void *dev, struct queue_info *que); +void mtk_cldma_pm_suspend(struct mtk_md_dev *mdev); +void mtk_cldma_pm_resume_early(struct mtk_md_dev *mdev); +void mtk_cldma_pm_resume(struct mtk_md_dev *mdev); #define drv_ops_name(NAME) cldma_drv_ops_##NAME #define cldma_regs_name(NAME) mtk_cldma_regs_##NAMEdiff --git a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c index 43803587bfc3..63273a85e532 100644 --- a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c +++ b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c@@ -205,6 +205,7 @@ struct mtk_dpmaif_ctlb {struct dpmaif_irq_param *irq_params; bool dpmaif_sw_reset; + bool trans_enabled; unsigned char rxq_cnt; unsigned char txq_cnt; };@@ -1687,10 +1688,16 @@ static void mtk_dpmaif_trans_disable(struct mtk_dpmaif_ctlb *dcb)static void mtk_dpmaif_trans_ctl(struct mtk_dpmaif_ctlb *dcb, bool enable) { if (enable) { - if (dcb->dpmaif_state == DPMAIF_STATE_PWRON) + if (!dcb->trans_enabled && + dcb->dpmaif_state == DPMAIF_STATE_PWRON) { + dcb->trans_enabled = true; mtk_dpmaif_trans_enable(dcb); + } } else { - mtk_dpmaif_trans_disable(dcb); + if (dcb->trans_enabled) {
should it be done unconditionally from DPMAIF_STATE_PWRON?
quoted hunk ↗ jump to hunk
+ dcb->trans_enabled = false; + mtk_dpmaif_trans_disable(dcb); + } } }@@ -2060,6 +2067,30 @@ static int mtk_dpmaif_stop(struct mtk_md_dev *mdev)return 0; } +void mtk_dpmaif_pm_suspend(struct mtk_md_dev *mdev) +{ + struct mtk_dpmaif_ctlb *dcb = ((struct mtk_data_blk *)(mdev->data_blk))->dcb; + + if (!dcb) + return; + + mutex_lock(&dcb->trans_ctl_lock); + mtk_dpmaif_trans_ctl(dcb, false); + mutex_unlock(&dcb->trans_ctl_lock); +} + +void mtk_dpmaif_pm_resume(struct mtk_md_dev *mdev) +{ + struct mtk_dpmaif_ctlb *dcb = ((struct mtk_data_blk *)(mdev->data_blk))->dcb; + + if (!dcb) + return; + + mutex_lock(&dcb->trans_ctl_lock); + mtk_dpmaif_trans_ctl(dcb, true); + mutex_unlock(&dcb->trans_ctl_lock); +} + static void mtk_dpmaif_clear(struct mtk_md_dev *mdev) { struct mtk_dpmaif_ctlb *dcb = ((struct mtk_data_blk *)(mdev->data_blk))->dcb;diff --git a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h index e7e2f333141c..20fd53fd44b5 100644 --- a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h +++ b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h@@ -10,5 +10,7 @@int mtk_pcie_data_init(struct mtk_md_dev *mdev); int mtk_pcie_data_exit(struct mtk_md_dev *mdev); +void mtk_dpmaif_pm_suspend(struct mtk_md_dev *mdev); +void mtk_dpmaif_pm_resume(struct mtk_md_dev *mdev); #endif /* __MTK_DPMAIF_H__ */diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c index baac3692f1e3..f659c9a7aa96 100644 --- a/drivers/net/wwan/t9xx/pcie/mtk_pci.c +++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c@@ -11,8 +11,10 @@#include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/kernel.h> +#include <linux/kthread.h> #include <linux/module.h> +#include "mtk_cldma.h" #include "mtk_dev.h" #include "mtk_dpmaif.h" #include "mtk_trans_ctrl.h"@@ -199,7 +201,6 @@ int mtk_pci_register_irq(struct mtk_md_dev *mdev, int irq_id,} priv->irq_cb_list[irq_id] = irq_cb; priv->irq_cb_data[irq_id] = data; - return 0; }@@ -970,11 +971,93 @@ static const struct pci_error_handlers mtk_pci_err_handler = {.error_detected = mtk_pci_error_detected, }; +static void mtk_pci_pm_trb_park(struct mtk_md_dev *mdev) +{ + struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv; + int i; + + for (i = 0; i < trans->trb_srv_num; i++) + kthread_park(trans->trb_srv[i]->trb_thread); +} + +static void mtk_pci_pm_trb_unpark(struct mtk_md_dev *mdev) +{ + struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv; + int i; + + for (i = 0; i < trans->trb_srv_num; i++) + kthread_unpark(trans->trb_srv[i]->trb_thread); +} + +static int __maybe_unused mtk_pci_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct mtk_md_dev *mdev = pci_get_drvdata(pdev); + struct mtk_pci_priv *priv = mdev->hw_priv;
RCT
quoted hunk ↗ jump to hunk
+ + mtk_pci_pm_trb_park(mdev); + + mtk_cldma_pm_suspend(mdev); + + mtk_dpmaif_pm_suspend(mdev); + + /* Mask MHCCIF interrupt */ + mtk_pci_mask_irq(mdev, priv->mhccif_irq_id); + + /* Mask all MSI-X interrupts at the device level */ + mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0, U32_MAX); + + /* Save PCI configuration space */ + pci_save_state(pdev); + + return 0; +} + +static int __maybe_unused mtk_pci_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct mtk_md_dev *mdev = pci_get_drvdata(pdev); + struct mtk_pci_priv *priv = mdev->hw_priv; + int ret; + + /* Restore PCIe configuration space (including MSI-X enable bits) */
these comments are rather obvious
+ pci_restore_state(pdev);
+
+ /* Re-enable bus mastering for DMA */
+ pci_set_master(pdev);
+
+ /* Restore ATR (address translation registers in MMIO BAR space) */
+ ret = priv->cfg->atr_init(mdev);
+ if (ret) {
+ dev_err(mdev->dev, "PM: failed to re-init ATR on resume\n");
+ return ret;
+ }
+
+ /* Unmask MHCCIF interrupt */
+ mtk_pci_unmask_irq(mdev, priv->mhccif_irq_id);
+
+ mtk_cldma_pm_resume_early(mdev);
+
+ /* Restart CLDMA TX queues that have pending descriptors */
+ mtk_cldma_pm_resume(mdev);
+
+ mtk_dpmaif_pm_resume(mdev);
+
+ mtk_pci_pm_trb_unpark(mdev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mtk_pci_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mtk_pci_pm_suspend, mtk_pci_pm_resume)
+};
+
static struct pci_driver mtk_pci_drv = {
.name = "mtk_pci_drv",
.id_table = t9xx_pci_table,
.probe = mtk_pci_probe,
.remove = mtk_pci_remove,
+ .driver.pm = &mtk_pci_pm_ops,
.err_handler = &mtk_pci_err_handler
};
--
2.34.1