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 <jo...@kwiboo.se>
>> ---
>> Cc: David Wu <david...@rock-chips.com>
>> Cc: Ezequiel Garcia <ezequ...@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,
>> +};

Reply via email to