From 158456089c02ef7564decea6cff2c5b285fe66d7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 6 Mar 2023 15:53:47 +0100 Subject: net: dwc_eth_qos: Add DM CLK support for i.MX8M Plus The DWMAC clock in i.MX8M Plus were so far configured via ad-hoc architecture code. Replace that with DM clock instead. This way, the driver claims all its required clock, enables and disables them, and even gets the CSR clock rate and sets the TX clock rate, without any need of architecture specific register fiddling. Drop the architecture specific code while at it too. The adjustment here is modeled after STM32MP15xx clock handling in this driver. Signed-off-by: Marek Vasut --- drivers/net/dwc_eth_qos_imx.c | 131 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 10 deletions(-) (limited to 'drivers/net/dwc_eth_qos_imx.c') diff --git a/drivers/net/dwc_eth_qos_imx.c b/drivers/net/dwc_eth_qos_imx.c index 42cb164ad1..f5f3f2099f 100644 --- a/drivers/net/dwc_eth_qos_imx.c +++ b/drivers/net/dwc_eth_qos_imx.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -32,20 +33,18 @@ __weak u32 imx_get_eqos_csr_clk(void) return 100 * 1000000; } -__weak int imx_eqos_txclk_set_rate(unsigned long rate) -{ - return 0; -} - static ulong eqos_get_tick_clk_rate_imx(struct udevice *dev) { - return imx_get_eqos_csr_clk(); + struct eqos_priv *eqos = dev_get_priv(dev); + + return clk_get_rate(&eqos->clk_master_bus); } static int eqos_probe_resources_imx(struct udevice *dev) { struct eqos_priv *eqos = dev_get_priv(dev); phy_interface_t interface; + int ret; debug("%s(dev=%p):\n", __func__, dev); @@ -56,6 +55,118 @@ static int eqos_probe_resources_imx(struct udevice *dev) return -EINVAL; } + eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0); + + ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus); + if (ret) { + dev_dbg(dev, "clk_get_by_name(master_bus) failed: %d", ret); + goto err_probe; + } + + ret = clk_get_by_name(dev, "ptp_ref", &eqos->clk_ptp_ref); + if (ret) { + dev_dbg(dev, "clk_get_by_name(ptp_ref) failed: %d", ret); + goto err_free_clk_master_bus; + } + + ret = clk_get_by_name(dev, "tx", &eqos->clk_tx); + if (ret) { + dev_dbg(dev, "clk_get_by_name(tx) failed: %d", ret); + goto err_free_clk_ptp_ref; + } + + ret = clk_get_by_name(dev, "pclk", &eqos->clk_ck); + if (ret) { + dev_dbg(dev, "clk_get_by_name(pclk) failed: %d", ret); + goto err_free_clk_tx; + } + + debug("%s: OK\n", __func__); + return 0; + +err_free_clk_tx: + clk_free(&eqos->clk_tx); +err_free_clk_ptp_ref: + clk_free(&eqos->clk_ptp_ref); +err_free_clk_master_bus: + clk_free(&eqos->clk_master_bus); +err_probe: + + debug("%s: returns %d\n", __func__, ret); + return ret; +} + +static int eqos_remove_resources_imx(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clk_free(&eqos->clk_ck); + clk_free(&eqos->clk_tx); + clk_free(&eqos->clk_ptp_ref); + clk_free(&eqos->clk_master_bus); + + debug("%s: OK\n", __func__); + return 0; +} + +static int eqos_start_clks_imx(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + ret = clk_enable(&eqos->clk_master_bus); + if (ret < 0) { + dev_dbg(dev, "clk_enable(clk_master_bus) failed: %d", ret); + goto err; + } + + ret = clk_enable(&eqos->clk_ptp_ref); + if (ret < 0) { + dev_dbg(dev, "clk_enable(clk_ptp_ref) failed: %d", ret); + goto err_disable_clk_master_bus; + } + + ret = clk_enable(&eqos->clk_tx); + if (ret < 0) { + dev_dbg(dev, "clk_enable(clk_tx) failed: %d", ret); + goto err_disable_clk_ptp_ref; + } + + ret = clk_enable(&eqos->clk_ck); + if (ret < 0) { + dev_dbg(dev, "clk_enable(clk_ck) failed: %d", ret); + goto err_disable_clk_tx; + } + + debug("%s: OK\n", __func__); + return 0; + +err_disable_clk_tx: + clk_disable(&eqos->clk_tx); +err_disable_clk_ptp_ref: + clk_disable(&eqos->clk_ptp_ref); +err_disable_clk_master_bus: + clk_disable(&eqos->clk_master_bus); +err: + debug("%s: FAILED: %d\n", __func__, ret); + return ret; +} + +static int eqos_stop_clks_imx(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clk_disable(&eqos->clk_ck); + clk_disable(&eqos->clk_tx); + clk_disable(&eqos->clk_ptp_ref); + clk_disable(&eqos->clk_master_bus); + debug("%s: OK\n", __func__); return 0; } @@ -83,7 +194,7 @@ static int eqos_set_tx_clk_speed_imx(struct udevice *dev) return -EINVAL; } - ret = imx_eqos_txclk_set_rate(rate); + ret = clk_set_rate(&eqos->clk_tx, rate); if (ret < 0) { pr_err("imx (tx_clk, %lu) failed: %d", rate, ret); return ret; @@ -107,11 +218,11 @@ static struct eqos_ops eqos_imx_ops = { .eqos_inval_buffer = eqos_inval_buffer_generic, .eqos_flush_buffer = eqos_flush_buffer_generic, .eqos_probe_resources = eqos_probe_resources_imx, - .eqos_remove_resources = eqos_null_ops, + .eqos_remove_resources = eqos_remove_resources_imx, .eqos_stop_resets = eqos_null_ops, .eqos_start_resets = eqos_null_ops, - .eqos_stop_clks = eqos_null_ops, - .eqos_start_clks = eqos_null_ops, + .eqos_stop_clks = eqos_stop_clks_imx, + .eqos_start_clks = eqos_start_clks_imx, .eqos_calibrate_pads = eqos_null_ops, .eqos_disable_calibration = eqos_null_ops, .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_imx, -- cgit v1.2.3 From 2e9b3014dfa3d71d6d40e84060ce488b6a5b2836 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 6 Mar 2023 15:53:48 +0100 Subject: net: dwc_eth_qos: Add i.MX8M Plus RMII support With DM clock support in place, it is easy to add RMII support into the MAC driver. The RMII cannot operate at 1000 Mbps and at 100 and 10 Mbps the clock frequency is 50 MHz and 5 MHz instead of 25 MHz and 2.5 MHz. The board DT requires the following adjustments to EQoS node: phy-mode = "rmii"; assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_266M>, <&clk IMX8MP_SYS_PLL2_100M>, <&clk IMX8MP_SYS_PLL2_50M>; assigned-clock-rates = <0>, <100000000>, <50000000>; Reviewed-by: Ramon Fried Signed-off-by: Marek Vasut --- drivers/net/dwc_eth_qos_imx.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/net/dwc_eth_qos_imx.c') diff --git a/drivers/net/dwc_eth_qos_imx.c b/drivers/net/dwc_eth_qos_imx.c index f5f3f2099f..962c537324 100644 --- a/drivers/net/dwc_eth_qos_imx.c +++ b/drivers/net/dwc_eth_qos_imx.c @@ -179,21 +179,28 @@ static int eqos_set_tx_clk_speed_imx(struct udevice *dev) debug("%s(dev=%p):\n", __func__, dev); - switch (eqos->phy->speed) { - case SPEED_1000: - rate = 125 * 1000 * 1000; - break; - case SPEED_100: - rate = 25 * 1000 * 1000; - break; - case SPEED_10: - rate = 2.5 * 1000 * 1000; - break; - default: + if (eqos->phy->interface == PHY_INTERFACE_MODE_RMII) + rate = 5000; /* 5000 kHz = 5 MHz */ + else + rate = 2500; /* 2500 kHz = 2.5 MHz */ + + if (eqos->phy->speed == SPEED_1000 && + (eqos->phy->interface == PHY_INTERFACE_MODE_RGMII || + eqos->phy->interface == PHY_INTERFACE_MODE_RGMII_ID || + eqos->phy->interface == PHY_INTERFACE_MODE_RGMII_RXID || + eqos->phy->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { + rate *= 50; /* Use 50x base rate i.e. 125 MHz */ + } else if (eqos->phy->speed == SPEED_100) { + rate *= 10; /* Use 10x base rate */ + } else if (eqos->phy->speed == SPEED_10) { + rate *= 1; /* Use base rate */ + } else { pr_err("invalid speed %d", eqos->phy->speed); return -EINVAL; } + rate *= 1000; /* clk_set_rate() operates in Hz */ + ret = clk_set_rate(&eqos->clk_tx, rate); if (ret < 0) { pr_err("imx (tx_clk, %lu) failed: %d", rate, ret); -- cgit v1.2.3 From f9e950b9bfd88ac9b22aaf4e3ff04127bdace287 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 6 Mar 2023 15:53:49 +0100 Subject: net: dwc_eth_qos: Add board_interface_eth_init() for i.MX8M Plus Implement common board_interface_eth_init() and call it from the DWMAC driver to configure IOMUXC GPR[1] register according to the PHY mode obtained from DT. This supports all three interface modes supported by the i.MX8M Plus DWMAC and supersedes current board-side configuration of the same IOMUX GPR[1] duplicated in the board files. Reviewed-by: Ramon Fried Signed-off-by: Marek Vasut --- arch/arm/include/asm/arch-imx8m/imx-regs.h | 8 +++- arch/arm/mach-imx/imx8m/clock_imx8mm.c | 59 +++++++++++++++++++++++++++++- drivers/net/dwc_eth_qos_imx.c | 4 ++ 3 files changed, 69 insertions(+), 2 deletions(-) (limited to 'drivers/net/dwc_eth_qos_imx.c') diff --git a/arch/arm/include/asm/arch-imx8m/imx-regs.h b/arch/arm/include/asm/arch-imx8m/imx-regs.h index 1559bf6d21..1818b459fa 100644 --- a/arch/arm/include/asm/arch-imx8m/imx-regs.h +++ b/arch/arm/include/asm/arch-imx8m/imx-regs.h @@ -89,7 +89,13 @@ #define DDRC_IPS_BASE_ADDR(X) (0x3d400000 + ((X) * 0x2000000)) #define DDR_CSD1_BASE_ADDR 0x40000000 -#define IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_MASK 0x70000 +#define IOMUXC_GPR_GPR1_GPR_ENET_QOS_RGMII_EN BIT(21) +#define IOMUXC_GPR_GPR1_GPR_ENET_QOS_CLK_TX_CLK_SEL BIT(20) +#define IOMUXC_GPR_GPR1_GPR_ENET_QOS_CLK_GEN_EN BIT(19) +#define IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_MASK GENMASK(18, 16) +#define IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_MII (0 << 16) +#define IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_RGMII (1 << 16) +#define IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_RMII (4 << 16) #define FEC_QUIRK_ENET_MAC #ifdef CONFIG_ARMV8_PSCI /* Final jump location */ diff --git a/arch/arm/mach-imx/imx8m/clock_imx8mm.c b/arch/arm/mach-imx/imx8m/clock_imx8mm.c index 494bfbedc8..1546c9ba9a 100644 --- a/arch/arm/mach-imx/imx8m/clock_imx8mm.c +++ b/arch/arm/mach-imx/imx8m/clock_imx8mm.c @@ -15,6 +15,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -825,7 +826,7 @@ u32 mxc_get_clock(enum mxc_clock clk) return 0; } -#ifdef CONFIG_DWC_ETH_QOS +#if defined(CONFIG_IMX8MP) && defined(CONFIG_DWC_ETH_QOS) int set_clk_eqos(enum enet_freq type) { u32 target; @@ -872,6 +873,52 @@ int set_clk_eqos(enum enet_freq type) return 0; } + +static int imx8mp_eqos_interface_init(struct udevice *dev, + phy_interface_t interface_type) +{ + struct iomuxc_gpr_base_regs *gpr = + (struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR; + + clrbits_le32(&gpr->gpr[1], + IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_MASK | + IOMUXC_GPR_GPR1_GPR_ENET_QOS_RGMII_EN | + IOMUXC_GPR_GPR1_GPR_ENET_QOS_CLK_TX_CLK_SEL | + IOMUXC_GPR_GPR1_GPR_ENET_QOS_CLK_GEN_EN); + + switch (interface_type) { + case PHY_INTERFACE_MODE_MII: + setbits_le32(&gpr->gpr[1], + IOMUXC_GPR_GPR1_GPR_ENET_QOS_CLK_GEN_EN | + IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_MII); + break; + case PHY_INTERFACE_MODE_RMII: + setbits_le32(&gpr->gpr[1], + IOMUXC_GPR_GPR1_GPR_ENET_QOS_CLK_TX_CLK_SEL | + IOMUXC_GPR_GPR1_GPR_ENET_QOS_CLK_GEN_EN | + IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_RMII); + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + setbits_le32(&gpr->gpr[1], + IOMUXC_GPR_GPR1_GPR_ENET_QOS_RGMII_EN | + IOMUXC_GPR_GPR1_GPR_ENET_QOS_CLK_GEN_EN | + IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_RGMII); + break; + default: + return -EINVAL; + } + + return 0; +} +#else +static int imx8mp_eqos_interface_init(struct udevice *dev, + phy_interface_t interface_type) +{ + return 0; +} #endif #ifdef CONFIG_FEC_MXC @@ -922,3 +969,13 @@ int set_clk_enet(enum enet_freq type) return 0; } #endif + +int board_interface_eth_init(struct udevice *dev, phy_interface_t interface_type) +{ + if (IS_ENABLED(CONFIG_IMX8MP) && + IS_ENABLED(CONFIG_DWC_ETH_QOS) && + device_is_compatible(dev, "nxp,imx8mp-dwmac-eqos")) + return imx8mp_eqos_interface_init(dev, interface_type); + + return -EINVAL; +} diff --git a/drivers/net/dwc_eth_qos_imx.c b/drivers/net/dwc_eth_qos_imx.c index 962c537324..60f3f3f5a1 100644 --- a/drivers/net/dwc_eth_qos_imx.c +++ b/drivers/net/dwc_eth_qos_imx.c @@ -55,6 +55,10 @@ static int eqos_probe_resources_imx(struct udevice *dev) return -EINVAL; } + ret = board_interface_eth_init(dev, interface); + if (ret) + return -EINVAL; + eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0); ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus); -- cgit v1.2.3