diff mbox series

pwm: imx27: workaround of the pwm output bug

Message ID 20231229063013.1786-1-pratikmanvar09@gmail.com (mailing list archive)
State New, archived
Headers show
Series pwm: imx27: workaround of the pwm output bug | expand

Commit Message

pratikmanvar09@gmail.com Dec. 29, 2023, 6:30 a.m. UTC
From: Clark Wang <xiaoning.wang@nxp.com>

This fixes the pwm output bug when decrease the duty cycle.
This is a limited workaround for the PWM IP issue TKT0577206.

Root cause:
When the SAR FIFO is empty, the new write value will be directly applied
to SAR even the current period is not over.
If the new SAR value is less than the old one, and the counter is
greater than the new SAR value, the current period will not filp the
level. This will result in a pulse with a duty cycle of 100%.

Workaround:
Add an old value SAR write before updating the new duty cycle to SAR.
This will keep the new value is always in a not empty fifo, and can be
wait to update after a period finished.

Limitation:
This workaround can only solve this issue when the PWM period is longer
than 2us(or <500KHz).

Reviewed-by: Jun Li <jun.li@nxp.com>
Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Link: https://github.com/nxp-imx/linux-imx/commit/16181cc4eee61d87cbaba0e5a479990507816317
Tested-by: Pratik Manvar <pratik.manvar@ifm.com>
---
 drivers/pwm/pwm-imx27.c | 67 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 62 insertions(+), 5 deletions(-)

Comments

kernel test robot Dec. 30, 2023, 2:01 a.m. UTC | #1
Hi,

kernel test robot noticed the following build warnings:

