1 /* $NetBSD: rpi_vcmbox.c,v 1.4 2014/10/04 13:18:34 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.4 2014/10/04 13:18:34 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 237 error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MIN_CLOCKRATE, 238 VCPROP_CLK_ARM, &sc->sc_cpu_minrate); 239 if (error) { 240 aprint_error_dev(sc->sc_dev, "couldn't read min clkrate (%d)\n", 241 error); 242 return error; 243 } 244 error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MAX_CLOCKRATE, 245 VCPROP_CLK_ARM, &sc->sc_cpu_maxrate); 246 if (error) { 247 aprint_error_dev(sc->sc_dev, "couldn't read max clkrate (%d)\n", 248 error); 249 return error; 250 } 251 252 error = sysctl_createv(&sc->sc_log, 0, NULL, &node, 253 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, 254 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); 255 if (error) 256 goto sysctl_failed; 257 error = sysctl_createv(&sc->sc_log, 0, &node, &cpunode, 258 0, CTLTYPE_NODE, "cpu", NULL, 259 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 260 if (error) 261 goto sysctl_failed; 262 error = sysctl_createv(&sc->sc_log, 0, &cpunode, &freqnode, 263 0, CTLTYPE_NODE, "frequency", NULL, 264 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 265 if (error) 266 goto sysctl_failed; 267 268 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, 269 CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL, 270 vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0, 271 CTL_CREATE, CTL_EOL); 272 if (error) 273 goto sysctl_failed; 274 sc->sc_node_target = node->sysctl_num; 275 276 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, 277 0, CTLTYPE_INT, "current", NULL, 278 vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0, 279 CTL_CREATE, CTL_EOL); 280 if (error) 281 goto sysctl_failed; 282 sc->sc_node_current = node->sysctl_num; 283 284 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, 285 0, CTLTYPE_INT, "min", NULL, 286 vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0, 287 CTL_CREATE, CTL_EOL); 288 if (error) 289 goto sysctl_failed; 290 sc->sc_node_min = node->sysctl_num; 291 292 error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, 293 0, CTLTYPE_INT, "max", NULL, 294 vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0, 295 CTL_CREATE, CTL_EOL); 296 if (error) 297 goto sysctl_failed; 298 sc->sc_node_max = node->sysctl_num; 299 300 return 0; 301 302 sysctl_failed: 303 aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes (%d)\n", 304 error); 305 sysctl_teardown(&sc->sc_log); 306 return error; 307 } 308 309 static int 310 vcmbox_cpufreq_sysctl_helper(SYSCTLFN_ARGS) 311 { 312 struct sysctlnode node; 313 struct vcmbox_softc *sc; 314 int fq, oldfq = 0, error; 315 uint32_t rate; 316 317 node = *rnode; 318 sc = node.sysctl_data; 319 320 node.sysctl_data = &fq; 321 322 if (rnode->sysctl_num == sc->sc_node_target || 323 rnode->sysctl_num == sc->sc_node_current) { 324 error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_CLOCKRATE, 325 VCPROP_CLK_ARM, &rate); 326 if (error) 327 return error; 328 fq = RATE2MHZ(rate); 329 if (rnode->sysctl_num == sc->sc_node_target) 330 oldfq = fq; 331 } else if (rnode->sysctl_num == sc->sc_node_min) { 332 fq = RATE2MHZ(sc->sc_cpu_minrate); 333 } else if (rnode->sysctl_num == sc->sc_node_max) { 334 fq = RATE2MHZ(sc->sc_cpu_maxrate); 335 } else 336 return EOPNOTSUPP; 337 338 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 339 if (error || newp == NULL) 340 return error; 341 342 if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target) 343 return 0; 344 345 if (fq < RATE2MHZ(sc->sc_cpu_minrate)) 346 fq = RATE2MHZ(sc->sc_cpu_minrate); 347 if (fq > RATE2MHZ(sc->sc_cpu_maxrate)) 348 fq = RATE2MHZ(sc->sc_cpu_maxrate); 349 350 return vcmbox_write_clockrate(sc, VCPROPTAG_SET_CLOCKRATE, 351 VCPROP_CLK_ARM, MHZ2RATE(fq)); 352 } 353 354 static void 355 vcmbox_create_sensors(struct vcmbox_softc *sc) 356 { 357 uint32_t val; 358 359 sc->sc_sensor[VCMBOX_SENSOR_TEMP].sensor = VCMBOX_SENSOR_TEMP; 360 sc->sc_sensor[VCMBOX_SENSOR_TEMP].units = ENVSYS_STEMP; 361 sc->sc_sensor[VCMBOX_SENSOR_TEMP].state = ENVSYS_SINVALID; 362 sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags = ENVSYS_FMONLIMITS | 363 ENVSYS_FHAS_ENTROPY; 364 strlcpy(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc, 365 vcmbox_sensor_name[VCMBOX_SENSOR_TEMP], 366 sizeof(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc)); 367 if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE, 368 vcmbox_sensor_id[VCMBOX_SENSOR_TEMP], &val) == 0) { 369 sc->sc_sensor[VCMBOX_SENSOR_TEMP].value_max = 370 val * 1000 + 273150000; 371 sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags |= ENVSYS_FVALID_MAX; 372 } 373 sysmon_envsys_sensor_attach(sc->sc_sme, 374 &sc->sc_sensor[VCMBOX_SENSOR_TEMP]); 375 } 376 377 static void 378 vcmbox_sensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 379 sysmon_envsys_lim_t *limits, uint32_t *props) 380 { 381 struct vcmbox_softc *sc = sme->sme_cookie; 382 uint32_t val; 383 384 *props = 0; 385 386 if (edata->units == ENVSYS_STEMP) { 387 if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE, 388 vcmbox_sensor_id[edata->sensor], &val)) 389 return; 390 *props = PROP_CRITMAX; 391 limits->sel_critmax = val * 1000 + 273150000; 392 } 393 } 394 395 static void 396 vcmbox_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 397 { 398 struct vcmbox_softc *sc = sme->sme_cookie; 399 uint32_t val; 400 401 edata->state = ENVSYS_SINVALID; 402 403 if (edata->units == ENVSYS_STEMP) { 404 if (vcmbox_read_temp(sc, VCPROPTAG_GET_TEMPERATURE, 405 vcmbox_sensor_id[edata->sensor], &val)) 406 return; 407 408 edata->value_cur = val * 1000 + 273150000; 409 edata->state = ENVSYS_SVALID; 410 } 411 } 412