xref: /freebsd/sys/dev/pwm/controller/rockchip/rk_pwm.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
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