diff mbox series

Add LAN78XX OTP_ACCESS flag support

Message ID 20241025230550.25536-1-Fabi.Benschuh@fau.de
State New
Headers show
Series Add LAN78XX OTP_ACCESS flag support | expand

Commit Message

Fabian Benschuh Oct. 25, 2024, 11:05 p.m. UTC
With this flag we can now use ethtool to access the OTP:
ethtool --set-priv-flags eth0 OTP_ACCESS on
ethtool -e eth0  # this will read OTP if OTP_ACCESS is on, else EEPROM

When writing to OTP we need to set OTP_ACCESS on and write with the correct magic 0x7873 for OTP
---
 drivers/net/usb/lan78xx.c | 55 ++++++++++++++++++++++++++++++++-------
 1 file changed, 45 insertions(+), 10 deletions(-)

Comments

Andrew Lunn Oct. 28, 2024, 12:38 p.m. UTC | #1
On Sat, Oct 26, 2024 at 01:05:46AM +0200, Fabian Benschuh wrote:
> With this flag we can now use ethtool to access the OTP:
> ethtool --set-priv-flags eth0 OTP_ACCESS on
> ethtool -e eth0  # this will read OTP if OTP_ACCESS is on, else EEPROM
> 
> When writing to OTP we need to set OTP_ACCESS on and write with the correct magic 0x7873 for OTP


Please can you tell us more about OTP vs EEPROM? Is the OTP internal
while the EEPROM is external? What is contained in each? How does the
device decide which to use when it finds it has both?

I'm just wondering if we even need a private flag, if the hardware
will use one or the other exclusively?

	Andrew
Ronnie.Kunin@microchip.com Oct. 28, 2024, 3:02 p.m. UTC | #2
> From: Andrew Lunn <andrew@lunn.ch>
> Sent: Monday, October 28, 2024 8:38 AM
> 
> On Sat, Oct 26, 2024 at 01:05:46AM +0200, Fabian Benschuh wrote:
> > With this flag we can now use ethtool to access the OTP:
> > ethtool --set-priv-flags eth0 OTP_ACCESS on ethtool -e eth0  # this
> > will read OTP if OTP_ACCESS is on, else EEPROM
> >
> > When writing to OTP we need to set OTP_ACCESS on and write with the
> > correct magic 0x7873 for OTP
> 
> Please can you tell us more about OTP vs EEPROM? Is the OTP internal while the EEPROM is external?
> What is contained in each? How does the device decide which to use when it finds it has both?
> 
>         Andrew

This is pretty much the same implementation that is already in place for the Linux driver of the LAN743x PCIe device.

OTP (one time programmable) is a configuration memory internal to the device. EEPROM is external.
The internal OTP memory always exists but it may not be programmed. The EEPROM is optional, and if present can also be programmed or not.
A signature byte at offset 0 indicates whether the OTP or EEPROM device is programmed. 
If present, EEPROM has higher priority.
More info @ Chapter 10 here;
https://ww1.microchip.com/downloads/aemDocuments/documents/UNG/ProductDocuments/DataSheets/LAN7800-Data-Sheet-DS00001992H.pdf

> I'm just wondering if we even need a private flag, if the hardware will use one or the other exclusively?
>
Yes, both can be present/used, so we need the flag so we can tell the destination of a write or source of a read.

Ronnie
Andrew Lunn Oct. 28, 2024, 7:19 p.m. UTC | #3
On Mon, Oct 28, 2024 at 03:02:44PM +0000, Ronnie.Kunin@microchip.com wrote:
> > From: Andrew Lunn <andrew@lunn.ch>
> > Sent: Monday, October 28, 2024 8:38 AM
> > 
> > On Sat, Oct 26, 2024 at 01:05:46AM +0200, Fabian Benschuh wrote:
> > > With this flag we can now use ethtool to access the OTP:
> > > ethtool --set-priv-flags eth0 OTP_ACCESS on ethtool -e eth0  # this
> > > will read OTP if OTP_ACCESS is on, else EEPROM
> > >
> > > When writing to OTP we need to set OTP_ACCESS on and write with the
> > > correct magic 0x7873 for OTP
> > 
> > Please can you tell us more about OTP vs EEPROM? Is the OTP internal while the EEPROM is external?
> > What is contained in each? How does the device decide which to use when it finds it has both?
> > 
> >         Andrew
> 

