Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND PATCH net v3 1/2] net: stmmac: Prevent NULL deref when RX memory exhausted
From: Sam Edwards @ 2026-03-28 19:25 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
	Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
	Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
	linux-arm-kernel, linux-kernel, Sam Edwards, stable
In-Reply-To: <20260328192503.520689-1-CFSworks@gmail.com>

The CPU receives frames from the MAC through conventional DMA: the CPU
allocates buffers for the MAC, then the MAC fills them and returns
ownership to the CPU. For each hardware RX queue, the CPU and MAC
coordinate through a shared ring array of DMA descriptors: one
descriptor per DMA buffer. Each descriptor includes the buffer's
physical address and a status flag ("OWN") indicating which side owns
the buffer: OWN=0 for CPU, OWN=1 for MAC. The CPU is only allowed to set
the flag and the MAC is only allowed to clear it, and both must move
through the ring in sequence: thus the ring is used for both
"submissions" and "completions."

In the stmmac driver, stmmac_rx() bookmarks its position in the ring
with the `cur_rx` index. The main receive loop in that function checks
for rx_descs[cur_rx].own=0, gives the corresponding buffer to the
network stack (NULLing the pointer), and increments `cur_rx` modulo the
ring size. After the loop exits, stmmac_rx_refill(), which bookmarks its
position with `dirty_rx`, allocates fresh buffers and rearms the
descriptors (setting OWN=1). If it fails any allocation, it simply stops
early (leaving OWN=0) and will retry where it left off when next called.

This means descriptors have a three-stage lifecycle (terms my own):
- `empty` (OWN=1, buffer valid)
- `full` (OWN=0, buffer valid and populated)
- `dirty` (OWN=0, buffer NULL)

But because stmmac_rx() only checks OWN, it confuses `full`/`dirty`. In
the past (see 'Fixes:'), there was a bug where the loop could cycle
`cur_rx` all the way back to the first descriptor it dirtied, resulting
in a NULL dereference when mistaken for `full`. The aforementioned
commit resolved that *specific* failure by capping the loop's iteration
limit at `dma_rx_size - 1`, but this is only a partial fix: if the
previous stmmac_rx_refill() didn't complete, then there are leftover
`dirty` descriptors that the loop might encounter without needing to
cycle fully around. The current code therefore panics (see 'Closes:')
when stmmac_rx_refill() is memory-starved long enough for `cur_rx` to
catch up to `dirty_rx`.

Fix this by further tightening the clamp from `dma_rx_size - 1` to
`dma_rx_size - stmmac_rx_dirty() - 1`, subtracting any remnant dirty
entries and limiting the loop so that `cur_rx` cannot catch back up to
`dirty_rx`. This carries no risk of arithmetic underflow: since the
maximum possible return value of stmmac_rx_dirty() is `dma_rx_size - 1`,
the worst the clamp can do is prevent the loop from running at all.

Fixes: b6cb4541853c7 ("net: stmmac: avoid rx queue overrun")
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221010
Cc: stable@vger.kernel.org
Signed-off-by: Sam Edwards <CFSworks@gmail.com>
---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 6827c99bde8c..f98b070073c0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -5609,7 +5609,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
 
 	dma_dir = page_pool_get_dma_dir(rx_q->page_pool);
 	bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
-	limit = min(priv->dma_conf.dma_rx_size - 1, (unsigned int)limit);
+	limit = min(priv->dma_conf.dma_rx_size - stmmac_rx_dirty(priv, queue) - 1,
+		    (unsigned int)limit);
 
 	if (netif_msg_rx_status(priv)) {
 		void *rx_head;
-- 
2.52.0



^ permalink raw reply related

* [RESEND PATCH net v3 0/2] stmmac crash/stall fixes when under memory pressure
From: Sam Edwards @ 2026-03-28 19:25 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
	Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
	Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
	linux-arm-kernel, linux-kernel, Sam Edwards

Hi netdev,

This is v3 of my series containing a pair of bugfixes for the stmmac driver's
receive pipeline. These issues occur when stmmac_rx_refill() does not (fully)
succeed, which happens more frequently when free memory is low.

The first patch closes Bugzilla bug #221010 [1], where stmmac_rx() can circle
around to a still-dirty descriptor (with a NULL buffer pointer), mistake it for
a filled descriptor (due to OWN=0), and attempt to dereference the buffer.

In testing that patch, I discovered a second issue: starvation of available RX
buffers causes the NIC to stop sending interrupts; if the driver stops polling,
it will wait indefinitely for an interrupt that will never come. (Note: the
first patch makes this issue more prominent -- mostly because it lets the
system survive long enough to exhibit it -- but doesn't *cause* it.) The second
patch addresses that problem as well.

Both patches are minimal, appropriate for stable, and designated to `net`. My
focus is on small, obviously-correct, easy-to-explain changes: I'll follow up
with another patch/series (something like [2]) for `net-next` that fixes the
ring in a more robust way.

The tx and zc paths seem to have similar low-memory bugs, to be addressed in
separate series.

Regards,
Sam

---

RESEND: Oops, my subject line didn't have `net`, so I'm resending the series to
fix that.

[1] https://bugzilla.kernel.org/show_bug.cgi?id=221010
[2] https://lore.kernel.org/netdev/20260316021009.262358-4-CFSworks@gmail.com/

v3:
- Rebased on latest net/main
- Changed patch 2 to require that stmmac_rx_refill() *fully* succeeds before
  exiting polling, to reduce the chance of rx drops.
- DID NOT use the CIRC_SPACE() macro as suggested by Russell: I fear that the
  perspective shift (first think of the dirty descriptors as the "work" that
  refill "consumes" -- therefore the "space" is how much stmmac_rx() may loop)
  is too counterintuitive for a stable fix, but I'll do it in v4 if reviewers
  insist.
- Updated the recipients for the series, which was invalidated in v2 due to the
  `Fixes:`
v2: https://lore.kernel.org/netdev/20260319184031.8596-1-CFSworks@gmail.com/T/
- Completely rewrote the commit message of patch 1, now assuming the reader is
  generally familiar with DMA but wholly unfamiliar with the stmmac device
  (thanks Jakub!)
- Added missing `Fixes:` to patch 2
- Moved patch 2's `int budget = limit;` decl per the reverse-xmas-tree rule
- Dropped patch 3: this was a code improvement not appropriate for stable
- Generated the series with --subject-prefix='PATCH net'
v1: https://lore.kernel.org/netdev/20260316021009.262358-1-CFSworks@gmail.com/

Sam Edwards (2):
  net: stmmac: Prevent NULL deref when RX memory exhausted
  net: stmmac: Prevent indefinite RX stall on buffer exhaustion

 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

-- 
2.52.0



^ permalink raw reply

* Re: [PATCH v3 2/3] drm/gem-dma: Use the dma_*_attr API variant
From: kernel test robot @ 2026-03-28 19:16 UTC (permalink / raw)
  To: Chen-Yu Tsai, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter
  Cc: llvm, oe-kbuild-all, Rob Herring, dri-devel, linux-kernel,
	linux-arm-kernel, Chen-Yu Tsai
In-Reply-To: <20260326100248.1171828-3-wenst@chromium.org>

Hi Chen-Yu,

kernel test robot noticed the following build errors:

[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on next-20260327]
[cannot apply to sunxi/sunxi/for-next linus/master v7.0-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Chen-Yu-Tsai/drm-Introduce-DRM_MODE_DUMB_KERNEL_MAP-flag/20260328-141115
base:   https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link:    https://lore.kernel.org/r/20260326100248.1171828-3-wenst%40chromium.org
patch subject: [PATCH v3 2/3] drm/gem-dma: Use the dma_*_attr API variant
config: arm64-allmodconfig (https://download.01.org/0day-ci/archive/20260329/202603290350.pIgD9ufQ-lkp@intel.com/config)
compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260329/202603290350.pIgD9ufQ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603290350.pIgD9ufQ-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c:297:11: error: expected ')'
     297 |                                                     gem->dma_attrs);
         |                                                     ^
   drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c:295:31: note: to match this '('
     295 |                         ret = dma_get_sgtable_attrs(rcdu->dev, sgt, gem->vaddr,
         |                                                    ^
   1 error generated.


vim +297 drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c

   258	
   259	int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
   260			       struct sg_table sg_tables[3])
   261	{
   262		struct rcar_du_device *rcdu = vsp->dev;
   263		unsigned int i, j;
   264		int ret;
   265	
   266		for (i = 0; i < fb->format->num_planes; ++i) {
   267			struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);
   268			struct sg_table *sgt = &sg_tables[i];
   269	
   270			if (gem->sgt) {
   271				struct scatterlist *src;
   272				struct scatterlist *dst;
   273	
   274				/*
   275				 * If the GEM buffer has a scatter gather table, it has
   276				 * been imported from a dma-buf and has no physical
   277				 * address as it might not be physically contiguous.
   278				 * Copy the original scatter gather table to map it to
   279				 * the VSP.
   280				 */
   281				ret = sg_alloc_table(sgt, gem->sgt->orig_nents,
   282						     GFP_KERNEL);
   283				if (ret)
   284					goto fail;
   285	
   286				src = gem->sgt->sgl;
   287				dst = sgt->sgl;
   288				for (j = 0; j < gem->sgt->orig_nents; ++j) {
   289					sg_set_page(dst, sg_page(src), src->length,
   290						    src->offset);
   291					src = sg_next(src);
   292					dst = sg_next(dst);
   293				}
   294			} else {
   295				ret = dma_get_sgtable_attrs(rcdu->dev, sgt, gem->vaddr,
   296							    gem->dma_addr, gem->base.size
 > 297							    gem->dma_attrs);
   298				if (ret)
   299					goto fail;
   300			}
   301	
   302			ret = vsp1_du_map_sg(vsp->vsp, sgt);
   303			if (ret) {
   304				sg_free_table(sgt);
   305				goto fail;
   306			}
   307		}
   308	
   309		return 0;
   310	
   311	fail:
   312		while (i--) {
   313			struct sg_table *sgt = &sg_tables[i];
   314	
   315			vsp1_du_unmap_sg(vsp->vsp, sgt);
   316			sg_free_table(sgt);
   317		}
   318	
   319		return ret;
   320	}
   321	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply

* [PATCH v3 2/2] net: stmmac: Prevent indefinite RX stall on buffer exhaustion
From: Sam Edwards @ 2026-03-28 19:12 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
	Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
	Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
	linux-arm-kernel, linux-kernel, Sam Edwards, stable
In-Reply-To: <20260328191233.519950-1-CFSworks@gmail.com>

The stmmac driver handles interrupts in the usual NAPI way: an interrupt
arrives, the NAPI instance is scheduled and interrupts are masked, and
the actual work occurs in the NAPI polling function. Once no further
work remains, interrupts are unmasked and the NAPI instance is put to
sleep to await a future interrupt. In the receive case, the MAC only
sends the interrupt when a DMA operation completes; thus the driver must
make sure a usable RX DMA descriptor exists before expecting a future
interrupt.

The main receive loop in stmmac_rx() exits under one of 3 conditions:
1) It encounters a DMA descriptor with OWN=1, indicating that no further
   pending data exists. The MAC will use this descriptor for the next
   RX DMA operation, so the driver can expect a future interrupt.
2) It exhausts the NAPI budget. In this case, the driver doesn't know
   whether the MAC has any usable DMA descriptors. But when the driver
   consumes its full budget, that signals NAPI to keep polling, so the
   question is moot.
3) It runs out of (non-dirty) descriptors in the RX ring. In this case,
   the MAC will only have a usable descriptor if stmmac_rx_refill()
   succeeds (at least partially).

Currently, stmmac_rx() lacks any check against scenario #3 and
stmmac_rx_refill() failing: it will stop NAPI polling and unmask
interrupts to await an interrupt that will never arrive, stalling the
receive pipeline indefinitely.

Fix this by checking stmmac_rx_dirty(): it will return 0 if
stmmac_rx_refill() fully succeeded and we can safely await an interrupt.
Any nonzero value means some allocations failed, in which case we risk
dropping frames if a large traffic burst exhausts the surviving
non-dirties. Therefore, simply return the full budget (to keep polling)
until all allocations succeed.

Fixes: 47dd7a540b8a ("net: add support for STMicroelectronics Ethernet controllers.")
Cc: stable@vger.kernel.org
Signed-off-by: Sam Edwards <CFSworks@gmail.com>
---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f98b070073c0..81f764352f3d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -5604,6 +5604,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
 	unsigned int desc_size;
 	struct sk_buff *skb = NULL;
 	struct stmmac_xdp_buff ctx;
+	int budget = limit;
 	int xdp_status = 0;
 	int bufsz;
 
@@ -5870,6 +5871,10 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
 	priv->xstats.rx_dropped += rx_dropped;
 	priv->xstats.rx_errors += rx_errors;
 
+	/* If stmmac_rx_refill() failed, keep trying until it doesn't. */
+	if (unlikely(stmmac_rx_dirty(priv, queue) > 0))
+		return budget;
+
 	return count;
 }
 
-- 
2.52.0



^ permalink raw reply related

* [PATCH v3 1/2] net: stmmac: Prevent NULL deref when RX memory exhausted
From: Sam Edwards @ 2026-03-28 19:12 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
	Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
	Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
	linux-arm-kernel, linux-kernel, Sam Edwards, stable
In-Reply-To: <20260328191233.519950-1-CFSworks@gmail.com>

The CPU receives frames from the MAC through conventional DMA: the CPU
allocates buffers for the MAC, then the MAC fills them and returns
ownership to the CPU. For each hardware RX queue, the CPU and MAC
coordinate through a shared ring array of DMA descriptors: one
descriptor per DMA buffer. Each descriptor includes the buffer's
physical address and a status flag ("OWN") indicating which side owns
the buffer: OWN=0 for CPU, OWN=1 for MAC. The CPU is only allowed to set
the flag and the MAC is only allowed to clear it, and both must move
through the ring in sequence: thus the ring is used for both
"submissions" and "completions."

In the stmmac driver, stmmac_rx() bookmarks its position in the ring
with the `cur_rx` index. The main receive loop in that function checks
for rx_descs[cur_rx].own=0, gives the corresponding buffer to the
network stack (NULLing the pointer), and increments `cur_rx` modulo the
ring size. After the loop exits, stmmac_rx_refill(), which bookmarks its
position with `dirty_rx`, allocates fresh buffers and rearms the
descriptors (setting OWN=1). If it fails any allocation, it simply stops
early (leaving OWN=0) and will retry where it left off when next called.

This means descriptors have a three-stage lifecycle (terms my own):
- `empty` (OWN=1, buffer valid)
- `full` (OWN=0, buffer valid and populated)
- `dirty` (OWN=0, buffer NULL)

But because stmmac_rx() only checks OWN, it confuses `full`/`dirty`. In
the past (see 'Fixes:'), there was a bug where the loop could cycle
`cur_rx` all the way back to the first descriptor it dirtied, resulting
in a NULL dereference when mistaken for `full`. The aforementioned
commit resolved that *specific* failure by capping the loop's iteration
limit at `dma_rx_size - 1`, but this is only a partial fix: if the
previous stmmac_rx_refill() didn't complete, then there are leftover
`dirty` descriptors that the loop might encounter without needing to
cycle fully around. The current code therefore panics (see 'Closes:')
when stmmac_rx_refill() is memory-starved long enough for `cur_rx` to
catch up to `dirty_rx`.

Fix this by further tightening the clamp from `dma_rx_size - 1` to
`dma_rx_size - stmmac_rx_dirty() - 1`, subtracting any remnant dirty
entries and limiting the loop so that `cur_rx` cannot catch back up to
`dirty_rx`. This carries no risk of arithmetic underflow: since the
maximum possible return value of stmmac_rx_dirty() is `dma_rx_size - 1`,
the worst the clamp can do is prevent the loop from running at all.

Fixes: b6cb4541853c7 ("net: stmmac: avoid rx queue overrun")
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221010
Cc: stable@vger.kernel.org
Signed-off-by: Sam Edwards <CFSworks@gmail.com>
---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 6827c99bde8c..f98b070073c0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -5609,7 +5609,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
 
 	dma_dir = page_pool_get_dma_dir(rx_q->page_pool);
 	bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
