Linux Documentation
 help / color / mirror / Atom feed
* Re: [PATCH net-next 00/10] docs: net: updates for old and cobwebbed docs
From: Randy Dunlap @ 2026-05-27  0:51 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, corbet,
	vladimir.oltean, willemb, sdf.kernel, ecree.xilinx,
	jesse.brandeburg, linux-doc
In-Reply-To: <20260526154001.4a8d30bf@kernel.org>



On 5/26/26 3:40 PM, Jakub Kicinski wrote:
> On Tue, 26 May 2026 15:37:19 -0700 Jakub Kicinski wrote:
>> On Tue, 26 May 2026 11:48:41 -0700 Randy Dunlap wrote:
>>> WARNING: ../include/linux/netdevice.h:2622 Excess struct member 'ax25_ptr' description in 'net_device'  
>>
>> I wonder how that sneaked in? ;) ;)
>>
>> I'll clean this up separately, hopefully we haven't regressed too many
>> things while the script was broken :(
> 
> Ugh, I'm still not seeing this on Linus's tree.
> Is the fix for kernel-doc skipping such warnings on its way to Linus, 
> or queue for -next?

It's in today's linux-next (20260526), line 1950 of
include/linux/netdevice.h:

 *	@ax25_ptr:	AX.25 specific data

from 2014:
commit 536721b1cb3f
Author: Karoly Kemeny <karoly.kemeny@gmail.com>
Date:   Wed Jul 30 20:27:36 2014 +0200
    net: kernel-doc compliant documentation for net_device


-- 
~Randy


^ permalink raw reply

* Re: [PATCH net-next] Documentation: networking: Add a test plan for ethtool pause validation
From: Jakub Kicinski @ 2026-05-27  0:24 UTC (permalink / raw)
  To: Maxime Chevallier (Netdev Foundation)
  Cc: Andrew Lunn, davem, Eric Dumazet, Paolo Abeni, Simon Horman,
	Russell King, Heiner Kallweit, Jonathan Corbet, Shuah Khan,
	Oleksij Rempel, Vladimir Oltean, Florian Fainelli,
	thomas.petazzoni, netdev, linux-kernel, linux-doc
In-Reply-To: <20260522175109.198059-1-maxime.chevallier@bootlin.com>

On Fri, 22 May 2026 19:51:06 +0200 Maxime Chevallier (Netdev
Foundation) wrote:
>  Documentation/networking/pause_test_plan.rst | 556 +++++++++++++++++++

It'd be great to hear from others but IMHO in the current form this is
not suitable for Documentation/networking/ We can commit the "knowledge"
part but enumerating the test cases seems odd for Documentation/.

^ permalink raw reply

* Re: [mtd:spi-nor/next 20/28] htmldocs: Documentation/driver-api/mtd/spi-nor.rst:216: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
From: Randy Dunlap @ 2026-05-27  0:16 UTC (permalink / raw)
  To: kernel test robot, Miquel Raynal; +Cc: oe-kbuild-all, Pratyush Yadav, linux-doc
In-Reply-To: <202605270105.CZQo8wSO-lkp@intel.com>



On 5/26/26 4:24 PM, kernel test robot wrote:
> tree:   https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git spi-nor/next
> head:   113ff79be3bcf0262eb7860d3325dd951ff29499
> commit: ecbba8d91f4bd0fccd722a7e5a57cec385c11ca8 [20/28] mtd: spi-nor: Add steps for testing locking support
> compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
> docutils: docutils (Docutils 0.21.2, Python 3.13.5, on linux)
> reproduce: (https://download.01.org/0day-ci/archive/20260527/202605270105.CZQo8wSO-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202605270105.CZQo8wSO-lkp@intel.com/
> 
> All warnings (new ones prefixed by >>):
> 

>    Documentation/driver-api/mtd/spi-nor.rst:215: ERROR: Unexpected indentation. [docutils]
>>> Documentation/driver-api/mtd/spi-nor.rst:216: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]


I've already sent a patch for this.
https://lore.kernel.org/linux-doc/20260526172341.773398-1-rdunlap@infradead.org/T/#u


-- 
~Randy


^ permalink raw reply

* [PATCH] leds: Fix sysfs ABI date
From: Armin Wolf @ 2026-05-27  0:14 UTC (permalink / raw)
  To: lee, pavel; +Cc: corbet, linux-leds, linux-doc

The "multi_max_intensity" sysfs attribute was not included
in kernel 7.1, so update the KernelVersion and Date tags
accordingly.

Fixes: b1a9b7a904af ("leds: Introduce the multi_max_intensity sysfs attribute")
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 Documentation/ABI/testing/sysfs-class-led-multicolor | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-led-multicolor b/Documentation/ABI/testing/sysfs-class-led-multicolor
index 197da3e775b4..62ce58f393d6 100644
--- a/Documentation/ABI/testing/sysfs-class-led-multicolor
+++ b/Documentation/ABI/testing/sysfs-class-led-multicolor
@@ -26,8 +26,8 @@ Description:	read/write
 		Documentation/leds/leds-class-multicolor.rst.
 
 What:		/sys/class/leds/<led>/multi_max_intensity
-Date:		March 2026
-KernelVersion:	7.1
+Date:		May 2026
+KernelVersion:	7.2
 Contact:	Armin Wolf <W_Armin@gmx.de>
 Description:	read
 		This file contains an array of integers describing the maximum
-- 
2.39.5


^ permalink raw reply related

* Re: [PATCH] platform/x86: thinkpad_acpi: Add USB-C Security (USCS) support
From: Vishnu Sankar @ 2026-05-27  0:09 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: Mark Pearson, skhan, hmh, Hans de Goede, corbet, derekjohn.clark,
	LKML, ibm-acpi-devel, linux-doc, platform-driver-x86, vsankar
In-Reply-To: <d9f33111-cd96-a37f-cf3f-23f6758c7e18@linux.intel.com>

On Tue, May 26, 2026 at 6:16 PM Ilpo Järvinen
<ilpo.jarvinen@linux.intel.com> wrote:
>
> On Tue, 26 May 2026, Vishnu Sankar wrote:
>
> > Hi Ilpo,
> >
> > Thank you for the comments.
> >
> > On Tue, May 26, 2026 at 2:19 AM Ilpo Järvinen
> > <ilpo.jarvinen@linux.intel.com> wrote:
> > >
> > > On Fri, 22 May 2026, Vishnu Sankar wrote:
> > >
> > > > Newer ThinkPad systems expose a USB-C Security (Restricted Mode) feature.
> > > > When active, USB-C data connections are disabled while power delivery is
> > > > preserved. This is useful for kiosk and physically-secured deployments.
> > > >
> > > > Hardware interface:
> > > >
> > > > The HKEY device exposes a read-only ACPI method USCS():
> > > >
> > > >   Return value bit layout:
> > > >     Bit 16 : Capability flag (1 = feature present on this SKU)
> > > >     Bit  0 : Current state  (0 = security OFF, 1 = security ON)
> > > >
> > > > The sysfs attribute is read-only.
> > > >
> > > > The Fn+U followed by Fn+S hotkey chord is the only way to toggle the
> > > > hardware state.
> > > >
> > > > Hotkey:
> > > >
> > > > Fn+U followed by Fn+S generates HKEY event 0x131e.
> > > >
> > > > sysfs interface:
> > > >
> > > >   /sys/devices/platform/thinkpad_acpi/usb_c_security  (read-only)
> > > >   "enabled\n"  -- data connections are currently blocked
> > > >   "disabled\n" -- data connections are currently allowed
> > > >
> > > >   The attribute is hidden on SKUs where the USCS capability bit (bit 16)
> > > >   is not set, so there is no ABI impact on unsupported hardware.
> > > >
> > > > Suggested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> > > > Signed-off-by: Vishnu Sankar <vishnuocv@gmail.com>
> > > > ---
> > > >  .../admin-guide/laptops/thinkpad-acpi.rst     |  24 ++++
> > > >  drivers/platform/x86/lenovo/thinkpad_acpi.c   | 115 ++++++++++++++++++
> > > >  2 files changed, 139 insertions(+)
> > > >
> > > > diff --git a/Documentation/admin-guide/laptops/thinkpad-acpi.rst b/Documentation/admin-guide/laptops/thinkpad-acpi.rst
> > > > index f874db31801d..db4588af0278 100644
> > > > --- a/Documentation/admin-guide/laptops/thinkpad-acpi.rst
> > > > +++ b/Documentation/admin-guide/laptops/thinkpad-acpi.rst
> > > > @@ -1543,6 +1543,30 @@ Values:
> > > >
> > > >       This setting can also be toggled via the Fn+doubletap hotkey.
> > > >
> > > > +USB-C Security
> > > > +--------------
> > > > +
> > > > +sysfs: usb_c_security
> > > > +
> > > > +Reports the current state of the USB-C Security (Restricted Mode) feature
> > > > +on supported ThinkPad systems. When enabled, USB-C data connections are
> > > > +disabled while power delivery is preserved.
> > > > +
> > > > +The available command is::
> > > > +
> > > > +        cat /sys/devices/platform/thinkpad_acpi/usb_c_security
> > > > +
> > > > +Values:
> > > > +
> > > > +     * ``enabled``  - USB-C data connections are currently blocked
> > > > +     * ``disabled`` - USB-C data connections are currently allowed
> > > > +
> > > > +The attribute is read-only. The USB-C Security state can only be toggled
> > > > +via the Fn+U followed by Fn+S hotkey chord.
> > > > +
> > > > +The sysfs attribute is not created on platforms that do not support this
> > > > +feature.
> > > > +
> > > >  Auxmac
> > > >  ------
> > > >
> > > > diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c
> > > > index e1cee42a1683..889db802185a 100644
> > > > --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c
> > > > +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c
> > > > @@ -185,6 +185,7 @@ enum tpacpi_hkey_event_t {
> > > >       TP_HKEY_EV_AMT_TOGGLE           = 0x131a, /* Toggle AMT on/off */
> > > >       TP_HKEY_EV_CAMERASHUTTER_TOGGLE = 0x131b, /* Toggle Camera Shutter */
> > > >       TP_HKEY_EV_DOUBLETAP_TOGGLE     = 0x131c, /* Toggle trackpoint doubletap on/off */
> > > > +     TP_HKEY_EV_USB_C_SECURITY       = 0x131e, /* Toggle USB C Security ON/OFF */
> > > >       TP_HKEY_EV_PROFILE_TOGGLE       = 0x131f, /* Toggle platform profile in 2024 systems */
> > > >       TP_HKEY_EV_PROFILE_TOGGLE2      = 0x1401, /* Toggle platform profile in 2025 + systems */
> > > >
> > > > @@ -373,6 +374,8 @@ static struct {
> > > >       u32 has_adaptive_kbd:1;
> > > >       u32 kbd_lang:1;
> > > >       u32 trackpoint_doubletap_enable:1;
> > > > +     u32 usbc_security_supported:1;
> > > > +     u32 usbc_security_enabled:1;
> > > >       struct quirk_entry *quirks;
> > > >  } tp_features;
> > > >
> > > > @@ -11265,6 +11268,111 @@ static struct ibm_struct hwdd_driver_data = {
> > > >       .name = "hwdd",
> > > >  };
> > > >
> > > > +/*************************************************************************
> > > > + * USB-C Security subdriver
> > > > + *
> > > > + * HKEY.USCS(0) is a read-only ACPI method; its argument is ignored.
> > > > + * It always returns:
> > > > + *   bit 16 - USB-C security capability present on this SKU or not
> > > > + *   bit  0 - USB-C Security state (enable or disable)
> > > > + *
> > > > + * Hotkey
> > > > + * ------
> > > > + * 0x131e (Fn+U, Fn+S): firmware toggles USBS before firing the event.
> > > > + * The driver reads back the new state and notifies the sysfs attribute.
> > > > + *
> > > > + */
> > > > +
> > > > +/* USCS() return word bit layout */
> > > > +#define USCS_CAP_BIT         BIT(16) /* capability: feature present on SKU */
> > > > +#define USCS_STATUS_BIT              BIT(0)  /* current security state */
> > > > +
> > > > +static DEFINE_MUTEX(usbc_security_mutex);
> > > > +
> > > > +/*
> > > > + * usbc_security_query - read current USB-C security state via USCS()
> > > > + * @enabled: out - true when security is ON (data connections blocked)
> > > > + *
> > > > + * Returns true if the feature is supported and query succeeded,
> > > > + * false otherwise (feature absent or ACPI call failed).
> > > > + */
> > > > +static bool usbc_security_query(bool *enabled)
> > > > +{
> > > > +     int status;
> > > > +
> > > > +     mutex_lock(&usbc_security_mutex);
> > > > +     if (!acpi_evalf(hkey_handle, &status, "USCS", "dd", 0)) {
> > > > +             mutex_unlock(&usbc_security_mutex);
> > > > +             return false;
> > > > +     }
> > > > +     mutex_unlock(&usbc_security_mutex);
> > >
> > > Please use cleanup.h.
> > Will do.
> > Will replace the manual mutex_lock/unlock pattern with guard(mutex).
> > >
> > > > +
> > > > +     if (!(status & USCS_CAP_BIT)) {
> > > > +             pr_debug("USCS cap bit absent (raw=0x%x)\n", status);
> > > > +             return false;
> > > > +     }
> > > > +
> > > > +     *enabled = !!(status & USCS_STATUS_BIT);
> > >
> > > No need to do !! when assigning to bool.
> > Agreed, will remove the !!.
> > >
> > > > +     return true;
> > > > +}
> > > > +
> > > > +/* sysfs: /sys/devices/platform/thinkpad_acpi/usb_c_security ---------- */
> > > > +static ssize_t usb_c_security_show(struct device *dev,
> > > > +                                struct device_attribute *attr,
> > > > +                                char *buf)
> > > > +{
> > > > +     return sysfs_emit(buf, "%s\n",
> > > > +                       tp_features.usbc_security_enabled ? "enabled" : "disabled");
> > >
> > > Please use string_choices.h.
> > Will use str_enabled_disabled() which is already used in
> > thinkpad_acpi.c for similar pattern.
> > >
> > > > +}
> > > > +
> > > > +static DEVICE_ATTR_RO(usb_c_security);
> > > > +
> > > > +static struct attribute *usbc_security_attributes[] = {
> > > > +     &dev_attr_usb_c_security.attr,
> > > > +     NULL,
> > > > +};
> > > > +
> > > > +static umode_t usbc_security_attr_is_visible(struct kobject *kobj,
> > > > +                                          struct attribute *attr, int n)
> > > > +{
> > > > +     return tp_features.usbc_security_supported ? attr->mode : 0;
> > > > +}
> > > > +
> > > > +static const struct attribute_group usbc_security_attr_group = {
> > > > +     .is_visible = usbc_security_attr_is_visible,
> > > > +     .attrs = usbc_security_attributes,
> > > > +};
> > > > +
> > > > +static int tpacpi_usbc_security_init(struct ibm_init_struct *iibm)
> > > > +{
> > > > +     bool enabled;
> > > > +
> > > > +     tp_features.usbc_security_supported =
> > > > +             usbc_security_query(&enabled);
> > >
> > > Fits to one line.
> > Acked.
> > >
> > > > +     tp_features.usbc_security_enabled = enabled;
> > >
> > > I'm not sure I follow the logic here as you always seem set it enabled
> > > disregarding even support or not?
> > >
> > > And that hotkey enabling seems deadcode with it already enabled here.
> > >
> > > I feel like I must blind to something obvious but cannot just find
> > > what that would be.
> > I'm sorry, this is a bug.
> > I will change to the following:
> >     tp_features.usbc_security_supported = usbc_security_query(&enabled);
> >     if (tp_features.usbc_security_supported)
> >         tp_features.usbc_security_enabled = enabled;
> >
> > The hotkey path is not dead code — it updates usbc_security_enabled
> > when the user presses Fn+U Fn+S, since the EC firmware toggles the
> > state and fires the event. The init only captures the state at boot;
> > subsequent toggles come through the hotkey. Sorry for the confusion in
> > the init code.
>
> Oh, I see now what I was missing and it's just as obvious I was I
> thinking... I, for some reason, read these as:
>
>         tp_features.usbc_security_enabled = true;
>
> ...but you had = enabled there. I'm sorry about my confusion.
>
> I find the the way usbc_security_query() is architected a bit confusing
> though, it returns supported/not supported in return value and another
> boolean through the bool pointer. And that looks broken, if
> usbc_security_query() returns early, it won't set *enabled which then
> remains uninitialized (contains pseudogarbage from stack so your testing
> might have been luck and had it always working the way you wanted).
Yes, the usbc_security_query() is confusing and does have the
uninitialized *enabled bug.
I only tested on systems that have the USB-C Security feature, so the
unsupported path
was never exercised and the bug was not caught.

I will do something like this:

    static int usbc_security_query(bool *enabled)
    {
        int status;

        guard(mutex)(&usbc_security_mutex);
        if (!acpi_evalf(hkey_handle, &status, "USCS", "dd", 0))
            return -EIO;

        if (!(status & USCS_CAP_BIT))
            return -ENODEV;

        *enabled = status & USCS_STATUS_BIT;
        return 0;
    }

With this, *enabled is only ever read after a successful return 0, so
the uninitialized value issue may not happen. Init and hotkey paths
become straightforward as well.
Hope this is fine.
>
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +/* tpacpi_usbc_security_hotkey - handle Fn+U Fn+S hotkey (0x131e) */
> > > > +static bool tpacpi_usbc_security_hotkey(void)
> > > > +{
> > > > +     bool enabled;
> > > > +
> > > > +     if (!tp_features.usbc_security_supported)
> > > > +             return false;
> > > > +
> > > > +     if (!usbc_security_query(&enabled))
> > > > +             return false;
> > > > +
> > > > +     tp_features.usbc_security_enabled = enabled;
> > > > +     sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "usb_c_security");
> > > > +     return true;
> > > > +}
> > > > +
> > > > +static struct ibm_struct usbc_security_driver_data = {
> > > > +     .name = "usbc_security",
> > > > +};
> > > > +
> > > >  /* --------------------------------------------------------------------- */
> > > >
> > > >  static struct attribute *tpacpi_driver_attributes[] = {
> > > > @@ -11325,6 +11433,7 @@ static const struct attribute_group *tpacpi_groups[] = {
> > > >       &dprc_attr_group,
> > > >       &auxmac_attr_group,
> > > >       &hwdd_attr_group,
> > > > +     &usbc_security_attr_group,
> > > >       NULL,
> > > >  };
> > > >
> > > > @@ -11479,6 +11588,8 @@ static bool tpacpi_driver_event(const unsigned int hkey_event)
> > > >       case TP_HKEY_EV_PROFILE_TOGGLE2:
> > > >               platform_profile_cycle();
> > > >               return true;
> > > > +     case TP_HKEY_EV_USB_C_SECURITY:
> > > > +             return tpacpi_usbc_security_hotkey();
> > > >       }
> > > >
> > > >       return false;
> > > > @@ -11930,6 +12041,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
> > > >               .init = tpacpi_hwdd_init,
> > > >               .data = &hwdd_driver_data,
> > > >       },
> > > > +     {
> > > > +             .init = tpacpi_usbc_security_init,
> > > > +             .data = &usbc_security_driver_data,
> > > > +     },
> > > >  };
> > > >
> > > >  static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
> > > >
> > >
> > > --
> > >  i.
> > >
> >
> > I will update the v2 with all these changes soon, if no further comments.
> >
>
> --
>  i.



