Merge branch 'net-stmmac-multi-interface-stmmac'

Russell King says:

====================
net: stmmac: multi-interface stmmac

This series adds a callback for platform glue to configure the stmmac
core interface mode depending on the PHY interface mode that is being
used. This is currently only called just before the dwmac core is reset
since these signals are latched on reset.

Included in this series are changes to s32 to move its PHY_INTF_SEL_x
definitions out of the way of the dwmac core's signals which has more
entitlement to use this name. We convert dwmac-imx as an example.

Including other platform glue would make this series excessively large,
but once this core code is merged, the individual platform glue updates
can be posted one after another as they will be independent of each
other.

It is hoped that this callback can be used in future to reconfigure the
dwmac core when the interface mode changes to support PHYs that change
their interface mode, but we're nowhere near being able to do that yet.
====================

Link: https://patch.msgid.link/aQiWzyrXU_2hGJ4j@shell.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-11-04 16:15:56 -08:00
commit 31113a452a
6 changed files with 113 additions and 93 deletions

View File

@ -313,6 +313,16 @@ struct stmmac_safety_stats {
#define DMA_HW_FEAT_ACTPHYIF 0x70000000 /* Active/selected PHY iface */
#define DEFAULT_DMA_PBL 8
/* phy_intf_sel_i and ACTPHYIF encodings */
#define PHY_INTF_SEL_GMII_MII 0
#define PHY_INTF_SEL_RGMII 1
#define PHY_INTF_SEL_SGMII 2
#define PHY_INTF_SEL_TBI 3
#define PHY_INTF_SEL_RMII 4
#define PHY_INTF_SEL_RTBI 5
#define PHY_INTF_SEL_SMII 6
#define PHY_INTF_SEL_REVMII 7
/* MSI defines */
#define STMMAC_MSI_VEC_MAX 32

View File

@ -23,18 +23,13 @@
#include "stmmac_platform.h"
#define GPR_ENET_QOS_INTF_MODE_MASK GENMASK(21, 16)
#define GPR_ENET_QOS_INTF_SEL_MII (0x0 << 16)
#define GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 16)
#define GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 16)
#define GPR_ENET_QOS_INTF_SEL_MASK GENMASK(20, 16)
#define GPR_ENET_QOS_CLK_GEN_EN (0x1 << 19)
#define GPR_ENET_QOS_CLK_TX_CLK_SEL (0x1 << 20)
#define GPR_ENET_QOS_RGMII_EN (0x1 << 21)
#define MX93_GPR_ENET_QOS_INTF_MODE_MASK GENMASK(3, 0)
#define MX93_GPR_ENET_QOS_INTF_MASK GENMASK(3, 1)
#define MX93_GPR_ENET_QOS_INTF_SEL_MII (0x0 << 1)
#define MX93_GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 1)
#define MX93_GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 1)
#define MX93_GPR_ENET_QOS_INTF_SEL_MASK GENMASK(3, 1)
#define MX93_GPR_ENET_QOS_CLK_GEN_EN (0x1 << 0)
#define MX93_GPR_ENET_QOS_CLK_SEL_MASK BIT_MASK(0)
#define MX93_GPR_CLK_SEL_OFFSET (4)
@ -44,13 +39,15 @@
#define RMII_RESET_SPEED (0x3 << 14)
#define CTRL_SPEED_MASK GENMASK(15, 14)
struct imx_priv_data;
struct imx_dwmac_ops {
u32 addr_width;
u32 flags;
bool mac_rgmii_txclk_auto_adj;
int (*fix_soc_reset)(struct stmmac_priv *priv, void __iomem *ioaddr);
int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat);
int (*set_intf_mode)(struct imx_priv_data *dwmac, u8 phy_intf_sel);
void (*fix_mac_speed)(void *priv, int speed, unsigned int mode);
};
@ -67,79 +64,46 @@ struct imx_priv_data {
struct plat_stmmacenet_data *plat_dat;
};
static int imx8mp_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
static int imx8mp_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel)
{
struct imx_priv_data *dwmac = plat_dat->bsp_priv;
int val;
unsigned int val;
switch (plat_dat->phy_interface) {
case PHY_INTERFACE_MODE_MII:
val = GPR_ENET_QOS_INTF_SEL_MII;
break;
case PHY_INTERFACE_MODE_RMII:
val = GPR_ENET_QOS_INTF_SEL_RMII;
val |= (dwmac->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL);
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
val = GPR_ENET_QOS_INTF_SEL_RGMII |
GPR_ENET_QOS_RGMII_EN;
break;
default:
pr_debug("imx dwmac doesn't support %s interface\n",
phy_modes(plat_dat->phy_interface));
return -EINVAL;
}
val = FIELD_PREP(GPR_ENET_QOS_INTF_SEL_MASK, phy_intf_sel) |
GPR_ENET_QOS_CLK_GEN_EN;
if (phy_intf_sel == PHY_INTF_SEL_RMII && !dwmac->rmii_refclk_ext)
val |= GPR_ENET_QOS_CLK_TX_CLK_SEL;
else if (phy_intf_sel == PHY_INTF_SEL_RGMII)
val |= GPR_ENET_QOS_RGMII_EN;
val |= GPR_ENET_QOS_CLK_GEN_EN;
return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
GPR_ENET_QOS_INTF_MODE_MASK, val);
};
static int
imx8dxl_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
imx8dxl_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel)
{
int ret = 0;
/* TBD: depends on imx8dxl scu interfaces to be upstreamed */
return ret;
return 0;
}
static int imx93_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
static int imx93_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel)
{
struct imx_priv_data *dwmac = plat_dat->bsp_priv;
int val, ret;
unsigned int val;
int ret;
switch (plat_dat->phy_interface) {
case PHY_INTERFACE_MODE_MII:
val = MX93_GPR_ENET_QOS_INTF_SEL_MII;
break;
case PHY_INTERFACE_MODE_RMII:
if (dwmac->rmii_refclk_ext) {
ret = regmap_clear_bits(dwmac->intf_regmap,
dwmac->intf_reg_off +
MX93_GPR_CLK_SEL_OFFSET,
MX93_GPR_ENET_QOS_CLK_SEL_MASK);
if (ret)
return ret;
}
val = MX93_GPR_ENET_QOS_INTF_SEL_RMII;
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
val = MX93_GPR_ENET_QOS_INTF_SEL_RGMII;
break;
default:
dev_dbg(dwmac->dev, "imx dwmac doesn't support %s interface\n",
phy_modes(plat_dat->phy_interface));
return -EINVAL;
if (phy_intf_sel == PHY_INTF_SEL_RMII && dwmac->rmii_refclk_ext) {
ret = regmap_clear_bits(dwmac->intf_regmap,
dwmac->intf_reg_off +
MX93_GPR_CLK_SEL_OFFSET,
MX93_GPR_ENET_QOS_CLK_SEL_MASK);
if (ret)
return ret;
}
val |= MX93_GPR_ENET_QOS_CLK_GEN_EN;
val = FIELD_PREP(MX93_GPR_ENET_QOS_INTF_SEL_MASK, phy_intf_sel) |
MX93_GPR_ENET_QOS_CLK_GEN_EN;
return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
MX93_GPR_ENET_QOS_INTF_MODE_MASK, val);
};
@ -170,34 +134,24 @@ static int imx_dwmac_clks_config(void *priv, bool enabled)
return ret;
}
static int imx_dwmac_init(struct platform_device *pdev, void *priv)
static int imx_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel)
{
struct plat_stmmacenet_data *plat_dat;
struct imx_priv_data *dwmac = priv;
int ret;
struct imx_priv_data *dwmac = bsp_priv;
plat_dat = dwmac->plat_dat;
if (!dwmac->ops->set_intf_mode)
return 0;
if (dwmac->ops->set_intf_mode) {
ret = dwmac->ops->set_intf_mode(plat_dat);
if (ret)
return ret;
}
if (phy_intf_sel != PHY_INTF_SEL_GMII_MII &&
phy_intf_sel != PHY_INTF_SEL_RGMII &&
phy_intf_sel != PHY_INTF_SEL_RMII)
return -EINVAL;
return 0;
}
static void imx_dwmac_exit(struct platform_device *pdev, void *priv)
{
/* nothing to do now */
return dwmac->ops->set_intf_mode(dwmac, phy_intf_sel);
}
static int imx_dwmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
phy_interface_t interface, int speed)
{
struct imx_priv_data *dwmac = bsp_priv;
interface = dwmac->plat_dat->phy_interface;
if (interface == PHY_INTERFACE_MODE_RMII ||
interface == PHY_INTERFACE_MODE_MII)
return 0;
@ -244,8 +198,8 @@ static void imx93_dwmac_fix_speed(void *priv, int speed, unsigned int mode)
if (regmap_read(dwmac->intf_regmap, dwmac->intf_reg_off, &iface))
return;
iface &= MX93_GPR_ENET_QOS_INTF_MASK;
if (iface != MX93_GPR_ENET_QOS_INTF_SEL_RGMII)
if (FIELD_GET(MX93_GPR_ENET_QOS_INTF_SEL_MASK, iface) !=
PHY_INTF_SEL_RGMII)
return;
old_ctrl = readl(dwmac->base_addr + MAC_CTRL_REG);
@ -258,6 +212,7 @@ static void imx93_dwmac_fix_speed(void *priv, int speed, unsigned int mode)
readl(dwmac->base_addr + MAC_CTRL_REG);
usleep_range(10, 20);
iface &= MX93_GPR_ENET_QOS_INTF_SEL_MASK;
iface |= MX93_GPR_ENET_QOS_CLK_GEN_EN;
regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
MX93_GPR_ENET_QOS_INTF_MODE_MASK, iface);
@ -370,8 +325,7 @@ static int imx_dwmac_probe(struct platform_device *pdev)
plat_dat->tx_queues_cfg[i].tbs_en = 1;
plat_dat->host_dma_width = dwmac->ops->addr_width;
plat_dat->init = imx_dwmac_init;
plat_dat->exit = imx_dwmac_exit;
plat_dat->set_phy_intf_sel = imx_set_phy_intf_sel;
plat_dat->clks_config = imx_dwmac_clks_config;
plat_dat->bsp_priv = dwmac;
dwmac->plat_dat = plat_dat;

