1275963Srpaulo/*- 2275963Srpaulo * Copyright (C) 2013-2014 Daisuke Aoyama <aoyama@peach.ne.jp> 3275963Srpaulo * All rights reserved. 4275963Srpaulo * 5275963Srpaulo * Redistribution and use in source and binary forms, with or without 6275963Srpaulo * modification, are permitted provided that the following conditions 7275963Srpaulo * are met: 8275963Srpaulo * 1. Redistributions of source code must retain the above copyright 9275963Srpaulo * notice, this list of conditions and the following disclaimer. 10275963Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11275963Srpaulo * notice, this list of conditions and the following disclaimer in the 12275963Srpaulo * documentation and/or other materials provided with the distribution. 13275963Srpaulo * 14275963Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15275963Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16275963Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17275963Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18275963Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19275963Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20275963Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21275963Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22275963Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23275963Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24275963Srpaulo * SUCH DAMAGE. 25275963Srpaulo * 26275963Srpaulo */ 27275963Srpaulo 28275963Srpaulo#include <sys/cdefs.h> 29275963Srpaulo__FBSDID("$FreeBSD: stable/10/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c 322724 2017-08-20 16:52:27Z marius $"); 30275963Srpaulo 31275963Srpaulo#include <sys/param.h> 32275963Srpaulo#include <sys/systm.h> 33275963Srpaulo#include <sys/bus.h> 34275963Srpaulo#include <sys/cpu.h> 35275963Srpaulo#include <sys/kernel.h> 36275963Srpaulo#include <sys/lock.h> 37275963Srpaulo#include <sys/malloc.h> 38275963Srpaulo#include <sys/module.h> 39275963Srpaulo#include <sys/mutex.h> 40275963Srpaulo#include <sys/sema.h> 41275963Srpaulo#include <sys/sysctl.h> 42275963Srpaulo 43275963Srpaulo#include <machine/bus.h> 44275963Srpaulo#include <machine/cpu.h> 45275963Srpaulo#include <machine/intr.h> 46275963Srpaulo 47322724Smarius#include <dev/fdt/fdt_common.h> 48322724Smarius 49322724Smarius#include <dev/ofw/ofw_bus.h> 50322724Smarius#include <dev/ofw/ofw_bus_subr.h> 51322724Smarius 52275963Srpaulo#include <arm/broadcom/bcm2835/bcm2835_mbox.h> 53275963Srpaulo#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h> 54275963Srpaulo#include <arm/broadcom/bcm2835/bcm2835_vcbus.h> 55275963Srpaulo 56275963Srpaulo#include "cpufreq_if.h" 57275963Srpaulo#include "mbox_if.h" 58275963Srpaulo 59275963Srpaulo#ifdef DEBUG 60275963Srpaulo#define DPRINTF(fmt, ...) do { \ 61275963Srpaulo printf("%s:%u: ", __func__, __LINE__); \ 62275963Srpaulo printf(fmt, ##__VA_ARGS__); \ 63275963Srpaulo} while (0) 64275963Srpaulo#else 65275963Srpaulo#define DPRINTF(fmt, ...) 66275963Srpaulo#endif 67275963Srpaulo 68275963Srpaulo#define HZ2MHZ(freq) ((freq) / (1000 * 1000)) 69275963Srpaulo#define MHZ2HZ(freq) ((freq) * (1000 * 1000)) 70275963Srpaulo#define OFFSET2MVOLT(val) (1200 + ((val) * 25)) 71275963Srpaulo#define MVOLT2OFFSET(val) (((val) - 1200) / 25) 72275963Srpaulo 73275963Srpaulo#define DEFAULT_ARM_FREQUENCY 700 74275963Srpaulo#define DEFAULT_CORE_FREQUENCY 250 75275963Srpaulo#define DEFAULT_SDRAM_FREQUENCY 400 76275963Srpaulo#define DEFAULT_LOWEST_FREQ 300 77275963Srpaulo#define TRANSITION_LATENCY 1000 78275963Srpaulo#define MIN_OVER_VOLTAGE -16 79275963Srpaulo#define MAX_OVER_VOLTAGE 6 80275963Srpaulo#define MSG_ERROR -999999999 81275963Srpaulo#define MHZSTEP 100 82275963Srpaulo#define HZSTEP (MHZ2HZ(MHZSTEP)) 83278768Sloos#define TZ_ZEROC 2732 84275963Srpaulo 85275963Srpaulo#define VC_LOCK(sc) do { \ 86275963Srpaulo sema_wait(&vc_sema); \ 87275963Srpaulo } while (0) 88275963Srpaulo#define VC_UNLOCK(sc) do { \ 89275963Srpaulo sema_post(&vc_sema); \ 90275963Srpaulo } while (0) 91275963Srpaulo 92275963Srpaulo/* ARM->VC mailbox property semaphore */ 93275963Srpaulostatic struct sema vc_sema; 94275963Srpaulo 95275963Srpaulostatic struct sysctl_ctx_list bcm2835_sysctl_ctx; 96275963Srpaulo 97275963Srpaulostruct bcm2835_cpufreq_softc { 98275963Srpaulo device_t dev; 99275963Srpaulo int arm_max_freq; 100275963Srpaulo int arm_min_freq; 101275963Srpaulo int core_max_freq; 102275963Srpaulo int core_min_freq; 103275963Srpaulo int sdram_max_freq; 104275963Srpaulo int sdram_min_freq; 105275963Srpaulo int max_voltage_core; 106275963Srpaulo int min_voltage_core; 107275963Srpaulo 108275963Srpaulo /* the values written in mbox */ 109275963Srpaulo int voltage_core; 110275963Srpaulo int voltage_sdram; 111275963Srpaulo int voltage_sdram_c; 112275963Srpaulo int voltage_sdram_i; 113275963Srpaulo int voltage_sdram_p; 114275963Srpaulo int turbo_mode; 115275963Srpaulo 116275963Srpaulo /* initial hook for waiting mbox intr */ 117275963Srpaulo struct intr_config_hook init_hook; 118275963Srpaulo}; 119275963Srpaulo 120322724Smariusstatic struct ofw_compat_data compat_data[] = { 121322724Smarius { "broadcom,bcm2835-vc", 1 }, 122322724Smarius { "broadcom,bcm2708-vc", 1 }, 123322724Smarius { "brcm,bcm2709", 1 }, 124322724Smarius { NULL, 0 } 125322724Smarius}; 126322724Smarius 127275963Srpaulostatic int cpufreq_verbose = 0; 128275963SrpauloTUNABLE_INT("hw.bcm2835.cpufreq.verbose", &cpufreq_verbose); 129275963Srpaulostatic int cpufreq_lowest_freq = DEFAULT_LOWEST_FREQ; 130275963SrpauloTUNABLE_INT("hw.bcm2835.cpufreq.lowest_freq", &cpufreq_lowest_freq); 131275963Srpaulo 132278768Sloos#ifdef PROP_DEBUG 133275963Srpaulostatic void 134275963Srpaulobcm2835_dump(const void *data, int len) 135275963Srpaulo{ 136275963Srpaulo const uint8_t *p = (const uint8_t*)data; 137275963Srpaulo int i; 138275963Srpaulo 139275963Srpaulo printf("dump @ %p:\n", data); 140275963Srpaulo for (i = 0; i < len; i++) { 141275963Srpaulo printf("%2.2x ", p[i]); 142275963Srpaulo if ((i % 4) == 3) 143275963Srpaulo printf(" "); 144275963Srpaulo if ((i % 16) == 15) 145275963Srpaulo printf("\n"); 146275963Srpaulo } 147275963Srpaulo printf("\n"); 148275963Srpaulo} 149275963Srpaulo#endif 150275963Srpaulo 151275963Srpaulostatic int 152275963Srpaulobcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc *sc, 153275963Srpaulo uint32_t clock_id) 154275963Srpaulo{ 155322724Smarius struct msg_get_clock_rate msg; 156275963Srpaulo int rate; 157275963Srpaulo int err; 158275963Srpaulo 159275963Srpaulo /* 160275963Srpaulo * Get clock rate 161275963Srpaulo * Tag: 0x00030002 162275963Srpaulo * Request: 163275963Srpaulo * Length: 4 164275963Srpaulo * Value: 165275963Srpaulo * u32: clock id 166275963Srpaulo * Response: 167275963Srpaulo * Length: 8 168275963Srpaulo * Value: 169275963Srpaulo * u32: clock id 170275963Srpaulo * u32: rate (in Hz) 171275963Srpaulo */ 172275963Srpaulo 173275963Srpaulo /* setup single tag buffer */ 174322724Smarius memset(&msg, 0, sizeof(msg)); 175322724Smarius msg.hdr.buf_size = sizeof(msg); 176322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 177322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; 178322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 179322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 180322724Smarius msg.body.req.clock_id = clock_id; 181322724Smarius msg.end_tag = 0; 182275963Srpaulo 183275963Srpaulo /* call mailbox property */ 184322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 185275963Srpaulo if (err) { 186275963Srpaulo device_printf(sc->dev, "can't get clock rate (id=%u)\n", 187275963Srpaulo clock_id); 188275963Srpaulo return (MSG_ERROR); 189275963Srpaulo } 190275963Srpaulo 191275963Srpaulo /* result (Hz) */ 192322724Smarius rate = (int)msg.body.resp.rate_hz; 193275963Srpaulo DPRINTF("clock = %d(Hz)\n", rate); 194275963Srpaulo return (rate); 195275963Srpaulo} 196275963Srpaulo 197275963Srpaulostatic int 198275963Srpaulobcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc *sc, 199275963Srpaulo uint32_t clock_id) 200275963Srpaulo{ 201322724Smarius struct msg_get_max_clock_rate msg; 202275963Srpaulo int rate; 203275963Srpaulo int err; 204275963Srpaulo 205275963Srpaulo /* 206275963Srpaulo * Get max clock rate 207275963Srpaulo * Tag: 0x00030004 208275963Srpaulo * Request: 209275963Srpaulo * Length: 4 210275963Srpaulo * Value: 211275963Srpaulo * u32: clock id 212275963Srpaulo * Response: 213275963Srpaulo * Length: 8 214275963Srpaulo * Value: 215275963Srpaulo * u32: clock id 216275963Srpaulo * u32: rate (in Hz) 217275963Srpaulo */ 218275963Srpaulo 219275963Srpaulo /* setup single tag buffer */ 220322724Smarius memset(&msg, 0, sizeof(msg)); 221322724Smarius msg.hdr.buf_size = sizeof(msg); 222322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 223322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE; 224322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 225322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 226322724Smarius msg.body.req.clock_id = clock_id; 227322724Smarius msg.end_tag = 0; 228275963Srpaulo 229275963Srpaulo /* call mailbox property */ 230322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 231275963Srpaulo if (err) { 232275963Srpaulo device_printf(sc->dev, "can't get max clock rate (id=%u)\n", 233275963Srpaulo clock_id); 234275963Srpaulo return (MSG_ERROR); 235275963Srpaulo } 236275963Srpaulo 237275963Srpaulo /* result (Hz) */ 238322724Smarius rate = (int)msg.body.resp.rate_hz; 239275963Srpaulo DPRINTF("clock = %d(Hz)\n", rate); 240275963Srpaulo return (rate); 241275963Srpaulo} 242275963Srpaulo 243275963Srpaulostatic int 244275963Srpaulobcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc *sc, 245275963Srpaulo uint32_t clock_id) 246275963Srpaulo{ 247322724Smarius struct msg_get_min_clock_rate msg; 248275963Srpaulo int rate; 249275963Srpaulo int err; 250275963Srpaulo 251275963Srpaulo /* 252275963Srpaulo * Get min clock rate 253275963Srpaulo * Tag: 0x00030007 254275963Srpaulo * Request: 255275963Srpaulo * Length: 4 256275963Srpaulo * Value: 257275963Srpaulo * u32: clock id 258275963Srpaulo * Response: 259275963Srpaulo * Length: 8 260275963Srpaulo * Value: 261275963Srpaulo * u32: clock id 262275963Srpaulo * u32: rate (in Hz) 263275963Srpaulo */ 264275963Srpaulo 265275963Srpaulo /* setup single tag buffer */ 266322724Smarius memset(&msg, 0, sizeof(msg)); 267322724Smarius msg.hdr.buf_size = sizeof(msg); 268322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 269322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE; 270322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 271322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 272322724Smarius msg.body.req.clock_id = clock_id; 273322724Smarius msg.end_tag = 0; 274275963Srpaulo 275275963Srpaulo /* call mailbox property */ 276322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 277275963Srpaulo if (err) { 278275963Srpaulo device_printf(sc->dev, "can't get min clock rate (id=%u)\n", 279275963Srpaulo clock_id); 280275963Srpaulo return (MSG_ERROR); 281275963Srpaulo } 282275963Srpaulo 283275963Srpaulo /* result (Hz) */ 284322724Smarius rate = (int)msg.body.resp.rate_hz; 285275963Srpaulo DPRINTF("clock = %d(Hz)\n", rate); 286275963Srpaulo return (rate); 287275963Srpaulo} 288275963Srpaulo 289275963Srpaulostatic int 290275963Srpaulobcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc *sc, 291275963Srpaulo uint32_t clock_id, uint32_t rate_hz) 292275963Srpaulo{ 293322724Smarius struct msg_set_clock_rate msg; 294275963Srpaulo int rate; 295275963Srpaulo int err; 296275963Srpaulo 297275963Srpaulo /* 298275963Srpaulo * Set clock rate 299275963Srpaulo * Tag: 0x00038002 300275963Srpaulo * Request: 301275963Srpaulo * Length: 8 302275963Srpaulo * Value: 303275963Srpaulo * u32: clock id 304275963Srpaulo * u32: rate (in Hz) 305275963Srpaulo * Response: 306275963Srpaulo * Length: 8 307275963Srpaulo * Value: 308275963Srpaulo * u32: clock id 309275963Srpaulo * u32: rate (in Hz) 310275963Srpaulo */ 311275963Srpaulo 312275963Srpaulo /* setup single tag buffer */ 313322724Smarius memset(&msg, 0, sizeof(msg)); 314322724Smarius msg.hdr.buf_size = sizeof(msg); 315322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 316322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; 317322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 318322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 319322724Smarius msg.body.req.clock_id = clock_id; 320322724Smarius msg.body.req.rate_hz = rate_hz; 321322724Smarius msg.end_tag = 0; 322275963Srpaulo 323275963Srpaulo /* call mailbox property */ 324322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 325275963Srpaulo if (err) { 326275963Srpaulo device_printf(sc->dev, "can't set clock rate (id=%u)\n", 327275963Srpaulo clock_id); 328275963Srpaulo return (MSG_ERROR); 329275963Srpaulo } 330275963Srpaulo 331275963Srpaulo /* workaround for core clock */ 332275963Srpaulo if (clock_id == BCM2835_MBOX_CLOCK_ID_CORE) { 333275963Srpaulo /* for safety (may change voltage without changing clock) */ 334275963Srpaulo DELAY(TRANSITION_LATENCY); 335275963Srpaulo 336275963Srpaulo /* 337275963Srpaulo * XXX: the core clock is unable to change at once, 338275963Srpaulo * to change certainly, write it twice now. 339275963Srpaulo */ 340275963Srpaulo 341275963Srpaulo /* setup single tag buffer */ 342322724Smarius memset(&msg, 0, sizeof(msg)); 343322724Smarius msg.hdr.buf_size = sizeof(msg); 344322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 345322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; 346322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 347322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 348322724Smarius msg.body.req.clock_id = clock_id; 349322724Smarius msg.body.req.rate_hz = rate_hz; 350322724Smarius msg.end_tag = 0; 351275963Srpaulo 352275963Srpaulo /* call mailbox property */ 353322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 354275963Srpaulo if (err) { 355275963Srpaulo device_printf(sc->dev, 356275963Srpaulo "can't set clock rate (id=%u)\n", clock_id); 357275963Srpaulo return (MSG_ERROR); 358275963Srpaulo } 359275963Srpaulo } 360275963Srpaulo 361275963Srpaulo /* result (Hz) */ 362322724Smarius rate = (int)msg.body.resp.rate_hz; 363275963Srpaulo DPRINTF("clock = %d(Hz)\n", rate); 364275963Srpaulo return (rate); 365275963Srpaulo} 366275963Srpaulo 367275963Srpaulostatic int 368275963Srpaulobcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc *sc) 369275963Srpaulo{ 370322724Smarius struct msg_get_turbo msg; 371275963Srpaulo int level; 372275963Srpaulo int err; 373275963Srpaulo 374275963Srpaulo /* 375275963Srpaulo * Get turbo 376275963Srpaulo * Tag: 0x00030009 377275963Srpaulo * Request: 378275963Srpaulo * Length: 4 379275963Srpaulo * Value: 380275963Srpaulo * u32: id 381275963Srpaulo * Response: 382275963Srpaulo * Length: 8 383275963Srpaulo * Value: 384275963Srpaulo * u32: id 385275963Srpaulo * u32: level 386275963Srpaulo */ 387275963Srpaulo 388275963Srpaulo /* setup single tag buffer */ 389322724Smarius memset(&msg, 0, sizeof(msg)); 390322724Smarius msg.hdr.buf_size = sizeof(msg); 391322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 392322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TURBO; 393322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 394322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 395322724Smarius msg.body.req.id = 0; 396322724Smarius msg.end_tag = 0; 397275963Srpaulo 398275963Srpaulo /* call mailbox property */ 399322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 400275963Srpaulo if (err) { 401275963Srpaulo device_printf(sc->dev, "can't get turbo\n"); 402275963Srpaulo return (MSG_ERROR); 403275963Srpaulo } 404275963Srpaulo 405275963Srpaulo /* result 0=non-turbo, 1=turbo */ 406322724Smarius level = (int)msg.body.resp.level; 407275963Srpaulo DPRINTF("level = %d\n", level); 408275963Srpaulo return (level); 409275963Srpaulo} 410275963Srpaulo 411275963Srpaulostatic int 412275963Srpaulobcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level) 413275963Srpaulo{ 414322724Smarius struct msg_set_turbo msg; 415275963Srpaulo int value; 416275963Srpaulo int err; 417275963Srpaulo 418275963Srpaulo /* 419275963Srpaulo * Set turbo 420275963Srpaulo * Tag: 0x00038009 421275963Srpaulo * Request: 422275963Srpaulo * Length: 8 423275963Srpaulo * Value: 424275963Srpaulo * u32: id 425275963Srpaulo * u32: level 426275963Srpaulo * Response: 427275963Srpaulo * Length: 8 428275963Srpaulo * Value: 429275963Srpaulo * u32: id 430275963Srpaulo * u32: level 431275963Srpaulo */ 432275963Srpaulo 433275963Srpaulo /* replace unknown value to OFF */ 434275963Srpaulo if (level != BCM2835_MBOX_TURBO_ON && level != BCM2835_MBOX_TURBO_OFF) 435275963Srpaulo level = BCM2835_MBOX_TURBO_OFF; 436275963Srpaulo 437275963Srpaulo /* setup single tag buffer */ 438322724Smarius memset(&msg, 0, sizeof(msg)); 439322724Smarius msg.hdr.buf_size = sizeof(msg); 440322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 441322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_TURBO; 442322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 443322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 444322724Smarius msg.body.req.id = 0; 445322724Smarius msg.body.req.level = level; 446322724Smarius msg.end_tag = 0; 447275963Srpaulo 448275963Srpaulo /* call mailbox property */ 449322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 450275963Srpaulo if (err) { 451275963Srpaulo device_printf(sc->dev, "can't set turbo\n"); 452275963Srpaulo return (MSG_ERROR); 453275963Srpaulo } 454275963Srpaulo 455275963Srpaulo /* result 0=non-turbo, 1=turbo */ 456322724Smarius value = (int)msg.body.resp.level; 457275963Srpaulo DPRINTF("level = %d\n", value); 458275963Srpaulo return (value); 459275963Srpaulo} 460275963Srpaulo 461275963Srpaulostatic int 462275963Srpaulobcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc *sc, 463275963Srpaulo uint32_t voltage_id) 464275963Srpaulo{ 465322724Smarius struct msg_get_voltage msg; 466275963Srpaulo int value; 467275963Srpaulo int err; 468275963Srpaulo 469275963Srpaulo /* 470275963Srpaulo * Get voltage 471275963Srpaulo * Tag: 0x00030003 472275963Srpaulo * Request: 473275963Srpaulo * Length: 4 474275963Srpaulo * Value: 475275963Srpaulo * u32: voltage id 476275963Srpaulo * Response: 477275963Srpaulo * Length: 8 478275963Srpaulo * Value: 479275963Srpaulo * u32: voltage id 480275963Srpaulo * u32: value (offset from 1.2V in units of 0.025V) 481275963Srpaulo */ 482275963Srpaulo 483275963Srpaulo /* setup single tag buffer */ 484322724Smarius memset(&msg, 0, sizeof(msg)); 485322724Smarius msg.hdr.buf_size = sizeof(msg); 486322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 487322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_VOLTAGE; 488322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 489322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 490322724Smarius msg.body.req.voltage_id = voltage_id; 491322724Smarius msg.end_tag = 0; 492275963Srpaulo 493275963Srpaulo /* call mailbox property */ 494322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 495275963Srpaulo if (err) { 496275963Srpaulo device_printf(sc->dev, "can't get voltage\n"); 497275963Srpaulo return (MSG_ERROR); 498275963Srpaulo } 499275963Srpaulo 500275963Srpaulo /* result (offset from 1.2V) */ 501322724Smarius value = (int)msg.body.resp.value; 502275963Srpaulo DPRINTF("value = %d\n", value); 503275963Srpaulo return (value); 504275963Srpaulo} 505275963Srpaulo 506275963Srpaulostatic int 507275963Srpaulobcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc *sc, 508275963Srpaulo uint32_t voltage_id) 509275963Srpaulo{ 510322724Smarius struct msg_get_max_voltage msg; 511275963Srpaulo int value; 512275963Srpaulo int err; 513275963Srpaulo 514275963Srpaulo /* 515275963Srpaulo * Get voltage 516275963Srpaulo * Tag: 0x00030005 517275963Srpaulo * Request: 518275963Srpaulo * Length: 4 519275963Srpaulo * Value: 520275963Srpaulo * u32: voltage id 521275963Srpaulo * Response: 522275963Srpaulo * Length: 8 523275963Srpaulo * Value: 524275963Srpaulo * u32: voltage id 525275963Srpaulo * u32: value (offset from 1.2V in units of 0.025V) 526275963Srpaulo */ 527275963Srpaulo 528275963Srpaulo /* setup single tag buffer */ 529322724Smarius memset(&msg, 0, sizeof(msg)); 530322724Smarius msg.hdr.buf_size = sizeof(msg); 531322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 532322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_VOLTAGE; 533322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 534322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 535322724Smarius msg.body.req.voltage_id = voltage_id; 536322724Smarius msg.end_tag = 0; 537275963Srpaulo 538275963Srpaulo /* call mailbox property */ 539322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 540275963Srpaulo if (err) { 541275963Srpaulo device_printf(sc->dev, "can't get max voltage\n"); 542275963Srpaulo return (MSG_ERROR); 543275963Srpaulo } 544275963Srpaulo 545275963Srpaulo /* result (offset from 1.2V) */ 546322724Smarius value = (int)msg.body.resp.value; 547275963Srpaulo DPRINTF("value = %d\n", value); 548275963Srpaulo return (value); 549275963Srpaulo} 550275963Srpaulostatic int 551275963Srpaulobcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc *sc, 552275963Srpaulo uint32_t voltage_id) 553275963Srpaulo{ 554322724Smarius struct msg_get_min_voltage msg; 555275963Srpaulo int value; 556275963Srpaulo int err; 557275963Srpaulo 558275963Srpaulo /* 559275963Srpaulo * Get voltage 560275963Srpaulo * Tag: 0x00030008 561275963Srpaulo * Request: 562275963Srpaulo * Length: 4 563275963Srpaulo * Value: 564275963Srpaulo * u32: voltage id 565275963Srpaulo * Response: 566275963Srpaulo * Length: 8 567275963Srpaulo * Value: 568275963Srpaulo * u32: voltage id 569275963Srpaulo * u32: value (offset from 1.2V in units of 0.025V) 570275963Srpaulo */ 571275963Srpaulo 572275963Srpaulo /* setup single tag buffer */ 573322724Smarius memset(&msg, 0, sizeof(msg)); 574322724Smarius msg.hdr.buf_size = sizeof(msg); 575322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 576322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_VOLTAGE; 577322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 578322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 579322724Smarius msg.body.req.voltage_id = voltage_id; 580322724Smarius msg.end_tag = 0; 581275963Srpaulo 582275963Srpaulo /* call mailbox property */ 583322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 584275963Srpaulo if (err) { 585275963Srpaulo device_printf(sc->dev, "can't get min voltage\n"); 586275963Srpaulo return (MSG_ERROR); 587275963Srpaulo } 588275963Srpaulo 589275963Srpaulo /* result (offset from 1.2V) */ 590322724Smarius value = (int)msg.body.resp.value; 591275963Srpaulo DPRINTF("value = %d\n", value); 592275963Srpaulo return (value); 593275963Srpaulo} 594275963Srpaulo 595275963Srpaulostatic int 596275963Srpaulobcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc *sc, 597275963Srpaulo uint32_t voltage_id, int32_t value) 598275963Srpaulo{ 599322724Smarius struct msg_set_voltage msg; 600275963Srpaulo int err; 601275963Srpaulo 602275963Srpaulo /* 603275963Srpaulo * Set voltage 604275963Srpaulo * Tag: 0x00038003 605275963Srpaulo * Request: 606275963Srpaulo * Length: 4 607275963Srpaulo * Value: 608275963Srpaulo * u32: voltage id 609275963Srpaulo * u32: value (offset from 1.2V in units of 0.025V) 610275963Srpaulo * Response: 611275963Srpaulo * Length: 8 612275963Srpaulo * Value: 613275963Srpaulo * u32: voltage id 614275963Srpaulo * u32: value (offset from 1.2V in units of 0.025V) 615275963Srpaulo */ 616275963Srpaulo 617275963Srpaulo /* 618275963Srpaulo * over_voltage: 619275963Srpaulo * 0 (1.2 V). Values above 6 are only allowed when force_turbo or 620275963Srpaulo * current_limit_override are specified (which set the warranty bit). 621275963Srpaulo */ 622275963Srpaulo if (value > MAX_OVER_VOLTAGE || value < MIN_OVER_VOLTAGE) { 623275963Srpaulo /* currently not supported */ 624275963Srpaulo device_printf(sc->dev, "not supported voltage: %d\n", value); 625275963Srpaulo return (MSG_ERROR); 626275963Srpaulo } 627275963Srpaulo 628275963Srpaulo /* setup single tag buffer */ 629322724Smarius memset(&msg, 0, sizeof(msg)); 630322724Smarius msg.hdr.buf_size = sizeof(msg); 631322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 632322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_VOLTAGE; 633322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 634322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 635322724Smarius msg.body.req.voltage_id = voltage_id; 636322724Smarius msg.body.req.value = (uint32_t)value; 637322724Smarius msg.end_tag = 0; 638275963Srpaulo 639275963Srpaulo /* call mailbox property */ 640322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 641275963Srpaulo if (err) { 642275963Srpaulo device_printf(sc->dev, "can't set voltage\n"); 643275963Srpaulo return (MSG_ERROR); 644275963Srpaulo } 645275963Srpaulo 646275963Srpaulo /* result (offset from 1.2V) */ 647322724Smarius value = (int)msg.body.resp.value; 648275963Srpaulo DPRINTF("value = %d\n", value); 649275963Srpaulo return (value); 650275963Srpaulo} 651275963Srpaulo 652275963Srpaulostatic int 653275963Srpaulobcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc *sc) 654275963Srpaulo{ 655322724Smarius struct msg_get_temperature msg; 656275963Srpaulo int value; 657275963Srpaulo int err; 658275963Srpaulo 659275963Srpaulo /* 660275963Srpaulo * Get temperature 661275963Srpaulo * Tag: 0x00030006 662275963Srpaulo * Request: 663275963Srpaulo * Length: 4 664275963Srpaulo * Value: 665275963Srpaulo * u32: temperature id 666275963Srpaulo * Response: 667275963Srpaulo * Length: 8 668275963Srpaulo * Value: 669275963Srpaulo * u32: temperature id 670275963Srpaulo * u32: value 671275963Srpaulo */ 672275963Srpaulo 673275963Srpaulo /* setup single tag buffer */ 674322724Smarius memset(&msg, 0, sizeof(msg)); 675322724Smarius msg.hdr.buf_size = sizeof(msg); 676322724Smarius msg.hdr.code = BCM2835_MBOX_CODE_REQ; 677322724Smarius msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TEMPERATURE; 678322724Smarius msg.tag_hdr.val_buf_size = sizeof(msg.body); 679322724Smarius msg.tag_hdr.val_len = sizeof(msg.body.req); 680322724Smarius msg.body.req.temperature_id = 0; 681322724Smarius msg.end_tag = 0; 682275963Srpaulo 683275963Srpaulo /* call mailbox property */ 684322724Smarius err = bcm2835_mbox_property(&msg, sizeof(msg)); 685275963Srpaulo if (err) { 686275963Srpaulo device_printf(sc->dev, "can't get temperature\n"); 687275963Srpaulo return (MSG_ERROR); 688275963Srpaulo } 689275963Srpaulo 690275963Srpaulo /* result (temperature of degree C) */ 691322724Smarius value = (int)msg.body.resp.value; 692275963Srpaulo DPRINTF("value = %d\n", value); 693275963Srpaulo return (value); 694275963Srpaulo} 695275963Srpaulo 696275963Srpaulo 697275963Srpaulo 698275963Srpaulostatic int 699275963Srpaulosysctl_bcm2835_cpufreq_arm_freq(SYSCTL_HANDLER_ARGS) 700275963Srpaulo{ 701275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 702275963Srpaulo int val; 703275963Srpaulo int err; 704275963Srpaulo 705275963Srpaulo /* get realtime value */ 706275963Srpaulo VC_LOCK(sc); 707275963Srpaulo val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); 708275963Srpaulo VC_UNLOCK(sc); 709275963Srpaulo if (val == MSG_ERROR) 710275963Srpaulo return (EIO); 711275963Srpaulo 712275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 713275963Srpaulo if (err || !req->newptr) /* error || read request */ 714275963Srpaulo return (err); 715275963Srpaulo 716275963Srpaulo /* write request */ 717275963Srpaulo VC_LOCK(sc); 718275963Srpaulo err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, 719275963Srpaulo val); 720275963Srpaulo VC_UNLOCK(sc); 721275963Srpaulo if (err == MSG_ERROR) { 722275963Srpaulo device_printf(sc->dev, "set clock arm_freq error\n"); 723275963Srpaulo return (EIO); 724275963Srpaulo } 725275963Srpaulo DELAY(TRANSITION_LATENCY); 726275963Srpaulo 727275963Srpaulo return (0); 728275963Srpaulo} 729275963Srpaulo 730275963Srpaulostatic int 731275963Srpaulosysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS) 732275963Srpaulo{ 733275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 734275963Srpaulo int val; 735275963Srpaulo int err; 736275963Srpaulo 737275963Srpaulo /* get realtime value */ 738275963Srpaulo VC_LOCK(sc); 739275963Srpaulo val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); 740275963Srpaulo VC_UNLOCK(sc); 741275963Srpaulo if (val == MSG_ERROR) 742275963Srpaulo return (EIO); 743275963Srpaulo 744275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 745275963Srpaulo if (err || !req->newptr) /* error || read request */ 746275963Srpaulo return (err); 747275963Srpaulo 748275963Srpaulo /* write request */ 749275963Srpaulo VC_LOCK(sc); 750275963Srpaulo err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, 751275963Srpaulo val); 752275963Srpaulo if (err == MSG_ERROR) { 753275963Srpaulo VC_UNLOCK(sc); 754275963Srpaulo device_printf(sc->dev, "set clock core_freq error\n"); 755275963Srpaulo return (EIO); 756275963Srpaulo } 757275963Srpaulo VC_UNLOCK(sc); 758275963Srpaulo DELAY(TRANSITION_LATENCY); 759275963Srpaulo 760275963Srpaulo return (0); 761275963Srpaulo} 762275963Srpaulo 763275963Srpaulostatic int 764275963Srpaulosysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS) 765275963Srpaulo{ 766275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 767275963Srpaulo int val; 768275963Srpaulo int err; 769275963Srpaulo 770275963Srpaulo /* get realtime value */ 771275963Srpaulo VC_LOCK(sc); 772275963Srpaulo val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); 773275963Srpaulo VC_UNLOCK(sc); 774275963Srpaulo if (val == MSG_ERROR) 775275963Srpaulo return (EIO); 776275963Srpaulo 777275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 778275963Srpaulo if (err || !req->newptr) /* error || read request */ 779275963Srpaulo return (err); 780275963Srpaulo 781275963Srpaulo /* write request */ 782275963Srpaulo VC_LOCK(sc); 783275963Srpaulo err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, 784275963Srpaulo val); 785275963Srpaulo VC_UNLOCK(sc); 786275963Srpaulo if (err == MSG_ERROR) { 787275963Srpaulo device_printf(sc->dev, "set clock sdram_freq error\n"); 788275963Srpaulo return (EIO); 789275963Srpaulo } 790275963Srpaulo DELAY(TRANSITION_LATENCY); 791275963Srpaulo 792275963Srpaulo return (0); 793275963Srpaulo} 794275963Srpaulo 795275963Srpaulostatic int 796275963Srpaulosysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS) 797275963Srpaulo{ 798275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 799275963Srpaulo int val; 800275963Srpaulo int err; 801275963Srpaulo 802275963Srpaulo /* get realtime value */ 803275963Srpaulo VC_LOCK(sc); 804275963Srpaulo val = bcm2835_cpufreq_get_turbo(sc); 805275963Srpaulo VC_UNLOCK(sc); 806275963Srpaulo if (val == MSG_ERROR) 807275963Srpaulo return (EIO); 808275963Srpaulo 809275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 810275963Srpaulo if (err || !req->newptr) /* error || read request */ 811275963Srpaulo return (err); 812275963Srpaulo 813275963Srpaulo /* write request */ 814275963Srpaulo if (val > 0) 815275963Srpaulo sc->turbo_mode = BCM2835_MBOX_TURBO_ON; 816275963Srpaulo else 817275963Srpaulo sc->turbo_mode = BCM2835_MBOX_TURBO_OFF; 818275963Srpaulo 819275963Srpaulo VC_LOCK(sc); 820275963Srpaulo err = bcm2835_cpufreq_set_turbo(sc, sc->turbo_mode); 821275963Srpaulo VC_UNLOCK(sc); 822275963Srpaulo if (err == MSG_ERROR) { 823275963Srpaulo device_printf(sc->dev, "set turbo error\n"); 824275963Srpaulo return (EIO); 825275963Srpaulo } 826275963Srpaulo DELAY(TRANSITION_LATENCY); 827275963Srpaulo 828275963Srpaulo return (0); 829275963Srpaulo} 830275963Srpaulo 831275963Srpaulostatic int 832275963Srpaulosysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS) 833275963Srpaulo{ 834275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 835275963Srpaulo int val; 836275963Srpaulo int err; 837275963Srpaulo 838275963Srpaulo /* get realtime value */ 839275963Srpaulo VC_LOCK(sc); 840275963Srpaulo val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); 841275963Srpaulo VC_UNLOCK(sc); 842275963Srpaulo if (val == MSG_ERROR) 843275963Srpaulo return (EIO); 844275963Srpaulo 845275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 846275963Srpaulo if (err || !req->newptr) /* error || read request */ 847275963Srpaulo return (err); 848275963Srpaulo 849275963Srpaulo /* write request */ 850275963Srpaulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) 851275963Srpaulo return (EINVAL); 852275963Srpaulo sc->voltage_core = val; 853275963Srpaulo 854275963Srpaulo VC_LOCK(sc); 855275963Srpaulo err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE, 856275963Srpaulo sc->voltage_core); 857275963Srpaulo VC_UNLOCK(sc); 858275963Srpaulo if (err == MSG_ERROR) { 859275963Srpaulo device_printf(sc->dev, "set voltage core error\n"); 860275963Srpaulo return (EIO); 861275963Srpaulo } 862275963Srpaulo DELAY(TRANSITION_LATENCY); 863275963Srpaulo 864275963Srpaulo return (0); 865275963Srpaulo} 866275963Srpaulo 867275963Srpaulostatic int 868275963Srpaulosysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS) 869275963Srpaulo{ 870275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 871275963Srpaulo int val; 872275963Srpaulo int err; 873275963Srpaulo 874275963Srpaulo /* get realtime value */ 875275963Srpaulo VC_LOCK(sc); 876275963Srpaulo val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); 877275963Srpaulo VC_UNLOCK(sc); 878275963Srpaulo if (val == MSG_ERROR) 879275963Srpaulo return (EIO); 880275963Srpaulo 881275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 882275963Srpaulo if (err || !req->newptr) /* error || read request */ 883275963Srpaulo return (err); 884275963Srpaulo 885275963Srpaulo /* write request */ 886275963Srpaulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) 887275963Srpaulo return (EINVAL); 888275963Srpaulo sc->voltage_sdram_c = val; 889275963Srpaulo 890275963Srpaulo VC_LOCK(sc); 891275963Srpaulo err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C, 892275963Srpaulo sc->voltage_sdram_c); 893275963Srpaulo VC_UNLOCK(sc); 894275963Srpaulo if (err == MSG_ERROR) { 895275963Srpaulo device_printf(sc->dev, "set voltage sdram_c error\n"); 896275963Srpaulo return (EIO); 897275963Srpaulo } 898275963Srpaulo DELAY(TRANSITION_LATENCY); 899275963Srpaulo 900275963Srpaulo return (0); 901275963Srpaulo} 902275963Srpaulo 903275963Srpaulostatic int 904275963Srpaulosysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS) 905275963Srpaulo{ 906275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 907275963Srpaulo int val; 908275963Srpaulo int err; 909275963Srpaulo 910275963Srpaulo /* get realtime value */ 911275963Srpaulo VC_LOCK(sc); 912275963Srpaulo val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); 913275963Srpaulo VC_UNLOCK(sc); 914275963Srpaulo if (val == MSG_ERROR) 915275963Srpaulo return (EIO); 916275963Srpaulo 917275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 918275963Srpaulo if (err || !req->newptr) /* error || read request */ 919275963Srpaulo return (err); 920275963Srpaulo 921275963Srpaulo /* write request */ 922275963Srpaulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) 923275963Srpaulo return (EINVAL); 924275963Srpaulo sc->voltage_sdram_i = val; 925275963Srpaulo 926275963Srpaulo VC_LOCK(sc); 927275963Srpaulo err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I, 928275963Srpaulo sc->voltage_sdram_i); 929275963Srpaulo VC_UNLOCK(sc); 930275963Srpaulo if (err == MSG_ERROR) { 931275963Srpaulo device_printf(sc->dev, "set voltage sdram_i error\n"); 932275963Srpaulo return (EIO); 933275963Srpaulo } 934275963Srpaulo DELAY(TRANSITION_LATENCY); 935275963Srpaulo 936275963Srpaulo return (0); 937275963Srpaulo} 938275963Srpaulo 939275963Srpaulostatic int 940275963Srpaulosysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS) 941275963Srpaulo{ 942275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 943275963Srpaulo int val; 944275963Srpaulo int err; 945275963Srpaulo 946275963Srpaulo /* get realtime value */ 947275963Srpaulo VC_LOCK(sc); 948275963Srpaulo val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); 949275963Srpaulo VC_UNLOCK(sc); 950275963Srpaulo if (val == MSG_ERROR) 951275963Srpaulo return (EIO); 952275963Srpaulo 953275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 954275963Srpaulo if (err || !req->newptr) /* error || read request */ 955275963Srpaulo return (err); 956275963Srpaulo 957275963Srpaulo /* write request */ 958275963Srpaulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) 959275963Srpaulo return (EINVAL); 960275963Srpaulo sc->voltage_sdram_p = val; 961275963Srpaulo 962275963Srpaulo VC_LOCK(sc); 963275963Srpaulo err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P, 964275963Srpaulo sc->voltage_sdram_p); 965275963Srpaulo VC_UNLOCK(sc); 966275963Srpaulo if (err == MSG_ERROR) { 967275963Srpaulo device_printf(sc->dev, "set voltage sdram_p error\n"); 968275963Srpaulo return (EIO); 969275963Srpaulo } 970275963Srpaulo DELAY(TRANSITION_LATENCY); 971275963Srpaulo 972275963Srpaulo return (0); 973275963Srpaulo} 974275963Srpaulo 975275963Srpaulostatic int 976275963Srpaulosysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS) 977275963Srpaulo{ 978275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 979275963Srpaulo int val; 980275963Srpaulo int err; 981275963Srpaulo 982275963Srpaulo /* multiple write only */ 983275963Srpaulo if (!req->newptr) 984275963Srpaulo return (EINVAL); 985275963Srpaulo val = 0; 986275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 987275963Srpaulo if (err) 988275963Srpaulo return (err); 989275963Srpaulo 990275963Srpaulo /* write request */ 991275963Srpaulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) 992275963Srpaulo return (EINVAL); 993275963Srpaulo sc->voltage_sdram = val; 994275963Srpaulo 995275963Srpaulo VC_LOCK(sc); 996275963Srpaulo err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C, 997275963Srpaulo val); 998275963Srpaulo if (err == MSG_ERROR) { 999275963Srpaulo VC_UNLOCK(sc); 1000275963Srpaulo device_printf(sc->dev, "set voltage sdram_c error\n"); 1001275963Srpaulo return (EIO); 1002275963Srpaulo } 1003275963Srpaulo err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I, 1004275963Srpaulo val); 1005275963Srpaulo if (err == MSG_ERROR) { 1006275963Srpaulo VC_UNLOCK(sc); 1007275963Srpaulo device_printf(sc->dev, "set voltage sdram_i error\n"); 1008275963Srpaulo return (EIO); 1009275963Srpaulo } 1010275963Srpaulo err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P, 1011275963Srpaulo val); 1012275963Srpaulo if (err == MSG_ERROR) { 1013275963Srpaulo VC_UNLOCK(sc); 1014275963Srpaulo device_printf(sc->dev, "set voltage sdram_p error\n"); 1015275963Srpaulo return (EIO); 1016275963Srpaulo } 1017275963Srpaulo VC_UNLOCK(sc); 1018275963Srpaulo DELAY(TRANSITION_LATENCY); 1019275963Srpaulo 1020275963Srpaulo return (0); 1021275963Srpaulo} 1022275963Srpaulo 1023275963Srpaulostatic int 1024275963Srpaulosysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS) 1025275963Srpaulo{ 1026275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 1027275963Srpaulo int val; 1028275963Srpaulo int err; 1029275963Srpaulo 1030275963Srpaulo /* get realtime value */ 1031275963Srpaulo VC_LOCK(sc); 1032275963Srpaulo val = bcm2835_cpufreq_get_temperature(sc); 1033275963Srpaulo VC_UNLOCK(sc); 1034275963Srpaulo if (val == MSG_ERROR) 1035275963Srpaulo return (EIO); 1036275963Srpaulo 1037275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 1038275963Srpaulo if (err || !req->newptr) /* error || read request */ 1039275963Srpaulo return (err); 1040275963Srpaulo 1041275963Srpaulo /* write request */ 1042275963Srpaulo return (EINVAL); 1043275963Srpaulo} 1044275963Srpaulo 1045275963Srpaulostatic int 1046275963Srpaulosysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS) 1047275963Srpaulo{ 1048275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg1; 1049275963Srpaulo int val; 1050275963Srpaulo int err; 1051275963Srpaulo 1052275963Srpaulo /* get realtime value */ 1053275963Srpaulo VC_LOCK(sc); 1054275963Srpaulo val = bcm2835_cpufreq_get_temperature(sc); 1055275963Srpaulo VC_UNLOCK(sc); 1056275963Srpaulo if (val == MSG_ERROR) 1057275963Srpaulo return (EIO); 1058275963Srpaulo 1059275963Srpaulo /* 1/1000 celsius (raw) to 1/10 kelvin */ 1060278768Sloos val = val / 100 + TZ_ZEROC; 1061275963Srpaulo 1062275963Srpaulo err = sysctl_handle_int(oidp, &val, 0, req); 1063275963Srpaulo if (err || !req->newptr) /* error || read request */ 1064275963Srpaulo return (err); 1065275963Srpaulo 1066275963Srpaulo /* write request */ 1067275963Srpaulo return (EINVAL); 1068275963Srpaulo} 1069275963Srpaulo 1070275963Srpaulo 1071275963Srpaulostatic void 1072275963Srpaulobcm2835_cpufreq_init(void *arg) 1073275963Srpaulo{ 1074275963Srpaulo struct bcm2835_cpufreq_softc *sc = arg; 1075275963Srpaulo struct sysctl_ctx_list *ctx; 1076275963Srpaulo device_t cpu; 1077275963Srpaulo int arm_freq, core_freq, sdram_freq; 1078275963Srpaulo int arm_max_freq, arm_min_freq, core_max_freq, core_min_freq; 1079275963Srpaulo int sdram_max_freq, sdram_min_freq; 1080275963Srpaulo int voltage_core, voltage_sdram_c, voltage_sdram_i, voltage_sdram_p; 1081275963Srpaulo int max_voltage_core, min_voltage_core; 1082275963Srpaulo int max_voltage_sdram_c, min_voltage_sdram_c; 1083275963Srpaulo int max_voltage_sdram_i, min_voltage_sdram_i; 1084275963Srpaulo int max_voltage_sdram_p, min_voltage_sdram_p; 1085275963Srpaulo int turbo, temperature; 1086275963Srpaulo 1087275963Srpaulo VC_LOCK(sc); 1088275963Srpaulo 1089275963Srpaulo /* current clock */ 1090275963Srpaulo arm_freq = bcm2835_cpufreq_get_clock_rate(sc, 1091275963Srpaulo BCM2835_MBOX_CLOCK_ID_ARM); 1092275963Srpaulo core_freq = bcm2835_cpufreq_get_clock_rate(sc, 1093275963Srpaulo BCM2835_MBOX_CLOCK_ID_CORE); 1094275963Srpaulo sdram_freq = bcm2835_cpufreq_get_clock_rate(sc, 1095275963Srpaulo BCM2835_MBOX_CLOCK_ID_SDRAM); 1096275963Srpaulo 1097275963Srpaulo /* max/min clock */ 1098275963Srpaulo arm_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, 1099275963Srpaulo BCM2835_MBOX_CLOCK_ID_ARM); 1100275963Srpaulo arm_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, 1101275963Srpaulo BCM2835_MBOX_CLOCK_ID_ARM); 1102275963Srpaulo core_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, 1103275963Srpaulo BCM2835_MBOX_CLOCK_ID_CORE); 1104275963Srpaulo core_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, 1105275963Srpaulo BCM2835_MBOX_CLOCK_ID_CORE); 1106275963Srpaulo sdram_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, 1107275963Srpaulo BCM2835_MBOX_CLOCK_ID_SDRAM); 1108275963Srpaulo sdram_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, 1109275963Srpaulo BCM2835_MBOX_CLOCK_ID_SDRAM); 1110275963Srpaulo 1111275963Srpaulo /* turbo mode */ 1112275963Srpaulo turbo = bcm2835_cpufreq_get_turbo(sc); 1113275963Srpaulo if (turbo > 0) 1114275963Srpaulo sc->turbo_mode = BCM2835_MBOX_TURBO_ON; 1115275963Srpaulo else 1116275963Srpaulo sc->turbo_mode = BCM2835_MBOX_TURBO_OFF; 1117275963Srpaulo 1118275963Srpaulo /* voltage */ 1119275963Srpaulo voltage_core = bcm2835_cpufreq_get_voltage(sc, 1120275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_CORE); 1121275963Srpaulo voltage_sdram_c = bcm2835_cpufreq_get_voltage(sc, 1122275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); 1123275963Srpaulo voltage_sdram_i = bcm2835_cpufreq_get_voltage(sc, 1124275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); 1125275963Srpaulo voltage_sdram_p = bcm2835_cpufreq_get_voltage(sc, 1126275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); 1127275963Srpaulo 1128275963Srpaulo /* current values (offset from 1.2V) */ 1129275963Srpaulo sc->voltage_core = voltage_core; 1130275963Srpaulo sc->voltage_sdram = voltage_sdram_c; 1131275963Srpaulo sc->voltage_sdram_c = voltage_sdram_c; 1132275963Srpaulo sc->voltage_sdram_i = voltage_sdram_i; 1133275963Srpaulo sc->voltage_sdram_p = voltage_sdram_p; 1134275963Srpaulo 1135275963Srpaulo /* max/min voltage */ 1136275963Srpaulo max_voltage_core = bcm2835_cpufreq_get_max_voltage(sc, 1137275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_CORE); 1138275963Srpaulo min_voltage_core = bcm2835_cpufreq_get_min_voltage(sc, 1139275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_CORE); 1140275963Srpaulo max_voltage_sdram_c = bcm2835_cpufreq_get_max_voltage(sc, 1141275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); 1142275963Srpaulo max_voltage_sdram_i = bcm2835_cpufreq_get_max_voltage(sc, 1143275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); 1144275963Srpaulo max_voltage_sdram_p = bcm2835_cpufreq_get_max_voltage(sc, 1145275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); 1146275963Srpaulo min_voltage_sdram_c = bcm2835_cpufreq_get_min_voltage(sc, 1147275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); 1148275963Srpaulo min_voltage_sdram_i = bcm2835_cpufreq_get_min_voltage(sc, 1149275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); 1150275963Srpaulo min_voltage_sdram_p = bcm2835_cpufreq_get_min_voltage(sc, 1151275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); 1152275963Srpaulo 1153275963Srpaulo /* temperature */ 1154275963Srpaulo temperature = bcm2835_cpufreq_get_temperature(sc); 1155275963Srpaulo 1156275963Srpaulo /* show result */ 1157275963Srpaulo if (cpufreq_verbose || bootverbose) { 1158275963Srpaulo device_printf(sc->dev, "Boot settings:\n"); 1159275963Srpaulo device_printf(sc->dev, 1160275963Srpaulo "current ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n", 1161275963Srpaulo HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq), 1162275963Srpaulo (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF"); 1163275963Srpaulo 1164275963Srpaulo device_printf(sc->dev, 1165275963Srpaulo "max/min ARM %d/%dMHz, Core %d/%dMHz, SDRAM %d/%dMHz\n", 1166275963Srpaulo HZ2MHZ(arm_max_freq), HZ2MHZ(arm_min_freq), 1167275963Srpaulo HZ2MHZ(core_max_freq), HZ2MHZ(core_min_freq), 1168275963Srpaulo HZ2MHZ(sdram_max_freq), HZ2MHZ(sdram_min_freq)); 1169275963Srpaulo 1170275963Srpaulo device_printf(sc->dev, 1171275963Srpaulo "current Core %dmV, SDRAM_C %dmV, SDRAM_I %dmV, " 1172275963Srpaulo "SDRAM_P %dmV\n", 1173275963Srpaulo OFFSET2MVOLT(voltage_core), OFFSET2MVOLT(voltage_sdram_c), 1174275963Srpaulo OFFSET2MVOLT(voltage_sdram_i), 1175275963Srpaulo OFFSET2MVOLT(voltage_sdram_p)); 1176275963Srpaulo 1177275963Srpaulo device_printf(sc->dev, 1178275963Srpaulo "max/min Core %d/%dmV, SDRAM_C %d/%dmV, SDRAM_I %d/%dmV, " 1179275963Srpaulo "SDRAM_P %d/%dmV\n", 1180275963Srpaulo OFFSET2MVOLT(max_voltage_core), 1181275963Srpaulo OFFSET2MVOLT(min_voltage_core), 1182275963Srpaulo OFFSET2MVOLT(max_voltage_sdram_c), 1183275963Srpaulo OFFSET2MVOLT(min_voltage_sdram_c), 1184275963Srpaulo OFFSET2MVOLT(max_voltage_sdram_i), 1185275963Srpaulo OFFSET2MVOLT(min_voltage_sdram_i), 1186275963Srpaulo OFFSET2MVOLT(max_voltage_sdram_p), 1187275963Srpaulo OFFSET2MVOLT(min_voltage_sdram_p)); 1188275963Srpaulo 1189275963Srpaulo device_printf(sc->dev, 1190275963Srpaulo "Temperature %d.%dC\n", (temperature / 1000), 1191275963Srpaulo (temperature % 1000) / 100); 1192275963Srpaulo } else { /* !cpufreq_verbose && !bootverbose */ 1193275963Srpaulo device_printf(sc->dev, 1194275963Srpaulo "ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n", 1195275963Srpaulo HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq), 1196275963Srpaulo (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF"); 1197275963Srpaulo } 1198275963Srpaulo 1199275963Srpaulo /* keep in softc (MHz/mV) */ 1200275963Srpaulo sc->arm_max_freq = HZ2MHZ(arm_max_freq); 1201275963Srpaulo sc->arm_min_freq = HZ2MHZ(arm_min_freq); 1202275963Srpaulo sc->core_max_freq = HZ2MHZ(core_max_freq); 1203275963Srpaulo sc->core_min_freq = HZ2MHZ(core_min_freq); 1204275963Srpaulo sc->sdram_max_freq = HZ2MHZ(sdram_max_freq); 1205275963Srpaulo sc->sdram_min_freq = HZ2MHZ(sdram_min_freq); 1206275963Srpaulo sc->max_voltage_core = OFFSET2MVOLT(max_voltage_core); 1207275963Srpaulo sc->min_voltage_core = OFFSET2MVOLT(min_voltage_core); 1208275963Srpaulo 1209275963Srpaulo /* if turbo is on, set to max values */ 1210275963Srpaulo if (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) { 1211275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, 1212275963Srpaulo arm_max_freq); 1213275963Srpaulo DELAY(TRANSITION_LATENCY); 1214275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, 1215275963Srpaulo core_max_freq); 1216275963Srpaulo DELAY(TRANSITION_LATENCY); 1217275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, 1218275963Srpaulo BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_max_freq); 1219275963Srpaulo DELAY(TRANSITION_LATENCY); 1220275963Srpaulo } else { 1221275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, 1222275963Srpaulo arm_min_freq); 1223275963Srpaulo DELAY(TRANSITION_LATENCY); 1224275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, 1225275963Srpaulo core_min_freq); 1226275963Srpaulo DELAY(TRANSITION_LATENCY); 1227275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, 1228275963Srpaulo BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_min_freq); 1229275963Srpaulo DELAY(TRANSITION_LATENCY); 1230275963Srpaulo } 1231275963Srpaulo 1232275963Srpaulo VC_UNLOCK(sc); 1233275963Srpaulo 1234275963Srpaulo /* add human readable temperature to dev.cpu node */ 1235275963Srpaulo cpu = device_get_parent(sc->dev); 1236275963Srpaulo if (cpu != NULL) { 1237275963Srpaulo ctx = device_get_sysctl_ctx(cpu); 1238275963Srpaulo SYSCTL_ADD_PROC(ctx, 1239275963Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO, 1240275963Srpaulo "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, 1241275963Srpaulo sysctl_bcm2835_devcpu_temperature, "IK", 1242275963Srpaulo "Current SoC temperature"); 1243275963Srpaulo } 1244275963Srpaulo 1245275963Srpaulo /* release this hook (continue boot) */ 1246275963Srpaulo config_intrhook_disestablish(&sc->init_hook); 1247275963Srpaulo} 1248275963Srpaulo 1249275963Srpaulostatic void 1250275963Srpaulobcm2835_cpufreq_identify(driver_t *driver, device_t parent) 1251275963Srpaulo{ 1252322724Smarius const struct ofw_compat_data *compat; 1253322724Smarius phandle_t root; 1254275963Srpaulo 1255322724Smarius root = OF_finddevice("/"); 1256322724Smarius for (compat = compat_data; compat->ocd_str != NULL; compat++) 1257322724Smarius if (fdt_is_compatible(root, compat->ocd_str)) 1258322724Smarius break; 1259322724Smarius 1260322724Smarius if (compat->ocd_data == 0) 1261322724Smarius return; 1262322724Smarius 1263275963Srpaulo DPRINTF("driver=%p, parent=%p\n", driver, parent); 1264275963Srpaulo if (device_find_child(parent, "bcm2835_cpufreq", -1) != NULL) 1265275963Srpaulo return; 1266275963Srpaulo if (BUS_ADD_CHILD(parent, 0, "bcm2835_cpufreq", -1) == NULL) 1267275963Srpaulo device_printf(parent, "add child failed\n"); 1268275963Srpaulo} 1269275963Srpaulo 1270275963Srpaulostatic int 1271275963Srpaulobcm2835_cpufreq_probe(device_t dev) 1272275963Srpaulo{ 1273275963Srpaulo 1274275963Srpaulo device_set_desc(dev, "CPU Frequency Control"); 1275275963Srpaulo return (0); 1276275963Srpaulo} 1277275963Srpaulo 1278275963Srpaulostatic int 1279275963Srpaulobcm2835_cpufreq_attach(device_t dev) 1280275963Srpaulo{ 1281275963Srpaulo struct bcm2835_cpufreq_softc *sc; 1282275963Srpaulo struct sysctl_oid *oid; 1283275963Srpaulo 1284275963Srpaulo /* set self dev */ 1285275963Srpaulo sc = device_get_softc(dev); 1286275963Srpaulo sc->dev = dev; 1287275963Srpaulo 1288275963Srpaulo /* initial values */ 1289275963Srpaulo sc->arm_max_freq = -1; 1290275963Srpaulo sc->arm_min_freq = -1; 1291275963Srpaulo sc->core_max_freq = -1; 1292275963Srpaulo sc->core_min_freq = -1; 1293275963Srpaulo sc->sdram_max_freq = -1; 1294275963Srpaulo sc->sdram_min_freq = -1; 1295275963Srpaulo sc->max_voltage_core = 0; 1296275963Srpaulo sc->min_voltage_core = 0; 1297275963Srpaulo 1298275963Srpaulo /* setup sysctl at first device */ 1299275963Srpaulo if (device_get_unit(dev) == 0) { 1300275963Srpaulo sysctl_ctx_init(&bcm2835_sysctl_ctx); 1301275963Srpaulo /* create node for hw.cpufreq */ 1302275963Srpaulo oid = SYSCTL_ADD_NODE(&bcm2835_sysctl_ctx, 1303275963Srpaulo SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "cpufreq", 1304275963Srpaulo CTLFLAG_RD, NULL, ""); 1305275963Srpaulo 1306275963Srpaulo /* Frequency (Hz) */ 1307275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1308275963Srpaulo OID_AUTO, "arm_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 1309275963Srpaulo sysctl_bcm2835_cpufreq_arm_freq, "IU", 1310275963Srpaulo "ARM frequency (Hz)"); 1311275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1312275963Srpaulo OID_AUTO, "core_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 1313275963Srpaulo sysctl_bcm2835_cpufreq_core_freq, "IU", 1314275963Srpaulo "Core frequency (Hz)"); 1315275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1316275963Srpaulo OID_AUTO, "sdram_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 1317275963Srpaulo sysctl_bcm2835_cpufreq_sdram_freq, "IU", 1318275963Srpaulo "SDRAM frequency (Hz)"); 1319275963Srpaulo 1320275963Srpaulo /* Turbo state */ 1321275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1322275963Srpaulo OID_AUTO, "turbo", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 1323275963Srpaulo sysctl_bcm2835_cpufreq_turbo, "IU", 1324275963Srpaulo "Disables dynamic clocking"); 1325275963Srpaulo 1326275963Srpaulo /* Voltage (offset from 1.2V in units of 0.025V) */ 1327275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1328275963Srpaulo OID_AUTO, "voltage_core", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 1329275963Srpaulo sysctl_bcm2835_cpufreq_voltage_core, "I", 1330275963Srpaulo "ARM/GPU core voltage" 1331275963Srpaulo "(offset from 1.2V in units of 0.025V)"); 1332275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1333275963Srpaulo OID_AUTO, "voltage_sdram", CTLTYPE_INT | CTLFLAG_WR, sc, 1334275963Srpaulo 0, sysctl_bcm2835_cpufreq_voltage_sdram, "I", 1335275963Srpaulo "SDRAM voltage (offset from 1.2V in units of 0.025V)"); 1336275963Srpaulo 1337275963Srpaulo /* Voltage individual SDRAM */ 1338275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1339275963Srpaulo OID_AUTO, "voltage_sdram_c", CTLTYPE_INT | CTLFLAG_RW, sc, 1340275963Srpaulo 0, sysctl_bcm2835_cpufreq_voltage_sdram_c, "I", 1341275963Srpaulo "SDRAM controller voltage" 1342275963Srpaulo "(offset from 1.2V in units of 0.025V)"); 1343275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1344275963Srpaulo OID_AUTO, "voltage_sdram_i", CTLTYPE_INT | CTLFLAG_RW, sc, 1345275963Srpaulo 0, sysctl_bcm2835_cpufreq_voltage_sdram_i, "I", 1346275963Srpaulo "SDRAM I/O voltage (offset from 1.2V in units of 0.025V)"); 1347275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1348275963Srpaulo OID_AUTO, "voltage_sdram_p", CTLTYPE_INT | CTLFLAG_RW, sc, 1349275963Srpaulo 0, sysctl_bcm2835_cpufreq_voltage_sdram_p, "I", 1350275963Srpaulo "SDRAM phy voltage (offset from 1.2V in units of 0.025V)"); 1351275963Srpaulo 1352275963Srpaulo /* Temperature */ 1353275963Srpaulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), 1354275963Srpaulo OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, 1355275963Srpaulo sysctl_bcm2835_cpufreq_temperature, "I", 1356275963Srpaulo "SoC temperature (thousandths of a degree C)"); 1357275963Srpaulo } 1358275963Srpaulo 1359275963Srpaulo /* ARM->VC lock */ 1360275963Srpaulo sema_init(&vc_sema, 1, "vcsema"); 1361275963Srpaulo 1362275963Srpaulo /* register callback for using mbox when interrupts are enabled */ 1363275963Srpaulo sc->init_hook.ich_func = bcm2835_cpufreq_init; 1364275963Srpaulo sc->init_hook.ich_arg = sc; 1365275963Srpaulo 1366275963Srpaulo if (config_intrhook_establish(&sc->init_hook) != 0) { 1367275963Srpaulo device_printf(dev, "config_intrhook_establish failed\n"); 1368275963Srpaulo return (ENOMEM); 1369275963Srpaulo } 1370275963Srpaulo 1371275963Srpaulo /* this device is controlled by cpufreq(4) */ 1372275963Srpaulo cpufreq_register(dev); 1373275963Srpaulo 1374275963Srpaulo return (0); 1375275963Srpaulo} 1376275963Srpaulo 1377275963Srpaulostatic int 1378275963Srpaulobcm2835_cpufreq_detach(device_t dev) 1379275963Srpaulo{ 1380275963Srpaulo struct bcm2835_cpufreq_softc *sc; 1381275963Srpaulo 1382275963Srpaulo sc = device_get_softc(dev); 1383275963Srpaulo 1384275963Srpaulo sema_destroy(&vc_sema); 1385275963Srpaulo 1386275963Srpaulo return (cpufreq_unregister(dev)); 1387275963Srpaulo} 1388275963Srpaulo 1389275963Srpaulostatic int 1390275963Srpaulobcm2835_cpufreq_set(device_t dev, const struct cf_setting *cf) 1391275963Srpaulo{ 1392275963Srpaulo struct bcm2835_cpufreq_softc *sc; 1393275963Srpaulo uint32_t rate_hz, rem; 1394275963Srpaulo int cur_freq, resp_freq, arm_freq, min_freq, core_freq; 1395275963Srpaulo 1396275963Srpaulo if (cf == NULL || cf->freq < 0) 1397275963Srpaulo return (EINVAL); 1398275963Srpaulo 1399275963Srpaulo sc = device_get_softc(dev); 1400275963Srpaulo 1401275963Srpaulo /* setting clock (Hz) */ 1402275963Srpaulo rate_hz = (uint32_t)MHZ2HZ(cf->freq); 1403275963Srpaulo rem = rate_hz % HZSTEP; 1404275963Srpaulo rate_hz -= rem; 1405275963Srpaulo if (rate_hz == 0) 1406275963Srpaulo return (EINVAL); 1407275963Srpaulo 1408275963Srpaulo /* adjust min freq */ 1409275963Srpaulo min_freq = sc->arm_min_freq; 1410275963Srpaulo if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) 1411275963Srpaulo if (min_freq > cpufreq_lowest_freq) 1412275963Srpaulo min_freq = cpufreq_lowest_freq; 1413275963Srpaulo 1414275963Srpaulo if (rate_hz < MHZ2HZ(min_freq) || rate_hz > MHZ2HZ(sc->arm_max_freq)) 1415275963Srpaulo return (EINVAL); 1416275963Srpaulo 1417275963Srpaulo /* set new value and verify it */ 1418275963Srpaulo VC_LOCK(sc); 1419275963Srpaulo cur_freq = bcm2835_cpufreq_get_clock_rate(sc, 1420275963Srpaulo BCM2835_MBOX_CLOCK_ID_ARM); 1421275963Srpaulo resp_freq = bcm2835_cpufreq_set_clock_rate(sc, 1422275963Srpaulo BCM2835_MBOX_CLOCK_ID_ARM, rate_hz); 1423275963Srpaulo DELAY(TRANSITION_LATENCY); 1424275963Srpaulo arm_freq = bcm2835_cpufreq_get_clock_rate(sc, 1425275963Srpaulo BCM2835_MBOX_CLOCK_ID_ARM); 1426275963Srpaulo 1427275963Srpaulo /* 1428275963Srpaulo * if non-turbo and lower than or equal min_freq, 1429275963Srpaulo * clock down core and sdram to default first. 1430275963Srpaulo */ 1431275963Srpaulo if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) { 1432275963Srpaulo core_freq = bcm2835_cpufreq_get_clock_rate(sc, 1433275963Srpaulo BCM2835_MBOX_CLOCK_ID_CORE); 1434275963Srpaulo if (rate_hz > MHZ2HZ(sc->arm_min_freq)) { 1435275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, 1436275963Srpaulo BCM2835_MBOX_CLOCK_ID_CORE, 1437275963Srpaulo MHZ2HZ(sc->core_max_freq)); 1438275963Srpaulo DELAY(TRANSITION_LATENCY); 1439275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, 1440275963Srpaulo BCM2835_MBOX_CLOCK_ID_SDRAM, 1441275963Srpaulo MHZ2HZ(sc->sdram_max_freq)); 1442275963Srpaulo DELAY(TRANSITION_LATENCY); 1443275963Srpaulo } else { 1444275963Srpaulo if (sc->core_min_freq < DEFAULT_CORE_FREQUENCY && 1445275963Srpaulo core_freq > DEFAULT_CORE_FREQUENCY) { 1446275963Srpaulo /* first, down to 250, then down to min */ 1447275963Srpaulo DELAY(TRANSITION_LATENCY); 1448275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, 1449275963Srpaulo BCM2835_MBOX_CLOCK_ID_CORE, 1450275963Srpaulo MHZ2HZ(DEFAULT_CORE_FREQUENCY)); 1451275963Srpaulo DELAY(TRANSITION_LATENCY); 1452275963Srpaulo /* reset core voltage */ 1453275963Srpaulo bcm2835_cpufreq_set_voltage(sc, 1454275963Srpaulo BCM2835_MBOX_VOLTAGE_ID_CORE, 0); 1455275963Srpaulo DELAY(TRANSITION_LATENCY); 1456275963Srpaulo } 1457275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, 1458275963Srpaulo BCM2835_MBOX_CLOCK_ID_CORE, 1459275963Srpaulo MHZ2HZ(sc->core_min_freq)); 1460275963Srpaulo DELAY(TRANSITION_LATENCY); 1461275963Srpaulo bcm2835_cpufreq_set_clock_rate(sc, 1462275963Srpaulo BCM2835_MBOX_CLOCK_ID_SDRAM, 1463275963Srpaulo MHZ2HZ(sc->sdram_min_freq)); 1464275963Srpaulo DELAY(TRANSITION_LATENCY); 1465275963Srpaulo } 1466275963Srpaulo } 1467275963Srpaulo 1468275963Srpaulo VC_UNLOCK(sc); 1469275963Srpaulo 1470275963Srpaulo if (resp_freq < 0 || arm_freq < 0 || resp_freq != arm_freq) { 1471275963Srpaulo device_printf(dev, "wrong freq\n"); 1472275963Srpaulo return (EIO); 1473275963Srpaulo } 1474275963Srpaulo DPRINTF("cpufreq: %d -> %d\n", cur_freq, arm_freq); 1475275963Srpaulo 1476275963Srpaulo return (0); 1477275963Srpaulo} 1478275963Srpaulo 1479275963Srpaulostatic int 1480275963Srpaulobcm2835_cpufreq_get(device_t dev, struct cf_setting *cf) 1481275963Srpaulo{ 1482275963Srpaulo struct bcm2835_cpufreq_softc *sc; 1483275963Srpaulo int arm_freq; 1484275963Srpaulo 1485275963Srpaulo if (cf == NULL) 1486275963Srpaulo return (EINVAL); 1487275963Srpaulo 1488275963Srpaulo sc = device_get_softc(dev); 1489275963Srpaulo memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); 1490275963Srpaulo cf->dev = NULL; 1491275963Srpaulo 1492275963Srpaulo /* get cuurent value */ 1493275963Srpaulo VC_LOCK(sc); 1494275963Srpaulo arm_freq = bcm2835_cpufreq_get_clock_rate(sc, 1495275963Srpaulo BCM2835_MBOX_CLOCK_ID_ARM); 1496275963Srpaulo VC_UNLOCK(sc); 1497275963Srpaulo if (arm_freq < 0) { 1498275963Srpaulo device_printf(dev, "can't get clock\n"); 1499275963Srpaulo return (EINVAL); 1500275963Srpaulo } 1501275963Srpaulo 1502275963Srpaulo /* CPU clock in MHz or 100ths of a percent. */ 1503275963Srpaulo cf->freq = HZ2MHZ(arm_freq); 1504275963Srpaulo /* Voltage in mV. */ 1505275963Srpaulo cf->volts = CPUFREQ_VAL_UNKNOWN; 1506275963Srpaulo /* Power consumed in mW. */ 1507275963Srpaulo cf->power = CPUFREQ_VAL_UNKNOWN; 1508275963Srpaulo /* Transition latency in us. */ 1509275963Srpaulo cf->lat = TRANSITION_LATENCY; 1510275963Srpaulo /* Driver providing this setting. */ 1511275963Srpaulo cf->dev = dev; 1512275963Srpaulo 1513275963Srpaulo return (0); 1514275963Srpaulo} 1515275963Srpaulo 1516275963Srpaulostatic int 1517275963Srpaulobcm2835_cpufreq_make_freq_list(device_t dev, struct cf_setting *sets, 1518275963Srpaulo int *count) 1519275963Srpaulo{ 1520275963Srpaulo struct bcm2835_cpufreq_softc *sc; 1521275963Srpaulo int freq, min_freq, volts, rem; 1522275963Srpaulo int idx; 1523275963Srpaulo 1524275963Srpaulo sc = device_get_softc(dev); 1525275963Srpaulo freq = sc->arm_max_freq; 1526275963Srpaulo min_freq = sc->arm_min_freq; 1527275963Srpaulo 1528275963Srpaulo /* adjust head freq to STEP */ 1529275963Srpaulo rem = freq % MHZSTEP; 1530275963Srpaulo freq -= rem; 1531275963Srpaulo if (freq < min_freq) 1532275963Srpaulo freq = min_freq; 1533275963Srpaulo 1534275963Srpaulo /* if non-turbo, add extra low freq */ 1535275963Srpaulo if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) 1536275963Srpaulo if (min_freq > cpufreq_lowest_freq) 1537275963Srpaulo min_freq = cpufreq_lowest_freq; 1538275963Srpaulo 1539275963Srpaulo /* from freq to min_freq */ 1540275963Srpaulo for (idx = 0; idx < *count && freq >= min_freq; idx++) { 1541275963Srpaulo if (freq > sc->arm_min_freq) 1542275963Srpaulo volts = sc->max_voltage_core; 1543275963Srpaulo else 1544275963Srpaulo volts = sc->min_voltage_core; 1545275963Srpaulo sets[idx].freq = freq; 1546275963Srpaulo sets[idx].volts = volts; 1547275963Srpaulo sets[idx].lat = TRANSITION_LATENCY; 1548275963Srpaulo sets[idx].dev = dev; 1549275963Srpaulo freq -= MHZSTEP; 1550275963Srpaulo } 1551275963Srpaulo *count = ++idx; 1552275963Srpaulo 1553275963Srpaulo return (0); 1554275963Srpaulo} 1555275963Srpaulo 1556275963Srpaulostatic int 1557275963Srpaulobcm2835_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) 1558275963Srpaulo{ 1559275963Srpaulo struct bcm2835_cpufreq_softc *sc; 1560275963Srpaulo 1561275963Srpaulo if (sets == NULL || count == NULL) 1562275963Srpaulo return (EINVAL); 1563275963Srpaulo 1564275963Srpaulo sc = device_get_softc(dev); 1565275963Srpaulo if (sc->arm_min_freq < 0 || sc->arm_max_freq < 0) { 1566275963Srpaulo printf("device is not configured\n"); 1567275963Srpaulo return (EINVAL); 1568275963Srpaulo } 1569275963Srpaulo 1570275963Srpaulo /* fill data with unknown value */ 1571275963Srpaulo memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); 1572275963Srpaulo /* create new array up to count */ 1573275963Srpaulo bcm2835_cpufreq_make_freq_list(dev, sets, count); 1574275963Srpaulo 1575275963Srpaulo return (0); 1576275963Srpaulo} 1577275963Srpaulo 1578275963Srpaulostatic int 1579275963Srpaulobcm2835_cpufreq_type(device_t dev, int *type) 1580275963Srpaulo{ 1581275963Srpaulo 1582275963Srpaulo if (type == NULL) 1583275963Srpaulo return (EINVAL); 1584275963Srpaulo *type = CPUFREQ_TYPE_ABSOLUTE; 1585275963Srpaulo 1586275963Srpaulo return (0); 1587275963Srpaulo} 1588275963Srpaulo 1589275963Srpaulostatic device_method_t bcm2835_cpufreq_methods[] = { 1590275963Srpaulo /* Device interface */ 1591275963Srpaulo DEVMETHOD(device_identify, bcm2835_cpufreq_identify), 1592275963Srpaulo DEVMETHOD(device_probe, bcm2835_cpufreq_probe), 1593275963Srpaulo DEVMETHOD(device_attach, bcm2835_cpufreq_attach), 1594275963Srpaulo DEVMETHOD(device_detach, bcm2835_cpufreq_detach), 1595275963Srpaulo 1596275963Srpaulo /* cpufreq interface */ 1597275963Srpaulo DEVMETHOD(cpufreq_drv_set, bcm2835_cpufreq_set), 1598275963Srpaulo DEVMETHOD(cpufreq_drv_get, bcm2835_cpufreq_get), 1599275963Srpaulo DEVMETHOD(cpufreq_drv_settings, bcm2835_cpufreq_settings), 1600275963Srpaulo DEVMETHOD(cpufreq_drv_type, bcm2835_cpufreq_type), 1601275963Srpaulo 1602275963Srpaulo DEVMETHOD_END 1603275963Srpaulo}; 1604275963Srpaulo 1605275963Srpaulostatic devclass_t bcm2835_cpufreq_devclass; 1606275963Srpaulostatic driver_t bcm2835_cpufreq_driver = { 1607275963Srpaulo "bcm2835_cpufreq", 1608275963Srpaulo bcm2835_cpufreq_methods, 1609275963Srpaulo sizeof(struct bcm2835_cpufreq_softc), 1610275963Srpaulo}; 1611275963Srpaulo 1612275963SrpauloDRIVER_MODULE(bcm2835_cpufreq, cpu, bcm2835_cpufreq_driver, 1613275963Srpaulo bcm2835_cpufreq_devclass, 0, 0); 1614