[PATCH 4/7] net: dwc_eth_qos: Add glue driver for GMAC on Rockchip RK3568

Jonas Karlman jonas at kwiboo.se
Sun Oct 1 21:04:30 CEST 2023


Hi David,

On 2023-08-15 15:12, David Wu wrote:
> Hi Jonas,
> 
> Thank you for your help.
> 
> 在 2023/8/7 08:08, Jonas Karlman 写道:
>> Add a new glue driver for Rockchip SoCs, i.e RK3568, with a GMAC based
>> on Synopsys DWC Ethernet QoS IP.
>>
>> rk_gmac_ops was ported from linux commit:
>> 3bb3d6b1c195 ("net: stmmac: Add RK3566/RK3568 SoC support")
>>
>> Signed-off-by: Jonas Karlman <jonas at kwiboo.se>
>> ---
>> Cc: David Wu <david.wu at rock-chips.com>
>> Cc: Ezequiel Garcia <ezequiel at collabora.com>
>> ---
>>   drivers/net/Kconfig                |   8 +
>>   drivers/net/Makefile               |   1 +
>>   drivers/net/dwc_eth_qos.c          |   8 +-
>>   drivers/net/dwc_eth_qos.h          |   2 +
>>   drivers/net/dwc_eth_qos_rockchip.c | 348 +++++++++++++++++++++++++++++
>>   5 files changed, 365 insertions(+), 2 deletions(-)
>>   create mode 100644 drivers/net/dwc_eth_qos_rockchip.c
>>
>> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
>> index 0ed39a61e4de..29304fd77759 100644
>> --- a/drivers/net/Kconfig
>> +++ b/drivers/net/Kconfig
>> @@ -225,6 +225,14 @@ config DWC_ETH_QOS_IMX
>>   	  The Synopsys Designware Ethernet QOS IP block with the specific
>>   	  configuration used in IMX soc.
>>   
>> +config DWC_ETH_QOS_ROCKCHIP
>> +	bool "Synopsys DWC Ethernet QOS device support for Rockchip SoCs"
>> +	depends on DWC_ETH_QOS
>> +	select DM_ETH_PHY
>> +	help
>> +	  The Synopsys Designware Ethernet QOS IP block with specific
>> +	  configuration used in Rockchip SoCs.
>> +
>>   config DWC_ETH_QOS_STM32
>>   	bool "Synopsys DWC Ethernet QOS device support for STM32"
>>   	depends on DWC_ETH_QOS
>> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
>> index d4af253b6f28..1d444f5b4a69 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_ROCKCHIP) += dwc_eth_qos_rockchip.o
>>   obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o
>>   obj-$(CONFIG_DWC_ETH_QOS_STARFIVE) += dwc_eth_qos_starfive.o
>>   obj-$(CONFIG_E1000) += e1000.o
>> diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
>> index 24fb3fac1f12..9fb98a2c3c74 100644
>> --- a/drivers/net/dwc_eth_qos.c
>> +++ b/drivers/net/dwc_eth_qos.c
>> @@ -1707,7 +1707,12 @@ static const struct udevice_id eqos_ids[] = {
>>   		.data = (ulong)&eqos_imx_config
>>   	},
>>   #endif
>> -
>> +#if IS_ENABLED(CONFIG_DWC_ETH_QOS_ROCKCHIP)
>> +	{
>> +		.compatible = "rockchip,rk3568-gmac",
>> +		.data = (ulong)&eqos_rockchip_config
>> +	},
>> +#endif
> 
> If this compatible could move to dwc_eth_qos_rockchip.c, it is better,
> we have other SoCs  that also use this driver in the feature,  and it 
> must increase
> this array all the time for new SoCs later, it will be better only change
> dwc_eth_qos_rockchip.c, we don't need to change the current file.

Agree that this could be improved, not sure how we should/could do it,
and is something that can be done in a follow-up series.

Will leave as-is for v2.

Regards,
Jonas

