linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields
@ 2025-08-22  4:40 Rafael V. Volkmer
  2025-08-22  4:43 ` [PATCH v5 2/6] pwm: tiehrpwm: use FIELD_PREP()/FIELD_GET() for prescalers Rafael V. Volkmer
  2025-08-25  8:27 ` [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields Uwe Kleine-König
  0 siblings, 2 replies; 8+ messages in thread
From: Rafael V. Volkmer @ 2025-08-22  4:40 UTC (permalink / raw)
  To: ukleinek; +Cc: linux-kernel, linux-pwm, Rafael V. Volkmer

Make register field definitions use GENMASK() and FIELD_PREP() across
AQCTL, AQCSFRC, and TBCTL. This clarifies bit layout, reduces hand-rolled
shift logic, and aligns the driver with common kernel patterns.

No functional change intended.

Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@gmail.com>
---
 drivers/pwm/pwm-tiehrpwm.c | 88 ++++++++++++++++++++------------------
 1 file changed, 47 insertions(+), 41 deletions(-)

diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index 0125e73b9..8509dd587 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -13,6 +13,7 @@
 #include <linux/clk.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
+#include <linux/bitfield.h>
 
 /* EHRPWM registers and bits definitions */
 
@@ -21,15 +22,16 @@
 #define TBPRD			0x0A
 
 #define TBCTL_PRDLD_MASK	BIT(3)
-#define TBCTL_PRDLD_SHDW	0
-#define TBCTL_PRDLD_IMDT	BIT(3)
-#define TBCTL_CLKDIV_MASK	(BIT(12) | BIT(11) | BIT(10) | BIT(9) | \
-				BIT(8) | BIT(7))
-#define TBCTL_CTRMODE_MASK	(BIT(1) | BIT(0))
-#define TBCTL_CTRMODE_UP	0
-#define TBCTL_CTRMODE_DOWN	BIT(0)
-#define TBCTL_CTRMODE_UPDOWN	BIT(1)
-#define TBCTL_CTRMODE_FREEZE	(BIT(1) | BIT(0))
+#define TBCTL_PRDLD_SHDW	FIELD_PREP(TBCTL_PRDLD_MASK, 0)
+#define TBCTL_PRDLD_IMDT	FIELD_PREP(TBCTL_PRDLD_MASK, 1)
+
+#define TBCTL_CLKDIV_MASK	GENMASK(12, 7)
+
+#define TBCTL_CTRMODE_MASK	GENMASK(1, 0)
+#define TBCTL_CTRMODE_UP	FIELD_PREP(TBCTL_CTRMODE_MASK, 0)
+#define TBCTL_CTRMODE_DOWN	FIELD_PREP(TBCTL_CTRMODE_MASK, 1)
+#define TBCTL_CTRMODE_UPDOWN	FIELD_PREP(TBCTL_CTRMODE_MASK, 2)
+#define TBCTL_CTRMODE_FREEZE	FIELD_PREP(TBCTL_CTRMODE_MASK, 3)
 
 #define TBCTL_HSPCLKDIV_SHIFT	7
 #define TBCTL_CLKDIV_SHIFT	10
@@ -48,22 +50,25 @@
 #define AQSFRC			0x1A
 #define AQCSFRC			0x1C
 
-#define AQCTL_CBU_MASK		(BIT(9) | BIT(8))
-#define AQCTL_CBU_FRCLOW	BIT(8)
-#define AQCTL_CBU_FRCHIGH	BIT(9)
-#define AQCTL_CBU_FRCTOGGLE	(BIT(9) | BIT(8))
-#define AQCTL_CAU_MASK		(BIT(5) | BIT(4))
-#define AQCTL_CAU_FRCLOW	BIT(4)
-#define AQCTL_CAU_FRCHIGH	BIT(5)
-#define AQCTL_CAU_FRCTOGGLE	(BIT(5) | BIT(4))
-#define AQCTL_PRD_MASK		(BIT(3) | BIT(2))
-#define AQCTL_PRD_FRCLOW	BIT(2)
-#define AQCTL_PRD_FRCHIGH	BIT(3)
-#define AQCTL_PRD_FRCTOGGLE	(BIT(3) | BIT(2))
-#define AQCTL_ZRO_MASK		(BIT(1) | BIT(0))
-#define AQCTL_ZRO_FRCLOW	BIT(0)
-#define AQCTL_ZRO_FRCHIGH	BIT(1)
-#define AQCTL_ZRO_FRCTOGGLE	(BIT(1) | BIT(0))
+#define AQCTL_CBU_MASK		GENMASK(9, 8)
+#define AQCTL_CBU_FRCLOW	FIELD_PREP(AQCTL_CBU_MASK, 1)
+#define AQCTL_CBU_FRCHIGH	FIELD_PREP(AQCTL_CBU_MASK, 2)
+#define AQCTL_CBU_FRCTOGGLE	FIELD_PREP(AQCTL_CBU_MASK, 3)
+
+#define AQCTL_CAU_MASK		GENMASK(5, 4)
+#define AQCTL_CAU_FRCLOW	FIELD_PREP(AQCTL_CAU_MASK, 1)
+#define AQCTL_CAU_FRCHIGH	FIELD_PREP(AQCTL_CAU_MASK, 2)
+#define AQCTL_CAU_FRCTOGGLE	FIELD_PREP(AQCTL_CAU_MASK, 3)
+
+#define AQCTL_PRD_MASK		GENMASK(3, 2)
+#define AQCTL_PRD_FRCLOW	FIELD_PREP(AQCTL_PRD_MASK, 1)
+#define AQCTL_PRD_FRCHIGH	FIELD_PREP(AQCTL_PRD_MASK, 2)
+#define AQCTL_PRD_FRCTOGGLE	FIELD_PREP(AQCTL_PRD_MASK, 3)
+
+#define AQCTL_ZRO_MASK		GENMASK(1, 0)
+#define AQCTL_ZRO_FRCLOW	FIELD_PREP(AQCTL_ZRO_MASK, 1)
+#define AQCTL_ZRO_FRCHIGH	FIELD_PREP(AQCTL_ZRO_MASK, 2)
+#define AQCTL_ZRO_FRCTOGGLE	FIELD_PREP(AQCTL_ZRO_MASK, 3)
 
 #define AQCTL_CHANA_POLNORMAL	(AQCTL_CAU_FRCLOW | AQCTL_PRD_FRCHIGH | \
 				AQCTL_ZRO_FRCHIGH)
