Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* RE: [PATCH v2] spi: imx: reconfigure for PIO when DMA cannot be started
From: Carlos Song @ 2026-06-24 15:27 UTC (permalink / raw)
  To: Javier Fernandez Pastrana, Mark Brown, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, linux-spi@vger.kernel.org,
	imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
  Cc: stable@vger.kernel.org
In-Reply-To: <20260624151958.18626-1-javier.pastrana@linutronix.de>



> -----Original Message-----
> From: Javier Fernandez Pastrana <javier.pastrana@linutronix.de>
> Sent: Wednesday, June 24, 2026 11:20 PM
> To: Mark Brown <broonie@kernel.org>; Frank Li <frank.li@nxp.com>; Sascha
> Hauer <s.hauer@pengutronix.de>; Pengutronix Kernel Team
> <kernel@pengutronix.de>; Fabio Estevam <festevam@gmail.com>; Carlos Song
> <carlos.song@nxp.com>; linux-spi@vger.kernel.org; imx@lists.linux.dev;
> linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org
> Cc: javier.pastrana@linutronix.de; stable@vger.kernel.org
> Subject: [EXT] [PATCH v2] spi: imx: reconfigure for PIO when DMA cannot be
> started
> 
> [Some people who received this message don't often get email from
> javier.pastrana@linutronix.de. Learn why this is important at
> https://aka.ms/LearnAboutSenderIdentification ]
> 
> Caution: This is an external email. Please take care when clicking links or opening
> attachments. When in doubt, report the message using the 'Report this email'
> button
> 
> 
> When spi_imx_can_dma() selects DMA, the ECSPI is configured for DMA:
> spi_imx_setupxfer() sets CTRL.SMC and clears dynamic_burst, and
> spi_imx_dma_transfer() programs the dynamic-burst BURST_LENGTH and the
> SDMA watermarks.
> 
> If the DMA descriptor cannot be prepared (dmaengine_prep_slave_single()
> returns NULL), the transfer is failed with SPI_TRANS_FAIL_NO_START and falls
> back to PIO. The dynamic-burst DMA path uses its own bounce buffers instead of
> the SPI core's mapping, so xfer->{tx,rx}_sg_mapped are not set and the core's
> DMA->PIO retry is skipped; the driver falls back to PIO internally. But none of the
> DMA-mode configuration is undone, so the PIO transfer runs with CTRL.SMC set,
> the wrong burst length and dynamic_burst cleared, and the transferred data is
> corrupted.
> 
> This is easily hit on i.MX8MP boards that describe ECSPI DMA in the device tree
> but run SDMA on ROM firmware (no external sdma-imx7d.bin):
> every ECSPI DMA prepare fails. An Infineon SLB9670 TPM on ECSPI1 then returns
> shifted TPM2_GetCapability data, is flagged "field failure mode", /dev/tpmrm0 is
> never created.
> 
> Set controller->fallback before re-running spi_imx_setupxfer() so the ECSPI is
> reconfigured exactly like a normal PIO transfer. With
> controller->fallback set, spi_imx_setupxfer() sees spi_imx_can_dma()
> return false, so it clears spi_imx->usedma and reprograms the controller (clears
> CTRL.SMC, restores dynamic_burst and the PIO burst length). No explicit
> spi_imx->usedma = false is needed: setupxfer() already updates it from the
> can_dma() result.
> 
> Fixes: faa8e404ad8e ("spi: imx: support dynamic burst length for ECSPI DMA
> mode")
> Cc: stable@vger.kernel.org
> Signed-off-by: Javier Fernandez Pastrana <javier.pastrana@linutronix.de>

Hi,

LGTM. Thank you!

Acked-by: Carlos Song <carlos.song@nxp.com>

> ---
> v2: drop redundant spi_imx->usedma = false; spi_imx_setupxfer() already
>     clears it via spi_imx_can_dma() (Carlos Song)
> 
>  drivers/spi/spi-imx.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index
> 480d1e8b281f..1837cc7b0b96 100644
> --- a/drivers/spi/spi-imx.c
> +++ b/drivers/spi/spi-imx.c
> @@ -2152,7 +2152,8 @@ static int spi_imx_transfer_one(struct spi_controller
> *controller,
>         if (spi_imx->usedma) {
>                 ret = spi_imx_dma_transfer(spi_imx, transfer);
>                 if (transfer->error & SPI_TRANS_FAIL_NO_START) {
> -                       spi_imx->usedma = false;
> +                       controller->fallback = true;
> +                       spi_imx_setupxfer(spi, transfer);
>                         if (spi_imx->target_mode)
>                                 return spi_imx_pio_transfer_target(spi,
> transfer);
>                         else
> --
> 2.47.3



^ permalink raw reply

* Re: [PATCH 19/37] drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
From: Maxime Ripard @ 2026-06-24 15:28 UTC (permalink / raw)
  To: Luca Ceresoli
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
	Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
	Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
	linux-arm-kernel
In-Reply-To: <DJ63DNXUOW46.1DI6982PGPFRR@bootlin.com>

[-- Attachment #1: Type: text/plain, Size: 4791 bytes --]

On Thu, Jun 11, 2026 at 10:54:14AM +0200, Luca Ceresoli wrote:
> On Mon Jun 8, 2026 at 1:58 PM CEST, Maxime Ripard wrote:
> > On Tue, May 19, 2026 at 12:37:36PM +0200, Luca Ceresoli wrote:
> >> This bridge driver calls drm_bridge_add() in the DSI host .attach callback
> >> instead of in the probe function.
> >>
> >> This works for current use cases but is problematic for supporting hotplug
> >> of DRM bridges. The problematic case is when this DSI host is always
> >> present while its DSI device is hot-pluggable. In such case with the
> >> current code the DRM card will not be populated until after the DSI device
> >> attaches to the host, which could happen a very long time after booting, or
> >> even not happen at all.
> >>
> >> The reason is that the previous pipeline component (the encoder in this
> >> case) when probing cannot find the samsung-dsim bridge. What happens is:
> >>
> >>  [1 and 2 can happen in any order, same result]
> >>  1) samsung-dsim probes (does not drm_bridge_add() itself)
> >>  2) The lcdif starts probing multiple times, but
> >>     lcdif_probe
> >>     -> lcdif_load
> >>        -> lcdif_attach_bridge
> >>           -> devm_drm_of_get_bridge() returns -EPROBE_DEFER because
> >>              the samsung-dsim is not in the global bridge_list
> >>              (deferred probe pending: imx-lcdif: Cannot connect bridge)
> >>
> >> The samsung-dsim will not drm_bridge_add() itself until a DSI device will
> >> try to mipi_dsi_attach() to the DSI Host, which can happen arbitratily late
> >> on hot-pluggable hardware.
> >>
> >> As a preliminary step to supporting hotplug move drm_bridge_add() at probe
> >> time, so that the samsung-dsim DSI host bridge is available during boot,
> >> even without a connected DSI device. This results in:
> >>
> >>  1) samsung-dsim probes (and adds to drm_bridge_add() itself)
> >>  2) The lcdif starts probing multiple times, but
> >>     lcdif_probe
> >>     -> lcdif_load
> >>        -> lcdif_attach_bridge
> >>           -> devm_drm_of_get_bridge() --> OK, returns samsung-dsim ptr
> >>
> >> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > We should probably amend
> > https://www.kernel.org/doc/html/latest/gpu/drm-kms-helpers.html#special-care-with-mipi-dsi-bridges
> >
> > To mention this use case here
> 
> Right. I haven't updated the docs for this v1 because I was not sure the
> overall approach would be acked. Now Dmitry acked it overall, and I kind of
> infer you are not against, so I'll look into updating the docs in v2.
> 
> However I find that section of the docs a bit hard to read especially from
> a newcomer perspective.

It's a complex problem, so I don't think we should expect the target
audience to be newcomers. But maybe we can indeed improve it.

> A better understanding on my side would help in doing the right change as
> far as this patch is concerned, and as a bonus in improving the section
> overall (that would probably be a separate series).
> 
> So I have a couple questions to start from:
> 
>  * Do I understand correctly that using the component framework is legacy,
>    not recommended for new DRM development, and that converting existing
>    code to stop using it is welcome?

No. It's not legacy or deprecated. And about the conversion, I guess
it's on a case-by-case basis? It's not encouraged or discouraged anyway.

>  * The first bullet quotes "The upstream driver [...] isn’t a MIPI-DSI
>    host". If the upstream driver of a MIPI DSI link isn't a MIPI DSI host,
>    what else could it be? What are the use cases here?

Nowhere is it said that we're considering a MIPI-DSI link here, so the
use case is any bridge that isn't using MIPI-DSI at all.

>  * If read literally, none of the 4 bullets after "Indeed, there’s multiple
>    cases that needs to be considered" covers this driver (it does not use
>    the component framework, it does not use DCS, and the upstream device is
>    a DSI host). However the 3 bullets after "The ideal pattern to cover the
>    last item" appear to cover what this driver does.  Do we need a fifth
>    bullet for drivers like this one? Or...?

You tell me :)

How does hotplugging, say, a MIPI-DSI device bridge controlled over I2C,
or a MIPI-DSI host bridge, affect the probing sequence, and can we end
up in endless probe deferrals?

>  * To me it looks like the "bridge" word in this section is always used to
>    refer to the DSI device. Would it make sense to replace "bridge" ->
>    "[DSI ]device" in this section?

Not necessarily, and from a KMS point of view, MIPI-DSI doesn't exist,
only bridge do.

> (especially as the DSI host is also a DRM bridge)

It doesn't have to be.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]

^ permalink raw reply

* Re: [PATCH] mtd: rawnand: lpc32xx_slc: fail DMA transfer on completion timeout
From: Vladimir Zapolskiy @ 2026-06-24 15:29 UTC (permalink / raw)
  To: Pengpeng Hou, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Piotr Wojtaszczyk
  Cc: linux-mtd, linux-arm-kernel, linux-kernel
In-Reply-To: <20260624144127.69075-1-pengpeng@iscas.ac.cn>

On 6/24/26 17:41, Pengpeng Hou wrote:
> lpc32xx_xmit_dma() waits for the DMA completion callback but ignores
> wait_for_completion_timeout(). A timed out DMA transfer is therefore
> unmapped and reported as successful to the NAND read/write path.
> 
> Return -ETIMEDOUT when the completion wait expires. Terminate the DMA
> channel before unmapping the scatterlist so the timed out transfer cannot
> continue to access the buffer after the error is returned.
> 
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
>   drivers/mtd/nand/raw/lpc32xx_slc.c | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
> index 3ca30e7dc..10c808020 100644
> --- a/drivers/mtd/nand/raw/lpc32xx_slc.c
> +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
> @@ -430,6 +430,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
>   	struct dma_async_tx_descriptor *desc;
>   	int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
>   	int res;
> +	unsigned long time_left;
>   
>   	host->dma_slave_config.direction = dir;
>   	host->dma_slave_config.src_addr = dma;
> @@ -467,12 +468,19 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
>   	dmaengine_submit(desc);
>   	dma_async_issue_pending(host->dma_chan);
>   
> -	wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000));
> +	time_left = wait_for_completion_timeout(&host->comp,
> +						msecs_to_jiffies(1000));
> +	if (!time_left) {
> +		dmaengine_terminate_sync(host->dma_chan);
> +		res = -ETIMEDOUT;
> +	} else {
> +		res = 0;
> +	}
>   
>   	dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
>   		     DMA_BIDIRECTIONAL);
>   
> -	return 0;
> +	return res;
>   out1:
>   	dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
>   		     DMA_BIDIRECTIONAL);

Thank you for the change.

Reviewed-by: Vladimir Zapolskiy <vz@kernel.org>

-- 
Best wishes,
Vladimir


^ permalink raw reply

* Re: [PATCH v15 10/11] arm64: entry: Convert to generic entry
From: Ada Couprie Diaz @ 2026-06-24 15:32 UTC (permalink / raw)
  To: Jinjie Ruan
  Cc: mark.rutland, peterz, catalin.marinas, ldv, song, will, kees,
	thuth, ryan.roberts, anshuman.khandual, kevin.brodsky, pengcan,
	broonie, luto, linux-arm-kernel, wad, yeoreum.yun, oleg,
	linux-kernel, james.morse, tglx, liqiang01, linusw
