• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/mips/jz4740/
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);
178