From ef12379f205bed7e92434e12ddd44e62d13bebe1 Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Wed, 29 Jul 2009 22:41:06 +0100 Subject: 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 Signed-off-by: H Hartley Sweeten Acked-by: Ryan Mallon Signed-off-by: Russell King --- arch/arm/mach-ep93xx/clock.c | 4 ++ arch/arm/mach-ep93xx/core.c | 85 +++++++++++++++++++++++++ arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 1 + arch/arm/mach-ep93xx/include/mach/platform.h | 4 ++ 4 files changed, 94 insertions(+) (limited to 'arch') 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; -- cgit v1.2.3