Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v3 2/2] clk: amlogic: Add A9 peripherals clock controller driver
From: Jian Hu @ 2026-06-15 11:25 UTC (permalink / raw)
  To: Jerome Brunet, Jian Hu via B4 Relay
  Cc: Neil Armstrong, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Xianwei Zhao, Kevin Hilman,
	Martin Blumenstingl, linux-amlogic, linux-clk, devicetree,
	linux-kernel, linux-arm-kernel
In-Reply-To: <1jecieftme.fsf@starbuckisacylon.baylibre.com>


On 6/10/2026 8:49 PM, Jerome Brunet wrote:
> [ EXTERNAL EMAIL ]
>
> On mer. 10 juin 2026 at 16:14, Jian Hu via B4 Relay <devnull+jian.hu.amlogic.com@kernel.org> wrote:
>
>> From: Jian Hu <jian.hu@amlogic.com>
>>
>> Add the peripherals clock controller driver for the Amlogic A9 SoC family.
>>
>> Signed-off-by: Jian Hu <jian.hu@amlogic.com>
>> ---
>>   drivers/clk/meson/Kconfig          |   15 +
>>   drivers/clk/meson/Makefile         |    1 +
>>   drivers/clk/meson/a9-peripherals.c | 1925 ++++++++++++++++++++++++++++++++++++
>>   3 files changed, 1941 insertions(+)
>>

[ ... ]

>> +
>> +/* Channel 6 is unconnected. */
>> +static u32 a9_glb_parents_val_table[] = { 0, 1, 2, 3, 4, 5, 7 };
>> +static struct clk_regmap a9_dspa;
> What is this ?


The peripheral clock definitions are ordered by register offset.

dspa is one of the parents of the glb clock, while the dsp clock 
registers are located after the GLB clock registers.

Since glb references a9_dspa before its full definition appears, the 
declaration

static struct clk_regmap a9_dspa;

is added as a forward declaration to satisfy the compiler.


Would it make sense to relax the register-offset ordering in this case?

By defining the DSP clock before the GLB clock, we could remove the 
forward declaration of a9_dspa.