> This is pretty much the same implementation that is already in place
> for the Linux driver of the LAN743x PCIe device.

That is good, it gives some degree of consistency. But i wounder if we
should go further. I doubt these are the only two devices which
support both EEPROM and OTP. It would be nicer to extend ethtool:

       ethtool -e|--eeprom-dump devname [raw on|off] [offset N] [length N] [otp] [eeprom]

There does not appear to be a netlink implementation of this ethtool
call. If we add one, we can add an additional optional attribute,
indicating OTP or EEPROM. We have done similar in the past. It
probably means within the kernel you replace struct ethtool_eeprom
with struct kernel_ethtool_eeprom which has the additional
parameter. The ioctl interface then never sees the new parameter,
which keeps with the kAPI. get_eeprom() and set_eeprom() probably have
all they need. get_eeprom_len() is more complex since it currently
only takes netdev. I think get_eeprom_len() needs its functionality
extended to indicate if the driver actually looks at the additional
parameter. We want the kAPI calls for get and set to failed with
-EOPNOTSUPP when otp or eeprom is not supported, which will initially
be 99% of the drivers. And we don't want to have to make proper code
changes to every driver. So maybe an additional parameter

	int	(*get_eeprom_len)(struct net_device *,
	                          struct kernel_eeprom_len *eeprom_len);

struct kernel_eeprom_len {
	int otp;
	int eeprom;
}

Have the core zero this. After the call, if they are still zero, they
are not supported.

I know this is a lot more work than your current patch, but it is a
better solution, should be well documented, easy to find and should
work for everybody, against a device private parameter which is not
obvious and unlikely to be consistent across drivers.

	Andrew
Ronnie.Kunin@microchip.com Oct. 28, 2024, 11:31 p.m. UTC | #4
Thanks Andrew.

Adding Rakesh who has been working on this patch internally at Microchip (though for an earlier kernel version) and after completing that was going to be upstreaming it for net-next (Fabian beat him to it). 

Ronnie

-----Original Message-----
From: Andrew Lunn <andrew@lunn.ch> 
Sent: Monday, October 28, 2024 3:19 PM
To: Ronnie Kunin - C21729 <Ronnie.Kunin@microchip.com>
Cc: Fabi.Benschuh@fau.de; Woojung Huh - C21699 <Woojung.Huh@microchip.com>; UNGLinuxDriver <UNGLinuxDriver@microchip.com>; andrew+netdev@lunn.ch; davem@davemloft.net; edumazet@google.com; kuba@kernel.org; pabeni@redhat.com; netdev@vger.kernel.org; linux-usb@vger.kernel.org
Subject: Re: [PATCH] Add LAN78XX OTP_ACCESS flag support

EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe

On Mon, Oct 28, 2024 at 03:02:44PM +0000, Ronnie.Kunin@microchip.com wrote:
> > From: Andrew Lunn <andrew@lunn.ch>
> > Sent: Monday, October 28, 2024 8:38 AM
> >
> > On Sat, Oct 26, 2024 at 01:05:46AM +0200, Fabian Benschuh wrote:
> > > With this flag we can now use ethtool to access the OTP:
> > > ethtool --set-priv-flags eth0 OTP_ACCESS on ethtool -e eth0  # 
> > > this will read OTP if OTP_ACCESS is on, else EEPROM
> > >
> > > When writing to OTP we need to set OTP_ACCESS on and write with 
> > > the correct magic 0x7873 for OTP
> >
> > Please can you tell us more about OTP vs EEPROM? Is the OTP internal while the EEPROM is external?
> > What is contained in each? How does the device decide which to use when it finds it has both?
> >
> >         Andrew
>

> This is pretty much the same implementation that is already in place 
> for the Linux driver of the LAN743x PCIe device.

That is good, it gives some degree of consistency. But i wounder if we should go further. I doubt these are the only two devices which support both EEPROM and OTP. It would be nicer to extend ethtool:

       ethtool -e|--eeprom-dump devname [raw on|off] [offset N] [length N] [otp] [eeprom]

