Commit 6b28fb6f authored by Lokesh Vutla's avatar Lokesh Vutla Committed by Thierry Reding
Browse files

pwm: omap-dmtimer: Implement .apply callback



Implement .apply callback and drop the legacy callbacks(enable, disable,
config, set_polarity). In .apply() check for the current hardware status
before changing the PWM configuration.

Signed-off-by: default avatarLokesh Vutla <lokeshvutla@ti.com>
Tested-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarThierry Reding <thierry.reding@gmail.com>
parent e793eef8
Loading
Loading
Loading
Loading
+129 −51
Original line number Diff line number Diff line
@@ -26,6 +26,11 @@
 *   can get updated as below based on the current timer counter:
 *   	- period for current cycle =  current_period + new period
 *   	- duty_cycle for current period = current period + new duty_cycle.
 * - PWM OMAP DM timer cannot change the polarity when pwm is active. When
 *   user requests a change in polarity when in active state:
 *	- PWM is stopped abruptly(without completing the current cycle)
 *	- Polarity is changed
 *	- A fresh cycle is started.
 */

#include <linux/clk.h>
@@ -46,8 +51,18 @@
#define DM_TIMER_LOAD_MIN 0xfffffffe
#define DM_TIMER_MAX      0xffffffff

/**
 * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip
 *				  corresponding to omap dmtimer.
 * @chip:		PWM chip structure representing PWM controller
 * @mutex:		Mutex to protect pwm apply state
 * @dm_timer:		Pointer to omap dm timer.
 * @pdata:		Pointer to omap dm timer ops.
 * dm_timer_pdev:	Pointer to omap dm timer platform device
 */
struct pwm_omap_dmtimer_chip {
	struct pwm_chip chip;
	/* Mutex to protect pwm apply state */
	struct mutex mutex;
	struct omap_dm_timer *dm_timer;
	const struct omap_dm_timer_ops *pdata;
@@ -60,11 +75,22 @@ to_pwm_omap_dmtimer_chip(struct pwm_chip *chip)
	return container_of(chip, struct pwm_omap_dmtimer_chip, chip);
}

/**
 * pwm_omap_dmtimer_get_clock_cycles() - Get clock cycles in a time frame
 * @clk_rate:	pwm timer clock rate
 * @ns:		time frame in nano seconds.
 *
 * Return number of clock cycles in a given period(ins ns).
 */
static u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns)
{
	return DIV_ROUND_CLOSEST_ULL((u64)clk_rate * ns, NSEC_PER_SEC);
}

/**
 * pwm_omap_dmtimer_start() - Start the pwm omap dm timer in pwm mode
 * @omap:	Pointer to pwm omap dm timer chip
 */
static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap)
{
	/*
@@ -82,33 +108,46 @@ static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap)
	omap->pdata->start(omap->dm_timer);
}

static int pwm_omap_dmtimer_enable(struct pwm_chip *chip,
				   struct pwm_device *pwm)
/**
 * pwm_omap_dmtimer_is_enabled() -  Detect if the pwm is enabled.
 * @omap:	Pointer to pwm omap dm timer chip
 *
 * Return true if pwm is enabled else false.
 */
static bool pwm_omap_dmtimer_is_enabled(struct pwm_omap_dmtimer_chip *omap)
{
	struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);

	mutex_lock(&omap->mutex);
	omap->pdata->set_pwm(omap->dm_timer,
			     pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED,
			     true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
			     true);
	u32 status;

	pwm_omap_dmtimer_start(omap);
	mutex_unlock(&omap->mutex);
	status = omap->pdata->get_pwm_status(omap->dm_timer);

	return 0;
	return !!(status & OMAP_TIMER_CTRL_ST);
}

static void pwm_omap_dmtimer_disable(struct pwm_chip *chip,
				     struct pwm_device *pwm)
/**
 * pwm_omap_dmtimer_polarity() -  Detect the polarity of pwm.
 * @omap:	Pointer to pwm omap dm timer chip
 *
 * Return the polarity of pwm.
 */
static int pwm_omap_dmtimer_polarity(struct pwm_omap_dmtimer_chip *omap)
{
	struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
	u32 status;

	mutex_lock(&omap->mutex);
	omap->pdata->stop(omap->dm_timer);
	mutex_unlock(&omap->mutex);
	status = omap->pdata->get_pwm_status(omap->dm_timer);

	return !!(status & OMAP_TIMER_CTRL_SCPWM);
}

/**
 * pwm_omap_dmtimer_config() - Update the configuration of pwm omap dm timer
 * @chip:	Pointer to PWM controller
 * @pwm:	Pointer to PWM channel
 * @duty_ns:	New duty cycle in nano seconds
 * @period_ns:	New period in nano seconds
 *
 * Return 0 if successfully changed the period/duty_cycle else appropriate
 * error.
 */
static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
				   struct pwm_device *pwm,
				   int duty_ns, int period_ns)
