pwm: Changes for v5.17-rc1

This contains a number of nice cleanups and improvements for the core
 and various drivers as well as a minor tweak to the json-schema device
 tree bindings.
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEEiOrDCAFJzPfAjcif3SOs138+s6EFAmHn9jsZHHRoaWVycnku
 cmVkaW5nQGdtYWlsLmNvbQAKCRDdI6zXfz6zofJxEACUZbZ7IcJoq2zQ+TbP6DPW
 DN8UyT41JsvHmHwWOv1Y3LZkHFE2HN9novJWxTx9tS0aUjwizKpJpBC3up7NC9gj
 67vnHdyISAqdRGsrgGkza0q2h3nA0rgz3EINj+XeCdvtU+3wCYHahaiy0hH8GSjN
 XnqBOJQDLKqC3a5MGtZZ+oGUIARyCVVhbDlUrXUU+6Q71SjmjbVUtt/RjjYl0qsO
 RKHR5Wz8wg6reR9eJDOST2HvYoi2Z1Zf8M+i4kSF+dwHcRzbCEu7VZLGiz8lovBO
 l9+2jU/11Lqqr7a3qIw7/xeBF3WSL31QQTFaDq+hX/xWFa4fmtyvw2J68ZoLAKib
 eEjx6lfrbAx/9dlfs7l0IQLsPgty/JBJE760vwDXYpQ5VqAqhCRWHPABQjpGi/nB
 CSvXw1ZJrxnMUISwfkrEZxQzsKLDKBjqTaTHQ8VK8nmwjHvktE/5wfuTAIfipuj4
 8JCNSX8JzfHkhATv7TBepNxJh1JEjrqeQ2abFgmRqJrGNeoN4wgRSg6OWxIiBXCj
 XQNhxfmEmPguJ85XHIvMqSt8d+9o/DCdOB5y9IKGFTrAJZVBhdxw06qxTCiXasar
 31mQYDCejzxVVsPRfN5q0fZXlz6Sskzcoj7fKKpsyNtik71ONxv3XPLuq5JOLuwv
 Z1wMLELztSo/3AFELNi25Q==
 =T47H
 -----END PGP SIGNATURE-----

Merge tag 'pwm/for-5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm

Pull pwm updates from Thierry Reding:
 "This contains a number of nice cleanups and improvements for the core
  and various drivers, as well as a minor tweak to the json-schema
  device tree bindings"

* tag 'pwm/for-5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  dt-bindings: pwm: Avoid selecting schema on node name match
  pwm: img: Use only a single idiom to get a runtime PM reference
  pwm: vt8500: Implement .apply() callback
  pwm: img: Implement .apply() callback
  pwm: twl: Implement .apply() callback
  pwm: Restore initial state if a legacy callback fails
  pwm: Prevent a glitch for legacy drivers
  pwm: Move legacy driver handling into a dedicated function
This commit is contained in:
Linus Torvalds 2022-01-20 13:25:01 +02:00
commit 41652aae67
5 changed files with 213 additions and 82 deletions

View file

@ -9,6 +9,8 @@ title: PWM controllers (providers)
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
select: false
properties:
$nodename:
pattern: "^pwm(@.*|-[0-9a-f])*$"

View file

@ -548,6 +548,73 @@ static void pwm_apply_state_debug(struct pwm_device *pwm,
}
}
static int pwm_apply_legacy(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
int err;
struct pwm_state initial_state = pwm->state;
if (state->polarity != pwm->state.polarity) {
if (!chip->ops->set_polarity)
return -EINVAL;
/*
* Changing the polarity of a running PWM is only allowed when
* the PWM driver implements ->apply().
*/
if (pwm->state.enabled) {
chip->ops->disable(chip, pwm);
/*
* Update pwm->state already here in case
* .set_polarity() or another callback depend on that.
*/
pwm->state.enabled = false;
}
err = chip->ops->set_polarity(chip, pwm, state->polarity);
if (err)
goto rollback;
pwm->state.polarity = state->polarity;
}
if (!state->enabled) {
if (pwm->state.enabled)
chip->ops->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.
*/
err = chip->ops->config(pwm->chip, pwm,
state->duty_cycle,
state->period);
if (err)
goto rollback;
pwm->state.period = state->period;
pwm->state.duty_cycle = state->duty_cycle;
if (!pwm->state.enabled) {
err = chip->ops->enable(chip, pwm);
if (err)
goto rollback;
}
return 0;
rollback:
pwm->state = initial_state;
return err;
}
/**
* pwm_apply_state() - atomically apply a new state to a PWM device
* @pwm: PWM device
@ -580,70 +647,22 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
state->usage_power == pwm->state.usage_power)
return 0;
if (chip->ops->apply) {
if (chip->ops->apply)
err = chip->ops->apply(chip, pwm, state);
if (err)
return err;
else
err = pwm_apply_legacy(chip, pwm, state);
if (err)
return err;
trace_pwm_apply(pwm, state);
trace_pwm_apply(pwm, state);
pwm->state = *state;
pwm->state = *state;
/*
* only do this after pwm->state was applied as some
* implementations of .get_state depend on this
*/
pwm_apply_state_debug(pwm, state);
} else {
/*
* FIXME: restore the initial state in case of error.
*/
if (state->polarity != pwm->state.polarity) {
if (!chip->ops->set_polarity)
return -EINVAL;
/*
* Changing the polarity of a running PWM is
* only allowed when the PWM driver implements
* ->apply().
*/
if (pwm->state.enabled) {
chip->ops->disable(chip, pwm);
pwm->state.enabled = false;
}
err = chip->ops->set_polarity(chip, pwm,
state->polarity);
if (err)
return err;
pwm->state.polarity = state->polarity;
}
if (state->period != pwm->state.period ||
state->duty_cycle != pwm->state.duty_cycle) {
err = chip->ops->config(pwm->chip, pwm,
state->duty_cycle,
state->period);
if (err)
return err;
pwm->state.duty_cycle = state->duty_cycle;
pwm->state.period = state->period;
}
if (state->enabled != pwm->state.enabled) {
if (state->enabled) {
err = chip->ops->enable(chip, pwm);
if (err)
return err;
} else {
chip->ops->disable(chip, pwm);
}
pwm->state.enabled = state->enabled;
}
}
/*
* only do this after pwm->state was applied as some
* implementations of .get_state depend on this
*/
pwm_apply_state_debug(pwm, state);
return 0;
}

