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 53struct 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 59struct 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 71struct 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 90static const char *vcmbox_sensor_name[VCMBOX_NSENSORS] = { 91 "temperature", 92}; 93 94static int vcmbox_sensor_id[VCMBOX_NSENSORS] = { 95 VCPROP_TEMP_SOC, 96}; 97 98static int vcmbox_match(device_t, cfdata_t, void *); 99static void vcmbox_attach(device_t, device_t, void *); 100 101static int vcmbox_read_temp(struct vcmbox_softc *, uint32_t, int, 102 uint32_t *); 103static int vcmbox_read_clockrate(struct vcmbox_softc *, uint32_t, int, 104 uint32_t *); 105static int vcmbox_write_clockrate(struct vcmbox_softc *, uint32_t, int, 106 uint32_t); 107 108static int vcmbox_cpufreq_init(struct vcmbox_softc *); 109static int vcmbox_cpufreq_sysctl_helper(SYSCTLFN_PROTO); 110 111static void vcmbox_create_sensors(struct vcmbox_softc *); 112static void vcmbox_sensor_get_limits(struct sysmon_envsys *, 113 envsys_data_t *, 114 sysmon_envsys_lim_t *, uint32_t *); 115static void vcmbox_sensor_refresh(struct sysmon_envsys *, 116 envsys_data_t *); 117 118CFATTACH_DECL_NEW(vcmbox, sizeof(struct vcmbox_softc), 119 vcmbox_match, vcmbox_attach, NULL, NULL); 120 121static int 122vcmbox_match(device_t parent, cfdata_t match, void *aux) 123{ 124 return 1; 125} 126 127static void 128vcmbox_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 152static int 153vcmbox_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 174static int 175vcmbox_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 197static int 198vcmbox_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 221static int 222vcmbox_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 303sysctl_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 310static int 311vcmbox_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 355static void 356vcmbox_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 378static void 379vcmbox_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 396static void 397vcmbox_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