* Re: [patch 05/38] treewide: Remove CLOCK_TICK_RATE
From: Geert Uytterhoeven @ 2026-04-16 11:22 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Arnd Bergmann, x86, Lu Baolu, iommu, Michael Grzeschik,
netdev, linux-wireless, Herbert Xu, linux-crypto, Vlastimil Babka,
linux-mm, David Woodhouse, Bernie Thompson, linux-fbdev,
Theodore Tso, linux-ext4, Andrew Morton, Uladzislau Rezki,
Marco Elver, Dmitry Vyukov, kasan-dev, Andrey Ryabinin,
Thomas Sailer, linux-hams, Jason A. Donenfeld, Richard Henderson,
linux-alpha, Russell King, linux-arm-kernel, Catalin Marinas,
Huacai Chen, loongarch, linux-m68k, Dinh Nguyen, Jonas Bonn,
linux-openrisc, Helge Deller, linux-parisc, Michael Ellerman,
linuxppc-dev, Paul Walmsley, linux-riscv, Heiko Carstens,
linux-s390, David S. Miller, sparclinux
In-Reply-To: <20260410120317.910770161@kernel.org>
On Fri, 10 Apr 2026 at 14:18, Thomas Gleixner <tglx@kernel.org> wrote:
> This has been scheduled for removal more than a decade ago and the comments
> related to it have been dutifully ignored. The last dependencies are gone.
>
> Remove it along with various now empty asm/timex.h files.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> arch/m68k/include/asm/timex.h | 15 ---------------
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org> # m68k
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [patch 27/38] m68k: Select ARCH_HAS_RANDOM_ENTROPY
From: Geert Uytterhoeven @ 2026-04-16 11:22 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, linux-m68k, Arnd Bergmann, x86, Lu Baolu, iommu,
Michael Grzeschik, netdev, linux-wireless, Herbert Xu,
linux-crypto, Vlastimil Babka, linux-mm, David Woodhouse,
Bernie Thompson, linux-fbdev, Theodore Tso, linux-ext4,
Andrew Morton, Uladzislau Rezki, Marco Elver, Dmitry Vyukov,
kasan-dev, Andrey Ryabinin, Thomas Sailer, linux-hams,
Jason A. Donenfeld, Richard Henderson, linux-alpha, Russell King,
linux-arm-kernel, Catalin Marinas, Huacai Chen, loongarch,
Dinh Nguyen, Jonas Bonn, linux-openrisc, Helge Deller,
linux-parisc, Michael Ellerman, linuxppc-dev, Paul Walmsley,
linux-riscv, Heiko Carstens, linux-s390, David S. Miller,
sparclinux
In-Reply-To: <20260410120319.397219631@kernel.org>
On Fri, 10 Apr 2026 at 14:20, Thomas Gleixner <tglx@kernel.org> wrote:
> The only remaining usage of get_cycles() is to provide
> random_get_entropy().
>
> Switch m68k over to the new scheme of selecting ARCH_HAS_RANDOM_ENTROPY and
> providing random_get_entropy() in asm/random.h.
>
> Remove asm/timex.h as it has no functionality anymore.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Reviewed-by: Geert Uytterhoeven <geert@linux-m68k.org>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH 1/9] dt-bindings: sound: mt2701-afe-pcm: add HDMI audio path clocks
From: Daniel Golle @ 2026-04-16 11:41 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Cyril Chao, Arnd Bergmann,
Kuninori Morimoto, Nícolas F. R. A. Prado, Eugen Hristev,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20260416-qualified-violet-salmon-4bec7e@quoll>
On Thu, Apr 16, 2026 at 11:38:24AM +0200, Krzysztof Kozlowski wrote:
> On Wed, Apr 15, 2026 at 04:23:27PM +0100, Daniel Golle wrote:
> > Document four additional optional clocks feeding the HDMI audio
> > output path on MT2701 and MT7623N: the HADDS2 PLL (root of the
>
> There is no MT7623N compatible in this file, so that's confusing. Does
> mt7622 have it? If not, then it should be restricted per variant. If
> yet, the model name is confusing.
Only MT7623N (which is apparently identical with MT2701) has all the
multimedia features (ie. HDMI, a Mali-450 GPU, ...). Neither MT7623A
nor any of the other MediaTek router SoCs got any of that. So it
should be restricted to that variant. I'll fix this in v2.
Thanks for the review!
Daniel
^ permalink raw reply
* [PATCH] spi: mtk-snfi: fix memory leak in probe
From: Felix Gu @ 2026-04-16 11:46 UTC (permalink / raw)
To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
Xiangsheng Hou
Cc: linux-spi, linux-kernel, linux-arm-kernel, linux-mediatek,
Felix Gu
ms->buf is allocated in mtk_snand_setup_pagefmt() but was not freed on
the following error paths.
Fixes: 2b1e19811a8e ("spi: mtk-snfi: Change default page format to setup default setting")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
drivers/spi/spi-mtk-snfi.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c
index 73fa84475f0e..a728cf571b78 100644
--- a/drivers/spi/spi-mtk-snfi.c
+++ b/drivers/spi/spi-mtk-snfi.c
@@ -1447,14 +1447,14 @@ static int mtk_snand_probe(struct platform_device *pdev)
ret = nand_ecc_register_on_host_hw_engine(&ms->ecc_eng);
if (ret) {
dev_err(&pdev->dev, "failed to register ecc engine.\n");
- goto release_ecc;
+ goto free_buf;
}
ret = devm_add_action_or_reset(&pdev->dev, mtk_unregister_ecc_engine,
&ms->ecc_eng);
if (ret) {
dev_err_probe(&pdev->dev, ret, "failed to add ECC unregister action\n");
- goto release_ecc;
+ goto free_buf;
}
ctlr->num_chipselect = 1;
@@ -1469,6 +1469,8 @@ static int mtk_snand_probe(struct platform_device *pdev)
}
return 0;
+free_buf:
+ kfree(ms->buf);
release_ecc:
mtk_ecc_release(ms->ecc);
return ret;
---
base-commit: 936c21068d7ade00325e40d82bfd2f3f29d9f659
change-id: 20260415-mtk-snfi-651a984002ba
Best regards,
--
Felix Gu <ustc.gu@gmail.com>
^ permalink raw reply related
* Re: [PATCH 0/3] mm: split the file's i_mmap tree for NUMA
From: Huang Shijie @ 2026-04-16 11:48 UTC (permalink / raw)
To: Mateusz Guzik
Cc: akpm, viro, brauner, linux-mm, linux-kernel, linux-arm-kernel,
linux-fsdevel, muchun.song, osalvador, linux-trace-kernel,
linux-perf-users, linux-parisc, nvdimm, zhongyuan, fangbaoshun,
yingzhiwei
In-Reply-To: <CAGudoHGLaoc+CoBPNCvFRYojnj+6E_Lsdv7NaJWxFMoHezemMQ@mail.gmail.com>
On Thu, Apr 16, 2026 at 12:29:50PM +0200, Mateusz Guzik wrote:
> On Tue, Apr 14, 2026 at 11:11 AM Huang Shijie <huangsj@hygon.cn> wrote:
> >
> > On Mon, Apr 13, 2026 at 05:33:21PM +0200, Mateusz Guzik wrote:
> > > On Mon, Apr 13, 2026 at 02:20:39PM +0800, Huang Shijie wrote:
> > > > In NUMA, there are maybe many NUMA nodes and many CPUs.
> > > > For example, a Hygon's server has 12 NUMA nodes, and 384 CPUs.
> > > > In the UnixBench tests, there is a test "execl" which tests
> > > > the execve system call.
> > > >
> > > > When we test our server with "./Run -c 384 execl",
> > > > the test result is not good enough. The i_mmap locks contended heavily on
> > > > "libc.so" and "ld.so". For example, the i_mmap tree for "libc.so" can have
> > > > over 6000 VMAs, all the VMAs can be in different NUMA mode.
> > > > The insert/remove operations do not run quickly enough.
> > > >
> > > > patch 1 & patch 2 are try to hide the direct access of i_mmap.
> > > > patch 3 splits the i_mmap into sibling trees, and we can get better
> > > > performance with this patch set:
> > > > we can get 77% performance improvement(10 times average)
> > > >
> > >
> > > To my reading you kept the lock as-is and only distributed the protected
> > > state.
> > >
> > > While I don't doubt the improvement, I'm confident should you take a
> > > look at the profile you are going to find this still does not scale with
> > > rwsem being one of the problems (there are other global locks, some of
> > > which have experimental patches for).
> > IMHO, when the number of VMAs in the i_mmap is very large, only optimise the rwsem
> > lock does not help too much for our NUMA case.
> >
> > In our NUMA server, the remote access could be the major issue.
> >
>
> I'm confused how this is not supposed to help. You moved your data to
> be stored per-domain. With my proposal the lock itself will also get
> that treatment.
>
> Modulo the issue of what to do with code wanting to iterate the entire
> thing, this is blatantly faster.
>
I tested an old lock patch yesterday. It really helps a lot.
The lock patch is from this link:
https://lkml.org/lkml/2024/9/14/280
The test results:
v7.0-rc5 + (lock patch) : improve about %60%
v7.0-rc5 + (lock patch) + (this patch set) : improve about 130%
> >
> > >
> > > Apart from that this does nothing to help high core systems which are
> > > all one node, which imo puts another question mark on this specific
> > > proposal.
> > Yes, this patch set only focus on the NUMA case.
> > The one-node case should use the original i_mmap.
> >
> > Maybe I can add a new config, CONFIG_SPILT_I_MMAP. The config is disabled
> > by default, and enabled when the NUMA node is not one.
> >
> > >
> > > Of course one may question whether a RB tree is the right choice here,
> > > it may be the lock-protected cost can go way down with merely a better
> > > data structure.
> > >
> > > Regardless of that, for actual scalability, there will be no way around
> > > decentralazing locking around this and partitioning per some core count
> > > (not just by numa awareness).
> > >
> > > Decentralizing locking is definitely possible, but I have not looked
> > > into specifics of how problematic it is. Best case scenario it will
> > > merely with separate locks. Worst case scenario something needs a fully
> > > stabilized state for traversal, in that case another rw lock can be
> > Yes.
> >
> > The traversal may need to hold many locks.
> >
>
> The very paragraph you partially quoted answers what to do in that
> case: wrap everything with a new rwsem taken for reading when
> adding/removing entries and taken for writing when iterating the
> entire thing. Then the iteration sticks to one lock.
>
> The new rw lock puts an upper ceiling on scalability of the thing, but
> it is way higher than the current state.
Could you tell me the patch about it?
Is this lock patch merged ? or not?
I can test it.
>
> Given the extra overhead associated with it one could consider
> sticking to one centralized state by default and switching to
> distributed state if there is enough contention.
>
> > > slapped around this, creating locking order read lock -> per-subset
> > > write lock -- this will suffer scalability due to the read locking, but
> > > it will still scale drastically better as apart from that there will be
> > > no serialization. In this setting the problematic consumer will write
> > > lock the new thing to stabilize the state.
> > >
> > > So my non-maintainer opinion is that the patchset is not worth it as it
> > > fails to address anything for significantly more common and already
> > > affected setups.
> > This patch set is to reduce the remote access latency for insert/remove VMA
> > in NUMA.
> >
>
> And I am saying the mmap semaphore is a significant problem already on
> high-core no-numa setups. Addressing scalability in that case would
> sort out the problem in your setup and to a significantly higher
> extent.
I am afraid even the lock patch resolves the scalability high-core no-numa setups,
we still need to split the i_mmap for NUMA.
>
> > >
> > > Have you looked into splitting the lock?
> > >
> > I ever tried.
> >
> > But there are two disadvantages:
> > 1.) The traversal may need to hold many locks which makes the
> > code very horrible.
> >
>
> I already above this is avoidable.
>
> > 2.) Even we split the locks. Each lock protects a tree, when the tree becomes
> > big enough, the VMA insert/remove will also become slow in NUMA.
> > The reason is that the tree has VMAs in different NUMA nodes.
> >
>
> This is orthogonal to my proposal. In fact, if one is to pretend this
> is never a factor with your patch, I would like to point out it will
> remain not a factor if the per-numa struct gets its own lock.
Yes. It is orthogonal to your proposal.
Thanks
Huang Shijie
^ permalink raw reply
* Re: [PATCH net-next 5/6] net: stmmac: move PHY handling out of __stmmac_open()/release()
From: Alexander Stein @ 2026-04-16 12:02 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Alexandre Torgue, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, linux-arm-kernel,
linux-stm32, Maxime Coquelin, netdev, Paolo Abeni
In-Reply-To: <aeC-tc2CooYDoBok@shell.armlinux.org.uk>
Hi Russel,
Am Donnerstag, 16. April 2026, 12:49:25 CEST schrieb Russell King (Oracle):
> On Thu, Apr 16, 2026 at 08:20:13AM +0200, Alexander Stein wrote:
> > Am Mittwoch, 15. April 2026, 14:59:32 CEST schrieb Russell King (Oracle):
> > > On Wed, Apr 15, 2026 at 08:08:40AM +0200, Alexander Stein wrote:
> > > > Hi,
> > > >
> > > > Am Dienstag, 23. September 2025, 13:26:19 CEST schrieb Russell King (Oracle):
> > > > > Move the PHY attachment/detachment from the network driver out of
> > > > > __stmmac_open() and __stmmac_release() into stmmac_open() and
> > > > > stmmac_release() where these actions will only happen when the
> > > > > interface is administratively brought up or down. It does not make
> > > > > sense to detach and re-attach the PHY during a change of MTU.
> > > >
> > > > Sorry for coming up now. But I recently noticed this commit breaks changing
> > > > the MTU on i.MX8MP. Once I simply change the MTU I run into some DMA error:
> > > > $ ip link set dev end1 mtu 1400
> > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-0
> > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-1
> > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-2
> > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-3
> > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-4
> > > > imx-dwmac 30bf0000.ethernet end1: Link is Down
> > > > imx-dwmac 30bf0000.ethernet end1: Failed to reset the dma
> > > > imx-dwmac 30bf0000.ethernet end1: stmmac_hw_setup: DMA engine initialization failed
> > >
> > > This basically means that a clock is missing. Please provide more
> > > information:
> > >
> > > - what kernel version are you using?
> >
> > Currently I am using v6.18.22.
> > $ ethtool -i end1
> > driver: st_gmac
> > version: 6.18.22
> > firmware-version:
> > expansion-rom-version:
> > bus-info: 30bf0000.ethernet
> > supports-statistics: yes
> > supports-test: no
> > supports-eeprom-access: no
> > supports-register-dump: yes
> > supports-priv-flags: no
> >
> > > - has EEE been negotiated?
> >
> > No. It is marked as not supported
> >
> > $ ethtool --show-eee end1
> > EEE settings for end1:
> > EEE status: not supported
> >
> > > - does the problem persist when EEE is disabled?
> >
> > As EEE is not supported the problem occurs even with EEE disabled.
> >
> > > - which PHY is attached to stmmac?
> >
> > It is a TI DP83867.
> >
> > imx-dwmac 30bf0000.ethernet eth1: PHY [stmmac-1:03] driver [TI DP83867] (irq=136)
> >
> > > - which PHY interface mode is being used to connect the PHY to stmmac?
> >
> > For this interface
> > > phy-mode = "rgmii-id";
> > is set.
> >
> > In case it is helpful. My platform is arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts
> > Thanks for assisting. If there a further questions, don't hesitate to ask.
>
> Thanks.
>
> So, as best I can determine at the moment, we end up with the following
> sequence:
>
> stmmac_change_mtu()
> __stmmac_release()
> phylink_stop()
> phy_stop()
> phy->state = PHY_HALTED
> _phy_state_machine() returns PHY_STATE_WORK_SUSPEND
> _phy_state_machine_post_work()
> phy_suspend()
> genphy_suspend()
> phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN)
>
> With the DP83867, this causes most of the PHY to be powered down, thus
> stopping the clocks, and this causes the stmmac reset to time out.
>
> Prior to this commit, we would have called phylink_disconnect_phy()
> immediately after phylink_stop(), but I can see nothing that would
> be affected by this change there (since that also calls
> phy_suspend(), but as the PHY is already suspended, this becomes a
> no-op.)
>
> However, __stmmac_open() would have called stmmac_init_phy(), which
> would reattach the PHY. This would have called phy_init_hw(),
> resetting the PHY, and phy_resume() which would ensure that the
> PDOWN bit is clear - thus clocks would be running.
>
> As a hack, please can you try calling phylink_prepare_resume()
> between the __stmmac_release() and __stmmac_open() in
> stmmac_change_mtu(). This should resume the PHY, thus restoring the
> clocks necessary for stmmac to reset.
I tried the following patch. This works as you suspected.
Thanks and best regards
Alexander
---8<---
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -5875,6 +5875,8 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
__stmmac_release(dev);
+ phylink_prepare_resume(priv->phylink);
+
ret = __stmmac_open(dev, dma_conf);
if (ret) {
free_dma_desc_resources(priv, dma_conf);
---8<---
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
http://www.tq-group.com/
^ permalink raw reply
* Re: [PATCH v2 1/3] dt-bindings: power: Add power-domains-child-ids property
From: Rob Herring (Arm) @ 2026-04-16 12:03 UTC (permalink / raw)
To: Kevin Hilman (TI)
Cc: devicetree, Ulf Hansson, linux-arm-kernel, linux-pm, linux-kernel,
arm-scmi, Geert Uytterhoeven
In-Reply-To: <20260410-topic-lpm-pmdomain-child-ids-v2-1-83396e4b5f8b@baylibre.com>
On Fri, 10 Apr 2026 16:44:36 -0700, Kevin Hilman (TI) wrote:
> Add binding documentation for the new power-domains-child-ids property,
> which works in conjunction with the existing power-domains property to
> establish parent-child relationships between a multi-domain power domain
> provider and external parent domains.
>
> Each element in the uint32 array identifies the child domain
> ID (index) within the provider that should be made a child domain of
> the corresponding phandle entry in power-domains. The two arrays must
> have the same number of elements.
>
> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
> ---
> Documentation/devicetree/bindings/power/power-domain.yaml | 34 ++++++++++++++++++++++++++++++++++
> 1 file changed, 34 insertions(+)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH 1/5] dt-bindings: interrupt-controller: Add support for secure donated SGIs
From: Rob Herring @ 2026-04-16 12:11 UTC (permalink / raw)
To: Sudeep Holla
Cc: Krzysztof Kozlowski, Conor Dooley, Marc Zyngier, devicetree,
linux-kernel, linux-arm-kernel
In-Reply-To: <20260412-b4-ffa_ns_sgi_gicv3-v1-1-af61243eb405@kernel.org>
On Sun, Apr 12, 2026 at 06:04:37PM +0100, Sudeep Holla wrote:
> In GICv3, SGI security is defined by interrupt grouping and configuration
> rather than by SGI number alone. Linux conventionally reserves SGIs 0-7
> for non-secure internal kernel IPIs, while higher SGIs is assumed to be
> owned/stolen by the Secure world unless explicitly made available.
>
> Document secure donated SGI interrupt specifiers for the GICv3 binding.
> It describes "arm,secure-donated-ns-sgi-ranges" for SGIs donated by the
> secure world to non-secure software. It excludes SGIs 0-7, which are
> already used by the kernel for internal IPI purposes.
>
> Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
> ---
> .../bindings/interrupt-controller/arm,gic-v3.yaml | 27 +++++++++++++++++++++-
> include/dt-bindings/interrupt-controller/arm-gic.h | 1 +
> 2 files changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
> index bfd30aae682b..664727d071c9 100644
> --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
> +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
> @@ -45,17 +45,24 @@ description: |
>
> The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
> interrupts, 2 for interrupts in the Extended SPI range, 3 for the
> - Extended PPI range. Other values are reserved for future use.
> + Extended PPI range, and 4 for SGI interrupts. Other values are
> + reserved for future use.
>
> The 2nd cell contains the interrupt number for the interrupt type.
> SPI interrupts are in the range [0-987]. PPI interrupts are in the
> range [0-15]. Extended SPI interrupts are in the range [0-1023].
> Extended PPI interrupts are in the range [0-127].
>
> + SGI interrupts are in the range [8-15] which overlaps with the SGIs
> + assigned to/reserved for the secure world but donated to the non
> + secure world to use. Refer "arm,secure-donated-ns-sgi-ranges" for
> + more details.
> +
> The 3rd cell is the flags, encoded as follows:
> bits[3:0] trigger type and level flags.
> 1 = edge triggered
> 4 = level triggered
> + SGIs are edge triggered and must be described as such.
>
> The 4th cell is a phandle to a node describing a set of CPUs this
> interrupt is affine to. The interrupt must be a PPI, and the node
> @@ -136,6 +143,24 @@ description: |
> - $ref: /schemas/types.yaml#/definitions/uint32
> - $ref: /schemas/types.yaml#/definitions/uint64
>
> + arm,secure-donated-ns-sgi-ranges:
> + description:
> + A list of pairs <sgi span>, where "sgi" is the first SGI INTID of a
> + range donated by the secure side to non-secure software, and "span" is
> + the size of that range. Multiple ranges can be provided.
> +
> + SGIs described by interrupt specifiers with type 4 (SGI) must fall
> + within one of these ranges. SGIs(0-7) reserved by non-secure world
> + for internal IPIs must not be listed here. "sgi" must be in the
> + range [8-15], "span" must be in the range [1-8], and the range must
> + not extend past SGI 15.
> + $ref: /schemas/types.yaml#/definitions/uint32-matrix
> + items:
As a matrix, you need:
items:
items:
- ...
- ...
However, given this is at most 8 entries, I would just do an array:
minItems: 1
maxItems: 8
uniqueItems: true
items:
minimum: 8
maximum: 15
Unless we need more flexibility in GICv5?
Is there an example we can stick this property into so it gets tested?
> + - minimum: 8
> + maximum: 15
> + - minimum: 1
> + maximum: 8
> +
> ppi-partitions:
> type: object
> additionalProperties: false
> diff --git a/include/dt-bindings/interrupt-controller/arm-gic.h b/include/dt-bindings/interrupt-controller/arm-gic.h
> index 887f53363e8a..52c2f3f090c5 100644
> --- a/include/dt-bindings/interrupt-controller/arm-gic.h
> +++ b/include/dt-bindings/interrupt-controller/arm-gic.h
> @@ -14,6 +14,7 @@
> #define GIC_PPI 1
> #define GIC_ESPI 2
> #define GIC_EPPI 3
> +#define GIC_SGI 4
>
> /*
> * Interrupt specifier cell 2.
>
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH net-next 5/6] net: stmmac: move PHY handling out of __stmmac_open()/release()
From: Russell King (Oracle) @ 2026-04-16 12:13 UTC (permalink / raw)
To: Alexander Stein
Cc: Andrew Lunn, Heiner Kallweit, Alexandre Torgue, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, linux-arm-kernel,
linux-stm32, Maxime Coquelin, netdev, Paolo Abeni
In-Reply-To: <2410317.ElGaqSPkdT@steina-w>
On Thu, Apr 16, 2026 at 02:02:53PM +0200, Alexander Stein wrote:
> Hi Russel,
>
> Am Donnerstag, 16. April 2026, 12:49:25 CEST schrieb Russell King (Oracle):
> > On Thu, Apr 16, 2026 at 08:20:13AM +0200, Alexander Stein wrote:
> > > Am Mittwoch, 15. April 2026, 14:59:32 CEST schrieb Russell King (Oracle):
> > > > On Wed, Apr 15, 2026 at 08:08:40AM +0200, Alexander Stein wrote:
> > > > > Hi,
> > > > >
> > > > > Am Dienstag, 23. September 2025, 13:26:19 CEST schrieb Russell King (Oracle):
> > > > > > Move the PHY attachment/detachment from the network driver out of
> > > > > > __stmmac_open() and __stmmac_release() into stmmac_open() and
> > > > > > stmmac_release() where these actions will only happen when the
> > > > > > interface is administratively brought up or down. It does not make
> > > > > > sense to detach and re-attach the PHY during a change of MTU.
> > > > >
> > > > > Sorry for coming up now. But I recently noticed this commit breaks changing
> > > > > the MTU on i.MX8MP. Once I simply change the MTU I run into some DMA error:
> > > > > $ ip link set dev end1 mtu 1400
> > > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-0
> > > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-1
> > > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-2
> > > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-3
> > > > > imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-4
> > > > > imx-dwmac 30bf0000.ethernet end1: Link is Down
> > > > > imx-dwmac 30bf0000.ethernet end1: Failed to reset the dma
> > > > > imx-dwmac 30bf0000.ethernet end1: stmmac_hw_setup: DMA engine initialization failed
> > > >
> > > > This basically means that a clock is missing. Please provide more
> > > > information:
> > > >
> > > > - what kernel version are you using?
> > >
> > > Currently I am using v6.18.22.
> > > $ ethtool -i end1
> > > driver: st_gmac
> > > version: 6.18.22
> > > firmware-version:
> > > expansion-rom-version:
> > > bus-info: 30bf0000.ethernet
> > > supports-statistics: yes
> > > supports-test: no
> > > supports-eeprom-access: no
> > > supports-register-dump: yes
> > > supports-priv-flags: no
> > >
> > > > - has EEE been negotiated?
> > >
> > > No. It is marked as not supported
> > >
> > > $ ethtool --show-eee end1
> > > EEE settings for end1:
> > > EEE status: not supported
> > >
> > > > - does the problem persist when EEE is disabled?
> > >
> > > As EEE is not supported the problem occurs even with EEE disabled.
> > >
> > > > - which PHY is attached to stmmac?
> > >
> > > It is a TI DP83867.
> > >
> > > imx-dwmac 30bf0000.ethernet eth1: PHY [stmmac-1:03] driver [TI DP83867] (irq=136)
> > >
> > > > - which PHY interface mode is being used to connect the PHY to stmmac?
> > >
> > > For this interface
> > > > phy-mode = "rgmii-id";
> > > is set.
> > >
> > > In case it is helpful. My platform is arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts
> > > Thanks for assisting. If there a further questions, don't hesitate to ask.
> >
> > Thanks.
> >
> > So, as best I can determine at the moment, we end up with the following
> > sequence:
> >
> > stmmac_change_mtu()
> > __stmmac_release()
> > phylink_stop()
> > phy_stop()
> > phy->state = PHY_HALTED
> > _phy_state_machine() returns PHY_STATE_WORK_SUSPEND
> > _phy_state_machine_post_work()
> > phy_suspend()
> > genphy_suspend()
> > phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN)
> >
> > With the DP83867, this causes most of the PHY to be powered down, thus
> > stopping the clocks, and this causes the stmmac reset to time out.
> >
> > Prior to this commit, we would have called phylink_disconnect_phy()
> > immediately after phylink_stop(), but I can see nothing that would
> > be affected by this change there (since that also calls
> > phy_suspend(), but as the PHY is already suspended, this becomes a
> > no-op.)
> >
> > However, __stmmac_open() would have called stmmac_init_phy(), which
> > would reattach the PHY. This would have called phy_init_hw(),
> > resetting the PHY, and phy_resume() which would ensure that the
> > PDOWN bit is clear - thus clocks would be running.
> >
> > As a hack, please can you try calling phylink_prepare_resume()
> > between the __stmmac_release() and __stmmac_open() in
> > stmmac_change_mtu(). This should resume the PHY, thus restoring the
> > clocks necessary for stmmac to reset.
>
> I tried the following patch. This works as you suspected.
Brilliant, thanks for proving the theory why it broke.
I'll have a think about the best way to solve this, because
phylink_prepare_resume() is supposed to be paired with phylink_resume()
and that isn't the case here.
Please bear with me as my availability for looking at the kernel is
very unpredictable at present (family health issues.)
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH v2] raid6: arm64: add SVE optimized implementation for syndrome generation
From: Demian Shulhan @ 2026-04-16 12:40 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Ard Biesheuvel, Mark Rutland, Song Liu, Yu Kuai, Will Deacon,
Catalin Marinas, Mark Brown, linux-arm-kernel, robin.murphy,
Li Nan, linux-raid, linux-kernel
In-Reply-To: <CAOLeWCtnZz=kHGk4C2f9Mbfi3tEBE100iCkMBhcG47dmR9eDWw@mail.gmail.com>
Hi all,
Sorry for the delay. The tests became more complex than I initially
thought, so I needed to gather more data and properly validate the
results across different hardware configurations.
Firstly, I want to clarify the results from my March 29 tests. I found
a flaw in my initial custom benchmark. The massive 2x throughput gap on
24 disks wasn't solely due to SVE's superiority, but rather a severe L1
D-Cache thrashing issue that disproportionately penalized NEON.
My custom test lacked memset() initialization, causing all data buffers
to map to the Linux Zero Page (Virtually Indexed, Physically Tagged
cache aliasing). Furthermore, even with memset(), allocating contiguous
page-aligned buffers can causes severe Cache Address Sharing (a known
issue that Andrea Mazzoleni solved in SnapRAID 13 years ago using
RAID_MALLOC_DISPLACEMENT).
Because SVE (svex4) uses 256-bit registers on Neoverse-V1, it performs
exactly half the number of memory load instructions compared to 128-bit
NEON. This dramatically reduced the L1 cache alias thrashing, allowing
SVE to survive the memory bottleneck while NEON choked:
Custom test without memset (4kb block):
| algo=neonx4 ndisks=24 iterations=1M time=11.014s MB/s=7802.57
| algo=svex4 ndisks=24 iterations=1M time=5.719s MB/s=15026.92
Custom test with memset (4kb block):
| algo=neonx4 ndisks=24 iterations=1M time=6.165s MB/s=13939.08
| algo=svex4 ndisks=24 iterations=1M time=5.839s MB/s=14718.23
Even with the corrected memory setup, the throughput gap narrowed, but
the fundamental CPU-efficiency result remained fully intact.
To completely isolate these variables and provide accurate real-world
data, the following test campaigns were done based on the SnapRAID
project (https://github.com/amadvance/snapraid) using its
perf_bench.c tool with proper memory displacement and a 256 KiB block
size.
Test configurations:
- c7g.medium (AWS Graviton3, 1 vCPU): Neoverse-V1, 256-bit SVE
- c7g.xlarge (AWS Graviton3, 4 vCPUs): Neoverse-V1, 256-bit SVE
- c8g.xlarge (AWS Graviton4, 4 vCPUs): Neoverse-V2, 128-bit SVE
=========================================================
Section 1: SnapRAID Validation on Graviton3 / Neoverse-V1
=========================================================
These runs are the most representative userspace validation. The tests
were run with standard -O2 optimizations.
1.1 SnapRAID speedtest, O2, c7g.xlarge (Raw Throughput)
disks neonx4 neonx8 svex4 delta(nx4) delta(nx8)
----- ------ ------ ------ ---------- ----------
8 21394 21138 23601 +10.3% +11.6%
24 20368 19850 21009 +3.1% +5.8%
48 16727 19290 20222 +20.9% +4.8%
96 15562 18925 17549 +12.8% -7.3%
1.2 perf_bench, O2, c7g.xlarge (Hardware Efficiency)
disks neonx4 inst svex4 inst reduction | neonx4 cyc svex4 cyc | MB/s (N/S)
----- ----------- ---------- --------- | ---------- --------- | -----------
8 4.02 B 2.61 B -35.1% | 1.01 B 0.92 B | 20304/22346
24 12.16 B 8.00 B -34.2% | 3.20 B 3.11 B | 19354/19933
48 24.37 B 16.08 B -34.0% | 7.73 B 6.51 B | 16048/19047
96 48.80 B 32.24 B -33.9% | 16.94 B 15.11 B | 14638/16421
1.3 Main Graviton3 Conclusions
- On 256-bit SVE hardware, svex4 consistently retires about ~34% fewer
instructions and ~10-15% fewer CPU cycles than neonx4.
=========================================================
Section 2: SnapRAID Validation on Graviton4 / Neoverse-V2
=========================================================
2.1 SnapRAID speedtest, O2, c8g.xlarge (Raw Throughput)
disks neonx4 neonx8 svex4 delta(nx4) delta(nx8)
----- ------ ------ ------ ---------- ----------
8 24802 25409 20451 -17.5% -19.5%
24 22607 24026 18577 -17.8% -22.7%
48 20984 22171 18019 -14.1% -18.7%
96 21254 21690 17108 -19.5% -21.1%
2.2 perf_bench, O2, c8g.xlarge (Hardware Efficiency)
disks neonx4 inst svex4 inst overhead | neonx4 cyc svex4 cyc | MB/s (N/S)
----- ----------- ---------- --------- | ---------- --------- | -----------
8 4.02 B 5.22 B +29.9% | 0.95 B 1.14 B | 23529/19512
24 12.16 B 15.98 B +31.4% | 3.11 B 3.79 B | 21621/17777
48 24.37 B 32.12 B +31.8% | 6.70 B 7.81 B | 20000/17204
96 48.78 B 64.40 B +32.0% | 13.24 B 16.32 B | 20253/16410
2.3 Main Graviton4 Conclusions
- On Neoverse-V2, SVE vector length is 128-bit (same as NEON).
- Without the 256-bit width, NEON outperforms SVE.
- svex4 retires ~32% MORE instructions here and is consistently slower.
=========================================================
Section 3: Validation on c7g.medium (1 vCPU)
=========================================================
3.1 SnapRAID speedtest, O2, c7g.medium (Raw Throughput)
disks neonx4 neonx8 svex4 delta(nx4) delta(nx8)
----- ------ ------ ------ ---------- ----------
8 16768 17466 17310 +3.2% -0.9%
24 15843 16684 16205 +2.3% -2.9%
48 14032 14475 15389 +9.7% +6.3%
96 13404 13045 14677 +9.5% +12.5%
3.2 perf_bench, O2, c7g.medium (Hardware Efficiency)
disks neonx4 inst svex4 inst reduction | neonx4 cyc svex4 cyc | MB/s (N/S)
----- ----------- ---------- --------- | ---------- --------- | -----------
8 3.99 B 2.61 B -34.6% | 1.30 B 1.25 B | 16000/16666
24 12.13 B 8.00 B -34.0% | 4.08 B 4.02 B | 15189/15483
48 24.34 B 16.08 B -33.9% | 9.23 B 8.35 B | 13445/14860
96 48.76 B 32.24 B -33.9% | 19.34 B 17.92 B | 12834/13852
3.3 Main c7g.medium Conclusions
- The instruction count reduction (~34%) perfectly matches the 4-vCPU
instance.
- The single vCPU is heavily memory-bandwidth constrained (cycle counts
are much higher waiting for RAM).
=========================================================
Section 4: The Pitfalls of the Current Kernel Benchmark
=========================================================
As Christoph pointed out, the current in-kernel benchmark setup
(hardcoded to 8 disks and a PAGE_SIZE buffer) can be not representative
of real-life arrays.
Because 8 disks * 4 KiB = 32 KiB total data, the entire benchmark fits
into the 64 KiB L1 D-Cache of Neoverse-V1, masking memory bandwidth limits
and register spilling. This leads to objectively wrong selections.
---------------------------------------------------
Case 1: Wrong NEON unrolling selection (Graviton3)
--------------------------------------------------
The kernel benchmark tests 8 disks and locks in neonx4. However, on
real-world wide arrays (48-96 disks), neonx8 is significantly faster.
disks neonx4 MB/s neonx8 MB/s Actual Winner Kernel's Choice
-------- ------------- ------------- ------------- ---------------
8 (Boot) 21,394 21,138 neonx4 neonx4 (Locked)
48 16,727 19,290 neonx8 neonx4 (-15.3%)
96 15,562 18,925 neonx8 neonx4 (-21.6%)
Result: Users lose up to 21% NEON throughput because of the 8-disk test.
---------------------------------------------------
Case 2: Wrong SVE vs NEON selection (Graviton3)
--------------------------------------------------
If SVE is enabled, the 8-disk benchmark strongly prefers svex4. But on
extreme wide arrays (96 disks), the heavily unrolled neonx8 actually
overtakes SVE.
disks neonx8 MB/s svex4 MB/s Actual Winner Kernel's Choice
-------- ------------- ------------- ------------- ---------------
8 (Boot) 21,138 23,601 svex4 svex4 (Locked)
96 18,925 17,549 neonx8 svex4 (-7.8%)
Result: On extreme workloads, forcing svex4 loses ~7.8% throughput.
Conclusion: The kernel benchmark requires testing with larger buffers
(exceeding L1 capacity) or simulated wide arrays to guarantee the optimal
algorithm is chosen for actual storage workloads.
---------------------------------------------------
Case 3: Buffer size distortion (Graviton3, 8 disks)
---------------------------------------------------
Even on the exact same 8-disk array, testing with a 4 KiB buffer (which
fits entirely in the L1 cache) yields a completely different winner than
testing with 256 KiB buffer (which exercises L2/L3/RAM).
buffer neonx4 MB/s svex4 MB/s Actual Winner Kernel's Choice
---------- ------------- ------------- ------------- ---------------
4 KiB 20211 19818 neonx4 neonx4 (Locked)
256 KiB 21394 23601 svex4 neonx4 (-9.3%)
Result: By benchmarking exclusively in the L1 cache (4 KiB buffer), the
kernel incorrectly chooses neonx4, losing ~9.3% throughput for
larger I/O block sizes.
Thanks again for your time and review!
вт, 31 бер. 2026 р. о 16:18 Demian Shulhan <demyansh@gmail.com> пише:
>
> Hi all,
>
> Ard, your questions regarding real-world I/O bottlenecks and SVE power
> efficiency versus raw throughput are entirely valid. I agree that
> introducing SVE support requires solid real-world data to justify the
> added complexity.
>
> Due to my current workload, I won't be able to run the necessary
> hardware tests and prepare the benchmark code immediately. I will get
> back to the list in about 1 week with the requested source code,
> unmangled test results, and further analysis.
>
> Thanks!
>
>
> вт, 31 бер. 2026 р. о 09:37 Christoph Hellwig <hch@lst.de> пише:
> >
> > On Mon, Mar 30, 2026 at 06:39:49PM +0200, Ard Biesheuvel wrote:
> > > I think the results are impressive, but I'd like to better understand
> > > its implications on a real-world scenario. Is this code only a
> > > bottleneck when rebuilding an array?
> >
> > The syndrome generation is run every time you write data to a RAID6
> > array, and if you do partial stripe writes it (or rather the XOR
> > variant) is run twice. So this is the most performance critical
> > path for writing to RAID6.
> >
> > Rebuild usually runs totally different code, but can end up here as well
> > when both parity disks are lost.
> >
> > > > Furthermore, as Christoph suggested, I tested scalability on wider
> > > > arrays since the default kernel benchmark is hardcoded to 8 disks,
> > > > which doesn't give the unrolled SVE loop enough data to shine. On a
> > > > 16-disk array, svex4 hits 15.1 GB/s compared to 8.0 GB/s for neonx4.
> > > > On a 24-disk array, while neonx4 chokes and drops to 7.8 GB/s, svex4
> > > > maintains a stable 15.0 GB/s — effectively doubling the throughput.
> > >
> > > Does this mean the kernel benchmark is no longer fit for purpose? If
> > > it cannot distinguish between implementations that differ in performance
> > > by a factor of 2, I don't think we can rely on it to pick the optimal one.
> >
> > It is not good, and we should either fix it or run more than one.
> > The current setup is not really representative of real-life array.
> > It also leads to wrong selections on x86, but only at the which unroll
> > level to pick level, and only for minor differences so far. I plan
> > to add this to the next version of the raid6 lib patches.
> >
^ permalink raw reply
* Re: [PATCH] arm64: cpufeature: Fix GCIE field ordering in ftr_id_aa64pfr2
From: Mukesh Ojha @ 2026-04-16 12:42 UTC (permalink / raw)
To: Marc Zyngier; +Cc: Catalin Marinas, Will Deacon, linux-arm-kernel, linux-kernel
In-Reply-To: <868qan2ypl.wl-maz@kernel.org>
On Thu, Apr 16, 2026 at 07:42:14AM +0100, Marc Zyngier wrote:
> On Wed, 15 Apr 2026 21:00:31 +0100,
> Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> wrote:
> >
> > The ftr_id_aa64pfr2[] array must be sorted in descending order of
> > shift value so that the overlap validation in init_cpu_features()
> > works correctly. The GCIE field (bits 15:12, shift=12) was placed
> > last in the array, after MTEFAR (bits 11:8, shift=8) and
> > MTESTOREONLY (bits 7:4, shift=4), causing a spurious warning at
> > boot:
> >
> > [ 0.000000] SYS_ID_AA64PFR2_EL1 has feature overlap at shift 12
> > [ 0.000000] WARNING: arch/arm64/kernel/cpufeature.c:989 at init_cpu_features+0x144/0x3d0, CPU#0:
> > swapper/0
> > ..
> >
> > [ 0.000000] pc : init_cpu_features+0x144/0x3d0
> > [ 0.000000] lr : init_cpu_features+0x144/0x3d0
> > [ 0.000000] sp : ffffc08678f03dc0
> >
> > ...
> > 0.000000] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffffc08678f14000
> > [ 0.000000] Call trace:
> > [ 0.000000] init_cpu_features+0x144/0x3d0 (P)
> > [ 0.000000] cpuinfo_store_boot_cpu+0x4c/0x5c
> > [ 0.000000] smp_prepare_boot_cpu+0x28/0x38
> > [ 0.000000] start_kernel+0x1d4/0x848
> > [ 0.000000] __primary_switched+0x88/0x90
> >
> > This is because the overlap check computes (shift + width) > prev_shift,
> > i.e. (12 + 4) > 8, which triggers since GCIE occupies bits above MTEFAR
> > but was listed after it.
> >
> > Fix the ordering to match the register layout: FPMR(35:32), GCIE(15:12),
> > MTEFAR(11:8), MTESTOREONLY(7:4).
> >
> > Fixes: 899ff451fcee ("KVM: arm64: Advertise ID_AA64PFR2_EL1.GCIE")
> > Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
>
> This was present in next-20260403, identified the following day, a fix
> proposed in [1], and the fixed commit appeared in next-20260407 (which
> was the subsequent -next build) as 7e629348df81b.
>
> May I humbly suggest that you check with the latest -next branch
> before spending time on this sort of things? Two weeks is a pretty
> long time...
Sure, Thank you.
>
> Thanks,
>
> M.
>
> [1] https://lore.kernel.org/all/874ilqcu3c.wl-maz@kernel.org/
>
> --
> Without deviation from the norm, progress is not possible.
--
-Mukesh Ojha
^ permalink raw reply
* Re: [PATCH] spi: mtk-snfi: fix memory leak in probe
From: Mark Brown @ 2026-04-16 12:51 UTC (permalink / raw)
To: Felix Gu
Cc: Matthias Brugger, AngeloGioacchino Del Regno, Xiangsheng Hou,
linux-spi, linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260416-mtk-snfi-v1-1-fafccfd288f9@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 703 bytes --]
On Thu, Apr 16, 2026 at 07:46:20PM +0800, Felix Gu wrote:
> ms->buf is allocated in mtk_snand_setup_pagefmt() but was not freed on
> the following error paths.
> ret = nand_ecc_register_on_host_hw_engine(&ms->ecc_eng);
> if (ret) {
> dev_err(&pdev->dev, "failed to register ecc engine.\n");
> - goto release_ecc;
> + goto free_buf;
> }
>
> ret = devm_add_action_or_reset(&pdev->dev, mtk_unregister_ecc_engine,
> &ms->ecc_eng);
> if (ret) {
> dev_err_probe(&pdev->dev, ret, "failed to add ECC unregister action\n");
> - goto release_ecc;
> + goto free_buf;
> }
>
> ctlr->num_chipselect = 1;
What about spi_register_controller() failures?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH v2] spi: mtk-snfi: fix memory leak in probe
From: Felix Gu @ 2026-04-16 13:00 UTC (permalink / raw)
To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
Xiangsheng Hou
Cc: linux-spi, linux-kernel, linux-arm-kernel, linux-mediatek,
Felix Gu
ms->buf is allocated in mtk_snand_setup_pagefmt() but was not freed on
the following error paths.
Fixes: 2b1e19811a8e ("spi: mtk-snfi: Change default page format to setup default setting")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
Changes in v2:
- Fix Mark's comment.
- Link to v1: https://lore.kernel.org/r/20260416-mtk-snfi-v1-1-fafccfd288f9@gmail.com
---
drivers/spi/spi-mtk-snfi.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c
index 73fa84475f0e..e616e6800e92 100644
--- a/drivers/spi/spi-mtk-snfi.c
+++ b/drivers/spi/spi-mtk-snfi.c
@@ -1447,14 +1447,14 @@ static int mtk_snand_probe(struct platform_device *pdev)
ret = nand_ecc_register_on_host_hw_engine(&ms->ecc_eng);
if (ret) {
dev_err(&pdev->dev, "failed to register ecc engine.\n");
- goto release_ecc;
+ goto free_buf;
}
ret = devm_add_action_or_reset(&pdev->dev, mtk_unregister_ecc_engine,
&ms->ecc_eng);
if (ret) {
dev_err_probe(&pdev->dev, ret, "failed to add ECC unregister action\n");
- goto release_ecc;
+ goto free_buf;
}
ctlr->num_chipselect = 1;
@@ -1465,10 +1465,12 @@ static int mtk_snand_probe(struct platform_device *pdev)
ret = spi_register_controller(ctlr);
if (ret) {
dev_err(&pdev->dev, "spi_register_controller failed.\n");
- goto release_ecc;
+ goto free_buf;
}
return 0;
+free_buf:
+ kfree(ms->buf);
release_ecc:
mtk_ecc_release(ms->ecc);
return ret;
---
base-commit: 936c21068d7ade00325e40d82bfd2f3f29d9f659
change-id: 20260415-mtk-snfi-651a984002ba
Best regards,
--
Felix Gu <ustc.gu@gmail.com>
^ permalink raw reply related
* Re: [PATCH v2 1/2] thermal/drivers/imx: Fix thermal zone leak on probe error path
From: Felix Gu @ 2026-04-16 13:04 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Oleksij Rempel
Cc: linux-pm, imx, linux-arm-kernel, linux-kernel
In-Reply-To: <20260415-imx-v2-1-aeacff9e72b2@gmail.com>
On Wed, Apr 15, 2026 at 9:10 PM Felix Gu <ustc.gu@gmail.com> wrote:
>
> If pm_runtime_resume_and_get() fails after the thermal zone has been
> registered, the probe error path cleans up runtime PM but skips
> thermal_zone_device_unregister(), leaking the thermal zone device.
>
> Switch to use devm_thermal_of_zone_register() to fix the problem.
>
> Fixes: 4cf2ddf16e17 ("thermal/drivers/imx: Implement runtime PM support")
> Signed-off-by: Felix Gu <ustc.gu@gmail.com>
> ---
> drivers/thermal/imx_thermal.c | 35 +++++++++++++++++++----------------
> 1 file changed, 19 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
> index 38c993d1bcb3..3729c3eac748 100644
> --- a/drivers/thermal/imx_thermal.c
> +++ b/drivers/thermal/imx_thermal.c
> @@ -216,6 +216,20 @@ struct imx_thermal_data {
> const char *temp_grade;
> };
>
> +static int imx_thermal_sync_zone_trip(struct thermal_trip *trip, void *arg)
> +{
> + struct imx_thermal_data *data = arg;
> + int temp;
> +
> + if (trip->type != THERMAL_TRIP_PASSIVE && trip->type != THERMAL_TRIP_CRITICAL)
> + return 0;
> +
> + temp = trips[trip->type].temperature;
> + thermal_zone_set_trip_temp(data->tz, trip, temp);
> +
> + return 0;
> +}
> +
> static void imx_set_panic_temp(struct imx_thermal_data *data,
> int panic_temp)
> {
> @@ -679,13 +693,8 @@ static int imx_thermal_probe(struct platform_device *pdev)
> goto legacy_cleanup;
> }
>
> - data->tz = thermal_zone_device_register_with_trips("imx_thermal_zone",
> - trips,
> - ARRAY_SIZE(trips),
> - data,
> - &imx_tz_ops, NULL,
> - IMX_PASSIVE_DELAY,
> - IMX_POLLING_DELAY);
> + data->irq_enabled = true;
> + data->tz = devm_thermal_of_zone_register(dev, 0, data, &imx_tz_ops);
> if (IS_ERR(data->tz)) {
> ret = PTR_ERR(data->tz);
> dev_err(dev, "failed to register thermal zone device %d\n",
> @@ -693,6 +702,8 @@ static int imx_thermal_probe(struct platform_device *pdev)
> goto clk_disable;
> }
>
> + thermal_zone_for_each_trip(data->tz, imx_thermal_sync_zone_trip, data);
> +
> dev_info(dev, "%s CPU temperature grade - max:%dC"
> " critical:%dC passive:%dC\n", data->temp_grade,
> data->temp_max / 1000, trips[IMX_TRIP_CRITICAL].temperature / 1000,
> @@ -724,25 +735,18 @@ static int imx_thermal_probe(struct platform_device *pdev)
> if (ret < 0)
> goto disable_runtime_pm;
>
> - data->irq_enabled = true;
> - ret = thermal_zone_device_enable(data->tz);
> - if (ret)
> - goto thermal_zone_unregister;
> -
> ret = devm_request_threaded_irq(dev, data->irq,
> imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
> 0, "imx_thermal", data);
> if (ret < 0) {
> dev_err(dev, "failed to request alarm irq: %d\n", ret);
> - goto thermal_zone_unregister;
> + goto disable_runtime_pm;
> }
>
> pm_runtime_put(data->dev);
>
> return 0;
>
> -thermal_zone_unregister:
> - thermal_zone_device_unregister(data->tz);
> disable_runtime_pm:
> pm_runtime_put_noidle(data->dev);
> pm_runtime_disable(data->dev);
> @@ -761,7 +765,6 @@ static void imx_thermal_remove(struct platform_device *pdev)
> pm_runtime_put_noidle(data->dev);
> pm_runtime_disable(data->dev);
>
> - thermal_zone_device_unregister(data->tz);
> imx_thermal_unregister_legacy_cooling(data);
> }
>
>
> --
> 2.43.0
>
Hi all,
This patch has a problem, I will fix it and send v3.
Best regards,
Felix
^ permalink raw reply
* Re: [PATCH 1/5] dt-bindings: interrupt-controller: Add support for secure donated SGIs
From: Sudeep Holla @ 2026-04-16 13:09 UTC (permalink / raw)
To: Rob Herring
Cc: Krzysztof Kozlowski, Sudeep Holla, Conor Dooley, Marc Zyngier,
devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <20260416121146.GA2736962-robh@kernel.org>
On Thu, Apr 16, 2026 at 07:11:46AM -0500, Rob Herring wrote:
> On Sun, Apr 12, 2026 at 06:04:37PM +0100, Sudeep Holla wrote:
> > In GICv3, SGI security is defined by interrupt grouping and configuration
> > rather than by SGI number alone. Linux conventionally reserves SGIs 0-7
> > for non-secure internal kernel IPIs, while higher SGIs is assumed to be
> > owned/stolen by the Secure world unless explicitly made available.
> >
> > Document secure donated SGI interrupt specifiers for the GICv3 binding.
> > It describes "arm,secure-donated-ns-sgi-ranges" for SGIs donated by the
> > secure world to non-secure software. It excludes SGIs 0-7, which are
> > already used by the kernel for internal IPI purposes.
> >
> > Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
> > ---
> > .../bindings/interrupt-controller/arm,gic-v3.yaml | 27 +++++++++++++++++++++-
> > include/dt-bindings/interrupt-controller/arm-gic.h | 1 +
> > 2 files changed, 27 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
> > index bfd30aae682b..664727d071c9 100644
> > --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
> > @@ -45,17 +45,24 @@ description: |
> >
> > The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
> > interrupts, 2 for interrupts in the Extended SPI range, 3 for the
> > - Extended PPI range. Other values are reserved for future use.
> > + Extended PPI range, and 4 for SGI interrupts. Other values are
> > + reserved for future use.
> >
> > The 2nd cell contains the interrupt number for the interrupt type.
> > SPI interrupts are in the range [0-987]. PPI interrupts are in the
> > range [0-15]. Extended SPI interrupts are in the range [0-1023].
> > Extended PPI interrupts are in the range [0-127].
> >
> > + SGI interrupts are in the range [8-15] which overlaps with the SGIs
> > + assigned to/reserved for the secure world but donated to the non
> > + secure world to use. Refer "arm,secure-donated-ns-sgi-ranges" for
> > + more details.
> > +
> > The 3rd cell is the flags, encoded as follows:
> > bits[3:0] trigger type and level flags.
> > 1 = edge triggered
> > 4 = level triggered
> > + SGIs are edge triggered and must be described as such.
> >
> > The 4th cell is a phandle to a node describing a set of CPUs this
> > interrupt is affine to. The interrupt must be a PPI, and the node
> > @@ -136,6 +143,24 @@ description: |
> > - $ref: /schemas/types.yaml#/definitions/uint32
> > - $ref: /schemas/types.yaml#/definitions/uint64
> >
> > + arm,secure-donated-ns-sgi-ranges:
> > + description:
> > + A list of pairs <sgi span>, where "sgi" is the first SGI INTID of a
> > + range donated by the secure side to non-secure software, and "span" is
> > + the size of that range. Multiple ranges can be provided.
> > +
> > + SGIs described by interrupt specifiers with type 4 (SGI) must fall
> > + within one of these ranges. SGIs(0-7) reserved by non-secure world
> > + for internal IPIs must not be listed here. "sgi" must be in the
> > + range [8-15], "span" must be in the range [1-8], and the range must
> > + not extend past SGI 15.
> > + $ref: /schemas/types.yaml#/definitions/uint32-matrix
> > + items:
>
> As a matrix, you need:
>
> items:
> items:
> - ...
> - ...
>
> However, given this is at most 8 entries, I would just do an array:
>
> minItems: 1
> maxItems: 8
> uniqueItems: true
> items:
> minimum: 8
> maximum: 15
>
Makes sense.
> Unless we need more flexibility in GICv5?
>
IIUC, there are not SGIs in GICv5 and we may need to use one software PPI
as a replacement for SGIs. LPIs are used for IPIs.
I am assuming Marc will soon post his opinion/rejection on this series 😉
based on some offline discussion we had with respect to how it fits with
GICv5.
> Is there an example we can stick this property into so it gets tested?
>
Not sure if [1] serves as an example or you are looking for something else.
--
Regards,
Sudeep
[1] https://lore.kernel.org/all/20260412-b4-ffa_ns_sgi_gicv3-v1-5-af61243eb405@kernel.org/
^ permalink raw reply
* [PATCH v5 0/4] Introduce Allwinner H616 PWM controller
From: Richard Genoud @ 2026-04-16 13:14 UTC (permalink / raw)
To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Philipp Zabel
Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, Richard Genoud
Allwinner H616 PWM controller is quite different from the A10 one.
It can drive 6 PWM channels, and like for the A10, each channel has a
bypass that permits to output a clock, bypassing the PWM logic, when
enabled.
But, the channels are paired 2 by 2, sharing a first set of
MUX/prescaler/gate.
Then, for each channel, there's another prescaler (that will be bypassed
if the bypass is enabled for this channel).
It looks like that:
_____ ______ ________
OSC24M --->| | | | | |
APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> PWM_clock_src_xy
|_____| |______| |________|
________
| |
+->| /div_k |---> PWM_clock_x
| |________|
| ______
| | |
+-->| Gate |----> PWM_bypass_clock_x
| |______|
PWM_clock_src_xy -----+ ________
| | |
+->| /div_k |---> PWM_clock_y
| |________|
| ______
| | |
+-->| Gate |----> PWM_bypass_clock_y
|______|
Where xy can be 0/1, 2/3, 4/5
PWM_clock_x/y serve for the PWM purpose.
PWM_bypass_clock_x/y serve for the clock-provider purpose.
The common clock framework has been used to manage those clocks.
This PWM driver serves as a clock-provider for PWM_bypass_clocks.
This is needed for example by the embedded AC300 PHY which clock comes
from PMW5 pin (PB12).
Usually, to get a clock from a PWM driver, we use the pwm-clock driver
so that the PWM driver doesn't need to be a clk-provider itself.
While this works in most cases, here it just doesn't.
That's because the pwm-clock request a period from the PWM driver,
without any clue that it actually wants a clock at a specific frequency,
and not a PWM signal with duty cycle capability.
So, the PWM driver doesn't know if it can use the bypass or not, it
doesn't even have the real accurate frequency information (23809524 Hz
instead of 24MHz) because PWM drivers only deal with periods.
With pwm-clock, we loose a precious information along the way (that we
actually want a clock and not a PWM signal).
That's ok with simple PWM drivers that don't have multiple input clocks,
but in this case, without this information, we can't know for sure which
clock to use.
And here, for instance, if we ask for a 24MHz clock, pwm-clock will
requests 42ns (assigned-clocks doesn't help for that matter). The logic
is to select the highest clock (100MHz) with no prescaler and a duty
cycle value of 2/4 => we have 25MHz instead of 24MHz.
And that's a perfectly fine choice for a PMW, because we still can
change the duty cycle in the range [0-4]/4.
But obviously for a clock, we don't care about the duty cycle, but more
about the clock accuracy.
And actually, this PWM is really a PWM AND a real clock when the bypass
is set.
This series is based onto v7.0
NB: checkpatch is not happy with patch 2, but it's a false positive.
It doesn't detect that PWM_XY_SRC_MUX/GATE/DIV are structures, but as
it's more readable like that, I prefer keeping it that way.
Changes since v4:
- Fix a bug on bypass for channels greater than 1
- add colons to clarify 2 debug messages
- switch from H616 to sun8i prefix (in code, filename, module name)
- fix consistency issues in macro parameters
- rename some macros with a confusing naming
- rebase on v7.0
Changes since v3:
- gather Acked-by/Tested-by
- fix cast from pointer to integer of different size (kernel test robot
with arc platform)
- add devm_action for clk_hw_unregister_composite as suggested by Philipp
- remove now unused pwm_remove as suggested by Philipp
Changes since v2:
- use U32_MAX instead of defining UINT32_MAX
- add a comment on U32_MAX usage in clk_round_rate()
- change clk_table_div_m (use macros)
- fix formatting (double space, superfluous comma, extra line feed)
- fix the parent clock order
- simplify code by using scoped_guard()
- add missing const in to_h616_pwm_chip() and rename to
h616_pwm_from_chip()
- add/remove missing/superfluous error messages
- rename cnt->period_ticks, duty_cnt->duty_ticks
- fix PWM_PERIOD_MAX
- add .remove() callback
- fix DIV_ROUND_CLOSEST_ULL->DIV_ROUND_UP_ULL
- add H616_ prefix
- protect _reg in macros
- switch to waveforms instead of apply/get_state
- shrink struct h616_pwm_channel
- rebase on v6.19-rc4
Changes since v1:
- rebase onto v6.19-rc1
- add missing headers
- remove MODULE_ALIAS (suggested by Krzysztof)
- use sun4i-pwm binding instead of creating a new one (suggested by Krzysztof)
- retrieve the parent clocks from the devicetree
- switch num_parents to unsigned int
Richard Genoud (4):
dt-bindings: pwm: allwinner: add h616 pwm compatible
pwm: sun8i: Add H616 PWM support
arm64: dts: allwinner: h616: add PWM controller
MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver
.../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 19 +-
MAINTAINERS | 5 +
.../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 47 +
drivers/pwm/Kconfig | 12 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-sun8i.c | 938 ++++++++++++++++++
6 files changed, 1021 insertions(+), 1 deletion(-)
create mode 100644 drivers/pwm/pwm-sun8i.c
base-commit: 028ef9c96e96197026887c0f092424679298aae8
^ permalink raw reply
* [PATCH v5 4/4] MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver
From: Richard Genoud @ 2026-04-16 13:14 UTC (permalink / raw)
To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Philipp Zabel
Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, Richard Genoud
In-Reply-To: <20260416131419.3152419-1-richard.genoud@bootlin.com>
Add myself as maintainer of Allwinner SUN8I PWM driver.
Tested-by: John Stultz <jstultz@google.com>
Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
maintainer
---
MAINTAINERS | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f..256ab8be19f1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -912,6 +912,11 @@ S: Maintained
F: Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml
F: drivers/hwspinlock/sun6i_hwspinlock.c
+ALLWINNER SUN8I PWM DRIVER
+M: Richard Genoud <richard.genoud@bootlin.com>
+S: Maintained
+F: drivers/pwm/pwm-sun8i.c
+
ALLWINNER THERMAL DRIVER
M: Vasily Khoruzhick <anarsoul@gmail.com>
M: Yangtao Li <tiny.windzz@gmail.com>
^ permalink raw reply related
* [PATCH v5 3/4] arm64: dts: allwinner: h616: add PWM controller
From: Richard Genoud @ 2026-04-16 13:14 UTC (permalink / raw)
To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Philipp Zabel
Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, Richard Genoud
In-Reply-To: <20260416131419.3152419-1-richard.genoud@bootlin.com>
The H616 has a PWM controller that can provide PWM signals, but also
plain clocks.
Add the PWM controller node and pins in the device tree.
Tested-by: John Stultz <jstultz@google.com>
Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
.../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
index 8d1110c14bad..1c7628a6e4bb 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
@@ -236,6 +236,17 @@ watchdog: watchdog@30090a0 {
clocks = <&osc24M>;
};
+ pwm: pwm@300a000 {
+ compatible = "allwinner,sun50i-h616-pwm";
+ reg = <0x0300a000 0x400>;
+ clocks = <&osc24M>, <&ccu CLK_BUS_PWM>;
+ clock-names = "mod", "bus";
+ resets = <&ccu RST_BUS_PWM>;
+ #pwm-cells = <3>;
+ #clock-cells = <1>;
+ status = "disabled";
+ };
+
pio: pinctrl@300b000 {
compatible = "allwinner,sun50i-h616-pinctrl";
reg = <0x0300b000 0x400>;
@@ -340,6 +351,42 @@ nand_rb1_pin: nand-rb1-pin {
bias-pull-up;
};
+ /omit-if-no-ref/
+ pwm0_pin: pwm0-pin {
+ pins = "PD28";
+ function = "pwm0";
+ };
+
+ /omit-if-no-ref/
+ pwm1_pin: pwm1-pin {
+ pins = "PG19";
+ function = "pwm1";
+ };
+
+ /omit-if-no-ref/
+ pwm2_pin: pwm2-pin {
+ pins = "PH2";
+ function = "pwm2";
+ };
+
+ /omit-if-no-ref/
+ pwm3_pin: pwm3-pin {
+ pins = "PH0";
+ function = "pwm3";
+ };
+
+ /omit-if-no-ref/
+ pwm4_pin: pwm4-pin {
+ pins = "PI14";
+ function = "pwm4";
+ };
+
+ /omit-if-no-ref/
+ pwm5_pin: pwm5-pin {
+ pins = "PA12";
+ function = "pwm5";
+ };
+
/omit-if-no-ref/
spi0_pins: spi0-pins {
pins = "PC0", "PC2", "PC4";
^ permalink raw reply related
* [PATCH v5 2/4] pwm: sun8i: Add H616 PWM support
From: Richard Genoud @ 2026-04-16 13:14 UTC (permalink / raw)
To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Philipp Zabel
Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, Richard Genoud
In-Reply-To: <20260416131419.3152419-1-richard.genoud@bootlin.com>
Add driver for Allwinner H616 PWM controller, supporting up to 6
channels.
Those channels output can be either a PWM signal output or a clock
output, thanks to the bypass.
The channels are paired (0/1, 2/3 and 4/5) and each pair has a
prescaler/mux/gate.
Moreover, each channel has its own prescaler and bypass.
The clock provider part of this driver is needed not only because the
H616 PWM controller provides also clocks when bypass is enabled, but
really because pwm-clock isn't fit to handle all cases here.
pwm-clock would work if the 100MHz clock is requested, but if a lower
clock is requested (like 24MHz), it will request a 42ns period to the
PWM driver which will happily serve, with the 100MHz clock as input a
25MHz frequency and a duty cycle adjustable in the range [0-4]/4,
because that is a sane thing to do for a PWM.
The information missing is that a real clock is resquested, not a PWM.
Tested-by: John Stultz <jstultz@google.com>
Tested-by: Joao Schim <joao@schimsalabim.eu>
Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
drivers/pwm/Kconfig | 12 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-sun8i.c | 938 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 951 insertions(+)
create mode 100644 drivers/pwm/pwm-sun8i.c
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6f3147518376..c4fd682860d6 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -736,6 +736,18 @@ config PWM_SUN4I
To compile this driver as a module, choose M here: the module
will be called pwm-sun4i.
+config PWM_SUN8I
+ tristate "Allwinner sun8i/sun50i PWM support"
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on HAS_IOMEM && COMMON_CLK
+ help
+ Generic PWM framework driver for Allwinner H616 SoCs.
+ It supports generic PWM, but can also provides a plain clock.
+ The AC300 PHY integrated in H616 SoC needs such a clock.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sun8i.
+
config PWM_SUNPLUS
tristate "Sunplus PWM support"
depends on ARCH_SUNPLUS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 0dc0d2b69025..ba2e0ec7fc17 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
+obj-$(CONFIG_PWM_SUN8I) += pwm-sun8i.o
obj-$(CONFIG_PWM_SUNPLUS) += pwm-sunplus.o
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
obj-$(CONFIG_PWM_TH1520) += pwm_th1520.o
diff --git a/drivers/pwm/pwm-sun8i.c b/drivers/pwm/pwm-sun8i.c
new file mode 100644
index 000000000000..8f1023e3a2e5
--- /dev/null
+++ b/drivers/pwm/pwm-sun8i.c
@@ -0,0 +1,938 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Allwinner sun8i Pulse Width Modulation Controller
+ *
+ * (C) Copyright 2025 Richard Genoud, Bootlin <richard.genoud@bootlin.com>
+ *
+ * Based on drivers/pwm/pwm-sun4i.c with Copyright:
+ *
+ * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@bootlin.com>
+ *
+ * Limitations:
+ * - As the channels are paired (0/1, 2/3, 4/5), they share the same clock
+ * source and prescaler(div_m), but they also have their own prescaler(div_k)
+ * and bypass.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/limits.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+
+/* PWM IRQ Enable Register */
+#define SUN8I_PWM_PIER 0x0
+
+/* PWM IRQ Status Register */
+#define SUN8I_PWM_PISR 0x4
+
+/* PWM Capture IRQ Enable Register */
+#define SUN8I_PWM_CIER 0x10
+
+/* PWM Capture IRQ Status Register */
+#define SUN8I_PWM_CISR 0x14
+
+/* PWMCC Pairs Clock Configuration Registers */
+#define SUN8I_PWM_PCCR(pair) (0x20 + ((pair) * 0x4))
+#define SUN8I_PWM_PCCR_SRC_SHIFT 7
+#define SUN8I_PWM_PCCR_SRC_MASK 1
+#define SUN8I_PWM_PCCR_GATE_BIT 4
+#define SUN8I_PWM_PCCR_BYPASS_BIT(chan) ((chan) % 2 + 5)
+#define SUN8I_PWM_PCCR_DIV_M_SHIFT 0
+
+/* PWMCC Pairs Dead Zone Control Registers */
+#define SUN8I_PWM_PDZCR(pair) (0x30 + ((pair) * 0x4))
+
+/* PWM Enable Register */
+#define SUN8I_PWM_PER 0x40
+#define SUN8I_PWM_ENABLE(chan) BIT(chan)
+
+/* PWM Capture Enable Register */
+#define SUN8I_PWM_CER 0x44
+
+/* PWM Control Register */
+#define SUN8I_PWM_PCR(chan) (0x60 + (chan) * 0x20)
+#define SUN8I_PWM_PCR_PRESCAL_K_SHIFT 0
+#define SUN8I_PWM_PCR_PRESCAL_K_WIDTH 8
+#define SUN8I_PWM_PCR_ACTIVE_STATE BIT(8)
+
+/* PWM Period Register */
+#define SUN8I_PWM_PPR(chan) (0x64 + (chan) * 0x20)
+#define SUN8I_PWM_PPR_PERIOD_MASK GENMASK(31, 16)
+#define SUN8I_PWM_PPR_DUTY_MASK GENMASK(15, 0)
+#define SUN8I_PWM_PPR_PERIOD_VALUE(reg) (FIELD_GET(SUN8I_PWM_PPR_PERIOD_MASK, reg) + 1)
+#define SUN8I_PWM_PPR_DUTY_VALUE(reg) FIELD_GET(SUN8I_PWM_PPR_DUTY_MASK, reg)
+#define SUN8I_PWM_PPR_PERIOD(prd) FIELD_PREP(SUN8I_PWM_PPR_PERIOD_MASK, (prd) - 1)
+#define SUN8I_PWM_DUTY(dty) FIELD_PREP(SUN8I_PWM_PPR_DUTY_MASK, dty)
+#define SUN8I_PWM_PPR_PERIOD_MAX (FIELD_MAX(SUN8I_PWM_PPR_PERIOD_MASK) + 1)
+
+/* PWM Count Register */
+#define SUN8I_PWM_PCNTR(chan) (0x68 + (chan) * 0x20)
+
+/* PWM Capture Control Register */
+#define SUN8I_PWM_CCR(chan) (0x6c + (chan) * 0x20)
+
+/* PWM Capture Rise Lock Register */
+#define SUN8I_PWM_CRLR(chan) (0x70 + (chan) * 0x20)
+
+/* PWM Capture Fall Lock Register */
+#define SUN8I_PWM_CFLR(chan) (0x74 + (chan) * 0x20)
+
+#define SUN8I_PWM_PAIR_IDX(chan) ((chan) >> 1)
+
+/*
+ * Block diagram of the PWM clock controller:
+ *
+ * _____ ______ ________
+ * OSC24M --->| | | | | |
+ * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> SUN8I_PWM_clock_src_xy
+ * |_____| |______| |________|
+ * ________
+ * | |
+ * +->| /div_k |---> SUN8I_PWM_clock_x
+ * | |________|
+ * | ______
+ * | | |
+ * +-->| Gate |----> SUN8I_PWM_bypass_clock_x
+ * | |______|
+ * SUN8I_PWM_clock_src_xy ---+ ________
+ * | | |
+ * +->| /div_k |---> SUN8I_PWM_clock_y
+ * | |________|
+ * | ______
+ * | | |
+ * +-->| Gate |----> SUN8I_PWM_bypass_clock_y
+ * |______|
+ *
+ * NB: when the bypass is set, all the PWM logic is bypassed.
+ * So, the duty cycle and polarity can't be modified (we just have a clock).
+ * The bypass in PWM mode is used to achieve a 1/2 relative duty cycle with the
+ * fastest clock.
+ *
+ * SUN8I_PWM_clock_x/y serve for the PWM purpose.
+ * SUN8I_PWM_bypass_clock_x/y serve for the clock-provider purpose.
+ *
+ */
+
+/*
+ * Table used for /div_m (diviser before obtaining SUN8I_PWM_clock_src_xy)
+ * It's actually CLK_DIVIDER_POWER_OF_TWO, but limited to /256
+ */
+#define CLK_TABLE_DIV_M_ENTRY(i) { \
+ .val = (i), .div = 1 << (i) \
+}
+
+static const struct clk_div_table clk_table_div_m[] = {
+ CLK_TABLE_DIV_M_ENTRY(0),
+ CLK_TABLE_DIV_M_ENTRY(1),
+ CLK_TABLE_DIV_M_ENTRY(2),
+ CLK_TABLE_DIV_M_ENTRY(3),
+ CLK_TABLE_DIV_M_ENTRY(4),
+ CLK_TABLE_DIV_M_ENTRY(5),
+ CLK_TABLE_DIV_M_ENTRY(6),
+ CLK_TABLE_DIV_M_ENTRY(7),
+ CLK_TABLE_DIV_M_ENTRY(8),
+ { /* sentinel */ }
+};
+
+#define SUN8I_PWM_XY_SRC_GATE(_pair, _reg) \
+struct clk_gate gate_xy_##_pair = { \
+ .reg = (void *)(_reg), \
+ .bit_idx = SUN8I_PWM_PCCR_GATE_BIT, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_gate_ops, \
+ } \
+}
+
+#define SUN8I_PWM_XY_SRC_MUX(_pair, _reg) \
+struct clk_mux mux_xy_##_pair = { \
+ .reg = (void *)(_reg), \
+ .shift = SUN8I_PWM_PCCR_SRC_SHIFT, \
+ .mask = SUN8I_PWM_PCCR_SRC_MASK, \
+ .flags = CLK_MUX_ROUND_CLOSEST, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_mux_ops, \
+ } \
+}
+
+#define SUN8I_PWM_XY_SRC_DIV(_pair, _reg) \
+struct clk_divider rate_xy_##_pair = { \
+ .reg = (void *)(_reg), \
+ .shift = SUN8I_PWM_PCCR_DIV_M_SHIFT, \
+ .table = clk_table_div_m, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_divider_ops, \
+ } \
+}
+
+#define SUN8I_PWM_X_DIV(_idx, _reg) \
+struct clk_divider rate_x_##_idx = { \
+ .reg = (void *)(_reg), \
+ .shift = SUN8I_PWM_PCR_PRESCAL_K_SHIFT, \
+ .width = SUN8I_PWM_PCR_PRESCAL_K_WIDTH, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_divider_ops, \
+ } \
+}
+
+#define SUN8I_PWM_X_BYPASS_GATE(_idx) \
+struct clk_gate gate_x_bypass_##_idx = { \
+ .reg = (void *)SUN8I_PWM_PER, \
+ .bit_idx = _idx, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_gate_ops, \
+ } \
+}
+
+#define SUN8I_PWM_XY_CLK_SRC(_pair, _reg) \
+ static SUN8I_PWM_XY_SRC_MUX(_pair, _reg); \
+ static SUN8I_PWM_XY_SRC_GATE(_pair, _reg); \
+ static SUN8I_PWM_XY_SRC_DIV(_pair, _reg)
+
+#define SUN8I_PWM_X_CLK(_idx) \
+ static SUN8I_PWM_X_DIV(_idx, SUN8I_PWM_PCR(_idx))
+
+#define SUN8I_PWM_X_BYPASS_CLK(_idx) \
+ SUN8I_PWM_X_BYPASS_GATE(_idx)
+
+#define REF_CLK_XY_SRC(_pair) \
+ { \
+ .name = "pwm-clk-src" #_pair, \
+ .mux_hw = &mux_xy_##_pair.hw, \
+ .gate_hw = &gate_xy_##_pair.hw, \
+ .rate_hw = &rate_xy_##_pair.hw, \
+ }
+
+#define REF_CLK_X(_idx, _pair) \
+ { \
+ .name = "pwm-clk" #_idx, \
+ .parent_names = (const char *[]){ "pwm-clk-src" #_pair }, \
+ .num_parents = 1, \
+ .rate_hw = &rate_x_##_idx.hw, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }
+
+#define REF_CLK_BYPASS(_idx, _pair) \
+ { \
+ .name = "pwm-clk-bypass" #_idx, \
+ .parent_names = (const char *[]){ "pwm-clk-src" #_pair }, \
+ .num_parents = 1, \
+ .gate_hw = &gate_x_bypass_##_idx.hw, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }
+
+/*
+ * SUN8I_PWM_clock_src_xy generation:
+ * _____ ______ ________
+ * OSC24M --->| | | | | |
+ * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> SUN8I_PWM_clock_src_xy
+ * |_____| |______| |________|
+ */
+SUN8I_PWM_XY_CLK_SRC(01, SUN8I_PWM_PCCR(0));
+SUN8I_PWM_XY_CLK_SRC(23, SUN8I_PWM_PCCR(1));
+SUN8I_PWM_XY_CLK_SRC(45, SUN8I_PWM_PCCR(2));
+
+/*
+ * SUN8I_PWM_clock_x_div generation:
+ * ________
+ * | | SUN8I_PWM_clock_x/y
+ * SUN8I_PWM_clock_src_xy -->| /div_k |--------------->
+ * |________|
+ */
+SUN8I_PWM_X_CLK(0);
+SUN8I_PWM_X_CLK(1);
+SUN8I_PWM_X_CLK(2);
+SUN8I_PWM_X_CLK(3);
+SUN8I_PWM_X_CLK(4);
+SUN8I_PWM_X_CLK(5);
+
+/*
+ * SUN8I_PWM_bypass_clock_xy generation:
+ * ______
+ * | |
+ * SUN8I_PWM_clock_src_xy --->| Gate |-------> SUN8I_PWM_bypass_clock_x
+ * |______|
+ *
+ * The gate is actually SUN8I_PWM_PER register.
+ */
+SUN8I_PWM_X_BYPASS_CLK(0);
+SUN8I_PWM_X_BYPASS_CLK(1);
+SUN8I_PWM_X_BYPASS_CLK(2);
+SUN8I_PWM_X_BYPASS_CLK(3);
+SUN8I_PWM_X_BYPASS_CLK(4);
+SUN8I_PWM_X_BYPASS_CLK(5);
+
+struct clk_pwm_data {
+ const char *name;
+ const char **parent_names;
+ unsigned int num_parents;
+ struct clk_hw *mux_hw;
+ struct clk_hw *rate_hw;
+ struct clk_hw *gate_hw;
+ unsigned long flags;
+};
+
+/* Indexes of REF_CLK_BYPASS and REF_CLK_XY_SRC in the array */
+#define CLK_BYPASS_IDX(sun8i_chip, chan) ((sun8i_chip)->data->npwm + (chan))
+#define CLK_XY_SRC_IDX(sun8i_chip, chan) \
+ ((sun8i_chip)->data->npwm * 2 + SUN8I_PWM_PAIR_IDX(chan))
+static struct clk_pwm_data pwmcc_data[] = {
+ REF_CLK_X(0, 01),
+ REF_CLK_X(1, 01),
+ REF_CLK_X(2, 23),
+ REF_CLK_X(3, 23),
+ REF_CLK_X(4, 45),
+ REF_CLK_X(5, 45),
+ REF_CLK_BYPASS(0, 01),
+ REF_CLK_BYPASS(1, 01),
+ REF_CLK_BYPASS(2, 23),
+ REF_CLK_BYPASS(3, 23),
+ REF_CLK_BYPASS(4, 45),
+ REF_CLK_BYPASS(5, 45),
+ REF_CLK_XY_SRC(01),
+ REF_CLK_XY_SRC(23),
+ REF_CLK_XY_SRC(45),
+ { /* sentinel */ }
+};
+
+enum sun8i_pwm_mode {
+ SUN8I_PWM_MODE_NONE,
+ SUN8I_PWM_MODE_PWM,
+ SUN8I_PWM_MODE_CLK,
+};
+
+struct sun8i_pwm_data {
+ unsigned int npwm;
+};
+
+struct sun8i_pwm_channel {
+ struct clk *pwm_clk;
+ enum sun8i_pwm_mode mode;
+};
+
+struct clk_pwm_pdata {
+ struct clk_hw_onecell_data *hw_data;
+ spinlock_t lock;
+ void __iomem *reg;
+};
+
+struct sun8i_pwm_chip {
+ struct clk_pwm_pdata *clk_pdata;
+ struct sun8i_pwm_channel *channels;
+ struct clk *bus_clk;
+ struct reset_control *rst;
+ void __iomem *base;
+ const struct sun8i_pwm_data *data;
+};
+
+struct sun8i_pwm_waveform {
+ u8 enabled:1;
+ u8 active_state:1;
+ u8 bypass_en:1;
+ u16 duty_ticks;
+ u32 period_ticks;
+ unsigned long clk_rate;
+};
+
+static inline struct sun8i_pwm_chip *sun8i_pwm_from_chip(const struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 sun8i_pwm_readl(struct sun8i_pwm_chip *sun8i_chip,
+ unsigned long offset)
+{
+ return readl(sun8i_chip->base + offset);
+}
+
+static inline void sun8i_pwm_writel(struct sun8i_pwm_chip *sun8i_chip,
+ u32 val, unsigned long offset)
+{
+ writel(val, sun8i_chip->base + offset);
+}
+
+static void sun8i_pwm_set_bypass(struct sun8i_pwm_chip *sun8i_chip,
+ unsigned int idx, bool en_bypass)
+{
+ unsigned long flags, reg_offset;
+ u32 val;
+
+ spin_lock_irqsave(&sun8i_chip->clk_pdata->lock, flags);
+
+ reg_offset = SUN8I_PWM_PCCR(SUN8I_PWM_PAIR_IDX(idx));
+ val = sun8i_pwm_readl(sun8i_chip, reg_offset);
+ if (en_bypass)
+ val |= BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx));
+ else
+ val &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx));
+
+ sun8i_pwm_writel(sun8i_chip, val, reg_offset);
+
+ spin_unlock_irqrestore(&sun8i_chip->clk_pdata->lock, flags);
+}
+
+static int sun8i_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+
+ scoped_guard(spinlock_irqsave, &sun8i_chip->clk_pdata->lock) {
+ if (chan->mode == SUN8I_PWM_MODE_CLK)
+ return -EBUSY;
+ chan->mode = SUN8I_PWM_MODE_PWM;
+ }
+
+ return clk_prepare_enable(chan->pwm_clk);
+}
+
+static void sun8i_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+
+ clk_disable_unprepare(chan->pwm_clk);
+ chan->mode = SUN8I_PWM_MODE_NONE;
+}
+
+static int sun8i_pwm_read_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ void *_wfhw)
+{
+ struct sun8i_pwm_waveform *wfhw = _wfhw;
+ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+ u32 val;
+
+ wfhw->clk_rate = clk_get_rate(chan->pwm_clk);
+
+ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER);
+ wfhw->enabled = !!(SUN8I_PWM_ENABLE(pwm->hwpwm) & val);
+
+ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(SUN8I_PWM_PAIR_IDX(pwm->hwpwm)));
+ wfhw->bypass_en = !!(val & BIT(SUN8I_PWM_PCCR_BYPASS_BIT(pwm->hwpwm)));
+
+ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(pwm->hwpwm));
+ wfhw->active_state = !!(val & SUN8I_PWM_PCR_ACTIVE_STATE);
+
+ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PPR(pwm->hwpwm));
+ wfhw->duty_ticks = SUN8I_PWM_PPR_DUTY_VALUE(val);
+ wfhw->period_ticks = SUN8I_PWM_PPR_PERIOD_VALUE(val);
+
+ dev_dbg(pwmchip_parent(chip),
+ "pwm%u: %s, bypass: %s, polarity: %s, clk_rate=%lu period_ticks=%u duty_ticks=%u\n",
+ pwm->hwpwm,
+ wfhw->enabled ? "enabled" : "disabled",
+ wfhw->bypass_en ? "enabled" : "disabled",
+ wfhw->active_state ? "normal" : "inversed",
+ wfhw->clk_rate, wfhw->period_ticks, wfhw->duty_ticks);
+
+ return 0;
+}
+
+static int sun8i_pwm_round_waveform_fromhw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw,
+ struct pwm_waveform *wf)
+{
+ const struct sun8i_pwm_waveform *wfhw = _wfhw;
+ u64 tmp, resolution;
+
+ dev_dbg(pwmchip_parent(chip),
+ "pwm%u: %s, bypass: %s, polarity: %s, clk_rate=%lu period_ticks=%u duty_ticks=%u\n",
+ pwm->hwpwm,
+ wfhw->enabled ? "enabled" : "disabled",
+ wfhw->bypass_en ? "enabled" : "disabled",
+ wfhw->active_state ? "normal" : "inversed",
+ wfhw->clk_rate, wfhw->period_ticks, wfhw->duty_ticks);
+
+ wf->duty_offset_ns = 0;
+
+ if (!wfhw->enabled || !wfhw->clk_rate) {
+ wf->period_length_ns = 0;
+ wf->duty_length_ns = 0;
+ return 0;
+ }
+
+ if (wfhw->bypass_en) {
+ wf->period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC,
+ wfhw->clk_rate);
+ wf->duty_length_ns = DIV_ROUND_UP_ULL(wf->period_length_ns, 2);
+ return 0;
+ }
+
+ tmp = NSEC_PER_SEC * (u64)wfhw->period_ticks;
+ wf->period_length_ns = DIV_ROUND_UP_ULL(tmp, wfhw->clk_rate);
+
+ tmp = NSEC_PER_SEC * (u64)wfhw->duty_ticks;
+ wf->duty_length_ns = DIV_ROUND_UP_ULL(tmp, wfhw->clk_rate);
+ if (!wfhw->active_state) {
+ /*
+ * For inverted polarity, we have to fix cases where
+ * computed duty_length_ns > requested duty_length_ns
+ * For that, we subtract the actual resolution of the PWM
+ * registers
+ */
+ wf->duty_offset_ns = wf->duty_length_ns;
+ wf->duty_length_ns = wf->period_length_ns - wf->duty_length_ns;
+
+ resolution = DIV_ROUND_UP_ULL(NSEC_PER_SEC, wfhw->clk_rate);
+
+ if (wf->duty_offset_ns >= resolution)
+ wf->duty_offset_ns -= resolution;
+ }
+
+ dev_dbg(pwmchip_parent(chip),
+ "pwm%u period_length_ns=%llu duty_length_ns=%llu duty_offset_ns=%llu\n",
+ pwm->hwpwm, wf->period_length_ns, wf->duty_length_ns,
+ wf->duty_offset_ns);
+
+ return 0;
+}
+
+static int sun8i_pwm_write_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm, const void *_wfhw)
+{
+ const struct sun8i_pwm_waveform *wfhw = _wfhw;
+ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+ unsigned long flags;
+ u32 val;
+ int ret;
+
+ ret = clk_set_rate(chan->pwm_clk, wfhw->clk_rate);
+ if (ret)
+ return ret;
+
+ sun8i_pwm_set_bypass(sun8i_chip, pwm->hwpwm, wfhw->bypass_en);
+
+ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(pwm->hwpwm));
+ if (wfhw->active_state)
+ val |= SUN8I_PWM_PCR_ACTIVE_STATE;
+ else
+ val &= ~SUN8I_PWM_PCR_ACTIVE_STATE;
+ sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PCR(pwm->hwpwm));
+
+ val = SUN8I_PWM_DUTY(wfhw->duty_ticks);
+ val |= SUN8I_PWM_PPR_PERIOD(wfhw->period_ticks);
+ sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PPR(pwm->hwpwm));
+
+ spin_lock_irqsave(&sun8i_chip->clk_pdata->lock, flags);
+
+ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER);
+ if (wfhw->enabled)
+ val |= SUN8I_PWM_ENABLE(pwm->hwpwm);
+ else
+ val &= ~SUN8I_PWM_ENABLE(pwm->hwpwm);
+ sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PER);
+
+ spin_unlock_irqrestore(&sun8i_chip->clk_pdata->lock, flags);
+
+ return 0;
+}
+
+static int sun8i_pwm_round_waveform_tohw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_waveform *wf,
+ void *_wfhw)
+{
+ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+ struct sun8i_pwm_waveform *wfhw = _wfhw;
+ unsigned long max_rate;
+ long calc_rate;
+ u64 period_ratio, double_duty_ratio, freq, duty_cycle;
+
+ dev_dbg(pwmchip_parent(chip),
+ "pwm%u period_length_ns=%llu duty_length_ns=%llu duty_offset_ns=%llu\n",
+ pwm->hwpwm, wf->period_length_ns, wf->duty_length_ns,
+ wf->duty_offset_ns);
+
+ if (wf->period_length_ns == 0) {
+ wfhw->enabled = 0;
+ return 0;
+ }
+
+ wfhw->enabled = 1;
+
+ duty_cycle = wf->duty_length_ns;
+ if (wf->duty_length_ns + wf->duty_offset_ns < wf->period_length_ns)
+ wfhw->active_state = 1;
+ else
+ wfhw->active_state = 0;
+
+ dev_dbg(pwmchip_parent(chip), "polarity: %s\n",
+ wfhw->active_state ? "normal" : "inversed");
+
+ /*
+ * Lowest possible period case:
+ * Without bypass, the lowest possible period is when:
+ * duty cycle = 1 and period cycle = 2 (0x10001 in period register)
+ * E.g. if the input clock is 100MHz, we have a lowest period of 20ns.
+ * Now, with the bypass, the period register is ignored and we directly
+ * have the 100MHz clock as PWM output, that can act as a 10ns period
+ * with 5ns duty.
+ * So, to detect this lowest period case, just get the maximum possible
+ * rate from chan->pwm_clk and compare it with requested period and
+ * duty_cycle.
+ *
+ * But, to get the maximum possible rate, we have to use U32_MAX instead
+ * of (unsigned long)-1.
+ * This is because clk_round_rate() uses ultimately DIV_ROUND_UP_ULL()
+ * that in turn do_div(n,base). And base is uint32_t divisor.
+ */
+ max_rate = clk_round_rate(chan->pwm_clk, U32_MAX);
+
+ dev_dbg(pwmchip_parent(chip), "max_rate: %ld Hz\n", max_rate);
+
+ period_ratio = mul_u64_u64_div_u64(wf->period_length_ns,
+ max_rate, NSEC_PER_SEC);
+ double_duty_ratio = mul_u64_u64_div_u64(duty_cycle, (u64)max_rate * 2,
+ NSEC_PER_SEC);
+ if (period_ratio == 1) {
+ if (double_duty_ratio == 0)
+ /* requested period and duty are too small */
+ return -EINVAL;
+ /*
+ * If the requested period is to small to be generated by the
+ * PWM, but matches the highest clock with a
+ * duty_cycle >= period*2, just bypass the PWM logic
+ */
+ freq = div64_u64(NSEC_PER_SEC, wf->period_length_ns);
+ wfhw->bypass_en = true;
+ } else {
+ wfhw->bypass_en = false;
+ freq = div64_u64(NSEC_PER_SEC * (u64)SUN8I_PWM_PPR_PERIOD_MAX,
+ wf->period_length_ns);
+ /*
+ * Same remark as above, this is to prevent a value to big for
+ * clk_round_rate() to handle
+ */
+ if (freq > U32_MAX)
+ freq = U32_MAX;
+ }
+
+ dev_dbg(pwmchip_parent(chip), "bypass: %s\n",
+ wfhw->bypass_en ? "enabled" : "disabled");
+
+ calc_rate = clk_round_rate(chan->pwm_clk, freq);
+ if (calc_rate <= 0)
+ return calc_rate ? calc_rate : -EINVAL;
+
+ dev_dbg(pwmchip_parent(chip), "calc_rate: %ld Hz\n", calc_rate);
+
+ wfhw->period_ticks = mul_u64_u64_div_u64(calc_rate,
+ wf->period_length_ns,
+ NSEC_PER_SEC);
+ if (wfhw->period_ticks > SUN8I_PWM_PPR_PERIOD_MAX)
+ wfhw->period_ticks = SUN8I_PWM_PPR_PERIOD_MAX;
+
+ /* min value in period register is 1 */
+ if (wfhw->period_ticks == 0)
+ return -EINVAL;
+
+ wfhw->duty_ticks = mul_u64_u64_div_u64(calc_rate, duty_cycle,
+ NSEC_PER_SEC);
+
+ if (wfhw->duty_ticks > wfhw->period_ticks)
+ wfhw->duty_ticks = wfhw->period_ticks;
+
+ if (!wfhw->active_state)
+ wfhw->duty_ticks = wfhw->period_ticks - wfhw->duty_ticks;
+
+ dev_dbg(pwmchip_parent(chip),
+ "pwm%u period_ticks=%u duty_cycle=%llu duty_ticks=%u\n",
+ pwm->hwpwm, wfhw->period_ticks, duty_cycle, wfhw->duty_ticks);
+
+ wfhw->clk_rate = calc_rate;
+
+ return 0;
+}
+
+static const struct pwm_ops sun8i_pwm_ops = {
+ .request = sun8i_pwm_request,
+ .free = sun8i_pwm_free,
+ .sizeof_wfhw = sizeof(struct sun8i_pwm_waveform),
+ .round_waveform_tohw = sun8i_pwm_round_waveform_tohw,
+ .round_waveform_fromhw = sun8i_pwm_round_waveform_fromhw,
+ .read_waveform = sun8i_pwm_read_waveform,
+ .write_waveform = sun8i_pwm_write_waveform,
+};
+
+static struct clk_hw *sun8i_pwm_of_clk_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct sun8i_pwm_chip *sun8i_chip = data;
+ struct clk_hw_onecell_data *hw_data = sun8i_chip->clk_pdata->hw_data;
+ unsigned int idx = clkspec->args[0];
+ struct sun8i_pwm_channel *chan;
+ struct clk_hw *ret_clk = NULL;
+ unsigned long flags;
+
+ if (idx >= sun8i_chip->data->npwm)
+ return ERR_PTR(-EINVAL);
+
+ chan = &sun8i_chip->channels[idx];
+
+ spin_lock_irqsave(&sun8i_chip->clk_pdata->lock, flags);
+
+ if (chan->mode == SUN8I_PWM_MODE_PWM) {
+ ret_clk = ERR_PTR(-EBUSY);
+ } else {
+ chan->mode = SUN8I_PWM_MODE_CLK;
+ ret_clk = hw_data->hws[CLK_BYPASS_IDX(sun8i_chip, idx)];
+ }
+ spin_unlock_irqrestore(&sun8i_chip->clk_pdata->lock, flags);
+
+ if (IS_ERR(ret_clk))
+ goto out;
+
+ sun8i_pwm_set_bypass(sun8i_chip, idx, true);
+out:
+ return ret_clk;
+}
+
+static int sun8i_add_composite_clk(struct clk_pwm_data *data,
+ void __iomem *reg, spinlock_t *lock,
+ struct device *dev, struct clk_hw **hw)
+{
+ const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *rate_ops = NULL;
+ struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *rate_hw = NULL;
+ struct device_node *node = dev->of_node;
+
+ if (data->mux_hw) {
+ struct clk_mux *mux;
+
+ mux_hw = data->mux_hw;
+ mux = to_clk_mux(mux_hw);
+ mux->lock = lock;
+ mux_ops = mux_hw->init->ops;
+ mux->reg = (uintptr_t)mux->reg + reg;
+ }
+
+ if (data->gate_hw) {
+ struct clk_gate *gate;
+
+ gate_hw = data->gate_hw;
+ gate = to_clk_gate(gate_hw);
+ gate->lock = lock;
+ gate_ops = gate_hw->init->ops;
+ gate->reg = (uintptr_t)gate->reg + reg;
+ }
+
+ if (data->rate_hw) {
+ struct clk_divider *rate;
+
+ rate_hw = data->rate_hw;
+ rate = to_clk_divider(rate_hw);
+ rate_ops = rate_hw->init->ops;
+ rate->lock = lock;
+ rate->reg = (uintptr_t)rate->reg + reg;
+
+ if (rate->table) {
+ const struct clk_div_table *clkt;
+ int table_size = 0;
+
+ for (clkt = rate->table; clkt->div; clkt++)
+ table_size++;
+ rate->width = order_base_2(table_size);
+ }
+ }
+
+ /*
+ * Retrieve the parent clock names from DTS for pwm-clk-srcxy
+ */
+ if (!data->parent_names) {
+ data->num_parents = of_clk_get_parent_count(node);
+ if (data->num_parents == 0)
+ return -ENOENT;
+
+ data->parent_names = devm_kzalloc(dev,
+ sizeof(*data->parent_names),
+ GFP_KERNEL);
+ for (unsigned int i = 0; i < data->num_parents; i++)
+ data->parent_names[i] = of_clk_get_parent_name(node, i);
+ }
+
+ *hw = clk_hw_register_composite(dev, data->name, data->parent_names,
+ data->num_parents, mux_hw,
+ mux_ops, rate_hw, rate_ops,
+ gate_hw, gate_ops, data->flags);
+
+ return PTR_ERR_OR_ZERO(*hw);
+}
+
+static int sun8i_pwm_init_clocks(struct platform_device *pdev,
+ struct sun8i_pwm_chip *sun8i_chip)
+{
+ struct clk_pwm_pdata *pdata;
+ struct device *dev = &pdev->dev;
+ int num_clocks = 0;
+ int ret;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed to allocate clk_pwm_pdata\n");
+
+ while (pwmcc_data[num_clocks].name)
+ num_clocks++;
+
+ pdata->hw_data = devm_kzalloc(dev, struct_size(pdata->hw_data, hws, num_clocks),
+ GFP_KERNEL);
+ if (!pdata->hw_data)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed to allocate hw clocks\n");
+
+ pdata->hw_data->num = num_clocks;
+ pdata->reg = sun8i_chip->base;
+
+ spin_lock_init(&pdata->lock);
+
+ for (int i = 0; i < num_clocks; i++) {
+ struct clk_hw **hw = &pdata->hw_data->hws[i];
+
+ ret = sun8i_add_composite_clk(&pwmcc_data[i], pdata->reg,
+ &pdata->lock, dev, hw);
+ if (ret) {
+ dev_err_probe(dev, ret,
+ "Failed to register hw clock %s\n",
+ pwmcc_data[i].name);
+ for (i--; i >= 0; i--)
+ clk_hw_unregister_composite(pdata->hw_data->hws[i]);
+ return ret;
+ }
+ }
+
+ sun8i_chip->clk_pdata = pdata;
+
+ return 0;
+}
+
+static void sun8i_pwm_unregister_clk(void *data)
+{
+ struct clk_hw_onecell_data *hw_data = data;
+
+ for (unsigned int i = 0; i < hw_data->num; i++)
+ clk_hw_unregister_composite(hw_data->hws[i]);
+}
+
+static int sun8i_pwm_probe(struct platform_device *pdev)
+{
+ const struct sun8i_pwm_data *data;
+ struct device *dev = &pdev->dev;
+ struct sun8i_pwm_chip *sun8i_chip;
+ struct pwm_chip *chip;
+ int ret;
+
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return dev_err_probe(dev, -ENODEV,
+ "Missing specific data structure\n");
+
+ chip = devm_pwmchip_alloc(dev, data->npwm, sizeof(*sun8i_chip));
+ if (IS_ERR(chip))
+ return dev_err_probe(dev, PTR_ERR(chip),
+ "Failed to allocate pwmchip\n");
+
+ sun8i_chip = sun8i_pwm_from_chip(chip);
+ sun8i_chip->data = data;
+ sun8i_chip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(sun8i_chip->base))
+ return dev_err_probe(dev, PTR_ERR(sun8i_chip->base),
+ "Failed to get PWM base address\n");
+
+ sun8i_chip->bus_clk = devm_clk_get_enabled(dev, "bus");
+ if (IS_ERR(sun8i_chip->bus_clk))
+ return dev_err_probe(dev, PTR_ERR(sun8i_chip->bus_clk),
+ "Failed to get bus clock\n");
+
+ sun8i_chip->channels = devm_kmalloc_array(dev, data->npwm,
+ sizeof(*(sun8i_chip->channels)),
+ GFP_KERNEL);
+ if (!sun8i_chip->channels)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed to allocate %d channels array\n",
+ data->npwm);
+
+ chip->ops = &sun8i_pwm_ops;
+
+ ret = sun8i_pwm_init_clocks(pdev, sun8i_chip);
+ if (ret)
+ return ret;
+
+ for (unsigned int i = 0; i < data->npwm; i++) {
+ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[i];
+ struct clk_hw **hw = &sun8i_chip->clk_pdata->hw_data->hws[i];
+
+ chan->pwm_clk = devm_clk_hw_get_clk(dev, *hw, NULL);
+ if (IS_ERR(chan->pwm_clk)) {
+ ret = dev_err_probe(dev, PTR_ERR(chan->pwm_clk),
+ "Failed to register PWM clock %d\n", i);
+ return ret;
+ }
+ chan->mode = SUN8I_PWM_MODE_NONE;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, sun8i_pwm_of_clk_get, sun8i_chip);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add HW clock provider\n");
+
+ ret = devm_add_action_or_reset(dev, sun8i_pwm_unregister_clk,
+ sun8i_chip->clk_pdata->hw_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add devm action\n");
+
+ /* Deassert reset */
+ sun8i_chip->rst = devm_reset_control_get_shared_deasserted(dev, NULL);
+ if (IS_ERR(sun8i_chip->rst))
+ return dev_err_probe(dev, PTR_ERR(sun8i_chip->rst),
+ "Failed to get reset control\n");
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static const struct sun8i_pwm_data sun50i_h616_pwm_data = {
+ .npwm = 6,
+};
+
+static const struct of_device_id sun8i_pwm_dt_ids[] = {
+ {
+ .compatible = "allwinner,sun50i-h616-pwm",
+ .data = &sun50i_h616_pwm_data,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, sun8i_pwm_dt_ids);
+
+static struct platform_driver sun8i_pwm_driver = {
+ .driver = {
+ .name = "sun8i-pwm",
+ .of_match_table = sun8i_pwm_dt_ids,
+ },
+ .probe = sun8i_pwm_probe,
+};
+module_platform_driver(sun8i_pwm_driver);
+
+MODULE_AUTHOR("Richard Genoud <richard.genoud@bootlin.com>");
+MODULE_DESCRIPTION("Allwinner sun8i PWM driver");
+MODULE_LICENSE("GPL");
^ permalink raw reply related
* [PATCH v5 1/4] dt-bindings: pwm: allwinner: add h616 pwm compatible
From: Richard Genoud @ 2026-04-16 13:14 UTC (permalink / raw)
To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Philipp Zabel
Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, Richard Genoud, Conor Dooley
In-Reply-To: <20260416131419.3152419-1-richard.genoud@bootlin.com>
Allwinner H616 PWM block is quite different from the A10 or H6, but at
the end, it uses the same clocks as the H6; so the sun4i pwm binding can
be used.
It has 6 channels than can generate PWM waveforms.
If the bypass is enabled (one bypass per channel) the output is no more
a PWM waveform, but a clock that can (and is) used as input for other
devices, like the AC300 PHY.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Tested-by: John Stultz <jstultz@google.com>
Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
.../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
index 1197858e431f..4f58110ec98f 100644
--- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
+++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
@@ -14,6 +14,9 @@ properties:
"#pwm-cells":
const: 3
+ "#clock-cells":
+ const: 1
+
compatible:
oneOf:
- const: allwinner,sun4i-a10-pwm
@@ -36,6 +39,7 @@ properties:
- const: allwinner,sun50i-h5-pwm
- const: allwinner,sun5i-a13-pwm
- const: allwinner,sun50i-h6-pwm
+ - const: allwinner,sun50i-h616-pwm
reg:
maxItems: 1
@@ -62,7 +66,9 @@ allOf:
properties:
compatible:
contains:
- const: allwinner,sun50i-h6-pwm
+ enum:
+ - allwinner,sun50i-h6-pwm
+ - allwinner,sun50i-h616-pwm
then:
properties:
@@ -83,6 +89,17 @@ allOf:
clocks:
maxItems: 1
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ const: allwinner,sun50i-h616-pwm
+
+ then:
+ properties:
+ "#clock-cells": false
+
required:
- compatible
- reg
^ permalink raw reply related
* [PATCH] MAINTAINERS: Move Peter De Schrijver to CREDITS
From: Thierry Reding @ 2026-04-16 13:18 UTC (permalink / raw)
To: Thierry Reding
Cc: linux-tegra, linux-arm-kernel, linux-pm, linux-omap, linux-kernel,
Paul Walmsley
From: Thierry Reding <treding@nvidia.com>
Peter sadly passed away a while back. Paul did a much better job at
finding the right words to mourn this loss than I ever could, so I will
leave this link here:
https://lore.kernel.org/lkml/alpine.DEB.2.21.999.2407240345480.11116@utopia.booyaka.com/T/#u
Co-developed-by: Paul Walmsley <pjw@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
CREDITS | 6 ++++++
MAINTAINERS | 1 -
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CREDITS b/CREDITS
index 885fb05d8816..29fcfa679430 100644
--- a/CREDITS
+++ b/CREDITS
@@ -3645,7 +3645,13 @@ D: Macintosh IDE Driver
N: Peter De Schrijver
E: stud11@cc4.kuleuven.ac.be
+E: p2@mind.be
+E: peter.de-schrijver@nokia.com
+E: pdeschrijver@nvidia.com
+E: p2@psychaos.be
D: Mitsumi CD-ROM driver patches March version
+D: OMAP power management
+D: NVIDIA Tegra clock and BPMP drivers, among many other things
S: Molenbaan 29
S: B2240 Zandhoven
S: Belgium
diff --git a/MAINTAINERS b/MAINTAINERS
index ef978bfca514..ffe20d770249 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26145,7 +26145,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git
N: [^a-z]tegra
TEGRA CLOCK DRIVER
-M: Peter De Schrijver <pdeschrijver@nvidia.com>
M: Prashant Gaikwad <pgaikwad@nvidia.com>
S: Supported
F: drivers/clk/tegra/
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v5 4/4] MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver
From: Krzysztof Kozlowski @ 2026-04-16 13:23 UTC (permalink / raw)
To: Richard Genoud, Uwe Kleine-König, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Philipp Zabel
Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel
In-Reply-To: <20260416131419.3152419-5-richard.genoud@bootlin.com>
On 16/04/2026 15:14, Richard Genoud wrote:
> Add myself as maintainer of Allwinner SUN8I PWM driver.
>
> Tested-by: John Stultz <jstultz@google.com>
Please drop or help me understand how one can test maintainers change?
Build process tools are not testing.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v5 1/4] dt-bindings: pwm: allwinner: add h616 pwm compatible
From: Krzysztof Kozlowski @ 2026-04-16 13:24 UTC (permalink / raw)
To: Richard Genoud, Uwe Kleine-König, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Philipp Zabel
Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, Conor Dooley
In-Reply-To: <20260416131419.3152419-2-richard.genoud@bootlin.com>
On 16/04/2026 15:14, Richard Genoud wrote:
> Allwinner H616 PWM block is quite different from the A10 or H6, but at
> the end, it uses the same clocks as the H6; so the sun4i pwm binding can
> be used.
> It has 6 channels than can generate PWM waveforms.
> If the bypass is enabled (one bypass per channel) the output is no more
> a PWM waveform, but a clock that can (and is) used as input for other
> devices, like the AC300 PHY.
>
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> Tested-by: John Stultz <jstultz@google.com>
Please drop or help me understand how one can test bindings on the
device? Build process tools are not testing.
Don't blindly copy tags from cover letter.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v5 4/4] MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver
From: Krzysztof Kozlowski @ 2026-04-16 13:24 UTC (permalink / raw)
To: Richard Genoud, Uwe Kleine-König, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Philipp Zabel
Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel
In-Reply-To: <20260416131419.3152419-5-richard.genoud@bootlin.com>
On 16/04/2026 15:14, Richard Genoud wrote:
> Add myself as maintainer of Allwinner SUN8I PWM driver.
>
> Tested-by: John Stultz <jstultz@google.com>
> Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
>
> maintainer
> ---
And you have commit msg trailing junk.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH net v2] net: airoha: Add missing bits in airoha_qdma_cleanup_tx_queue()
From: Simon Horman @ 2026-04-16 13:29 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-arm-kernel, linux-mediatek, netdev
In-Reply-To: <ad_DrrC6duye_lR0@lore-desk>
On Wed, Apr 15, 2026 at 06:58:22PM +0200, Lorenzo Bianconi wrote:
> On Apr 15, Simon Horman wrote:
> > On Tue, Apr 14, 2026 at 08:50:52AM +0200, Lorenzo Bianconi wrote:
> > > Similar to airoha_qdma_cleanup_rx_queue(), reset DMA TX descriptors in
> > > airoha_qdma_cleanup_tx_queue routine. Moreover, reset TX_DMA_IDX to
> > > TX_CPU_IDX to notify the NIC the QDMA TX ring is empty.
> > >
> > > Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC")
> > > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > > ---
> > > Changes in v2:
> > > - Move q->ndesc initialization at end of airoha_qdma_init_tx routine in
> > > order to avoid any possible NULL pointer dereference in
> > > airoha_qdma_cleanup_tx_queue()
> >
> > This seems to be a separate issue.
> > If so, I think it should be split out into a separate patch.
> >
> > > - Check if q->tx_list is empty in airoha_qdma_cleanup_tx_queue()
> > > - Link to v1: https://lore.kernel.org/r/20260410-airoha_qdma_cleanup_tx_queue-fix-net-v1-1-b7171c8f1e78@kernel.org
> >
> > I think it was covered in the review Jakub forwarded for v1. But FTR,
> > Sashiko has some feedback on this patch in the form of an existing bug
> > (that should almost certainly be handled separately from this patch).
>
> Hi Simon,
>
> I took a look to the Sashiko's report [0] but this issue is not introduced by
> this patch and, even if it would be a better approach, I guess the hw is
> capable of managing out-of-order TX descriptors. So I guess this patch is fine
> in this way, agree?
>
> [0] https://sashiko.dev/#/patchset/20260414-airoha_qdma_cleanup_tx_queue-fix-net-v2-1-875de57cc022%40kernel.org
Hi Lorenzo,
You responded in a different sub thread, so I think this is probably
implied. But FTR:
1. I agree [0] is not introduced by this patch
2. If the hw is capable of managing TX descriptors then
I think that [0] is a false positive
Regardless, [0] doesn't effect this patch.
^ 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