[PATCH 2/2] net: eqos: Rockchip support

Ahmad Fatoum a.fatoum at pengutronix.de
Mon Jun 7 09:05:20 PDT 2021



On 07.06.21 16:10, Sascha Hauer wrote:
> A variant of the designware eqos core is used on Rockchip SoCs. Add
> support for it.
> 
> Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
> ---
>  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,

STM32MP1 and Tegra186 both have a .remove calling eqos_remove here.
I think that's required, because net core doesn't ->halt network
drivers otherwise.

> +	.of_compatible = DRV_OF_COMPAT(dwc_ether_compatible),
> +};
> +device_platform_driver(dwc_ether_driver);

With the above addressed:
Reviewed-by: Ahmad Fatoum <a.fatoum at pengutronix.de>

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



More information about the barebox mailing list