PCI: layerscape: Add the endpoint linkup notifier support
[ Upstream commit061cbfab09
] Layerscape has PME interrupt, which can be used as linkup notifier. Set CFG_READY bit of PEX_PF0_CONFIG to enable accesses from root complex when linkup detected. Link: https://lore.kernel.org/r/20230515151049.2797105-1-Frank.Li@nxp.com Signed-off-by: Xiaowei Bao <xiaowei.bao@nxp.com> Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Stable-dep-of:17cf8661ee
("PCI: layerscape: Add workaround for lost link capabilities during reset") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Родитель
69c712389e
Коммит
4be323c737
|
@ -18,6 +18,20 @@
|
||||||
|
|
||||||
#include "pcie-designware.h"
|
#include "pcie-designware.h"
|
||||||
|
|
||||||
|
#define PEX_PF0_CONFIG 0xC0014
|
||||||
|
#define PEX_PF0_CFG_READY BIT(0)
|
||||||
|
|
||||||
|
/* PEX PFa PCIE PME and message interrupt registers*/
|
||||||
|
#define PEX_PF0_PME_MES_DR 0xC0020
|
||||||
|
#define PEX_PF0_PME_MES_DR_LUD BIT(7)
|
||||||
|
#define PEX_PF0_PME_MES_DR_LDD BIT(9)
|
||||||
|
#define PEX_PF0_PME_MES_DR_HRD BIT(10)
|
||||||
|
|
||||||
|
#define PEX_PF0_PME_MES_IER 0xC0028
|
||||||
|
#define PEX_PF0_PME_MES_IER_LUDIE BIT(7)
|
||||||
|
#define PEX_PF0_PME_MES_IER_LDDIE BIT(9)
|
||||||
|
#define PEX_PF0_PME_MES_IER_HRDIE BIT(10)
|
||||||
|
|
||||||
#define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev)
|
#define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev)
|
||||||
|
|
||||||
struct ls_pcie_ep_drvdata {
|
struct ls_pcie_ep_drvdata {
|
||||||
|
@ -30,8 +44,84 @@ struct ls_pcie_ep {
|
||||||
struct dw_pcie *pci;
|
struct dw_pcie *pci;
|
||||||
struct pci_epc_features *ls_epc;
|
struct pci_epc_features *ls_epc;
|
||||||
const struct ls_pcie_ep_drvdata *drvdata;
|
const struct ls_pcie_ep_drvdata *drvdata;
|
||||||
|
int irq;
|
||||||
|
bool big_endian;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
|
||||||
|
{
|
||||||
|
struct dw_pcie *pci = pcie->pci;
|
||||||
|
|
||||||
|
if (pcie->big_endian)
|
||||||
|
return ioread32be(pci->dbi_base + offset);
|
||||||
|
else
|
||||||
|
return ioread32(pci->dbi_base + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ls_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value)
|
||||||
|
{
|
||||||
|
struct dw_pcie *pci = pcie->pci;
|
||||||
|
|
||||||
|
if (pcie->big_endian)
|
||||||
|
iowrite32be(value, pci->dbi_base + offset);
|
||||||
|
else
|
||||||
|
iowrite32(value, pci->dbi_base + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct ls_pcie_ep *pcie = dev_id;
|
||||||
|
struct dw_pcie *pci = pcie->pci;
|
||||||
|
u32 val, cfg;
|
||||||
|
|
||||||
|
val = ls_lut_readl(pcie, PEX_PF0_PME_MES_DR);
|
||||||
|
ls_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);
|
||||||
|
|
||||||
|
if (!val)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
if (val & PEX_PF0_PME_MES_DR_LUD) {
|
||||||
|
cfg = ls_lut_readl(pcie, PEX_PF0_CONFIG);
|
||||||
|
cfg |= PEX_PF0_CFG_READY;
|
||||||
|
ls_lut_writel(pcie, PEX_PF0_CONFIG, cfg);
|
||||||
|
dw_pcie_ep_linkup(&pci->ep);
|
||||||
|
|
||||||
|
dev_dbg(pci->dev, "Link up\n");
|
||||||
|
} else if (val & PEX_PF0_PME_MES_DR_LDD) {
|
||||||
|
dev_dbg(pci->dev, "Link down\n");
|
||||||
|
} else if (val & PEX_PF0_PME_MES_DR_HRD) {
|
||||||
|
dev_dbg(pci->dev, "Hot reset\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
|
||||||
|
struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pcie->irq = platform_get_irq_byname(pdev, "pme");
|
||||||
|
if (pcie->irq < 0)
|
||||||
|
return pcie->irq;
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, pcie->irq, ls_pcie_ep_event_handler,
|
||||||
|
IRQF_SHARED, pdev->name, pcie);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Can't register PCIe IRQ\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
val = ls_lut_readl(pcie, PEX_PF0_PME_MES_IER);
|
||||||
|
val |= PEX_PF0_PME_MES_IER_LDDIE | PEX_PF0_PME_MES_IER_HRDIE |
|
||||||
|
PEX_PF0_PME_MES_IER_LUDIE;
|
||||||
|
ls_lut_writel(pcie, PEX_PF0_PME_MES_IER, val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct pci_epc_features*
|
static const struct pci_epc_features*
|
||||||
ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
|
ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
|
||||||
{
|
{
|
||||||
|
@ -124,6 +214,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)
|
||||||
struct ls_pcie_ep *pcie;
|
struct ls_pcie_ep *pcie;
|
||||||
struct pci_epc_features *ls_epc;
|
struct pci_epc_features *ls_epc;
|
||||||
struct resource *dbi_base;
|
struct resource *dbi_base;
|
||||||
|
int ret;
|
||||||
|
|
||||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||||
if (!pcie)
|
if (!pcie)
|
||||||
|
@ -143,6 +234,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)
|
||||||
pci->ops = pcie->drvdata->dw_pcie_ops;
|
pci->ops = pcie->drvdata->dw_pcie_ops;
|
||||||
|
|
||||||
ls_epc->bar_fixed_64bit = (1 << BAR_2) | (1 << BAR_4);
|
ls_epc->bar_fixed_64bit = (1 << BAR_2) | (1 << BAR_4);
|
||||||
|
ls_epc->linkup_notifier = true;
|
||||||
|
|
||||||
pcie->pci = pci;
|
pcie->pci = pci;
|
||||||
pcie->ls_epc = ls_epc;
|
pcie->ls_epc = ls_epc;
|
||||||
|
@ -154,9 +246,15 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
pci->ep.ops = &ls_pcie_ep_ops;
|
pci->ep.ops = &ls_pcie_ep_ops;
|
||||||
|
|
||||||
|
pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian");
|
||||||
|
|
||||||
platform_set_drvdata(pdev, pcie);
|
platform_set_drvdata(pdev, pcie);
|
||||||
|
|
||||||
return dw_pcie_ep_init(&pci->ep);
|
ret = dw_pcie_ep_init(&pci->ep);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return ls_pcie_ep_interrupt_init(pcie, pdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver ls_pcie_ep_driver = {
|
static struct platform_driver ls_pcie_ep_driver = {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче