12e3507c2SEmmanuel Vadot /*-
22e3507c2SEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause
32e3507c2SEmmanuel Vadot *
42e3507c2SEmmanuel Vadot * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
52e3507c2SEmmanuel Vadot * Copyright (c) 2019 Brandon Bergren <git@bdragon.rtk0.net>
62e3507c2SEmmanuel Vadot *
72e3507c2SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without
82e3507c2SEmmanuel Vadot * modification, are permitted provided that the following conditions
92e3507c2SEmmanuel Vadot * are met:
102e3507c2SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright
112e3507c2SEmmanuel Vadot * notice, this list of conditions and the following disclaimer.
122e3507c2SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright
132e3507c2SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the
142e3507c2SEmmanuel Vadot * documentation and/or other materials provided with the distribution.
152e3507c2SEmmanuel Vadot *
162e3507c2SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
172e3507c2SEmmanuel Vadot * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
182e3507c2SEmmanuel Vadot * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
192e3507c2SEmmanuel Vadot * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
202e3507c2SEmmanuel Vadot * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
212e3507c2SEmmanuel Vadot * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
222e3507c2SEmmanuel Vadot * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
232e3507c2SEmmanuel Vadot * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
242e3507c2SEmmanuel Vadot * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252e3507c2SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262e3507c2SEmmanuel Vadot * SUCH DAMAGE.
272e3507c2SEmmanuel Vadot */
282e3507c2SEmmanuel Vadot
292e3507c2SEmmanuel Vadot #include <sys/param.h>
302e3507c2SEmmanuel Vadot #include <sys/systm.h>
312e3507c2SEmmanuel Vadot #include <sys/bus.h>
322e3507c2SEmmanuel Vadot #include <sys/kernel.h>
332e3507c2SEmmanuel Vadot #include <sys/module.h>
342e3507c2SEmmanuel Vadot #include <sys/rman.h>
352e3507c2SEmmanuel Vadot #include <sys/resource.h>
362e3507c2SEmmanuel Vadot #include <machine/bus.h>
372e3507c2SEmmanuel Vadot
382e3507c2SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
392e3507c2SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
402e3507c2SEmmanuel Vadot
41be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
422e3507c2SEmmanuel Vadot
432e3507c2SEmmanuel Vadot #include "pwmbus_if.h"
442e3507c2SEmmanuel Vadot
452e3507c2SEmmanuel Vadot /* Register offsets. */
462e3507c2SEmmanuel Vadot #define RK_PWM_COUNTER 0x00
472e3507c2SEmmanuel Vadot #define RK_PWM_PERIOD 0x04
482e3507c2SEmmanuel Vadot #define RK_PWM_DUTY 0x08
492e3507c2SEmmanuel Vadot #define RK_PWM_CTRL 0x0c
502e3507c2SEmmanuel Vadot
512e3507c2SEmmanuel Vadot #define SET(reg,mask,val) reg = ((reg & ~mask) | val)
522e3507c2SEmmanuel Vadot
532e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_ENABLE_MASK (1 << 0)
542e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_ENABLED (1 << 0)
552e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_DISABLED (0)
562e3507c2SEmmanuel Vadot
572e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_MODE_MASK (3 << 1)
582e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_MODE_ONESHOT (0)
592e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_MODE_CONTINUOUS (1 << 1)
602e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_MODE_CAPTURE (1 << 2)
612e3507c2SEmmanuel Vadot
622e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_DUTY_MASK (1 << 3)
632e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_DUTY_POSITIVE (1 << 3)
642e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_DUTY_NEGATIVE (0)
652e3507c2SEmmanuel Vadot
662e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_INACTIVE_MASK (1 << 4)
672e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_INACTIVE_POSITIVE (1 << 4)
682e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_INACTIVE_NEGATIVE (0)
692e3507c2SEmmanuel Vadot
702e3507c2SEmmanuel Vadot /* PWM Output Alignment */
712e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_ALIGN_MASK (1 << 5)
722e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_ALIGN_CENTER (1 << 5)
732e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_ALIGN_LEFT (0)
742e3507c2SEmmanuel Vadot
752e3507c2SEmmanuel Vadot /* Low power mode: disable prescaler when inactive */
762e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_LP_MASK (1 << 8)
772e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_LP_ENABLE (1 << 8)
782e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_LP_DISABLE (0)
792e3507c2SEmmanuel Vadot
802e3507c2SEmmanuel Vadot /* Clock source: bypass the scaler or not */
812e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_CLOCKSRC_MASK (1 << 9)
822e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_CLOCKSRC_NONSCALED (0)
832e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_CLOCKSRC_SCALED (1 << 9)
842e3507c2SEmmanuel Vadot
852e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_PRESCALE_MASK (7 << 12)
862e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_PRESCALE_SHIFT 12
872e3507c2SEmmanuel Vadot
882e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_SCALE_MASK (0xFF << 16)
892e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_SCALE_SHIFT 16
902e3507c2SEmmanuel Vadot
912e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_REPEAT_MASK (0xFF << 24)
922e3507c2SEmmanuel Vadot #define RK_PWM_CTRL_REPEAT_SHIFT 24
932e3507c2SEmmanuel Vadot
942e3507c2SEmmanuel Vadot #define NS_PER_SEC 1000000000
952e3507c2SEmmanuel Vadot
962e3507c2SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
972e3507c2SEmmanuel Vadot { "rockchip,rk3288-pwm", 1 },
982e3507c2SEmmanuel Vadot { "rockchip,rk3399-pwm", 1 },
992e3507c2SEmmanuel Vadot { NULL, 0 }
1002e3507c2SEmmanuel Vadot };
1012e3507c2SEmmanuel Vadot
1022e3507c2SEmmanuel Vadot static struct resource_spec rk_pwm_spec[] = {
1032e3507c2SEmmanuel Vadot { SYS_RES_MEMORY, 0, RF_ACTIVE },
1042e3507c2SEmmanuel Vadot { -1, 0 }
1052e3507c2SEmmanuel Vadot };
1062e3507c2SEmmanuel Vadot
1072e3507c2SEmmanuel Vadot struct rk_pwm_softc {
1082e3507c2SEmmanuel Vadot device_t dev;
1092e3507c2SEmmanuel Vadot device_t busdev;
1102e3507c2SEmmanuel Vadot clk_t clk;
1112e3507c2SEmmanuel Vadot struct resource *res;
1122e3507c2SEmmanuel Vadot
1132e3507c2SEmmanuel Vadot uint64_t clk_freq;
1142e3507c2SEmmanuel Vadot unsigned int period;
1152e3507c2SEmmanuel Vadot unsigned int duty;
1162e3507c2SEmmanuel Vadot uint32_t flags;
1172e3507c2SEmmanuel Vadot uint8_t prescaler;
1182e3507c2SEmmanuel Vadot uint8_t scaler;
1192e3507c2SEmmanuel Vadot bool using_scaler;
1202e3507c2SEmmanuel Vadot bool enabled;
1212e3507c2SEmmanuel Vadot };
1222e3507c2SEmmanuel Vadot
1232e3507c2SEmmanuel Vadot #define RK_PWM_READ(sc, reg) bus_read_4((sc)->res, (reg))
1242e3507c2SEmmanuel Vadot #define RK_PWM_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
1252e3507c2SEmmanuel Vadot
1262e3507c2SEmmanuel Vadot static int rk_pwm_probe(device_t dev);
1272e3507c2SEmmanuel Vadot static int rk_pwm_attach(device_t dev);
1282e3507c2SEmmanuel Vadot static int rk_pwm_detach(device_t dev);
1292e3507c2SEmmanuel Vadot
1302e3507c2SEmmanuel Vadot static int
rk_pwm_probe(device_t dev)1312e3507c2SEmmanuel Vadot rk_pwm_probe(device_t dev)
1322e3507c2SEmmanuel Vadot {
1332e3507c2SEmmanuel Vadot if (!ofw_bus_status_okay(dev))
1342e3507c2SEmmanuel Vadot return (ENXIO);
1352e3507c2SEmmanuel Vadot
1362e3507c2SEmmanuel Vadot if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
1372e3507c2SEmmanuel Vadot return (ENXIO);
1382e3507c2SEmmanuel Vadot
1392e3507c2SEmmanuel Vadot device_set_desc(dev, "Rockchip PWM");
1402e3507c2SEmmanuel Vadot return (BUS_PROBE_DEFAULT);
1412e3507c2SEmmanuel Vadot }
1422e3507c2SEmmanuel Vadot
1432e3507c2SEmmanuel Vadot static int
rk_pwm_attach(device_t dev)1442e3507c2SEmmanuel Vadot rk_pwm_attach(device_t dev)
1452e3507c2SEmmanuel Vadot {
1462e3507c2SEmmanuel Vadot struct rk_pwm_softc *sc;
1472e3507c2SEmmanuel Vadot phandle_t node;
1482e3507c2SEmmanuel Vadot uint64_t clk_freq;
1492e3507c2SEmmanuel Vadot uint32_t reg;
1502e3507c2SEmmanuel Vadot int error;
1512e3507c2SEmmanuel Vadot
1522e3507c2SEmmanuel Vadot sc = device_get_softc(dev);
1532e3507c2SEmmanuel Vadot sc->dev = dev;
1542e3507c2SEmmanuel Vadot
1552e3507c2SEmmanuel Vadot error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
1562e3507c2SEmmanuel Vadot if (error != 0) {
1572e3507c2SEmmanuel Vadot device_printf(dev, "cannot get clock\n");
1582e3507c2SEmmanuel Vadot goto fail;
1592e3507c2SEmmanuel Vadot }
1602e3507c2SEmmanuel Vadot error = clk_enable(sc->clk);
1612e3507c2SEmmanuel Vadot if (error != 0) {
1622e3507c2SEmmanuel Vadot device_printf(dev, "cannot enable clock\n");
1632e3507c2SEmmanuel Vadot goto fail;
1642e3507c2SEmmanuel Vadot }
1652e3507c2SEmmanuel Vadot error = clk_get_freq(sc->clk, &sc->clk_freq);
1662e3507c2SEmmanuel Vadot if (error != 0) {
1672e3507c2SEmmanuel Vadot device_printf(dev, "cannot get base frequency\n");
1682e3507c2SEmmanuel Vadot goto fail;
1692e3507c2SEmmanuel Vadot }
1702e3507c2SEmmanuel Vadot
1712e3507c2SEmmanuel Vadot if (bus_alloc_resources(dev, rk_pwm_spec, &sc->res) != 0) {
1722e3507c2SEmmanuel Vadot device_printf(dev, "cannot allocate resources for device\n");
1732e3507c2SEmmanuel Vadot error = ENXIO;
1742e3507c2SEmmanuel Vadot goto fail;
1752e3507c2SEmmanuel Vadot }
1762e3507c2SEmmanuel Vadot
1772e3507c2SEmmanuel Vadot /* Read the configuration left by U-Boot */
1782e3507c2SEmmanuel Vadot reg = RK_PWM_READ(sc, RK_PWM_CTRL);
1792e3507c2SEmmanuel Vadot if ((reg & RK_PWM_CTRL_ENABLE_MASK) == RK_PWM_CTRL_ENABLED)
1802e3507c2SEmmanuel Vadot sc->enabled = true;
1812e3507c2SEmmanuel Vadot
1822e3507c2SEmmanuel Vadot reg = RK_PWM_READ(sc, RK_PWM_CTRL);
1832e3507c2SEmmanuel Vadot reg &= RK_PWM_CTRL_PRESCALE_MASK;
1842e3507c2SEmmanuel Vadot sc->prescaler = reg >> RK_PWM_CTRL_PRESCALE_SHIFT;
1852e3507c2SEmmanuel Vadot
1862e3507c2SEmmanuel Vadot reg = RK_PWM_READ(sc, RK_PWM_CTRL);
1872e3507c2SEmmanuel Vadot reg &= RK_PWM_CTRL_SCALE_MASK;
1882e3507c2SEmmanuel Vadot sc->scaler = reg >> RK_PWM_CTRL_SCALE_SHIFT;
1892e3507c2SEmmanuel Vadot
1902e3507c2SEmmanuel Vadot reg = RK_PWM_READ(sc, RK_PWM_CTRL);
1912e3507c2SEmmanuel Vadot if ((reg & RK_PWM_CTRL_CLOCKSRC_MASK) == RK_PWM_CTRL_CLOCKSRC_SCALED)
1922e3507c2SEmmanuel Vadot sc->using_scaler = true;
1932e3507c2SEmmanuel Vadot else
1942e3507c2SEmmanuel Vadot sc->using_scaler = false;
1952e3507c2SEmmanuel Vadot
1962e3507c2SEmmanuel Vadot clk_freq = sc->clk_freq / (2 ^ sc->prescaler);
1972e3507c2SEmmanuel Vadot
1982e3507c2SEmmanuel Vadot if (sc->using_scaler) {
1992e3507c2SEmmanuel Vadot if (sc->scaler == 0)
2002e3507c2SEmmanuel Vadot clk_freq /= 512;
2012e3507c2SEmmanuel Vadot else
2022e3507c2SEmmanuel Vadot clk_freq /= (sc->scaler * 2);
2032e3507c2SEmmanuel Vadot }
2042e3507c2SEmmanuel Vadot
2052e3507c2SEmmanuel Vadot reg = RK_PWM_READ(sc, RK_PWM_PERIOD);
2062e3507c2SEmmanuel Vadot sc->period = NS_PER_SEC /
2072e3507c2SEmmanuel Vadot (clk_freq / reg);
2082e3507c2SEmmanuel Vadot reg = RK_PWM_READ(sc, RK_PWM_DUTY);
2092e3507c2SEmmanuel Vadot sc->duty = NS_PER_SEC /
2102e3507c2SEmmanuel Vadot (clk_freq / reg);
2112e3507c2SEmmanuel Vadot
2122e3507c2SEmmanuel Vadot node = ofw_bus_get_node(dev);
2132e3507c2SEmmanuel Vadot OF_device_register_xref(OF_xref_from_node(node), dev);
2142e3507c2SEmmanuel Vadot
2155b56413dSWarner Losh sc->busdev = device_add_child(dev, "pwmbus", DEVICE_UNIT_ANY);
2162e3507c2SEmmanuel Vadot
217*18250ec6SJohn Baldwin bus_attach_children(dev);
218*18250ec6SJohn Baldwin return (0);
2192e3507c2SEmmanuel Vadot
2202e3507c2SEmmanuel Vadot fail:
2212e3507c2SEmmanuel Vadot rk_pwm_detach(dev);
2222e3507c2SEmmanuel Vadot return (error);
2232e3507c2SEmmanuel Vadot }
2242e3507c2SEmmanuel Vadot
2252e3507c2SEmmanuel Vadot static int
rk_pwm_detach(device_t dev)2262e3507c2SEmmanuel Vadot rk_pwm_detach(device_t dev)
2272e3507c2SEmmanuel Vadot {
2282e3507c2SEmmanuel Vadot struct rk_pwm_softc *sc;
2292e3507c2SEmmanuel Vadot
2302e3507c2SEmmanuel Vadot sc = device_get_softc(dev);
2312e3507c2SEmmanuel Vadot
2322e3507c2SEmmanuel Vadot bus_generic_detach(sc->dev);
2332e3507c2SEmmanuel Vadot
2342e3507c2SEmmanuel Vadot bus_release_resources(dev, rk_pwm_spec, &sc->res);
2352e3507c2SEmmanuel Vadot
2362e3507c2SEmmanuel Vadot return (0);
2372e3507c2SEmmanuel Vadot }
2382e3507c2SEmmanuel Vadot
2392e3507c2SEmmanuel Vadot static phandle_t
aw_pwm_get_node(device_t bus,device_t dev)2402e3507c2SEmmanuel Vadot aw_pwm_get_node(device_t bus, device_t dev)
2412e3507c2SEmmanuel Vadot {
2422e3507c2SEmmanuel Vadot
2432e3507c2SEmmanuel Vadot /*
2442e3507c2SEmmanuel Vadot * Share our controller node with our pwmbus child; it instantiates
2452e3507c2SEmmanuel Vadot * devices by walking the children contained within our node.
2462e3507c2SEmmanuel Vadot */
2472e3507c2SEmmanuel Vadot return ofw_bus_get_node(bus);
2482e3507c2SEmmanuel Vadot }
2492e3507c2SEmmanuel Vadot
2502e3507c2SEmmanuel Vadot static int
rk_pwm_channel_count(device_t dev,u_int * nchannel)2512e3507c2SEmmanuel Vadot rk_pwm_channel_count(device_t dev, u_int *nchannel)
2522e3507c2SEmmanuel Vadot {
2532e3507c2SEmmanuel Vadot /* The device supports 4 channels, but attaches multiple times in the
2542e3507c2SEmmanuel Vadot * device tree. This interferes with advanced usage though, as
2552e3507c2SEmmanuel Vadot * the interrupt capability and channel 3 FIFO register offsets
2562e3507c2SEmmanuel Vadot * don't work right in this situation.
2572e3507c2SEmmanuel Vadot * But since we don't support those yet, pretend we are singlechannel.
2582e3507c2SEmmanuel Vadot */
2592e3507c2SEmmanuel Vadot *nchannel = 1;
2602e3507c2SEmmanuel Vadot
2612e3507c2SEmmanuel Vadot return (0);
2622e3507c2SEmmanuel Vadot }
2632e3507c2SEmmanuel Vadot
2642e3507c2SEmmanuel Vadot static int
rk_pwm_channel_config(device_t dev,u_int channel,u_int period,u_int duty)2652e3507c2SEmmanuel Vadot rk_pwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
2662e3507c2SEmmanuel Vadot {
2672e3507c2SEmmanuel Vadot struct rk_pwm_softc *sc;
2682e3507c2SEmmanuel Vadot uint64_t period_freq, duty_freq;
2692e3507c2SEmmanuel Vadot uint32_t reg;
2702e3507c2SEmmanuel Vadot uint32_t period_out;
2712e3507c2SEmmanuel Vadot uint32_t duty_out;
2722e3507c2SEmmanuel Vadot uint8_t prescaler;
2732e3507c2SEmmanuel Vadot uint8_t scaler;
2742e3507c2SEmmanuel Vadot bool using_scaler;
2752e3507c2SEmmanuel Vadot
2762e3507c2SEmmanuel Vadot sc = device_get_softc(dev);
2772e3507c2SEmmanuel Vadot
2782e3507c2SEmmanuel Vadot period_freq = NS_PER_SEC / period;
2792e3507c2SEmmanuel Vadot /* Datasheet doesn't define, so use Nyquist frequency. */
2802e3507c2SEmmanuel Vadot if (period_freq > (sc->clk_freq / 2))
2812e3507c2SEmmanuel Vadot return (EINVAL);
2822e3507c2SEmmanuel Vadot duty_freq = NS_PER_SEC / duty;
2832e3507c2SEmmanuel Vadot if (duty_freq < period_freq) {
2842e3507c2SEmmanuel Vadot device_printf(sc->dev, "duty < period\n");
2852e3507c2SEmmanuel Vadot return (EINVAL);
2862e3507c2SEmmanuel Vadot }
2872e3507c2SEmmanuel Vadot
2882e3507c2SEmmanuel Vadot /* Assuming 24 MHz reference, we should never actually have
2892e3507c2SEmmanuel Vadot to use the divider due to pwm API limitations. */
2902e3507c2SEmmanuel Vadot prescaler = 0;
2912e3507c2SEmmanuel Vadot scaler = 0;
2922e3507c2SEmmanuel Vadot using_scaler = false;
2932e3507c2SEmmanuel Vadot
2942e3507c2SEmmanuel Vadot /* XXX Expand API to allow for 64 bit period/duty. */
2952e3507c2SEmmanuel Vadot period_out = (sc->clk_freq * period) / NS_PER_SEC;
2962e3507c2SEmmanuel Vadot duty_out = (sc->clk_freq * duty) / NS_PER_SEC;
2972e3507c2SEmmanuel Vadot
2982e3507c2SEmmanuel Vadot reg = RK_PWM_READ(sc, RK_PWM_CTRL);
2992e3507c2SEmmanuel Vadot
3002e3507c2SEmmanuel Vadot if ((reg & RK_PWM_CTRL_MODE_MASK) != RK_PWM_CTRL_MODE_CONTINUOUS) {
3012e3507c2SEmmanuel Vadot /* Switching modes, disable just in case. */
3022e3507c2SEmmanuel Vadot SET(reg, RK_PWM_CTRL_ENABLE_MASK, RK_PWM_CTRL_DISABLED);
3032e3507c2SEmmanuel Vadot RK_PWM_WRITE(sc, RK_PWM_CTRL, reg);
3042e3507c2SEmmanuel Vadot }
3052e3507c2SEmmanuel Vadot
3062e3507c2SEmmanuel Vadot RK_PWM_WRITE(sc, RK_PWM_PERIOD, period_out);
3072e3507c2SEmmanuel Vadot RK_PWM_WRITE(sc, RK_PWM_DUTY, duty_out);
3082e3507c2SEmmanuel Vadot
3092e3507c2SEmmanuel Vadot SET(reg, RK_PWM_CTRL_ENABLE_MASK, RK_PWM_CTRL_ENABLED);
3102e3507c2SEmmanuel Vadot SET(reg, RK_PWM_CTRL_MODE_MASK, RK_PWM_CTRL_MODE_CONTINUOUS);
3112e3507c2SEmmanuel Vadot SET(reg, RK_PWM_CTRL_ALIGN_MASK, RK_PWM_CTRL_ALIGN_LEFT);
3122e3507c2SEmmanuel Vadot SET(reg, RK_PWM_CTRL_CLOCKSRC_MASK, using_scaler);
3132e3507c2SEmmanuel Vadot SET(reg, RK_PWM_CTRL_PRESCALE_MASK,
3142e3507c2SEmmanuel Vadot prescaler << RK_PWM_CTRL_PRESCALE_SHIFT);
3152e3507c2SEmmanuel Vadot SET(reg, RK_PWM_CTRL_SCALE_MASK,
3162e3507c2SEmmanuel Vadot scaler << RK_PWM_CTRL_SCALE_SHIFT);
3172e3507c2SEmmanuel Vadot
3182e3507c2SEmmanuel Vadot RK_PWM_WRITE(sc, RK_PWM_CTRL, reg);
3192e3507c2SEmmanuel Vadot
3202e3507c2SEmmanuel Vadot sc->period = period;
3212e3507c2SEmmanuel Vadot sc->duty = duty;
3222e3507c2SEmmanuel Vadot
3232e3507c2SEmmanuel Vadot return (0);
3242e3507c2SEmmanuel Vadot }
3252e3507c2SEmmanuel Vadot
3262e3507c2SEmmanuel Vadot static int
rk_pwm_channel_get_config(device_t dev,u_int channel,u_int * period,u_int * duty)3272e3507c2SEmmanuel Vadot rk_pwm_channel_get_config(device_t dev, u_int channel, u_int *period, u_int *duty)
3282e3507c2SEmmanuel Vadot {
3292e3507c2SEmmanuel Vadot struct rk_pwm_softc *sc;
3302e3507c2SEmmanuel Vadot
3312e3507c2SEmmanuel Vadot sc = device_get_softc(dev);
3322e3507c2SEmmanuel Vadot
3332e3507c2SEmmanuel Vadot *period = sc->period;
3342e3507c2SEmmanuel Vadot *duty = sc->duty;
3352e3507c2SEmmanuel Vadot
3362e3507c2SEmmanuel Vadot return (0);
3372e3507c2SEmmanuel Vadot }
3382e3507c2SEmmanuel Vadot
3392e3507c2SEmmanuel Vadot static int
rk_pwm_channel_enable(device_t dev,u_int channel,bool enable)3402e3507c2SEmmanuel Vadot rk_pwm_channel_enable(device_t dev, u_int channel, bool enable)
3412e3507c2SEmmanuel Vadot {
3422e3507c2SEmmanuel Vadot struct rk_pwm_softc *sc;
3432e3507c2SEmmanuel Vadot uint32_t reg;
3442e3507c2SEmmanuel Vadot
3452e3507c2SEmmanuel Vadot sc = device_get_softc(dev);
3462e3507c2SEmmanuel Vadot
3472e3507c2SEmmanuel Vadot if (enable && sc->enabled)
3482e3507c2SEmmanuel Vadot return (0);
3492e3507c2SEmmanuel Vadot
3502e3507c2SEmmanuel Vadot reg = RK_PWM_READ(sc, RK_PWM_CTRL);
3512e3507c2SEmmanuel Vadot SET(reg, RK_PWM_CTRL_ENABLE_MASK, enable);
3522e3507c2SEmmanuel Vadot
3532e3507c2SEmmanuel Vadot RK_PWM_WRITE(sc, RK_PWM_CTRL, reg);
3542e3507c2SEmmanuel Vadot
3552e3507c2SEmmanuel Vadot sc->enabled = enable;
3562e3507c2SEmmanuel Vadot
3572e3507c2SEmmanuel Vadot return (0);
3582e3507c2SEmmanuel Vadot }
3592e3507c2SEmmanuel Vadot
3602e3507c2SEmmanuel Vadot static int
rk_pwm_channel_is_enabled(device_t dev,u_int channel,bool * enabled)3612e3507c2SEmmanuel Vadot rk_pwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
3622e3507c2SEmmanuel Vadot {
3632e3507c2SEmmanuel Vadot struct rk_pwm_softc *sc;
3642e3507c2SEmmanuel Vadot
3652e3507c2SEmmanuel Vadot sc = device_get_softc(dev);
3662e3507c2SEmmanuel Vadot
3672e3507c2SEmmanuel Vadot *enabled = sc->enabled;
3682e3507c2SEmmanuel Vadot
3692e3507c2SEmmanuel Vadot return (0);
3702e3507c2SEmmanuel Vadot }
3712e3507c2SEmmanuel Vadot
3722e3507c2SEmmanuel Vadot static device_method_t rk_pwm_methods[] = {
3732e3507c2SEmmanuel Vadot /* Device interface */
3742e3507c2SEmmanuel Vadot DEVMETHOD(device_probe, rk_pwm_probe),
3752e3507c2SEmmanuel Vadot DEVMETHOD(device_attach, rk_pwm_attach),
3762e3507c2SEmmanuel Vadot DEVMETHOD(device_detach, rk_pwm_detach),
3772e3507c2SEmmanuel Vadot
3782e3507c2SEmmanuel Vadot /* ofw_bus interface */
3792e3507c2SEmmanuel Vadot DEVMETHOD(ofw_bus_get_node, aw_pwm_get_node),
3802e3507c2SEmmanuel Vadot
3812e3507c2SEmmanuel Vadot /* pwm interface */
3822e3507c2SEmmanuel Vadot DEVMETHOD(pwmbus_channel_count, rk_pwm_channel_count),
3832e3507c2SEmmanuel Vadot DEVMETHOD(pwmbus_channel_config, rk_pwm_channel_config),
3842e3507c2SEmmanuel Vadot DEVMETHOD(pwmbus_channel_get_config, rk_pwm_channel_get_config),
3852e3507c2SEmmanuel Vadot DEVMETHOD(pwmbus_channel_enable, rk_pwm_channel_enable),
3862e3507c2SEmmanuel Vadot DEVMETHOD(pwmbus_channel_is_enabled, rk_pwm_channel_is_enabled),
3872e3507c2SEmmanuel Vadot
3882e3507c2SEmmanuel Vadot DEVMETHOD_END
3892e3507c2SEmmanuel Vadot };
3902e3507c2SEmmanuel Vadot
3912e3507c2SEmmanuel Vadot static driver_t rk_pwm_driver = {
3922e3507c2SEmmanuel Vadot "pwm",
3932e3507c2SEmmanuel Vadot rk_pwm_methods,
3942e3507c2SEmmanuel Vadot sizeof(struct rk_pwm_softc),
3952e3507c2SEmmanuel Vadot };
3962e3507c2SEmmanuel Vadot
3972e3507c2SEmmanuel Vadot DRIVER_MODULE(rk_pwm, simplebus, rk_pwm_driver, 0, 0);
3982e3507c2SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data);
399