>> +
>> +static const struct clk_parent_data a9_glb_parents[] = {
>> +     { .fw_name = "xtal", },
>> +     { .hw = &a9_dspa.hw },
>> +     { .fw_name = "fdiv3", },
>> +     { .fw_name = "fdiv4", },
>> +     { .fw_name = "fdiv5", },
>> +     { .hw = &a9_isp.hw },
>> +     { .fw_name = "rtc", }
>> +};
>> +
>> +static A9_COMP_SEL(glb, GLB_CLK_CTRL, 9, 0x7, a9_glb_parents,
>> +                a9_glb_parents_val_table);
>> +static A9_COMP_DIV(glb, GLB_CLK_CTRL, 0, 7);
>> +static A9_COMP_GATE(glb, GLB_CLK_CTRL, 8, 0);
>> +
>> +static struct clk_regmap a9_usb_48m_dualdiv_in = {
>> +     .data = &(struct clk_regmap_gate_data) {
>> +             .offset = USB_CLK_CTRL,
>> +             .bit_idx = 31,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("usb_48m_dualdiv_in", &a9_usb_48m_pre.hw,
>> +                               &clk_regmap_gate_ops, 0),
> Same comment as on the AO controller


Ok, I will drop CLK_HW_INIT*, and apply the same change to the other 
clock definitions.


[ ... ]

>> +
>> +/* Channel 3 is unconnected. */
> You meant 3rd I guess but this is misleading and confusing with the
> table bellow. Channel 2 would be more appropriate I think, since those
> are 0-based.


You are right,  The 3rd channel is unconnected.  I will fix it in the 
next version.

>> +static u32 a9_can_pe_parents_val_table[] = { 0, 1, 3 };
>> +static const struct clk_parent_data a9_can_pe_parents[] = {
>> +     { .fw_name = "sys", },
>> +     { .fw_name = "xtal", },
>> +     { .fw_name = "fdiv5", }
>> +};
>> +


[ ... ]

>> +
>> +/*
>> + * Channel 3(ddr_dpll_pt_clk) is manged by the DDR module;
>> + * channel 12(msr_clk) is manged by clock measures module.
>> + * channel 16(audio_dac1_clk) is manged by audio module.
> Some why can't you expose those then ? gen clk is used for debugging
> AFAIK. The clock above are worth debugging I think
>
> Please be consistent with the CaSing.


For channel 3 (ddr_dpll_pt_clk), it is sourced from the DDR PLL clock 
controller rather than
this clock controller. I will expose it in the DT.

For channel 12 (msr_clk), it depends on the clock measurement 
configuration.
The measurement source must first be selected through freq_ctrl[20:26], and
only then does msr_clk become meaningful. In practice.
It is not a real clock but an output of the measurement logic, so it can 
not be exposed as a clock.

https://elixir.bootlin.com/linux/v7.1-rc7/source/drivers/soc/amlogic/meson-clk-measure.c#L809

For channel 16 (audio_dac1_clk), I confirmed with the clock hardware 
designer that
it is actually unconnected. It looks like there is an error in the 
documentation.

I'll also fix the casing to keep it consistent throughout the comments.
>> + * Channel 10, 11, 13, 14 are not connected.
>> + */
>> +static u32 a9_gen_parents_val_table[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 15, 17, 18,
>> +                                       19, 20, 21, 22, 23, 24, 25, 26};
>> +static struct clk_regmap a9_vid_pll;
>> +
>> +static const struct clk_parent_data a9_gen_parents[] = {
>> +     { .fw_name = "xtal" },
>> +     { .fw_name = "rtc" },
>> +     { .fw_name = "sysplldiv16" },
>> +     { .hw = &a9_vid_pll.hw },
>> +     { .fw_name = "gp0" },
>> +     { .fw_name = "hifi1" },
>> +     { .fw_name = "hifi0" },
>> +     { .fw_name = "gp1" },
>> +     { .fw_name = "gp2" },
>> +     { .fw_name = "dsudiv16" },
>> +     { .fw_name = "cpudiv16" },
>> +     { .fw_name = "a78div16" },
>> +     { .fw_name = "fdiv2" },
>> +     { .fw_name = "fdiv2p5" },
>> +     { .fw_name = "fdiv3" },
>> +     { .fw_name = "fdiv4" },
>> +     { .fw_name = "fdiv5" },
>> +     { .fw_name = "fdiv7" },
>> +     { .fw_name = "mclk0" },
>> +     { .fw_name = "mclk1" }
>> +};
>> +
>> +static A9_COMP_SEL(gen, GEN_CLK_CTRL, 12, 0x1f, a9_gen_parents,
>> +                a9_gen_parents_val_table);
>> +static A9_COMP_DIV(gen, GEN_CLK_CTRL, 0, 11);
>> +static A9_COMP_GATE(gen, GEN_CLK_CTRL, 11, 0);
>> +


[ ... ]

>> +
>> +static struct clk_regmap a9_enc, a9_enc1;
> What is this again ?? and please come up with better names.


Same as the previous dspa clock declaration.

enc stands for encoder. I'll rename a9_enc to a9_encoder0 and a9_enc1 to 
a9_encoder1 in the next version.

>> +
>> +static const struct clk_parent_data a9_vid_lock_parents[] = {
>> +     { .fw_name = "xtal", },
>> +     { .hw = &a9_enc.hw },
>> +     { .hw = &a9_enc1.hw }
>> +};
>> +
>> +static A9_COMP_SEL(vid_lock, VID_LOCK_CLK_CTRL, 9, 0x7, a9_vid_lock_parents,
>> +                NULL);
>> +static A9_COMP_DIV(vid_lock, VID_LOCK_CLK_CTRL, 0, 7);
>> +static A9_COMP_GATE(vid_lock, VID_LOCK_CLK_CTRL, 8, 0);
>> +
>> +static const struct clk_parent_data a9_vdin_meas_parents[] = {
>> +     { .fw_name = "xtal", },
>> +     { .fw_name = "fdiv4", },
>> +     { .fw_name = "fdiv3", },
>> +     { .fw_name = "fdiv5", }
>> +};
>> +
>> +static A9_COMP_SEL(vdin_meas, VDIN_MEAS_CLK_CTRL, 9, 0x7, a9_vdin_meas_parents,
>> +                NULL);
>> +static A9_COMP_DIV(vdin_meas, VDIN_MEAS_CLK_CTRL, 0, 7);
>> +static A9_COMP_GATE(vdin_meas, VDIN_MEAS_CLK_CTRL, 8, 0);
>> +
>> +static struct clk_regmap a9_vid_pll_div = {
>> +     .data = &(struct meson_vid_pll_div_data){
>> +             .val = {
>> +                     .reg_off = VID_PLL_CLK_DIV,
>> +                     .shift   = 0,
>> +                     .width   = 15,
>> +             },
>> +             .sel = {
>> +                     .reg_off = VID_PLL_CLK_DIV,
>> +                     .shift   = 16,
>> +                     .width   = 2,
>> +             },
>> +     },
>> +     .hw.init = CLK_HW_INIT_FW_NAME("vid_pll_div", "hdmiout2",
>> +                                    &meson_vid_pll_div_ro_ops, 0),
>> +};
>> +
>> +static struct clk_regmap a9_vid_pll_sel = {
>> +     .data = &(struct clk_regmap_mux_data){
>> +             .offset = VID_PLL_CLK_DIV,
>> +             .mask = 0x1,
>> +             .shift = 18,
>> +     },
>> +     .hw.init = CLK_HW_INIT_PARENTS_DATA("vid_pll_sel",
>> +                     ((const struct clk_parent_data []) {
>> +                             { .hw = &a9_vid_pll_div.hw },
>> +                             { .fw_name = "hdmiout2" }
>> +                     }), &clk_regmap_mux_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vid_pll = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_PLL_CLK_DIV,
>> +             .bit_idx = 19,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vid_pll", &a9_vid_pll_sel.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vid_pll_vclk = {
>> +     .data = &(struct clk_regmap_mux_data){
>> +             .offset = HDMI_CLK_CTRL,
>> +             .mask = 0x1,
>> +             .shift = 15,
>> +     },
>> +     .hw.init = CLK_HW_INIT_PARENTS_DATA("vid_pll_vclk",
>> +                     ((const struct clk_parent_data []) {
>> +                             { .hw = &a9_vid_pll.hw },
>> +                             { .fw_name = "hdmipix" }
>> +                     }), &clk_regmap_mux_ops, 0),
>> +};
>> +
>> +static const struct clk_parent_data a9_vclk_parents[] = {
>> +     { .hw = &a9_vid_pll_vclk.hw },
>> +     { .fw_name = "pix0", },
>> +     { .fw_name = "vid1", },
>> +     { .fw_name = "pix1", },
>> +     { .fw_name = "fdiv3", },
>> +     { .fw_name = "fdiv4", },
>> +     { .fw_name = "fdiv5", },
>> +     { .fw_name = "vid2", }
>> +};
>> +
>> +static struct clk_regmap a9_vclk_sel = {
>> +     .data = &(struct clk_regmap_mux_data){
>> +             .offset = VID_CLK_CTRL,
>> +             .mask = 0x7,
>> +             .shift = 16,
>> +     },
>> +     .hw.init = CLK_HW_INIT_PARENTS_DATA("vclk_sel", a9_vclk_parents,
>> +                     &clk_regmap_mux_ops, 0),
>> +};
>> +
>> +static struct clk_regmap a9_vclk_in = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_DIV,
>> +             .bit_idx = 16,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk_in", &a9_vclk_sel.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk_div = {
>> +     .data = &(struct clk_regmap_div_data){
>> +             .offset = VID_CLK_DIV,
>> +             .shift = 0,
>> +             .width = 8,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div", &a9_vclk_in.hw,
>> +                               &clk_regmap_divider_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_CTRL,
>> +             .bit_idx = 19,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk", &a9_vclk_div.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk_div1_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_CTRL,
>> +             .bit_idx = 0,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div1_en", &a9_vclk.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk_div2_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_CTRL,
>> +             .bit_idx = 1,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div2_en", &a9_vclk.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
> Looks to me all this div_en / div repeating pattern would be easier to review
> with tiny macro .


Good point.

I tried to reduce the repeated div_en/div pattern using a helper macro.

It keeps the relationship between gate and fixed-factor clock more 
compact and easier to review.

After using the helper macro, the div_en/div code can be simplified to 
the following:

#define A9_VCLK(_name, _reg, _bit, _div, _parent)        \
struct clk_regmap a9_##_name##_en = {      \
         .data = &(struct clk_regmap_gate_data){          \
                 .offset = _reg,      \
                 .bit_idx = _bit,       \
         },       \
         .hw.init = &(struct clk_init_data) {           \
                 .name = #_name "_en",      \
                 .ops = &clk_regmap_gate_ops,           \
                 .parent_hws = (const struct clk_hw *[]) { _parent },    \
                 .num_parents = 1,      \
                 .flags = CLK_SET_RATE_PARENT,      \
         },       \
};       \
       \
struct clk_fixed_factor a9_##_name = {       \
         .mult = 1,       \
         .div = _div,       \
         .hw.init = &(struct clk_init_data){          \
                 .name = #_name,      \
                 .ops = &clk_fixed_factor_ops,          \
                 .parent_hws = (const struct clk_hw *[]) {      \
                         &a9_##_name##_en.hw          \
                 },       \
                 .num_parents = 1,      \
                 .flags = CLK_SET_RATE_PARENT,      \
         },       \
};       \

static A9_VCLK(vclk_div2, VID_CLK_CTRL, 1, 2, &a9_vclk.hw);
static A9_VCLK(vclk_div4, VID_CLK_CTRL, 2, 4, &a9_vclk.hw);
static A9_VCLK(vclk_div6, VID_CLK_CTRL, 3, 6, &a9_vclk.hw);
static A9_VCLK(vclk_div6, VID_CLK_CTRL, 4, 12, &a9_vclk.hw);
static A9_VCLK(vclk2_div2, VIID_CLK_CTRL, 1, 2, &a9_vclk2.hw);
static A9_VCLK(vclk2_div4, VIID_CLK_CTRL, 2, 4, &a9_vclk2.hw);
static A9_VCLK(vclk2_div6, VIID_CLK_CTRL, 3, 6, &a9_vclk2.hw);
static A9_VCLK(vclk2_div6, VIID_CLK_CTRL, 4, 12, &a9_vclk2.hw);


If you think splitting it further into separate helper macros would 
improve readability.

I can do that as well.

>> +
>> +static struct clk_fixed_factor a9_vclk_div2 = {
>> +     .mult = 1,
>> +     .div = 2,
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div2", &a9_vclk_div2_en.hw,
>> +                               &clk_fixed_factor_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk_div4_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_CTRL,
>> +             .bit_idx = 2,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div4_en", &a9_vclk.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_fixed_factor a9_vclk_div4 = {
>> +     .mult = 1,
>> +     .div = 4,
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div4", &a9_vclk_div4_en.hw,
>> +                               &clk_fixed_factor_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk_div6_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_CTRL,
>> +             .bit_idx = 3,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div6_en", &a9_vclk.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_fixed_factor a9_vclk_div6 = {
>> +     .mult = 1,
>> +     .div = 6,
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div6", &a9_vclk_div6_en.hw,
>> +                               &clk_fixed_factor_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk_div12_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_CTRL,
>> +             .bit_idx = 4,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div12_en", &a9_vclk.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_fixed_factor a9_vclk_div12 = {
>> +     .mult = 1,
>> +     .div = 12,
>> +     .hw.init = CLK_HW_INIT_HW("vclk_div12", &a9_vclk_div12_en.hw,
>> +                               &clk_fixed_factor_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2_sel = {
>> +     .data = &(struct clk_regmap_mux_data){
>> +             .offset = VIID_CLK_CTRL,
>> +             .mask = 0x7,
>> +             .shift = 16,
>> +     },
>> +     .hw.init = CLK_HW_INIT_PARENTS_DATA("vclk2_sel", a9_vclk_parents,
>> +                     &clk_regmap_mux_ops, 0),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2_in = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VIID_CLK_DIV,
>> +             .bit_idx = 16,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_in", &a9_vclk2_sel.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2_div = {
>> +     .data = &(struct clk_regmap_div_data){
>> +             .offset = VIID_CLK_DIV,
>> +             .shift = 0,
>> +             .width = 8,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div", &a9_vclk2_in.hw,
>> +                               &clk_regmap_divider_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2 = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VIID_CLK_CTRL,
>> +             .bit_idx = 19,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk2", &a9_vclk2_div.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2_div1_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VIID_CLK_CTRL,
>> +             .bit_idx = 0,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div1_en", &a9_vclk2.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2_div2_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VIID_CLK_CTRL,
>> +             .bit_idx = 1,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div2_en", &a9_vclk2.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_fixed_factor a9_vclk2_div2 = {
>> +     .mult = 1,
>> +     .div = 2,
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div2", &a9_vclk2_div2_en.hw,
>> +                               &clk_fixed_factor_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2_div4_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VIID_CLK_CTRL,
>> +             .bit_idx = 2,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div4_en", &a9_vclk2.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_fixed_factor a9_vclk2_div4 = {
>> +     .mult = 1,
>> +     .div = 4,
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div4", &a9_vclk2_div4_en.hw,
>> +                               &clk_fixed_factor_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2_div6_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VIID_CLK_CTRL,
>> +             .bit_idx = 3,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div6_en", &a9_vclk2.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_fixed_factor a9_vclk2_div6 = {
>> +     .mult = 1,
>> +     .div = 6,
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div6", &a9_vclk2_div6_en.hw,
>> +                               &clk_fixed_factor_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_vclk2_div12_en = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VIID_CLK_CTRL,
>> +             .bit_idx = 4,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div12_en", &a9_vclk2.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_fixed_factor a9_vclk2_div12 = {
>> +     .mult = 1,
>> +     .div = 12,
>> +     .hw.init = CLK_HW_INIT_HW("vclk2_div12", &a9_vclk2_div12_en.hw,
>> +                               &clk_fixed_factor_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +/* Channel 5, 6 and 7 are unconnected */
>> +static u32 a9_vid_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
>> +static const struct clk_hw *a9_vid_parents[] = {
>> +     &a9_vclk_div1_en.hw,
>> +     &a9_vclk_div2.hw,
>> +     &a9_vclk_div4.hw,
>> +     &a9_vclk_div6.hw,
>> +     &a9_vclk_div12.hw,
>> +     &a9_vclk2_div1_en.hw,
>> +     &a9_vclk2_div2.hw,
>> +     &a9_vclk2_div4.hw,
>> +     &a9_vclk2_div6.hw,
>> +     &a9_vclk2_div12.hw
>> +};
>> +
>> +static struct clk_regmap a9_vdac_sel = {
>> +     .data = &(struct clk_regmap_mux_data){
>> +             .offset = VIID_CLK_DIV,
>> +             .mask = 0xf,
>> +             .shift = 28,
>> +             .table = a9_vid_parents_val_table,
>> +     },
>> +     .hw.init = CLK_HW_INIT_PARENTS_HW("vdac_sel", a9_vid_parents
>> +                     , &clk_regmap_mux_ops, 0),
>> +};
>> +
>> +static struct clk_regmap a9_vdac = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_CTRL2,
>> +             .bit_idx = 4,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("vdac", &a9_vdac_sel.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +
>> +static struct clk_regmap a9_enc_sel = {
> Should this be enc0 then ? for consistency ?
> Same applies to similar instance (it is the same discussion we already
> had on the T7 I believe)


Yes, that makes sense.

I'll rename them to a9_encoder0 and a9_encoder1 for consistency, and 
I'll check for other similar instances as well.

>> +     .data = &(struct clk_regmap_mux_data){
>> +             .offset = VIID_CLK_DIV,
>> +             .mask = 0xf,
>> +             .shift = 12,
>> +             .table = a9_vid_parents_val_table,
>> +     },
>> +     .hw.init = CLK_HW_INIT_PARENTS_HW("enc_sel", a9_vid_parents
>> +                     , &clk_regmap_mux_ops, 0),
>> +};
>> +
>> +static struct clk_regmap a9_enc = {
>> +     .data = &(struct clk_regmap_gate_data){
>> +             .offset = VID_CLK_CTRL2,
>> +             .bit_idx = 10,
>> +     },
>> +     .hw.init = CLK_HW_INIT_HW("enc", &a9_enc_sel.hw,
>> +                               &clk_regmap_gate_ops, CLK_SET_RATE_PARENT),
>> +};
>> +


[ ... ]

> --
> Jerome


--

Jian



^ permalink raw reply

* Re: [PATCH v2] arm64: tlbflush: Don't broadcast if mm was only active on local cpu
From: Ryan Roberts @ 2026-06-15 11:21 UTC (permalink / raw)
  To: Will Deacon, Linu Cherian
  Cc: Catalin Marinas, Kevin Brodsky, Anshuman Khandual, Yang Shi,
	Mark Rutland, Huang Ying, linux-arm-kernel, linux-kernel
In-Reply-To: <ai6KzFgfMAxqplcr@willie-the-truck>

On 14/06/2026 12:04, Will Deacon wrote:
> On Sat, May 23, 2026 at 07:17:10PM +0530, Linu Cherian wrote:
>> From: Ryan Roberts <ryan.roberts@arm.com>
>>
>> There are 3 variants of tlb flush that invalidate user mappings:
>> flush_tlb_mm(), flush_tlb_page() and __flush_tlb_range(). All of these
>> would previously unconditionally broadcast their tlbis to all cpus in
>> the inner shareable domain.
>>
>> But this is a waste of effort if we can prove that the mm for which we
>> are flushing the mappings has only ever been active on the local cpu. In
>> that case, it is safe to avoid the broadcast and simply invalidate the
>> current cpu.
>>
>> So let's track in mm_context_t::active_cpu either the mm has never been
>> active on any cpu, has been active on more than 1 cpu, or has been
>> active on precisely 1 cpu - and in that case, which one. We update this
>> when switching context, being careful to ensure that it gets updated
>> *before* installing the mm's pgtables. On the reader side, we ensure we
>> read *after* the previous write(s) to the pgtable(s) that necessitated
>> the tlb flush have completed. This guarrantees that if a cpu that is
>> doing a tlb flush sees it's own id in active_cpu, then the old pgtable
>> entry cannot have been seen by any other cpu and we can flush only the
>> local cpu.
>>
>> Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
>> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
>> Tested-by: Huang Ying <ying.huang@linux.alibaba.com>
>> [linu.cherian@arm.com: Adapted for v7.1 flush tlb API changes]
>> Signed-off-by: Linu Cherian <linu.cherian@arm.com>
>> ---
>> Changelog from RFC v1:
>> - Adapted for v7.1 flush tlb API changes
>>   No changes in core logic
>> - Collected Rb and Tb tags
>> - lat_mmap benchmark showed dsb(ishst) performs better than dsb(ish),
>>   hence retained dsb(ishst) in flush_tlb_user_pre	
>>
>>
>> Testing with 7.1-rc4 :
>> +-----------------------+---------------------------------------------------+-------------+
>> | Benchmark             | Result Class                                      |  Improvement|  
>> +=======================+===================================================+=============+
>> | perf/syscall          | fork (ops/sec)                                    |   (I) 3.25% |
>> +-----------------------+---------------------------------------------------+-------------+
>> | pts/memtier-benchmark | Protocol: Redis Clients: 100 Ratio: 1:5 (Ops/sec) |   (I) 2.70% |
>> | 			| Protocol: Redis Clients: 100 Ratio: 5:1 (Ops/sec) |   (I) 2.13% |
>> +-----------------------+---------------------------------------------------+-------------+
> 
> I think we need a much more comprehensive set of benchmarks before we can
> begin to consider a change like this.

I believe that Linu ran a wider set of benchmarks and didn't find any
regressions. These are just the ones that show improvement (Linu, please correct
me and/or provide details).

Additionally Huang Ying did some testing against the RFC and reported 4.5%
improvement with Redis:

https://lore.kernel.org/linux-arm-kernel/87segumv6w.fsf@DESKTOP-5N7EMDA/

> 
>>  arch/arm64/include/asm/mmu.h         |  12 +++
>>  arch/arm64/include/asm/mmu_context.h |   2 +
>>  arch/arm64/include/asm/tlbflush.h    | 127 +++++++++++++++++++++------
>>  arch/arm64/mm/context.c              |  30 ++++++-
>>  4 files changed, 141 insertions(+), 30 deletions(-)
> 
> Doesn't this break BTM/SVM with the SMMU? I think that's a non-starter
> even if you can provide some more compelling numbers.

AIUI, we don't support BTM upstream - the SMMU uses private ASIDs and implements
MMU notifiers to forward the TLBIs via its command queue interface.

I was also under the impression that supporting BTM upsteam was not desired;
Please correct me if that's not accurate or if you're aware of plans to add
support. I've been (coincidentlly) looking at some other stuff that could
benefit from BTM but had concluded it wouldn't be an acceptable approach upstream.

If we did ever want to add SMMU BTM support though, I think it would be simple
enough to add an interface to allow the SMMU to disable the optimization (i.e.
force active_cpu to ACTIVE_CPU_MULTIPLE)?

> 
>> +static inline bool flush_tlb_user_pre(struct mm_struct *mm, tlbf_t flags)
>> +{
>> +	unsigned int self, active;
>> +	bool local;
>> +
>> +	migrate_disable();
>> +
>> +	if (flags & TLBF_NOBROADCAST) {
>> +		dsb(nshst);
>> +		return true;
>> +	}
> 
> Why does the NOBROADCAST case need migration disabled? It didn't before...

The existing semantic for TLBF_BOBROADCAST is that it emits a local TLBI on
whatever CPU we happen to be executing on. It's used for lazily fixing up
spurious faults (i.e. hitting RO TLB entries when the PTE has been relaxed to
RW). So it's still functionally correct if the thread migrates CPU between
taking the fault and issuing the local TLBI - in the worst case it just leads to
another spurious fault.

For this new case, we need to ensure we don't get migrated between reading
active_cpu and issuing the local TLBI, otherwise we would only issue a local
TLBI when a broadcast was required.

> 
>> +
>> +	self = smp_processor_id();
>> +
>> +	/*
>> +	 * The load of mm->context.active_cpu must not be reordered before the
>> +	 * store to the pgtable that necessitated this flush. This ensures that
>> +	 * if the value read is our cpu id, then no other cpu can have seen the
>> +	 * old pgtable value and therefore does not need this old value to be
>> +	 * flushed from its tlb. But we don't want to upgrade the dsb(ishst),
>> +	 * needed to make the pgtable updates visible to the walker, to a
>> +	 * dsb(ish) by default. So speculatively load without a barrier and if
>> +	 * it indicates our cpu id, then upgrade the barrier and re-load.
>> +	 */
>> +	active = READ_ONCE(mm->context.active_cpu);
>> +	if (active == self) {
>> +		dsb(ish);
>> +		active = READ_ONCE(mm->context.active_cpu);
>> +	} else {
>> +		dsb(ishst);
>> +	}
> 
> Why can't you just do:
> 
> 	dsb(ishst);
> 	active = READ_ONCE(mm->context.active_cpu);
> 
> ?

Prior to this optimization, we always issued a dsb(ishst) here. Catalin
suggested the same simplification against the RFC. I believe Linu tried it but
saw regressions; Hopefully Linu can provide the details.

> 
>> +
>> +	local = active == self;
>> +	if (!local)
>> +		migrate_enable();
>> +
>> +	return local;
>> +}
>> +
>> +static inline void flush_tlb_user_post(bool local)
>> +{
>> +	if (local)
>> +		migrate_enable();
>> +}
> 
> I was under the impression that disabling/enabling migration was an
> expensive thing to do, so I'd really want to see some more numbers to
> justify this (including from inside a VM) and allow us to consider the
> trade-offs properly. It's also not at all clear to me that it's safe
> from such a low-level TLB invalidation helper.

I had assumed it wasn't very expensive, but perhaps I'm wrong. I know
preempt_enable() can be expensive because it has to test to see if it needs to
reschedule. But I assumed for disabling/enabling migration, it would just be a
counter and the scheduler would check that it's zero before considing moving the
task to another run queue. (But I have practically zero understanding of the
scheduler so I'll assume I'm wrong...).

Instead of disabling migration, perhaps we could re-check active_cpu after
issuing the local tlbi - if it's now reporting "multiple" we must have been
migrated and we need to upgrade to a braodcast TLBI?

> 
>> +
>>  /*
>>   *	TLB Invalidation
>>   *	================
>> @@ -408,12 +482,20 @@ static inline void flush_tlb_all(void)
>>  static inline void flush_tlb_mm(struct mm_struct *mm)
>>  {
>>  	unsigned long asid;
>> +	bool local;
>>  
>> -	dsb(ishst);
>> +	local = flush_tlb_user_pre(mm, TLBF_NONE);
>>  	asid = __TLBI_VADDR(0, ASID(mm));
>> -	__tlbi(aside1is, asid);
>> -	__tlbi_user(aside1is, asid);
>> -	__tlbi_sync_s1ish(mm);
>> +	if (local) {
>> +		__tlbi(aside1, asid);
>> +		__tlbi_user(aside1, asid);
>> +		dsb(nsh);
>> +	} else {
>> +		__tlbi(aside1is, asid);
>> +		__tlbi_user(aside1is, asid);
>> +		__tlbi_sync_s1ish(mm);
>> +	}
>> +	flush_tlb_user_post(local);
> 
> I think you've changed this since Ryan's original patch, but why are you
> only calling __tlbi_sync_s1ish() for the !local case? Doesn't that break
> the erratum workaround when running as a VM if the vCPU is migrated?

Hmm. So from the guest kernel's perspective, it has concluded that it only needs
to target the local (v)CPU. Since the errata only affect boardcast TLBIs, it
concludes there is no need to issue the workarounds. But since it's a VM, the HW
will upgrade the local TLBIs to broadcast TLBIs, but will not magically
re-instate the workarounds. I guess the simplest solution would be to disable
the optimization when either workaround is enabled.

Perhaps this is all getting a bit too complex for not enough benefit...

Thanks,
Ryan

> 
>> diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
>> index 0f4a28b87469..f34ed78393e0 100644
>> --- a/arch/arm64/mm/context.c
>> +++ b/arch/arm64/mm/context.c
>> @@ -214,9 +214,10 @@ static u64 new_context(struct mm_struct *mm)
>>  
>>  void check_and_switch_context(struct mm_struct *mm)
>>  {
>> -	unsigned long flags;
>> -	unsigned int cpu;
>> +	unsigned int cpu = smp_processor_id();
>>  	u64 asid, old_active_asid;
>> +	unsigned int active;
>> +	unsigned long flags;
>>  
>>  	if (system_supports_cnp())
>>  		cpu_set_reserved_ttbr0();
>> @@ -251,7 +252,6 @@ void check_and_switch_context(struct mm_struct *mm)
>>  		atomic64_set(&mm->context.id, asid);
>>  	}
>>  
>> -	cpu = smp_processor_id();
>>  	if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending))
>>  		local_flush_tlb_all();
>>  
>> @@ -262,6 +262,30 @@ void check_and_switch_context(struct mm_struct *mm)
>>  
>>  	arm64_apply_bp_hardening();
>>  
>> +	/*
>> +	 * Update mm->context.active_cpu in such a manner that we avoid cmpxchg
>> +	 * and dsb unless we definitely need it. If initially ACTIVE_CPU_NONE
>> +	 * then we are the first cpu to run so set it to our id. If initially
>> +	 * any id other than ours, we are the second cpu to run so set it to
>> +	 * ACTIVE_CPU_MULTIPLE. If we update the value then we must issue
>> +	 * dsb(ishst) to ensure stores to mm->context.active_cpu are ordered
>> +	 * against the TTBR0 write in cpu_switch_mm()/uaccess_enable(); the
>> +	 * store must be visible to another cpu before this cpu could have
>> +	 * populated any TLB entries based on the pgtables that will be
>> +	 * installed.
>> +	 */
>> +	active = READ_ONCE(mm->context.active_cpu);
>> +	if (active != cpu && active != ACTIVE_CPU_MULTIPLE) {
>> +		if (active == ACTIVE_CPU_NONE)
>> +			active = cmpxchg_relaxed(&mm->context.active_cpu,
>> +						 ACTIVE_CPU_NONE, cpu);
>> +
>> +		if (active != ACTIVE_CPU_NONE)
>> +			WRITE_ONCE(mm->context.active_cpu, ACTIVE_CPU_MULTIPLE);
>> +
>> +		dsb(ishst);
>> +	}
>> +
> 
> Can you simplify the 'if' condition here?
> 
> 	if (active == ACTIVE_CPU_NONE) {
> 		if (!try_cmpxchg_relaxed(...))
> 			WRITE_ONCE(...);
> 
> 		dsb(ishst);
> 	}
> 
> (as an aside, maybe we should implement arch_try_cmpxchg{,_relaxed} so
>  we could drop the READ_ONCE() here as well?)
> 
> Will



^ permalink raw reply

* Re: [PATCH v5 09/10] iio: dac: add mcf54415 DAC
From: Angelo Dureghello @ 2026-06-15 11:20 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Angelo Dureghello, Greg Ungerer, Geert Uytterhoeven, Steven King,
	Arnd Bergmann, Maxime Coquelin, Alexandre Torgue, David Lechner,
	Nuno Sá, Andy Shevchenko, Greg Ungerer, linux-m68k,
	linux-kernel, linux-stm32, linux-arm-kernel, linux-iio,
	Michael Turquette, Stephen Boyd, Brian Masney
In-Reply-To: <20260611114800.009d9797@jic23-huawei>

Hi Jonathan,

On Thu, Jun 11, 2026 at 11:48:00AM +0100, Jonathan Cameron wrote:
> On Wed, 10 Jun 2026 22:35:14 +0200
> Angelo Dureghello <adureghello@baylibre.com> wrote:
>
> > From: Angelo Dureghello <adureghello@baylibre.com>
> >
> > Add basic version of mcf54415 DAC driver. DAC is embedded in the SoC and
> > DAC configuration registers are mapped in the internal IO address space.
> >
> > The DAC accepts a 12-bit digital signal and creates a monotonic 12-bit
> > analog output varying from DAC_VREFL to DAC_VREFH. The DAC module
> > consists of a conversion unit, an output amplifier, and the associated
> > digital control blocks. Default register values for DAC_VREFL and DAC_VREFH
> > are respectively 0 and 0xfff, left untouched in this initial version.
> >
> > This initial version of the driver is minimalistic, "output raw" only, to
> > be extended in the future. DMA and external sync are disabled, default mode
> > is high speed, default format is right-justified 12-bit on 16-bit word.
> >
> > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> I'm lazy so didn't check earlier versions but assume the two bits
> of feedback from Sashiko are false positives:
> https://sashiko.dev/#/patchset/20260610-wip-stmark2-dac-v5-0-b76b83366d5c%40baylibre.com
>
> The one about clock underflow if resume fails, and then devm cleanup happens later
> is a bit nasty.
>
> I did a bit of digging and maybe it is better to just leave the clock on?
> The status dev.power.is_suspended is set to false whether or not resume succeeded
> and I believe a following suspend will not take into account that resume failed.
>
> I'm not set up to poke the combinations but it might be worth trying that.
> +CC common clk people who may immediately know what the right answer is.
>

was about testing this, there are no bus faults on read/write of registers
with clock disabled, nor warnings in dmesg on double disable.

Anyway, i see now from arch Kconfig that is not possible to have CONFIG_PM
for this specifig CPU (with MMU, PM is force-disabled), so would remove pm_ops.
Ok ?

Will fix all other things you pointyed out in v6.

> Otherwise just a few minor style comments inline.
>
> Thanks,
>
> Jonathan
>

thanks,
regards,
angelo


^ permalink raw reply

* Re: [PATCH] PCI: meson: Fix PERST# timing by asserting reset before LTSSM enable
From: Ronald Claveau @ 2026-06-15 10:34 UTC (permalink / raw)
  To: Gowtham Kudupudi
  Cc: robh, bhelgaas, neil.armstrong, khilman, jbrunet,
	martin.blumenstingl, linux-pci, linux-amlogic, linux-arm-kernel,
	linux-kernel, yue.wang, lpieralisi, kwilczynski, mani
In-Reply-To: <20260614015620.20432-1-gowtham@ferryfair.com>

On 6/14/26 3:56 AM, Gowtham Kudupudi wrote:
> On warm reboot, the PCIe controller's LTSSM starts link training
> immediately if PERST# is already deasserted from the previous boot.
> The driver then pulses PERST# for only 500us, which is too short to
> properly reset the endpoint device that has already started training.
> 
> Fix by moving the PERST# assert/deassert pulse BEFORE enabling LTSSM,
> so the endpoint gets a clean reset cycle before link training begins.
> 
> This was found on Amlogic G12B (A311D) with NVMe on an M.2 slot.
> Cold boot worked because POR held PERST# low; warm reboot did not.
> The fix was confirmed on a Banana Pi CM4 with Waveshare IO base board.
> 
> Signed-off-by: Gowtham Kudupudi <gowtham@ferryfair.com>
> ---
>  drivers/pci/controller/dwc/pci-meson.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
> index 5f8e2f4b3c12..3a7e9f1d5b8c 100644
> --- a/drivers/pci/controller/dwc/pci-meson.c
> +++ b/drivers/pci/controller/dwc/pci-meson.c
> @@ -310,8 +310,8 @@ static int meson_pcie_start_link(struct dw_pcie *pci)
>  {
>  	struct meson_pcie *mp = to_meson_pcie(pci);
>  
> +	meson_pcie_assert_reset(mp);
>  	meson_pcie_ltssm_enable(mp);
> -	meson_pcie_assert_reset(mp);
>  
>  	return 0;
>  }

Hi Gowtham,

I have a patch [1] that I haven't submitted yet.
This might be related to your issue, what do you think ?

[1] https://github.com/rclaveau-tech/linux-khadas/commit/bee0a02d9756

-- 
Best regards,
Ronald


^ permalink raw reply

* Re: [PATCH] KVM: arm64: Sync SPSR_EL1 when injecting an exception into a pVM
From: Fuad Tabba @ 2026-06-15 10:52 UTC (permalink / raw)
  To: Will Deacon
  Cc: Marc Zyngier, Oliver Upton, linux-arm-kernel, kvmarm,
	linux-kernel, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Sascha Bischoff, Andrew Jones
In-Reply-To: <ai_OevUNS-OACPm7@willie-the-truck>

Hi Will,


On Mon, 15 Jun 2026 at 11:05, Will Deacon <will@kernel.org> wrote:
>
> On Fri, Jun 12, 2026 at 12:34:14PM +0100, Fuad Tabba wrote:
> > When pKVM injects a synchronous exception into a protected guest, it
> > re-enters without restoring the guest's EL1 sysregs and writes the EL1
> > exception registers to hardware by hand: ESR_EL1 and ELR_EL1, but not
> > SPSR_EL1. enter_exception64() sets SPSR_EL1 (the interrupted PSTATE)
> > only in memory, so the guest's handler reads a stale SPSR_EL1 and
> > restores the wrong PSTATE on eret.
> >
> > Write SPSR_EL1 alongside the other exception registers.
> >
> > Fixes: 6c30bfb18d0b ("KVM: arm64: Add handlers for protected VM System Registers")
> > Reported-by: sashiko <sashiko@sashiko.dev>
> > Signed-off-by: Fuad Tabba <tabba@google.com>
> > ---
> >  arch/arm64/kvm/hyp/nvhe/sys_regs.c | 1 +
> >  1 file changed, 1 insertion(+)
> >
> > diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
> > index 8c3fbb413a06..1a7d5cd16d72 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c
> > +++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
> > @@ -268,6 +268,7 @@ static void inject_sync64(struct kvm_vcpu *vcpu, u64 esr)
> >
> >       write_sysreg_el1(esr, SYS_ESR);
> >       write_sysreg_el1(read_sysreg_el2(SYS_ELR), SYS_ELR);
> > +     write_sysreg_el1(read_sysreg_el2(SYS_SPSR), SYS_SPSR);
> >       write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR);
> >       write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR);
> >  }
>
> Is SPSR_EL1 not set in enter_exception64() using vcpu_cpsr(vcpu), which
> *is* set here? I'm just a bit wary of the report, as I'd have expected
> fireworks if we weren't initialising the guest's SPSR on the exception
> injection path.

Yes, enter_exception64() sets SPSR_EL1, but only in memory:
__vcpu_write_spsr() takes the nVHE path and calls
__vcpu_assign_sys_reg(vcpu, SPSR_EL1, val), which writes
vcpu->arch.ctxt.sys_regs[SPSR_EL1] without touching the hardware
register.

In the normal nVHE entry path that memory value reaches hardware via
__sysreg_restore_state_nvhe() before __guest_enter(). But
inject_sync64() runs inside the fixup_guest_exit() loop, which
re-enters the guest directly without a sysreg restore pass. ESR_EL1
and ELR_EL1 are already written to hardware by hand for this reason
but SPSR_EL1 was missed.

I think we don't see fireworks because inject_sync64() only fires on
error paths, e.g., accesses to restricted or undefined sysregs. A
well-behaved guest shouldn't trigger these, so the stale SPSR_EL1
isn't observed.

Cheers,
/fuad

>
> Will


^ permalink raw reply

* Re: [PATCH 05/10] [v2] mips: select legacy gpiolib interfaces where used
From: Thomas Bogendoerfer @ 2026-06-15 10:33 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-gpio, linux-kernel, Arnd Bergmann, Christian Lamparter,
	Johannes Berg, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, John Paul Adrian Glaubitz,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Lee Jones, Pavel Machek, Matti Vaittinen,
	Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	linux-wireless, linux-omap, linux-arm-kernel, linux-mips,
	linux-sh, linux-input, linux-leds, netdev, Bartosz Golaszewski
In-Reply-To: <20260520183815.2510387-6-arnd@kernel.org>

On Wed, May 20, 2026 at 08:38:10PM +0200, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
> 
> A few old machines have not been converted away from the old-style
> gpiolib interfaces. Make these select the new CONFIG_GPIOLIB_LEGACY
> symbol so the code still works where it is needed but can be left
> out otherwise.
> 
> This is the list of all gpio_request() calls in mips:
> 
>   arch/mips/alchemy/devboards/db1000.c:           gpio_request(19, "sd0_cd");
>   arch/mips/alchemy/devboards/db1000.c:           gpio_request(20, "sd1_cd");
>   arch/mips/alchemy/devboards/db1200.c:   gpio_request(215, "otg-vbus");
>   arch/mips/bcm47xx/workarounds.c:        err = gpio_request_one(usb_power, GPIOF_OUT_INIT_HIGH, "usb_power");
>   arch/mips/bcm63xx/boards/board_bcm963xx.c:              gpio_request_one(board.ephy_reset_gpio,
>   arch/mips/txx9/rbtx4927/setup.c:        gpio_request(15, "sio-dtr");
> 
> Most of these should be easy enough to change to modern gpio descriptors
> or remove if they are no longer in use.
> 
> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> v2: no changes. There was no discussion on this, but the patch
>     has so far not made it into the linux-mips tree, so I'm including
>     it for completeness.
> ---
>  arch/mips/Kconfig         | 5 +++++
>  arch/mips/alchemy/Kconfig | 1 -
>  arch/mips/txx9/Kconfig    | 1 +
>  3 files changed, 6 insertions(+), 1 deletion(-)

applied to mips-next

Thomas.

-- 
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
good idea.                                                [ RFC1925, 2.3 ]


^ permalink raw reply

* Re: [PATCH v5 2/3] pwm: rp1: Add RP1 PWM controller driver
From: Julian Braha @ 2026-06-15 10:29 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Andrea della Porta, linux-pwm, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Florian Fainelli,
	Broadcom internal kernel review list, devicetree,
	linux-rpi-kernel, linux-arm-kernel, linux-kernel, Naushir Patuck,
	Stanimir Varbanov, mbrugger
In-Reply-To: <ai-dNlC1_nbQTy5Z@monoceros>

Hi Uwe,

On 6/15/26 07:37, Uwe Kleine-König wrote:
> IMHO selecting REGMAP_MMIO explicitly here is fine because at least to
> me it's not obvious that MFD_SYSCON enforces REGMAP_MMIO.

I think it's better to use comments to document non-obvious behavior,
rather than dead code.
E.g.:
'select MFD_SYSCON # selects REGMAP_MMIO'

But I guess this is not really worth bikeshedding over.

- Julian Braha


^ permalink raw reply

* Re: [PATCH v3 3/3] arm64: escalate smp_send_stop() to an SDEI NMI as a last resort
From: Puranjay Mohan @ 2026-06-15 10:25 UTC (permalink / raw)
  To: Kiryl Shutsemau
  Cc: Catalin Marinas, Will Deacon, James Morse, Mark Rutland,
	Marc Zyngier, Doug Anderson, Petr Mladek, Thomas Gleixner,
	Andrew Morton, Baoquan He, Usama Arif, Breno Leitao,
	Julien Thierry, Lecopzer Chen, Sumit Garg, kernel-team, kexec,
	linux-arm-kernel, linux-kernel, Kiryl Shutsemau (Meta)
In-Reply-To: <167493d30ef6c99a44291de14cddd41ced8149c4.1781490440.git.kas@kernel.org>

On Mon, Jun 15, 2026 at 4:36 AM Kiryl Shutsemau <kirill@shutemov.name> wrote:
>
> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
>
> A CPU wedged with interrupts masked ignores the stop IPI, and without
> pseudo-NMI there is no NMI IPI to escalate to: a reboot proceeds with
> the CPU still running, and a kdump misses its registers.
>
> Add a third rung to smp_send_stop(): once the IPI (and pseudo-NMI IPI,
> if enabled) rungs have run, signal SDEI event 0 at whatever stayed
> online. Firmware delivers it regardless of the target's DAIF, so it
> reaches a CPU a plain IPI cannot; the target acks by going offline,
> which the caller already polls for.
>
> Fold the stop bookkeeping into one arm64_nmi_cpu_stop(regs,
> die_on_crash), shared by the stop IPI handlers, panic_smp_self_stop()
> and the SDEI handler, replacing the near-duplicate local_cpu_stop() and
> ipi_cpu_crash_stop(). @die_on_crash is the only difference: the IPI
> handlers pass true and PSCI CPU_OFF the CPU on a crash stop so a capture
> kernel can reclaim it; the SDEI handler and self-stop pass false and
> park. The SDEI park is required, not conservative -- its handler runs
> inside an SDEI event that is never completed (completing it resumes the
> wedged context), and a CPU_OFF from that unfinished-event context wedges
> EL3 on some firmware (left as a follow-up). The dump is unaffected; only
> re-onlining the CPU in an SMP capture kernel is lost.
>
> Suggested-by: Douglas Anderson <dianders@chromium.org>
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> ---
>  arch/arm64/include/asm/nmi.h    |  24 +++++++
>  arch/arm64/kernel/smp.c         | 109 +++++++++++++++++++++-----------
>  drivers/firmware/Kconfig        |   2 +
>  drivers/firmware/arm_sdei_nmi.c |  75 ++++++++++++++++++++++
>  4 files changed, 172 insertions(+), 38 deletions(-)
>
> diff --git a/arch/arm64/include/asm/nmi.h b/arch/arm64/include/asm/nmi.h
> index 9366be419d18..2e8974ff8d63 100644
> --- a/arch/arm64/include/asm/nmi.h
> +++ b/arch/arm64/include/asm/nmi.h
> @@ -4,21 +4,45 @@
>
>  #include <linux/cpumask.h>
>
> +struct pt_regs;
> +
>  /*
>   * Cross-CPU NMI provider hooks, consulted by the arm64 arch code before
>   * its regular-IRQ / pseudo-NMI IPI paths. The SDEI provider in
>   * drivers/firmware/arm_sdei_nmi.c implements them when active; a future
>   * FEAT_NMI provider could slot in here too. The stubs let callers stay
>   * unconditional when ARM_SDEI_NMI is off.
> + *
> + * sdei_nmi_active() lets a caller test for the service before committing
> + * to (and waiting on) the SDEI stop rung; sdei_nmi_stop_cpus() then signals
> + * the targets, which ack by going offline.
>   */
>  #ifdef CONFIG_ARM_SDEI_NMI
>  bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu);
> +bool sdei_nmi_active(void);
> +void sdei_nmi_stop_cpus(const cpumask_t *mask);
>  #else
>  static inline bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
>                                                       int exclude_cpu)
>  {
>         return false;
>  }
> +
> +static inline bool sdei_nmi_active(void)
> +{
> +       return false;
> +}
> +
> +static inline void sdei_nmi_stop_cpus(const cpumask_t *mask) { }
>  #endif
>
> +/*
> + * The common "stop this CPU" entry every arm64 stop path funnels through:
> + * the regular/pseudo-NMI stop IPI handlers, panic_smp_self_stop(), and the
> + * SDEI cross-CPU NMI handler. @die_on_crash powers the CPU off on the kdump
> + * crash path (IPI handlers) instead of parking it (SDEI / self-stop).
> + * Defined in arch/arm64/kernel/smp.c.
> + */
> +void __noreturn arm64_nmi_cpu_stop(struct pt_regs *regs, bool die_on_crash);
> +
>  #endif /* __ASM_NMI_H */
> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> index a670434a8cae..e85a4ba18d5c 100644
> --- a/arch/arm64/kernel/smp.c
> +++ b/arch/arm64/kernel/smp.c
> @@ -33,6 +33,7 @@
>  #include <linux/kernel_stat.h>
>  #include <linux/kexec.h>
>  #include <linux/kgdb.h>
> +#include <linux/kprobes.h>
>  #include <linux/kvm_host.h>
>  #include <linux/nmi.h>
>
> @@ -862,14 +863,58 @@ void arch_irq_work_raise(void)
>  }
>  #endif
>
> -static void __noreturn local_cpu_stop(unsigned int cpu)
> +/*
> + * Bring the local CPU to a stop, saving its register state into the vmcore
> + * on the kdump crash path first. The single point every arm64 stop path
> + * funnels through, so the bookkeeping (mask interrupts, mark offline, mask
> + * SDEI, optionally power off) lives in one place:
> + *
> + *   - the regular IPI_CPU_STOP and pseudo-NMI IPI_CPU_STOP_NMI handlers;
> + *   - panic_smp_self_stop(), a CPU parking itself on a parallel panic();
> + *   - the SDEI cross-CPU NMI handler (drivers/firmware/arm_sdei_nmi.c),
> + *     which reaches CPUs the stop IPIs could not.
> + *
> + * @regs is the register state to record in the vmcore on a crash stop; NULL
> + * means "capture the current context". @die_on_crash decides the kdump crash
> + * path: the IPI stop handlers pass true and power the CPU off (PSCI CPU_OFF,
> + * via __cpu_try_die()) so a capture kernel can reclaim it. The SDEI handler
> + * and panic_smp_self_stop() pass false and only park. For SDEI that is
> + * required, not just conservative: it runs inside an SDEI event that is
> + * deliberately never completed (completing it has firmware resume the wedged
> + * context), and a CPU_OFF from that not-yet-completed context wedges EL3 on
> + * some firmware -- a documented follow-up. Parking also matches this path's
> + * own fallback when CPU_OFF is unavailable.
> + */
> +void __noreturn arm64_nmi_cpu_stop(struct pt_regs *regs, bool die_on_crash)
>  {
> +       unsigned int cpu = smp_processor_id();
> +       bool crash = IS_ENABLED(CONFIG_KEXEC_CORE) && crash_stop;
> +
> +       /*
> +        * Use local_daif_mask() instead of local_irq_disable() to make sure
> +        * that pseudo-NMIs are disabled. The "stop" code starts with an IRQ
> +        * and falls back to NMI (which might be pseudo). If the IRQ finally
> +        * goes through right as we're timing out then the NMI could interrupt
> +        * us. It's better to prevent the NMI and let the IRQ finish since the
> +        * pt_regs will be better.
> +        */
> +       local_daif_mask();
> +
> +       if (crash)
> +               crash_save_cpu(regs, cpu);
> +
> +       /* the ack a stop requester (e.g. smp_send_stop()) polls for */
>         set_cpu_online(cpu, false);
>
> -       local_daif_mask();
>         sdei_mask_local_cpu();
> +
> +       if (crash && die_on_crash)
> +               __cpu_try_die(cpu);
> +
> +       /* just in case */
>         cpu_park_loop();
>  }
> +NOKPROBE_SYMBOL(arm64_nmi_cpu_stop);
>
>  /*
>   * We need to implement panic_smp_self_stop() for parallel panic() calls, so
> @@ -878,36 +923,7 @@ static void __noreturn local_cpu_stop(unsigned int cpu)
>   */
>  void __noreturn panic_smp_self_stop(void)
>  {
> -       local_cpu_stop(smp_processor_id());
> -}
> -
> -static void __noreturn ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
> -{
> -#ifdef CONFIG_KEXEC_CORE
> -       /*
> -        * Use local_daif_mask() instead of local_irq_disable() to make sure
> -        * that pseudo-NMIs are disabled. The "crash stop" code starts with
> -        * an IRQ and falls back to NMI (which might be pseudo). If the IRQ
> -        * finally goes through right as we're timing out then the NMI could
> -        * interrupt us. It's better to prevent the NMI and let the IRQ
> -        * finish since the pt_regs will be better.
> -        */
> -       local_daif_mask();
> -
> -       crash_save_cpu(regs, cpu);
> -
> -       set_cpu_online(cpu, false);
> -
> -       sdei_mask_local_cpu();
> -
> -       if (IS_ENABLED(CONFIG_HOTPLUG_CPU))
> -               __cpu_try_die(cpu);
> -
> -       /* just in case */
> -       cpu_park_loop();
> -#else
> -       BUG();
> -#endif
> +       arm64_nmi_cpu_stop(NULL, false);
>  }

panic_smp_self_stop() passes regs == NULL. If a second CPU panics
after the primary has already set crash_stop, it loses the panic_cpu
cmpxchg and calls panic_smp_self_stop(); if it was running with
interrupts masked it never took the stop IPI, so it gets here with
crash_stop == 1. crash is then true and we do crash_save_cpu(NULL,
cpu), which ends up in elf_core_copy_regs(), and on arm64 that is just

        *(struct user_pt_regs *)&(dest) = (regs)->user_regs;

so a straight NULL deref -> synchronous abort while we're in the
middle of crashing. The old local_cpu_stop() never called
crash_save_cpu(), so this is a new regression from the unification.

The comment above the function says NULL means "capture the current
context", but crash_save_cpu() doesn't do that, it just dereferences
regs. If that's the intent, materialise it:

        if (crash) {
                struct pt_regs local;

                if (!regs) {
                        crash_setup_regs(&local, NULL);
                        regs = &local;
                }
                crash_save_cpu(regs, cpu);
        }

  crash_setup_regs(..., NULL) is the existing "capture current" helper. Or just
  skip the save when regs is NULL if the self-stop registers aren't
worth having.


^ permalink raw reply

* Re: [PATCH v3 2/3] drivers/firmware: add SDEI cross-CPU NMI service for arm64
From: Puranjay Mohan @ 2026-06-15 10:18 UTC (permalink / raw)
  To: Kiryl Shutsemau
  Cc: Catalin Marinas, Will Deacon, James Morse, Mark Rutland,
	Marc Zyngier, Doug Anderson, Petr Mladek, Thomas Gleixner,
	Andrew Morton, Baoquan He, Usama Arif, Breno Leitao,
	Julien Thierry, Lecopzer Chen, Sumit Garg, kernel-team, kexec,
	linux-arm-kernel, linux-kernel, Kiryl Shutsemau (Meta)
In-Reply-To: <704b467d5b320da9cf49fc9bb4a6814063986f3b.1781490440.git.kas@kernel.org>

On Mon, Jun 15, 2026 at 4:35 AM Kiryl Shutsemau <kirill@shutemov.name> wrote:
>
> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
>
> Deliver an NMI-like event to an interrupt-masked arm64 CPU via the
> standard SDEI software-signalled event (event 0), without the pseudo-NMI
> hot-path cost: register a handler for event 0 and poke a target with
> sdei_event_signal(0, mpidr).
>
> First user is arch_trigger_cpumask_backtrace() (sysrq-l, RCU stalls,
> hung-task/soft-lockup dumps), which otherwise rides an IPI that can't
> reach a masked CPU. Falls back to the IPI path when SDEI is absent; no
> watchdog backend yet, so the stock detector is untouched.
>
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> Reviewed-by: Douglas Anderson <dianders@chromium.org>
> ---
>  MAINTAINERS                     |   2 +-
>  arch/arm64/include/asm/nmi.h    |  24 +++++
>  arch/arm64/kernel/smp.c         |  11 +++
>  drivers/firmware/Kconfig        |  19 ++++
>  drivers/firmware/Makefile       |   1 +
>  drivers/firmware/arm_sdei_nmi.c | 149 ++++++++++++++++++++++++++++++++
>  6 files changed, 205 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm64/include/asm/nmi.h
>  create mode 100644 drivers/firmware/arm_sdei_nmi.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c8d4b913f26c..b5ddfb85dce9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -24797,7 +24797,7 @@ M:      James Morse <james.morse@arm.com>
>  L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
>  S:     Maintained
>  F:     Documentation/devicetree/bindings/arm/firmware/sdei.txt
> -F:     drivers/firmware/arm_sdei.c
> +F:     drivers/firmware/arm_sdei*
>  F:     include/linux/arm_sdei.h
>  F:     include/uapi/linux/arm_sdei.h
>
> diff --git a/arch/arm64/include/asm/nmi.h b/arch/arm64/include/asm/nmi.h
> new file mode 100644
> index 000000000000..9366be419d18
> --- /dev/null
> +++ b/arch/arm64/include/asm/nmi.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __ASM_NMI_H
> +#define __ASM_NMI_H
> +
> +#include <linux/cpumask.h>
> +
> +/*
> + * Cross-CPU NMI provider hooks, consulted by the arm64 arch code before
> + * its regular-IRQ / pseudo-NMI IPI paths. The SDEI provider in
> + * drivers/firmware/arm_sdei_nmi.c implements them when active; a future
> + * FEAT_NMI provider could slot in here too. The stubs let callers stay
> + * unconditional when ARM_SDEI_NMI is off.
> + */
> +#ifdef CONFIG_ARM_SDEI_NMI
> +bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu);
> +#else
> +static inline bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
> +                                                     int exclude_cpu)
> +{
> +       return false;
> +}
> +#endif
> +
> +#endif /* __ASM_NMI_H */
> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> index 1aa324104afb..a670434a8cae 100644
> --- a/arch/arm64/kernel/smp.c
> +++ b/arch/arm64/kernel/smp.c
> @@ -45,6 +45,7 @@
>  #include <asm/daifflags.h>
>  #include <asm/kvm_mmu.h>
>  #include <asm/mmu_context.h>
> +#include <asm/nmi.h>
>  #include <asm/numa.h>
>  #include <asm/processor.h>
>  #include <asm/smp_plat.h>
> @@ -927,6 +928,16 @@ static void arm64_backtrace_ipi(cpumask_t *mask)
>
>  void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
>  {
> +       /*
> +        * Prefer the SDEI cross-CPU NMI provider when active: firmware
> +        * dispatches the event out of EL3 and reaches CPUs that have
> +        * interrupts locally masked, without the per-IRQ-mask cost that
> +        * pseudo-NMI pays for the same reach. The plain IPI path below
> +        * can't reach such a CPU unless pseudo-NMI is enabled.
> +        */
> +       if (sdei_nmi_trigger_cpumask_backtrace(mask, exclude_cpu))
> +               return;
> +
>         /*
>          * NOTE: though nmi_trigger_cpumask_backtrace() has "nmi_" in the name,
>          * nothing about it truly needs to be implemented using an NMI, it's
> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> index bbd2155d8483..6501087ff90d 100644
> --- a/drivers/firmware/Kconfig
> +++ b/drivers/firmware/Kconfig
> @@ -36,6 +36,25 @@ config ARM_SDE_INTERFACE
>           standard for registering callbacks from the platform firmware
>           into the OS. This is typically used to implement RAS notifications.
>
> +config ARM_SDEI_NMI
> +       bool "SDEI-based cross-CPU NMI service (arm64)"
> +       depends on ARM64 && ARM_SDE_INTERFACE
> +       help
> +         Provides SDEI-based cross-CPU NMI delivery for hooks that need
> +         to reach interrupt-masked CPUs on silicon that lacks FEAT_NMI:
> +
> +           - arch_trigger_cpumask_backtrace()  (sysrq-l, RCU stalls,
> +             hardlockup_all_cpu_backtrace, soft-lockup secondary dumps,
> +             hung-task auxiliary dumps)
> +
> +         The driver registers a handler for the SDEI software-signalled
> +         event (event 0) and reaches a target CPU by signalling it with
> +         SDEI_EVENT_SIGNAL. Firmware delivers the event out of EL3
> +         regardless of the target's PSTATE.DAIF -- forced delivery into a
> +         CPU wedged with interrupts locally masked.
> +
> +         If unsure, say N.
> +
>  config EDD
>         tristate "BIOS Enhanced Disk Drive calls determine boot disk"
>         depends on X86
> diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
> index 4ddec2820c96..be46f1e1dc77 100644
> --- a/drivers/firmware/Makefile
> +++ b/drivers/firmware/Makefile
> @@ -4,6 +4,7 @@
>  #
>  obj-$(CONFIG_ARM_SCPI_PROTOCOL)        += arm_scpi.o
>  obj-$(CONFIG_ARM_SDE_INTERFACE)        += arm_sdei.o
> +obj-$(CONFIG_ARM_SDEI_NMI)     += arm_sdei_nmi.o
>  obj-$(CONFIG_DMI)              += dmi_scan.o
>  obj-$(CONFIG_DMI_SYSFS)                += dmi-sysfs.o
>  obj-$(CONFIG_EDD)              += edd.o
> diff --git a/drivers/firmware/arm_sdei_nmi.c b/drivers/firmware/arm_sdei_nmi.c
> new file mode 100644
> index 000000000000..a82776e7b55a
> --- /dev/null
> +++ b/drivers/firmware/arm_sdei_nmi.c
> @@ -0,0 +1,149 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * arm64 SDEI-based cross-CPU NMI service.
> + *
> + * Delivering an "NMI-shaped" event to an EL1 context that has locally
> + * masked interrupts, on silicon without FEAT_NMI, can be done two ways:
> + *
> + *   - pseudo-NMI: mask "interrupts" via the GIC priority register
> + *     (ICC_PMR_EL1) instead of PSTATE.DAIF, leaving a high-priority band
> + *     deliverable. Functionally this works -- but it reimplements every
> + *     local_irq_disable()/enable() and exception entry/exit as a PMR
> + *     write plus synchronisation, a cost paid on that hot path forever,
> + *     whether or not an NMI is ever delivered.
> + *
> + *   - SDEI: leave interrupt masking as the cheap PSTATE.DAIF operation
> + *     and have the firmware bounce an EL3-routed Group-0 SGI back to
> + *     NS-EL1 as an event callback. The cost is a firmware round-trip,
> + *     but only at the rare moment delivery is actually needed.
> + *
> + * This driver takes the second path: it keeps the IRQ-mask hot path
> + * free and pays only when it fires, which is what makes cross-CPU NMI
> + * affordable on hardware where the pseudo-NMI tax isn't, until FEAT_NMI
> + * makes NMI masking cheap in the architecture itself.
> + *
> + * Capabilities provided:
> + *
> + *   - sdei_nmi_trigger_cpumask_backtrace() — override for arm64's
> + *     arch_trigger_cpumask_backtrace(), so sysrq-l, RCU stall dumps,
> + *     hardlockup_all_cpu_backtrace, soft-lockup/hung-task secondary
> + *     dumps all reach interrupt-masked CPUs.
> + *
> + * Delivery uses the standard SDEI software-signalled event (event 0) and
> + * SDEI_EVENT_SIGNAL. We register a handler for event 0, enable it, and
> + * poke a target CPU with sdei_event_signal(0, mpidr): firmware makes
> + * event 0 pending on that PE and dispatches the handler NMI-like,
> + * regardless of the target's DAIF.
> + * Availability is simply whether event 0 registers and enables -- if SDEI
> + * and its software-signalled event are present we use it, otherwise the
> + * driver stays inert.
> + */
> +
> +#define pr_fmt(fmt) "sdei_nmi: " fmt
> +
> +#include <linux/arm_sdei.h>
> +#include <linux/cpumask.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +#include <linux/nmi.h>
> +#include <linux/printk.h>
> +#include <linux/ptrace.h>
> +#include <linux/smp.h>
> +#include <linux/types.h>
> +
> +#include <asm/nmi.h>
> +#include <asm/smp_plat.h>
> +
> +static bool sdei_nmi_available;
> +
> +#define SDEI_NMI_EVENT                 0
> +
> +static int sdei_nmi_handler(u32 event, struct pt_regs *regs, void *arg)
> +{
> +       /*
> +        * nmi_cpu_backtrace() no-ops unless this CPU's bit is set in the
> +        * global backtrace mask (driven by nmi_trigger_cpumask_backtrace()),
> +        * so a fire that reaches a CPU not being backtraced is harmless.
> +        */
> +       nmi_cpu_backtrace(regs);
> +       return SDEI_EV_HANDLED;
> +}
> +NOKPROBE_SYMBOL(sdei_nmi_handler);
> +
> +static void sdei_nmi_fire(unsigned int target_cpu)
> +{
> +       int err = sdei_event_signal(SDEI_NMI_EVENT, cpu_logical_map(target_cpu));
> +
> +       if (err)
> +               pr_warn("SDEI_EVENT_SIGNAL to CPU %u failed: %d\n",
> +                       target_cpu, err);
> +}
> +
> +/*
> + * Raise callback for nmi_trigger_cpumask_backtrace(): signal event 0
> + * at every CPU still pending in @mask. The framework excludes the local
> + * CPU from @mask before calling us.
> + */
> +static void sdei_nmi_raise_backtrace(cpumask_t *mask)
> +{
> +       unsigned int cpu;
> +
> +       for_each_cpu(cpu, mask)
> +               sdei_nmi_fire(cpu);
> +}
> +
> +/*
> + * Override hook for arch_trigger_cpumask_backtrace() (see
> + * arch/arm64/kernel/smp.c). Returns true when SDEI handled the request,
> + * which is the case whenever SDEI is active; on a false return the arch
> + * falls back to its regular-IRQ (or pseudo-NMI, if enabled) IPI.
> + *
> + * On a kernel built without paying the pseudo-NMI hot-path cost (the
> + * usual case for this driver's target), the IPI can't reach a CPU that
> + * has interrupts masked -- so the backtrace of the one CPU you care
> + * about comes back empty. SDEI is dispatched out of EL3 and lands
> + * regardless of the target's DAIF, without taxing the IRQ-mask path.
> + */
> +bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
> +{
> +       if (!sdei_nmi_available)
> +               return false;
> +
> +       nmi_trigger_cpumask_backtrace(mask, exclude_cpu,
> +                                     sdei_nmi_raise_backtrace);
> +       return true;
> +}
> +
> +/*
> + * device_initcall (after arch_initcall(sdei_init), so the SDEI subsystem
> + * is up): probe the firmware, register the event, and turn on the
> + * cross-CPU service. If the probe fails the driver stays inert and the
> + * override hooks decline, leaving the arch's own paths in place.
> + */
> +static int __init sdei_nmi_init(void)
> +{
> +       int err;
> +
> +       err = sdei_event_register(SDEI_NMI_EVENT, sdei_nmi_handler, NULL);
> +       if (err) {
> +               pr_err("sdei_event_register(%u) failed: %d\n",
> +                      SDEI_NMI_EVENT, err);
> +               return 0;
> +       }

This initcall runs unconditionally whenever ARM_SDEI_NMI is built in,
which includes the many arm64 systems that have no SDEI at all. On
those, sdei_event_register() -> sdei_event_create() ->
invoke_sdei_fn() returns -EIO, and the core already complains:
    pr_warn("Failed to create event %u: %d\n", event_num, err);

(that one isn't gated on err != -EIO, unlike sdei_mask_local_cpu() & friends).
We then add a second pr_err() on top, so every boot on a non-SDEI box
with this config gets two alarming lines for what is just "no firmware
support". At minimum, don't shout for -EIO/-EOPNOTSUPP here. Better,
skip the probe when SDEI isn't present  there's no exported predicate
today, but -EIO is the de-facto one.

> +       err = sdei_event_enable(SDEI_NMI_EVENT);
> +       if (err) {
> +               pr_err("sdei_event_enable(%u) failed: %d\n",
> +                      SDEI_NMI_EVENT, err);
> +               sdei_event_unregister(SDEI_NMI_EVENT);
> +               return 0;
> +       }
> +
> +       sdei_nmi_available = true;
> +       pr_info("using SDEI cross-CPU NMI (SDEI_EVENT_SIGNAL, event %u)\n",
> +               SDEI_NMI_EVENT);
> +
> +       return 0;
> +}
> +device_initcall(sdei_nmi_init);
> --
> 2.54.0
>


^ permalink raw reply

* [GIT PULL] arm64 updates for 7.2
From: Will Deacon @ 2026-06-15 10:08 UTC (permalink / raw)
  To: torvalds
  Cc: catalin.marinas, linux-arm-kernel, linux-kernel, kernel-team, maz

Hi Linus,

Please pull these arm64 updates for 7.2. The usual summary is in the
tag, although it feels like the new world of AI tooling has slowed us
down a little on the feature side when compared to the fixes side. The
extra rounds of Sashiko review have also pushed a few things out until
next time.

Still, there's some good foundational stuff here for the fpsimd code and
hardening work towards removing the predictable linear alias of the
kernel image.

There's a small conflict with an upstream fix that landed post -rc1 via
the KVM/arm64 tree (837263307489 ("KVM: arm64: Correctly cap ZCR_EL2
provided by a guest hypervisor")). My resolution is below.

Cheers,

Will

diff --cc arch/arm64/kvm/hyp/include/hyp/switch.h
index e9b36a3b27bb,1f12c4ba295a..000000000000
--- a/arch/arm64/kvm/hyp/include/hyp/switch.h
+++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
@@@ -470,10 -466,9 +470,9 @@@ static inline void __hyp_sve_restore_gu
  	 * The vCPU's saved SVE state layout always matches the max VL of the
  	 * vCPU. Start off with the max VL so we can load the SVE state.
  	 */
 -	sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2);
 +	sve_cond_update_zcr_vq(zcr_el2, SYS_ZCR_EL2);
- 	__sve_restore_state(vcpu_sve_pffr(vcpu),
- 			    &vcpu->arch.ctxt.fp_regs.fpsr,
- 			    true);
+ 	sve_load_state(kern_hyp_va(vcpu->arch.sve_state), true);
+ 	fpsimd_load_common(&vcpu->arch.ctxt.fp_regs);
  
  	/*
  	 * The effective VL for a VM could differ from the max VL when running a

--->8

The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:

  Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git tags/arm64-upstream

for you to fetch changes up to 61c19a9feb1d87156e46e38d7759f3ad23710e24:

  Merge branch 'for-next/sysregs' into for-next/core (2026-06-14 12:18:27 +0100)

----------------------------------------------------------------
arm64 updates for 7.2

CPU errata handling:
- Extend CnP disabling workaround to HiSilicon HIP09 hardware.
- Work around eternally broken broadcast TLB invalidation on more CPUs.
- Documentation and code cleanups.

CPU features:
- Add new hwcaps for the 2025 dpISA extensions.

Floating point / SVE / SME:
- Significant cleanup to the low-level state management code in the core
  architecture code and KVM.
- Use correct register widths during SVE/SME save/restore assembly.
- Expose SVE/SME save/restore memory accesses to sanitisers.

Memory management:
- Preparatory work for unmapping the kernel data and bss sections from
  the linear map.

Miscellaneous:
- Inline DAIF manipulation helpers so they can be used safely from
  non-instrumentable code.
- Fix handling of the 'nosmp' cmdline option to avoid marking secondary
  cores as "possible".

MPAM:
- Add support for v0.1 of the MPAM architecture.

Perf:
- Update HiSilicon PMU MAINTAINERS entry.
- Fix event encodings for the DVM node in the CMN driver.

Selftests:
- Extend sigframe tests to cover POE context.
- Add coverage for the newly added 2025 dpISA hwcaps.

System registers:
- Add new registers and ESR encodings for the HDBSS feature.

Plus minor fixes and cleanups across the board.

----------------------------------------------------------------
Anshuman Khandual (2):
      arm64/mm: Replace BUG_ON() with VM_WARN_ON_ONCE()
      arm64/mm: Rename ptdesc_t

Ard Biesheuvel (20):
      arm64: mm: Remove bogus stop condition from map_mem() loop
      arm64: mm: Drop redundant pgd_t* argument from map_mem()
      arm64: mm: Check for pud_/pmd_set_huge() failures on kernel mappings
      arm64: mm: Preserve existing table mappings when mapping DRAM
      arm64: mm: Preserve non-contiguous descriptors when mapping DRAM
      arm64: mm: Permit contiguous descriptors to be manipulated
      arm64: kfence: Avoid NOMAP tricks when mapping the early pool
      arm64: mm: Permit contiguous attribute for preliminary mappings
      arm64: Move fixmap and kasan page tables to end of kernel image
      arm64: mm: Don't abuse memblock NOMAP to check for overlaps
      powerpc/code-patching: Avoid r/w mapping of the zero page
      sh: Drop cache flush of the zero page at boot
      mm: Make empty_zero_page[] const
      arm64: mm: Map the kernel data/bss read-only in the linear map
      arm64: mm: Unmap kernel data/bss entirely from the linear map
      arm64: Rename page table BSS section to .bss..pgtbl
      kasan: Move generic KASAN page tables out of BSS too
      arm64: Avoid double evaluation of __ptep_get()
      KVM: arm64: Omit tag sync on stage-2 mappings of the zero page
      arm64: mm: Defer remap of linear alias of data/bss

Breno Leitao (1):
      arm64: arch_timer: reuse arch_timer_read_cnt{p,v}ct_el0() helpers

Ethan Nelson-Moore (1):
      ARM64: remove unnecessary architecture-specific <asm/device.h>

Jonathan Cameron (1):
      MAINTAINERS: Update HiSilicon PMU driver maintainer to Yushan Wang

Kevin Brodsky (4):
      selftests/mm: Fix resv_sz when parsing arm64 signal frame
      kselftest/arm64: Add POE as a feature in the signal tests
      kselftest/arm64: Move/add POE helpers to test_signals_utils.h
      kselftest/arm64: Add tests for POR_EL0 save/reset/restore

Krzysztof Kozlowski (1):
      perf: qcom: Unify user-visible "Qualcomm" name

Leonardo Bras (1):
      arm64/daifflags: Make local_daif_*() helpers __always_inline

Marco Elver (1):
      arm64: Implement _THIS_IP_ using inline asm

Mark Brown (3):
      arm64/cpufeature: Define hwcaps for 2025 dpISA features
      kselftest/arm64: Add 2025 dpISA coverage to hwcaps
      arm64: Document SVE constraints on new hwcaps

Mark Rutland (23):
      arm64: fpsimd: Fix type mismatch in sve_{save,load}_state()
      arm64: fpsimd: Fix type mismatch in sme_{save,load}_state()
      KVM: arm64: Don't include <asm/fpsimdmacros.h>
      KVM: arm64: Don't override FFR save/restore argument
      KVM: arm64: pkvm: Save host FPMR in host cpu context
      KVM: arm64: pkvm: Remove struct cpu_sve_state
      arm64: fpsimd: Fold sve_init_regs() into do_sve_acc()
      arm64: fpsimd: Remove sve_set_vq() and sme_set_vq()
      arm64: fpsimd: Use assembler for SVE instructions
      arm64: fpsimd: Use assembler for baseline SME instructions
      arm64: fpsimd: Move sve_get_vl() and sme_get_vl() inline
      arm64: sysreg: Add FPCR and FPSR
      arm64: fpsimd: Split FPSR/FPCR from SVE save/restore
      arm64: fpsimd: Move fpsimd save/restore inline
      arm64: fpsimd: Use opaque type for SVE state
      arm64: fpsimd: Use opaque type for SME state
      arm64: fpsimd: Move SVE save/restore inline
      arm64: fpsimd: Move sve_flush_live() inline
      arm64: fpsimd: Move SME save/restore inline
      arm64: fpsimd: Remove <asm/fpsimdmacros.h>
      arm64: cputype: Add C1-Ultra definitions
      arm64: cputype: Add C1-Premium definitions
      arm64: errata: Mitigate TLBI errata on various Arm CPUs

Osama Abdelkader (1):
      arm64: panic from init_IRQ if IRQ handler stacks cannot be allocated

Pengjie Zhang (1):
      arm64: smp: Do not mark secondary CPUs possible under nosmp

Robin Murphy (2):
      arm64: errata: Reformat table for IDs
      perf/arm-cmn: Fix DVM node events

Shanker Donthineni (1):
      arm64: errata: Mitigate TLBI errata on NVIDIA Olympus CPU

Thorsten Blum (2):
      arm64: proton-pack: use sysfs_emit in sysfs show functions
      arm64: patching: replace min_t with min in __text_poke

Will Deacon (12):
      Revert "arm64: mm: Defer remap of linear alias of data/bss"
      Revert "arm64: mm: Unmap kernel data/bss entirely from the linear map"
      arm64: errata: Mitigate TLBI errata on Microsoft Azure Cobalt 100 CPU
      Merge branch 'for-next/cpufeature' into for-next/core
      Merge branch 'for-next/errata' into for-next/core
      Merge branch 'for-next/fpsimd-cleanups' into for-next/core
      Merge branch 'for-next/misc' into for-next/core
      Merge branch 'for-next/mm' into for-next/core
      Merge branch 'for-next/mpam' into for-next/core
      Merge branch 'for-next/perf' into for-next/core
      Merge branch 'for-next/selftests' into for-next/core
      Merge branch 'for-next/sysregs' into for-next/core

Zeng Heng (4):
      arm64: cpufeature: Add support for the MPAM v0.1 architecture version
      arm_mpam: Update architecture version check for MPAM MSC
      arm64: cpufeature: Add WORKAROUND_DISABLE_CNP capability
      arm64: kernel: Disable CNP on HiSilicon HIP09

eillon (1):
      arm64/sysreg: Add HDBSS related register information

 Documentation/arch/arm64/elf_hwcaps.rst            |  27 ++
 Documentation/arch/arm64/silicon-errata.rst        |  93 +++--
 MAINTAINERS                                        |   2 +-
 arch/arm64/Kconfig                                 |  63 ++++
 arch/arm64/include/asm/arch_timer.h                |  12 +-
 arch/arm64/include/asm/cpucaps.h                   |   4 +-
 arch/arm64/include/asm/cpufeature.h                |   7 +
 arch/arm64/include/asm/cputype.h                   |   4 +
 arch/arm64/include/asm/daifflags.h                 |  10 +-
 arch/arm64/include/asm/device.h                    |  14 -
 arch/arm64/include/asm/el2_setup.h                 |   4 +-
 arch/arm64/include/asm/esr.h                       |   2 +
 arch/arm64/include/asm/fpsimd.h                    | 386 +++++++++++++++++++--
 arch/arm64/include/asm/fpsimdmacros.h              | 357 -------------------
 arch/arm64/include/asm/io.h                        |   2 +-
 arch/arm64/include/asm/kvm_host.h                  |  27 +-
 arch/arm64/include/asm/kvm_hyp.h                   |   5 -
 arch/arm64/include/asm/kvm_pkvm.h                  |   3 +-
 arch/arm64/include/asm/linkage.h                   |   4 +
 arch/arm64/include/asm/pgtable-types.h             |  14 +-
 arch/arm64/include/asm/pgtable.h                   |   4 +-
 arch/arm64/include/asm/processor.h                 |   7 +-
 arch/arm64/include/asm/ptdump.h                    |   8 +-
 arch/arm64/include/asm/tlbflush.h                  |   4 +-
 arch/arm64/include/uapi/asm/hwcap.h                |   8 +
 arch/arm64/kernel/Makefile                         |   2 +-
 arch/arm64/kernel/cpu_errata.c                     |  55 ++-
 arch/arm64/kernel/cpufeature.c                     |  28 +-
 arch/arm64/kernel/cpuinfo.c                        |   8 +
 arch/arm64/kernel/efi.c                            |   4 +-
 arch/arm64/kernel/entry-common.c                   |   8 +-
 arch/arm64/kernel/entry-fpsimd.S                   | 134 -------
 arch/arm64/kernel/fpsimd.c                         |  90 +++--
 arch/arm64/kernel/irq.c                            |  29 +-
 arch/arm64/kernel/patching.c                       |   3 +-
 arch/arm64/kernel/pi/map_kernel.c                  |   2 +-
 arch/arm64/kernel/pi/map_range.c                   |   4 +-
 arch/arm64/kernel/pi/pi.h                          |   2 +-
 arch/arm64/kernel/proton-pack.c                    |  17 +-
 arch/arm64/kernel/smp.c                            |  14 +-
 arch/arm64/kernel/vmlinux.lds.S                    |   8 +-
 arch/arm64/kvm/arm.c                               |  16 +-
 arch/arm64/kvm/guest.c                             |   4 +-
 arch/arm64/kvm/hyp/entry.S                         |   1 -
 arch/arm64/kvm/hyp/fpsimd.S                        |  33 --
 arch/arm64/kvm/hyp/include/hyp/switch.h            |  23 +-
 arch/arm64/kvm/hyp/nvhe/Makefile                   |   2 +-
 arch/arm64/kvm/hyp/nvhe/hyp-main.c                 |  20 +-
 arch/arm64/kvm/hyp/nvhe/setup.c                    |   4 +-
 arch/arm64/kvm/hyp/vhe/Makefile                    |   2 +-
 arch/arm64/kvm/mmu.c                               |   5 +
 arch/arm64/mm/fixmap.c                             |   6 +-
 arch/arm64/mm/kasan_init.c                         |   2 +-
 arch/arm64/mm/mmap.c                               |   4 +-
 arch/arm64/mm/mmu.c                                | 145 ++++----
 arch/arm64/mm/pageattr.c                           |   2 +-
 arch/arm64/mm/ptdump.c                             |   2 +-
 arch/arm64/tools/cpucaps                           |   2 +-
 arch/arm64/tools/sysreg                            |  74 ++++
 arch/powerpc/lib/code-patching.c                   |  52 +--
 arch/sh/mm/init.c                                  |   3 -
 drivers/perf/Kconfig                               |   4 +-
 drivers/perf/arm-cmn.c                             |  23 +-
 drivers/resctrl/mpam_devices.c                     |  23 +-
 include/linux/linkage.h                            |   4 +
 include/linux/pgtable.h                            |   2 +-
 mm/kasan/init.c                                    |  10 +-
 mm/mm_init.c                                       |   2 +-
 tools/testing/selftests/arm64/abi/hwcap.c          | 116 +++++++
 .../testing/selftests/arm64/signal/test_signals.h  |   2 +
 .../selftests/arm64/signal/test_signals_utils.c    |   3 +
 .../selftests/arm64/signal/test_signals_utils.h    |  16 +
 .../signal/testcases/poe_missing_poe_context.c     |  73 ++++
 .../selftests/arm64/signal/testcases/poe_restore.c |  64 ++++
 .../selftests/arm64/signal/testcases/poe_siginfo.c |  15 -
 tools/testing/selftests/mm/pkey-arm64.h            |   3 +-
 76 files changed, 1275 insertions(+), 966 deletions(-)
 delete mode 100644 arch/arm64/include/asm/device.h
 delete mode 100644 arch/arm64/include/asm/fpsimdmacros.h
 delete mode 100644 arch/arm64/kernel/entry-fpsimd.S
 delete mode 100644 arch/arm64/kvm/hyp/fpsimd.S
 create mode 100644 tools/testing/selftests/arm64/signal/testcases/poe_missing_poe_context.c
 create mode 100644 tools/testing/selftests/arm64/signal/testcases/poe_restore.c


^ permalink raw reply

* Re: [PATCH v2] arm64/irqflags: __always_inline the arch_local_irq_*() helpers
From: Breno Leitao @ 2026-06-15 10:07 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Catalin Marinas, Will Deacon, leo.bras, leo.yan, linux-arm-kernel,
	linux-kernel, palmer, paulmck, puranjay, usama.arif, rmikey,
	kernel-team
In-Reply-To: <ae9f6_MSCMfgG8VT@J2N7QTR9R3.cambridge.arm.com>

On Mon, Apr 27, 2026 at 02:08:59PM +0100, Mark Rutland wrote:
> On Mon, Apr 27, 2026 at 01:26:18PM +0100, Catalin Marinas wrote:
> > On Tue, Apr 21, 2026 at 08:58:57AM -0700, Breno Leitao wrote:
> > > Force-inline all of the arch_local_irq_*() wrappers so they cannot be
> > > emitted out-of-line:
> > > 
> > >   - arch_local_irq_enable()
> > >   - arch_local_irq_disable()
> > >   - arch_local_save_flags()
> > >   - arch_irqs_disabled_flags()
> > >   - arch_irqs_disabled()
> > >   - arch_local_irq_save()
> > >   - arch_local_irq_restore()
> > 
> > I'll queue this, thanks!
> > 
> > I think we should also do local_daif_{mask,restore,inherit} as they seem
> > to be called from noinstr locations in entry-common.c.
> 
> I agree we probably should mark those as __always_inline, but I beleive
> they're safe as-is.

Returning to this topic, I've also observed arch_counter_get_cntpct()
appearing in backtraces during lock spinning, which obscures the actual
code location and makes debugging less straightforward.

This scenario is common at Meta when a remote function call targets a
stuck CPU, resulting in an infinite loop, and dying at arch_counter_get_cntpc().

Is it worth making arch_counter_get_cntpc() always inline as well?


 [     T18] Sending NMI from CPU 1 to CPUs 18:
 [     C18] NMI backtrace for cpu 18
 [     C18] CPU: 18 UID: 0 PID: 7467 Comm: dynoKernelMon Kdump: loaded Not tainted 6.16.1-0_fbk3_0_gd6c130b80483 #1 NONE
 [     C18] pstate: 63401009 (nZCv daif +PAN -UAO +TCO +DIT +SSBS BTYPE=--)
 [     C18] pc : arch_counter_get_cntpct+0x14/0x18
 [     C18] lr : ktime_get_mono_fast_ns+0x5c/0x1a8
 [     C18] sp : ffff8000d43afa30
 [     C18] x29: ffff8000d43afa30 x28: 00000000000f4240 x27: 0000000000000000
 [     C18] x26: 0000028ac8fe4369 x25: 0000000000001388 x24: 000000000054fb54
 [     C18] x23: ffff800082fbd000 x22: 0000028f22c480dd x21: ffff800081b60000
 [     C18] x20: ffff800080c71058 x19: ffff800082fb8848 x18: 0000000000000018
 [     C18] x17: 0000000000000058 x16: 00000000ffffffff x15: 00000000ffff0000
 [     C18] x14: 0000000000000002 x13: 0000000000000003 x12: 000000000000ffd8
 [     C18] x11: ffff8000d43affd8 x10: 0000000000000038 x9 : 0000000000000000
 [     C18] x8 : ffff8000d43afa30 x7 : ff7fff7f7f7f7f7f x6 : 000000000000000a
 [     C18] x5 : ffff8000832772ba x4 : 0000000000000000 x3 : 0000000000000000
 [     C18] x2 : 0000000000000000 x1 : 000000000014b4c0 x0 : 000002a87573b7a0
 [     C18] Call trace:
 [     C18]  arch_counter_get_cntpct+0x14/0x18 (P)
 [     C18]  smp_call_function_single+0x2d0/0xd90
 [     C18]  event_function_call+0x80/0x2e0
 [     C18]  _perf_event_disable+0x5c/0xf0
 [     C18]  perf_ioctl+0x170/0x890
 [     C18]  __arm64_sys_ioctl+0xbb0/0xd28
 [     C18]  invoke_syscall+0x4c/0xd0
 [     C18]  do_el0_svc+0x80/0xb8
 [     C18]  el0_svc+0x30/0xf0
 [     C18]  el0t_64_sync_handler+0x70/0x100
 [     C18]  el0t_64_sync+0x17c/0x180


Thanks,
--breno


^ permalink raw reply

* Re: [PATCH] KVM: arm64: Sync SPSR_EL1 when injecting an exception into a pVM
From: Will Deacon @ 2026-06-15 10:05 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, Oliver Upton, linux-arm-kernel, kvmarm,
	linux-kernel, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Sascha Bischoff, Andrew Jones
In-Reply-To: <20260612113414.1022901-1-tabba@google.com>

On Fri, Jun 12, 2026 at 12:34:14PM +0100, Fuad Tabba wrote:
> When pKVM injects a synchronous exception into a protected guest, it
> re-enters without restoring the guest's EL1 sysregs and writes the EL1
> exception registers to hardware by hand: ESR_EL1 and ELR_EL1, but not
> SPSR_EL1. enter_exception64() sets SPSR_EL1 (the interrupted PSTATE)
> only in memory, so the guest's handler reads a stale SPSR_EL1 and
> restores the wrong PSTATE on eret.
> 
> Write SPSR_EL1 alongside the other exception registers.
> 
> Fixes: 6c30bfb18d0b ("KVM: arm64: Add handlers for protected VM System Registers")
> Reported-by: sashiko <sashiko@sashiko.dev>
> Signed-off-by: Fuad Tabba <tabba@google.com>
> ---
>  arch/arm64/kvm/hyp/nvhe/sys_regs.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
> index 8c3fbb413a06..1a7d5cd16d72 100644
> --- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c
> +++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
> @@ -268,6 +268,7 @@ static void inject_sync64(struct kvm_vcpu *vcpu, u64 esr)
>  
>  	write_sysreg_el1(esr, SYS_ESR);
>  	write_sysreg_el1(read_sysreg_el2(SYS_ELR), SYS_ELR);
> +	write_sysreg_el1(read_sysreg_el2(SYS_SPSR), SYS_SPSR);
>  	write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR);
>  	write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR);
>  }

Is SPSR_EL1 not set in enter_exception64() using vcpu_cpsr(vcpu), which
*is* set here? I'm just a bit wary of the report, as I'd have expected
fireworks if we weren't initialising the guest's SPSR on the exception
injection path.

Will


^ permalink raw reply

* Re: [PATCH RFC 3/3] arm64: Add HOTPLUG_PARALLEL support for secondary CPUs
From: Jinjie Ruan @ 2026-06-15  9:57 UTC (permalink / raw)
  To: Michael Kelley, catalin.marinas@arm.com, will@kernel.org,
	tsbogend@alpha.franken.de, pjw@kernel.org, palmer@dabbelt.com,
	aou@eecs.berkeley.edu, alex@ghiti.fr, tglx@kernel.org,
	mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com,
	hpa@zytor.com, peterz@infradead.org, kees@kernel.org,
	nathan@kernel.org, linusw@kernel.org, ojeda@kernel.org,
	david.kaplan@amd.com, lukas.bulwahn@redhat.com,
	ryan.roberts@arm.com, maz@kernel.org, timothy.hayes@arm.com,
	lpieralisi@kernel.org, thuth@redhat.com, oupton@kernel.org,
	yeoreum.yun@arm.com, miko.lenczewski@arm.com, broonie@kernel.org,
	kevin.brodsky@arm.com, james.clark@linaro.org, tabba@google.com,
	mrigendra.chaubey@gmail.com, arnd@arndb.de,
	anshuman.khandual@arm.com, x86@kernel.org,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, linux-mips@vger.kernel.org,
	linux-riscv@lists.infradead.org
In-Reply-To: <SN6PR02MB41575306521E6223561F476FD4182@SN6PR02MB4157.namprd02.prod.outlook.com>



On 6/12/2026 11:45 PM, Michael Kelley wrote:
> From: Jinjie Ruan <ruanjinjie@huawei.com> Sent: Thursday, June 11, 2026 6:38 AM
>>
>> Support for parallel secondary CPU bringup is already utilized by x86,
>> MIPS, and RISC-V. This patch brings this capability to the arm64
>> architecture.
>>
>> Rework the global `secondary_data` accessed during early boot into
>> a per-CPU array. This array maps logical CPU IDs to MPIDR_EL1 values,
>> enabling the early boot code in head.S to resolve each secondary CPU's
>> logical ID concurrently.
>>
>> To fully enable HOTPLUG_PARALLEL, this patch implements:
>> 1) An arm64-specific arch_cpuhp_kick_ap_alive() handler.
>> 2) Callbacks to cpuhp_ap_sync_alive() inside secondary_start_kernel().
>>
>> Successfully tested on QEMU ARM64 virt machine (KVM on, 128 vCPUs).
>>
>> |     test kernel	   | secondary CPUs boot time |
>> |  ---------------------   |	--------------------  |
>> |   Without this patch     |		155.672	      |
>> |   cpuhp.parallel=0	   |		62.897	      |
>> |   cpuhp.parallel=1	   |		166.703	      |
> 
> The last two rows seem mixed up. I would expect parallel=0 to
> result in a longer boot time.

Without this patch:

KVM event statistics (6 entries)
Event name       Samples       Sample%     Time (ns)         Time%
Mean Time (ns)
  DABT_LOW        323112        75.00%    1669148000        17.00%
5165
       WFx         85817        19.00%     723215800         7.00%
8427
     SYS64         14914         3.00%     419934530         4.00%
28157
       IRQ          5643         1.00%    6732439250        70.00%
1193060
     HVC64           282         0.00%      35543970         0.00%
126042
  IABT_LOW             1         0.00%          6130         0.00%
6130

cpuhp.parallel=0:

Event name       Samples       Sample%     Time (ns)         Time%
Mean Time (ns)
 DABT_LOW        308175        80.00%     643628050         6.00%
2088
      WFx         55208        14.00%     261925270         2.00%
4744
    SYS64         14975         3.00%     155727880         1.00%
10399
      IRQ          4755         1.00%    8496162210        88.00%
1786784
    HVC64           280         0.00%      19429900         0.00%
69392
 IABT_LOW             1         0.00%          5850         0.00%
5850

cpuhp.parallel=1:

 Event name       Samples       Sample%     Time (ns)         Time%
Mean Time (ns)
 DABT_LOW        307923        77.00%     692965050         2.00%
     2250
      WFx         59549        15.00%     287888960         0.00%
     4834
    SYS64         15127         3.00%     334366230         1.00%
    22103
      IRQ         12861         3.00%   29784004970        95.00%
  2315838
    HVC64           280         0.00%      21869940         0.00%
    78106
 IABT_LOW             1         0.00%          9320         0.00%
     9320

- Default (no patch): Slowest HVC64 handling (126 μs), highest WFx count
(85k), and most total VM‑exits.

- cpuhp.parallel=1: HVC64 latency improved to 78 μs (close to
cpuhp.parallel=0), but IRQ exits increased dramatically (12.9k, 2.7×
that of `cpuhp.parallel=0`), accounting for 95% of event time and
becoming the new bottleneck.

- cpuhp.parallel=0: Fastest HVC64 (69 μs), lowest IRQ exits (4.8k), and
lowest total samples, delivering the best overall boot performance.

Therefor, `cpuhp.parallel=1` reduces HVC cost but suffers from a massive
increase in IRQ exits, while `cpuhp.parallel=0` avoids this interrupt
storm and therefore performs best in a KVM guest.

> 
> Michael
> 
>>
>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>> ---
>>  arch/arm64/Kconfig           |  1 +
>>  arch/arm64/include/asm/smp.h |  8 ++++++++
>>  arch/arm64/kernel/head.S     | 23 +++++++++++++++++++++++
>>  arch/arm64/kernel/smp.c      | 27 +++++++++++++++++++++++++++
>>  4 files changed, 59 insertions(+)
>>
> 
> 



^ permalink raw reply

* Re: [PATCH v2 0/7] KVM: arm64: Forward FFA_NOTIFICATION* calls to TrustZone
From: Will Deacon @ 2026-06-15  9:47 UTC (permalink / raw)
  To: Sebastian Ene
  Cc: Vincent Donnefort, catalin.marinas, maz, oupton, joey.gouly,
	korneld, kvmarm, linux-arm-kernel, linux-kernel, android-kvm,
	mrigendra.chaubey, perlarsen, suzuki.poulose, yuzenghui
In-Reply-To: <ai-tO-sSNGp7mjAU@google.com>

On Mon, Jun 15, 2026 at 07:43:55AM +0000, Sebastian Ene wrote:
> On Sun, Jun 14, 2026 at 10:29:34AM +0100, Will Deacon wrote:
> > Yes, that part now seems to be missing.
> 
> I am a bit worried to apply for all of them the check from the relayer
> (ffa_check_unused_args_sbz) because of how it's written in the spec in
> (11.2 Reserved parameter convention). To be more specific, there is no
> mention of what the relayer is expected to do here (which is what hyp
> does). It says 2 things:
> 1. the caller (in this case the host driver) is expected to zero out
> unused args
> 2. the callee (Trustzone) ignores the values in these registers.
> 
> If we enforce SBZ in the relayer but (1) doesn't comply with it, we will
> introduce a regression. I left it on purpose without enforcing
> ffa_check_unused_args_sbz for the others.

I think that's ok -- if the caller isn't passing zeroes when it should,
then it's already on borrowed (no pun intended!) time and it will need
to be fixed.

Will


^ permalink raw reply

* [PATCH v2 11/11] ASoC: fsl: mpc5200_psc_ac97: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for mutex locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---

Changes in v2:
 - psc_ac97_cold_reset(): Fix scoped_guard() usage by replacing
   scoped_guard(mutex_lock, ...) with scoped_guard(mutex, ...).

 sound/soc/fsl/mpc5200_psc_ac97.c | 34 +++++++++++---------------------
 1 file changed, 12 insertions(+), 22 deletions(-)

diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 8554fb690772..d4d9f5b6bc07 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -31,14 +31,13 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
 	int status;
 	unsigned int val;
 
-	mutex_lock(&psc_dma->mutex);
+	guard(mutex)(&psc_dma->mutex);
 
 	/* Wait for command send status zero = ready */
 	status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) &
 				MPC52xx_PSC_SR_CMDSEND), 100, 0);
 	if (status == 0) {
 		pr_err("timeout on ac97 bus (rdy)\n");
-		mutex_unlock(&psc_dma->mutex);
 		return -ENODEV;
 	}
 
@@ -54,19 +53,16 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
 	if (status == 0) {
 		pr_err("timeout on ac97 read (val) %x\n",
 				in_be16(&psc_dma->psc_regs->sr_csr.status));
-		mutex_unlock(&psc_dma->mutex);
 		return -ENODEV;
 	}
 	/* Get the data */
 	val = in_be32(&psc_dma->psc_regs->ac97_data);
 	if (((val >> 24) & 0x7f) != reg) {
 		pr_err("reg echo error on ac97 read\n");
-		mutex_unlock(&psc_dma->mutex);
 		return -ENODEV;
 	}
 	val = (val >> 8) & 0xffff;
 
-	mutex_unlock(&psc_dma->mutex);
 	return (unsigned short) val;
 }
 
