diff mbox series

[v2,08/14] net: dwc_eth_qos: Add Qcom ethernet driver glue layer

Message ID 20230201135901.482671-9-sumit.garg@linaro.org
State Accepted
Commit d382025dc555b4e847877bb9b2d492ad1127d144
Delegated to: Tom Rini
Headers show
Series QCS404: Add ethernet and I2C drivers | expand

Commit Message

Sumit Garg Feb. 1, 2023, 1:58 p.m. UTC
The Qualcom ETHQOS hardware supports an RGMII macro which needs to be
configured according to following link speeds:
- SPEED_1000
- SPEED_100
- SPEED_10

So add a corresponding glue driver to configure RGMII macro.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 drivers/net/Kconfig            |   7 +
 drivers/net/Makefile           |   1 +
 drivers/net/dwc_eth_qos.c      |   7 +
 drivers/net/dwc_eth_qos.h      |   3 +
 drivers/net/dwc_eth_qos_qcom.c | 612 +++++++++++++++++++++++++++++++++
 5 files changed, 630 insertions(+)
 create mode 100644 drivers/net/dwc_eth_qos_qcom.c

Comments

Ramon Fried Feb. 4, 2023, 12:42 a.m. UTC | #1
On Wed, Feb 1, 2023 at 3:59 PM Sumit Garg <sumit.garg@linaro.org> wrote:
>
> The Qualcom ETHQOS hardware supports an RGMII macro which needs to be
> configured according to following link speeds:
> - SPEED_1000
> - SPEED_100
> - SPEED_10
>
> So add a corresponding glue driver to configure RGMII macro.
>
> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> ---
>  drivers/net/Kconfig            |   7 +
>  drivers/net/Makefile           |   1 +
>  drivers/net/dwc_eth_qos.c      |   7 +
>  drivers/net/dwc_eth_qos.h      |   3 +
>  drivers/net/dwc_eth_qos_qcom.c | 612 +++++++++++++++++++++++++++++++++
>  5 files changed, 630 insertions(+)
>  create mode 100644 drivers/net/dwc_eth_qos_qcom.c
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 7873538cc2..815e1f9248 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -242,6 +242,13 @@ config DWC_ETH_QOS_TEGRA186
>           The Synopsys Designware Ethernet QOS IP block with specific
>           configuration used in NVIDIA's Tegra186 chip.
>
> +config DWC_ETH_QOS_QCOM
> +       bool "Synopsys DWC Ethernet QOS device support for Qcom SoCs"
> +       depends on DWC_ETH_QOS
> +       help
> +         The Synopsys Designware Ethernet QOS IP block with specific
> +         configuration used in Qcom QCS404 SoC.
> +
>  config E1000
>         bool "Intel PRO/1000 Gigabit Ethernet support"
>         depends on PCI
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 5b4e60eea3..b009b10aca 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
>  obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o
>  obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o
>  obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o
> +obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o
>  obj-$(CONFIG_E1000) += e1000.o
>  obj-$(CONFIG_E1000_SPI) += e1000_spi.o
>  obj-$(CONFIG_EEPRO100) += eepro100.o
> diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
> index 65b8556be2..112deb546d 100644
> --- a/drivers/net/dwc_eth_qos.c
> +++ b/drivers/net/dwc_eth_qos.c
> @@ -1712,6 +1712,13 @@ static const struct udevice_id eqos_ids[] = {
>         },
>  #endif
>
> +#if IS_ENABLED(CONFIG_DWC_ETH_QOS_QCOM)
> +       {
> +               .compatible = "qcom,qcs404-ethqos",
> +               .data = (ulong)&eqos_qcom_config
> +       },
> +#endif
> +
>         { }
>  };
>
> diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
> index 466a792de7..fddbe9336c 100644
> --- a/drivers/net/dwc_eth_qos.h
> +++ b/drivers/net/dwc_eth_qos.h
> @@ -253,6 +253,7 @@ struct eqos_priv {
>         struct eqos_mtl_regs *mtl_regs;
>         struct eqos_dma_regs *dma_regs;
>         struct eqos_tegra186_regs *tegra186_regs;
> +       void *eqos_qcom_rgmii_regs;
>         struct reset_ctl reset_ctl;
>         struct gpio_desc phy_reset_gpio;
>         struct clk clk_master_bus;
> @@ -277,6 +278,7 @@ struct eqos_priv {
>         bool reg_access_ok;
>         bool clk_ck_enabled;
>         unsigned int tx_fifo_sz, rx_fifo_sz;
> +       u32 reset_delays[3];
>  };
>
>  void eqos_inval_desc_generic(void *desc);
> @@ -286,3 +288,4 @@ void eqos_flush_buffer_generic(void *buf, size_t size);
>  int eqos_null_ops(struct udevice *dev);
>
>  extern struct eqos_config eqos_imx_config;
> +extern struct eqos_config eqos_qcom_config;
> diff --git a/drivers/net/dwc_eth_qos_qcom.c b/drivers/net/dwc_eth_qos_qcom.c
> new file mode 100644
> index 0000000000..df83f1c5f9
> --- /dev/null
> +++ b/drivers/net/dwc_eth_qos_qcom.c
> @@ -0,0 +1,612 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * (C) Copyright 2022-2023 Sumit Garg <sumit.garg@linaro.org>
> + *
> + * Qcom DWMAC specific glue layer
> + */
> +
> +#include <common.h>
> +#include <asm/global_data.h>
> +#include <asm/gpio.h>
> +#include <asm/io.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <dm/device_compat.h>
> +#include <phy.h>
> +#include <reset.h>
> +#include <syscon.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +
> +#include "dwc_eth_qos.h"
> +
> +/* RGMII_IO_MACRO_CONFIG fields */
> +#define RGMII_CONFIG_FUNC_CLK_EN               BIT(30)
> +#define RGMII_CONFIG_POS_NEG_DATA_SEL          BIT(23)
> +#define RGMII_CONFIG_GPIO_CFG_RX_INT           GENMASK(21, 20)
> +#define RGMII_CONFIG_GPIO_CFG_TX_INT           GENMASK(19, 17)
> +#define RGMII_CONFIG_MAX_SPD_PRG_9             GENMASK(16, 8)
> +#define RGMII_CONFIG_MAX_SPD_PRG_2             GENMASK(7, 6)
> +#define RGMII_CONFIG_INTF_SEL                  GENMASK(5, 4)
> +#define RGMII_CONFIG_BYPASS_TX_ID_EN           BIT(3)
> +#define RGMII_CONFIG_LOOPBACK_EN               BIT(2)
> +#define RGMII_CONFIG_PROG_SWAP                 BIT(1)
> +#define RGMII_CONFIG_DDR_MODE                  BIT(0)
> +
> +/* SDCC_HC_REG_DLL_CONFIG fields */
> +#define SDCC_DLL_CONFIG_DLL_RST                        BIT(30)
> +#define SDCC_DLL_CONFIG_PDN                    BIT(29)
> +#define SDCC_DLL_CONFIG_MCLK_FREQ              GENMASK(26, 24)
> +#define SDCC_DLL_CONFIG_CDR_SELEXT             GENMASK(23, 20)
> +#define SDCC_DLL_CONFIG_CDR_EXT_EN             BIT(19)
> +#define SDCC_DLL_CONFIG_CK_OUT_EN              BIT(18)
> +#define SDCC_DLL_CONFIG_CDR_EN                 BIT(17)
> +#define SDCC_DLL_CONFIG_DLL_EN                 BIT(16)
> +#define SDCC_DLL_MCLK_GATING_EN                        BIT(5)
> +#define SDCC_DLL_CDR_FINE_PHASE                        GENMASK(3, 2)
> +
> +/* SDCC_HC_REG_DDR_CONFIG fields */
> +#define SDCC_DDR_CONFIG_PRG_DLY_EN             BIT(31)
> +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY       GENMASK(26, 21)
> +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE  GENMASK(29, 27)
> +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN    BIT(30)
> +#define SDCC_DDR_CONFIG_PRG_RCLK_DLY           GENMASK(8, 0)
> +
> +/* SDCC_HC_REG_DLL_CONFIG2 fields */
> +#define SDCC_DLL_CONFIG2_DLL_CLOCK_DIS         BIT(21)
> +#define SDCC_DLL_CONFIG2_MCLK_FREQ_CALC                GENMASK(17, 10)
> +#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL  GENMASK(3, 2)
> +#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW   BIT(1)
> +#define SDCC_DLL_CONFIG2_DDR_CAL_EN            BIT(0)
> +
> +/* SDC4_STATUS bits */
> +#define SDC4_STATUS_DLL_LOCK                   BIT(7)
> +
> +/* RGMII_IO_MACRO_CONFIG2 fields */
> +#define RGMII_CONFIG2_RSVD_CONFIG15            GENMASK(31, 17)
> +#define RGMII_CONFIG2_RGMII_CLK_SEL_CFG                BIT(16)
> +#define RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN     BIT(13)
> +#define RGMII_CONFIG2_CLK_DIVIDE_SEL           BIT(12)
> +#define RGMII_CONFIG2_RX_PROG_SWAP             BIT(7)
> +#define RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL      BIT(6)
> +#define RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN    BIT(5)
> +
> +struct dwmac_rgmii_regs {
> +       u32 io_macro_config;            /* 0x00 */
> +       u32 sdcc_hc_dll_config;         /* 0x04 */
> +       u32 reserved_1;                 /* 0x08 */
> +       u32 sdcc_hc_ddr_config;         /* 0x0c */
> +       u32 sdcc_hc_dll_config2;        /* 0x10 */
> +       u32 sdc4_status;                /* 0x14 */
> +       u32 sdcc_usr_ctl;               /* 0x18 */
> +       u32 io_macro_config2;           /* 0x1c */
> +       u32 io_macro_debug1;            /* 0x20 */
> +       u32 reserved_2;                 /* 0x24 */
> +       u32 emac_sys_low_power_dbg;     /* 0x28 */
> +       u32 reserved_3[53];             /* upto 0x100 */
> +};
> +
> +static struct dwmac_rgmii_regs emac_v2_3_0_por = {
> +       .io_macro_config = 0x00C01343,
> +       .sdcc_hc_dll_config = 0x2004642C,
> +       .sdcc_hc_ddr_config = 0x00000000,
> +       .sdcc_hc_dll_config2 = 0x00200000,
> +       .sdcc_usr_ctl = 0x00010800,
> +       .io_macro_config2 = 0x00002060
> +};
> +
> +static void ethqos_set_func_clk_en(struct dwmac_rgmii_regs *regs)
> +{
> +       setbits_le32(&regs->io_macro_config, RGMII_CONFIG_FUNC_CLK_EN);
> +}
> +
> +static int ethqos_dll_configure(struct udevice *dev,
> +                               struct dwmac_rgmii_regs *regs)
> +{
> +       unsigned int val;
> +       int retry = 1000;
> +
> +       /* Set CDR_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN);
> +
> +       /* Set CDR_EXT_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN);
> +
> +       /* Clear CK_OUT_EN */
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
> +
> +       /* Set DLL_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
> +
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN);
> +
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE);
> +
> +       /* Wait for CK_OUT_EN clear */
> +       do {
> +               val = readl(&regs->sdcc_hc_dll_config);
> +               val &= SDCC_DLL_CONFIG_CK_OUT_EN;
> +               if (!val)
> +                       break;
> +               mdelay(1);
> +               retry--;
> +       } while (retry > 0);
> +       if (!retry)
> +               dev_err(dev, "Clear CK_OUT_EN timedout\n");
> +
> +       /* Set CK_OUT_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
> +
> +       /* Wait for CK_OUT_EN set */
> +       retry = 1000;
> +       do {
> +               val = readl(&regs->sdcc_hc_dll_config);
> +               val &= SDCC_DLL_CONFIG_CK_OUT_EN;
> +               if (val)
> +                       break;
> +               mdelay(1);
> +               retry--;
> +       } while (retry > 0);
> +       if (!retry)
> +               dev_err(dev, "Set CK_OUT_EN timedout\n");
> +
> +       /* Set DDR_CAL_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN);
> +
> +       clrbits_le32(&regs->sdcc_hc_dll_config2,
> +                    SDCC_DLL_CONFIG2_DLL_CLOCK_DIS);
> +
> +       clrsetbits_le32(&regs->sdcc_hc_dll_config2,
> +                       SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10);
> +
> +       clrsetbits_le32(&regs->sdcc_hc_dll_config2,
> +                       SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2));
> +
> +       setbits_le32(&regs->sdcc_hc_dll_config2,
> +                    SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW);
> +
> +       return 0;
> +}
> +
> +static int ethqos_rgmii_macro_init(struct udevice *dev,
> +                                  struct dwmac_rgmii_regs *regs,
> +                                  unsigned long speed)
> +{
> +       /* Disable loopback mode */
> +       clrbits_le32(&regs->io_macro_config2,
> +                    RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN);
> +
> +       /* Select RGMII, write 0 to interface select */
> +       clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_INTF_SEL);
> +
> +       switch (speed) {
> +       case SPEED_1000:
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
> +               clrbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_BYPASS_TX_ID_EN);
> +               setbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_POS_NEG_DATA_SEL);
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
> +
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
> +               setbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RSVD_CONFIG15);
> +               setbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RX_PROG_SWAP);
> +
> +               /* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */
> +               clrsetbits_le32(&regs->sdcc_hc_ddr_config,
> +                               SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57);
> +               setbits_le32(&regs->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN);
> +
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
> +               break;
> +
> +       case SPEED_100:
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
> +               setbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_BYPASS_TX_ID_EN);
> +               clrbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_POS_NEG_DATA_SEL);
> +               clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
> +               clrsetbits_le32(&regs->io_macro_config,
> +                               RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6));
> +
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
> +               setbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RSVD_CONFIG15);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RX_PROG_SWAP);
> +
> +               /* Write 0x5 to PRG_RCLK_DLY_CODE */
> +               clrsetbits_le32(&regs->sdcc_hc_ddr_config,
> +                               SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
> +                               (BIT(29) | BIT(27)));
> +               setbits_le32(&regs->sdcc_hc_ddr_config,
> +                            SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
> +               setbits_le32(&regs->sdcc_hc_ddr_config,
> +                            SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
> +
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
> +               break;
> +
> +       case SPEED_10:
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
> +               setbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_BYPASS_TX_ID_EN);
> +               clrbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_POS_NEG_DATA_SEL);
> +               clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
> +               clrsetbits_le32(&regs->io_macro_config,
> +                               RGMII_CONFIG_MAX_SPD_PRG_9,
> +                               BIT(12) | GENMASK(9, 8));
> +
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RSVD_CONFIG15);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RX_PROG_SWAP);
> +
> +               /* Write 0x5 to PRG_RCLK_DLY_CODE */
> +               clrsetbits_le32(&regs->sdcc_hc_ddr_config,
> +                               SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
> +                               (BIT(29) | BIT(27)));
> +               setbits_le32(&regs->sdcc_hc_ddr_config,
> +                            SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
> +               setbits_le32(&regs->sdcc_hc_ddr_config,
> +                            SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
> +
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
> +               break;
> +
> +       default:
> +               dev_err(dev, "Invalid speed %ld\n", speed);
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int ethqos_configure(struct udevice *dev,
> +                           struct dwmac_rgmii_regs *regs,
> +                           unsigned long speed)
> +{
> +       unsigned int retry = 1000;
> +
> +       /* Reset to POR values and enable clk */
> +       writel(emac_v2_3_0_por.io_macro_config, &regs->io_macro_config);
> +       writel(emac_v2_3_0_por.sdcc_hc_dll_config, &regs->sdcc_hc_dll_config);
> +       writel(emac_v2_3_0_por.sdcc_hc_ddr_config, &regs->sdcc_hc_ddr_config);
> +       writel(emac_v2_3_0_por.sdcc_hc_dll_config2, &regs->sdcc_hc_dll_config2);
> +       writel(emac_v2_3_0_por.sdcc_usr_ctl, &regs->sdcc_usr_ctl);
> +       writel(emac_v2_3_0_por.io_macro_config2, &regs->io_macro_config2);
> +
> +       ethqos_set_func_clk_en(regs);
> +
> +       /* Initialize the DLL first */
> +
> +       /* Set DLL_RST */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
> +
> +       /* Set PDN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
> +
> +       /* Clear DLL_RST */
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
> +
> +       /* Clear PDN */
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
> +
> +       if (speed == SPEED_1000) {
> +               /* Set DLL_EN */
> +               setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
> +
> +               /* Set CK_OUT_EN */
> +               setbits_le32(&regs->sdcc_hc_dll_config,
> +                            SDCC_DLL_CONFIG_CK_OUT_EN);
> +
> +               /* Set USR_CTL bit 26 with mask of 3 bits */
> +               clrsetbits_le32(&regs->sdcc_usr_ctl, GENMASK(26, 24), BIT(26));
> +
> +               /* wait for DLL LOCK */
> +               do {
> +                       mdelay(1);
> +                       if (readl(&regs->sdc4_status) & SDC4_STATUS_DLL_LOCK)
> +                               break;
> +                       retry--;
> +               } while (retry > 0);
> +               if (!retry)
> +                       dev_err(dev, "Timeout while waiting for DLL lock\n");
> +
> +               ethqos_dll_configure(dev, regs);
> +       }
> +
> +       ethqos_rgmii_macro_init(dev, regs, speed);
> +
> +       return 0;
> +}
> +
> +static void ethqos_rgmii_dump(struct udevice *dev,
> +                             struct dwmac_rgmii_regs *regs)
> +{
> +       dev_dbg(dev, "Rgmii register dump\n");
> +       dev_dbg(dev, "RGMII_IO_MACRO_CONFIG: %08x\n",
> +               readl(&regs->io_macro_config));
> +       dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n",
> +               readl(&regs->sdcc_hc_dll_config));
> +       dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n",
> +               readl(&regs->sdcc_hc_ddr_config));
> +       dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n",
> +               readl(&regs->sdcc_hc_dll_config2));
> +       dev_dbg(dev, "SDC4_STATUS: %08x\n",
> +               readl(&regs->sdc4_status));
> +       dev_dbg(dev, "SDCC_USR_CTL: %08x\n",
> +               readl(&regs->sdcc_usr_ctl));
> +       dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n",
> +               readl(&regs->io_macro_config2));
> +       dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n",
> +               readl(&regs->io_macro_debug1));
> +       dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n",
> +               readl(&regs->emac_sys_low_power_dbg));
> +}
> +
> +static int qcom_eqos_rgmii_set_speed(struct udevice *dev,
> +                                    void *rgmii_regs,
> +                                    unsigned long speed)
> +{
> +       int ret;
> +
> +       ethqos_rgmii_dump(dev, rgmii_regs);
> +
> +       ret = ethqos_configure(dev, rgmii_regs, speed);
> +       if (ret)
> +               return ret;
> +
> +       ethqos_rgmii_dump(dev, rgmii_regs);
> +
> +       return 0;
> +}
> +
> +static int qcom_eqos_rgmii_reset(struct udevice *dev, void *rgmii_regs)
> +{
> +       ethqos_set_func_clk_en(rgmii_regs);
> +
> +       return 0;
> +}
> +
> +static int eqos_start_clks_qcom(struct udevice *dev)
> +{
> +       if (IS_ENABLED(CONFIG_CLK)) {
> +               struct clk_bulk clocks;
> +               int ret;
> +
> +               ret = clk_get_bulk(dev, &clocks);
> +               if (ret)
> +                       return ret;
> +
> +               ret = clk_enable_bulk(&clocks);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +static int eqos_stop_clks_qcom(struct udevice *dev)
> +{
> +       if (IS_ENABLED(CONFIG_CLK)) {
> +               struct clk_bulk clocks;
> +               int ret;
> +
> +               ret = clk_get_bulk(dev, &clocks);
> +               if (ret)
> +                       return ret;
> +
> +               ret = clk_disable_bulk(&clocks);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +static int eqos_start_resets_qcom(struct udevice *dev)
> +{
> +       struct eqos_priv *eqos = dev_get_priv(dev);
> +       int ret;
> +
> +       debug("%s(dev=%p):\n", __func__, dev);
> +
> +       if (!eqos->phy) {
> +               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
> +               if (ret < 0) {
> +                       pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d", ret);
> +                       return ret;
> +               }
> +
> +               udelay(eqos->reset_delays[0]);
> +
> +               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1);
> +               if (ret < 0) {
> +                       pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret);
> +                       return ret;
> +               }
> +
> +               udelay(eqos->reset_delays[1]);
> +
> +               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
> +               if (ret < 0) {
> +                       pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret);
> +                       return ret;
> +               }
> +
> +               udelay(eqos->reset_delays[2]);
> +       }
> +
> +       ret = reset_deassert(&eqos->reset_ctl);
> +       if (ret < 0) {
> +               pr_err("reset_deassert() failed: %d", ret);
> +               return ret;
> +       }
> +
> +       ret = qcom_eqos_rgmii_reset(dev, eqos->eqos_qcom_rgmii_regs);
> +       if (ret < 0) {
> +               pr_err("qcom rgmii_reset failed: %d", ret);
> +               return ret;
> +       }
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +/* Clock rates */
> +#define RGMII_1000_NOM_CLK_FREQ                        (250 * 1000 * 1000UL)
> +#define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ      (50 * 1000 * 1000UL)
> +#define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ        (5 * 1000 * 1000UL)
> +
> +static int eqos_set_tx_clk_speed_qcom(struct udevice *dev)
> +{
> +       struct eqos_priv *eqos = dev_get_priv(dev);
> +       ulong rate;
> +       int ret;
> +
> +       debug("%s(dev=%p):\n", __func__, dev);
> +
> +       switch (eqos->phy->speed) {
> +       case SPEED_1000:
> +               rate = RGMII_1000_NOM_CLK_FREQ;
> +               break;
> +       case SPEED_100:
> +               rate = RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ;
> +               break;
> +       case SPEED_10:
> +               rate = RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ;
> +               break;
> +       default:
> +               pr_err("invalid speed %d", eqos->phy->speed);
> +               return -EINVAL;
> +       }
> +
> +       ret = clk_set_rate(&eqos->clk_tx, rate);
> +       if (ret < 0) {
> +               pr_err("clk_set_rate(tx_clk, %lu) failed: %d", rate, ret);
> +               return ret;
> +       }
> +
> +       ret = qcom_eqos_rgmii_set_speed(dev, eqos->eqos_qcom_rgmii_regs,
> +                                       eqos->phy->speed);
> +       if (ret < 0) {
> +               pr_err("qcom set_speed: %d, failed: %d", eqos->phy->speed, ret);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int eqos_probe_resources_qcom(struct udevice *dev)
> +{
> +       struct eqos_priv *eqos = dev_get_priv(dev);
> +       phy_interface_t interface;
> +       int reset_flags = GPIOD_IS_OUT;
> +       int ret;
> +
> +       debug("%s(dev=%p):\n", __func__, dev);
> +
> +       interface = eqos->config->interface(dev);
> +
> +       if (interface == PHY_INTERFACE_MODE_NA) {
> +               pr_err("Invalid PHY interface\n");
> +               return -EINVAL;
> +       }
> +
> +       eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0);
> +
> +       eqos->tx_fifo_sz = dev_read_u32_default(dev, "tx-fifo-depth", 0);
> +       eqos->rx_fifo_sz = dev_read_u32_default(dev, "rx-fifo-depth", 0);
> +
> +       ret = reset_get_by_name(dev, "emac", &eqos->reset_ctl);
> +       if (ret) {
> +               pr_err("reset_get_by_name(rst) failed: %d", ret);
> +               return ret;
> +       }
> +
> +       if (dev_read_bool(dev, "snps,reset-active-low"))
> +               reset_flags |= GPIOD_ACTIVE_LOW;
> +
> +       ret = gpio_request_by_name(dev, "snps,reset-gpio", 0,
> +                                  &eqos->phy_reset_gpio, reset_flags);
> +       if (ret == 0) {
> +               ret = dev_read_u32_array(dev, "snps,reset-delays-us",
> +                                        eqos->reset_delays, 3);
> +       } else if (ret == -ENOENT) {
> +               ret = 0;
> +       }
> +
> +       eqos->eqos_qcom_rgmii_regs = (void *)dev_read_addr_name(dev, "rgmii");
> +       if ((fdt_addr_t)eqos->eqos_qcom_rgmii_regs == FDT_ADDR_T_NONE) {
> +               pr_err("Invalid RGMII address\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = clk_get_by_name(dev, "rgmii", &eqos->clk_tx);
> +       if (ret) {
> +               pr_err("clk_get_by_name(tx) failed: %d", ret);
> +               return -EINVAL;
> +       }
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +static int eqos_remove_resources_qcom(struct udevice *dev)
> +{
> +       struct eqos_priv *eqos = dev_get_priv(dev);
> +
> +       debug("%s(dev=%p):\n", __func__, dev);
> +
> +       clk_free(&eqos->clk_tx);
> +       dm_gpio_free(dev, &eqos->phy_reset_gpio);
> +       reset_free(&eqos->reset_ctl);
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +static struct eqos_ops eqos_qcom_ops = {
> +       .eqos_inval_desc = eqos_inval_desc_generic,
> +       .eqos_flush_desc = eqos_flush_desc_generic,
> +       .eqos_inval_buffer = eqos_inval_buffer_generic,
> +       .eqos_flush_buffer = eqos_flush_buffer_generic,
> +       .eqos_probe_resources = eqos_probe_resources_qcom,
> +       .eqos_remove_resources = eqos_remove_resources_qcom,
> +       .eqos_stop_resets = eqos_null_ops,
> +       .eqos_start_resets = eqos_start_resets_qcom,
> +       .eqos_stop_clks = eqos_stop_clks_qcom,
> +       .eqos_start_clks = eqos_start_clks_qcom,
> +       .eqos_calibrate_pads = eqos_null_ops,
> +       .eqos_disable_calibration = eqos_null_ops,
> +       .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_qcom,
> +       .eqos_get_enetaddr = eqos_null_ops,
> +};
> +
> +struct eqos_config __maybe_unused eqos_qcom_config = {
> +       .reg_access_always_ok = false,
> +       .mdio_wait = 10,
> +       .swr_wait = 50,
> +       .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
> +       .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
> +       .axi_bus_width = EQOS_AXI_WIDTH_64,
> +       .interface = dev_read_phy_mode,
> +       .ops = &eqos_qcom_ops
> +};
> --
> 2.34.1
>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
Tom Rini Feb. 10, 2023, 6:44 p.m. UTC | #2
On Wed, Feb 01, 2023 at 07:28:55PM +0530, Sumit Garg wrote:

> The Qualcom ETHQOS hardware supports an RGMII macro which needs to be
> configured according to following link speeds:
> - SPEED_1000
> - SPEED_100
> - SPEED_10
> 
> So add a corresponding glue driver to configure RGMII macro.
> 
> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> Reviewed-by: Ramon Fried <rfried.dev@gmail.com>

Applied to u-boot/master, thanks!
diff mbox series

Patch

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 7873538cc2..815e1f9248 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -242,6 +242,13 @@  config DWC_ETH_QOS_TEGRA186
 	  The Synopsys Designware Ethernet QOS IP block with specific
 	  configuration used in NVIDIA's Tegra186 chip.
 
+config DWC_ETH_QOS_QCOM
+	bool "Synopsys DWC Ethernet QOS device support for Qcom SoCs"
+	depends on DWC_ETH_QOS
+	help
+	  The Synopsys Designware Ethernet QOS IP block with specific
+	  configuration used in Qcom QCS404 SoC.
+
 config E1000
 	bool "Intel PRO/1000 Gigabit Ethernet support"
 	depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 5b4e60eea3..b009b10aca 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -20,6 +20,7 @@  obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
 obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o
 obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o
 obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o
+obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o
 obj-$(CONFIG_E1000) += e1000.o
 obj-$(CONFIG_E1000_SPI) += e1000_spi.o
 obj-$(CONFIG_EEPRO100) += eepro100.o
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 65b8556be2..112deb546d 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -1712,6 +1712,13 @@  static const struct udevice_id eqos_ids[] = {
 	},
 #endif
 
+#if IS_ENABLED(CONFIG_DWC_ETH_QOS_QCOM)
+	{
+		.compatible = "qcom,qcs404-ethqos",
+		.data = (ulong)&eqos_qcom_config
+	},
+#endif
+
 	{ }
 };
 
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index 466a792de7..fddbe9336c 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -253,6 +253,7 @@  struct eqos_priv {
 	struct eqos_mtl_regs *mtl_regs;
 	struct eqos_dma_regs *dma_regs;
 	struct eqos_tegra186_regs *tegra186_regs;
+	void *eqos_qcom_rgmii_regs;
 	struct reset_ctl reset_ctl;
 	struct gpio_desc phy_reset_gpio;
 	struct clk clk_master_bus;
@@ -277,6 +278,7 @@  struct eqos_priv {
 	bool reg_access_ok;
 	bool clk_ck_enabled;
 	unsigned int tx_fifo_sz, rx_fifo_sz;
+	u32 reset_delays[3];
 };
 
 void eqos_inval_desc_generic(void *desc);
@@ -286,3 +288,4 @@  void eqos_flush_buffer_generic(void *buf, size_t size);
 int eqos_null_ops(struct udevice *dev);
 
 extern struct eqos_config eqos_imx_config;
+extern struct eqos_config eqos_qcom_config;
diff --git a/drivers/net/dwc_eth_qos_qcom.c b/drivers/net/dwc_eth_qos_qcom.c
new file mode 100644
index 0000000000..df83f1c5f9
--- /dev/null
+++ b/drivers/net/dwc_eth_qos_qcom.c
@@ -0,0 +1,612 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2022-2023 Sumit Garg <sumit.garg@linaro.org>
+ *
+ * Qcom DWMAC specific glue layer
+ */
+
+#include <common.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <phy.h>
+#include <reset.h>
+#include <syscon.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#include "dwc_eth_qos.h"
+
+/* RGMII_IO_MACRO_CONFIG fields */
+#define RGMII_CONFIG_FUNC_CLK_EN		BIT(30)
+#define RGMII_CONFIG_POS_NEG_DATA_SEL		BIT(23)
+#define RGMII_CONFIG_GPIO_CFG_RX_INT		GENMASK(21, 20)
+#define RGMII_CONFIG_GPIO_CFG_TX_INT		GENMASK(19, 17)
+#define RGMII_CONFIG_MAX_SPD_PRG_9		GENMASK(16, 8)
+#define RGMII_CONFIG_MAX_SPD_PRG_2		GENMASK(7, 6)
+#define RGMII_CONFIG_INTF_SEL			GENMASK(5, 4)
+#define RGMII_CONFIG_BYPASS_TX_ID_EN		BIT(3)
+#define RGMII_CONFIG_LOOPBACK_EN		BIT(2)
+#define RGMII_CONFIG_PROG_SWAP			BIT(1)
+#define RGMII_CONFIG_DDR_MODE			BIT(0)
+
+/* SDCC_HC_REG_DLL_CONFIG fields */
+#define SDCC_DLL_CONFIG_DLL_RST			BIT(30)
+#define SDCC_DLL_CONFIG_PDN			BIT(29)
+#define SDCC_DLL_CONFIG_MCLK_FREQ		GENMASK(26, 24)
+#define SDCC_DLL_CONFIG_CDR_SELEXT		GENMASK(23, 20)
+#define SDCC_DLL_CONFIG_CDR_EXT_EN		BIT(19)
+#define SDCC_DLL_CONFIG_CK_OUT_EN		BIT(18)
+#define SDCC_DLL_CONFIG_CDR_EN			BIT(17)
+#define SDCC_DLL_CONFIG_DLL_EN			BIT(16)
+#define SDCC_DLL_MCLK_GATING_EN			BIT(5)
+#define SDCC_DLL_CDR_FINE_PHASE			GENMASK(3, 2)
+
+/* SDCC_HC_REG_DDR_CONFIG fields */
+#define SDCC_DDR_CONFIG_PRG_DLY_EN		BIT(31)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY	GENMASK(26, 21)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE	GENMASK(29, 27)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN	BIT(30)
+#define SDCC_DDR_CONFIG_PRG_RCLK_DLY		GENMASK(8, 0)
+
+/* SDCC_HC_REG_DLL_CONFIG2 fields */
+#define SDCC_DLL_CONFIG2_DLL_CLOCK_DIS		BIT(21)
+#define SDCC_DLL_CONFIG2_MCLK_FREQ_CALC		GENMASK(17, 10)
+#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL	GENMASK(3, 2)
+#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW	BIT(1)
+#define SDCC_DLL_CONFIG2_DDR_CAL_EN		BIT(0)
+
+/* SDC4_STATUS bits */
+#define SDC4_STATUS_DLL_LOCK			BIT(7)
+
+/* RGMII_IO_MACRO_CONFIG2 fields */
+#define RGMII_CONFIG2_RSVD_CONFIG15		GENMASK(31, 17)
+#define RGMII_CONFIG2_RGMII_CLK_SEL_CFG		BIT(16)
+#define RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN	BIT(13)
+#define RGMII_CONFIG2_CLK_DIVIDE_SEL		BIT(12)
+#define RGMII_CONFIG2_RX_PROG_SWAP		BIT(7)
+#define RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL	BIT(6)
+#define RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN	BIT(5)
+
+struct dwmac_rgmii_regs {
+	u32 io_macro_config;		/* 0x00 */
+	u32 sdcc_hc_dll_config;		/* 0x04 */
+	u32 reserved_1;			/* 0x08 */
+	u32 sdcc_hc_ddr_config;		/* 0x0c */
+	u32 sdcc_hc_dll_config2;	/* 0x10 */
+	u32 sdc4_status;		/* 0x14 */
+	u32 sdcc_usr_ctl;		/* 0x18 */
+	u32 io_macro_config2;		/* 0x1c */
+	u32 io_macro_debug1;		/* 0x20 */
+	u32 reserved_2;			/* 0x24 */
+	u32 emac_sys_low_power_dbg;	/* 0x28 */
+	u32 reserved_3[53];		/* upto 0x100 */
+};
+
+static struct dwmac_rgmii_regs emac_v2_3_0_por = {
+	.io_macro_config = 0x00C01343,
+	.sdcc_hc_dll_config = 0x2004642C,
+	.sdcc_hc_ddr_config = 0x00000000,
+	.sdcc_hc_dll_config2 = 0x00200000,
+	.sdcc_usr_ctl = 0x00010800,
+	.io_macro_config2 = 0x00002060
+};
+
+static void ethqos_set_func_clk_en(struct dwmac_rgmii_regs *regs)
+{
+	setbits_le32(&regs->io_macro_config, RGMII_CONFIG_FUNC_CLK_EN);
+}
+
+static int ethqos_dll_configure(struct udevice *dev,
+				struct dwmac_rgmii_regs *regs)
+{
+	unsigned int val;
+	int retry = 1000;
+
+	/* Set CDR_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN);
+
+	/* Set CDR_EXT_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN);
+
+	/* Clear CK_OUT_EN */
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
+
+	/* Set DLL_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
+
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN);
+
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE);
+
+	/* Wait for CK_OUT_EN clear */
+	do {
+		val = readl(&regs->sdcc_hc_dll_config);
+		val &= SDCC_DLL_CONFIG_CK_OUT_EN;
+		if (!val)
+			break;
+		mdelay(1);
+		retry--;
+	} while (retry > 0);
+	if (!retry)
+		dev_err(dev, "Clear CK_OUT_EN timedout\n");
+
+	/* Set CK_OUT_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
+
+	/* Wait for CK_OUT_EN set */
+	retry = 1000;
+	do {
+		val = readl(&regs->sdcc_hc_dll_config);
+		val &= SDCC_DLL_CONFIG_CK_OUT_EN;
+		if (val)
+			break;
+		mdelay(1);
+		retry--;
+	} while (retry > 0);
+	if (!retry)
+		dev_err(dev, "Set CK_OUT_EN timedout\n");
+
+	/* Set DDR_CAL_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN);
+
+	clrbits_le32(&regs->sdcc_hc_dll_config2,
+		     SDCC_DLL_CONFIG2_DLL_CLOCK_DIS);
+
+	clrsetbits_le32(&regs->sdcc_hc_dll_config2,
+			SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10);
+
+	clrsetbits_le32(&regs->sdcc_hc_dll_config2,
+			SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2));
+
+	setbits_le32(&regs->sdcc_hc_dll_config2,
+		     SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW);
+
+	return 0;
+}
+
+static int ethqos_rgmii_macro_init(struct udevice *dev,
+				   struct dwmac_rgmii_regs *regs,
+				   unsigned long speed)
+{
+	/* Disable loopback mode */
+	clrbits_le32(&regs->io_macro_config2,
+		     RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN);
+
+	/* Select RGMII, write 0 to interface select */
+	clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_INTF_SEL);
+
+	switch (speed) {
+	case SPEED_1000:
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
+		clrbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_BYPASS_TX_ID_EN);
+		setbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_POS_NEG_DATA_SEL);
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
+
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
+		setbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RSVD_CONFIG15);
+		setbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RX_PROG_SWAP);
+
+		/* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */
+		clrsetbits_le32(&regs->sdcc_hc_ddr_config,
+				SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57);
+		setbits_le32(&regs->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN);
+
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
+		break;
+
+	case SPEED_100:
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
+		setbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_BYPASS_TX_ID_EN);
+		clrbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_POS_NEG_DATA_SEL);
+		clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
+		clrsetbits_le32(&regs->io_macro_config,
+				RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6));
+
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
+		setbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RSVD_CONFIG15);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RX_PROG_SWAP);
+
+		/* Write 0x5 to PRG_RCLK_DLY_CODE */
+		clrsetbits_le32(&regs->sdcc_hc_ddr_config,
+				SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
+				(BIT(29) | BIT(27)));
+		setbits_le32(&regs->sdcc_hc_ddr_config,
+			     SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
+		setbits_le32(&regs->sdcc_hc_ddr_config,
+			     SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
+
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
+		break;
+
+	case SPEED_10:
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
+		setbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_BYPASS_TX_ID_EN);
+		clrbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_POS_NEG_DATA_SEL);
+		clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
+		clrsetbits_le32(&regs->io_macro_config,
+				RGMII_CONFIG_MAX_SPD_PRG_9,
+				BIT(12) | GENMASK(9, 8));
+
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RSVD_CONFIG15);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RX_PROG_SWAP);
+
+		/* Write 0x5 to PRG_RCLK_DLY_CODE */
+		clrsetbits_le32(&regs->sdcc_hc_ddr_config,
+				SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
+				(BIT(29) | BIT(27)));
+		setbits_le32(&regs->sdcc_hc_ddr_config,
+			     SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
+		setbits_le32(&regs->sdcc_hc_ddr_config,
+			     SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
+
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
+		break;
+
+	default:
+		dev_err(dev, "Invalid speed %ld\n", speed);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ethqos_configure(struct udevice *dev,
+			    struct dwmac_rgmii_regs *regs,
+			    unsigned long speed)
+{
+	unsigned int retry = 1000;
+
+	/* Reset to POR values and enable clk */
+	writel(emac_v2_3_0_por.io_macro_config, &regs->io_macro_config);
+	writel(emac_v2_3_0_por.sdcc_hc_dll_config, &regs->sdcc_hc_dll_config);
+	writel(emac_v2_3_0_por.sdcc_hc_ddr_config, &regs->sdcc_hc_ddr_config);
+	writel(emac_v2_3_0_por.sdcc_hc_dll_config2, &regs->sdcc_hc_dll_config2);
+	writel(emac_v2_3_0_por.sdcc_usr_ctl, &regs->sdcc_usr_ctl);
+	writel(emac_v2_3_0_por.io_macro_config2, &regs->io_macro_config2);
+
+	ethqos_set_func_clk_en(regs);
+
+	/* Initialize the DLL first */
+
+	/* Set DLL_RST */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
+
+	/* Set PDN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
+
+	/* Clear DLL_RST */
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
+
+	/* Clear PDN */
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
+
+	if (speed == SPEED_1000) {
+		/* Set DLL_EN */
+		setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
+
+		/* Set CK_OUT_EN */
+		setbits_le32(&regs->sdcc_hc_dll_config,
+			     SDCC_DLL_CONFIG_CK_OUT_EN);
+
+		/* Set USR_CTL bit 26 with mask of 3 bits */
+		clrsetbits_le32(&regs->sdcc_usr_ctl, GENMASK(26, 24), BIT(26));
+
+		/* wait for DLL LOCK */
+		do {
+			mdelay(1);
+			if (readl(&regs->sdc4_status) & SDC4_STATUS_DLL_LOCK)
+				break;
+			retry--;
+		} while (retry > 0);
+		if (!retry)
+			dev_err(dev, "Timeout while waiting for DLL lock\n");
+
+		ethqos_dll_configure(dev, regs);
+	}
+
+	ethqos_rgmii_macro_init(dev, regs, speed);
+
+	return 0;
+}
+
+static void ethqos_rgmii_dump(struct udevice *dev,
+			      struct dwmac_rgmii_regs *regs)
+{
+	dev_dbg(dev, "Rgmii register dump\n");
+	dev_dbg(dev, "RGMII_IO_MACRO_CONFIG: %08x\n",
+		readl(&regs->io_macro_config));
+	dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n",
+		readl(&regs->sdcc_hc_dll_config));
+	dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n",
+		readl(&regs->sdcc_hc_ddr_config));
+	dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n",
+		readl(&regs->sdcc_hc_dll_config2));
+	dev_dbg(dev, "SDC4_STATUS: %08x\n",
+		readl(&regs->sdc4_status));
+	dev_dbg(dev, "SDCC_USR_CTL: %08x\n",
+		readl(&regs->sdcc_usr_ctl));
+	dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n",
+		readl(&regs->io_macro_config2));
+	dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n",
+		readl(&regs->io_macro_debug1));
+	dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n",
+		readl(&regs->emac_sys_low_power_dbg));
+}
+
+static int qcom_eqos_rgmii_set_speed(struct udevice *dev,
+				     void *rgmii_regs,
+				     unsigned long speed)
+{
+	int ret;
+
+	ethqos_rgmii_dump(dev, rgmii_regs);
+
+	ret = ethqos_configure(dev, rgmii_regs, speed);
+	if (ret)
+		return ret;
+
+	ethqos_rgmii_dump(dev, rgmii_regs);
+
+	return 0;
+}
+
+static int qcom_eqos_rgmii_reset(struct udevice *dev, void *rgmii_regs)
+{
+	ethqos_set_func_clk_en(rgmii_regs);
+
+	return 0;
+}
+
+static int eqos_start_clks_qcom(struct udevice *dev)
+{
+	if (IS_ENABLED(CONFIG_CLK)) {
+		struct clk_bulk clocks;
+		int ret;
+
+		ret = clk_get_bulk(dev, &clocks);
+		if (ret)
+			return ret;
+
+		ret = clk_enable_bulk(&clocks);
+		if (ret)
+			return ret;
+	}
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+static int eqos_stop_clks_qcom(struct udevice *dev)
+{
+	if (IS_ENABLED(CONFIG_CLK)) {
+		struct clk_bulk clocks;
+		int ret;
+
+		ret = clk_get_bulk(dev, &clocks);
+		if (ret)
+			return ret;
+
+		ret = clk_disable_bulk(&clocks);
+		if (ret)
+			return ret;
+	}
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+static int eqos_start_resets_qcom(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	int ret;
+
+	debug("%s(dev=%p):\n", __func__, dev);
+
+	if (!eqos->phy) {
+		ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
+		if (ret < 0) {
+			pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d", ret);
+			return ret;
+		}
+
+		udelay(eqos->reset_delays[0]);
+
+		ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1);
+		if (ret < 0) {
+			pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret);
+			return ret;
+		}
+
+		udelay(eqos->reset_delays[1]);
+
+		ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
+		if (ret < 0) {
+			pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret);
+			return ret;
+		}
+
+		udelay(eqos->reset_delays[2]);
+	}
+
+	ret = reset_deassert(&eqos->reset_ctl);
+	if (ret < 0) {
+		pr_err("reset_deassert() failed: %d", ret);
+		return ret;
+	}
+
+	ret = qcom_eqos_rgmii_reset(dev, eqos->eqos_qcom_rgmii_regs);
+	if (ret < 0) {
+		pr_err("qcom rgmii_reset failed: %d", ret);
+		return ret;
+	}
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+/* Clock rates */
+#define RGMII_1000_NOM_CLK_FREQ			(250 * 1000 * 1000UL)
+#define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ	 (50 * 1000 * 1000UL)
+#define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ	  (5 * 1000 * 1000UL)
+
+static int eqos_set_tx_clk_speed_qcom(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	ulong rate;
+	int ret;
+
+	debug("%s(dev=%p):\n", __func__, dev);
+
+	switch (eqos->phy->speed) {
+	case SPEED_1000:
+		rate = RGMII_1000_NOM_CLK_FREQ;
+		break;
+	case SPEED_100:
+		rate = RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ;
+		break;
+	case SPEED_10:
+		rate = RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ;
+		break;
+	default:
+		pr_err("invalid speed %d", eqos->phy->speed);
+		return -EINVAL;
+	}
+
+	ret = clk_set_rate(&eqos->clk_tx, rate);
+	if (ret < 0) {
+		pr_err("clk_set_rate(tx_clk, %lu) failed: %d", rate, ret);
+		return ret;
+	}
+
+	ret = qcom_eqos_rgmii_set_speed(dev, eqos->eqos_qcom_rgmii_regs,
+					eqos->phy->speed);
+	if (ret < 0) {
+		pr_err("qcom set_speed: %d, failed: %d", eqos->phy->speed, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int eqos_probe_resources_qcom(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	phy_interface_t interface;
+	int reset_flags = GPIOD_IS_OUT;
+	int ret;
+
+	debug("%s(dev=%p):\n", __func__, dev);
+
+	interface = eqos->config->interface(dev);
+
+	if (interface == PHY_INTERFACE_MODE_NA) {
+		pr_err("Invalid PHY interface\n");
+		return -EINVAL;
+	}
+
+	eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0);
+
+	eqos->tx_fifo_sz = dev_read_u32_default(dev, "tx-fifo-depth", 0);
+	eqos->rx_fifo_sz = dev_read_u32_default(dev, "rx-fifo-depth", 0);
+
+	ret = reset_get_by_name(dev, "emac", &eqos->reset_ctl);
+	if (ret) {
+		pr_err("reset_get_by_name(rst) failed: %d", ret);
+		return ret;
+	}
+
+	if (dev_read_bool(dev, "snps,reset-active-low"))
+		reset_flags |= GPIOD_ACTIVE_LOW;
+
+	ret = gpio_request_by_name(dev, "snps,reset-gpio", 0,
+				   &eqos->phy_reset_gpio, reset_flags);
+	if (ret == 0) {
+		ret = dev_read_u32_array(dev, "snps,reset-delays-us",
+					 eqos->reset_delays, 3);
+	} else if (ret == -ENOENT) {
+		ret = 0;
+	}
+
+	eqos->eqos_qcom_rgmii_regs = (void *)dev_read_addr_name(dev, "rgmii");
+	if ((fdt_addr_t)eqos->eqos_qcom_rgmii_regs == FDT_ADDR_T_NONE) {
+		pr_err("Invalid RGMII address\n");
+		return -EINVAL;
+	}
+
+	ret = clk_get_by_name(dev, "rgmii", &eqos->clk_tx);
+	if (ret) {
+		pr_err("clk_get_by_name(tx) failed: %d", ret);
+		return -EINVAL;
+	}
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+static int eqos_remove_resources_qcom(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+
+	debug("%s(dev=%p):\n", __func__, dev);
+
+	clk_free(&eqos->clk_tx);
+	dm_gpio_free(dev, &eqos->phy_reset_gpio);
+	reset_free(&eqos->reset_ctl);
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+static struct eqos_ops eqos_qcom_ops = {
+	.eqos_inval_desc = eqos_inval_desc_generic,
+	.eqos_flush_desc = eqos_flush_desc_generic,
+	.eqos_inval_buffer = eqos_inval_buffer_generic,
+	.eqos_flush_buffer = eqos_flush_buffer_generic,
+	.eqos_probe_resources = eqos_probe_resources_qcom,
+	.eqos_remove_resources = eqos_remove_resources_qcom,
+	.eqos_stop_resets = eqos_null_ops,
+	.eqos_start_resets = eqos_start_resets_qcom,
+	.eqos_stop_clks = eqos_stop_clks_qcom,
+	.eqos_start_clks = eqos_start_clks_qcom,
+	.eqos_calibrate_pads = eqos_null_ops,
+	.eqos_disable_calibration = eqos_null_ops,
+	.eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_qcom,
+	.eqos_get_enetaddr = eqos_null_ops,
+};
+
+struct eqos_config __maybe_unused eqos_qcom_config = {
+	.reg_access_always_ok = false,
+	.mdio_wait = 10,
+	.swr_wait = 50,
+	.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+	.config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+	.axi_bus_width = EQOS_AXI_WIDTH_64,
+	.interface = dev_read_phy_mode,
+	.ops = &eqos_qcom_ops
+};