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