In-Reply-To: <20260511092103.1974980-11-ruanjinjie@huawei.com>

On 11/05/2026 10:21, Jinjie Ruan wrote:
> Implement the generic entry framework for arm64 to handle system call
> entry and exit. This follows the migration of x86, RISC-V, and LoongArch,
> consolidating architecture-specific syscall tracing and auditing into
> the common kernel entry infrastructure.
If I understand correctly, as Syscall User Dispatch is gated being
`CONFIG_GENERIC_ENTRY` only and handled via ptrace, this patch
effectively enables Syscall User Dispatch for arm64.
I think it would be great to mention it here explicitly !
>
> [Background]
> Arm64 has already adopted generic IRQ entry. Completing the conversion
> to the generic syscall entry framework reduces architectural divergence,
> simplifies maintenance, and allows arm64 to automatically benefit from
> improvements in the common entry code.
>
> [Changes]
>
> 1. Kconfig and Infrastructure:
> - Select GENERIC_ENTRY and remove GENERIC_IRQ_ENTRY (now implied).
>
> - Migrate struct thread_info to use the syscall_work field instead
>    of TIF flags for syscall-related tasks.
>
> 2. Thread Info and Flags:
> - Remove definitions for TIF_SYSCALL_TRACE, TIF_SYSCALL_AUDIT,
>    TIF_SYSCALL_TRACEPOINT, TIF_SECCOMP, and TIF_SYSCALL_EMU.
>
> - Replace _TIF_SYSCALL_WORK and _TIF_SYSCALL_EXIT_WORK with the
>    generic SYSCALL_WORK bitmask.
>
> - Map single-step state to SYSCALL_EXIT_TRAP in debug-monitors.c.
>
> 3. Architecture-Specific Hooks (asm/entry-common.h):
> - Implement arch_ptrace_report_syscall_entry() and _exit() by
>    porting the existing arm64 logic to the generic interface.
>
> - Add arch_syscall_is_vdso_sigreturn() to asm/syscall.h to
>    support Syscall User Dispatch (SUD).
Related to the above : I feel this is missing an important information.
Given that SUD is only controlled by `CONFIG_GENERIC_ENTRY`,
converting to generic entry _requires_ supporting SUD, so we do it here.
I think this would be important to mention, as I otherwise felt like this
change did not belong in this patch.

General question that follows : does it make sense to require an arch
to support Syscall User Dispatch to be able to convert to generic entry ?
(I assume not really, given that only `arch_syscall_is_vdso_sigreturn()` is
required on the arch side, but I am curious)

>
> 4. Cleanup and Refactoring:
> - Remove redundant arm64-specific syscall tracing functions from
>    ptrace.c, including syscall_trace_enter(), syscall_exit_work(),
>    and related audit/step helpers.
>
> - Update el0_svc_common() in syscall.c to use the generic
>    syscall_work checks and entry/exit call sites.
>
> [Why this matters]
> - Unified Interface: Aligns arm64 with the modern kernel entry standard.
>
> - Improved Maintainability: Bug fixes in kernel/entry/common.c now
>    apply to arm64 automatically.
>
> - Feature Readiness: Simplifies the implementation of future
>    cross-architecture syscall features.
>
> [Compatibility]
> This conversion maintains full ABI compatibility with existing
> userspace. The ptrace register-saving behavior, seccomp filtering, and
> syscall tracing semantics remain identical to the previous implementation.
I agree, would it make sense to mention that there is no change related
to RSEQ as arm64 does not have `HAVE_GENERIC_TIF_BITS` ? As that is
part of generic entry, but is indeed a no-op for us.
>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
> Reviewed-by: Kevin Brodsky <kevin.brodsky@arm.com>
> Suggested-by: Kevin Brodsky <kevin.brodsky@arm.com>
> Suggested-by: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> ---
> [...]
> diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
> index 29307642f4c9..e67643a70405 100644
> --- a/arch/arm64/kernel/debug-monitors.c
> +++ b/arch/arm64/kernel/debug-monitors.c
> @@ -385,11 +385,18 @@ void user_enable_single_step(struct task_struct *task)
>   
>   	if (!test_and_set_ti_thread_flag(ti, TIF_SINGLESTEP))
>   		set_regs_spsr_ss(task_pt_regs(task));
> +
> +	/*
> +	 * Ensure that a trap is triggered once stepping out of a system
> +	 * call prior to executing any user instruction.
> +	 */
I was a bit confused by the comment in isolation at first : we already
have a signal that we are stepping and would need a trap, `TIF_SINGLESTEP`.
Would it make sense to mention here that this is for/handled by the generic
entry code ?
Something along the lines of "[...], as the generic entry code does not
check for `TIF_SINGLESTEP`.", or "Ensure that the generic entry code
triggers a trap [...]", if you think its useful ?
> +	set_task_syscall_work(task, SYSCALL_EXIT_TRAP);
>   }
>   NOKPROBE_SYMBOL(user_enable_single_step);
>   
>   void user_disable_single_step(struct task_struct *task)
>   {
>   	clear_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
> +	clear_task_syscall_work(task, SYSCALL_EXIT_TRAP);
>   }
>   NOKPROBE_SYMBOL(user_disable_single_step);

Apart from my minor nitpicks :

Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>

Thanks,
Ada



^ permalink raw reply

* Re: [PATCH v1] ASoC: rockchip: rockchip_sai: #include <linux/platform_device.h> explicitly
From: Mark Brown @ 2026-06-24 12:01 UTC (permalink / raw)
  To: Nicolas Frattaroli, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
	Uwe Kleine-König (The Capable Hub)
  Cc: Heiko Stuebner, linux-rockchip, linux-sound, linux-arm-kernel,
	linux-kernel
In-Reply-To: <20260624083708.254517-2-u.kleine-koenig@baylibre.com>

On Wed, 24 Jun 2026 10:37:07 +0200, Uwe Kleine-König (The Capable Hub) wrote:
> ASoC: rockchip: rockchip_sai: #include <linux/platform_device.h> explicitly

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-7.2

Thanks!

[1/1] ASoC: rockchip: rockchip_sai: #include <linux/platform_device.h> explicitly
      https://git.kernel.org/broonie/sound/c/83d53eca7e55

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark



^ permalink raw reply

* Re: [PATCH 15/37] drm/display: bridge-connector: allocate the connector dynamically
From: Luca Ceresoli @ 2026-06-24 15:34 UTC (permalink / raw)
  To: Maxime Ripard, Luca Ceresoli
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
	Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
	Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
	linux-arm-kernel
In-Reply-To: <20260624-judicious-enigmatic-hippo-b13ffe@houat>

Hi Maxime,

thanks for the feedback.

On Wed Jun 24, 2026 at 1:48 PM CEST, Maxime Ripard wrote:
> On Fri, Jun 12, 2026 at 02:44:43PM +0200, Luca Ceresoli wrote:
>> On Mon Jun 8, 2026 at 1:46 PM CEST, Maxime Ripard wrote:
>> > On Tue, May 19, 2026 at 12:37:32PM +0200, Luca Ceresoli wrote:
>> >> Currently the drm_bridge_connector has an embedded drm_connector, so their
>> >> allocation lifetimes are tied to each other. This is insufficient to
>> >> support DRM bridge hotplugging, which requires the connector to be added
>> >> and removed dynamically at runtime multiple times based on hotplug/unplug
>> >> events while the drm_bridge_connector is persistent.
>> >>
>> >> Moreover the drm_connector is exposed to user space and thus an ongoing
>> >> operation (e.g. an ioctl) might last for an arbitrarily long time even
>> >> after the hardware gets removed. This means a new connector might have to
>> >> be added when the previous one is still referenced by user space.
>> >>
>> >> In preparation to handle hotplug, allocate the drm-connector dynamically,
>> >> to allow:
>> >>
>> >>  * creating and destroying a connector multiple times during a single
>> >>    drm_bridge_connector lifetime
>> >>  * creating a new connector even though the previous one is still in use
>> >>    and thus still refcounted and not yet freed
>> >>
>> >> This commit does not introduce the actions in the two bullets (it will
>> >> happen in a later commit), it only moves to dynamic APIs for connector
>> >> allocation and init.
>> >>
>> >> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>> >
>> > I think this patch should be split in half, with the switch to using
>> > destroy first, and then the actual move to the dynamically allocated
>> > connector API.
>>
>> Is it doable? drm_connector_dynamic_init() mandates a .destroy callback,
>> drm_connector_init() forbids it.
>
> drmm_connector_init forbids it. drm_connector_init mandates it.

Something bogus in my reply, sorry. :)

So you mean splitting in:

 * first patch: move from drmm_connector[_hdmi]_init() to
   drm_connector[_hdmi]_init() and add a .destroy
 * second patch: move from drm_connector[_hdmi]_init() to
   drm_connector[_hdmi]_dynamic_init() +
   drm_connector_dynamic_register/unregister()

?

Luca

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply

* Re: [PATCH v2] arm64: mm: Defer read-only remap of data/bss linear alias
From: Will Deacon @ 2026-06-24 15:35 UTC (permalink / raw)
  To: linux-arm-kernel, Ard Biesheuvel
  Cc: catalin.marinas, kernel-team, Will Deacon, linux-kernel,
	Kevin Brodsky, Ard Biesheuvel, Fuad Tabba
In-Reply-To: <20260623202817.2225495-2-ardb+git@google.com>

On Tue, 23 Jun 2026 22:28:18 +0200, Ard Biesheuvel wrote:
> Since commit
> 
>   f2ba877402e5 ("arm64: mm: Map the kernel data/bss read-only in the linear map")
> 
> the linear alias of the .data and .bss regions is remapped read-only
> early during the boot. (Note that a subsequent patch to unmap this
> region entirely was reverted just before the v7.2 merge window, and will
> be brought back in an improved form for the v7.3 cycle)
> 
> [...]

Applied to arm64 (for-next/core), thanks!

[1/1] arm64: mm: Defer read-only remap of data/bss linear alias
      https://git.kernel.org/arm64/c/36fa5ffa6034

Cheers,
-- 
Will

https://fixes.arm64.dev
https://next.arm64.dev
https://will.arm64.dev


^ permalink raw reply

* Re: [PATCH v15 11/11] arm64: Inline el0_svc_common()
From: Ada Couprie Diaz @ 2026-06-24 15:36 UTC (permalink / raw)
  To: Jinjie Ruan
  Cc: mark.rutland, peterz, catalin.marinas, ldv, song, will, kees,
	thuth, ryan.roberts, anshuman.khandual, kevin.brodsky, pengcan,
	broonie, luto, linux-arm-kernel, wad, yeoreum.yun, oleg,
	linux-kernel, james.morse, tglx, liqiang01, linusw
In-Reply-To: <20260511092103.1974980-12-ruanjinjie@huawei.com>

On 11/05/2026 10:21, Jinjie Ruan wrote:
> After converting arm64 to Generic Entry framework, the compiler no longer
> inlines el0_svc_common() into its caller do_el0_svc(). This introduces
> a small but measurable overhead in the critical system call path.
>
> Manually forcing el0_svc_common() to be inlined restores the
> performance. Benchmarking with perf bench syscall basic on a
> Kunpeng 920 platform (based on v6.19-rc1) shows a ~1% performance
> uplift.
>
> Inlining this function reduces function prologue/epilogue overhead
> and allows for better compiler optimization in the hot system call
> dispatch path.
>
> | Metric     | W/O this patch | With this patch | Change    |
> | ---------- | -------------- | --------------- | --------- |
> | Total time | 2.195 [sec]    | 2.171 [sec]     |  ↓1.1%   |
> | usecs/op   | 0.219575       | 0.217192        |  ↓1.1%   |
> | ops/sec    | 4,554,260      | 4,604,225       |  ↑1.1%    |
>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
> Reviewed-by: Kevin Brodsky <kevin.brodsky@arm.com>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> ---

Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>



^ permalink raw reply

* Re: [PATCH 06/37] drm/display: bridge-connector: use a drm_bridge_connector internally, not a drm_connector
From: Luca Ceresoli @ 2026-06-24 15:39 UTC (permalink / raw)
  To: Maxime Ripard, Luca Ceresoli
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
	Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
	Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
	linux-arm-kernel
In-Reply-To: <20260624-chicken-of-infinite-enthusiasm-8b0e3f@houat>

On Wed Jun 24, 2026 at 1:47 PM CEST, Maxime Ripard wrote:
> On Fri, Jun 12, 2026 at 02:57:39PM +0200, Luca Ceresoli wrote:
>> On Mon Jun 8, 2026 at 1:41 PM CEST, Maxime Ripard wrote:
>> > On Tue, May 19, 2026 at 12:37:23PM +0200, Luca Ceresoli wrote:
>> >> Currently drm_bridge_connector_init() always returns the added connector or
>> >> errors out. When adding bridge hotplug the bridge-connector can be
>> >> successfully initialized without creating a connector, which can be added
>> >> later when the pipeline will be complete.
>> >>
>> >> For this the internal function drm_bridge_connector_add_connector() must be
>> >> able to return a valid drm_bridge_connector even without any drm_connector.
>> >>
>> >> In preparation to support bridge hotplug, change its return value to be the
>> >> same drm_bridge_connector pointer it gets as input, or a PTR_ERR.
>> >>
>> >> No functional changes, just changing an internal API.
>> >>
>> >> Note the return value could now become an int (0 or negative error) because
>> >> returning the same value received as input does not carry any added
>> >> value. However this would be change a lot of lines, so leave such change as
>> >> a future cleanup.
>> >
>> > You just created that function and changed "a lot of lines" already, so
>> > I'm not sure that argument holds.
>>
>> Do you refer to the previous patch?
>>
>> My comment is more about the following patches. It means I separated
>> changes moving code to a subfunction from changes to the the return value
>> in separate patches, so that each patch is trivial to review for
>> correctness.
>>
>> Makes sense?
>
> What confused me is that I took it as "I'm not going to do that work
> (yet?)". If you do it later on, I'd drop the "future cleanup" part, or
> rephrase it.

Ah, I see the issue. I really meant "this change [returning int] is done in
a following commit".

I guess I'll just drop the whole paragraph.

Luca

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply

* Re: [PATCH 4/7] net: stmmac: dwmac-rk: Enable refout clock for RGMII
From: Andrew Lunn @ 2026-06-24 15:39 UTC (permalink / raw)
  To: Yanan He
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Wu, Maxime Coquelin, Alexandre Torgue,
	devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
	netdev, linux-stm32
In-Reply-To: <20260624-rv1126-alientek-dlrv1126-v1-4-5aef608a3f64@gmail.com>

On Wed, Jun 24, 2026 at 04:44:41PM +0800, Yanan He wrote:
> Some Rockchip GMAC integrations use clk_mac_refout as an external PHY
> reference clock even when the MAC is configured for RGMII.
> 
> RV1126 boards can route CLK_GMAC_ETHERNET_OUT to the external PHY as a
> 25 MHz reference clock. If the driver does not acquire and enable this
> clock in RGMII mode, the common clock framework may disable it as unused
> and the PHY can lose its reference clock.
> 
> Enable the refout clock handling for RGMII in addition to RMII.
> 
> Signed-off-by: Yanan He <grumpycat921013@gmail.com>
> ---
>  drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
> index 8d7042e68926..f6fdc0c5b475 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
> @@ -1112,7 +1112,8 @@ static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat)
>  	bsp_priv->clk_enabled = false;
>  
>  	bsp_priv->num_clks = ARRAY_SIZE(rk_clocks);
> -	if (phy_iface == PHY_INTERFACE_MODE_RMII)
> +	if (phy_iface == PHY_INTERFACE_MODE_RMII ||
> +	    phy_iface == PHY_INTERFACE_MODE_RGMII)

Apart from Heiko commenting that this patch is completely wrong, there
are 4 RGMII modes, not one. You should of used
phy_interface_mode_is_rgmii().

    Andrew

---
pw-bot: cr
 


^ permalink raw reply

* Re: [PATCH 6/7] ARM: dts: rockchip: Add RV1126 I2C5
From: Andrew Lunn @ 2026-06-24 15:42 UTC (permalink / raw)
  To: Yanan He
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Wu, Maxime Coquelin, Alexandre Torgue,
	devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
	netdev, linux-stm32
In-Reply-To: <20260624-rv1126-alientek-dlrv1126-v1-6-5aef608a3f64@gmail.com>

On Wed, Jun 24, 2026 at 04:44:43PM +0800, Yanan He wrote:
> The controller is present in the SoC and can be used by boards for
> external peripherals, such as an RTC on the Alientek DLRV1126 carrier
> board.

This has nothing to do with networking, so please post it separately.

What i would actually like to see is the patch adding networking
nodes, because my guess is, you have the RGMII delays wrong.

       Andrew


^ permalink raw reply

* Re: [PATCH v15 00/11] arm64: entry: Convert to Generic Entry
From: Ada Couprie Diaz @ 2026-06-24 15:44 UTC (permalink / raw)
  To: Jinjie Ruan
  Cc: mark.rutland, peterz, catalin.marinas, ldv, song, will, kees,
	thuth, ryan.roberts, anshuman.khandual, kevin.brodsky, pengcan,
	broonie, luto, linux-arm-kernel, wad, yeoreum.yun, oleg,
	linux-kernel, james.morse, tglx, liqiang01, linusw
In-Reply-To: <ec181396-e398-4ce2-8cb8-10d7bdfeed61@arm.com>

Hi Jinjie,

On 17/06/2026 17:27, Ada Couprie Diaz wrote:
> Hi Jinjie,
>
> On 11/05/2026 10:20, Jinjie Ruan wrote:
>> Currently, x86, Riscv, Loongarch use the Generic Entry which makes
>> maintainers' work easier and codes more elegant. arm64 has already
>> successfully switched to the Generic IRQ Entry in commit
>> b3cf07851b6c ("arm64: entry: Switch to generic IRQ entry"), it is
>> time to completely convert arm64 to Generic Entry.
>>
>> [...]
> [...], when combining pseudo-NMIs with PREEMPT_RT under heavy pNMI load,
> I was able to trigger a new warning compared to upstream :
>
>     BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low!
>
> Specifically, this was when running `stress-ng --all 100 --class vm -t 
> 300` with
> `perf top -a -e 'cycles'` in another shell.
>
> This does not feel like a major issue : from my understanding it only 
> happens
> when running the full suite for some time and with many stressors (I 
> was not
> able to reproduce it by running individual tests), and flooding the 
> system with
> pseudo-NMIs.
>
> Given that this only happen with PREEMPT_RT, my guess is that it 
> interacts
> with generic entry in a way that can lead to more nesting than before,
> leading to an easier exhaustion of the limit on lockdep.
> As the system was still able to recover and did not lock up, I think 
> it can be OK
> as-is, or simply bumped a bit ? Happy for more opinions on that.
>
>
> Otherwise, this is
> Tested-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
>
> As this is an important change, any other testing, especially on real 
> workloads
> as well as on very large systems (which we haven't covered), would be 
> very welcome !
>
>
> I will take some time soon to review this latest version, now that I 
> am able to.

I went through the series and only had some minor nitpicks, and some small
worries about the intentionality of the behaviour changes in patch 7.
It also feels like the above bug is more of a stress limit being easier 
to reach
than a core issue with the series, but I'd be happy to get more thoughts 
on it.

This looks great, and I am looking forward to seeing it merged ! :)
Thanks again for carrying this change through.

Kind regards,
Ada



^ permalink raw reply

* Re: [PATCH 7/7] ARM: dts: rockchip: Add Alientek DLRV1126
From: Andrew Lunn @ 2026-06-24 15:44 UTC (permalink / raw)
  To: Yanan He
  Cc: robh, krzk+dt, conor+dt, heiko, andrew+netdev, davem, edumazet,
	kuba, pabeni, david.wu, mcoquelin.stm32, alexandre.torgue,
	devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
	netdev, linux-stm32
In-Reply-To: <20260624-rv1126-alientek-dlrv1126-v1-7-dc42d99f75a7@gmail.com>

> The board consists of a CLRV1126F core module and a DLRV1126 carrier
> board. The core module contains the RV1126 SoC, eMMC and RK809 PMIC,
> while the carrier board provides Ethernet, SD card, AP6212 WiFi and
> Bluetooth, PCF8563 RTC, ADC keys, GPIO LEDs and audio connectors.
> 
> The board has been tested with Ethernet/NFS boot, eMMC, SD card, SDIO
> WiFi enumeration, Bluetooth LE scanning, RTC, ADC keys, GPIO LEDs and
> RK809 audio card registration.

Ah, here is the networking nodes. But why was it not threaded to the
rest of the series?

> +&gmac {
> +	phy-mode = "rgmii";
> +	clock_in_out = "input";
> +	assigned-clocks = <&cru CLK_GMAC_SRC>, <&cru CLK_GMAC_TX_RX>,
> +			  <&cru CLK_GMAC_ETHERNET_OUT>;
> +	assigned-clock-parents = <&cru CLK_GMAC_SRC_M1>,
> +				 <&cru RGMII_MODE_CLK>;
> +	assigned-clock-rates = <125000000>, <0>, <25000000>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&rgmiim1_miim &rgmiim1_bus2 &rgmiim1_bus4
> +		     &clk_out_ethernetm1_pins>;
> +	tx_delay = <0x2a>;
> +	rx_delay = <0x1a>;

As i predicted, this is wrong.

https://elixir.bootlin.com/linux/v6.15/source/Documentation/devicetree/bindings/net/ethernet-controller.yaml#L287

Please try removing rx_delay, rx_delay and setting phy-mode to
rgmii-id.

	Andrew


^ permalink raw reply

* [PATCH v2 2/2] arm64: dts: mediatek: mt8395-radxa-nio-12l: Enable i2c3 on 40-pin header
From: Ricardo Pardini via B4 Relay @ 2026-06-24 15:45 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno
  Cc: devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	Ricardo Pardini
In-Reply-To: <20260624-nio-12l-add-i2c-40-pin-v2-0-cf3707a6aaf1@pardini.net>

From: Ricardo Pardini <ricardo@pardini.net>

i2c3 (SDA3/SCL3 on GPIO14/GPIO15) is routed to the 40-pin GPIO header,
exposed on the blue-colored pins 27 (SCL3) and 28 (SDA3). Enable the
controller and add the corresponding pin configuration in the pinctrl
node so users can use external I2C devices.

Signed-off-by: Ricardo Pardini <ricardo@pardini.net>
---
 arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
index 589a5f07d5dde..9b0966c271cb5 100644
--- a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
@@ -371,6 +371,14 @@ it5205_sbu_mux: endpoint {
 	};
 };
 
