* Re: [PATCH RFC 06/10] arm64: dts: qcom: msm8939-asus-z00t: add Venus
From: Erikas Bitovtas @ 2026-04-16 16:57 UTC (permalink / raw)
To: Konrad Dybcio, Bryan O'Donoghue, Vikash Garodia,
Dikshita Agarwal, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, André Apitzsch,
Bjorn Andersson, Konrad Dybcio, Michael Turquette, Stephen Boyd
Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
~postmarketos/upstreaming, phone-devel
In-Reply-To: <0a5f9bd6-d3ea-4819-8be3-cc5a06ec0339@oss.qualcomm.com>
On 4/16/26 6:17 PM, Konrad Dybcio wrote:
> On 4/16/26 3:43 PM, Erikas Bitovtas wrote:
>> Enable Venus video encoder/decoder for Asus ZenFone 2 Laser/Selfie.
>>
>> Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
>> ---
>> arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts | 8 ++++++++
>> 1 file changed, 8 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts b/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts
>> index 90e966242720..231a3e9c1929 100644
>> --- a/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts
>> +++ b/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts
>> @@ -267,6 +267,14 @@ &usb_hs_phy {
>> extcon = <&usb_id>;
>> };
>>
>> +&venus {
>> + status = "okay";
>
> You need a firmware path here
When I tested Venus on my device, it loaded without one specified -
msm-firmware-loader creates a symbolic link from modem partition for
firmware. Additionally, none of the MSM8916 devices seem to include a
firmware name. Has something changed since then?
> Konrad
^ permalink raw reply
* Re: [PATCH RFC 00/10] media: qcom: venus: add MSM8939 support
From: Erikas Bitovtas @ 2026-04-16 17:00 UTC (permalink / raw)
To: Konrad Dybcio, Bryan O'Donoghue, Vikash Garodia,
Dikshita Agarwal, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, André Apitzsch,
Bjorn Andersson, Konrad Dybcio, Michael Turquette, Stephen Boyd
Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
~postmarketos/upstreaming, phone-devel
In-Reply-To: <b7b6c3e7-f8e6-4b73-b17a-e5e1691a54f8@oss.qualcomm.com>
>> 3. MSM8939 supports HEVC decoding, however, as the patchset is written
>> now, it does not work. It can be enabled, however, it will result in
>> breakage of Venus for faulty MSM8916 firmwares, because the code
>> disabling HEVC for HFI v1 needs to be removed, and as per commit
>> c50cc6dc6c48 ("media: venus: hfi_parser: Ignore HEVC encoding for V1"),
>> this would break support for some MSM8916 devices. What could be the
>> best way to work around this?
>
> if (!device_is_compatible(core->dev, "qcom,msm8939-venus"))?
>
> Also, you mentioned HEVC *de*coding, while the commit you pointed to
> disables *en*coding (decoding had been already disabled prior to that
> commit)
>
> Konrad
From the commit message I assumed HEVC decoding had already been
disabled for the same reasons encoding was - faulty firmware reporting
codecs it doesn't actually support.
^ permalink raw reply
* [PATCH 0/2] clocksource/timer-econet-en751221: Support irq number per timer
From: Caleb James DeLisle @ 2026-04-16 17:50 UTC (permalink / raw)
To: linux-mips
Cc: naseefkm, daniel.lezcano, tglx, robh, krzk+dt, conor+dt,
linux-kernel, devicetree, Caleb James DeLisle
In prep for adding EN751627 and EN7528 SoCs, we need to support the GIC
interrupt controller. Unlike the intc in the EN751221, this intc does
not create a percpu interrupt for the timers, so we update the timer
driver to support both models.
Caleb James DeLisle (2):
dt-bindings: timer: econet: Update EN751627 for multi-IRQ
clocksource/timer-econet-en751221: Support irq number per timer
.../bindings/timer/econet,en751221-timer.yaml | 16 +-
drivers/clocksource/Kconfig | 5 +-
drivers/clocksource/timer-econet-en751221.c | 137 ++++++++++++++----
3 files changed, 127 insertions(+), 31 deletions(-)
base-commit: ff1c0c5d07028a84837950b619d30da623f8ddb2
--
2.39.5
^ permalink raw reply
* [PATCH 1/2] dt-bindings: timer: econet: Update EN751627 for multi-IRQ
From: Caleb James DeLisle @ 2026-04-16 17:51 UTC (permalink / raw)
To: linux-mips
Cc: naseefkm, daniel.lezcano, tglx, robh, krzk+dt, conor+dt,
linux-kernel, devicetree, Caleb James DeLisle
In-Reply-To: <20260416175101.958073-1-cjd@cjdns.fr>
From conception, this driver supported EN751627 as it is the same
hardware that is used in EN751221. However, it was expected that
EN751627 would use a percpu IRQ as does EN751221, this is how it
works in vendor code. However upon finding that the "mti,gic" intc
works on EN751627 with no modification - but it provides a unique
interrupt per-timer, it is deemed best to make this driver use
multiple IRQs when on the EN751627 platform.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
.../bindings/timer/econet,en751221-timer.yaml | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml b/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
index c1e7c2b6afde..f338739e039c 100644
--- a/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
@@ -28,8 +28,8 @@ properties:
maxItems: 2
interrupts:
- maxItems: 1
- description: A percpu-devid timer interrupt shared across CPUs.
+ minItems: 1
+ maxItems: 4
clocks:
maxItems: 1
@@ -52,21 +52,31 @@ allOf:
items:
- description: VPE timers 0 and 1
- description: VPE timers 2 and 3
+ interrupts:
+ description: An interrupt for each timer (one per VPE)
+ minItems: 4
else:
properties:
reg:
items:
- description: VPE timers 0 and 1
+ interrupts:
+ description: A percpu-devid timer interrupt shared across timers
+ maxItems: 1
additionalProperties: false
examples:
- |
+ #include <dt-bindings/interrupt-controller/mips-gic.h>
timer@1fbf0400 {
compatible = "econet,en751627-timer", "econet,en751221-timer";
reg = <0x1fbf0400 0x100>, <0x1fbe0000 0x100>;
interrupt-parent = <&intc>;
- interrupts = <30>;
+ interrupts = <GIC_SHARED 30 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 29 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 37 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&hpt_clock>;
};
- |
--
2.39.5
^ permalink raw reply related
* [PATCH 2/2] clocksource/timer-econet-en751221: Support irq number per timer
From: Caleb James DeLisle @ 2026-04-16 17:51 UTC (permalink / raw)
To: linux-mips
Cc: naseefkm, daniel.lezcano, tglx, robh, krzk+dt, conor+dt,
linux-kernel, devicetree, Caleb James DeLisle
In-Reply-To: <20260416175101.958073-1-cjd@cjdns.fr>
This timer was first developed on the EN751221 which is a MIPS 34Kc
and therefore has a custom interrupt controller. The hardware for
econet,en751221-intc implements percpu routing of the timer
interrupts.
However, the EN751627 and EN7528 are MIPS 1004Kc based, and
therefore use the standard mti,gic compatible interrupt controller.
This interrupt controller uses a different IRQ number for each
timer interrupt.
Add support for both models in this timer driver.
Co-developed-by: Ahmed Naseef <naseefkm@gmail.com>
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
Link: https://github.com/openwrt/openwrt/commit/fab098cb6121647ca9cc6e501d56ebe8a9ea550b#diff-a09ee5e4166e89df337d03c1455dce7b81eb89797b1d0f714476b188e6685334
[cjd@cjdns.fr minor changes:
Set ECONET_MAX_IRQS to NR_CPUS rather than 4
Use is_percpu_irq() instead of field
Do not set CLOCK_EVT_FEAT_PERCPU in non-percpu mode
Fold cevt_init() into timer_init()
]
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
drivers/clocksource/Kconfig | 5 +-
drivers/clocksource/timer-econet-en751221.c | 137 ++++++++++++++++----
2 files changed, 114 insertions(+), 28 deletions(-)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index d1a33a231a44..9a77f38d5fb7 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -79,7 +79,10 @@ config ECONET_EN751221_TIMER
select CLKSRC_MMIO
select TIMER_OF
help
- Support for CPU timer found on EcoNet MIPS based SoCs.
+ Support for CPU timer found on EcoNet EN75xx MIPS based SoCs
+ (EN751221, EN751627, EN7528). The driver supports both GIC-based
+ (separate IRQ per CPU) and legacy interrupt controller (percpu IRQ)
+ modes.
config FTTMR010_TIMER
bool "Faraday Technology timer driver" if COMPILE_TEST
diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c
index 4008076b1a21..e280ee8c2b1c 100644
--- a/drivers/clocksource/timer-econet-en751221.c
+++ b/drivers/clocksource/timer-econet-en751221.c
@@ -3,11 +3,13 @@
* Timer present on EcoNet EN75xx MIPS based SoCs.
*
* Copyright (C) 2025 by Caleb James DeLisle <cjd@cjdns.fr>
+ * Copyright (C) 2025 by Ahmed Naseef <naseefkm@gmail.com>
*/
#include <linux/io.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
@@ -21,14 +23,26 @@
#define ECONET_MAX_DELTA GENMASK(ECONET_BITS - 2, 0)
/* 34Kc hardware has 1 block and 1004Kc has 2. */
#define ECONET_NUM_BLOCKS DIV_ROUND_UP(NR_CPUS, 2)
+#define ECONET_MAX_IRQS NR_CPUS
static struct {
void __iomem *membase[ECONET_NUM_BLOCKS];
u32 freq_hz;
+ int irqs[ECONET_MAX_IRQS];
+ int num_irqs;
} econet_timer __ro_after_init;
static DEFINE_PER_CPU(struct clock_event_device, econet_timer_pcpu);
+/* This timer supports two interrupt controller models, either 1 IRQ which is in per-cpu
+ * mode which is used on 34Kc CPUs, and separate IRQ number per CPU which is used on
+ * 1004Kc CPUs with GIC intc.
+ */
+static inline bool is_percpu_irq(void)
+{
+ return econet_timer.num_irqs == 1;
+}
+
/* Each memory block has 2 timers, the order of registers is:
* CTL, CMR0, CNT0, CMR1, CNT1
*/
@@ -98,12 +112,21 @@ static int cevt_init_cpu(uint cpu)
struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, cpu);
u32 reg;
+ if (!is_percpu_irq() && cpu >= econet_timer.num_irqs)
+ return -EINVAL;
+
pr_debug("%s: Setting up clockevent for CPU %d\n", cd->name, cpu);
reg = ioread32(reg_ctl(cpu)) | ctl_bit_enabled(cpu);
iowrite32(reg, reg_ctl(cpu));
- enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
+ if (is_percpu_irq()) {
+ enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
+ } else {
+ if (irq_force_affinity(econet_timer.irqs[cpu], cpumask_of(cpu)))
+ pr_warn("%s: failed to set IRQ %d affinity to CPU %d\n",
+ cd->name, econet_timer.irqs[cpu], cpu);
+ }
/* Do this last because it synchronously configures the timer */
clockevents_config_and_register(cd, econet_timer.freq_hz,
@@ -126,7 +149,20 @@ static void __init cevt_dev_init(uint cpu)
iowrite32(U32_MAX, reg_compare(cpu));
}
-static int __init cevt_init(struct device_node *np)
+static void __init cevt_setup_clockevent(struct clock_event_device *cd,
+ struct device_node *np,
+ int irq, int cpu)
+{
+ cd->rating = 310;
+ cd->features = CLOCK_EVT_FEAT_ONESHOT |
+ CLOCK_EVT_FEAT_C3STOP;
+ cd->set_next_event = cevt_set_next_event;
+ cd->irq = irq;
+ cd->cpumask = cpumask_of(cpu);
+ cd->name = np->name;
+}
+
+static int __init cevt_init_percpu(struct device_node *np)
{
int i, irq, ret;
@@ -137,42 +173,65 @@ static int __init cevt_init(struct device_node *np)
}
ret = request_percpu_irq(irq, cevt_interrupt, np->name, &econet_timer_pcpu);
-
if (ret < 0) {
pr_err("%pOFn: IRQ %d setup failed (%d)\n", np, irq, ret);
- goto err_unmap_irq;
+ irq_dispose_mapping(irq);
+ return ret;
}
for_each_possible_cpu(i) {
struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, i);
- cd->rating = 310;
- cd->features = CLOCK_EVT_FEAT_ONESHOT |
- CLOCK_EVT_FEAT_C3STOP |
- CLOCK_EVT_FEAT_PERCPU;
- cd->set_next_event = cevt_set_next_event;
- cd->irq = irq;
- cd->cpumask = cpumask_of(i);
- cd->name = np->name;
+ cevt_setup_clockevent(cd, np, irq, i);
+ cd->features |= CLOCK_EVT_FEAT_PERCPU;
+ cevt_dev_init(i);
+ }
+
+ return 0;
+}
+static int __init cevt_init_separate(struct device_node *np)
+{
+ int i, ret;
+
+ for (i = 0; i < econet_timer.num_irqs; i++) {
+ struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, i);
+
+ econet_timer.irqs[i] = irq_of_parse_and_map(np, i);
+ if (econet_timer.irqs[i] <= 0) {
+ pr_err("%pOFn: irq_of_parse_and_map failed", np);
+ ret = -EINVAL;
+ goto err_free_irqs;
+ }
+
+ ret = request_irq(econet_timer.irqs[i], cevt_interrupt,
+ IRQF_TIMER | IRQF_NOBALANCING,
+ np->name, NULL);
+ if (ret < 0) {
+ pr_err("%pOFn: IRQ %d setup failed (%d)\n", np,
+ econet_timer.irqs[i], ret);
+ irq_dispose_mapping(econet_timer.irqs[i]);
+ goto err_free_irqs;
+ }
+
+ cevt_setup_clockevent(cd, np, econet_timer.irqs[i], i);
cevt_dev_init(i);
}
- cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
- "clockevents/econet/timer:starting",
- cevt_init_cpu, NULL);
return 0;
-err_unmap_irq:
- irq_dispose_mapping(irq);
+err_free_irqs:
+ while (--i >= 0) {
+ free_irq(econet_timer.irqs[i], NULL);
+ irq_dispose_mapping(econet_timer.irqs[i]);
+ }
return ret;
}
static int __init timer_init(struct device_node *np)
{
- int num_blocks = DIV_ROUND_UP(num_possible_cpus(), 2);
struct clk *clk;
- int ret;
+ int ret, i;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
@@ -182,11 +241,18 @@ static int __init timer_init(struct device_node *np)
econet_timer.freq_hz = clk_get_rate(clk);
- for (int i = 0; i < num_blocks; i++) {
+ econet_timer.num_irqs = of_irq_count(np);
+ if (econet_timer.num_irqs <= 0 || econet_timer.num_irqs > ECONET_MAX_IRQS) {
+ pr_err("%pOFn: invalid IRQ count %d\n", np, econet_timer.num_irqs);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ECONET_NUM_BLOCKS; i++) {
econet_timer.membase[i] = of_iomap(np, i);
if (!econet_timer.membase[i]) {
pr_err("%pOFn: failed to map register [%d]\n", np, i);
- return -ENXIO;
+ ret = -ENXIO;
+ goto err_unmap;
}
}
@@ -196,21 +262,38 @@ static int __init timer_init(struct device_node *np)
clocksource_mmio_readl_up);
if (ret) {
pr_err("%pOFn: clocksource_mmio_init failed: %d", np, ret);
- return ret;
+ goto err_unmap;
}
- ret = cevt_init(np);
+ if (is_percpu_irq())
+ ret = cevt_init_percpu(np);
+ else
+ ret = cevt_init_separate(np);
+
if (ret < 0)
- return ret;
+ goto err_unmap;
+
+ cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "clockevents/econet/timer:starting",
+ cevt_init_cpu, NULL);
sched_clock_register(sched_clock_read, ECONET_BITS,
econet_timer.freq_hz);
- pr_info("%pOFn: using %u.%03u MHz high precision timer\n", np,
+ pr_info("%pOFn: using %u.%03u MHz high precision timer (%s mode)\n", np,
econet_timer.freq_hz / 1000000,
- (econet_timer.freq_hz / 1000) % 1000);
+ (econet_timer.freq_hz / 1000) % 1000,
+ is_percpu_irq() ? "percpu" : "separate IRQ");
return 0;
+
+err_unmap:
+ for (i = 0; i < ECONET_NUM_BLOCKS; i++) {
+ if (econet_timer.membase[i])
+ iounmap(econet_timer.membase[i]);
+ }
+
+ return ret;
}
-TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer", timer_init);
+TIMER_OF_DECLARE(econet_en751221_timer, "econet,en751221-timer", timer_init);
--
2.39.5
^ permalink raw reply related
* Re: [PATCH v2 1/2] dt-bindings: hwmon: pmbus: add max20830
From: Guenter Roeck @ 2026-04-16 18:01 UTC (permalink / raw)
To: Conor Dooley
Cc: Alexis Czezar Torreno, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jonathan Corbet, Shuah Khan, linux-hwmon,
devicetree, linux-kernel, linux-doc
In-Reply-To: <20260416-diaphragm-corrode-494560404ed4@spud>
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.
Thanks,
Guenter
^ permalink raw reply
* Re: [PATCH 02/16] dt-bindings: interrupt-controller: Describe EIP-201 AIC
From: Aleksander Jan Bajkowski @ 2026-04-16 18:04 UTC (permalink / raw)
To: Miquel Raynal (Schneider Electric), Michael Turquette,
Stephen Boyd, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Gleixner, Olivia Mackall, Herbert Xu, Jayesh Choudhary,
David S. Miller, Christian Marangi, Antoine Tenart,
Geert Uytterhoeven, Magnus Damm
Cc: Thomas Petazzoni, Pascal EBERHARD, Wolfram Sang, linux-clk,
devicetree, linux-kernel, linux-crypto, linux-renesas-soc
In-Reply-To: <20260327-schneider-v7-0-rc1-crypto-v1-2-5e6ff7853994@bootlin.com>
Hi Miquel,
On 27/03/2026 21:09, Miquel Raynal (Schneider Electric) wrote:
> diff --git a/include/dt-bindings/interrupt-controller/inside-secure,safexcel-eip201.h b/include/dt-bindings/interrupt-controller/inside-secure,safexcel-eip201.h
> new file mode 100644
> index 000000000000..ead73bd96296
> --- /dev/null
> +++ b/include/dt-bindings/interrupt-controller/inside-secure,safexcel-eip201.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
> +
> +#ifndef _DT_BINDINGS_IRQ_SAFEXCEL_EIP201_AIC_H
> +#define _DT_BINDINGS_IRQ_SAFEXCEL_EIP201_AIC_H
> +
> +#define AIC_PKA_INT0 0
> +#define AIC_PKA_INT1 1
> +#define AIC_PKA_INT2 2
> +#define AIC_TRNG_INT 3
> +#define AIC_RESERVED 4
> +#define AIC_SL_ERR_INT 5
> +#define AIC_PROTECTION_INT 6
> +
> +#endif
This interrupt mapping is specific to the EIP-150. The EIP-201 is also
integrated
into other accelerators, such as the EIP-97, EIP-196, and EIP-197, and the
interrupt mapping is likely different there. Maybe it would be better to use
eip150 name instead of eip201?
As for EIP-28, it is also part of EIP-94. EIP-94 is supported by the
amcc driver.
EIP-94 consists of four components:
* crypto accelerator (unnamed?),
* PRNG (EIP-73d),
* TRNG (unnamed?),
* PKA (EIP-28).
Only the first three components are supported by the amcc driver.
Best regards,
Aleksander
^ permalink raw reply
* Re: [PATCH v2 2/2] riscv: dts: spacemit: Add cpu scaling for K1 SoC
From: Yao Zi @ 2026-04-16 18:28 UTC (permalink / raw)
To: Shuwei Wu, Anand Moon
Cc: Rafael J. Wysocki, Viresh Kumar, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Yixun Lan, linux-pm, linux-kernel, linux-riscv,
spacemit, devicetree
In-Reply-To: <DHUCL24GMX7D.369IWK9DLPZPX@mailbox.org>
On Thu, Apr 16, 2026 at 01:59:05PM +0800, Shuwei Wu wrote:
> On Tue Apr 14, 2026 at 9:25 PM CST, Anand Moon wrote:
> > Hi Shuwei,
> >
> > On Fri, 10 Apr 2026 at 13:30, Shuwei Wu <shuwei.wu@mailbox.org> wrote:
> >>
> >> Add Operating Performance Points (OPP) tables and CPU clock properties
> >> for the two clusters in the SpacemiT K1 SoC.
> >>
> >> Also assign the CPU power supply (cpu-supply) for the Banana Pi BPI-F3
> >> board to fully enable CPU DVFS.
> >>
> >> Signed-off-by: Shuwei Wu <shuwei.wu@mailbox.org>
> >>
> >> ---
> >> Changes in v2:
> >> - Add k1-opp.dtsi with OPP tables for both CPU clusters
> >> - Assign CPU supplies and include OPP table for Banana Pi BPI-F3
> >> ---
> >> arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 35 +++++++-
> >> arch/riscv/boot/dts/spacemit/k1-opp.dtsi | 105 ++++++++++++++++++++++++
> >> arch/riscv/boot/dts/spacemit/k1.dtsi | 8 ++
> >> 3 files changed, 147 insertions(+), 1 deletion(-)
> >>
...
> Regarding the necessity of listing these clocks in the DT, my analysis is as follows:
> 1. For CCI550, I did not find a clear definition of this clock's specific role
> in the SoC datasheet. Although the vendor kernel increases its frequency,
> my benchmarks show that maintaining the mainline default (245.76MHz) has a
> negligible impact on CPU performance.
FYI, CCI550 is used for naming an ARM interconnect IP[1], which matches
your observation.
...
> Best regards,
> Shuwei Wu
Regards,
Yao Zi.
[1]: https://developer.arm.com/Processors/CoreLink%20CCI-550
^ permalink raw reply
* 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
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