* [PATCH 60/78] ASoC: codecs: tscs42xx: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-18 11:08 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260618110827.232983-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/codecs/tscs42xx.c | 44 ++++++++++++-------------------------
1 file changed, 14 insertions(+), 30 deletions(-)
diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c
index dba581857920..66810b992935 100644
--- a/sound/soc/codecs/tscs42xx.c
+++ b/sound/soc/codecs/tscs42xx.c
@@ -210,23 +210,21 @@ static int power_up_audio_plls(struct snd_soc_component *component)
return ret;
}
- mutex_lock(&tscs42xx->pll_lock);
+ guard(mutex)(&tscs42xx->pll_lock);
ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val);
if (ret < 0) {
dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret);
- goto exit;
+ return ret;
}
if (!plls_locked(component)) {
dev_err(component->dev, "Failed to lock plls\n");
ret = -ENOMSG;
- goto exit;
+ return ret;
}
ret = 0;
-exit:
- mutex_unlock(&tscs42xx->pll_lock);
return ret;
}
@@ -236,26 +234,24 @@ static int power_down_audio_plls(struct snd_soc_component *component)
struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
int ret;
- mutex_lock(&tscs42xx->pll_lock);
+ guard(mutex)(&tscs42xx->pll_lock);
ret = snd_soc_component_update_bits(component, R_PLLCTL1C,
RM_PLLCTL1C_PDB_PLL1,
RV_PLLCTL1C_PDB_PLL1_DISABLE);
if (ret < 0) {
dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret);
- goto exit;
+ return ret;
}
ret = snd_soc_component_update_bits(component, R_PLLCTL1C,
RM_PLLCTL1C_PDB_PLL2,
RV_PLLCTL1C_PDB_PLL2_DISABLE);
if (ret < 0) {
dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret);
- goto exit;
+ return ret;
}
ret = 0;
-exit:
- mutex_unlock(&tscs42xx->pll_lock);
return ret;
}
@@ -269,13 +265,11 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol,
(struct coeff_ram_ctl *)kcontrol->private_value;
struct soc_bytes_ext *params = &ctl->bytes_ext;
- mutex_lock(&tscs42xx->coeff_ram_lock);
+ guard(mutex)(&tscs42xx->coeff_ram_lock);
memcpy(ucontrol->value.bytes.data,
&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max);
- mutex_unlock(&tscs42xx->coeff_ram_lock);
-
return 0;
}
@@ -290,14 +284,14 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol,
unsigned int coeff_cnt = params->max / COEFF_SIZE;
int ret;
- mutex_lock(&tscs42xx->coeff_ram_lock);
+ guard(mutex)(&tscs42xx->coeff_ram_lock);
tscs42xx->coeff_ram_synced = false;
memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE],
ucontrol->value.bytes.data, params->max);
- mutex_lock(&tscs42xx->pll_lock);
+ guard(mutex)(&tscs42xx->pll_lock);
if (plls_locked(component)) {
ret = write_coeff_ram(component, tscs42xx->coeff_ram,
@@ -305,16 +299,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol,
if (ret < 0) {
dev_err(component->dev,
"Failed to flush coeff ram cache (%d)\n", ret);
- goto exit;
+ return ret;
}
tscs42xx->coeff_ram_synced = true;
}
ret = 0;
-exit:
- mutex_unlock(&tscs42xx->pll_lock);
-
- mutex_unlock(&tscs42xx->coeff_ram_lock);
return ret;
}
@@ -385,19 +375,17 @@ static int dac_event(struct snd_soc_dapm_widget *w,
struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
int ret;
- mutex_lock(&tscs42xx->coeff_ram_lock);
+ guard(mutex)(&tscs42xx->coeff_ram_lock);
if (!tscs42xx->coeff_ram_synced) {
ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00,
COEFF_RAM_COEFF_COUNT);
if (ret < 0)
- goto exit;
+ return ret;
tscs42xx->coeff_ram_synced = true;
}
ret = 0;
-exit:
- mutex_unlock(&tscs42xx->coeff_ram_lock);
return ret;
}
@@ -926,12 +914,10 @@ static int setup_sample_rate(struct snd_soc_component *component,
return ret;
}
- mutex_lock(&tscs42xx->audio_params_lock);
+ guard(mutex)(&tscs42xx->audio_params_lock);
tscs42xx->samplerate = rate;
- mutex_unlock(&tscs42xx->audio_params_lock);
-
return 0;
}
@@ -1253,12 +1239,10 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai,
return ret;
}
- mutex_lock(&tscs42xx->audio_params_lock);
+ guard(mutex)(&tscs42xx->audio_params_lock);
tscs42xx->bclk_ratio = ratio;
- mutex_unlock(&tscs42xx->audio_params_lock);
-
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 59/78] ASoC: codecs: tlv320dac33: Use guard() for mutex & spin locks
From: phucduc.bui @ 2026-06-18 11:08 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260618110827.232983-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/codecs/tlv320dac33.c | 77 ++++++++++++++--------------------
1 file changed, 31 insertions(+), 46 deletions(-)
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 223c49dfc450..85c42a4efbf7 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -236,13 +236,10 @@ static int dac33_write_locked(struct snd_soc_component *component, unsigned int
unsigned int value)
{
struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
- int ret;
- mutex_lock(&dac33->mutex);
- ret = dac33_write(component, reg, value);
- mutex_unlock(&dac33->mutex);
+ guard(mutex)(&dac33->mutex);
- return ret;
+ return dac33_write(component, reg, value);
}
#define DAC33_I2C_ADDR_AUTOINC 0x80
@@ -365,13 +362,13 @@ static int dac33_hard_power(struct snd_soc_component *component, int power)
struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
int ret = 0;
- mutex_lock(&dac33->mutex);
+ guard(mutex)(&dac33->mutex);
/* Safety check */
if (unlikely(power == dac33->chip_power)) {
dev_dbg(component->dev, "Trying to set the same power state: %s\n",
power ? "ON" : "OFF");
- goto exit;
+ return ret;
}
if (power) {
@@ -380,7 +377,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power)
if (ret != 0) {
dev_err(component->dev,
"Failed to enable supplies: %d\n", ret);
- goto exit;
+ return ret;
}
if (dac33->reset_gpiod) {
@@ -388,7 +385,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power)
if (ret < 0) {
dev_err(&dac33->i2c->dev,
"Failed to set reset GPIO: %d\n", ret);
- goto exit;
+ return ret;
}
}
@@ -400,7 +397,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power)
if (ret < 0) {
dev_err(&dac33->i2c->dev,
"Failed to set reset GPIO: %d\n", ret);
- goto exit;
+ return ret;
}
}
@@ -409,14 +406,12 @@ static int dac33_hard_power(struct snd_soc_component *component, int power)
if (ret != 0) {
dev_err(component->dev,
"Failed to disable supplies: %d\n", ret);
- goto exit;
+ return ret;
}
dac33->chip_power = 0;
}
-exit:
- mutex_unlock(&dac33->mutex);
return ret;
}
@@ -659,7 +654,6 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
{
struct snd_soc_component *component = dac33->component;
unsigned int delay;
- unsigned long flags;
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
@@ -667,10 +661,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
DAC33_THRREG(dac33->nsample));
/* Take the timestamps */
- spin_lock_irqsave(&dac33->lock, flags);
- dac33->t_stamp2 = ktime_to_us(ktime_get());
- dac33->t_stamp1 = dac33->t_stamp2;
- spin_unlock_irqrestore(&dac33->lock, flags);
+ scoped_guard(spinlock_irqsave, &dac33->lock) {
+ dac33->t_stamp2 = ktime_to_us(ktime_get());
+ dac33->t_stamp1 = dac33->t_stamp2;
+ }
dac33_write16(component, DAC33_PREFILL_MSB,
DAC33_THRREG(dac33->alarm_threshold));
@@ -682,11 +676,11 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
break;
case DAC33_FIFO_MODE7:
/* Take the timestamp */
- spin_lock_irqsave(&dac33->lock, flags);
- dac33->t_stamp1 = ktime_to_us(ktime_get());
- /* Move back the timestamp with drain time */
- dac33->t_stamp1 -= dac33->mode7_us_to_lthr;
- spin_unlock_irqrestore(&dac33->lock, flags);
+ scoped_guard(spinlock_irqsave, &dac33->lock) {
+ dac33->t_stamp1 = ktime_to_us(ktime_get());
+ /* Move back the timestamp with drain time */
+ dac33->t_stamp1 -= dac33->mode7_us_to_lthr;
+ }
dac33_write16(component, DAC33_PREFILL_MSB,
DAC33_THRREG(DAC33_MODE7_MARGIN));
@@ -704,14 +698,12 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33)
{
struct snd_soc_component *component = dac33->component;
- unsigned long flags;
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
/* Take the timestamp */
- spin_lock_irqsave(&dac33->lock, flags);
- dac33->t_stamp2 = ktime_to_us(ktime_get());
- spin_unlock_irqrestore(&dac33->lock, flags);
+ scoped_guard(spinlock_irqsave, &dac33->lock)
+ dac33->t_stamp2 = ktime_to_us(ktime_get());
dac33_write16(component, DAC33_NSAMPLE_MSB,
DAC33_THRREG(dac33->nsample));
@@ -735,7 +727,7 @@ static void dac33_work(struct work_struct *work)
dac33 = container_of(work, struct tlv320dac33_priv, work);
component = dac33->component;
- mutex_lock(&dac33->mutex);
+ guard(mutex)(&dac33->mutex);
switch (dac33->state) {
case DAC33_PREFILL:
dac33->state = DAC33_PLAYBACK;
@@ -757,18 +749,15 @@ static void dac33_work(struct work_struct *work)
dac33_write(component, DAC33_FIFO_CTRL_A, reg);
break;
}
- mutex_unlock(&dac33->mutex);
}
static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
{
struct snd_soc_component *component = dev;
struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
- unsigned long flags;
- spin_lock_irqsave(&dac33->lock, flags);
- dac33->t_stamp1 = ktime_to_us(ktime_get());
- spin_unlock_irqrestore(&dac33->lock, flags);
+ scoped_guard(spinlock_irqsave, &dac33->lock)
+ dac33->t_stamp1 = ktime_to_us(ktime_get());
/* Do not schedule the workqueue in Mode7 */
if (dac33->fifo_mode != DAC33_FIFO_MODE7)
@@ -902,14 +891,13 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
return -EINVAL;
}
- mutex_lock(&dac33->mutex);
+ guard(mutex)(&dac33->mutex);
if (!dac33->chip_power) {
/*
* Chip is not powered yet.
* Do the init in the dac33_set_bias_level later.
*/
- mutex_unlock(&dac33->mutex);
return 0;
}
@@ -1053,8 +1041,6 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
break;
}
- mutex_unlock(&dac33->mutex);
-
return 0;
}
@@ -1156,16 +1142,15 @@ static snd_pcm_sframes_t dac33_dai_delay(
unsigned int time_delta, uthr;
int samples_out, samples_in, samples;
snd_pcm_sframes_t delay = 0;
- unsigned long flags;
switch (dac33->fifo_mode) {
case DAC33_FIFO_BYPASS:
break;
case DAC33_FIFO_MODE1:
- spin_lock_irqsave(&dac33->lock, flags);
- t0 = dac33->t_stamp1;
- t1 = dac33->t_stamp2;
- spin_unlock_irqrestore(&dac33->lock, flags);
+ scoped_guard(spinlock_irqsave, &dac33->lock) {
+ t0 = dac33->t_stamp1;
+ t1 = dac33->t_stamp2;
+ }
t_now = ktime_to_us(ktime_get());
/* We have not started to fill the FIFO yet, delay is 0 */
@@ -1230,10 +1215,10 @@ static snd_pcm_sframes_t dac33_dai_delay(
}
break;
case DAC33_FIFO_MODE7:
- spin_lock_irqsave(&dac33->lock, flags);
- t0 = dac33->t_stamp1;
- uthr = dac33->uthr;
- spin_unlock_irqrestore(&dac33->lock, flags);
+ scoped_guard(spinlock_irqsave, &dac33->lock) {
+ t0 = dac33->t_stamp1;
+ uthr = dac33->uthr;
+ }
t_now = ktime_to_us(ktime_get());
/* We have not started to fill the FIFO yet, delay is 0 */
--
2.43.0
^ permalink raw reply related
* [PATCH 58/78] ASoC: codecs: tas675x: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-18 11:08 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260618110827.232983-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/codecs/tas675x.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c
index 6f89a422f3c6..c44bf9144e44 100644
--- a/sound/soc/codecs/tas675x.c
+++ b/sound/soc/codecs/tas675x.c
@@ -162,7 +162,7 @@ static int tas675x_dsp_mem_write(struct tas675x_priv *tas, u8 page, u8 reg, u32
* DSP regs in a different book, therefore block
* regmap access before completion.
*/
- mutex_lock(&tas->io_lock);
+ guard(mutex)(&tas->io_lock);
ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP);
if (ret)
@@ -176,7 +176,6 @@ static int tas675x_dsp_mem_write(struct tas675x_priv *tas, u8 page, u8 reg, u32
out:
__tas675x_select_book(tas, TAS675X_BOOK_DEFAULT);
- mutex_unlock(&tas->io_lock);
return ret;
}
@@ -191,7 +190,7 @@ static int tas675x_dsp_mem_read(struct tas675x_priv *tas, u8 page, u8 reg, u32 *
* DSP regs in a different book, therefore block
* regmap access before completion.
*/
- mutex_lock(&tas->io_lock);
+ guard(mutex)(&tas->io_lock);
ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP);
if (ret)
@@ -211,7 +210,6 @@ static int tas675x_dsp_mem_read(struct tas675x_priv *tas, u8 page, u8 reg, u32 *
out:
__tas675x_select_book(tas, TAS675X_BOOK_DEFAULT);
- mutex_unlock(&tas->io_lock);
return ret;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 57/78] ASoC: codecs: tas5805m: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-18 11:08 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260617103235.449609-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/codecs/tas5805m.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c
index bcc8cab8d667..f21670dab0f4 100644
--- a/sound/soc/codecs/tas5805m.c
+++ b/sound/soc/codecs/tas5805m.c
@@ -230,10 +230,9 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol,
struct tas5805m_priv *tas5805m =
snd_soc_component_get_drvdata(component);
- mutex_lock(&tas5805m->lock);
+ guard(mutex)(&tas5805m->lock);
ucontrol->value.integer.value[0] = tas5805m->vol[0];
ucontrol->value.integer.value[1] = tas5805m->vol[1];
- mutex_unlock(&tas5805m->lock);
return 0;
}
@@ -255,7 +254,7 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol,
volume_is_valid(ucontrol->value.integer.value[1])))
return -EINVAL;
- mutex_lock(&tas5805m->lock);
+ guard(mutex)(&tas5805m->lock);
if (tas5805m->vol[0] != ucontrol->value.integer.value[0] ||
tas5805m->vol[1] != ucontrol->value.integer.value[1]) {
tas5805m->vol[0] = ucontrol->value.integer.value[0];
@@ -267,7 +266,6 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol,
tas5805m_refresh(tas5805m);
ret = 1;
}
- mutex_unlock(&tas5805m->lock);
return ret;
}
@@ -332,7 +330,7 @@ static void do_work(struct work_struct *work)
dev_dbg(&tas5805m->i2c->dev, "DSP startup\n");
- mutex_lock(&tas5805m->lock);
+ guard(mutex)(&tas5805m->lock);
/* We mustn't issue any I2C transactions until the I2S
* clock is stable. Furthermore, we must allow a 5ms
* delay after the first set of register writes to
@@ -345,7 +343,6 @@ static void do_work(struct work_struct *work)
tas5805m->is_powered = true;
tas5805m_refresh(tas5805m);
- mutex_unlock(&tas5805m->lock);
}
static int tas5805m_dac_event(struct snd_soc_dapm_widget *w,
@@ -362,7 +359,7 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w,
dev_dbg(component->dev, "DSP shutdown\n");
cancel_work_sync(&tas5805m->work);
- mutex_lock(&tas5805m->lock);
+ guard(mutex)(&tas5805m->lock);
if (tas5805m->is_powered) {
tas5805m->is_powered = false;
@@ -379,7 +376,6 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w,
regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ);
}
- mutex_unlock(&tas5805m->lock);
}
return 0;
@@ -414,14 +410,13 @@ static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction)
struct tas5805m_priv *tas5805m =
snd_soc_component_get_drvdata(component);
- mutex_lock(&tas5805m->lock);
+ guard(mutex)(&tas5805m->lock);
dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n",
mute, tas5805m->is_powered);
tas5805m->is_muted = mute;
if (tas5805m->is_powered)
tas5805m_refresh(tas5805m);
- mutex_unlock(&tas5805m->lock);
return 0;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net v3] net: airoha: Fix skb->priority underflow in airoha_dev_select_queue()
From: Wayen Yan @ 2026-06-18 14:10 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Lorenzo Bianconi, netdev, horms, pabeni, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20260617161951.52abe413@kernel.org>
On Wed, Jun 18, 2026 at 08:19:51AM +0200, Jakub Kicinski wrote:
> Hi Lorenzo, is there a reason we're subtracting 1 here in the first
> place? Could be just me, but may be worth adding a comment here.
>
> Please respin with some sort of an explanation..
Hi Jakub,
The (priority - 1) mapping predates my involvement — I only addressed
the underflow bug when skb->priority is 0, where the unsigned
subtraction wraps and routes best-effort packets to the highest-priority
queue.
Lorenzo, could you clarify the intended priority-to-queue mapping so
I can add a proper comment in the respin?
Regards,
Wayen
^ permalink raw reply
* Re: [PATCH 05/78] ASoC: codecs: aw87390: Use guard() for mutex locks
From: Clint @ 2026-06-18 10:19 UTC (permalink / raw)
To: majordomo
Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Cheng-Yi Chiang, Tzung-Bi Shih, Guenter Roeck, Benson Leung,
David Rhodes, Richard Fitzgerald, povik+lin, Charles Keepax,
Support Opensource, Nick Li, Herve Codina, Srinivas Kandagatla,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Oder Chiou, Lars-Peter Clausen,
nuno.sa, Steven Eckhoff, patches, chrome-platform, asahi,
linux-arm-msm, linux-sound, linux-kernel, linux-arm-kernel,
linux-mediatek, bui duc phuc
In-Reply-To: <20260617103235.449609-6-phucduc.bui@gmail.com>
unsubscribe linux-kernel
unsubscribe linux-sound
unsubscribe linux-arm-kernel
Clint
-------- Original Message --------
On Wednesday, 06/17/26 at 20:34 phucduc.bui@gmail.com wrote:
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/codecs/aw87390.c | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c
index 020213e0ca4b..88110e720f85 100644
--- a/sound/soc/codecs/aw87390.c
+++ b/sound/soc/codecs/aw87390.c
@@ -225,11 +225,10 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol,
struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
int ret;
- mutex_lock(&aw87390->lock);
+ guard(mutex)(&aw87390->lock);
ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]);
if (ret) {
dev_dbg(codec->dev, "profile index does not change\n");
- mutex_unlock(&aw87390->lock);
return 0;
}
@@ -238,8 +237,6 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol,
aw87390_power_on(aw87390->aw_pa);
}
- mutex_unlock(&aw87390->lock);
-
return 1;
}
@@ -280,14 +277,12 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390)
return ret;
}
- mutex_lock(&aw87390->lock);
+ guard(mutex)(&aw87390->lock);
ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg);
if (ret)
dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n");
- mutex_unlock(&aw87390->lock);
-
return ret;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net v3] net: airoha: Fix skb->priority underflow in airoha_dev_select_queue()
From: Lorenzo Bianconi @ 2026-06-18 10:03 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Wayen Yan, netdev, horms, pabeni, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20260617161951.52abe413@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 2515 bytes --]
> On Sun, 14 Jun 2026 07:30:54 +0800 Wayen Yan wrote:
> > In airoha_dev_select_queue(), the expression:
> >
> > queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES;
> >
> > implicitly converts to unsigned arithmetic: when skb->priority is 0
> > (the default for unclassified traffic), (0u - 1u) wraps to UINT_MAX,
> > and UINT_MAX % 8 = 7, routing default best-effort packets to the
> > highest-priority QoS queue. This causes QoS inversion where the
> > majority of traffic on a PON gateway starves actual high-priority
> > flows (VoIP, gaming, etc.).
> >
> > Fix by guarding the subtraction: when priority is 0, map to queue 0
> > (lowest priority), otherwise apply the original (priority - 1) % 8
> > mapping.
> >
> > Fixes: 2b288b81560b ("net: airoha: Introduce ndo_select_queue callback")
> > Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > Reviewed-by: Joe Damato <joe@dama.to>
> > Signed-off-by: Wayen Yan <win847@gmail.com>
> > ---
> > drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index 31cdb11cd7..d476ef83c3 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> > @@ -1933,7 +1933,7 @@ static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
> > */
> > channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id;
> > channel = channel % AIROHA_NUM_QOS_CHANNELS;
> > - queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
> > + queue = skb->priority ? (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES : 0;
>
> Hi Lorenzo, is there a reason we're subtracting 1 here in the first
> place? Could be just me, but may be worth adding a comment here.
>
> Intuitively if we are "narrowing" 16 prios to 8 queues it'd make most
> sense to group the adjacent ones -- divide by two.
>
> Please respin with some sort of an explanation..
IIRC this is a leftover of the ETS offload support.
I agree it is righ to just do:
queue = skb->priority % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
@Wayen: can you please respin fixing the issue? Please add even my Acked-by:
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Regards,
Lorenzo
>
> > queue = channel * AIROHA_NUM_QOS_QUEUES + queue;
> >
> > return queue < dev->num_tx_queues ? queue : 0;
> --
> pw-bot: cr
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH net] net: airoha: Add retry mechanism to airoha_qdma_set_trtcm_param()
From: Leto Liu (刘涛) @ 2026-06-18 7:52 UTC (permalink / raw)
To: Lorenzo Bianconi, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, netdev@vger.kernel.org,
Brown Huang (黃柏翰)
In-Reply-To: <aiqHgJ6FiAMu5gmF@lore-desk>
>I think the issue reported by sashiko in [0] is valid and it needs to be addressed in v2, but since I am not the original author of the patch, I will let Brown or Leto comment on it.
>
>Regards,
>Lorenzo
>
Thanks for the careful review — you are absolutely right.
In the current version, the immediate call to airoha_qdma_get_trtcm_param() can indeed clobber the write command we just issued. That helper programs REG_TRTCM_CFG_PARAM() to start a read transaction, so without first waiting for TRTCM_PARAM_RW_DONE_MASK after the write, we may overwrite the command register before the hardware has latched/finished the write. The added wmb() only enforces the ordering between the DATA and CFG writes, but it does not guarantee the write transaction has completed.
I will address this by restoring the DONE polling after the write, and only then performing the read-back verification (i.e. “write → poll DONE → read-back → compare”, with retries as needed). Also, to avoid clobbering the val argument, the poll will use a separate temporary variable.
I have revised the patch into a v2 version based on this approach, and it is currently under testing and validation. Since this is an intermittent issue, I am running long-duration stability tests. I expect to provide the v2 patch by next Monday.
Best Regards, Leto
^ permalink raw reply
* Re: [PATCH net] net: airoha: Fix TX scheduler queue mask loop upper bound
From: Lorenzo Bianconi @ 2026-06-18 7:42 UTC (permalink / raw)
To: Wayen Yan
Cc: netdev, horms, pabeni, kuba, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek
In-Reply-To: <178166704952.2212140.11002626760717132754@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1846 bytes --]
> In airoha_qdma_set_chan_tx_sched(), the loop clearing queue mask was
> using AIROHA_NUM_TX_RING (32) instead of AIROHA_NUM_QOS_QUEUES (8).
>
> Each channel has 8 queues, and TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)
> computes BIT(i + (channel * 8)). With i ranging 0..31, this causes:
> - channel 0: clears bit 0..31 (all 4 channels) instead of 0..7
> - channel 1: clears bit 8..31 (channels 1-3) instead of 8..15
> - channel 2: clears bit 16..31 (channels 2-3) instead of 16..23
> - channel 3: clears bit 24..31 (channel 3 only) - correct by accident
>
> While BIT(32+) on arm64 produces 64-bit values truncated to 0 in u32
> mask parameter, the loop still incorrectly clears queues within the
> same channel beyond queue 7.
>
> Fix by using AIROHA_NUM_QOS_QUEUES (8) as the loop upper bound.
I guess it is ok now if you respin this patch on top of net branch.
If so, please add my Acked-by.
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
>
> Fixes: ef1ca9271313 ("net: airoha: Add sched HTB offload support")
> Signed-off-by: Wayen Yan <win847@gmail.com>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 31cdb11cd7..a1eda13400 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -2217,7 +2217,7 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *dev,
> struct airoha_gdm_port *port = netdev_priv(dev);
> int i;
>
> - for (i = 0; i < AIROHA_NUM_TX_RING; i++)
> + for (i = 0; i < AIROHA_NUM_QOS_QUEUES; i++)
> airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel),
> TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
>
> --
> 2.51.0
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH] net: airoha: Stop TX queues on error path in airoha_dev_open
From: Lorenzo Bianconi @ 2026-06-18 7:33 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Wayen Yan, netdev, horms, pabeni, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20260617164448.31e189bc@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 558 bytes --]
> On Tue, 16 Jun 2026 18:50:39 +0800 Wayen Yan wrote:
> > In airoha_dev_open(), if airoha_set_vip_for_gdm_port() fails after
> > netif_tx_start_all_queues() has been called, the TX queues remain
> > started while the device configuration is incomplete. This leaves
> > the device in an inconsistent state where packets could be
> > transmitted before the VIP/IFC port configuration is complete.
>
> Not sure if this was superseded by another posting but FWIW
> this posting did not apply.
I think we do not need this patch.
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH net] net: airoha: fix BQL underflow and UAF in shared QDMA TX ring
From: Lorenzo Bianconi @ 2026-06-18 6:13 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Wayen Yan, linux-arm-kernel, linux-mediatek, netdev,
Lorenzo Bianconi
When multiple netdevs share a QDMA TX ring and one device is stopped,
netdev_tx_reset_subqueue() zeroes that device's BQL counters while its
pending skbs remain in the shared HW TX ring. When NAPI later completes
those skbs via netdev_tx_completed_queue(), the already-zeroed
dql->num_queued counter underflows.
Moreover, in the airoha_remove() path, netdevs are unregistered
sequentially while skbs from previously unregistered netdevs may still
reference freed net_device memory via skb->dev, causing a use-after-free
during BQL accounting.
Fix both issues:
- Remove netdev_tx_reset_subqueue() from airoha_dev_stop() so pending
skbs are completed naturally by NAPI with proper BQL accounting.
- Add netdev_tx_completed_queue() in airoha_qdma_cleanup_tx_queue()
to properly account for skbs freed during queue teardown.
- Introduce airoha_qdma_tx_disable() to stop TX on all registered
netdevs for a given QDMA instance under RTNL lock.
- Move DMA engine start/stop into probe/remove and
airoha_qdma_cleanup(), ensuring TX queues are cleaned up while all
netdevs are still registered and skb->dev is valid.
Fixes: 6df0488dc9dd ("net: airoha: fix BQL accounting in airoha_qdma_cleanup_tx_queue()")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 95 ++++++++++++++++++++++++--------
1 file changed, 72 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 64dde6464f3f..4d6a061cd779 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1004,6 +1004,7 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)
e = &q->entry[index];
skb = e->skb;
+ e->skb = NULL;
dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
DMA_TO_DEVICE);
@@ -1147,6 +1148,42 @@ static int airoha_qdma_init_tx(struct airoha_qdma *qdma)
return 0;
}
+static void airoha_qdma_tx_disable(struct airoha_qdma *qdma)
+{
+ struct airoha_eth *eth = qdma->eth;
+ int i;
+
+ /* Protect netdev->reg_state and netif_tx_disable() calls. */
+ rtnl_lock();
+
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *port = eth->ports[i];
+ int j;
+
+ if (!port)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ struct net_device *netdev;
+
+ if (!dev)
+ continue;
+
+ if (dev->qdma != qdma)
+ continue;
+
+ netdev = netdev_from_priv(dev);
+ if (netdev->reg_state != NETREG_REGISTERED)
+ continue;
+
+ netif_tx_disable(netdev);
+ }
+ }
+
+ rtnl_unlock();
+}
+
static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q)
{
struct airoha_qdma *qdma = q->qdma;
@@ -1158,13 +1195,20 @@ static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q)
for (i = 0; i < q->ndesc; i++) {
struct airoha_queue_entry *e = &q->entry[i];
struct airoha_qdma_desc *desc = &q->desc[i];
+ struct sk_buff *skb = e->skb;
if (!e->dma_addr)
continue;
dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
DMA_TO_DEVICE);
- dev_kfree_skb_any(e->skb);
+ if (skb) {
+ struct netdev_queue *txq;
+
+ txq = skb_get_tx_queue(skb->dev, skb);
+ netdev_tx_completed_queue(txq, 1, skb->len);
+ dev_kfree_skb_any(skb);
+ }
e->dma_addr = 0;
e->skb = NULL;
list_add_tail(&e->list, &q->tx_list);
@@ -1527,6 +1571,23 @@ static void airoha_qdma_cleanup(struct airoha_qdma *qdma)
{
int i;
+ if (test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state)) {
+ u32 status;
+
+ airoha_qdma_tx_disable(qdma);
+
+ airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
+ GLOBAL_CFG_TX_DMA_EN_MASK |
+ GLOBAL_CFG_RX_DMA_EN_MASK);
+ if (read_poll_timeout(airoha_qdma_rr, status,
+ !(status & (GLOBAL_CFG_TX_DMA_BUSY_MASK |
+ GLOBAL_CFG_RX_DMA_BUSY_MASK)),
+ USEC_PER_MSEC, 50 * USEC_PER_MSEC, true,
+ qdma, REG_QDMA_GLOBAL_CFG))
+ dev_warn(qdma->eth->dev,
+ "QDMA DMA engine busy timeout\n");
+ }
+
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
if (!qdma->q_rx[i].ndesc)
continue;
@@ -1837,9 +1898,6 @@ static int airoha_dev_open(struct net_device *netdev)
}
port->users++;
- airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
- GLOBAL_CFG_TX_DMA_EN_MASK |
- GLOBAL_CFG_RX_DMA_EN_MASK);
qdma->users++;
if (!airoha_is_lan_gdm_dev(dev) &&
@@ -1880,12 +1938,9 @@ static int airoha_dev_stop(struct net_device *netdev)
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = dev->qdma;
- int i;
netif_tx_disable(netdev);
airoha_set_vip_for_gdm_port(dev, false);
- for (i = 0; i < netdev->num_tx_queues; i++)
- netdev_tx_reset_subqueue(netdev, i);
if (--port->users)
airoha_set_port_mtu(dev->eth, port);
@@ -1893,19 +1948,7 @@ static int airoha_dev_stop(struct net_device *netdev)
airoha_set_gdm_port_fwd_cfg(qdma->eth,
REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
-
- if (!--qdma->users) {
- airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
- GLOBAL_CFG_TX_DMA_EN_MASK |
- GLOBAL_CFG_RX_DMA_EN_MASK);
-
- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
- if (!qdma->q_tx[i].ndesc)
- continue;
-
- airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]);
- }
- }
+ qdma->users--;
return 0;
}
@@ -3413,8 +3456,12 @@ static int airoha_probe(struct platform_device *pdev)
if (err)
goto error_netdev_free;
- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
+ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) {
airoha_qdma_start_napi(ð->qdma[i]);
+ airoha_qdma_set(ð->qdma[i], REG_QDMA_GLOBAL_CFG,
+ GLOBAL_CFG_TX_DMA_EN_MASK |
+ GLOBAL_CFG_RX_DMA_EN_MASK);
+ }
for_each_child_of_node(pdev->dev.of_node, np) {
if (!of_device_is_compatible(np, "airoha,eth-mac"))
@@ -3440,6 +3487,8 @@ static int airoha_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
airoha_qdma_stop_napi(ð->qdma[i]);
+ airoha_hw_cleanup(eth);
+
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
int j;
@@ -3461,7 +3510,6 @@ static int airoha_probe(struct platform_device *pdev)
}
airoha_metadata_dst_free(port);
}
- airoha_hw_cleanup(eth);
error_netdev_free:
free_netdev(eth->napi_dev);
platform_set_drvdata(pdev, NULL);
@@ -3477,6 +3525,8 @@ static void airoha_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
airoha_qdma_stop_napi(ð->qdma[i]);
+ airoha_hw_cleanup(eth);
+
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
int j;
@@ -3497,7 +3547,6 @@ static void airoha_remove(struct platform_device *pdev)
}
airoha_metadata_dst_free(port);
}
- airoha_hw_cleanup(eth);
free_netdev(eth->napi_dev);
platform_set_drvdata(pdev, NULL);
---
base-commit: 7d8297e26b4e20b5d1c3c3fe51fe81a1c7fbc823
change-id: 20260618-airoha-bql-fixes-f57b2d108573
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply related
* Re: [PATCH] wifi: mt76: mt7925: support new WoW pattern TLV
From: kernel test robot @ 2026-06-18 6:00 UTC (permalink / raw)
To: Sean Wang, Felix Fietkau, Lorenzo Bianconi
Cc: oe-kbuild-all, linux-wireless, linux-mediatek, Sean Wang,
Stella Liu
In-Reply-To: <20260615212137.477893-1-sean.wang@kernel.org>
Hi Sean,
kernel test robot noticed the following build warnings:
[auto build test WARNING on wireless-next/main]
[also build test WARNING on wireless/main linus/master v7.1 next-20260616]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sean-Wang/wifi-mt76-mt7925-support-new-WoW-pattern-TLV/20260616-052424
base: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
patch link: https://lore.kernel.org/r/20260615212137.477893-1-sean.wang%40kernel.org
patch subject: [PATCH] wifi: mt76: mt7925: support new WoW pattern TLV
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260618/202606181323.heBYmqMQ-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260618/202606181323.heBYmqMQ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606181323.heBYmqMQ-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/net/wireless/mediatek/mt76/mt7925/mcu.c:15:13: warning: 'mt7925_mcu_wow_pattern_old_tlv' defined but not used [-Wunused-function]
15 | static bool mt7925_mcu_wow_pattern_old_tlv(struct mt76_dev *dev)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vim +/mt7925_mcu_wow_pattern_old_tlv +15 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
14
> 15 static bool mt7925_mcu_wow_pattern_old_tlv(struct mt76_dev *dev)
16 {
17 const char *fw_version = dev->hw->wiphy->fw_version;
18 const char *build_date = strrchr(fw_version, '-');
19
20 if (!is_mt7925(dev))
21 return false;
22
23 if (!build_date)
24 return false;
25
26 build_date++;
27
28 return strncmp(build_date, MT7925_WOW_PATTERN_NEW_FW_DATE,
29 strlen(MT7925_WOW_PATTERN_NEW_FW_DATE)) < 0;
30 }
31
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH net 2/2] net: airoha: fix netif_set_real_num_tx_queues for sparse QoS channels
From: Lorenzo Bianconi @ 2026-06-18 6:00 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Wayen Yan, linux-arm-kernel, linux-mediatek, netdev,
Lorenzo Bianconi
In-Reply-To: <20260618-airoha-qos-fixes-v1-0-37192652157f@kernel.org>
airoha_tc_htb_alloc_leaf_queue() assigns queue IDs based on the channel
index (opt->qid = AIROHA_NUM_TX_RING + channel), but updates
real_num_tx_queues with a simple increment (num_tx_queues + 1). When QoS
channels are allocated sparsely (e.g., channels 0 and 3 without 1 and
2), the returned qid can exceed real_num_tx_queues, causing out-of-bounds
accesses in the networking stack.
For example, allocating channel 0 then channel 3 results in
real_num_tx_queues = 34 but qid = 35, which is out of range [0, 34).
Fix this by computing real_num_tx_queues based on the highest active
channel index rather than using a simple counter, in both the allocation
and deletion paths.
Fixes: ef1ca9271313b ("net: airoha: Add sched HTB offload support")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index aa98d1823ab6..e2652cff67c0 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2789,7 +2789,7 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
- int err, num_tx_queues = netdev->real_num_tx_queues;
+ int err, num_tx_queues = AIROHA_NUM_TX_RING + channel + 1;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_qdma *qdma = dev->qdma;
@@ -2806,7 +2806,10 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
if (err)
goto error;
- err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
+ if (num_tx_queues <= netdev->real_num_tx_queues)
+ goto set_qos_sq_bmap;
+
+ err = netif_set_real_num_tx_queues(netdev, num_tx_queues);
if (err) {
airoha_qdma_set_tx_rate_limit(netdev, channel, 0,
opt->quantum);
@@ -2815,6 +2818,7 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
goto error;
}
+set_qos_sq_bmap:
set_bit(channel, dev->qos_sq_bmap);
opt->qid = AIROHA_NUM_TX_RING + channel;
@@ -3003,13 +3007,18 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ int num_tx_queues = AIROHA_NUM_TX_RING;
struct airoha_qdma *qdma = dev->qdma;
- netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
airoha_qdma_set_tx_rate_limit(netdev, queue, 0, 0);
clear_bit(queue, qdma->qos_channel_map);
clear_bit(queue, dev->qos_sq_bmap);
+
+ if (!bitmap_empty(dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS))
+ num_tx_queues += find_last_bit(dev->qos_sq_bmap,
+ AIROHA_NUM_QOS_CHANNELS) + 1;
+ netif_set_real_num_tx_queues(netdev, num_tx_queues);
}
static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
--
2.54.0
^ permalink raw reply related
* [PATCH net 1/2] net: airoha: Fix off-by-one in airoha_tc_remove_htb_queue()
From: Lorenzo Bianconi @ 2026-06-18 6:00 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Wayen Yan, linux-arm-kernel, linux-mediatek, netdev,
Lorenzo Bianconi
In-Reply-To: <20260618-airoha-qos-fixes-v1-0-37192652157f@kernel.org>
airoha_tc_htb_alloc_leaf_queue() computes the HTB QoS channel index
as opt->classid % AIROHA_NUM_QOS_CHANNELS and stores it in qos_sq_bmap.
However, airoha_tc_remove_htb_queue() clears the HTB configuration
using queue + 1 as the channel index, causing an off-by-one error.
Use queue directly as the QoS channel index to match the allocation
logic.
Fixes: ef1ca9271313b ("net: airoha: Add sched HTB offload support")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 64dde6464f3f..aa98d1823ab6 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -3006,7 +3006,7 @@ static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
struct airoha_qdma *qdma = dev->qdma;
netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
- airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
+ airoha_qdma_set_tx_rate_limit(netdev, queue, 0, 0);
clear_bit(queue, qdma->qos_channel_map);
clear_bit(queue, dev->qos_sq_bmap);
--
2.54.0
^ permalink raw reply related
* [PATCH net 0/2] airoha: fixes for sched HTB offload support
From: Lorenzo Bianconi @ 2026-06-18 6:00 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Wayen Yan, linux-arm-kernel, linux-mediatek, netdev,
Lorenzo Bianconi
---
Lorenzo Bianconi (2):
net: airoha: Fix off-by-one in airoha_tc_remove_htb_queue()
net: airoha: fix netif_set_real_num_tx_queues for sparse QoS channels
drivers/net/ethernet/airoha/airoha_eth.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
---
base-commit: 7d8297e26b4e20b5d1c3c3fe51fe81a1c7fbc823
change-id: 20260618-airoha-qos-fixes-b6460b085680
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply
* Re: [PATCH v3] Bluetooth: btmtk: Add MT7928 support
From: Paul Menzel @ 2026-06-18 5:30 UTC (permalink / raw)
To: Chris Lu
Cc: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz, Sean Wang,
Will Lee, SS Wu, Steve Lee, linux-bluetooth, linux-kernel,
linux-mediatek
In-Reply-To: <20260617235145.2582862-1-chris.lu@mediatek.com>
Am 18.06.26 um 01:51 schrieb Chris Lu:
> Add support for MT7928 (internal device ID is MT7935) which
> requires additional firmware (CBMCU firmware) loading before
> Bluetooth firmware.
>
> CBMCU is a new component on MT7928 to handle common part shared
> across the combo chip (Wi-Fi/Bluetooth's subsystem), providing
> a better user experience through improved coordination between
> subsystems.
>
> Implement two-phase CBMCU firmware download: Phase 1 loads
> section with type 0x5 containing global descriptor,
> section maps and signature data; Phase 2 loads remaining
> firmware sections. Add retry mechanism for concurrent download
> protection.
>
> After CBMCU firmware loads successfully, the driver continues
> to load corresponding BT firmware based on device ID through
> fallthrough to case 0x7922/0x7925.
>
> The firmware required for MT7928 will be scheduled for upload
> to linux-firmware at a later stage.
Should you resend please re-flow for 72 characters per line to use less
lines.
Also, please mention the document name, revision and file name, where
the CBMCU format is described.
> MT7928 bringup kernel log:
> [90.209995] usb 1-3: New USB device found, idVendor=0e8d,
> idProduct=7935, bcdDevice= 1.00
> [90.210027] usb 1-3: New USB device strings: Mfr=5,
> Product=6, SerialNumber=7
> [90.210046] usb 1-3: Product: Wireless_Device
> [90.210060] usb 1-3: Manufacturer: MediaTek Inc.
> [90.210075] usb 1-3: SerialNumber: 000000000
> [90.223089] Bluetooth: hci1: CBMCU Version: 0x00000000,
> Build Time: 20260601T161751+0800
> [90.664706] Bluetooth: hci1: CBMCU firmware download completed
> [90.685424] Bluetooth: hci1: HW/SW Version: 0x00000000,
> Build Time: 20260527000816
> [93.771612] Bluetooth: hci1: Device setup in 3467323 usecs
Wow, over three seconds is too long. Can you please check how to bring
this below one second.
> [93.771657] Bluetooth: hci1: HCI Enhanced Setup Synchronous
> Connection command is advertised, but not supported.
> [93.890840] Bluetooth: hci1: AOSP extensions version v2.00
> [93.890887] Bluetooth: hci1: AOSP quality report is supported
> [93.893444] Bluetooth: MGMT ver 1.23
Please do not wrap pasted logs. `scripts/checkpatch.pl` should not complain:
```
# Check if the commit log is in a possible stack dump
if ($in_commit_log && !$commit_log_possible_stack_dump &&
($line =~ /^\s*(?:WARNING:|BUG:)/ ||
$line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ ||
# timestamp
$line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) ||
$line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ ||
$line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at
[0-9a-fA-F]+/) {
# stack dump address styles
$commit_log_possible_stack_dump = 1;
}
```
> Signed-off-by: Chris Lu <chris.lu@mediatek.com>
> ---
> v1->v2: Update error message; Use macro instead of magic number.
> v2->v3: Update commit message.
> ---
> drivers/bluetooth/btmtk.c | 348 +++++++++++++++++++++++++++++++++++++-
> drivers/bluetooth/btmtk.h | 3 +
> 2 files changed, 350 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
> index 02a96342e964..6bae0b0794dd 100644
> --- a/drivers/bluetooth/btmtk.c
> +++ b/drivers/bluetooth/btmtk.c
> @@ -21,6 +21,8 @@
> #define MTK_FW_ROM_PATCH_SEC_MAP_SIZE 64
> #define MTK_SEC_MAP_COMMON_SIZE 12
> #define MTK_SEC_MAP_NEED_SEND_SIZE 52
> +#define MTK_SEC_MAP_LENGTH_SIZE 4
> +#define MTK_SEC_CBMCU_DESC 0x5
>
> /* It is for mt79xx iso data transmission setting */
> #define MTK_ISO_THRESHOLD 264
> @@ -120,6 +122,10 @@ void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver,
> snprintf(buf, size,
> "mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
> dev_id & 0xffff, dev_id & 0xffff, (fw_ver & 0xff) + 1);
> + else if (dev_id == 0x7935)
I wonder if the marketing name should be added as a comment.
> + snprintf(buf, size,
> + "mediatek/mt7928/BT_RAM_CODE_MT%04x_1_1_hdr.bin",
> + dev_id & 0xffff);
`dev_id` is u32, so the truncatation is unnecessary?
> else if (dev_id == 0x7961 && fw_flavor)
> snprintf(buf, size,
> "mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
> @@ -734,6 +740,7 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
> status = BTMTK_WMT_ON_UNDONE;
> break;
> case BTMTK_WMT_PATCH_DWNLD:
> + case BTMTK_WMT_CBMCU_DWNLD:
> if (wmt_evt->whdr.flag == 2)
> status = BTMTK_WMT_PATCH_DONE;
> else if (wmt_evt->whdr.flag == 1)
> @@ -870,6 +877,334 @@ static u32 btmtk_usb_reset_done(struct hci_dev *hdev)
> return val & MTK_BT_RST_DONE;
> }
>
> +static int btmtk_cbmcu_patch_status(struct hci_dev *hdev,
> + wmt_cmd_sync_func_t wmt_cmd_sync,
> + u8 *patch_status)
> +{
> + struct btmtk_hci_wmt_params wmt_params;
> + int status, err, retry = 20;
> +
> + do {
> + wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
> + wmt_params.flag = 0xF0;
> + wmt_params.dlen = 0;
> + wmt_params.data = NULL;
> + wmt_params.status = &status;
> +
> + err = wmt_cmd_sync(hdev, &wmt_params);
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to query CBMCU patch status (%d)", err);
> + return err;
> + }
> +
> + *patch_status = (u8)status;
> +
> + if (*patch_status == BTMTK_WMT_PATCH_PROGRESS) {
> + msleep(100);
> + retry--;
> + } else {
> + break;
> + }
> + } while (retry > 0);
> +
> + return 0;
> +}
> +
> +static int btmtk_query_cbmcu_section(struct hci_dev *hdev,
> + wmt_cmd_sync_func_t wmt_cmd_sync,
> + u8 cbmcu_type,
> + const u8 *section_map,
> + u32 cert_len)
> +{
> + struct btmtk_hci_wmt_params wmt_params;
> + u8 cmd[64];
> + int status, err;
> +
> + cmd[0] = 0;
> + cmd[1] = cbmcu_type;
> +
> + if (cbmcu_type == 0)
> + put_unaligned_le32(cert_len, &cmd[2]);
> + else
> + memcpy(&cmd[2], section_map, MTK_SEC_MAP_NEED_SEND_SIZE);
> +
> + wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
> + wmt_params.flag = 0;
> + wmt_params.dlen = cbmcu_type ?
> + MTK_SEC_MAP_NEED_SEND_SIZE + 2 :
> + MTK_SEC_MAP_LENGTH_SIZE + 2;
> + wmt_params.data = cmd;
> + wmt_params.status = &status;
> +
> + err = wmt_cmd_sync(hdev, &wmt_params);
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to query CBMCU section (%d)", err);
> + return err;
> + }
> +
> + /* Query should return UNDONE status for successful section query */
> + if (status != BTMTK_WMT_PATCH_UNDONE) {
> + bt_dev_err(hdev, "CBMCU section query status error (%d)", status);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int btmtk_download_cbmcu_section(struct hci_dev *hdev,
> + wmt_cmd_sync_func_t wmt_cmd_sync,
> + const u8 *fw_data,
> + u32 dl_size)
> +{
> + struct btmtk_hci_wmt_params wmt_params;
> + u32 sent_len, total_size = dl_size;
> + int err;
> +
> + wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
> + wmt_params.status = NULL;
> +
> + while (dl_size > 0) {
> + sent_len = min_t(u32, 250, dl_size);
> +
> + if (dl_size == total_size)
> + wmt_params.flag = 1;
> + else if (dl_size == sent_len)
> + wmt_params.flag = 3;
> + else
> + wmt_params.flag = 2;
This is not easy to read, as it’s not clear right away, what 1, 2 and 3
mean. Maybe use enums?
> +
> + wmt_params.dlen = sent_len;
> + wmt_params.data = fw_data;
> +
> + err = wmt_cmd_sync(hdev, &wmt_params);
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to send CBMCU section data (%d)", err);
Print the sent parameters? Or will `wmt_cmd_sync()` log something?
> + return err;
> + }
> +
> + dl_size -= sent_len;
> + fw_data += sent_len;
> + }
> +
> + return 0;
> +}
> +
> +static int btmtk_enable_cbmcu_patch(struct hci_dev *hdev,
> + wmt_cmd_sync_func_t wmt_cmd_sync)
> +{
> + struct btmtk_hci_wmt_params wmt_params;
> + int err;
> +
> + wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
> + wmt_params.flag = 0xF1;
Ditto.
> + wmt_params.dlen = 0;
> + wmt_params.data = NULL;
> + wmt_params.status = NULL;
> +
> + err = wmt_cmd_sync(hdev, &wmt_params);
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to enable CBMCU patch (%d)", err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int btmtk_load_cbmcu_firmware(struct hci_dev *hdev,
> + const char *fwname,
> + wmt_cmd_sync_func_t wmt_cmd_sync)
> +{
> + struct btmtk_patch_header *hdr;
> + struct btmtk_global_desc *globaldesc;
> + struct btmtk_section_map *sectionmap;
> + const struct firmware *fw;
> + const u8 *fw_ptr;
> + u8 *cert_buf = NULL;
> + u32 section_num, section_offset, dl_size, cert_len;
> + int i, err;
> +
> + err = request_firmware(&fw, fwname, &hdev->dev);
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to load CBMCU firmware file %s (%d)",
> + fwname, err);
> + return err;
> + }
> +
> + if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE) {
> + bt_dev_err(hdev, "CBMCU firmware too small (%zu bytes)", fw->size);
Please log `MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE`.
> + err = -EINVAL;
> + goto err_release_fw;
> + }
> +
> + fw_ptr = fw->data;
> + hdr = (struct btmtk_patch_header *)fw_ptr;
> + globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
> + section_num = le32_to_cpu(globaldesc->section_num);
> +
> + if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE +
> + (size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num) {
> + bt_dev_err(hdev, "CBMCU firmware truncated: size=%zu, expected=%zu (section_num=%u)",
> + fw->size,
> + MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE +
> + (size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num,
> + section_num);
> + err = -EINVAL;
> + goto err_release_fw;
> + }
> +
> + bt_dev_info(hdev, "CBMCU Version: 0x%04x%04x, Build Time: %s",
> + le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime);
> +
> + /* Phase 1: Download section type MTK_SEC_CBMCU_DESC */
> + for (i = 0; i < section_num; i++) {
> + sectionmap = (struct btmtk_section_map *)
> + (fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
> + MTK_FW_ROM_PATCH_GD_SIZE +
> + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
> +
> + /* Only process MTK_SEC_CBMCU_DESC section in Phase 1 */
> + if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) != MTK_SEC_CBMCU_DESC)
> + continue;
> +
> + section_offset = le32_to_cpu(sectionmap->secoffset);
> + dl_size = le32_to_cpu(sectionmap->secsize);
> +
> + if (dl_size == 0)
> + continue;
> +
> + if (section_offset > fw->size ||
> + dl_size > fw->size - section_offset) {
> + bt_dev_err(hdev, "CBMCU Phase 1 section out of bounds");
> + err = -EINVAL;
> + goto err_release_fw;
> + }
> +
> + cert_len = MTK_FW_ROM_PATCH_GD_SIZE +
> + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num +
> + dl_size;
> +
> + /* Query cbmcu section */
> + err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync, 0, NULL,
> + cert_len);
> + if (err < 0)
> + goto err_release_fw;
> +
> + cert_buf = kmalloc(cert_len, GFP_KERNEL);
> + if (!cert_buf) {
> + err = -ENOMEM;
> + goto err_release_fw;
> + }
> +
> + /* Copy Global Descriptor + All Section Maps */
> + memcpy(cert_buf,
> + fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE,
> + MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num);
> +
> + /* Copy Phase 1 section data */
> + memcpy(cert_buf + MTK_FW_ROM_PATCH_GD_SIZE +
> + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num,
> + fw_ptr + section_offset,
> + dl_size);
> +
> + /* Download Phase 1 section */
> + err = btmtk_download_cbmcu_section(hdev, wmt_cmd_sync,
> + cert_buf, cert_len);
> + kfree(cert_buf);
> + cert_buf = NULL;
> +
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to download CBMCU Phase 1 section (%d)", err);
> + goto err_release_fw;
> + }
> +
> + break;
> + }
> +
> + /* Phase 2: Download other sections (type != MTK_SEC_CBMCU_DESC) */
> + for (i = 0; i < section_num; i++) {
> + sectionmap = (struct btmtk_section_map *)
> + (fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
> + MTK_FW_ROM_PATCH_GD_SIZE +
> + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
> +
> + /* Skip MTK_SEC_CBMCU_DESC section in Phase 2 */
> + if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) == MTK_SEC_CBMCU_DESC)
> + continue;
> +
> + section_offset = le32_to_cpu(sectionmap->secoffset);
> + dl_size = le32_to_cpu(sectionmap->bin_info_spec.dlsize);
> +
> + if (dl_size == 0)
> + continue;
> +
> + if (section_offset > fw->size ||
> + dl_size > fw->size - section_offset) {
> + bt_dev_err(hdev, "CBMCU Phase 2 section %d out of bounds", i);
> + err = -EINVAL;
> + goto err_release_fw;
> + }
> +
> + /* Query cbmcu section */
> + err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync, 1,
> + (u8 *)§ionmap->bin_info_spec,
> + 0);
> + if (err < 0)
> + goto err_release_fw;
> +
> + /* Download section data */
> + err = btmtk_download_cbmcu_section(hdev, wmt_cmd_sync,
> + fw_ptr + section_offset,
> + dl_size);
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to download CBMCU section %d (%d)", i, err);
> + goto err_release_fw;
> + }
> + }
> +
> + /* Wait for firmware activation */
> + usleep_range(100000, 120000);
Does the firmware really take this long? (Please report to the firmware
engineers to optimize this. Optimized Linux takes less time. ;-)) Isn’t
there a way to poll the readiness?
> +
> + bt_dev_info(hdev, "CBMCU firmware download completed");
For me, Linux uploads firmware to the device (and the BT device would
download it). But this is not a BT device message but the OS message?
Feel free to ignore.
> +
> +err_release_fw:
> + release_firmware(fw);
> + return err;
> +}
> +
> +static int btmtk_setup_cbmcu_firmware(struct hci_dev *hdev,
> + wmt_cmd_sync_func_t wmt_cmd_sync,
> + u32 dev_id)
> +{
> + char cbmcu_fwname[64];
> + u8 patch_status;
> + int err;
> +
> + err = btmtk_cbmcu_patch_status(hdev, wmt_cmd_sync, &patch_status);
> + if (err < 0)
> + return err;
> +
> + bt_dev_dbg(hdev, "CBMCU patch status: 0x%02x", patch_status);
> +
> + if (patch_status != BTMTK_WMT_PATCH_UNDONE)
> + return 0;
> +
> + snprintf(cbmcu_fwname, sizeof(cbmcu_fwname),
> + "mediatek/mt7928/CBMCU_CODE_MT%04x_1_1.bin",
> + dev_id & 0xffff);
> +
> + err = btmtk_load_cbmcu_firmware(hdev, cbmcu_fwname, wmt_cmd_sync);
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to download CBMCU firmware (%d)", err);
> + return err;
> + }
> +
> + err = btmtk_enable_cbmcu_patch(hdev, wmt_cmd_sync);
> + if (err < 0)
> + return err;
> +
> + return 0;
> +}
> +
> int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
> {
> u32 val;
> @@ -894,7 +1229,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
> if (err < 0)
> return err;
> msleep(100);
> - } else if (dev_id == 0x7925 || dev_id == 0x6639) {
> + } else if (dev_id == 0x7925 || dev_id == 0x6639 || dev_id == 0x7935) {
This should be sorted in my opinion. Maybe add a commit before, and put
the new id at the beginning.
> err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
> if (err < 0)
> return err;
> @@ -1379,6 +1714,15 @@ int btmtk_usb_setup(struct hci_dev *hdev)
> case 0x7668:
> fwname = FIRMWARE_MT7668;
> break;
> + case 0x7935:
> + /* Requires CBMCU firmware before BT firmware */
> + err = btmtk_setup_cbmcu_firmware(hdev, btmtk_usb_hci_wmt_sync,
> + dev_id);
> + if (err < 0) {
> + bt_dev_err(hdev, "Failed to set up CBMCU firmware (%d)", err);
> + return err;
> + }
> + fallthrough;
> case 0x7922:
> case 0x7925:
> /*
> @@ -1596,3 +1940,5 @@ MODULE_FIRMWARE(FIRMWARE_MT7922);
> MODULE_FIRMWARE(FIRMWARE_MT7961);
> MODULE_FIRMWARE(FIRMWARE_MT7925);
> MODULE_FIRMWARE(FIRMWARE_MT7927);
> +MODULE_FIRMWARE(FIRMWARE_MT7928);
> +MODULE_FIRMWARE(FIRMWARE_MT7928_CBMCU);
> diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
> index c83c24897c95..6d3bf6b74a1d 100644
> --- a/drivers/bluetooth/btmtk.h
> +++ b/drivers/bluetooth/btmtk.h
> @@ -9,6 +9,8 @@
> #define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
> #define FIRMWARE_MT7925 "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
> #define FIRMWARE_MT7927 "mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin"
> +#define FIRMWARE_MT7928 "mediatek/mt7928/BT_RAM_CODE_MT7935_1_1_hdr.bin"
> +#define FIRMWARE_MT7928_CBMCU "mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin"
>
> #define HCI_EV_WMT 0xe4
> #define HCI_WMT_MAX_EVENT_SIZE 64
> @@ -54,6 +56,7 @@ enum {
> BTMTK_WMT_RST = 0x7,
> BTMTK_WMT_REGISTER = 0x8,
> BTMTK_WMT_SEMAPHORE = 0x17,
> + BTMTK_WMT_CBMCU_DWNLD = 0x58,
> };
>
> enum {
^ permalink raw reply
* [PATCH 56/78] ASoC: codecs: tas2783: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-18 4:54 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260617103235.449609-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/codecs/tas2783-sdw.c | 89 ++++++++++++++++------------------
1 file changed, 43 insertions(+), 46 deletions(-)
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index 7d70e7e3f24f..763663daf3e1 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -691,12 +691,12 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev)
return 0;
}
- mutex_lock(&tas_dev->calib_lock);
- ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data,
- tas_dev->cali_data.read_sz);
- if (!ret)
- tas2783_set_calib_params_to_device(tas_dev, tmp_val);
- mutex_unlock(&tas_dev->calib_lock);
+ scoped_guard(mutex, &tas_dev->calib_lock) {
+ ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data,
+ tas_dev->cali_data.read_sz);
+ if (!ret)
+ tas2783_set_calib_params_to_device(tas_dev, tmp_val);
+ }
return ret;
}
@@ -764,27 +764,27 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
goto out;
}
- mutex_lock(&tas_dev->pde_lock);
- while (offset < (img_sz - FW_FL_HDR)) {
- offset += tas_fw_get_next_file(&buf[offset], file);
- dev_dbg(tas_dev->dev,
- "v=%d, fid=%d, ver=%d, len=%d, daddr=0x%x, fw=%p",
- file->vendor_id, file->file_id,
- file->version, file->length,
- file->dest_addr, file->fw_data);
-
- ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
- file->dest_addr,
- file->length,
- file->fw_data);
- if (ret < 0) {
- dev_err(tas_dev->dev,
- "FW download failed: %d", ret);
- break;
+ scoped_guard(mutex, &tas_dev->pde_lock) {
+ while (offset < (img_sz - FW_FL_HDR)) {
+ offset += tas_fw_get_next_file(&buf[offset], file);
+ dev_dbg(tas_dev->dev,
+ "v=%d, fid=%d, ver=%d, len=%d, daddr=0x%x, fw=%p",
+ file->vendor_id, file->file_id,
+ file->version, file->length,
+ file->dest_addr, file->fw_data);
+
+ ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
+ file->dest_addr,
+ file->length,
+ file->fw_data);
+ if (ret < 0) {
+ dev_err(tas_dev->dev,
+ "FW download failed: %d", ret);
+ break;
+ }
+ cur_file++;
}
- cur_file++;
}
- mutex_unlock(&tas_dev->pde_lock);
if (cur_file == 0) {
dev_err(tas_dev->dev, "fw with no files");
@@ -917,22 +917,22 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream,
dev_err(tas_dev->dev,
"clear latch failed, err=%d", ret);
- mutex_lock(&tas_dev->pde_lock);
- /*
- * Sometimes, there is error returned during power on.
- * So added retry logic to ensure power on so that
- * port prepare succeeds
- */
- do {
- ret = regmap_write(tas_dev->regmap,
- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23,
- TAS2783_SDCA_CTL_REQ_POW_STATE, 0),
- TAS2783_SDCA_POW_STATE_ON);
- if (!ret)
- break;
- usleep_range(2000, 2200);
- } while (retry--);
- mutex_unlock(&tas_dev->pde_lock);
+ scoped_guard(mutex, &tas_dev->pde_lock) {
+ /*
+ * Sometimes, there is error returned during power on.
+ * So added retry logic to ensure power on so that
+ * port prepare succeeds
+ */
+ do {
+ ret = regmap_write(tas_dev->regmap,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23,
+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0),
+ TAS2783_SDCA_POW_STATE_ON);
+ if (!ret)
+ break;
+ usleep_range(2000, 2200);
+ } while (retry--);
+ }
if (ret)
return ret;
@@ -965,14 +965,11 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream);
- mutex_lock(&tas_dev->pde_lock);
- ret = regmap_write(tas_dev->regmap,
+ guard(mutex)(&tas_dev->pde_lock);
+ return regmap_write(tas_dev->regmap,
SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23,
TAS2783_SDCA_CTL_REQ_POW_STATE, 0),
TAS2783_SDCA_POW_STATE_OFF);
- mutex_unlock(&tas_dev->pde_lock);
-
- return ret;
}
static const struct snd_soc_dai_ops tas_dai_ops = {
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 11/78] ASoC: codecs: cros_ec_codec: Use guard() for mutex locks
From: Tzung-Bi Shih @ 2026-06-18 4:05 UTC (permalink / raw)
To: phucduc.bui
Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Cheng-Yi Chiang, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260617103235.449609-12-phucduc.bui@gmail.com>
On Wed, Jun 17, 2026 at 05:31:28PM +0700, phucduc.bui@gmail.com wrote:
> 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>
Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>
^ permalink raw reply
* [PATCH v4 1/2] Bluetooth: btmtksdio: test for BUS IO errors in btmtksdio_txrx_work()
From: Sergey Senozhatsky @ 2026-06-18 3:13 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Sean Wang
Cc: Tomasz Figa, linux-bluetooth, linux-kernel, linux-arm-kernel,
linux-mediatek, Sergey Senozhatsky, stable
In-Reply-To: <20260618031338.1011410-1-senozhatsky@chromium.org>
btmtksdio_txrx_work() loop termination condition checks for
int_status being non-zero, however, this evaluates to true
even when sdio_readl() encounters BUS I/O error (in which
case int_status is 0xffffffff). Break out of the loop if
sdio_readl() errors out.
Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to work")
Cc: stable@vger.kernel.org
Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
---
drivers/bluetooth/btmtksdio.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index c6f80c419e90..d8c8d2857527 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -574,7 +574,9 @@ static void btmtksdio_txrx_work(struct work_struct *work)
txrx_timeout = jiffies + 5 * HZ;
do {
- int_status = sdio_readl(bdev->func, MTK_REG_CHISR, NULL);
+ int_status = sdio_readl(bdev->func, MTK_REG_CHISR, &err);
+ if (err < 0 || int_status == 0xffffffff)
+ break;
/* Ack an interrupt as soon as possible before any operation on
* hardware.
--
2.54.0.1189.g8c84645362-goog
^ permalink raw reply related
* [PATCH v4 2/2] Bluetooth: btmtksdio: call cancel_work_sync() out of host lock scope
From: Sergey Senozhatsky @ 2026-06-18 3:13 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Sean Wang
Cc: Tomasz Figa, linux-bluetooth, linux-kernel, linux-arm-kernel,
linux-mediatek, Sergey Senozhatsky, stable
In-Reply-To: <20260618031338.1011410-1-senozhatsky@chromium.org>
cancel_work_sync() should be called outside of host lock scope
in order to avoid circular locking scenario:
CPU0 CPU1
close()/reset()
sdio_claim_host()
txrx_work
sdio_claim_host() // sleeps
cancel_work_sync() // sleeps
In addition, when txrx_work() runs concurrently with close()/reset()
it better not to re-enable interrupts by testing for BTMTKSDIO_FUNC_ENABLED
and not BTMTKSDIO_HW_RESET_ACTIVE before C_INT_EN_SET write. However,
btmtksdio_close() clears the BTMTKSDIO_FUNC_ENABLED too late (after
cancel_work_sync() call). Move BTMTKSDIO_FUNC_ENABLED bit-clear earlier
so that txrx_work can see concurrent close().
Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to work")
Cc: stable@vger.kernel.org
Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
---
drivers/bluetooth/btmtksdio.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index d8c8d2857527..207d04cc2282 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -625,7 +625,9 @@ static void btmtksdio_txrx_work(struct work_struct *work)
} while (int_status && time_is_after_jiffies(txrx_timeout));
/* Enable interrupt */
- if (bdev->func->irq_handler)
+ if (bdev->func->irq_handler &&
+ test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state) &&
+ !test_bit(BTMTKSDIO_HW_RESET_ACTIVE, &bdev->tx_state))
sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, NULL);
sdio_release_host(bdev->func);
@@ -741,6 +743,8 @@ static int btmtksdio_close(struct hci_dev *hdev)
if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state))
return 0;
+ clear_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state);
+
sdio_claim_host(bdev->func);
/* Disable interrupt */
@@ -748,11 +752,12 @@ static int btmtksdio_close(struct hci_dev *hdev)
sdio_release_irq(bdev->func);
+ sdio_release_host(bdev->func);
cancel_work_sync(&bdev->txrx_work);
+ sdio_claim_host(bdev->func);
btmtksdio_fw_pmctrl(bdev);
- clear_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state);
sdio_disable_func(bdev->func);
sdio_release_host(bdev->func);
@@ -1295,7 +1300,10 @@ static void btmtksdio_reset(struct hci_dev *hdev)
sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
skb_queue_purge(&bdev->txq);
+
+ sdio_release_host(bdev->func);
cancel_work_sync(&bdev->txrx_work);
+ sdio_claim_host(bdev->func);
gpiod_set_value_cansleep(bdev->reset, 1);
msleep(100);
--
2.54.0.1189.g8c84645362-goog
^ permalink raw reply related
* [PATCH v4 0/2] Bluetooth: btmtksdio: teardown fixes
From: Sergey Senozhatsky @ 2026-06-18 3:13 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Sean Wang
Cc: Tomasz Figa, linux-bluetooth, linux-kernel, linux-arm-kernel,
linux-mediatek, Sergey Senozhatsky
This fixes several teardown issues:
INFO: task kworker/u17:0:189 blocked for more than 122 seconds.
__cancel_work_timer+0x3f4/0x460
cancel_work_sync+0x1c/0x2c
btmtksdio_flush+0x2c/0x40
hci_dev_open_sync+0x10c4/0x2190
[..]
close/flush can deadlock when run concurrently with btmtksdio_txrx_work().
In addition btmtksdio_txrx_work() re-enables interrupts regardless of
close/flush being executed on another CPU.
v3 -> v4:
- fix commit message linter warnings/errors (tabs, subject line over 80
chars).
Sergey Senozhatsky (2):
Bluetooth: btmtksdio: test for BUS IO errors in btmtksdio_txrx_work()
Bluetooth: btmtksdio: call cancel_work_sync() out of host lock scope
drivers/bluetooth/btmtksdio.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
--
2.54.0.1189.g8c84645362-goog
^ permalink raw reply
* Re: [PATCH v1] mfd: mt6397-irq: Fix PM notifier use-after-free
From: 최유호 @ 2026-06-18 1:53 UTC (permalink / raw)
To: Lee Jones
Cc: Matthias Brugger, AngeloGioacchino Del Regno, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <20260617152615.GO10056@google.com>
On Wed, 17 Jun 2026 at 11:26, Lee Jones <lee@kernel.org> wrote:
> [Severity: High]
> Is it safe to explicitly remove the IRQ domain here while the devm-managed
> parent threaded IRQ remains active?
>
> The parent IRQ was requested earlier via devm_request_threaded_irq(). Because
> devres cleanup does not run until the probe function completely fails and
> returns, the parent IRQ remains active and enabled during this window.
>
> If an interrupt triggers during this error path, the active IRQ handler
> mt6397_irq_thread() will execute and call mt6397_irq_handle_reg(). This in
> turn calls irq_find_mapping(), which will dereference the freed irq_domain,
> resulting in a use-after-free.
Thanks for pointing this out. Findings 1 and 3 look like pre-existing
issues. Finding 2, however, appears to be related to the error-path cleanup
I added.
I will rework the patch so the notifier and irq_domain cleanup ordering is
handled consistently.
^ permalink raw reply
* Re: [PATCH net] net: ethernet: mtk_eth_soc: fix supported_interface set after phylink_create
From: patchwork-bot+netdevbpf @ 2026-06-18 0:20 UTC (permalink / raw)
To: Christian Marangi
Cc: nbd, lorenzo, andrew+netdev, davem, edumazet, kuba, pabeni,
matthias.bgg, angelogioacchino.delregno, linux, daniel, netdev,
linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260615151106.15438-1-ansuelsmth@gmail.com>
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Mon, 15 Jun 2026 17:11:00 +0200 you wrote:
> Everything configured in phylink_config it's assumed to be set before
> calling phylink_create() to permit correct parsing of all the different
> modes and capabilities.
>
> Commit 51cf06ddafc9 ("net: ethernet: mtk_eth_soc: add support for MT7988
> internal 2.5G PHY") while introducing support for 2.5G phy for MT7988,
> probably due to an auto-rebase, placed the configuration of the INTERNAL
> interface mode for the supported_interfaces for phylink_config right after
> phylink_create() introducing a possible problem with supported interfaces
> parsing.
>
> [...]
Here is the summary with links:
- [net] net: ethernet: mtk_eth_soc: fix supported_interface set after phylink_create
https://git.kernel.org/netdev/net/c/e4b4d8410c7c
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH v3] Bluetooth: btmtk: Add MT7928 support
From: Chris Lu @ 2026-06-17 23:51 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, linux-bluetooth,
linux-kernel, linux-mediatek, Chris Lu
Add support for MT7928 (internal device ID is MT7935) which
requires additional firmware (CBMCU firmware) loading before
Bluetooth firmware.
CBMCU is a new component on MT7928 to handle common part shared
across the combo chip (Wi-Fi/Bluetooth's subsystem), providing
a better user experience through improved coordination between
subsystems.
Implement two-phase CBMCU firmware download: Phase 1 loads
section with type 0x5 containing global descriptor,
section maps and signature data; Phase 2 loads remaining
firmware sections. Add retry mechanism for concurrent download
protection.
After CBMCU firmware loads successfully, the driver continues
to load corresponding BT firmware based on device ID through
fallthrough to case 0x7922/0x7925.
The firmware required for MT7928 will be scheduled for upload
to linux-firmware at a later stage.
MT7928 bringup kernel log:
[90.209995] usb 1-3: New USB device found, idVendor=0e8d,
idProduct=7935, bcdDevice= 1.00
[90.210027] usb 1-3: New USB device strings: Mfr=5,
Product=6, SerialNumber=7
[90.210046] usb 1-3: Product: Wireless_Device
[90.210060] usb 1-3: Manufacturer: MediaTek Inc.
[90.210075] usb 1-3: SerialNumber: 000000000
[90.223089] Bluetooth: hci1: CBMCU Version: 0x00000000,
Build Time: 20260601T161751+0800
[90.664706] Bluetooth: hci1: CBMCU firmware download completed
[90.685424] Bluetooth: hci1: HW/SW Version: 0x00000000,
Build Time: 20260527000816
[93.771612] Bluetooth: hci1: Device setup in 3467323 usecs
[93.771657] Bluetooth: hci1: HCI Enhanced Setup Synchronous
Connection command is advertised, but not supported.
[93.890840] Bluetooth: hci1: AOSP extensions version v2.00
[93.890887] Bluetooth: hci1: AOSP quality report is supported
[93.893444] Bluetooth: MGMT ver 1.23
Signed-off-by: Chris Lu <chris.lu@mediatek.com>
---
v1->v2: Update error message; Use macro instead of magic number.
v2->v3: Update commit message.
---
drivers/bluetooth/btmtk.c | 348 +++++++++++++++++++++++++++++++++++++-
drivers/bluetooth/btmtk.h | 3 +
2 files changed, 350 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 02a96342e964..6bae0b0794dd 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -21,6 +21,8 @@
#define MTK_FW_ROM_PATCH_SEC_MAP_SIZE 64
#define MTK_SEC_MAP_COMMON_SIZE 12
#define MTK_SEC_MAP_NEED_SEND_SIZE 52
+#define MTK_SEC_MAP_LENGTH_SIZE 4
+#define MTK_SEC_CBMCU_DESC 0x5
/* It is for mt79xx iso data transmission setting */
#define MTK_ISO_THRESHOLD 264
@@ -120,6 +122,10 @@ void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver,
snprintf(buf, size,
"mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
dev_id & 0xffff, dev_id & 0xffff, (fw_ver & 0xff) + 1);
+ else if (dev_id == 0x7935)
+ snprintf(buf, size,
+ "mediatek/mt7928/BT_RAM_CODE_MT%04x_1_1_hdr.bin",
+ dev_id & 0xffff);
else if (dev_id == 0x7961 && fw_flavor)
snprintf(buf, size,
"mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
@@ -734,6 +740,7 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
status = BTMTK_WMT_ON_UNDONE;
break;
case BTMTK_WMT_PATCH_DWNLD:
+ case BTMTK_WMT_CBMCU_DWNLD:
if (wmt_evt->whdr.flag == 2)
status = BTMTK_WMT_PATCH_DONE;
else if (wmt_evt->whdr.flag == 1)
@@ -870,6 +877,334 @@ static u32 btmtk_usb_reset_done(struct hci_dev *hdev)
return val & MTK_BT_RST_DONE;
}
+static int btmtk_cbmcu_patch_status(struct hci_dev *hdev,
+ wmt_cmd_sync_func_t wmt_cmd_sync,
+ u8 *patch_status)
+{
+ struct btmtk_hci_wmt_params wmt_params;
+ int status, err, retry = 20;
+
+ do {
+ wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+ wmt_params.flag = 0xF0;
+ wmt_params.dlen = 0;
+ wmt_params.data = NULL;
+ wmt_params.status = &status;
+
+ err = wmt_cmd_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to query CBMCU patch status (%d)", err);
+ return err;
+ }
+
+ *patch_status = (u8)status;
+
+ if (*patch_status == BTMTK_WMT_PATCH_PROGRESS) {
+ msleep(100);
+ retry--;
+ } else {
+ break;
+ }
+ } while (retry > 0);
+
+ return 0;
+}
+
+static int btmtk_query_cbmcu_section(struct hci_dev *hdev,
+ wmt_cmd_sync_func_t wmt_cmd_sync,
+ u8 cbmcu_type,
+ const u8 *section_map,
+ u32 cert_len)
+{
+ struct btmtk_hci_wmt_params wmt_params;
+ u8 cmd[64];
+ int status, err;
+
+ cmd[0] = 0;
+ cmd[1] = cbmcu_type;
+
+ if (cbmcu_type == 0)
+ put_unaligned_le32(cert_len, &cmd[2]);
+ else
+ memcpy(&cmd[2], section_map, MTK_SEC_MAP_NEED_SEND_SIZE);
+
+ wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+ wmt_params.flag = 0;
+ wmt_params.dlen = cbmcu_type ?
+ MTK_SEC_MAP_NEED_SEND_SIZE + 2 :
+ MTK_SEC_MAP_LENGTH_SIZE + 2;
+ wmt_params.data = cmd;
+ wmt_params.status = &status;
+
+ err = wmt_cmd_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to query CBMCU section (%d)", err);
+ return err;
+ }
+
+ /* Query should return UNDONE status for successful section query */
+ if (status != BTMTK_WMT_PATCH_UNDONE) {
+ bt_dev_err(hdev, "CBMCU section query status error (%d)", status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int btmtk_download_cbmcu_section(struct hci_dev *hdev,
+ wmt_cmd_sync_func_t wmt_cmd_sync,
+ const u8 *fw_data,
+ u32 dl_size)
+{
+ struct btmtk_hci_wmt_params wmt_params;
+ u32 sent_len, total_size = dl_size;
+ int err;
+
+ wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+ wmt_params.status = NULL;
+
+ while (dl_size > 0) {
+ sent_len = min_t(u32, 250, dl_size);
+
+ if (dl_size == total_size)
+ wmt_params.flag = 1;
+ else if (dl_size == sent_len)
+ wmt_params.flag = 3;
+ else
+ wmt_params.flag = 2;
+
+ wmt_params.dlen = sent_len;
+ wmt_params.data = fw_data;
+
+ err = wmt_cmd_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to send CBMCU section data (%d)", err);
+ return err;
+ }
+
+ dl_size -= sent_len;
+ fw_data += sent_len;
+ }
+
+ return 0;
+}
+
+static int btmtk_enable_cbmcu_patch(struct hci_dev *hdev,
+ wmt_cmd_sync_func_t wmt_cmd_sync)
+{
+ struct btmtk_hci_wmt_params wmt_params;
+ int err;
+
+ wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+ wmt_params.flag = 0xF1;
+ wmt_params.dlen = 0;
+ wmt_params.data = NULL;
+ wmt_params.status = NULL;
+
+ err = wmt_cmd_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to enable CBMCU patch (%d)", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int btmtk_load_cbmcu_firmware(struct hci_dev *hdev,
+ const char *fwname,
+ wmt_cmd_sync_func_t wmt_cmd_sync)
+{
+ struct btmtk_patch_header *hdr;
+ struct btmtk_global_desc *globaldesc;
+ struct btmtk_section_map *sectionmap;
+ const struct firmware *fw;
+ const u8 *fw_ptr;
+ u8 *cert_buf = NULL;
+ u32 section_num, section_offset, dl_size, cert_len;
+ int i, err;
+
+ err = request_firmware(&fw, fwname, &hdev->dev);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to load CBMCU firmware file %s (%d)",
+ fwname, err);
+ return err;
+ }
+
+ if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE) {
+ bt_dev_err(hdev, "CBMCU firmware too small (%zu bytes)", fw->size);
+ err = -EINVAL;
+ goto err_release_fw;
+ }
+
+ fw_ptr = fw->data;
+ hdr = (struct btmtk_patch_header *)fw_ptr;
+ globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
+ section_num = le32_to_cpu(globaldesc->section_num);
+
+ if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE +
+ (size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num) {
+ bt_dev_err(hdev, "CBMCU firmware truncated: size=%zu, expected=%zu (section_num=%u)",
+ fw->size,
+ MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE +
+ (size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num,
+ section_num);
+ err = -EINVAL;
+ goto err_release_fw;
+ }
+
+ bt_dev_info(hdev, "CBMCU Version: 0x%04x%04x, Build Time: %s",
+ le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime);
+
+ /* Phase 1: Download section type MTK_SEC_CBMCU_DESC */
+ for (i = 0; i < section_num; i++) {
+ sectionmap = (struct btmtk_section_map *)
+ (fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
+ MTK_FW_ROM_PATCH_GD_SIZE +
+ MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
+
+ /* Only process MTK_SEC_CBMCU_DESC section in Phase 1 */
+ if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) != MTK_SEC_CBMCU_DESC)
+ continue;
+
+ section_offset = le32_to_cpu(sectionmap->secoffset);
+ dl_size = le32_to_cpu(sectionmap->secsize);
+
+ if (dl_size == 0)
+ continue;
+
+ if (section_offset > fw->size ||
+ dl_size > fw->size - section_offset) {
+ bt_dev_err(hdev, "CBMCU Phase 1 section out of bounds");
+ err = -EINVAL;
+ goto err_release_fw;
+ }
+
+ cert_len = MTK_FW_ROM_PATCH_GD_SIZE +
+ MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num +
+ dl_size;
+
+ /* Query cbmcu section */
+ err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync, 0, NULL,
+ cert_len);
+ if (err < 0)
+ goto err_release_fw;
+
+ cert_buf = kmalloc(cert_len, GFP_KERNEL);
+ if (!cert_buf) {
+ err = -ENOMEM;
+ goto err_release_fw;
+ }
+
+ /* Copy Global Descriptor + All Section Maps */
+ memcpy(cert_buf,
+ fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE,
+ MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num);
+
+ /* Copy Phase 1 section data */
+ memcpy(cert_buf + MTK_FW_ROM_PATCH_GD_SIZE +
+ MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num,
+ fw_ptr + section_offset,
+ dl_size);
+
+ /* Download Phase 1 section */
+ err = btmtk_download_cbmcu_section(hdev, wmt_cmd_sync,
+ cert_buf, cert_len);
+ kfree(cert_buf);
+ cert_buf = NULL;
+
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to download CBMCU Phase 1 section (%d)", err);
+ goto err_release_fw;
+ }
+
+ break;
+ }
+
+ /* Phase 2: Download other sections (type != MTK_SEC_CBMCU_DESC) */
+ for (i = 0; i < section_num; i++) {
+ sectionmap = (struct btmtk_section_map *)
+ (fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
+ MTK_FW_ROM_PATCH_GD_SIZE +
+ MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
+
+ /* Skip MTK_SEC_CBMCU_DESC section in Phase 2 */
+ if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) == MTK_SEC_CBMCU_DESC)
+ continue;
+
+ section_offset = le32_to_cpu(sectionmap->secoffset);
+ dl_size = le32_to_cpu(sectionmap->bin_info_spec.dlsize);
+
+ if (dl_size == 0)
+ continue;
+
+ if (section_offset > fw->size ||
+ dl_size > fw->size - section_offset) {
+ bt_dev_err(hdev, "CBMCU Phase 2 section %d out of bounds", i);
+ err = -EINVAL;
+ goto err_release_fw;
+ }
+
+ /* Query cbmcu section */
+ err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync, 1,
+ (u8 *)§ionmap->bin_info_spec,
+ 0);
+ if (err < 0)
+ goto err_release_fw;
+
+ /* Download section data */
+ err = btmtk_download_cbmcu_section(hdev, wmt_cmd_sync,
+ fw_ptr + section_offset,
+ dl_size);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to download CBMCU section %d (%d)", i, err);
+ goto err_release_fw;
+ }
+ }
+
+ /* Wait for firmware activation */
+ usleep_range(100000, 120000);
+
+ bt_dev_info(hdev, "CBMCU firmware download completed");
+
+err_release_fw:
+ release_firmware(fw);
+ return err;
+}
+
+static int btmtk_setup_cbmcu_firmware(struct hci_dev *hdev,
+ wmt_cmd_sync_func_t wmt_cmd_sync,
+ u32 dev_id)
+{
+ char cbmcu_fwname[64];
+ u8 patch_status;
+ int err;
+
+ err = btmtk_cbmcu_patch_status(hdev, wmt_cmd_sync, &patch_status);
+ if (err < 0)
+ return err;
+
+ bt_dev_dbg(hdev, "CBMCU patch status: 0x%02x", patch_status);
+
+ if (patch_status != BTMTK_WMT_PATCH_UNDONE)
+ return 0;
+
+ snprintf(cbmcu_fwname, sizeof(cbmcu_fwname),
+ "mediatek/mt7928/CBMCU_CODE_MT%04x_1_1.bin",
+ dev_id & 0xffff);
+
+ err = btmtk_load_cbmcu_firmware(hdev, cbmcu_fwname, wmt_cmd_sync);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to download CBMCU firmware (%d)", err);
+ return err;
+ }
+
+ err = btmtk_enable_cbmcu_patch(hdev, wmt_cmd_sync);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
{
u32 val;
@@ -894,7 +1229,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
if (err < 0)
return err;
msleep(100);
- } else if (dev_id == 0x7925 || dev_id == 0x6639) {
+ } else if (dev_id == 0x7925 || dev_id == 0x6639 || dev_id == 0x7935) {
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
if (err < 0)
return err;
@@ -1379,6 +1714,15 @@ int btmtk_usb_setup(struct hci_dev *hdev)
case 0x7668:
fwname = FIRMWARE_MT7668;
break;
+ case 0x7935:
+ /* Requires CBMCU firmware before BT firmware */
+ err = btmtk_setup_cbmcu_firmware(hdev, btmtk_usb_hci_wmt_sync,
+ dev_id);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to set up CBMCU firmware (%d)", err);
+ return err;
+ }
+ fallthrough;
case 0x7922:
case 0x7925:
/*
@@ -1596,3 +1940,5 @@ MODULE_FIRMWARE(FIRMWARE_MT7922);
MODULE_FIRMWARE(FIRMWARE_MT7961);
MODULE_FIRMWARE(FIRMWARE_MT7925);
MODULE_FIRMWARE(FIRMWARE_MT7927);
+MODULE_FIRMWARE(FIRMWARE_MT7928);
+MODULE_FIRMWARE(FIRMWARE_MT7928_CBMCU);
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index c83c24897c95..6d3bf6b74a1d 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -9,6 +9,8 @@
#define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
#define FIRMWARE_MT7925 "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
#define FIRMWARE_MT7927 "mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin"
+#define FIRMWARE_MT7928 "mediatek/mt7928/BT_RAM_CODE_MT7935_1_1_hdr.bin"
+#define FIRMWARE_MT7928_CBMCU "mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin"
#define HCI_EV_WMT 0xe4
#define HCI_WMT_MAX_EVENT_SIZE 64
@@ -54,6 +56,7 @@ enum {
BTMTK_WMT_RST = 0x7,
BTMTK_WMT_REGISTER = 0x8,
BTMTK_WMT_SEMAPHORE = 0x17,
+ BTMTK_WMT_CBMCU_DWNLD = 0x58,
};
enum {
--
2.45.2
^ permalink raw reply related
* Re: [PATCH] net: airoha: Stop TX queues on error path in airoha_dev_open
From: Jakub Kicinski @ 2026-06-17 23:44 UTC (permalink / raw)
To: Wayen Yan
Cc: netdev, lorenzo, horms, pabeni, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek
In-Reply-To: <178160729880.2156257.7978513589649053826@gmail.com>
On Tue, 16 Jun 2026 18:50:39 +0800 Wayen Yan wrote:
> In airoha_dev_open(), if airoha_set_vip_for_gdm_port() fails after
> netif_tx_start_all_queues() has been called, the TX queues remain
> started while the device configuration is incomplete. This leaves
> the device in an inconsistent state where packets could be
> transmitted before the VIP/IFC port configuration is complete.
Not sure if this was superseded by another posting but FWIW
this posting did not apply.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox