diff mbox

[v5,2/2] davinci: da850: eCAP driver for PWM signal generation

Message ID 1284460603-24792-1-git-send-email-sugumar@ti.com (mailing list archive)
State Superseded
Headers show

Commit Message

Sugumar Natarajan Sept. 14, 2010, 10:36 a.m. UTC
None
diff mbox

Patch

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..066b489
--- /dev/null
+++ b/arch/arm/mach-davinci/ecap.c
@@ -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");
diff --git a/arch/arm/mach-davinci/include/mach/davinci_pwm.h b/arch/arm/mach-davinci/include/mach/davinci_pwm.h
index 6dd8fb2..db84580 100644
--- a/arch/arm/mach-davinci/include/mach/davinci_pwm.h
+++ b/arch/arm/mach-davinci/include/mach/davinci_pwm.h
@@ -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;
 };