From 1f12d3c11f4952295070f56fba6095b896399007 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 12 Mar 2017 23:23:52 +0100 Subject: PCI: Add DT bindings for Faraday Technology PCI Host Bridge Add device tree bindings for the Faraday technology PCI Host Bridge. This IP is found in the Storlink/Storm/Cortina Gemini SoC platform. Signed-off-by: Linus Walleij Signed-off-by: Bjorn Helgaas Acked-by: Rob Herring CC: Janos Laube CC: Paulius Zaleckas CC: Hans Ulli Kroll CC: Florian Fainelli CC: devicetree@vger.kernel.org CC: Feng-Hsin Chiang CC: Greentime Hu --- .../devicetree/bindings/pci/faraday,ftpci100.txt | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/faraday,ftpci100.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pci/faraday,ftpci100.txt b/Documentation/devicetree/bindings/pci/faraday,ftpci100.txt new file mode 100644 index 000000000000..35d4a979bb7b --- /dev/null +++ b/Documentation/devicetree/bindings/pci/faraday,ftpci100.txt @@ -0,0 +1,129 @@ +Faraday Technology FTPCI100 PCI Host Bridge + +This PCI bridge is found inside that Cortina Systems Gemini SoC platform and +is a generic IP block from Faraday Technology. It exists in two variants: +plain and dual PCI. The plain version embeds a cascading interrupt controller +into the host bridge. The dual version routes the interrupts to the host +chips interrupt controller. + +The host controller appear on the PCI bus with vendor ID 0x159b (Faraday +Technology) and product ID 0x4321. + +Mandatory properties: + +- compatible: ranging from specific to generic, should be one of + "cortina,gemini-pci", "faraday,ftpci100" + "cortina,gemini-pci-dual", "faraday,ftpci100-dual" + "faraday,ftpci100" + "faraday,ftpci100-dual" +- reg: memory base and size for the host bridge +- #address-cells: set to <3> +- #size-cells: set to <2> +- #interrupt-cells: set to <1> +- bus-range: set to <0x00 0xff> +- device_type, set to "pci" +- ranges: see pci.txt +- interrupt-map-mask: see pci.txt +- interrupt-map: see pci.txt +- dma-ranges: three ranges for the inbound memory region. The ranges must + be aligned to a 1MB boundary, and may be 1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB, + 128MB, 256MB, 512MB, 1GB or 2GB in size. The memory should be marked as + pre-fetchable. + +Mandatory subnodes: +- For "faraday,ftpci100" a node representing the interrupt-controller inside the + host bridge is mandatory. It has the following mandatory properties: + - interrupt: see interrupt-controller/interrupts.txt + - interrupt-parent: see interrupt-controller/interrupts.txt + - interrupt-controller: see interrupt-controller/interrupts.txt + - #address-cells: set to <0> + - #interrupt-cells: set to <1> + +I/O space considerations: + +The plain variant has 128MiB of non-prefetchable memory space, whereas the +"dual" variant has 64MiB. Take this into account when describing the ranges. + +Interrupt map considerations: + +The "dual" variant will get INT A, B, C, D from the system interrupt controller +and should point to respective interrupt in that controller in its +interrupt-map. + +The code which is the only documentation of how the Faraday PCI (the non-dual +variant) interrupts assigns the default interrupt mapping/swizzling has +typically been like this, doing the swizzling on the interrupt controller side +rather than in the interconnect: + +interrupt-map-mask = <0xf800 0 0 7>; +interrupt-map = + <0x4800 0 0 1 &pci_intc 0>, /* Slot 9 */ + <0x4800 0 0 2 &pci_intc 1>, + <0x4800 0 0 3 &pci_intc 2>, + <0x4800 0 0 4 &pci_intc 3>, + <0x5000 0 0 1 &pci_intc 1>, /* Slot 10 */ + <0x5000 0 0 2 &pci_intc 2>, + <0x5000 0 0 3 &pci_intc 3>, + <0x5000 0 0 4 &pci_intc 0>, + <0x5800 0 0 1 &pci_intc 2>, /* Slot 11 */ + <0x5800 0 0 2 &pci_intc 3>, + <0x5800 0 0 3 &pci_intc 0>, + <0x5800 0 0 4 &pci_intc 1>, + <0x6000 0 0 1 &pci_intc 3>, /* Slot 12 */ + <0x6000 0 0 2 &pci_intc 0>, + <0x6000 0 0 3 &pci_intc 1>, + <0x6000 0 0 4 &pci_intc 2>; + +Example: + +pci@50000000 { + compatible = "cortina,gemini-pci", "faraday,ftpci100"; + reg = <0x50000000 0x100>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH>, /* PCI A */ + <26 IRQ_TYPE_LEVEL_HIGH>, /* PCI B */ + <27 IRQ_TYPE_LEVEL_HIGH>, /* PCI C */ + <28 IRQ_TYPE_LEVEL_HIGH>; /* PCI D */ + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + bus-range = <0x00 0xff>; + ranges = /* 1MiB I/O space 0x50000000-0x500fffff */ + <0x01000000 0 0 0x50000000 0 0x00100000>, + /* 128MiB non-prefetchable memory 0x58000000-0x5fffffff */ + <0x02000000 0 0x58000000 0x58000000 0 0x08000000>; + + /* DMA ranges */ + dma-ranges = + /* 128MiB at 0x00000000-0x07ffffff */ + <0x02000000 0 0x00000000 0x00000000 0 0x08000000>, + /* 64MiB at 0x00000000-0x03ffffff */ + <0x02000000 0 0x00000000 0x00000000 0 0x04000000>, + /* 64MiB at 0x00000000-0x03ffffff */ + <0x02000000 0 0x00000000 0x00000000 0 0x04000000>; + + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = + <0x4800 0 0 1 &pci_intc 0>, /* Slot 9 */ + <0x4800 0 0 2 &pci_intc 1>, + <0x4800 0 0 3 &pci_intc 2>, + <0x4800 0 0 4 &pci_intc 3>, + <0x5000 0 0 1 &pci_intc 1>, /* Slot 10 */ + <0x5000 0 0 2 &pci_intc 2>, + <0x5000 0 0 3 &pci_intc 3>, + <0x5000 0 0 4 &pci_intc 0>, + <0x5800 0 0 1 &pci_intc 2>, /* Slot 11 */ + <0x5800 0 0 2 &pci_intc 3>, + <0x5800 0 0 3 &pci_intc 0>, + <0x5800 0 0 4 &pci_intc 1>, + <0x6000 0 0 1 &pci_intc 3>, /* Slot 12 */ + <0x6000 0 0 2 &pci_intc 0>, + <0x6000 0 0 3 &pci_intc 0>, + <0x6000 0 0 4 &pci_intc 0>; + pci_intc: interrupt-controller { + interrupt-parent = <&intcon>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; +}; -- cgit v1.2.3 From 9b3fe6796d7c0e0c2b87243ce0c7f4744c54efad Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 28 Mar 2017 08:42:49 -0700 Subject: PCI: imx6: Add code to support i.MX7D Add various bits of code needed to support i.MX7D variant of the IP. Signed-off-by: Andrey Smirnov Signed-off-by: Bjorn Helgaas Reviewed-by: Lucas Stach Acked-by: Lee Jones Acked-by: Rob Herring Cc: yurovsky@gmail.com Cc: Mark Rutland Cc: Fabio Estevam Cc: Dong Aisheng Cc: linux-arm-kernel@lists.infradead.org Cc: devicetree@vger.kernel.org --- .../devicetree/bindings/pci/fsl,imx6q-pcie.txt | 14 ++- drivers/pci/dwc/pci-imx6.c | 120 ++++++++++++++++----- include/linux/mfd/syscon/imx7-iomuxc-gpr.h | 4 + 3 files changed, 112 insertions(+), 26 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt index 83aeb1f5a645..e3d5680875b1 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt @@ -4,7 +4,11 @@ This PCIe host controller is based on the Synopsis Designware PCIe IP and thus inherits all the common properties defined in designware-pcie.txt. Required properties: -- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie", "fsl,imx6qp-pcie" +- compatible: + - "fsl,imx6q-pcie" + - "fsl,imx6sx-pcie", + - "fsl,imx6qp-pcie" + - "fsl,imx7d-pcie" - reg: base address and length of the PCIe controller - interrupts: A list of interrupt outputs of the controller. Must contain an entry for each entry in the interrupt-names property. @@ -34,6 +38,14 @@ Additional required properties for imx6sx-pcie: - clock names: Must include the following additional entries: - "pcie_inbound_axi" +Additional required properties for imx7d-pcie: +- power-domains: Must be set to a phandle pointing to PCIE_PHY power domain +- resets: Must contain phandles to PCIe-related reset lines exposed by SRC + IP block +- reset-names: Must contain the following entires: + - "pciephy" + - "apps" + Example: pcie@0x01000000 { diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c index 801e46cd266d..e0c36d6f5a21 100644 --- a/drivers/pci/dwc/pci-imx6.c +++ b/drivers/pci/dwc/pci-imx6.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include "pcie-designware.h" @@ -36,6 +38,7 @@ enum imx6_pcie_variants { IMX6Q, IMX6SX, IMX6QP, + IMX7D, }; struct imx6_pcie { @@ -47,6 +50,8 @@ struct imx6_pcie { struct clk *pcie_inbound_axi; struct clk *pcie; struct regmap *iomuxc_gpr; + struct reset_control *pciephy_reset; + struct reset_control *apps_reset; enum imx6_pcie_variants variant; u32 tx_deemph_gen1; u32 tx_deemph_gen2_3p5db; @@ -56,6 +61,11 @@ struct imx6_pcie { int link_gen; }; +/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ +#define PHY_PLL_LOCK_WAIT_MAX_RETRIES 2000 +#define PHY_PLL_LOCK_WAIT_USLEEP_MIN 50 +#define PHY_PLL_LOCK_WAIT_USLEEP_MAX 200 + /* PCIe Root Complex registers (memory-mapped) */ #define PCIE_RC_LCR 0x7c #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1 @@ -248,6 +258,10 @@ static int imx6q_pcie_abort_handler(unsigned long addr, static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { switch (imx6_pcie->variant) { + case IMX7D: + reset_control_assert(imx6_pcie->pciephy_reset); + reset_control_assert(imx6_pcie->apps_reset); + break; case IMX6SX: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_TEST_POWERDOWN, @@ -303,11 +317,32 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); break; + case IMX7D: + break; } return ret; } +static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) +{ + u32 val; + unsigned int retries; + struct device *dev = imx6_pcie->pci->dev; + + for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) { + regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR22, &val); + + if (val & IMX7D_GPR22_PCIE_PHY_PLL_LOCKED) + return; + + usleep_range(PHY_PLL_LOCK_WAIT_USLEEP_MIN, + PHY_PLL_LOCK_WAIT_USLEEP_MAX); + } + + dev_err(dev, "PCIe PLL lock timeout\n"); +} + static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) { struct dw_pcie *pci = imx6_pcie->pci; @@ -351,6 +386,10 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) } switch (imx6_pcie->variant) { + case IMX7D: + reset_control_deassert(imx6_pcie->pciephy_reset); + imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); + break; case IMX6SX: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET, 0); @@ -377,35 +416,44 @@ err_pcie_bus: static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { - if (imx6_pcie->variant == IMX6SX) + switch (imx6_pcie->variant) { + case IMX7D: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); + break; + case IMX6SX: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_RX_EQ_MASK, IMX6SX_GPR12_PCIE_RX_EQ_2); + /* FALLTHROUGH */ + default: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); + /* configure constant input signal to the pcie ctrl and phy */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_LOS_LEVEL, 9 << 4); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN1, + imx6_pcie->tx_deemph_gen1 << 0); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, + imx6_pcie->tx_deemph_gen2_3p5db << 6); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, + imx6_pcie->tx_deemph_gen2_6db << 12); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_SWING_FULL, + imx6_pcie->tx_swing_full << 18); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_SWING_LOW, + imx6_pcie->tx_swing_low << 25); + break; + } - /* configure constant input signal to the pcie ctrl and phy */ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_LOS_LEVEL, 9 << 4); - - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN1, - imx6_pcie->tx_deemph_gen1 << 0); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, - imx6_pcie->tx_deemph_gen2_3p5db << 6); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, - imx6_pcie->tx_deemph_gen2_6db << 12); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_SWING_FULL, - imx6_pcie->tx_swing_full << 18); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_SWING_LOW, - imx6_pcie->tx_swing_low << 25); } static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) @@ -469,8 +517,11 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); /* Start LTSSM. */ - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); + if (imx6_pcie->variant == IMX7D) + reset_control_deassert(imx6_pcie->apps_reset); + else + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); ret = imx6_pcie_wait_for_link(imx6_pcie); if (ret) @@ -653,13 +704,31 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx6_pcie->pcie); } - if (imx6_pcie->variant == IMX6SX) { + switch (imx6_pcie->variant) { + case IMX6SX: imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, "pcie_inbound_axi"); if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { dev_err(dev, "pcie_inbound_axi clock missing or invalid\n"); return PTR_ERR(imx6_pcie->pcie_inbound_axi); } + break; + case IMX7D: + imx6_pcie->pciephy_reset = devm_reset_control_get(dev, + "pciephy"); + if (IS_ERR(imx6_pcie->pciephy_reset)) { + dev_err(dev, "Failed to get PCIEPHY reset contol\n"); + return PTR_ERR(imx6_pcie->pciephy_reset); + } + + imx6_pcie->apps_reset = devm_reset_control_get(dev, "apps"); + if (IS_ERR(imx6_pcie->apps_reset)) { + dev_err(dev, "Failed to get PCIE APPS reset contol\n"); + return PTR_ERR(imx6_pcie->apps_reset); + } + break; + default: + break; } /* Grab GPR config register range */ @@ -718,6 +787,7 @@ static const struct of_device_id imx6_pcie_of_match[] = { { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, }, { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, }, { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, + { .compatible = "fsl,imx7d-pcie", .data = (void *)IMX7D, }, {}, }; diff --git a/include/linux/mfd/syscon/imx7-iomuxc-gpr.h b/include/linux/mfd/syscon/imx7-iomuxc-gpr.h index 4585d6105d68..abbd52466573 100644 --- a/include/linux/mfd/syscon/imx7-iomuxc-gpr.h +++ b/include/linux/mfd/syscon/imx7-iomuxc-gpr.h @@ -44,4 +44,8 @@ #define IMX7D_GPR5_CSI_MUX_CONTROL_MIPI (0x1 << 4) +#define IMX7D_GPR12_PCIE_PHY_REFCLK_SEL BIT(5) + +#define IMX7D_GPR22_PCIE_PHY_PLL_LOCKED BIT(31) + #endif /* __LINUX_IMX7_IOMUXC_GPR_H */ -- cgit v1.2.3 From a8a5be098885e8394497a6b64fb3f96dd217846b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:14:58 +0530 Subject: Documentation: PCI: Guide to use PCI Endpoint Core Layer Add Documentation to help users use endpoint library to enable endpoint mode in the PCI controller and add new PCI endpoint functions. Signed-off-by: Kishon Vijay Abraham I Acked-By: Joao Pinto Signed-off-by: Bjorn Helgaas --- Documentation/PCI/00-INDEX | 2 + Documentation/PCI/endpoint/pci-endpoint.txt | 215 ++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 Documentation/PCI/endpoint/pci-endpoint.txt (limited to 'Documentation') diff --git a/Documentation/PCI/00-INDEX b/Documentation/PCI/00-INDEX index 147231f1613e..ba950b296bd8 100644 --- a/Documentation/PCI/00-INDEX +++ b/Documentation/PCI/00-INDEX @@ -12,3 +12,5 @@ pci.txt - info on the PCI subsystem for device driver authors pcieaer-howto.txt - the PCI Express Advanced Error Reporting Driver Guide HOWTO +endpoint/pci-endpoint.txt + - guide to add endpoint controller driver and endpoint function driver. diff --git a/Documentation/PCI/endpoint/pci-endpoint.txt b/Documentation/PCI/endpoint/pci-endpoint.txt new file mode 100644 index 000000000000..9b1d66829290 --- /dev/null +++ b/Documentation/PCI/endpoint/pci-endpoint.txt @@ -0,0 +1,215 @@ + PCI ENDPOINT FRAMEWORK + Kishon Vijay Abraham I + +This document is a guide to use the PCI Endpoint Framework in order to create +endpoint controller driver, endpoint function driver, and using configfs +interface to bind the function driver to the controller driver. + +1. Introduction + +Linux has a comprehensive PCI subsystem to support PCI controllers that +operates in Root Complex mode. The subsystem has capability to scan PCI bus, +assign memory resources and IRQ resources, load PCI driver (based on +vendor ID, device ID), support other services like hot-plug, power management, +advanced error reporting and virtual channels. + +However the PCI controller IP integrated in some SoCs is capable of operating +either in Root Complex mode or Endpoint mode. PCI Endpoint Framework will +add endpoint mode support in Linux. This will help to run Linux in an +EP system which can have a wide variety of use cases from testing or +validation, co-processor accelerator, etc. + +2. PCI Endpoint Core + +The PCI Endpoint Core layer comprises 3 components: the Endpoint Controller +library, the Endpoint Function library, and the configfs layer to bind the +endpoint function with the endpoint controller. + +2.1 PCI Endpoint Controller(EPC) Library + +The EPC library provides APIs to be used by the controller that can operate +in endpoint mode. It also provides APIs to be used by function driver/library +in order to implement a particular endpoint function. + +2.1.1 APIs for the PCI controller Driver + +This section lists the APIs that the PCI Endpoint core provides to be used +by the PCI controller driver. + +*) devm_pci_epc_create()/pci_epc_create() + + The PCI controller driver should implement the following ops: + * write_header: ops to populate configuration space header + * set_bar: ops to configure the BAR + * clear_bar: ops to reset the BAR + * alloc_addr_space: ops to allocate in PCI controller address space + * free_addr_space: ops to free the allocated address space + * raise_irq: ops to raise a legacy or MSI interrupt + * start: ops to start the PCI link + * stop: ops to stop the PCI link + + The PCI controller driver can then create a new EPC device by invoking + devm_pci_epc_create()/pci_epc_create(). + +*) devm_pci_epc_destroy()/pci_epc_destroy() + + The PCI controller driver can destroy the EPC device created by either + devm_pci_epc_create() or pci_epc_create() using devm_pci_epc_destroy() or + pci_epc_destroy(). + +*) pci_epc_linkup() + + In order to notify all the function devices that the EPC device to which + they are linked has established a link with the host, the PCI controller + driver should invoke pci_epc_linkup(). + +*) pci_epc_mem_init() + + Initialize the pci_epc_mem structure used for allocating EPC addr space. + +*) pci_epc_mem_exit() + + Cleanup the pci_epc_mem structure allocated during pci_epc_mem_init(). + +2.1.2 APIs for the PCI Endpoint Function Driver + +This section lists the APIs that the PCI Endpoint core provides to be used +by the PCI endpoint function driver. + +*) pci_epc_write_header() + + The PCI endpoint function driver should use pci_epc_write_header() to + write the standard configuration header to the endpoint controller. + +*) pci_epc_set_bar() + + The PCI endpoint function driver should use pci_epc_set_bar() to configure + the Base Address Register in order for the host to assign PCI addr space. + Register space of the function driver is usually configured + using this API. + +*) pci_epc_clear_bar() + + The PCI endpoint function driver should use pci_epc_clear_bar() to reset + the BAR. + +*) pci_epc_raise_irq() + + The PCI endpoint function driver should use pci_epc_raise_irq() to raise + Legacy Interrupt or MSI Interrupt. + +*) pci_epc_mem_alloc_addr() + + The PCI endpoint function driver should use pci_epc_mem_alloc_addr(), to + allocate memory address from EPC addr space which is required to access + RC's buffer + +*) pci_epc_mem_free_addr() + + The PCI endpoint function driver should use pci_epc_mem_free_addr() to + free the memory space allocated using pci_epc_mem_alloc_addr(). + +2.1.3 Other APIs + +There are other APIs provided by the EPC library. These are used for binding +the EPF device with EPC device. pci-ep-cfs.c can be used as reference for +using these APIs. + +*) pci_epc_get() + + Get a reference to the PCI endpoint controller based on the device name of + the controller. + +*) pci_epc_put() + + Release the reference to the PCI endpoint controller obtained using + pci_epc_get() + +*) pci_epc_add_epf() + + Add a PCI endpoint function to a PCI endpoint controller. A PCIe device + can have up to 8 functions according to the specification. + +*) pci_epc_remove_epf() + + Remove the PCI endpoint function from PCI endpoint controller. + +*) pci_epc_start() + + The PCI endpoint function driver should invoke pci_epc_start() once it + has configured the endpoint function and wants to start the PCI link. + +*) pci_epc_stop() + + The PCI endpoint function driver should invoke pci_epc_stop() to stop + the PCI LINK. + +2.2 PCI Endpoint Function(EPF) Library + +The EPF library provides APIs to be used by the function driver and the EPC +library to provide endpoint mode functionality. + +2.2.1 APIs for the PCI Endpoint Function Driver + +This section lists the APIs that the PCI Endpoint core provides to be used +by the PCI endpoint function driver. + +*) pci_epf_register_driver() + + The PCI Endpoint Function driver should implement the following ops: + * bind: ops to perform when a EPC device has been bound to EPF device + * unbind: ops to perform when a binding has been lost between a EPC + device and EPF device + * linkup: ops to perform when the EPC device has established a + connection with a host system + + The PCI Function driver can then register the PCI EPF driver by using + pci_epf_register_driver(). + +*) pci_epf_unregister_driver() + + The PCI Function driver can unregister the PCI EPF driver by using + pci_epf_unregister_driver(). + +*) pci_epf_alloc_space() + + The PCI Function driver can allocate space for a particular BAR using + pci_epf_alloc_space(). + +*) pci_epf_free_space() + + The PCI Function driver can free the allocated space + (using pci_epf_alloc_space) by invoking pci_epf_free_space(). + +2.2.2 APIs for the PCI Endpoint Controller Library +This section lists the APIs that the PCI Endpoint core provides to be used +by the PCI endpoint controller library. + +*) pci_epf_linkup() + + The PCI endpoint controller library invokes pci_epf_linkup() when the + EPC device has established the connection to the host. + +2.2.2 Other APIs +There are other APIs provided by the EPF library. These are used to notify +the function driver when the EPF device is bound to the EPC device. +pci-ep-cfs.c can be used as reference for using these APIs. + +*) pci_epf_create() + + Create a new PCI EPF device by passing the name of the PCI EPF device. + This name will be used to bind the the EPF device to a EPF driver. + +*) pci_epf_destroy() + + Destroy the created PCI EPF device. + +*) pci_epf_bind() + + pci_epf_bind() should be invoked when the EPF device has been bound to + a EPC device. + +*) pci_epf_unbind() + + pci_epf_unbind() should be invoked when the binding between EPC device + and EPF device is lost. -- cgit v1.2.3 From bea37d3d3121d051b12541f06327d7ac63996fc0 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:15:00 +0530 Subject: Documentation: PCI: Guide to use PCI endpoint configfs Add Documentation to help users use PCI endpoint to configure PCI endpoint function and to bind the endpoint function with endpoint controller. Signed-off-by: Kishon Vijay Abraham I Acked-By: Joao Pinto Signed-off-by: Bjorn Helgaas --- Documentation/PCI/00-INDEX | 2 + Documentation/PCI/endpoint/pci-endpoint-cfs.txt | 105 ++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 Documentation/PCI/endpoint/pci-endpoint-cfs.txt (limited to 'Documentation') diff --git a/Documentation/PCI/00-INDEX b/Documentation/PCI/00-INDEX index ba950b296bd8..bf8223a1bf08 100644 --- a/Documentation/PCI/00-INDEX +++ b/Documentation/PCI/00-INDEX @@ -14,3 +14,5 @@ pcieaer-howto.txt - the PCI Express Advanced Error Reporting Driver Guide HOWTO endpoint/pci-endpoint.txt - guide to add endpoint controller driver and endpoint function driver. +endpoint/pci-endpoint-cfs.txt + - guide to use configfs to configure the PCI endpoint function. diff --git a/Documentation/PCI/endpoint/pci-endpoint-cfs.txt b/Documentation/PCI/endpoint/pci-endpoint-cfs.txt new file mode 100644 index 000000000000..d740f29960a4 --- /dev/null +++ b/Documentation/PCI/endpoint/pci-endpoint-cfs.txt @@ -0,0 +1,105 @@ + CONFIGURING PCI ENDPOINT USING CONFIGFS + Kishon Vijay Abraham I + +The PCI Endpoint Core exposes configfs entry (pci_ep) to configure the +PCI endpoint function and to bind the endpoint function +with the endpoint controller. (For introducing other mechanisms to +configure the PCI Endpoint Function refer to [1]). + +*) Mounting configfs + +The PCI Endpoint Core layer creates pci_ep directory in the mounted configfs +directory. configfs can be mounted using the following command. + + mount -t configfs none /sys/kernel/config + +*) Directory Structure + +The pci_ep configfs has two directories at its root: controllers and +functions. Every EPC device present in the system will have an entry in +the *controllers* directory and and every EPF driver present in the system +will have an entry in the *functions* directory. + +/sys/kernel/config/pci_ep/ + .. controllers/ + .. functions/ + +*) Creating EPF Device + +Every registered EPF driver will be listed in controllers directory. The +entries corresponding to EPF driver will be created by the EPF core. + +/sys/kernel/config/pci_ep/functions/ + .. / + ... / + ... / + .. / + ... / + ... / + +In order to create a of the type probed by , the +user has to create a directory inside . + +Every directory consists of the following entries that can be +used to configure the standard configuration header of the endpoint function. +(These entries are created by the framework when any new is +created) + + .. / + ... / + ... vendorid + ... deviceid + ... revid + ... progif_code + ... subclass_code + ... baseclass_code + ... cache_line_size + ... subsys_vendor_id + ... subsys_id + ... interrupt_pin + +*) EPC Device + +Every registered EPC device will be listed in controllers directory. The +entries corresponding to EPC device will be created by the EPC core. + +/sys/kernel/config/pci_ep/controllers/ + .. / + ... / + ... / + ... start + .. / + ... / + ... / + ... start + +The directory will have a list of symbolic links to +. These symbolic links should be created by the user to +represent the functions present in the endpoint device. + +The directory will also have a *start* field. Once +"1" is written to this field, the endpoint device will be ready to +establish the link with the host. This is usually done after +all the EPF devices are created and linked with the EPC device. + + + | controllers/ + | / + | + | start + | functions/ + | / + | / + | vendorid + | deviceid + | revid + | progif_code + | subclass_code + | baseclass_code + | cache_line_size + | subsys_vendor_id + | subsys_id + | interrupt_pin + | function + +[1] -> Documentation/PCI/endpoint/pci-endpoint.txt -- cgit v1.2.3 From 42fc2ac6e809a9f383e8ffa78b7f3fcd2fab1649 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:15:02 +0530 Subject: Documentation: PCI: Add specification for the *PCI test* function device Add specification for the *PCI test* virtual function device. The endpoint function driver and the host PCI driver should be created based on this specification. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas --- Documentation/PCI/00-INDEX | 2 + Documentation/PCI/endpoint/pci-test-function.txt | 66 ++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 Documentation/PCI/endpoint/pci-test-function.txt (limited to 'Documentation') diff --git a/Documentation/PCI/00-INDEX b/Documentation/PCI/00-INDEX index bf8223a1bf08..ab35e4bbdb1c 100644 --- a/Documentation/PCI/00-INDEX +++ b/Documentation/PCI/00-INDEX @@ -16,3 +16,5 @@ endpoint/pci-endpoint.txt - guide to add endpoint controller driver and endpoint function driver. endpoint/pci-endpoint-cfs.txt - guide to use configfs to configure the PCI endpoint function. +endpoint/pci-test-function.txt + - specification of *PCI test* function device. diff --git a/Documentation/PCI/endpoint/pci-test-function.txt b/Documentation/PCI/endpoint/pci-test-function.txt new file mode 100644 index 000000000000..0c519c9bf94a --- /dev/null +++ b/Documentation/PCI/endpoint/pci-test-function.txt @@ -0,0 +1,66 @@ + PCI TEST + Kishon Vijay Abraham I + +Traditionally PCI RC has always been validated by using standard +PCI cards like ethernet PCI cards or USB PCI cards or SATA PCI cards. +However with the addition of EP-core in linux kernel, it is possible +to configure a PCI controller that can operate in EP mode to work as +a test device. + +The PCI endpoint test device is a virtual device (defined in software) +used to test the endpoint functionality and serve as a sample driver +for other PCI endpoint devices (to use the EP framework). + +The PCI endpoint test device has the following registers: + + 1) PCI_ENDPOINT_TEST_MAGIC + 2) PCI_ENDPOINT_TEST_COMMAND + 3) PCI_ENDPOINT_TEST_STATUS + 4) PCI_ENDPOINT_TEST_SRC_ADDR + 5) PCI_ENDPOINT_TEST_DST_ADDR + 6) PCI_ENDPOINT_TEST_SIZE + 7) PCI_ENDPOINT_TEST_CHECKSUM + +*) PCI_ENDPOINT_TEST_MAGIC + +This register will be used to test BAR0. A known pattern will be written +and read back from MAGIC register to verify BAR0. + +*) PCI_ENDPOINT_TEST_COMMAND: + +This register will be used by the host driver to indicate the function +that the endpoint device must perform. + +Bitfield Description: + Bit 0 : raise legacy IRQ + Bit 1 : raise MSI IRQ + Bit 2 - 7 : MSI interrupt number + Bit 8 : read command (read data from RC buffer) + Bit 9 : write command (write data to RC buffer) + Bit 10 : copy command (copy data from one RC buffer to another + RC buffer) + +*) PCI_ENDPOINT_TEST_STATUS + +This register reflects the status of the PCI endpoint device. + +Bitfield Description: + Bit 0 : read success + Bit 1 : read fail + Bit 2 : write success + Bit 3 : write fail + Bit 4 : copy success + Bit 5 : copy fail + Bit 6 : IRQ raised + Bit 7 : source address is invalid + Bit 8 : destination address is invalid + +*) PCI_ENDPOINT_TEST_SRC_ADDR + +This register contains the source address (RC buffer address) for the +COPY/READ command. + +*) PCI_ENDPOINT_TEST_DST_ADDR + +This register contains the destination address (RC buffer address) for +the COPY/WRITE command. -- cgit v1.2.3 From 44fc691b4fac3aff3e99b42cad3a994f5a6621ac Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Thu, 2 Mar 2017 16:24:32 -0700 Subject: switchtec: Add user interface documentation Add standard documentation for the sysfs switchtec attributes and a RST formatted text file which documents the char device interface. Jonathan Corbet has indicated he will move this to a new user-space developer documentation book once it's created. Tested-by: Krishna Dhulipala Signed-off-by: Logan Gunthorpe Signed-off-by: Stephen Bates Signed-off-by: Bjorn Helgaas Reviewed-by: Wei Zhang Reviewed-by: Jens Axboe --- Documentation/switchtec.txt | 53 +++++++++++++++++++++++++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 54 insertions(+) create mode 100644 Documentation/switchtec.txt (limited to 'Documentation') diff --git a/Documentation/switchtec.txt b/Documentation/switchtec.txt new file mode 100644 index 000000000000..4bced4c78446 --- /dev/null +++ b/Documentation/switchtec.txt @@ -0,0 +1,53 @@ +======================== +Linux Switchtec Support +======================== + +Microsemi's "Switchtec" line of PCI switch devices is already +supported by the kernel with standard PCI switch drivers. However, the +Switchtec device advertises a special management endpoint which +enables some additional functionality. This includes: + +* Packet and Byte Counters +* Firmware Upgrades +* Event and Error logs +* Querying port link status +* Custom user firmware commands + +The switchtec kernel module implements this functionality. + + +Interface +========= + +The primary means of communicating with the Switchtec management firmware is +through the Memory-mapped Remote Procedure Call (MRPC) interface. +Commands are submitted to the interface with a 4-byte command +identifier and up to 1KB of command specific data. The firmware will +respond with a 4 bytes return code and up to 1KB of command specific +data. The interface only processes a single command at a time. + + +Userspace Interface +=================== + +The MRPC interface will be exposed to userspace through a simple char +device: /dev/switchtec#, one for each management endpoint in the system. + +The char device has the following semantics: + +* A write must consist of at least 4 bytes and no more than 1028 bytes. + The first four bytes will be interpreted as the command to run and + the remainder will be used as the input data. A write will send the + command to the firmware to begin processing. + +* Each write must be followed by exactly one read. Any double write will + produce an error and any read that doesn't follow a write will + produce an error. + +* A read will block until the firmware completes the command and return + the four bytes of status plus up to 1024 bytes of output data. (The + length will be specified by the size parameter of the read call -- + reading less than 4 bytes will produce an error. + +* The poll call will also be supported for userspace applications that + need to do other things while waiting for the command to complete. diff --git a/MAINTAINERS b/MAINTAINERS index 16bd75d30ac1..5bc3b7c7e0d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9661,6 +9661,7 @@ M: Stephen Bates M: Logan Gunthorpe L: linux-pci@vger.kernel.org S: Maintained +F: Documentation/switchtec.txt F: drivers/pci/switch/switchtec* PCI DRIVER FOR NVIDIA TEGRA -- cgit v1.2.3 From 5d8e1881f431cf470170813eb048e6a384340904 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Thu, 2 Mar 2017 16:24:33 -0700 Subject: switchtec: Add sysfs attributes to the Switchtec driver Add a few read-only sysfs attributes which provide some device information that is exposed from the devices, primarily component and device names and versions. These are documented in Documentation/ABI/testing/sysfs-class-switchtec. Tested-by: Krishna Dhulipala Signed-off-by: Logan Gunthorpe Signed-off-by: Stephen Bates Signed-off-by: Bjorn Helgaas Reviewed-by: Wei Zhang Reviewed-by: Jens Axboe Reviewed-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-switchtec | 96 ++++++++++++++++++++ MAINTAINERS | 1 + drivers/pci/switch/switchtec.c | 113 ++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-switchtec (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-class-switchtec b/Documentation/ABI/testing/sysfs-class-switchtec new file mode 100644 index 000000000000..48cb4c15e430 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-switchtec @@ -0,0 +1,96 @@ +switchtec - Microsemi Switchtec PCI Switch Management Endpoint + +For details on this subsystem look at Documentation/switchtec.txt. + +What: /sys/class/switchtec +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: The switchtec class subsystem folder. + Each registered switchtec driver is represented by a switchtecX + subfolder (X being an integer >= 0). + + +What: /sys/class/switchtec/switchtec[0-9]+/component_id +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Component identifier as stored in the hardware (eg. PM8543) + (read only) +Values: arbitrary string. + + +What: /sys/class/switchtec/switchtec[0-9]+/component_revision +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Component revision stored in the hardware (read only) +Values: integer. + + +What: /sys/class/switchtec/switchtec[0-9]+/component_vendor +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Component vendor as stored in the hardware (eg. MICROSEM) + (read only) +Values: arbitrary string. + + +What: /sys/class/switchtec/switchtec[0-9]+/device_version +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Device version as stored in the hardware (read only) +Values: integer. + + +What: /sys/class/switchtec/switchtec[0-9]+/fw_version +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Currently running firmware version (read only) +Values: integer (in hexadecimal). + + +What: /sys/class/switchtec/switchtec[0-9]+/partition +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Partition number for this device in the switch (read only) +Values: integer. + + +What: /sys/class/switchtec/switchtec[0-9]+/partition_count +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Total number of partitions in the switch (read only) +Values: integer. + + +What: /sys/class/switchtec/switchtec[0-9]+/product_id +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Product identifier as stored in the hardware (eg. PSX 48XG3) + (read only) +Values: arbitrary string. + + +What: /sys/class/switchtec/switchtec[0-9]+/product_revision +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Product revision stored in the hardware (eg. RevB) + (read only) +Values: arbitrary string. + + +What: /sys/class/switchtec/switchtec[0-9]+/product_vendor +Date: 05-Jan-2017 +KernelVersion: v4.11 +Contact: Logan Gunthorpe +Description: Product vendor as stored in the hardware (eg. MICROSEM) + (read only) +Values: arbitrary string. diff --git a/MAINTAINERS b/MAINTAINERS index 5bc3b7c7e0d9..76ccc5a805dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9662,6 +9662,7 @@ M: Logan Gunthorpe L: linux-pci@vger.kernel.org S: Maintained F: Documentation/switchtec.txt +F: Documentation/ABI/testing/sysfs-class-switchtec F: drivers/pci/switch/switchtec* PCI DRIVER FOR NVIDIA TEGRA diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 0bbe90df968d..15ff61c7718b 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -486,6 +486,118 @@ out: mutex_unlock(&stdev->mrpc_mutex); } +static ssize_t device_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + u32 ver; + + ver = ioread32(&stdev->mmio_sys_info->device_version); + + return sprintf(buf, "%x\n", ver); +} +static DEVICE_ATTR_RO(device_version); + +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + u32 ver; + + ver = ioread32(&stdev->mmio_sys_info->firmware_version); + + return sprintf(buf, "%08x\n", ver); +} +static DEVICE_ATTR_RO(fw_version); + +static ssize_t io_string_show(char *buf, void __iomem *attr, size_t len) +{ + int i; + + memcpy_fromio(buf, attr, len); + buf[len] = '\n'; + buf[len + 1] = 0; + + for (i = len - 1; i > 0; i--) { + if (buf[i] != ' ') + break; + buf[i] = '\n'; + buf[i + 1] = 0; + } + + return strlen(buf); +} + +#define DEVICE_ATTR_SYS_INFO_STR(field) \ +static ssize_t field ## _show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct switchtec_dev *stdev = to_stdev(dev); \ + return io_string_show(buf, &stdev->mmio_sys_info->field, \ + sizeof(stdev->mmio_sys_info->field)); \ +} \ +\ +static DEVICE_ATTR_RO(field) + +DEVICE_ATTR_SYS_INFO_STR(vendor_id); +DEVICE_ATTR_SYS_INFO_STR(product_id); +DEVICE_ATTR_SYS_INFO_STR(product_revision); +DEVICE_ATTR_SYS_INFO_STR(component_vendor); + +static ssize_t component_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + int id = ioread16(&stdev->mmio_sys_info->component_id); + + return sprintf(buf, "PM%04X\n", id); +} +static DEVICE_ATTR_RO(component_id); + +static ssize_t component_revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + int rev = ioread8(&stdev->mmio_sys_info->component_revision); + + return sprintf(buf, "%d\n", rev); +} +static DEVICE_ATTR_RO(component_revision); + +static ssize_t partition_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + + return sprintf(buf, "%d\n", stdev->partition); +} +static DEVICE_ATTR_RO(partition); + +static ssize_t partition_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + + return sprintf(buf, "%d\n", stdev->partition_count); +} +static DEVICE_ATTR_RO(partition_count); + +static struct attribute *switchtec_device_attrs[] = { + &dev_attr_device_version.attr, + &dev_attr_fw_version.attr, + &dev_attr_vendor_id.attr, + &dev_attr_product_id.attr, + &dev_attr_product_revision.attr, + &dev_attr_component_vendor.attr, + &dev_attr_component_id.attr, + &dev_attr_component_revision.attr, + &dev_attr_partition.attr, + &dev_attr_partition_count.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(switchtec_device); + static int switchtec_dev_open(struct inode *inode, struct file *filp) { struct switchtec_dev *stdev; @@ -734,6 +846,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev) device_initialize(dev); dev->class = switchtec_class; dev->parent = &pdev->dev; + dev->groups = switchtec_device_groups; dev->release = stdev_release; minor = ida_simple_get(&switchtec_minor_ida, 0, 0, -- cgit v1.2.3 From 52eabba5bcdb2853dec6ef007ba427b092034738 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Thu, 2 Mar 2017 16:24:34 -0700 Subject: switchtec: Add IOCTLs to the Switchtec driver Add a couple of special IOCTLs to: * Inform userspace of firmware partition locations * Pass event counts and allow userspace to wait on events * Translate PFF numbers used by the switch to port numbers [Dan Carpenter : fix off-by-one in ioctl_event_ctl()] Tested-by: Krishna Dhulipala Signed-off-by: Logan Gunthorpe Signed-off-by: Stephen Bates Signed-off-by: Bjorn Helgaas Reviewed-by: Wei Zhang Reviewed-by: Jens Axboe --- Documentation/ioctl/ioctl-number.txt | 1 + Documentation/switchtec.txt | 27 ++ MAINTAINERS | 1 + drivers/pci/switch/switchtec.c | 481 +++++++++++++++++++++++++++++++++++ include/uapi/linux/switchtec_ioctl.h | 132 ++++++++++ 5 files changed, 642 insertions(+) create mode 100644 include/uapi/linux/switchtec_ioctl.h (limited to 'Documentation') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 08244bea5048..0682bd3eaa8a 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -191,6 +191,7 @@ Code Seq#(hex) Include File Comments 'W' 00-1F linux/watchdog.h conflict! 'W' 00-1F linux/wanrouter.h conflict! (pre 3.9) 'W' 00-3F sound/asound.h conflict! +'W' 40-5F drivers/pci/switch/switchtec.c 'X' all fs/xfs/xfs_fs.h conflict! and fs/xfs/linux-2.6/xfs_ioctl32.h and include/linux/falloc.h diff --git a/Documentation/switchtec.txt b/Documentation/switchtec.txt index 4bced4c78446..a0a9c7b3d4d5 100644 --- a/Documentation/switchtec.txt +++ b/Documentation/switchtec.txt @@ -51,3 +51,30 @@ The char device has the following semantics: * The poll call will also be supported for userspace applications that need to do other things while waiting for the command to complete. + +The following IOCTLs are also supported by the device: + +* SWITCHTEC_IOCTL_FLASH_INFO - Retrieve firmware length and number + of partitions in the device. + +* SWITCHTEC_IOCTL_FLASH_PART_INFO - Retrieve address and lengeth for + any specified partition in flash. + +* SWITCHTEC_IOCTL_EVENT_SUMMARY - Read a structure of bitmaps + indicating all uncleared events. + +* SWITCHTEC_IOCTL_EVENT_CTL - Get the current count, clear and set flags + for any event. This ioctl takes in a switchtec_ioctl_event_ctl struct + with the event_id, index and flags set (index being the partition or PFF + number for non-global events). It returns whether the event has + occurred, the number of times and any event specific data. The flags + can be used to clear the count or enable and disable actions to + happen when the event occurs. + By using the SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL flag, + you can set an event to trigger a poll command to return with + POLLPRI. In this way, userspace can wait for events to occur. + +* SWITCHTEC_IOCTL_PFF_TO_PORT and SWITCHTEC_IOCTL_PORT_TO_PFF convert + between PCI Function Framework number (used by the event system) + and Switchtec Logic Port ID and Partition number (which is more + user friendly). diff --git a/MAINTAINERS b/MAINTAINERS index 76ccc5a805dd..3744019a8853 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9664,6 +9664,7 @@ S: Maintained F: Documentation/switchtec.txt F: Documentation/ABI/testing/sysfs-class-switchtec F: drivers/pci/switch/switchtec* +F: include/uapi/linux/switchtec_ioctl.h PCI DRIVER FOR NVIDIA TEGRA M: Thierry Reding diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 15ff61c7718b..cc6e085008fb 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -13,6 +13,8 @@ * */ +#include + #include #include #include @@ -778,6 +780,431 @@ static unsigned int switchtec_dev_poll(struct file *filp, poll_table *wait) return ret; } +static int ioctl_flash_info(struct switchtec_dev *stdev, + struct switchtec_ioctl_flash_info __user *uinfo) +{ + struct switchtec_ioctl_flash_info info = {0}; + struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; + + info.flash_length = ioread32(&fi->flash_length); + info.num_partitions = SWITCHTEC_IOCTL_NUM_PARTITIONS; + + if (copy_to_user(uinfo, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info, + struct partition_info __iomem *pi) +{ + info->address = ioread32(&pi->address); + info->length = ioread32(&pi->length); +} + +static int ioctl_flash_part_info(struct switchtec_dev *stdev, + struct switchtec_ioctl_flash_part_info __user *uinfo) +{ + struct switchtec_ioctl_flash_part_info info = {0}; + struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; + u32 active_addr = -1; + + if (copy_from_user(&info, uinfo, sizeof(info))) + return -EFAULT; + + switch (info.flash_partition) { + case SWITCHTEC_IOCTL_PART_CFG0: + active_addr = ioread32(&fi->active_cfg); + set_fw_info_part(&info, &fi->cfg0); + break; + case SWITCHTEC_IOCTL_PART_CFG1: + active_addr = ioread32(&fi->active_cfg); + set_fw_info_part(&info, &fi->cfg1); + break; + case SWITCHTEC_IOCTL_PART_IMG0: + active_addr = ioread32(&fi->active_img); + set_fw_info_part(&info, &fi->img0); + break; + case SWITCHTEC_IOCTL_PART_IMG1: + active_addr = ioread32(&fi->active_img); + set_fw_info_part(&info, &fi->img1); + break; + case SWITCHTEC_IOCTL_PART_NVLOG: + set_fw_info_part(&info, &fi->nvlog); + break; + case SWITCHTEC_IOCTL_PART_VENDOR0: + set_fw_info_part(&info, &fi->vendor[0]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR1: + set_fw_info_part(&info, &fi->vendor[1]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR2: + set_fw_info_part(&info, &fi->vendor[2]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR3: + set_fw_info_part(&info, &fi->vendor[3]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR4: + set_fw_info_part(&info, &fi->vendor[4]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR5: + set_fw_info_part(&info, &fi->vendor[5]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR6: + set_fw_info_part(&info, &fi->vendor[6]); + break; + case SWITCHTEC_IOCTL_PART_VENDOR7: + set_fw_info_part(&info, &fi->vendor[7]); + break; + default: + return -EINVAL; + } + + if (info.address == active_addr) + info.active = 1; + + if (copy_to_user(uinfo, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int ioctl_event_summary(struct switchtec_dev *stdev, + struct switchtec_user *stuser, + struct switchtec_ioctl_event_summary __user *usum) +{ + struct switchtec_ioctl_event_summary s = {0}; + int i; + u32 reg; + + s.global = ioread32(&stdev->mmio_sw_event->global_summary); + s.part_bitmap = ioread32(&stdev->mmio_sw_event->part_event_bitmap); + s.local_part = ioread32(&stdev->mmio_part_cfg->part_event_summary); + + for (i = 0; i < stdev->partition_count; i++) { + reg = ioread32(&stdev->mmio_part_cfg_all[i].part_event_summary); + s.part[i] = reg; + } + + for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) { + reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id); + if (reg != MICROSEMI_VENDOR_ID) + break; + + reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary); + s.pff[i] = reg; + } + + if (copy_to_user(usum, &s, sizeof(s))) + return -EFAULT; + + stuser->event_cnt = atomic_read(&stdev->event_cnt); + + return 0; +} + +static u32 __iomem *global_ev_reg(struct switchtec_dev *stdev, + size_t offset, int index) +{ + return (void __iomem *)stdev->mmio_sw_event + offset; +} + +static u32 __iomem *part_ev_reg(struct switchtec_dev *stdev, + size_t offset, int index) +{ + return (void __iomem *)&stdev->mmio_part_cfg_all[index] + offset; +} + +static u32 __iomem *pff_ev_reg(struct switchtec_dev *stdev, + size_t offset, int index) +{ + return (void __iomem *)&stdev->mmio_pff_csr[index] + offset; +} + +#define EV_GLB(i, r)[i] = {offsetof(struct sw_event_regs, r), global_ev_reg} +#define EV_PAR(i, r)[i] = {offsetof(struct part_cfg_regs, r), part_ev_reg} +#define EV_PFF(i, r)[i] = {offsetof(struct pff_csr_regs, r), pff_ev_reg} + +const struct event_reg { + size_t offset; + u32 __iomem *(*map_reg)(struct switchtec_dev *stdev, + size_t offset, int index); +} event_regs[] = { + EV_GLB(SWITCHTEC_IOCTL_EVENT_STACK_ERROR, stack_error_event_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_PPU_ERROR, ppu_error_event_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_ISP_ERROR, isp_error_event_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_SYS_RESET, sys_reset_event_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_EXC, fw_exception_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NMI, fw_nmi_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NON_FATAL, fw_non_fatal_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_FATAL, fw_fatal_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP, twi_mrpc_comp_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP_ASYNC, + twi_mrpc_comp_async_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP, cli_mrpc_comp_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP_ASYNC, + cli_mrpc_comp_async_hdr), + EV_GLB(SWITCHTEC_IOCTL_EVENT_GPIO_INT, gpio_interrupt_hdr), + EV_PAR(SWITCHTEC_IOCTL_EVENT_PART_RESET, part_reset_hdr), + EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP, mrpc_comp_hdr), + EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC, mrpc_comp_async_hdr), + EV_PAR(SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP, dyn_binding_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_P2P, aer_in_p2p_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_VEP, aer_in_vep_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_DPC, dpc_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_CTS, cts_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_HOTPLUG, hotplug_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_IER, ier_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_THRESH, threshold_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_POWER_MGMT, power_mgmt_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_TLP_THROTTLING, tlp_throttling_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_FORCE_SPEED, force_speed_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT, credit_timeout_hdr), + EV_PFF(SWITCHTEC_IOCTL_EVENT_LINK_STATE, link_state_hdr), +}; + +static u32 __iomem *event_hdr_addr(struct switchtec_dev *stdev, + int event_id, int index) +{ + size_t off; + + if (event_id < 0 || event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) + return ERR_PTR(-EINVAL); + + off = event_regs[event_id].offset; + + if (event_regs[event_id].map_reg == part_ev_reg) { + if (index == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) + index = stdev->partition; + else if (index < 0 || index >= stdev->partition_count) + return ERR_PTR(-EINVAL); + } else if (event_regs[event_id].map_reg == pff_ev_reg) { + if (index < 0 || index >= stdev->pff_csr_count) + return ERR_PTR(-EINVAL); + } + + return event_regs[event_id].map_reg(stdev, off, index); +} + +static int event_ctl(struct switchtec_dev *stdev, + struct switchtec_ioctl_event_ctl *ctl) +{ + int i; + u32 __iomem *reg; + u32 hdr; + + reg = event_hdr_addr(stdev, ctl->event_id, ctl->index); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + hdr = ioread32(reg); + for (i = 0; i < ARRAY_SIZE(ctl->data); i++) + ctl->data[i] = ioread32(®[i + 1]); + + ctl->occurred = hdr & SWITCHTEC_EVENT_OCCURRED; + ctl->count = (hdr >> 5) & 0xFF; + + if (!(ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR)) + hdr &= ~SWITCHTEC_EVENT_CLEAR; + if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL) + hdr |= SWITCHTEC_EVENT_EN_IRQ; + if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL) + hdr &= ~SWITCHTEC_EVENT_EN_IRQ; + if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG) + hdr |= SWITCHTEC_EVENT_EN_LOG; + if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG) + hdr &= ~SWITCHTEC_EVENT_EN_LOG; + if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI) + hdr |= SWITCHTEC_EVENT_EN_CLI; + if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI) + hdr &= ~SWITCHTEC_EVENT_EN_CLI; + if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL) + hdr |= SWITCHTEC_EVENT_FATAL; + if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL) + hdr &= ~SWITCHTEC_EVENT_FATAL; + + if (ctl->flags) + iowrite32(hdr, reg); + + ctl->flags = 0; + if (hdr & SWITCHTEC_EVENT_EN_IRQ) + ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL; + if (hdr & SWITCHTEC_EVENT_EN_LOG) + ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG; + if (hdr & SWITCHTEC_EVENT_EN_CLI) + ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI; + if (hdr & SWITCHTEC_EVENT_FATAL) + ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL; + + return 0; +} + +static int ioctl_event_ctl(struct switchtec_dev *stdev, + struct switchtec_ioctl_event_ctl __user *uctl) +{ + int ret; + int nr_idxs; + struct switchtec_ioctl_event_ctl ctl; + + if (copy_from_user(&ctl, uctl, sizeof(ctl))) + return -EFAULT; + + if (ctl.event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) + return -EINVAL; + + if (ctl.flags & SWITCHTEC_IOCTL_EVENT_FLAG_UNUSED) + return -EINVAL; + + if (ctl.index == SWITCHTEC_IOCTL_EVENT_IDX_ALL) { + if (event_regs[ctl.event_id].map_reg == global_ev_reg) + nr_idxs = 1; + else if (event_regs[ctl.event_id].map_reg == part_ev_reg) + nr_idxs = stdev->partition_count; + else if (event_regs[ctl.event_id].map_reg == pff_ev_reg) + nr_idxs = stdev->pff_csr_count; + else + return -EINVAL; + + for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) { + ret = event_ctl(stdev, &ctl); + if (ret < 0) + return ret; + } + } else { + ret = event_ctl(stdev, &ctl); + if (ret < 0) + return ret; + } + + if (copy_to_user(uctl, &ctl, sizeof(ctl))) + return -EFAULT; + + return 0; +} + +static int ioctl_pff_to_port(struct switchtec_dev *stdev, + struct switchtec_ioctl_pff_port *up) +{ + int i, part; + u32 reg; + struct part_cfg_regs *pcfg; + struct switchtec_ioctl_pff_port p; + + if (copy_from_user(&p, up, sizeof(p))) + return -EFAULT; + + p.port = -1; + for (part = 0; part < stdev->partition_count; part++) { + pcfg = &stdev->mmio_part_cfg_all[part]; + p.partition = part; + + reg = ioread32(&pcfg->usp_pff_inst_id); + if (reg == p.pff) { + p.port = 0; + break; + } + + reg = ioread32(&pcfg->vep_pff_inst_id); + if (reg == p.pff) { + p.port = SWITCHTEC_IOCTL_PFF_VEP; + break; + } + + for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) { + reg = ioread32(&pcfg->dsp_pff_inst_id[i]); + if (reg != p.pff) + continue; + + p.port = i + 1; + break; + } + + if (p.port != -1) + break; + } + + if (copy_to_user(up, &p, sizeof(p))) + return -EFAULT; + + return 0; +} + +static int ioctl_port_to_pff(struct switchtec_dev *stdev, + struct switchtec_ioctl_pff_port *up) +{ + struct switchtec_ioctl_pff_port p; + struct part_cfg_regs *pcfg; + + if (copy_from_user(&p, up, sizeof(p))) + return -EFAULT; + + if (p.partition == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) + pcfg = stdev->mmio_part_cfg; + else if (p.partition < stdev->partition_count) + pcfg = &stdev->mmio_part_cfg_all[p.partition]; + else + return -EINVAL; + + switch (p.port) { + case 0: + p.pff = ioread32(&pcfg->usp_pff_inst_id); + break; + case SWITCHTEC_IOCTL_PFF_VEP: + p.pff = ioread32(&pcfg->vep_pff_inst_id); + break; + default: + if (p.port > ARRAY_SIZE(pcfg->dsp_pff_inst_id)) + return -EINVAL; + p.pff = ioread32(&pcfg->dsp_pff_inst_id[p.port - 1]); + break; + } + + if (copy_to_user(up, &p, sizeof(p))) + return -EFAULT; + + return 0; +} + +static long switchtec_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct switchtec_user *stuser = filp->private_data; + struct switchtec_dev *stdev = stuser->stdev; + int rc; + void __user *argp = (void __user *)arg; + + rc = lock_mutex_and_test_alive(stdev); + if (rc) + return rc; + + switch (cmd) { + case SWITCHTEC_IOCTL_FLASH_INFO: + rc = ioctl_flash_info(stdev, argp); + break; + case SWITCHTEC_IOCTL_FLASH_PART_INFO: + rc = ioctl_flash_part_info(stdev, argp); + break; + case SWITCHTEC_IOCTL_EVENT_SUMMARY: + rc = ioctl_event_summary(stdev, stuser, argp); + break; + case SWITCHTEC_IOCTL_EVENT_CTL: + rc = ioctl_event_ctl(stdev, argp); + break; + case SWITCHTEC_IOCTL_PFF_TO_PORT: + rc = ioctl_pff_to_port(stdev, argp); + break; + case SWITCHTEC_IOCTL_PORT_TO_PFF: + rc = ioctl_port_to_pff(stdev, argp); + break; + default: + rc = -ENOTTY; + break; + } + + mutex_unlock(&stdev->mrpc_mutex); + return rc; +} + static const struct file_operations switchtec_fops = { .owner = THIS_MODULE, .open = switchtec_dev_open, @@ -785,6 +1212,8 @@ static const struct file_operations switchtec_fops = { .write = switchtec_dev_write, .read = switchtec_dev_read, .poll = switchtec_dev_poll, + .unlocked_ioctl = switchtec_dev_ioctl, + .compat_ioctl = switchtec_dev_ioctl, }; static void stdev_release(struct device *dev) @@ -871,11 +1300,52 @@ err_put: return ERR_PTR(rc); } +static int mask_event(struct switchtec_dev *stdev, int eid, int idx) +{ + size_t off = event_regs[eid].offset; + u32 __iomem *hdr_reg; + u32 hdr; + + hdr_reg = event_regs[eid].map_reg(stdev, off, idx); + hdr = ioread32(hdr_reg); + + if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ)) + return 0; + + dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr); + hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED); + iowrite32(hdr, hdr_reg); + + return 1; +} + +static int mask_all_events(struct switchtec_dev *stdev, int eid) +{ + int idx; + int count = 0; + + if (event_regs[eid].map_reg == part_ev_reg) { + for (idx = 0; idx < stdev->partition_count; idx++) + count += mask_event(stdev, eid, idx); + } else if (event_regs[eid].map_reg == pff_ev_reg) { + for (idx = 0; idx < stdev->pff_csr_count; idx++) { + if (!stdev->pff_local[idx]) + continue; + count += mask_event(stdev, eid, idx); + } + } else { + count += mask_event(stdev, eid, 0); + } + + return count; +} + static irqreturn_t switchtec_event_isr(int irq, void *dev) { struct switchtec_dev *stdev = dev; u32 reg; irqreturn_t ret = IRQ_NONE; + int eid, event_count = 0; reg = ioread32(&stdev->mmio_part_cfg->mrpc_comp_hdr); if (reg & SWITCHTEC_EVENT_OCCURRED) { @@ -885,6 +1355,17 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev) iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr); } + for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) + event_count += mask_all_events(stdev, eid); + + if (event_count) { + atomic_inc(&stdev->event_cnt); + wake_up_interruptible(&stdev->event_wq); + dev_dbg(&stdev->dev, "%s: %d events\n", __func__, + event_count); + return IRQ_HANDLED; + } + return ret; } diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h new file mode 100644 index 000000000000..3e824e1a6495 --- /dev/null +++ b/include/uapi/linux/switchtec_ioctl.h @@ -0,0 +1,132 @@ +/* + * Microsemi Switchtec PCIe Driver + * Copyright (c) 2017, Microsemi Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _UAPI_LINUX_SWITCHTEC_IOCTL_H +#define _UAPI_LINUX_SWITCHTEC_IOCTL_H + +#include + +#define SWITCHTEC_IOCTL_PART_CFG0 0 +#define SWITCHTEC_IOCTL_PART_CFG1 1 +#define SWITCHTEC_IOCTL_PART_IMG0 2 +#define SWITCHTEC_IOCTL_PART_IMG1 3 +#define SWITCHTEC_IOCTL_PART_NVLOG 4 +#define SWITCHTEC_IOCTL_PART_VENDOR0 5 +#define SWITCHTEC_IOCTL_PART_VENDOR1 6 +#define SWITCHTEC_IOCTL_PART_VENDOR2 7 +#define SWITCHTEC_IOCTL_PART_VENDOR3 8 +#define SWITCHTEC_IOCTL_PART_VENDOR4 9 +#define SWITCHTEC_IOCTL_PART_VENDOR5 10 +#define SWITCHTEC_IOCTL_PART_VENDOR6 11 +#define SWITCHTEC_IOCTL_PART_VENDOR7 12 +#define SWITCHTEC_IOCTL_NUM_PARTITIONS 13 + +struct switchtec_ioctl_flash_info { + __u64 flash_length; + __u32 num_partitions; + __u32 padding; +}; + +struct switchtec_ioctl_flash_part_info { + __u32 flash_partition; + __u32 address; + __u32 length; + __u32 active; +}; + +struct switchtec_ioctl_event_summary { + __u64 global; + __u64 part_bitmap; + __u32 local_part; + __u32 padding; + __u32 part[48]; + __u32 pff[48]; +}; + +#define SWITCHTEC_IOCTL_EVENT_STACK_ERROR 0 +#define SWITCHTEC_IOCTL_EVENT_PPU_ERROR 1 +#define SWITCHTEC_IOCTL_EVENT_ISP_ERROR 2 +#define SWITCHTEC_IOCTL_EVENT_SYS_RESET 3 +#define SWITCHTEC_IOCTL_EVENT_FW_EXC 4 +#define SWITCHTEC_IOCTL_EVENT_FW_NMI 5 +#define SWITCHTEC_IOCTL_EVENT_FW_NON_FATAL 6 +#define SWITCHTEC_IOCTL_EVENT_FW_FATAL 7 +#define SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP 8 +#define SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP_ASYNC 9 +#define SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP 10 +#define SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP_ASYNC 11 +#define SWITCHTEC_IOCTL_EVENT_GPIO_INT 12 +#define SWITCHTEC_IOCTL_EVENT_PART_RESET 13 +#define SWITCHTEC_IOCTL_EVENT_MRPC_COMP 14 +#define SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC 15 +#define SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP 16 +#define SWITCHTEC_IOCTL_EVENT_AER_IN_P2P 17 +#define SWITCHTEC_IOCTL_EVENT_AER_IN_VEP 18 +#define SWITCHTEC_IOCTL_EVENT_DPC 19 +#define SWITCHTEC_IOCTL_EVENT_CTS 20 +#define SWITCHTEC_IOCTL_EVENT_HOTPLUG 21 +#define SWITCHTEC_IOCTL_EVENT_IER 22 +#define SWITCHTEC_IOCTL_EVENT_THRESH 23 +#define SWITCHTEC_IOCTL_EVENT_POWER_MGMT 24 +#define SWITCHTEC_IOCTL_EVENT_TLP_THROTTLING 25 +#define SWITCHTEC_IOCTL_EVENT_FORCE_SPEED 26 +#define SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT 27 +#define SWITCHTEC_IOCTL_EVENT_LINK_STATE 28 +#define SWITCHTEC_IOCTL_MAX_EVENTS 29 + +#define SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX -1 +#define SWITCHTEC_IOCTL_EVENT_IDX_ALL -2 + +#define SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR (1 << 0) +#define SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL (1 << 1) +#define SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG (1 << 2) +#define SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI (1 << 3) +#define SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL (1 << 4) +#define SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL (1 << 5) +#define SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG (1 << 6) +#define SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI (1 << 7) +#define SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL (1 << 8) +#define SWITCHTEC_IOCTL_EVENT_FLAG_UNUSED (~0x1ff) + +struct switchtec_ioctl_event_ctl { + __u32 event_id; + __s32 index; + __u32 flags; + __u32 occurred; + __u32 count; + __u32 data[5]; +}; + +#define SWITCHTEC_IOCTL_PFF_VEP 100 +struct switchtec_ioctl_pff_port { + __u32 pff; + __u32 partition; + __u32 port; +}; + +#define SWITCHTEC_IOCTL_FLASH_INFO \ + _IOR('W', 0x40, struct switchtec_ioctl_flash_info) +#define SWITCHTEC_IOCTL_FLASH_PART_INFO \ + _IOWR('W', 0x41, struct switchtec_ioctl_flash_part_info) +#define SWITCHTEC_IOCTL_EVENT_SUMMARY \ + _IOR('W', 0x42, struct switchtec_ioctl_event_summary) +#define SWITCHTEC_IOCTL_EVENT_CTL \ + _IOWR('W', 0x43, struct switchtec_ioctl_event_ctl) +#define SWITCHTEC_IOCTL_PFF_TO_PORT \ + _IOWR('W', 0x44, struct switchtec_ioctl_pff_port) +#define SWITCHTEC_IOCTL_PORT_TO_PFF \ + _IOWR('W', 0x45, struct switchtec_ioctl_pff_port) + +#endif -- cgit v1.2.3 From ae749c7ab475de2c9c427058db19921c91846e89 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:54 +0100 Subject: PCI: Add arch_can_pci_mmap_wc() macro Most of the almost-identical versions of pci_mmap_page_range() silently ignore the 'write_combine' argument and give uncached mappings. Yet we allow the PCIIOC_WRITE_COMBINE ioctl in /proc/bus/pci, expose the 'resourceX_wc' file in sysfs, and allow an attempted mapping to apparently succeed. To fix this, introduce a macro arch_can_pci_mmap_wc() which indicates whether the platform can do a write-combining mapping. On x86 this ends up being pat_enabled(), while the few other platforms that support it can just set it to a literal '1'. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- Documentation/filesystems/sysfs-pci.txt | 4 ++++ arch/ia64/include/asm/pci.h | 2 ++ arch/powerpc/include/asm/pci.h | 5 +++-- arch/x86/include/asm/pci.h | 2 ++ drivers/pci/pci-sysfs.c | 4 ++-- drivers/pci/proc.c | 15 ++++++++------- include/linux/pci.h | 4 ++++ 7 files changed, 25 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 6ea1ceda6f52..25b7f1c1b868 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -117,6 +117,10 @@ code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function. Platforms are free to only support subsets of the mmap functionality, but useful return codes should be provided. +Platforms which support write-combining maps of PCI resources must define +arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when +write-combining is permitted. + Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms wishing to support legacy functionality should define it and provide pci_legacy_read, pci_legacy_write and pci_mmap_legacy_page_range functions. diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index c0835b0dc722..6283758474ad 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -51,6 +51,8 @@ extern unsigned long ia64_max_iommu_merge_mask; #define PCI_DMA_BUS_IS_PHYS (ia64_max_iommu_merge_mask == ~0UL) #define HAVE_PCI_MMAP +#define arch_can_pci_mmap_wc() 1 + extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); #define HAVE_PCI_LEGACY diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 93eded8d3843..b5b68c6a10b1 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -81,8 +81,9 @@ struct vm_area_struct; int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); -/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ -#define HAVE_PCI_MMAP 1 +/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() and it does WC */ +#define HAVE_PCI_MMAP 1 +#define arch_can_pci_mmap_wc() 1 extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t count); diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 1411dbed5e5e..f6e22c271efa 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #ifdef __KERNEL__ @@ -102,6 +103,7 @@ int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); #define HAVE_PCI_MMAP +#define arch_can_pci_mmap_wc() pat_enabled() extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7ac258fd3c5c..7d494bd66a97 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1211,9 +1211,9 @@ static int pci_create_resource_files(struct pci_dev *pdev) retval = pci_create_attr(pdev, i, 0); /* for prefetchable resources, create a WC mappable file */ - if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH) + if (!retval && arch_can_pci_mmap_wc() && + pdev->resource[i].flags & IORESOURCE_PREFETCH) retval = pci_create_attr(pdev, i, 1); - if (retval) { pci_remove_resource_files(pdev); return retval; diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index dc8912e2d4a1..a2aa58a8fb96 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -210,14 +210,15 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, break; case PCIIOC_WRITE_COMBINE: - if (arg) - fpriv->write_combine = 1; - else - fpriv->write_combine = 0; - break; - + if (arch_can_pci_mmap_wc()) { + if (arg) + fpriv->write_combine = 1; + else + fpriv->write_combine = 0; + break; + } + /* If arch decided it can't, fall through... */ #endif /* HAVE_PCI_MMAP */ - default: ret = -EINVAL; break; diff --git a/include/linux/pci.h b/include/linux/pci.h index eb3da1a04e6c..e614fb42d8bb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1626,6 +1626,10 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } #include +#ifndef arch_can_pci_mmap_wc +#define arch_can_pci_mmap_wc() 0 +#endif + #ifndef pci_root_bus_fwnode #define pci_root_bus_fwnode(bus) NULL #endif -- cgit v1.2.3 From e854d8b2a82ef76521ad2bed68211fde0511d417 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:56 +0100 Subject: PCI: Add arch_can_pci_mmap_io() on architectures which can mmap() I/O space This is relatively esoteric, and knowing that we don't have it makes life easier in some cases rather than just an eventual -EINVAL from pci_mmap_page_range(). Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- Documentation/filesystems/sysfs-pci.txt | 3 ++- arch/microblaze/include/asm/pci.h | 3 ++- arch/powerpc/include/asm/pci.h | 1 + arch/sparc/include/asm/pci_64.h | 1 + arch/xtensa/include/asm/pci.h | 3 ++- drivers/pci/pci-sysfs.c | 13 ++++++++----- drivers/pci/proc.c | 11 +++++++---- include/linux/pci.h | 3 +++ 8 files changed, 26 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 25b7f1c1b868..46b95d82c4fd 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -119,7 +119,8 @@ useful return codes should be provided. Platforms which support write-combining maps of PCI resources must define arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when -write-combining is permitted. +write-combining is permitted. Platforms which support maps of I/O resources +define arch_can_pci_mmap_io() similarly. Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms wishing to support legacy functionality should define it and provide diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h index ab381d283fb9..efd4983cb697 100644 --- a/arch/microblaze/include/asm/pci.h +++ b/arch/microblaze/include/asm/pci.h @@ -48,7 +48,8 @@ extern int pci_proc_domain(struct pci_bus *bus); struct vm_area_struct; /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ -#define HAVE_PCI_MMAP 1 +#define HAVE_PCI_MMAP 1 +#define arch_can_pci_mmap_io() 1 extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t count); diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 55887d171205..c8975dac535f 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -80,6 +80,7 @@ struct vm_area_struct; /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() and it does WC */ #define HAVE_PCI_MMAP 1 +#define arch_can_pci_mmap_io() 1 #define arch_can_pci_mmap_wc() 1 extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, diff --git a/arch/sparc/include/asm/pci_64.h b/arch/sparc/include/asm/pci_64.h index 516fda70a1bb..b957ca5527a3 100644 --- a/arch/sparc/include/asm/pci_64.h +++ b/arch/sparc/include/asm/pci_64.h @@ -42,6 +42,7 @@ static inline int pci_proc_domain(struct pci_bus *bus) /* Platform support for /proc/bus/pci/X/Y mmap()s. */ #define HAVE_PCI_MMAP +#define arch_can_pci_mmap_io() 1 #define HAVE_ARCH_PCI_GET_UNMAPPED_AREA #define get_pci_unmapped_area get_fb_unmapped_area diff --git a/arch/xtensa/include/asm/pci.h b/arch/xtensa/include/asm/pci.h index bb5510b28754..e4f366a488d3 100644 --- a/arch/xtensa/include/asm/pci.h +++ b/arch/xtensa/include/asm/pci.h @@ -47,7 +47,8 @@ struct pci_dev; #define PCI_DMA_BUS_IS_PHYS (1) /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ -#define HAVE_PCI_MMAP 1 +#define HAVE_PCI_MMAP 1 +#define arch_can_pci_mmap_io() 1 #endif /* __KERNEL__ */ diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7d494bd66a97..cb04bc29943f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1174,11 +1174,14 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) } else { pdev->res_attr[num] = res_attr; sprintf(res_attr_name, "resource%d", num); - res_attr->mmap = pci_mmap_resource_uc; - } - if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { - res_attr->read = pci_read_resource_io; - res_attr->write = pci_write_resource_io; + if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { + res_attr->read = pci_read_resource_io; + res_attr->write = pci_write_resource_io; + if (arch_can_pci_mmap_io()) + res_attr->mmap = pci_mmap_resource_uc; + } else { + res_attr->mmap = pci_mmap_resource_uc; + } } res_attr->attr.name = res_attr_name; res_attr->attr.mode = S_IRUSR | S_IWUSR; diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index a2aa58a8fb96..45e5cf7e9193 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -202,6 +202,8 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, #ifdef HAVE_PCI_MMAP case PCIIOC_MMAP_IS_IO: + if (!arch_can_pci_mmap_io()) + return -EINVAL; fpriv->mmap_state = pci_mmap_io; break; @@ -232,15 +234,16 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) { struct pci_dev *dev = PDE_DATA(file_inode(file)); struct pci_filp_private *fpriv = file->private_data; - int i, ret, write_combine = 0, res_bit; + int i, ret, write_combine = 0, res_bit = IORESOURCE_MEM; if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (fpriv->mmap_state == pci_mmap_io) + if (fpriv->mmap_state == pci_mmap_io) { + if (!arch_can_pci_mmap_io()) + return -EINVAL; res_bit = IORESOURCE_IO; - else - res_bit = IORESOURCE_MEM; + } /* Make sure the caller is mapping a real resource for this device */ for (i = 0; i < PCI_ROM_RESOURCE; i++) { diff --git a/include/linux/pci.h b/include/linux/pci.h index e7bb4b62cc97..590cfcf6acf5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1636,6 +1636,9 @@ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, #ifndef arch_can_pci_mmap_wc #define arch_can_pci_mmap_wc() 0 #endif +#ifndef arch_can_pci_mmap_io +#define arch_can_pci_mmap_io() 0 +#endif #ifndef pci_root_bus_fwnode #define pci_root_bus_fwnode(bus) NULL -- cgit v1.2.3 From f719582435afe9c7985206e42d804ea6aa315d33 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:59 +0100 Subject: PCI: Add pci_mmap_resource_range() and use it for ARM64 Starting to leave behind the legacy of the pci_mmap_page_range() interface which takes "user-visible" BAR addresses. This takes just the resource and offset. For now, both APIs coexist and depending on the platform, one is implemented as a wrapper around the other. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- Documentation/filesystems/sysfs-pci.txt | 10 ++-- arch/arm64/include/asm/pci.h | 2 + drivers/pci/Makefile | 2 +- drivers/pci/mmap.c | 95 +++++++++++++++++++++++++++++++++ drivers/pci/pci-sysfs.c | 13 ++--- drivers/pci/pci.h | 4 +- include/linux/pci.h | 19 +++++-- 7 files changed, 125 insertions(+), 20 deletions(-) create mode 100644 drivers/pci/mmap.c (limited to 'Documentation') diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 46b95d82c4fd..06f1d64c6f70 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -113,9 +113,13 @@ Supporting PCI access on new platforms -------------------------------------- In order to support PCI resource mapping as described above, Linux platform -code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function. -Platforms are free to only support subsets of the mmap functionality, but -useful return codes should be provided. +code should ideally define ARCH_GENERIC_PCI_MMAP_RESOURCE and use the generic +implementation of that functionality. To support the historical interface of +mmap() through files in /proc/bus/pci, platforms may also set HAVE_PCI_MMAP. + +Alternatively, platforms which set HAVE_PCI_MMAP may provide their own +implementation of pci_mmap_page_range() instead of defining +ARCH_GENERIC_PCI_MMAP_RESOURCE. Platforms which support write-combining maps of PCI resources must define arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h index b9a7ba9ca44c..1fc19744ffe9 100644 --- a/arch/arm64/include/asm/pci.h +++ b/arch/arm64/include/asm/pci.h @@ -22,6 +22,8 @@ */ #define PCI_DMA_BUS_IS_PHYS (0) +#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1 + extern int isa_dma_bridge_buggy; #ifdef CONFIG_PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 8db5079f09a7..3d40e415a171 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -4,7 +4,7 @@ obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ - irq.o vpd.o setup-bus.o vc.o + irq.o vpd.o setup-bus.o vc.o mmap.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSFS) += slot.o diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c new file mode 100644 index 000000000000..b7aca9c89c31 --- /dev/null +++ b/drivers/pci/mmap.c @@ -0,0 +1,95 @@ +/* + * mmap.c — generic PCI resource mmap helper + * + * Copyright © 2017 Amazon.com, Inc. or its affiliates. + * + * Author: David Woodhouse + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#ifdef ARCH_GENERIC_PCI_MMAP_RESOURCE + +/* + * Modern setup: generic pci_mmap_resource_range(), and implement the legacy + * pci_mmap_page_range() (if needed) as a wrapper round it. + */ + +#ifdef HAVE_PCI_MMAP +int pci_mmap_page_range(struct pci_dev *pdev, int bar, + struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + resource_size_t start, end; + + pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end); + + /* Adjust vm_pgoff to be the offset within the resource */ + vma->vm_pgoff -= start >> PAGE_SHIFT; + return pci_mmap_resource_range(pdev, bar, vma, mmap_state, + write_combine); +} +#endif + +static const struct vm_operations_struct pci_phys_vm_ops = { +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys, +#endif +}; + +int pci_mmap_resource_range(struct pci_dev *pdev, int bar, + struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + unsigned long size; + + if (mmap_state == pci_mmap_io) + return -EINVAL; + + size = ((pci_resource_len(pdev, bar) - 1) >> PAGE_SHIFT) + 1; + if (vma->vm_pgoff + vma_pages(vma) > size) + return -EINVAL; + + if (write_combine) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_device(vma->vm_page_prot); + + vma->vm_pgoff += (pci_resource_start(pdev, bar) >> PAGE_SHIFT); + vma->vm_ops = &pci_phys_vm_ops; + + return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +#elif defined(HAVE_PCI_MMAP) /* && !ARCH_GENERIC_PCI_MMAP_RESOURCE */ + +/* + * Legacy setup: Impement pci_mmap_resource_range() as a wrapper around + * the architecture's pci_mmap_page_range(), converting to "user visible" + * addresses as necessary. + */ + +int pci_mmap_resource_range(struct pci_dev *pdev, int bar, + struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + resource_size_t start, end; + + /* + * pci_mmap_page_range() expects the same kind of entry as coming + * from /proc/bus/pci/ which is a "user visible" value. If this is + * different from the resource itself, arch will do necessary fixup. + */ + pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end); + vma->vm_pgoff += start >> PAGE_SHIFT; + return pci_mmap_page_range(pdev, bar, vma, mmap_state, write_combine); +} +#endif diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index bfd9efe637c4..10feb98a2b1d 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -980,7 +980,7 @@ void pci_remove_legacy_files(struct pci_bus *b) } #endif /* HAVE_PCI_LEGACY */ -#ifdef HAVE_PCI_MMAP +#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, enum pci_mmap_api mmap_api) @@ -1019,7 +1019,6 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); int bar = (unsigned long)attr->private; enum pci_mmap_state mmap_type; - resource_size_t start, end; struct resource *res = &pdev->resource[bar]; if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) @@ -1033,15 +1032,9 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, (u64)pci_resource_len(pdev, bar)); return -EINVAL; } - - /* pci_mmap_page_range() expects the same kind of entry as coming - * from /proc/bus/pci/ which is a "user visible" value. If this is - * different from the resource itself, arch will do necessary fixup. - */ - pci_resource_to_user(pdev, bar, res, &start, &end); - vma->vm_pgoff += start >> PAGE_SHIFT; mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; - return pci_mmap_page_range(pdev, bar, vma, mmap_type, write_combine); + + return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine); } static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8dd38e69d6f2..8e5ca2dec7e7 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -21,14 +21,14 @@ void pci_create_firmware_label_files(struct pci_dev *pdev); void pci_remove_firmware_label_files(struct pci_dev *pdev); #endif void pci_cleanup_rom(struct pci_dev *dev); -#ifdef HAVE_PCI_MMAP + enum pci_mmap_api { PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices//resource */ PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/ */ }; int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai, enum pci_mmap_api mmap_api); -#endif + int pci_probe_reset_function(struct pci_dev *dev); /** diff --git a/include/linux/pci.h b/include/linux/pci.h index 7173a677d6dd..98a72abcf361 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1626,10 +1626,21 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } #include -/* Map a range of PCI memory or I/O space for a device into user space. - * Architectures provide this function if they set HAVE_PCI_MMAP, and - * it accepts the 'write_combine' argument when arch_can_pci_mmap_wc() - * evaluates to nonzero. */ +/* These two functions provide almost identical functionality. Depennding + * on the architecture, one will be implemented as a wrapper around the + * other (in drivers/pci/mmap.c). + * + * pci_mmap_resource_range() maps a specific BAR, and vm->vm_pgoff + * is expected to be an offset within that region. + * + * pci_mmap_page_range() is the legacy architecture-specific interface, + * which accepts a "user visible" resource address converted by + * pci_resource_to_user(), as used in the legacy mmap() interface in + * /proc/bus/pci/. + */ +int pci_mmap_resource_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine); int pci_mmap_page_range(struct pci_dev *pdev, int bar, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); -- cgit v1.2.3 From 0e7df22401a3dfd403b26dea62dd00e0598b538b Mon Sep 17 00:00:00 2001 From: Bodong Wang Date: Thu, 13 Apr 2017 01:51:40 +0300 Subject: PCI: Add sysfs sriov_drivers_autoprobe to control VF driver binding Sometimes it is not desirable to bind SR-IOV VFs to drivers. This can save host side resource usage by VF instances that will be assigned to VMs. Add a new PCI sysfs interface "sriov_drivers_autoprobe" to control that from the PF. To modify it, echo 0/n/N (disable probe) or 1/y/Y (enable probe) to: /sys/bus/pci/devices//sriov_drivers_autoprobe Note that this must be done before enabling VFs. The change will not take effect if VFs are already enabled. Simply, one can disable VFs by setting sriov_numvfs to 0, choose whether to probe or not, and then re-enable the VFs by restoring sriov_numvfs. [bhelgaas: changelog, ABI doc] Signed-off-by: Bodong Wang Signed-off-by: Eli Cohen Signed-off-by: Bjorn Helgaas Reviewed-by: Gavin Shan Reviewed-by: Alex Williamson --- Documentation/ABI/testing/sysfs-bus-pci | 22 ++++++++++++++++++++++ Documentation/PCI/pci-iov-howto.txt | 12 ++++++++++++ drivers/pci/iov.c | 1 + drivers/pci/pci-driver.c | 22 ++++++++++++++++++---- drivers/pci/pci-sysfs.c | 28 ++++++++++++++++++++++++++++ drivers/pci/pci.h | 1 + 6 files changed, 82 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 5a1732b78707..b728f700340d 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -301,3 +301,25 @@ Contact: Emil Velikov Description: This file contains the revision field of the the PCI device. The value comes from device config space. The file is read only. + +What: /sys/bus/pci/devices/.../sriov_drivers_autoprobe +Date: April 2017 +Contact: Bodong Wang +Description: + This file is associated with the PF of a device that + supports SR-IOV. It determines whether newly-enabled VFs + are immediately bound to a driver. It initially contains + 1, which means the kernel automatically binds VFs to a + compatible driver immediately after they are enabled. If + an application writes 0 to the file before enabling VFs, + the kernel will not bind VFs to a driver. + + A typical use case is to write 0 to this file, then enable + VFs, then assign the newly-created VFs to virtual machines. + Note that changing this file does not affect already- + enabled VFs. In this scenario, the user must first disable + the VFs, write 0 to sriov_drivers_autoprobe, then re-enable + the VFs. + + This is similar to /sys/bus/pci/drivers_autoprobe, but + affects only the VFs associated with a specific PF. diff --git a/Documentation/PCI/pci-iov-howto.txt b/Documentation/PCI/pci-iov-howto.txt index 2d91ae251982..d2a84151e99c 100644 --- a/Documentation/PCI/pci-iov-howto.txt +++ b/Documentation/PCI/pci-iov-howto.txt @@ -68,6 +68,18 @@ To disable SR-IOV capability: echo 0 > \ /sys/bus/pci/devices//sriov_numvfs +To enable auto probing VFs by a compatible driver on the host, run +command below before enabling SR-IOV capabilities. This is the +default behavior. + echo 1 > \ + /sys/bus/pci/devices//sriov_drivers_autoprobe + +To disable auto probing VFs by a compatible driver on the host, run +command below before enabling SR-IOV capabilities. Updating this +entry will not affect VFs which are already probed. + echo 0 > \ + /sys/bus/pci/devices//sriov_drivers_autoprobe + 3.2 Usage example Following piece of code illustrates the usage of the SR-IOV API. diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 2479ae876482..d9dc7363ac77 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -450,6 +450,7 @@ found: iov->total_VFs = total; iov->pgsz = pgsz; iov->self = dev; + iov->drivers_autoprobe = true; pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index afa72717a979..f99f7fe749b8 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -394,6 +394,18 @@ void __weak pcibios_free_irq(struct pci_dev *dev) { } +#ifdef CONFIG_PCI_IOV +static inline bool pci_device_can_probe(struct pci_dev *pdev) +{ + return (!pdev->is_virtfn || pdev->physfn->sriov->drivers_autoprobe); +} +#else +static inline bool pci_device_can_probe(struct pci_dev *pdev) +{ + return true; +} +#endif + static int pci_device_probe(struct device *dev) { int error; @@ -405,10 +417,12 @@ static int pci_device_probe(struct device *dev) return error; pci_dev_get(pci_dev); - error = __pci_device_probe(drv, pci_dev); - if (error) { - pcibios_free_irq(pci_dev); - pci_dev_put(pci_dev); + if (pci_device_can_probe(pci_dev)) { + error = __pci_device_probe(drv, pci_dev); + if (error) { + pcibios_free_irq(pci_dev); + pci_dev_put(pci_dev); + } } return error; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 25d010d449a3..369c999683ca 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -526,10 +526,37 @@ exit: return count; } +static ssize_t sriov_drivers_autoprobe_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%u\n", pdev->sriov->drivers_autoprobe); +} + +static ssize_t sriov_drivers_autoprobe_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + bool drivers_autoprobe; + + if (kstrtobool(buf, &drivers_autoprobe) < 0) + return -EINVAL; + + pdev->sriov->drivers_autoprobe = drivers_autoprobe; + + return count; +} + static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs); static struct device_attribute sriov_numvfs_attr = __ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP), sriov_numvfs_show, sriov_numvfs_store); +static struct device_attribute sriov_drivers_autoprobe_attr = + __ATTR(sriov_drivers_autoprobe, (S_IRUGO|S_IWUSR|S_IWGRP), + sriov_drivers_autoprobe_show, sriov_drivers_autoprobe_store); #endif /* CONFIG_PCI_IOV */ static ssize_t driver_override_store(struct device *dev, @@ -1549,6 +1576,7 @@ static struct attribute_group pci_dev_hp_attr_group = { static struct attribute *sriov_dev_attrs[] = { &sriov_totalvfs_attr.attr, &sriov_numvfs_attr.attr, + &sriov_drivers_autoprobe_attr.attr, NULL, }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8dd38e69d6f2..3ba7d586f522 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -272,6 +272,7 @@ struct pci_sriov { struct pci_dev *self; /* this PF */ struct mutex lock; /* lock for setting sriov_numvfs in sysfs */ resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */ + bool drivers_autoprobe; /* auto probing of VFs by driver */ }; #ifdef CONFIG_PCI_ATS -- cgit v1.2.3 From 490cb6ddb17df5ef5f5eb33c9a34f3033b31c204 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Wed, 19 Apr 2017 17:48:55 +0100 Subject: PCI: Implement devm_pci_remap_cfgspace() The introduction of the pci_remap_cfgspace() interface allows PCI host controller drivers to map PCI config space through a dedicated kernel interface. Current PCI host controller drivers use the devm_ioremap_*() devres interfaces to map PCI configuration space regions so in order to update them to the new pci_remap_cfgspace() mapping interface a new set of devres interfaces should be implemented so that PCI host controller drivers can make use of them. Introduce two new functions in the PCI kernel layer and Devres documentation: - devm_pci_remap_cfgspace() - devm_pci_remap_cfg_resource() so that PCI host controller drivers can make use of them to map PCI configuration space regions. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Cc: Jonathan Corbet --- Documentation/driver-model/devres.txt | 6 ++- drivers/pci/pci.c | 82 +++++++++++++++++++++++++++++++++++ include/linux/pci.h | 5 +++ 3 files changed, 91 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index bf34d5b3a733..e72587fe477d 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -342,8 +342,10 @@ PER-CPU MEM devm_free_percpu() PCI - pcim_enable_device() : after success, all PCI ops become managed - pcim_pin_device() : keep PCI device enabled after release + devm_pci_remap_cfgspace() : ioremap PCI configuration space + devm_pci_remap_cfg_resource() : ioremap PCI configuration space resource + pcim_enable_device() : after success, all PCI ops become managed + pcim_pin_device() : keep PCI device enabled after release PHY devm_usb_get_phy() diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bd98674a0419..4129f9453861 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3401,6 +3401,88 @@ void pci_unmap_iospace(struct resource *res) #endif } +/** + * devm_pci_remap_cfgspace - Managed pci_remap_cfgspace() + * @dev: Generic device to remap IO address for + * @offset: Resource address to map + * @size: Size of map + * + * Managed pci_remap_cfgspace(). Map is automatically unmapped on driver + * detach. + */ +void __iomem *devm_pci_remap_cfgspace(struct device *dev, + resource_size_t offset, + resource_size_t size) +{ + void __iomem **ptr, *addr; + + ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + addr = pci_remap_cfgspace(offset, size); + if (addr) { + *ptr = addr; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return addr; +} +EXPORT_SYMBOL(devm_pci_remap_cfgspace); + +/** + * devm_pci_remap_cfg_resource - check, request region and ioremap cfg resource + * @dev: generic device to handle the resource for + * @res: configuration space resource to be handled + * + * Checks that a resource is a valid memory region, requests the memory + * region and ioremaps with pci_remap_cfgspace() API that ensures the + * proper PCI configuration space memory attributes are guaranteed. + * + * All operations are managed and will be undone on driver detach. + * + * Returns a pointer to the remapped memory or an ERR_PTR() encoded error code + * on failure. Usage example: + * + * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + * base = devm_pci_remap_cfg_resource(&pdev->dev, res); + * if (IS_ERR(base)) + * return PTR_ERR(base); + */ +void __iomem *devm_pci_remap_cfg_resource(struct device *dev, + struct resource *res) +{ + resource_size_t size; + const char *name; + void __iomem *dest_ptr; + + BUG_ON(!dev); + + if (!res || resource_type(res) != IORESOURCE_MEM) { + dev_err(dev, "invalid resource\n"); + return IOMEM_ERR_PTR(-EINVAL); + } + + size = resource_size(res); + name = res->name ?: dev_name(dev); + + if (!devm_request_mem_region(dev, res->start, size, name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return IOMEM_ERR_PTR(-EBUSY); + } + + dest_ptr = devm_pci_remap_cfgspace(dev, res->start, size); + if (!dest_ptr) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + devm_release_mem_region(dev, res->start, size); + dest_ptr = IOMEM_ERR_PTR(-ENOMEM); + } + + return dest_ptr; +} +EXPORT_SYMBOL(devm_pci_remap_cfg_resource); + static void __pci_set_master(struct pci_dev *dev, bool enable) { u16 old_cmd, cmd; diff --git a/include/linux/pci.h b/include/linux/pci.h index eb3da1a04e6c..70534d66d18a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1199,6 +1199,11 @@ unsigned long pci_address_to_pio(phys_addr_t addr); phys_addr_t pci_pio_to_address(unsigned long pio); int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr); void pci_unmap_iospace(struct resource *res); +void __iomem *devm_pci_remap_cfgspace(struct device *dev, + resource_size_t offset, + resource_size_t size); +void __iomem *devm_pci_remap_cfg_resource(struct device *dev, + struct resource *res); static inline pci_bus_addr_t pci_bus_address(struct pci_dev *pdev, int bar) { -- cgit v1.2.3 From 13570533813cf111bb49954392cca0c2c409e775 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:15:04 +0530 Subject: Documentation: PCI: Add binding documentation for pci-test endpoint function Add binding documentation for pci-test endpoint function that helps in adding and configuring pci-test endpoint function. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas --- Documentation/PCI/00-INDEX | 2 ++ .../PCI/endpoint/function/binding/pci-test.txt | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 Documentation/PCI/endpoint/function/binding/pci-test.txt (limited to 'Documentation') diff --git a/Documentation/PCI/00-INDEX b/Documentation/PCI/00-INDEX index ab35e4bbdb1c..2fc901a1c32e 100644 --- a/Documentation/PCI/00-INDEX +++ b/Documentation/PCI/00-INDEX @@ -18,3 +18,5 @@ endpoint/pci-endpoint-cfs.txt - guide to use configfs to configure the PCI endpoint function. endpoint/pci-test-function.txt - specification of *PCI test* function device. +endpoint/function/binding/ + - binding documentation for PCI endpoint function diff --git a/Documentation/PCI/endpoint/function/binding/pci-test.txt b/Documentation/PCI/endpoint/function/binding/pci-test.txt new file mode 100644 index 000000000000..3b68b955fb50 --- /dev/null +++ b/Documentation/PCI/endpoint/function/binding/pci-test.txt @@ -0,0 +1,17 @@ +PCI TEST ENDPOINT FUNCTION + +name: Should be "pci_epf_test" to bind to the pci_epf_test driver. + +Configurable Fields: +vendorid : should be 0x104c +deviceid : should be 0xb500 for DRA74x and 0xb501 for DRA72x +revid : don't care +progif_code : don't care +subclass_code : don't care +baseclass_code : should be 0xff +cache_line_size : don't care +subsys_vendor_id : don't care +subsys_id : don't care +interrupt_pin : Should be 1 - INTA, 2 - INTB, 3 - INTC, 4 -INTD +msi_interrupts : Should be 1 to 32 depending on the number of MSI interrupts + to test -- cgit v1.2.3 From b12befecd7de905a05053eb6a266e3b97528c6d4 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:15:06 +0530 Subject: dt-bindings: PCI: Add DT bindings for PCI designware EP mode Add device tree binding documentation for PCI designware EP mode. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas Acked-by: Rob Herring --- .../devicetree/bindings/pci/designware-pcie.txt | 26 +++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt index 1392c705ceca..b2480dd38c11 100644 --- a/Documentation/devicetree/bindings/pci/designware-pcie.txt +++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt @@ -6,30 +6,40 @@ Required properties: - reg-names: Must be "config" for the PCIe configuration space. (The old way of getting the configuration address space from "ranges" is deprecated and should be avoided.) +- num-lanes: number of lanes to use +RC mode: - #address-cells: set to <3> - #size-cells: set to <2> - device_type: set to "pci" - ranges: ranges for the PCI memory and I/O regions - #interrupt-cells: set to <1> -- interrupt-map-mask and interrupt-map: standard PCI properties - to define the mapping of the PCIe interface to interrupt +- interrupt-map-mask and interrupt-map: standard PCI + properties to define the mapping of the PCIe interface to interrupt numbers. -- num-lanes: number of lanes to use +EP mode: +- num-ib-windows: number of inbound address translation + windows +- num-ob-windows: number of outbound address translation + windows Optional properties: -- num-viewport: number of view ports configured in hardware. If a platform - does not specify it, the driver assumes 2. - num-lanes: number of lanes to use (this property should be specified unless the link is brought already up in BIOS) - reset-gpio: gpio pin number of power good signal -- bus-range: PCI bus numbers covered (it is recommended for new devicetrees to - specify this property, to keep backwards compatibility a range of 0x00-0xff - is assumed if not present) - clocks: Must contain an entry for each entry in clock-names. See ../clocks/clock-bindings.txt for details. - clock-names: Must include the following entries: - "pcie" - "pcie_bus" +RC mode: +- num-viewport: number of view ports configured in + hardware. If a platform does not specify it, the driver assumes 2. +- bus-range: PCI bus numbers covered (it is recommended + for new devicetrees to specify this property, to keep backwards + compatibility a range of 0x00-0xff is assumed if not present) +EP mode: +- max-functions: maximum number of functions that can be + configured Example configuration: -- cgit v1.2.3 From 40cc72e2b51f61956f965510ceba35bddb0c373f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:15:09 +0530 Subject: dt-bindings: PCI: dra7xx: Add DT bindings for PCI dra7xx EP mode Add device tree binding documentation for PCI dra7xx EP mode. Acked-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas --- Documentation/devicetree/bindings/pci/ti-pci.txt | 37 +++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt index 60e25161f351..60c3cccefabc 100644 --- a/Documentation/devicetree/bindings/pci/ti-pci.txt +++ b/Documentation/devicetree/bindings/pci/ti-pci.txt @@ -1,17 +1,22 @@ TI PCI Controllers PCIe Designware Controller - - compatible: Should be "ti,dra7-pcie"" - - reg : Two register ranges as listed in the reg-names property - - reg-names : The first entry must be "ti-conf" for the TI specific registers - The second entry must be "rc-dbics" for the designware pcie - registers - The third entry must be "config" for the PCIe configuration space + - compatible: Should be "ti,dra7-pcie" for RC + Should be "ti,dra7-pcie-ep" for EP - phys : list of PHY specifiers (used by generic PHY framework) - phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the number of PHYs as specified in *phys* property. - ti,hwmods : Name of the hwmod associated to the pcie, "pcie", where is the instance number of the pcie from the HW spec. + - num-lanes as specified in ../designware-pcie.txt + +HOST MODE +========= + - reg : Two register ranges as listed in the reg-names property + - reg-names : The first entry must be "ti-conf" for the TI specific registers + The second entry must be "rc-dbics" for the DesignWare PCIe + registers + The third entry must be "config" for the PCIe configuration space - interrupts : Two interrupt entries must be specified. The first one is for main interrupt line and the second for MSI interrupt line. - #address-cells, @@ -19,13 +24,31 @@ PCIe Designware Controller #interrupt-cells, device_type, ranges, - num-lanes, interrupt-map-mask, interrupt-map : as specified in ../designware-pcie.txt +DEVICE MODE +=========== + - reg : Four register ranges as listed in the reg-names property + - reg-names : "ti-conf" for the TI specific registers + "ep_dbics" for the standard configuration registers as + they are locally accessed within the DIF CS space + "ep_dbics2" for the standard configuration registers as + they are locally accessed within the DIF CS2 space + "addr_space" used to map remote RC address space + - interrupts : one interrupt entries must be specified for main interrupt. + - num-ib-windows : number of inbound address translation windows + - num-ob-windows : number of outbound address translation windows + Optional Property: - gpios : Should be added if a gpio line is required to drive PERST# line +NOTE: Two DT nodes may be added for each PCI controller; one for host +mode and another for device mode. So in order for PCI to +work in host mode, EP mode DT node should be disabled and in order to PCI to +work in EP mode, host mode DT node should be disabled. Host mode and EP +mode are mutually exclusive. + Example: axi { compatible = "simple-bus"; -- cgit v1.2.3 From 66abca1d9db24e9e0b0d031d9b246e440e26fdae Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:15:12 +0530 Subject: dt-bindings: PCI: dra7xx: Add DT bindings to enable unaligned access Update device tree binding documentation of TI's dra7xx PCI controller to include property for enabling unaligned mem access. Signed-off-by: Kishon Vijay Abraham I Acked-by: Rob Herring Signed-off-by: Bjorn Helgaas --- Documentation/devicetree/bindings/pci/ti-pci.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt index 60c3cccefabc..6a07c96227e0 100644 --- a/Documentation/devicetree/bindings/pci/ti-pci.txt +++ b/Documentation/devicetree/bindings/pci/ti-pci.txt @@ -39,6 +39,11 @@ DEVICE MODE - interrupts : one interrupt entries must be specified for main interrupt. - num-ib-windows : number of inbound address translation windows - num-ob-windows : number of outbound address translation windows + - ti,syscon-unaligned-access: phandle to the syscon DT node. The 1st argument + should contain the register offset within syscon + and the 2nd argument should contain the bit field + for setting the bit to enable unaligned + access. Optional Property: - gpios : Should be added if a gpio line is required to drive PERST# line -- cgit v1.2.3 From 0cfb512e4acc7049d36478221f55bf6053bd81a6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:15:15 +0530 Subject: Documentation: misc-devices: Add Documentation for pci-endpoint-test driver Add Documentation for pci-endpoint-test driver. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas --- Documentation/misc-devices/pci-endpoint-test.txt | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/misc-devices/pci-endpoint-test.txt (limited to 'Documentation') diff --git a/Documentation/misc-devices/pci-endpoint-test.txt b/Documentation/misc-devices/pci-endpoint-test.txt new file mode 100644 index 000000000000..4ebc3594b32c --- /dev/null +++ b/Documentation/misc-devices/pci-endpoint-test.txt @@ -0,0 +1,35 @@ +Driver for PCI Endpoint Test Function + +This driver should be used as a host side driver if the root complex is +connected to a configurable PCI endpoint running *pci_epf_test* function +driver configured according to [1]. + +The "pci_endpoint_test" driver can be used to perform the following tests. + +The PCI driver for the test device performs the following tests + *) verifying addresses programmed in BAR + *) raise legacy IRQ + *) raise MSI IRQ + *) read data + *) write data + *) copy data + +This misc driver creates /dev/pci-endpoint-test. for every +*pci_epf_test* function connected to the root complex and "ioctls" +should be used to perform the above tests. + +ioctl +----- + PCITEST_BAR: Tests the BAR. The number of the BAR to be tested + should be passed as argument. + PCITEST_LEGACY_IRQ: Tests legacy IRQ + PCITEST_MSI: Tests message signalled interrupts. The MSI number + to be tested should be passed as argument. + PCITEST_WRITE: Perform write tests. The size of the buffer should be passed + as argument. + PCITEST_READ: Perform read tests. The size of the buffer should be passed + as argument. + PCITEST_COPY: Perform read tests. The size of the buffer should be passed + as argument. + +[1] -> Documentation/PCI/endpoint/function/binding/pci-test.txt -- cgit v1.2.3 From 16263d9e1dedb30ddca51d684f306cdc4294e705 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 27 Mar 2017 15:15:18 +0530 Subject: Documentation: PCI: Add userguide for PCI endpoint test function Add documentation to help users use pci-epf-test function driver and pci_endpoint_test host driver for testing PCI. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas --- Documentation/PCI/00-INDEX | 2 + Documentation/PCI/endpoint/pci-test-howto.txt | 179 ++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 Documentation/PCI/endpoint/pci-test-howto.txt (limited to 'Documentation') diff --git a/Documentation/PCI/00-INDEX b/Documentation/PCI/00-INDEX index 2fc901a1c32e..00c9a90b6f38 100644 --- a/Documentation/PCI/00-INDEX +++ b/Documentation/PCI/00-INDEX @@ -18,5 +18,7 @@ endpoint/pci-endpoint-cfs.txt - guide to use configfs to configure the PCI endpoint function. endpoint/pci-test-function.txt - specification of *PCI test* function device. +endpoint/pci-test-howto.txt + - userguide for PCI endpoint test function. endpoint/function/binding/ - binding documentation for PCI endpoint function diff --git a/Documentation/PCI/endpoint/pci-test-howto.txt b/Documentation/PCI/endpoint/pci-test-howto.txt new file mode 100644 index 000000000000..75f48c3bb191 --- /dev/null +++ b/Documentation/PCI/endpoint/pci-test-howto.txt @@ -0,0 +1,179 @@ + PCI TEST USERGUIDE + Kishon Vijay Abraham I + +This document is a guide to help users use pci-epf-test function driver +and pci_endpoint_test host driver for testing PCI. The list of steps to +be followed in the host side and EP side is given below. + +1. Endpoint Device + +1.1 Endpoint Controller Devices + +To find the list of endpoint controller devices in the system: + + # ls /sys/class/pci_epc/ + 51000000.pcie_ep + +If PCI_ENDPOINT_CONFIGFS is enabled + # ls /sys/kernel/config/pci_ep/controllers + 51000000.pcie_ep + +1.2 Endpoint Function Drivers + +To find the list of endpoint function drivers in the system: + + # ls /sys/bus/pci-epf/drivers + pci_epf_test + +If PCI_ENDPOINT_CONFIGFS is enabled + # ls /sys/kernel/config/pci_ep/functions + pci_epf_test + +1.3 Creating pci-epf-test Device + +PCI endpoint function device can be created using the configfs. To create +pci-epf-test device, the following commands can be used + + # mount -t configfs none /sys/kernel/config + # cd /sys/kernel/config/pci_ep/ + # mkdir functions/pci_epf_test/func1 + +The "mkdir func1" above creates the pci-epf-test function device that will +be probed by pci_epf_test driver. + +The PCI endpoint framework populates the directory with the following +configurable fields. + + # ls functions/pci_epf_test/func1 + baseclass_code interrupt_pin revid subsys_vendor_id + cache_line_size msi_interrupts subclass_code vendorid + deviceid progif_code subsys_id + +The PCI endpoint function driver populates these entries with default values +when the device is bound to the driver. The pci-epf-test driver populates +vendorid with 0xffff and interrupt_pin with 0x0001 + + # cat functions/pci_epf_test/func1/vendorid + 0xffff + # cat functions/pci_epf_test/func1/interrupt_pin + 0x0001 + +1.4 Configuring pci-epf-test Device + +The user can configure the pci-epf-test device using configfs entry. In order +to change the vendorid and the number of MSI interrupts used by the function +device, the following commands can be used. + + # echo 0x104c > functions/pci_epf_test/func1/vendorid + # echo 0xb500 > functions/pci_epf_test/func1/deviceid + # echo 16 > functions/pci_epf_test/func1/msi_interrupts + +1.5 Binding pci-epf-test Device to EP Controller + +In order for the endpoint function device to be useful, it has to be bound to +a PCI endpoint controller driver. Use the configfs to bind the function +device to one of the controller driver present in the system. + + # ln -s functions/pci_epf_test/func1 controllers/51000000.pcie_ep/ + +Once the above step is completed, the PCI endpoint is ready to establish a link +with the host. + +1.6 Start the Link + +In order for the endpoint device to establish a link with the host, the _start_ +field should be populated with '1'. + + # echo 1 > controllers/51000000.pcie_ep/start + +2. RootComplex Device + +2.1 lspci Output + +Note that the devices listed here correspond to the value populated in 1.4 above + + 00:00.0 PCI bridge: Texas Instruments Device 8888 (rev 01) + 01:00.0 Unassigned class [ff00]: Texas Instruments Device b500 + +2.2 Using Endpoint Test function Device + +pcitest.sh added in tools/pci/ can be used to run all the default PCI endpoint +tests. Before pcitest.sh can be used pcitest.c should be compiled using the +following commands. + + cd + make headers_install ARCH=arm + arm-linux-gnueabihf-gcc -Iusr/include tools/pci/pcitest.c -o pcitest + cp pcitest /usr/sbin/ + cp tools/pci/pcitest.sh + +2.2.1 pcitest.sh Output + # ./pcitest.sh + BAR tests + + BAR0: OKAY + BAR1: OKAY + BAR2: OKAY + BAR3: OKAY + BAR4: NOT OKAY + BAR5: NOT OKAY + + Interrupt tests + + LEGACY IRQ: NOT OKAY + MSI1: OKAY + MSI2: OKAY + MSI3: OKAY + MSI4: OKAY + MSI5: OKAY + MSI6: OKAY + MSI7: OKAY + MSI8: OKAY + MSI9: OKAY + MSI10: OKAY + MSI11: OKAY + MSI12: OKAY + MSI13: OKAY + MSI14: OKAY + MSI15: OKAY + MSI16: OKAY + MSI17: NOT OKAY + MSI18: NOT OKAY + MSI19: NOT OKAY + MSI20: NOT OKAY + MSI21: NOT OKAY + MSI22: NOT OKAY + MSI23: NOT OKAY + MSI24: NOT OKAY + MSI25: NOT OKAY + MSI26: NOT OKAY + MSI27: NOT OKAY + MSI28: NOT OKAY + MSI29: NOT OKAY + MSI30: NOT OKAY + MSI31: NOT OKAY + MSI32: NOT OKAY + + Read Tests + + READ ( 1 bytes): OKAY + READ ( 1024 bytes): OKAY + READ ( 1025 bytes): OKAY + READ (1024000 bytes): OKAY + READ (1024001 bytes): OKAY + + Write Tests + + WRITE ( 1 bytes): OKAY + WRITE ( 1024 bytes): OKAY + WRITE ( 1025 bytes): OKAY + WRITE (1024000 bytes): OKAY + WRITE (1024001 bytes): OKAY + + Copy Tests + + COPY ( 1 bytes): OKAY + COPY ( 1024 bytes): OKAY + COPY ( 1025 bytes): OKAY + COPY (1024000 bytes): OKAY + COPY (1024001 bytes): OKAY -- cgit v1.2.3