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