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@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