@@ -75,52 +71,46 @@ static void psc_ac97_write(struct snd_ac97 *ac97,
 {
 	int status;
 
-	mutex_lock(&psc_dma->mutex);
+	guard(mutex)(&psc_dma->mutex);
 
 	/* Wait for command status zero = ready */
 	status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) &
 				MPC52xx_PSC_SR_CMDSEND), 100, 0);
 	if (status == 0) {
 		pr_err("timeout on ac97 bus (write)\n");
-		goto out;
+		return;
 	}
 	/* Write data */
 	out_be32(&psc_dma->psc_regs->ac97_cmd,
 			((reg & 0x7f) << 24) | (val << 8));
-
- out:
-	mutex_unlock(&psc_dma->mutex);
 }
 
 static void psc_ac97_warm_reset(struct snd_ac97 *ac97)
 {
 	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
 
-	mutex_lock(&psc_dma->mutex);
+	guard(mutex)(&psc_dma->mutex);
 
 	out_be32(&regs->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR);
 	udelay(3);
 	out_be32(&regs->sicr, psc_dma->sicr);
-
-	mutex_unlock(&psc_dma->mutex);
 }
 
 static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
 {
 	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
 
-	mutex_lock(&psc_dma->mutex);
-	dev_dbg(psc_dma->dev, "cold reset\n");
+	scoped_guard(mutex, &psc_dma->mutex) {
+		dev_dbg(psc_dma->dev, "cold reset\n");
 
-	mpc5200_psc_ac97_gpio_reset(psc_dma->id);
+		mpc5200_psc_ac97_gpio_reset(psc_dma->id);
 
-	/* Notify the PSC that a reset has occurred */
-	out_be32(&regs->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB);
+		/* Notify the PSC that a reset has occurred */
+		out_be32(&regs->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB);
 
-	/* Re-enable RX and TX */
-	out_8(&regs->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
-
-	mutex_unlock(&psc_dma->mutex);
+		/* Re-enable RX and TX */
+		out_8(&regs->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
+	}
 
 	usleep_range(1000, 2000);
 	psc_ac97_warm_reset(ac97);
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 10/11] ASoC: fsl: mpc5200_dma: Use guard() for spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/mpc5200_dma.c | 56 ++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index 56e2cf2f727b..bfedb2dea0b3 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -77,18 +77,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)
 {
 	struct psc_dma_stream *s = _psc_dma_stream;
 
-	spin_lock(&s->psc_dma->lock);
-	/* For each finished period, dequeue the completed period buffer
-	 * and enqueue a new one in it's place. */
-	while (bcom_buffer_done(s->bcom_task)) {
-		bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+	scoped_guard(spinlock, &s->psc_dma->lock) {
+		/*
+		 * For each finished period, dequeue the completed period buffer
+		 * and enqueue a new one in its place
+		 */
+		while (bcom_buffer_done(s->bcom_task)) {
+			bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
 
-		s->period_current = (s->period_current+1) % s->runtime->periods;
-		s->period_count++;
+			s->period_current = (s->period_current+1) % s->runtime->periods;
+			s->period_count++;
 
-		psc_dma_bcom_enqueue_next_buffer(s);
+			psc_dma_bcom_enqueue_next_buffer(s);
+		}
 	}
-	spin_unlock(&s->psc_dma->lock);
 
 	/* If the stream is active, then also inform the PCM middle layer
 	 * of the period finished event. */
@@ -116,7 +118,6 @@ static int psc_dma_trigger(struct snd_soc_component *component,
 	struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);
 	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
 	u16 imr;
-	unsigned long flags;
 	int i;
 
 	switch (cmd) {
@@ -135,19 +136,18 @@ static int psc_dma_trigger(struct snd_soc_component *component,
 		/* Fill up the bestcomm bd queue and enable DMA.
 		 * This will begin filling the PSC's fifo.
 		 */
-		spin_lock_irqsave(&psc_dma->lock, flags);
-
-		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-			bcom_gen_bd_rx_reset(s->bcom_task);
-		else
-			bcom_gen_bd_tx_reset(s->bcom_task);
+		scoped_guard(spinlock_irqsave, &psc_dma->lock) {
+			if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+				bcom_gen_bd_rx_reset(s->bcom_task);
+			else
+				bcom_gen_bd_tx_reset(s->bcom_task);
 
-		for (i = 0; i < runtime->periods; i++)
-			if (!bcom_queue_full(s->bcom_task))
-				psc_dma_bcom_enqueue_next_buffer(s);
+			for (i = 0; i < runtime->periods; i++)
+				if (!bcom_queue_full(s->bcom_task))
+					psc_dma_bcom_enqueue_next_buffer(s);
 
-		bcom_enable(s->bcom_task);
-		spin_unlock_irqrestore(&psc_dma->lock, flags);
+			bcom_enable(s->bcom_task);
+		}
 
 		out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
 
@@ -158,13 +158,13 @@ static int psc_dma_trigger(struct snd_soc_component *component,
 			substream->pstr->stream, s->period_count);
 		s->active = 0;
 
-		spin_lock_irqsave(&psc_dma->lock, flags);
-		bcom_disable(s->bcom_task);
-		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-			bcom_gen_bd_rx_reset(s->bcom_task);
-		else
-			bcom_gen_bd_tx_reset(s->bcom_task);
-		spin_unlock_irqrestore(&psc_dma->lock, flags);
+		scoped_guard(spinlock_irqsave, &psc_dma->lock) {
+			bcom_disable(s->bcom_task);
+			if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+				bcom_gen_bd_rx_reset(s->bcom_task);
+			else
+				bcom_gen_bd_tx_reset(s->bcom_task);
+		}
 
 		break;
 
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 09/11] ASoC: fsl_rpmsg: Use guard() for mutex & spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for mutex & spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/imx-pcm-rpmsg.c | 69 +++++++++++++++--------------------
 1 file changed, 30 insertions(+), 39 deletions(-)

diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c
index 031e5272215d..7210393dfa5d 100644
--- a/sound/soc/fsl/imx-pcm-rpmsg.c
+++ b/sound/soc/fsl/imx-pcm-rpmsg.c
@@ -39,10 +39,9 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg,
 	struct rpmsg_device *rpdev = info->rpdev;
 	int ret = 0;
 
-	mutex_lock(&info->msg_lock);
+	guard(mutex)(&info->msg_lock);
 	if (!rpdev) {
 		dev_err(info->dev, "rpmsg channel not ready\n");
-		mutex_unlock(&info->msg_lock);
 		return -EINVAL;
 	}
 
@@ -55,15 +54,12 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg,
 			 sizeof(struct rpmsg_s_msg));
 	if (ret) {
 		dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
-		mutex_unlock(&info->msg_lock);
 		return ret;
 	}
 
 	/* No receive msg for TYPE_C command */
-	if (msg->s_msg.header.type == MSG_TYPE_C) {
-		mutex_unlock(&info->msg_lock);
+	if (msg->s_msg.header.type == MSG_TYPE_C)
 		return 0;
-	}
 
 	/* wait response from rpmsg */
 	ret = wait_for_completion_timeout(&info->cmd_complete,
@@ -71,7 +67,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg,
 	if (!ret) {
 		dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n",
 			msg->s_msg.header.cmd);
-		mutex_unlock(&info->msg_lock);
 		return -ETIMEDOUT;
 	}
 
@@ -100,8 +95,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg,
 	dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd,
 		info->r_msg.param.resp);
 
-	mutex_unlock(&info->msg_lock);
-
 	return 0;
 }
 
@@ -109,14 +102,13 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream,
 				      struct rpmsg_msg *msg,
 				      struct rpmsg_info *info)
 {
-	unsigned long flags;
 	int ret = 0;
 
 	/*
 	 * Queue the work to workqueue.
 	 * If the queue is full, drop the message.
 	 */
-	spin_lock_irqsave(&info->wq_lock, flags);
+	guard(spinlock_irqsave)(&info->wq_lock);
 	if (info->work_write_index != info->work_read_index) {
 		int index = info->work_write_index;
 
@@ -130,7 +122,6 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream,
 		info->msg_drop_count[substream->stream]++;
 		ret = -EPIPE;
 	}
-	spin_unlock_irqrestore(&info->wq_lock, flags);
 
 	return ret;
 }
@@ -523,7 +514,6 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component,
 	snd_pcm_sframes_t avail;
 	struct timer_list *timer;
 	struct rpmsg_msg *msg;
-	unsigned long flags;
 	int buffer_tail = 0;
 	int written_num;
 
@@ -553,11 +543,11 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component,
 		msg->s_msg.param.buffer_tail = buffer_tail;
 
 		/* The notification message is updated to latest */
-		spin_lock_irqsave(&info->lock[substream->stream], flags);
-		memcpy(&info->notify[substream->stream], msg,
-		       sizeof(struct rpmsg_s_msg));
-		info->notify_updated[substream->stream] = true;
-		spin_unlock_irqrestore(&info->lock[substream->stream], flags);
+		scoped_guard(spinlock_irqsave, &info->lock[substream->stream]) {
+			memcpy(&info->notify[substream->stream], msg,
+			       sizeof(struct rpmsg_s_msg));
+			info->notify_updated[substream->stream] = true;
+		}
 
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 			avail = snd_pcm_playback_hw_avail(runtime);
@@ -640,7 +630,7 @@ static void imx_rpmsg_pcm_work(struct work_struct *work)
 	bool is_notification = false;
 	struct rpmsg_info *info;
 	struct rpmsg_msg msg;
-	unsigned long flags;
+	bool updated;
 
 	work_of_rpmsg = container_of(work, struct work_of_rpmsg, work);
 	info = work_of_rpmsg->info;
@@ -651,25 +641,26 @@ static void imx_rpmsg_pcm_work(struct work_struct *work)
 	 * enough data in M core side, need to let M core know
 	 * data is updated immediately.
 	 */
-	spin_lock_irqsave(&info->lock[TX], flags);
-	if (info->notify_updated[TX]) {
-		memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg));
-		info->notify_updated[TX] = false;
-		spin_unlock_irqrestore(&info->lock[TX], flags);
-		info->send_message(&msg, info);
-	} else {
-		spin_unlock_irqrestore(&info->lock[TX], flags);
+	scoped_guard(spinlock_irqsave, &info->lock[TX]) {
+		updated = info->notify_updated[TX];
+		if (updated) {
+			memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg));
+			info->notify_updated[TX] = false;
+		}
 	}
-
-	spin_lock_irqsave(&info->lock[RX], flags);
-	if (info->notify_updated[RX]) {
-		memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg));
-		info->notify_updated[RX] = false;
-		spin_unlock_irqrestore(&info->lock[RX], flags);
+	if (updated)
 		info->send_message(&msg, info);
-	} else {
-		spin_unlock_irqrestore(&info->lock[RX], flags);
+
+	scoped_guard(spinlock_irqsave, &info->lock[RX]) {
+		updated = info->notify_updated[RX];
+		if (updated) {
+			memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg));
+			info->notify_updated[RX] = false;
+		}
 	}
