[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250321135854.1431375-10-ivo.ivanov.ivanov1@gmail.com>
Date: Fri, 21 Mar 2025 15:58:53 +0200
From: Ivaylo Ivanov <ivo.ivanov.ivanov1@...il.com>
To: Vinod Koul <vkoul@...nel.org>,
Kishon Vijay Abraham I <kishon@...nel.org>,
Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>,
Alim Akhtar <alim.akhtar@...sung.com>,
Philipp Zabel <p.zabel@...gutronix.de>,
Marek Szyprowski <m.szyprowski@...sung.com>,
Sylwester Nawrocki <s.nawrocki@...sung.com>,
Abel Vesa <abel.vesa@...aro.org>
Cc: linux-arm-msm@...r.kernel.org,
linux-phy@...ts.infradead.org,
devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org,
linux-samsung-soc@...r.kernel.org
Subject: [PATCH v3 09/10] phy: phy-snps-eusb2: add support for exynos2200
The Exynos2200 SoC reuses the Synopsis eUSB2 PHY IP, alongside an
external repeater, for USB 2.0. Add support for it to the existing
driver, while keeping in mind that it requires enabled more than the
reference clock.
Signed-off-by: Ivaylo Ivanov <ivo.ivanov.ivanov1@...il.com>
---
drivers/phy/Kconfig | 2 +-
drivers/phy/phy-snps-eusb2.c | 162 ++++++++++++++++++++++++++++++++++-
2 files changed, 159 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 11c166204..58c911e1b 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -45,7 +45,7 @@ config PHY_PISTACHIO_USB
config PHY_SNPS_EUSB2
tristate "SNPS eUSB2 PHY Driver"
- depends on OF && (ARCH_QCOM || COMPILE_TEST)
+ depends on OF && (ARCH_EXYNOS || ARCH_QCOM || COMPILE_TEST)
select GENERIC_PHY
help
Enable support for the USB high-speed SNPS eUSB2 phy on select
diff --git a/drivers/phy/phy-snps-eusb2.c b/drivers/phy/phy-snps-eusb2.c
index 4ca11860a..951f58119 100644
--- a/drivers/phy/phy-snps-eusb2.c
+++ b/drivers/phy/phy-snps-eusb2.c
@@ -13,6 +13,39 @@
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
+#define EXYNOS_USB_PHY_HS_PHY_CTRL_RST (0x0)
+#define USB_PHY_RST_MASK GENMASK(1, 0)
+#define UTMI_PORT_RST_MASK GENMASK(5, 4)
+
+#define EXYNOS_USB_PHY_HS_PHY_CTRL_COMMON (0x4)
+#define RPTR_MODE BIT(10)
+#define FSEL_20_MHZ_VAL (0x1)
+#define FSEL_24_MHZ_VAL (0x2)
+#define FSEL_26_MHZ_VAL (0x3)
+#define FSEL_48_MHZ_VAL (0x2)
+
+#define EXYNOS_USB_PHY_CFG_PLLCFG0 (0x8)
+#define PHY_CFG_PLL_FB_DIV_19_8_MASK GENMASK(19, 8)
+#define DIV_19_8_19_2_MHZ_VAL (0x170)
+#define DIV_19_8_20_MHZ_VAL (0x160)
+#define DIV_19_8_24_MHZ_VAL (0x120)
+#define DIV_19_8_26_MHZ_VAL (0x107)
+#define DIV_19_8_48_MHZ_VAL (0x120)
+
+#define EXYNOS_USB_PHY_CFG_PLLCFG1 (0xc)
+#define EXYNOS_PHY_CFG_PLL_FB_DIV_11_8_MASK GENMASK(11, 8)
+#define EXYNOS_DIV_11_8_19_2_MHZ_VAL (0x0)
+#define EXYNOS_DIV_11_8_20_MHZ_VAL (0x0)
+#define EXYNOS_DIV_11_8_24_MHZ_VAL (0x0)
+#define EXYNOS_DIV_11_8_26_MHZ_VAL (0x0)
+#define EXYNOS_DIV_11_8_48_MHZ_VAL (0x1)
+
+#define EXYNOS_PHY_CFG_TX (0x14)
+#define EXYNOS_PHY_CFG_TX_FSLS_VREF_TUNE_MASK GENMASK(2, 1)
+
+#define EXYNOS_USB_PHY_UTMI_TESTSE (0x20)
+#define TEST_IDDQ BIT(6)
+
#define QCOM_USB_PHY_UTMI_CTRL0 (0x3c)
#define SLEEPM BIT(0)
#define OPMODE_MASK GENMASK(4, 3)
@@ -123,6 +156,8 @@ static const char * const eusb2_hsphy_vreg_names[] = {
struct snps_eusb2_phy_drvdata {
int (*phy_init)(struct phy *p);
+ const char * const *clk_names;
+ int num_clks;
};
struct snps_eusb2_hsphy {
@@ -130,6 +165,7 @@ struct snps_eusb2_hsphy {
void __iomem *base;
struct clk *ref_clk;
+ struct clk_bulk_data *clks;
struct reset_control *phy_reset;
struct regulator_bulk_data vregs[EUSB2_NUM_VREGS];
@@ -199,6 +235,46 @@ struct snps_eusb2_ref_clk {
u32 div_11_8_val;
};
+static const struct snps_eusb2_ref_clk exynos_eusb2_ref_clk[] = {
+ { 19200000, FSEL_19_2_MHZ_VAL, DIV_19_8_19_2_MHZ_VAL, EXYNOS_DIV_11_8_19_2_MHZ_VAL },
+ { 20000000, FSEL_20_MHZ_VAL, DIV_19_8_20_MHZ_VAL, EXYNOS_DIV_11_8_20_MHZ_VAL },
+ { 24000000, FSEL_24_MHZ_VAL, DIV_19_8_24_MHZ_VAL, EXYNOS_DIV_11_8_24_MHZ_VAL },
+ { 26000000, FSEL_26_MHZ_VAL, DIV_19_8_26_MHZ_VAL, EXYNOS_DIV_11_8_26_MHZ_VAL },
+ { 48000000, FSEL_48_MHZ_VAL, DIV_19_8_48_MHZ_VAL, EXYNOS_DIV_11_8_48_MHZ_VAL },
+};
+
+static int exynos_eusb2_ref_clk_init(struct snps_eusb2_hsphy *phy)
+{
+ const struct snps_eusb2_ref_clk *config = NULL;
+ unsigned long ref_clk_freq = clk_get_rate(phy->ref_clk);
+
+ for (int i = 0; i < ARRAY_SIZE(exynos_eusb2_ref_clk); i++) {
+ if (exynos_eusb2_ref_clk[i].freq == ref_clk_freq) {
+ config = &exynos_eusb2_ref_clk[i];
+ break;
+ }
+ }
+
+ if (!config) {
+ dev_err(&phy->phy->dev, "unsupported ref_clk_freq:%lu\n", ref_clk_freq);
+ return -EINVAL;
+ }
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_COMMON,
+ FSEL_MASK,
+ FIELD_PREP(FSEL_MASK, config->fsel_val));
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_CFG_PLLCFG0,
+ PHY_CFG_PLL_FB_DIV_19_8_MASK,
+ FIELD_PREP(PHY_CFG_PLL_FB_DIV_19_8_MASK,
+ config->div_7_0_val));
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_CFG_PLLCFG1,
+ EXYNOS_PHY_CFG_PLL_FB_DIV_11_8_MASK,
+ config->div_11_8_val);
+ return 0;
+}
+
static const struct snps_eusb2_ref_clk qcom_eusb2_ref_clk[] = {
{ 19200000, FSEL_19_2_MHZ_VAL, DIV_7_0_19_2_MHZ_VAL, DIV_11_8_19_2_MHZ_VAL },
{ 38400000, FSEL_38_4_MHZ_VAL, DIV_7_0_38_4_MHZ_VAL, DIV_11_8_38_4_MHZ_VAL },
@@ -239,6 +315,55 @@ static int qcom_eusb2_ref_clk_init(struct snps_eusb2_hsphy *phy)
return 0;
}
+static int exynos_snps_eusb2_hsphy_init(struct phy *p)
+{
+ struct snps_eusb2_hsphy *phy = phy_get_drvdata(p);
+ int ret;
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_RST,
+ USB_PHY_RST_MASK | UTMI_PORT_RST_MASK,
+ USB_PHY_RST_MASK | UTMI_PORT_RST_MASK);
+ fsleep(50); /* required after holding phy in reset */
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_COMMON,
+ RPTR_MODE, RPTR_MODE);
+
+ /* update ref_clk related registers */
+ ret = exynos_eusb2_ref_clk_init(phy);
+ if (ret)
+ return ret;
+
+ /* default parameter: tx fsls-vref */
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_PHY_CFG_TX,
+ EXYNOS_PHY_CFG_TX_FSLS_VREF_TUNE_MASK,
+ FIELD_PREP(EXYNOS_PHY_CFG_TX_FSLS_VREF_TUNE_MASK, 0x0));
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_UTMI_TESTSE,
+ TEST_IDDQ, 0);
+ fsleep(10); /* required after releasing test_iddq */
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_RST,
+ USB_PHY_RST_MASK, 0);
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_COMMON,
+ PHY_ENABLE, PHY_ENABLE);
+
+ snps_eusb2_hsphy_write_mask(phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_RST,
+ UTMI_PORT_RST_MASK, 0);
+
+ return 0;
+}
+
+static const char * const exynos_eusb2_hsphy_clock_names[] = {
+ "ref", "bus", "ctrl",
+};
+
+static const struct snps_eusb2_phy_drvdata exynos2200_snps_eusb2_phy = {
+ .phy_init = exynos_snps_eusb2_hsphy_init,
+ .clk_names = exynos_eusb2_hsphy_clock_names,
+ .num_clks = ARRAY_SIZE(exynos_eusb2_hsphy_clock_names),
+};
+
static int qcom_snps_eusb2_hsphy_init(struct phy *p)
{
struct snps_eusb2_hsphy *phy = phy_get_drvdata(p);
@@ -315,8 +440,14 @@ static int qcom_snps_eusb2_hsphy_init(struct phy *p)
return 0;
}
+static const char * const qcom_eusb2_hsphy_clock_names[] = {
+ "ref",
+};
+
static const struct snps_eusb2_phy_drvdata sm8550_snps_eusb2_phy = {
.phy_init = qcom_snps_eusb2_hsphy_init,
+ .clk_names = qcom_eusb2_hsphy_clock_names,
+ .num_clks = ARRAY_SIZE(qcom_eusb2_hsphy_clock_names),
};
static int snps_eusb2_hsphy_init(struct phy *p)
@@ -334,7 +465,7 @@ static int snps_eusb2_hsphy_init(struct phy *p)
goto disable_vreg;
}
- ret = clk_prepare_enable(phy->ref_clk);
+ ret = clk_bulk_prepare_enable(phy->data->num_clks, phy->clks);
if (ret) {
dev_err(&p->dev, "failed to enable ref clock, %d\n", ret);
goto disable_vreg;
@@ -361,7 +492,7 @@ static int snps_eusb2_hsphy_init(struct phy *p)
return 0;
disable_ref_clk:
- clk_disable_unprepare(phy->ref_clk);
+ clk_bulk_disable_unprepare(phy->data->num_clks, phy->clks);
disable_vreg:
regulator_bulk_disable(ARRAY_SIZE(phy->vregs), phy->vregs);
@@ -417,8 +548,28 @@ static int snps_eusb2_hsphy_probe(struct platform_device *pdev)
if (IS_ERR(phy->phy_reset))
return PTR_ERR(phy->phy_reset);
- phy->ref_clk = devm_clk_get(dev, "ref");
- if (IS_ERR(phy->ref_clk))
+ phy->clks = devm_kcalloc(dev, phy->data->num_clks, sizeof(*phy->clks),
+ GFP_KERNEL);
+ if (!phy->clks)
+ return -ENOMEM;
+
+ for (int i = 0; i < phy->data->num_clks; ++i)
+ phy->clks[i].id = phy->data->clk_names[i];
+
+ ret = devm_clk_bulk_get(dev, phy->data->num_clks, phy->clks);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get phy clock(s)\n");
+
+ phy->ref_clk = NULL;
+ for (int i = 0; i < phy->data->num_clks; ++i) {
+ if (!strcmp(phy->clks[i].id, "ref")) {
+ phy->ref_clk = phy->clks[i].clk;
+ break;
+ }
+ }
+
+ if (IS_ERR_OR_NULL(phy->ref_clk))
return dev_err_probe(dev, PTR_ERR(phy->ref_clk),
"failed to get ref clk\n");
@@ -458,6 +609,9 @@ static const struct of_device_id snps_eusb2_hsphy_of_match_table[] = {
{
.compatible = "qcom,sm8550-snps-eusb2-phy",
.data = &sm8550_snps_eusb2_phy,
+ }, {
+ .compatible = "samsung,exynos2200-snps-eusb2-phy",
+ .data = &exynos2200_snps_eusb2_phy,
}, { },
};
MODULE_DEVICE_TABLE(of, snps_eusb2_hsphy_of_match_table);
--
2.43.0
Powered by blists - more mailing lists