+/* Exposed on 40-pin header (blue-colored pins 27:SCL3 28:SDA3) */
+&i2c3 {
+	clock-frequency = <400000>;
+	pinctrl-0 = <&i2c3_pins>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
 &i2c4 {
 	clock-frequency = <400000>;
 	pinctrl-0 = <&i2c4_pins>;
@@ -788,6 +796,15 @@ pins-bus {
 		};
 	};
 
+	i2c3_pins: i2c3-pins {
+		pins-bus {
+			pinmux = <PINMUX_GPIO14__FUNC_SDA3>,
+				 <PINMUX_GPIO15__FUNC_SCL3>;
+			bias-pull-up = <1000>;
+			drive-strength-microamp = <1000>;
+		};
+	};
+
 	i2c4_pins: i2c4-pins {
 		pins-bus {
 			pinmux = <PINMUX_GPIO16__FUNC_SDA4>,

-- 
2.54.0




^ permalink raw reply related

* [PATCH v2 0/2] arm64: dts: mediatek: mt8395-radxa-nio-12l: Enable i2c3 on 40-pin header
From: Ricardo Pardini via B4 Relay @ 2026-06-24 15:45 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno
  Cc: devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	Ricardo Pardini

The Radxa NIO 12L exposes i2c3 (SDA3/SCL3, GPIO14/GPIO15) on its 40-pin
GPIO header, on the blue-colored pins 27 (SCL3) and 28 (SDA3).

Enable the i2c3 controller, add the matching pinctrl configuration and run
the bus at 400 kHz, matching the other I2C buses already enabled on this
board.

While at it, drop a pre-existing redundant drive-strength from i2c2_pins
that was also about to be copied into i2c3: specifying both drive-strength
(mA) and drive-strength-microamp (uA) makes the generic pinconf parser log
"cannot have multiple drive strength properties" at boot, and the advanced
(uA) setting wins in hardware, leaving the mA value dead.

Tested using a SD1306 I2C OLED display.

---
Changes in v2:
- Add a drive-by patch dropping the redundant drive-strength in i2c2_pins
  (via Claude, reported by Sashiko).
- i2c3: use only drive-strength-microamp, as per Sashiko's review.
- Link to v1: https://patch.msgid.link/20260624-nio-12l-add-i2c-40-pin-v1-1-f6c11ed2184c@pardini.net

To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Matthias Brugger <matthias.bgg@gmail.com>
To: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-mediatek@lists.infradead.org
Signed-off-by: Ricardo Pardini <ricardo@pardini.net>

---
Ricardo Pardini (2):
      arm64: dts: mediatek: mt8395-radxa-nio-12l: Drop redundant i2c2 drive-strength
      arm64: dts: mediatek: mt8395-radxa-nio-12l: Enable i2c3 on 40-pin header

 arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260624-nio-12l-add-i2c-40-pin-19e0482fd835

Best regards,
--  
Ricardo Pardini <ricardo@pardini.net>




^ permalink raw reply

* [PATCH v2 1/2] arm64: dts: mediatek: mt8395-radxa-nio-12l: Drop redundant i2c2 drive-strength
From: Ricardo Pardini via B4 Relay @ 2026-06-24 15:45 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno
  Cc: devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	Ricardo Pardini
In-Reply-To: <20260624-nio-12l-add-i2c-40-pin-v2-0-cf3707a6aaf1@pardini.net>

From: Ricardo Pardini <ricardo@pardini.net>

The i2c2_pins node specifies both drive-strength (mA, standard driving)
and drive-strength-microamp (uA, advanced driving). These are mutually
exclusive: the generic pinconf parser logs "cannot have multiple drive
strength properties" at boot, and on MediaTek the advanced driving enable
bit makes the uA value authoritative, leaving the mA value dead.

Drop the redundant drive-strength, keeping only drive-strength-microamp,
matching i2c4_pins.

Signed-off-by: Ricardo Pardini <ricardo@pardini.net>
Assisted-by: Claude:claude-opus-4-8 # vs Sashiko review of upcoming i2c3
---
 arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
index bf91305e8e4a5..589a5f07d5dde 100644
--- a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
@@ -784,7 +784,6 @@ pins-bus {
 			pinmux = <PINMUX_GPIO12__FUNC_SDA2>,
 				 <PINMUX_GPIO13__FUNC_SCL2>;
 			bias-pull-up = <1000>;
-			drive-strength = <6>;
 			drive-strength-microamp = <1000>;
 		};
 	};

-- 
2.54.0




^ permalink raw reply related

* Re: [PATCH 05/37] drm/display: bridge-connector: split code creating the connector to a subfunction
From: Luca Ceresoli @ 2026-06-24 15:47 UTC (permalink / raw)
  To: Maxime Ripard, Luca Ceresoli
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
	Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
	Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
	linux-arm-kernel
In-Reply-To: <20260624-proud-unbiased-pony-ecc3c3@houat>

On Wed Jun 24, 2026 at 1:41 PM CEST, Maxime Ripard wrote:
> Hi,x
>
> On Fri, Jun 12, 2026 at 02:56:24PM +0200, Luca Ceresoli wrote:
>> On Mon Jun 8, 2026 at 1:40 PM CEST, Maxime Ripard wrote:
>> > On Tue, May 19, 2026 at 12:37:22PM +0200, Luca Ceresoli wrote:
>> >> In preparation to introduce bridge hotplug, split out from
>> >> drm_bridge_connector_init() the code adding the drm_connector into a
>> >> dedicated function. This will be needed to be able to add (and re-add) the
>> >> connector from different code paths.
>> >
>> > Same story here, explaining what you need later on that calls for that
>> > change would be nice.
>>
>> Here's a more verbose version:
>>
>>     Currently drm_bridge_connector_init() does two things:
>>
>>      * allocate and initialize the drm_bridge_connector
>>        (which embeds a drm_connector)
>>      * initialize and register the embedded drm_connector
>>
>>     For bridge hotplug we need to separate these two actions:
>>
>>      * the drm_connector needs to be added and removed at any time based on
>>        hotplug events
>>      * the drm_bridge_connector is designated to create and remove the
>>        drm_connector, so it must be persistent for the card lifetime
>>
>>     As the lifetimes of drm_bridge_connector and drm_connector become
>>     different, we need to create them in different moments.
>>
>>     In preparation to support that, split out from
>>     drm_bridge_connector_init() the code adding the drm_connector into a
>>     dedicated function. No functional changes, just moving code around for
>>     now. A future commit will make the drm_connector be created based on
>>     hotplug events.
>>
>> Looks good?
>
> The message itself, yes, thanks.
>
> However, I have questions now :)
>
> Do we really expect drm_bridge_connector to stick around when a bridge
> gets unplugged? If so, how does it cope with having, say, an HDMI
> connector, and then swapping out the hotplugged part for an LVDS one?
> Does the HDMI connector sticks around indefinitely?

In your example, the HDMI drm_connector would be unregistered and put on
hotunplug. Its allocation will stick around until the last put but that's
quite irrelevant. Then, on plugging the LVDS addon, a new LVDS
drm_connector will be created and registered.

> *Especially* if we're using overlays for this, I'd expect everything
>  after the first hotplugged bridge to be destroyed, no?

As said, it would be unregistered immediately but might be freed later on
if still refcounted.

This is visible in patches 36+15, the path to follow is:

 drm_bridge_connector_handle_event(event = DRM_BRIDGE_DETACHED) [patch 36]
 -> drm_bridge_connector_dynconn_release()                      [patch 15]

Does this solve your concern?

Luca

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply

* Re: mm: opaque hardware page-table entry handles
From: Zi Yan @ 2026-06-24 15:52 UTC (permalink / raw)
  To: Usama Anjum, Andrew Morton, Lorenzo Stoakes, David Hildenbrand,
	Liam R. Howlett, Mike Rapoport, Ryan Roberts, Anshuman Khandual,
	Catalin Marinas, Will Deacon, Samuel Holland
  Cc: linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <74182e50-b54f-4d2d-a27f-3a59a538d6bc@arm.com>

On Wed Jun 24, 2026 at 10:09 AM EDT, Usama Anjum wrote:
> Hi all,
>
> This is a direction-check with the wider community before spending time on the
> development. This picks up the idea that was raised and broadly agreed in the
> earlier thread (Ryan Roberts, Lorenzo Stoakes, David Hildenbrand) [1].
>
> The problem
> -----------
> Core MM code reaches page-table entries by raw pointer dereference (pte_t *,
> pmd_t *, *pud, ...) in places, implicitly assuming a single, uniform
> representation. Sprinkling getters wouldn't solve the problem entirely. The
> problem is one level up: the *pointer type* itself is overloaded. At each level
> there are really three distinct things:
>
>   1. a page-table entry value (pte_t, pmd_t, ...)
>   2. a pointer to an entry value, e.g. a pXX_t on the stack
>   3. a pointer to a live entry in the hardware page table

This sounds good to me, but can you clarify the situation below?

A live entry means the entry can be accessed by hardware when the code
is manipulating it? What type should we use if we are pre-populating
PTEs in a PMD page before we establish the PMD page as a HW page table?
In __split_huge_pmd_locked(), we do that. A PMD page is first withdrawn
and filled with after-split PTEs, pmd_populate() and pte_offset_map()
are used for this not-yet-HW page table. Later, pmd_populate() is used
to make this page table visible to HW. Should we have two versions of
pmd_populate() and pte_offset_map()? Since the first pmd_populate()
would accept pmd_t*, but the second one would accept hw_pmdp, if we are
pedantic. Of course, we can be flexible here to use pmd_populate()
accpeting hw_pmdp for both, since the PMD page table we are modifying
is going to be visible to HW soon. But I think we should have clear
definitions for where these types are used and document them well.

You probably can ask LLMs to check these ambiguous/vague uses throughout
the code base.

>
> Today (2) and (3) share the same type - pte_t *, pmd_t *, and so on. Nothing
> distinguishes a pointer into a live table from a pointer to a stack copy.
>
> A pointer to an on-stack entry value and a pointer to a live hardware entry have
> the same type, so the compiler cannot distinguish them. Passing the stack
> pointer to an arch helper that expects a hardware-entry pointer compiles fine,
> but is wrong - a bug class the type system makes invisible. It also blocks
> evolution: an arch helper may need to read beyond the addressed entry (e.g.
> adjacent or contiguous entries), which only makes sense for a real page-table
> pointer, not a stack copy.
>
> The idea
> --------
> Give (3) its own opaque type that cannot be dereferenced:
>
>     /* opaque handle to a HW page-table entry; not dereferenceable */
>     typedef struct {
> 	pte_t *ptr;
>     } hw_ptep;
>
> With this:
>
>   - a stack value can no longer masquerade as a hardware table entry,
>   - a hardware handle can no longer be raw-dereferenced,
>   - cases that genuinely operate on a value can be refactored to pass the value
>     and let the caller, which knows whether it holds a handle or a stack copy,
>     read it once.
>
> The overload becomes a compile-time type error instead of a silent runtime bug,
> and converting the tree forces every such site to be made explicit. This gives
> us a framework where the architecture can completely virtualize the pgtable if
> it likes; and the compiler can enforce that higher level code can't accidentally
> work around it.
>
> It is opt-in by architectures and incremental. The generic definition is
> just an alias, so arches that do not care build unchanged:
>
>     typedef pte_t *hw_ptep;
>
> An arch flips to the strong struct type when it is ready, and only then does
> it get the stronger checking. This lets the conversion land gradually.
>
> Beyond fixing the latent bug class, this abstraction is an enabler for upcoming
> features that need tighter control over how page tables are accessed and
> manipulated.
>
> Getter flavours
> ---------------
> While converting, it is useful to have two accessor flavours at each level:
>
>   - pXXp_get(hw_ptep)        plain C dereference (compiler may optimize)
>   - pXXp_get_once(hw_ptep)   single-copy-atomic, not torn, elided or
>                              duplicated by the compiler
>
> Keeping them distinct simplifies the conversion and avoids re-introducing the
> class of lockless-read bugs seen on 32-bit.
>
> Example conversion
> ------------------
> Most of the conversion is mechanical.
>
>   -static inline void set_ptes(struct mm_struct *mm, unsigned long addr,
>   -		pte_t *ptep, pte_t pte, unsigned int nr)
>   +static inline void set_ptes(struct mm_struct *mm, unsigned long addr,
>   +		hw_ptep ptep, pte_t pte, unsigned int nr)
>    {
>    	page_table_check_ptes_set(mm, addr, ptep, pte, nr);
>    	for (;;) {
>    		set_pte(ptep, pte);
>    		if (--nr == 0)
>    			break;
>   -		ptep++;
>   +		ptep = hw_pte_next(ptep);
>    		pte = pte_next_pfn(pte);
>    	}
>    }
>
> The bulk of work is this kind of rote substitution. The genuine work is the
> handful of sites that turn out to be operating on a stack copy rather than a
> live entry - those are exactly the ones the new type forces us to surface and 
> fix.
>
> Estimated churn:
> ----------------
> Half way through the prototyping converting only PTE and PMD levels:
>   77 files changed, +1801 / -1425
>   ~57 files reference the new types
>
> So the line count will grow once PUD/P4D/PGD and the remaining call sites are
> converted; expect meaningfully more churn than the numbers above.
>
> Introduce the type as an alias, convert one helper family per patch, and flip
> an arch to the strong type last - with non-opted arches building unchanged at
> every step.
>
> Open questions
> --------------
>   - Is the type-safety + future-feature enablement worth the churn?
>   - Naming: hw_ptep/hw_pmdp vs something else?
>   - Should all five levels be converted before merging anything, or is a staged
>     PTE-and-PMD then landing others acceptable?
>   - Do we want the two getter flavours (pXXp_get / pXXp_get_once) at every
>     level?
>
> [1] https://lore.kernel.org/all/a063f6c5-2785-4a9f-8079-25edb3e54cef@arm.com
>
> Thanks,
> Usama




-- 
Best Regards,
Yan, Zi



^ permalink raw reply

* Re: [PATCH v3 1/7] net: wwan: t9xx: Add PCIe core
From: Andrew Lunn @ 2026-06-24 15:56 UTC (permalink / raw)
  To: jackbb_wu
  Cc: Loic Poulain, Sergey Ryazanov, Johannes Berg, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Wen-Zhi Huang, Shi-Wei Yeh, Minano Tseng, Matthias Brugger,
	AngeloGioacchino Del Regno, Simon Horman, Jonathan Corbet,
	Shuah Khan, linux-kernel, netdev, linux-arm-kernel,
	linux-mediatek, linux-doc
In-Reply-To: <20260624-t9xx_driver_v1-v3-1-73ff03f60c48@compal.com>


> From: Jack Wu <jackbb_wu@compal.com>
> 
> Registers the T900 device driver with the kernel. Set up all
> the fundamental configurations for the device: PCIe layer,
> Modem Host Cross Core Interface (MHCCIF), Reset Generation
> Unit (RGU), modem common control operations and build
> infrastructure.
> 
> * PCIe layer code implements driver probe and removal, MSI-X
>   interrupt initialization and de-initialization, and the way
>   of resetting the device.
> * MHCCIF provides interrupt channels to communicate events
>   such as handshake, PM and port enumeration.
> * RGU provides interrupt channels to generate notifications
>   from the device so that the T900 driver could get the
>   device reset.
> * Modem common control operations provide the basic read/write
>   functions of the device's hardware registers,
>   mask/unmask/get/clear functions of the device's interrupt
>   registers and inquiry functions of the device's status.
> 
> Signed-off-by: Jack Wu <jackbb_wu@compal.com>
> ---
>  drivers/net/wwan/Kconfig                      |   12 +
>  drivers/net/wwan/Makefile                     |    1 +
>  drivers/net/wwan/t9xx/Makefile                |   10 +
>  drivers/net/wwan/t9xx/mtk_dev.h               |  108 +++
>  drivers/net/wwan/t9xx/pcie/mtk_pci.c          | 1049 +++++++++++++++++++++++++
>  drivers/net/wwan/t9xx/pcie/mtk_pci.h          |  234 ++++++
>  drivers/net/wwan/t9xx/pcie/mtk_pci_drv_m9xx.c |   69 ++
>  drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h      |   70 ++
>  8 files changed, 1553 insertions(+)
> 
> diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
> index 88df55d78d90..4cee537c739f 100644
> --- a/drivers/net/wwan/Kconfig
> +++ b/drivers/net/wwan/Kconfig
> @@ -121,6 +121,18 @@ config MTK_T7XX
>  
>  	  If unsure, say N.
>  
> +config MTK_T9XX
> +	tristate "MediaTek PCIe 5G WWAN modem T9xx device"
> +	depends on PCI
> +	select NET_DEVLINK
> +	help
> +	  Enables MediaTek PCIe based 5G WWAN modem (T9xx series) device.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called mtk_t9xx.
> +
> +	  If unsure, say N.
> +
>  endif # WWAN
>  
>  endmenu
> diff --git a/drivers/net/wwan/Makefile b/drivers/net/wwan/Makefile
> index 3960c0ae2445..7361eef4c472 100644
> --- a/drivers/net/wwan/Makefile
> +++ b/drivers/net/wwan/Makefile
> @@ -14,3 +14,4 @@ obj-$(CONFIG_QCOM_BAM_DMUX) += qcom_bam_dmux.o
>  obj-$(CONFIG_RPMSG_WWAN_CTRL) += rpmsg_wwan_ctrl.o
>  obj-$(CONFIG_IOSM) += iosm/
>  obj-$(CONFIG_MTK_T7XX) += t7xx/
> +obj-$(CONFIG_MTK_T9XX) += t9xx/
> diff --git a/drivers/net/wwan/t9xx/Makefile b/drivers/net/wwan/t9xx/Makefile
> new file mode 100644
> index 000000000000..6f2dd3f91454
> --- /dev/null
> +++ b/drivers/net/wwan/t9xx/Makefile
> @@ -0,0 +1,10 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +ccflags-y += -I$(src)/pcie
> +ccflags-y += -I$(src)
> +
> +obj-$(CONFIG_MTK_T9XX) += mtk_t9xx.o
> +
> +mtk_t9xx-y := \
> +	pcie/mtk_pci.o \
> +	pcie/mtk_pci_drv_m9xx.o
> diff --git a/drivers/net/wwan/t9xx/mtk_dev.h b/drivers/net/wwan/t9xx/mtk_dev.h
> new file mode 100644
> index 000000000000..8278a0e2875e
> --- /dev/null
> +++ b/drivers/net/wwan/t9xx/mtk_dev.h
> @@ -0,0 +1,108 @@
> +/* SPDX-License-Identifier: GPL-2.0-only
> + *
> + * Copyright (c) 2022, MediaTek Inc.
> + */
> +
> +#ifndef __MTK_DEV_H__
> +#define __MTK_DEV_H__
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/dmapool.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +#define MTK_DEV_STR_LEN 16
> +
> +enum mtk_user_id {
> +	MTK_USER_MIN,
> +	MTK_USER_CTRL,
> +	MTK_USER_DATA,
> +	MTK_USER_MAX
> +};
> +
> +enum mtk_dev_evt_h2d {
> +	DEV_EVT_H2D_DEVICE_RESET	= BIT(2),
> +	DEV_EVT_H2D_MAX			= BIT(5)
> +};
> +
> +enum mtk_dev_evt_d2h {
> +	DEV_EVT_D2H_BOOT_FLOW_SYNC	= BIT(4),
> +	DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP = BIT(5),
> +	DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD	= BIT(6),
> +	DEV_EVT_D2H_MAX			= BIT(11)
> +};
> +
> +struct mtk_md_dev;
> +
> +struct mtk_dev_ops {
> +	u32 (*get_dev_state)(struct mtk_md_dev *mdev);
> +	void (*ack_dev_state)(struct mtk_md_dev *mdev, u32 state);
> +	u32 (*get_dev_cfg)(struct mtk_md_dev *mdev);
> +	int (*register_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt,
> +				int (*evt_cb)(u32 status, void *data), void *data);
> +	void (*unregister_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
> +	void (*mask_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
> +	void (*unmask_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
> +	void (*clear_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
> +	int (*send_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
> +};
> +
> +/* mtk_md_dev defines the structure of MTK modem device */
> +struct mtk_md_dev {
> +	struct device *dev;
> +	const struct mtk_dev_ops *dev_ops;
> +	void *hw_priv;
> +	u32 hw_ver;
> +	char dev_str[MTK_DEV_STR_LEN];
> +};
> +
> +static inline u32 mtk_dev_get_dev_state(struct mtk_md_dev *mdev)
> +{
> +	return mdev->dev_ops->get_dev_state(mdev);
> +}
> +
> +static inline void mtk_dev_ack_dev_state(struct mtk_md_dev *mdev, u32 state)
> +{
> +	return mdev->dev_ops->ack_dev_state(mdev, state);
> +}
> +
> +static inline u32 mtk_dev_get_dev_cfg(struct mtk_md_dev *mdev)
> +{
> +	return mdev->dev_ops->get_dev_cfg(mdev);
> +}
> +
> +static inline int mtk_dev_register_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt,
> +					   int (*evt_cb)(u32 status, void *data), void *data)
> +{
> +	return mdev->dev_ops->register_dev_evt(mdev, dev_evt, evt_cb, data);
> +}
> +
> +static inline void mtk_dev_unregister_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
> +{
> +	mdev->dev_ops->unregister_dev_evt(mdev, dev_evt);
> +}
> +
> +static inline void mtk_dev_mask_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
> +{
> +	mdev->dev_ops->mask_dev_evt(mdev, dev_evt);
> +}
> +
> +static inline void mtk_dev_unmask_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
> +{
> +	mdev->dev_ops->unmask_dev_evt(mdev, dev_evt);
> +}
> +
> +static inline void mtk_dev_clear_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
> +{
> +	mdev->dev_ops->clear_dev_evt(mdev, dev_evt);
> +}
> +
> +static inline int mtk_dev_send_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
> +{
> +	return mdev->dev_ops->send_dev_evt(mdev, dev_evt);
> +}
> +
> +#endif /* __MTK_DEV_H__ */
> diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
> new file mode 100644
> index 000000000000..c6a7196fcdd6
> --- /dev/null
> +++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
> @@ -0,0 +1,1049 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2022, MediaTek Inc.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/aer.h>
> +#include <linux/bitfield.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include "mtk_dev.h"
> +#include "mtk_pci.h"
> +#include "mtk_pci_reg.h"
> +
> +#define MTK_PCI_BAR_NUM		6
> +#define MTK_PCI_TRANSPARENT_ATR_SIZE	(0x3F)
> +#define MTK_PCI_MINIMUM_ATR_SIZE	(0x1000)
> +#define ATR_SIZE_LO32_MASK		GENMASK_ULL(31, 0)
> +#define ATR_SIZE_HI32_MASK		GENMASK_ULL(63, 32)
> +#define ATR_SIZE_BIAS_FROM_LO32		2
> +#define ATR_ADDR_ALIGN_MASK		0xFFFFF000
> +#define ATR_EN				BIT(0)
> +#define ATR_PARAM_OFFSET		16
> +/* Delay between ACPI PXP._OFF and _ON for modem power cycle stabilization */
> +#define MTK_PLDR_POWER_OFF_DELAY_MS	500
> +#define LE32_TO_U32(x) ((__force u32)(__le32)(x))
> +#define SET_HW_BITS(dest, chs, mhccif, dev)		\
> +	({						\
> +		if ((chs) & (dev))					\
> +			(dest) |= FIELD_PREP(mhccif, 1);		\
> +	})
> +
> +struct mtk_mhccif_cb {
> +	struct list_head entry;
> +	int (*evt_cb)(u32 status, void *data);
> +	void *data;
> +	u32 chs;
> +};
> +
> +/**
> + * mtk_pci_setup_atr() - Configure a PCIe address translation rule
> + * @mdev: MTK MD device
> + * @cfg: ATR configuration parameters
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_setup_atr(struct mtk_md_dev *mdev, struct mtk_atr_cfg *cfg)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +	u32 addr, val, size_h, size_l;
> +	int atr_size, pos, offset;
> +
> +	if (cfg->transparent) {
> +		/* No address conversion is performed */
> +		atr_size = MTK_PCI_TRANSPARENT_ATR_SIZE;
> +	} else {
> +		if (cfg->size < MTK_PCI_MINIMUM_ATR_SIZE)
> +			cfg->size = MTK_PCI_MINIMUM_ATR_SIZE;
> +
> +		if (cfg->src_addr & (cfg->size - 1)) {
> +			dev_err((mdev)->dev, "Invalid atr src addr is not aligned to size\n");
> +			return -EFAULT;
> +		}
> +
> +		if (cfg->trsl_addr & (cfg->size - 1)) {
> +			dev_err((mdev)->dev,
> +				"Invalid atr trsl addr is not aligned to size, %llx, %llx\n",
> +				cfg->trsl_addr, cfg->size - 1);
> +			return -EFAULT;
> +		}
> +
> +		size_l = FIELD_GET(ATR_SIZE_LO32_MASK, cfg->size);
> +		size_h = FIELD_GET(ATR_SIZE_HI32_MASK, cfg->size);
> +		pos = ffs(size_l);
> +		if (pos) {
> +			atr_size = pos - ATR_SIZE_BIAS_FROM_LO32;
> +		} else {
> +			pos = ffs(size_h);
> +			atr_size = pos + 32 - ATR_SIZE_BIAS_FROM_LO32;
> +		}
> +	}
> +
> +	/* Calculate table offset */
> +	offset = ATR_PORT_OFFSET * cfg->port + ATR_TABLE_OFFSET * cfg->table;
> +	addr = REG_ATR_PCIE_WIN0_T0_SRC_ADDR_MSB + offset;
> +	val = (u32)(cfg->src_addr >> 32);
> +	mtk_pci_mac_write32(priv, addr, val);
> +
> +	addr = REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset;
> +	val = (u32)(cfg->src_addr & ATR_ADDR_ALIGN_MASK) | (atr_size << 1) | ATR_EN;
> +	mtk_pci_mac_write32(priv, addr, val);
> +
> +	addr = REG_ATR_PCIE_WIN0_T0_TRSL_ADDR_MSB + offset;
> +	val = (u32)(cfg->trsl_addr >> 32);
> +	mtk_pci_mac_write32(priv, addr, val);
> +
> +	addr = REG_ATR_PCIE_WIN0_T0_TRSL_ADDR_LSB + offset;
> +	val = (u32)(cfg->trsl_addr & ATR_ADDR_ALIGN_MASK);
> +	mtk_pci_mac_write32(priv, addr, val);
> +
> +	/* TRSL_PARAM */
> +	addr = REG_ATR_PCIE_WIN0_T0_TRSL_PARAM + offset;
> +	val = (cfg->trsl_param << ATR_PARAM_OFFSET) | cfg->trsl_id;
> +	mtk_pci_mac_write32(priv, addr, val);
> +
> +	return 0;
> +}
> +
> +/**
> + * mtk_pci_atr_disable() - Disable all PCIe address translation rules
> + * @priv: MTK PCI private data
> + */
> +void mtk_pci_atr_disable(struct mtk_pci_priv *priv)
> +{
> +	int port, tbl, offset;
> +	u32 val;
> +
> +	/* Disable all ATR table for all ports */
> +	for (port = ATR_SRC_PCI_WIN0; port <= ATR_SRC_AXIS_3; port++)
> +		for (tbl = 0; tbl < ATR_TABLE_NUM_PER_ATR; tbl++) {
> +			/* Calculate table offset */
> +			offset = ATR_PORT_OFFSET * port + ATR_TABLE_OFFSET * tbl;
> +			val = mtk_pci_mac_read32(priv, REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset);
> +			val = val & (~BIT(0));
> +			/* Disable table by SRC_ADDR_L */
> +			mtk_pci_mac_write32(priv, REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset, val);
> +		}
> +}
> +
> +static void mtk_pci_set_msix_merged(struct mtk_pci_priv *priv, int irq_cnt)
> +{
> +	mtk_pci_mac_write32(priv, REG_PCIE_CFG_MSIX, ffs(irq_cnt) * 2 - 1);
> +}
> +
> +/**
> + * mtk_pci_get_dev_state() - Read the device state from the modem
> + * @mdev: MTK MD device
> + *
> + * Return: Device state value.
> + */
> +u32 mtk_pci_get_dev_state(struct mtk_md_dev *mdev)
> +{
> +	return mtk_pci_mac_read32(mdev->hw_priv, REG_PCIE_DEBUG_DUMMY_7);
> +}
> +
> +/**
> + * mtk_pci_ack_dev_state() - Acknowledge the device state to the modem
> + * @mdev: MTK MD device
> + * @state: State value to acknowledge
> + */
> +void mtk_pci_ack_dev_state(struct mtk_md_dev *mdev, u32 state)
> +{
> +	mtk_pci_mac_write32(mdev->hw_priv, REG_PCIE_DEBUG_DUMMY_7, state);
> +}
> +
> +/**
> + * mtk_pci_get_irq_id() - Map an IRQ source to its hardware IRQ ID
> + * @mdev: MTK MD device
> + * @irq_src: IRQ source enum
> + *
> + * Return: IRQ ID on success, -EINVAL on failure.
> + */
> +int mtk_pci_get_irq_id(struct mtk_md_dev *mdev, enum mtk_irq_src irq_src)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +	const int *irq_tbl = priv->cfg->irq_tbl;
> +	int irq_id = -EINVAL;
> +
> +	if (irq_src > MTK_IRQ_SRC_MIN && irq_src < MTK_IRQ_SRC_MAX) {
> +		irq_id = irq_tbl[irq_src];
> +		if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX)
> +			irq_id = -EINVAL;
> +	}
> +
> +	return irq_id;
> +}
> +
> +/**
> + * mtk_pci_get_virq_id() - Get the Linux virtual IRQ for a hardware IRQ ID
> + * @mdev: MTK MD device
> + * @irq_id: Hardware IRQ ID
> + *
> + * Return: Virtual IRQ number on success, negative error code on failure.
> + */
> +int mtk_pci_get_virq_id(struct mtk_md_dev *mdev, int irq_id)
> +{
> +	struct pci_dev *pdev = to_pci_dev(mdev->dev);
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +
> +	if (!priv->irq_cnt || irq_id < 0)
> +		return -EINVAL;
> +
> +	return pci_irq_vector(pdev, irq_id % priv->irq_cnt);
> +}
> +
> +/**
> + * mtk_pci_register_irq() - Register a callback for a hardware IRQ
> + * @mdev: MTK MD device
> + * @irq_id: Hardware IRQ ID
> + * @irq_cb: Callback function
> + * @data: Private data passed to callback
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_register_irq(struct mtk_md_dev *mdev, int irq_id,
> +			 int (*irq_cb)(int irq_id, void *data), void *data)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +
> +	if ((irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX) || !irq_cb)
> +		return -EINVAL;
> +
> +	if (priv->irq_cb_list[irq_id]) {
> +		dev_err((mdev)->dev,
> +			"Unable to register irq, irq_id=%d, it's already been register by %ps.\n",
> +			irq_id, priv->irq_cb_list[irq_id]);
> +		return -EFAULT;
> +	}
> +	priv->irq_cb_list[irq_id] = irq_cb;
> +	priv->irq_cb_data[irq_id] = data;
> +
> +	return 0;
> +}
> +
> +/**
> + * mtk_pci_unregister_irq() - Unregister a hardware IRQ callback
> + * @mdev: MTK MD device
> + * @irq_id: Hardware IRQ ID
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_unregister_irq(struct mtk_md_dev *mdev, int irq_id)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +
> +	if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX)
> +		return -EINVAL;
> +
> +	if (!priv->irq_cb_list[irq_id]) {
> +		dev_err((mdev)->dev, "irq_id=%d has not been registered\n", irq_id);
> +		return -EFAULT;
> +	}
> +	priv->irq_cb_list[irq_id] = NULL;
> +	priv->irq_cb_data[irq_id] = NULL;
> +
> +	return 0;
> +}
> +
> +/**
> + * mtk_pci_mask_irq() - Mask (disable) a hardware IRQ
> + * @mdev: MTK MD device
> + * @irq_id: Hardware IRQ ID
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_mask_irq(struct mtk_md_dev *mdev, int irq_id)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +
> +	if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX ||
> +	    priv->irq_type != PCI_IRQ_MSIX) {
> +		dev_err(mdev->dev, "Failed to mask irq: input irq_id=%d\n", irq_id);
> +		return -EINVAL;
> +	}
> +
> +	mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0, BIT(irq_id));
> +
> +	return 0;
> +}
> +
> +/**
> + * mtk_pci_unmask_irq() - Unmask (enable) a hardware IRQ
> + * @mdev: MTK MD device
> + * @irq_id: Hardware IRQ ID
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_unmask_irq(struct mtk_md_dev *mdev, int irq_id)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +
> +	if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX ||
> +	    priv->irq_type != PCI_IRQ_MSIX) {
> +		dev_err(mdev->dev, "Failed to unmask irq: input irq_id=%d\n", irq_id);
> +		return -EINVAL;
> +	}
> +
> +	mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_SET_GRP0_0, BIT(irq_id));
> +
> +	return 0;
> +}
> +
> +/**
> + * mtk_pci_clear_irq() - Clear (acknowledge) a hardware IRQ
> + * @mdev: MTK MD device
> + * @irq_id: Hardware IRQ ID
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_clear_irq(struct mtk_md_dev *mdev, int irq_id)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +
> +	if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX ||
> +	    priv->irq_type != PCI_IRQ_MSIX) {
> +		dev_err(mdev->dev, "Failed to clear irq: input irq_id=%d\n", irq_id);
> +		return -EINVAL;
> +	}
> +
> +	mtk_pci_mac_write32(priv, REG_MSIX_ISTATUS_HOST_GRP0_0, BIT(irq_id));
> +
> +	return 0;
> +}
> +
> +static u32 mtk_pci_ext_d2h_evt_hw_bits(u32 chs)
> +{
> +	u32 hw_bits = 0;
> +
> +	SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_BOOT_FLOW_SYNC,
> +		    DEV_EVT_D2H_BOOT_FLOW_SYNC);
> +	SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_SAP,
> +		    DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP);
> +	SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_MD,
> +		    DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD);
> +
> +	return LE32_TO_U32(cpu_to_le32(hw_bits));
> +}
> +
> +static u32 mtk_pci_ext_d2h_evt_chs(u32 hw_bits)
> +{
> +	u32 chs = 0;
> +
> +	if (!hw_bits)
> +		return chs;
> +
> +	chs = FIELD_PREP(DEV_EVT_D2H_BOOT_FLOW_SYNC,
> +			 FIELD_GET(MHCCIF_EP2RC_EVT_BOOT_FLOW_SYNC, hw_bits)) |
> +	      FIELD_PREP(DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP,
> +			 FIELD_GET(MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_SAP, hw_bits)) |
> +	      FIELD_PREP(DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD,
> +			 FIELD_GET(MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_MD, hw_bits));
> +
> +	return chs;
> +}
> +
> +/**
> + * mtk_pci_register_ext_evt() - Register a callback for MHCCIF device events
> + * @mdev: MTK MD device
> + * @chs: Bitmask of event channels to register
> + * @evt_cb: Callback function
> + * @data: Private data passed to callback
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_register_ext_evt(struct mtk_md_dev *mdev, u32 chs,
> +			     int (*evt_cb)(u32 status, void *data), void *data)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +	struct mtk_mhccif_cb *cb;
> +	int ret = 0;
> +
> +	if (!chs || !evt_cb)
> +		return -EINVAL;
> +
> +	spin_lock_bh(&priv->mhccif_lock);
> +	list_for_each_entry(cb, &priv->mhccif_cb_list, entry) {
> +		if (cb->chs & chs) {
> +			ret = -EFAULT;
> +			dev_err((mdev)->dev,
> +				"Unable to register evt, intersection: chs=0x%08x&0x%08x cb=%ps\n",
> +				chs, cb->chs, cb->evt_cb);
> +			goto err_spin_unlock;
> +		}
> +	}
> +	cb = devm_kzalloc(mdev->dev, sizeof(*cb), GFP_ATOMIC);
> +	if (!cb) {
> +		ret = -ENOMEM;
> +		goto err_spin_unlock;
> +	}
> +	cb->evt_cb = evt_cb;
> +	cb->data = data;
> +	cb->chs = chs;
> +	list_add_tail(&cb->entry, &priv->mhccif_cb_list);
> +err_spin_unlock:
> +	spin_unlock_bh(&priv->mhccif_lock);
> +
> +	return ret;
> +}
> +
> +/**
> + * mtk_pci_unregister_ext_evt() - Unregister an MHCCIF device event callback
> + * @mdev: MTK MD device
> + * @chs: Bitmask of event channels to unregister
> + */
> +void mtk_pci_unregister_ext_evt(struct mtk_md_dev *mdev, u32 chs)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +	struct mtk_mhccif_cb *cb, *next;
> +
> +	if (!chs)
> +		return;
> +
> +	spin_lock_bh(&priv->mhccif_lock);
> +	list_for_each_entry_safe(cb, next, &priv->mhccif_cb_list, entry) {
> +		if (cb->chs == chs) {
> +			list_del(&cb->entry);
> +			devm_kfree(mdev->dev, cb);
> +			goto out;
> +		}
> +	}
> +	dev_warn((mdev)->dev,
> +		 "Unable to unregister evt, no chs=0x%08x has been registered.\n", chs);
> +out:
> +	spin_unlock_bh(&priv->mhccif_lock);
> +}
> +
> +/**
> + * mtk_pci_mask_ext_evt() - Mask (disable) MHCCIF device events
> + * @mdev: MTK MD device
> + * @chs: Bitmask of event channels to mask
> + */
> +void mtk_pci_mask_ext_evt(struct mtk_md_dev *mdev, u32 chs)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +	u32 hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
> +
> +	mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
> +			MHCCIF_EP2RC_SW_INT_EAP_MASK_SET, hw_bits);
> +}
> +
> +/**
> + * mtk_pci_unmask_ext_evt() - Unmask (enable) MHCCIF device events
> + * @mdev: MTK MD device
> + * @chs: Bitmask of event channels to unmask
> + */
> +void mtk_pci_unmask_ext_evt(struct mtk_md_dev *mdev, u32 chs)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +	u32 hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
> +
> +	mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
> +			MHCCIF_EP2RC_SW_INT_EAP_MASK_CLR, hw_bits);
> +}
> +
> +/**
> + * mtk_pci_clear_ext_evt() - Clear (acknowledge) MHCCIF device events
> + * @mdev: MTK MD device
> + * @chs: Bitmask of event channels to clear
> + */
> +void mtk_pci_clear_ext_evt(struct mtk_md_dev *mdev, u32 chs)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +	u32 hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
> +
> +	mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
> +			MHCCIF_EP2RC_SW_INT_ACK, hw_bits);
> +}
> +
> +static u32 mtk_pci_ext_h2d_evt_hw_bits(u32 chs)
> +{
> +	u32 hw_bits = 0;
> +
> +	SET_HW_BITS(hw_bits, chs, MHCCIF_RC2EP_EVT_DEVICE_RESET,
> +		    DEV_EVT_H2D_DEVICE_RESET);
> +	return LE32_TO_U32(cpu_to_le32(hw_bits));
> +}
> +
> +/**
> + * mtk_pci_send_ext_evt() - Send an MHCCIF event to the modem
> + * @mdev: MTK MD device
> + * @ch: Event channel to trigger (must be a single bit)
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_send_ext_evt(struct mtk_md_dev *mdev, u32 ch)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +	u32 rc_base, hw_bits;
> +
> +	rc_base = priv->cfg->mhccif_rc_base_addr;
> +
> +	/* Only allow one ch to be triggered at a time */
> +	if (!is_power_of_2(ch)) {
> +		dev_err((mdev)->dev, "Unsupported ext evt ch=0x%08x\n", ch);
> +		return -EINVAL;
> +	}
> +
> +	hw_bits = mtk_pci_ext_h2d_evt_hw_bits(ch);
> +	mtk_pci_write32(mdev, rc_base + MHCCIF_RC2EP_SW_BSY, hw_bits);
> +	mtk_pci_write32(mdev, rc_base + MHCCIF_RC2EP_SW_TCHNUM, ffs(hw_bits) - 1);
> +	return 0;
> +}
> +
> +static u32 mtk_pci_get_ext_evt_hw_status(struct mtk_md_dev *mdev)
> +{
> +	struct mtk_pci_priv *priv = mdev->hw_priv;
> +
> +	return mtk_pci_read32(mdev, priv->cfg->mhccif_rc_base_addr +
> +			      MHCCIF_EP2RC_SW_INT_STS);
> +}
> +
> +/**
> + * mtk_pci_fldr() - Perform a Function Level Device Reset via ACPI _RST
> + * @mdev: MTK MD device
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int mtk_pci_fldr(struct mtk_md_dev *mdev)
> +{
> +#ifdef CONFIG_ACPI

...

> +#else /* !CONFIG_ACPI */
> +	dev_err((mdev)->dev, "Unsupported, CONFIG ACPI hasn't been set to 'y'\n");