There does not appear to be a netlink implementation of this ethtool call. If we add one, we can add an additional optional attribute, indicating OTP or EEPROM. We have done similar in the past. It probably means within the kernel you replace struct ethtool_eeprom with struct kernel_ethtool_eeprom which has the additional parameter. The ioctl interface then never sees the new parameter, which keeps with the kAPI. get_eeprom() and set_eeprom() probably have all they need. get_eeprom_len() is more complex since it currently only takes netdev. I think get_eeprom_len() needs its functionality extended to indicate if the driver actually looks at the additional parameter. We want the kAPI calls for get and set to failed with -EOPNOTSUPP when otp or eeprom is not supported, which will initially be 99% of the drivers. And we don't want to have to make proper code changes to every driver. So maybe an additional parameter

        int     (*get_eeprom_len)(struct net_device *,
                                  struct kernel_eeprom_len *eeprom_len);

struct kernel_eeprom_len {
        int otp;
        int eeprom;
}

Have the core zero this. After the call, if they are still zero, they are not supported.

I know this is a lot more work than your current patch, but it is a better solution, should be well documented, easy to find and should work for everybody, against a device private parameter which is not obvious and unlikely to be consistent across drivers.

        Andrew
Jakub Kicinski Oct. 29, 2024, 9:57 p.m. UTC | #5
On Tue, 29 Oct 2024 22:14:12 +0100 Andrew Lunn wrote:
> > > That is good, it gives some degree of consistency. But i wounder if we
> > > should go further. I doubt these are the only two devices which
> > > support both EEPROM and OTP. It would be nicer to extend ethtool:
> > > 
> > >        ethtool -e|--eeprom-dump devname [raw on|off] [offset N] [length N] [otp] [eeprom]  
> > 
> > After a cursory look at the conversation I wonder if it wouldn't 
> > be easier to register devlink regions for eeprom and otp?  
> 
> devlink regions don't allow write. ethtool does.

Sorry I missed the write part.
I see you already asked the "why" but I don't think the answer 
is entirely to the point. We need to know more - netdev focuses on
production use cases. Burning an OTP seems like a manufacturing action.
diff mbox series

Patch

diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 8adf77e3557e..2fc9b9b138b0 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -85,6 +85,7 @@ 
 #define EEPROM_INDICATOR		(0xA5)
 #define EEPROM_MAC_OFFSET		(0x01)
 #define MAX_EEPROM_SIZE			512
+#define MAX_OTP_SIZE			512
 #define OTP_INDICATOR_1			(0xF3)
 #define OTP_INDICATOR_2			(0xF7)
 
@@ -172,6 +173,7 @@ 
 #define INT_EP_GPIO_2			(2)
 #define INT_EP_GPIO_1			(1)
 #define INT_EP_GPIO_0			(0)
