mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 07:39:12 -04:00
Merge branch 'pci/controller/root-port-reset'
* pci/controller/root-port-reset: misc: pci_endpoint_test: Add AER error handlers PCI: dw-rockchip: Implement .reset_root_port() and use for link down PCI: qcom: Implement .reset_root_port() and use for link down PCI: host-common: Add link down handling for Root Ports PCI/ERR: Add support for resetting the Root Ports in a platform-specific way PCI: dwc: ep: Clear MSI iATU mapping in dw_pcie_ep_cleanup()
This commit is contained in:
@@ -1326,6 +1326,8 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
|
||||
misc_device->parent = &pdev->dev;
|
||||
misc_device->fops = &pci_endpoint_test_fops;
|
||||
|
||||
pci_save_state(pdev);
|
||||
|
||||
ret = misc_register(misc_device);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register device\n");
|
||||
@@ -1453,12 +1455,33 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
|
||||
|
||||
static pci_ers_result_t pci_endpoint_test_error_detected(struct pci_dev *pdev,
|
||||
pci_channel_state_t state)
|
||||
{
|
||||
if (state == pci_channel_io_perm_failure)
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
|
||||
return PCI_ERS_RESULT_NEED_RESET;
|
||||
}
|
||||
|
||||
static pci_ers_result_t pci_endpoint_test_slot_reset(struct pci_dev *pdev)
|
||||
{
|
||||
pci_restore_state(pdev);
|
||||
return PCI_ERS_RESULT_RECOVERED;
|
||||
}
|
||||
|
||||
static const struct pci_error_handlers pci_endpoint_test_err_handler = {
|
||||
.error_detected = pci_endpoint_test_error_detected,
|
||||
.slot_reset = pci_endpoint_test_slot_reset,
|
||||
};
|
||||
|
||||
static struct pci_driver pci_endpoint_test_driver = {
|
||||
.name = DRV_MODULE_NAME,
|
||||
.id_table = pci_endpoint_test_tbl,
|
||||
.probe = pci_endpoint_test_probe,
|
||||
.remove = pci_endpoint_test_remove,
|
||||
.sriov_configure = pci_sriov_configure_simple,
|
||||
.err_handler = &pci_endpoint_test_err_handler,
|
||||
};
|
||||
module_pci_driver(pci_endpoint_test_driver);
|
||||
|
||||
|
||||
@@ -374,6 +374,7 @@ config PCIE_ROCKCHIP_DW_HOST
|
||||
depends on OF
|
||||
select PCIE_DW_HOST
|
||||
select PCIE_ROCKCHIP_DW
|
||||
select PCI_HOST_COMMON
|
||||
help
|
||||
Enables support for the DesignWare PCIe controller in the
|
||||
Rockchip SoC (except RK3399) to work in host mode.
|
||||
|
||||
@@ -1153,6 +1153,11 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
|
||||
if (ep->msi_iatu_mapped) {
|
||||
dw_pcie_ep_unmap_addr(ep->epc, 0, 0, ep->msi_mem_phys);
|
||||
ep->msi_iatu_mapped = false;
|
||||
}
|
||||
|
||||
dwc_pcie_debugfs_deinit(pci);
|
||||
dw_pcie_edma_remove(pci);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <trace/events/pci_controller.h>
|
||||
|
||||
#include "../../pci.h"
|
||||
#include "../pci-host-common.h"
|
||||
#include "pcie-designware.h"
|
||||
|
||||
/*
|
||||
@@ -122,6 +123,9 @@ struct rockchip_pcie_of_data {
|
||||
const struct pci_epc_features *epc_features;
|
||||
};
|
||||
|
||||
static int rockchip_pcie_rc_reset_root_port(struct pci_host_bridge *bridge,
|
||||
struct pci_dev *pdev);
|
||||
|
||||
static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg)
|
||||
{
|
||||
return readl_relaxed(rockchip->apb_base + reg);
|
||||
@@ -436,6 +440,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
|
||||
|
||||
rockchip_pcie_configure_l1ss(pci);
|
||||
rockchip_pcie_enable_l0s(pci);
|
||||
pp->bridge->reset_root_port = rockchip_pcie_rc_reset_root_port;
|
||||
|
||||
/* Disable Root Ports BAR0 and BAR1 as they report bogus size */
|
||||
dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0);
|
||||
@@ -634,6 +639,32 @@ static const struct dw_pcie_ops dw_pcie_ops = {
|
||||
.get_ltssm = rockchip_pcie_get_ltssm,
|
||||
};
|
||||
|
||||
static irqreturn_t rockchip_pcie_rc_sys_irq_thread(int irq, void *arg)
|
||||
{
|
||||
struct rockchip_pcie *rockchip = arg;
|
||||
struct dw_pcie *pci = &rockchip->pci;
|
||||
struct dw_pcie_rp *pp = &pci->pp;
|
||||
struct device *dev = pci->dev;
|
||||
struct pci_dev *port;
|
||||
u32 reg;
|
||||
|
||||
reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);
|
||||
rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
|
||||
|
||||
dev_dbg(dev, "PCIE_CLIENT_INTR_STATUS_MISC: %#x\n", reg);
|
||||
dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm_reg(rockchip));
|
||||
|
||||
if (reg & PCIE_LINK_REQ_RST_NOT_INT) {
|
||||
dev_dbg(dev, "hot reset or link-down reset\n");
|
||||
for_each_pci_bridge(port, pp->bridge->bus) {
|
||||
if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT)
|
||||
pci_host_handle_link_down(port);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)
|
||||
{
|
||||
struct rockchip_pcie *rockchip = arg;
|
||||
@@ -666,14 +697,29 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip)
|
||||
static int rockchip_pcie_configure_rc(struct platform_device *pdev,
|
||||
struct rockchip_pcie *rockchip)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dw_pcie_rp *pp;
|
||||
int irq, ret;
|
||||
u32 val;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PCIE_ROCKCHIP_DW_HOST))
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "sys");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
rockchip_pcie_rc_sys_irq_thread,
|
||||
IRQF_ONESHOT, "pcie-sys-rc", rockchip);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request PCIe sys IRQ\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* LTSSM enable control mode */
|
||||
val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1);
|
||||
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
|
||||
@@ -685,7 +731,17 @@ static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip)
|
||||
pp = &rockchip->pci.pp;
|
||||
pp->ops = &rockchip_pcie_host_ops;
|
||||
|
||||
return dw_pcie_host_init(pp);
|
||||
ret = dw_pcie_host_init(pp);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* unmask hot reset/link-down reset */
|
||||
val = FIELD_PREP_WM16(PCIE_LINK_REQ_RST_NOT_INT, 0);
|
||||
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_pcie_configure_ep(struct platform_device *pdev,
|
||||
@@ -804,7 +860,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
||||
|
||||
switch (data->mode) {
|
||||
case DW_PCIE_RC_TYPE:
|
||||
ret = rockchip_pcie_configure_rc(rockchip);
|
||||
ret = rockchip_pcie_configure_rc(pdev, rockchip);
|
||||
if (ret)
|
||||
goto deinit_clk;
|
||||
break;
|
||||
@@ -829,6 +885,78 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_pcie_rc_reset_root_port(struct pci_host_bridge *bridge,
|
||||
struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_bus *bus = bridge->bus;
|
||||
struct dw_pcie_rp *pp = bus->sysdata;
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
|
||||
struct device *dev = rockchip->pci.dev;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
dw_pcie_stop_link(pci);
|
||||
clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks);
|
||||
rockchip_pcie_phy_deinit(rockchip);
|
||||
|
||||
ret = reset_control_assert(rockchip->rst);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rockchip_pcie_phy_init(rockchip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_deassert(rockchip->rst);
|
||||
if (ret)
|
||||
goto deinit_phy;
|
||||
|
||||
ret = rockchip_pcie_clk_init(rockchip);
|
||||
if (ret)
|
||||
goto deinit_phy;
|
||||
|
||||
ret = pp->ops->init(pp);
|
||||
if (ret) {
|
||||
dev_err(dev, "Host init failed: %d\n", ret);
|
||||
goto deinit_clk;
|
||||
}
|
||||
|
||||
/* LTSSM enable control mode */
|
||||
val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1);
|
||||
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
|
||||
|
||||
rockchip_pcie_writel_apb(rockchip,
|
||||
PCIE_CLIENT_SET_MODE(PCIE_CLIENT_MODE_RC),
|
||||
PCIE_CLIENT_GENERAL_CON);
|
||||
|
||||
ret = dw_pcie_setup_rc(pp);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to setup RC: %d\n", ret);
|
||||
goto deinit_clk;
|
||||
}
|
||||
|
||||
/* unmask hot reset/link-down reset */
|
||||
val = FIELD_PREP_WM16(PCIE_LINK_REQ_RST_NOT_INT, 0);
|
||||
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC);
|
||||
|
||||
ret = dw_pcie_start_link(pci);
|
||||
if (ret)
|
||||
goto deinit_clk;
|
||||
|
||||
/* Ignore errors, the link may come up later */
|
||||
dw_pcie_wait_for_link(pci);
|
||||
dev_dbg(dev, "Root Port reset completed\n");
|
||||
return ret;
|
||||
|
||||
deinit_clk:
|
||||
clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks);
|
||||
deinit_phy:
|
||||
rockchip_pcie_phy_deinit(rockchip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rockchip_pcie_of_data rockchip_pcie_rc_of_data_rk3568 = {
|
||||
.mode = DW_PCIE_RC_TYPE,
|
||||
};
|
||||
|
||||
@@ -56,6 +56,10 @@
|
||||
#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8
|
||||
#define PARF_Q2A_FLUSH 0x1ac
|
||||
#define PARF_LTSSM 0x1b0
|
||||
#define PARF_INT_ALL_STATUS 0x224
|
||||
#define PARF_INT_ALL_CLEAR 0x228
|
||||
#define PARF_INT_ALL_MASK 0x22c
|
||||
#define PARF_STATUS 0x230
|
||||
#define PARF_SID_OFFSET 0x234
|
||||
#define PARF_BDF_TRANSLATE_CFG 0x24c
|
||||
#define PARF_DBI_BASE_ADDR_V2 0x350
|
||||
@@ -133,6 +137,13 @@
|
||||
/* PARF_LTSSM register fields */
|
||||
#define LTSSM_EN BIT(8)
|
||||
#define PARF_LTSSM_STATE_MASK GENMASK(5, 0)
|
||||
#define SW_CLEAR_FLUSH_MODE BIT(10)
|
||||
#define FLUSH_MODE BIT(11)
|
||||
|
||||
/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */
|
||||
#define INT_ALL_LINK_DOWN 1
|
||||
#define PARF_INT_ALL_LINK_DOWN BIT(INT_ALL_LINK_DOWN)
|
||||
#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23)
|
||||
|
||||
/* PARF_NO_SNOOP_OVERRIDE register fields */
|
||||
#define WR_NO_SNOOP_OVERRIDE_EN BIT(1)
|
||||
@@ -144,6 +155,9 @@
|
||||
/* PARF_BDF_TO_SID_CFG fields */
|
||||
#define BDF_TO_SID_BYPASS BIT(0)
|
||||
|
||||
/* PARF_STATUS fields */
|
||||
#define FLUSH_COMPLETED BIT(8)
|
||||
|
||||
/* ELBI_SYS_CTRL register fields */
|
||||
#define ELBI_SYS_CTRL_LT_ENABLE BIT(0)
|
||||
#define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4)
|
||||
@@ -172,6 +186,7 @@
|
||||
PCIE_CAP_SLOT_POWER_LIMIT_SCALE)
|
||||
|
||||
#define PERST_DELAY_US 1000
|
||||
#define FLUSH_TIMEOUT_US 100
|
||||
|
||||
#define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0))
|
||||
|
||||
@@ -291,10 +306,13 @@ struct qcom_pcie {
|
||||
struct dentry *debugfs;
|
||||
struct list_head ports;
|
||||
struct gpio_desc *reset;
|
||||
int global_irq;
|
||||
bool use_pm_opp;
|
||||
};
|
||||
|
||||
#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
|
||||
static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge,
|
||||
struct pci_dev *pdev);
|
||||
|
||||
static void __qcom_pcie_perst_assert(struct qcom_pcie *pcie, bool assert)
|
||||
{
|
||||
@@ -1406,6 +1424,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
|
||||
goto err_assert_reset;
|
||||
}
|
||||
|
||||
pp->bridge->reset_root_port = qcom_pcie_reset_root_port;
|
||||
|
||||
return 0;
|
||||
|
||||
err_assert_reset:
|
||||
@@ -1734,6 +1754,75 @@ static int qcom_pcie_set_max_opp(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Qcom PCIe controllers only support one Root Port per controller instance. So
|
||||
* this function ignores the 'pci_dev' associated with the Root Port and just
|
||||
* resets the host bridge, which in turn resets the Root Port also.
|
||||
*/
|
||||
static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge,
|
||||
struct pci_dev *pdev)
|
||||
{
|
||||
struct device *dev = bridge->dev.parent;
|
||||
struct qcom_pcie *pcie = dev_get_drvdata(dev);
|
||||
struct dw_pcie *pci = pcie->pci;
|
||||
struct dw_pcie_rp *pp = &pci->pp;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* Wait for the pending transactions to be completed */
|
||||
ret = readl_relaxed_poll_timeout(pcie->parf + PARF_STATUS, val,
|
||||
val & FLUSH_COMPLETED, 10,
|
||||
FLUSH_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(dev, "Flush completion failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clear the FLUSH_MODE to allow the core to be reset */
|
||||
val = readl(pcie->parf + PARF_LTSSM);
|
||||
val |= SW_CLEAR_FLUSH_MODE;
|
||||
writel(val, pcie->parf + PARF_LTSSM);
|
||||
|
||||
/* Wait for the FLUSH_MODE to clear */
|
||||
ret = readl_relaxed_poll_timeout(pcie->parf + PARF_LTSSM, val,
|
||||
!(val & FLUSH_MODE), 10,
|
||||
FLUSH_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(dev, "Flush mode clear failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qcom_pcie_host_deinit(pp);
|
||||
|
||||
ret = qcom_pcie_host_init(pp);
|
||||
if (ret) {
|
||||
dev_err(dev, "Host init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dw_pcie_setup_rc(pp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Re-enable global IRQ events as the PARF_INT_ALL_MASK register is
|
||||
* non-sticky.
|
||||
*/
|
||||
if (pcie->global_irq)
|
||||
writel_relaxed(PARF_INT_ALL_LINK_DOWN | PARF_INT_MSI_DEV_0_7,
|
||||
pcie->parf + PARF_INT_ALL_MASK);
|
||||
|
||||
qcom_pcie_start_link(pci);
|
||||
|
||||
ret = dw_pcie_wait_for_link(pci);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "Root Port reset completed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_pcie_link_transition_count(struct seq_file *s, void *data)
|
||||
{
|
||||
struct qcom_pcie *pcie = (struct qcom_pcie *)dev_get_drvdata(s->private);
|
||||
@@ -1771,6 +1860,27 @@ static void qcom_pcie_init_debugfs(struct qcom_pcie *pcie)
|
||||
qcom_pcie_link_transition_count);
|
||||
}
|
||||
|
||||
static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct qcom_pcie *pcie = data;
|
||||
struct dw_pcie_rp *pp = &pcie->pci->pp;
|
||||
struct device *dev = pcie->pci->dev;
|
||||
struct pci_dev *port;
|
||||
unsigned long status = readl_relaxed(pcie->parf + PARF_INT_ALL_STATUS);
|
||||
|
||||
writel_relaxed(status, pcie->parf + PARF_INT_ALL_CLEAR);
|
||||
|
||||
if (test_and_clear_bit(INT_ALL_LINK_DOWN, &status)) {
|
||||
dev_dbg(dev, "Received Link down event\n");
|
||||
for_each_pci_bridge(port, pp->bridge->bus) {
|
||||
if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT)
|
||||
pci_host_handle_link_down(port);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void qcom_pci_free_msi(void *ptr)
|
||||
{
|
||||
struct dw_pcie_rp *pp = (struct dw_pcie_rp *)ptr;
|
||||
@@ -1990,7 +2100,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
||||
struct dw_pcie_rp *pp;
|
||||
struct resource *res;
|
||||
struct dw_pcie *pci;
|
||||
int ret;
|
||||
int ret, irq;
|
||||
|
||||
pcie_cfg = of_device_get_match_data(dev);
|
||||
if (!pcie_cfg) {
|
||||
@@ -2135,6 +2245,32 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
||||
goto err_phy_exit;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "global");
|
||||
if (irq > 0) {
|
||||
const char *name;
|
||||
|
||||
name = devm_kasprintf(dev, GFP_KERNEL, "qcom_pcie_global_irq%d",
|
||||
pci_domain_nr(pp->bridge->bus));
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
goto err_host_deinit;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
qcom_pcie_global_irq_thread,
|
||||
IRQF_ONESHOT, name, pcie);
|
||||
if (ret) {
|
||||
dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to request Global IRQ\n");
|
||||
goto err_host_deinit;
|
||||
}
|
||||
|
||||
writel_relaxed(PARF_INT_ALL_LINK_DOWN | PARF_INT_MSI_DEV_0_7,
|
||||
pcie->parf + PARF_INT_ALL_MASK);
|
||||
|
||||
pcie->global_irq = irq;
|
||||
}
|
||||
|
||||
qcom_pcie_icc_opp_update(pcie);
|
||||
|
||||
if (pcie->mhi)
|
||||
@@ -2142,6 +2278,8 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
err_host_deinit:
|
||||
dw_pcie_host_deinit(pp);
|
||||
err_phy_exit:
|
||||
list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) {
|
||||
list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "../pci.h"
|
||||
#include "pci-host-common.h"
|
||||
|
||||
/**
|
||||
@@ -342,5 +344,38 @@ bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible);
|
||||
|
||||
static pci_ers_result_t pci_host_reset_root_port(struct pci_dev *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
ret = pci_bus_error_reset(dev);
|
||||
pci_unlock_rescan_remove();
|
||||
if (ret) {
|
||||
pci_err(dev, "Failed to reset Root Port: %d\n", ret);
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
}
|
||||
|
||||
pci_info(dev, "Root Port has been reset\n");
|
||||
|
||||
return PCI_ERS_RESULT_RECOVERED;
|
||||
}
|
||||
|
||||
static void pci_host_recover_root_port(struct pci_dev *port)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_PCIEAER)
|
||||
pcie_do_recovery(port, pci_channel_io_frozen, pci_host_reset_root_port);
|
||||
#else
|
||||
pci_host_reset_root_port(port);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pci_host_handle_link_down(struct pci_dev *port)
|
||||
{
|
||||
pci_info(port, "Recovering Root Port due to Link Down\n");
|
||||
pci_host_recover_root_port(port);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_host_handle_link_down);
|
||||
|
||||
MODULE_DESCRIPTION("Common library for PCI host controller drivers");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -48,6 +48,7 @@ int pci_host_common_init(struct platform_device *pdev,
|
||||
struct pci_host_bridge *bridge,
|
||||
const struct pci_ecam_ops *ops);
|
||||
void pci_host_common_remove(struct platform_device *pdev);
|
||||
void pci_host_handle_link_down(struct pci_dev *port);
|
||||
|
||||
struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
|
||||
struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops);
|
||||
|
||||
@@ -4852,6 +4852,19 @@ void pci_reset_secondary_bus(struct pci_dev *dev)
|
||||
|
||||
void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
|
||||
int ret;
|
||||
|
||||
if (pci_is_root_bus(dev->bus) && host->reset_root_port) {
|
||||
ret = host->reset_root_port(host, dev);
|
||||
if (ret)
|
||||
pci_err(dev, "Failed to reset Root Port: %d\n", ret);
|
||||
else
|
||||
pci_restore_state(dev);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pci_reset_secondary_bus(dev);
|
||||
}
|
||||
|
||||
@@ -5698,6 +5711,7 @@ int pci_bus_error_reset(struct pci_dev *bridge)
|
||||
{
|
||||
return pci_reset_bridge(bridge, PCI_RESET_NO_RESTORE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_bus_error_reset);
|
||||
|
||||
int pci_try_reset_bridge(struct pci_dev *bridge)
|
||||
{
|
||||
|
||||
@@ -256,11 +256,6 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
|
||||
}
|
||||
|
||||
if (status == PCI_ERS_RESULT_NEED_RESET) {
|
||||
/*
|
||||
* TODO: Should call platform-specific
|
||||
* functions to reset slot before calling
|
||||
* drivers' slot_reset callbacks?
|
||||
*/
|
||||
status = PCI_ERS_RESULT_RECOVERED;
|
||||
pci_dbg(bridge, "broadcast slot_reset message\n");
|
||||
pci_walk_bridge(bridge, report_slot_reset, &status);
|
||||
@@ -297,3 +292,4 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcie_do_recovery);
|
||||
|
||||
@@ -648,6 +648,7 @@ struct pci_host_bridge {
|
||||
void (*release_fn)(struct pci_host_bridge *);
|
||||
int (*enable_device)(struct pci_host_bridge *bridge, struct pci_dev *dev);
|
||||
void (*disable_device)(struct pci_host_bridge *bridge, struct pci_dev *dev);
|
||||
int (*reset_root_port)(struct pci_host_bridge *bridge, struct pci_dev *dev);
|
||||
void *release_data;
|
||||
unsigned int ignore_reset_delay:1; /* For entire hierarchy */
|
||||
unsigned int no_ext_tags:1; /* No Extended Tags */
|
||||
|
||||
Reference in New Issue
Block a user