xref: /netbsd-src/sys/arch/evbarm/rpi/rpi_vcmbox.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /* $NetBSD: rpi_vcmbox.c,v 1.3 2014/04/01 06:55:29 skrll 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.3 2014/04/01 06:55:29 skrll Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 #include <sys/conf.h>
41 #include <sys/bus.h>
42 #include <sys/kmem.h>
43 #include <sys/sysctl.h>
44 
45 #include <dev/sysmon/sysmonvar.h>
46 
47 #include <arm/broadcom/bcm2835_mbox.h>
48 
49 #include <evbarm/rpi/vcio.h>
50 #include <evbarm/rpi/vcprop.h>
51 
52 struct vcmbox_temp_request {
53 	struct vcprop_buffer_hdr	vb_hdr;
54 	struct vcprop_tag_temperature	vbt_temp;
55 	struct vcprop_tag end;
56 } __packed;
57 
58 struct vcmbox_clockrate_request {
59 	struct vcprop_buffer_hdr	vb_hdr;
60 	struct vcprop_tag_clockrate	vbt_clockrate;
61 	struct vcprop_tag end;
62 } __packed;
63 
64 #define RATE2MHZ(rate)	((rate) / 1000000)
65 #define MHZ2RATE(mhz)	((mhz) * 1000000)
66 
67 #define VCMBOX_INIT_REQUEST(req)					\
68 	do {								\
69 		memset(&(req), 0, sizeof((req)));			\
70 		(req).vb_hdr.vpb_len = sizeof((req));			\
71 		(req).vb_hdr.vpb_rcode = VCPROP_PROCESS_REQUEST;	\
72 		(req).end.vpt_tag = VCPROPTAG_NULL;			\
73 	} while (0)
74 #define VCMBOX_INIT_TAG(s, t)						\
75 	do {								\
76 		(s).tag.vpt_tag = (t);					\
77 		(s).tag.vpt_rcode = VCPROPTAG_REQUEST;			\
78 		(s).tag.vpt_len = VCPROPTAG_LEN(s);			\
79 	} while (0)
80 
81 struct vcmbox_softc {
82 	device_t		sc_dev;
83 
84 	/* temperature sensor */
85 	struct sysmon_envsys	*sc_sme;
86 #define VCMBOX_SENSOR_TEMP	0
87 #define VCMBOX_NSENSORS		1
88 	envsys_data_t		sc_sensor[VCMBOX_NSENSORS];
89 
90 	/* cpu frequency scaling */
91 	struct sysctllog	*sc_log;
92 	uint32_t		sc_cpu_minrate;
93 	uint32_t		sc_cpu_maxrate;
94 	int			sc_node_target;
95 	int			sc_node_current;
96 	int			sc_node_min;
97 	int			sc_node_max;
98 };
99 
100 static const char *vcmbox_sensor_name[VCMBOX_NSENSORS] = {
101 	"temperature",
102 };
103 
104 static int vcmbox_sensor_id[VCMBOX_NSENSORS] = {
105 	VCPROP_TEMP_SOC,
106 };
107 
108 static int	vcmbox_match(device_t, cfdata_t, void *);
109 static void	vcmbox_attach(device_t, device_t, void *);
110 
111 static int	vcmbox_read_temp(struct vcmbox_softc *, uint32_t, int,
112 				 uint32_t *);
113 static int	vcmbox_read_clockrate(struct vcmbox_softc *, uint32_t, int,
114 				 uint32_t *);
115 static int	vcmbox_write_clockrate(struct vcmbox_softc *, uint32_t, int,
116 				 uint32_t);
117 
118 static int	vcmbox_cpufreq_init(struct vcmbox_softc *);
119 static int	vcmbox_cpufreq_sysctl_helper(SYSCTLFN_PROTO);
120 
121 static void	vcmbox_create_sensors(struct vcmbox_softc *);
122 static void	vcmbox_sensor_get_limits(struct sysmon_envsys *,
123 					 envsys_data_t *,
124 					 sysmon_envsys_lim_t *, uint32_t *);
125 static void	vcmbox_sensor_refresh(struct sysmon_envsys *,
126 				      envsys_data_t *);
127 
128 CFATTACH_DECL_NEW(vcmbox, sizeof(struct vcmbox_softc),
129     vcmbox_match, vcmbox_attach, NULL, NULL);
130 
131 static int
132 vcmbox_match(device_t parent, cfdata_t match, void *aux)
133 {
134 	return 1;
135 }
136 
137 static void
138 vcmbox_attach(device_t parent, device_t self, void *aux)
139 {
140 	struct vcmbox_softc *sc = device_private(self);
141 
142 	sc->sc_dev = self;
143 
144 	aprint_naive("\n");
145 	aprint_normal("\n");
146 
147 	vcmbox_cpufreq_init(sc);
148 
149 	sc->sc_sme = sysmon_envsys_create();
150 	sc->sc_sme->sme_cookie = sc;
151 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
152 	sc->sc_sme->sme_refresh = vcmbox_sensor_refresh;
153 	sc->sc_sme->sme_get_limits = vcmbox_sensor_get_limits;
154 	vcmbox_create_sensors(sc);
155 	sysmon_envsys_register(sc->sc_sme);
156 }
157 
158 static int
159 vcmbox_read_temp(struct vcmbox_softc *sc, uint32_t tag, int id, uint32_t *val)
160 {
161 	struct vcmbox_temp_request vb;
162 	uint32_t res;
163 	int error;
164 
165 	VCMBOX_INIT_REQUEST(vb);
166 	VCMBOX_INIT_TAG(vb.vbt_temp, tag);
167 	vb.vbt_temp.id = id;
168 	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
169 	if (error)
170 		return error;
171 	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
172 	    !vcprop_tag_success_p(&vb.vbt_temp.tag)) {
173 		return EIO;
174 	}
175 	*val = vb.vbt_temp.value;
176 
177 	return 0;
178 }
179 
180 static int
181 vcmbox_read_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id,
182     uint32_t *val)
183 {
184 	struct vcmbox_clockrate_request vb;
185 	uint32_t res;
186 	int error;
187 
188 	VCMBOX_INIT_REQUEST(vb);
189 	VCMBOX_INIT_TAG(vb.vbt_clockrate, tag);
190 	vb.vbt_clockrate.id = id;
191 	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
192 	if (error)
193 		return error;
194 	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
195 	    !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) {
196 		return EIO;
197 	}
198 	*val = vb.vbt_clockrate.rate;
199 
200 	return 0;
201 }
202 
203 static int
204 vcmbox_write_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id,
205     uint32_t val)
206 {
207 	struct vcmbox_clockrate_request vb;
208 	uint32_t res;
209 	int error;
210 
211 	VCMBOX_INIT_REQUEST(vb);
212 	VCMBOX_INIT_TAG(vb.vbt_clockrate, tag);
213 	vb.vbt_clockrate.id = id;
214 	vb.vbt_clockrate.rate = val;
215 	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
216 	if (error)
217 		return error;
218 	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
219 	    !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) {
220 		return EIO;
221 	}
222 
223 	return 0;
224 }
225 
226 
227 static int
228 vcmbox_cpufreq_init(struct vcmbox_softc *sc)
229 {
230 	const struct sysctlnode *node, *cpunode, *freqnode;
231 	int error;
232 
233 	error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MIN_CLOCKRATE,
234 	    VCPROP_CLK_ARM, &sc->sc_cpu_minrate);
235 	if (error) {
236 		aprint_error_dev(sc->sc_dev, "couldn't read min clkrate (%d)\n",
237 		    error);
238 		return error;
239 	}
240 	error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MAX_CLOCKRATE,
241 	    VCPROP_CLK_ARM, &sc->sc_cpu_maxrate);
242 	if (error) {
243 		aprint_error_dev(sc->sc_dev, "couldn't read max clkrate (%d)\n",
244 		    error);
245 		return error;
246 	}
247 
248 	error = sysctl_createv(&sc->sc_log, 0, NULL, &node,
249 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
250 	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
251 	if (error)
252 		goto sysctl_failed;
253 	error = sysctl_createv(&sc->sc_log, 0, &node, &cpunode,
254 	    0, CTLTYPE_NODE, "cpu", NULL,
255 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
256 	if (error)
257 		goto sysctl_failed;
258 	error = sysctl_createv(&sc->sc_log, 0, &cpunode, &freqnode,
259 	    0, CTLTYPE_NODE, "frequency", NULL,
260 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
261 	if (error)
262 		goto sysctl_failed;
263 
264 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
265 	    CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
266 	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
267 	    CTL_CREATE, CTL_EOL);
268 	if (error)
269 		goto sysctl_failed;
270 	sc->sc_node_target = node->sysctl_num;
271 
272 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
273 	    0, CTLTYPE_INT, "current", NULL,
274 	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
275 	    CTL_CREATE, CTL_EOL);
276 	if (error)
277 		goto sysctl_failed;
278 	sc->sc_node_current = node->sysctl_num;
279 
280 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
281 	    0, CTLTYPE_INT, "min", NULL,
282 	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
283 	    CTL_CREATE, CTL_EOL);
284 	if (error)
285 		goto sysctl_failed;
286 	sc->sc_node_min = node->sysctl_num;
287 
288 	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
289 	    0, CTLTYPE_INT, "max", NULL,
290 	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
291 	    CTL_CREATE, CTL_EOL);
292 	if (error)
293 		goto sysctl_failed;
294 	sc->sc_node_max = node->sysctl_num;
295 
296 	return 0;
297 
298 sysctl_failed:
299 	aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes (%d)\n",
300 	    error);
301 	sysctl_teardown(&sc->sc_log);
302 	return error;
303 }
304 
305 static int
306 vcmbox_cpufreq_sysctl_helper(SYSCTLFN_ARGS)
307 {
308 	struct sysctlnode node;
309 	struct vcmbox_softc *sc;
310 	int fq, oldfq = 0, error;
311 	uint32_t rate;
312 
313 	node = *rnode;
314 	sc = node.sysctl_data;
315 
316 	node.sysctl_data = &fq;
317 
318 	if (rnode->sysctl_num == sc->sc_node_target ||
319 	    rnode->sysctl_num == sc->sc_node_current) {
320 		error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_CLOCKRATE,
321 		    VCPROP_CLK_ARM, &rate);
322 		if (error)
323 			return error;
324 		fq = RATE2MHZ(rate);
325 		if (rnode->sysctl_num == sc->sc_node_target)
326 			oldfq = fq;
327 	} else if (rnode->sysctl_num == sc->sc_node_min) {
328 		fq = RATE2MHZ(sc->sc_cpu_minrate);
329 	} else if (rnode->sysctl_num == sc->sc_node_max) {
330 		fq = RATE2MHZ(sc->sc_cpu_maxrate);
331 	} else
332 		return EOPNOTSUPP;
333 
334 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
335 	if (error || newp == NULL)
336 		return error;
337 
338 	if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target)
339 		return 0;
340 
341 	if (fq < RATE2MHZ(sc->sc_cpu_minrate))
342 		fq = RATE2MHZ(sc->sc_cpu_minrate);
343 	if (fq > RATE2MHZ(sc->sc_cpu_maxrate))
344 		fq = RATE2MHZ(sc->sc_cpu_maxrate);
345 
346 	return vcmbox_write_clockrate(sc, VCPROPTAG_SET_CLOCKRATE,
347 	    VCPROP_CLK_ARM, MHZ2RATE(fq));
348 }
349 
350 static void
351 vcmbox_create_sensors(struct vcmbox_softc *sc)
352 {
353 	uint32_t val;
354 
355 	sc->sc_sensor[VCMBOX_SENSOR_TEMP].sensor = VCMBOX_SENSOR_TEMP;
356 	sc->sc_sensor[VCMBOX_SENSOR_TEMP].units = ENVSYS_STEMP;
357 	sc->sc_sensor[VCMBOX_SENSOR_TEMP].state = ENVSYS_SINVALID;
358 	sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags = ENVSYS_FHAS_ENTROPY;
359 	strlcpy(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc,
360 	    vcmbox_sensor_name[VCMBOX_SENSOR_TEMP],
361 	    sizeof(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc));
362 	if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE,
363 			     vcmbox_sensor_id[VCMBOX_SENSOR_TEMP], &val) == 0) {
364 		sc->sc_sensor[VCMBOX_SENSOR_TEMP].value_max =
365 		    val * 1000 + 273150000;
366 		sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags |= ENVSYS_FVALID_MAX;
367 	}
368 	sysmon_envsys_sensor_attach(sc->sc_sme,
369 	    &sc->sc_sensor[VCMBOX_SENSOR_TEMP]);
370 }
371 
372 static void
373 vcmbox_sensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
374     sysmon_envsys_lim_t *limits, uint32_t *props)
375 {
376 	struct vcmbox_softc *sc = sme->sme_cookie;
377 	uint32_t val;
378 
379 	*props = 0;
380 
381 	if (edata->units == ENVSYS_STEMP) {
382 		if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE,
383 				     vcmbox_sensor_id[edata->sensor], &val))
384 			return;
385 		*props = PROP_CRITMAX;
386 		limits->sel_critmax = val * 1000 + 273150000;
387 	}
388 }
389 
390 static void
391 vcmbox_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
392 {
393 	struct vcmbox_softc *sc = sme->sme_cookie;
394 	uint32_t val;
395 
396 	edata->state = ENVSYS_SINVALID;
397 
398 	if (edata->units == ENVSYS_STEMP) {
399 		if (vcmbox_read_temp(sc, VCPROPTAG_GET_TEMPERATURE,
400 				     vcmbox_sensor_id[edata->sensor], &val))
401 			return;
402 
403 		edata->value_cur = val * 1000 + 273150000;
404 		edata->state = ENVSYS_SVALID;
405 	}
406 }
407