* Re: [RFC PATCH 1/3] dt-bindings: pinctrl: mt8516/mt8167: Move compatibles from mt66xx to mt6795
From: Conor Dooley @ 2026-06-25 18:17 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: linux-mediatek, Sean Wang, Linus Walleij, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, linux-gpio, devicetree, linux-kernel,
linux-arm-kernel
In-Reply-To: <CAORyz2JHj7i6VhKom+tVd8PWBjM=TFhbr8-mOy3GH6eDYu4WPw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2943 bytes --]
On Thu, Jun 25, 2026 at 06:47:32PM +0200, Luca Leonardo Scorcia wrote:
> Hi,
>
> > I've not done a very through analysis, but this seems like a massive ABI
> > break.
> > The change you're trying to make here will mean that new kernels will
> > not work with older devicetrees AFAICT.
>
> Correct, that's the reason I sent it as an RFC (I mentioned this in
> the cover letter). I am new to kernel work and I'm not sure how to
> deal with this change. On one hand I am almost certain now that the
> upstream driver has never been used in actual devices, since the older
> code was only partially merged and also, as Sashiko correctly pointed
> out in [1], it had serious errors when matched against the data sheet:
>
> Sashiko:
> > Does this configuration cause a regression in pin multiplexing across the SoC?
> > The legacy driver used a 4-bit shift per pin to pack 5 pins per 32-bit
> > register. By passing 3 as the width here, the framework calculates mode
> > offsets using 3 bits per pin. This causes pinmux writes to align with
> > the wrong bits and can overwrite the configurations of adjacent pins.
>
> Data sheet here clearly shows 3 bits per pin are used to choose the
> pin function.
>
> On the other hand I know that breaking the ABI is a big no. But what
> would be an appropriate solution? Maybe duplicating the driver with a
If you can substantiate a claim that the current setup doesn't actually
work for these devices (which seems plausible), you can justify changing
the ABI on that basis.
> different name, something like mediatek,mt8167-pinctrl-v2? Is there
> another driver I could have a look at to learn how to approach this
> problem?
Usually when making ABI changes because something was inaccurate (but
not wrong to the point that it didn't work at all) it's possible to
support both new and old ABIs at the same time because of new properties
etc. This is a difficult one because it's using the same properties in
different ways. A new compatible would definitely be required for a
genuine fresh start while retaining kernel support for the old mechanism
in this case.
But as I said, if what's in the kernel right now does not work at all,
then you can probably just rework in place. Your commit messages will
have to be very clear about why what you're doing is okay however.
It'd probably be best to try to detect the old devicetrees (if that's
even possible, will be tricky unless the devices you're moving are the
ones that need mediatek,pctl-regmap) and reject probe.
> Sashiko also pointed out some other minor issues with the register
> maps I already fixed locally after confirming with the data sheet, but
> did not provide clues about how to solve the ABI breakage.
>
> [1] https://sashiko.dev/#/message/20260625111629.6CD701F000E9%40smtp.kernel.org
>
> Thank you for your time!
> --
> Luca Leonardo Scorcia
> l.scorcia@gmail.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH v4 0/2] arm64: errata: NVIDIA Olympus device store/load ordering
From: Shanker Donthineni @ 2026-06-25 18:24 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Vladimir Murzin
Cc: Jason Gunthorpe, linux-arm-kernel, Mark Rutland, linux-kernel,
linux-doc, Shanker Donthineni, Vikram Sethi, Jason Sequeira
This series works around the NVIDIA Olympus device store/load ordering
erratum (T410-OLY-1027): a Device-nGnR* load can be observed by a
peripheral before an older, non-overlapping Device-nGnR* store to the
same peripheral, breaking the program order that drivers rely on for
MMIO and potentially leaving a device in an incorrect state.
Patch 1 adds the workaround. It promotes the raw MMIO store helpers
(__raw_writeb/w/l/q, and therefore writel()/writel_relaxed()) to
store-release on affected CPUs, and promotes the trailing DGH of the
write-combining __iowrite{32,64}_copy() helpers to dmb osh. Everything is
gated on a new ARM64_WORKAROUND_DEVICE_STORE_RELEASE cpucap and patched
in only on affected parts, so it is a no-op elsewhere.
Patch 2 provides arm64 memset_io()/memcpy_toio(). The generic versions
are built on __raw_write*(), so patch 1 would promote every store in a
block to a store-release; as each STLR drains the write-combining buffer,
block MMIO becomes O(n) store-releases. The arm64 versions emit plain
STR in the loop and order the whole block with a single trailing dmb osh,
keeping block MMIO at one-barrier cost.
Performance: NVIDIA Olympus, write-combining MMIO to a device BAR, single
PE pinned; per-call cost in ns. Consecutive writes ping-pong between two
buffers so repeated stores are not coalesced. iowrite64/iowrite32 =
__iowrite{64,32}_copy().
Table 1 - workaround off (CONFIG_NVIDIA_OLYMPUS_1027_ERRATUM=n)
+-------+-----------+-----------+-----------+-------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-----------+-------------+
| 8B | 67.9 ns | 67.8 ns | 3.6 ns | 3.6 ns |
| 16B | 67.9 ns | 67.8 ns | 4.0 ns | 4.0 ns |
| 32B | 67.9 ns | 67.9 ns | 4.6 ns | 4.6 ns |
| 64B | 69.1 ns | 69.1 ns | 69.1 ns | 69.0 ns |
| 128B | 138.3 ns | 138.3 ns | 138.4 ns | 138.3 ns |
| 256B | 276.6 ns | 276.6 ns | 276.6 ns | 276.7 ns |
| 512B | 276.6 ns | 276.5 ns | 276.6 ns | 276.6 ns |
| 1KB | 276.6 ns | 278.4 ns | 276.6 ns | 276.6 ns |
| 2KB | 278.4 ns | 278.4 ns | 275.9 ns | 276.6 ns |
| 4KB | 365.7 ns | 365.7 ns | 365.7 ns | 365.7 ns |
+-------+-----------+-----------+-----------+-------------+
relaxed/no-flush: memset_io()/memcpy_toio() issue plain stores with no
trailing dgh() or barrier, unlike __iowrite*_copy() which ends with dgh().
Table 2 - workaround on, arm64 memset_io/memcpy_toio (this series)
+-------+-----------+-----------+-----------+-------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-----------+-------------+
| 8B | 231.6 ns | 231.6 ns | 232.4 ns | 232.4 ns |
| 16B | 231.7 ns | 231.9 ns | 232.7 ns | 232.6 ns |
| 32B | 231.9 ns | 232.7 ns | 232.9 ns | 232.9 ns |
| 64B | 232.7 ns | 235.0 ns | 233.7 ns | 233.6 ns |
| 128B | 233.6 ns | 235.8 ns | 234.4 ns | 234.3 ns |
| 256B | 237.7 ns | 276.8 ns | 264.0 ns | 276.7 ns |
| 512B | 237.7 ns | 277.1 ns | 238.1 ns | 277.6 ns |
| 1KB | 253.7 ns | 279.3 ns | 276.1 ns | 294.1 ns |
| 2KB | 295.0 ns | 318.7 ns | 288.5 ns | 308.3 ns |
| 4KB | 365.9 ns | 381.4 ns | 365.7 ns | 381.3 ns |
+-------+-----------+-----------+-----------+-------------+
all four helpers end with a single trailing barrier (dmb osh).
Table 3 - workaround on, generic per-store memset_io/memcpy_toio
+-------+-----------+-----------+-------------+--------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-------------+--------------+
| 8B | 231.6 ns | 231.6 ns | 229.0 ns | 229.0 ns |
| 16B | 231.7 ns | 231.9 ns | 458.4 ns | 458.5 ns |
| 32B | 231.9 ns | 232.7 ns | 917.4 ns | 917.5 ns |
| 64B | 232.7 ns | 234.8 ns | 1835.4 ns | 1835.5 ns |
| 128B | 233.6 ns | 235.8 ns | 3670.9 ns | 3670.8 ns |
| 256B | 237.7 ns | 276.7 ns | 7341.6 ns | 7341.6 ns |
| 512B | 237.7 ns | 279.4 ns | 14001.4 ns | 14001.3 ns |
| 1KB | 253.7 ns | 279.1 ns | 28631.5 ns | 28631.8 ns |
| 2KB | 279.4 ns | 317.9 ns | 57276.3 ns | 57275.2 ns |
| 4KB | 365.7 ns | 381.5 ns | 114564.4 ns | 114563.6 ns |
+-------+-----------+-----------+-------------+--------------+
the generic memset_io()/memcpy_toio() build on __raw_write*(), which the
workaround promotes to store-release, so every store is individually
ordered - hence O(n) in the store count.
Tables 2 and 3 show why patch 2 is needed: the generic per-store block
writers collapse to O(n) under the workaround (4KB ~314x slower, ~115 us
vs ~366 ns), while the arm64 versions stay flat at one-barrier cost.
Changes since v3:
- Split the workaround into two patches: the erratum fix (1/2) and the
arm64 memset_io()/memcpy_toio() block writers (2/2).
- Reworked the raw MMIO write helpers to use a direct base-register
str*/stlr* alternative sequence instead of a per-write static branch.
- Covered the write-combining __iowrite{32,64}_copy() path by patching
dgh() to dmb osh on affected CPUs, keeping the contiguous STR groups
and the ordering barrier outside the copy loop; the single-element
case now uses a plain str* as well.
- Added arm64 memset_io()/memcpy_toio() so the byte/word block writers
take one trailing dmb osh instead of a per-store store-release.
- Updated the commit messages to describe the offset-addressing
trade-off.
Changes since v2:
- Reworked the raw MMIO write helpers so unaffected CPUs keep the
existing offset-addressed STR sequence, while affected CPUs use the
base-register STLR path.
- Updated the commit message to match the code changes.
- Rebased on top of the arm64 for-next/errata branch:
https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/log/?h=for-next/errata
Changes since v1:
- Updated the commit message based on feedback from Vladimir Murzin.
Shanker Donthineni (2):
arm64: errata: Workaround NVIDIA Olympus device store/load ordering
arm64: io: apply the device store-release workaround once per block
write
Documentation/arch/arm64/silicon-errata.rst | 2 +
arch/arm64/Kconfig | 25 +++++++++
arch/arm64/include/asm/barrier.h | 4 +-
arch/arm64/include/asm/io.h | 36 +++++++++----
arch/arm64/kernel/cpu_errata.c | 8 +++
arch/arm64/kernel/io.c | 82 +++++++++++++++++++++++++++++
arch/arm64/tools/cpucaps | 1 +
7 files changed, 146 insertions(+), 12 deletions(-)
--
2.54.0.windows.1
^ permalink raw reply
* [PATCH v4 2/2] arm64: io: apply the device store-release workaround once per block write
From: Shanker Donthineni @ 2026-06-25 18:24 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Vladimir Murzin
Cc: Jason Gunthorpe, linux-arm-kernel, Mark Rutland, linux-kernel,
linux-doc, Shanker Donthineni, Vikram Sethi, Jason Sequeira
In-Reply-To: <20260625182425.3194066-1-sdonthineni@nvidia.com>
The generic memset_io()/memcpy_toio() are built on __raw_write*(), so on
parts with the NVIDIA Olympus device store/load ordering erratum the
ARM64_WORKAROUND_DEVICE_STORE_RELEASE workaround promotes every store in
the block to a store-release. Each stlr* carries a barrier cost, so block
MMIO becomes O(n) store-releases, making a block copy many times slower
than a single ordered burst and growing with the transfer size.
Provide arm64 memset_io()/memcpy_toio() that emit plain str* in the loop
and order the whole block against subsequent loads with a single
trailing dmb osh on affected CPUs (a no-op elsewhere, preserving the
relaxed contract of these helpers). This keeps block MMIO writes at
one-barrier cost rather than scaling with the transfer size.
Performance (NVIDIA Olympus, write-combining MMIO to a device BAR, single
PE pinned; per-call cost in ns; consecutive writes ping-pong between two
buffers so repeated stores are not coalesced; iowrite64/iowrite32 =
__iowrite{64,32}_copy()):
Table 1 - arm64 memset_io/memcpy_toio (this patch)
+-------+-----------+-----------+-----------+-------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-----------+-------------+
| 8B | 231.6 ns | 231.6 ns | 232.4 ns | 232.4 ns |
| 16B | 231.7 ns | 231.9 ns | 232.7 ns | 232.6 ns |
| 32B | 231.9 ns | 232.7 ns | 232.9 ns | 232.9 ns |
| 64B | 232.7 ns | 235.0 ns | 233.7 ns | 233.6 ns |
| 128B | 233.6 ns | 235.8 ns | 234.4 ns | 234.3 ns |
| 256B | 237.7 ns | 276.8 ns | 264.0 ns | 276.7 ns |
| 512B | 237.7 ns | 277.1 ns | 238.1 ns | 277.6 ns |
| 1KB | 253.7 ns | 279.3 ns | 276.1 ns | 294.1 ns |
| 2KB | 295.0 ns | 318.7 ns | 288.5 ns | 308.3 ns |
| 4KB | 365.9 ns | 381.4 ns | 365.7 ns | 381.3 ns |
+-------+-----------+-----------+-----------+-------------+
all four helpers end with a single trailing barrier (dmb osh).
Table 2 - generic per-store memset_io/memcpy_toio
+-------+-----------+-----------+-------------+--------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-------------+--------------+
| 8B | 231.6 ns | 231.6 ns | 229.0 ns | 229.0 ns |
| 16B | 231.7 ns | 231.9 ns | 458.4 ns | 458.5 ns |
| 32B | 231.9 ns | 232.7 ns | 917.4 ns | 917.5 ns |
| 64B | 232.7 ns | 234.8 ns | 1835.4 ns | 1835.5 ns |
| 128B | 233.6 ns | 235.8 ns | 3670.9 ns | 3670.8 ns |
| 256B | 237.7 ns | 276.7 ns | 7341.6 ns | 7341.6 ns |
| 512B | 237.7 ns | 279.4 ns | 14001.4 ns | 14001.3 ns |
| 1KB | 253.7 ns | 279.1 ns | 28631.5 ns | 28631.8 ns |
| 2KB | 279.4 ns | 317.9 ns | 57276.3 ns | 57275.2 ns |
| 4KB | 365.7 ns | 381.5 ns | 114564.4 ns | 114563.6 ns |
+-------+-----------+-----------+-------------+--------------+
the generic memset_io()/memcpy_toio() build on __raw_write*(), which the
workaround promotes to store-release, so every store is individually
ordered - hence O(n) in the store count.
The arm64 versions stay flat at one-barrier cost while the generic
per-store writers collapse to O(n): at 4KB ~314x slower (~115 us vs
~366 ns).
Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
---
arch/arm64/include/asm/io.h | 5 +++
arch/arm64/kernel/io.c | 82 +++++++++++++++++++++++++++++++++++++
2 files changed, 87 insertions(+)
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 69e0fa004d31..649503f347bc 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -266,6 +266,11 @@ __iowrite64_copy(void __iomem *to, const void *from, size_t count)
}
#define __iowrite64_copy __iowrite64_copy
+void memset_io(volatile void __iomem *dst, int c, size_t count);
+#define memset_io memset_io
+void memcpy_toio(volatile void __iomem *dst, const void *src, size_t count);
+#define memcpy_toio memcpy_toio
+
/*
* I/O memory mapping functions.
*/
diff --git a/arch/arm64/kernel/io.c b/arch/arm64/kernel/io.c
index fe86ada23c7d..b5fd9ee6d9eb 100644
--- a/arch/arm64/kernel/io.c
+++ b/arch/arm64/kernel/io.c
@@ -5,9 +5,91 @@
* Copyright (C) 2012 ARM Ltd.
*/
+#include <linux/align.h>
#include <linux/export.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/unaligned.h>
+
+#include <asm/alternative.h>
+
+/*
+ * ARM64_WORKAROUND_DEVICE_STORE_RELEASE promotes every raw MMIO store
+ * (__raw_write*()) to a store-release on affected CPUs. The generic
+ * memset_io()/memcpy_toio() are built on those helpers, so the workaround would
+ * emit one store-release per element and turn a block write into O(n) ordered
+ * stores - far more costly than the single barrier a block actually needs.
+ *
+ * Provide arm64 versions that emit plain STR in the loop and order the whole
+ * block against subsequent loads with one trailing DMB OSH, patched in only on
+ * affected CPUs (a no-op elsewhere, so the relaxed contract of these helpers is
+ * preserved).
+ *
+ * This capability is currently enabled only for the NVIDIA Olympus device
+ * store/load ordering erratum, where a Device-nGnR* load may be observed before
+ * an older, non-overlapping Device-nGnR* store to the same peripheral.
+ */
+static __always_inline void iomem_block_store_barrier(void)
+{
+ asm volatile(ALTERNATIVE("nop", "dmb osh",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : : "memory");
+}
+
+void memset_io(volatile void __iomem *dst, int c, size_t count)
+{
+ u64 qc = (u8)c;
+
+ qc *= ~0ULL / 0xff;
+
+ while (count && !IS_ALIGNED((__force unsigned long)dst, sizeof(u64))) {
+ asm volatile("strb %w0, [%1]" : : "rZ"((u8)c), "r"(dst) : "memory");
+ dst++;
+ count--;
+ }
+ while (count >= sizeof(u64)) {
+ asm volatile("str %x0, [%1]" : : "rZ"(qc), "r"(dst) : "memory");
+ dst += sizeof(u64);
+ count -= sizeof(u64);
+ }
+ while (count) {
+ asm volatile("strb %w0, [%1]" : : "rZ"((u8)c), "r"(dst) : "memory");
+ dst++;
+ count--;
+ }
+
+ iomem_block_store_barrier();
+}
+EXPORT_SYMBOL(memset_io);
+
+void memcpy_toio(volatile void __iomem *dst, const void *src, size_t count)
+{
+ while (count && !IS_ALIGNED((__force unsigned long)dst, sizeof(u64))) {
+ asm volatile("strb %w0, [%1]"
+ : : "rZ"(*(const u8 *)src), "r"(dst) : "memory");
+ src++;
+ dst++;
+ count--;
+ }
+ while (count >= sizeof(u64)) {
+ asm volatile("str %x0, [%1]"
+ : : "rZ"(get_unaligned((const u64 *)src)), "r"(dst)
+ : "memory");
+ src += sizeof(u64);
+ dst += sizeof(u64);
+ count -= sizeof(u64);
+ }
+ while (count) {
+ asm volatile("strb %w0, [%1]"
+ : : "rZ"(*(const u8 *)src), "r"(dst) : "memory");
+ src++;
+ dst++;
+ count--;
+ }
+
+ iomem_block_store_barrier();
+}
+EXPORT_SYMBOL(memcpy_toio);
/*
* This generates a memcpy that works on a from/to address which is aligned to
--
2.54.0.windows.1
^ permalink raw reply related
* [PATCH v4 1/2] arm64: errata: Workaround NVIDIA Olympus device store/load ordering
From: Shanker Donthineni @ 2026-06-25 18:24 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Vladimir Murzin
Cc: Jason Gunthorpe, linux-arm-kernel, Mark Rutland, linux-kernel,
linux-doc, Shanker Donthineni, Vikram Sethi, Jason Sequeira
In-Reply-To: <20260625182425.3194066-1-sdonthineni@nvidia.com>
On systems with NVIDIA Olympus cores, a Device-nGnR* load can be
observed by a peripheral before an older, non-overlapping Device-nGnR*
store to the same peripheral. This breaks the program-order guarantee
that software expects for Device-nGnR* accesses and can leave a
peripheral in an incorrect state, as a load is observed before an
earlier store takes effect.
The erratum can occur only when all of the following apply:
- A PE executes a Device-nGnR* store followed by a younger
Device-nGnR* load.
- The store is not a store-release.
- The accesses target the same peripheral and do not overlap in bytes.
- There is at most one intervening Device-nGnR* store in program
order, and there are no intervening Device-nGnR* loads.
- There is no DSB, and no DMB that orders loads, between the store and
the load.
- Specific micro-architectural and timing conditions occur.
Promote the raw MMIO store helpers (__raw_writeb/w/l/q) from plain str*
to stlr* (Store-Release) on affected CPUs, which removes the "store is
not a store-release" condition for every device write the kernel issues.
Because writel() and writel_relaxed() are both built on __raw_writel()
in asm-generic/io.h, patching the raw variants covers both the
non-relaxed and relaxed APIs without touching the higher layers. Note
that writel()'s own barrier sits before the store, so it does not order
the store against a subsequent readl(); the store-release promotion is
what provides that ordering.
Like ARM64_ERRATUM_832075 on the load side, the change is gated on a new
ARM64_WORKAROUND_DEVICE_STORE_RELEASE capability and only activated on
parts that match MIDR_NVIDIA_OLYMPUS, so unaffected CPUs continue to use
plain str* instructions.
Note: stlr* only supports base-register addressing, so the raw MMIO
write helpers use a base-register str*/stlr* alternative sequence. This
gives up the offset-addressed str* code generation introduced by commit
d044d6ba6f02 ("arm64: io: permit offset addressing"). A static-branch
implementation would add extra control flow without preserving the
desired offset-addressed code generation in practice, so use a direct
base-register str*/stlr* alternative instead.
For the write-combining copy helpers (__iowrite{32,64}_copy()), the
contiguous str* groups are kept, because replacing those stores would
defeat the write-combining behaviour used to improve store performance.
Rather than rely on the relaxed, no-ordering contract of these helpers -
which would leave affected CPUs behaving differently from every other
arm64 system and exposed to any future driver that depends on ordering
across such copies - the DGH hint emitted once after each copy is
promoted to dmb osh on affected CPUs. That orders the grouped stores
against subsequent loads without placing a barrier in the copy loop,
while unaffected CPUs keep the existing DGH hint. The single-element
case of __const_memcpy_toio_aligned{32,64}() likewise uses a plain str*
(instead of __raw_write*()) so it shares that str* group + DGH path
rather than taking a per-store store-release.
Co-developed-by: Vikram Sethi <vsethi@nvidia.com>
Signed-off-by: Vikram Sethi <vsethi@nvidia.com>
Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
Link: https://lore.kernel.org/all/ajVZBJgKn-5sxHD6@willie-the-truck/
---
Documentation/arch/arm64/silicon-errata.rst | 2 ++
arch/arm64/Kconfig | 25 +++++++++++++++++
arch/arm64/include/asm/barrier.h | 4 ++-
arch/arm64/include/asm/io.h | 31 +++++++++++++--------
arch/arm64/kernel/cpu_errata.c | 8 ++++++
arch/arm64/tools/cpucaps | 1 +
6 files changed, 59 insertions(+), 12 deletions(-)
diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
index ad04d1cdc0f0..c4137f89acef 100644
--- a/Documentation/arch/arm64/silicon-errata.rst
+++ b/Documentation/arch/arm64/silicon-errata.rst
@@ -298,6 +298,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | Carmel Core | N/A | NVIDIA_CARMEL_CNP_ERRATUM |
+----------------+-----------------+-----------------+-----------------------------+
+| NVIDIA | Olympus core | T410-OLY-1027 | NVIDIA_OLYMPUS_1027_ERRATUM |
++----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | Olympus core | T410-OLY-1029 | ARM64_ERRATUM_4118414 |
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | T241 GICv3/4.x | T241-FABRIC-4 | N/A |
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 10c69474f276..da4e66b19209 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -564,6 +564,31 @@ config ARM64_ERRATUM_832075
If unsure, say Y.
+config NVIDIA_OLYMPUS_1027_ERRATUM
+ bool "NVIDIA Olympus: device store/load ordering erratum"
+ default y
+ help
+ This option adds an alternative code sequence to work around an
+ NVIDIA Olympus core erratum where a Device-nGnR* store can be
+ observed by a peripheral after a younger Device-nGnR* load to the
+ same peripheral. This breaks the program order that drivers rely
+ on for MMIO and can leave a device in an incorrect state.
+
+ The workaround promotes the raw MMIO store helpers
+ (__raw_writeb/w/l/q) to Store-Release (STLR), which restores the
+ required ordering. Because writel() and writel_relaxed() are built
+ on __raw_writel(), both are covered without changes to the higher
+ layers. It also promotes the DGH hint used after write-combining
+ memcpy-to-IO sequences to a DMB, so grouped stores are ordered
+ against subsequent reads without placing a barrier in the copy loop.
+
+ The fix is applied through the alternatives framework, so enabling
+ this option does not by itself activate the workaround: it is
+ patched in only when an affected CPU is detected, and is a no-op on
+ unaffected CPUs.
+
+ If unsure, say Y.
+
config ARM64_ERRATUM_834220
bool "Cortex-A57: 834220: Stage 2 translation fault might be incorrectly reported in presence of a Stage 1 fault (rare)"
depends on KVM
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 9495c4441a46..22792d1305aa 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -38,7 +38,9 @@
* Device-GRE attributes before the hint instruction with any memory accesses
* appearing after the hint instruction.
*/
-#define dgh() asm volatile("hint #6" : : : "memory")
+#define dgh() asm volatile(ALTERNATIVE("hint #6", "dmb osh", \
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE) \
+ : : : "memory")
#define spec_bar() asm volatile(ALTERNATIVE("dsb nsh\nisb\n", \
SB_BARRIER_INSN"nop\n", \
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 8cbd1e96fd50..69e0fa004d31 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -16,7 +16,6 @@
#include <asm/memory.h>
#include <asm/early_ioremap.h>
#include <asm/alternative.h>
-#include <asm/cpufeature.h>
#include <asm/rsi.h>
/*
@@ -25,29 +24,37 @@
#define __raw_writeb __raw_writeb
static __always_inline void __raw_writeb(u8 val, volatile void __iomem *addr)
{
- volatile u8 __iomem *ptr = addr;
- asm volatile("strb %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("strb %w0, [%1]",
+ "stlrb %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writew __raw_writew
static __always_inline void __raw_writew(u16 val, volatile void __iomem *addr)
{
- volatile u16 __iomem *ptr = addr;
- asm volatile("strh %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("strh %w0, [%1]",
+ "stlrh %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writel __raw_writel
static __always_inline void __raw_writel(u32 val, volatile void __iomem *addr)
{
- volatile u32 __iomem *ptr = addr;
- asm volatile("str %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("str %w0, [%1]",
+ "stlr %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writeq __raw_writeq
static __always_inline void __raw_writeq(u64 val, volatile void __iomem *addr)
{
- volatile u64 __iomem *ptr = addr;
- asm volatile("str %x0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("str %x0, [%1]",
+ "stlr %x0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_readb __raw_readb
@@ -178,7 +185,8 @@ __const_memcpy_toio_aligned32(volatile u32 __iomem *to, const u32 *from,
: "rZ"(from[0]), "rZ"(from[1]), "r"(to));
break;
case 1:
- __raw_writel(*from, to);
+ asm volatile("str %w0, [%1]"
+ : : "rZ"(from[0]), "r"(to) : "memory");
break;
default:
BUILD_BUG();
@@ -235,7 +243,8 @@ __const_memcpy_toio_aligned64(volatile u64 __iomem *to, const u64 *from,
: "rZ"(from[0]), "rZ"(from[1]), "r"(to));
break;
case 1:
- __raw_writeq(*from, to);
+ asm volatile("str %x0, [%1]"
+ : : "rZ"(from[0]), "r"(to) : "memory");
break;
default:
BUILD_BUG();
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 4b0d5d932897..76c1f8cf1ee0 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -839,6 +839,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
ERRATA_MIDR_ALL_VERSIONS(MIDR_NVIDIA_CARMEL),
},
#endif
+#ifdef CONFIG_NVIDIA_OLYMPUS_1027_ERRATUM
+ {
+ /* NVIDIA Olympus core */
+ .desc = "NVIDIA Olympus device load/store ordering erratum",
+ .capability = ARM64_WORKAROUND_DEVICE_STORE_RELEASE,
+ ERRATA_MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS),
+ },
+#endif
#ifdef CONFIG_ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE
{
/*
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 811c2479e82d..d367257bf770 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -120,6 +120,7 @@ WORKAROUND_CAVIUM_TX2_219_PRFM
WORKAROUND_CAVIUM_TX2_219_TVM
WORKAROUND_CLEAN_CACHE
WORKAROUND_DEVICE_LOAD_ACQUIRE
+WORKAROUND_DEVICE_STORE_RELEASE
WORKAROUND_NVIDIA_CARMEL_CNP
WORKAROUND_PMUV3_IMPDEF_TRAPS
WORKAROUND_QCOM_FALKOR_E1003
--
2.54.0.windows.1
^ permalink raw reply related
* [PATCH] ARM: remove unreachable invalid range check in kasan_init()
From: Sang-Heon Jeon @ 2026-06-25 18:40 UTC (permalink / raw)
To: Andrey Ryabinin, Russell King
Cc: Sang-Heon Jeon, Alexander Potapenko, Andrey Konovalov,
Dmitry Vyukov, kasan-dev, linux-arm-kernel, Vincenzo Frascino
kasan_init() maps each memblock region with for_each_mem_range(), which
guarantees pa_start < pa_end. Then it skips any region with
pa_start >= arm_lowmem_limit, so pa_start < arm_lowmem_limit is guaranteed
as well.
When pa_end <= arm_lowmem_limit, pa_start < pa_end means start < end, so
the start >= end check is unreachable.
When pa_end > arm_lowmem_limit, end is clamped to __va(arm_lowmem_limit),
and pa_start < arm_lowmem_limit means start < end, so the check is
unreachable as well.
No functional change.
Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
---
arch/arm/mm/kasan_init.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/arch/arm/mm/kasan_init.c b/arch/arm/mm/kasan_init.c
index c6625e808bf8..1f7c74c5df9e 100644
--- a/arch/arm/mm/kasan_init.c
+++ b/arch/arm/mm/kasan_init.c
@@ -262,12 +262,6 @@ void __init kasan_init(void)
&pa_start, &pa_end, &arm_lowmem_limit);
end = __va(arm_lowmem_limit);
}
- if (start >= end) {
- pr_info("Skipping invalid memory block %pa-%pa (virtual %p-%p)\n",
- &pa_start, &pa_end, start, end);
- continue;
- }
-
create_mapping(start, end);
}
--
2.43.0
^ permalink raw reply related
* RE: [PATCH v2 3/6] arm64: hyperv: Add per-CPU RSI host call infrastructure for CCA Realms
From: Michael Kelley @ 2026-06-25 18:58 UTC (permalink / raw)
To: Kameron Carr, kys@microsoft.com, haiyangz@microsoft.com,
wei.liu@kernel.org, decui@microsoft.com, longli@microsoft.com
Cc: catalin.marinas@arm.com, will@kernel.org, mark.rutland@arm.com,
lpieralisi@kernel.org, sudeep.holla@kernel.org, arnd@arndb.de,
thuth@redhat.com, linux-hyperv@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
Michael Kelley
In-Reply-To: <20260625173500.1995481-4-kameroncarr@linux.microsoft.com>
From: Kameron Carr <kameroncarr@linux.microsoft.com> Sent: Thursday, June 25, 2026 10:35 AM
> To: kys@microsoft.com; haiyangz@microsoft.com; wei.liu@kernel.org;
> decui@microsoft.com; longli@microsoft.com
> Cc: catalin.marinas@arm.com; will@kernel.org; mark.rutland@arm.com;
> lpieralisi@kernel.org; sudeep.holla@kernel.org; arnd@arndb.de; thuth@redhat.com; linux-
> hyperv@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
> kernel@vger.kernel.org; linux-arch@vger.kernel.org; mhklinux@outlook.com
> Subject: [PATCH v2 3/6] arm64: hyperv: Add per-CPU RSI host call infrastructure for CCA
> Realms
>
> Arm CCA Realms cannot issue Hyper-V hypercalls via HVC; the guest must
> route them through the RSI_HOST_CALL interface, which takes the IPA of a
> per-CPU rsi_host_call structure as its argument.
>
> Add hv_hostcall_array as a per-CPU struct array and allocate it during
> hyperv_init(). The allocation is gated on is_realm_world() so non-Realm
> arm64 Hyper-V guests pay no memory cost.
>
> Signed-off-by: Kameron Carr <kameroncarr@linux.microsoft.com>
> ---
> arch/arm64/hyperv/mshyperv.c | 32 ++++++++++++++++++++++++++++++-
> arch/arm64/include/asm/mshyperv.h | 4 ++++
> 2 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
> index 4fdc26ade1d74..7d536d7fb557e 100644
> --- a/arch/arm64/hyperv/mshyperv.c
> +++ b/arch/arm64/hyperv/mshyperv.c
> @@ -15,10 +15,15 @@
> #include <linux/errno.h>
> #include <linux/version.h>
> #include <linux/cpuhotplug.h>
> +#include <linux/slab.h>
> #include <asm/mshyperv.h>
> +#include <asm/rsi.h>
>
> static bool hyperv_initialized;
>
> +struct rsi_host_call *hv_hostcall_array;
> +EXPORT_SYMBOL_GPL(hv_hostcall_array);
> +
> int hv_get_hypervisor_version(union hv_hypervisor_version_info *info)
> {
> hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION,
> @@ -60,6 +65,12 @@ static bool __init hyperv_detect_via_acpi(void)
>
> #endif
>
> +static void hv_hostcall_free(void)
> +{
> + kfree(hv_hostcall_array);
> + hv_hostcall_array = NULL;
> +}
> +
> static bool __init hyperv_detect_via_smccc(void)
> {
> uuid_t hyperv_uuid = UUID_INIT(
> @@ -85,6 +96,20 @@ static int __init hyperv_init(void)
> if (!hyperv_detect_via_acpi() && !hyperv_detect_via_smccc())
> return 0;
>
> + /*
> + * The RSI host-call buffers are only ever used when
> + * is_realm_world() is true. Skip the allocation on non-Realm
> + * guests. A single contiguous array of nr_cpu_ids entries is
> + * allocated; each CPU indexes into it by its processor ID.
> + */
> + if (is_realm_world()) {
> + hv_hostcall_array = kcalloc(nr_cpu_ids,
> + sizeof(struct rsi_host_call),
> + GFP_KERNEL);
> + if (!hv_hostcall_array)
> + return -ENOMEM;
> + }
> +
> /* Setup the guest ID */
> guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
> hv_set_vpreg(HV_REGISTER_GUEST_OS_ID, guest_id);
> @@ -106,12 +131,13 @@ static int __init hyperv_init(void)
>
> ret = hv_common_init();
> if (ret)
> - return ret;
> + goto free_hostcall_mem;
>
> ret = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "arm64/hyperv_init:online",
> hv_common_cpu_init, hv_common_cpu_die);
> if (ret < 0) {
> hv_common_free();
> + hv_hostcall_free();
> return ret;
Let me suggest a small additional simplification. For this error
path, call hv_common_free() as you have now, but then do
"goto free_hostcall_mem". At the free_hostcall_mem label, do
kfree(hv_hostcall_array);
hv_hostcall_array = NULL;
directly inline, and eliminate the hv_hostcall_free() helper
function. Saves about 5 lines of code overall and I think is a
bit simpler.
> }
>
> @@ -125,6 +151,10 @@ static int __init hyperv_init(void)
>
> hyperv_initialized = true;
> return 0;
> +
> +free_hostcall_mem:
> + hv_hostcall_free();
> + return ret;
> }
>
> early_initcall(hyperv_init);
> diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
> index b721d3134ab66..c207a3f79b99b 100644
> --- a/arch/arm64/include/asm/mshyperv.h
> +++ b/arch/arm64/include/asm/mshyperv.h
> @@ -63,4 +63,8 @@ static inline u64 hv_get_non_nested_msr(unsigned int reg)
>
> #include <asm-generic/mshyperv.h>
>
> +/* Per-CPU-indexed RSI host call structures for CCA Realms */
> +struct rsi_host_call;
> +extern struct rsi_host_call *hv_hostcall_array;
> +
The intent is that the #include of asm-generic/mshyperv.h should be
last in the arch-specific version of mshyperv.h. If there's a need to go
after the #include, that's a red flag to check if some restructuring of
the definitions would be appropriate.
Unless I'm missing something, I think these new definitions can go
above the #include.
Michael
> #endif
> --
> 2.45.4
>
^ permalink raw reply
* RE: [PATCH v2 4/6] Drivers: hv: Mark shared memory as decrypted for CCA Realms
From: Michael Kelley @ 2026-06-25 18:58 UTC (permalink / raw)
To: Kameron Carr, kys@microsoft.com, haiyangz@microsoft.com,
wei.liu@kernel.org, decui@microsoft.com, longli@microsoft.com
Cc: catalin.marinas@arm.com, will@kernel.org, mark.rutland@arm.com,
lpieralisi@kernel.org, sudeep.holla@kernel.org, arnd@arndb.de,
thuth@redhat.com, linux-hyperv@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
Michael Kelley
In-Reply-To: <20260625173500.1995481-5-kameroncarr@linux.microsoft.com>
From: Kameron Carr <kameroncarr@linux.microsoft.com> Sent: Thursday, June 25, 2026 10:35 AM
>
> In hv_common_cpu_init(), the per-CPU hypercall input/output pages need
> to be marked as decrypted (shared) for confidential VM isolation types.
> This is already done for SNP and TDX isolation; extend the same handling
> to Arm CCA Realm guests so that the host hypervisor can access the
> shared hypercall buffers.
>
> We need to round up the memory allocated for the input/output pages to
> the nearest PAGE_SIZE, since set_memory_decrypted() requires the size to
> be a multiple of PAGE_SIZE. This only has an effect on ARM VMs that are
> using PAGE_SIZE larger than 4K.
I think this change resulted from a Sashiko comment. My understanding is
that the ARM CCA architecture only supports CCA guests with 4 KiB page
size. Is that still the case, or has that restriction been lifted in a later version
of the architecture? I'm in favor of handling the larger page sizes, if only for
future proofing. But I wondered whether your intent is to always support
> 4 KiB page sizes even if CCA doesn't support them now. Another way to
put it: In reviewing code, should I flag issues related to page sizes > 4 KiB?
>
> is_realm_world() is only declared in arch/arm64/include/asm/rsi.h, so
> using it directly in the arch-neutral drivers/hv/hv_common.c would
> break the x86 build. Introduce a Hyper-V-specific helper following the
> established hv_isolation_type_snp() / hv_isolation_type_tdx() pattern.
>
> On architectures other than arm64 the weak default keeps the existing
> behaviour.
>
> Signed-off-by: Kameron Carr <kameroncarr@linux.microsoft.com>
> ---
> arch/arm64/hyperv/mshyperv.c | 5 +++++
> drivers/hv/hv_common.c | 17 +++++++++++++----
> include/asm-generic/mshyperv.h | 1 +
> 3 files changed, 19 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
> index 7d536d7fb557e..8e8148b723d9c 100644
> --- a/arch/arm64/hyperv/mshyperv.c
> +++ b/arch/arm64/hyperv/mshyperv.c
> @@ -164,3 +164,8 @@ bool hv_is_hyperv_initialized(void)
> return hyperv_initialized;
> }
> EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
> +
> +bool hv_isolation_type_cca(void)
> +{
> + return is_realm_world();
> +}
> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
> index 6b67ac6167891..17048a0a18729 100644
> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
> @@ -476,6 +476,7 @@ int hv_common_cpu_init(unsigned int cpu)
> u64 msr_vp_index;
> gfp_t flags;
> const int pgcount = hv_output_page_exists() ? 2 : 1;
> + const size_t alloc_size = ALIGN((size_t)pgcount * HV_HYP_PAGE_SIZE, PAGE_SIZE);
> void *mem;
> int ret = 0;
>
> @@ -489,7 +490,7 @@ int hv_common_cpu_init(unsigned int cpu)
> * online and then taken offline
> */
> if (!*inputarg) {
> - mem = kmalloc_array(pgcount, HV_HYP_PAGE_SIZE, flags);
> + mem = kmalloc(alloc_size, flags);
> if (!mem)
> return -ENOMEM;
>
> @@ -499,14 +500,16 @@ int hv_common_cpu_init(unsigned int cpu)
> }
>
> if (!ms_hyperv.paravisor_present &&
> - (hv_isolation_type_snp() || hv_isolation_type_tdx())) {
> - ret = set_memory_decrypted((unsigned long)mem, pgcount);
> + (hv_isolation_type_snp() || hv_isolation_type_tdx() ||
> + hv_isolation_type_cca())) {
> + ret = set_memory_decrypted((unsigned long)kasan_reset_tag(mem),
> + alloc_size >> PAGE_SHIFT);
I don't know enough about KASAN or the tag situation on ARM64
to comment on this change. But this same sequence of allocating
memory and then decrypting it occurs in other places in Hyper-V
code. It seems like those places would also need the same
kasan_reset_tag() call.
Michael
> if (ret) {
> /* It may be unsafe to free 'mem' */
> return ret;
> }
>
> - memset(mem, 0x00, pgcount * HV_HYP_PAGE_SIZE);
> + memset(mem, 0x00, alloc_size);
> }
>
> /*
> @@ -666,6 +669,12 @@ bool __weak hv_isolation_type_tdx(void)
> }
> EXPORT_SYMBOL_GPL(hv_isolation_type_tdx);
>
> +bool __weak hv_isolation_type_cca(void)
> +{
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(hv_isolation_type_cca);
> +
> void __weak hv_setup_vmbus_handler(void (*handler)(void))
> {
> }
> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
> index bf601d67cecb9..1fa79abce743c 100644
> --- a/include/asm-generic/mshyperv.h
> +++ b/include/asm-generic/mshyperv.h
> @@ -79,6 +79,7 @@ u64 hv_do_fast_hypercall16(u16 control, u64 input1, u64 input2);
>
> bool hv_isolation_type_snp(void);
> bool hv_isolation_type_tdx(void);
> +bool hv_isolation_type_cca(void);
>
> /*
> * On architectures where Hyper-V doesn't support AEOI (e.g., ARM64),
> --
> 2.45.4
>
^ permalink raw reply
* [PATCH v7 26/27] phy: rockchip: usbdp: Add some extra debug messages
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
It's useful to log PHY reinit to ease debugging issues around
USB-C hotplugging.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index fc788f2bf5fb..8c5d6b8595e2 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -24,6 +24,7 @@
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h>
+#include <linux/string_choices.h>
#include <linux/usb/ch9.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
@@ -494,6 +495,8 @@ static void rk_udphy_u3_port_disable(struct rk_udphy *udphy, u8 disable)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
const struct rk_udphy_grf_reg *preg;
+ dev_dbg(udphy->dev, "USB3 port %s\n", str_on_off(!disable));
+
preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg;
rk_udphy_grfreg_write(udphy->usbgrf, preg, disable);
}
@@ -787,6 +790,10 @@ static int rk_udphy_init(struct rk_udphy *udphy)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
int ret;
+ dev_dbg(udphy->dev, "(re-)init PHY with USB=%s and DP=%s\n",
+ str_enabled_disabled(udphy->mode & UDPHY_MODE_USB),
+ str_enabled_disabled(udphy->mode & UDPHY_MODE_DP));
+
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
@@ -857,6 +864,8 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
{
int ret;
+ dev_dbg(udphy->dev, "enable PHY\n");
+
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
if (ret) {
dev_err(udphy->dev, "failed to enable clk\n");
@@ -875,6 +884,7 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
static void rk_udphy_disable(struct rk_udphy *udphy)
{
+ dev_dbg(udphy->dev, "disable PHY\n");
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
rk_udphy_reset_assert_all(udphy);
}
--
2.53.0
^ permalink raw reply related
* [PATCH v7 16/27] phy: rockchip: usbdp: Cleanup DP lane selection function
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Use FIELD_PREP_WM16() helpers to simplify the DP lane selection
logic.
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 28 +++++++---------------------
1 file changed, 7 insertions(+), 21 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 1bae8172d1fe..71ee2f4faf0b 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -553,30 +553,16 @@ static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
- u32 value = 0;
-
- switch (udphy->dp_lanes) {
- case 4:
- value |= 3 << udphy->dp_lane_sel[3] * 2;
- value |= 2 << udphy->dp_lane_sel[2] * 2;
- fallthrough;
-
- case 2:
- value |= 1 << udphy->dp_lane_sel[1] * 2;
- fallthrough;
+ u32 value = FIELD_PREP_WM16(DP_LANE_SEL_ALL, 0);
+ int i;
- case 1:
- value |= 0 << udphy->dp_lane_sel[0] * 2;
- break;
+ for (i = 0; i < udphy->dp_lanes; i++)
+ value |= field_prep(DP_LANE_SEL_N(udphy->dp_lane_sel[i]), i);
- default:
- break;
- }
+ value |= FIELD_PREP_WM16(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel);
+ value |= FIELD_PREP_WM16(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel);
- regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg,
- ((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) |
- FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) |
- FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
+ regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg, value);
}
static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v14 4/5] gpio: rpmsg: add generic rpmsg GPIO driver
From: Andrew Davis @ 2026-06-25 20:32 UTC (permalink / raw)
To: Shenwei Wang, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Mathieu Poirier, Frank Li, Sascha Hauer
Cc: Shuah Khan, linux-gpio, linux-doc, linux-kernel,
Pengutronix Kernel Team, Fabio Estevam, Shenwei Wang, Peng Fan,
devicetree, linux-remoteproc, imx, linux-arm-kernel, linux-imx,
Arnaud POULIQUEN, b-padhi, Andrew Lunn, Bartosz Golaszewski
In-Reply-To: <20260625155432.815185-5-shenwei.wang@oss.nxp.com>
On 6/25/26 10:54 AM, Shenwei Wang wrote:
> From: Shenwei Wang <shenwei.wang@nxp.com>
>
> On an AMP platform, the system may include multiple processors:
> - MCUs running an RTOS
> - An MPU running Linux
>
> These processors communicate via the RPMSG protocol.
> The driver implements the standard GPIO interface, allowing
> the Linux side to control GPIO controllers which reside in
> the remote processor via RPMSG protocol.
>
> Cc: Bartosz Golaszewski <brgl@bgdev.pl>
> Cc: Andrew Lunn <andrew@lunn.ch>
> Signed-off-by: Shenwei Wang <shenwei.wang@nxp.com>
> ---
> drivers/gpio/Kconfig | 17 ++
> drivers/gpio/Makefile | 1 +
> drivers/gpio/gpio-rpmsg.c | 568 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 586 insertions(+)
> create mode 100644 drivers/gpio/gpio-rpmsg.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 020e51e30317..4ad299fe3c6f 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -1917,6 +1917,23 @@ config GPIO_SODAVILLE
>
> endmenu
>
> +menu "RPMSG GPIO drivers"
> + depends on RPMSG
> +
> +config GPIO_RPMSG
> + tristate "Generic RPMSG GPIO support"
> + depends on OF && REMOTEPROC
> + select GPIOLIB_IRQCHIP
> + default REMOTEPROC
> + help
> + Say yes here to support the generic GPIO functions over the RPMSG
> + bus. Currently supported devices: i.MX7ULP, i.MX8ULP, i.MX8x, and
> + i.MX9x.
The support would depend on if the right firmware is loaded/running on the given
remote core. Also if you want to make this generic, then any vendor should be able
to make a firmware that implements this protocol and make use of this driver.
Suggest dropping this NXP specific device list.
> +
> + If unsure, say N.
> +
> +endmenu
> +
> menu "SPI GPIO expanders"
> depends on SPI_MASTER
>
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index b267598b517d..ee75c0e65b8b 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -157,6 +157,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
> obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
> obj-$(CONFIG_GPIO_REG) += gpio-reg.o
> obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
> +obj-$(CONFIG_GPIO_RPMSG) += gpio-rpmsg.o
> obj-$(CONFIG_GPIO_RTD) += gpio-rtd.o
> obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
> obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
> diff --git a/drivers/gpio/gpio-rpmsg.c b/drivers/gpio/gpio-rpmsg.c
> new file mode 100644
> index 000000000000..332e2925a830
> --- /dev/null
> +++ b/drivers/gpio/gpio-rpmsg.c
> @@ -0,0 +1,568 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2026 NXP
> + *
> + * The driver exports a standard gpiochip interface to control
> + * the GPIO controllers via RPMSG on a remote processor.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/init.h>
> +#include <linux/irqdomain.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/remoteproc.h>
> +#include <linux/rpmsg.h>
> +#include <linux/virtio_gpio.h>
> +
> +#define GPIOS_PER_PORT_DEFAULT 32
> +#define RPMSG_TIMEOUT 1000
> +
> +/* Additional commands beyond virtio-gpio */
> +#define VIRTIO_GPIO_MSG_SET_WAKEUP 0x0010
> +
> +/* GPIO Receive MSG Type */
> +#define GPIO_RPMSG_REPLY 1
> +#define GPIO_RPMSG_NOTIFY 2
> +
> +#define CHAN_NAME_PREFIX "rpmsg-io-"
> +#define GPIO_COMPAT_STR "rpmsg-gpio"
> +
> +struct rpmsg_gpio_response {
> + __u8 type;
> + union {
> + /* command reply */
> + struct {
> + __u8 status;
> + __u8 value;
> + };
> +
> + /* interrupt notification */
> + struct {
> + __u8 line;
> + __u8 trigger; /* rising/falling/high/low */
> + };
> + };
> +};
> +
> +struct rpmsg_gpio_line {
> + u8 irq_shutdown;
> + u8 irq_unmask;
> + u8 irq_mask;
> + u32 irq_wake_enable;
> + u32 irq_type;
> +};
> +
> +struct rpmsg_gpio_port {
> + struct gpio_chip gc;
> + struct rpmsg_device *rpdev;
> + struct virtio_gpio_request *send_msg;
> + struct rpmsg_gpio_response *recv_msg;
> + struct completion cmd_complete;
> + struct mutex lock;
> + u32 ngpios;
> + u32 idx;
> + struct rpmsg_gpio_line lines[GPIOS_PER_PORT_DEFAULT];
> +};
> +
> +static int rpmsg_gpio_send_message(struct rpmsg_gpio_port *port)
> +{
> + int ret;
> +
> + reinit_completion(&port->cmd_complete);
> +
> + ret = rpmsg_send(port->rpdev->ept, port->send_msg, sizeof(*port->send_msg));
> + if (ret) {
> + dev_err(&port->rpdev->dev, "rpmsg_send failed: cmd=%d ret=%d\n",
> + port->send_msg->type, ret);
> + return ret;
> + }
> +
> + ret = wait_for_completion_timeout(&port->cmd_complete,
> + msecs_to_jiffies(RPMSG_TIMEOUT));
> + if (ret == 0) {
> + dev_err(&port->rpdev->dev, "rpmsg_send timeout! cmd=%d\n",
> + port->send_msg->type);
> + return -ETIMEDOUT;
> + }
> +
> + if (unlikely(port->recv_msg->status != VIRTIO_GPIO_STATUS_OK)) {
> + dev_err(&port->rpdev->dev, "remote core replies an error: cmd=%d!\n",
> + port->send_msg->type);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static struct virtio_gpio_request *
> +rpmsg_gpio_msg_prepare(struct rpmsg_gpio_port *port, u16 line, u16 cmd, u32 val)
> +{
> + struct virtio_gpio_request *msg = port->send_msg;
> +
> + msg->type = cmd;
> + msg->gpio = line;
> + msg->value = val;
> +
> + return msg;
> +}
> +
> +static int rpmsg_gpio_get(struct gpio_chip *gc, unsigned int line)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> + int ret;
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_GET_VALUE, 0);
> +
> + ret = rpmsg_gpio_send_message(port);
> + return ret ? ret : port->recv_msg->value;
> +}
> +
> +static int rpmsg_gpio_get_direction(struct gpio_chip *gc, unsigned int line)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> + int ret;
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_GET_DIRECTION, 0);
> +
> + ret = rpmsg_gpio_send_message(port);
> + if (ret)
> + return ret;
> +
> + switch (port->recv_msg->value) {
> + case VIRTIO_GPIO_DIRECTION_IN:
> + return GPIO_LINE_DIRECTION_IN;
> + case VIRTIO_GPIO_DIRECTION_OUT:
> + return GPIO_LINE_DIRECTION_OUT;
> + default:
> + break;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int rpmsg_gpio_direction_input(struct gpio_chip *gc, unsigned int line)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_DIRECTION,
> + VIRTIO_GPIO_DIRECTION_IN);
> +
> + return rpmsg_gpio_send_message(port);
> +}
> +
> +static int rpmsg_gpio_set(struct gpio_chip *gc, unsigned int line, int val)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_VALUE, val);
> +
> + return rpmsg_gpio_send_message(port);
> +}
> +
> +static int rpmsg_gpio_direction_output(struct gpio_chip *gc, unsigned int line, int val)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> + int ret;
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_DIRECTION,
> + VIRTIO_GPIO_DIRECTION_OUT);
> +
> + ret = rpmsg_gpio_send_message(port);
> + if (ret)
> + return ret;
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_VALUE, val);
> +
> + return rpmsg_gpio_send_message(port);
> +}
> +
> +static int gpio_rpmsg_irq_set_type(struct irq_data *d, u32 type)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + switch (type) {
> + case IRQ_TYPE_EDGE_RISING:
> + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING;
> + irq_set_handler_locked(d, handle_simple_irq);
> + break;
> + case IRQ_TYPE_EDGE_FALLING:
> + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING;
> + irq_set_handler_locked(d, handle_simple_irq);
> + break;
> + case IRQ_TYPE_EDGE_BOTH:
> + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH;
> + irq_set_handler_locked(d, handle_simple_irq);
> + break;
> + case IRQ_TYPE_LEVEL_LOW:
> + type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW;
> + irq_set_handler_locked(d, handle_level_irq);
> + break;
> + case IRQ_TYPE_LEVEL_HIGH:
> + type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH;
> + irq_set_handler_locked(d, handle_level_irq);
> + break;
> + default:
> + dev_err(&port->rpdev->dev, "unsupported irq type: %u\n", type);
> + return -EINVAL;
> + }
> +
> + port->lines[line].irq_type = type;
> +
> + return 0;
> +}
> +
> +static int gpio_rpmsg_irq_set_wake(struct irq_data *d, u32 enable)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + port->lines[line].irq_wake_enable = enable;
> +
> + return 0;
> +}
> +
> +/*
> + * This unmask/mask function is invoked in two situations:
> + * - when an interrupt is being set up, and
> + * - after an interrupt has occurred.
> + *
> + * The GPIO driver does not access hardware registers directly.
> + * Instead, it caches all relevant information locally, and then sends
> + * the accumulated state to the remote system at this stage.
> + */
> +static void gpio_rpmsg_unmask_irq(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + port->lines[line].irq_unmask = 1;
> +}
> +
> +static void gpio_rpmsg_mask_irq(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + /*
> + * When an interrupt occurs, the remote system masks the interrupt
> + * and then sends a notification to Linux. After Linux processes
> + * that notification, it sends an RPMsg command back to the remote
> + * system to unmask the interrupt again.
> + */
> + port->lines[line].irq_mask = 1;
> +}
> +
> +static void gpio_rpmsg_irq_shutdown(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + port->lines[line].irq_shutdown = 1;
> +}
> +
> +static void gpio_rpmsg_irq_bus_lock(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> +
> + mutex_lock(&port->lock);
> +}
> +
> +static void gpio_rpmsg_irq_bus_sync_unlock(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_WAKEUP,
> + port->lines[line].irq_wake_enable);
> + rpmsg_gpio_send_message(port);
> +
> + /*
> + * For mask irq, do nothing here.
> + * The remote system will mask interrupt after an interrupt occurs,
> + * and then send a notification to Linux system. After Linux system
> + * handles the notification, it sends an rpmsg back to the remote
> + * system to unmask this interrupt again.
> + */
> + if (port->lines[line].irq_mask && !port->lines[line].irq_unmask) {
> + port->lines[line].irq_mask = 0;
> + mutex_unlock(&port->lock);
> + return;
> + }
> +
> + if (port->lines[line].irq_shutdown) {
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_IRQ_TYPE,
> + VIRTIO_GPIO_IRQ_TYPE_NONE);
> + port->lines[line].irq_shutdown = 0;
> + } else {
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_IRQ_TYPE,
> + port->lines[line].irq_type);
> +
> + if (port->lines[line].irq_unmask)
> + port->lines[line].irq_unmask = 0;
> + }
> +
> + rpmsg_gpio_send_message(port);
> + mutex_unlock(&port->lock);
> +}
> +
> +static const struct irq_chip gpio_rpmsg_irq_chip = {
> + .irq_mask = gpio_rpmsg_mask_irq,
> + .irq_unmask = gpio_rpmsg_unmask_irq,
> + .irq_set_wake = gpio_rpmsg_irq_set_wake,
> + .irq_set_type = gpio_rpmsg_irq_set_type,
> + .irq_shutdown = gpio_rpmsg_irq_shutdown,
> + .irq_bus_lock = gpio_rpmsg_irq_bus_lock,
> + .irq_bus_sync_unlock = gpio_rpmsg_irq_bus_sync_unlock,
> + .flags = IRQCHIP_IMMUTABLE,
> +};
> +
> +static int rpmsg_gpiochip_register(struct rpmsg_device *rpdev,
> + struct device_node *np, const char *name)
> +{
> + struct rpmsg_gpio_port *port;
> + struct gpio_irq_chip *girq;
> + struct gpio_chip *gc;
> + int ret;
> +
> + port = devm_kzalloc(&rpdev->dev, sizeof(*port), GFP_KERNEL);
> + if (!port)
> + return -ENOMEM;
> +
> + ret = of_property_read_u32(np, "reg", &port->idx);
> + if (ret)
> + return ret;
> +
> + ret = devm_mutex_init(&rpdev->dev, &port->lock);
> + if (ret)
> + return ret;
> +
> + ret = of_property_read_u32(np, "ngpios", &port->ngpios);
> + if (ret || port->ngpios > GPIOS_PER_PORT_DEFAULT)
> + port->ngpios = GPIOS_PER_PORT_DEFAULT;
> +
> + port->send_msg = devm_kzalloc(&rpdev->dev,
> + sizeof(*port->send_msg),
> + GFP_KERNEL);
> +
> + port->recv_msg = devm_kzalloc(&rpdev->dev,
> + sizeof(*port->recv_msg),
> + GFP_KERNEL);
> + if (!port->send_msg || !port->recv_msg)
> + return -ENOMEM;
> +
> + init_completion(&port->cmd_complete);
> + port->rpdev = rpdev;
> +
> + gc = &port->gc;
> + gc->owner = THIS_MODULE;
> + gc->parent = &rpdev->dev;
> + gc->fwnode = of_fwnode_handle(np);
> + gc->ngpio = port->ngpios;
> + gc->base = -1;
> + gc->label = devm_kasprintf(&rpdev->dev, GFP_KERNEL, "%s-gpio%d",
> + name, port->idx);
> +
> + gc->direction_input = rpmsg_gpio_direction_input;
> + gc->direction_output = rpmsg_gpio_direction_output;
> + gc->get_direction = rpmsg_gpio_get_direction;
> + gc->get = rpmsg_gpio_get;
> + gc->set = rpmsg_gpio_set;
> +
> + girq = &gc->irq;
> + gpio_irq_chip_set_chip(girq, &gpio_rpmsg_irq_chip);
> + girq->parent_handler = NULL;
> + girq->num_parents = 0;
> + girq->parents = NULL;
> + girq->chip->name = devm_kstrdup(&rpdev->dev, gc->label, GFP_KERNEL);
> +
> + dev_set_drvdata(&rpdev->dev, port);
> +
> + return devm_gpiochip_add_data(&rpdev->dev, gc, port);
> +}
> +
> +static const char *rpmsg_get_rproc_node_name(struct rpmsg_device *rpdev)
> +{
> + const char *name = NULL;
> + struct device_node *np;
> + struct rproc *rproc;
> +
> + rproc = rproc_get_by_child(&rpdev->dev);
> + if (!rproc)
> + return NULL;
> +
> + np = of_node_get(rproc->dev.of_node);
> + if (!np && rproc->dev.parent)
> + np = of_node_get(rproc->dev.parent->of_node);
> +
> + if (np) {
> + name = devm_kstrdup(&rpdev->dev, np->name, GFP_KERNEL);
> + of_node_put(np);
> + }
> +
> + return name;
> +}
> +
> +static struct device_node *
> +rpmsg_find_child_by_compat_reg(struct device_node *parent, const char *compat, u32 idx)
> +{
> + struct device_node *child;
> + u32 reg;
> +
> + for_each_available_child_of_node(parent, child) {
> + if (!of_device_is_compatible(child, compat))
> + continue;
> +
> + if (of_property_read_u32(child, "reg", ®))
> + continue;
> +
> + if (reg == idx)
> + return child;
> + }
> +
> + return NULL;
> +}
> +
> +static struct device_node *
> +rpmsg_get_channel_ofnode(struct rpmsg_device *rpdev, const char *compat, u32 idx)
> +{
> + struct device_node *np_chan = NULL, *np;
> + struct rproc *rproc;
> +
> + rproc = rproc_get_by_child(&rpdev->dev);
> + if (!rproc)
> + return NULL;
> +
> + np = of_node_get(rproc->dev.of_node);
> + if (!np && rproc->dev.parent)
> + np = of_node_get(rproc->dev.parent->of_node);
> +
> + if (np)
> + np_chan = rpmsg_find_child_by_compat_reg(np, compat, idx);
> +
> + return np_chan;
> +}
> +
> +static int rpmsg_get_gpio_index(const char *name, const char *prefix)
> +{
> + const char *p;
> + int base = 10;
> + int val;
> +
> + if (!name)
> + return -EINVAL;
> +
> + /* Ensure correct prefix */
> + if (!str_has_prefix(name, prefix))
> + return -EINVAL;
> +
> + /* Find last '-' */
> + p = strrchr(name, '-');
> +
> + if (!p || *(p + 1) == '\0')
> + return -EINVAL;
> +
> + if (p[1] == '0' && (p[2] == 'x' || p[2] == 'X'))
> + base = 16;
> +
> + if (kstrtoint(p + 1, base, &val))
> + return -EINVAL;
> +
> + return val;
> +}
> +
> +static int rpmsg_gpio_channel_callback(struct rpmsg_device *rpdev, void *data,
> + int len, void *priv, u32 src)
> +{
> + struct rpmsg_gpio_response *msg = data;
> + struct rpmsg_gpio_port *port = NULL;
> +
> + port = dev_get_drvdata(&rpdev->dev);
> +
> + if (!port) {
> + dev_err(&rpdev->dev, "port is null\n");
> + return -EINVAL;
> + }
> +
> + if (msg->type == GPIO_RPMSG_REPLY) {
> + *port->recv_msg = *msg;
> + complete(&port->cmd_complete);
> + } else if (msg->type == GPIO_RPMSG_NOTIFY) {
> + generic_handle_domain_irq_safe(port->gc.irq.domain, msg->line);
> + } else {
> + dev_err(&rpdev->dev, "wrong message type (0x%x)\n", msg->type);
> + }
> +
> + return 0;
> +}
> +
> +static int rpmsg_gpio_channel_probe(struct rpmsg_device *rpdev)
> +{
> + struct device *dev = &rpdev->dev;
> + struct device_node *np;
> + const char *rproc_name;
> + int idx;
> +
> + idx = rpmsg_get_gpio_index(rpdev->id.name, CHAN_NAME_PREFIX);
> + if (idx < 0)
> + return -EINVAL;
> +
> + if (!dev->of_node) {
> + np = rpmsg_get_channel_ofnode(rpdev, GPIO_COMPAT_STR, idx);
> + if (!np)
> + return -ENODEV;
This seems to imply that DT nodes are required. RPMSG is a discoverable
bus with a nameservice that can bind/probe new devices. While then optionally
binding to a DT node when available so sub-devices can be described in DT is
fine, I don't see why it should be required.
> +
> + dev->of_node = np;
> + set_primary_fwnode(dev, of_fwnode_handle(np));
> + return -EPROBE_DEFER;
> + }
> +
> + rproc_name = rpmsg_get_rproc_node_name(rpdev);
> +
> + return rpmsg_gpiochip_register(rpdev, dev->of_node, rproc_name);
> +}
> +
> +static const struct of_device_id rpmsg_gpio_dt_ids[] = {
> + { .compatible = GPIO_COMPAT_STR },
> + { /* sentinel */ }
> +};
> +
> +static struct rpmsg_device_id rpmsg_gpio_channel_id_table[] = {
> + { .name = CHAN_NAME_PREFIX },
> + { },
> +};
> +MODULE_DEVICE_TABLE(rpmsg, rpmsg_gpio_channel_id_table);
> +
> +static struct rpmsg_driver rpmsg_gpio_channel_client = {
> + .callback = rpmsg_gpio_channel_callback,
> + .id_table = rpmsg_gpio_channel_id_table,
> + .probe = rpmsg_gpio_channel_probe,
> + .drv = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = rpmsg_gpio_dt_ids,
Does this line actually do anything anymore? Maybe it did when
this was a platform_driver, but this is a rpmsg_driver and
will probe though .id_table matches.
Andrew
> + },
> +};
> +module_rpmsg_driver(rpmsg_gpio_channel_client);
> +
> +MODULE_AUTHOR("Shenwei Wang <shenwei.wang@nxp.com>");
> +MODULE_DESCRIPTION("generic rpmsg gpio driver");
> +MODULE_LICENSE("GPL");
^ permalink raw reply
* Re: [PATCH 2/2] arm64: dts: ti: Add support for the phyCORE-AM67x
From: Andrew Davis @ 2026-06-25 20:37 UTC (permalink / raw)
To: Nathan Morrisson, nm, vigneshr, kristo, robh, krzk+dt, conor+dt
Cc: linux-arm-kernel, devicetree, linux-kernel, upstream
In-Reply-To: <20260625160214.4001298-2-nmorrisson@phytec.com>
On 6/25/26 11:02 AM, Nathan Morrisson wrote:
> Add support for the PHYTEC phyCORE-AM67x SoM [1] and the
> corresponding phyBOARD-Rigel carrier board [2]. The phyCORE-AM67x SoM
> uses the TI AM67x SoC and can come with different sizes and models of
> DDR, eMMC, and SPI NOR Flash.
>
> Supported features:
> * Audio playback and recording
> * CAN
> * Debug UART
> * eMMC
> * Ethernet
> * GPIO buttons
> * Heartbeat LED
> * I2C Current sensor
> * I2C EEPROM
> * I2C Light sensor
> * I2C RTC
> * Micro SD card
> * PCIe
> * SPI NOR flash
> * USB
>
> [1] https://www.phytec.com/product/phycore-am67x/
> [2] https://www.phytec.com/product/phyboard-am67x-development-kit/
>
> Signed-off-by: Nathan Morrisson <nmorrisson@phytec.com>
> ---
> arch/arm64/boot/dts/ti/Makefile | 1 +
> .../boot/dts/ti/k3-am67-phycore-som.dtsi | 328 ++++++++++++
> .../boot/dts/ti/k3-am6754-phyboard-rigel.dts | 502 ++++++++++++++++++
> 3 files changed, 831 insertions(+)
> create mode 100644 arch/arm64/boot/dts/ti/k3-am67-phycore-som.dtsi
> create mode 100644 arch/arm64/boot/dts/ti/k3-am6754-phyboard-rigel.dts
>
> diff --git a/arch/arm64/boot/dts/ti/Makefile b/arch/arm64/boot/dts/ti/Makefile
> index 371f9a043fe5..623ee2369132 100644
> --- a/arch/arm64/boot/dts/ti/Makefile
> +++ b/arch/arm64/boot/dts/ti/Makefile
> @@ -184,6 +184,7 @@ dtb-$(CONFIG_ARCH_K3) += k3-j721s2-evm-pcie1-ep.dtbo
> dtb-$(CONFIG_ARCH_K3) += k3-j721s2-evm-usb0-type-a.dtbo
>
> # Boards with J722s SoC
> +dtb-$(CONFIG_ARCH_K3) += k3-am6754-phyboard-rigel.dtb
> dtb-$(CONFIG_ARCH_K3) += k3-am67a-beagley-ai.dtb
> dtb-$(CONFIG_ARCH_K3) += k3-j722s-evm.dtb
> dtb-$(CONFIG_ARCH_K3) += k3-j722s-evm-csi2-quad-rpi-cam-imx219.dtbo
> diff --git a/arch/arm64/boot/dts/ti/k3-am67-phycore-som.dtsi b/arch/arm64/boot/dts/ti/k3-am67-phycore-som.dtsi
> new file mode 100644
> index 000000000000..8a40f648098e
> --- /dev/null
> +++ b/arch/arm64/boot/dts/ti/k3-am67-phycore-som.dtsi
> @@ -0,0 +1,328 @@
> +// SPDX-License-Identifier: GPL-2.0-only OR MIT
> +/*
> + * Copyright (C) 2026 PHYTEC America LLC
> + * Author: Nathan Morrisson <nmorrisson@phytec.com>
> + */
> +
> +#include <dt-bindings/net/ti-dp83867.h>
> +#include <dt-bindings/leds/common.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/interrupt-controller/irq.h>
> +
> +/ {
> + compatible = "phytec,am67-phycore-som", "ti,j722s";
> + model = "PHYTEC phyCORE-AM67";
> +
> + aliases {
> + ethernet0 = &cpsw_port1;
> + gpio0 = &main_gpio0;
> + mmc0 = &sdhci0;
> + rtc0 = &i2c_som_rtc;
> + rtc1 = &wkup_rtc0;
> + spi0 = &ospi0;
> + };
> +
> + memory@80000000 {
> + /* 4G RAM */
> + reg = <0x00000000 0x80000000 0x00000000 0x80000000>,
> + <0x00000008 0x80000000 0x00000000 0x80000000>;
> + device_type = "memory";
> + bootph-all;
> + };
> +
> + reserved_memory: reserved-memory {
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> + secure_tfa_ddr: tfa@9e780000 {
> + reg = <0x00 0x9e780000 0x00 0x80000>;
> + no-map;
> + };
> +
> + secure_ddr: optee@9e800000 {
> + reg = <0x00 0x9e800000 0x00 0x01800000>;
> + no-map;
> + };
> +
> + wkup_r5fss0_core0_dma_memory_region: memory@a0000000 {
> + compatible = "shared-dma-pool";
> + reg = <0x00 0xa0000000 0x00 0x100000>;
> + no-map;
> + };
> +
> + wkup_r5fss0_core0_memory_region: memory@a0100000 {
> + compatible = "shared-dma-pool";
> + reg = <0x00 0xa0100000 0x00 0xf00000>;
> + no-map;
> + };
> + };
> +
> + vcc_5v0_som: regulator-vcc-5v0-som {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_5V0_SOM";
> + regulator-min-microvolt = <5000000>;
> + regulator-max-microvolt = <5000000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + leds {
> + compatible = "gpio-leds";
> + pinctrl-names = "default";
> + pinctrl-0 = <&leds_pins_default>;
> +
> + led-0 {
> + color = <LED_COLOR_ID_GREEN>;
> + gpios = <&main_gpio0 13 GPIO_ACTIVE_HIGH>;
> + linux,default-trigger = "heartbeat";
> + function = LED_FUNCTION_HEARTBEAT;
> + };
> + };
> +};
> +
> +&main_pmx0 {
> + leds_pins_default: leds-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x034, PIN_OUTPUT, 7) /* (K22) OSPI0_CSN2.GPIO0_13 */
> + >;
> + };
> +
> + mdio_pins_default: mdio-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0160, PIN_OUTPUT, 0) /* (AC24) MDIO0_MDC */
> + J722S_IOPAD(0x015c, PIN_INPUT, 0) /* (AD25) MDIO0_MDIO */
> + >;
> + bootph-all;
> + };
> +
> + ospi0_pins_default: ospi0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x000, PIN_OUTPUT, 0) /* (L24) OSPI0_CLK */
> + J722S_IOPAD(0x02c, PIN_OUTPUT, 0) /* (K26) OSPI0_CSn0 */
> + J722S_IOPAD(0x00c, PIN_INPUT, 0) /* (K27) OSPI0_D0 */
> + J722S_IOPAD(0x010, PIN_INPUT, 0) /* (L27) OSPI0_D1 */
> + J722S_IOPAD(0x014, PIN_INPUT, 0) /* (L26) OSPI0_D2 */
> + J722S_IOPAD(0x018, PIN_INPUT, 0) /* (L25) OSPI0_D3 */
> + J722S_IOPAD(0x01c, PIN_INPUT, 0) /* (L21) OSPI0_D4 */
> + J722S_IOPAD(0x020, PIN_INPUT, 0) /* (M26) OSPI0_D5 */
> + J722S_IOPAD(0x024, PIN_INPUT, 0) /* (N27) OSPI0_D6 */
> + J722S_IOPAD(0x028, PIN_INPUT, 0) /* (M27) OSPI0_D7 */
> + J722S_IOPAD(0x008, PIN_INPUT, 0) /* (L22) OSPI0_DQS */
> + J722S_IOPAD(0x038, PIN_INPUT, 7) /* (J22) OSPI0_CSn3.GPIO0_14 */
> + >;
> + bootph-all;
> + };
> +
> + pmic_irq_pins_default: pmic-irq-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x030, PIN_INPUT, 7) /* (K23) OSPI0_CSN1.GPIO0_12 */
> + >;
> + };
> +
> + rgmii1_pins_default: rgmii1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x014c, PIN_INPUT, 0) /* (AC25) RGMII1_RD0 */
> + J722S_IOPAD(0x0150, PIN_INPUT, 0) /* (AD27) RGMII1_RD1 */
> + J722S_IOPAD(0x0154, PIN_INPUT, 0) /* (AE24) RGMII1_RD2 */
> + J722S_IOPAD(0x0158, PIN_INPUT, 0) /* (AE26) RGMII1_RD3 */
> + J722S_IOPAD(0x0148, PIN_INPUT, 0) /* (AE27) RGMII1_RXC */
> + J722S_IOPAD(0x0144, PIN_INPUT, 0) /* (AD23) RGMII1_RX_CTL */
> + J722S_IOPAD(0x0134, PIN_OUTPUT, 0) /* (AF27) RGMII1_TD0 */
> + J722S_IOPAD(0x0138, PIN_OUTPUT, 0) /* (AE23) RGMII1_TD1 */
> + J722S_IOPAD(0x013c, PIN_OUTPUT, 0) /* (AG25) RGMII1_TD2 */
> + J722S_IOPAD(0x0140, PIN_OUTPUT, 0) /* (AF24) RGMII1_TD3 */
> + J722S_IOPAD(0x0130, PIN_OUTPUT, 0) /* (AG26) RGMII1_TXC */
> + J722S_IOPAD(0x012c, PIN_OUTPUT, 0) /* (AF25) RGMII1_TX_CTL */
> + >;
> + bootph-all;
> + };
> +};
> +
> +&mcu_pmx0 {
> + wkup_i2c0_pins_default: wkup-i2c0-default-pins {
> + pinctrl-single,pins = <
> + J722S_MCU_IOPAD(0x04c, PIN_INPUT_PULLUP, 0) /* (B9) WKUP_I2C0_SCL */
> + J722S_MCU_IOPAD(0x050, PIN_INPUT_PULLUP, 0) /* (D11) WKUP_I2C0_SDA */
> + >;
> + bootph-all;
> + };
> +};
> +
> +&cpsw3g {
> + pinctrl-names = "default";
> + pinctrl-0 = <&rgmii1_pins_default>;
> + bootph-all;
> + status = "okay";
> +};
> +
> +&cpsw3g_mdio {
> + pinctrl-names = "default";
> + pinctrl-0 = <&mdio_pins_default>;
> + status = "okay";
> +
> + cpsw3g_phy1: ethernet-phy@1 {
> + compatible = "ethernet-phy-ieee802.3-c22";
> + reg = <1>;
> + ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
> + tx-fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
> + ti,min-output-impedance;
> + };
> +};
> +
> +&cpsw_port1 {
> + phy-mode = "rgmii-id";
> + phy-handle = <&cpsw3g_phy1>;
> + status = "okay";
> +};
> +
> +&cpsw_port2 {
> + status = "disabled";
This should already be default disabled in the SoC dtsi,
no need to re-disable it here.
> +};
> +
> +&ospi0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&ospi0_pins_default>;
> + bootph-all;
> + status = "okay";
> +
> + serial_flash: flash@0 {
> + compatible = "jedec,spi-nor";
> + reg = <0x0>;
> + spi-tx-bus-width = <8>;
> + spi-rx-bus-width = <8>;
> + spi-max-frequency = <25000000>;
> + vcc-supply = <&vdd_1v8>;
> + cdns,tshsl-ns = <60>;
> + cdns,tsd2d-ns = <60>;
> + cdns,tchsh-ns = <60>;
> + cdns,tslch-ns = <60>;
> + cdns,read-delay = <0>;
> + };
> +};
> +
> +&sdhci0 {
> + non-removable;
> + bootph-all;
> + ti,driver-strength-ohm = <50>;
> + status = "okay";
> +};
> +
> +&wkup_i2c0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&wkup_i2c0_pins_default>;
> + clock-frequency = <400000>;
> + bootph-all;
> + status = "okay";
> +
> + pmic@30 {
> + compatible = "ti,tps65219";
> + reg = <0x30>;
> + buck1-supply = <&vcc_5v0_som>;
> + buck2-supply = <&vcc_5v0_som>;
> + buck3-supply = <&vcc_5v0_som>;
> + ldo1-supply = <&vdd_3v3>;
> + ldo2-supply = <&vdd_1v8>;
> + ldo3-supply = <&vdd_3v3>;
> + ldo4-supply = <&vdd_3v3>;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&pmic_irq_pins_default>;
> + interrupt-parent = <&main_gpio0>;
> + interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
> + interrupt-controller;
> + #interrupt-cells = <1>;
> +
> + system-power-controller;
> + ti,power-button;
> +
> + regulators {
> + vdd_3v3: buck1 {
> + regulator-name = "VDD_3V3";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdd_1v8: buck2 {
> + regulator-name = "VDD_1V8";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdd_lpddr4: buck3 {
> + regulator-name = "VDD_LPDDR4";
> + regulator-min-microvolt = <1100000>;
> + regulator-max-microvolt = <1100000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vddshv_sdio: ldo1 {
> + regulator-name = "VDDSHV_SDIO";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-allow-bypass;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdd_1v2: ldo2 {
> + regulator-name = "VDD_1V2";
> + regulator-min-microvolt = <1200000>;
> + regulator-max-microvolt = <1200000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdda_1v8_phy: ldo3 {
> + regulator-name = "VDDA_1V8_PHY";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdd_1v8_pll: ldo4 {
> + regulator-name = "VDD_1V8_PLL";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> + };
> + };
> +
> + vdd_core: regulator-vdd-core@44 {
> + compatible = "ti,tps62873";
> + reg = <0x44>;
> + bootph-pre-ram;
> + regulator-name = "VDD_CORE";
> + regulator-min-microvolt = <850000>;
> + regulator-max-microvolt = <850000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + eeprom@50 {
> + compatible = "atmel,24c32";
> + reg = <0x50>;
> + pagesize = <32>;
> + };
> +
> + som_eeprom_opt: eeprom@51 {
> + compatible = "atmel,24c32";
> + reg = <0x51>;
> + pagesize = <32>;
> + };
> +
> + i2c_som_rtc: rtc@52 {
> + compatible = "microcrystal,rv3028";
> + reg = <0x52>;
> + };
> +};
> +
> +#include "k3-j722s-ti-ipc-firmware.dtsi"
> diff --git a/arch/arm64/boot/dts/ti/k3-am6754-phyboard-rigel.dts b/arch/arm64/boot/dts/ti/k3-am6754-phyboard-rigel.dts
> new file mode 100644
> index 000000000000..7853d4f5d3b9
> --- /dev/null
> +++ b/arch/arm64/boot/dts/ti/k3-am6754-phyboard-rigel.dts
> @@ -0,0 +1,502 @@
> +// SPDX-License-Identifier: GPL-2.0-only OR MIT
> +/*
> + * Copyright (C) 2026 PHYTEC America LLC
> + * Author: Nathan Morrisson <nmorrisson@phytec.com>
> + */
> +
> +/dts-v1/;
> +
> +#include <dt-bindings/input/input.h>
> +#include <dt-bindings/phy/phy.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/interrupt-controller/irq.h>
> +#include "k3-serdes.h"
> +#include "k3-j722s.dtsi"
This should be included by the som.dtsi, keeps the include chain sane.
Andrew
> +#include "k3-am67-phycore-som.dtsi"
> +
> +/ {
> + compatible = "phytec,am6754-phyboard-rigel",
> + "phytec,am67-phycore-som", "ti,j722s";
> + model = "PHYTEC phyBOARD-Rigel AM67";
> +
> + aliases {
> + gpio1 = &main_gpio1;
> + mmc1 = &sdhci1;
> + serial2 = &main_uart0;
> + usb0 = &usb0;
> + usb1 = &usb1;
> + };
> +
> + can_tc0: can-phy0 {
> + compatible = "ti,tcan1042";
> + #phy-cells = <0>;
> + max-bitrate = <8000000>;
> + standby-gpios = <&gpio_exp1 1 GPIO_ACTIVE_HIGH>;
> + };
> +
> + usb0_connector: connector {
> + compatible = "gpio-usb-b-connector", "usb-b-connector";
> + label = "USB-C";
> + data-role = "dual";
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_usbc_power_pins_default>;
> +
> + id-gpios = <&main_gpio1 15 GPIO_ACTIVE_HIGH>;
> +
> + port {
> + usb0_con: endpoint {
> + remote-endpoint = <&usb0_ep>;
> + };
> + };
> + };
> +
> + keys {
> + compatible = "gpio-keys";
> + autorepeat;
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio_keys_pins_default>;
> +
> + key-home {
> + label = "home";
> + linux,code = <KEY_HOME>;
> + gpios = <&main_gpio1 23 GPIO_ACTIVE_HIGH>;
> + };
> +
> + key-menu {
> + label = "menu";
> + linux,code = <KEY_MENU>;
> + gpios = <&gpio_exp1 4 GPIO_ACTIVE_HIGH>;
> + };
> + };
> +
> + pcie_refclk0: pcie-refclk0 {
> + compatible = "gpio-gate-clock";
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_pcie_usb_sel_pins_default>;
> + clocks = <&serdes_refclk>;
> + #clock-cells = <0>;
> + enable-gpios = <&main_gpio0 22 GPIO_ACTIVE_LOW>;
> + };
> +
> + vcc_1v8: regulator-vcc-1v8 {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_1V8";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + vcc_3v3_aud: regulator-vcc-3v3-aud {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_3V3_AUD";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + vcc_3v3_mmc: regulator-vcc-3v3-mmc {
> + /* TPS22963C OUTPUT */
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_3V3_MMC";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + vcc_3v3_sw: regulator-vcc-3v3-sw {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_3V3_SW";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + vcc_speaker: regulator-vcc-speaker {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_SPEAKER";
> + regulator-min-microvolt = <5000000>;
> + regulator-max-microvolt = <5000000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + sound {
> + compatible = "simple-audio-card";
> + simple-audio-card,widgets =
> + "Microphone", "Mic Jack",
> + "Headphone", "Headphone Jack",
> + "Line", "Stereo Jack",
> + "Speaker", "L SPKR",
> + "Speaker", "R SPKR";
> + simple-audio-card,routing =
> + "MIC1RP", "Mic Jack",
> + "Mic Jack", "MICBIAS",
> + "Headphone Jack", "HPL",
> + "Headphone Jack", "HPR",
> + "MIC1LM", "Stereo Jack",
> + "MIC1LP", "Stereo Jack",
> + "SPL", "L SPKR",
> + "SPR", "R SPKR";
> + simple-audio-card,name = "phyBOARD-Rigel";
> + simple-audio-card,format = "dsp_b";
> + simple-audio-card,bitclock-master = <&sound_master>;
> + simple-audio-card,frame-master = <&sound_master>;
> + simple-audio-card,bitclock-inversion;
> +
> + simple-audio-card,cpu {
> + sound-dai = <&mcasp0>;
> + };
> +
> + sound_master: simple-audio-card,codec {
> + sound-dai = <&audio_codec>;
> + clocks = <&audio_refclk1>;
> + };
> + };
> +};
> +
> +&main_pmx0 {
> + audio_ext_refclk1_pins_default: audio-ext-refclk1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0a0, PIN_OUTPUT, 1) /* (N24) GPMC0_WPn.AUDIO_EXT_REFCLK1 */
> + >;
> + };
> +
> + gpio_exp0_int_pins_default: gpio-exp0-int-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0054, PIN_INPUT, 7) /* (T21) GPMC0_AD6.GPIO0_21 */
> + >;
> + };
> +
> + gpio_exp1_int_pins_default: gpio-exp1-int-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0244, PIN_INPUT, 7) /* (A24) MMC1_SDWP.GPIO1_49 */
> + >;
> + };
> +
> + gpio_exp2_int_pins_default: gpio-exp2-int-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0050, PIN_INPUT, 7) /* (T24) GPMC0_AD5.GPIO0_20 */
> + >;
> + };
> +
> + gpio_keys_pins_default: gpio-keys-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x01d4, PIN_INPUT, 7) /* (B21) UART0_RTSn.GPIO1_23 */
> + >;
> + };
> +
> + main_i2c0_pins_default: main-i2c0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x01e0, PIN_INPUT_PULLUP, 0) /* (D23) I2C0_SCL */
> + J722S_IOPAD(0x01e4, PIN_INPUT_PULLUP, 0) /* (B22) I2C0_SDA */
> + >;
> + bootph-all;
> + };
> +
> + main_i2c1_pins_default: main-i2c1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x01e8, PIN_INPUT_PULLUP, 0) /* (C24) I2C1_SCL */
> + J722S_IOPAD(0x01ec, PIN_INPUT_PULLUP, 0) /* (A22) I2C1_SDA */
> + >;
> + bootph-all;
> + };
> +
> + main_mcan0_pins_default: main-mcan0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x1dc, PIN_INPUT, 0) /* (C22) MCAN0_RX */
> + J722S_IOPAD(0x1d8, PIN_OUTPUT, 0) /* (D22) MCAN0_TX */
> + >;
> + };
> +
> + main_mcasp0_pins_default: main-mcasp0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x1a8, PIN_INPUT, 0) /* (C26) MCASP0_AFSX */
> + J722S_IOPAD(0x1a4, PIN_INPUT, 0) /* (D25) MCASP0_ACLKX */
> + J722S_IOPAD(0x198, PIN_OUTPUT, 0) /* (A26) MCASP0_AXR2 */
> + J722S_IOPAD(0x194, PIN_INPUT, 0) /* (A25) MCASP0_AXR3 */
> + >;
> + };
> +
> + main_mcasp1_pins_default: main-mcasp1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0090, PIN_INPUT, 2) /* (P27) GPMC0_BE0n_CLE.MCASP1_ACLKX */
> + J722S_IOPAD(0x0098, PIN_INPUT, 2) /* (V21) GPMC0_WAIT0.MCASP1_AFSX */
> + J722S_IOPAD(0x008c, PIN_OUTPUT, 2) /* (N23) GPMC0_WEn.MCASP1_AXR0 */
> + >;
> + };
> +
> + main_mmc1_pins_default: main-mmc1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x023c, PIN_INPUT, 0) /* (H22) MMC1_CMD */
> + J722S_IOPAD(0x0234, PIN_INPUT, 0) /* (H24) MMC1_CLK */
> + J722S_IOPAD(0x0230, PIN_INPUT, 0) /* (H23) MMC1_DAT0 */
> + J722S_IOPAD(0x022c, PIN_INPUT, 0) /* (H20) MMC1_DAT1 */
> + J722S_IOPAD(0x0228, PIN_INPUT, 0) /* (J23) MMC1_DAT2 */
> + J722S_IOPAD(0x0224, PIN_INPUT, 0) /* (H25) MMC1_DAT3 */
> + J722S_IOPAD(0x0240, PIN_INPUT, 0) /* (B24) MMC1_SDCD */
> + >;
> + bootph-all;
> + };
> +
> + main_pcie_pins_default: main-pcie-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x07c, PIN_INPUT, 7) /* (T23) GPMC0_CLK.GPIO0_31 */
> + >;
> + };
> +
> + main_pcie_usb_sel_pins_default: main-pcie-usb-sel-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x058, PIN_INPUT, 7) /* (T22) GPMC0_AD7.GPIO0_22 */
> + >;
> + };
> +
> + main_uart0_pins_default: main-uart0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x01c8, PIN_INPUT, 0) /* (F19) UART0_RXD */
> + J722S_IOPAD(0x01cc, PIN_OUTPUT, 0) /* (F20) UART0_TXD */
> + >;
> + bootph-all;
> + };
> +
> + main_usbc_power_pins_default: main-usbc-power-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x1b4, PIN_INPUT, 7) /* (B20) SPI0_CS0.GPIO1_15 */
> + >;
> + };
> +};
> +
> +&audio_refclk1 {
> + assigned-clock-rates = <25000000>;
> +};
> +
> +&main_i2c0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_i2c0_pins_default>;
> + clock-frequency = <400000>;
> + status = "okay";
> +
> + veml6030: light-sensor@10 {
> + compatible = "vishay,veml6030";
> + reg = <0x10>;
> + vdd-supply = <&vcc_3v3_sw>;
> + };
> +};
> +
> +&main_i2c1 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_i2c1_pins_default>;
> + clock-frequency = <100000>;
> + status = "okay";
> +
> + audio_codec: audio-codec@18 {
> + compatible = "ti,tlv320aic3110";
> + reg = <0x18>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&audio_ext_refclk1_pins_default>;
> + #sound-dai-cells = <0>;
> + ai3xx-micbias-vg = <2>;
> + reset-gpios = <&gpio_exp1 7 GPIO_ACTIVE_LOW>;
> +
> + HPVDD-supply = <&vcc_3v3_aud>;
> + SPRVDD-supply = <&vcc_speaker>;
> + SPLVDD-supply = <&vcc_speaker>;
> + AVDD-supply = <&vcc_3v3_aud>;
> + IOVDD-supply = <&vcc_3v3_aud>;
> + DVDD-supply = <&vcc_1v8>;
> + };
> +
> + gpio_exp0: gpio@20 {
> + compatible = "nxp,pcf8574";
> + reg = <0x20>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio_exp0_int_pins_default>;
> + interrupt-parent = <&main_gpio0>;
> + interrupts = <21 IRQ_TYPE_LEVEL_LOW>;
> + gpio-line-names = "CSI3_STROBE", "CSI3_TRIGGER",
> + "CSI3_SHUTTER", "CSI3_OE",
> + "CSI2_STROBE", "CSI2_TRIGGER",
> + "CSI2_SHUTTER", "CSI2_OE";
> + };
> +
> + gpio_exp1: gpio@21 {
> + compatible = "nxp,pcf8574";
> + reg = <0x21>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio_exp1_int_pins_default>;
> + interrupt-parent = <&main_gpio1>;
> + interrupts = <49 IRQ_TYPE_LEVEL_LOW>;
> + gpio-line-names = "GPIO0_HDMI_RST", "GPIO1_CAN_nEN",
> + "GPIO2_LED", "GPIO3_MCU_CAN0_nEN",
> + "GPIO4_BUT2", "GPIO5_MCU_CAN1_nEN",
> + "GPIO6_AUDIO_GPIO", "GPIO7_AUDIO_USER_RESET";
> + };
> +
> + gpio_exp2: gpio@23 {
> + compatible = "nxp,pcf8574";
> + reg = <0x23>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio_exp2_int_pins_default>;
> + interrupt-parent = <&main_gpio0>;
> + interrupts = <20 IRQ_TYPE_LEVEL_LOW>;
> + gpio-line-names = "CSI1_STROBE", "CSI1_TRIGGER",
> + "CSI1_SHUTTER", "CSI1_OE",
> + "CSI0_STROBE", "CSI0_TRIGGER",
> + "CSI0_SHUTTER", "CSI0_OE";
> + };
> +
> + current-sensor@40 {
> + compatible = "ti,ina233";
> + reg = <0x40>;
> + shunt-resistor = <18000>;
> + };
> +
> + eeprom@51 {
> + compatible = "atmel,24c02";
> + reg = <0x51>;
> + pagesize = <16>;
> + };
> +};
> +
> +&main_mcan0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_mcan0_pins_default>;
> + phys = <&can_tc0>;
> + status = "okay";
> +};
> +
> +&main_uart0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_uart0_pins_default>;
> + bootph-all;
> + status = "okay";
> +};
> +
> +&mcasp0 {
> + #sound-dai-cells = <0>;
> + op-mode = <0>; /* MCASP_IIS_MODE */
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_mcasp0_pins_default>;
> + tdm-slots = <2>;
> + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
> + 0 0 1 2
> + 0 0 0 0
> + 0 0 0 0
> + 0 0 0 0
> + >;
> + status = "okay";
> +};
> +
> +&mcasp1 {
> + #sound-dai-cells = <0>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_mcasp1_pins_default>;
> + op-mode = <0>; /* MCASP_IIS_MODE */
> + tdm-slots = <2>;
> + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
> + 1 0 2 0
> + 0 0 0 0
> + 0 0 0 0
> + 0 0 0 0
> + >;
> + status = "okay";
> +};
> +
> +&pcie0_rc {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_pcie_pins_default>;
> + num-lanes = <1>;
> + phys = <&serdes1_pcie_link>;
> + phy-names = "pcie-phy";
> + reset-gpios = <&main_gpio0 31 GPIO_ACTIVE_HIGH>;
> + status = "okay";
> +};
> +
> +&sdhci1 {
> + /* SD/MMC */
> + vmmc-supply = <&vcc_3v3_mmc>;
> + vqmmc-supply = <&vddshv_sdio>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_mmc1_pins_default>;
> + disable-wp;
> + no-1-8-v;
> + bootph-all;
> + status = "okay";
> +};
> +
> +&serdes_ln_ctrl {
> + idle-states = <J722S_SERDES0_LANE0_USB>,
> + <J722S_SERDES1_LANE0_PCIE0_LANE0>;
> +};
> +
> +&serdes0 {
> + status = "okay";
> +
> + serdes0_usb_link: phy@0 {
> + reg = <0>;
> + cdns,num-lanes = <1>;
> + #phy-cells = <0>;
> + cdns,phy-type = <PHY_TYPE_USB3>;
> + resets = <&serdes_wiz0 1>;
> + };
> +};
> +
> +&serdes_wiz0 {
> + status = "okay";
> +};
> +
> +&serdes1 {
> + status = "okay";
> +
> + serdes1_pcie_link: phy@0 {
> + reg = <0>;
> + cdns,num-lanes = <1>;
> + #phy-cells = <0>;
> + cdns,phy-type = <PHY_TYPE_PCIE>;
> + resets = <&serdes_wiz1 1>;
> + };
> +};
> +
> +&serdes_wiz1 {
> + clocks = <&k3_clks 280 0>, <&k3_clks 280 1>, <&pcie_refclk0>;
> + status = "okay";
> +};
> +
> +&usbss0 {
> + ti,vbus-divider;
> + status = "okay";
> +};
> +
> +&usb0 {
> + dr_mode = "otg";
> + usb-role-switch;
> + maximum-speed = "high-speed";
> +
> + port {
> + usb0_ep: endpoint {
> + remote-endpoint = <&usb0_con>;
> + };
> + };
> +};
> +
> +&usbss1 {
> + ti,vbus-divider;
> + status = "okay";
> +};
> +
> +&usb1 {
> + dr_mode = "host";
> + phys = <&serdes0_usb_link>;
> + phy-names = "cdns3,usb3-phy";
> + maximum-speed = "super-speed";
> +};
^ permalink raw reply
* Re: [PATCH] arm64: dts: ti: k3-am62a7-sk: Add bootph-all property in cpsw_mac_syscon node
From: Andrew Davis @ 2026-06-25 20:48 UTC (permalink / raw)
To: Chintan Vankar, Conor Dooley, Krzysztof Kozlowski, Rob Herring,
Tero Kristo, Vignesh Raghavendra, Nishanth Menon
Cc: linux-kernel, devicetree, linux-arm-kernel
In-Reply-To: <20260625113223.1711052-1-c-vankar@ti.com>
On 6/25/26 6:32 AM, Chintan Vankar wrote:
> Ethernet boot requires CPSW node to be present starting from R5 SPL stage.
> Add "bootph-all" property in CPSW MAC's eFuse node "cpsw_mac_syscon" to
> enable this node during SPL stage along with later boot stage so that CPSW
> port will get static MAC address.
>
> Signed-off-by: Chintan Vankar <c-vankar@ti.com>
> ---
>
> Hello All,
>
> This patch is based on linux-next tagged next-20260623.
>
> arch/arm64/boot/dts/ti/k3-am62a7-sk.dts | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
> index 821a9705bb7d..d3b3675e7a8f 100644
> --- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
> +++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
> @@ -230,6 +230,10 @@ AM62AX_MCU_IOPAD(0x0030, PIN_OUTPUT, 0) /* (C8) WKUP_UART0_RTSn */
> };
> };
>
> +&cpsw_mac_syscon {
> + bootph-all;
Seems you need this because cpsw_port1 uses it though a phandle reference.
cpsw_port1 has bootph-all, why is this property not transitive though
phandles? Would not having that cause missing references when the phandles
are resolved to nodes that get dropped for some given boot stage?
Andrew
> +};
> +
> /* WKUP UART0 is used for DM firmware logs */
> &wkup_uart0 {
> pinctrl-names = "default";
^ permalink raw reply
* [PATCH 0/4] PCI: mediatek-gen3: Add 2-lanes mode support + clock
From: Christian Marangi @ 2026-06-25 21:57 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Ryder Lee, Michael Turquette,
Stephen Boyd, Brian Masney, Philipp Zabel, Matthias Brugger,
AngeloGioacchino Del Regno, Christian Marangi, Jianjun Wang,
linux-pci, devicetree, linux-kernel, linux-mediatek, linux-clk,
linux-arm-kernel
This small series introduce support for 2-lanes mode for Airoha AN7581
SoC. This is needed for correctly functionality of Eagle WiFi Card
normally attached to this SoC that require a 2-line PCIe card to
correctly work (and give the proper performance)
The first 2 patch address a limitation of the PCIe implementation
where the PERSTOUT reset were indirectly asserted and deasserted
all at the same time (for all the 3 PCIe card) with PCIe
enable and disable.
The 2 patch address this and introduce correct reset to control
reset line for the relevant PCIe line.
The last 2 patch add additional logic and support to assert
and deassert the PERSTOUT and also apply the required configuration
for 2-lanes mode.
2-lanes mode is implemented in DT by adding the required property
and by defining the "num-lanes" to 2.
Christian Marangi (4):
dt-bindings: clock: airoha: Add additional reset for PCIe PERSTOUT
clk: en7523: add support for dedicated PCIe PERSTOUT reset
dt-bindings: PCI: mediatek-gen3: Split Airoha schema and document
2-lanes
PCI: mediatek-gen3: Add 2-lanes mode support for Airoha AN7581
.../bindings/pci/airoha,en7581-pcie.yaml | 251 ++++++++++++++++++
.../bindings/pci/mediatek-pcie-gen3.yaml | 77 +-----
drivers/clk/clk-en7523.c | 27 +-
drivers/pci/controller/pcie-mediatek-gen3.c | 98 +++++--
.../dt-bindings/reset/airoha,en7581-reset.h | 4 +
5 files changed, 358 insertions(+), 99 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pci/airoha,en7581-pcie.yaml
--
2.53.0
^ permalink raw reply
* [PATCH 1/4] dt-bindings: clock: airoha: Add additional reset for PCIe PERSTOUT
From: Christian Marangi @ 2026-06-25 21:57 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Ryder Lee, Michael Turquette,
Stephen Boyd, Brian Masney, Philipp Zabel, Matthias Brugger,
AngeloGioacchino Del Regno, Christian Marangi, Jianjun Wang,
linux-pci, devicetree, linux-kernel, linux-mediatek, linux-clk,
linux-arm-kernel
In-Reply-To: <20260625215741.3253212-1-ansuelsmth@gmail.com>
Add additional reset to control PCIe PERSTOUT reset line for each of the 3
PCIe lines.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
include/dt-bindings/reset/airoha,en7581-reset.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/include/dt-bindings/reset/airoha,en7581-reset.h b/include/dt-bindings/reset/airoha,en7581-reset.h
index 6544a1790b83..25e75534daa9 100644
--- a/include/dt-bindings/reset/airoha,en7581-reset.h
+++ b/include/dt-bindings/reset/airoha,en7581-reset.h
@@ -62,5 +62,9 @@
#define EN7581_CPU_TIMER_RST 50
#define EN7581_PCIE_HB_RST 51
#define EN7581_XPON_MAC_RST 52
+/* RST_PCIC */
+#define EN7581_PCIC_PERSTOUT0_RST 53
+#define EN7581_PCIC_PERSTOUT1_RST 54
+#define EN7581_PCIC_PERSTOUT2_RST 55
#endif /* __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7581_H_ */
--
2.53.0
^ permalink raw reply related
* [PATCH 2/4] clk: en7523: add support for dedicated PCIe PERSTOUT reset
From: Christian Marangi @ 2026-06-25 21:57 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Ryder Lee, Michael Turquette,
Stephen Boyd, Brian Masney, Philipp Zabel, Matthias Brugger,
AngeloGioacchino Del Regno, Christian Marangi, Jianjun Wang,
linux-pci, devicetree, linux-kernel, linux-mediatek, linux-clk,
linux-arm-kernel
In-Reply-To: <20260625215741.3253212-1-ansuelsmth@gmail.com>
Add support for resetting the PCIe lines with the PERSTOUT reset. These
special reset are controlled by the PCIC register and are specific to each
of the 3 PCIe lines.
Notice that reset logic is inverted for these bit where 0 is assert and 1
deassert. This is intenrally handled in the reset function.
PCI enable/disable are updated to drop PERSTOUT bits in favor dedicated
reset handling.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/clk/clk-en7523.c | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c
index 1ab0e2eca5d3..a33cf2e1b76f 100644
--- a/drivers/clk/clk-en7523.c
+++ b/drivers/clk/clk-en7523.c
@@ -338,6 +338,7 @@ static const struct en_clk_desc en7581_base_clks[] = {
static const u16 en7581_rst_ofs[] = {
REG_RST_CTRL2,
REG_RST_CTRL1,
+ REG_NP_SCU_PCIC,
};
static const u16 en751221_rst_ofs[] = {
@@ -450,6 +451,11 @@ static const u16 en7581_rst_map[] = {
[EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28,
[EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29,
[EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31,
+
+ /* RST_PCIC */
+ [EN7581_PCIC_PERSTOUT0_RST] = 2 * RST_NR_PER_BANK + 29,
+ [EN7581_PCIC_PERSTOUT1_RST] = 2 * RST_NR_PER_BANK + 26,
+ [EN7581_PCIC_PERSTOUT2_RST] = 2 * RST_NR_PER_BANK + 16,
};
static const u16 en751221_rst_map[] = {
@@ -635,9 +641,7 @@ static int en7581_pci_enable(struct clk_hw *hw)
void __iomem *np_base = cg->base;
u32 val, mask;
- mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
- REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
- REG_PCI_CONTROL_PERSTOUT;
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
val = readl(np_base + REG_PCI_CONTROL);
writel(val | mask, np_base + REG_PCI_CONTROL);
@@ -650,9 +654,7 @@ static void en7581_pci_disable(struct clk_hw *hw)
void __iomem *np_base = cg->base;
u32 val, mask;
- mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
- REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
- REG_PCI_CONTROL_PERSTOUT;
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
val = readl(np_base + REG_PCI_CONTROL);
writel(val & ~mask, np_base + REG_PCI_CONTROL);
usleep_range(1000, 2000);
@@ -754,14 +756,21 @@ static int en7523_reset_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
- void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK];
+ u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK];
+ void __iomem *addr = rst_data->base + offset;
+ bool inverted = false;
u32 val;
+ /* For PCIC reset logic is inverted, 0:assert 1:deassert*/
+ if (offset == REG_NP_SCU_PCIC)
+ inverted = true;
+
val = readl(addr);
+ val &= ~BIT(id % RST_NR_PER_BANK);
if (assert)
- val |= BIT(id % RST_NR_PER_BANK);
+ val |= inverted ? 0 : BIT(id % RST_NR_PER_BANK);
else
- val &= ~BIT(id % RST_NR_PER_BANK);
+ val |= inverted ? BIT(id % RST_NR_PER_BANK) : 0;
writel(val, addr);
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH 3/4] dt-bindings: PCI: mediatek-gen3: Split Airoha schema and document 2-lanes
From: Christian Marangi @ 2026-06-25 21:57 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Ryder Lee, Michael Turquette,
Stephen Boyd, Brian Masney, Philipp Zabel, Matthias Brugger,
AngeloGioacchino Del Regno, Christian Marangi, Jianjun Wang,
linux-pci, devicetree, linux-kernel, linux-mediatek, linux-clk,
linux-arm-kernel
In-Reply-To: <20260625215741.3253212-1-ansuelsmth@gmail.com>
To permit proper documentation of required property to support PCIe
configured for 2-lanes mode, split the Airoha schema part from the
mediatek-gen3 schema to a dedicated schema.
A PCIe configured for 2-lanes mode require an additional reg for the
secondary PCIe to be configured and the airoha,scu phandle to correctly
configure the PCIe MUX.
Rework the mediatek-gen3 schema to drop any redundant constraint previsouly
introduced for Airoha PCIe properties.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
.../bindings/pci/airoha,en7581-pcie.yaml | 251 ++++++++++++++++++
.../bindings/pci/mediatek-pcie-gen3.yaml | 77 +-----
2 files changed, 256 insertions(+), 72 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pci/airoha,en7581-pcie.yaml
diff --git a/Documentation/devicetree/bindings/pci/airoha,en7581-pcie.yaml b/Documentation/devicetree/bindings/pci/airoha,en7581-pcie.yaml
new file mode 100644
index 000000000000..977c1816572c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/airoha,en7581-pcie.yaml
@@ -0,0 +1,251 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/airoha,en7581-pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Gen3 PCIe controller on Airoha SoCs
+
+maintainers:
+ - Christian Marangi <ansuelsmth@gmail.com>
+
+description: |+
+ PCIe Gen3 MAC controller for Airoha SoCs, it supports Gen3 speed
+ and compatible with Gen2, Gen1 speed.
+
+ This PCIe controller supports up to 256 MSI vectors, the MSI hardware
+ block diagram is as follows:
+
+ +-----+
+ | GIC |
+ +-----+
+ ^
+ |
+ port->irq
+ |
+ +-+-+-+-+-+-+-+-+
+ |0|1|2|3|4|5|6|7| (PCIe intc)
+ +-+-+-+-+-+-+-+-+
+ ^ ^ ^
+ | | ... |
+ +-------+ +------+ +-----------+
+ | | |
+ +-+-+---+--+--+ +-+-+---+--+--+ +-+-+---+--+--+
+ |0|1|...|30|31| |0|1|...|30|31| |0|1|...|30|31| (MSI sets)
+ +-+-+---+--+--+ +-+-+---+--+--+ +-+-+---+--+--+
+ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
+ | | | | | | | | | | | | (MSI vectors)
+ | | | | | | | | | | | |
+
+ (MSI SET0) (MSI SET1) ... (MSI SET7)
+
+ With 256 MSI vectors supported, the MSI vectors are composed of 8 sets,
+ each set has its own address for MSI message, and supports 32 MSI vectors
+ to generate interrupt.
+
+properties:
+ compatible:
+ const: airoha,en7581-pcie
+
+ reg:
+ minItems: 1
+ maxItems: 2
+
+ reg-names:
+ minItems: 1
+ maxItems: 2
+
+ interrupts:
+ maxItems: 1
+
+ ranges:
+ minItems: 1
+ maxItems: 8
+
+ iommu-map:
+ maxItems: 1
+
+ iommu-map-mask:
+ const: 0
+
+ resets:
+ minItems: 1
+ maxItems: 4
+
+ reset-names:
+ minItems: 1
+ maxItems: 4
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ items:
+ - const: sys-ck
+
+ phys:
+ maxItems: 1
+
+ phy-names:
+ items:
+ - const: pcie-phy
+
+ num-lanes:
+ enum: [1, 2]
+
+ mediatek,pbus-csr:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: phandle to pbus-csr syscon
+ - description: offset of pbus-csr base address register
+ - description: offset of pbus-csr base address mask register
+ description:
+ Phandle with two arguments to the syscon node used to detect if
+ a given address is accessible on PCIe controller.
+
+ airoha,scu:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: phandle to airoha SCU syscon
+ description:
+ Phandle to SCU syscon to configure PCIe MUX for 2 lines support.
+
+ '#interrupt-cells':
+ const: 1
+
+ interrupt-controller:
+ description: Interrupt controller node for handling legacy PCI interrupts.
+ type: object
+ properties:
+ '#address-cells':
+ const: 0
+ '#interrupt-cells':
+ const: 1
+ interrupt-controller: true
+
+ required:
+ - '#address-cells'
+ - '#interrupt-cells'
+ - interrupt-controller
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - interrupts
+ - ranges
+ - clocks
+ - clock-names
+ - '#interrupt-cells'
+ - interrupt-controller
+
+allOf:
+ - $ref: /schemas/pci/pci-host-bridge.yaml#
+ - if:
+ properties:
+ num-lanes:
+ const: 2
+ then:
+ properties:
+ regs:
+ minItems: 2
+
+ reg-names:
+ items:
+ - const: pcie-mac
+ - const: sec-pcie-mac
+
+ resets:
+ minItems: 4
+
+ reset-names:
+ items:
+ - const: phy-lane0
+ - const: phy-lane1
+ - const: perstout
+ - const: sec-perstout
+
+ required:
+ - airoha,scu
+
+ else:
+ properties:
+ reg:
+ maxItems: 1
+
+ reg-names:
+ items:
+ - const: pcie-mac
+
+ resets:
+ minItems: 2
+ maxItems: 3
+
+ reset-names:
+ minItems: 2
+ items:
+ - enum: [ phy-lane0, phy-lane1, phy-lan2 ]
+ - enum: [ phy-lane1, perstout ]
+ - const: phy-lane2
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ bus {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@1fc00000 {
+ compatible = "airoha,en7581-pcie";
+ device_type = "pci";
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ reg = <0x0 0x1fc00000 0x0 0x1670>,
+ <0x0 0x1fc20000 0x0 0x1670>;
+ reg-names = "pcie-mac", "sec-pcie-mac";
+
+ clocks = <&scuclk 7>;
+ clock-names = "sys-ck";
+
+ phys = <&pciephy>;
+ phy-names = "pcie-phy";
+
+ ranges = <0x02000000 0 0x20000000 0x0 0x20000000 0 0x4000000>;
+
+ resets = <&scuclk 48>,
+ <&scuclk 49>,
+ <&scuclk 53>,
+ <&scuclk 54>;
+ reset-names = "phy-lane0", "phy-lane1",
+ "perstout", "sec-perstout";
+
+ num-lanes = <2>;
+
+ mediatek,pbus-csr = <&pbus_csr 0x0 0x4>;
+
+ airoha,scu = <&scuclk>;
+
+ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+ bus-range = <0x00 0xff>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &pcie_intc 0>,
+ <0 0 0 2 &pcie_intc 1>,
+ <0 0 0 3 &pcie_intc 2>,
+ <0 0 0 4 &pcie_intc 3>;
+ pcie_intc: interrupt-controller {
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
index 4db700fc36ba..510f1f2b1c5a 100644
--- a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
+++ b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
@@ -59,7 +59,6 @@ properties:
- const: mediatek,mt8196-pcie
- const: mediatek,mt8192-pcie
- const: mediatek,mt8196-pcie
- - const: airoha,en7581-pcie
reg:
maxItems: 1
@@ -83,20 +82,20 @@ properties:
resets:
minItems: 1
- maxItems: 3
+ maxItems: 2
reset-names:
minItems: 1
- maxItems: 3
+ maxItems: 2
items:
- enum: [ phy, mac, phy-lane0, phy-lane1, phy-lane2 ]
+ enum: [ phy, mac ]
clocks:
- minItems: 1
+ minItems: 4
maxItems: 6
clock-names:
- minItems: 1
+ minItems: 4
maxItems: 6
assigned-clocks:
@@ -115,17 +114,6 @@ properties:
power-domains:
maxItems: 1
- mediatek,pbus-csr:
- $ref: /schemas/types.yaml#/definitions/phandle-array
- items:
- - items:
- - description: phandle to pbus-csr syscon
- - description: offset of pbus-csr base address register
- - description: offset of pbus-csr base address mask register
- description:
- Phandle with two arguments to the syscon node used to detect if
- a given address is accessible on PCIe controller.
-
'#interrupt-cells':
const: 1
@@ -177,16 +165,6 @@ allOf:
- const: peri_26m
- const: top_133m
- resets:
- minItems: 1
- maxItems: 2
-
- reset-names:
- minItems: 1
- maxItems: 2
-
- mediatek,pbus-csr: false
-
- if:
properties:
compatible:
@@ -208,16 +186,6 @@ allOf:
- const: peri_26m
- const: peri_mem
- resets:
- minItems: 1
- maxItems: 2
-
- reset-names:
- minItems: 1
- maxItems: 2
-
- mediatek,pbus-csr: false
-
- if:
properties:
compatible:
@@ -246,8 +214,6 @@ allOf:
- const: phy
- const: mac
- mediatek,pbus-csr: false
-
- if:
properties:
compatible:
@@ -257,7 +223,6 @@ allOf:
then:
properties:
clocks:
- minItems: 4
maxItems: 4
clock-names:
@@ -267,38 +232,6 @@ allOf:
- const: peri_26m
- const: top_133m
- resets:
- minItems: 1
- maxItems: 2
-
- reset-names:
- minItems: 1
- maxItems: 2
-
- mediatek,pbus-csr: false
-
- - if:
- properties:
- compatible:
- const: airoha,en7581-pcie
- then:
- properties:
- clocks:
- maxItems: 1
-
- clock-names:
- items:
- - const: sys-ck
-
- resets:
- minItems: 3
-
- reset-names:
- items:
- - const: phy-lane0
- - const: phy-lane1
- - const: phy-lane2
-
unevaluatedProperties: false
examples:
--
2.53.0
^ permalink raw reply related
* [PATCH 4/4] PCI: mediatek-gen3: Add 2-lanes mode support for Airoha AN7581
From: Christian Marangi @ 2026-06-25 21:57 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Ryder Lee, Michael Turquette,
Stephen Boyd, Brian Masney, Philipp Zabel, Matthias Brugger,
AngeloGioacchino Del Regno, Christian Marangi, Jianjun Wang,
linux-pci, devicetree, linux-kernel, linux-mediatek, linux-clk,
linux-arm-kernel
In-Reply-To: <20260625215741.3253212-1-ansuelsmth@gmail.com>
The Airoha AN7581 SoC supports configuring the first PCIe0 line to 2-lanes
mode by bonding it with the second PCIe line. This is done by configuring
the PCIe MUX in the SCU register.
To correctly configure the line for 2-lanes mode, it's required to define
in DT an additional reg, 'sec-pcie-mac' for the secondary PCIe.
It's also needed to define the additional reset and the PERSTOUT reset.
Also 'airoha,scu' property is mandatory to correctly configure the SCU
register for the PCIe MUX.
Finally to toggle 2-lanes mode, it's needed to define in DT 'num-lanes' as
2.
In such configuration the EQ preset are configured to the same values.
To permit correct configuration of the PCIe line, additional logic is added
to assert and deassert the PERSTOUT resets.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/pci/controller/pcie-mediatek-gen3.c | 98 +++++++++++++++++----
1 file changed, 80 insertions(+), 18 deletions(-)
diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c
index b0accd828589..f750759bbc1d 100644
--- a/drivers/pci/controller/pcie-mediatek-gen3.c
+++ b/drivers/pci/controller/pcie-mediatek-gen3.c
@@ -32,6 +32,11 @@
#include "../pci.h"
+/* AN7581 SCU register */
+#define SCU_PCIC 0x88
+#define SCU_PCIC_PCIE_CTRL GENMASK(7, 0)
+
+/* PCIe register */
#define PCIE_BASE_CFG_REG 0x14
#define PCIE_BASE_CFG_SPEED GENMASK(15, 8)
@@ -131,6 +136,7 @@
#define PCIE_ATR_TLP_TYPE_IO PCIE_ATR_TLP_TYPE(2)
#define MAX_NUM_PHY_RESETS 3
+#define MAX_NUM_PERSTOUT_RESETS 2
#define PCIE_MTK_RESET_TIME_US 10
@@ -203,9 +209,11 @@ struct mtk_msi_set {
struct mtk_gen3_pcie {
struct device *dev;
void __iomem *base;
+ void __iomem *sec_base;
phys_addr_t reg_base;
struct reset_control *mac_reset;
struct reset_control_bulk_data phy_resets[MAX_NUM_PHY_RESETS];
+ struct reset_control_bulk_data perstout_resets[MAX_NUM_PERSTOUT_RESETS];
struct phy *phy;
struct clk_bulk_data *clks;
int num_clks;
@@ -222,6 +230,9 @@ struct mtk_gen3_pcie {
DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_IRQS_NUM);
const struct mtk_gen3_pcie_pdata *soc;
+
+ /* AN7581 specific */
+ struct regmap *scu;
};
/* LTSSM state in PCIE_LTSSM_STATUS_REG bit[28:24] */
@@ -928,6 +939,14 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie)
if (ret)
return dev_err_probe(dev, ret, "failed to get PHY bulk reset\n");
+ pcie->perstout_resets[0].id = "perstout";
+ pcie->perstout_resets[1].id = "sec-perstout";
+
+ ret = devm_reset_control_bulk_get_optional_exclusive(dev, MAX_NUM_PERSTOUT_RESETS,
+ pcie->perstout_resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get PERSTOUT bulk reset\n");
+
pcie->mac_reset = devm_reset_control_get_optional_exclusive(dev, "mac");
if (IS_ERR(pcie->mac_reset))
return dev_err_probe(dev, PTR_ERR(pcie->mac_reset), "failed to get MAC reset\n");
@@ -955,12 +974,29 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie)
static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie)
{
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ unsigned int num_lanes = max(1, pcie->num_lanes);
struct device *dev = pcie->dev;
struct resource_entry *entry;
struct regmap *pbus_regmap;
u32 val, args[2], size;
resource_size_t addr;
- int err;
+ int i, err;
+
+ if (num_lanes == 2) {
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *regs;
+
+ regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sec-pcie-mac");
+ if (!regs)
+ return -EINVAL;
+ pcie->sec_base = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(pcie->sec_base))
+ return dev_err_probe(dev, PTR_ERR(pcie->sec_base), "failed to map secondary register base\n");
+
+ pcie->scu = syscon_regmap_lookup_by_phandle(dev->of_node, "airoha,scu");
+ if (IS_ERR(pcie->scu))
+ return dev_err_probe(dev, PTR_ERR(pcie->scu), "failed to map SCU regmap\n");
+ }
/*
* The controller may have been left out of reset by the bootloader
@@ -1024,34 +1060,60 @@ static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie)
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
- val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) |
- FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) |
- FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) |
- FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41);
- writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG);
+ /* Assert PERSTOUT for all relevant lines */
+ err = reset_control_bulk_assert(MAX_NUM_PERSTOUT_RESETS,
+ pcie->perstout_resets);
+ if (err) {
+ dev_err(dev, "failed to assert PERSTOUTs\n");
+ goto err_perstout_assert;
+ }
+
+ /* Configure SCU MUX to disable PCIE1 for 2 lines mode */
+ if (num_lanes == 2)
+ regmap_update_bits(pcie->scu, SCU_PCIC, SCU_PCIC_PCIE_CTRL,
+ FIELD_PREP(SCU_PCIC_PCIE_CTRL, BIT(1)));
- val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT |
- FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) |
- FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) |
- FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf);
- writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG);
err = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
if (err) {
dev_err(dev, "failed to prepare clock\n");
- goto err_clk_prepare_enable;
+ goto err_perstout_assert;
}
- /*
- * Airoha EN7581 performs PCIe reset via clk callbacks since it has a
- * hw issue with PCIE_PE_RSTB signal. Add wait for the time needed to
- * complete the PCIe reset.
- */
+ /* Wait for refclk to stabilize */
msleep(PCIE_T_PVPERL_MS);
+ /* Configure all the lines to the same EQ config */
+ for (i = 0; i < num_lanes; i++) {
+ void __iomem *base = pcie->base;
+
+ if (i == 1)
+ base = pcie->sec_base;
+
+ val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) |
+ FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) |
+ FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) |
+ FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41);
+ writel_relaxed(val, base + PCIE_EQ_PRESET_01_REG);
+
+ val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT |
+ FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) |
+ FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) |
+ FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf);
+ writel_relaxed(val, base + PCIE_PIPE4_PIE8_REG);
+ }
+
+ /* Deassert PERSTOUT for all relevant lines */
+ err = reset_control_bulk_deassert(MAX_NUM_PERSTOUT_RESETS,
+ pcie->perstout_resets);
+ if (err) {
+ dev_err(dev, "failed to deassert PERSTOUTs\n");
+ goto err_perstout_assert;
+ }
+
return 0;
-err_clk_prepare_enable:
+err_perstout_assert:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
reset_control_bulk_assert(pcie->soc->phy_resets.num_resets,
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v3 3/3] selftests/resctrl: Enable dynamic management of IMC counters via linked list
From: Reinette Chatre @ 2026-06-25 22:14 UTC (permalink / raw)
To: Yifan Wu, tony.luck, Dave.Martin, james.morse, babu.moger, shuah,
tan.shaopeng, fenghuay, ben.horgan, zengheng4, linux-kernel,
linux-arm-kernel, linux-kselftest, linuxarm
Cc: xiaqinxin, prime.zeng, wangyushan12, xuwei5, fanghao11, wangzhou1
In-Reply-To: <20260522090540.444554-4-wuyifan50@huawei.com>
Hi Yifan,
On 5/22/26 2:05 AM, Yifan Wu wrote:
> @@ -242,14 +233,13 @@ static int read_from_imc_dir(char *imc_dir, unsigned int *count)
> * counter's event and umask for the memory read events that will be
> * measured.
> *
> - * Enumerate all these details into an array of structures.
> + * Enumerate all these details into a linked list of structures.
> *
> * Return: >= 0 on success. < 0 on failure.
> */
> -static int num_of_imcs(void)
> +static int enumerate_imcs(void)
The first sentence of this function's comments still describes the behavior being
changed in this patch. How about a fixup like below:
diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
index 129e8d76222a..fe3b3517ae49 100644
--- a/tools/testing/selftests/resctrl/resctrl_val.c
+++ b/tools/testing/selftests/resctrl/resctrl_val.c
@@ -226,12 +226,10 @@ static int read_from_imc_dir(char *imc_dir)
}
/*
- * A system can have 'n' number of iMC (Integrated Memory Controller)
- * counters, get that 'n'. Discover the properties of the available
- * counters in support of needed performance measurement via perf.
- * For each iMC counter get it's type and config. Also obtain each
- * counter's event and umask for the memory read events that will be
- * measured.
+ * Discover the properties of the available iMC (Integrated Memory Controller)
+ * counters in support of needed performance measurement via perf. For each iMC
+ * counter get it's type and config. Also obtain each counter's event and umask
+ * for the memory read events that will be measured.
*
* Enumerate all these details into a linked list of structures.
*
...
> int initialize_read_mem_bw_imc(void)
> {
> - int imc;
> + struct imc_counter_config *imc_counter;
> + int ret;
>
> - imcs = num_of_imcs();
> - if (imcs <= 0)
> - return imcs;
> + ret = enumerate_imcs();
> + if (ret < 0)
> + return ret;
>
> /* Initialize perf_event_attr structures for all iMC's */
> - for (imc = 0; imc < imcs; imc++)
> - read_mem_bw_initialize_perf_event_attr(&imc_counters_config[imc]);
> + list_for_each_entry(imc_counter, &imc_counters_list, entry) {
> + read_mem_bw_initialize_perf_event_attr(imc_counter);
> + }
nit: unnecessary braces
>
> return 0;
> }
> @@ -328,11 +320,11 @@ void cleanup_read_mem_bw_imc(void)
>
> static void perf_close_imc_read_mem_bw(void)
> {
> - int mc;
> + struct imc_counter_config *imc_counter;
>
> - for (mc = 0; mc < imcs; mc++) {
> - if (imc_counters_config[mc].fd != -1)
> - close(imc_counters_config[mc].fd);
> + list_for_each_entry(imc_counter, &imc_counters_list, entry) {
> + if (imc_counter->fd != -1)
> + close(imc_counter->fd);
> }
> }
>
With fixup applied and nit addressed:
| Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Reinette
^ permalink raw reply related
* Re: [PATCH v3 1/3] selftests/resctrl: Introduce linked list management for IMC counters
From: Reinette Chatre @ 2026-06-25 22:14 UTC (permalink / raw)
To: Yifan Wu, tony.luck, Dave.Martin, james.morse, babu.moger, shuah,
tan.shaopeng, fenghuay, ben.horgan, zengheng4, linux-kernel,
linux-arm-kernel, linux-kselftest, linuxarm
Cc: xiaqinxin, prime.zeng, wangyushan12, xuwei5, fanghao11, wangzhou1
In-Reply-To: <20260522090540.444554-2-wuyifan50@huawei.com>
Hi Yifan,
On 5/22/26 2:05 AM, Yifan Wu wrote:
> The static array approach to managing IMC counters has fixed size
> constraints and limited compatibility and scalability. Introduce
> a linked list-based dynamic management infrastructure to address
> these limitations. Add the core data structure definitions and
> memory allocation and cleanup functions for dynamic counter
> configurations.
>
> Signed-off-by: Yifan Wu <wuyifan50@huawei.com>
> ---
Thank you.
Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Reinette
^ permalink raw reply
* Re: [PATCH v3 2/3] selftests/resctrl: Replace counter index references with pointers
From: Reinette Chatre @ 2026-06-25 22:14 UTC (permalink / raw)
To: Yifan Wu, tony.luck, Dave.Martin, james.morse, babu.moger, shuah,
tan.shaopeng, fenghuay, ben.horgan, zengheng4, linux-kernel,
linux-arm-kernel, linux-kselftest, linuxarm
Cc: xiaqinxin, prime.zeng, wangyushan12, xuwei5, fanghao11, wangzhou1
In-Reply-To: <20260522090540.444554-3-wuyifan50@huawei.com>
Hi Yifan,
Thank you. I just have a few style fixup comments ...
On 5/22/26 2:05 AM, Yifan Wu wrote:
> Replace direct counter number references with pointers to remove the
> dependency on fixed array indexing and enable the use of different
> data structures for counter management.
>
> Signed-off-by: Yifan Wu <wuyifan50@huawei.com>
> ---
> tools/testing/selftests/resctrl/resctrl_val.c | 62 +++++++++----------
> 1 file changed, 31 insertions(+), 31 deletions(-)
>
> diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
> index a72dc4ae61fe..3d2b6919717a 100644
> --- a/tools/testing/selftests/resctrl/resctrl_val.c
> +++ b/tools/testing/selftests/resctrl/resctrl_val.c
> @@ -42,40 +42,40 @@ static struct imc_counter_config imc_counters_config[MAX_IMCS];
> LIST_HEAD(imc_counters_list);
> static const struct resctrl_test *current_test;
>
> -static void read_mem_bw_initialize_perf_event_attr(int i)
> +static void read_mem_bw_initialize_perf_event_attr(struct imc_counter_config *imc_counter)
> {
> - memset(&imc_counters_config[i].pe, 0,
> + memset(&imc_counter->pe, 0,
> sizeof(struct perf_event_attr));
nit: above can fit on a single line
> - imc_counters_config[i].pe.type = imc_counters_config[i].type;
> - imc_counters_config[i].pe.size = sizeof(struct perf_event_attr);
> - imc_counters_config[i].pe.disabled = 1;
> - imc_counters_config[i].pe.inherit = 1;
> - imc_counters_config[i].pe.exclude_guest = 0;
> - imc_counters_config[i].pe.config =
> - imc_counters_config[i].umask << 8 |
> - imc_counters_config[i].event;
> - imc_counters_config[i].pe.sample_type = PERF_SAMPLE_IDENTIFIER;
> - imc_counters_config[i].pe.read_format =
> + imc_counter->pe.type = imc_counter->type;
> + imc_counter->pe.size = sizeof(struct perf_event_attr);
> + imc_counter->pe.disabled = 1;
> + imc_counter->pe.inherit = 1;
> + imc_counter->pe.exclude_guest = 0;
> + imc_counter->pe.config =
> + imc_counter->umask << 8 |
> + imc_counter->event;
nit: above can fit on a single line
> + imc_counter->pe.sample_type = PERF_SAMPLE_IDENTIFIER;
> + imc_counter->pe.read_format =
> PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
> }
>
...
> @@ -89,21 +89,21 @@ static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count)
> if (!token[i])
> break;
> if (strcmp(token[i], "event") == 0)
> - imc_counters_config[count].event = strtol(token[i + 1], NULL, 16);
> + imc_counter->event = strtol(token[i + 1], NULL, 16);
> if (strcmp(token[i], "umask") == 0)
> - imc_counters_config[count].umask = strtol(token[i + 1], NULL, 16);
> + imc_counter->umask = strtol(token[i + 1], NULL, 16);
> }
> }
>
> -static int open_perf_read_event(int i, int cpu_no)
> +static int open_perf_read_event(int cpu_no, struct imc_counter_config *imc_counter)
> {
> - imc_counters_config[i].fd =
> - perf_event_open(&imc_counters_config[i].pe, -1, cpu_no, -1,
> + imc_counter->fd =
> + perf_event_open(&imc_counter->pe, -1, cpu_no, -1,
> PERF_FLAG_FD_CLOEXEC);
Please improve readibility here by moving the above two lines up while ensuring alignment with
open parenthesis. Specifically:
imc_counter->fd = perf_event_open(&imc_counter->pe, -1, cpu_no, -1,
PERF_FLAG_FD_CLOEXEC);
...
With the style fixups:
| Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Reinette
^ permalink raw reply
* Re: [PATCH v14 4/5] gpio: rpmsg: add generic rpmsg GPIO driver
From: Julian Braha @ 2026-06-25 22:17 UTC (permalink / raw)
To: Shenwei Wang, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Mathieu Poirier, Frank Li, Sascha Hauer
Cc: Shuah Khan, linux-gpio, linux-doc, linux-kernel,
Pengutronix Kernel Team, Fabio Estevam, Shenwei Wang, Peng Fan,
devicetree, linux-remoteproc, imx, linux-arm-kernel, linux-imx,
Arnaud POULIQUEN, b-padhi, Andrew Lunn, Bartosz Golaszewski
In-Reply-To: <20260625155432.815185-5-shenwei.wang@oss.nxp.com>
Hi Shenwei,
On 6/25/26 16:54, Shenwei Wang wrote:
> +config GPIO_RPMSG
> + tristate "Generic RPMSG GPIO support"
> + depends on OF && REMOTEPROC
> + select GPIOLIB_IRQCHIP
> + default REMOTEPROC
> + help
> + Say yes here to support the generic GPIO functions over the RPMSG
> + bus. Currently supported devices: i.MX7ULP, i.MX8ULP, i.MX8x, and
> + i.MX9x.
> +
> + If unsure, say N.
You've got GPIO_RPMSG defaulting to REMOTEPROC, which is also a
dependency, so it's effectively the same as using 'default y'.
But then you've got "If unsure, say N" in the help text. Usually this is
used when the default is N.
- Julian Braha
^ permalink raw reply
* Re: [PATCH net] net: airoha: dma map xmit frags with skb_frag_dma_map()
From: Harshitha Ramamurthy @ 2026-06-25 22:59 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-arm-kernel, linux-mediatek, netdev
In-Reply-To: <20260625-airoha-eth-skb_frag_dma_map-v1-1-31d9e460aae6@kernel.org>
On Thu, Jun 25, 2026 at 2:43 AM Lorenzo Bianconi <lorenzo@kernel.org> wrote:
>
> Map xmit skb fragments using skb_frag_dma_map() instead of
> dma_map_single(skb_frag_address()). skb_frag_address() relies on
> page_address() to obtain a kernel virtual address, which is not
> guaranteed to work for all page types (e.g. highmem pages or
> user-pinned pages from MSG_ZEROCOPY).
> skb_frag_dma_map() maps the fragment directly via its struct page and
> offset through dma_map_page(), avoiding the need for a kernel virtual
> address entirely.
> Introduce an enum airoha_dma_map_type to track how each queue entry was
> mapped (single vs page), so that the matching unmap function is called
> on completion and in error paths.
>
> Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC")
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Harshitha Ramamurthy <hramamurthy@google.com>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 61 ++++++++++++++++++++------------
> drivers/net/ethernet/airoha/airoha_eth.h | 7 ++++
> 2 files changed, 45 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 932b3a3df2e5..1caf6766f2c0 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -944,6 +944,25 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
> q->txq_stopped = false;
> }
>
> +static void airoha_unmap_xmit_buf(struct airoha_eth *eth,
> + struct airoha_queue_entry *e)
> +{
> + switch (e->dma_type) {
> + case AIROHA_DMA_MAP_PAGE:
> + dma_unmap_page(eth->dev, e->dma_addr, e->dma_len,
> + DMA_TO_DEVICE);
> + break;
> + case AIROHA_DMA_MAP_SINGLE:
> + dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
> + DMA_TO_DEVICE);
> + break;
> + case AIROHA_DMA_UNMAPPED:
> + default:
> + break;
> + }
> + e->dma_type = AIROHA_DMA_UNMAPPED;
> +}
> +
> static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)
> {
> struct airoha_tx_irq_queue *irq_q;
> @@ -1006,9 +1025,7 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)
> skb = e->skb;
> e->skb = NULL;
>
> - dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
> - DMA_TO_DEVICE);
> - e->dma_addr = 0;
> + airoha_unmap_xmit_buf(eth, e);
> list_add_tail(&e->list, &q->tx_list);
>
> WRITE_ONCE(desc->msg0, 0);
> @@ -1177,12 +1194,10 @@ static void airoha_qdma_tx_cleanup(struct airoha_qdma *qdma)
> struct airoha_qdma_desc *desc = &q->desc[j];
> struct sk_buff *skb = e->skb;
>
> - if (!e->dma_addr)
> + if (e->dma_type == AIROHA_DMA_UNMAPPED)
> continue;
>
> - dma_unmap_single(qdma->eth->dev, e->dma_addr,
> - e->dma_len, DMA_TO_DEVICE);
> - e->dma_addr = 0;
> + airoha_unmap_xmit_buf(qdma->eth, e);
> list_add_tail(&e->list, &q->tx_list);
>
> WRITE_ONCE(desc->ctrl, 0);
> @@ -2193,8 +2208,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> struct netdev_queue *txq;
> struct airoha_queue *q;
> LIST_HEAD(tx_list);
> + dma_addr_t addr;
> int i = 0, qid;
> - void *data;
> u16 index;
> u8 fport;
>
> @@ -2250,24 +2265,22 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> return NETDEV_TX_BUSY;
> }
>
> - len = skb_headlen(skb);
> - data = skb->data;
> -
> e = list_first_entry(&q->tx_list, struct airoha_queue_entry,
> list);
> + len = skb_headlen(skb);
> + addr = dma_map_single(netdev->dev.parent, skb->data, len,
> + DMA_TO_DEVICE);
> + if (unlikely(dma_mapping_error(netdev->dev.parent, addr)))
> + goto error_unlock;
> +
> + e->dma_type = AIROHA_DMA_MAP_SINGLE;
> index = e - q->entry;
>
> while (true) {
> struct airoha_qdma_desc *desc = &q->desc[index];
> skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> - dma_addr_t addr;
> u32 val;
>
> - addr = dma_map_single(netdev->dev.parent, data, len,
> - DMA_TO_DEVICE);
> - if (unlikely(dma_mapping_error(netdev->dev.parent, addr)))
> - goto error_unmap;
> -
> list_move_tail(&e->list, &tx_list);
> e->skb = i == nr_frags - 1 ? skb : NULL;
> e->dma_addr = addr;
> @@ -2291,8 +2304,13 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> if (++i == nr_frags)
> break;
>
> - data = skb_frag_address(frag);
> len = skb_frag_size(frag);
> + addr = skb_frag_dma_map(netdev->dev.parent, frag, 0, len,
> + DMA_TO_DEVICE);
> + if (unlikely(dma_mapping_error(netdev->dev.parent, addr)))
> + goto error_unmap;
> +
> + e->dma_type = AIROHA_DMA_MAP_PAGE;
> }
> q->queued += i;
>
> @@ -2313,11 +2331,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> return NETDEV_TX_OK;
>
> error_unmap:
> - list_for_each_entry(e, &tx_list, list) {
> - dma_unmap_single(netdev->dev.parent, e->dma_addr, e->dma_len,
> - DMA_TO_DEVICE);
> - e->dma_addr = 0;
> - }
> + list_for_each_entry(e, &tx_list, list)
> + airoha_unmap_xmit_buf(dev->eth, e);
> list_splice(&tx_list, &q->tx_list);
> error_unlock:
> spin_unlock_bh(&q->lock);
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index d7ff8c5200e2..2765244d937c 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -170,12 +170,19 @@ enum trtcm_param {
> #define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6)
> #define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0)
>
> +enum airoha_dma_map_type {
> + AIROHA_DMA_UNMAPPED,
> + AIROHA_DMA_MAP_SINGLE,
> + AIROHA_DMA_MAP_PAGE,
> +};
> +
> struct airoha_queue_entry {
> union {
> void *buf;
> struct {
> struct list_head list;
> struct sk_buff *skb;
> + enum airoha_dma_map_type dma_type;
> };
> };
> dma_addr_t dma_addr;
>
> ---
> base-commit: 232c4ca2343d1181cbfc061f9856d9591e397579
> change-id: 20260625-airoha-eth-skb_frag_dma_map-bcccd5d6e4b1
>
> Best regards,
> --
> Lorenzo Bianconi <lorenzo@kernel.org>
>
>
^ permalink raw reply
* Re: [PATCH v4 2/2] tracing: Remove trace_printk.h from kernel.h
From: Nathan Chancellor @ 2026-06-25 23:41 UTC (permalink / raw)
To: Steven Rostedt
Cc: linux-kernel, linux-trace-kernel, Masami Hiramatsu, Mark Rutland,
Mathieu Desnoyers, Andrew Morton, Linus Torvalds,
Sebastian Andrzej Siewior, John Ogness, Thomas Gleixner,
Peter Zijlstra, Julia Lawall, Yury Norov, linux-doc, linux-kbuild,
linuxppc-dev, dri-devel, linux-stm32, linux-arm-kernel,
linux-rdma, linux-usb, linux-ext4, linux-nfs, kvm, intel-gfx
In-Reply-To: <20260625104402.210473477@kernel.org>
Hi Steve,
On Thu, Jun 25, 2026 at 06:40:09AM -0400, Steven Rostedt wrote:
> From: Steven Rostedt <rostedt@goodmis.org>
>
> There have been complaints about trace_printk.h causing more build time
> for being in kernel.h if it changes. There is also an effort to clean up
> kernel.h to have it not include unneeded header files. Move trace_printk.h
> out of kernel.h and place it in the headers and C files that use it.
>
> Link: https://lore.kernel.org/all/CAHk-=wikCBeVFjVXiY4o-oepdbjAoir5+TcAgtL12c4u1TpZLQ@mail.gmail.com/
>
> Suggested-by: Yury Norov <yury.norov@gmail.com>
> Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This patch breaks lib/test_context-analysis.c for me in several
configurations:
In file included from lib/test_context-analysis.c:9:
In file included from include/linux/local_lock.h:5:
include/linux/local_lock_internal.h:46:2: error: use of undeclared identifier '_THIS_IP_'
46 | lock_map_acquire(&l->dep_map);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/lockdep.h:541:69: note: expanded from macro 'lock_map_acquire'
541 | #define lock_map_acquire(l) lock_acquire_exclusive(l, 0, 0, NULL, _THIS_IP_)
| ^~~~~~~~~
In file included from lib/test_context-analysis.c:9:
In file included from include/linux/local_lock.h:5:
include/linux/local_lock_internal.h:53:2: error: use of undeclared identifier '_THIS_IP_'
53 | lock_map_acquire_try(&l->dep_map);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/lockdep.h:542:73: note: expanded from macro 'lock_map_acquire_try'
542 | #define lock_map_acquire_try(l) lock_acquire_exclusive(l, 0, 1, NULL, _THIS_IP_)
| ^~~~~~~~~
In file included from lib/test_context-analysis.c:9:
In file included from include/linux/local_lock.h:5:
include/linux/local_lock_internal.h:62:2: error: use of undeclared identifier '_THIS_IP_'
62 | lock_map_release(&l->dep_map);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/lockdep.h:545:47: note: expanded from macro 'lock_map_release'
545 | #define lock_map_release(l) lock_release(l, _THIS_IP_)
| ^~~~~~~~~
3 errors generated.
The following diff resolves it for me, should I send it as a separate
patch or do you want to just fold it in with a note?
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index 621566345406..2301a701ffbb 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -10,6 +10,7 @@
#ifndef __LINUX_LOCKDEP_H
#define __LINUX_LOCKDEP_H
+#include <linux/instruction_pointer.h>
#include <linux/lockdep_types.h>
#include <linux/smp.h>
#include <asm/percpu.h>
--
Cheers,
Nathan
^ permalink raw reply related
* [PATCH RFC 0/4] media: i2c: Add Samsung S5K3L6 and Librem 5 rear camera
From: Vincent Cloutier @ 2026-06-26 0:06 UTC (permalink / raw)
To: linux-media, devicetree, linux-arm-kernel
Cc: linux-kernel, linux-imx, kernel, Vincent Cloutier
From: Vincent Cloutier <vincent@cloutier.co>
This RFC fixes the i.MX8MQ CSI-2 reset path needed by Librem 5 camera
capture, adds initial upstream support for the Samsung S5K3L6 image
sensor, and wires it up as the rear camera on the Purism Librem 5.
This is intentionally a rewrite, not a direct forwarding of the Librem 5
downstream S5K3L6 carry. The downstream history is a long bring-up
series with debugfs register overrides, debug-frame plumbing, commented-out
old subdev code, FIXME/TODO scaffolding, and several API-era migrations.
This RFC collapses the usable production path into a current V4L2 sensor
driver using CCI regmap, runtime PM, fwnode endpoint validation, and the
current subdev stream API.
Many thanks to Martin Kepplinger, Dorota Czaplejewicz, and Sebastian
Krzyszkowiak for the original Librem 5 S5K3L6 driver work and rear-camera
bring-up. Martin authored the initial downstream driver, and Dorota and
Sebastian substantially developed the mode tables, controls, power-up
sequence, and sensor tuning that this rewrite is based on.
This series covers:
- 25 MHz input clock, matching the Librem 5 downstream configuration
- two MIPI CSI-2 data lanes
- RAW8 and RAW10 SGRBG modes at 1052x780, 2104x1560, and 4208x3120
- exposure, analogue gain, digital gain, blanking, pixel-rate,
link-frequency, test-pattern, orientation, and rotation controls
- Librem 5 DTS integration for the rear sensor and second CSI-2 path
The rewritten driver and DTS path have now been tested on Librem 5r4
hardware in a v7.1.1 carry build, with the i.MX8MQ CSI-2 reset fix from this
series applied.
Patch 1 keeps the i.MX8MQ CSI-2 software reset sequence compatible with
the Librem 5 camera pipeline by making the post-assert reset release
SoC-specific. This is included as an RFC prerequisite for the Librem 5
rear-camera enablement; if preferred, it can be split out and handled as
a separate media/platform fix.
Vincent Cloutier (4):
media: imx8mq-mipi-csi2: Keep i.MX8MQ reset assert-only
dt-bindings: media: i2c: Add Samsung S5K3L6 image sensor
media: i2c: Add Samsung S5K3L6 image sensor driver
arm64: dts: imx8mq-librem5: Add rear camera
.../bindings/media/i2c/samsung,s5k3l6.yaml | 117 ++
arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi | 51 +
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/s5k3l6.c | 1055 +++++++++++++++++
drivers/media/platform/nxp/imx8mq-mipi-csi2.c | 9 +-
6 files changed, 1241 insertions(+), 2 deletions(-)
--
2.53.0
^ permalink raw reply
* [PATCH RFC 1/4] media: imx8mq-mipi-csi2: Make reset release SoC-specific
From: Vincent Cloutier @ 2026-06-26 0:06 UTC (permalink / raw)
To: linux-media, devicetree, linux-arm-kernel
Cc: linux-kernel, linux-imx, kernel, Vincent Cloutier,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Mauro Carvalho Chehab, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Philipp Zabel, imx
In-Reply-To: <20260626000715.1111803-1-vincent.cloutier@icloud.com>
From: Vincent Cloutier <vincent@cloutier.co>
The CSI-2 software reset helper currently asserts the reset control and
then releases it again unconditionally.
That release step is required by the i.MX8QXP path, but it changes the
reset sequence used by i.MX8MQ. On Librem 5r4, which is i.MX8MQ-based,
the unconditional release step prevents the camera pipeline from producing
frames after reset; captures time out waiting for EOF from the CSI bridge.
This series enables the Librem 5 rear camera on the second i.MX8MQ CSI-2
receiver. Keep the i.MX8MQ path on the known-working assert-only software
reset sequence while preserving the explicit release step for i.MX8QXP.
Make reset release opt-in through platform data.
Tested on Librem 5r4 with the existing HI846 front camera and the S5K3L6
rear camera added by this series.
Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
Assisted-by: OpenCode:gpt-5.5
---
drivers/media/platform/nxp/imx8mq-mipi-csi2.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index 950793297496..xxxxxxxxxxxx 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -76,6 +76,7 @@ struct imx8mq_plat_data {
int (*enable)(struct csi_state *state, u32 hs_settle);
void (*disable)(struct csi_state *state);
bool use_reg_csr;
+ bool needs_reset_deassert;
};
/*
@@ -244,6 +245,7 @@ static const struct imx8mq_plat_data imx8qxp_data = {
.enable = imx8qxp_gpr_enable,
.disable = imx8qxp_gpr_disable,
.use_reg_csr = true,
+ .needs_reset_deassert = true,
};
static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
@@ -363,8 +365,12 @@ static int imx8mq_mipi_csi_sw_reset(struct csi_state *state)
return ret;
}
- /* Explicitly release reset to make sure reset bits are cleared. */
- return reset_control_deassert(state->rst);
+ /*
+ * Some SoC integrations require an explicit release after reset
+ * assertion. Keep this SoC-specific so i.MX8MQ retains its
+ * known-working assert-only sequence.
+ */
+ if (!state->pdata->needs_reset_deassert)
+ return 0;
+
+ return reset_control_deassert(state->rst);
}
static void imx8mq_mipi_csi_set_params(struct csi_state *state)
--
2.53.0
^ permalink raw reply
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