am335x_pwm.c revision 266152
1178354Ssam/*- 2178354Ssam * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org> 3178354Ssam * All rights reserved. 4178354Ssam * 5178354Ssam * Redistribution and use in source and binary forms, with or without 6178354Ssam * modification, are permitted provided that the following conditions 7178354Ssam * are met: 8178354Ssam * 1. Redistributions of source code must retain the above copyright 9178354Ssam * notice, this list of conditions and the following disclaimer. 10178354Ssam * 2. Redistributions in binary form must reproduce the above copyright 11178354Ssam * notice, this list of conditions and the following disclaimer in the 12178354Ssam * documentation and/or other materials provided with the distribution. 13178354Ssam * 14178354Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15178354Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16178354Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17178354Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18178354Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19178354Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20178354Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21178354Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22178354Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23178354Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24178354Ssam * SUCH DAMAGE. 25178354Ssam */ 26178354Ssam 27178354Ssam#include <sys/cdefs.h> 28178354Ssam__FBSDID("$FreeBSD: stable/10/sys/arm/ti/am335x/am335x_pwm.c 266152 2014-05-15 16:11:06Z ian $"); 29178354Ssam 30178354Ssam#include <sys/param.h> 31178354Ssam#include <sys/systm.h> 32178354Ssam#include <sys/kernel.h> 33178354Ssam#include <sys/module.h> 34178354Ssam#include <sys/bus.h> 35178354Ssam#include <sys/lock.h> 36178354Ssam#include <sys/mutex.h> 37178354Ssam#include <sys/resource.h> 38178354Ssam#include <sys/rman.h> 39178354Ssam#include <sys/sysctl.h> 40178354Ssam 41178354Ssam#include <machine/bus.h> 42178354Ssam 43178354Ssam#include <dev/fdt/fdt_common.h> 44178354Ssam#include <dev/ofw/openfirm.h> 45178354Ssam#include <dev/ofw/ofw_bus.h> 46206358Srpaulo#include <dev/ofw/ofw_bus_subr.h> 47178354Ssam 48178354Ssam#include <arm/ti/ti_prcm.h> 49178354Ssam#include <arm/ti/ti_scm.h> 50178354Ssam 51178354Ssam#include "am335x_pwm.h" 52178354Ssam#include "am335x_scm.h" 53178354Ssam 54178354Ssam/* In ticks */ 55178354Ssam#define DEFAULT_PWM_PERIOD 1000 56178354Ssam 57178354Ssam#define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 58178354Ssam#define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 59178354Ssam#define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \ 60178354Ssam device_get_nameunit(_sc->sc_dev), "am335x_pwm softc", MTX_DEF) 61178354Ssam#define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx); 62178354Ssam 63178354Ssamstatic struct resource_spec am335x_pwm_mem_spec[] = { 64178354Ssam { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* PWMSS */ 65178354Ssam { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* eCAP */ 66178354Ssam { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* eQEP */ 67178354Ssam { SYS_RES_MEMORY, 3, RF_ACTIVE }, /*ePWM */ 68178354Ssam { -1, 0, 0 } 69178354Ssam}; 70178354Ssam 71178354Ssam#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 170static device_probe_t am335x_pwm_probe; 171static device_attach_t am335x_pwm_attach; 172static device_detach_t am335x_pwm_detach; 173 174struct 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 188static 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 196static driver_t am335x_pwm_driver = { 197 "am335x_pwm", 198 am335x_pwm_methods, 199 sizeof(struct am335x_pwm_softc), 200}; 201 202static devclass_t am335x_pwm_devclass; 203 204/* 205 * API function to set period/duty cycles for ECASx 206 */ 207int 208am335x_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 244static int 245am335x_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 279static int 280am335x_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 309static int 310am335x_pwm_probe(device_t dev) 311{ 312 313 if (!ofw_bus_status_okay(dev)) 314 return (ENXIO); 315 316 if (!ofw_bus_is_compatible(dev, "ti,am335x-pwm")) 317 return (ENXIO); 318 319 device_set_desc(dev, "AM335x PWM"); 320 321 return (BUS_PROBE_DEFAULT); 322} 323 324static int 325am335x_pwm_attach(device_t dev) 326{ 327 struct am335x_pwm_softc *sc; 328 int err; 329 uint32_t reg; 330 phandle_t node; 331 pcell_t did; 332 struct sysctl_ctx_list *ctx; 333 struct sysctl_oid *tree; 334 335 sc = device_get_softc(dev); 336 sc->sc_dev = dev; 337 /* Get the PWM module id */ 338 node = ofw_bus_get_node(dev); 339 if ((OF_getprop(node, "pwm-device-id", &did, sizeof(did))) <= 0) { 340 device_printf(dev, "missing pwm-device-id attribute in FDT\n"); 341 return (ENXIO); 342 } 343 sc->sc_id = fdt32_to_cpu(did); 344 345 PWM_LOCK_INIT(sc); 346 347 err = bus_alloc_resources(dev, am335x_pwm_mem_spec, 348 sc->sc_mem_res); 349 if (err) { 350 device_printf(dev, "cannot allocate memory resources\n"); 351 goto fail; 352 } 353 354 ti_prcm_clk_enable(PWMSS0_CLK + sc->sc_id); 355 ti_scm_reg_read_4(SCM_PWMSS_CTRL, ®); 356 reg |= (1 << sc->sc_id); 357 ti_scm_reg_write_4(SCM_PWMSS_CTRL, reg); 358 359 /* Init backlight interface */ 360 ctx = device_get_sysctl_ctx(sc->sc_dev); 361 tree = device_get_sysctl_tree(sc->sc_dev); 362 363 sc->sc_period_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 364 "period", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 365 am335x_pwm_sysctl_period, "I", "PWM period"); 366 367 sc->sc_chanA_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 368 "dutyA", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 369 am335x_pwm_sysctl_duty, "I", "Channel A duty cycles"); 370 371 sc->sc_chanB_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 372 "dutyB", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 373 am335x_pwm_sysctl_duty, "I", "Channel B duty cycles"); 374 375 376 /* CONFIGURE EPWM1 */ 377 reg = EPWM_READ2(sc, EPWM_TBCTL); 378 reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK); 379 EPWM_WRITE2(sc, EPWM_TBCTL, reg); 380 381 sc->sc_pwm_period = DEFAULT_PWM_PERIOD; 382 sc->sc_pwm_dutyA = 0; 383 sc->sc_pwm_dutyB = 0; 384 385 EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1); 386 EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA); 387 EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB); 388 389 EPWM_WRITE2(sc, EPWM_AQCTLA, (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR)); 390 EPWM_WRITE2(sc, EPWM_AQCTLB, (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR)); 391 392 /* START EPWM */ 393 reg &= ~TBCTL_CTRMODE_MASK; 394 reg |= TBCTL_CTRMODE_UP | TBCTL_FREERUN; 395 EPWM_WRITE2(sc, EPWM_TBCTL, reg); 396 397 EPWM_WRITE2(sc, EPWM_TZCTL, 0xf); 398 reg = EPWM_READ2(sc, EPWM_TZFLG); 399 400 return (0); 401fail: 402 PWM_LOCK_DESTROY(sc); 403 if (sc->sc_mem_res[0]) 404 bus_release_resources(dev, am335x_pwm_mem_spec, 405 sc->sc_mem_res); 406 407 return(ENXIO); 408} 409 410static int 411am335x_pwm_detach(device_t dev) 412{ 413 struct am335x_pwm_softc *sc; 414 415 sc = device_get_softc(dev); 416 417 PWM_LOCK(sc); 418 if (sc->sc_mem_res[0]) 419 bus_release_resources(dev, am335x_pwm_mem_spec, 420 sc->sc_mem_res); 421 PWM_UNLOCK(sc); 422 423 PWM_LOCK_DESTROY(sc); 424 425 return (0); 426} 427 428DRIVER_MODULE(am335x_pwm, simplebus, am335x_pwm_driver, am335x_pwm_devclass, 0, 0); 429MODULE_VERSION(am335x_pwm, 1); 430MODULE_DEPEND(am335x_pwm, simplebus, 1, 1, 1); 431