forked from Minki/linux
Merge branch 'for-4.7/pwm-atomic' into for-next
This commit is contained in:
commit
18c588786c
@ -42,9 +42,26 @@ variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist.
|
||||
|
||||
After being requested, a PWM has to be configured using:
|
||||
|
||||
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
|
||||
int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
|
||||
|
||||
To start/stop toggling the PWM output use pwm_enable()/pwm_disable().
|
||||
This API controls both the PWM period/duty_cycle config and the
|
||||
enable/disable state.
|
||||
|
||||
The pwm_config(), pwm_enable() and pwm_disable() functions are just wrappers
|
||||
around pwm_apply_state() and should not be used if the user wants to change
|
||||
several parameter at once. For example, if you see pwm_config() and
|
||||
pwm_{enable,disable}() calls in the same function, this probably means you
|
||||
should switch to pwm_apply_state().
|
||||
|
||||
The PWM user API also allows one to query the PWM state with pwm_get_state().
|
||||
|
||||
In addition to the PWM state, the PWM API also exposes PWM arguments, which
|
||||
are the reference PWM config one should use on this PWM.
|
||||
PWM arguments are usually platform-specific and allows the PWM user to only
|
||||
care about dutycycle relatively to the full period (like, duty = 50% of the
|
||||
period). struct pwm_args contains 2 fields (period and polarity) and should
|
||||
be used to set the initial PWM config (usually done in the probe function
|
||||
of the PWM user). PWM arguments are retrieved with pwm_get_args().
|
||||
|
||||
Using PWMs with the sysfs interface
|
||||
-----------------------------------
|
||||
@ -105,6 +122,15 @@ goes low for the remainder of the period. Conversely, a signal with inversed
|
||||
polarity starts low for the duration of the duty cycle and goes high for the
|
||||
remainder of the period.
|
||||
|
||||
Drivers are encouraged to implement ->apply() instead of the legacy
|
||||
->enable(), ->disable() and ->config() methods. Doing that should provide
|
||||
atomicity in the PWM config workflow, which is required when the PWM controls
|
||||
a critical device (like a regulator).
|
||||
|
||||
The implementation of ->get_state() (a method used to retrieve initial PWM
|
||||
state) is also encouraged for the same reason: letting the PWM user know
|
||||
about the current PWM state would allow him to avoid glitches.
|
||||
|
||||
Locking
|
||||
-------
|
||||
|
||||
|
@ -496,6 +496,12 @@ static int rx1950_backlight_init(struct device *dev)
|
||||
return PTR_ERR(lcd_pwm);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(lcd_pwm);
|
||||
|
||||
rx1950_lcd_power(1);
|
||||
rx1950_bl_power(1);
|
||||
|
||||
|
@ -59,6 +59,7 @@ static int clk_pwm_probe(struct platform_device *pdev)
|
||||
struct clk_init_data init;
|
||||
struct clk_pwm *clk_pwm;
|
||||
struct pwm_device *pwm;
|
||||
struct pwm_args pargs;
|
||||
const char *clk_name;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
@ -71,22 +72,28 @@ static int clk_pwm_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(pwm))
|
||||
return PTR_ERR(pwm);
|
||||
|
||||
if (!pwm->period) {
|
||||
pwm_get_args(pwm, &pargs);
|
||||
if (!pargs.period) {
|
||||
dev_err(&pdev->dev, "invalid PWM period\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate))
|
||||
clk_pwm->fixed_rate = NSEC_PER_SEC / pwm->period;
|
||||
clk_pwm->fixed_rate = NSEC_PER_SEC / pargs.period;
|
||||
|
||||
if (pwm->period != NSEC_PER_SEC / clk_pwm->fixed_rate &&
|
||||
pwm->period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
|
||||
if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate &&
|
||||
pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
|
||||
dev_err(&pdev->dev,
|
||||
"clock-frequency does not match PWM period\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = pwm_config(pwm, (pwm->period + 1) >> 1, pwm->period);
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to the
|
||||
* atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(pwm);
|
||||
ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -1640,6 +1640,12 @@ static int pwm_setup_backlight(struct intel_connector *connector,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(panel->backlight.pwm);
|
||||
|
||||
retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS,
|
||||
CRC_PMIC_PWM_PERIOD_NS);
|
||||
if (retval < 0) {
|
||||
|
@ -40,15 +40,18 @@ struct pwm_fan_ctx {
|
||||
|
||||
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
|
||||
{
|
||||
struct pwm_args pargs;
|
||||
unsigned long duty;
|
||||
int ret = 0;
|
||||
|
||||
pwm_get_args(ctx->pwm, &pargs);
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
if (ctx->pwm_value == pwm)
|
||||
goto exit_set_pwm_err;
|
||||
|
||||
duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM);
|
||||
ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
|
||||
duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM);
|
||||
ret = pwm_config(ctx->pwm, duty, pargs.period);
|
||||
if (ret)
|
||||
goto exit_set_pwm_err;
|
||||
|
||||
@ -215,6 +218,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct pwm_fan_ctx *ctx;
|
||||
struct pwm_args pargs;
|
||||
struct device *hwmon;
|
||||
int duty_cycle;
|
||||
int ret;
|
||||
@ -233,11 +237,19 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to the
|
||||
* atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(ctx->pwm);
|
||||
|
||||
/* Set duty cycle to maximum allowed */
|
||||
duty_cycle = ctx->pwm->period - 1;
|
||||
pwm_get_args(ctx->pwm, &pargs);
|
||||
|
||||
duty_cycle = pargs.period - 1;
|
||||
ctx->pwm_value = MAX_PWM;
|
||||
|
||||
ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period);
|
||||
ret = pwm_config(ctx->pwm, duty_cycle, pargs.period);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to configure PWM\n");
|
||||
return ret;
|
||||
@ -303,14 +315,16 @@ static int pwm_fan_suspend(struct device *dev)
|
||||
static int pwm_fan_resume(struct device *dev)
|
||||
{
|
||||
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||
struct pwm_args pargs;
|
||||
unsigned long duty;
|
||||
int ret;
|
||||
|
||||
if (ctx->pwm_value == 0)
|
||||
return 0;
|
||||
|
||||
duty = DIV_ROUND_UP(ctx->pwm_value * (ctx->pwm->period - 1), MAX_PWM);
|
||||
ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
|
||||
pwm_get_args(ctx->pwm, &pargs);
|
||||
duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
|
||||
ret = pwm_config(ctx->pwm, duty, pargs.period);
|
||||
if (ret)
|
||||
return ret;
|
||||
return pwm_enable(ctx->pwm);
|
||||
|
@ -70,10 +70,13 @@ struct max77693_haptic {
|
||||
|
||||
static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic)
|
||||
{
|
||||
int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
|
||||
struct pwm_args pargs;
|
||||
int delta;
|
||||
int error;
|
||||
|
||||
error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
|
||||
pwm_get_args(haptic->pwm_dev, &pargs);
|
||||
delta = (pargs.period + haptic->pwm_duty) / 2;
|
||||
error = pwm_config(haptic->pwm_dev, delta, pargs.period);
|
||||
if (error) {
|
||||
dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
|
||||
return error;
|
||||
@ -234,6 +237,7 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct max77693_haptic *haptic = input_get_drvdata(dev);
|
||||
struct pwm_args pargs;
|
||||
u64 period_mag_multi;
|
||||
|
||||
haptic->magnitude = effect->u.rumble.strong_magnitude;
|
||||
@ -245,7 +249,8 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
|
||||
* The formula to convert magnitude to pwm_duty as follows:
|
||||
* - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF)
|
||||
*/
|
||||
period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude;
|
||||
pwm_get_args(haptic->pwm_dev, &pargs);
|
||||
period_mag_multi = (u64)pargs.period * haptic->magnitude;
|
||||
haptic->pwm_duty = (unsigned int)(period_mag_multi >>
|
||||
MAX_MAGNITUDE_SHIFT);
|
||||
|
||||
@ -329,6 +334,12 @@ static int max77693_haptic_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(haptic->pwm_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to the
|
||||
* atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(haptic->pwm_dev);
|
||||
|
||||
haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic");
|
||||
if (IS_ERR(haptic->motor_reg)) {
|
||||
dev_err(&pdev->dev, "failed to get regulator\n");
|
||||
|
@ -304,6 +304,12 @@ static int max8997_haptic_probe(struct platform_device *pdev)
|
||||
error);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(chip->pwm);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -87,6 +87,12 @@ static int pwm_beeper_probe(struct platform_device *pdev)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(beeper->pwm);
|
||||
|
||||
beeper->input = input_allocate_device();
|
||||
if (!beeper->input) {
|
||||
dev_err(&pdev->dev, "Failed to allocate input device\n");
|
||||
|
@ -91,6 +91,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
|
||||
struct led_pwm *led, struct device_node *child)
|
||||
{
|
||||
struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
|
||||
struct pwm_args pargs;
|
||||
int ret;
|
||||
|
||||
led_data->active_low = led->active_low;
|
||||
@ -117,7 +118,15 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
|
||||
else
|
||||
led_data->cdev.brightness_set_blocking = led_pwm_set_blocking;
|
||||
|
||||
led_data->period = pwm_get_period(led_data->pwm);
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to the
|
||||
* atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(led_data->pwm);
|
||||
|
||||
pwm_get_args(led_data->pwm, &pargs);
|
||||
|
||||
led_data->period = pargs.period;
|
||||
if (!led_data->period && (led->pwm_period_ns > 0))
|
||||
led_data->period = led->pwm_period_ns;
|
||||
|
||||
|
@ -227,6 +227,19 @@ void *pwm_get_chip_data(struct pwm_device *pwm)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_get_chip_data);
|
||||
|
||||
static bool pwm_ops_check(const struct pwm_ops *ops)
|
||||
{
|
||||
/* driver supports legacy, non-atomic operation */
|
||||
if (ops->config && ops->enable && ops->disable)
|
||||
return true;
|
||||
|
||||
/* driver supports atomic operation */
|
||||
if (ops->apply)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* pwmchip_add_with_polarity() - register a new PWM chip
|
||||
* @chip: the PWM chip to add
|
||||
@ -245,8 +258,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
|
||||
!chip->ops->enable || !chip->ops->disable || !chip->npwm)
|
||||
if (!chip || !chip->dev || !chip->ops || !chip->npwm)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pwm_ops_check(chip->ops))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pwm_lock);
|
||||
@ -269,8 +284,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
|
||||
pwm->chip = chip;
|
||||
pwm->pwm = chip->base + i;
|
||||
pwm->hwpwm = i;
|
||||
pwm->polarity = polarity;
|
||||
mutex_init(&pwm->lock);
|
||||
pwm->state.polarity = polarity;
|
||||
|
||||
if (chip->ops->get_state)
|
||||
chip->ops->get_state(chip, pwm, &pwm->state);
|
||||
|
||||
radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
|
||||
}
|
||||
@ -430,107 +447,138 @@ void pwm_free(struct pwm_device *pwm)
|
||||
EXPORT_SYMBOL_GPL(pwm_free);
|
||||
|
||||
/**
|
||||
* pwm_config() - change a PWM device configuration
|
||||
* pwm_apply_state() - atomically apply a new state to a PWM device
|
||||
* @pwm: PWM device
|
||||
* @duty_ns: "on" time (in nanoseconds)
|
||||
* @period_ns: duration (in nanoseconds) of one cycle
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
* @state: new state to apply. This can be adjusted by the PWM driver
|
||||
* if the requested config is not achievable, for example,
|
||||
* ->duty_cycle and ->period might be approximated.
|
||||
*/
|
||||
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
|
||||
int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns)
|
||||
return -EINVAL;
|
||||
|
||||
err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pwm->duty_cycle = duty_ns;
|
||||
pwm->period = period_ns;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_config);
|
||||
|
||||
/**
|
||||
* pwm_set_polarity() - configure the polarity of a PWM signal
|
||||
* @pwm: PWM device
|
||||
* @polarity: new polarity of the PWM signal
|
||||
*
|
||||
* Note that the polarity cannot be configured while the PWM device is
|
||||
* enabled.
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!pwm || !pwm->chip->ops)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pwm->chip->ops->set_polarity)
|
||||
return -ENOSYS;
|
||||
|
||||
mutex_lock(&pwm->lock);
|
||||
|
||||
if (pwm_is_enabled(pwm)) {
|
||||
err = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
pwm->polarity = polarity;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pwm->lock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_set_polarity);
|
||||
|
||||
/**
|
||||
* pwm_enable() - start a PWM output toggling
|
||||
* @pwm: PWM device
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int pwm_enable(struct pwm_device *pwm)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!pwm)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pwm->lock);
|
||||
if (!memcmp(state, &pwm->state, sizeof(*state)))
|
||||
return 0;
|
||||
|
||||
if (!test_and_set_bit(PWMF_ENABLED, &pwm->flags)) {
|
||||
err = pwm->chip->ops->enable(pwm->chip, pwm);
|
||||
if (pwm->chip->ops->apply) {
|
||||
err = pwm->chip->ops->apply(pwm->chip, pwm, state);
|
||||
if (err)
|
||||
clear_bit(PWMF_ENABLED, &pwm->flags);
|
||||
return err;
|
||||
|
||||
pwm->state = *state;
|
||||
} else {
|
||||
/*
|
||||
* FIXME: restore the initial state in case of error.
|
||||
*/
|
||||
if (state->polarity != pwm->state.polarity) {
|
||||
if (!pwm->chip->ops->set_polarity)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/*
|
||||
* Changing the polarity of a running PWM is
|
||||
* only allowed when the PWM driver implements
|
||||
* ->apply().
|
||||
*/
|
||||
if (pwm->state.enabled) {
|
||||
pwm->chip->ops->disable(pwm->chip, pwm);
|
||||
pwm->state.enabled = false;
|
||||
}
|
||||
|
||||
err = pwm->chip->ops->set_polarity(pwm->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 = pwm->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 = pwm->chip->ops->enable(pwm->chip, pwm);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
pwm->chip->ops->disable(pwm->chip, pwm);
|
||||
}
|
||||
|
||||
pwm->state.enabled = state->enabled;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&pwm->lock);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_enable);
|
||||
EXPORT_SYMBOL_GPL(pwm_apply_state);
|
||||
|
||||
/**
|
||||
* pwm_disable() - stop a PWM output toggling
|
||||
* 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.
|
||||
*/
|
||||
void pwm_disable(struct pwm_device *pwm)
|
||||
int pwm_adjust_config(struct pwm_device *pwm)
|
||||
{
|
||||
if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags))
|
||||
pwm->chip->ops->disable(pwm->chip, pwm);
|
||||
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;
|
||||
|
||||
return pwm_apply_state(pwm, &state);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
do_div(dutycycle, state.period);
|
||||
state.duty_cycle = dutycycle;
|
||||
state.period = pargs.period;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_state(pwm, &state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_disable);
|
||||
EXPORT_SYMBOL_GPL(pwm_adjust_config);
|
||||
|
||||
static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
|
||||
{
|
||||
@ -621,13 +669,6 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
|
||||
|
||||
pwm->label = con_id;
|
||||
|
||||
/*
|
||||
* FIXME: This should be removed once all PWM users properly make use
|
||||
* of struct pwm_args to initialize the PWM device. As long as this is
|
||||
* here, the PWM state and hardware state can get out of sync.
|
||||
*/
|
||||
pwm_apply_args(pwm);
|
||||
|
||||
put:
|
||||
of_node_put(args.np);
|
||||
|
||||
@ -762,13 +803,6 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
|
||||
pwm->args.period = chosen->period;
|
||||
pwm->args.polarity = chosen->polarity;
|
||||
|
||||
/*
|
||||
* FIXME: This should be removed once all PWM users properly make use
|
||||
* of struct pwm_args to initialize the PWM device. As long as this is
|
||||
* here, the PWM state and hardware state can get out of sync.
|
||||
*/
|
||||
pwm_apply_args(pwm);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pwm_lookup_lock);
|
||||
return pwm;
|
||||
@ -915,15 +949,23 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
|
||||
|
||||
for (i = 0; i < chip->npwm; i++) {
|
||||
struct pwm_device *pwm = &chip->pwms[i];
|
||||
struct pwm_state state;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label);
|
||||
|
||||
if (test_bit(PWMF_REQUESTED, &pwm->flags))
|
||||
seq_puts(s, " requested");
|
||||
|
||||
if (pwm_is_enabled(pwm))
|
||||
if (state.enabled)
|
||||
seq_puts(s, " enabled");
|
||||
|
||||
seq_printf(s, " period: %u ns", state.period);
|
||||
seq_printf(s, " duty: %u ns", state.duty_cycle);
|
||||
seq_printf(s, " polarity: %s",
|
||||
state.polarity ? "inverse" : "normal");
|
||||
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pwm->period != period_ns) {
|
||||
if (pwm_get_period(pwm) != period_ns) {
|
||||
int clk_div;
|
||||
|
||||
/* changing the clk divisor, need to disable fisrt */
|
||||
|
@ -249,7 +249,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event),
|
||||
LPC18XX_PWM_EVSTATEMSK_ALL);
|
||||
|
||||
if (pwm->polarity == PWM_POLARITY_NORMAL) {
|
||||
if (pwm_get_polarity(pwm) == PWM_POLARITY_NORMAL) {
|
||||
set_event = lpc18xx_pwm->period_event;
|
||||
clear_event = lpc18xx_data->duty_event;
|
||||
res_action = LPC18XX_PWM_RES_SET;
|
||||
|
@ -192,7 +192,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
|
||||
load_value, load_value, match_value, match_value);
|
||||
|
||||
omap->pdata->set_pwm(omap->dm_timer,
|
||||
pwm->polarity == PWM_POLARITY_INVERSED,
|
||||
pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED,
|
||||
true,
|
||||
PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE);
|
||||
|
||||
|
@ -157,7 +157,7 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
return div;
|
||||
|
||||
/* Let the core driver set pwm->period if disabled and duty_ns == 0 */
|
||||
if (!test_bit(PWMF_ENABLED, &pwm->flags) && !duty_ns)
|
||||
if (!pwm_is_enabled(pwm) && !duty_ns)
|
||||
return 0;
|
||||
|
||||
rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
|
||||
|
@ -354,7 +354,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
|
||||
val = sun4i_pwm_readl(pwm, PWM_CTRL_REG);
|
||||
for (i = 0; i < pwm->chip.npwm; i++)
|
||||
if (!(val & BIT_CH(PWM_ACT_STATE, i)))
|
||||
pwm->chip.pwms[i].polarity = PWM_POLARITY_INVERSED;
|
||||
pwm_set_polarity(&pwm->chip.pwms[i],
|
||||
PWM_POLARITY_INVERSED);
|
||||
clk_disable_unprepare(pwm->clk);
|
||||
|
||||
return 0;
|
||||
|
@ -26,6 +26,7 @@
|
||||
struct pwm_export {
|
||||
struct device child;
|
||||
struct pwm_device *pwm;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static struct pwm_export *child_to_pwm_export(struct device *child)
|
||||
@ -45,15 +46,20 @@ static ssize_t period_show(struct device *child,
|
||||
char *buf)
|
||||
{
|
||||
const struct pwm_device *pwm = child_to_pwm_device(child);
|
||||
struct pwm_state state;
|
||||
|
||||
return sprintf(buf, "%u\n", pwm_get_period(pwm));
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return sprintf(buf, "%u\n", state.period);
|
||||
}
|
||||
|
||||
static ssize_t period_store(struct device *child,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct pwm_device *pwm = child_to_pwm_device(child);
|
||||
struct pwm_export *export = child_to_pwm_export(child);
|
||||
struct pwm_device *pwm = export->pwm;
|
||||
struct pwm_state state;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
@ -61,7 +67,11 @@ static ssize_t period_store(struct device *child,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pwm_config(pwm, pwm_get_duty_cycle(pwm), val);
|
||||
mutex_lock(&export->lock);
|
||||
pwm_get_state(pwm, &state);
|
||||
state.period = val;
|
||||
ret = pwm_apply_state(pwm, &state);
|
||||
mutex_unlock(&export->lock);
|
||||
|
||||
return ret ? : size;
|
||||
}
|
||||
@ -71,15 +81,20 @@ static ssize_t duty_cycle_show(struct device *child,
|
||||
char *buf)
|
||||
{
|
||||
const struct pwm_device *pwm = child_to_pwm_device(child);
|
||||
struct pwm_state state;
|
||||
|
||||
return sprintf(buf, "%u\n", pwm_get_duty_cycle(pwm));
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return sprintf(buf, "%u\n", state.duty_cycle);
|
||||
}
|
||||
|
||||
static ssize_t duty_cycle_store(struct device *child,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct pwm_device *pwm = child_to_pwm_device(child);
|
||||
struct pwm_export *export = child_to_pwm_export(child);
|
||||
struct pwm_device *pwm = export->pwm;
|
||||
struct pwm_state state;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
@ -87,7 +102,11 @@ static ssize_t duty_cycle_store(struct device *child,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pwm_config(pwm, val, pwm_get_period(pwm));
|
||||
mutex_lock(&export->lock);
|
||||
pwm_get_state(pwm, &state);
|
||||
state.duty_cycle = val;
|
||||
ret = pwm_apply_state(pwm, &state);
|
||||
mutex_unlock(&export->lock);
|
||||
|
||||
return ret ? : size;
|
||||
}
|
||||
@ -97,33 +116,46 @@ static ssize_t enable_show(struct device *child,
|
||||
char *buf)
|
||||
{
|
||||
const struct pwm_device *pwm = child_to_pwm_device(child);
|
||||
struct pwm_state state;
|
||||
|
||||
return sprintf(buf, "%d\n", pwm_is_enabled(pwm));
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return sprintf(buf, "%d\n", state.enabled);
|
||||
}
|
||||
|
||||
static ssize_t enable_store(struct device *child,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct pwm_device *pwm = child_to_pwm_device(child);
|
||||
struct pwm_export *export = child_to_pwm_export(child);
|
||||
struct pwm_device *pwm = export->pwm;
|
||||
struct pwm_state state;
|
||||
int val, ret;
|
||||
|
||||
ret = kstrtoint(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&export->lock);
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
pwm_disable(pwm);
|
||||
state.enabled = false;
|
||||
break;
|
||||
case 1:
|
||||
ret = pwm_enable(pwm);
|
||||
state.enabled = true;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
pwm_apply_state(pwm, &state);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&export->lock);
|
||||
return ret ? : size;
|
||||
}
|
||||
|
||||
@ -133,8 +165,11 @@ static ssize_t polarity_show(struct device *child,
|
||||
{
|
||||
const struct pwm_device *pwm = child_to_pwm_device(child);
|
||||
const char *polarity = "unknown";
|
||||
struct pwm_state state;
|
||||
|
||||
switch (pwm_get_polarity(pwm)) {
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
switch (state.polarity) {
|
||||
case PWM_POLARITY_NORMAL:
|
||||
polarity = "normal";
|
||||
break;
|
||||
@ -151,8 +186,10 @@ static ssize_t polarity_store(struct device *child,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct pwm_device *pwm = child_to_pwm_device(child);
|
||||
struct pwm_export *export = child_to_pwm_export(child);
|
||||
struct pwm_device *pwm = export->pwm;
|
||||
enum pwm_polarity polarity;
|
||||
struct pwm_state state;
|
||||
int ret;
|
||||
|
||||
if (sysfs_streq(buf, "normal"))
|
||||
@ -162,7 +199,11 @@ static ssize_t polarity_store(struct device *child,
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
ret = pwm_set_polarity(pwm, polarity);
|
||||
mutex_lock(&export->lock);
|
||||
pwm_get_state(pwm, &state);
|
||||
state.polarity = polarity;
|
||||
ret = pwm_apply_state(pwm, &state);
|
||||
mutex_unlock(&export->lock);
|
||||
|
||||
return ret ? : size;
|
||||
}
|
||||
@ -203,6 +244,7 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
|
||||
}
|
||||
|
||||
export->pwm = pwm;
|
||||
mutex_init(&export->lock);
|
||||
|
||||
export->child.release = pwm_export_release;
|
||||
export->child.parent = parent;
|
||||
|
@ -162,7 +162,7 @@ static int lm3630a_intr_config(struct lm3630a_chip *pchip)
|
||||
|
||||
static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
|
||||
{
|
||||
unsigned int period = pwm_get_period(pchip->pwmd);
|
||||
unsigned int period = pchip->pdata->pwm_period;
|
||||
unsigned int duty = br * period / br_max;
|
||||
|
||||
pwm_config(pchip->pwmd, duty, period);
|
||||
@ -424,8 +424,13 @@ static int lm3630a_probe(struct i2c_client *client,
|
||||
dev_err(&client->dev, "fail : get pwm device\n");
|
||||
return PTR_ERR(pchip->pwmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(pchip->pwmd);
|
||||
}
|
||||
pchip->pwmd->period = pdata->pwm_period;
|
||||
|
||||
/* interrupt enable : irq 0 is not allowed */
|
||||
pchip->irq = client->irq;
|
||||
|
@ -246,6 +246,12 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
|
||||
return;
|
||||
|
||||
lp->pwm = pwm;
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(pwm);
|
||||
}
|
||||
|
||||
pwm_config(lp->pwm, duty, period);
|
||||
|
@ -145,6 +145,12 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
|
||||
}
|
||||
|
||||
bl->pwm = pwm;
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(pwm);
|
||||
}
|
||||
|
||||
pwm_config(bl->pwm, duty, period);
|
||||
|
@ -201,6 +201,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct pwm_bl_data *pb;
|
||||
int initial_blank = FB_BLANK_UNBLANK;
|
||||
struct pwm_args pargs;
|
||||
int ret;
|
||||
|
||||
if (!data) {
|
||||
@ -306,17 +307,22 @@ static int pwm_backlight_probe(struct platform_device *pdev)
|
||||
|
||||
dev_dbg(&pdev->dev, "got pwm for backlight\n");
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(pb->pwm);
|
||||
|
||||
/*
|
||||
* The DT case will set the pwm_period_ns field to 0 and store the
|
||||
* period, parsed from the DT, in the PWM device. For the non-DT case,
|
||||
* set the period from platform data if it has not already been set
|
||||
* via the PWM lookup table.
|
||||
*/
|
||||
pb->period = pwm_get_period(pb->pwm);
|
||||
if (!pb->period && (data->pwm_period_ns > 0)) {
|
||||
pwm_get_args(pb->pwm, &pargs);
|
||||
pb->period = pargs.period;
|
||||
if (!pb->period && (data->pwm_period_ns > 0))
|
||||
pb->period = data->pwm_period_ns;
|
||||
pwm_set_period(pb->pwm, data->pwm_period_ns);
|
||||
}
|
||||
|
||||
pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
|
||||
|
||||
|
@ -286,6 +286,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
|
||||
{
|
||||
int ret;
|
||||
u32 precharge, dclk, com_invdir, compins;
|
||||
struct pwm_args pargs;
|
||||
|
||||
if (par->device_info->need_pwm) {
|
||||
par->pwm = pwm_get(&par->client->dev, NULL);
|
||||
@ -294,7 +295,15 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
|
||||
return PTR_ERR(par->pwm);
|
||||
}
|
||||
|
||||
par->pwm_period = pwm_get_period(par->pwm);
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to
|
||||
* the atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(par->pwm);
|
||||
|
||||
pwm_get_args(par->pwm, &pargs);
|
||||
|
||||
par->pwm_period = pargs.period;
|
||||
/* Enable the PWM */
|
||||
pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
|
||||
pwm_enable(par->pwm);
|
||||
|
@ -5,59 +5,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
struct pwm_device;
|
||||
struct seq_file;
|
||||
|
||||
#if IS_ENABLED(CONFIG_PWM)
|
||||
/*
|
||||
* pwm_request - request a PWM device
|
||||
*/
|
||||
struct pwm_device *pwm_request(int pwm_id, const char *label);
|
||||
|
||||
/*
|
||||
* pwm_free - free a PWM device
|
||||
*/
|
||||
void pwm_free(struct pwm_device *pwm);
|
||||
|
||||
/*
|
||||
* pwm_config - change a PWM device configuration
|
||||
*/
|
||||
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
|
||||
|
||||
/*
|
||||
* pwm_enable - start a PWM output toggling
|
||||
*/
|
||||
int pwm_enable(struct pwm_device *pwm);
|
||||
|
||||
/*
|
||||
* pwm_disable - stop a PWM output toggling
|
||||
*/
|
||||
void pwm_disable(struct pwm_device *pwm);
|
||||
#else
|
||||
static inline struct pwm_device *pwm_request(int pwm_id, const char *label)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline void pwm_free(struct pwm_device *pwm)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int pwm_enable(struct pwm_device *pwm)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void pwm_disable(struct pwm_device *pwm)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
struct pwm_chip;
|
||||
|
||||
/**
|
||||
@ -94,8 +42,21 @@ struct pwm_args {
|
||||
|
||||
enum {
|
||||
PWMF_REQUESTED = 1 << 0,
|
||||
PWMF_ENABLED = 1 << 1,
|
||||
PWMF_EXPORTED = 1 << 2,
|
||||
PWMF_EXPORTED = 1 << 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* struct pwm_state - state of a PWM channel
|
||||
* @period: PWM period (in nanoseconds)
|
||||
* @duty_cycle: PWM duty cycle (in nanoseconds)
|
||||
* @polarity: PWM polarity
|
||||
* @enabled: PWM enabled status
|
||||
*/
|
||||
struct pwm_state {
|
||||
unsigned int period;
|
||||
unsigned int duty_cycle;
|
||||
enum pwm_polarity polarity;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -106,11 +67,8 @@ enum {
|
||||
* @pwm: global index of the PWM device
|
||||
* @chip: PWM chip providing this PWM device
|
||||
* @chip_data: chip-private data associated with the PWM device
|
||||
* @lock: used to serialize accesses to the PWM device where necessary
|
||||
* @period: period of the PWM signal (in nanoseconds)
|
||||
* @duty_cycle: duty cycle of the PWM signal (in nanoseconds)
|
||||
* @polarity: polarity of the PWM signal
|
||||
* @args: PWM arguments
|
||||
* @state: curent PWM channel state
|
||||
*/
|
||||
struct pwm_device {
|
||||
const char *label;
|
||||
@ -119,50 +77,68 @@ struct pwm_device {
|
||||
unsigned int pwm;
|
||||
struct pwm_chip *chip;
|
||||
void *chip_data;
|
||||
struct mutex lock;
|
||||
|
||||
unsigned int period;
|
||||
unsigned int duty_cycle;
|
||||
enum pwm_polarity polarity;
|
||||
|
||||
struct pwm_args args;
|
||||
struct pwm_state state;
|
||||
};
|
||||
|
||||
/**
|
||||
* pwm_get_state() - retrieve the current PWM state
|
||||
* @pwm: PWM device
|
||||
* @state: state to fill with the current PWM state
|
||||
*/
|
||||
static inline void pwm_get_state(const struct pwm_device *pwm,
|
||||
struct pwm_state *state)
|
||||
{
|
||||
*state = pwm->state;
|
||||
}
|
||||
|
||||
static inline bool pwm_is_enabled(const struct pwm_device *pwm)
|
||||
{
|
||||
return test_bit(PWMF_ENABLED, &pwm->flags);
|
||||
struct pwm_state state;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return state.enabled;
|
||||
}
|
||||
|
||||
static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
|
||||
{
|
||||
if (pwm)
|
||||
pwm->period = period;
|
||||
pwm->state.period = period;
|
||||
}
|
||||
|
||||
static inline unsigned int pwm_get_period(const struct pwm_device *pwm)
|
||||
{
|
||||
return pwm ? pwm->period : 0;
|
||||
struct pwm_state state;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return state.period;
|
||||
}
|
||||
|
||||
static inline void pwm_set_duty_cycle(struct pwm_device *pwm, unsigned int duty)
|
||||
{
|
||||
if (pwm)
|
||||
pwm->duty_cycle = duty;
|
||||
pwm->state.duty_cycle = duty;
|
||||
}
|
||||
|
||||
static inline unsigned int pwm_get_duty_cycle(const struct pwm_device *pwm)
|
||||
{
|
||||
return pwm ? pwm->duty_cycle : 0;
|
||||
}
|
||||
struct pwm_state state;
|
||||
|
||||
/*
|
||||
* pwm_set_polarity - configure the polarity of a PWM signal
|
||||
*/
|
||||
int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return state.duty_cycle;
|
||||
}
|
||||
|
||||
static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm)
|
||||
{
|
||||
return pwm ? pwm->polarity : PWM_POLARITY_NORMAL;
|
||||
struct pwm_state state;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return state.polarity;
|
||||
}
|
||||
|
||||
static inline void pwm_get_args(const struct pwm_device *pwm,
|
||||
@ -171,12 +147,6 @@ static inline void pwm_get_args(const struct pwm_device *pwm,
|
||||
*args = pwm->args;
|
||||
}
|
||||
|
||||
static inline void pwm_apply_args(struct pwm_device *pwm)
|
||||
{
|
||||
pwm_set_period(pwm, pwm->args.period);
|
||||
pwm_set_polarity(pwm, pwm->args.polarity);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct pwm_ops - PWM controller operations
|
||||
* @request: optional hook for requesting a PWM
|
||||
@ -185,6 +155,13 @@ static inline void pwm_apply_args(struct pwm_device *pwm)
|
||||
* @set_polarity: configure the polarity of this PWM
|
||||
* @enable: enable PWM output toggling
|
||||
* @disable: disable PWM output toggling
|
||||
* @apply: atomically apply a new PWM config. The state argument
|
||||
* should be adjusted with the real hardware config (if the
|
||||
* approximate the period or duty_cycle value, state should
|
||||
* reflect it)
|
||||
* @get_state: get the current PWM state. This function is only
|
||||
* called once per PWM device when the PWM chip is
|
||||
* registered.
|
||||
* @dbg_show: optional routine to show contents in debugfs
|
||||
* @owner: helps prevent removal of modules exporting active PWMs
|
||||
*/
|
||||
@ -197,6 +174,10 @@ struct pwm_ops {
|
||||
enum pwm_polarity polarity);
|
||||
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
|
||||
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
|
||||
int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_state *state);
|
||||
void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_state *state);
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
|
||||
#endif
|
||||
@ -232,6 +213,115 @@ struct pwm_chip {
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_PWM)
|
||||
/* PWM user APIs */
|
||||
struct pwm_device *pwm_request(int pwm_id, const char *label);
|
||||
void pwm_free(struct pwm_device *pwm);
|
||||
int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
|
||||
int pwm_adjust_config(struct pwm_device *pwm);
|
||||
|
||||
/**
|
||||
* pwm_config() - change a PWM device configuration
|
||||
* @pwm: PWM device
|
||||
* @duty_ns: "on" time (in nanoseconds)
|
||||
* @period_ns: duration (in nanoseconds) of one cycle
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
|
||||
int period_ns)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
if (!pwm)
|
||||
return -EINVAL;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
if (state.duty_cycle == duty_ns && state.period == period_ns)
|
||||
return 0;
|
||||
|
||||
state.duty_cycle = duty_ns;
|
||||
state.period = period_ns;
|
||||
return pwm_apply_state(pwm, &state);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_set_polarity() - configure the polarity of a PWM signal
|
||||
* @pwm: PWM device
|
||||
* @polarity: new polarity of the PWM signal
|
||||
*
|
||||
* Note that the polarity cannot be configured while the PWM device is
|
||||
* enabled.
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static inline int pwm_set_polarity(struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
if (!pwm)
|
||||
return -EINVAL;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
if (state.polarity == polarity)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Changing the polarity of a running PWM without adjusting the
|
||||
* dutycycle/period value is a bit risky (can introduce glitches).
|
||||
* Return -EBUSY in this case.
|
||||
* Note that this is allowed when using pwm_apply_state() because
|
||||
* the user specifies all the parameters.
|
||||
*/
|
||||
if (state.enabled)
|
||||
return -EBUSY;
|
||||
|
||||
state.polarity = polarity;
|
||||
return pwm_apply_state(pwm, &state);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_enable() - start a PWM output toggling
|
||||
* @pwm: PWM device
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static inline int pwm_enable(struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
if (!pwm)
|
||||
return -EINVAL;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
if (state.enabled)
|
||||
return 0;
|
||||
|
||||
state.enabled = true;
|
||||
return pwm_apply_state(pwm, &state);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_disable() - stop a PWM output toggling
|
||||
* @pwm: PWM device
|
||||
*/
|
||||
static inline void pwm_disable(struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
if (!pwm)
|
||||
return;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
if (!state.enabled)
|
||||
return;
|
||||
|
||||
state.enabled = false;
|
||||
pwm_apply_state(pwm, &state);
|
||||
}
|
||||
|
||||
|
||||
/* PWM provider APIs */
|
||||
int pwm_set_chip_data(struct pwm_device *pwm, void *data);
|
||||
void *pwm_get_chip_data(struct pwm_device *pwm);
|
||||
|
||||
@ -257,6 +347,47 @@ void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
|
||||
|
||||
bool pwm_can_sleep(struct pwm_device *pwm);
|
||||
#else
|
||||
static inline struct pwm_device *pwm_request(int pwm_id, const char *label)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline void pwm_free(struct pwm_device *pwm)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int pwm_apply_state(struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int pwm_adjust_config(struct pwm_device *pwm)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
|
||||
int period_ns)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int pwm_set_polarity(struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int pwm_enable(struct pwm_device *pwm)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void pwm_disable(struct pwm_device *pwm)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data)
|
||||
{
|
||||
return -EINVAL;
|
||||
@ -328,6 +459,34 @@ static inline bool pwm_can_sleep(struct pwm_device *pwm)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void pwm_apply_args(struct pwm_device *pwm)
|
||||
{
|
||||
/*
|
||||
* PWM users calling pwm_apply_args() expect to have a fresh config
|
||||
* where the polarity and period are set according to pwm_args info.
|
||||
* The problem is, polarity can only be changed when the PWM is
|
||||
* disabled.
|
||||
*
|
||||
* PWM drivers supporting hardware readout may declare the PWM device
|
||||
* as enabled, and prevent polarity setting, which changes from the
|
||||
* existing behavior, where all PWM devices are declared as disabled
|
||||
* at startup (even if they are actually enabled), thus authorizing
|
||||
* polarity setting.
|
||||
*
|
||||
* Instead of setting ->enabled to false, we call pwm_disable()
|
||||
* before pwm_set_polarity() to ensure that everything is configured
|
||||
* as expected, and the PWM is really disabled when the user request
|
||||
* it.
|
||||
*
|
||||
* Note that PWM users requiring a smooth handover between the
|
||||
* bootloader and the kernel (like critical regulators controlled by
|
||||
* PWM devices) will have to switch to the atomic API and avoid calling
|
||||
* pwm_apply_args().
|
||||
*/
|
||||
pwm_disable(pwm);
|
||||
pwm_set_polarity(pwm, pwm->args.polarity);
|
||||
}
|
||||
|
||||
struct pwm_lookup {
|
||||
struct list_head list;
|
||||
const char *provider;
|
||||
|
Loading…
Reference in New Issue
Block a user