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