mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-16 12:14:06 +00:00
pci-v5.14-changes
-----BEGIN PGP SIGNATURE----- iQJIBAABCgAyFiEEgMe7l+5h9hnxdsnuWYigwDrT+vwFAmDl0F8UHGJoZWxnYWFz QGdvb2dsZS5jb20ACgkQWYigwDrT+vxkwA//dJmplIv12k94xy8rAlfMnNLg9TaO VEgKopeH1IZdFSwry7qTsTn6IJ1hZ6aSRrBJo+SYB5/V+kTSbsW9htyL2ZEjU1s0 MBb6b1T6qKwKoFqMrxSMeF7nXbLy+NE6MhvlCa1wk0rbOoL5+f/t4nHblqV/Wrwh syzf7+sSfuJmJtzytnZ88Jo2f6EIifGQyMNgaUk9MEJSrydhavOUKKQia/9gOBz8 Ogjnlh5pGdFQdSaLYhO0VXcimouJLqjVb/mvkE15hjzOTYw+NFaL9C3PDisxLdvQ sQCSFzmwJnXd2aKETPInqMamkYja+U7bCXlbt6xh2XREWioc1KLBpJpjTQeb2VxV w32otXCezfFJhmY2G+lLTRcGSTJ7OmBwqjTph9Pp2b4OYRNjXFtoMrLr6LxKMUuw dzpWUHuC/Ca+9iiMRTXJs11Bhdk1Sax3GM/Y6DzW5i/w9HWSIgvxEB7mB8jk/p6l C4IGOdTp2FtBjhnv5cgllMQqWd1a2cIGgF3gj7FmP5szVgoOJSyC4GgQXIe+91XO DM1gclWnFNyvi40bBvhsubBFeIVgzLksN7jjzsECJDpr8ql4yHiE1vcX0NqJ/qxE SpKnWv0m1aqUmYWSI6mvQrcsnxH5vs6O6cVoVxMzl4uhog2UzCLeiQyzpfDJpI7g s0FEqop9OjHkm40= =d0cQ -----END PGP SIGNATURE----- Merge tag 'pci-v5.14-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci Pull pci updates from Bjorn Helgaas: "Enumeration: - Fix dsm_label_utf16s_to_utf8s() buffer overrun (Krzysztof Wilczyński) - Rely on lengths from scnprintf(), dsm_label_utf16s_to_utf8s() (Krzysztof Wilczyński) - Use sysfs_emit() and sysfs_emit_at() in "show" functions (Krzysztof Wilczyński) - Fix 'resource_alignment' newline issues (Krzysztof Wilczyński) - Add 'devspec' newline (Krzysztof Wilczyński) - Dynamically map ECAM regions (Russell King) Resource management: - Coalesce host bridge contiguous apertures (Kai-Heng Feng) PCIe native device hotplug: - Ignore Link Down/Up caused by DPC (Lukas Wunner) Power management: - Leave Apple Thunderbolt controllers on for s2idle or standby (Konstantin Kharlamov) Virtualization: - Work around Huawei Intelligent NIC VF FLR erratum (Chiqijun) - Clarify error message for unbound IOV devices (Moritz Fischer) - Add pci_reset_bus_function() Secondary Bus Reset interface (Raphael Norwitz) Peer-to-peer DMA: - Simplify distance calculation (Christoph Hellwig) - Finish RCU conversion of pdev->p2pdma (Eric Dumazet) - Rename upstream_bridge_distance() and rework doc (Logan Gunthorpe) - Collect acs list in stack buffer to avoid sleeping (Logan Gunthorpe) - Use correct calc_map_type_and_dist() return type (Logan Gunthorpe) - Warn if host bridge not in whitelist (Logan Gunthorpe) - Refactor pci_p2pdma_map_type() (Logan Gunthorpe) - Avoid pci_get_slot(), which may sleep (Logan Gunthorpe) Altera PCIe controller driver: - Add Joyce Ooi as Altera PCIe maintainer (Joyce Ooi) Broadcom iProc PCIe controller driver: - Fix multi-MSI base vector number allocation (Sandor Bodo-Merle) - Support multi-MSI only on uniprocessor kernel (Sandor Bodo-Merle) Freescale i.MX6 PCIe controller driver: - Limit DBI register length for imx6qp PCIe (Richard Zhu) - Add "vph-supply" for PHY supply voltage (Richard Zhu) - Enable PHY internal regulator when supplied >3V (Richard Zhu) - Remove imx6_pcie_probe() redundant error message (Zhen Lei) Intel Gateway PCIe controller driver: - Fix INTx enable (Martin Blumenstingl) Marvell Aardvark PCIe controller driver: - Fix checking for PIO Non-posted Request (Pali Rohár) - Implement workaround for the readback value of VEND_ID (Pali Rohár) MediaTek PCIe controller driver: - Remove redundant error printing in mtk_pcie_subsys_powerup() (Zhen Lei) MediaTek PCIe Gen3 controller driver: - Add missing MODULE_DEVICE_TABLE (Zou Wei) Microchip PolarFlare PCIe controller driver: - Make struct event_descs static (Krzysztof Wilczyński) Microsoft Hyper-V host bridge driver: - Fix race condition when removing the device (Long Li) - Remove bus device removal unused refcount/functions (Long Li) Mobiveil PCIe controller driver: - Remove unused readl and writel functions (Krzysztof Wilczyński) NVIDIA Tegra PCIe controller driver: - Add missing MODULE_DEVICE_TABLE (Zou Wei) NVIDIA Tegra194 PCIe controller driver: - Fix tegra_pcie_ep_raise_msi_irq() ill-defined shift (Jon Hunter) - Fix host initialization during resume (Vidya Sagar) Rockchip PCIe controller driver: - Register IRQ handlers after device and data are ready (Javier Martinez Canillas)" * tag 'pci-v5.14-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (48 commits) PCI/P2PDMA: Finish RCU conversion of pdev->p2pdma PCI: xgene: Annotate __iomem pointer PCI: Fix kernel-doc formatting PCI: cpcihp: Declare cpci_debug in header file MAINTAINERS: Add Joyce Ooi as Altera PCIe maintainer PCI: rockchip: Register IRQ handlers after device and data are ready PCI: tegra194: Fix tegra_pcie_ep_raise_msi_irq() ill-defined shift PCI: aardvark: Implement workaround for the readback value of VEND_ID PCI: aardvark: Fix checking for PIO Non-posted Request PCI: tegra194: Fix host initialization during resume PCI: tegra: Add missing MODULE_DEVICE_TABLE PCI: imx6: Enable PHY internal regulator when supplied >3V dt-bindings: imx6q-pcie: Add "vph-supply" for PHY supply voltage PCI: imx6: Limit DBI register length for imx6qp PCIe PCI: imx6: Remove imx6_pcie_probe() redundant error message PCI: intel-gw: Fix INTx enable PCI: iproc: Support multi-MSI only on uniprocessor kernel PCI: iproc: Fix multi-MSI base vector number allocation PCI: mediatek-gen3: Add missing MODULE_DEVICE_TABLE PCI: Dynamically map ECAM regions ...
This commit is contained in:
commit
316a2c9b6a
52 changed files with 725 additions and 432 deletions
|
@ -295,7 +295,7 @@ and let the driver restart normal I/O processing.
|
|||
A driver can still return a critical failure for this function if
|
||||
it can't get the device operational after reset. If the platform
|
||||
previously tried a soft reset, it might now try a hard reset (power
|
||||
cycle) and then call slot_reset() again. It the device still can't
|
||||
cycle) and then call slot_reset() again. If the device still can't
|
||||
be recovered, there is nothing more that can be done; the platform
|
||||
will typically report a "permanent failure" in such a case. The
|
||||
device will be considered "dead" in this case.
|
||||
|
|
|
@ -38,6 +38,9 @@ Optional properties:
|
|||
The regulator will be enabled when initializing the PCIe host and
|
||||
disabled either as part of the init process or when shutting down the
|
||||
host.
|
||||
- vph-supply: Should specify the regulator in charge of VPH one of the three
|
||||
PCIe PHY powers. This regulator can be supplied by both 1.8v and 3.3v voltage
|
||||
supplies.
|
||||
|
||||
Additional required properties for imx6sx-pcie:
|
||||
- clock names: Must include the following additional entries:
|
||||
|
|
|
@ -14106,8 +14106,7 @@ F: Documentation/devicetree/bindings/pci/aardvark-pci.txt
|
|||
F: drivers/pci/controller/pci-aardvark.c
|
||||
|
||||
PCI DRIVER FOR ALTERA PCIE IP
|
||||
M: Ley Foon Tan <ley.foon.tan@intel.com>
|
||||
L: rfi@lists.rocketboards.org (moderated for non-subscribers)
|
||||
M: Joyce Ooi <joyce.ooi@intel.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/pci/altera-pcie.txt
|
||||
|
@ -14305,8 +14304,7 @@ S: Supported
|
|||
F: Documentation/PCI/pci-error-recovery.rst
|
||||
|
||||
PCI MSI DRIVER FOR ALTERA MSI IP
|
||||
M: Ley Foon Tan <ley.foon.tan@intel.com>
|
||||
L: rfi@lists.rocketboards.org (moderated for non-subscribers)
|
||||
M: Joyce Ooi <joyce.ooi@intel.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
|
||||
|
|
|
@ -461,7 +461,7 @@ static bool __ref is_mmconf_reserved(check_reserved_t is_reserved,
|
|||
}
|
||||
|
||||
if (size < (16UL<<20) && size != old_size)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
if (dev)
|
||||
dev_info(dev, "MMCONFIG at %pR reserved in %s\n",
|
||||
|
@ -493,7 +493,7 @@ static bool __ref is_mmconf_reserved(check_reserved_t is_reserved,
|
|||
&cfg->res, (unsigned long) cfg->address);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __ref
|
||||
|
@ -501,7 +501,7 @@ pci_mmcfg_check_reserved(struct device *dev, struct pci_mmcfg_region *cfg, int e
|
|||
{
|
||||
if (!early && !acpi_disabled) {
|
||||
if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (dev)
|
||||
dev_info(dev, FW_INFO
|
||||
|
@ -522,14 +522,14 @@ pci_mmcfg_check_reserved(struct device *dev, struct pci_mmcfg_region *cfg, int e
|
|||
* _CBA method, just assume it's reserved.
|
||||
*/
|
||||
if (pci_mmcfg_running_state)
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
/* Don't try to do this check unless configuration
|
||||
type 1 is available. how about type 2 ?*/
|
||||
if (raw_pci_ops)
|
||||
return is_mmconf_reserved(e820__mapped_all, cfg, dev, 1);
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __init pci_mmcfg_reject_broken(int early)
|
||||
|
|
|
@ -263,9 +263,12 @@ struct cdns_pcie_ops {
|
|||
* struct cdns_pcie - private data for Cadence PCIe controller drivers
|
||||
* @reg_base: IO mapped register base
|
||||
* @mem_res: start/end offsets in the physical system memory to map PCI accesses
|
||||
* @dev: PCIe controller
|
||||
* @is_rc: tell whether the PCIe controller mode is Root Complex or Endpoint.
|
||||
* @bus: In Root Complex mode, the bus number
|
||||
* @ops: Platform specific ops to control various inputs from Cadence PCIe
|
||||
* @phy_count: number of supported PHY devices
|
||||
* @phy: list of pointers to specific PHY control blocks
|
||||
* @link: list of pointers to corresponding device link representations
|
||||
* @ops: Platform-specific ops to control various inputs from Cadence PCIe
|
||||
* wrapper
|
||||
*/
|
||||
struct cdns_pcie {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9)
|
||||
#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10)
|
||||
#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11)
|
||||
#define IMX8MQ_GPR_PCIE_VREG_BYPASS BIT(12)
|
||||
#define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8)
|
||||
#define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000
|
||||
|
||||
|
@ -80,6 +81,7 @@ struct imx6_pcie {
|
|||
u32 tx_swing_full;
|
||||
u32 tx_swing_low;
|
||||
struct regulator *vpcie;
|
||||
struct regulator *vph;
|
||||
void __iomem *phy_base;
|
||||
|
||||
/* power domain for pcie */
|
||||
|
@ -621,6 +623,17 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
|
|||
imx6_pcie_grp_offset(imx6_pcie),
|
||||
IMX8MQ_GPR_PCIE_REF_USE_PAD,
|
||||
IMX8MQ_GPR_PCIE_REF_USE_PAD);
|
||||
/*
|
||||
* Regarding the datasheet, the PCIE_VPH is suggested
|
||||
* to be 1.8V. If the PCIE_VPH is supplied by 3.3V, the
|
||||
* VREG_BYPASS should be cleared to zero.
|
||||
*/
|
||||
if (imx6_pcie->vph &&
|
||||
regulator_get_voltage(imx6_pcie->vph) > 3000000)
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr,
|
||||
imx6_pcie_grp_offset(imx6_pcie),
|
||||
IMX8MQ_GPR_PCIE_VREG_BYPASS,
|
||||
0);
|
||||
break;
|
||||
case IMX7D:
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||
|
@ -1002,10 +1015,8 @@ static int imx6_pcie_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
imx6_pcie->phy_base = devm_ioremap_resource(dev, &res);
|
||||
if (IS_ERR(imx6_pcie->phy_base)) {
|
||||
dev_err(dev, "Unable to map PCIe PHY\n");
|
||||
if (IS_ERR(imx6_pcie->phy_base))
|
||||
return PTR_ERR(imx6_pcie->phy_base);
|
||||
}
|
||||
}
|
||||
|
||||
dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -1130,6 +1141,13 @@ static int imx6_pcie_probe(struct platform_device *pdev)
|
|||
imx6_pcie->vpcie = NULL;
|
||||
}
|
||||
|
||||
imx6_pcie->vph = devm_regulator_get_optional(&pdev->dev, "vph");
|
||||
if (IS_ERR(imx6_pcie->vph)) {
|
||||
if (PTR_ERR(imx6_pcie->vph) != -ENODEV)
|
||||
return PTR_ERR(imx6_pcie->vph);
|
||||
imx6_pcie->vph = NULL;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, imx6_pcie);
|
||||
|
||||
ret = imx6_pcie_attach_pd(dev);
|
||||
|
@ -1175,6 +1193,7 @@ static const struct imx6_pcie_drvdata drvdata[] = {
|
|||
.variant = IMX6QP,
|
||||
.flags = IMX6_PCIE_FLAG_IMX6_PHY |
|
||||
IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
|
||||
.dbi_length = 0x200,
|
||||
},
|
||||
[IMX7D] = {
|
||||
.variant = IMX7D,
|
||||
|
|
|
@ -39,6 +39,10 @@
|
|||
#define PCIE_APP_IRN_PM_TO_ACK BIT(9)
|
||||
#define PCIE_APP_IRN_LINK_AUTO_BW_STAT BIT(11)
|
||||
#define PCIE_APP_IRN_BW_MGT BIT(12)
|
||||
#define PCIE_APP_IRN_INTA BIT(13)
|
||||
#define PCIE_APP_IRN_INTB BIT(14)
|
||||
#define PCIE_APP_IRN_INTC BIT(15)
|
||||
#define PCIE_APP_IRN_INTD BIT(16)
|
||||
#define PCIE_APP_IRN_MSG_LTR BIT(18)
|
||||
#define PCIE_APP_IRN_SYS_ERR_RC BIT(29)
|
||||
#define PCIE_APP_INTX_OFST 12
|
||||
|
@ -48,10 +52,8 @@
|
|||
PCIE_APP_IRN_RX_VDM_MSG | PCIE_APP_IRN_SYS_ERR_RC | \
|
||||
PCIE_APP_IRN_PM_TO_ACK | PCIE_APP_IRN_MSG_LTR | \
|
||||
PCIE_APP_IRN_BW_MGT | PCIE_APP_IRN_LINK_AUTO_BW_STAT | \
|
||||
(PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTA) | \
|
||||
(PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTB) | \
|
||||
(PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTC) | \
|
||||
(PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTD))
|
||||
PCIE_APP_IRN_INTA | PCIE_APP_IRN_INTB | \
|
||||
PCIE_APP_IRN_INTC | PCIE_APP_IRN_INTD)
|
||||
|
||||
#define BUS_IATU_OFFSET SZ_256M
|
||||
#define RESET_INTERVAL_MS 100
|
||||
|
|
|
@ -1826,7 +1826,7 @@ static int tegra_pcie_ep_raise_msi_irq(struct tegra_pcie_dw *pcie, u16 irq)
|
|||
if (unlikely(irq > 31))
|
||||
return -EINVAL;
|
||||
|
||||
appl_writel(pcie, (1 << irq), APPL_MSI_CTRL_1);
|
||||
appl_writel(pcie, BIT(irq), APPL_MSI_CTRL_1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2214,6 +2214,8 @@ static int tegra_pcie_dw_resume_noirq(struct device *dev)
|
|||
goto fail_host_init;
|
||||
}
|
||||
|
||||
dw_pcie_setup_rc(&pcie->pci.pp);
|
||||
|
||||
ret = tegra_pcie_dw_start_link(&pcie->pci);
|
||||
if (ret < 0)
|
||||
goto fail_host_init;
|
||||
|
|
|
@ -42,17 +42,6 @@ struct ls_pcie_g4 {
|
|||
int irq;
|
||||
};
|
||||
|
||||
static inline u32 ls_pcie_g4_lut_readl(struct ls_pcie_g4 *pcie, u32 off)
|
||||
{
|
||||
return ioread32(pcie->pci.csr_axi_slave_base + PCIE_LUT_OFF + off);
|
||||
}
|
||||
|
||||
static inline void ls_pcie_g4_lut_writel(struct ls_pcie_g4 *pcie,
|
||||
u32 off, u32 val)
|
||||
{
|
||||
iowrite32(val, pcie->pci.csr_axi_slave_base + PCIE_LUT_OFF + off);
|
||||
}
|
||||
|
||||
static inline u32 ls_pcie_g4_pf_readl(struct ls_pcie_g4 *pcie, u32 off)
|
||||
{
|
||||
return ioread32(pcie->pci.csr_axi_slave_base + PCIE_PF_OFF + off);
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
#define PIO_COMPLETION_STATUS_UR 1
|
||||
#define PIO_COMPLETION_STATUS_CRS 2
|
||||
#define PIO_COMPLETION_STATUS_CA 4
|
||||
#define PIO_NON_POSTED_REQ BIT(0)
|
||||
#define PIO_NON_POSTED_REQ BIT(10)
|
||||
#define PIO_ADDR_LS (PIO_BASE_ADDR + 0x8)
|
||||
#define PIO_ADDR_MS (PIO_BASE_ADDR + 0xc)
|
||||
#define PIO_WR_DATA (PIO_BASE_ADDR + 0x10)
|
||||
|
@ -125,6 +125,7 @@
|
|||
#define LTSSM_MASK 0x3f
|
||||
#define LTSSM_L0 0x10
|
||||
#define RC_BAR_CONFIG 0x300
|
||||
#define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44)
|
||||
|
||||
/* PCIe core controller registers */
|
||||
#define CTRL_CORE_BASE_ADDR 0x18000
|
||||
|
@ -385,6 +386,16 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
|
|||
reg |= (IS_RC_MSK << IS_RC_SHIFT);
|
||||
advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
|
||||
|
||||
/*
|
||||
* Replace incorrect PCI vendor id value 0x1b4b by correct value 0x11ab.
|
||||
* VENDOR_ID_REG contains vendor id in low 16 bits and subsystem vendor
|
||||
* id in high 16 bits. Updating this register changes readback value of
|
||||
* read-only vendor id bits in PCIE_CORE_DEV_ID_REG register. Workaround
|
||||
* for erratum 4.1: "The value of device and vendor ID is incorrect".
|
||||
*/
|
||||
reg = (PCI_VENDOR_ID_MARVELL << 16) | PCI_VENDOR_ID_MARVELL;
|
||||
advk_writel(pcie, reg, VENDOR_ID_REG);
|
||||
|
||||
/* Set Advanced Error Capabilities and Control PF0 register */
|
||||
reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX |
|
||||
PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN |
|
||||
|
|
|
@ -34,12 +34,12 @@
|
|||
* Special configuration registers directly in the first few words
|
||||
* in I/O space.
|
||||
*/
|
||||
#define PCI_IOSIZE 0x00
|
||||
#define PCI_PROT 0x04 /* AHB protection */
|
||||
#define PCI_CTRL 0x08 /* PCI control signal */
|
||||
#define PCI_SOFTRST 0x10 /* Soft reset counter and response error enable */
|
||||
#define PCI_CONFIG 0x28 /* PCI configuration command register */
|
||||
#define PCI_DATA 0x2C
|
||||
#define FTPCI_IOSIZE 0x00
|
||||
#define FTPCI_PROT 0x04 /* AHB protection */
|
||||
#define FTPCI_CTRL 0x08 /* PCI control signal */
|
||||
#define FTPCI_SOFTRST 0x10 /* Soft reset counter and response error enable */
|
||||
#define FTPCI_CONFIG 0x28 /* PCI configuration command register */
|
||||
#define FTPCI_DATA 0x2C
|
||||
|
||||
#define FARADAY_PCI_STATUS_CMD 0x04 /* Status and command */
|
||||
#define FARADAY_PCI_PMC 0x40 /* Power management control */
|
||||
|
@ -195,9 +195,9 @@ static int faraday_raw_pci_read_config(struct faraday_pci *p, int bus_number,
|
|||
PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
|
||||
PCI_CONF_WHERE(config) |
|
||||
PCI_CONF_ENABLE,
|
||||
p->base + PCI_CONFIG);
|
||||
p->base + FTPCI_CONFIG);
|
||||
|
||||
*value = readl(p->base + PCI_DATA);
|
||||
*value = readl(p->base + FTPCI_DATA);
|
||||
|
||||
if (size == 1)
|
||||
*value = (*value >> (8 * (config & 3))) & 0xFF;
|
||||
|
@ -230,17 +230,17 @@ static int faraday_raw_pci_write_config(struct faraday_pci *p, int bus_number,
|
|||
PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
|
||||
PCI_CONF_WHERE(config) |
|
||||
PCI_CONF_ENABLE,
|
||||
p->base + PCI_CONFIG);
|
||||
p->base + FTPCI_CONFIG);
|
||||
|
||||
switch (size) {
|
||||
case 4:
|
||||
writel(value, p->base + PCI_DATA);
|
||||
writel(value, p->base + FTPCI_DATA);
|
||||
break;
|
||||
case 2:
|
||||
writew(value, p->base + PCI_DATA + (config & 3));
|
||||
writew(value, p->base + FTPCI_DATA + (config & 3));
|
||||
break;
|
||||
case 1:
|
||||
writeb(value, p->base + PCI_DATA + (config & 3));
|
||||
writeb(value, p->base + FTPCI_DATA + (config & 3));
|
||||
break;
|
||||
default:
|
||||
ret = PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
@ -469,7 +469,7 @@ static int faraday_pci_probe(struct platform_device *pdev)
|
|||
if (!faraday_res_to_memcfg(io->start - win->offset,
|
||||
resource_size(io), &val)) {
|
||||
/* setup I/O space size */
|
||||
writel(val, p->base + PCI_IOSIZE);
|
||||
writel(val, p->base + FTPCI_IOSIZE);
|
||||
} else {
|
||||
dev_err(dev, "illegal IO mem size\n");
|
||||
return -EINVAL;
|
||||
|
@ -477,11 +477,11 @@ static int faraday_pci_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Setup hostbridge */
|
||||
val = readl(p->base + PCI_CTRL);
|
||||
val = readl(p->base + FTPCI_CTRL);
|
||||
val |= PCI_COMMAND_IO;
|
||||
val |= PCI_COMMAND_MEMORY;
|
||||
val |= PCI_COMMAND_MASTER;
|
||||
writel(val, p->base + PCI_CTRL);
|
||||
writel(val, p->base + FTPCI_CTRL);
|
||||
/* Mask and clear all interrupts */
|
||||
faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2 + 2, 2, 0xF000);
|
||||
if (variant->cascaded_irq) {
|
||||
|
|
|
@ -444,7 +444,6 @@ enum hv_pcibus_state {
|
|||
hv_pcibus_probed,
|
||||
hv_pcibus_installed,
|
||||
hv_pcibus_removing,
|
||||
hv_pcibus_removed,
|
||||
hv_pcibus_maximum
|
||||
};
|
||||
|
||||
|
@ -453,7 +452,6 @@ struct hv_pcibus_device {
|
|||
/* Protocol version negotiated with the host */
|
||||
enum pci_protocol_version_t protocol_version;
|
||||
enum hv_pcibus_state state;
|
||||
refcount_t remove_lock;
|
||||
struct hv_device *hdev;
|
||||
resource_size_t low_mmio_space;
|
||||
resource_size_t high_mmio_space;
|
||||
|
@ -461,7 +459,6 @@ struct hv_pcibus_device {
|
|||
struct resource *low_mmio_res;
|
||||
struct resource *high_mmio_res;
|
||||
struct completion *survey_event;
|
||||
struct completion remove_event;
|
||||
struct pci_bus *pci_bus;
|
||||
spinlock_t config_lock; /* Avoid two threads writing index page */
|
||||
spinlock_t device_list_lock; /* Protect lists below */
|
||||
|
@ -593,9 +590,6 @@ static void put_pcichild(struct hv_pci_dev *hpdev)
|
|||
kfree(hpdev);
|
||||
}
|
||||
|
||||
static void get_hvpcibus(struct hv_pcibus_device *hv_pcibus);
|
||||
static void put_hvpcibus(struct hv_pcibus_device *hv_pcibus);
|
||||
|
||||
/*
|
||||
* There is no good way to get notified from vmbus_onoffer_rescind(),
|
||||
* so let's use polling here, since this is not a hot path.
|
||||
|
@ -2064,10 +2058,8 @@ static void pci_devices_present_work(struct work_struct *work)
|
|||
}
|
||||
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
|
||||
|
||||
if (!dr) {
|
||||
put_hvpcibus(hbus);
|
||||
if (!dr)
|
||||
return;
|
||||
}
|
||||
|
||||
/* First, mark all existing children as reported missing. */
|
||||
spin_lock_irqsave(&hbus->device_list_lock, flags);
|
||||
|
@ -2150,7 +2142,6 @@ static void pci_devices_present_work(struct work_struct *work)
|
|||
break;
|
||||
}
|
||||
|
||||
put_hvpcibus(hbus);
|
||||
kfree(dr);
|
||||
}
|
||||
|
||||
|
@ -2191,12 +2182,10 @@ static int hv_pci_start_relations_work(struct hv_pcibus_device *hbus,
|
|||
list_add_tail(&dr->list_entry, &hbus->dr_list);
|
||||
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
|
||||
|
||||
if (pending_dr) {
|
||||
if (pending_dr)
|
||||
kfree(dr_wrk);
|
||||
} else {
|
||||
get_hvpcibus(hbus);
|
||||
else
|
||||
queue_work(hbus->wq, &dr_wrk->wrk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2339,8 +2328,6 @@ static void hv_eject_device_work(struct work_struct *work)
|
|||
put_pcichild(hpdev);
|
||||
put_pcichild(hpdev);
|
||||
/* hpdev has been freed. Do not use it any more. */
|
||||
|
||||
put_hvpcibus(hbus);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2364,7 +2351,6 @@ static void hv_pci_eject_device(struct hv_pci_dev *hpdev)
|
|||
hpdev->state = hv_pcichild_ejecting;
|
||||
get_pcichild(hpdev);
|
||||
INIT_WORK(&hpdev->wrk, hv_eject_device_work);
|
||||
get_hvpcibus(hbus);
|
||||
queue_work(hbus->wq, &hpdev->wrk);
|
||||
}
|
||||
|
||||
|
@ -2964,17 +2950,6 @@ static int hv_send_resources_released(struct hv_device *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void get_hvpcibus(struct hv_pcibus_device *hbus)
|
||||
{
|
||||
refcount_inc(&hbus->remove_lock);
|
||||
}
|
||||
|
||||
static void put_hvpcibus(struct hv_pcibus_device *hbus)
|
||||
{
|
||||
if (refcount_dec_and_test(&hbus->remove_lock))
|
||||
complete(&hbus->remove_event);
|
||||
}
|
||||
|
||||
#define HVPCI_DOM_MAP_SIZE (64 * 1024)
|
||||
static DECLARE_BITMAP(hvpci_dom_map, HVPCI_DOM_MAP_SIZE);
|
||||
|
||||
|
@ -3094,14 +3069,12 @@ static int hv_pci_probe(struct hv_device *hdev,
|
|||
hbus->sysdata.domain = dom;
|
||||
|
||||
hbus->hdev = hdev;
|
||||
refcount_set(&hbus->remove_lock, 1);
|
||||
INIT_LIST_HEAD(&hbus->children);
|
||||
INIT_LIST_HEAD(&hbus->dr_list);
|
||||
INIT_LIST_HEAD(&hbus->resources_for_children);
|
||||
spin_lock_init(&hbus->config_lock);
|
||||
spin_lock_init(&hbus->device_list_lock);
|
||||
spin_lock_init(&hbus->retarget_msi_interrupt_lock);
|
||||
init_completion(&hbus->remove_event);
|
||||
hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0,
|
||||
hbus->sysdata.domain);
|
||||
if (!hbus->wq) {
|
||||
|
@ -3243,8 +3216,9 @@ static int hv_pci_bus_exit(struct hv_device *hdev, bool keep_devs)
|
|||
struct pci_packet teardown_packet;
|
||||
u8 buffer[sizeof(struct pci_message)];
|
||||
} pkt;
|
||||
struct hv_dr_state *dr;
|
||||
struct hv_pci_compl comp_pkt;
|
||||
struct hv_pci_dev *hpdev, *tmp;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
|
@ -3256,9 +3230,16 @@ static int hv_pci_bus_exit(struct hv_device *hdev, bool keep_devs)
|
|||
|
||||
if (!keep_devs) {
|
||||
/* Delete any children which might still exist. */
|
||||
dr = kzalloc(sizeof(*dr), GFP_KERNEL);
|
||||
if (dr && hv_pci_start_relations_work(hbus, dr))
|
||||
kfree(dr);
|
||||
spin_lock_irqsave(&hbus->device_list_lock, flags);
|
||||
list_for_each_entry_safe(hpdev, tmp, &hbus->children, list_entry) {
|
||||
list_del(&hpdev->list_entry);
|
||||
if (hpdev->pci_slot)
|
||||
pci_destroy_slot(hpdev->pci_slot);
|
||||
/* For the two refs got in new_pcichild_device() */
|
||||
put_pcichild(hpdev);
|
||||
put_pcichild(hpdev);
|
||||
}
|
||||
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
|
||||
}
|
||||
|
||||
ret = hv_send_resources_released(hdev);
|
||||
|
@ -3301,13 +3282,23 @@ static int hv_pci_remove(struct hv_device *hdev)
|
|||
|
||||
hbus = hv_get_drvdata(hdev);
|
||||
if (hbus->state == hv_pcibus_installed) {
|
||||
tasklet_disable(&hdev->channel->callback_event);
|
||||
hbus->state = hv_pcibus_removing;
|
||||
tasklet_enable(&hdev->channel->callback_event);
|
||||
destroy_workqueue(hbus->wq);
|
||||
hbus->wq = NULL;
|
||||
/*
|
||||
* At this point, no work is running or can be scheduled
|
||||
* on hbus-wq. We can't race with hv_pci_devices_present()
|
||||
* or hv_pci_eject_device(), it's safe to proceed.
|
||||
*/
|
||||
|
||||
/* Remove the bus from PCI's point of view. */
|
||||
pci_lock_rescan_remove();
|
||||
pci_stop_root_bus(hbus->pci_bus);
|
||||
hv_pci_remove_slots(hbus);
|
||||
pci_remove_root_bus(hbus->pci_bus);
|
||||
pci_unlock_rescan_remove();
|
||||
hbus->state = hv_pcibus_removed;
|
||||
}
|
||||
|
||||
ret = hv_pci_bus_exit(hdev, false);
|
||||
|
@ -3320,9 +3311,6 @@ static int hv_pci_remove(struct hv_device *hdev)
|
|||
hv_pci_free_bridge_windows(hbus);
|
||||
irq_domain_remove(hbus->irq_domain);
|
||||
irq_domain_free_fwnode(hbus->sysdata.fwnode);
|
||||
put_hvpcibus(hbus);
|
||||
wait_for_completion(&hbus->remove_event);
|
||||
destroy_workqueue(hbus->wq);
|
||||
|
||||
hv_put_dom_num(hbus->sysdata.domain);
|
||||
|
||||
|
|
|
@ -2539,6 +2539,7 @@ static const struct of_device_id tegra_pcie_of_match[] = {
|
|||
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_pcie_of_match);
|
||||
|
||||
static void *tegra_pcie_ports_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/**
|
||||
/*
|
||||
* APM X-Gene PCIe Driver
|
||||
*
|
||||
* Copyright (c) 2014 Applied Micro Circuits Corporation.
|
||||
|
@ -485,7 +485,7 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
|
|||
{
|
||||
void __iomem *cfg_base = port->cfg_base;
|
||||
struct device *dev = port->dev;
|
||||
void *bar_addr;
|
||||
void __iomem *bar_addr;
|
||||
u32 pim_reg;
|
||||
u64 cpu_addr = entry->res->start;
|
||||
u64 pci_addr = cpu_addr - entry->offset;
|
||||
|
|
|
@ -49,7 +49,7 @@ enum iproc_msi_reg {
|
|||
struct iproc_msi;
|
||||
|
||||
/**
|
||||
* iProc MSI group
|
||||
* struct iproc_msi_grp - iProc MSI group
|
||||
*
|
||||
* One MSI group is allocated per GIC interrupt, serviced by one iProc MSI
|
||||
* event queue.
|
||||
|
@ -65,7 +65,7 @@ struct iproc_msi_grp {
|
|||
};
|
||||
|
||||
/**
|
||||
* iProc event queue based MSI
|
||||
* struct iproc_msi - iProc event queue based MSI
|
||||
*
|
||||
* Only meant to be used on platforms without MSI support integrated into the
|
||||
* GIC.
|
||||
|
@ -171,7 +171,7 @@ static struct irq_chip iproc_msi_irq_chip = {
|
|||
|
||||
static struct msi_domain_info iproc_msi_domain_info = {
|
||||
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
|
||||
MSI_FLAG_PCI_MSIX,
|
||||
.chip = &iproc_msi_irq_chip,
|
||||
};
|
||||
|
||||
|
@ -250,20 +250,23 @@ static int iproc_msi_irq_domain_alloc(struct irq_domain *domain,
|
|||
struct iproc_msi *msi = domain->host_data;
|
||||
int hwirq, i;
|
||||
|
||||
if (msi->nr_cpus > 1 && nr_irqs > 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&msi->bitmap_lock);
|
||||
|
||||
/* Allocate 'nr_cpus' number of MSI vectors each time */
|
||||
hwirq = bitmap_find_next_zero_area(msi->bitmap, msi->nr_msi_vecs, 0,
|
||||
msi->nr_cpus, 0);
|
||||
if (hwirq < msi->nr_msi_vecs) {
|
||||
bitmap_set(msi->bitmap, hwirq, msi->nr_cpus);
|
||||
} else {
|
||||
mutex_unlock(&msi->bitmap_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
/*
|
||||
* Allocate 'nr_irqs' multiplied by 'nr_cpus' number of MSI vectors
|
||||
* each time
|
||||
*/
|
||||
hwirq = bitmap_find_free_region(msi->bitmap, msi->nr_msi_vecs,
|
||||
order_base_2(msi->nr_cpus * nr_irqs));
|
||||
|
||||
mutex_unlock(&msi->bitmap_lock);
|
||||
|
||||
if (hwirq < 0)
|
||||
return -ENOSPC;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++) {
|
||||
irq_domain_set_info(domain, virq + i, hwirq + i,
|
||||
&iproc_msi_bottom_irq_chip,
|
||||
|
@ -284,7 +287,8 @@ static void iproc_msi_irq_domain_free(struct irq_domain *domain,
|
|||
mutex_lock(&msi->bitmap_lock);
|
||||
|
||||
hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq);
|
||||
bitmap_clear(msi->bitmap, hwirq, msi->nr_cpus);
|
||||
bitmap_release_region(msi->bitmap, hwirq,
|
||||
order_base_2(msi->nr_cpus * nr_irqs));
|
||||
|
||||
mutex_unlock(&msi->bitmap_lock);
|
||||
|
||||
|
@ -539,6 +543,9 @@ int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node)
|
|||
mutex_init(&msi->bitmap_lock);
|
||||
msi->nr_cpus = num_possible_cpus();
|
||||
|
||||
if (msi->nr_cpus == 1)
|
||||
iproc_msi_domain_info.flags |= MSI_FLAG_MULTI_PCI_MSI;
|
||||
|
||||
msi->nr_irqs = of_irq_count(node);
|
||||
if (!msi->nr_irqs) {
|
||||
dev_err(pcie->dev, "found no MSI GIC interrupt\n");
|
||||
|
|
|
@ -89,8 +89,8 @@
|
|||
#define IPROC_PCIE_REG_INVALID 0xffff
|
||||
|
||||
/**
|
||||
* iProc PCIe outbound mapping controller specific parameters
|
||||
*
|
||||
* struct iproc_pcie_ob_map - iProc PCIe outbound mapping controller-specific
|
||||
* parameters
|
||||
* @window_sizes: list of supported outbound mapping window sizes in MB
|
||||
* @nr_sizes: number of supported outbound mapping window sizes
|
||||
*/
|
||||
|
@ -136,22 +136,20 @@ static const struct iproc_pcie_ob_map paxb_v2_ob_map[] = {
|
|||
};
|
||||
|
||||
/**
|
||||
* iProc PCIe inbound mapping type
|
||||
* enum iproc_pcie_ib_map_type - iProc PCIe inbound mapping type
|
||||
* @IPROC_PCIE_IB_MAP_MEM: DDR memory
|
||||
* @IPROC_PCIE_IB_MAP_IO: device I/O memory
|
||||
* @IPROC_PCIE_IB_MAP_INVALID: invalid or unused
|
||||
*/
|
||||
enum iproc_pcie_ib_map_type {
|
||||
/* for DDR memory */
|
||||
IPROC_PCIE_IB_MAP_MEM = 0,
|
||||
|
||||
/* for device I/O memory */
|
||||
IPROC_PCIE_IB_MAP_IO,
|
||||
|
||||
/* invalid or unused */
|
||||
IPROC_PCIE_IB_MAP_INVALID
|
||||
};
|
||||
|
||||
/**
|
||||
* iProc PCIe inbound mapping controller specific parameters
|
||||
*
|
||||
* struct iproc_pcie_ib_map - iProc PCIe inbound mapping controller-specific
|
||||
* parameters
|
||||
* @type: inbound mapping region type
|
||||
* @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or
|
||||
* SZ_1G
|
||||
|
@ -437,7 +435,7 @@ static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie,
|
|||
writel(val, pcie->base + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* APB error forwarding can be disabled during access of configuration
|
||||
* registers of the endpoint device, to prevent unsupported requests
|
||||
* (typically seen during enumeration with multi-function devices) from
|
||||
|
@ -619,7 +617,7 @@ static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
|
|||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Note access to the configuration registers are protected at the higher layer
|
||||
* by 'pci_lock' in drivers/pci/access.c
|
||||
*/
|
||||
|
@ -897,7 +895,7 @@ static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Some iProc SoCs require the SW to configure the outbound address mapping
|
||||
*
|
||||
* Outbound address translation:
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
#define _PCIE_IPROC_H
|
||||
|
||||
/**
|
||||
* iProc PCIe interface type
|
||||
* enum iproc_pcie_type - iProc PCIe interface type
|
||||
* @IPROC_PCIE_PAXB_BCMA: BCMA-based host controllers
|
||||
* @IPROC_PCIE_PAXB: PAXB-based host controllers for
|
||||
* NS, NSP, Cygnus, NS2, and Pegasus SOCs
|
||||
* @IPROC_PCIE_PAXB_V2: PAXB-based host controllers for Stingray SoCs
|
||||
* @IPROC_PCIE_PAXC: PAXC-based host controllers
|
||||
* @IPROC_PCIE_PAXC_V2: PAXC-based host controllers (second generation)
|
||||
*
|
||||
* PAXB is the wrapper used in root complex that can be connected to an
|
||||
* external endpoint device.
|
||||
|
@ -24,7 +30,7 @@ enum iproc_pcie_type {
|
|||
};
|
||||
|
||||
/**
|
||||
* iProc PCIe outbound mapping
|
||||
* struct iproc_pcie_ob - iProc PCIe outbound mapping
|
||||
* @axi_offset: offset from the AXI address to the internal address used by
|
||||
* the iProc PCIe core
|
||||
* @nr_windows: total number of supported outbound mapping windows
|
||||
|
@ -35,7 +41,7 @@ struct iproc_pcie_ob {
|
|||
};
|
||||
|
||||
/**
|
||||
* iProc PCIe inbound mapping
|
||||
* struct iproc_pcie_ib - iProc PCIe inbound mapping
|
||||
* @nr_regions: total number of supported inbound mapping regions
|
||||
*/
|
||||
struct iproc_pcie_ib {
|
||||
|
@ -47,13 +53,13 @@ struct iproc_pcie_ib_map;
|
|||
struct iproc_msi;
|
||||
|
||||
/**
|
||||
* iProc PCIe device
|
||||
*
|
||||
* struct iproc_pcie - iProc PCIe device
|
||||
* @dev: pointer to device data structure
|
||||
* @type: iProc PCIe interface type
|
||||
* @reg_offsets: register offsets
|
||||
* @base: PCIe host controller I/O register base
|
||||
* @base_addr: PCIe host controller register base physical address
|
||||
* @mem: host bridge memory window resource
|
||||
* @phy: optional PHY device that controls the Serdes
|
||||
* @map_irq: function callback to map interrupts
|
||||
* @ep_is_internal: indicates an internal emulated endpoint device is connected
|
||||
|
|
|
@ -1012,6 +1012,7 @@ static const struct of_device_id mtk_pcie_of_match[] = {
|
|||
{ .compatible = "mediatek,mt8192-pcie" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_pcie_of_match);
|
||||
|
||||
static struct platform_driver mtk_pcie_driver = {
|
||||
.probe = mtk_pcie_probe,
|
||||
|
|
|
@ -991,10 +991,8 @@ static int mtk_pcie_subsys_powerup(struct mtk_pcie *pcie)
|
|||
regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "subsys");
|
||||
if (regs) {
|
||||
pcie->base = devm_ioremap_resource(dev, regs);
|
||||
if (IS_ERR(pcie->base)) {
|
||||
dev_err(dev, "failed to map shared register\n");
|
||||
if (IS_ERR(pcie->base))
|
||||
return PTR_ERR(pcie->base);
|
||||
}
|
||||
}
|
||||
|
||||
pcie->free_ck = devm_clk_get(dev, "free_ck");
|
||||
|
|
|
@ -341,7 +341,7 @@ static struct event_map local_status_to_event[] = {
|
|||
LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR),
|
||||
};
|
||||
|
||||
struct {
|
||||
static struct {
|
||||
u32 base;
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
|
|
|
@ -592,10 +592,6 @@ static int rockchip_pcie_parse_host_dt(struct rockchip_pcie *rockchip)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = rockchip_pcie_setup_irq(rockchip);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
rockchip->vpcie12v = devm_regulator_get_optional(dev, "vpcie12v");
|
||||
if (IS_ERR(rockchip->vpcie12v)) {
|
||||
if (PTR_ERR(rockchip->vpcie12v) != -ENODEV)
|
||||
|
@ -973,8 +969,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto err_vpcie;
|
||||
|
||||
rockchip_pcie_enable_interrupts(rockchip);
|
||||
|
||||
err = rockchip_pcie_init_irq_domain(rockchip);
|
||||
if (err < 0)
|
||||
goto err_deinit_port;
|
||||
|
@ -992,6 +986,12 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
|||
bridge->sysdata = rockchip;
|
||||
bridge->ops = &rockchip_pcie_ops;
|
||||
|
||||
err = rockchip_pcie_setup_irq(rockchip);
|
||||
if (err)
|
||||
goto err_remove_irq_domain;
|
||||
|
||||
rockchip_pcie_enable_interrupts(rockchip);
|
||||
|
||||
err = pci_host_probe(bridge);
|
||||
if (err < 0)
|
||||
goto err_remove_irq_domain;
|
||||
|
|
|
@ -32,7 +32,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev,
|
|||
struct pci_config_window *cfg;
|
||||
unsigned int bus_range, bus_range_max, bsz;
|
||||
struct resource *conflict;
|
||||
int i, err;
|
||||
int err;
|
||||
|
||||
if (busr->start > busr->end)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -50,6 +50,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev,
|
|||
cfg->busr.start = busr->start;
|
||||
cfg->busr.end = busr->end;
|
||||
cfg->busr.flags = IORESOURCE_BUS;
|
||||
cfg->bus_shift = bus_shift;
|
||||
bus_range = resource_size(&cfg->busr);
|
||||
bus_range_max = resource_size(cfgres) >> bus_shift;
|
||||
if (bus_range > bus_range_max) {
|
||||
|
@ -77,13 +78,6 @@ struct pci_config_window *pci_ecam_create(struct device *dev,
|
|||
cfg->winp = kcalloc(bus_range, sizeof(*cfg->winp), GFP_KERNEL);
|
||||
if (!cfg->winp)
|
||||
goto err_exit_malloc;
|
||||
for (i = 0; i < bus_range; i++) {
|
||||
cfg->winp[i] =
|
||||
pci_remap_cfgspace(cfgres->start + i * bsz,
|
||||
bsz);
|
||||
if (!cfg->winp[i])
|
||||
goto err_exit_iomap;
|
||||
}
|
||||
} else {
|
||||
cfg->win = pci_remap_cfgspace(cfgres->start, bus_range * bsz);
|
||||
if (!cfg->win)
|
||||
|
@ -129,6 +123,44 @@ void pci_ecam_free(struct pci_config_window *cfg)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pci_ecam_free);
|
||||
|
||||
static int pci_ecam_add_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
unsigned int bsz = 1 << cfg->bus_shift;
|
||||
unsigned int busn = bus->number;
|
||||
phys_addr_t start;
|
||||
|
||||
if (!per_bus_mapping)
|
||||
return 0;
|
||||
|
||||
if (busn < cfg->busr.start || busn > cfg->busr.end)
|
||||
return -EINVAL;
|
||||
|
||||
busn -= cfg->busr.start;
|
||||
start = cfg->res.start + busn * bsz;
|
||||
|
||||
cfg->winp[busn] = pci_remap_cfgspace(start, bsz);
|
||||
if (!cfg->winp[busn])
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_ecam_remove_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
unsigned int busn = bus->number;
|
||||
|
||||
if (!per_bus_mapping || busn < cfg->busr.start || busn > cfg->busr.end)
|
||||
return;
|
||||
|
||||
busn -= cfg->busr.start;
|
||||
if (cfg->winp[busn]) {
|
||||
iounmap(cfg->winp[busn]);
|
||||
cfg->winp[busn] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to implement the pci_ops ->map_bus method
|
||||
*/
|
||||
|
@ -167,6 +199,8 @@ EXPORT_SYMBOL_GPL(pci_ecam_map_bus);
|
|||
/* ECAM ops */
|
||||
const struct pci_ecam_ops pci_generic_ecam_ops = {
|
||||
.pci_ops = {
|
||||
.add_bus = pci_ecam_add_bus,
|
||||
.remove_bus = pci_ecam_remove_bus,
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
|
@ -178,6 +212,8 @@ EXPORT_SYMBOL_GPL(pci_generic_ecam_ops);
|
|||
/* ECAM ops for 32-bit access only (non-compliant) */
|
||||
const struct pci_ecam_ops pci_32b_ops = {
|
||||
.pci_ops = {
|
||||
.add_bus = pci_ecam_add_bus,
|
||||
.remove_bus = pci_ecam_remove_bus,
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = pci_generic_config_read32,
|
||||
.write = pci_generic_config_write32,
|
||||
|
@ -187,6 +223,8 @@ const struct pci_ecam_ops pci_32b_ops = {
|
|||
/* ECAM ops for 32-bit read only (non-compliant) */
|
||||
const struct pci_ecam_ops pci_32b_read_ops = {
|
||||
.pci_ops = {
|
||||
.add_bus = pci_ecam_add_bus,
|
||||
.remove_bus = pci_ecam_remove_bus,
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = pci_generic_config_read32,
|
||||
.write = pci_generic_config_write,
|
||||
|
|
|
@ -75,6 +75,9 @@ int cpci_hp_unregister_bus(struct pci_bus *bus);
|
|||
int cpci_hp_start(void);
|
||||
int cpci_hp_stop(void);
|
||||
|
||||
/* Global variables */
|
||||
extern int cpci_debug;
|
||||
|
||||
/*
|
||||
* Internal function prototypes, these functions should not be used by
|
||||
* board/chassis drivers.
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
#define MY_NAME "cpci_hotplug"
|
||||
|
||||
extern int cpci_debug;
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (cpci_debug) \
|
||||
|
|
|
@ -296,9 +296,10 @@ static int ctrl_slot_cleanup(struct controller *ctrl)
|
|||
*
|
||||
* Won't work for more than one PCI-PCI bridge in a slot.
|
||||
*
|
||||
* @bus_num - bus number of PCI device
|
||||
* @dev_num - device number of PCI device
|
||||
* @slot - Pointer to u8 where slot number will be returned
|
||||
* @bus: pointer to the PCI bus structure
|
||||
* @bus_num: bus number of PCI device
|
||||
* @dev_num: device number of PCI device
|
||||
* @slot: Pointer to u8 where slot number will be returned
|
||||
*
|
||||
* Output: SUCCESS or FAILURE
|
||||
*/
|
||||
|
|
|
@ -1877,7 +1877,7 @@ static void interrupt_event_handler(struct controller *ctrl)
|
|||
|
||||
/**
|
||||
* cpqhp_pushbutton_thread - handle pushbutton events
|
||||
* @slot: target slot (struct)
|
||||
* @t: pointer to struct timer_list which holds all timer-related callbacks
|
||||
*
|
||||
* Scheduled procedure to handle blocking stuff for the pushbuttons.
|
||||
* Handles all pending events and exits.
|
||||
|
|
|
@ -73,7 +73,7 @@ static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
return sprintf(buf, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
|
||||
|
@ -130,7 +130,7 @@ static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
return sprintf(buf, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
|
||||
|
@ -175,7 +175,7 @@ static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
return sprintf(buf, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static struct pci_slot_attribute hotplug_slot_attr_latch = {
|
||||
|
@ -192,7 +192,7 @@ static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
return sprintf(buf, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static struct pci_slot_attribute hotplug_slot_attr_presence = {
|
||||
|
|
|
@ -47,6 +47,9 @@ extern int pciehp_poll_time;
|
|||
* struct controller - PCIe hotplug controller
|
||||
* @pcie: pointer to the controller's PCIe port service device
|
||||
* @slot_cap: cached copy of the Slot Capabilities register
|
||||
* @inband_presence_disabled: In-Band Presence Detect Disable supported by
|
||||
* controller and disabled per spec recommendation (PCIe r5.0, appendix I
|
||||
* implementation note)
|
||||
* @slot_ctrl: cached copy of the Slot Control register
|
||||
* @ctrl_lock: serializes writes to the Slot Control register
|
||||
* @cmd_started: jiffies when the Slot Control register was last written;
|
||||
|
|
|
@ -563,6 +563,32 @@ void pciehp_power_off_slot(struct controller *ctrl)
|
|||
PCI_EXP_SLTCTL_PWR_OFF);
|
||||
}
|
||||
|
||||
static void pciehp_ignore_dpc_link_change(struct controller *ctrl,
|
||||
struct pci_dev *pdev, int irq)
|
||||
{
|
||||
/*
|
||||
* Ignore link changes which occurred while waiting for DPC recovery.
|
||||
* Could be several if DPC triggered multiple times consecutively.
|
||||
*/
|
||||
synchronize_hardirq(irq);
|
||||
atomic_and(~PCI_EXP_SLTSTA_DLLSC, &ctrl->pending_events);
|
||||
if (pciehp_poll_mode)
|
||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
|
||||
PCI_EXP_SLTSTA_DLLSC);
|
||||
ctrl_info(ctrl, "Slot(%s): Link Down/Up ignored (recovered by DPC)\n",
|
||||
slot_name(ctrl));
|
||||
|
||||
/*
|
||||
* If the link is unexpectedly down after successful recovery,
|
||||
* the corresponding link change may have been ignored above.
|
||||
* Synthesize it to ensure that it is acted on.
|
||||
*/
|
||||
down_read(&ctrl->reset_lock);
|
||||
if (!pciehp_check_link_active(ctrl))
|
||||
pciehp_request(ctrl, PCI_EXP_SLTSTA_DLLSC);
|
||||
up_read(&ctrl->reset_lock);
|
||||
}
|
||||
|
||||
static irqreturn_t pciehp_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct controller *ctrl = (struct controller *)dev_id;
|
||||
|
@ -706,6 +732,16 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
|
|||
PCI_EXP_SLTCTL_ATTN_IND_ON);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore Link Down/Up events caused by Downstream Port Containment
|
||||
* if recovery from the error succeeded.
|
||||
*/
|
||||
if ((events & PCI_EXP_SLTSTA_DLLSC) && pci_dpc_recovered(pdev) &&
|
||||
ctrl->state == ON_STATE) {
|
||||
events &= ~PCI_EXP_SLTSTA_DLLSC;
|
||||
pciehp_ignore_dpc_link_change(ctrl, pdev, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable requests have higher priority than Presence Detect Changed
|
||||
* or Data Link Layer State Changed events.
|
||||
|
|
|
@ -50,7 +50,7 @@ static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
static ssize_t add_slot_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "0\n");
|
||||
return sysfs_emit(buf, "0\n");
|
||||
}
|
||||
|
||||
static ssize_t remove_slot_store(struct kobject *kobj,
|
||||
|
@ -80,7 +80,7 @@ static ssize_t remove_slot_store(struct kobject *kobj,
|
|||
static ssize_t remove_slot_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "0\n");
|
||||
return sysfs_emit(buf, "0\n");
|
||||
}
|
||||
|
||||
static struct kobj_attribute add_slot_attr =
|
||||
|
|
|
@ -24,50 +24,54 @@
|
|||
static ssize_t show_ctrl(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
char *out = buf;
|
||||
int index, busnr;
|
||||
struct resource *res;
|
||||
struct pci_bus *bus;
|
||||
size_t len = 0;
|
||||
|
||||
pdev = to_pci_dev(dev);
|
||||
bus = pdev->subordinate;
|
||||
|
||||
out += sprintf(buf, "Free resources: memory\n");
|
||||
len += sysfs_emit_at(buf, len, "Free resources: memory\n");
|
||||
pci_bus_for_each_resource(bus, res, index) {
|
||||
if (res && (res->flags & IORESOURCE_MEM) &&
|
||||
!(res->flags & IORESOURCE_PREFETCH)) {
|
||||
out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)resource_size(res));
|
||||
len += sysfs_emit_at(buf, len,
|
||||
"start = %8.8llx, length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)resource_size(res));
|
||||
}
|
||||
}
|
||||
out += sprintf(out, "Free resources: prefetchable memory\n");
|
||||
len += sysfs_emit_at(buf, len, "Free resources: prefetchable memory\n");
|
||||
pci_bus_for_each_resource(bus, res, index) {
|
||||
if (res && (res->flags & IORESOURCE_MEM) &&
|
||||
(res->flags & IORESOURCE_PREFETCH)) {
|
||||
out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)resource_size(res));
|
||||
len += sysfs_emit_at(buf, len,
|
||||
"start = %8.8llx, length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)resource_size(res));
|
||||
}
|
||||
}
|
||||
out += sprintf(out, "Free resources: IO\n");
|
||||
len += sysfs_emit_at(buf, len, "Free resources: IO\n");
|
||||
pci_bus_for_each_resource(bus, res, index) {
|
||||
if (res && (res->flags & IORESOURCE_IO)) {
|
||||
out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)resource_size(res));
|
||||
len += sysfs_emit_at(buf, len,
|
||||
"start = %8.8llx, length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)resource_size(res));
|
||||
}
|
||||
}
|
||||
out += sprintf(out, "Free resources: bus numbers\n");
|
||||
len += sysfs_emit_at(buf, len, "Free resources: bus numbers\n");
|
||||
for (busnr = bus->busn_res.start; busnr <= bus->busn_res.end; busnr++) {
|
||||
if (!pci_find_bus(pci_domain_nr(bus), busnr))
|
||||
break;
|
||||
}
|
||||
if (busnr < bus->busn_res.end)
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n",
|
||||
busnr, (int)(bus->busn_res.end - busnr));
|
||||
len += sysfs_emit_at(buf, len,
|
||||
"start = %8.8x, length = %8.8x\n",
|
||||
busnr, (int)(bus->busn_res.end - busnr));
|
||||
|
||||
return out - buf;
|
||||
return len;
|
||||
}
|
||||
static DEVICE_ATTR(ctrl, S_IRUGO, show_ctrl, NULL);
|
||||
|
||||
|
|
|
@ -346,7 +346,7 @@ static ssize_t sriov_totalvfs_show(struct device *dev,
|
|||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
|
||||
return sysfs_emit(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
|
||||
}
|
||||
|
||||
static ssize_t sriov_numvfs_show(struct device *dev,
|
||||
|
@ -361,7 +361,7 @@ static ssize_t sriov_numvfs_show(struct device *dev,
|
|||
num_vfs = pdev->sriov->num_VFs;
|
||||
device_unlock(&pdev->dev);
|
||||
|
||||
return sprintf(buf, "%u\n", num_vfs);
|
||||
return sysfs_emit(buf, "%u\n", num_vfs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -391,9 +391,16 @@ static ssize_t sriov_numvfs_store(struct device *dev,
|
|||
if (num_vfs == pdev->sriov->num_VFs)
|
||||
goto exit;
|
||||
|
||||
/* is PF driver loaded */
|
||||
if (!pdev->driver) {
|
||||
pci_info(pdev, "no driver bound to device; cannot configure SR-IOV\n");
|
||||
ret = -ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* is PF driver loaded w/callback */
|
||||
if (!pdev->driver || !pdev->driver->sriov_configure) {
|
||||
pci_info(pdev, "Driver does not support SRIOV configuration via sysfs\n");
|
||||
if (!pdev->driver->sriov_configure) {
|
||||
pci_info(pdev, "driver does not support SR-IOV configuration via sysfs\n");
|
||||
ret = -ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
@ -435,7 +442,7 @@ static ssize_t sriov_offset_show(struct device *dev,
|
|||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", pdev->sriov->offset);
|
||||
return sysfs_emit(buf, "%u\n", pdev->sriov->offset);
|
||||
}
|
||||
|
||||
static ssize_t sriov_stride_show(struct device *dev,
|
||||
|
@ -444,7 +451,7 @@ static ssize_t sriov_stride_show(struct device *dev,
|
|||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", pdev->sriov->stride);
|
||||
return sysfs_emit(buf, "%u\n", pdev->sriov->stride);
|
||||
}
|
||||
|
||||
static ssize_t sriov_vf_device_show(struct device *dev,
|
||||
|
@ -453,7 +460,7 @@ static ssize_t sriov_vf_device_show(struct device *dev,
|
|||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
return sprintf(buf, "%x\n", pdev->sriov->vf_device);
|
||||
return sysfs_emit(buf, "%x\n", pdev->sriov->vf_device);
|
||||
}
|
||||
|
||||
static ssize_t sriov_drivers_autoprobe_show(struct device *dev,
|
||||
|
@ -462,7 +469,7 @@ static ssize_t sriov_drivers_autoprobe_show(struct device *dev,
|
|||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", pdev->sriov->drivers_autoprobe);
|
||||
return sysfs_emit(buf, "%u\n", pdev->sriov->drivers_autoprobe);
|
||||
}
|
||||
|
||||
static ssize_t sriov_drivers_autoprobe_store(struct device *dev,
|
||||
|
|
|
@ -464,11 +464,11 @@ static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr,
|
|||
return retval;
|
||||
|
||||
entry = irq_get_msi_desc(irq);
|
||||
if (entry)
|
||||
return sprintf(buf, "%s\n",
|
||||
entry->msi_attrib.is_msix ? "msix" : "msi");
|
||||
if (!entry)
|
||||
return -ENODEV;
|
||||
|
||||
return -ENODEV;
|
||||
return sysfs_emit(buf, "%s\n",
|
||||
entry->msi_attrib.is_msix ? "msix" : "msi");
|
||||
}
|
||||
|
||||
static int populate_msi_sysfs(struct pci_dev *pdev)
|
||||
|
|
|
@ -48,12 +48,16 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr,
|
|||
char *buf)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pci_p2pdma *p2pdma;
|
||||
size_t size = 0;
|
||||
|
||||
if (pdev->p2pdma->pool)
|
||||
size = gen_pool_size(pdev->p2pdma->pool);
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(pdev->p2pdma);
|
||||
if (p2pdma && p2pdma->pool)
|
||||
size = gen_pool_size(p2pdma->pool);
|
||||
rcu_read_unlock();
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%zd\n", size);
|
||||
return sysfs_emit(buf, "%zd\n", size);
|
||||
}
|
||||
static DEVICE_ATTR_RO(size);
|
||||
|
||||
|
@ -61,12 +65,16 @@ static ssize_t available_show(struct device *dev, struct device_attribute *attr,
|
|||
char *buf)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pci_p2pdma *p2pdma;
|
||||
size_t avail = 0;
|
||||
|
||||
if (pdev->p2pdma->pool)
|
||||
avail = gen_pool_avail(pdev->p2pdma->pool);
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(pdev->p2pdma);
|
||||
if (p2pdma && p2pdma->pool)
|
||||
avail = gen_pool_avail(p2pdma->pool);
|
||||
rcu_read_unlock();
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%zd\n", avail);
|
||||
return sysfs_emit(buf, "%zd\n", avail);
|
||||
}
|
||||
static DEVICE_ATTR_RO(available);
|
||||
|
||||
|
@ -74,9 +82,16 @@ static ssize_t published_show(struct device *dev, struct device_attribute *attr,
|
|||
char *buf)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pci_p2pdma *p2pdma;
|
||||
bool published = false;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
pdev->p2pdma->p2pmem_published);
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(pdev->p2pdma);
|
||||
if (p2pdma)
|
||||
published = p2pdma->p2pmem_published;
|
||||
rcu_read_unlock();
|
||||
|
||||
return sysfs_emit(buf, "%d\n", published);
|
||||
}
|
||||
static DEVICE_ATTR_RO(published);
|
||||
|
||||
|
@ -95,8 +110,9 @@ static const struct attribute_group p2pmem_group = {
|
|||
static void pci_p2pdma_release(void *data)
|
||||
{
|
||||
struct pci_dev *pdev = data;
|
||||
struct pci_p2pdma *p2pdma = pdev->p2pdma;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
|
||||
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
|
||||
if (!p2pdma)
|
||||
return;
|
||||
|
||||
|
@ -128,16 +144,14 @@ static int pci_p2pdma_setup(struct pci_dev *pdev)
|
|||
if (error)
|
||||
goto out_pool_destroy;
|
||||
|
||||
pdev->p2pdma = p2p;
|
||||
|
||||
error = sysfs_create_group(&pdev->dev.kobj, &p2pmem_group);
|
||||
if (error)
|
||||
goto out_pool_destroy;
|
||||
|
||||
rcu_assign_pointer(pdev->p2pdma, p2p);
|
||||
return 0;
|
||||
|
||||
out_pool_destroy:
|
||||
pdev->p2pdma = NULL;
|
||||
gen_pool_destroy(p2p->pool);
|
||||
out:
|
||||
devm_kfree(&pdev->dev, p2p);
|
||||
|
@ -159,6 +173,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
|
|||
{
|
||||
struct pci_p2pdma_pagemap *p2p_pgmap;
|
||||
struct dev_pagemap *pgmap;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
void *addr;
|
||||
int error;
|
||||
|
||||
|
@ -200,7 +215,8 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
|
|||
goto pgmap_free;
|
||||
}
|
||||
|
||||
error = gen_pool_add_owner(pdev->p2pdma->pool, (unsigned long)addr,
|
||||
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
|
||||
error = gen_pool_add_owner(p2pdma->pool, (unsigned long)addr,
|
||||
pci_bus_address(pdev, bar) + offset,
|
||||
range_len(&pgmap->range), dev_to_node(&pdev->dev),
|
||||
pgmap->ref);
|
||||
|
@ -308,10 +324,41 @@ static const struct pci_p2pdma_whitelist_entry {
|
|||
{}
|
||||
};
|
||||
|
||||
static bool __host_bridge_whitelist(struct pci_host_bridge *host,
|
||||
bool same_host_bridge)
|
||||
/*
|
||||
* This lookup function tries to find the PCI device corresponding to a given
|
||||
* host bridge.
|
||||
*
|
||||
* It assumes the host bridge device is the first PCI device in the
|
||||
* bus->devices list and that the devfn is 00.0. These assumptions should hold
|
||||
* for all the devices in the whitelist above.
|
||||
*
|
||||
* This function is equivalent to pci_get_slot(host->bus, 0), however it does
|
||||
* not take the pci_bus_sem lock seeing __host_bridge_whitelist() must not
|
||||
* sleep.
|
||||
*
|
||||
* For this to be safe, the caller should hold a reference to a device on the
|
||||
* bridge, which should ensure the host_bridge device will not be freed
|
||||
* or removed from the head of the devices list.
|
||||
*/
|
||||
static struct pci_dev *pci_host_bridge_dev(struct pci_host_bridge *host)
|
||||
{
|
||||
struct pci_dev *root = pci_get_slot(host->bus, PCI_DEVFN(0, 0));
|
||||
struct pci_dev *root;
|
||||
|
||||
root = list_first_entry_or_null(&host->bus->devices,
|
||||
struct pci_dev, bus_list);
|
||||
|
||||
if (!root)
|
||||
return NULL;
|
||||
if (root->devfn != PCI_DEVFN(0, 0))
|
||||
return NULL;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static bool __host_bridge_whitelist(struct pci_host_bridge *host,
|
||||
bool same_host_bridge, bool warn)
|
||||
{
|
||||
struct pci_dev *root = pci_host_bridge_dev(host);
|
||||
const struct pci_p2pdma_whitelist_entry *entry;
|
||||
unsigned short vendor, device;
|
||||
|
||||
|
@ -320,7 +367,6 @@ static bool __host_bridge_whitelist(struct pci_host_bridge *host,
|
|||
|
||||
vendor = root->vendor;
|
||||
device = root->device;
|
||||
pci_dev_put(root);
|
||||
|
||||
for (entry = pci_p2pdma_whitelist; entry->vendor; entry++) {
|
||||
if (vendor != entry->vendor || device != entry->device)
|
||||
|
@ -331,6 +377,10 @@ static bool __host_bridge_whitelist(struct pci_host_bridge *host,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (warn)
|
||||
pci_warn(root, "Host bridge not in P2PDMA whitelist: %04x:%04x\n",
|
||||
vendor, device);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -338,44 +388,90 @@ static bool __host_bridge_whitelist(struct pci_host_bridge *host,
|
|||
* If we can't find a common upstream bridge take a look at the root
|
||||
* complex and compare it to a whitelist of known good hardware.
|
||||
*/
|
||||
static bool host_bridge_whitelist(struct pci_dev *a, struct pci_dev *b)
|
||||
static bool host_bridge_whitelist(struct pci_dev *a, struct pci_dev *b,
|
||||
bool warn)
|
||||
{
|
||||
struct pci_host_bridge *host_a = pci_find_host_bridge(a->bus);
|
||||
struct pci_host_bridge *host_b = pci_find_host_bridge(b->bus);
|
||||
|
||||
if (host_a == host_b)
|
||||
return __host_bridge_whitelist(host_a, true);
|
||||
return __host_bridge_whitelist(host_a, true, warn);
|
||||
|
||||
if (__host_bridge_whitelist(host_a, false) &&
|
||||
__host_bridge_whitelist(host_b, false))
|
||||
if (__host_bridge_whitelist(host_a, false, warn) &&
|
||||
__host_bridge_whitelist(host_b, false, warn))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum pci_p2pdma_map_type
|
||||
__upstream_bridge_distance(struct pci_dev *provider, struct pci_dev *client,
|
||||
int *dist, bool *acs_redirects, struct seq_buf *acs_list)
|
||||
static unsigned long map_types_idx(struct pci_dev *client)
|
||||
{
|
||||
return (pci_domain_nr(client->bus) << 16) |
|
||||
(client->bus->number << 8) | client->devfn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the P2PDMA mapping type and distance between two PCI devices.
|
||||
*
|
||||
* If the two devices are the same PCI function, return
|
||||
* PCI_P2PDMA_MAP_BUS_ADDR and a distance of 0.
|
||||
*
|
||||
* If they are two functions of the same device, return
|
||||
* PCI_P2PDMA_MAP_BUS_ADDR and a distance of 2 (one hop up to the bridge,
|
||||
* then one hop back down to another function of the same device).
|
||||
*
|
||||
* In the case where two devices are connected to the same PCIe switch,
|
||||
* return a distance of 4. This corresponds to the following PCI tree:
|
||||
*
|
||||
* -+ Root Port
|
||||
* \+ Switch Upstream Port
|
||||
* +-+ Switch Downstream Port 0
|
||||
* + \- Device A
|
||||
* \-+ Switch Downstream Port 1
|
||||
* \- Device B
|
||||
*
|
||||
* The distance is 4 because we traverse from Device A to Downstream Port 0
|
||||
* to the common Switch Upstream Port, back down to Downstream Port 1 and
|
||||
* then to Device B. The mapping type returned depends on the ACS
|
||||
* redirection setting of the ports along the path.
|
||||
*
|
||||
* If ACS redirect is set on any port in the path, traffic between the
|
||||
* devices will go through the host bridge, so return
|
||||
* PCI_P2PDMA_MAP_THRU_HOST_BRIDGE; otherwise return
|
||||
* PCI_P2PDMA_MAP_BUS_ADDR.
|
||||
*
|
||||
* Any two devices that have a data path that goes through the host bridge
|
||||
* will consult a whitelist. If the host bridge is in the whitelist, return
|
||||
* PCI_P2PDMA_MAP_THRU_HOST_BRIDGE with the distance set to the number of
|
||||
* ports per above. If the device is not in the whitelist, return
|
||||
* PCI_P2PDMA_MAP_NOT_SUPPORTED.
|
||||
*/
|
||||
static enum pci_p2pdma_map_type
|
||||
calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
|
||||
int *dist, bool verbose)
|
||||
{
|
||||
enum pci_p2pdma_map_type map_type = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
|
||||
struct pci_dev *a = provider, *b = client, *bb;
|
||||
bool acs_redirects = false;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
struct seq_buf acs_list;
|
||||
int acs_cnt = 0;
|
||||
int dist_a = 0;
|
||||
int dist_b = 0;
|
||||
int acs_cnt = 0;
|
||||
char buf[128];
|
||||
|
||||
if (acs_redirects)
|
||||
*acs_redirects = false;
|
||||
seq_buf_init(&acs_list, buf, sizeof(buf));
|
||||
|
||||
/*
|
||||
* Note, we don't need to take references to devices returned by
|
||||
* pci_upstream_bridge() seeing we hold a reference to a child
|
||||
* device which will already hold a reference to the upstream bridge.
|
||||
*/
|
||||
|
||||
while (a) {
|
||||
dist_b = 0;
|
||||
|
||||
if (pci_bridge_has_acs_redir(a)) {
|
||||
seq_buf_print_bus_devfn(acs_list, a);
|
||||
seq_buf_print_bus_devfn(&acs_list, a);
|
||||
acs_cnt++;
|
||||
}
|
||||
|
||||
|
@ -393,10 +489,8 @@ __upstream_bridge_distance(struct pci_dev *provider, struct pci_dev *client,
|
|||
dist_a++;
|
||||
}
|
||||
|
||||
if (dist)
|
||||
*dist = dist_a + dist_b;
|
||||
|
||||
return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
|
||||
*dist = dist_a + dist_b;
|
||||
goto map_through_host_bridge;
|
||||
|
||||
check_b_path_acs:
|
||||
bb = b;
|
||||
|
@ -406,124 +500,45 @@ check_b_path_acs:
|
|||
break;
|
||||
|
||||
if (pci_bridge_has_acs_redir(bb)) {
|
||||
seq_buf_print_bus_devfn(acs_list, bb);
|
||||
seq_buf_print_bus_devfn(&acs_list, bb);
|
||||
acs_cnt++;
|
||||
}
|
||||
|
||||
bb = pci_upstream_bridge(bb);
|
||||
}
|
||||
|
||||
if (dist)
|
||||
*dist = dist_a + dist_b;
|
||||
*dist = dist_a + dist_b;
|
||||
|
||||
if (acs_cnt) {
|
||||
if (acs_redirects)
|
||||
*acs_redirects = true;
|
||||
|
||||
return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
|
||||
if (!acs_cnt) {
|
||||
map_type = PCI_P2PDMA_MAP_BUS_ADDR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
return PCI_P2PDMA_MAP_BUS_ADDR;
|
||||
}
|
||||
|
||||
static unsigned long map_types_idx(struct pci_dev *client)
|
||||
{
|
||||
return (pci_domain_nr(client->bus) << 16) |
|
||||
(client->bus->number << 8) | client->devfn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the distance through the nearest common upstream bridge between
|
||||
* two PCI devices.
|
||||
*
|
||||
* If the two devices are the same device then 0 will be returned.
|
||||
*
|
||||
* If there are two virtual functions of the same device behind the same
|
||||
* bridge port then 2 will be returned (one step down to the PCIe switch,
|
||||
* then one step back to the same device).
|
||||
*
|
||||
* In the case where two devices are connected to the same PCIe switch, the
|
||||
* value 4 will be returned. This corresponds to the following PCI tree:
|
||||
*
|
||||
* -+ Root Port
|
||||
* \+ Switch Upstream Port
|
||||
* +-+ Switch Downstream Port
|
||||
* + \- Device A
|
||||
* \-+ Switch Downstream Port
|
||||
* \- Device B
|
||||
*
|
||||
* The distance is 4 because we traverse from Device A through the downstream
|
||||
* port of the switch, to the common upstream port, back up to the second
|
||||
* downstream port and then to Device B.
|
||||
*
|
||||
* Any two devices that cannot communicate using p2pdma will return
|
||||
* PCI_P2PDMA_MAP_NOT_SUPPORTED.
|
||||
*
|
||||
* Any two devices that have a data path that goes through the host bridge
|
||||
* will consult a whitelist. If the host bridges are on the whitelist,
|
||||
* this function will return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE.
|
||||
*
|
||||
* If either bridge is not on the whitelist this function returns
|
||||
* PCI_P2PDMA_MAP_NOT_SUPPORTED.
|
||||
*
|
||||
* If a bridge which has any ACS redirection bits set is in the path,
|
||||
* acs_redirects will be set to true. In this case, a list of all infringing
|
||||
* bridge addresses will be populated in acs_list (assuming it's non-null)
|
||||
* for printk purposes.
|
||||
*/
|
||||
static enum pci_p2pdma_map_type
|
||||
upstream_bridge_distance(struct pci_dev *provider, struct pci_dev *client,
|
||||
int *dist, bool *acs_redirects, struct seq_buf *acs_list)
|
||||
{
|
||||
enum pci_p2pdma_map_type map_type;
|
||||
|
||||
map_type = __upstream_bridge_distance(provider, client, dist,
|
||||
acs_redirects, acs_list);
|
||||
|
||||
if (map_type == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE) {
|
||||
if (!cpu_supports_p2pdma() &&
|
||||
!host_bridge_whitelist(provider, client))
|
||||
map_type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (provider->p2pdma)
|
||||
xa_store(&provider->p2pdma->map_types, map_types_idx(client),
|
||||
xa_mk_value(map_type), GFP_KERNEL);
|
||||
|
||||
return map_type;
|
||||
}
|
||||
|
||||
static enum pci_p2pdma_map_type
|
||||
upstream_bridge_distance_warn(struct pci_dev *provider, struct pci_dev *client,
|
||||
int *dist)
|
||||
{
|
||||
struct seq_buf acs_list;
|
||||
bool acs_redirects;
|
||||
int ret;
|
||||
|
||||
seq_buf_init(&acs_list, kmalloc(PAGE_SIZE, GFP_KERNEL), PAGE_SIZE);
|
||||
if (!acs_list.buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = upstream_bridge_distance(provider, client, dist, &acs_redirects,
|
||||
&acs_list);
|
||||
if (acs_redirects) {
|
||||
if (verbose) {
|
||||
acs_list.buffer[acs_list.len-1] = 0; /* drop final semicolon */
|
||||
pci_warn(client, "ACS redirect is set between the client and provider (%s)\n",
|
||||
pci_name(provider));
|
||||
/* Drop final semicolon */
|
||||
acs_list.buffer[acs_list.len-1] = 0;
|
||||
pci_warn(client, "to disable ACS redirect for this path, add the kernel parameter: pci=disable_acs_redir=%s\n",
|
||||
acs_list.buffer);
|
||||
}
|
||||
acs_redirects = true;
|
||||
|
||||
if (ret == PCI_P2PDMA_MAP_NOT_SUPPORTED) {
|
||||
pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge or whitelisted host bridge\n",
|
||||
pci_name(provider));
|
||||
map_through_host_bridge:
|
||||
if (!cpu_supports_p2pdma() &&
|
||||
!host_bridge_whitelist(provider, client, acs_redirects)) {
|
||||
if (verbose)
|
||||
pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge or whitelisted host bridge\n",
|
||||
pci_name(provider));
|
||||
map_type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
kfree(acs_list.buffer);
|
||||
|
||||
return ret;
|
||||
done:
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(provider->p2pdma);
|
||||
if (p2pdma)
|
||||
xa_store(&p2pdma->map_types, map_types_idx(client),
|
||||
xa_mk_value(map_type), GFP_KERNEL);
|
||||
rcu_read_unlock();
|
||||
return map_type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -546,11 +561,11 @@ upstream_bridge_distance_warn(struct pci_dev *provider, struct pci_dev *client,
|
|||
int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients,
|
||||
int num_clients, bool verbose)
|
||||
{
|
||||
enum pci_p2pdma_map_type map;
|
||||
bool not_supported = false;
|
||||
struct pci_dev *pci_client;
|
||||
int total_dist = 0;
|
||||
int distance;
|
||||
int i, ret;
|
||||
int i, distance;
|
||||
|
||||
if (num_clients == 0)
|
||||
return -1;
|
||||
|
@ -564,16 +579,12 @@ int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
ret = upstream_bridge_distance_warn(provider,
|
||||
pci_client, &distance);
|
||||
else
|
||||
ret = upstream_bridge_distance(provider, pci_client,
|
||||
&distance, NULL, NULL);
|
||||
map = calc_map_type_and_dist(provider, pci_client, &distance,
|
||||
verbose);
|
||||
|
||||
pci_dev_put(pci_client);
|
||||
|
||||
if (ret == PCI_P2PDMA_MAP_NOT_SUPPORTED)
|
||||
if (map == PCI_P2PDMA_MAP_NOT_SUPPORTED)
|
||||
not_supported = true;
|
||||
|
||||
if (not_supported && !verbose)
|
||||
|
@ -595,7 +606,15 @@ EXPORT_SYMBOL_GPL(pci_p2pdma_distance_many);
|
|||
*/
|
||||
bool pci_has_p2pmem(struct pci_dev *pdev)
|
||||
{
|
||||
return pdev->p2pdma && pdev->p2pdma->p2pmem_published;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
bool res;
|
||||
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(pdev->p2pdma);
|
||||
res = p2pdma && p2pdma->p2pmem_published;
|
||||
rcu_read_unlock();
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_has_p2pmem);
|
||||
|
||||
|
@ -675,6 +694,7 @@ void *pci_alloc_p2pmem(struct pci_dev *pdev, size_t size)
|
|||
{
|
||||
void *ret = NULL;
|
||||
struct percpu_ref *ref;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
|
||||
/*
|
||||
* Pairs with synchronize_rcu() in pci_p2pdma_release() to
|
||||
|
@ -682,16 +702,16 @@ void *pci_alloc_p2pmem(struct pci_dev *pdev, size_t size)
|
|||
* read-lock.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
if (unlikely(!pdev->p2pdma))
|
||||
p2pdma = rcu_dereference(pdev->p2pdma);
|
||||
if (unlikely(!p2pdma))
|
||||
goto out;
|
||||
|
||||
ret = (void *)gen_pool_alloc_owner(pdev->p2pdma->pool, size,
|
||||
(void **) &ref);
|
||||
ret = (void *)gen_pool_alloc_owner(p2pdma->pool, size, (void **) &ref);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (unlikely(!percpu_ref_tryget_live(ref))) {
|
||||
gen_pool_free(pdev->p2pdma->pool, (unsigned long) ret, size);
|
||||
gen_pool_free(p2pdma->pool, (unsigned long) ret, size);
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -710,8 +730,9 @@ EXPORT_SYMBOL_GPL(pci_alloc_p2pmem);
|
|||
void pci_free_p2pmem(struct pci_dev *pdev, void *addr, size_t size)
|
||||
{
|
||||
struct percpu_ref *ref;
|
||||
struct pci_p2pdma *p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
|
||||
|
||||
gen_pool_free_owner(pdev->p2pdma->pool, (uintptr_t)addr, size,
|
||||
gen_pool_free_owner(p2pdma->pool, (uintptr_t)addr, size,
|
||||
(void **) &ref);
|
||||
percpu_ref_put(ref);
|
||||
}
|
||||
|
@ -725,9 +746,13 @@ EXPORT_SYMBOL_GPL(pci_free_p2pmem);
|
|||
*/
|
||||
pci_bus_addr_t pci_p2pmem_virt_to_bus(struct pci_dev *pdev, void *addr)
|
||||
{
|
||||
struct pci_p2pdma *p2pdma;
|
||||
|
||||
if (!addr)
|
||||
return 0;
|
||||
if (!pdev->p2pdma)
|
||||
|
||||
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
|
||||
if (!p2pdma)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -735,7 +760,7 @@ pci_bus_addr_t pci_p2pmem_virt_to_bus(struct pci_dev *pdev, void *addr)
|
|||
* bus address as the physical address. So gen_pool_virt_to_phys()
|
||||
* actually returns the bus address despite the misleading name.
|
||||
*/
|
||||
return gen_pool_virt_to_phys(pdev->p2pdma->pool, (unsigned long)addr);
|
||||
return gen_pool_virt_to_phys(p2pdma->pool, (unsigned long)addr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_p2pmem_virt_to_bus);
|
||||
|
||||
|
@ -806,19 +831,40 @@ EXPORT_SYMBOL_GPL(pci_p2pmem_free_sgl);
|
|||
*/
|
||||
void pci_p2pmem_publish(struct pci_dev *pdev, bool publish)
|
||||
{
|
||||
if (pdev->p2pdma)
|
||||
pdev->p2pdma->p2pmem_published = publish;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(pdev->p2pdma);
|
||||
if (p2pdma)
|
||||
p2pdma->p2pmem_published = publish;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_p2pmem_publish);
|
||||
|
||||
static enum pci_p2pdma_map_type pci_p2pdma_map_type(struct pci_dev *provider,
|
||||
struct pci_dev *client)
|
||||
static enum pci_p2pdma_map_type pci_p2pdma_map_type(struct dev_pagemap *pgmap,
|
||||
struct device *dev)
|
||||
{
|
||||
enum pci_p2pdma_map_type type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
||||
struct pci_dev *provider = to_p2p_pgmap(pgmap)->provider;
|
||||
struct pci_dev *client;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
|
||||
if (!provider->p2pdma)
|
||||
return PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
||||
|
||||
return xa_to_value(xa_load(&provider->p2pdma->map_types,
|
||||
map_types_idx(client)));
|
||||
if (!dev_is_pci(dev))
|
||||
return PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
||||
|
||||
client = to_pci_dev(dev);
|
||||
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(provider->p2pdma);
|
||||
|
||||
if (p2pdma)
|
||||
type = xa_to_value(xa_load(&p2pdma->map_types,
|
||||
map_types_idx(client)));
|
||||
rcu_read_unlock();
|
||||
return type;
|
||||
}
|
||||
|
||||
static int __pci_p2pdma_map_sg(struct pci_p2pdma_pagemap *p2p_pgmap,
|
||||
|
@ -853,14 +899,8 @@ int pci_p2pdma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
|
|||
{
|
||||
struct pci_p2pdma_pagemap *p2p_pgmap =
|
||||
to_p2p_pgmap(sg_page(sg)->pgmap);
|
||||
struct pci_dev *client;
|
||||
|
||||
if (WARN_ON_ONCE(!dev_is_pci(dev)))
|
||||
return 0;
|
||||
|
||||
client = to_pci_dev(dev);
|
||||
|
||||
switch (pci_p2pdma_map_type(p2p_pgmap->provider, client)) {
|
||||
switch (pci_p2pdma_map_type(sg_page(sg)->pgmap, dev)) {
|
||||
case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:
|
||||
return dma_map_sg_attrs(dev, sg, nents, dir, attrs);
|
||||
case PCI_P2PDMA_MAP_BUS_ADDR:
|
||||
|
@ -884,17 +924,9 @@ EXPORT_SYMBOL_GPL(pci_p2pdma_map_sg_attrs);
|
|||
void pci_p2pdma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
struct pci_p2pdma_pagemap *p2p_pgmap =
|
||||
to_p2p_pgmap(sg_page(sg)->pgmap);
|
||||
enum pci_p2pdma_map_type map_type;
|
||||
struct pci_dev *client;
|
||||
|
||||
if (WARN_ON_ONCE(!dev_is_pci(dev)))
|
||||
return;
|
||||
|
||||
client = to_pci_dev(dev);
|
||||
|
||||
map_type = pci_p2pdma_map_type(p2p_pgmap->provider, client);
|
||||
map_type = pci_p2pdma_map_type(sg_page(sg)->pgmap, dev);
|
||||
|
||||
if (map_type == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE)
|
||||
dma_unmap_sg_attrs(dev, sg, nents, dir, attrs);
|
||||
|
|
|
@ -139,14 +139,17 @@ enum acpi_attr_enum {
|
|||
ACPI_ATTR_INDEX_SHOW,
|
||||
};
|
||||
|
||||
static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
|
||||
static int dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = utf16s_to_utf8s((const wchar_t *)obj->buffer.pointer,
|
||||
obj->buffer.length,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
buf, PAGE_SIZE);
|
||||
buf[len] = '\n';
|
||||
buf, PAGE_SIZE - 1);
|
||||
buf[len++] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int dsm_get_label(struct device *dev, char *buf,
|
||||
|
@ -154,7 +157,7 @@ static int dsm_get_label(struct device *dev, char *buf,
|
|||
{
|
||||
acpi_handle handle = ACPI_HANDLE(dev);
|
||||
union acpi_object *obj, *tmp;
|
||||
int len = -1;
|
||||
int len = 0;
|
||||
|
||||
if (!handle)
|
||||
return -1;
|
||||
|
@ -175,20 +178,19 @@ static int dsm_get_label(struct device *dev, char *buf,
|
|||
* this entry must return a null string.
|
||||
*/
|
||||
if (attr == ACPI_ATTR_INDEX_SHOW) {
|
||||
scnprintf(buf, PAGE_SIZE, "%llu\n", tmp->integer.value);
|
||||
len = sysfs_emit(buf, "%llu\n", tmp->integer.value);
|
||||
} else if (attr == ACPI_ATTR_LABEL_SHOW) {
|
||||
if (tmp[1].type == ACPI_TYPE_STRING)
|
||||
scnprintf(buf, PAGE_SIZE, "%s\n",
|
||||
tmp[1].string.pointer);
|
||||
len = sysfs_emit(buf, "%s\n",
|
||||
tmp[1].string.pointer);
|
||||
else if (tmp[1].type == ACPI_TYPE_BUFFER)
|
||||
dsm_label_utf16s_to_utf8s(tmp + 1, buf);
|
||||
len = dsm_label_utf16s_to_utf8s(tmp + 1, buf);
|
||||
}
|
||||
len = strlen(buf) > 0 ? strlen(buf) : -1;
|
||||
}
|
||||
|
||||
ACPI_FREE(obj);
|
||||
|
||||
return len;
|
||||
return len > 0 ? len : -1;
|
||||
}
|
||||
|
||||
static ssize_t label_show(struct device *dev, struct device_attribute *attr,
|
||||
|
|
|
@ -537,7 +537,7 @@ static ssize_t devspec_show(struct device *dev,
|
|||
|
||||
if (np == NULL)
|
||||
return 0;
|
||||
return sysfs_emit(buf, "%pOF", np);
|
||||
return sysfs_emit(buf, "%pOF\n", np);
|
||||
}
|
||||
static DEVICE_ATTR_RO(devspec);
|
||||
#endif
|
||||
|
|
|
@ -5030,6 +5030,16 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
|
|||
return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
|
||||
}
|
||||
|
||||
static int pci_reset_bus_function(struct pci_dev *dev, int probe)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = pci_dev_reset_slot_function(dev, probe);
|
||||
if (rc != -ENOTTY)
|
||||
return rc;
|
||||
return pci_parent_bus_reset(dev, probe);
|
||||
}
|
||||
|
||||
static void pci_dev_lock(struct pci_dev *dev)
|
||||
{
|
||||
pci_cfg_access_lock(dev);
|
||||
|
@ -5152,10 +5162,7 @@ int __pci_reset_function_locked(struct pci_dev *dev)
|
|||
rc = pci_pm_reset(dev, 0);
|
||||
if (rc != -ENOTTY)
|
||||
return rc;
|
||||
rc = pci_dev_reset_slot_function(dev, 0);
|
||||
if (rc != -ENOTTY)
|
||||
return rc;
|
||||
return pci_parent_bus_reset(dev, 0);
|
||||
return pci_reset_bus_function(dev, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
|
||||
|
||||
|
@ -5185,13 +5192,10 @@ int pci_probe_reset_function(struct pci_dev *dev)
|
|||
if (rc != -ENOTTY)
|
||||
return rc;
|
||||
rc = pci_pm_reset(dev, 1);
|
||||
if (rc != -ENOTTY)
|
||||
return rc;
|
||||
rc = pci_dev_reset_slot_function(dev, 1);
|
||||
if (rc != -ENOTTY)
|
||||
return rc;
|
||||
|
||||
return pci_parent_bus_reset(dev, 1);
|
||||
return pci_reset_bus_function(dev, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6451,34 +6455,40 @@ static ssize_t resource_alignment_show(struct bus_type *bus, char *buf)
|
|||
|
||||
spin_lock(&resource_alignment_lock);
|
||||
if (resource_alignment_param)
|
||||
count = scnprintf(buf, PAGE_SIZE, "%s", resource_alignment_param);
|
||||
count = sysfs_emit(buf, "%s\n", resource_alignment_param);
|
||||
spin_unlock(&resource_alignment_lock);
|
||||
|
||||
/*
|
||||
* When set by the command line, resource_alignment_param will not
|
||||
* have a trailing line feed, which is ugly. So conditionally add
|
||||
* it here.
|
||||
*/
|
||||
if (count >= 2 && buf[count - 2] != '\n' && count < PAGE_SIZE - 1) {
|
||||
buf[count - 1] = '\n';
|
||||
buf[count++] = 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t resource_alignment_store(struct bus_type *bus,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
char *param = kstrndup(buf, count, GFP_KERNEL);
|
||||
char *param, *old, *end;
|
||||
|
||||
if (count >= (PAGE_SIZE - 1))
|
||||
return -EINVAL;
|
||||
|
||||
param = kstrndup(buf, count, GFP_KERNEL);
|
||||
if (!param)
|
||||
return -ENOMEM;
|
||||
|
||||
end = strchr(param, '\n');
|
||||
if (end)
|
||||
*end = '\0';
|
||||
|
||||
spin_lock(&resource_alignment_lock);
|
||||
kfree(resource_alignment_param);
|
||||
resource_alignment_param = param;
|
||||
old = resource_alignment_param;
|
||||
if (strlen(param)) {
|
||||
resource_alignment_param = param;
|
||||
} else {
|
||||
kfree(param);
|
||||
resource_alignment_param = NULL;
|
||||
}
|
||||
spin_unlock(&resource_alignment_lock);
|
||||
|
||||
kfree(old);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
@ -324,8 +324,8 @@ struct pci_sriov {
|
|||
/**
|
||||
* pci_dev_set_io_state - Set the new error state if possible.
|
||||
*
|
||||
* @dev - pci device to set new error_state
|
||||
* @new - the state we want dev to be in
|
||||
* @dev: PCI device to set new error_state
|
||||
* @new: the state we want dev to be in
|
||||
*
|
||||
* Must be called with device_lock held.
|
||||
*
|
||||
|
@ -385,6 +385,8 @@ static inline bool pci_dev_is_disconnected(const struct pci_dev *dev)
|
|||
|
||||
/* pci_dev priv_flags */
|
||||
#define PCI_DEV_ADDED 0
|
||||
#define PCI_DPC_RECOVERED 1
|
||||
#define PCI_DPC_RECOVERING 2
|
||||
|
||||
static inline void pci_dev_assign_added(struct pci_dev *dev, bool added)
|
||||
{
|
||||
|
@ -439,10 +441,12 @@ void pci_restore_dpc_state(struct pci_dev *dev);
|
|||
void pci_dpc_init(struct pci_dev *pdev);
|
||||
void dpc_process_error(struct pci_dev *pdev);
|
||||
pci_ers_result_t dpc_reset_link(struct pci_dev *pdev);
|
||||
bool pci_dpc_recovered(struct pci_dev *pdev);
|
||||
#else
|
||||
static inline void pci_save_dpc_state(struct pci_dev *dev) {}
|
||||
static inline void pci_restore_dpc_state(struct pci_dev *dev) {}
|
||||
static inline void pci_dpc_init(struct pci_dev *pdev) {}
|
||||
static inline bool pci_dpc_recovered(struct pci_dev *pdev) { return false; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIEPORTBUS
|
||||
|
|
|
@ -529,21 +529,23 @@ static const char *aer_agent_string[] = {
|
|||
char *buf) \
|
||||
{ \
|
||||
unsigned int i; \
|
||||
char *str = buf; \
|
||||
struct pci_dev *pdev = to_pci_dev(dev); \
|
||||
u64 *stats = pdev->aer_stats->stats_array; \
|
||||
size_t len = 0; \
|
||||
\
|
||||
for (i = 0; i < ARRAY_SIZE(strings_array); i++) { \
|
||||
if (strings_array[i]) \
|
||||
str += sprintf(str, "%s %llu\n", \
|
||||
strings_array[i], stats[i]); \
|
||||
len += sysfs_emit_at(buf, len, "%s %llu\n", \
|
||||
strings_array[i], \
|
||||
stats[i]); \
|
||||
else if (stats[i]) \
|
||||
str += sprintf(str, #stats_array "_bit[%d] %llu\n",\
|
||||
i, stats[i]); \
|
||||
len += sysfs_emit_at(buf, len, \
|
||||
#stats_array "_bit[%d] %llu\n",\
|
||||
i, stats[i]); \
|
||||
} \
|
||||
str += sprintf(str, "TOTAL_%s %llu\n", total_string, \
|
||||
pdev->aer_stats->total_field); \
|
||||
return str-buf; \
|
||||
len += sysfs_emit_at(buf, len, "TOTAL_%s %llu\n", total_string, \
|
||||
pdev->aer_stats->total_field); \
|
||||
return len; \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
|
@ -563,7 +565,7 @@ aer_stats_dev_attr(aer_dev_nonfatal, dev_nonfatal_errs,
|
|||
char *buf) \
|
||||
{ \
|
||||
struct pci_dev *pdev = to_pci_dev(dev); \
|
||||
return sprintf(buf, "%llu\n", pdev->aer_stats->field); \
|
||||
return sysfs_emit(buf, "%llu\n", pdev->aer_stats->field); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
|
@ -983,7 +985,7 @@ static void aer_recover_work_func(struct work_struct *work)
|
|||
pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus,
|
||||
entry.devfn);
|
||||
if (!pdev) {
|
||||
pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n",
|
||||
pr_err("no pci_dev for %04x:%02x:%02x.%x\n",
|
||||
entry.domain, entry.bus,
|
||||
PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
|
||||
continue;
|
||||
|
@ -1022,7 +1024,7 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
|
|||
&aer_recover_ring_lock))
|
||||
schedule_work(&aer_recover_work);
|
||||
else
|
||||
pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n",
|
||||
pr_err("buffer overflow in recovery for %04x:%02x:%02x.%x\n",
|
||||
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aer_recover_queue);
|
||||
|
|
|
@ -1208,7 +1208,7 @@ static ssize_t aspm_attr_show_common(struct device *dev,
|
|||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pcie_link_state *link = pcie_aspm_get_link(pdev);
|
||||
|
||||
return sprintf(buf, "%d\n", (link->aspm_enabled & state) ? 1 : 0);
|
||||
return sysfs_emit(buf, "%d\n", (link->aspm_enabled & state) ? 1 : 0);
|
||||
}
|
||||
|
||||
static ssize_t aspm_attr_store_common(struct device *dev,
|
||||
|
@ -1265,7 +1265,7 @@ static ssize_t clkpm_show(struct device *dev,
|
|||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pcie_link_state *link = pcie_aspm_get_link(pdev);
|
||||
|
||||
return sprintf(buf, "%d\n", link->clkpm_enabled);
|
||||
return sysfs_emit(buf, "%d\n", link->clkpm_enabled);
|
||||
}
|
||||
|
||||
static ssize_t clkpm_store(struct device *dev,
|
||||
|
|
|
@ -71,6 +71,58 @@ void pci_restore_dpc_state(struct pci_dev *dev)
|
|||
pci_write_config_word(dev, dev->dpc_cap + PCI_EXP_DPC_CTL, *cap);
|
||||
}
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(dpc_completed_waitqueue);
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_PCI_PCIE
|
||||
static bool dpc_completed(struct pci_dev *pdev)
|
||||
{
|
||||
u16 status;
|
||||
|
||||
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_STATUS, &status);
|
||||
if ((status != 0xffff) && (status & PCI_EXP_DPC_STATUS_TRIGGER))
|
||||
return false;
|
||||
|
||||
if (test_bit(PCI_DPC_RECOVERING, &pdev->priv_flags))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_dpc_recovered - whether DPC triggered and has recovered successfully
|
||||
* @pdev: PCI device
|
||||
*
|
||||
* Return true if DPC was triggered for @pdev and has recovered successfully.
|
||||
* Wait for recovery if it hasn't completed yet. Called from the PCIe hotplug
|
||||
* driver to recognize and ignore Link Down/Up events caused by DPC.
|
||||
*/
|
||||
bool pci_dpc_recovered(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_host_bridge *host;
|
||||
|
||||
if (!pdev->dpc_cap)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Synchronization between hotplug and DPC is not supported
|
||||
* if DPC is owned by firmware and EDR is not enabled.
|
||||
*/
|
||||
host = pci_find_host_bridge(pdev->bus);
|
||||
if (!host->native_dpc && !IS_ENABLED(CONFIG_PCIE_EDR))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Need a timeout in case DPC never completes due to failure of
|
||||
* dpc_wait_rp_inactive(). The spec doesn't mandate a time limit,
|
||||
* but reports indicate that DPC completes within 4 seconds.
|
||||
*/
|
||||
wait_event_timeout(dpc_completed_waitqueue, dpc_completed(pdev),
|
||||
msecs_to_jiffies(4000));
|
||||
|
||||
return test_and_clear_bit(PCI_DPC_RECOVERED, &pdev->priv_flags);
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_PCI_PCIE */
|
||||
|
||||
static int dpc_wait_rp_inactive(struct pci_dev *pdev)
|
||||
{
|
||||
unsigned long timeout = jiffies + HZ;
|
||||
|
@ -91,8 +143,11 @@ static int dpc_wait_rp_inactive(struct pci_dev *pdev)
|
|||
|
||||
pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
|
||||
{
|
||||
pci_ers_result_t ret;
|
||||
u16 cap;
|
||||
|
||||
set_bit(PCI_DPC_RECOVERING, &pdev->priv_flags);
|
||||
|
||||
/*
|
||||
* DPC disables the Link automatically in hardware, so it has
|
||||
* already been reset by the time we get here.
|
||||
|
@ -106,18 +161,27 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
|
|||
if (!pcie_wait_for_link(pdev, false))
|
||||
pci_info(pdev, "Data Link Layer Link Active not cleared in 1000 msec\n");
|
||||
|
||||
if (pdev->dpc_rp_extensions && dpc_wait_rp_inactive(pdev))
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
if (pdev->dpc_rp_extensions && dpc_wait_rp_inactive(pdev)) {
|
||||
clear_bit(PCI_DPC_RECOVERED, &pdev->priv_flags);
|
||||
ret = PCI_ERS_RESULT_DISCONNECT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
|
||||
PCI_EXP_DPC_STATUS_TRIGGER);
|
||||
|
||||
if (!pcie_wait_for_link(pdev, true)) {
|
||||
pci_info(pdev, "Data Link Layer Link Active not set in 1000 msec\n");
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
clear_bit(PCI_DPC_RECOVERED, &pdev->priv_flags);
|
||||
ret = PCI_ERS_RESULT_DISCONNECT;
|
||||
} else {
|
||||
set_bit(PCI_DPC_RECOVERED, &pdev->priv_flags);
|
||||
ret = PCI_ERS_RESULT_RECOVERED;
|
||||
}
|
||||
|
||||
return PCI_ERS_RESULT_RECOVERED;
|
||||
out:
|
||||
clear_bit(PCI_DPC_RECOVERING, &pdev->priv_flags);
|
||||
wake_up_all(&dpc_completed_waitqueue);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dpc_process_rp_pio_error(struct pci_dev *pdev)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/hypervisor.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/list_sort.h>
|
||||
#include "pci.h"
|
||||
|
||||
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
|
||||
|
@ -874,14 +875,31 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus)
|
|||
dev_set_msi_domain(&bus->dev, d);
|
||||
}
|
||||
|
||||
static int res_cmp(void *priv, const struct list_head *a,
|
||||
const struct list_head *b)
|
||||
{
|
||||
struct resource_entry *entry1, *entry2;
|
||||
|
||||
entry1 = container_of(a, struct resource_entry, node);
|
||||
entry2 = container_of(b, struct resource_entry, node);
|
||||
|
||||
if (entry1->res->flags != entry2->res->flags)
|
||||
return entry1->res->flags > entry2->res->flags;
|
||||
|
||||
if (entry1->offset != entry2->offset)
|
||||
return entry1->offset > entry2->offset;
|
||||
|
||||
return entry1->res->start > entry2->res->start;
|
||||
}
|
||||
|
||||
static int pci_register_host_bridge(struct pci_host_bridge *bridge)
|
||||
{
|
||||
struct device *parent = bridge->dev.parent;
|
||||
struct resource_entry *window, *n;
|
||||
struct resource_entry *window, *next, *n;
|
||||
struct pci_bus *bus, *b;
|
||||
resource_size_t offset;
|
||||
resource_size_t offset, next_offset;
|
||||
LIST_HEAD(resources);
|
||||
struct resource *res;
|
||||
struct resource *res, *next_res;
|
||||
char addr[64], *fmt;
|
||||
const char *name;
|
||||
int err;
|
||||
|
@ -961,11 +979,35 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
|
|||
if (nr_node_ids > 1 && pcibus_to_node(bus) == NUMA_NO_NODE)
|
||||
dev_warn(&bus->dev, "Unknown NUMA node; performance will be reduced\n");
|
||||
|
||||
/* Add initial resources to the bus */
|
||||
/* Sort and coalesce contiguous windows */
|
||||
list_sort(NULL, &resources, res_cmp);
|
||||
resource_list_for_each_entry_safe(window, n, &resources) {
|
||||
list_move_tail(&window->node, &bridge->windows);
|
||||
if (list_is_last(&window->node, &resources))
|
||||
break;
|
||||
|
||||
next = list_next_entry(window, node);
|
||||
offset = window->offset;
|
||||
res = window->res;
|
||||
next_offset = next->offset;
|
||||
next_res = next->res;
|
||||
|
||||
if (res->flags != next_res->flags || offset != next_offset)
|
||||
continue;
|
||||
|
||||
if (res->end + 1 == next_res->start) {
|
||||
next_res->start = res->start;
|
||||
res->flags = res->start = res->end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add initial resources to the bus */
|
||||
resource_list_for_each_entry_safe(window, n, &resources) {
|
||||
offset = window->offset;
|
||||
res = window->res;
|
||||
if (!res->end)
|
||||
continue;
|
||||
|
||||
list_move_tail(&window->node, &bridge->windows);
|
||||
|
||||
if (res->flags & IORESOURCE_BUS)
|
||||
pci_bus_insert_busn_res(bus, bus->number, res->end);
|
||||
|
@ -2249,6 +2291,7 @@ static void pci_release_dev(struct device *dev)
|
|||
pci_bus_put(pci_dev->bus);
|
||||
kfree(pci_dev->driver_override);
|
||||
bitmap_free(pci_dev->dma_alias_mask);
|
||||
dev_dbg(dev, "device released\n");
|
||||
kfree(pci_dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/nvme.h>
|
||||
#include <linux/platform_data/x86/apple.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/switchtec.h>
|
||||
#include <asm/dma.h> /* isa_dma_bridge_buggy */
|
||||
#include "pci.h"
|
||||
|
@ -3656,6 +3657,16 @@ static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
|
|||
return;
|
||||
if (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM)
|
||||
return;
|
||||
|
||||
/*
|
||||
* SXIO/SXFP/SXLF turns off power to the Thunderbolt controller.
|
||||
* We don't know how to turn it back on again, but firmware does,
|
||||
* so we can only use SXIO/SXFP/SXLF if we're suspending via
|
||||
* firmware.
|
||||
*/
|
||||
if (!pm_suspend_via_firmware())
|
||||
return;
|
||||
|
||||
bridge = ACPI_HANDLE(&dev->dev);
|
||||
if (!bridge)
|
||||
return;
|
||||
|
|
|
@ -39,19 +39,19 @@ static const struct sysfs_ops pci_slot_sysfs_ops = {
|
|||
static ssize_t address_read_file(struct pci_slot *slot, char *buf)
|
||||
{
|
||||
if (slot->number == 0xff)
|
||||
return sprintf(buf, "%04x:%02x\n",
|
||||
pci_domain_nr(slot->bus),
|
||||
slot->bus->number);
|
||||
else
|
||||
return sprintf(buf, "%04x:%02x:%02x\n",
|
||||
pci_domain_nr(slot->bus),
|
||||
slot->bus->number,
|
||||
slot->number);
|
||||
return sysfs_emit(buf, "%04x:%02x\n",
|
||||
pci_domain_nr(slot->bus),
|
||||
slot->bus->number);
|
||||
|
||||
return sysfs_emit(buf, "%04x:%02x:%02x\n",
|
||||
pci_domain_nr(slot->bus),
|
||||
slot->bus->number,
|
||||
slot->number);
|
||||
}
|
||||
|
||||
static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", pci_speed_string(speed));
|
||||
return sysfs_emit(buf, "%s\n", pci_speed_string(speed));
|
||||
}
|
||||
|
||||
static ssize_t max_speed_read_file(struct pci_slot *slot, char *buf)
|
||||
|
|
|
@ -280,7 +280,7 @@ static ssize_t device_version_show(struct device *dev,
|
|||
|
||||
ver = ioread32(&stdev->mmio_sys_info->device_version);
|
||||
|
||||
return sprintf(buf, "%x\n", ver);
|
||||
return sysfs_emit(buf, "%x\n", ver);
|
||||
}
|
||||
static DEVICE_ATTR_RO(device_version);
|
||||
|
||||
|
@ -292,7 +292,7 @@ static ssize_t fw_version_show(struct device *dev,
|
|||
|
||||
ver = ioread32(&stdev->mmio_sys_info->firmware_version);
|
||||
|
||||
return sprintf(buf, "%08x\n", ver);
|
||||
return sysfs_emit(buf, "%08x\n", ver);
|
||||
}
|
||||
static DEVICE_ATTR_RO(fw_version);
|
||||
|
||||
|
@ -344,7 +344,7 @@ static ssize_t component_vendor_show(struct device *dev,
|
|||
|
||||
/* component_vendor field not supported after gen3 */
|
||||
if (stdev->gen != SWITCHTEC_GEN3)
|
||||
return sprintf(buf, "none\n");
|
||||
return sysfs_emit(buf, "none\n");
|
||||
|
||||
return io_string_show(buf, &si->gen3.component_vendor,
|
||||
sizeof(si->gen3.component_vendor));
|
||||
|
@ -359,9 +359,9 @@ static ssize_t component_id_show(struct device *dev,
|
|||
|
||||
/* component_id field not supported after gen3 */
|
||||
if (stdev->gen != SWITCHTEC_GEN3)
|
||||
return sprintf(buf, "none\n");
|
||||
return sysfs_emit(buf, "none\n");
|
||||
|
||||
return sprintf(buf, "PM%04X\n", id);
|
||||
return sysfs_emit(buf, "PM%04X\n", id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(component_id);
|
||||
|
||||
|
@ -373,9 +373,9 @@ static ssize_t component_revision_show(struct device *dev,
|
|||
|
||||
/* component_revision field not supported after gen3 */
|
||||
if (stdev->gen != SWITCHTEC_GEN3)
|
||||
return sprintf(buf, "255\n");
|
||||
return sysfs_emit(buf, "255\n");
|
||||
|
||||
return sprintf(buf, "%d\n", rev);
|
||||
return sysfs_emit(buf, "%d\n", rev);
|
||||
}
|
||||
static DEVICE_ATTR_RO(component_revision);
|
||||
|
||||
|
@ -384,7 +384,7 @@ static ssize_t partition_show(struct device *dev,
|
|||
{
|
||||
struct switchtec_dev *stdev = to_stdev(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", stdev->partition);
|
||||
return sysfs_emit(buf, "%d\n", stdev->partition);
|
||||
}
|
||||
static DEVICE_ATTR_RO(partition);
|
||||
|
||||
|
@ -393,7 +393,7 @@ static ssize_t partition_count_show(struct device *dev,
|
|||
{
|
||||
struct switchtec_dev *stdev = to_stdev(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", stdev->partition_count);
|
||||
return sysfs_emit(buf, "%d\n", stdev->partition_count);
|
||||
}
|
||||
static DEVICE_ATTR_RO(partition_count);
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ struct pci_ecam_ops {
|
|||
struct pci_config_window {
|
||||
struct resource res;
|
||||
struct resource busr;
|
||||
unsigned int bus_shift;
|
||||
void *priv;
|
||||
const struct pci_ecam_ops *ops;
|
||||
union {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/**
|
||||
/*
|
||||
* PCI Endpoint ConfigFS header file
|
||||
*
|
||||
* Copyright (C) 2017 Texas Instruments
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
/*
|
||||
* PCI Endpoint *Controller* (EPC) header file
|
||||
*
|
||||
* Copyright (C) 2017 Texas Instruments
|
||||
|
@ -58,6 +58,7 @@ pci_epc_interface_string(enum pci_epc_interface_type type)
|
|||
* @map_msi_irq: ops to map physical address to MSI address and return MSI data
|
||||
* @start: ops to start the PCI link
|
||||
* @stop: ops to stop the PCI link
|
||||
* @get_features: ops to get the features supported by the EPC
|
||||
* @owner: the module owner containing the ops
|
||||
*/
|
||||
struct pci_epc_ops {
|
||||
|
@ -150,6 +151,8 @@ struct pci_epc {
|
|||
/**
|
||||
* struct pci_epc_features - features supported by a EPC device per function
|
||||
* @linkup_notifier: indicate if the EPC device can notify EPF driver on link up
|
||||
* @core_init_notifier: indicate cores that can notify about their availability
|
||||
* for initialization
|
||||
* @msi_capable: indicate if the endpoint function has MSI capability
|
||||
* @msix_capable: indicate if the endpoint function has MSI-X capability
|
||||
* @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
/*
|
||||
* PCI Endpoint *Function* (EPF) header file
|
||||
*
|
||||
* Copyright (C) 2017 Texas Instruments
|
||||
|
@ -102,6 +102,8 @@ struct pci_epf_driver {
|
|||
* @phys_addr: physical address that should be mapped to the BAR
|
||||
* @addr: virtual address corresponding to the @phys_addr
|
||||
* @size: the size of the address space present in BAR
|
||||
* @barno: BAR number
|
||||
* @flags: flags that are set for the BAR
|
||||
*/
|
||||
struct pci_epf_bar {
|
||||
dma_addr_t phys_addr;
|
||||
|
@ -118,6 +120,7 @@ struct pci_epf_bar {
|
|||
* @header: represents standard configuration header
|
||||
* @bar: represents the BAR of EPF device
|
||||
* @msi_interrupts: number of MSI interrupts required by this function
|
||||
* @msix_interrupts: number of MSI-X interrupts required by this function
|
||||
* @func_no: unique function number within this endpoint device
|
||||
* @epc: the EPC device to which this EPF device is bound
|
||||
* @driver: the EPF driver to which this EPF device is bound
|
||||
|
|
|
@ -497,7 +497,7 @@ struct pci_dev {
|
|||
u16 pasid_features;
|
||||
#endif
|
||||
#ifdef CONFIG_PCI_P2PDMA
|
||||
struct pci_p2pdma *p2pdma;
|
||||
struct pci_p2pdma __rcu *p2pdma;
|
||||
#endif
|
||||
u16 acs_cap; /* ACS Capability offset */
|
||||
phys_addr_t rom; /* Physical address if not from BAR */
|
||||
|
|
|
@ -50,6 +50,8 @@ struct hotplug_slot_ops {
|
|||
/**
|
||||
* struct hotplug_slot - used to register a physical slot with the hotplug pci core
|
||||
* @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
|
||||
* @slot_list: internal list used to track hotplug PCI slots
|
||||
* @pci_slot: represents a physical slot
|
||||
* @owner: The module owner of this structure
|
||||
* @mod_name: The module name (KBUILD_MODNAME) of this structure
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/**
|
||||
/*
|
||||
* pcitest.h - PCI test uapi defines
|
||||
*
|
||||
* Copyright (C) 2017 Texas Instruments
|
||||
|
|
Loading…
Add table
Reference in a new issue