@@ -74,22 +79,23 @@
 #define AQCTL_CHANB_POLINVERSED	(AQCTL_CBU_FRCHIGH | AQCTL_PRD_FRCLOW | \
 				AQCTL_ZRO_FRCLOW)
 
-#define AQSFRC_RLDCSF_MASK	(BIT(7) | BIT(6))
-#define AQSFRC_RLDCSF_ZRO	0
-#define AQSFRC_RLDCSF_PRD	BIT(6)
-#define AQSFRC_RLDCSF_ZROPRD	BIT(7)
-#define AQSFRC_RLDCSF_IMDT	(BIT(7) | BIT(6))
-
-#define AQCSFRC_CSFB_MASK	(BIT(3) | BIT(2))
-#define AQCSFRC_CSFB_FRCDIS	0
-#define AQCSFRC_CSFB_FRCLOW	BIT(2)
-#define AQCSFRC_CSFB_FRCHIGH	BIT(3)
-#define AQCSFRC_CSFB_DISSWFRC	(BIT(3) | BIT(2))
-#define AQCSFRC_CSFA_MASK	(BIT(1) | BIT(0))
-#define AQCSFRC_CSFA_FRCDIS	0
-#define AQCSFRC_CSFA_FRCLOW	BIT(0)
-#define AQCSFRC_CSFA_FRCHIGH	BIT(1)
-#define AQCSFRC_CSFA_DISSWFRC	(BIT(1) | BIT(0))
+#define AQSFRC_RLDCSF_MASK	GENMASK(7, 6)
+#define AQSFRC_RLDCSF_ZRO	FIELD_PREP(AQSFRC_RLDCSF_MASK, 0)
+#define AQSFRC_RLDCSF_PRD	FIELD_PREP(AQSFRC_RLDCSF_MASK, 1)
+#define AQSFRC_RLDCSF_ZROPRD	FIELD_PREP(AQSFRC_RLDCSF_MASK, 2)
+#define AQSFRC_RLDCSF_IMDT	FIELD_PREP(AQSFRC_RLDCSF_MASK, 3)
+
+#define AQCSFRC_CSFB_MASK	GENMASK(3, 2)
+#define AQCSFRC_CSFB_FRCDIS	FIELD_PREP(AQCSFRC_CSFB_MASK, 0)
+#define AQCSFRC_CSFB_FRCLOW	FIELD_PREP(AQCSFRC_CSFB_MASK, 1)
+#define AQCSFRC_CSFB_FRCHIGH	FIELD_PREP(AQCSFRC_CSFB_MASK, 2)
+#define AQCSFRC_CSFB_DISSWFRC	FIELD_PREP(AQCSFRC_CSFB_MASK, 3)
+
+#define AQCSFRC_CSFA_MASK	GENMASK(1, 0)
+#define AQCSFRC_CSFA_FRCDIS	FIELD_PREP(AQCSFRC_CSFA_MASK, 0)
+#define AQCSFRC_CSFA_FRCLOW	FIELD_PREP(AQCSFRC_CSFA_MASK, 1)
+#define AQCSFRC_CSFA_FRCHIGH	FIELD_PREP(AQCSFRC_CSFA_MASK, 2)
+#define AQCSFRC_CSFA_DISSWFRC	FIELD_PREP(AQCSFRC_CSFA_MASK, 3)
 
 #define NUM_PWM_CHANNEL		2	/* EHRPWM channels */
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v5 2/6] pwm: tiehrpwm: use FIELD_PREP()/FIELD_GET() for prescalers
  2025-08-22  4:40 [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields Rafael V. Volkmer
@ 2025-08-22  4:43 ` Rafael V. Volkmer
  2025-08-22  4:45   ` [PATCH v5 3/6] pwm: tiehrpwm: refactor AQCTL macros Rafael V. Volkmer
  2025-08-25  8:27 ` [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields Uwe Kleine-König
  1 sibling, 1 reply; 8+ messages in thread
From: Rafael V. Volkmer @ 2025-08-22  4:43 UTC (permalink / raw)
  To: rafael.v.volkmer; +Cc: linux-kernel, linux-pwm, ukleinek

Replace manual CLKDIV/HSPCLKDIV handling with GENMASK() and
FIELD_PREP()/FIELD_GET(). Introduce TBCTL_PRESCALE_MASK to update both
fields in a single ehrpwm_modify() call, and drop the unused SHIFT
macros.

This improves readability and lowers chances of off-by-shift errors.

No functional change intended.

Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@gmail.com>
---
 drivers/pwm/pwm-tiehrpwm.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index 8509dd587..d140814a1 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -25,7 +25,9 @@
 #define TBCTL_PRDLD_SHDW	FIELD_PREP(TBCTL_PRDLD_MASK, 0)
 #define TBCTL_PRDLD_IMDT	FIELD_PREP(TBCTL_PRDLD_MASK, 1)
 
-#define TBCTL_CLKDIV_MASK	GENMASK(12, 7)
+#define TBCTL_CLKDIV_MASK	GENMASK(12, 10)
+#define TBCTL_HSPCLKDIV_MASK	GENMASK(9, 7)
+#define TBCTL_PRESCALE_MASK	(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK)
 
 #define TBCTL_CTRMODE_MASK	GENMASK(1, 0)
 #define TBCTL_CTRMODE_UP	FIELD_PREP(TBCTL_CTRMODE_MASK, 0)
@@ -33,9 +35,6 @@
 #define TBCTL_CTRMODE_UPDOWN	FIELD_PREP(TBCTL_CTRMODE_MASK, 2)
 #define TBCTL_CTRMODE_FREEZE	FIELD_PREP(TBCTL_CTRMODE_MASK, 3)
 
-#define TBCTL_HSPCLKDIV_SHIFT	7
-#define TBCTL_CLKDIV_SHIFT	10
-
 #define CLKDIV_MAX		7
 #define HSPCLKDIV_MAX		7
 #define PERIOD_MAX		0xFFFF
@@ -173,8 +172,8 @@ static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div,
 			*prescale_div = (1 << clkdiv) *
 					(hspclkdiv ? (hspclkdiv * 2) : 1);
 			if (*prescale_div > rqst_prescaler) {
-				*tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) |
-					(hspclkdiv << TBCTL_HSPCLKDIV_SHIFT);
+				*tb_clk_div = FIELD_PREP(TBCTL_CLKDIV_MASK, clkdiv) |
+					FIELD_PREP(TBCTL_HSPCLKDIV_MASK, hspclkdiv);
 				return 0;
 			}
 		}
@@ -280,7 +279,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	pm_runtime_get_sync(pwmchip_parent(chip));
 
 	/* Update clock prescaler values */
-	ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval);
+	ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRESCALE_MASK, tb_divval);
 
 	/* Update period & duty cycle with presacler division */
 	period_cycles = period_cycles / ps_divval;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v5 3/6] pwm: tiehrpwm: refactor AQCTL macros
  2025-08-22  4:43 ` [PATCH v5 2/6] pwm: tiehrpwm: use FIELD_PREP()/FIELD_GET() for prescalers Rafael V. Volkmer
@ 2025-08-22  4:45   ` Rafael V. Volkmer
  2025-08-22  4:48     ` [PATCH v5 4/6] pwm: tiehrpwm: implement .get_state Rafael V. Volkmer
  0 siblings, 1 reply; 8+ messages in thread
From: Rafael V. Volkmer @ 2025-08-22  4:45 UTC (permalink / raw)
  To: rafael.v.volkmer; +Cc: linux-kernel, linux-pwm, ukleinek

Refactor AQCTL polarity/action macros to make channel and count
direction explicit. Keep existing up-count names and add consistent
down-count variants for CAD/CBD events. Centralize action values
(LOW/HIGH/TOGGLE) and reuse them for both channels and directions.

This reduces duplication and makes action selection self-documenting.

No functional change intended.

Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@gmail.com>
---
 drivers/pwm/pwm-tiehrpwm.c | 63 ++++++++++++++++++++++++++------------
 1 file changed, 43 insertions(+), 20 deletions(-)

diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index d140814a1..b2a55e59b 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -49,33 +49,56 @@
 #define AQSFRC			0x1A
 #define AQCSFRC			0x1C
 
+#define AQCTL_FRCLOW		1
+#define AQCTL_FRCHIGH		2
+#define AQCTL_FRCTOGGLE		3
+
+#define AQCTL_CBD_MASK		GENMASK(11, 10)
+#define AQCTL_CBD_FRCLOW	FIELD_PREP(AQCTL_CBD_MASK, AQCTL_FRCLOW)
+#define AQCTL_CBD_FRCHIGH	FIELD_PREP(AQCTL_CBD_MASK, AQCTL_FRCHIGH)
+#define AQCTL_CBD_FRCTOGGLE	FIELD_PREP(AQCTL_CBD_MASK, AQCTL_FRCTOGGLE)
+
 #define AQCTL_CBU_MASK		GENMASK(9, 8)
-#define AQCTL_CBU_FRCLOW	FIELD_PREP(AQCTL_CBU_MASK, 1)
-#define AQCTL_CBU_FRCHIGH	FIELD_PREP(AQCTL_CBU_MASK, 2)
-#define AQCTL_CBU_FRCTOGGLE	FIELD_PREP(AQCTL_CBU_MASK, 3)
+#define AQCTL_CBU_FRCLOW	FIELD_PREP(AQCTL_CBU_MASK, AQCTL_FRCLOW)
+#define AQCTL_CBU_FRCHIGH	FIELD_PREP(AQCTL_CBU_MASK, AQCTL_FRCHIGH)
+#define AQCTL_CBU_FRCTOGGLE	FIELD_PREP(AQCTL_CBU_MASK, AQCTL_FRCTOGGLE)
+
+#define AQCTL_CAD_MASK		GENMASK(7, 6)
+#define AQCTL_CAD_FRCLOW	FIELD_PREP(AQCTL_CAD_MASK, AQCTL_FRCLOW)
+#define AQCTL_CAD_FRCHIGH	FIELD_PREP(AQCTL_CAD_MASK, AQCTL_FRCHIGH)
+#define AQCTL_CAD_FRCTOGGLE	FIELD_PREP(AQCTL_CAD_MASK, AQCTL_FRCTOGGLE)
 
 #define AQCTL_CAU_MASK		GENMASK(5, 4)
-#define AQCTL_CAU_FRCLOW	FIELD_PREP(AQCTL_CAU_MASK, 1)
-#define AQCTL_CAU_FRCHIGH	FIELD_PREP(AQCTL_CAU_MASK, 2)
-#define AQCTL_CAU_FRCTOGGLE	FIELD_PREP(AQCTL_CAU_MASK, 3)
+#define AQCTL_CAU_FRCLOW	FIELD_PREP(AQCTL_CAU_MASK, AQCTL_FRCLOW)
+#define AQCTL_CAU_FRCHIGH	FIELD_PREP(AQCTL_CAU_MASK, AQCTL_FRCHIGH)
+#define AQCTL_CAU_FRCTOGGLE	FIELD_PREP(AQCTL_CAU_MASK, AQCTL_FRCTOGGLE)
 
 #define AQCTL_PRD_MASK		GENMASK(3, 2)
-#define AQCTL_PRD_FRCLOW	FIELD_PREP(AQCTL_PRD_MASK, 1)
-#define AQCTL_PRD_FRCHIGH	FIELD_PREP(AQCTL_PRD_MASK, 2)
-#define AQCTL_PRD_FRCTOGGLE	FIELD_PREP(AQCTL_PRD_MASK, 3)
+#define AQCTL_PRD_FRCLOW	FIELD_PREP(AQCTL_PRD_MASK, AQCTL_FRCLOW)
+#define AQCTL_PRD_FRCHIGH	FIELD_PREP(AQCTL_PRD_MASK, AQCTL_FRCHIGH)
+#define AQCTL_PRD_FRCTOGGLE	FIELD_PREP(AQCTL_PRD_MASK, AQCTL_FRCTOGGLE)
 
 #define AQCTL_ZRO_MASK		GENMASK(1, 0)
-#define AQCTL_ZRO_FRCLOW	FIELD_PREP(AQCTL_ZRO_MASK, 1)
-#define AQCTL_ZRO_FRCHIGH	FIELD_PREP(AQCTL_ZRO_MASK, 2)
-#define AQCTL_ZRO_FRCTOGGLE	FIELD_PREP(AQCTL_ZRO_MASK, 3)
+#define AQCTL_ZRO_FRCLOW	FIELD_PREP(AQCTL_ZRO_MASK, AQCTL_FRCLOW)
+#define AQCTL_ZRO_FRCHIGH	FIELD_PREP(AQCTL_ZRO_MASK, AQCTL_FRCHIGH)
+#define AQCTL_ZRO_FRCTOGGLE	FIELD_PREP(AQCTL_ZRO_MASK, AQCTL_FRCTOGGLE)
+
+#define AQCTL_CHA_UP_POLNORMAL	(AQCTL_CAU_FRCLOW | AQCTL_PRD_FRCHIGH | \
+				AQCTL_ZRO_FRCHIGH)
+#define AQCTL_CHA_UP_POLINVERSE	(AQCTL_CAU_FRCHIGH | AQCTL_PRD_FRCLOW | \
+				AQCTL_ZRO_FRCLOW)
+#define AQCTL_CHB_UP_POLNORMAL	(AQCTL_CBU_FRCLOW | AQCTL_PRD_FRCHIGH | \
+				AQCTL_ZRO_FRCHIGH)
+#define AQCTL_CHB_UP_POLINVERSE	(AQCTL_CBU_FRCHIGH | AQCTL_PRD_FRCLOW | \
+				AQCTL_ZRO_FRCLOW)
 
-#define AQCTL_CHANA_POLNORMAL	(AQCTL_CAU_FRCLOW | AQCTL_PRD_FRCHIGH | \
+#define AQCTL_CHA_DN_POLNORMAL	(AQCTL_CAD_FRCLOW | AQCTL_PRD_FRCHIGH | \
 				AQCTL_ZRO_FRCHIGH)
-#define AQCTL_CHANA_POLINVERSED	(AQCTL_CAU_FRCHIGH | AQCTL_PRD_FRCLOW | \
+#define AQCTL_CHA_DN_POLINVERSE	(AQCTL_CAD_FRCHIGH | AQCTL_PRD_FRCLOW | \
 				AQCTL_ZRO_FRCLOW)
-#define AQCTL_CHANB_POLNORMAL	(AQCTL_CBU_FRCLOW | AQCTL_PRD_FRCHIGH | \
+#define AQCTL_CHB_DN_POLNORMAL	(AQCTL_CBD_FRCLOW | AQCTL_PRD_FRCHIGH | \
 				AQCTL_ZRO_FRCHIGH)
-#define AQCTL_CHANB_POLINVERSED	(AQCTL_CBU_FRCHIGH | AQCTL_PRD_FRCLOW | \
+#define AQCTL_CHB_DN_POLINVERSE	(AQCTL_CBD_FRCHIGH | AQCTL_PRD_FRCLOW | \
 				AQCTL_ZRO_FRCLOW)
 
 #define AQSFRC_RLDCSF_MASK	GENMASK(7, 6)