Why not just have the Kconfig depend on ACPI?

> +	if (ret) {
> +		dev_err((mdev)->dev, "Failed to register mhccif_irq callback\n");

Why the () around mdev?


    Andrew

---
pw-bot: cr


^ permalink raw reply

* Re: [PATCH v3 2/7] net: wwan: t9xx: Add control plane transaction layer
From: Andrew Lunn @ 2026-06-24 16:00 UTC (permalink / raw)
  To: jackbb_wu
  Cc: Loic Poulain, Sergey Ryazanov, Johannes Berg, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Wen-Zhi Huang, Shi-Wei Yeh, Minano Tseng, Matthias Brugger,
	AngeloGioacchino Del Regno, Simon Horman, Jonathan Corbet,
	Shuah Khan, linux-kernel, netdev, linux-arm-kernel,
	linux-mediatek, linux-doc
In-Reply-To: <20260624-t9xx_driver_v1-v3-2-73ff03f60c48@compal.com>

> +static int __init mtk_common_drv_init(void)
> +{
> +	return 0;
> +}
> +module_init(mtk_common_drv_init);
> +
> +static void __exit mtk_common_drv_exit(void)
> +{
> +}
> +module_exit(mtk_common_drv_exit);

Since these don't do anything, they should not be needed.

> @@ -467,6 +468,7 @@ static u32 mtk_pci_ext_h2d_evt_hw_bits(u32 chs)
>  
>  	SET_HW_BITS(hw_bits, chs, MHCCIF_RC2EP_EVT_DEVICE_RESET,
>  		    DEV_EVT_H2D_DEVICE_RESET);
> +
>  	return LE32_TO_U32(cpu_to_le32(hw_bits));

Please don't add white space like this. I assume a previous patch
added this code, so move this to that patch.

> @@ -908,13 +910,11 @@ static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  	struct mtk_md_dev *mdev;
>  	int ret;
>  
> -	mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL);
> +	mdev = mtk_dev_alloc(dev, &pci_hw_ops);
>  	if (!mdev) {
>  		ret = -ENOMEM;
>  		goto log_err;
>  	}
> -	mdev->dev_ops = &pci_hw_ops;
> -	mdev->dev = dev;
>  
>  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>  	if (!priv) {
> @@ -991,7 +991,7 @@ static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  free_priv_data:
>  	devm_kfree(dev, priv);
>  free_cntx_data:
> -	devm_kfree(dev, mdev);
> +	mtk_dev_free(mdev);

Why are you removing devm_ calls?

	Andrew


^ permalink raw reply

* [PATCH v3 1/3] KVM: arm64: skip pKVM cache flushes for non cacheable mappings
From: Bradley Morgan @ 2026-06-24 16:00 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Quentin Perret,
	Vincent Donnefort, Gavin Shan, Alexandru Elisei, linux-arm-kernel,
	kvmarm, linux-kernel, Bradley Morgan
In-Reply-To: <20260624160028.15591-1-include@grrlz.net>

pKVM keeps its own mapping list for stage 2 operations. Its flush path
uses that list directly, so it lost the PTE attribute check done by the
generic stage 2 walker.

Record whether a mapping is cacheable and skip cache maintenance for
mappings that are not cacheable.

Fixes: e912efed485a ("KVM: arm64: Introduce the EL1 pKVM MMU")
Signed-off-by: Bradley Morgan <include@grrlz.net>
---
 arch/arm64/kvm/pkvm.c | 51 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 41 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 428723b1b0f5..ca6e823028c2 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -302,9 +302,32 @@ static u64 __pkvm_mapping_start(struct pkvm_mapping *m)
 	return m->gfn * PAGE_SIZE;
 }
 
