@@ -27,6 +27,7 @@
#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)
@@ -232,8 +233,11 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
{
unsigned long period_cycles, duty_cycles, prescale;
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
+ void __iomem *reg_sar = imx->mmio_base + MX3_PWMSAR;
unsigned long long c;
unsigned long long clkrate;
+ unsigned long flags;
+ int val;
int ret;
u32 cr;
@@ -274,7 +278,53 @@ 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.
+ */
+ val = FIELD_GET(MX3_PWMSR_FIFOAV, readl_relaxed(imx->mmio_base + MX3_PWMSR));
+ if (duty_cycles < imx->duty_cycle && val < MX3_PWMSR_FIFOAV_2WORDS) {
+ c = clkrate * 1500;
+ do_div(c, NSEC_PER_SEC);
+
+ local_irq_save(flags);
+ if (state->period >= 2000)
+ readl_poll_timeout_atomic(imx->mmio_base + MX3_PWMCNR, val,
+ period_cycles - val >= c, 0, 10);
+
+ val = FIELD_GET(MX3_PWMSR_FIFOAV, readl_relaxed(imx->mmio_base + MX3_PWMSR));
+ if (!val)
+ writel_relaxed(imx->duty_cycle, reg_sar);
+ writel_relaxed(duty_cycles, reg_sar);
+ local_irq_restore(flags);
+ } else {
+ writel_relaxed(duty_cycles, reg_sar);
+ }
+
writel(period_cycles, imx->mmio_base + MX3_PWMPR);
/*