From 76047c0312414915035f4ebbaa533fe3817a21e0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 3 Jun 2024 07:08:49 +0200 Subject: Adding upstream version 6.8.12. Signed-off-by: Daniel Baumann --- drivers/pwm/core.c | 852 ++++++++++++++++++++++++++--------------------------- 1 file changed, 416 insertions(+), 436 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index f2728ee787..9d2dc5e1c8 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -24,277 +24,358 @@ #define CREATE_TRACE_POINTS #include -static DEFINE_MUTEX(pwm_lookup_lock); -static LIST_HEAD(pwm_lookup_list); - /* protects access to pwm_chips */ static DEFINE_MUTEX(pwm_lock); static DEFINE_IDR(pwm_chips); -static struct pwm_chip *pwmchip_find_by_name(const char *name) +static void pwm_apply_debug(struct pwm_device *pwm, + const struct pwm_state *state) { - struct pwm_chip *chip; - unsigned long id, tmp; - - if (!name) - return NULL; - - mutex_lock(&pwm_lock); + struct pwm_state *last = &pwm->last; + struct pwm_chip *chip = pwm->chip; + struct pwm_state s1 = { 0 }, s2 = { 0 }; + int err; - idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) { - const char *chip_name = dev_name(chip->dev); + if (!IS_ENABLED(CONFIG_PWM_DEBUG)) + return; - if (chip_name && strcmp(chip_name, name) == 0) { - mutex_unlock(&pwm_lock); - return chip; - } - } + /* No reasonable diagnosis possible without .get_state() */ + if (!chip->ops->get_state) + return; - mutex_unlock(&pwm_lock); + /* + * *state was just applied. Read out the hardware state and do some + * checks. + */ - return NULL; -} + err = chip->ops->get_state(chip, pwm, &s1); + trace_pwm_get(pwm, &s1, err); + if (err) + /* If that failed there isn't much to debug */ + return; -static int pwm_device_request(struct pwm_device *pwm, const char *label) -{ - int err; - struct pwm_chip *chip = pwm->chip; - const struct pwm_ops *ops = chip->ops; + /* + * The lowlevel driver either ignored .polarity (which is a bug) or as + * best effort inverted .polarity and fixed .duty_cycle respectively. + * Undo this inversion and fixup for further tests. + */ + if (s1.enabled && s1.polarity != state->polarity) { + s2.polarity = state->polarity; + s2.duty_cycle = s1.period - s1.duty_cycle; + s2.period = s1.period; + s2.enabled = s1.enabled; + } else { + s2 = s1; + } - if (test_bit(PWMF_REQUESTED, &pwm->flags)) - return -EBUSY; + if (s2.polarity != state->polarity && + state->duty_cycle < state->period) + dev_warn(pwmchip_parent(chip), ".apply ignored .polarity\n"); - if (!try_module_get(chip->owner)) - return -ENODEV; + if (state->enabled && + last->polarity == state->polarity && + last->period > s2.period && + last->period <= state->period) + dev_warn(pwmchip_parent(chip), + ".apply didn't pick the best available period (requested: %llu, applied: %llu, possible: %llu)\n", + state->period, s2.period, last->period); - if (ops->request) { - err = ops->request(chip, pwm); - if (err) { - module_put(chip->owner); - return err; - } - } + if (state->enabled && state->period < s2.period) + dev_warn(pwmchip_parent(chip), + ".apply is supposed to round down period (requested: %llu, applied: %llu)\n", + state->period, s2.period); - if (ops->get_state) { - /* - * Zero-initialize state because most drivers are unaware of - * .usage_power. The other members of state are supposed to be - * set by lowlevel drivers. We still initialize the whole - * structure for simplicity even though this might paper over - * faulty implementations of .get_state(). - */ - struct pwm_state state = { 0, }; + if (state->enabled && + last->polarity == state->polarity && + last->period == s2.period && + last->duty_cycle > s2.duty_cycle && + last->duty_cycle <= state->duty_cycle) + dev_warn(pwmchip_parent(chip), + ".apply didn't pick the best available duty cycle (requested: %llu/%llu, applied: %llu/%llu, possible: %llu/%llu)\n", + state->duty_cycle, state->period, + s2.duty_cycle, s2.period, + last->duty_cycle, last->period); - err = ops->get_state(chip, pwm, &state); - trace_pwm_get(pwm, &state, err); + if (state->enabled && state->duty_cycle < s2.duty_cycle) + dev_warn(pwmchip_parent(chip), + ".apply is supposed to round down duty_cycle (requested: %llu/%llu, applied: %llu/%llu)\n", + state->duty_cycle, state->period, + s2.duty_cycle, s2.period); - if (!err) - pwm->state = state; + if (!state->enabled && s2.enabled && s2.duty_cycle > 0) + dev_warn(pwmchip_parent(chip), + "requested disabled, but yielded enabled with duty > 0\n"); - if (IS_ENABLED(CONFIG_PWM_DEBUG)) - pwm->last = pwm->state; + /* reapply the state that the driver reported being configured. */ + err = chip->ops->apply(chip, pwm, &s1); + trace_pwm_apply(pwm, &s1, err); + if (err) { + *last = s1; + dev_err(pwmchip_parent(chip), "failed to reapply current setting\n"); + return; } - set_bit(PWMF_REQUESTED, &pwm->flags); - pwm->label = label; + *last = (struct pwm_state){ 0 }; + err = chip->ops->get_state(chip, pwm, last); + trace_pwm_get(pwm, last, err); + if (err) + return; - return 0; + /* reapplication of the current state should give an exact match */ + if (s1.enabled != last->enabled || + s1.polarity != last->polarity || + (s1.enabled && s1.period != last->period) || + (s1.enabled && s1.duty_cycle != last->duty_cycle)) { + dev_err(pwmchip_parent(chip), + ".apply is not idempotent (ena=%d pol=%d %llu/%llu) -> (ena=%d pol=%d %llu/%llu)\n", + s1.enabled, s1.polarity, s1.duty_cycle, s1.period, + last->enabled, last->polarity, last->duty_cycle, + last->period); + } } -struct pwm_device * -of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args) +/** + * __pwm_apply() - atomically apply a new state to a PWM device + * @pwm: PWM device + * @state: new state to apply + */ +static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) { - struct pwm_device *pwm; + struct pwm_chip *chip; + int err; - if (chip->of_pwm_n_cells < 2) - return ERR_PTR(-EINVAL); + if (!pwm || !state || !state->period || + state->duty_cycle > state->period) + return -EINVAL; - /* flags in the third cell are optional */ - if (args->args_count < 2) - return ERR_PTR(-EINVAL); + chip = pwm->chip; - if (args->args[0] >= chip->npwm) - return ERR_PTR(-EINVAL); + if (state->period == pwm->state.period && + state->duty_cycle == pwm->state.duty_cycle && + state->polarity == pwm->state.polarity && + state->enabled == pwm->state.enabled && + state->usage_power == pwm->state.usage_power) + return 0; - pwm = pwm_request_from_chip(chip, args->args[0], NULL); - if (IS_ERR(pwm)) - return pwm; + err = chip->ops->apply(chip, pwm, state); + trace_pwm_apply(pwm, state, err); + if (err) + return err; - pwm->args.period = args->args[1]; - pwm->args.polarity = PWM_POLARITY_NORMAL; + pwm->state = *state; - if (chip->of_pwm_n_cells >= 3) { - if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) - pwm->args.polarity = PWM_POLARITY_INVERSED; - } + /* + * only do this after pwm->state was applied as some + * implementations of .get_state depend on this + */ + pwm_apply_debug(pwm, state); - return pwm; + return 0; } -EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); -struct pwm_device * -of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) +/** + * pwm_apply_might_sleep() - atomically apply a new state to a PWM device + * Cannot be used in atomic context. + * @pwm: PWM device + * @state: new state to apply + */ +int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) { - struct pwm_device *pwm; - - if (chip->of_pwm_n_cells < 1) - return ERR_PTR(-EINVAL); + int err; - /* validate that one cell is specified, optionally with flags */ - if (args->args_count != 1 && args->args_count != 2) - return ERR_PTR(-EINVAL); + /* + * Some lowlevel driver's implementations of .apply() make use of + * mutexes, also with some drivers only returning when the new + * configuration is active calling pwm_apply_might_sleep() from atomic context + * is a bad idea. So make it explicit that calling this function might + * sleep. + */ + might_sleep(); - pwm = pwm_request_from_chip(chip, 0, NULL); - if (IS_ERR(pwm)) - return pwm; + if (IS_ENABLED(CONFIG_PWM_DEBUG) && pwm->chip->atomic) { + /* + * Catch any drivers that have been marked as atomic but + * that will sleep anyway. + */ + non_block_start(); + err = __pwm_apply(pwm, state); + non_block_end(); + } else { + err = __pwm_apply(pwm, state); + } - pwm->args.period = args->args[0]; - pwm->args.polarity = PWM_POLARITY_NORMAL; + return err; +} +EXPORT_SYMBOL_GPL(pwm_apply_might_sleep); - if (args->args_count == 2 && args->args[1] & PWM_POLARITY_INVERTED) - pwm->args.polarity = PWM_POLARITY_INVERSED; +/** + * pwm_apply_atomic() - apply a new state to a PWM device from atomic context + * Not all PWM devices support this function, check with pwm_might_sleep(). + * @pwm: PWM device + * @state: new state to apply + */ +int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state) +{ + WARN_ONCE(!pwm->chip->atomic, + "sleeping PWM driver used in atomic context\n"); - return pwm; + return __pwm_apply(pwm, state); } -EXPORT_SYMBOL_GPL(of_pwm_single_xlate); +EXPORT_SYMBOL_GPL(pwm_apply_atomic); -static void of_pwmchip_add(struct pwm_chip *chip) +/** + * pwm_adjust_config() - adjust the current PWM config to the PWM arguments + * @pwm: PWM device + * + * This function will adjust the PWM config to the PWM arguments provided + * by the DT or PWM lookup table. This is particularly useful to adapt + * the bootloader config to the Linux one. + */ +int pwm_adjust_config(struct pwm_device *pwm) { - if (!chip->dev || !chip->dev->of_node) - return; + struct pwm_state state; + struct pwm_args pargs; - if (!chip->of_xlate) { - u32 pwm_cells; + pwm_get_args(pwm, &pargs); + pwm_get_state(pwm, &state); - if (of_property_read_u32(chip->dev->of_node, "#pwm-cells", - &pwm_cells)) - pwm_cells = 2; + /* + * If the current period is zero it means that either the PWM driver + * does not support initial state retrieval or the PWM has not yet + * been configured. + * + * In either case, we setup the new period and polarity, and assign a + * duty cycle of 0. + */ + if (!state.period) { + state.duty_cycle = 0; + state.period = pargs.period; + state.polarity = pargs.polarity; - chip->of_xlate = of_pwm_xlate_with_flags; - chip->of_pwm_n_cells = pwm_cells; + return pwm_apply_might_sleep(pwm, &state); } - of_node_get(chip->dev->of_node); -} - -static void of_pwmchip_remove(struct pwm_chip *chip) -{ - if (chip->dev) - of_node_put(chip->dev->of_node); -} - -static bool pwm_ops_check(const struct pwm_chip *chip) -{ - const struct pwm_ops *ops = chip->ops; - - if (!ops->apply) - return false; + /* + * Adjust the PWM duty cycle/period based on the period value provided + * in PWM args. + */ + if (pargs.period != state.period) { + u64 dutycycle = (u64)state.duty_cycle * pargs.period; - if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state) - dev_warn(chip->dev, - "Please implement the .get_state() callback\n"); + do_div(dutycycle, state.period); + state.duty_cycle = dutycycle; + state.period = pargs.period; + } - return true; + /* + * If the polarity changed, we should also change the duty cycle. + */ + if (pargs.polarity != state.polarity) { + state.polarity = pargs.polarity; + state.duty_cycle = state.period - state.duty_cycle; + } + + return pwm_apply_might_sleep(pwm, &state); } +EXPORT_SYMBOL_GPL(pwm_adjust_config); /** - * __pwmchip_add() - register a new PWM chip - * @chip: the PWM chip to add - * @owner: reference to the module providing the chip. - * - * Register a new PWM chip. @owner is supposed to be THIS_MODULE, use the - * pwmchip_add wrapper to do this right. + * pwm_capture() - capture and report a PWM signal + * @pwm: PWM device + * @result: structure to fill with capture result + * @timeout: time to wait, in milliseconds, before giving up on capture * * Returns: 0 on success or a negative error code on failure. */ -int __pwmchip_add(struct pwm_chip *chip, struct module *owner) +int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result, + unsigned long timeout) { - unsigned int i; - int ret; + int err; - if (!chip || !chip->dev || !chip->ops || !chip->npwm) + if (!pwm || !pwm->chip->ops) return -EINVAL; - if (!pwm_ops_check(chip)) - return -EINVAL; + if (!pwm->chip->ops->capture) + return -ENOSYS; - chip->owner = owner; + mutex_lock(&pwm_lock); + err = pwm->chip->ops->capture(pwm->chip, pwm, result, timeout); + mutex_unlock(&pwm_lock); - chip->pwms = kcalloc(chip->npwm, sizeof(*chip->pwms), GFP_KERNEL); - if (!chip->pwms) - return -ENOMEM; + return err; +} +EXPORT_SYMBOL_GPL(pwm_capture); - mutex_lock(&pwm_lock); +static struct pwm_chip *pwmchip_find_by_name(const char *name) +{ + struct pwm_chip *chip; + unsigned long id, tmp; - ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL); - if (ret < 0) { - mutex_unlock(&pwm_lock); - kfree(chip->pwms); - return ret; - } + if (!name) + return NULL; - chip->id = ret; + mutex_lock(&pwm_lock); - for (i = 0; i < chip->npwm; i++) { - struct pwm_device *pwm = &chip->pwms[i]; + idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) { + const char *chip_name = dev_name(pwmchip_parent(chip)); - pwm->chip = chip; - pwm->hwpwm = i; + if (chip_name && strcmp(chip_name, name) == 0) { + mutex_unlock(&pwm_lock); + return chip; + } } mutex_unlock(&pwm_lock); - if (IS_ENABLED(CONFIG_OF)) - of_pwmchip_add(chip); - - pwmchip_sysfs_export(chip); - - return 0; + return NULL; } -EXPORT_SYMBOL_GPL(__pwmchip_add); -/** - * pwmchip_remove() - remove a PWM chip - * @chip: the PWM chip to remove - * - * Removes a PWM chip. - */ -void pwmchip_remove(struct pwm_chip *chip) +static int pwm_device_request(struct pwm_device *pwm, const char *label) { - pwmchip_sysfs_unexport(chip); - - if (IS_ENABLED(CONFIG_OF)) - of_pwmchip_remove(chip); + int err; + struct pwm_chip *chip = pwm->chip; + const struct pwm_ops *ops = chip->ops; - mutex_lock(&pwm_lock); + if (test_bit(PWMF_REQUESTED, &pwm->flags)) + return -EBUSY; - idr_remove(&pwm_chips, chip->id); + if (!try_module_get(chip->owner)) + return -ENODEV; - mutex_unlock(&pwm_lock); + if (ops->request) { + err = ops->request(chip, pwm); + if (err) { + module_put(chip->owner); + return err; + } + } - kfree(chip->pwms); -} -EXPORT_SYMBOL_GPL(pwmchip_remove); + if (ops->get_state) { + /* + * Zero-initialize state because most drivers are unaware of + * .usage_power. The other members of state are supposed to be + * set by lowlevel drivers. We still initialize the whole + * structure for simplicity even though this might paper over + * faulty implementations of .get_state(). + */ + struct pwm_state state = { 0, }; -static void devm_pwmchip_remove(void *data) -{ - struct pwm_chip *chip = data; + err = ops->get_state(chip, pwm, &state); + trace_pwm_get(pwm, &state, err); - pwmchip_remove(chip); -} + if (!err) + pwm->state = state; -int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner) -{ - int ret; + if (IS_ENABLED(CONFIG_PWM_DEBUG)) + pwm->last = pwm->state; + } - ret = __pwmchip_add(chip, owner); - if (ret) - return ret; + set_bit(PWMF_REQUESTED, &pwm->flags); + pwm->label = label; - return devm_add_action_or_reset(dev, devm_pwmchip_remove, chip); + return 0; } -EXPORT_SYMBOL_GPL(__devm_pwmchip_add); /** * pwm_request_from_chip() - request a PWM device relative to a PWM chip @@ -328,301 +409,179 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, } EXPORT_SYMBOL_GPL(pwm_request_from_chip); -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; - - /* - * *state was just applied. Read out the hardware state and do some - * checks. - */ - - err = chip->ops->get_state(chip, pwm, &s1); - trace_pwm_get(pwm, &s1, err); - if (err) - /* If that failed there isn't much to debug */ - return; - - /* - * The lowlevel driver either ignored .polarity (which is a bug) or as - * best effort inverted .polarity and fixed .duty_cycle respectively. - * Undo this inversion and fixup for further tests. - */ - if (s1.enabled && s1.polarity != state->polarity) { - s2.polarity = state->polarity; - s2.duty_cycle = s1.period - s1.duty_cycle; - s2.period = s1.period; - s2.enabled = s1.enabled; - } else { - s2 = s1; - } - - if (s2.polarity != state->polarity && - state->duty_cycle < state->period) - dev_warn(chip->dev, ".apply ignored .polarity\n"); - - if (state->enabled && - last->polarity == state->polarity && - last->period > s2.period && - last->period <= state->period) - dev_warn(chip->dev, - ".apply didn't pick the best available period (requested: %llu, applied: %llu, possible: %llu)\n", - state->period, s2.period, last->period); - - if (state->enabled && state->period < s2.period) - dev_warn(chip->dev, - ".apply is supposed to round down period (requested: %llu, applied: %llu)\n", - state->period, s2.period); - - if (state->enabled && - last->polarity == state->polarity && - last->period == s2.period && - last->duty_cycle > s2.duty_cycle && - last->duty_cycle <= state->duty_cycle) - dev_warn(chip->dev, - ".apply didn't pick the best available duty cycle (requested: %llu/%llu, applied: %llu/%llu, possible: %llu/%llu)\n", - state->duty_cycle, state->period, - s2.duty_cycle, s2.period, - last->duty_cycle, last->period); - - if (state->enabled && state->duty_cycle < s2.duty_cycle) - dev_warn(chip->dev, - ".apply is supposed to round down duty_cycle (requested: %llu/%llu, applied: %llu/%llu)\n", - state->duty_cycle, state->period, - s2.duty_cycle, s2.period); - - if (!state->enabled && s2.enabled && s2.duty_cycle > 0) - dev_warn(chip->dev, - "requested disabled, but yielded enabled with duty > 0\n"); - - /* reapply the state that the driver reported being configured. */ - err = chip->ops->apply(chip, pwm, &s1); - trace_pwm_apply(pwm, &s1, err); - if (err) { - *last = s1; - dev_err(chip->dev, "failed to reapply current setting\n"); - return; - } - - *last = (struct pwm_state){ 0 }; - err = chip->ops->get_state(chip, pwm, last); - trace_pwm_get(pwm, last, err); - if (err) - return; - - /* reapplication of the current state should give an exact match */ - if (s1.enabled != last->enabled || - s1.polarity != last->polarity || - (s1.enabled && s1.period != last->period) || - (s1.enabled && s1.duty_cycle != last->duty_cycle)) { - dev_err(chip->dev, - ".apply is not idempotent (ena=%d pol=%d %llu/%llu) -> (ena=%d pol=%d %llu/%llu)\n", - s1.enabled, s1.polarity, s1.duty_cycle, s1.period, - last->enabled, last->polarity, last->duty_cycle, - last->period); - } -} -/** - * __pwm_apply() - atomically apply a new state to a PWM device - * @pwm: PWM device - * @state: new state to apply - */ -static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) +struct pwm_device * +of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args) { - struct pwm_chip *chip; - int err; + struct pwm_device *pwm; - if (!pwm || !state || !state->period || - state->duty_cycle > state->period) - return -EINVAL; + /* period in the second cell and flags in the third cell are optional */ + if (args->args_count < 1) + return ERR_PTR(-EINVAL); - chip = pwm->chip; + pwm = pwm_request_from_chip(chip, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; - if (state->period == pwm->state.period && - state->duty_cycle == pwm->state.duty_cycle && - state->polarity == pwm->state.polarity && - state->enabled == pwm->state.enabled && - state->usage_power == pwm->state.usage_power) - return 0; + if (args->args_count > 1) + pwm->args.period = args->args[1]; - err = chip->ops->apply(chip, pwm, state); - trace_pwm_apply(pwm, state, err); - if (err) - return err; + pwm->args.polarity = PWM_POLARITY_NORMAL; + if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) + pwm->args.polarity = PWM_POLARITY_INVERSED; - pwm->state = *state; + return pwm; +} +EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); - /* - * only do this after pwm->state was applied as some - * implementations of .get_state depend on this - */ - pwm_apply_debug(pwm, state); +struct pwm_device * +of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) +{ + struct pwm_device *pwm; - return 0; + pwm = pwm_request_from_chip(chip, 0, NULL); + if (IS_ERR(pwm)) + return pwm; + + if (args->args_count > 0) + pwm->args.period = args->args[0]; + + pwm->args.polarity = PWM_POLARITY_NORMAL; + if (args->args_count > 1 && args->args[1] & PWM_POLARITY_INVERTED) + pwm->args.polarity = PWM_POLARITY_INVERSED; + + return pwm; } +EXPORT_SYMBOL_GPL(of_pwm_single_xlate); -/** - * pwm_apply_might_sleep() - atomically apply a new state to a PWM device - * Cannot be used in atomic context. - * @pwm: PWM device - * @state: new state to apply - */ -int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) +static void of_pwmchip_add(struct pwm_chip *chip) { - int err; + if (!pwmchip_parent(chip) || !pwmchip_parent(chip)->of_node) + return; - /* - * Some lowlevel driver's implementations of .apply() make use of - * mutexes, also with some drivers only returning when the new - * configuration is active calling pwm_apply_might_sleep() from atomic context - * is a bad idea. So make it explicit that calling this function might - * sleep. - */ - might_sleep(); + if (!chip->of_xlate) + chip->of_xlate = of_pwm_xlate_with_flags; - if (IS_ENABLED(CONFIG_PWM_DEBUG) && pwm->chip->atomic) { - /* - * Catch any drivers that have been marked as atomic but - * that will sleep anyway. - */ - non_block_start(); - err = __pwm_apply(pwm, state); - non_block_end(); - } else { - err = __pwm_apply(pwm, state); - } + of_node_get(pwmchip_parent(chip)->of_node); +} - return err; +static void of_pwmchip_remove(struct pwm_chip *chip) +{ + if (pwmchip_parent(chip)) + of_node_put(pwmchip_parent(chip)->of_node); } -EXPORT_SYMBOL_GPL(pwm_apply_might_sleep); -/** - * pwm_apply_atomic() - apply a new state to a PWM device from atomic context - * Not all PWM devices support this function, check with pwm_might_sleep(). - * @pwm: PWM device - * @state: new state to apply - */ -int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state) +static bool pwm_ops_check(const struct pwm_chip *chip) { - WARN_ONCE(!pwm->chip->atomic, - "sleeping PWM driver used in atomic context\n"); + const struct pwm_ops *ops = chip->ops; - return __pwm_apply(pwm, state); + if (!ops->apply) + return false; + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state) + dev_warn(pwmchip_parent(chip), + "Please implement the .get_state() callback\n"); + + return true; } -EXPORT_SYMBOL_GPL(pwm_apply_atomic); /** - * pwm_capture() - capture and report a PWM signal - * @pwm: PWM device - * @result: structure to fill with capture result - * @timeout: time to wait, in milliseconds, before giving up on capture + * __pwmchip_add() - register a new PWM chip + * @chip: the PWM chip to add + * @owner: reference to the module providing the chip. + * + * Register a new PWM chip. @owner is supposed to be THIS_MODULE, use the + * pwmchip_add wrapper to do this right. * * Returns: 0 on success or a negative error code on failure. */ -int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result, - unsigned long timeout) +int __pwmchip_add(struct pwm_chip *chip, struct module *owner) { - int err; + unsigned int i; + int ret; - if (!pwm || !pwm->chip->ops) + if (!chip || !pwmchip_parent(chip) || !chip->ops || !chip->npwm) return -EINVAL; - if (!pwm->chip->ops->capture) - return -ENOSYS; + if (!pwm_ops_check(chip)) + return -EINVAL; + + chip->owner = owner; + + chip->pwms = kcalloc(chip->npwm, sizeof(*chip->pwms), GFP_KERNEL); + if (!chip->pwms) + return -ENOMEM; mutex_lock(&pwm_lock); - err = pwm->chip->ops->capture(pwm->chip, pwm, result, timeout); + + ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL); + if (ret < 0) { + mutex_unlock(&pwm_lock); + kfree(chip->pwms); + return ret; + } + + chip->id = ret; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + pwm->chip = chip; + pwm->hwpwm = i; + } + mutex_unlock(&pwm_lock); - return err; + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_add(chip); + + pwmchip_sysfs_export(chip); + + return 0; } -EXPORT_SYMBOL_GPL(pwm_capture); +EXPORT_SYMBOL_GPL(__pwmchip_add); /** - * pwm_adjust_config() - adjust the current PWM config to the PWM arguments - * @pwm: PWM device + * pwmchip_remove() - remove a PWM chip + * @chip: the PWM chip to remove * - * This function will adjust the PWM config to the PWM arguments provided - * by the DT or PWM lookup table. This is particularly useful to adapt - * the bootloader config to the Linux one. + * Removes a PWM chip. */ -int pwm_adjust_config(struct pwm_device *pwm) +void pwmchip_remove(struct pwm_chip *chip) { - struct pwm_state state; - struct pwm_args pargs; - - pwm_get_args(pwm, &pargs); - pwm_get_state(pwm, &state); - - /* - * If the current period is zero it means that either the PWM driver - * does not support initial state retrieval or the PWM has not yet - * been configured. - * - * In either case, we setup the new period and polarity, and assign a - * duty cycle of 0. - */ - if (!state.period) { - state.duty_cycle = 0; - state.period = pargs.period; - state.polarity = pargs.polarity; + pwmchip_sysfs_unexport(chip); - return pwm_apply_might_sleep(pwm, &state); - } + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_remove(chip); - /* - * Adjust the PWM duty cycle/period based on the period value provided - * in PWM args. - */ - if (pargs.period != state.period) { - u64 dutycycle = (u64)state.duty_cycle * pargs.period; + mutex_lock(&pwm_lock); - do_div(dutycycle, state.period); - state.duty_cycle = dutycycle; - state.period = pargs.period; - } + idr_remove(&pwm_chips, chip->id); - /* - * If the polarity changed, we should also change the duty cycle. - */ - if (pargs.polarity != state.polarity) { - state.polarity = pargs.polarity; - state.duty_cycle = state.period - state.duty_cycle; - } + mutex_unlock(&pwm_lock); - return pwm_apply_might_sleep(pwm, &state); + kfree(chip->pwms); } -EXPORT_SYMBOL_GPL(pwm_adjust_config); +EXPORT_SYMBOL_GPL(pwmchip_remove); -static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode) +static void devm_pwmchip_remove(void *data) { - struct pwm_chip *chip; - unsigned long id, tmp; + struct pwm_chip *chip = data; - mutex_lock(&pwm_lock); + pwmchip_remove(chip); +} - idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) - if (chip->dev && device_match_fwnode(chip->dev, fwnode)) { - mutex_unlock(&pwm_lock); - return chip; - } +int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner) +{ + int ret; - mutex_unlock(&pwm_lock); + ret = __pwmchip_add(chip, owner); + if (ret) + return ret; - return ERR_PTR(-EPROBE_DEFER); + return devm_add_action_or_reset(dev, devm_pwmchip_remove, chip); } +EXPORT_SYMBOL_GPL(__devm_pwmchip_add); static struct device_link *pwm_device_link_add(struct device *dev, struct pwm_device *pwm) @@ -635,21 +594,39 @@ static struct device_link *pwm_device_link_add(struct device *dev, * impact the PM sequence ordering: the PWM supplier may get * suspended before the consumer. */ - dev_warn(pwm->chip->dev, + dev_warn(pwmchip_parent(pwm->chip), "No consumer device specified to create a link to\n"); return NULL; } - dl = device_link_add(dev, pwm->chip->dev, DL_FLAG_AUTOREMOVE_CONSUMER); + dl = device_link_add(dev, pwmchip_parent(pwm->chip), DL_FLAG_AUTOREMOVE_CONSUMER); if (!dl) { dev_err(dev, "failed to create device link to %s\n", - dev_name(pwm->chip->dev)); + dev_name(pwmchip_parent(pwm->chip))); return ERR_PTR(-EINVAL); } return dl; } +static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode) +{ + struct pwm_chip *chip; + unsigned long id, tmp; + + mutex_lock(&pwm_lock); + + idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) + if (pwmchip_parent(chip) && device_match_fwnode(pwmchip_parent(chip), fwnode)) { + mutex_unlock(&pwm_lock); + return chip; + } + + mutex_unlock(&pwm_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + /** * of_pwm_get() - request a PWM via the PWM framework * @dev: device for PWM consumer @@ -784,6 +761,9 @@ static struct pwm_device *acpi_pwm_get(const struct fwnode_handle *fwnode) return pwm; } +static DEFINE_MUTEX(pwm_lookup_lock); +static LIST_HEAD(pwm_lookup_list); + /** * pwm_add_table() - register PWM device consumers * @table: array of consumers to register @@ -1105,8 +1085,8 @@ static int pwm_seq_show(struct seq_file *s, void *v) seq_printf(s, "%s%d: %s/%s, %d PWM device%s\n", (char *)s->private, chip->id, - chip->dev->bus ? chip->dev->bus->name : "no-bus", - dev_name(chip->dev), chip->npwm, + pwmchip_parent(chip)->bus ? pwmchip_parent(chip)->bus->name : "no-bus", + dev_name(pwmchip_parent(chip)), chip->npwm, (chip->npwm != 1) ? "s" : ""); pwm_dbg_show(chip, s); -- cgit v1.2.3