+	if (updated)
+		info->send_message(&msg, info);
+
 
 	/* Skip the notification message for it has been processed above */
 	if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C &&
@@ -681,10 +672,10 @@ static void imx_rpmsg_pcm_work(struct work_struct *work)
 		info->send_message(&work_of_rpmsg->msg, info);
 
 	/* update read index */
-	spin_lock_irqsave(&info->wq_lock, flags);
-	info->work_read_index++;
-	info->work_read_index %= WORK_MAX_NUM;
-	spin_unlock_irqrestore(&info->wq_lock, flags);
+	scoped_guard(spinlock_irqsave, &info->wq_lock) {
+		info->work_read_index++;
+		info->work_read_index %= WORK_MAX_NUM;
+	}
 }
 
 static int imx_rpmsg_pcm_probe(struct platform_device *pdev)
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 08/11] ASoC: imx-audio-rpmsg: Use guard() for spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/imx-audio-rpmsg.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c
index 38aafb8954c7..b55dfbdb4502 100644
--- a/sound/soc/fsl/imx-audio-rpmsg.c
+++ b/sound/soc/fsl/imx-audio-rpmsg.c
@@ -22,7 +22,6 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
 	struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data;
 	struct rpmsg_info *info;
 	struct rpmsg_msg *msg;
