Skip to content

Commit 867ab11

Browse files
Mani-Sadhasivambjorn-helgaas
authored andcommitted
PCI: dwc: ep: Add a generic dw_pcie_ep_linkdown() API to handle Link Down event
Per PCIe r6.0, sec 5.2, a Link Down event can happen under any of the following circumstances: 1. Fundamental/Hot reset 2. Link disable transmission by upstream component 3. Moving from L2/L3 to L0 In those cases, Link Down causes some non-sticky DWC registers to lose the state (like REBAR, etc.), so drivers need to reinitialize them to function properly once the link comes back again. This is not a problem for drivers supporting PERST# IRQ, since they can reinitialize the registers in the PERST# IRQ callback. But for the drivers not supporting PERST#, there is no way they can reinitialize the registers other than relying on Link Down IRQ received when the link goes down. So add a DWC generic API dw_pcie_ep_linkdown() that reinitializes the non-sticky registers and also notifies the EPF drivers about link going down. This API can also be used by the drivers supporting PERST# to handle the scenario (2) mentioned above. NOTE: For the sake of code organization, move the dw_pcie_ep_linkup() definition just above dw_pcie_ep_linkdown(). Link: https://lore.kernel.org/linux-pci/[email protected] Tested-by: Niklas Cassel <[email protected]> Signed-off-by: Manivannan Sadhasivam <[email protected]> Signed-off-by: Krzysztof Wilczyński <[email protected]> [bhelgaas: update spec citation] Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Niklas Cassel <[email protected]>
1 parent e1a4ec1 commit 867ab11

File tree

2 files changed

+73
-35
lines changed

2 files changed

+73
-35
lines changed

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

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,6 @@
1515
#include <linux/pci-epc.h>
1616
#include <linux/pci-epf.h>
1717

18-
/**
19-
* dw_pcie_ep_linkup - Notify EPF drivers about Link Up event
20-
* @ep: DWC EP device
21-
*/
22-
void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
23-
{
24-
struct pci_epc *epc = ep->epc;
25-
26-
pci_epc_linkup(epc);
27-
}
28-
EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup);
29-
3018
/**
3119
* dw_pcie_ep_init_notify - Notify EPF drivers about EPC initialization complete
3220
* @ep: DWC EP device
@@ -685,6 +673,34 @@ static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
685673
return 0;
686674
}
687675

676+
static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
677+
{
678+
unsigned int offset;
679+
unsigned int nbars;
680+
u32 reg, i;
681+
682+
offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
683+
684+
dw_pcie_dbi_ro_wr_en(pci);
685+
686+
if (offset) {
687+
reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
688+
nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >>
689+
PCI_REBAR_CTRL_NBAR_SHIFT;
690+
691+
/*
692+
* PCIe r6.0, sec 7.8.6.2 require us to support at least one
693+
* size in the range from 1 MB to 512 GB. Advertise support
694+
* for 1 MB BAR size only.
695+
*/
696+
for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL)
697+
dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0);
698+
}
699+
700+
dw_pcie_setup(pci);
701+
dw_pcie_dbi_ro_wr_dis(pci);
702+
}
703+
688704
/**
689705
* dw_pcie_ep_init_registers - Initialize DWC EP specific registers
690706
* @ep: DWC EP device
@@ -699,13 +715,11 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
699715
struct dw_pcie_ep_func *ep_func;
700716
struct device *dev = pci->dev;
701717
struct pci_epc *epc = ep->epc;
702-
unsigned int offset, ptm_cap_base;
703-
unsigned int nbars;
718+
u32 ptm_cap_base, reg;
704719
u8 hdr_type;
705720
u8 func_no;
706-
int i, ret;
707721
void *addr;
708-
u32 reg;
722+
int ret;
709723

710724
hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) &
711725
PCI_HEADER_TYPE_MASK;
@@ -768,25 +782,8 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
768782
if (ep->ops->init)
769783
ep->ops->init(ep);
770784

771-
offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
772785
ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);
773786

774-
dw_pcie_dbi_ro_wr_en(pci);
775-
776-
if (offset) {
777-
reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
778-
nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >>
779-
PCI_REBAR_CTRL_NBAR_SHIFT;
780-
781-
/*
782-
* PCIe r6.0, sec 7.8.6.2 require us to support at least one
783-
* size in the range from 1 MB to 512 GB. Advertise support
784-
* for 1 MB BAR size only.
785-
*/
786-
for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL)
787-
dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, BIT(4));
788-
}
789-
790787
/*
791788
* PTM responder capability can be disabled only after disabling
792789
* PTM root capability.
@@ -803,8 +800,7 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
803800
dw_pcie_dbi_ro_wr_dis(pci);
804801
}
805802

806-
dw_pcie_setup(pci);
807-
dw_pcie_dbi_ro_wr_dis(pci);
803+
dw_pcie_ep_init_non_sticky_registers(pci);
808804

809805
return 0;
810806

@@ -815,6 +811,43 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
815811
}
816812
EXPORT_SYMBOL_GPL(dw_pcie_ep_init_registers);
817813

814+
/**
815+
* dw_pcie_ep_linkup - Notify EPF drivers about Link Up event
816+
* @ep: DWC EP device
817+
*/
818+
void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
819+
{
820+
struct pci_epc *epc = ep->epc;
821+
822+
pci_epc_linkup(epc);
823+
}
824+
EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup);
825+
826+
/**
827+
* dw_pcie_ep_linkdown - Notify EPF drivers about Link Down event
828+
* @ep: DWC EP device
829+
*
830+
* Non-sticky registers are also initialized before sending the notification to
831+
* the EPF drivers. This is needed since the registers need to be initialized
832+
* before the link comes back again.
833+
*/
834+
void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep)
835+
{
836+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
837+
struct pci_epc *epc = ep->epc;
838+
839+
/*
840+
* Initialize the non-sticky DWC registers as they would've reset post
841+
* Link Down. This is specifically needed for drivers not supporting
842+
* PERST# as they have no way to reinitialize the registers before the
843+
* link comes back again.
844+
*/
845+
dw_pcie_ep_init_non_sticky_registers(pci);
846+
847+
pci_epc_linkdown(epc);
848+
}
849+
EXPORT_SYMBOL_GPL(dw_pcie_ep_linkdown);
850+
818851
/**
819852
* dw_pcie_ep_init - Initialize the endpoint device
820853
* @ep: DWC EP device

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ static inline void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus,
681681

682682
#ifdef CONFIG_PCIE_DW_EP
683683
void dw_pcie_ep_linkup(struct dw_pcie_ep *ep);
684+
void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep);
684685
int dw_pcie_ep_init(struct dw_pcie_ep *ep);
685686
int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep);
686687
void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep);
@@ -701,6 +702,10 @@ static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
701702
{
702703
}
703704

705+
static inline void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep)
706+
{
707+
}
708+
704709
static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep)
705710
{
706711
return 0;

0 commit comments

Comments
 (0)