@@ -116,30 +155,26 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
	struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
	u32 period_cycles, duty_cycles;
	u32 load_value, match_value;
	struct clk *fclk;
	unsigned long clk_rate;
	struct clk *fclk;

	dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n",
		duty_ns, period_ns);

	mutex_lock(&omap->mutex);
	if (duty_ns == pwm_get_duty_cycle(pwm) &&
	    period_ns == pwm_get_period(pwm)) {
		/* No change - don't cause any transients. */
		mutex_unlock(&omap->mutex);
	    period_ns == pwm_get_period(pwm))
		return 0;
	}

	fclk = omap->pdata->get_fclk(omap->dm_timer);
	if (!fclk) {
		dev_err(chip->dev, "invalid pmtimer fclk\n");
		goto err_einval;
		return -EINVAL;
	}

	clk_rate = clk_get_rate(fclk);
	if (!clk_rate) {
		dev_err(chip->dev, "invalid pmtimer fclk rate\n");
		goto err_einval;
		return -EINVAL;
	}

	dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate);
@@ -167,7 +202,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
		dev_info(chip->dev,
			 "period %d ns too short for clock rate %lu Hz\n",
			 period_ns, clk_rate);
		goto err_einval;
		return -EINVAL;
	}

	if (duty_cycles < 1) {
@@ -199,55 +234,97 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
	dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n",
		load_value, load_value,	match_value, match_value);

	mutex_unlock(&omap->mutex);

	return 0;

err_einval:
	mutex_unlock(&omap->mutex);

	return -EINVAL;
}

static int pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip,
/**
 * pwm_omap_dmtimer_set_polarity() - Changes the polarity of the pwm dm timer.
 * @chip:	Pointer to PWM controller
 * @pwm:	Pointer to PWM channel
 * @polarity:	New pwm polarity to be set
 */
static void pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip,
					  struct pwm_device *pwm,
					  enum pwm_polarity polarity)
{
	struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
	bool enabled;

	/* Disable the PWM before changing the polarity. */
	enabled = pwm_omap_dmtimer_is_enabled(omap);
	if (enabled)
		omap->pdata->stop(omap->dm_timer);

	/*
	 * PWM core will not call set_polarity while PWM is enabled so it's
	 * safe to reconfigure the timer here without stopping it first.
	 */
	mutex_lock(&omap->mutex);
	omap->pdata->set_pwm(omap->dm_timer,
			     polarity == PWM_POLARITY_INVERSED,
			     true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
			     true);

	if (enabled)
		pwm_omap_dmtimer_start(omap);
}

/**
 * pwm_omap_dmtimer_apply() - Changes the state of the pwm omap dm timer.
 * @chip:	Pointer to PWM controller
 * @pwm:	Pointer to PWM channel
 * @state:	New state to apply
 *
 * Return 0 if successfully changed the state else appropriate error.
 */
static int pwm_omap_dmtimer_apply(struct pwm_chip *chip,
				  struct pwm_device *pwm,
				  const struct pwm_state *state)
{
	struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
	int ret = 0;

	mutex_lock(&omap->mutex);

	if (pwm_omap_dmtimer_is_enabled(omap) && !state->enabled) {
		omap->pdata->stop(omap->dm_timer);
		goto unlock_mutex;
	}

	if (pwm_omap_dmtimer_polarity(omap) != state->polarity)
		pwm_omap_dmtimer_set_polarity(chip, pwm, state->polarity);

	ret = pwm_omap_dmtimer_config(chip, pwm, state->duty_cycle,
				      state->period);
	if (ret)
		goto unlock_mutex;

	if (!pwm_omap_dmtimer_is_enabled(omap) && state->enabled) {
		omap->pdata->set_pwm(omap->dm_timer,
				     state->polarity == PWM_POLARITY_INVERSED,
				     true,
				     OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
				     true);
		pwm_omap_dmtimer_start(omap);
	}

unlock_mutex:
	mutex_unlock(&omap->mutex);

	return 0;
	return ret;
}

static const struct pwm_ops pwm_omap_dmtimer_ops = {
	.enable	= pwm_omap_dmtimer_enable,
	.disable = pwm_omap_dmtimer_disable,
	.config	= pwm_omap_dmtimer_config,
	.set_polarity = pwm_omap_dmtimer_set_polarity,
	.apply = pwm_omap_dmtimer_apply,
	.owner = THIS_MODULE,
};

static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct device_node *timer;
	struct platform_device *timer_pdev;
	struct pwm_omap_dmtimer_chip *omap;
	struct dmtimer_platform_data *timer_pdata;
	const struct omap_dm_timer_ops *pdata;
	struct platform_device *timer_pdev;
	struct pwm_omap_dmtimer_chip *omap;
	struct omap_dm_timer *dm_timer;
	u32 v;
	struct device_node *timer;
	int ret = 0;
	u32 v;

	timer = of_parse_phandle(np, "ti,timers", 0);
	if (!timer)
@@ -280,6 +357,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
	    !pdata->set_load ||
	    !pdata->set_match ||
	    !pdata->set_pwm ||
	    !pdata->get_pwm_status ||
	    !pdata->set_prescaler ||
	    !pdata->write_counter) {
		dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");