[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