diff options
author | Linaro CI <ci_notify@linaro.org> | 2019-05-30 05:56:37 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2019-05-30 05:56:37 +0000 |
commit | 3f84c2f0e778b850bd89fcd69f0a251fc4631f8e (patch) | |
tree | ee1a4da0036af3a1fce44ed4689350388d923445 | |
parent | 59f36da81ff3bab33b35da114cd87ce10be25629 (diff) | |
parent | c32439c42d3681a46ae460fe889e695d546b1c7e (diff) |
Merge remote-tracking branch 'sdm845-pcie/tracking-qcomlt-sdm845-pcie' into integration-linux-qcomlt
-rw-r--r-- | Documentation/devicetree/bindings/pci/qcom,pcie.txt | 19 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt | 3 | ||||
-rw-r--r-- | arch/arm64/boot/dts/qcom/sdm845.dtsi | 103 | ||||
-rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 146 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp.c | 160 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 539 |
6 files changed, 970 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index 1fd703bd73e0..2cf92ed39499 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -10,6 +10,7 @@ - "qcom,pcie-msm8996" for msm8996 or apq8096 - "qcom,pcie-ipq4019" for ipq4019 - "qcom,pcie-ipq8074" for ipq8074 + - "qcom,pcie-sdm845" for sdm845 - reg: Usage: required @@ -116,6 +117,18 @@ - "ahb" AHB clock - "aux" Auxiliary clock +- clock-names: + Usage: required for sdm845 + Value type: <stringlist> + Definition: Should contain the following entries + - "aux" Auxiliary clock + - "cfg" Configuration clock + - "bus_master" Master AXI clock + - "bus_slave" Slave AXI clock + - "slave_q2a" Slave Q2A clock + - "tbu" PCIe TBU clock + - "pipe" PIPE clock + - resets: Usage: required Value type: <prop-encoded-array> @@ -167,6 +180,12 @@ - "ahb" AHB Reset - "axi_m_sticky" AXI Master Sticky reset +- reset-names: + Usage: required for sdm845 + Value type: <stringlist> + Definition: Should contain the following entries + - "pci" PCIe core reset + - power-domains: Usage: required for apq8084 and msm8996/apq8096 Value type: <prop-encoded-array> diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt index 085fbd676cfc..467394472094 100644 --- a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt @@ -12,6 +12,7 @@ Required properties: "qcom,msm8998-qmp-usb3-phy" for USB3 QMP V3 phy on msm8998, "qcom,msm8998-qmp-ufs-phy" for UFS QMP phy on msm8998, "qcom,msm8998-qmp-pcie-phy" for PCIe QMP phy on msm8998, + "qcom,sdm845-qmp-pcie-phy" for PCIe phy on sdm845, "qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845, "qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845, "qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845. @@ -77,6 +78,8 @@ Required properties: "ufsphy". For "qcom,msm8998-qmp-pcie-phy" must contain: "phy", "common". + For "qcom,sdm845-qmp-pcie-phy" must contain: + "phy". For "qcom,sdm845-qmp-usb3-phy" must contain: "phy", "common". For "qcom,sdm845-qmp-usb3-uni-phy" must contain: diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi index 289fdba9ad21..8a50f2c26826 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -1199,6 +1199,109 @@ }; }; + pcie0: pci@1c00000 { + compatible = "qcom,pcie-sdm845", "snps,dw-pcie"; + reg = <0 0x01c00000 0 0x2000>, + <0 0x60000000 0 0xf1d>, + <0 0x60000f20 0 0xa8>, + <0 0x60100000 0 0x100000>; + reg-names = "parf", "dbi", "elbi", "config"; + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x01000000 0x0 0x60200000 0 0x60200000 0x0 0x100000>, + <0x02000000 0x0 0x60300000 0 0x60300000 0x0 0xd00000>; + + interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 150 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 151 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 152 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + clocks = <&gcc GCC_PCIE_0_PIPE_CLK>, + <&gcc GCC_PCIE_0_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + clock-names = "pipe", + "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "tbu"; + + iommu-map = <0x0 &apps_smmu 0x1c10 0x1>, + <0x100 &apps_smmu 0x1c11 0x1>, + <0x200 &apps_smmu 0x1c12 0x1>, + <0x300 &apps_smmu 0x1c13 0x1>, + <0x400 &apps_smmu 0x1c14 0x1>, + <0x500 &apps_smmu 0x1c15 0x1>, + <0x600 &apps_smmu 0x1c16 0x1>, + <0x700 &apps_smmu 0x1c17 0x1>, + <0x800 &apps_smmu 0x1c18 0x1>, + <0x900 &apps_smmu 0x1c19 0x1>, + <0xa00 &apps_smmu 0x1c1a 0x1>, + <0xb00 &apps_smmu 0x1c1b 0x1>, + <0xc00 &apps_smmu 0x1c1c 0x1>, + <0xd00 &apps_smmu 0x1c1d 0x1>, + <0xe00 &apps_smmu 0x1c1e 0x1>, + <0xf00 &apps_smmu 0x1c1f 0x1>; + + resets = <&gcc GCC_PCIE_0_BCR>; + reset-names = "pci"; + + power-domains = <&gcc PCIE_0_GDSC>; + + phys = <&pcie0_lane>; + phy-names = "pciephy"; + + status = "disabled"; + }; + + pcie0_phy: phy@1c06000 { + compatible = "qcom,sdm845-qmp-pcie-phy"; + reg = <0 0x01c06000 0 0x18c>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_0_CLKREF_CLK>, + <&gcc GCC_PCIE_PHY_REFGEN_CLK>; + clock-names = "aux", "cfg_ahb", "ref", "refgen"; + + resets = <&gcc GCC_PCIE_0_PHY_BCR>; + reset-names = "phy"; + + assigned-clocks = <&gcc GCC_PCIE_PHY_REFGEN_CLK>; + assigned-clock-rates = <100000000>; + + status = "disabled"; + + pcie0_lane: lanes@1c06200 { + reg = <0 0x01c06200 0 0x128>, + <0 0x01c06400 0 0x1fc>, + <0 0x01c06800 0 0x218>, + <0 0x01c06600 0 0x70>; + clocks = <&gcc GCC_PCIE_0_PIPE_CLK>; + clock-names = "pipe0"; + + #phy-cells = <0>; + clock-output-names = "pcie_0_pipe_clk"; + }; + }; + ufs_mem_hc: ufshc@1d84000 { compatible = "qcom,sdm845-ufshc", "qcom,ufshc", "jedec,ufs-2.0"; diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 0ed235d560e3..b425e5b4729e 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -54,6 +54,7 @@ #define PCIE20_PARF_LTSSM 0x1B0 #define PCIE20_PARF_SID_OFFSET 0x234 #define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C +#define PCIE20_PARF_DEVICE_TYPE 0x1000 #define PCIE20_ELBI_SYS_CTRL 0x04 #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) @@ -80,6 +81,8 @@ #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 #define SLV_ADDR_SPACE_SZ 0x10000000 +#define DEVICE_TYPE_RC 0x4 + #define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 struct qcom_pcie_resources_2_1_0 { struct clk *iface_clk; @@ -139,12 +142,21 @@ struct qcom_pcie_resources_2_3_3 { struct reset_control *rst[7]; }; +struct qcom_pcie_resources_2_7_0 { + struct clk_bulk_data clks[6]; + struct regulator_bulk_data supplies[2]; + + struct reset_control *pci_reset; + struct clk *pipe_clk; +}; + union qcom_pcie_resources { struct qcom_pcie_resources_1_0_0 v1_0_0; struct qcom_pcie_resources_2_1_0 v2_1_0; struct qcom_pcie_resources_2_3_2 v2_3_2; struct qcom_pcie_resources_2_3_3 v2_3_3; struct qcom_pcie_resources_2_4_0 v2_4_0; + struct qcom_pcie_resources_2_7_0 v2_7_0; }; struct qcom_pcie; @@ -1076,6 +1088,129 @@ err_clk_iface: return ret; } +static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int ret; + + res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); + if (IS_ERR(res->pci_reset)) + return PTR_ERR(res->pci_reset); + + res->supplies[0].supply = "vdda"; + res->supplies[1].supply = "vddpe-3v3"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), + res->supplies); + if (ret) + return ret; + + res->clks[0].id = "aux"; + res->clks[1].id = "cfg"; + res->clks[2].id = "bus_master"; + res->clks[3].id = "bus_slave"; + res->clks[4].id = "slave_q2a"; + res->clks[5].id = "tbu"; + + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); + if (ret < 0) + return ret; + + res->pipe_clk = devm_clk_get(dev, "pipe"); + return PTR_ERR_OR_ZERO(res->pipe_clk); +} + +static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + u32 val; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); + if (ret < 0) { + dev_err(dev, "cannot enable regulators\n"); + return ret; + } + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); + if (ret < 0) + goto err_disable_regulators; + + ret = reset_control_assert(res->pci_reset); + if (ret) { + dev_err(dev, "cannot deassert pci reset\n"); + return ret; + } + + msleep(10); + + ret = reset_control_deassert(res->pci_reset); + if (ret) { + dev_err(dev, "cannot deassert pci reset\n"); + return ret; + } + + clk_prepare_enable(res->pipe_clk); + + /* configure PCIe to RC mode */ + writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE); + + /* enable PCIe clocks and resets */ + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~BIT(0); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + + /* change DBI base address */ + writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + + /* MAC PHY_POWERDOWN MUX DISABLE */ + val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); + val &= ~BIT(29); + writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); + + val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + val |= BIT(4); + writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + val |= BIT(31); + writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + } + + return 0; + +err_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); + + return ret; +} + +static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); +} + +static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + return clk_prepare_enable(res->pipe_clk); +} + +static void qcom_pcie_post_deinit_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + clk_disable_unprepare(res->pipe_clk); +} + static int qcom_pcie_link_up(struct dw_pcie *pci) { u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA); @@ -1175,6 +1310,16 @@ static const struct qcom_pcie_ops ops_2_3_3 = { .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, }; +/* Qcom IP rev.: 2.7.0 Synopsys IP rev.: 4.30a */ +static const struct qcom_pcie_ops ops_2_7_0 = { + .get_resources = qcom_pcie_get_resources_2_7_0, + .init = qcom_pcie_init_2_7_0, + .deinit = qcom_pcie_deinit_2_7_0, + .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, + .post_init = qcom_pcie_post_init_2_7_0, + .post_deinit = qcom_pcie_post_deinit_2_7_0, +}; + static const struct dw_pcie_ops dw_pcie_ops = { .link_up = qcom_pcie_link_up, }; @@ -1289,6 +1434,7 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-msm8996", .data = &ops_2_3_2 }, { .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 }, { .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 }, + { .compatible = "qcom,pcie-sdm845", .data = &ops_2_7_0 }, { } }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index cd91b4179b10..012ad1050b5a 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -159,6 +159,11 @@ static const unsigned int qmp_v3_usb3phy_regs_layout[] = { [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, }; +static const unsigned int sdm845_pciephy_regs_layout[] = { + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_READY_STATUS] = 0x174, +}; + static const unsigned int sdm845_ufsphy_regs_layout[] = { [QPHY_START_CTRL] = 0x00, [QPHY_PCS_READY_STATUS] = 0x160, @@ -474,6 +479,109 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = { QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3), }; +static const struct qmp_phy_init_tbl sdm845_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x007), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15), +}; + +static const struct qmp_phy_init_tbl sdm845_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06), +}; + +static const struct qmp_phy_init_tbl sdm845_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x71), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_01, 0x59), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40), +}; + +static const struct qmp_phy_init_tbl sdm845_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xbb), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x0d), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG4, 0x00), +}; + +static const struct qmp_phy_init_tbl sdm845_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2, 0x52), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2, 0x10), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4, 0x1a), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5, 0x06), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1, 0x00), +}; + static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), @@ -895,6 +1003,8 @@ struct qmp_phy_cfg { int rx_tbl_num; const struct qmp_phy_init_tbl *pcs_tbl; int pcs_tbl_num; + const struct qmp_phy_init_tbl *pcs_misc_tbl; + int pcs_misc_tbl_num; /* clock ids to be requested */ const char * const *clk_list; @@ -1034,6 +1144,10 @@ static const char * const qmp_v3_phy_clk_l[] = { "aux", "cfg_ahb", "ref", "com_aux", }; +static const char * const sdm845_pciephy_clk_l[] = { + "aux", "cfg_ahb", "ref", "refgen", +}; + static const char * const sdm845_ufs_phy_clk_l[] = { "ref", "ref_aux", }; @@ -1047,6 +1161,10 @@ static const char * const msm8996_usb3phy_reset_l[] = { "phy", "common", }; +static const char * const sdm845_pciephy_reset_l[] = { + "phy", +}; + /* list of regulators */ static const char * const qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", @@ -1144,6 +1262,39 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .pwrdn_delay_max = 1005, /* us */ }; +static const struct qmp_phy_cfg sdm845_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 1, + + .serdes_tbl = sdm845_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdm845_pcie_serdes_tbl), + .tx_tbl = sdm845_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdm845_pcie_tx_tbl), + .rx_tbl = sdm845_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdm845_pcie_rx_tbl), + .pcs_tbl = sdm845_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdm845_pcie_pcs_tbl), + .pcs_misc_tbl = sdm845_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sdm845_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sdm845_pciephy_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .mask_com_pcs_ready = PCS_READY, + + .has_phy_com_ctrl = false, + .has_lane_rst = false, + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -1454,6 +1605,7 @@ static int qcom_qmp_phy_enable(struct phy *phy) void __iomem *tx = qphy->tx; void __iomem *rx = qphy->rx; void __iomem *pcs = qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; void __iomem *dp_com = qmp->dp_com; void __iomem *status; unsigned int mask, val; @@ -1524,6 +1676,11 @@ static int qcom_qmp_phy_enable(struct phy *phy) if (ret) goto err_lane_rst; + if (cfg->pcs_misc_tbl) { + qcom_qmp_phy_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl, + cfg->pcs_misc_tbl_num); + } + /* * Pull out PHY from POWER DOWN state. * This is active low enable signal to power-down PHY. @@ -1987,6 +2144,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { .compatible = "qcom,ipq8074-qmp-pcie-phy", .data = &ipq8074_pciephy_cfg, }, { + .compatible = "qcom,sdm845-qmp-pcie-phy", + .data = &sdm845_pciephy_cfg, + }, { .compatible = "qcom,sdm845-qmp-usb3-phy", .data = &qmp_v3_usb3phy_cfg, }, { diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index c2fe218e051f..38a56128c1ee 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -12,6 +12,8 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/acpi.h> +#include <linux/firmware.h> +#include <asm/unaligned.h> #include "xhci.h" #include "xhci-trace.h" @@ -279,6 +281,507 @@ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } #endif /* CONFIG_ACPI */ +static const struct renesas_fw_entry { + const char *firmware_name; + u16 device; + u8 revision; + u16 expected_version; +} renesas_fw_table[] = { + /* + * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A + * are listed in R19UH0078EJ0500 Rev.5.00 as devices which + * need the software loader. + * + * PP2U/ReleaseNote_USB3-201-202-FW.txt: + * + * Note: This firmware is for the following devices. + * - uPD720201 ES 2.0 sample whose revision ID is 2. + * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. + * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. + */ + { "K2013080.mem", 0x0014, 0x02, 0x2013 }, + { "K2013080.mem", 0x0014, 0x03, 0x2013 }, + { "K2013080.mem", 0x0015, 0x02, 0x2013 }, +}; + +static const struct renesas_fw_entry *renesas_needs_fw_dl(struct pci_dev *dev) +{ + const struct renesas_fw_entry *entry; + size_t i; + + /* This loader will only work with a RENESAS device. */ + if (!(dev->vendor == PCI_VENDOR_ID_RENESAS)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { + entry = &renesas_fw_table[i]; + if (entry->device == dev->device && + entry->revision == dev->revision) + return entry; + } + + return NULL; +} + +static int renesas_fw_download_image(struct pci_dev *dev, + const u32 *fw, + size_t step) +{ + size_t i; + int err; + u8 fw_status; + bool data0_or_data1; + + /* + * The hardware does alternate between two 32-bit pages. + * (This is because each row of the firmware is 8 bytes). + * + * for even steps we use DATA0, for odd steps DATA1. + */ + data0_or_data1 = (step & 1) == 1; + + /* step+1. Read "Set DATAX" and confirm it is cleared. */ + for (i = 0; i < 10000; i++) { + err = pci_read_config_byte(dev, 0xF5, &fw_status); + if (err) + return pcibios_err_to_errno(err); + if (!(fw_status & BIT(data0_or_data1))) + break; + + udelay(1); + } + if (i == 10000) + return -ETIMEDOUT; + + /* + * step+2. Write FW data to "DATAX". + * "LSB is left" => force little endian + */ + err = pci_write_config_dword(dev, data0_or_data1 ? 0xFC : 0xF8, + (__force u32) cpu_to_le32(fw[step])); + if (err) + return pcibios_err_to_errno(err); + + udelay(100); + + /* step+3. Set "Set DATAX". */ + err = pci_write_config_byte(dev, 0xF5, BIT(data0_or_data1)); + if (err) + return pcibios_err_to_errno(err); + + return 0; +} + +static int renesas_fw_verify(struct pci_dev *dev, + const void *fw_data, + size_t length) +{ + const struct renesas_fw_entry *entry = renesas_needs_fw_dl(dev); + u16 fw_version_pointer; + u16 fw_version; + + if (!entry) + return -EINVAL; + + /* + * The Firmware's Data Format is describe in + * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124 + */ + + /* "Each row is 8 bytes". => firmware size must be a multiple of 8. */ + if (length % 8 != 0) { + dev_err(&dev->dev, "firmware size is not a multipe of 8."); + return -EINVAL; + } + + /* + * The bootrom chips of the big brother have sizes up to 64k, let's + * assume that's the biggest the firmware can get. + */ + if (length < 0x1000 || length >= 0x10000) { + dev_err(&dev->dev, "firmware is size %zd is not (4k - 64k).", + length); + return -EINVAL; + } + + /* The First 2 bytes are fixed value (55aa). "LSB on Left" */ + if (get_unaligned_le16(fw_data) != 0x55aa) { + dev_err(&dev->dev, "no valid firmware header found."); + return -EINVAL; + } + + /* verify the firmware version position and print it. */ + fw_version_pointer = get_unaligned_le16(fw_data + 4); + if (fw_version_pointer + 2 >= length) { + dev_err(&dev->dev, "firmware version pointer is outside of the firmware image."); + return -EINVAL; + } + + fw_version = get_unaligned_le16(fw_data + fw_version_pointer); + dev_dbg(&dev->dev, "got firmware version: %02x.", fw_version); + + if (fw_version != entry->expected_version) { + dev_err(&dev->dev, "firmware version mismatch, expected version: %02x.", + entry->expected_version); + return -EINVAL; + } + + return 0; +} + +static int renesas_fw_check_running(struct pci_dev *pdev) +{ + int err; + u8 fw_state; + + /* + * Test if the device is actually needing the firmware. As most + * BIOSes will initialize the device for us. If the device is + * initialized. + */ + err = pci_read_config_byte(pdev, 0xF4, &fw_state); + if (err) + return pcibios_err_to_errno(err); + + /* + * Check if "FW Download Lock" is locked. If it is and the FW is + * ready we can simply continue. If the FW is not ready, we have + * to give up. + */ + if (fw_state & BIT(1)) { + dev_dbg(&pdev->dev, "FW Download Lock is engaged."); + + if (fw_state & BIT(4)) + return 0; + + dev_err(&pdev->dev, "FW Download Lock is set and FW is not ready. Giving Up."); + return -EIO; + } + + /* + * Check if "FW Download Enable" is set. If someone (us?) tampered + * with it and it can't be resetted, we have to give up too... and + * ask for a forgiveness and a reboot. + */ + if (fw_state & BIT(0)) { + dev_err(&pdev->dev, "FW Download Enable is stale. Giving Up (poweroff/reboot needed)."); + return -EIO; + } + + /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */ + switch ((fw_state & 0x70)) { + case 0: /* No result yet */ + dev_dbg(&pdev->dev, "FW is not ready/loaded yet."); + + /* tell the caller, that this device needs the firmware. */ + return 1; + + case BIT(4): /* Success, device should be working. */ + dev_dbg(&pdev->dev, "FW is ready."); + return 0; + + case BIT(5): /* Error State */ + dev_err(&pdev->dev, "hardware is in an error state. Giving up (poweroff/reboot needed)."); + return -ENODEV; + + default: /* All other states are marked as "Reserved states" */ + dev_err(&pdev->dev, "hardware is in an invalid state %x. Giving up (poweroff/reboot needed).", + (fw_state & 0x70) >> 4); + return -EINVAL; + } +} + +static int renesas_hw_check_run_stop_busy(struct pci_dev *pdev) +{ +#if 0 + u32 val; + + /* + * 7.1.3 Note 3: "... must not set 'FW Download Enable' when + * 'RUN/STOP' of USBCMD Register is set" + */ + val = readl(hcd->regs + 0x20); + if (val & BIT(0)) { + dev_err(&pdev->dev, "hardware is busy and can't receive a FW."); + return -EBUSY; + } +#endif + return 0; +} + +static int renesas_fw_download(struct pci_dev *pdev, + const struct firmware *fw, unsigned int retry_counter) +{ + const u32 *fw_data = (const u32 *) fw->data; + size_t i; + int err; + u8 fw_status; + + /* + * For more information and the big picture: please look at the + * "Firmware Download Sequence" in "7.1 FW Download Interface" + * of R19UH0078EJ0500 Rev.5.00 page 131 + */ + err = renesas_hw_check_run_stop_busy(pdev); + if (err) + return err; + + /* + * 0. Set "FW Download Enable" bit in the + * "FW Download Control & Status Register" at 0xF4 + */ + err = pci_write_config_byte(pdev, 0xF4, BIT(0)); + if (err) + return pcibios_err_to_errno(err); + + /* 1 - 10 follow one step after the other. */ + for (i = 0; i < fw->size / 4; i++) { + err = renesas_fw_download_image(pdev, fw_data, i); + if (err) { + dev_err(&pdev->dev, "Firmware Download Step %zd failed at position %zd bytes with (%d).", + i, i * 4, err); + return err; + } + } + + /* + * This sequence continues until the last data is written to + * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1" + * is cleared by the hardware beforehand. + */ + for (i = 0; i < 10000; i++) { + err = pci_read_config_byte(pdev, 0xF5, &fw_status); + if (err) + return pcibios_err_to_errno(err); + if (!(fw_status & (BIT(0) | BIT(1)))) + break; + + udelay(1); + } + if (i == 10000) + dev_warn(&pdev->dev, "Final Firmware Download step timed out."); + + /* + * 11. After finishing writing the last data of FW, the + * System Software must clear "FW Download Enable" + */ + err = pci_write_config_byte(pdev, 0xF4, 0); + if (err) + return pcibios_err_to_errno(err); + + /* 12. Read "Result Code" and confirm it is good. */ + for (i = 0; i < 10000; i++) { + err = pci_read_config_byte(pdev, 0xF4, &fw_status); + if (err) + return pcibios_err_to_errno(err); + if (fw_status & BIT(4)) + break; + + udelay(1); + } + if (i == 10000) { + /* Timed out / Error - let's see if we can fix this */ + err = renesas_fw_check_running(pdev); + switch (err) { + case 0: /* + * we shouldn't end up here. + * maybe it took a little bit longer. + * But all should be well? + */ + break; + + case 1: /* (No result yet? - we can try to retry) */ + if (retry_counter < 10) { + retry_counter++; + dev_warn(&pdev->dev, "Retry Firmware download: %d try.", + retry_counter); + return renesas_fw_download(pdev, fw, + retry_counter); + } + return -ETIMEDOUT; + + default: + return err; + } + } + /* + * Optional last step: Engage Firmware Lock + * + * err = pci_write_config_byte(pdev, 0xF4, BIT(2)); + * if (err) + * return pcibios_err_to_errno(err); + */ + + return 0; +} + +struct renesas_fw_ctx { + struct pci_dev *pdev; + const struct pci_device_id *id; + bool resume; +}; + +static int xhci_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id); + +static void renesas_fw_callback(const struct firmware *fw, + void *context) +{ + struct renesas_fw_ctx *ctx = context; + struct pci_dev *pdev = ctx->pdev; + struct device *parent = pdev->dev.parent; + int err = -ENOENT; + + if (fw) { + err = renesas_fw_verify(pdev, fw->data, fw->size); + if (!err) { + err = renesas_fw_download(pdev, fw, 0); + release_firmware(fw); + if (!err) { + if (ctx->resume) + return; + + err = xhci_pci_probe(pdev, ctx->id); + if (!err) { + /* everything worked */ + devm_kfree(&pdev->dev, ctx); + return; + } + + /* in case of an error - fall through */ + } else { + dev_err(&pdev->dev, "firmware failed to download (%d).", + err); + } + } + } else { + dev_err(&pdev->dev, "firmware failed to load (%d).", err); + } + + dev_info(&pdev->dev, "Unloading driver"); + + if (parent) + device_lock(parent); + + device_release_driver(&pdev->dev); + + if (parent) + device_unlock(parent); + + pci_dev_put(pdev); +} + +static int renesas_fw_alive_check(struct pci_dev *pdev) +{ + const struct renesas_fw_entry *entry; + int err; + + /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ + entry = renesas_needs_fw_dl(pdev); + if (!entry) + return 0; + + err = renesas_fw_check_running(pdev); + /* Also go ahead, if the firmware is running */ + if (err == 0) + return 0; + + /* At this point, we can be sure that the FW isn't ready. */ + return err; +} + +static int renesas_fw_download_to_hw(struct pci_dev *pdev, + const struct pci_device_id *id, + bool do_resume) +{ + const struct renesas_fw_entry *entry; + struct renesas_fw_ctx *ctx; + int err; + + /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ + entry = renesas_needs_fw_dl(pdev); + if (!entry) + return 0; + + err = renesas_fw_check_running(pdev); + /* Continue ahead, if the firmware is already running. */ + if (err == 0) + return 0; + + if (err != 1) + return err; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + ctx->pdev = pdev; + ctx->resume = do_resume; + ctx->id = id; + + pci_dev_get(pdev); + err = request_firmware_nowait(THIS_MODULE, 1, entry->firmware_name, + &pdev->dev, GFP_KERNEL, ctx, renesas_fw_callback); + if (err) { + pci_dev_put(pdev); + return err; + } + + /* + * The renesas_fw_callback() callback will continue the probe + * process, once it aquires the firmware. + */ + return 1; +} + +static int renesas_check_if_fw_dl_is_needed(struct pci_dev *pdev) +{ + int err; + u8 fw_state; + + /* + * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A + * are listed in R19UH0078EJ0500 Rev.5.00 as devices which + * need a firmware in order to work. + * + * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. + * - uPD720201 ES 2.0 sample whose revision ID is 2. + * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. + */ + if (!((pdev->vendor == PCI_VENDOR_ID_RENESAS) && + ((pdev->device == 0x0015 && pdev->revision == 0x02) || + (pdev->device == 0x0014 && + (pdev->revision == 0x02 || pdev->revision == 0x03))))) + return 0; + + /* + * Test if the firmware was uploaded and is running. + * As most BIOSes will initialize the device for us. + */ + err = pci_read_config_byte(pdev, 0xf4, &fw_state); + if (err) + return pcibios_err_to_errno(err); + + /* Check the "Result Code" Bits (6:4) and act accordingly */ + switch (fw_state & 0x70) { + case 0: /* No result yet */ + dev_err(&pdev->dev, "FW is not ready/loaded yet."); + return -ENODEV; + + case BIT(4): /* Success, device should be working. */ + dev_dbg(&pdev->dev, "FW is ready."); + return 0; + + case BIT(5): /* Error State */ + dev_err(&pdev->dev, "HW is in an error state."); + return -ENODEV; + + default: /* All other states are marked as "Reserved states" */ + dev_err(&pdev->dev, "HW is in an invalid state (%x).", + (fw_state & 0x70) >> 4); + return -EINVAL; + } +} + /* called during probe() after chip reset completes */ static int xhci_pci_setup(struct usb_hcd *hcd) { @@ -317,6 +820,27 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) struct hc_driver *driver; struct usb_hcd *hcd; + /* + * Check if this device is a RENESAS uPD720201/2 device. + * Otherwise, we can continue with xhci_pci_probe as usual. + */ + retval = renesas_fw_download_to_hw(dev, id, false); + switch (retval) { + case 0: + break; + + case 1: /* let it load the firmware and recontinue the probe. */ + return 0; + + default: + return retval; + }; + + /* Check if this device is a RENESAS uPD720201/2 device. */ + retval = renesas_check_if_fw_dl_is_needed(dev); + if (retval) + return retval; + driver = (struct hc_driver *)id->driver_data; /* Prevent runtime suspending between USB-2 and USB-3 initialization */ @@ -381,6 +905,16 @@ static void xhci_pci_remove(struct pci_dev *dev) { struct xhci_hcd *xhci; + if (renesas_fw_alive_check(dev)) { + /* + * bail out early, if this was a renesas device w/o FW. + * Else we might hit the NMI watchdog in xhci_handsake + * during xhci_reset as part of the driver's unloading. + * which we forced in the renesas_fw_callback(). + */ + return; + } + xhci = hcd_to_xhci(pci_get_drvdata(dev)); xhci->xhc_state |= XHCI_STATE_REMOVING; @@ -508,6 +1042,11 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) if (pdev->vendor == PCI_VENDOR_ID_INTEL) usb_enable_intel_xhci_ports(pdev); + /* Check if this device is a RENESAS uPD720201/2 device. */ + retval = renesas_check_if_fw_dl_is_needed(pdev); + if (retval) + return retval; + if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) xhci_ssic_port_unused_quirk(hcd, false); |