-- 

Regards,

      Vishnu Sankar

^ permalink raw reply

* [mtd:spi-nor/next 20/28] htmldocs: Documentation/driver-api/mtd/spi-nor.rst:216: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
From: kernel test robot @ 2026-05-26 23:24 UTC (permalink / raw)
  To: Miquel Raynal; +Cc: oe-kbuild-all, Pratyush Yadav, linux-doc

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git spi-nor/next
head:   113ff79be3bcf0262eb7860d3325dd951ff29499
commit: ecbba8d91f4bd0fccd722a7e5a57cec385c11ca8 [20/28] mtd: spi-nor: Add steps for testing locking support
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
docutils: docutils (Docutils 0.21.2, Python 3.13.5, on linux)
reproduce: (https://download.01.org/0day-ci/archive/20260527/202605270105.CZQo8wSO-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202605270105.CZQo8wSO-lkp@intel.com/

All warnings (new ones prefixed by >>):

   Documentation/driver-api/basics:42: ./kernel/time/time.c:370: WARNING: Duplicate C declaration, also defined at driver-api/basics:436.
   Declaration is '.. c:function:: unsigned int jiffies_to_msecs (const unsigned long j)'. [duplicate_declaration.c]
   Documentation/driver-api/basics:42: ./kernel/time/time.c:393: WARNING: Duplicate C declaration, also defined at driver-api/basics:453.
   Declaration is '.. c:function:: unsigned int jiffies_to_usecs (const unsigned long j)'. [duplicate_declaration.c]
   Documentation/driver-api/mtd/spi-nor.rst:215: ERROR: Unexpected indentation. [docutils]
>> Documentation/driver-api/mtd/spi-nor.rst:216: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
   Documentation/driver-api/target:25: ./drivers/target/target_core_user.c:35: ERROR: Unexpected section title.


vim +216 Documentation/driver-api/mtd/spi-nor.rst

    78	
    79	    root@1:~# cat /sys/kernel/debug/spi-nor/spi0.0/capabilities
    80	    Supported read modes by the flash
    81	     1S-1S-1S
    82	      opcode		0x03
    83	      mode cycles	0
    84	      dummy cycles	0
    85	     1S-1S-1S (fast read)
    86	      opcode		0x0b
    87	      mode cycles	0
    88	      dummy cycles	8
    89	     1S-1S-2S
    90	      opcode		0x3b
    91	      mode cycles	0
    92	      dummy cycles	8
    93	     1S-2S-2S
    94	      opcode		0xbb
    95	      mode cycles	4
    96	      dummy cycles	0
    97	     1S-1S-4S
    98	      opcode		0x6b
    99	      mode cycles	0
   100	      dummy cycles	8
   101	     1S-4S-4S
   102	      opcode		0xeb
   103	      mode cycles	2
   104	      dummy cycles	4
   105	     4S-4S-4S
   106	      opcode		0x0b
   107	      mode cycles	2
   108	      dummy cycles	4
   109	
   110	    Supported page program modes by the flash
   111	     1S-1S-1S
   112	      opcode	0x02
   113	
   114	    root@1:~# cat /sys/kernel/debug/spi-nor/spi0.0/params
   115	    name		sst26vf064b
   116	    id			bf 26 43 bf 26 43
   117	    size		8.00 MiB
   118	    write size		1
   119	    page size		256
   120	    address nbytes	3
   121	    flags		HAS_LOCK | HAS_16BIT_SR | SOFT_RESET | SWP_IS_VOLATILE
   122	
   123	    opcodes
   124	     read		0xeb
   125	      dummy cycles	6
   126	     erase		0x20
   127	     program		0x02
   128	     8D extension	none
   129	
   130	    protocols
   131	     read		1S-4S-4S
   132	     write		1S-1S-1S
   133	     register		1S-1S-1S
   134	
   135	    erase commands
   136	     20 (4.00 KiB) [0]
   137	     d8 (8.00 KiB) [1]
   138	     d8 (32.0 KiB) [2]
   139	     d8 (64.0 KiB) [3]
   140	     c7 (8.00 MiB)
   141	
   142	    sector map
   143	     region (in hex)   | erase mask | flags
   144	     ------------------+------------+----------
   145	     00000000-00007fff |     [01  ] |
   146	     00008000-0000ffff |     [0 2 ] |
   147	     00010000-007effff |     [0  3] |
   148	     007f0000-007f7fff |     [0 2 ] |
   149	     007f8000-007fffff |     [01  ] |
   150	
   151	4) Use `mtd-utils <https://git.infradead.org/mtd-utils.git>`__
   152	   and verify that erase, read and page program operations work fine::
   153	
   154	    root@1:~# dd if=/dev/urandom of=./spi_test bs=1M count=2
   155	    2+0 records in
   156	    2+0 records out
   157	    2097152 bytes (2.1 MB, 2.0 MiB) copied, 0.848566 s, 2.5 MB/s
   158	
   159	    root@1:~# mtd_debug erase /dev/mtd0 0 2097152
   160	    Erased 2097152 bytes from address 0x00000000 in flash
   161	
   162	    root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
   163	    Copied 2097152 bytes from address 0x00000000 in flash to spi_read
   164	
   165	    root@1:~# hexdump spi_read
   166	    0000000 ffff ffff ffff ffff ffff ffff ffff ffff
   167	    *
   168	    0200000
   169	
   170	    root@1:~# sha256sum spi_read
   171	    4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5  spi_read
   172	
   173	    root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test
   174	    Copied 2097152 bytes from spi_test to address 0x00000000 in flash
   175	
   176	    root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
   177	    Copied 2097152 bytes from address 0x00000000 in flash to spi_read
   178	
   179	    root@1:~# sha256sum spi*
   180	    c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8  spi_read
   181	    c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8  spi_test
   182	
   183	   If the flash comes erased by default and the previous erase was ignored,
   184	   we won't catch it, thus test the erase again::
   185	
   186	    root@1:~# mtd_debug erase /dev/mtd0 0 2097152
   187	    Erased 2097152 bytes from address 0x00000000 in flash
   188	
   189	    root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
   190	    Copied 2097152 bytes from address 0x00000000 in flash to spi_read
   191	
   192	    root@1:~# sha256sum spi*
   193	    4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5  spi_read
   194	    c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8  spi_test
   195	
   196	   Dump some other relevant data::
   197	
   198	    root@1:~# mtd_debug info /dev/mtd0
   199	    mtd.type = MTD_NORFLASH
   200	    mtd.flags = MTD_CAP_NORFLASH
   201	    mtd.size = 8388608 (8M)
   202	    mtd.erasesize = 4096 (4K)
   203	    mtd.writesize = 1
   204	    mtd.oobsize = 0
   205	    regions = 0
   206	
   207	5) If your flash supports locking, please go through the following test
   208	   procedure to make sure it correctly behaves. The below example
   209	   expects the typical situation where eraseblocks and lock sectors have
   210	   the same size. In case you enabled MTD_SPI_NOR_USE_4K_SECTORS, you
   211	   must adapt `bs` accordingly.
   212	
   213	   Warning: These tests may hard lock your device! Make sure:
   214	   - The device is not hard locked already (#WP strapped to low and
   215	     SR_SRWD bit set)
 > 216	   - If you have a WPn pin, you may want to set `no-wp` in your DT for

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH v16 net-next 00/11] nbl driver for Nebulamatrix NICs
From: Jakub Kicinski @ 2026-05-26 23:21 UTC (permalink / raw)
  To: illusion.wang
  Cc: dimon.zhao, alvin.wang, sam.chen, netdev, andrew+netdev, corbet,
	horms, linux-doc, pabeni, vadim.fedorenko, lukas.bulwahn,
	edumazet, enelsonmoore, skhan, hkallweit1, open list
