static int sti_pwm_config()

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;
}