A variant of the designware eqos core is used on Rockchip SoCs. Add support for it. Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> --- drivers/net/Kconfig | 6 + drivers/net/Makefile | 1 + drivers/net/designware_rockchip.c | 311 ++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 drivers/net/designware_rockchip.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 0d55ea7a3b..18931211b5 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -108,6 +108,12 @@ config DRIVER_NET_DESIGNWARE_TEGRA186 help This option enables support for the ethernet MAC on the Tegra186 & 194. +config DRIVER_NET_DESIGNWARE_ROCKCHIP + bool "Designware Universal MAC ethernet driver for Rockchip platforms" + select MFD_SYSCON + help + This option enables support for the ethernet MAC on different Rockchip SoCs + endif config DRIVER_NET_DM9K diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 656d45a868..1674d53dff 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_DRIVER_NET_DESIGNWARE_SOCFPGA) += designware_socfpga.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE_EQOS) += designware_eqos.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STM32) += designware_stm32.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE_TEGRA186) += designware_tegra186.o +obj-$(CONFIG_DRIVER_NET_DESIGNWARE_ROCKCHIP) += designware_rockchip.o obj-$(CONFIG_DRIVER_NET_DM9K) += dm9k.o obj-$(CONFIG_DRIVER_NET_E1000) += e1000/regio.o e1000/main.o e1000/eeprom.o obj-$(CONFIG_DRIVER_NET_ENC28J60) += enc28j60.o diff --git a/drivers/net/designware_rockchip.c b/drivers/net/designware_rockchip.c new file mode 100644 index 0000000000..883c1d806d --- /dev/null +++ b/drivers/net/designware_rockchip.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <common.h> +#include <init.h> +#include <dma.h> +#include <net.h> +#include <regmap.h> +#include <of_net.h> +#include <mfd/syscon.h> +#include <linux/iopoll.h> +#include <linux/sizes.h> +#include <linux/time.h> +#include <linux/clk.h> + +#include "designware_eqos.h" + +struct rk_gmac_ops { + void (*set_to_rgmii)(struct eqos *eqos, + int tx_delay, int rx_delay); + void (*set_to_rmii)(struct eqos *eqos); + void (*set_speed)(struct eqos *eqos, int speed); + void (*integrated_phy_powerup)(struct eqos *eqos); +}; + +struct eqos_rk_gmac { + struct clk_bulk_data *clks; + int num_clks; + bool clock_input; + const struct rk_gmac_ops *ops; + struct regmap *grf; + int bus_id; + u32 tx_delay; + u32 rx_delay; + struct device_d *dev; +}; + +enum { + CLK_STMMACETH, + CLK_MAC_RX, + CLK_MAC_TX, + CLK_MAC_REFOUT, + CLK_MAC_ACLK, + CLK_MAC_PCLK, + CLK_MAC_SPEED, + CLK_PTP_REF, + CLK_XPCS_PCLK, +}; + +static const struct clk_bulk_data rk_gmac_clks[] = { + [CLK_STMMACETH] = { .id = "stmmaceth" }, + [CLK_MAC_RX] = { .id = "mac_clk_rx" }, + [CLK_MAC_TX] = { .id = "mac_clk_tx" }, + [CLK_MAC_REFOUT] = { .id = "clk_mac_refout" }, + [CLK_MAC_ACLK] = { .id = "aclk_mac" }, + [CLK_MAC_PCLK] = { .id = "pclk_mac" }, + [CLK_MAC_SPEED] = { .id = "clk_mac_speed" }, + [CLK_PTP_REF] = { .id = "ptp_ref" }, + [CLK_XPCS_PCLK] = { .id = "pclk_xpcs" }, +}; + +static inline struct eqos_rk_gmac *to_rk_gmac(struct eqos *eqos) +{ + return eqos->priv; +} + +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define GRF_BIT(nr) (BIT(nr) | BIT((nr) + 16)) +#define GRF_CLR_BIT(nr) (BIT((nr) + 16)) + +#define RK3568_GRF_GMAC0_CON0 0X0380 +#define RK3568_GRF_GMAC0_CON1 0X0384 +#define RK3568_GRF_GMAC1_CON0 0X0388 +#define RK3568_GRF_GMAC1_CON1 0X038c + +/* RK3568_GRF_GMAC0_CON1 && RK3568_GRF_GMAC1_CON1 */ +#define RK3568_GMAC_PHY_INTF_SEL_RGMII \ + (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6)) +#define RK3568_GMAC_PHY_INTF_SEL_RMII \ + (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6)) +#define RK3568_GMAC_FLOW_CTRL GRF_BIT(3) +#define RK3568_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3) +#define RK3568_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1) +#define RK3568_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(1) +#define RK3568_GMAC_TXCLK_DLY_ENABLE GRF_BIT(0) +#define RK3568_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(0) + +/* RK3568_GRF_GMAC0_CON0 && RK3568_GRF_GMAC1_CON0 */ +#define RK3568_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) +#define RK3568_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) + +static unsigned long eqos_get_csr_clk_rate_rk_gmac(struct eqos *eqos) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + + return clk_get_rate(priv->clks[CLK_STMMACETH].clk); +} + +static void rk3568_set_to_rgmii(struct eqos *eqos, + int tx_delay, int rx_delay) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + struct device_d *dev = priv->dev; + u32 offset_con0, offset_con1; + + if (IS_ERR(priv->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return; + } + + offset_con0 = (priv->bus_id == 1) + ? RK3568_GRF_GMAC1_CON0 : RK3568_GRF_GMAC0_CON0; + offset_con1 = (priv->bus_id == 1) + ? RK3568_GRF_GMAC1_CON1 : RK3568_GRF_GMAC0_CON1; + + regmap_write(priv->grf, offset_con1, + RK3568_GMAC_PHY_INTF_SEL_RGMII | + RK3568_GMAC_RXCLK_DLY_ENABLE | + RK3568_GMAC_TXCLK_DLY_ENABLE); + + regmap_write(priv->grf, offset_con0, + RK3568_GMAC_CLK_RX_DL_CFG(rx_delay) | + RK3568_GMAC_CLK_TX_DL_CFG(tx_delay)); +} + +static void rk3568_set_to_rmii(struct eqos *eqos) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + struct device_d *dev = priv->dev; + u32 offset_con1; + + if (IS_ERR(priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + offset_con1 = (priv->bus_id == 1) + ? RK3568_GRF_GMAC1_CON1 : RK3568_GRF_GMAC0_CON1; + + regmap_write(priv->grf, offset_con1, + RK3568_GMAC_PHY_INTF_SEL_RMII); +} + +static void rk3568_set_gmac_speed(struct eqos *eqos, int speed) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + struct device_d *dev = priv->dev; + unsigned long rate; + int ret; + + switch (speed) { + case SPEED_10: + rate = 2500000; + break; + case SPEED_100: + rate = 25000000; + break; + case SPEED_1000: + rate = 125000000; + break; + default: + dev_err(dev, "unknown speed value for GMAC speed=%d", speed); + return; + } + + ret = clk_set_rate(priv->clks[CLK_MAC_SPEED].clk, rate); + if (ret) + dev_err(dev, "%s: set clk_mac_speed rate %ld failed %d\n", + __func__, rate, ret); +} + +static const struct rk_gmac_ops rk3568_ops = { + .set_to_rgmii = rk3568_set_to_rgmii, + .set_to_rmii = rk3568_set_to_rmii, + .set_speed = rk3568_set_gmac_speed, +}; + +static int rk_gmac_powerup(struct eqos *eqos) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + struct device_d *dev = priv->dev; + + /*rmii or rgmii*/ + switch (eqos->interface) { + case PHY_INTERFACE_MODE_RGMII: + dev_dbg(dev, "init for RGMII\n"); + priv->ops->set_to_rgmii(eqos, priv->tx_delay, + priv->rx_delay); + break; + case PHY_INTERFACE_MODE_RGMII_ID: + dev_dbg(dev, "init for RGMII_ID\n"); + priv->ops->set_to_rgmii(eqos, 0, 0); + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + dev_dbg(dev, "init for RGMII_RXID\n"); + priv->ops->set_to_rgmii(eqos, priv->tx_delay, 0); + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + dev_dbg(dev, "init for RGMII_TXID\n"); + priv->ops->set_to_rgmii(eqos, 0, priv->rx_delay); + break; + case PHY_INTERFACE_MODE_RMII: + dev_dbg(dev, "init for RMII\n"); + priv->ops->set_to_rmii(eqos); + break; + default: + dev_err(dev, "NO interface defined!\n"); + } + + return 0; +} + +static void eqos_rk_adjust_link(struct eth_device *edev) +{ + struct eqos *eqos = edev->priv; + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + + priv->ops->set_speed(eqos, edev->phydev->speed); + + eqos_adjust_link(edev); +} + +static int eqos_init_rk_gmac(struct device_d *dev, struct eqos *eqos) +{ + struct device_node *np = dev->device_node; + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + int ret; + const char *strings; + + priv->dev = dev; + + ret = of_property_read_string(np, "clock_in_out", &strings); + if (ret) { + dev_err(dev, "Can not read property: clock_in_out.\n"); + priv->clock_input = true; + } else { + dev_dbg(dev, "clock is %s\n", strings); + if (!strcmp(strings, "input")) + priv->clock_input = true; + else + priv->clock_input = false; + } + + priv->ops = device_get_match_data(dev); + + priv->bus_id = of_alias_get_id(np, "ethernet"); + + priv->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(priv->grf)) { + dev_err(dev, "unable to get grf"); + return PTR_ERR(priv->grf); + } + + priv->tx_delay = 0x30; + of_property_read_u32(np, "tx_delay", &priv->tx_delay); + priv->rx_delay = 0x10; + of_property_read_u32(np, "rx_delay", &priv->rx_delay); + + priv->num_clks = ARRAY_SIZE(rk_gmac_clks); + priv->clks = xmalloc(priv->num_clks * sizeof(*priv->clks)); + memcpy(priv->clks, rk_gmac_clks, sizeof rk_gmac_clks); + + ret = clk_bulk_get(dev, priv->num_clks, priv->clks); + if (ret) { + dev_err(dev, "Failed to get clks: %s\n", strerror(-ret)); + return ret; + } + + ret = clk_bulk_enable(priv->num_clks, priv->clks); + if (ret) { + dev_err(dev, "Failed to enable clks: %s\n", strerror(-ret)); + return ret; + } + + rk_gmac_powerup(eqos); + + return 0; +} + +static struct eqos_ops rk_gmac_ops = { + .init = eqos_init_rk_gmac, + .get_ethaddr = eqos_get_ethaddr, + .set_ethaddr = eqos_set_ethaddr, + .adjust_link = eqos_rk_adjust_link, + .get_csr_clk_rate = eqos_get_csr_clk_rate_rk_gmac, + + .clk_csr = EQOS_MDIO_ADDR_CR_250_300, + .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV, +}; + +static int dwc_ether_probe(struct device_d *dev) +{ + return eqos_probe(dev, &rk_gmac_ops, xzalloc(sizeof(struct eqos_rk_gmac))); +} + +static __maybe_unused struct of_device_id dwc_ether_compatible[] = { + { + .compatible = "rockchip,rk3568-gmac", + .data = &rk3568_ops, + }, { + /* sentinel */ + } +}; + +static struct driver_d dwc_ether_driver = { + .name = "designware_eqos", + .probe = dwc_ether_probe, + .of_compatible = DRV_OF_COMPAT(dwc_ether_compatible), +}; +device_platform_driver(dwc_ether_driver); -- 2.29.2 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox