aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Reding <thierry.reding@avionic-design.de>2012-08-22 04:01:24 -0400
committerThierry Reding <thierry.reding@avionic-design.de>2012-10-05 14:56:42 -0400
commitf6b8a5700057cc1b531c2f9b7806428a6f83b467 (patch)
treebf019c60548d37c21c8f1ca00a7968a425ea53da
parent46a98765133bd880916415d431698797010d4ba8 (diff)
pwm: Add Ingenic JZ4740 support
This commit moves the driver to drivers/pwm and converts it to the new PWM framework. Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de> Acked-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--arch/mips/include/asm/mach-jz4740/platform.h1
-rw-r--r--arch/mips/jz4740/Kconfig3
-rw-r--r--arch/mips/jz4740/Makefile2
-rw-r--r--arch/mips/jz4740/board-qi_lb60.c1
-rw-r--r--arch/mips/jz4740/platform.c6
-rw-r--r--arch/mips/jz4740/pwm.c177
-rw-r--r--drivers/pwm/Kconfig11
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-jz4740.c221
9 files changed, 241 insertions, 182 deletions
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;
31extern struct platform_device jz4740_codec_device; 31extern struct platform_device jz4740_codec_device;
32extern struct platform_device jz4740_adc_device; 32extern struct platform_device jz4740_adc_device;
33extern struct platform_device jz4740_wdt_device; 33extern struct platform_device jz4740_wdt_device;
34extern struct platform_device jz4740_pwm_device;
34 35
35void jz4740_serial_device_register(void); 36void jz4740_serial_device_register(void);
36 37
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
9endchoice 9endchoice
10
11config 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
7obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ 7obj-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
10obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o 10obj-$(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 */
328struct 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
26static struct clk *jz4740_pwm_clk;
27
28DEFINE_MUTEX(jz4740_pwm_mutex);
29
30struct pwm_device {
31 unsigned int id;
32 unsigned int gpio;
33 bool used;
34};
35
36static 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
45struct 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
81void 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
94int 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
144int 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
155void 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
164static 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}
177subsys_initcall(jz4740_pwm_init);
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index d954c72e242e..e67800581d60 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -1,6 +1,5 @@
1menuconfig PWM 1menuconfig PWM
2 bool "Pulse-Width Modulation (PWM) Support" 2 bool "Pulse-Width Modulation (PWM) Support"
3 depends on !MACH_JZ4740
4 help 3 help
5 Generic Pulse-Width Modulation (PWM) support. 4 Generic Pulse-Width Modulation (PWM) support.
6 5
@@ -56,6 +55,16 @@ config PWM_IMX
56 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
57 will be called pwm-imx. 56 will be called pwm-imx.
58 57
58config 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
59config PWM_LPC32XX 68config PWM_LPC32XX
60 tristate "LPC32XX PWM support" 69 tristate "LPC32XX PWM support"
61 depends on ARCH_LPC32XX 70 depends on ARCH_LPC32XX
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 360aad8fa3ae..29cf57e2c25e 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_PWM) += core.o
2obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o 2obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
3obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o 3obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
4obj-$(CONFIG_PWM_IMX) += pwm-imx.o 4obj-$(CONFIG_PWM_IMX) += pwm-imx.o
5obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
5obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o 6obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
6obj-$(CONFIG_PWM_MXS) += pwm-mxs.o 7obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
7obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o 8obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o
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
29static 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
40struct jz4740_pwm_chip {
41 struct pwm_chip chip;
42 struct clk *clk;
43};
44
45static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
46{
47 return container_of(chip, struct jz4740_pwm_chip, chip);
48}
49
50static 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
76static 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
88static 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
99static 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
108static 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
156static 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
165static 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
194static 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
208static 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};
216module_platform_driver(jz4740_pwm_driver);
217
218MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
219MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver");
220MODULE_ALIAS("platform:jz4740-pwm");
221MODULE_LICENSE("GPL");