From 09298542cd891b43778db1f65aa3613aa5a562eb Mon Sep 17 00:00:00 2001 From: James Sewart Date: Tue, 10 Dec 2019 16:07:30 -0600 Subject: PCI: Add nr_devfns parameter to pci_add_dma_alias() Add a "nr_devfns" parameter to pci_add_dma_alias() so it can be used to create DMA aliases for a range of devfns. [bhelgaas: incorporate nr_devfns fix from James, update quirk_pex_vca_alias() and setup_aliases()] Signed-off-by: James Sewart Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index c393dff2d66f..930fab293073 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2310,7 +2310,7 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) } #endif -void pci_add_dma_alias(struct pci_dev *dev, u8 devfn); +void pci_add_dma_alias(struct pci_dev *dev, u8 devfn_from, unsigned nr_devfns); bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2); int pci_for_each_dma_alias(struct pci_dev *pdev, int (*fn)(struct pci_dev *pdev, -- cgit v1.2.3 From ed22aaaede44f647477a5048e62855c0ed49c9bd Mon Sep 17 00:00:00 2001 From: Dilip Kota Date: Mon, 9 Dec 2019 11:20:05 +0800 Subject: PCI: dwc: intel: PCIe RC controller driver Add support to PCIe RC controller on Intel Gateway SoCs. PCIe controller is based of Synopsys DesignWare PCIe core. Intel PCIe driver requires Upconfigure support, Fast Training Sequence and link speed configurations. So adding the respective helper functions in the PCIe DesignWare framework. It also programs hardware autonomous speed during speed configuration so defining it in pci_regs.h. Also, mark Intel PCIe driver depends on MSI IRQ Domain as Synopsys DesignWare framework depends on the PCI_MSI_IRQ_DOMAIN. Signed-off-by: Dilip Kota Signed-off-by: Lorenzo Pieralisi Reviewed-by: Andrew Murray Reviewed-by: Andy Shevchenko Acked-by: Gustavo Pimentel --- drivers/pci/controller/dwc/Kconfig | 11 + drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-designware.c | 56 +++ drivers/pci/controller/dwc/pcie-designware.h | 12 + drivers/pci/controller/dwc/pcie-intel-gw.c | 545 +++++++++++++++++++++++++++ include/uapi/linux/pci_regs.h | 1 + 6 files changed, 626 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-intel-gw.c (limited to 'include') diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 625a031b2193..0830dfcfa43a 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -209,6 +209,17 @@ config PCIE_ARTPEC6_EP Enables support for the PCIe controller in the ARTPEC-6 SoC to work in endpoint mode. This uses the DesignWare core. +config PCIE_INTEL_GW + bool "Intel Gateway PCIe host controller support" + depends on OF && (X86 || COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say 'Y' here to enable PCIe Host controller support on Intel + Gateway SoCs. + The PCIe controller uses the DesignWare core plus Intel-specific + hardware wrappers. + config PCIE_KIRIN depends on OF && (ARM64 || COMPILE_TEST) bool "HiSilicon Kirin series SoCs PCIe controllers" diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 69faff371f11..8a637cfcf6e9 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o +obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o obj-$(CONFIG_PCI_MESON) += pci-meson.o diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 820488dfeaed..681548c88282 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -12,6 +12,7 @@ #include #include +#include "../../pci.h" #include "pcie-designware.h" /* @@ -474,6 +475,61 @@ int dw_pcie_link_up(struct dw_pcie *pci) (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING))); } +void dw_pcie_upconfig_setup(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL); + val |= PORT_MLTI_UPCFG_SUPPORT; + dw_pcie_writel_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL, val); +} +EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup); + +void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen) +{ + u32 reg, val; + u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + + reg = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCTL2); + reg &= ~PCI_EXP_LNKCTL2_TLS; + + switch (pcie_link_speed[link_gen]) { + case PCIE_SPEED_2_5GT: + reg |= PCI_EXP_LNKCTL2_TLS_2_5GT; + break; + case PCIE_SPEED_5_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_5_0GT; + break; + case PCIE_SPEED_8_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_8_0GT; + break; + case PCIE_SPEED_16_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_16_0GT; + break; + default: + /* Use hardware capability */ + val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP); + val = FIELD_GET(PCI_EXP_LNKCAP_SLS, val); + reg &= ~PCI_EXP_LNKCTL2_HASD; + reg |= FIELD_PREP(PCI_EXP_LNKCTL2_TLS, val); + break; + } + + dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCTL2, reg); +} +EXPORT_SYMBOL_GPL(dw_pcie_link_set_max_speed); + +void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_N_FTS_MASK; + val |= n_fts & PORT_LOGIC_N_FTS_MASK; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); +} +EXPORT_SYMBOL_GPL(dw_pcie_link_set_n_fts); + static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) { u32 val; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 5accdd6bc388..a22ea5982817 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -30,7 +30,12 @@ #define LINK_WAIT_IATU 9 /* Synopsys-specific PCIe configuration registers */ +#define PCIE_PORT_AFR 0x70C +#define PORT_AFR_N_FTS_MASK GENMASK(15, 8) +#define PORT_AFR_CC_N_FTS_MASK GENMASK(23, 16) + #define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_DLL_LINK_EN BIT(5) #define PORT_LINK_MODE_MASK GENMASK(21, 16) #define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n) #define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1) @@ -46,6 +51,7 @@ #define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29) #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_N_FTS_MASK GENMASK(7, 0) #define PORT_LOGIC_SPEED_CHANGE BIT(17) #define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8) #define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n) @@ -60,6 +66,9 @@ #define PCIE_MSI_INTR0_MASK 0x82C #define PCIE_MSI_INTR0_STATUS 0x830 +#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 +#define PORT_MLTI_UPCFG_SUPPORT BIT(7) + #define PCIE_ATU_VIEWPORT 0x900 #define PCIE_ATU_REGION_INBOUND BIT(31) #define PCIE_ATU_REGION_OUTBOUND 0 @@ -273,6 +282,9 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val); u32 dw_pcie_read_atu(struct dw_pcie *pci, u32 reg, size_t size); void dw_pcie_write_atu(struct dw_pcie *pci, u32 reg, size_t size, u32 val); int dw_pcie_link_up(struct dw_pcie *pci); +void dw_pcie_upconfig_setup(struct dw_pcie *pci); +void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen); +void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts); int dw_pcie_wait_for_link(struct dw_pcie *pci); void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, u64 cpu_addr, u64 pci_addr, diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c new file mode 100644 index 000000000000..fc2a12212dec --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Intel Gateway SoCs + * + * Copyright (c) 2019 Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../pci.h" +#include "pcie-designware.h" + +#define PORT_AFR_N_FTS_GEN12_DFT (SZ_128 - 1) +#define PORT_AFR_N_FTS_GEN3 180 +#define PORT_AFR_N_FTS_GEN4 196 + +/* PCIe Application logic Registers */ +#define PCIE_APP_CCR 0x10 +#define PCIE_APP_CCR_LTSSM_ENABLE BIT(0) + +#define PCIE_APP_MSG_CR 0x30 +#define PCIE_APP_MSG_XMT_PM_TURNOFF BIT(0) + +#define PCIE_APP_PMC 0x44 +#define PCIE_APP_PMC_IN_L2 BIT(20) + +#define PCIE_APP_IRNEN 0xF4 +#define PCIE_APP_IRNCR 0xF8 +#define PCIE_APP_IRN_AER_REPORT BIT(0) +#define PCIE_APP_IRN_PME BIT(2) +#define PCIE_APP_IRN_RX_VDM_MSG BIT(4) +#define PCIE_APP_IRN_PM_TO_ACK BIT(9) +#define PCIE_APP_IRN_LINK_AUTO_BW_STAT BIT(11) +#define PCIE_APP_IRN_BW_MGT BIT(12) +#define PCIE_APP_IRN_MSG_LTR BIT(18) +#define PCIE_APP_IRN_SYS_ERR_RC BIT(29) +#define PCIE_APP_INTX_OFST 12 + +#define PCIE_APP_IRN_INT \ + (PCIE_APP_IRN_AER_REPORT | PCIE_APP_IRN_PME | \ + PCIE_APP_IRN_RX_VDM_MSG | PCIE_APP_IRN_SYS_ERR_RC | \ + PCIE_APP_IRN_PM_TO_ACK | PCIE_APP_IRN_MSG_LTR | \ + PCIE_APP_IRN_BW_MGT | PCIE_APP_IRN_LINK_AUTO_BW_STAT | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTA) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTB) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTC) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTD)) + +#define BUS_IATU_OFFSET SZ_256M +#define RESET_INTERVAL_MS 100 + +struct intel_pcie_soc { + unsigned int pcie_ver; + unsigned int pcie_atu_offset; + u32 num_viewport; +}; + +struct intel_pcie_port { + struct dw_pcie pci; + void __iomem *app_base; + struct gpio_desc *reset_gpio; + u32 rst_intrvl; + u32 max_speed; + u32 link_gen; + u32 max_width; + u32 n_fts; + struct clk *core_clk; + struct reset_control *core_rst; + struct phy *phy; + u8 pcie_cap_ofst; +}; + +static void pcie_update_bits(void __iomem *base, u32 ofs, u32 mask, u32 val) +{ + u32 old; + + old = readl(base + ofs); + val = (old & ~mask) | (val & mask); + + if (val != old) + writel(val, base + ofs); +} + +static inline u32 pcie_app_rd(struct intel_pcie_port *lpp, u32 ofs) +{ + return readl(lpp->app_base + ofs); +} + +static inline void pcie_app_wr(struct intel_pcie_port *lpp, u32 ofs, u32 val) +{ + writel(val, lpp->app_base + ofs); +} + +static void pcie_app_wr_mask(struct intel_pcie_port *lpp, u32 ofs, + u32 mask, u32 val) +{ + pcie_update_bits(lpp->app_base, ofs, mask, val); +} + +static inline u32 pcie_rc_cfg_rd(struct intel_pcie_port *lpp, u32 ofs) +{ + return dw_pcie_readl_dbi(&lpp->pci, ofs); +} + +static inline void pcie_rc_cfg_wr(struct intel_pcie_port *lpp, u32 ofs, u32 val) +{ + dw_pcie_writel_dbi(&lpp->pci, ofs, val); +} + +static void pcie_rc_cfg_wr_mask(struct intel_pcie_port *lpp, u32 ofs, + u32 mask, u32 val) +{ + pcie_update_bits(lpp->pci.dbi_base, ofs, mask, val); +} + +static void intel_pcie_ltssm_enable(struct intel_pcie_port *lpp) +{ + pcie_app_wr_mask(lpp, PCIE_APP_CCR, PCIE_APP_CCR_LTSSM_ENABLE, + PCIE_APP_CCR_LTSSM_ENABLE); +} + +static void intel_pcie_ltssm_disable(struct intel_pcie_port *lpp) +{ + pcie_app_wr_mask(lpp, PCIE_APP_CCR, PCIE_APP_CCR_LTSSM_ENABLE, 0); +} + +static void intel_pcie_link_setup(struct intel_pcie_port *lpp) +{ + u32 val; + u8 offset = lpp->pcie_cap_ofst; + + val = pcie_rc_cfg_rd(lpp, offset + PCI_EXP_LNKCAP); + lpp->max_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, val); + lpp->max_width = FIELD_GET(PCI_EXP_LNKCAP_MLW, val); + + val = pcie_rc_cfg_rd(lpp, offset + PCI_EXP_LNKCTL); + + val &= ~(PCI_EXP_LNKCTL_LD | PCI_EXP_LNKCTL_ASPMC); + pcie_rc_cfg_wr(lpp, offset + PCI_EXP_LNKCTL, val); +} + +static void intel_pcie_port_logic_setup(struct intel_pcie_port *lpp) +{ + u32 val, mask; + + switch (pcie_link_speed[lpp->max_speed]) { + case PCIE_SPEED_8_0GT: + lpp->n_fts = PORT_AFR_N_FTS_GEN3; + break; + case PCIE_SPEED_16_0GT: + lpp->n_fts = PORT_AFR_N_FTS_GEN4; + break; + default: + lpp->n_fts = PORT_AFR_N_FTS_GEN12_DFT; + break; + } + + mask = PORT_AFR_N_FTS_MASK | PORT_AFR_CC_N_FTS_MASK; + val = FIELD_PREP(PORT_AFR_N_FTS_MASK, lpp->n_fts) | + FIELD_PREP(PORT_AFR_CC_N_FTS_MASK, lpp->n_fts); + pcie_rc_cfg_wr_mask(lpp, PCIE_PORT_AFR, mask, val); + + /* Port Link Control Register */ + pcie_rc_cfg_wr_mask(lpp, PCIE_PORT_LINK_CONTROL, PORT_LINK_DLL_LINK_EN, + PORT_LINK_DLL_LINK_EN); +} + +static void intel_pcie_rc_setup(struct intel_pcie_port *lpp) +{ + intel_pcie_ltssm_disable(lpp); + intel_pcie_link_setup(lpp); + dw_pcie_setup_rc(&lpp->pci.pp); + dw_pcie_upconfig_setup(&lpp->pci); + intel_pcie_port_logic_setup(lpp); + dw_pcie_link_set_max_speed(&lpp->pci, lpp->link_gen); + dw_pcie_link_set_n_fts(&lpp->pci, lpp->n_fts); +} + +static int intel_pcie_ep_rst_init(struct intel_pcie_port *lpp) +{ + struct device *dev = lpp->pci.dev; + int ret; + + lpp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(lpp->reset_gpio)) { + ret = PTR_ERR(lpp->reset_gpio); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request PCIe GPIO: %d\n", ret); + return ret; + } + + /* Make initial reset last for 100us */ + usleep_range(100, 200); + + return 0; +} + +static void intel_pcie_core_rst_assert(struct intel_pcie_port *lpp) +{ + reset_control_assert(lpp->core_rst); +} + +static void intel_pcie_core_rst_deassert(struct intel_pcie_port *lpp) +{ + /* + * One micro-second delay to make sure the reset pulse + * wide enough so that core reset is clean. + */ + udelay(1); + reset_control_deassert(lpp->core_rst); + + /* + * Some SoC core reset also reset PHY, more delay needed + * to make sure the reset process is done. + */ + usleep_range(1000, 2000); +} + +static void intel_pcie_device_rst_assert(struct intel_pcie_port *lpp) +{ + gpiod_set_value_cansleep(lpp->reset_gpio, 1); +} + +static void intel_pcie_device_rst_deassert(struct intel_pcie_port *lpp) +{ + msleep(lpp->rst_intrvl); + gpiod_set_value_cansleep(lpp->reset_gpio, 0); +} + +static int intel_pcie_app_logic_setup(struct intel_pcie_port *lpp) +{ + intel_pcie_device_rst_deassert(lpp); + intel_pcie_ltssm_enable(lpp); + + return dw_pcie_wait_for_link(&lpp->pci); +} + +static void intel_pcie_core_irq_disable(struct intel_pcie_port *lpp) +{ + pcie_app_wr(lpp, PCIE_APP_IRNEN, 0); + pcie_app_wr(lpp, PCIE_APP_IRNCR, PCIE_APP_IRN_INT); +} + +static int intel_pcie_get_resources(struct platform_device *pdev) +{ + struct intel_pcie_port *lpp = platform_get_drvdata(pdev); + struct dw_pcie *pci = &lpp->pci; + struct device *dev = pci->dev; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + lpp->core_clk = devm_clk_get(dev, NULL); + if (IS_ERR(lpp->core_clk)) { + ret = PTR_ERR(lpp->core_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get clks: %d\n", ret); + return ret; + } + + lpp->core_rst = devm_reset_control_get(dev, NULL); + if (IS_ERR(lpp->core_rst)) { + ret = PTR_ERR(lpp->core_rst); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get resets: %d\n", ret); + return ret; + } + + ret = device_property_match_string(dev, "device_type", "pci"); + if (ret) { + dev_err(dev, "Failed to find pci device type: %d\n", ret); + return ret; + } + + ret = device_property_read_u32(dev, "reset-assert-ms", + &lpp->rst_intrvl); + if (ret) + lpp->rst_intrvl = RESET_INTERVAL_MS; + + ret = of_pci_get_max_link_speed(dev->of_node); + lpp->link_gen = ret < 0 ? 0 : ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app"); + lpp->app_base = devm_ioremap_resource(dev, res); + if (IS_ERR(lpp->app_base)) + return PTR_ERR(lpp->app_base); + + lpp->phy = devm_phy_get(dev, "pcie"); + if (IS_ERR(lpp->phy)) { + ret = PTR_ERR(lpp->phy); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Couldn't get pcie-phy: %d\n", ret); + return ret; + } + + return 0; +} + +static void intel_pcie_deinit_phy(struct intel_pcie_port *lpp) +{ + phy_exit(lpp->phy); +} + +static int intel_pcie_wait_l2(struct intel_pcie_port *lpp) +{ + u32 value; + int ret; + + if (pcie_link_speed[lpp->max_speed] < PCIE_SPEED_8_0GT) + return 0; + + /* Send PME_TURN_OFF message */ + pcie_app_wr_mask(lpp, PCIE_APP_MSG_CR, PCIE_APP_MSG_XMT_PM_TURNOFF, + PCIE_APP_MSG_XMT_PM_TURNOFF); + + /* Read PMC status and wait for falling into L2 link state */ + ret = readl_poll_timeout(lpp->app_base + PCIE_APP_PMC, value, + value & PCIE_APP_PMC_IN_L2, 20, + jiffies_to_usecs(5 * HZ)); + if (ret) + dev_err(lpp->pci.dev, "PCIe link enter L2 timeout!\n"); + + return ret; +} + +static void intel_pcie_turn_off(struct intel_pcie_port *lpp) +{ + if (dw_pcie_link_up(&lpp->pci)) + intel_pcie_wait_l2(lpp); + + /* Put endpoint device in reset state */ + intel_pcie_device_rst_assert(lpp); + pcie_rc_cfg_wr_mask(lpp, PCI_COMMAND, PCI_COMMAND_MEMORY, 0); +} + +static int intel_pcie_host_setup(struct intel_pcie_port *lpp) +{ + struct device *dev = lpp->pci.dev; + int ret; + + intel_pcie_core_rst_assert(lpp); + intel_pcie_device_rst_assert(lpp); + + ret = phy_init(lpp->phy); + if (ret) + return ret; + + intel_pcie_core_rst_deassert(lpp); + + ret = clk_prepare_enable(lpp->core_clk); + if (ret) { + dev_err(lpp->pci.dev, "Core clock enable failed: %d\n", ret); + goto clk_err; + } + + if (!lpp->pcie_cap_ofst) { + ret = dw_pcie_find_capability(&lpp->pci, PCI_CAP_ID_EXP); + if (!ret) { + ret = -ENXIO; + dev_err(dev, "Invalid PCIe capability offset\n"); + goto app_init_err; + } + + lpp->pcie_cap_ofst = ret; + } + + intel_pcie_rc_setup(lpp); + ret = intel_pcie_app_logic_setup(lpp); + if (ret) + goto app_init_err; + + /* Enable integrated interrupts */ + pcie_app_wr_mask(lpp, PCIE_APP_IRNEN, PCIE_APP_IRN_INT, + PCIE_APP_IRN_INT); + + return 0; + +app_init_err: + clk_disable_unprepare(lpp->core_clk); +clk_err: + intel_pcie_core_rst_assert(lpp); + intel_pcie_deinit_phy(lpp); + + return ret; +} + +static void __intel_pcie_remove(struct intel_pcie_port *lpp) +{ + intel_pcie_core_irq_disable(lpp); + intel_pcie_turn_off(lpp); + clk_disable_unprepare(lpp->core_clk); + intel_pcie_core_rst_assert(lpp); + intel_pcie_deinit_phy(lpp); +} + +static int intel_pcie_remove(struct platform_device *pdev) +{ + struct intel_pcie_port *lpp = platform_get_drvdata(pdev); + struct pcie_port *pp = &lpp->pci.pp; + + dw_pcie_host_deinit(pp); + __intel_pcie_remove(lpp); + + return 0; +} + +static int __maybe_unused intel_pcie_suspend_noirq(struct device *dev) +{ + struct intel_pcie_port *lpp = dev_get_drvdata(dev); + int ret; + + intel_pcie_core_irq_disable(lpp); + ret = intel_pcie_wait_l2(lpp); + if (ret) + return ret; + + intel_pcie_deinit_phy(lpp); + clk_disable_unprepare(lpp->core_clk); + return ret; +} + +static int __maybe_unused intel_pcie_resume_noirq(struct device *dev) +{ + struct intel_pcie_port *lpp = dev_get_drvdata(dev); + + return intel_pcie_host_setup(lpp); +} + +static int intel_pcie_rc_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct intel_pcie_port *lpp = dev_get_drvdata(pci->dev); + + return intel_pcie_host_setup(lpp); +} + +/* + * Dummy function so that DW core doesn't configure MSI + */ +static int intel_pcie_msi_init(struct pcie_port *pp) +{ + return 0; +} + +u64 intel_pcie_cpu_addr(struct dw_pcie *pcie, u64 cpu_addr) +{ + return cpu_addr + BUS_IATU_OFFSET; +} + +static const struct dw_pcie_ops intel_pcie_ops = { + .cpu_addr_fixup = intel_pcie_cpu_addr, +}; + +static const struct dw_pcie_host_ops intel_pcie_dw_ops = { + .host_init = intel_pcie_rc_init, + .msi_host_init = intel_pcie_msi_init, +}; + +static const struct intel_pcie_soc pcie_data = { + .pcie_ver = 0x520A, + .pcie_atu_offset = 0xC0000, + .num_viewport = 3, +}; + +static int intel_pcie_probe(struct platform_device *pdev) +{ + const struct intel_pcie_soc *data; + struct device *dev = &pdev->dev; + struct intel_pcie_port *lpp; + struct pcie_port *pp; + struct dw_pcie *pci; + int ret; + + lpp = devm_kzalloc(dev, sizeof(*lpp), GFP_KERNEL); + if (!lpp) + return -ENOMEM; + + platform_set_drvdata(pdev, lpp); + pci = &lpp->pci; + pci->dev = dev; + pp = &pci->pp; + + ret = intel_pcie_get_resources(pdev); + if (ret) + return ret; + + ret = intel_pcie_ep_rst_init(lpp); + if (ret) + return ret; + + data = device_get_match_data(dev); + if (!data) + return -ENODEV; + + pci->ops = &intel_pcie_ops; + pci->version = data->pcie_ver; + pci->atu_base = pci->dbi_base + data->pcie_atu_offset; + pp->ops = &intel_pcie_dw_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Cannot initialize host\n"); + return ret; + } + + /* + * Intel PCIe doesn't configure IO region, so set viewport + * to not perform IO region access. + */ + pci->num_viewport = data->num_viewport; + + return 0; +} + +static const struct dev_pm_ops intel_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pcie_suspend_noirq, + intel_pcie_resume_noirq) +}; + +static const struct of_device_id of_intel_pcie_match[] = { + { .compatible = "intel,lgm-pcie", .data = &pcie_data }, + {} +}; + +static struct platform_driver intel_pcie_driver = { + .probe = intel_pcie_probe, + .remove = intel_pcie_remove, + .driver = { + .name = "intel-gw-pcie", + .of_match_table = of_intel_pcie_match, + .pm = &intel_pcie_pm_ops, + }, +}; +builtin_platform_driver(intel_pcie_driver); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index acb7d2bdb419..5437690483cd 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -676,6 +676,7 @@ #define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed 32GT/s */ #define PCI_EXP_LNKCTL2_ENTER_COMP 0x0010 /* Enter Compliance */ #define PCI_EXP_LNKCTL2_TX_MARGIN 0x0380 /* Transmit Margin */ +#define PCI_EXP_LNKCTL2_HASD 0x0020 /* HW Autonomous Speed Disable */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ -- cgit v1.2.3 From a6b0ef9a7d03bb78d37c420753741ef8a082160b Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Mon, 6 Jan 2020 12:03:28 -0700 Subject: PCI/switchtec: Add support for Intercomm Notify and Upstream Error Containment Add support for the Inter Fabric Manager Communication (Intercomm) Notify event in PAX variants of Switchtec hardware and the Upstream Error Containment port in the MR1 release of Gen3 firmware. Link: https://lore.kernel.org/r/20200106190337.2428-4-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 3 +++ include/linux/switchtec.h | 7 +++++-- include/uapi/linux/switchtec_ioctl.h | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 9c3ad09d3022..218e67428cf9 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -751,10 +751,13 @@ static const struct event_reg { 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_PAR(SWITCHTEC_IOCTL_EVENT_INTERCOMM_REQ_NOTIFY, + intercomm_notify_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_UEC, uec_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), diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index e295515bc3f3..b4ba3a38f30f 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -196,7 +196,9 @@ struct part_cfg_regs { u32 mrpc_comp_async_data[5]; u32 dyn_binding_hdr; u32 dyn_binding_data[5]; - u32 reserved4[159]; + u32 intercomm_notify_hdr; + u32 intercomm_notify_data[5]; + u32 reserved4[153]; } __packed; enum { @@ -320,7 +322,8 @@ struct pff_csr_regs { u32 dpc_data[5]; u32 cts_hdr; u32 cts_data[5]; - u32 reserved3[6]; + u32 uec_hdr; + u32 uec_data[5]; u32 hotplug_hdr; u32 hotplug_data[5]; u32 ier_hdr; diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h index c912b5a678e4..e8db938985ca 100644 --- a/include/uapi/linux/switchtec_ioctl.h +++ b/include/uapi/linux/switchtec_ioctl.h @@ -98,7 +98,9 @@ struct switchtec_ioctl_event_summary { #define SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT 27 #define SWITCHTEC_IOCTL_EVENT_LINK_STATE 28 #define SWITCHTEC_IOCTL_EVENT_GFMS 29 -#define SWITCHTEC_IOCTL_MAX_EVENTS 30 +#define SWITCHTEC_IOCTL_EVENT_INTERCOMM_REQ_NOTIFY 30 +#define SWITCHTEC_IOCTL_EVENT_UEC 31 +#define SWITCHTEC_IOCTL_MAX_EVENTS 32 #define SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX -1 #define SWITCHTEC_IOCTL_EVENT_IDX_ALL -2 -- cgit v1.2.3 From fcccd282b633ab9fc7d53ff8ccf82ab5c30a0985 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:42 -0700 Subject: PCI/switchtec: Rename generation-specific constants Gen4 hardware will have different values for the SWITCHTEC_X_RUNNING and SWITCHTEC_IOCTL_NUM_PARTITIONS, so rename them with GEN3 in their name. No functional changes intended. Link: https://lore.kernel.org/r/20200115035648.2578-2-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 10 +++++----- include/linux/switchtec.h | 8 ++++---- include/uapi/linux/switchtec_ioctl.h | 5 ++++- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 05d4cb49219b..e4d9291864bc 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -569,7 +569,7 @@ static int ioctl_flash_info(struct switchtec_dev *stdev, struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; info.flash_length = ioread32(&fi->flash_length); - info.num_partitions = SWITCHTEC_IOCTL_NUM_PARTITIONS; + info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; if (copy_to_user(uinfo, &info, sizeof(info))) return -EFAULT; @@ -599,25 +599,25 @@ static int ioctl_flash_part_info(struct switchtec_dev *stdev, case SWITCHTEC_IOCTL_PART_CFG0: active_addr = ioread32(&fi->active_cfg); set_fw_info_part(&info, &fi->cfg0); - if (ioread16(&si->cfg_running) == SWITCHTEC_CFG0_RUNNING) + if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG0_RUNNING) info.active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_CFG1: active_addr = ioread32(&fi->active_cfg); set_fw_info_part(&info, &fi->cfg1); - if (ioread16(&si->cfg_running) == SWITCHTEC_CFG1_RUNNING) + if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG1_RUNNING) info.active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_IMG0: active_addr = ioread32(&fi->active_img); set_fw_info_part(&info, &fi->img0); - if (ioread16(&si->img_running) == SWITCHTEC_IMG0_RUNNING) + if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG0_RUNNING) info.active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_IMG1: active_addr = ioread32(&fi->active_img); set_fw_info_part(&info, &fi->img1); - if (ioread16(&si->img_running) == SWITCHTEC_IMG1_RUNNING) + if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG1_RUNNING) info.active |= SWITCHTEC_IOCTL_PART_RUNNING; break; case SWITCHTEC_IOCTL_PART_NVLOG: diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index b4ba3a38f30f..4ee450487fe4 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -98,10 +98,10 @@ struct sw_event_regs { } __packed; enum { - SWITCHTEC_CFG0_RUNNING = 0x04, - SWITCHTEC_CFG1_RUNNING = 0x05, - SWITCHTEC_IMG0_RUNNING = 0x03, - SWITCHTEC_IMG1_RUNNING = 0x07, + SWITCHTEC_GEN3_CFG0_RUNNING = 0x04, + SWITCHTEC_GEN3_CFG1_RUNNING = 0x05, + SWITCHTEC_GEN3_IMG0_RUNNING = 0x03, + SWITCHTEC_GEN3_IMG1_RUNNING = 0x07, }; struct sys_info_regs { diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h index e8db938985ca..4d09cfa2e9e6 100644 --- a/include/uapi/linux/switchtec_ioctl.h +++ b/include/uapi/linux/switchtec_ioctl.h @@ -32,7 +32,10 @@ #define SWITCHTEC_IOCTL_PART_VENDOR5 10 #define SWITCHTEC_IOCTL_PART_VENDOR6 11 #define SWITCHTEC_IOCTL_PART_VENDOR7 12 -#define SWITCHTEC_IOCTL_NUM_PARTITIONS 13 +#define SWITCHTEC_NUM_PARTITIONS_GEN3 13 + +/* obsolete: for compatibility with old userspace software */ +#define SWITCHTEC_IOCTL_NUM_PARTITIONS SWITCHTEC_NUM_PARTITIONS_GEN3 struct switchtec_ioctl_flash_info { __u64 flash_length; -- cgit v1.2.3 From b13313a01a6a607bd92268822d498d5c7356ca71 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:43 -0700 Subject: PCI/switchtec: Add 'generation' variable Add a generation variable passed through the device ID table and test for Gen3-specific registers. This will allow us to add Gen4 and other devices that extend the programming model. Link: https://lore.kernel.org/r/20200115035648.2578-3-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 90 +++++++++++++++++++++++++++--------------- include/linux/switchtec.h | 6 +++ 2 files changed, 64 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index e4d9291864bc..8302524734f0 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -326,7 +326,21 @@ 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_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + struct sys_info_regs __iomem *si = stdev->mmio_sys_info; + + /* component_vendor field not supported after gen3 */ + if (stdev->gen != SWITCHTEC_GEN3) + return sprintf(buf, "none\n"); + + return io_string_show(buf, &si->component_vendor, + sizeof(si->component_vendor)); +} +static DEVICE_ATTR_RO(component_vendor); static ssize_t component_id_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -334,6 +348,10 @@ static ssize_t component_id_show(struct device *dev, struct switchtec_dev *stdev = to_stdev(dev); int id = ioread16(&stdev->mmio_sys_info->component_id); + /* component_id field not supported after gen3 */ + if (stdev->gen != SWITCHTEC_GEN3) + return sprintf(buf, "none\n"); + return sprintf(buf, "PM%04X\n", id); } static DEVICE_ATTR_RO(component_id); @@ -344,6 +362,10 @@ static ssize_t component_revision_show(struct device *dev, struct switchtec_dev *stdev = to_stdev(dev); int rev = ioread8(&stdev->mmio_sys_info->component_revision); + /* component_revision field not supported after gen3 */ + if (stdev->gen != SWITCHTEC_GEN3) + return sprintf(buf, "255\n"); + return sprintf(buf, "%d\n", rev); } static DEVICE_ATTR_RO(component_revision); @@ -1420,6 +1442,8 @@ static int switchtec_pci_probe(struct pci_dev *pdev, if (IS_ERR(stdev)) return PTR_ERR(stdev); + stdev->gen = id->driver_data; + rc = switchtec_init_pci(stdev, pdev); if (rc) goto err_put; @@ -1467,7 +1491,7 @@ static void switchtec_pci_remove(struct pci_dev *pdev) put_device(&stdev->dev); } -#define SWITCHTEC_PCI_DEVICE(device_id) \ +#define SWITCHTEC_PCI_DEVICE(device_id, gen) \ { \ .vendor = PCI_VENDOR_ID_MICROSEMI, \ .device = device_id, \ @@ -1475,6 +1499,7 @@ static void switchtec_pci_remove(struct pci_dev *pdev) .subdevice = PCI_ANY_ID, \ .class = (PCI_CLASS_MEMORY_OTHER << 8), \ .class_mask = 0xFFFFFFFF, \ + .driver_data = gen, \ }, \ { \ .vendor = PCI_VENDOR_ID_MICROSEMI, \ @@ -1483,39 +1508,40 @@ static void switchtec_pci_remove(struct pci_dev *pdev) .subdevice = PCI_ANY_ID, \ .class = (PCI_CLASS_BRIDGE_OTHER << 8), \ .class_mask = 0xFFFFFFFF, \ + .driver_data = gen, \ } static const struct pci_device_id switchtec_pci_tbl[] = { - SWITCHTEC_PCI_DEVICE(0x8531), //PFX 24xG3 - SWITCHTEC_PCI_DEVICE(0x8532), //PFX 32xG3 - SWITCHTEC_PCI_DEVICE(0x8533), //PFX 48xG3 - SWITCHTEC_PCI_DEVICE(0x8534), //PFX 64xG3 - SWITCHTEC_PCI_DEVICE(0x8535), //PFX 80xG3 - SWITCHTEC_PCI_DEVICE(0x8536), //PFX 96xG3 - SWITCHTEC_PCI_DEVICE(0x8541), //PSX 24xG3 - SWITCHTEC_PCI_DEVICE(0x8542), //PSX 32xG3 - SWITCHTEC_PCI_DEVICE(0x8543), //PSX 48xG3 - SWITCHTEC_PCI_DEVICE(0x8544), //PSX 64xG3 - SWITCHTEC_PCI_DEVICE(0x8545), //PSX 80xG3 - SWITCHTEC_PCI_DEVICE(0x8546), //PSX 96xG3 - SWITCHTEC_PCI_DEVICE(0x8551), //PAX 24XG3 - SWITCHTEC_PCI_DEVICE(0x8552), //PAX 32XG3 - SWITCHTEC_PCI_DEVICE(0x8553), //PAX 48XG3 - SWITCHTEC_PCI_DEVICE(0x8554), //PAX 64XG3 - SWITCHTEC_PCI_DEVICE(0x8555), //PAX 80XG3 - SWITCHTEC_PCI_DEVICE(0x8556), //PAX 96XG3 - SWITCHTEC_PCI_DEVICE(0x8561), //PFXL 24XG3 - SWITCHTEC_PCI_DEVICE(0x8562), //PFXL 32XG3 - SWITCHTEC_PCI_DEVICE(0x8563), //PFXL 48XG3 - SWITCHTEC_PCI_DEVICE(0x8564), //PFXL 64XG3 - SWITCHTEC_PCI_DEVICE(0x8565), //PFXL 80XG3 - SWITCHTEC_PCI_DEVICE(0x8566), //PFXL 96XG3 - SWITCHTEC_PCI_DEVICE(0x8571), //PFXI 24XG3 - SWITCHTEC_PCI_DEVICE(0x8572), //PFXI 32XG3 - SWITCHTEC_PCI_DEVICE(0x8573), //PFXI 48XG3 - SWITCHTEC_PCI_DEVICE(0x8574), //PFXI 64XG3 - SWITCHTEC_PCI_DEVICE(0x8575), //PFXI 80XG3 - SWITCHTEC_PCI_DEVICE(0x8576), //PFXI 96XG3 + SWITCHTEC_PCI_DEVICE(0x8531, SWITCHTEC_GEN3), //PFX 24xG3 + SWITCHTEC_PCI_DEVICE(0x8532, SWITCHTEC_GEN3), //PFX 32xG3 + SWITCHTEC_PCI_DEVICE(0x8533, SWITCHTEC_GEN3), //PFX 48xG3 + SWITCHTEC_PCI_DEVICE(0x8534, SWITCHTEC_GEN3), //PFX 64xG3 + SWITCHTEC_PCI_DEVICE(0x8535, SWITCHTEC_GEN3), //PFX 80xG3 + SWITCHTEC_PCI_DEVICE(0x8536, SWITCHTEC_GEN3), //PFX 96xG3 + SWITCHTEC_PCI_DEVICE(0x8541, SWITCHTEC_GEN3), //PSX 24xG3 + SWITCHTEC_PCI_DEVICE(0x8542, SWITCHTEC_GEN3), //PSX 32xG3 + SWITCHTEC_PCI_DEVICE(0x8543, SWITCHTEC_GEN3), //PSX 48xG3 + SWITCHTEC_PCI_DEVICE(0x8544, SWITCHTEC_GEN3), //PSX 64xG3 + SWITCHTEC_PCI_DEVICE(0x8545, SWITCHTEC_GEN3), //PSX 80xG3 + SWITCHTEC_PCI_DEVICE(0x8546, SWITCHTEC_GEN3), //PSX 96xG3 + SWITCHTEC_PCI_DEVICE(0x8551, SWITCHTEC_GEN3), //PAX 24XG3 + SWITCHTEC_PCI_DEVICE(0x8552, SWITCHTEC_GEN3), //PAX 32XG3 + SWITCHTEC_PCI_DEVICE(0x8553, SWITCHTEC_GEN3), //PAX 48XG3 + SWITCHTEC_PCI_DEVICE(0x8554, SWITCHTEC_GEN3), //PAX 64XG3 + SWITCHTEC_PCI_DEVICE(0x8555, SWITCHTEC_GEN3), //PAX 80XG3 + SWITCHTEC_PCI_DEVICE(0x8556, SWITCHTEC_GEN3), //PAX 96XG3 + SWITCHTEC_PCI_DEVICE(0x8561, SWITCHTEC_GEN3), //PFXL 24XG3 + SWITCHTEC_PCI_DEVICE(0x8562, SWITCHTEC_GEN3), //PFXL 32XG3 + SWITCHTEC_PCI_DEVICE(0x8563, SWITCHTEC_GEN3), //PFXL 48XG3 + SWITCHTEC_PCI_DEVICE(0x8564, SWITCHTEC_GEN3), //PFXL 64XG3 + SWITCHTEC_PCI_DEVICE(0x8565, SWITCHTEC_GEN3), //PFXL 80XG3 + SWITCHTEC_PCI_DEVICE(0x8566, SWITCHTEC_GEN3), //PFXL 96XG3 + SWITCHTEC_PCI_DEVICE(0x8571, SWITCHTEC_GEN3), //PFXI 24XG3 + SWITCHTEC_PCI_DEVICE(0x8572, SWITCHTEC_GEN3), //PFXI 32XG3 + SWITCHTEC_PCI_DEVICE(0x8573, SWITCHTEC_GEN3), //PFXI 48XG3 + SWITCHTEC_PCI_DEVICE(0x8574, SWITCHTEC_GEN3), //PFXI 64XG3 + SWITCHTEC_PCI_DEVICE(0x8575, SWITCHTEC_GEN3), //PFXI 80XG3 + SWITCHTEC_PCI_DEVICE(0x8576, SWITCHTEC_GEN3), //PFXI 96XG3 {0} }; MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl); diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index 4ee450487fe4..d0b5816549ed 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -32,6 +32,10 @@ enum { SWITCHTEC_GAS_PFF_CSR_OFFSET = 0x134000, }; +enum switchtec_gen { + SWITCHTEC_GEN3, +}; + struct mrpc_regs { u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; @@ -358,6 +362,8 @@ struct switchtec_dev { struct device dev; struct cdev cdev; + enum switchtec_gen gen; + int partition; int partition_count; int pff_csr_count; -- cgit v1.2.3 From 993d208daaebebc3f3ec211e862a413a90e8d69b Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:45 -0700 Subject: PCI/switchtec: Separate Gen3 register structures into unions Since the sys_info and flash_info registers differ significantly in Gen4 hardware, separate out the Gen3 registers into their own structure with a union in the main structure. No functional changes intended. Link: https://lore.kernel.org/r/20200115035648.2578-5-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 38 +++++++++++++++++++++++++++----------- include/linux/switchtec.h | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 0ae444517790..c4dae075a692 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -317,8 +317,12 @@ 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)); \ + struct sys_info_regs __iomem *si = stdev->mmio_sys_info; \ + if (stdev->gen == SWITCHTEC_GEN3) \ + return io_string_show(buf, &si->gen3.field, \ + sizeof(si->gen3.field)); \ + else \ + return -ENOTSUPP; \ } \ \ static DEVICE_ATTR_RO(field) @@ -337,8 +341,8 @@ static ssize_t component_vendor_show(struct device *dev, if (stdev->gen != SWITCHTEC_GEN3) return sprintf(buf, "none\n"); - return io_string_show(buf, &si->component_vendor, - sizeof(si->component_vendor)); + return io_string_show(buf, &si->gen3.component_vendor, + sizeof(si->gen3.component_vendor)); } static DEVICE_ATTR_RO(component_vendor); @@ -346,7 +350,7 @@ 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); + int id = ioread16(&stdev->mmio_sys_info->gen3.component_id); /* component_id field not supported after gen3 */ if (stdev->gen != SWITCHTEC_GEN3) @@ -360,7 +364,7 @@ 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); + int rev = ioread8(&stdev->mmio_sys_info->gen3.component_revision); /* component_revision field not supported after gen3 */ if (stdev->gen != SWITCHTEC_GEN3) @@ -590,8 +594,12 @@ static int ioctl_flash_info(struct switchtec_dev *stdev, 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_NUM_PARTITIONS_GEN3; + if (stdev->gen == SWITCHTEC_GEN3) { + info.flash_length = ioread32(&fi->gen3.flash_length); + info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; + } else { + return -ENOTSUPP; + } if (copy_to_user(uinfo, &info, sizeof(info))) return -EFAULT; @@ -609,8 +617,9 @@ static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info, static int flash_part_info_gen3(struct switchtec_dev *stdev, struct switchtec_ioctl_flash_part_info *info) { - struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; - struct sys_info_regs __iomem *si = stdev->mmio_sys_info; + struct flash_info_regs_gen3 __iomem *fi = + &stdev->mmio_flash_info->gen3; + struct sys_info_regs_gen3 __iomem *si = &stdev->mmio_sys_info->gen3; u32 active_addr = -1; switch (info->flash_partition) { @@ -1382,6 +1391,7 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, int rc; void __iomem *map; unsigned long res_start, res_len; + u32 __iomem *part_id; rc = pcim_enable_device(pdev); if (rc) @@ -1416,7 +1426,13 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET; stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET; stdev->mmio_ntb = stdev->mmio + SWITCHTEC_GAS_NTB_OFFSET; - stdev->partition = ioread8(&stdev->mmio_sys_info->partition_id); + + if (stdev->gen == SWITCHTEC_GEN3) + part_id = &stdev->mmio_sys_info->gen3.partition_id; + else + return -ENOTSUPP; + + stdev->partition = ioread8(part_id); stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count); stdev->mmio_part_cfg_all = stdev->mmio + SWITCHTEC_GAS_PART_CFG_OFFSET; stdev->mmio_part_cfg = &stdev->mmio_part_cfg_all[stdev->partition]; diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index d0b5816549ed..32a20d10b02e 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -108,10 +108,7 @@ enum { SWITCHTEC_GEN3_IMG1_RUNNING = 0x07, }; -struct sys_info_regs { - u32 device_id; - u32 device_version; - u32 firmware_version; +struct sys_info_regs_gen3 { u32 reserved1; u32 vendor_table_revision; u32 table_format_version; @@ -128,26 +125,36 @@ struct sys_info_regs { u8 component_revision; } __packed; -struct flash_info_regs { +struct sys_info_regs { + u32 device_id; + u32 device_version; + u32 firmware_version; + union { + struct sys_info_regs_gen3 gen3; + }; +} __packed; + +struct partition_info { + u32 address; + u32 length; +}; + +struct flash_info_regs_gen3 { u32 flash_part_map_upd_idx; - struct active_partition_info { + struct active_partition_info_gen3 { u32 address; u32 build_version; u32 build_string; } active_img; - struct active_partition_info active_cfg; - struct active_partition_info inactive_img; - struct active_partition_info inactive_cfg; + struct active_partition_info_gen3 active_cfg; + struct active_partition_info_gen3 inactive_img; + struct active_partition_info_gen3 inactive_cfg; u32 flash_length; - struct partition_info { - u32 address; - u32 length; - } cfg0; - + struct partition_info cfg0; struct partition_info cfg1; struct partition_info img0; struct partition_info img1; @@ -155,6 +162,12 @@ struct flash_info_regs { struct partition_info vendor[8]; }; +struct flash_info_regs { + union { + struct flash_info_regs_gen3 gen3; + }; +}; + enum { SWITCHTEC_NTB_REG_INFO_OFFSET = 0x0000, SWITCHTEC_NTB_REG_CTRL_OFFSET = 0x4000, -- cgit v1.2.3 From a3321ca394082f403b447646d81c18ff6b39f4a6 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 14 Jan 2020 20:56:46 -0700 Subject: PCI/switchtec: Add Gen4 system info register support Add the Gen4-specific system info registers and ensure their usage is guarded by a check on the device's generation. Link: https://lore.kernel.org/r/20200115035648.2578-6-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 5 +++++ include/linux/switchtec.h | 43 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'include') diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index c4dae075a692..b09c3f53a552 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -321,6 +321,9 @@ static ssize_t field ## _show(struct device *dev, \ if (stdev->gen == SWITCHTEC_GEN3) \ return io_string_show(buf, &si->gen3.field, \ sizeof(si->gen3.field)); \ + else if (stdev->gen == SWITCHTEC_GEN4) \ + return io_string_show(buf, &si->gen4.field, \ + sizeof(si->gen4.field)); \ else \ return -ENOTSUPP; \ } \ @@ -1429,6 +1432,8 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, if (stdev->gen == SWITCHTEC_GEN3) part_id = &stdev->mmio_sys_info->gen3.partition_id; + else if (stdev->gen == SWITCHTEC_GEN4) + part_id = &stdev->mmio_sys_info->gen4.partition_id; else return -ENOTSUPP; diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index 32a20d10b02e..d012520e5cc5 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -34,6 +34,7 @@ enum { enum switchtec_gen { SWITCHTEC_GEN3, + SWITCHTEC_GEN4, }; struct mrpc_regs { @@ -125,12 +126,54 @@ struct sys_info_regs_gen3 { u8 component_revision; } __packed; +struct sys_info_regs_gen4 { + u16 gas_layout_ver; + u8 evlist_ver; + u8 reserved1; + u16 mgmt_cmd_set_ver; + u16 fabric_cmd_set_ver; + u32 reserved2[2]; + u8 mrpc_uart_ver; + u8 mrpc_twi_ver; + u8 mrpc_eth_ver; + u8 mrpc_inband_ver; + u32 reserved3[7]; + u32 fw_update_tmo; + u32 xml_version_cfg; + u32 xml_version_img; + u32 partition_id; + u16 bl2_running; + u16 cfg_running; + u16 img_running; + u16 key_running; + u32 reserved4[43]; + u32 vendor_seeprom_twi; + u32 vendor_table_revision; + u32 vendor_specific_info[2]; + u16 p2p_vendor_id; + u16 p2p_device_id; + u8 p2p_revision_id; + u8 reserved5[3]; + u32 p2p_class_id; + u16 subsystem_vendor_id; + u16 subsystem_id; + u32 p2p_serial_number[2]; + u8 mac_addr[6]; + u8 reserved6[2]; + u32 reserved7[3]; + char vendor_id[8]; + char product_id[24]; + char product_revision[2]; + u16 reserved8; +} __packed; + struct sys_info_regs { u32 device_id; u32 device_version; u32 firmware_version; union { struct sys_info_regs_gen3 gen3; + struct sys_info_regs_gen4 gen4; }; } __packed; -- cgit v1.2.3 From 4efa1d2e36976d7b26f2e67f4c838330fbc91299 Mon Sep 17 00:00:00 2001 From: Kelvin Cao Date: Tue, 14 Jan 2020 20:56:47 -0700 Subject: PCI/switchtec: Add Gen4 flash information interface support Add the new flash_info registers struct and the implementation of ioctl_flash_part_info() for the new Gen4 hardware. [logang@deltatee.com: rewrote commit message] Link: https://lore.kernel.org/r/20200115035648.2578-7-logang@deltatee.com Signed-off-by: Kelvin Cao Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 111 +++++++++++++++++++++++++++++++++++ include/linux/switchtec.h | 52 ++++++++++++++++ include/uapi/linux/switchtec_ioctl.h | 8 +++ 3 files changed, 171 insertions(+) (limited to 'include') diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index b09c3f53a552..af85d232d200 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -600,6 +600,9 @@ static int ioctl_flash_info(struct switchtec_dev *stdev, if (stdev->gen == SWITCHTEC_GEN3) { info.flash_length = ioread32(&fi->gen3.flash_length); info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; + } else if (stdev->gen == SWITCHTEC_GEN4) { + info.flash_length = ioread32(&fi->gen4.flash_length); + info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN4; } else { return -ENOTSUPP; } @@ -687,6 +690,110 @@ static int flash_part_info_gen3(struct switchtec_dev *stdev, return 0; } +static int flash_part_info_gen4(struct switchtec_dev *stdev, + struct switchtec_ioctl_flash_part_info *info) +{ + struct flash_info_regs_gen4 __iomem *fi = &stdev->mmio_flash_info->gen4; + struct sys_info_regs_gen4 __iomem *si = &stdev->mmio_sys_info->gen4; + struct active_partition_info_gen4 __iomem *af = &fi->active_flag; + + switch (info->flash_partition) { + case SWITCHTEC_IOCTL_PART_MAP_0: + set_fw_info_part(info, &fi->map0); + break; + case SWITCHTEC_IOCTL_PART_MAP_1: + set_fw_info_part(info, &fi->map1); + break; + case SWITCHTEC_IOCTL_PART_KEY_0: + set_fw_info_part(info, &fi->key0); + if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_KEY_1: + set_fw_info_part(info, &fi->key1); + if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_BL2_0: + set_fw_info_part(info, &fi->bl2_0); + if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_BL2_1: + set_fw_info_part(info, &fi->bl2_1); + if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_CFG0: + set_fw_info_part(info, &fi->cfg0); + if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_CFG1: + set_fw_info_part(info, &fi->cfg1); + if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_IMG0: + set_fw_info_part(info, &fi->img0); + if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_IMG1: + set_fw_info_part(info, &fi->img1); + if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + 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; + } + + return 0; +} + static int ioctl_flash_part_info(struct switchtec_dev *stdev, struct switchtec_ioctl_flash_part_info __user *uinfo) { @@ -700,6 +807,10 @@ static int ioctl_flash_part_info(struct switchtec_dev *stdev, ret = flash_part_info_gen3(stdev, &info); if (ret) return ret; + } else if (stdev->gen == SWITCHTEC_GEN4) { + ret = flash_part_info_gen4(stdev, &info); + if (ret) + return ret; } else { return -ENOTSUPP; } diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index d012520e5cc5..e85155244135 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -109,6 +109,30 @@ enum { SWITCHTEC_GEN3_IMG1_RUNNING = 0x07, }; +enum { + SWITCHTEC_GEN4_MAP0_RUNNING = 0x00, + SWITCHTEC_GEN4_MAP1_RUNNING = 0x01, + SWITCHTEC_GEN4_KEY0_RUNNING = 0x02, + SWITCHTEC_GEN4_KEY1_RUNNING = 0x03, + SWITCHTEC_GEN4_BL2_0_RUNNING = 0x04, + SWITCHTEC_GEN4_BL2_1_RUNNING = 0x05, + SWITCHTEC_GEN4_CFG0_RUNNING = 0x06, + SWITCHTEC_GEN4_CFG1_RUNNING = 0x07, + SWITCHTEC_GEN4_IMG0_RUNNING = 0x08, + SWITCHTEC_GEN4_IMG1_RUNNING = 0x09, +}; + +enum { + SWITCHTEC_GEN4_KEY0_ACTIVE = 0, + SWITCHTEC_GEN4_KEY1_ACTIVE = 1, + SWITCHTEC_GEN4_BL2_0_ACTIVE = 0, + SWITCHTEC_GEN4_BL2_1_ACTIVE = 1, + SWITCHTEC_GEN4_CFG0_ACTIVE = 0, + SWITCHTEC_GEN4_CFG1_ACTIVE = 1, + SWITCHTEC_GEN4_IMG0_ACTIVE = 0, + SWITCHTEC_GEN4_IMG1_ACTIVE = 1, +}; + struct sys_info_regs_gen3 { u32 reserved1; u32 vendor_table_revision; @@ -205,9 +229,37 @@ struct flash_info_regs_gen3 { struct partition_info vendor[8]; }; +struct flash_info_regs_gen4 { + u32 flash_address; + u32 flash_length; + + struct active_partition_info_gen4 { + unsigned char bl2; + unsigned char cfg; + unsigned char img; + unsigned char key; + } active_flag; + + u32 reserved[3]; + + struct partition_info map0; + struct partition_info map1; + struct partition_info key0; + struct partition_info key1; + struct partition_info bl2_0; + struct partition_info bl2_1; + struct partition_info cfg0; + struct partition_info cfg1; + struct partition_info img0; + struct partition_info img1; + struct partition_info nvlog; + struct partition_info vendor[8]; +}; + struct flash_info_regs { union { struct flash_info_regs_gen3 gen3; + struct flash_info_regs_gen4 gen4; }; }; diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h index 4d09cfa2e9e6..2c661a3557e5 100644 --- a/include/uapi/linux/switchtec_ioctl.h +++ b/include/uapi/linux/switchtec_ioctl.h @@ -32,7 +32,15 @@ #define SWITCHTEC_IOCTL_PART_VENDOR5 10 #define SWITCHTEC_IOCTL_PART_VENDOR6 11 #define SWITCHTEC_IOCTL_PART_VENDOR7 12 +#define SWITCHTEC_IOCTL_PART_BL2_0 13 +#define SWITCHTEC_IOCTL_PART_BL2_1 14 +#define SWITCHTEC_IOCTL_PART_MAP_0 15 +#define SWITCHTEC_IOCTL_PART_MAP_1 16 +#define SWITCHTEC_IOCTL_PART_KEY_0 17 +#define SWITCHTEC_IOCTL_PART_KEY_1 18 + #define SWITCHTEC_NUM_PARTITIONS_GEN3 13 +#define SWITCHTEC_NUM_PARTITIONS_GEN4 19 /* obsolete: for compatibility with old userspace software */ #define SWITCHTEC_IOCTL_NUM_PARTITIONS SWITCHTEC_NUM_PARTITIONS_GEN3 -- cgit v1.2.3 From ce7c88600bf8ec0d12cf8e147907f96cd58eeb2e Mon Sep 17 00:00:00 2001 From: Kelvin Cao Date: Mon, 6 Jan 2020 12:03:35 -0700 Subject: PCI/switchtec: Add Gen4 MRPC GAS access permission check Gen4 hardware provides new MRPC commands to read and write directly from any address in the PCI BAR (which Microsemi refers to as GAS). Since accessing BARs can be dangerous and break the driver, we don't want unprivileged users to have this ability. Therefore, require CAP_SYS_ADMIN for the local and remote GAS access MRPC commands. Privileged processes will already have access to the BAR through the sysfs resource file so this doesn't give userspace any capabilities it didn't already have. [logang@deltatee.com: rework commit message] Link: https://lore.kernel.org/r/20200106190337.2428-11-logang@deltatee.com Signed-off-by: Kelvin Cao Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 6 ++++++ include/linux/switchtec.h | 5 +++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index af85d232d200..92b95e8067c0 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -479,6 +479,12 @@ static ssize_t switchtec_dev_write(struct file *filp, const char __user *data, rc = -EFAULT; goto out; } + if (((MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_WRITE) || + (MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_READ)) && + !capable(CAP_SYS_ADMIN)) { + rc = -EPERM; + goto out; + } data += sizeof(stuser->cmd); rc = copy_from_user(&stuser->data, data, size - sizeof(stuser->cmd)); diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index e85155244135..082f1d51957a 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -21,6 +21,11 @@ #define SWITCHTEC_EVENT_FATAL BIT(4) #define SWITCHTEC_DMA_MRPC_EN BIT(0) + +#define MRPC_GAS_READ 0x29 +#define MRPC_GAS_WRITE 0x87 +#define MRPC_CMD_ID(x) ((x) & 0xffff) + enum { SWITCHTEC_GAS_MRPC_OFFSET = 0x0000, SWITCHTEC_GAS_TOP_CFG_OFFSET = 0x1000, -- cgit v1.2.3 From 2856ba6020fc5cbf051d5a75b2abb3046072c144 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Tue, 21 Jan 2020 06:37:47 -0700 Subject: PCI: Introduce pci_real_dma_dev() The current DMA alias implementation requires the aliased device be on the same PCI bus as the requester ID. Add an arch-specific mechanism to point to another PCI device when doing mapping and PCI DMA alias search. The default case returns the actual device. Link: https://lore.kernel.org/r/1579613871-301529-4-git-send-email-jonathan.derrick@intel.com Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas Cc: Christoph Hellwig --- arch/x86/pci/common.c | 10 ++++++++++ drivers/pci/pci.c | 19 ++++++++++++++++++- drivers/pci/search.c | 6 ++++++ include/linux/pci.h | 1 + 4 files changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 1e59df041456..fe21a5c557e6 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -736,3 +736,13 @@ int pci_ext_cfg_avail(void) else return 0; } + +#if IS_ENABLED(CONFIG_VMD) +struct pci_dev *pci_real_dma_dev(struct pci_dev *dev) +{ + if (is_vmd(dev->bus)) + return to_pci_sysdata(dev->bus)->vmd_dev; + + return dev; +} +#endif diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e87196cc1a7f..ab2b8bcd29bf 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -6033,7 +6033,9 @@ bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2) return (dev1->dma_alias_mask && test_bit(dev2->devfn, dev1->dma_alias_mask)) || (dev2->dma_alias_mask && - test_bit(dev1->devfn, dev2->dma_alias_mask)); + test_bit(dev1->devfn, dev2->dma_alias_mask)) || + pci_real_dma_dev(dev1) == dev2 || + pci_real_dma_dev(dev2) == dev1; } bool pci_device_is_present(struct pci_dev *pdev) @@ -6057,6 +6059,21 @@ void pci_ignore_hotplug(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_ignore_hotplug); +/** + * pci_real_dma_dev - Get PCI DMA device for PCI device + * @dev: the PCI device that may have a PCI DMA alias + * + * Permits the platform to provide architecture-specific functionality to + * devices needing to alias DMA to another PCI device on another PCI bus. If + * the PCI device is on the same bus, it is recommended to use + * pci_add_dma_alias(). This is the default implementation. Architecture + * implementations can override this. + */ +struct pci_dev __weak *pci_real_dma_dev(struct pci_dev *dev) +{ + return dev; +} + resource_size_t __weak pcibios_default_alignment(void) { return 0; diff --git a/drivers/pci/search.c b/drivers/pci/search.c index bade14002fd8..efdb8b56035f 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -32,6 +32,12 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, struct pci_bus *bus; int ret; + /* + * The device may have an explicit alias requester ID for DMA where the + * requester is on another PCI bus. + */ + pdev = pci_real_dma_dev(pdev); + ret = fn(pdev, pci_dev_id(pdev), data); if (ret) return ret; diff --git a/include/linux/pci.h b/include/linux/pci.h index c393dff2d66f..7f4e80666972 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1202,6 +1202,7 @@ int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size); int pci_select_bars(struct pci_dev *dev, unsigned long flags); bool pci_device_is_present(struct pci_dev *pdev); void pci_ignore_hotplug(struct pci_dev *dev); +struct pci_dev *pci_real_dma_dev(struct pci_dev *dev); int __printf(6, 7) pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler, irq_handler_t thread_fn, void *dev_id, -- cgit v1.2.3