@@ -198,17 +221,17 @@ static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan)
 		aqctl_mask = AQCTL_CBU_MASK;
 
 		if (pc->polarity[chan] == PWM_POLARITY_INVERSED)
-			aqctl_val = AQCTL_CHANB_POLINVERSED;
+			aqctl_val = AQCTL_CHB_UP_POLINVERSE;
 		else
-			aqctl_val = AQCTL_CHANB_POLNORMAL;
+			aqctl_val = AQCTL_CHB_UP_POLNORMAL;
 	} else {
 		aqctl_reg = AQCTLA;
 		aqctl_mask = AQCTL_CAU_MASK;
 
 		if (pc->polarity[chan] == PWM_POLARITY_INVERSED)
-			aqctl_val = AQCTL_CHANA_POLINVERSED;
+			aqctl_val = AQCTL_CHA_UP_POLINVERSE;
 		else
-			aqctl_val = AQCTL_CHANA_POLNORMAL;
+			aqctl_val = AQCTL_CHA_UP_POLNORMAL;
 	}
 
 	aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v5 4/6] pwm: tiehrpwm: implement .get_state
  2025-08-22  4:45   ` [PATCH v5 3/6] pwm: tiehrpwm: refactor AQCTL macros Rafael V. Volkmer
@ 2025-08-22  4:48     ` Rafael V. Volkmer
  2025-08-22  4:50       ` [PATCH v5 5/6] pwm: tiehrpwm: account for active channels at probe Rafael V. Volkmer
  0 siblings, 1 reply; 8+ messages in thread
From: Rafael V. Volkmer @ 2025-08-22  4:48 UTC (permalink / raw)
  To: rafael.v.volkmer; +Cc: linux-kernel, linux-pwm, ukleinek

Implement ehrpwm_get_state() to report enable, period, duty cycle and
polarity. Read AQCSFRC and AQCTL(A/B) to determine whether a channel is
software-forced (treated as disabled) and expose current timing and
polarity through the pwm_state.

This lets consumers query the current hardware state reliably.

Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@gmail.com>
---
 drivers/pwm/pwm-tiehrpwm.c | 103 +++++++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index b2a55e59b..1dae3b8b5 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -461,9 +461,112 @@ static int ehrpwm_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	return err;
 }
 
+static int ehrpwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+			    struct pwm_state *state)
+{
+	int ret = 0;
+
+	struct ehrpwm_pwm_chip *pc = NULL;
+
+	/* Registers */
+	u16 aqcsfrc_reg, aqctl_reg, tbprd_reg, tbctl_reg;
+
+	/* Bits */
+	u8 csf_bits, clkdiv_bits, hspclkdiv_bits;
+
+	/* Values */
+	u64 period_ticks, duty_ticks, ps_div;
+
+	/* Actions */
+	u8 up_action, down_action;
+
+	pc = to_ehrpwm_pwm_chip(chip);
+
+	/*
+	 * The 'hwpwm' field identifies which hardware output channel (e.g.,
+	 * 0 for channel A and 1 for channel B) of the eHRPWM module is in use.
+	 */
+	if (pwm->hwpwm == 0) {
+		aqcsfrc_reg = readw(pc->mmio_base + AQCSFRC);
+		csf_bits = FIELD_GET(AQCSFRC_CSFA_MASK, aqcsfrc_reg);
+		aqctl_reg = readw(pc->mmio_base + AQCTLA);
+	} else {
+		aqcsfrc_reg = readw(pc->mmio_base + AQCSFRC);
+		csf_bits = FIELD_GET(AQCSFRC_CSFB_MASK, aqcsfrc_reg);
+		aqctl_reg = readw(pc->mmio_base + AQCTLB);
+	}
+
+	if (csf_bits || !aqctl_reg) {
+		state->enabled = false;
+		return 0;
+	}
+
+	state->enabled = true;
+
+	tbprd_reg = readw(pc->mmio_base + TBPRD);
+	period_ticks = (u64)tbprd_reg + 1u;
+
+	tbctl_reg = readw(pc->mmio_base + TBCTL);
+	hspclkdiv_bits = FIELD_GET(TBCTL_HSPCLKDIV_MASK, tbctl_reg);
+	clkdiv_bits = FIELD_GET(TBCTL_CLKDIV_MASK, tbctl_reg);
+
+	ps_div = (1 << clkdiv_bits) * (hspclkdiv_bits ? (hspclkdiv_bits * 2) : 1);
+
+	/*
+	 * period (in ns) = (period_ticks * preescaler * 1e9) / clk_rate
+	 * Using DIV_ROUND_UP_ULL to avoid floating-point operations.
+	 */
+	state->period = DIV_ROUND_UP_ULL(period_ticks * ps_div * NSEC_PER_SEC, pc->clk_rate);
+
+	if (pwm->hwpwm == 0)
+		duty_ticks = readw(pc->mmio_base + CMPA);
+	else
+		duty_ticks = readw(pc->mmio_base + CMPB);
+
+	/*
+	 * duty_cycle (in ns) = (duty_ticks * preescaler * 1e9) / clk_rate
+	 * Using DIV_ROUND_UP_ULL to avoid floating-point operations.
+	 */
+	state->duty_cycle = DIV_ROUND_UP_ULL(duty_ticks * ps_div * NSEC_PER_SEC, pc->clk_rate);
+
+	/*
+	 * The 'hwpwm' field identifies which hardware output channel (e.g.,
+	 * 0 for channel A and 1 for channel B) of the eHRPWM module is in use.
+	 */
+	if (pwm->hwpwm == 0) {
+		aqctl_reg = readw(pc->mmio_base + AQCTLA);
+		up_action = FIELD_GET(AQCTL_CAU_MASK, aqctl_reg);
+		down_action = FIELD_GET(AQCTL_CAD_MASK, aqctl_reg);
+	} else {
+		aqctl_reg = readw(pc->mmio_base + AQCTLB);
+		up_action = FIELD_GET(AQCTL_CBU_MASK, aqctl_reg);
+		down_action = FIELD_GET(AQCTL_CBD_MASK, aqctl_reg);
+	}
+
+	/*
+	 * Evaluate the actions to determine the PWM polarity:
+	 *  - If an up-count event sets the output (AQCTL_FRCHIGH) and a down-count
+	 *    event clears it (AQ_CLEAR), then polarity is NORMAL.
+	 *  - If an up-count event clears the output (AQ_CLEAR) and a down-count
+	 *    event sets it (AQCTL_FRCLOW), then polarity is INVERSED.
+	 */
+	if (up_action == AQCTL_FRCHIGH && down_action == AQCTL_FRCLOW) {
+		state->polarity = PWM_POLARITY_NORMAL;
+	} else if (up_action == AQCTL_FRCLOW && down_action == AQCTL_FRCHIGH) {
+		state->polarity = PWM_POLARITY_INVERSED;
+	} else {
+		state->polarity = PWM_POLARITY_NORMAL;
+		dev_dbg(&chip->dev, "ehrpwm: unknown polarity bits (0x%x/0x%x), defaulting to NORMAL\n",
+			up_action, down_action);
+	}
+
+	return ret;
+}
+
 static const struct pwm_ops ehrpwm_pwm_ops = {
 	.free = ehrpwm_pwm_free,
 	.apply = ehrpwm_pwm_apply,
+	.get_state = ehrpwm_get_state,
 };
 
 static const struct of_device_id ehrpwm_of_match[] = {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v5 5/6] pwm: tiehrpwm: account for active channels at probe
  2025-08-22  4:48     ` [PATCH v5 4/6] pwm: tiehrpwm: implement .get_state Rafael V. Volkmer