-	unsigned long flags;
 
 	if (!rpmsg->rpmsg_pdev)
 		return 0;
@@ -37,21 +36,21 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
 		/* TYPE C is notification from M core */
 		switch (r_msg->header.cmd) {
 		case TX_PERIOD_DONE:
-			spin_lock_irqsave(&info->lock[TX], flags);
-			msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
-			msg->r_msg.param.buffer_tail =
-						r_msg->param.buffer_tail;
-			msg->r_msg.param.buffer_tail %= info->num_period[TX];
-			spin_unlock_irqrestore(&info->lock[TX], flags);
+			scoped_guard(spinlock_irqsave, &info->lock[TX]) {
+				msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+				msg->r_msg.param.buffer_tail =
+							r_msg->param.buffer_tail;
+				msg->r_msg.param.buffer_tail %= info->num_period[TX];
+			}
 			info->callback[TX](info->callback_param[TX]);
 			break;
 		case RX_PERIOD_DONE:
-			spin_lock_irqsave(&info->lock[RX], flags);
-			msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
-			msg->r_msg.param.buffer_tail =
-						r_msg->param.buffer_tail;
-			msg->r_msg.param.buffer_tail %= info->num_period[1];
-			spin_unlock_irqrestore(&info->lock[RX], flags);
+			scoped_guard(spinlock_irqsave, &info->lock[RX]) {
+				msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+				msg->r_msg.param.buffer_tail =
+							r_msg->param.buffer_tail;
+				msg->r_msg.param.buffer_tail %= info->num_period[1];
+			}
 			info->callback[RX](info->callback_param[RX]);
 			break;
 		default:
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 07/11] ASoC: fsl_xcvr: Use guard() for spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/fsl_xcvr.c | 29 ++++++++++++-----------------
 1 file changed, 12 insertions(+), 17 deletions(-)

diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index 6677d3bf36ec..41d100500534 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -797,10 +797,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 {
 	struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
 	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-	unsigned long lock_flags;
 	int ret = 0;
 
-	spin_lock_irqsave(&xcvr->lock, lock_flags);
+	guard(spinlock_irqsave)(&xcvr->lock);
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
@@ -812,7 +811,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 					 FSL_XCVR_EXT_CTRL_DPTH_RESET(tx));
 		if (ret < 0) {
 			dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret);
-			goto release_lock;
+			return ret;
 		}
 
 		if (tx) {
@@ -824,7 +823,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 						   FSL_XCVR_ISR_CMDC_TX_EN);
 				if (ret < 0) {
 					dev_err(dai->dev, "err updating isr %d\n", ret);
-					goto release_lock;
+					return ret;
 				}
 				fallthrough;
 			case FSL_XCVR_MODE_SPDIF:
@@ -833,7 +832,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 						      FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
 				if (ret < 0) {
 					dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret);
-					goto release_lock;
+					return ret;
 				}
 				break;
 			}
@@ -844,14 +843,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 					 FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0);
 		if (ret < 0) {
 			dev_err(dai->dev, "Failed to enable DMA: %d\n", ret);
-			goto release_lock;
+			return ret;
 		}
 
 		ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
 					 FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL);
 		if (ret < 0) {
 			dev_err(dai->dev, "Error while setting IER0: %d\n", ret);
-			goto release_lock;
+			return ret;
 		}
 
 		/* clear DPATH RESET */
@@ -860,7 +859,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 					 0);
 		if (ret < 0) {
 			dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret);
-			goto release_lock;
+			return ret;
 		}
 
 		break;
@@ -873,14 +872,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 					 FSL_XCVR_EXT_CTRL_DMA_DIS(tx));
 		if (ret < 0) {
 			dev_err(dai->dev, "Failed to disable DMA: %d\n", ret);
-			goto release_lock;
+			return ret;
 		}
 
 		ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
 					 FSL_XCVR_IRQ_EARC_ALL, 0);
 		if (ret < 0) {
 			dev_err(dai->dev, "Failed to clear IER0: %d\n", ret);
-			goto release_lock;
+			return ret;
 		}
 
 		if (tx) {
@@ -891,7 +890,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 							FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
 				if (ret < 0) {
 					dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret);
-					goto release_lock;
+					return ret;
 				}
 				if (xcvr->soc_data->spdif_only)
 					break;
@@ -905,7 +904,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 				if (ret < 0) {
 					dev_err(dai->dev,
 						"Err updating ISR %d\n", ret);
-					goto release_lock;
+					return ret;
 				}
 				break;
 			}
@@ -916,8 +915,6 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
 		break;
 	}
 
-release_lock:
-	spin_unlock_irqrestore(&xcvr->lock, lock_flags);
 	return ret;
 }
 
@@ -1448,11 +1445,10 @@ static void reset_rx_work(struct work_struct *work)
 {
 	struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst);
 	struct device *dev = &xcvr->pdev->dev;
-	unsigned long lock_flags;
 	u32 ext_ctrl;
 
 	dev_dbg(dev, "reset rx path\n");
-	spin_lock_irqsave(&xcvr->lock, lock_flags);
+	guard(spinlock_irqsave)(&xcvr->lock);
 	regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl);
 
 	if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) {
@@ -1469,7 +1465,6 @@ static void reset_rx_work(struct work_struct *work)
 				   FSL_XCVR_EXT_CTRL_RX_DPTH_RESET,
 				   0);
 	}
-	spin_unlock_irqrestore(&xcvr->lock, lock_flags);
 }
 
 static irqreturn_t irq0_isr(int irq, void *devid)
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 06/11] ASoC: fsl_ssi: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for mutex locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/fsl_ssi.c | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index b2e1da1781ae..dc022976c982 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1218,13 +1218,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 	if (reg > 0x7f)
 		return;
 
-	mutex_lock(&fsl_ac97_data->ac97_reg_lock);
+	guard(mutex)(&fsl_ac97_data->ac97_reg_lock);
 
 	ret = clk_prepare_enable(fsl_ac97_data->clk);
 	if (ret) {
 		pr_err("ac97 write clk_prepare_enable failed: %d\n",
 			ret);
-		goto ret_unlock;
+		return;
 	}
 
 	lreg = reg <<  12;
@@ -1238,9 +1238,6 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 	udelay(100);
 
 	clk_disable_unprepare(fsl_ac97_data->clk);
-
-ret_unlock:
-	mutex_unlock(&fsl_ac97_data->ac97_reg_lock);
 }
 
 static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
@@ -1252,12 +1249,12 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
 	unsigned int lreg;
 	int ret;
 
-	mutex_lock(&fsl_ac97_data->ac97_reg_lock);
+	guard(mutex)(&fsl_ac97_data->ac97_reg_lock);
 
 	ret = clk_prepare_enable(fsl_ac97_data->clk);
 	if (ret) {
 		pr_err("ac97 read clk_prepare_enable failed: %d\n", ret);
-		goto ret_unlock;
+		return val;
 	}
 
 	lreg = (reg & 0x7f) <<  12;
@@ -1272,8 +1269,6 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
 
 	clk_disable_unprepare(fsl_ac97_data->clk);
 
-ret_unlock:
-	mutex_unlock(&fsl_ac97_data->ac97_reg_lock);
 	return val;
 }
 
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 05/11] ASoC: fsl_spdif: Use guard() for spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/fsl_spdif.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 1b9be85b34c2..ad1206ed9882 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -853,17 +853,15 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
 	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
 	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
 	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
-	unsigned long flags;
 	int ret = -EAGAIN;
 
-	spin_lock_irqsave(&ctrl->ctl_lock, flags);
+	guard(spinlock_irqsave)(&ctrl->ctl_lock);
 	if (ctrl->ready_buf) {
 		int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
 		memcpy(&ucontrol->value.iec958.subcode[0],
 				&ctrl->subcode[idx], SPDIF_UBITS_SIZE);
 		ret = 0;
 	}
-	spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
 
 	return ret;
 }
@@ -885,17 +883,15 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
 	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
 	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
 	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
-	unsigned long flags;
 	int ret = -EAGAIN;
 
-	spin_lock_irqsave(&ctrl->ctl_lock, flags);
+	guard(spinlock_irqsave)(&ctrl->ctl_lock);
 	if (ctrl->ready_buf) {
 		int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
 		memcpy(&ucontrol->value.bytes.data[0],
 				&ctrl->qsub[idx], SPDIF_QSUB_SIZE);
 		ret = 0;
 	}
-	spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
 
 	return ret;
 }
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 04/11] ASoC: fsl_esai: Use guard() for spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/fsl_esai.c | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index cde0b0c6c1ef..4a530a6c33f0 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -709,10 +709,9 @@ static void fsl_esai_hw_reset(struct work_struct *work)
 {
 	struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work);
 	bool tx = true, rx = false, enabled[2];
-	unsigned long lock_flags;
 	u32 tfcr, rfcr;
 
-	spin_lock_irqsave(&esai_priv->lock, lock_flags);
+	guard(spinlock_irqsave)(&esai_priv->lock);
 	/* Save the registers */
 	regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
 	regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
@@ -750,8 +749,6 @@ static void fsl_esai_hw_reset(struct work_struct *work)
 		fsl_esai_trigger_start(esai_priv, tx);
 	if (enabled[rx])
 		fsl_esai_trigger_start(esai_priv, rx);
-
-	spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
 }
 
 static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -759,7 +756,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
 {
 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
 	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-	unsigned long lock_flags;
 
 	esai_priv->channels[tx] = substream->runtime->channels;
 
@@ -767,16 +763,14 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		spin_lock_irqsave(&esai_priv->lock, lock_flags);
-		fsl_esai_trigger_start(esai_priv, tx);
-		spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
+		scoped_guard(spinlock_irqsave, &esai_priv->lock)
+			fsl_esai_trigger_start(esai_priv, tx);
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		spin_lock_irqsave(&esai_priv->lock, lock_flags);
-		fsl_esai_trigger_stop(esai_priv, tx);
-		spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
+		scoped_guard(spinlock_irqsave, &esai_priv->lock)
+			fsl_esai_trigger_stop(esai_priv, tx);
 		break;
 	default:
 		return -EINVAL;
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 03/11] ASoC: fsl_easrc: Use guard() for spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/fsl_easrc.c | 36 ++++++++++--------------------------
 1 file changed, 10 insertions(+), 26 deletions(-)

diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
index 114a6c0b6b73..edfd943197a0 100644
--- a/sound/soc/fsl/fsl_easrc.c
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -1025,7 +1025,6 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id)
 	struct fsl_easrc_ctx_priv *ctx_priv;
 	struct fsl_asrc_pair *ctx;
 	struct device *dev;
-	unsigned long lock_flags;
 	int ret;
 
 	if (!easrc)
@@ -1053,9 +1052,8 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id)
 	if (ret)
 		return ret;
 
-	spin_lock_irqsave(&easrc->lock, lock_flags);
-	ret = fsl_easrc_config_slot(easrc, ctx->index);
-	spin_unlock_irqrestore(&easrc->lock, lock_flags);
+	scoped_guard(spinlock_irqsave, &easrc->lock)
+		ret = fsl_easrc_config_slot(easrc, ctx->index);
 	if (ret)
 		return ret;
 
@@ -1301,13 +1299,12 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx)
 	enum asrc_pair_index index = ASRC_INVALID_PAIR;
 	struct fsl_asrc *easrc = ctx->asrc;
 	struct device *dev;
-	unsigned long lock_flags;
 	int ret = 0;
 	int i;
 
 	dev = &easrc->pdev->dev;
 
-	spin_lock_irqsave(&easrc->lock, lock_flags);
+	guard(spinlock_irqsave)(&easrc->lock);
 
 	for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) {
 		if (easrc->pair[i])
@@ -1331,8 +1328,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx)
 		easrc->channel_avail -= channels;
 	}
 
-	spin_unlock_irqrestore(&easrc->lock, lock_flags);
-
 	return ret;
 }
 
@@ -1343,7 +1338,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx)
  */
 static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx)
 {
-	unsigned long lock_flags;
 	struct fsl_asrc *easrc;
 
 	if (!ctx)
@@ -1351,14 +1345,12 @@ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx)
 
 	easrc = ctx->asrc;
 
-	spin_lock_irqsave(&easrc->lock, lock_flags);
+	guard(spinlock_irqsave)(&easrc->lock);
 
 	fsl_easrc_release_slot(easrc, ctx->index);
 
 	easrc->channel_avail += ctx->channels;
 	easrc->pair[ctx->index] = NULL;
-
-	spin_unlock_irqrestore(&easrc->lock, lock_flags);
 }
 
 /*
@@ -2292,15 +2284,13 @@ static int fsl_easrc_runtime_suspend(struct device *dev)
 {
 	struct fsl_asrc *easrc = dev_get_drvdata(dev);
 	struct fsl_easrc_priv *easrc_priv = easrc->private;
-	unsigned long lock_flags;
 
 	regcache_cache_only(easrc->regmap, true);
 
 	clk_disable_unprepare(easrc->mem_clk);
 
-	spin_lock_irqsave(&easrc->lock, lock_flags);
-	easrc_priv->firmware_loaded = 0;
-	spin_unlock_irqrestore(&easrc->lock, lock_flags);
+	scoped_guard(spinlock_irqsave, &easrc->lock)
+		easrc_priv->firmware_loaded = 0;
 
 	return 0;
 }
@@ -2311,7 +2301,6 @@ static int fsl_easrc_runtime_resume(struct device *dev)
 	struct fsl_easrc_priv *easrc_priv = easrc->private;
 	struct fsl_easrc_ctx_priv *ctx_priv;
 	struct fsl_asrc_pair *ctx;
-	unsigned long lock_flags;
 	int ret;
 	int i;
 
@@ -2323,13 +2312,11 @@ static int fsl_easrc_runtime_resume(struct device *dev)
 	regcache_mark_dirty(easrc->regmap);
 	regcache_sync(easrc->regmap);
 
-	spin_lock_irqsave(&easrc->lock, lock_flags);
-	if (easrc_priv->firmware_loaded) {
-		spin_unlock_irqrestore(&easrc->lock, lock_flags);
-		goto skip_load;
+	scoped_guard(spinlock_irqsave, &easrc->lock) {
+		if (easrc_priv->firmware_loaded)
+			return 0;
+		easrc_priv->firmware_loaded = 1;
 	}
-	easrc_priv->firmware_loaded = 1;
-	spin_unlock_irqrestore(&easrc->lock, lock_flags);
 
 	ret = fsl_easrc_get_firmware(easrc);
 	if (ret) {
@@ -2377,9 +2364,6 @@ static int fsl_easrc_runtime_resume(struct device *dev)
 			goto disable_mem_clk;
 	}
 
-skip_load:
-	return 0;
-
 disable_mem_clk:
 	clk_disable_unprepare(easrc->mem_clk);
 	return ret;
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 02/11] ASoC: fsl_audmix: Use guard() for spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/fsl_audmix.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
index 40a3b7432174..066239c64037 100644
--- a/sound/soc/fsl/fsl_audmix.c
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -280,7 +280,6 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 				  struct snd_soc_dai *dai)
 {
 	struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
-	unsigned long lock_flags;
 
 	/* Capture stream shall not be handled */
 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -290,16 +289,14 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		spin_lock_irqsave(&priv->lock, lock_flags);
-		priv->tdms |= BIT(dai->driver->id);
-		spin_unlock_irqrestore(&priv->lock, lock_flags);
+		scoped_guard(spinlock_irqsave, &priv->lock)
+			priv->tdms |= BIT(dai->driver->id);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		spin_lock_irqsave(&priv->lock, lock_flags);
-		priv->tdms &= ~BIT(dai->driver->id);
-		spin_unlock_irqrestore(&priv->lock, lock_flags);
+		scoped_guard(spinlock_irqsave, &priv->lock)
+			priv->tdms &= ~BIT(dai->driver->id);
 		break;
 	default:
 		return -EINVAL;
-- 
2.43.0



^ permalink raw reply related

* [PATCH v2 01/11] ASoC: fsl_asrc: Use guard() for spin locks
From: phucduc.bui @ 2026-06-15  9:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
	Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
	Pengutronix Kernel Team, linux-sound, linux-kernel,
	linux-arm-kernel, imx, linuxppc-dev, bui duc phuc
In-Reply-To: <20260615093824.115751-1-phucduc.bui@gmail.com>

From: bui duc phuc <phucduc.bui@gmail.com>

Clean up the code using guard() for spin locks.
Merely code refactoring, and no behavior change.

Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
 sound/soc/fsl/fsl_asrc.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 5fda9b647c70..0b28bcfa47fe 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -222,10 +222,9 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
 	enum asrc_pair_index index = ASRC_INVALID_PAIR;
 	struct fsl_asrc *asrc = pair->asrc;
 	struct device *dev = &asrc->pdev->dev;
-	unsigned long lock_flags;
 	int i, ret = 0;
 
-	spin_lock_irqsave(&asrc->lock, lock_flags);
+	guard(spinlock_irqsave)(&asrc->lock);
 
 	for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
 		if (asrc->pair[i] != NULL)
@@ -250,8 +249,6 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
 		pair->index = index;
 	}
 
-	spin_unlock_irqrestore(&asrc->lock, lock_flags);
-
 	return ret;
 }
 
@@ -265,19 +262,16 @@ static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
 {
 	struct fsl_asrc *asrc = pair->asrc;
 	enum asrc_pair_index index = pair->index;
-	unsigned long lock_flags;
 
 	/* Make sure the pair is disabled */
 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
 			   ASRCTR_ASRCEi_MASK(index), 0);
 
-	spin_lock_irqsave(&asrc->lock, lock_flags);
+	guard(spinlock_irqsave)(&asrc->lock);
 
 	asrc->channel_avail += pair->channels;
 	asrc->pair[index] = NULL;
 	pair->error = 0;
-
-	spin_unlock_irqrestore(&asrc->lock, lock_flags);
 }
 
 /**
-- 
2.43.0



^ permalink raw reply related


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