-	limit = min(priv->dma_conf.dma_rx_size - 1, (unsigned int)limit);
+	limit = min(priv->dma_conf.dma_rx_size - stmmac_rx_dirty(priv, queue) - 1,
+		    (unsigned int)limit);
 
 	if (netif_msg_rx_status(priv)) {
 		void *rx_head;
-- 
2.52.0



^ permalink raw reply related

* [PATCH v3 0/2] stmmac crash/stall fixes when under memory pressure
From: Sam Edwards @ 2026-03-28 19:12 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
	Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
	Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
	linux-arm-kernel, linux-kernel, Sam Edwards

Hi netdev,

This is v3 of my series containing a pair of bugfixes for the stmmac driver's
receive pipeline. These issues occur when stmmac_rx_refill() does not (fully)
succeed, which happens more frequently when free memory is low.

The first patch closes Bugzilla bug #221010 [1], where stmmac_rx() can circle
around to a still-dirty descriptor (with a NULL buffer pointer), mistake it for
a filled descriptor (due to OWN=0), and attempt to dereference the buffer.

In testing that patch, I discovered a second issue: starvation of available RX
buffers causes the NIC to stop sending interrupts; if the driver stops polling,
it will wait indefinitely for an interrupt that will never come. (Note: the
first patch makes this issue more prominent -- mostly because it lets the
system survive long enough to exhibit it -- but doesn't *cause* it.) The second
patch addresses that problem as well.

Both patches are minimal, appropriate for stable, and designated to `net`. My
focus is on small, obviously-correct, easy-to-explain changes: I'll follow up
with another patch/series (something like [2]) for `net-next` that fixes the
ring in a more robust way.

The tx and zc paths seem to have similar low-memory bugs, to be addressed in
separate series.

Regards,
Sam

[1] https://bugzilla.kernel.org/show_bug.cgi?id=221010
[2] https://lore.kernel.org/netdev/20260316021009.262358-4-CFSworks@gmail.com/

v3:
- Rebased on latest net/main
- Changed patch 2 to require that stmmac_rx_refill() *fully* succeeds before
  exiting polling, to reduce the chance of rx drops.
- DID NOT use the CIRC_SPACE() macro as suggested by Russell: I fear that the
  perspective shift (first think of the dirty descriptors as the "work" that
  refill "consumes" -- therefore the "space" is how much stmmac_rx() may loop)
  is too counterintuitive for a stable fix, but I'll do it in v4 if reviewers
  insist.
- Updated the recipients for the series, which was invalidated in v2 due to the
  `Fixes:`
v2: https://lore.kernel.org/netdev/20260319184031.8596-1-CFSworks@gmail.com/T/
- Completely rewrote the commit message of patch 1, now assuming the reader is
  generally familiar with DMA but wholly unfamiliar with the stmmac device
  (thanks Jakub!)
- Added missing `Fixes:` to patch 2
- Moved patch 2's `int budget = limit;` decl per the reverse-xmas-tree rule
- Dropped patch 3: this was a code improvement not appropriate for stable
- Generated the series with --subject-prefix='PATCH net'
v1: https://lore.kernel.org/netdev/20260316021009.262358-1-CFSworks@gmail.com/

Sam Edwards (2):
  net: stmmac: Prevent NULL deref when RX memory exhausted
  net: stmmac: Prevent indefinite RX stall on buffer exhaustion

 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

-- 
2.52.0



^ permalink raw reply

* Re: [PATCH net-next 2/2] net: stmmac: simplify GSO/TSO test in stmmac_xmit()
From: Andrew Lunn @ 2026-03-28 19:01 UTC (permalink / raw)
  To: Russell King (Oracle)
  Cc: Ong Boon Leong, Alexandre Torgue, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, linux-arm-kernel, linux-stm32,
	netdev, Paolo Abeni
In-Reply-To: <acgkM2FGS2h-fPVN@shell.armlinux.org.uk>

> I'm surprised we haven't had reports of brokenness in this area.

Could a self test be written for this case?

      Andrew


^ permalink raw reply

* Re: [PATCH net-next 2/2] net: stmmac: simplify GSO/TSO test in stmmac_xmit()
From: Russell King (Oracle) @ 2026-03-28 18:55 UTC (permalink / raw)
  To: Andrew Lunn, Ong Boon Leong
  Cc: Alexandre Torgue, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, linux-arm-kernel, linux-stm32, netdev,
	Paolo Abeni
In-Reply-To: <acgPJgW9r0l952qu@shell.armlinux.org.uk>

On Sat, Mar 28, 2026 at 05:25:58PM +0000, Russell King (Oracle) wrote:
> On Sat, Mar 28, 2026 at 09:31:46AM +0000, Russell King (Oracle) wrote:
> > On Sat, Mar 28, 2026 at 08:24:37AM +0000, Russell King (Oracle) wrote:
> > > On Fri, Mar 27, 2026 at 09:40:09AM +0000, Russell King (Oracle) wrote:
> > > > The test in stmmac_xmit() to see whether we should pass the skbuff to
> > > > stmmac_tso_xmit() is more complex than it needs to be. This test can
> > > > be simplified by storing the mask of GSO types that we will pass, and
> > > > setting it according to the enabled features.
> > > > 
> > > > Note that "tso" is a mis-nomer since commit b776620651a1 ("net:
> > > > stmmac: Implement UDP Segmentation Offload"). Also note that this
> > > > commit controls both via the TSO feature. We preserve this behaviour
> > > > in this commit.
> > > > 
> > > > Also, this commit unconditionally accessed skb_shinfo(skb)->gso_type
> > > > for all frames, even when skb_is_gso() was false. This access is
> > > > eliminated.
> > > > 
> > > > Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
> > > 
> > > AI review of this patch regurgitates Jakub's point that was discussed.
> > > 
> > > > @@ -3700,7 +3700,7 @@ static int stmmac_hw_setup(struct net_device *dev)
> > > >  	stmmac_set_rings_length(priv);
> > > >  
> > > >  	/* Enable TSO */
> > > > -	if (priv->tso) {
> > > > +	if (priv->gso_enabled_types) {
> > > >  		for (chan = 0; chan < tx_cnt; chan++) {
> > > >  			struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
> > > >  
> > > 
> > > ...
> > > 
> > > > @@ -7828,7 +7834,7 @@ static int __stmmac_dvr_probe(struct device *device,
> > > >  		ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
> > > >  		if (priv->plat->core_type == DWMAC_CORE_GMAC4)
> > > >  			ndev->hw_features |= NETIF_F_GSO_UDP_L4;
> > > > -		priv->tso = true;
> > > > +		stmmac_set_gso_types(priv, true);
> > > 
> > > Clearly, the issue it is regurgitating has been there for a long time
> > > and isn't a new issue introduced by this patch.
> > > 
> > > AI needs to stop doing this, because it is encouraging multiple changes
> > > in a single patch, which is against the normal kernel process.
> > > 
> > > As already pointed out, there are multiple issues with stmmac TSO
> > > support, particularly with glue drivers that enable TSO on some
> > > queues/channels and not others, since netdev core TSO support is
> > > global across all channels.
> > > 
> > > So, won't the AI response in this patch - it's just another pre-
> > > existing issue that needs fixing in a separate patch.
> > 
> > Looking at the TSO vs TBS issue (which precludes the use of TSO on a
> > channel in stmmac) I can't find an obvious reason for this in the
> > available documentation. However, unfortunately, iMX8MP doesn't support
> > TSO, so the TSO bits are elided there, but does support TBS (needing
> > enhanced descriptors to be enabled). STM32MP151 on the other hand
> > supports TSO but not TBS, and thus fails to mention anything about
> > enhanced descriptors or TBS.
> > 
> > When stmmac_enable_tbs() enables TBS, it isn't actually enabling a
> > feature specific bit, but switching the channel to use enhanced
> > descriptor format. This format extends the basic descriptors by
> > placing four extra 32-bit words before the basic descriptor.
> > 
> > Looking at the enhanced normal descriptor format for TDES3, it
> > indicates that the format includes bit 18 in the control field, which
> > is the TSE bit (TCP segmentation enable for this packet.) So, it seems
> > it's not a limitation of the descriptor format.
> > 
> > So, either "TSO and TBS cannot co-exist" is incorrect, or there is a
> > hardware limitation that isn't documented between these two manuals.
> > 
> > One other interesting point is that stmmac_tso_xmit() seems to
> > handle the case where TSO and TBS are enabled on the channel:
> > 
> >                 if (tx_q->tbs & STMMAC_TBS_AVAIL)
> >                         mss_desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
> >                 else
> >                         mss_desc = &tx_q->dma_tx[tx_q->cur_tx];
> > 
> >                 stmmac_set_mss(priv, mss_desc, mss);
> > ...
> >         if (tx_q->tbs & STMMAC_TBS_AVAIL)
> >                 desc = &tx_q->dma_entx[first_entry].basic;
> >         else
> >                 desc = &tx_q->dma_tx[first_entry];
> >         first = desc;
> > 
> > etc.
> > 
> > Avoiding enabling TSO for a TBS channel was added by this commit:
> > 
> > commit 5e6038b88a5718910dd74b949946d9d9cee9a041
> > Author: Ong Boon Leong <boon.leong.ong@intel.com>
> > Date:   Wed Apr 21 17:11:49 2021 +0800
> > 
> >     net: stmmac: fix TSO and TBS feature enabling during driver open
> > 
> >     TSO and TBS cannot co-exist and current implementation requires two
> >     fixes:
> > 
> >      1) stmmac_open() does not need to call stmmac_enable_tbs() because
> >         the MAC is reset in stmmac_init_dma_engine() anyway.
> >      2) Inside stmmac_hw_setup(), we should call stmmac_enable_tso() for
> >         TX Q that is _not_ configured for TBS.
> > 
> >     Fixes: 579a25a854d4 ("net: stmmac: Initial support for TBS")
> >     Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com>
> >     Signed-off-by: David S. Miller <davem@davemloft.net>
> > 
> > which doesn't really explain the background, and leaves all the TBS
> > cruft in the TSO transmit path (nothing like properly updating the
> > driver, eh? No wonder stmmac is such a mess!)
> 
> The more I look at this, the more I'm convinced this commit is
> incorrect, even if it is the case that the hardware doesn't support
> TSO and TBS together.
> 
> When TSO is enabled (NETIF_F_TSO set in the netif's features) then
> the core net layer can submit skbuffs that need to be processed using
> TSO.
> 
> If such a skbuff hits a channel that has TSO disabled (because the
> above commit caused:
> 
> 	stmmac_enable_tso(priv, priv->ioaddr, 1, chan);
> 
> not to be called) then the TSE bit in the transmit control register
> will not be set, thereby disabling TSO on this particular channel.
> 
> However, stmmac_xmit() will still call through to stmmac_tso_xmit()
> which will dutifully populate the transmit ring with descriptors that
> assume TSE has been set in the transmit control register.
> 
> It seems to me _that_ is even more broken than "the hardware doesn't
> support TSO and TBS together" - I have no idea what the stmmac hardware
> does if it encounters descriptors with TSE set but TSE is disabled in
> the transmit control register. My guess would be it would ignore the
> TSE bit in the descriptor and assume that it's one very large packet
> to be sent - and either error out because it's longer than the
> maximum the hardware can support or it will just try to transmit it
> anyway.

Okay, I've found a statement in the stm32mp25xx documentation which
backs up Intel's commit, but that commit is still wrong because it
only half does the job.

However, I think I now have a solution - implementing
.ndo_features_check() which will mask out the NETIF_F_GSO_MASK
features if either the header length is greater than 1023 (the
hardware maximum) or the queue (as returned by
skb_get_queue_mapping(skb)) is for a queue which has TBS available,
and thus has TSO disabled.

I'm surprised we haven't had reports of brokenness in this area.

I think it's time to re-shuffle these patches (plus I think there's
a bit more scope to clean up some of the TSO code which would mean
placing stmmac_set_gso_types() in a different location to keep
all the TSO-related code together. I'll mark this series as
superseded once I have its replacement ready.

-- 
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

* [PATCH v8 2/3] drm/v3d: Allocate all resources before enabling the clock
From: Maíra Canal @ 2026-03-28 18:52 UTC (permalink / raw)
  To: Stefan Wahren, Maxime Ripard, Melissa Wen, Iago Toral Quiroga,
	Dave Stevenson
  Cc: dri-devel, linux-rpi-kernel, linux-arm-kernel,
	Broadcom internal kernel review list, kernel-dev,
	Maíra Canal
In-Reply-To: <20260328-v3d-power-management-v8-0-94336830df5f@igalia.com>

Move all resource allocation operations before actually enabling the
clock, as those operations don't require the GPU to be powered on.

This is a preparation for runtime PM support. The next commit will
move all code related to powering on and initiating the GPU into the
runtime PM resume callback and all resource allocation will happen
before resume().

Reviewed-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Maíra Canal <mcanal@igalia.com>
---
 drivers/gpu/drm/v3d/v3d_drv.c | 93 ++++++++++++++++++++++---------------------
 drivers/gpu/drm/v3d/v3d_drv.h |  1 +
 drivers/gpu/drm/v3d/v3d_gem.c | 18 ++++-----
 drivers/gpu/drm/v3d/v3d_irq.c | 15 +++----
 4 files changed, 62 insertions(+), 65 deletions(-)

diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c
index c5e7c778ec7a1a39d81155f7ed2a7ba811b5e3aa..e57b36f4d81a59d15a223afdc4078ae6456de9a9 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.c
+++ b/drivers/gpu/drm/v3d/v3d_drv.c
@@ -363,14 +363,50 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
 			return ret;
 	}
 
+	if (v3d->ver < V3D_GEN_41) {
+		ret = map_regs(v3d, &v3d->gca_regs, "gca");
+		if (ret)
+			return ret;
+	}
+
+	v3d->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
+	if (IS_ERR(v3d->reset))
+		return dev_err_probe(dev, PTR_ERR(v3d->reset),
+				     "Failed to get reset control\n");
+
+	if (!v3d->reset) {
+		ret = map_regs(v3d, &v3d->bridge_regs, "bridge");
+		if (ret) {
+			dev_err(dev, "Failed to get bridge registers\n");
+			return ret;
+		}
+	}
+
 	v3d->clk = devm_clk_get_optional(dev, NULL);
 	if (IS_ERR(v3d->clk))
 		return dev_err_probe(dev, PTR_ERR(v3d->clk), "Failed to get V3D clock\n");
 
+	ret = v3d_irq_init(v3d);
+	if (ret)
+		return ret;
+
+	v3d_perfmon_init(v3d);
+
+	v3d->mmu_scratch = dma_alloc_wc(dev, 4096, &v3d->mmu_scratch_paddr,
+					GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO);
+	if (!v3d->mmu_scratch) {
+		dev_err(dev, "Failed to allocate MMU scratch page\n");
+		return -ENOMEM;
+	}
+
+	ret = v3d_gem_init(drm);
+	if (ret)
+		goto dma_free;
+
 	ret = clk_prepare_enable(v3d->clk);
 	if (ret) {
 		dev_err(&pdev->dev, "Couldn't enable the V3D clock\n");
-		return ret;
+		goto gem_destroy;
 	}
 
 	v3d_idle_sms(v3d);
@@ -399,44 +435,9 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
 	ident3 = V3D_READ(V3D_HUB_IDENT3);
 	v3d->rev = V3D_GET_FIELD(ident3, V3D_HUB_IDENT3_IPREV);
 
-	v3d_perfmon_init(v3d);
-
-	v3d->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
-	if (IS_ERR(v3d->reset)) {
-		ret = dev_err_probe(dev, PTR_ERR(v3d->reset),
-				    "Failed to get reset control\n");
-		goto clk_disable;
-	}
-
-	if (!v3d->reset) {
-		ret = map_regs(v3d, &v3d->bridge_regs, "bridge");
-		if (ret) {
-			dev_err(dev, "Failed to get bridge registers\n");
-			goto clk_disable;
-		}
-	}
-
-	if (v3d->ver < V3D_GEN_41) {
-		ret = map_regs(v3d, &v3d->gca_regs, "gca");
-		if (ret)
-			goto clk_disable;
-	}
-
-	v3d->mmu_scratch = dma_alloc_wc(dev, 4096, &v3d->mmu_scratch_paddr,
-					GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO);
-	if (!v3d->mmu_scratch) {
-		dev_err(dev, "Failed to allocate MMU scratch page\n");
-		ret = -ENOMEM;
-		goto clk_disable;
-	}
-
-	ret = v3d_gem_init(drm);
-	if (ret)
-		goto dma_free;
-
-	ret = v3d_irq_init(v3d);
-	if (ret)
-		goto gem_destroy;
+	v3d_init_hw_state(v3d);
+	v3d_mmu_set_page_table(v3d);
+	v3d_irq_enable(v3d);
 
 	ret = drm_dev_register(drm, 0);
 	if (ret)
@@ -452,12 +453,13 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
 	drm_dev_unregister(drm);
 irq_disable:
 	v3d_irq_disable(v3d);
+clk_disable:
+	v3d_power_off_sms(v3d);
+	clk_disable_unprepare(v3d->clk);
 gem_destroy:
 	v3d_gem_destroy(drm);
 dma_free:
 	dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr);
-clk_disable:
-	clk_disable_unprepare(v3d->clk);
 	return ret;
 }
 
@@ -471,14 +473,13 @@ static void v3d_platform_drm_remove(struct platform_device *pdev)
 
 	drm_dev_unregister(drm);
 
-	v3d_gem_destroy(drm);
-
-	dma_free_wc(v3d->drm.dev, 4096, v3d->mmu_scratch,
-		    v3d->mmu_scratch_paddr);
-
 	v3d_power_off_sms(v3d);
 
 	clk_disable_unprepare(v3d->clk);
+
+	v3d_gem_destroy(drm);
+
+	dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr);
 }
 
 static struct platform_driver v3d_platform_driver = {
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 6a3cad933439812d78da5797749c020a9bf46402..ebf406b615bb52de9cb98ea8efe941a5787fb4bd 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -565,6 +565,7 @@ struct dma_fence *v3d_fence_create(struct v3d_dev *v3d, enum v3d_queue q);
 
 /* v3d_gem.c */
 extern bool super_pages;
+void v3d_init_hw_state(struct v3d_dev *v3d);
 int v3d_gem_init(struct drm_device *dev);
 void v3d_gem_destroy(struct drm_device *dev);
 void v3d_reset_sms(struct v3d_dev *v3d);
diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c
index 75d9eccd796664e67277c1f83ad59063f164d1da..def6a9612b857a241f6b2e1f601509928e3f9f8b 100644
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -36,13 +36,6 @@ v3d_init_core(struct v3d_dev *v3d, int core)
 	V3D_CORE_WRITE(core, V3D_CTL_L2TFLEND, ~0);
 }
 
-/* Sets invariant state for the HW. */
-static void
-v3d_init_hw_state(struct v3d_dev *v3d)
-{
-	v3d_init_core(v3d, 0);
-}
-
 static void
 v3d_idle_axi(struct v3d_dev *v3d, int core)
 {
@@ -259,6 +252,14 @@ v3d_invalidate_caches(struct v3d_dev *v3d)
 	v3d_invalidate_slices(v3d, 0);
 }
 
+/* Sets invariant state for the HW. */
+void
+v3d_init_hw_state(struct v3d_dev *v3d)
+{
+	v3d_init_core(v3d, 0);
+}
+
+
 static void
 v3d_huge_mnt_init(struct v3d_dev *v3d)
 {
@@ -328,9 +329,6 @@ v3d_gem_init(struct drm_device *dev)
 		goto err_dma_alloc;
 	}
 
-	v3d_init_hw_state(v3d);
-	v3d_mmu_set_page_table(v3d);
-
 	v3d_huge_mnt_init(v3d);
 
 	ret = v3d_sched_init(v3d);
diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c
index c28e74ab5442857031b48bcbd4e43eb48c1e0f07..86efaef2722c3602346b037ba536228b2f368a81 100644
--- a/drivers/gpu/drm/v3d/v3d_irq.c
+++ b/drivers/gpu/drm/v3d/v3d_irq.c
@@ -248,17 +248,10 @@ v3d_hub_irq(int irq, void *arg)
 int
 v3d_irq_init(struct v3d_dev *v3d)
 {
-	int irq, ret, core;
+	int irq, ret;
 
 	INIT_WORK(&v3d->overflow_mem_work, v3d_overflow_mem_work);
 
-	/* Clear any pending interrupts someone might have left around
-	 * for us.
-	 */
-	for (core = 0; core < v3d->cores; core++)
-		V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver));
-	V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver));
-
 	irq = platform_get_irq_optional(v3d_to_pdev(v3d), 1);
 	if (irq == -EPROBE_DEFER)
 		return irq;
@@ -296,7 +289,6 @@ v3d_irq_init(struct v3d_dev *v3d)
 			goto fail;
 	}
 
-	v3d_irq_enable(v3d);
 	return 0;
 
 fail:
@@ -310,6 +302,11 @@ v3d_irq_enable(struct v3d_dev *v3d)
 {
 	int core;
 
+	/* Clear any pending interrupts someone might have left around for us. */
+	for (core = 0; core < v3d->cores; core++)
+		V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver));
+	V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver));
+
 	/* Enable our set of interrupts, masking out any others. */
 	for (core = 0; core < v3d->cores; core++) {
 		V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~V3D_CORE_IRQS(v3d->ver));

-- 
2.53.0



^ permalink raw reply related

* [PATCH v8 3/3] drm/v3d: Introduce Runtime Power Management
From: Maíra Canal @ 2026-03-28 18:52 UTC (permalink / raw)
  To: Stefan Wahren, Maxime Ripard, Melissa Wen, Iago Toral Quiroga,
	Dave Stevenson
  Cc: dri-devel, linux-rpi-kernel, linux-arm-kernel,
	Broadcom internal kernel review list, kernel-dev,
	Maíra Canal
In-Reply-To: <20260328-v3d-power-management-v8-0-94336830df5f@igalia.com>

Commit 90a64adb0876 ("drm/v3d: Get rid of pm code") removed the last
bits of power management code that V3D had, which were actually never
hooked. Therefore, currently, the GPU clock is enabled during probe
and only disabled when removing the driver.

Implement proper power management using the kernel's Runtime PM
framework.

Reviewed-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Maíra Canal <mcanal@igalia.com>
---
 drivers/gpu/drm/v3d/Makefile      |  1 +
 drivers/gpu/drm/v3d/v3d_debugfs.c | 23 ++++++++++-
 drivers/gpu/drm/v3d/v3d_drv.c     | 84 +++++++++++++++++----------------------
 drivers/gpu/drm/v3d/v3d_drv.h     | 17 ++++++++
 drivers/gpu/drm/v3d/v3d_mmu.c     | 10 ++++-
 drivers/gpu/drm/v3d/v3d_perfmon.c | 18 +++++++--
 drivers/gpu/drm/v3d/v3d_power.c   | 69 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/v3d/v3d_submit.c  | 19 +++++++--
 8 files changed, 182 insertions(+), 59 deletions(-)

diff --git a/drivers/gpu/drm/v3d/Makefile b/drivers/gpu/drm/v3d/Makefile
index b7d673f1153bef16db3800e50b2bfaf36bf8871b..601b834e377e8342c6668645112347cca4214024 100644
--- a/drivers/gpu/drm/v3d/Makefile
+++ b/drivers/gpu/drm/v3d/Makefile
@@ -10,6 +10,7 @@ v3d-y := \
 	v3d_irq.o \
 	v3d_mmu.o \
 	v3d_perfmon.o \
+	v3d_power.o \
 	v3d_trace_points.o \
 	v3d_sched.o \
 	v3d_sysfs.o \
diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c
index 89f24eec62a74ec49b28f0b22dbf626ba7a35206..634cc796ba2324dc497694c070f2cfffcc4424c9 100644
--- a/drivers/gpu/drm/v3d/v3d_debugfs.c
+++ b/drivers/gpu/drm/v3d/v3d_debugfs.c
@@ -97,7 +97,11 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused)
 	struct drm_debugfs_entry *entry = m->private;
 	struct drm_device *dev = entry->dev;
 	struct v3d_dev *v3d = to_v3d_dev(dev);
-	int i, core;
+	int i, core, ret;
+
+	ret = v3d_pm_runtime_get(v3d);
+	if (ret)
+		return ret;
 
 	for (i = 0; i < ARRAY_SIZE(v3d_hub_reg_defs); i++) {
 		const struct v3d_reg_def *def = &v3d_hub_reg_defs[i];
@@ -139,6 +143,8 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused)
 		}
 	}
 
+	v3d_pm_runtime_put(v3d);
+
 	return 0;
 }
 
@@ -148,7 +154,11 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused)
 	struct drm_device *dev = entry->dev;
 	struct v3d_dev *v3d = to_v3d_dev(dev);
 	u32 ident0, ident1, ident2, ident3, cores;
-	int core;
+	int core, ret;
+
+	ret = v3d_pm_runtime_get(v3d);
+	if (ret)
+		return ret;
 
 	ident0 = V3D_READ(V3D_HUB_IDENT0);
 	ident1 = V3D_READ(V3D_HUB_IDENT1);
@@ -207,6 +217,8 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused)
 		}
 	}
 
+	v3d_pm_runtime_put(v3d);
+
 	return 0;
 }
 
@@ -234,6 +246,11 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
 	uint32_t cycles;
 	int core = 0;
 	int measure_ms = 1000;
+	int ret;
+
+	ret = v3d_pm_runtime_get(v3d);
+	if (ret)
+		return ret;
 
 	if (v3d->ver >= V3D_GEN_41) {
 		int cycle_count_reg = V3D_PCTR_CYCLE_COUNT(v3d->ver);
@@ -253,6 +270,8 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
 	msleep(measure_ms);
 	cycles = V3D_CORE_READ(core, V3D_PCTR_0_PCTR0);
 
+	v3d_pm_runtime_put(v3d);
+
 	seq_printf(m, "cycles: %d (%d.%d Mhz)\n",
 		   cycles,
 		   cycles / (measure_ms * 1000),
diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c
index e57b36f4d81a59d15a223afdc4078ae6456de9a9..fc81dd1247e33e15cd838cf11ae46d79aaf03976 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.c
+++ b/drivers/gpu/drm/v3d/v3d_drv.c
@@ -59,6 +59,7 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
 		[DRM_V3D_PARAM_V3D_CORE0_IDENT1] = V3D_CTL_IDENT1,
 		[DRM_V3D_PARAM_V3D_CORE0_IDENT2] = V3D_CTL_IDENT2,
 	};
+	int ret;
 
 	if (args->pad != 0)
 		return -EINVAL;
@@ -75,12 +76,19 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
 		if (args->value != 0)
 			return -EINVAL;
 
+		ret = v3d_pm_runtime_get(v3d);
+		if (ret)
+			return ret;
+
 		if (args->param >= DRM_V3D_PARAM_V3D_CORE0_IDENT0 &&
 		    args->param <= DRM_V3D_PARAM_V3D_CORE0_IDENT2) {
 			args->value = V3D_CORE_READ(0, offset);
 		} else {
 			args->value = V3D_READ(offset);
 		}
+
+		v3d_pm_runtime_put(v3d);
+
 		return 0;
 	}
 
@@ -290,36 +298,6 @@ static const struct of_device_id v3d_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, v3d_of_match);
 
-static void
-v3d_idle_sms(struct v3d_dev *v3d)
-{
-	if (v3d->ver < V3D_GEN_71)
-		return;
-
-	V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_CLEAR_POWER_OFF);
-
-	if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
-				    V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) {
-		drm_err(&v3d->drm, "Failed to power up SMS\n");
-	}
-
-	v3d_reset_sms(v3d);
-}
-
-static void
-v3d_power_off_sms(struct v3d_dev *v3d)
-{
-	if (v3d->ver < V3D_GEN_71)
-		return;
-
-	V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_POWER_OFF);
-
-	if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
-				    V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) {
-		drm_err(&v3d->drm, "Failed to power off SMS\n");
-	}
-}
-
 static int
 map_regs(struct v3d_dev *v3d, void __iomem **regs, const char *name)
 {
@@ -403,19 +381,26 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
 	if (ret)
 		goto dma_free;
 
-	ret = clk_prepare_enable(v3d->clk);
-	if (ret) {
-		dev_err(&pdev->dev, "Couldn't enable the V3D clock\n");
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
 		goto gem_destroy;
-	}
 
-	v3d_idle_sms(v3d);
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		goto gem_destroy;
+
+	/* If PM is disabled, we need to call v3d_power_resume() manually. */
+	if (!IS_ENABLED(CONFIG_PM)) {
+		ret = v3d_power_resume(dev);
+		if (ret)
+			goto gem_destroy;
+	}
 
 	mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO);
 	mask = DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH));
 	ret = dma_set_mask_and_coherent(dev, mask);
 	if (ret)
-		goto clk_disable;
+		goto runtime_pm_put;
 
 	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
 
@@ -435,27 +420,26 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
 	ident3 = V3D_READ(V3D_HUB_IDENT3);
 	v3d->rev = V3D_GET_FIELD(ident3, V3D_HUB_IDENT3_IPREV);
 
-	v3d_init_hw_state(v3d);
-	v3d_mmu_set_page_table(v3d);
-	v3d_irq_enable(v3d);
+	pm_runtime_set_autosuspend_delay(dev, 100);
+	pm_runtime_use_autosuspend(dev);
 
 	ret = drm_dev_register(drm, 0);
 	if (ret)
-		goto irq_disable;
+		goto runtime_pm_put;
 
 	ret = v3d_sysfs_init(dev);
 	if (ret)
 		goto drm_unregister;
 
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
 	return 0;
 
 drm_unregister:
 	drm_dev_unregister(drm);
-irq_disable:
-	v3d_irq_disable(v3d);
-clk_disable:
-	v3d_power_off_sms(v3d);
-	clk_disable_unprepare(v3d->clk);
+runtime_pm_put:
+	pm_runtime_put_sync_suspend(dev);
 gem_destroy:
 	v3d_gem_destroy(drm);
 dma_free:
@@ -473,21 +457,27 @@ static void v3d_platform_drm_remove(struct platform_device *pdev)
 
 	drm_dev_unregister(drm);
 
-	v3d_power_off_sms(v3d);
+	pm_runtime_suspend(dev);
 
-	clk_disable_unprepare(v3d->clk);
+	/* If PM is disabled, we need to call v3d_power_suspend() manually. */
+	if (!IS_ENABLED(CONFIG_PM))
+		v3d_power_suspend(dev);
 
 	v3d_gem_destroy(drm);
 
 	dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr);
 }
 
+static DEFINE_RUNTIME_DEV_PM_OPS(v3d_pm_ops, v3d_power_suspend,
+				 v3d_power_resume, NULL);
+
 static struct platform_driver v3d_platform_driver = {
 	.probe		= v3d_platform_drm_probe,
 	.remove		= v3d_platform_drm_remove,
 	.driver		= {
 		.name	= "v3d",
 		.of_match_table = v3d_of_match,
+		.pm = pm_ptr(&v3d_pm_ops),
 	},
 };
 
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index ebf406b615bb52de9cb98ea8efe941a5787fb4bd..4ebe175a8c6b0016d087388ce02cd35a8ae65a13 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -3,6 +3,7 @@
 
 #include <linux/delay.h>
 #include <linux/mutex.h>
+#include <linux/pm_runtime.h>
 #include <linux/spinlock_types.h>
 #include <linux/workqueue.h>
 
@@ -324,6 +325,8 @@ struct v3d_job {
 
 	/* Callback for the freeing of the job on refcount going to 0. */
 	void (*free)(struct kref *ref);
+
+	bool has_pm_ref;
 };
 
 struct v3d_bin_job {
@@ -597,6 +600,20 @@ int v3d_mmu_set_page_table(struct v3d_dev *v3d);
 void v3d_mmu_insert_ptes(struct v3d_bo *bo);
 void v3d_mmu_remove_ptes(struct v3d_bo *bo);
 
+/* v3d_power.c */
+int v3d_power_suspend(struct device *dev);
+int v3d_power_resume(struct device *dev);
+
+static __always_inline int v3d_pm_runtime_get(struct v3d_dev *v3d)
+{
+	return pm_runtime_resume_and_get(v3d->drm.dev);
+}
+
+static __always_inline int v3d_pm_runtime_put(struct v3d_dev *v3d)
+{
+	return pm_runtime_put_autosuspend(v3d->drm.dev);
+}
+
 /* v3d_sched.c */
 void v3d_timestamp_query_info_free(struct v3d_timestamp_query_info *query_info,
 				   unsigned int count);
diff --git a/drivers/gpu/drm/v3d/v3d_mmu.c b/drivers/gpu/drm/v3d/v3d_mmu.c
index c513a393c0313772650fd6d7236127b2dc4101d9..630c64e51d2f2ad30e59fa2b175487efe0bfba49 100644
--- a/drivers/gpu/drm/v3d/v3d_mmu.c
+++ b/drivers/gpu/drm/v3d/v3d_mmu.c
@@ -39,7 +39,11 @@ static bool v3d_mmu_is_aligned(u32 page, u32 page_address, size_t alignment)
 
 int v3d_mmu_flush_all(struct v3d_dev *v3d)
 {
-	int ret;
+	int ret = 0;
+
+	/* Flush the PTs only if we're already awake */
+	if (!pm_runtime_get_if_active(v3d->drm.dev))
+		return 0;
 
 	V3D_WRITE(V3D_MMUC_CONTROL, V3D_MMUC_CONTROL_FLUSH |
 		  V3D_MMUC_CONTROL_ENABLE);
@@ -48,7 +52,7 @@ int v3d_mmu_flush_all(struct v3d_dev *v3d)
 			 V3D_MMUC_CONTROL_FLUSHING), 100);
 	if (ret) {
 		dev_err(v3d->drm.dev, "MMUC flush wait idle failed\n");
-		return ret;
+		goto pm_put;
 	}
 
 	V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL) |
@@ -59,6 +63,8 @@ int v3d_mmu_flush_all(struct v3d_dev *v3d)
 	if (ret)
 		dev_err(v3d->drm.dev, "MMU TLB clear wait idle failed\n");
 
+pm_put:
+	v3d_pm_runtime_put(v3d);
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c
index 8e0249580bbacac507b2d7c0bcac37ef19c1a54e..02451fc09dbbf6d33640000249786e2836732647 100644
--- a/drivers/gpu/drm/v3d/v3d_perfmon.c
+++ b/drivers/gpu/drm/v3d/v3d_perfmon.c
@@ -232,6 +232,9 @@ void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
 	if (WARN_ON_ONCE(!perfmon || v3d->active_perfmon))
 		return;
 
+	if (!pm_runtime_get_if_active(v3d->drm.dev))
+		return;
+
 	ncounters = perfmon->ncounters;
 	mask = GENMASK(ncounters - 1, 0);
 
@@ -257,6 +260,8 @@ void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
 	V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
 
 	v3d->active_perfmon = perfmon;
+
+	v3d_pm_runtime_put(v3d);
 }
 
 void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
@@ -268,10 +273,11 @@ void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
 		return;
 
 	mutex_lock(&perfmon->lock);
-	if (perfmon != v3d->active_perfmon) {
-		mutex_unlock(&perfmon->lock);
-		return;
-	}
+	if (perfmon != v3d->active_perfmon)
+		goto out;
+
+	if (!pm_runtime_get_if_active(v3d->drm.dev))
+		goto out_clear;
 
 	if (capture)
 		for (i = 0; i < perfmon->ncounters; i++)
@@ -279,7 +285,11 @@ void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
 
 	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
 
+	v3d_pm_runtime_put(v3d);
+
+out_clear:
 	v3d->active_perfmon = NULL;
+out:
 	mutex_unlock(&perfmon->lock);
 }
 
diff --git a/drivers/gpu/drm/v3d/v3d_power.c b/drivers/gpu/drm/v3d/v3d_power.c
new file mode 100644
index 0000000000000000000000000000000000000000..2b7e639d5da30c798ccc12cc1559e005915e2d8d
--- /dev/null
+++ b/drivers/gpu/drm/v3d/v3d_power.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Raspberry Pi */
+
+#include <linux/clk.h>
+
+#include <drm/drm_print.h>
+
+#include "v3d_drv.h"
+#include "v3d_regs.h"
+
+static void
+v3d_resume_sms(struct v3d_dev *v3d)
+{
+	if (v3d->ver < V3D_GEN_71)
+		return;
+
+	V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_CLEAR_POWER_OFF);
+
+	if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
+				    V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) {
+		drm_err(&v3d->drm, "Failed to power up SMS\n");
+	}
+
+	v3d_reset_sms(v3d);
+}
+
+static void
+v3d_suspend_sms(struct v3d_dev *v3d)
+{
+	if (v3d->ver < V3D_GEN_71)
+		return;
+
+	V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_POWER_OFF);
+
+	if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
+				    V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) {
+		drm_err(&v3d->drm, "Failed to power off SMS\n");
+	}
+}
+
+int v3d_power_suspend(struct device *dev)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+	struct v3d_dev *v3d = to_v3d_dev(drm);
+
+	v3d_irq_disable(v3d);
+	v3d_suspend_sms(v3d);
+	clk_disable_unprepare(v3d->clk);
+
+	return 0;
+}
+
+int v3d_power_resume(struct device *dev)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+	struct v3d_dev *v3d = to_v3d_dev(drm);
+	int ret;
+
+	ret = clk_prepare_enable(v3d->clk);
+	if (ret)
+		return ret;
+
+	v3d_resume_sms(v3d);
+	v3d_init_hw_state(v3d);
+	v3d_mmu_set_page_table(v3d);
+	v3d_irq_enable(v3d);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c
index 8f061b6a05c6aa76ea5513407ebf3c0ce80b8256..f75da2e3533e236821818924f91ec602950af85a 100644
--- a/drivers/gpu/drm/v3d/v3d_submit.c
+++ b/drivers/gpu/drm/v3d/v3d_submit.c
@@ -106,6 +106,9 @@ v3d_job_free(struct kref *ref)
 	v3d_stats_put(job->client_stats);
 	v3d_stats_put(job->global_stats);
 
+	if (job->has_pm_ref)
+		v3d_pm_runtime_put(job->v3d);
+
 	kfree(job);
 }
 
@@ -187,13 +190,13 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
 				if (copy_from_user(&in, handle++, sizeof(in))) {
 					ret = -EFAULT;
 					drm_dbg(&v3d->drm, "Failed to copy wait dep handle.\n");
-					goto fail_deps;
+					goto fail_job_init;
 				}
 				ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in.handle, 0);
 
 				// TODO: Investigate why this was filtered out for the IOCTL.
 				if (ret && ret != -ENOENT)
-					goto fail_deps;
+					goto fail_job_init;
 			}
 		}
 	} else {
@@ -201,7 +204,15 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
 
 		// TODO: Investigate why this was filtered out for the IOCTL.
 		if (ret && ret != -ENOENT)
-			goto fail_deps;
+			goto fail_job_init;
+	}
+
+	/* CPU jobs don't require hardware resources */
+	if (queue != V3D_CPU) {
+		ret = v3d_pm_runtime_get(v3d);
+		if (ret)
+			goto fail_job_init;
+		job->has_pm_ref = true;
 	}
 
 	kref_init(&job->refcount);
@@ -211,7 +222,7 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
 
 	return 0;
 
-fail_deps:
+fail_job_init:
 	drm_sched_job_cleanup(&job->base);
 	return ret;
 }

-- 
2.53.0



^ permalink raw reply related

* [PATCH v8 0/3] Power Management for Raspberry Pi V3D GPU
From: Maíra Canal @ 2026-03-28 18:52 UTC (permalink / raw)
  To: Stefan Wahren, Maxime Ripard, Melissa Wen, Iago Toral Quiroga,
	Dave Stevenson
  Cc: dri-devel, linux-rpi-kernel, linux-arm-kernel,
	Broadcom internal kernel review list, kernel-dev, Philipp Zabel,
	Maíra Canal

This series introduces Runtime Power Management (PM) support for the
Raspberry Pi V3D GPU.

Currently, the V3D clock remains enabled for the entire system uptime,
even when the GPU is idle. With the introduction of Runtime PM, the
clock can now be disabled during idle periods. For example, with this
series applied to a Raspberry Pi 5, if we check `vcgencmd measure_clock
v3d`, we get:

(idle)

$ vcgencmd measure_clock v3d
frequency(0)=0

(running glmark2)

$ vcgencmd measure_clock v3d
frequency(0)=960016128

I left Melissa's R-b in the last patch even with the changes, as I
considered them uncontroversial. However, Melissa, please let me know if
you would like to have your tag removed or you would like to request more
changes to the last patch.

In the case of no comments, I plan to merge this series by the end of
next week.

Best regards,
- Maíra

v1 -> v2: https://lore.kernel.org/r/20250728-v3d-power-management-v1-0-780f922b1048@igalia.com

- [1/5] NEW PATCH: "clk: bcm: rpi: Add missing logs if firmware fails" (Stefan Wahren)
- [2/5] Remove the "Fixes:" tag (Stefan Wahren)
- [2/5] dev_err_ratelimited() instead of dev_err() (Stefan Wahren)
- [2/5] Instead of logging the clock ID, use clk_hw_get_name(hw) to log the name (Stefan Wahren)
- [2/5] Add a newline character at the end of the log message (Stefan Wahren)
- [2/5] Use CLK_IS_CRITICAL for all clocks that can't be disabled (Maxime Ripard)
- [3/5] NEW PATCH: "clk: bcm: rpi: Maximize V3D clock"
- [4/5] Use devm_reset_control_get_optional_exclusive() (Philipp Zabel)
- [4/5] Make sure that resources are cleaned in the inverse order of allocation (Philipp Zabel)

v2 -> v3: https://lore.kernel.org/r/20250731-v3d-power-management-v2-0-032d56b01964@igalia.com

- Rebased on top of drm-misc-next
- Patches "[PATCH v2 1/5] clk: bcm: rpi: Add missing logs if firmware
  fails", "[PATCH v2 2/5] clk: bcm: rpi: Turn firmware clock on/off when
  preparing/unpreparing", and "[PATCH v2 3/5] clk: bcm: rpi: Maximize
  V3D clock" were applied to clk-next.
- [1/4] NEW PATCH: "clk: bcm: rpi: Let V3D consumers manage clock rate"
- [2/4] NEW PATCH: "clk: bcm: rpi: Mark PIXEL_CLK and HEVC_CLK as CLK_IGNORE_UNUSED"
- [3/4] Add Philipp's R-b (Philipp Zabel)
- [4/4] s/DRM_ERROR/drm_err
- [4/4] Set the clock rate to 0 during suspend and to the maximum rate during resume

v3 -> v4: https://lore.kernel.org/r/20260116-v3d-power-management-v3-0-4e1874e81dd6@igalia.com

- Rebased on top of drm-misc-next
- [1/6, 3/6] Add Melissa's A-b (Melissa Wen)
- [2/6] NEW PATCH: "clk: bcm: rpi: Add a comment about RPI_FIRMWARE_SET_CLOCK_STATE
  behavior" (Stefan Wahren)
- [4/6] NEW PATCH: "drm/v3d: Use devm_reset_control_get_optional_exclusive()" (Melissa Wen)
- [5/6] Include more context in the commit message (Melissa Wen)
- [5/6, 6/6] Instead of creating the function v3d_gem_allocate(), use v3d_gem_init()
  and move HW initialization out of it (Melissa Wen)

v4 -> v5: https://lore.kernel.org/r/20260126-v3d-power-management-v4-0-caf2df16d4e2@igalia.com

- [2/7] Add Stefan's A-b (Stefan Wahren)
- [2/7, 5/7, 6/7] Add Melissa's R-b (Melissa Wen)
- [4/7] NEW PATCH: "pmdomain: bcm: bcm2835-power: Increase ASB control timeout"
- [7/7] Remove redundant pm_runtime_mark_last_busy() from v3d_pm_runtime_put()
- [7/7] Use pm_runtime_get_if_active() in v3d_mmu_flush_all() instead of
  pm_runtime_get_noresume() + pm_runtime_active()
- [7/7] Add missing PM runtime calls to v3d_perfmon_start() and v3d_perfmon_stop()

v5 -> v6: https://lore.kernel.org/r/20260213-v3d-power-management-v5-0-7a8b381eb379@igalia.com

- [1/6] NEW PATCH: "clk: bcm: rpi: Manage clock rate in prepare/unprepare callbacks" (Maxime Ripard)
    - Replaces "clk: bcm: rpi: Let V3D consumers manage clock rate" and
      "clk: bcm: rpi: Add a comment about RPI_FIRMWARE_SET_CLOCK_STATE
      behavior" 
- [6/6] Stop setting min and max clock rates directly in v3d (Maxime Ripard)

v6 -> v7: https://lore.kernel.org/r/20260218-v3d-power-management-v6-0-40683fd39865@igalia.com

- Drop commit "[PATCH v6 2/6] clk: bcm: rpi: Mark PIXEL_CLK and HEVC_CLK as CLK_IGNORE_UNUSED"
- [1/5] Add comment about why is okay to set the clock's rate at prepare/unprepare (Maxime Ripard)
- [1/5] Use clk_hw_get_rate_range() (Maxime Ripard)
- [2/5] Add Stefan's R-b and stable tag (Stefan Wahren)
- [3/5] Add Philipp's R-b (Philipp Zabel)
- [5/5] Keep the alphabetical order in the Makefile (Stefan Wahren)
- [5/5] Propagate `reset_control_assert()` error (Stefan Wahren)
- [5/5] Add v3d_init_hw_state() before v3d_mmu_set_page_table()
- [5/5] Stop any active perfmon during suspend

v7 -> v8: https://lore.kernel.org/r/20260312-v3d-power-management-v7-0-9f006a1d4c55@igalia.com

- "[PATCH v7 2/5] pmdomain: bcm: bcm2835-power: Increase ASB control timeout"
  was merged through -fixes (Thanks Stefan and Ulf!)
- "[PATCH v7 1/5] clk: bcm: rpi: Manage clock rate in prepare/unprepare callbacks"
  was merged through clk-fixes (Thanks Maxime and Stephen!)
- Rebased on top of drm-misc-next.
- [3/3] The perfmon will never be active during a suspend call (Melissa Wen)
- [3/3] No need to call reset_control_(de)assert() as bcm2835-power doesn't
  implement these hooks.
- [3/3] Increase the autosuspend delay to 100ms, as 50ms was too short for RPi 4.
- [3/3] Add Melissa's R-b (Melissa Wen)

---
Maíra Canal (3):
      drm/v3d: Use devm_reset_control_get_optional_exclusive()
      drm/v3d: Allocate all resources before enabling the clock
      drm/v3d: Introduce Runtime Power Management

 drivers/gpu/drm/v3d/Makefile      |   1 +
 drivers/gpu/drm/v3d/v3d_debugfs.c |  23 +++++-
 drivers/gpu/drm/v3d/v3d_drv.c     | 160 ++++++++++++++++++--------------------
 drivers/gpu/drm/v3d/v3d_drv.h     |  18 +++++
 drivers/gpu/drm/v3d/v3d_gem.c     |  18 ++---
 drivers/gpu/drm/v3d/v3d_irq.c     |  15 ++--
 drivers/gpu/drm/v3d/v3d_mmu.c     |  10 ++-
 drivers/gpu/drm/v3d/v3d_perfmon.c |  18 ++++-
 drivers/gpu/drm/v3d/v3d_power.c   |  69 ++++++++++++++++
 drivers/gpu/drm/v3d/v3d_submit.c  |  19 ++++-
 10 files changed, 235 insertions(+), 116 deletions(-)
---
base-commit: dac97a6bd6c4a24e60a147cb2cf750eddb7594a8
change-id: 20250728-v3d-power-management-eebb2024dc96



^ permalink raw reply

* [PATCH v8 1/3] drm/v3d: Use devm_reset_control_get_optional_exclusive()
From: Maíra Canal @ 2026-03-28 18:52 UTC (permalink / raw)
  To: Stefan Wahren, Maxime Ripard, Melissa Wen, Iago Toral Quiroga,
	Dave Stevenson
  Cc: dri-devel, linux-rpi-kernel, linux-arm-kernel,
	Broadcom internal kernel review list, kernel-dev, Philipp Zabel,
	Maíra Canal
In-Reply-To: <20260328-v3d-power-management-v8-0-94336830df5f@igalia.com>

Simplify optional reset handling by using the function
devm_reset_control_get_optional_exclusive().

Reviewed-by: Melissa Wen <mwen@igalia.com>
Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Maíra Canal <mcanal@igalia.com>
---
 drivers/gpu/drm/v3d/v3d_drv.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c
index 4b441afcb602de08bd193d57649121e44ab31f2a..c5e7c778ec7a1a39d81155f7ed2a7ba811b5e3aa 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.c
+++ b/drivers/gpu/drm/v3d/v3d_drv.c
@@ -401,18 +401,17 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
 
 	v3d_perfmon_init(v3d);
 
-	v3d->reset = devm_reset_control_get_exclusive(dev, NULL);
+	v3d->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
 	if (IS_ERR(v3d->reset)) {
-		ret = PTR_ERR(v3d->reset);
+		ret = dev_err_probe(dev, PTR_ERR(v3d->reset),
+				    "Failed to get reset control\n");
+		goto clk_disable;
+	}
 
-		if (ret == -EPROBE_DEFER)
-			goto clk_disable;
-
-		v3d->reset = NULL;
+	if (!v3d->reset) {
 		ret = map_regs(v3d, &v3d->bridge_regs, "bridge");
 		if (ret) {
-			dev_err(dev,
-				"Failed to get reset control or bridge regs\n");
+			dev_err(dev, "Failed to get bridge registers\n");
 			goto clk_disable;
 		}
 	}

-- 
2.53.0



^ permalink raw reply related

* Re: [PATCH v4 5/6] drm/mediatek: Support multiple CCORR component
From: kernel test robot @ 2026-03-28 18:43 UTC (permalink / raw)
  To: Jay Liu, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: oe-kbuild-all, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, Jay Liu
In-Reply-To: <20260324125315.4715-6-jay.liu@mediatek.com>

Hi Jay,

kernel test robot noticed the following build errors:

[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on drm/drm-next pza/reset/next linus/master v7.0-rc5 next-20260327]
[cannot apply to pza/imx-drm/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jay-Liu/dt-bindings-display-mediatek-gamma-Add-support-for-MT8196/20260328-083359
base:   https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link:    https://lore.kernel.org/r/20260324125315.4715-6-jay.liu%40mediatek.com
patch subject: [PATCH v4 5/6] drm/mediatek: Support multiple CCORR component
config: arm64-defconfig (https://download.01.org/0day-ci/archive/20260329/202603290249.ZJ6ioqey-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260329/202603290249.ZJ6ioqey-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603290249.ZJ6ioqey-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/gpu/drm/mediatek/mtk_ddp_comp.c:461:10: error: 'DDP_COMPONENT_CCORR0' undeclared here (not in a function); did you mean 'DDP_COMPONENT_CCORR'?
     461 |         [DDP_COMPONENT_CCORR0]          = { MTK_DISP_CCORR,             0, &ddp_ccorr },
         |          ^~~~~~~~~~~~~~~~~~~~
         |          DDP_COMPONENT_CCORR
>> drivers/gpu/drm/mediatek/mtk_ddp_comp.c:461:10: error: array index in initializer not of integer type
   drivers/gpu/drm/mediatek/mtk_ddp_comp.c:461:10: note: (near initialization for 'mtk_ddp_matches')
>> drivers/gpu/drm/mediatek/mtk_ddp_comp.c:462:10: error: 'DDP_COMPONENT_CCORR1' undeclared here (not in a function); did you mean 'DDP_COMPONENT_CCORR'?
     462 |         [DDP_COMPONENT_CCORR1]          = { MTK_DISP_CCORR,             1, &ddp_ccorr },
         |          ^~~~~~~~~~~~~~~~~~~~
         |          DDP_COMPONENT_CCORR
   drivers/gpu/drm/mediatek/mtk_ddp_comp.c:462:10: error: array index in initializer not of integer type
   drivers/gpu/drm/mediatek/mtk_ddp_comp.c:462:10: note: (near initialization for 'mtk_ddp_matches')
   drivers/gpu/drm/mediatek/mtk_ddp_comp.c:463:43: warning: initialized field overwritten [-Woverride-init]
     463 |         [DDP_COMPONENT_COLOR0]          = { MTK_DISP_COLOR,             0, &ddp_color },
         |                                           ^
   drivers/gpu/drm/mediatek/mtk_ddp_comp.c:463:43: note: (near initialization for 'mtk_ddp_matches[4]')


vim +461 drivers/gpu/drm/mediatek/mtk_ddp_comp.c

   456	
   457	static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_DRM_ID_MAX] = {
   458		[DDP_COMPONENT_AAL0]		= { MTK_DISP_AAL,		0, &ddp_aal },
   459		[DDP_COMPONENT_AAL1]		= { MTK_DISP_AAL,		1, &ddp_aal },
   460		[DDP_COMPONENT_BLS]		= { MTK_DISP_BLS,		0, NULL },
 > 461		[DDP_COMPONENT_CCORR0]		= { MTK_DISP_CCORR,		0, &ddp_ccorr },
 > 462		[DDP_COMPONENT_CCORR1]		= { MTK_DISP_CCORR,		1, &ddp_ccorr },
   463		[DDP_COMPONENT_COLOR0]		= { MTK_DISP_COLOR,		0, &ddp_color },
   464		[DDP_COMPONENT_COLOR1]		= { MTK_DISP_COLOR,		1, &ddp_color },
   465		[DDP_COMPONENT_DITHER0]		= { MTK_DISP_DITHER,		0, &ddp_dither },
   466		[DDP_COMPONENT_DP_INTF0]	= { MTK_DP_INTF,		0, &ddp_dpi },
   467		[DDP_COMPONENT_DP_INTF1]	= { MTK_DP_INTF,		1, &ddp_dpi },
   468		[DDP_COMPONENT_DPI0]		= { MTK_DPI,			0, &ddp_dpi },
   469		[DDP_COMPONENT_DPI1]		= { MTK_DPI,			1, &ddp_dpi },
   470		[DDP_COMPONENT_DRM_OVL_ADAPTOR]	= { MTK_DISP_OVL_ADAPTOR,	0, &ddp_ovl_adaptor },
   471		[DDP_COMPONENT_DSC0]		= { MTK_DISP_DSC,		0, &ddp_dsc },
   472		[DDP_COMPONENT_DSC1]		= { MTK_DISP_DSC,		1, &ddp_dsc },
   473		[DDP_COMPONENT_DSI0]		= { MTK_DSI,			0, &ddp_dsi },
   474		[DDP_COMPONENT_DSI1]		= { MTK_DSI,			1, &ddp_dsi },
   475		[DDP_COMPONENT_DSI2]		= { MTK_DSI,			2, &ddp_dsi },
   476		[DDP_COMPONENT_DSI3]		= { MTK_DSI,			3, &ddp_dsi },
   477		[DDP_COMPONENT_GAMMA]		= { MTK_DISP_GAMMA,		0, &ddp_gamma },
   478		[DDP_COMPONENT_MERGE0]		= { MTK_DISP_MERGE,		0, &ddp_merge },
   479		[DDP_COMPONENT_MERGE1]		= { MTK_DISP_MERGE,		1, &ddp_merge },
   480		[DDP_COMPONENT_MERGE2]		= { MTK_DISP_MERGE,		2, &ddp_merge },
   481		[DDP_COMPONENT_MERGE3]		= { MTK_DISP_MERGE,		3, &ddp_merge },
   482		[DDP_COMPONENT_MERGE4]		= { MTK_DISP_MERGE,		4, &ddp_merge },
   483		[DDP_COMPONENT_MERGE5]		= { MTK_DISP_MERGE,		5, &ddp_merge },
   484		[DDP_COMPONENT_OD0]		= { MTK_DISP_OD,		0, &ddp_od },
   485		[DDP_COMPONENT_OD1]		= { MTK_DISP_OD,		1, &ddp_od },
   486		[DDP_COMPONENT_OVL0]		= { MTK_DISP_OVL,		0, &ddp_ovl },
   487		[DDP_COMPONENT_OVL1]		= { MTK_DISP_OVL,		1, &ddp_ovl },
   488		[DDP_COMPONENT_OVL_2L0]		= { MTK_DISP_OVL_2L,		0, &ddp_ovl },
   489		[DDP_COMPONENT_OVL_2L1]		= { MTK_DISP_OVL_2L,		1, &ddp_ovl },
   490		[DDP_COMPONENT_OVL_2L2]		= { MTK_DISP_OVL_2L,		2, &ddp_ovl },
   491		[DDP_COMPONENT_POSTMASK0]	= { MTK_DISP_POSTMASK,		0, &ddp_postmask },
   492		[DDP_COMPONENT_PWM0]		= { MTK_DISP_PWM,		0, NULL },
   493		[DDP_COMPONENT_PWM1]		= { MTK_DISP_PWM,		1, NULL },
   494		[DDP_COMPONENT_PWM2]		= { MTK_DISP_PWM,		2, NULL },
   495		[DDP_COMPONENT_RDMA0]		= { MTK_DISP_RDMA,		0, &ddp_rdma },
   496		[DDP_COMPONENT_RDMA1]		= { MTK_DISP_RDMA,		1, &ddp_rdma },
   497		[DDP_COMPONENT_RDMA2]		= { MTK_DISP_RDMA,		2, &ddp_rdma },
   498		[DDP_COMPONENT_RDMA4]		= { MTK_DISP_RDMA,		4, &ddp_rdma },
   499		[DDP_COMPONENT_UFOE]		= { MTK_DISP_UFOE,		0, &ddp_ufoe },
   500		[DDP_COMPONENT_WDMA0]		= { MTK_DISP_WDMA,		0, NULL },
   501		[DDP_COMPONENT_WDMA1]		= { MTK_DISP_WDMA,		1, NULL },
   502	};
   503	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply

* Re: [PATCH net-next 2/2] net: stmmac: simplify GSO/TSO test in stmmac_xmit()
From: Russell King (Oracle) @ 2026-03-28 17:25 UTC (permalink / raw)
  To: Andrew Lunn, Ong Boon Leong
  Cc: Alexandre Torgue, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, linux-arm-kernel, linux-stm32, netdev,
	Paolo Abeni
In-Reply-To: <acegAqUb-Dzy87d8@shell.armlinux.org.uk>

On Sat, Mar 28, 2026 at 09:31:46AM +0000, Russell King (Oracle) wrote:
> On Sat, Mar 28, 2026 at 08:24:37AM +0000, Russell King (Oracle) wrote:
> > On Fri, Mar 27, 2026 at 09:40:09AM +0000, Russell King (Oracle) wrote:
> > > The test in stmmac_xmit() to see whether we should pass the skbuff to
> > > stmmac_tso_xmit() is more complex than it needs to be. This test can
> > > be simplified by storing the mask of GSO types that we will pass, and
> > > setting it according to the enabled features.
> > > 
> > > Note that "tso" is a mis-nomer since commit b776620651a1 ("net:
> > > stmmac: Implement UDP Segmentation Offload"). Also note that this
> > > commit controls both via the TSO feature. We preserve this behaviour
> > > in this commit.
> > > 
> > > Also, this commit unconditionally accessed skb_shinfo(skb)->gso_type
> > > for all frames, even when skb_is_gso() was false. This access is
> > > eliminated.
> > > 
> > > Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
> > 
> > AI review of this patch regurgitates Jakub's point that was discussed.
> > 
> > > @@ -3700,7 +3700,7 @@ static int stmmac_hw_setup(struct net_device *dev)
> > >  	stmmac_set_rings_length(priv);
> > >  
> > >  	/* Enable TSO */
> > > -	if (priv->tso) {
> > > +	if (priv->gso_enabled_types) {
> > >  		for (chan = 0; chan < tx_cnt; chan++) {
> > >  			struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
> > >  
> > 
> > ...
> > 
> > > @@ -7828,7 +7834,7 @@ static int __stmmac_dvr_probe(struct device *device,
> > >  		ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
> > >  		if (priv->plat->core_type == DWMAC_CORE_GMAC4)
> > >  			ndev->hw_features |= NETIF_F_GSO_UDP_L4;
> > > -		priv->tso = true;
> > > +		stmmac_set_gso_types(priv, true);
> > 
> > Clearly, the issue it is regurgitating has been there for a long time
> > and isn't a new issue introduced by this patch.
> > 
> > AI needs to stop doing this, because it is encouraging multiple changes
> > in a single patch, which is against the normal kernel process.
> > 
> > As already pointed out, there are multiple issues with stmmac TSO
> > support, particularly with glue drivers that enable TSO on some
> > queues/channels and not others, since netdev core TSO support is
> > global across all channels.
> > 
> > So, won't the AI response in this patch - it's just another pre-
> > existing issue that needs fixing in a separate patch.
> 
> Looking at the TSO vs TBS issue (which precludes the use of TSO on a
> channel in stmmac) I can't find an obvious reason for this in the
> available documentation. However, unfortunately, iMX8MP doesn't support
> TSO, so the TSO bits are elided there, but does support TBS (needing
> enhanced descriptors to be enabled). STM32MP151 on the other hand
> supports TSO but not TBS, and thus fails to mention anything about
> enhanced descriptors or TBS.
> 
> When stmmac_enable_tbs() enables TBS, it isn't actually enabling a
> feature specific bit, but switching the channel to use enhanced
> descriptor format. This format extends the basic descriptors by
> placing four extra 32-bit words before the basic descriptor.
> 
> Looking at the enhanced normal descriptor format for TDES3, it
> indicates that the format includes bit 18 in the control field, which
> is the TSE bit (TCP segmentation enable for this packet.) So, it seems
> it's not a limitation of the descriptor format.
> 
> So, either "TSO and TBS cannot co-exist" is incorrect, or there is a
> hardware limitation that isn't documented between these two manuals.
> 
> One other interesting point is that stmmac_tso_xmit() seems to
> handle the case where TSO and TBS are enabled on the channel:
> 
>                 if (tx_q->tbs & STMMAC_TBS_AVAIL)
>                         mss_desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
>                 else
>                         mss_desc = &tx_q->dma_tx[tx_q->cur_tx];
> 
>                 stmmac_set_mss(priv, mss_desc, mss);
> ...
>         if (tx_q->tbs & STMMAC_TBS_AVAIL)
>                 desc = &tx_q->dma_entx[first_entry].basic;
>         else
>                 desc = &tx_q->dma_tx[first_entry];
>         first = desc;
> 
> etc.
> 
> Avoiding enabling TSO for a TBS channel was added by this commit:
> 
> commit 5e6038b88a5718910dd74b949946d9d9cee9a041
> Author: Ong Boon Leong <boon.leong.ong@intel.com>
> Date:   Wed Apr 21 17:11:49 2021 +0800
> 
>     net: stmmac: fix TSO and TBS feature enabling during driver open
> 
>     TSO and TBS cannot co-exist and current implementation requires two
>     fixes:
> 
>      1) stmmac_open() does not need to call stmmac_enable_tbs() because
>         the MAC is reset in stmmac_init_dma_engine() anyway.
>      2) Inside stmmac_hw_setup(), we should call stmmac_enable_tso() for
>         TX Q that is _not_ configured for TBS.
> 
>     Fixes: 579a25a854d4 ("net: stmmac: Initial support for TBS")
>     Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com>
>     Signed-off-by: David S. Miller <davem@davemloft.net>
> 
> which doesn't really explain the background, and leaves all the TBS
> cruft in the TSO transmit path (nothing like properly updating the
> driver, eh? No wonder stmmac is such a mess!)

The more I look at this, the more I'm convinced this commit is
incorrect, even if it is the case that the hardware doesn't support
TSO and TBS together.

When TSO is enabled (NETIF_F_TSO set in the netif's features) then
the core net layer can submit skbuffs that need to be processed using
TSO.

If such a skbuff hits a channel that has TSO disabled (because the
above commit caused:

	stmmac_enable_tso(priv, priv->ioaddr, 1, chan);

not to be called) then the TSE bit in the transmit control register
will not be set, thereby disabling TSO on this particular channel.

However, stmmac_xmit() will still call through to stmmac_tso_xmit()
which will dutifully populate the transmit ring with descriptors that
assume TSE has been set in the transmit control register.

It seems to me _that_ is even more broken than "the hardware doesn't
support TSO and TBS together" - I have no idea what the stmmac hardware
does if it encounters descriptors with TSE set but TSE is disabled in
the transmit control register. My guess would be it would ignore the
TSE bit in the descriptor and assume that it's one very large packet
to be sent - and either error out because it's longer than the
maximum the hardware can support or it will just try to transmit it
anyway.

-- 
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 v4 0/3] KVM: arm64: Fix SPE and TRBE nVHE world switch
From: Marc Zyngier @ 2026-03-28 17:13 UTC (permalink / raw)
  To: kvmarm, Will Deacon
  Cc: mark.rutland, linux-arm-kernel, Oliver Upton, James Clark,
	Leo Yan, Suzuki K Poulose, Fuad Tabba, Alexandru Elisei,
	Yabin Cui
In-Reply-To: <20260327130047.21065-1-will@kernel.org>

On Fri, 27 Mar 2026 13:00:43 +0000, Will Deacon wrote:
> I got the Sashiko treatment on v3, so here's a quick respin to address
> the BRBE thinko it found in the last patch.
> 
> Previous versions of the series are available at:
> 
>   v1: https://lore.kernel.org/r/20260216130959.19317-1-will@kernel.org
>   v2: https://lore.kernel.org/r/20260227212136.7660-1-will@kernel.org
>   v3: https://lore.kernel.org/r/20260326141214.18990-1-will@kernel.org
> 
> [...]

Applied to next, thanks!

[1/3] KVM: arm64: Disable TRBE Trace Buffer Unit when running in guest context
      commit: d133aa75e39dd72e0b8577ab1f5fc17c72246536
[2/3] KVM: arm64: Disable SPE Profiling Buffer when running in guest context
      commit: 07695f7dc1e141601254057a00bf4e23301eb0b2
[3/3] KVM: arm64: Don't pass host_debug_state to BRBE world-switch routines
      commit: 7aba10efef1d972fc82b00b84911f07f6afbdb78

Cheers,

	M.
-- 
Without deviation from the norm, progress is not possible.




^ permalink raw reply

* [PATCH 2/2] spi: stm32-ospi: Fix DMA channel leak on stm32_ospi_dma_setup() failure
From: Felix Gu @ 2026-03-28 16:07 UTC (permalink / raw)
  To: Mark Brown, Maxime Coquelin, Alexandre Torgue, Philipp Zabel,
	Patrice Chotard
  Cc: linux-spi, linux-stm32, linux-arm-kernel, linux-kernel, Felix Gu
In-Reply-To: <20260329-stm32-ospi-v1-0-142122466412@gmail.com>

When stm32_ospi_dma_setup() fails, the DMA channels allocated by
stm32_ospi_get_resources() were never released. Add proper cleanup
in the error path.

Fixes: e35a7607e05d ("spi: stm32-ospi: Set DMA maxburst dynamically")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
 drivers/spi/spi-stm32-ospi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c
index 52997c3f7174..34498939bcdf 100644
--- a/drivers/spi/spi-stm32-ospi.c
+++ b/drivers/spi/spi-stm32-ospi.c
@@ -923,7 +923,7 @@ static int stm32_ospi_probe(struct platform_device *pdev)
 	dma_cfg.dst_addr = ospi->regs_phys_base + OSPI_DR;
 	ret = stm32_ospi_dma_setup(ospi, &dma_cfg);
 	if (ret)
-		return ret;
+		goto err_dma_free;
 
 	mutex_init(&ospi->lock);
 
@@ -975,6 +975,7 @@ static int stm32_ospi_probe(struct platform_device *pdev)
 err_pm_enable:
 	pm_runtime_force_suspend(ospi->dev);
 	mutex_destroy(&ospi->lock);
+err_dma_free:
 	if (ospi->dma_chtx)
 		dma_release_channel(ospi->dma_chtx);
 	if (ospi->dma_chrx)

-- 
2.43.0



^ permalink raw reply related

* [PATCH 1/2] spi: stm32-ospi: Fix reset control leak on probe error
From: Felix Gu @ 2026-03-28 16:07 UTC (permalink / raw)
  To: Mark Brown, Maxime Coquelin, Alexandre Torgue, Philipp Zabel,
	Patrice Chotard
  Cc: linux-spi, linux-stm32, linux-arm-kernel, linux-kernel, Felix Gu
In-Reply-To: <20260329-stm32-ospi-v1-0-142122466412@gmail.com>

When spi_register_controller() fails after reset_control_acquire()
succeeds, the reset control is never released. This causes a resource
leak in the error path.

Add the missing reset_control_release() call in the error path.

Fixes: cf2c3eceb757 ("spi: stm32-ospi: Make usage of reset_control_acquire/release() API")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
 drivers/spi/spi-stm32-ospi.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c
index acf2d182e8b1..52997c3f7174 100644
--- a/drivers/spi/spi-stm32-ospi.c
+++ b/drivers/spi/spi-stm32-ospi.c
@@ -960,13 +960,15 @@ static int stm32_ospi_probe(struct platform_device *pdev)
 	if (ret) {
 		/* Disable ospi */
 		writel_relaxed(0, ospi->regs_base + OSPI_CR);
-		goto err_pm_resume;
+		goto err_reset_control;
 	}
 
 	pm_runtime_put_autosuspend(ospi->dev);
 
 	return 0;
 
+err_reset_control:
+	reset_control_release(ospi->rstc);
 err_pm_resume:
 	pm_runtime_put_sync_suspend(ospi->dev);
 

-- 
2.43.0



^ permalink raw reply related

* [PATCH 0/2] spi: stm32-ospi: two fixes
From: Felix Gu @ 2026-03-28 16:07 UTC (permalink / raw)
  To: Mark Brown, Maxime Coquelin, Alexandre Torgue, Philipp Zabel,
	Patrice Chotard
  Cc: linux-spi, linux-stm32, linux-arm-kernel, linux-kernel, Felix Gu

Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
Felix Gu (2):
      spi: stm32-ospi: Fix reset control leak on probe error
      spi: stm32-ospi: Fix DMA channel leak on stm32_ospi_dma_setup() failure

 drivers/spi/spi-stm32-ospi.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
---
base-commit: 3b058d1aeeeff27a7289529c4944291613b364e9
change-id: 20260328-stm32-ospi-6d2ca0833eb6

Best regards,
-- 
Felix Gu <ustc.gu@gmail.com>



^ permalink raw reply

* Re: [PATCH v3 2/3] drm/gem-dma: Use the dma_*_attr API variant
From: kernel test robot @ 2026-03-28 15:54 UTC (permalink / raw)
  To: Chen-Yu Tsai, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter
  Cc: oe-kbuild-all, Rob Herring, dri-devel, linux-kernel,
	linux-arm-kernel, Chen-Yu Tsai
In-Reply-To: <20260326100248.1171828-3-wenst@chromium.org>

Hi Chen-Yu,

kernel test robot noticed the following build errors:

[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on next-20260327]
[cannot apply to sunxi/sunxi/for-next linus/master v7.0-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Chen-Yu-Tsai/drm-Introduce-DRM_MODE_DUMB_KERNEL_MAP-flag/20260328-141115
base:   https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link:    https://lore.kernel.org/r/20260326100248.1171828-3-wenst%40chromium.org
patch subject: [PATCH v3 2/3] drm/gem-dma: Use the dma_*_attr API variant
config: arm64-defconfig (https://download.01.org/0day-ci/archive/20260328/202603282331.VKpi5ANh-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260328/202603282331.VKpi5ANh-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603282331.VKpi5ANh-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c: In function 'rcar_du_vsp_map_fb':
>> drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c:296:82: error: expected ')' before 'gem'
     296 |                                                     gem->dma_addr, gem->base.size
         |                                                                                  ^
         |                                                                                  )
     297 |                                                     gem->dma_attrs);
         |                                                     ~~~                           
   drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c:295:52: note: to match this '('
     295 |                         ret = dma_get_sgtable_attrs(rcdu->dev, sgt, gem->vaddr,
         |                                                    ^
>> drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c:295:31: error: too few arguments to function 'dma_get_sgtable_attrs'; expected 6, have 5
     295 |                         ret = dma_get_sgtable_attrs(rcdu->dev, sgt, gem->vaddr,
         |                               ^~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/dma-buf.h:21,
                    from include/drm/drm_gem.h:38,
                    from include/drm/drm_gem_dma_helper.h:7,
                    from drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c:18:
   include/linux/dma-mapping.h:172:5: note: declared here
     172 | int dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt,
         |     ^~~~~~~~~~~~~~~~~~~~~


vim +296 drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c

   258	
   259	int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
   260			       struct sg_table sg_tables[3])
   261	{
   262		struct rcar_du_device *rcdu = vsp->dev;
   263		unsigned int i, j;
   264		int ret;
   265	
   266		for (i = 0; i < fb->format->num_planes; ++i) {
   267			struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);
   268			struct sg_table *sgt = &sg_tables[i];
   269	
   270			if (gem->sgt) {
   271				struct scatterlist *src;
   272				struct scatterlist *dst;
   273	
   274				/*
   275				 * If the GEM buffer has a scatter gather table, it has
   276				 * been imported from a dma-buf and has no physical
   277				 * address as it might not be physically contiguous.
   278				 * Copy the original scatter gather table to map it to
   279				 * the VSP.
   280				 */
   281				ret = sg_alloc_table(sgt, gem->sgt->orig_nents,
   282						     GFP_KERNEL);
   283				if (ret)
   284					goto fail;
   285	
   286				src = gem->sgt->sgl;
   287				dst = sgt->sgl;
   288				for (j = 0; j < gem->sgt->orig_nents; ++j) {
   289					sg_set_page(dst, sg_page(src), src->length,
   290						    src->offset);
   291					src = sg_next(src);
   292					dst = sg_next(dst);
   293				}
   294			} else {
 > 295				ret = dma_get_sgtable_attrs(rcdu->dev, sgt, gem->vaddr,
 > 296							    gem->dma_addr, gem->base.size
   297							    gem->dma_attrs);
   298				if (ret)
   299					goto fail;
   300			}
   301	
   302			ret = vsp1_du_map_sg(vsp->vsp, sgt);
   303			if (ret) {
   304				sg_free_table(sgt);
   305				goto fail;
   306			}
   307		}
   308	
   309		return 0;
   310	
   311	fail:
   312		while (i--) {
   313			struct sg_table *sgt = &sg_tables[i];
   314	
   315			vsp1_du_unmap_sg(vsp->vsp, sgt);
   316			sg_free_table(sgt);
   317		}
   318	
   319		return ret;
   320	}
   321	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply

* Re: [PATCH 1/7] dt-bindings: rtc: sun6i: Add Allwinner A733 support
From: Chen-Yu Tsai @ 2026-03-28 12:37 UTC (permalink / raw)
  To: Junhui Liu
  Cc: Michael Turquette, Stephen Boyd, Jernej Skrabec, Samuel Holland,
	Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Maxime Ripard, linux-clk, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-rtc, devicetree
In-Reply-To: <20260121-a733-rtc-v1-1-d359437f23a7@pigmoral.tech>

On Wed, Jan 21, 2026 at 7:03 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> The RTC module in the Allwinner A733 SoC is functionally compatible with
> the sun6i RTC, but its internal Clock Control Unit (CCU) has significant
> changes.
>
> The A733 supports selecting the oscillator between three frequencies:
> 19.2MHz, 24MHz, and 26MHz. The RTC CCU relies on hardware to detect
> which frequency is actually used on the board. By defining all three
> frequencies as fixed-clocks in the device tree, the driver can identify
> the hardware-detected frequency and expose it to the rest of the system.

No. The board device tree shall have the exact and correct frequency
defined in the external crystal device node. The operating system can
use the hardware-detected frequency to "fix" the in-system representation
if it is off.

> Additionally, the A733 RTC CCU provides several new DCXO gate clocks for
> specific modules, including SerDes, HDMI, and UFS.
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
>  .../bindings/rtc/allwinner,sun6i-a31-rtc.yaml      | 38 ++++++++++++++++++++--
>  include/dt-bindings/clock/sun60i-a733-rtc.h        | 16 +++++++++
>  2 files changed, 52 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> index 9df5cdb6f63f..b18431955783 100644
> --- a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> +++ b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> @@ -26,6 +26,7 @@ properties:
>            - allwinner,sun50i-h6-rtc
>            - allwinner,sun50i-h616-rtc
>            - allwinner,sun50i-r329-rtc
> +          - allwinner,sun60i-a733-rtc
>        - items:
>            - const: allwinner,sun50i-a64-rtc
>            - const: allwinner,sun8i-h3-rtc
> @@ -46,11 +47,11 @@ properties:
>
>    clocks:
>      minItems: 1
> -    maxItems: 4
> +    maxItems: 6
>
>    clock-names:
>      minItems: 1
> -    maxItems: 4
> +    maxItems: 6
>
>    clock-output-names:
>      minItems: 1
> @@ -156,6 +157,38 @@ allOf:
>          - clocks
>          - clock-names
>
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: allwinner,sun60i-a733-rtc
> +
> +    then:
> +      properties:
> +        clocks:
> +          minItems: 5
> +          items:
> +            - description: Bus clock for register access

> +            - description: 19.2 MHz oscillator
> +            - description: 24 MHz oscillator
> +            - description: 26 MHz oscillator

No. There is only one input. As in there is only one set of pins for the
DCXO. The inputs are the same as on R329 / A523. Just use that list.

> +            - description: AHB parent for internal SPI clock
> +            - description: External 32768 Hz oscillator
> +
> +        clock-names:
> +          minItems: 5
> +          items:
> +            - const: bus
> +            - const: osc19M
> +            - const: osc24M
> +            - const: osc26M
> +            - const: ahb
> +            - const: ext-osc32k
> +
> +      required:
> +        - clocks
> +        - clock-names
> +
>    - if:
>        properties:
>          compatible:
> @@ -164,6 +197,7 @@ allOf:
>                - allwinner,sun8i-r40-rtc
>                - allwinner,sun50i-h616-rtc
>                - allwinner,sun50i-r329-rtc
> +              - allwinner,sun60i-a733-rtc
>
>      then:
>        properties:
> diff --git a/include/dt-bindings/clock/sun60i-a733-rtc.h b/include/dt-bindings/clock/sun60i-a733-rtc.h
> new file mode 100644
> index 000000000000..8a2b5facad73
> --- /dev/null
> +++ b/include/dt-bindings/clock/sun60i-a733-rtc.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
> +
> +#ifndef _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_
> +#define _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_
> +
> +#define CLK_IOSC               0
> +#define CLK_OSC32K             1
> +#define CLK_HOSC               2

The DCXO enable control has been present since at least the H6. We just
never added it, as we would never disable it anyway.

If you compare the RTC clock trees of the A733 and A523, the only addition
besides the new gates seems to be the LOSC auto selection. But even that
is just an illusion, as the A523 has the same registers for that.

One could say the A733 RTC is almost backward compatible to the A523, if
not for the two fastboot registers the A523 has at 0x120 and 0x124.

So I ask that you try to integrate the differences into the existing
driver and bindings. You can tweak and export internal clks if you
need.

> +#define CLK_RTC_32K            3

AFAICT besides being an internal clock, this is also fed to GPIO for
debounce? We probably need to expose this on the A523 as well.


Thanks
ChenYu


> +#define CLK_OSC32K_FANOUT      4
> +#define CLK_HOSC_SERDES1       5
> +#define CLK_HOSC_SERDES0       6
> +#define CLK_HOSC_HDMI          7
> +#define CLK_HOSC_UFS           8
> +
> +#endif /* _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_ */
>
> --
> 2.52.0
>
>


^ permalink raw reply

* Re: [PATCH 7/7] clk: sunxi-ng: Add Allwinner A733 RTC CCU support
From: Chen-Yu Tsai @ 2026-03-28 14:41 UTC (permalink / raw)
  To: Junhui Liu
  Cc: Michael Turquette, Stephen Boyd, Jernej Skrabec, Samuel Holland,
	Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Maxime Ripard, linux-clk, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-rtc, devicetree, André Przywara
In-Reply-To: <20260121-a733-rtc-v1-7-d359437f23a7@pigmoral.tech>

On Wed, Jan 21, 2026 at 7:04 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> Add support for the internal CCU found in the RTC module of the Allwinner
> A733 SoC. While the basic 16MHz (IOSC) and 32kHz logic remains compatible
> with older SoCs like the sun6i, the A733 introduces several new features.
>
> The A733 RTC CCU supports choosing one of three external crystal
> frequencies: 19.2MHz, 24MHz, and 26MHz. It features hardware detection
> logic to automatically identify the frequency used on the board and
> exports this DCXO signal as the "hosc" clock.
>
> Furthermore, the driver implements logic to derive a 32kHz reference
> from the HOSC. This is achieved through a muxed clock path using fixed
> pre-dividers to normalize the different crystal frequencies to ~32kHz.

Have you tested whether the actually normalizes the frequency, i.e.
selects a different divider based on the DCXO frequency? Otherwise
we're just lying about the frequency.

> This path reuses the same hardware mux registers as the HOSC clock.
>
> Additionally, this CCU provides several gate clocks for specific
> peripherals, including SerDes, HDMI, and UFS. The driver is implemented
> as an auxiliary driver to be bound to the sun6i-rtc driver.
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
>  drivers/clk/sunxi-ng/Kconfig               |   5 +
>  drivers/clk/sunxi-ng/Makefile              |   2 +
>  drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c | 204 +++++++++++++++++++++++++++++
>  drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h |  18 +++
>  drivers/clk/sunxi-ng/ccu_rtc.h             |   7 +
>  5 files changed, 236 insertions(+)
>
> diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
> index 6af2d020e03e..16afbf249f26 100644
> --- a/drivers/clk/sunxi-ng/Kconfig
> +++ b/drivers/clk/sunxi-ng/Kconfig
> @@ -67,6 +67,11 @@ config SUN55I_A523_R_CCU
>         default ARCH_SUNXI
>         depends on ARM64 || COMPILE_TEST
>
> +config SUN60I_A733_RTC_CCU
> +       tristate "Support for the Allwinner A733 RTC CCU"
> +       default ARCH_SUNXI
> +       depends on ARM64 || COMPILE_TEST
> +
>  config SUN4I_A10_CCU
>         tristate "Support for the Allwinner A10/A20 CCU"
>         default ARCH_SUNXI
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index c3f810a025a8..b0d823440c33 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
>  obj-$(CONFIG_SUN55I_A523_CCU)  += sun55i-a523-ccu.o
>  obj-$(CONFIG_SUN55I_A523_MCU_CCU)      += sun55i-a523-mcu-ccu.o
>  obj-$(CONFIG_SUN55I_A523_R_CCU)        += sun55i-a523-r-ccu.o
> +obj-$(CONFIG_SUN60I_A733_RTC_CCU)      += sun60i-a733-rtc-ccu.o
>  obj-$(CONFIG_SUN4I_A10_CCU)    += sun4i-a10-ccu.o
>  obj-$(CONFIG_SUN5I_CCU)                += sun5i-ccu.o
>  obj-$(CONFIG_SUN6I_A31_CCU)    += sun6i-a31-ccu.o
> @@ -67,6 +68,7 @@ sun50i-h616-ccu-y             += ccu-sun50i-h616.o
>  sun55i-a523-ccu-y              += ccu-sun55i-a523.o
>  sun55i-a523-mcu-ccu-y          += ccu-sun55i-a523-mcu.o
>  sun55i-a523-r-ccu-y            += ccu-sun55i-a523-r.o
> +sun60i-a733-rtc-ccu-y          += ccu-sun60i-a733-rtc.o
>  sun4i-a10-ccu-y                        += ccu-sun4i-a10.o
>  sun5i-ccu-y                    += ccu-sun5i.o
>  sun6i-a31-ccu-y                        += ccu-sun6i-a31.o
> diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c
> new file mode 100644
> index 000000000000..d17aceffa16e
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c
> @@ -0,0 +1,204 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/auxiliary_bus.h>
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +
> +#include "ccu_common.h"
> +
> +#include "ccu_gate.h"
> +#include "ccu_mux.h"
> +#include "ccu_rtc.h"
> +
> +#include "ccu-sun60i-a733-rtc.h"
> +
> +static struct ccu_common iosc_clk = {
> +       .reg            = DCXO_CTRL_REG,
> +       .features       = CCU_FEATURE_IOSC_CALIBRATION,
> +       .hw.init        = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
> +                                               CLK_GET_RATE_NOCACHE),
> +};
> +
> +static struct ccu_common iosc_32k_clk = {
> +       .features       = CCU_FEATURE_IOSC_CALIBRATION,
> +       .hw.init        = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
> +                                        &ccu_iosc_32k_ops,
> +                                        CLK_GET_RATE_NOCACHE),
> +};
> +
> +static SUNXI_CCU_GATE_FW(ext_osc32k_gate_clk, "ext-osc32k-gate",
> +                        "ext-osc32k", 0x0, BIT(4), 0);
> +
> +static const struct clk_hw *osc32k_parents[] = {
> +       &iosc_32k_clk.hw,
> +       &ext_osc32k_gate_clk.common.hw,
> +};
> +
> +static struct ccu_mux osc32k_clk = {
> +       .mux    = _SUNXI_CCU_MUX(0, 1),
> +       .common = {
> +               .reg            = LOSC_CTRL_REG,
> +               .features       = CCU_FEATURE_KEY_FIELD,
> +               .hw.init        = CLK_HW_INIT_PARENTS_HW("osc32k",
> +                                                        osc32k_parents,
> +                                                        &ccu_mux_ops,
> +                                                        0),
> +       },
> +};
> +
> +static const struct clk_parent_data hosc_parents[] = {
> +       { .fw_name = "osc24M" },
> +       { .fw_name = "osc19M" },
> +       { .fw_name = "osc26M" },
> +       { .fw_name = "osc24M" },
> +};

As mentioned in my reply to the binding, this is wrong. There is only
one input.

The most you can do is check the rate of the parent clock against the
detected one, and _scream_ that the DT is wrong. And maybe override
the reported frequency.

If you want to do the latter, you could add a new fixed rate gated
clock type to our library. You would fill in the rate before the
clocks get registered. I probably wouldn't go that far. We want people
to have correct hardware descriptions.

Funnily enough Allwinner's BSP actually implements a fixed rate gate
for the next 24M-to-32k divider clock.

> +
> +struct ccu_mux hosc_clk = {
> +       .enable = DCXO_CTRL_DCXO_EN,
> +       .mux    = _SUNXI_CCU_MUX(14, 2),
> +       .common = {
> +               .reg            = DCXO_CTRL_REG,
> +               .hw.init        = CLK_HW_INIT_PARENTS_DATA("hosc",
> +                                                          hosc_parents,
> +                                                          &ccu_mux_ro_ops,
> +                                                          0),
> +       },
> +};

So this is wrong.

> +
> +static const struct ccu_mux_fixed_prediv hosc_32k_predivs[] = {
> +       { .index = 0, .div = 732 },

Why is it 732 instead of 750?

> +       { .index = 1, .div = 586 },
> +       { .index = 2, .div = 793 },
> +       { .index = 3, .div = 732 },
> +};
> +
> +static struct ccu_mux hosc_32k_mux_clk = {
> +       .enable         = DCXO_CTRL_DCXO_EN,

No. The parent "hosc" clock owns this.  The enable bit for this clock
is actually bit 16 of LOSC_OUT_GATING_REG, which you model below as
a separate gate.

> +       .mux            = {
> +               .shift          = 14,
> +               .width          = 2,
> +               .fixed_predivs  = hosc_32k_predivs,
> +               .n_predivs      = ARRAY_SIZE(hosc_32k_predivs),
> +       },
> +       .common         = {
> +               .reg            = DCXO_CTRL_REG,
> +               .features       = CCU_FEATURE_FIXED_PREDIV,
> +               .hw.init        = CLK_HW_INIT_PARENTS_DATA("hosc-32k-mux",
> +                                                          hosc_parents,
> +                                                          &ccu_mux_ro_ops,

Again, this is just not the way to do it.

> +                                                          0),
> +       },
> +};

I would test that it actually does switch dividers, Or at the very least,
it has a larger divider for 26M.

Maybe Andre can help? At least on this SoC the fanout pins are much more
accessible.

> +
> +static SUNXI_CCU_GATE_HW(hosc_32k_clk, "hosc-32k", &hosc_32k_mux_clk.common.hw,
> +                        LOSC_OUT_GATING_REG, BIT(16), 0);
> +
> +static const struct clk_hw *rtc_32k_parents[] = {
> +       &osc32k_clk.common.hw,
> +       &hosc_32k_clk.common.hw,
> +};
> +
> +static struct ccu_mux rtc_32k_clk = {
> +       .mux    = _SUNXI_CCU_MUX(1, 1),
> +       .common = {
> +               .reg            = LOSC_CTRL_REG,
> +               .features       = CCU_FEATURE_KEY_FIELD,
> +               .hw.init        = CLK_HW_INIT_PARENTS_HW("rtc-32k",
> +                                                        rtc_32k_parents,
> +                                                        &ccu_mux_ops,
> +                                                        0),
> +       },
> +};
> +
> +static const struct clk_parent_data osc32k_fanout_parents[] = {
> +       { .hw = &osc32k_clk.common.hw },
> +       { .hw = &ext_osc32k_gate_clk.common.hw },
> +       { .hw = &hosc_32k_clk.common.hw },
> +};
> +
> +static SUNXI_CCU_MUX_DATA_WITH_GATE(osc32k_fanout_clk, "osc32k-fanout", osc32k_fanout_parents,
> +                                   LOSC_OUT_GATING_REG,
> +                                   1, 2,       /* mux */
> +                                   BIT(0),     /* gate */
> +                                   0);
> +
> +static SUNXI_CCU_GATE_HW(hosc_serdes1_clk, "hosc-serdes1", &hosc_clk.common.hw,
> +                        DCXO_GATING_REG, DCXO_SERDES1_GATING, 0);

                                            ^
Just use the BIT() expression here. Adding these macros doesn't really help.

> +static SUNXI_CCU_GATE_HW(hosc_serdes0_clk, "hosc-serdes0", &hosc_clk.common.hw,
> +                        DCXO_GATING_REG, DCXO_SERDES0_GATING, 0);
> +static SUNXI_CCU_GATE_HW(hosc_hdmi_clk, "hosc-hdmi", &hosc_clk.common.hw,
> +                        DCXO_GATING_REG, DCXO_HDMI_GATING, 0);
> +static SUNXI_CCU_GATE_HW(hosc_ufs_clk, "hosc-ufs", &hosc_clk.common.hw,
> +                        DCXO_GATING_REG, DCXO_UFS_GATING, 0);
> +
> +static struct ccu_common *sun60i_rtc_ccu_clks[] = {
> +       &iosc_clk,
> +       &iosc_32k_clk,
> +       &ext_osc32k_gate_clk.common,
> +       &osc32k_clk.common,
> +       &hosc_clk.common,
> +       &hosc_32k_mux_clk.common,
> +       &hosc_32k_clk.common,
> +       &rtc_32k_clk.common,
> +       &osc32k_fanout_clk.common,
> +       &hosc_serdes1_clk.common,
> +       &hosc_serdes0_clk.common,
> +       &hosc_hdmi_clk.common,
> +       &hosc_ufs_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sun60i_rtc_ccu_hw_clks = {
> +       .num = CLK_NUMBER,
> +       .hws = {
> +               [CLK_IOSC]              = &iosc_clk.hw,
> +               [CLK_OSC32K]            = &osc32k_clk.common.hw,
> +               [CLK_HOSC]              = &hosc_clk.common.hw,
> +               [CLK_RTC_32K]           = &rtc_32k_clk.common.hw,
> +               [CLK_OSC32K_FANOUT]     = &osc32k_fanout_clk.common.hw,
> +               [CLK_HOSC_SERDES1]      = &hosc_serdes1_clk.common.hw,
> +               [CLK_HOSC_SERDES0]      = &hosc_serdes0_clk.common.hw,
> +               [CLK_HOSC_HDMI]         = &hosc_hdmi_clk.common.hw,
> +               [CLK_HOSC_UFS]          = &hosc_ufs_clk.common.hw,
> +               [CLK_IOSC_32K]          = &iosc_32k_clk.hw,
> +               [CLK_EXT_OSC32K_GATE]   = &ext_osc32k_gate_clk.common.hw,
> +               [CLK_HOSC_32K_MUX]      = &hosc_32k_mux_clk.common.hw,
> +               [CLK_HOSC_32K]          = &hosc_32k_clk.common.hw,
> +       },
> +};
> +
> +static const struct sunxi_ccu_desc sun60i_rtc_ccu_desc = {
> +       .ccu_clks       = sun60i_rtc_ccu_clks,
> +       .num_ccu_clks   = ARRAY_SIZE(sun60i_rtc_ccu_clks),
> +
> +       .hw_clks        = &sun60i_rtc_ccu_hw_clks,
> +};
> +
> +static int sun60i_rtc_ccu_probe(struct auxiliary_device *adev,
> +                               const struct auxiliary_device_id *id)
> +{
> +       struct device *dev = &adev->dev;
> +       void __iomem *reg = dev->platform_data;
> +
> +       return devm_sunxi_ccu_probe(dev, reg, &sun60i_rtc_ccu_desc);
> +}
> +
> +static const struct auxiliary_device_id sun60i_ccu_rtc_ids[] = {
> +       { .name = SUN6I_RTC_AUX_ID(sun60i) },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(auxiliary, sun60i_ccu_rtc_ids);
> +
> +static struct auxiliary_driver sun60i_ccu_rtc_driver = {
> +       .probe = sun60i_rtc_ccu_probe,
> +       .id_table = sun60i_ccu_rtc_ids,
> +};
> +module_auxiliary_driver(sun60i_ccu_rtc_driver);
> +
> +MODULE_IMPORT_NS("SUNXI_CCU");
> +MODULE_DESCRIPTION("Support for the Allwinner A733 RTC CCU");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h
> new file mode 100644
> index 000000000000..41ec6195b5e7
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
> + */
> +
> +#ifndef _CCU_SUN60I_A733_RTC_H_
> +#define _CCU_SUN60I_A733_RTC_H_
> +
> +#include <dt-bindings/clock/sun60i-a733-rtc.h>
> +
> +#define CLK_IOSC_32K           9
> +#define CLK_EXT_OSC32K_GATE    10
> +#define CLK_HOSC_32K_MUX       11
> +#define CLK_HOSC_32K           12
> +
> +#define CLK_NUMBER             (CLK_HOSC_32K + 1)
> +
> +#endif /* _CCU_SUN60I_A733_RTC_H_ */
> diff --git a/drivers/clk/sunxi-ng/ccu_rtc.h b/drivers/clk/sunxi-ng/ccu_rtc.h
> index 1c44c2206a25..665162723796 100644
> --- a/drivers/clk/sunxi-ng/ccu_rtc.h
> +++ b/drivers/clk/sunxi-ng/ccu_rtc.h
> @@ -27,8 +27,15 @@
>  #define LOSC_OUT_GATING_REG            0x60
>
>  #define DCXO_CTRL_REG                  0x160
> +#define DCXO_CTRL_DCXO_EN              BIT(1)
>  #define DCXO_CTRL_CLK16M_RC_EN         BIT(0)
>
> +#define DCXO_GATING_REG                        0x16c


> +#define DCXO_SERDES1_GATING            BIT(5)
> +#define DCXO_SERDES0_GATING            BIT(4)
> +#define DCXO_HDMI_GATING               BIT(1)
> +#define DCXO_UFS_GATING                        BIT(0)

Adding them to the header is probably even less useful, as the output
could change in future chips.


ChenYu


^ permalink raw reply

* Re: [PATCH 6/7] rtc: sun6i: Add support for A733 RTC
From: Chen-Yu Tsai @ 2026-03-28 12:40 UTC (permalink / raw)
  To: Junhui Liu
  Cc: Michael Turquette, Stephen Boyd, Jernej Skrabec, Samuel Holland,
	Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Maxime Ripard, linux-clk, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-rtc, devicetree
In-Reply-To: <20260121-a733-rtc-v1-6-d359437f23a7@pigmoral.tech>

On Wed, Jan 21, 2026 at 7:04 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> The RTC in the Allwinner A733 SoC is compatible with the H616 in terms
> of its time storage and alarm functionality. However, its internal CCU
> is different, with additional DCXO handling logic.
>
> Add new match data to register a new auxiliary device for its CCU part.

This is probably incorrect, since you aren't actually adding auxiliary
devices. It should just say "add a new compatible and matching data for
the new SoC".

>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
>  drivers/rtc/rtc-sun6i.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
> index b4489e0a09ce..a58d9c6b917c 100644
> --- a/drivers/rtc/rtc-sun6i.c
> +++ b/drivers/rtc/rtc-sun6i.c
> @@ -865,6 +865,11 @@ static const struct sun6i_rtc_match_data sun6i_rtc_match_data = {
>         .flags = RTC_LINEAR_DAY,
>  };
>
> +static const struct sun6i_rtc_match_data sun60i_rtc_match_data = {
> +       .adev_name = "sun60i",
> +       .flags = RTC_LINEAR_DAY,
> +};
> +
>  /*
>   * As far as RTC functionality goes, all models are the same. The
>   * datasheets claim that different models have different number of
> @@ -883,6 +888,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
>                 .data = &sun6i_rtc_match_data },
>         { .compatible = "allwinner,sun50i-r329-rtc",
>                 .data = &sun6i_rtc_match_data },
> +       { .compatible = "allwinner,sun60i-a733-rtc",
> +               .data = &sun60i_rtc_match_data },
>         { /* sentinel */ },
>  };
>  MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
>
> --
> 2.52.0
>
>


^ permalink raw reply

* Re: [PATCH RFC 3/8] clk: sunxi-ng: a733: Add PRCM CCU
From: Chen-Yu Tsai @ 2026-03-28 15:04 UTC (permalink / raw)
  To: Junhui Liu
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Jernej Skrabec, Samuel Holland, Philipp Zabel,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Richard Cochran, linux-clk, devicetree, linux-arm-kernel,
	linux-sunxi, linux-kernel, linux-riscv, netdev
In-Reply-To: <20260310-a733-clk-v1-3-36b4e9b24457@pigmoral.tech>

On Tue, Mar 10, 2026 at 4:42 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> Add support for the Power Reset Clock Management (PRCM) module found in
> the Allwinner A733 SoC. This clock controller manages the clock control
> and reset functions for device modules within the CPUS domain.
>
> The PRCM module includes the management of three primary buses: r-ahb,
> r-apb0, and r-apb1. It also provides clocking for several key
> peripherals, such as R-UART, R-I2C, R-SPI, and the R-RISCV subsystem.
> Additionally, the reset lines for these modules are integrated.
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
>  drivers/clk/sunxi-ng/Kconfig             |   5 +
>  drivers/clk/sunxi-ng/Makefile            |   2 +
>  drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c | 276 +++++++++++++++++++++++++++++++
>  3 files changed, 283 insertions(+)
>
> diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
> index 6af2d020e03e..202e793dc754 100644
> --- a/drivers/clk/sunxi-ng/Kconfig
> +++ b/drivers/clk/sunxi-ng/Kconfig
> @@ -67,6 +67,11 @@ config SUN55I_A523_R_CCU
>         default ARCH_SUNXI
>         depends on ARM64 || COMPILE_TEST
>
> +config SUN60I_A733_R_CCU
> +       tristate "Support for the Allwinner A733 PRCM CCU"
> +       default ARCH_SUNXI
> +       depends on ARM64 || COMPILE_TEST
> +
>  config SUN4I_A10_CCU
>         tristate "Support for the Allwinner A10/A20 CCU"
>         default ARCH_SUNXI
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index a1c4087d7241..d3702bdb7a23 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
>  obj-$(CONFIG_SUN55I_A523_CCU)  += sun55i-a523-ccu.o
>  obj-$(CONFIG_SUN55I_A523_MCU_CCU)      += sun55i-a523-mcu-ccu.o
>  obj-$(CONFIG_SUN55I_A523_R_CCU)        += sun55i-a523-r-ccu.o
> +obj-$(CONFIG_SUN60I_A733_R_CCU)        += sun60i-a733-r-ccu.o
>  obj-$(CONFIG_SUN4I_A10_CCU)    += sun4i-a10-ccu.o
>  obj-$(CONFIG_SUN5I_CCU)                += sun5i-ccu.o
>  obj-$(CONFIG_SUN6I_A31_CCU)    += sun6i-a31-ccu.o
> @@ -64,6 +65,7 @@ sun50i-h616-ccu-y             += ccu-sun50i-h616.o
>  sun55i-a523-ccu-y              += ccu-sun55i-a523.o
>  sun55i-a523-mcu-ccu-y          += ccu-sun55i-a523-mcu.o
>  sun55i-a523-r-ccu-y            += ccu-sun55i-a523-r.o
> +sun60i-a733-r-ccu-y            += ccu-sun60i-a733-r.o
>  sun4i-a10-ccu-y                        += ccu-sun4i-a10.o
>  sun5i-ccu-y                    += ccu-sun5i.o
>  sun6i-a31-ccu-y                        += ccu-sun6i-a31.o
> diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c
> new file mode 100644
> index 000000000000..06679be1eaae
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c
> @@ -0,0 +1,276 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 rengaomin@allwinnertech.com
> + * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
> + * Based on the A523 CCU driver:
> + *   Copyright (C) 2024 Arm Ltd.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include <dt-bindings/clock/sun60i-a733-r-ccu.h>
> +#include <dt-bindings/reset/sun60i-a733-r-ccu.h>
> +
> +#include "ccu_common.h"
> +#include "ccu_reset.h"
> +
> +#include "ccu_gate.h"
> +#include "ccu_mp.h"
> +
> +static const struct clk_parent_data r_ahb_parents[] = {
> +       { .fw_name = "hosc" },
> +       { .fw_name = "losc" },
> +       { .fw_name = "iosc" },
> +       { .fw_name = "pll-periph0-200m" },
> +       { .fw_name = "pll-periph0-300m" },
> +};
> +static SUNXI_CCU_M_DATA_WITH_MUX(r_ahb_clk, "r-ahb", r_ahb_parents, 0x000,
> +                                0, 5,  /* M */
> +                                24, 3, /* mux */
> +                                0);
> +
> +static const struct clk_parent_data r_apb_parents[] = {
> +       { .fw_name = "hosc" },
> +       { .fw_name = "losc" },
> +       { .fw_name = "iosc" },
> +       { .fw_name = "pll-periph0-200m" },
> +       { .fw_name = "sys-24m" },
> +};
> +
> +static SUNXI_CCU_M_DATA_WITH_MUX(r_apb0_clk, "r-apb0", r_apb_parents, 0x00c,
> +                                0, 5,  /* M */
> +                                24, 3, /* mux */
> +                                0);
> +
> +static SUNXI_CCU_M_DATA_WITH_MUX(r_apb1_clk, "r-apb1", r_apb_parents, 0x010,
> +                                0, 5,  /* M */
> +                                24, 3, /* mux */
> +                                0);
> +
> +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer0, "r-timer0", r_apb_parents, 0x100,
> +                                      0, 0,    /* no M */
> +                                      1, 3,    /* P */
> +                                      4, 3,    /* mux */
> +                                      BIT(0),  /* gate */
> +                                      0);

Use SUNXI_CCU_P_DATA_WITH_MUX_GATE(). Same for the other ones.

> +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer1, "r-timer1", r_apb_parents, 0x104,
> +                                      0, 0,    /* no M */
> +                                      1, 3,    /* P */
> +                                      4, 3,    /* mux */
> +                                      BIT(0),  /* gate */
> +                                      0);
> +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer2, "r-timer2", r_apb_parents, 0x108,
> +                                      0, 0,    /* no M */
> +                                      1, 3,    /* P */
> +                                      4, 3,    /* mux */
> +                                      BIT(0),  /* gate */
> +                                      0);
> +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer3, "r-timer3", r_apb_parents, 0x10c,
> +                                      0, 0,    /* no M */
> +                                      1, 3,    /* P */
> +                                      4, 3,    /* mux */
> +                                      BIT(0),  /* gate */
> +                                      0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_timer_clk, "bus-r-timer", &r_ahb_clk.common.hw, 0x11c, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_twd_clk, "bus-r-twd", &r_apb0_clk.common.hw, 0x12c, BIT(0), 0);
> +
> +static const struct clk_parent_data r_pwmctrl_parents[] = {
> +       { .fw_name = "hosc" },
> +       { .fw_name = "losc" },
> +       { .fw_name = "iosc" },
> +       { .fw_name = "sys-24m" },
> +};
> +static SUNXI_CCU_MUX_DATA_WITH_GATE(r_pwmctrl_clk, "r-pwmctrl", r_pwmctrl_parents, 0x130,

r_pwm_clk, "r-pwm", ...

> +                                   24, 2,      /* mux */
> +                                   BIT(31),    /* gate */
> +                                   0);
> +static SUNXI_CCU_GATE_HW(bus_r_pwmctrl_clk, "bus-r-pwmctrl",

bus_r_pwm_clk, "bus-r-pwm".

> +                        &r_apb0_clk.common.hw, 0x13c, BIT(0), 0);
> +
> +static const struct clk_parent_data r_spi_parents[] = {
> +       { .fw_name = "hosc" },
> +       { .fw_name = "pll-periph0-200m" },
> +       { .fw_name = "pll-periph0-300m" },
> +       { .fw_name = "pll-periph1-300m" },
> +       { .fw_name = "sys-24m" },
> +};
> +static SUNXI_CCU_DUALDIV_MUX_GATE(r_spi_clk, "r-spi", r_spi_parents, 0x150,
> +                                 0, 5,         /* M */
> +                                 8, 5,         /* N */
> +                                 24, 3,        /* mux */
> +                                 BIT(31),      /* gate */
> +                                 0);
> +static SUNXI_CCU_GATE_HW(bus_r_spi_clk, "bus-r-spi", &r_ahb_clk.common.hw, 0x15c, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_msgbox_clk, "bus-r-msgbox", &r_ahb_clk.common.hw, 0x17c, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_uart0_clk, "bus-r-uart0", &r_apb1_clk.common.hw, 0x18c, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_uart1_clk, "bus-r-uart1", &r_apb1_clk.common.hw, 0x18c, BIT(1), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_i2c0_clk, "bus-r-i2c0", &r_apb1_clk.common.hw, 0x19c, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_i2c1_clk, "bus-r-i2c1", &r_apb1_clk.common.hw, 0x19c, BIT(1), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_i2c2_clk, "bus-r-i2c2", &r_apb1_clk.common.hw, 0x19c, BIT(2), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_ppu_clk, "bus-r-ppu", &r_apb0_clk.common.hw, 0x1ac, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_tzma_clk, "bus-r-tzma", &r_apb0_clk.common.hw, 0x1b0, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_cpu_bist_clk, "bus-r-cpu-bist", &r_apb0_clk.common.hw,
> +                        0x1bc, BIT(0), 0);
> +
> +static const struct clk_parent_data r_ir_rx_parents[] = {
> +       { .fw_name = "losc" },
> +       { .fw_name = "hosc" },
> +       { .fw_name = "sys-24m" },
> +};
> +static SUNXI_CCU_M_DATA_WITH_MUX_GATE(r_ir_rx_clk, "r-ir-rx", r_ir_rx_parents, 0x1c0,
> +                                     0, 5,     /* M */
> +                                     24, 2,    /* mux */
> +                                     BIT(31),  /* gate */
> +                                     0);
> +static SUNXI_CCU_GATE_HW(bus_r_ir_rx_clk, "bus-r-ir-rx", &r_apb0_clk.common.hw, 0x1cc, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_rtc_clk, "bus-r-rtc", &r_ahb_clk.common.hw, 0x20c, BIT(0), 0);
> +
> +static const struct clk_parent_data r_riscv_parents[] = {
> +       { .fw_name = "hosc" },
> +       { .fw_name = "losc" },
> +       { .fw_name = "iosc" },
> +};
> +static SUNXI_CCU_MUX_DATA_WITH_GATE(r_riscv_clk, "r-riscv", r_riscv_parents, 0x210,
> +                                 24, 2,        /* mux */
> +                                 BIT(31),      /* gate */
> +                                 0);
> +static SUNXI_CCU_GATE_HW(bus_r_riscv_clk, "bus-r-riscv", &r_apb0_clk.common.hw,
> +                        0x21c, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_riscv_cfg_clk, "bus-r-riscv-cfg", &r_apb0_clk.common.hw,
> +                        0x21c, BIT(1), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_cpucfg_clk, "bus-r-cpucfg", &r_apb0_clk.common.hw,
> +                        0x22c, BIT(0), CLK_IS_CRITICAL);
> +
> +static struct ccu_common *sun60i_a733_r_ccu_clks[] = {
> +       &r_ahb_clk.common,
> +       &r_apb0_clk.common,
> +       &r_apb1_clk.common,
> +       &r_cpu_timer0.common,
> +       &r_cpu_timer1.common,
> +       &r_cpu_timer2.common,
> +       &r_cpu_timer3.common,
> +       &bus_r_timer_clk.common,
> +       &bus_r_twd_clk.common,
> +       &r_pwmctrl_clk.common,
> +       &bus_r_pwmctrl_clk.common,
> +       &r_spi_clk.common,
> +       &bus_r_spi_clk.common,
> +       &bus_r_msgbox_clk.common,
> +       &bus_r_uart0_clk.common,
> +       &bus_r_uart1_clk.common,
> +       &bus_r_i2c0_clk.common,
> +       &bus_r_i2c1_clk.common,
> +       &bus_r_i2c2_clk.common,
> +       &bus_r_ppu_clk.common,
> +       &bus_r_tzma_clk.common,
> +       &bus_r_cpu_bist_clk.common,
> +       &r_ir_rx_clk.common,
> +       &bus_r_ir_rx_clk.common,
> +       &bus_r_rtc_clk.common,
> +       &r_riscv_clk.common,
> +       &bus_r_riscv_clk.common,
> +       &bus_r_riscv_cfg_clk.common,
> +       &bus_r_cpucfg_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sun60i_a733_r_hw_clks = {
> +       .hws = {
> +               [CLK_R_AHB]             = &r_ahb_clk.common.hw,
> +               [CLK_R_APB0]            = &r_apb0_clk.common.hw,
> +               [CLK_R_APB1]            = &r_apb1_clk.common.hw,
> +               [CLK_R_TIMER0]          = &r_cpu_timer0.common.hw,
> +               [CLK_R_TIMER1]          = &r_cpu_timer1.common.hw,
> +               [CLK_R_TIMER2]          = &r_cpu_timer2.common.hw,
> +               [CLK_R_TIMER3]          = &r_cpu_timer3.common.hw,
> +               [CLK_BUS_R_TIMER]       = &bus_r_timer_clk.common.hw,
> +               [CLK_BUS_R_TWD]         = &bus_r_twd_clk.common.hw,
> +               [CLK_R_PWMCTRL]         = &r_pwmctrl_clk.common.hw,
> +               [CLK_BUS_R_PWMCTRL]     = &bus_r_pwmctrl_clk.common.hw,
> +               [CLK_R_SPI]             = &r_spi_clk.common.hw,
> +               [CLK_BUS_R_SPI]         = &bus_r_spi_clk.common.hw,
> +               [CLK_BUS_R_MSGBOX]      = &bus_r_msgbox_clk.common.hw,
> +               [CLK_BUS_R_UART0]       = &bus_r_uart0_clk.common.hw,
> +               [CLK_BUS_R_UART1]       = &bus_r_uart1_clk.common.hw,
> +               [CLK_BUS_R_I2C0]        = &bus_r_i2c0_clk.common.hw,
> +               [CLK_BUS_R_I2C1]        = &bus_r_i2c1_clk.common.hw,
> +               [CLK_BUS_R_I2C2]        = &bus_r_i2c2_clk.common.hw,
> +               [CLK_BUS_R_PPU]         = &bus_r_ppu_clk.common.hw,
> +               [CLK_BUS_R_TZMA]        = &bus_r_tzma_clk.common.hw,
> +               [CLK_BUS_R_CPU_BIST]    = &bus_r_cpu_bist_clk.common.hw,
> +               [CLK_R_IR_RX]           = &r_ir_rx_clk.common.hw,
> +               [CLK_BUS_R_IR_RX]       = &bus_r_ir_rx_clk.common.hw,
> +               [CLK_BUS_R_RTC]         = &bus_r_rtc_clk.common.hw,
> +               [CLK_R_RISCV]           = &r_riscv_clk.common.hw,
> +               [CLK_BUS_R_RISCV]       = &bus_r_riscv_clk.common.hw,
> +               [CLK_BUS_R_RISCV_CFG]   = &bus_r_riscv_cfg_clk.common.hw,
> +               [CLK_BUS_R_CPUCFG]      = &bus_r_cpucfg_clk.common.hw,
> +       },
> +       .num = CLK_BUS_R_CPUCFG + 1,
> +};
> +
> +static struct ccu_reset_map sun60i_a733_r_ccu_resets[] = {
> +       [RST_BUS_R_TIMER]       = { 0x11c, BIT(16) },
> +       [RST_BUS_R_PWM]         = { 0x13c, BIT(16) },
> +       [RST_BUS_R_SPI]         = { 0x15c, BIT(16) },
> +       [RST_BUS_R_MSGBOX]      = { 0x17c, BIT(16) },
> +       [RST_BUS_R_UART0]       = { 0x18c, BIT(16) },
> +       [RST_BUS_R_UART1]       = { 0x18c, BIT(17) },
> +       [RST_BUS_R_I2C0]        = { 0x19c, BIT(16) },
> +       [RST_BUS_R_I2C1]        = { 0x19c, BIT(17) },
> +       [RST_BUS_R_I2C2]        = { 0x19c, BIT(18) },
> +       [RST_BUS_R_IR_RX]       = { 0x1cc, BIT(16) },
> +       [RST_BUS_R_RTC]         = { 0x20c, BIT(16) },
> +       [RST_BUS_R_RISCV_CFG]   = { 0x21c, BIT(16) },
> +       [RST_BUS_R_CPUCFG]      = { 0x22c, BIT(16) },
> +};
> +
> +static const struct sunxi_ccu_desc sun60i_a733_r_ccu_desc = {
> +       .ccu_clks       = sun60i_a733_r_ccu_clks,
> +       .num_ccu_clks   = ARRAY_SIZE(sun60i_a733_r_ccu_clks),
> +
> +       .hw_clks        = &sun60i_a733_r_hw_clks,
> +
> +       .resets         = sun60i_a733_r_ccu_resets,
> +       .num_resets     = ARRAY_SIZE(sun60i_a733_r_ccu_resets),
> +};
> +
> +static int sun60i_a733_r_ccu_probe(struct platform_device *pdev)
> +{
> +       void __iomem *reg;
> +
> +       reg = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(reg))
> +               return PTR_ERR(reg);
> +
> +       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun60i_a733_r_ccu_desc);
> +}
> +
> +static const struct of_device_id sun60i_a733_r_ccu_ids[] = {
> +       { .compatible = "allwinner,sun60i-a733-r-ccu" },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, sun60i_a733_r_ccu_ids);
> +
> +static struct platform_driver sun60i_a733_r_ccu_driver = {
> +       .probe  = sun60i_a733_r_ccu_probe,
> +       .driver = {
> +               .name                   = "sun60i-a733-r-ccu",
> +               .suppress_bind_attrs    = true,
> +               .of_match_table         = sun60i_a733_r_ccu_ids,
> +       },
> +};
> +module_platform_driver(sun60i_a733_r_ccu_driver);
> +
> +MODULE_IMPORT_NS("SUNXI_CCU");
> +MODULE_DESCRIPTION("Support for the Allwinner A733 PRCM CCU");
> +MODULE_LICENSE("GPL");