In-Reply-To: <20260526035453.2359-1-illusion.wang@nebula-matrix.com>

On Tue, 26 May 2026 11:54:37 +0800 illusion.wang wrote:
> This patch series represents the first phase. We plan to integrate it in
> two phases: the first phase covers mailbox and chip configuration,
> while the second phase involves net dev configuration.
> Together, they will provide basic PF-based Ethernet port transmission and
> reception capabilities.
> 
> After that, we will consider other features, such as ethtool support,
> flow management, adminq messaging, VF support, debugfs support, etc.

The coccicheck bot flagged a new Coccinelle warning introduced by the
nebula-matrix driver series:

  drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c:187:45-52:
    WARNING: Consider using %pe to print PTR_ERR()

The scripts/coccinelle/misc/ptr_err.cocci rule fires when a PTR_ERR()
value is passed to a format string without using the %pe specifier,
which is the kernel-preferred way to print error pointers (it displays
both the numeric value and the symbolic name).

Please fix nbl_main.c line 187 to use %pe instead of passing the raw
PTR_ERR() value to %ld or similar.

For example, change something like:
  pr_err("...: %ld\n", PTR_ERR(ptr));
to:
  pr_err("...: %pe\n", ptr);

^ permalink raw reply

* Re: [PATCH net-next 00/10] docs: net: updates for old and cobwebbed docs
From: Jakub Kicinski @ 2026-05-26 22:40 UTC (permalink / raw)
  To: Randy Dunlap
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, corbet,
	vladimir.oltean, willemb, sdf.kernel, ecree.xilinx,
	jesse.brandeburg, linux-doc
In-Reply-To: <20260526153719.7261b7f2@kernel.org>

On Tue, 26 May 2026 15:37:19 -0700 Jakub Kicinski wrote:
> On Tue, 26 May 2026 11:48:41 -0700 Randy Dunlap wrote:
> > WARNING: ../include/linux/netdevice.h:2622 Excess struct member 'ax25_ptr' description in 'net_device'  
> 
> I wonder how that sneaked in? ;) ;)
> 
> I'll clean this up separately, hopefully we haven't regressed too many
> things while the script was broken :(

Ugh, I'm still not seeing this on Linus's tree.
Is the fix for kernel-doc skipping such warnings on its way to Linus, 
or queue for -next?

^ permalink raw reply

* Re: [PATCH net-next 00/10] docs: net: updates for old and cobwebbed docs
From: Jakub Kicinski @ 2026-05-26 22:37 UTC (permalink / raw)
  To: Randy Dunlap
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, corbet,
	vladimir.oltean, willemb, sdf.kernel, ecree.xilinx,
	jesse.brandeburg, linux-doc
In-Reply-To: <1c341b25-e720-4f63-9db1-99348c8ebed9@infradead.org>

On Tue, 26 May 2026 11:48:41 -0700 Randy Dunlap wrote:
> WARNING: ../include/linux/netdevice.h:2622 Excess struct member 'ax25_ptr' description in 'net_device'

I wonder how that sneaked in? ;) ;)

