Merge branch 'net-dsa-lantiq-add-support-for-intel-gsw150'

Daniel Golle says:

====================
net: dsa: lantiq: add support for Intel GSW150

The Intel GSW150 Ethernet Switch (aka. Lantiq PEB7084) is the predecessor of
MaxLinear's GSW1xx series of switches. It shares most features, but has a
slightly different port layout and different MII interfaces.
Adding support for this switch to the mxl-gsw1xx driver is quite trivial.
====================

Link: https://patch.msgid.link/cover.1769099517.git.daniel@makrotopia.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-01-27 11:52:46 +01:00
commit 4c17c01c31
7 changed files with 168 additions and 74 deletions

View File

@ -19,6 +19,8 @@ maintainers:
properties:
compatible:
enum:
- intel,gsw150
- lantiq,peb7084
- lantiq,xrx200-gswip
- lantiq,xrx300-gswip
- lantiq,xrx330-gswip
@ -338,7 +340,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
switchphy0: switchphy@0 {
switchphy0: ethernet-phy@0 {
reg = <0>;
leds {
@ -353,7 +355,7 @@ examples:
};
};
switchphy1: switchphy@1 {
switchphy1: ethernet-phy@1 {
reg = <1>;
leds {

View File

@ -16,9 +16,11 @@ config NET_DSA_MXL_GSW1XX
select NET_DSA_TAG_MXL_GSW1XX
select NET_DSA_LANTIQ_COMMON
help
This enables support for the MaxLinear GSW1xx family of 1GE switches
This enables support for the Intel/MaxLinear GSW1xx family of 1GE
switches.
GSW120 4 port, 2 PHYs, RGMII & SGMII/2500Base-X
GSW125 4 port, 2 PHYs, RGMII & SGMII/2500Base-X, industrial temperature
GSW140 6 port, 4 PHYs, RGMII & SGMII/2500Base-X
GSW141 6 port, 4 PHYs, RGMII & SGMII
GSW145 6 port, 4 PHYs, RGMII & SGMII/2500Base-X, industrial temperature
GSW150 7 port, 5 PHYs, 1x GMII/RGMII, 1x RGMII

View File

@ -33,8 +33,7 @@ static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
switch (port) {
case 0:
case 1:
case 0 ... 1:
phy_interface_set_rgmii(config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_MII,
config->supported_interfaces);
@ -44,9 +43,7 @@ static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port,
config->supported_interfaces);
break;
case 2:
case 3:
case 4:
case 2 ... 4:
case 6:
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
@ -75,10 +72,7 @@ static void gswip_xrx300_phylink_get_caps(struct dsa_switch *ds, int port,
config->supported_interfaces);
break;
case 1:
case 2:
case 3:
case 4:
case 1 ... 4:
case 6:
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
@ -463,10 +457,22 @@ static void gswip_shutdown(struct platform_device *pdev)
}
static const struct gswip_hw_info gswip_xrx200 = {
.max_ports = 7,
.max_ports = GSWIP_MAX_PORTS,
.allowed_cpu_ports = BIT(6),
.mii_ports = BIT(0) | BIT(1) | BIT(5),
.mii_port_reg_offset = 0,
.mii_cfg = {
[0] = GSWIP_MII_CFGp(0),
[1] = GSWIP_MII_CFGp(1),
[2 ... 4] = -1,
[5] = GSWIP_MII_CFGp(5),
[6] = -1,
},
.mii_pcdu = {
[0] = GSWIP_MII_PCDU0,
[1] = GSWIP_MII_PCDU1,
[2 ... 4] = -1,
[5] = GSWIP_MII_PCDU5,
[6] = -1,
},
.phylink_get_caps = gswip_xrx200_phylink_get_caps,
.pce_microcode = &gswip_pce_microcode,
.pce_microcode_size = ARRAY_SIZE(gswip_pce_microcode),
@ -474,10 +480,20 @@ static const struct gswip_hw_info gswip_xrx200 = {
};
static const struct gswip_hw_info gswip_xrx300 = {
.max_ports = 7,
.max_ports = GSWIP_MAX_PORTS,
.allowed_cpu_ports = BIT(6),
.mii_ports = BIT(0) | BIT(5),
.mii_port_reg_offset = 0,
.mii_cfg = {
[0] = GSWIP_MII_CFGp(0),
[1 ... 4] = -1,
[5] = GSWIP_MII_CFGp(5),
[6] = -1,
},
.mii_pcdu = {
[0] = GSWIP_MII_PCDU0,
[1 ... 4] = -1,
[5] = GSWIP_MII_PCDU5,
[6] = -1,
},
.phylink_get_caps = gswip_xrx300_phylink_get_caps,
.pce_microcode = &gswip_pce_microcode,
.pce_microcode_size = ARRAY_SIZE(gswip_pce_microcode),

View File

@ -243,6 +243,8 @@
#define GSWIP_VLAN_UNAWARE_PVID 0
#define GSWIP_MAX_PORTS 7
struct gswip_pce_microcode {
u16 val_3;
u16 val_2;
@ -253,8 +255,8 @@ struct gswip_pce_microcode {
struct gswip_hw_info {
int max_ports;
unsigned int allowed_cpu_ports;
unsigned int mii_ports;
int mii_port_reg_offset;
s16 mii_cfg[GSWIP_MAX_PORTS];
s16 mii_pcdu[GSWIP_MAX_PORTS];
bool supports_2500m;
const struct gswip_pce_microcode (*pce_microcode)[];
size_t pce_microcode_size;

View File

@ -118,15 +118,11 @@ static u32 gswip_switch_r_timeout(struct gswip_priv *priv, u32 offset,
static void gswip_mii_mask_cfg(struct gswip_priv *priv, u32 mask, u32 set,
int port)
{
int reg_port;
/* MII_CFG register only exists for MII ports */
if (!(priv->hw_info->mii_ports & BIT(port)))
if (priv->hw_info->mii_cfg[port] == -1)
return;
reg_port = port + priv->hw_info->mii_port_reg_offset;
regmap_write_bits(priv->mii, GSWIP_MII_CFGp(reg_port), mask,
regmap_write_bits(priv->mii, priv->hw_info->mii_cfg[port], mask,
set);
}
@ -610,28 +606,13 @@ static void gswip_mii_delay_setup(struct gswip_priv *priv, struct dsa_port *dp,
u32 tx_delay = GSWIP_MII_PCDU_TXDLY_DEFAULT;
u32 rx_delay = GSWIP_MII_PCDU_RXDLY_DEFAULT;
struct device_node *port_dn = dp->dn;
u16 mii_pcdu_reg;
/* As MII_PCDU registers only exist for MII ports, silently return
* unless the port is an MII port
*/
if (!(priv->hw_info->mii_ports & BIT(dp->index)))
if (priv->hw_info->mii_pcdu[dp->index] == -1)
return;
switch (dp->index + priv->hw_info->mii_port_reg_offset) {
case 0:
mii_pcdu_reg = GSWIP_MII_PCDU0;
break;
case 1:
mii_pcdu_reg = GSWIP_MII_PCDU1;
break;
case 5:
mii_pcdu_reg = GSWIP_MII_PCDU5;
break;
default:
return;
}
/* legacy code to set default delays according to the interface mode */
switch (interface) {
case PHY_INTERFACE_MODE_RGMII_ID:
@ -652,7 +633,7 @@ static void gswip_mii_delay_setup(struct gswip_priv *priv, struct dsa_port *dp,
of_property_read_u32(port_dn, "rx-internal-delay-ps", &rx_delay);
of_property_read_u32(port_dn, "tx-internal-delay-ps", &tx_delay);
regmap_write_bits(priv->mii, mii_pcdu_reg,
regmap_write_bits(priv->mii, priv->hw_info->mii_pcdu[dp->index],
GSWIP_MII_PCDU_TXDLY_MASK |
GSWIP_MII_PCDU_RXDLY_MASK,
GSWIP_MII_PCDU_TXDLY(tx_delay) |

View File

@ -502,6 +502,14 @@ static const struct phylink_pcs_ops gsw1xx_pcs_ops = {
.pcs_link_up = gsw1xx_pcs_link_up,
};
static void gsw1xx_phylink_get_lpi_caps(struct phylink_config *config)
{
config->lpi_capabilities = MAC_100FD | MAC_1000FD;
config->lpi_timer_default = 20;
memcpy(config->lpi_interfaces, config->supported_interfaces,
sizeof(config->lpi_interfaces));
}
static void gsw1xx_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
@ -511,14 +519,12 @@ static void gsw1xx_phylink_get_caps(struct dsa_switch *ds, int port,
MAC_10 | MAC_100 | MAC_1000;
switch (port) {
case 0:
case 1:
case 2:
case 3:
case 0 ... 3: /* built-in PHYs */
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
break;
case 4: /* port 4: SGMII */
case 4: /* SGMII */
__set_bit(PHY_INTERFACE_MODE_SGMII,
config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
@ -529,17 +535,40 @@ static void gsw1xx_phylink_get_caps(struct dsa_switch *ds, int port,
config->mac_capabilities |= MAC_2500FD;
}
return; /* no support for EEE on SGMII port */
case 5: /* port 5: RGMII or RMII */
case 5: /* RGMII or RMII */
__set_bit(PHY_INTERFACE_MODE_RMII,
config->supported_interfaces);
phy_interface_set_rgmii(config->supported_interfaces);
break;
}
config->lpi_capabilities = MAC_100FD | MAC_1000FD;
config->lpi_timer_default = 20;
memcpy(config->lpi_interfaces, config->supported_interfaces,
sizeof(config->lpi_interfaces));
gsw1xx_phylink_get_lpi_caps(config);
}
static void gsw150_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
MAC_10 | MAC_100 | MAC_1000;
switch (port) {
case 0 ... 4: /* built-in PHYs */
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
break;
case 5: /* GMII or RGMII */
__set_bit(PHY_INTERFACE_MODE_GMII,
config->supported_interfaces);
fallthrough;
case 6: /* RGMII */
phy_interface_set_rgmii(config->supported_interfaces);
break;
}
gsw1xx_phylink_get_lpi_caps(config);
}
static struct phylink_pcs *gsw1xx_phylink_mac_select_pcs(struct phylink_config *config,
@ -616,6 +645,28 @@ static struct regmap *gsw1xx_regmap_init(struct gsw1xx_priv *priv,
priv, &config);
}
static int gsw1xx_serdes_pcs_init(struct gsw1xx_priv *priv)
{
/* do nothing if the chip doesn't have a SerDes PCS */
if (!priv->gswip.hw_info->mac_select_pcs)
return 0;
priv->pcs.ops = &gsw1xx_pcs_ops;
priv->pcs.poll = true;
__set_bit(PHY_INTERFACE_MODE_SGMII,
priv->pcs.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
priv->pcs.supported_interfaces);
if (priv->gswip.hw_info->supports_2500m)
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
priv->pcs.supported_interfaces);
priv->tbi_interface = PHY_INTERFACE_MODE_NA;
/* assert SGMII reset to power down SGMII unit */
return regmap_set_bits(priv->shell, GSW1XX_SHELL_RST_REQ,
GSW1XX_RST_REQ_SGMII_SHELL);
}
static int gsw1xx_probe(struct mdio_device *mdiodev)
{
struct device *dev = &mdiodev->dev;
@ -668,20 +719,7 @@ static int gsw1xx_probe(struct mdio_device *mdiodev)
if (IS_ERR(priv->shell))
return PTR_ERR(priv->shell);
priv->pcs.ops = &gsw1xx_pcs_ops;
priv->pcs.poll = true;
__set_bit(PHY_INTERFACE_MODE_SGMII,
priv->pcs.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
priv->pcs.supported_interfaces);
if (priv->gswip.hw_info->supports_2500m)
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
priv->pcs.supported_interfaces);
priv->tbi_interface = PHY_INTERFACE_MODE_NA;
/* assert SGMII reset to power down SGMII unit */
ret = regmap_set_bits(priv->shell, GSW1XX_SHELL_RST_REQ,
GSW1XX_RST_REQ_SGMII_SHELL);
ret = gsw1xx_serdes_pcs_init(priv);
if (ret < 0)
return ret;
@ -739,8 +777,16 @@ static void gsw1xx_shutdown(struct mdio_device *mdiodev)
static const struct gswip_hw_info gsw12x_data = {
.max_ports = GSW1XX_PORTS,
.allowed_cpu_ports = BIT(GSW1XX_MII_PORT) | BIT(GSW1XX_SGMII_PORT),
.mii_ports = BIT(GSW1XX_MII_PORT),
.mii_port_reg_offset = -GSW1XX_MII_PORT,
.mii_cfg = {
[0 ... GSW1XX_MII_PORT - 1] = -1,
[GSW1XX_MII_PORT] = GSWIP_MII_CFGp(0),
[GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1,
},
.mii_pcdu = {
[0 ... GSW1XX_MII_PORT - 1] = -1,
[GSW1XX_MII_PORT] = GSWIP_MII_PCDU0,
[GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1,
},
.mac_select_pcs = gsw1xx_phylink_mac_select_pcs,
.phylink_get_caps = &gsw1xx_phylink_get_caps,
.supports_2500m = true,
@ -753,8 +799,16 @@ static const struct gswip_hw_info gsw12x_data = {
static const struct gswip_hw_info gsw140_data = {
.max_ports = GSW1XX_PORTS,
.allowed_cpu_ports = BIT(GSW1XX_MII_PORT) | BIT(GSW1XX_SGMII_PORT),
.mii_ports = BIT(GSW1XX_MII_PORT),
.mii_port_reg_offset = -GSW1XX_MII_PORT,
.mii_cfg = {
[0 ... GSW1XX_MII_PORT - 1] = -1,
[GSW1XX_MII_PORT] = GSWIP_MII_CFGp(0),
[GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1,
},
.mii_pcdu = {
[0 ... GSW1XX_MII_PORT - 1] = -1,
[GSW1XX_MII_PORT] = GSWIP_MII_PCDU0,
[GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1,
},
.mac_select_pcs = gsw1xx_phylink_mac_select_pcs,
.phylink_get_caps = &gsw1xx_phylink_get_caps,
.supports_2500m = true,
@ -767,8 +821,16 @@ static const struct gswip_hw_info gsw140_data = {
static const struct gswip_hw_info gsw141_data = {
.max_ports = GSW1XX_PORTS,
.allowed_cpu_ports = BIT(GSW1XX_MII_PORT) | BIT(GSW1XX_SGMII_PORT),
.mii_ports = BIT(GSW1XX_MII_PORT),
.mii_port_reg_offset = -GSW1XX_MII_PORT,
.mii_cfg = {
[0 ... GSW1XX_MII_PORT - 1] = -1,
[GSW1XX_MII_PORT] = GSWIP_MII_CFGp(0),
[GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1,
},
.mii_pcdu = {
[0 ... GSW1XX_MII_PORT - 1] = -1,
[GSW1XX_MII_PORT] = GSWIP_MII_PCDU0,
[GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1,
},
.mac_select_pcs = gsw1xx_phylink_mac_select_pcs,
.phylink_get_caps = gsw1xx_phylink_get_caps,
.port_setup = gsw1xx_port_setup,
@ -777,11 +839,38 @@ static const struct gswip_hw_info gsw141_data = {
.tag_protocol = DSA_TAG_PROTO_MXL_GSW1XX,
};
static const struct gswip_hw_info gsw150_data = {
.max_ports = GSW150_PORTS,
.allowed_cpu_ports = BIT(5) | BIT(6),
.mii_cfg = {
[0 ... 4] = -1,
[5] = 0,
[6] = 10,
},
.mii_pcdu = {
[0 ... 4] = -1,
[5] = 1,
[6] = 11,
},
.phylink_get_caps = gsw150_phylink_get_caps,
/* There is only a single RGMII_SLEW_CFG register in GSW150 and it is
* unknown if RGMII slew configuration affects both RGMII ports
* or only port 5. Use .port_setup which assumes it affects port 5
* for now.
*/
.port_setup = gsw1xx_port_setup,
.pce_microcode = &gsw1xx_pce_microcode,
.pce_microcode_size = ARRAY_SIZE(gsw1xx_pce_microcode),
.tag_protocol = DSA_TAG_PROTO_MXL_GSW1XX,
};
/*
* GSW125 is the industrial temperature version of GSW120.
* GSW145 is the industrial temperature version of GSW140.
*/
static const struct of_device_id gsw1xx_of_match[] = {
{ .compatible = "intel,gsw150", .data = &gsw150_data },
{ .compatible = "lantiq,peb7084", .data = &gsw150_data },
{ .compatible = "maxlinear,gsw120", .data = &gsw12x_data },
{ .compatible = "maxlinear,gsw125", .data = &gsw12x_data },
{ .compatible = "maxlinear,gsw140", .data = &gsw140_data },
@ -805,5 +894,5 @@ static struct mdio_driver gsw1xx_driver = {
mdio_module_driver(gsw1xx_driver);
MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
MODULE_DESCRIPTION("Driver for MaxLinear GSW1xx ethernet switch");
MODULE_DESCRIPTION("Driver for Intel/MaxLinear GSW1xx Ethernet switch");
MODULE_LICENSE("GPL");

View File

@ -10,6 +10,8 @@
#include <linux/bitfield.h>
#define GSW1XX_PORTS 6
#define GSW150_PORTS 7
/* Port used for RGMII or optional RMII */
#define GSW1XX_MII_PORT 5
/* Port used for SGMII */