// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016, NVIDIA CORPORATION. * Copyright (c) 2019, Ahmad Fatoum, Pengutronix * * Portions based on U-Boot's rtl8169.c and dwc_eth_qos. */ #include #include #include #include #include #include "designware_eqos.h" #define SYSCFG_PMCR_ETH_CLK_SEL BIT(16) #define SYSCFG_PMCR_ETH_REF_CLK_SEL BIT(17) /* Ethernet PHY interface selection in register SYSCFG Configuration *------------------------------------------ * src |BIT(23)| BIT(22)| BIT(21)|BIT(20)| *------------------------------------------ * MII | 0 | 0 | 0 | 1 | *------------------------------------------ * GMII | 0 | 0 | 0 | 0 | *------------------------------------------ * RGMII | 0 | 0 | 1 | n/a | *------------------------------------------ * RMII | 1 | 0 | 0 | n/a | *------------------------------------------ */ #define SYSCFG_PMCR_ETH_SEL_MII BIT(20) #define SYSCFG_PMCR_ETH_SEL_RGMII BIT(21) #define SYSCFG_PMCR_ETH_SEL_RMII BIT(23) #define SYSCFG_PMCR_ETH_SEL_GMII 0 #define SYSCFG_MCU_ETH_SEL_MII 0 #define SYSCFG_MCU_ETH_SEL_RMII 1 /* Descriptors */ #define SYSCFG_MCU_ETH_MASK BIT(23) #define SYSCFG_MP1_ETH_MASK GENMASK(23, 16) #define SYSCFG_PMCCLRR_OFFSET 0x40 struct eqos_stm32 { struct clk_bulk_data *clks; int num_clks; struct regmap *regmap; u32 mode_reg; int eth_clk_sel_reg; int eth_ref_clk_sel_reg; }; static inline struct eqos_stm32 *to_stm32(struct eqos *eqos) { return eqos->priv; } enum { CLK_STMMACETH, CLK_MAX_RX, CLK_MAX_TX, CLK_SYSCFG, }; static const struct clk_bulk_data stm32_clks[] = { [CLK_STMMACETH] = { .id = "stmmaceth" }, [CLK_MAX_RX] = { .id = "mac-clk-rx" }, [CLK_MAX_TX] = { .id = "mac-clk-tx" }, [CLK_SYSCFG] = { .id = "syscfg-clk" }, }; static unsigned long eqos_get_csr_clk_rate_stm32(struct eqos *eqos) { return clk_get_rate(to_stm32(eqos)->clks[CLK_STMMACETH].clk); } static int eqos_set_mode_stm32(struct eqos_stm32 *priv, phy_interface_t interface) { u32 val, reg = priv->mode_reg; int ret; switch (interface) { case PHY_INTERFACE_MODE_MII: val = SYSCFG_PMCR_ETH_SEL_MII; break; case PHY_INTERFACE_MODE_GMII: val = SYSCFG_PMCR_ETH_SEL_GMII; if (priv->eth_clk_sel_reg) val |= SYSCFG_PMCR_ETH_CLK_SEL; break; case PHY_INTERFACE_MODE_RMII: val = SYSCFG_PMCR_ETH_SEL_RMII; if (priv->eth_ref_clk_sel_reg) val |= SYSCFG_PMCR_ETH_REF_CLK_SEL; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: val = SYSCFG_PMCR_ETH_SEL_RGMII; if (priv->eth_clk_sel_reg) val |= SYSCFG_PMCR_ETH_CLK_SEL; break; default: return -EINVAL; } /* Need to update PMCCLRR (clear register) */ ret = regmap_write(priv->regmap, reg + SYSCFG_PMCCLRR_OFFSET, SYSCFG_MP1_ETH_MASK); if (ret) return -EIO; /* Update PMCSETR (set register) */ regmap_update_bits(priv->regmap, reg, GENMASK(23, 16), val); return 0; } static int eqos_init_stm32(struct device_d *dev, struct eqos *eqos) { struct device_node *np = dev->device_node; struct eqos_stm32 *priv = to_stm32(eqos); struct clk_bulk_data *eth_ck; int ret; /* Gigabit Ethernet 125MHz clock selection. */ priv->eth_clk_sel_reg = of_property_read_bool(np, "st,eth-clk-sel"); /* Ethernet 50Mhz RMII clock selection */ priv->eth_ref_clk_sel_reg = of_property_read_bool(np, "st,eth-ref-clk-sel"); priv->regmap = syscon_regmap_lookup_by_phandle(dev->device_node, "st,syscon"); if (IS_ERR(priv->regmap)) { dev_err(dev, "Could not get st,syscon node\n"); return PTR_ERR(priv->regmap); } ret = of_property_read_u32_index(dev->device_node, "st,syscon", 1, &priv->mode_reg); if (ret) { dev_err(dev, "Can't get sysconfig mode offset (%s)\n", strerror(-ret)); return -EINVAL; } ret = eqos_set_mode_stm32(priv, eqos->interface); if (ret) dev_warn(dev, "Configuring syscfg failed: %s\n", strerror(-ret)); priv->num_clks = ARRAY_SIZE(stm32_clks) + 1; priv->clks = xmalloc(priv->num_clks * sizeof(*priv->clks)); memcpy(priv->clks, stm32_clks, sizeof stm32_clks); ret = clk_bulk_get(dev, ARRAY_SIZE(stm32_clks), priv->clks); if (ret) { dev_err(dev, "Failed to get clks: %s\n", strerror(-ret)); return ret; } eth_ck = &priv->clks[ARRAY_SIZE(stm32_clks)]; eth_ck->id = "eth-ck"; eth_ck->clk = clk_get(dev, eth_ck->id); if (IS_ERR(eth_ck->clk)) { priv->num_clks--; dev_dbg(dev, "No phy clock provided. Continuing without.\n"); } return 0; } static int eqos_start_stm32(struct eth_device *edev) { struct eqos *eqos = edev->priv; struct eqos_stm32 *priv = to_stm32(eqos); int ret; ret = clk_bulk_enable(priv->num_clks, priv->clks); if (ret < 0) { eqos_err(eqos, "clk_bulk_enable() failed: %s\n", strerror(-ret)); return ret; } udelay(10); ret = eqos_start(edev); if (ret) goto err_stop_clks; return 0; err_stop_clks: clk_bulk_disable(priv->num_clks, priv->clks); return ret; } static void eqos_stop_stm32(struct eth_device *edev) { struct eqos_stm32 *priv = to_stm32(edev->priv); clk_bulk_disable(priv->num_clks, priv->clks); } // todo split! static struct eqos_ops stm32_ops = { .init = eqos_init_stm32, .get_ethaddr = eqos_get_ethaddr, .set_ethaddr = eqos_set_ethaddr, .start = eqos_start_stm32, .stop = eqos_stop_stm32, .adjust_link = eqos_adjust_link, .get_csr_clk_rate = eqos_get_csr_clk_rate_stm32, .mdio_wait_us = 10 * USEC_PER_MSEC, .clk_csr = EQOS_MDIO_ADDR_CR_250_300, .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV, }; static int eqos_probe_stm32(struct device_d *dev) { return eqos_probe(dev, &stm32_ops, xzalloc(sizeof(struct eqos_stm32))); } static void eqos_remove_stm32(struct device_d *dev) { struct eqos_stm32 *priv = to_stm32(dev->priv); eqos_remove(dev); clk_bulk_put(priv->num_clks, priv->clks); } static const struct of_device_id eqos_stm32_ids[] = { { .compatible = "st,stm32mp1-dwmac" }, { /* sentinel */ } }; static struct driver_d eqos_stm32_driver = { .name = "eqos-stm32", .probe = eqos_probe_stm32, .remove = eqos_remove_stm32, .of_compatible = DRV_OF_COMPAT(eqos_stm32_ids), }; device_platform_driver(eqos_stm32_driver);