Skip to content

Commit

Permalink
Blackfin: pwm: implement linux/pwm.h API
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mike Frysinger committed Jul 23, 2011
1 parent e1b5596 commit 006669e
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
10 changes: 10 additions & 0 deletions arch/blackfin/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions arch/blackfin/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
100 changes: 100 additions & 0 deletions arch/blackfin/kernel/pwm.c
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit 006669e

Please sign in to comment.