rpi_vcmbox.c revision 1.5 1 /* $NetBSD: rpi_vcmbox.c,v 1.5 2018/12/08 06:53:11 mlelstv Exp $ */
2
3 /*-
4 * Copyright (c) 2013 Jared D. McNeill <jmcneill (at) 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.5 2018/12/08 06:53:11 mlelstv 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 if (sysmon_envsys_register(sc->sc_sme) == 0)
156 return;
157
158 aprint_error_dev(self, "unable to register with sysmon\n");
159 sysmon_envsys_destroy(sc->sc_sme);
160 }
161
162 static int
163 vcmbox_read_temp(struct vcmbox_softc *sc, uint32_t tag, int id, uint32_t *val)
164 {
165 struct vcmbox_temp_request vb;
166 uint32_t res;
167 int error;
168
169 VCMBOX_INIT_REQUEST(vb);
170 VCMBOX_INIT_TAG(vb.vbt_temp, tag);
171 vb.vbt_temp.id = id;
172 error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
173 if (error)
174 return error;
175 if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
176 !vcprop_tag_success_p(&vb.vbt_temp.tag)) {
177 return EIO;
178 }
179 *val = vb.vbt_temp.value;
180
181 return 0;
182 }
183
184 static int
185 vcmbox_read_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id,
186 uint32_t *val)
187 {
188 struct vcmbox_clockrate_request vb;
189 uint32_t res;
190 int error;
191
192 VCMBOX_INIT_REQUEST(vb);
193 VCMBOX_INIT_TAG(vb.vbt_clockrate, tag);
194 vb.vbt_clockrate.id = id;
195 error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
196 if (error)
197 return error;
198 if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
199 !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) {
200 return EIO;
201 }
202 *val = vb.vbt_clockrate.rate;
203
204 return 0;
205 }
206
207 static int
208 vcmbox_write_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id,
209 uint32_t val)
210 {
211 struct vcmbox_clockrate_request vb;
212 uint32_t res;
213 int error;
214
215 VCMBOX_INIT_REQUEST(vb);
216 VCMBOX_INIT_TAG(vb.vbt_clockrate, tag);
217 vb.vbt_clockrate.id = id;
218 vb.vbt_clockrate.rate = val;
219 error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
220 if (error)
221 return error;
222 if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
223 !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) {
224 return EIO;
225 }
226
227 return 0;
228 }
229
230
231 static int
232 vcmbox_cpufreq_init(struct vcmbox_softc *sc)
233 {
234 const struct sysctlnode *node, *cpunode, *freqnode;
235 int error;
236 static char available[20];
237
238 error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MIN_CLOCKRATE,
239 VCPROP_CLK_ARM, &sc->sc_cpu_minrate);
240 if (error) {
241 aprint_error_dev(sc->sc_dev, "couldn't read min clkrate (%d)\n",
242 error);
243 return error;
244 }
245 error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MAX_CLOCKRATE,
246 VCPROP_CLK_ARM, &sc->sc_cpu_maxrate);
247 if (error) {
248 aprint_error_dev(sc->sc_dev, "couldn't read max clkrate (%d)\n",
249 error);
250 return error;
251 }
252
253 error = sysctl_createv(&sc->sc_log, 0, NULL, &node,
254 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
255 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
256 if (error)
257 goto sysctl_failed;
258 error = sysctl_createv(&sc->sc_log, 0, &node, &cpunode,
259 0, CTLTYPE_NODE, "cpu", NULL,
260 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
261 if (error)
262 goto sysctl_failed;
263 error = sysctl_createv(&sc->sc_log, 0, &cpunode, &freqnode,
264 0, CTLTYPE_NODE, "frequency", NULL,
265 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
266 if (error)
267 goto sysctl_failed;
268
269 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
270 CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
271 vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
272 CTL_CREATE, CTL_EOL);
273 if (error)
274 goto sysctl_failed;
275 sc->sc_node_target = node->sysctl_num;
276
277 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
278 0, CTLTYPE_INT, "current", NULL,
279 vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
280 CTL_CREATE, CTL_EOL);
281 if (error)
282 goto sysctl_failed;
283 sc->sc_node_current = node->sysctl_num;
284
285 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
286 0, CTLTYPE_INT, "min", NULL,
287 vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
288 CTL_CREATE, CTL_EOL);
289 if (error)
290 goto sysctl_failed;
291 sc->sc_node_min = node->sysctl_num;
292
293 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
294 0, CTLTYPE_INT, "max", NULL,
295 vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
296 CTL_CREATE, CTL_EOL);
297 if (error)
298 goto sysctl_failed;
299 sc->sc_node_max = node->sysctl_num;
300
301 snprintf(available, sizeof(available), "%" PRIu32 " %" PRIu32,
302 RATE2MHZ(sc->sc_cpu_minrate), RATE2MHZ(sc->sc_cpu_maxrate));
303
304 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
305 CTLFLAG_PERMANENT, CTLTYPE_STRING, "available", NULL,
306 NULL, 0, available, strlen(available),
307 CTL_CREATE, CTL_EOL);
308 if (error)
309 goto sysctl_failed;
310
311 return 0;
312
313 sysctl_failed:
314 aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes (%d)\n",
315 error);
316 sysctl_teardown(&sc->sc_log);
317 return error;
318 }
319
320 static int
321 vcmbox_cpufreq_sysctl_helper(SYSCTLFN_ARGS)
322 {
323 struct sysctlnode node;
324 struct vcmbox_softc *sc;
325 int fq, oldfq = 0, error;
326 uint32_t rate;
327
328 node = *rnode;
329 sc = node.sysctl_data;
330
331 node.sysctl_data = &fq;
332
333 if (rnode->sysctl_num == sc->sc_node_target ||
334 rnode->sysctl_num == sc->sc_node_current) {
335 error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_CLOCKRATE,
336 VCPROP_CLK_ARM, &rate);
337 if (error)
338 return error;
339 fq = RATE2MHZ(rate);
340 if (rnode->sysctl_num == sc->sc_node_target)
341 oldfq = fq;
342 } else if (rnode->sysctl_num == sc->sc_node_min) {
343 fq = RATE2MHZ(sc->sc_cpu_minrate);
344 } else if (rnode->sysctl_num == sc->sc_node_max) {
345 fq = RATE2MHZ(sc->sc_cpu_maxrate);
346 } else
347 return EOPNOTSUPP;
348
349 error = sysctl_lookup(SYSCTLFN_CALL(&node));
350 if (error || newp == NULL)
351 return error;
352
353 if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target)
354 return 0;
355
356 if (fq < RATE2MHZ(sc->sc_cpu_minrate))
357 fq = RATE2MHZ(sc->sc_cpu_minrate);
358 if (fq > RATE2MHZ(sc->sc_cpu_maxrate))
359 fq = RATE2MHZ(sc->sc_cpu_maxrate);
360
361 return vcmbox_write_clockrate(sc, VCPROPTAG_SET_CLOCKRATE,
362 VCPROP_CLK_ARM, MHZ2RATE(fq));
363 }
364
365 static void
366 vcmbox_create_sensors(struct vcmbox_softc *sc)
367 {
368 uint32_t val;
369
370 sc->sc_sensor[VCMBOX_SENSOR_TEMP].sensor = VCMBOX_SENSOR_TEMP;
371 sc->sc_sensor[VCMBOX_SENSOR_TEMP].units = ENVSYS_STEMP;
372 sc->sc_sensor[VCMBOX_SENSOR_TEMP].state = ENVSYS_SINVALID;
373 sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags = ENVSYS_FMONLIMITS |
374 ENVSYS_FHAS_ENTROPY;
375 strlcpy(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc,
376 vcmbox_sensor_name[VCMBOX_SENSOR_TEMP],
377 sizeof(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc));
378 if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE,
379 vcmbox_sensor_id[VCMBOX_SENSOR_TEMP], &val) == 0) {
380 sc->sc_sensor[VCMBOX_SENSOR_TEMP].value_max =
381 val * 1000 + 273150000;
382 sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags |= ENVSYS_FVALID_MAX;
383 }
384 sysmon_envsys_sensor_attach(sc->sc_sme,
385 &sc->sc_sensor[VCMBOX_SENSOR_TEMP]);
386 }
387
388 static void
389 vcmbox_sensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
390 sysmon_envsys_lim_t *limits, uint32_t *props)
391 {
392 struct vcmbox_softc *sc = sme->sme_cookie;
393 uint32_t val;
394
395 *props = 0;
396
397 if (edata->units == ENVSYS_STEMP) {
398 if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE,
399 vcmbox_sensor_id[edata->sensor], &val))
400 return;
401 *props = PROP_CRITMAX;
402 limits->sel_critmax = val * 1000 + 273150000;
403 }
404 }
405
406 static void
407 vcmbox_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
408 {
409 struct vcmbox_softc *sc = sme->sme_cookie;
410 uint32_t val;
411
412 edata->state = ENVSYS_SINVALID;
413
414 if (edata->units == ENVSYS_STEMP) {
415 if (vcmbox_read_temp(sc, VCPROPTAG_GET_TEMPERATURE,
416 vcmbox_sensor_id[edata->sensor], &val))
417 return;
418
419 edata->value_cur = val * 1000 + 273150000;
420 edata->state = ENVSYS_SVALID;
421 }
422 }
423