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