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_ehrpwm.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$");
   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 
   41 #include <machine/bus.h>
   42 
   43 #include <dev/ofw/openfirm.h>
   44 #include <dev/ofw/ofw_bus.h>
   45 #include <dev/ofw/ofw_bus_subr.h>
   46 
   47 #include <dev/pwm/pwmc.h>
   48 
   49 #include "pwmbus_if.h"
   50 
   51 #include "am335x_pwm.h"
   52 
   53 /*******************************************************************************
   54  * Enhanced resolution PWM driver.  Many of the advanced featues of the hardware
   55  * are not supported by this driver.  What is implemented here is simple
   56  * variable-duty-cycle PWM output.
   57  ******************************************************************************/
   58 
   59 /* In ticks */
   60 #define DEFAULT_PWM_PERIOD      1000
   61 #define PWM_CLOCK               100000000UL
   62 
   63 #define NS_PER_SEC              1000000000
   64 
   65 #define PWM_LOCK(_sc)           mtx_lock(&(_sc)->sc_mtx)
   66 #define PWM_UNLOCK(_sc)         mtx_unlock(&(_sc)->sc_mtx)
   67 #define PWM_LOCK_ASSERT(_sc)    mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
   68 #define PWM_LOCK_INIT(_sc)      mtx_init(&(_sc)->sc_mtx, \
   69     device_get_nameunit(_sc->sc_dev), "am335x_ehrpwm softc", MTX_DEF)
   70 #define PWM_LOCK_DESTROY(_sc)   mtx_destroy(&(_sc)->sc_mtx)
   71 
   72 #define EPWM_READ2(_sc, reg)    bus_read_2((_sc)->sc_mem_res, reg)
   73 #define EPWM_WRITE2(_sc, reg, value)    \
   74     bus_write_2((_sc)->sc_mem_res, reg, value)
   75 
   76 #define EPWM_TBCTL              0x00
   77 /* see 15.2.2.11 for the first two, used in debug situations */
   78 #define         TBCTL_FREERUN_STOP_NEXT_TBC_INCREMENT   (0 << 14)
   79 #define         TBCTL_FREERUN_STOP_COMPLETE_CYCLE       (1 << 14)
   80 /* ignore suspend control signal */
   81 #define         TBCTL_FREERUN                           (2 << 14)
   82 
   83 #define         TBCTL_PHDIR_UP          (1 << 13)
   84 #define         TBCTL_PHDIR_DOWN        (0 << 13)
   85 #define         TBCTL_CLKDIV(x)         ((x) << 10)
   86 #define         TBCTL_CLKDIV_MASK       (7 << 10)
   87 #define         TBCTL_HSPCLKDIV(x)      ((x) << 7)
   88 #define         TBCTL_HSPCLKDIV_MASK    (7 << 7)
   89 #define         TBCTL_SYNCOSEL_DISABLED (3 << 4)
   90 #define         TBCTL_PRDLD_SHADOW      (0 << 3)
   91 #define         TBCTL_PRDLD_IMMEDIATE   (1 << 3)
   92 #define         TBCTL_PHSEN_DISABLED    (0 << 2)
   93 #define         TBCTL_PHSEN_ENABLED     (1 << 2)
   94 #define         TBCTL_CTRMODE_MASK      (3)
   95 #define         TBCTL_CTRMODE_UP        (0 << 0)
   96 #define         TBCTL_CTRMODE_DOWN      (1 << 0)
   97 #define         TBCTL_CTRMODE_UPDOWN    (2 << 0)
   98 #define         TBCTL_CTRMODE_FREEZE    (3 << 0)
   99 
  100 #define EPWM_TBSTS              0x02
  101 #define EPWM_TBPHSHR            0x04
  102 #define EPWM_TBPHS              0x06
  103 #define EPWM_TBCNT              0x08
  104 #define EPWM_TBPRD              0x0a
  105 /* Counter-compare */
  106 #define EPWM_CMPCTL             0x0e
  107 #define         CMPCTL_SHDWBMODE_SHADOW         (1 << 6)
  108 #define         CMPCTL_SHDWBMODE_IMMEDIATE      (0 << 6)
  109 #define         CMPCTL_SHDWAMODE_SHADOW         (1 << 4)
  110 #define         CMPCTL_SHDWAMODE_IMMEDIATE      (0 << 4)
  111 #define         CMPCTL_LOADBMODE_ZERO           (0 << 2)
  112 #define         CMPCTL_LOADBMODE_PRD            (1 << 2)
  113 #define         CMPCTL_LOADBMODE_EITHER         (2 << 2)
  114 #define         CMPCTL_LOADBMODE_FREEZE         (3 << 2)
  115 #define         CMPCTL_LOADAMODE_ZERO           (0 << 0)
  116 #define         CMPCTL_LOADAMODE_PRD            (1 << 0)
  117 #define         CMPCTL_LOADAMODE_EITHER         (2 << 0)
  118 #define         CMPCTL_LOADAMODE_FREEZE         (3 << 0)
  119 #define EPWM_CMPAHR             0x10
  120 #define EPWM_CMPA               0x12
  121 #define EPWM_CMPB               0x14
  122 /* CMPCTL_LOADAMODE_ZERO */
  123 #define EPWM_AQCTLA             0x16
  124 #define EPWM_AQCTLB             0x18
  125 #define         AQCTL_CBU_NONE          (0 << 8)
  126 #define         AQCTL_CBU_CLEAR         (1 << 8)
  127 #define         AQCTL_CBU_SET           (2 << 8)
  128 #define         AQCTL_CBU_TOGGLE        (3 << 8)
  129 #define         AQCTL_CAU_NONE          (0 << 4)
  130 #define         AQCTL_CAU_CLEAR         (1 << 4)
  131 #define         AQCTL_CAU_SET           (2 << 4)
  132 #define         AQCTL_CAU_TOGGLE        (3 << 4)
  133 #define         AQCTL_ZRO_NONE          (0 << 0)
  134 #define         AQCTL_ZRO_CLEAR         (1 << 0)
  135 #define         AQCTL_ZRO_SET           (2 << 0)
  136 #define         AQCTL_ZRO_TOGGLE        (3 << 0)
  137 #define EPWM_AQSFRC             0x1a
  138 #define EPWM_AQCSFRC            0x1c
  139 #define         AQCSFRC_OFF             0
  140 #define         AQCSFRC_LO              1
  141 #define         AQCSFRC_HI              2
  142 #define         AQCSFRC_MASK            3
  143 #define         AQCSFRC(chan, hilo)     ((hilo) << (2 * chan))
  144 
  145 /* Trip-Zone module */
  146 #define EPWM_TZSEL              0x24
  147 #define EPWM_TZCTL              0x28
  148 #define EPWM_TZFLG              0x2C
  149 
  150 /* Dead band */
  151 #define EPWM_DBCTL              0x1E
  152 #define         DBCTL_MASK              (3 << 0)
  153 #define         DBCTL_BYPASS            0
  154 #define         DBCTL_RISING_EDGE       1
  155 #define         DBCTL_FALLING_EDGE      2
  156 #define         DBCTL_BOTH_EDGE         3
  157 
  158 /* PWM-chopper */
  159 #define EPWM_PCCTL              0x3C
  160 #define         PCCTL_CHPEN_MASK        (1 << 0)
  161 #define         PCCTL_CHPEN_DISABLE     0
  162 #define         PCCTL_CHPEN_ENABLE      1
  163 
  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_ehrpwm_probe;
  171 static device_attach_t am335x_ehrpwm_attach;
  172 static device_detach_t am335x_ehrpwm_detach;
  173 
  174 struct ehrpwm_channel {
  175         u_int   duty;           /* on duration, in ns */
  176         bool    enabled;        /* channel enabled? */
  177         bool    inverted;       /* signal inverted? */
  178 };
  179 #define NUM_CHANNELS    2
  180 
  181 struct am335x_ehrpwm_softc {
  182         device_t                sc_dev;
  183         device_t                sc_busdev;
  184         struct mtx              sc_mtx;
  185         struct resource         *sc_mem_res;
  186         int                     sc_mem_rid;
  187 
  188         /* Things used for configuration via pwm(9) api. */
  189         u_int                   sc_clkfreq; /* frequency in Hz */
  190         u_int                   sc_clktick; /* duration in ns */
  191         u_int                   sc_period;  /* duration in ns */
  192         struct ehrpwm_channel   sc_channels[NUM_CHANNELS];
  193 };
  194 
  195 static struct ofw_compat_data compat_data[] = {
  196         {"ti,am3352-ehrpwm",    true},
  197         {"ti,am33xx-ehrpwm",    true},
  198         {NULL,                  false},
  199 };
  200 SIMPLEBUS_PNP_INFO(compat_data);
  201 
  202 static void
  203 am335x_ehrpwm_cfg_duty(struct am335x_ehrpwm_softc *sc, u_int chan, u_int duty)
  204 {
  205         u_int tbcmp;
  206 
  207         if (duty == 0)
  208                 tbcmp = 0;
  209         else
  210                 tbcmp = max(1, duty / sc->sc_clktick);
  211 
  212         sc->sc_channels[chan].duty = tbcmp * sc->sc_clktick;
  213 
  214         PWM_LOCK_ASSERT(sc);
  215         EPWM_WRITE2(sc, (chan == 0) ? EPWM_CMPA : EPWM_CMPB, tbcmp);
  216 }
  217 
  218 static void
  219 am335x_ehrpwm_cfg_enable(struct am335x_ehrpwm_softc *sc, u_int chan, bool enable)
  220 {
  221         uint16_t regval;
  222 
  223         sc->sc_channels[chan].enabled = enable;
  224 
  225         /*
  226          * Turn off any existing software-force of the channel, then force
  227          * it in the right direction (high or low) if it's not being enabled.
  228          */
  229         PWM_LOCK_ASSERT(sc);
  230         regval = EPWM_READ2(sc, EPWM_AQCSFRC);
  231         regval &= ~AQCSFRC(chan, AQCSFRC_MASK);
  232         if (!sc->sc_channels[chan].enabled) {
  233                 if (sc->sc_channels[chan].inverted)
  234                         regval |= AQCSFRC(chan, AQCSFRC_HI);
  235                 else
  236                         regval |= AQCSFRC(chan, AQCSFRC_LO);
  237         }
  238         EPWM_WRITE2(sc, EPWM_AQCSFRC, regval);
  239 }
  240 
  241 static bool
  242 am335x_ehrpwm_cfg_period(struct am335x_ehrpwm_softc *sc, u_int period)
  243 {
  244         uint16_t regval;
  245         u_int clkdiv, hspclkdiv, pwmclk, pwmtick, tbprd;
  246 
  247         /* Can't do a period shorter than 2 clock ticks. */
  248         if (period < 2 * NS_PER_SEC / PWM_CLOCK) {
  249                 sc->sc_clkfreq = 0;
  250                 sc->sc_clktick = 0;
  251                 sc->sc_period  = 0;
  252                 return (false);
  253         }
  254 
  255         /*
  256          * Figure out how much we have to divide down the base 100MHz clock so
  257          * that we can express the requested period as a 16-bit tick count.
  258          */
  259         tbprd = 0;
  260         for (clkdiv = 0; clkdiv < 8; ++clkdiv) {
  261                 const u_int cd = 1 << clkdiv;
  262                 for (hspclkdiv = 0; hspclkdiv < 8; ++hspclkdiv) {
  263                         const u_int cdhs = max(1, hspclkdiv * 2);
  264                         pwmclk = PWM_CLOCK / (cd * cdhs);
  265                         pwmtick = NS_PER_SEC / pwmclk;
  266                         if (period / pwmtick < 65536) {
  267                                 tbprd = period / pwmtick;
  268                                 break;
  269                         }
  270                 }
  271                 if (tbprd != 0)
  272                         break;
  273         }
  274 
  275         /* Handle requested period too long for available clock divisors. */
  276         if (tbprd == 0)
  277                 return (false);
  278 
  279         /*
  280          * If anything has changed from the current settings, reprogram the
  281          * clock divisors and period register.
  282          */
  283         if (sc->sc_clkfreq != pwmclk || sc->sc_clktick != pwmtick ||
  284             sc->sc_period != tbprd * pwmtick) {
  285                 sc->sc_clkfreq = pwmclk;
  286                 sc->sc_clktick = pwmtick;
  287                 sc->sc_period  = tbprd * pwmtick;
  288 
  289                 PWM_LOCK_ASSERT(sc);
  290                 regval = EPWM_READ2(sc, EPWM_TBCTL);
  291                 regval &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
  292                 regval |= TBCTL_CLKDIV(clkdiv) | TBCTL_HSPCLKDIV(hspclkdiv);
  293                 EPWM_WRITE2(sc, EPWM_TBCTL, regval);
  294                 EPWM_WRITE2(sc, EPWM_TBPRD, tbprd - 1);
  295 #if 0
  296                 device_printf(sc->sc_dev, "clkdiv %u hspclkdiv %u tbprd %u "
  297                     "clkfreq %u Hz clktick %u ns period got %u requested %u\n",
  298                     clkdiv, hspclkdiv, tbprd - 1,
  299                     sc->sc_clkfreq, sc->sc_clktick, sc->sc_period, period);
  300 #endif
  301                 /*
  302                  * If the period changed, that invalidates the current CMP
  303                  * registers (duty values), just zero them out.
  304                  */
  305                 am335x_ehrpwm_cfg_duty(sc, 0, 0);
  306                 am335x_ehrpwm_cfg_duty(sc, 1, 0);
  307         }
  308 
  309         return (true);
  310 }
  311 
  312 static int
  313 am335x_ehrpwm_channel_count(device_t dev, u_int *nchannel)
  314 {
  315 
  316         *nchannel = NUM_CHANNELS;
  317 
  318         return (0);
  319 }
  320 
  321 static int
  322 am335x_ehrpwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
  323 {
  324         struct am335x_ehrpwm_softc *sc;
  325         bool status;
  326 
  327         if (channel >= NUM_CHANNELS)
  328                 return (EINVAL);
  329 
  330         sc = device_get_softc(dev);
  331 
  332         PWM_LOCK(sc);
  333         status = am335x_ehrpwm_cfg_period(sc, period);
  334         if (status)
  335                 am335x_ehrpwm_cfg_duty(sc, channel, duty);
  336         PWM_UNLOCK(sc);
  337 
  338         return (status ? 0 : EINVAL);
  339 }
  340 
  341 static int
  342 am335x_ehrpwm_channel_get_config(device_t dev, u_int channel, 
  343     u_int *period, u_int *duty)
  344 {
  345         struct am335x_ehrpwm_softc *sc;
  346 
  347         if (channel >= NUM_CHANNELS)
  348                 return (EINVAL);
  349 
  350         sc = device_get_softc(dev);
  351         *period = sc->sc_period;
  352         *duty = sc->sc_channels[channel].duty;
  353         return (0);
  354 }
  355 
  356 static int
  357 am335x_ehrpwm_channel_set_flags(device_t dev, u_int channel,
  358        uint32_t flags)
  359 {
  360         struct am335x_ehrpwm_softc *sc;
  361 
  362         if (channel >= NUM_CHANNELS)
  363                 return (EINVAL);
  364 
  365         sc = device_get_softc(dev);
  366 
  367         PWM_LOCK(sc);
  368         if (flags & PWM_POLARITY_INVERTED) {
  369                 sc->sc_channels[channel].inverted = true;
  370                 /* Action-Qualifier 15.2.2.5 */
  371                 if (channel == 0)
  372                         EPWM_WRITE2(sc, EPWM_AQCTLA,
  373                             (AQCTL_ZRO_CLEAR | AQCTL_CAU_SET));
  374                 else
  375                         EPWM_WRITE2(sc, EPWM_AQCTLB,
  376                             (AQCTL_ZRO_CLEAR | AQCTL_CBU_SET));
  377         } else {
  378                 sc->sc_channels[channel].inverted = false;
  379                 if (channel == 0)
  380                         EPWM_WRITE2(sc, EPWM_AQCTLA,
  381                             (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
  382                 else
  383                         EPWM_WRITE2(sc, EPWM_AQCTLB,
  384                             (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
  385         }
  386         PWM_UNLOCK(sc);
  387 
  388         return (0);
  389 }
  390 
  391 static int
  392 am335x_ehrpwm_channel_get_flags(device_t dev, u_int channel,
  393     uint32_t *flags)
  394 {
  395         struct am335x_ehrpwm_softc *sc;
  396         if (channel >= NUM_CHANNELS)
  397                 return (EINVAL);
  398 
  399         sc = device_get_softc(dev);
  400 
  401         if (sc->sc_channels[channel].inverted == true)
  402                 *flags = PWM_POLARITY_INVERTED;
  403         else
  404                 *flags = 0;
  405 
  406         return (0);
  407 }
  408 
  409 
  410 static int
  411 am335x_ehrpwm_channel_enable(device_t dev, u_int channel, bool enable)
  412 {
  413         struct am335x_ehrpwm_softc *sc;
  414 
  415         if (channel >= NUM_CHANNELS)
  416                 return (EINVAL);
  417 
  418         sc = device_get_softc(dev);
  419 
  420         PWM_LOCK(sc);
  421         am335x_ehrpwm_cfg_enable(sc, channel, enable);
  422         PWM_UNLOCK(sc);
  423 
  424         return (0);
  425 }
  426 
  427 static int
  428 am335x_ehrpwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
  429 {
  430         struct am335x_ehrpwm_softc *sc;
  431 
  432         if (channel >= NUM_CHANNELS)
  433                 return (EINVAL);
  434 
  435         sc = device_get_softc(dev);
  436 
  437         *enabled = sc->sc_channels[channel].enabled;
  438 
  439         return (0);
  440 }
  441 
  442 static int
  443 am335x_ehrpwm_probe(device_t dev)
  444 {
  445 
  446         if (!ofw_bus_status_okay(dev))
  447                 return (ENXIO);
  448 
  449         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
  450                 return (ENXIO);
  451 
  452         device_set_desc(dev, "AM335x EHRPWM");
  453 
  454         return (BUS_PROBE_DEFAULT);
  455 }
  456 
  457 static int
  458 am335x_ehrpwm_attach(device_t dev)
  459 {
  460         struct am335x_ehrpwm_softc *sc;
  461         uint16_t reg;
  462 
  463         sc = device_get_softc(dev);
  464         sc->sc_dev = dev;
  465 
  466         PWM_LOCK_INIT(sc);
  467 
  468         sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
  469             &sc->sc_mem_rid, RF_ACTIVE);
  470         if (sc->sc_mem_res == NULL) {
  471                 device_printf(dev, "cannot allocate memory resources\n");
  472                 goto fail;
  473         }
  474 
  475         /* CONFIGURE EPWM */
  476         reg = EPWM_READ2(sc, EPWM_TBCTL);
  477         reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
  478         EPWM_WRITE2(sc, EPWM_TBCTL, reg);
  479 
  480         EPWM_WRITE2(sc, EPWM_TBPRD, DEFAULT_PWM_PERIOD - 1);
  481         EPWM_WRITE2(sc, EPWM_CMPA, 0);
  482         EPWM_WRITE2(sc, EPWM_CMPB, 0);
  483 
  484         /* Action-Qualifier 15.2.2.5 */
  485         EPWM_WRITE2(sc, EPWM_AQCTLA, (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
  486         EPWM_WRITE2(sc, EPWM_AQCTLB, (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
  487 
  488         /* Dead band 15.2.2.6 */
  489         reg = EPWM_READ2(sc, EPWM_DBCTL);
  490         reg &= ~DBCTL_MASK;
  491         reg |= DBCTL_BYPASS;
  492         EPWM_WRITE2(sc, EPWM_DBCTL, reg);
  493 
  494         /* PWM-chopper described in 15.2.2.7 */
  495         /* Acc. TRM used in pulse transformerbased gate drivers
  496          * to control the power switching-elements
  497          */
  498         reg = EPWM_READ2(sc, EPWM_PCCTL);
  499         reg &= ~PCCTL_CHPEN_MASK;
  500         reg |= PCCTL_CHPEN_DISABLE;
  501         EPWM_WRITE2(sc, EPWM_PCCTL, PCCTL_CHPEN_DISABLE);
  502 
  503         /* Trip zone are described in 15.2.2.8.
  504          * Essential its used to detect faults and can be configured
  505          * to react on such faults..
  506          */
  507         /* disable TZn as one-shot / CVC trip source 15.2.4.18 */
  508         EPWM_WRITE2(sc, EPWM_TZSEL, 0x0);
  509         /* reg described in 15.2.4.19 */
  510         EPWM_WRITE2(sc, EPWM_TZCTL, 0xf);
  511         reg = EPWM_READ2(sc, EPWM_TZFLG);
  512 
  513         /* START EPWM */
  514         reg &= ~TBCTL_CTRMODE_MASK;
  515         reg |= TBCTL_CTRMODE_UP | TBCTL_FREERUN;
  516         EPWM_WRITE2(sc, EPWM_TBCTL, reg);
  517 
  518         if ((sc->sc_busdev = device_add_child(dev, "pwmbus", -1)) == NULL) {
  519                 device_printf(dev, "Cannot add child pwmbus\n");
  520                 // This driver can still do things even without the bus child.
  521         }
  522 
  523         bus_generic_probe(dev);
  524         return (bus_generic_attach(dev));
  525 fail:
  526         PWM_LOCK_DESTROY(sc);
  527         if (sc->sc_mem_res)
  528                 bus_release_resource(dev, SYS_RES_MEMORY,
  529                     sc->sc_mem_rid, sc->sc_mem_res);
  530 
  531         return(ENXIO);
  532 }
  533 
  534 static int
  535 am335x_ehrpwm_detach(device_t dev)
  536 {
  537         struct am335x_ehrpwm_softc *sc;
  538         int error;
  539 
  540         sc = device_get_softc(dev);
  541 
  542         if ((error = bus_generic_detach(sc->sc_dev)) != 0)
  543                 return (error);
  544 
  545         PWM_LOCK(sc);
  546 
  547         if (sc->sc_busdev != NULL)
  548                 device_delete_child(dev, sc->sc_busdev);
  549 
  550         if (sc->sc_mem_res)
  551                 bus_release_resource(dev, SYS_RES_MEMORY,
  552                     sc->sc_mem_rid, sc->sc_mem_res);
  553 
  554         PWM_UNLOCK(sc);
  555 
  556         PWM_LOCK_DESTROY(sc);
  557 
  558         return (0);
  559 }
  560 
  561 static phandle_t
  562 am335x_ehrpwm_get_node(device_t bus, device_t dev)
  563 {
  564 
  565         /*
  566          * Share our controller node with our pwmbus child; it instantiates
  567          * devices by walking the children contained within our node.
  568          */
  569         return ofw_bus_get_node(bus);
  570 }
  571 
  572 static device_method_t am335x_ehrpwm_methods[] = {
  573         DEVMETHOD(device_probe,         am335x_ehrpwm_probe),
  574         DEVMETHOD(device_attach,        am335x_ehrpwm_attach),
  575         DEVMETHOD(device_detach,        am335x_ehrpwm_detach),
  576 
  577         /* ofw_bus_if */
  578         DEVMETHOD(ofw_bus_get_node,     am335x_ehrpwm_get_node),
  579 
  580         /* pwm interface */
  581         DEVMETHOD(pwmbus_channel_count,         am335x_ehrpwm_channel_count),
  582         DEVMETHOD(pwmbus_channel_config,        am335x_ehrpwm_channel_config),
  583         DEVMETHOD(pwmbus_channel_get_config,    am335x_ehrpwm_channel_get_config),
  584         DEVMETHOD(pwmbus_channel_set_flags,     am335x_ehrpwm_channel_set_flags),
  585         DEVMETHOD(pwmbus_channel_get_flags,     am335x_ehrpwm_channel_get_flags),
  586         DEVMETHOD(pwmbus_channel_enable,        am335x_ehrpwm_channel_enable),
  587         DEVMETHOD(pwmbus_channel_is_enabled,    am335x_ehrpwm_channel_is_enabled),
  588 
  589         DEVMETHOD_END
  590 };
  591 
  592 static driver_t am335x_ehrpwm_driver = {
  593         "pwm",
  594         am335x_ehrpwm_methods,
  595         sizeof(struct am335x_ehrpwm_softc),
  596 };
  597 
  598 DRIVER_MODULE(am335x_ehrpwm, am335x_pwmss, am335x_ehrpwm_driver, 0, 0);
  599 MODULE_VERSION(am335x_ehrpwm, 1);
  600 MODULE_DEPEND(am335x_ehrpwm, am335x_pwmss, 1, 1, 1);
  601 MODULE_DEPEND(am335x_ehrpwm, pwmbus, 1, 1, 1);

Cache object: 3d4e86239843acfdb6d252d5cb2bd8c7


[ 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.