diff mbox

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

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

Commit Message

Sugumar Natarajan Sept. 20, 2010, 7:41 a.m. UTC
None
diff mbox

Patch

diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig
index 9aca60c..c9634c7 100644
--- a/arch/arm/mach-davinci/Kconfig
+++ b/arch/arm/mach-davinci/Kconfig
@@ -230,6 +230,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..daab87f
--- /dev/null
+++ b/arch/arm/mach-davinci/ecap.c
@@ -0,0 +1,181 @@ 
+/*
+ * 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 ECAPCTRL2_MODESL_ECAP		BIT(9)
+#define ECAPCTRL2_SYNCOSEL_DISABLE	(0x3 << 6)
+#define ECAPCTRL2_TSCTRSTOP_FREERUN	BIT(4)
+
+static struct private {
+	int duty_val;
+} pv_data;
+
+/*
+ * ecap_pwm_config - configures the eCAP Module for the PWM output with given
+ * period and duty cycle.
+ */
+
+static int ecap_pwm_config(struct pwm_device *pwm, unsigned int period_cycles,
+			unsigned int duty_cycle)
+{
+
+	struct private *private_data = (struct private *)(pwm->private);
+
+	__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;
+
+	private_data->duty_val = duty_cycle;
+	return 0;
+}
+
+int ecap_pwm_enable(struct pwm_device *pwm)
+{
+	struct private *private_data = (struct private *)(pwm->private);
+
+	__raw_writel(private_data->duty_val, pwm->mmio_base
+		+ CAPTURE_4_REG);
+	return 0;
+}
+
+void ecap_pwm_disable(struct pwm_device *pwm)
+{
+	__raw_writel(0, 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_pwm_config;
+	pwm->pwm_enable = ecap_pwm_enable;
+	pwm->pwm_disable = ecap_pwm_disable;
+	pwm->private = &pv_data;
+
+	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);
+	clk_enable(pwm->clk);
+	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_disable(pwm->clk);
+	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 5762881..e6db614 100644
--- a/arch/arm/mach-davinci/include/mach/davinci_pwm.h
+++ b/arch/arm/mach-davinci/include/mach/davinci_pwm.h
@@ -18,12 +18,14 @@ 
 struct pwm_device {
 	struct list_head	node;
 	struct platform_device	*pdev;
+	void			*private;
 	int (*pwm_enable)(struct pwm_device *pwm);
 	void (*pwm_disable)(struct pwm_device *pwm);
 	int (*pwm_config_device)(struct pwm_device *pwm,
 		unsigned int period, unsigned int dutycycle);
 	const char	*label;
 	struct clk	*clk;
+	void __iomem    *mmio_base;
 	unsigned int	use_count;
 	unsigned int	pwm_id;
 };