[PATCH 2/3] pwm: pwm-omap-dmtimer: Fix frequency when using prescaler

Claudiu Beznea Claudiu.Beznea at microchip.com
Mon Jan 22 01:17:08 PST 2018



On 17.01.2018 23:47, Ladislav Michl wrote:
> Prescaler setting is currently not taken into account.
> Fix that by introducing freq member variable and initialize
> it at device probe time. This also avoids frequency
> recomputing at each pwm configure time.
> 
> Signed-off-by: Ladislav Michl <ladis at linux-mips.org>
> ---
>  drivers/pwm/pwm-omap-dmtimer.c |   92 +++++++++++++++++++++++----------------
>  1 file changed, 53 insertions(+), 39 deletions(-)
> 
> diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c
> index cc485d9946f3..81c79e41a167 100644
> --- a/drivers/pwm/pwm-omap-dmtimer.c
> +++ b/drivers/pwm/pwm-omap-dmtimer.c
> @@ -40,6 +40,7 @@ struct pwm_omap_dmtimer_chip {
>  	pwm_omap_dmtimer *dm_timer;
>  	struct omap_dm_timer_ops *pdata;
>  	struct platform_device *dm_timer_pdev;
> +	unsigned long freq;
>  };
>  
>  static inline struct pwm_omap_dmtimer_chip *
> @@ -48,9 +49,10 @@ to_pwm_omap_dmtimer_chip(struct pwm_chip *chip)
>  	return container_of(chip, struct pwm_omap_dmtimer_chip, chip);
>  }
>  
> -static u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns)
> +static inline u32
> +pwm_omap_dmtimer_get_clock_cycles(struct pwm_omap_dmtimer_chip *omap, int ns)
>  {
> -	return DIV_ROUND_CLOSEST_ULL((u64)clk_rate * ns, NSEC_PER_SEC);
> +	return DIV_ROUND_CLOSEST_ULL((u64)omap->freq * ns, NSEC_PER_SEC);
>  }
>  
>  static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap)
> @@ -99,8 +101,6 @@ 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;
>  	bool timer_active;
>  
>  	dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n",
> @@ -114,19 +114,6 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
>  		return 0;
>  	}
>  
> -	fclk = omap->pdata->get_fclk(omap->dm_timer);
> -	if (!fclk) {
> -		dev_err(chip->dev, "invalid pmtimer fclk\n");
> -		goto err_einval;
> -	}
> -
> -	clk_rate = clk_get_rate(fclk);
> -	if (!clk_rate) {
> -		dev_err(chip->dev, "invalid pmtimer fclk rate\n");
> -		goto err_einval;
> -	}
> -
> -	dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate);
>  
>  	/*
>  	 * Calculate the appropriate load and match values based on the
> @@ -144,35 +131,35 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
>  	 *   OMAP4430/60/70 TRM sections 22.2.4.10 and 22.2.4.11
>  	 *   AM335x Sitara TRM sections 20.1.3.5 and 20.1.3.6
>  	 */
> -	period_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, period_ns);
> -	duty_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, duty_ns);
> +	period_cycles = pwm_omap_dmtimer_get_clock_cycles(omap, period_ns);
> +	duty_cycles = pwm_omap_dmtimer_get_clock_cycles(omap, duty_ns);
>  
>  	if (period_cycles < 2) {
>  		dev_info(chip->dev,
>  			 "period %d ns too short for clock rate %lu Hz\n",
> -			 period_ns, clk_rate);
> +			 period_ns, omap->freq);
>  		goto err_einval;
>  	}
>  
>  	if (duty_cycles < 1) {
>  		dev_dbg(chip->dev,
>  			"duty cycle %d ns is too short for clock rate %lu Hz\n",
> -			duty_ns, clk_rate);
> +			duty_ns, omap->freq);
>  		dev_dbg(chip->dev, "using minimum of 1 clock cycle\n");
>  		duty_cycles = 1;
>  	} else if (duty_cycles >= period_cycles) {
>  		dev_dbg(chip->dev,
>  			"duty cycle %d ns is too long for period %d ns at clock rate %lu Hz\n",
> -			duty_ns, period_ns, clk_rate);
> +			duty_ns, period_ns, omap->freq);
>  		dev_dbg(chip->dev, "using maximum of 1 clock cycle less than period\n");
>  		duty_cycles = period_cycles - 1;
>  	}
>  
>  	dev_dbg(chip->dev, "effective duty cycle: %lld ns, period: %lld ns\n",
>  		DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * duty_cycles,
> -				      clk_rate),
> +				      omap->freq),
>  		DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * period_cycles,
> -				      clk_rate));
> +				      omap->freq));
>  
>  	load_value = (DM_TIMER_MAX - period_cycles) + 1;
>  	match_value = load_value + duty_cycles - 1;
> @@ -248,8 +235,9 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
>  	struct dmtimer_platform_data *timer_pdata;
>  	struct omap_dm_timer_ops *pdata;
>  	pwm_omap_dmtimer *dm_timer;
> +	struct clk *fclk;
>  	u32 v;
> -	int status, ret;
> +	int ret;
>  
>  	timer = of_parse_phandle(np, "ti,timers", 0);
>  	if (!timer)
> @@ -302,9 +290,8 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
>  
>  	omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL);
>  	if (!omap) {
> -		pdata->free(dm_timer);
>  		ret = -ENOMEM;
> -		goto put;
> +		goto free;
>  	}
>  
>  	omap->pdata = pdata;
> @@ -315,15 +302,42 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
>  	 * Ensure that the timer is stopped before we allow PWM core to call
>  	 * pwm_enable.
>  	 */
> -	if (pm_runtime_active(&omap->dm_timer_pdev->dev))
> -		omap->pdata->stop(omap->dm_timer);
> -
> -	if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v))
> -		omap->pdata->set_prescaler(omap->dm_timer, v);
> +	if (pm_runtime_active(&timer_pdev->dev))
> +		pdata->stop(dm_timer);
>  
>  	/* setup dmtimer clock source */
> -	if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v))
> -		omap->pdata->set_source(omap->dm_timer, v);
> +	if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v)) {
> +		ret = pdata->set_source(dm_timer, v);
> +		if (ret) {
> +			dev_err(&pdev->dev, "invalid clock-source\n");
> +			goto free;
> +		}
> +	}
> +
> +	fclk = pdata->get_fclk(dm_timer);
> +	if (!fclk) {
> +		dev_err(&pdev->dev, "invalid fclk\n");
> +		ret = -EINVAL;
> +		goto free;
> +	}
> +
> +	omap->freq = clk_get_rate(fclk);
> +	if (!omap->freq) {
> +		dev_err(&pdev->dev, "invalid fclk rate\n");
> +		ret = -EINVAL;
> +		goto free;
> +	}
> +
> +	if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v)) {
> +		ret = pdata->set_prescaler(dm_timer, v);
> +		if (ret) {
> +			dev_err(&pdev->dev, "invalid prescaler\n");
> +			goto free;
> +		}
> +		omap->freq >>= v + 1;
> +	}
> +
> +	dev_dbg(&pdev->dev, "clk rate: %luHz\n", omap->freq);
>  
>  	omap->chip.dev = &pdev->dev;
>  	omap->chip.ops = &pwm_omap_dmtimer_ops;
> @@ -334,18 +348,18 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
>  
>  	mutex_init(&omap->mutex);
>  
> -	status = pwmchip_add(&omap->chip);
> -	if (status < 0) {
> +	ret = pwmchip_add(&omap->chip);
> +	if (ret < 0) {
>  		dev_err(&pdev->dev, "failed to register PWM\n");
> -		omap->pdata->free(omap->dm_timer);
> -		ret = status;
> -		goto put;
> +		goto free;
>  	}
>  
>  	platform_set_drvdata(pdev, omap);
>From documentation: "of_parse_phandle(): Returns the device_node pointer with refcount
incremented. Use of_node_put() on it when done."
In case of success the of_node_put() should also be called as I see.

>  
>  	return 0;
>  
> +free:
> +	pdata->free(dm_timer);
>  put:
>  	of_node_put(timer);
>  
> 



More information about the linux-arm-kernel mailing list