From 02239f0a4264608686cc0015d906c7b2dead89df Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Wed, 8 Jul 2009 02:00:49 +0100 Subject: [ARM] 5577/2: ep93xx: syscon locked register functions Add core functions to handle writes to the ep93xx software locked registers. There are a number of registers in the EP93xx System Controller that require a write to the software lock register before they can be updated. This patch adds a number of exported functions to the ep93xx core that handle this access. The software locked clock divider registers, VidClkDiv, MIRClkDiv, I2SClkDiv and KeyTchClkDiv would typically involve writing a specific value to the register. To support this the ep93xx_syscon_swlocked_write() function is provided. For the DeviceCfg register it's more typical to only need to set or clear a single bit. A generic ep93xx_devcfg_set_clear() function is provided to handle both operations. Two inline functions, ep93xx_devcfg_set_bits() and ep93xx_devcfg_clear_bits() are also provided to improve code readability. In addition, the remaining bits in the System Controller Device Config Register have been documented and the previously defined names shortened. All code paths that use this functionality have been updated except for arch/arm/kernel/crunch.c. That code is in a context switch path, which is not reentrant, so it is safe against itself. Cc: Lennert Buytenhek Cc: Matthias Kaehlcke Signed-off-by: H Hartley Sweeten Acked-by: Ryan Mallon Signed-off-by: Russell King --- arch/arm/mach-ep93xx/core.c | 52 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) (limited to 'arch/arm/mach-ep93xx/core.c') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 204dc5cbd0b8..9399d3af9906 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -384,6 +384,47 @@ void __init ep93xx_init_irq(void) } +/************************************************************************* + * EP93xx System Controller Software Locked register handling + *************************************************************************/ + +/* + * syscon_swlock prevents anything else from writing to the syscon + * block while a software locked register is being written. + */ +static DEFINE_SPINLOCK(syscon_swlock); + +void ep93xx_syscon_swlocked_write(unsigned int val, unsigned int reg) +{ + unsigned long flags; + + spin_lock_irqsave(&syscon_swlock, flags); + + __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); + __raw_writel(val, reg); + + spin_unlock_irqrestore(&syscon_swlock, flags); +} +EXPORT_SYMBOL(ep93xx_syscon_swlocked_write); + +void ep93xx_devcfg_set_clear(unsigned int set_bits, unsigned int clear_bits) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&syscon_swlock, flags); + + val = __raw_readl(EP93XX_SYSCON_DEVCFG); + val |= set_bits; + val &= ~clear_bits; + __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); + __raw_writel(val, EP93XX_SYSCON_DEVCFG); + + spin_unlock_irqrestore(&syscon_swlock, flags); +} +EXPORT_SYMBOL(ep93xx_devcfg_set_clear); + + /************************************************************************* * EP93xx peripheral handling *************************************************************************/ @@ -550,15 +591,8 @@ extern void ep93xx_gpio_init(void); void __init ep93xx_init_devices(void) { - unsigned int v; - - /* - * Disallow access to MaverickCrunch initially. - */ - v = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG); - v &= ~EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE; - __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); - __raw_writel(v, EP93XX_SYSCON_DEVICE_CONFIG); + /* Disallow access to MaverickCrunch initially */ + ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_CPENA); ep93xx_gpio_init(); -- cgit v1.2.3-59-g8ed1b From 583ddafe1787528d08b0026bb3273490a499b730 Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Mon, 6 Jul 2009 17:39:50 +0100 Subject: [ARM] 5592/1: ep93xx: cleanup platform header includes arch/arm/mach-ep93xx/include/mach/hardware.h 1. Properly name the include files so that they are loaded from the directory and not the local directory. 2. Remove including the ts72xx.h header. This header is not generic to the ep93xx platform. It should only be included by the ts72xx specific files that require it. The only two users in the tree are arch/arm/mach-ep93xx/ts72xx.c and drivers/mtd/nand/ts7250.c. arch/arm/mach-ep93xx/include/mach/ts72xx.h 1. should already be included by any user of this header. Doing the include here hides it from being needed by the calling source file. arch/arm/mach-ep93xx/core.c 1. Remove unnecessary headers. They were probably included originally due to cut-and-paste from other files. 2. should be included not arch/arm/mach-ep93xx/adsphere.c arch/arm/mach-ep93xx/edb93xx.c arch/arm/mach-ep93xx/gesbc9312.c arch/arm/mach-ep93xx/micro9.c arch/arm/mach-ep93xx/ts72xx.c 1. Remove unnecessary headers. arch/arm/mach-ep93xx/ts72xx.c 1. Remove unnecessary headers. 2. Add platform specific header . drivers/mtd/nand/ts7250.c 1. should be included not . 2. Add platform specific header . Cc: Ryan Mallon Signed-off-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/mach-ep93xx/adssphere.c | 11 ++++------- arch/arm/mach-ep93xx/core.c | 23 +++-------------------- arch/arm/mach-ep93xx/edb93xx.c | 10 ++++------ arch/arm/mach-ep93xx/gesbc9312.c | 11 ++++------- arch/arm/mach-ep93xx/include/mach/hardware.h | 7 ++----- arch/arm/mach-ep93xx/include/mach/ts72xx.h | 1 - arch/arm/mach-ep93xx/micro9.c | 11 +++-------- arch/arm/mach-ep93xx/ts72xx.c | 15 +++++++-------- drivers/mtd/nand/ts7250.c | 5 ++++- 9 files changed, 31 insertions(+), 63 deletions(-) (limited to 'arch/arm/mach-ep93xx/core.c') diff --git a/arch/arm/mach-ep93xx/adssphere.c b/arch/arm/mach-ep93xx/adssphere.c index 3fbd9b0fbe24..caf6d5154aec 100644 --- a/arch/arm/mach-ep93xx/adssphere.c +++ b/arch/arm/mach-ep93xx/adssphere.c @@ -12,18 +12,15 @@ #include #include -#include -#include -#include -#include -#include #include -#include -#include +#include + #include + #include #include + static struct physmap_flash_data adssphere_flash_data = { .width = 4, }; diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 9399d3af9906..ce10f5b5dfbb 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -16,40 +16,23 @@ #include #include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include #include -#include #include -#include +#include +#include #include #include #include -#include #include #include -#include -#include -#include #include -#include -#include -#include -#include #include #include #include -#include #include diff --git a/arch/arm/mach-ep93xx/edb93xx.c b/arch/arm/mach-ep93xx/edb93xx.c index e9e45b92457e..73145ae5d3fa 100644 --- a/arch/arm/mach-ep93xx/edb93xx.c +++ b/arch/arm/mach-ep93xx/edb93xx.c @@ -26,18 +26,16 @@ #include #include -#include -#include -#include -#include -#include #include -#include #include +#include + #include + #include #include + static struct physmap_flash_data edb93xx_flash_data; static struct resource edb93xx_flash_resource = { diff --git a/arch/arm/mach-ep93xx/gesbc9312.c b/arch/arm/mach-ep93xx/gesbc9312.c index 3bad500b71b6..3da7ca816d19 100644 --- a/arch/arm/mach-ep93xx/gesbc9312.c +++ b/arch/arm/mach-ep93xx/gesbc9312.c @@ -12,18 +12,15 @@ #include #include -#include -#include -#include -#include -#include #include -#include -#include +#include + #include + #include #include + static struct physmap_flash_data gesbc9312_flash_data = { .width = 4, }; diff --git a/arch/arm/mach-ep93xx/include/mach/hardware.h b/arch/arm/mach-ep93xx/include/mach/hardware.h index 587c07bdec5b..349fa7cb72d5 100644 --- a/arch/arm/mach-ep93xx/include/mach/hardware.h +++ b/arch/arm/mach-ep93xx/include/mach/hardware.h @@ -4,14 +4,11 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H -#include "ep93xx-regs.h" +#include +#include #define pcibios_assign_all_busses() 0 -#include "platform.h" - -#include "ts72xx.h" - /* * The EP93xx has two external crystal oscillators. To generate the * required high-frequency clocks, the processor uses two phase-locked- diff --git a/arch/arm/mach-ep93xx/include/mach/ts72xx.h b/arch/arm/mach-ep93xx/include/mach/ts72xx.h index 34ddec081c40..2737666e800e 100644 --- a/arch/arm/mach-ep93xx/include/mach/ts72xx.h +++ b/arch/arm/mach-ep93xx/include/mach/ts72xx.h @@ -70,7 +70,6 @@ #ifndef __ASSEMBLY__ -#include static inline int board_is_ts7200(void) { diff --git a/arch/arm/mach-ep93xx/micro9.c b/arch/arm/mach-ep93xx/micro9.c index 15d6815d78c4..0a313e82fb74 100644 --- a/arch/arm/mach-ep93xx/micro9.c +++ b/arch/arm/mach-ep93xx/micro9.c @@ -9,21 +9,16 @@ * published by the Free Software Foundation. */ -#include -#include -#include #include -#include +#include #include -#include -#include -#include #include #include -#include #include +#include + static struct ep93xx_eth_data micro9_eth_data = { .phy_id = 0x1f, diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c index 7ee024d34829..5255dddd3067 100644 --- a/arch/arm/mach-ep93xx/ts72xx.c +++ b/arch/arm/mach-ep93xx/ts72xx.c @@ -12,19 +12,18 @@ #include #include -#include -#include -#include -#include -#include #include -#include #include -#include +#include +#include + #include +#include + #include -#include #include +#include + static struct map_desc ts72xx_io_desc[] __initdata = { { diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c index 2c410a011317..0f5562aeedc1 100644 --- a/drivers/mtd/nand/ts7250.c +++ b/drivers/mtd/nand/ts7250.c @@ -24,8 +24,11 @@ #include #include #include -#include +#include + #include +#include + #include #include -- cgit v1.2.3-59-g8ed1b From 5396730b3caad500ceba84c9391143cafb5938a8 Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Thu, 9 Jul 2009 23:22:07 +0100 Subject: [ARM] 5598/1: ep93xx: core.c typesafe vic_init The EP93XX_VIC{1/2}_BASE defines are now typesafe due to [ARM] 5573/1: ep93xx: ensure typesafe io. The (void *) cast is not needed when calling vic_init(). Signed-off-by: H Hartley Sweeten Acked-by: Ryan Mallon Signed-off-by: Russell King --- arch/arm/mach-ep93xx/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/arm/mach-ep93xx/core.c') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index ce10f5b5dfbb..68ac0545f570 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -345,8 +345,8 @@ void __init ep93xx_init_irq(void) { int gpio_irq; - vic_init((void *)EP93XX_VIC1_BASE, 0, EP93XX_VIC1_VALID_IRQ_MASK, 0); - vic_init((void *)EP93XX_VIC2_BASE, 32, EP93XX_VIC2_VALID_IRQ_MASK, 0); + vic_init(EP93XX_VIC1_BASE, 0, EP93XX_VIC1_VALID_IRQ_MASK, 0); + vic_init(EP93XX_VIC2_BASE, 32, EP93XX_VIC2_VALID_IRQ_MASK, 0); for (gpio_irq = gpio_to_irq(0); gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) { -- cgit v1.2.3-59-g8ed1b From 5b1c3c858cff4da95b9f7091eef424e706784cab Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Mon, 13 Jul 2009 19:50:10 +0100 Subject: [ARM] 5600/1: ep93xx: core.c remove cast when copying dev_addr The MAC address for the ep93xx ethernet driver can be optionally copied from registers in the controller when booting. Due to [ARM] 5573/1: ep93xx: ensure typesafe io, the cast for the source address is no longer needed. EP93XX_ETHERNET_BASE is typed as a (void __iomem __force *) so memcpy_fromio() needs to be used instead of memcpy(). Signed-off-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/mach-ep93xx/core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'arch/arm/mach-ep93xx/core.c') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 68ac0545f570..4c38941a92c7 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -541,10 +541,8 @@ static struct platform_device ep93xx_eth_device = { void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr) { - if (copy_addr) { - memcpy(data->dev_addr, - (void *)(EP93XX_ETHERNET_BASE + 0x50), 6); - } + if (copy_addr) + memcpy_fromio(data->dev_addr, EP93XX_ETHERNET_BASE + 0x50, 6); ep93xx_eth_data = *data; platform_device_register(&ep93xx_eth_device); -- cgit v1.2.3-59-g8ed1b From fbeeea5386ab213bd55b223f3a75c823fccd6df5 Mon Sep 17 00:00:00 2001 From: Ryan Mallon Date: Wed, 15 Jul 2009 21:51:59 +0100 Subject: [ARM] 5607/1: ep93xx: Use __iomem pointer on syscon write function Change the reg argument of the ep93xx_syscon_swlocked_write function to be an __iomem pointer. Fixes a number of build warnings. Signed-off-by: Ryan Mallon Acked-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/mach-ep93xx/core.c | 2 +- arch/arm/mach-ep93xx/include/mach/platform.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/arm/mach-ep93xx/core.c') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 4c38941a92c7..b390c35f8bc1 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -377,7 +377,7 @@ void __init ep93xx_init_irq(void) */ static DEFINE_SPINLOCK(syscon_swlock); -void ep93xx_syscon_swlocked_write(unsigned int val, unsigned int reg) +void ep93xx_syscon_swlocked_write(unsigned int val, void __iomem *reg) { unsigned long flags; diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h index fb5e59a3ea04..0af0a3ba7047 100644 --- a/arch/arm/mach-ep93xx/include/mach/platform.h +++ b/arch/arm/mach-ep93xx/include/mach/platform.h @@ -17,7 +17,7 @@ void ep93xx_init_irq(void); void ep93xx_init_time(unsigned long); /* EP93xx System Controller software locked register write */ -void ep93xx_syscon_swlocked_write(unsigned int val, unsigned int reg); +void ep93xx_syscon_swlocked_write(unsigned int val, void __iomem *reg); void ep93xx_devcfg_set_clear(unsigned int set_bits, unsigned int clear_bits); static inline void ep93xx_devcfg_set_bits(unsigned int bits) -- cgit v1.2.3-59-g8ed1b From 3aa7a9a3cf8774f6701b1903a6353f9545f561ce Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Mon, 20 Jul 2009 18:22:36 +0100 Subject: [ARM] 5612/1: ep93xx: add platform LEDs The EP93xx has two gpio pins specifically assigned to drive external LEDs. Add core support for these LEDs. On the EDB93xx development boards, the rdled is connected to an external reset circuit. Turning this led on for an extended amount of time will cause the circuit to issue a manual reset. Refer to Cirrus App Note AN258 for more information. http://www.cirrus.com/en/pubs/appNote/AN258REV2.pdf This led can be safely used as the system heartbeat with the ledtrig-heartbeat driver. echo heartbeat > /sys/class/leds/platform:rdled/trigger The grled can be used for any desired purpose. Tested-by: Matthieu Crapet Signed-off-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/mach-ep93xx/core.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'arch/arm/mach-ep93xx/core.c') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index b390c35f8bc1..2b58df0184c1 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -568,6 +569,34 @@ void __init ep93xx_register_i2c(struct i2c_board_info *devices, int num) platform_device_register(&ep93xx_i2c_device); } + +/************************************************************************* + * EP93xx LEDs + *************************************************************************/ +static struct gpio_led ep93xx_led_pins[] = { + { + .name = "platform:grled", + .gpio = EP93XX_GPIO_LINE_GRLED, + }, { + .name = "platform:rdled", + .gpio = EP93XX_GPIO_LINE_RDLED, + }, +}; + +static struct gpio_led_platform_data ep93xx_led_data = { + .num_leds = ARRAY_SIZE(ep93xx_led_pins), + .leds = ep93xx_led_pins, +}; + +static struct platform_device ep93xx_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &ep93xx_led_data, + }, +}; + + extern void ep93xx_gpio_init(void); void __init ep93xx_init_devices(void) @@ -583,4 +612,5 @@ void __init ep93xx_init_devices(void) platform_device_register(&ep93xx_rtc_device); platform_device_register(&ep93xx_ohci_device); + platform_device_register(&ep93xx_leds); } -- cgit v1.2.3-59-g8ed1b 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 + drivers/misc/Kconfig | 13 + drivers/misc/Makefile | 1 + drivers/misc/ep93xx_pwm.c | 384 ++++++++++++++++++++++++ 7 files changed, 492 insertions(+) create mode 100644 drivers/misc/ep93xx_pwm.c (limited to 'arch/arm/mach-ep93xx/core.c') 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; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 68ab39d7cb35..df1f86b5c83e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -233,6 +233,19 @@ config ISL29003 This driver can also be built as a module. If so, the module will be called isl29003. +config EP93XX_PWM + tristate "EP93xx PWM support" + depends on ARCH_EP93XX + help + This option enables device driver support for the PWM channels + on the Cirrus EP93xx processors. The EP9307 chip only has one + PWM channel all the others have two, the second channel is an + alternate function of the EGPIO14 pin. A sysfs interface is + provided to control the PWM channels. + + To compile this driver as a module, choose M here: the module will + be called ep93xx_pwm. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 36f733cd60e6..f982d2ecfde7 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_ISL29003) += isl29003.o +obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o obj-$(CONFIG_C2PORT) += c2port/ obj-y += eeprom/ obj-y += cb710/ diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c new file mode 100644 index 000000000000..ba4694169d79 --- /dev/null +++ b/drivers/misc/ep93xx_pwm.c @@ -0,0 +1,384 @@ +/* + * Simple PWM driver for EP93XX + * + * (c) Copyright 2009 Matthieu Crapet + * (c) Copyright 2009 H Hartley Sweeten + * + * 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; either version + * 2 of the License, or (at your option) any later version. + * + * EP9307 has only one channel: + * - PWMOUT + * + * EP9301/02/12/15 have two channels: + * - PWMOUT + * - PWMOUT1 (alternate function for EGPIO14) + */ + +#include +#include +#include +#include +#include + +#include + +#define EP93XX_PWMx_TERM_COUNT 0x00 +#define EP93XX_PWMx_DUTY_CYCLE 0x04 +#define EP93XX_PWMx_ENABLE 0x08 +#define EP93XX_PWMx_INVERT 0x0C + +#define EP93XX_PWM_MAX_COUNT 0xFFFF + +struct ep93xx_pwm { + void __iomem *mmio_base; + struct clk *clk; + u32 duty_percent; +}; + +static inline void ep93xx_pwm_writel(struct ep93xx_pwm *pwm, + unsigned int val, unsigned int off) +{ + __raw_writel(val, pwm->mmio_base + off); +} + +static inline unsigned int ep93xx_pwm_readl(struct ep93xx_pwm *pwm, + unsigned int off) +{ + return __raw_readl(pwm->mmio_base + off); +} + +static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm *pwm, u16 value) +{ + ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_TERM_COUNT); +} + +static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) +{ + return ep93xx_pwm_readl(pwm, EP93XX_PWMx_TERM_COUNT); +} + +static inline void ep93xx_pwm_write_dc(struct ep93xx_pwm *pwm, u16 value) +{ + ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_DUTY_CYCLE); +} + +static inline void ep93xx_pwm_enable(struct ep93xx_pwm *pwm) +{ + ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_ENABLE); +} + +static inline void ep93xx_pwm_disable(struct ep93xx_pwm *pwm) +{ + ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_ENABLE); +} + +static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) +{ + return ep93xx_pwm_readl(pwm, EP93XX_PWMx_ENABLE) & 0x1; +} + +static inline void ep93xx_pwm_invert(struct ep93xx_pwm *pwm) +{ + ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_INVERT); +} + +static inline void ep93xx_pwm_normal(struct ep93xx_pwm *pwm) +{ + ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_INVERT); +} + +static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm) +{ + return ep93xx_pwm_readl(pwm, EP93XX_PWMx_INVERT) & 0x1; +} + +/* + * /sys/devices/platform/ep93xx-pwm.N + * /min_freq read-only minimum pwm output frequency + * /max_req read-only maximum pwm output frequency + * /freq read-write pwm output frequency (0 = disable output) + * /duty_percent read-write pwm duty cycle percent (1..99) + * /invert read-write invert pwm output + */ + +static ssize_t ep93xx_pwm_get_min_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + unsigned long rate = clk_get_rate(pwm->clk); + + return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1)); +} + +static ssize_t ep93xx_pwm_get_max_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + unsigned long rate = clk_get_rate(pwm->clk); + + return sprintf(buf, "%ld\n", rate / 2); +} + +static ssize_t ep93xx_pwm_get_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + + if (ep93xx_pwm_is_enabled(pwm)) { + unsigned long rate = clk_get_rate(pwm->clk); + u16 term = ep93xx_pwm_read_tc(pwm); + + return sprintf(buf, "%ld\n", rate / (term + 1)); + } else { + return sprintf(buf, "disabled\n"); + } +} + +static ssize_t ep93xx_pwm_set_freq(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err) + return -EINVAL; + + if (val == 0) { + ep93xx_pwm_disable(pwm); + } else if (val <= (clk_get_rate(pwm->clk) / 2)) { + u32 term, duty; + + val = (clk_get_rate(pwm->clk) / val) - 1; + if (val > EP93XX_PWM_MAX_COUNT) + val = EP93XX_PWM_MAX_COUNT; + if (val < 1) + val = 1; + + term = ep93xx_pwm_read_tc(pwm); + duty = ((val + 1) * pwm->duty_percent / 100) - 1; + + /* If pwm is running, order is important */ + if (val > term) { + ep93xx_pwm_write_tc(pwm, val); + ep93xx_pwm_write_dc(pwm, duty); + } else { + ep93xx_pwm_write_dc(pwm, duty); + ep93xx_pwm_write_tc(pwm, val); + } + + if (!ep93xx_pwm_is_enabled(pwm)) + ep93xx_pwm_enable(pwm); + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", pwm->duty_percent); +} + +static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err) + return -EINVAL; + + if (val > 0 && val < 100) { + u32 term = ep93xx_pwm_read_tc(pwm); + ep93xx_pwm_write_dc(pwm, ((term + 1) * val / 100) - 1); + pwm->duty_percent = val; + return count; + } + + return -EINVAL; +} + +static ssize_t ep93xx_pwm_get_invert(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", ep93xx_pwm_is_inverted(pwm)); +} + +static ssize_t ep93xx_pwm_set_invert(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err) + return -EINVAL; + + if (val == 0) + ep93xx_pwm_normal(pwm); + else if (val == 1) + ep93xx_pwm_invert(pwm); + else + return -EINVAL; + + return count; +} + +static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); +static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); +static DEVICE_ATTR(freq, S_IWUGO | S_IRUGO, + ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); +static DEVICE_ATTR(duty_percent, S_IWUGO | S_IRUGO, + ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); +static DEVICE_ATTR(invert, S_IWUGO | S_IRUGO, + ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); + +static struct attribute *ep93xx_pwm_attrs[] = { + &dev_attr_min_freq.attr, + &dev_attr_max_freq.attr, + &dev_attr_freq.attr, + &dev_attr_duty_percent.attr, + &dev_attr_invert.attr, + NULL +}; + +static const struct attribute_group ep93xx_pwm_sysfs_files = { + .attrs = ep93xx_pwm_attrs, +}; + +static int __init ep93xx_pwm_probe(struct platform_device *pdev) +{ + struct ep93xx_pwm *pwm; + struct resource *res; + int err; + + err = ep93xx_pwm_acquire_gpio(pdev); + if (err) + return err; + + pwm = kzalloc(sizeof(struct ep93xx_pwm), GFP_KERNEL); + if (!pwm) { + err = -ENOMEM; + goto fail_no_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + err = -ENXIO; + goto fail_no_mem_resource; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + err = -EBUSY; + goto fail_no_mem_resource; + } + + pwm->mmio_base = ioremap(res->start, resource_size(res)); + if (pwm->mmio_base == NULL) { + err = -ENXIO; + goto fail_no_ioremap; + } + + err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); + if (err) + goto fail_no_sysfs; + + pwm->clk = clk_get(&pdev->dev, "pwm_clk"); + if (IS_ERR(pwm->clk)) { + err = PTR_ERR(pwm->clk); + goto fail_no_clk; + } + + pwm->duty_percent = 50; + + platform_set_drvdata(pdev, pwm); + + /* disable pwm at startup. Avoids zero value. */ + ep93xx_pwm_disable(pwm); + ep93xx_pwm_write_tc(pwm, EP93XX_PWM_MAX_COUNT); + ep93xx_pwm_write_dc(pwm, EP93XX_PWM_MAX_COUNT / 2); + + clk_enable(pwm->clk); + + return 0; + +fail_no_clk: + sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); +fail_no_sysfs: + iounmap(pwm->mmio_base); +fail_no_ioremap: + release_mem_region(res->start, resource_size(res)); +fail_no_mem_resource: + kfree(pwm); +fail_no_mem: + ep93xx_pwm_release_gpio(pdev); + return err; +} + +static int __exit ep93xx_pwm_remove(struct platform_device *pdev) +{ + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ep93xx_pwm_disable(pwm); + clk_disable(pwm->clk); + clk_put(pwm->clk); + platform_set_drvdata(pdev, NULL); + sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); + iounmap(pwm->mmio_base); + release_mem_region(res->start, resource_size(res)); + kfree(pwm); + ep93xx_pwm_release_gpio(pdev); + + return 0; +} + +static struct platform_driver ep93xx_pwm_driver = { + .driver = { + .name = "ep93xx-pwm", + .owner = THIS_MODULE, + }, + .remove = __exit_p(ep93xx_pwm_remove), +}; + +static int __init ep93xx_pwm_init(void) +{ + return platform_driver_probe(&ep93xx_pwm_driver, ep93xx_pwm_probe); +} + +static void __exit ep93xx_pwm_exit(void) +{ + platform_driver_unregister(&ep93xx_pwm_driver); +} + +module_init(ep93xx_pwm_init); +module_exit(ep93xx_pwm_exit); + +MODULE_AUTHOR("Matthieu Crapet , " + "H Hartley Sweeten "); +MODULE_DESCRIPTION("EP93xx PWM driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-pwm"); -- cgit v1.2.3-59-g8ed1b From af1057abd7d5f97e17ab96e34d1920746188ddcb Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 3 Aug 2009 11:57:20 +0100 Subject: ARM: 5635/1: Use DIV_ROUND_CLOSEST The kernel.h macro DIV_ROUND_CLOSEST performs the computation (x + d/2)/d but is perhaps more readable. The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @haskernel@ @@ @depends on haskernel@ expression x,__divisor; @@ - (((x) + ((__divisor) / 2)) / (__divisor)) + DIV_ROUND_CLOSEST(x,__divisor) // Signed-off-by: Julia Lawall Acked-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/mach-ep93xx/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/arm/mach-ep93xx/core.c') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 40755298fa33..16b92c37ec99 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -82,7 +82,7 @@ void __init ep93xx_map_io(void) */ static unsigned int last_jiffy_time; -#define TIMER4_TICKS_PER_JIFFY ((CLOCK_TICK_RATE + (HZ/2)) / HZ) +#define TIMER4_TICKS_PER_JIFFY DIV_ROUND_CLOSEST(CLOCK_TICK_RATE, HZ) static irqreturn_t ep93xx_timer_interrupt(int irq, void *dev_id) { -- cgit v1.2.3-59-g8ed1b