diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c')
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c new file mode 100644 index 000000000000..be7f5eb2cdcf --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * dwmac-renesas-gbeth.c - DWMAC Specific Glue layer for Renesas GBETH + * + * The Rx and Tx clocks are supplied as follows for the GBETH IP. + * + * Rx / Tx + * -------+------------- on / off ------- + * | + * | Rx-180 / Tx-180 + * +---- not ---- on / off ------- + * + * Copyright (C) 2025 Renesas Electronics Corporation + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pcs-rzn1-miic.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/types.h> + +#include "stmmac_platform.h" + +/** + * struct renesas_gbeth_of_data - OF data for Renesas GBETH + * + * @clks: Array of clock names + * @num_clks: Number of clocks + * @stmmac_flags: Flags for the stmmac driver + * @handle_reset: Flag to indicate if reset control is + * handled by the glue driver or core driver. + * @set_clk_tx_rate: Flag to indicate if Tx clock is fixed or + * set_clk_tx_rate is needed. + * @has_pcs: Flag to indicate if the MAC has a PCS + */ +struct renesas_gbeth_of_data { + const char * const *clks; + u8 num_clks; + u32 stmmac_flags; + bool handle_reset; + bool set_clk_tx_rate; + bool has_pcs; +}; + +struct renesas_gbeth { + const struct renesas_gbeth_of_data *of_data; + struct plat_stmmacenet_data *plat_dat; + struct reset_control *rstc; + struct device *dev; +}; + +static const char *const renesas_gbeth_clks[] = { + "tx", "tx-180", "rx", "rx-180", +}; + +static const char *const renesas_gmac_clks[] = { + "tx", +}; + +static int renesas_gmac_pcs_init(struct stmmac_priv *priv) +{ + struct device_node *np = priv->device->of_node; + struct device_node *pcs_node; + struct phylink_pcs *pcs; + + pcs_node = of_parse_phandle(np, "pcs-handle", 0); + if (pcs_node) { + pcs = miic_create(priv->device, pcs_node); + of_node_put(pcs_node); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); + + priv->hw->phylink_pcs = pcs; + } + + return 0; +} + +static void renesas_gmac_pcs_exit(struct stmmac_priv *priv) +{ + if (priv->hw->phylink_pcs) + miic_destroy(priv->hw->phylink_pcs); +} + +static struct phylink_pcs *renesas_gmac_select_pcs(struct stmmac_priv *priv, + phy_interface_t interface) +{ + return priv->hw->phylink_pcs; +} + +static int renesas_gbeth_init(struct device *dev, void *priv) +{ + struct plat_stmmacenet_data *plat_dat; + struct renesas_gbeth *gbeth = priv; + int ret; + + plat_dat = gbeth->plat_dat; + + ret = reset_control_deassert(gbeth->rstc); + if (ret) { + dev_err(gbeth->dev, "Reset deassert failed\n"); + return ret; + } + + ret = clk_bulk_prepare_enable(plat_dat->num_clks, + plat_dat->clks); + if (ret) + reset_control_assert(gbeth->rstc); + + return ret; +} + +static void renesas_gbeth_exit(struct device *dev, void *priv) +{ + struct plat_stmmacenet_data *plat_dat; + struct renesas_gbeth *gbeth = priv; + int ret; + + plat_dat = gbeth->plat_dat; + + clk_bulk_disable_unprepare(plat_dat->num_clks, plat_dat->clks); + + ret = reset_control_assert(gbeth->rstc); + if (ret) + dev_err(gbeth->dev, "Reset assert failed\n"); +} + +static int renesas_gbeth_probe(struct platform_device *pdev) +{ + const struct renesas_gbeth_of_data *of_data; + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct device *dev = &pdev->dev; + struct renesas_gbeth *gbeth; + unsigned int i; + int err; + + err = stmmac_get_platform_resources(pdev, &stmmac_res); + if (err) + return dev_err_probe(dev, err, + "failed to get resources\n"); + + plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); + if (IS_ERR(plat_dat)) + return dev_err_probe(dev, PTR_ERR(plat_dat), + "dt configuration failed\n"); + + gbeth = devm_kzalloc(dev, sizeof(*gbeth), GFP_KERNEL); + if (!gbeth) + return -ENOMEM; + + of_data = of_device_get_match_data(&pdev->dev); + gbeth->of_data = of_data; + + plat_dat->num_clks = of_data->num_clks; + plat_dat->clks = devm_kcalloc(dev, plat_dat->num_clks, + sizeof(*plat_dat->clks), GFP_KERNEL); + if (!plat_dat->clks) + return -ENOMEM; + + for (i = 0; i < plat_dat->num_clks; i++) + plat_dat->clks[i].id = of_data->clks[i]; + + err = devm_clk_bulk_get(dev, plat_dat->num_clks, plat_dat->clks); + if (err < 0) + return err; + + plat_dat->clk_tx_i = stmmac_pltfr_find_clk(plat_dat, "tx"); + if (!plat_dat->clk_tx_i) + return dev_err_probe(dev, -EINVAL, + "error finding tx clock\n"); + + if (of_data->handle_reset) { + gbeth->rstc = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(gbeth->rstc)) + return PTR_ERR(gbeth->rstc); + } + + gbeth->dev = dev; + gbeth->plat_dat = plat_dat; + plat_dat->bsp_priv = gbeth; + if (of_data->set_clk_tx_rate) + plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate; + plat_dat->init = renesas_gbeth_init; + plat_dat->exit = renesas_gbeth_exit; + plat_dat->flags |= STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP | + gbeth->of_data->stmmac_flags; + if (of_data->has_pcs) { + plat_dat->pcs_init = renesas_gmac_pcs_init; + plat_dat->pcs_exit = renesas_gmac_pcs_exit; + plat_dat->select_pcs = renesas_gmac_select_pcs; + } + + return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); +} + +static const struct renesas_gbeth_of_data renesas_gbeth_of_data = { + .clks = renesas_gbeth_clks, + .num_clks = ARRAY_SIZE(renesas_gbeth_clks), + .handle_reset = true, + .set_clk_tx_rate = true, + .stmmac_flags = STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY | + STMMAC_FLAG_SPH_DISABLE, +}; + +static const struct renesas_gbeth_of_data renesas_gmac_of_data = { + .clks = renesas_gmac_clks, + .num_clks = ARRAY_SIZE(renesas_gmac_clks), + .stmmac_flags = STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY, + .has_pcs = true, +}; + +static const struct of_device_id renesas_gbeth_match[] = { + { .compatible = "renesas,r9a09g077-gbeth", .data = &renesas_gmac_of_data }, + { .compatible = "renesas,rzv2h-gbeth", .data = &renesas_gbeth_of_data }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, renesas_gbeth_match); + +static struct platform_driver renesas_gbeth_driver = { + .probe = renesas_gbeth_probe, + .driver = { + .name = "renesas-gbeth", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = renesas_gbeth_match, + }, +}; +module_platform_driver(renesas_gbeth_driver); + +MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>"); +MODULE_DESCRIPTION("Renesas GBETH DWMAC Specific Glue layer"); +MODULE_LICENSE("GPL"); |
