Skip to content

Commit e1a4ec1

Browse files
nxpfranklibjorn-helgaas
authored andcommitted
PCI: dwc: Add generic MSG TLP support for sending PME_Turn_Off when system suspend
Instead of relying on the vendor specific implementations to send the PME_Turn_Off message, introduce a generic way of sending the message using the MSG TLP. This is achieved by reserving a region for MSG TLP of size 'pci->region_align', at the end of the first IORESOURCE_MEM window of the host bridge. And then sending the PME_Turn_Off message during system suspend with the help of iATU. The reason for reserving the MSG TLP region at the end of the IORESOURCE_MEM is to avoid generating holes in between, because when the region is allocated using allocate_resource(), memory will be allocated from the start of the window. Later, if memory gets allocated for an endpoint of size bigger than 'region_align', there will be a hole between MSG TLP region and endpoint memory. This generic implementation is optional for the glue drivers and can be overridden by a custom 'pme_turn_off' callback. Link: https://lore.kernel.org/linux-pci/[email protected] Signed-off-by: Frank Li <[email protected]> Signed-off-by: Krzysztof Wilczyński <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Manivannan Sadhasivam <[email protected]>
1 parent 9972b17 commit e1a4ec1

File tree

2 files changed

+93
-5
lines changed

2 files changed

+93
-5
lines changed

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

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,32 @@ static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
398398
return 0;
399399
}
400400

401+
static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp)
402+
{
403+
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
404+
struct resource_entry *win;
405+
struct resource *res;
406+
407+
win = resource_list_first_type(&pp->bridge->windows, IORESOURCE_MEM);
408+
if (win) {
409+
res = devm_kzalloc(pci->dev, sizeof(*res), GFP_KERNEL);
410+
if (!res)
411+
return;
412+
413+
/*
414+
* Allocate MSG TLP region of size 'region_align' at the end of
415+
* the host bridge window.
416+
*/
417+
res->start = win->res->end - pci->region_align + 1;
418+
res->end = win->res->end;
419+
res->name = "msg";
420+
res->flags = win->res->flags | IORESOURCE_BUSY;
421+
422+
if (!devm_request_resource(pci->dev, win->res, res))
423+
pp->msg_res = res;
424+
}
425+
}
426+
401427
int dw_pcie_host_init(struct dw_pcie_rp *pp)
402428
{
403429
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -484,6 +510,18 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
484510

485511
dw_pcie_iatu_detect(pci);
486512

513+
/*
514+
* Allocate the resource for MSG TLP before programming the iATU
515+
* outbound window in dw_pcie_setup_rc(). Since the allocation depends
516+
* on the value of 'region_align', this has to be done after
517+
* dw_pcie_iatu_detect().
518+
*
519+
* Glue drivers need to set 'use_atu_msg' before dw_pcie_host_init() to
520+
* make use of the generic MSG TLP implementation.
521+
*/
522+
if (pp->use_atu_msg)
523+
dw_pcie_host_request_msg_tlp_res(pp);
524+
487525
ret = dw_pcie_edma_detect(pci);
488526
if (ret)
489527
goto err_free_msi;
@@ -700,7 +738,13 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
700738
atu.type = PCIE_ATU_TYPE_MEM;
701739
atu.cpu_addr = entry->res->start;
702740
atu.pci_addr = entry->res->start - entry->offset;
703-
atu.size = resource_size(entry->res);
741+
742+
/* Adjust iATU size if MSG TLP region was allocated before */
743+
if (pp->msg_res && pp->msg_res->parent == entry->res)
744+
atu.size = resource_size(entry->res) -
745+
resource_size(pp->msg_res);
746+
else
747+
atu.size = resource_size(entry->res);
704748

705749
ret = dw_pcie_prog_outbound_atu(pci, &atu);
706750
if (ret) {
@@ -733,6 +777,8 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
733777
dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
734778
pci->num_ob_windows);
735779

780+
pp->msg_atu_index = i;
781+
736782
i = 0;
737783
resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
738784
if (resource_type(entry->res) != IORESOURCE_MEM)
@@ -838,11 +884,47 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
838884
}
839885
EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
840886

887+
static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
888+
{
889+
struct dw_pcie_ob_atu_cfg atu = { 0 };
890+
void __iomem *mem;
891+
int ret;
892+
893+
if (pci->num_ob_windows <= pci->pp.msg_atu_index)
894+
return -ENOSPC;
895+
896+
if (!pci->pp.msg_res)
897+
return -ENOSPC;
898+
899+
atu.code = PCIE_MSG_CODE_PME_TURN_OFF;
900+
atu.routing = PCIE_MSG_TYPE_R_BC;
901+
atu.type = PCIE_ATU_TYPE_MSG;
902+
atu.size = resource_size(pci->pp.msg_res);
903+
atu.index = pci->pp.msg_atu_index;
904+
905+
atu.cpu_addr = pci->pp.msg_res->start;
906+
907+
ret = dw_pcie_prog_outbound_atu(pci, &atu);
908+
if (ret)
909+
return ret;
910+
911+
mem = ioremap(atu.cpu_addr, pci->region_align);
912+
if (!mem)
913+
return -ENOMEM;
914+
915+
/* A dummy write is converted to a Msg TLP */
916+
writel(0, mem);
917+
918+
iounmap(mem);
919+
920+
return 0;
921+
}
922+
841923
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
842924
{
843925
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
844926
u32 val;
845-
int ret;
927+
int ret = 0;
846928

847929
/*
848930
* If L1SS is supported, then do not put the link into L2 as some
@@ -854,10 +936,13 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
854936
if (dw_pcie_get_ltssm(pci) <= DW_PCIE_LTSSM_DETECT_ACT)
855937
return 0;
856938

857-
if (!pci->pp.ops->pme_turn_off)
858-
return 0;
939+
if (pci->pp.ops->pme_turn_off)
940+
pci->pp.ops->pme_turn_off(&pci->pp);
941+
else
942+
ret = dw_pcie_pme_turn_off(pci);
859943

860-
pci->pp.ops->pme_turn_off(&pci->pp);
944+
if (ret)
945+
return ret;
861946

862947
ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE,
863948
PCIE_PME_TO_L2_TIMEOUT_US/10,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ struct dw_pcie_rp {
340340
struct pci_host_bridge *bridge;
341341
raw_spinlock_t lock;
342342
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
343+
bool use_atu_msg;
344+
int msg_atu_index;
345+
struct resource *msg_res;
343346
};
344347

345348
struct dw_pcie_ep_ops {

0 commit comments

Comments
 (0)