Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  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);