From nobody Mon Feb 9 12:11:20 2026 Received: from relmlie6.idc.renesas.com (relmlor2.renesas.com [210.160.252.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id F2C1627703E; Fri, 30 Jan 2026 12:25:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.160.252.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775903; cv=none; b=p8WU5XUtWga9xYvY3A8Xs8LHtTpz308cD+18ESuPrZ55Zxppv7IaDRskdxbXqQrKYaM7Nc3UvEX9X5lkYqAuzr5BRZdq8LPXhlEikcnO0NZhubmZOlDAdYKPdg0nk9iKg9wEpOWeLphdDvA+EMPQIuwUugSuKNV8WAugTfG7uZE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775903; c=relaxed/simple; bh=UCnersT8IzdlengUOQCVxr4bmPM5TvftAlC9gzv+n9Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AeEVT52kjYuqWf6kAim4+39d2cxhhe/2whu/KvbWoMeWu8S00jPLvaoXBQYu/KXRODL0jjwWvfD8f5vkIvyuQ0ceZ8f8O8GNx4wXxZ2FTiU+a8vw9fyhOi6ss6B6faDN/HrJLddGPg/b1R7JEu+5fkBeENNVtJ/VqbPX+uSEg2s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com; spf=pass smtp.mailfrom=renesas.com; arc=none smtp.client-ip=210.160.252.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=renesas.com X-CSE-ConnectionGUID: DuDAgcJJQ9WiR5gqxtAtRQ== X-CSE-MsgGUID: 0QgbC7VoTISaoYFjLhwfJw== Received: from unknown (HELO relmlir6.idc.renesas.com) ([10.200.68.152]) by relmlie6.idc.renesas.com with ESMTP; 30 Jan 2026 21:24:54 +0900 Received: from demon-pc.localdomain (unknown [10.226.92.78]) by relmlir6.idc.renesas.com (Postfix) with ESMTP id C799C419087D; Fri, 30 Jan 2026 21:24:50 +0900 (JST) From: Cosmin Tanislav To: Biju Das , William Breathitt Gray , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Lee Jones , Thierry Reding Cc: linux-iio@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, Cosmin Tanislav , stable@vger.kernel.org Subject: [PATCH 1/5] pwm: rz-mtu3: fix prescale check when enabling 2nd channel Date: Fri, 30 Jan 2026 14:23:49 +0200 Message-ID: <20260130122353.2263273-2-cosmin-gabriel.tanislav.xa@renesas.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> References: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" enable_count is only incremented after rz_mtu3_pwm_config() is called for the current PWM channel, causing prescale to not be checked if one PWM channel is enabled and we're enabling the second PWM channel of the same HW channel. To handle this edge case, if the user_count of the HW channel is larger than 1 and the sibling PWM channel is enabled, check that the new prescale is not smaller than the sibling's prescale. If the new prescale is larger than the sibling's prescale, use the sibling's prescale. The user_count check is ensures that we are indeed dealing with a HW channel that has two IOs. Cc: stable@vger.kernel.org Fixes: 254d3a727421 ("pwm: Add Renesas RZ/G2L MTU3a PWM driver") Signed-off-by: Cosmin Tanislav --- drivers/pwm/pwm-rz-mtu3.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-rz-mtu3.c b/drivers/pwm/pwm-rz-mtu3.c index ab39bd37edaf..f6073be1c2f8 100644 --- a/drivers/pwm/pwm-rz-mtu3.c +++ b/drivers/pwm/pwm-rz-mtu3.c @@ -142,6 +142,14 @@ rz_mtu3_get_channel(struct rz_mtu3_pwm_chip *rz_mtu3_p= wm, u32 hwpwm) return priv; } =20 +static u32 rz_mtu3_sibling_hwpwm(u32 hwpwm, bool is_primary) +{ + if (is_primary) + return hwpwm + 1; + else + return hwpwm - 1; +} + static bool rz_mtu3_pwm_is_ch_enabled(struct rz_mtu3_pwm_chip *rz_mtu3_pwm, u32 hwpwm) { @@ -322,6 +330,7 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, st= ruct pwm_device *pwm, struct rz_mtu3_pwm_channel *priv; u64 period_cycles; u64 duty_cycles; + bool is_primary; u8 prescale; u16 pv, dc; u8 val; @@ -329,6 +338,7 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, st= ruct pwm_device *pwm, =20 priv =3D rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm); ch =3D priv - rz_mtu3_pwm->channel_data; + is_primary =3D priv->map->base_pwm_number =3D=3D pwm->hwpwm; =20 period_cycles =3D mul_u64_u32_div(state->period, rz_mtu3_pwm->rate, NSEC_PER_SEC); @@ -340,11 +350,15 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, = struct pwm_device *pwm, * different settings. Modify prescalar if other PWM is off or handle * it, if current prescale value is less than the one we want to set. */ - if (rz_mtu3_pwm->enable_count[ch] > 1) { - if (rz_mtu3_pwm->prescale[ch] > prescale) - return -EBUSY; + if (rz_mtu3_pwm->user_count[ch] > 1) { + u32 sibling_hwpwm =3D rz_mtu3_sibling_hwpwm(pwm->hwpwm, is_primary); =20 - prescale =3D rz_mtu3_pwm->prescale[ch]; + if (rz_mtu3_pwm_is_ch_enabled(rz_mtu3_pwm, sibling_hwpwm)) { + if (rz_mtu3_pwm->prescale[ch] > prescale) + return -EBUSY; + + prescale =3D rz_mtu3_pwm->prescale[ch]; + } } =20 pv =3D rz_mtu3_pwm_calculate_pv_or_dc(period_cycles, prescale); @@ -371,7 +385,7 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, st= ruct pwm_device *pwm, if (rz_mtu3_pwm->prescale[ch] !=3D prescale && rz_mtu3_pwm->enable_count[= ch]) rz_mtu3_disable(priv->mtu); =20 - if (priv->map->base_pwm_number =3D=3D pwm->hwpwm) { + if (is_primary) { rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA | val); rz_mtu3_pwm_write_tgr_registers(priv, RZ_MTU3_TGRA, pv, --=20 2.52.0 From nobody Mon Feb 9 12:11:20 2026 Received: from relmlie6.idc.renesas.com (relmlor2.renesas.com [210.160.252.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BC50137A486; Fri, 30 Jan 2026 12:25:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.160.252.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775906; cv=none; b=eNEFBZmY/7Xh3YNzitMiije/OPGOMyi7UvVhw275aWWe3d6xSuI9L/33wxE2+4GWlhSGfyMgSM6vaigfb4s+NBWHua6P0oy5YvRLbA4M0bo2pgtWMFuDmXSVc60iJVTMdGc23Zly9UeR1b7fbdimVnJatAdeZB6urCN5vOjvsIM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775906; c=relaxed/simple; bh=3HLy3NLYhHfGM1yhkdhqvvH3wJsXKlhu/Cs2/PJAsPw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Nj1jNSeN5ivYDyRoUc6R8+b/aVV+9258HBM9EbOOIj2Qs6pBv7s9qXhNV6W+aMDiyZsR6qFCBiP1fHPsNOaEz6KAIWBpFe6MGxOOkQy6/QHoCtrV8xDNgk0ssYCdX7IFIV36DwV4x2I5mOFxYCCOV3KfaeRe+QMYj0IFZk05ibI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com; spf=pass smtp.mailfrom=renesas.com; arc=none smtp.client-ip=210.160.252.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=renesas.com X-CSE-ConnectionGUID: +ddeJPQMTJSSbb0AxMA1jg== X-CSE-MsgGUID: A48i6VkFRJ+juLR/m06Tfw== Received: from unknown (HELO relmlir6.idc.renesas.com) ([10.200.68.152]) by relmlie6.idc.renesas.com with ESMTP; 30 Jan 2026 21:24:59 +0900 Received: from demon-pc.localdomain (unknown [10.226.92.78]) by relmlir6.idc.renesas.com (Postfix) with ESMTP id A2B334190888; Fri, 30 Jan 2026 21:24:55 +0900 (JST) From: Cosmin Tanislav To: Biju Das , William Breathitt Gray , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Lee Jones , Thierry Reding Cc: linux-iio@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, Cosmin Tanislav , stable@vger.kernel.org Subject: [PATCH 2/5] pwm: rz-mtu3: impose period restrictions Date: Fri, 30 Jan 2026 14:23:50 +0200 Message-ID: <20260130122353.2263273-3-cosmin-gabriel.tanislav.xa@renesas.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> References: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The counter is shared by all IOs of a HW channel, and we cannot clear it from multiple sources, as the TCR register for each HW channel can only select one clearing source between TGRA, TGRB, TGRC, and TGRD, or the counter being cleared in another channel when synchronous clearing is enabled. Because of this hardware limitation, both IOs of a HW channel must share the same period. To provide some flexibility, allow setting different periods on each PWM channel, with the following restrictions. If the requested period is smaller than the already programmed period of the sibling PWM channel, return -EBUSY. Otherwise, if the requested period is larger to the already programmed period of the sibling PWM channel, adjust the requested period to match the already programmed period, and adjust the duty cycle to not exceed the already programmed period. Since only one period is being used, always use TGRA for resetting the counter, and program TGRA for secondary IOs too. Cc: stable@vger.kernel.org Fixes: 254d3a727421 ("pwm: Add Renesas RZ/G2L MTU3a PWM driver") Signed-off-by: Cosmin Tanislav --- drivers/pwm/pwm-rz-mtu3.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/pwm/pwm-rz-mtu3.c b/drivers/pwm/pwm-rz-mtu3.c index f6073be1c2f8..7558e28f4786 100644 --- a/drivers/pwm/pwm-rz-mtu3.c +++ b/drivers/pwm/pwm-rz-mtu3.c @@ -18,6 +18,13 @@ * - MTU{1, 2} channels have a single IO, whereas all other HW channels ha= ve * 2 IOs. * - Each IO is modelled as an independent PWM channel. + * - Sibling IOs must use the same period as they share a common counter. + * The counter can be reset on one of the following conditions: TGRA or = TGRB + * or TGRC or TGRD compare match, or when the counter is cleared in anot= her + * channel when synchronous clearing is enabled. + * The driver always uses TGRA compare match to reset the counter. + * The driver adjusts the period and duty cycle of the sibling IO when + * appropriate. * - rz_mtu3_channel_io_map table is used to map the PWM channel to the * corresponding HW channel as there are difference in number of IOs * between HW channels. @@ -64,6 +71,7 @@ struct rz_mtu3_pwm_channel { * @clk: MTU3 module clock * @lock: Lock to prevent concurrent access for usage count * @rate: MTU3 clock rate + * @period_cycles: MTU3 period cycles * @user_count: MTU3 usage count * @enable_count: MTU3 enable count * @prescale: MTU3 prescale @@ -74,6 +82,7 @@ struct rz_mtu3_pwm_chip { struct clk *clk; struct mutex lock; unsigned long rate; + u64 period_cycles[RZ_MTU3_MAX_HW_CHANNELS]; u32 user_count[RZ_MTU3_MAX_HW_CHANNELS]; u32 enable_count[RZ_MTU3_MAX_HW_CHANNELS]; u8 prescale[RZ_MTU3_MAX_HW_CHANNELS]; @@ -333,7 +342,6 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, st= ruct pwm_device *pwm, bool is_primary; u8 prescale; u16 pv, dc; - u8 val; u32 ch; =20 priv =3D rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm); @@ -342,29 +350,31 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, = struct pwm_device *pwm, =20 period_cycles =3D mul_u64_u32_div(state->period, rz_mtu3_pwm->rate, NSEC_PER_SEC); - prescale =3D rz_mtu3_pwm_calculate_prescale(rz_mtu3_pwm, period_cycles); =20 /* - * Prescalar is shared by multiple channels, so prescale can - * NOT be modified when there are multiple channels in use with - * different settings. Modify prescalar if other PWM is off or handle - * it, if current prescale value is less than the one we want to set. + * The counter is shared by all IOs of a HW channel, and we cannot clear + * it from multiple sources, as the TCR register for each HW channel can + * only select one clearing source between TGRA, TGRB, TGRC, and TGRD. + * Enforce that all IOs use the same period cycle. */ if (rz_mtu3_pwm->user_count[ch] > 1) { u32 sibling_hwpwm =3D rz_mtu3_sibling_hwpwm(pwm->hwpwm, is_primary); =20 if (rz_mtu3_pwm_is_ch_enabled(rz_mtu3_pwm, sibling_hwpwm)) { - if (rz_mtu3_pwm->prescale[ch] > prescale) + if (rz_mtu3_pwm->period_cycles[ch] > period_cycles) return -EBUSY; =20 - prescale =3D rz_mtu3_pwm->prescale[ch]; + period_cycles =3D rz_mtu3_pwm->period_cycles[ch]; } } =20 + prescale =3D rz_mtu3_pwm_calculate_prescale(rz_mtu3_pwm, period_cycles); pv =3D rz_mtu3_pwm_calculate_pv_or_dc(period_cycles, prescale); =20 duty_cycles =3D mul_u64_u32_div(state->duty_cycle, rz_mtu3_pwm->rate, NSEC_PER_SEC); + if (duty_cycles > period_cycles) + duty_cycles =3D period_cycles; dc =3D rz_mtu3_pwm_calculate_pv_or_dc(duty_cycles, prescale); =20 /* @@ -379,20 +389,19 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, = struct pwm_device *pwm, return rc; } =20 - val =3D RZ_MTU3_TCR_CKEG_RISING | prescale; - /* Counter must be stopped while updating TCR register */ if (rz_mtu3_pwm->prescale[ch] !=3D prescale && rz_mtu3_pwm->enable_count[= ch]) rz_mtu3_disable(priv->mtu); =20 + rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA | + RZ_MTU3_TCR_CKEG_RISING | prescale); + if (is_primary) { - rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TCR, - RZ_MTU3_TCR_CCLR_TGRA | val); rz_mtu3_pwm_write_tgr_registers(priv, RZ_MTU3_TGRA, pv, RZ_MTU3_TGRB, dc); } else { - rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TCR, - RZ_MTU3_TCR_CCLR_TGRC | val); + /* TGRA is used to reset the counter for both IOs. */ + rz_mtu3_16bit_ch_write(priv->mtu, RZ_MTU3_TGRA, pv); rz_mtu3_pwm_write_tgr_registers(priv, RZ_MTU3_TGRC, pv, RZ_MTU3_TGRD, dc); } @@ -409,6 +418,8 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, st= ruct pwm_device *pwm, rz_mtu3_enable(priv->mtu); } =20 + rz_mtu3_pwm->period_cycles[ch] =3D period_cycles; + /* If the PWM is not enabled, turn the clock off again to save power. */ if (!pwm->state.enabled) pm_runtime_put(pwmchip_parent(chip)); --=20 2.52.0 From nobody Mon Feb 9 12:11:20 2026 Received: from relmlie5.idc.renesas.com (relmlor1.renesas.com [210.160.252.171]) by smtp.subspace.kernel.org (Postfix) with ESMTP id B4C1F3793DA; Fri, 30 Jan 2026 12:25:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.160.252.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775907; cv=none; b=ZK1IrubF6lLq0MQsM9TO8SwXLmrjPJkWwjdoesinvtramnSgmMy/dDSMjucrxtpMqznWEhJDhg/yhXHu2eufLRluv5MVoOfWUCzOsH+tUW0oltMcYMPnK+c90AILZFEE02L7q6EIYw8xr50OBxSMyQMgCkJVnW6DruU+Vo3+k0s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775907; c=relaxed/simple; bh=aWxSqyUwGAOoEHpdWB2kVArbfiZoKhA+/2A39gDK1eQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=meRASec7BTxUf3Kt1SYTpCHhGonL1pv2c+J4DG94Agsi8DTUxm9FF6LzKxe1R8iYIc1ibg/O2g5lR4avRBAoVLJ7VaZlP3vhyjnWbTvTAjNSM1pFjRnoCKoXca64KwEG97J99l3MlgmMsb+KzHawgTtWFvkjFQ0lpmS69+ZcYzo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com; spf=pass smtp.mailfrom=renesas.com; arc=none smtp.client-ip=210.160.252.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=renesas.com X-CSE-ConnectionGUID: Q9ShKTg3QuC0RJlV7XeW1Q== X-CSE-MsgGUID: BBud2CgoRF+3ZKjE0NjVxg== Received: from unknown (HELO relmlir6.idc.renesas.com) ([10.200.68.152]) by relmlie5.idc.renesas.com with ESMTP; 30 Jan 2026 21:25:03 +0900 Received: from demon-pc.localdomain (unknown [10.226.92.78]) by relmlir6.idc.renesas.com (Postfix) with ESMTP id 629954190888; Fri, 30 Jan 2026 21:25:00 +0900 (JST) From: Cosmin Tanislav To: Biju Das , William Breathitt Gray , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Lee Jones , Thierry Reding Cc: linux-iio@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, Cosmin Tanislav , stable@vger.kernel.org Subject: [PATCH 3/5] pwm: rz-mtu3: correctly enable HW channel 4 and 7 Date: Fri, 30 Jan 2026 14:23:51 +0200 Message-ID: <20260130122353.2263273-4-cosmin-gabriel.tanislav.xa@renesas.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> References: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" HW channels 4 and 7 require an additional bit to be set in the TOER{A,B} registers in order to enable PWM output. Add the necessary logic to update these bits when enabling or disabling PWM on these channels. Cc: stable@vger.kernel.org Fixes: 254d3a727421 ("pwm: Add Renesas RZ/G2L MTU3a PWM driver") Signed-off-by: Cosmin Tanislav --- drivers/pwm/pwm-rz-mtu3.c | 40 +++++++++++++++++++++++++++++++++++-- include/linux/mfd/rz-mtu3.h | 2 ++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-rz-mtu3.c b/drivers/pwm/pwm-rz-mtu3.c index 7558e28f4786..ed5fbc4015aa 100644 --- a/drivers/pwm/pwm-rz-mtu3.c +++ b/drivers/pwm/pwm-rz-mtu3.c @@ -226,10 +226,37 @@ static void rz_mtu3_pwm_free(struct pwm_chip *chip, s= truct pwm_device *pwm) mutex_unlock(&rz_mtu3_pwm->lock); } =20 +static void rz_mtu3_pwm_set_toer_bit(struct rz_mtu3_pwm_chip *rz_mtu3_pwm, + struct rz_mtu3_pwm_channel *priv, + bool is_primary, bool set) +{ + u8 bitpos; + u16 reg; + + /* + * HW channels 4 and 7 require an additional register write to enable + * PWM output. + */ + if (priv->mtu->channel_number =3D=3D RZ_MTU3_CHAN_4) + reg =3D RZ_MTU3_TOERA; + else if (priv->mtu->channel_number =3D=3D RZ_MTU3_CHAN_7) + reg =3D RZ_MTU3_TOERB; + else + return; + + if (is_primary) + bitpos =3D 1; + else + bitpos =3D 4; + + rz_mtu3_shared_reg_update_bit(priv->mtu, reg, bitpos, set); +} + static int rz_mtu3_pwm_enable(struct pwm_chip *chip, struct pwm_device *pw= m) { struct rz_mtu3_pwm_chip *rz_mtu3_pwm =3D to_rz_mtu3_pwm_chip(chip); struct rz_mtu3_pwm_channel *priv; + bool is_primary; u32 ch; u8 val; int rc; @@ -240,10 +267,15 @@ static int rz_mtu3_pwm_enable(struct pwm_chip *chip, = struct pwm_device *pwm) =20 priv =3D rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm); ch =3D priv - rz_mtu3_pwm->channel_data; + is_primary =3D priv->map->base_pwm_number =3D=3D pwm->hwpwm; + val =3D RZ_MTU3_TIOR_OC_IOB_TOGGLE | RZ_MTU3_TIOR_OC_IOA_H_COMP_MATCH; =20 rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_MD_PWMMODE1= ); - if (priv->map->base_pwm_number =3D=3D pwm->hwpwm) + + rz_mtu3_pwm_set_toer_bit(rz_mtu3_pwm, priv, is_primary, true); + + if (is_primary) rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TIORH, val); else rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TIORL, val); @@ -262,17 +294,21 @@ static void rz_mtu3_pwm_disable(struct pwm_chip *chip= , struct pwm_device *pwm) { struct rz_mtu3_pwm_chip *rz_mtu3_pwm =3D to_rz_mtu3_pwm_chip(chip); struct rz_mtu3_pwm_channel *priv; + bool is_primary; u32 ch; =20 priv =3D rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm); ch =3D priv - rz_mtu3_pwm->channel_data; + is_primary =3D priv->map->base_pwm_number =3D=3D pwm->hwpwm; =20 /* Disable output pins of MTU3 channel */ - if (priv->map->base_pwm_number =3D=3D pwm->hwpwm) + if (is_primary) rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TIORH, RZ_MTU3_TIOR_OC_RETAIN); else rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TIORL, RZ_MTU3_TIOR_OC_RETAIN); =20 + rz_mtu3_pwm_set_toer_bit(rz_mtu3_pwm, priv, is_primary, false); + mutex_lock(&rz_mtu3_pwm->lock); rz_mtu3_pwm->enable_count[ch]--; if (!rz_mtu3_pwm->enable_count[ch]) diff --git a/include/linux/mfd/rz-mtu3.h b/include/linux/mfd/rz-mtu3.h index 8421d49500bf..37da5f7bb83a 100644 --- a/include/linux/mfd/rz-mtu3.h +++ b/include/linux/mfd/rz-mtu3.h @@ -10,6 +10,8 @@ #include =20 /* 8-bit shared register offsets macros */ +#define RZ_MTU3_TOERA 0x00A /* Timer output master enable register A */ +#define RZ_MTU3_TOERB 0x80A /* Timer output master enable register B */ #define RZ_MTU3_TSTRA 0x080 /* Timer start register A */ #define RZ_MTU3_TSTRB 0x880 /* Timer start register B */ =20 --=20 2.52.0 From nobody Mon Feb 9 12:11:20 2026 Received: from relmlie5.idc.renesas.com (relmlor1.renesas.com [210.160.252.171]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 86F91378810; Fri, 30 Jan 2026 12:25:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.160.252.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775911; cv=none; b=N0GocKFFAbJFju0q6VdnWucKBE6K0i6MOtupvRDXvUw/hG6ylSjrEM2JFlNNASrnTseP17pijANeC+9QjURNJdNyccrTnp/lnNNbkROUzg7xp43aV7bHOwuRt8dLdNjEVJs/u1umnKOU7+v/wZldaiJleBkKk/8jgTQxgVSjdpw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775911; c=relaxed/simple; bh=xhmPVXoMDCu27t4fzv10/A0dd5EUYSmW7DlIX7IuBis=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dFV/qsodaF+euR+OJWa1BfNSVnstGCGhW7mDSBJvkFTuMFTJojzUsCkjJ4i5O9WbycTXeN2MUkaL1lX0BIhZT6eLOnrdVWGdWrWvKgxC/SPbKjfdZmwwI48smQGmebyoEaN9SHgicfbHfUI6lFlG6SI+4TdTAnjSDo+FNCSqPr8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com; spf=pass smtp.mailfrom=renesas.com; arc=none smtp.client-ip=210.160.252.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=renesas.com X-CSE-ConnectionGUID: 4kL42DtVQM+HTyQ2uqJ0Ww== X-CSE-MsgGUID: 2sbEYGBaRJ2+U+YnQiScmA== Received: from unknown (HELO relmlir6.idc.renesas.com) ([10.200.68.152]) by relmlie5.idc.renesas.com with ESMTP; 30 Jan 2026 21:25:09 +0900 Received: from demon-pc.localdomain (unknown [10.226.92.78]) by relmlir6.idc.renesas.com (Postfix) with ESMTP id 044F84177CAA; Fri, 30 Jan 2026 21:25:04 +0900 (JST) From: Cosmin Tanislav To: Biju Das , William Breathitt Gray , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Lee Jones , Thierry Reding Cc: linux-iio@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, Cosmin Tanislav , stable@vger.kernel.org Subject: [PATCH 4/5] counter: rz-mtu3-cnt: prevent counter from being toggled multiple times Date: Fri, 30 Jan 2026 14:23:52 +0200 Message-ID: <20260130122353.2263273-5-cosmin-gabriel.tanislav.xa@renesas.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> References: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Runtime PM counter is incremented / decremented each time the sysfs enable file is written to. If user writes 0 to the sysfs enable file multiple times, runtime PM usage count underflows, generating the following message. rz-mtu3-counter rz-mtu3-counter.0: Runtime PM usage count underflow! At the same time, hardware registers end up being accessed with clocks off in rz_mtu3_terminate_counter() to disable an already disabled channel. If user writes 1 to the sysfs enable file multiple times, runtime PM usage count will be incremented each time, requiring the same number of 0 writes to get it back to 0. If user writes 0 to the sysfs enable file while PWM is in progress, PWM is stopped without counter being the owner of the underlying MTU3 channel. Check against the cached count_is_enabled value and exit if the user is trying to set the same enable value. Cc: stable@vger.kernel.org Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver") Signed-off-by: Cosmin Tanislav --- drivers/counter/rz-mtu3-cnt.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c index e755d54dfece..a4a8ef2d88f0 100644 --- a/drivers/counter/rz-mtu3-cnt.c +++ b/drivers/counter/rz-mtu3-cnt.c @@ -499,21 +499,25 @@ static int rz_mtu3_count_enable_write(struct counter_= device *counter, struct rz_mtu3_cnt *const priv =3D counter_priv(counter); int ret =3D 0; =20 + mutex_lock(&priv->lock); + + if (priv->count_is_enabled[count->id] =3D=3D enable) + goto exit; + if (enable) { - mutex_lock(&priv->lock); pm_runtime_get_sync(ch->dev); ret =3D rz_mtu3_initialize_counter(counter, count->id); if (ret =3D=3D 0) priv->count_is_enabled[count->id] =3D true; - mutex_unlock(&priv->lock); } else { - mutex_lock(&priv->lock); rz_mtu3_terminate_counter(counter, count->id); priv->count_is_enabled[count->id] =3D false; pm_runtime_put(ch->dev); - mutex_unlock(&priv->lock); } =20 +exit: + mutex_unlock(&priv->lock); + return ret; } =20 --=20 2.52.0 From nobody Mon Feb 9 12:11:20 2026 Received: from relmlie5.idc.renesas.com (relmlor1.renesas.com [210.160.252.171]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6F99336E485; Fri, 30 Jan 2026 12:25:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.160.252.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775916; cv=none; b=tlOYQtSGVfLSQcBTn40PFztebauYg1je70B1+c/Vwxi7/GV9OcqDTSoBL3hmEfbGj0cGQgrCPNrZQ4Pe+5wmfkmZiYGA1sco7JatO/z+sA40VbS/oZro82rfu0ikj4sWS9dRC0ZGePmT8rE1bq84OY1OMRShX/OvtjmdKxkhNno= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769775916; c=relaxed/simple; bh=eBNmb4XwvxZEc5QzJxcUFNRYKaKGG0cTVjnmf8T2EpU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WYtyJ26dhdOEsPCljr/I6Vo/9jJoXcD53bSOGZip5K4vWERMd0ItHKKCRY1cOaA1UNNnGzVHUWFOuekJKZraw6lVWrjgDNQBUbeEYWm8c68Br4FAJ5Rvv5i/4BdOwtRwfSAMvktE0Hs6OUF6p38aqfUEejDIm1IxLJFNU6F8LXQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com; spf=pass smtp.mailfrom=renesas.com; arc=none smtp.client-ip=210.160.252.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=renesas.com X-CSE-ConnectionGUID: vOUo3aDmQzWM2BvYqBU99w== X-CSE-MsgGUID: MdXTImUJTo+b8HUxOpb72Q== Received: from unknown (HELO relmlir6.idc.renesas.com) ([10.200.68.152]) by relmlie5.idc.renesas.com with ESMTP; 30 Jan 2026 21:25:14 +0900 Received: from demon-pc.localdomain (unknown [10.226.92.78]) by relmlir6.idc.renesas.com (Postfix) with ESMTP id 65AF64177D41; Fri, 30 Jan 2026 21:25:10 +0900 (JST) From: Cosmin Tanislav To: Biju Das , William Breathitt Gray , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Lee Jones , Thierry Reding Cc: linux-iio@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, Cosmin Tanislav , stable@vger.kernel.org Subject: [PATCH 5/5] counter: rz-mtu3-cnt: do not use struct rz_mtu3_channel's dev member Date: Fri, 30 Jan 2026 14:23:53 +0200 Message-ID: <20260130122353.2263273-6-cosmin-gabriel.tanislav.xa@renesas.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> References: <20260130122353.2263273-1-cosmin-gabriel.tanislav.xa@renesas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The counter driver can use HW channels 1 and 2, while the PWM driver can use HW channels 0, 1, 2, 3, 4, 6, 7. The dev member is assigned both by the counter driver and the PWM driver for channels 1 and 2, to their own struct device instance, overwriting the previous value. The sub-drivers race to assign their own struct device pointer to the same struct rz_mtu3_channel's dev member. The dev member of struct rz_mtu3_channel is used by the counter sub-driver for runtime PM. Depending on the probe order of the counter and PWM sub-drivers, the dev member may point to the wrong struct device instance, causing the counter sub-driver to do runtime PM actions on the wrong device. To fix this, use the parent pointer of the counter, which is assigned during probe to the correct struct device, not the struct device pointer inside the shared struct rz_mtu3_channel. Cc: stable@vger.kernel.org Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver") Signed-off-by: Cosmin Tanislav --- drivers/counter/rz-mtu3-cnt.c | 55 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c index a4a8ef2d88f0..7bfb6979193c 100644 --- a/drivers/counter/rz-mtu3-cnt.c +++ b/drivers/counter/rz-mtu3-cnt.c @@ -107,9 +107,9 @@ static bool rz_mtu3_is_counter_invalid(struct counter_d= evice *counter, int id) struct rz_mtu3_cnt *const priv =3D counter_priv(counter); unsigned long tmdr; =20 - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr =3D rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); =20 if (id =3D=3D RZ_MTU3_32_BIT_CH && test_bit(RZ_MTU3_TMDR3_LWA, &tmdr)) return false; @@ -165,12 +165,12 @@ static int rz_mtu3_count_read(struct counter_device *= counter, if (ret) return ret; =20 - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id =3D=3D RZ_MTU3_32_BIT_CH) *val =3D rz_mtu3_32bit_ch_read(ch, RZ_MTU3_TCNTLW); else *val =3D rz_mtu3_16bit_ch_read(ch, RZ_MTU3_TCNT); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); =20 return 0; @@ -187,26 +187,26 @@ static int rz_mtu3_count_write(struct counter_device = *counter, if (ret) return ret; =20 - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id =3D=3D RZ_MTU3_32_BIT_CH) rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TCNTLW, val); else rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, val); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); =20 return 0; } =20 static int rz_mtu3_count_function_read_helper(struct rz_mtu3_channel *cons= t ch, - struct rz_mtu3_cnt *const priv, + struct counter_device *const counter, enum counter_function *function) { u8 timer_mode; =20 - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); timer_mode =3D rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); =20 switch (timer_mode & RZ_MTU3_TMDR1_PH_CNT_MODE_MASK) { case RZ_MTU3_TMDR1_PH_CNT_MODE_1: @@ -240,7 +240,7 @@ static int rz_mtu3_count_function_read(struct counter_d= evice *counter, if (ret) return ret; =20 - ret =3D rz_mtu3_count_function_read_helper(ch, priv, function); + ret =3D rz_mtu3_count_function_read_helper(ch, counter, function); mutex_unlock(&priv->lock); =20 return ret; @@ -279,9 +279,9 @@ static int rz_mtu3_count_function_write(struct counter_= device *counter, return -EINVAL; } =20 - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); =20 return 0; @@ -300,9 +300,9 @@ static int rz_mtu3_count_direction_read(struct counter_= device *counter, if (ret) return ret; =20 - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); tsr =3D rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); =20 *direction =3D (tsr & RZ_MTU3_TSR_TCFD) ? COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD; @@ -377,14 +377,14 @@ static int rz_mtu3_count_ceiling_write(struct counter= _device *counter, return -EINVAL; } =20 - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id =3D=3D RZ_MTU3_32_BIT_CH) rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TGRALW, ceiling); else rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling); =20 rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); =20 return 0; @@ -495,7 +495,6 @@ static int rz_mtu3_count_enable_read(struct counter_dev= ice *counter, static int rz_mtu3_count_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct rz_mtu3_channel *const ch =3D rz_mtu3_get_ch(counter, count->id); struct rz_mtu3_cnt *const priv =3D counter_priv(counter); int ret =3D 0; =20 @@ -505,14 +504,14 @@ static int rz_mtu3_count_enable_write(struct counter_= device *counter, goto exit; =20 if (enable) { - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); ret =3D rz_mtu3_initialize_counter(counter, count->id); if (ret =3D=3D 0) priv->count_is_enabled[count->id] =3D true; } else { rz_mtu3_terminate_counter(counter, count->id); priv->count_is_enabled[count->id] =3D false; - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); } =20 exit: @@ -544,9 +543,9 @@ static int rz_mtu3_cascade_counts_enable_get(struct cou= nter_device *counter, if (ret) return ret; =20 - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr =3D rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); *cascade_enable =3D test_bit(RZ_MTU3_TMDR3_LWA, &tmdr); mutex_unlock(&priv->lock); =20 @@ -563,10 +562,10 @@ static int rz_mtu3_cascade_counts_enable_set(struct c= ounter_device *counter, if (ret) return ret; =20 - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, RZ_MTU3_TMDR3_LWA, cascade_enable); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); =20 return 0; @@ -583,9 +582,9 @@ static int rz_mtu3_ext_input_phase_clock_select_get(str= uct counter_device *count if (ret) return ret; =20 - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr =3D rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); *ext_input_phase_clock_select =3D test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr); mutex_unlock(&priv->lock); =20 @@ -602,11 +601,11 @@ static int rz_mtu3_ext_input_phase_clock_select_set(s= truct counter_device *count if (ret) return ret; =20 - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, RZ_MTU3_TMDR3_PHCKSEL, ext_input_phase_clock_select); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); =20 return 0; @@ -644,7 +643,7 @@ static int rz_mtu3_action_read(struct counter_device *c= ounter, if (ret) return ret; =20 - ret =3D rz_mtu3_count_function_read_helper(ch, priv, &function); + ret =3D rz_mtu3_count_function_read_helper(ch, counter, &function); if (ret) { mutex_unlock(&priv->lock); return ret; --=20 2.52.0