summaryrefslogtreecommitdiff
path: root/drivers/pwm/pwm-tiehrpwm.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-26 09:34:29 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-26 09:34:29 -0800
commit3eb05225ee8efb81fe50558f5f9d94e7477ade8f (patch)
tree40692236718c808455f1fcdfbc1c6de6a274eb36 /drivers/pwm/pwm-tiehrpwm.c
parented5dc2372dba46e0ecd08791b1a0399d313e5cff (diff)
parent30f786170352b8264bc7b61c2482713e54accec8 (diff)
Merge tag 'for-3.9-rc1' of git://gitorious.org/linux-pwm/linux-pwm
Pull PWM changes from Thierry Reding: "A new driver has been added to support the PWM mode of the timer counter blocks found on Atmel AT91 SoCs. The VT8500 driver now supports changing the PWM signal polarity and the TI drivers (EHRPWM and ECAP) gained suspend and resume functionality. User drivers can now query the core for whether access to a PWM device will sleep (if the PWM chip is on a slow bus such as I2C or SPI). The pwm-backlight driver now handles the backlight BL_CORE_FBBLANK state in addition to the FB layer's blanking states. To round things off, a few fixes and cleanups are also included" * tag 'for-3.9-rc1' of git://gitorious.org/linux-pwm/linux-pwm: pwm: twl: Use to_twl() instead of container_of() pwm: tegra: assume CONFIG_OF pwm_backlight: Validate dft_brightness in main probe function pwm: Export pwm_{set,get}_chip_data() pwm: Make Kconfig entries more consistent pwm: Add can_sleep property to drivers pwm: Add pwm_can_sleep() as exported API to users pwm-backlight: handle BL_CORE_FBBLANK state pwm: pwm-tiecap: Low power sleep support pwm: pwm-tiehrpwm: Low power sleep support pwm: pwm-tiehrpwm: Update the clock handling of pwm-tiehrpwm driver pwm: vt8500: Add polarity support pwm: vt8500: Register write busy test performed incorrectly pwm: atmel: add Timer Counter Block PWM driver
Diffstat (limited to 'drivers/pwm/pwm-tiehrpwm.c')
-rw-r--r--drivers/pwm/pwm-tiehrpwm.c93
1 files changed, 91 insertions, 2 deletions
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index 5a1399580533..8b4c86fa99c8 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -113,6 +113,17 @@
#define NUM_PWM_CHANNEL 2 /* EHRPWM channels */
+struct ehrpwm_context {
+ u16 tbctl;
+ u16 tbprd;
+ u16 cmpa;
+ u16 cmpb;
+ u16 aqctla;
+ u16 aqctlb;
+ u16 aqsfrc;
+ u16 aqcsfrc;
+};
+
struct ehrpwm_pwm_chip {
struct pwm_chip chip;
unsigned int clk_rate;
@@ -120,6 +131,7 @@ struct ehrpwm_pwm_chip {
unsigned long period_cycles[NUM_PWM_CHANNEL];
enum pwm_polarity polarity[NUM_PWM_CHANNEL];
struct clk *tbclk;
+ struct ehrpwm_context ctx;
};
static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
@@ -127,6 +139,11 @@ static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
return container_of(chip, struct ehrpwm_pwm_chip, chip);
}
+static u16 ehrpwm_read(void *base, int offset)
+{
+ return readw(base + offset);
+}
+
static void ehrpwm_write(void *base, int offset, unsigned int val)
{
writew(val & 0xFFFF, base + offset);
@@ -318,6 +335,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
unsigned short aqcsfrc_val, aqcsfrc_mask;
+ int ret;
/* Leave clock enabled on enabling PWM */
pm_runtime_get_sync(chip->dev);
@@ -341,7 +359,12 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
configure_polarity(pc, pwm->hwpwm);
/* Enable TBCLK before enabling PWM device */
- clk_enable(pc->tbclk);
+ ret = clk_prepare_enable(pc->tbclk);
+ if (ret) {
+ pr_err("Failed to enable TBCLK for %s\n",
+ dev_name(pc->chip.dev));
+ return ret;
+ }
/* Enable time counter for free_run */
ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN);
@@ -372,7 +395,7 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
/* Disabling TBCLK on PWM disable */
- clk_disable(pc->tbclk);
+ clk_disable_unprepare(pc->tbclk);
/* Stop Time base counter */
ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT);
@@ -510,11 +533,77 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&pc->chip);
}
+void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc)
+{
+ pm_runtime_get_sync(pc->chip.dev);
+ pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL);
+ pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD);
+ pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA);
+ pc->ctx.cmpb = ehrpwm_read(pc->mmio_base, CMPB);
+ pc->ctx.aqctla = ehrpwm_read(pc->mmio_base, AQCTLA);
+ pc->ctx.aqctlb = ehrpwm_read(pc->mmio_base, AQCTLB);
+ pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC);
+ pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC);
+ pm_runtime_put_sync(pc->chip.dev);
+}
+
+void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc)
+{
+ ehrpwm_write(pc->mmio_base, TBPRD, pc->ctx.tbprd);
+ ehrpwm_write(pc->mmio_base, CMPA, pc->ctx.cmpa);
+ ehrpwm_write(pc->mmio_base, CMPB, pc->ctx.cmpb);
+ ehrpwm_write(pc->mmio_base, AQCTLA, pc->ctx.aqctla);
+ ehrpwm_write(pc->mmio_base, AQCTLB, pc->ctx.aqctlb);
+ ehrpwm_write(pc->mmio_base, AQSFRC, pc->ctx.aqsfrc);
+ ehrpwm_write(pc->mmio_base, AQCSFRC, pc->ctx.aqcsfrc);
+ ehrpwm_write(pc->mmio_base, TBCTL, pc->ctx.tbctl);
+}
+
+static int ehrpwm_pwm_suspend(struct device *dev)
+{
+ struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev);
+ int i;
+
+ ehrpwm_pwm_save_context(pc);
+ for (i = 0; i < pc->chip.npwm; i++) {
+ struct pwm_device *pwm = &pc->chip.pwms[i];
+
+ if (!test_bit(PWMF_ENABLED, &pwm->flags))
+ continue;
+
+ /* Disable explicitly if PWM is running */
+ pm_runtime_put_sync(dev);
+ }
+ return 0;
+}
+
+static int ehrpwm_pwm_resume(struct device *dev)
+{
+ struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < pc->chip.npwm; i++) {
+ struct pwm_device *pwm = &pc->chip.pwms[i];
+
+ if (!test_bit(PWMF_ENABLED, &pwm->flags))
+ continue;
+
+ /* Enable explicitly if PWM was running */
+ pm_runtime_get_sync(dev);
+ }
+ ehrpwm_pwm_restore_context(pc);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend,
+ ehrpwm_pwm_resume);
+
static struct platform_driver ehrpwm_pwm_driver = {
.driver = {
.name = "ehrpwm",
.owner = THIS_MODULE,
.of_match_table = ehrpwm_of_match,
+ .pm = &ehrpwm_pwm_pm_ops,
},
.probe = ehrpwm_pwm_probe,
.remove = ehrpwm_pwm_remove,