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/arm64/rockchip/rk_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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
    5  * Copyright (c) 2019 Brandon Bergren <git@bdragon.rtk0.net>
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $FreeBSD$
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/bus.h>
   37 #include <sys/kernel.h>
   38 #include <sys/module.h>
   39 #include <sys/rman.h>
   40 #include <sys/resource.h>
   41 #include <machine/bus.h>
   42 
   43 #include <dev/ofw/ofw_bus.h>
   44 #include <dev/ofw/ofw_bus_subr.h>
   45 
   46 #include <dev/extres/clk/clk.h>
   47 
   48 #include "pwmbus_if.h"
   49 
   50 /* Register offsets. */
   51 #define RK_PWM_COUNTER                  0x00
   52 #define RK_PWM_PERIOD                   0x04
   53 #define RK_PWM_DUTY                     0x08
   54 #define RK_PWM_CTRL                     0x0c
   55 
   56 #define SET(reg,mask,val)               reg = ((reg & ~mask) | val)
   57 
   58 #define RK_PWM_CTRL_ENABLE_MASK         (1 << 0)
   59 #define  RK_PWM_CTRL_ENABLED            (1 << 0)
   60 #define  RK_PWM_CTRL_DISABLED           (0)
   61 
   62 #define RK_PWM_CTRL_MODE_MASK           (3 << 1)
   63 #define  RK_PWM_CTRL_MODE_ONESHOT       (0)
   64 #define  RK_PWM_CTRL_MODE_CONTINUOUS    (1 << 1)
   65 #define  RK_PWM_CTRL_MODE_CAPTURE       (1 << 2)
   66 
   67 #define RK_PWM_CTRL_DUTY_MASK           (1 << 3)
   68 #define  RK_PWM_CTRL_DUTY_POSITIVE      (1 << 3)
   69 #define  RK_PWM_CTRL_DUTY_NEGATIVE      (0)
   70 
   71 #define RK_PWM_CTRL_INACTIVE_MASK       (1 << 4)
   72 #define  RK_PWM_CTRL_INACTIVE_POSITIVE  (1 << 4)
   73 #define  RK_PWM_CTRL_INACTIVE_NEGATIVE  (0)
   74 
   75 /* PWM Output Alignment */
   76 #define RK_PWM_CTRL_ALIGN_MASK          (1 << 5)
   77 #define  RK_PWM_CTRL_ALIGN_CENTER       (1 << 5)
   78 #define  RK_PWM_CTRL_ALIGN_LEFT         (0)
   79 
   80 /* Low power mode: disable prescaler when inactive */
   81 #define RK_PWM_CTRL_LP_MASK             (1 << 8)
   82 #define  RK_PWM_CTRL_LP_ENABLE          (1 << 8)
   83 #define  RK_PWM_CTRL_LP_DISABLE         (0)
   84 
   85 /* Clock source: bypass the scaler or not */
   86 #define RK_PWM_CTRL_CLOCKSRC_MASK       (1 << 9)
   87 #define  RK_PWM_CTRL_CLOCKSRC_NONSCALED (0)
   88 #define  RK_PWM_CTRL_CLOCKSRC_SCALED    (1 << 9)
   89 
   90 #define RK_PWM_CTRL_PRESCALE_MASK       (7 << 12)
   91 #define RK_PWM_CTRL_PRESCALE_SHIFT      12
   92 
   93 #define RK_PWM_CTRL_SCALE_MASK          (0xFF << 16)
   94 #define RK_PWM_CTRL_SCALE_SHIFT         16
   95 
   96 #define RK_PWM_CTRL_REPEAT_MASK         (0xFF << 24)
   97 #define RK_PWM_CTRL_REPEAT_SHIFT        24
   98 
   99 #define NS_PER_SEC      1000000000
  100 
  101 static struct ofw_compat_data compat_data[] = {
  102         { "rockchip,rk3288-pwm",                1 },
  103         { "rockchip,rk3399-pwm",                1 },
  104         { NULL,                                 0 }
  105 };
  106 
  107 static struct resource_spec rk_pwm_spec[] = {
  108         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
  109         { -1, 0 }
  110 };
  111 
  112 struct rk_pwm_softc {
  113         device_t        dev;
  114         device_t        busdev;
  115         clk_t           clk;
  116         struct resource *res;
  117 
  118         uint64_t        clk_freq;
  119         unsigned int    period;
  120         unsigned int    duty;
  121         uint32_t        flags;
  122         uint8_t         prescaler;
  123         uint8_t         scaler;
  124         bool            using_scaler;
  125         bool            enabled;
  126 };
  127 
  128 #define RK_PWM_READ(sc, reg)            bus_read_4((sc)->res, (reg))
  129 #define RK_PWM_WRITE(sc, reg, val)      bus_write_4((sc)->res, (reg), (val))
  130 
  131 static int rk_pwm_probe(device_t dev);
  132 static int rk_pwm_attach(device_t dev);
  133 static int rk_pwm_detach(device_t dev);
  134 
  135 static int
  136 rk_pwm_probe(device_t dev)
  137 {
  138         if (!ofw_bus_status_okay(dev))
  139                 return (ENXIO);
  140 
  141         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
  142                 return (ENXIO);
  143 
  144         device_set_desc(dev, "Rockchip PWM");
  145         return (BUS_PROBE_DEFAULT);
  146 }
  147 
  148 static int
  149 rk_pwm_attach(device_t dev)
  150 {
  151         struct rk_pwm_softc *sc;
  152         phandle_t node;
  153         uint64_t clk_freq;
  154         uint32_t reg;
  155         int error;
  156 
  157         sc = device_get_softc(dev);
  158         sc->dev = dev;
  159 
  160         error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
  161         if (error != 0) {
  162                 device_printf(dev, "cannot get clock\n");
  163                 goto fail;
  164         }
  165         error = clk_enable(sc->clk);
  166         if (error != 0) {
  167                 device_printf(dev, "cannot enable clock\n");
  168                 goto fail;
  169         }
  170         error = clk_get_freq(sc->clk, &sc->clk_freq);
  171         if (error != 0) {
  172                 device_printf(dev, "cannot get base frequency\n");
  173                 goto fail;
  174         }
  175 
  176         if (bus_alloc_resources(dev, rk_pwm_spec, &sc->res) != 0) {
  177                 device_printf(dev, "cannot allocate resources for device\n");
  178                 error = ENXIO;
  179                 goto fail;
  180         }
  181 
  182         /* Read the configuration left by U-Boot */
  183         reg = RK_PWM_READ(sc, RK_PWM_CTRL);
  184         if ((reg & RK_PWM_CTRL_ENABLE_MASK) == RK_PWM_CTRL_ENABLED)
  185                 sc->enabled = true;
  186 
  187         reg = RK_PWM_READ(sc, RK_PWM_CTRL);
  188         reg &= RK_PWM_CTRL_PRESCALE_MASK;
  189         sc->prescaler = reg >> RK_PWM_CTRL_PRESCALE_SHIFT;
  190 
  191         reg = RK_PWM_READ(sc, RK_PWM_CTRL);
  192         reg &= RK_PWM_CTRL_SCALE_MASK;
  193         sc->scaler = reg >> RK_PWM_CTRL_SCALE_SHIFT;
  194 
  195         reg = RK_PWM_READ(sc, RK_PWM_CTRL);
  196         if ((reg & RK_PWM_CTRL_CLOCKSRC_MASK) == RK_PWM_CTRL_CLOCKSRC_SCALED)
  197                 sc->using_scaler = true;
  198         else
  199                 sc->using_scaler = false;
  200 
  201         clk_freq = sc->clk_freq / (2 ^ sc->prescaler);
  202 
  203         if (sc->using_scaler) {
  204                 if (sc->scaler == 0)
  205                         clk_freq /= 512;
  206                 else
  207                         clk_freq /= (sc->scaler * 2);
  208         }
  209 
  210         reg = RK_PWM_READ(sc, RK_PWM_PERIOD);
  211         sc->period = NS_PER_SEC /
  212                 (clk_freq / reg);
  213         reg = RK_PWM_READ(sc, RK_PWM_DUTY);
  214         sc->duty = NS_PER_SEC /
  215                 (clk_freq / reg);
  216 
  217         node = ofw_bus_get_node(dev);
  218         OF_device_register_xref(OF_xref_from_node(node), dev);
  219 
  220         sc->busdev = device_add_child(dev, "pwmbus", -1);
  221 
  222         return (bus_generic_attach(dev));
  223 
  224 fail:
  225         rk_pwm_detach(dev);
  226         return (error);
  227 }
  228 
  229 static int
  230 rk_pwm_detach(device_t dev)
  231 {
  232         struct rk_pwm_softc *sc;
  233 
  234         sc = device_get_softc(dev);
  235 
  236         bus_generic_detach(sc->dev);
  237 
  238         bus_release_resources(dev, rk_pwm_spec, &sc->res);
  239 
  240         return (0);
  241 }
  242 
  243 static phandle_t
  244 aw_pwm_get_node(device_t bus, device_t dev)
  245 {
  246 
  247         /*
  248          * Share our controller node with our pwmbus child; it instantiates
  249          * devices by walking the children contained within our node.
  250          */
  251         return ofw_bus_get_node(bus);
  252 }
  253 
  254 static int
  255 rk_pwm_channel_count(device_t dev, u_int *nchannel)
  256 {
  257         /* The device supports 4 channels, but attaches multiple times in the
  258          * device tree. This interferes with advanced usage though, as
  259          * the interrupt capability and channel 3 FIFO register offsets
  260          * don't work right in this situation.
  261          * But since we don't support those yet, pretend we are singlechannel.
  262          */
  263         *nchannel = 1;
  264 
  265         return (0);
  266 }
  267 
  268 static int
  269 rk_pwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
  270 {
  271         struct rk_pwm_softc *sc;
  272         uint64_t period_freq, duty_freq;
  273         uint32_t reg;
  274         uint32_t period_out;
  275         uint32_t duty_out;
  276         uint8_t prescaler;
  277         uint8_t scaler;
  278         bool using_scaler;
  279 
  280         sc = device_get_softc(dev);
  281 
  282         period_freq = NS_PER_SEC / period;
  283         /* Datasheet doesn't define, so use Nyquist frequency. */
  284         if (period_freq > (sc->clk_freq / 2))
  285                 return (EINVAL);
  286         duty_freq = NS_PER_SEC / duty;
  287         if (duty_freq < period_freq) {
  288                 device_printf(sc->dev, "duty < period\n");
  289                 return (EINVAL);
  290         }
  291 
  292         /* Assuming 24 MHz reference, we should never actually have
  293            to use the divider due to pwm API limitations. */
  294         prescaler = 0;
  295         scaler = 0;
  296         using_scaler = false;
  297 
  298         /* XXX Expand API to allow for 64 bit period/duty. */
  299         period_out = (sc->clk_freq * period) / NS_PER_SEC;
  300         duty_out = (sc->clk_freq * duty) / NS_PER_SEC;
  301 
  302         reg = RK_PWM_READ(sc, RK_PWM_CTRL);
  303 
  304         if ((reg & RK_PWM_CTRL_MODE_MASK) != RK_PWM_CTRL_MODE_CONTINUOUS) {
  305                 /* Switching modes, disable just in case. */
  306                 SET(reg, RK_PWM_CTRL_ENABLE_MASK, RK_PWM_CTRL_DISABLED);
  307                 RK_PWM_WRITE(sc, RK_PWM_CTRL, reg);
  308         }
  309 
  310         RK_PWM_WRITE(sc, RK_PWM_PERIOD, period_out);
  311         RK_PWM_WRITE(sc, RK_PWM_DUTY, duty_out);
  312 
  313         SET(reg, RK_PWM_CTRL_ENABLE_MASK, RK_PWM_CTRL_ENABLED);
  314         SET(reg, RK_PWM_CTRL_MODE_MASK, RK_PWM_CTRL_MODE_CONTINUOUS);
  315         SET(reg, RK_PWM_CTRL_ALIGN_MASK, RK_PWM_CTRL_ALIGN_LEFT);
  316         SET(reg, RK_PWM_CTRL_CLOCKSRC_MASK, using_scaler);
  317         SET(reg, RK_PWM_CTRL_PRESCALE_MASK,
  318                 prescaler <<  RK_PWM_CTRL_PRESCALE_SHIFT);
  319         SET(reg, RK_PWM_CTRL_SCALE_MASK,
  320                 scaler << RK_PWM_CTRL_SCALE_SHIFT);
  321 
  322         RK_PWM_WRITE(sc, RK_PWM_CTRL, reg);
  323 
  324         sc->period = period;
  325         sc->duty = duty;
  326 
  327         return (0);
  328 }
  329 
  330 static int
  331 rk_pwm_channel_get_config(device_t dev, u_int channel, u_int *period, u_int *duty)
  332 {
  333         struct rk_pwm_softc *sc;
  334 
  335         sc = device_get_softc(dev);
  336 
  337         *period = sc->period;
  338         *duty = sc->duty;
  339 
  340         return (0);
  341 }
  342 
  343 static int
  344 rk_pwm_channel_enable(device_t dev, u_int channel, bool enable)
  345 {
  346         struct rk_pwm_softc *sc;
  347         uint32_t reg;
  348 
  349         sc = device_get_softc(dev);
  350 
  351         if (enable && sc->enabled)
  352                 return (0);
  353 
  354         reg = RK_PWM_READ(sc, RK_PWM_CTRL);
  355         SET(reg, RK_PWM_CTRL_ENABLE_MASK, enable);
  356 
  357         RK_PWM_WRITE(sc, RK_PWM_CTRL, reg);
  358 
  359         sc->enabled = enable;
  360 
  361         return (0);
  362 }
  363 
  364 static int
  365 rk_pwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
  366 {
  367         struct rk_pwm_softc *sc;
  368 
  369         sc = device_get_softc(dev);
  370 
  371         *enabled = sc->enabled;
  372 
  373         return (0);
  374 }
  375 
  376 static device_method_t rk_pwm_methods[] = {
  377         /* Device interface */
  378         DEVMETHOD(device_probe,         rk_pwm_probe),
  379         DEVMETHOD(device_attach,        rk_pwm_attach),
  380         DEVMETHOD(device_detach,        rk_pwm_detach),
  381 
  382         /* ofw_bus interface */
  383         DEVMETHOD(ofw_bus_get_node,     aw_pwm_get_node),
  384 
  385         /* pwm interface */
  386         DEVMETHOD(pwmbus_channel_count,         rk_pwm_channel_count),
  387         DEVMETHOD(pwmbus_channel_config,        rk_pwm_channel_config),
  388         DEVMETHOD(pwmbus_channel_get_config,    rk_pwm_channel_get_config),
  389         DEVMETHOD(pwmbus_channel_enable,        rk_pwm_channel_enable),
  390         DEVMETHOD(pwmbus_channel_is_enabled,    rk_pwm_channel_is_enabled),
  391 
  392         DEVMETHOD_END
  393 };
  394 
  395 static driver_t rk_pwm_driver = {
  396         "pwm",
  397         rk_pwm_methods,
  398         sizeof(struct rk_pwm_softc),
  399 };
  400 
  401 DRIVER_MODULE(rk_pwm, simplebus, rk_pwm_driver, 0, 0);
  402 SIMPLEBUS_PNP_INFO(compat_data);

Cache object: 28f16da4d61b54b3df51643eb3e66ec3


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