Index: sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c =================================================================== --- /dev/null +++ sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c @@ -0,0 +1,1828 @@ +/* + * Copyright (C) 2013-2014 Daisuke Aoyama + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "cpufreq_if.h" +#include "mbox_if.h" + +#define HZ2MHZ(freq) ((freq) / (1000 * 1000)) +#define MHZ2HZ(freq) ((freq) * (1000 * 1000)) +#define OFFSET2MVOLT(val) (1200 + ((val) * 25)) +#define MVOLT2OFFSET(val) (((val) - 1200) / 25) +#define RAW2K(temp) (((temp) + 273150) / 1000) +#define K2RAW(temp) (((temp) * 1000) - 273150) + +#define DEFAULT_ARM_FREQUENCY 700 +#define DEFAULT_CORE_FREQUENCY 250 +#define DEFAULT_SDRAM_FREQUENCY 400 +#define DEFAULT_LOWEST_FREQ 300 +#define TRANSITION_LATENCY 1000 +#define MIN_OVER_VOLTAGE -16 +#define MAX_OVER_VOLTAGE 6 +#define MSG_ERROR -999999999 +#define MHZSTEP 100 +#define HZSTEP (MHZ2HZ(MHZSTEP)) + +#if 0 +#define VC_LOCK(sc) \ + do { \ + mtx_lock(&(sc)->vc_mtx); \ + } while (0) + +#define VC_UNLOCK(sc) \ + do { \ + mtx_unlock(&(sc)->vc_mtx); \ + } while (0) +#else +#define VC_LOCK(sc) \ + do { \ + sema_wait(&vc_sema); \ + } while (0) + +#define VC_UNLOCK(sc) \ + do { \ + sema_post(&vc_sema); \ + } while (0) +#endif + +/* ARM->VC mailbox property semaphore */ +static struct sema vc_sema; + +static struct sysctl_ctx_list bcm2835_sysctl_ctx; + +struct bcm2835_cpufreq_softc { + device_t dev; + int arm_max_freq; + int arm_min_freq; + int core_max_freq; + int core_min_freq; + int sdram_max_freq; + int sdram_min_freq; + int max_voltage_core; + int min_voltage_core; + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_size_t dma_size; + void *dma_buf; + bus_addr_t dma_phys; +#if 0 + struct mtx vc_mtx; +#endif + struct intr_config_hook init_hook; +}; + +static void +bcm2835_dump(const void *data, int len) +{ + const uint8_t *p = (const uint8_t*)data; + int i; + + printf("dump @ %p:\n", data); + for (i = 0; i < len; i++) { + printf("%2.2x ", p[i]); + if ((i % 4) == 3) + printf(" "); + if ((i % 16) == 15) + printf("\n"); + } + printf("\n"); +} + +static int +bcm2835_mbox_call_prop(struct bcm2835_cpufreq_softc *sc) +{ + struct bcm2835_mbox_hdr *msg = (struct bcm2835_mbox_hdr *)sc->dma_buf; + struct bcm2835_mbox_tag_hdr *tag, *last; + device_t mbox; + int idx; + int err; + + /* For multiple calls, locking is not here. The caller must have VC semaphore. */ + + /* get mbox device */ + mbox = devclass_get_device(devclass_find("mbox"), 0); + if (mbox == NULL) { + if (bootverbose) + device_printf(sc->dev, "can't find mbox\n"); + return (-1); + } + + /* go mailbox property */ +#ifdef PROP_DEBUG + bcm2835_dump(msg, 64); +#endif + bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)sc->dma_phys); + MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, &err); + bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_POSTREAD); +#ifdef PROP_DEBUG + bcm2835_dump(msg, 64); +#endif + + /* check response code */ + if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) { + if (bootverbose) + device_printf(sc->dev, "mbox error\n"); + return (-1); + } + + /* tag = first tag */ + tag = (struct bcm2835_mbox_tag_hdr *) + (((uint8_t *)msg) + sizeof(struct bcm2835_mbox_hdr)); + /* last = end of buffer specified by header */ + last = (struct bcm2835_mbox_tag_hdr *) + (((uint8_t *)msg) + msg->buf_size); + + /* loop unitl end tag (=0x0) */ + for (idx = 0; tag->tag != 0; idx++) { + if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) { + if (bootverbose) + device_printf(sc->dev, "tag%d response error\n", idx); + return (-1); + } + /* clear response bit */ + tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + + /* get next tag */ + tag = (struct bcm2835_mbox_tag_hdr *) + (((uint8_t *)tag) + sizeof(struct bcm2835_mbox_tag_hdr) + + tag->val_buf_size); + + /* check buffer size of header */ + if (tag > last) { + device_printf(sc->dev, "mbox buffer size error\n"); + return (-1); + } + } + + return (0); +} + +static int +bcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) +{ + struct msg_get_clock_rate *msg; + int rate; + int err; + + /* + * Get clock rate + * Tag: 0x00030002 + * Request: + * Length: 4 + * Value: + * u32: clock id + * Response: + * Length: 8 + * Value: + * u32: clock id + * u32: rate (in Hz) + */ + + /* using DMA buffer for VC */ + msg = (struct msg_get_clock_rate *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.clock_id = clock_id; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get clock rate (clock id=%u)\n", + clock_id); + return (MSG_ERROR); + } + + /* result (Hz) */ + rate = (int)msg->body.resp.rate_hz; +#ifdef DEBUG + printf("clock = %d(Hz)\n", rate); +#endif + + return (rate); +} + +static int +bcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) +{ + struct msg_get_max_clock_rate *msg; + int rate; + int err; + + /* + * Get max clock rate + * Tag: 0x00030004 + * Request: + * Length: 4 + * Value: + * u32: clock id + * Response: + * Length: 8 + * Value: + * u32: clock id + * u32: rate (in Hz) + */ + + /* using DMA buffer for VC */ + msg = (struct msg_get_max_clock_rate *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.clock_id = clock_id; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get max clock rate (clock id=%u)\n", + clock_id); + return (MSG_ERROR); + } + + /* result (Hz) */ + rate = (int)msg->body.resp.rate_hz; +#ifdef DEBUG + printf("clock = %d(Hz)\n", rate); +#endif + + return (rate); +} + +static int +bcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) +{ + struct msg_get_min_clock_rate *msg; + int rate; + int err; + + /* + * Get min clock rate + * Tag: 0x00030007 + * Request: + * Length: 4 + * Value: + * u32: clock id + * Response: + * Length: 8 + * Value: + * u32: clock id + * u32: rate (in Hz) + */ + + /* using DMA buffer for VC */ + msg = (struct msg_get_min_clock_rate *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.clock_id = clock_id; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get min clock rate (clock id=%u)\n", + clock_id); + return (MSG_ERROR); + } + + /* result (Hz) */ + rate = (int)msg->body.resp.rate_hz; +#ifdef DEBUG + printf("clock = %d(Hz)\n", rate); +#endif + + return (rate); +} + +static int +bcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id, + uint32_t rate_hz) +{ + struct msg_set_clock_rate *msg; + int rate; + int err; + + /* + * Set clock rate + * Tag: 0x00038002 + * Request: + * Length: 8 + * Value: + * u32: clock id + * u32: rate (in Hz) + * Response: + * Length: 8 + * Value: + * u32: clock id + * u32: rate (in Hz) + */ + + /* using DMA buffer for VC */ + msg = (struct msg_set_clock_rate *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.clock_id = clock_id; + msg->body.req.rate_hz = rate_hz; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't set clock rate (clock id=%u)\n", + clock_id); + return (MSG_ERROR); + } + + /* result (Hz) */ + rate = (int)msg->body.resp.rate_hz; +#ifdef DEBUG + printf("clock = %d(Hz)\n", rate); +#endif + + return (rate); +} + +static int +bcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc *sc) +{ + struct msg_get_turbo *msg; + int level; + int err; + + /* + * Get turbo + * Tag: 0x00030009 + * Request: + * Length: 4 + * Value: + * u32: id + * Response: + * Length: 8 + * Value: + * u32: id + * u32: level + */ + + /* using DMA buffer for VC */ + msg = (struct msg_get_turbo *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_TURBO; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.id = 0; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get turbo\n"); + return (MSG_ERROR); + } + + /* result 0=non-turbo, 1=turbo */ + level = (int)msg->body.resp.level; +#ifdef DEBUG + printf("level = %d\n", level); +#endif + + return (level); +} + +static int +bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level) +{ + struct msg_set_turbo *msg; + int err; + + /* + * Set turbo + * Tag: 0x00038009 + * Request: + * Length: 8 + * Value: + * u32: id + * u32: level + * Response: + * Length: 8 + * Value: + * u32: id + * u32: level + */ + + /* using DMA buffer for VC */ + msg = (struct msg_set_turbo *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_TURBO; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.id = 0; + msg->body.req.level = ((level == BCM2835_MBOX_TURBO_ON) + ? BCM2835_MBOX_TURBO_ON : BCM2835_MBOX_TURBO_OFF); + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't set turbo\n"); + return (MSG_ERROR); + } + + /* result 0=non-turbo, 1=turbo */ + level = (int)msg->body.resp.level; +#ifdef DEBUG + printf("level = %d\n", level); +#endif + + return (level); +} + +static int +bcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) +{ + struct msg_get_voltage *msg; + int value; + int err; + + /* + * Get voltage + * Tag: 0x00030003 + * Request: + * Length: 4 + * Value: + * u32: voltage id + * Response: + * Length: 8 + * Value: + * u32: voltage id + * u32: value (offset from 1.2V in units of 0.025V) + */ + + /* using DMA buffer for VC */ + msg = (struct msg_get_voltage *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_VOLTAGE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.voltage_id = voltage_id; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get voltage\n"); + return (MSG_ERROR); + } + + /* result (offset from 1.2V) */ + value = (int)msg->body.resp.value; +#ifdef DEBUG + printf("value = %d\n", value); +#endif + + return (value); +} + +static int +bcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) +{ + struct msg_get_max_voltage *msg; + int value; + int err; + + /* + * Get voltage + * Tag: 0x00030005 + * Request: + * Length: 4 + * Value: + * u32: voltage id + * Response: + * Length: 8 + * Value: + * u32: voltage id + * u32: value (offset from 1.2V in units of 0.025V) + */ + + /* using DMA buffer for VC */ + msg = (struct msg_get_max_voltage *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_VOLTAGE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.voltage_id = voltage_id; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get voltage\n"); + return (MSG_ERROR); + } + + /* result (offset from 1.2V) */ + value = (int)msg->body.resp.value; +#ifdef DEBUG + printf("value = %d\n", value); +#endif + + return (value); +} +static int +bcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) +{ + struct msg_get_min_voltage *msg; + int value; + int err; + + /* + * Get voltage + * Tag: 0x00030008 + * Request: + * Length: 4 + * Value: + * u32: voltage id + * Response: + * Length: 8 + * Value: + * u32: voltage id + * u32: value (offset from 1.2V in units of 0.025V) + */ + + /* using DMA buffer for VC */ + msg = (struct msg_get_min_voltage *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_VOLTAGE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.voltage_id = voltage_id; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get min voltage\n"); + return (MSG_ERROR); + } + + /* result (offset from 1.2V) */ + value = (int)msg->body.resp.value; +#ifdef DEBUG + printf("value = %d\n", value); +#endif + + return (value); +} + +static int +bcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id, + int32_t value) +{ + struct msg_set_voltage *msg; + int err; + + /* + * Set voltage + * Tag: 0x00038003 + * Request: + * Length: 4 + * Value: + * u32: voltage id + * u32: value (offset from 1.2V in units of 0.025V) + * Response: + * Length: 8 + * Value: + * u32: voltage id + * u32: value (offset from 1.2V in units of 0.025V) + */ + + /* + * over_voltage: + * 0 (1.2 V). Values above 6 are only allowed when force_turbo or + * current_limit_override are specified (which set the warranty bit). + */ + if (value > MAX_OVER_VOLTAGE || value < MIN_OVER_VOLTAGE) { + /* currently not supported */ + device_printf(sc->dev, "not supported voltage: %d\n", value); + return (MSG_ERROR); + } + + /* using DMA buffer for VC */ + msg = (struct msg_set_voltage *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_VOLTAGE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.voltage_id = voltage_id; + msg->body.req.value = (uint32_t)value; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get voltage\n"); + return (MSG_ERROR); + } + + /* result (offset from 1.2V) */ + value = (int)msg->body.resp.value; +#ifdef DEBUG + printf("value = %d\n", value); +#endif + + return (value); +} + +static int +bcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc *sc) +{ + struct msg_get_temperature *msg; + int value; + int err; + + /* + * Get temperature + * Tag: 0x00030006 + * Request: + * Length: 4 + * Value: + * u32: temperature id + * Response: + * Length: 8 + * Value: + * u32: temperature id + * u32: value + */ + + /* using DMA buffer for VC */ + msg = (struct msg_get_temperature *)sc->dma_buf; + if (sizeof(*msg) > sc->dma_size) { + device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", + sizeof(*msg), sc->dma_size); + return (MSG_ERROR); + } + + /* setup single tag buffer */ + memset(msg, 0, sizeof(*msg)); + msg->hdr.buf_size = sizeof(*msg); + msg->hdr.code = BCM2835_MBOX_CODE_REQ; + msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_TEMPERATURE; + msg->tag_hdr.val_buf_size = sizeof(msg->body); + msg->tag_hdr.val_len = sizeof(msg->body.req); + msg->tag_hdr.val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + msg->body.req.temperature_id = 0; + msg->end_tag = 0; + + /* call mailbox property */ + err = bcm2835_mbox_call_prop(sc); + if (err) { + device_printf(sc->dev, "can't get voltage\n"); + return (MSG_ERROR); + } + + /* result (temperature of degree C) */ + value = (int)msg->body.resp.value; +#ifdef DEBUG + printf("value = %d\n", value); +#endif + + return (value); +} + + +/* the value written in mbox */ +static int g_voltage_core = 0; +static int g_voltage_sdram = 0; +static int g_voltage_sdram_c = 0; +static int g_voltage_sdram_i = 0; +static int g_voltage_sdram_p = 0; +static int g_turbo_mode = BCM2835_MBOX_TURBO_OFF; + +static int +sysctl_bcm2835_cpufreq_arm_freq(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + VC_LOCK(sc); + err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, val); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set clock arm_freq error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + VC_LOCK(sc); + err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, val); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set clock core_freq error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + /* XXX core clock is unable to change at once, write it twice */ + VC_LOCK(sc); + err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, val); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set clock core_freq error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + VC_LOCK(sc); + err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, val); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set clock sdram_freq error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + /* val = g_turbo_mode; */ + VC_LOCK(sc); + val = bcm2835_cpufreq_get_turbo(sc); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + if (val > 0) + g_turbo_mode = BCM2835_MBOX_TURBO_ON; + else + g_turbo_mode = BCM2835_MBOX_TURBO_OFF; + + VC_LOCK(sc); + err = bcm2835_cpufreq_set_turbo(sc, g_turbo_mode); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set turbo error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + /* val = g_voltage_core; */ + val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) + return (EINVAL); + g_voltage_core = val; + + VC_LOCK(sc); + err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE, g_voltage_core); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set voltage core error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + /* val = g_voltage_sdram_c; */ + val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) + return (EINVAL); + g_voltage_sdram_c = val; + + VC_LOCK(sc); + err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C, + g_voltage_sdram_c); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set voltage sdram_c error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + /* val = g_voltage_sdram_i; */ + val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) + return (EINVAL); + g_voltage_sdram_i = val; + + VC_LOCK(sc); + err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I, + g_voltage_sdram_i); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set voltage sdram_i error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + /* val = g_voltage_sdram_p; */ + val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) + return (EINVAL); + g_voltage_sdram_p = val; + + VC_LOCK(sc); + err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P, + g_voltage_sdram_p); + VC_UNLOCK(sc); + if (err == MSG_ERROR) { + device_printf(sc->dev, "set voltage sdram_p error"); + return (EIO); + } + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* multiple write only */ + if (!req->newptr) + return (EINVAL); + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) + return (EINVAL); + g_voltage_sdram = val; + + VC_LOCK(sc); + err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C, + val); + if (err == MSG_ERROR) { + VC_UNLOCK(sc); + device_printf(sc->dev, "set voltage sdram_c error"); + return (EIO); + } + err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I, + val); + if (err == MSG_ERROR) { + VC_UNLOCK(sc); + device_printf(sc->dev, "set voltage sdram_i error"); + return (EIO); + } + err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P, + val); + if (err == MSG_ERROR) { + VC_UNLOCK(sc); + device_printf(sc->dev, "set voltage sdram_p error"); + return (EIO); + } + VC_UNLOCK(sc); + DELAY(TRANSITION_LATENCY); + + return (0); +} + +static int +sysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + val = bcm2835_cpufreq_get_temperature(sc); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + return (EINVAL); +} + +static int +sysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS) +{ + struct bcm2835_cpufreq_softc *sc = arg1; + int val; + int err; + + /* get realtime value */ + VC_LOCK(sc); + val = bcm2835_cpufreq_get_temperature(sc); + VC_UNLOCK(sc); + if (val == MSG_ERROR) + return (EIO); + + /* 1/1000 celsius (raw) to 1/10 kelvin */ + val = RAW2K(val) * 10; + + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); + + /* write request */ + return (EINVAL); +} + + +static void +bcm2835_cpufreq_init(void *arg) +{ + struct bcm2835_cpufreq_softc *sc = arg; + struct sysctl_ctx_list *ctx; + device_t cpu; + int arm_freq, core_freq, sdram_freq; + int arm_max_freq, arm_min_freq, core_max_freq, core_min_freq; + int sdram_max_freq, sdram_min_freq; + int voltage_core, voltage_sdram_c, voltage_sdram_i, voltage_sdram_p; + int max_voltage_core, min_voltage_core; + int max_voltage_sdram_c, min_voltage_sdram_c; + int max_voltage_sdram_i, min_voltage_sdram_i; + int max_voltage_sdram_p, min_voltage_sdram_p; + int turbo, temperature; + + VC_LOCK(sc); + + /* current clock */ + arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); + core_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); + sdram_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); + + /* max/min clock */ + arm_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); + arm_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); + core_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); + core_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); + sdram_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); + sdram_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); + + /* turbo mode */ + turbo = bcm2835_cpufreq_get_turbo(sc); + if (turbo > 0) + g_turbo_mode = BCM2835_MBOX_TURBO_ON; + else + g_turbo_mode = BCM2835_MBOX_TURBO_OFF; + + /* voltage */ + voltage_core = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); + voltage_sdram_c = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); + voltage_sdram_i = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); + voltage_sdram_p = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); + g_voltage_core = voltage_core; + g_voltage_sdram = voltage_sdram_c; + g_voltage_sdram_c = voltage_sdram_c; + g_voltage_sdram_i = voltage_sdram_i; + g_voltage_sdram_p = voltage_sdram_p; + + /* max/min voltage */ + max_voltage_core = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); + min_voltage_core = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); + max_voltage_sdram_c + = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); + max_voltage_sdram_i + = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); + max_voltage_sdram_p + = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); + min_voltage_sdram_c + = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); + min_voltage_sdram_i + = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); + min_voltage_sdram_p + = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); + + /* temperature */ + temperature = bcm2835_cpufreq_get_temperature(sc); + + /* show result */ + device_printf(sc->dev, "Boot settings:\n"); + device_printf(sc->dev, "current ARM %dMHz, Core %dMHz, SDRAM %dMHz\n", + HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq)); + + device_printf(sc->dev, "max/min ARM %d/%dMHz, Core %d/%dMHz, SDRAM %d/%dMHz, Turbo %s\n", + HZ2MHZ(arm_max_freq), HZ2MHZ(arm_min_freq), + HZ2MHZ(core_max_freq), HZ2MHZ(core_min_freq), + HZ2MHZ(sdram_max_freq), HZ2MHZ(sdram_min_freq), + (g_turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF"); + + device_printf(sc->dev, "current Core %dmV, SDRAM_C %dmV, SDRAM_I %dmV, SDRAM_P %dmV\n", + OFFSET2MVOLT(voltage_core), OFFSET2MVOLT(voltage_sdram_c), + OFFSET2MVOLT(voltage_sdram_i), OFFSET2MVOLT(voltage_sdram_p)); + + device_printf(sc->dev, "max/min Core %d/%dmV, SDRAM_C %d/%dmV, SDRAM_I %d/%dmV, SDRAM_P %d/%dmV\n", + OFFSET2MVOLT(max_voltage_core), OFFSET2MVOLT(min_voltage_core), + OFFSET2MVOLT(max_voltage_sdram_c), OFFSET2MVOLT(min_voltage_sdram_c), + OFFSET2MVOLT(max_voltage_sdram_i), OFFSET2MVOLT(min_voltage_sdram_i), + OFFSET2MVOLT(max_voltage_sdram_p), OFFSET2MVOLT(min_voltage_sdram_p)); + + device_printf(sc->dev, "Temperature %d.%dC\n", (temperature / 1000), + (temperature % 1000) / 100); + + /* keep in softc (MHz/mV) */ + sc->arm_max_freq = HZ2MHZ(arm_max_freq); + sc->arm_min_freq = HZ2MHZ(arm_min_freq); + sc->core_max_freq = HZ2MHZ(core_max_freq); + sc->core_min_freq = HZ2MHZ(core_min_freq); + sc->sdram_max_freq = HZ2MHZ(sdram_max_freq); + sc->sdram_min_freq = HZ2MHZ(sdram_min_freq); + sc->max_voltage_core = OFFSET2MVOLT(max_voltage_core); + sc->min_voltage_core = OFFSET2MVOLT(min_voltage_core); + + /* if turbo is on, set to max values */ + if (g_turbo_mode == BCM2835_MBOX_TURBO_ON) { + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, arm_max_freq); + DELAY(TRANSITION_LATENCY); + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, core_max_freq); + DELAY(TRANSITION_LATENCY); + /* XXX core clock is unable to change at once, write it twice */ + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, core_max_freq); + DELAY(TRANSITION_LATENCY); + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_max_freq); + DELAY(TRANSITION_LATENCY); + } else { + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, arm_min_freq); + DELAY(TRANSITION_LATENCY); + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, core_min_freq); + DELAY(TRANSITION_LATENCY); + /* XXX core clock is unable to change at once, write it twice */ + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, core_min_freq); + DELAY(TRANSITION_LATENCY); + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_min_freq); + DELAY(TRANSITION_LATENCY); + } + + VC_UNLOCK(sc); + + /* add human readable temperature to dev.cpu node */ + cpu = device_get_parent(sc->dev); + if (cpu != NULL) { + ctx = device_get_sysctl_ctx(cpu); + SYSCTL_ADD_PROC(ctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO, + "temperature", CTLTYPE_INT | CTLFLAG_RD, + sc, 0, sysctl_bcm2835_devcpu_temperature, "IK", + "Current SoC temperature"); + } + + /* release this hook (continue boot) */ + config_intrhook_disestablish(&sc->init_hook); +} + +static void +bcm2835_cpufreq_identify(driver_t *driver, device_t parent) +{ + +#ifdef DEBUG + printf("%s(driver=%p, parent=%p)\n", __func__, driver, parent); +#endif + if (device_find_child(parent, "bcm2835_cpufreq", -1) != NULL) + return; + if (BUS_ADD_CHILD(parent, 0, "bcm2835_cpufreq", -1) == NULL) + device_printf(parent, "add child failed\n"); +} + +static int +bcm2835_cpufreq_probe(device_t dev) +{ + + device_set_desc(dev, "CPU Frequency Control"); + + return (0); +} + +static void +bcm2835_cpufreq_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) +{ + bus_addr_t *addr; + + if (err) + return; + + addr = (bus_addr_t *)arg; + *addr = PHYS_TO_VCBUS(segs[0].ds_addr); +} + +static int +bcm2835_cpufreq_attach(device_t dev) +{ + struct bcm2835_cpufreq_softc *sc; + struct sysctl_oid *oid; + int err; + + sc = device_get_softc(dev); + memset(sc, 0, sizeof(*sc)); + sc->dev = dev; + + /* initial values */ + sc->arm_max_freq = -1; + sc->arm_min_freq = -1; + sc->core_max_freq = -1; + sc->core_min_freq = -1; + sc->sdram_max_freq = -1; + sc->sdram_min_freq = -1; + sc->max_voltage_core = 0; + sc->min_voltage_core = 0; + + /* create VC mbox buffer */ + sc->dma_size = PAGE_SIZE; + err = bus_dma_tag_create( + bus_get_dma_tag(sc->dev), + PAGE_SIZE, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sc->dma_size, 1, /* maxsize, nsegments */ + sc->dma_size, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->dma_tag); + if (err) { + device_printf(dev, "can't create DMA tag\n"); + return (ENXIO); + } + + err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->dma_buf, + 0, &sc->dma_map); + if (err) { + bus_dma_tag_destroy(sc->dma_tag); + device_printf(dev, "can't allocate dmamem\n"); + return (ENXIO); + } + + err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->dma_buf, + sc->dma_size, bcm2835_cpufreq_cb, &sc->dma_phys, + BUS_DMA_NOWAIT); + if (err) { + bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); + bus_dma_tag_destroy(sc->dma_tag); + device_printf(dev, "can't load DMA map\n"); + return (ENXIO); + } + /* OK, ready to use VC buffer */ + + /* setup sysctl at first device */ + if (device_get_unit(dev) == 0) { + sysctl_ctx_init(&bcm2835_sysctl_ctx); + /* create node for hw.cpufreq */ + oid = SYSCTL_ADD_NODE(&bcm2835_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), + OID_AUTO, "cpufreq", CTLFLAG_RD, NULL, ""); + + /* Frequency (Hz) */ + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "arm_freq", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, sysctl_bcm2835_cpufreq_arm_freq, "IU", + "ARM frequency (Hz)"); + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "core_freq", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, sysctl_bcm2835_cpufreq_core_freq, "IU", + "Core frequency (Hz)"); + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "sdram_freq", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, sysctl_bcm2835_cpufreq_sdram_freq, "IU", + "SDRAM frequency (Hz)"); + + /* Turbo state */ + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "turbo", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, sysctl_bcm2835_cpufreq_turbo, "IU", + "Disables dynamic clocking"); + + /* Voltage (offset from 1.2V in units of 0.025V) */ + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "voltage_core", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, sysctl_bcm2835_cpufreq_voltage_core, "I", + "ARM/GPU core voltage (offset from 1.2V in units of 0.025V)"); + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "voltage_sdram", CTLTYPE_INT | CTLFLAG_WR, + sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram, "I", + "SDRAM voltage (offset from 1.2V in units of 0.025V)"); + + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "voltage_sdram_c", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_c, "I", + "SDRAM controller voltage (offset from 1.2V in units of 0.025V)"); + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "voltage_sdram_i", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_i, "I", + "SDRAM I/O voltage (offset from 1.2V in units of 0.025V)"); + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "voltage_sdram_p", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_p, "I", + "SDRAM phy voltage (offset from 1.2V in units of 0.025V)"); + + /* Temperature */ + SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, + "temperature", CTLTYPE_INT | CTLFLAG_RD, + sc, 0, sysctl_bcm2835_cpufreq_temperature, "I", + "SoC temperature (thousandths of a degree C)"); + } + + /* VC lock */ +#if 0 + mtx_init(&sc->vc_mtx, "cpufreq", NULL, MTX_DEF); +#endif + sema_init(&vc_sema, 1, "vcsema"); + + /* register callback for using mbox when interrupts are enabled */ + sc->init_hook.ich_func = bcm2835_cpufreq_init; + sc->init_hook.ich_arg = sc; + + if (config_intrhook_establish(&sc->init_hook) != 0) { + bus_dmamap_unload(sc->dma_tag, sc->dma_map); + bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); + bus_dma_tag_destroy(sc->dma_tag); + device_printf(dev, "config_intrhook_establish failed\n"); + return (ENOMEM); + } + + /* this device is controlled by cpufreq(4) */ + cpufreq_register(dev); + + return (0); +} + +static int +bcm2835_cpufreq_detach(device_t dev) +{ + struct bcm2835_cpufreq_softc *sc; + + sc = device_get_softc(dev); + +#if 0 + mtx_destroy(&sc->vc_mtx); +#endif + sema_destroy(&vc_sema); + + if (sc->dma_phys != 0) + bus_dmamap_unload(sc->dma_tag, sc->dma_map); + if (sc->dma_buf != NULL) + bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); + if (sc->dma_tag != NULL) + bus_dma_tag_destroy(sc->dma_tag); + + return (cpufreq_unregister(dev)); +} + +static int +bcm2835_cpufreq_set(device_t dev, const struct cf_setting *cf) +{ + struct bcm2835_cpufreq_softc *sc; + uint32_t rate_hz, rem; + int cur_freq, resp_freq, arm_freq, min_freq, core_freq; + + if (cf == NULL || cf->freq < 0) + return (EINVAL); + + sc = device_get_softc(dev); + + VC_LOCK(sc); + + /* setting clock (Hz) */ + rate_hz = (uint32_t)MHZ2HZ(cf->freq); + rem = rate_hz % HZSTEP; + rate_hz -= rem; + if (rate_hz == 0) { + VC_UNLOCK(sc); + return (EINVAL); + } + + /* adjust min freq */ + min_freq = sc->arm_min_freq; + if (g_turbo_mode != BCM2835_MBOX_TURBO_ON) + if (min_freq > DEFAULT_LOWEST_FREQ) + min_freq = DEFAULT_LOWEST_FREQ; + + if (rate_hz < MHZ2HZ(min_freq) || rate_hz > MHZ2HZ(sc->arm_max_freq)) { + VC_UNLOCK(sc); + return (EINVAL); + } + + /* set new value and verify it */ + cur_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); + resp_freq = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, rate_hz); + DELAY(TRANSITION_LATENCY); + arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); + + /* if non-turbo and lower than or equal min freq, down core and sdram */ + if (g_turbo_mode != BCM2835_MBOX_TURBO_ON) { + core_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); + if (rate_hz > MHZ2HZ(sc->arm_min_freq)) { + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, + MHZ2HZ(sc->core_max_freq)); + DELAY(TRANSITION_LATENCY); + /* XXX core clock is unable to change at once, write it twice */ + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, + MHZ2HZ(sc->core_max_freq)); + DELAY(TRANSITION_LATENCY); + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, + MHZ2HZ(sc->sdram_max_freq)); + DELAY(TRANSITION_LATENCY); + } else { + if (sc->core_min_freq < DEFAULT_CORE_FREQUENCY + && core_freq > DEFAULT_CORE_FREQUENCY) { + /* first, down to 250, then down to min */ + DELAY(TRANSITION_LATENCY); + bcm2835_cpufreq_set_clock_rate(sc, + BCM2835_MBOX_CLOCK_ID_CORE, + MHZ2HZ(DEFAULT_CORE_FREQUENCY)); + DELAY(TRANSITION_LATENCY); + /* XXX write it twice */ + bcm2835_cpufreq_set_clock_rate(sc, + BCM2835_MBOX_CLOCK_ID_CORE, + MHZ2HZ(DEFAULT_CORE_FREQUENCY)); + DELAY(TRANSITION_LATENCY); + /* reset core voltage */ + bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE, + 0); + DELAY(TRANSITION_LATENCY); + } + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, + MHZ2HZ(sc->core_min_freq)); + DELAY(TRANSITION_LATENCY); + /* XXX core clock is unable to change at once, write it twice */ + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, + MHZ2HZ(sc->core_min_freq)); + DELAY(TRANSITION_LATENCY); + bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, + MHZ2HZ(sc->sdram_min_freq)); + DELAY(TRANSITION_LATENCY); +#if 0 + /* set new voltage if max=0 and lower than default(=0) */ + if (MVOLT2OFFSET(sc->max_voltage_core) == 0 + && MVOLT2OFFSET(sc->min_voltage_core) < 0) { + bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE, + MVOLT2OFFSET(sc->min_voltage_core)); + DELAY(TRANSITION_LATENCY); + } +#endif + } + } + + VC_UNLOCK(sc); + + if (resp_freq < 0 || arm_freq < 0 || resp_freq != arm_freq) { + device_printf(dev, "wrong freq\n"); + return (EIO); + } +#ifdef DEBUG + if (bootverbose) + device_printf(dev, "cpufreq: %d -> %d\n", cur_freq, arm_freq); +#endif + + return (0); +} + +static int +bcm2835_cpufreq_get(device_t dev, struct cf_setting *cf) +{ + struct bcm2835_cpufreq_softc *sc; + int arm_freq; + + if (cf == NULL) + return (EINVAL); + + sc = device_get_softc(dev); + + memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); + cf->dev = NULL; + + + /* get cuurent value */ + VC_LOCK(sc); + arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); + VC_UNLOCK(sc); + if (arm_freq < 0) { + device_printf(dev, "can't get clock\n"); + return (EINVAL); + } + + /* CPU clock in MHz or 100ths of a percent. */ + cf->freq = HZ2MHZ(arm_freq); + /* Voltage in mV. */ + cf->volts = CPUFREQ_VAL_UNKNOWN; + /* Power consumed in mW. */ + cf->power = CPUFREQ_VAL_UNKNOWN; + /* Transition latency in us. */ + cf->lat = TRANSITION_LATENCY; + /* Driver providing this setting. */ + cf->dev = dev; + + return (0); +} + +static int +bcm2835_cpufreq_make_freq_list(device_t dev, struct cf_setting *sets, int *count) +{ + struct bcm2835_cpufreq_softc *sc; + int freq, min_freq, volts, rem; + int idx; + + sc = device_get_softc(dev); + freq = sc->arm_max_freq; + min_freq = sc->arm_min_freq; + + /* adjust head freq to STEP */ + rem = freq % MHZSTEP; + freq -= rem; + if (freq < min_freq) + freq = min_freq; + + /* if non-turbo, add extra low freq */ + if (g_turbo_mode != BCM2835_MBOX_TURBO_ON) + if (min_freq > DEFAULT_LOWEST_FREQ) + min_freq = DEFAULT_LOWEST_FREQ; + + /* from freq to min_freq */ + for (idx = 0; idx < *count && freq >= min_freq; idx++) { + if (freq > sc->arm_min_freq) + volts = sc->max_voltage_core; + else + volts = sc->min_voltage_core; + sets[idx].freq = freq; + sets[idx].volts = volts; + sets[idx].lat = TRANSITION_LATENCY; + sets[idx].dev = dev; + freq -= MHZSTEP; + } + *count = ++idx; + + return (0); +} + +static int +bcm2835_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) +{ + struct bcm2835_cpufreq_softc *sc; + + if (sets == NULL || count == NULL) + return (EINVAL); + + sc = device_get_softc(dev); +#if 0 + if (*count < sc->max_states) + return (E2BIG); +#endif + if (sc->arm_min_freq < 0 || sc->arm_max_freq < 0) { + printf("device is not configured\n"); + return (EINVAL); + } + + /* fill data with unknown value */ + memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); + /* create new array */ + bcm2835_cpufreq_make_freq_list(dev, sets, count); + + return (0); +} + +static int +bcm2835_cpufreq_type(device_t dev, int *type) +{ + + if (type == NULL) + return (EINVAL); + *type = CPUFREQ_TYPE_ABSOLUTE; + + return (0); +} + +static device_method_t bcm2835_cpufreq_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, bcm2835_cpufreq_identify), + DEVMETHOD(device_probe, bcm2835_cpufreq_probe), + DEVMETHOD(device_attach, bcm2835_cpufreq_attach), + DEVMETHOD(device_detach, bcm2835_cpufreq_detach), + + /* cpufreq interface */ + DEVMETHOD(cpufreq_drv_set, bcm2835_cpufreq_set), + DEVMETHOD(cpufreq_drv_get, bcm2835_cpufreq_get), + DEVMETHOD(cpufreq_drv_settings, bcm2835_cpufreq_settings), + DEVMETHOD(cpufreq_drv_type, bcm2835_cpufreq_type), + + DEVMETHOD_END +}; + +static devclass_t bcm2835_cpufreq_devclass; +static driver_t bcm2835_cpufreq_driver = { + "bcm2835_cpufreq", + bcm2835_cpufreq_methods, + sizeof(struct bcm2835_cpufreq_softc), +}; + +DRIVER_MODULE(bcm2835_cpufreq, cpu, bcm2835_cpufreq_driver, bcm2835_cpufreq_devclass, 0, 0); Index: sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h =================================================================== --- /dev/null +++ sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2013-2014 Daisuke Aoyama + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _BCM2835_MBOX_PROP_H_ +#define _BCM2835_MBOX_PROP_H_ + +#include +#include + +/* + * Mailbox property interface: + * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface + */ +#define BCM2835_MBOX_CODE_REQ 0 +#define BCM2835_MBOX_CODE_RESP_SUCCESS 0x80000000 +#define BCM2835_MBOX_CODE_RESP_ERROR 0x80000001 +#define BCM2835_MBOX_TAG_VAL_LEN_RESPONSE 0x80000000 + +struct bcm2835_mbox_hdr { + uint32_t buf_size; + uint32_t code; +}; + +struct bcm2835_mbox_tag_hdr { + uint32_t tag; + uint32_t val_buf_size; + uint32_t val_len; +}; + +#define BCM2835_MBOX_CLOCK_ID_EMMC 0x00000001 +#define BCM2835_MBOX_CLOCK_ID_UART 0x00000002 +#define BCM2835_MBOX_CLOCK_ID_ARM 0x00000003 +#define BCM2835_MBOX_CLOCK_ID_CORE 0x00000004 +#define BCM2835_MBOX_CLOCK_ID_V3D 0x00000005 +#define BCM2835_MBOX_CLOCK_ID_H264 0x00000006 +#define BCM2835_MBOX_CLOCK_ID_ISP 0x00000007 +#define BCM2835_MBOX_CLOCK_ID_SDRAM 0x00000008 +#define BCM2835_MBOX_CLOCK_ID_PIXEL 0x00000009 +#define BCM2835_MBOX_CLOCK_ID_PWM 0x0000000a + +#define BCM2835_MBOX_TAG_GET_CLOCK_RATE 0x00030002 +#define BCM2835_MBOX_TAG_SET_CLOCK_RATE 0x00038002 +#define BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE 0x00030004 +#define BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE 0x00030007 + +struct msg_get_clock_rate { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t clock_id; + } req; + struct { + uint32_t clock_id; + uint32_t rate_hz; + } resp; + } body; + uint32_t end_tag; +}; + +struct msg_set_clock_rate { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t clock_id; + uint32_t rate_hz; + } req; + struct { + uint32_t clock_id; + uint32_t rate_hz; + } resp; + } body; + uint32_t end_tag; +}; + +struct msg_get_max_clock_rate { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t clock_id; + } req; + struct { + uint32_t clock_id; + uint32_t rate_hz; + } resp; + } body; + uint32_t end_tag; +}; + +struct msg_get_min_clock_rate { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t clock_id; + } req; + struct { + uint32_t clock_id; + uint32_t rate_hz; + } resp; + } body; + uint32_t end_tag; +}; + +#define BCM2835_MBOX_TURBO_ON 1 +#define BCM2835_MBOX_TURBO_OFF 0 + +#define BCM2835_MBOX_TAG_GET_TURBO 0x00030009 +#define BCM2835_MBOX_TAG_SET_TURBO 0x00038009 + +struct msg_get_turbo { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t id; + } req; + struct { + uint32_t id; + uint32_t level; + } resp; + } body; + uint32_t end_tag; +}; + +struct msg_set_turbo { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t id; + uint32_t level; + } req; + struct { + uint32_t id; + uint32_t level; + } resp; + } body; + uint32_t end_tag; +}; + +#define BCM2835_MBOX_VOLTAGE_ID_CORE 0x00000001 +#define BCM2835_MBOX_VOLTAGE_ID_SDRAM_C 0x00000002 +#define BCM2835_MBOX_VOLTAGE_ID_SDRAM_P 0x00000003 +#define BCM2835_MBOX_VOLTAGE_ID_SDRAM_I 0x00000004 + +#define BCM2835_MBOX_TAG_GET_VOLTAGE 0x00030003 +#define BCM2835_MBOX_TAG_SET_VOLTAGE 0x00038003 +#define BCM2835_MBOX_TAG_GET_MAX_VOLTAGE 0x00030005 +#define BCM2835_MBOX_TAG_GET_MIN_VOLTAGE 0x00030008 + +struct msg_get_voltage { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t voltage_id; + } req; + struct { + uint32_t voltage_id; + uint32_t value; + } resp; + } body; + uint32_t end_tag; +}; + +struct msg_set_voltage { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t voltage_id; + uint32_t value; + } req; + struct { + uint32_t voltage_id; + uint32_t value; + } resp; + } body; + uint32_t end_tag; +}; + +struct msg_get_max_voltage { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t voltage_id; + } req; + struct { + uint32_t voltage_id; + uint32_t value; + } resp; + } body; + uint32_t end_tag; +}; +struct msg_get_min_voltage { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t voltage_id; + } req; + struct { + uint32_t voltage_id; + uint32_t value; + } resp; + } body; + uint32_t end_tag; +}; + +#define BCM2835_MBOX_TAG_GET_TEMPERATURE 0x00030006 +#define BCM2835_MBOX_TAG_SET_TEMPERATURE 0x0003000a + +struct msg_get_temperature { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t temperature_id; + } req; + struct { + uint32_t temperature_id; + uint32_t value; + } resp; + } body; + uint32_t end_tag; +}; + +struct msg_get_max_temperature { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t temperature_id; + } req; + struct { + uint32_t temperature_id; + uint32_t value; + } resp; + } body; + uint32_t end_tag; +}; + +#endif /* _BCM2835_MBOX_PROP_H_ */