View file

@ -128,11 +128,9 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
duty = DIV_ROUND_UP(timebase * duty_ns, period_ns);
ret = pm_runtime_get_sync(chip->dev);
if (ret < 0) {
pm_runtime_put_autosuspend(chip->dev);
ret = pm_runtime_resume_and_get(chip->dev);
if (ret < 0)
return ret;
}
val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG);
val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm));
@ -184,10 +182,33 @@ static void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
pm_runtime_put_autosuspend(chip->dev);
}
static int img_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
int err;
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
if (!state->enabled) {
if (pwm->state.enabled)
img_pwm_disable(chip, pwm);
return 0;
}
err = img_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
if (err)
return err;
if (!pwm->state.enabled)
err = img_pwm_enable(chip, pwm);
return err;
}
static const struct pwm_ops img_pwm_ops = {
.config = img_pwm_config,
.enable = img_pwm_enable,
.disable = img_pwm_disable,
.apply = img_pwm_apply,
.owner = THIS_MODULE,
};

View file

@ -58,9 +58,9 @@ static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip)
}
static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
u64 duty_ns, u64 period_ns)
{
int duty_cycle = DIV_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1;
int duty_cycle = DIV64_U64_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1;
u8 pwm_config[2] = { 1, 0 };
int base, ret;
@ -279,19 +279,65 @@ static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
mutex_unlock(&twl->mutex);
}
static int twl4030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
int err;
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
if (!state->enabled) {
if (pwm->state.enabled)
twl4030_pwm_disable(chip, pwm);
return 0;
}
err = twl_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
if (err)
return err;
if (!pwm->state.enabled)
err = twl4030_pwm_enable(chip, pwm);
return err;
}
static int twl6030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
int err;
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
if (!state->enabled) {
if (pwm->state.enabled)
twl6030_pwm_disable(chip, pwm);
return 0;
}
err = twl_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
if (err)
return err;
if (!pwm->state.enabled)
err = twl6030_pwm_enable(chip, pwm);
return err;
}
static const struct pwm_ops twl4030_pwm_ops = {
.config = twl_pwm_config,
.enable = twl4030_pwm_enable,
.disable = twl4030_pwm_disable,
.apply = twl4030_pwm_apply,
.request = twl4030_pwm_request,
.free = twl4030_pwm_free,
.owner = THIS_MODULE,
};
static const struct pwm_ops twl6030_pwm_ops = {
.config = twl_pwm_config,
.enable = twl6030_pwm_enable,
.disable = twl6030_pwm_disable,
.apply = twl6030_pwm_apply,
.owner = THIS_MODULE,
};

View file

@ -70,7 +70,7 @@ static inline void vt8500_pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 b
}
static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
u64 duty_ns, u64 period_ns)
{
struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
unsigned long long c;
@ -102,8 +102,8 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
}
c = (unsigned long long)pv * duty_ns;
do_div(c, period_ns);
dc = c;
dc = div64_u64(c, period_ns);
writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm));
vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE);
@ -176,11 +176,54 @@ static int vt8500_pwm_set_polarity(struct pwm_chip *chip,
return 0;
}
static int vt8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
int err;
bool enabled = pwm->state.enabled;
if (state->polarity != pwm->state.polarity) {
/*
* Changing the polarity of a running PWM is only allowed when
* the PWM driver implements ->apply().
*/
if (enabled) {
vt8500_pwm_disable(chip, pwm);
enabled = false;
}
err = vt8500_pwm_set_polarity(chip, pwm, state->polarity);
if (err)
return err;
}
if (!state->enabled) {
if (enabled)
vt8500_pwm_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.
*/
err = vt8500_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
if (err)
return err;
if (!enabled)
err = vt8500_pwm_enable(chip, pwm);
return err;
}
static const struct pwm_ops vt8500_pwm_ops = {
.enable = vt8500_pwm_enable,
.disable = vt8500_pwm_disable,
.config = vt8500_pwm_config,
.set_polarity = vt8500_pwm_set_polarity,
.apply = vt8500_pwm_apply,
.owner = THIS_MODULE,
};