aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-pca9685.c
diff options
context:
space:
mode:
authorClemens Gruber <clemens.gruber@pqgruber.com>2021-04-15 14:14:49 +0200
committerThierry Reding <thierry.reding@gmail.com>2021-04-23 18:45:14 +0200
commit8f4768a56b673cbff3f24cf7b1784852c0f572d1 (patch)
tree0fd1a6b6a495231cde55423c86bd7301e7da727b /drivers/pwm/pwm-pca9685.c
parentpwm: pca9685: Switch to atomic API (diff)
downloadlinux-dev-8f4768a56b673cbff3f24cf7b1784852c0f572d1.tar.xz
linux-dev-8f4768a56b673cbff3f24cf7b1784852c0f572d1.zip
pwm: pca9685: Support hardware readout
Implement .get_state to read-out the current hardware state. The hardware readout may return slightly different values than those that were set in apply due to the limited range of possible prescale and counter register values. Also note that although the datasheet mentions 200 Hz as default frequency when using the internal 25 MHz oscillator, the calculated period from the default prescaler register setting of 30 is 5079040ns. Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com> Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm/pwm-pca9685.c')
-rw-r--r--drivers/pwm/pwm-pca9685.c36
1 files changed, 36 insertions, 0 deletions
diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index b740b824bda9..5db62e5af518 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -331,6 +331,41 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
+static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct pca9685 *pca = to_pca(chip);
+ unsigned long long duty;
+ unsigned int val = 0;
+
+ /* Calculate (chip-wide) period from prescale value */
+ regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
+ /*
+ * PCA9685_OSC_CLOCK_MHZ is 25, i.e. an integer divider of 1000.
+ * The following calculation is therefore only a multiplication
+ * and we are not losing precision.
+ */
+ state->period = (PCA9685_COUNTER_RANGE * 1000 / PCA9685_OSC_CLOCK_MHZ) *
+ (val + 1);
+
+ /* The (per-channel) polarity is fixed */
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ if (pwm->hwpwm >= PCA9685_MAXCHAN) {
+ /*
+ * The "all LEDs" channel does not support HW readout
+ * Return 0 and disabled for backwards compatibility
+ */
+ state->duty_cycle = 0;
+ state->enabled = false;
+ return;
+ }
+
+ state->enabled = true;
+ duty = pca9685_pwm_get_duty(pca, pwm->hwpwm);
+ state->duty_cycle = DIV_ROUND_DOWN_ULL(duty * state->period, PCA9685_COUNTER_RANGE);
+}
+
static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pca9685 *pca = to_pca(chip);
@@ -353,6 +388,7 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
static const struct pwm_ops pca9685_pwm_ops = {
.apply = pca9685_pwm_apply,
+ .get_state = pca9685_pwm_get_state,
.request = pca9685_pwm_request,
.free = pca9685_pwm_free,
.owner = THIS_MODULE,