// SPDX-License-Identifier: GPL-2.0-only /* * NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver * * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com> * * Notes * ===== * NXP LPC18xx provides a State Configurable Timer (SCT) which can be configured * as a Pulse Width Modulator. * * SCT supports 16 outputs, 16 events and 16 registers. Each event will be * triggered when its related register matches the SCT counter value, and it * will set or clear a selected output. * * One of the events is preselected to generate the period, thus the maximum * number of simultaneous channels is limited to 15. Notice that period is * global to all the channels, thus PWM driver will refuse setting different * values to it, unless there's only one channel requested.
*/
/* * Simultaneous set and clear may happen on an output, that is the case * when duty_ns == period_ns. LPC18xx SCT allows to set a conflict * resolution action to be taken in such a case.
*/
val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_RES_BASE);
val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm);
val |= LPC18XX_PWM_RES(pwm->hwpwm, action);
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val);
}
/* * With clk_rate < NSEC_PER_SEC this cannot overflow. * With period_ns < max_period_ns this also fits into an u32. * As period_ns >= min_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, lpc18xx_pwm->clk_rate); * we have val >= 1.
*/
val = mul_u64_u64_div_u64(period_ns, lpc18xx_pwm->clk_rate, NSEC_PER_SEC);
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_MATCH(lpc18xx_pwm->period_event),
val - 1);
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_MATCHREL(lpc18xx_pwm->period_event),
val - 1);
}
/* * With clk_rate <= NSEC_PER_SEC this cannot overflow. * With duty_ns <= period_ns < max_period_ns this also fits into an u32.
*/
val = mul_u64_u64_div_u64(duty_ns, lpc18xx_pwm->clk_rate, NSEC_PER_SEC);
/* * The PWM supports only a single period for all PWM channels. * Once the period is set, it can only be changed if no more than one * channel is requested at that moment.
*/ if (requested_events > 2 && lpc18xx_pwm->period_ns != period_ns &&
lpc18xx_pwm->period_ns) {
dev_err(pwmchip_parent(chip), "conflicting period requested for PWM %u\n",
pwm->hwpwm); return -EBUSY;
}
lpc18xx_pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(lpc18xx_pwm->base)) return PTR_ERR(lpc18xx_pwm->base);
lpc18xx_pwm->pwm_clk = devm_clk_get_enabled(&pdev->dev, "pwm"); if (IS_ERR(lpc18xx_pwm->pwm_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(lpc18xx_pwm->pwm_clk), "failed to get pwm clock\n");
lpc18xx_pwm->clk_rate = clk_get_rate(lpc18xx_pwm->pwm_clk); if (!lpc18xx_pwm->clk_rate) return dev_err_probe(&pdev->dev,
-EINVAL, "pwm clock has no frequency\n");
/* * If clkrate is too fast, the calculations in .apply() might overflow.
*/ if (lpc18xx_pwm->clk_rate > NSEC_PER_SEC) return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock to fast\n");
/* SCT counter must be in unify (32 bit) mode */
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CONFIG,
LPC18XX_PWM_CONFIG_UNIFY);
/* * Everytime the timer counter reaches the period value, the related * event will be triggered and the counter reset to 0.
*/
set_bit(LPC18XX_PWM_EVENT_PERIOD, &lpc18xx_pwm->event_map);
lpc18xx_pwm->period_event = LPC18XX_PWM_EVENT_PERIOD;
val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL);
val &= ~LPC18XX_PWM_BIDIR;
val &= ~LPC18XX_PWM_CTRL_HALT;
val &= ~LPC18XX_PWM_PRE_MASK;
val |= LPC18XX_PWM_PRE(0);
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val);
ret = pwmchip_add(chip); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "pwmchip_add failed\n");
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.