xref: /openbsd/sys/dev/fdt/pwmreg.c (revision 09467b48)
1 /*	$OpenBSD: pwmreg.c,v 1.1 2019/09/30 20:44:13 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 
23 #include <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/ofw_regulator.h>
30 #include <dev/ofw/fdt.h>
31 
32 struct pwmreg_softc {
33 	struct device		sc_dev;
34 	uint32_t		*sc_pwm;
35 	uint32_t		sc_dutycycle_unit;
36 	uint32_t		sc_dutycycle_range[2];
37 
38 	struct regulator_device	sc_rd;
39 };
40 
41 int pwmreg_match(struct device *, void *, void *);
42 void pwmreg_attach(struct device *, struct device *, void *);
43 
44 struct cfattach	pwmreg_ca = {
45 	sizeof (struct pwmreg_softc), pwmreg_match, pwmreg_attach
46 };
47 
48 struct cfdriver pwmreg_cd = {
49 	NULL, "pwmreg", DV_DULL
50 };
51 
52 
53 uint32_t pwmreg_get_voltage(void *);
54 int	pwmreg_set_voltage(void *, uint32_t);
55 int	pwmreg_enable(void *, int);
56 
57 int
58 pwmreg_match(struct device *parent, void *match, void *aux)
59 {
60 	struct fdt_attach_args *faa = aux;
61 
62 	return OF_is_compatible(faa->fa_node, "pwm-regulator");
63 }
64 
65 void
66 pwmreg_attach(struct device *parent, struct device *self, void *aux)
67 {
68 	struct pwmreg_softc *sc = (struct pwmreg_softc *)self;
69 	struct fdt_attach_args *faa = aux;
70 	int len;
71 
72 	if (OF_getproplen(faa->fa_node, "voltage-tables") > 0) {
73 		printf(": voltage table mode unsupported\n");
74 		return;
75 	}
76 
77 	len = OF_getproplen(faa->fa_node, "pwms");
78 	if (len <= 4) {
79 		printf(": no pwm\n");
80 		return;
81 	}
82 
83 	sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
84 	OF_getpropintarray(faa->fa_node, "pwms", sc->sc_pwm, len);
85 
86 	sc->sc_dutycycle_unit =
87 	    OF_getpropint(faa->fa_node, "pwm-dutycycle-unit", 100);
88 	sc->sc_dutycycle_range[0] = 0;
89 	sc->sc_dutycycle_range[1] = 100;
90 	OF_getpropintarray(faa->fa_node, "pwm-dutycycle-range",
91 	    sc->sc_dutycycle_range, sizeof(sc->sc_dutycycle_range));
92 
93 	printf("\n");
94 
95 	sc->sc_rd.rd_node = faa->fa_node;
96 	sc->sc_rd.rd_cookie = sc;
97 	sc->sc_rd.rd_get_voltage = pwmreg_get_voltage;
98 	sc->sc_rd.rd_set_voltage = pwmreg_set_voltage;
99 	sc->sc_rd.rd_enable = pwmreg_enable;
100 	regulator_register(&sc->sc_rd);
101 }
102 
103 uint32_t
104 pwmreg_get_voltage(void *cookie)
105 {
106 	struct pwmreg_softc *sc = cookie;
107 	struct pwm_state ps;
108 	int32_t x0, x1, y0, y1;
109 	int32_t x, y;
110 
111 	if (pwm_get_state(sc->sc_pwm, &ps))
112 		return 0;
113 
114 	x0 = sc->sc_dutycycle_range[0];
115 	x1 = sc->sc_dutycycle_range[1];
116 	y0 = sc->sc_rd.rd_volt_min;
117 	y1 = sc->sc_rd.rd_volt_max;
118 	x = (ps.ps_pulse_width * sc->sc_dutycycle_unit) / ps.ps_period;
119 	y = y0 + (x - x0) * (y1 - y0) / (x1 - x0);
120 	return y;
121 }
122 
123 int
124 pwmreg_set_voltage(void *cookie, uint32_t voltage)
125 {
126 	struct pwmreg_softc *sc = cookie;
127 	struct pwm_state ps;
128 	int32_t x0, x1, y0, y1;
129 	int32_t x, y;
130 
131 	if (pwm_init_state(sc->sc_pwm, &ps))
132 		return 0;
133 
134 	x0 = sc->sc_rd.rd_volt_min;
135 	x1 = sc->sc_rd.rd_volt_max;
136 	y0 = sc->sc_dutycycle_range[0];
137 	y1 = sc->sc_dutycycle_range[1];
138 	x = voltage;
139 	y = y0 + (x - x0) * (y1 - y0) / (x1 - x0);
140 
141 	ps.ps_pulse_width = (y * ps.ps_period) / sc->sc_dutycycle_unit;
142 	return pwm_set_state(sc->sc_pwm, &ps);
143 }
144 
145 int
146 pwmreg_enable(void *cookie, int on)
147 {
148 	struct pwmreg_softc *sc = cookie;
149 	struct pwm_state ps;
150 	int error;
151 
152 	error = pwm_get_state(sc->sc_pwm, &ps);
153 	if (error)
154 		return error;
155 
156 	if (ps.ps_enabled == on)
157 		return 0;
158 
159 	ps.ps_enabled = on;
160 	return pwm_set_state(sc->sc_pwm, &ps);
161 }
162