]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/arm/ti/am335x/am335x_pwm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / arm / ti / am335x / am335x_pwm.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/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, &reg);
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);