Commit 006669ec21d99e161015150ffedeeaeaad513c3b

Authored by Mike Frysinger
1 parent e1b5596533

Blackfin: pwm: implement linux/pwm.h API

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>

Showing 3 changed files with 111 additions and 0 deletions Side-by-side Diff

arch/blackfin/Kconfig
... ... @@ -953,6 +953,16 @@
953 953 To compile this driver as a module, choose M here: the module
954 954 will be called gptimers.
955 955  
  956 +config HAVE_PWM
  957 + tristate "Enable PWM API support"
  958 + depends on BFIN_GPTIMERS
  959 + help
  960 + Enable support for the Pulse Width Modulation framework (as
  961 + found in linux/pwm.h).
  962 +
  963 + To compile this driver as a module, choose M here: the module
  964 + will be called pwm.
  965 +
956 966 choice
957 967 prompt "Uncached DMA region"
958 968 default DMA_UNCACHED_1M
arch/blackfin/kernel/Makefile
... ... @@ -21,6 +21,7 @@
21 21 obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
22 22 CFLAGS_REMOVE_ftrace.o = -pg
23 23  
  24 +obj-$(CONFIG_HAVE_PWM) += pwm.o
24 25 obj-$(CONFIG_IPIPE) += ipipe.o
25 26 obj-$(CONFIG_BFIN_GPTIMERS) += gptimers.o
26 27 obj-$(CONFIG_CPLB_INFO) += cplbinfo.o
arch/blackfin/kernel/pwm.c
  1 +/*
  2 + * Blackfin Pulse Width Modulation (PWM) core
  3 + *
  4 + * Copyright (c) 2011 Analog Devices Inc.
  5 + *
  6 + * Licensed under the GPL-2 or later.
  7 + */
  8 +
  9 +#include <linux/module.h>
  10 +#include <linux/pwm.h>
  11 +#include <linux/slab.h>
  12 +
  13 +#include <asm/gptimers.h>
  14 +#include <asm/portmux.h>
  15 +
  16 +struct pwm_device {
  17 + unsigned id;
  18 + unsigned short pin;
  19 +};
  20 +
  21 +static const unsigned short pwm_to_gptimer_per[] = {
  22 + P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
  23 + P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
  24 +};
  25 +
  26 +struct pwm_device *pwm_request(int pwm_id, const char *label)
  27 +{
  28 + struct pwm_device *pwm;
  29 + int ret;
  30 +
  31 + /* XXX: pwm_id really should be unsigned */
  32 + if (pwm_id < 0)
  33 + return NULL;
  34 +
  35 + pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
  36 + if (!pwm)
  37 + return pwm;
  38 +
  39 + pwm->id = pwm_id;
  40 + if (pwm->id >= ARRAY_SIZE(pwm_to_gptimer_per))
  41 + goto err;
  42 +
  43 + pwm->pin = pwm_to_gptimer_per[pwm->id];
  44 + ret = peripheral_request(pwm->pin, label);
  45 + if (ret)
  46 + goto err;
  47 +
  48 + return pwm;
  49 + err:
  50 + kfree(pwm);
  51 + return NULL;
  52 +}
  53 +EXPORT_SYMBOL(pwm_request);
  54 +
  55 +void pwm_free(struct pwm_device *pwm)
  56 +{
  57 + peripheral_free(pwm->pin);
  58 + kfree(pwm);
  59 +}
  60 +EXPORT_SYMBOL(pwm_free);
  61 +
  62 +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
  63 +{
  64 + unsigned long period, duty;
  65 + unsigned long long val;
  66 +
  67 + if (duty_ns < 0 || duty_ns > period_ns)
  68 + return -EINVAL;
  69 +
  70 + val = (unsigned long long)get_sclk() * period_ns;
  71 + do_div(val, NSEC_PER_SEC);
  72 + period = val;
  73 +
  74 + val = (unsigned long long)period * duty_ns;
  75 + do_div(val, period_ns);
  76 + duty = period - val;
  77 +
  78 + if (duty >= period)
  79 + duty = period - 1;
  80 +
  81 + set_gptimer_config(pwm->id, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
  82 + set_gptimer_pwidth(pwm->id, duty);
  83 + set_gptimer_period(pwm->id, period);
  84 +
  85 + return 0;
  86 +}
  87 +EXPORT_SYMBOL(pwm_config);
  88 +
  89 +int pwm_enable(struct pwm_device *pwm)
  90 +{
  91 + enable_gptimer(pwm->id);
  92 + return 0;
  93 +}
  94 +EXPORT_SYMBOL(pwm_enable);
  95 +
  96 +void pwm_disable(struct pwm_device *pwm)
  97 +{
  98 + disable_gptimer(pwm->id);
  99 +}
  100 +EXPORT_SYMBOL(pwm_disable);