+#define PKVM_MAPPING_NR_PAGES_MASK	GENMASK_ULL(47, 0)
+#define PKVM_MAPPING_CACHEABLE		BIT_ULL(48)
+
+static u64 pkvm_mapping_nr_pages(struct pkvm_mapping *m)
+{
+	return m->nr_pages & PKVM_MAPPING_NR_PAGES_MASK;
+}
+
+static bool pkvm_mapping_is_cacheable(struct pkvm_mapping *m)
+{
+	return m->nr_pages & PKVM_MAPPING_CACHEABLE;
+}
+
+static void pkvm_mapping_set_nr_pages(struct pkvm_mapping *m, u64 nr_pages,
+				      bool cacheable)
+{
+	WARN_ON_ONCE(nr_pages & ~PKVM_MAPPING_NR_PAGES_MASK);
+
+	m->nr_pages = nr_pages & PKVM_MAPPING_NR_PAGES_MASK;
+	if (cacheable)
+		m->nr_pages |= PKVM_MAPPING_CACHEABLE;
+}
+
 static u64 __pkvm_mapping_end(struct pkvm_mapping *m)
 {
-	return (m->gfn + m->nr_pages) * PAGE_SIZE - 1;
+	return (m->gfn + pkvm_mapping_nr_pages(m)) * PAGE_SIZE - 1;
 }
 
 INTERVAL_TREE_DEFINE(struct pkvm_mapping, node, u64, __subtree_last,
@@ -350,7 +373,7 @@ static int __pkvm_pgtable_stage2_reclaim(struct kvm_pgtable *pgt, u64 start, u64
 			continue;
 
 		page = pfn_to_page(mapping->pfn);
-		WARN_ON_ONCE(mapping->nr_pages != 1);
+		WARN_ON_ONCE(pkvm_mapping_nr_pages(mapping) != 1);
 		unpin_user_pages_dirty_lock(&page, 1, true);
 		account_locked_vm(kvm->mm, 1, false);
 		pkvm_mapping_remove(mapping, &pgt->pkvm_mappings);
@@ -369,7 +392,7 @@ static int __pkvm_pgtable_stage2_unshare(struct kvm_pgtable *pgt, u64 start, u64
 
 	for_each_mapping_in_range_safe(pgt, start, end, mapping) {
 		ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_guest, handle, mapping->gfn,
-					mapping->nr_pages);
+					pkvm_mapping_nr_pages(mapping));
 		if (WARN_ON(ret))
 			return ret;
 		pkvm_mapping_remove(mapping, &pgt->pkvm_mappings);
@@ -448,7 +471,7 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
 		 * permission faults are handled in the relax_perms() path.
 		 */
 		if (mapping) {
-			if (size == (mapping->nr_pages * PAGE_SIZE))
+			if (size == (pkvm_mapping_nr_pages(mapping) * PAGE_SIZE))
 				return -EAGAIN;
 
 			/*
@@ -472,7 +495,9 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
 	swap(mapping, cache->mapping);
 	mapping->gfn = gfn;
 	mapping->pfn = pfn;
-	mapping->nr_pages = size / PAGE_SIZE;
+	pkvm_mapping_set_nr_pages(mapping, size / PAGE_SIZE,
+				  !(prot & (KVM_PGTABLE_PROT_DEVICE |
+					    KVM_PGTABLE_PROT_NORMAL_NC)));
 	pkvm_mapping_insert(mapping, &pgt->pkvm_mappings);
 
 	return ret;
@@ -503,7 +528,7 @@ int pkvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size)
 	lockdep_assert_held(&kvm->mmu_lock);
 	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
 		ret = kvm_call_hyp_nvhe(__pkvm_host_wrprotect_guest, handle, mapping->gfn,
-					mapping->nr_pages);
+					pkvm_mapping_nr_pages(mapping));
 		if (WARN_ON(ret))
 			break;
 	}
@@ -517,9 +542,13 @@ int pkvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size)
 	struct pkvm_mapping *mapping;
 
 	lockdep_assert_held(&kvm->mmu_lock);
-	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
+	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
+		if (!pkvm_mapping_is_cacheable(mapping))
+			continue;
+
 		__clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn),
-					  PAGE_SIZE * mapping->nr_pages);
+					  PAGE_SIZE * pkvm_mapping_nr_pages(mapping));
+	}
 
 	return 0;
 }
@@ -536,8 +565,10 @@ bool pkvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr, u64
 
 	lockdep_assert_held(&kvm->mmu_lock);
 	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
-		young |= kvm_call_hyp_nvhe(__pkvm_host_test_clear_young_guest, handle, mapping->gfn,
-					   mapping->nr_pages, mkold);
+		young |= kvm_call_hyp_nvhe(__pkvm_host_test_clear_young_guest,
+					   handle, mapping->gfn,
+					   pkvm_mapping_nr_pages(mapping),
+					   mkold);
 
 	return young;
 }
-- 
2.53.0



^ permalink raw reply related

* [PATCH v3 3/3] KVM: arm64: top up stage 2 memcache for dirty logging faults
From: Bradley Morgan @ 2026-06-24 16:00 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Quentin Perret,
	Vincent Donnefort, Gavin Shan, Alexandru Elisei, linux-arm-kernel,
	kvmarm, linux-kernel, Bradley Morgan, stable
In-Reply-To: <20260624160028.15591-1-include@grrlz.net>

Dirty logging forces new stage 2 mappings down to page size, but
it does not always remove an existing block mapping before the next
fault. Eager splitting is best effort and is disabled by default.

A permission fault on such a block can still need a page table page
to install the smaller mapping. Top up the memcache for any permission
fault while dirty logging is active, not only for write faults.

The issue was discovered [1] by Sashiko.

Link: https://lore.kernel.org/all/59984F6D-06F2-4302-BDD7-92DF334E8FA0@grrlz.net/T/#t [1]

Fixes: 6f745f1bb5bf ("KVM: arm64: Convert user_mem_abort() to generic page-table API")
Cc: stable@vger.kernel.org
Signed-off-by: Bradley Morgan <include@grrlz.net>
---
 arch/arm64/kvm/mmu.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 3f57f6825a33..8911e319e6fa 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2122,13 +2122,12 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 	 * Permission faults just need to update the existing leaf entry,
 	 * and so normally don't require allocations from the memcache. The
 	 * only exception to this is when dirty logging is enabled at runtime
-	 * and a write fault needs to collapse a block entry into a table. With
-	 * pKVM, they may still need a fresh mapping object if the fault turns
-	 * page entries into a block entry.
+	 * and a fault needs to collapse a block entry into a table. With pKVM,
+	 * they may still need a fresh mapping object if the fault turns page
+	 * entries into a block entry.
 	 */
 	memcache = get_mmu_memcache(s2fd->vcpu);
-	if (!perm_fault || (memslot_is_logging(s2fd->memslot) &&
-			    kvm_is_write_fault(s2fd->vcpu))) {
+	if (!perm_fault || memslot_is_logging(s2fd->memslot)) {
 		ret = topup_mmu_memcache(s2fd->vcpu, memcache);
 		if (ret)
 			return ret;
-- 
2.53.0



^ permalink raw reply related

* [PATCH v3 2/3] KVM: arm64: top up pKVM mapping cache for permission faults
From: Bradley Morgan @ 2026-06-24 16:00 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Quentin Perret,
	Vincent Donnefort, Gavin Shan, Alexandru Elisei, linux-arm-kernel,
	kvmarm, linux-kernel, Bradley Morgan, stable
In-Reply-To: <20260624160028.15591-1-include@grrlz.net>

Permission faults normally only relax an existing leaf, so the fault path
does not top up the memcache.

With pKVM, a permission fault can also replace page mappings with a
PMD mapping. That path needs a fresh pkvm_mapping object, and can
dereference a NULL cache->mapping if the cache was not topped up.

Allocate just that object for pKVM permission faults.

The issue was discovered [1] by Sashiko.

Link: https://lore.kernel.org/all/20260623161545.EA08E1F000E9@smtp.kernel.org/ [1]

Fixes: db14091d8f75 ("KVM: arm64: Stage-2 huge mappings for np-guests")
Cc: stable@vger.kernel.org
Signed-off-by: Bradley Morgan <include@grrlz.net>
---
 arch/arm64/kvm/mmu.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 6c941aaa10c6..3f57f6825a33 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1177,17 +1177,26 @@ void free_hyp_memcache(struct kvm_hyp_memcache *mc)
 	__free_hyp_memcache(mc, hyp_mc_free_fn, kvm_host_va, mc);
 }
 
+static int topup_hyp_memcache_mapping(struct kvm_hyp_memcache *mc)
+{
+	if (mc->mapping)
+		return 0;
+
+	mc->mapping = kzalloc_obj(struct pkvm_mapping,
+				  GFP_KERNEL_ACCOUNT);
+	return mc->mapping ? 0 : -ENOMEM;
+}
+
 int topup_hyp_memcache(struct kvm_hyp_memcache *mc, unsigned long min_pages)
 {
+	int ret;
+
 	if (!is_protected_kvm_enabled())
 		return 0;
 
-	if (!mc->mapping) {
-		mc->mapping = kzalloc_obj(struct pkvm_mapping,
-					  GFP_KERNEL_ACCOUNT);
-		if (!mc->mapping)
-			return -ENOMEM;
-	}
+	ret = topup_hyp_memcache_mapping(mc);
+	if (ret)
+		return ret;
 
 	return __topup_hyp_memcache(mc, min_pages, hyp_mc_alloc_fn,
 				    kvm_host_pa, mc);
@@ -2113,7 +2122,9 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 	 * Permission faults just need to update the existing leaf entry,
 	 * and so normally don't require allocations from the memcache. The
 	 * only exception to this is when dirty logging is enabled at runtime
-	 * and a write fault needs to collapse a block entry into a table.
+	 * and a write fault needs to collapse a block entry into a table. With
+	 * pKVM, they may still need a fresh mapping object if the fault turns
+	 * page entries into a block entry.
 	 */
 	memcache = get_mmu_memcache(s2fd->vcpu);
 	if (!perm_fault || (memslot_is_logging(s2fd->memslot) &&
@@ -2121,6 +2132,10 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 		ret = topup_mmu_memcache(s2fd->vcpu, memcache);
 		if (ret)
 			return ret;
+	} else if (is_protected_kvm_enabled()) {
+		ret = topup_hyp_memcache_mapping(memcache);
+		if (ret)
+			return ret;
 	}
 
 	/*
-- 
2.53.0



^ permalink raw reply related

* [PATCH v3 0/3] KVM: arm64: fix pKVM mapping cache corner cases
From: Bradley Morgan @ 2026-06-24 16:00 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Quentin Perret,
	Vincent Donnefort, Gavin Shan, Alexandru Elisei, linux-arm-kernel,
	kvmarm, linux-kernel, Bradley Morgan

This is a standalone v3.

Patch 1 fixes pKVM cache maintenance for non cacheable mappings without
growing struct pkvm_mapping.

Patch 2 fixes a pKVM mapping cache topup bug on permission faults that
replace page mappings with a PMD mapping.

Patch 3 fixes the generic dirty logging case where a permission fault
can still need a page table allocation to split a block mapping.

Changes in v3:
- Send as a standalone series with a cover letter.
- Store the pKVM cacheable bit in nr_pages instead of adding a bool.
- Drop stable from patch 1.
- Add patch 3 for dirty logging permission faults.

Changes in v2:
- Add patch 2 for the pKVM permission fault mapping cache bug.

Bradley Morgan (3):
  KVM: arm64: skip pKVM cache flushes for non cacheable mappings
  KVM: arm64: top up pKVM mapping cache for permission faults
  KVM: arm64: top up stage 2 memcache for dirty logging faults

 arch/arm64/kvm/mmu.c  | 32 +++++++++++++++++++--------
 arch/arm64/kvm/pkvm.c | 51 ++++++++++++++++++++++++++++++++++---------
 2 files changed, 64 insertions(+), 19 deletions(-)

-- 
2.53.0


^ permalink raw reply

* Re: [PATCH 30/37] drm/bridge: add drm_bridge_is_tail() to know whether a bridge completes the pipeline
From: Luca Ceresoli @ 2026-06-24 16:06 UTC (permalink / raw)
  To: Maxime Ripard, Luca Ceresoli
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
	Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
	Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
	linux-arm-kernel
In-Reply-To: <20260624-vagabond-neon-gorilla-cd6487@houat>

On Wed Jun 24, 2026 at 3:04 PM CEST, Maxime Ripard wrote:
> On Tue, Jun 09, 2026 at 10:23:24AM +0200, Luca Ceresoli wrote:
>> Hi Maxime,
>>
>> thanks for the review of this long series!
>>
>> On Mon Jun 8, 2026 at 2:34 PM CEST, Maxime Ripard wrote:
>> ...
>> >> --- a/include/drm/drm_bridge.h
>> >> +++ b/include/drm/drm_bridge.h
>> >> @@ -78,6 +78,19 @@ struct drm_bridge_funcs {
>> >>  	int (*attach)(struct drm_bridge *bridge, struct drm_encoder *encoder,
>> >>  		      enum drm_bridge_attach_flags flags);
>> >>
>> >> +	/**
>> >> +	 * @is_tail:
>> >> +	 *
>> >> +	 * Returns true if this is a tail bridge, i.e. it does not need a
>> >> +	 * next bridge to work. E.g. a panel_bridge is a tail bridge, a
>> >> +	 * DSI-to-LVDS bridge is not a tail bridge (no matter whether the
>> >> +	 * next bridge is present or not).
>> >
>> > Why a DSI-to-LDVS bridge isn't a tail bridge? It only needs a panel
>> > next, right?
>>
>> Uhm, good point, but I'd say it can be a tail bridge or not: in the
>> ATTACH_NO_CONNECTOR case it will need a bridge (a panel_bridge most
>> likely), no?
>
> Yeah, I think it cannot (always) be a blanket statement from the driver.
> For drivers that do not support ATTACH_NO_CONNECTOR, then it's always
> going to be a tail, but if the driver supports it, we should use a
> helper because it's going to depend on the DT, basically.
>
>> However this is possibly irrelevant as the whole .is_tail is meant to
>> disappear in v2, see below.
>>
>> >> +	 * The @is_tail callback is optional but it is required if the
>> >> +	 * bridge is part of a pipeline with hot-pluggable components.
>> >> +	 */
>> >> +	bool (*is_tail)(struct drm_bridge *bridge);
>> >> +
>> >
>> > I don't think that's the right way to think about it, if only because
>> > you never really know at the driver level if you're supposed to be last
>> > or not. A DSI-to-LVDS bridge might just as well be chained with an
>> > LVDS-to-eDP bridge, or feed the panel directly without any additional
>> > bridge.
>> >
>> > I *think* that what you're trying to find out here is whether the chain
>> > is complete or not.
>>
>> You nailed it.
>>
>> That was the main point discussed during the Display Next Hackfest 2026
>> (see the report [0]) and we all agreed the .is_tail along with the
>> -EPROBE_DEFER changes (patches 20+35) are not a good approach.
>>
>> This is actually the most crucial aspect of the whole work still not having
>> no well-defined solution.
>
> Ok
>
>> > I think you can get the same information by checking
>> > whether you have a connector for that bridge chain. If you don't, you
>> > know the chain isn't complete, and if you do, it's supposed to be.
>>
>> There's a checken-egg problem here. Knowing whether the pipeline is
>> complete or not is needed to know whether we have to create a
>> connector. See patch 36:
>>
>>   +  if (drm_bridge_connector_pipeline_is_complete(bridge_connector))
>>   +          drm_bridge_connector_add_connector(bridge_connector);
>>
>> So the question is still open, what I need the most right now is comments
>> on the overall architecture of this aspecs. Maybe replying to [0] can be
>> more effective than here.
>>
>> In a nutshell what we need is:
>>
>>  * when a bridge needs a following element (a bridge, or a panel which
>>    however might/should have a panel_bridge), but that element is not
>>    present, instead of deferring the whole card probe the card should be
>>    allowed to probe but without a connector
>>
>>  * when something is added to an incomplete pipeline we need to know
>>    whether the pipeline has become complete (in order to create the
>>    connector)
>>
>> How to implement this is still an open point. I'll welcome proposals in
>> addition to the ones in [0].
>
> Thanks for the notes, I think I largely agree with the discussion.

Good! :)

> Reading through it, I thought it would be nice for a new callback called
> get_next_bridge or something that would return either an error, NULL or a
> (refcounted) pointer to the next bridge in the chain. And have some kind
> of special error (ENODEV?) when the bridge should be there but isn't
> (and thus the chain isn't complete).

I initially preferred exposing the fwnode of the next bridge as discussed
with Dmitry during the Display Next Hackfest, which looks simpler for
driver writers. But then I realized it would be tricyk in cases such as
dual-LVDS output bridges (ti-sn65dsi83.c) which have two output ports in
DT, none of which is mandatory. So I've come to thinking a callback is
better as it should be flexible enough.

Picking the best errno is probably the only aspect needing special
attention now.

Luca

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply


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