summary refs log tree commit diff
path: root/arch/arm/mach-ep93xx
diff options
context:
space:
mode:
authorHartley Sweeten <hartleys@visionengravers.com>2009-07-29 22:41:06 +0100
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-07-30 17:45:43 +0100
commitef12379f205bed7e92434e12ddd44e62d13bebe1 (patch)
treec372f5741826e7fbdaec8eac86191b52e3e621af /arch/arm/mach-ep93xx
parent3aa7a9a3cf8774f6701b1903a6353f9545f561ce (diff)
downloadlinux-ef12379f205bed7e92434e12ddd44e62d13bebe1.tar.gz
ARM: 5628/1: ep93xx: Introduce Pulse Width Modulator (PWM) driver
The EP93xx features two PWMs (one on the EP9307) with the following
features:

  * Configurable dual output
  * Separate input clocks for each PWM output
  * 16-bit resolution
  * Programmable pulse width (duty cycle), interval (frequency), and
    polarity

This adds the necessary core support as well as the driver.  A sysfs
interface is provided to control the PWM outputs.

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Acked-by: Ryan Mallon <ryan@bluewatersys.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-ep93xx')
-rw-r--r--arch/arm/mach-ep93xx/clock.c4
-rw-r--r--arch/arm/mach-ep93xx/core.c85
-rw-r--r--arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h1
-rw-r--r--arch/arm/mach-ep93xx/include/mach/platform.h4
4 files changed, 94 insertions, 0 deletions
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
index b6b53447b1b4..3dd0e2a23095 100644
--- a/arch/arm/mach-ep93xx/clock.c
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -72,6 +72,9 @@ static struct clk clk_keypad = {
 	.enable_mask	= EP93XX_SYSCON_KEYTCHCLKDIV_KEN,
 	.set_rate	= set_keytchclk_rate,
 };
+static struct clk clk_pwm = {
+	.rate		= EP93XX_EXT_CLK_RATE,
+};
 
 /* DMA Clocks */
 static struct clk clk_m2p0 = {
@@ -137,6 +140,7 @@ static struct clk_lookup clocks[] = {
 	INIT_CK(NULL,			"pll2",		&clk_pll2),
 	INIT_CK("ep93xx-ohci",		NULL,		&clk_usb_host),
 	INIT_CK("ep93xx-keypad",	NULL,		&clk_keypad),
+	INIT_CK(NULL,			"pwm_clk",	&clk_pwm),
 	INIT_CK(NULL,			"m2p0",		&clk_m2p0),
 	INIT_CK(NULL,			"m2p1",		&clk_m2p1),
 	INIT_CK(NULL,			"m2p2",		&clk_m2p2),
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 2b58df0184c1..40755298fa33 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -597,6 +597,91 @@ static struct platform_device ep93xx_leds = {
 };
 
 
+/*************************************************************************
+ * EP93xx pwm peripheral handling
+ *************************************************************************/
+static struct resource ep93xx_pwm0_resource[] = {
+	{
+		.start	= EP93XX_PWM_PHYS_BASE,
+		.end	= EP93XX_PWM_PHYS_BASE + 0x10 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device ep93xx_pwm0_device = {
+	.name		= "ep93xx-pwm",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(ep93xx_pwm0_resource),
+	.resource	= ep93xx_pwm0_resource,
+};
+
+static struct resource ep93xx_pwm1_resource[] = {
+	{
+		.start	= EP93XX_PWM_PHYS_BASE + 0x20,
+		.end	= EP93XX_PWM_PHYS_BASE + 0x30 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device ep93xx_pwm1_device = {
+	.name		= "ep93xx-pwm",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(ep93xx_pwm1_resource),
+	.resource	= ep93xx_pwm1_resource,
+};
+
+void __init ep93xx_register_pwm(int pwm0, int pwm1)
+{
+	if (pwm0)
+		platform_device_register(&ep93xx_pwm0_device);
+
+	/* NOTE: EP9307 does not have PWMOUT1 (pin EGPIO14) */
+	if (pwm1)
+		platform_device_register(&ep93xx_pwm1_device);
+}
+
+int ep93xx_pwm_acquire_gpio(struct platform_device *pdev)
+{
+	int err;
+
+	if (pdev->id == 0) {
+		err = 0;
+	} else if (pdev->id == 1) {
+		err = gpio_request(EP93XX_GPIO_LINE_EGPIO14,
+				   dev_name(&pdev->dev));
+		if (err)
+			return err;
+		err = gpio_direction_output(EP93XX_GPIO_LINE_EGPIO14, 0);
+		if (err)
+			goto fail;
+
+		/* PWM 1 output on EGPIO[14] */
+		ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_PONG);
+	} else {
+		err = -ENODEV;
+	}
+
+	return err;
+
+fail:
+	gpio_free(EP93XX_GPIO_LINE_EGPIO14);
+	return err;
+}
+EXPORT_SYMBOL(ep93xx_pwm_acquire_gpio);
+
+void ep93xx_pwm_release_gpio(struct platform_device *pdev)
+{
+	if (pdev->id == 1) {
+		gpio_direction_input(EP93XX_GPIO_LINE_EGPIO14);
+		gpio_free(EP93XX_GPIO_LINE_EGPIO14);
+
+		/* EGPIO[14] used for GPIO */
+		ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_PONG);
+	}
+}
+EXPORT_SYMBOL(ep93xx_pwm_release_gpio);
+
+
 extern void ep93xx_gpio_init(void);
 
 void __init ep93xx_init_devices(void)
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
index a11ae779baa0..ea78e908fc82 100644
--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
@@ -147,6 +147,7 @@
 #define EP93XX_ADC_BASE			EP93XX_APB_IOMEM(0x00100000)
 #define EP93XX_TOUCHSCREEN_BASE		EP93XX_APB_IOMEM(0x00100000)
 
+#define EP93XX_PWM_PHYS_BASE		(EP93XX_APB_PHYS_BASE + 0x00110000)
 #define EP93XX_PWM_BASE			EP93XX_APB_IOMEM(0x00110000)
 
 #define EP93XX_RTC_PHYS_BASE		(EP93XX_APB_PHYS_BASE + 0x00120000)
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
index 0af0a3ba7047..5f5fa6574d34 100644
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -5,6 +5,7 @@
 #ifndef __ASSEMBLY__
 
 struct i2c_board_info;
+struct platform_device;
 
 struct ep93xx_eth_data
 {
@@ -32,6 +33,9 @@ static inline void ep93xx_devcfg_clear_bits(unsigned int bits)
 
 void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr);
 void ep93xx_register_i2c(struct i2c_board_info *devices, int num);
+void ep93xx_register_pwm(int pwm0, int pwm1);
+int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
+void ep93xx_pwm_release_gpio(struct platform_device *pdev);
 
 void ep93xx_init_devices(void);
 extern struct sys_timer ep93xx_timer;