The rest look OK.


ChenYu


^ permalink raw reply

* Re: [PATCH] arm64: dts: rockchip: Add RK3562 serial aliases
From: Krzysztof Kozlowski @ 2026-03-28 15:08 UTC (permalink / raw)
  To: 谢致邦 (XIE Zhibang), linux-rockchip
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
	Kever Yang, Finley Xiao, devicetree, linux-arm-kernel,
	linux-kernel
In-Reply-To: <tencent_4BED6C3FFB8102B4BB3D08D6F47F2CCFC908@qq.com>

On 28/03/2026 14:05, 谢致邦 (XIE Zhibang) wrote:
> This fixes the stdout-path in rk3562-evb2-v10.dts.
> 
> Fixes: ceb6ef1ea900 ("arm64: dts: rockchip: Add RK3562 evb2 devicetree")
> Signed-off-by: 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
> ---
>  arch/arm64/boot/dts/rockchip/rk3562.dtsi | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/rockchip/rk3562.dtsi b/arch/arm64/boot/dts/rockchip/rk3562.dtsi
> index e4816aa3dae0..14e74e8ac7df 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3562.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3562.dtsi
> @@ -26,6 +26,16 @@ aliases {
>  		gpio2 = &gpio2;
>  		gpio3 = &gpio3;
>  		gpio4 = &gpio4;
> +		serial0 = &uart0;
> +		serial1 = &uart1;
> +		serial2 = &uart2;
> +		serial3 = &uart3;
> +		serial4 = &uart4;
> +		serial5 = &uart5;
> +		serial6 = &uart6;
> +		serial7 = &uart7;
> +		serial8 = &uart8;
> +		serial9 = &uart9;

UART aliases are properties of the boards, not SoC.

Best regards,
Krzysztof


^ permalink raw reply

* [PATCH 2/2] KVM: arm64: Refactor stage2 mmu tear down functions
From: Wei-Lin Chang @ 2026-03-28 14:54 UTC (permalink / raw)
  To: linux-arm-kernel, kvmarm, linux-kernel
  Cc: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Wei-Lin Chang
In-Reply-To: <20260328145439.2501562-1-weilin.chang@arm.com>

With NV, "stage2 mmu" becomes ambiguous, it could refer to the canonical
one or one of the nested ones. Modify kvm_uninit_stage2_mmu() so that it
can uninitialize both the canonical s2 mmu and nested s2 mmus.

Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
---
 arch/arm64/include/asm/kvm_mmu.h | 2 +-
 arch/arm64/kvm/mmu.c             | 8 +++++---
 arch/arm64/kvm/nested.c          | 4 ++--
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 960b6aed4ffa..4b49aabecbf3 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -176,7 +176,7 @@ void kvm_stage2_wp_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t e
 
 void stage2_unmap_vm(struct kvm *kvm);
 int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long type);
