xref: /openbsd-src/sys/dev/fdt/qcpwm.c (revision 8aeda1735db8bfc04c8da3c2dca1319aa20480d6)
1 /*	$OpenBSD: qcpwm.c,v 1.2 2022/11/30 09:52:13 patrick Exp $	*/
2 /*
3  * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se>
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/malloc.h>
21 #include <sys/device.h>
22 #include <sys/sysctl.h>
23 
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/fdt/spmivar.h>
28 
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/fdt.h>
31 #include <dev/ofw/ofw_clock.h>
32 #include <dev/ofw/ofw_pinctrl.h>
33 #include <dev/ofw/ofw_misc.h>
34 
35 #define PWM_CHAN_OFF(x)			(0x100 * (x))
36 #define PWM_SUBTYPE			0x05
37 #define  PWM_SUBTYPE_LPG			0x2
38 #define  PWM_SUBTYPE_PWM			0xb
39 #define  PWM_SUBTYPE_LPG_LITE			0x11
40 #define PWM_SIZE_CLK			0x41
41 #define  PWM_SIZE_CLK_SELECT_SHIFT		0
42 #define  PWM_SIZE_CLK_SELECT_MASK		0x3
43 #define  PWM_SIZE_CLK_LPG_9BIT			(3 << 4)
44 #define  PWM_SIZE_CLK_PWM_9BIT			(1 << 2)
45 #define  PWM_SIZE_CLK_LPG_LITE_9BIT		(1 << 4)
46 #define PWM_PREDIV_CLK			0x42
47 #define  PWM_PREDIV_CLK_EXP_SHIFT		0
48 #define  PWM_PREDIV_CLK_EXP_MASK		0x7
49 #define  PWM_PREDIV_CLK_PREDIV_SHIFT		5
50 #define  PWM_PREDIV_CLK_PREDIV_MASK		0x3
51 #define PWM_TYPE_CONFIG			0x43
52 #define  PWM_TYPE_CONFIG_GLITCH_REMOVAL		(1 << 5)
53 #define PWM_VALUE			0x44
54 #define PWM_ENABLE_CONTROL		0x46
55 #define  PWM_ENABLE_CONTROL_OUTPUT		(1U << 7)
56 #define  PWM_ENABLE_CONTROL_BUFFER_TRISTATE	(1 << 5)
57 #define  PWM_ENABLE_CONTROL_SRC_PWM		(1 << 2)
58 #define  PWM_ENABLE_CONTROL_RAMP_GEN		(1 << 1)
59 #define PWM_SYNC			0x47
60 #define  PWM_SYNC_PWM				(1 << 0)
61 
62 #define PWM_RESOLUTION			512
63 
64 #define NS_PER_S			1000000000LLU
65 
66 uint64_t qcpwm_clk_rates[] = { 0, 1024, 32768, 19200000 };
67 uint64_t qcpwm_pre_divs[] = { 1, 3, 5, 6 };
68 
69 struct qcpwm_softc {
70 	struct device		sc_dev;
71 	int			sc_node;
72 
73 	spmi_tag_t		sc_tag;
74 	int8_t			sc_sid;
75 	uint16_t		sc_addr;
76 
77 	int			sc_nchannel;
78 
79 	struct pwm_device	sc_pd;
80 };
81 
82 int	qcpwm_match(struct device *, void *, void *);
83 void	qcpwm_attach(struct device *, struct device *, void *);
84 
85 const struct cfattach qcpwm_ca = {
86 	sizeof(struct qcpwm_softc), qcpwm_match, qcpwm_attach
87 };
88 
89 struct cfdriver qcpwm_cd = {
90 	NULL, "qcpwm", DV_DULL
91 };
92 
93 uint8_t	qcpwm_read(struct qcpwm_softc *, uint16_t);
94 void	qcpwm_write(struct qcpwm_softc *, uint16_t, uint8_t);
95 
96 int	qcpwm_get_state(void *, uint32_t *, struct pwm_state *);
97 int	qcpwm_set_state(void *, uint32_t *, struct pwm_state *);
98 
99 int
qcpwm_match(struct device * parent,void * match,void * aux)100 qcpwm_match(struct device *parent, void *match, void *aux)
101 {
102 	struct spmi_attach_args *saa = aux;
103 
104 	return OF_is_compatible(saa->sa_node, "qcom,pm8350c-pwm");
105 }
106 
107 void
qcpwm_attach(struct device * parent,struct device * self,void * aux)108 qcpwm_attach(struct device *parent, struct device *self, void *aux)
109 {
110 	struct qcpwm_softc *sc = (struct qcpwm_softc *)self;
111 	struct spmi_attach_args *saa = aux;
112 
113 	sc->sc_addr = OF_getpropint(saa->sa_node, "reg", 0xe800);
114 	sc->sc_node = saa->sa_node;
115 	sc->sc_tag = saa->sa_tag;
116 	sc->sc_sid = saa->sa_sid;
117 
118 	sc->sc_nchannel = 4;
119 
120 	printf("\n");
121 
122 	pinctrl_byname(saa->sa_node, "default");
123 
124 	clock_enable_all(saa->sa_node);
125 	reset_deassert_all(saa->sa_node);
126 
127 	sc->sc_pd.pd_node = saa->sa_node;
128 	sc->sc_pd.pd_cookie = sc;
129 	sc->sc_pd.pd_get_state = qcpwm_get_state;
130 	sc->sc_pd.pd_set_state = qcpwm_set_state;
131 
132 	pwm_register(&sc->sc_pd);
133 }
134 
135 int
qcpwm_get_state(void * cookie,uint32_t * cells,struct pwm_state * ps)136 qcpwm_get_state(void *cookie, uint32_t *cells, struct pwm_state *ps)
137 {
138 	struct qcpwm_softc *sc = cookie;
139 	int chan = cells[0];
140 	uint64_t refclk, prediv, exp;
141 	uint64_t pcycles, dcycles;
142 	uint16_t pwmval;
143 	uint8_t reg;
144 
145 	if (chan >= sc->sc_nchannel)
146 		return ENXIO;
147 
148 	memset(ps, 0, sizeof(struct pwm_state));
149 
150 	ps->ps_enabled = !!(qcpwm_read(sc, PWM_CHAN_OFF(chan) +
151 	    PWM_ENABLE_CONTROL) & PWM_ENABLE_CONTROL_OUTPUT);
152 
153 	reg = qcpwm_read(sc, PWM_CHAN_OFF(chan) + PWM_SIZE_CLK);
154 	refclk = (reg >> PWM_SIZE_CLK_SELECT_SHIFT) &
155 	    PWM_SIZE_CLK_SELECT_MASK;
156 	refclk = qcpwm_clk_rates[refclk];
157 	if (refclk == 0)
158 		return 0;
159 
160 	reg = qcpwm_read(sc, PWM_CHAN_OFF(chan) + PWM_PREDIV_CLK);
161 	exp = (reg >> PWM_PREDIV_CLK_EXP_SHIFT) &
162 	    PWM_PREDIV_CLK_EXP_MASK;
163 	prediv = (reg >> PWM_PREDIV_CLK_PREDIV_SHIFT) &
164 	    PWM_PREDIV_CLK_PREDIV_MASK;
165 	prediv = qcpwm_pre_divs[prediv];
166 
167 	spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
168 	    sc->sc_addr + PWM_CHAN_OFF(chan) + PWM_VALUE,
169 	    &pwmval, sizeof(pwmval));
170 
171 	pcycles = (NS_PER_S * PWM_RESOLUTION * prediv * (1 << exp));
172 	pcycles = (pcycles + refclk - 1) / refclk;
173 	dcycles = (NS_PER_S * pwmval * prediv * (1 << exp));
174 	dcycles = (dcycles + refclk - 1) / refclk;
175 
176 	ps->ps_period = pcycles;
177 	ps->ps_pulse_width = dcycles;
178 	return 0;
179 }
180 
181 int
qcpwm_set_state(void * cookie,uint32_t * cells,struct pwm_state * ps)182 qcpwm_set_state(void *cookie, uint32_t *cells, struct pwm_state *ps)
183 {
184 	struct qcpwm_softc *sc = cookie;
185 	uint64_t dcycles, pcycles, acycles, diff = UINT64_MAX;
186 	int chan = cells[0];
187 	int clksel, divsel, exp;
188 	uint16_t pwmval;
189 	uint8_t reg;
190 	int i, j, m;
191 
192 	if (chan >= sc->sc_nchannel)
193 		return ENXIO;
194 
195 	pcycles = ps->ps_period;
196 	dcycles = ps->ps_pulse_width;
197 
198 	for (i = 1; i < nitems(qcpwm_clk_rates); i++) {
199 		for (j = 0; j < nitems(qcpwm_pre_divs); j++) {
200 			if (pcycles * qcpwm_clk_rates[i] <
201 			    NS_PER_S * qcpwm_pre_divs[j] * PWM_RESOLUTION)
202 				continue;
203 
204 			m = flsl((pcycles * qcpwm_clk_rates[i]) /
205 			    (NS_PER_S * qcpwm_pre_divs[j] * PWM_RESOLUTION))
206 			    - 1;
207 			if (m > PWM_PREDIV_CLK_EXP_MASK)
208 				m = PWM_PREDIV_CLK_EXP_MASK;
209 
210 			acycles = (NS_PER_S * qcpwm_pre_divs[j] *
211 			    PWM_RESOLUTION * (1 << m)) /
212 			    qcpwm_clk_rates[i];
213 			if (pcycles - acycles < diff) {
214 				diff = pcycles - acycles;
215 				ps->ps_period = acycles;
216 				clksel = i;
217 				divsel = j;
218 				exp = m;
219 			}
220 		}
221 	}
222 
223 	ps->ps_pulse_width = (dcycles * ps->ps_period) / pcycles;
224 	if (ps->ps_pulse_width > ps->ps_period)
225 		ps->ps_pulse_width = ps->ps_period;
226 
227 	pcycles = ps->ps_period;
228 	dcycles = ps->ps_pulse_width;
229 	pwmval = ((uint64_t)ps->ps_pulse_width * qcpwm_clk_rates[clksel]) /
230 	    (NS_PER_S * qcpwm_pre_divs[divsel] * (1 << exp));
231 
232 	reg = qcpwm_read(sc, PWM_CHAN_OFF(chan) + PWM_TYPE_CONFIG);
233 	reg |= PWM_TYPE_CONFIG_GLITCH_REMOVAL;
234 	qcpwm_write(sc, PWM_CHAN_OFF(chan) + PWM_TYPE_CONFIG, reg);
235 
236 	reg = clksel << PWM_SIZE_CLK_SELECT_SHIFT;
237 	switch (qcpwm_read(sc, PWM_CHAN_OFF(chan) + PWM_SUBTYPE)) {
238 	case PWM_SUBTYPE_LPG:
239 		reg |= PWM_SIZE_CLK_LPG_9BIT;
240 		break;
241 	case PWM_SUBTYPE_PWM:
242 		reg |= PWM_SIZE_CLK_PWM_9BIT;
243 		break;
244 	case PWM_SUBTYPE_LPG_LITE:
245 	default:
246 		reg |= PWM_SIZE_CLK_LPG_LITE_9BIT;
247 		break;
248 	}
249 	qcpwm_write(sc, PWM_CHAN_OFF(chan) + PWM_SIZE_CLK, reg);
250 	qcpwm_write(sc, PWM_CHAN_OFF(chan) + PWM_PREDIV_CLK,
251 	    divsel << PWM_PREDIV_CLK_PREDIV_SHIFT |
252 	    exp << PWM_PREDIV_CLK_EXP_SHIFT);
253 
254 	if (ps->ps_enabled) {
255 		spmi_cmd_write(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_WRITEL,
256 		    sc->sc_addr + PWM_CHAN_OFF(chan) + PWM_VALUE,
257 		    &pwmval, sizeof(pwmval));
258 	}
259 
260 	reg = PWM_ENABLE_CONTROL_BUFFER_TRISTATE;
261 	if (ps->ps_enabled)
262 		reg |= PWM_ENABLE_CONTROL_OUTPUT;
263 	reg |= PWM_ENABLE_CONTROL_SRC_PWM;
264 	qcpwm_write(sc, PWM_CHAN_OFF(chan) + PWM_ENABLE_CONTROL, reg);
265 
266 	/* HW quirk: rewrite after enable */
267 	if (ps->ps_enabled) {
268 		spmi_cmd_write(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_WRITEL,
269 		    sc->sc_addr + PWM_CHAN_OFF(chan) + PWM_VALUE,
270 		    &pwmval, sizeof(pwmval));
271 	}
272 
273 	qcpwm_write(sc, PWM_CHAN_OFF(chan) + PWM_SYNC, PWM_SYNC_PWM);
274 
275 	reg = qcpwm_read(sc, PWM_CHAN_OFF(chan) + PWM_TYPE_CONFIG);
276 	reg &= ~PWM_TYPE_CONFIG_GLITCH_REMOVAL;
277 	qcpwm_write(sc, PWM_CHAN_OFF(chan) + PWM_TYPE_CONFIG, reg);
278 
279 	return 0;
280 }
281 
282 uint8_t
qcpwm_read(struct qcpwm_softc * sc,uint16_t reg)283 qcpwm_read(struct qcpwm_softc *sc, uint16_t reg)
284 {
285 	uint8_t val = 0;
286 	int error;
287 
288 	error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
289 	    sc->sc_addr + reg, &val, sizeof(val));
290 	if (error)
291 		printf("%s: error reading\n", sc->sc_dev.dv_xname);
292 
293 	return val;
294 }
295 
296 void
qcpwm_write(struct qcpwm_softc * sc,uint16_t reg,uint8_t val)297 qcpwm_write(struct qcpwm_softc *sc, uint16_t reg, uint8_t val)
298 {
299 	int error;
300 
301 	error = spmi_cmd_write(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_WRITEL,
302 	    sc->sc_addr + reg, &val, sizeof(val));
303 	if (error)
304 		printf("%s: error writing\n", sc->sc_dev.dv_xname);
305 }
306