@ 2025-08-22  4:50       ` Rafael V. Volkmer
  2025-08-22  4:52         ` [PATCH v5 6/6] pwm: tiehrpwm: tidy prescale calculation style Rafael V. Volkmer
  0 siblings, 1 reply; 8+ messages in thread
From: Rafael V. Volkmer @ 2025-08-22  4:50 UTC (permalink / raw)
  To: rafael.v.volkmer; +Cc: linux-kernel, linux-pwm, ukleinek

Handle already-running eHRPWM channels at probe. Detect active A/B from
AQCTL(A/B) when not software-forced in AQCSFRC, take one
pm_runtime_get_sync() per active channel, and enable tbclk only if at
least one is active. Keep PM refcounts and clocks consistent with the
hardware state.

Before: active hardware could miss runtime PM gets and clk enable.
After: PM/clk state matches active channels observed at probe.

Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@gmail.com>
---
 drivers/pwm/pwm-tiehrpwm.c | 55 +++++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 6 deletions(-)

diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index 1dae3b8b5..a801912be 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -121,6 +121,9 @@
 
 #define NUM_PWM_CHANNEL		2	/* EHRPWM channels */
 
+#define PWM_CHA			0
+#define PWM_CHB			1
+
 struct ehrpwm_context {
 	u16 tbctl;
 	u16 tbprd;
@@ -582,13 +585,31 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
 	struct ehrpwm_pwm_chip *pc;
 	struct pwm_chip *chip;
 	struct clk *clk;
-	int ret;
+	int ret, ch_idx, ch_disable;
+
+	u16 aqcsfrc_reg, aqctla_reg, aqctlb_reg;
+
+	bool enabled_ch[NUM_PWM_CHANNEL] = { false, false };
 
 	chip = devm_pwmchip_alloc(&pdev->dev, NUM_PWM_CHANNEL, sizeof(*pc));
 	if (IS_ERR(chip))
 		return PTR_ERR(chip);
 	pc = to_ehrpwm_pwm_chip(chip);
 
+	pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(pc->mmio_base))
+		return PTR_ERR(pc->mmio_base);
+
+	aqcsfrc_reg = readw(pc->mmio_base + AQCSFRC);
+	aqctla_reg = readw(pc->mmio_base + AQCTLA);
+	aqctlb_reg = readw(pc->mmio_base + AQCTLB);
+
+	if (aqctla_reg != 0 && !FIELD_GET(AQCSFRC_CSFA_MASK, aqcsfrc_reg))
+		enabled_ch[PWM_CHA] = true;
+
+	if (aqctlb_reg != 0 && !FIELD_GET(AQCSFRC_CSFB_MASK, aqcsfrc_reg))
+		enabled_ch[PWM_CHB] = true;
+
 	clk = devm_clk_get(&pdev->dev, "fck");
 	if (IS_ERR(clk)) {
 		if (of_device_is_compatible(np, "ti,am33xx-ecap")) {
@@ -608,10 +629,6 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
 
 	chip->ops = &ehrpwm_pwm_ops;
 
-	pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
-	if (IS_ERR(pc->mmio_base))
-		return PTR_ERR(pc->mmio_base);
-
 	/* Acquire tbclk for Time Base EHRPWM submodule */
 	pc->tbclk = devm_clk_get(&pdev->dev, "tbclk");
 	if (IS_ERR(pc->tbclk))
@@ -623,17 +640,43 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	if (enabled_ch[PWM_CHA] || enabled_ch[PWM_CHB]) {
+		ret = clk_enable(pc->tbclk);
+		if (ret) {
+			dev_err_probe(&pdev->dev, ret, "clk_enable(tbclk) failed\n");
+			goto err_clk_unprepare;
+		}
+	}
+
 	ret = pwmchip_add(chip);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
-		goto err_clk_unprepare;
+		goto err_disable_tbclk;
 	}
 
 	platform_set_drvdata(pdev, chip);
 	pm_runtime_enable(&pdev->dev);
+	for (ch_idx = 0; ch_idx < NUM_PWM_CHANNEL; ch_idx++) {
+		if (enabled_ch[ch_idx]) {
+			ret = pm_runtime_get_sync(&pdev->dev);
+			if (ret < 0) {
+				for (ch_disable = 0; ch_disable <= ch_idx; ch_disable++) {
+					if (enabled_ch[ch_disable])
+						pm_runtime_put_noidle(&pdev->dev);
+				}
+
+				pwmchip_remove(chip);
+				pm_runtime_disable(&pdev->dev);
+				return ret;
+			}
+		}
+	}
 
 	return 0;
 
+err_disable_tbclk:
+	if (enabled_ch[PWM_CHA] || enabled_ch[PWM_CHB])
+		clk_disable(pc->tbclk);
 err_clk_unprepare:
 	clk_unprepare(pc->tbclk);
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v5 6/6] pwm: tiehrpwm: tidy prescale calculation style
  2025-08-22  4:50       ` [PATCH v5 5/6] pwm: tiehrpwm: account for active channels at probe Rafael V. Volkmer
@ 2025-08-22  4:52         ` Rafael V. Volkmer
  0 siblings, 0 replies; 8+ messages in thread
