1 /*-
2 * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/limits.h>
35 #include <sys/lock.h>
36 #include <sys/module.h>
37 #include <sys/mutex.h>
38 #include <sys/resource.h>
39 #include <sys/rman.h>
40
41 #include <machine/bus.h>
42
43 #include <dev/ofw/openfirm.h>
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46
47 #include <dev/pwm/pwmc.h>
48
49 #include "pwmbus_if.h"
50
51 #include "am335x_pwm.h"
52
53 /*******************************************************************************
54 * Enhanced resolution PWM driver. Many of the advanced featues of the hardware
55 * are not supported by this driver. What is implemented here is simple
56 * variable-duty-cycle PWM output.
57 ******************************************************************************/
58
59 /* In ticks */
60 #define DEFAULT_PWM_PERIOD 1000
61 #define PWM_CLOCK 100000000UL
62
63 #define NS_PER_SEC 1000000000
64
65 #define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
66 #define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
67 #define PWM_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
68 #define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
69 device_get_nameunit(_sc->sc_dev), "am335x_ehrpwm softc", MTX_DEF)
70 #define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
71
72 #define EPWM_READ2(_sc, reg) bus_read_2((_sc)->sc_mem_res, reg)
73 #define EPWM_WRITE2(_sc, reg, value) \
74 bus_write_2((_sc)->sc_mem_res, reg, value)
75
76 #define EPWM_TBCTL 0x00
77 /* see 15.2.2.11 for the first two, used in debug situations */
78 #define TBCTL_FREERUN_STOP_NEXT_TBC_INCREMENT (0 << 14)
79 #define TBCTL_FREERUN_STOP_COMPLETE_CYCLE (1 << 14)
80 /* ignore suspend control signal */
81 #define TBCTL_FREERUN (2 << 14)
82
83 #define TBCTL_PHDIR_UP (1 << 13)
84 #define TBCTL_PHDIR_DOWN (0 << 13)
85 #define TBCTL_CLKDIV(x) ((x) << 10)
86 #define TBCTL_CLKDIV_MASK (7 << 10)
87 #define TBCTL_HSPCLKDIV(x) ((x) << 7)
88 #define TBCTL_HSPCLKDIV_MASK (7 << 7)
89 #define TBCTL_SYNCOSEL_DISABLED (3 << 4)
90 #define TBCTL_PRDLD_SHADOW (0 << 3)
91 #define TBCTL_PRDLD_IMMEDIATE (1 << 3)
92 #define TBCTL_PHSEN_DISABLED (0 << 2)
93 #define TBCTL_PHSEN_ENABLED (1 << 2)
94 #define TBCTL_CTRMODE_MASK (3)
95 #define TBCTL_CTRMODE_UP (0 << 0)
96 #define TBCTL_CTRMODE_DOWN (1 << 0)
97 #define TBCTL_CTRMODE_UPDOWN (2 << 0)
98 #define TBCTL_CTRMODE_FREEZE (3 << 0)
99
100 #define EPWM_TBSTS 0x02
101 #define EPWM_TBPHSHR 0x04
102 #define EPWM_TBPHS 0x06
103 #define EPWM_TBCNT 0x08
104 #define EPWM_TBPRD 0x0a
105 /* Counter-compare */
106 #define EPWM_CMPCTL 0x0e
107 #define CMPCTL_SHDWBMODE_SHADOW (1 << 6)
108 #define CMPCTL_SHDWBMODE_IMMEDIATE (0 << 6)
109 #define CMPCTL_SHDWAMODE_SHADOW (1 << 4)
110 #define CMPCTL_SHDWAMODE_IMMEDIATE (0 << 4)
111 #define CMPCTL_LOADBMODE_ZERO (0 << 2)
112 #define CMPCTL_LOADBMODE_PRD (1 << 2)
113 #define CMPCTL_LOADBMODE_EITHER (2 << 2)
114 #define CMPCTL_LOADBMODE_FREEZE (3 << 2)
115 #define CMPCTL_LOADAMODE_ZERO (0 << 0)
116 #define CMPCTL_LOADAMODE_PRD (1 << 0)
117 #define CMPCTL_LOADAMODE_EITHER (2 << 0)
118 #define CMPCTL_LOADAMODE_FREEZE (3 << 0)
119 #define EPWM_CMPAHR 0x10
120 #define EPWM_CMPA 0x12
121 #define EPWM_CMPB 0x14
122 /* CMPCTL_LOADAMODE_ZERO */
123 #define EPWM_AQCTLA 0x16
124 #define EPWM_AQCTLB 0x18
125 #define AQCTL_CBU_NONE (0 << 8)
126 #define AQCTL_CBU_CLEAR (1 << 8)
127 #define AQCTL_CBU_SET (2 << 8)
128 #define AQCTL_CBU_TOGGLE (3 << 8)
129 #define AQCTL_CAU_NONE (0 << 4)
130 #define AQCTL_CAU_CLEAR (1 << 4)
131 #define AQCTL_CAU_SET (2 << 4)
132 #define AQCTL_CAU_TOGGLE (3 << 4)
133 #define AQCTL_ZRO_NONE (0 << 0)
134 #define AQCTL_ZRO_CLEAR (1 << 0)
135 #define AQCTL_ZRO_SET (2 << 0)
136 #define AQCTL_ZRO_TOGGLE (3 << 0)
137 #define EPWM_AQSFRC 0x1a
138 #define EPWM_AQCSFRC 0x1c
139 #define AQCSFRC_OFF 0
140 #define AQCSFRC_LO 1
141 #define AQCSFRC_HI 2
142 #define AQCSFRC_MASK 3
143 #define AQCSFRC(chan, hilo) ((hilo) << (2 * chan))
144
145 /* Trip-Zone module */
146 #define EPWM_TZSEL 0x24
147 #define EPWM_TZCTL 0x28
148 #define EPWM_TZFLG 0x2C
149
150 /* Dead band */
151 #define EPWM_DBCTL 0x1E
152 #define DBCTL_MASK (3 << 0)
153 #define DBCTL_BYPASS 0
154 #define DBCTL_RISING_EDGE 1
155 #define DBCTL_FALLING_EDGE 2
156 #define DBCTL_BOTH_EDGE 3
157
158 /* PWM-chopper */
159 #define EPWM_PCCTL 0x3C
160 #define PCCTL_CHPEN_MASK (1 << 0)
161 #define PCCTL_CHPEN_DISABLE 0
162 #define PCCTL_CHPEN_ENABLE 1
163
164 /* High-Resolution PWM */
165 #define EPWM_HRCTL 0x40
166 #define HRCTL_DELMODE_BOTH 3
167 #define HRCTL_DELMODE_FALL 2
168 #define HRCTL_DELMODE_RISE 1
169
170 static device_probe_t am335x_ehrpwm_probe;
171 static device_attach_t am335x_ehrpwm_attach;
172 static device_detach_t am335x_ehrpwm_detach;
173
174 struct ehrpwm_channel {
175 u_int duty; /* on duration, in ns */
176 bool enabled; /* channel enabled? */
177 bool inverted; /* signal inverted? */
178 };
179 #define NUM_CHANNELS 2
180
181 struct am335x_ehrpwm_softc {
182 device_t sc_dev;
183 device_t sc_busdev;
184 struct mtx sc_mtx;
185 struct resource *sc_mem_res;
186 int sc_mem_rid;
187
188 /* Things used for configuration via pwm(9) api. */
189 u_int sc_clkfreq; /* frequency in Hz */
190 u_int sc_clktick; /* duration in ns */
191 u_int sc_period; /* duration in ns */
192 struct ehrpwm_channel sc_channels[NUM_CHANNELS];
193 };
194
195 static struct ofw_compat_data compat_data[] = {
196 {"ti,am3352-ehrpwm", true},
197 {"ti,am33xx-ehrpwm", true},
198 {NULL, false},
199 };
200 SIMPLEBUS_PNP_INFO(compat_data);
201
202 static void
203 am335x_ehrpwm_cfg_duty(struct am335x_ehrpwm_softc *sc, u_int chan, u_int duty)
204 {
205 u_int tbcmp;
206
207 if (duty == 0)
208 tbcmp = 0;
209 else
210 tbcmp = max(1, duty / sc->sc_clktick);
211
212 sc->sc_channels[chan].duty = tbcmp * sc->sc_clktick;
213
214 PWM_LOCK_ASSERT(sc);
215 EPWM_WRITE2(sc, (chan == 0) ? EPWM_CMPA : EPWM_CMPB, tbcmp);
216 }
217
218 static void
219 am335x_ehrpwm_cfg_enable(struct am335x_ehrpwm_softc *sc, u_int chan, bool enable)
220 {
221 uint16_t regval;
222
223 sc->sc_channels[chan].enabled = enable;
224
225 /*
226 * Turn off any existing software-force of the channel, then force
227 * it in the right direction (high or low) if it's not being enabled.
228 */
229 PWM_LOCK_ASSERT(sc);
230 regval = EPWM_READ2(sc, EPWM_AQCSFRC);
231 regval &= ~AQCSFRC(chan, AQCSFRC_MASK);
232 if (!sc->sc_channels[chan].enabled) {
233 if (sc->sc_channels[chan].inverted)
234 regval |= AQCSFRC(chan, AQCSFRC_HI);
235 else
236 regval |= AQCSFRC(chan, AQCSFRC_LO);
237 }
238 EPWM_WRITE2(sc, EPWM_AQCSFRC, regval);
239 }
240
241 static bool
242 am335x_ehrpwm_cfg_period(struct am335x_ehrpwm_softc *sc, u_int period)
243 {
244 uint16_t regval;
245 u_int clkdiv, hspclkdiv, pwmclk, pwmtick, tbprd;
246
247 /* Can't do a period shorter than 2 clock ticks. */
248 if (period < 2 * NS_PER_SEC / PWM_CLOCK) {
249 sc->sc_clkfreq = 0;
250 sc->sc_clktick = 0;
251 sc->sc_period = 0;
252 return (false);
253 }
254
255 /*
256 * Figure out how much we have to divide down the base 100MHz clock so
257 * that we can express the requested period as a 16-bit tick count.
258 */
259 tbprd = 0;
260 for (clkdiv = 0; clkdiv < 8; ++clkdiv) {
261 const u_int cd = 1 << clkdiv;
262 for (hspclkdiv = 0; hspclkdiv < 8; ++hspclkdiv) {
263 const u_int cdhs = max(1, hspclkdiv * 2);
264 pwmclk = PWM_CLOCK / (cd * cdhs);
265 pwmtick = NS_PER_SEC / pwmclk;
266 if (period / pwmtick < 65536) {
267 tbprd = period / pwmtick;
268 break;
269 }
270 }
271 if (tbprd != 0)
272 break;
273 }
274
275 /* Handle requested period too long for available clock divisors. */
276 if (tbprd == 0)
277 return (false);
278
279 /*
280 * If anything has changed from the current settings, reprogram the
281 * clock divisors and period register.
282 */
283 if (sc->sc_clkfreq != pwmclk || sc->sc_clktick != pwmtick ||
284 sc->sc_period != tbprd * pwmtick) {
285 sc->sc_clkfreq = pwmclk;
286 sc->sc_clktick = pwmtick;
287 sc->sc_period = tbprd * pwmtick;
288
289 PWM_LOCK_ASSERT(sc);
290 regval = EPWM_READ2(sc, EPWM_TBCTL);
291 regval &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
292 regval |= TBCTL_CLKDIV(clkdiv) | TBCTL_HSPCLKDIV(hspclkdiv);
293 EPWM_WRITE2(sc, EPWM_TBCTL, regval);
294 EPWM_WRITE2(sc, EPWM_TBPRD, tbprd - 1);
295 #if 0
296 device_printf(sc->sc_dev, "clkdiv %u hspclkdiv %u tbprd %u "
297 "clkfreq %u Hz clktick %u ns period got %u requested %u\n",
298 clkdiv, hspclkdiv, tbprd - 1,
299 sc->sc_clkfreq, sc->sc_clktick, sc->sc_period, period);
300 #endif
301 /*
302 * If the period changed, that invalidates the current CMP
303 * registers (duty values), just zero them out.
304 */
305 am335x_ehrpwm_cfg_duty(sc, 0, 0);
306 am335x_ehrpwm_cfg_duty(sc, 1, 0);
307 }
308
309 return (true);
310 }
311
312 static int
313 am335x_ehrpwm_channel_count(device_t dev, u_int *nchannel)
314 {
315
316 *nchannel = NUM_CHANNELS;
317
318 return (0);
319 }
320
321 static int
322 am335x_ehrpwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
323 {
324 struct am335x_ehrpwm_softc *sc;
325 bool status;
326
327 if (channel >= NUM_CHANNELS)
328 return (EINVAL);
329
330 sc = device_get_softc(dev);
331
332 PWM_LOCK(sc);
333 status = am335x_ehrpwm_cfg_period(sc, period);
334 if (status)
335 am335x_ehrpwm_cfg_duty(sc, channel, duty);
336 PWM_UNLOCK(sc);
337
338 return (status ? 0 : EINVAL);
339 }
340
341 static int
342 am335x_ehrpwm_channel_get_config(device_t dev, u_int channel,
343 u_int *period, u_int *duty)
344 {
345 struct am335x_ehrpwm_softc *sc;
346
347 if (channel >= NUM_CHANNELS)
348 return (EINVAL);
349
350 sc = device_get_softc(dev);
351 *period = sc->sc_period;
352 *duty = sc->sc_channels[channel].duty;
353 return (0);
354 }
355
356 static int
357 am335x_ehrpwm_channel_set_flags(device_t dev, u_int channel,
358 uint32_t flags)
359 {
360 struct am335x_ehrpwm_softc *sc;
361
362 if (channel >= NUM_CHANNELS)
363 return (EINVAL);
364
365 sc = device_get_softc(dev);
366
367 PWM_LOCK(sc);
368 if (flags & PWM_POLARITY_INVERTED) {
369 sc->sc_channels[channel].inverted = true;
370 /* Action-Qualifier 15.2.2.5 */
371 if (channel == 0)
372 EPWM_WRITE2(sc, EPWM_AQCTLA,
373 (AQCTL_ZRO_CLEAR | AQCTL_CAU_SET));
374 else
375 EPWM_WRITE2(sc, EPWM_AQCTLB,
376 (AQCTL_ZRO_CLEAR | AQCTL_CBU_SET));
377 } else {
378 sc->sc_channels[channel].inverted = false;
379 if (channel == 0)
380 EPWM_WRITE2(sc, EPWM_AQCTLA,
381 (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
382 else
383 EPWM_WRITE2(sc, EPWM_AQCTLB,
384 (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
385 }
386 PWM_UNLOCK(sc);
387
388 return (0);
389 }
390
391 static int
392 am335x_ehrpwm_channel_get_flags(device_t dev, u_int channel,
393 uint32_t *flags)
394 {
395 struct am335x_ehrpwm_softc *sc;
396 if (channel >= NUM_CHANNELS)
397 return (EINVAL);
398
399 sc = device_get_softc(dev);
400
401 if (sc->sc_channels[channel].inverted == true)
402 *flags = PWM_POLARITY_INVERTED;
403 else
404 *flags = 0;
405
406 return (0);
407 }
408
409
410 static int
411 am335x_ehrpwm_channel_enable(device_t dev, u_int channel, bool enable)
412 {
413 struct am335x_ehrpwm_softc *sc;
414
415 if (channel >= NUM_CHANNELS)
416 return (EINVAL);
417
418 sc = device_get_softc(dev);
419
420 PWM_LOCK(sc);
421 am335x_ehrpwm_cfg_enable(sc, channel, enable);
422 PWM_UNLOCK(sc);
423
424 return (0);
425 }
426
427 static int
428 am335x_ehrpwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
429 {
430 struct am335x_ehrpwm_softc *sc;
431
432 if (channel >= NUM_CHANNELS)
433 return (EINVAL);
434
435 sc = device_get_softc(dev);
436
437 *enabled = sc->sc_channels[channel].enabled;
438
439 return (0);
440 }
441
442 static int
443 am335x_ehrpwm_probe(device_t dev)
444 {
445
446 if (!ofw_bus_status_okay(dev))
447 return (ENXIO);
448
449 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
450 return (ENXIO);
451
452 device_set_desc(dev, "AM335x EHRPWM");
453
454 return (BUS_PROBE_DEFAULT);
455 }
456
457 static int
458 am335x_ehrpwm_attach(device_t dev)
459 {
460 struct am335x_ehrpwm_softc *sc;
461 uint16_t reg;
462
463 sc = device_get_softc(dev);
464 sc->sc_dev = dev;
465
466 PWM_LOCK_INIT(sc);
467
468 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
469 &sc->sc_mem_rid, RF_ACTIVE);
470 if (sc->sc_mem_res == NULL) {
471 device_printf(dev, "cannot allocate memory resources\n");
472 goto fail;
473 }
474
475 /* CONFIGURE EPWM */
476 reg = EPWM_READ2(sc, EPWM_TBCTL);
477 reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
478 EPWM_WRITE2(sc, EPWM_TBCTL, reg);
479
480 EPWM_WRITE2(sc, EPWM_TBPRD, DEFAULT_PWM_PERIOD - 1);
481 EPWM_WRITE2(sc, EPWM_CMPA, 0);
482 EPWM_WRITE2(sc, EPWM_CMPB, 0);
483
484 /* Action-Qualifier 15.2.2.5 */
485 EPWM_WRITE2(sc, EPWM_AQCTLA, (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
486 EPWM_WRITE2(sc, EPWM_AQCTLB, (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
487
488 /* Dead band 15.2.2.6 */
489 reg = EPWM_READ2(sc, EPWM_DBCTL);
490 reg &= ~DBCTL_MASK;
491 reg |= DBCTL_BYPASS;
492 EPWM_WRITE2(sc, EPWM_DBCTL, reg);
493
494 /* PWM-chopper described in 15.2.2.7 */
495 /* Acc. TRM used in pulse transformerbased gate drivers
496 * to control the power switching-elements
497 */
498 reg = EPWM_READ2(sc, EPWM_PCCTL);
499 reg &= ~PCCTL_CHPEN_MASK;
500 reg |= PCCTL_CHPEN_DISABLE;
501 EPWM_WRITE2(sc, EPWM_PCCTL, PCCTL_CHPEN_DISABLE);
502
503 /* Trip zone are described in 15.2.2.8.
504 * Essential its used to detect faults and can be configured
505 * to react on such faults..
506 */
507 /* disable TZn as one-shot / CVC trip source 15.2.4.18 */
508 EPWM_WRITE2(sc, EPWM_TZSEL, 0x0);
509 /* reg described in 15.2.4.19 */
510 EPWM_WRITE2(sc, EPWM_TZCTL, 0xf);
511 reg = EPWM_READ2(sc, EPWM_TZFLG);
512
513 /* START EPWM */
514 reg &= ~TBCTL_CTRMODE_MASK;
515 reg |= TBCTL_CTRMODE_UP | TBCTL_FREERUN;
516 EPWM_WRITE2(sc, EPWM_TBCTL, reg);
517
518 if ((sc->sc_busdev = device_add_child(dev, "pwmbus", -1)) == NULL) {
519 device_printf(dev, "Cannot add child pwmbus\n");
520 // This driver can still do things even without the bus child.
521 }
522
523 bus_generic_probe(dev);
524 return (bus_generic_attach(dev));
525 fail:
526 PWM_LOCK_DESTROY(sc);
527 if (sc->sc_mem_res)
528 bus_release_resource(dev, SYS_RES_MEMORY,
529 sc->sc_mem_rid, sc->sc_mem_res);
530
531 return(ENXIO);
532 }
533
534 static int
535 am335x_ehrpwm_detach(device_t dev)
536 {
537 struct am335x_ehrpwm_softc *sc;
538 int error;
539
540 sc = device_get_softc(dev);
541
542 if ((error = bus_generic_detach(sc->sc_dev)) != 0)
543 return (error);
544
545 PWM_LOCK(sc);
546
547 if (sc->sc_busdev != NULL)
548 device_delete_child(dev, sc->sc_busdev);
549
550 if (sc->sc_mem_res)
551 bus_release_resource(dev, SYS_RES_MEMORY,
552 sc->sc_mem_rid, sc->sc_mem_res);
553
554 PWM_UNLOCK(sc);
555
556 PWM_LOCK_DESTROY(sc);
557
558 return (0);
559 }
560
561 static phandle_t
562 am335x_ehrpwm_get_node(device_t bus, device_t dev)
563 {
564
565 /*
566 * Share our controller node with our pwmbus child; it instantiates
567 * devices by walking the children contained within our node.
568 */
569 return ofw_bus_get_node(bus);
570 }
571
572 static device_method_t am335x_ehrpwm_methods[] = {
573 DEVMETHOD(device_probe, am335x_ehrpwm_probe),
574 DEVMETHOD(device_attach, am335x_ehrpwm_attach),
575 DEVMETHOD(device_detach, am335x_ehrpwm_detach),
576
577 /* ofw_bus_if */
578 DEVMETHOD(ofw_bus_get_node, am335x_ehrpwm_get_node),
579
580 /* pwm interface */
581 DEVMETHOD(pwmbus_channel_count, am335x_ehrpwm_channel_count),
582 DEVMETHOD(pwmbus_channel_config, am335x_ehrpwm_channel_config),
583 DEVMETHOD(pwmbus_channel_get_config, am335x_ehrpwm_channel_get_config),
584 DEVMETHOD(pwmbus_channel_set_flags, am335x_ehrpwm_channel_set_flags),
585 DEVMETHOD(pwmbus_channel_get_flags, am335x_ehrpwm_channel_get_flags),
586 DEVMETHOD(pwmbus_channel_enable, am335x_ehrpwm_channel_enable),
587 DEVMETHOD(pwmbus_channel_is_enabled, am335x_ehrpwm_channel_is_enabled),
588
589 DEVMETHOD_END
590 };
591
592 static driver_t am335x_ehrpwm_driver = {
593 "pwm",
594 am335x_ehrpwm_methods,
595 sizeof(struct am335x_ehrpwm_softc),
596 };
597
598 DRIVER_MODULE(am335x_ehrpwm, am335x_pwmss, am335x_ehrpwm_driver, 0, 0);
599 MODULE_VERSION(am335x_ehrpwm, 1);
600 MODULE_DEPEND(am335x_ehrpwm, am335x_pwmss, 1, 1, 1);
601 MODULE_DEPEND(am335x_ehrpwm, pwmbus, 1, 1, 1);
Cache object: 3d4e86239843acfdb6d252d5cb2bd8c7
|