-void kvm_uninit_stage2_mmu(struct kvm *kvm);
+void kvm_uninit_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu);
 void kvm_free_stage2(struct kvm_s2_mmu *mmu);
 int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
 			  phys_addr_t pa, unsigned long size, bool writable);
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index b19b9a9b3c27..a2affd70eca6 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1018,10 +1018,12 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
 	return err;
 }
 
-void kvm_uninit_stage2_mmu(struct kvm *kvm)
+void kvm_uninit_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
 {
-	kvm_free_stage2(&kvm->arch.mmu);
-	kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache);
+	kvm_free_stage2(mmu);
+
+	if (!kvm_is_nested_s2_mmu(kvm, mmu))
+		kvm_mmu_free_memory_cache(&mmu->split_page_cache);
 }
 
 static void stage2_unmap_memslot(struct kvm *kvm,
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 772d922cf0ee..5eba94d6cc67 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1194,12 +1194,12 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)
 		struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
 
 		if (!WARN_ON(atomic_read(&mmu->refcnt)))
-			kvm_free_stage2(mmu);
+			kvm_uninit_stage2_mmu(kvm, mmu);
 	}
 	kvfree(kvm->arch.nested_mmus);
 	kvm->arch.nested_mmus = NULL;
 	kvm->arch.nested_mmus_size = 0;
-	kvm_uninit_stage2_mmu(kvm);
+	kvm_uninit_stage2_mmu(kvm, &kvm->arch.mmu);
 }
 
 /*
-- 
2.43.0



^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox