Skip to content

Commit

Permalink
pwm: lpss: Switch to new atomic API
Browse files Browse the repository at this point in the history
Instead of doing things separately, which is not so reliable on some platforms,
switch the driver to use new atomic API, i.e. ->apply() callback.

The change has been tested on Intel platforms such as Broxton, BayTrail, and
Merrifield.

Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
  • Loading branch information
Andy Shevchenko authored and Thierry Reding committed Jan 30, 2017
1 parent b5c050c commit b14e8ce
Showing 1 changed file with 29 additions and 35 deletions.
64 changes: 29 additions & 35 deletions drivers/pwm/pwm-lpss.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,20 @@ static inline void pwm_lpss_write(const struct pwm_device *pwm, u32 value)

static void pwm_lpss_update(struct pwm_device *pwm)
{
/*
* Set a limit for busyloop since not all implementations correctly
* clear PWM_SW_UPDATE bit (at least it's not visible on OS side).
*/
unsigned int count = 10;

pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE);
/* Give it some time to propagate */
usleep_range(10, 50);
while (pwm_lpss_read(pwm) & PWM_SW_UPDATE && --count)
usleep_range(10, 20);
}

static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct pwm_lpss_chip *lpwm = to_lpwm(chip);
unsigned long long on_time_div;
unsigned long c = lpwm->info->clk_rate, base_unit_range;
unsigned long long base_unit, freq = NSEC_PER_SEC;
Expand All @@ -111,51 +116,40 @@ static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
do_div(on_time_div, period_ns);
on_time_div = 255ULL - on_time_div;

pm_runtime_get_sync(chip->dev);

ctrl = pwm_lpss_read(pwm);
ctrl &= ~PWM_ON_TIME_DIV_MASK;
ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT);
base_unit &= base_unit_range;
ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT;
ctrl |= on_time_div;
pwm_lpss_write(pwm, ctrl);

/*
* If the PWM is already enabled we need to notify the hardware
* about the change by setting PWM_SW_UPDATE.
*/
if (pwm_is_enabled(pwm))
pwm_lpss_update(pwm);

pm_runtime_put(chip->dev);

return 0;
}

static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm)
static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
pm_runtime_get_sync(chip->dev);
struct pwm_lpss_chip *lpwm = to_lpwm(chip);

/*
* Hardware must first see PWM_SW_UPDATE before the PWM can be
* enabled.
*/
pwm_lpss_update(pwm);
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE);
return 0;
}
if (state->enabled) {
if (!pwm_is_enabled(pwm)) {
pm_runtime_get_sync(chip->dev);
pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
pwm_lpss_update(pwm);
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE);
} else {
pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
pwm_lpss_update(pwm);
}
} else if (pwm_is_enabled(pwm)) {
pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE);
pm_runtime_put(chip->dev);
}

static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE);
pm_runtime_put(chip->dev);
return 0;
}

static const struct pwm_ops pwm_lpss_ops = {
.config = pwm_lpss_config,
.enable = pwm_lpss_enable,
.disable = pwm_lpss_disable,
.apply = pwm_lpss_apply,
.owner = THIS_MODULE,
};

Expand Down

0 comments on commit b14e8ce

Please sign in to comment.