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 */ |