diff options
Diffstat (limited to 'drivers/pwm/pwm-twl-led.c')
| -rw-r--r-- | drivers/pwm/pwm-twl-led.c | 92 |
1 files changed, 82 insertions, 10 deletions
diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c index 49d9f7a78012..8fb84b441853 100644 --- a/drivers/pwm/pwm-twl-led.c +++ b/drivers/pwm/pwm-twl-led.c @@ -7,6 +7,22 @@ * * This driver is a complete rewrite of the former pwm-twl6030.c authorded by: * Hemanth V <[email protected]> + * + * Reference manual for the twl6030 is available at: + * https://www.ti.com/lit/ds/symlink/twl6030.pdf + * + * Limitations: + * - The twl6030 hardware only supports two period lengths (128 clock ticks and + * 64 clock ticks), the driver only uses 128 ticks + * - The hardware doesn't support ON = 0, so the active part of a period doesn't + * start at its beginning. + * - The hardware could support inverted polarity (with a similar limitation as + * for normal: the last clock tick is always inactive). + * - The hardware emits a constant low output when disabled. + * - A request for .duty_cycle = 0 results in an output wave with one active + * clock tick per period. This should better use the disabled state. + * - The driver only implements setting the relative duty cycle. + * - The driver doesn't implement .get_state(). */ #include <linux/module.h> @@ -137,6 +153,45 @@ out: mutex_unlock(&twl->mutex); } +static int twl4030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int ret; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (!state->enabled) { + if (pwm->state.enabled) + twl4030_pwmled_disable(chip, pwm); + + return 0; + } + + /* + * We cannot skip calling ->config even if state->period == + * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle + * because we might have exited early in the last call to + * pwm_apply_state because of !state->enabled and so the two values in + * pwm->state might not be configured in hardware. + */ + ret = twl4030_pwmled_config(pwm->chip, pwm, + state->duty_cycle, state->period); + if (ret) + return ret; + + if (!pwm->state.enabled) + ret = twl4030_pwmled_enable(chip, pwm); + + return ret; +} + + +static const struct pwm_ops twl4030_pwmled_ops = { + .apply = twl4030_pwmled_apply, + .owner = THIS_MODULE, +}; + static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { @@ -206,6 +261,32 @@ out: mutex_unlock(&twl->mutex); } +static int twl6030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int err; + + if (state->polarity != pwm->state.polarity) + return -EINVAL; + + if (!state->enabled) { + if (pwm->state.enabled) + twl6030_pwmled_disable(chip, pwm); + + return 0; + } + + err = twl6030_pwmled_config(pwm->chip, pwm, + state->duty_cycle, state->period); + if (err) + return err; + + if (!pwm->state.enabled) + err = twl6030_pwmled_enable(chip, pwm); + + return err; +} + static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) { struct twl_pwmled_chip *twl = to_twl(chip); @@ -257,17 +338,8 @@ out: mutex_unlock(&twl->mutex); } -static const struct pwm_ops twl4030_pwmled_ops = { - .enable = twl4030_pwmled_enable, - .disable = twl4030_pwmled_disable, - .config = twl4030_pwmled_config, - .owner = THIS_MODULE, -}; - static const struct pwm_ops twl6030_pwmled_ops = { - .enable = twl6030_pwmled_enable, - .disable = twl6030_pwmled_disable, - .config = twl6030_pwmled_config, + .apply = twl6030_pwmled_apply, .request = twl6030_pwmled_request, .free = twl6030_pwmled_free, .owner = THIS_MODULE, |