Skip to content

Commit 4774faf

Browse files
nxpfrankliLorenzo Pieralisi
authored andcommitted
PCI: dwc: Implement generic suspend/resume functionality
Introduce an helper function (dw_pcie_get_ltssm()) to retrieve SMLH_LTSS_STATE. Add common dw_pcie_suspend(resume)_noirq() API to implement the DWC controller generic suspend/resume functionality. Add a controller specific callback to send the PME_Turn_Off message (ie .pme_turn_off) for controller platform specific PME handling. Link: https://lore.kernel.org/r/20230821184815.2167131-3-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> [lpieralisi@kernel.org: commit log] Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Acked-by: Manivannan Sadhasivam <mani@kernel.org>
1 parent e78bd50 commit 4774faf

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

drivers/pci/controller/dwc/pcie-designware-host.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* Author: Jingoo Han <jg1.han@samsung.com>
99
*/
1010

11+
#include <linux/iopoll.h>
1112
#include <linux/irqchip/chained_irq.h>
1213
#include <linux/irqdomain.h>
1314
#include <linux/msi.h>
@@ -16,6 +17,7 @@
1617
#include <linux/pci_regs.h>
1718
#include <linux/platform_device.h>
1819

20+
#include "../../pci.h"
1921
#include "pcie-designware.h"
2022

2123
static struct pci_ops dw_pcie_ops;
@@ -812,3 +814,72 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
812814
return 0;
813815
}
814816
EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
817+
818+
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
819+
{
820+
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
821+
u32 val;
822+
int ret;
823+
824+
/*
825+
* If L1SS is supported, then do not put the link into L2 as some
826+
* devices such as NVMe expect low resume latency.
827+
*/
828+
if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1)
829+
return 0;
830+
831+
if (dw_pcie_get_ltssm(pci) <= DW_PCIE_LTSSM_DETECT_ACT)
832+
return 0;
833+
834+
if (!pci->pp.ops->pme_turn_off)
835+
return 0;
836+
837+
pci->pp.ops->pme_turn_off(&pci->pp);
838+
839+
ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE,
840+
PCIE_PME_TO_L2_TIMEOUT_US/10,
841+
PCIE_PME_TO_L2_TIMEOUT_US, false, pci);
842+
if (ret) {
843+
dev_err(pci->dev, "Timeout waiting for L2 entry! LTSSM: 0x%x\n", val);
844+
return ret;
845+
}
846+
847+
if (pci->pp.ops->host_deinit)
848+
pci->pp.ops->host_deinit(&pci->pp);
849+
850+
pci->suspended = true;
851+
852+
return ret;
853+
}
854+
EXPORT_SYMBOL_GPL(dw_pcie_suspend_noirq);
855+
856+
int dw_pcie_resume_noirq(struct dw_pcie *pci)
857+
{
858+
int ret;
859+
860+
if (!pci->suspended)
861+
return 0;
862+
863+
pci->suspended = false;
864+
865+
if (pci->pp.ops->host_init) {
866+
ret = pci->pp.ops->host_init(&pci->pp);
867+
if (ret) {
868+
dev_err(pci->dev, "Host init failed: %d\n", ret);
869+
return ret;
870+
}
871+
}
872+
873+
dw_pcie_setup_rc(&pci->pp);
874+
875+
ret = dw_pcie_start_link(pci);
876+
if (ret)
877+
return ret;
878+
879+
ret = dw_pcie_wait_for_link(pci);
880+
if (ret)
881+
return ret;
882+
883+
return ret;
884+
}
885+
EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq);

drivers/pci/controller/dwc/pcie-designware.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,21 @@ enum dw_pcie_core_rst {
288288
DW_PCIE_NUM_CORE_RSTS
289289
};
290290

291+
enum dw_pcie_ltssm {
292+
/* Need to align with PCIE_PORT_DEBUG0 bits 0:5 */
293+
DW_PCIE_LTSSM_DETECT_QUIET = 0x0,
294+
DW_PCIE_LTSSM_DETECT_ACT = 0x1,
295+
DW_PCIE_LTSSM_L0 = 0x11,
296+
DW_PCIE_LTSSM_L2_IDLE = 0x15,
297+
298+
DW_PCIE_LTSSM_UNKNOWN = 0xFFFFFFFF,
299+
};
300+
291301
struct dw_pcie_host_ops {
292302
int (*host_init)(struct dw_pcie_rp *pp);
293303
void (*host_deinit)(struct dw_pcie_rp *pp);
294304
int (*msi_host_init)(struct dw_pcie_rp *pp);
305+
void (*pme_turn_off)(struct dw_pcie_rp *pp);
295306
};
296307

297308
struct dw_pcie_rp {
@@ -364,6 +375,7 @@ struct dw_pcie_ops {
364375
void (*write_dbi2)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
365376
size_t size, u32 val);
366377
int (*link_up)(struct dw_pcie *pcie);
378+
enum dw_pcie_ltssm (*get_ltssm)(struct dw_pcie *pcie);
367379
int (*start_link)(struct dw_pcie *pcie);
368380
void (*stop_link)(struct dw_pcie *pcie);
369381
};
@@ -393,6 +405,7 @@ struct dw_pcie {
393405
struct reset_control_bulk_data app_rsts[DW_PCIE_NUM_APP_RSTS];
394406
struct reset_control_bulk_data core_rsts[DW_PCIE_NUM_CORE_RSTS];
395407
struct gpio_desc *pe_rst;
408+
bool suspended;
396409
};
397410

398411
#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
@@ -431,6 +444,9 @@ int dw_pcie_edma_detect(struct dw_pcie *pci);
431444
void dw_pcie_edma_remove(struct dw_pcie *pci);
432445
void dw_pcie_print_link_status(struct dw_pcie *pci);
433446

447+
int dw_pcie_suspend_noirq(struct dw_pcie *pci);
448+
int dw_pcie_resume_noirq(struct dw_pcie *pci);
449+
434450
static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
435451
{
436452
dw_pcie_write_dbi(pci, reg, 0x4, val);
@@ -502,6 +518,18 @@ static inline void dw_pcie_stop_link(struct dw_pcie *pci)
502518
pci->ops->stop_link(pci);
503519
}
504520

521+
static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
522+
{
523+
u32 val;
524+
525+
if (pci->ops && pci->ops->get_ltssm)
526+
return pci->ops->get_ltssm(pci);
527+
528+
val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0);
529+
530+
return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val);
531+
}
532+
505533
#ifdef CONFIG_PCIE_DW_HOST
506534
irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp);
507535
int dw_pcie_setup_rc(struct dw_pcie_rp *pp);

0 commit comments

Comments
 (0)