pwm: tegra: Don't needlessly enable and disable the clock in .remove()
[linux-2.6-microblaze.git] / drivers / pwm / pwm-bcm2835.c
index 6ff5f04..50b8594 100644 (file)
@@ -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,10 +158,7 @@ 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;
 
        platform_set_drvdata(pdev, pc);
 
@@ -161,9 +177,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[] = {