xref: /openbsd-src/sys/arch/arm64/dev/aplpwm.c (revision e4010951843c0b5db28bee274fcbaade191a1379)
1*e4010951Skettenis /*	$OpenBSD: aplpwm.c,v 1.1 2022/11/21 21:48:06 kettenis Exp $	*/
2*e4010951Skettenis /*
3*e4010951Skettenis  * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
4*e4010951Skettenis  *
5*e4010951Skettenis  * Permission to use, copy, modify, and distribute this software for any
6*e4010951Skettenis  * purpose with or without fee is hereby granted, provided that the above
7*e4010951Skettenis  * copyright notice and this permission notice appear in all copies.
8*e4010951Skettenis  *
9*e4010951Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*e4010951Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*e4010951Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*e4010951Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*e4010951Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*e4010951Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*e4010951Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*e4010951Skettenis  */
17*e4010951Skettenis 
18*e4010951Skettenis #include <sys/param.h>
19*e4010951Skettenis #include <sys/systm.h>
20*e4010951Skettenis #include <sys/device.h>
21*e4010951Skettenis 
22*e4010951Skettenis #include <machine/bus.h>
23*e4010951Skettenis #include <machine/fdt.h>
24*e4010951Skettenis 
25*e4010951Skettenis #include <dev/ofw/openfirm.h>
26*e4010951Skettenis #include <dev/ofw/ofw_clock.h>
27*e4010951Skettenis #include <dev/ofw/ofw_misc.h>
28*e4010951Skettenis #include <dev/ofw/ofw_power.h>
29*e4010951Skettenis #include <dev/ofw/fdt.h>
30*e4010951Skettenis 
31*e4010951Skettenis #define PWM_CTRL		0x0000
32*e4010951Skettenis #define  PWM_CTRL_EN		(1 << 0)
33*e4010951Skettenis #define  PWM_CTRL_UPDATE	(1 << 5)
34*e4010951Skettenis #define  PWM_CTRL_OUTPUT_EN	(1 << 14)
35*e4010951Skettenis #define PWM_OFF_CYCLES		0x0018
36*e4010951Skettenis #define PWM_ON_CYCLES		0x001c
37*e4010951Skettenis 
38*e4010951Skettenis #define NS_PER_S		1000000000
39*e4010951Skettenis 
40*e4010951Skettenis #define HREAD4(sc, reg)							\
41*e4010951Skettenis 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
42*e4010951Skettenis #define HWRITE4(sc, reg, val)						\
43*e4010951Skettenis 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
44*e4010951Skettenis 
45*e4010951Skettenis struct aplpwm_softc {
46*e4010951Skettenis 	struct device		sc_dev;
47*e4010951Skettenis 	bus_space_tag_t		sc_iot;
48*e4010951Skettenis 	bus_space_handle_t	sc_ioh;
49*e4010951Skettenis 
50*e4010951Skettenis 	uint64_t		sc_clkin;
51*e4010951Skettenis 	struct pwm_device	sc_pd;
52*e4010951Skettenis };
53*e4010951Skettenis 
54*e4010951Skettenis int	aplpwm_match(struct device *, void *, void *);
55*e4010951Skettenis void	aplpwm_attach(struct device *, struct device *, void *);
56*e4010951Skettenis 
57*e4010951Skettenis const struct cfattach aplpwm_ca = {
58*e4010951Skettenis 	sizeof (struct aplpwm_softc), aplpwm_match, aplpwm_attach
59*e4010951Skettenis };
60*e4010951Skettenis 
61*e4010951Skettenis struct cfdriver aplpwm_cd = {
62*e4010951Skettenis 	NULL, "aplpwm", DV_DULL
63*e4010951Skettenis };
64*e4010951Skettenis 
65*e4010951Skettenis int	aplpwm_get_state(void *, uint32_t *, struct pwm_state *);
66*e4010951Skettenis int	aplpwm_set_state(void *, uint32_t *, struct pwm_state *);
67*e4010951Skettenis 
68*e4010951Skettenis int
aplpwm_match(struct device * parent,void * match,void * aux)69*e4010951Skettenis aplpwm_match(struct device *parent, void *match, void *aux)
70*e4010951Skettenis {
71*e4010951Skettenis 	struct fdt_attach_args *faa = aux;
72*e4010951Skettenis 
73*e4010951Skettenis 	return OF_is_compatible(faa->fa_node, "apple,s5l-fpwm");
74*e4010951Skettenis }
75*e4010951Skettenis 
76*e4010951Skettenis void
aplpwm_attach(struct device * parent,struct device * self,void * aux)77*e4010951Skettenis aplpwm_attach(struct device *parent, struct device *self, void *aux)
78*e4010951Skettenis {
79*e4010951Skettenis 	struct aplpwm_softc *sc = (struct aplpwm_softc *)self;
80*e4010951Skettenis 	struct fdt_attach_args *faa = aux;
81*e4010951Skettenis 
82*e4010951Skettenis 	if (faa->fa_nreg < 1) {
83*e4010951Skettenis 		printf(": no registers\n");
84*e4010951Skettenis 		return;
85*e4010951Skettenis 	}
86*e4010951Skettenis 
87*e4010951Skettenis 	sc->sc_iot = faa->fa_iot;
88*e4010951Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
89*e4010951Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
90*e4010951Skettenis 		printf(": can't map registers\n");
91*e4010951Skettenis 		return;
92*e4010951Skettenis 	}
93*e4010951Skettenis 
94*e4010951Skettenis 	sc->sc_clkin = clock_get_frequency(faa->fa_node, NULL);
95*e4010951Skettenis 	if (sc->sc_clkin == 0) {
96*e4010951Skettenis 		printf(": no clock\n");
97*e4010951Skettenis 		return;
98*e4010951Skettenis 	}
99*e4010951Skettenis 
100*e4010951Skettenis 	printf("\n");
101*e4010951Skettenis 
102*e4010951Skettenis 	power_domain_enable(faa->fa_node);
103*e4010951Skettenis 
104*e4010951Skettenis 	sc->sc_pd.pd_node = faa->fa_node;
105*e4010951Skettenis 	sc->sc_pd.pd_cookie = sc;
106*e4010951Skettenis 	sc->sc_pd.pd_get_state = aplpwm_get_state;
107*e4010951Skettenis 	sc->sc_pd.pd_set_state = aplpwm_set_state;
108*e4010951Skettenis 	pwm_register(&sc->sc_pd);
109*e4010951Skettenis }
110*e4010951Skettenis 
111*e4010951Skettenis int
aplpwm_get_state(void * cookie,uint32_t * cells,struct pwm_state * ps)112*e4010951Skettenis aplpwm_get_state(void *cookie, uint32_t *cells, struct pwm_state *ps)
113*e4010951Skettenis {
114*e4010951Skettenis 	struct aplpwm_softc *sc = cookie;
115*e4010951Skettenis 	uint64_t on_cycles, off_cycles;
116*e4010951Skettenis 	uint32_t ctrl;
117*e4010951Skettenis 
118*e4010951Skettenis 	ctrl = HREAD4(sc, PWM_CTRL);
119*e4010951Skettenis 	on_cycles = HREAD4(sc, PWM_ON_CYCLES);
120*e4010951Skettenis 	off_cycles = HREAD4(sc, PWM_OFF_CYCLES);
121*e4010951Skettenis 
122*e4010951Skettenis 	memset(ps, 0, sizeof(struct pwm_state));
123*e4010951Skettenis 	ps->ps_period = ((on_cycles + off_cycles) * NS_PER_S) / sc->sc_clkin;
124*e4010951Skettenis 	ps->ps_pulse_width = (on_cycles * NS_PER_S) / sc->sc_clkin;
125*e4010951Skettenis 	if ((ctrl & PWM_CTRL_EN) && (ctrl & PWM_CTRL_OUTPUT_EN))
126*e4010951Skettenis 		ps->ps_enabled = 1;
127*e4010951Skettenis 
128*e4010951Skettenis 	return 0;
129*e4010951Skettenis }
130*e4010951Skettenis 
131*e4010951Skettenis int
aplpwm_set_state(void * cookie,uint32_t * cells,struct pwm_state * ps)132*e4010951Skettenis aplpwm_set_state(void *cookie, uint32_t *cells, struct pwm_state *ps)
133*e4010951Skettenis {
134*e4010951Skettenis 	struct aplpwm_softc *sc = cookie;
135*e4010951Skettenis 	uint64_t cycles, on_cycles, off_cycles;
136*e4010951Skettenis 	uint32_t ctrl;
137*e4010951Skettenis 
138*e4010951Skettenis 	if (ps->ps_pulse_width > ps->ps_period)
139*e4010951Skettenis 		return EINVAL;
140*e4010951Skettenis 
141*e4010951Skettenis 	cycles = (ps->ps_period * sc->sc_clkin) / NS_PER_S;
142*e4010951Skettenis 	on_cycles = (ps->ps_pulse_width * sc->sc_clkin) / NS_PER_S;
143*e4010951Skettenis 	off_cycles = cycles - on_cycles;
144*e4010951Skettenis 	if (on_cycles > UINT32_MAX || off_cycles > UINT32_MAX)
145*e4010951Skettenis 		return EINVAL;
146*e4010951Skettenis 
147*e4010951Skettenis 	if (ps->ps_enabled)
148*e4010951Skettenis 		ctrl = PWM_CTRL_EN | PWM_CTRL_OUTPUT_EN | PWM_CTRL_UPDATE;
149*e4010951Skettenis 	else
150*e4010951Skettenis 		ctrl = 0;
151*e4010951Skettenis 
152*e4010951Skettenis 	HWRITE4(sc, PWM_ON_CYCLES, on_cycles);
153*e4010951Skettenis 	HWRITE4(sc, PWM_OFF_CYCLES, off_cycles);
154*e4010951Skettenis 	HWRITE4(sc, PWM_CTRL, ctrl);
155*e4010951Skettenis 
156*e4010951Skettenis 	return 0;
157*e4010951Skettenis }
158