I'll clean this up separately, hopefully we haven't regressed too many
things while the script was broken :(

^ permalink raw reply

* Re: [PATCH net-next 06/10] docs: net: refresh netdev feature guidance
From: Jakub Kicinski @ 2026-05-26 22:35 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, corbet,
	vladimir.oltean, willemb, sdf.kernel, ecree.xilinx,
	jesse.brandeburg, linux-doc
In-Reply-To: <422219d6-8377-423c-8ccd-641b2fccaa1a@bootlin.com>

On Tue, 26 May 2026 20:41:10 +0200 Maxime Chevallier wrote:
> >   
> >    1. netdev->hw_features set contains features whose state may possibly
> >       be changed (enabled or disabled) for a particular device by user's
> > -    request.  This set should be initialized in ndo_init callback and not
> > -    changed later.
> > +    request.  Drivers normally initialize this set before registration or
> > +    in the ndo_init callback. Changes after registration should be made
> > +    very carefully as other parts of the code may assume hw_features are
> > +    static. At the very least changes must be made under rtnl_lock and
> > +    the netdev instance lock, and followed by netdev_update_features().  
> Feel free to keep this description as-is, but can we get somewhere the
> actual meaning of "hw" in "hw_features" ? I've seen this cause confusion
> before as this is sometimes wrongly interpreted as "Hardware features",
> which isn't correct as the hardware may do stuff without allowing users
> to change that behaviour.
> 
> I vaguely recall something along the lines of "Host-Writeable features",
> but I am not sure at all about that...

Hm. I assumed the hw in hw_features stands for hardware.
The magic behavior of host controllable vs hardwired was
probably added later without renaming the field.

As you indicate the usual confusion is that it's legal to have 
a feature in ->features which is not set in ->hw_features which 
means that features is hardwired "on", it can't be disabled by
the user.

The current text does say this: "features whose state may [..]
be changed [..] by user's request". But perhaps it's not emphatic
enough.

Main question is whether this series should be clarifying
this or our criteria is that the series doesn't _add_ confusion,
even if it doesn't clarify all the potential confusion points? :)

^ permalink raw reply

* Re: [PATCH net-next 04/10] docs: net: update devmem code examples
From: Stanislav Fomichev @ 2026-05-26 22:17 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, corbet,
	vladimir.oltean, willemb, ecree.xilinx, jesse.brandeburg,
	linux-doc
In-Reply-To: <20260526160151.2793354-5-kuba@kernel.org>

On 05/26, Jakub Kicinski wrote:
> Update the code examples
>  - update the YNL sample with the latest(?) APIs
>  - struct dmabuf_tx_cmsg does not exist, use __u32 directly
> 
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

(wondering if a better strategy is to add links to ncdevmem code)

^ permalink raw reply

* Re: [PATCH net-next 01/10] docs: net: netdevices: small fixes and clarifications
From: Stanislav Fomichev @ 2026-05-26 22:12 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, corbet,
	vladimir.oltean, willemb, ecree.xilinx, jesse.brandeburg,
	linux-doc
In-Reply-To: <20260526160151.2793354-2-kuba@kernel.org>

On 05/26, Jakub Kicinski wrote:
> A handful of unrelated nits:
> 
>  - free_netdevice() does not exist; replace two stray references
>    with free_netdev().
>  - The simple-driver probe example fell through into err_undo after
>    register_netdev() success; add return 0 for clarity.
>  - Clarify the netdev_priv() paragraph: "(netdev_priv())" was easy
>    to misread as the thing that needs explicit freeing; spell out
>    that it refers to extra pointers stored in the device private
>    struct.
>  - ndo_setup_tc synchronization note: TC_SETUP_BLOCK / TC_SETUP_FT
>    actually run under block->cb_lock, not "NFT locks", and rtnl_lock
>    may or may not be held depending on path.
>  - ->lltx guidance reads as very outdated, it's not really deprecated.
>    I suspect people may have been trying to use it for HW drivers
>    in the past but I can't think of such a case in the last decade.
> 
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
>  Documentation/networking/netdevices.rst | 31 ++++++++++++++-----------
>  1 file changed, 17 insertions(+), 14 deletions(-)
> 
> diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst
> index 93e06e8d51a9..60492d4df2ee 100644
> --- a/Documentation/networking/netdevices.rst
> +++ b/Documentation/networking/netdevices.rst
> @@ -21,13 +21,14 @@ by free_netdev(). This is required to handle the pathological case cleanly
>  alloc_netdev_mqs() / alloc_netdev() reserve extra space for driver
>  private data which gets freed when the network device is freed. If
>  separately allocated data is attached to the network device
> -(netdev_priv()) then it is up to the module exit handler to free that.
> +(extra pointers stored in the device private struct) then it is up
> +to the module exit handler to free that.
>  
>  There are two groups of APIs for registering struct net_device.
>  First group can be used in normal contexts where ``rtnl_lock`` is not already
>  held: register_netdev(), unregister_netdev().
>  Second group can be used when ``rtnl_lock`` is already held:
> -register_netdevice(), unregister_netdevice(), free_netdevice().
> +register_netdevice(), unregister_netdevice(), free_netdev().
>  
>  Simple drivers
>  --------------
> @@ -58,6 +59,7 @@ In that case the struct net_device registration is done using
>        goto err_undo;
>  
>      /* net_device is visible to the user! */
> +    return 0;
>  
>    err_undo:
>      /* ... undo the device setup ... */
> @@ -73,7 +75,7 @@ In that case the struct net_device registration is done using
>  
>  Note that after calling register_netdev() the device is visible in the system.
>  Users can open it and start sending / receiving traffic immediately,
> -or run any other callback, so all initialization must be done prior to
> +or run any other callback, so all initialization must be **complete** prior to
>  registration.
>  
>  unregister_netdev() closes the device and waits for all users to be done
> @@ -157,7 +159,7 @@ register_netdevice() fails. The callback may be invoked with or without
>  There is no explicit constructor callback, driver "constructs" the private
>  netdev state after allocating it and before registration.
>  
> -Setting struct net_device.needs_free_netdev makes core call free_netdevice()
> +Setting struct net_device.needs_free_netdev makes core call free_netdev()
>  automatically after unregister_netdevice() when all references to the device
>  are gone. It only takes effect after a successful call to register_netdevice()
>  so if register_netdevice() fails driver is responsible for calling
> @@ -256,7 +258,7 @@ struct net_device synchronization rules
>  	lock if the driver implements queue management or shaper API.
>  	Context: process
>  
> -ndo_get_stats:
> +ndo_get_stats / ndo_get_stats64:
>  	Synchronization: RCU (can be called concurrently with the stats
>  	update path).
>  	Context: atomic (can't sleep under RCU)
> @@ -264,12 +266,9 @@ struct net_device synchronization rules
>  ndo_start_xmit:
>  	Synchronization: __netif_tx_lock spinlock.
>  
> -	When the driver sets dev->lltx this will be
> -	called without holding netif_tx_lock. In this case the driver
> -	has to lock by itself when needed.
> -	The locking there should also properly protect against
> -	set_rx_mode. WARNING: use of dev->lltx is deprecated.
> -	Don't use it for new drivers.
> +	When the driver sets dev->lltx this will be called without holding
> +	netif_tx_lock. dev->lltx is meant for software drivers only, since
> +	they often have no per-queue state.
>  
>  	Context: Process with BHs disabled or BH (timer),
>  		 will be called with interrupts disabled by netconsole.
> @@ -304,11 +303,15 @@ struct net_device synchronization rules
>  	lock if the driver implements queue management or shaper API.
>  

[..]

>  ndo_setup_tc:
> -	``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` are running under NFT locks
> -	(i.e. no ``rtnl_lock`` and no device instance lock). The rest of
> -	``tc_setup_type`` types run under netdev instance lock if the driver
> +	Locking depends on ``tc_setup_type``. For most types the callback
> +	is invoked under ``rtnl_lock`` and netdev instance lock if the driver
>  	implements queue management or shaper API.
>  
> +	For ``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` ``rtnl_lock`` may or
> +	may not be held, and the netdev instance lock is not held.
> +	``TC_SETUP_BLOCK`` runs under ``block->cb_lock`` and ``TC_SETUP_FT``
> +	runs under ``flowtable->flow_block_lock``.
> +
>  Most ndo callbacks not specified in the list above are running
>  under ``rtnl_lock``. In addition, netdev instance lock is taken as well if
>  the driver implements queue management or shaper API.

LGTM!

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* [PATCH net-next v2 10/10] net: ethernet: adi: Add a driver for the ADIN1140 MACPHY
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

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>
+
+#define ADIN1140_CONFIG2_FWD_UNK2HOST	BIT(2)
+
+#define ADIN1140_MAC_P1_LOOP_ADDR_REG	OA_TC6_MMS_REG(0x1, 0xC4)
+
+#define ADIN1140_MAC_ADDR_FILT_UPR_REG		OA_TC6_MMS_REG(0x1, 0x50)
+#define ADIN1140_MAC_ADDR_FILT_APPLY2PORT1	BIT(30)
+#define ADIN1140_MAC_ADDR_FILT_TO_HOST		BIT(16)
+
+#define ADIN1140_MAC_ADDR_FILT_LWR_REG		OA_TC6_MMS_REG(0x1, 0x51)
+
+#define ADIN1140_MAC_ADDR_MASK_UPR_REG	OA_TC6_MMS_REG(0x1, 0x70)
+#define ADIN1140_MAC_ADDR_MASK_LWR_REG	OA_TC6_MMS_REG(0x1, 0x71)
+
+#define ADIN1140_MAC_FILT_MC_SLOT	0U
+#define ADIN1140_MAC_FILT_BC_SLOT	1U
+#define ADIN1140_MAC_FILT_UC_SLOT	2U
+#define ADIN1140_MAC_FILT_MAX_SLOT	16U
+
+#define ADIN1140_RX_FRAME_CNT		OA_TC6_MMS_REG(0x1, 0xA1)
+#define ADIN1140_RX_BC_FRAME_CNT	OA_TC6_MMS_REG(0x1, 0xA2)
+#define ADIN1140_RX_MC_FRAME_CNT	OA_TC6_MMS_REG(0x1, 0xA3)
+#define ADIN1140_RX_UC_FRAME_CNT	OA_TC6_MMS_REG(0x1, 0xA4)
+#define ADIN1140_RX_CRC_ERR_CNT		OA_TC6_MMS_REG(0x1, 0xA5)
+#define ADIN1140_RX_ALIGN_ERR_CNT	OA_TC6_MMS_REG(0x1, 0xA6)
+#define ADIN1140_RX_PREAMBLE_ERR_CNT	OA_TC6_MMS_REG(0x1, 0xA7)
+#define ADIN1140_RX_SHORT_ERR_CNT	OA_TC6_MMS_REG(0x1, 0xA8)
+#define ADIN1140_RX_LONG_ERR_CNT	OA_TC6_MMS_REG(0x1, 0xA9)
+#define ADIN1140_RX_PHY_ERR_CNT		OA_TC6_MMS_REG(0x1, 0xAA)
+#define ADIN1140_RX_DRP_FULL_CNT	OA_TC6_MMS_REG(0x1, 0xAB)
+#define ADIN1140_RX_DRP_FILTER_CNT	OA_TC6_MMS_REG(0x1, 0xAD)
+#define ADIN1140_RX_IFG_ERR_CNT		OA_TC6_MMS_REG(0x1, 0xAE)
+#define ADIN1140_TX_FRAME_CNT		OA_TC6_MMS_REG(0x1, 0xB1)
+#define ADIN1140_TX_BC_FRAME_CNT	OA_TC6_MMS_REG(0x1, 0xB2)
+#define ADIN1140_TX_MC_FRAME_CNT	OA_TC6_MMS_REG(0x1, 0xB3)
+#define ADIN1140_TX_UC_FRAME_CNT	OA_TC6_MMS_REG(0x1, 0xB4)
+#define ADIN1140_TX_SINGLE_COL_CNT	OA_TC6_MMS_REG(0x1, 0xB5)
+#define ADIN1140_TX_MULTI_COL_CNT	OA_TC6_MMS_REG(0x1, 0xB6)
+#define ADIN1140_TX_DEFERRED_CNT	OA_TC6_MMS_REG(0x1, 0xB7)
+#define ADIN1140_TX_LATE_COL_CNT	OA_TC6_MMS_REG(0x1, 0xB8)
+#define ADIN1140_TX_EXCESS_COL_CNT	OA_TC6_MMS_REG(0x1, 0xB9)
+#define ADIN1140_TX_UNDERRUN_CNT	OA_TC6_MMS_REG(0x1, 0xBA)
+
+/* ADIN1140_MAC_FILT_MAX_SLOT - 3 (multicast, broadcast and unicast
+ * reserved slots)
+ */
+#define ADIN1140_MAC_FILT_AVAIL	13U
+
+#define ADIN1140_PHY_CTRL_DEFAULT	0x1000
+#define ADIN1140_PHY_STATUS_DEFAULT	0x082D
+#define ADIN1140_PHY_ID1		0x0283
+#define ADIN1140_PHY_ID2		0xBE00
+
+#define ADIN1140_STATS_CHECK_DELAY	(3 * HZ)
+
+enum adin1140_statistics_entry {
+	rx_frames,
+	rx_bc_frames,
+	rx_mc_frames,
+	rx_uc_frames,
+	rx_crc_errors,
+	rx_align_errors,
+	rx_preamble_errors,
+	rx_short_frame_errors,
+	rx_long_frame_errors,
+	rx_phy_errors,
+	rx_fifo_full_dropped,
+	rx_addr_filter_dropped,
+	rx_ifg_errors,
+	tx_frames,
+	tx_bc_frames,
+	tx_mc_frames,
+	tx_uc_frames,
+	tx_single_collision,
+	tx_multi_collision,
+	tx_deferred,
+	tx_late_collision,
+	tx_excess_collision,
+	tx_underrun,
+	ADIN1140_STATS_CNT,
+};
+
+struct adin1140_statistics_reg {
+	const char *name;
+	enum adin1140_statistics_entry idx;
+};
+
+struct adin1140_priv {
+	struct net_device *netdev;
+	struct oa_tc6 *tc6;
+	struct mii_bus *mdiobus;
+	struct phy_device *phydev;
+	struct work_struct rx_mode_work;
+	struct delayed_work stats_work;
+
+	/* Protects stats[] from concurrent updates in adin1140_stats_work
+	 * and reads in the get_stats functions
+	 */
+	spinlock_t stat_lock;
+	u64 stats[ADIN1140_STATS_CNT];
+};
+
+static const u32 adin1140_stat_regs[] = {
+	[rx_frames] = ADIN1140_RX_FRAME_CNT,
+	[rx_bc_frames] = ADIN1140_RX_BC_FRAME_CNT,
+	[rx_mc_frames] = ADIN1140_RX_MC_FRAME_CNT,
+	[rx_uc_frames] = ADIN1140_RX_UC_FRAME_CNT,
+	[rx_crc_errors] = ADIN1140_RX_CRC_ERR_CNT,
+	[rx_align_errors] = ADIN1140_RX_ALIGN_ERR_CNT,
+	[rx_preamble_errors] = ADIN1140_RX_PREAMBLE_ERR_CNT,
+	[rx_short_frame_errors]	= ADIN1140_RX_SHORT_ERR_CNT,
+	[rx_long_frame_errors] = ADIN1140_RX_LONG_ERR_CNT,
+	[rx_phy_errors] = ADIN1140_RX_PHY_ERR_CNT,
+	[rx_fifo_full_dropped] = ADIN1140_RX_DRP_FULL_CNT,
+	[rx_addr_filter_dropped] = ADIN1140_RX_DRP_FILTER_CNT,
+	[rx_ifg_errors] = ADIN1140_RX_IFG_ERR_CNT,
+	[tx_frames] = ADIN1140_TX_FRAME_CNT,
+	[tx_bc_frames] = ADIN1140_TX_BC_FRAME_CNT,
+	[tx_mc_frames] = ADIN1140_TX_MC_FRAME_CNT,
+	[tx_uc_frames] = ADIN1140_TX_UC_FRAME_CNT,
+	[tx_single_collision] = ADIN1140_TX_SINGLE_COL_CNT,
+	[tx_multi_collision] = ADIN1140_TX_MULTI_COL_CNT,
+	[tx_deferred] = ADIN1140_TX_DEFERRED_CNT,
+	[tx_late_collision] = ADIN1140_TX_LATE_COL_CNT,
+	[tx_excess_collision] = ADIN1140_TX_EXCESS_COL_CNT,
+	[tx_underrun] = ADIN1140_TX_UNDERRUN_CNT,
+};
+
+static const struct adin1140_statistics_reg adin1140_stats[] = {
+	{.name = "rx_unicast_frames", .idx = rx_uc_frames},
+	{.name = "rx_preamble_errors", .idx = rx_preamble_errors},
+	{.name = "rx_ifg_errors", .idx = rx_ifg_errors},
+	{.name = "rx_addr_filter_dropped", .idx = rx_addr_filter_dropped},
+	{.name = "tx_unicast_frames", .idx = tx_uc_frames},
+};
+
+static int adin1140_mac_filter_set(struct adin1140_priv *priv,
+				   const u8 *addr, const u8 *mask,
+				   u8 slot)
+{
+	u32 mask_reg;
+	u32 val;
+	int ret;
+
+	if (slot >= ADIN1140_MAC_FILT_MAX_SLOT)
+		return -ENOSPC;
+
+	ret = oa_tc6_write_register(priv->tc6,
+				    ADIN1140_MAC_ADDR_FILT_UPR_REG + 2 * slot,
+				    get_unaligned_be16(&addr[0]) |
+				    ADIN1140_MAC_ADDR_FILT_APPLY2PORT1 |
+				    ADIN1140_MAC_ADDR_FILT_TO_HOST);
+	if (ret)
+		return ret;
+
+	ret = oa_tc6_write_register(priv->tc6,
+				    ADIN1140_MAC_ADDR_FILT_LWR_REG + 2 * slot,
+				    get_unaligned_be32(&addr[2]));
+	if (ret)
+		return ret;
+
+	val = get_unaligned_be16(&mask[0]);
+	mask_reg = ADIN1140_MAC_ADDR_MASK_UPR_REG + (2 * slot);
+
+	ret = oa_tc6_write_register(priv->tc6, mask_reg, val);
+	if (ret)
+		return ret;
+
+	val = get_unaligned_be32(&mask[2]);
+	mask_reg = ADIN1140_MAC_ADDR_MASK_LWR_REG + (2 * slot);
+
+	return oa_tc6_write_register(priv->tc6, mask_reg, val);
+}
+
+static int adin1140_mac_filter_clear(struct adin1140_priv *priv, u8 slot)
+{
+	u8 mask[ETH_ALEN];
+	u8 addr[ETH_ALEN];
+
+	memset(mask, 0xFF, ETH_ALEN);
+	memset(addr, 0x0, ETH_ALEN);
+
+	return adin1140_mac_filter_set(priv, addr, mask, slot);
+}
+
+static int adin1140_filter_unicast(struct adin1140_priv *priv)
+{
+	u8 mask[ETH_ALEN];
+
+	memset(mask, 0xFF, ETH_ALEN);
+
+	return adin1140_mac_filter_set(priv, priv->netdev->dev_addr, mask,
+				       ADIN1140_MAC_FILT_UC_SLOT);
+}
+
+static int adin1140_filter_all_multicast(struct adin1140_priv *priv, bool en)
+{
+	u8 multicast_addr[ETH_ALEN] = {1, 0, 0, 0, 0, 0};
+
+	if (en)
+		return adin1140_mac_filter_set(priv, multicast_addr,
+					       multicast_addr,
+					       ADIN1140_MAC_FILT_MC_SLOT);
+
+	return adin1140_mac_filter_clear(priv, ADIN1140_MAC_FILT_MC_SLOT);
+}
+
+static int adin1140_filter_broadcast(struct adin1140_priv *priv, bool enabled)
+{
+	u8 mask[ETH_ALEN];
+
+	if (enabled) {
+		memset(mask, 0xFF, ETH_ALEN);
+		return adin1140_mac_filter_set(priv, mask, mask,
+					       ADIN1140_MAC_FILT_BC_SLOT);
+	}
+
+	return adin1140_mac_filter_clear(priv, ADIN1140_MAC_FILT_BC_SLOT);
+}
+
+static int adin1140_default_filter_config(struct adin1140_priv *priv)
+{
+	int ret;
+
+	ret = adin1140_filter_broadcast(priv, true);
+	if (ret)
+		return ret;
+
+	return adin1140_filter_unicast(priv);
+}
+
+static int adin1140_promiscuous_mode(struct adin1140_priv *priv, bool enabled)
+{
+	int ret;
+	u32 val;
+
+	ret = oa_tc6_read_register(priv->tc6, OA_TC6_REG_CONFIG2, &val);
+	if (ret)
+		return ret;
+
+	if (enabled)
+		val |= ADIN1140_CONFIG2_FWD_UNK2HOST;
+	else
+		val &= ~ADIN1140_CONFIG2_FWD_UNK2HOST;
+
+	return oa_tc6_write_register(priv->tc6, OA_TC6_REG_CONFIG2, val);
+}
+
+static void adin1140_rx_mode_work(struct work_struct *work)
+{
+	struct adin1140_priv *priv = container_of(work, struct adin1140_priv,
+						  rx_mode_work);
+	struct netdev_hw_addr *ha;
+	bool all_multi, promisc;
+	u8 mask[ETH_ALEN];
+	u8 start, end;
+	u32 mac_addrs;
+	u8 slot, i;
+	int ret;
+
+	/* The ADIN1140 has 16 dest MAC address filter slots:
+	 * 0 - reserved for all multicast filter.
+	 * 1 - reserved for broadcast filter.
+	 * 2 - reserved for the device's own unicast MAC.
+	 * 3 -> 15 - available for other unicast/multicast filters.
+	 */
+
+	mac_addrs = netdev_uc_count(priv->netdev) +
+		    netdev_mc_count(priv->netdev);
+
+	if (priv->netdev->flags & IFF_PROMISC) {
+		promisc = true;
+		all_multi = false;
+	} else if (priv->netdev->flags & IFF_ALLMULTI) {
+		promisc = false;
+		all_multi = true;
+	} else if (mac_addrs <= ADIN1140_MAC_FILT_AVAIL) {
+		promisc = false;
+		all_multi = false;
+
+		slot = ADIN1140_MAC_FILT_UC_SLOT + 1;
+		memset(mask, 0xFF, ETH_ALEN);
+
+		netdev_for_each_uc_addr(ha, priv->netdev) {
+			ret = adin1140_mac_filter_set(priv, ha->addr, mask,
+						      slot);
+			if (ret)
+				return;
+
+			slot++;
+		}
+
+		netdev_for_each_mc_addr(ha, priv->netdev) {
+			ret = adin1140_mac_filter_set(priv, ha->addr, mask,
+						      slot);
+			if (ret)
+				return;
+
+			slot++;
+		}
+	} else {
+		/* The filter table is full. Enable promisc mode. */
+		promisc = true;
+		all_multi = false;
+
+		start = ADIN1140_MAC_FILT_UC_SLOT + 1;
+		end = ADIN1140_MAC_FILT_MAX_SLOT;
+		for (i = start; i < end; i++) {
+			ret = adin1140_mac_filter_clear(priv, i);
+			if (ret)
+				return;
+		}
+	}
+
+	ret = adin1140_promiscuous_mode(priv, promisc);
+	if (ret)
+		return;
+
+	adin1140_filter_all_multicast(priv, all_multi);
+}
+
+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);
+	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;
+
+	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);
+		goto phy_exit;
+	}
+
+	return 0;
+
+phy_exit:
+	adin1140_phy_remove(priv);
+oa_tc6_exit:
+	oa_tc6_exit(priv->tc6);
+netdev_free:
+	free_netdev(priv->netdev);
+
+	return ret;
+}
+
+static void adin1140_remove(struct spi_device *spi)
+{
+	struct adin1140_priv *priv = spi_get_drvdata(spi);
+
+	cancel_work_sync(&priv->rx_mode_work);
+	unregister_netdev(priv->netdev);
+	adin1140_phy_remove(priv);
+	oa_tc6_exit(priv->tc6);
+	free_netdev(priv->netdev);
+}
+
+static const struct spi_device_id adin1140_spi_id[] = {
+	{ .name = "ad3306" },
+	{ .name = "adin1140" },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, adin1140_spi_id);
+
+static const struct of_device_id adin1140_match_table[] = {
+	{ .compatible = "adi,ad3306" },
+	{ .compatible = "adi,adin1140" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adin1140_match_table);
+
+static struct spi_driver adin1140_driver = {
+	.driver = {
+		.name = "adin1140",
+		.of_match_table = adin1140_match_table,
+	 },
+	.probe = adin1140_probe,
+	.remove = adin1140_remove,
+	.id_table = adin1140_spi_id,
+};
+module_spi_driver(adin1140_driver);
+
+MODULE_DESCRIPTION("Analog Devices, Inc. ADIN1140 10BASE-T1S MAC-PHY");
+MODULE_AUTHOR("Ciprian Regus <ciprian.regus@analog.com>");
+MODULE_LICENSE("GPL");

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 09/10] net: phy: Add support for the ADIN1140 PHY
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Add a driver for the ADIN1140's internal 10BASE-T1S PHY. The device
doesn't implement autonegotiation, so the link is always reported as
being up.

