lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260130122353.2263273-3-cosmin-gabriel.tanislav.xa@renesas.com>
Date: Fri, 30 Jan 2026 14:23:50 +0200
From: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@...esas.com>
To: Biju Das <biju.das.jz@...renesas.com>,
	William Breathitt Gray <wbg@...nel.org>,
	Uwe Kleine-König <ukleinek@...nel.org>,
	Lee Jones <lee@...nel.org>,
	Thierry Reding <thierry.reding@...il.com>
Cc: linux-iio@...r.kernel.org,
	linux-renesas-soc@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	linux-pwm@...r.kernel.org,
	Cosmin Tanislav <cosmin-gabriel.tanislav.xa@...esas.com>,
	stable@...r.kernel.org
Subject: [PATCH 2/5] pwm: rz-mtu3: impose period restrictions

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@...r.kernel.org
Fixes: 254d3a727421 ("pwm: Add Renesas RZ/G2L MTU3a PWM driver")
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@...esas.com>
---
 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 have
  *   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 another
+ *   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, struct pwm_device *pwm,
 	bool is_primary;
 	u8 prescale;
 	u16 pv, dc;
-	u8 val;
 	u32 ch;
 
 	priv = 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,
 
 	period_cycles = mul_u64_u32_div(state->period, rz_mtu3_pwm->rate,
 					NSEC_PER_SEC);
-	prescale = rz_mtu3_pwm_calculate_prescale(rz_mtu3_pwm, period_cycles);
 
 	/*
-	 * 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 = rz_mtu3_sibling_hwpwm(pwm->hwpwm, is_primary);
 
 		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;
 
-			prescale = rz_mtu3_pwm->prescale[ch];
+			period_cycles = rz_mtu3_pwm->period_cycles[ch];
 		}
 	}
 
+	prescale = rz_mtu3_pwm_calculate_prescale(rz_mtu3_pwm, period_cycles);
 	pv = rz_mtu3_pwm_calculate_pv_or_dc(period_cycles, prescale);
 
 	duty_cycles = mul_u64_u32_div(state->duty_cycle, rz_mtu3_pwm->rate,
 				      NSEC_PER_SEC);
+	if (duty_cycles > period_cycles)
+		duty_cycles = period_cycles;
 	dc = rz_mtu3_pwm_calculate_pv_or_dc(duty_cycles, prescale);
 
 	/*
@@ -379,20 +389,19 @@ static int rz_mtu3_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 			return rc;
 	}
 
-	val = RZ_MTU3_TCR_CKEG_RISING | prescale;
-
 	/* Counter must be stopped while updating TCR register */
 	if (rz_mtu3_pwm->prescale[ch] != prescale && rz_mtu3_pwm->enable_count[ch])
 		rz_mtu3_disable(priv->mtu);
 
+	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, struct pwm_device *pwm,
 			rz_mtu3_enable(priv->mtu);
 	}
 
+	rz_mtu3_pwm->period_cycles[ch] = 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));
-- 
2.52.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