@@ -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
@@ -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
new file mode 100644
@@ -0,0 +1,160 @@
+/*
+ * 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 <linux/delay.h>
+#include <mach/davinci_pwm.h>
+
+#define CAPTURE_3_REG 0x10
+#define CAPTURE_4_REG 0x14
+#define CAPTURE_CTRL2_REG 0x2A
+
+#define ECAPCTRL2_MODESL_ECAP BIT(9)
+#define ECAPCTRL2_SYNCOSEL_DISABLE (0x3 << 6)
+#define ECAPCTRL2_TSCTRSTOP_FREERUN BIT(4)
+
+/*
+ * ecap_config_pwm - configures the eCAP Module for the PWM output with given
+ * period and duty cycle.
+ */
+
+static int ecap_config_pwm(struct pwm_device *pwm, unsigned int period_cycles,
+ unsigned int duty_cycle)
+{
+ clk_enable(pwm->clk);
+
+ __raw_writew(ECAPCTRL2_MODESL_ECAP | ECAPCTRL2_SYNCOSEL_DISABLE |
+ ECAPCTRL2_TSCTRSTOP_FREERUN, 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);
+ mdelay(10);
+
+ clk_disable(pwm->clk);
+ return 0;
+}
+
+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;
+ }
+
+ 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");
@@ -23,6 +23,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;
};