> 
>>   #if IS_ENABLED(CONFIG_DWC_ETH_QOS_QCOM)
>>   	{
>>   		.compatible = "qcom,qcs404-ethqos",
>> @@ -1720,7 +1725,6 @@ static const struct udevice_id eqos_ids[] = {
>>   		.data = (ulong)&eqos_jh7110_config
>>   	},
>>   #endif
>> -
>>   	{ }
>>   };
>>   
>> diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
>> index 06a082da72ef..e3222e1e17e5 100644
>> --- a/drivers/net/dwc_eth_qos.h
>> +++ b/drivers/net/dwc_eth_qos.h
>> @@ -82,6 +82,7 @@ struct eqos_mac_regs {
>>   #define EQOS_MAC_MDIO_ADDRESS_PA_SHIFT			21
>>   #define EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT			16
>>   #define EQOS_MAC_MDIO_ADDRESS_CR_SHIFT			8
>> +#define EQOS_MAC_MDIO_ADDRESS_CR_100_150		1
>>   #define EQOS_MAC_MDIO_ADDRESS_CR_20_35			2
>>   #define EQOS_MAC_MDIO_ADDRESS_CR_250_300		5
>>   #define EQOS_MAC_MDIO_ADDRESS_SKAP			BIT(4)
>> @@ -287,5 +288,6 @@ 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_rockchip_config;
>>   extern struct eqos_config eqos_qcom_config;
>>   extern struct eqos_config eqos_jh7110_config;
>> diff --git a/drivers/net/dwc_eth_qos_rockchip.c b/drivers/net/dwc_eth_qos_rockchip.c
>> new file mode 100644
>> index 000000000000..c8abe351fc3e
>> --- /dev/null
>> +++ b/drivers/net/dwc_eth_qos_rockchip.c
>> @@ -0,0 +1,348 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +
>> +#include <common.h>
>> +#include <clk.h>
>> +#include <dm.h>
>> +#include <dm/device_compat.h>
>> +#include <net.h>
>> +#include <phy.h>
>> +#include <regmap.h>
>> +#include <reset.h>
>> +#include <syscon.h>
>> +#include <asm/gpio.h>
>> +#include <linux/delay.h>
>> +
>> +#include "dwc_eth_qos.h"
>> +
>> +struct rk_gmac_ops {
>> +	const char *compatible;
>> +	int (*set_to_rgmii)(struct udevice *dev,
>> +			    int tx_delay, int rx_delay);
>> +	int (*set_to_rmii)(struct udevice *dev);
>> +	int (*set_gmac_speed)(struct udevice *dev);
>> +	u32 regs[3];
>> +};
>> +
>> +struct rockchip_platform_data {
>> +	struct reset_ctl_bulk resets;
>> +	const struct rk_gmac_ops *ops;
>> +	int id;
>> +	struct regmap *grf;
>> +};
>> +
>> +#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 int rk3568_set_to_rgmii(struct udevice *dev,
>> +			       int tx_delay, int rx_delay)
>> +{
>> +	struct eth_pdata *pdata = dev_get_plat(dev);
>> +	struct rockchip_platform_data *data = pdata->priv_pdata;
>> +	u32 con0, con1;
>> +
>> +	con0 = (data->id == 1) ? RK3568_GRF_GMAC1_CON0 :
>> +				 RK3568_GRF_GMAC0_CON0;
>> +	con1 = (data->id == 1) ? RK3568_GRF_GMAC1_CON1 :
>> +				 RK3568_GRF_GMAC0_CON1;
>> +
>> +	regmap_write(data->grf, con0,
>> +		     RK3568_GMAC_CLK_RX_DL_CFG(rx_delay) |
>> +		     RK3568_GMAC_CLK_TX_DL_CFG(tx_delay));
>> +
>> +	regmap_write(data->grf, con1,
>> +		     RK3568_GMAC_PHY_INTF_SEL_RGMII |
>> +		     RK3568_GMAC_RXCLK_DLY_ENABLE |
>> +		     RK3568_GMAC_TXCLK_DLY_ENABLE);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rk3568_set_to_rmii(struct udevice *dev)
>> +{
>> +	struct eth_pdata *pdata = dev_get_plat(dev);
>> +	struct rockchip_platform_data *data = pdata->priv_pdata;
>> +	u32 con1;
>> +
>> +	con1 = (data->id == 1) ? RK3568_GRF_GMAC1_CON1 :
>> +				 RK3568_GRF_GMAC0_CON1;
>> +	regmap_write(data->grf, con1, RK3568_GMAC_PHY_INTF_SEL_RMII);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rk3568_set_gmac_speed(struct udevice *dev)
>> +{
>> +	struct eqos_priv *eqos = dev_get_priv(dev);
>> +	ulong rate;
>> +	int ret;
>> +
>> +	switch (eqos->phy->speed) {
>> +	case SPEED_10:
>> +		rate = 2500000;
>> +		break;
>> +	case SPEED_100:
>> +		rate = 25000000;
>> +		break;
>> +	case SPEED_1000:
>> +		rate = 125000000;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = clk_set_rate(&eqos->clk_tx, rate);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct rk_gmac_ops rk_gmac_ops[] = {
>> +	{
>> +		.compatible = "rockchip,rk3568-gmac",
>> +		.set_to_rgmii = rk3568_set_to_rgmii,
>> +		.set_to_rmii = rk3568_set_to_rmii,
>> +		.set_gmac_speed = rk3568_set_gmac_speed,
>> +		.regs = {
>> +			0xfe2a0000, /* gmac0 */
>> +			0xfe010000, /* gmac1 */
>> +			0x0, /* sentinel */
>> +		},
>> +	},
>> +	{ }
>> +};
>> +
>> +static const struct rk_gmac_ops *get_rk_gmac_ops(struct udevice *dev)
>> +{
>> +	const struct rk_gmac_ops *ops = rk_gmac_ops;
>> +
>> +	while (ops->compatible) {
>> +		if (device_is_compatible(dev, ops->compatible))
>> +			return ops;
>> +		ops++;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +static int eqos_probe_resources_rk(struct udevice *dev)
>> +{
>> +	struct eqos_priv *eqos = dev_get_priv(dev);
>> +	struct eth_pdata *pdata = dev_get_plat(dev);
>> +	struct rockchip_platform_data *data;
>> +	int reset_flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE;
>> +	int ret;
>> +
>> +	data = calloc(1, sizeof(struct rockchip_platform_data));
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	data->ops = get_rk_gmac_ops(dev);
>> +	if (!data->ops) {
>> +		ret = -EINVAL;
>> +		goto err_free;
>> +	}
>> +
>> +	for (int i = 0; data->ops->regs[i]; i++) {
>> +		if (data->ops->regs[i] == (u32)eqos->regs) {
>> +			data->id = i;
>> +			break;
>> +		}
>> +	}
>> +
>> +	pdata->priv_pdata = data;
>> +	pdata->phy_interface = eqos->config->interface(dev);
>> +	pdata->max_speed = eqos->max_speed;
>> +
>> +	if (pdata->phy_interface == PHY_INTERFACE_MODE_NA) {
>> +		pr_err("Invalid PHY interface\n");
>> +		ret = -EINVAL;
>> +		goto err_free;
>> +	}
>> +
>> +	data->grf = syscon_regmap_lookup_by_phandle(dev, "rockchip,grf");
>> +	if (IS_ERR(data->grf)) {
>> +		dev_err(dev, "Missing rockchip,grf property\n");
>> +		ret = -EINVAL;
>> +		goto err_free;
>> +	}
>> +
>> +	ret = reset_get_bulk(dev, &data->resets);
>> +	if (ret < 0)
>> +		goto err_free;
>> +
>> +	reset_assert_bulk(&data->resets);
>> +
>> +	ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus);
>> +	if (ret) {
>> +		dev_dbg(dev, "clk_get_by_name(stmmaceth) failed: %d", ret);
>> +		goto err_release_resets;
>> +	}
>> +
>> +	ret = clk_get_by_name(dev, "clk_mac_speed", &eqos->clk_tx);
>> +	if (ret) {
>> +		dev_dbg(dev, "clk_get_by_name(clk_mac_speed) failed: %d", ret);
>> +		goto err_free_clk_master_bus;
>> +	}
>> +
>> +	if (dev_read_bool(dev, "snps,reset-active-low"))
>> +		reset_flags |= GPIOD_ACTIVE_LOW;
>> +
>> +	dev_read_u32_array(dev, "snps,reset-delays-us", eqos->reset_delays, 3);
>> +
>> +	gpio_request_by_name(dev, "snps,reset-gpio", 0,
>> +			     &eqos->phy_reset_gpio, reset_flags);
>> +
>> +	return 0;
>> +
>> +err_free_clk_master_bus:
>> +	clk_free(&eqos->clk_master_bus);
>> +err_release_resets:
>> +	reset_release_bulk(&data->resets);
>> +err_free:
>> +	free(data);
>> +
>> +	return ret;
>> +}
>> +
>> +static int eqos_remove_resources_rk(struct udevice *dev)
>> +{
>> +	struct eqos_priv *eqos = dev_get_priv(dev);
>> +	struct eth_pdata *pdata = dev_get_plat(dev);
>> +	struct rockchip_platform_data *data = pdata->priv_pdata;
>> +
>> +	if (dm_gpio_is_valid(&eqos->phy_reset_gpio))
>> +		dm_gpio_free(dev, &eqos->phy_reset_gpio);
>> +
>> +	clk_free(&eqos->clk_tx);
>> +	clk_free(&eqos->clk_master_bus);
>> +	reset_release_bulk(&data->resets);
>> +	free(data);
>> +
>> +	return 0;
>> +}
>> +
>> +static int eqos_stop_resets_rk(struct udevice *dev)
>> +{
>> +	struct eth_pdata *pdata = dev_get_plat(dev);
>> +	struct rockchip_platform_data *data = pdata->priv_pdata;
>> +
>> +	return reset_assert_bulk(&data->resets);
>> +}
>> +
>> +static int eqos_start_resets_rk(struct udevice *dev)
>> +{
>> +	struct eth_pdata *pdata = dev_get_plat(dev);
>> +	struct rockchip_platform_data *data = pdata->priv_pdata;
>> +
>> +	return reset_deassert_bulk(&data->resets);
>> +}
>> +
>> +static int eqos_stop_clks_rk(struct udevice *dev)
>> +{
>> +	return 0;
>> +}
>> +
>> +static int eqos_start_clks_rk(struct udevice *dev)
>> +{
>> +	struct eqos_priv *eqos = dev_get_priv(dev);
>> +	struct eth_pdata *pdata = dev_get_plat(dev);
>> +	struct rockchip_platform_data *data = pdata->priv_pdata;
>> +	int tx_delay, rx_delay, ret;
>> +
>> +	if (dm_gpio_is_valid(&eqos->phy_reset_gpio)) {
>> +		udelay(eqos->reset_delays[1]);
>> +
>> +		ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		udelay(eqos->reset_delays[2]);
>> +	}
>> +
>> +	tx_delay = dev_read_u32_default(dev, "tx_delay", 0x30);
>> +	rx_delay = dev_read_u32_default(dev, "rx_delay", 0x10);
>> +
>> +	switch (pdata->phy_interface) {
>> +	case PHY_INTERFACE_MODE_RGMII:
>> +		return data->ops->set_to_rgmii(dev, tx_delay, rx_delay);
>> +	case PHY_INTERFACE_MODE_RGMII_ID:
>> +		return data->ops->set_to_rgmii(dev, 0, 0);
>> +	case PHY_INTERFACE_MODE_RGMII_RXID:
>> +		return data->ops->set_to_rgmii(dev, tx_delay, 0);
>> +	case PHY_INTERFACE_MODE_RGMII_TXID:
>> +		return data->ops->set_to_rgmii(dev, 0, rx_delay);
>> +	case PHY_INTERFACE_MODE_RMII:
>> +		return data->ops->set_to_rmii(dev);
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int eqos_set_tx_clk_speed_rk(struct udevice *dev)
>> +{
>> +	struct eth_pdata *pdata = dev_get_plat(dev);
>> +	struct rockchip_platform_data *data = pdata->priv_pdata;
>> +
>> +	return data->ops->set_gmac_speed(dev);
>> +}
>> +
>> +static ulong eqos_get_tick_clk_rate_rk(struct udevice *dev)
>> +{
>> +	struct eqos_priv *eqos = dev_get_priv(dev);
>> +
>> +	return clk_get_rate(&eqos->clk_master_bus);
>> +}
>> +
>> +static struct eqos_ops eqos_rockchip_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_rk,
>> +	.eqos_remove_resources = eqos_remove_resources_rk,
>> +	.eqos_stop_resets = eqos_stop_resets_rk,
>> +	.eqos_start_resets = eqos_start_resets_rk,
>> +	.eqos_stop_clks = eqos_stop_clks_rk,
>> +	.eqos_start_clks = eqos_start_clks_rk,
>> +	.eqos_calibrate_pads = eqos_null_ops,
>> +	.eqos_disable_calibration = eqos_null_ops,
>> +	.eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_rk,
>> +	.eqos_get_enetaddr = eqos_null_ops,
>> +	.eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_rk,
>> +};
>> +
>> +struct eqos_config eqos_rockchip_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_100_150,
>> +	.axi_bus_width = EQOS_AXI_WIDTH_64,
>> +	.interface = dev_read_phy_mode,
>> +	.ops = &eqos_rockchip_ops,
>> +};



More information about the U-Boot mailing list