Commit 0621be48 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-stmmac-add-support-for-rzn1-gmac-devices'

Romain Gantois says:

====================
net: stmmac: Add support for RZN1 GMAC devices

This is version seven of my series that adds support for a Gigabit Ethernet
controller featured in the Renesas r9a06g032 SoC, of the RZ/N1 family. This
GMAC device is based on a Synopsys IP and is compatible with the stmmac driver.

My former colleague Clément Léger originally sent a series for this driver,
but an issue in bringing up the PCS clock had blocked the upstreaming
process. This issue has since been resolved by the following series:

https://lore.kernel.org/all/20240326-rxc_bugfix-v6-0-24a74e5c761f@bootlin.com/

This series consists of a devicetree binding describing the RZN1 GMAC
controller IP, a node for the GMAC1 device in the r9a06g032 SoC device
tree, and the GMAC driver itself which is a glue layer in stmmac.

There are also two patches by Russell that improve pcs initialization handling
in stmmac.
====================

Link: https://lore.kernel.org/r/20240513-rzn1-gmac1-v7-0-6acf58b5440d@bootlin.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 486ffc33 f360446e
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/renesas,rzn1-gmac.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas GMAC
maintainers:
- Romain Gantois <romain.gantois@bootlin.com>
select:
properties:
compatible:
contains:
enum:
- renesas,r9a06g032-gmac
- renesas,rzn1-gmac
required:
- compatible
allOf:
- $ref: snps,dwmac.yaml#
properties:
compatible:
items:
- enum:
- renesas,r9a06g032-gmac
- const: renesas,rzn1-gmac
- const: snps,dwmac
pcs-handle:
description:
phandle pointing to a PCS sub-node compatible with
renesas,rzn1-miic.yaml#
required:
- compatible
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/r9a06g032-sysctrl.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
ethernet@44000000 {
compatible = "renesas,r9a06g032-gmac", "renesas,rzn1-gmac", "snps,dwmac";
reg = <0x44000000 0x2000>;
interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
clock-names = "stmmaceth";
clocks = <&sysctrl R9A06G032_HCLK_GMAC0>;
power-domains = <&sysctrl>;
snps,multicast-filter-bins = <256>;
snps,perfect-filter-entries = <128>;
tx-fifo-depth = <2048>;
rx-fifo-depth = <4096>;
pcs-handle = <&mii_conv1>;
phy-mode = "mii";
};
...
...@@ -18874,6 +18874,12 @@ F: include/dt-bindings/net/pcs-rzn1-miic.h ...@@ -18874,6 +18874,12 @@ F: include/dt-bindings/net/pcs-rzn1-miic.h
F: include/linux/pcs-rzn1-miic.h F: include/linux/pcs-rzn1-miic.h
F: net/dsa/tag_rzn1_a5psw.c F: net/dsa/tag_rzn1_a5psw.c
RENESAS RZ/N1 DWMAC GLUE LAYER
M: Romain Gantois <romain.gantois@bootlin.com>
S: Maintained
F: Documentation/devicetree/bindings/net/renesas,rzn1-gmac.yaml
F: drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c
RENESAS RZ/N1 RTC CONTROLLER DRIVER RENESAS RZ/N1 RTC CONTROLLER DRIVER
M: Miquel Raynal <miquel.raynal@bootlin.com> M: Miquel Raynal <miquel.raynal@bootlin.com>
L: linux-rtc@vger.kernel.org L: linux-rtc@vger.kernel.org
......
...@@ -142,6 +142,18 @@ config DWMAC_ROCKCHIP ...@@ -142,6 +142,18 @@ config DWMAC_ROCKCHIP
This selects the Rockchip RK3288 SoC glue layer support for This selects the Rockchip RK3288 SoC glue layer support for
the stmmac device driver. the stmmac device driver.
config DWMAC_RZN1
tristate "Renesas RZ/N1 dwmac support"
default ARCH_RZN1
depends on OF && (ARCH_RZN1 || COMPILE_TEST)
select PCS_RZN1_MIIC
help
Support for Ethernet controller on Renesas RZ/N1 SoC family.
This selects the Renesas RZ/N1 SoC glue layer support for
the stmmac device driver. This support can make use of a custom MII
converter PCS device.
config DWMAC_SOCFPGA config DWMAC_SOCFPGA
tristate "SOCFPGA dwmac support" tristate "SOCFPGA dwmac support"
default ARCH_INTEL_SOCFPGA default ARCH_INTEL_SOCFPGA
......
...@@ -21,6 +21,7 @@ obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o ...@@ -21,6 +21,7 @@ obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o
obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o
obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o
obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o
obj-$(CONFIG_DWMAC_RZN1) += dwmac-rzn1.o
obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o
obj-$(CONFIG_DWMAC_STARFIVE) += dwmac-starfive.o obj-$(CONFIG_DWMAC_STARFIVE) += dwmac-starfive.o
obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2024 Schneider-Electric
*
* Clément Léger <clement.leger@bootlin.com>
*/
#include <linux/of.h>
#include <linux/pcs-rzn1-miic.h>
#include <linux/phylink.h>
#include <linux/platform_device.h>
#include "stmmac_platform.h"
#include "stmmac.h"
static int rzn1_dwmac_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 rzn1_dwmac_pcs_exit(struct stmmac_priv *priv)
{
if (priv->hw->phylink_pcs)
miic_destroy(priv->hw->phylink_pcs);
}
static int rzn1_dwmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
struct stmmac_resources stmmac_res;
struct device *dev = &pdev->dev;
int ret;
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
if (ret)
return ret;
plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
if (IS_ERR(plat_dat))
return PTR_ERR(plat_dat);
plat_dat->bsp_priv = plat_dat;
plat_dat->pcs_init = rzn1_dwmac_pcs_init;
plat_dat->pcs_exit = rzn1_dwmac_pcs_exit;
ret = stmmac_dvr_probe(dev, plat_dat, &stmmac_res);
if (ret)
return ret;
return 0;
}
static const struct of_device_id rzn1_dwmac_match[] = {
{ .compatible = "renesas,rzn1-gmac" },
{ }
};
MODULE_DEVICE_TABLE(of, rzn1_dwmac_match);
static struct platform_driver rzn1_dwmac_driver = {
.probe = rzn1_dwmac_probe,
.remove_new = stmmac_pltfr_remove,
.driver = {
.name = "rzn1-dwmac",
.of_match_table = rzn1_dwmac_match,
},
};
module_platform_driver(rzn1_dwmac_driver);
MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
MODULE_DESCRIPTION("Renesas RZN1 DWMAC specific glue layer");
MODULE_LICENSE("GPL");
...@@ -379,6 +379,56 @@ static int socfpga_gen10_set_phy_mode(struct socfpga_dwmac *dwmac) ...@@ -379,6 +379,56 @@ static int socfpga_gen10_set_phy_mode(struct socfpga_dwmac *dwmac)
return 0; return 0;
} }
static int socfpga_dwmac_pcs_init(struct stmmac_priv *priv)
{
struct socfpga_dwmac *dwmac = priv->plat->bsp_priv;
struct regmap_config pcs_regmap_cfg = {
.reg_bits = 16,
.val_bits = 16,
.reg_shift = REGMAP_UPSHIFT(1),
};
struct mdio_regmap_config mrc;
struct regmap *pcs_regmap;
struct phylink_pcs *pcs;
struct mii_bus *pcs_bus;
if (!dwmac->tse_pcs_base)
return 0;
pcs_regmap = devm_regmap_init_mmio(priv->device, dwmac->tse_pcs_base,
&pcs_regmap_cfg);
if (IS_ERR(pcs_regmap))
return PTR_ERR(pcs_regmap);
memset(&mrc, 0, sizeof(mrc));
mrc.regmap = pcs_regmap;
mrc.parent = priv->device;
mrc.valid_addr = 0x0;
mrc.autoscan = false;
/* Can't use ndev->name here because it will not have been initialised,
* and in any case, the user can rename network interfaces at runtime.
*/
snprintf(mrc.name, MII_BUS_ID_SIZE, "%s-pcs-mii",
dev_name(priv->device));
pcs_bus = devm_mdio_regmap_register(priv->device, &mrc);
if (IS_ERR(pcs_bus))
return PTR_ERR(pcs_bus);
pcs = lynx_pcs_create_mdiodev(pcs_bus, 0);
if (IS_ERR(pcs))
return PTR_ERR(pcs);
priv->hw->phylink_pcs = pcs;
return 0;
}
static void socfpga_dwmac_pcs_exit(struct stmmac_priv *priv)
{
if (priv->hw->phylink_pcs)
lynx_pcs_destroy(priv->hw->phylink_pcs);
}
static int socfpga_dwmac_probe(struct platform_device *pdev) static int socfpga_dwmac_probe(struct platform_device *pdev)
{ {
struct plat_stmmacenet_data *plat_dat; struct plat_stmmacenet_data *plat_dat;
...@@ -426,6 +476,8 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) ...@@ -426,6 +476,8 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
dwmac->ops = ops; dwmac->ops = ops;
plat_dat->bsp_priv = dwmac; plat_dat->bsp_priv = dwmac;
plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed;
plat_dat->pcs_init = socfpga_dwmac_pcs_init;
plat_dat->pcs_exit = socfpga_dwmac_pcs_exit;
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
if (ret) if (ret)
...@@ -444,48 +496,6 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) ...@@ -444,48 +496,6 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
if (ret) if (ret)
goto err_dvr_remove; goto err_dvr_remove;
/* Create a regmap for the PCS so that it can be used by the PCS driver,
* if we have such a PCS
*/
if (dwmac->tse_pcs_base) {
struct regmap_config pcs_regmap_cfg;
struct mdio_regmap_config mrc;
struct regmap *pcs_regmap;
struct mii_bus *pcs_bus;
memset(&pcs_regmap_cfg, 0, sizeof(pcs_regmap_cfg));
memset(&mrc, 0, sizeof(mrc));
pcs_regmap_cfg.reg_bits = 16;
pcs_regmap_cfg.val_bits = 16;
pcs_regmap_cfg.reg_shift = REGMAP_UPSHIFT(1);
pcs_regmap = devm_regmap_init_mmio(&pdev->dev, dwmac->tse_pcs_base,
&pcs_regmap_cfg);
if (IS_ERR(pcs_regmap)) {
ret = PTR_ERR(pcs_regmap);
goto err_dvr_remove;
}
mrc.regmap = pcs_regmap;
mrc.parent = &pdev->dev;
mrc.valid_addr = 0x0;
mrc.autoscan = false;
snprintf(mrc.name, MII_BUS_ID_SIZE, "%s-pcs-mii", ndev->name);
pcs_bus = devm_mdio_regmap_register(&pdev->dev, &mrc);
if (IS_ERR(pcs_bus)) {
ret = PTR_ERR(pcs_bus);
goto err_dvr_remove;
}
stpriv->hw->phylink_pcs = lynx_pcs_create_mdiodev(pcs_bus, 0);
if (IS_ERR(stpriv->hw->phylink_pcs)) {
ret = PTR_ERR(stpriv->hw->phylink_pcs);
goto err_dvr_remove;
}
}
return 0; return 0;
err_dvr_remove: err_dvr_remove:
...@@ -494,17 +504,6 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) ...@@ -494,17 +504,6 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
return ret; return ret;
} }
static void socfpga_dwmac_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct phylink_pcs *pcs = priv->hw->phylink_pcs;
stmmac_pltfr_remove(pdev);
lynx_pcs_destroy(pcs);
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int socfpga_dwmac_resume(struct device *dev) static int socfpga_dwmac_resume(struct device *dev)
{ {
...@@ -576,7 +575,7 @@ MODULE_DEVICE_TABLE(of, socfpga_dwmac_match); ...@@ -576,7 +575,7 @@ MODULE_DEVICE_TABLE(of, socfpga_dwmac_match);
static struct platform_driver socfpga_dwmac_driver = { static struct platform_driver socfpga_dwmac_driver = {
.probe = socfpga_dwmac_probe, .probe = socfpga_dwmac_probe,
.remove_new = socfpga_dwmac_remove, .remove_new = stmmac_pltfr_remove,
.driver = { .driver = {
.name = "socfpga-dwmac", .name = "socfpga-dwmac",
.pm = &socfpga_dwmac_pm_ops, .pm = &socfpga_dwmac_pm_ops,
......
...@@ -360,7 +360,8 @@ enum stmmac_state { ...@@ -360,7 +360,8 @@ enum stmmac_state {
int stmmac_mdio_unregister(struct net_device *ndev); int stmmac_mdio_unregister(struct net_device *ndev);
int stmmac_mdio_register(struct net_device *ndev); int stmmac_mdio_register(struct net_device *ndev);
int stmmac_mdio_reset(struct mii_bus *mii); int stmmac_mdio_reset(struct mii_bus *mii);
int stmmac_xpcs_setup(struct mii_bus *mii); int stmmac_pcs_setup(struct net_device *ndev);
void stmmac_pcs_clean(struct net_device *ndev);
void stmmac_set_ethtool_ops(struct net_device *netdev); void stmmac_set_ethtool_ops(struct net_device *netdev);
int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags); int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags);
......
...@@ -7754,11 +7754,9 @@ int stmmac_dvr_probe(struct device *device, ...@@ -7754,11 +7754,9 @@ int stmmac_dvr_probe(struct device *device,
if (priv->plat->speed_mode_2500) if (priv->plat->speed_mode_2500)
priv->plat->speed_mode_2500(ndev, priv->plat->bsp_priv); priv->plat->speed_mode_2500(ndev, priv->plat->bsp_priv);
if (priv->plat->mdio_bus_data && priv->plat->mdio_bus_data->has_xpcs) { ret = stmmac_pcs_setup(ndev);
ret = stmmac_xpcs_setup(priv->mii); if (ret)
if (ret) goto error_pcs_setup;
goto error_xpcs_setup;
}
ret = stmmac_phy_setup(priv); ret = stmmac_phy_setup(priv);
if (ret) { if (ret) {
...@@ -7789,8 +7787,9 @@ int stmmac_dvr_probe(struct device *device, ...@@ -7789,8 +7787,9 @@ int stmmac_dvr_probe(struct device *device,
error_netdev_register: error_netdev_register:
phylink_destroy(priv->phylink); phylink_destroy(priv->phylink);
error_xpcs_setup:
error_phy_setup: error_phy_setup:
stmmac_pcs_clean(ndev);
error_pcs_setup:
if (priv->hw->pcs != STMMAC_PCS_TBI && if (priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI) priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev); stmmac_mdio_unregister(ndev);
...@@ -7832,6 +7831,9 @@ void stmmac_dvr_remove(struct device *dev) ...@@ -7832,6 +7831,9 @@ void stmmac_dvr_remove(struct device *dev)
if (priv->plat->stmmac_rst) if (priv->plat->stmmac_rst)
reset_control_assert(priv->plat->stmmac_rst); reset_control_assert(priv->plat->stmmac_rst);
reset_control_assert(priv->plat->stmmac_ahb_rst); reset_control_assert(priv->plat->stmmac_ahb_rst);
stmmac_pcs_clean(ndev);
if (priv->hw->pcs != STMMAC_PCS_TBI && if (priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI) priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev); stmmac_mdio_unregister(ndev);
......
...@@ -495,34 +495,57 @@ int stmmac_mdio_reset(struct mii_bus *bus) ...@@ -495,34 +495,57 @@ int stmmac_mdio_reset(struct mii_bus *bus)
return 0; return 0;
} }
int stmmac_xpcs_setup(struct mii_bus *bus) int stmmac_pcs_setup(struct net_device *ndev)
{ {
struct net_device *ndev = bus->priv; struct dw_xpcs *xpcs = NULL;
struct stmmac_priv *priv; struct stmmac_priv *priv;
struct dw_xpcs *xpcs; int ret = -ENODEV;
int mode, addr; int mode, addr;
priv = netdev_priv(ndev); priv = netdev_priv(ndev);
mode = priv->plat->phy_interface; mode = priv->plat->phy_interface;
/* Try to probe the XPCS by scanning all addresses. */ if (priv->plat->pcs_init) {
for (addr = 0; addr < PHY_MAX_ADDR; addr++) { ret = priv->plat->pcs_init(priv);
xpcs = xpcs_create_mdiodev(bus, addr, mode); } else if (priv->plat->mdio_bus_data &&
if (IS_ERR(xpcs)) priv->plat->mdio_bus_data->has_xpcs) {
continue; /* Try to probe the XPCS by scanning all addresses */
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
priv->hw->xpcs = xpcs; xpcs = xpcs_create_mdiodev(priv->mii, addr, mode);
break; if (IS_ERR(xpcs))
continue;
ret = 0;
break;
}
} else {
return 0;
} }
if (!priv->hw->xpcs) { if (ret) {
dev_warn(priv->device, "No xPCS found\n"); dev_warn(priv->device, "No xPCS found\n");
return -ENODEV; return ret;
} }
priv->hw->xpcs = xpcs;
return 0; return 0;
} }
void stmmac_pcs_clean(struct net_device *ndev)
{
struct stmmac_priv *priv = netdev_priv(ndev);
if (priv->plat->pcs_exit)
priv->plat->pcs_exit(priv);
if (!priv->hw->xpcs)
return;
xpcs_destroy(priv->hw->xpcs);
priv->hw->xpcs = NULL;
}
/** /**
* stmmac_mdio_register * stmmac_mdio_register
* @ndev: net device structure * @ndev: net device structure
...@@ -679,9 +702,6 @@ int stmmac_mdio_unregister(struct net_device *ndev) ...@@ -679,9 +702,6 @@ int stmmac_mdio_unregister(struct net_device *ndev)
if (!priv->mii) if (!priv->mii)
return 0; return 0;
if (priv->hw->xpcs)
xpcs_destroy(priv->hw->xpcs);
mdiobus_unregister(priv->mii); mdiobus_unregister(priv->mii);
priv->mii->priv = NULL; priv->mii->priv = NULL;
mdiobus_free(priv->mii); mdiobus_free(priv->mii);
......
...@@ -285,6 +285,8 @@ struct plat_stmmacenet_data { ...@@ -285,6 +285,8 @@ struct plat_stmmacenet_data {
int (*crosststamp)(ktime_t *device, struct system_counterval_t *system, int (*crosststamp)(ktime_t *device, struct system_counterval_t *system,
void *ctx); void *ctx);
void (*dump_debug_regs)(void *priv); void (*dump_debug_regs)(void *priv);
int (*pcs_init)(struct stmmac_priv *priv);
void (*pcs_exit)(struct stmmac_priv *priv);
void *bsp_priv; void *bsp_priv;
struct clk *stmmac_clk; struct clk *stmmac_clk;
struct clk *pclk; struct clk *pclk;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment