Re: [net-next PATCH v4 11/11] net: airoha: add phylink support for GDM2/3/4
flat view
From: Christian Marangi <ansuelsmth@gmail.com>
Date: 2025-05-12 09:27:20
Also in:
linux-arm-kernel, linux-devicetree, linux-mediatek, lkml, llvm
On Mon, May 12, 2025 at 11:21:47AM +0200, Lorenzo Bianconi wrote:
quoted
Add phylink support for GDM2/3/4 port that require configuration of the PCS to make the external PHY or attached SFP cage work. These needs to be defined in the GDM port node using the pcs-handle property. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> --- drivers/net/ethernet/airoha/airoha_eth.c | 155 +++++++++++++++++++++- drivers/net/ethernet/airoha/airoha_eth.h | 3 + drivers/net/ethernet/airoha/airoha_regs.h | 12 ++ 3 files changed, 169 insertions(+), 1 deletion(-)diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 16c7896f931f..3be1ae077a76 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c@@ -7,6 +7,7 @@ #include <linux/of_net.h> #include <linux/platform_device.h> #include <linux/tcp.h> +#include <linux/pcs/pcs.h> #include <linux/u64_stats_sync.h> #include <net/dst_metadata.h> #include <net/page_pool/helpers.h>@@ -79,6 +80,11 @@ static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) return port->id == 1; } +static bool airhoa_is_phy_external(struct airoha_gdm_port *port) +{ + return port->id != 1; +} + static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) { struct airoha_eth *eth = port->qdma->eth;@@ -1613,6 +1619,17 @@ static int airoha_dev_open(struct net_device *dev) struct airoha_gdm_port *port = netdev_priv(dev); struct airoha_qdma *qdma = port->qdma; + if (airhoa_is_phy_external(port)) { + err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);nit: even if it is not strictly required, in order to align with the rest of the codebase, can you please stay below 79 columns?quoted
+ if (err) { + netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, + err);same herequoted
+ return err; + } + + phylink_start(port->phylink); + } + netif_tx_start_all_queues(dev); err = airoha_set_vip_for_gdm_port(port, true); if (err)@@ -1665,6 +1682,11 @@ static int airoha_dev_stop(struct net_device *dev) } } + if (airhoa_is_phy_external(port)) { + phylink_stop(port->phylink); + phylink_disconnect_phy(port->phylink); + } + return 0; }@@ -2795,6 +2817,115 @@ bool airoha_is_valid_gdm_port(struct airoha_eth *eth, return false; } +static void airoha_mac_link_up(struct phylink_config *config, struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, bool tx_pause, bool rx_pause)ditto.quoted
+{ + struct airoha_gdm_port *port = container_of(config, struct airoha_gdm_port, + phylink_config);ditto.quoted
+ struct airoha_qdma *qdma = port->qdma; + struct airoha_eth *eth = qdma->eth; + u32 frag_size_tx, frag_size_rx; + + if (port->id != 4) + return; + + switch (speed) { + case SPEED_10000: + case SPEED_5000: + frag_size_tx = 8; + frag_size_rx = 8; + break; + case SPEED_2500: + frag_size_tx = 2; + frag_size_rx = 1; + break; + default: + frag_size_tx = 1; + frag_size_rx = 0; + } + + /* Configure TX/RX frag based on speed */ + airoha_fe_rmw(eth, REG_GDMA4_TMBI_FRAG, + GDMA4_SGMII0_TX_FRAG_SIZE_MASK, + FIELD_PREP(GDMA4_SGMII0_TX_FRAG_SIZE_MASK, + frag_size_tx)); + + airoha_fe_rmw(eth, REG_GDMA4_RMBI_FRAG, + GDMA4_SGMII0_RX_FRAG_SIZE_MASK, + FIELD_PREP(GDMA4_SGMII0_RX_FRAG_SIZE_MASK, + frag_size_rx)); +} + +static const struct phylink_mac_ops airoha_phylink_ops = { + .mac_link_up = airoha_mac_link_up, +}; + +static int airoha_setup_phylink(struct net_device *dev) +{ + struct airoha_gdm_port *port = netdev_priv(dev); + struct device_node *np = dev->dev.of_node; + struct phylink_pcs **available_pcs; + phy_interface_t phy_mode; + struct phylink *phylink; + unsigned int num_pcs; + int err; + + err = of_get_phy_mode(np, &phy_mode); + if (err) { + dev_err(&dev->dev, "incorrect phy-mode\n"); + return err; + } + + port->phylink_config.dev = &dev->dev; + port->phylink_config.type = PHYLINK_NETDEV; + port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD | + MAC_5000FD | MAC_10000FD;over 79 columnsquoted
+ + err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), NULL, &num_pcs); + if (err) + return err; + + available_pcs = kcalloc(num_pcs, sizeof(*available_pcs), GFP_KERNEL);I guess you can use devm_kcalloc() and get rid of kfree() here.
I forgot to answer to this in the previous revision. No devm can't be used there available_pcs is just an array allocated for phylink_config. Phylink then copy the data in it and doesn't use it anymore hence it just needs to be allocated here and doesn't need to stay till the driver gets removed.
quoted
+ if (!available_pcs) + return -ENOMEM; + + err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), available_pcs, + &num_pcs); + if (err) + goto out; + + port->phylink_config.available_pcs = available_pcs; + port->phylink_config.num_available_pcs = num_pcs; + + __set_bit(PHY_INTERFACE_MODE_SGMII, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, + port->phylink_config.supported_interfaces); + + phy_interface_copy(port->phylink_config.pcs_interfaces, + port->phylink_config.supported_interfaces); + + phylink = phylink_create(&port->phylink_config, + of_fwnode_handle(np), + phy_mode, &airoha_phylink_ops); + if (IS_ERR(phylink)) { + err = PTR_ERR(phylink); + goto out; + } + + port->phylink = phylink; +out: + kfree(available_pcs); + + return err; +} + static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np, int index) {@@ -2873,7 +3004,23 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth, if (err) return err; - return register_netdev(dev); + if (airhoa_is_phy_external(port)) { + err = airoha_setup_phylink(dev);This will re-introduce the issue reported here: https://lore.kernel.org/netdev/5c94b9b3850f7f29ed653e2205325620df28c3ff.1746715755.git.christophe.jaillet@wanadoo.fr/ (local)
I'm confused about this. The suggestion wasn't that register_netdev might fail and I need to destroy phylink? Or the linked patch was merged and I need to rebase on top of net-next? I didn't include that change to not cause conflicts but once it's merged I will rebase and include that fix.
quoted
+ if (err) + return err; + } + + err = register_netdev(dev); + if (err) + goto free_phylink; + + return 0; + +free_phylink: + if (airhoa_is_phy_external(port)) + phylink_destroy(port->phylink); + + return err; } static int airoha_probe(struct platform_device *pdev)@@ -2967,6 +3114,9 @@ static int airoha_probe(struct platform_device *pdev) struct airoha_gdm_port *port = eth->ports[i]; if (port && port->dev->reg_state == NETREG_REGISTERED) { + if (airhoa_is_phy_external(port)) + phylink_destroy(port->phylink); + unregister_netdev(port->dev); airoha_metadata_dst_free(port); }@@ -2994,6 +3144,9 @@ static void airoha_remove(struct platform_device *pdev) continue; airoha_dev_stop(port->dev); + if (airhoa_is_phy_external(port)) + phylink_destroy(port->phylink); + unregister_netdev(port->dev); airoha_metadata_dst_free(port); }diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 53f39083a8b0..73a500474076 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h@@ -498,6 +498,9 @@ struct airoha_gdm_port { struct net_device *dev; int id; + struct phylink *phylink; + struct phylink_config phylink_config; + struct airoha_hw_stats stats; DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h index d931530fc96f..54f7079b28b0 100644 --- a/drivers/net/ethernet/airoha/airoha_regs.h +++ b/drivers/net/ethernet/airoha/airoha_regs.h@@ -357,6 +357,18 @@ #define IP_FRAGMENT_PORT_MASK GENMASK(8, 5) #define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0) +#define REG_GDMA4_TMBI_FRAG 0x2028 +#define GDMA4_SGMII1_TX_WEIGHT_MASK GENMASK(31, 26) +#define GDMA4_SGMII1_TX_FRAG_SIZE_MASK GENMASK(25, 16) +#define GDMA4_SGMII0_TX_WEIGHT_MASK GENMASK(15, 10) +#define GDMA4_SGMII0_TX_FRAG_SIZE_MASK GENMASK(9, 0) + +#define REG_GDMA4_RMBI_FRAG 0x202c +#define GDMA4_SGMII1_RX_WEIGHT_MASK GENMASK(31, 26) +#define GDMA4_SGMII1_RX_FRAG_SIZE_MASK GENMASK(25, 16) +#define GDMA4_SGMII0_RX_WEIGHT_MASK GENMASK(15, 10) +#define GDMA4_SGMII0_RX_FRAG_SIZE_MASK GENMASK(9, 0) + #define REG_MC_VLAN_EN 0x2100 #define MC_VLAN_EN_MASK BIT(0)-- 2.48.1
-- Ansuel