The device implements both C22 and C45 MDIO access methods, but can only
be discovered over C22, since the C45 MMD devices lack the MDIO_DEVID1 and
MDIO_DEVID2 registers. The indirect C45 over C22 feature is not
supported.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - No longer setting PHY_MAC_INTERRUPT in order to avoid state polling.
 - Replace the driver specific .read/write_mmd() functions with the ones
   exported from genphy.
 - Renamed the file to adin1140-phy.c in order to avoid module name
   conflicts with the adin1140 ethernet driver.
---
 MAINTAINERS                    |  7 ++++
 drivers/net/phy/Kconfig        |  6 ++++
 drivers/net/phy/Makefile       |  1 +
 drivers/net/phy/adin1140-phy.c | 72 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ca6c7425b45f..eda74f3154dc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1857,6 +1857,13 @@ S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	drivers/dma/dma-axi-dmac.c
 
+ANALOG DEVICES INC ETHERNET PHY DRIVERS
+M:	Ciprian Regus <ciprian.regus@analog.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+W:	https://ez.analog.com/linux-software-drivers
+F:	drivers/net/phy/adin1140-phy.c
+
 ANALOG DEVICES INC IIO DRIVERS
 M:	Lars-Peter Clausen <lars@metafoo.de>
 M:	Michael Hennerich <Michael.Hennerich@analog.com>
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 8b51bdc2e945..bd21a5dad366 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -124,6 +124,12 @@ config ADIN1100_PHY
 	  Currently supports the:
 	  - ADIN1100 - Robust,Industrial, Low Power 10BASE-T1L Ethernet PHY
 
+config ADIN1140_PHY
+	tristate "Analog Devices ADIN1140 10BASE-T1S PHY"
+	help
+	  Adds support for the Analog Devices, Inc. ADIN1140's internal
+	  10BASE-T1S PHY.
+
 config AMCC_QT2025_PHY
 	tristate "AMCC QT2025 PHY"
 	depends on RUST_PHYLIB_ABSTRACTIONS
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 05e4878af27a..73152845b0b2 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -29,6 +29,7 @@ obj-y				+= $(sfp-obj-y) $(sfp-obj-m)
 
 obj-$(CONFIG_ADIN_PHY)		+= adin.o
 obj-$(CONFIG_ADIN1100_PHY)	+= adin1100.o
+obj-$(CONFIG_ADIN1140_PHY)	+= adin1140-phy.o
 obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
 obj-$(CONFIG_AMD_PHY)		+= amd.o
 obj-$(CONFIG_AMCC_QT2025_PHY)	+= qt2025.o
diff --git a/drivers/net/phy/adin1140-phy.c b/drivers/net/phy/adin1140-phy.c
new file mode 100644
index 000000000000..d35da4ad680d
--- /dev/null
+++ b/drivers/net/phy/adin1140-phy.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Analog Devices, Inc. ADIN1140 10BASE-T1S PHY
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#define ADIN1140_PHY_ID		0x0283be00
+
+#define ADIN1140_PCS_CTRL		0x08f3
+#define ADIN1140_PCS_CTRL_LOOPBACK	BIT(14)
+
+static int adin1140_config_aneg(struct phy_device *phydev)
+{
+	/* phylib tries to clear BIT(12) in MDIO_CTRL1, since AN is disabled.
+	 * However, on the ADIN1140, that field is non-standard, being used
+	 * to control the reset status of the PHY (thus it needs to remain set).
+	 */
+	return 0;
+}
+
+static int adin1140_loopback(struct phy_device *phydev, bool enable, int speed)
+{
+	if (enable && speed)
+		return -EOPNOTSUPP;
+
+	return phy_modify_mmd(phydev, MDIO_MMD_PCS, ADIN1140_PCS_CTRL,
+			      ADIN1140_PCS_CTRL_LOOPBACK,
+			      enable ? ADIN1140_PCS_CTRL_LOOPBACK : 0);
+}
+
+static int adin1140_read_status(struct phy_device *phydev)
+{
+	phydev->link = 1;
+	phydev->duplex = DUPLEX_HALF;
+	phydev->speed = SPEED_10;
+	phydev->autoneg = AUTONEG_DISABLE;
+
+	return 0;
+}
+
+static struct phy_driver adin1140_driver[] = {
+	{
+		PHY_ID_MATCH_EXACT(ADIN1140_PHY_ID),
+		.name = "ADIN1140_PHY",
+		.features = PHY_BASIC_T1S_P2MP_FEATURES,
+		.read_status = adin1140_read_status,
+		.config_aneg = adin1140_config_aneg,
+		.set_loopback = adin1140_loopback,
+		.read_mmd = genphy_read_mmd_c45,
+		.write_mmd = genphy_write_mmd_c45,
+		.get_plca_cfg = genphy_c45_plca_get_cfg,
+		.set_plca_cfg = genphy_c45_plca_set_cfg,
+		.get_plca_status = genphy_c45_plca_get_status,
+	},
+};
+module_phy_driver(adin1140_driver);
+
+static const struct mdio_device_id __maybe_unused adin1140_tbl[] = {
+	{ PHY_ID_MATCH_EXACT(ADIN1140_PHY_ID) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(mdio, adin1140_tbl);
+
+MODULE_DESCRIPTION("Analog Devices, Inc. ADIN1140 10BASE-T1S PHY");
+MODULE_AUTHOR("Ciprian Regus <ciprian.regus@analog.com>");
+MODULE_LICENSE("GPL");

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 08/10] net: phy: microchip-t1s: use generic C45 MMD access helpers
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Replace the driver specific lan865x_phy_read_mmd() and
lan865x_phy_write_mmd() with the shared genphy_read_mmd_c45() and
genphy_write_mmd_c45() helpers.

No functional change.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - New patch
---
 drivers/net/phy/microchip_t1s.c | 32 ++------------------------------
 1 file changed, 2 insertions(+), 30 deletions(-)

diff --git a/drivers/net/phy/microchip_t1s.c b/drivers/net/phy/microchip_t1s.c
index e601d56b2507..73c23d311d72 100644
--- a/drivers/net/phy/microchip_t1s.c
+++ b/drivers/net/phy/microchip_t1s.c
@@ -506,34 +506,6 @@ static int lan86xx_read_status(struct phy_device *phydev)
 	return 0;
 }
 
-/* OPEN Alliance 10BASE-T1x compliance MAC-PHYs will have both C22 and
- * C45 registers space. If the PHY is discovered via C22 bus protocol it assumes
- * it uses C22 protocol and always uses C22 registers indirect access to access
- * C45 registers. This is because, we don't have a clean separation between
- * C22/C45 register space and C22/C45 MDIO bus protocols. Resulting, PHY C45
- * registers direct access can't be used which can save multiple SPI bus access.
- * To support this feature, set .read_mmd/.write_mmd in the PHY driver to call
- * .read_c45/.write_c45 in the OPEN Alliance framework
- * drivers/net/ethernet/oa_tc6.c
- */
-static int lan865x_phy_read_mmd(struct phy_device *phydev, int devnum,
-				u16 regnum)
-{
-	struct mii_bus *bus = phydev->mdio.bus;
-	int addr = phydev->mdio.addr;
-
-	return __mdiobus_c45_read(bus, addr, devnum, regnum);
-}
-
-static int lan865x_phy_write_mmd(struct phy_device *phydev, int devnum,
-				 u16 regnum, u16 val)
-{
-	struct mii_bus *bus = phydev->mdio.bus;
-	int addr = phydev->mdio.addr;
-
-	return __mdiobus_c45_write(bus, addr, devnum, regnum, val);
-}
-
 static struct phy_driver microchip_t1s_driver[] = {
 	{
 		PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1),
@@ -584,8 +556,8 @@ static struct phy_driver microchip_t1s_driver[] = {
 		.features           = PHY_BASIC_T1S_P2MP_FEATURES,
 		.config_init        = lan865x_revb_config_init,
 		.read_status        = lan86xx_read_status,
-		.read_mmd           = lan865x_phy_read_mmd,
-		.write_mmd          = lan865x_phy_write_mmd,
+		.read_mmd           = genphy_read_mmd_c45,
+		.write_mmd          = genphy_write_mmd_c45,
 		.get_plca_cfg	    = genphy_c45_plca_get_cfg,
 		.set_plca_cfg	    = lan86xx_plca_set_cfg,
 		.get_plca_status    = genphy_c45_plca_get_status,

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 07/10] net: phy: add generic helpers for direct C45 MMD access
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Some PHYs support direct C45 register access but not C22 indirect MMD
access (registers 0xD and 0xE). When discovered via C22, phylib routes
MMD access through the indirect path, which won't work on these
devices.

Add genphy_read_mmd_c45() and genphy_write_mmd_c45() as read_mmd/
write_mmd callbacks that bypass the C22 indirect path and use the bus
C45 accessors directly.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - New patch
---
 drivers/net/phy/phy_device.c | 25 +++++++++++++++++++++++++
 include/linux/phy.h          |  3 +++
 2 files changed, 28 insertions(+)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 3370eb822017..d33d096e700d 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2764,6 +2764,31 @@ int genphy_read_abilities(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(genphy_read_abilities);
 
+/* Some PHYs support direct C45 register access but not C22 indirect
+ * MMD access (registers 13 and 14). When discovered via C22, phylib
+ * routes MMD access through the indirect path, which won't work on
+ * these devices. These helpers bypass indirect access and use the bus
+ * C45 accessors directly.
+ */
+int genphy_read_mmd_c45(struct phy_device *phydev, int devnum, u16 regnum)
+{
+	struct mii_bus *bus = phydev->mdio.bus;
+	int addr = phydev->mdio.addr;
+
+	return __mdiobus_c45_read(bus, addr, devnum, regnum);
+}
+EXPORT_SYMBOL(genphy_read_mmd_c45);
+
+int genphy_write_mmd_c45(struct phy_device *phydev, int devnum, u16 regnum,
+			 u16 val)
+{
+	struct mii_bus *bus = phydev->mdio.bus;
+	int addr = phydev->mdio.addr;
+
+	return __mdiobus_c45_write(bus, addr, devnum, regnum, val);
+}
+EXPORT_SYMBOL(genphy_write_mmd_c45);
+
 /* This is used for the phy device which doesn't support the MMD extended
  * register access, but it does have side effect when we are trying to access
  * the MMD register via indirect method.
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 199a7aaa341b..432d44188dbc 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2297,6 +2297,9 @@ static inline int genphy_no_config_intr(struct phy_device *phydev)
 {
 	return 0;
 }
+int genphy_read_mmd_c45(struct phy_device *phydev, int devnum, u16 regnum);
+int genphy_write_mmd_c45(struct phy_device *phydev, int devnum, u16 regnum,
+			 u16 val);
 int genphy_read_mmd_unsupported(struct phy_device *phdev, int devad,
 				u16 regnum);
 int genphy_write_mmd_unsupported(struct phy_device *phdev, int devnum,

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 05/10] net: ethernet: oa_tc6: Export standard defined registers
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Move defines for standard Open Alliance TC6 register addresses and
subfields in the oa_tc6's header and add entries for the PHYID and
CONFIG2. As such, other ethernet drivers that rely on oa_tc6 can use
them directly.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - New patch
---
 drivers/net/ethernet/oa_tc6.c | 74 +++++++++++--------------------------------
 include/linux/oa_tc6.h        | 44 +++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 56 deletions(-)

diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index 4a1fd9fd75ab..904bd790159d 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -11,45 +11,6 @@
 #include <linux/phy.h>
 #include <linux/oa_tc6.h>
 
-/* OPEN Alliance TC6 registers */
-/* Standard Capabilities Register */
-#define OA_TC6_REG_STDCAP			0x0002
-#define STDCAP_DIRECT_PHY_REG_ACCESS		BIT(8)
-
-/* Reset Control and Status Register */
-#define OA_TC6_REG_RESET			0x0003
-#define RESET_SWRESET				BIT(0)	/* Software Reset */
-
-/* Configuration Register #0 */
-#define OA_TC6_REG_CONFIG0			0x0004
-#define CONFIG0_SYNC				BIT(15)
-#define CONFIG0_ZARFE_ENABLE			BIT(12)
-#define CONFIG0_PROTE				BIT(5)
-
-/* Status Register #0 */
-#define OA_TC6_REG_STATUS0			0x0008
-#define STATUS0_RESETC				BIT(6)	/* Reset Complete */
-#define STATUS0_HEADER_ERROR			BIT(5)
-#define STATUS0_LOSS_OF_FRAME_ERROR		BIT(4)
-#define STATUS0_RX_BUFFER_OVERFLOW_ERROR	BIT(3)
-#define STATUS0_TX_PROTOCOL_ERROR		BIT(0)
-
-/* Buffer Status Register */
-#define OA_TC6_REG_BUFFER_STATUS		0x000B
-#define BUFFER_STATUS_TX_CREDITS_AVAILABLE	GENMASK(15, 8)
-#define BUFFER_STATUS_RX_CHUNKS_AVAILABLE	GENMASK(7, 0)
-
-/* Interrupt Mask Register #0 */
-#define OA_TC6_REG_INT_MASK0			0x000C
-#define INT_MASK0_HEADER_ERR_MASK		BIT(5)
-#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK	BIT(4)
-#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK	BIT(3)
-#define INT_MASK0_TX_PROTOCOL_ERR_MASK		BIT(0)
-
-/* PHY Clause 22 registers base address and mask */
-#define OA_TC6_PHY_STD_REG_ADDR_BASE		0xFF00
-#define OA_TC6_PHY_STD_REG_ADDR_MASK		0x1F
-
 /* Control command header */
 #define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL	BIT(31)
 #define OA_TC6_CTRL_HEADER_WRITE_NOT_READ	BIT(29)
@@ -445,7 +406,7 @@ static int oa_tc6_check_phy_reg_direct_access_capability(struct oa_tc6 *tc6)
 	if (ret)
 		return ret;
 
-	if (!(regval & STDCAP_DIRECT_PHY_REG_ACCESS))
+	if (!(regval & OA_TC6_STDCAP_DIRECT_PHY_REG_ACCESS))
 		return -ENODEV;
 
 	return 0;
@@ -646,7 +607,7 @@ static int oa_tc6_read_status0(struct oa_tc6 *tc6)
 
 static int oa_tc6_sw_reset_macphy(struct oa_tc6 *tc6)
 {
-	u32 regval = RESET_SWRESET;
+	u32 regval = OA_TC6_RESET_SWRESET;
 	int ret;
 
 	ret = oa_tc6_write_register(tc6, OA_TC6_REG_RESET, regval);
@@ -655,7 +616,7 @@ static int oa_tc6_sw_reset_macphy(struct oa_tc6 *tc6)
 
 	/* Poll for soft reset complete for every 1ms until 1s timeout */
 	ret = readx_poll_timeout(oa_tc6_read_status0, tc6, regval,
-				 regval & STATUS0_RESETC,
+				 regval & OA_TC6_STATUS0_RESETC,
 				 STATUS0_RESETC_POLL_DELAY,
 				 STATUS0_RESETC_POLL_TIMEOUT);
 	if (ret)
@@ -674,10 +635,10 @@ static int oa_tc6_unmask_macphy_error_interrupts(struct oa_tc6 *tc6)
 	if (ret)
 		return ret;
 
-	regval &= ~(INT_MASK0_TX_PROTOCOL_ERR_MASK |
-		    INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK |
-		    INT_MASK0_LOSS_OF_FRAME_ERR_MASK |
-		    INT_MASK0_HEADER_ERR_MASK);
+	regval &= ~(OA_TC6_INT_MASK0_TX_PROTOCOL_ERR_MASK |
+		    OA_TC6_INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK |
+		    OA_TC6_INT_MASK0_LOSS_OF_FRAME_ERR_MASK |
+		    OA_TC6_INT_MASK0_HEADER_ERR_MASK);
 
 	return oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, regval);
 }
@@ -692,7 +653,7 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)
 		return ret;
 
 	/* Enable configuration synchronization for data transfer */
