/* This function is supposed to be called with the lock mutex held */ staticbool pca9685_prescaler_can_change(struct pca9685 *pca, int channel)
{ /* No PWM enabled: Change allowed */ if (bitmap_empty(pca->pwms_enabled, PCA9685_MAXCHAN + 1)) returntrue; /* More than one PWM enabled: Change not allowed */ if (bitmap_weight(pca->pwms_enabled, PCA9685_MAXCHAN + 1) > 1) returnfalse; /* *OnlyonePWMenabled:ChangeallowedifthePWMaboutto *bechangedistheonethatisalreadyenabled
*/ return test_bit(channel, pca->pwms_enabled);
}
err = regmap_bulk_write(pca->regmap, reg, val, 4); if (err)
dev_err(dev, "regmap_write to register 0x%x failed: %pe\n", reg, ERR_PTR(err));
return err;
}
/* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */ staticvoid pca9685_pwm_set_duty(struct pwm_chip *chip, int channel, unsignedint duty)
{ struct pwm_device *pwm = &chip->pwms[channel]; unsignedint on, off;
if (duty == 0) { /* Set the full OFF bit, which has the highest precedence */
pca9685_write_reg(chip, REG_OFF_H(channel), LED_FULL); return;
} elseif (duty >= PCA9685_COUNTER_RANGE) { /* Set the full ON bit and clear the full OFF bit */
pca9685_write_4reg(chip, REG_ON_L(channel), (u8[4]){ 0, LED_FULL, 0, 0 }); return;
}
if (pwm->state.usage_power && channel < PCA9685_MAXCHAN) { /* *Ifusage_powerisset,thepca9685driverwillphaseshift *theindividualchannelsrelativetotheirchannelnumber. *ThisimprovesEMIbecausetheenabledchannelsnolonger *turnonatthesametime,whilestillmaintainingthe *configureddutycycle/poweroutput.
*/
on = channel * PCA9685_COUNTER_RANGE / PCA9685_MAXCHAN;
} else
on = 0;
off = (on + duty) % PCA9685_COUNTER_RANGE;
/* implicitly clear full ON and full OFF bit */
pca9685_write_4reg(chip, REG_ON_L(channel),
(u8[4]){ on & 0xff, (on >> 8) & 0xf, off & 0xff, (off >> 8) & 0xf });
}
staticunsignedint pca9685_pwm_get_duty(struct pwm_chip *chip, int channel)
{ struct pwm_device *pwm = &chip->pwms[channel]; unsignedint off = 0, on = 0, val = 0;
if (WARN_ON(channel >= PCA9685_MAXCHAN)) { /* HW does not support reading state of "all LEDs" channel */ return0;
}
pca9685_read_reg(chip, LED_N_OFF_H(channel), &off); if (off & LED_FULL) { /* Full OFF bit is set */ return0;
}
pca9685_read_reg(chip, LED_N_ON_H(channel), &on); if (on & LED_FULL) { /* Full ON bit is set */ return PCA9685_COUNTER_RANGE;
}
pca9685_read_reg(chip, LED_N_OFF_L(channel), &val);
off = ((off & 0xf) << 8) | (val & 0xff); if (!pwm->state.usage_power) return off;
/* Read ON register to calculate duty cycle of staggered output */ if (pca9685_read_reg(chip, LED_N_ON_L(channel), &val)) { /* Reset val to 0 in case reading LED_N_ON_L failed */
val = 0;
}
on = ((on & 0xf) << 8) | (val & 0xff); return (off - on) & (PCA9685_COUNTER_RANGE - 1);
}
if (state->polarity != PWM_POLARITY_NORMAL) return -EINVAL;
prescale = DIV_ROUND_CLOSEST_ULL(PCA9685_OSC_CLOCK_MHZ * state->period,
PCA9685_COUNTER_RANGE * 1000) - 1; if (prescale < PCA9685_PRESCALE_MIN || prescale > PCA9685_PRESCALE_MAX) {
dev_err(pwmchip_parent(chip), "pwm not changed: period out of bounds!\n"); return -EINVAL;
}
if (!state->enabled) {
pca9685_pwm_set_duty(chip, pwm->hwpwm, 0); return0;
}
pca9685_read_reg(chip, PCA9685_PRESCALE, &val); if (prescale != val) { if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) {
dev_err(pwmchip_parent(chip), "pwm not changed: periods of enabled pwms must match!\n"); return -EBUSY;
}
/* *PuttingthechipbrieflyintoSLEEPmode *atthispointwon'tinterferewiththe *pm_runtimeframework,becausethepm_runtime *stateisguaranteedactivehere.
*/ /* Put chip into sleep mode */
pca9685_set_sleep_mode(chip, true);
/* Change the chip-wide output frequency */
pca9685_write_reg(chip, PCA9685_PRESCALE, prescale);
/* Wake the chip up */
pca9685_set_sleep_mode(chip, false);
}
/* Reset OFF/ON registers to POR default */
ret = pca9685_write_4reg(chip, PCA9685_ALL_LED_ON_L, (u8[]){ 0, LED_FULL, 0, LED_FULL }); if (ret < 0) return dev_err_probe(&client->dev, ret, "Failed to reset ON/OFF registers\n");
chip->ops = &pca9685_pwm_ops;
ret = pwmchip_add(chip); if (ret < 0) return ret;
ret = pca9685_pwm_gpio_probe(chip); if (ret < 0) {
pwmchip_remove(chip); return ret;
}
pm_runtime_enable(&client->dev);
if (pm_runtime_enabled(&client->dev)) { /* *Althoughthechipcomesoutofpower-upinthesleepstate, *weforceittosleepincaseitwaswokenupbefore
*/
pca9685_set_sleep_mode(chip, true);
pm_runtime_set_suspended(&client->dev);
} else { /* Wake the chip up if runtime PM is disabled */
pca9685_set_sleep_mode(chip, false);
}
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.