* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: John Ericson @ 2026-06-09 17:27 UTC (permalink / raw)
To: Li Chen, Andy Lutomirski
Cc: Christian Brauner, Kees Cook, Al Viro, linux-fsdevel, linux-api,
LKML, linux-mm, linux-arch, linux-doc, linux-kselftest, x86,
Arnd Bergmann, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Jan Kara, Jonathan Corbet,
Shuah Khan
In-Reply-To: <19eacd64508.26b92c022125848.262962729296162879@linux.beauty>
On Tue, Jun 9, 2026, at 10:43 AM, Li Chen wrote:
> Hi Andy,
>
> ---- On Tue, 09 Jun 2026 08:01:57 +0800 Andy Lutomirski <luto@kernel.org> wrote ---
> > [...]
> >
> > After contemplating this for a bit... why pidfd? Doesn't a pidfd
> > refer to an actual process that is, or at least was, running? This
> > new thing is a process that we are contemplating spawning. I can
> > imagine that basically all pidfd APIs would be a bit confused by the
> > nonexistence of the process in question.
> >
>
> Yes, I think that is a real concern.
>
> In my current local WIP I tried to keep that distinction explicit.
> pidfd_spawn_open() returns a pidfs-backed builder fd, not a normal pidfd
> referring to a process. The builder fd is allocated as an anonymous pidfs
> file with builder-specific file operations:
>
> file = pidfs_alloc_anon_file("[pidfd_spawn]",
> &pidfd_spawn_builder_fops, builder,
> O_RDWR);
>
What does your builder fd point to, explicitly? For example in my other reply I
talked about how it was "real" process state. In my FreeBSD patch, for example,
I found there was already a status for a process "in exec", and I figured that
was clean to reuse for one of these "embryonic" processes that also hadn't
started running. I would reckon that Linux probably has some similar notions.
> and the normal pidfd helpers still reject it because it does not use the
> ordinary pidfd file operations:
>
> struct pid *pidfd_pid(const struct file *file)
> {
> if (file->f_op != &pidfs_file_operations)
> return ERR_PTR(-EBADF);
> return file_inode(file)->i_private;
> }
>
> So the current split is:
>
> builder_fd = pidfd_spawn_open(...); /* builder object */
> pidfd_config(builder_fd, ...);
> child_pidfd = pidfd_spawn_run(builder_fd, ...); /* real pidfd */
>
> Only the last fd is a normal pidfd for an actual child process. The builder
> fd is only accepted by the builder operations.
>
> This avoids having to define what waitid(P_PIDFD), pidfd_send_signal(),
> pidfd_getfd(), poll(), etc. mean before the process exists.
I wouldn't be so sure this is necessary/good. For example, I think it could
make sense to wait on a process that has yet to be started; one just waits for
both the process to start and the process to exit. Obviously a blocking syscall
in the thread that is spawning the process is not useful, but the asynchronous
poll variation seems fine.
As long as there is real process state here, it shouldn't be too hard to
implement.
> The downside is that it adds a separate open-style entry point and is less
> uniform than the pidfd_open(0, PIDFD_EMPTY) spelling Christian sketched.
I do think there is no point having two file descriptors. The file descriptor
that previously referred to the builder/embryonic process then can refer to the
real process, right?
> If people think there is a better way to represent the pre-spawn builder
> state, or if the preference is to integrate it directly into pidfd_open()
> with an explicit empty/future-pidfd state, I would be happy to discuss that.
Hope the above answers your question? I suppose my ideas lean more on the
"future" than "empty" side --- there is indeed a thread in the thread group,
with real VM/namespace/file descriptor etc. state. Moreover, state gets
initialized before the process is started, so the actual start is a pretty
lightweight step of just letting the scheduler know the now-ready process can
be scheduled. The only thing that distinguishes the embryonic process from a
real one is simply that it isn't running --- i.e. isn't (yet) available to be
scheduled --- so the pidfds holders are free to poke at its state.
Cheers,
John
^ permalink raw reply
* Re: [PATCH v5 15/19] perf test cs-etm: Remove unused Coresight workloads
From: Leo Yan @ 2026-06-09 17:22 UTC (permalink / raw)
To: James Clark
Cc: Suzuki K Poulose, Mike Leach, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Amir Ayupov, Jonathan Corbet,
Shuah Khan, Paschalis Mpeis, coresight, linux-perf-users,
linux-kernel, Arnaldo Carvalho de Melo, linux-doc
In-Reply-To: <20260609-james-cs-context-tracking-fix-v5-15-d53a7d096a19@linaro.org>
On Tue, Jun 09, 2026 at 03:40:20PM +0100, James Clark wrote:
> These are now unused and had various issues like not working with out of
> source builds and being slow to compile. Delete them.
> +There are a set of Perf tests for CoreSight which can be run with::
> +
> + sudo perf test coresight
Tested-by: Leo Yan <leo.yan@arm.com>
^ permalink raw reply
* Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices
From: Pranjal Shrivastava @ 2026-06-09 17:20 UTC (permalink / raw)
To: David Matlack
Cc: kexec, linux-doc, linux-kernel, linux-mm, linux-pci,
Adithya Jayachandran, Alexander Graf, Alex Williamson,
Bjorn Helgaas, Chris Li, David Rientjes, Jacob Pan,
Jason Gunthorpe, Jonathan Corbet, Josh Hilke, Leon Romanovsky,
Lukas Wunner, Mike Rapoport, Parav Pandit, Pasha Tatashin,
Pratyush Yadav, Saeed Mahameed, Samiullah Khawaja, Shuah Khan,
Vipin Sharma, William Tu, Yi Liu
In-Reply-To: <aic6mdiZ0qUJpFca@google.com>
On Mon, Jun 08, 2026 at 09:56:41PM +0000, David Matlack wrote:
> On 2026-06-07 08:37 PM, Pranjal Shrivastava wrote:
> > On Fri, May 22, 2026 at 08:24:06PM +0000, David Matlack wrote:
> > > Inherit Access Control Services (ACS) flags on all incoming preserved
> > > devices (endpoints and upstream bridges) during a Live Update.
> > >
> > > Inheriting ACS flags avoids changing routing rules while memory
> > > transactions are in flight from preserved devices. This is also strictly
> > > necessary to ensure that IOMMU group assignments do not change across
> > > a Live Update for preserved devices, as changing ACS configurations can
> > > split or merge IOMMU groups.
> > >
> > > Cache the inherited ACS controls established by the previous kernel in
> > > struct pci_dev so that ACS controls do not change after a reset
> > > (pci_restore_state() calls pci_enable_acs()).
> > >
> > > To simplify ACS inheritance, reject preserving any devices that require
> > > quirks to enable ACS as those quirks would also have to take Live Update
> > > into account.
> > >
> > > Signed-off-by: David Matlack <dmatlack@google.com>
> > > ---
> > > drivers/pci/liveupdate.c | 68 ++++++++++++++++++++++++++++++++++
> > > drivers/pci/liveupdate.h | 11 ++++++
> > > drivers/pci/pci.c | 5 +++
> > > drivers/pci/pci.h | 5 +++
> > > drivers/pci/quirks.c | 7 ++++
> > > include/linux/pci_liveupdate.h | 6 +++
> > > 6 files changed, 102 insertions(+)
> > >
> >
> > [...]
> >
> > >
> > > +void pci_liveupdate_init_acs(struct pci_dev *dev)
> > > +{
> > > + guard(rwsem_read)(&pci_liveupdate.rwsem);
> > > +
> > > + if (!dev->acs_cap || !dev->liveupdate.incoming)
> > > + return;
> > > +
> > > + pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);
> >
> > I might be thinking out loud here, but as an attacker, this motivates me
> > to somehow hack the EP FW to mis-report the PCI_ACS_CTRL register across
> > a liveupdate to fool the incoming kernel. If the FW feeds a 0, it silently
> > strips ACS protections.
> >
> > Should we also serialize ACS state in ser somehow to ensure we aren't
> > fooled by something like this?
>
> What does "EP FW" mean?
I was referring to the Endpoint Firmware (basically any SW running on
a downstream device)
>
> Does such an attacker even need Live Update to attack the system? It
> seems like such an attacker could route TLPs in whatever malicious way
> they want regardless of Live Update.
>
I agree that compromised PCIe devices are a menace anyway. But I was
talking about the potential window opened up by Live Update here,
suppose we have Device A & B assigned to 2 different VMs (implying they
are in separate IOMMU groups because the switch set ACS_RR = 1).
Now, the attacker has an opportunity with Liveupdate, since the devices
are already assigned, if *somehow* it flips a bit like ACS_RR, the
incoming kernel might see both the devices in the same IOMMU group.
Who detects this case and what happens if this happens if the devices
are kept assigned to these VMs?
Thanks,
Praan
^ permalink raw reply
* Re: [PATCH v5 10/19] perf test cs-etm: Test decoding for concurrent threads test
From: Leo Yan @ 2026-06-09 17:18 UTC (permalink / raw)
To: James Clark
Cc: Suzuki K Poulose, Mike Leach, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Amir Ayupov, Jonathan Corbet,
Shuah Khan, Paschalis Mpeis, coresight, linux-perf-users,
linux-kernel, Arnaldo Carvalho de Melo, linux-doc
In-Reply-To: <20260609-james-cs-context-tracking-fix-v5-10-d53a7d096a19@linaro.org>
On Tue, Jun 09, 2026 at 03:40:15PM +0100, James Clark wrote:
> The thread_loop test only looks for context IDs in the raw trace.
> There's a lot more that can go wrong when decoding these, so replace it
> with a test that looks at the final output for matching thread names and
> symbols.
>
> In the future we might use timestamps and context switch events to track
> threads, so looking at context IDs in the raw trace wouldn't always
> work.
>
> Reviewed-by: Leo Yan <leo.yan@arm.com>
> Signed-off-by: James Clark <james.clark@linaro.org>
Tested-by: Leo Yan <leo.yan@arm.com>
^ permalink raw reply
* Re: [PATCH v5 08/19] perf test cs-etm: Replace memcpy test with raw dump stress test
From: Leo Yan @ 2026-06-09 17:16 UTC (permalink / raw)
To: James Clark
Cc: Suzuki K Poulose, Mike Leach, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Amir Ayupov, Jonathan Corbet,
Shuah Khan, Paschalis Mpeis, coresight, linux-perf-users,
linux-kernel, Arnaldo Carvalho de Melo, linux-doc
In-Reply-To: <20260609-james-cs-context-tracking-fix-v5-8-d53a7d096a19@linaro.org>
On Tue, Jun 09, 2026 at 03:40:13PM +0100, James Clark wrote:
> Like asm_pure_loop, this memcpy test only checks that 10 of each of a
> few trace packet types occur after recording a lot of trace, which isn't
> more specific than other existing Coresight tests.
>
> Assume it was supposed to be a stress test for dumping and replace it
> with one that doesn't require a custom binary and checks for a specific
> amount of raw output. Don't bother checking for packets because the
> other tests that test decoding will catch issues with malformed data.
>
> This also adds coverage for exit snapshot mode which was missing.
>
> Signed-off-by: James Clark <james.clark@linaro.org>
Tested-by: Leo Yan <leo.yan@arm.com>
^ permalink raw reply
* Re: [PATCH v5 06/19] perf test cs-etm: Replace unroll loop thread with deterministic decode test
From: Leo Yan @ 2026-06-09 17:04 UTC (permalink / raw)
To: James Clark
Cc: Suzuki K Poulose, Mike Leach, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Amir Ayupov, Jonathan Corbet,
Shuah Khan, Paschalis Mpeis, coresight, linux-perf-users,
linux-kernel, Arnaldo Carvalho de Melo, linux-doc
In-Reply-To: <20260609-james-cs-context-tracking-fix-v5-6-d53a7d096a19@linaro.org>
On Tue, Jun 09, 2026 at 03:40:11PM +0100, James Clark wrote:
> Testing a long sequence without branches seems like it would be better
> as a decoder unit test, and this test doesn't test decoding either, so
> it's not clear what bugs this is trying to catch.
>
> The new deterministic workload has somewhat long sequences when built
> unoptimized, and we can always increase them later if we want to. But
> now we test that decoding always gives the same result for the same
> sequence of code which we've never had before.
>
> Signed-off-by: James Clark <james.clark@linaro.org>
Tested-by: Leo Yan <leo.yan@arm.com>
^ permalink raw reply
* Re: [PATCH v2] docs: Fix minor grammatical error
From: Randy Dunlap @ 2026-06-09 16:56 UTC (permalink / raw)
To: Brigham Campbell, Thorsten Leemhuis, Jonathan Corbet, Shuah Khan,
open list:DOCUMENTATION REPORTING ISSUES, open list
In-Reply-To: <20260609070618.12566-1-me@brighamcampbell.com>
On 6/9/26 12:06 AM, Brigham Campbell wrote:
> Fix minor grammatical error in the administration guide.
>
> Signed-off-by: Brigham Campbell <me@brighamcampbell.com>
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.
> ---
>
> Since v1:
> * Drop pedantic line re-wrapping.
>
> In hindsight, I should have guessed that reflowing the paragraph was
> overzealous. Thanks for the guidance, Randy, Thorsten. I'll remember it
> if I make minor doc fixes in the future.
>
> Documentation/admin-guide/quickly-build-trimmed-linux.rst | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/admin-guide/quickly-build-trimmed-linux.rst b/Documentation/admin-guide/quickly-build-trimmed-linux.rst
> index cb178e0a6208..3432dc8e1a85 100644
> --- a/Documentation/admin-guide/quickly-build-trimmed-linux.rst
> +++ b/Documentation/admin-guide/quickly-build-trimmed-linux.rst
> @@ -217,7 +217,7 @@ again.
>
> There is a catch: 'localmodconfig' is likely to disable kernel features you
> did not use since you booted your Linux -- like drivers for currently
> - disconnected peripherals or a virtualization software not haven't used yet.
> + disconnected peripherals or virtualization software not currently in use.
> You can reduce or nearly eliminate that risk with tricks the reference
> section outlines; but when building a kernel just for quick testing purposes
> it is often negligible if such features are missing. But you should keep that
>
> base-commit: 738bb6e6c8d992f33335b3cbcce051ab118a33dc
--
~Randy
^ permalink raw reply
* Re: [PATCH] Documentation: KVM: Synchronize x86 VM types
From: Sean Christopherson @ 2026-06-09 16:31 UTC (permalink / raw)
To: Sean Christopherson, kvm, linux-doc, linux-kernel,
Carlos López
Cc: Paolo Bonzini, Jonathan Corbet, Shuah Khan, Ashish Kalra,
Michael Roth, Brijesh Singh, Isaku Yamahata, Binbin Wu
In-Reply-To: <20260603114504.814647-2-clopez@suse.de>
On Wed, 03 Jun 2026 13:45:04 +0200, Carlos López wrote:
> KVM has reflected KVM_X86_SNP_VM to userspace since 1dfe571c12cf
> ("KVM: SEV: Add initial SEV-SNP support"), and KVM_X86_TDX_VM since
> 161d34609f9b ("KVM: TDX: Make TDX VM type supported"). Update the
> documentation to reflect this fact.
>
>
Applied to kvm-x86 misc, thanks!
[1/1] Documentation: KVM: Synchronize x86 VM types
https://github.com/kvm-x86/linux/commit/65aa483f32ec
--
https://github.com/kvm-x86/linux/tree/next
^ permalink raw reply
* htmldocs: Documentation/driver-api/dma-buf:145: ./include/linux/dma-buf.h:121: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
From: kernel test robot @ 2026-06-09 16:22 UTC (permalink / raw)
To: Zhiping Zhang; +Cc: oe-kbuild-all, 0day robot, linux-doc
tree: https://github.com/intel-lab-lkp/linux/commits/Zhiping-Zhang/net-mlx5-free-mlx5_st_idx_data-on-final-dealloc/20260609-033856
head: 17018c9e3c4f48eeb0404a61fd0cf942eaaddbb8
commit: 90816fc011a0d8855056adba3208381d2129d29f dma-buf: add optional get_tph() callback
date: 21 hours ago
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project f43d6834093b19baf79beda8c0337ab020ac5f17)
docutils: docutils (Docutils 0.21.2, Python 3.13.5, on linux)
reproduce: (https://download.01.org/0day-ci/archive/20260609/202606091818.rizEWJZ8-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/202606091818.rizEWJZ8-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/dma-buf:145: ./include/linux/dma-buf.h:120: ERROR: Unexpected indentation. [docutils]
>> Documentation/driver-api/dma-buf:145: ./include/linux/dma-buf.h:121: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
>> Documentation/driver-api/dma-buf:145: ./include/linux/dma-buf.h:123: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils]
Documentation/driver-api/target:25: ./drivers/target/target_core_user.c:35: ERROR: Unexpected section title.
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH RFC v4 1/6] dt-bindings: iio: add Open Sensor Fusion device
From: Conor Dooley @ 2026-06-09 16:19 UTC (permalink / raw)
To: Jinseob Kim
Cc: Jonathan Cameron, linux-iio, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, devicetree, linux-kernel, linux-doc
In-Reply-To: <20260607234343.22109-2-kimjinseob88@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1733 bytes --]
Jonathan/IIO folks,
On Mon, Jun 08, 2026 at 08:43:38AM +0900, Jinseob Kim wrote:
> diff --git a/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml b/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> new file mode 100644
> index 000000000..a4049715a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> @@ -0,0 +1,43 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/opensensorfusion,osf.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Open Sensor Fusion Sensor Aggregation Hub
> +
> +maintainers:
> + - Jinseob Kim <kimjinseob88@gmail.com>
> +
> +description: |
> + Open Sensor Fusion is a sensor aggregation hub. The hub exposes an OSF
> + protocol data stream over its host interface and may report capabilities and
> + samples for multiple sensor classes. The Linux driver discovers the actual
> + sensor channels from OSF capability reports instead of describing those
> + sensors in Device Tree.
> +
> + Open Sensor Fusion is not a generic industry standard. Public project
> + documentation is available at:
> +
> + https://github.com/opensensorfusion
> +
> +allOf:
> + - $ref: /schemas/serial/serial-peripheral-props.yaml#
> +
> +properties:
> + compatible:
> + const: opensensorfusion,osf
> +
> +required:
> + - compatible
Do you think it makes sense to permit a regulator here, so that the
"host" OS can power on/off the board running the osf stack?
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + serial {
> + sensor {
> + compatible = "opensensorfusion,osf";
> + };
> + };
> +...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v9 2/6] mm/memory-failure: surface unhandlable kernel pages as -ENOTRECOVERABLE
From: Breno Leitao @ 2026-06-09 16:15 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Miaohe Lin, Andrew Morton, Lorenzo Stoakes, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Naoya Horiguchi, Jonathan Corbet, Shuah Khan, Liam R. Howlett,
lance.yang, Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
linux-mm, linux-kernel, linux-doc, linux-kselftest,
linux-trace-kernel, kernel-team
In-Reply-To: <174b8d76-5514-4942-af5d-c975ff95ee03@kernel.org>
On Tue, Jun 09, 2026 at 04:41:01PM +0200, David Hildenbrand (Arm) wrote:
> On 6/9/26 12:56, Breno Leitao wrote:
> > get_any_page() collapses every HWPoisonHandlable() rejection into a
> > single -EIO via the __get_hwpoison_page() -> -EBUSY -> shake_page()
> > -> retry path. That is correct for the transient case (a userspace
> > folio briefly off LRU during migration or compaction, which a later
> > shake can drag back), but wrong for stable kernel-owned pages: slab,
> > page-table, large-kmalloc and PG_reserved pages will never become
> > HWPoisonHandlable(), so the retry loop is wasted work and the final
> > -EIO loses the "this is structurally unrecoverable" information.
> > memory_failure() then maps -EIO into MF_MSG_GET_HWPOISON, which the
> > panic-on-unrecoverable sysctl deliberately does not act on.
> >
> > Introduce HWPoisonKernelOwned(), a small predicate that positively
> > identifies pages the hwpoison handler cannot recover from:
> >
> > HWPoisonKernelOwned(p, flags) :=
> > !(MF_SOFT_OFFLINE && page_has_movable_ops(p)) &&
> > (PageReserved(p) ||
> > PageSlab(head) || PageTable(head) || PageLargeKmalloc(head))
> >
> > where head = compound_head(p).
> >
> > PG_reserved is a per-page flag (PF_NO_COMPOUND) and is tested on the
> > page directly. The slab, page-table and large-kmalloc page-type bits
> > are only stored on the head page, so those tests resolve the compound
> > head first, then re-read compound_head(page) afterwards: a concurrent
> > split or compound free that moves head invalidates the just-read flags
> > and the loop retries. The lookup still takes no refcount, mirroring
> > the rest of get_any_page(); the recheck closes the common split race,
> > and a residual free->alloc->free in the same window can only mis-tag
> > a genuinely poisoned page, never reclassify a handlable one.
> >
> > The MF_SOFT_OFFLINE / page_has_movable_ops() opt-out mirrors the
> > same exception in HWPoisonHandlable(): soft-offline is allowed to
> > migrate movable_ops pages even though they are not on the LRU, and
> > we must not pre-empt that with an unrecoverable verdict.
> >
> > The list is intentionally not exhaustive. vmalloc and kernel-stack
> > pages, for example, do not carry a page_type bit and would need a
> > different oracle; they keep going through the existing retry path
> > unchanged. This is the smallest set we can identify with certainty
> > by page type.
> >
> > Wire the helper into the top of get_any_page() to short-circuit
> > those pages before the retry loop runs. On a hit, drop the caller's
> > MF_COUNT_INCREASED reference (if any) and return -ENOTRECOVERABLE
> > straight away. Pages outside the helper's positive list still take
> > the existing retry path and return -EIO, leaving operator-visible
> > behaviour for those cases unchanged.
> >
> > Extend the unhandlable-page pr_err() to fire for either errno and
> > update the get_hwpoison_page() kerneldoc to document the new return.
> >
> > memory_failure() still folds every negative return into
> > MF_MSG_GET_HWPOISON via its existing "else if (res < 0)" branch, so
> > this patch on its own only changes the errno that soft_offline_page()
> > can propagate to its callers. A follow-up wires -ENOTRECOVERABLE
> > through memory_failure() and reports MF_MSG_KERNEL for the
> > unrecoverable cases, which is what the
> > panic_on_unrecoverable_memory_failure sysctl observes.
> >
> > Suggested-by: David Hildenbrand <david@kernel.org>
> > Suggested-by: Lance Yang <lance.yang@linux.dev>
> > Signed-off-by: Breno Leitao <leitao@debian.org>
> > ---
> > mm/memory-failure.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++--
> > 1 file changed, 58 insertions(+), 2 deletions(-)
> >
> > diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> > index f4d3e6e20e13..eed9de387694 100644
> > --- a/mm/memory-failure.c
> > +++ b/mm/memory-failure.c
> > @@ -1325,6 +1325,46 @@ static inline bool HWPoisonHandlable(struct page *page, unsigned long flags)
> > return PageLRU(page) || is_free_buddy_page(page);
> > }
> >
> > +/*
> > + * Positive identification of pages the hwpoison handler cannot recover.
> > + * These page types are owned by kernel internals (no userspace mapping
> > + * to unmap, no file mapping to invalidate, no migration target), so the
> > + * shake_page() / retry loop in get_any_page() can never turn them into
> > + * something HWPoisonHandlable() will accept. Short-circuit them to
> > + * -ENOTRECOVERABLE so callers can panic on operator request instead of
> > + * spinning through retries that exit as a transient-looking -EIO.
> > + *
> > + * The MF_SOFT_OFFLINE / page_has_movable_ops() opt-out mirrors
> > + * HWPoisonHandlable(): soft-offline is allowed to migrate movable_ops
> > + * pages even though they are not on the LRU.
> > + */
> > +static inline bool HWPoisonKernelOwned(struct page *page, unsigned long flags)
> > +{
> > + struct page *head;
> > +
> > + if ((flags & MF_SOFT_OFFLINE) && page_has_movable_ops(page))
> > + return false;
> > +
>
> On a second look: Do we really need that? The page types below never support
> migration. So I guess that check is not required?
>
> Apart from that, looks good with two comments:
>
> a) HWPoisonKernelOwned: this is not the common style for us to name functions.
>
> is_kernel_owned_page() or sth like that would do.
Ack, I will rename it is_kernel_owned_page()
In my defence, most of the functions similar to HWPoisonKernelOwned()
has this name format, and I got this discussion earlier (with Lance?
I think). Here are the similar function names in that file:
* HWPoisonHandlable
* PageHWPoisonTakenOff()
* SetPageHWPoisonTakenOff
I will update in the new version.
> b) The function doc can likely be simplified a bit. No need to mention the
> short-circuit stuff, for example, IMHO.
Ack
Thanks for the review,
--breno
^ permalink raw reply
* Re: [PATCH v3 1/3] PM: core: Rename module parameters prefix to "power"
From: Tomasz Figa @ 2026-06-09 16:07 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Tzung-Bi Shih, Jonathan Corbet, Greg Kroah-Hartman,
Danilo Krummrich, Shuah Khan, Pavel Machek, Len Brown, linux-doc,
linux-kernel, linux-pm, driver-core, senozhatsky, Randy Dunlap
In-Reply-To: <CAJZ5v0gQTx_k9j2pGha7NKxAjdPS1J_ySZNYiL1=_+_M9znQ_g@mail.gmail.com>
On Tue, Jun 9, 2026 at 10:40 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
>
> On Tue, Jun 9, 2026 at 3:03 PM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> >
> > On Tue, Jun 09, 2026 at 08:17:03PM +0900, Tomasz Figa wrote:
> > > On Tue, Jun 9, 2026 at 8:10 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
> > > >
> > > > On Tue, Jun 9, 2026 at 11:03 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> > > > >
> > > > > On Mon, Jun 08, 2026 at 04:11:30PM +0200, Rafael J. Wysocki wrote:
> > > > > > On Mon, Jun 8, 2026 at 4:16 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> > > > > > >
> > > > > > > Currently, the module parameters defined in drivers/base/power/main.c
> > > > > > > use the default prefix "main" (derived from the filename). The prefix
> > > > > > > "main" is too generic and non-descriptive for power management
> > > > > > > parameters.
> > > > > > >
> > > > > > > Redefine MODULE_PARAM_PREFIX to "power." at the beginning of the file
> > > > > > > to group the module parameters under the "power" namespace instead.
> > > > > > > This makes the parameters more descriptive.
> > > > > > >
> > > > > > > Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
> > > > > > > ---
> > > > > > > v3:
> > > > > > > - No changes.
> > > > > > >
> > > > > > > v2: https://lore.kernel.org/all/20260604090756.2884671-2-tzungbi@kernel.org
> > > > > > > - New to the series.
> > > > > > >
> > > > > > > v1: Doesn't exist.
> > > > > > >
> > > > > > > drivers/base/power/main.c | 3 +++
> > > > > > > 1 file changed, 3 insertions(+)
> > > > > > >
> > > > > > > diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
> > > > > > > index ed48c292f575..cd864f3a2799 100644
> > > > > > > --- a/drivers/base/power/main.c
> > > > > > > +++ b/drivers/base/power/main.c
> > > > > > > @@ -40,6 +40,9 @@
> > > > > > > #include "../base.h"
> > > > > > > #include "power.h"
> > > > > > >
> > > > > > > +#undef MODULE_PARAM_PREFIX
> > > > > > > +#define MODULE_PARAM_PREFIX "power."
> > > > > >
> > > > > > "power" may be confused with the power supply support, so I'd rather
> > > > > > use "pm" or even "pm_sleep" (in which case the "dpm_" prefix could be
> > > > > > dropped from the new module param name in the next patch).
> > > > >
> > > > > Ack, will use "pm_sleep" in the next version.
> > > > >
> > > > > Regarding dropping the "dpm_" prefix, should this also apply to the existing
> > > > > dpm_watchdog_all_cpu_backtrace parameter? Or should we leave it as-is to
> > > > > avoid breaking existing configurations?
> > > >
> > > > Breaking things for someone would be unfortunate.
> > > >
> > > > For consistency, let's retain the "dpm_watchdog" part in this name and
> > > > use it in the new one.
> > >
> > > Hmm, doesn't the prefix change already break things?
> >
> > Theoretically yes, though it's worth noting that this parameter is
> > relatively new. It was introduced in v6.19 (Oct 2025) via commit
> > a67818f74512 ("PM: dpm_watchdog: add module param to backtrace all CPUs").
> >
> > To my knowledge, the flag was originally introduced for ChromeOS [1], which
> > doesn't actually rely on this module parameter in its production
> > configuration yet [2].
> >
> > That said, I can't know the status of all other Linux distributions. If we
> > want to be 100% safe against breaking existing setups, retaining the "main"
> > prefix is definitely the safest.
> >
> > Given its recent introduction and limited known usage, this might be the
> > best window to rename the prefix before it gains wider adoption.
> >
> > What are your thoughts?
>
> If somebody complains about it, we can reconsider, but using "main" as
> a prefix for power management module parameters is definitely
> confusing.
Agreed.
Best,
Tomasz
>
> > [1] https://chromium-review.git.corp.google.com/c/chromiumos/third_party/kernel/+/7414781
> > [2] https://chromium-review.git.corp.google.com/c/chromiumos/third_party/kernel/+/7414721
^ permalink raw reply
* Re: [PATCH v4 1/2] dt-bindings: iio: dac: Add AD5529R
From: Conor Dooley @ 2026-06-09 15:58 UTC (permalink / raw)
To: Janani Sunil
Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet,
Shuah Khan, linux-iio, devicetree, linux-kernel, linux-doc,
Janani Sunil
In-Reply-To: <20260609-ad5529r-driver-v4-1-2e4c02234a1a@analog.com>
[-- Attachment #1: Type: text/plain, Size: 78 bytes --]
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices
From: Pranjal Shrivastava @ 2026-06-09 15:34 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: David Matlack, kexec, linux-doc, linux-kernel, linux-mm,
linux-pci, Adithya Jayachandran, Alexander Graf, Alex Williamson,
Bjorn Helgaas, Chris Li, David Rientjes, Jacob Pan,
Jonathan Corbet, Josh Hilke, Leon Romanovsky, Lukas Wunner,
Mike Rapoport, Parav Pandit, Pasha Tatashin, Pratyush Yadav,
Saeed Mahameed, Samiullah Khawaja, Shuah Khan, Vipin Sharma,
William Tu, Yi Liu
In-Reply-To: <aigtS3UDdhUGp3m0@google.com>
On Tue, Jun 09, 2026 at 03:12:11PM +0000, Pranjal Shrivastava wrote:
> On Mon, Jun 08, 2026 at 03:16:40PM -0300, Jason Gunthorpe wrote:
> > On Mon, Jun 08, 2026 at 10:49:29AM +0000, Pranjal Shrivastava wrote:
> >
> > > My point was that a FW exploit can meddle with the bitfields of the
> > > ACS_CTRL to spoof and mis-report the ACS flags.
> >
> > Devices can also ignore the ACS flags. I don't think this is an area
> > where we should be worrying about devices being actively hostile.
>
> I'm wondering what happens if we preserve IOMMU groups across a kexec,
> but a switch's ACS capability is dropped or the ACS_RR bit gets cleared?
> The incoming kernel assumes that it's the same ACS cap from the old one
>
> Now, the incoming kernel restores the groups assuming they're still
> isolated, but the hardware no longer enforces it, silently allowing DMAs
> & breaking isolation?
Again, to clarify, I'm aware that we aren't preserving IOMMU groups,
the incoming kernel has to rebuild the groups. My concern is that if the
ACS_RR bit is cleared during the kexec window, the produced grouping would
be different than the old kernel. What happens if two devices on the
same bridge were assigned to 2 different VMs?
Thanks,
Praan
^ permalink raw reply
* Re: [PATCH v13 3/3] gpio: gpio-ltc4283: Add support for the LTC4283 Swap Controller
From: Guenter Roeck @ 2026-06-09 15:30 UTC (permalink / raw)
To: Nuno Sá
Cc: linux-gpio, linux-hwmon, devicetree, linux-doc, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Linus Walleij,
Bartosz Golaszewski
In-Reply-To: <20260502-ltc4283-support-v13-3-1c206542e652@analog.com>
On Sat, May 02, 2026 at 10:56:54AM +0100, Nuno Sá wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> The LTC4283 device has up to 8 pins that can be configured as GPIOs.
>
> Note that PGIO pins are not set as GPIOs by default so if they are
> configured to be used as GPIOs we need to make sure to initialize them
> to a sane default. They are set as inputs by default.
>
> Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Applied.
Thanks,
Guenter
^ permalink raw reply
* Re: [PATCH net-next v6 12/12] net: airoha: add phylink support
From: Lorenzo Bianconi @ 2026-06-09 15:29 UTC (permalink / raw)
To: Christian Marangi
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Simon Horman, Jonathan Corbet, Shuah Khan, Heiner Kallweit,
Russell King, Saravana Kannan, Philipp Zabel, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt, netdev, devicetree,
linux-kernel, linux-doc, linux-arm-kernel, linux-mediatek, llvm
In-Reply-To: <20260609151212.29469-13-ansuelsmth@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 9935 bytes --]
> Add phylink support for each GDM port. For GDM1 add the internal interface
> mode as the only supported mode. For GDM2/3/4 add the required
> configuration of the PCS to make the external PHY or attached SFP cage
> work.
>
> These needs to be defined in the GDM port node using the pcs-handle
> property.
>
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Hi Christian,
some nits inline.
Regards,
Lorenzo
> ---
> drivers/net/ethernet/airoha/Kconfig | 1 +
> drivers/net/ethernet/airoha/airoha_eth.c | 167 +++++++++++++++++++++-
> drivers/net/ethernet/airoha/airoha_eth.h | 3 +
> drivers/net/ethernet/airoha/airoha_regs.h | 12 ++
> 4 files changed, 181 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/Kconfig b/drivers/net/ethernet/airoha/Kconfig
> index ad3ce501e7a5..38dcc76e5998 100644
> --- a/drivers/net/ethernet/airoha/Kconfig
> +++ b/drivers/net/ethernet/airoha/Kconfig
> @@ -20,6 +20,7 @@ config NET_AIROHA
> depends on NET_DSA || !NET_DSA
> select NET_AIROHA_NPU
> select PAGE_POOL
> + select PHYLINK
> help
> This driver supports the gigabit ethernet MACs in the
> Airoha SoC family.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 5a8e84fa9918..eabd7b058f82 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -8,6 +8,7 @@
> #include <linux/of_reserved_mem.h>
> #include <linux/platform_device.h>
> #include <linux/tcp.h>
> +#include <linux/pcs/pcs.h>
Can you please respect the alphabetic order?
> #include <linux/u64_stats_sync.h>
> #include <net/dst_metadata.h>
> #include <net/page_pool/helpers.h>
> @@ -1779,6 +1780,15 @@ static int airoha_dev_open(struct net_device *netdev)
> u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
> struct airoha_qdma *qdma = dev->qdma;
>
> + err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0);
> + if (err) {
> + netdev_err(netdev, "%s: could not attach PHY: %d\n", __func__,
> + err);
Do we need specify the __func__ argument here?
> + return err;
> + }
> +
> + phylink_start(dev->phylink);
> +
> netif_tx_start_all_queues(netdev);
> err = airoha_set_vip_for_gdm_port(dev, true);
> if (err)
> @@ -1876,6 +1886,9 @@ static int airoha_dev_stop(struct net_device *netdev)
> }
> }
>
> + phylink_stop(dev->phylink);
> + phylink_disconnect_phy(dev->phylink);
> +
> return 0;
> }
>
> @@ -3148,6 +3161,153 @@ bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
> return false;
> }
>
> +/* Nothing to do in MAC, everything is handled in PCS */
> +static void airoha_mac_config(struct phylink_config *config, unsigned int mode,
> + const struct phylink_link_state *state)
> +{
> +}
> +
> +static void airoha_mac_link_up(struct phylink_config *config, struct phy_device *phy,
> + unsigned int mode, phy_interface_t interface,
> + int speed, int duplex, bool tx_pause, bool rx_pause)
> +{
> + struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev,
> + phylink_config);
> + struct airoha_gdm_port *port = dev->port;
> + struct airoha_eth *eth = dev->eth;
> + u32 frag_size_tx, frag_size_rx;
> + u32 mask, val;
> +
> + /* TX/RX frag is configured only for GDM4 */
> + if (port->id != 4)
if (port->id != AIROHA_GDM4_IDX)
...
> + return;
> +
> + switch (speed) {
> + case SPEED_10000:
> + case SPEED_5000:
> + frag_size_tx = 8;
> + frag_size_rx = 8;
> + break;
> + case SPEED_2500:
> + frag_size_tx = 2;
> + frag_size_rx = 1;
> + break;
> + default:
> + frag_size_tx = 1;
> + frag_size_rx = 0;
> + }
> +
> + /* Configure TX/RX frag based on speed */
> + if (dev->nbq == 1) {
> + mask = GDMA4_SGMII1_TX_FRAG_SIZE_MASK;
> + val = FIELD_PREP(GDMA4_SGMII1_TX_FRAG_SIZE_MASK,
> + frag_size_tx);
> + } else {
> + mask = GDMA4_SGMII0_TX_FRAG_SIZE_MASK;
> + val = FIELD_PREP(GDMA4_SGMII0_TX_FRAG_SIZE_MASK,
> + frag_size_tx);
> + }
> + airoha_fe_rmw(eth, REG_GDMA4_TMBI_FRAG, mask, val);
> +
> + if (dev->nbq == 1) {
> + mask = GDMA4_SGMII1_RX_FRAG_SIZE_MASK;
> + val = FIELD_PREP(GDMA4_SGMII1_RX_FRAG_SIZE_MASK,
> + frag_size_tx);
> + } else {
> + mask = GDMA4_SGMII0_RX_FRAG_SIZE_MASK;
> + val = FIELD_PREP(GDMA4_SGMII0_RX_FRAG_SIZE_MASK,
> + frag_size_tx);
> + }
> + airoha_fe_rmw(eth, REG_GDMA4_RMBI_FRAG, mask, val);
> +}
> +
> +/* Nothing to do in MAC, everything is handled in PCS */
> +static void airoha_mac_link_down(struct phylink_config *config, unsigned int mode,
> + phy_interface_t interface)
> +{
> +}
> +
> +static const struct phylink_mac_ops airoha_phylink_ops = {
> + .mac_config = airoha_mac_config,
> + .mac_link_up = airoha_mac_link_up,
> + .mac_link_down = airoha_mac_link_down,
> +};
> +
> +static int airoha_fill_available_pcs(struct phylink_config *config,
> + struct phylink_pcs **available_pcs,
> + unsigned int num_available_pcs)
> +{
> + struct device *dev = config->dev;
> +
> + return fwnode_phylink_pcs_parse(dev_fwnode(dev), available_pcs,
> + &num_available_pcs);
> +}
> +
> +static int airoha_setup_phylink(struct net_device *netdev)
> +{
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct device_node *np = netdev->dev.of_node;
> + struct airoha_gdm_port *port = dev->port;
> + struct phylink_config *config;
> + phy_interface_t phy_mode;
> + struct phylink *phylink;
> + int err;
> +
> + err = of_get_phy_mode(np, &phy_mode);
> + if (err) {
> + dev_err(&netdev->dev, "incorrect phy-mode\n");
> + return err;
> + }
> +
> + config = &dev->phylink_config;
remove new-line here.
> +
> + config->dev = &netdev->dev;
> + config->type = PHYLINK_NETDEV;
> + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10000FD;
> + if (port->id > AIROHA_GDM1_IDX)
maybe
if (port->id != AIROHA_GDM1_IDX)
...
> + config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000 |
> + MAC_2500FD | MAC_5000FD;
> +
> + err = fwnode_phylink_pcs_parse(dev_fwnode(&netdev->dev), NULL,
> + &config->num_available_pcs);
> + if (err)
> + return err;
> +
> + config->fill_available_pcs = airoha_fill_available_pcs;
> +
> + /*
> + * GDM1 only supports internal for Embedded Switch
> + * and doesn't require a PCS.
> + */
> + if (port->id == AIROHA_GDM1_IDX) {
> + __set_bit(PHY_INTERFACE_MODE_INTERNAL,
> + config->supported_interfaces);
> + } else {
> + __set_bit(PHY_INTERFACE_MODE_SGMII,
> + config->supported_interfaces);
> + __set_bit(PHY_INTERFACE_MODE_1000BASEX,
> + config->supported_interfaces);
> + __set_bit(PHY_INTERFACE_MODE_2500BASEX,
> + config->supported_interfaces);
> + __set_bit(PHY_INTERFACE_MODE_10GBASER,
> + config->supported_interfaces);
> + __set_bit(PHY_INTERFACE_MODE_USXGMII,
> + config->supported_interfaces);
> +
> + phy_interface_copy(config->pcs_interfaces,
> + config->supported_interfaces);
> + }
> +
> + phylink = phylink_create(config, of_fwnode_handle(np),
> + phy_mode, &airoha_phylink_ops);
> + if (IS_ERR(phylink))
> + return PTR_ERR(phylink);
> +
> + dev->phylink = phylink;
> +
> + return 0;
> +}
> +
> static int airoha_alloc_gdm_device(struct airoha_eth *eth,
> struct airoha_gdm_port *port,
> int nbq, struct device_node *np)
> @@ -3210,7 +3370,7 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
> dev->nbq = nbq;
> port->devs[index] = dev;
>
> - return 0;
> + return airoha_setup_phylink(netdev);
> }
>
> static int airoha_alloc_gdm_port(struct airoha_eth *eth,
> @@ -3435,8 +3595,10 @@ static int airoha_probe(struct platform_device *pdev)
> continue;
>
> netdev = netdev_from_priv(dev);
> - if (netdev->reg_state == NETREG_REGISTERED)
> + if (netdev->reg_state == NETREG_REGISTERED) {
> + phylink_destroy(dev->phylink);
> unregister_netdev(netdev);
> + }
> of_node_put(netdev->dev.of_node);
> }
> airoha_metadata_dst_free(port);
> @@ -3472,6 +3634,7 @@ static void airoha_remove(struct platform_device *pdev)
> continue;
>
> netdev = netdev_from_priv(dev);
> + phylink_destroy(dev->phylink);
> unregister_netdev(netdev);
> of_node_put(netdev->dev.of_node);
> }
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index 8f42973f9cf5..1b25603dc64d 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -554,6 +554,9 @@ struct airoha_gdm_dev {
>
> u32 flags;
> int nbq;
> +
> + struct phylink *phylink;
> + struct phylink_config phylink_config;
> };
>
> struct airoha_gdm_port {
> diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
> index 436f3c8779c1..27f2583e143a 100644
> --- a/drivers/net/ethernet/airoha/airoha_regs.h
> +++ b/drivers/net/ethernet/airoha/airoha_regs.h
> @@ -358,6 +358,18 @@
> #define IP_FRAGMENT_PORT_MASK GENMASK(8, 5)
> #define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0)
>
> +#define REG_GDMA4_TMBI_FRAG 0x2028
> +#define GDMA4_SGMII1_TX_WEIGHT_MASK GENMASK(31, 26)
> +#define GDMA4_SGMII1_TX_FRAG_SIZE_MASK GENMASK(25, 16)
> +#define GDMA4_SGMII0_TX_WEIGHT_MASK GENMASK(15, 10)
> +#define GDMA4_SGMII0_TX_FRAG_SIZE_MASK GENMASK(9, 0)
> +
> +#define REG_GDMA4_RMBI_FRAG 0x202c
> +#define GDMA4_SGMII1_RX_WEIGHT_MASK GENMASK(31, 26)
> +#define GDMA4_SGMII1_RX_FRAG_SIZE_MASK GENMASK(25, 16)
> +#define GDMA4_SGMII0_RX_WEIGHT_MASK GENMASK(15, 10)
> +#define GDMA4_SGMII0_RX_FRAG_SIZE_MASK GENMASK(9, 0)
> +
> #define REG_MC_VLAN_EN 0x2100
> #define MC_VLAN_EN_MASK BIT(0)
>
> --
> 2.53.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v13 2/3] hwmon: ltc4283: Add support for the LTC4283 Swap Controller
From: Guenter Roeck @ 2026-06-09 15:29 UTC (permalink / raw)
To: Nuno Sá
Cc: linux-gpio, linux-hwmon, devicetree, linux-doc, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Linus Walleij,
Bartosz Golaszewski
In-Reply-To: <20260502-ltc4283-support-v13-2-1c206542e652@analog.com>
On Sat, May 02, 2026 at 10:56:53AM +0100, Nuno Sá wrote:
> From: Nuno Sá <nuno.sa@analog.com>
>
> Support the LTC4283 Hot Swap Controller. The device features programmable
> current limit with foldback and independently adjustable inrush current to
> optimize the MOSFET safe operating area (SOA). The SOA timer limits MOSFET
> temperature rise for reliable protection against overstresses.
>
> An I2C interface and onboard ADC allow monitoring of board current,
> voltage, power, energy, and fault status.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Applied.
Thanks,
Guenter
^ permalink raw reply
* [PATCH net-next v4 13/13] net: ethernet: adi: Add a driver for the ADIN1140 MACPHY
From: Ciprian Regus via B4 Relay @ 2026-06-09 15:28 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: <20260609-adin1140-driver-v4-0-0753e28ee004@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.
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v4:
- Renamed the macros used to define registers in MMS0 and MMS1 to
include ADIN1140_MAC_ prefix in order to match the TRM naming scheme
(MAC_register_name for MMS0 and MMS1).
- Used the renamed OA_TC6_MAC_MMS1 macro for MMS1 instead of
OA_TC6_VEND_MMS1.
- Updated the MAINTAINERS entry to reflect the dt bindings file rename to
adi,ad3306.yaml.
v3 changelog:
- Clear the unused destination address filter slots in
adin1140_rx_mode_work(). This is required in case we remove
multicast or unicast addresses from a netdev.
- The device only allows destination MAC masks for the first 2 slots.
Fix adin1140_mac_filter_set() to take this into account.
- use oa_tc6_{write,read}_register_mms, since the OA_TC6_MMS_REG()
macro was removed. Update the register address defines accordingly.
- use the devres API for mdio and netdev alloc/register instead of
manually managing those.
- use dev_err_probe() in several places to simplify logging during
probe.
- use scoped_guard() instead of spin_lock/unlock(). Had to break some
sequences in their own function to fit the 80 character limit.
- fix the comment describing the reason for skb padding in the TX path.
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 | 815 ++++++++++++++++++++++++++++++++++++
4 files changed, 836 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 50f795dfa880..c3cab8f7b9ef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1858,6 +1858,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,ad3306.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..306e5de6faa5
--- /dev/null
+++ b/drivers/net/ethernet/adi/adin1140.c
@@ -0,0 +1,815 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Analog Devices, Inc. ADIN1140 10BASE-T1S MAC-PHY
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+
+#include <linux/cleanup.h>
+#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_MAC_CONFIG2_FWD_UNK2HOST BIT(2)
+
+#define ADIN1140_MAC_P1_LOOP_ADDR_REG 0xC4
+
+#define ADIN1140_MAC_ADDR_FILT_UPR_REG 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 0x51
+
+#define ADIN1140_MAC_ADDR_MASK_UPR_REG 0x70
+#define ADIN1140_MAC_ADDR_MASK_LWR_REG 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_MAC_FILT_MASK_LIMIT 2U
+
+#define ADIN1140_MAC_RX_FRAME_CNT 0xA1
+#define ADIN1140_MAC_RX_BC_FRAME_CNT 0xA2
+#define ADIN1140_MAC_RX_MC_FRAME_CNT 0xA3
+#define ADIN1140_MAC_RX_UC_FRAME_CNT 0xA4
+#define ADIN1140_MAC_RX_CRC_ERR_CNT 0xA5
+#define ADIN1140_MAC_RX_ALIGN_ERR_CNT 0xA6
+#define ADIN1140_MAC_RX_PREAMBLE_ERR_CNT 0xA7
+#define ADIN1140_MAC_RX_SHORT_ERR_CNT 0xA8
+#define ADIN1140_MAC_RX_LONG_ERR_CNT 0xA9
+#define ADIN1140_MAC_RX_PHY_ERR_CNT 0xAA
+#define ADIN1140_MAC_RX_DRP_FULL_CNT 0xAB
+#define ADIN1140_MAC_RX_DRP_FILTER_CNT 0xAD
+#define ADIN1140_MAC_RX_IFG_ERR_CNT 0xAE
+#define ADIN1140_MAC_TX_FRAME_CNT 0xB1
+#define ADIN1140_MAC_TX_BC_FRAME_CNT 0xB2
+#define ADIN1140_MAC_TX_MC_FRAME_CNT 0xB3
+#define ADIN1140_MAC_TX_UC_FRAME_CNT 0xB4
+#define ADIN1140_MAC_TX_SINGLE_COL_CNT 0xB5
+#define ADIN1140_MAC_TX_MULTI_COL_CNT 0xB6
+#define ADIN1140_MAC_TX_DEFERRED_CNT 0xB7
+#define ADIN1140_MAC_TX_LATE_COL_CNT 0xB8
+#define ADIN1140_MAC_TX_EXCESS_COL_CNT 0xB9
+#define ADIN1140_MAC_TX_UNDERRUN_CNT 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_MAC_RX_FRAME_CNT,
+ [rx_bc_frames] = ADIN1140_MAC_RX_BC_FRAME_CNT,
+ [rx_mc_frames] = ADIN1140_MAC_RX_MC_FRAME_CNT,
+ [rx_uc_frames] = ADIN1140_MAC_RX_UC_FRAME_CNT,
+ [rx_crc_errors] = ADIN1140_MAC_RX_CRC_ERR_CNT,
+ [rx_align_errors] = ADIN1140_MAC_RX_ALIGN_ERR_CNT,
+ [rx_preamble_errors] = ADIN1140_MAC_RX_PREAMBLE_ERR_CNT,
+ [rx_short_frame_errors] = ADIN1140_MAC_RX_SHORT_ERR_CNT,
+ [rx_long_frame_errors] = ADIN1140_MAC_RX_LONG_ERR_CNT,
+ [rx_phy_errors] = ADIN1140_MAC_RX_PHY_ERR_CNT,
+ [rx_fifo_full_dropped] = ADIN1140_MAC_RX_DRP_FULL_CNT,
+ [rx_addr_filter_dropped] = ADIN1140_MAC_RX_DRP_FILTER_CNT,
+ [rx_ifg_errors] = ADIN1140_MAC_RX_IFG_ERR_CNT,
+ [tx_frames] = ADIN1140_MAC_TX_FRAME_CNT,
+ [tx_bc_frames] = ADIN1140_MAC_TX_BC_FRAME_CNT,
+ [tx_mc_frames] = ADIN1140_MAC_TX_MC_FRAME_CNT,
+ [tx_uc_frames] = ADIN1140_MAC_TX_UC_FRAME_CNT,
+ [tx_single_collision] = ADIN1140_MAC_TX_SINGLE_COL_CNT,
+ [tx_multi_collision] = ADIN1140_MAC_TX_MULTI_COL_CNT,
+ [tx_deferred] = ADIN1140_MAC_TX_DEFERRED_CNT,
+ [tx_late_collision] = ADIN1140_MAC_TX_LATE_COL_CNT,
+ [tx_excess_collision] = ADIN1140_MAC_TX_EXCESS_COL_CNT,
+ [tx_underrun] = ADIN1140_MAC_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 reg_address;
+ u32 val;
+ int ret;
+
+ if (slot >= ADIN1140_MAC_FILT_MAX_SLOT)
+ return -ENOSPC;
+
+ reg_address = ADIN1140_MAC_ADDR_FILT_UPR_REG + 2 * slot;
+
+ ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
+ reg_address,
+ get_unaligned_be16(&addr[0]) |
+ ADIN1140_MAC_ADDR_FILT_APPLY2PORT1 |
+ ADIN1140_MAC_ADDR_FILT_TO_HOST);
+ if (ret)
+ return ret;
+
+ reg_address = ADIN1140_MAC_ADDR_FILT_LWR_REG + 2 * slot;
+ ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
+ reg_address,
+ get_unaligned_be32(&addr[2]));
+ if (ret)
+ return ret;
+
+ /* Only the first 2 destination MAC filter slots support masking.
+ * For the other entries, the destination address in the received
+ * frame must match exactly.
+ */
+ if (slot >= ADIN1140_MAC_FILT_MASK_LIMIT)
+ return 0;
+
+ val = get_unaligned_be16(&mask[0]);
+ reg_address = ADIN1140_MAC_ADDR_MASK_UPR_REG + (2 * slot);
+
+ ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
+ reg_address, val);
+ if (ret)
+ return ret;
+
+ val = get_unaligned_be32(&mask[2]);
+ reg_address = ADIN1140_MAC_ADDR_MASK_LWR_REG + (2 * slot);
+
+ return oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
+ reg_address, 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)
+{
+ /* Only the first 2 filter slots support masking, so no unicast
+ * address will ever need a mask. The first slots are used for the
+ * all multicast and broadcast filter.
+ */
+ return adin1140_mac_filter_set(priv, priv->netdev->dev_addr, NULL,
+ 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_MAC_CONFIG2_FWD_UNK2HOST;
+ else
+ val &= ~ADIN1140_MAC_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++;
+ }
+
+ for (i = slot; i < ADIN1140_MAC_FILT_MAX_SLOT; i++) {
+ ret = adin1140_mac_filter_clear(priv, i);
+ if (ret)
+ return;
+ }
+
+ } 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_mms(priv->tc6, OA_TC6_MAC_MMS1,
+ adin1140_stat_regs[i],
+ ®_val);
+ if (ret)
+ break;
+
+ stat_buff[i] = reg_val;
+ }
+
+ scoped_guard(spinlock, &priv->stat_lock)
+ memcpy(&priv->stats, stat_buff, sizeof(priv->stats));
+
+ 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_mms(priv->tc6, OA_TC6_MAC_MMS1,
+ 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);
+
+ /* The MAC doesn't automatically pad the frame to a 60 byte minimum
+ * size in case the host sends a shorter skb, so we have to do it in
+ * the driver. The FCS will be added by the MAC.
+ */
+ 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 adin1140_priv *priv,
+ struct rtnl_link_stats64 *storage)
+{
+ 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];
+}
+
+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;
+
+ scoped_guard(spinlock, &priv->stat_lock)
+ __adin1140_ndo_get_stats64(priv, storage);
+}
+
+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;
+
+ scoped_guard(spinlock, &priv->stat_lock) {
+ for (i = 0; i < ARRAY_SIZE(adin1140_stats); i++)
+ data[i] = priv->stats[adin1140_stats[i].idx];
+ }
+}
+
+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_eth_mac_stats(struct adin1140_priv *priv,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ 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];
+}
+
+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);
+
+ scoped_guard(spinlock, &priv->stat_lock)
+ __adin1140_eth_mac_stats(priv, mac_stats);
+}
+
+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)
+{
+ priv->mdiobus = devm_mdiobus_alloc(&spidev->dev);
+ if (!priv->mdiobus)
+ return dev_err_probe(&spidev->dev, -ENOMEM,
+ "MDIO bus alloc failed\n");
+
+ priv->mdiobus->name = "adin1140-mdiobus";
+ priv->mdiobus->priv = priv->tc6;
+ priv->mdiobus->parent = &spidev->dev;
+ priv->mdiobus->phy_mask = GENMASK(31, 1);
+ 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));
+
+ return devm_mdiobus_register(&spidev->dev, priv->mdiobus);
+}
+
+static void adin1140_handle_link_change(struct net_device *netdev)
+{
+ phy_print_status(netdev->phydev);
+}
+
+static void adin1140_phy_remove(void *data)
+{
+ phy_disconnect(data);
+}
+
+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)
+ return dev_err_probe(&spidev->dev, -ENODEV, "No PHY found\n");
+
+ priv->phydev->is_internal = true;
+ ret = phy_connect_direct(priv->netdev, priv->phydev,
+ &adin1140_handle_link_change,
+ PHY_INTERFACE_MODE_INTERNAL);
+ if (ret)
+ return dev_err_probe(&spidev->dev, ret,
+ "Can't attach PHY to %s\n",
+ priv->mdiobus->id);
+
+ ret = devm_add_action_or_reset(&spidev->dev, adin1140_phy_remove,
+ priv->phydev);
+ if (ret)
+ return ret;
+
+ phy_attached_info(priv->phydev);
+
+ return 0;
+}
+
+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 void adin1140_oa_tc6_remove(void *data)
+{
+ oa_tc6_exit(data);
+}
+
+static void adin1140_cancel_rx_mode_work(void *data)
+{
+ cancel_work_sync(data);
+}
+
+static int adin1140_probe(struct spi_device *spi)
+{
+ struct oa_tc6_quirks tc6_quirks = {};
+ struct net_device *netdev;
+ struct adin1140_priv *priv;
+ int ret;
+
+ netdev = devm_alloc_etherdev(&spi->dev, 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_quirks.quirk_flags = OA_TC6_BROKEN_PHY;
+
+ priv->tc6 = oa_tc6_init(spi, netdev, &tc6_quirks);
+ if (!priv->tc6)
+ return -ENODEV;
+
+ ret = devm_add_action_or_reset(&spi->dev, adin1140_oa_tc6_remove,
+ priv->tc6);
+ if (ret)
+ return ret;
+
+ ret = adin1140_phy_init(priv, spi);
+ if (ret)
+ return ret;
+
+ if (device_get_ethdev_address(&spi->dev, netdev))
+ eth_hw_addr_random(netdev);
+
+ ret = adin1140_configure(priv);
+ if (ret)
+ return ret;
+
+ INIT_WORK(&priv->rx_mode_work, adin1140_rx_mode_work);
+ INIT_DELAYED_WORK(&priv->stats_work, adin1140_stats_work);
+
+ ret = devm_add_action_or_reset(&spi->dev, adin1140_cancel_rx_mode_work,
+ &priv->rx_mode_work);
+ if (ret)
+ return ret;
+
+ 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 = devm_register_netdev(&spi->dev, netdev);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret,
+ "Failed to register netdev");
+
+ return 0;
+}
+
+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,
+ .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 v4 12/13] net: phy: Add support for the ADIN1140 PHY
From: Ciprian Regus via B4 Relay @ 2026-06-09 15:28 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: <20260609-adin1140-driver-v4-0-0753e28ee004@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.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v4 changelog:
- no change
v3 changelog:
- no change
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 eb8cdcc76324..50f795dfa880 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1858,6 +1858,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 099f25dceabb..a29d3fed8a05 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -136,6 +136,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 de660ae94945..e23df5e836e9 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_AN8801_PHY) += air_an8801.o
obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o
obj-$(CONFIG_AIR_NET_PHYLIB) += air_phy_lib.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 v4 10/13] net: phy: add generic helpers for direct C45 MMD access
From: Ciprian Regus via B4 Relay @ 2026-06-09 15:28 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: <20260609-adin1140-driver-v4-0-0753e28ee004@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.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v4 changelog:
- no change
v3 changelog:
- no change
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 v4 11/13] net: phy: microchip-t1s: use generic C45 MMD access helpers
From: Ciprian Regus via B4 Relay @ 2026-06-09 15:28 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: <20260609-adin1140-driver-v4-0-0753e28ee004@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.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v4 changelog:
- no change
v3 changelog:
- no change
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 v4 09/13] net: ethernet: oa_tc6: Add new register address defines
From: Ciprian Regus via B4 Relay @ 2026-06-09 15:28 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: <20260609-adin1140-driver-v4-0-0753e28ee004@analog.com>
From: Ciprian Regus <ciprian.regus@analog.com>
Add macro defines for the CONFIG2 register and the MMS1 memory map.
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v4 changelog:
- rename OA_TC6_VEND_MMS1 to OA_TC6_MAC_MMS1.
- group the MMS1 entry with the other MMS values and update the comment
describing that set of macros.
v3 changelog:
- New patch
---
include/linux/oa_tc6.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h
index cace67cfc709..24c5899912c0 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -25,6 +25,9 @@
#define OA_TC6_CONFIG0_ZARFE_ENABLE BIT(12)
#define OA_TC6_CONFIG0_PROTE BIT(5)
+/* Configuration Register #2 */
+#define OA_TC6_REG_CONFIG2 0x0006
+
/* Status Register #0 */
#define OA_TC6_REG_STATUS0 0x0008
#define OA_TC6_STATUS0_RESETC BIT(6) /* Reset Complete */
@@ -49,9 +52,10 @@
#define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00
#define OA_TC6_PHY_STD_REG_ADDR_MASK 0x1F
-/* PHY – Clause 45 registers memory map selector (MMS) as per table 6 in the
+/* Memory map selector (MMS) values as per table 6 in the
* OPEN Alliance specification.
*/
+#define OA_TC6_MAC_MMS1 1
#define OA_TC6_PHY_C45_PCS_MMS2 2 /* MMD 3 */
#define OA_TC6_PHY_C45_PMA_PMD_MMS3 3 /* MMD 1 */
#define OA_TC6_PHY_C45_VS_PLCA_MMS4 4 /* MMD 31 */
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v4 07/13] net: ethernet: oa_tc6: Add read_mms/write_mms register access functions
From: Ciprian Regus via B4 Relay @ 2026-06-09 15:28 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: <20260609-adin1140-driver-v4-0-0753e28ee004@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. Define register access functions that
allow the caller to specify the MMS.
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v4 changelog:
- changed the address parameter type to u16 from u32.
- reworded the kdoc header for the new {read,write}_register_mms
functions to better explain their use case.
v3 changelog:
- replace the OA_TC6_MMS_REG() macro with the register access functions
that allow passing an mms parameter.
v2 changelog:
- New patch
---
drivers/net/ethernet/oa_tc6.c | 44 +++++++++++++++++++++++++++++++++++++++++++
include/linux/oa_tc6.h | 4 ++++
2 files changed, 48 insertions(+)
diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index 92da5bb74cc7..d32a1a323d48 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -61,6 +61,8 @@
#define STATUS0_RESETC_POLL_DELAY 1000
#define STATUS0_RESETC_POLL_TIMEOUT 1000000
+#define OA_TC6_REG_MMS_MASK GENMASK(19, 16)
+
/* Internal structure for MAC-PHY drivers */
struct oa_tc6 {
struct device *dev;
@@ -344,6 +346,27 @@ int oa_tc6_read_register(struct oa_tc6 *tc6, u32 address, u32 *value)
}
EXPORT_SYMBOL_GPL(oa_tc6_read_register);
+/**
+ * oa_tc6_read_register_mms - function for reading a MAC-PHY register in a
+ * specified memory map.
+ * @tc6: oa_tc6 struct.
+ * @mms: Memory map selector for the register.
+ * @address: register address of the MAC-PHY to be read.
+ * @value: value read from the @address register address of the MAC-PHY.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int oa_tc6_read_register_mms(struct oa_tc6 *tc6, u8 mms, u16 address,
+ u32 *value)
+{
+ u32 mms_reg;
+
+ mms_reg = FIELD_PREP(OA_TC6_REG_MMS_MASK, mms) | address;
+
+ return oa_tc6_read_registers(tc6, mms_reg, value, 1);
+}
+EXPORT_SYMBOL_GPL(oa_tc6_read_register_mms);
+
/**
* oa_tc6_write_registers - function for writing multiple consecutive registers.
* @tc6: oa_tc6 struct.
@@ -388,6 +411,27 @@ int oa_tc6_write_register(struct oa_tc6 *tc6, u32 address, u32 value)
}
EXPORT_SYMBOL_GPL(oa_tc6_write_register);
+/**
+ * oa_tc6_write_register_mms - function for writing a MAC-PHY register in a
+ * specified memory map.
+ * @tc6: oa_tc6 struct.
+ * @mms: Memory map selector for the register.
+ * @address: register address of the MAC-PHY to be written.
+ * @value: value to be written in the @address register address of the MAC-PHY.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int oa_tc6_write_register_mms(struct oa_tc6 *tc6, u8 mms, u16 address,
+ u32 value)
+{
+ u32 mms_reg;
+
+ mms_reg = FIELD_PREP(OA_TC6_REG_MMS_MASK, mms) | address;
+
+ return oa_tc6_write_registers(tc6, mms_reg, &value, 1);
+}
+EXPORT_SYMBOL_GPL(oa_tc6_write_register_mms);
+
static int oa_tc6_check_phy_reg_direct_access_capability(struct oa_tc6 *tc6)
{
u32 regval;
diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h
index bd369aac9c3b..cace67cfc709 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -72,9 +72,13 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev,
struct oa_tc6_quirks *quirks);
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_register_mms(struct oa_tc6 *tc6, u8 mms, u16 address,
+ u32 value);
int oa_tc6_write_registers(struct oa_tc6 *tc6, u32 address, u32 value[],
u8 length);
int oa_tc6_read_register(struct oa_tc6 *tc6, u32 address, u32 *value);
+int oa_tc6_read_register_mms(struct oa_tc6 *tc6, u8 mms, u16 address,
+ u32 *value);
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);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v4 08/13] net: ethernet: oa_tc6: Use the read_mms/write_mms functions for C45
From: Ciprian Regus via B4 Relay @ 2026-06-09 15:28 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: <20260609-adin1140-driver-v4-0-0753e28ee004@analog.com>
From: Ciprian Regus <ciprian.regus@analog.com>
Accessing PHY MMD devices requires control transactions to registers in
a memory map other than 0. Replace the current formatting of the
register addresses with the oa_tc6_{read,write}_register_mms()
functions. While we're here, introduce the mms variable to store the
memory map returned by oa_tc6_get_phy_c45_mms() instead of ret, in order
to improve the code readability.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v4 changelog:
- no change
v3 changelog:
- New patch
---
drivers/net/ethernet/oa_tc6.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index d32a1a323d48..4fe4e41136c1 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -500,13 +500,14 @@ int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum,
{
struct oa_tc6 *tc6 = bus->priv;
u32 regval;
+ int mms;
int ret;
- ret = oa_tc6_get_phy_c45_mms(devnum);
- if (ret < 0)
- return ret;
+ mms = oa_tc6_get_phy_c45_mms(devnum);
+ if (mms < 0)
+ return mms;
- ret = oa_tc6_read_register(tc6, (ret << 16) | regnum, ®val);
+ ret = oa_tc6_read_register_mms(tc6, mms, regnum, ®val);
if (ret)
return ret;
@@ -518,13 +519,13 @@ 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;
+ int mms;
- ret = oa_tc6_get_phy_c45_mms(devnum);
- if (ret < 0)
- return ret;
+ mms = oa_tc6_get_phy_c45_mms(devnum);
+ if (mms < 0)
+ return mms;
- return oa_tc6_write_register(tc6, (ret << 16) | regnum, val);
+ return oa_tc6_write_register_mms(tc6, mms, regnum, val);
}
EXPORT_SYMBOL_GPL(oa_tc6_mdiobus_write_c45);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v4 06/13] net: ethernet: oa_tc6: Add the OA_TC6_ prefix to standard registers
From: Ciprian Regus via B4 Relay @ 2026-06-09 15:28 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: <20260609-adin1140-driver-v4-0-0753e28ee004@analog.com>
From: Ciprian Regus <ciprian.regus@analog.com>
The OA TC6 standard registers are currently exported in a header file.
Add the OA_TC6_ prefix to the register address and subfield mask macros
to avoid future naming conflicts.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v4 changelog:
- no change
v3 changelog:
- New patch
---
drivers/net/ethernet/oa_tc6.c | 35 ++++++++++++++++++-----------------
include/linux/oa_tc6.h | 36 ++++++++++++++++++------------------
2 files changed, 36 insertions(+), 35 deletions(-)
diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index 97df38207827..92da5bb74cc7 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -397,7 +397,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;
@@ -598,7 +598,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);
@@ -607,7 +607,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)
@@ -626,10 +626,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);
}
@@ -644,7 +644,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);
}
@@ -687,25 +687,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;
}
@@ -1141,9 +1141,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;
}
@@ -1183,7 +1184,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);
}
@@ -1231,7 +1232,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 bbc42758a313..bd369aac9c3b 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -13,37 +13,37 @@
/* OPEN Alliance TC6 registers */
/* Standard Capabilities Register */
#define OA_TC6_REG_STDCAP 0x0002
-#define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8)
+#define OA_TC6_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 */
+#define OA_TC6_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)
+#define OA_TC6_CONFIG0_SYNC BIT(15)
+#define OA_TC6_CONFIG0_ZARFE_ENABLE BIT(12)
+#define OA_TC6_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)
+#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 BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8)
-#define BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0)
+#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 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)
+#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
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox