OMAPL138/DA850 contains three instances of eCAP module. Each eCAP
module has one dedicated pin that can be used either in capture
mode(input) or in PWM mode. For more information on eCAP module
operation, please refer to the following url.

        http://focus.ti.com/lit/ug/sprufl2a/sprufl2a.pdf

This patch adds eCAP driver support for PWM signal generation.

Signed-off-by: Sugumar Natarajan <[email protected]>
---
 Since v1, following changes have been made:
 1. request_mem_region is called before ioremap (ecap.c)
 2. release_mem_region is done during pwm removal (ecap.c)
 3. prompt string and help text are included in Kconfig
 
 arch/arm/mach-davinci/Kconfig                    |    9 ++
 arch/arm/mach-davinci/Makefile                   |    3 +
 arch/arm/mach-davinci/ecap.c                     |  155 ++++++++++++++++++++++
 arch/arm/mach-davinci/include/mach/davinci_pwm.h |    1 +
 4 files changed, 168 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-davinci/ecap.c

diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig
index 9aca60c..8192866 100644
--- a/arch/arm/mach-davinci/Kconfig
+++ b/arch/arm/mach-davinci/Kconfig
@@ -40,6 +40,7 @@ config ARCH_DAVINCI_DA850
        select CP_INTC
        select ARCH_DAVINCI_DA8XX
        select ARCH_HAS_CPUFREQ
+       select HAVE_PWM
 
 config ARCH_DAVINCI_DA8XX
        select CPU_ARM926T
@@ -230,6 +231,14 @@ config DAVINCI_RESET_CLOCKS
          probably do not want this option enabled until your
          device drivers work properly.
 
+config DAVINCI_ECAP_PWM
+       bool "eCAP driver support for PWM control"
+       depends on ARCH_DAVINCI_DA8XX
+       depends on HAVE_PWM
+       default n
+       help
+         Say Y to select the eCAP module for PWM control.
+
 endmenu
 
 endif
diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile
index 90ca821..90bd88e 100644
--- a/arch/arm/mach-davinci/Makefile
+++ b/arch/arm/mach-davinci/Makefile
@@ -42,3 +42,6 @@ obj-$(CONFIG_SUSPEND)                 += pm.o sleep.o
 
 # Generic PWM control support
 obj-$(CONFIG_HAVE_PWM)                 += davinci_pwm.o
+
+# eCAP driver support for PWM
+obj-$(CONFIG_DAVINCI_ECAP_PWM)         += ecap.o
diff --git a/arch/arm/mach-davinci/ecap.c b/arch/arm/mach-davinci/ecap.c
new file mode 100644
index 0000000..5127174
--- /dev/null
+++ b/arch/arm/mach-davinci/ecap.c
@@ -0,0 +1,155 @@
+/*
+ * DA850/OMAP-L138 eCAP driver for PWM output generation
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <mach/davinci_pwm.h>
+
+#define CAPTURE_3_REG          0x10
+#define CAPTURE_4_REG          0x14
+#define CAPTURE_CTRL2_REG      0x2A
+
+#define ECAP_CTRL2_APWMPOLL    BIT(10)
+#define ECAP_CTRL2_MODESL      BIT(9)
+#define ECAP_CTRL2_SWSYNC      BIT(8)
+#define ECAP_CTRL2_SYNCO_SEL   (BIT(7) | BIT(6))
+#define ECAP_CTRL2_SYNCI_EN    BIT(5)
+#define ECAP_CTRL2_TSCTRSTOP   BIT(4)
+
+/*
+ * ecap_config_pwm - configures the eCAP Module for the PWM output with given
+ * period and duty cycle.
+ */
+
+static void ecap_config_pwm(struct pwm_device *pwm, unsigned int period_cycles,
+                       unsigned int duty_cycle)
+{
+       __raw_writew(ECAP_CTRL2_MODESL | ECAP_CTRL2_SYNCO_SEL |
+               ECAP_CTRL2_TSCTRSTOP, pwm->mmio_base + CAPTURE_CTRL2_REG);
+       __raw_writel(period_cycles, pwm->mmio_base + CAPTURE_3_REG);
+
+       /* 100% duty cycle is obtained when the duty cycle value is one
+        * greater than period ie duty cycle = period + 1
+        */
+       if (duty_cycle == period_cycles)
+               duty_cycle = duty_cycle + 1;
+
+       __raw_writel(duty_cycle, pwm->mmio_base + CAPTURE_4_REG);
+}
+
+static int __devinit ecap_probe(struct platform_device *pdev)
+{
+       struct pwm_device *pwm = NULL;
+       struct resource *r;
+       int ret = 0;
+
+       pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+       if (!pwm) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       pwm->clk = clk_get(&pdev->dev, "ecap");
+       if (IS_ERR(pwm->clk)) {
+               ret = PTR_ERR(pwm->clk);
+               goto err_free;
+       }
+
+       pwm->pwm_id = pdev->id;
+       pwm->pdev = pdev;
+       pwm->pwm_config_device = ecap_config_pwm;
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "no memory resource defined\n");
+               ret = -ENODEV;
+               goto err_free_clk;
+       }
+
+       r = request_mem_region(r->start, resource_size(r), pdev->name);
+       if (!r) {
+               dev_err(&pdev->dev, "failed to request memory resource\n");
+               ret = -EBUSY;
+               goto err_free_clk;
+       }
+
+       pwm->mmio_base = ioremap(r->start, resource_size(r));
+       if (!pwm->mmio_base) {
+               dev_err(&pdev->dev, "failed to ioremap() registers\n");
+               ret = -ENODEV;
+               goto err_free_mem;
+       }
+
+       clk_enable(pwm->clk);
+       pwm_add(pwm);
+       platform_set_drvdata(pdev, pwm);
+       return 0;
+
+err_free_mem:
+       release_mem_region(r->start, resource_size(r));
+err_free_clk:
+       clk_put(pwm->clk);
+err_free:
+       kfree(pwm);
+       return ret;
+}
+
+static int __devexit ecap_remove(struct platform_device *pdev)
+{
+       struct pwm_device *pwm;
+       struct resource *r;
+
+       pwm = platform_get_drvdata(pdev);
+       if (!pwm)
+               return -ENODEV;
+
+       pwm_remove(pwm);
+       iounmap(pwm->mmio_base);
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(r->start, resource_size(r));
+       clk_put(pwm->clk);
+       kfree(pwm);
+
+       return 0;
+}
+
+static struct platform_driver ecap_driver = {
+       .driver = {
+               .name   = "ecap",
+               .owner  = THIS_MODULE,
+       },
+       .probe  = ecap_probe,
+       .remove = __devexit_p(ecap_remove),
+};
+
+static int __init ecap_init(void)
+{
+       return platform_driver_register(&ecap_driver);
+}
+
+static void __exit ecap_exit(void)
+{
+       platform_driver_unregister(&ecap_driver);
+}
+
+module_init(ecap_init);
+module_exit(ecap_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-davinci/include/mach/davinci_pwm.h 
b/arch/arm/mach-davinci/include/mach/davinci_pwm.h
index ba2720b..9778a15 100644
--- a/arch/arm/mach-davinci/include/mach/davinci_pwm.h
+++ b/arch/arm/mach-davinci/include/mach/davinci_pwm.h
@@ -22,6 +22,7 @@ struct pwm_device {
                unsigned int period, unsigned int dutycycle);
        const char      *label;
        struct clk      *clk;
+       void __iomem    *mmio_base;
        unsigned int    use_count;
        unsigned int    pwm_id;
 };
-- 
1.5.6

_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to