-	value |= CONFIG0_SYNC;
+	value |= OA_TC6_CONFIG0_SYNC;
 
 	return oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, value);
 }
@@ -735,25 +696,25 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
 		return ret;
 	}
 
-	if (FIELD_GET(STATUS0_RX_BUFFER_OVERFLOW_ERROR, value)) {
+	if (FIELD_GET(OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR, value)) {
 		tc6->rx_buf_overflow = true;
 		oa_tc6_cleanup_ongoing_rx_skb(tc6);
 		net_err_ratelimited("%s: Receive buffer overflow error\n",
 				    tc6->netdev->name);
 		return -EAGAIN;
 	}
-	if (FIELD_GET(STATUS0_TX_PROTOCOL_ERROR, value)) {
+	if (FIELD_GET(OA_TC6_STATUS0_TX_PROTOCOL_ERROR, value)) {
 		netdev_err(tc6->netdev, "Transmit protocol error\n");
 		return -ENODEV;
 	}
 	/* TODO: Currently loss of frame and header errors are treated as
 	 * non-recoverable errors. They will be handled in the next version.
 	 */
-	if (FIELD_GET(STATUS0_LOSS_OF_FRAME_ERROR, value)) {
+	if (FIELD_GET(OA_TC6_STATUS0_LOSS_OF_FRAME_ERROR, value)) {
 		netdev_err(tc6->netdev, "Loss of frame error\n");
 		return -ENODEV;
 	}
-	if (FIELD_GET(STATUS0_HEADER_ERROR, value)) {
+	if (FIELD_GET(OA_TC6_STATUS0_HEADER_ERROR, value)) {
 		netdev_err(tc6->netdev, "Header error\n");
 		return -ENODEV;
 	}
@@ -1189,9 +1150,10 @@ static int oa_tc6_update_buffer_status_from_register(struct oa_tc6 *tc6)
 	if (ret)
 		return ret;
 
-	tc6->tx_credits = FIELD_GET(BUFFER_STATUS_TX_CREDITS_AVAILABLE, value);
-	tc6->rx_chunks_available = FIELD_GET(BUFFER_STATUS_RX_CHUNKS_AVAILABLE,
-					     value);
+	tc6->tx_credits = FIELD_GET(OA_TC6_BUFFER_STATUS_TX_CREDITS_AVAILABLE,
+				    value);
+	tc6->rx_chunks_available =
+		FIELD_GET(OA_TC6_BUFFER_STATUS_RX_CHUNKS_AVAILABLE, value);
 
 	return 0;
 }
@@ -1231,7 +1193,7 @@ int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6)
 		return ret;
 
 	/* Set Zero-Align Receive Frame Enable */
-	regval |= CONFIG0_ZARFE_ENABLE;
+	regval |= OA_TC6_CONFIG0_ZARFE_ENABLE;
 
 	return oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, regval);
 }
@@ -1279,7 +1241,7 @@ static int oa_tc6_check_ctrl_protection(struct oa_tc6 *tc6)
 	if (ret)
 		return ret;
 
-	tc6->prot_ctrl = FIELD_GET(CONFIG0_PROTE, regval);
+	tc6->prot_ctrl = FIELD_GET(OA_TC6_CONFIG0_PROTE, regval);
 
 	return 0;
 }
diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h
index 95e041d7d77b..ad6f17218603 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -10,6 +10,50 @@
 #include <linux/etherdevice.h>
 #include <linux/spi/spi.h>
 
+/* OPEN Alliance TC6 registers */
+
+#define OA_TC6_REG_PHYID			0x0001
+
+/* Standard Capabilities Register */
+#define OA_TC6_REG_STDCAP			0x0002
+#define OA_TC6_STDCAP_DIRECT_PHY_REG_ACCESS	BIT(8)
+
+/* Reset Control and Status Register */
+#define OA_TC6_REG_RESET			0x0003
+#define OA_TC6_RESET_SWRESET			BIT(0)	/* Software Reset */
+
+/* Configuration Register #0 */
+#define OA_TC6_REG_CONFIG0			0x0004
+#define OA_TC6_CONFIG0_SYNC			BIT(15)
+#define OA_TC6_CONFIG0_ZARFE_ENABLE		BIT(12)
+#define OA_TC6_CONFIG0_PROTE			BIT(5)
+
+#define OA_TC6_REG_CONFIG2			0x0006
+
+/* Status Register #0 */
+#define OA_TC6_REG_STATUS0			0x0008
+#define OA_TC6_STATUS0_RESETC			BIT(6)	/* Reset Complete */
+#define OA_TC6_STATUS0_HEADER_ERROR		BIT(5)
+#define OA_TC6_STATUS0_LOSS_OF_FRAME_ERROR	BIT(4)
+#define OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR	BIT(3)
+#define OA_TC6_STATUS0_TX_PROTOCOL_ERROR	BIT(0)
+
+/* Buffer Status Register */
+#define OA_TC6_REG_BUFFER_STATUS			0x000B
+#define OA_TC6_BUFFER_STATUS_TX_CREDITS_AVAILABLE	GENMASK(15, 8)
+#define OA_TC6_BUFFER_STATUS_RX_CHUNKS_AVAILABLE	GENMASK(7, 0)
+
+/* Interrupt Mask Register #0 */
+#define OA_TC6_REG_INT_MASK0				0x000C
+#define OA_TC6_INT_MASK0_HEADER_ERR_MASK		BIT(5)
+#define OA_TC6_INT_MASK0_LOSS_OF_FRAME_ERR_MASK		BIT(4)
+#define OA_TC6_INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK	BIT(3)
+#define OA_TC6_INT_MASK0_TX_PROTOCOL_ERR_MASK		BIT(0)
+
+/* PHY Clause 22 registers base address and mask */
+#define OA_TC6_PHY_STD_REG_ADDR_BASE		0xFF00
+#define OA_TC6_PHY_STD_REG_ADDR_MASK		0x1F
+
 struct oa_tc6;
 
 enum oa_tc6_quirk_flag {

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 06/10] net: ethernet: oa_tc6: Add MMS register formatting macro
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

The Open Alliance TC6 standard defines multiple memory maps for the
MAC-PHY's register space. These are used to separate standard, vendor
and PHY MMD specific registers. Add a macro to make it more clear which
memory map each register is part of and allow easier definition.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - New patch
---
 drivers/net/ethernet/oa_tc6.c | 4 ++--
 include/linux/oa_tc6.h        | 3 +++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index 904bd790159d..876891ca9859 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -471,7 +471,7 @@ int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum,
 	if (ret < 0)
 		return ret;
 
-	ret = oa_tc6_read_register(tc6, (ret << 16) | regnum, &regval);
+	ret = oa_tc6_read_register(tc6, OA_TC6_MMS_REG(ret, regnum), &regval);
 	if (ret)
 		return ret;
 
@@ -489,7 +489,7 @@ int oa_tc6_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
 	if (ret < 0)
 		return ret;
 
-	return oa_tc6_write_register(tc6, (ret << 16) | regnum, val);
+	return oa_tc6_write_register(tc6, OA_TC6_MMS_REG(ret, regnum), val);
 }
 EXPORT_SYMBOL_GPL(oa_tc6_mdiobus_write_c45);
 
diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h
index ad6f17218603..8b56a132c9e3 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -10,6 +10,9 @@
 #include <linux/etherdevice.h>
 #include <linux/spi/spi.h>
 
+#define OA_TC6_MMS_REG(mms, reg) \
+	((((mms) & GENMASK(3, 0)) << 16) | ((reg) & GENMASK(15, 0)))
+
 /* OPEN Alliance TC6 registers */
 
 #define OA_TC6_REG_PHYID			0x0001

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 04/10] net: ethernet: oa_tc6: Export the C45 access functions
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

The C45 access functions can still be used by some Ethernet drivers
which set the OA_TC6_BROKEN_PHY flag. Export them.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - New patch
---
 drivers/net/ethernet/oa_tc6.c | 10 ++++++----
 include/linux/oa_tc6.h        |  4 ++++
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index 7ae3639beadd..4a1fd9fd75ab 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -499,8 +499,8 @@ static int oa_tc6_get_phy_c45_mms(int devnum)
 	}
 }
 
-static int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum,
-				   int regnum)
+int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum,
+			    int regnum)
 {
 	struct oa_tc6 *tc6 = bus->priv;
 	u32 regval;
@@ -516,9 +516,10 @@ static int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum,
 
 	return regval;
 }
+EXPORT_SYMBOL_GPL(oa_tc6_mdiobus_read_c45);
 
-static int oa_tc6_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
-				    int regnum, u16 val)
+int oa_tc6_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
+			     int regnum, u16 val)
 {
 	struct oa_tc6 *tc6 = bus->priv;
 	int ret;
@@ -529,6 +530,7 @@ static int oa_tc6_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
 
 	return oa_tc6_write_register(tc6, (ret << 16) | regnum, val);
 }
+EXPORT_SYMBOL_GPL(oa_tc6_mdiobus_write_c45);
 
 static int oa_tc6_mdiobus_register(struct oa_tc6 *tc6)
 {
diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h
index 6c38bf49e2a7..95e041d7d77b 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -32,3 +32,7 @@ int oa_tc6_read_registers(struct oa_tc6 *tc6, u32 address, u32 value[],
 			  u8 length);
 netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb);
 int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6);
