diff options
35 files changed, 1069 insertions, 822 deletions
diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt b/Documentation/devicetree/bindings/pwm/imx-pwm.txt new file mode 100644 index 000000000000..8522bfbccfd7 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | Freescale i.MX PWM controller | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: should be "fsl,<soc>-pwm" | ||
| 5 | - reg: physical base address and length of the controller's registers | ||
| 6 | - #pwm-cells: should be 2. The first cell specifies the per-chip index | ||
| 7 | of the PWM to use and the second cell is the period in nanoseconds. | ||
| 8 | - interrupts: The interrupt for the pwm controller | ||
| 9 | |||
| 10 | Example: | ||
| 11 | |||
| 12 | pwm1: pwm@53fb4000 { | ||
| 13 | #pwm-cells = <2>; | ||
| 14 | compatible = "fsl,imx53-pwm", "fsl,imx27-pwm"; | ||
| 15 | reg = <0x53fb4000 0x4000>; | ||
| 16 | interrupts = <61>; | ||
| 17 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/mxs-pwm.txt b/Documentation/devicetree/bindings/pwm/mxs-pwm.txt index 11963e4d6bc4..9e3f8f1d46a2 100644 --- a/Documentation/devicetree/bindings/pwm/mxs-pwm.txt +++ b/Documentation/devicetree/bindings/pwm/mxs-pwm.txt | |||
| @@ -4,7 +4,7 @@ Required properties: | |||
| 4 | - compatible: should be "fsl,imx23-pwm" | 4 | - compatible: should be "fsl,imx23-pwm" |
| 5 | - reg: physical base address and length of the controller's registers | 5 | - reg: physical base address and length of the controller's registers |
| 6 | - #pwm-cells: should be 2. The first cell specifies the per-chip index | 6 | - #pwm-cells: should be 2. The first cell specifies the per-chip index |
| 7 | of the PWM to use and the second cell is the duty cycle in nanoseconds. | 7 | of the PWM to use and the second cell is the period in nanoseconds. |
| 8 | - fsl,pwm-number: the number of PWM devices | 8 | - fsl,pwm-number: the number of PWM devices |
| 9 | 9 | ||
| 10 | Example: | 10 | Example: |
diff --git a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt index bbbeedb4ec05..01438ecd6628 100644 --- a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt +++ b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt | |||
| @@ -7,7 +7,7 @@ Required properties: | |||
| 7 | - reg: physical base address and length of the controller's registers | 7 | - reg: physical base address and length of the controller's registers |
| 8 | - #pwm-cells: On Tegra the number of cells used to specify a PWM is 2. The | 8 | - #pwm-cells: On Tegra the number of cells used to specify a PWM is 2. The |
| 9 | first cell specifies the per-chip index of the PWM to use and the second | 9 | first cell specifies the per-chip index of the PWM to use and the second |
| 10 | cell is the duty cycle in nanoseconds. | 10 | cell is the period in nanoseconds. |
| 11 | 11 | ||
| 12 | Example: | 12 | Example: |
| 13 | 13 | ||
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 950856bd2e39..43cff70465ab 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt | |||
| @@ -284,3 +284,7 @@ CLOCK | |||
| 284 | PINCTRL | 284 | PINCTRL |
| 285 | devm_pinctrl_get() | 285 | devm_pinctrl_get() |
| 286 | devm_pinctrl_put() | 286 | devm_pinctrl_put() |
| 287 | |||
| 288 | PWM | ||
| 289 | devm_pwm_get() | ||
| 290 | devm_pwm_put() | ||
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt index 554290ebab94..7d2b4c9b544b 100644 --- a/Documentation/pwm.txt +++ b/Documentation/pwm.txt | |||
| @@ -36,7 +36,8 @@ Legacy users can request a PWM device using pwm_request() and free it | |||
| 36 | after usage with pwm_free(). | 36 | after usage with pwm_free(). |
| 37 | 37 | ||
| 38 | New users should use the pwm_get() function and pass to it the consumer | 38 | New users should use the pwm_get() function and pass to it the consumer |
| 39 | device or a consumer name. pwm_put() is used to free the PWM device. | 39 | device or a consumer name. pwm_put() is used to free the PWM device. Managed |
| 40 | variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist. | ||
| 40 | 41 | ||
| 41 | After being requested a PWM has to be configured using: | 42 | After being requested a PWM has to be configured using: |
| 42 | 43 | ||
diff --git a/arch/mips/include/asm/mach-jz4740/platform.h b/arch/mips/include/asm/mach-jz4740/platform.h index 564ab81d6cdc..163e81db880d 100644 --- a/arch/mips/include/asm/mach-jz4740/platform.h +++ b/arch/mips/include/asm/mach-jz4740/platform.h | |||
| @@ -31,6 +31,7 @@ extern struct platform_device jz4740_pcm_device; | |||
| 31 | extern struct platform_device jz4740_codec_device; | 31 | extern struct platform_device jz4740_codec_device; |
| 32 | extern struct platform_device jz4740_adc_device; | 32 | extern struct platform_device jz4740_adc_device; |
| 33 | extern struct platform_device jz4740_wdt_device; | 33 | extern struct platform_device jz4740_wdt_device; |
| 34 | extern struct platform_device jz4740_pwm_device; | ||
| 34 | 35 | ||
| 35 | void jz4740_serial_device_register(void); | 36 | void jz4740_serial_device_register(void); |
| 36 | 37 | ||
diff --git a/arch/mips/include/asm/mach-jz4740/timer.h b/arch/mips/include/asm/mach-jz4740/timer.h index 9baa03ce748c..a7759fb1f73d 100644 --- a/arch/mips/include/asm/mach-jz4740/timer.h +++ b/arch/mips/include/asm/mach-jz4740/timer.h | |||
| @@ -16,7 +16,120 @@ | |||
| 16 | #ifndef __ASM_MACH_JZ4740_TIMER | 16 | #ifndef __ASM_MACH_JZ4740_TIMER |
| 17 | #define __ASM_MACH_JZ4740_TIMER | 17 | #define __ASM_MACH_JZ4740_TIMER |
| 18 | 18 | ||
| 19 | #define JZ_REG_TIMER_STOP 0x0C | ||
| 20 | #define JZ_REG_TIMER_STOP_SET 0x1C | ||
| 21 | #define JZ_REG_TIMER_STOP_CLEAR 0x2C | ||
| 22 | #define JZ_REG_TIMER_ENABLE 0x00 | ||
| 23 | #define JZ_REG_TIMER_ENABLE_SET 0x04 | ||
| 24 | #define JZ_REG_TIMER_ENABLE_CLEAR 0x08 | ||
| 25 | #define JZ_REG_TIMER_FLAG 0x10 | ||
| 26 | #define JZ_REG_TIMER_FLAG_SET 0x14 | ||
| 27 | #define JZ_REG_TIMER_FLAG_CLEAR 0x18 | ||
| 28 | #define JZ_REG_TIMER_MASK 0x20 | ||
| 29 | #define JZ_REG_TIMER_MASK_SET 0x24 | ||
| 30 | #define JZ_REG_TIMER_MASK_CLEAR 0x28 | ||
| 31 | |||
| 32 | #define JZ_REG_TIMER_DFR(x) (((x) * 0x10) + 0x30) | ||
| 33 | #define JZ_REG_TIMER_DHR(x) (((x) * 0x10) + 0x34) | ||
| 34 | #define JZ_REG_TIMER_CNT(x) (((x) * 0x10) + 0x38) | ||
| 35 | #define JZ_REG_TIMER_CTRL(x) (((x) * 0x10) + 0x3C) | ||
| 36 | |||
| 37 | #define JZ_TIMER_IRQ_HALF(x) BIT((x) + 0x10) | ||
| 38 | #define JZ_TIMER_IRQ_FULL(x) BIT(x) | ||
| 39 | |||
| 40 | #define JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN BIT(9) | ||
| 41 | #define JZ_TIMER_CTRL_PWM_ACTIVE_LOW BIT(8) | ||
| 42 | #define JZ_TIMER_CTRL_PWM_ENABLE BIT(7) | ||
| 43 | #define JZ_TIMER_CTRL_PRESCALE_MASK 0x1c | ||
| 44 | #define JZ_TIMER_CTRL_PRESCALE_OFFSET 0x3 | ||
| 45 | #define JZ_TIMER_CTRL_PRESCALE_1 (0 << 3) | ||
| 46 | #define JZ_TIMER_CTRL_PRESCALE_4 (1 << 3) | ||
| 47 | #define JZ_TIMER_CTRL_PRESCALE_16 (2 << 3) | ||
| 48 | #define JZ_TIMER_CTRL_PRESCALE_64 (3 << 3) | ||
| 49 | #define JZ_TIMER_CTRL_PRESCALE_256 (4 << 3) | ||
| 50 | #define JZ_TIMER_CTRL_PRESCALE_1024 (5 << 3) | ||
| 51 | |||
| 52 | #define JZ_TIMER_CTRL_PRESCALER(x) ((x) << JZ_TIMER_CTRL_PRESCALE_OFFSET) | ||
| 53 | |||
| 54 | #define JZ_TIMER_CTRL_SRC_EXT BIT(2) | ||
| 55 | #define JZ_TIMER_CTRL_SRC_RTC BIT(1) | ||
| 56 | #define JZ_TIMER_CTRL_SRC_PCLK BIT(0) | ||
| 57 | |||
| 58 | extern void __iomem *jz4740_timer_base; | ||
| 59 | void __init jz4740_timer_init(void); | ||
| 60 | |||
| 19 | void jz4740_timer_enable_watchdog(void); | 61 | void jz4740_timer_enable_watchdog(void); |
| 20 | void jz4740_timer_disable_watchdog(void); | 62 | void jz4740_timer_disable_watchdog(void); |
| 21 | 63 | ||
| 64 | static inline void jz4740_timer_stop(unsigned int timer) | ||
| 65 | { | ||
| 66 | writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_SET); | ||
| 67 | } | ||
| 68 | |||
| 69 | static inline void jz4740_timer_start(unsigned int timer) | ||
| 70 | { | ||
| 71 | writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_CLEAR); | ||
| 72 | } | ||
| 73 | |||
| 74 | static inline bool jz4740_timer_is_enabled(unsigned int timer) | ||
| 75 | { | ||
| 76 | return readb(jz4740_timer_base + JZ_REG_TIMER_ENABLE) & BIT(timer); | ||
| 77 | } | ||
| 78 | |||
| 79 | static inline void jz4740_timer_enable(unsigned int timer) | ||
| 80 | { | ||
| 81 | writeb(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_ENABLE_SET); | ||
| 82 | } | ||
| 83 | |||
| 84 | static inline void jz4740_timer_disable(unsigned int timer) | ||
| 85 | { | ||
| 86 | writeb(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_ENABLE_CLEAR); | ||
| 87 | } | ||
| 88 | |||
| 89 | static inline void jz4740_timer_set_period(unsigned int timer, uint16_t period) | ||
| 90 | { | ||
| 91 | writew(period, jz4740_timer_base + JZ_REG_TIMER_DFR(timer)); | ||
| 92 | } | ||
| 93 | |||
| 94 | static inline void jz4740_timer_set_duty(unsigned int timer, uint16_t duty) | ||
| 95 | { | ||
| 96 | writew(duty, jz4740_timer_base + JZ_REG_TIMER_DHR(timer)); | ||
| 97 | } | ||
| 98 | |||
| 99 | static inline void jz4740_timer_set_count(unsigned int timer, uint16_t count) | ||
| 100 | { | ||
| 101 | writew(count, jz4740_timer_base + JZ_REG_TIMER_CNT(timer)); | ||
| 102 | } | ||
| 103 | |||
| 104 | static inline uint16_t jz4740_timer_get_count(unsigned int timer) | ||
| 105 | { | ||
| 106 | return readw(jz4740_timer_base + JZ_REG_TIMER_CNT(timer)); | ||
| 107 | } | ||
| 108 | |||
| 109 | static inline void jz4740_timer_ack_full(unsigned int timer) | ||
| 110 | { | ||
| 111 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_FLAG_CLEAR); | ||
| 112 | } | ||
| 113 | |||
| 114 | static inline void jz4740_timer_irq_full_enable(unsigned int timer) | ||
| 115 | { | ||
| 116 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_FLAG_CLEAR); | ||
| 117 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_MASK_CLEAR); | ||
| 118 | } | ||
| 119 | |||
| 120 | static inline void jz4740_timer_irq_full_disable(unsigned int timer) | ||
| 121 | { | ||
| 122 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_MASK_SET); | ||
| 123 | } | ||
| 124 | |||
| 125 | static inline void jz4740_timer_set_ctrl(unsigned int timer, uint16_t ctrl) | ||
| 126 | { | ||
| 127 | writew(ctrl, jz4740_timer_base + JZ_REG_TIMER_CTRL(timer)); | ||
| 128 | } | ||
| 129 | |||
| 130 | static inline uint16_t jz4740_timer_get_ctrl(unsigned int timer) | ||
| 131 | { | ||
| 132 | return readw(jz4740_timer_base + JZ_REG_TIMER_CTRL(timer)); | ||
| 133 | } | ||
| 134 | |||
| 22 | #endif | 135 | #endif |
diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig index 3e7141f0746c..468903053883 100644 --- a/arch/mips/jz4740/Kconfig +++ b/arch/mips/jz4740/Kconfig | |||
| @@ -7,6 +7,3 @@ config JZ4740_QI_LB60 | |||
| 7 | bool "Qi Hardware Ben NanoNote" | 7 | bool "Qi Hardware Ben NanoNote" |
| 8 | 8 | ||
| 9 | endchoice | 9 | endchoice |
| 10 | |||
| 11 | config HAVE_PWM | ||
| 12 | bool | ||
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile index e44abea9c209..63bad0e491d0 100644 --- a/arch/mips/jz4740/Makefile +++ b/arch/mips/jz4740/Makefile | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | # Object file lists. | 5 | # Object file lists. |
| 6 | 6 | ||
| 7 | obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ | 7 | obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ |
| 8 | gpio.o clock.o platform.o timer.o pwm.o serial.o | 8 | gpio.o clock.o platform.o timer.o serial.o |
| 9 | 9 | ||
| 10 | obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o | 10 | obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o |
| 11 | 11 | ||
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c index 9a3d9de4d04e..43d964d36288 100644 --- a/arch/mips/jz4740/board-qi_lb60.c +++ b/arch/mips/jz4740/board-qi_lb60.c | |||
| @@ -437,6 +437,7 @@ static struct platform_device *jz_platform_devices[] __initdata = { | |||
| 437 | &jz4740_codec_device, | 437 | &jz4740_codec_device, |
| 438 | &jz4740_rtc_device, | 438 | &jz4740_rtc_device, |
| 439 | &jz4740_adc_device, | 439 | &jz4740_adc_device, |
| 440 | &jz4740_pwm_device, | ||
| 440 | &qi_lb60_gpio_keys, | 441 | &qi_lb60_gpio_keys, |
| 441 | &qi_lb60_pwm_beeper, | 442 | &qi_lb60_pwm_beeper, |
| 442 | &qi_lb60_charger_device, | 443 | &qi_lb60_charger_device, |
diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c index e342ed4cbd43..6d14dcdbd908 100644 --- a/arch/mips/jz4740/platform.c +++ b/arch/mips/jz4740/platform.c | |||
| @@ -323,3 +323,9 @@ struct platform_device jz4740_wdt_device = { | |||
| 323 | .num_resources = ARRAY_SIZE(jz4740_wdt_resources), | 323 | .num_resources = ARRAY_SIZE(jz4740_wdt_resources), |
| 324 | .resource = jz4740_wdt_resources, | 324 | .resource = jz4740_wdt_resources, |
| 325 | }; | 325 | }; |
| 326 | |||
| 327 | /* PWM */ | ||
| 328 | struct platform_device jz4740_pwm_device = { | ||
| 329 | .name = "jz4740-pwm", | ||
| 330 | .id = -1, | ||
| 331 | }; | ||
diff --git a/arch/mips/jz4740/pwm.c b/arch/mips/jz4740/pwm.c deleted file mode 100644 index a26a6faec9a6..000000000000 --- a/arch/mips/jz4740/pwm.c +++ /dev/null | |||
| @@ -1,177 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
| 3 | * JZ4740 platform PWM support | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License as published by the | ||
| 7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 8 | * option) any later version. | ||
| 9 | * | ||
| 10 | * You should have received a copy of the GNU General Public License along | ||
| 11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/kernel.h> | ||
| 17 | |||
| 18 | #include <linux/clk.h> | ||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/pwm.h> | ||
| 21 | #include <linux/gpio.h> | ||
| 22 | |||
| 23 | #include <asm/mach-jz4740/gpio.h> | ||
| 24 | #include "timer.h" | ||
| 25 | |||
| 26 | static struct clk *jz4740_pwm_clk; | ||
| 27 | |||
| 28 | DEFINE_MUTEX(jz4740_pwm_mutex); | ||
| 29 | |||
| 30 | struct pwm_device { | ||
| 31 | unsigned int id; | ||
| 32 | unsigned int gpio; | ||
| 33 | bool used; | ||
| 34 | }; | ||
| 35 | |||
| 36 | static struct pwm_device jz4740_pwm_list[] = { | ||
| 37 | { 2, JZ_GPIO_PWM2, false }, | ||
| 38 | { 3, JZ_GPIO_PWM3, false }, | ||
| 39 | { 4, JZ_GPIO_PWM4, false }, | ||
| 40 | { 5, JZ_GPIO_PWM5, false }, | ||
| 41 | { 6, JZ_GPIO_PWM6, false }, | ||
| 42 | { 7, JZ_GPIO_PWM7, false }, | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct pwm_device *pwm_request(int id, const char *label) | ||
| 46 | { | ||
| 47 | int ret = 0; | ||
| 48 | struct pwm_device *pwm; | ||
| 49 | |||
| 50 | if (id < 2 || id > 7 || !jz4740_pwm_clk) | ||
| 51 | return ERR_PTR(-ENODEV); | ||
| 52 | |||
| 53 | mutex_lock(&jz4740_pwm_mutex); | ||
| 54 | |||
| 55 | pwm = &jz4740_pwm_list[id - 2]; | ||
| 56 | if (pwm->used) | ||
| 57 | ret = -EBUSY; | ||
| 58 | else | ||
| 59 | pwm->used = true; | ||
| 60 | |||
| 61 | mutex_unlock(&jz4740_pwm_mutex); | ||
| 62 | |||
| 63 | if (ret) | ||
| 64 | return ERR_PTR(ret); | ||
| 65 | |||
| 66 | ret = gpio_request(pwm->gpio, label); | ||
| 67 | |||
| 68 | if (ret) { | ||
| 69 | printk(KERN_ERR "Failed to request pwm gpio: %d\n", ret); | ||
| 70 | pwm->used = false; | ||
| 71 | return ERR_PTR(ret); | ||
| 72 | } | ||
| 73 | |||
| 74 | jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_PWM); | ||
| 75 | |||
| 76 | jz4740_timer_start(id); | ||
| 77 | |||
| 78 | return pwm; | ||
| 79 | } | ||
| 80 | |||
| 81 | void pwm_free(struct pwm_device *pwm) | ||
| 82 | { | ||
| 83 | pwm_disable(pwm); | ||
| 84 | jz4740_timer_set_ctrl(pwm->id, 0); | ||
| 85 | |||
| 86 | jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_NONE); | ||
| 87 | gpio_free(pwm->gpio); | ||
| 88 | |||
| 89 | jz4740_timer_stop(pwm->id); | ||
| 90 | |||
| 91 | pwm->used = false; | ||
| 92 | } | ||
| 93 | |||
| 94 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | ||
| 95 | { | ||
| 96 | unsigned long long tmp; | ||
| 97 | unsigned long period, duty; | ||
| 98 | unsigned int prescaler = 0; | ||
| 99 | unsigned int id = pwm->id; | ||
| 100 | uint16_t ctrl; | ||
| 101 | bool is_enabled; | ||
| 102 | |||
| 103 | if (duty_ns < 0 || duty_ns > period_ns) | ||
| 104 | return -EINVAL; | ||
| 105 | |||
| 106 | tmp = (unsigned long long)clk_get_rate(jz4740_pwm_clk) * period_ns; | ||
| 107 | do_div(tmp, 1000000000); | ||
| 108 | period = tmp; | ||
| 109 | |||
| 110 | while (period > 0xffff && prescaler < 6) { | ||
| 111 | period >>= 2; | ||
| 112 | ++prescaler; | ||
| 113 | } | ||
| 114 | |||
| 115 | if (prescaler == 6) | ||
| 116 | return -EINVAL; | ||
| 117 | |||
| 118 | tmp = (unsigned long long)period * duty_ns; | ||
| 119 | do_div(tmp, period_ns); | ||
| 120 | duty = period - tmp; | ||
| 121 | |||
| 122 | if (duty >= period) | ||
| 123 | duty = period - 1; | ||
| 124 | |||
| 125 | is_enabled = jz4740_timer_is_enabled(id); | ||
| 126 | if (is_enabled) | ||
| 127 | pwm_disable(pwm); | ||
| 128 | |||
| 129 | jz4740_timer_set_count(id, 0); | ||
| 130 | jz4740_timer_set_duty(id, duty); | ||
| 131 | jz4740_timer_set_period(id, period); | ||
| 132 | |||
| 133 | ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | | ||
| 134 | JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; | ||
| 135 | |||
| 136 | jz4740_timer_set_ctrl(id, ctrl); | ||
| 137 | |||
| 138 | if (is_enabled) | ||
| 139 | pwm_enable(pwm); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | int pwm_enable(struct pwm_device *pwm) | ||
| 145 | { | ||
| 146 | uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); | ||
| 147 | |||
| 148 | ctrl |= JZ_TIMER_CTRL_PWM_ENABLE; | ||
| 149 | jz4740_timer_set_ctrl(pwm->id, ctrl); | ||
| 150 | jz4740_timer_enable(pwm->id); | ||
| 151 | |||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | void pwm_disable(struct pwm_device *pwm) | ||
| 156 | { | ||
| 157 | uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); | ||
| 158 | |||
| 159 | ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; | ||
| 160 | jz4740_timer_disable(pwm->id); | ||
| 161 | jz4740_timer_set_ctrl(pwm->id, ctrl); | ||
| 162 | } | ||
| 163 | |||
| 164 | static int __init jz4740_pwm_init(void) | ||
| 165 | { | ||
| 166 | int ret = 0; | ||
| 167 | |||
| 168 | jz4740_pwm_clk = clk_get(NULL, "ext"); | ||
| 169 | |||
| 170 | if (IS_ERR(jz4740_pwm_clk)) { | ||
| 171 | ret = PTR_ERR(jz4740_pwm_clk); | ||
| 172 | jz4740_pwm_clk = NULL; | ||
| 173 | } | ||
| 174 | |||
| 175 | return ret; | ||
| 176 | } | ||
| 177 | subsys_initcall(jz4740_pwm_init); | ||
diff --git a/arch/mips/jz4740/time.c b/arch/mips/jz4740/time.c index f83c2dd07a27..39bb4bbf43e7 100644 --- a/arch/mips/jz4740/time.c +++ b/arch/mips/jz4740/time.c | |||
| @@ -20,10 +20,10 @@ | |||
| 20 | #include <linux/clockchips.h> | 20 | #include <linux/clockchips.h> |
| 21 | 21 | ||
| 22 | #include <asm/mach-jz4740/irq.h> | 22 | #include <asm/mach-jz4740/irq.h> |
| 23 | #include <asm/mach-jz4740/timer.h> | ||
| 23 | #include <asm/time.h> | 24 | #include <asm/time.h> |
| 24 | 25 | ||
| 25 | #include "clock.h" | 26 | #include "clock.h" |
| 26 | #include "timer.h" | ||
| 27 | 27 | ||
| 28 | #define TIMER_CLOCKEVENT 0 | 28 | #define TIMER_CLOCKEVENT 0 |
| 29 | #define TIMER_CLOCKSOURCE 1 | 29 | #define TIMER_CLOCKSOURCE 1 |
diff --git a/arch/mips/jz4740/timer.c b/arch/mips/jz4740/timer.c index 654d5c3900b6..22f11d73a17d 100644 --- a/arch/mips/jz4740/timer.c +++ b/arch/mips/jz4740/timer.c | |||
| @@ -17,11 +17,11 @@ | |||
| 17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
| 18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 19 | 19 | ||
| 20 | #include "timer.h" | ||
| 21 | |||
| 22 | #include <asm/mach-jz4740/base.h> | 20 | #include <asm/mach-jz4740/base.h> |
| 21 | #include <asm/mach-jz4740/timer.h> | ||
| 23 | 22 | ||
| 24 | void __iomem *jz4740_timer_base; | 23 | void __iomem *jz4740_timer_base; |
| 24 | EXPORT_SYMBOL_GPL(jz4740_timer_base); | ||
| 25 | 25 | ||
| 26 | void jz4740_timer_enable_watchdog(void) | 26 | void jz4740_timer_enable_watchdog(void) |
| 27 | { | 27 | { |
diff --git a/arch/mips/jz4740/timer.h b/arch/mips/jz4740/timer.h deleted file mode 100644 index fca3994f2e6d..000000000000 --- a/arch/mips/jz4740/timer.h +++ /dev/null | |||
| @@ -1,136 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
| 3 | * JZ4740 platform timer support | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License as published by the | ||
| 7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 8 | * option) any later version. | ||
| 9 | * | ||
| 10 | * You should have received a copy of the GNU General Public License along | ||
| 11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef __MIPS_JZ4740_TIMER_H__ | ||
| 17 | #define __MIPS_JZ4740_TIMER_H__ | ||
| 18 | |||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/io.h> | ||
| 21 | |||
| 22 | #define JZ_REG_TIMER_STOP 0x0C | ||
| 23 | #define JZ_REG_TIMER_STOP_SET 0x1C | ||
| 24 | #define JZ_REG_TIMER_STOP_CLEAR 0x2C | ||
| 25 | #define JZ_REG_TIMER_ENABLE 0x00 | ||
| 26 | #define JZ_REG_TIMER_ENABLE_SET 0x04 | ||
| 27 | #define JZ_REG_TIMER_ENABLE_CLEAR 0x08 | ||
| 28 | #define JZ_REG_TIMER_FLAG 0x10 | ||
| 29 | #define JZ_REG_TIMER_FLAG_SET 0x14 | ||
| 30 | #define JZ_REG_TIMER_FLAG_CLEAR 0x18 | ||
| 31 | #define JZ_REG_TIMER_MASK 0x20 | ||
| 32 | #define JZ_REG_TIMER_MASK_SET 0x24 | ||
| 33 | #define JZ_REG_TIMER_MASK_CLEAR 0x28 | ||
| 34 | |||
| 35 | #define JZ_REG_TIMER_DFR(x) (((x) * 0x10) + 0x30) | ||
| 36 | #define JZ_REG_TIMER_DHR(x) (((x) * 0x10) + 0x34) | ||
| 37 | #define JZ_REG_TIMER_CNT(x) (((x) * 0x10) + 0x38) | ||
| 38 | #define JZ_REG_TIMER_CTRL(x) (((x) * 0x10) + 0x3C) | ||
| 39 | |||
| 40 | #define JZ_TIMER_IRQ_HALF(x) BIT((x) + 0x10) | ||
| 41 | #define JZ_TIMER_IRQ_FULL(x) BIT(x) | ||
| 42 | |||
| 43 | #define JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN BIT(9) | ||
| 44 | #define JZ_TIMER_CTRL_PWM_ACTIVE_LOW BIT(8) | ||
| 45 | #define JZ_TIMER_CTRL_PWM_ENABLE BIT(7) | ||
| 46 | #define JZ_TIMER_CTRL_PRESCALE_MASK 0x1c | ||
| 47 | #define JZ_TIMER_CTRL_PRESCALE_OFFSET 0x3 | ||
| 48 | #define JZ_TIMER_CTRL_PRESCALE_1 (0 << 3) | ||
| 49 | #define JZ_TIMER_CTRL_PRESCALE_4 (1 << 3) | ||
| 50 | #define JZ_TIMER_CTRL_PRESCALE_16 (2 << 3) | ||
| 51 | #define JZ_TIMER_CTRL_PRESCALE_64 (3 << 3) | ||
| 52 | #define JZ_TIMER_CTRL_PRESCALE_256 (4 << 3) | ||
| 53 | #define JZ_TIMER_CTRL_PRESCALE_1024 (5 << 3) | ||
| 54 | |||
| 55 | #define JZ_TIMER_CTRL_PRESCALER(x) ((x) << JZ_TIMER_CTRL_PRESCALE_OFFSET) | ||
| 56 | |||
| 57 | #define JZ_TIMER_CTRL_SRC_EXT BIT(2) | ||
| 58 | #define JZ_TIMER_CTRL_SRC_RTC BIT(1) | ||
| 59 | #define JZ_TIMER_CTRL_SRC_PCLK BIT(0) | ||
| 60 | |||
| 61 | extern void __iomem *jz4740_timer_base; | ||
| 62 | void __init jz4740_timer_init(void); | ||
| 63 | |||
| 64 | static inline void jz4740_timer_stop(unsigned int timer) | ||
| 65 | { | ||
| 66 | writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_SET); | ||
| 67 | } | ||
| 68 | |||
| 69 | static inline void jz4740_timer_start(unsigned int timer) | ||
| 70 | { | ||
| 71 | writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_CLEAR); | ||
| 72 | } | ||
| 73 | |||
| 74 | static inline bool jz4740_timer_is_enabled(unsigned int timer) | ||
| 75 | { | ||
| 76 | return readb(jz4740_timer_base + JZ_REG_TIMER_ENABLE) & BIT(timer); | ||
| 77 | } | ||
| 78 | |||
| 79 | static inline void jz4740_timer_enable(unsigned int timer) | ||
| 80 | { | ||
| 81 | writeb(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_ENABLE_SET); | ||
| 82 | } | ||
| 83 | |||
| 84 | static inline void jz4740_timer_disable(unsigned int timer) | ||
| 85 | { | ||
| 86 | writeb(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_ENABLE_CLEAR); | ||
| 87 | } | ||
| 88 | |||
| 89 | |||
| 90 | static inline void jz4740_timer_set_period(unsigned int timer, uint16_t period) | ||
| 91 | { | ||
| 92 | writew(period, jz4740_timer_base + JZ_REG_TIMER_DFR(timer)); | ||
| 93 | } | ||
| 94 | |||
| 95 | static inline void jz4740_timer_set_duty(unsigned int timer, uint16_t duty) | ||
| 96 | { | ||
| 97 | writew(duty, jz4740_timer_base + JZ_REG_TIMER_DHR(timer)); | ||
| 98 | } | ||
| 99 | |||
| 100 | static inline void jz4740_timer_set_count(unsigned int timer, uint16_t count) | ||
| 101 | { | ||
| 102 | writew(count, jz4740_timer_base + JZ_REG_TIMER_CNT(timer)); | ||
| 103 | } | ||
| 104 | |||
| 105 | static inline uint16_t jz4740_timer_get_count(unsigned int timer) | ||
| 106 | { | ||
| 107 | return readw(jz4740_timer_base + JZ_REG_TIMER_CNT(timer)); | ||
| 108 | } | ||
| 109 | |||
| 110 | static inline void jz4740_timer_ack_full(unsigned int timer) | ||
| 111 | { | ||
| 112 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_FLAG_CLEAR); | ||
| 113 | } | ||
| 114 | |||
| 115 | static inline void jz4740_timer_irq_full_enable(unsigned int timer) | ||
| 116 | { | ||
| 117 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_FLAG_CLEAR); | ||
| 118 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_MASK_CLEAR); | ||
| 119 | } | ||
| 120 | |||
| 121 | static inline void jz4740_timer_irq_full_disable(unsigned int timer) | ||
| 122 | { | ||
| 123 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_MASK_SET); | ||
| 124 | } | ||
| 125 | |||
| 126 | static inline void jz4740_timer_set_ctrl(unsigned int timer, uint16_t ctrl) | ||
| 127 | { | ||
| 128 | writew(ctrl, jz4740_timer_base + JZ_REG_TIMER_CTRL(timer)); | ||
| 129 | } | ||
| 130 | |||
| 131 | static inline uint16_t jz4740_timer_get_ctrl(unsigned int timer) | ||
| 132 | { | ||
| 133 | return readw(jz4740_timer_base + JZ_REG_TIMER_CTRL(timer)); | ||
| 134 | } | ||
| 135 | |||
| 136 | #endif | ||
diff --git a/arch/unicore32/Kconfig b/arch/unicore32/Kconfig index 1e638e75a6b7..35ee2bf66354 100644 --- a/arch/unicore32/Kconfig +++ b/arch/unicore32/Kconfig | |||
| @@ -21,9 +21,6 @@ config UNICORE32 | |||
| 21 | designs licensed by PKUnity Ltd. | 21 | designs licensed by PKUnity Ltd. |
| 22 | Please see web page at <http://www.pkunity.com/>. | 22 | Please see web page at <http://www.pkunity.com/>. |
| 23 | 23 | ||
| 24 | config HAVE_PWM | ||
| 25 | bool | ||
| 26 | |||
| 27 | config GENERIC_GPIO | 24 | config GENERIC_GPIO |
| 28 | def_bool y | 25 | def_bool y |
| 29 | 26 | ||
| @@ -106,7 +103,8 @@ config PUV3_DB0913 | |||
| 106 | 103 | ||
| 107 | config PUV3_NB0916 | 104 | config PUV3_NB0916 |
| 108 | bool "NetBook board (0916)" | 105 | bool "NetBook board (0916)" |
| 109 | select HAVE_PWM | 106 | select PWM |
| 107 | select PWM_PUV3 | ||
| 110 | 108 | ||
| 111 | config PUV3_SMW0919 | 109 | config PUV3_SMW0919 |
| 112 | bool "Security Mini-Workstation board (0919)" | 110 | bool "Security Mini-Workstation board (0919)" |
| @@ -220,12 +218,6 @@ config PUV3_GPIO | |||
| 220 | select GPIO_SYSFS if EXPERIMENTAL | 218 | select GPIO_SYSFS if EXPERIMENTAL |
| 221 | default y | 219 | default y |
| 222 | 220 | ||
| 223 | config PUV3_PWM | ||
| 224 | tristate | ||
| 225 | default BACKLIGHT_PWM | ||
| 226 | help | ||
| 227 | Enable support for NB0916 PWM controllers | ||
| 228 | |||
| 229 | if PUV3_NB0916 | 221 | if PUV3_NB0916 |
| 230 | 222 | ||
| 231 | menu "PKUnity NetBook-0916 Features" | 223 | menu "PKUnity NetBook-0916 Features" |
diff --git a/arch/unicore32/include/mach/regs-ost.h b/arch/unicore32/include/mach/regs-ost.h index 7b91fe698eed..4a85fb463848 100644 --- a/arch/unicore32/include/mach/regs-ost.h +++ b/arch/unicore32/include/mach/regs-ost.h | |||
| @@ -33,18 +33,16 @@ | |||
| 33 | * Interrupt Enable Reg OST_OIER | 33 | * Interrupt Enable Reg OST_OIER |
| 34 | */ | 34 | */ |
| 35 | #define OST_OIER (PKUNITY_OST_BASE + 0x001C) | 35 | #define OST_OIER (PKUNITY_OST_BASE + 0x001C) |
| 36 | |||
| 36 | /* | 37 | /* |
| 37 | * PWM Pulse Width Control Reg OST_PWMPWCR | 38 | * PWM Registers: IO base address: PKUNITY_OST_BASE + 0x80 |
| 38 | */ | 39 | * PWCR: Pulse Width Control Reg |
| 39 | #define OST_PWMPWCR (PKUNITY_OST_BASE + 0x0080) | 40 | * DCCR: Duty Cycle Control Reg |
| 40 | /* | 41 | * PCR: Period Control Reg |
| 41 | * PWM Duty Cycle Control Reg OST_PWMDCCR | ||
| 42 | */ | ||
| 43 | #define OST_PWMDCCR (PKUNITY_OST_BASE + 0x0084) | ||
| 44 | /* | ||
| 45 | * PWM Period Control Reg OST_PWMPCR | ||
| 46 | */ | 42 | */ |
| 47 | #define OST_PWMPCR (PKUNITY_OST_BASE + 0x0088) | 43 | #define OST_PWM_PWCR (0x00) |
| 44 | #define OST_PWM_DCCR (0x04) | ||
| 45 | #define OST_PWM_PCR (0x08) | ||
| 48 | 46 | ||
| 49 | /* | 47 | /* |
| 50 | * Match detected 0 OST_OSSR_M0 | 48 | * Match detected 0 OST_OSSR_M0 |
diff --git a/arch/unicore32/kernel/Makefile b/arch/unicore32/kernel/Makefile index 324010156958..fa497e0efe5a 100644 --- a/arch/unicore32/kernel/Makefile +++ b/arch/unicore32/kernel/Makefile | |||
| @@ -16,7 +16,6 @@ obj-$(CONFIG_UNICORE_FPU_F64) += fpu-ucf64.o | |||
| 16 | obj-$(CONFIG_ARCH_PUV3) += clock.o irq.o time.o | 16 | obj-$(CONFIG_ARCH_PUV3) += clock.o irq.o time.o |
| 17 | 17 | ||
| 18 | obj-$(CONFIG_PUV3_GPIO) += gpio.o | 18 | obj-$(CONFIG_PUV3_GPIO) += gpio.o |
| 19 | obj-$(CONFIG_PUV3_PWM) += pwm.o | ||
| 20 | obj-$(CONFIG_PUV3_PM) += pm.o sleep.o | 19 | obj-$(CONFIG_PUV3_PM) += pm.o sleep.o |
| 21 | obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate_asm.o | 20 | obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate_asm.o |
| 22 | 21 | ||
diff --git a/arch/unicore32/kernel/pwm.c b/arch/unicore32/kernel/pwm.c deleted file mode 100644 index 4615d51e3ba6..000000000000 --- a/arch/unicore32/kernel/pwm.c +++ /dev/null | |||
| @@ -1,263 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/unicore32/kernel/pwm.c | ||
| 3 | * | ||
| 4 | * Code specific to PKUnity SoC and UniCore ISA | ||
| 5 | * | ||
| 6 | * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> | ||
| 7 | * Copyright (C) 2001-2010 Guan Xuetao | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/err.h> | ||
| 19 | #include <linux/clk.h> | ||
| 20 | #include <linux/io.h> | ||
| 21 | #include <linux/pwm.h> | ||
| 22 | |||
| 23 | #include <asm/div64.h> | ||
| 24 | #include <mach/hardware.h> | ||
| 25 | |||
| 26 | struct pwm_device { | ||
| 27 | struct list_head node; | ||
| 28 | struct platform_device *pdev; | ||
| 29 | |||
| 30 | const char *label; | ||
| 31 | struct clk *clk; | ||
| 32 | int clk_enabled; | ||
| 33 | |||
| 34 | unsigned int use_count; | ||
| 35 | unsigned int pwm_id; | ||
| 36 | }; | ||
| 37 | |||
| 38 | /* | ||
| 39 | * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE | ||
| 40 | * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE | ||
| 41 | */ | ||
| 42 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | ||
| 43 | { | ||
| 44 | unsigned long long c; | ||
| 45 | unsigned long period_cycles, prescale, pv, dc; | ||
| 46 | |||
| 47 | if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) | ||
| 48 | return -EINVAL; | ||
| 49 | |||
| 50 | c = clk_get_rate(pwm->clk); | ||
| 51 | c = c * period_ns; | ||
| 52 | do_div(c, 1000000000); | ||
| 53 | period_cycles = c; | ||
| 54 | |||
| 55 | if (period_cycles < 1) | ||
| 56 | period_cycles = 1; | ||
| 57 | prescale = (period_cycles - 1) / 1024; | ||
| 58 | pv = period_cycles / (prescale + 1) - 1; | ||
| 59 | |||
| 60 | if (prescale > 63) | ||
| 61 | return -EINVAL; | ||
| 62 | |||
| 63 | if (duty_ns == period_ns) | ||
| 64 | dc = OST_PWMDCCR_FDCYCLE; | ||
| 65 | else | ||
| 66 | dc = (pv + 1) * duty_ns / period_ns; | ||
| 67 | |||
| 68 | /* NOTE: the clock to PWM has to be enabled first | ||
| 69 | * before writing to the registers | ||
| 70 | */ | ||
| 71 | clk_enable(pwm->clk); | ||
| 72 | OST_PWMPWCR = prescale; | ||
| 73 | OST_PWMDCCR = pv - dc; | ||
| 74 | OST_PWMPCR = pv; | ||
| 75 | clk_disable(pwm->clk); | ||
| 76 | |||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | EXPORT_SYMBOL(pwm_config); | ||
| 80 | |||
| 81 | int pwm_enable(struct pwm_device *pwm) | ||
| 82 | { | ||
| 83 | int rc = 0; | ||
| 84 | |||
| 85 | if (!pwm->clk_enabled) { | ||
| 86 | rc = clk_enable(pwm->clk); | ||
| 87 | if (!rc) | ||
| 88 | pwm->clk_enabled = 1; | ||
| 89 | } | ||
| 90 | return rc; | ||
| 91 | } | ||
| 92 | EXPORT_SYMBOL(pwm_enable); | ||
| 93 | |||
| 94 | void pwm_disable(struct pwm_device *pwm) | ||
| 95 | { | ||
| 96 | if (pwm->clk_enabled) { | ||
| 97 | clk_disable(pwm->clk); | ||
| 98 | pwm->clk_enabled = 0; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | EXPORT_SYMBOL(pwm_disable); | ||
| 102 | |||
| 103 | static DEFINE_MUTEX(pwm_lock); | ||
| 104 | static LIST_HEAD(pwm_list); | ||
| 105 | |||
| 106 | struct pwm_device *pwm_request(int pwm_id, const char *label) | ||
| 107 | { | ||
| 108 | struct pwm_device *pwm; | ||
| 109 | int found = 0; | ||
| 110 | |||
| 111 | mutex_lock(&pwm_lock); | ||
| 112 | |||
| 113 | list_for_each_entry(pwm, &pwm_list, node) { | ||
| 114 | if (pwm->pwm_id == pwm_id) { | ||
| 115 | found = 1; | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | if (found) { | ||
| 121 | if (pwm->use_count == 0) { | ||
| 122 | pwm->use_count++; | ||
| 123 | pwm->label = label; | ||
| 124 | } else | ||
| 125 | pwm = ERR_PTR(-EBUSY); | ||
| 126 | } else | ||
| 127 | pwm = ERR_PTR(-ENOENT); | ||
| 128 | |||
| 129 | mutex_unlock(&pwm_lock); | ||
| 130 | return pwm; | ||
| 131 | } | ||
| 132 | EXPORT_SYMBOL(pwm_request); | ||
| 133 | |||
| 134 | void pwm_free(struct pwm_device *pwm) | ||
| 135 | { | ||
| 136 | mutex_lock(&pwm_lock); | ||
| 137 | |||
| 138 | if (pwm->use_count) { | ||
| 139 | pwm->use_count--; | ||
| 140 | pwm->label = NULL; | ||
| 141 | } else | ||
| 142 | pr_warning("PWM device already freed\n"); | ||
| 143 | |||
| 144 | mutex_unlock(&pwm_lock); | ||
| 145 | } | ||
| 146 | EXPORT_SYMBOL(pwm_free); | ||
| 147 | |||
| 148 | static inline void __add_pwm(struct pwm_device *pwm) | ||
| 149 | { | ||
| 150 | mutex_lock(&pwm_lock); | ||
| 151 | list_add_tail(&pwm->node, &pwm_list); | ||
| 152 | mutex_unlock(&pwm_lock); | ||
| 153 | } | ||
| 154 | |||
| 155 | static struct pwm_device *pwm_probe(struct platform_device *pdev, | ||
| 156 | unsigned int pwm_id, struct pwm_device *parent_pwm) | ||
| 157 | { | ||
| 158 | struct pwm_device *pwm; | ||
| 159 | struct resource *r; | ||
| 160 | int ret = 0; | ||
| 161 | |||
| 162 | pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); | ||
| 163 | if (pwm == NULL) { | ||
| 164 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 165 | return ERR_PTR(-ENOMEM); | ||
| 166 | } | ||
| 167 | |||
| 168 | pwm->clk = clk_get(NULL, "OST_CLK"); | ||
| 169 | if (IS_ERR(pwm->clk)) { | ||
| 170 | ret = PTR_ERR(pwm->clk); | ||
| 171 | goto err_free; | ||
| 172 | } | ||
| 173 | pwm->clk_enabled = 0; | ||
| 174 | |||
| 175 | pwm->use_count = 0; | ||
| 176 | pwm->pwm_id = pwm_id; | ||
| 177 | pwm->pdev = pdev; | ||
| 178 | |||
| 179 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 180 | if (r == NULL) { | ||
| 181 | dev_err(&pdev->dev, "no memory resource defined\n"); | ||
| 182 | ret = -ENODEV; | ||
| 183 | goto err_free_clk; | ||
| 184 | } | ||
| 185 | |||
| 186 | r = request_mem_region(r->start, resource_size(r), pdev->name); | ||
| 187 | if (r == NULL) { | ||
| 188 | dev_err(&pdev->dev, "failed to request memory resource\n"); | ||
| 189 | ret = -EBUSY; | ||
| 190 | goto err_free_clk; | ||
| 191 | } | ||
| 192 | |||
| 193 | __add_pwm(pwm); | ||
| 194 | platform_set_drvdata(pdev, pwm); | ||
| 195 | return pwm; | ||
| 196 | |||
| 197 | err_free_clk: | ||
| 198 | clk_put(pwm->clk); | ||
| 199 | err_free: | ||
| 200 | kfree(pwm); | ||
| 201 | return ERR_PTR(ret); | ||
| 202 | } | ||
| 203 | |||
| 204 | static int __devinit puv3_pwm_probe(struct platform_device *pdev) | ||
| 205 | { | ||
| 206 | struct pwm_device *pwm = pwm_probe(pdev, pdev->id, NULL); | ||
| 207 | |||
| 208 | if (IS_ERR(pwm)) | ||
| 209 | return PTR_ERR(pwm); | ||
| 210 | |||
| 211 | return 0; | ||
| 212 | } | ||
| 213 | |||
| 214 | static int __devexit pwm_remove(struct platform_device *pdev) | ||
| 215 | { | ||
| 216 | struct pwm_device *pwm; | ||
| 217 | struct resource *r; | ||
| 218 | |||
| 219 | pwm = platform_get_drvdata(pdev); | ||
| 220 | if (pwm == NULL) | ||
| 221 | return -ENODEV; | ||
| 222 | |||
| 223 | mutex_lock(&pwm_lock); | ||
| 224 | list_del(&pwm->node); | ||
| 225 | mutex_unlock(&pwm_lock); | ||
| 226 | |||
| 227 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 228 | release_mem_region(r->start, resource_size(r)); | ||
| 229 | |||
| 230 | clk_put(pwm->clk); | ||
| 231 | kfree(pwm); | ||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | static struct platform_driver puv3_pwm_driver = { | ||
| 236 | .driver = { | ||
| 237 | .name = "PKUnity-v3-PWM", | ||
| 238 | }, | ||
| 239 | .probe = puv3_pwm_probe, | ||
| 240 | .remove = __devexit_p(pwm_remove), | ||
| 241 | }; | ||
| 242 | |||
| 243 | static int __init pwm_init(void) | ||
| 244 | { | ||
| 245 | int ret = 0; | ||
| 246 | |||
| 247 | ret = platform_driver_register(&puv3_pwm_driver); | ||
| 248 | if (ret) { | ||
| 249 | printk(KERN_ERR "failed to register puv3_pwm_driver\n"); | ||
| 250 | return ret; | ||
| 251 | } | ||
| 252 | |||
| 253 | return ret; | ||
| 254 | } | ||
| 255 | arch_initcall(pwm_init); | ||
| 256 | |||
| 257 | static void __exit pwm_exit(void) | ||
| 258 | { | ||
| 259 | platform_driver_unregister(&puv3_pwm_driver); | ||
| 260 | } | ||
| 261 | module_exit(pwm_exit); | ||
| 262 | |||
| 263 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 99c73352c430..b151b7c1bd59 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
| @@ -60,16 +60,6 @@ config ATMEL_PWM | |||
| 60 | purposes including software controlled power-efficient backlights | 60 | purposes including software controlled power-efficient backlights |
| 61 | on LCD displays, motor control, and waveform generation. | 61 | on LCD displays, motor control, and waveform generation. |
| 62 | 62 | ||
| 63 | config AB8500_PWM | ||
| 64 | bool "AB8500 PWM support" | ||
| 65 | depends on AB8500_CORE && ARCH_U8500 | ||
| 66 | select HAVE_PWM | ||
| 67 | depends on !PWM | ||
| 68 | help | ||
| 69 | This driver exports functions to enable/disble/config/free Pulse | ||
| 70 | Width Modulation in the Analog Baseband Chip AB8500. | ||
| 71 | It is used by led and backlight driver to control the intensity. | ||
| 72 | |||
| 73 | config ATMEL_TCLIB | 63 | config ATMEL_TCLIB |
| 74 | bool "Atmel AT32/AT91 Timer/Counter Library" | 64 | bool "Atmel AT32/AT91 Timer/Counter Library" |
| 75 | depends on (AVR32 || ARCH_AT91) | 65 | depends on (AVR32 || ARCH_AT91) |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b88df7a350b8..2129377c0de6 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
| @@ -44,7 +44,6 @@ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o | |||
| 44 | obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o | 44 | obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o |
| 45 | obj-$(CONFIG_PCH_PHUB) += pch_phub.o | 45 | obj-$(CONFIG_PCH_PHUB) += pch_phub.o |
| 46 | obj-y += ti-st/ | 46 | obj-y += ti-st/ |
| 47 | obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o | ||
| 48 | obj-y += lis3lv02d/ | 47 | obj-y += lis3lv02d/ |
| 49 | obj-y += carma/ | 48 | obj-y += carma/ |
| 50 | obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o | 49 | obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o |
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index d7c6b83097c1..ed81720e7b2b 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | menuconfig PWM | 1 | menuconfig PWM |
| 2 | bool "Pulse-Width Modulation (PWM) Support" | 2 | bool "Pulse-Width Modulation (PWM) Support" |
| 3 | depends on !MACH_JZ4740 && !PUV3_PWM | ||
| 4 | help | 3 | help |
| 5 | Generic Pulse-Width Modulation (PWM) support. | 4 | Generic Pulse-Width Modulation (PWM) support. |
| 6 | 5 | ||
| @@ -29,6 +28,15 @@ menuconfig PWM | |||
| 29 | 28 | ||
| 30 | if PWM | 29 | if PWM |
| 31 | 30 | ||
| 31 | config PWM_AB8500 | ||
| 32 | tristate "AB8500 PWM support" | ||
| 33 | depends on AB8500_CORE && ARCH_U8500 | ||
| 34 | help | ||
| 35 | Generic PWM framework driver for Analog Baseband AB8500. | ||
| 36 | |||
| 37 | To compile this driver as a module, choose M here: the module | ||
| 38 | will be called pwm-ab8500. | ||
| 39 | |||
| 32 | config PWM_BFIN | 40 | config PWM_BFIN |
| 33 | tristate "Blackfin PWM support" | 41 | tristate "Blackfin PWM support" |
| 34 | depends on BFIN_GPTIMERS | 42 | depends on BFIN_GPTIMERS |
| @@ -47,6 +55,16 @@ config PWM_IMX | |||
| 47 | To compile this driver as a module, choose M here: the module | 55 | To compile this driver as a module, choose M here: the module |
| 48 | will be called pwm-imx. | 56 | will be called pwm-imx. |
| 49 | 57 | ||
| 58 | config PWM_JZ4740 | ||
| 59 | tristate "Ingenic JZ4740 PWM support" | ||
| 60 | depends on MACH_JZ4740 | ||
| 61 | help | ||
| 62 | Generic PWM framework driver for Ingenic JZ4740 based | ||
| 63 | machines. | ||
| 64 | |||
| 65 | To compile this driver as a module, choose M here: the module | ||
| 66 | will be called pwm-jz4740. | ||
| 67 | |||
| 50 | config PWM_LPC32XX | 68 | config PWM_LPC32XX |
| 51 | tristate "LPC32XX PWM support" | 69 | tristate "LPC32XX PWM support" |
| 52 | depends on ARCH_LPC32XX | 70 | depends on ARCH_LPC32XX |
| @@ -67,6 +85,15 @@ config PWM_MXS | |||
| 67 | To compile this driver as a module, choose M here: the module | 85 | To compile this driver as a module, choose M here: the module |
| 68 | will be called pwm-mxs. | 86 | will be called pwm-mxs. |
| 69 | 87 | ||
| 88 | config PWM_PUV3 | ||
| 89 | tristate "PKUnity NetBook-0916 PWM support" | ||
| 90 | depends on ARCH_PUV3 | ||
| 91 | help | ||
| 92 | Generic PWM framework driver for PKUnity NetBook-0916. | ||
| 93 | |||
| 94 | To compile this driver as a module, choose M here: the module | ||
| 95 | will be called pwm-puv3. | ||
| 96 | |||
| 70 | config PWM_PXA | 97 | config PWM_PXA |
| 71 | tristate "PXA PWM support" | 98 | tristate "PXA PWM support" |
| 72 | depends on ARCH_PXA | 99 | depends on ARCH_PXA |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 78f123dca30d..acfe4821c58b 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
| @@ -1,8 +1,11 @@ | |||
| 1 | obj-$(CONFIG_PWM) += core.o | 1 | obj-$(CONFIG_PWM) += core.o |
| 2 | obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o | ||
| 2 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o | 3 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o |
| 3 | obj-$(CONFIG_PWM_IMX) += pwm-imx.o | 4 | obj-$(CONFIG_PWM_IMX) += pwm-imx.o |
| 5 | obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o | ||
| 4 | obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o | 6 | obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o |
| 5 | obj-$(CONFIG_PWM_MXS) += pwm-mxs.o | 7 | obj-$(CONFIG_PWM_MXS) += pwm-mxs.o |
| 8 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o | ||
| 6 | obj-$(CONFIG_PWM_PXA) += pwm-pxa.o | 9 | obj-$(CONFIG_PWM_PXA) += pwm-pxa.o |
| 7 | obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o | 10 | obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o |
| 8 | obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o | 11 | obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o |
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c6e05078d3ad..f5acdaa52707 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c | |||
| @@ -371,7 +371,7 @@ EXPORT_SYMBOL_GPL(pwm_free); | |||
| 371 | */ | 371 | */ |
| 372 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | 372 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) |
| 373 | { | 373 | { |
| 374 | if (!pwm || period_ns == 0 || duty_ns > period_ns) | 374 | if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns) |
| 375 | return -EINVAL; | 375 | return -EINVAL; |
| 376 | 376 | ||
| 377 | return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); | 377 | return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); |
| @@ -379,6 +379,28 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | |||
| 379 | EXPORT_SYMBOL_GPL(pwm_config); | 379 | EXPORT_SYMBOL_GPL(pwm_config); |
| 380 | 380 | ||
| 381 | /** | 381 | /** |
| 382 | * pwm_set_polarity() - configure the polarity of a PWM signal | ||
| 383 | * @pwm: PWM device | ||
| 384 | * @polarity: new polarity of the PWM signal | ||
| 385 | * | ||
| 386 | * Note that the polarity cannot be configured while the PWM device is enabled | ||
| 387 | */ | ||
| 388 | int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity) | ||
| 389 | { | ||
| 390 | if (!pwm || !pwm->chip->ops) | ||
| 391 | return -EINVAL; | ||
| 392 | |||
| 393 | if (!pwm->chip->ops->set_polarity) | ||
| 394 | return -ENOSYS; | ||
| 395 | |||
| 396 | if (test_bit(PWMF_ENABLED, &pwm->flags)) | ||
| 397 | return -EBUSY; | ||
| 398 | |||
| 399 | return pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity); | ||
| 400 | } | ||
| 401 | EXPORT_SYMBOL_GPL(pwm_set_polarity); | ||
| 402 | |||
| 403 | /** | ||
| 382 | * pwm_enable() - start a PWM output toggling | 404 | * pwm_enable() - start a PWM output toggling |
| 383 | * @pwm: PWM device | 405 | * @pwm: PWM device |
| 384 | */ | 406 | */ |
| @@ -624,6 +646,64 @@ out: | |||
| 624 | } | 646 | } |
| 625 | EXPORT_SYMBOL_GPL(pwm_put); | 647 | EXPORT_SYMBOL_GPL(pwm_put); |
| 626 | 648 | ||
| 649 | static void devm_pwm_release(struct device *dev, void *res) | ||
| 650 | { | ||
| 651 | pwm_put(*(struct pwm_device **)res); | ||
| 652 | } | ||
| 653 | |||
| 654 | /** | ||
| 655 | * devm_pwm_get() - resource managed pwm_get() | ||
| 656 | * @dev: device for PWM consumer | ||
| 657 | * @con_id: consumer name | ||
| 658 | * | ||
| 659 | * This function performs like pwm_get() but the acquired PWM device will | ||
| 660 | * automatically be released on driver detach. | ||
| 661 | */ | ||
| 662 | struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) | ||
| 663 | { | ||
| 664 | struct pwm_device **ptr, *pwm; | ||
| 665 | |||
| 666 | ptr = devres_alloc(devm_pwm_release, sizeof(**ptr), GFP_KERNEL); | ||
| 667 | if (!ptr) | ||
| 668 | return ERR_PTR(-ENOMEM); | ||
| 669 | |||
| 670 | pwm = pwm_get(dev, con_id); | ||
| 671 | if (!IS_ERR(pwm)) { | ||
| 672 | *ptr = pwm; | ||
| 673 | devres_add(dev, ptr); | ||
| 674 | } else { | ||
| 675 | devres_free(ptr); | ||
| 676 | } | ||
| 677 | |||
| 678 | return pwm; | ||
| 679 | } | ||
| 680 | EXPORT_SYMBOL_GPL(devm_pwm_get); | ||
| 681 | |||
| 682 | static int devm_pwm_match(struct device *dev, void *res, void *data) | ||
| 683 | { | ||
| 684 | struct pwm_device **p = res; | ||
| 685 | |||
| 686 | if (WARN_ON(!p || !*p)) | ||
| 687 | return 0; | ||
| 688 | |||
| 689 | return *p == data; | ||
| 690 | } | ||
| 691 | |||
| 692 | /** | ||
| 693 | * devm_pwm_put() - resource managed pwm_put() | ||
| 694 | * @dev: device for PWM consumer | ||
| 695 | * @pwm: PWM device | ||
| 696 | * | ||
| 697 | * Release a PWM previously allocated using devm_pwm_get(). Calling this | ||
| 698 | * function is usually not needed because devm-allocated resources are | ||
| 699 | * automatically released on driver detach. | ||
| 700 | */ | ||
| 701 | void devm_pwm_put(struct device *dev, struct pwm_device *pwm) | ||
| 702 | { | ||
| 703 | WARN_ON(devres_release(dev, devm_pwm_release, devm_pwm_match, pwm)); | ||
| 704 | } | ||
| 705 | EXPORT_SYMBOL_GPL(devm_pwm_put); | ||
| 706 | |||
| 627 | #ifdef CONFIG_DEBUG_FS | 707 | #ifdef CONFIG_DEBUG_FS |
| 628 | static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) | 708 | static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) |
| 629 | { | 709 | { |
diff --git a/drivers/misc/ab8500-pwm.c b/drivers/pwm/pwm-ab8500.c index d7a9aa14e5d5..cfb72ca873d1 100644 --- a/drivers/misc/ab8500-pwm.c +++ b/drivers/pwm/pwm-ab8500.c | |||
| @@ -24,16 +24,12 @@ | |||
| 24 | #define ENABLE_PWM 1 | 24 | #define ENABLE_PWM 1 |
| 25 | #define DISABLE_PWM 0 | 25 | #define DISABLE_PWM 0 |
| 26 | 26 | ||
| 27 | struct pwm_device { | 27 | struct ab8500_pwm_chip { |
| 28 | struct device *dev; | 28 | struct pwm_chip chip; |
| 29 | struct list_head node; | ||
| 30 | const char *label; | ||
| 31 | unsigned int pwm_id; | ||
| 32 | }; | 29 | }; |
| 33 | 30 | ||
| 34 | static LIST_HEAD(pwm_list); | 31 | static int ab8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, |
| 35 | 32 | int duty_ns, int period_ns) | |
| 36 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | ||
| 37 | { | 33 | { |
| 38 | int ret = 0; | 34 | int ret = 0; |
| 39 | unsigned int higher_val, lower_val; | 35 | unsigned int higher_val, lower_val; |
| @@ -50,95 +46,94 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | |||
| 50 | */ | 46 | */ |
| 51 | higher_val = ((duty_ns & 0x0300) >> 8); | 47 | higher_val = ((duty_ns & 0x0300) >> 8); |
| 52 | 48 | ||
| 53 | reg = AB8500_PWM_OUT_CTRL1_REG + ((pwm->pwm_id - 1) * 2); | 49 | reg = AB8500_PWM_OUT_CTRL1_REG + ((chip->base - 1) * 2); |
| 54 | 50 | ||
| 55 | ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC, | 51 | ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC, |
| 56 | reg, (u8)lower_val); | 52 | reg, (u8)lower_val); |
| 57 | if (ret < 0) | 53 | if (ret < 0) |
| 58 | return ret; | 54 | return ret; |
| 59 | ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC, | 55 | ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC, |
| 60 | (reg + 1), (u8)higher_val); | 56 | (reg + 1), (u8)higher_val); |
| 61 | 57 | ||
| 62 | return ret; | 58 | return ret; |
| 63 | } | 59 | } |
| 64 | EXPORT_SYMBOL(pwm_config); | ||
| 65 | 60 | ||
| 66 | int pwm_enable(struct pwm_device *pwm) | 61 | static int ab8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 67 | { | 62 | { |
| 68 | int ret; | 63 | int ret; |
| 69 | 64 | ||
| 70 | ret = abx500_mask_and_set_register_interruptible(pwm->dev, | 65 | ret = abx500_mask_and_set_register_interruptible(chip->dev, |
| 71 | AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, | 66 | AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, |
| 72 | 1 << (pwm->pwm_id-1), ENABLE_PWM); | 67 | 1 << (chip->base - 1), ENABLE_PWM); |
| 73 | if (ret < 0) | 68 | if (ret < 0) |
| 74 | dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n", | 69 | dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", |
| 75 | pwm->label, ret); | 70 | pwm->label, ret); |
| 76 | return ret; | 71 | return ret; |
| 77 | } | 72 | } |
| 78 | EXPORT_SYMBOL(pwm_enable); | ||
| 79 | 73 | ||
| 80 | void pwm_disable(struct pwm_device *pwm) | 74 | static void ab8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 81 | { | 75 | { |
| 82 | int ret; | 76 | int ret; |
| 83 | 77 | ||
| 84 | ret = abx500_mask_and_set_register_interruptible(pwm->dev, | 78 | ret = abx500_mask_and_set_register_interruptible(chip->dev, |
| 85 | AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, | 79 | AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, |
| 86 | 1 << (pwm->pwm_id-1), DISABLE_PWM); | 80 | 1 << (chip->base - 1), DISABLE_PWM); |
| 87 | if (ret < 0) | 81 | if (ret < 0) |
| 88 | dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n", | 82 | dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", |
| 89 | pwm->label, ret); | 83 | pwm->label, ret); |
| 90 | return; | 84 | return; |
| 91 | } | 85 | } |
| 92 | EXPORT_SYMBOL(pwm_disable); | ||
| 93 | |||
| 94 | struct pwm_device *pwm_request(int pwm_id, const char *label) | ||
| 95 | { | ||
| 96 | struct pwm_device *pwm; | ||
| 97 | |||
| 98 | list_for_each_entry(pwm, &pwm_list, node) { | ||
| 99 | if (pwm->pwm_id == pwm_id) { | ||
| 100 | pwm->label = label; | ||
| 101 | pwm->pwm_id = pwm_id; | ||
| 102 | return pwm; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | return ERR_PTR(-ENOENT); | ||
| 107 | } | ||
| 108 | EXPORT_SYMBOL(pwm_request); | ||
| 109 | 86 | ||
| 110 | void pwm_free(struct pwm_device *pwm) | 87 | static const struct pwm_ops ab8500_pwm_ops = { |
| 111 | { | 88 | .config = ab8500_pwm_config, |
| 112 | pwm_disable(pwm); | 89 | .enable = ab8500_pwm_enable, |
| 113 | } | 90 | .disable = ab8500_pwm_disable, |
| 114 | EXPORT_SYMBOL(pwm_free); | 91 | }; |
| 115 | 92 | ||
| 116 | static int __devinit ab8500_pwm_probe(struct platform_device *pdev) | 93 | static int __devinit ab8500_pwm_probe(struct platform_device *pdev) |
| 117 | { | 94 | { |
| 118 | struct pwm_device *pwm; | 95 | struct ab8500_pwm_chip *ab8500; |
| 96 | int err; | ||
| 97 | |||
| 119 | /* | 98 | /* |
| 120 | * Nothing to be done in probe, this is required to get the | 99 | * Nothing to be done in probe, this is required to get the |
| 121 | * device which is required for ab8500 read and write | 100 | * device which is required for ab8500 read and write |
| 122 | */ | 101 | */ |
| 123 | pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); | 102 | ab8500 = kzalloc(sizeof(*ab8500), GFP_KERNEL); |
| 124 | if (pwm == NULL) { | 103 | if (ab8500 == NULL) { |
| 125 | dev_err(&pdev->dev, "failed to allocate memory\n"); | 104 | dev_err(&pdev->dev, "failed to allocate memory\n"); |
| 126 | return -ENOMEM; | 105 | return -ENOMEM; |
| 127 | } | 106 | } |
| 128 | pwm->dev = &pdev->dev; | 107 | |
| 129 | pwm->pwm_id = pdev->id; | 108 | ab8500->chip.dev = &pdev->dev; |
| 130 | list_add_tail(&pwm->node, &pwm_list); | 109 | ab8500->chip.ops = &ab8500_pwm_ops; |
| 131 | platform_set_drvdata(pdev, pwm); | 110 | ab8500->chip.base = pdev->id; |
| 132 | dev_dbg(pwm->dev, "pwm probe successful\n"); | 111 | ab8500->chip.npwm = 1; |
| 112 | |||
| 113 | err = pwmchip_add(&ab8500->chip); | ||
| 114 | if (err < 0) { | ||
| 115 | kfree(ab8500); | ||
| 116 | return err; | ||
| 117 | } | ||
| 118 | |||
| 119 | dev_dbg(&pdev->dev, "pwm probe successful\n"); | ||
| 120 | platform_set_drvdata(pdev, ab8500); | ||
| 121 | |||
| 133 | return 0; | 122 | return 0; |
| 134 | } | 123 | } |
| 135 | 124 | ||
| 136 | static int __devexit ab8500_pwm_remove(struct platform_device *pdev) | 125 | static int __devexit ab8500_pwm_remove(struct platform_device *pdev) |
| 137 | { | 126 | { |
| 138 | struct pwm_device *pwm = platform_get_drvdata(pdev); | 127 | struct ab8500_pwm_chip *ab8500 = platform_get_drvdata(pdev); |
| 139 | list_del(&pwm->node); | 128 | int err; |
| 129 | |||
| 130 | err = pwmchip_remove(&ab8500->chip); | ||
| 131 | if (err < 0) | ||
| 132 | return err; | ||
| 133 | |||
| 140 | dev_dbg(&pdev->dev, "pwm driver removed\n"); | 134 | dev_dbg(&pdev->dev, "pwm driver removed\n"); |
| 141 | kfree(pwm); | 135 | kfree(ab8500); |
| 136 | |||
| 142 | return 0; | 137 | return 0; |
| 143 | } | 138 | } |
| 144 | 139 | ||
| @@ -150,19 +145,8 @@ static struct platform_driver ab8500_pwm_driver = { | |||
| 150 | .probe = ab8500_pwm_probe, | 145 | .probe = ab8500_pwm_probe, |
| 151 | .remove = __devexit_p(ab8500_pwm_remove), | 146 | .remove = __devexit_p(ab8500_pwm_remove), |
| 152 | }; | 147 | }; |
| 148 | module_platform_driver(ab8500_pwm_driver); | ||
| 153 | 149 | ||
| 154 | static int __init ab8500_pwm_init(void) | ||
| 155 | { | ||
| 156 | return platform_driver_register(&ab8500_pwm_driver); | ||
| 157 | } | ||
| 158 | |||
| 159 | static void __exit ab8500_pwm_exit(void) | ||
| 160 | { | ||
| 161 | platform_driver_unregister(&ab8500_pwm_driver); | ||
| 162 | } | ||
| 163 | |||
| 164 | subsys_initcall(ab8500_pwm_init); | ||
| 165 | module_exit(ab8500_pwm_exit); | ||
| 166 | MODULE_AUTHOR("Arun MURTHY <arun.murthy@stericsson.com>"); | 150 | MODULE_AUTHOR("Arun MURTHY <arun.murthy@stericsson.com>"); |
| 167 | MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver"); | 151 | MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver"); |
| 168 | MODULE_ALIAS("platform:ab8500-pwm"); | 152 | MODULE_ALIAS("platform:ab8500-pwm"); |
diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c index d53c4e7941ef..5da8e185e838 100644 --- a/drivers/pwm/pwm-bfin.c +++ b/drivers/pwm/pwm-bfin.c | |||
| @@ -69,9 +69,6 @@ static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 69 | unsigned long period, duty; | 69 | unsigned long period, duty; |
| 70 | unsigned long long val; | 70 | unsigned long long val; |
| 71 | 71 | ||
| 72 | if (duty_ns < 0 || duty_ns > period_ns) | ||
| 73 | return -EINVAL; | ||
| 74 | |||
| 75 | val = (unsigned long long)get_sclk() * period_ns; | 72 | val = (unsigned long long)get_sclk() * period_ns; |
| 76 | do_div(val, NSEC_PER_SEC); | 73 | do_div(val, NSEC_PER_SEC); |
| 77 | period = val; | 74 | period = val; |
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 2a0b35333972..8a5d3ae2946a 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c | |||
| @@ -16,8 +16,7 @@ | |||
| 16 | #include <linux/clk.h> | 16 | #include <linux/clk.h> |
| 17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
| 18 | #include <linux/pwm.h> | 18 | #include <linux/pwm.h> |
| 19 | #include <mach/hardware.h> | 19 | #include <linux/of_device.h> |
| 20 | |||
| 21 | 20 | ||
| 22 | /* i.MX1 and i.MX21 share the same PWM function block: */ | 21 | /* i.MX1 and i.MX21 share the same PWM function block: */ |
| 23 | 22 | ||
| @@ -25,6 +24,7 @@ | |||
| 25 | #define MX1_PWMS 0x04 /* PWM Sample Register */ | 24 | #define MX1_PWMS 0x04 /* PWM Sample Register */ |
| 26 | #define MX1_PWMP 0x08 /* PWM Period Register */ | 25 | #define MX1_PWMP 0x08 /* PWM Period Register */ |
| 27 | 26 | ||
| 27 | #define MX1_PWMC_EN (1 << 4) | ||
| 28 | 28 | ||
| 29 | /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ | 29 | /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ |
| 30 | 30 | ||
| @@ -40,110 +40,165 @@ | |||
| 40 | #define MX3_PWMCR_EN (1 << 0) | 40 | #define MX3_PWMCR_EN (1 << 0) |
| 41 | 41 | ||
| 42 | struct imx_chip { | 42 | struct imx_chip { |
| 43 | struct clk *clk; | 43 | struct clk *clk_per; |
| 44 | struct clk *clk_ipg; | ||
| 44 | 45 | ||
| 45 | int clk_enabled; | 46 | int enabled; |
| 46 | void __iomem *mmio_base; | 47 | void __iomem *mmio_base; |
| 47 | 48 | ||
| 48 | struct pwm_chip chip; | 49 | struct pwm_chip chip; |
| 50 | |||
| 51 | int (*config)(struct pwm_chip *chip, | ||
| 52 | struct pwm_device *pwm, int duty_ns, int period_ns); | ||
| 53 | void (*set_enable)(struct pwm_chip *chip, bool enable); | ||
| 49 | }; | 54 | }; |
| 50 | 55 | ||
| 51 | #define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) | 56 | #define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) |
| 52 | 57 | ||
| 53 | static int imx_pwm_config(struct pwm_chip *chip, | 58 | static int imx_pwm_config_v1(struct pwm_chip *chip, |
| 54 | struct pwm_device *pwm, int duty_ns, int period_ns) | 59 | struct pwm_device *pwm, int duty_ns, int period_ns) |
| 55 | { | 60 | { |
| 56 | struct imx_chip *imx = to_imx_chip(chip); | 61 | struct imx_chip *imx = to_imx_chip(chip); |
| 57 | 62 | ||
| 58 | if (!(cpu_is_mx1() || cpu_is_mx21())) { | 63 | /* |
| 59 | unsigned long long c; | 64 | * The PWM subsystem allows for exact frequencies. However, |
| 60 | unsigned long period_cycles, duty_cycles, prescale; | 65 | * I cannot connect a scope on my device to the PWM line and |
| 61 | u32 cr; | 66 | * thus cannot provide the program the PWM controller |
| 62 | 67 | * exactly. Instead, I'm relying on the fact that the | |
| 63 | c = clk_get_rate(imx->clk); | 68 | * Bootloader (u-boot or WinCE+haret) has programmed the PWM |
| 64 | c = c * period_ns; | 69 | * function group already. So I'll just modify the PWM sample |
| 65 | do_div(c, 1000000000); | 70 | * register to follow the ratio of duty_ns vs. period_ns |
| 66 | period_cycles = c; | 71 | * accordingly. |
| 67 | 72 | * | |
| 68 | prescale = period_cycles / 0x10000 + 1; | 73 | * This is good enough for programming the brightness of |
| 69 | 74 | * the LCD backlight. | |
| 70 | period_cycles /= prescale; | 75 | * |
| 71 | c = (unsigned long long)period_cycles * duty_ns; | 76 | * The real implementation would divide PERCLK[0] first by |
| 72 | do_div(c, period_ns); | 77 | * both the prescaler (/1 .. /128) and then by CLKSEL |
| 73 | duty_cycles = c; | 78 | * (/2 .. /16). |
| 74 | 79 | */ | |
| 75 | /* | 80 | u32 max = readl(imx->mmio_base + MX1_PWMP); |
| 76 | * according to imx pwm RM, the real period value should be | 81 | u32 p = max * duty_ns / period_ns; |
| 77 | * PERIOD value in PWMPR plus 2. | 82 | writel(max - p, imx->mmio_base + MX1_PWMS); |
| 78 | */ | ||
| 79 | if (period_cycles > 2) | ||
| 80 | period_cycles -= 2; | ||
| 81 | else | ||
| 82 | period_cycles = 0; | ||
| 83 | |||
| 84 | writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); | ||
| 85 | writel(period_cycles, imx->mmio_base + MX3_PWMPR); | ||
| 86 | |||
| 87 | cr = MX3_PWMCR_PRESCALER(prescale) | | ||
| 88 | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | | ||
| 89 | MX3_PWMCR_DBGEN | MX3_PWMCR_EN; | ||
| 90 | |||
| 91 | if (cpu_is_mx25()) | ||
| 92 | cr |= MX3_PWMCR_CLKSRC_IPG; | ||
| 93 | else | ||
| 94 | cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; | ||
| 95 | |||
| 96 | writel(cr, imx->mmio_base + MX3_PWMCR); | ||
| 97 | } else if (cpu_is_mx1() || cpu_is_mx21()) { | ||
| 98 | /* The PWM subsystem allows for exact frequencies. However, | ||
| 99 | * I cannot connect a scope on my device to the PWM line and | ||
| 100 | * thus cannot provide the program the PWM controller | ||
| 101 | * exactly. Instead, I'm relying on the fact that the | ||
| 102 | * Bootloader (u-boot or WinCE+haret) has programmed the PWM | ||
| 103 | * function group already. So I'll just modify the PWM sample | ||
| 104 | * register to follow the ratio of duty_ns vs. period_ns | ||
| 105 | * accordingly. | ||
| 106 | * | ||
| 107 | * This is good enough for programming the brightness of | ||
| 108 | * the LCD backlight. | ||
| 109 | * | ||
| 110 | * The real implementation would divide PERCLK[0] first by | ||
| 111 | * both the prescaler (/1 .. /128) and then by CLKSEL | ||
| 112 | * (/2 .. /16). | ||
| 113 | */ | ||
| 114 | u32 max = readl(imx->mmio_base + MX1_PWMP); | ||
| 115 | u32 p = max * duty_ns / period_ns; | ||
| 116 | writel(max - p, imx->mmio_base + MX1_PWMS); | ||
| 117 | } else { | ||
| 118 | BUG(); | ||
| 119 | } | ||
| 120 | 83 | ||
| 121 | return 0; | 84 | return 0; |
| 122 | } | 85 | } |
| 123 | 86 | ||
| 87 | static void imx_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) | ||
| 88 | { | ||
| 89 | struct imx_chip *imx = to_imx_chip(chip); | ||
| 90 | u32 val; | ||
| 91 | |||
| 92 | val = readl(imx->mmio_base + MX1_PWMC); | ||
| 93 | |||
| 94 | if (enable) | ||
| 95 | val |= MX1_PWMC_EN; | ||
| 96 | else | ||
| 97 | val &= ~MX1_PWMC_EN; | ||
| 98 | |||
| 99 | writel(val, imx->mmio_base + MX1_PWMC); | ||
| 100 | } | ||
| 101 | |||
| 102 | static int imx_pwm_config_v2(struct pwm_chip *chip, | ||
| 103 | struct pwm_device *pwm, int duty_ns, int period_ns) | ||
| 104 | { | ||
| 105 | struct imx_chip *imx = to_imx_chip(chip); | ||
| 106 | unsigned long long c; | ||
| 107 | unsigned long period_cycles, duty_cycles, prescale; | ||
| 108 | u32 cr; | ||
| 109 | |||
| 110 | c = clk_get_rate(imx->clk_per); | ||
| 111 | c = c * period_ns; | ||
| 112 | do_div(c, 1000000000); | ||
| 113 | period_cycles = c; | ||
| 114 | |||
| 115 | prescale = period_cycles / 0x10000 + 1; | ||
| 116 | |||
| 117 | period_cycles /= prescale; | ||
| 118 | c = (unsigned long long)period_cycles * duty_ns; | ||
| 119 | do_div(c, period_ns); | ||
| 120 | duty_cycles = c; | ||
| 121 | |||
| 122 | /* | ||
| 123 | * according to imx pwm RM, the real period value should be | ||
| 124 | * PERIOD value in PWMPR plus 2. | ||
| 125 | */ | ||
| 126 | if (period_cycles > 2) | ||
| 127 | period_cycles -= 2; | ||
| 128 | else | ||
| 129 | period_cycles = 0; | ||
| 130 | |||
| 131 | writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); | ||
| 132 | writel(period_cycles, imx->mmio_base + MX3_PWMPR); | ||
| 133 | |||
| 134 | cr = MX3_PWMCR_PRESCALER(prescale) | | ||
| 135 | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | | ||
| 136 | MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; | ||
| 137 | |||
| 138 | if (imx->enabled) | ||
| 139 | cr |= MX3_PWMCR_EN; | ||
| 140 | |||
| 141 | writel(cr, imx->mmio_base + MX3_PWMCR); | ||
| 142 | |||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) | ||
| 147 | { | ||
| 148 | struct imx_chip *imx = to_imx_chip(chip); | ||
| 149 | u32 val; | ||
| 150 | |||
| 151 | val = readl(imx->mmio_base + MX3_PWMCR); | ||
| 152 | |||
| 153 | if (enable) | ||
| 154 | val |= MX3_PWMCR_EN; | ||
| 155 | else | ||
| 156 | val &= ~MX3_PWMCR_EN; | ||
| 157 | |||
| 158 | writel(val, imx->mmio_base + MX3_PWMCR); | ||
| 159 | } | ||
| 160 | |||
| 161 | static int imx_pwm_config(struct pwm_chip *chip, | ||
| 162 | struct pwm_device *pwm, int duty_ns, int period_ns) | ||
| 163 | { | ||
| 164 | struct imx_chip *imx = to_imx_chip(chip); | ||
| 165 | int ret; | ||
| 166 | |||
| 167 | ret = clk_prepare_enable(imx->clk_ipg); | ||
| 168 | if (ret) | ||
| 169 | return ret; | ||
| 170 | |||
| 171 | ret = imx->config(chip, pwm, duty_ns, period_ns); | ||
| 172 | |||
| 173 | clk_disable_unprepare(imx->clk_ipg); | ||
| 174 | |||
| 175 | return ret; | ||
| 176 | } | ||
| 177 | |||
| 124 | static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 178 | static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 125 | { | 179 | { |
| 126 | struct imx_chip *imx = to_imx_chip(chip); | 180 | struct imx_chip *imx = to_imx_chip(chip); |
| 127 | int rc = 0; | 181 | int ret; |
| 128 | 182 | ||
| 129 | if (!imx->clk_enabled) { | 183 | ret = clk_prepare_enable(imx->clk_per); |
| 130 | rc = clk_prepare_enable(imx->clk); | 184 | if (ret) |
| 131 | if (!rc) | 185 | return ret; |
| 132 | imx->clk_enabled = 1; | 186 | |
| 133 | } | 187 | imx->set_enable(chip, true); |
| 134 | return rc; | 188 | |
| 189 | imx->enabled = 1; | ||
| 190 | |||
| 191 | return 0; | ||
| 135 | } | 192 | } |
| 136 | 193 | ||
| 137 | static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | 194 | static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 138 | { | 195 | { |
| 139 | struct imx_chip *imx = to_imx_chip(chip); | 196 | struct imx_chip *imx = to_imx_chip(chip); |
| 140 | 197 | ||
| 141 | writel(0, imx->mmio_base + MX3_PWMCR); | 198 | imx->set_enable(chip, false); |
| 142 | 199 | ||
| 143 | if (imx->clk_enabled) { | 200 | clk_disable_unprepare(imx->clk_per); |
| 144 | clk_disable_unprepare(imx->clk); | 201 | imx->enabled = 0; |
| 145 | imx->clk_enabled = 0; | ||
| 146 | } | ||
| 147 | } | 202 | } |
| 148 | 203 | ||
| 149 | static struct pwm_ops imx_pwm_ops = { | 204 | static struct pwm_ops imx_pwm_ops = { |
| @@ -153,30 +208,66 @@ static struct pwm_ops imx_pwm_ops = { | |||
| 153 | .owner = THIS_MODULE, | 208 | .owner = THIS_MODULE, |
| 154 | }; | 209 | }; |
| 155 | 210 | ||
| 211 | struct imx_pwm_data { | ||
| 212 | int (*config)(struct pwm_chip *chip, | ||
| 213 | struct pwm_device *pwm, int duty_ns, int period_ns); | ||
| 214 | void (*set_enable)(struct pwm_chip *chip, bool enable); | ||
| 215 | }; | ||
| 216 | |||
| 217 | static struct imx_pwm_data imx_pwm_data_v1 = { | ||
| 218 | .config = imx_pwm_config_v1, | ||
| 219 | .set_enable = imx_pwm_set_enable_v1, | ||
| 220 | }; | ||
| 221 | |||
| 222 | static struct imx_pwm_data imx_pwm_data_v2 = { | ||
| 223 | .config = imx_pwm_config_v2, | ||
| 224 | .set_enable = imx_pwm_set_enable_v2, | ||
| 225 | }; | ||
| 226 | |||
| 227 | static const struct of_device_id imx_pwm_dt_ids[] = { | ||
| 228 | { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, }, | ||
| 229 | { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, }, | ||
| 230 | { /* sentinel */ } | ||
| 231 | }; | ||
| 232 | MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids); | ||
| 233 | |||
| 156 | static int __devinit imx_pwm_probe(struct platform_device *pdev) | 234 | static int __devinit imx_pwm_probe(struct platform_device *pdev) |
| 157 | { | 235 | { |
| 236 | const struct of_device_id *of_id = | ||
| 237 | of_match_device(imx_pwm_dt_ids, &pdev->dev); | ||
| 238 | struct imx_pwm_data *data; | ||
| 158 | struct imx_chip *imx; | 239 | struct imx_chip *imx; |
| 159 | struct resource *r; | 240 | struct resource *r; |
| 160 | int ret = 0; | 241 | int ret = 0; |
| 161 | 242 | ||
| 243 | if (!of_id) | ||
| 244 | return -ENODEV; | ||
| 245 | |||
| 162 | imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); | 246 | imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); |
| 163 | if (imx == NULL) { | 247 | if (imx == NULL) { |
| 164 | dev_err(&pdev->dev, "failed to allocate memory\n"); | 248 | dev_err(&pdev->dev, "failed to allocate memory\n"); |
| 165 | return -ENOMEM; | 249 | return -ENOMEM; |
| 166 | } | 250 | } |
| 167 | 251 | ||
| 168 | imx->clk = devm_clk_get(&pdev->dev, "pwm"); | 252 | imx->clk_per = devm_clk_get(&pdev->dev, "per"); |
| 253 | if (IS_ERR(imx->clk_per)) { | ||
| 254 | dev_err(&pdev->dev, "getting per clock failed with %ld\n", | ||
| 255 | PTR_ERR(imx->clk_per)); | ||
| 256 | return PTR_ERR(imx->clk_per); | ||
| 257 | } | ||
| 169 | 258 | ||
| 170 | if (IS_ERR(imx->clk)) | 259 | imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); |
| 171 | return PTR_ERR(imx->clk); | 260 | if (IS_ERR(imx->clk_ipg)) { |
| 261 | dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", | ||
| 262 | PTR_ERR(imx->clk_ipg)); | ||
| 263 | return PTR_ERR(imx->clk_ipg); | ||
| 264 | } | ||
| 172 | 265 | ||
| 173 | imx->chip.ops = &imx_pwm_ops; | 266 | imx->chip.ops = &imx_pwm_ops; |
| 174 | imx->chip.dev = &pdev->dev; | 267 | imx->chip.dev = &pdev->dev; |
| 175 | imx->chip.base = -1; | 268 | imx->chip.base = -1; |
| 176 | imx->chip.npwm = 1; | 269 | imx->chip.npwm = 1; |
| 177 | 270 | ||
| 178 | imx->clk_enabled = 0; | ||
| 179 | |||
| 180 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 271 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 181 | if (r == NULL) { | 272 | if (r == NULL) { |
| 182 | dev_err(&pdev->dev, "no memory resource defined\n"); | 273 | dev_err(&pdev->dev, "no memory resource defined\n"); |
| @@ -187,6 +278,10 @@ static int __devinit imx_pwm_probe(struct platform_device *pdev) | |||
| 187 | if (imx->mmio_base == NULL) | 278 | if (imx->mmio_base == NULL) |
| 188 | return -EADDRNOTAVAIL; | 279 | return -EADDRNOTAVAIL; |
| 189 | 280 | ||
| 281 | data = of_id->data; | ||
| 282 | imx->config = data->config; | ||
| 283 | imx->set_enable = data->set_enable; | ||
| 284 | |||
| 190 | ret = pwmchip_add(&imx->chip); | 285 | ret = pwmchip_add(&imx->chip); |
| 191 | if (ret < 0) | 286 | if (ret < 0) |
| 192 | return ret; | 287 | return ret; |
| @@ -208,23 +303,14 @@ static int __devexit imx_pwm_remove(struct platform_device *pdev) | |||
| 208 | 303 | ||
| 209 | static struct platform_driver imx_pwm_driver = { | 304 | static struct platform_driver imx_pwm_driver = { |
| 210 | .driver = { | 305 | .driver = { |
| 211 | .name = "mxc_pwm", | 306 | .name = "imx-pwm", |
| 307 | .of_match_table = of_match_ptr(imx_pwm_dt_ids), | ||
| 212 | }, | 308 | }, |
| 213 | .probe = imx_pwm_probe, | 309 | .probe = imx_pwm_probe, |
| 214 | .remove = __devexit_p(imx_pwm_remove), | 310 | .remove = __devexit_p(imx_pwm_remove), |
| 215 | }; | 311 | }; |
| 216 | 312 | ||
| 217 | static int __init imx_pwm_init(void) | 313 | module_platform_driver(imx_pwm_driver); |
| 218 | { | ||
| 219 | return platform_driver_register(&imx_pwm_driver); | ||
| 220 | } | ||
| 221 | arch_initcall(imx_pwm_init); | ||
| 222 | |||
| 223 | static void __exit imx_pwm_exit(void) | ||
| 224 | { | ||
| 225 | platform_driver_unregister(&imx_pwm_driver); | ||
| 226 | } | ||
| 227 | module_exit(imx_pwm_exit); | ||
| 228 | 314 | ||
| 229 | MODULE_LICENSE("GPL v2"); | 315 | MODULE_LICENSE("GPL v2"); |
| 230 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | 316 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); |
diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c new file mode 100644 index 000000000000..10250fcefb98 --- /dev/null +++ b/drivers/pwm/pwm-jz4740.c | |||
| @@ -0,0 +1,221 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
| 3 | * JZ4740 platform PWM support | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License as published by the | ||
| 7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 8 | * option) any later version. | ||
| 9 | * | ||
| 10 | * You should have received a copy of the GNU General Public License along | ||
| 11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/clk.h> | ||
| 17 | #include <linux/err.h> | ||
| 18 | #include <linux/gpio.h> | ||
| 19 | #include <linux/kernel.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/pwm.h> | ||
| 23 | |||
| 24 | #include <asm/mach-jz4740/gpio.h> | ||
| 25 | #include <asm/mach-jz4740/timer.h> | ||
| 26 | |||
| 27 | #define NUM_PWM 8 | ||
| 28 | |||
| 29 | static const unsigned int jz4740_pwm_gpio_list[NUM_PWM] = { | ||
| 30 | JZ_GPIO_PWM0, | ||
| 31 | JZ_GPIO_PWM1, | ||
| 32 | JZ_GPIO_PWM2, | ||
| 33 | JZ_GPIO_PWM3, | ||
| 34 | JZ_GPIO_PWM4, | ||
| 35 | JZ_GPIO_PWM5, | ||
| 36 | JZ_GPIO_PWM6, | ||
| 37 | JZ_GPIO_PWM7, | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct jz4740_pwm_chip { | ||
| 41 | struct pwm_chip chip; | ||
| 42 | struct clk *clk; | ||
| 43 | }; | ||
| 44 | |||
| 45 | static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) | ||
| 46 | { | ||
| 47 | return container_of(chip, struct jz4740_pwm_chip, chip); | ||
| 48 | } | ||
| 49 | |||
| 50 | static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 51 | { | ||
| 52 | unsigned int gpio = jz4740_pwm_gpio_list[pwm->hwpwm]; | ||
| 53 | int ret; | ||
| 54 | |||
| 55 | /* | ||
| 56 | * Timers 0 and 1 are used for system tasks, so they are unavailable | ||
| 57 | * for use as PWMs. | ||
| 58 | */ | ||
| 59 | if (pwm->hwpwm < 2) | ||
| 60 | return -EBUSY; | ||
| 61 | |||
| 62 | ret = gpio_request(gpio, pwm->label); | ||
| 63 | if (ret) { | ||
| 64 | dev_err(chip->dev, "Failed to request GPIO#%u for PWM: %d\n", | ||
| 65 | gpio, ret); | ||
| 66 | return ret; | ||
| 67 | } | ||
| 68 | |||
| 69 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_PWM); | ||
| 70 | |||
| 71 | jz4740_timer_start(pwm->hwpwm); | ||
| 72 | |||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 77 | { | ||
| 78 | unsigned int gpio = jz4740_pwm_gpio_list[pwm->hwpwm]; | ||
| 79 | |||
| 80 | jz4740_timer_set_ctrl(pwm->hwpwm, 0); | ||
| 81 | |||
| 82 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); | ||
| 83 | gpio_free(gpio); | ||
| 84 | |||
| 85 | jz4740_timer_stop(pwm->hwpwm); | ||
| 86 | } | ||
| 87 | |||
| 88 | static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 89 | { | ||
| 90 | uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm); | ||
| 91 | |||
| 92 | ctrl |= JZ_TIMER_CTRL_PWM_ENABLE; | ||
| 93 | jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); | ||
| 94 | jz4740_timer_enable(pwm->hwpwm); | ||
| 95 | |||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 100 | { | ||
| 101 | uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm); | ||
| 102 | |||
| 103 | ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; | ||
| 104 | jz4740_timer_disable(pwm->hwpwm); | ||
| 105 | jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); | ||
| 106 | } | ||
| 107 | |||
| 108 | static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 109 | int duty_ns, int period_ns) | ||
| 110 | { | ||
| 111 | struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip); | ||
| 112 | unsigned long long tmp; | ||
| 113 | unsigned long period, duty; | ||
| 114 | unsigned int prescaler = 0; | ||
| 115 | uint16_t ctrl; | ||
| 116 | bool is_enabled; | ||
| 117 | |||
| 118 | tmp = (unsigned long long)clk_get_rate(jz4740->clk) * period_ns; | ||
| 119 | do_div(tmp, 1000000000); | ||
| 120 | period = tmp; | ||
| 121 | |||
| 122 | while (period > 0xffff && prescaler < 6) { | ||
| 123 | period >>= 2; | ||
| 124 | ++prescaler; | ||
| 125 | } | ||
| 126 | |||
| 127 | if (prescaler == 6) | ||
| 128 | return -EINVAL; | ||
| 129 | |||
| 130 | tmp = (unsigned long long)period * duty_ns; | ||
| 131 | do_div(tmp, period_ns); | ||
| 132 | duty = period - tmp; | ||
| 133 | |||
| 134 | if (duty >= period) | ||
| 135 | duty = period - 1; | ||
| 136 | |||
| 137 | is_enabled = jz4740_timer_is_enabled(pwm->hwpwm); | ||
| 138 | if (is_enabled) | ||
| 139 | jz4740_pwm_disable(chip, pwm); | ||
| 140 | |||
| 141 | jz4740_timer_set_count(pwm->hwpwm, 0); | ||
| 142 | jz4740_timer_set_duty(pwm->hwpwm, duty); | ||
| 143 | jz4740_timer_set_period(pwm->hwpwm, period); | ||
| 144 | |||
| 145 | ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | | ||
| 146 | JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; | ||
| 147 | |||
| 148 | jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); | ||
| 149 | |||
| 150 | if (is_enabled) | ||
| 151 | jz4740_pwm_enable(chip, pwm); | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static const struct pwm_ops jz4740_pwm_ops = { | ||
| 157 | .request = jz4740_pwm_request, | ||
| 158 | .free = jz4740_pwm_free, | ||
| 159 | .config = jz4740_pwm_config, | ||
| 160 | .enable = jz4740_pwm_enable, | ||
| 161 | .disable = jz4740_pwm_disable, | ||
| 162 | .owner = THIS_MODULE, | ||
| 163 | }; | ||
| 164 | |||
| 165 | static int __devinit jz4740_pwm_probe(struct platform_device *pdev) | ||
| 166 | { | ||
| 167 | struct jz4740_pwm_chip *jz4740; | ||
| 168 | int ret; | ||
| 169 | |||
| 170 | jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL); | ||
| 171 | if (!jz4740) | ||
| 172 | return -ENOMEM; | ||
| 173 | |||
| 174 | jz4740->clk = clk_get(NULL, "ext"); | ||
| 175 | if (IS_ERR(jz4740->clk)) | ||
| 176 | return PTR_ERR(jz4740->clk); | ||
| 177 | |||
| 178 | jz4740->chip.dev = &pdev->dev; | ||
| 179 | jz4740->chip.ops = &jz4740_pwm_ops; | ||
| 180 | jz4740->chip.npwm = NUM_PWM; | ||
| 181 | jz4740->chip.base = -1; | ||
| 182 | |||
| 183 | ret = pwmchip_add(&jz4740->chip); | ||
| 184 | if (ret < 0) { | ||
| 185 | clk_put(jz4740->clk); | ||
| 186 | return ret; | ||
| 187 | } | ||
| 188 | |||
| 189 | platform_set_drvdata(pdev, jz4740); | ||
| 190 | |||
| 191 | return 0; | ||
| 192 | } | ||
| 193 | |||
| 194 | static int __devexit jz4740_pwm_remove(struct platform_device *pdev) | ||
| 195 | { | ||
| 196 | struct jz4740_pwm_chip *jz4740 = platform_get_drvdata(pdev); | ||
| 197 | int ret; | ||
| 198 | |||
| 199 | ret = pwmchip_remove(&jz4740->chip); | ||
| 200 | if (ret < 0) | ||
| 201 | return ret; | ||
| 202 | |||
| 203 | clk_put(jz4740->clk); | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | static struct platform_driver jz4740_pwm_driver = { | ||
| 209 | .driver = { | ||
| 210 | .name = "jz4740-pwm", | ||
| 211 | .owner = THIS_MODULE, | ||
| 212 | }, | ||
| 213 | .probe = jz4740_pwm_probe, | ||
| 214 | .remove = __devexit_p(jz4740_pwm_remove), | ||
| 215 | }; | ||
| 216 | module_platform_driver(jz4740_pwm_driver); | ||
| 217 | |||
| 218 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
| 219 | MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver"); | ||
| 220 | MODULE_ALIAS("platform:jz4740-pwm"); | ||
| 221 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pwm/pwm-puv3.c b/drivers/pwm/pwm-puv3.c new file mode 100644 index 000000000000..2a93f37c46ad --- /dev/null +++ b/drivers/pwm/pwm-puv3.c | |||
| @@ -0,0 +1,161 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/unicore32/kernel/pwm.c | ||
| 3 | * | ||
| 4 | * Code specific to PKUnity SoC and UniCore ISA | ||
| 5 | * | ||
| 6 | * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> | ||
| 7 | * Copyright (C) 2001-2010 Guan Xuetao | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/err.h> | ||
| 19 | #include <linux/clk.h> | ||
| 20 | #include <linux/io.h> | ||
| 21 | #include <linux/pwm.h> | ||
| 22 | |||
| 23 | #include <asm/div64.h> | ||
| 24 | #include <mach/hardware.h> | ||
| 25 | |||
| 26 | struct puv3_pwm_chip { | ||
| 27 | struct pwm_chip chip; | ||
| 28 | void __iomem *base; | ||
| 29 | struct clk *clk; | ||
| 30 | bool enabled; | ||
| 31 | }; | ||
| 32 | |||
| 33 | static inline struct puv3_pwm_chip *to_puv3(struct pwm_chip *chip) | ||
| 34 | { | ||
| 35 | return container_of(chip, struct puv3_pwm_chip, chip); | ||
| 36 | } | ||
| 37 | |||
| 38 | /* | ||
| 39 | * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE | ||
| 40 | * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE | ||
| 41 | */ | ||
| 42 | static int puv3_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 43 | int duty_ns, int period_ns) | ||
| 44 | { | ||
| 45 | unsigned long period_cycles, prescale, pv, dc; | ||
| 46 | struct puv3_pwm_chip *puv3 = to_puv3(chip); | ||
| 47 | unsigned long long c; | ||
| 48 | |||
| 49 | c = clk_get_rate(puv3->clk); | ||
| 50 | c = c * period_ns; | ||
| 51 | do_div(c, 1000000000); | ||
| 52 | period_cycles = c; | ||
| 53 | |||
| 54 | if (period_cycles < 1) | ||
| 55 | period_cycles = 1; | ||
| 56 | |||
| 57 | prescale = (period_cycles - 1) / 1024; | ||
| 58 | pv = period_cycles / (prescale + 1) - 1; | ||
| 59 | |||
| 60 | if (prescale > 63) | ||
| 61 | return -EINVAL; | ||
| 62 | |||
| 63 | if (duty_ns == period_ns) | ||
| 64 | dc = OST_PWMDCCR_FDCYCLE; | ||
| 65 | else | ||
| 66 | dc = (pv + 1) * duty_ns / period_ns; | ||
| 67 | |||
| 68 | /* | ||
| 69 | * NOTE: the clock to PWM has to be enabled first | ||
| 70 | * before writing to the registers | ||
| 71 | */ | ||
| 72 | clk_prepare_enable(puv3->clk); | ||
| 73 | |||
| 74 | writel(prescale, puv3->base + OST_PWM_PWCR); | ||
| 75 | writel(pv - dc, puv3->base + OST_PWM_DCCR); | ||
| 76 | writel(pv, puv3->base + OST_PWM_PCR); | ||
| 77 | |||
| 78 | clk_disable_unprepare(puv3->clk); | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | static int puv3_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 84 | { | ||
| 85 | struct puv3_pwm_chip *puv3 = to_puv3(chip); | ||
| 86 | |||
| 87 | return clk_prepare_enable(puv3->clk); | ||
| 88 | } | ||
| 89 | |||
| 90 | static void puv3_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 91 | { | ||
| 92 | struct puv3_pwm_chip *puv3 = to_puv3(chip); | ||
| 93 | |||
| 94 | clk_disable_unprepare(puv3->clk); | ||
| 95 | } | ||
| 96 | |||
| 97 | static const struct pwm_ops puv3_pwm_ops = { | ||
| 98 | .config = puv3_pwm_config, | ||
| 99 | .enable = puv3_pwm_enable, | ||
| 100 | .disable = puv3_pwm_disable, | ||
| 101 | .owner = THIS_MODULE, | ||
| 102 | }; | ||
| 103 | |||
| 104 | static int __devinit pwm_probe(struct platform_device *pdev) | ||
| 105 | { | ||
| 106 | struct puv3_pwm_chip *puv3; | ||
| 107 | struct resource *r; | ||
| 108 | int ret; | ||
| 109 | |||
| 110 | puv3 = devm_kzalloc(&pdev->dev, sizeof(*puv3), GFP_KERNEL); | ||
| 111 | if (puv3 == NULL) { | ||
| 112 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 113 | return -ENOMEM; | ||
| 114 | } | ||
| 115 | |||
| 116 | puv3->clk = devm_clk_get(&pdev->dev, "OST_CLK"); | ||
| 117 | if (IS_ERR(puv3->clk)) | ||
| 118 | return PTR_ERR(puv3->clk); | ||
| 119 | |||
| 120 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 121 | if (r == NULL) { | ||
| 122 | dev_err(&pdev->dev, "no memory resource defined\n"); | ||
| 123 | return -ENODEV; | ||
| 124 | } | ||
| 125 | |||
| 126 | puv3->base = devm_request_and_ioremap(&pdev->dev, r); | ||
| 127 | if (puv3->base == NULL) | ||
| 128 | return -EADDRNOTAVAIL; | ||
| 129 | |||
| 130 | puv3->chip.dev = &pdev->dev; | ||
| 131 | puv3->chip.ops = &puv3_pwm_ops; | ||
| 132 | puv3->chip.base = -1; | ||
| 133 | puv3->chip.npwm = 1; | ||
| 134 | |||
| 135 | ret = pwmchip_add(&puv3->chip); | ||
| 136 | if (ret < 0) { | ||
| 137 | dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); | ||
| 138 | return ret; | ||
| 139 | } | ||
| 140 | |||
| 141 | platform_set_drvdata(pdev, puv3); | ||
| 142 | return 0; | ||
| 143 | } | ||
| 144 | |||
| 145 | static int __devexit pwm_remove(struct platform_device *pdev) | ||
| 146 | { | ||
| 147 | struct puv3_pwm_chip *puv3 = platform_get_drvdata(pdev); | ||
| 148 | |||
| 149 | return pwmchip_remove(&puv3->chip); | ||
| 150 | } | ||
| 151 | |||
| 152 | static struct platform_driver puv3_pwm_driver = { | ||
| 153 | .driver = { | ||
| 154 | .name = "PKUnity-v3-PWM", | ||
| 155 | }, | ||
| 156 | .probe = pwm_probe, | ||
| 157 | .remove = __devexit_p(pwm_remove), | ||
| 158 | }; | ||
| 159 | module_platform_driver(puv3_pwm_driver); | ||
| 160 | |||
| 161 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index bd5867a1c700..260c3a88564d 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c | |||
| @@ -70,9 +70,6 @@ static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 70 | unsigned long offset; | 70 | unsigned long offset; |
| 71 | int rc; | 71 | int rc; |
| 72 | 72 | ||
| 73 | if (period_ns == 0 || duty_ns > period_ns) | ||
| 74 | return -EINVAL; | ||
| 75 | |||
| 76 | offset = pwm->hwpwm ? 0x10 : 0; | 73 | offset = pwm->hwpwm ? 0x10 : 0; |
| 77 | 74 | ||
| 78 | c = clk_get_rate(pc->clk); | 75 | c = clk_get_rate(pc->clk); |
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index e5187c0ade9f..023a3bee76e7 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c | |||
| @@ -126,9 +126,6 @@ static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 126 | if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ) | 126 | if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ) |
| 127 | return -ERANGE; | 127 | return -ERANGE; |
| 128 | 128 | ||
| 129 | if (duty_ns > period_ns) | ||
| 130 | return -EINVAL; | ||
| 131 | |||
| 132 | if (period_ns == s3c->period_ns && | 129 | if (period_ns == s3c->period_ns && |
| 133 | duty_ns == s3c->duty_ns) | 130 | duty_ns == s3c->duty_ns) |
| 134 | return 0; | 131 | return 0; |
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 4b6688909fee..d6d4cf05565e 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #define CAP3 0x10 | 32 | #define CAP3 0x10 |
| 33 | #define CAP4 0x14 | 33 | #define CAP4 0x14 |
| 34 | #define ECCTL2 0x2A | 34 | #define ECCTL2 0x2A |
| 35 | #define ECCTL2_APWM_POL_LOW BIT(10) | ||
| 35 | #define ECCTL2_APWM_MODE BIT(9) | 36 | #define ECCTL2_APWM_MODE BIT(9) |
| 36 | #define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6)) | 37 | #define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6)) |
| 37 | #define ECCTL2_TSCTR_FREERUN BIT(4) | 38 | #define ECCTL2_TSCTR_FREERUN BIT(4) |
| @@ -59,7 +60,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 59 | unsigned long period_cycles, duty_cycles; | 60 | unsigned long period_cycles, duty_cycles; |
| 60 | unsigned int reg_val; | 61 | unsigned int reg_val; |
| 61 | 62 | ||
| 62 | if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) | 63 | if (period_ns > NSEC_PER_SEC) |
| 63 | return -ERANGE; | 64 | return -ERANGE; |
| 64 | 65 | ||
| 65 | c = pc->clk_rate; | 66 | c = pc->clk_rate; |
| @@ -111,6 +112,26 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 111 | return 0; | 112 | return 0; |
| 112 | } | 113 | } |
| 113 | 114 | ||
| 115 | static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 116 | enum pwm_polarity polarity) | ||
| 117 | { | ||
| 118 | struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); | ||
| 119 | unsigned short reg_val; | ||
| 120 | |||
| 121 | pm_runtime_get_sync(pc->chip.dev); | ||
| 122 | reg_val = readw(pc->mmio_base + ECCTL2); | ||
| 123 | if (polarity == PWM_POLARITY_INVERSED) | ||
| 124 | /* Duty cycle defines LOW period of PWM */ | ||
| 125 | reg_val |= ECCTL2_APWM_POL_LOW; | ||
| 126 | else | ||
| 127 | /* Duty cycle defines HIGH period of PWM */ | ||
| 128 | reg_val &= ~ECCTL2_APWM_POL_LOW; | ||
| 129 | |||
| 130 | writew(reg_val, pc->mmio_base + ECCTL2); | ||
| 131 | pm_runtime_put_sync(pc->chip.dev); | ||
| 132 | return 0; | ||
| 133 | } | ||
| 134 | |||
| 114 | static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 135 | static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 115 | { | 136 | { |
| 116 | struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); | 137 | struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); |
| @@ -157,6 +178,7 @@ static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 157 | static const struct pwm_ops ecap_pwm_ops = { | 178 | static const struct pwm_ops ecap_pwm_ops = { |
| 158 | .free = ecap_pwm_free, | 179 | .free = ecap_pwm_free, |
| 159 | .config = ecap_pwm_config, | 180 | .config = ecap_pwm_config, |
| 181 | .set_polarity = ecap_pwm_set_polarity, | ||
| 160 | .enable = ecap_pwm_enable, | 182 | .enable = ecap_pwm_enable, |
| 161 | .disable = ecap_pwm_disable, | 183 | .disable = ecap_pwm_disable, |
| 162 | .owner = THIS_MODULE, | 184 | .owner = THIS_MODULE, |
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index b1996bcd5b78..d3c1dff0a0dc 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c | |||
| @@ -81,6 +81,15 @@ | |||
| 81 | #define AQCTL_ZRO_FRCHIGH BIT(1) | 81 | #define AQCTL_ZRO_FRCHIGH BIT(1) |
| 82 | #define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0)) | 82 | #define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0)) |
| 83 | 83 | ||
| 84 | #define AQCTL_CHANA_POLNORMAL (AQCTL_CAU_FRCLOW | AQCTL_PRD_FRCHIGH | \ | ||
| 85 | AQCTL_ZRO_FRCHIGH) | ||
| 86 | #define AQCTL_CHANA_POLINVERSED (AQCTL_CAU_FRCHIGH | AQCTL_PRD_FRCLOW | \ | ||
| 87 | AQCTL_ZRO_FRCLOW) | ||
| 88 | #define AQCTL_CHANB_POLNORMAL (AQCTL_CBU_FRCLOW | AQCTL_PRD_FRCHIGH | \ | ||
| 89 | AQCTL_ZRO_FRCHIGH) | ||
| 90 | #define AQCTL_CHANB_POLINVERSED (AQCTL_CBU_FRCHIGH | AQCTL_PRD_FRCLOW | \ | ||
| 91 | AQCTL_ZRO_FRCLOW) | ||
| 92 | |||
| 84 | #define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6)) | 93 | #define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6)) |
| 85 | #define AQSFRC_RLDCSF_ZRO 0 | 94 | #define AQSFRC_RLDCSF_ZRO 0 |
| 86 | #define AQSFRC_RLDCSF_PRD BIT(6) | 95 | #define AQSFRC_RLDCSF_PRD BIT(6) |
| @@ -105,6 +114,7 @@ struct ehrpwm_pwm_chip { | |||
| 105 | unsigned int clk_rate; | 114 | unsigned int clk_rate; |
| 106 | void __iomem *mmio_base; | 115 | void __iomem *mmio_base; |
| 107 | unsigned long period_cycles[NUM_PWM_CHANNEL]; | 116 | unsigned long period_cycles[NUM_PWM_CHANNEL]; |
| 117 | enum pwm_polarity polarity[NUM_PWM_CHANNEL]; | ||
| 108 | }; | 118 | }; |
| 109 | 119 | ||
| 110 | static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) | 120 | static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) |
| @@ -165,39 +175,37 @@ static int set_prescale_div(unsigned long rqst_prescaler, | |||
| 165 | return 1; | 175 | return 1; |
| 166 | } | 176 | } |
| 167 | 177 | ||
| 168 | static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan, | 178 | static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) |
| 169 | unsigned long duty_cycles) | ||
| 170 | { | 179 | { |
| 171 | int cmp_reg, aqctl_reg; | 180 | int aqctl_reg; |
| 172 | unsigned short aqctl_val, aqctl_mask; | 181 | unsigned short aqctl_val, aqctl_mask; |
| 173 | 182 | ||
| 174 | /* | 183 | /* |
| 175 | * Channels can be configured from action qualifier module. | 184 | * Configure PWM output to HIGH/LOW level on counter |
| 176 | * Channel 0 configured with compare A register and for | 185 | * reaches compare register value and LOW/HIGH level |
| 177 | * up-counter mode. | 186 | * on counter value reaches period register value and |
| 178 | * Channel 1 configured with compare B register and for | 187 | * zero value on counter |
| 179 | * up-counter mode. | ||
| 180 | */ | 188 | */ |
| 181 | if (chan == 1) { | 189 | if (chan == 1) { |
| 182 | aqctl_reg = AQCTLB; | 190 | aqctl_reg = AQCTLB; |
| 183 | cmp_reg = CMPB; | ||
| 184 | /* Configure PWM Low from compare B value */ | ||
| 185 | aqctl_val = AQCTL_CBU_FRCLOW; | ||
| 186 | aqctl_mask = AQCTL_CBU_MASK; | 191 | aqctl_mask = AQCTL_CBU_MASK; |
| 192 | |||
| 193 | if (pc->polarity[chan] == PWM_POLARITY_INVERSED) | ||
| 194 | aqctl_val = AQCTL_CHANB_POLINVERSED; | ||
| 195 | else | ||
| 196 | aqctl_val = AQCTL_CHANB_POLNORMAL; | ||
| 187 | } else { | 197 | } else { |
| 188 | cmp_reg = CMPA; | ||
| 189 | aqctl_reg = AQCTLA; | 198 | aqctl_reg = AQCTLA; |
| 190 | /* Configure PWM Low from compare A value*/ | ||
| 191 | aqctl_val = AQCTL_CAU_FRCLOW; | ||
| 192 | aqctl_mask = AQCTL_CAU_MASK; | 199 | aqctl_mask = AQCTL_CAU_MASK; |
| 200 | |||
| 201 | if (pc->polarity[chan] == PWM_POLARITY_INVERSED) | ||
| 202 | aqctl_val = AQCTL_CHANA_POLINVERSED; | ||
| 203 | else | ||
| 204 | aqctl_val = AQCTL_CHANA_POLNORMAL; | ||
| 193 | } | 205 | } |
| 194 | 206 | ||
| 195 | /* Configure PWM High from period value and zero value */ | ||
| 196 | aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH; | ||
| 197 | aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; | 207 | aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; |
| 198 | ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); | 208 | ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); |
| 199 | |||
| 200 | ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); | ||
| 201 | } | 209 | } |
| 202 | 210 | ||
| 203 | /* | 211 | /* |
| @@ -211,9 +219,9 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 211 | unsigned long long c; | 219 | unsigned long long c; |
| 212 | unsigned long period_cycles, duty_cycles; | 220 | unsigned long period_cycles, duty_cycles; |
| 213 | unsigned short ps_divval, tb_divval; | 221 | unsigned short ps_divval, tb_divval; |
| 214 | int i; | 222 | int i, cmp_reg; |
| 215 | 223 | ||
| 216 | if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) | 224 | if (period_ns > NSEC_PER_SEC) |
| 217 | return -ERANGE; | 225 | return -ERANGE; |
| 218 | 226 | ||
| 219 | c = pc->clk_rate; | 227 | c = pc->clk_rate; |
| @@ -278,12 +286,29 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 278 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, | 286 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, |
| 279 | TBCTL_CTRMODE_UP); | 287 | TBCTL_CTRMODE_UP); |
| 280 | 288 | ||
| 281 | /* Configure the channel for duty cycle */ | 289 | if (pwm->hwpwm == 1) |
| 282 | configure_chans(pc, pwm->hwpwm, duty_cycles); | 290 | /* Channel 1 configured with compare B register */ |
| 291 | cmp_reg = CMPB; | ||
| 292 | else | ||
| 293 | /* Channel 0 configured with compare A register */ | ||
| 294 | cmp_reg = CMPA; | ||
| 295 | |||
| 296 | ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); | ||
| 297 | |||
| 283 | pm_runtime_put_sync(chip->dev); | 298 | pm_runtime_put_sync(chip->dev); |
| 284 | return 0; | 299 | return 0; |
| 285 | } | 300 | } |
| 286 | 301 | ||
| 302 | static int ehrpwm_pwm_set_polarity(struct pwm_chip *chip, | ||
| 303 | struct pwm_device *pwm, enum pwm_polarity polarity) | ||
| 304 | { | ||
| 305 | struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); | ||
| 306 | |||
| 307 | /* Configuration of polarity in hardware delayed, do at enable */ | ||
| 308 | pc->polarity[pwm->hwpwm] = polarity; | ||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | |||
| 287 | static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 312 | static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 288 | { | 313 | { |
| 289 | struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); | 314 | struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); |
| @@ -307,6 +332,9 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 307 | 332 | ||
| 308 | ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); | 333 | ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); |
| 309 | 334 | ||
| 335 | /* Channels polarity can be configured from action qualifier module */ | ||
| 336 | configure_polarity(pc, pwm->hwpwm); | ||
| 337 | |||
| 310 | /* Enable time counter for free_run */ | 338 | /* Enable time counter for free_run */ |
| 311 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); | 339 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); |
| 312 | return 0; | 340 | return 0; |
| @@ -358,6 +386,7 @@ static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 358 | static const struct pwm_ops ehrpwm_pwm_ops = { | 386 | static const struct pwm_ops ehrpwm_pwm_ops = { |
| 359 | .free = ehrpwm_pwm_free, | 387 | .free = ehrpwm_pwm_free, |
| 360 | .config = ehrpwm_pwm_config, | 388 | .config = ehrpwm_pwm_config, |
| 389 | .set_polarity = ehrpwm_pwm_set_polarity, | ||
| 361 | .enable = ehrpwm_pwm_enable, | 390 | .enable = ehrpwm_pwm_enable, |
| 362 | .disable = ehrpwm_pwm_disable, | 391 | .disable = ehrpwm_pwm_disable, |
| 363 | .owner = THIS_MODULE, | 392 | .owner = THIS_MODULE, |
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 995f0164c9b0..069983ca49ff 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c | |||
| @@ -213,7 +213,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) | |||
| 213 | pb->exit = data->exit; | 213 | pb->exit = data->exit; |
| 214 | pb->dev = &pdev->dev; | 214 | pb->dev = &pdev->dev; |
| 215 | 215 | ||
| 216 | pb->pwm = pwm_get(&pdev->dev, NULL); | 216 | pb->pwm = devm_pwm_get(&pdev->dev, NULL); |
| 217 | if (IS_ERR(pb->pwm)) { | 217 | if (IS_ERR(pb->pwm)) { |
| 218 | dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n"); | 218 | dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n"); |
| 219 | 219 | ||
| @@ -246,7 +246,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) | |||
| 246 | if (IS_ERR(bl)) { | 246 | if (IS_ERR(bl)) { |
| 247 | dev_err(&pdev->dev, "failed to register backlight\n"); | 247 | dev_err(&pdev->dev, "failed to register backlight\n"); |
| 248 | ret = PTR_ERR(bl); | 248 | ret = PTR_ERR(bl); |
| 249 | goto err_bl; | 249 | goto err_alloc; |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | bl->props.brightness = data->dft_brightness; | 252 | bl->props.brightness = data->dft_brightness; |
| @@ -255,8 +255,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) | |||
| 255 | platform_set_drvdata(pdev, bl); | 255 | platform_set_drvdata(pdev, bl); |
| 256 | return 0; | 256 | return 0; |
| 257 | 257 | ||
| 258 | err_bl: | ||
| 259 | pwm_put(pb->pwm); | ||
| 260 | err_alloc: | 258 | err_alloc: |
| 261 | if (data->exit) | 259 | if (data->exit) |
| 262 | data->exit(&pdev->dev); | 260 | data->exit(&pdev->dev); |
| @@ -271,7 +269,6 @@ static int pwm_backlight_remove(struct platform_device *pdev) | |||
| 271 | backlight_device_unregister(bl); | 269 | backlight_device_unregister(bl); |
| 272 | pwm_config(pb->pwm, 0, pb->period); | 270 | pwm_config(pb->pwm, 0, pb->period); |
| 273 | pwm_disable(pb->pwm); | 271 | pwm_disable(pb->pwm); |
| 274 | pwm_put(pb->pwm); | ||
| 275 | if (pb->exit) | 272 | if (pb->exit) |
| 276 | pb->exit(&pdev->dev); | 273 | pb->exit(&pdev->dev); |
| 277 | return 0; | 274 | return 0; |
diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 21d076c5089e..112b31436848 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h | |||
| @@ -1,11 +1,13 @@ | |||
| 1 | #ifndef __LINUX_PWM_H | 1 | #ifndef __LINUX_PWM_H |
| 2 | #define __LINUX_PWM_H | 2 | #define __LINUX_PWM_H |
| 3 | 3 | ||
| 4 | #include <linux/err.h> | ||
| 4 | #include <linux/of.h> | 5 | #include <linux/of.h> |
| 5 | 6 | ||
| 6 | struct pwm_device; | 7 | struct pwm_device; |
| 7 | struct seq_file; | 8 | struct seq_file; |
| 8 | 9 | ||
| 10 | #if IS_ENABLED(CONFIG_PWM) || IS_ENABLED(CONFIG_HAVE_PWM) | ||
| 9 | /* | 11 | /* |
| 10 | * pwm_request - request a PWM device | 12 | * pwm_request - request a PWM device |
| 11 | */ | 13 | */ |
| @@ -30,10 +32,47 @@ int pwm_enable(struct pwm_device *pwm); | |||
| 30 | * pwm_disable - stop a PWM output toggling | 32 | * pwm_disable - stop a PWM output toggling |
| 31 | */ | 33 | */ |
| 32 | void pwm_disable(struct pwm_device *pwm); | 34 | void pwm_disable(struct pwm_device *pwm); |
| 35 | #else | ||
| 36 | static inline struct pwm_device *pwm_request(int pwm_id, const char *label) | ||
| 37 | { | ||
| 38 | return ERR_PTR(-ENODEV); | ||
| 39 | } | ||
| 40 | |||
| 41 | static inline void pwm_free(struct pwm_device *pwm) | ||
| 42 | { | ||
| 43 | } | ||
| 44 | |||
| 45 | static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | ||
| 46 | { | ||
| 47 | return -EINVAL; | ||
| 48 | } | ||
| 49 | |||
| 50 | static inline int pwm_enable(struct pwm_device *pwm) | ||
| 51 | { | ||
| 52 | return -EINVAL; | ||
| 53 | } | ||
| 54 | |||
| 55 | static inline void pwm_disable(struct pwm_device *pwm) | ||
| 56 | { | ||
| 57 | } | ||
| 58 | #endif | ||
| 33 | 59 | ||
| 34 | #ifdef CONFIG_PWM | ||
| 35 | struct pwm_chip; | 60 | struct pwm_chip; |
| 36 | 61 | ||
| 62 | /** | ||
| 63 | * enum pwm_polarity - polarity of a PWM signal | ||
| 64 | * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty- | ||
| 65 | * cycle, followed by a low signal for the remainder of the pulse | ||
| 66 | * period | ||
| 67 | * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty- | ||
| 68 | * cycle, followed by a high signal for the remainder of the pulse | ||
| 69 | * period | ||
| 70 | */ | ||
| 71 | enum pwm_polarity { | ||
| 72 | PWM_POLARITY_NORMAL, | ||
| 73 | PWM_POLARITY_INVERSED, | ||
| 74 | }; | ||
| 75 | |||
| 37 | enum { | 76 | enum { |
| 38 | PWMF_REQUESTED = 1 << 0, | 77 | PWMF_REQUESTED = 1 << 0, |
| 39 | PWMF_ENABLED = 1 << 1, | 78 | PWMF_ENABLED = 1 << 1, |
| @@ -61,11 +100,17 @@ static inline unsigned int pwm_get_period(struct pwm_device *pwm) | |||
| 61 | return pwm ? pwm->period : 0; | 100 | return pwm ? pwm->period : 0; |
| 62 | } | 101 | } |
| 63 | 102 | ||
| 103 | /* | ||
| 104 | * pwm_set_polarity - configure the polarity of a PWM signal | ||
| 105 | */ | ||
| 106 | int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity); | ||
| 107 | |||
| 64 | /** | 108 | /** |
| 65 | * struct pwm_ops - PWM controller operations | 109 | * struct pwm_ops - PWM controller operations |
| 66 | * @request: optional hook for requesting a PWM | 110 | * @request: optional hook for requesting a PWM |
| 67 | * @free: optional hook for freeing a PWM | 111 | * @free: optional hook for freeing a PWM |
| 68 | * @config: configure duty cycles and period length for this PWM | 112 | * @config: configure duty cycles and period length for this PWM |
| 113 | * @set_polarity: configure the polarity of this PWM | ||
| 69 | * @enable: enable PWM output toggling | 114 | * @enable: enable PWM output toggling |
| 70 | * @disable: disable PWM output toggling | 115 | * @disable: disable PWM output toggling |
| 71 | * @dbg_show: optional routine to show contents in debugfs | 116 | * @dbg_show: optional routine to show contents in debugfs |
| @@ -79,6 +124,9 @@ struct pwm_ops { | |||
| 79 | int (*config)(struct pwm_chip *chip, | 124 | int (*config)(struct pwm_chip *chip, |
| 80 | struct pwm_device *pwm, | 125 | struct pwm_device *pwm, |
| 81 | int duty_ns, int period_ns); | 126 | int duty_ns, int period_ns); |
| 127 | int (*set_polarity)(struct pwm_chip *chip, | ||
| 128 | struct pwm_device *pwm, | ||
| 129 | enum pwm_polarity polarity); | ||
| 82 | int (*enable)(struct pwm_chip *chip, | 130 | int (*enable)(struct pwm_chip *chip, |
| 83 | struct pwm_device *pwm); | 131 | struct pwm_device *pwm); |
| 84 | void (*disable)(struct pwm_chip *chip, | 132 | void (*disable)(struct pwm_chip *chip, |
| @@ -113,6 +161,7 @@ struct pwm_chip { | |||
| 113 | unsigned int of_pwm_n_cells; | 161 | unsigned int of_pwm_n_cells; |
| 114 | }; | 162 | }; |
| 115 | 163 | ||
| 164 | #if IS_ENABLED(CONFIG_PWM) | ||
| 116 | int pwm_set_chip_data(struct pwm_device *pwm, void *data); | 165 | int pwm_set_chip_data(struct pwm_device *pwm, void *data); |
| 117 | void *pwm_get_chip_data(struct pwm_device *pwm); | 166 | void *pwm_get_chip_data(struct pwm_device *pwm); |
| 118 | 167 | ||
| @@ -125,6 +174,57 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, | |||
| 125 | struct pwm_device *pwm_get(struct device *dev, const char *consumer); | 174 | struct pwm_device *pwm_get(struct device *dev, const char *consumer); |
| 126 | void pwm_put(struct pwm_device *pwm); | 175 | void pwm_put(struct pwm_device *pwm); |
| 127 | 176 | ||
| 177 | struct pwm_device *devm_pwm_get(struct device *dev, const char *consumer); | ||
| 178 | void devm_pwm_put(struct device *dev, struct pwm_device *pwm); | ||
| 179 | #else | ||
| 180 | static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data) | ||
| 181 | { | ||
| 182 | return -EINVAL; | ||
| 183 | } | ||
| 184 | |||
| 185 | static inline void *pwm_get_chip_data(struct pwm_device *pwm) | ||
| 186 | { | ||
| 187 | return NULL; | ||
| 188 | } | ||
| 189 | |||
| 190 | static inline int pwmchip_add(struct pwm_chip *chip) | ||
| 191 | { | ||
| 192 | return -EINVAL; | ||
| 193 | } | ||
| 194 | |||
| 195 | static inline int pwmchip_remove(struct pwm_chip *chip) | ||
| 196 | { | ||
| 197 | return -EINVAL; | ||
| 198 | } | ||
| 199 | |||
| 200 | static inline struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, | ||
| 201 | unsigned int index, | ||
| 202 | const char *label) | ||
| 203 | { | ||
| 204 | return ERR_PTR(-ENODEV); | ||
| 205 | } | ||
| 206 | |||
| 207 | static inline struct pwm_device *pwm_get(struct device *dev, | ||
| 208 | const char *consumer) | ||
| 209 | { | ||
| 210 | return ERR_PTR(-ENODEV); | ||
| 211 | } | ||
| 212 | |||
| 213 | static inline void pwm_put(struct pwm_device *pwm) | ||
| 214 | { | ||
| 215 | } | ||
| 216 | |||
| 217 | static inline struct pwm_device *devm_pwm_get(struct device *dev, | ||
| 218 | const char *consumer) | ||
| 219 | { | ||
| 220 | return ERR_PTR(-ENODEV); | ||
| 221 | } | ||
| 222 | |||
| 223 | static inline void devm_pwm_put(struct device *dev, struct pwm_device *pwm) | ||
| 224 | { | ||
| 225 | } | ||
| 226 | #endif | ||
| 227 | |||
| 128 | struct pwm_lookup { | 228 | struct pwm_lookup { |
| 129 | struct list_head list; | 229 | struct list_head list; |
| 130 | const char *provider; | 230 | const char *provider; |
| @@ -141,8 +241,12 @@ struct pwm_lookup { | |||
| 141 | .con_id = _con_id, \ | 241 | .con_id = _con_id, \ |
| 142 | } | 242 | } |
| 143 | 243 | ||
| 244 | #if IS_ENABLED(CONFIG_PWM) | ||
| 144 | void pwm_add_table(struct pwm_lookup *table, size_t num); | 245 | void pwm_add_table(struct pwm_lookup *table, size_t num); |
| 145 | 246 | #else | |
| 247 | static inline void pwm_add_table(struct pwm_lookup *table, size_t num) | ||
| 248 | { | ||
| 249 | } | ||
| 146 | #endif | 250 | #endif |
| 147 | 251 | ||
| 148 | #endif /* __LINUX_PWM_H */ | 252 | #endif /* __LINUX_PWM_H */ |
