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
F: include/linux/pcs-rzn1-miic.h
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
M: Miquel Raynal <miquel.raynal@bootlin.com>
L: linux-rtc@vger.kernel.org
......
......@@ -142,6 +142,18 @@ config DWMAC_ROCKCHIP
This selects the Rockchip RK3288 SoC glue layer support for
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
tristate "SOCFPGA dwmac support"
default ARCH_INTEL_SOCFPGA
......
......@@ -21,6 +21,7 @@ obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o
obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o
obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.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_STARFIVE) += dwmac-starfive.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)
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)
{
struct plat_stmmacenet_data *plat_dat;
......@@ -426,6 +476,8 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
dwmac->ops = ops;
plat_dat->bsp_priv = dwmac;
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);
if (ret)
......@@ -444,48 +496,6 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
if (ret)
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;
err_dvr_remove:
......@@ -494,17 +504,6 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
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
static int socfpga_dwmac_resume(struct device *dev)
{
......@@ -576,7 +575,7 @@ MODULE_DEVICE_TABLE(of, socfpga_dwmac_match);
static struct platform_driver socfpga_dwmac_driver = {
.probe = socfpga_dwmac_probe,
.remove_new = socfpga_dwmac_remove,
.remove_new = stmmac_pltfr_remove,
.driver = {
.name = "socfpga-dwmac",
.pm = &socfpga_dwmac_pm_ops,
......
......@@ -360,7 +360,8 @@ enum stmmac_state {
int stmmac_mdio_unregister(struct net_device *ndev);
int stmmac_mdio_register(struct net_device *ndev);
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);
int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags);
......
......@@ -7754,11 +7754,9 @@ int stmmac_dvr_probe(struct device *device,
if (priv->plat->speed_mode_2500)
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_xpcs_setup(priv->mii);
if (ret)
goto error_xpcs_setup;
}
ret = stmmac_pcs_setup(ndev);
if (ret)
goto error_pcs_setup;
ret = stmmac_phy_setup(priv);
if (ret) {
......@@ -7789,8 +7787,9 @@ int stmmac_dvr_probe(struct device *device,
error_netdev_register:
phylink_destroy(priv->phylink);
error_xpcs_setup:
error_phy_setup:
stmmac_pcs_clean(ndev);
error_pcs_setup:
if (priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev);
......@@ -7832,6 +7831,9 @@ void stmmac_dvr_remove(struct device *dev)
if (priv->plat->stmmac_rst)
reset_control_assert(priv->plat->stmmac_rst);
reset_control_assert(priv->plat->stmmac_ahb_rst);
stmmac_pcs_clean(ndev);
if (priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev);
......
......@@ -495,34 +495,57 @@ int stmmac_mdio_reset(struct mii_bus *bus)
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 dw_xpcs *xpcs;
int ret = -ENODEV;
int mode, addr;
priv = netdev_priv(ndev);
mode = priv->plat->phy_interface;
/* Try to probe the XPCS by scanning all addresses. */
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
xpcs = xpcs_create_mdiodev(bus, addr, mode);
if (IS_ERR(xpcs))
continue;
priv->hw->xpcs = xpcs;
break;
if (priv->plat->pcs_init) {
ret = priv->plat->pcs_init(priv);
} else if (priv->plat->mdio_bus_data &&
priv->plat->mdio_bus_data->has_xpcs) {
/* Try to probe the XPCS by scanning all addresses */
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
xpcs = xpcs_create_mdiodev(priv->mii, addr, mode);
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");
return -ENODEV;
return ret;
}
priv->hw->xpcs = xpcs;
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
* @ndev: net device structure
......@@ -679,9 +702,6 @@ int stmmac_mdio_unregister(struct net_device *ndev)
if (!priv->mii)
return 0;
if (priv->hw->xpcs)
xpcs_destroy(priv->hw->xpcs);
mdiobus_unregister(priv->mii);
priv->mii->priv = NULL;
mdiobus_free(priv->mii);
......
......@@ -285,6 +285,8 @@ struct plat_stmmacenet_data {
int (*crosststamp)(ktime_t *device, struct system_counterval_t *system,
void *ctx);
void (*dump_debug_regs)(void *priv);
int (*pcs_init)(struct stmmac_priv *priv);
void (*pcs_exit)(struct stmmac_priv *priv);
void *bsp_priv;
struct clk *stmmac_clk;
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