+int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum,
+			    int regnum);
+int oa_tc6_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
+			     int regnum, u16 val);

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 02/10] net: ethernet: oa_tc6: Handle the OA TC6 SPI protected mode
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Implement the OA TC6 standard defined protected mode for control (register
access) transactions. In addition to the current register access formats
the oa_tc6 driver handles, 1's complement values of the data field
are included (by both the host and the MACPHY) in the SPI transfer frames.
This feature acts as an integrity check.

Control write transactions look like this:

          |<- 32 bits ->|<--- data_size --->|<- 32 bits ->|
    MOSI: | ctrl header | reg write data    | ignored     |
    MISO: | (discard)   | echoed ctrl hdr   | echoed data |

    data_size (LEN = number of registers to read in a sequence):
      Unprotected: 32 x (LEN + 1) bits
      Protected:   2 x 32 x (LEN + 1) bits

Control read transaction:

          |<- 32 bits ->|<--- 32 bits --> |<- data_size ->|
    MOSI: | ctrl header | ignored ...                     |
    MISO: | (discard)   | echoed ctrl hdr | reg read data |

    data_size (LEN = number of registers to read in a sequence):
      Unprotected: 32 x (LEN + 1) bits
      Protected:   2 x 32 x (LEN + 1) bits

Register data format ("reg write data" and "reg read data"):

    Unprotected:
      | W1 (normal) | W2 (normal) | ... | Wx (normal) |

    Protected:
    | W1 (normal) | W1 (complement) | ... | Wx (normal) | Wx (complement)|

The protected mode state can be read from the bit 5 of CONFIG0 (0x4)
register, and this setting is usually only configured during the
MACPHY's reset (depending on the device it can be done by setting the
state of a pin). We can read the protected mode configuration before any
other register access and since the SPI transfer is initially sized for an
unprotected read, the MACPHY's complement words are never clocked out
and no checking is required. The data transactions (Ethernet frames)
remain unchanged.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - Updated OA_TC6_CTRL_SPI_BUF_SIZE to always alloc the control
   transaction buffer size required by the protected mode, instead of
   calling krealloc if the PROTE bit is set.
 - Formatting to fit the 80 character column limit.
---
 drivers/net/ethernet/oa_tc6.c | 93 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 76 insertions(+), 17 deletions(-)

diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index 91a906a7918a..baba5aad84df 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -24,6 +24,7 @@
 #define OA_TC6_REG_CONFIG0			0x0004
 #define CONFIG0_SYNC				BIT(15)
 #define CONFIG0_ZARFE_ENABLE			BIT(12)
+#define CONFIG0_PROTE				BIT(5)
 
 /* Status Register #0 */
 #define OA_TC6_REG_STATUS0			0x0008
@@ -87,14 +88,17 @@
 #define OA_TC6_PHY_C45_AUTO_NEG_MMS5		5	/* MMD 7 */
 #define OA_TC6_PHY_C45_POWER_UNIT_MMS6		6	/* MMD 13 */
 
+#define OA_TC6_CTRL_PROT_REPLY_SIZE		4
 #define OA_TC6_CTRL_HEADER_SIZE			4
 #define OA_TC6_CTRL_REG_VALUE_SIZE		4
 #define OA_TC6_CTRL_IGNORED_SIZE		4
 #define OA_TC6_CTRL_MAX_REGISTERS		128
-#define OA_TC6_CTRL_SPI_BUF_SIZE		(OA_TC6_CTRL_HEADER_SIZE +\
-						(OA_TC6_CTRL_MAX_REGISTERS *\
-						OA_TC6_CTRL_REG_VALUE_SIZE) +\
-						OA_TC6_CTRL_IGNORED_SIZE)
+#define OA_TC6_CTRL_SPI_BUF_SIZE	(OA_TC6_CTRL_HEADER_SIZE +\
+					(OA_TC6_CTRL_MAX_REGISTERS *\
+					(OA_TC6_CTRL_REG_VALUE_SIZE +\
+					OA_TC6_CTRL_PROT_REPLY_SIZE)) +\
+					OA_TC6_CTRL_IGNORED_SIZE)
+
 #define OA_TC6_CHUNK_PAYLOAD_SIZE		64
 #define OA_TC6_DATA_HEADER_SIZE			4
 #define OA_TC6_CHUNK_SIZE			(OA_TC6_DATA_HEADER_SIZE +\
@@ -129,6 +133,7 @@ struct oa_tc6 {
 	u8 rx_chunks_available;
 	bool rx_buf_overflow;
 	bool int_flag;
+	bool prot_ctrl;
 };
 
 enum oa_tc6_header_type {
@@ -212,25 +217,36 @@ static void oa_tc6_update_ctrl_write_data(struct oa_tc6 *tc6, u32 value[],
 {
 	__be32 *tx_buf = tc6->spi_ctrl_tx_buf + OA_TC6_CTRL_HEADER_SIZE;
 
-	for (int i = 0; i < length; i++)
+	for (int i = 0; i < length; i++) {
 		*tx_buf++ = cpu_to_be32(value[i]);
+		if (tc6->prot_ctrl)
+			*tx_buf++ = cpu_to_be32(~value[i]);
+	}
 }
 
-static u16 oa_tc6_calculate_ctrl_buf_size(u8 length)
+static u16 oa_tc6_calculate_ctrl_buf_size(u8 length, bool ctrl_prot)
 {
+	u32 reply_size = OA_TC6_CTRL_REG_VALUE_SIZE;
+
+	if (ctrl_prot)
+		reply_size += OA_TC6_CTRL_PROT_REPLY_SIZE;
+
 	/* Control command consists 4 bytes header + 4 bytes register value for
-	 * each register + 4 bytes ignored value.
+	 * each register (+ 4 bytes for the register value complement in case
+	 * protected mode is used) + 4 bytes ignored value.
 	 */
-	return OA_TC6_CTRL_HEADER_SIZE + OA_TC6_CTRL_REG_VALUE_SIZE * length +
+	return OA_TC6_CTRL_HEADER_SIZE + reply_size * length +
 	       OA_TC6_CTRL_IGNORED_SIZE;
 }
 
 static void oa_tc6_prepare_ctrl_spi_buf(struct oa_tc6 *tc6, u32 address,
 					u32 value[], u8 length,
-					enum oa_tc6_register_op reg_op)
+					enum oa_tc6_register_op reg_op,
+					u16 buf_size)
 {
 	__be32 *tx_buf = tc6->spi_ctrl_tx_buf;
 
+	memset(tx_buf, 0, buf_size);
 	*tx_buf = oa_tc6_prepare_ctrl_header(address, length, reg_op);
 
 	if (reg_op == OA_TC6_CTRL_REG_WRITE)
@@ -253,10 +269,12 @@ static int oa_tc6_check_ctrl_write_reply(struct oa_tc6 *tc6, u8 size)
 	return 0;
 }
 
-static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 size)
+static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 length)
 {
-	u32 *rx_buf = tc6->spi_ctrl_rx_buf + OA_TC6_CTRL_IGNORED_SIZE;
-	u32 *tx_buf = tc6->spi_ctrl_tx_buf;
+	__be32 *rx_buf = tc6->spi_ctrl_rx_buf + OA_TC6_CTRL_IGNORED_SIZE;
+	__be32 *tx_buf = tc6->spi_ctrl_tx_buf;
+	u32 complement;
+	u32 reply;
 
 	/* The echoed control read header must match with the one that was
 	 * transmitted.
@@ -264,6 +282,20 @@ static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 size)
 	if (*tx_buf != *rx_buf)
 		return -EPROTO;
 
+	if (tc6->prot_ctrl) {
+		/* Skip past the echoed header to the value/complement pairs */
+		rx_buf += 1;
+		for (int i = 0; i < length; i++) {
+			reply = be32_to_cpu(rx_buf[0]);
+			complement = be32_to_cpu(rx_buf[1]);
+
+			if (complement != ~reply)
+				return -EPROTO;
+
+			rx_buf += 2;
+		}
+	}
+
 	return 0;
 }
 
@@ -273,8 +305,13 @@ static void oa_tc6_copy_ctrl_read_data(struct oa_tc6 *tc6, u32 value[],
 	__be32 *rx_buf = tc6->spi_ctrl_rx_buf + OA_TC6_CTRL_IGNORED_SIZE +
 			 OA_TC6_CTRL_HEADER_SIZE;
 
-	for (int i = 0; i < length; i++)
+	for (int i = 0; i < length; i++) {
 		value[i] = be32_to_cpu(*rx_buf++);
+
+		/* skip complement word */
+		if (tc6->prot_ctrl)
+			rx_buf++;
+	}
 }
 
 static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],
@@ -283,10 +320,10 @@ static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],
 	u16 size;
 	int ret;
 
-	/* Prepare control command and copy to SPI control buffer */
-	oa_tc6_prepare_ctrl_spi_buf(tc6, address, value, length, reg_op);
+	size = oa_tc6_calculate_ctrl_buf_size(length, tc6->prot_ctrl);
 
-	size = oa_tc6_calculate_ctrl_buf_size(length);
+	/* Prepare control command and copy to SPI control buffer */
+	oa_tc6_prepare_ctrl_spi_buf(tc6, address, value, length, reg_op, size);
 
 	/* Perform SPI transfer */
 	ret = oa_tc6_spi_transfer(tc6, OA_TC6_CTRL_HEADER, size);
@@ -301,7 +338,7 @@ static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],
 		return oa_tc6_check_ctrl_write_reply(tc6, size);
 
 	/* Check echoed/received control read command reply for errors */
-	ret = oa_tc6_check_ctrl_read_reply(tc6, size);
+	ret = oa_tc6_check_ctrl_read_reply(tc6, length);
 	if (ret)
 		return ret;
 
@@ -1224,6 +1261,20 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(oa_tc6_start_xmit);
 
