The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/ti/am335x/am335x_pwm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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, &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);

Cache object: 5d9eeb0a1ec202d0a61d12edfef770ec


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.