[auto build test WARNING on thierry-reding-pwm/for-next]
[also build test WARNING on linus/master v6.7-rc7 next-20231222]
[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/pratikmanvar09-gmail-com/pwm-imx27-workaround-of-the-pwm-output-bug/20231229-143435
base:   https://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git for-next
patch link:    https://lore.kernel.org/r/20231229063013.1786-1-pratikmanvar09%40gmail.com
patch subject: [PATCH] pwm: imx27: workaround of the pwm output bug
config: arm-randconfig-r133-20231230 (https://download.01.org/0day-ci/archive/20231230/202312300907.RGtYsKxb-lkp@intel.com/config)
compiler: clang version 18.0.0git (https://github.com/llvm/llvm-project 8a4266a626914765c0c69839e8a51be383013c1a)
reproduce: (https://download.01.org/0day-ci/archive/20231230/202312300907.RGtYsKxb-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/202312300907.RGtYsKxb-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/pwm/pwm-imx27.c:303:26: sparse: sparse: incorrect type in assignment (different base types) @@     expected unsigned int [usertype] sar_last @@     got restricted __le32 [usertype] @@
   drivers/pwm/pwm-imx27.c:303:26: sparse:     expected unsigned int [usertype] sar_last
   drivers/pwm/pwm-imx27.c:303:26: sparse:     got restricted __le32 [usertype]
>> drivers/pwm/pwm-imx27.c:304:29: sparse: sparse: incorrect type in assignment (different base types) @@     expected unsigned int [usertype] sar_current @@     got restricted __le32 [usertype] @@
   drivers/pwm/pwm-imx27.c:304:29: sparse:     expected unsigned int [usertype] sar_current
   drivers/pwm/pwm-imx27.c:304:29: sparse:     got restricted __le32 [usertype]

vim +303 drivers/pwm/pwm-imx27.c

   219	
   220	static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
   221				   const struct pwm_state *state)
   222	{
   223		unsigned long period_cycles, duty_cycles, prescale, counter_check, flags;
   224		struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
   225		void __iomem *reg_sar = imx->mmio_base + MX3_PWMSAR;
   226		__force u32 sar_last, sar_current;
   227		struct pwm_state cstate;
   228		unsigned long long c;
   229		unsigned long long clkrate;
   230		int ret;
   231		u32 cr, timeout = 1000;
   232	
   233		pwm_get_state(pwm, &cstate);
   234	
   235		clkrate = clk_get_rate(imx->clk_per);
   236		c = clkrate * state->period;
   237	
   238		do_div(c, NSEC_PER_SEC);
   239		period_cycles = c;
   240	
   241		prescale = period_cycles / 0x10000 + 1;
   242	
   243		period_cycles /= prescale;
   244		c = clkrate * state->duty_cycle;
   245		do_div(c, NSEC_PER_SEC);
   246		duty_cycles = c;
   247		duty_cycles /= prescale;
   248	
   249		/*
   250		 * according to imx pwm RM, the real period value should be PERIOD
   251		 * value in PWMPR plus 2.
   252		 */
   253		if (period_cycles > 2)
   254			period_cycles -= 2;
   255		else
   256			period_cycles = 0;
   257	
   258		/*
   259		 * Wait for a free FIFO slot if the PWM is already enabled, and flush
   260		 * the FIFO if the PWM was disabled and is about to be enabled.
   261		 */
   262		if (cstate.enabled) {
   263			pwm_imx27_wait_fifo_slot(chip, pwm);
   264		} else {
   265			ret = pwm_imx27_clk_prepare_enable(imx);
   266			if (ret)
   267				return ret;
   268	
   269			pwm_imx27_sw_reset(chip);
   270		}
   271	
   272		/*
   273		 * This is a limited workaround. When the SAR FIFO is empty, the new
   274		 * write value will be directly applied to SAR even the current period
   275		 * is not over.
   276		 * If the new SAR value is less than the old one, and the counter is
   277		 * greater than the new SAR value, the current period will not filp
   278		 * the level. This will result in a pulse with a duty cycle of 100%.
   279		 * So, writing the current value of the SAR to SAR here before updating
   280		 * the new SAR value can avoid this issue.
   281		 *
   282		 * Add a spin lock and turn off the interrupt to ensure that the
   283		 * real-time performance can be guaranteed as much as possible when
   284		 * operating the following operations.
   285		 *
   286		 * 1. Add a threshold of 1.5us. If the time T between the read current
   287		 * count value CNR and the end of the cycle is less than 1.5us, wait
   288		 * for T to be longer than 1.5us before updating the SAR register.
   289		 * This is to avoid the situation that when the first SAR is written,
   290		 * the current cycle just ends and the SAR FIFO that just be written
   291		 * is emptied again.
   292		 *
   293		 * 2. Use __raw_writel() to minimize the interval between two writes to
   294		 * the SAR register to increase the fastest pwm frequency supported.
   295		 *
   296		 * When the PWM period is longer than 2us(or <500KHz), this workaround
   297		 * can solve this problem.
   298		 */
   299		if (duty_cycles < imx->duty_cycle) {
   300			c = clkrate * 1500;
   301			do_div(c, NSEC_PER_SEC);
   302			counter_check = c;
 > 303			sar_last = cpu_to_le32(imx->duty_cycle);
 > 304			sar_current = cpu_to_le32(duty_cycles);
   305	
   306			spin_lock_irqsave(&imx->lock, flags);
   307			if (state->period >= 2000) {
   308				while ((period_cycles -
   309					readl_relaxed(imx->mmio_base + MX3_PWMCNR))
   310					< counter_check) {
   311					if (!--timeout)
   312						break;
   313				};
   314			}
   315			if (!(MX3_PWMSR_FIFOAV &
   316			      readl_relaxed(imx->mmio_base + MX3_PWMSR)))
   317				__raw_writel(sar_last, reg_sar);
   318			__raw_writel(sar_current, reg_sar);
   319			spin_unlock_irqrestore(&imx->lock, flags);
   320		} else
   321			writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
   322	
   323		writel(period_cycles, imx->mmio_base + MX3_PWMPR);
   324	
   325		/*
   326		 * Store the duty cycle for future reference in cases where the
   327		 * MX3_PWMSAR register can't be read (i.e. when the PWM is disabled).
   328		 */
   329		imx->duty_cycle = duty_cycles;
   330	
   331		cr = MX3_PWMCR_PRESCALER_SET(prescale) |
   332		     MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEN | MX3_PWMCR_WAITEN |
   333		     FIELD_PREP(MX3_PWMCR_CLKSRC, MX3_PWMCR_CLKSRC_IPG_HIGH) |
   334		     MX3_PWMCR_DBGEN;
   335	
   336		if (state->polarity == PWM_POLARITY_INVERSED)
   337			cr |= FIELD_PREP(MX3_PWMCR_POUTC,
   338					MX3_PWMCR_POUTC_INVERTED);
   339	
   340		if (state->enabled)
   341			cr |= MX3_PWMCR_EN;
   342	
   343		writel(cr, imx->mmio_base + MX3_PWMCR);
   344	
   345		if (!state->enabled)
   346			pwm_imx27_clk_disable_unprepare(imx);
   347	
   348		return 0;
   349	}
   350
Francesco Dolcini Jan. 3, 2024, 10:34 a.m. UTC | #2
On Fri, Dec 29, 2023 at 12:00:07PM +0530, pratikmanvar09@gmail.com wrote:
> From: Clark Wang <xiaoning.wang@nxp.com>
> 
> This fixes the pwm output bug when decrease the duty cycle.
> This is a limited workaround for the PWM IP issue TKT0577206.
> 
> Root cause:
> When the SAR FIFO is empty, the new write value will be directly applied
> to SAR even the current period is not over.
> If the new SAR value is less than the old one, and the counter is
> greater than the new SAR value, the current period will not filp the
> level. This will result in a pulse with a duty cycle of 100%.
> 
> Workaround:
> Add an old value SAR write before updating the new duty cycle to SAR.
> This will keep the new value is always in a not empty fifo, and can be
> wait to update after a period finished.
> 
> Limitation:
> This workaround can only solve this issue when the PWM period is longer
> than 2us(or <500KHz).
> 
> Reviewed-by: Jun Li <jun.li@nxp.com>
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> Link: https://github.com/nxp-imx/linux-imx/commit/16181cc4eee61d87cbaba0e5a479990507816317
> Tested-by: Pratik Manvar <pratik.manvar@ifm.com>

You need to add your signed-off-by at the end when sending a patch, no
matter if you are the author or not.

Francesco
Uwe Kleine-König Jan. 3, 2024, 12:18 p.m. UTC | #3
On Wed, Jan 03, 2024 at 11:34:21AM +0100, Francesco Dolcini wrote:
> On Fri, Dec 29, 2023 at 12:00:07PM +0530, pratikmanvar09@gmail.com wrote:
> > From: Clark Wang <xiaoning.wang@nxp.com>
> > 
> > This fixes the pwm output bug when decrease the duty cycle.
> > This is a limited workaround for the PWM IP issue TKT0577206.
> > 
> > Root cause:
> > When the SAR FIFO is empty, the new write value will be directly applied
> > to SAR even the current period is not over.
> > If the new SAR value is less than the old one, and the counter is
> > greater than the new SAR value, the current period will not filp the
> > level. This will result in a pulse with a duty cycle of 100%.
> > 
> > Workaround:
> > Add an old value SAR write before updating the new duty cycle to SAR.
> > This will keep the new value is always in a not empty fifo, and can be
> > wait to update after a period finished.
> > 
> > Limitation:
> > This workaround can only solve this issue when the PWM period is longer
> > than 2us(or <500KHz).
> > 
> > Reviewed-by: Jun Li <jun.li@nxp.com>
> > Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> > Link: https://github.com/nxp-imx/linux-imx/commit/16181cc4eee61d87cbaba0e5a479990507816317
> > Tested-by: Pratik Manvar <pratik.manvar@ifm.com>
> 
> You need to add your signed-off-by at the end when sending a patch, no
> matter if you are the author or not.

FTR: This also applies to the v2 patch. I discarded both from the pwm
patchwork.

Best regards
Uwe
diff mbox series

Patch

diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c
index 29a3089c534c..c21b2a9f02ae 100644
--- a/drivers/pwm/pwm-imx27.c
+++ b/drivers/pwm/pwm-imx27.c
@@ -21,11 +21,13 @@ 
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 
 #define MX3_PWMCR			0x00    /* PWM Control Register */
 #define MX3_PWMSR			0x04    /* PWM Status Register */
 #define MX3_PWMSAR			0x0C    /* PWM Sample Register */
 #define MX3_PWMPR			0x10    /* PWM Period Register */
+#define MX3_PWMCNR			0x14    /* PWM Counter Register */
 
 #define MX3_PWMCR_FWM			GENMASK(27, 26)
 #define MX3_PWMCR_STOPEN		BIT(25)
@@ -91,6 +93,7 @@  struct pwm_imx27_chip {
 	 * value to return in that case.
 	 */
 	unsigned int duty_cycle;
+	spinlock_t lock;
 };
 
 #define to_pwm_imx27_chip(chip)	container_of(chip, struct pwm_imx27_chip, chip)
@@ -203,10 +206,10 @@  static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
 
 	sr = readl(imx->mmio_base + MX3_PWMSR);
 	fifoav = FIELD_GET(MX3_PWMSR_FIFOAV, sr);
-	if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) {
+	if (fifoav >= MX3_PWMSR_FIFOAV_3WORDS) {
 		period_ms = DIV_ROUND_UP_ULL(pwm_get_period(pwm),
 					 NSEC_PER_MSEC);
-		msleep(period_ms);
+		msleep(period_ms * (fifoav - 2));
 
 		sr = readl(imx->mmio_base + MX3_PWMSR);
 		if (fifoav == FIELD_GET(MX3_PWMSR_FIFOAV, sr))
@@ -217,13 +220,15 @@  static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
 static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 			   const struct pwm_state *state)
 {
-	unsigned long period_cycles, duty_cycles, prescale;
+	unsigned long period_cycles, duty_cycles, prescale, counter_check, flags;
 	struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
+	void __iomem *reg_sar = imx->mmio_base + MX3_PWMSAR;
+	__force u32 sar_last, sar_current;
 	struct pwm_state cstate;
 	unsigned long long c;
 	unsigned long long clkrate;
 	int ret;
-	u32 cr;
+	u32 cr, timeout = 1000;
 
 	pwm_get_state(pwm, &cstate);
 
@@ -264,7 +269,57 @@  static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 		pwm_imx27_sw_reset(chip);
 	}
 
-	writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
+	/*
+	 * This is a limited workaround. When the SAR FIFO is empty, the new
+	 * write value will be directly applied to SAR even the current period
+	 * is not over.
+	 * If the new SAR value is less than the old one, and the counter is
+	 * greater than the new SAR value, the current period will not filp
+	 * the level. This will result in a pulse with a duty cycle of 100%.
+	 * So, writing the current value of the SAR to SAR here before updating
+	 * the new SAR value can avoid this issue.
+	 *
+	 * Add a spin lock and turn off the interrupt to ensure that the
+	 * real-time performance can be guaranteed as much as possible when
+	 * operating the following operations.
+	 *
+	 * 1. Add a threshold of 1.5us. If the time T between the read current
+	 * count value CNR and the end of the cycle is less than 1.5us, wait
+	 * for T to be longer than 1.5us before updating the SAR register.
+	 * This is to avoid the situation that when the first SAR is written,
+	 * the current cycle just ends and the SAR FIFO that just be written
+	 * is emptied again.
+	 *
+	 * 2. Use __raw_writel() to minimize the interval between two writes to
+	 * the SAR register to increase the fastest pwm frequency supported.
+	 *
+	 * When the PWM period is longer than 2us(or <500KHz), this workaround
+	 * can solve this problem.
+	 */
+	if (duty_cycles < imx->duty_cycle) {
+		c = clkrate * 1500;
+		do_div(c, NSEC_PER_SEC);
+		counter_check = c;
+		sar_last = cpu_to_le32(imx->duty_cycle);
+		sar_current = cpu_to_le32(duty_cycles);
+
+		spin_lock_irqsave(&imx->lock, flags);
+		if (state->period >= 2000) {
+			while ((period_cycles -
+				readl_relaxed(imx->mmio_base + MX3_PWMCNR))
+				< counter_check) {
+				if (!--timeout)
+					break;
+			};
+		}
+		if (!(MX3_PWMSR_FIFOAV &
+		      readl_relaxed(imx->mmio_base + MX3_PWMSR)))
+			__raw_writel(sar_last, reg_sar);
+		__raw_writel(sar_current, reg_sar);
+		spin_unlock_irqrestore(&imx->lock, flags);
+	} else
+		writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
+
 	writel(period_cycles, imx->mmio_base + MX3_PWMPR);
 
 	/*
@@ -325,6 +380,8 @@  static int pwm_imx27_probe(struct platform_device *pdev)
 		return dev_err_probe(&pdev->dev, PTR_ERR(imx->clk_per),
 				     "failed to get peripheral clock\n");
 
+	spin_lock_init(&imx->lock);
+	imx->duty_cycle = 0;
 	imx->chip.ops = &pwm_imx27_ops;
 	imx->chip.dev = &pdev->dev;
 	imx->chip.npwm = 1;