// SPDX-License-Identifier: GPL-2.0-or-later/* * Generic pwmlib implementation * * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2011-2012 Avionic Design GmbH */ #define DEFAULT_SYMBOL_NAMESPACE "PWM" #include <linux/acpi.h>#include <linux/module.h>#include <linux/idr.h>#include <linux/of.h>#include <linux/pwm.h>#include <linux/list.h>#include <linux/mutex.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/debugfs.h>#include <linux/seq_file.h> #include <dt-bindings/pwm/pwm.h> #include <uapi/linux/pwm.h> #define CREATE_TRACE_POINTS#include <trace/events/pwm.h> #define PWM_MINOR_COUNT 256 /* protects access to pwm_chips */static DEFINE_MUTEX(pwm_lock); static DEFINE_IDR(pwm_chips); static void pwmchip_lock(struct pwm_chip *chip){ if (chip->atomic) spin_lock(&chip->atomic_lock); else mutex_lock(&chip->nonatomic_lock);} static void pwmchip_unlock(struct pwm_chip *chip){ if (chip->atomic) spin_unlock(&chip->atomic_lock); else mutex_unlock(&chip->nonatomic_lock);} DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T)) static bool pwm_wf_valid(const struct pwm_waveform *wf){ /* * For now restrict waveforms to period_length_ns <= S64_MAX to provide * some space for future extensions. One possibility is to simplify * representing waveforms with inverted polarity using negative values * somehow. */ if (wf->period_length_ns > S64_MAX) return false; if (wf->duty_length_ns > wf->period_length_ns) return false; /* * .duty_offset_ns is supposed to be smaller than .period_length_ns, apart * from the corner case .duty_offset_ns == 0 && .period_length_ns == 0. */ if (wf->duty_offset_ns && wf->duty_offset_ns >= wf->period_length_ns) return false; return true;} static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state){ if (wf->period_length_ns) { if (wf->duty_length_ns + wf->duty_offset_ns < wf->period_length_ns) *state = (struct pwm_state){ .enabled = true, .polarity = PWM_POLARITY_NORMAL, .period = wf->period_length_ns, .duty_cycle = wf->duty_length_ns, }; else *state = (struct pwm_state){ .enabled = true, .polarity = PWM_POLARITY_INVERSED, .period = wf->period_length_ns, .duty_cycle = wf->period_length_ns - wf->duty_length_ns, }; } else { *state = (struct pwm_state){ .enabled = false, }; }} static void pwm_state2wf(const struct pwm_state *state, struct pwm_waveform *wf){ if (state->enabled) { if (state->polarity == PWM_POLARITY_NORMAL) *wf = (struct pwm_waveform){ .period_length_ns = state->period, .duty_length_ns = state->duty_cycle, .duty_offset_ns = 0, }; else *wf = (struct pwm_waveform){ .period_length_ns = state->period, .duty_length_ns = state->period - state->duty_cycle, .duty_offset_ns = state->duty_cycle, }; } else { *wf = (struct pwm_waveform){ .period_length_ns = 0, }; }} static int pwmwfcmp(const struct pwm_waveform *a, const struct pwm_waveform *b){ if (a->period_length_ns > b->period_length_ns) return 1; if (a->period_length_ns < b->period_length_ns) return -1; if (a->duty_length_ns > b->duty_length_ns) return 1; if (a->duty_length_ns < b->duty_length_ns) return -1; if (a->duty_offset_ns > b->duty_offset_ns) return 1; if (a->duty_offset_ns < b->duty_offset_ns) return -1; return 0;} static bool pwm_check_rounding(const struct pwm_waveform *wf, const struct pwm_waveform *wf_rounded){ if (!wf->period_length_ns) return true; if (wf->period_length_ns < wf_rounded->period_length_ns) return false; if (wf->duty_length_ns < wf_rounded->duty_length_ns) return false; if (wf->duty_offset_ns < wf_rounded->duty_offset_ns) return false; return true;} static int __pwm_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_waveform *wf, void *wfhw){ const struct pwm_ops *ops = chip->ops; int ret; ret = ops->round_waveform_tohw(chip, pwm, wf, wfhw); trace_pwm_round_waveform_tohw(pwm, wf, wfhw, ret); return ret;} static int __pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw, struct pwm_waveform *wf){ const struct pwm_ops *ops = chip->ops; int ret; ret = ops->round_waveform_fromhw(chip, pwm, wfhw, wf); trace_pwm_round_waveform_fromhw(pwm, wfhw, wf, ret); return ret;} static int __pwm_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *wfhw){ const struct pwm_ops *ops = chip->ops; int ret; ret = ops->read_waveform(chip, pwm, wfhw); trace_pwm_read_waveform(pwm, wfhw, ret); return ret;} static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw){ const struct pwm_ops *ops = chip->ops; int ret; ret = ops->write_waveform(chip, pwm, wfhw); trace_pwm_write_waveform(pwm, wfhw, ret); return ret;} /** * pwm_round_waveform_might_sleep - Query hardware capabilities * Cannot be used in atomic context. * @pwm: PWM device * @wf: waveform to round and output parameter * * Typically a given waveform cannot be implemented exactly by hardware, e.g. * because hardware only supports coarse period resolution or no duty_offset. * This function returns the actually implemented waveform if you pass @wf to * pwm_set_waveform_might_sleep() now. * * Note however that the world doesn't stop turning when you call it, so when * doing:: * * pwm_round_waveform_might_sleep(mypwm, &wf); * pwm_set_waveform_might_sleep(mypwm, &wf, true); * * the latter might fail, e.g. because an input clock changed its rate between * these two calls and the waveform determined by * pwm_round_waveform_might_sleep() cannot be implemented any more. * * Usually all values passed in @wf are rounded down to the nearest possible * value (in the order period_length_ns, duty_length_ns and then * duty_offset_ns). Only if this isn't possible, a value might grow. See the * documentation for pwm_set_waveform_might_sleep() for a more formal * description. * * Returns: 0 on success, 1 if at least one value had to be rounded up or a * negative errno. * Context: May sleep. */int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf){ struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; struct pwm_waveform wf_req = *wf; char wfhw[PWM_WFHWSIZE]; int ret_tohw, ret_fromhw; BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip)) return -EOPNOTSUPP; if (!pwm_wf_valid(wf)) return -EINVAL; guard(pwmchip)(chip); if (!chip->operational) return -ENODEV; ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, wfhw); if (ret_tohw < 0) return ret_tohw; if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw > 1) dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_tohw: requested %llu/%llu [+%llu], return value %d\n", wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw); ret_fromhw = __pwm_round_waveform_fromhw(chip, pwm, wfhw, wf); if (ret_fromhw < 0) return ret_fromhw; if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_fromhw > 0) dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_fromhw: requested %llu/%llu [+%llu], return value %d\n", wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_fromhw); if (IS_ENABLED(CONFIG_PWM_DEBUG) && (ret_tohw == 0) != pwm_check_rounding(&wf_req, wf)) dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n", wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, ret_tohw); return ret_tohw;}EXPORT_SYMBOL_GPL(pwm_round_waveform_might_sleep); /** * pwm_get_waveform_might_sleep - Query hardware about current configuration * Cannot be used in atomic context. * @pwm: PWM device * @wf: output parameter * * Stores the current configuration of the PWM in @wf. Note this is the * equivalent of pwm_get_state_hw() (and not pwm_get_state()) for pwm_waveform. * * Returns: 0 on success or a negative errno * Context: May sleep. */int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf){ struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; char wfhw[PWM_WFHWSIZE]; int err; BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip) || !ops->read_waveform) return -EOPNOTSUPP; guard(pwmchip)(chip); if (!chip->operational) return -ENODEV; err = __pwm_read_waveform(chip, pwm, &wfhw); if (err) return err; return __pwm_round_waveform_fromhw(chip, pwm, &wfhw, wf);}EXPORT_SYMBOL_GPL(pwm_get_waveform_might_sleep); /* Called with the pwmchip lock held */static int __pwm_set_waveform(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact){ struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; char wfhw[PWM_WFHWSIZE]; struct pwm_waveform wf_rounded; int err, ret_tohw; BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip)) return -EOPNOTSUPP; if (!pwm_wf_valid(wf)) return -EINVAL; ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw); if (ret_tohw < 0) return ret_tohw; if ((IS_ENABLED(CONFIG_PWM_DEBUG) || exact) && wf->period_length_ns) { err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded); if (err) return err; if (IS_ENABLED(CONFIG_PWM_DEBUG) && (ret_tohw == 0) != pwm_check_rounding(wf, &wf_rounded)) dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n", wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, ret_tohw); if (exact && pwmwfcmp(wf, &wf_rounded)) { dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\n", wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns); return 1; } } err = __pwm_write_waveform(chip, pwm, &wfhw); if (err) return err; /* update .state */ pwm_wf2state(wf, &pwm->state); if (IS_ENABLED(CONFIG_PWM_DEBUG) && ops->read_waveform && wf->period_length_ns) { struct pwm_waveform wf_set; err = __pwm_read_waveform(chip, pwm, &wfhw); if (err) /* maybe ignore? */ return err; err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_set); if (err) /* maybe ignore? */ return err; if (pwmwfcmp(&wf_set, &wf_rounded) != 0) dev_err(&chip->dev, "Unexpected setting: requested %llu/%llu [+%llu], expected %llu/%llu [+%llu], set %llu/%llu [+%llu]\n", wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, wf_set.duty_length_ns, wf_set.period_length_ns, wf_set.duty_offset_ns); } return ret_tohw;} /** * pwm_set_waveform_might_sleep - Apply a new waveform * Cannot be used in atomic context. * @pwm: PWM device * @wf: The waveform to apply * @exact: If true no rounding is allowed * * Typically a requested waveform cannot be implemented exactly, e.g. because * you requested .period_length_ns = 100 ns, but the hardware can only set * periods that are a multiple of 8.5 ns. With that hardware passing @exact = * true results in pwm_set_waveform_might_sleep() failing and returning -EDOM. * If @exact = false you get a period of 93.5 ns (i.e. the biggest period not * bigger than the requested value). * Note that even with @exact = true, some rounding by less than 1 ns is * possible/needed. In the above example requesting .period_length_ns = 94 and * @exact = true, you get the hardware configured with period = 93.5 ns. * * Let C be the set of possible hardware configurations for a given PWM device, * consisting of tuples (p, d, o) where p is the period length, d is the duty * length and o the duty offset. * * The following algorithm is implemented to pick the hardware setting * (p, d, o) ∈ C for a given request (p', d', o') with @exact = false:: * * p = max( { ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C ∧ ṗ ≤ p' } ∪ { min({ ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C }) }) * d = max( { ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C ∧ ḋ ≤ d' } ∪ { min({ ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C }) }) * o = max( { ȯ | (p, d, ȯ) ∈ C ∧ ȯ ≤ o' } ∪ { min({ ȯ | (p, d, ȯ) ∈ C }) }) * * In words: The chosen period length is the maximal possible period length not * bigger than the requested period length and if that doesn't exist, the * minimal period length. The chosen duty length is the maximal possible duty * length that is compatible with the chosen period length and isn't bigger than * the requested duty length. Again if such a value doesn't exist, the minimal * duty length compatible with the chosen period is picked. After that the duty * offset compatible with the chosen period and duty length is chosen in the * same way. * * Returns: 0 on success, -EDOM if setting failed due to the exact waveform not * being possible (if @exact), or a different negative errno on failure. * Context: May sleep. */int pwm_set_waveform_might_sleep(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact){ struct pwm_chip *chip = pwm->chip; int err; might_sleep(); guard(pwmchip)(chip); if (!chip->operational) return -ENODEV; if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) { /* * Catch any drivers that have been marked as atomic but * that will sleep anyway. */ non_block_start(); err = __pwm_set_waveform(pwm, wf, exact); non_block_end(); } else { err = __pwm_set_waveform(pwm, wf, exact); } /* * map err == 1 to -EDOM for exact requests and 0 for !exact ones. Also * make sure that -EDOM is only returned in exactly that case. Note that * __pwm_set_waveform() should never return -EDOM which justifies the * unlikely(). */ if (unlikely(err == -EDOM)) err = -EINVAL; else if (exact && err == 1) err = -EDOM; else if (err == 1) err = 0; return err;}EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep); static void pwm_apply_debug(struct pwm_device *pwm, const struct pwm_state *state){ struct pwm_state *last = &pwm->last; struct pwm_chip *chip = pwm->chip; struct pwm_state s1 = { 0 }, s2 = { 0 }; int err; if (!IS_ENABLED(CONFIG_PWM_DEBUG)) return; /* No reasonable diagnosis possible without .get_state() */ if (!chip->ops->get_state) return; /* * If a disabled PWM was requested the result is unspecified, so nothingLines 1–500 of 2759. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Generic pwmlib implementation * * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2011-2012 Avionic Design GmbH */ #define DEFAULT_SYMBOL_NAMESPACE "PWM" #include <linux/acpi.h> #include <linux/module.h> #include <linux/idr.h> #include <linux/of.h> #include <linux/pwm.h> #include <linux/list.h> #include <linux/mutex.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <dt-bindings/pwm/pwm.h> #include <uapi/linux/pwm.h> #define CREATE_TRACE_POINTS #include <trace/events/pwm.h> #define PWM_MINOR_COUNT 256 /* protects access to pwm_chips */ static DEFINE_MUTEX(pwm_lock); static DEFINE_IDR(pwm_chips); static void pwmchip_lock(struct pwm_chip *chip) { if (chip->atomic) spin_lock(&chip->atomic_lock); else mutex_lock(&chip->nonatomic_lock); } static void pwmchip_unlock(struct pwm_chip *chip) { if (chip->atomic) spin_unlock(&chip->atomic_lock); else mutex_unlock(&chip->nonatomic_lock); } DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T)) static bool pwm_wf_valid(const struct pwm_waveform *wf) { /* * For now restrict waveforms to period_length_ns <= S64_MAX to provide * some space for future extensions. One possibility is to simplify * representing waveforms with inverted polarity using negative values * somehow. */ if (wf->period_length_ns > S64_MAX) return false; if (wf->duty_length_ns > wf->period_length_ns) return false; /* * .duty_offset_ns is supposed to be smaller than .period_length_ns, apart * from the corner case .duty_offset_ns == 0 && .period_length_ns == 0. */ if (wf->duty_offset_ns && wf->duty_offset_ns >= wf->period_length_ns) return false; return true; } static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state) { if (wf->period_length_ns) { if (wf->duty_length_ns + wf->duty_offset_ns < wf->period_length_ns) *state = (struct pwm_state){ .enabled = true, .polarity = PWM_POLARITY_NORMAL, .period = wf->period_length_ns, .duty_cycle = wf->duty_length_ns, }; else *state = (struct pwm_state){ .enabled = true, .polarity = PWM_POLARITY_INVERSED, .period = wf->period_length_ns, .duty_cycle = wf->period_length_ns - wf->duty_length_ns, }; } else { *state = (struct pwm_state){ .enabled = false, }; } } static void pwm_state2wf(const struct pwm_state *state, struct pwm_waveform *wf) { if (state->enabled) { if (state->polarity == PWM_POLARITY_NORMAL) *wf = (struct pwm_waveform){ .period_length_ns = state->period, .duty_length_ns = state->duty_cycle, .duty_offset_ns = 0, }; else *wf = (struct pwm_waveform){ .period_length_ns = state->period, .duty_length_ns = state->period - state->duty_cycle, .duty_offset_ns = state->duty_cycle, }; } else { *wf = (struct pwm_waveform){ .period_length_ns = 0, }; } } static int pwmwfcmp(const struct pwm_waveform *a, const struct pwm_waveform *b) { if (a->period_length_ns > b->period_length_ns) return 1; if (a->period_length_ns < b->period_length_ns) return -1; if (a->duty_length_ns > b->duty_length_ns) return 1; if (a->duty_length_ns < b->duty_length_ns) return -1; if (a->duty_offset_ns > b->duty_offset_ns) return 1; if (a->duty_offset_ns < b->duty_offset_ns) return -1; return 0; } static bool pwm_check_rounding(const struct pwm_waveform *wf, const struct pwm_waveform *wf_rounded) { if (!wf->period_length_ns) return true; if (wf->period_length_ns < wf_rounded->period_length_ns) return false; if (wf->duty_length_ns < wf_rounded->duty_length_ns) return false; if (wf->duty_offset_ns < wf_rounded->duty_offset_ns) return false; return true; } static int __pwm_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_waveform *wf, void *wfhw) { const struct pwm_ops *ops = chip->ops; int ret; ret = ops->round_waveform_tohw(chip, pwm, wf, wfhw); trace_pwm_round_waveform_tohw(pwm, wf, wfhw, ret); return ret; } static int __pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw, struct pwm_waveform *wf) { const struct pwm_ops *ops = chip->ops; int ret; ret = ops->round_waveform_fromhw(chip, pwm, wfhw, wf); trace_pwm_round_waveform_fromhw(pwm, wfhw, wf, ret); return ret; } static int __pwm_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *wfhw) { const struct pwm_ops *ops = chip->ops; int ret; ret = ops->read_waveform(chip, pwm, wfhw); trace_pwm_read_waveform(pwm, wfhw, ret); return ret; } static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw) { const struct pwm_ops *ops = chip->ops; int ret; ret = ops->write_waveform(chip, pwm, wfhw); trace_pwm_write_waveform(pwm, wfhw, ret); return ret; } /** * pwm_round_waveform_might_sleep - Query hardware capabilities * Cannot be used in atomic context. * @pwm: PWM device * @wf: waveform to round and output parameter * * Typically a given waveform cannot be implemented exactly by hardware, e.g. * because hardware only supports coarse period resolution or no duty_offset. * This function returns the actually implemented waveform if you pass @wf to * pwm_set_waveform_might_sleep() now. * * Note however that the world doesn't stop turning when you call it, so when * doing:: * * pwm_round_waveform_might_sleep(mypwm, &wf); * pwm_set_waveform_might_sleep(mypwm, &wf, true); * * the latter might fail, e.g. because an input clock changed its rate between * these two calls and the waveform determined by * pwm_round_waveform_might_sleep() cannot be implemented any more. * * Usually all values passed in @wf are rounded down to the nearest possible * value (in the order period_length_ns, duty_length_ns and then * duty_offset_ns). Only if this isn't possible, a value might grow. See the * documentation for pwm_set_waveform_might_sleep() for a more formal * description. * * Returns: 0 on success, 1 if at least one value had to be rounded up or a * negative errno. * Context: May sleep. */ int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; struct pwm_waveform wf_req = *wf; char wfhw[PWM_WFHWSIZE]; int ret_tohw, ret_fromhw; BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip)) return -EOPNOTSUPP; if (!pwm_wf_valid(wf)) return -EINVAL; guard(pwmchip)(chip); if (!chip->operational) return -ENODEV; ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, wfhw); if (ret_tohw < 0) return ret_tohw; if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw > 1) dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_tohw: requested %llu/%llu [+%llu], return value %d\n", wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw); ret_fromhw = __pwm_round_waveform_fromhw(chip, pwm, wfhw, wf); if (ret_fromhw < 0) return ret_fromhw; if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_fromhw > 0) dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_fromhw: requested %llu/%llu [+%llu], return value %d\n", wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_fromhw); if (IS_ENABLED(CONFIG_PWM_DEBUG) && (ret_tohw == 0) != pwm_check_rounding(&wf_req, wf)) dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n", wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, ret_tohw); return ret_tohw; } EXPORT_SYMBOL_GPL(pwm_round_waveform_might_sleep); /** * pwm_get_waveform_might_sleep - Query hardware about current configuration * Cannot be used in atomic context. * @pwm: PWM device * @wf: output parameter * * Stores the current configuration of the PWM in @wf. Note this is the * equivalent of pwm_get_state_hw() (and not pwm_get_state()) for pwm_waveform. * * Returns: 0 on success or a negative errno * Context: May sleep. */ int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; char wfhw[PWM_WFHWSIZE]; int err; BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip) || !ops->read_waveform) return -EOPNOTSUPP; guard(pwmchip)(chip); if (!chip->operational) return -ENODEV; err = __pwm_read_waveform(chip, pwm, &wfhw); if (err) return err; return __pwm_round_waveform_fromhw(chip, pwm, &wfhw, wf); } EXPORT_SYMBOL_GPL(pwm_get_waveform_might_sleep); /* Called with the pwmchip lock held */ static int __pwm_set_waveform(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact) { struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; char wfhw[PWM_WFHWSIZE]; struct pwm_waveform wf_rounded; int err, ret_tohw; BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip)) return -EOPNOTSUPP; if (!pwm_wf_valid(wf)) return -EINVAL; ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw); if (ret_tohw < 0) return ret_tohw; if ((IS_ENABLED(CONFIG_PWM_DEBUG) || exact) && wf->period_length_ns) { err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded); if (err) return err; if (IS_ENABLED(CONFIG_PWM_DEBUG) && (ret_tohw == 0) != pwm_check_rounding(wf, &wf_rounded)) dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n", wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, ret_tohw); if (exact && pwmwfcmp(wf, &wf_rounded)) { dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\n", wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns); return 1; } } err = __pwm_write_waveform(chip, pwm, &wfhw); if (err) return err; /* update .state */ pwm_wf2state(wf, &pwm->state); if (IS_ENABLED(CONFIG_PWM_DEBUG) && ops->read_waveform && wf->period_length_ns) { struct pwm_waveform wf_set; err = __pwm_read_waveform(chip, pwm, &wfhw); if (err) /* maybe ignore? */ return err; err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_set); if (err) /* maybe ignore? */ return err; if (pwmwfcmp(&wf_set, &wf_rounded) != 0) dev_err(&chip->dev, "Unexpected setting: requested %llu/%llu [+%llu], expected %llu/%llu [+%llu], set %llu/%llu [+%llu]\n", wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, wf_set.duty_length_ns, wf_set.period_length_ns, wf_set.duty_offset_ns); } return ret_tohw; } /** * pwm_set_waveform_might_sleep - Apply a new waveform * Cannot be used in atomic context. * @pwm: PWM device * @wf: The waveform to apply * @exact: If true no rounding is allowed * * Typically a requested waveform cannot be implemented exactly, e.g. because * you requested .period_length_ns = 100 ns, but the hardware can only set * periods that are a multiple of 8.5 ns. With that hardware passing @exact = * true results in pwm_set_waveform_might_sleep() failing and returning -EDOM. * If @exact = false you get a period of 93.5 ns (i.e. the biggest period not * bigger than the requested value). * Note that even with @exact = true, some rounding by less than 1 ns is * possible/needed. In the above example requesting .period_length_ns = 94 and * @exact = true, you get the hardware configured with period = 93.5 ns. * * Let C be the set of possible hardware configurations for a given PWM device, * consisting of tuples (p, d, o) where p is the period length, d is the duty * length and o the duty offset. * * The following algorithm is implemented to pick the hardware setting * (p, d, o) ∈ C for a given request (p', d', o') with @exact = false:: * * p = max( { ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C ∧ ṗ ≤ p' } ∪ { min({ ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C }) }) * d = max( { ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C ∧ ḋ ≤ d' } ∪ { min({ ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C }) }) * o = max( { ȯ | (p, d, ȯ) ∈ C ∧ ȯ ≤ o' } ∪ { min({ ȯ | (p, d, ȯ) ∈ C }) }) * * In words: The chosen period length is the maximal possible period length not * bigger than the requested period length and if that doesn't exist, the * minimal period length. The chosen duty length is the maximal possible duty * length that is compatible with the chosen period length and isn't bigger than * the requested duty length. Again if such a value doesn't exist, the minimal * duty length compatible with the chosen period is picked. After that the duty * offset compatible with the chosen period and duty length is chosen in the * same way. * * Returns: 0 on success, -EDOM if setting failed due to the exact waveform not * being possible (if @exact), or a different negative errno on failure. * Context: May sleep. */ int pwm_set_waveform_might_sleep(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact) { struct pwm_chip *chip = pwm->chip; int err; might_sleep(); guard(pwmchip)(chip); if (!chip->operational) return -ENODEV; if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) { /* * Catch any drivers that have been marked as atomic but * that will sleep anyway. */ non_block_start(); err = __pwm_set_waveform(pwm, wf, exact); non_block_end(); } else { err = __pwm_set_waveform(pwm, wf, exact); } /* * map err == 1 to -EDOM for exact requests and 0 for !exact ones. Also * make sure that -EDOM is only returned in exactly that case. Note that * __pwm_set_waveform() should never return -EDOM which justifies the * unlikely(). */ if (unlikely(err == -EDOM)) err = -EINVAL; else if (exact && err == 1) err = -EDOM; else if (err == 1) err = 0; return err; } EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep); static void pwm_apply_debug(struct pwm_device *pwm, const struct pwm_state *state) { struct pwm_state *last = &pwm->last; struct pwm_chip *chip = pwm->chip; struct pwm_state s1 = { 0 }, s2 = { 0 }; int err; if (!IS_ENABLED(CONFIG_PWM_DEBUG)) return; /* No reasonable diagnosis possible without .get_state() */ if (!chip->ops->get_state) return; /* * If a disabled PWM was requested the result is unspecified, so nothing