All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Nuno Sá" <noname.nuno@gmail.com>
To: ciprian.regus@analog.com
Cc: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>,
	 Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	 Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>,
	 Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
	 Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	 Andrew Lunn <andrew@lunn.ch>,
	Heiner Kallweit <hkallweit1@gmail.com>,
	 Russell King <linux@armlinux.org.uk>,
	Rob Herring <robh@kernel.org>,
	 Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	netdev@vger.kernel.org,  linux-kernel@vger.kernel.org,
	linux-doc@vger.kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH net-next v2 10/10] net: ethernet: adi: Add a driver for the ADIN1140 MACPHY
Date: Fri, 29 May 2026 10:21:40 +0100	[thread overview]
Message-ID: <ahlY8WSRO_HPmGtK@nsa> (raw)
In-Reply-To: <20260527-adin1140-driver-v2-10-37e5c8d4e0a0@analog.com>

On Wed, May 27, 2026 at 12:51:55AM +0300, Ciprian Regus via B4 Relay wrote:
> From: Ciprian Regus <ciprian.regus@analog.com>
> 
> Add a driver for ADIN1140. The device is a 10BASE-T1S MAC-PHY
> (integrated in the same package) that connects to a CPU over an SPI bus,
> and implements the Open Alliance TC6 protocol for control and frame
> transfers. As such, this driver relies on oa_tc6 for the communication
> with the device. The device has an alternative name (AD3306), so the
> driver can be probed using one of the two compatible strings.
> 
> For control transactions, ADIN1140 only implements the protected mode.
> The driver has a custom implementation for the mii_bus access methods as a
> workaround for hardware issues:
> 
> 1. The OA TC6 standard defines the direct and indirect access modes for
>    MDIO transactions. The ADIN1140 incorrectly advertises indirect mode
>    only (supported capabilities register - 0x2, bit 9), while actually
>    implementing just the direct mode. We cannot rely on the CAP register
>    to choose an access method (which oa_tc6 does by default, even though
>    it only implements the direct mode), so the driver has to use its
>    own.
> 2. The ADIN1140 cannot access the C22 register space of the internal
>    PHY, while the PHY is busy receiving frames. If that happens, the
>    CONFIG0 and CONFIG2 registers of the MAC will get corrupted and the
>    data transfer will stop. Those two registers configure settings for
>    the transfer protocol between the MAC and host, so the value for some
>    of their subfields shouldn't be changed while the netdev is up.
>    Since we know the PHY is internal, the MAC driver can implement a
>    custom mii_bus, which can intercept C22 accesses. Most of the
>    registers mapped in the 0x0 - 0x3 range (the only ones the PHY offers)
>    are read only, and their value can be read from somewhere else (e.g
>    the PHYID 1 & 2 have the same value as 0x1 in the MAC memory map).
>    For the fields that are R/W (loopback and AN/reset) in the control
>    register, the PHY driver already implements the set_loopback() and
>    config_aneg() functions. The C22 write function of the driver is a
>    no-op and is used to protect against the ioctl MDIO access path.
>    C45 accesses do not cause this issue, so we can properly implement
>    them.
> 
> Update the oa_tc6 header file to include definitions for the standard
> registers used by the adin1140 driver.
> 
> Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
> 
> ---
> v2 changelog:
>  - Exported statistics that match the ethtool_stats entries as such and
>    kept the other ones custom, using ethtool strings.
>  - Used phy_do_ioctl_running() for ndo_eth_ioctl.
>  - Adapted the mii_bus and PHY handling to the newly added
>    OA_TC6_BROKEN_PHY flag for oa_tc6.
>  - Used the oa_tc6_mdiobus_read_c45/oa_tc6_mdiobus_write_c45 functions
>    for the C45 read/write mii_bus operations.
>  - Removed OA TC6 register definitions (e.g CONFIG2) from the adin1140
>    driver and instead used the ones exported from oa_tc6.h
>  - Used OA_TC6_MMS_REG to define MMS registers instead of
>    ADIN1140_MMS_REG.
>  - Returned default values for the MII_PHYSID1/MII_PHYSID2.
>  - Set the mii_bus->phy_mask, since the the same PHY will be registered
>    32 times otherwise.
>  - Updated the MAINTAINERS entry to include the dt-bindings.
> ---
>  MAINTAINERS                         |   8 +
>  drivers/net/ethernet/adi/Kconfig    |  12 +
>  drivers/net/ethernet/adi/Makefile   |   1 +
>  drivers/net/ethernet/adi/adin1140.c | 811 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 832 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index eda74f3154dc..3d6da16c4312 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1857,6 +1857,14 @@ S:	Supported
>  W:	https://ez.analog.com/linux-software-drivers
>  F:	drivers/dma/dma-axi-dmac.c
>  
> +ANALOG DEVICES INC ETHERNET DRIVERS
> +M:	Ciprian Regus <ciprian.regus@analog.com>
> +L:	netdev@vger.kernel.org
> +S:	Maintained
> +W:	https://ez.analog.com/linux-software-drivers
> +F:	Documentation/devicetree/bindings/net/adi,adin1140.yaml
> +F:	drivers/net/ethernet/adi/adin1140.c
> +
>  ANALOG DEVICES INC ETHERNET PHY DRIVERS
>  M:	Ciprian Regus <ciprian.regus@analog.com>
>  L:	netdev@vger.kernel.org
> diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig
> index 760a9a60bc15..bdb8ff7d15da 100644
> --- a/drivers/net/ethernet/adi/Kconfig
> +++ b/drivers/net/ethernet/adi/Kconfig
> @@ -26,4 +26,16 @@ config ADIN1110
>  	  Say yes here to build support for Analog Devices ADIN1110
>  	  Low Power 10BASE-T1L Ethernet MAC-PHY.
>  
> +config ADIN1140
> +	tristate "Analog Devices ADIN1140 MAC-PHY"
> +	depends on SPI
> +	select ADIN1140_PHY
> +	select OA_TC6
> +	help
> +	  Say yes here to build support for Analog Devices, Inc. ADIN1140
> +	  10BASE-T1S Ethernet MAC-PHY.
> +
> +	  To compile this driver as a module, choose M here. The module will be
> +	  called adin1140.
> +
>  endif # NET_VENDOR_ADI
> diff --git a/drivers/net/ethernet/adi/Makefile b/drivers/net/ethernet/adi/Makefile
> index d0383d94303c..0390ca8ccc49 100644
> --- a/drivers/net/ethernet/adi/Makefile
> +++ b/drivers/net/ethernet/adi/Makefile
> @@ -4,3 +4,4 @@
>  #
>  
>  obj-$(CONFIG_ADIN1110) += adin1110.o
> +obj-$(CONFIG_ADIN1140) += adin1140.o
> diff --git a/drivers/net/ethernet/adi/adin1140.c b/drivers/net/ethernet/adi/adin1140.c
> new file mode 100644
> index 000000000000..671bfc08ce38
> --- /dev/null
> +++ b/drivers/net/ethernet/adi/adin1140.c
> @@ -0,0 +1,811 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Analog Devices, Inc. ADIN1140 10BASE-T1S MAC-PHY
> + *
> + * Copyright 2026 Analog Devices Inc.
> + */
> +
> +#include <linux/etherdevice.h>
> +#include <linux/kernel.h>
> +#include <linux/mdio.h>
> +#include <linux/module.h>
> +#include <linux/oa_tc6.h>
> +#include <linux/phy.h>

