diff --git a/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-usb3-28lp.c b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-usb3-28lp.c
index 15b17044fde3..810121508634 100644
--- a/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-usb3-28lp.c
+++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-usb3-28lp.c
@@ -14,15 +14,22 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/units.h>
-#define RST_ASSERT_DELAY_MIN_US 100
-#define RST_ASSERT_DELAY_MAX_US 150
-#define PIPE_CLK_DELAY_MIN_US 5000
-#define PIPE_CLK_DELAY_MAX_US 5100
-#define CLK_EN_DELAY_MIN_US 30
-#define CLK_EN_DELAY_MAX_US 50
+#define PCIE_RST_ASSERT_DELAY_MIN_US 100
+#define PCIE_RST_ASSERT_DELAY_MAX_US 150
+#define PCIE_PIPE_CLK_DELAY_MIN_US 5000
+#define PCIE_PIPE_CLK_DELAY_MAX_US 5100
+#define PCIE_CLK_EN_DELAY_MIN_US 30
+#define PCIE_CLK_EN_DELAY_MAX_US 50
+
+#define USB3_RST_ASSERT_DELAY_MIN_US 1
+#define USB3_RST_ASSERT_DELAY_MAX_US 5
+#define USB3_CLK_EN_DELAY_MIN_US 35
+#define USB3_CLK_EN_DELAY_MAX_US 40
+
#define CDR_CTRL_REG_1 0x80
#define CDR_CTRL_REG_2 0x84
#define CDR_CTRL_REG_3 0x88
@@ -42,10 +49,13 @@
#define PHY_CFG_EIOS_DTCT_REG 0x3e4
#define PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME 0x3e8
+#define TCSR_USB_MUX_SEL BIT(0)
+
+#define PHY_IS_USB3(phy) ((phy)->data->phy_type == PHY_TYPE_USB3)
+
enum qcom_uniphy_type {
PHY_TYPE_PCIE = 1,
- PHY_TYPE_PCIE_GEN2,
- PHY_TYPE_PCIE_GEN3,
+ PHY_TYPE_USB3,
};
struct qcom_uniphy_regs {@@ -71,6 +81,9 @@ struct qcom_uniphy {
int num_resets;
void __iomem *base;
int lanes;
+ struct regulator *vreg;
+ struct regmap *tcsr;
+ unsigned int mux_offset;
};
#define PHY_INIT_CFG(o, v) \@@ -92,6 +105,14 @@ static const struct qcom_uniphy_regs ipq5018_pcie_regs[] = {
PHY_INIT_CFG(PCS_INTERNAL_CONTROL_2, 0xf101),
};
+static const struct qcom_uniphy_regs ipq5018_usb3_regs[] = {
+ PHY_INIT_CFG(SSCG_CTRL_REG_4, 0x1cb9),
+ PHY_INIT_CFG(SSCG_CTRL_REG_5, 0x023a),
+ PHY_INIT_CFG(SSCG_CTRL_REG_3, 0xd360),
+ PHY_INIT_CFG(SSCG_CTRL_REG_1, 0x1),
+ PHY_INIT_CFG(SSCG_CTRL_REG_2, 0xeb),
+};
+
static const struct qcom_uniphy_regs ipq5332_pcie_regs[] = {
PHY_INIT_CFG(PHY_CFG_PLLCFG, 0x30),
PHY_INIT_CFG(PHY_CFG_EIOS_DTCT_REG, 0x53ef),@@ -100,15 +121,23 @@ static const struct qcom_uniphy_regs ipq5332_pcie_regs[] = {
static const struct qcom_uniphy_data ipq5018_pcie_data = {
.lane_offset = 0x800,
- .phy_type = PHY_TYPE_PCIE_GEN2,
+ .phy_type = PHY_TYPE_PCIE,
.init_seq = ipq5018_pcie_regs,
.init_seq_num = ARRAY_SIZE(ipq5018_pcie_regs),
.pipe_clk_rate = 125 * MEGA,
};
+static const struct qcom_uniphy_data ipq5018_usb3_data = {
+ .lane_offset = 0x800,
+ .phy_type = PHY_TYPE_USB3,
+ .init_seq = ipq5018_usb3_regs,
+ .init_seq_num = ARRAY_SIZE(ipq5018_usb3_regs),
+ .pipe_clk_rate = 250 * MEGA,
+};
+
static const struct qcom_uniphy_data ipq5332_pcie_data = {
.lane_offset = 0x800,
- .phy_type = PHY_TYPE_PCIE_GEN3,
+ .phy_type = PHY_TYPE_PCIE,
.init_seq = ipq5332_pcie_regs,
.init_seq_num = ARRAY_SIZE(ipq5332_pcie_regs),
.pipe_clk_rate = 250 * MEGA,@@ -151,7 +180,7 @@ static int qcom_uniphy_pcie_power_on(struct phy *x)
return ret;
}
- usleep_range(RST_ASSERT_DELAY_MIN_US, RST_ASSERT_DELAY_MAX_US);
+ usleep_range(PCIE_RST_ASSERT_DELAY_MIN_US, PCIE_RST_ASSERT_DELAY_MAX_US);
ret = reset_control_bulk_deassert(phy->num_resets, phy->resets);
if (ret) {@@ -159,7 +188,7 @@ static int qcom_uniphy_pcie_power_on(struct phy *x)
return ret;
}
- usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US);
+ usleep_range(PCIE_PIPE_CLK_DELAY_MIN_US, PCIE_PIPE_CLK_DELAY_MAX_US);
ret = clk_bulk_prepare_enable(phy->num_clks, phy->clks);
if (ret) {@@ -168,13 +197,90 @@ static int qcom_uniphy_pcie_power_on(struct phy *x)
return ret;
}
- usleep_range(CLK_EN_DELAY_MIN_US, CLK_EN_DELAY_MAX_US);
+ usleep_range(PCIE_CLK_EN_DELAY_MIN_US, PCIE_CLK_EN_DELAY_MAX_US);
qcom_uniphy_pcie_usb3_init(phy);
return 0;
}
+static int qcom_uniphy_usb3_power_off(struct phy *x)
+{
+ struct qcom_uniphy *phy = phy_get_drvdata(x);
+
+ clk_bulk_disable_unprepare(phy->num_clks, phy->clks);
+
+ reset_control_bulk_assert(phy->num_resets, phy->resets);
+
+ regulator_disable(phy->vreg);
+
+ return 0;
+}
+
+static int qcom_uniphy_usb3_power_on(struct phy *x)
+{
+ struct qcom_uniphy *phy = phy_get_drvdata(x);
+ int ret;
+
+ ret = regulator_enable(phy->vreg);
+ if (ret) {
+ dev_err(phy->dev, "failed to enable regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_bulk_assert(phy->num_resets, phy->resets);
+ if (ret) {
+ dev_err(phy->dev, "reset assert failed (%d)\n", ret);
+ goto err_disable_regulator;
+ }
+
+ usleep_range(USB3_RST_ASSERT_DELAY_MIN_US, USB3_RST_ASSERT_DELAY_MAX_US);
+
+ ret = reset_control_bulk_deassert(phy->num_resets, phy->resets);
+ if (ret) {
+ dev_err(phy->dev, "reset deassert failed (%d)\n", ret);
+ goto err_disable_regulator;
+ }
+
+ ret = clk_bulk_prepare_enable(phy->num_clks, phy->clks);
+ if (ret) {
+ dev_err(phy->dev, "clk prepare and enable failed %d\n", ret);
+ reset_control_bulk_assert(phy->num_resets, phy->resets);
+ goto err_disable_regulator;
+ }
+
+ usleep_range(USB3_CLK_EN_DELAY_MIN_US, USB3_CLK_EN_DELAY_MAX_US);
+
+ qcom_uniphy_pcie_usb3_init(phy);
+
+ return 0;
+
+err_disable_regulator:
+ regulator_disable(phy->vreg);
+
+ return ret;
+}
+
+static inline int qcom_uniphy_usb3_get_resources(struct platform_device *pdev,
+ struct qcom_uniphy *phy)
+{
+ struct device *dev = phy->dev;
+
+ phy->vreg = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(phy->vreg))
+ return dev_err_probe(dev, PTR_ERR(phy->vreg), "failed to get regulator\n");
+
+ phy->tcsr = syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "qcom,phy-usb-mux-sel",
+ 1, &phy->mux_offset);
+ if (IS_ERR(phy->tcsr))
+ return dev_err_probe(dev, PTR_ERR(phy->tcsr), "failed to get regmap\n");
+
+ phy->lanes = 1;
+
+ return 0;
+}
+
static inline int qcom_uniphy_pcie_usb3_get_resources(struct platform_device *pdev,
struct qcom_uniphy *phy)
{@@ -206,6 +312,13 @@ static inline int qcom_uniphy_pcie_usb3_get_resources(struct platform_device *pd
phy->num_resets = count;
+ if (PHY_IS_USB3(phy))
+ return qcom_uniphy_usb3_get_resources(pdev, phy);
+
+ ret = of_property_read_u32(dev_of_node(dev), "num-lanes", &phy->lanes);
+ if (ret)
+ return dev_err_probe(dev, ret, "Couldn't read num-lanes\n");
+
return 0;
}
@@ -233,7 +346,8 @@ static inline int phy_pipe_clk_register(struct qcom_uniphy *phy, int id)
struct clk_hw *hw;
char name[64];
- snprintf(name, sizeof(name), "phy%d_pipe_clk_src", id);
+ snprintf(name, sizeof(name), "%sphy%d_pipe_clk_src",
+ (PHY_IS_USB3(phy)) ? "usb" : "", id);
hw = devm_clk_hw_register_fixed_rate(phy->dev, name, NULL, 0,
data->pipe_clk_rate);
if (IS_ERR(hw))
@@ -243,10 +357,19 @@ static inline int phy_pipe_clk_register(struct qcom_uniphy *phy, int id)
return devm_of_clk_add_hw_provider(phy->dev, of_clk_hw_simple_get, hw);
}
+static int qcom_uniphy_usb_mux_select(struct qcom_uniphy *phy, bool enable)
+{
+ return regmap_assign_bits(phy->tcsr, phy->mux_offset,
+ TCSR_USB_MUX_SEL, enable);
+}
+
static const struct of_device_id qcom_uniphy_pcie_usb3_id_table[] = {
{
.compatible = "qcom,ipq5018-uniphy-pcie-phy",
.data = &ipq5018_pcie_data,
+ }, {
+ .compatible = "qcom,ipq5018-uniphy-usb3-phy",
+ .data = &ipq5018_usb3_data,
}, {
.compatible = "qcom,ipq5332-uniphy-pcie-phy",
.data = &ipq5332_pcie_data,@@ -261,6 +384,12 @@ static const struct phy_ops qcom_uniphy_pcie_ops = {
.owner = THIS_MODULE,
};
+static const struct phy_ops qcom_uniphy_usb3_ops = {
+ .power_on = qcom_uniphy_usb3_power_on,
+ .power_off = qcom_uniphy_usb3_power_off,
+ .owner = THIS_MODULE,
+};
+
static int qcom_uniphy_pcie_usb3_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;@@ -280,16 +409,15 @@ static int qcom_uniphy_pcie_usb3_probe(struct platform_device *pdev)
if (!phy->data)
return -EINVAL;
- ret = of_property_read_u32(dev_of_node(dev), "num-lanes", &phy->lanes);
- if (ret)
- return dev_err_probe(dev, ret, "Couldn't read num-lanes\n");
-
ret = qcom_uniphy_pcie_usb3_get_resources(pdev, phy);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"failed to get resources: %d\n", ret);
- generic_phy = devm_phy_create(phy->dev, NULL, &qcom_uniphy_pcie_ops);
+ if (PHY_IS_USB3(phy))
+ generic_phy = devm_phy_create(phy->dev, NULL, &qcom_uniphy_usb3_ops);
+ else
+ generic_phy = devm_phy_create(phy->dev, NULL, &qcom_uniphy_pcie_ops);
if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy);
@@ -304,6 +432,18 @@ static int qcom_uniphy_pcie_usb3_probe(struct platform_device *pdev)
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
+ /*
+ * Select USB mux before the controller comes out of reset. Selecting it
+ * later in .power_on leaves the SuperSpeed pads muxed away while the
+ * controller resets, and the SS link training remains in Rx.Detect
+ */
+ if (PHY_IS_USB3(phy)) {
+ ret = qcom_uniphy_usb_mux_select(phy, true);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to select usb mux\n");
+ }
+
return 0;
}
--
2.53.0