xref: /freebsd/sys/arm/ti/am335x/am335x_ehrpwm.c (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
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);
602