Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pwm-jz4740.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
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] = {
38 };
39 
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 
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 
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 
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 |
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 <[email protected]>");
219 MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver");
220 MODULE_ALIAS("platform:jz4740-pwm");
221 MODULE_LICENSE("GPL");