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: releng/10.0/sys/arm/ti/am335x/am335x_pwm.c 252722 2013-07-04 20:13:22Z gonzo $");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/resource.h>
38 #include <sys/rman.h>
39 #include <sys/sysctl.h>
40
41 #include <machine/bus.h>
42
43 #include <dev/fdt/fdt_common.h>
44 #include <dev/ofw/openfirm.h>
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
47
48 #include <arm/ti/ti_prcm.h>
49 #include <arm/ti/ti_scm.h>
50
51 #include "am335x_pwm.h"
52 #include "am335x_scm.h"
53
54 /* In ticks */
55 #define DEFAULT_PWM_PERIOD 1000
56
57 #define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
58 #define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
59 #define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
60 device_get_nameunit(_sc->sc_dev), "am335x_pwm softc", MTX_DEF)
61 #define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx);
62
63 static struct resource_spec am335x_pwm_mem_spec[] = {
64 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* PWMSS */
65 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* eCAP */
66 { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* eQEP */
67 { SYS_RES_MEMORY, 3, RF_ACTIVE }, /*ePWM */
68 { -1, 0, 0 }
69 };
70
71 #define PWMSS_READ4(_sc, reg) bus_read_4((_sc)->sc_mem_res[0], reg);
72 #define PWMSS_WRITE4(_sc, reg, value) \
73 bus_write_4((_sc)->sc_mem_res[0], reg, value);
74
75 #define ECAP_READ2(_sc, reg) bus_read_2((_sc)->sc_mem_res[1], reg);
76 #define ECAP_WRITE2(_sc, reg, value) \
77 bus_write_2((_sc)->sc_mem_res[1], reg, value);
78 #define ECAP_READ4(_sc, reg) bus_read_4((_sc)->sc_mem_res[1], reg);
79 #define ECAP_WRITE4(_sc, reg, value) \
80 bus_write_4((_sc)->sc_mem_res[1], reg, value);
81
82 #define EPWM_READ2(_sc, reg) bus_read_2((_sc)->sc_mem_res[3], reg);
83 #define EPWM_WRITE2(_sc, reg, value) \
84 bus_write_2((_sc)->sc_mem_res[3], reg, value);
85
86 #define PWMSS_IDVER 0x00
87 #define PWMSS_SYSCONFIG 0x04
88 #define PWMSS_CLKCONFIG 0x08
89 #define CLKCONFIG_EPWMCLK_EN (1 << 8)
90 #define PWMSS_CLKSTATUS 0x0C
91
92 #define ECAP_TSCTR 0x00
93 #define ECAP_CAP1 0x08
94 #define ECAP_CAP2 0x0C
95 #define ECAP_CAP3 0x10
96 #define ECAP_CAP4 0x14
97 #define ECAP_ECCTL2 0x2A
98 #define ECCTL2_MODE_APWM (1 << 9)
99 #define ECCTL2_SYNCO_SEL (3 << 6)
100 #define ECCTL2_TSCTRSTOP_FREERUN (1 << 4)
101
102 #define EPWM_TBCTL 0x00
103 #define TBCTL_FREERUN (2 << 14)
104 #define TBCTL_PHDIR_UP (1 << 13)
105 #define TBCTL_PHDIR_DOWN (0 << 13)
106 #define TBCTL_CLKDIV(x) ((x) << 10)
107 #define TBCTL_CLKDIV_MASK (3 << 10)
108 #define TBCTL_HSPCLKDIV(x) ((x) << 7)
109 #define TBCTL_HSPCLKDIV_MASK (3 << 7)
110 #define TBCTL_SYNCOSEL_DISABLED (3 << 4)
111 #define TBCTL_PRDLD_SHADOW (0 << 3)
112 #define TBCTL_PRDLD_IMMEDIATE (0 << 3)
113 #define TBCTL_PHSEN_ENABLED (1 << 2)
114 #define TBCTL_PHSEN_DISABLED (0 << 2)
115 #define TBCTL_CTRMODE_MASK (3)
116 #define TBCTL_CTRMODE_UP (0 << 0)
117 #define TBCTL_CTRMODE_DOWN (1 << 0)
118 #define TBCTL_CTRMODE_UPDOWN (2 << 0)
119 #define TBCTL_CTRMODE_FREEZE (3 << 0)
120
121 #define EPWM_TBSTS 0x02
122 #define EPWM_TBPHSHR 0x04
123 #define EPWM_TBPHS 0x06
124 #define EPWM_TBCNT 0x08
125 #define EPWM_TBPRD 0x0a
126 /* Counter-compare */
127 #define EPWM_CMPCTL 0x0e
128 #define CMPCTL_SHDWBMODE_SHADOW (1 << 6)
129 #define CMPCTL_SHDWBMODE_IMMEDIATE (0 << 6)
130 #define CMPCTL_SHDWAMODE_SHADOW (1 << 4)
131 #define CMPCTL_SHDWAMODE_IMMEDIATE (0 << 4)
132 #define CMPCTL_LOADBMODE_ZERO (0 << 2)
133 #define CMPCTL_LOADBMODE_PRD (1 << 2)
134 #define CMPCTL_LOADBMODE_EITHER (2 << 2)
135 #define CMPCTL_LOADBMODE_FREEZE (3 << 2)
136 #define CMPCTL_LOADAMODE_ZERO (0 << 0)
137 #define CMPCTL_LOADAMODE_PRD (1 << 0)
138 #define CMPCTL_LOADAMODE_EITHER (2 << 0)
139 #define CMPCTL_LOADAMODE_FREEZE (3 << 0)
140 #define EPWM_CMPAHR 0x10
141 #define EPWM_CMPA 0x12
142 #define EPWM_CMPB 0x14
143 /* CMPCTL_LOADAMODE_ZERO */
144 #define EPWM_AQCTLA 0x16
145 #define EPWM_AQCTLB 0x18
146 #define AQCTL_CBU_NONE (0 << 8)
147 #define AQCTL_CBU_CLEAR (1 << 8)
148 #define AQCTL_CBU_SET (2 << 8)
149 #define AQCTL_CBU_TOGGLE (3 << 8)
150 #define AQCTL_CAU_NONE (0 << 4)
151 #define AQCTL_CAU_CLEAR (1 << 4)
152 #define AQCTL_CAU_SET (2 << 4)
153 #define AQCTL_CAU_TOGGLE (3 << 4)
154 #define AQCTL_ZRO_NONE (0 << 0)
155 #define AQCTL_ZRO_CLEAR (1 << 0)
156 #define AQCTL_ZRO_SET (2 << 0)
157 #define AQCTL_ZRO_TOGGLE (3 << 0)
158 #define EPWM_AQSFRC 0x1a
159 #define EPWM_AQCSFRC 0x1c
160
161 /* Trip-Zone module */
162 #define EPWM_TZCTL 0x28
163 #define EPWM_TZFLG 0x2C
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_pwm_probe;
171 static device_attach_t am335x_pwm_attach;
172 static device_detach_t am335x_pwm_detach;
173
174 struct am335x_pwm_softc {
175 device_t sc_dev;
176 struct mtx sc_mtx;
177 struct resource *sc_mem_res[4];
178 int sc_id;
179 /* sysctl for configuration */
180 struct sysctl_oid *sc_period_oid;
181 struct sysctl_oid *sc_chanA_oid;
182 struct sysctl_oid *sc_chanB_oid;
183 uint32_t sc_pwm_period;
184 uint32_t sc_pwm_dutyA;
185 uint32_t sc_pwm_dutyB;
186 };
187
188 static device_method_t am335x_pwm_methods[] = {
189 DEVMETHOD(device_probe, am335x_pwm_probe),
190 DEVMETHOD(device_attach, am335x_pwm_attach),
191 DEVMETHOD(device_detach, am335x_pwm_detach),
192
193 DEVMETHOD_END
194 };
195
196 static driver_t am335x_pwm_driver = {
197 "am335x_pwm",
198 am335x_pwm_methods,
199 sizeof(struct am335x_pwm_softc),
200 };
201
202 static devclass_t am335x_pwm_devclass;
203
204 /*
205 * API function to set period/duty cycles for ECASx
206 */
207 int
208 am335x_pwm_config_ecas(int unit, int period, int duty)
209 {
210 device_t dev;
211 struct am335x_pwm_softc *sc;
212 uint16_t reg;
213
214 dev = devclass_get_device(am335x_pwm_devclass, unit);
215 if (dev == NULL)
216 return (ENXIO);
217
218 if (duty > period)
219 return (EINVAL);
220
221 if (period == 0)
222 return (EINVAL);
223
224 sc = device_get_softc(dev);
225 PWM_LOCK(sc);
226
227 reg = ECAP_READ2(sc, ECAP_ECCTL2);
228 reg |= ECCTL2_MODE_APWM | ECCTL2_TSCTRSTOP_FREERUN | ECCTL2_SYNCO_SEL;
229 ECAP_WRITE2(sc, ECAP_ECCTL2, reg);
230
231 /* CAP3 in APWM mode is APRD shadow register */
232 ECAP_WRITE4(sc, ECAP_CAP3, period - 1);
233
234 /* CAP4 in APWM mode is ACMP shadow register */
235 ECAP_WRITE4(sc, ECAP_CAP4, duty);
236 /* Restart counter */
237 ECAP_WRITE4(sc, ECAP_TSCTR, 0);
238
239 PWM_UNLOCK(sc);
240
241 return (0);
242 }
243
244 static int
245 am335x_pwm_sysctl_duty(SYSCTL_HANDLER_ARGS)
246 {
247 struct am335x_pwm_softc *sc = (struct am335x_pwm_softc*)arg1;
248 int error;
249 uint32_t duty;
250
251 if (oidp == sc->sc_chanA_oid)
252 duty = sc->sc_pwm_dutyA;
253 else
254 duty = sc->sc_pwm_dutyB;
255 error = sysctl_handle_int(oidp, &duty, 0, req);
256
257 if (error != 0 || req->newptr == NULL)
258 return (error);
259
260 if (duty > sc->sc_pwm_period) {
261 device_printf(sc->sc_dev, "Duty cycle can't be greater then period\n");
262 return (EINVAL);
263 }
264
265 PWM_LOCK(sc);
266 if (oidp == sc->sc_chanA_oid) {
267 sc->sc_pwm_dutyA = duty;
268 EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
269 }
270 else {
271 sc->sc_pwm_dutyB = duty;
272 EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
273 }
274 PWM_UNLOCK(sc);
275
276 return (error);
277 }
278
279 static int
280 am335x_pwm_sysctl_period(SYSCTL_HANDLER_ARGS)
281 {
282 struct am335x_pwm_softc *sc = (struct am335x_pwm_softc*)arg1;
283 int error;
284 uint32_t period;
285
286 period = sc->sc_pwm_period;
287 error = sysctl_handle_int(oidp, &period, 0, req);
288
289 if (error != 0 || req->newptr == NULL)
290 return (error);
291
292 if (period < 1)
293 return (EINVAL);
294
295 if ((period < sc->sc_pwm_dutyA) || (period < sc->sc_pwm_dutyB)) {
296 device_printf(sc->sc_dev, "Period can't be less then duty cycle\n");
297 return (EINVAL);
298 }
299
300
301 PWM_LOCK(sc);
302 sc->sc_pwm_period = period;
303 EPWM_WRITE2(sc, EPWM_TBPRD, period - 1);
304 PWM_UNLOCK(sc);
305
306 return (error);
307 }
308
309 static int
310 am335x_pwm_probe(device_t dev)
311 {
312 if (!ofw_bus_is_compatible(dev, "ti,am335x-pwm"))
313 return (ENXIO);
314
315 device_set_desc(dev, "AM335x PWM");
316
317 return (BUS_PROBE_DEFAULT);
318 }
319
320 static int
321 am335x_pwm_attach(device_t dev)
322 {
323 struct am335x_pwm_softc *sc;
324 int err;
325 uint32_t reg;
326 phandle_t node;
327 pcell_t did;
328 struct sysctl_ctx_list *ctx;
329 struct sysctl_oid *tree;
330
331 sc = device_get_softc(dev);
332 sc->sc_dev = dev;
333 /* Get the PWM module id */
334 node = ofw_bus_get_node(dev);
335 if ((OF_getprop(node, "pwm-device-id", &did, sizeof(did))) <= 0) {
336 device_printf(dev, "missing pwm-device-id attribute in FDT\n");
337 return (ENXIO);
338 }
339 sc->sc_id = fdt32_to_cpu(did);
340
341 PWM_LOCK_INIT(sc);
342
343 err = bus_alloc_resources(dev, am335x_pwm_mem_spec,
344 sc->sc_mem_res);
345 if (err) {
346 device_printf(dev, "cannot allocate memory resources\n");
347 goto fail;
348 }
349
350 ti_prcm_clk_enable(PWMSS0_CLK + sc->sc_id);
351 ti_scm_reg_read_4(SCM_PWMSS_CTRL, ®);
352 reg |= (1 << sc->sc_id);
353 ti_scm_reg_write_4(SCM_PWMSS_CTRL, reg);
354
355 /* Init backlight interface */
356 ctx = device_get_sysctl_ctx(sc->sc_dev);
357 tree = device_get_sysctl_tree(sc->sc_dev);
358
359 sc->sc_period_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
360 "period", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
361 am335x_pwm_sysctl_period, "I", "PWM period");
362
363 sc->sc_chanA_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
364 "dutyA", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
365 am335x_pwm_sysctl_duty, "I", "Channel A duty cycles");
366
367 sc->sc_chanB_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
368 "dutyB", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
369 am335x_pwm_sysctl_duty, "I", "Channel B duty cycles");
370
371
372 /* CONFIGURE EPWM1 */
373 reg = EPWM_READ2(sc, EPWM_TBCTL);
374 reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
375 EPWM_WRITE2(sc, EPWM_TBCTL, reg);
376
377 sc->sc_pwm_period = DEFAULT_PWM_PERIOD;
378 sc->sc_pwm_dutyA = 0;
379 sc->sc_pwm_dutyB = 0;
380
381 EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1);
382 EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
383 EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
384
385 EPWM_WRITE2(sc, EPWM_AQCTLA, (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
386 EPWM_WRITE2(sc, EPWM_AQCTLB, (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
387
388 /* START EPWM */
389 reg &= ~TBCTL_CTRMODE_MASK;
390 reg |= TBCTL_CTRMODE_UP | TBCTL_FREERUN;
391 EPWM_WRITE2(sc, EPWM_TBCTL, reg);
392
393 EPWM_WRITE2(sc, EPWM_TZCTL, 0xf);
394 reg = EPWM_READ2(sc, EPWM_TZFLG);
395
396 return (0);
397 fail:
398 PWM_LOCK_DESTROY(sc);
399 if (sc->sc_mem_res[0])
400 bus_release_resources(dev, am335x_pwm_mem_spec,
401 sc->sc_mem_res);
402
403 return(ENXIO);
404 }
405
406 static int
407 am335x_pwm_detach(device_t dev)
408 {
409 struct am335x_pwm_softc *sc;
410
411 sc = device_get_softc(dev);
412
413 PWM_LOCK(sc);
414 if (sc->sc_mem_res[0])
415 bus_release_resources(dev, am335x_pwm_mem_spec,
416 sc->sc_mem_res);
417 PWM_UNLOCK(sc);
418
419 PWM_LOCK_DESTROY(sc);
420
421 return (0);
422 }
423
424 DRIVER_MODULE(am335x_pwm, simplebus, am335x_pwm_driver, am335x_pwm_devclass, 0, 0);
425 MODULE_VERSION(am335x_pwm, 1);
426 MODULE_DEPEND(am335x_pwm, simplebus, 1, 1, 1);
Cache object: 5d9eeb0a1ec202d0a61d12edfef770ec
|