// SPDX-License-Identifier: GPL-2.0 /* * simple driver for PWM (Pulse Width Modulator) controller * * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com> * * Limitations: * - When disabled the output is driven to 0 independent of the configured * polarity.
*/
/* * The driver cannot read the current duty cycle from the hardware if * the hardware is disabled. Cache the last programmed duty cycle * value to return in that case.
*/ unsignedint duty_cycle;
};
/* * PWMSAR can be read only if PWM is enabled. If the PWM is disabled, * use the cached value.
*/ if (state->enabled)
val = readl(imx->mmio_base + MX3_PWMSAR); else
val = imx->duty_cycle;
/* * according to imx pwm RM, the real period value should be PERIOD * value in PWMPR plus 2.
*/ if (period_cycles > 2)
period_cycles -= 2; else
period_cycles = 0;
/* * Wait for a free FIFO slot if the PWM is already enabled, and flush * the FIFO if the PWM was disabled and is about to be enabled.
*/ if (pwm->state.enabled) {
pwm_imx27_wait_fifo_slot(chip, pwm);
} else {
ret = clk_bulk_prepare_enable(imx->clks_cnt, imx->clks); if (ret) return ret;
/* * ERR051198: * PWM: PWM output may not function correctly if the FIFO is empty when * a new SAR value is programmed * * Description: * When the PWM FIFO is empty, a new value programmed to the PWM Sample * register (PWM_PWMSAR) will be directly applied even if the current * timer period has not expired. * * If the new SAMPLE value programmed in the PWM_PWMSAR register is * less than the previous value, and the PWM counter register * (PWM_PWMCNR) that contains the current COUNT value is greater than * the new programmed SAMPLE value, the current period will not flip * the level. This may result in an output pulse with a duty cycle of * 100%. * * Consider a change from * ________ * / \______/ * ^ * ^ * to * ____ * / \__________/ * ^ ^ * At the time marked by *, the new write value will be directly applied * to SAR even the current period is not over if FIFO is empty. * * ________ ____________________ * / \______/ \__________/ * ^ ^ * ^ ^ * |<-- old SAR -->| |<-- new SAR -->| * * That is the output is active for a whole period. * * Workaround: * Check new SAR less than old SAR and current counter is in errata * windows, write extra old SAR into FIFO and new SAR will effect at * next period. * * Sometime period is quite long, such as over 1 second. If add old SAR * into FIFO unconditional, new SAR have to wait for next period. It * may be too long. * * Turn off the interrupt to ensure that not IRQ and schedule happen * during above operations. If any irq and schedule happen, counter * in PWM will be out of data and take wrong action. * * Add a safety margin 1.5us because it needs some time to complete * IO write. * * Use writel_relaxed() 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. No software workaround is available if PWM * period is shorter than IO write. Just try best to fill old data * into FIFO.
*/
c = clkrate * 1500;
do_div(c, NSEC_PER_SEC);
local_irq_save(flags);
val = FIELD_GET(MX3_PWMSR_FIFOAV, readl_relaxed(imx->mmio_base + MX3_PWMSR));
if (duty_cycles < imx->duty_cycle && (cr & MX3_PWMCR_EN)) { if (period_us < 2) { /* 2us = 500 kHz */ /* Best effort attempt to fix up >500 kHz case */
udelay(3 * period_us);
writel_relaxed(imx->duty_cycle, imx->mmio_base + MX3_PWMSAR);
writel_relaxed(imx->duty_cycle, imx->mmio_base + MX3_PWMSAR);
} elseif (val < MX3_PWMSR_FIFOAV_2WORDS) {
val = readl_relaxed(imx->mmio_base + MX3_PWMCNR); /* * If counter is close to period, controller may roll over when * next IO write.
*/ if ((val + c >= duty_cycles && val < imx->duty_cycle) ||
val + c >= period_cycles)
writel_relaxed(imx->duty_cycle, imx->mmio_base + MX3_PWMSAR);
}
}
writel_relaxed(duty_cycles, imx->mmio_base + MX3_PWMSAR);
local_irq_restore(flags);
/* * Store the duty cycle for future reference in cases where the * MX3_PWMSAR register can't be read (i.e. when the PWM is disabled).
*/
imx->duty_cycle = duty_cycles;
imx->clks_cnt = ARRAY_SIZE(pwm_imx27_clks); for (i = 0; i < imx->clks_cnt; ++i)
imx->clks[i].id = pwm_imx27_clks[i];
ret = devm_clk_bulk_get(&pdev->dev, imx->clks_cnt, imx->clks);
if (ret) return dev_err_probe(&pdev->dev, ret, "getting clocks failed\n");
chip->ops = &pwm_imx27_ops;
imx->mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base);
ret = clk_bulk_prepare_enable(imx->clks_cnt, imx->clks); if (ret) return ret;
/* keep clks on if pwm is running */
pwmcr = readl(imx->mmio_base + MX3_PWMCR); if (!(pwmcr & MX3_PWMCR_EN))
clk_bulk_disable_unprepare(imx->clks_cnt, imx->clks);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.