diff mbox series

[net-next,3/3] net: axienet: Support dynamic switching between 1000BaseX and SGMII

Message ID 20210213002356.2557207-4-robert.hancock@calian.com (mailing list archive)
State Accepted
Commit 6c8f06bb2e5147b2c25bdd726365df8416c13987
Delegated to: Netdev Maintainers
Headers show
Series Xilinx axienet updates | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net-next
netdev/subject_prefix success Link
netdev/cc_maintainers warning 2 maintainers not CCed: michal.simek@xilinx.com linux-arm-kernel@lists.infradead.org
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 7 this patch: 7
netdev/kdoc success Errors and warnings before: 9 this patch: 1
netdev/verify_fixes success Link
netdev/checkpatch warning WARNING: line length of 82 exceeds 80 columns
netdev/build_allmodconfig_warn success Errors and warnings before: 7 this patch: 7
netdev/header_inline success Link
netdev/stable success Stable not CCed

Commit Message

Robert Hancock Feb. 13, 2021, 12:23 a.m. UTC
Newer versions of the Xilinx AXI Ethernet core (specifically version 7.2 or
later) allow the core to be configured with a PHY interface mode of "Both",
allowing either 1000BaseX or SGMII modes to be selected at runtime. Add
support for this in the driver to allow better support for applications
which can use both fiber and copper SFP modules.

Signed-off-by: Robert Hancock <robert.hancock@calian.com>
---
 drivers/net/ethernet/xilinx/xilinx_axienet.h  | 29 +++++----
 .../net/ethernet/xilinx/xilinx_axienet_main.c | 60 ++++++++++++++++---
 2 files changed, 71 insertions(+), 18 deletions(-)

Comments

Andrew Lunn Feb. 13, 2021, 4:43 p.m. UTC | #1
On Fri, Feb 12, 2021 at 06:23:56PM -0600, Robert Hancock wrote:
> Newer versions of the Xilinx AXI Ethernet core (specifically version 7.2 or
> later) allow the core to be configured with a PHY interface mode of "Both",

Hi Robert

Is it possible to read the version of the core from a register? Is it
possible to synthesizer a version 7.2 or > without this feature? I'm
just wondering if the DT property is actually needed?

>  /**
>   * struct axidma_bd - Axi Dma buffer descriptor layout
>   * @next:         MM2S/S2MM Next Descriptor Pointer
> @@ -377,22 +381,29 @@ struct axidma_bd {
>   * @ndev:	Pointer for net_device to which it will be attached.
>   * @dev:	Pointer to device structure
>   * @phy_node:	Pointer to device node structure
> + * @phylink:	Pointer to phylink instance
> + * @phylink_config: phylink configuration settings
> + * @pcs_phy:	Reference to PCS/PMA PHY if used
> + * @switch_x_sgmii: Whether switchable 1000BaseX/SGMII mode is enabled in the core
> + * @clk:	Clock for AXI bus
>   * @mii_bus:	Pointer to MII bus structure
>   * @mii_clk_div: MII bus clock divider value
>   * @regs_start: Resource start for axienet device addresses
>   * @regs:	Base address for the axienet_local device address space
>   * @dma_regs:	Base address for the axidma device address space
> - * @dma_err_tasklet: Tasklet structure to process Axi DMA errors
> + * @dma_err_task: Work structure to process Axi DMA errors
>   * @tx_irq:	Axidma TX IRQ number
>   * @rx_irq:	Axidma RX IRQ number
> + * @eth_irq:	Ethernet core IRQ number
>   * @phy_mode:	Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X
>   * @options:	AxiEthernet option word
> - * @last_link:	Phy link state in which the PHY was negotiated earlier
>   * @features:	Stores the extended features supported by the axienet hw
>   * @tx_bd_v:	Virtual address of the TX buffer descriptor ring
>   * @tx_bd_p:	Physical address(start address) of the TX buffer descr. ring
> + * @tx_bd_num:	Size of TX buffer descriptor ring
>   * @rx_bd_v:	Virtual address of the RX buffer descriptor ring
>   * @rx_bd_p:	Physical address(start address) of the RX buffer descr. ring
> + * @rx_bd_num:	Size of RX buffer descriptor ring
>   * @tx_bd_ci:	Stores the index of the Tx buffer descriptor in the ring being
>   *		accessed currently. Used while alloc. BDs before a TX starts
>   * @tx_bd_tail:	Stores the index of the Tx buffer descriptor in the ring being
> @@ -414,23 +425,20 @@ struct axienet_local {
>  	struct net_device *ndev;
>  	struct device *dev;
>  
> -	/* Connection to PHY device */
>  	struct device_node *phy_node;
>  
>  	struct phylink *phylink;
>  	struct phylink_config phylink_config;
>  
> -	/* Reference to PCS/PMA PHY if used */
>  	struct mdio_device *pcs_phy;

