Message ID | 20230201135901.482671-9-sumit.garg@linaro.org |
---|---|
State | Accepted |
Commit | d382025dc555b4e847877bb9b2d492ad1127d144 |
Headers | show |
Series | QCS404: Add ethernet and I2C drivers | expand |
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(®s->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(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN); > + > + /* Set CDR_EXT_EN */ > + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN); > + > + /* Clear CK_OUT_EN */ > + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN); > + > + /* Set DLL_EN */ > + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN); > + > + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN); > + > + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE); > + > + /* Wait for CK_OUT_EN clear */ > + do { > + val = readl(®s->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(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN); > + > + /* Wait for CK_OUT_EN set */ > + retry = 1000; > + do { > + val = readl(®s->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(®s->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN); > + > + clrbits_le32(®s->sdcc_hc_dll_config2, > + SDCC_DLL_CONFIG2_DLL_CLOCK_DIS); > + > + clrsetbits_le32(®s->sdcc_hc_dll_config2, > + SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10); > + > + clrsetbits_le32(®s->sdcc_hc_dll_config2, > + SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2)); > + > + setbits_le32(®s->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(®s->io_macro_config2, > + RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN); > + > + /* Select RGMII, write 0 to interface select */ > + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_INTF_SEL); > + > + switch (speed) { > + case SPEED_1000: > + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); > + clrbits_le32(®s->io_macro_config, > + RGMII_CONFIG_BYPASS_TX_ID_EN); > + setbits_le32(®s->io_macro_config, > + RGMII_CONFIG_POS_NEG_DATA_SEL); > + setbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); > + > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); > + setbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_RSVD_CONFIG15); > + setbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_RX_PROG_SWAP); > + > + /* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */ > + clrsetbits_le32(®s->sdcc_hc_ddr_config, > + SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57); > + setbits_le32(®s->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN); > + > + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); > + break; > + > + case SPEED_100: > + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); > + setbits_le32(®s->io_macro_config, > + RGMII_CONFIG_BYPASS_TX_ID_EN); > + clrbits_le32(®s->io_macro_config, > + RGMII_CONFIG_POS_NEG_DATA_SEL); > + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); > + clrsetbits_le32(®s->io_macro_config, > + RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6)); > + > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); > + setbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_RSVD_CONFIG15); > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_RX_PROG_SWAP); > + > + /* Write 0x5 to PRG_RCLK_DLY_CODE */ > + clrsetbits_le32(®s->sdcc_hc_ddr_config, > + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, > + (BIT(29) | BIT(27))); > + setbits_le32(®s->sdcc_hc_ddr_config, > + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY); > + setbits_le32(®s->sdcc_hc_ddr_config, > + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN); > + > + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); > + break; > + > + case SPEED_10: > + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); > + setbits_le32(®s->io_macro_config, > + RGMII_CONFIG_BYPASS_TX_ID_EN); > + clrbits_le32(®s->io_macro_config, > + RGMII_CONFIG_POS_NEG_DATA_SEL); > + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); > + clrsetbits_le32(®s->io_macro_config, > + RGMII_CONFIG_MAX_SPD_PRG_9, > + BIT(12) | GENMASK(9, 8)); > + > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_RSVD_CONFIG15); > + clrbits_le32(®s->io_macro_config2, > + RGMII_CONFIG2_RX_PROG_SWAP); > + > + /* Write 0x5 to PRG_RCLK_DLY_CODE */ > + clrsetbits_le32(®s->sdcc_hc_ddr_config, > + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, > + (BIT(29) | BIT(27))); > + setbits_le32(®s->sdcc_hc_ddr_config, > + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY); > + setbits_le32(®s->sdcc_hc_ddr_config, > + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN); > + > + setbits_le32(®s->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, ®s->io_macro_config); > + writel(emac_v2_3_0_por.sdcc_hc_dll_config, ®s->sdcc_hc_dll_config); > + writel(emac_v2_3_0_por.sdcc_hc_ddr_config, ®s->sdcc_hc_ddr_config); > + writel(emac_v2_3_0_por.sdcc_hc_dll_config2, ®s->sdcc_hc_dll_config2); > + writel(emac_v2_3_0_por.sdcc_usr_ctl, ®s->sdcc_usr_ctl); > + writel(emac_v2_3_0_por.io_macro_config2, ®s->io_macro_config2); > + > + ethqos_set_func_clk_en(regs); > + > + /* Initialize the DLL first */ > + > + /* Set DLL_RST */ > + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST); > + > + /* Set PDN */ > + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN); > + > + /* Clear DLL_RST */ > + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST); > + > + /* Clear PDN */ > + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN); > + > + if (speed == SPEED_1000) { > + /* Set DLL_EN */ > + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN); > + > + /* Set CK_OUT_EN */ > + setbits_le32(®s->sdcc_hc_dll_config, > + SDCC_DLL_CONFIG_CK_OUT_EN); > + > + /* Set USR_CTL bit 26 with mask of 3 bits */ > + clrsetbits_le32(®s->sdcc_usr_ctl, GENMASK(26, 24), BIT(26)); > + > + /* wait for DLL LOCK */ > + do { > + mdelay(1); > + if (readl(®s->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(®s->io_macro_config)); > + dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n", > + readl(®s->sdcc_hc_dll_config)); > + dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n", > + readl(®s->sdcc_hc_ddr_config)); > + dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n", > + readl(®s->sdcc_hc_dll_config2)); > + dev_dbg(dev, "SDC4_STATUS: %08x\n", > + readl(®s->sdc4_status)); > + dev_dbg(dev, "SDCC_USR_CTL: %08x\n", > + readl(®s->sdcc_usr_ctl)); > + dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n", > + readl(®s->io_macro_config2)); > + dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n", > + readl(®s->io_macro_debug1)); > + dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n", > + readl(®s->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>
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 --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(®s->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(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN); + + /* Set CDR_EXT_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN); + + /* Clear CK_OUT_EN */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Set DLL_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN); + + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN); + + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE); + + /* Wait for CK_OUT_EN clear */ + do { + val = readl(®s->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(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Wait for CK_OUT_EN set */ + retry = 1000; + do { + val = readl(®s->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(®s->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN); + + clrbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_DLL_CLOCK_DIS); + + clrsetbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10); + + clrsetbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2)); + + setbits_le32(®s->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(®s->io_macro_config2, + RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN); + + /* Select RGMII, write 0 to interface select */ + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_INTF_SEL); + + switch (speed) { + case SPEED_1000: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + setbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57); + setbits_le32(®s->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN); + + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); + break; + + case SPEED_100: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + clrsetbits_le32(®s->io_macro_config, + RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6)); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Write 0x5 to PRG_RCLK_DLY_CODE */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, + (BIT(29) | BIT(27))); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN); + + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); + break; + + case SPEED_10: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + clrsetbits_le32(®s->io_macro_config, + RGMII_CONFIG_MAX_SPD_PRG_9, + BIT(12) | GENMASK(9, 8)); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Write 0x5 to PRG_RCLK_DLY_CODE */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, + (BIT(29) | BIT(27))); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN); + + setbits_le32(®s->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, ®s->io_macro_config); + writel(emac_v2_3_0_por.sdcc_hc_dll_config, ®s->sdcc_hc_dll_config); + writel(emac_v2_3_0_por.sdcc_hc_ddr_config, ®s->sdcc_hc_ddr_config); + writel(emac_v2_3_0_por.sdcc_hc_dll_config2, ®s->sdcc_hc_dll_config2); + writel(emac_v2_3_0_por.sdcc_usr_ctl, ®s->sdcc_usr_ctl); + writel(emac_v2_3_0_por.io_macro_config2, ®s->io_macro_config2); + + ethqos_set_func_clk_en(regs); + + /* Initialize the DLL first */ + + /* Set DLL_RST */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST); + + /* Set PDN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN); + + /* Clear DLL_RST */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST); + + /* Clear PDN */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN); + + if (speed == SPEED_1000) { + /* Set DLL_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN); + + /* Set CK_OUT_EN */ + setbits_le32(®s->sdcc_hc_dll_config, + SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Set USR_CTL bit 26 with mask of 3 bits */ + clrsetbits_le32(®s->sdcc_usr_ctl, GENMASK(26, 24), BIT(26)); + + /* wait for DLL LOCK */ + do { + mdelay(1); + if (readl(®s->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(®s->io_macro_config)); + dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n", + readl(®s->sdcc_hc_dll_config)); + dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n", + readl(®s->sdcc_hc_ddr_config)); + dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n", + readl(®s->sdcc_hc_dll_config2)); + dev_dbg(dev, "SDC4_STATUS: %08x\n", + readl(®s->sdc4_status)); + dev_dbg(dev, "SDCC_USR_CTL: %08x\n", + readl(®s->sdcc_usr_ctl)); + dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n", + readl(®s->io_macro_config2)); + dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n", + readl(®s->io_macro_debug1)); + dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n", + readl(®s->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 +};
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