* Re: [PATCH v5 05/14] ASoC: rsnd: Add audmacpp clock and reset support for RZ/G3E
From: Mark Brown @ 2026-04-16 18:57 UTC (permalink / raw)
To: John Madieu
Cc: Kuninori Morimoto, Liam Girdwood, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Geert Uytterhoeven, Magnus Damm, Philipp Zabel, Claudiu Beznea,
Biju Das, linux-sound, linux-renesas-soc, devicetree,
linux-kernel, John Madieu
In-Reply-To: <20260415124731.3684773-6-john.madieu.xa@bp.renesas.com>
[-- Attachment #1: Type: text/plain, Size: 918 bytes --]
On Wed, Apr 15, 2026 at 12:47:22PM +0000, John Madieu wrote:
> + /*
> + * Audio DMAC peri-peri clock and reset for RZ/G3E.
> + * These use optional APIs, so they gracefully return NULL
> + * (no error) on platforms whose DT does not provide them.
> + */
> + dmac->audmapp_rstc =
> + devm_reset_control_get_optional_exclusive_deasserted(dev, "audmapp");
> + if (IS_ERR(dmac->audmapp_rstc)) {
> + return dev_err_probe(dev, PTR_ERR(dmac->audmapp_rstc),
> + "failed to get audmapp reset\n");
> + }
> +
> + dmac->audmapp_clk = devm_clk_get_optional_enabled(dev, "audmapp");
> + if (IS_ERR(dmac->audmapp_clk)) {
> + return dev_err_probe(dev, PTR_ERR(dmac->audmapp_clk),
> + "failed to get audmapp clock\n");
> + }
Do we need the clock running before deasserting reset? Usually the flow
is to get the resources the hardware requires stable before we release,
that helps everything start up cleanly.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v4 1/2] dt-bindings: pwm: dwc: add reset optional
From: Uwe Kleine-König @ 2026-04-16 19:53 UTC (permalink / raw)
To: dongxuyang
Cc: robh, krzk+dt, conor+dt, ben-linux, ben.dooks, p.zabel, linux-pwm,
devicetree, linux-kernel, ningyu, linmin, xuxiang, wangguosheng,
pinkesh.vaghela
In-Reply-To: <20260415095020.1597-1-dongxuyang@eswincomputing.com>
[-- Attachment #1: Type: text/plain, Size: 94 bytes --]
Hello,
I suggest
dt-bindings: pwm: dwc: Add optional reset
as shortlog.
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH v4 0/8] Add support for ZTE zx297520v3
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
Hi,
This is a follow-up on my RFC patches from January [0] for ZTE's
zx297520v3 chipset. This chipset is popular in cheap LTE-to-wifi routers
sold in developing countries. My goal is to run OpenWRT on them. I made
more progress in more work on this SoC and it is time to get serious
about code review and upstreaming.
Since my version in January I managed to get more hardware running: SPI,
I2C, PMIC with real time clock and voltage regulators, Watchdog. LTE is
not working yet, but I am able to start the coprocessor that handles it
and talk to it via mailbox + shared memory. Wifi is working on a few
more devices. Since WiFi, USB and Ethernet are working, the devices can
have actual use with OpenWRT even without LTE.
Another hacker created a free software program to talk to the USB loader
[1] and boot U-Boot and Linux without modifying the on disk files. At
the moment it needs a proprietary blob, so my documentation is
emphasising booting with the on-device U-Boot.
This patchset here is mostly unmodified from the version I sent in
January. It is the bare minimum to get an interactive shell working on
the UART. Future patches can be found on my git repository [2] for those
curious to peek ahead. The first 30 patches are in reasonable shape, but
the further you go the more cleanup is necessary. I expect all of the
patches go require a few rounds of feedback though.
My plan for upstreaming is largly this:
1) This bare minimum boot patchset
2) Add clock and pinctrl drivers
3) Add standard hardware to the device tree
4) Add zx29 specific drivers one by one: Watchdog, spi, i2c, DMA, PMIC,
battery
5) SDIO backend for rtl8xxxu
6) rproc, mailbox and rpmsg
I am willing to maintain support for the SoC within reason. My patches
add myself as maintainer. This is a hobby project for me though, keep
that in mind if you want to ship a commercial product with these SoCs
and upstreaming Linux.
Cheers,
Stefan
0: https://lists.infradead.org/pipermail/linux-arm-kernel/2026-January/1099306.html
1: https://github.com/zx297520v3-mainline/zx297520v3-loader
2: https://gitlab.com/stefandoesinger/zx297520-kernel/
Patch changelog:
v4: rename zx29.yaml to zte.yaml and add board enums
v3: Remove [RFC] tag, add defconfig
v2: checkpatch.pl fixes
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Stefan Dösinger (8):
ARM: zte: Add zx297520v3 platform support
dt-bindings: arm: Add zx297520v3 board binding
ARM: dts: Add D-Link DWR-932M support
ARM: zte: Add support for zx29 low level debug
ARM: dts: Add an armv7 timer for zx297520v3
ARM: zte: Bring back zx29 UART support
ARM: dts: Declare UART1 on zx297520v3 boards
ARM: defconfig: Add a zx29 defconfig file
Documentation/arch/arm/zte/zx297520v3.rst | 158 +++++++++++++++++++++++++
Documentation/devicetree/bindings/arm/zte.yaml | 25 ++++
MAINTAINERS | 6 +
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 12 ++
arch/arm/Makefile | 1 +
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/zte/Makefile | 3 +
arch/arm/boot/dts/zte/dlink-dwr-932m.dts | 21 ++++
arch/arm/boot/dts/zte/zx297520v3.dtsi | 83 +++++++++++++
arch/arm/configs/zx29_defconfig | 90 ++++++++++++++
arch/arm/include/debug/pl01x.S | 7 ++
arch/arm/mach-zte/Kconfig | 24 ++++
arch/arm/mach-zte/Makefile | 2 +
arch/arm/mach-zte/zx297520v3.c | 19 +++
drivers/tty/serial/amba-pl011.c | 37 ++++++
include/linux/amba/bus.h | 6 +
17 files changed, 497 insertions(+)
---
base-commit: 028ef9c96e96197026887c0f092424679298aae8
change-id: 20260416-send-5c08e095e5c9
Best regards,
--
Stefan Dösinger <stefandoesinger@gmail.com>
^ permalink raw reply
* [PATCH v4 1/8] ARM: zte: Add zx297520v3 platform support
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This SoC is used in low end LTE-to-WiFi routers, for example some D-Link
DWR 932 revisions, ZTE K10, ZLT S10 4G, but also models that are branded
and sold by ISPs themselves. They are widespread in Africa, China,
Russia and Eastern Europe.
This SoC is a relative of the zx296702 and zx296718 that had some
upstream support until commit 89d4f98ae90d ("ARM: remove zte zx
platform"). My eventual goal is to enable OpenWRT to run on these
devices.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Documentation/arch/arm/zte/zx297520v3.rst | 158 ++++++++++++++++++++++++++++++
MAINTAINERS | 4 +
arch/arm/Kconfig | 2 +
arch/arm/Makefile | 1 +
arch/arm/mach-zte/Kconfig | 24 +++++
arch/arm/mach-zte/Makefile | 2 +
arch/arm/mach-zte/zx297520v3.c | 19 ++++
7 files changed, 210 insertions(+)
diff --git a/Documentation/arch/arm/zte/zx297520v3.rst b/Documentation/arch/arm/zte/zx297520v3.rst
new file mode 100644
index 000000000000..a0f25ade0a3d
--- /dev/null
+++ b/Documentation/arch/arm/zte/zx297520v3.rst
@@ -0,0 +1,158 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================================
+Booting Linux on ZTE zx297520v3 SoCs
+====================================
+
+...............................................................................
+
+Author: Stefan Dösinger
+
+Date : 27 Jan 2026
+
+1. Hardware description
+---------------------------
+Zx297520v3 SoCs use a 64 bit capable Cortex-A53 CPU and GICv3, although they
+run in aarch32 mode only. The CPU has support EL3, but no hypervisor (EL2) and
+it seems to lack VFP and NEON.
+
+The SoC is used in a number of cheap LTE to Wifi routers, both battery powered
+MiFis and stationary CPEs. In addition to the CPU these devices usually have
+64 MB Ram (although some is shared with the LTE chip), 128 MB NAND flash, an
+SDIO connected RTL8192-type Wifi chip limited to 2.4 ghz operation, USB 2,
+and buttons. Devices with as low as 32 MB or as high as 128 MB ram exist, as
+do devices with 8 or 16 MB of NOR flash.
+
+Some devices, especially the stationary ones, have 100 mbit Ethernet and an
+Ethernet switch.
+
+Usually the devices have LEDs for status indication, although some have SPI or
+i2c connected displays
+
+Some have an SD card slot. If it exists, it is a better choice for the root
+file system because it easily outperforms the built-in NAND.
+
+The LTE interface runs on a separate DSP called ZSP880. It is probably derived
+from LSI ZSPs and has an undocumented instruction set. The ZSP communicates
+with the main CPU via SRAM and DRAM and a mailbox hardware that can generate
+IRQs on either ends.
+
+There is also a Cortex M0 CPU, which is responsible for early HW initialization
+and starting the Cortex A53 CPU. It does not have any essential purpose once
+U-Boot is started. A SRAM-Based handover protocol exists to run custom code on
+this CPU.
+
+2. Booting via USB
+---------------------------
+
+The Boot ROM has support for booting custom code via USB. This mode can be
+entered by connecting a Boot PIN to GND or by modifying the third byte on NAND
+(set it to anything other than 0x5A aka 'Z'). A free software tool to start
+custom uboot and kernels can be found here:
+
+https://github.com/zx297520v3-mainline/zx297520v3-loader
+
+If USB download mode is entered but no boot commands are sent through USB, the
+device will proceed to boot normally after a few seconds. It is therefore
+possible to enable USB boot permanently and still leave the default boot files
+in place.
+
+3. Building for built-in U-Boot
+---------------------------
+The devices come with an ancient U-Boot that loads legacy uImages from NAND and
+boots them without a chance for the user to interrupt. The images are stored in
+files ap_cpuap.bin and ap_recovery.bin on a jffs2 partition named imagefs,
+usually mtd4. A file named "fotaflag" switches between the two modes.
+
+In addition to the uImage header, those files have a 384 byte signature header,
+which is used for authenticating the images on some devices. Most devices have
+this authentication disabled and it is enough to pad the uImage files with 384
+zero bytes.
+
+Builtin U-Boot also poorly sets up the CPU. Read the next section for details
+on this. It has no support for loading DTBs, so CONFIG_ARM_APPENDED_DTB is
+needed.
+
+So to build an image that boots from NAND the following steps are necessary:
+
+1) Patch the assembly code from section 3 into arch/arm/kernel/head.S.
+2) make zx29_defconfig
+3) make [-j x]
+4) cat arch/arm/boot/zImage arch/arm/boot/dts/zte/[device].dtb > kernel+dtb
+5) mkimage -A arm -O linux -T kernel -C none -a 0x20008000 -d kernel+dtb uimg
+6) dd if=/dev/zero bs=1 count=384 of=ap_recovery.bin
+7) cat uimg >> ap_recovery.bin
+8) Place this file onto imagefs on the device. Delete ap_cpuap.bin if the
+free space is not enough.
+9) Create the file fotaflag: echo -n FOTA-RECOVERY > fotaflag
+
+For development, booting ap_recovery.bin is recommended because the normal boot
+mode arms the watchdog before starting the kernel.
+
+4. CPU and GIC Setup
+---------------------------
+
+Generally CPU and GICv3 need to be set up according to the requirements spelled
+out in Documentation/arch/arm64/booting.rst. For zx297520v3 this means:
+
+1. GICD_CTLR.DS=1 to disable GIC security
+2. Enable access to ICC_SRE
+3. Disable trapping IRQs into monitor mode
+4. Configure EL2 and below to run in insecure mode.
+5. Configure timer PPIs to active-low.
+
+The kernel sources provided by ZTE do not boot either (interrupts do not work
+at all). They are incomplete in other aspects too, so it is assumed that there
+is some workaround similar to the one described in this document somewhere in
+the binary blobs.
+
+The assembly code below is given as an example of how to achieve this:
+
+```
+#include <linux/irqchip/arm-gic-v3.h>
+#include <asm/assembler.h>
+#include <asm/cp15.h>
+
+@ This allows EL1 to handle ints hat are normally handled by EL2/3.
+ldr r3, =0xf2000000
+ldr r4, =#(GICD_CTLR_ARE_NS | GICD_CTLR_DS)
+str r4, [r3]
+
+cps #MON_MODE
+
+@ Work in non-secure physical address space: SCR_EL3.NS = 1. At least the UART
+@ seems to respond only to non-secure addresses. I have taken insipiration from
+@ Raspberry pi's armstub7.S here.
+@
+@ ARM docs say modify this bit in monitor mode only...
+mov r3, #0x131 @ non-secure, Make F, A bits in CPSR writeable
+ @ Allow hypervisor call.
+mcr p15, 0, r3, c1, c1, 0
+
+@ AP_PPI_MODE_REG: Configure timer PPIs (10, 11, 13, 14) to active-low.
+ldr r3, =0xF22020a8
+ldr r4, =0x50
+str r4, [r3]
+ldr r3, =0xF22020ac
+ldr r4, =0x14
+str r4, [r3]
+
+@ Enable EL2 access to ICC_SRE (bit 3, ICC_SRE_EL3.Enable). Enable system reg
+@ access to GICv3 registers (bit 0, ICC_SRE_EL3.SRE) for EL1 and EL3.
+mrc p15, 6, r3, c12, c12, 5 @ ICC_SRE_EL3
+orr r3, #0x9 @ FIXME: No defines for SRE_EL3 values?
+mcr p15, 6, r3, c12, c12, 5
+mrc p15, 0, r3, c12, c12, 5 @ ICC_SRE_EL1
+orr r3, #(ICC_SRE_EL1_SRE)
+mcr p15, 0, r3, c12, c12, 5
+
+@ Like ICC_SRE_EL3, enable EL1 access to ICC_SRE and system register access
+@ for EL2.
+mrc p15, 4, r3, c12, c9, 5 @ ICC_SRE_EL2 aka ICC_HSRE
+orr r3, r3, #(ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE)
+mcr p15, 4, r3, c12, c9, 5
+isb
+
+@ Back to SVC mode. TODO: Doesn't safe_svcmode_maskall do this for us anyway?
+cps #SVC_MODE
+```
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f..974d7a98956a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29200,6 +29200,10 @@ F: include/linux/zswap.h
F: mm/zswap.c
F: tools/testing/selftests/cgroup/test_zswap.c
+ZX29
+M: Stefan Dösinger <stefandoesinger@gmail.com>
+F: arch/arm/mach-zte/
+
SENARYTECH AUDIO CODEC DRIVER
M: bo liu <bo.liu@senarytech.com>
S: Maintained
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ec33376f8e2b..4217ed704e48 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -464,6 +464,8 @@ source "arch/arm/mach-versatile/Kconfig"
source "arch/arm/mach-vt8500/Kconfig"
+source "arch/arm/mach-zte/Kconfig"
+
source "arch/arm/mach-zynq/Kconfig"
# ARMv7-M architecture
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b7de4b6b284c..573813ef5e77 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -223,6 +223,7 @@ machine-$(CONFIG_ARCH_SUNXI) += sunxi
machine-$(CONFIG_ARCH_TEGRA) += tegra
machine-$(CONFIG_ARCH_U8500) += ux500
machine-$(CONFIG_ARCH_VT8500) += vt8500
+machine-$(CONFIG_ARCH_ZTE) += zte
machine-$(CONFIG_ARCH_ZYNQ) += zynq
machine-$(CONFIG_PLAT_VERSATILE) += versatile
machine-$(CONFIG_PLAT_SPEAR) += spear
diff --git a/arch/arm/mach-zte/Kconfig b/arch/arm/mach-zte/Kconfig
new file mode 100644
index 000000000000..24699256863b
--- /dev/null
+++ b/arch/arm/mach-zte/Kconfig
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+menuconfig ARCH_ZTE
+ bool "ZTE zx family"
+ depends on ARCH_MULTI_V7
+ help
+ Support for ZTE zx-based family of processors.
+
+if ARCH_ZTE
+
+config SOC_ZX297520V3
+ default y if ARCH_ZTE
+ bool "ZX297520v3"
+ select ARM_GIC_V3
+ select ARM_AMBA
+ select HAVE_ARM_ARCH_TIMER
+ select PM_GENERIC_DOMAINS if PM
+ help
+ Support for ZTE zx297520v3 SoC. It a single core SoC used in cheap LTE to WiFi routers.
+ These devices can be Identified by the occurrence of the string "zx297520v3" in the boot
+ output and /proc/cpuinfo of their stock firmware.
+
+ Please read Documentation/arch/arm/zte/zx297520v3.rst on how to boot the kernel.
+
+endif
diff --git a/arch/arm/mach-zte/Makefile b/arch/arm/mach-zte/Makefile
new file mode 100644
index 000000000000..1bfe4fddd6af
--- /dev/null
+++ b/arch/arm/mach-zte/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SOC_ZX297520V3) += zx297520v3.o
diff --git a/arch/arm/mach-zte/zx297520v3.c b/arch/arm/mach-zte/zx297520v3.c
new file mode 100644
index 000000000000..c11c7e836f91
--- /dev/null
+++ b/arch/arm/mach-zte/zx297520v3.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2026 Stefan Dösinger
+ */
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+static const char *const zx297520v3_dt_compat[] __initconst = {
+ "zte,zx297520v3",
+ NULL,
+};
+
+DT_MACHINE_START(ZX, "ZTE zx297520v3 (Device Tree)")
+ .dt_compat = zx297520v3_dt_compat,
+MACHINE_END
--
2.52.0
^ permalink raw reply related
* [PATCH v4 2/8] dt-bindings: arm: Add zx297520v3 board binding
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
Add a compatible for boards based on the ZTE zx297520v3 SoC.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
The list of devices is the devices I have access to for testing. There
are many more devices based on this board and it is not always easy to
identify them. Often they are sold without any branding ("4G home
router") or with mobile carrier branding.
---
Documentation/devicetree/bindings/arm/zte.yaml | 25 +++++++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 26 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/zte.yaml b/Documentation/devicetree/bindings/arm/zte.yaml
new file mode 100644
index 000000000000..6eba09edd2c5
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/zte.yaml
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/zte.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ZTE zx29
+
+maintainers:
+ - Stefan Dösinger <stefandoesinger@gmail.com>
+
+properties:
+ $nodename:
+ const: "/"
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - dlink,dwr932m
+ - hgsd,r310
+ - tecno,tr118
+ - zte,k10
+ - const: zte,zx297520v3
+
+additionalProperties: true
diff --git a/MAINTAINERS b/MAINTAINERS
index 974d7a98956a..bcade90ca14e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29202,6 +29202,7 @@ F: tools/testing/selftests/cgroup/test_zswap.c
ZX29
M: Stefan Dösinger <stefandoesinger@gmail.com>
+F: Documentation/devicetree/bindings/arm/zte.yaml
F: arch/arm/mach-zte/
SENARYTECH AUDIO CODEC DRIVER
--
2.52.0
^ permalink raw reply related
* [PATCH v4 3/8] ARM: dts: Add D-Link DWR-932M support
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This adds DT bindings for zx297520v3 and one board that consumes it.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
MAINTAINERS | 1 +
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/zte/Makefile | 3 +++
arch/arm/boot/dts/zte/dlink-dwr-932m.dts | 21 ++++++++++++++++++
arch/arm/boot/dts/zte/zx297520v3.dtsi | 37 ++++++++++++++++++++++++++++++++
5 files changed, 63 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index bcade90ca14e..f7ca0d478e81 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29203,6 +29203,7 @@ F: tools/testing/selftests/cgroup/test_zswap.c
ZX29
M: Stefan Dösinger <stefandoesinger@gmail.com>
F: Documentation/devicetree/bindings/arm/zte.yaml
+F: arch/arm/boot/dts/zte
F: arch/arm/mach-zte/
SENARYTECH AUDIO CODEC DRIVER
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index efe38eb25301..28fba538d552 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -39,3 +39,4 @@ subdir-y += unisoc
subdir-y += vt8500
subdir-y += xen
subdir-y += xilinx
+subdir-y += zte
diff --git a/arch/arm/boot/dts/zte/Makefile b/arch/arm/boot/dts/zte/Makefile
new file mode 100644
index 000000000000..416c24a489cd
--- /dev/null
+++ b/arch/arm/boot/dts/zte/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_ZX297520V3) += \
+ dlink-dwr-932m.dtb
diff --git a/arch/arm/boot/dts/zte/dlink-dwr-932m.dts b/arch/arm/boot/dts/zte/dlink-dwr-932m.dts
new file mode 100644
index 000000000000..7b2a26aaaecb
--- /dev/null
+++ b/arch/arm/boot/dts/zte/dlink-dwr-932m.dts
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * D-Link DWR-932M Board
+ *
+ * (C) Copyright 2026 Stefan Dösinger
+ *
+ */
+
+/dts-v1/;
+
+#include "zx297520v3.dtsi"
+
+/ {
+ model = "D-Link DWR-932M";
+ compatible = "dlink,dwr932m", "zte,zx297520v3";
+
+ memory@20000000 {
+ device_type = "memory";
+ reg = <0x20000000 0x04000000>;
+ };
+};
diff --git a/arch/arm/boot/dts/zte/zx297520v3.dtsi b/arch/arm/boot/dts/zte/zx297520v3.dtsi
new file mode 100644
index 000000000000..d6c71d52b26c
--- /dev/null
+++ b/arch/arm/boot/dts/zte/zx297520v3.dtsi
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0>;
+ };
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ interrupt-parent = <&gic>;
+ ranges;
+
+ gic: interrupt-controller@f2000000 {
+ compatible = "arm,gic-v3";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf2000000 0x10000>,
+ <0xf2040000 0x20000>;
+ };
+ };
+};
--
2.52.0
^ permalink raw reply related
* [PATCH v4 4/8] ARM: zte: Add support for zx29 low level debug
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This is based on the removed zx29 code. A separate (more complicated)
patch will re-add the register map to the pl011 serial driver.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
I am unsure about the virtual address. It doesn't seem to matter, as
long as it is a valid address. This address is based on the old removed
code. Is there a rule-of-thumb physical to virtual mapping I can use to
give a sensible default value?
---
arch/arm/Kconfig.debug | 12 ++++++++++++
arch/arm/include/debug/pl01x.S | 7 +++++++
2 files changed, 19 insertions(+)
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 366f162e147d..98d8a5a60048 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1331,6 +1331,16 @@ choice
This option selects UART0 on VIA/Wondermedia System-on-a-chip
devices, including VT8500, WM8505, WM8650 and WM8850.
+ config DEBUG_ZTE_ZX
+ bool "Kernel low-level debugging via zx29 UART"
+ select DEBUG_UART_PL01X
+ depends on ARCH_ZTE
+ help
+ Say Y here if you are enabling ZTE zx297520v3 SOC and need
+ debug UART support. This UART is a PL011 with different
+ register addresses. The UART for boot messages on zx29 boards
+ is usually UART1 and is operating at 921600 8N1.
+
config DEBUG_ZYNQ_UART0
bool "Kernel low-level debugging on Xilinx Zynq using UART0"
depends on ARCH_ZYNQ
@@ -1545,6 +1555,7 @@ config DEBUG_UART_8250
config DEBUG_UART_PHYS
hex "Physical base address of debug UART"
+ default 0x01408000 if DEBUG_ZTE_ZX
default 0x01c28000 if DEBUG_SUNXI_UART0
default 0x01c28400 if DEBUG_SUNXI_UART1
default 0x01d0c000 if DEBUG_DAVINCI_DA8XX_UART1
@@ -1701,6 +1712,7 @@ config DEBUG_UART_VIRT
default 0xf31004c0 if DEBUG_MESON_UARTAO
default 0xf4090000 if DEBUG_LPC32XX
default 0xf4200000 if DEBUG_GEMINI
+ default 0xf4708000 if DEBUG_ZTE_ZX
default 0xf6200000 if DEBUG_PXA_UART1
default 0xf7000000 if DEBUG_SUN9I_UART0
default 0xf7000000 if DEBUG_S3C64XX_UART && DEBUG_S3C_UART0
diff --git a/arch/arm/include/debug/pl01x.S b/arch/arm/include/debug/pl01x.S
index c7e02d0628bf..0c7bfa4c10db 100644
--- a/arch/arm/include/debug/pl01x.S
+++ b/arch/arm/include/debug/pl01x.S
@@ -8,6 +8,13 @@
*/
#include <linux/amba/serial.h>
+#ifdef CONFIG_DEBUG_ZTE_ZX
+#undef UART01x_DR
+#undef UART01x_FR
+#define UART01x_DR 0x04
+#define UART01x_FR 0x14
+#endif
+
#ifdef CONFIG_DEBUG_UART_PHYS
.macro addruart, rp, rv, tmp
ldr \rp, =CONFIG_DEBUG_UART_PHYS
--
2.52.0
^ permalink raw reply related
* [PATCH v4 5/8] ARM: dts: Add an armv7 timer for zx297520v3
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
The stock kernel does not use this timer, but it seems to work fine. The
board has other board-specific timers that would need a driver and I see
no reason to bother with them since the arm standard timer works.
The caveat is the non-standard GIC setup needed to handle the timer's
level-low PPI. This is the responsibility of the boot loader and
documented in Documentation/arch/arm/zte/zx297520v3.rst.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
arch/arm/boot/dts/zte/zx297520v3.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/arch/arm/boot/dts/zte/zx297520v3.dtsi b/arch/arm/boot/dts/zte/zx297520v3.dtsi
index d6c71d52b26c..ecd07f3fb8b3 100644
--- a/arch/arm/boot/dts/zte/zx297520v3.dtsi
+++ b/arch/arm/boot/dts/zte/zx297520v3.dtsi
@@ -24,6 +24,15 @@ soc {
interrupt-parent = <&gic>;
ranges;
+ /* The GIC has a non-standard way of configuring ints between level-low/level
+ * high or rising edge/falling edge at 0xf2202070 and onwards. See AP_INT_MODE_BASE
+ * and AP_PPI_MODE_REG in the ZTE kernel, although the offsets in the kernel source
+ * seem wrong.
+ *
+ * Everything defaults to active-high/rising edge, but the timer is active-low. We
+ * currently rely on the boot loader to change timer IRQs to active-low for us for
+ * now.
+ */
gic: interrupt-controller@f2000000 {
compatible = "arm,gic-v3";
interrupt-controller;
@@ -33,5 +42,20 @@ gic: interrupt-controller@f2000000 {
reg = <0xf2000000 0x10000>,
<0xf2040000 0x20000>;
};
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ clock-frequency = <26000000>;
+ interrupt-parent = <&gic>;
+ /* I don't think uboot sets CNTVOFF and the stock kernel doesn't use the
+ * arm timer at all. Since this is a single CPU system I don't think it
+ * really matters that the offset is random though.
+ */
+ arm,cpu-registers-not-fw-configured;
+ };
};
};
--
2.52.0
^ permalink raw reply related
* [PATCH v4 6/8] ARM: zte: Bring back zx29 UART support
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This is based on code removed in commit 89d4f98ae90d ("ARM: remove zte
zx platform"). I did not bring back the zx29-uart .compatible as the
arm,primecell-periphid does the job.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
drivers/tty/serial/amba-pl011.c | 37 +++++++++++++++++++++++++++++++++++++
include/linux/amba/bus.h | 6 ++++++
2 files changed, 43 insertions(+)
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 7f17d288c807..858a0edd3e3b 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -216,6 +216,38 @@ static struct vendor_data vendor_st = {
.get_fifosize = get_fifosize_st,
};
+static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = {
+ [REG_DR] = ZX_UART011_DR,
+ [REG_FR] = ZX_UART011_FR,
+ [REG_LCRH_RX] = ZX_UART011_LCRH,
+ [REG_LCRH_TX] = ZX_UART011_LCRH,
+ [REG_IBRD] = ZX_UART011_IBRD,
+ [REG_FBRD] = ZX_UART011_FBRD,
+ [REG_CR] = ZX_UART011_CR,
+ [REG_IFLS] = ZX_UART011_IFLS,
+ [REG_IMSC] = ZX_UART011_IMSC,
+ [REG_RIS] = ZX_UART011_RIS,
+ [REG_MIS] = ZX_UART011_MIS,
+ [REG_ICR] = ZX_UART011_ICR,
+ [REG_DMACR] = ZX_UART011_DMACR,
+};
+
+static unsigned int get_fifosize_zte(struct amba_device *dev)
+{
+ return 16;
+}
+
+static struct vendor_data vendor_zte = {
+ .reg_offset = pl011_zte_offsets,
+ .access_32b = true,
+ .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8,
+ .fr_busy = ZX_UART01x_FR_BUSY,
+ .fr_dsr = ZX_UART01x_FR_DSR,
+ .fr_cts = ZX_UART01x_FR_CTS,
+ .fr_ri = ZX_UART011_FR_RI,
+ .get_fifosize = get_fifosize_zte,
+};
+
/* Deals with DMA transactions */
struct pl011_dmabuf {
@@ -3081,6 +3113,11 @@ static const struct amba_id pl011_ids[] = {
.mask = 0x00ffffff,
.data = &vendor_st,
},
+ {
+ .id = AMBA_LINUX_ID(0x00, 0x1, 0xffe),
+ .mask = 0x00ffffff,
+ .data = &vendor_zte,
+ },
{ 0, 0 },
};
diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index 9946276aff73..854c962d70f5 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -103,8 +103,14 @@ enum amba_vendor {
AMBA_VENDOR_ST = 0x80,
AMBA_VENDOR_QCOM = 0x51,
AMBA_VENDOR_LSI = 0xb6,
+ AMBA_VENDOR_LINUX = 0xfe, /* This value is not official */
};
+/* This is used to generate pseudo-ID for AMBA device */
+#define AMBA_LINUX_ID(conf, rev, part) \
+ (((conf) & 0xff) << 24 | ((rev) & 0xf) << 20 | \
+ AMBA_VENDOR_LINUX << 12 | ((part) & 0xfff))
+
extern const struct bus_type amba_bustype;
#define to_amba_device(d) container_of_const(d, struct amba_device, dev)
--
2.52.0
^ permalink raw reply related
* [PATCH v4 7/8] ARM: dts: Declare UART1 on zx297520v3 boards
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This is the UART that sends Uboot messages and is accessible via pins on
the boards I have seen so far. UART0 and UART2 exist as well in the SoC
and can be used with the right pinmux settings on some boards. They will
be added later.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
The reason why I add the serial1=uart1 alias is to keep console=ttyAMA1
stable regardless of the other enabled UARTs. UART0, as the name
implies, has a lower MMIO address, but uart1 is the one that usually has
the boot output and console.
---
arch/arm/boot/dts/zte/zx297520v3.dtsi | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm/boot/dts/zte/zx297520v3.dtsi b/arch/arm/boot/dts/zte/zx297520v3.dtsi
index ecd07f3fb8b3..09fbb1d052e3 100644
--- a/arch/arm/boot/dts/zte/zx297520v3.dtsi
+++ b/arch/arm/boot/dts/zte/zx297520v3.dtsi
@@ -6,6 +6,10 @@ / {
#address-cells = <1>;
#size-cells = <1>;
+ aliases {
+ serial1 = &uart1;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -57,5 +61,23 @@ timer {
*/
arm,cpu-registers-not-fw-configured;
};
+
+ /* The UART clock defaults to 26 mhz. It will be replaced when the zx29 clock
+ * framework is added.
+ */
+ uartclk: uartclk: clock-26000000 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <26000000>;
+ };
+
+ uart1: serial@1408000 {
+ compatible = "arm,pl011", "arm,primecell";
+ arm,primecell-periphid = <0x001feffe>;
+ reg = <0x01408000 0x1000>;
+ interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&uartclk>;
+ clock-names = "apb_pclk";
+ };
};
};
--
2.52.0
^ permalink raw reply related
* [PATCH v4 8/8] ARM: defconfig: Add a zx29 defconfig file
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This enables existing drivers that already are (UART) or will be (USB,
GPIO) necessary to operate this board even if they aren't declared in
the DTS yet.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
arch/arm/configs/zx29_defconfig | 90 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/arch/arm/configs/zx29_defconfig b/arch/arm/configs/zx29_defconfig
new file mode 100644
index 000000000000..dae2d86c7583
--- /dev/null
+++ b/arch/arm/configs/zx29_defconfig
@@ -0,0 +1,90 @@
+CONFIG_SYSVIPC=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_EXPERT=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_MMU=y
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_ZTE=y
+CONFIG_SOC_ZX297520V3=y
+# FIXME: There is no PSCI on this board, but ARM_GIC_V3 depends on it
+CONFIG_ARM_PSCI=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_CMDLINE="console=ttyAMA1 earlyprintk root=/dev/ram rw"
+# CONFIG_SUSPEND is not set
+CONFIG_BINFMT_FLAT=y
+# CONFIG_UEVENT_HELPER is not set
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_ALLOW_DEV_COREDUMP is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=4
+CONFIG_CPU_FREQ=y
+CONFIG_CPUFREQ_DT_PLATDEV=y
+CONFIG_PM=y
+CONFIG_PM_CLK=y
+CONFIG_PM_GENERIC_DOMAINS=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_DEVTMPFS=y # FIXME: This is specific to my initrd. Remove before upstream
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_KEYBOARD_GPIO_POLLED=y
+CONFIG_GPIOLIB=y
+CONFIG_OF_GPIO=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_DEV_BUS=y
+CONFIG_SERIAL_DEV_CTRL_TTYPORT=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_MFD_SYSCON=y
+# CONFIG_HID is not set
+CONFIG_PINCTRL=y
+CONFIG_GENERIC_PINCTRL_GROUPS=y
+CONFIG_PINMUX=y
+CONFIG_GENERIC_PINMUX_FUNCTIONS=y
+CONFIG_PINCONF=y
+CONFIG_GENERIC_PINCONF=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_POWER_RESET=y
+CONFIG_RESET_SIMPLE=y
+CONFIG_LEDS_GPIO=y
+CONFIG_USB_DWC2=y
+CONFIG_USB_GADGET=y
+CONFIG_MTD=y
+CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_SPI_MASTER=y
+CONFIG_MMC=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_PLTFM=y
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_PLATFORM=y
+CONFIG_MDIO_BUS=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_SRAM=y
+CONFIG_MISC_FILESYSTEMS=y
+CONFIG_JFFS2_FS=y
+CONFIG_CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_PRINTK_TIME=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_ZTE_ZX=y
+CONFIG_DEBUG_LL_INCLUDE="debug/pl01x.S"
+CONFIG_DEBUG_UART_PL01X=y
+CONFIG_DEBUG_UART_PHYS=0x01408000
+CONFIG_DEBUG_UART_VIRT=0xf4708000
--
2.52.0
^ permalink raw reply related
* Re: [PATCH V13 02/12] PCI: host-generic: Add common helpers for parsing Root Port properties
From: Bjorn Helgaas @ 2026-04-16 20:39 UTC (permalink / raw)
To: Sherry Sun
Cc: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach,
imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260416111422.183860-3-sherry.sun@nxp.com>
On Thu, Apr 16, 2026 at 07:14:12PM +0800, Sherry Sun wrote:
> Introduce generic helper functions to parse Root Port device tree nodes
> and extract common properties like reset GPIOs. This allows multiple
> PCI host controller drivers to share the same parsing logic.
>
> Define struct pci_host_port to hold common Root Port properties
> (currently only reset GPIO descriptor) and add
> pci_host_common_parse_ports() to parse Root Port nodes from device tree.
Are the Root Port and the RC the only possible places for 'reset' GPIO
descriptions in DT? I think PERST# routing is outside the PCIe spec,
so it seems like a system could provide a PERST# GPIO routed to any
Switch Upstream Port or Endpoint (I assume a PERST# connected to a
switch would apply to both the upstream port and the downstream
ports).
^ permalink raw reply
* [PATCH 0/3] counter: add GPIO-based quadrature encoder driver
From: Wadim Mueller @ 2026-04-16 20:48 UTC (permalink / raw)
To: wbg
Cc: robh, krzk+dt, conor+dt, linux-iio, devicetree, linux-kernel,
Wadim Mueller
This series adds a new counter subsystem driver that implements
quadrature encoder position tracking using plain GPIO pins with
edge-triggered interrupts.
The driver is intended for low to medium speed rotary encoders where
hardware counter peripherals (eQEP, FTM, etc.) are unavailable or
already in use. It targets the same use-cases as interrupt-cnt.c but
provides full quadrature decoding instead of simple pulse counting.
Features:
- X1, X2, X4 quadrature decoding and pulse-direction mode
- Optional index signal for zero-reset
- Configurable ceiling (position clamping)
- Standard counter subsystem sysfs + chrdev interface
- Enable/disable via sysfs with IRQ gating
Tested on TI AM64x (Cortex-A53) with a motor-driven rotary encoder
at up to 2 kHz quadrature edge rate.
Wadim Mueller (3):
dt-bindings: counter: add gpio-quadrature-encoder binding
counter: add GPIO-based quadrature encoder driver
MAINTAINERS: add entry for GPIO quadrature encoder counter driver
.../counter/gpio-quadrature-encoder.yaml | 69 ++
MAINTAINERS | 7 +
drivers/counter/Kconfig | 15 +
drivers/counter/Makefile | 1 +
drivers/counter/gpio-quadrature-encoder.c | 710 ++++++++++++++++++
5 files changed, 802 insertions(+)
create mode 100644 Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
create mode 100644 drivers/counter/gpio-quadrature-encoder.c
--
2.52.0
^ permalink raw reply
* [PATCH 1/3] dt-bindings: counter: add gpio-quadrature-encoder binding
From: Wadim Mueller @ 2026-04-16 20:48 UTC (permalink / raw)
To: wbg
Cc: robh, krzk+dt, conor+dt, linux-iio, devicetree, linux-kernel,
Wadim Mueller
In-Reply-To: <cover.1776372319.git.wafgo01@gmail.com>
Add devicetree binding documentation for the GPIO-based quadrature
encoder counter driver. The driver reads A/B quadrature signals and
an optional index pulse via edge-triggered GPIO interrupts, supporting
X1, X2, X4 quadrature decoding and pulse-direction mode.
This is useful on SoCs that lack a dedicated hardware quadrature
decoder or where the encoder is wired to generic GPIO pins.
Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
---
.../counter/gpio-quadrature-encoder.yaml | 69 +++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
diff --git a/Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml b/Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
new file mode 100644
index 000000000..a52deaab6
--- /dev/null
+++ b/Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/counter/gpio-quadrature-encoder.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO-based Quadrature Encoder
+
+maintainers:
+ - Wadim Mueller <wadim.mueller@cmblu.de>
+
+description: |
+ A generic GPIO-based quadrature encoder counter. Reads A/B quadrature
+ signals and an optional index pulse via edge-triggered GPIO interrupts.
+ Supports X1, X2, X4 quadrature decoding and pulse-direction mode.
+
+ This driver is useful on SoCs that lack a dedicated hardware quadrature
+ decoder (eQEP, QEI, etc.) or where the encoder is wired to generic GPIO
+ pins rather than to a dedicated peripheral.
+
+properties:
+ compatible:
+ const: gpio-quadrature-encoder
+
+ encoder-a-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the encoder's A (phase A) output.
+
+ encoder-b-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the encoder's B (phase B) output.
+
+ encoder-index-gpios:
+ maxItems: 1
+ description:
+ Optional GPIO connected to the encoder's index (Z) output.
+ When the index input is enabled via sysfs, the count resets
+ to zero on each index pulse.
+
+required:
+ - compatible
+ - encoder-a-gpios
+ - encoder-b-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ quadrature-encoder-0 {
+ compatible = "gpio-quadrature-encoder";
+ encoder-a-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
+ encoder-b-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ quadrature-encoder-1 {
+ compatible = "gpio-quadrature-encoder";
+ encoder-a-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
+ encoder-b-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
+ encoder-index-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
+ };
+
+...
--
2.52.0
^ permalink raw reply related
* [PATCH 2/3] counter: add GPIO-based quadrature encoder driver
From: Wadim Mueller @ 2026-04-16 20:48 UTC (permalink / raw)
To: wbg
Cc: robh, krzk+dt, conor+dt, linux-iio, devicetree, linux-kernel,
Wadim Mueller
In-Reply-To: <cover.1776372319.git.wafgo01@gmail.com>
Add a platform driver that turns ordinary GPIOs into a quadrature
encoder counter device. The driver requests edge-triggered interrupts
on the A and B (and optional Index) GPIOs and decodes the quadrature
signal in software using a classic state-table approach.
Supported counting modes:
- Quadrature X1 (count on A rising edge only)
- Quadrature X2 (count on both A edges)
- Quadrature X4 (count on every A and B edge)
- Pulse-direction (A = pulse, B = direction)
An optional index signal resets the count to zero on its rising edge
when enabled through sysfs. A configurable ceiling clamps the count
to [0, ceiling].
Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
---
drivers/counter/Kconfig | 15 +
drivers/counter/Makefile | 1 +
drivers/counter/gpio-quadrature-encoder.c | 710 ++++++++++++++++++++++
3 files changed, 726 insertions(+)
create mode 100644 drivers/counter/gpio-quadrature-encoder.c
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index d30d22dfe..72c5c8159 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -68,6 +68,21 @@ config INTEL_QEP
To compile this driver as a module, choose M here: the module
will be called intel-qep.
+config GPIO_QUADRATURE_ENCODER
+ tristate "GPIO-based quadrature encoder counter driver"
+ depends on GPIOLIB
+ help
+ Select this option to enable the GPIO-based quadrature encoder
+ counter driver. It reads A/B quadrature signals and an optional
+ index pulse via edge-triggered GPIO interrupts, supporting X1, X2,
+ X4 quadrature decoding and pulse-direction mode.
+
+ This is useful on SoCs that lack a dedicated hardware quadrature
+ decoder or where the encoder is wired to generic GPIO pins.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gpio-quadrature-encoder.
+
config INTERRUPT_CNT
tristate "Interrupt counter driver"
depends on GPIOLIB
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index fa3c1d08f..2bef64d10 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o
obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
obj-$(CONFIG_TI_EQEP) += ti-eqep.o
obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o
+obj-$(CONFIG_GPIO_QUADRATURE_ENCODER) += gpio-quadrature-encoder.o
obj-$(CONFIG_MICROCHIP_TCB_CAPTURE) += microchip-tcb-capture.o
obj-$(CONFIG_INTEL_QEP) += intel-qep.o
obj-$(CONFIG_TI_ECAP_CAPTURE) += ti-ecap-capture.o
diff --git a/drivers/counter/gpio-quadrature-encoder.c b/drivers/counter/gpio-quadrature-encoder.c
new file mode 100644
index 000000000..0822f0a8a
--- /dev/null
+++ b/drivers/counter/gpio-quadrature-encoder.c
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GPIO-based Quadrature Encoder Counter Driver
+ *
+ * Reads quadrature encoder signals (A, B, and optional Index) via GPIOs.
+ * Supports X1, X2, X4 quadrature decoding and pulse-direction mode.
+ *
+ * Copyright (C) 2026 CMBlu Energy AG
+ * Author: Wadim Mueller <wafgo01@gmail.com>
+ */
+
+#include <linux/counter.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+enum gpio_qenc_function {
+ GPIO_QENC_FUNC_QUAD_X1 = 0,
+ GPIO_QENC_FUNC_QUAD_X2,
+ GPIO_QENC_FUNC_QUAD_X4,
+ GPIO_QENC_FUNC_PULSE_DIR,
+};
+
+enum gpio_qenc_signal_id {
+ GPIO_QENC_SIGNAL_A = 0,
+ GPIO_QENC_SIGNAL_B,
+ GPIO_QENC_SIGNAL_INDEX,
+};
+
+struct gpio_qenc_priv {
+ struct gpio_desc *gpio_a;
+ struct gpio_desc *gpio_b;
+ struct gpio_desc *gpio_index;
+
+ int irq_a;
+ int irq_b;
+ int irq_index;
+
+ spinlock_t lock;
+
+ s64 count;
+ u64 ceiling;
+ bool enabled;
+ enum counter_count_direction direction;
+ enum gpio_qenc_function function;
+
+ int prev_a;
+ int prev_b;
+
+ bool index_enabled;
+
+ struct counter_signal signals[3];
+ struct counter_synapse synapses[3];
+ struct counter_count cnts;
+};
+
+/*
+ * Quadrature state table for X4 decoding.
+ * Rows = previous state (A<<1 | B), Columns = new state (A<<1 | B).
+ * Values: 0 = no change, +1 = forward, -1 = backward, 2 = error (skip).
+ */
+static const int quad_table[4][4] = {
+ /* 00 01 10 11 <- new */
+ /* 00 */ { 0, -1, 1, 2 },
+ /* 01 */ { 1, 0, 2, -1 },
+ /* 10 */ { -1, 2, 0, 1 },
+ /* 11 */ { 2, 1, -1, 0 },
+};
+
+static void gpio_qenc_update_count(struct gpio_qenc_priv *priv, int delta)
+{
+ s64 new_count;
+
+ if (!delta)
+ return;
+
+ new_count = priv->count + delta;
+
+ if (priv->ceiling) {
+ if (new_count < 0)
+ new_count = 0;
+ else if (new_count > (s64)priv->ceiling)
+ new_count = priv->ceiling;
+ }
+
+ priv->count = new_count;
+ priv->direction = (delta > 0) ? COUNTER_COUNT_DIRECTION_FORWARD
+ : COUNTER_COUNT_DIRECTION_BACKWARD;
+}
+
+static irqreturn_t gpio_qenc_a_isr(int irq, void *dev_id)
+{
+ struct counter_device *counter = dev_id;
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+ int a, b, prev_state, new_state, delta;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (!priv->enabled)
+ goto out;
+
+ a = gpiod_get_value(priv->gpio_a);
+ b = gpiod_get_value(priv->gpio_b);
+
+ prev_state = (priv->prev_a << 1) | priv->prev_b;
+ new_state = (a << 1) | b;
+
+ switch (priv->function) {
+ case GPIO_QENC_FUNC_QUAD_X4:
+ delta = quad_table[prev_state][new_state];
+ if (delta == 2)
+ delta = 0;
+ gpio_qenc_update_count(priv, delta);
+ break;
+
+ case GPIO_QENC_FUNC_QUAD_X2:
+ delta = quad_table[prev_state][new_state];
+ if (delta == 2)
+ delta = 0;
+ gpio_qenc_update_count(priv, delta);
+ break;
+
+ case GPIO_QENC_FUNC_QUAD_X1:
+ if (!priv->prev_a && a) {
+ delta = b ? -1 : 1;
+ gpio_qenc_update_count(priv, delta);
+ }
+ break;
+
+ case GPIO_QENC_FUNC_PULSE_DIR:
+ if (!priv->prev_a && a) {
+ delta = b ? -1 : 1;
+ gpio_qenc_update_count(priv, delta);
+ }
+ break;
+ }
+
+ priv->prev_a = a;
+ priv->prev_b = b;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE, 0);
+
+ return IRQ_HANDLED;
+
+out:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t gpio_qenc_b_isr(int irq, void *dev_id)
+{
+ struct counter_device *counter = dev_id;
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+ int a, b, prev_state, new_state, delta;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (!priv->enabled)
+ goto out;
+
+ a = gpiod_get_value(priv->gpio_a);
+ b = gpiod_get_value(priv->gpio_b);
+
+ prev_state = (priv->prev_a << 1) | priv->prev_b;
+ new_state = (a << 1) | b;
+
+ switch (priv->function) {
+ case GPIO_QENC_FUNC_QUAD_X4:
+ delta = quad_table[prev_state][new_state];
+ if (delta == 2)
+ delta = 0;
+ gpio_qenc_update_count(priv, delta);
+ break;
+
+ case GPIO_QENC_FUNC_QUAD_X2:
+ /* X2: only A-channel edges update count */
+ break;
+
+ case GPIO_QENC_FUNC_QUAD_X1:
+ case GPIO_QENC_FUNC_PULSE_DIR:
+ break;
+ }
+
+ priv->prev_a = a;
+ priv->prev_b = b;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return IRQ_HANDLED;
+
+out:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t gpio_qenc_index_isr(int irq, void *dev_id)
+{
+ struct counter_device *counter = dev_id;
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->enabled && priv->index_enabled)
+ priv->count = 0;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ counter_push_event(counter, COUNTER_EVENT_INDEX, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int gpio_qenc_count_read(struct counter_device *counter,
+ struct counter_count *count, u64 *val)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ *val = (u64)priv->count;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int gpio_qenc_count_write(struct counter_device *counter,
+ struct counter_count *count, const u64 val)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->ceiling && val > priv->ceiling) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -EINVAL;
+ }
+
+ priv->count = (s64)val;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static const enum counter_function gpio_qenc_functions[] = {
+ COUNTER_FUNCTION_QUADRATURE_X1_A,
+ COUNTER_FUNCTION_QUADRATURE_X2_A,
+ COUNTER_FUNCTION_QUADRATURE_X4,
+ COUNTER_FUNCTION_PULSE_DIRECTION,
+};
+
+static int gpio_qenc_function_read(struct counter_device *counter,
+ struct counter_count *count,
+ enum counter_function *function)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ switch (priv->function) {
+ case GPIO_QENC_FUNC_QUAD_X1:
+ *function = COUNTER_FUNCTION_QUADRATURE_X1_A;
+ break;
+ case GPIO_QENC_FUNC_QUAD_X2:
+ *function = COUNTER_FUNCTION_QUADRATURE_X2_A;
+ break;
+ case GPIO_QENC_FUNC_QUAD_X4:
+ *function = COUNTER_FUNCTION_QUADRATURE_X4;
+ break;
+ case GPIO_QENC_FUNC_PULSE_DIR:
+ *function = COUNTER_FUNCTION_PULSE_DIRECTION;
+ break;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 0;
+}
+
+static int gpio_qenc_function_write(struct counter_device *counter,
+ struct counter_count *count,
+ enum counter_function function)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ switch (function) {
+ case COUNTER_FUNCTION_QUADRATURE_X1_A:
+ priv->function = GPIO_QENC_FUNC_QUAD_X1;
+ break;
+ case COUNTER_FUNCTION_QUADRATURE_X2_A:
+ priv->function = GPIO_QENC_FUNC_QUAD_X2;
+ break;
+ case COUNTER_FUNCTION_QUADRATURE_X4:
+ priv->function = GPIO_QENC_FUNC_QUAD_X4;
+ break;
+ case COUNTER_FUNCTION_PULSE_DIRECTION:
+ priv->function = GPIO_QENC_FUNC_PULSE_DIR;
+ break;
+ default:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 0;
+}
+
+static const enum counter_synapse_action gpio_qenc_synapse_actions[] = {
+ COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
+ COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+ COUNTER_SYNAPSE_ACTION_NONE,
+};
+
+static int gpio_qenc_action_read(struct counter_device *counter,
+ struct counter_count *count,
+ struct counter_synapse *synapse,
+ enum counter_synapse_action *action)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ enum gpio_qenc_signal_id signal_id = synapse->signal->id;
+
+ switch (priv->function) {
+ case GPIO_QENC_FUNC_QUAD_X4:
+ if (signal_id == GPIO_QENC_SIGNAL_A ||
+ signal_id == GPIO_QENC_SIGNAL_B)
+ *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
+ else
+ *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
+ return 0;
+
+ case GPIO_QENC_FUNC_QUAD_X2:
+ if (signal_id == GPIO_QENC_SIGNAL_A)
+ *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
+ else if (signal_id == GPIO_QENC_SIGNAL_B)
+ *action = COUNTER_SYNAPSE_ACTION_NONE;
+ else
+ *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
+ return 0;
+
+ case GPIO_QENC_FUNC_QUAD_X1:
+ if (signal_id == GPIO_QENC_SIGNAL_A)
+ *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
+ else if (signal_id == GPIO_QENC_SIGNAL_B)
+ *action = COUNTER_SYNAPSE_ACTION_NONE;
+ else
+ *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
+ return 0;
+
+ case GPIO_QENC_FUNC_PULSE_DIR:
+ if (signal_id == GPIO_QENC_SIGNAL_A)
+ *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
+ else
+ *action = COUNTER_SYNAPSE_ACTION_NONE;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int gpio_qenc_signal_read(struct counter_device *counter,
+ struct counter_signal *signal,
+ enum counter_signal_level *level)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ struct gpio_desc *gpio;
+ int ret;
+
+ switch (signal->id) {
+ case GPIO_QENC_SIGNAL_A:
+ gpio = priv->gpio_a;
+ break;
+ case GPIO_QENC_SIGNAL_B:
+ gpio = priv->gpio_b;
+ break;
+ case GPIO_QENC_SIGNAL_INDEX:
+ gpio = priv->gpio_index;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!gpio)
+ return -EINVAL;
+
+ ret = gpiod_get_value(gpio);
+ if (ret < 0)
+ return ret;
+
+ *level = ret ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
+ return 0;
+}
+
+static int gpio_qenc_events_configure(struct counter_device *counter)
+{
+ return 0;
+}
+
+static int gpio_qenc_watch_validate(struct counter_device *counter,
+ const struct counter_watch *watch)
+{
+ if (watch->channel != 0)
+ return -EINVAL;
+
+ switch (watch->event) {
+ case COUNTER_EVENT_CHANGE_OF_STATE:
+ case COUNTER_EVENT_INDEX:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct counter_ops gpio_qenc_ops = {
+ .count_read = gpio_qenc_count_read,
+ .count_write = gpio_qenc_count_write,
+ .function_read = gpio_qenc_function_read,
+ .function_write = gpio_qenc_function_write,
+ .action_read = gpio_qenc_action_read,
+ .signal_read = gpio_qenc_signal_read,
+ .events_configure = gpio_qenc_events_configure,
+ .watch_validate = gpio_qenc_watch_validate,
+};
+
+static int gpio_qenc_ceiling_read(struct counter_device *counter,
+ struct counter_count *count, u64 *val)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ *val = priv->ceiling;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int gpio_qenc_ceiling_write(struct counter_device *counter,
+ struct counter_count *count, const u64 val)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->ceiling = val;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int gpio_qenc_enable_read(struct counter_device *counter,
+ struct counter_count *count, u8 *enable)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+
+ *enable = priv->enabled;
+ return 0;
+}
+
+static int gpio_qenc_enable_write(struct counter_device *counter,
+ struct counter_count *count, u8 enable)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->enabled == !!enable) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 0;
+ }
+
+ if (enable) {
+ priv->enabled = true;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ enable_irq(priv->irq_a);
+ enable_irq(priv->irq_b);
+ if (priv->irq_index)
+ enable_irq(priv->irq_index);
+ } else {
+ priv->enabled = false;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ disable_irq(priv->irq_a);
+ disable_irq(priv->irq_b);
+ if (priv->irq_index)
+ disable_irq(priv->irq_index);
+ }
+
+ return 0;
+}
+
+static int gpio_qenc_direction_read(struct counter_device *counter,
+ struct counter_count *count, u32 *direction)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ *direction = priv->direction;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int gpio_qenc_index_enable_read(struct counter_device *counter,
+ struct counter_count *count, u8 *val)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+
+ *val = priv->index_enabled;
+ return 0;
+}
+
+static int gpio_qenc_index_enable_write(struct counter_device *counter,
+ struct counter_count *count, u8 val)
+{
+ struct gpio_qenc_priv *priv = counter_priv(counter);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->index_enabled = !!val;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static struct counter_comp gpio_qenc_count_ext[] = {
+ COUNTER_COMP_CEILING(gpio_qenc_ceiling_read, gpio_qenc_ceiling_write),
+ COUNTER_COMP_ENABLE(gpio_qenc_enable_read, gpio_qenc_enable_write),
+ COUNTER_COMP_DIRECTION(gpio_qenc_direction_read),
+ COUNTER_COMP_COUNT_BOOL("index_enabled",
+ gpio_qenc_index_enable_read,
+ gpio_qenc_index_enable_write),
+};
+
+static int gpio_qenc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct counter_device *counter;
+ struct gpio_qenc_priv *priv;
+ bool has_index;
+ int num_signals;
+ int num_synapses;
+ int ret;
+
+ counter = devm_counter_alloc(dev, sizeof(*priv));
+ if (!counter)
+ return -ENOMEM;
+
+ priv = counter_priv(counter);
+ spin_lock_init(&priv->lock);
+
+ priv->gpio_a = devm_gpiod_get(dev, "encoder-a", GPIOD_IN);
+ if (IS_ERR(priv->gpio_a))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_a),
+ "failed to get encoder-a GPIO\n");
+
+ priv->gpio_b = devm_gpiod_get(dev, "encoder-b", GPIOD_IN);
+ if (IS_ERR(priv->gpio_b))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_b),
+ "failed to get encoder-b GPIO\n");
+
+ priv->gpio_index = devm_gpiod_get_optional(dev, "encoder-index",
+ GPIOD_IN);
+ if (IS_ERR(priv->gpio_index))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_index),
+ "failed to get encoder-index GPIO\n");
+
+ has_index = !!priv->gpio_index;
+
+ priv->irq_a = gpiod_to_irq(priv->gpio_a);
+ if (priv->irq_a < 0)
+ return dev_err_probe(dev, priv->irq_a,
+ "failed to get IRQ for encoder-a\n");
+
+ priv->irq_b = gpiod_to_irq(priv->gpio_b);
+ if (priv->irq_b < 0)
+ return dev_err_probe(dev, priv->irq_b,
+ "failed to get IRQ for encoder-b\n");
+
+ if (has_index) {
+ priv->irq_index = gpiod_to_irq(priv->gpio_index);
+ if (priv->irq_index < 0)
+ return dev_err_probe(dev, priv->irq_index,
+ "failed to get IRQ for encoder-index\n");
+ }
+
+ priv->prev_a = gpiod_get_value(priv->gpio_a);
+ priv->prev_b = gpiod_get_value(priv->gpio_b);
+
+ priv->function = GPIO_QENC_FUNC_QUAD_X4;
+ priv->direction = COUNTER_COUNT_DIRECTION_FORWARD;
+
+ num_signals = has_index ? 3 : 2;
+
+ priv->signals[GPIO_QENC_SIGNAL_A].id = GPIO_QENC_SIGNAL_A;
+ priv->signals[GPIO_QENC_SIGNAL_A].name = "Signal A";
+
+ priv->signals[GPIO_QENC_SIGNAL_B].id = GPIO_QENC_SIGNAL_B;
+ priv->signals[GPIO_QENC_SIGNAL_B].name = "Signal B";
+
+ if (has_index) {
+ priv->signals[GPIO_QENC_SIGNAL_INDEX].id =
+ GPIO_QENC_SIGNAL_INDEX;
+ priv->signals[GPIO_QENC_SIGNAL_INDEX].name = "Index";
+ }
+
+ num_synapses = num_signals;
+
+ priv->synapses[0].actions_list = gpio_qenc_synapse_actions;
+ priv->synapses[0].num_actions = ARRAY_SIZE(gpio_qenc_synapse_actions);
+ priv->synapses[0].signal = &priv->signals[GPIO_QENC_SIGNAL_A];
+
+ priv->synapses[1].actions_list = gpio_qenc_synapse_actions;
+ priv->synapses[1].num_actions = ARRAY_SIZE(gpio_qenc_synapse_actions);
+ priv->synapses[1].signal = &priv->signals[GPIO_QENC_SIGNAL_B];
+
+ if (has_index) {
+ priv->synapses[2].actions_list = gpio_qenc_synapse_actions;
+ priv->synapses[2].num_actions =
+ ARRAY_SIZE(gpio_qenc_synapse_actions);
+ priv->synapses[2].signal =
+ &priv->signals[GPIO_QENC_SIGNAL_INDEX];
+ }
+
+ priv->cnts.id = 0;
+ priv->cnts.name = "Position";
+ priv->cnts.functions_list = gpio_qenc_functions;
+ priv->cnts.num_functions = ARRAY_SIZE(gpio_qenc_functions);
+ priv->cnts.synapses = priv->synapses;
+ priv->cnts.num_synapses = num_synapses;
+ priv->cnts.ext = gpio_qenc_count_ext;
+ priv->cnts.num_ext = ARRAY_SIZE(gpio_qenc_count_ext);
+
+ counter->name = dev_name(dev);
+ counter->parent = dev;
+ counter->ops = &gpio_qenc_ops;
+ counter->signals = priv->signals;
+ counter->num_signals = num_signals;
+ counter->counts = &priv->cnts;
+ counter->num_counts = 1;
+
+ irq_set_status_flags(priv->irq_a, IRQ_NOAUTOEN);
+ ret = devm_request_irq(dev, priv->irq_a, gpio_qenc_a_isr,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "gpio-qenc-a", counter);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to request IRQ for encoder-a\n");
+
+ irq_set_status_flags(priv->irq_b, IRQ_NOAUTOEN);
+ ret = devm_request_irq(dev, priv->irq_b, gpio_qenc_b_isr,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "gpio-qenc-b", counter);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to request IRQ for encoder-b\n");
+
+ if (has_index) {
+ irq_set_status_flags(priv->irq_index, IRQ_NOAUTOEN);
+ ret = devm_request_irq(dev, priv->irq_index,
+ gpio_qenc_index_isr,
+ IRQF_TRIGGER_RISING,
+ "gpio-qenc-index", counter);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to request IRQ for encoder-index\n");
+ }
+
+ ret = devm_counter_add(dev, counter);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to add counter\n");
+
+ dev_info(dev, "GPIO quadrature encoder registered (signals: A, B%s)\n",
+ has_index ? ", Index" : "");
+
+ return 0;
+}
+
+static const struct of_device_id gpio_qenc_of_match[] = {
+ { .compatible = "gpio-quadrature-encoder" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gpio_qenc_of_match);
+
+static struct platform_driver gpio_qenc_driver = {
+ .probe = gpio_qenc_probe,
+ .driver = {
+ .name = "gpio-quadrature-encoder",
+ .of_match_table = gpio_qenc_of_match,
+ },
+};
+module_platform_driver(gpio_qenc_driver);
+
+MODULE_ALIAS("platform:gpio-quadrature-encoder");
+MODULE_AUTHOR("Wadim Mueller <wafgo01@gmail.com>");
+MODULE_DESCRIPTION("GPIO-based quadrature encoder counter driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("COUNTER");
--
2.52.0
^ permalink raw reply related
* [PATCH 3/3] MAINTAINERS: add entry for GPIO quadrature encoder counter driver
From: Wadim Mueller @ 2026-04-16 20:48 UTC (permalink / raw)
To: wbg
Cc: robh, krzk+dt, conor+dt, linux-iio, devicetree, linux-kernel,
Wadim Mueller
In-Reply-To: <cover.1776372319.git.wafgo01@gmail.com>
Add myself as maintainer for the new gpio-quadrature-encoder counter
driver and its devicetree binding.
Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 06a8c7457..fca62baa7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11018,6 +11018,13 @@ F: Documentation/dev-tools/gpio-sloppy-logic-analyzer.rst
F: drivers/gpio/gpio-sloppy-logic-analyzer.c
F: tools/gpio/gpio-sloppy-logic-analyzer.sh
+GPIO QUADRATURE ENCODER COUNTER DRIVER
+M: Wadim Mueller <wafgo01@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
+F: drivers/counter/gpio-quadrature-encoder.c
+
GPIO SUBSYSTEM
M: Linus Walleij <linusw@kernel.org>
M: Bartosz Golaszewski <brgl@kernel.org>
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v2 3/3] arm64: tegra: Add GTE nodes for Tegra264
From: Dipen Patel @ 2026-04-16 21:06 UTC (permalink / raw)
To: Suneel Garapati, jonathanh, thierry.reding, krzk+dt, conor+dt,
amhetre, sheetal, kkartik, robh, pshete, timestamp, devicetree,
linux-tegra, linux-kernel
In-Reply-To: <20260408212413.217692-4-suneelg@nvidia.com>
On 4/8/26 2:24 PM, Suneel Garapati wrote:
> Add AON GPIO and system LIC GTE instances for Tegra264.
>
> Signed-off-by: Suneel Garapati <suneelg@nvidia.com>
> ---
> arch/arm64/boot/dts/nvidia/tegra264.dtsi | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> index 06d8357bdf52..c6630733d5e3 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> @@ -3207,6 +3207,15 @@ agic_page5: interrupt-controller@99b0000 {
> };
> };
>
> + hte_lic: hardware-timestamp@8380000 {
> + compatible = "nvidia,tegra264-gte-lic";
> + reg = <0x0 0x08380000 0x0 0x10000>;
> + interrupts = <GIC_SPI 0x00000268 IRQ_TYPE_LEVEL_HIGH>;
> + nvidia,int-threshold = <1>;
> + #timestamp-cells = <1>;
> + status = "disabled";
> + };
> +
> gpcdma: dma-controller@8400000 {
> compatible = "nvidia,tegra264-gpcdma", "nvidia,tegra186-gpcdma";
> reg = <0x0 0x08400000 0x0 0x210000>;
> @@ -3267,6 +3276,16 @@ hsp_top: hsp@8800000 {
> #mbox-cells = <2>;
> };
>
> + hte_aon: hardware-timestamp@c2b0000 {
> + compatible = "nvidia,tegra264-gte-aon";
> + reg = <0x0 0x0c2b0000 0x0 0x10000>;
> + interrupts = <GIC_SPI 0x00000226 IRQ_TYPE_LEVEL_HIGH>;
> + nvidia,int-threshold = <1>;
> + #timestamp-cells = <1>;
> + nvidia,gpio-controller = <&gpio_aon>;
> + status = "disabled";
> + };
> +
> rtc: rtc@c2c0000 {
> compatible = "nvidia,tegra264-rtc", "nvidia,tegra20-rtc";
> reg = <0x0 0x0c2c0000 0x0 0x10000>;
Acked-by: Dipen Patel <dipenp@nvidia.com>
^ permalink raw reply
* Re: [PATCH V10 4/4] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring
From: Daniel Lezcano @ 2026-04-16 21:12 UTC (permalink / raw)
To: Jishnu Prakash
Cc: jic23, robh, krzk+dt, conor+dt, agross, andersson, lumag,
dmitry.baryshkov, konradybcio, daniel.lezcano, sboyd, amitk,
thara.gopinath, lee, rafael, subbaraman.narayanamurthy,
david.collins, anjelique.melendez, kamal.wadhwa, rui.zhang,
lukasz.luba, devicetree, linux-arm-msm, linux-iio, linux-kernel,
linux-pm, cros-qcom-dts-watchers, quic_kotarake, neil.armstrong,
stephan.gerhold
In-Reply-To: <12d683aa-44c2-4e2d-8459-78ba9f2ab61e@oss.qualcomm.com>
On 4/16/26 10:05, Jishnu Prakash wrote:
> Hi Daniel,
>
> On 4/9/2026 11:42 AM, Daniel Lezcano wrote:
>> On Fri, Jan 30, 2026 at 05:24:21PM +0530, Jishnu Prakash wrote:
>>> Add support for ADC_TM part of PMIC5 Gen3.
>>>
>>> This is an auxiliary driver under the Gen3 ADC driver, which implements the
>>> threshold setting and interrupt generating functionalities of QCOM ADC_TM
>>> drivers, used to support thermal trip points.
>>>
>>> Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
>
> ...
>
>>> +
>>> +static irqreturn_t adctm5_gen3_isr(int irq, void *dev_id)
>>> +{
>>> + struct adc_tm5_gen3_chip *adc_tm5 = dev_id;
>>> + int ret, sdam_num;
>>> + u8 tm_status[2];
>>> + u8 status, val;
>>> +
>>> + sdam_num = get_sdam_from_irq(adc_tm5, irq);
>>> + if (sdam_num < 0) {
>>> + dev_err(adc_tm5->dev, "adc irq %d not associated with an sdam\n",
>>> + irq);
>>> + return IRQ_HANDLED;
>>> + }
>>> +
>>> + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_STATUS1,
>>> + &status, sizeof(status));
>>> + if (ret) {
>>> + dev_err(adc_tm5->dev, "adc read status1 failed with %d\n", ret);
>>> + return IRQ_HANDLED;
>>> + }
>>> +
>>> + if (status & ADC5_GEN3_STATUS1_CONV_FAULT) {
>>> + dev_err_ratelimited(adc_tm5->dev,
>>> + "Unexpected conversion fault, status:%#x\n",
>>> + status);
>>> + val = ADC5_GEN3_CONV_ERR_CLR_REQ;
>>> + adc5_gen3_status_clear(adc_tm5->dev_data, sdam_num,
>>> + ADC5_GEN3_CONV_ERR_CLR, &val, 1);
>>> + return IRQ_HANDLED;
>>> + }
>>> +
>>> + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_TM_HIGH_STS,
>>> + tm_status, sizeof(tm_status));
>>> + if (ret) {
>>> + dev_err(adc_tm5->dev, "adc read TM status failed with %d\n", ret);
>>> + return IRQ_HANDLED;
>>> + }
>>> +
>>> + if (tm_status[0] || tm_status[1])
>>> + schedule_work(&adc_tm5->tm_handler_work);
>>> +
>>> + dev_dbg(adc_tm5->dev, "Interrupt status:%#x, high:%#x, low:%#x\n",
>>> + status, tm_status[0], tm_status[1]);
>>> +
>>> + return IRQ_HANDLED;
>>
>> This ISR routine should be revisited:
>>
>> - no error message inside
>
> I'll drop all the error messages, but does that also include the debug print at the end?
> In addition, the print for conversion fault is ratelimited and may be useful as it
> indicates a possible HW issue, can I keep that?
It is not a good practice to put an error message in the ISR. If the
conversion fails, then the thread blocked on the read will timeout and
then show a message.
>> - use a shared interrupt to split what is handled by the ADC and the
>> TM drivers
>
> I'll make the required updates in the main ADC driver and this driver to share the first
> SDAM's interrupt.
>
>>
>> - do not return IRQ_HANDLED in case of error (cf. irqreturn.h doc)
>>
>
> I'll replace IRQ_HANDLED with IRQ_NONE at places where errors are returned.
> But in the case of conversion fault, I think returning IRQ_HANDLED may be
> more appropriate because we do handle it by clearing the status, to
> allow subsequent conversion requests to be sent.
>
> What do you think, is this fine?
It is a good point.
Actually, if get_sdam_from_irq() or adc5_gen3_read() fail, they will
return without clearing the interrupt flag, so we should potentially end
up in an infinite loop.
So the status should be cleared at the end with IRQ_HANDLED. IRQ_NONE
returned if it is for another subsystem.
If you think there can be a significant number of errors in the handler
may be you should add statistics but later in an additional series if it
makes sense.
[ ... ]
>>> + adc_tm5 = prop->chip;
>>> +
>>> + if (prop->last_temp_set) {
>>> + pr_debug("last_temp: %d\n", prop->last_temp);
>>> + prop->last_temp_set = false;
>>> + *temp = prop->last_temp;
>>> + return 0;
>>> + }
>>
>> Why do you need to do that?
>>
>> The temperature should reflect the current situation even if the
>> reading was triggered by a thermal trip violation.
>>
>
> This logic is needed to handle a corner case issue we have seen earlier.
> In this case, the ADC_TM threshold violation interrupt gets triggered ,
> but when get_temp() is subsequently called by the thermal framework, the
> temperature has fluctuated and the value read now lies within the thresholds,
> so the thresholds do not get updated by the thermal framework and the violation
> interrupts get repeated several times, until there is a get_temp() call
> which returns a temperature outside the threshold range.
Oh, that's clearly an issue with the thermal framework, not the driver.
> In order to avoid this issue, when the interrupt handler runs, we find the actual
> temperature read in ADC_TM that led to threshold violation by reading the ADC_TM
> data registers and we cache it and return it when get_temp() is called in the flow
> of thermal_zone_device_update(). Any subsequent calls to get_temp() would
> return the actual channel temperature at the time.
>
> This is only done to avoid delaying thermal mitigation due to temperature
> fluctuations. Do you think this needs to be changed?
I think it is an interesting problem certainly impacting all thermal
sensors. It should be fixed in the thermal framework itself if possible.
Just drop this portion of code and let's handle that correctly in the
thermal framework.
[ ... ]
>>> + dev_dbg(adc_tm5->dev, "channel:%s, low_temp(mdegC):%d, high_temp(mdegC):%d\n",
>>> + prop->common_props.label, low_temp, high_temp);
>>> +
>>> + guard(adc5_gen3)(adc_tm5);
>>> + if (high_temp == INT_MAX && low_temp == -INT_MAX)
>>> + return adc_tm5_gen3_disable_channel(prop);
>>
>> Why disable the channel instead of returning an errno ?
>>
>
> This is the convention we follow in our existing ADC_TM driver at
> drivers/thermal/qcom/qcom-spmi-adc-tm5.c. If both upper and lower
> thresholds are meant to be disabled, we disable the channel fully
> in HW to save some power and it can be enabled later if this API
> is called for it with valid thresholds.
>
> Is it considered invalid in the thermal framework to try to disable
> both thresholds? Should I both disable the channel and return some
> error from here?
Well, if the channel is disabled, then the temperature sensor of the
thermal zone is disabled, consequently the thermal zone is disabled from
a HW POV but enabled from the kernel POV.
Why not add the 'change_mode' ops and then disable the thermal zone (+
pm_runtime) ?
[ ... ]
>>> + /*
>>> + * Skipping first SDAM IRQ as it is requested in parent driver.
>>> + * If there is a TM violation on that IRQ, the parent driver calls
>>> + * the notifier (adctm_event_handler) exposed from this driver to handle it.
>>> + */
>>> + for (i = 1; i < adc_tm5->dev_data->num_sdams; i++) {
>>> + ret = devm_request_threaded_irq(dev,
>>> + adc_tm5->dev_data->base[i].irq,
>>> + NULL, adctm5_gen3_isr, IRQF_ONESHOT,
>>> + adc_tm5->dev_data->base[i].irq_name,
>>> + adc_tm5);
>>
>> The threaded interrupts set the isr in a thread and from the thread
>> handling the event, there is a work queue scheduled. Why not use the
>> top and bottom halves of the threaded interrupt ? Hopefully you should
>> be able to remove the lock.
>
> Yes, I can use the top and bottom halves of the threaded interrupt as you
> suggested. But what exactly do you mean by removing the lock?
>
> If you meant the mutex lock used in this driver, we cannot remove that.
> This is because the ADC_TM driver needs to write into several registers
> shared with the main ADC driver for setting new thresholds, so we
> have to share a mutex between the drivers to prevent concurrency issues.
When using a workqueue tampering with registers while an interrupt
handler is doing the same, the lock is needed.
But if the workqueue is replaced by threaded interrupt, the lock *may*
not be needed because the design may prevent race conditions.
That may be not true in this case, I did not investigate deeper in the
code to figure it out. Let's see the next version
> I'll address all your other comments too in the next version of this patch.
Thanks
-- Daniel
^ permalink raw reply
* Re: [PATCH v4 1/8] ARM: zte: Add zx297520v3 platform support
From: Randy Dunlap @ 2026-04-16 21:17 UTC (permalink / raw)
To: Stefan Dösinger, Jonathan Corbet, Shuah Khan, Russell King,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial
In-Reply-To: <20260416-send-v4-1-e19d02b944ec@gmail.com>
On 4/16/26 1:19 PM, Stefan Dösinger wrote:
> diff --git a/arch/arm/mach-zte/Kconfig b/arch/arm/mach-zte/Kconfig
> new file mode 100644
> index 000000000000..24699256863b
> --- /dev/null
> +++ b/arch/arm/mach-zte/Kconfig
> @@ -0,0 +1,24 @@
> +# SPDX-License-Identifier: GPL-2.0
> +menuconfig ARCH_ZTE
> + bool "ZTE zx family"
> + depends on ARCH_MULTI_V7
> + help
> + Support for ZTE zx-based family of processors.
> +
> +if ARCH_ZTE
> +
> +config SOC_ZX297520V3
> + default y if ARCH_ZTE
> + bool "ZX297520v3"
> + select ARM_GIC_V3
> + select ARM_AMBA
> + select HAVE_ARM_ARCH_TIMER
> + select PM_GENERIC_DOMAINS if PM
> + help
> + Support for ZTE zx297520v3 SoC. It a single core SoC used in cheap LTE to WiFi routers.
It is
> + These devices can be Identified by the occurrence of the string "zx297520v3" in the boot
identified
> + output and /proc/cpuinfo of their stock firmware.
> +
> + Please read Documentation/arch/arm/zte/zx297520v3.rst on how to boot the kernel.
--
~Randy
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: hwmon: pmbus: add max20830
From: Conor Dooley @ 2026-04-16 21:31 UTC (permalink / raw)
To: Guenter Roeck
Cc: Alexis Czezar Torreno, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jonathan Corbet, Shuah Khan, linux-hwmon,
devicetree, linux-kernel, linux-doc
In-Reply-To: <84a5154f-1139-425e-94ae-31d7e662cd0e@roeck-us.net>
[-- Attachment #1: Type: text/plain, Size: 2971 bytes --]
On Thu, Apr 16, 2026 at 11:01:49AM -0700, Guenter Roeck wrote:
> On Thu, Apr 16, 2026 at 04:51:37PM +0100, Conor Dooley wrote:
> > On Thu, Apr 16, 2026 at 03:59:10PM +0800, Alexis Czezar Torreno wrote:
> > > Add device tree documentation for MAX20830 step-down DC-DC switching
> > > regulator with PMBus interface.
> > >
> > > Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
> > > ---
> > > .../bindings/hwmon/pmbus/adi,max20830.yaml | 61 ++++++++++++++++++++++
> > > MAINTAINERS | 7 +++
> > > 2 files changed, 68 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> > > new file mode 100644
> > > index 0000000000000000000000000000000000000000..8b3ec1ffa0c9460de2122f6606ce3dcbcdfbbcc7
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> > > @@ -0,0 +1,61 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20830.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Analog Devices MAX20830 Step-Down Switching Regulator with PMBus
> > > +
> > > +maintainers:
> > > + - Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
> > > +
> > > +description: |
> > > + The MAX20830 is a fully integrated step-down DC-DC switching regulator with
> > > + PMBus interface. It provides 2.7V to 16V input, 0.4V to 5.8V adjustable
> > > + output, and up to 30A output current. It allows monitoring of input/output
> > > + voltage, output current and temperature through the PMBus serial interface.
> > > + Datasheet:
> > > + https://www.analog.com/en/products/max20830.html
> > > +
> > > +allOf:
> > > + - $ref: /schemas/regulator/regulator.yaml#
> > > +
> > > +properties:
> > > + compatible:
> > > + const: adi,max20830
> > > +
> > > + reg:
> > > + maxItems: 1
> >
> > On the previous version, you got an LLM comment about not having the
> > interrupts property amongst other things.
> > I think the other things got implemented, but I didn't see any reply to
> > the bot about that?
> > I think the answer is that it shouldn't because the pin it referenced
> > doesn't exist, but when looking at the schematic I have to wonder if
>
> I had to look this up in the datasheet. A SMBus chip with no alert pin is
> a bit odd, but you are correct.
>
> > there should be an interrupts property for dealing with "pgood"?
> >
> FWIW, I have never seen that. Normally such pins are used to take devices
> out of reset.
It's an output on this device seemingly. I don't care if the driver
ignores it, but for completeness (and we like completeness with
bindings) I think it should be documented as an interrupt or gpio.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v1 1/1] dt-bindings: media: mt9m114: document common video device properties
From: Laurent Pinchart @ 2026-04-16 21:40 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-media, devicetree, linux-kernel
In-Reply-To: <20260406081330.30362-2-clamor95@gmail.com>
On Mon, Apr 06, 2026 at 11:13:30AM +0300, Svyatoslav Ryhel wrote:
> Document common video interface device properties, such as rotation and
> orientation.
>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> .../devicetree/bindings/media/i2c/onnn,mt9m114.yaml | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml b/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml
> index e896f4db2421..2b39614f5cbf 100644
> --- a/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml
> +++ b/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml
> @@ -15,6 +15,9 @@ description: |-
> an I2C interface and outputs image data over a 8-bit parallel or 1-lane MIPI
> CSI-2 connection.
>
> +allOf:
> + - $ref: /schemas/media/video-interface-devices.yaml#
> +
> properties:
> compatible:
> enum:
> @@ -90,7 +93,7 @@ required:
> - vaa-supply
> - port
>
> -additionalProperties: false
> +unevaluatedProperties: false
>
> examples:
> - |
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH RFC 0/4] net: ipa: add support for Eliza SoC (IPA 5.5)
From: Alexander Koskovich @ 2026-04-16 22:40 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alex Elder
Cc: linux-arm-msm, netdev, devicetree, linux-kernel,
Alexander Koskovich
This series adds support to the IPA driver for the Eliza SoC (IPA 5.5).
Wanted some feedback on how best to handle the difference in the Q6 FNR
counters between Eliza & SM8550/SM8650, since it also changes the memory
layout [1].
I was thinking about something like checking firmware version after
loading IPA firmware and if above X version, use increased FnR counters
but I am not sure what firmware version this was introduced in.
For now I am just doing it with a seperate Eliza compatible but this
feels kind of meh. I'm also not sure if it's possible there's some Eliza
variant out there that actually has firmware that is too old, and then
68 for FnR counters is too much.
I also wanted some clarification on the general need to pass hw filter
stats info, downstream this is marked as "optional", but seems very
much needed for Eliza.
Is this actually optional and there is just some other misconfiguration
or is firmware broken? Hard to debug what modem wants since system does
a complete halt shortly after starting IPA if I don't pass this.
[1]: https://git.codelinaro.org/clo/la/platform/vendor/opensource/dataipa/-/commit/0a3c432e4fd294eba6def56378acb6fa39feb400
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
Alexander Koskovich (4):
dt-bindings: net: qcom,ipa: document Eliza compatible
net: ipa: fix IPA v5.5 configuration data
net: ipa: add new QMI request for HW filter stats info
net: ipa: add Eliza configuration data
.../devicetree/bindings/net/qcom,ipa.yaml | 1 +
drivers/net/ipa/data/ipa_data-v4.5.c | 1 +
drivers/net/ipa/data/ipa_data-v4.7.c | 1 +
drivers/net/ipa/data/ipa_data-v4.9.c | 1 +
drivers/net/ipa/data/ipa_data-v5.0.c | 1 +
drivers/net/ipa/data/ipa_data-v5.5.c | 180 ++++++++++++++++++++-
drivers/net/ipa/ipa.h | 3 +
drivers/net/ipa/ipa_data.h | 4 +
drivers/net/ipa/ipa_main.c | 4 +
drivers/net/ipa/ipa_mem.c | 2 +
drivers/net/ipa/ipa_qmi.c | 18 +++
drivers/net/ipa/ipa_qmi_msg.c | 58 +++++++
drivers/net/ipa/ipa_qmi_msg.h | 15 +-
13 files changed, 280 insertions(+), 9 deletions(-)
---
base-commit: 936c21068d7ade00325e40d82bfd2f3f29d9f659
change-id: 20260416-eliza-ipa-c26a88213ff3
Best regards,
--
Alexander Koskovich <akoskovich@pm.me>
^ permalink raw reply
* [PATCH RFC 1/4] dt-bindings: net: qcom,ipa: document Eliza compatible
From: Alexander Koskovich @ 2026-04-16 22:40 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alex Elder
Cc: linux-arm-msm, netdev, devicetree, linux-kernel,
Alexander Koskovich
In-Reply-To: <20260416-eliza-ipa-v1-0-f4109a8e43c4@pm.me>
Document the IPA on the Eliza Platform which uses version 5.5.1,
which is a minor revision of v5.5 found on SM8550, thus we can
use the SM8550 bindings as fallback since it shares the same
register mappings.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
Documentation/devicetree/bindings/net/qcom,ipa.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/net/qcom,ipa.yaml b/Documentation/devicetree/bindings/net/qcom,ipa.yaml
index fdeaa81b9645..38a5a337c34f 100644
--- a/Documentation/devicetree/bindings/net/qcom,ipa.yaml
+++ b/Documentation/devicetree/bindings/net/qcom,ipa.yaml
@@ -60,6 +60,7 @@ properties:
- const: qcom,sc7180-ipa
- items:
- enum:
+ - qcom,eliza-ipa
- qcom,sm8650-ipa
- const: qcom,sm8550-ipa
--
2.53.0
^ permalink raw reply related
* [PATCH RFC 2/4] net: ipa: fix IPA v5.5 configuration data
From: Alexander Koskovich @ 2026-04-16 22:40 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alex Elder
Cc: linux-arm-msm, netdev, devicetree, linux-kernel,
Alexander Koskovich
In-Reply-To: <20260416-eliza-ipa-v1-0-f4109a8e43c4@pm.me>
struct ipa_qmb_outstanding {
u16 ot_reads;
u16 ot_writes;
u16 ot_read_beats;
};
[IPA_5_5][IPA_QMB_INSTANCE_DDR] = {16, 12, 0},
[IPA_5_5][IPA_QMB_INSTANCE_PCIE] = {16, 8, 0},
IPA_ENDPOINT_AP_LAN_RX:
[IPA_5_5][IPA_CLIENT_APPS_LAN_CONS] = {
true, IPA_v5_5_GROUP_UL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 17, 14, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY, 0 },
IPA_TX_INSTANCE_UL },
IPA_ENDPOINT_AP_MODEM_RX:
[IPA_5_5][IPA_CLIENT_APPS_WAN_CONS] = {
true, IPA_v5_5_GROUP_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 24, 1, 9, 9, IPA_EE_AP, GSI_SMART_PRE_FETCH, 3 },
IPA_TX_INSTANCE_DL },
IPA_ENDPOINT_MODEM_AP_RX:
[IPA_5_5][IPA_CLIENT_Q6_WAN_CONS] = {
true, IPA_v5_5_GROUP_UL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 22, 7, 9, 9, IPA_EE_Q6, GSI_ESCAPE_BUF_ONLY, 0 },
IPA_TX_INSTANCE_UL },
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
drivers/net/ipa/data/ipa_data-v5.5.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ipa/data/ipa_data-v5.5.c b/drivers/net/ipa/data/ipa_data-v5.5.c
index 741ae21d9d78..f6ba3b944700 100644
--- a/drivers/net/ipa/data/ipa_data-v5.5.c
+++ b/drivers/net/ipa/data/ipa_data-v5.5.c
@@ -50,13 +50,13 @@ enum ipa_rsrc_group_id {
/* QSB configuration data for an SoC having IPA v5.5 */
static const struct ipa_qsb_data ipa_qsb_data[] = {
[IPA_QSB_MASTER_DDR] = {
- .max_writes = 0, /* Unlimited */
- .max_reads = 12,
+ .max_writes = 12,
+ .max_reads = 0, /* Unlimited */
.max_reads_beats = 0,
},
[IPA_QSB_MASTER_PCIE] = {
- .max_writes = 0, /* Unlimited */
- .max_reads = 8,
+ .max_writes = 8,
+ .max_reads = 0, /* Unlimited */
.max_reads_beats = 0,
},
};
@@ -86,8 +86,8 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = {
},
[IPA_ENDPOINT_AP_LAN_RX] = {
.ee_id = GSI_EE_AP,
- .channel_id = 13,
- .endpoint_id = 16,
+ .channel_id = 14,
+ .endpoint_id = 17,
.toward_ipa = false,
.channel = {
.tre_count = 256,
@@ -135,7 +135,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = {
[IPA_ENDPOINT_AP_MODEM_RX] = {
.ee_id = GSI_EE_AP,
.channel_id = 1,
- .endpoint_id = 23,
+ .endpoint_id = 24,
.toward_ipa = false,
.channel = {
.tre_count = 256,
@@ -168,7 +168,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = {
[IPA_ENDPOINT_MODEM_AP_RX] = {
.ee_id = GSI_EE_MODEM,
.channel_id = 7,
- .endpoint_id = 21,
+ .endpoint_id = 22,
.toward_ipa = false,
},
[IPA_ENDPOINT_MODEM_DL_NLO_TX] = {
--
2.53.0
^ permalink raw reply related
* [PATCH RFC 3/4] net: ipa: add new QMI request for HW filter stats info
From: Alexander Koskovich @ 2026-04-16 22:41 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alex Elder
Cc: linux-arm-msm, netdev, devicetree, linux-kernel,
Alexander Koskovich
In-Reply-To: <20260416-eliza-ipa-v1-0-f4109a8e43c4@pm.me>
Some IPA firmware versions on IPA 5.5 require ipa_filter_stats_info in
the init_modem_driver QMI request. For example on Eliza, if this is not
passed then shortly after IPA init the system will halt and reboot
shortly after.
Downstream this is marked as optional but does not seem to be the case
on newer IPA firmware versions.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
drivers/net/ipa/data/ipa_data-v4.5.c | 1 +
drivers/net/ipa/data/ipa_data-v4.7.c | 1 +
drivers/net/ipa/data/ipa_data-v4.9.c | 1 +
drivers/net/ipa/data/ipa_data-v5.0.c | 1 +
drivers/net/ipa/data/ipa_data-v5.5.c | 1 +
drivers/net/ipa/ipa.h | 3 ++
drivers/net/ipa/ipa_data.h | 3 ++
drivers/net/ipa/ipa_mem.c | 2 ++
drivers/net/ipa/ipa_qmi.c | 18 +++++++++++
drivers/net/ipa/ipa_qmi_msg.c | 58 ++++++++++++++++++++++++++++++++++++
drivers/net/ipa/ipa_qmi_msg.h | 15 +++++++++-
11 files changed, 103 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ipa/data/ipa_data-v4.5.c b/drivers/net/ipa/data/ipa_data-v4.5.c
index 730d8c43a45c..25eceffdd9f7 100644
--- a/drivers/net/ipa/data/ipa_data-v4.5.c
+++ b/drivers/net/ipa/data/ipa_data-v4.5.c
@@ -419,6 +419,7 @@ static const struct ipa_mem_data ipa_mem_data = {
.imem_addr = 0x14688000,
.imem_size = 0x00003000,
.smem_size = 0x00009000,
+ .fnr_idx_cnt = 52,
};
/* Interconnect rates are in 1000 byte/second units */
diff --git a/drivers/net/ipa/data/ipa_data-v4.7.c b/drivers/net/ipa/data/ipa_data-v4.7.c
index 5e1d9049c62b..ef4695ee1b2d 100644
--- a/drivers/net/ipa/data/ipa_data-v4.7.c
+++ b/drivers/net/ipa/data/ipa_data-v4.7.c
@@ -361,6 +361,7 @@ static const struct ipa_mem_data ipa_mem_data = {
.imem_addr = 0x146a8000,
.imem_size = 0x00002000,
.smem_size = 0x00009000,
+ .fnr_idx_cnt = 52,
};
/* Interconnect rates are in 1000 byte/second units */
diff --git a/drivers/net/ipa/data/ipa_data-v4.9.c b/drivers/net/ipa/data/ipa_data-v4.9.c
index da472a2a2e29..0e2e521d98bb 100644
--- a/drivers/net/ipa/data/ipa_data-v4.9.c
+++ b/drivers/net/ipa/data/ipa_data-v4.9.c
@@ -417,6 +417,7 @@ static const struct ipa_mem_data ipa_mem_data = {
.imem_addr = 0x146bd000,
.imem_size = 0x00002000,
.smem_size = 0x00009000,
+ .fnr_idx_cnt = 52,
};
/* Interconnect rates are in 1000 byte/second units */
diff --git a/drivers/net/ipa/data/ipa_data-v5.0.c b/drivers/net/ipa/data/ipa_data-v5.0.c
index bc5722e4b053..9f7aaf37b8fd 100644
--- a/drivers/net/ipa/data/ipa_data-v5.0.c
+++ b/drivers/net/ipa/data/ipa_data-v5.0.c
@@ -443,6 +443,7 @@ static const struct ipa_mem_data ipa_mem_data = {
.imem_addr = 0x14688000,
.imem_size = 0x00003000,
.smem_size = 0x00009000,
+ .fnr_idx_cnt = 52,
};
/* Interconnect rates are in 1000 byte/second units */
diff --git a/drivers/net/ipa/data/ipa_data-v5.5.c b/drivers/net/ipa/data/ipa_data-v5.5.c
index f6ba3b944700..44a9df7346b7 100644
--- a/drivers/net/ipa/data/ipa_data-v5.5.c
+++ b/drivers/net/ipa/data/ipa_data-v5.5.c
@@ -449,6 +449,7 @@ static const struct ipa_mem_data ipa_mem_data = {
.imem_addr = 0x14688000,
.imem_size = 0x00002000,
.smem_size = 0x0000b000,
+ .fnr_idx_cnt = 52,
};
/* Interconnect rates are in 1000 byte/second units */
diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h
index 7ef10a4ff35e..d2ceaea31635 100644
--- a/drivers/net/ipa/ipa.h
+++ b/drivers/net/ipa/ipa.h
@@ -69,6 +69,7 @@ struct ipa_smp2p;
* @modem_state: State of modem (stopped, running)
* @modem_netdev: Network device structure used for modem
* @qmi: QMI information
+ * @fnr_idx_cnt: Number of FnR counters
*/
struct ipa {
struct gsi gsi;
@@ -129,6 +130,8 @@ struct ipa {
atomic_t modem_state; /* enum ipa_modem_state */
struct net_device *modem_netdev;
struct ipa_qmi qmi;
+
+ u8 fnr_idx_cnt;
};
/**
diff --git a/drivers/net/ipa/ipa_data.h b/drivers/net/ipa/ipa_data.h
index 3eb9dc2ce339..f7566c8edabd 100644
--- a/drivers/net/ipa/ipa_data.h
+++ b/drivers/net/ipa/ipa_data.h
@@ -181,6 +181,7 @@ struct ipa_resource_data {
* @imem_addr: physical address of IPA region within IMEM
* @imem_size: size in bytes of IPA IMEM region
* @smem_size: size in bytes of the IPA SMEM region
+ * @fnr_idx_cnt: Number of FnR counters
*/
struct ipa_mem_data {
u32 local_count;
@@ -193,6 +194,8 @@ struct ipa_mem_data {
u32 imem_size; /* DEPRECATED */
u32 smem_size;
+
+ u8 fnr_idx_cnt;
};
/**
diff --git a/drivers/net/ipa/ipa_mem.c b/drivers/net/ipa/ipa_mem.c
index 078d32a18dbf..fb04f953bf7a 100644
--- a/drivers/net/ipa/ipa_mem.c
+++ b/drivers/net/ipa/ipa_mem.c
@@ -631,6 +631,8 @@ int ipa_mem_init(struct ipa *ipa, struct platform_device *pdev,
ipa->mem_count = mem_data->local_count;
ipa->mem = mem_data->local;
+ ipa->fnr_idx_cnt = mem_data->fnr_idx_cnt;
+
/* Check the route and filter table memory regions */
if (!ipa_table_mem_valid(ipa, false))
return -EINVAL;
diff --git a/drivers/net/ipa/ipa_qmi.c b/drivers/net/ipa/ipa_qmi.c
index d771f3a71f94..a5a5572c9ccd 100644
--- a/drivers/net/ipa/ipa_qmi.c
+++ b/drivers/net/ipa/ipa_qmi.c
@@ -74,6 +74,8 @@
#define IPA_MODEM_SERVICE_INS_ID 2
#define IPA_MODEM_SVC_VERS 1
+#define IPA_MODEM_FNR_IDX_START 128
+
#define QMI_INIT_DRIVER_TIMEOUT 60000 /* A minute in milliseconds */
/* Send an INIT_COMPLETE indication message to the modem */
@@ -394,6 +396,22 @@ init_modem_driver_req(struct ipa_qmi *ipa_qmi)
}
}
+ if (ipa->version >= IPA_VERSION_4_5 && ipa->fnr_idx_cnt) {
+ mem = ipa_mem_find(ipa, IPA_MEM_STATS_FILTER_ROUTE);
+ if (mem && mem->size) {
+ req.hw_stats_filter_info_valid = 1;
+ req.hw_stats_filter_info.start_addr =
+ ipa->mem_offset + mem->offset;
+ req.hw_stats_filter_info.size =
+ ipa->fnr_idx_cnt * 16;
+ req.hw_stats_filter_info.start_index =
+ IPA_MODEM_FNR_IDX_START;
+ req.hw_stats_filter_info.end_index =
+ IPA_MODEM_FNR_IDX_START +
+ ipa->fnr_idx_cnt - 1;
+ }
+ }
+
return &req;
}
diff --git a/drivers/net/ipa/ipa_qmi_msg.c b/drivers/net/ipa/ipa_qmi_msg.c
index 51dc13a577a5..160c0d207691 100644
--- a/drivers/net/ipa/ipa_qmi_msg.c
+++ b/drivers/net/ipa/ipa_qmi_msg.c
@@ -250,6 +250,43 @@ const struct qmi_elem_info ipa_mem_range_ei[] = {
},
};
+/* QMI message structure definition for struct ipa_stats_filter */
+const struct qmi_elem_info ipa_stats_filter_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct ipa_stats_filter, start_addr),
+ .offset = offsetof(struct ipa_stats_filter,
+ start_addr),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct ipa_stats_filter, size),
+ .offset = offsetof(struct ipa_stats_filter, size),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct ipa_stats_filter, start_index),
+ .offset = offsetof(struct ipa_stats_filter,
+ start_index),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct ipa_stats_filter, end_index),
+ .offset = offsetof(struct ipa_stats_filter, end_index),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+
/* QMI message structure definition for struct ipa_init_modem_driver_req */
const struct qmi_elem_info ipa_init_modem_driver_req_ei[] = {
{
@@ -640,6 +677,27 @@ const struct qmi_elem_info ipa_init_modem_driver_req_ei[] = {
.offset = offsetof(struct ipa_init_modem_driver_req,
hw_stats_drop_size),
},
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct ipa_init_modem_driver_req,
+ hw_stats_filter_info_valid),
+ .tlv_type = 0x23,
+ .offset = offsetof(struct ipa_init_modem_driver_req,
+ hw_stats_filter_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct ipa_init_modem_driver_req,
+ hw_stats_filter_info),
+ .tlv_type = 0x23,
+ .offset = offsetof(struct ipa_init_modem_driver_req,
+ hw_stats_filter_info),
+ .ei_array = ipa_stats_filter_ei,
+ },
{
.data_type = QMI_EOTI,
},
diff --git a/drivers/net/ipa/ipa_qmi_msg.h b/drivers/net/ipa/ipa_qmi_msg.h
index 644b8c27108b..3a2205c213d1 100644
--- a/drivers/net/ipa/ipa_qmi_msg.h
+++ b/drivers/net/ipa/ipa_qmi_msg.h
@@ -27,7 +27,7 @@
*/
#define IPA_QMI_INDICATION_REGISTER_REQ_SZ 20 /* -> server handle */
#define IPA_QMI_INDICATION_REGISTER_RSP_SZ 7 /* <- server handle */
-#define IPA_QMI_INIT_DRIVER_REQ_SZ 162 /* client handle -> */
+#define IPA_QMI_INIT_DRIVER_REQ_SZ 186 /* client handle -> */
#define IPA_QMI_INIT_DRIVER_RSP_SZ 25 /* client handle <- */
#define IPA_QMI_INIT_COMPLETE_IND_SZ 7 /* <- server handle */
#define IPA_QMI_DRIVER_INIT_COMPLETE_REQ_SZ 4 /* -> server handle */
@@ -119,6 +119,13 @@ struct ipa_mem_range {
u32 size;
};
+struct ipa_stats_filter {
+ u32 start_addr;
+ u32 size;
+ u8 start_index;
+ u8 end_index;
+};
+
/* The message for the IPA_QMI_INIT_DRIVER request contains information
* from the AP that affects modem initialization.
*/
@@ -216,6 +223,11 @@ struct ipa_init_modem_driver_req {
u32 hw_stats_drop_base_addr;
u8 hw_stats_drop_size_valid;
u32 hw_stats_drop_size;
+
+ /* Hardware filter statistics information. (IPA v4.5 and above)
+ */
+ u8 hw_stats_filter_info_valid;
+ struct ipa_stats_filter hw_stats_filter_info;
};
/* The response to a IPA_QMI_INIT_DRIVER request begins with a standard
@@ -256,6 +268,7 @@ extern const struct qmi_elem_info ipa_init_complete_ind_ei[];
extern const struct qmi_elem_info ipa_mem_bounds_ei[];
extern const struct qmi_elem_info ipa_mem_array_ei[];
extern const struct qmi_elem_info ipa_mem_range_ei[];
+extern const struct qmi_elem_info ipa_stats_filter_ei[];
extern const struct qmi_elem_info ipa_init_modem_driver_req_ei[];
extern const struct qmi_elem_info ipa_init_modem_driver_rsp_ei[];
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox