// SPDX-License-Identifier: GPL-2.0-only /* * Driver for Atmel Pulse Width Modulation Controller * * Copyright (C) 2013 Atmel Corporation * Bo Shen <voice.shen@atmel.com> * * Links to reference manuals for the supported PWM chips can be found in * Documentation/arch/arm/microchip.rst. * * Limitations: * - Periods start with the inactive level. * - Hardware has to be stopped in general to update settings. * * Software bugs/possible improvements: * - When atmel_pwm_apply() is called with state->enabled=false a change in * state->polarity isn't honored. * - Instead of sleeping to wait for a completed period, the interrupt * functionality could be used.
*/
/* The following is global registers for PWM controller */ #define PWM_ENA 0x04 #define PWM_DIS 0x08 #define PWM_SR 0x0C #define PWM_ISR 0x1C /* Bit field in SR */ #define PWM_SR_ALL_CH_MASK 0x0F
/* The following register is PWM channel related registers */ #define PWM_CH_REG_OFFSET 0x200 #define PWM_CH_REG_SIZE 0x20
#define PWM_CMR 0x0 /* Bit field in CMR */ #define PWM_CMR_CPOL (1 << 9) #define PWM_CMR_UPD_CDTY (1 << 10) #define PWM_CMR_CPRE_MSK 0xF
/* The following registers for PWM v1 */ #define PWMV1_CDTY 0x04 #define PWMV1_CPRD 0x08 #define PWMV1_CUPD 0x10
/* The following registers for PWM v2 */ #define PWMV2_CDTY 0x04 #define PWMV2_CDTYUPD 0x08 #define PWMV2_CPRD 0x0C #define PWMV2_CPRDUPD 0x10
/* * The hardware supports a mechanism to update a channel's duty cycle at * the end of the currently running period. When such an update is * pending we delay disabling the PWM until the new configuration is * active because otherwise pmw_config(duty_cycle=0); pwm_disable(); * might not result in an inactive output. * This bitmask tracks for which channels an update is pending in * hardware.
*/
u32 update_pending;
};
staticvoid atmel_pwm_update_pending(struct atmel_pwm_chip *chip)
{ /* * Each channel that has its bit in ISR set started a new period since * ISR was cleared and so there is no more update pending. Note that * reading ISR clears it, so this needs to handle all channels to not * loose information.
*/
u32 isr = atmel_pwm_readl(chip, PWM_ISR);
chip->update_pending &= ~isr;
}
staticvoid atmel_pwm_set_pending(struct atmel_pwm_chip *chip, unsignedint ch)
{ /* * Clear pending flags in hardware because otherwise there might still * be a stale flag in ISR.
*/
atmel_pwm_update_pending(chip);
chip->update_pending |= (1 << ch);
}
staticint atmel_pwm_test_pending(struct atmel_pwm_chip *chip, unsignedint ch)
{ int ret = 0;
if (chip->update_pending & (1 << ch)) {
atmel_pwm_update_pending(chip);
/* Calculate the period cycles and prescale value */
cycles *= clkrate;
do_div(cycles, NSEC_PER_SEC);
/* * The register for the period length is cfg.period_bits bits wide. * So for each bit the number of clock cycles is wider divide the input * clock frequency by two using pres and shift cprd accordingly.
*/
shift = fls(cycles) - atmel_pwm->data->cfg.period_bits;
ret = atmel_pwm_calculate_cprd_and_pres(chip, clkrate, state, &cprd,
&pres); if (ret) {
dev_err(pwmchip_parent(chip), "failed to calculate cprd and prescaler\n"); return ret;
}
staticint atmel_pwm_enable_clk_if_on(struct pwm_chip *chip, bool on)
{ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); unsignedint i, cnt = 0; unsignedlong sr; int ret = 0;
sr = atmel_pwm_readl(atmel_pwm, PWM_SR) & PWM_SR_ALL_CH_MASK; if (!sr) return 0;
cnt = bitmap_weight(&sr, chip->npwm);
if (!on) goto disable_clk;
for (i = 0; i < cnt; i++) {
ret = clk_enable(atmel_pwm->clk); if (ret) {
dev_err(pwmchip_parent(chip), "failed to enable clock for pwm %pe\n",
ERR_PTR(ret));
cnt = i; goto disable_clk;
}
}
return 0;
disable_clk: while (cnt--)
clk_disable(atmel_pwm->clk);
atmel_pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(atmel_pwm->base)) return PTR_ERR(atmel_pwm->base);
atmel_pwm->clk = devm_clk_get_prepared(&pdev->dev, NULL); if (IS_ERR(atmel_pwm->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(atmel_pwm->clk), "failed to get prepared PWM clock\n");
chip->ops = &atmel_pwm_ops;
ret = atmel_pwm_enable_clk_if_on(chip, true); if (ret < 0) return ret;
ret = devm_pwmchip_add(&pdev->dev, chip); if (ret < 0) {
dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n"); goto disable_clk;
}
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.