svn commit: r275963 - in head/sys: arm/broadcom/bcm2835 boot/fdt/dts/arm

Rui Paulo rpaulo at FreeBSD.org
Sat Dec 20 19:15:11 UTC 2014


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 aoyama at 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 <aoyama at 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 ***


More information about the svn-src-head mailing list