+#define LAN78XX_NET_FLAG_OTP		BIT(0)
 
 static const char lan78xx_gstrings[][ETH_GSTRING_LEN] = {
 	"RX FCS Errors",
@@ -446,6 +448,7 @@  struct lan78xx_net {
 	unsigned int		burst_cap;
 
 	unsigned long		flags;
+	u32			priv_flags;
 
 	wait_queue_head_t	*wait;
 	unsigned char		suspend_count;
@@ -1542,6 +1545,10 @@  static void lan78xx_status(struct lan78xx_net *dev, struct urb *urb)
 
 static int lan78xx_ethtool_get_eeprom_len(struct net_device *netdev)
 {
+	struct lan78xx_net *dev = netdev_priv(netdev);
+
+	if (dev->priv_flags & LAN78XX_NET_FLAG_OTP)
+		return MAX_OTP_SIZE;
 	return MAX_EEPROM_SIZE;
 }
 
@@ -1555,9 +1562,10 @@  static int lan78xx_ethtool_get_eeprom(struct net_device *netdev,
 	if (ret)
 		return ret;
 
-	ee->magic = LAN78XX_EEPROM_MAGIC;
-
-	ret = lan78xx_read_raw_eeprom(dev, ee->offset, ee->len, data);
+	if (dev->priv_flags & LAN78XX_NET_FLAG_OTP)
+		ret = lan78xx_read_raw_otp(dev, ee->offset, ee->len, data);
+	else
+		ret = lan78xx_read_raw_eeprom(dev, ee->offset, ee->len, data);
 
 	usb_autopm_put_interface(dev->intf);
 
@@ -1577,30 +1585,39 @@  static int lan78xx_ethtool_set_eeprom(struct net_device *netdev,
 	/* Invalid EEPROM_INDICATOR at offset zero will result in a failure
 	 * to load data from EEPROM
 	 */
-	if (ee->magic == LAN78XX_EEPROM_MAGIC)
-		ret = lan78xx_write_raw_eeprom(dev, ee->offset, ee->len, data);
-	else if ((ee->magic == LAN78XX_OTP_MAGIC) &&
-		 (ee->offset == 0) &&
-		 (ee->len == 512) &&
-		 (data[0] == OTP_INDICATOR_1))
-		ret = lan78xx_write_raw_otp(dev, ee->offset, ee->len, data);
+	if (dev->priv_flags & LAN78XX_NET_FLAG_OTP) {
+		/* Beware!  OTP is One Time Programming ONLY! */
+		if (ee->magic == LAN78XX_OTP_MAGIC)
+		    ret = lan78xx_write_raw_otp(dev, ee->offset, ee->len, data);
+	} else {
+		if (ee->magic == LAN78XX_EEPROM_MAGIC)
+		    ret = lan78xx_write_raw_eeprom(dev, ee->offset, ee->len, data);
+	}
 
 	usb_autopm_put_interface(dev->intf);
 
 	return ret;
 }
 
+static const char lan78xx_priv_flags_strings[][ETH_GSTRING_LEN] = {
+	"OTP_ACCESS",
+};
+
 static void lan78xx_get_strings(struct net_device *netdev, u32 stringset,
 				u8 *data)
 {
 	if (stringset == ETH_SS_STATS)
 		memcpy(data, lan78xx_gstrings, sizeof(lan78xx_gstrings));
+	else if (stringset == ETH_SS_PRIV_FLAGS)
+		memcpy(data, lan78xx_priv_flags_strings, sizeof(lan78xx_priv_flags_strings));
 }
 
 static int lan78xx_get_sset_count(struct net_device *netdev, int sset)
 {
 	if (sset == ETH_SS_STATS)
 		return ARRAY_SIZE(lan78xx_gstrings);
+	else if (sset == ETH_SS_PRIV_FLAGS)
+		return ARRAY_SIZE(lan78xx_priv_flags_strings);
 	else
 		return -EOPNOTSUPP;
 }
@@ -1617,6 +1634,22 @@  static void lan78xx_get_stats(struct net_device *netdev,
 	mutex_unlock(&dev->stats.access_lock);
 }
 
+static u32 lan78xx_ethtool_get_priv_flags(struct net_device *netdev)
+{
+	struct lan78xx_net *dev = netdev_priv(netdev);
+
+	return dev->priv_flags;
+}
+
+static int lan78xx_ethtool_set_priv_flags(struct net_device *netdev, u32 flags)
+{
+	struct lan78xx_net *dev = netdev_priv(netdev);
+
+	dev->priv_flags = flags;
+
+	return 0;
+}
+
 static void lan78xx_get_wol(struct net_device *netdev,
 			    struct ethtool_wolinfo *wol)
 {
@@ -1905,6 +1938,8 @@  static const struct ethtool_ops lan78xx_ethtool_ops = {
 	.get_eeprom	= lan78xx_ethtool_get_eeprom,
 	.set_eeprom	= lan78xx_ethtool_set_eeprom,
 	.get_ethtool_stats = lan78xx_get_stats,
+	.get_priv_flags = lan78xx_ethtool_get_priv_flags,
+	.set_priv_flags = lan78xx_ethtool_set_priv_flags,
 	.get_sset_count = lan78xx_get_sset_count,
 	.get_strings	= lan78xx_get_strings,
 	.get_wol	= lan78xx_get_wol,