]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/ti/am335x/am335x_ehrpwm.c
contrib/tzdata: import tzdata 2021c
[FreeBSD/FreeBSD.git] / sys / arm / ti / am335x / am335x_ehrpwm.c
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,am33xx-ehrpwm",    true},
197         {NULL,                  false},
198 };
199 SIMPLEBUS_PNP_INFO(compat_data);
200
201 static void
202 am335x_ehrpwm_cfg_duty(struct am335x_ehrpwm_softc *sc, u_int chan, u_int duty)
203 {
204         u_int tbcmp;
205
206         if (duty == 0)
207                 tbcmp = 0;
208         else
209                 tbcmp = max(1, duty / sc->sc_clktick);
210
211         sc->sc_channels[chan].duty = tbcmp * sc->sc_clktick;
212
213         PWM_LOCK_ASSERT(sc);
214         EPWM_WRITE2(sc, (chan == 0) ? EPWM_CMPA : EPWM_CMPB, tbcmp);
215 }
216
217 static void
218 am335x_ehrpwm_cfg_enable(struct am335x_ehrpwm_softc *sc, u_int chan, bool enable)
219 {
220         uint16_t regval;
221
222         sc->sc_channels[chan].enabled = enable;
223
224         /*
225          * Turn off any existing software-force of the channel, then force
226          * it in the right direction (high or low) if it's not being enabled.
227          */
228         PWM_LOCK_ASSERT(sc);
229         regval = EPWM_READ2(sc, EPWM_AQCSFRC);
230         regval &= ~AQCSFRC(chan, AQCSFRC_MASK);
231         if (!sc->sc_channels[chan].enabled) {
232                 if (sc->sc_channels[chan].inverted)
233                         regval |= AQCSFRC(chan, AQCSFRC_HI);
234                 else
235                         regval |= AQCSFRC(chan, AQCSFRC_LO);
236         }
237         EPWM_WRITE2(sc, EPWM_AQCSFRC, regval);
238 }
239
240 static bool
241 am335x_ehrpwm_cfg_period(struct am335x_ehrpwm_softc *sc, u_int period)
242 {
243         uint16_t regval;
244         u_int clkdiv, hspclkdiv, pwmclk, pwmtick, tbprd;
245
246         /* Can't do a period shorter than 2 clock ticks. */
247         if (period < 2 * NS_PER_SEC / PWM_CLOCK) {
248                 sc->sc_clkfreq = 0;
249                 sc->sc_clktick = 0;
250                 sc->sc_period  = 0;
251                 return (false);
252         }
253
254         /*
255          * Figure out how much we have to divide down the base 100MHz clock so
256          * that we can express the requested period as a 16-bit tick count.
257          */
258         tbprd = 0;
259         for (clkdiv = 0; clkdiv < 8; ++clkdiv) {
260                 const u_int cd = 1 << clkdiv;
261                 for (hspclkdiv = 0; hspclkdiv < 8; ++hspclkdiv) {
262                         const u_int cdhs = max(1, hspclkdiv * 2);
263                         pwmclk = PWM_CLOCK / (cd * cdhs);
264                         pwmtick = NS_PER_SEC / pwmclk;
265                         if (period / pwmtick < 65536) {
266                                 tbprd = period / pwmtick;
267                                 break;
268                         }
269                 }
270                 if (tbprd != 0)
271                         break;
272         }
273
274         /* Handle requested period too long for available clock divisors. */
275         if (tbprd == 0)
276                 return (false);
277
278         /*
279          * If anything has changed from the current settings, reprogram the
280          * clock divisors and period register.
281          */
282         if (sc->sc_clkfreq != pwmclk || sc->sc_clktick != pwmtick ||
283             sc->sc_period != tbprd * pwmtick) {
284                 sc->sc_clkfreq = pwmclk;
285                 sc->sc_clktick = pwmtick;
286                 sc->sc_period  = tbprd * pwmtick;
287
288                 PWM_LOCK_ASSERT(sc);
289                 regval = EPWM_READ2(sc, EPWM_TBCTL);
290                 regval &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
291                 regval |= TBCTL_CLKDIV(clkdiv) | TBCTL_HSPCLKDIV(hspclkdiv);
292                 EPWM_WRITE2(sc, EPWM_TBCTL, regval);
293                 EPWM_WRITE2(sc, EPWM_TBPRD, tbprd - 1);
294 #if 0
295                 device_printf(sc->sc_dev, "clkdiv %u hspclkdiv %u tbprd %u "
296                     "clkfreq %u Hz clktick %u ns period got %u requested %u\n",
297                     clkdiv, hspclkdiv, tbprd - 1,
298                     sc->sc_clkfreq, sc->sc_clktick, sc->sc_period, period);
299 #endif
300                 /*
301                  * If the period changed, that invalidates the current CMP
302                  * registers (duty values), just zero them out.
303                  */
304                 am335x_ehrpwm_cfg_duty(sc, 0, 0);
305                 am335x_ehrpwm_cfg_duty(sc, 1, 0);
306         }
307
308         return (true);
309 }
310
311 static int
312 am335x_ehrpwm_channel_count(device_t dev, u_int *nchannel)
313 {
314
315         *nchannel = NUM_CHANNELS;
316
317         return (0);
318 }
319
320 static int
321 am335x_ehrpwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
322 {
323         struct am335x_ehrpwm_softc *sc;
324         bool status;
325
326         if (channel >= NUM_CHANNELS)
327                 return (EINVAL);
328
329         sc = device_get_softc(dev);
330
331         PWM_LOCK(sc);
332         status = am335x_ehrpwm_cfg_period(sc, period);
333         if (status)
334                 am335x_ehrpwm_cfg_duty(sc, channel, duty);
335         PWM_UNLOCK(sc);
336
337         return (status ? 0 : EINVAL);
338 }
339
340 static int
341 am335x_ehrpwm_channel_get_config(device_t dev, u_int channel, 
342     u_int *period, u_int *duty)
343 {
344         struct am335x_ehrpwm_softc *sc;
345
346         if (channel >= NUM_CHANNELS)
347                 return (EINVAL);
348
349         sc = device_get_softc(dev);
350         *period = sc->sc_period;
351         *duty = sc->sc_channels[channel].duty;
352         return (0);
353 }
354
355 static int
356 am335x_ehrpwm_channel_set_flags(device_t dev, u_int channel,
357        uint32_t flags)
358 {
359         struct am335x_ehrpwm_softc *sc;
360
361         if (channel >= NUM_CHANNELS)
362                 return (EINVAL);
363
364         sc = device_get_softc(dev);
365
366         PWM_LOCK(sc);
367         if (flags & PWM_POLARITY_INVERTED) {
368                 sc->sc_channels[channel].inverted = true;
369                 /* Action-Qualifier 15.2.2.5 */
370                 if (channel == 0)
371                         EPWM_WRITE2(sc, EPWM_AQCTLA,
372                             (AQCTL_ZRO_CLEAR | AQCTL_CAU_SET));
373                 else
374                         EPWM_WRITE2(sc, EPWM_AQCTLB,
375                             (AQCTL_ZRO_CLEAR | AQCTL_CBU_SET));
376         } else {
377                 sc->sc_channels[channel].inverted = false;
378                 if (channel == 0)
379                         EPWM_WRITE2(sc, EPWM_AQCTLA,
380                             (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
381                 else
382                         EPWM_WRITE2(sc, EPWM_AQCTLB,
383                             (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
384         }
385         PWM_UNLOCK(sc);
386
387         return (0);
388 }
389
390 static int
391 am335x_ehrpwm_channel_get_flags(device_t dev, u_int channel,
392     uint32_t *flags)
393 {
394         struct am335x_ehrpwm_softc *sc;
395         if (channel >= NUM_CHANNELS)
396                 return (EINVAL);
397
398         sc = device_get_softc(dev);
399
400         if (sc->sc_channels[channel].inverted == true)
401                 *flags = PWM_POLARITY_INVERTED;
402         else
403                 *flags = 0;
404
405         return (0);
406 }
407
408
409 static int
410 am335x_ehrpwm_channel_enable(device_t dev, u_int channel, bool enable)
411 {
412         struct am335x_ehrpwm_softc *sc;
413
414         if (channel >= NUM_CHANNELS)
415                 return (EINVAL);
416
417         sc = device_get_softc(dev);
418
419         PWM_LOCK(sc);
420         am335x_ehrpwm_cfg_enable(sc, channel, enable);
421         PWM_UNLOCK(sc);
422
423         return (0);
424 }
425
426 static int
427 am335x_ehrpwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
428 {
429         struct am335x_ehrpwm_softc *sc;
430
431         if (channel >= NUM_CHANNELS)
432                 return (EINVAL);
433
434         sc = device_get_softc(dev);
435
436         *enabled = sc->sc_channels[channel].enabled;
437
438         return (0);
439 }
440
441 static int
442 am335x_ehrpwm_probe(device_t dev)
443 {
444
445         if (!ofw_bus_status_okay(dev))
446                 return (ENXIO);
447
448         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
449                 return (ENXIO);
450
451         device_set_desc(dev, "AM335x EHRPWM");
452
453         return (BUS_PROBE_DEFAULT);
454 }
455
456 static int
457 am335x_ehrpwm_attach(device_t dev)
458 {
459         struct am335x_ehrpwm_softc *sc;
460         uint16_t reg;
461
462         sc = device_get_softc(dev);
463         sc->sc_dev = dev;
464
465         PWM_LOCK_INIT(sc);
466
467         sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
468             &sc->sc_mem_rid, RF_ACTIVE);
469         if (sc->sc_mem_res == NULL) {
470                 device_printf(dev, "cannot allocate memory resources\n");
471                 goto fail;
472         }
473
474         /* CONFIGURE EPWM */
475         reg = EPWM_READ2(sc, EPWM_TBCTL);
476         reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
477         EPWM_WRITE2(sc, EPWM_TBCTL, reg);
478
479         EPWM_WRITE2(sc, EPWM_TBPRD, DEFAULT_PWM_PERIOD - 1);
480         EPWM_WRITE2(sc, EPWM_CMPA, 0);
481         EPWM_WRITE2(sc, EPWM_CMPB, 0);
482
483         /* Action-Qualifier 15.2.2.5 */
484         EPWM_WRITE2(sc, EPWM_AQCTLA, (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
485         EPWM_WRITE2(sc, EPWM_AQCTLB, (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
486
487         /* Dead band 15.2.2.6 */
488         reg = EPWM_READ2(sc, EPWM_DBCTL);
489         reg &= ~DBCTL_MASK;
490         reg |= DBCTL_BYPASS;
491         EPWM_WRITE2(sc, EPWM_DBCTL, reg);
492
493         /* PWM-chopper described in 15.2.2.7 */
494         /* Acc. TRM used in pulse transformerbased gate drivers
495          * to control the power switching-elements
496          */
497         reg = EPWM_READ2(sc, EPWM_PCCTL);
498         reg &= ~PCCTL_CHPEN_MASK;
499         reg |= PCCTL_CHPEN_DISABLE;
500         EPWM_WRITE2(sc, EPWM_PCCTL, PCCTL_CHPEN_DISABLE);
501
502         /* Trip zone are described in 15.2.2.8.
503          * Essential its used to detect faults and can be configured
504          * to react on such faults..
505          */
506         /* disable TZn as one-shot / CVC trip source 15.2.4.18 */
507         EPWM_WRITE2(sc, EPWM_TZSEL, 0x0);
508         /* reg described in 15.2.4.19 */
509         EPWM_WRITE2(sc, EPWM_TZCTL, 0xf);
510         reg = EPWM_READ2(sc, EPWM_TZFLG);
511
512         /* START EPWM */
513         reg &= ~TBCTL_CTRMODE_MASK;
514         reg |= TBCTL_CTRMODE_UP | TBCTL_FREERUN;
515         EPWM_WRITE2(sc, EPWM_TBCTL, reg);
516
517         if ((sc->sc_busdev = device_add_child(dev, "pwmbus", -1)) == NULL) {
518                 device_printf(dev, "Cannot add child pwmbus\n");
519                 // This driver can still do things even without the bus child.
520         }
521
522         bus_generic_probe(dev);
523         return (bus_generic_attach(dev));
524 fail:
525         PWM_LOCK_DESTROY(sc);
526         if (sc->sc_mem_res)
527                 bus_release_resource(dev, SYS_RES_MEMORY,
528                     sc->sc_mem_rid, sc->sc_mem_res);
529
530         return(ENXIO);
531 }
532
533 static int
534 am335x_ehrpwm_detach(device_t dev)
535 {
536         struct am335x_ehrpwm_softc *sc;
537         int error;
538
539         sc = device_get_softc(dev);
540
541         if ((error = bus_generic_detach(sc->sc_dev)) != 0)
542                 return (error);
543
544         PWM_LOCK(sc);
545
546         if (sc->sc_busdev != NULL)
547                 device_delete_child(dev, sc->sc_busdev);
548
549         if (sc->sc_mem_res)
550                 bus_release_resource(dev, SYS_RES_MEMORY,
551                     sc->sc_mem_rid, sc->sc_mem_res);
552
553         PWM_UNLOCK(sc);
554
555         PWM_LOCK_DESTROY(sc);
556
557         return (0);
558 }
559
560 static phandle_t
561 am335x_ehrpwm_get_node(device_t bus, device_t dev)
562 {
563
564         /*
565          * Share our controller node with our pwmbus child; it instantiates
566          * devices by walking the children contained within our node.
567          */
568         return ofw_bus_get_node(bus);
569 }
570
571 static device_method_t am335x_ehrpwm_methods[] = {
572         DEVMETHOD(device_probe,         am335x_ehrpwm_probe),
573         DEVMETHOD(device_attach,        am335x_ehrpwm_attach),
574         DEVMETHOD(device_detach,        am335x_ehrpwm_detach),
575
576         /* ofw_bus_if */
577         DEVMETHOD(ofw_bus_get_node,     am335x_ehrpwm_get_node),
578
579         /* pwm interface */
580         DEVMETHOD(pwmbus_channel_count,         am335x_ehrpwm_channel_count),
581         DEVMETHOD(pwmbus_channel_config,        am335x_ehrpwm_channel_config),
582         DEVMETHOD(pwmbus_channel_get_config,    am335x_ehrpwm_channel_get_config),
583         DEVMETHOD(pwmbus_channel_set_flags,     am335x_ehrpwm_channel_set_flags),
584         DEVMETHOD(pwmbus_channel_get_flags,     am335x_ehrpwm_channel_get_flags),
585         DEVMETHOD(pwmbus_channel_enable,        am335x_ehrpwm_channel_enable),
586         DEVMETHOD(pwmbus_channel_is_enabled,    am335x_ehrpwm_channel_is_enabled),
587
588         DEVMETHOD_END
589 };
590
591 static driver_t am335x_ehrpwm_driver = {
592         "pwm",
593         am335x_ehrpwm_methods,
594         sizeof(struct am335x_ehrpwm_softc),
595 };
596
597 static devclass_t am335x_ehrpwm_devclass;
598
599 DRIVER_MODULE(am335x_ehrpwm, am335x_pwmss, am335x_ehrpwm_driver, am335x_ehrpwm_devclass, 0, 0);
600 MODULE_VERSION(am335x_ehrpwm, 1);
601 MODULE_DEPEND(am335x_ehrpwm, am335x_pwmss, 1, 1, 1);
602 MODULE_DEPEND(am335x_ehrpwm, pwmbus, 1, 1, 1);