diff options
Diffstat (limited to 'drivers/pwm/pwm-bcm2835.c')
| -rw-r--r-- | drivers/pwm/pwm-bcm2835.c | 40 | 
1 files changed, 30 insertions, 10 deletions
| diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index 6ff5f04b3e07..fc240d5b8121 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -64,8 +64,9 @@ static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,  	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);  	unsigned long rate = clk_get_rate(pc->clk); -	unsigned long long period; -	unsigned long scaler; +	unsigned long long period_cycles; +	u64 max_period; +  	u32 val;  	if (!rate) { @@ -73,18 +74,36 @@ static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,  		return -EINVAL;  	} -	scaler = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate); +	/* +	 * period_cycles must be a 32 bit value, so period * rate / NSEC_PER_SEC +	 * must be <= U32_MAX. As U32_MAX * NSEC_PER_SEC < U64_MAX the +	 * multiplication period * rate doesn't overflow. +	 * To calculate the maximal possible period that guarantees the +	 * above inequality: +	 * +	 *     round(period * rate / NSEC_PER_SEC) <= U32_MAX +	 * <=> period * rate / NSEC_PER_SEC < U32_MAX + 0.5 +	 * <=> period * rate < (U32_MAX + 0.5) * NSEC_PER_SEC +	 * <=> period < ((U32_MAX + 0.5) * NSEC_PER_SEC) / rate +	 * <=> period < ((U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate +	 * <=> period <= ceil((U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate) - 1 +	 */ +	max_period = DIV_ROUND_UP_ULL((u64)U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC / 2, rate) - 1; + +	if (state->period > max_period) +		return -EINVAL; +  	/* set period */ -	period = DIV_ROUND_CLOSEST_ULL(state->period, scaler); +	period_cycles = DIV_ROUND_CLOSEST_ULL(state->period * rate, NSEC_PER_SEC); -	/* dont accept a period that is too small or has been truncated */ -	if ((period < PERIOD_MIN) || (period > U32_MAX)) +	/* don't accept a period that is too small */ +	if (period_cycles < PERIOD_MIN)  		return -EINVAL; -	writel(period, pc->base + PERIOD(pwm->hwpwm)); +	writel(period_cycles, pc->base + PERIOD(pwm->hwpwm));  	/* set duty cycle */ -	val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, scaler); +	val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle * rate, NSEC_PER_SEC);  	writel(val, pc->base + DUTY(pwm->hwpwm));  	/* set polarity */ @@ -139,7 +158,6 @@ static int bcm2835_pwm_probe(struct platform_device *pdev)  	pc->chip.dev = &pdev->dev;  	pc->chip.ops = &bcm2835_pwm_ops; -	pc->chip.base = -1;  	pc->chip.npwm = 2;  	pc->chip.of_xlate = of_pwm_xlate_with_flags;  	pc->chip.of_pwm_n_cells = 3; @@ -161,9 +179,11 @@ static int bcm2835_pwm_remove(struct platform_device *pdev)  {  	struct bcm2835_pwm *pc = platform_get_drvdata(pdev); +	pwmchip_remove(&pc->chip); +  	clk_disable_unprepare(pc->clk); -	return pwmchip_remove(&pc->chip); +	return 0;  }  static const struct of_device_id bcm2835_pwm_of_match[] = { | 