...

> +}
> +
> +static void adin1140_rx_mode(struct net_device *netdev)
> +{
> +	struct adin1140_priv *priv = netdev_priv(netdev);
> +
> +	schedule_work(&priv->rx_mode_work);
> +}
> +
> +static void adin1140_stats_work(struct work_struct *work)
> +{
> +	struct delayed_work *dwork = to_delayed_work(work);
> +	u64 stat_buff[ADIN1140_STATS_CNT] = {};
> +	struct adin1140_priv *priv;
> +	u32 reg_val;
> +	int ret;
> +	u32 i;
> +
> +	priv = container_of(dwork, struct adin1140_priv, stats_work);
> +
> +	for (i = 0; i < ARRAY_SIZE(adin1140_stat_regs); i++) {
> +		ret = oa_tc6_read_register(priv->tc6, adin1140_stat_regs[i],
> +					   &reg_val);
> +		if (ret)
> +			break;
> +
> +		stat_buff[i] = reg_val;
> +	}
> +
> +	spin_lock(&priv->stat_lock);

Maybe consider using scoped_guard() and similar for other places?
Marginal win though so up to you.

> +	memcpy(&priv->stats, stat_buff, sizeof(priv->stats));
> +	spin_unlock(&priv->stat_lock);
> +
> +	schedule_delayed_work(dwork, ADIN1140_STATS_CHECK_DELAY);
> +}
> +
> +static int adin1140_configure(struct adin1140_priv *priv)
> +{
> +	int ret;
> +
> +	ret = oa_tc6_zero_align_receive_frame_enable(priv->tc6);
> +	if (ret)
> +		return ret;
> +
> +	/* Disable MAC loopback */
> +	ret = oa_tc6_write_register(priv->tc6, ADIN1140_MAC_P1_LOOP_ADDR_REG,
> +				    0x0);
> +	if (ret)
> +		return ret;
> +
> +	return adin1140_default_filter_config(priv);
> +}
> +
> +static int adin1140_open(struct net_device *netdev)
> +{
> +	struct adin1140_priv *priv = netdev_priv(netdev);
> +
> +	schedule_delayed_work(&priv->stats_work, ADIN1140_STATS_CHECK_DELAY);
> +
> +	phy_start(netdev->phydev);
> +	netif_start_queue(netdev);
> +
> +	return 0;
> +}
> +
> +static int adin1140_close(struct net_device *netdev)
> +{
> +	struct adin1140_priv *priv = netdev_priv(netdev);
> +
> +	cancel_delayed_work_sync(&priv->stats_work);
> +
> +	netif_stop_queue(netdev);
> +	phy_stop(netdev->phydev);
> +
> +	return 0;
> +}
> +
> +static netdev_tx_t adin1140_start_xmit(struct sk_buff *skb,
> +				       struct net_device *netdev)
> +{
> +	struct adin1140_priv *priv = netdev_priv(netdev);
> +
> +	/* Pad frames to minimum Ethernet frame size (60 bytes without FCS).
> +	 * The MAC will append the FCS, but we need to ensure the frame is
> +	 * at least ETH_ZLEN bytes.
> +	 */
> +	if (skb_put_padto(skb, ETH_ZLEN))
> +		return NETDEV_TX_OK;
> +
> +	return oa_tc6_start_xmit(priv->tc6, skb);
> +}
> +
> +static int adin1140_set_mac_address(struct net_device *netdev, void *addr)
> +{
> +	struct adin1140_priv *priv = netdev_priv(netdev);
> +	struct sockaddr *address = addr;
> +	u8 mask[ETH_ALEN];
> +	int ret;
> +
> +	ret = eth_prepare_mac_addr_change(netdev, addr);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (ether_addr_equal(address->sa_data, netdev->dev_addr))
> +		return 0;
> +
> +	memset(mask, 0xFF, ETH_ALEN);
> +	ret = adin1140_mac_filter_set(priv, address->sa_data, mask,
> +				      ADIN1140_MAC_FILT_UC_SLOT);
> +	if (ret)
> +		return ret;
> +
> +	eth_commit_mac_addr_change(netdev, addr);
> +
> +	return 0;
> +}
> +
> +static void adin1140_ndo_get_stats64(struct net_device *dev,
> +				     struct rtnl_link_stats64 *storage)
> +{
> +	struct adin1140_priv *priv = netdev_priv(dev);
> +
> +	storage->rx_packets = priv->netdev->stats.rx_packets;
> +	storage->tx_packets = priv->netdev->stats.tx_packets;
> +
> +	storage->rx_bytes = priv->netdev->stats.rx_bytes;
> +	storage->tx_bytes = priv->netdev->stats.tx_bytes;
> +
> +	spin_lock(&priv->stat_lock);
> +
> +	storage->rx_errors = priv->stats[rx_crc_errors] +
> +			     priv->stats[rx_align_errors] +
> +			     priv->stats[rx_preamble_errors] +
> +			     priv->stats[rx_short_frame_errors] +
> +			     priv->stats[rx_long_frame_errors] +
> +			     priv->stats[rx_phy_errors] +
> +			     priv->stats[rx_ifg_errors];
> +
> +	storage->tx_errors = priv->stats[tx_excess_collision] +
> +			     priv->stats[tx_underrun];
> +
> +	storage->rx_dropped = priv->stats[rx_fifo_full_dropped] +
> +			      priv->stats[rx_addr_filter_dropped];
> +
> +	storage->multicast = priv->stats[rx_mc_frames];
> +
> +	storage->collisions = priv->stats[tx_single_collision] +
> +			      priv->stats[tx_multi_collision];
> +
> +	storage->rx_length_errors = priv->stats[rx_short_frame_errors] +
> +				    priv->stats[rx_long_frame_errors];
> +	storage->rx_over_errors = priv->stats[rx_fifo_full_dropped];
> +	storage->rx_crc_errors = priv->stats[rx_crc_errors];
> +	storage->rx_frame_errors = priv->stats[rx_align_errors];
> +	storage->rx_missed_errors = priv->stats[rx_fifo_full_dropped];
> +
> +	storage->tx_aborted_errors = priv->stats[tx_excess_collision];
> +	storage->tx_fifo_errors = priv->stats[tx_underrun];
> +	storage->tx_window_errors = priv->stats[tx_late_collision];
> +
> +	spin_unlock(&priv->stat_lock);
> +}
> +
> +static void adin1140_get_drvinfo(struct net_device *netdev,
> +				 struct ethtool_drvinfo *info)
> +{
> +	strscpy(info->driver, "ADIN1140", sizeof(info->driver));
> +	strscpy(info->bus_info, dev_name(netdev->dev.parent),
> +		sizeof(info->bus_info));
> +}
> +
> +static void adin1140_get_ethtool_stats(struct net_device *netdev,
> +				       struct ethtool_stats *stats, u64 *data)
> +{
> +	struct adin1140_priv *priv = netdev_priv(netdev);
> +	u32 i;
> +
> +	spin_lock(&priv->stat_lock);
> +	for (i = 0; i < ARRAY_SIZE(adin1140_stats); i++)
> +		data[i] = priv->stats[adin1140_stats[i].idx];
> +	spin_unlock(&priv->stat_lock);
> +}
> +
> +static void adin1140_get_ethtool_strings(struct net_device *netdev, u32 sset,
> +					 u8 *p)
> +{
> +	u32 i;
> +
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		for (i = 0; i < ARRAY_SIZE(adin1140_stats); i++)
> +			ethtool_puts(&p, adin1140_stats[i].name);
> +
> +		break;
> +	}
> +}
> +
> +static int adin1140_get_sset_count(struct net_device *netdev, int sset)
> +{
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		return ARRAY_SIZE(adin1140_stats);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +static void adin1140_get_eth_mac_stats(struct net_device *netdev,
> +				       struct ethtool_eth_mac_stats *mac_stats)
> +{
> +	struct adin1140_priv *priv = netdev_priv(netdev);
> +
> +	spin_lock(&priv->stat_lock);
> +
> +	mac_stats->FramesReceivedOK = priv->stats[rx_frames];
> +	mac_stats->BroadcastFramesReceivedOK = priv->stats[rx_bc_frames];
> +	mac_stats->MulticastFramesReceivedOK = priv->stats[rx_mc_frames];
> +	mac_stats->FrameCheckSequenceErrors = priv->stats[rx_crc_errors];
> +	mac_stats->AlignmentErrors = priv->stats[rx_align_errors];
> +	mac_stats->FrameTooLongErrors = priv->stats[rx_long_frame_errors];
> +	mac_stats->FramesLostDueToIntMACRcvError =
> +					priv->stats[rx_fifo_full_dropped];
> +	mac_stats->FramesTransmittedOK = priv->stats[tx_frames];
> +	mac_stats->BroadcastFramesXmittedOK = priv->stats[tx_bc_frames];
> +	mac_stats->MulticastFramesXmittedOK = priv->stats[tx_mc_frames];
> +	mac_stats->SingleCollisionFrames = priv->stats[tx_single_collision];
> +	mac_stats->MultipleCollisionFrames = priv->stats[tx_multi_collision];
> +	mac_stats->FramesWithDeferredXmissions = priv->stats[tx_deferred];
> +	mac_stats->LateCollisions = priv->stats[tx_late_collision];
> +	mac_stats->FramesAbortedDueToXSColls = priv->stats[tx_excess_collision];
> +	mac_stats->FramesLostDueToIntMACXmitError = priv->stats[tx_underrun];
> +
> +	spin_unlock(&priv->stat_lock);
> +}
> +
> +static int adin1140_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
> +{
> +	/* The ADIN1140's standard PHY C22 register map (OA TC6 0xFF00 -
> +	 * 0xFF1F), of which only 0xFF00 - 0xFF03 are implemented) cannot be
> +	 * accessed while frames are being received by the PHY. In case this
> +	 * happens the CONFIG0 and CONFIG2 register values will get corrupted,
> +	 * getting a random value. Both reads and writes cause the same
> +	 * behavior. This is a workaround that avoids MDIO accesses all
> +	 * together. Since this is a 10BASE-T1S PHY, only the loopback and
> +	 * reset (AN) bits in the control register (0x0) can be written.
> +	 * These functionalities have custom implementations in the PHY
> +	 * driver. C45 accesses do not cause this issue.
> +	 */
> +
> +	switch (regnum) {
> +	case MII_BMCR:
> +		return ADIN1140_PHY_CTRL_DEFAULT;
> +	case MII_BMSR:
> +		return ADIN1140_PHY_STATUS_DEFAULT;
> +	case MII_PHYSID1:
> +		return ADIN1140_PHY_ID1;
> +	case MII_PHYSID2:
> +		return ADIN1140_PHY_ID2;
> +	default:
> +		return 0xFFFF;
> +	}
> +}
> +
> +static int adin1140_mdiobus_write(struct mii_bus *bus, int addr, int regnum,
> +				  u16 val)
> +{
> +	return -EIO;
> +}
> +
> +static int adin1140_mdio_register(struct adin1140_priv *priv,
> +				  struct spi_device *spidev)
> +{
> +	int ret;
> +
> +	priv->mdiobus = mdiobus_alloc();
> +	if (!priv->mdiobus) {
> +		netdev_err(priv->netdev, "MDIO bus alloc failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	priv->mdiobus->name = "adin1140-mdiobus";
> +	priv->mdiobus->priv = priv->tc6;
> +	priv->mdiobus->parent = &spidev->dev;
> +	priv->mdiobus->phy_mask = ~BIT(0);
> +	priv->mdiobus->read = adin1140_mdiobus_read;
> +	priv->mdiobus->write = adin1140_mdiobus_write;
> +	priv->mdiobus->read_c45 = oa_tc6_mdiobus_read_c45;
> +	priv->mdiobus->write_c45 = oa_tc6_mdiobus_write_c45;
> +
> +	snprintf(priv->mdiobus->id, MII_BUS_ID_SIZE, "adin1140-%s.%u",
> +		 dev_name(&spidev->dev), spi_get_chipselect(spidev, 0));
> +
> +	ret = mdiobus_register(priv->mdiobus);
> +	if (ret) {
> +		netdev_err(priv->netdev, "Could not register MDIO bus\n");
> +		mdiobus_free(priv->mdiobus);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void adin1140_handle_link_change(struct net_device *netdev)
> +{
> +	phy_print_status(netdev->phydev);
> +}
> +
> +static int adin1140_phy_init(struct adin1140_priv *priv,
> +			     struct spi_device *spidev)
> +{
> +	int ret;
> +
> +	ret = adin1140_mdio_register(priv, spidev);
> +	if (ret)
> +		return ret;
> +
> +	priv->phydev = phy_find_first(priv->mdiobus);
> +	if (!priv->phydev) {
> +		netdev_err(priv->netdev, "No PHY found\n");
> +		ret = -ENODEV;
> +		goto free_mdio;
> +	}
> +
> +	priv->phydev->is_internal = true;
> +	ret = phy_connect_direct(priv->netdev, priv->phydev,
> +				 &adin1140_handle_link_change,
> +				 PHY_INTERFACE_MODE_INTERNAL);
> +	if (ret) {
> +		netdev_err(priv->netdev, "Can't attach PHY to %s\n",
> +			   priv->mdiobus->id);
> +		goto unregister_mdio;
> +	}
> +
> +	phy_attached_info(priv->phydev);
> +
> +	return 0;
> +
> +unregister_mdio:
> +	mdiobus_unregister(priv->mdiobus);
> +free_mdio:
> +	mdiobus_free(priv->mdiobus);
> +
> +	return ret;
> +}
> +
> +static void adin1140_phy_remove(struct adin1140_priv *priv)
> +{
> +	phy_disconnect(priv->phydev);
> +	mdiobus_unregister(priv->mdiobus);
> +	mdiobus_free(priv->mdiobus);
> +}
> +
> +static const struct ethtool_ops adin1140_ethtool_ops = {
> +	.get_drvinfo = adin1140_get_drvinfo,
> +	.get_link = ethtool_op_get_link,
> +	.get_ethtool_stats = adin1140_get_ethtool_stats,
> +	.get_sset_count = adin1140_get_sset_count,
> +	.get_strings = adin1140_get_ethtool_strings,
> +	.get_link_ksettings = phy_ethtool_get_link_ksettings,
> +	.set_link_ksettings = phy_ethtool_set_link_ksettings,
> +	.get_eth_mac_stats = adin1140_get_eth_mac_stats,
> +};
> +
> +static const struct net_device_ops adin1140_netdev_ops = {
> +	.ndo_open = adin1140_open,
> +	.ndo_stop = adin1140_close,
> +	.ndo_start_xmit	= adin1140_start_xmit,
> +	.ndo_set_mac_address = adin1140_set_mac_address,
> +	.ndo_validate_addr = eth_validate_addr,
> +	.ndo_set_rx_mode = adin1140_rx_mode,
> +	.ndo_eth_ioctl = phy_do_ioctl_running,
> +	.ndo_get_stats64 = adin1140_ndo_get_stats64,
> +};
> +
> +static int adin1140_probe(struct spi_device *spi)
> +{
> +	struct oa_tc6_config tc6_config = {};
> +	struct net_device *netdev;
> +	struct adin1140_priv *priv;
> +	int ret;
> +
> +	netdev = alloc_etherdev(sizeof(struct adin1140_priv));
> +	if (!netdev)
> +		return -ENOMEM;

devm_alloc_etherdev() and similar so that we can make error handling
easier? Any strong reason to not use device managed allocations?

> +
> +	priv = netdev_priv(netdev);
> +	priv->netdev = netdev;
> +	spi_set_drvdata(spi, priv);
> +	spin_lock_init(&priv->stat_lock);
> +
> +	tc6_config.spi = spi;
> +	tc6_config.netdev = netdev;
> +	tc6_config.quirk_flags = OA_TC6_BROKEN_PHY;
> +
> +	priv->tc6 = oa_tc6_init(&tc6_config);
> +	if (!priv->tc6) {
> +		ret = -ENODEV;
> +		goto netdev_free;
> +	}
> +
> +	ret = adin1140_phy_init(priv, spi);
> +	if (ret)
> +		goto oa_tc6_exit;
> +
> +	if (device_get_ethdev_address(&spi->dev, netdev))
> +		eth_hw_addr_random(netdev);
> +
> +	ret = adin1140_configure(priv);
> +	if (ret)
> +		goto phy_exit;
> +
> +	INIT_WORK(&priv->rx_mode_work, adin1140_rx_mode_work);
> +	INIT_DELAYED_WORK(&priv->stats_work, adin1140_stats_work);
> +
> +	netdev->if_port = IF_PORT_10BASET;
> +	netdev->irq = spi->irq;
> +	netdev->netdev_ops = &adin1140_netdev_ops;
> +	netdev->ethtool_ops = &adin1140_ethtool_ops;
> +	netdev->netns_immutable = true;
> +	netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE |
> +			      IFF_UNICAST_FLT;
> +
> +	ret = register_netdev(netdev);
> +	if (ret) {
> +		dev_err(&spi->dev, "Failed to register netdev (%d)", ret);

If we go to devm, this could be return dev_err_probe().

- Nuno Sá


  parent reply	other threads:[~2026-05-29  9:20 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-26 21:51 [PATCH net-next v2 00/10] net: Add ADIN1140 support Ciprian Regus
2026-05-26 21:51 ` Ciprian Regus via B4 Relay
2026-05-26 21:51 ` [PATCH net-next v2 01/10] dt-bindings: net: Add ADIN1140 Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-27 15:11   ` Conor Dooley
2026-05-28 16:46     ` Regus, Ciprian
2026-05-28 17:18       ` Conor Dooley
2026-05-26 21:51 ` [PATCH net-next v2 02/10] net: ethernet: oa_tc6: Handle the OA TC6 SPI protected mode Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-30  0:49   ` sashiko-bot
2026-05-26 21:51 ` [PATCH net-next v2 03/10] net: ethernet: oa_tc6: add OA_TC6_BROKEN_PHY quirk flag Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-28  2:12   ` Andrew Lunn
2026-05-30  0:49   ` sashiko-bot
2026-05-26 21:51 ` [PATCH net-next v2 04/10] net: ethernet: oa_tc6: Export the C45 access functions Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-28  2:13   ` Andrew Lunn
2026-05-30  0:49   ` sashiko-bot
2026-05-26 21:51 ` [PATCH net-next v2 05/10] net: ethernet: oa_tc6: Export standard defined registers Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-28  2:21   ` Andrew Lunn
2026-05-26 21:51 ` [PATCH net-next v2 06/10] net: ethernet: oa_tc6: Add MMS register formatting macro Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-28  2:31   ` Andrew Lunn
2026-05-26 21:51 ` [PATCH net-next v2 07/10] net: phy: add generic helpers for direct C45 MMD access Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-28  2:33   ` Andrew Lunn
2026-05-26 21:51 ` [PATCH net-next v2 08/10] net: phy: microchip-t1s: use generic C45 MMD access helpers Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-28  2:33   ` Andrew Lunn
2026-05-26 21:51 ` [PATCH net-next v2 09/10] net: phy: Add support for the ADIN1140 PHY Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-28  2:50   ` Andrew Lunn
2026-05-26 21:51 ` [PATCH net-next v2 10/10] net: ethernet: adi: Add a driver for the ADIN1140 MACPHY Ciprian Regus
2026-05-26 21:51   ` Ciprian Regus via B4 Relay
2026-05-28  3:10   ` Andrew Lunn
2026-05-28 12:43     ` Regus, Ciprian
2026-05-28 14:05       ` Andrew Lunn
2026-05-29  9:21   ` Nuno Sá [this message]
2026-05-29 13:03     ` Andrew Lunn
2026-06-01  9:17       ` Nuno Sá
2026-05-30  0:49   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ahlY8WSRO_HPmGtK@nsa \
    --to=noname.nuno@gmail.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=andrew@lunn.ch \
    --cc=ciprian.regus@analog.com \
    --cc=conor+dt@kernel.org \
    --cc=corbet@lwn.net \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=edumazet@google.com \
    --cc=hkallweit1@gmail.com \
    --cc=horms@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=parthiban.veerasooran@microchip.com \
    --cc=robh@kernel.org \
    --cc=skhan@linuxfoundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.