xref: /netbsd-src/sys/arch/evbarm/rpi/rpi_vcmbox.c (revision fd9b697d9c834c84bcd0e32d1c314b466a1cd21a)
1 /* $NetBSD: rpi_vcmbox.c,v 1.8 2021/03/08 13:53:08 mlelstv Exp $ */
2 
3 /*-
4  * Copyright (c) 2013 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Raspberry Pi VC Mailbox Interface
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: rpi_vcmbox.c,v 1.8 2021/03/08 13:53:08 mlelstv Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/bus.h>
39 #include <sys/conf.h>
40 #include <sys/device.h>
41 #include <sys/endian.h>
42 #include <sys/kmem.h>
43 #include <sys/systm.h>
44 #include <sys/sysctl.h>
45 
46 #include <dev/sysmon/sysmonvar.h>
47 
48 #include <arm/broadcom/bcm2835_mbox.h>
49 
50 #include <evbarm/rpi/vcio.h>
51 #include <evbarm/rpi/vcprop.h>
52 
53 struct vcmbox_temp_request {
54 	struct vcprop_buffer_hdr	vb_hdr;
55 	struct vcprop_tag_temperature	vbt_temp;
56 	struct vcprop_tag end;
57 } __packed;
58 
59 struct vcmbox_clockrate_request {
60 	struct vcprop_buffer_hdr	vb_hdr;
61 	struct vcprop_tag_clockrate	vbt_clockrate;
62 	struct vcprop_tag end;
63 } __packed;
64 
65 #define RATE2MHZ(rate)	((rate) / 1000000)
66 #define MHZ2RATE(mhz)	((mhz) * 1000000)
67 
68 #define VCMBOX_INIT_REQUEST(r)		VCPROP_INIT_REQUEST(r)
69 #define VCMBOX_INIT_TAG(s, t)		VCPROP_INIT_TAG(s, t)
70 
71 struct vcmbox_softc {
72 	device_t		sc_dev;
73 
74 	/* temperature sensor */
75 	struct sysmon_envsys	*sc_sme;
76 #define VCMBOX_SENSOR_TEMP	0
77 #define VCMBOX_NSENSORS		1
78 	envsys_data_t		sc_sensor[VCMBOX_NSENSORS];
79 
80 	/* cpu frequency scaling */
81 	struct sysctllog	*sc_log;
82 	uint32_t		sc_cpu_minrate;
83 	uint32_t		sc_cpu_maxrate;
84 	int			sc_node_target;
85 	int			sc_node_current;
86 	int			sc_node_min;
87 	int			sc_node_max;
88 };
89 
90 static const char *vcmbox_sensor_name[VCMBOX_NSENSORS] = {
91 	"temperature",
92 };
93 
94 static int vcmbox_sensor_id[VCMBOX_NSENSORS] = {
95 	VCPROP_TEMP_SOC,
96 };
97 
98 static int	vcmbox_match(device_t, cfdata_t, void *);
99 static void	vcmbox_attach(device_t, device_t, void *);
100 
101 static int	vcmbox_read_temp(struct vcmbox_softc *, uint32_t, int,
102 				 uint32_t *);
103 static int	vcmbox_read_clockrate(struct vcmbox_softc *, uint32_t, int,
104 				 uint32_t *);
105 static int	vcmbox_write_clockrate(struct vcmbox_softc *, uint32_t, int,
106 				 uint32_t);
107 
108 static int	vcmbox_cpufreq_init(struct vcmbox_softc *);
109 static int	vcmbox_cpufreq_sysctl_helper(SYSCTLFN_PROTO);
110 
111 static void	vcmbox_create_sensors(struct vcmbox_softc *);
112 static void	vcmbox_sensor_get_limits(struct sysmon_envsys *,
113 					 envsys_data_t *,
114 					 sysmon_envsys_lim_t *, uint32_t *);
115 static void	vcmbox_sensor_refresh(struct sysmon_envsys *,
116 				      envsys_data_t *);
117 
118 CFATTACH_DECL_NEW(vcmbox, sizeof(struct vcmbox_softc),
119     vcmbox_match, vcmbox_attach, NULL, NULL);
120 
121 static int
vcmbox_match(device_t parent,cfdata_t match,void * aux)122 vcmbox_match(device_t parent, cfdata_t match, void *aux)
123 {
124 	return 1;
125 }
126 
127 static void
vcmbox_attach(device_t parent,device_t self,void * aux)128 vcmbox_attach(device_t parent, device_t self, void *aux)
129 {
130 	struct vcmbox_softc *sc = device_private(self);
131 
132 	sc->sc_dev = self;
133 
134 	aprint_naive("\n");
135 	aprint_normal("\n");
136 
137 	vcmbox_cpufreq_init(sc);
138 
139 	sc->sc_sme = sysmon_envsys_create();
140 	sc->sc_sme->sme_cookie = sc;
141 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
142 	sc->sc_sme->sme_refresh = vcmbox_sensor_refresh;
143 	sc->sc_sme->sme_get_limits = vcmbox_sensor_get_limits;
144 	vcmbox_create_sensors(sc);
145 	if (sysmon_envsys_register(sc->sc_sme) == 0)
146 		return;
147 
148 	aprint_error_dev(self, "unable to register with sysmon\n");
149 	sysmon_envsys_destroy(sc->sc_sme);
150 }
151 
152 static int
vcmbox_read_temp(struct vcmbox_softc * sc,uint32_t tag,int id,uint32_t * val)153 vcmbox_read_temp(struct vcmbox_softc *sc, uint32_t tag, int id, uint32_t *val)
154 {
155 	struct vcmbox_temp_request vb;
156 	uint32_t res;
157 	int error;
158 
159 	VCMBOX_INIT_REQUEST(vb);
160 	VCMBOX_INIT_TAG(vb.vbt_temp, tag);
161 	vb.vbt_temp.id = htole32(id);
162 	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
163 	if (error)
164 		return error;
165 	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
166 	    !vcprop_tag_success_p(&vb.vbt_temp.tag)) {
167 		return EIO;
168 	}
169 	*val = le32toh(vb.vbt_temp.value);
170 
171 	return 0;
172 }
173 
174 static int
vcmbox_read_clockrate(struct vcmbox_softc * sc,uint32_t tag,int id,uint32_t * val)175 vcmbox_read_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id,
176     uint32_t *val)
177 {
178 	struct vcmbox_clockrate_request vb;
179 	uint32_t res;
180 	int error;
181 
182 	VCMBOX_INIT_REQUEST(vb);
183 	VCMBOX_INIT_TAG(vb.vbt_clockrate, tag);
184 	vb.vbt_clockrate.id = htole32(id);
185 	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
186 	if (error)
187 		return error;
188 	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
189 	    !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) {
190 		return EIO;
191 	}
192 	*val = le32toh(vb.vbt_clockrate.rate);
193 
194 	return 0;
195 }
196 
197 static int
vcmbox_write_clockrate(struct vcmbox_softc * sc,uint32_t tag,int id,uint32_t val)198 vcmbox_write_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id,
199     uint32_t val)
200 {
201 	struct vcmbox_clockrate_request vb;
202 	uint32_t res;
203 	int error;
204 
205 	VCMBOX_INIT_REQUEST(vb);
206 	VCMBOX_INIT_TAG(vb.vbt_clockrate, tag);
207 	vb.vbt_clockrate.id = htole32(id);
208 	vb.vbt_clockrate.rate = htole32(val);
209 	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
210 	if (error)
211 		return error;
212 	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
213 	    !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) {
214 		return EIO;
215 	}
216 
217 	return 0;
218 }
219 
220 
221 static int
vcmbox_cpufreq_init(struct vcmbox_softc * sc)222 vcmbox_cpufreq_init(struct vcmbox_softc *sc)
223 {
224 	const struct sysctlnode *node, *cpunode, *freqnode;
225 	int error;
226 	static char available[20];
227 
228 	error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MIN_CLOCKRATE,
229 	    VCPROP_CLK_ARM, &sc->sc_cpu_minrate);
230 	if (error) {
231 		aprint_error_dev(sc->sc_dev, "couldn't read min clkrate (%d)\n",
232 		    error);
233 		return error;
234 	}
235 	error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MAX_CLOCKRATE,
236 	    VCPROP_CLK_ARM, &sc->sc_cpu_maxrate);
237 	if (error) {
238 		aprint_error_dev(sc->sc_dev, "couldn't read max clkrate (%d)\n",
239 		    error);
240 		return error;
241 	}
242 
243 	error = sysctl_createv(&sc->sc_log, 0, NULL, &node,
244 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
245 	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
246 	if (error)
247 		goto sysctl_failed;
248 	error = sysctl_createv(&sc->sc_log, 0, &node, &cpunode,
249 	    0, CTLTYPE_NODE, "cpu", NULL,
250 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
251 	if (error)
252 		goto sysctl_failed;
253 	error = sysctl_createv(&sc->sc_log, 0, &cpunode, &freqnode,
254 	    0, CTLTYPE_NODE, "frequency", NULL,
255 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
256 	if (error)
257 		goto sysctl_failed;
258 
259 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
260 	    CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
261 	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
262 	    CTL_CREATE, CTL_EOL);
263 	if (error)
264 		goto sysctl_failed;
265 	sc->sc_node_target = node->sysctl_num;
266 
267 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
268 	    0, CTLTYPE_INT, "current", NULL,
269 	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
270 	    CTL_CREATE, CTL_EOL);
271 	if (error)
272 		goto sysctl_failed;
273 	sc->sc_node_current = node->sysctl_num;
274 
275 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
276 	    0, CTLTYPE_INT, "min", NULL,
277 	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
278 	    CTL_CREATE, CTL_EOL);
279 	if (error)
280 		goto sysctl_failed;
281 	sc->sc_node_min = node->sysctl_num;
282 
283 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
284 	    0, CTLTYPE_INT, "max", NULL,
285 	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
286 	    CTL_CREATE, CTL_EOL);
287 	if (error)
288 		goto sysctl_failed;
289 	sc->sc_node_max = node->sysctl_num;
290 
291 	snprintf(available, sizeof(available), "%" PRIu32 " %" PRIu32,
292 	    RATE2MHZ(sc->sc_cpu_minrate), RATE2MHZ(sc->sc_cpu_maxrate));
293 
294 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
295 	    CTLFLAG_PERMANENT, CTLTYPE_STRING, "available", NULL,
296 	    NULL, 0, available, strlen(available),
297 	    CTL_CREATE, CTL_EOL);
298 	if (error)
299 		goto sysctl_failed;
300 
301 	return 0;
302 
303 sysctl_failed:
304 	aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes (%d)\n",
305 	    error);
306 	sysctl_teardown(&sc->sc_log);
307 	return error;
308 }
309 
310 static int
vcmbox_cpufreq_sysctl_helper(SYSCTLFN_ARGS)311 vcmbox_cpufreq_sysctl_helper(SYSCTLFN_ARGS)
312 {
313 	struct sysctlnode node;
314 	struct vcmbox_softc *sc;
315 	int fq, oldfq = 0, error;
316 	uint32_t rate;
317 
318 	node = *rnode;
319 	sc = node.sysctl_data;
320 
321 	node.sysctl_data = &fq;
322 
323 	if (rnode->sysctl_num == sc->sc_node_target ||
324 	    rnode->sysctl_num == sc->sc_node_current) {
325 		error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_CLOCKRATE,
326 		    VCPROP_CLK_ARM, &rate);
327 		if (error)
328 			return error;
329 		fq = RATE2MHZ(rate);
330 		if (rnode->sysctl_num == sc->sc_node_target)
331 			oldfq = fq;
332 	} else if (rnode->sysctl_num == sc->sc_node_min) {
333 		fq = RATE2MHZ(sc->sc_cpu_minrate);
334 	} else if (rnode->sysctl_num == sc->sc_node_max) {
335 		fq = RATE2MHZ(sc->sc_cpu_maxrate);
336 	} else
337 		return EOPNOTSUPP;
338 
339 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
340 	if (error || newp == NULL)
341 		return error;
342 
343 	if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target)
344 		return 0;
345 
346 	if (fq < RATE2MHZ(sc->sc_cpu_minrate))
347 		fq = RATE2MHZ(sc->sc_cpu_minrate);
348 	if (fq > RATE2MHZ(sc->sc_cpu_maxrate))
349 		fq = RATE2MHZ(sc->sc_cpu_maxrate);
350 
351 	return vcmbox_write_clockrate(sc, VCPROPTAG_SET_CLOCKRATE,
352 	    VCPROP_CLK_ARM, MHZ2RATE(fq));
353 }
354 
355 static void
vcmbox_create_sensors(struct vcmbox_softc * sc)356 vcmbox_create_sensors(struct vcmbox_softc *sc)
357 {
358 	uint32_t val;
359 
360 	sc->sc_sensor[VCMBOX_SENSOR_TEMP].sensor = VCMBOX_SENSOR_TEMP;
361 	sc->sc_sensor[VCMBOX_SENSOR_TEMP].units = ENVSYS_STEMP;
362 	sc->sc_sensor[VCMBOX_SENSOR_TEMP].state = ENVSYS_SINVALID;
363 	sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags = ENVSYS_FMONLIMITS |
364 						  ENVSYS_FHAS_ENTROPY;
365 	strlcpy(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc,
366 	    vcmbox_sensor_name[VCMBOX_SENSOR_TEMP],
367 	    sizeof(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc));
368 	if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE,
369 			     vcmbox_sensor_id[VCMBOX_SENSOR_TEMP], &val) == 0) {
370 		sc->sc_sensor[VCMBOX_SENSOR_TEMP].value_max =
371 		    val * 1000 + 273150000;
372 		sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags |= ENVSYS_FVALID_MAX;
373 	}
374 	sysmon_envsys_sensor_attach(sc->sc_sme,
375 	    &sc->sc_sensor[VCMBOX_SENSOR_TEMP]);
376 }
377 
378 static void
vcmbox_sensor_get_limits(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)379 vcmbox_sensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
380     sysmon_envsys_lim_t *limits, uint32_t *props)
381 {
382 	struct vcmbox_softc *sc = sme->sme_cookie;
383 	uint32_t val;
384 
385 	*props = 0;
386 
387 	if (edata->units == ENVSYS_STEMP) {
388 		if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE,
389 				     vcmbox_sensor_id[edata->sensor], &val))
390 			return;
391 		*props = PROP_CRITMAX;
392 		limits->sel_critmax = val * 1000 + 273150000;
393 	}
394 }
395 
396 static void
vcmbox_sensor_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)397 vcmbox_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
398 {
399 	struct vcmbox_softc *sc = sme->sme_cookie;
400 	uint32_t val;
401 
402 	edata->state = ENVSYS_SINVALID;
403 
404 	if (edata->units == ENVSYS_STEMP) {
405 		if (vcmbox_read_temp(sc, VCPROPTAG_GET_TEMPERATURE,
406 				     vcmbox_sensor_id[edata->sensor], &val))
407 			return;
408 
409 		edata->value_cur = val * 1000 + 273150000;
410 		edata->state = ENVSYS_SVALID;
411 	}
412 }
413