--- /dev/null
+From: Ahmed Naseef <naseefkm@gmail.com>
+Subject: phy: add EN7528 USB PHY driver
+
+Add USB PHY driver for EcoNet EN7528 SoC.
+
+Based on GPL vendor code at https://github.com/keenetic/kernel-49
+and the Airoha AN7581 USB PHY driver by Christian Marangi.
+
+Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -93,6 +93,17 @@ config PHY_EN7528_PCIE
+ This driver provides PHY initialization for the two PCIe ports
+ on EN7528 SoC.
+
++config PHY_EN7528_USB
++ tristate "EcoNet EN7528 USB PHY Driver"
++ depends on ECONET || COMPILE_TEST
++ depends on OF
++ select GENERIC_PHY
++ select REGMAP_MMIO
++ help
++ Say 'Y' here to add support for EcoNet EN7528 USB PHY driver.
++ This driver creates the basic PHY instance and provides
++ initialization callback for the USB port.
++
+ source "drivers/phy/allwinner/Kconfig"
+ source "drivers/phy/amlogic/Kconfig"
+ source "drivers/phy/broadcom/Kconfig"
+--- a/drivers/phy/Makefile
++++ b/drivers/phy/Makefile
+@@ -12,6 +12,7 @@ obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-
+ obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o
+ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o
+ obj-$(CONFIG_PHY_EN7528_PCIE) += phy-en7528-pcie.o
++obj-$(CONFIG_PHY_EN7528_USB) += phy-en7528-usb.o
+ obj-y += allwinner/ \
+ amlogic/ \
+ broadcom/ \
+--- /dev/null
++++ b/drivers/phy/phy-en7528-usb.c
+@@ -0,0 +1,316 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * EcoNet EN7528 USB PHY driver
++ *
++ * Based on GPL vendor code at https://github.com/keenetic/kernel-49
++ * and the Airoha AN7581 USB PHY driver by
++ * Christian Marangi <ansuelsmth@gmail.com>
++ */
++
++#include <dt-bindings/phy/phy.h>
++#include <linux/bitfield.h>
++#include <linux/math.h>
++#include <linux/module.h>
++#include <linux/phy/phy.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++/* Frequency Meter registers (shared across ports) */
++#define EN7528_USB_PHY_FMCR0 0x100
++#define EN7528_USB_PHY_MONCLK_SEL GENMASK(27, 26)
++#define EN7528_USB_PHY_FREQDET_EN BIT(24)
++#define EN7528_USB_PHY_CYCLECNT GENMASK(23, 0)
++#define EN7528_USB_PHY_FMMONR0 0x10c
++#define EN7528_USB_PHY_FMMONR1 0x110
++#define EN7528_USB_PHY_FRCK_EN BIT(8)
++
++/* U2 PHY port bases */
++#define EN7528_USB_PHY_U2_P0_BASE 0x300
++#define EN7528_USB_PHY_U2_P1_BASE 0x1300
++#define EN7528_USB_PHY_NUM_U2_PORTS 2
++
++/* U2 PHY register offsets (relative to port base) */
++#define EN7528_USB_PHY_ACR0 0x10
++#define EN7528_USB_PHY_HSTX_SRCAL_EN BIT(23)
++#define EN7528_USB_PHY_HSTX_SRCTRL GENMASK(18, 16)
++#define EN7528_USB_PHY_ACR3 0x1c
++#define EN7528_USB_PHY_ACR3_ENABLE 0xC0240000
++
++/* U3 PHYA registers (base at +0xb00) */
++#define EN7528_USB_PHY_U3_PHYA_REG11 0xb2c
++#define EN7528_USB_PHY_RX_IMPSEL GENMASK(13, 12)
++
++#define EN7528_USB_PHY_FM_DET_CYCLE_CNT 1024
++#define EN7528_USB_PHY_REF_CK 20 /* MHz */
++#define EN7528_USB_PHY_SR_COEF 28
++#define EN7528_USB_PHY_SR_COEF_DIVISOR 1000
++#define EN7528_USB_PHY_DEFAULT_SR 4
++
++#define EN7528_USB_PHY_FREQDET_SLEEP 1000 /* 1ms */
++#define EN7528_USB_PHY_FREQDET_TIMEOUT (EN7528_USB_PHY_FREQDET_SLEEP * 10)
++
++static const unsigned int en7528_u2_port_bases[EN7528_USB_PHY_NUM_U2_PORTS] = {
++ EN7528_USB_PHY_U2_P0_BASE,
++ EN7528_USB_PHY_U2_P1_BASE,
++};
++
++struct en7528_usb_phy_instance {
++ struct phy *phy;
++ u32 type;
++};
++
++enum en7528_usb_phy_type {
++ EN7528_PHY_USB2,
++ EN7528_PHY_USB3,
++
++ EN7528_PHY_USB_MAX,
++};
++
++struct en7528_usb_phy_priv {
++ struct device *dev;
++ struct regmap *regmap;
++
++ struct en7528_usb_phy_instance *phys[EN7528_PHY_USB_MAX];
++};
++
++static void en7528_usb_phy_slew_rate_calibration(struct en7528_usb_phy_priv *priv,
++ unsigned int port_id,
++ unsigned int port_base)
++{
++ unsigned int acr0 = port_base + EN7528_USB_PHY_ACR0;
++ u32 fm_out;
++ u32 srctrl;
++
++ /* Enable HS TX SR calibration */
++ regmap_set_bits(priv->regmap, acr0,
++ EN7528_USB_PHY_HSTX_SRCAL_EN);
++
++ usleep_range(1000, 1500);
++
++ /* Enable free run clock */
++ regmap_set_bits(priv->regmap, EN7528_USB_PHY_FMMONR1,
++ EN7528_USB_PHY_FRCK_EN);
++
++ /* Select monitor clock: port 0 = MONCLK_SEL 0, port 1 = MONCLK_SEL 1 */
++ regmap_update_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
++ EN7528_USB_PHY_MONCLK_SEL,
++ FIELD_PREP(EN7528_USB_PHY_MONCLK_SEL, port_id));
++
++ /* Set cycle count */
++ regmap_update_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
++ EN7528_USB_PHY_CYCLECNT,
++ FIELD_PREP(EN7528_USB_PHY_CYCLECNT,
++ EN7528_USB_PHY_FM_DET_CYCLE_CNT));
++
++ /* Enable frequency meter */
++ regmap_set_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
++ EN7528_USB_PHY_FREQDET_EN);
++
++ /* Timeout can happen and we will apply default value at the end */
++ (void)regmap_read_poll_timeout(priv->regmap, EN7528_USB_PHY_FMMONR0,
++ fm_out, fm_out,
++ EN7528_USB_PHY_FREQDET_SLEEP,
++ EN7528_USB_PHY_FREQDET_TIMEOUT);
++
++ /* Disable frequency meter */
++ regmap_clear_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
++ EN7528_USB_PHY_FREQDET_EN);
++
++ /* Disable free run clock */
++ regmap_clear_bits(priv->regmap, EN7528_USB_PHY_FMMONR1,
++ EN7528_USB_PHY_FRCK_EN);
++
++ /* Disable HS TX SR calibration */
++ regmap_clear_bits(priv->regmap, acr0,
++ EN7528_USB_PHY_HSTX_SRCAL_EN);
++
++ usleep_range(1000, 1500);
++
++ if (!fm_out) {
++ srctrl = EN7528_USB_PHY_DEFAULT_SR;
++ dev_err(priv->dev, "port%u: frequency not detected, using default SR calibration.\n",
++ port_id);
++ } else {
++ /* (1024 / FM_OUT) * REF_CK * SR_COEF */
++ srctrl = EN7528_USB_PHY_REF_CK * EN7528_USB_PHY_SR_COEF;
++ srctrl = (srctrl * EN7528_USB_PHY_FM_DET_CYCLE_CNT) / fm_out;
++ srctrl = DIV_ROUND_CLOSEST(srctrl, EN7528_USB_PHY_SR_COEF_DIVISOR);
++ dev_dbg(priv->dev, "port%u: SR calibration applied: %x\n",
++ port_id, srctrl);
++ }
++
++ regmap_update_bits(priv->regmap, acr0,
++ EN7528_USB_PHY_HSTX_SRCTRL,
++ FIELD_PREP(EN7528_USB_PHY_HSTX_SRCTRL, srctrl));
++}
++
++static int en7528_usb_phy_init(struct phy *phy)
++{
++ struct en7528_usb_phy_instance *instance = phy_get_drvdata(phy);
++ struct en7528_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
++ unsigned int i;
++
++ switch (instance->type) {
++ case PHY_TYPE_USB2:
++ /* Enable both U2 PHY ports before calibration */
++ for (i = 0; i < EN7528_USB_PHY_NUM_U2_PORTS; i++)
++ regmap_write(priv->regmap,
++ en7528_u2_port_bases[i] + EN7528_USB_PHY_ACR3,
++ EN7528_USB_PHY_ACR3_ENABLE);
++ break;
++ case PHY_TYPE_USB3:
++ /* Combo PHY Rx R mean value too high, tune -5 Ohm */
++ regmap_update_bits(priv->regmap,
++ EN7528_USB_PHY_U3_PHYA_REG11,
++ EN7528_USB_PHY_RX_IMPSEL,
++ FIELD_PREP(EN7528_USB_PHY_RX_IMPSEL, 0x1));
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int en7528_usb_phy_power_on(struct phy *phy)
++{
++ struct en7528_usb_phy_instance *instance = phy_get_drvdata(phy);
++ struct en7528_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
++ unsigned int i;
++
++ if (instance->type != PHY_TYPE_USB2)
++ return 0;
++
++ /* Calibrate slew rate for both U2 PHY ports */
++ for (i = 0; i < EN7528_USB_PHY_NUM_U2_PORTS; i++)
++ en7528_usb_phy_slew_rate_calibration(priv, i,
++ en7528_u2_port_bases[i]);
++
++ return 0;
++}
++
++static const struct phy_ops en7528_usb_phy_ops = {
++ .init = en7528_usb_phy_init,
++ .power_on = en7528_usb_phy_power_on,
++ .owner = THIS_MODULE,
++};
++
++static struct phy *en7528_usb_phy_xlate(struct device *dev,
++ const struct of_phandle_args *args)
++{
++ struct en7528_usb_phy_priv *priv = dev_get_drvdata(dev);
++ struct en7528_usb_phy_instance *instance = NULL;
++ unsigned int index, phy_type;
++
++ if (args->args_count != 1) {
++ dev_err(dev, "invalid number of cells in 'phy' property\n");
++ return ERR_PTR(-EINVAL);
++ }
++
++ phy_type = args->args[0];
++ if (!(phy_type == PHY_TYPE_USB2 || phy_type == PHY_TYPE_USB3)) {
++ dev_err(dev, "unsupported device type: %d\n", phy_type);
++ return ERR_PTR(-EINVAL);
++ }
++
++ for (index = 0; index < EN7528_PHY_USB_MAX; index++)
++ if (priv->phys[index] &&
++ phy_type == priv->phys[index]->type) {
++ instance = priv->phys[index];
++ break;
++ }
++
++ if (!instance) {
++ dev_err(dev, "failed to find appropriate phy\n");
++ return ERR_PTR(-EINVAL);
++ }
++
++ return instance->phy;
++}
++
++static const struct regmap_config en7528_usb_phy_regmap_config = {
++ .reg_bits = 32,
++ .val_bits = 32,
++ .reg_stride = 4,
++ .max_register = EN7528_USB_PHY_U2_P1_BASE + EN7528_USB_PHY_ACR3,
++};
++
++static int en7528_usb_phy_probe(struct platform_device *pdev)
++{
++ struct phy_provider *phy_provider;
++ struct en7528_usb_phy_priv *priv;
++ struct device *dev = &pdev->dev;
++ unsigned int index;
++ void *base;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->dev = dev;
++
++ base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ priv->regmap = devm_regmap_init_mmio(dev, base,
++ &en7528_usb_phy_regmap_config);
++ if (IS_ERR(priv->regmap))
++ return PTR_ERR(priv->regmap);
++
++ platform_set_drvdata(pdev, priv);
++
++ for (index = 0; index < EN7528_PHY_USB_MAX; index++) {
++ struct en7528_usb_phy_instance *instance;
++ enum en7528_usb_phy_type phy_type;
++
++ switch (index) {
++ case EN7528_PHY_USB2:
++ phy_type = PHY_TYPE_USB2;
++ break;
++ case EN7528_PHY_USB3:
++ phy_type = PHY_TYPE_USB3;
++ break;
++ default:
++ continue;
++ }
++
++ instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
++ if (!instance)
++ return -ENOMEM;
++
++ instance->type = phy_type;
++ priv->phys[index] = instance;
++
++ instance->phy = devm_phy_create(dev, NULL, &en7528_usb_phy_ops);
++ if (IS_ERR(instance->phy))
++ return dev_err_probe(dev, PTR_ERR(instance->phy),
++ "failed to create phy\n");
++
++ phy_set_drvdata(instance->phy, instance);
++ }
++
++ phy_provider = devm_of_phy_provider_register(dev,
++ en7528_usb_phy_xlate);
++
++ return PTR_ERR_OR_ZERO(phy_provider);
++}
++
++static const struct of_device_id en7528_usb_phy_match[] = {
++ { .compatible = "econet,en7528-usb-phy" },
++ { },
++};
++MODULE_DEVICE_TABLE(of, en7528_usb_phy_match);
++
++static struct platform_driver en7528_usb_phy_driver = {
++ .probe = en7528_usb_phy_probe,
++ .driver = {
++ .name = "en7528-usb-phy",
++ .of_match_table = en7528_usb_phy_match,
++ },
++};
++
++module_platform_driver(en7528_usb_phy_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("EcoNet EN7528 USB PHY driver");