Blackfin: pwm: implement linux/pwm.h API
authorMike Frysinger <vapier@gentoo.org>
Wed, 15 Jun 2011 20:55:39 +0000 (16:55 -0400)
committerMike Frysinger <vapier@gentoo.org>
Sat, 23 Jul 2011 05:18:23 +0000 (01:18 -0400)
For now, this only supports gptimers.  Support for dedicated PWM devices
as found on newer parts to come.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
arch/blackfin/Kconfig
arch/blackfin/kernel/Makefile
arch/blackfin/kernel/pwm.c [new file with mode: 0644]

index d619b17..c747629 100644 (file)
@@ -953,6 +953,16 @@ config BFIN_GPTIMERS
          To compile this driver as a module, choose M here: the module
          will be called gptimers.
 
+config HAVE_PWM
+       tristate "Enable PWM API support"
+       depends on BFIN_GPTIMERS
+       help
+         Enable support for the Pulse Width Modulation framework (as
+         found in linux/pwm.h).
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm.
+
 choice
        prompt "Uncached DMA region"
        default DMA_UNCACHED_1M
index d550b24..b7bdc42 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_FUNCTION_TRACER)        += ftrace-entry.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)  += ftrace.o
 CFLAGS_REMOVE_ftrace.o = -pg
 
+obj-$(CONFIG_HAVE_PWM)               += pwm.o
 obj-$(CONFIG_IPIPE)                  += ipipe.o
 obj-$(CONFIG_BFIN_GPTIMERS)          += gptimers.o
 obj-$(CONFIG_CPLB_INFO)              += cplbinfo.o
diff --git a/arch/blackfin/kernel/pwm.c b/arch/blackfin/kernel/pwm.c
new file mode 100644 (file)
index 0000000..33f5942
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Blackfin Pulse Width Modulation (PWM) core
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#include <asm/gptimers.h>
+#include <asm/portmux.h>
+
+struct pwm_device {
+       unsigned id;
+       unsigned short pin;
+};
+
+static const unsigned short pwm_to_gptimer_per[] = {
+       P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
+       P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
+};
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+       struct pwm_device *pwm;
+       int ret;
+
+       /* XXX: pwm_id really should be unsigned */
+       if (pwm_id < 0)
+               return NULL;
+
+       pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
+       if (!pwm)
+               return pwm;
+
+       pwm->id = pwm_id;
+       if (pwm->id >= ARRAY_SIZE(pwm_to_gptimer_per))
+               goto err;
+
+       pwm->pin = pwm_to_gptimer_per[pwm->id];
+       ret = peripheral_request(pwm->pin, label);
+       if (ret)
+               goto err;
+
+       return pwm;
+ err:
+       kfree(pwm);
+       return NULL;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+       peripheral_free(pwm->pin);
+       kfree(pwm);
+}
+EXPORT_SYMBOL(pwm_free);
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+       unsigned long period, duty;
+       unsigned long long val;
+
+       if (duty_ns < 0 || duty_ns > period_ns)
+               return -EINVAL;
+
+       val = (unsigned long long)get_sclk() * period_ns;
+       do_div(val, NSEC_PER_SEC);
+       period = val;
+
+       val = (unsigned long long)period * duty_ns;
+       do_div(val, period_ns);
+       duty = period - val;
+
+       if (duty >= period)
+               duty = period - 1;
+
+       set_gptimer_config(pwm->id, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
+       set_gptimer_pwidth(pwm->id, duty);
+       set_gptimer_period(pwm->id, period);
+
+       return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+       enable_gptimer(pwm->id);
+       return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+       disable_gptimer(pwm->id);
+}
+EXPORT_SYMBOL(pwm_disable);