diff options
author | Tim Kryger <tim.kryger@linaro.org> | 2014-04-25 14:31:12 -0400 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2014-04-28 07:07:44 -0400 |
commit | 6a4e4bff9699ee1d77e51242a2982411cff6c280 (patch) | |
tree | 4d02149e0ee9cd5af4550fe483d08c8311a4e656 /drivers/pwm | |
parent | 810b4f51e8d0c2de9685f4addbf5ede7e589dd20 (diff) |
pwm: kona: Introduce Kona PWM controller support
Add support for the six-channel Kona PWM controller found on Broadcom
mobile SoCs like bcm281xx.
Signed-off-by: Tim Kryger <tim.kryger@linaro.org>
Reviewed-by: Alex Elder <elder@linaro.org>
Reviewed-by: Markus Mayer <markus.mayer@linaro.org>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm')
-rw-r--r-- | drivers/pwm/Kconfig | 9 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-bcm-kona.c | 318 |
3 files changed, 328 insertions, 0 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 5b34ff29ea38..4ad7b89a4cb4 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
@@ -62,6 +62,15 @@ config PWM_ATMEL_TCB | |||
62 | To compile this driver as a module, choose M here: the module | 62 | To compile this driver as a module, choose M here: the module |
63 | will be called pwm-atmel-tcb. | 63 | will be called pwm-atmel-tcb. |
64 | 64 | ||
65 | config PWM_BCM_KONA | ||
66 | tristate "Kona PWM support" | ||
67 | depends on ARCH_BCM_MOBILE | ||
68 | help | ||
69 | Generic PWM framework driver for Broadcom Kona PWM block. | ||
70 | |||
71 | To compile this driver as a module, choose M here: the module | ||
72 | will be called pwm-bcm-kona. | ||
73 | |||
65 | config PWM_BFIN | 74 | config PWM_BFIN |
66 | tristate "Blackfin PWM support" | 75 | tristate "Blackfin PWM support" |
67 | depends on BFIN_GPTIMERS | 76 | depends on BFIN_GPTIMERS |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index e57d2c38a794..5c86a19d5d39 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
@@ -3,6 +3,7 @@ obj-$(CONFIG_PWM_SYSFS) += sysfs.o | |||
3 | obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o | 3 | obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o |
4 | obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o | 4 | obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o |
5 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o | 5 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o |
6 | obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o | ||
6 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o | 7 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o |
7 | obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o | 8 | obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o |
8 | obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o | 9 | obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o |
diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c new file mode 100644 index 000000000000..02bc048892a9 --- /dev/null +++ b/drivers/pwm/pwm-bcm-kona.c | |||
@@ -0,0 +1,318 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Broadcom Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation version 2. | ||
7 | * | ||
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
9 | * kind, whether express or implied; without even the implied warranty | ||
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/clk.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/ioport.h> | ||
19 | #include <linux/math64.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/pwm.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/types.h> | ||
26 | |||
27 | /* | ||
28 | * The Kona PWM has some unusual characteristics. Here are the main points. | ||
29 | * | ||
30 | * 1) There is no disable bit and the hardware docs advise programming a zero | ||
31 | * duty to achieve output equivalent to that of a normal disable operation. | ||
32 | * | ||
33 | * 2) Changes to prescale, duty, period, and polarity do not take effect until | ||
34 | * a subsequent rising edge of the trigger bit. | ||
35 | * | ||
36 | * 3) If the smooth bit and trigger bit are both low, the output is a constant | ||
37 | * high signal. Otherwise, the earlier waveform continues to be output. | ||
38 | * | ||
39 | * 4) If the smooth bit is set on the rising edge of the trigger bit, output | ||
40 | * will transition to the new settings on a period boundary (which could be | ||
41 | * seconds away). If the smooth bit is clear, new settings will be applied | ||
42 | * as soon as possible (the hardware always has a 400ns delay). | ||
43 | * | ||
44 | * 5) When the external clock that feeds the PWM is disabled, output is pegged | ||
45 | * high or low depending on its state at that exact instant. | ||
46 | */ | ||
47 | |||
48 | #define PWM_CONTROL_OFFSET (0x00000000) | ||
49 | #define PWM_CONTROL_SMOOTH_SHIFT(chan) (24 + (chan)) | ||
50 | #define PWM_CONTROL_TYPE_SHIFT(chan) (16 + (chan)) | ||
51 | #define PWM_CONTROL_POLARITY_SHIFT(chan) (8 + (chan)) | ||
52 | #define PWM_CONTROL_TRIGGER_SHIFT(chan) (chan) | ||
53 | |||
54 | #define PRESCALE_OFFSET (0x00000004) | ||
55 | #define PRESCALE_SHIFT(chan) ((chan) << 2) | ||
56 | #define PRESCALE_MASK(chan) (0x7 << PRESCALE_SHIFT(chan)) | ||
57 | #define PRESCALE_MIN (0x00000000) | ||
58 | #define PRESCALE_MAX (0x00000007) | ||
59 | |||
60 | #define PERIOD_COUNT_OFFSET(chan) (0x00000008 + ((chan) << 3)) | ||
61 | #define PERIOD_COUNT_MIN (0x00000002) | ||
62 | #define PERIOD_COUNT_MAX (0x00ffffff) | ||
63 | |||
64 | #define DUTY_CYCLE_HIGH_OFFSET(chan) (0x0000000c + ((chan) << 3)) | ||
65 | #define DUTY_CYCLE_HIGH_MIN (0x00000000) | ||
66 | #define DUTY_CYCLE_HIGH_MAX (0x00ffffff) | ||
67 | |||
68 | struct kona_pwmc { | ||
69 | struct pwm_chip chip; | ||
70 | void __iomem *base; | ||
71 | struct clk *clk; | ||
72 | }; | ||
73 | |||
74 | static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *_chip) | ||
75 | { | ||
76 | return container_of(_chip, struct kona_pwmc, chip); | ||
77 | } | ||
78 | |||
79 | static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan) | ||
80 | { | ||
81 | unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET); | ||
82 | |||
83 | /* Clear trigger bit but set smooth bit to maintain old output */ | ||
84 | value |= 1 << PWM_CONTROL_SMOOTH_SHIFT(chan); | ||
85 | value &= ~(1 << PWM_CONTROL_TRIGGER_SHIFT(chan)); | ||
86 | writel(value, kp->base + PWM_CONTROL_OFFSET); | ||
87 | |||
88 | /* Set trigger bit and clear smooth bit to apply new settings */ | ||
89 | value &= ~(1 << PWM_CONTROL_SMOOTH_SHIFT(chan)); | ||
90 | value |= 1 << PWM_CONTROL_TRIGGER_SHIFT(chan); | ||
91 | writel(value, kp->base + PWM_CONTROL_OFFSET); | ||
92 | } | ||
93 | |||
94 | static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
95 | int duty_ns, int period_ns) | ||
96 | { | ||
97 | struct kona_pwmc *kp = to_kona_pwmc(chip); | ||
98 | u64 val, div, rate; | ||
99 | unsigned long prescale = PRESCALE_MIN, pc, dc; | ||
100 | unsigned int value, chan = pwm->hwpwm; | ||
101 | |||
102 | /* | ||
103 | * Find period count, duty count and prescale to suit duty_ns and | ||
104 | * period_ns. This is done according to formulas described below: | ||
105 | * | ||
106 | * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE | ||
107 | * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE | ||
108 | * | ||
109 | * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) | ||
110 | * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) | ||
111 | */ | ||
112 | |||
113 | rate = clk_get_rate(kp->clk); | ||
114 | |||
115 | while (1) { | ||
116 | div = 1000000000; | ||
117 | div *= 1 + prescale; | ||
118 | val = rate * period_ns; | ||
119 | pc = div64_u64(val, div); | ||
120 | val = rate * duty_ns; | ||
121 | dc = div64_u64(val, div); | ||
122 | |||
123 | /* If duty_ns or period_ns are not achievable then return */ | ||
124 | if (pc < PERIOD_COUNT_MIN || dc < DUTY_CYCLE_HIGH_MIN) | ||
125 | return -EINVAL; | ||
126 | |||
127 | /* If pc and dc are in bounds, the calculation is done */ | ||
128 | if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX) | ||
129 | break; | ||
130 | |||
131 | /* Otherwise, increase prescale and recalculate pc and dc */ | ||
132 | if (++prescale > PRESCALE_MAX) | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | |||
136 | /* If the PWM channel is enabled, write the settings to the HW */ | ||
137 | if (test_bit(PWMF_ENABLED, &pwm->flags)) { | ||
138 | value = readl(kp->base + PRESCALE_OFFSET); | ||
139 | value &= ~PRESCALE_MASK(chan); | ||
140 | value |= prescale << PRESCALE_SHIFT(chan); | ||
141 | writel(value, kp->base + PRESCALE_OFFSET); | ||
142 | |||
143 | writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan)); | ||
144 | |||
145 | writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan)); | ||
146 | |||
147 | kona_pwmc_apply_settings(kp, chan); | ||
148 | } | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, | ||
154 | enum pwm_polarity polarity) | ||
155 | { | ||
156 | struct kona_pwmc *kp = to_kona_pwmc(chip); | ||
157 | unsigned int chan = pwm->hwpwm; | ||
158 | unsigned int value; | ||
159 | int ret; | ||
160 | |||
161 | ret = clk_prepare_enable(kp->clk); | ||
162 | if (ret < 0) { | ||
163 | dev_err(chip->dev, "failed to enable clock: %d\n", ret); | ||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | value = readl(kp->base + PWM_CONTROL_OFFSET); | ||
168 | |||
169 | if (polarity == PWM_POLARITY_NORMAL) | ||
170 | value |= 1 << PWM_CONTROL_POLARITY_SHIFT(chan); | ||
171 | else | ||
172 | value &= ~(1 << PWM_CONTROL_POLARITY_SHIFT(chan)); | ||
173 | |||
174 | writel(value, kp->base + PWM_CONTROL_OFFSET); | ||
175 | |||
176 | kona_pwmc_apply_settings(kp, chan); | ||
177 | |||
178 | /* Wait for waveform to settle before gating off the clock */ | ||
179 | ndelay(400); | ||
180 | |||
181 | clk_disable_unprepare(kp->clk); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
187 | { | ||
188 | struct kona_pwmc *kp = to_kona_pwmc(chip); | ||
189 | int ret; | ||
190 | |||
191 | ret = clk_prepare_enable(kp->clk); | ||
192 | if (ret < 0) { | ||
193 | dev_err(chip->dev, "failed to enable clock: %d\n", ret); | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | ret = kona_pwmc_config(chip, pwm, pwm->duty_cycle, pwm->period); | ||
198 | if (ret < 0) { | ||
199 | clk_disable_unprepare(kp->clk); | ||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
207 | { | ||
208 | struct kona_pwmc *kp = to_kona_pwmc(chip); | ||
209 | unsigned int chan = pwm->hwpwm; | ||
210 | |||
211 | /* Simulate a disable by configuring for zero duty */ | ||
212 | writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan)); | ||
213 | kona_pwmc_apply_settings(kp, chan); | ||
214 | |||
215 | /* Wait for waveform to settle before gating off the clock */ | ||
216 | ndelay(400); | ||
217 | |||
218 | clk_disable_unprepare(kp->clk); | ||
219 | } | ||
220 | |||
221 | static const struct pwm_ops kona_pwm_ops = { | ||
222 | .config = kona_pwmc_config, | ||
223 | .set_polarity = kona_pwmc_set_polarity, | ||
224 | .enable = kona_pwmc_enable, | ||
225 | .disable = kona_pwmc_disable, | ||
226 | .owner = THIS_MODULE, | ||
227 | }; | ||
228 | |||
229 | static int kona_pwmc_probe(struct platform_device *pdev) | ||
230 | { | ||
231 | struct kona_pwmc *kp; | ||
232 | struct resource *res; | ||
233 | unsigned int chan; | ||
234 | unsigned int value = 0; | ||
235 | int ret = 0; | ||
236 | |||
237 | kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); | ||
238 | if (kp == NULL) | ||
239 | return -ENOMEM; | ||
240 | |||
241 | platform_set_drvdata(pdev, kp); | ||
242 | |||
243 | kp->chip.dev = &pdev->dev; | ||
244 | kp->chip.ops = &kona_pwm_ops; | ||
245 | kp->chip.base = -1; | ||
246 | kp->chip.npwm = 6; | ||
247 | kp->chip.of_xlate = of_pwm_xlate_with_flags; | ||
248 | kp->chip.of_pwm_n_cells = 3; | ||
249 | kp->chip.can_sleep = true; | ||
250 | |||
251 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
252 | kp->base = devm_ioremap_resource(&pdev->dev, res); | ||
253 | if (IS_ERR(kp->base)) | ||
254 | return PTR_ERR(kp->base); | ||
255 | |||
256 | kp->clk = devm_clk_get(&pdev->dev, NULL); | ||
257 | if (IS_ERR(kp->clk)) { | ||
258 | dev_err(&pdev->dev, "failed to get clock: %ld\n", | ||
259 | PTR_ERR(kp->clk)); | ||
260 | return PTR_ERR(kp->clk); | ||
261 | } | ||
262 | |||
263 | ret = clk_prepare_enable(kp->clk); | ||
264 | if (ret < 0) { | ||
265 | dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); | ||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | /* Set smooth mode, push/pull, and normal polarity for all channels */ | ||
270 | for (chan = 0; chan < kp->chip.npwm; chan++) { | ||
271 | value |= (1 << PWM_CONTROL_SMOOTH_SHIFT(chan)); | ||
272 | value |= (1 << PWM_CONTROL_TYPE_SHIFT(chan)); | ||
273 | value |= (1 << PWM_CONTROL_POLARITY_SHIFT(chan)); | ||
274 | } | ||
275 | |||
276 | writel(value, kp->base + PWM_CONTROL_OFFSET); | ||
277 | |||
278 | clk_disable_unprepare(kp->clk); | ||
279 | |||
280 | ret = pwmchip_add(&kp->chip); | ||
281 | if (ret < 0) | ||
282 | dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); | ||
283 | |||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | static int kona_pwmc_remove(struct platform_device *pdev) | ||
288 | { | ||
289 | struct kona_pwmc *kp = platform_get_drvdata(pdev); | ||
290 | unsigned int chan; | ||
291 | |||
292 | for (chan = 0; chan < kp->chip.npwm; chan++) | ||
293 | if (test_bit(PWMF_ENABLED, &kp->chip.pwms[chan].flags)) | ||
294 | clk_disable_unprepare(kp->clk); | ||
295 | |||
296 | return pwmchip_remove(&kp->chip); | ||
297 | } | ||
298 | |||
299 | static const struct of_device_id bcm_kona_pwmc_dt[] = { | ||
300 | { .compatible = "brcm,kona-pwm" }, | ||
301 | { }, | ||
302 | }; | ||
303 | MODULE_DEVICE_TABLE(of, bcm_kona_pwmc_dt); | ||
304 | |||
305 | static struct platform_driver kona_pwmc_driver = { | ||
306 | .driver = { | ||
307 | .name = "bcm-kona-pwm", | ||
308 | .of_match_table = bcm_kona_pwmc_dt, | ||
309 | }, | ||
310 | .probe = kona_pwmc_probe, | ||
311 | .remove = kona_pwmc_remove, | ||
312 | }; | ||
313 | module_platform_driver(kona_pwmc_driver); | ||
314 | |||
315 | MODULE_AUTHOR("Broadcom Corporation <bcm-kernel-feedback-list@broadcom.com>"); | ||
316 | MODULE_AUTHOR("Tim Kryger <tkryger@broadcom.com>"); | ||
317 | MODULE_DESCRIPTION("Broadcom Kona PWM driver"); | ||
318 | MODULE_LICENSE("GPL v2"); | ||