+static int oa_tc6_check_ctrl_protection(struct oa_tc6 *tc6)
+{
+	u32 regval;
+	int ret;
+
+	ret = oa_tc6_read_register(tc6, OA_TC6_REG_CONFIG0, &regval);
+	if (ret)
+		return ret;
+
+	tc6->prot_ctrl = FIELD_GET(CONFIG0_PROTE, regval);
+
+	return 0;
+}
+
 /**
  * oa_tc6_init - allocates and initializes oa_tc6 structure.
  * @spi: device with which data will be exchanged.
@@ -1276,6 +1327,14 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)
 	if (!tc6->spi_data_rx_buf)
 		return NULL;
 
+	/* Check the PROTE bit status so that we can reset the device */
+	ret = oa_tc6_check_ctrl_protection(tc6);
+	if (ret) {
+		dev_err(&tc6->spi->dev,
+			"Failed to check the protection mode: %d\n", ret);
+		return NULL;
+	}
+
 	ret = oa_tc6_sw_reset_macphy(tc6);
 	if (ret) {
 		dev_err(&tc6->spi->dev,

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 03/10] net: ethernet: oa_tc6: add OA_TC6_BROKEN_PHY quirk flag
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Some MAC-PHY devices need custom MDIO bus access functions to work
around hardware issues. Add the OA_TC6_BROKEN_PHY quirk flag so drivers
can opt in to skip oa_tc6's internal PHY init and manage the PHY
themselves. When the flag is set, oa_tc6 skips MDIO bus registration,
PHY discovery and PHY connection, leaving these to the driver.

Drivers that do not set the flag retain the existing behavior.

To avoid extending the oa_tc6_init() signature for each new option,
convert it to take a config struct. Update lan865x and the framework
documentation accordingly.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - Added the quirk flag field in the oa_tc6_config struct and a first
   value entry (OA_TC6_BROKEN_PHY) instead of the mii_bus struct.
---
 Documentation/networking/oa-tc6-framework.rst    |  3 +--
 drivers/net/ethernet/microchip/lan865x/lan865x.c |  6 +++++-
 drivers/net/ethernet/oa_tc6.c                    | 25 +++++++++++++++++-------
 include/linux/oa_tc6.h                           | 12 +++++++++++-
 4 files changed, 35 insertions(+), 11 deletions(-)

diff --git a/Documentation/networking/oa-tc6-framework.rst b/Documentation/networking/oa-tc6-framework.rst
index fe2aabde923a..eaa5b4b85b34 100644
--- a/Documentation/networking/oa-tc6-framework.rst
+++ b/Documentation/networking/oa-tc6-framework.rst
@@ -453,8 +453,7 @@ Device drivers API
 
 The include/linux/oa_tc6.h defines the following functions:
 
-.. c:function:: struct oa_tc6 *oa_tc6_init(struct spi_device *spi, \
-                                           struct net_device *netdev)
+.. c:function:: struct oa_tc6 *oa_tc6_init(struct oa_tc6_config *config);
 
 Initialize OA TC6 lib.
 
diff --git a/drivers/net/ethernet/microchip/lan865x/lan865x.c b/drivers/net/ethernet/microchip/lan865x/lan865x.c
index 0277d9737369..c509c8a3e321 100644
--- a/drivers/net/ethernet/microchip/lan865x/lan865x.c
+++ b/drivers/net/ethernet/microchip/lan865x/lan865x.c
@@ -332,6 +332,7 @@ static const struct net_device_ops lan865x_netdev_ops = {
 
 static int lan865x_probe(struct spi_device *spi)
 {
+	struct oa_tc6_config tc6_config = {};
 	struct net_device *netdev;
 	struct lan865x_priv *priv;
 	int ret;
@@ -346,7 +347,10 @@ static int lan865x_probe(struct spi_device *spi)
 	spi_set_drvdata(spi, priv);
 	INIT_WORK(&priv->multicast_work, lan865x_multicast_work_handler);
 
-	priv->tc6 = oa_tc6_init(spi, netdev);
+	tc6_config.spi = spi;
+	tc6_config.netdev = netdev;
+
+	priv->tc6 = oa_tc6_init(&tc6_config);
 	if (!priv->tc6) {
 		ret = -ENODEV;
 		goto free_netdev;
diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index baba5aad84df..7ae3639beadd 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -134,6 +134,7 @@ struct oa_tc6 {
 	bool rx_buf_overflow;
 	bool int_flag;
 	bool prot_ctrl;
+	enum oa_tc6_quirk_flag quirk_flags;
 };
 
 enum oa_tc6_header_type {
@@ -580,6 +581,9 @@ static int oa_tc6_phy_init(struct oa_tc6 *tc6)
 {
 	int ret;
 
+	if (tc6->quirk_flags & OA_TC6_BROKEN_PHY)
+		return 0;
+
 	ret = oa_tc6_check_phy_reg_direct_access_capability(tc6);
 	if (ret) {
 		netdev_err(tc6->netdev,
@@ -616,6 +620,9 @@ static int oa_tc6_phy_init(struct oa_tc6 *tc6)
 
 static void oa_tc6_phy_exit(struct oa_tc6 *tc6)
 {
+	if (tc6->quirk_flags & OA_TC6_BROKEN_PHY)
+		return;
+
 	phy_disconnect(tc6->phydev);
 	oa_tc6_mdiobus_unregister(tc6);
 }
@@ -1277,24 +1284,28 @@ static int oa_tc6_check_ctrl_protection(struct oa_tc6 *tc6)
 
 /**
  * oa_tc6_init - allocates and initializes oa_tc6 structure.
- * @spi: device with which data will be exchanged.
- * @netdev: network device interface structure.
+ * @config: pointer to a caller-filled structure describing the MACPHY
+ *          (SPI device, net_device, and config flags).
  *
  * Return: pointer reference to the oa_tc6 structure if the MAC-PHY
  * initialization is successful otherwise NULL.
  */
-struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)
+struct oa_tc6 *oa_tc6_init(struct oa_tc6_config *config)
 {
 	struct oa_tc6 *tc6;
 	int ret;
 
-	tc6 = devm_kzalloc(&spi->dev, sizeof(*tc6), GFP_KERNEL);
+	if (!config)
+		return NULL;
+
+	tc6 = devm_kzalloc(&config->spi->dev, sizeof(*tc6), GFP_KERNEL);
 	if (!tc6)
 		return NULL;
 
-	tc6->spi = spi;
-	tc6->netdev = netdev;
-	SET_NETDEV_DEV(netdev, &spi->dev);
+	tc6->spi = config->spi;
+	tc6->netdev = config->netdev;
+	tc6->quirk_flags = config->quirk_flags;
+	SET_NETDEV_DEV(tc6->netdev, &tc6->spi->dev);
 	mutex_init(&tc6->spi_ctrl_lock);
 	spin_lock_init(&tc6->tx_skb_lock);
 
diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h
index 15f58e3c56c7..6c38bf49e2a7 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -12,7 +12,17 @@
 
 struct oa_tc6;
 
-struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev);
+enum oa_tc6_quirk_flag {
+	OA_TC6_BROKEN_PHY = BIT(0),
+};
+
+struct oa_tc6_config {
+	struct spi_device *spi;
+	struct net_device *netdev;
+	enum oa_tc6_quirk_flag quirk_flags;
+};
+
+struct oa_tc6 *oa_tc6_init(struct oa_tc6_config *config);
 void oa_tc6_exit(struct oa_tc6 *tc6);
 int oa_tc6_write_register(struct oa_tc6 *tc6, u32 address, u32 value);
 int oa_tc6_write_registers(struct oa_tc6 *tc6, u32 address, u32 value[],

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 01/10] dt-bindings: net: Add ADIN1140
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

The ADIN1140 is a single port 10BASE-T1S Ethernet controller that
includes both the MAC and a PHY in the same package.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v2 changelog:
 - Reorder the compatible entries in the dt schema (ad3306, adin1140).
 - Removed "dt-bindings" from the commit title and message.
 - Updated the DT example to use IRQ_TYPE_LEVEL_LOW instead of
   IRQ_TYPE_EDGE_FALLING for the interrupt trigger condition.
 - "implements" -> "tries to implement" in the description.
 - Removed the MAINTAINERS entry, as it will be added in a later patch
   in the series.
 - Reordered as the first patch of the series
---
 .../devicetree/bindings/net/adi,adin1140.yaml      | 69 ++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/adi,adin1140.yaml b/Documentation/devicetree/bindings/net/adi,adin1140.yaml
new file mode 100644
index 000000000000..518ff6b36c46
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/adi,adin1140.yaml
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/adi,adin1140.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ADI ADIN1140 10BASE-T1S MAC-PHY
+
+maintainers:
+  - Ciprian Regus <ciprian.regus@analog.com>
+
+description: |
+  The ADIN1140 (also called AD3306) is a low power single port
+  10BASE-T1S MAC-PHY. It integrates an Ethernet PHY with a MAC
+  and all the associated analog circuitry.
+  The device tries to implement the Open Alliance TC6 10BASE-T1x MAC-PHY
+  Serial Interface specification and is compliant with the
+  IEEE 802.3cg-2019 Ethernet standard for 10 Mbps single pair
+  Ethernet (SPE). The device has a 4-wire SPI interface for
+  communication between the MAC and host processor.
+
+allOf:
+  - $ref: /schemas/net/ethernet-controller.yaml#
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+  compatible:
+    enum:
+      - adi,ad3306
+      - adi,adin1140
+
+  reg:
+    maxItems: 1
+
+  spi-max-frequency:
+    maximum: 25000000
+
+  interrupts:
+    maxItems: 1
+    description: Interrupt from the MAC-PHY for receive data available
+      and error conditions
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - spi-max-frequency
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        ethernet@0 {
+            compatible = "adi,adin1140";
+            reg = <0>;
+            spi-max-frequency = <23000000>;
+
+            interrupt-parent = <&gpio>;
+            interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
+
+            local-mac-address = [ 00 11 22 33 44 55 ];
+        };
+    };

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v2 00/10] net: Add ADIN1140 support
From: Ciprian Regus via B4 Relay @ 2026-05-26 21:51 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus

This series introduces support for the ADIN1140 (also called AD3306)
10BASE-T1S single port MACPHY. The device integrates the MAC and PHY in
the same package. The communication with the host CPU is done through an
SPI interface, using the Open Alliance TC6 protocol for control and data
transactions. As a result, the oa_tc6 framework is used to implement
the communication with the device (register accesses and Ethernet frame
RX/TX).

The MAC and PHY are connected internally using an MII and MDIO bus.

The PHY is a half duplex 10Mbps device, which implements both the PLCA
RS (IEEE 802.3 clause 148) and CSMA/CD methods of accessing the Ethernet
medium. The 10BASE-T1S standard allows multiple PHY devices to be
connected (in parallel) on the same single twisted pair network segment,
so PLCA can be configured in order to provide a fair access scheme to
all the nodes and reduce the jitter introduced by the unordered CSMA/CD
transmits. The PHY's internal register map can be accessed using the
direct MDIO mode of the OA TC6. The control, status, phy id 1 & 2 C22
registers are mapped to the 0xFF00 - 0xFF03 range. As for C45
addressable devices, the PHY has PCS, PMA and PLCA blocks.

The oa_tc6 framework patches are changes that would make the library
usable by the subsequent ADIN1140 MAC driver.

The protected mode patch is required because the ADIN1140 only allows
protected mode OA TC6 control transactions, which the oa_tc6 framework
doesn't currently implement.

The OA_TC6_BROKEN_PHY quirk patch is required in order to allow the MAC
driver to have 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).
   C45 accesses do not cause this issue, so we can properly implement
   them.

Even though they have different driver, the MAC one cannot function
without the PHY driver, since the PHY is not compatible with the generic
c22 driver. As such CONFIG_ADIN1140 selects CONFIG_ADIN1140_PHY.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
Changes in v2:
- Add the OA_TC6_BROKEN_PHY quirk flag to the oa_tc6 framework in order
  to allow ethernet drivers to manage their own mii_bus struct and PHY.
- Move the OA TC6 standard register definitions in the oa_tc6.h header
  so other drivers can use them. Do the same for the C45 access
  functions
- Add the genphy_read_mmd_c45 and genphy_write_mmd_c45 functions to
  genphy, which allow drivers that have been discovered over C22 to make
  direct C45 operations, without using the 0xD and 0xE register. The
  change was implemented based on the suggestion in this thread:
  https://lore.kernel.org/all/CY8PR02MB9249CDA8F2C560FDD0F662D883382@CY8PR02MB9249.namprd02.prod.outlook.com/
- Link to v1: https://lore.kernel.org/r/20260503-adin1140-driver-v1-0-dd043cdd88f0@analog.com

---
Ciprian Regus (10):
      dt-bindings: net: Add ADIN1140
      net: ethernet: oa_tc6: Handle the OA TC6 SPI protected mode
      net: ethernet: oa_tc6: add OA_TC6_BROKEN_PHY quirk flag
      net: ethernet: oa_tc6: Export the C45 access functions
      net: ethernet: oa_tc6: Export standard defined registers
      net: ethernet: oa_tc6: Add MMS register formatting macro
      net: phy: add generic helpers for direct C45 MMD access
      net: phy: microchip-t1s: use generic C45 MMD access helpers
      net: phy: Add support for the ADIN1140 PHY
      net: ethernet: adi: Add a driver for the ADIN1140 MACPHY

 .../devicetree/bindings/net/adi,adin1140.yaml      |  69 ++
 Documentation/networking/oa-tc6-framework.rst      |   3 +-
 MAINTAINERS                                        |  15 +
 drivers/net/ethernet/adi/Kconfig                   |  12 +
 drivers/net/ethernet/adi/Makefile                  |   1 +
 drivers/net/ethernet/adi/adin1140.c                | 811 +++++++++++++++++++++
 drivers/net/ethernet/microchip/lan865x/lan865x.c   |   6 +-
 drivers/net/ethernet/oa_tc6.c                      | 202 ++---
 drivers/net/phy/Kconfig                            |   6 +
 drivers/net/phy/Makefile                           |   1 +
 drivers/net/phy/adin1140-phy.c                     |  72 ++
 drivers/net/phy/microchip_t1s.c                    |  32 +-
 drivers/net/phy/phy_device.c                       |  25 +
 include/linux/oa_tc6.h                             |  63 +-
 include/linux/phy.h                                |   3 +
 15 files changed, 1203 insertions(+), 118 deletions(-)
---
base-commit: b217a5c25fe4a5fdb63f57d028a88da470601f57
change-id: 20260429-adin1140-driver-93ae0d376318

Best regards,
-- 
Ciprian Regus <ciprian.regus@analog.com>



^ permalink raw reply

* Re: [PATCH net-next v3 01/14] virtchnl: create 'include/linux/intel' and move necessary header files
From: Jacob Keller @ 2026-05-26 21:36 UTC (permalink / raw)
  To: Alexander Lobakin, Aleksandr Loktionov
  Cc: Larysa Zaremba, Tony Nguyen, davem, pabeni, edumazet,
	andrew+netdev, netdev, przemyslaw.kitszel, sridhar.samudrala,
	anjali.singhai, michal.swiatkowski, maciej.fijalkowski,
	emil.s.tantilov, madhu.chittim, joshua.a.hay,
	jayaprakash.shanmugam, jiri, horms, corbet, richardcochran,
	linux-doc, tatyana.e.nikolova, krzysztof.czurylo, jgg, leon,
	linux-rdma, Samuel Salin, NXNE CNSE OSDT ITP Upstreaming,
	Jakub Kicinski
In-Reply-To: <d3191a29-0ce8-4338-80b8-a49c53c1a472@intel.com>

On 5/26/2026 7:45 AM, Alexander Lobakin wrote:
> Re review score -- I noticed that as well and have been focusing on
> reviews more than sending patches, *but*:
> 
> To Aleksandr:
> 
> Please *stop* spamming IWL/netdev with patches. This doesn't help at all.
> Or at least do minimum 1 review per each patch you send.
> 

+1. I was actually thinking about some sort of limit on outstanding
patches/series in the Intel Wired LAN patchwork. I was noticing that we
have a lot of minor cleanups sitting in IWL queue for months while I was
covering for Tony.

> The imbalance you (not only, but mostly) create will only lead to that
> we won't be able to send anything at all.
> 
> To you and sorta everyone from Intel:
> 
> Please focus more on reviews and wait with sending stuff until Intel is
> back to the top 10 reviewers and has at least +/- neutral score.
> 

I know I've been a bit lacking in review last few months, with a lot on
my plate. I'm hoping to improve over the next release.

> Most of stuff coming from Intel right now is not critical for customers
> and users, by doing that you paralyze really important series.
> 
> Thanks,
> Olek


^ permalink raw reply

* Re: [PATCH net-next 4/4] net: make page_pool_get_stats() void
From: Nicolai Buchwitz @ 2026-05-26 19:36 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, corbet,
	tariqt, dtatulea, linux-doc, hawk, ilias.apalodimas
In-Reply-To: <20260526155722.2790742-5-kuba@kernel.org>

On 26.5.2026 17:57, Jakub Kicinski wrote:
> The kdoc for page_pool_get_stats() is missing a Returns: statement.
> Looking at this function, I have no idea what is the purpose of
> the bool it returns. My guess was that maybe the static inline
> stub returns false if CONFIG_PAGE_POOL_STATS=n but such static
> inline helper doesn't exist at all. All callers pass a pointer
> to a struct on the stack. Make this function void.
> 
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---

> [...]

Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox