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