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#include <sys/sysctl.h>
41
42#include <machine/bus.h>
43
44#include <dev/fdt/fdt_common.h>
45#include <dev/ofw/openfirm.h>
46#include <dev/ofw/ofw_bus.h>
47#include <dev/ofw/ofw_bus_subr.h>
48
49#include "am335x_pwm.h"
50
51/* In ticks */
52#define	DEFAULT_PWM_PERIOD	1000
53#define	PWM_CLOCK		100000000UL
54
55#define	PWM_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
56#define	PWM_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
57#define	PWM_LOCK_INIT(_sc)	mtx_init(&(_sc)->sc_mtx, \
58    device_get_nameunit(_sc->sc_dev), "am335x_ehrpwm softc", MTX_DEF)
59#define	PWM_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_mtx)
60
61#define	EPWM_READ2(_sc, reg)	bus_read_2((_sc)->sc_mem_res, reg);
62#define	EPWM_WRITE2(_sc, reg, value)	\
63    bus_write_2((_sc)->sc_mem_res, reg, value);
64
65#define	EPWM_TBCTL		0x00
66#define		TBCTL_FREERUN		(2 << 14)
67#define		TBCTL_PHDIR_UP		(1 << 13)
68#define		TBCTL_PHDIR_DOWN	(0 << 13)
69#define		TBCTL_CLKDIV(x)		((x) << 10)
70#define		TBCTL_CLKDIV_MASK	(3 << 10)
71#define		TBCTL_HSPCLKDIV(x)	((x) << 7)
72#define		TBCTL_HSPCLKDIV_MASK	(3 << 7)
73#define		TBCTL_SYNCOSEL_DISABLED	(3 << 4)
74#define		TBCTL_PRDLD_SHADOW	(0 << 3)
75#define		TBCTL_PRDLD_IMMEDIATE	(0 << 3)
76#define		TBCTL_PHSEN_ENABLED	(1 << 2)
77#define		TBCTL_PHSEN_DISABLED	(0 << 2)
78#define		TBCTL_CTRMODE_MASK	(3)
79#define		TBCTL_CTRMODE_UP	(0 << 0)
80#define		TBCTL_CTRMODE_DOWN	(1 << 0)
81#define		TBCTL_CTRMODE_UPDOWN	(2 << 0)
82#define		TBCTL_CTRMODE_FREEZE	(3 << 0)
83
84#define	EPWM_TBSTS		0x02
85#define	EPWM_TBPHSHR		0x04
86#define	EPWM_TBPHS		0x06
87#define	EPWM_TBCNT		0x08
88#define	EPWM_TBPRD		0x0a
89/* Counter-compare */
90#define	EPWM_CMPCTL		0x0e
91#define		CMPCTL_SHDWBMODE_SHADOW		(1 << 6)
92#define		CMPCTL_SHDWBMODE_IMMEDIATE	(0 << 6)
93#define		CMPCTL_SHDWAMODE_SHADOW		(1 << 4)
94#define		CMPCTL_SHDWAMODE_IMMEDIATE	(0 << 4)
95#define		CMPCTL_LOADBMODE_ZERO		(0 << 2)
96#define		CMPCTL_LOADBMODE_PRD		(1 << 2)
97#define		CMPCTL_LOADBMODE_EITHER		(2 << 2)
98#define		CMPCTL_LOADBMODE_FREEZE		(3 << 2)
99#define		CMPCTL_LOADAMODE_ZERO		(0 << 0)
100#define		CMPCTL_LOADAMODE_PRD		(1 << 0)
101#define		CMPCTL_LOADAMODE_EITHER		(2 << 0)
102#define		CMPCTL_LOADAMODE_FREEZE		(3 << 0)
103#define	EPWM_CMPAHR		0x10
104#define	EPWM_CMPA		0x12
105#define	EPWM_CMPB		0x14
106/* CMPCTL_LOADAMODE_ZERO */
107#define	EPWM_AQCTLA		0x16
108#define	EPWM_AQCTLB		0x18
109#define		AQCTL_CBU_NONE		(0 << 8)
110#define		AQCTL_CBU_CLEAR		(1 << 8)
111#define		AQCTL_CBU_SET		(2 << 8)
112#define		AQCTL_CBU_TOGGLE	(3 << 8)
113#define		AQCTL_CAU_NONE		(0 << 4)
114#define		AQCTL_CAU_CLEAR		(1 << 4)
115#define		AQCTL_CAU_SET		(2 << 4)
116#define		AQCTL_CAU_TOGGLE	(3 << 4)
117#define		AQCTL_ZRO_NONE		(0 << 0)
118#define		AQCTL_ZRO_CLEAR		(1 << 0)
119#define		AQCTL_ZRO_SET		(2 << 0)
120#define		AQCTL_ZRO_TOGGLE	(3 << 0)
121#define	EPWM_AQSFRC		0x1a
122#define	EPWM_AQCSFRC		0x1c
123
124/* Trip-Zone module */
125#define	EPWM_TZCTL		0x28
126#define	EPWM_TZFLG		0x2C
127/* High-Resolution PWM */
128#define	EPWM_HRCTL		0x40
129#define		HRCTL_DELMODE_BOTH	3
130#define		HRCTL_DELMODE_FALL	2
131#define		HRCTL_DELMODE_RISE	1
132
133static device_probe_t am335x_ehrpwm_probe;
134static device_attach_t am335x_ehrpwm_attach;
135static device_detach_t am335x_ehrpwm_detach;
136
137static int am335x_ehrpwm_clkdiv[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
138
139struct am335x_ehrpwm_softc {
140	device_t		sc_dev;
141	struct mtx		sc_mtx;
142	struct resource		*sc_mem_res;
143	int			sc_mem_rid;
144	/* sysctl for configuration */
145	int			sc_pwm_clkdiv;
146	int			sc_pwm_freq;
147	struct sysctl_oid	*sc_clkdiv_oid;
148	struct sysctl_oid	*sc_freq_oid;
149	struct sysctl_oid	*sc_period_oid;
150	struct sysctl_oid	*sc_chanA_oid;
151	struct sysctl_oid	*sc_chanB_oid;
152	uint32_t		sc_pwm_period;
153	uint32_t		sc_pwm_dutyA;
154	uint32_t		sc_pwm_dutyB;
155};
156
157static device_method_t am335x_ehrpwm_methods[] = {
158	DEVMETHOD(device_probe,		am335x_ehrpwm_probe),
159	DEVMETHOD(device_attach,	am335x_ehrpwm_attach),
160	DEVMETHOD(device_detach,	am335x_ehrpwm_detach),
161
162	DEVMETHOD_END
163};
164
165static driver_t am335x_ehrpwm_driver = {
166	"am335x_ehrpwm",
167	am335x_ehrpwm_methods,
168	sizeof(struct am335x_ehrpwm_softc),
169};
170
171static devclass_t am335x_ehrpwm_devclass;
172
173static void
174am335x_ehrpwm_freq(struct am335x_ehrpwm_softc *sc)
175{
176	int clkdiv;
177
178	clkdiv = am335x_ehrpwm_clkdiv[sc->sc_pwm_clkdiv];
179	sc->sc_pwm_freq = PWM_CLOCK / (1 * clkdiv) / sc->sc_pwm_period;
180}
181
182static int
183am335x_ehrpwm_sysctl_freq(SYSCTL_HANDLER_ARGS)
184{
185	int clkdiv, error, freq, i, period;
186	struct am335x_ehrpwm_softc *sc;
187	uint32_t reg;
188
189	sc = (struct am335x_ehrpwm_softc *)arg1;
190
191	PWM_LOCK(sc);
192	freq = sc->sc_pwm_freq;
193	PWM_UNLOCK(sc);
194
195	error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
196	if (error != 0 || req->newptr == NULL)
197		return (error);
198
199	if (freq > PWM_CLOCK)
200		freq = PWM_CLOCK;
201
202	PWM_LOCK(sc);
203	if (freq != sc->sc_pwm_freq) {
204		for (i = nitems(am335x_ehrpwm_clkdiv) - 1; i >= 0; i--) {
205			clkdiv = am335x_ehrpwm_clkdiv[i];
206			period = PWM_CLOCK / clkdiv / freq;
207			if (period > USHRT_MAX)
208				break;
209			sc->sc_pwm_clkdiv = i;
210			sc->sc_pwm_period = period;
211		}
212		/* Reset the duty cycle settings. */
213		sc->sc_pwm_dutyA = 0;
214		sc->sc_pwm_dutyB = 0;
215		EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
216		EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
217		/* Update the clkdiv settings. */
218		reg = EPWM_READ2(sc, EPWM_TBCTL);
219		reg &= ~TBCTL_CLKDIV_MASK;
220		reg |= TBCTL_CLKDIV(sc->sc_pwm_clkdiv);
221		EPWM_WRITE2(sc, EPWM_TBCTL, reg);
222		/* Update the period settings. */
223		EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1);
224		am335x_ehrpwm_freq(sc);
225	}
226	PWM_UNLOCK(sc);
227
228	return (0);
229}
230
231static int
232am335x_ehrpwm_sysctl_clkdiv(SYSCTL_HANDLER_ARGS)
233{
234	int error, i, clkdiv;
235	struct am335x_ehrpwm_softc *sc;
236	uint32_t reg;
237
238	sc = (struct am335x_ehrpwm_softc *)arg1;
239
240	PWM_LOCK(sc);
241	clkdiv = am335x_ehrpwm_clkdiv[sc->sc_pwm_clkdiv];
242	PWM_UNLOCK(sc);
243
244	error = sysctl_handle_int(oidp, &clkdiv, sizeof(clkdiv), req);
245	if (error != 0 || req->newptr == NULL)
246		return (error);
247
248	PWM_LOCK(sc);
249	if (clkdiv != am335x_ehrpwm_clkdiv[sc->sc_pwm_clkdiv]) {
250		for (i = 0; i < nitems(am335x_ehrpwm_clkdiv); i++)
251			if (clkdiv >= am335x_ehrpwm_clkdiv[i])
252				sc->sc_pwm_clkdiv = i;
253
254		reg = EPWM_READ2(sc, EPWM_TBCTL);
255		reg &= ~TBCTL_CLKDIV_MASK;
256		reg |= TBCTL_CLKDIV(sc->sc_pwm_clkdiv);
257		EPWM_WRITE2(sc, EPWM_TBCTL, reg);
258		am335x_ehrpwm_freq(sc);
259	}
260	PWM_UNLOCK(sc);
261
262	return (0);
263}
264
265static int
266am335x_ehrpwm_sysctl_duty(SYSCTL_HANDLER_ARGS)
267{
268	struct am335x_ehrpwm_softc *sc = (struct am335x_ehrpwm_softc*)arg1;
269	int error;
270	uint32_t duty;
271
272	if (oidp == sc->sc_chanA_oid)
273		duty = sc->sc_pwm_dutyA;
274	else
275		duty = sc->sc_pwm_dutyB;
276	error = sysctl_handle_int(oidp, &duty, 0, req);
277
278	if (error != 0 || req->newptr == NULL)
279		return (error);
280
281	if (duty > sc->sc_pwm_period) {
282		device_printf(sc->sc_dev, "Duty cycle can't be greater then period\n");
283		return (EINVAL);
284	}
285
286	PWM_LOCK(sc);
287	if (oidp == sc->sc_chanA_oid) {
288		sc->sc_pwm_dutyA = duty;
289		EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
290	}
291	else {
292		sc->sc_pwm_dutyB = duty;
293		EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
294	}
295	PWM_UNLOCK(sc);
296
297	return (error);
298}
299
300static int
301am335x_ehrpwm_sysctl_period(SYSCTL_HANDLER_ARGS)
302{
303	struct am335x_ehrpwm_softc *sc = (struct am335x_ehrpwm_softc*)arg1;
304	int error;
305	uint32_t period;
306
307	period = sc->sc_pwm_period;
308	error = sysctl_handle_int(oidp, &period, 0, req);
309
310	if (error != 0 || req->newptr == NULL)
311		return (error);
312
313	if (period < 1)
314		return (EINVAL);
315
316	if (period > USHRT_MAX)
317		period = USHRT_MAX;
318
319	PWM_LOCK(sc);
320	/* Reset the duty cycle settings. */
321	sc->sc_pwm_dutyA = 0;
322	sc->sc_pwm_dutyB = 0;
323	EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
324	EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
325	/* Update the period settings. */
326	sc->sc_pwm_period = period;
327	EPWM_WRITE2(sc, EPWM_TBPRD, period - 1);
328	am335x_ehrpwm_freq(sc);
329	PWM_UNLOCK(sc);
330
331	return (error);
332}
333
334static int
335am335x_ehrpwm_probe(device_t dev)
336{
337
338	if (!ofw_bus_status_okay(dev))
339		return (ENXIO);
340
341	if (!ofw_bus_is_compatible(dev, "ti,am33xx-ehrpwm"))
342		return (ENXIO);
343
344	device_set_desc(dev, "AM335x EHRPWM");
345
346	return (BUS_PROBE_DEFAULT);
347}
348
349static int
350am335x_ehrpwm_attach(device_t dev)
351{
352	struct am335x_ehrpwm_softc *sc;
353	uint32_t reg;
354	struct sysctl_ctx_list *ctx;
355	struct sysctl_oid *tree;
356
357	sc = device_get_softc(dev);
358	sc->sc_dev = dev;
359
360	PWM_LOCK_INIT(sc);
361
362	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
363	    &sc->sc_mem_rid, RF_ACTIVE);
364	if (sc->sc_mem_res == NULL) {
365		device_printf(dev, "cannot allocate memory resources\n");
366		goto fail;
367	}
368
369	/* Init backlight interface */
370	ctx = device_get_sysctl_ctx(sc->sc_dev);
371	tree = device_get_sysctl_tree(sc->sc_dev);
372
373	sc->sc_clkdiv_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
374	    "clkdiv", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
375	    am335x_ehrpwm_sysctl_clkdiv, "I", "PWM clock prescaler");
376
377	sc->sc_freq_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
378	    "freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
379	    am335x_ehrpwm_sysctl_freq, "I", "PWM frequency");
380
381	sc->sc_period_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
382	    "period", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
383	    am335x_ehrpwm_sysctl_period, "I", "PWM period");
384
385	sc->sc_chanA_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
386	    "dutyA", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
387	    am335x_ehrpwm_sysctl_duty, "I", "Channel A duty cycles");
388
389	sc->sc_chanB_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
390	    "dutyB", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
391	    am335x_ehrpwm_sysctl_duty, "I", "Channel B duty cycles");
392
393	/* CONFIGURE EPWM1 */
394	reg = EPWM_READ2(sc, EPWM_TBCTL);
395	reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
396	EPWM_WRITE2(sc, EPWM_TBCTL, reg);
397
398	sc->sc_pwm_period = DEFAULT_PWM_PERIOD;
399	sc->sc_pwm_dutyA = 0;
400	sc->sc_pwm_dutyB = 0;
401	am335x_ehrpwm_freq(sc);
402
403	EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1);
404	EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
405	EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
406
407	EPWM_WRITE2(sc, EPWM_AQCTLA, (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
408	EPWM_WRITE2(sc, EPWM_AQCTLB, (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
409
410	/* START EPWM */
411	reg &= ~TBCTL_CTRMODE_MASK;
412	reg |= TBCTL_CTRMODE_UP | TBCTL_FREERUN;
413	EPWM_WRITE2(sc, EPWM_TBCTL, reg);
414
415	EPWM_WRITE2(sc, EPWM_TZCTL, 0xf);
416	reg = EPWM_READ2(sc, EPWM_TZFLG);
417
418	return (0);
419fail:
420	PWM_LOCK_DESTROY(sc);
421	if (sc->sc_mem_res)
422		bus_release_resource(dev, SYS_RES_MEMORY,
423		    sc->sc_mem_rid, sc->sc_mem_res);
424
425	return(ENXIO);
426}
427
428static int
429am335x_ehrpwm_detach(device_t dev)
430{
431	struct am335x_ehrpwm_softc *sc;
432
433	sc = device_get_softc(dev);
434
435	PWM_LOCK(sc);
436	if (sc->sc_mem_res)
437		bus_release_resource(dev, SYS_RES_MEMORY,
438		    sc->sc_mem_rid, sc->sc_mem_res);
439	PWM_UNLOCK(sc);
440
441	PWM_LOCK_DESTROY(sc);
442
443	return (0);
444}
445
446DRIVER_MODULE(am335x_ehrpwm, am335x_pwmss, am335x_ehrpwm_driver, am335x_ehrpwm_devclass, 0, 0);
447MODULE_VERSION(am335x_ehrpwm, 1);
448MODULE_DEPEND(am335x_ehrpwm, am335x_pwmss, 1, 1, 1);
449