]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/allwinner/aw_pwm.c
Rename the channel_max method to channel_count, because that's what it's
[FreeBSD/FreeBSD.git] / sys / arm / allwinner / aw_pwm.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/rman.h>
39 #include <sys/resource.h>
40 #include <machine/bus.h>
41
42 #include <dev/ofw/ofw_bus.h>
43 #include <dev/ofw/ofw_bus_subr.h>
44
45 #include <dev/extres/clk/clk.h>
46
47 #include <dev/pwm/pwmbus.h>
48
49 #include "pwm_if.h"
50
51 #define AW_PWM_CTRL                     0x00
52 #define  AW_PWM_CTRL_PRESCALE_MASK      0xF
53 #define  AW_PWM_CTRL_EN                 (1 << 4)
54 #define  AW_PWM_CTRL_ACTIVE_LEVEL_HIGH  (1 << 5)
55 #define  AW_PWM_CTRL_GATE               (1 << 6)
56 #define  AW_PWM_CTRL_MODE_MASK          0x80
57 #define  AW_PWM_CTRL_PULSE_MODE         (1 << 7)
58 #define  AW_PWM_CTRL_CYCLE_MODE         (0 << 7)
59 #define  AW_PWM_CTRL_PULSE_START        (1 << 8)
60 #define  AW_PWM_CTRL_CLK_BYPASS         (1 << 9)
61 #define  AW_PWM_CTRL_PERIOD_BUSY        (1 << 28)
62
63 #define AW_PWM_PERIOD                   0x04
64 #define AW_PWM_PERIOD_TOTAL_MASK        0xFFFF
65 #define AW_PWM_PERIOD_TOTAL_SHIFT       16
66 #define AW_PWM_PERIOD_ACTIVE_MASK       0xFFFF
67 #define AW_PWM_PERIOD_ACTIVE_SHIFT      0
68
69 #define AW_PWM_MAX_FREQ                 24000000
70
71 #define NS_PER_SEC      1000000000
72
73 static struct ofw_compat_data compat_data[] = {
74         { "allwinner,sun5i-a13-pwm",            1 },
75         { NULL,                                 0 }
76 };
77
78 static struct resource_spec aw_pwm_spec[] = {
79         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
80         { -1, 0 }
81 };
82
83 struct aw_pwm_softc {
84         device_t        dev;
85         device_t        busdev;
86         clk_t           clk;
87         struct resource *res;
88
89         uint64_t        clk_freq;
90         unsigned int    period;
91         unsigned int    duty;
92         uint32_t        flags;
93         bool            enabled;
94 };
95
96 static uint32_t aw_pwm_clk_prescaler[] = {
97         120,
98         180,
99         240,
100         360,
101         480,
102         0,
103         0,
104         0,
105         12000,
106         24000,
107         36000,
108         48000,
109         72000,
110         0,
111         0,
112         1,
113 };
114
115 #define AW_PWM_READ(sc, reg)            bus_read_4((sc)->res, (reg))
116 #define AW_PWM_WRITE(sc, reg, val)      bus_write_4((sc)->res, (reg), (val))
117
118 static int aw_pwm_probe(device_t dev);
119 static int aw_pwm_attach(device_t dev);
120 static int aw_pwm_detach(device_t dev);
121
122 static int
123 aw_pwm_probe(device_t dev)
124 {
125         if (!ofw_bus_status_okay(dev))
126                 return (ENXIO);
127
128         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
129                 return (ENXIO);
130
131         device_set_desc(dev, "Allwinner PWM");
132         return (BUS_PROBE_DEFAULT);
133 }
134
135 static int
136 aw_pwm_attach(device_t dev)
137 {
138         struct aw_pwm_softc *sc;
139         uint64_t clk_freq;
140         uint32_t reg;
141         phandle_t node;
142         int error;
143
144         sc = device_get_softc(dev);
145         sc->dev = dev;
146
147         error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
148         if (error != 0) {
149                 device_printf(dev, "cannot get clock\n");
150                 goto fail;
151         }
152         error = clk_enable(sc->clk);
153         if (error != 0) {
154                 device_printf(dev, "cannot enable clock\n");
155                 goto fail;
156         }
157
158         error = clk_get_freq(sc->clk, &sc->clk_freq);
159         if (error != 0) {
160                 device_printf(dev, "cannot get clock frequency\n");
161                 goto fail;
162         }
163
164         if (bus_alloc_resources(dev, aw_pwm_spec, &sc->res) != 0) {
165                 device_printf(dev, "cannot allocate resources for device\n");
166                 error = ENXIO;
167                 goto fail;
168         }
169
170         /* Read the configuration left by U-Boot */
171         reg = AW_PWM_READ(sc, AW_PWM_CTRL);
172         if (reg & (AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN))
173                 sc->enabled = true;
174
175         reg = AW_PWM_READ(sc, AW_PWM_CTRL);
176         reg &= AW_PWM_CTRL_PRESCALE_MASK;
177         if (reg > nitems(aw_pwm_clk_prescaler)) {
178                 device_printf(dev, "Bad prescaler %x, cannot guess current settings\n", reg);
179                 goto skipcfg;
180         }
181         clk_freq = sc->clk_freq / aw_pwm_clk_prescaler[reg];
182
183         reg = AW_PWM_READ(sc, AW_PWM_PERIOD);
184         sc->period = NS_PER_SEC /
185                 (clk_freq / ((reg >> AW_PWM_PERIOD_TOTAL_SHIFT) & AW_PWM_PERIOD_TOTAL_MASK));
186         sc->duty = NS_PER_SEC /
187                 (clk_freq / ((reg >> AW_PWM_PERIOD_ACTIVE_SHIFT) & AW_PWM_PERIOD_ACTIVE_MASK));
188
189 skipcfg:
190         /*
191          * Note that we don't check for failure to attach pwmbus -- even without
192          * it we can still service clients who connect via fdt xref data.
193          */
194         node = ofw_bus_get_node(dev);
195         OF_device_register_xref(OF_xref_from_node(node), dev);
196
197         sc->busdev = device_add_child(dev, "pwmbus", -1);
198
199         return (bus_generic_attach(dev));
200
201 fail:
202         aw_pwm_detach(dev);
203         return (error);
204 }
205
206 static int
207 aw_pwm_detach(device_t dev)
208 {
209         struct aw_pwm_softc *sc;
210         int error;
211
212         sc = device_get_softc(dev);
213
214         if ((error = bus_generic_detach(sc->dev)) != 0) {
215                 device_printf(sc->dev, "cannot detach child devices\n");
216                 return (error);
217         }
218
219         if (sc->busdev != NULL)
220                 device_delete_child(dev, sc->busdev);
221
222         if (sc->res != NULL)
223                 bus_release_resources(dev, aw_pwm_spec, &sc->res);
224
225         return (0);
226 }
227
228 static int
229 aw_pwm_channel_count(device_t dev, int *nchannel)
230 {
231
232         *nchannel = 1;
233
234         return (0);
235 }
236
237 static int
238 aw_pwm_channel_config(device_t dev, int channel, unsigned int period, unsigned int duty)
239 {
240         struct aw_pwm_softc *sc;
241         uint64_t period_freq, duty_freq;
242         uint64_t clk_rate, div;
243         uint32_t reg;
244         int prescaler;
245         int i;
246
247         sc = device_get_softc(dev);
248
249         period_freq = NS_PER_SEC / period;
250         if (period_freq > AW_PWM_MAX_FREQ)
251                 return (EINVAL);
252         duty_freq = NS_PER_SEC / duty;
253         if (duty_freq < period_freq) {
254                 device_printf(sc->dev, "duty < period\n");
255                 return (EINVAL);
256         }
257
258         /* First test without prescaler */
259         clk_rate = AW_PWM_MAX_FREQ;
260         prescaler = AW_PWM_CTRL_PRESCALE_MASK;
261         div = AW_PWM_MAX_FREQ / period_freq;
262         if ((div - 1) > AW_PWM_PERIOD_TOTAL_MASK) {
263                 /* Test all prescaler */
264                 for (i = 0; i < nitems(aw_pwm_clk_prescaler); i++) {
265                         if (aw_pwm_clk_prescaler[i] == 0)
266                                 continue;
267                         div = (AW_PWM_MAX_FREQ * aw_pwm_clk_prescaler[i]) / period_freq;
268                         if ((div - 1) < AW_PWM_PERIOD_TOTAL_MASK ) {
269                                 prescaler = i;
270                                 clk_rate = AW_PWM_MAX_FREQ / aw_pwm_clk_prescaler[i];
271                                 break;
272                         }
273                 }
274                 if (prescaler == AW_PWM_CTRL_PRESCALE_MASK)
275                         return (EINVAL);
276         }
277
278         reg = AW_PWM_READ(sc, AW_PWM_CTRL);
279         if (reg & AW_PWM_CTRL_PERIOD_BUSY) {
280                 device_printf(sc->dev, "pwm busy\n");
281                 return (EBUSY);
282         }
283
284         /* Write the prescalar */
285         reg &= ~AW_PWM_CTRL_PRESCALE_MASK;
286         reg |= prescaler;
287         AW_PWM_WRITE(sc, AW_PWM_CTRL, reg);
288
289         /* Write the total/active cycles */
290         reg = ((clk_rate / period_freq) << AW_PWM_PERIOD_TOTAL_SHIFT) |
291           ((clk_rate / duty_freq) << AW_PWM_PERIOD_ACTIVE_SHIFT);
292         AW_PWM_WRITE(sc, AW_PWM_PERIOD, reg);
293
294         sc->period = period;
295         sc->duty = duty;
296
297         return (0);
298 }
299
300 static int
301 aw_pwm_channel_get_config(device_t dev, int channel, unsigned int *period, unsigned int *duty)
302 {
303         struct aw_pwm_softc *sc;
304
305         sc = device_get_softc(dev);
306
307         *period = sc->period;
308         *duty = sc->duty;
309
310         return (0);
311 }
312
313 static int
314 aw_pwm_channel_enable(device_t dev, int channel, bool enable)
315 {
316         struct aw_pwm_softc *sc;
317         uint32_t reg;
318
319         sc = device_get_softc(dev);
320
321         if (enable && sc->enabled)
322                 return (0);
323
324         reg = AW_PWM_READ(sc, AW_PWM_CTRL);
325         if (enable)
326                 reg |= AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN;
327         else
328                 reg &= ~(AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN);
329
330         AW_PWM_WRITE(sc, AW_PWM_CTRL, reg);
331
332         sc->enabled = enable;
333
334         return (0);
335 }
336
337 static int
338 aw_pwm_channel_is_enabled(device_t dev, int channel, bool *enabled)
339 {
340         struct aw_pwm_softc *sc;
341
342         sc = device_get_softc(dev);
343
344         *enabled = sc->enabled;
345
346         return (0);
347 }
348
349 static device_t
350 aw_pwm_get_bus(device_t dev)
351 {
352         struct aw_pwm_softc *sc;
353
354         sc = device_get_softc(dev);
355
356         return (sc->busdev);
357 }
358 static device_method_t aw_pwm_methods[] = {
359         /* Device interface */
360         DEVMETHOD(device_probe,         aw_pwm_probe),
361         DEVMETHOD(device_attach,        aw_pwm_attach),
362         DEVMETHOD(device_detach,        aw_pwm_detach),
363
364         /* pwm interface */
365         DEVMETHOD(pwm_get_bus,                  aw_pwm_get_bus),
366         DEVMETHOD(pwm_channel_count,            aw_pwm_channel_count),
367         DEVMETHOD(pwm_channel_config,           aw_pwm_channel_config),
368         DEVMETHOD(pwm_channel_get_config,       aw_pwm_channel_get_config),
369         DEVMETHOD(pwm_channel_enable,           aw_pwm_channel_enable),
370         DEVMETHOD(pwm_channel_is_enabled,       aw_pwm_channel_is_enabled),
371
372         DEVMETHOD_END
373 };
374
375 static driver_t aw_pwm_driver = {
376         "pwm",
377         aw_pwm_methods,
378         sizeof(struct aw_pwm_softc),
379 };
380
381 static devclass_t aw_pwm_devclass;
382
383 DRIVER_MODULE(aw_pwm, simplebus, aw_pwm_driver, aw_pwm_devclass, 0, 0);
384 MODULE_VERSION(aw_pwm, 1);
385 SIMPLEBUS_PNP_INFO(compat_data);