From owner-svn-src-stable-10@FreeBSD.ORG Thu Feb 12 00:25:34 2015 Return-Path: Delivered-To: svn-src-stable-10@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 297991A1; Thu, 12 Feb 2015 00:25:34 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 116743D5; Thu, 12 Feb 2015 00:25:34 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t1C0PX1F016467; Thu, 12 Feb 2015 00:25:33 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t1C0PXBt016465; Thu, 12 Feb 2015 00:25:33 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201502120025.t1C0PXBt016465@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Thu, 12 Feb 2015 00:25:33 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r278608 - in stable/10/sys: arm/broadcom/bcm2835 boot/fdt/dts/arm conf dev/ofw powerpc/ofw X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-10@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for only the 10-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 12 Feb 2015 00:25:34 -0000 Author: ian Date: Thu Feb 12 00:25:33 2015 New Revision: 278608 URL: https://svnweb.freebsd.org/changeset/base/278608 Log: MFC r275779, r275963, r276101, r276161, r276297: Move ofw_cpu.c to sys/dev/ofw so that it can be used by other architectures. Add driver for CPU frequency/voltage control on the Raspberry Pi. On initialization, do not use bcm_mbox_intr() to read the pending messages. This fixes the hang that happens on boot while initializing the cpufreq on Raspberry Pi. Added: stable/10/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c - copied unchanged from r275963, head/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h - copied unchanged from r275963, head/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h stable/10/sys/dev/ofw/ofw_cpu.c - copied unchanged from r275779, head/sys/dev/ofw/ofw_cpu.c Deleted: stable/10/sys/powerpc/ofw/ofw_cpu.c Modified: stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox.c stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox.h stable/10/sys/arm/broadcom/bcm2835/files.bcm2835 stable/10/sys/boot/fdt/dts/arm/rpi.dts stable/10/sys/conf/files.powerpc Directory Properties: stable/10/ (props changed) Copied: stable/10/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c (from r275963, head/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c Thu Feb 12 00:25:33 2015 (r278608, copy of r275963, head/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c) @@ -0,0 +1,1818 @@ +/*- + * 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" + +#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 ***