summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/configs/colibri_vf_defconfig2
-rw-r--r--arch/arm/mach-mvf/board-colibri_vf.c28
-rw-r--r--arch/arm/mach-mvf/devices-mvf.h2
-rw-r--r--arch/arm/mach-mxs/devices/platform-mxs-pwm.c2
-rwxr-xr-xarch/arm/plat-mxc/Kconfig6
-rwxr-xr-xarch/arm/plat-mxc/Makefile1
-rwxr-xr-xarch/arm/plat-mxc/devices/platform-mxc_pwm.c26
-rwxr-xr-xarch/arm/plat-mxc/ftm-pwm.c460
-rwxr-xr-xarch/arm/plat-mxc/include/mach/devices-common.h3
9 files changed, 510 insertions, 20 deletions
diff --git a/arch/arm/configs/colibri_vf_defconfig b/arch/arm/configs/colibri_vf_defconfig
index 008b5854cd8e..4832addb28f7 100644
--- a/arch/arm/configs/colibri_vf_defconfig
+++ b/arch/arm/configs/colibri_vf_defconfig
@@ -27,7 +27,7 @@ CONFIG_ARCH_MXC=y
CONFIG_ARCH_MVF=y
CONFIG_MACH_COLIBRI_VF50=y
CONFIG_MACH_COLIBRI_VF61=y
-CONFIG_MXC_PWM=y
+CONFIG_MVF_FTM_PWM=y
CONFIG_CLK_DEBUG=y
CONFIG_DMA_ZONE_SIZE=16
CONFIG_NO_HZ=y
diff --git a/arch/arm/mach-mvf/board-colibri_vf.c b/arch/arm/mach-mvf/board-colibri_vf.c
index 8e5a84119dbc..4e463f556839 100644
--- a/arch/arm/mach-mvf/board-colibri_vf.c
+++ b/arch/arm/mach-mvf/board-colibri_vf.c
@@ -578,7 +578,7 @@ static int colibri_vf50_backlight_notify(struct device *dev, int brightness)
}
static struct platform_pwm_backlight_data colibri_vf50_backlight_data = {
- .pwm_id = 1, /* PWM<A> (FTM0CH0) */
+ .pwm_id = 0, /* PWM<A> (FTM0CH0) */
.max_brightness = 255,
.dft_brightness = 127,
.pwm_period_ns = 1000000, /* 1 kHz */
@@ -632,28 +632,28 @@ static struct led_pwm tegra_leds_pwm[] = {
#if 0
{
.name = "PWM<A>",
- .pwm_id = 1,
+ .pwm_id = 0, /* FTM0CH0 */
.max_brightness = 255,
- .pwm_period_ns = 19600,
+ .pwm_period_ns = 1000000,
},
#endif
{
.name = "PWM<B>",
- .pwm_id = 2,
+ .pwm_id = 8, /* FTM1CH0 */
.max_brightness = 255,
- .pwm_period_ns = 19600,
+ .pwm_period_ns = 1000000,
},
{
.name = "PWM<C>",
- .pwm_id = 3,
+ .pwm_id = 1, /* FTM0CH1 */
.max_brightness = 255,
- .pwm_period_ns = 19600,
+ .pwm_period_ns = 1000000,
},
{
.name = "PWM<D>",
- .pwm_id = 4,
+ .pwm_id = 9, /* FTM1CH1 */
.max_brightness = 255,
- .pwm_period_ns = 19600,
+ .pwm_period_ns = 1000000,
},
};
@@ -748,8 +748,14 @@ static void __init mvf_board_init(void)
spi_device_init();
mvfa5_add_dcu(0, &mvf_dcu_pdata);
+
+ /* Enable FTM0 and FTM1 */
mvf_add_mxc_pwm(0);
+ mvf_add_mxc_pwm(1);
+
+ /* Backlight on FTM0CH0 */
mvf_add_mxc_pwm_backlight(0, &colibri_vf50_backlight_data);
+ mvf_add_pwm_leds(&tegra_leds_pwm_data);
mvf_add_wdt(0);
@@ -757,10 +763,6 @@ static void __init mvf_board_init(void)
mvf_add_nand(&mvf_data);
- mvf_add_mxc_pwm(1);
- mvf_add_mxc_pwm(2);
- mvf_add_mxc_pwm(3);
- mvf_add_pwm_leds(&tegra_leds_pwm_data);
#ifdef CONFIG_CAN_FLEXCAN
mvf_add_flexcan0(&can0_pdata);
diff --git a/arch/arm/mach-mvf/devices-mvf.h b/arch/arm/mach-mvf/devices-mvf.h
index c5c3db9d13a1..9870c4ff04af 100644
--- a/arch/arm/mach-mvf/devices-mvf.h
+++ b/arch/arm/mach-mvf/devices-mvf.h
@@ -170,7 +170,7 @@ extern const struct imx_viv_gpu_data mvf_gc355_data __initconst;
extern const struct imx_mxc_pwm_data mvf_mxc_pwm_data[] __initconst;
#define mvf_add_mxc_pwm(id) \
- imx_add_mxc_pwm(&mvf_mxc_pwm_data[id])
+ imx_add_mvf_ftm_pwm(&mvf_mxc_pwm_data[id])
#define mvf_add_mxc_pwm_backlight(id, pdata) \
platform_device_register_resndata(NULL, "pwm-backlight",\
diff --git a/arch/arm/mach-mxs/devices/platform-mxs-pwm.c b/arch/arm/mach-mxs/devices/platform-mxs-pwm.c
index 680f5a902936..b1f5e1810f65 100644
--- a/arch/arm/mach-mxs/devices/platform-mxs-pwm.c
+++ b/arch/arm/mach-mxs/devices/platform-mxs-pwm.c
@@ -18,5 +18,7 @@ struct platform_device *__init mxs_add_mxs_pwm(resource_size_t iobase, int id)
res.start = iobase + 0x10 + 0x20 * id;
res.end = res.start + 0x1f;
+ printk(KERN_INFO "adding pwm start %x to end %x\n", res.start, res.end);
+
return mxs_add_platform_device("mxs-pwm", id, &res, 1, NULL, 0);
}
diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig
index d3cf435d6d28..0bb90b387489 100755
--- a/arch/arm/plat-mxc/Kconfig
+++ b/arch/arm/plat-mxc/Kconfig
@@ -102,6 +102,12 @@ config MXC_PWM
help
Enable support for the i.MX PWM controller(s).
+config MVF_FTM_PWM
+ tristate "Enable FlexTimer PWM driver"
+ select HAVE_PWM
+ help
+ Enable support for the FlexTimer PWM controller(s).
+
config MXC_PWM_CPWM
bool "Center-Aligned PWM mode"
depends on MXC_PWM && ARCH_MVF
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index 6bb77052bbf4..89a7c90987fe 100755
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_IMX_HAVE_IOMUX_V1) += iomux-v1.o
obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o
obj-$(CONFIG_IRAM_ALLOC) += iram_alloc.o
obj-$(CONFIG_MXC_PWM) += pwm.o
+obj-$(CONFIG_MVF_FTM_PWM) += ftm-pwm.o
obj-$(CONFIG_MXC_ULPI) += ulpi.o
obj-$(CONFIG_MXC_USE_EPIT) += epit.o
obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o
diff --git a/arch/arm/plat-mxc/devices/platform-mxc_pwm.c b/arch/arm/plat-mxc/devices/platform-mxc_pwm.c
index c157cf9c782c..32a7516019b9 100755
--- a/arch/arm/plat-mxc/devices/platform-mxc_pwm.c
+++ b/arch/arm/plat-mxc/devices/platform-mxc_pwm.c
@@ -72,31 +72,49 @@ const struct imx_mxc_pwm_data imx6q_mxc_pwm_data[] __initconst = {
#ifdef CONFIG_SOC_MVFA5
const struct imx_mxc_pwm_data mvf_mxc_pwm_data[] __initdata = {
[0] = {
- 1,
+ 0,
MVF_FTM0_BASE_ADDR,
SZ_4K,
MVF_INT_FLEXTIMER0,
},
[1] = {
- 2,
+ 1,
MVF_FTM1_BASE_ADDR,
SZ_4K,
MVF_INT_FLEXTIMER1,
},
[2] = {
- 3,
+ 2,
MVF_FTM2_BASE_ADDR,
SZ_4K,
MVF_INT_FLEXTIMER2,
},
[3] = {
- 4,
+ 3,
MVF_FTM3_BASE_ADDR,
SZ_4K,
MVF_INT_FLEXTIMER3,
},
};
+struct platform_device *__init imx_add_mvf_ftm_pwm(
+ const struct imx_mxc_pwm_data *data)
+{
+ struct resource res[] = {
+ {
+ .start = data->iobase,
+ .end = data->iobase + data->iosize - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = data->irq,
+ .end = data->irq,
+ .flags = IORESOURCE_IRQ,
+ },
+ };
+
+ return imx_add_platform_device("mvf_ftm_pwm", data->id,
+ res, ARRAY_SIZE(res), NULL, 0);
+}
#endif
struct platform_device *__init imx_add_mxc_pwm(
diff --git a/arch/arm/plat-mxc/ftm-pwm.c b/arch/arm/plat-mxc/ftm-pwm.c
new file mode 100755
index 000000000000..211018c14276
--- /dev/null
+++ b/arch/arm/plat-mxc/ftm-pwm.c
@@ -0,0 +1,460 @@
+/*
+ * FlexTimer Module PWM driver
+ *
+ * (c) 2014, Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ * Copyright 2009-2012 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/fsl_devices.h>
+#include <mach/hardware.h>
+
+/*
+ * Vybrid(CONFIG_ARCH_MVF) FlexTimer PWM registers defination
+ */
+#define MVF_PWM_FTM_SC 0x00 /* status and controls */
+#define MVF_PWM_FTM_CNT 0x04 /* counter */
+#define MVF_PWM_FTM_MOD 0x08 /* modulo */
+
+#define MVF_PWM_FTM_CxSC(ch) (((ch) * 0x8) + 0x0C) /* channel(x) status and control */
+#define MVF_PWM_FTM_CxV(ch) (((ch) * 0x8) + 0x10) /* channel(x) value */
+
+#define MVF_PWM_FTM_CNTIN 0x4C /* counter initial value */
+#define MVF_PWM_FTM_STATUS 0x50 /* capture and compare status */
+#define MVF_PWM_FTM_MODE 0x54 /* mode select */
+#define MVF_PWM_FTM_SYNC 0x58 /* synchronization */
+#define MVF_PWM_FTM_OUTINIT 0x5C /* initial state for channels output */
+#define MVF_PWM_FTM_OUTMASK 0x60 /* output mask */
+#define MVF_PWM_FTM_COMBINE 0x64 /* function for linked channels */
+#define MVF_PWM_FTM_DEADTIME 0x68 /* deadtime insertion control */
+#define MVF_PWM_FTM_EXTTRIG 0x6C /* external trigger */
+#define MVF_PWM_FTM_POL 0x70 /* channels polarity */
+#define MVF_PWM_FTM_FMS 0x74 /* fault mode status */
+#define MVF_PWM_FTM_FILTER 0x78 /* input capture filter control */
+#define MVF_PWM_FTM_FLTCTRL 0x7C /* fault control */
+#define MVF_PWM_FTM_QDCTRL 0x80 /* quadrature decoder ctrl and status */
+#define MVF_PWM_FTM_CONF 0x84 /* configuration */
+#define MVF_PWM_FTM_FLTPOL 0x88 /* fault input polarity */
+#define MVF_PWM_FTM_SYNCONF 0x8C /* synchronization configuration */
+#define MVF_PWM_FTM_INVCTRL 0x90 /* inverting control */
+#define MVF_PWM_FTM_SWOCTRL 0x94 /* software output control */
+#define MVF_PWM_FTM_PWMLOAD 0x98 /* PWM load */
+
+#define PWM_TYPE_EPWM 0x01 /* Edge-aligned pwm */
+#define PWM_TYPE_CPWM 0x02 /* Center-aligned pwm */
+
+#define PWM_FTMSC_CPWMS (0x01 << 5)
+#define PWM_FTMSC_CLK_MASK 0x3
+#define PWM_FTMSC_CLK_OFFSET 3
+#define PWM_FTMSC_CLKSYS (0x1 << 3)
+#define PWM_FTMSC_CLKFIX (0x2 << 3)
+#define PWM_FTMSC_CLKEXT (0x3 << 3)
+#define PWM_FTMSC_PS_MASK 0x7
+#define PWM_FTMSC_PS_OFFSET 0
+#define PWM_FTMSC_PS1 0x0
+#define PWM_FTMSC_PS2 0x1
+#define PWM_FTMSC_PS4 0x2
+#define PWM_FTMSC_PS8 0x3
+#define PWM_FTMSC_PS16 0x4
+#define PWM_FTMSC_PS32 0x5
+#define PWM_FTMSC_PS64 0x6
+#define PWM_FTMSC_PS128 0x7
+
+#define PWM_FTMCnSC_MSB (0x1 << 5)
+#define PWM_FTMCnSC_MSA (0x1 << 4)
+#define PWM_FTMCnSC_ELSB (0x1 << 3)
+#define PWM_FTMCnSC_ELSA (0x1 << 2)
+
+#define FTM_PWMMODE (PWM_FTMCnSC_MSB)
+#define FTM_PWM_HIGH_TRUE (PWM_FTMCnSC_ELSB)
+#define FTM_PWM_LOW_TRUE (PWM_FTMCnSC_ELSA)
+
+#define PWM_FTMMODE_FTMEN 0x01
+#define PWM_FTMMODE_INIT 0x02
+#define PWM_FTMMODE_PWMSYNC (0x01 << 3)
+
+#define PWM_FTM_OUTMASK(x) (0x01 << (x))
+#define PWM_FTM_OUTINIT(x) (0x01 << (x))
+
+struct pwm_device {
+ struct list_head node;
+ struct ftm_device *ftm;
+
+ const char *label;
+ unsigned int pwm_id;
+ unsigned char ftm_ch;
+ bool used;
+};
+
+struct ftm_device {
+ struct platform_device *pdev;
+
+ struct clk *clk;
+
+ int clk_enabled;
+ void __iomem *mmio_base;
+
+ unsigned int use_count;
+ int pwmo_invert;
+ unsigned int ftm_id;
+ unsigned int cpwm; /* CPWM mode */
+ unsigned int clk_ps; /* clock prescaler:1/2/4/8/16/32/64/128 */
+ void (*enable_pwm_pad)(void);
+ void (*disable_pwm_pad)(void);
+};
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ struct ftm_device *ftm;
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles;
+ u32 ps, reg;
+
+ printk(KERN_DEBUG "pwm_config %d, duty_ns %d period_ns %d\n",
+ pwm->pwm_id, duty_ns, period_ns);
+ if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+ return -EINVAL;
+
+ ftm = pwm->ftm;
+
+ /* FTM clock source prescaler */
+ ps = (0x1 << ftm->clk_ps) * 1000;
+ /* IPS bus clock source */
+ c = clk_get_rate(ftm->clk) / 1000000UL;
+
+ c = c * period_ns;
+ do_div(c, ps);
+ period_cycles = (unsigned long)c;
+
+ c = clk_get_rate(ftm->clk) / 1000000UL;
+ c = c * duty_ns;
+ do_div(c, ps);
+ duty_cycles = (unsigned long)c;
+
+ if (period_cycles > 0xFFFF) {
+ dev_warn(&ftm->pdev->dev,
+ "required PWM period cycles(%lu) overflow"
+ "16-bits counter!\n", period_cycles);
+ period_cycles = 0xFFFF;
+ }
+ if (duty_cycles >= 0xFFFF) {
+ dev_warn(&ftm->pdev->dev,
+ "required PWM duty cycles(%lu) overflow"
+ "16-bits counter!\n", duty_cycles);
+ duty_cycles = 0xFFFF - 1;
+ }
+ if (duty_cycles > period_cycles)
+ duty_cycles = period_cycles;
+
+ /* configure channel to pwm mode */
+ /* enable FTMEN */
+ if (ftm->cpwm) {
+ u32 reg;
+ reg = __raw_readl(ftm->mmio_base + MVF_PWM_FTM_SC);
+ reg |= 0x01 << 5;
+ __raw_writel(reg, ftm->mmio_base + MVF_PWM_FTM_SC);
+ }
+
+ __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE,
+ ftm->mmio_base + MVF_PWM_FTM_CxSC(pwm->ftm_ch));
+
+ reg = __raw_readl(ftm->mmio_base + MVF_PWM_FTM_OUTMASK);
+ reg &= ~PWM_FTM_OUTMASK(pwm->ftm_ch);
+ __raw_writel(reg, ftm->mmio_base + MVF_PWM_FTM_OUTMASK);
+
+ reg = __raw_readl(ftm->mmio_base + MVF_PWM_FTM_OUTINIT);
+ reg |= PWM_FTM_OUTINIT(pwm->ftm_ch);
+ __raw_writel(reg, ftm->mmio_base + MVF_PWM_FTM_OUTINIT);
+ __raw_writel(0x0, ftm->mmio_base + MVF_PWM_FTM_CNTIN);
+
+ if (ftm->cpwm) {
+ /*
+ * Center-aligned PWM:
+ * period = 2*(MOD - CNTIN)
+ * duty = 2*(CnV - CNTIN)
+ */
+ period_cycles /= 2;
+ duty_cycles /= 2;
+ } else {
+ /* Edge-aligend PWM
+ * period = MOD - CNTIN + 1
+ * duty = CnV - CNTIN
+ */
+ period_cycles -= 1;
+ }
+
+ __raw_writel(period_cycles, ftm->mmio_base + MVF_PWM_FTM_MOD);
+ __raw_writel(duty_cycles, ftm->mmio_base + MVF_PWM_FTM_CxV(pwm->ftm_ch));
+
+ return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+ struct ftm_device *ftm = pwm->ftm;
+ unsigned long reg;
+ int rc = 0;
+
+ if (!ftm->clk_enabled) {
+ rc = clk_enable(ftm->clk);
+ if (!rc)
+ ftm->clk_enabled++;
+
+ reg = __raw_readl(ftm->mmio_base + MVF_PWM_FTM_SC);
+ reg &= ~((PWM_FTMSC_CLK_MASK << PWM_FTMSC_CLK_OFFSET) |
+ (PWM_FTMSC_PS_MASK << PWM_FTMSC_PS_OFFSET));
+ /*
+ * select IPS bus clock source
+ * prescale 128
+ */
+ reg |= (PWM_FTMSC_CLKSYS | ftm->clk_ps);
+ __raw_writel(reg, ftm->mmio_base + MVF_PWM_FTM_SC);
+ }
+
+ if (ftm->enable_pwm_pad)
+ ftm->enable_pwm_pad();
+
+ return rc;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+ struct ftm_device *ftm = pwm->ftm;
+ u32 reg;
+
+ reg = __raw_readl(ftm->mmio_base + MVF_PWM_FTM_OUTMASK);
+ reg |= PWM_FTM_OUTMASK(pwm->ftm_ch);
+ __raw_writel(reg, ftm->mmio_base + MVF_PWM_FTM_OUTMASK);
+
+ if (ftm->disable_pwm_pad)
+ ftm->disable_pwm_pad();
+
+ if (ftm->clk_enabled) {
+ ftm->clk_enabled--;
+
+ if (!ftm->clk_enabled)
+ clk_disable(ftm->clk);
+ }
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+ struct pwm_device *pwm;
+ int found = 0;
+
+ mutex_lock(&pwm_lock);
+
+ list_for_each_entry(pwm, &pwm_list, node) {
+ if (pwm->pwm_id == pwm_id) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ if (!pwm->used) {
+ pwm->used = true;
+ pwm->ftm->use_count++;
+ pwm->label = label;
+ } else
+ pwm = ERR_PTR(-EBUSY);
+ } else
+ pwm = ERR_PTR(-ENOENT);
+ mutex_unlock(&pwm_lock);
+
+ return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+ mutex_lock(&pwm_lock);
+
+ if (pwm->used) {
+ pwm->used = false;
+ pwm->ftm->use_count--;
+ pwm->label = NULL;
+ } else
+ pr_warning("PWM device already freed\n");
+
+ mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static int mvf_ftm_pwm_remove_channels(struct ftm_device *ftm)
+{
+ struct pwm_device *pwm;
+
+ list_for_each_entry(pwm, &pwm_list, node) {
+ mutex_lock(&pwm_lock);
+ list_del(&pwm->node);
+ mutex_unlock(&pwm_lock);
+ }
+
+ return 0;
+}
+
+static int mvf_ftm_pwm_add_channels(struct ftm_device *ftm, int channels)
+{
+ static int pwm_id = 0;
+ struct pwm_device *pwm;
+ int i;
+
+ for (i = 0; i < channels; i++) {
+ pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+ if (pwm == NULL)
+ goto no_memory;
+
+ pwm->pwm_id = pwm_id++;
+ pwm->ftm = ftm;
+ pwm->ftm_ch = i;
+
+ mutex_lock(&pwm_lock);
+ list_add_tail(&pwm->node, &pwm_list);
+ mutex_unlock(&pwm_lock);
+ }
+
+ return 0;
+no_memory:
+ mvf_ftm_pwm_remove_channels(ftm);
+ return -ENOMEM;
+}
+
+static int __devinit mvf_ftm_pwm_probe(struct platform_device *pdev)
+{
+ struct ftm_device *ftm;
+ struct resource *r;
+ struct mxc_pwm_platform_data *plat_data = pdev->dev.platform_data;
+ int ret = 0;
+
+ ftm = kzalloc(sizeof(struct ftm_device), GFP_KERNEL);
+ if (ftm == NULL) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ ftm->clk = clk_get(&pdev->dev, "pwm");
+
+ if (IS_ERR(ftm->clk)) {
+ ret = PTR_ERR(ftm->clk);
+ goto err_free;
+ }
+
+ ftm->clk_enabled = 0;
+
+ ftm->use_count = 0;
+ ftm->ftm_id = pdev->id;
+ ftm->pdev = pdev;
+ /* default select IPS bus clock, and divided by 128 for MVF platform */
+ ftm->clk_ps = PWM_FTMSC_PS128;
+#ifdef CONFIG_MXC_PWM_CPWM
+ ftm->cpwm = 1;
+#endif
+ if (plat_data != NULL) {
+ ftm->pwmo_invert = plat_data->pwmo_invert;
+ ftm->enable_pwm_pad = plat_data->enable_pwm_pad;
+ ftm->disable_pwm_pad = plat_data->disable_pwm_pad;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ ret = -ENODEV;
+ goto err_free_clk;
+ }
+
+ r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "failed to request memory resource\n");
+ ret = -EBUSY;
+ goto err_free_clk;
+ }
+
+ ftm->mmio_base = ioremap(r->start, r->end - r->start + 1);
+ if (ftm->mmio_base == NULL) {
+ dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ ret = -ENODEV;
+ goto err_free_mem;
+ }
+
+ if (mvf_ftm_pwm_add_channels(ftm, 8))
+ goto err_free_mem;
+
+ dev_info(&pdev->dev, "added 8 PWM channels\n");
+
+ platform_set_drvdata(pdev, ftm);
+
+ return 0;
+err_free_mem:
+ release_mem_region(r->start, r->end - r->start + 1);
+err_free_clk:
+ clk_put(ftm->clk);
+err_free:
+ kfree(ftm);
+ return ret;
+}
+
+static int __devexit mvf_ftm_pwm_remove(struct platform_device *pdev)
+{
+ struct ftm_device *ftm;
+ struct resource *r;
+
+ ftm = platform_get_drvdata(pdev);
+ if (ftm == NULL)
+ return -ENODEV;
+
+ mvf_ftm_pwm_remove_channels(ftm);
+
+ iounmap(ftm->mmio_base);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(r->start, r->end - r->start + 1);
+
+ clk_put(ftm->clk);
+
+ kfree(ftm);
+ return 0;
+}
+
+static struct platform_driver mvf_ftm_pwm_driver = {
+ .driver = {
+ .name = "mvf_ftm_pwm",
+ },
+ .probe = mvf_ftm_pwm_probe,
+ .remove = __devexit_p(mvf_ftm_pwm_remove),
+};
+
+static int __init mvf_ftm_pwm_init(void)
+{
+ return platform_driver_register(&mvf_ftm_pwm_driver);
+}
+arch_initcall(mvf_ftm_pwm_init);
+
+static void __exit mvf_ftm_pwm_exit(void)
+{
+ platform_driver_unregister(&mvf_ftm_pwm_driver);
+}
+module_exit(mvf_ftm_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
+MODULE_DESCRIPTION("FlexTimer PWM driver");
diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index 0e2a2a1d13e9..877a78beb2a0 100755
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -326,7 +326,8 @@ struct imx_mxc_pwm_data {
};
struct platform_device *__init imx_add_mxc_pwm(
const struct imx_mxc_pwm_data *data);
-
+struct platform_device *__init imx_add_mvf_ftm_pwm(
+ const struct imx_mxc_pwm_data *data);
/* mxc_rtc */
struct imx_mxc_rtc_data {
resource_size_t iobase;