From: Rafael V. Volkmer @ 2025-08-22  4:52 UTC (permalink / raw)
  To: rafael.v.volkmer; +Cc: linux-kernel, linux-pwm, ukleinek

Tidy ehrpwm_pwm_config(): drop redundant parentheses, keep the condition
on a single line, and add spacing around the division operator to follow
kernel style.

This change addresses a style warning reported by checkpatch.pl.
No functional change intended.

Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@gmail.com>
---
 drivers/pwm/pwm-tiehrpwm.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index a801912be..35c7ee801 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -277,8 +277,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	 * same period register for multiple channels.
 	 */
 	for (i = 0; i < NUM_PWM_CHANNEL; i++) {
-		if (pc->period_cycles[i] &&
-				(pc->period_cycles[i] != period_cycles)) {
+		if (pc->period_cycles[i] && pc->period_cycles[i] != period_cycles) {
 			/*
 			 * Allow channel to reconfigure period if no other
 			 * channels being configured.
@@ -296,7 +295,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	pc->period_cycles[pwm->hwpwm] = period_cycles;
 
 	/* Configure clock prescaler to support Low frequency PWM wave */
-	if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval,
+	if (set_prescale_div(period_cycles / PERIOD_MAX, &ps_divval,
 			     &tb_divval)) {
 		dev_err(pwmchip_parent(chip), "Unsupported values\n");
 		return -EINVAL;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields
  2025-08-22  4:40 [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields Rafael V. Volkmer
  2025-08-22  4:43 ` [PATCH v5 2/6] pwm: tiehrpwm: use FIELD_PREP()/FIELD_GET() for prescalers Rafael V. Volkmer
@ 2025-08-25  8:27 ` Uwe Kleine-König
  2025-08-26  4:05   ` Rafael V. Volkmer
  1 sibling, 1 reply; 8+ messages in thread
From: Uwe Kleine-König @ 2025-08-25  8:27 UTC (permalink / raw)
  To: Rafael V. Volkmer; +Cc: linux-kernel, linux-pwm

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

Hello Rafael,

On Fri, Aug 22, 2025 at 01:40:24AM -0300, Rafael V. Volkmer wrote:
> Make register field definitions use GENMASK() and FIELD_PREP() across
> AQCTL, AQCSFRC, and TBCTL. This clarifies bit layout, reduces hand-rolled
> shift logic, and aligns the driver with common kernel patterns.
> 
> No functional change intended.

just a meta-comment without a deeper look into this series:

There is another series touching this driver at
https://lore.kernel.org/linux-pwm/cover.1754927682.git.u.kleine-koenig@baylibre.com/
that would be great to get some testing and that I tend to apply first
given that is contains also fixes. I guess you dind't base your series
on top of that?

Best regards
Uwe

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

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields
  2025-08-25  8:27 ` [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields Uwe Kleine-König
@ 2025-08-26  4:05   ` Rafael V. Volkmer
  0 siblings, 0 replies; 8+ messages in thread
From: Rafael V. Volkmer @ 2025-08-26  4:05 UTC (permalink / raw)
  To: ukleinek; +Cc: linux-kernel, linux-pwm, rafael.v.volkmer

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 615 bytes --]

Hi Uwe,

On Mon, Aug 25, 2025 at 10:27:26AM +0200, Uwe Kleine-König wrote:
> There is another series touching this driver at
> https://lore.kernel.org/linux-pwm/cover.1754927682.git.u.kleine-koenig@baylibre.com/
> that would be great to get some testing and that I tend to apply first
> given that it contains also fixes. I guess you didn't base your series
> on top of that?

You're right, I didn’t base my series on top of that one.
I’ll test your series and can rebase mine afterwards once it lands,
so we avoid conflicts.

Thanks for pointing this out.

Best regards,
Rafael

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-08-26  4:05 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-22  4:40 [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields Rafael V. Volkmer
2025-08-22  4:43 ` [PATCH v5 2/6] pwm: tiehrpwm: use FIELD_PREP()/FIELD_GET() for prescalers Rafael V. Volkmer
2025-08-22  4:45   ` [PATCH v5 3/6] pwm: tiehrpwm: refactor AQCTL macros Rafael V. Volkmer
2025-08-22  4:48     ` [PATCH v5 4/6] pwm: tiehrpwm: implement .get_state Rafael V. Volkmer
2025-08-22  4:50       ` [PATCH v5 5/6] pwm: tiehrpwm: account for active channels at probe Rafael V. Volkmer
2025-08-22  4:52         ` [PATCH v5 6/6] pwm: tiehrpwm: tidy prescale calculation style Rafael V. Volkmer
2025-08-25  8:27 ` [PATCH v5 1/6] pwm: tiehrpwm: use GENMASK()/FIELD_PREP() for register fields Uwe Kleine-König
2025-08-26  4:05   ` Rafael V. Volkmer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).