This really should of been two patches. One moving the comments
around, and a second one adding the new fields.

> +static int axienet_mac_prepare(struct phylink_config *config, unsigned int mode,
> +			       phy_interface_t iface)
> +{
> +	struct net_device *ndev = to_net_dev(config->dev);
> +	struct axienet_local *lp = netdev_priv(ndev);
> +	int ret;
> +
> +	switch (iface) {
> +	case PHY_INTERFACE_MODE_SGMII:
> +	case PHY_INTERFACE_MODE_1000BASEX:
> +		if (!lp->switch_x_sgmii)
> +			return 0;

Maybe -EOPNOTSUPP would be better?

      Andrew
Robert Hancock Feb. 16, 2021, 4:19 p.m. UTC | #2
On Sat, 2021-02-13 at 17:43 +0100, Andrew Lunn wrote:
> On Fri, Feb 12, 2021 at 06:23:56PM -0600, Robert Hancock wrote:
> > Newer versions of the Xilinx AXI Ethernet core (specifically version 7.2 or
> > later) allow the core to be configured with a PHY interface mode of "Both",
> 
> Hi Robert
> 
> Is it possible to read the version of the core from a register? Is it
> possible to synthesizer a version 7.2 or > without this feature? I'm
> just wondering if the DT property is actually needed?

The core can still be synthesized with a fixed 1000Base-X or SGMII interface
mode in addition to the "Both" option, and I'm not aware of a way to determine
what mode has been used based on registers, so I don't think there's really
another option.

> 
> >  /**
> >   * struct axidma_bd - Axi Dma buffer descriptor layout
> >   * @next:         MM2S/S2MM Next Descriptor Pointer
> > @@ -377,22 +381,29 @@ struct axidma_bd {
> >   * @ndev:	Pointer for net_device to which it will be attached.
> >   * @dev:	Pointer to device structure
> >   * @phy_node:	Pointer to device node structure
> > + * @phylink:	Pointer to phylink instance
> > + * @phylink_config: phylink configuration settings
> > + * @pcs_phy:	Reference to PCS/PMA PHY if used
> > + * @switch_x_sgmii: Whether switchable 1000BaseX/SGMII mode is enabled in
> > the core
> > + * @clk:	Clock for AXI bus
> >   * @mii_bus:	Pointer to MII bus structure
> >   * @mii_clk_div: MII bus clock divider value
> >   * @regs_start: Resource start for axienet device addresses
> >   * @regs:	Base address for the axienet_local device address space
> >   * @dma_regs:	Base address for the axidma device address space
> > - * @dma_err_tasklet: Tasklet structure to process Axi DMA errors
> > + * @dma_err_task: Work structure to process Axi DMA errors
> >   * @tx_irq:	Axidma TX IRQ number
> >   * @rx_irq:	Axidma RX IRQ number
> > + * @eth_irq:	Ethernet core IRQ number
> >   * @phy_mode:	Phy type to identify between MII/GMII/RGMII/SGMII/1000
> > Base-X
> >   * @options:	AxiEthernet option word
> > - * @last_link:	Phy link state in which the PHY was negotiated earlier
> >   * @features:	Stores the extended features supported by the axienet
> > hw
> >   * @tx_bd_v:	Virtual address of the TX buffer descriptor ring
> >   * @tx_bd_p:	Physical address(start address) of the TX buffer descr.
> > ring
> > + * @tx_bd_num:	Size of TX buffer descriptor ring
> >   * @rx_bd_v:	Virtual address of the RX buffer descriptor ring
> >   * @rx_bd_p:	Physical address(start address) of the RX buffer descr.
> > ring
> > + * @rx_bd_num:	Size of RX buffer descriptor ring
> >   * @tx_bd_ci:	Stores the index of the Tx buffer descriptor in the
> > ring being
> >   *		accessed currently. Used while alloc. BDs before a TX starts
> >   * @tx_bd_tail:	Stores the index of the Tx buffer descriptor in the
> > ring being
> > @@ -414,23 +425,20 @@ struct axienet_local {
> >  	struct net_device *ndev;
> >  	struct device *dev;
> >  
> > -	/* Connection to PHY device */
> >  	struct device_node *phy_node;
> >  
> >  	struct phylink *phylink;
> >  	struct phylink_config phylink_config;
> >  
> > -	/* Reference to PCS/PMA PHY if used */
> >  	struct mdio_device *pcs_phy;
> 
> This really should of been two patches. One moving the comments
> around, and a second one adding the new fields.
> 
> > +static int axienet_mac_prepare(struct phylink_config *config, unsigned int
> > mode,
> > +			       phy_interface_t iface)
> > +{
> > +	struct net_device *ndev = to_net_dev(config->dev);
> > +	struct axienet_local *lp = netdev_priv(ndev);
> > +	int ret;
> > +
> > +	switch (iface) {
> > +	case PHY_INTERFACE_MODE_SGMII:
> > +	case PHY_INTERFACE_MODE_1000BASEX:
> > +		if (!lp->switch_x_sgmii)
> > +			return 0;
> 
> Maybe -EOPNOTSUPP would be better?

From my reading of the code it appears that this function is called on startup
initially even if dynamic switching is not supported, so we would need to
return 0 here for that case. The validate callback should trap cases where we
attempt to switch modes and that isn't supported.

> 
>       Andrew
diff mbox series

Patch

diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index a03c3ca1b28d..1e966a39967e 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -339,6 +339,10 @@ 
 
 #define DELAY_OF_ONE_MILLISEC		1000
 
+/* Xilinx PCS/PMA PHY register for switching 1000BaseX or SGMII */
+#define XLNX_MII_STD_SELECT_REG		0x11
+#define XLNX_MII_STD_SELECT_SGMII	BIT(0)
+
 /**
  * struct axidma_bd - Axi Dma buffer descriptor layout
  * @next:         MM2S/S2MM Next Descriptor Pointer
@@ -377,22 +381,29 @@  struct axidma_bd {
  * @ndev:	Pointer for net_device to which it will be attached.
  * @dev:	Pointer to device structure
  * @phy_node:	Pointer to device node structure
+ * @phylink:	Pointer to phylink instance
+ * @phylink_config: phylink configuration settings
+ * @pcs_phy:	Reference to PCS/PMA PHY if used
+ * @switch_x_sgmii: Whether switchable 1000BaseX/SGMII mode is enabled in the core
+ * @clk:	Clock for AXI bus
  * @mii_bus:	Pointer to MII bus structure
  * @mii_clk_div: MII bus clock divider value
  * @regs_start: Resource start for axienet device addresses
  * @regs:	Base address for the axienet_local device address space
  * @dma_regs:	Base address for the axidma device address space
- * @dma_err_tasklet: Tasklet structure to process Axi DMA errors
+ * @dma_err_task: Work structure to process Axi DMA errors
  * @tx_irq:	Axidma TX IRQ number
  * @rx_irq:	Axidma RX IRQ number
+ * @eth_irq:	Ethernet core IRQ number
  * @phy_mode:	Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X
  * @options:	AxiEthernet option word
- * @last_link:	Phy link state in which the PHY was negotiated earlier
  * @features:	Stores the extended features supported by the axienet hw
  * @tx_bd_v:	Virtual address of the TX buffer descriptor ring
  * @tx_bd_p:	Physical address(start address) of the TX buffer descr. ring
+ * @tx_bd_num:	Size of TX buffer descriptor ring
  * @rx_bd_v:	Virtual address of the RX buffer descriptor ring
  * @rx_bd_p:	Physical address(start address) of the RX buffer descr. ring
+ * @rx_bd_num:	Size of RX buffer descriptor ring
  * @tx_bd_ci:	Stores the index of the Tx buffer descriptor in the ring being
  *		accessed currently. Used while alloc. BDs before a TX starts
  * @tx_bd_tail:	Stores the index of the Tx buffer descriptor in the ring being
@@ -414,23 +425,20 @@  struct axienet_local {
 	struct net_device *ndev;
 	struct device *dev;
 
-	/* Connection to PHY device */
 	struct device_node *phy_node;
 
 	struct phylink *phylink;
 	struct phylink_config phylink_config;
 
-	/* Reference to PCS/PMA PHY if used */
 	struct mdio_device *pcs_phy;
 
-	/* Clock for AXI bus */
+	bool switch_x_sgmii;
+
 	struct clk *clk;
 
-	/* MDIO bus data */
-	struct mii_bus *mii_bus;	/* MII bus reference */
-	u8 mii_clk_div; /* MII bus clock divider value */
+	struct mii_bus *mii_bus;
+	u8 mii_clk_div;
 
-	/* IO registers, dma functions and IRQs */
 	resource_size_t regs_start;
 	void __iomem *regs;
 	void __iomem *dma_regs;
@@ -442,10 +450,9 @@  struct axienet_local {
 	int eth_irq;
 	phy_interface_t phy_mode;
 
-	u32 options;			/* Current options word */
+	u32 options;
 	u32 features;
 
-	/* Buffer descriptors */
 	struct axidma_bd *tx_bd_v;
 	dma_addr_t tx_bd_p;
 	u32 tx_bd_num;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 3ef31bae71fb..3a8775e0ca55 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -1502,13 +1502,22 @@  static void axienet_validate(struct phylink_config *config,
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 
 	/* Only support the mode we are configured for */
-	if (state->interface != PHY_INTERFACE_MODE_NA &&
-	    state->interface != lp->phy_mode) {
-		netdev_warn(ndev, "Cannot use PHY mode %s, supported: %s\n",
-			    phy_modes(state->interface),
-			    phy_modes(lp->phy_mode));
-		bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
-		return;
+	switch (state->interface) {
+	case PHY_INTERFACE_MODE_NA:
+		break;
+	case PHY_INTERFACE_MODE_1000BASEX:
+	case PHY_INTERFACE_MODE_SGMII:
+		if (lp->switch_x_sgmii)
+			break;
+		fallthrough;
+	default:
+		if (state->interface != lp->phy_mode) {
+			netdev_warn(ndev, "Cannot use PHY mode %s, supported: %s\n",
+				    phy_modes(state->interface),
+				    phy_modes(lp->phy_mode));
+			bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+			return;
+		}
 	}
 
 	phylink_set(mask, Autoneg);
@@ -1568,6 +1577,33 @@  static void axienet_mac_an_restart(struct phylink_config *config)
 	phylink_mii_c22_pcs_an_restart(lp->pcs_phy);
 }
 
+static int axienet_mac_prepare(struct phylink_config *config, unsigned int mode,
+			       phy_interface_t iface)
+{
+	struct net_device *ndev = to_net_dev(config->dev);
+	struct axienet_local *lp = netdev_priv(ndev);
+	int ret;
+
+	switch (iface) {
+	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_1000BASEX:
+		if (!lp->switch_x_sgmii)
+			return 0;
+
+		ret = mdiobus_write(lp->pcs_phy->bus,
+				    lp->pcs_phy->addr,
+				    XLNX_MII_STD_SELECT_REG,
+				    iface == PHY_INTERFACE_MODE_SGMII ?
+					XLNX_MII_STD_SELECT_SGMII : 0);
+		if (ret < 0)
+			netdev_warn(ndev, "Failed to switch PHY interface: %d\n",
+				    ret);
+		return ret;
+	default:
+		return 0;
+	}
+}
+
 static void axienet_mac_config(struct phylink_config *config, unsigned int mode,
 			       const struct phylink_link_state *state)
 {
@@ -1645,6 +1681,7 @@  static const struct phylink_mac_ops axienet_phylink_ops = {
 	.validate = axienet_validate,
 	.mac_pcs_get_state = axienet_mac_pcs_get_state,
 	.mac_an_restart = axienet_mac_an_restart,
+	.mac_prepare = axienet_mac_prepare,
 	.mac_config = axienet_mac_config,
 	.mac_link_down = axienet_mac_link_down,
 	.mac_link_up = axienet_mac_link_up,
@@ -1896,6 +1933,9 @@  static int axienet_probe(struct platform_device *pdev)
 	 */
 	of_property_read_u32(pdev->dev.of_node, "xlnx,rxmem", &lp->rxmem);
 
+	lp->switch_x_sgmii = of_property_read_bool(pdev->dev.of_node,
+						   "xlnx,switch-x-sgmii");
+
 	/* Start with the proprietary, and broken phy_type */
 	ret = of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", &value);
 	if (!ret) {
@@ -1925,6 +1965,12 @@  static int axienet_probe(struct platform_device *pdev)
 		if (ret)
 			goto free_netdev;
 	}
+	if (lp->switch_x_sgmii && lp->phy_mode != PHY_INTERFACE_MODE_SGMII &&
+	    lp->phy_mode != PHY_INTERFACE_MODE_1000BASEX) {
+		dev_err(&pdev->dev, "xlnx,switch-x-sgmii only supported with SGMII or 1000BaseX\n");
+		ret = -EINVAL;
+		goto free_netdev;
+	}
 
 	/* Find the DMA node, map the DMA registers, and decode the DMA IRQs */
 	np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0);