xref: /netbsd-src/sys/arch/arm/rockchip/rk_pwm.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1*6e54367aSthorpej /* $NetBSD: rk_pwm.c,v 1.7 2021/01/27 03:10:19 thorpej Exp $ */
284c29531Sjmcneill 
384c29531Sjmcneill /*-
484c29531Sjmcneill  * Copyright (c) 2019 Jared McNeill <jmcneill@invisible.ca>
584c29531Sjmcneill  * All rights reserved.
684c29531Sjmcneill  *
784c29531Sjmcneill  * Redistribution and use in source and binary forms, with or without
884c29531Sjmcneill  * modification, are permitted provided that the following conditions
984c29531Sjmcneill  * are met:
1084c29531Sjmcneill  * 1. Redistributions of source code must retain the above copyright
1184c29531Sjmcneill  *    notice, this list of conditions and the following disclaimer.
1284c29531Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1384c29531Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
1484c29531Sjmcneill  *    documentation and/or other materials provided with the distribution.
1584c29531Sjmcneill  *
1684c29531Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1784c29531Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1884c29531Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1984c29531Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2084c29531Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2184c29531Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2284c29531Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2384c29531Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2484c29531Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2584c29531Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2684c29531Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
2784c29531Sjmcneill  */
2884c29531Sjmcneill 
2984c29531Sjmcneill #include <sys/cdefs.h>
3084c29531Sjmcneill 
31*6e54367aSthorpej __KERNEL_RCSID(1, "$NetBSD: rk_pwm.c,v 1.7 2021/01/27 03:10:19 thorpej Exp $");
3284c29531Sjmcneill 
3384c29531Sjmcneill #include <sys/param.h>
3484c29531Sjmcneill #include <sys/bus.h>
3584c29531Sjmcneill #include <sys/device.h>
3684c29531Sjmcneill #include <sys/intr.h>
3784c29531Sjmcneill #include <sys/systm.h>
3884c29531Sjmcneill #include <sys/time.h>
3984c29531Sjmcneill 
4084c29531Sjmcneill #include <dev/pwm/pwmvar.h>
4184c29531Sjmcneill 
4284c29531Sjmcneill #include <dev/fdt/fdtvar.h>
4384c29531Sjmcneill 
4484c29531Sjmcneill #define	PWM0_CNT		0x00
4584c29531Sjmcneill #define	PWM0_PERIOD_HPR		0x04
4684c29531Sjmcneill #define	PWM0_DUTY_LPR		0x08
4784c29531Sjmcneill #define	PWM0_CTRL		0x0c
4884c29531Sjmcneill #define	 CTRL_RPT		__BITS(31,24)
4984c29531Sjmcneill #define	 CTRL_SCALE		__BITS(23,16)
5084c29531Sjmcneill #define	 CTRL_PRESCALE		__BITS(14,12)
5184c29531Sjmcneill #define	 CTRL_CLK_SEL		__BIT(9)
5284c29531Sjmcneill #define	 CTRL_LP_EN		__BIT(8)
5384c29531Sjmcneill #define	 CTRL_OUTPUT_MODE	__BIT(5)
5484c29531Sjmcneill #define	 CTRL_INACTIVE_POL	__BIT(4)
5584c29531Sjmcneill #define	 CTRL_DUTY_POL		__BIT(3)
5684c29531Sjmcneill #define	 CTRL_PWM_MODE		__BITS(2,1)
5784c29531Sjmcneill #define	  CTRL_PWM_MODE_ONESHOT		0
5884c29531Sjmcneill #define	  CTRL_PWM_MODE_CONTINUOUS	1
5984c29531Sjmcneill #define	  CTRL_PWM_MODE_CAPTURE		2
6084c29531Sjmcneill #define	 CTRL_PWM_EN		__BIT(0)
6184c29531Sjmcneill 
6284c29531Sjmcneill enum rk_pwm_type {
6384c29531Sjmcneill 	PWM_RK3288 = 1,
6484c29531Sjmcneill };
6584c29531Sjmcneill 
66646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
67646c0f59Sthorpej 	{ .compat = "rockchip,rk3288-pwm",	.value = PWM_RK3288 },
68ec189949Sthorpej 	DEVICE_COMPAT_EOL
6984c29531Sjmcneill };
7084c29531Sjmcneill 
7184c29531Sjmcneill struct rk_pwm_softc {
7284c29531Sjmcneill 	device_t		sc_dev;
7384c29531Sjmcneill 	bus_space_tag_t		sc_bst;
7484c29531Sjmcneill 	bus_space_handle_t	sc_bsh;
7584c29531Sjmcneill 
7684c29531Sjmcneill 	struct pwm_controller	sc_pwm;
7784c29531Sjmcneill 	struct pwm_config	sc_conf;
7884c29531Sjmcneill 
7984c29531Sjmcneill 	u_int			sc_clkfreq;
8084c29531Sjmcneill };
8184c29531Sjmcneill 
8284c29531Sjmcneill #define	PWM_READ(sc, reg)		\
8384c29531Sjmcneill 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
8484c29531Sjmcneill #define	PWM_WRITE(sc, reg, val)		\
8584c29531Sjmcneill 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
8684c29531Sjmcneill 
8784c29531Sjmcneill static pwm_tag_t
rk_pwm_get_tag(device_t dev,const void * data,size_t len)8884c29531Sjmcneill rk_pwm_get_tag(device_t dev, const void *data, size_t len)
8984c29531Sjmcneill {
9084c29531Sjmcneill 	struct rk_pwm_softc * const sc = device_private(dev);
9184c29531Sjmcneill 	const u_int *pwm = data;
9284c29531Sjmcneill 
9384c29531Sjmcneill 	if (len != 16)
9484c29531Sjmcneill 		return NULL;
9584c29531Sjmcneill 
9684c29531Sjmcneill 	const u_int index = be32toh(pwm[1]);
9784c29531Sjmcneill 	if (index != 0)
9884c29531Sjmcneill 		return NULL;
9984c29531Sjmcneill 
10084c29531Sjmcneill 	const u_int period = be32toh(pwm[2]);
10184c29531Sjmcneill 	const u_int polarity = be32toh(pwm[3]);
10284c29531Sjmcneill 
10384c29531Sjmcneill 	sc->sc_conf.period = period;
10484c29531Sjmcneill 	sc->sc_conf.polarity = polarity ? PWM_ACTIVE_LOW : PWM_ACTIVE_HIGH;
10584c29531Sjmcneill 
10684c29531Sjmcneill 	return &sc->sc_pwm;
10784c29531Sjmcneill }
10884c29531Sjmcneill 
10984c29531Sjmcneill static struct fdtbus_pwm_controller_func rk_pwm_funcs = {
11084c29531Sjmcneill 	.get_tag = rk_pwm_get_tag
11184c29531Sjmcneill };
11284c29531Sjmcneill 
11384c29531Sjmcneill static int
rk_pwm_enable(pwm_tag_t pwm,bool enable)11484c29531Sjmcneill rk_pwm_enable(pwm_tag_t pwm, bool enable)
11584c29531Sjmcneill {
11684c29531Sjmcneill 	struct rk_pwm_softc * const sc = device_private(pwm->pwm_dev);
11784c29531Sjmcneill 	uint32_t ctrl, octrl;
11884c29531Sjmcneill 
11984c29531Sjmcneill 	octrl = ctrl = PWM_READ(sc, PWM0_CTRL);
12084c29531Sjmcneill 	if (enable)
12184c29531Sjmcneill 		ctrl |= CTRL_PWM_EN;
12284c29531Sjmcneill 	else
12384c29531Sjmcneill 		ctrl &= ~CTRL_PWM_EN;
12484c29531Sjmcneill 
12584c29531Sjmcneill 	if (ctrl != octrl)
12684c29531Sjmcneill 		PWM_WRITE(sc, PWM0_CTRL, ctrl);
12784c29531Sjmcneill 
12884c29531Sjmcneill 	return 0;
12984c29531Sjmcneill }
13084c29531Sjmcneill 
13184c29531Sjmcneill static int
rk_pwm_get_config(pwm_tag_t pwm,struct pwm_config * conf)13284c29531Sjmcneill rk_pwm_get_config(pwm_tag_t pwm, struct pwm_config *conf)
13384c29531Sjmcneill {
13484c29531Sjmcneill 	struct rk_pwm_softc * const sc = device_private(pwm->pwm_dev);
135797c778dSjakllsch 
136797c778dSjakllsch #if 0
137797c778dSjakllsch 	/* XXX may be useful someday */
138797c778dSjakllsch 
13984c29531Sjmcneill 	uint32_t ctrl, period, duty;
14084c29531Sjmcneill 	u_int div;
14184c29531Sjmcneill 
14284c29531Sjmcneill 	ctrl = PWM_READ(sc, PWM0_CTRL);
14384c29531Sjmcneill 	period = PWM_READ(sc, PWM0_PERIOD_HPR);
14484c29531Sjmcneill 	duty = PWM_READ(sc, PWM0_DUTY_LPR);
14584c29531Sjmcneill 
14684c29531Sjmcneill 	if (ctrl & CTRL_CLK_SEL) {
14784c29531Sjmcneill 		div = __SHIFTOUT(ctrl, CTRL_SCALE) * 2;
14884c29531Sjmcneill 		if (div == 0)
14984c29531Sjmcneill 			div = 512;
15084c29531Sjmcneill 	} else {
15184c29531Sjmcneill 		div = 1;
15284c29531Sjmcneill 	}
15384c29531Sjmcneill 	div /= (1 << __SHIFTOUT(ctrl, CTRL_PRESCALE));
15484c29531Sjmcneill 
15584c29531Sjmcneill 	const uint64_t rate = sc->sc_clkfreq / div;
15684c29531Sjmcneill 
15784c29531Sjmcneill 	conf->polarity = (ctrl & CTRL_DUTY_POL) ? PWM_ACTIVE_HIGH : PWM_ACTIVE_LOW;
15884c29531Sjmcneill         conf->period = (u_int)(((uint64_t)period * 1000000000) / rate);
15984c29531Sjmcneill         conf->duty_cycle = (u_int)(((uint64_t)duty * 1000000000) / rate);
160797c778dSjakllsch #else
161797c778dSjakllsch 	*conf = sc->sc_conf;
162797c778dSjakllsch #endif
16384c29531Sjmcneill 
16484c29531Sjmcneill 	return 0;
16584c29531Sjmcneill }
16684c29531Sjmcneill 
16784c29531Sjmcneill static int
rk_pwm_set_config(pwm_tag_t pwm,const struct pwm_config * conf)16884c29531Sjmcneill rk_pwm_set_config(pwm_tag_t pwm, const struct pwm_config *conf)
16984c29531Sjmcneill {
17084c29531Sjmcneill 	struct rk_pwm_softc * const sc = device_private(pwm->pwm_dev);
17184c29531Sjmcneill 	uint32_t ctrl;
17284c29531Sjmcneill 
17384c29531Sjmcneill 	const uint64_t rate = sc->sc_clkfreq;
17484c29531Sjmcneill 	const uint32_t period = (u_int)((conf->period * rate) / 1000000000);
17584c29531Sjmcneill 	const uint32_t duty = (u_int)((conf->duty_cycle * rate) / 1000000000);
17684c29531Sjmcneill 
17784c29531Sjmcneill 	/* Preserve PWM_EN flag */
17884c29531Sjmcneill 	ctrl = PWM_READ(sc, PWM0_CTRL) & CTRL_PWM_EN;
17984c29531Sjmcneill 
18084c29531Sjmcneill 	ctrl |= __SHIFTIN(CTRL_PWM_MODE_CONTINUOUS, CTRL_PWM_MODE);
18184c29531Sjmcneill 	if (conf->polarity == PWM_ACTIVE_HIGH)
18284c29531Sjmcneill 		ctrl |= CTRL_DUTY_POL;
18384c29531Sjmcneill 	else
18484c29531Sjmcneill 		ctrl |= CTRL_INACTIVE_POL;
18584c29531Sjmcneill 
18684c29531Sjmcneill 	PWM_WRITE(sc, PWM0_CTRL, 0);
18784c29531Sjmcneill 	PWM_WRITE(sc, PWM0_PERIOD_HPR, period);
18884c29531Sjmcneill 	PWM_WRITE(sc, PWM0_DUTY_LPR, duty);
18984c29531Sjmcneill 	PWM_WRITE(sc, PWM0_CTRL, ctrl);
19084c29531Sjmcneill 
19184c29531Sjmcneill 	sc->sc_conf = *conf;
19284c29531Sjmcneill 
19384c29531Sjmcneill 	return 0;
19484c29531Sjmcneill }
19584c29531Sjmcneill 
19684c29531Sjmcneill static int
rk_pwm_match(device_t parent,cfdata_t cf,void * aux)19784c29531Sjmcneill rk_pwm_match(device_t parent, cfdata_t cf, void *aux)
19884c29531Sjmcneill {
19984c29531Sjmcneill 	struct fdt_attach_args * const faa = aux;
20084c29531Sjmcneill 
201*6e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
20284c29531Sjmcneill }
20384c29531Sjmcneill 
20484c29531Sjmcneill static void
rk_pwm_attach(device_t parent,device_t self,void * aux)20584c29531Sjmcneill rk_pwm_attach(device_t parent, device_t self, void *aux)
20684c29531Sjmcneill {
20784c29531Sjmcneill 	struct rk_pwm_softc * const sc = device_private(self);
20884c29531Sjmcneill 	struct fdt_attach_args * const faa = aux;
20984c29531Sjmcneill 	const int phandle = faa->faa_phandle;
21084c29531Sjmcneill 	struct clk *clk;
21184c29531Sjmcneill 	bus_addr_t addr;
21284c29531Sjmcneill 	bus_size_t size;
21384c29531Sjmcneill 	int error;
21484c29531Sjmcneill 
21584c29531Sjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
21684c29531Sjmcneill 		aprint_error(": couldn't get registers\n");
21784c29531Sjmcneill 		return;
21884c29531Sjmcneill 	}
21984c29531Sjmcneill 
22084c29531Sjmcneill 	clk = fdtbus_clock_get_index(phandle, 0);
22184c29531Sjmcneill 	if (clk == NULL) {
22284c29531Sjmcneill 		aprint_error(": couldn't get clock\n");
22384c29531Sjmcneill 		return;
22484c29531Sjmcneill 	}
22584c29531Sjmcneill 
22684c29531Sjmcneill 	sc->sc_dev = self;
22784c29531Sjmcneill 	sc->sc_clkfreq = clk_get_rate(clk);
22884c29531Sjmcneill 	sc->sc_bst = faa->faa_bst;
22984c29531Sjmcneill 	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
23084c29531Sjmcneill 	if (error) {
231be5f8c7eSskrll 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d",
232be5f8c7eSskrll 		    addr, error);
23384c29531Sjmcneill 		return;
23484c29531Sjmcneill 	}
23584c29531Sjmcneill 
23684c29531Sjmcneill 	aprint_naive("\n");
23784c29531Sjmcneill 	aprint_normal(": PWM\n");
23884c29531Sjmcneill 
23984c29531Sjmcneill 	sc->sc_pwm.pwm_enable = rk_pwm_enable;
24084c29531Sjmcneill 	sc->sc_pwm.pwm_get_config = rk_pwm_get_config;
24184c29531Sjmcneill 	sc->sc_pwm.pwm_set_config = rk_pwm_set_config;
24284c29531Sjmcneill 	sc->sc_pwm.pwm_dev = self;
24384c29531Sjmcneill 
24484c29531Sjmcneill 	fdtbus_register_pwm_controller(self, phandle,
24584c29531Sjmcneill 	    &rk_pwm_funcs);
24684c29531Sjmcneill }
24784c29531Sjmcneill 
24884c29531Sjmcneill CFATTACH_DECL_NEW(rk_pwm, sizeof(struct rk_pwm_softc),
24984c29531Sjmcneill 	rk_pwm_match, rk_pwm_attach, NULL, NULL);
250