View File

@ -24,10 +24,10 @@
#define GMAC_INTF_RATE_125M 125000000 /* 125MHz */
/* SoC PHY interface control register */
#define PHY_INTF_SEL_MII 0x00
#define PHY_INTF_SEL_SGMII 0x01
#define PHY_INTF_SEL_RGMII 0x02
#define PHY_INTF_SEL_RMII 0x08
#define S32_PHY_INTF_SEL_MII 0x00
#define S32_PHY_INTF_SEL_SGMII 0x01
#define S32_PHY_INTF_SEL_RGMII 0x02
#define S32_PHY_INTF_SEL_RMII 0x08
struct s32_priv_data {
void __iomem *ioaddr;
@ -40,7 +40,7 @@ struct s32_priv_data {
static int s32_gmac_write_phy_intf_select(struct s32_priv_data *gmac)
{
writel(PHY_INTF_SEL_RGMII, gmac->ctrl_sts);
writel(S32_PHY_INTF_SEL_RGMII, gmac->ctrl_sts);
dev_dbg(gmac->dev, "PHY mode set to %s\n", phy_modes(*gmac->intf_mode));

View File

@ -396,6 +396,7 @@ void stmmac_ptp_register(struct stmmac_priv *priv);
void stmmac_ptp_unregister(struct stmmac_priv *priv);
int stmmac_xdp_open(struct net_device *dev);
void stmmac_xdp_release(struct net_device *dev);
int stmmac_get_phy_intf_sel(phy_interface_t interface);
int stmmac_resume(struct device *dev);
int stmmac_suspend(struct device *dev);
void stmmac_dvr_remove(struct device *dev);

View File

@ -3082,6 +3082,56 @@ static void stmmac_check_ether_addr(struct stmmac_priv *priv)
}
}
int stmmac_get_phy_intf_sel(phy_interface_t interface)
{
int phy_intf_sel = -EINVAL;
if (interface == PHY_INTERFACE_MODE_MII ||
interface == PHY_INTERFACE_MODE_GMII)
phy_intf_sel = PHY_INTF_SEL_GMII_MII;
else if (phy_interface_mode_is_rgmii(interface))
phy_intf_sel = PHY_INTF_SEL_RGMII;
else if (interface == PHY_INTERFACE_MODE_SGMII)
phy_intf_sel = PHY_INTF_SEL_SGMII;
else if (interface == PHY_INTERFACE_MODE_RMII)
phy_intf_sel = PHY_INTF_SEL_RMII;
else if (interface == PHY_INTERFACE_MODE_REVMII)
phy_intf_sel = PHY_INTF_SEL_REVMII;
return phy_intf_sel;
}
EXPORT_SYMBOL_GPL(stmmac_get_phy_intf_sel);
static int stmmac_prereset_configure(struct stmmac_priv *priv)
{
struct plat_stmmacenet_data *plat_dat = priv->plat;
phy_interface_t interface;
int phy_intf_sel, ret;
if (!plat_dat->set_phy_intf_sel)
return 0;
interface = plat_dat->phy_interface;
phy_intf_sel = stmmac_get_phy_intf_sel(interface);
if (phy_intf_sel < 0) {
netdev_err(priv->dev,
"failed to get phy_intf_sel for %s: %pe\n",
phy_modes(interface), ERR_PTR(phy_intf_sel));
return phy_intf_sel;
}
ret = plat_dat->set_phy_intf_sel(plat_dat->bsp_priv, phy_intf_sel);
if (ret == -EINVAL)
netdev_err(priv->dev, "platform does not support %s\n",
phy_modes(interface));
else if (ret < 0)
netdev_err(priv->dev,
"platform failed to set interface %s: %pe\n",
phy_modes(interface), ERR_PTR(ret));
return ret;
}
/**
* stmmac_init_dma_engine - DMA init.
* @priv: driver private structure
@ -3108,6 +3158,10 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
priv->plat->dma_cfg->atds = 1;
ret = stmmac_prereset_configure(priv);
if (ret)
return ret;
ret = stmmac_reset(priv, priv->ioaddr);
if (ret) {
netdev_err(priv->dev, "Failed to reset the dma\n");

View File

@ -250,6 +250,7 @@ struct plat_stmmacenet_data {
struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES];
void (*get_interfaces)(struct stmmac_priv *priv, void *bsp_priv,
unsigned long *interfaces);
int (*set_phy_intf_sel)(void *priv, u8 phy_intf_sel);
int (*set_clk_tx_rate)(void *priv, struct clk *clk_tx_i,
phy_interface_t interface, int speed);
void (*fix_mac_speed)(void *priv, int speed, unsigned int mode);