in pwm-sti.c [163:248]
static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
struct sti_pwm_compat_data *cdata = pc->cdata;
unsigned int ncfg, value, prescale = 0;
struct pwm_device *cur = pc->cur;
struct device *dev = pc->dev;
bool period_same = false;
int ret;
ncfg = hweight_long(pc->configured);
if (ncfg)
period_same = (period_ns == pwm_get_period(cur));
/*
* Allow configuration changes if one of the following conditions
* satisfy.
* 1. No devices have been configured.
* 2. Only one device has been configured and the new request is for
* the same device.
* 3. Only one device has been configured and the new request is for
* a new device and period of the new device is same as the current
* configured period.
* 4. More than one devices are configured and period of the new
* requestis the same as the current period.
*/
if (!ncfg ||
((ncfg == 1) && (pwm->hwpwm == cur->hwpwm)) ||
((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) ||
((ncfg > 1) && period_same)) {
/* Enable clock before writing to PWM registers. */
ret = clk_enable(pc->pwm_clk);
if (ret)
return ret;
ret = clk_enable(pc->cpt_clk);
if (ret)
return ret;
if (!period_same) {
ret = sti_pwm_get_prescale(pc, period_ns, &prescale);
if (ret)
goto clk_dis;
value = prescale & PWM_PRESCALE_LOW_MASK;
ret = regmap_field_write(pc->prescale_low, value);
if (ret)
goto clk_dis;
value = (prescale & PWM_PRESCALE_HIGH_MASK) >> 4;
ret = regmap_field_write(pc->prescale_high, value);
if (ret)
goto clk_dis;
}
/*
* When PWMVal == 0, PWM pulse = 1 local clock cycle.
* When PWMVal == max_pwm_count,
* PWM pulse = (max_pwm_count + 1) local cycles,
* that is continuous pulse: signal never goes low.
*/
value = cdata->max_pwm_cnt * duty_ns / period_ns;
ret = regmap_write(pc->regmap, PWM_OUT_VAL(pwm->hwpwm), value);
if (ret)
goto clk_dis;
ret = regmap_field_write(pc->pwm_cpt_int_en, 0);
set_bit(pwm->hwpwm, &pc->configured);
pc->cur = pwm;
dev_dbg(dev, "prescale:%u, period:%i, duty:%i, value:%u\n",
prescale, period_ns, duty_ns, value);
} else {
return -EINVAL;
}
clk_dis:
clk_disable(pc->pwm_clk);
clk_disable(pc->cpt_clk);
return ret;
}