Author: rpaulo
Date: Sat Dec 20 19:15:10 2014
New Revision: 275963
URL: https://svnweb.freebsd.org/changeset/base/275963

Log:
  Driver for CPU frequency/voltage control on the Raspberry Pi.
  
  Differential Revision:        https://reviews.freebsd.org/D1025
  Submitted by: Daisuke Aoyama aoy...@peach.ne.jp
  Reviewed by:  ian (earlier version), rpaulo
  MFC after:    1 month
  Relnotes:     yes

Added:
  head/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c   (contents, props changed)
  head/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h   (contents, props changed)
Modified:
  head/sys/arm/broadcom/bcm2835/bcm2835_mbox.c
  head/sys/arm/broadcom/bcm2835/bcm2835_mbox.h
  head/sys/arm/broadcom/bcm2835/files.bcm2835
  head/sys/boot/fdt/dts/arm/rpi.dts

Added: head/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c     Sat Dec 20 19:15:10 
2014        (r275963)
@@ -0,0 +1,1818 @@
+/*-
+ * Copyright (C) 2013-2014 Daisuke Aoyama <aoy...@peach.ne.jp>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/sema.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/broadcom/bcm2835/bcm2835_mbox.h>
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
+
+#include "cpufreq_if.h"
+#include "mbox_if.h"
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do {                 \
+       printf("%s:%u: ", __func__, __LINE__);  \
+       printf(fmt, ##__VA_ARGS__);             \
+} while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#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))
+
+#define VC_LOCK(sc) do {                       \
+               sema_wait(&vc_sema);            \
+       } while (0)
+#define VC_UNLOCK(sc) do {                     \
+               sema_post(&vc_sema);            \
+       } while (0)
+
+/* 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;
+
+       /* the values written in mbox */
+       int             voltage_core;
+       int             voltage_sdram;
+       int             voltage_sdram_c;
+       int             voltage_sdram_i;
+       int             voltage_sdram_p;
+       int             turbo_mode;
+
+       /* mbox buffer (physical address) */
+       bus_dma_tag_t   dma_tag;
+       bus_dmamap_t    dma_map;
+       bus_size_t      dma_size;
+       void            *dma_buf;
+       bus_addr_t      dma_phys;
+
+       /* initial hook for waiting mbox intr */
+       struct intr_config_hook init_hook;
+};
+
+static int cpufreq_verbose = 0;
+TUNABLE_INT("hw.bcm2835.cpufreq.verbose", &cpufreq_verbose);
+static int cpufreq_lowest_freq = DEFAULT_LOWEST_FREQ;
+TUNABLE_INT("hw.bcm2835.cpufreq.lowest_freq", &cpufreq_lowest_freq);
+
+#ifdef DEBUG
+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");
+}
+#endif
+
+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;
+       uint8_t *up;
+       device_t mbox;
+       size_t hdr_size;
+       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) {
+               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) {
+               device_printf(sc->dev, "mbox response error\n");
+               return (-1);
+       }
+
+       /* tag = first tag */
+       up = (uint8_t *)msg;
+       hdr_size = sizeof(struct bcm2835_mbox_hdr);
+       tag = (struct bcm2835_mbox_tag_hdr *)(up + hdr_size);
+       /* last = end of buffer specified by header */
+       last = (struct bcm2835_mbox_tag_hdr *)(up + msg->buf_size);
+
+       /* loop unitl end tag (=0x0) */
+       hdr_size = sizeof(struct bcm2835_mbox_tag_hdr);
+       for (idx = 0; tag->tag != 0; idx++) {
+               if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) {
+                       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 */
+               up = (uint8_t *)tag;
+               tag = (struct bcm2835_mbox_tag_hdr *)(up + hdr_size +
+                   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->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 (id=%u)\n",
+                   clock_id);
+               return (MSG_ERROR);
+       }
+
+       /* result (Hz) */
+       rate = (int)msg->body.resp.rate_hz;
+       DPRINTF("clock = %d(Hz)\n", rate);
+       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->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 (id=%u)\n",
+                   clock_id);
+               return (MSG_ERROR);
+       }
+
+       /* result (Hz) */
+       rate = (int)msg->body.resp.rate_hz;
+       DPRINTF("clock = %d(Hz)\n", rate);
+       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->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 (id=%u)\n",
+                   clock_id);
+               return (MSG_ERROR);
+       }
+
+       /* result (Hz) */
+       rate = (int)msg->body.resp.rate_hz;
+       DPRINTF("clock = %d(Hz)\n", rate);
+       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->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 (id=%u)\n",
+                   clock_id);
+               return (MSG_ERROR);
+       }
+
+       /* workaround for core clock */
+       if (clock_id == BCM2835_MBOX_CLOCK_ID_CORE) {
+               /* for safety (may change voltage without changing clock) */
+               DELAY(TRANSITION_LATENCY);
+
+               /*
+                * XXX: the core clock is unable to change at once,
+                * to change certainly, write it twice now.
+                */
+
+               /* 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->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 (id=%u)\n", clock_id);
+                       return (MSG_ERROR);
+               }
+       }
+
+       /* result (Hz) */
+       rate = (int)msg->body.resp.rate_hz;
+       DPRINTF("clock = %d(Hz)\n", rate);
+       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->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;
+       DPRINTF("level = %d\n", level);
+       return (level);
+}
+
+static int
+bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level)
+{
+       struct msg_set_turbo *msg;
+       int value;
+       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);
+       }
+
+       /* replace unknown value to OFF */
+       if (level != BCM2835_MBOX_TURBO_ON && level != BCM2835_MBOX_TURBO_OFF)
+               level = BCM2835_MBOX_TURBO_OFF;
+
+       /* 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->body.req.id = 0;
+       msg->body.req.level = level;
+       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 */
+       value = (int)msg->body.resp.level;
+       DPRINTF("level = %d\n", value);
+       return (value);
+}
+
+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->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;
+       DPRINTF("value = %d\n", value);
+       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->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 max voltage\n");
+               return (MSG_ERROR);
+       }
+
+       /* result (offset from 1.2V) */
+       value = (int)msg->body.resp.value;
+       DPRINTF("value = %d\n", value);
+       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->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;
+       DPRINTF("value = %d\n", value);
+       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->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 set voltage\n");
+               return (MSG_ERROR);
+       }
+
+       /* result (offset from 1.2V) */
+       value = (int)msg->body.resp.value;
+       DPRINTF("value = %d\n", value);
+       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->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 temperature\n");
+               return (MSG_ERROR);
+       }
+
+       /* result (temperature of degree C) */
+       value = (int)msg->body.resp.value;
+       DPRINTF("value = %d\n", value);
+       return (value);
+}
+
+
+
+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\n");
+               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);
+       if (err == MSG_ERROR) {
+               VC_UNLOCK(sc);
+               device_printf(sc->dev, "set clock core_freq error\n");
+               return (EIO);
+       }
+       VC_UNLOCK(sc);
+       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\n");
+               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 */
+       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)
+               sc->turbo_mode = BCM2835_MBOX_TURBO_ON;
+       else
+               sc->turbo_mode = BCM2835_MBOX_TURBO_OFF;
+
+       VC_LOCK(sc);
+       err = bcm2835_cpufreq_set_turbo(sc, sc->turbo_mode);
+       VC_UNLOCK(sc);
+       if (err == MSG_ERROR) {
+               device_printf(sc->dev, "set turbo error\n");
+               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);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to