Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH net v5] net: airoha: fix MIB stats collection to be lossless
From: patchwork-bot+netdevbpf @ 2026-07-21  7:50 UTC (permalink / raw)
  To: Aniket Negi
  Cc: netdev, lorenzo, matthias.bgg, angelogioacchino.delregno,
	aniket.negi, andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	ansuelsmth, linux-arm-kernel, linux-mediatek, linux-kernel
In-Reply-To: <20260707152639.105628-1-aniket.negi03@gmail.com>

Hello:

This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Tue,  7 Jul 2026 20:56:39 +0530 you wrote:
> REG_FE_GDM_MIB_CLEAR after every read creates a race window where
> packets arriving between read and clear are lost from statistics.
> 
> Switch to a delta-based approach instead:
> 
> - 64-bit H+L registers (ok pkts/bytes, E64..L1023): read absolute
>   hardware total directly into a local variable; clamp with max(new, old)
>   to prevent torn-read regression when the counter carries between the
>   two reads.
> 
> [...]

Here is the summary with links:
  - [net,v5] net: airoha: fix MIB stats collection to be lossless
    https://git.kernel.org/netdev/net/c/d163725af84a

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html




^ permalink raw reply

* Re: [PATCH] KVM: arm64: Optimize protected mode and FWB
From: Fuad Tabba @ 2026-07-21  7:49 UTC (permalink / raw)
  To: Mostafa Saleh
  Cc: linux-kernel, kvmarm, linux-arm-kernel, maz, oupton, seiden,
	joey.gouly, suzuki.poulose, yuzenghui, catalin.marinas, will,
	vdonnefort, sebastianene, keirf
In-Reply-To: <20260720203529.1276355-1-smostafa@google.com>

On Mon, 20 Jul 2026 at 21:35, Mostafa Saleh <smostafa@google.com> wrote:
>
> KVM opportunistically enables FWB if supported by the system for guest
> VMs, which allows it to elude cache maintenance for data as they are
> forced to be cacheable from stage-2.
> In that case, __clean_dcache_guest_page() will immediately return.
> However in protected mode, before calling __clean_dcache_guest_page()
> it loops over the range and fix_map/unmap it, issuing TLB
> invalidations, dsb() and isb() unnecessarily.
>
> This can be optimized by returning early if FWB is supported,
> kvm_pgtable_stage2_map() already issues dsb() and tlb invalidation
> functions issue dsb() for the unmap path.
>
> Signed-off-by: Mostafa Saleh <smostafa@google.com>
> ---
>  arch/arm64/kvm/hyp/nvhe/mem_protect.c | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
> index 4e329e39a695..6e9229106a25 100644
> --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
> +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
> @@ -261,6 +261,10 @@ static void __apply_guest_page(void *va, size_t size,
>
>  static void clean_dcache_guest_page(void *va, size_t size)
>  {
> +       /* See __clean_dcache_guest_page() */

This looks good to me. One comment: this file also has
hyp_poison_page(), which deliberately avoids
__clean_dcache_guest_page() because the FWB elision would be wrong
there (it even notes "Prefer kvm_flush_dcache_to_poc() over
__clean_dcache_guest_page()"). Given the two opposite treatments in
the same file, could this comment say why eliding is safe here, e.g.
that the consumer is the guest via the FWB-forced stage-2? It would
save the next reader (if they're anything like me) from reconciling
the two.

That fuller comment could also replace "See
__clean_dcache_guest_page()", which mostly just points back at the
function passed in the __apply_guest_page() call below.

With that fixed:

Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>

Cheers,
/fuad

> +       if (cpus_have_final_cap(ARM64_HAS_STAGE2_FWB))
> +               return;
> +
>         __apply_guest_page(va, size, __clean_dcache_guest_page);
>  }
>
> --
> 2.55.0.229.g6434b31f56-goog
>


^ permalink raw reply

* Re: [PATCH v5 7/7] ARM: configs: sama5: enable Microchip/SST SFDP EUI NVMEM layout
From: Linus Walleij @ 2026-07-21  7:49 UTC (permalink / raw)
  To: Manikandan Muralidharan
  Cc: pratyush, mwalle, takahiro.kuwano, miquel.raynal, richard,
	vigneshr, robh, krzk+dt, conor+dt, srini, nicolas.ferre,
	alexandre.belloni, claudiu.beznea, linux, richardcochran, arnd,
	michael, linux-mtd, devicetree, linux-kernel, linux-arm-kernel,
	netdev
In-Reply-To: <20260721052859.171341-8-manikandan.m@microchip.com>

On Tue, Jul 21, 2026 at 7:31 AM Manikandan Muralidharan
<manikandan.m@microchip.com> wrote:

> Enable CONFIG_NVMEM_LAYOUT_SST26VF_SFDP_EUI so the factory EUI-48 stored
> in the SST26VF QSPI flash SFDP can be used as a MAC address on boards
> such as the sama5d27_wlsom1.
>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>

Reviewed-by: Linus Walleij <linusw@kernel.org>

Yours,
Linus Walleij


^ permalink raw reply

* Re: [PATCH v5 6/7] ARM: dts: microchip: sama5d27_wlsom1: read MAC address from QSPI SFDP
From: Linus Walleij @ 2026-07-21  7:49 UTC (permalink / raw)
  To: Manikandan Muralidharan
  Cc: pratyush, mwalle, takahiro.kuwano, miquel.raynal, richard,
	vigneshr, robh, krzk+dt, conor+dt, srini, nicolas.ferre,
	alexandre.belloni, claudiu.beznea, linux, richardcochran, arnd,
	michael, linux-mtd, devicetree, linux-kernel, linux-arm-kernel,
	netdev
In-Reply-To: <20260721052859.171341-7-manikandan.m@microchip.com>

On Tue, Jul 21, 2026 at 7:30 AM Manikandan Muralidharan
<manikandan.m@microchip.com> wrote:

> Describe the QSPI flash SFDP as an NVMEM provider with the
> microchip,sst26vf-sfdp-eui layout, which exposes the factory-programmed
> EUI-48 as a "mac-address" cell, and point macb0 at it through
> nvmem-cells. This yields a stable MAC address on boards where U-Boot does
> not program one, instead of falling back to a random address.
>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>

Reviewed-by: Linus Walleij <linusw@kernel.org>

Yours,
Linus Walleij


^ permalink raw reply

* Re: [PATCH 1/2] arm64: dts: imx8qm-ss-conn: add usbotg3 iommu information
From: Alexander Stein @ 2026-07-21  7:48 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, linux-arm-kernel
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Xu Yang,
	Frank Li, Frank.Li
In-Reply-To: <20260720-qm_smmu-v1-1-3689fbd8b516@nxp.com>

Hi Frank,

Am Montag, 20. Juli 2026, 23:10:33 CEST schrieb Frank.Li@oss.nxp.com:
> From: Frank Li <Frank.Li@nxp.com>
> 
> Add usbotg3 iommu information.
> 
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
> ---
>  arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi b/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
> index ccf9f510e0f88..1370a571153a5 100644
> --- a/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
> @@ -32,3 +32,7 @@ &usdhc3 {
>  	compatible = "fsl,imx8qm-usdhc", "fsl,imx8qxp-usdhc", "fsl,imx7d-usdhc";
>  	iommus = <&smmu 0x11 0x7f80>;
>  };
> +
> +&usbotg3_cdns3 {
> +	iommus = <&smmu 0x4 0x7f80>;

Where in the RM can you get these values?

Thanks and best regards,
Alexander

> +};
> 
> 


-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
http://www.tq-group.com/




^ permalink raw reply

* Re: [PATCH v5 2/7] dt-bindings: nvmem: layouts: add Microchip/SST SFDP EUI layout
From: Linus Walleij @ 2026-07-21  7:45 UTC (permalink / raw)
  To: Manikandan Muralidharan
  Cc: pratyush, mwalle, takahiro.kuwano, miquel.raynal, richard,
	vigneshr, robh, krzk+dt, conor+dt, srini, nicolas.ferre,
	alexandre.belloni, claudiu.beznea, linux, richardcochran, arnd,
	michael, linux-mtd, devicetree, linux-kernel, linux-arm-kernel,
	netdev
In-Reply-To: <20260721052859.171341-3-manikandan.m@microchip.com>

On Tue, Jul 21, 2026 at 7:29 AM Manikandan Muralidharan
<manikandan.m@microchip.com> wrote:

> Add a binding for the NVMEM layout that exposes the factory-programmed
> EUI-48 identifier from the Microchip/SST vendor-specific SFDP parameter
> table (e.g. SST26VF064BEUI) as a "mac-address" NVMEM cell, and reference
> it from nvmem-layout.yaml.
>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>

I like it!
Reviewed-by: Linus Walleij <linusw@kernel.org>

Yours,
Linus Walleij


^ permalink raw reply

* Re: [PATCH 2/2] ASoC: meson: meson-card: use priv instead of card on each functions
From: Jerome Brunet @ 2026-07-21  7:35 UTC (permalink / raw)
  To: Kuninori Morimoto
  Cc: Jaroslav Kysela, Kevin Hilman, Liam Girdwood, Mark Brown,
	Martin Blumenstingl, Neil Armstrong, Takashi Iwai,
	linux-arm-kernel, linux-sound
In-Reply-To: <87se5di0n8.wl-kuninori.morimoto.gx@renesas.com>

On mar. 21 juil. 2026 at 01:36, Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> wrote:

> We can get card via priv->card, use priv for function param.
> No functional change, but is preparation for Card capsuling.

The change is simple but it will make more complex to backport fix an
older kernel once this fix is applied. So again, getting more context to
understand what you are trying to accomplish and why it needs a standard
card variable name would help.

>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> ---
>  sound/soc/meson/axg-card.c         | 63 ++++++++++++++++--------------
>  sound/soc/meson/gx-card.c          | 23 +++++------
>  sound/soc/meson/meson-card-utils.c | 58 ++++++++++++++-------------
>  sound/soc/meson/meson-card.h       | 11 +++---
>  4 files changed, 82 insertions(+), 73 deletions(-)
>
> diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
> index b4dca80e15e40..3d345dc33cb6f 100644
> --- a/sound/soc/meson/axg-card.c
> +++ b/sound/soc/meson/axg-card.c
> @@ -100,28 +100,28 @@ static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
>  	return 0;
>  }
>  
> -static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
> +static int axg_card_add_tdm_loopback(struct meson_card *priv,
>  				     int *index)
>  {
> -	struct meson_card *priv = snd_soc_card_get_drvdata(card);
>  	struct snd_soc_dai_link *pad;
>  	struct snd_soc_dai_link *lb;
>  	struct snd_soc_dai_link_component *dlc;
> +	struct device *dev = priv->card.dev;
>  	int ret;
>  
>  	/* extend links */
> -	ret = meson_card_reallocate_links(card, card->num_links + 1);
> +	ret = meson_card_reallocate_links(priv, priv->card.num_links + 1);
>  	if (ret)
>  		return ret;
>  
> -	pad = &card->dai_link[*index];
> -	lb = &card->dai_link[*index + 1];
> +	pad = &priv->card.dai_link[*index];
> +	lb = &priv->card.dai_link[*index + 1];
>  
> -	lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name);
> +	lb->name = devm_kasprintf(dev, GFP_KERNEL, "%s-lb", pad->name);
>  	if (!lb->name)
>  		return -ENOMEM;
>  
> -	dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL);
> +	dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL);
>  	if (!dlc)
>  		return -ENOMEM;
>  
> @@ -153,18 +153,19 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
>  	return 0;
>  }
>  
> -static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
> +static int axg_card_parse_cpu_tdm_slots(struct meson_card *priv,
>  					struct snd_soc_dai_link *link,
>  					struct device_node *node,
>  					struct axg_dai_link_tdm_data *be)
>  {
> +	struct device *dev = priv->card.dev;
>  	char propname[32];
>  	u32 tx, rx;
>  	int i;
>  
> -	be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
> +	be->tx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES,
>  				   sizeof(*be->tx_mask), GFP_KERNEL);
> -	be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
> +	be->rx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES,
>  				   sizeof(*be->rx_mask), GFP_KERNEL);
>  	if (!be->tx_mask || !be->rx_mask)
>  		return -ENOMEM;
> @@ -191,7 +192,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
>  
>  	/* ... but the interface should at least have one direction */
>  	if (!tx && !rx) {
> -		dev_err(card->dev, "tdm link has no cpu slots\n");
> +		dev_err(dev, "tdm link has no cpu slots\n");
>  		return -EINVAL;
>  	}
>  
> @@ -207,7 +208,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
>  		 * Error if the slots can't accommodate the largest mask or
>  		 * if it is just too big
>  		 */
> -		dev_err(card->dev, "bad slot number\n");
> +		dev_err(dev, "bad slot number\n");
>  		return -EINVAL;
>  	}
>  
> @@ -216,14 +217,15 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
>  	return 0;
>  }
>  
> -static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
> +static int axg_card_parse_codecs_masks(struct meson_card *priv,
>  				       struct snd_soc_dai_link *link,
>  				       struct device_node *node,
>  				       struct axg_dai_link_tdm_data *be)
>  {
>  	struct axg_dai_link_tdm_mask *codec_mask;
> +	struct device *dev = priv->card.dev;
>  
> -	codec_mask = devm_kcalloc(card->dev, link->num_codecs,
> +	codec_mask = devm_kcalloc(dev, link->num_codecs,
>  				  sizeof(*codec_mask), GFP_KERNEL);
>  	if (!codec_mask)
>  		return -ENOMEM;
> @@ -242,17 +244,17 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
>  	return 0;
>  }
>  
> -static int axg_card_parse_tdm(struct snd_soc_card *card,
> +static int axg_card_parse_tdm(struct meson_card *priv,
>  			      struct device_node *node,
>  			      int *index)
>  {
> -	struct meson_card *priv = snd_soc_card_get_drvdata(card);
> -	struct snd_soc_dai_link *link = &card->dai_link[*index];
> +	struct snd_soc_dai_link *link = &priv->card.dai_link[*index];
>  	struct axg_dai_link_tdm_data *be;
> +	struct device *dev = priv->card.dev;
>  	int ret;
>  
>  	/* Allocate tdm link parameters */
> -	be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
> +	be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL);
>  	if (!be)
>  		return -ENOMEM;
>  	priv->link_data[*index] = be;
> @@ -264,19 +266,19 @@ static int axg_card_parse_tdm(struct snd_soc_card *card,
>  
>  	of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
>  
> -	ret = axg_card_parse_cpu_tdm_slots(card, link, node, be);
> +	ret = axg_card_parse_cpu_tdm_slots(priv, link, node, be);
>  	if (ret) {
> -		dev_err(card->dev, "error parsing tdm link slots\n");
> +		dev_err(dev, "error parsing tdm link slots\n");
>  		return ret;
>  	}
>  
> -	ret = axg_card_parse_codecs_masks(card, link, node, be);
> +	ret = axg_card_parse_codecs_masks(priv, link, node, be);
>  	if (ret)
>  		return ret;
>  
>  	/* Add loopback if the pad dai has playback */
>  	if (!link->capture_only) {
> -		ret = axg_card_add_tdm_loopback(card, index);
> +		ret = axg_card_add_tdm_loopback(priv, index);
>  		if (ret)
>  			return ret;
>  	}
> @@ -305,14 +307,15 @@ static int axg_card_cpu_is_codec(struct device_node *np)
>  		of_device_is_compatible(np, DT_PREFIX "g12a-toacodec");
>  }
>  
> -static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
> +static int axg_card_add_link(struct meson_card *priv, struct device_node *np,
>  			     int *index)
>  {
> -	struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
> +	struct snd_soc_dai_link *dai_link = &priv->card.dai_link[*index];
>  	struct snd_soc_dai_link_component *cpu;
> +	struct device *dev = priv->card.dev;
>  	int ret;
>  
> -	cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
> +	cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL);
>  	if (!cpu)
>  		return -ENOMEM;
>  
> @@ -320,17 +323,17 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
>  	dai_link->num_cpus = 1;
>  	dai_link->nonatomic = true;
>  
> -	ret = meson_card_parse_dai(card, np, dai_link->cpus);
> +	ret = meson_card_parse_dai(priv, np, dai_link->cpus);
>  	if (ret)
>  		return ret;
>  
>  	if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node))
> -		return meson_card_set_fe_link(card, dai_link, np, true);
> +		return meson_card_set_fe_link(priv, dai_link, np, true);
>  	else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node))
> -		return meson_card_set_fe_link(card, dai_link, np, false);
> +		return meson_card_set_fe_link(priv, dai_link, np, false);
>  
>  
> -	ret = meson_card_set_be_link(card, dai_link, np);
> +	ret = meson_card_set_be_link(priv, dai_link, np);
>  	if (ret)
>  		return ret;
>  
> @@ -340,7 +343,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
>  	} else {
>  		dai_link->no_pcm = 1;
>  		if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
> -			ret = axg_card_parse_tdm(card, np, index);
> +			ret = axg_card_parse_tdm(priv, np, index);
>  	}
>  
>  	return ret;
> diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c
> index b408cc2bbc919..c0cbf31f28db5 100644
> --- a/sound/soc/meson/gx-card.c
> +++ b/sound/soc/meson/gx-card.c
> @@ -41,16 +41,16 @@ static const struct snd_soc_ops gx_card_i2s_be_ops = {
>  	.hw_params = gx_card_i2s_be_hw_params,
>  };
>  
> -static int gx_card_parse_i2s(struct snd_soc_card *card,
> +static int gx_card_parse_i2s(struct meson_card *priv,
>  			     struct device_node *node,
>  			     int *index)
>  {
> -	struct meson_card *priv = snd_soc_card_get_drvdata(card);
> -	struct snd_soc_dai_link *link = &card->dai_link[*index];
> +	struct snd_soc_dai_link *link = &priv->card.dai_link[*index];
>  	struct gx_dai_link_i2s_data *be;
> +	struct device *dev = priv->card.dev;
>  
>  	/* Allocate i2s link parameters */
> -	be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
> +	be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL);
>  	if (!be)
>  		return -ENOMEM;
>  	priv->link_data[*index] = be;
> @@ -76,28 +76,29 @@ static int gx_card_cpu_identify(struct snd_soc_dai_link_component *c,
>  	return 0;
>  }
>  
> -static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
> +static int gx_card_add_link(struct meson_card *priv, struct device_node *np,
>  			    int *index)
>  {
> -	struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
> +	struct snd_soc_dai_link *dai_link = &priv->card.dai_link[*index];
>  	struct snd_soc_dai_link_component *cpu;
> +	struct device *dev = priv->card.dev;
>  	int ret;
>  
> -	cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
> +	cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL);
>  	if (!cpu)
>  		return -ENOMEM;
>  
>  	dai_link->cpus = cpu;
>  	dai_link->num_cpus = 1;
>  
> -	ret = meson_card_parse_dai(card, np, dai_link->cpus);
> +	ret = meson_card_parse_dai(priv, np, dai_link->cpus);
>  	if (ret)
>  		return ret;
>  
>  	if (gx_card_cpu_identify(dai_link->cpus, "FIFO"))
> -		return  meson_card_set_fe_link(card, dai_link, np, true);
> +		return  meson_card_set_fe_link(priv, dai_link, np, true);
>  
> -	ret = meson_card_set_be_link(card, dai_link, np);
> +	ret = meson_card_set_be_link(priv, dai_link, np);
>  	if (ret)
>  		return ret;
>  
> @@ -109,7 +110,7 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
>  		dai_link->no_pcm = 1;
>  		/* Check if the cpu is the i2s encoder and parse i2s data */
>  		if (gx_card_cpu_identify(dai_link->cpus, "I2S Encoder"))
> -			ret = gx_card_parse_i2s(card, np, index);
> +			ret = gx_card_parse_i2s(priv, np, index);
>  	}
>  
>  	return ret;
> diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
> index d144ab79b645b..7329711aefa56 100644
> --- a/sound/soc/meson/meson-card-utils.c
> +++ b/sound/soc/meson/meson-card-utils.c
> @@ -39,10 +39,9 @@ int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
>  }
>  EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk);
>  
> -int meson_card_reallocate_links(struct snd_soc_card *card,
> +int meson_card_reallocate_links(struct meson_card *priv,
>  				unsigned int num_links)
>  {
> -	struct meson_card *priv = snd_soc_card_get_drvdata(card);
>  	struct snd_soc_dai_link *links;
>  	void **ldata;
>  
> @@ -67,10 +66,11 @@ int meson_card_reallocate_links(struct snd_soc_card *card,
>  }
>  EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
>  
> -int meson_card_parse_dai(struct snd_soc_card *card,
> +int meson_card_parse_dai(struct meson_card *priv,
>  			 struct device_node *node,
>  			 struct snd_soc_dai_link_component *dlc)
>  {
> +	struct device *dev = priv->card.dev;
>  	int ret;
>  
>  	if (!dlc || !node)
> @@ -78,18 +78,19 @@ int meson_card_parse_dai(struct snd_soc_card *card,
>  
>  	ret = snd_soc_of_get_dlc(node, NULL, dlc, 0);
>  	if (ret)
> -		return dev_err_probe(card->dev, ret, "can't parse dai\n");
> +		return dev_err_probe(dev, ret, "can't parse dai\n");
>  
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(meson_card_parse_dai);
>  
> -static int meson_card_set_link_name(struct snd_soc_card *card,
> +static int meson_card_set_link_name(struct meson_card *priv,
>  				    struct snd_soc_dai_link *link,
>  				    struct device_node *node,
>  				    const char *prefix)
>  {
> -	char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
> +	struct device *dev = priv->card.dev;
> +	char *name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s",
>  				    prefix, node->full_name);
>  	if (!name)
>  		return -ENOMEM;
> @@ -127,21 +128,22 @@ unsigned int meson_card_parse_daifmt(struct device_node *node,
>  }
>  EXPORT_SYMBOL_GPL(meson_card_parse_daifmt);
>  
> -int meson_card_set_be_link(struct snd_soc_card *card,
> +int meson_card_set_be_link(struct meson_card *priv,
>  			   struct snd_soc_dai_link *link,
>  			   struct device_node *node)
>  {
>  	struct snd_soc_dai_link_component *codec;
> +	struct device *dev = priv->card.dev;
>  	int ret, num_codecs;
>  
>  	num_codecs = of_get_child_count(node);
>  	if (!num_codecs) {
> -		dev_err(card->dev, "be link %s has no codec\n",
> +		dev_err(dev, "be link %s has no codec\n",
>  			node->full_name);
>  		return -EINVAL;
>  	}
>  
> -	codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL);
> +	codec = devm_kcalloc(dev, num_codecs, sizeof(*codec), GFP_KERNEL);
>  	if (!codec)
>  		return -ENOMEM;
>  
> @@ -149,22 +151,22 @@ int meson_card_set_be_link(struct snd_soc_card *card,
>  	link->num_codecs = num_codecs;
>  
>  	for_each_child_of_node_scoped(node, np) {
> -		ret = meson_card_parse_dai(card, np, codec);
> +		ret = meson_card_parse_dai(priv, np, codec);
>  		if (ret)
>  			return ret;
>  
>  		codec++;
>  	}
>  
> -	ret = meson_card_set_link_name(card, link, node, "be");
> +	ret = meson_card_set_link_name(priv, link, node, "be");
>  	if (ret)
> -		dev_err(card->dev, "error setting %pOFn link name\n", node);
> +		dev_err(dev, "error setting %pOFn link name\n", node);
>  
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(meson_card_set_be_link);
>  
> -int meson_card_set_fe_link(struct snd_soc_card *card,
> +int meson_card_set_fe_link(struct meson_card *priv,
>  			   struct snd_soc_dai_link *link,
>  			   struct device_node *node,
>  			   bool is_playback)
> @@ -182,29 +184,29 @@ int meson_card_set_fe_link(struct snd_soc_card *card,
>  	else
>  		link->capture_only = 1;
>  
> -	return meson_card_set_link_name(card, link, node, "fe");
> +	return meson_card_set_link_name(priv, link, node, "fe");
>  }
>  EXPORT_SYMBOL_GPL(meson_card_set_fe_link);
>  
> -static int meson_card_add_links(struct snd_soc_card *card)
> +static int meson_card_add_links(struct meson_card *priv)
>  {
> -	struct meson_card *priv = snd_soc_card_get_drvdata(card);
> -	struct device_node *node = card->dev->of_node;
> +	struct device *dev = priv->card.dev;
> +	struct device_node *node = dev->of_node;
>  	int num, i, ret;
>  
>  	num = of_get_child_count(node);
>  	if (!num) {
> -		dev_err(card->dev, "card has no links\n");
> +		dev_err(dev, "card has no links\n");
>  		return -EINVAL;
>  	}
>  
> -	ret = meson_card_reallocate_links(card, num);
> +	ret = meson_card_reallocate_links(priv, num);
>  	if (ret)
>  		return ret;
>  
>  	i = 0;
>  	for_each_child_of_node_scoped(node, np) {
> -		ret = priv->match_data->add_link(card, np, &i);
> +		ret = priv->match_data->add_link(priv, np, &i);
>  		if (ret)
>  			return ret;
>  
> @@ -214,17 +216,19 @@ static int meson_card_add_links(struct snd_soc_card *card)
>  	return 0;
>  }
>  
> -static int meson_card_parse_of_optional(struct snd_soc_card *card,
> +static int meson_card_parse_of_optional(struct meson_card *priv,
>  					const char *propname,
>  					int (*func)(struct snd_soc_card *c,
>  						    const char *p))
>  {
> +	struct device *dev = priv->card.dev;
> +
>  	/* If property is not provided, don't fail ... */
> -	if (!of_property_present(card->dev->of_node, propname))
> +	if (!of_property_present(dev->of_node, propname))
>  		return 0;
>  
>  	/* ... but do fail if it is provided and the parsing fails */
> -	return func(card, propname);
> +	return func(&priv->card, propname);
>  }
>  
>  static void meson_card_clean_references(struct meson_card *priv)
> @@ -282,25 +286,25 @@ int meson_card_probe(struct platform_device *pdev)
>  	if (ret < 0)
>  		return ret;
>  
> -	ret = meson_card_parse_of_optional(&priv->card, "audio-routing",
> +	ret = meson_card_parse_of_optional(priv, "audio-routing",
>  					   snd_soc_of_parse_audio_routing);
>  	if (ret) {
>  		dev_err(dev, "error while parsing routing\n");
>  		return ret;
>  	}
>  
> -	ret = meson_card_parse_of_optional(&priv->card, "audio-widgets",
> +	ret = meson_card_parse_of_optional(priv, "audio-widgets",
>  					   snd_soc_of_parse_audio_simple_widgets);
>  	if (ret) {
>  		dev_err(dev, "error while parsing widgets\n");
>  		return ret;
>  	}
>  
> -	ret = meson_card_add_links(&priv->card);
> +	ret = meson_card_add_links(priv);
>  	if (ret)
>  		goto out_err;
>  
> -	ret = meson_card_parse_of_optional(&priv->card, "audio-aux-devs",
> +	ret = meson_card_parse_of_optional(priv, "audio-aux-devs",
>  					   snd_soc_of_parse_aux_devs);
>  	if (ret)
>  		goto out_err;
> diff --git a/sound/soc/meson/meson-card.h b/sound/soc/meson/meson-card.h
> index a0d693e4f4608..ce8d9042793bb 100644
> --- a/sound/soc/meson/meson-card.h
> +++ b/sound/soc/meson/meson-card.h
> @@ -16,8 +16,9 @@ struct snd_pcm_hw_params;
>  
>  #define DT_PREFIX "amlogic,"
>  
> +struct meson_card;
>  struct meson_card_match_data {
> -	int (*add_link)(struct snd_soc_card *card,
> +	int (*add_link)(struct meson_card *priv,
>  			struct device_node *node,
>  			int *index);
>  };
> @@ -35,15 +36,15 @@ int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
>  			      struct snd_pcm_hw_params *params,
>  			      unsigned int mclk_fs);
>  
> -int meson_card_reallocate_links(struct snd_soc_card *card,
> +int meson_card_reallocate_links(struct meson_card *priv,
>  				unsigned int num_links);
> -int meson_card_parse_dai(struct snd_soc_card *card,
> +int meson_card_parse_dai(struct meson_card *priv,
>  			 struct device_node *node,
>  			 struct snd_soc_dai_link_component *dlc);
> -int meson_card_set_be_link(struct snd_soc_card *card,
> +int meson_card_set_be_link(struct meson_card *priv,
>  			   struct snd_soc_dai_link *link,
>  			   struct device_node *node);
> -int meson_card_set_fe_link(struct snd_soc_card *card,
> +int meson_card_set_fe_link(struct meson_card *priv,
>  			   struct snd_soc_dai_link *link,
>  			   struct device_node *node,
>  			   bool is_playback);

-- 
Jerome


^ permalink raw reply

* Re: [PATCH RFC 1/2] ASoC: meson: meson-card-utils: use meson_card_parse_of_optional()
From: Jerome Brunet @ 2026-07-21  7:32 UTC (permalink / raw)
  To: Kuninori Morimoto
  Cc: Jaroslav Kysela, Kevin Hilman, Liam Girdwood, Mark Brown,
	Martin Blumenstingl, Neil Armstrong, Takashi Iwai,
	linux-arm-kernel, linux-sound
In-Reply-To: <87tspti0ng.wl-kuninori.morimoto.gx@renesas.com>

On mar. 21 juil. 2026 at 01:36, Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> wrote:

> We already have meson_card_parse_of_optional(). Let's use it for
> snd_soc_of_parse_aux_devs() too.
>
> It is using snd_soc_of_parse_card_name(), but let's keep as-is
> for it for now. Beuase it will be not able to share code when
> Card capsuling.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> ---
>  sound/soc/meson/meson-card-utils.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
> index 8617a4661a339..d144ab79b645b 100644
> --- a/sound/soc/meson/meson-card-utils.c
> +++ b/sound/soc/meson/meson-card-utils.c
> @@ -300,7 +300,8 @@ int meson_card_probe(struct platform_device *pdev)
>  	if (ret)
>  		goto out_err;
>  
> -	ret = snd_soc_of_parse_aux_devs(&priv->card, "audio-aux-devs");
> +	ret = meson_card_parse_of_optional(&priv->card, "audio-aux-devs",
> +					   snd_soc_of_parse_aux_devs);

snd_soc_of_parse_aux_devs already properly handle if the property is not
present. I'm not a fan of adding another level of function if it does
not bring anything valuable to the mix.

Could you add more context describing what you trying to accomplish with
this "card capsuling" thing ? A link to the RFC maybe ?


>  	if (ret)
>  		goto out_err;

-- 
Jerome


^ permalink raw reply

* Re: [PATCH] mailbox: Remove redundant dev_err()/dev_err_probe()
From: Mikko Perttunen @ 2026-07-21  7:28 UTC (permalink / raw)
  To: Jassi Brar, Viresh Kumar, Tushar Khandelwal, Sudeep Holla,
	Cristian Marussi, Marek Behún, Florian Fainelli,
	Broadcom internal kernel review list, Ray Jui, Scott Branden,
	Conor Dooley, Daire McNamara, Sibi Sankar, Manivannan Sadhasivam,
	Orson Zhai, Baolin Wang, Chunyan Zhang, Maxime Coquelin,
	Alexandre Torgue, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thierry Reding, Jonathan Hunter, open list:MAILBOX API,
	moderated list:MAILBOX ARM MHUv3,
	moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE,
	open list:RISC-V MICROCHIP SUPPORT,
	open list:QUALCOMM CPUCP MAILBOX DRIVER,
	moderated list:ARM/STM32 ARCHITECTURE,
	open list:ARM/Allwinner sunXi SoC support,
	open list:TEGRA ARCHITECTURE SUPPORT, Pan Chuang
  Cc: Pan Chuang
In-Reply-To: <20260716025215.33592-1-panchuang@vivo.com>

On Thursday, July 16, 2026 11:52 AM Pan Chuang wrote:
> Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in
> devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq()
> automatically log detailed error messages on failure. Remove the
> now-redundant driver-specific dev_err() and dev_err_probe() calls.
> 
> Signed-off-by: Pan Chuang <panchuang@vivo.com>
> ---
>  drivers/mailbox/arm_mhu_db.c               |  1 -
>  drivers/mailbox/arm_mhuv2.c                |  9 ++-------
>  drivers/mailbox/arm_mhuv3.c                |  5 ++---
>  drivers/mailbox/armada-37xx-rwtm-mailbox.c |  4 +---
>  drivers/mailbox/bcm-pdc-mailbox.c          |  5 +----
>  drivers/mailbox/bcm2835-mailbox.c          |  5 +----
>  drivers/mailbox/bcm74110-mailbox.c         |  2 +-
>  drivers/mailbox/cv1800-mailbox.c           |  2 +-
>  drivers/mailbox/hi6220-mailbox.c           |  5 +----
>  drivers/mailbox/mailbox-mpfs.c             |  2 --
>  drivers/mailbox/mailbox-sti.c              |  4 +---
>  drivers/mailbox/qcom-cpucp-mbox.c          |  2 +-
>  drivers/mailbox/qcom-ipcc.c                |  4 +---
>  drivers/mailbox/sprd-mailbox.c             | 12 +++---------
>  drivers/mailbox/stm32-ipcc.c               |  4 +---
>  drivers/mailbox/sun6i-msgbox.c             |  4 +---
>  drivers/mailbox/tegra-hsp.c                | 11 ++---------
>  17 files changed, 20 insertions(+), 61 deletions(-)
> 
> diff --git a/drivers/mailbox/arm_mhu_db.c b/drivers/mailbox/arm_mhu_db.c
> index 9e937b09c5fb..a39239e38a47 100644
> --- a/drivers/mailbox/arm_mhu_db.c
> +++ b/drivers/mailbox/arm_mhu_db.c
> @@ -318,7 +318,6 @@ static int mhu_db_probe(struct amba_device *adev, const struct amba_id *id)
>  						mhu_db_mbox_rx_handler,
>  						IRQF_ONESHOT, "mhu_db_link", mhu);
>  		if (err) {
> -			dev_err(dev, "Can't claim IRQ %d\n", irq);
>  			mbox_controller_unregister(&mhu->mbox);
>  			return err;
>  		}
> diff --git a/drivers/mailbox/arm_mhuv2.c b/drivers/mailbox/arm_mhuv2.c
> index f035284944c0..213fd4a39993 100644
> --- a/drivers/mailbox/arm_mhuv2.c
> +++ b/drivers/mailbox/arm_mhuv2.c
> @@ -986,10 +986,7 @@ static int mhuv2_tx_init(struct amba_device *adev, struct mhuv2 *mhu,
>  		ret = devm_request_threaded_irq(dev, adev->irq[0], NULL,
>  						mhuv2_sender_interrupt,
>  						IRQF_ONESHOT, "mhuv2-tx", mhu);
> -		if (ret) {
> -			dev_err(dev, "Failed to request tx IRQ, fallback to polling mode: %d\n",
> -				ret);
> -		} else {
> +		if (!ret) {
>  			mhu->mbox.txdone_irq = true;
>  			mhu->mbox.txdone_poll = false;
>  			mhu->irq = adev->irq[0];
> @@ -1039,10 +1036,8 @@ static int mhuv2_rx_init(struct amba_device *adev, struct mhuv2 *mhu,
>  	ret = devm_request_threaded_irq(dev, mhu->irq, NULL,
>  					mhuv2_receiver_interrupt, IRQF_ONESHOT,
>  					"mhuv2-rx", mhu);
> -	if (ret) {
> -		dev_err(dev, "Failed to request rx IRQ\n");
> +	if (ret)
>  		return ret;
> -	}
>  
>  	/* Mask all the channel windows */
>  	for (i = 0; i < mhu->windows; i++)
> diff --git a/drivers/mailbox/arm_mhuv3.c b/drivers/mailbox/arm_mhuv3.c
> index a1c528be47f3..6569c3dda377 100644
> --- a/drivers/mailbox/arm_mhuv3.c
> +++ b/drivers/mailbox/arm_mhuv3.c
> @@ -982,8 +982,7 @@ static int mhuv3_setup_pbx(struct mhuv3 *mhu)
>  						mhuv3_pbx_comb_interrupt,
>  						IRQF_ONESHOT, "mhuv3-pbx", mhu);
>  		if (ret)
> -			return dev_err_probe(dev, ret,
> -					     "Failed to request PBX IRQ\n");
> +			return ret;
>  
>  		mhu->mbox.txdone_irq = true;
>  		mhu->mbox.txdone_poll = false;
> @@ -1020,7 +1019,7 @@ static int mhuv3_setup_mbx(struct mhuv3 *mhu)
>  					mhuv3_mbx_comb_interrupt, IRQF_ONESHOT,
>  					"mhuv3-mbx", mhu);
>  	if (ret)
> -		return dev_err_probe(dev, ret, "Failed to request MBX IRQ\n");
> +		return ret;
>  
>  	for (i = 0; i < NUM_EXT; i++)
>  		if (mhu->ext[i])
> diff --git a/drivers/mailbox/armada-37xx-rwtm-mailbox.c b/drivers/mailbox/armada-37xx-rwtm-mailbox.c
> index 456a117a65fd..047926711e96 100644
> --- a/drivers/mailbox/armada-37xx-rwtm-mailbox.c
> +++ b/drivers/mailbox/armada-37xx-rwtm-mailbox.c
> @@ -108,10 +108,8 @@ static int a37xx_mbox_startup(struct mbox_chan *chan)
>  
>  	ret = devm_request_irq(mbox->dev, mbox->irq, a37xx_mbox_irq_handler, 0,
>  			       DRIVER_NAME, chan);
> -	if (ret < 0) {
> -		dev_err(mbox->dev, "Cannot request irq\n");
> +	if (ret < 0)
>  		return ret;
> -	}
>  
>  	/* enable IRQ generation */
>  	reg = readl(mbox->base + RWTM_HOST_INT_MASK);
> diff --git a/drivers/mailbox/bcm-pdc-mailbox.c b/drivers/mailbox/bcm-pdc-mailbox.c
> index 406bc41cba60..6fcc4002ad4f 100644
> --- a/drivers/mailbox/bcm-pdc-mailbox.c
> +++ b/drivers/mailbox/bcm-pdc-mailbox.c
> @@ -1403,11 +1403,8 @@ static int pdc_interrupts_init(struct pdc_state *pdcs)
>  
>  	err = devm_request_irq(dev, pdcs->pdc_irq, pdc_irq_handler, 0,
>  			       dev_name(dev), dev);
> -	if (err) {
> -		dev_err(dev, "IRQ %u request failed with err %d\n",
> -			pdcs->pdc_irq, err);
> +	if (err)
>  		return err;
> -	}
>  	return PDC_SUCCESS;
>  }
>  
> diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c
> index ea12fb8d2401..5af1e434c71c 100644
> --- a/drivers/mailbox/bcm2835-mailbox.c
> +++ b/drivers/mailbox/bcm2835-mailbox.c
> @@ -147,11 +147,8 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
>  	ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
>  			       bcm2835_mbox_irq, IRQF_NO_SUSPEND, dev_name(dev),
>  			       mbox);
> -	if (ret) {
> -		dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
> -			ret);
> +	if (ret)
>  		return -ENODEV;
> -	}
>  
>  	mbox->regs = devm_platform_ioremap_resource(pdev, 0);
>  	if (IS_ERR(mbox->regs)) {
> diff --git a/drivers/mailbox/bcm74110-mailbox.c b/drivers/mailbox/bcm74110-mailbox.c
> index 344cfc35984b..c8709d509912 100644
> --- a/drivers/mailbox/bcm74110-mailbox.c
> +++ b/drivers/mailbox/bcm74110-mailbox.c
> @@ -601,7 +601,7 @@ static int bcm74110_mbox_probe(struct platform_device *pdev)
>  	ret = devm_request_irq(dev, mbox->rx_irq, bcm74110_mbox_isr,
>  			       IRQF_NO_SUSPEND, pdev->name, mbox);
>  	if (ret)
> -		return dev_err_probe(dev, ret, "Failed to request irq\n");
> +		return ret;
>  
>  	mbox->controller.ops = &bcm74110_mbox_chan_ops;
>  	mbox->controller.dev = dev;
> diff --git a/drivers/mailbox/cv1800-mailbox.c b/drivers/mailbox/cv1800-mailbox.c
> index 4bca9d8be4ba..b062c2c4fc61 100644
> --- a/drivers/mailbox/cv1800-mailbox.c
> +++ b/drivers/mailbox/cv1800-mailbox.c
> @@ -189,7 +189,7 @@ static int cv1800_mbox_probe(struct platform_device *pdev)
>  					cv1800_mbox_isr, IRQF_ONESHOT,
>  					dev_name(&pdev->dev), mb);
>  	if (err < 0)
> -		return dev_err_probe(dev, err, "Failed to register irq\n");
> +		return err;
>  
>  	for (idx = 0; idx < MAILBOX_MAX_CHAN; idx++) {
>  		mb->priv[idx].idx = idx;
> diff --git a/drivers/mailbox/hi6220-mailbox.c b/drivers/mailbox/hi6220-mailbox.c
> index 69d15b6283e9..8eb8242209be 100644
> --- a/drivers/mailbox/hi6220-mailbox.c
> +++ b/drivers/mailbox/hi6220-mailbox.c
> @@ -297,11 +297,8 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
>  
>  	err = devm_request_irq(dev, mbox->irq, hi6220_mbox_interrupt, 0,
>  			dev_name(dev), mbox);
> -	if (err) {
> -		dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
> -			err);
> +	if (err)
>  		return -ENODEV;
> -	}
>  
>  	mbox->controller.dev = dev;
>  	mbox->controller.chans = &mbox->chan[0];
> diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c
> index 6c40d865b3f1..704a259bbca8 100644
> --- a/drivers/mailbox/mailbox-mpfs.c
> +++ b/drivers/mailbox/mailbox-mpfs.c
> @@ -219,8 +219,6 @@ static int mpfs_mbox_startup(struct mbox_chan *chan)
>  		return -EINVAL;
>  
>  	ret = devm_request_irq(mbox->dev, mbox->irq, mpfs_mbox_inbox_isr, 0, "mpfs-mailbox", chan);
> -	if (ret)
> -		dev_err(mbox->dev, "failed to register mailbox interrupt:%d\n", ret);
>  
>  	return ret;
>  }
> diff --git a/drivers/mailbox/mailbox-sti.c b/drivers/mailbox/mailbox-sti.c
> index b6c9ecbbc8ec..4fb5d1b5504a 100644
> --- a/drivers/mailbox/mailbox-sti.c
> +++ b/drivers/mailbox/mailbox-sti.c
> @@ -468,10 +468,8 @@ static int sti_mbox_probe(struct platform_device *pdev)
>  					sti_mbox_irq_handler,
>  					sti_mbox_thread_handler,
>  					IRQF_ONESHOT, mdev->name, mdev);
> -	if (ret) {
> -		dev_err(&pdev->dev, "Can't claim IRQ %d\n", irq);
> +	if (ret)
>  		return -EINVAL;
> -	}
>  
>  	dev_info(&pdev->dev, "%s: Registered Tx/Rx Mailbox\n", mdev->name);
>  
> diff --git a/drivers/mailbox/qcom-cpucp-mbox.c b/drivers/mailbox/qcom-cpucp-mbox.c
> index 862e45e8fbd5..8d0000e2b2f9 100644
> --- a/drivers/mailbox/qcom-cpucp-mbox.c
> +++ b/drivers/mailbox/qcom-cpucp-mbox.c
> @@ -156,7 +156,7 @@ static int qcom_cpucp_mbox_probe(struct platform_device *pdev)
>  	ret = devm_request_irq(dev, irq, qcom_cpucp_mbox_irq_fn,
>  			       IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "apss_cpucp_mbox", cpucp);
>  	if (ret < 0)
> -		return dev_err_probe(dev, ret, "Failed to register irq: %d\n", irq);
> +		return ret;
>  
>  	writeq(APSS_CPUCP_RX_MBOX_CMD_MASK, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);
>  
> diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c
> index d957d989c0ce..e5ebb4a237ff 100644
> --- a/drivers/mailbox/qcom-ipcc.c
> +++ b/drivers/mailbox/qcom-ipcc.c
> @@ -323,10 +323,8 @@ static int qcom_ipcc_probe(struct platform_device *pdev)
>  	ret = devm_request_irq(&pdev->dev, ipcc->irq, qcom_ipcc_irq_fn,
>  			       IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND |
>  			       IRQF_NO_THREAD, name, ipcc);
> -	if (ret < 0) {
> -		dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret);
> +	if (ret < 0)
>  		goto err_req_irq;
> -	}
>  
>  	platform_set_drvdata(pdev, ipcc);
>  
> diff --git a/drivers/mailbox/sprd-mailbox.c b/drivers/mailbox/sprd-mailbox.c
> index 565502904e1f..9013d2ece55f 100644
> --- a/drivers/mailbox/sprd-mailbox.c
> +++ b/drivers/mailbox/sprd-mailbox.c
> @@ -377,10 +377,8 @@ static int sprd_mbox_probe(struct platform_device *pdev)
>  
>  	ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr,
>  			       IRQF_NO_SUSPEND, dev_name(dev), priv);
> -	if (ret) {
> -		dev_err(dev, "failed to request inbox IRQ: %d\n", ret);
> +	if (ret)
>  		return ret;
> -	}
>  
>  	outbox_irq = platform_get_irq_byname(pdev, "outbox");
>  	if (outbox_irq < 0)
> @@ -388,20 +386,16 @@ static int sprd_mbox_probe(struct platform_device *pdev)
>  
>  	ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr,
>  			       IRQF_NO_SUSPEND, dev_name(dev), priv);
> -	if (ret) {
> -		dev_err(dev, "failed to request outbox IRQ: %d\n", ret);
> +	if (ret)
>  		return ret;
> -	}
>  
>  	/* Supplementary outbox IRQ is optional */
>  	supp_irq = platform_get_irq_byname(pdev, "supp-outbox");
>  	if (supp_irq > 0) {
>  		ret = devm_request_irq(dev, supp_irq, sprd_mbox_supp_isr,
>  				       IRQF_NO_SUSPEND, dev_name(dev), priv);
> -		if (ret) {
> -			dev_err(dev, "failed to request outbox IRQ: %d\n", ret);
> +		if (ret)
>  			return ret;
> -		}
>  
>  		if (!priv->info->supp_id) {
>  			dev_err(dev, "no supplementary outbox specified\n");
> diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c
> index 4f63f1a14ca6..c6134de1a93b 100644
> --- a/drivers/mailbox/stm32-ipcc.c
> +++ b/drivers/mailbox/stm32-ipcc.c
> @@ -263,10 +263,8 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
>  		ret = devm_request_threaded_irq(dev, ipcc->irqs[i], NULL,
>  						irq_thread[i], IRQF_ONESHOT,
>  						dev_name(dev), ipcc);
> -		if (ret) {
> -			dev_err(dev, "failed to request irq %lu (%d)\n", i, ret);
> +		if (ret)
>  			goto err_clk;
> -		}
>  	}
>  
>  	/* mask and enable rx/tx irq */
> diff --git a/drivers/mailbox/sun6i-msgbox.c b/drivers/mailbox/sun6i-msgbox.c
> index 6ba6920f4645..1cbc4d478beb 100644
> --- a/drivers/mailbox/sun6i-msgbox.c
> +++ b/drivers/mailbox/sun6i-msgbox.c
> @@ -257,10 +257,8 @@ static int sun6i_msgbox_probe(struct platform_device *pdev)
>  
>  	ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
>  			       sun6i_msgbox_irq, 0, dev_name(dev), mbox);
> -	if (ret) {
> -		dev_err(dev, "Failed to register IRQ handler: %d\n", ret);
> +	if (ret)
>  		goto err_disable_unprepare;
> -	}
>  
>  	mbox->controller.dev           = dev;
>  	mbox->controller.ops           = &sun6i_msgbox_chan_ops;
> diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
> index 500fa77c7d53..03ee73aad488 100644
> --- a/drivers/mailbox/tegra-hsp.c
> +++ b/drivers/mailbox/tegra-hsp.c
> @@ -708,11 +708,8 @@ static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp)
>  
>  		err = devm_request_irq(hsp->dev, irq, tegra_hsp_shared_irq, 0,
>  				       dev_name(hsp->dev), hsp);
> -		if (err < 0) {
> -			dev_err(hsp->dev, "failed to request interrupt: %d\n",
> -				err);
> +		if (err < 0)
>  			continue;
> -		}
>  
>  		hsp->shared_irq = i;
>  
> @@ -856,12 +853,8 @@ static int tegra_hsp_probe(struct platform_device *pdev)
>  		err = devm_request_irq(&pdev->dev, hsp->doorbell_irq,
>  				       tegra_hsp_doorbell_irq, IRQF_NO_SUSPEND,
>  				       dev_name(&pdev->dev), hsp);
> -		if (err < 0) {
> -			dev_err(&pdev->dev,
> -			        "failed to request doorbell IRQ#%u: %d\n",
> -				hsp->doorbell_irq, err);
> +		if (err < 0)
>  			return err;
> -		}
>  	}
>  
>  	if (hsp->shared_irqs) {
> -- 
> 2.34.1
> 
> 

Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>






^ permalink raw reply

* Re: [PATCH net-next v10 0/4] net: stmmac: eic7700: add eth1 variant support and update delay bindings
From: Paolo Abeni @ 2026-07-21  7:22 UTC (permalink / raw)
  To: lizhi2, devicetree, andrew+netdev, davem, edumazet, kuba, robh,
	krzk+dt, conor+dt, netdev, mcoquelin.stm32, alexandre.torgue,
	rmk+kernel, linux-stm32, linux-arm-kernel, linux-kernel,
	maxime.chevallier
  Cc: ningyu, linmin, pinkesh.vaghela, pritesh.patel, weishangjuan,
	horms
In-Reply-To: <20260707064033.1265-1-lizhi2@eswincomputing.com>

On 7/7/26 8:40 AM, lizhi2@eswincomputing.com wrote:
> From: Zhi Li <lizhi2@eswincomputing.com>
> 
> This series updates Ethernet support for the ESWIN EIC7700 SoC,
> including support for the eth1 MAC variant.
> 
> The series includes DT binding updates and stmmac glue driver updates
> for the EIC7700 Ethernet controller.
> 
> The changes include:
>   - Update the tx-internal-delay-ps binding property from a fixed enum
>     list to a range-based definition, and make it optional with the
>     corresponding driver update.
> 
>   - Add support for the EIC7700 eth1 MAC variant, which has
>     silicon-specific RX sampling behavior and TX timing characteristics.
>     A dedicated compatible string is introduced for this hardware variant,
>     and the driver applies the required RX clock inversion handling and
>     timing adjustments.
> 
> Due to silicon characteristics, the eth1 interface has a fixed TX
> internal delay of approximately 2 ns and an RX sampling skew of 4-5 ns
> that cannot be compensated solely by standard RGMII delay settings.
> The binding models the effective TX delay range of this variant, while
> the driver handles the required hardware-specific timing configuration.
> 
> Only the DT bindings and driver patches are included in this series. The
> DTS changes are intentionally omitted for the following reasons:
>   - The HSP bus infrastructure is being introduced by Pinkesh Vaghela's
>     DT series [0], currently under review.
>   - The HSPCRG clock/reset series [1] provides additional infrastructure
>     for the HSP subsystem.
> 
> Once these dependencies are merged, a follow-up DT series will enable
> Ethernet, USB, eMMC, and SD across the HSP bus.
> 
> Reference:
>   [0] https://lore.kernel.org/lkml/20260706081055.1126275-1-pinkesh.vaghela@einfochips.com/
>   [1] https://lore.kernel.org/all/20260605060730.1605-1-dongxuyang@eswincomputing.com/
Applied, thanks!

/P



^ permalink raw reply

* Re: [PATCH v6 0/2] Minor fix and cleanup to irqchip/gic-v3-its
From: Marc Zyngier @ 2026-07-21  7:15 UTC (permalink / raw)
  To: Kemeng Shi; +Cc: tglx, radu, linux-arm-kernel, linux-kernel
In-Reply-To: <20260721063241.52549-1-shikemeng@huaweicloud.com>

On Tue, 21 Jul 2026 07:32:39 +0100,
Kemeng Shi <shikemeng@huaweicloud.com> wrote:
> 
> There are some random fixes and cleanups to irqchip/gic-v3-its. More
> details can be found in respective patches.
> Thanks.
> 
> v1->v2:
> - Drop unneeded patches and some minor improvement.
> v2->v3:
> - Fix an extra leak issue in its_vpe_irq_domain_alloc().
> - Remove redundant check in its_vpe_db_proxy_unmap_locked()
> v3->v4:
> - Add missing Fixes tags
> - Improve changelog description
> v4->v5:
> - Collect RVB from Radu
> - Drop more unneeded patches
> - Use existing error handling to do its_vpe_teardown() in patch 3/4
> v5->v6:
> - Collect more RVB from Radu
> - Drop first two patches which are already merged
> - Add comment and do an early when vpe is not initialized in patch 1/2 
> - Use single-line comment in patch 2/2
> 
> Kemeng Shi (2):
>   irqchip/gic-v3-its: Fix leak in its_vpe_irq_domain_alloc()
>   irqchip/gic-v3-its: Fix grammar and replace a bit number with its
>     symbol
> 
>  drivers/irqchip/irq-gic-v3-its.c | 18 ++++++++++++------
>  1 file changed, 12 insertions(+), 6 deletions(-)
> 
Acked-by: Marc Zyngier <maz@kernel.org>

	M.

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


^ permalink raw reply

* Re: [PATCH v5 3/7] mtd: spi-nor: sfdp: expose the SFDP as a read-only NVMEM device
From: Michael Walle @ 2026-07-21  7:02 UTC (permalink / raw)
  To: Manikandan Muralidharan, pratyush, mwalle, takahiro.kuwano,
	miquel.raynal, richard, vigneshr, robh, krzk+dt, conor+dt, srini,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, linux,
	richardcochran, linusw, arnd, linux-mtd, devicetree, linux-kernel,
	linux-arm-kernel, netdev
In-Reply-To: <20260721052859.171341-4-manikandan.m@microchip.com>

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

On Tue Jul 21, 2026 at 7:28 AM CEST, Manikandan Muralidharan wrote:
> The SPI NOR core already reads the SFDP tables during enumeration and
> caches them in nor->sfdp->dwords (see spi_nor_parse_sfdp()). Re-expose
> that cached data as a read-only NVMEM device, in on-flash byte order,
> rooted at the flash's SFDP child node (compatible "jedec,sfdp").
>
> This lets NVMEM cells reference any SFDP data: a fixed-layout for
> parameters at a known offset, or an nvmem-layout parser for vendor data
> whose location must be discovered at runtime.The device is only registered
> when an "sfdp" node is present in the device tree.
>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
> ---
>  drivers/mtd/spi-nor/core.c |  8 ++++
>  drivers/mtd/spi-nor/core.h |  1 +
>  drivers/mtd/spi-nor/sfdp.c | 86 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 95 insertions(+)
>
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index ccf4396cdcd0..b833d8ec2d65 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -3204,6 +3204,14 @@ static int spi_nor_init_params(struct spi_nor *nor)
>  		spi_nor_init_params_deprecated(nor);
>  	}
>  
> +	/*
> +	 * Expose the SFDP table as an NVMEM device only when
> +	 * the flash actually provides one
> +	 */
> +	ret = spi_nor_register_sfdp_nvmem(nor);
> +	if (ret)
> +		return ret;
> +
>  	ret = spi_nor_late_init_params(nor);
>  	if (ret)
>  		return ret;
> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
> index ba2d1a862c9d..0a6484298c5c 100644
> --- a/drivers/mtd/spi-nor/core.h
> +++ b/drivers/mtd/spi-nor/core.h
> @@ -698,6 +698,7 @@ int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
>  
>  int spi_nor_check_sfdp_signature(struct spi_nor *nor);
>  int spi_nor_parse_sfdp(struct spi_nor *nor);
> +int spi_nor_register_sfdp_nvmem(struct spi_nor *nor);

That's probably not needed if..

>  static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
>  {
> diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
> index 4600983cb579..704799fe92ae 100644
> --- a/drivers/mtd/spi-nor/sfdp.c
> +++ b/drivers/mtd/spi-nor/sfdp.c
> @@ -6,6 +6,8 @@
>  
>  #include <linux/bitfield.h>
>  #include <linux/mtd/spi-nor.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/of.h>
>  #include <linux/slab.h>
>  #include <linux/sort.h>
>  
> @@ -1612,3 +1614,87 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
>  	kfree(param_headers);
>  	return err;
>  }
> +
> +static int spi_nor_sfdp_reg_read(void *priv, unsigned int offset,
> +				 void *val, size_t bytes)
> +{
> +	struct spi_nor *nor = priv;
> +	struct sfdp *sfdp = nor->sfdp;
> +	size_t sfdp_size = sfdp->num_dwords * sizeof(*sfdp->dwords);
> +
> +	if (offset >= sfdp_size || bytes > sfdp_size - offset)
> +		return -EINVAL;
> +
> +	/* The cached SFDP is kept in on-flash (little-endian) byte order. */
> +	memcpy(val, (u8 *)sfdp->dwords + offset, bytes);
> +
> +	return 0;
> +}
> +
> +static void spi_nor_sfdp_nvmem_put_np(void *data)
> +{
> +	of_node_put(data);
> +}
> +
> +/**
> + * spi_nor_register_sfdp_nvmem() - expose the SFDP as a read-only NVMEM device
> + * @nor:	pointer to a 'struct spi_nor'
> + *
> + * Expose the whole SFDP, in on-flash byte order, as a read-only NVMEM device
> + * rooted at the flash's SFDP child node (compatible "jedec,sfdp"). This lets
> + * generic (fixed-layout) or vendor (nvmem-layout) cells reference any SFDP
> + * data. The device is only registered when a child node with the "jedec,sfdp"
> + * compatible is described in the device tree.
> + *
> + * Return: 0 on success or if there is nothing to do, -errno otherwise.
> + */
> +int spi_nor_register_sfdp_nvmem(struct spi_nor *nor)

.. you move all this into the core, as the sfdp.c is just for
parsing the tables.

> +{
> +	struct device *dev = nor->dev;
> +	struct nvmem_config config = { };
> +	struct nvmem_device *nvmem;
> +	struct device_node *np;
> +	int ret;
> +
> +	if (!nor->sfdp)
> +		return 0;
> +
> +	for_each_available_child_of_node(dev_of_node(dev), np)
> +		if (of_device_is_compatible(np, "jedec,sfdp"))
> +			break;

There is already of_get_compatible_child() doing exactly this.

> +	if (!np)
> +		return 0;
> +
> +	/*
> +	 * Register the put before devm_nvmem_register() so it runs last on
> +	 * detach, after the NVMEM device that uses the node is gone.
> +	 */
> +	ret = devm_add_action_or_reset(dev, spi_nor_sfdp_nvmem_put_np, np);
> +	if (ret)
> +		return ret;
> +
> +	config.dev = dev;
> +	config.of_node = np;
> +	config.name = "sfdp";
> +	config.id = NVMEM_DEVID_AUTO;

Or rather NVEMEM_DEVID_NONE? There will ever be just one SFDP nvmem
device.

How does the sysfs path looks like?

-michael

> +	config.owner = THIS_MODULE;
> +	config.read_only = true;
> +	config.word_size = 1;
> +	config.stride = 1;
> +	config.size = (int)(nor->sfdp->num_dwords * sizeof(*nor->sfdp->dwords));
> +	config.reg_read = spi_nor_sfdp_reg_read;
> +	config.priv = nor;
> +
> +	nvmem = devm_nvmem_register(dev, &config);
> +	if (IS_ERR(nvmem)) {
> +		/* NVMEM support is optional. */
> +		if (PTR_ERR(nvmem) == -EOPNOTSUPP)
> +			return 0;
> +		return dev_err_probe(dev, PTR_ERR(nvmem),
> +				     "failed to register SFDP NVMEM device\n");
> +	}
> +
> +	dev_dbg(dev, "exposed %d-byte SFDP as an NVMEM device\n", config.size);
> +
> +	return 0;
> +}

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

^ permalink raw reply

* Re: [PATCH v2] arm64: Disallow disabling boot CPU based on config
From: Sneh Mankad @ 2026-07-21  6:59 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Thomas Gleixner, Daniel Lezcano, Peter Zijlstra,
	Rafael J. Wysocki, Pavel Machek, Len Brown, Catalin Marinas,
	Will Deacon, linux-arm-msm, linux-kernel, linux-pm,
	linux-arm-kernel
In-Reply-To: <akfHIH_dZrr5bosu@J2N7QTR9R3>



On 03-Jul-26 7:58 PM, Mark Rutland wrote:
> On Fri, Jul 03, 2026 at 04:50:02PM +0530, Sneh Mankad wrote:
>> The Qualcomm SoCs like LeMans, Monaco 
> 
> Are those released products?

Yes, the SoCs are present in upstream:

arch/arm64/boot/dts/qcom/lemans.dtsi
arch/arm64/boot/dts/qcom/monaco.dtsi

> 
> Are those mobile phone parts, or somthing else?

These are ride (automotive) chipsets and IoT boards.

> 
>> support suspend to ram which leads the SoC to ACPI S3 similar state
>> where SoC is turned off and DDR is retained. The hardware design on
>> these SoCs forces a constraint to suspend and resume the system on
>> boot CPU / CPU0.
>>
>> If CPU0 is already offline before starting suspend to ram the
>> freeze_secondary_cpus() picks alternate CPU as primary / last CPU and
>> proceed further to invoke PSCI SYSTEM_SUSPEND.
>> This leads to a system crash.
> 
> Ok, so that's a firmware bug.
> 
> Why does the FW permit CPU0 to be offlined in the first place if it
> can't handle this?

FW permits CPU0 disablement because it functions properly in regular run time.
But there is an additional hardware constraint in above mentioned platforms, that
suspend to ram has to be performed via boot CPU / CPU0. Due to this constraint,
CPU0 is required to be online when suspend to ram is triggered.

> What does PSCI_MIGRATE_INFO_TYPE report?

It is a optional function call, which is not implemented in firmware.

> Ideally it'd report Uniprocessor (UP) not migrate capable (1), which
> would prevent CPU_OFF on that CPU, and would force suspend to happen
> there...

Also, above platforms are multicore (8 CPUs each).

>> In order to prevent such an issue introduce PM_SLEEP_SMP_CPU_ZERO_STRICT
>> config and when enabled prohibit the CPU0 from getting disabled.
> 
> I don't think it makes sense for this to be a config option.
> 
> This is a platform-specific property, and it's possible to build a
> kernel that boots on this platform and/or other platforms.

Yes, but for that reason the config will only be enabled for SoCs that have this
constraint, it will remain disabled for other SoCs.

>> Signed-off-by: Sneh Mankad <sneh.mankad@oss.qualcomm.com>
>> ---
>> Changes in v2:
>> - Moved the check to arm64 specific code.
>> - Link to v1: https://lore.kernel.org/r/20260605-disable_boot_cpu_offline-v1-1-4c68fe1a6cf8@oss.qualcomm.com
>> ---
>>  arch/arm64/Kconfig       | 9 +++++++++
>>  arch/arm64/kernel/psci.c | 6 ++++++
>>  2 files changed, 15 insertions(+)
>>
>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>> index fe60738e5943ba279e5571862423df4fed3db661..21697a535a25d286a2f8afe4921a41b13cc32c0a 100644
>> --- a/arch/arm64/Kconfig
>> +++ b/arch/arm64/Kconfig
>> @@ -263,6 +263,15 @@ config ARM64
>>  	help
>>  	  ARM 64-bit (AArch64) Linux support.
>>  
>> +config PM_SLEEP_SMP_CPU_ZERO_STRICT
>> +       bool "Disallow boot CPU (CPU0) offline"
>> +       depends on ARCH_QCOM
> 
> Why can't others select this?

I can remove ARCH_QCOM dependency if any other want to use this.

> 
>> +       depends on HOTPLUG_CPU
>> +       depends on SUSPEND
>> +       help
>> +         Disallow boot CPU (CPU0) offline when the suspend_ops->enter()
>> +         has to be executed by boot CPU.
> 
> As above, I don't think this makse sense as a config option.
> 
> Either we handle the FW bug, or we do not.
> 
> Mark.
> 

As mentioned above, its not a FW bug. CPU0 is allowed to be disabled from FW and HW constraint is for
suspend to ram to be performed on boot CPU.

Thanks,
Sneh



^ permalink raw reply

* Re: [PATCH v2] KVM: arm64: Fix hyp_trace clock disabling
From: Fuad Tabba @ 2026-07-21  6:55 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Vincent Donnefort, oupton, kvmarm, linux-arm-kernel, joey.gouly,
	seiden, suzuki.poulose, yuzenghui, catalin.marinas, will,
	kernel-team
In-Reply-To: <861pcwg7gz.wl-maz@kernel.org>

On Tue, 21 Jul 2026 at 07:52, 'Marc Zyngier' via kernel-team
<kernel-team@android.com> wrote:
>
> On Mon, 20 Jul 2026 20:45:00 +0100,
> Fuad Tabba <fuad.tabba@linux.dev> wrote:
> >
> > Hi Vincent,
> >
> > On Wed, 15 Jul 2026 at 11:51, Vincent Donnefort <vdonnefort@google.com> wrote:
> > >
> > > Fix the disable path in hyp_trace_clock_enable(), which fell through to
> > > re-initialize and reschedule the clock after cancelling the work. Return
> > > early instead.
> > >
> > > While at it, cleanup hyp_trace_clock::lock which is unused and
> > > hyp_trace_clock::running which is redundant: the trace_remote framework
> > > already serializes calls to the callback enable_tracing.
> >
> > Although the cleanup and the fix are to the same commit, could this be
> > split into the fix and the lock/running cleanup as a separate patch?
> > Since b22888917fa41 is already in a released kernel, the fix will
> > likely go to stable, and it'd be nice to keep that backport to just
> > the behavioural change.
>
> Not sure I get what you mean here. From what I can see, this patch
> fully applies to b22888917fa41. Given that this is the third version
> for something that trivial (two from Vincent, one from you), can we
> please agree on *something* quickly?

No strong preference from my side. The fix and the cleanup are
correct, I just thought combining them in one commit might be less
than ideal. If that's not an issue:

Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>

Cheers,
/fuad


>
> Thanks,
>
>         M.
>
> --
> Without deviation from the norm, progress is not possible.
>
> To unsubscribe from this group and stop receiving emails from it, send an email to kernel-team+unsubscribe@android.com.
>


^ permalink raw reply

* [PATCH v6 16/16] arm64: dts: mediatek: mt8188-geralt: Add WiFi/BT as M.2 E-key slot
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

The MT8188 Geralt design features a chip-on-board WiFi/BT solution. This
is a M.2 E-key WiFi/BT board layout directly inserted into the mainboard
design. The connections to the rest of the board are almost the same as
if it were a separate M.2 card. The only addition is the PMU_EN pin on
the chip; on M.2 cards this would be tied to the primary power source.

Model the chip-on-board WiFi/BT solution as a M.2 E-key slot with PCIe,
USB and auxiliary signals. The PMU_EN pin, which enables the internal
power controls and regulators, is modeled as a regulator fed by the
pp3300_wlan regulator. Since power sequencing is now correctly modeled
using the M.2 E-key slot, drop the "regulator-always-on" property one
pp3300_wlan regulator. Also drop the comment in xhci2 saying "MT7921's
power is controlled by PCIe".

Also drop the voltage range on the pp3300_wlan regulator. This
"regulator" is just a load switch and does not provide any regulation.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v2:
- Drop default GPIO output state from kill pins pinconfig
---
 .../boot/dts/mediatek/mt8188-geralt.dtsi      | 92 ++++++++++++++++++-
 1 file changed, 88 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi
index dee946309121..73b5c0fdeae6 100644
--- a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi
@@ -88,13 +88,11 @@ pp3300_z1: regulator-pp3300-z1 {
 	pp3300_wlan: regulator-pp3300-wlan {
 		compatible = "regulator-fixed";
 		regulator-name = "pp3300_wlan";
-		regulator-always-on;
-		regulator-min-microvolt = <3300000>;
-		regulator-max-microvolt = <3300000>;
 		enable-active-high;
 		gpio = <&pio 12 GPIO_ACTIVE_HIGH>;
 		pinctrl-0 = <&wlan_en>;
 		pinctrl-names = "default";
+		/* load switch */
 		vin-supply = <&pp3300_z1>;
 	};
 
@@ -161,6 +159,17 @@ ppvar_mipi_disp_avee: regulator-ppvar-mipi-disp-avee {
 		vin-supply = <&pp5000_z1>;
 	};
 
+	/* PMU_EN pin controls internal regulators and power sequence */
+	wlan_pmu: regulator-wlan-pmu {
+		compatible = "regulator-fixed";
+		regulator-name = "wlan-pmu";
+		enable-active-high;
+		gpio = <&pio 145 GPIO_ACTIVE_HIGH>;
+		pinctrl-0 = <&wlan_pmu_en>;
+		pinctrl-names = "default";
+		vin-supply = <&pp3300_wlan>;
+	};
+
 	reserved_memory: reserved-memory {
 		#address-cells = <2>;
 		#size-cells = <2>;
@@ -195,6 +204,39 @@ adsp_dma_mem: memory@61000000 {
 			no-map;
 		};
 	};
+
+	wifi-bt-connector {
+		compatible = "pcie-m2-e-connector";
+		pinctrl-names = "default";
+		pinctrl-0 = <&m2_e_key_kill_pins>;
+		vpcie1v8-supply = <&mt6359_vcn18_ldo_reg>;
+		vpcie3v3-supply = <&wlan_pmu>;
+		w-disable1-gpios = <&pio 13 GPIO_ACTIVE_LOW>;
+		w-disable2-gpios = <&pio 14 GPIO_ACTIVE_LOW>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			/* PCIe for WiFi */
+			port@0 {
+				reg = <0>;
+
+				wifi_ep: endpoint {
+					remote-endpoint = <&pcie_ep>;
+				};
+			};
+
+			/* USB for Bluetooth */
+			port@2 {
+				reg = <2>;
+
+				bt_ep: endpoint {
+					remote-endpoint = <&usb2_ep>;
+				};
+			};
+		};
+	};
 };
 
 &adsp {
@@ -659,6 +701,22 @@ &pcie {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pcie_pins>;
 	status = "okay";
+
+	pcie@0 {
+		compatible = "pciclass,0604";
+		reg = <0 0 0 0 0>;
+		device_type = "pci";
+		num-lanes = <1>;
+		#address-cells = <3>;
+		#size-cells = <2>;
+		ranges;
+
+		port {
+			pcie_ep: endpoint {
+				remote-endpoint = <&wifi_ep>;
+			};
+		};
+	};
 };
 
 &pciephy {
@@ -1002,6 +1060,13 @@ pins-bus {
 		};
 	};
 
+	m2_e_key_kill_pins: m2-e-key-kill-pins {
+		pins-kill {
+			pinmux = <PINMUX_GPIO13__FUNC_B_GPIO13>,
+				 <PINMUX_GPIO14__FUNC_B_GPIO14>;
+		};
+	};
+
 	mipi_disp_avdd_en: mipi-disp-avdd-en-pins {
 		pins-en-ppvar-mipi-disp {
 			pinmux = <PINMUX_GPIO3__FUNC_B_GPIO3>;
@@ -1174,6 +1239,13 @@ pins-bus {
 		};
 	};
 
+	wlan_pmu_en: wlan-pmu-en-pins {
+		pins-wlan-pmu-en {
+			pinmux = <PINMUX_GPIO145__FUNC_B_GPIO145>;
+			output-low;
+		};
+	};
+
 	wlan_en: wlan-en-pins {
 		pins-en-pp3300-wlan {
 			pinmux = <PINMUX_GPIO12__FUNC_B_GPIO12>;
@@ -1417,10 +1489,22 @@ vdosys1_ep_ext: endpoint@1 {
 };
 
 &xhci2 {
-	/* no power supply since MT7921's power is controlled by PCIe */
 	/* MT7921's USB BT has issues with USB2 LPM */
 	usb2-lpm-disable;
 	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@1 {
+			reg = <1>;
+
+			usb2_ep: endpoint {
+				remote-endpoint = <&bt_ep>;
+			};
+		};
+	};
 };
 
 #include <arm/cros-ec-keyboard.dtsi>
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 15/16] arm64: dts: mediatek: mt8195-cherry: Add M.2 E-key slot
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

The Mt8195 Cherry design features an M.2 E-key slot for WiFi/BT combo
cards. Only PCIe and USB are wired from the SoC to the slot, along with
some auxiliary signals.

Add the proper representation for it, replacing the PCIe wifi node and
vpcie3v3-supply property under the PCIe controller, and the vbus-supply
property under the xhci3 node.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v2:
- Drop default GPIO output state from kill pins pinconfig
---
 .../boot/dts/mediatek/mt8195-cherry.dtsi      | 73 +++++++++++++++++--
 1 file changed, 68 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi b/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi
index ef7afc436aef..8d4cc30d91e4 100644
--- a/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi
@@ -266,6 +266,47 @@ tboard_thermistor2: thermal-sensor-t2 {
 						120000 51
 						125000 44>;
 	};
+
+	wifi-bt-connector {
+		compatible = "pcie-m2-e-connector";
+		pinctrl-names = "default";
+		pinctrl-0 = <&m2_e_key_kill_pins>;
+		vpcie3v3-supply = <&pp3300_wlan>;
+		w-disable1-gpios = <&pio 61 GPIO_ACTIVE_LOW>;
+		w-disable2-gpios = <&pio 59 GPIO_ACTIVE_LOW>;
+		/* PCIe auxiliary signals wired to controller. */
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			/* PCIe for WiFi */
+			port@0 {
+				reg = <0>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				wifi_ep: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&pcie1_ep>;
+				};
+			};
+
+			/* USB for Bluetooth */
+			port@2 {
+				reg = <2>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				bt_ep: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&usb3_ep>;
+				};
+			};
+
+			/* SDIO, UART and I2S not implemented */
+		};
+	};
 };
 
 &adsp {
@@ -791,14 +832,14 @@ pcie@0 {
 		reg = <0 0 0 0 0>;
 		device_type = "pci";
 		num-lanes = <1>;
-		vpcie3v3-supply = <&pp3300_wlan>;
 		#address-cells = <3>;
 		#size-cells = <2>;
 		ranges;
 
-		wifi@0 {
-			reg = <0 0 0 0 0>;
-			wakeup-source;
+		port {
+			pcie1_ep: endpoint {
+				remote-endpoint = <&wifi_ep>;
+			};
 		};
 	};
 };
@@ -1085,6 +1126,13 @@ pins-bus {
 		};
 	};
 
+	m2_e_key_kill_pins: m2-e-key-kill-pins {
+		pins-kill {
+			pinmux = <PINMUX_GPIO61__FUNC_GPIO61>,
+				 <PINMUX_GPIO59__FUNC_GPIO59>;
+		};
+	};
+
 	mmc0_pins_default: mmc0-default-pins {
 		pins-cmd-dat {
 			pinmux = <PINMUX_GPIO126__FUNC_MSDC0_DAT0>,
@@ -1637,9 +1685,24 @@ &xhci2 {
 &xhci3 {
 	/* MT7921's USB Bluetooth has issues with USB2 LPM */
 	usb2-lpm-disable;
-	vbus-supply = <&pp3300_wlan>;
 	vusb33-supply = <&mt6359_vusb_ldo_reg>;
 	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			usb3_ep: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&bt_ep>;
+			};
+		};
+	};
 };
 
 #include <arm/cros-ec-keyboard.dtsi>
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 14/16] power: sequencing: pcie-m2: Split Bluetooth unit based on interface
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern, Wei Deng
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

Some UART / serdev based Bluetooth drivers need to be able to reset the
hardware during initialization or recovery. This is not possible with
the current power sequencing API if a unit is shared between USB and
UART interfaces.

For now, split the Bluetooth unit into two, one for each interface.
This allows either interface to independently toggle the power, with
the last action prevailing, thereby unbreaking the Qualcomm Bluetooth
serdev driver. This is also needed for the Realtek Bluetooth serdev
driver (hci_h5).

Having independent control from either interface unfortunately means
that userspace is able to shut down the controller from sysfs using
the USB port's "disable" setting without the serdev driver knowing
about it. On the USB side, independent control is also desired, as it
allows the USB core to power cycle the port/device during faults, and
for userspace to initiate reset and recovery using the aforementioned
"disable" setting. However when USB is used, a serdev device is not
created, and there is no conflicting usage, which allows the power
sequencing to work even without the split unit.

Suggested-by: Wei Deng <wei.deng@oss.qualcomm.com>
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v4:
- New patch

This patch is _not_ squashed into the previous to provide clear context
on the change, and the less than perfect nature of it.

I think what we need in this case is some sort of lockout. The serdev
consumer would want "exclusive" access, locking out the USB consumer,
which can tolerate "shared" access.

Something similar would be needed for the WiFi if we ever add support
for SDIO for toggling the state of W_DISABLE#1. At least the SDIO and
PCIe reset signals are separate.
---
 drivers/power/sequencing/pwrseq-pcie-m2.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c
index 693b0d33f8cf..56e4f40e0480 100644
--- a/drivers/power/sequencing/pwrseq-pcie-m2.c
+++ b/drivers/power/sequencing/pwrseq-pcie-m2.c
@@ -83,8 +83,22 @@ static int pwrseq_pci_m2_e_bt_disable(struct pwrseq_device *pwrseq)
 	return gpiod_set_value_cansleep(ctx->w_disable2_gpio, 1);
 }
 
-static const struct pwrseq_unit_data pwrseq_pcie_m2_e_bt_unit_data = {
-	.name = "bt-enable",
+/*
+ * XXX There are two Bluetooth units to allow either one to be able to power
+ * off and thus reset the controller. In practice only one of the interfaces
+ * is used, so there is no conflict. However userspace could power off the
+ * USB unit by disabling the associated USB port, without the UART unit or
+ * its consumer ever knowing.
+ */
+static const struct pwrseq_unit_data pwrseq_pcie_m2_e_bt_uart_unit_data = {
+	.name = "bt-uart-enable",
+	.deps = pwrseq_pcie_m2_unit_deps,
+	.enable = pwrseq_pci_m2_e_bt_enable,
+	.disable = pwrseq_pci_m2_e_bt_disable,
+};
+
+static const struct pwrseq_unit_data pwrseq_pcie_m2_e_bt_usb_unit_data = {
+	.name = "bt-usb-enable",
 	.deps = pwrseq_pcie_m2_unit_deps,
 	.enable = pwrseq_pci_m2_e_bt_enable,
 	.disable = pwrseq_pci_m2_e_bt_disable,
@@ -130,13 +144,13 @@ static int pwrseq_pcie_m2_e_pwup_delay(struct pwrseq_device *pwrseq)
 
 static const struct pwrseq_target_data pwrseq_pcie_m2_e_uart_target_data = {
 	.name = "uart",
-	.unit = &pwrseq_pcie_m2_e_bt_unit_data,
+	.unit = &pwrseq_pcie_m2_e_bt_uart_unit_data,
 	.post_enable = pwrseq_pcie_m2_e_pwup_delay,
 };
 
 static const struct pwrseq_target_data pwrseq_pcie_m2_e_usb_target_data = {
 	.name = "usb",
-	.unit = &pwrseq_pcie_m2_e_bt_unit_data,
+	.unit = &pwrseq_pcie_m2_e_bt_usb_unit_data,
 };
 
 static const struct pwrseq_target_data pwrseq_pcie_m2_e_pcie_target_data = {
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 13/16] power: sequencing: pcie-m2: Add usb and sdio targets for E-key connector
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

The M.2 E-key connector allows either PCIe or SDIO for WiFi and USB or
UART for BT. Currently the driver only supports PCIe and UART.

Add power sequencing targets for SDIO and USB. To avoid adding a
complicated dependency tree, rename the existing power sequencing units
"pcie" and "uart" to "wifi" and "bt". The existing target names are left
untouched. The new "sdio" and "usb" targets just point to the renamed
"wifi" and "bt" units.

The "unit" names are internal to the power sequencing framework, and
should be confined to a single provider. The names are only
informational. Dependencies are tracked with pointers to other units.

The "target" names are the strings that the consumer uses to acquire a
descriptor with. As these remain the same, existing users will continue
to work.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v2:
- Expand commit message
---
 drivers/power/sequencing/pwrseq-pcie-m2.c | 41 +++++++++++++++--------
 1 file changed, 27 insertions(+), 14 deletions(-)

diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c
index 691cad0a6dd0..693b0d33f8cf 100644
--- a/drivers/power/sequencing/pwrseq-pcie-m2.c
+++ b/drivers/power/sequencing/pwrseq-pcie-m2.c
@@ -69,46 +69,46 @@ static const struct pwrseq_unit_data *pwrseq_pcie_m2_unit_deps[] = {
 	NULL
 };
 
-static int pwrseq_pci_m2_e_uart_enable(struct pwrseq_device *pwrseq)
+static int pwrseq_pci_m2_e_bt_enable(struct pwrseq_device *pwrseq)
 {
 	struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
 
 	return gpiod_set_value_cansleep(ctx->w_disable2_gpio, 0);
 }
 
-static int pwrseq_pci_m2_e_uart_disable(struct pwrseq_device *pwrseq)
+static int pwrseq_pci_m2_e_bt_disable(struct pwrseq_device *pwrseq)
 {
 	struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
 
 	return gpiod_set_value_cansleep(ctx->w_disable2_gpio, 1);
 }
 
-static const struct pwrseq_unit_data pwrseq_pcie_m2_e_uart_unit_data = {
-	.name = "uart-enable",
+static const struct pwrseq_unit_data pwrseq_pcie_m2_e_bt_unit_data = {
+	.name = "bt-enable",
 	.deps = pwrseq_pcie_m2_unit_deps,
-	.enable = pwrseq_pci_m2_e_uart_enable,
-	.disable = pwrseq_pci_m2_e_uart_disable,
+	.enable = pwrseq_pci_m2_e_bt_enable,
+	.disable = pwrseq_pci_m2_e_bt_disable,
 };
 
-static int pwrseq_pci_m2_e_pcie_enable(struct pwrseq_device *pwrseq)
+static int pwrseq_pci_m2_e_wifi_enable(struct pwrseq_device *pwrseq)
 {
 	struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
 
 	return gpiod_set_value_cansleep(ctx->w_disable1_gpio, 0);
 }
 
-static int pwrseq_pci_m2_e_pcie_disable(struct pwrseq_device *pwrseq)
+static int pwrseq_pci_m2_e_wifi_disable(struct pwrseq_device *pwrseq)
 {
 	struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
 
 	return gpiod_set_value_cansleep(ctx->w_disable1_gpio, 1);
 }
 
-static const struct pwrseq_unit_data pwrseq_pcie_m2_e_pcie_unit_data = {
-	.name = "pcie-enable",
+static const struct pwrseq_unit_data pwrseq_pcie_m2_e_wifi_unit_data = {
+	.name = "wifi-enable",
 	.deps = pwrseq_pcie_m2_unit_deps,
-	.enable = pwrseq_pci_m2_e_pcie_enable,
-	.disable = pwrseq_pci_m2_e_pcie_disable,
+	.enable = pwrseq_pci_m2_e_wifi_enable,
+	.disable = pwrseq_pci_m2_e_wifi_disable,
 };
 
 static const struct pwrseq_unit_data pwrseq_pcie_m2_m_pcie_unit_data = {
@@ -130,13 +130,24 @@ static int pwrseq_pcie_m2_e_pwup_delay(struct pwrseq_device *pwrseq)
 
 static const struct pwrseq_target_data pwrseq_pcie_m2_e_uart_target_data = {
 	.name = "uart",
-	.unit = &pwrseq_pcie_m2_e_uart_unit_data,
+	.unit = &pwrseq_pcie_m2_e_bt_unit_data,
 	.post_enable = pwrseq_pcie_m2_e_pwup_delay,
 };
 
+static const struct pwrseq_target_data pwrseq_pcie_m2_e_usb_target_data = {
+	.name = "usb",
+	.unit = &pwrseq_pcie_m2_e_bt_unit_data,
+};
+
 static const struct pwrseq_target_data pwrseq_pcie_m2_e_pcie_target_data = {
 	.name = "pcie",
-	.unit = &pwrseq_pcie_m2_e_pcie_unit_data,
+	.unit = &pwrseq_pcie_m2_e_wifi_unit_data,
+	.post_enable = pwrseq_pcie_m2_e_pwup_delay,
+};
+
+static const struct pwrseq_target_data pwrseq_pcie_m2_e_sdio_target_data = {
+	.name = "sdio",
+	.unit = &pwrseq_pcie_m2_e_wifi_unit_data,
 	.post_enable = pwrseq_pcie_m2_e_pwup_delay,
 };
 
@@ -147,7 +158,9 @@ static const struct pwrseq_target_data pwrseq_pcie_m2_m_pcie_target_data = {
 
 static const struct pwrseq_target_data *pwrseq_pcie_m2_e_targets[] = {
 	&pwrseq_pcie_m2_e_pcie_target_data,
+	&pwrseq_pcie_m2_e_sdio_target_data,
 	&pwrseq_pcie_m2_e_uart_target_data,
+	&pwrseq_pcie_m2_e_usb_target_data,
 	NULL
 };
 
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 12/16] power: sequencing: pcie-m2: support matching on remote "port" node
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

A USB hub can have multiple ports, and this driver needs to
differentiate which port is being matched to. The USB hub driver now
associates the "port" node with the usb_port device, so here we can
use the remote "port" node to check for a match. Then fall back to
the remote device node for the other connection types.

Also rewrite the existing "remote == dev_of_node(dev)" with
device_match_of_node() for consistency.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v4:
- Dropped blank line between __free() variable declaration and
  subsequent use of the variable in conditional

Changes since v3:
- Drop redundant device node validity check; device_match_of_node()
  does it internally

Changes since v2:
- Use device_match_of_node()
---
 drivers/power/sequencing/pwrseq-pcie-m2.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c
index 83fe6a1396bc..691cad0a6dd0 100644
--- a/drivers/power/sequencing/pwrseq-pcie-m2.c
+++ b/drivers/power/sequencing/pwrseq-pcie-m2.c
@@ -175,9 +175,16 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq,
 	 * parent matches the OF node of 'dev'.
 	 */
 	for_each_endpoint_of_node(ctx->of_node, endpoint) {
+		/* USB port devices are tied to the port nodes. */
+		struct device_node *remote_port __free(device_node) =
+				of_graph_get_remote_port(endpoint);
+		if (device_match_of_node(dev, remote_port))
+			return PWRSEQ_MATCH_OK;
+
+		/* Try the remote port parent for other types. */
 		struct device_node *remote __free(device_node) =
 				of_graph_get_remote_port_parent(endpoint);
-		if (remote && (remote == dev_of_node(dev)))
+		if (device_match_of_node(dev, remote))
 			return PWRSEQ_MATCH_OK;
 	}
 
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 11/16] dt-bindings: usb: mediatek,mtk-xhci: Switch to ports for USB connections
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern, Krzysztof Kozlowski,
	Bartosz Golaszewski
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

This reverts commit 454a1e3cd36c113341d7b71e8e691c6e47ab4a8a.

MediaTek's XHCI implementation supports both USB 2.0 High Speed (HS)
and USB 3.x Super Speed (SS). The block can also be synthesized with
either HS-only capability or HS+SS capability. The SSUSB controller
handles the device or gadget mode. Saying that SSUSB handles the HS
portion is wrong.

For example, on the MT8195, the first two instances support both HS and
SS, while the latter two instances support only HS.

Switch to a "ports" sub-node for describing USB connections. Port 1 is
Super Speed if the controller is SS-capable, otherwise it is High Speed.
Port 2 is High Speed if SS-capable. This port mapping scheme directly
matches what the hardware returns in its capability registers.

Fixes: 454a1e3cd36c ("dt-bindings: usb: mediatek,mtk-xhci: Add port for SuperSpeed EP")
Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v1:
- Squashed DT binding revert and addition together
- Dropped reviewed-by from Bartosz
---
 .../bindings/usb/mediatek,mtk-xhci.yaml         | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml
index 231e6f35a986..d6c75bd20b78 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml
@@ -107,10 +107,6 @@ properties:
       - description: USB3/SS(P) PHY
       - description: USB2/HS PHY
 
-  port:
-    $ref: /schemas/graph.yaml#/properties/port
-    description: Super Speed (SS) Output endpoint to a Type-C connector
-
   vusb33-supply:
     description: Regulator of USB AVDD3.3v
 
@@ -188,6 +184,19 @@ properties:
   "#size-cells":
     const: 0
 
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Super Speed (SS) data bus if SS-capable;
+          otherwise High Speed (HS) data bus.
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: High Speed (HS) data bus if controller is SS-capable.
+
 patternProperties:
   "@[0-9a-f]{1}$":
     type: object
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 09/16] usb: hub: Use usb_hub_set_port_power() to control port power everywhere
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern, Bartosz Golaszewski
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

There are still some instances in the USB hub driver where port power is
directly controlled by toggling the USB_PORT_FEAT_POWER feature flag.

Switch these instances over to usb_hub_set_port_power() so that only one
unified function to do this exists. This makes adding external power
control with the power sequencing API easier and consistently applied.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v2:
- New patch
---
 drivers/usb/core/hub.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 6671b5e45191..a5c5038e1604 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -976,11 +976,8 @@ static void hub_power_on(struct usb_hub *hub, bool do_delay)
 		dev_dbg(hub->intfdev, "trying to enable port power on "
 				"non-switchable hub\n");
 	for (port1 = 1; port1 <= hub->hdev->maxchild; port1++)
-		if (test_bit(port1, hub->power_bits))
-			set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
-		else
-			usb_clear_port_feature(hub->hdev, port1,
-						USB_PORT_FEAT_POWER);
+		usb_hub_set_port_power(hub->hdev, hub, port1,
+				       test_bit(port1, hub->power_bits));
 	if (do_delay)
 		msleep(hub_power_on_good_delay(hub));
 }
@@ -5437,7 +5434,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 		if (hub_is_port_power_switchable(hub)
 				&& !usb_port_is_power_on(port_dev, portstatus)
 				&& !port_dev->port_owner)
-			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
+			usb_hub_set_port_power(hdev, hub, port1, true);
 
 		if (portstatus & USB_PORT_STAT_ENABLE)
 			goto done;
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 10/16] usb: hub: Power on connected M.2 E-key connectors with power sequencing API
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern, Bartosz Golaszewski
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

The new M.2 E-key connector can have a USB connection. For the USB device
on this connector to work, its power must be enabled and the W_DISABLE2#
signal deasserted. The connector driver handles this and provides a
toggle over the power sequencing API.

This feature currently only supports a directly connected (no mux in
between) M.2 E-key connector. Existing USB connector types are not
covered. The USB A connector was recently added to the onboard devices
driver. USB B connectors have historically been managed by the USB
gadget or dual-role device controller drivers. USB C connectors are
handled by TCPM drivers.

The power sequencing API does not know whether a power sequence provider
is not needed or not available yet, so we only request it for connectors
that we know need it, which at this time is just the E-key connector.

On the USB side, the port firmware node (if present) is tied to the
usb_port device. This device is used to acquire the power sequencing
descriptor. This allows the provider to tell the different ports on one
hub apart.

This feature is not implemented in the onboard USB devices driver. The
power sequencing API expects the consumer device to make the request,
but there is no device node to instantiate a platform device to tie
the driver to. The connector is not a child node of the USB host or
hub, and the graph connection is from a USB port to the connector.
And the connector itself already has a driver.

Power sequencing is not directly enabled in the connector driver as
that would completely decouple the timing of it from the USB subsystem.
It would not be possible for the USB subsystem to toggle the power
for a power cycle or to disable the port.

Sashiko mentions possible use-after-free of hub->ports from the sysfs
callbacks. This is actually not possible, since the sysfs callbacks
acquire the hub device and its lock, and then check if it is in the
process of disconnect / removal. If it is, then the callbacks just
error out.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v5:
- Only assign port_dev->pwrseq if successfully retrieved pwrseq
  descriptor (Andy)
- Dropped the pwrseq error pointer check in the release function (Andy)
- Added check for port->pwrseq != NULL before calling
  pwrseq_is_power_on() (API change in patch 3)

Changes since v4:
- Rewrote usb_port_is_power_on() to better express intent and
  restrictions of pwrseq API (Andy)
- Switched to dev_fwnode() in port_pwrseq_is_supported() (Andy)
- Added blank line separating normal variable declarations and __free()
  type declarations (Andy)
- Split out assign_bit() rewrite (Andy)
- Moved pwrseq_put() to release function to avoid UAF (Sashiko)
- Added back pwrseq_power_off() call in usb_hub_remove_port_device();
  otherwise power off could be delayed to object release
- Don't clear hub->ports[port1 - 1] in main error path;
  by that time the port device is registered and sysfs attributes are
  available to userspace (Sashiko)

Changes since v3:
- Adapted to move of usb_port_is_power_on() to port.c and port.h
- Simplified usb_hub_set_port_pwrseq() (Andy)
- Renamed usb_hub_set_port_pwrseq()'s "set" parameter to "on"
- Dropped usb_hub_restore_port_pwrseq() (use usb_hub_set_port_pwrseq()
  with inverted argument)
- Fixed off-by-one access in hub_is_port_power_switchable() (Sashiko)
- Assign retval from dev_err_probe() instead of the other way around (Andy)
- Clear hub->ports[port1 - 1] in USB port error and remove paths to
  avoid other threads from accidental UAF while the USB hub device is
  being unwound (Sashiko)
- Short-circuit out of helpers if !IS_ENABLED(CONFIG_POWER_SEQUENCING)
  to avoid errors from stub functions (Sashiko)

Changes since v2:
- Expanded subject to mention power sequencing API
- Dropped commit message bit about power sequencing Kconfig symbol change
  to bool
- Added optional dependency on POWER_SEQUENCING to USB
- Split out pwrseq_power_*() calls into separate helpers
- Rewrote set_bit() and clear_bit() branches with assign_bit()
- Dropped the pwrseq_power_off() before pwrseq_put(): pwrseq_put() does it
  automatically.
- Removed pwrseq_power_on() from usb_hub_create_port_device(); it will
  get called through usb_hub_set_port_power() in hub_activate().
- Added checks for port->pwrseq in hub_is_port_power_switchable()

- Use separate pwrseq descriptors for HighSpeed and SuperSpeed ports.
  This makes things simpler. On the other hand to power cycle a port
  userspace needs to toggle it on both the HS and SS ports together.
- Dropped pwrseq state tracking again
  The power sequencing consumer API already tracks the state internally;
  doing it again in |struct usb_port| is not necessary especially now
  that the descriptors aren't shared.

It's unclear to me how actual hubs reconcile USB_PORT_FEAT_POWER settings
from the HS side and SS side. One hub chip vendor said that VBUS_EN for
a port is on if the flag is set on either side; however actually testing
on one of their hubs showed that VBUS was cut as soon as the flag is
cleared on the HS port. Maybe it could be different if a SS device was
connected? That scenario was not tested. Testing on another retail
bought hub seemed to work exactly as described though: USB_PORT_FEAT_POWER
needed to be clear on both HS and SS ports to turn off VBUS.

Under this scheme, I'm not sure how the power cycle in hub_port_connect()
would work correctly.

- Link to v2:
  https://lore.kernel.org/all/20260610084053.2059858-1-wenst@chromium.org/

Changes since v1:
- Switch to fwnode instead of OF
- Tie port@ fwnode to usb_port device
- Move remote node compatible checking to separate helper
- Use usb_port device to request power sequencing descriptor
- Drop "index" parameter from pwrseq_get()
- Do not get pwrseq descriptor for SuperSpeed port; share one for one
  physical port
- Add pwrseq state tracking
- Link to v1:
  https://lore.kernel.org/all/20260515090149.3169406-1-wenst@chromium.org/
---
 drivers/usb/Kconfig     |  1 +
 drivers/usb/core/hub.c  | 20 ++++++++++++++-
 drivers/usb/core/hub.h  |  8 +++++-
 drivers/usb/core/port.c | 57 +++++++++++++++++++++++++++++++++++++++++
 drivers/usb/core/port.h |  2 ++
 5 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index abf8c6cdea9e..ef1959363fb1 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -44,6 +44,7 @@ config USB_ARCH_HAS_HCD
 config USB
 	tristate "Support for Host-side USB"
 	depends on USB_ARCH_HAS_HCD
+	depends on POWER_SEQUENCING if POWER_SEQUENCING
 	select GENERIC_ALLOCATOR
 	select USB_COMMON
 	select NLS  # for UTF-8 strings
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a5c5038e1604..35e035cfeb4d 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -33,6 +33,7 @@
 #include <linux/mutex.h>
 #include <linux/random.h>
 #include <linux/pm_qos.h>
+#include <linux/pwrseq/consumer.h>
 #include <linux/kobject.h>
 
 #include <linux/bitfield.h>
@@ -875,6 +876,16 @@ static void hub_tt_work(struct work_struct *work)
 	spin_unlock_irqrestore(&hub->tt.lock, flags);
 }
 
+static int usb_hub_set_port_pwrseq(struct usb_port *port, bool on)
+{
+	if (!IS_ENABLED(CONFIG_POWER_SEQUENCING))
+		return 0;
+
+	if (on)
+		return pwrseq_power_on(port->pwrseq);
+	return pwrseq_power_off(port->pwrseq);
+}
+
 /**
  * usb_hub_set_port_power - control hub port's power state
  * @hdev: USB device belonging to the usb hub
@@ -890,15 +901,22 @@ static void hub_tt_work(struct work_struct *work)
 int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
 			   int port1, bool set)
 {
+	struct usb_port *pwrseq_port = hub->ports[port1 - 1];
 	int ret;
 
+	ret = usb_hub_set_port_pwrseq(pwrseq_port, set);
+	if (ret)
+		return ret;
+
 	if (set)
 		ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 	else
 		ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
-	if (ret)
+	if (ret) {
+		usb_hub_set_port_pwrseq(pwrseq_port, !set);
 		return ret;
+	}
 
 	assign_bit(port1, hub->power_bits, set);
 	return 0;
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index de524c6da9fc..7277c38c9e62 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -103,7 +103,13 @@ static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
 	if (!hub)
 		return false;
 	hcs = hub->descriptor->wHubCharacteristics;
-	return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
+	if ((le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM)
+		return true;
+	/* check for controllable external power sequencers */
+	for (unsigned int i = 0; i < hub->hdev->maxchild; i++)
+		if (hub->ports[i] && hub->ports[i]->pwrseq)
+			return true;
+	return false;
 }
 
 static inline int hub_is_superspeed(struct usb_device *hdev)
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 8d686d43e996..38da7cc24c63 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -8,11 +8,14 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/cleanup.h>
 #include <linux/kstrtox.h>
 #include <linux/slab.h>
 #include <linux/string_choices.h>
 #include <linux/sysfs.h>
 #include <linux/pm_qos.h>
+#include <linux/property.h>
+#include <linux/pwrseq/consumer.h>
 #include <linux/component.h>
 #include <linux/usb/of.h>
 
@@ -35,6 +38,10 @@ int usb_port_is_power_on(struct usb_port *port, unsigned int portstatus)
 			ret = 1;
 	}
 
+	/* stub functions return error */
+	if (IS_ENABLED(CONFIG_POWER_SEQUENCING) && port->pwrseq)
+		return ret && pwrseq_power_is_on(port->pwrseq);
+
 	return ret;
 }
 
@@ -45,6 +52,9 @@ static bool usb_port_allow_power_off(struct usb_device *hdev,
 	if (hub_is_port_power_switchable(hub))
 		return true;
 
+	if (port_dev->pwrseq)
+		return true;
+
 	if (!IS_ENABLED(CONFIG_ACPI))
 		return false;
 
@@ -380,6 +390,8 @@ static void usb_port_device_release(struct device *dev)
 	 * device_platform_notify_remove() in device_del().
 	 */
 	fwnode_handle_put(dev_fwnode(dev));
+	/* usb_hub_create_port_device() could leave an error value */
+	pwrseq_put(port_dev->pwrseq);
 	kfree(port_dev->req);
 	kfree(port_dev);
 }
@@ -772,11 +784,46 @@ static const struct component_ops connector_ops = {
 	.unbind = connector_unbind,
 };
 
+static bool port_pwrseq_is_supported(struct usb_port *port_dev)
+{
+	struct device *dev = &port_dev->dev;
+	struct fwnode_handle *port = dev_fwnode(dev);
+
+	struct fwnode_handle *ep __free(fwnode_handle) =
+			fwnode_graph_get_next_port_endpoint(port, NULL);
+	if (!ep)
+		return false;
+
+	struct fwnode_handle *remote __free(fwnode_handle) =
+			fwnode_graph_get_remote_port_parent(ep);
+	if (!remote)
+		return false;
+
+	if (!fwnode_device_is_compatible(remote, "pcie-m2-e-connector")) {
+		dev_dbg(dev, "remote endpoint %pfw is not a supported connector", remote);
+		return false;
+	}
+
+	return true;
+}
+
+static struct pwrseq_desc *usb_hub_port_pwrseq_get(struct usb_port *port_dev)
+{
+	if (!IS_ENABLED(CONFIG_POWER_SEQUENCING))
+		return NULL;
+
+	if (!port_pwrseq_is_supported(port_dev))
+		return NULL;
+
+	return pwrseq_get(&port_dev->dev, "usb");
+}
+
 int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 {
 	struct usb_port *port_dev;
 	struct usb_device *hdev = hub->hdev;
 	struct fwnode_handle *fwnode = dev_fwnode(&hdev->dev);
+	struct pwrseq_desc *pwrseq;
 	int retval;
 
 	port_dev = kzalloc_obj(*port_dev);
@@ -827,6 +874,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 	retval = device_register(&port_dev->dev);
 	if (retval) {
 		put_device(&port_dev->dev);
+		hub->ports[port1 - 1] = NULL;
 		return retval;
 	}
 
@@ -844,6 +892,14 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 		goto err_put_kn;
 	}
 
+	pwrseq = usb_hub_port_pwrseq_get(port_dev);
+	if (IS_ERR(pwrseq)) {
+		retval = dev_err_probe(&port_dev->dev, PTR_ERR(pwrseq),
+				       "failed to get power sequencing descriptor\n");
+		goto err_put_kn;
+	}
+	port_dev->pwrseq = pwrseq;
+
 	retval = component_add(&port_dev->dev, &connector_ops);
 	if (retval) {
 		dev_warn(&port_dev->dev, "failed to add component\n");
@@ -901,6 +957,7 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
 	peer = port_dev->peer;
 	if (peer)
 		unlink_peers(port_dev, peer);
+	pwrseq_power_off(port_dev->pwrseq);
 	component_del(&port_dev->dev, &connector_ops);
 	sysfs_put(port_dev->state_kn);
 	device_unregister(&port_dev->dev);
diff --git a/drivers/usb/core/port.h b/drivers/usb/core/port.h
index 2f4349b3ce6b..088a182332d4 100644
--- a/drivers/usb/core/port.h
+++ b/drivers/usb/core/port.h
@@ -25,6 +25,7 @@
  * @port_owner: port's owner
  * @peer: related usb2 and usb3 ports (share the same connector)
  * @connector: USB Type-C connector
+ * @pwrseq: power sequencing descriptor for the port
  * @req: default pm qos request for hubs without port power control
  * @connect_type: port's connect type
  * @state: device state of the usb device attached to the port
@@ -44,6 +45,7 @@ struct usb_port {
 	struct usb_dev_state *port_owner;
 	struct usb_port *peer;
 	struct typec_connector *connector;
+	struct pwrseq_desc *pwrseq;
 	struct dev_pm_qos_request *req;
 	enum usb_port_connect_type connect_type;
 	enum usb_device_state state;
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 08/16] usb: hub: Pass |struct usb_port*| to usb_port_is_power_on()
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern, Bartosz Golaszewski
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

usb_port_is_power_on() currently takes |struct usb_hub*|, but only needs
it to tell if the hub/port is SuperSpeed or not.

In a subsequent change, usb_port_is_power_on() needs access to a pwrseq
state tracking field in |struct usb_port|. Either structure can be used
to identify whether a port/hub is SuperSpeed or not, as the field in
|struct usb_port| is inherited from the hub:

    port->is_superspeed = hub_is_superspeed(hub)

Replace usb_port_is_power_on()'s |struct usb_hub*| parameter with
|struct usb_port*| so a subsequent change can use it.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v3:
- Adapted to move of usb_port_is_power_on() to port.c and port.h
---
 drivers/usb/core/hub.c  | 7 ++++---
 drivers/usb/core/port.c | 6 +++---
 drivers/usb/core/port.h | 4 +---
 3 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a6c4e4c0368a..6671b5e45191 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3291,7 +3291,7 @@ static int check_port_resume_type(struct usb_device *udev,
 	}
 	/* Is the device still present? */
 	else if (status || port_is_suspended(hub, portstatus) ||
-			!usb_port_is_power_on(hub, portstatus)) {
+			!usb_port_is_power_on(port_dev, portstatus)) {
 		if (status >= 0)
 			status = -ENODEV;
 	} else if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
@@ -3733,12 +3733,13 @@ static int wait_for_connected(struct usb_device *udev,
 		struct usb_hub *hub, int port1,
 		u16 *portchange, u16 *portstatus)
 {
+	struct usb_port *port_dev = hub->ports[port1 - 1];
 	int status = 0, delay_ms = 0;
 
 	while (delay_ms < 2000) {
 		if (status || *portstatus & USB_PORT_STAT_CONNECTION)
 			break;
-		if (!usb_port_is_power_on(hub, *portstatus)) {
+		if (!usb_port_is_power_on(port_dev, *portstatus)) {
 			status = -ENODEV;
 			break;
 		}
@@ -5434,7 +5435,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 		 * but only if the port isn't owned by someone else.
 		 */
 		if (hub_is_port_power_switchable(hub)
-				&& !usb_port_is_power_on(hub, portstatus)
+				&& !usb_port_is_power_on(port_dev, portstatus)
 				&& !port_dev->port_owner)
 			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index b2e953f137f8..8d686d43e996 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -23,11 +23,11 @@ static int usb_port_block_power_off;
 static const struct attribute_group *port_dev_group[];
 
 /* Check if a port is power on */
-int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus)
+int usb_port_is_power_on(struct usb_port *port, unsigned int portstatus)
 {
 	int ret = 0;
 
-	if (hub_is_superspeed(hub->hdev)) {
+	if (port->is_superspeed) {
 		if (portstatus & USB_SS_PORT_STAT_POWER)
 			ret = 1;
 	} else {
@@ -114,7 +114,7 @@ static ssize_t disable_show(struct device *dev,
 	}
 
 	usb_hub_port_status(hub, port1, &portstatus, &unused);
-	disabled = !usb_port_is_power_on(hub, portstatus);
+	disabled = !usb_port_is_power_on(port_dev, portstatus);
 
  out_hdev_lock:
 	usb_unlock_device(hdev);
diff --git a/drivers/usb/core/port.h b/drivers/usb/core/port.h
index 00f7500af336..2f4349b3ce6b 100644
--- a/drivers/usb/core/port.h
+++ b/drivers/usb/core/port.h
@@ -63,6 +63,4 @@ struct usb_port {
 #define to_usb_port(_dev) \
 	container_of(_dev, struct usb_port, dev)
 
-struct usb_hub;
-
-int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus);
+int usb_port_is_power_on(struct usb_port *port, unsigned int portstatus);
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 07/16] usb: core: Move struct usb_port and related APIs to port.h
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern, Bartosz Golaszewski
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

|struct usb_port| and its matching container_of() macro should live in
its own port.h, matching the split we have for .c files.

Move them as described. Also move usb_port_is_power_on(), since in the
next change its |struct usb_hub *| parameter will be changed to
|struct usb_port *|, and becomes a non-static function that only
references |struct usb_port|.

port.h is only included from hub.h, as a subsequent patch will directly
use fields from |struct usb_port| in a static inline helper in hub.h.
The USB internal headers don't have header guards to help with this.

Also drop 'extern' from the header declaration of usb_port_is_power_on().
This is not needed in modern C.

Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v4:
- Dropped 'extern' (Andy)

Changes since v3:
- New patch (Andy)
---
 drivers/usb/core/hub.c  | 16 ----------
 drivers/usb/core/hub.h  | 48 ++---------------------------
 drivers/usb/core/port.c | 16 ++++++++++
 drivers/usb/core/port.h | 68 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+), 62 deletions(-)
 create mode 100644 drivers/usb/core/port.h

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 8f8624a0d36a..a6c4e4c0368a 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3240,22 +3240,6 @@ static bool hub_port_stop_enumerate(struct usb_hub *hub, int port1, int retries)
 	return port_dev->ignore_event;
 }
 
-/* Check if a port is power on */
-int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus)
-{
-	int ret = 0;
-
-	if (hub_is_superspeed(hub->hdev)) {
-		if (portstatus & USB_SS_PORT_STAT_POWER)
-			ret = 1;
-	} else {
-		if (portstatus & USB_PORT_STAT_POWER)
-			ret = 1;
-	}
-
-	return ret;
-}
-
 static void usb_lock_port(struct usb_port *port_dev)
 		__acquires(&port_dev->status_lock)
 {
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 9ebc5ef54a32..de524c6da9fc 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -15,7 +15,9 @@
 #include <linux/usb/ch11.h>
 #include <linux/usb/hcd.h>
 #include <linux/usb/typec.h>
+
 #include "usb.h"
+#include "port.h"
 
 struct usb_hub {
 	struct device		*intfdev;	/* the "interface" device */
@@ -78,51 +80,6 @@ struct usb_hub {
 	struct list_head        onboard_devs;
 };
 
-/**
- * struct usb port - kernel's representation of a usb port
- * @child: usb device attached to the port
- * @dev: generic device interface
- * @port_owner: port's owner
- * @peer: related usb2 and usb3 ports (share the same connector)
- * @connector: USB Type-C connector
- * @req: default pm qos request for hubs without port power control
- * @connect_type: port's connect type
- * @state: device state of the usb device attached to the port
- * @state_kn: kernfs_node of the sysfs attribute that accesses @state
- * @location: opaque representation of platform connector location
- * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
- * @portnum: port index num based one
- * @is_superspeed cache super-speed status
- * @usb3_lpm_u1_permit: whether USB3 U1 LPM is permitted.
- * @usb3_lpm_u2_permit: whether USB3 U2 LPM is permitted.
- * @early_stop: whether port initialization will be stopped earlier.
- * @ignore_event: whether events of the port are ignored.
- */
-struct usb_port {
-	struct usb_device *child;
-	struct device dev;
-	struct usb_dev_state *port_owner;
-	struct usb_port *peer;
-	struct typec_connector *connector;
-	struct dev_pm_qos_request *req;
-	enum usb_port_connect_type connect_type;
-	enum usb_device_state state;
-	struct kernfs_node *state_kn;
-	usb_port_location_t location;
-	struct mutex status_lock;
-	u32 over_current_count;
-	u8 portnum;
-	u32 quirks;
-	unsigned int early_stop:1;
-	unsigned int ignore_event:1;
-	unsigned int is_superspeed:1;
-	unsigned int usb3_lpm_u1_permit:1;
-	unsigned int usb3_lpm_u2_permit:1;
-};
-
-#define to_usb_port(_dev) \
-	container_of(_dev, struct usb_port, dev)
-
 extern int usb_hub_create_port_device(struct usb_hub *hub,
 		int port1);
 extern void usb_hub_remove_port_device(struct usb_hub *hub,
@@ -138,7 +95,6 @@ extern int usb_clear_port_feature(struct usb_device *hdev,
 		int port1, int feature);
 extern int usb_hub_port_status(struct usb_hub *hub, int port1,
 		u16 *status, u16 *change);
-extern int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus);
 
 static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
 {
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index c0a51d9b06a6..b2e953f137f8 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -22,6 +22,22 @@ static int usb_port_block_power_off;
 
 static const struct attribute_group *port_dev_group[];
 
+/* Check if a port is power on */
+int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus)
+{
+	int ret = 0;
+
+	if (hub_is_superspeed(hub->hdev)) {
+		if (portstatus & USB_SS_PORT_STAT_POWER)
+			ret = 1;
+	} else {
+		if (portstatus & USB_PORT_STAT_POWER)
+			ret = 1;
+	}
+
+	return ret;
+}
+
 static bool usb_port_allow_power_off(struct usb_device *hdev,
 				     struct usb_hub *hub,
 				     struct usb_port *port_dev)
diff --git a/drivers/usb/core/port.h b/drivers/usb/core/port.h
new file mode 100644
index 000000000000..00f7500af336
--- /dev/null
+++ b/drivers/usb/core/port.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * usb hub driver head file
+ *
+ * Copyright (C) 1999 Linus Torvalds
+ * Copyright (C) 1999 Johannes Erdfelt
+ * Copyright (C) 1999 Gregory P. Smith
+ * Copyright (C) 2001 Brad Hards (bhards@bigpond.net.au)
+ * Copyright (C) 2012 Intel Corp (tianyu.lan@intel.com)
+ *
+ *  move struct usb_port to this file.
+ */
+
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/mutex_types.h>
+#include <linux/usb.h>
+
+#include <uapi/linux/usb/ch9.h>
+
+/**
+ * struct usb port - kernel's representation of a usb port
+ * @child: usb device attached to the port
+ * @dev: generic device interface
+ * @port_owner: port's owner
+ * @peer: related usb2 and usb3 ports (share the same connector)
+ * @connector: USB Type-C connector
+ * @req: default pm qos request for hubs without port power control
+ * @connect_type: port's connect type
+ * @state: device state of the usb device attached to the port
+ * @state_kn: kernfs_node of the sysfs attribute that accesses @state
+ * @location: opaque representation of platform connector location
+ * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
+ * @portnum: port index num based one
+ * @is_superspeed cache super-speed status
+ * @usb3_lpm_u1_permit: whether USB3 U1 LPM is permitted.
+ * @usb3_lpm_u2_permit: whether USB3 U2 LPM is permitted.
+ * @early_stop: whether port initialization will be stopped earlier.
+ * @ignore_event: whether events of the port are ignored.
+ */
+struct usb_port {
+	struct usb_device *child;
+	struct device dev;
+	struct usb_dev_state *port_owner;
+	struct usb_port *peer;
+	struct typec_connector *connector;
+	struct dev_pm_qos_request *req;
+	enum usb_port_connect_type connect_type;
+	enum usb_device_state state;
+	struct kernfs_node *state_kn;
+	usb_port_location_t location;
+	struct mutex status_lock;
+	u32 over_current_count;
+	u8 portnum;
+	u32 quirks;
+	unsigned int early_stop:1;
+	unsigned int ignore_event:1;
+	unsigned int is_superspeed:1;
+	unsigned int usb3_lpm_u1_permit:1;
+	unsigned int usb3_lpm_u2_permit:1;
+};
+
+#define to_usb_port(_dev) \
+	container_of(_dev, struct usb_port, dev)
+
+struct usb_hub;
+
+int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus);
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [PATCH v6 06/16] usb: hub: Associate port@ fwnode with USB port device
From: Chen-Yu Tsai @ 2026-07-21  6:54 UTC (permalink / raw)
  To: Bartosz Golaszewski, Greg Kroah-Hartman, Andy Shevchenko,
	Daniel Scally, Heikki Krogerus, Sakari Ailus, Rafael J. Wysocki,
	Danilo Krummrich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: Chen-Yu Tsai, linux-acpi, driver-core, linux-pm, linux-usb,
	devicetree, linux-mediatek, linux-arm-kernel, linux-kernel,
	Manivannan Sadhasivam, Alan Stern, Bartosz Golaszewski
In-Reply-To: <20260721065413.2306137-1-wenst@chromium.org>

When a USB hub port is connected to a connector in a firmware node
graph, the port itself has a node in the graph.

Associate the port's firmware node with the USB port's device,
usb_port::dev. This is used in later changes for the M.2 slot power
sequencing provider to match against the requesting port.

To avoid potential conflicts with ACPI firmware nodes and then causing
power management issues, only assign the firmware node if the hub's
firmware node is not an ACPI firmware node.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v5:
- Added extra |struct fwnode_handle *| local variables to shorten lines
  (Andy)
- Added comment about passing fwnode_graph_get_port_by_id() return value
  directly to device_set_node() (Andy)

Changes since v4:
- Dropped unused |hdev| variable
- Added flags for fwnode_graph_get_port_by_id() with
  FWNODE_GRAPH_DEVICE_DISABLED so that even incomplete
  ports can be connected

Changes since v3:
- Added missing fwnode_handle_put()

Changes since v2:
- Skip assignment if hub firmware node is ACPI node
---
 drivers/usb/core/port.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index b4452b665f59..c0a51d9b06a6 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -7,6 +7,7 @@
  * Author: Lan Tianyu <tianyu.lan@intel.com>
  */
 
+#include <linux/acpi.h>
 #include <linux/kstrtox.h>
 #include <linux/slab.h>
 #include <linux/string_choices.h>
@@ -358,6 +359,11 @@ static void usb_port_device_release(struct device *dev)
 {
 	struct usb_port *port_dev = to_usb_port(dev);
 
+	/*
+	 * At this point ACPI nodes and swnodes have been removed by
+	 * device_platform_notify_remove() in device_del().
+	 */
+	fwnode_handle_put(dev_fwnode(dev));
 	kfree(port_dev->req);
 	kfree(port_dev);
 }
@@ -754,6 +760,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 {
 	struct usb_port *port_dev;
 	struct usb_device *hdev = hub->hdev;
+	struct fwnode_handle *fwnode = dev_fwnode(&hdev->dev);
 	int retval;
 
 	port_dev = kzalloc_obj(*port_dev);
@@ -782,6 +789,24 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 	port_dev->dev.driver = &usb_port_driver;
 	dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),
 			port1);
+
+	/*
+	 * ACPI FW nodes are associated later when device_register() happens.
+	 * Skip assigning one here to avoid potential conflicts.
+	 */
+	if (!is_acpi_node(fwnode)) {
+		struct fwnode_handle *port;
+
+		/*
+		 * fwnode_graph_get_port_by_id() returns either a valid fwnode handle
+		 * or NULL. Passing NULL to device_set_node() clears any associated
+		 * fwnode. It is effectively a no-op here, since no fwnode has been
+		 * assigned to the newly created device yet.
+		 */
+		port = fwnode_graph_get_port_by_id(fwnode, port1, FWNODE_GRAPH_DEVICE_DISABLED);
+		device_set_node(&port_dev->dev, port);
+	}
+
 	mutex_init(&port_dev->status_lock);
 	retval = device_register(&port_dev->dev);
 	if (retval) {
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related


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