aboutsummaryrefslogtreecommitdiff
path: root/drivers/pwm/pwm-bcm-kona.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org> 2015-09-09 10:55:32 -0700
committerGravatar Linus Torvalds <torvalds@linux-foundation.org> 2015-09-09 10:55:32 -0700
commit82278fc079542a04e076da34546a7d6dc649b0cc (patch)
tree96d32ea11f41ee6623190f31f4f6a9263f0c9ddc /drivers/pwm/pwm-bcm-kona.c
parentfix ufs write vs readpage race when writing into a hole (diff)
parentARM: at91: pwm: atmel-hlcdc: Add at91sam9n12 errata (diff)
downloadlinux-82278fc079542a04e076da34546a7d6dc649b0cc.tar.gz
linux-82278fc079542a04e076da34546a7d6dc649b0cc.tar.bz2
linux-82278fc079542a04e076da34546a7d6dc649b0cc.zip
Merge tag 'pwm/for-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm updates from Thierry Reding: "This set of changes introduces the beginnings of a new API that's based around the concept of states that can be atomically applied. Drivers go to various lengths to implement something similar, which indicates that the core should really be providing the necessary framework. On top of that, there is a bit of cleanup as well as improved kerneldoc and integration into the device-drivers DocBook. Regarding drivers there is a new one for the NXP LPC18xx family of SoCs and a couple of fixes for existing drivers (pca9685, Broadcom Kona and Atmel HLCDC)" * tag 'pwm/for-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: ARM: at91: pwm: atmel-hlcdc: Add at91sam9n12 errata pwm: Add NXP LPC18xx PWM/SCT DT binding documentation pwm: NXP LPC18xx PWM/SCT driver pwm-pca9685: Support changing the output frequency pwm-pca9685: Fix several driver bugs pwm: kona: Modify settings application sequence pwm: pca9685: Drop owner assignment pwm: Add to device-drivers documentation pwm: Clean up kerneldoc pwm: Remove useless whitespace pwm: sysfs: Remove unnecessary padding pwm: sysfs: Properly convert from enum to string pwm: Make use of pwm_get_xxx() helpers where appropriate pwm: Add pwm_get_polarity() helper function pwm: Constify PWM device where possible pwm: Add the pwm_is_enabled() helper
Diffstat (limited to 'drivers/pwm/pwm-bcm-kona.c')
-rw-r--r--drivers/pwm/pwm-bcm-kona.c54
1 files changed, 42 insertions, 12 deletions
diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
index 7af8fea2dc5b..c63418322023 100644
--- a/drivers/pwm/pwm-bcm-kona.c
+++ b/drivers/pwm/pwm-bcm-kona.c
@@ -76,19 +76,36 @@ static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *_chip)
return container_of(_chip, struct kona_pwmc, chip);
}
-static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
+/*
+ * Clear trigger bit but set smooth bit to maintain old output.
+ */
+static void kona_pwmc_prepare_for_settings(struct kona_pwmc *kp,
+ unsigned int chan)
{
unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET);
- /* Clear trigger bit but set smooth bit to maintain old output */
value |= 1 << PWM_CONTROL_SMOOTH_SHIFT(chan);
value &= ~(1 << PWM_CONTROL_TRIGGER_SHIFT(chan));
writel(value, kp->base + PWM_CONTROL_OFFSET);
+ /*
+ * There must be a min 400ns delay between clearing trigger and setting
+ * it. Failing to do this may result in no PWM signal.
+ */
+ ndelay(400);
+}
+
+static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
+{
+ unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET);
+
/* Set trigger bit and clear smooth bit to apply new settings */
value &= ~(1 << PWM_CONTROL_SMOOTH_SHIFT(chan));
value |= 1 << PWM_CONTROL_TRIGGER_SHIFT(chan);
writel(value, kp->base + PWM_CONTROL_OFFSET);
+
+ /* Trigger bit must be held high for at least 400 ns. */
+ ndelay(400);
}
static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -133,8 +150,14 @@ static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
return -EINVAL;
}
- /* If the PWM channel is enabled, write the settings to the HW */
- if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+ /*
+ * Don't apply settings if disabled. The period and duty cycle are
+ * always calculated above to ensure the new values are
+ * validated immediately instead of on enable.
+ */
+ if (pwm_is_enabled(pwm)) {
+ kona_pwmc_prepare_for_settings(kp, chan);
+
value = readl(kp->base + PRESCALE_OFFSET);
value &= ~PRESCALE_MASK(chan);
value |= prescale << PRESCALE_SHIFT(chan);
@@ -164,6 +187,8 @@ static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
return ret;
}
+ kona_pwmc_prepare_for_settings(kp, chan);
+
value = readl(kp->base + PWM_CONTROL_OFFSET);
if (polarity == PWM_POLARITY_NORMAL)
@@ -175,9 +200,6 @@ static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
kona_pwmc_apply_settings(kp, chan);
- /* Wait for waveform to settle before gating off the clock */
- ndelay(400);
-
clk_disable_unprepare(kp->clk);
return 0;
@@ -194,7 +216,8 @@ static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm)
return ret;
}
- ret = kona_pwmc_config(chip, pwm, pwm->duty_cycle, pwm->period);
+ ret = kona_pwmc_config(chip, pwm, pwm_get_duty_cycle(pwm),
+ pwm_get_period(pwm));
if (ret < 0) {
clk_disable_unprepare(kp->clk);
return ret;
@@ -207,13 +230,20 @@ static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct kona_pwmc *kp = to_kona_pwmc(chip);
unsigned int chan = pwm->hwpwm;
+ unsigned int value;
+
+ kona_pwmc_prepare_for_settings(kp, chan);
/* Simulate a disable by configuring for zero duty */
writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
- kona_pwmc_apply_settings(kp, chan);
+ writel(0, kp->base + PERIOD_COUNT_OFFSET(chan));
- /* Wait for waveform to settle before gating off the clock */
- ndelay(400);
+ /* Set prescale to 0 for this channel */
+ value = readl(kp->base + PRESCALE_OFFSET);
+ value &= ~PRESCALE_MASK(chan);
+ writel(value, kp->base + PRESCALE_OFFSET);
+
+ kona_pwmc_apply_settings(kp, chan);
clk_disable_unprepare(kp->clk);
}
@@ -287,7 +317,7 @@ static int kona_pwmc_remove(struct platform_device *pdev)
unsigned int chan;
for (chan = 0; chan < kp->chip.npwm; chan++)
- if (test_bit(PWMF_ENABLED, &kp->chip.pwms[chan].flags))
+ if (pwm_is_enabled(&kp->chip.pwms[chan]))
clk_disable_unprepare(kp->clk);
return pwmchip_remove(&kp->chip);