kernel: update kernel versions
[openwrt.git] / target / linux / xburst / patches-2.6.35 / 011-pwm.patch
1 From 5cd5a44b94b451ecaf593bb49919cfbb51ccb622 Mon Sep 17 00:00:00 2001
2 From: Lars-Peter Clausen <lars@metafoo.de>
3 Date: Sat, 17 Jul 2010 11:12:20 +0000
4 Subject: [PATCH] MIPS: JZ4740: Add PWM support
5
6 Add support for the PWM part of the timer unit on a JZ4740 SoC.
7
8 Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
9 Cc: linux-mips@linux-mips.org
10 Cc: linux-kernel@vger.kernel.org
11 Patchwork: https://patchwork.linux-mips.org/patch/1468/
12 Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
13 ---
14 arch/mips/jz4740/pwm.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++
15 1 files changed, 177 insertions(+), 0 deletions(-)
16 create mode 100644 arch/mips/jz4740/pwm.c
17
18 --- /dev/null
19 +++ b/arch/mips/jz4740/pwm.c
20 @@ -0,0 +1,177 @@
21 +/*
22 + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
23 + * JZ4740 platform PWM support
24 + *
25 + * This program is free software; you can redistribute it and/or modify it
26 + * under the terms of the GNU General Public License as published by the
27 + * Free Software Foundation; either version 2 of the License, or (at your
28 + * option) any later version.
29 + *
30 + * You should have received a copy of the GNU General Public License along
31 + * with this program; if not, write to the Free Software Foundation, Inc.,
32 + * 675 Mass Ave, Cambridge, MA 02139, USA.
33 + *
34 + */
35 +
36 +#include <linux/kernel.h>
37 +
38 +#include <linux/clk.h>
39 +#include <linux/err.h>
40 +#include <linux/pwm.h>
41 +#include <linux/gpio.h>
42 +
43 +#include <asm/mach-jz4740/gpio.h>
44 +#include "timer.h"
45 +
46 +static struct clk *jz4740_pwm_clk;
47 +
48 +DEFINE_MUTEX(jz4740_pwm_mutex);
49 +
50 +struct pwm_device {
51 + unsigned int id;
52 + unsigned int gpio;
53 + bool used;
54 +};
55 +
56 +static struct pwm_device jz4740_pwm_list[] = {
57 + { 2, JZ_GPIO_PWM2, false },
58 + { 3, JZ_GPIO_PWM3, false },
59 + { 4, JZ_GPIO_PWM4, false },
60 + { 5, JZ_GPIO_PWM5, false },
61 + { 6, JZ_GPIO_PWM6, false },
62 + { 7, JZ_GPIO_PWM7, false },
63 +};
64 +
65 +struct pwm_device *pwm_request(int id, const char *label)
66 +{
67 + int ret = 0;
68 + struct pwm_device *pwm;
69 +
70 + if (id < 2 || id > 7 || !jz4740_pwm_clk)
71 + return ERR_PTR(-ENODEV);
72 +
73 + mutex_lock(&jz4740_pwm_mutex);
74 +
75 + pwm = &jz4740_pwm_list[id - 2];
76 + if (pwm->used)
77 + ret = -EBUSY;
78 + else
79 + pwm->used = true;
80 +
81 + mutex_unlock(&jz4740_pwm_mutex);
82 +
83 + if (ret)
84 + return ERR_PTR(ret);
85 +
86 + ret = gpio_request(pwm->gpio, label);
87 +
88 + if (ret) {
89 + printk(KERN_ERR "Failed to request pwm gpio: %d\n", ret);
90 + pwm->used = false;
91 + return ERR_PTR(ret);
92 + }
93 +
94 + jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_PWM);
95 +
96 + jz4740_timer_start(id);
97 +
98 + return pwm;
99 +}
100 +
101 +void pwm_free(struct pwm_device *pwm)
102 +{
103 + pwm_disable(pwm);
104 + jz4740_timer_set_ctrl(pwm->id, 0);
105 +
106 + jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_NONE);
107 + gpio_free(pwm->gpio);
108 +
109 + jz4740_timer_stop(pwm->id);
110 +
111 + pwm->used = false;
112 +}
113 +
114 +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
115 +{
116 + unsigned long long tmp;
117 + unsigned long period, duty;
118 + unsigned int prescaler = 0;
119 + unsigned int id = pwm->id;
120 + uint16_t ctrl;
121 + bool is_enabled;
122 +
123 + if (duty_ns < 0 || duty_ns > period_ns)
124 + return -EINVAL;
125 +
126 + tmp = (unsigned long long)clk_get_rate(jz4740_pwm_clk) * period_ns;
127 + do_div(tmp, 1000000000);
128 + period = tmp;
129 +
130 + while (period > 0xffff && prescaler < 6) {
131 + period >>= 2;
132 + ++prescaler;
133 + }
134 +
135 + if (prescaler == 6)
136 + return -EINVAL;
137 +
138 + tmp = (unsigned long long)period * duty_ns;
139 + do_div(tmp, period_ns);
140 + duty = period - tmp;
141 +
142 + if (duty >= period)
143 + duty = period - 1;
144 +
145 + is_enabled = jz4740_timer_is_enabled(id);
146 + if (is_enabled)
147 + pwm_disable(pwm);
148 +
149 + jz4740_timer_set_count(id, 0);
150 + jz4740_timer_set_duty(id, duty);
151 + jz4740_timer_set_period(id, period);
152 +
153 + ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT |
154 + JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN;
155 +
156 + jz4740_timer_set_ctrl(id, ctrl);
157 +
158 + if (is_enabled)
159 + pwm_enable(pwm);
160 +
161 + return 0;
162 +}
163 +
164 +int pwm_enable(struct pwm_device *pwm)
165 +{
166 + uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id);
167 +
168 + ctrl |= JZ_TIMER_CTRL_PWM_ENABLE;
169 + jz4740_timer_set_ctrl(pwm->id, ctrl);
170 + jz4740_timer_enable(pwm->id);
171 +
172 + return 0;
173 +}
174 +
175 +void pwm_disable(struct pwm_device *pwm)
176 +{
177 + uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id);
178 +
179 + ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE;
180 + jz4740_timer_disable(pwm->id);
181 + jz4740_timer_set_ctrl(pwm->id, ctrl);
182 +}
183 +
184 +static int __init jz4740_pwm_init(void)
185 +{
186 + int ret = 0;
187 +
188 + jz4740_pwm_clk = clk_get(NULL, "ext");
189 +
190 + if (IS_ERR(jz4740_pwm_clk)) {
191 + ret = PTR_ERR(jz4740_pwm_clk);
192 + jz4740_pwm_clk = NULL;
193 + }
194 +
195 + return ret;
196 +}
197 +subsys_initcall(jz4740_pwm_init);
This page took 0.056373 seconds and 5 git commands to generate.