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