xref: /freebsd/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c (revision 9d6672e13bcdcb4d21f78db028570820ec9c2102)
1b9450e43SRui Paulo /*-
211cede48SLuiz Otavio O Souza  * Copyright (C) 2013-2015 Daisuke Aoyama <aoyama@peach.ne.jp>
3b9450e43SRui Paulo  * All rights reserved.
4b9450e43SRui Paulo  *
5b9450e43SRui Paulo  * Redistribution and use in source and binary forms, with or without
6b9450e43SRui Paulo  * modification, are permitted provided that the following conditions
7b9450e43SRui Paulo  * are met:
8b9450e43SRui Paulo  * 1. Redistributions of source code must retain the above copyright
9b9450e43SRui Paulo  *    notice, this list of conditions and the following disclaimer.
10b9450e43SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
11b9450e43SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
12b9450e43SRui Paulo  *    documentation and/or other materials provided with the distribution.
13b9450e43SRui Paulo  *
14b9450e43SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15b9450e43SRui Paulo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16b9450e43SRui Paulo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17b9450e43SRui Paulo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18b9450e43SRui Paulo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19b9450e43SRui Paulo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20b9450e43SRui Paulo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21b9450e43SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22b9450e43SRui Paulo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23b9450e43SRui Paulo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24b9450e43SRui Paulo  * SUCH DAMAGE.
25b9450e43SRui Paulo  *
26b9450e43SRui Paulo  */
27b9450e43SRui Paulo 
28b9450e43SRui Paulo #include <sys/cdefs.h>
29b9450e43SRui Paulo __FBSDID("$FreeBSD$");
30b9450e43SRui Paulo 
31b9450e43SRui Paulo #include <sys/param.h>
32b9450e43SRui Paulo #include <sys/systm.h>
33b9450e43SRui Paulo #include <sys/bus.h>
34b9450e43SRui Paulo #include <sys/cpu.h>
35b9450e43SRui Paulo #include <sys/kernel.h>
36b9450e43SRui Paulo #include <sys/lock.h>
37b9450e43SRui Paulo #include <sys/malloc.h>
38b9450e43SRui Paulo #include <sys/module.h>
39b9450e43SRui Paulo #include <sys/mutex.h>
40b9450e43SRui Paulo #include <sys/sema.h>
41b9450e43SRui Paulo #include <sys/sysctl.h>
42b9450e43SRui Paulo 
43b9450e43SRui Paulo #include <machine/bus.h>
44b9450e43SRui Paulo #include <machine/cpu.h>
45b9450e43SRui Paulo #include <machine/intr.h>
46b9450e43SRui Paulo 
47b9450e43SRui Paulo #include <arm/broadcom/bcm2835/bcm2835_mbox.h>
48b9450e43SRui Paulo #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
49b9450e43SRui Paulo #include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
50b9450e43SRui Paulo 
51b9450e43SRui Paulo #include "cpufreq_if.h"
52b9450e43SRui Paulo #include "mbox_if.h"
53b9450e43SRui Paulo 
54b9450e43SRui Paulo #ifdef DEBUG
55b9450e43SRui Paulo #define DPRINTF(fmt, ...) do {			\
56b9450e43SRui Paulo 	printf("%s:%u: ", __func__, __LINE__);	\
57b9450e43SRui Paulo 	printf(fmt, ##__VA_ARGS__);		\
58b9450e43SRui Paulo } while (0)
59b9450e43SRui Paulo #else
60b9450e43SRui Paulo #define DPRINTF(fmt, ...)
61b9450e43SRui Paulo #endif
62b9450e43SRui Paulo 
63b9450e43SRui Paulo #define	HZ2MHZ(freq) ((freq) / (1000 * 1000))
64b9450e43SRui Paulo #define	MHZ2HZ(freq) ((freq) * (1000 * 1000))
6511cede48SLuiz Otavio O Souza 
6611cede48SLuiz Otavio O Souza #ifdef SOC_BCM2836
6711cede48SLuiz Otavio O Souza #define	OFFSET2MVOLT(val) (((val) / 1000))
6811cede48SLuiz Otavio O Souza #define	MVOLT2OFFSET(val) (((val) * 1000))
6911cede48SLuiz Otavio O Souza #define	DEFAULT_ARM_FREQUENCY	 600
7011cede48SLuiz Otavio O Souza #define	DEFAULT_LOWEST_FREQ	 600
7111cede48SLuiz Otavio O Souza #else
72b9450e43SRui Paulo #define	OFFSET2MVOLT(val) (1200 + ((val) * 25))
73b9450e43SRui Paulo #define	MVOLT2OFFSET(val) (((val) - 1200) / 25)
74b9450e43SRui Paulo #define	DEFAULT_ARM_FREQUENCY	 700
7511cede48SLuiz Otavio O Souza #define	DEFAULT_LOWEST_FREQ	 300
7611cede48SLuiz Otavio O Souza #endif
77b9450e43SRui Paulo #define	DEFAULT_CORE_FREQUENCY	 250
78b9450e43SRui Paulo #define	DEFAULT_SDRAM_FREQUENCY	 400
79b9450e43SRui Paulo #define	TRANSITION_LATENCY	1000
80b9450e43SRui Paulo #define	MIN_OVER_VOLTAGE	 -16
81b9450e43SRui Paulo #define	MAX_OVER_VOLTAGE	   6
82b9450e43SRui Paulo #define	MSG_ERROR	  -999999999
83b9450e43SRui Paulo #define	MHZSTEP			 100
84b9450e43SRui Paulo #define	HZSTEP	   (MHZ2HZ(MHZSTEP))
85*9d6672e1SLuiz Otavio O Souza #define	TZ_ZEROC		2731
86b9450e43SRui Paulo 
87b9450e43SRui Paulo #define VC_LOCK(sc) do {			\
88b9450e43SRui Paulo 		sema_wait(&vc_sema);		\
89b9450e43SRui Paulo 	} while (0)
90b9450e43SRui Paulo #define VC_UNLOCK(sc) do {			\
91b9450e43SRui Paulo 		sema_post(&vc_sema);		\
92b9450e43SRui Paulo 	} while (0)
93b9450e43SRui Paulo 
94b9450e43SRui Paulo /* ARM->VC mailbox property semaphore */
95b9450e43SRui Paulo static struct sema vc_sema;
96b9450e43SRui Paulo 
97b9450e43SRui Paulo static struct sysctl_ctx_list bcm2835_sysctl_ctx;
98b9450e43SRui Paulo 
99b9450e43SRui Paulo struct bcm2835_cpufreq_softc {
100b9450e43SRui Paulo 	device_t	dev;
101b9450e43SRui Paulo 	int		arm_max_freq;
102b9450e43SRui Paulo 	int		arm_min_freq;
103b9450e43SRui Paulo 	int		core_max_freq;
104b9450e43SRui Paulo 	int		core_min_freq;
105b9450e43SRui Paulo 	int		sdram_max_freq;
106b9450e43SRui Paulo 	int		sdram_min_freq;
107b9450e43SRui Paulo 	int		max_voltage_core;
108b9450e43SRui Paulo 	int		min_voltage_core;
109b9450e43SRui Paulo 
110b9450e43SRui Paulo 	/* the values written in mbox */
111b9450e43SRui Paulo 	int		voltage_core;
112b9450e43SRui Paulo 	int		voltage_sdram;
113b9450e43SRui Paulo 	int		voltage_sdram_c;
114b9450e43SRui Paulo 	int		voltage_sdram_i;
115b9450e43SRui Paulo 	int		voltage_sdram_p;
116b9450e43SRui Paulo 	int		turbo_mode;
117b9450e43SRui Paulo 
118b9450e43SRui Paulo 	/* initial hook for waiting mbox intr */
119b9450e43SRui Paulo 	struct intr_config_hook	init_hook;
120b9450e43SRui Paulo };
121b9450e43SRui Paulo 
122b9450e43SRui Paulo static int cpufreq_verbose = 0;
123b9450e43SRui Paulo TUNABLE_INT("hw.bcm2835.cpufreq.verbose", &cpufreq_verbose);
124b9450e43SRui Paulo static int cpufreq_lowest_freq = DEFAULT_LOWEST_FREQ;
125b9450e43SRui Paulo TUNABLE_INT("hw.bcm2835.cpufreq.lowest_freq", &cpufreq_lowest_freq);
126b9450e43SRui Paulo 
127e9faba9dSLuiz Otavio O Souza #ifdef PROP_DEBUG
128b9450e43SRui Paulo static void
129b9450e43SRui Paulo bcm2835_dump(const void *data, int len)
130b9450e43SRui Paulo {
131b9450e43SRui Paulo 	const uint8_t *p = (const uint8_t*)data;
132b9450e43SRui Paulo 	int i;
133b9450e43SRui Paulo 
134b9450e43SRui Paulo 	printf("dump @ %p:\n", data);
135b9450e43SRui Paulo 	for (i = 0; i < len; i++) {
136b9450e43SRui Paulo 		printf("%2.2x ", p[i]);
137b9450e43SRui Paulo 		if ((i % 4) == 3)
138b9450e43SRui Paulo 			printf(" ");
139b9450e43SRui Paulo 		if ((i % 16) == 15)
140b9450e43SRui Paulo 			printf("\n");
141b9450e43SRui Paulo 	}
142b9450e43SRui Paulo 	printf("\n");
143b9450e43SRui Paulo }
144b9450e43SRui Paulo #endif
145b9450e43SRui Paulo 
146b9450e43SRui Paulo static int
147b9450e43SRui Paulo bcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc *sc,
148b9450e43SRui Paulo     uint32_t clock_id)
149b9450e43SRui Paulo {
150cd3903c6SOleksandr Tymoshenko 	struct msg_get_clock_rate msg;
151b9450e43SRui Paulo 	int rate;
152b9450e43SRui Paulo 	int err;
153b9450e43SRui Paulo 
154b9450e43SRui Paulo 	/*
155b9450e43SRui Paulo 	 * Get clock rate
156b9450e43SRui Paulo 	 *   Tag: 0x00030002
157b9450e43SRui Paulo 	 *   Request:
158b9450e43SRui Paulo 	 *     Length: 4
159b9450e43SRui Paulo 	 *     Value:
160b9450e43SRui Paulo 	 *       u32: clock id
161b9450e43SRui Paulo 	 *   Response:
162b9450e43SRui Paulo 	 *     Length: 8
163b9450e43SRui Paulo 	 *     Value:
164b9450e43SRui Paulo 	 *       u32: clock id
165b9450e43SRui Paulo 	 *       u32: rate (in Hz)
166b9450e43SRui Paulo 	 */
167b9450e43SRui Paulo 
168b9450e43SRui Paulo 	/* setup single tag buffer */
169cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
170cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
171cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
172cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE;
173cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
174cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
175cd3903c6SOleksandr Tymoshenko 	msg.body.req.clock_id = clock_id;
176cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
177b9450e43SRui Paulo 
178b9450e43SRui Paulo 	/* call mailbox property */
179cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
180b9450e43SRui Paulo 	if (err) {
181b9450e43SRui Paulo 		device_printf(sc->dev, "can't get clock rate (id=%u)\n",
182b9450e43SRui Paulo 		    clock_id);
183b9450e43SRui Paulo 		return (MSG_ERROR);
184b9450e43SRui Paulo 	}
185b9450e43SRui Paulo 
186b9450e43SRui Paulo 	/* result (Hz) */
187cd3903c6SOleksandr Tymoshenko 	rate = (int)msg.body.resp.rate_hz;
188b9450e43SRui Paulo 	DPRINTF("clock = %d(Hz)\n", rate);
189b9450e43SRui Paulo 	return (rate);
190b9450e43SRui Paulo }
191b9450e43SRui Paulo 
192b9450e43SRui Paulo static int
193b9450e43SRui Paulo bcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc *sc,
194b9450e43SRui Paulo     uint32_t clock_id)
195b9450e43SRui Paulo {
196cd3903c6SOleksandr Tymoshenko 	struct msg_get_max_clock_rate msg;
197b9450e43SRui Paulo 	int rate;
198b9450e43SRui Paulo 	int err;
199b9450e43SRui Paulo 
200b9450e43SRui Paulo 	/*
201b9450e43SRui Paulo 	 * Get max clock rate
202b9450e43SRui Paulo 	 *   Tag: 0x00030004
203b9450e43SRui Paulo 	 *   Request:
204b9450e43SRui Paulo 	 *     Length: 4
205b9450e43SRui Paulo 	 *     Value:
206b9450e43SRui Paulo 	 *       u32: clock id
207b9450e43SRui Paulo 	 *   Response:
208b9450e43SRui Paulo 	 *     Length: 8
209b9450e43SRui Paulo 	 *     Value:
210b9450e43SRui Paulo 	 *       u32: clock id
211b9450e43SRui Paulo 	 *       u32: rate (in Hz)
212b9450e43SRui Paulo 	 */
213b9450e43SRui Paulo 
214b9450e43SRui Paulo 	/* setup single tag buffer */
215cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
216cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
217cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
218cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE;
219cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
220cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
221cd3903c6SOleksandr Tymoshenko 	msg.body.req.clock_id = clock_id;
222cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
223b9450e43SRui Paulo 
224b9450e43SRui Paulo 	/* call mailbox property */
225cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
226b9450e43SRui Paulo 	if (err) {
227b9450e43SRui Paulo 		device_printf(sc->dev, "can't get max clock rate (id=%u)\n",
228b9450e43SRui Paulo 		    clock_id);
229b9450e43SRui Paulo 		return (MSG_ERROR);
230b9450e43SRui Paulo 	}
231b9450e43SRui Paulo 
232b9450e43SRui Paulo 	/* result (Hz) */
233cd3903c6SOleksandr Tymoshenko 	rate = (int)msg.body.resp.rate_hz;
234b9450e43SRui Paulo 	DPRINTF("clock = %d(Hz)\n", rate);
235b9450e43SRui Paulo 	return (rate);
236b9450e43SRui Paulo }
237b9450e43SRui Paulo 
238b9450e43SRui Paulo static int
239b9450e43SRui Paulo bcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc *sc,
240b9450e43SRui Paulo     uint32_t clock_id)
241b9450e43SRui Paulo {
242cd3903c6SOleksandr Tymoshenko 	struct msg_get_min_clock_rate msg;
243b9450e43SRui Paulo 	int rate;
244b9450e43SRui Paulo 	int err;
245b9450e43SRui Paulo 
246b9450e43SRui Paulo 	/*
247b9450e43SRui Paulo 	 * Get min clock rate
248b9450e43SRui Paulo 	 *   Tag: 0x00030007
249b9450e43SRui Paulo 	 *   Request:
250b9450e43SRui Paulo 	 *     Length: 4
251b9450e43SRui Paulo 	 *     Value:
252b9450e43SRui Paulo 	 *       u32: clock id
253b9450e43SRui Paulo 	 *   Response:
254b9450e43SRui Paulo 	 *     Length: 8
255b9450e43SRui Paulo 	 *     Value:
256b9450e43SRui Paulo 	 *       u32: clock id
257b9450e43SRui Paulo 	 *       u32: rate (in Hz)
258b9450e43SRui Paulo 	 */
259b9450e43SRui Paulo 
260b9450e43SRui Paulo 	/* setup single tag buffer */
261cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
262cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
263cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
264cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE;
265cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
266cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
267cd3903c6SOleksandr Tymoshenko 	msg.body.req.clock_id = clock_id;
268cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
269b9450e43SRui Paulo 
270b9450e43SRui Paulo 	/* call mailbox property */
271cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
272b9450e43SRui Paulo 	if (err) {
273b9450e43SRui Paulo 		device_printf(sc->dev, "can't get min clock rate (id=%u)\n",
274b9450e43SRui Paulo 		    clock_id);
275b9450e43SRui Paulo 		return (MSG_ERROR);
276b9450e43SRui Paulo 	}
277b9450e43SRui Paulo 
278b9450e43SRui Paulo 	/* result (Hz) */
279cd3903c6SOleksandr Tymoshenko 	rate = (int)msg.body.resp.rate_hz;
280b9450e43SRui Paulo 	DPRINTF("clock = %d(Hz)\n", rate);
281b9450e43SRui Paulo 	return (rate);
282b9450e43SRui Paulo }
283b9450e43SRui Paulo 
284b9450e43SRui Paulo static int
285b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc *sc,
286b9450e43SRui Paulo     uint32_t clock_id, uint32_t rate_hz)
287b9450e43SRui Paulo {
288cd3903c6SOleksandr Tymoshenko 	struct msg_set_clock_rate msg;
289b9450e43SRui Paulo 	int rate;
290b9450e43SRui Paulo 	int err;
291b9450e43SRui Paulo 
292b9450e43SRui Paulo 	/*
293b9450e43SRui Paulo 	 * Set clock rate
294b9450e43SRui Paulo 	 *   Tag: 0x00038002
295b9450e43SRui Paulo 	 *   Request:
296b9450e43SRui Paulo 	 *     Length: 8
297b9450e43SRui Paulo 	 *     Value:
298b9450e43SRui Paulo 	 *       u32: clock id
299b9450e43SRui Paulo 	 *       u32: rate (in Hz)
300b9450e43SRui Paulo 	 *   Response:
301b9450e43SRui Paulo 	 *     Length: 8
302b9450e43SRui Paulo 	 *     Value:
303b9450e43SRui Paulo 	 *       u32: clock id
304b9450e43SRui Paulo 	 *       u32: rate (in Hz)
305b9450e43SRui Paulo 	 */
306b9450e43SRui Paulo 
307b9450e43SRui Paulo 	/* setup single tag buffer */
308cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
309cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
310cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
311cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE;
312cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
313cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
314cd3903c6SOleksandr Tymoshenko 	msg.body.req.clock_id = clock_id;
315cd3903c6SOleksandr Tymoshenko 	msg.body.req.rate_hz = rate_hz;
316cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
317b9450e43SRui Paulo 
318b9450e43SRui Paulo 	/* call mailbox property */
319cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
320b9450e43SRui Paulo 	if (err) {
321b9450e43SRui Paulo 		device_printf(sc->dev, "can't set clock rate (id=%u)\n",
322b9450e43SRui Paulo 		    clock_id);
323b9450e43SRui Paulo 		return (MSG_ERROR);
324b9450e43SRui Paulo 	}
325b9450e43SRui Paulo 
326b9450e43SRui Paulo 	/* workaround for core clock */
327b9450e43SRui Paulo 	if (clock_id == BCM2835_MBOX_CLOCK_ID_CORE) {
328b9450e43SRui Paulo 		/* for safety (may change voltage without changing clock) */
329b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
330b9450e43SRui Paulo 
331b9450e43SRui Paulo 		/*
332b9450e43SRui Paulo 		 * XXX: the core clock is unable to change at once,
333b9450e43SRui Paulo 		 * to change certainly, write it twice now.
334b9450e43SRui Paulo 		 */
335b9450e43SRui Paulo 
336b9450e43SRui Paulo 		/* setup single tag buffer */
337cd3903c6SOleksandr Tymoshenko 		memset(&msg, 0, sizeof(msg));
338cd3903c6SOleksandr Tymoshenko 		msg.hdr.buf_size = sizeof(msg);
339cd3903c6SOleksandr Tymoshenko 		msg.hdr.code = BCM2835_MBOX_CODE_REQ;
340cd3903c6SOleksandr Tymoshenko 		msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE;
341cd3903c6SOleksandr Tymoshenko 		msg.tag_hdr.val_buf_size = sizeof(msg.body);
342cd3903c6SOleksandr Tymoshenko 		msg.tag_hdr.val_len = sizeof(msg.body.req);
343cd3903c6SOleksandr Tymoshenko 		msg.body.req.clock_id = clock_id;
344cd3903c6SOleksandr Tymoshenko 		msg.body.req.rate_hz = rate_hz;
345cd3903c6SOleksandr Tymoshenko 		msg.end_tag = 0;
346b9450e43SRui Paulo 
347b9450e43SRui Paulo 		/* call mailbox property */
348cd3903c6SOleksandr Tymoshenko 		err = bcm2835_mbox_property(&msg, sizeof(msg));
349b9450e43SRui Paulo 		if (err) {
350b9450e43SRui Paulo 			device_printf(sc->dev,
351b9450e43SRui Paulo 			    "can't set clock rate (id=%u)\n", clock_id);
352b9450e43SRui Paulo 			return (MSG_ERROR);
353b9450e43SRui Paulo 		}
354b9450e43SRui Paulo 	}
355b9450e43SRui Paulo 
356b9450e43SRui Paulo 	/* result (Hz) */
357cd3903c6SOleksandr Tymoshenko 	rate = (int)msg.body.resp.rate_hz;
358b9450e43SRui Paulo 	DPRINTF("clock = %d(Hz)\n", rate);
359b9450e43SRui Paulo 	return (rate);
360b9450e43SRui Paulo }
361b9450e43SRui Paulo 
362b9450e43SRui Paulo static int
363b9450e43SRui Paulo bcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc *sc)
364b9450e43SRui Paulo {
365cd3903c6SOleksandr Tymoshenko 	struct msg_get_turbo msg;
366b9450e43SRui Paulo 	int level;
367b9450e43SRui Paulo 	int err;
368b9450e43SRui Paulo 
369b9450e43SRui Paulo 	/*
370b9450e43SRui Paulo 	 * Get turbo
371b9450e43SRui Paulo 	 *   Tag: 0x00030009
372b9450e43SRui Paulo 	 *   Request:
373b9450e43SRui Paulo 	 *     Length: 4
374b9450e43SRui Paulo 	 *     Value:
375b9450e43SRui Paulo 	 *       u32: id
376b9450e43SRui Paulo 	 *   Response:
377b9450e43SRui Paulo 	 *     Length: 8
378b9450e43SRui Paulo 	 *     Value:
379b9450e43SRui Paulo 	 *       u32: id
380b9450e43SRui Paulo 	 *       u32: level
381b9450e43SRui Paulo 	 */
382b9450e43SRui Paulo 
383b9450e43SRui Paulo 	/* setup single tag buffer */
384cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
385cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
386cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
387cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TURBO;
388cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
389cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
390cd3903c6SOleksandr Tymoshenko 	msg.body.req.id = 0;
391cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
392b9450e43SRui Paulo 
393b9450e43SRui Paulo 	/* call mailbox property */
394cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
395b9450e43SRui Paulo 	if (err) {
396b9450e43SRui Paulo 		device_printf(sc->dev, "can't get turbo\n");
397b9450e43SRui Paulo 		return (MSG_ERROR);
398b9450e43SRui Paulo 	}
399b9450e43SRui Paulo 
400b9450e43SRui Paulo 	/* result 0=non-turbo, 1=turbo */
401cd3903c6SOleksandr Tymoshenko 	level = (int)msg.body.resp.level;
402b9450e43SRui Paulo 	DPRINTF("level = %d\n", level);
403b9450e43SRui Paulo 	return (level);
404b9450e43SRui Paulo }
405b9450e43SRui Paulo 
406b9450e43SRui Paulo static int
407b9450e43SRui Paulo bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level)
408b9450e43SRui Paulo {
409cd3903c6SOleksandr Tymoshenko 	struct msg_set_turbo msg;
410b9450e43SRui Paulo 	int value;
411b9450e43SRui Paulo 	int err;
412b9450e43SRui Paulo 
413b9450e43SRui Paulo 	/*
414b9450e43SRui Paulo 	 * Set turbo
415b9450e43SRui Paulo 	 *   Tag: 0x00038009
416b9450e43SRui Paulo 	 *   Request:
417b9450e43SRui Paulo 	 *     Length: 8
418b9450e43SRui Paulo 	 *     Value:
419b9450e43SRui Paulo 	 *       u32: id
420b9450e43SRui Paulo 	 *       u32: level
421b9450e43SRui Paulo 	 *   Response:
422b9450e43SRui Paulo 	 *     Length: 8
423b9450e43SRui Paulo 	 *     Value:
424b9450e43SRui Paulo 	 *       u32: id
425b9450e43SRui Paulo 	 *       u32: level
426b9450e43SRui Paulo 	 */
427b9450e43SRui Paulo 
428b9450e43SRui Paulo 	/* replace unknown value to OFF */
429b9450e43SRui Paulo 	if (level != BCM2835_MBOX_TURBO_ON && level != BCM2835_MBOX_TURBO_OFF)
430b9450e43SRui Paulo 		level = BCM2835_MBOX_TURBO_OFF;
431b9450e43SRui Paulo 
432b9450e43SRui Paulo 	/* setup single tag buffer */
433cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
434cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
435cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
436cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_TURBO;
437cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
438cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
439cd3903c6SOleksandr Tymoshenko 	msg.body.req.id = 0;
440cd3903c6SOleksandr Tymoshenko 	msg.body.req.level = level;
441cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
442b9450e43SRui Paulo 
443b9450e43SRui Paulo 	/* call mailbox property */
444cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
445b9450e43SRui Paulo 	if (err) {
446b9450e43SRui Paulo 		device_printf(sc->dev, "can't set turbo\n");
447b9450e43SRui Paulo 		return (MSG_ERROR);
448b9450e43SRui Paulo 	}
449b9450e43SRui Paulo 
450b9450e43SRui Paulo 	/* result 0=non-turbo, 1=turbo */
451cd3903c6SOleksandr Tymoshenko 	value = (int)msg.body.resp.level;
452b9450e43SRui Paulo 	DPRINTF("level = %d\n", value);
453b9450e43SRui Paulo 	return (value);
454b9450e43SRui Paulo }
455b9450e43SRui Paulo 
456b9450e43SRui Paulo static int
457b9450e43SRui Paulo bcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc *sc,
458b9450e43SRui Paulo     uint32_t voltage_id)
459b9450e43SRui Paulo {
460cd3903c6SOleksandr Tymoshenko 	struct msg_get_voltage msg;
461b9450e43SRui Paulo 	int value;
462b9450e43SRui Paulo 	int err;
463b9450e43SRui Paulo 
464b9450e43SRui Paulo 	/*
465b9450e43SRui Paulo 	 * Get voltage
466b9450e43SRui Paulo 	 *   Tag: 0x00030003
467b9450e43SRui Paulo 	 *   Request:
468b9450e43SRui Paulo 	 *     Length: 4
469b9450e43SRui Paulo 	 *     Value:
470b9450e43SRui Paulo 	 *       u32: voltage id
471b9450e43SRui Paulo 	 *   Response:
472b9450e43SRui Paulo 	 *     Length: 8
473b9450e43SRui Paulo 	 *     Value:
474b9450e43SRui Paulo 	 *       u32: voltage id
475b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
476b9450e43SRui Paulo 	 */
477b9450e43SRui Paulo 
478b9450e43SRui Paulo 	/* setup single tag buffer */
479cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
480cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
481cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
482cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_VOLTAGE;
483cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
484cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
485cd3903c6SOleksandr Tymoshenko 	msg.body.req.voltage_id = voltage_id;
486cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
487b9450e43SRui Paulo 
488b9450e43SRui Paulo 	/* call mailbox property */
489cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
490b9450e43SRui Paulo 	if (err) {
491b9450e43SRui Paulo 		device_printf(sc->dev, "can't get voltage\n");
492b9450e43SRui Paulo 		return (MSG_ERROR);
493b9450e43SRui Paulo 	}
494b9450e43SRui Paulo 
495b9450e43SRui Paulo 	/* result (offset from 1.2V) */
496cd3903c6SOleksandr Tymoshenko 	value = (int)msg.body.resp.value;
497b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
498b9450e43SRui Paulo 	return (value);
499b9450e43SRui Paulo }
500b9450e43SRui Paulo 
501b9450e43SRui Paulo static int
502b9450e43SRui Paulo bcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc *sc,
503b9450e43SRui Paulo     uint32_t voltage_id)
504b9450e43SRui Paulo {
505cd3903c6SOleksandr Tymoshenko 	struct msg_get_max_voltage msg;
506b9450e43SRui Paulo 	int value;
507b9450e43SRui Paulo 	int err;
508b9450e43SRui Paulo 
509b9450e43SRui Paulo 	/*
510b9450e43SRui Paulo 	 * Get voltage
511b9450e43SRui Paulo 	 *   Tag: 0x00030005
512b9450e43SRui Paulo 	 *   Request:
513b9450e43SRui Paulo 	 *     Length: 4
514b9450e43SRui Paulo 	 *     Value:
515b9450e43SRui Paulo 	 *       u32: voltage id
516b9450e43SRui Paulo 	 *   Response:
517b9450e43SRui Paulo 	 *     Length: 8
518b9450e43SRui Paulo 	 *     Value:
519b9450e43SRui Paulo 	 *       u32: voltage id
520b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
521b9450e43SRui Paulo 	 */
522b9450e43SRui Paulo 
523b9450e43SRui Paulo 	/* setup single tag buffer */
524cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
525cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
526cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
527cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_VOLTAGE;
528cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
529cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
530cd3903c6SOleksandr Tymoshenko 	msg.body.req.voltage_id = voltage_id;
531cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
532b9450e43SRui Paulo 
533b9450e43SRui Paulo 	/* call mailbox property */
534cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
535b9450e43SRui Paulo 	if (err) {
536b9450e43SRui Paulo 		device_printf(sc->dev, "can't get max voltage\n");
537b9450e43SRui Paulo 		return (MSG_ERROR);
538b9450e43SRui Paulo 	}
539b9450e43SRui Paulo 
540b9450e43SRui Paulo 	/* result (offset from 1.2V) */
541cd3903c6SOleksandr Tymoshenko 	value = (int)msg.body.resp.value;
542b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
543b9450e43SRui Paulo 	return (value);
544b9450e43SRui Paulo }
545b9450e43SRui Paulo static int
546b9450e43SRui Paulo bcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc *sc,
547b9450e43SRui Paulo     uint32_t voltage_id)
548b9450e43SRui Paulo {
549cd3903c6SOleksandr Tymoshenko 	struct msg_get_min_voltage msg;
550b9450e43SRui Paulo 	int value;
551b9450e43SRui Paulo 	int err;
552b9450e43SRui Paulo 
553b9450e43SRui Paulo 	/*
554b9450e43SRui Paulo 	 * Get voltage
555b9450e43SRui Paulo 	 *   Tag: 0x00030008
556b9450e43SRui Paulo 	 *   Request:
557b9450e43SRui Paulo 	 *     Length: 4
558b9450e43SRui Paulo 	 *     Value:
559b9450e43SRui Paulo 	 *       u32: voltage id
560b9450e43SRui Paulo 	 *   Response:
561b9450e43SRui Paulo 	 *     Length: 8
562b9450e43SRui Paulo 	 *     Value:
563b9450e43SRui Paulo 	 *       u32: voltage id
564b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
565b9450e43SRui Paulo 	 */
566b9450e43SRui Paulo 
567b9450e43SRui Paulo 	/* setup single tag buffer */
568cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
569cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
570cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
571cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_VOLTAGE;
572cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
573cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
574cd3903c6SOleksandr Tymoshenko 	msg.body.req.voltage_id = voltage_id;
575cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
576b9450e43SRui Paulo 
577b9450e43SRui Paulo 	/* call mailbox property */
578cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
579b9450e43SRui Paulo 	if (err) {
580b9450e43SRui Paulo 		device_printf(sc->dev, "can't get min voltage\n");
581b9450e43SRui Paulo 		return (MSG_ERROR);
582b9450e43SRui Paulo 	}
583b9450e43SRui Paulo 
584b9450e43SRui Paulo 	/* result (offset from 1.2V) */
585cd3903c6SOleksandr Tymoshenko 	value = (int)msg.body.resp.value;
586b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
587b9450e43SRui Paulo 	return (value);
588b9450e43SRui Paulo }
589b9450e43SRui Paulo 
590b9450e43SRui Paulo static int
591b9450e43SRui Paulo bcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc *sc,
592b9450e43SRui Paulo     uint32_t voltage_id, int32_t value)
593b9450e43SRui Paulo {
594cd3903c6SOleksandr Tymoshenko 	struct msg_set_voltage msg;
595b9450e43SRui Paulo 	int err;
596b9450e43SRui Paulo 
597b9450e43SRui Paulo 	/*
598b9450e43SRui Paulo 	 * Set voltage
599b9450e43SRui Paulo 	 *   Tag: 0x00038003
600b9450e43SRui Paulo 	 *   Request:
601b9450e43SRui Paulo 	 *     Length: 4
602b9450e43SRui Paulo 	 *     Value:
603b9450e43SRui Paulo 	 *       u32: voltage id
604b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
605b9450e43SRui Paulo 	 *   Response:
606b9450e43SRui Paulo 	 *     Length: 8
607b9450e43SRui Paulo 	 *     Value:
608b9450e43SRui Paulo 	 *       u32: voltage id
609b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
610b9450e43SRui Paulo 	 */
611b9450e43SRui Paulo 
612b9450e43SRui Paulo 	/*
613b9450e43SRui Paulo 	 * over_voltage:
614b9450e43SRui Paulo 	 * 0 (1.2 V). Values above 6 are only allowed when force_turbo or
615b9450e43SRui Paulo 	 * current_limit_override are specified (which set the warranty bit).
616b9450e43SRui Paulo 	 */
617b9450e43SRui Paulo 	if (value > MAX_OVER_VOLTAGE || value < MIN_OVER_VOLTAGE) {
618b9450e43SRui Paulo 		/* currently not supported */
619b9450e43SRui Paulo 		device_printf(sc->dev, "not supported voltage: %d\n", value);
620b9450e43SRui Paulo 		return (MSG_ERROR);
621b9450e43SRui Paulo 	}
622b9450e43SRui Paulo 
623b9450e43SRui Paulo 	/* setup single tag buffer */
624cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
625cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
626cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
627cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_VOLTAGE;
628cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
629cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
630cd3903c6SOleksandr Tymoshenko 	msg.body.req.voltage_id = voltage_id;
631cd3903c6SOleksandr Tymoshenko 	msg.body.req.value = (uint32_t)value;
632cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
633b9450e43SRui Paulo 
634b9450e43SRui Paulo 	/* call mailbox property */
635cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
636b9450e43SRui Paulo 	if (err) {
637b9450e43SRui Paulo 		device_printf(sc->dev, "can't set voltage\n");
638b9450e43SRui Paulo 		return (MSG_ERROR);
639b9450e43SRui Paulo 	}
640b9450e43SRui Paulo 
641b9450e43SRui Paulo 	/* result (offset from 1.2V) */
642cd3903c6SOleksandr Tymoshenko 	value = (int)msg.body.resp.value;
643b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
644b9450e43SRui Paulo 	return (value);
645b9450e43SRui Paulo }
646b9450e43SRui Paulo 
647b9450e43SRui Paulo static int
648b9450e43SRui Paulo bcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc *sc)
649b9450e43SRui Paulo {
650cd3903c6SOleksandr Tymoshenko 	struct msg_get_temperature msg;
651b9450e43SRui Paulo 	int value;
652b9450e43SRui Paulo 	int err;
653b9450e43SRui Paulo 
654b9450e43SRui Paulo 	/*
655b9450e43SRui Paulo 	 * Get temperature
656b9450e43SRui Paulo 	 *   Tag: 0x00030006
657b9450e43SRui Paulo 	 *   Request:
658b9450e43SRui Paulo 	 *     Length: 4
659b9450e43SRui Paulo 	 *     Value:
660b9450e43SRui Paulo 	 *       u32: temperature id
661b9450e43SRui Paulo 	 *   Response:
662b9450e43SRui Paulo 	 *     Length: 8
663b9450e43SRui Paulo 	 *     Value:
664b9450e43SRui Paulo 	 *       u32: temperature id
665b9450e43SRui Paulo 	 *       u32: value
666b9450e43SRui Paulo 	 */
667b9450e43SRui Paulo 
668b9450e43SRui Paulo 	/* setup single tag buffer */
669cd3903c6SOleksandr Tymoshenko 	memset(&msg, 0, sizeof(msg));
670cd3903c6SOleksandr Tymoshenko 	msg.hdr.buf_size = sizeof(msg);
671cd3903c6SOleksandr Tymoshenko 	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
672cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TEMPERATURE;
673cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_buf_size = sizeof(msg.body);
674cd3903c6SOleksandr Tymoshenko 	msg.tag_hdr.val_len = sizeof(msg.body.req);
675cd3903c6SOleksandr Tymoshenko 	msg.body.req.temperature_id = 0;
676cd3903c6SOleksandr Tymoshenko 	msg.end_tag = 0;
677b9450e43SRui Paulo 
678b9450e43SRui Paulo 	/* call mailbox property */
679cd3903c6SOleksandr Tymoshenko 	err = bcm2835_mbox_property(&msg, sizeof(msg));
680b9450e43SRui Paulo 	if (err) {
681b9450e43SRui Paulo 		device_printf(sc->dev, "can't get temperature\n");
682b9450e43SRui Paulo 		return (MSG_ERROR);
683b9450e43SRui Paulo 	}
684b9450e43SRui Paulo 
685b9450e43SRui Paulo 	/* result (temperature of degree C) */
686cd3903c6SOleksandr Tymoshenko 	value = (int)msg.body.resp.value;
687b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
688b9450e43SRui Paulo 	return (value);
689b9450e43SRui Paulo }
690b9450e43SRui Paulo 
691b9450e43SRui Paulo 
692b9450e43SRui Paulo 
693b9450e43SRui Paulo static int
694b9450e43SRui Paulo sysctl_bcm2835_cpufreq_arm_freq(SYSCTL_HANDLER_ARGS)
695b9450e43SRui Paulo {
696b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
697b9450e43SRui Paulo 	int val;
698b9450e43SRui Paulo 	int err;
699b9450e43SRui Paulo 
700b9450e43SRui Paulo 	/* get realtime value */
701b9450e43SRui Paulo 	VC_LOCK(sc);
702b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM);
703b9450e43SRui Paulo 	VC_UNLOCK(sc);
704b9450e43SRui Paulo 	if (val == MSG_ERROR)
705b9450e43SRui Paulo 		return (EIO);
706b9450e43SRui Paulo 
707b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
708b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
709b9450e43SRui Paulo 		return (err);
710b9450e43SRui Paulo 
711b9450e43SRui Paulo 	/* write request */
712b9450e43SRui Paulo 	VC_LOCK(sc);
713b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM,
714b9450e43SRui Paulo 	    val);
715b9450e43SRui Paulo 	VC_UNLOCK(sc);
716b9450e43SRui Paulo 	if (err == MSG_ERROR) {
717b9450e43SRui Paulo 		device_printf(sc->dev, "set clock arm_freq error\n");
718b9450e43SRui Paulo 		return (EIO);
719b9450e43SRui Paulo 	}
720b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
721b9450e43SRui Paulo 
722b9450e43SRui Paulo 	return (0);
723b9450e43SRui Paulo }
724b9450e43SRui Paulo 
725b9450e43SRui Paulo static int
726b9450e43SRui Paulo sysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS)
727b9450e43SRui Paulo {
728b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
729b9450e43SRui Paulo 	int val;
730b9450e43SRui Paulo 	int err;
731b9450e43SRui Paulo 
732b9450e43SRui Paulo 	/* get realtime value */
733b9450e43SRui Paulo 	VC_LOCK(sc);
734b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE);
735b9450e43SRui Paulo 	VC_UNLOCK(sc);
736b9450e43SRui Paulo 	if (val == MSG_ERROR)
737b9450e43SRui Paulo 		return (EIO);
738b9450e43SRui Paulo 
739b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
740b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
741b9450e43SRui Paulo 		return (err);
742b9450e43SRui Paulo 
743b9450e43SRui Paulo 	/* write request */
744b9450e43SRui Paulo 	VC_LOCK(sc);
745b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE,
746b9450e43SRui Paulo 	    val);
747b9450e43SRui Paulo 	if (err == MSG_ERROR) {
748b9450e43SRui Paulo 		VC_UNLOCK(sc);
749b9450e43SRui Paulo 		device_printf(sc->dev, "set clock core_freq error\n");
750b9450e43SRui Paulo 		return (EIO);
751b9450e43SRui Paulo 	}
752b9450e43SRui Paulo 	VC_UNLOCK(sc);
753b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
754b9450e43SRui Paulo 
755b9450e43SRui Paulo 	return (0);
756b9450e43SRui Paulo }
757b9450e43SRui Paulo 
758b9450e43SRui Paulo static int
759b9450e43SRui Paulo sysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS)
760b9450e43SRui Paulo {
761b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
762b9450e43SRui Paulo 	int val;
763b9450e43SRui Paulo 	int err;
764b9450e43SRui Paulo 
765b9450e43SRui Paulo 	/* get realtime value */
766b9450e43SRui Paulo 	VC_LOCK(sc);
767b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM);
768b9450e43SRui Paulo 	VC_UNLOCK(sc);
769b9450e43SRui Paulo 	if (val == MSG_ERROR)
770b9450e43SRui Paulo 		return (EIO);
771b9450e43SRui Paulo 
772b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
773b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
774b9450e43SRui Paulo 		return (err);
775b9450e43SRui Paulo 
776b9450e43SRui Paulo 	/* write request */
777b9450e43SRui Paulo 	VC_LOCK(sc);
778b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM,
779b9450e43SRui Paulo 	    val);
780b9450e43SRui Paulo 	VC_UNLOCK(sc);
781b9450e43SRui Paulo 	if (err == MSG_ERROR) {
782b9450e43SRui Paulo 		device_printf(sc->dev, "set clock sdram_freq error\n");
783b9450e43SRui Paulo 		return (EIO);
784b9450e43SRui Paulo 	}
785b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
786b9450e43SRui Paulo 
787b9450e43SRui Paulo 	return (0);
788b9450e43SRui Paulo }
789b9450e43SRui Paulo 
790b9450e43SRui Paulo static int
791b9450e43SRui Paulo sysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS)
792b9450e43SRui Paulo {
793b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
794b9450e43SRui Paulo 	int val;
795b9450e43SRui Paulo 	int err;
796b9450e43SRui Paulo 
797b9450e43SRui Paulo 	/* get realtime value */
798b9450e43SRui Paulo 	VC_LOCK(sc);
799b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_turbo(sc);
800b9450e43SRui Paulo 	VC_UNLOCK(sc);
801b9450e43SRui Paulo 	if (val == MSG_ERROR)
802b9450e43SRui Paulo 		return (EIO);
803b9450e43SRui Paulo 
804b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
805b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
806b9450e43SRui Paulo 		return (err);
807b9450e43SRui Paulo 
808b9450e43SRui Paulo 	/* write request */
809b9450e43SRui Paulo 	if (val > 0)
810b9450e43SRui Paulo 		sc->turbo_mode = BCM2835_MBOX_TURBO_ON;
811b9450e43SRui Paulo 	else
812b9450e43SRui Paulo 		sc->turbo_mode = BCM2835_MBOX_TURBO_OFF;
813b9450e43SRui Paulo 
814b9450e43SRui Paulo 	VC_LOCK(sc);
815b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_turbo(sc, sc->turbo_mode);
816b9450e43SRui Paulo 	VC_UNLOCK(sc);
817b9450e43SRui Paulo 	if (err == MSG_ERROR) {
818b9450e43SRui Paulo 		device_printf(sc->dev, "set turbo error\n");
819b9450e43SRui Paulo 		return (EIO);
820b9450e43SRui Paulo 	}
821b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
822b9450e43SRui Paulo 
823b9450e43SRui Paulo 	return (0);
824b9450e43SRui Paulo }
825b9450e43SRui Paulo 
826b9450e43SRui Paulo static int
827b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS)
828b9450e43SRui Paulo {
829b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
830b9450e43SRui Paulo 	int val;
831b9450e43SRui Paulo 	int err;
832b9450e43SRui Paulo 
833b9450e43SRui Paulo 	/* get realtime value */
834b9450e43SRui Paulo 	VC_LOCK(sc);
835b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE);
836b9450e43SRui Paulo 	VC_UNLOCK(sc);
837b9450e43SRui Paulo 	if (val == MSG_ERROR)
838b9450e43SRui Paulo 		return (EIO);
839b9450e43SRui Paulo 
840b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
841b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
842b9450e43SRui Paulo 		return (err);
843b9450e43SRui Paulo 
844b9450e43SRui Paulo 	/* write request */
845b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
846b9450e43SRui Paulo 		return (EINVAL);
847b9450e43SRui Paulo 	sc->voltage_core = val;
848b9450e43SRui Paulo 
849b9450e43SRui Paulo 	VC_LOCK(sc);
850b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE,
851b9450e43SRui Paulo 	    sc->voltage_core);
852b9450e43SRui Paulo 	VC_UNLOCK(sc);
853b9450e43SRui Paulo 	if (err == MSG_ERROR) {
854b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage core error\n");
855b9450e43SRui Paulo 		return (EIO);
856b9450e43SRui Paulo 	}
857b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
858b9450e43SRui Paulo 
859b9450e43SRui Paulo 	return (0);
860b9450e43SRui Paulo }
861b9450e43SRui Paulo 
862b9450e43SRui Paulo static int
863b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS)
864b9450e43SRui Paulo {
865b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
866b9450e43SRui Paulo 	int val;
867b9450e43SRui Paulo 	int err;
868b9450e43SRui Paulo 
869b9450e43SRui Paulo 	/* get realtime value */
870b9450e43SRui Paulo 	VC_LOCK(sc);
871b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C);
872b9450e43SRui Paulo 	VC_UNLOCK(sc);
873b9450e43SRui Paulo 	if (val == MSG_ERROR)
874b9450e43SRui Paulo 		return (EIO);
875b9450e43SRui Paulo 
876b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
877b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
878b9450e43SRui Paulo 		return (err);
879b9450e43SRui Paulo 
880b9450e43SRui Paulo 	/* write request */
881b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
882b9450e43SRui Paulo 		return (EINVAL);
883b9450e43SRui Paulo 	sc->voltage_sdram_c = val;
884b9450e43SRui Paulo 
885b9450e43SRui Paulo 	VC_LOCK(sc);
886b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C,
887b9450e43SRui Paulo 	   sc->voltage_sdram_c);
888b9450e43SRui Paulo 	VC_UNLOCK(sc);
889b9450e43SRui Paulo 	if (err == MSG_ERROR) {
890b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_c error\n");
891b9450e43SRui Paulo 		return (EIO);
892b9450e43SRui Paulo 	}
893b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
894b9450e43SRui Paulo 
895b9450e43SRui Paulo 	return (0);
896b9450e43SRui Paulo }
897b9450e43SRui Paulo 
898b9450e43SRui Paulo static int
899b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS)
900b9450e43SRui Paulo {
901b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
902b9450e43SRui Paulo 	int val;
903b9450e43SRui Paulo 	int err;
904b9450e43SRui Paulo 
905b9450e43SRui Paulo 	/* get realtime value */
906b9450e43SRui Paulo 	VC_LOCK(sc);
907b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I);
908b9450e43SRui Paulo 	VC_UNLOCK(sc);
909b9450e43SRui Paulo 	if (val == MSG_ERROR)
910b9450e43SRui Paulo 		return (EIO);
911b9450e43SRui Paulo 
912b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
913b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
914b9450e43SRui Paulo 		return (err);
915b9450e43SRui Paulo 
916b9450e43SRui Paulo 	/* write request */
917b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
918b9450e43SRui Paulo 		return (EINVAL);
919b9450e43SRui Paulo 	sc->voltage_sdram_i = val;
920b9450e43SRui Paulo 
921b9450e43SRui Paulo 	VC_LOCK(sc);
922b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I,
923b9450e43SRui Paulo 	    sc->voltage_sdram_i);
924b9450e43SRui Paulo 	VC_UNLOCK(sc);
925b9450e43SRui Paulo 	if (err == MSG_ERROR) {
926b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_i error\n");
927b9450e43SRui Paulo 		return (EIO);
928b9450e43SRui Paulo 	}
929b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
930b9450e43SRui Paulo 
931b9450e43SRui Paulo 	return (0);
932b9450e43SRui Paulo }
933b9450e43SRui Paulo 
934b9450e43SRui Paulo static int
935b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS)
936b9450e43SRui Paulo {
937b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
938b9450e43SRui Paulo 	int val;
939b9450e43SRui Paulo 	int err;
940b9450e43SRui Paulo 
941b9450e43SRui Paulo 	/* get realtime value */
942b9450e43SRui Paulo 	VC_LOCK(sc);
943b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P);
944b9450e43SRui Paulo 	VC_UNLOCK(sc);
945b9450e43SRui Paulo 	if (val == MSG_ERROR)
946b9450e43SRui Paulo 		return (EIO);
947b9450e43SRui Paulo 
948b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
949b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
950b9450e43SRui Paulo 		return (err);
951b9450e43SRui Paulo 
952b9450e43SRui Paulo 	/* write request */
953b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
954b9450e43SRui Paulo 		return (EINVAL);
955b9450e43SRui Paulo 	sc->voltage_sdram_p = val;
956b9450e43SRui Paulo 
957b9450e43SRui Paulo 	VC_LOCK(sc);
958b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P,
959b9450e43SRui Paulo 	    sc->voltage_sdram_p);
960b9450e43SRui Paulo 	VC_UNLOCK(sc);
961b9450e43SRui Paulo 	if (err == MSG_ERROR) {
962b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_p error\n");
963b9450e43SRui Paulo 		return (EIO);
964b9450e43SRui Paulo 	}
965b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
966b9450e43SRui Paulo 
967b9450e43SRui Paulo 	return (0);
968b9450e43SRui Paulo }
969b9450e43SRui Paulo 
970b9450e43SRui Paulo static int
971b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS)
972b9450e43SRui Paulo {
973b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
974b9450e43SRui Paulo 	int val;
975b9450e43SRui Paulo 	int err;
976b9450e43SRui Paulo 
977b9450e43SRui Paulo 	/* multiple write only */
978b9450e43SRui Paulo 	if (!req->newptr)
979b9450e43SRui Paulo 		return (EINVAL);
980b9450e43SRui Paulo 	val = 0;
981b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
982b9450e43SRui Paulo 	if (err)
983b9450e43SRui Paulo 		return (err);
984b9450e43SRui Paulo 
985b9450e43SRui Paulo 	/* write request */
986b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
987b9450e43SRui Paulo 		return (EINVAL);
988b9450e43SRui Paulo 	sc->voltage_sdram = val;
989b9450e43SRui Paulo 
990b9450e43SRui Paulo 	VC_LOCK(sc);
991b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C,
992b9450e43SRui Paulo 	    val);
993b9450e43SRui Paulo 	if (err == MSG_ERROR) {
994b9450e43SRui Paulo 		VC_UNLOCK(sc);
995b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_c error\n");
996b9450e43SRui Paulo 		return (EIO);
997b9450e43SRui Paulo 	}
998b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I,
999b9450e43SRui Paulo 	    val);
1000b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1001b9450e43SRui Paulo 		VC_UNLOCK(sc);
1002b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_i error\n");
1003b9450e43SRui Paulo 		return (EIO);
1004b9450e43SRui Paulo 	}
1005b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P,
1006b9450e43SRui Paulo 	    val);
1007b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1008b9450e43SRui Paulo 		VC_UNLOCK(sc);
1009b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_p error\n");
1010b9450e43SRui Paulo 		return (EIO);
1011b9450e43SRui Paulo 	}
1012b9450e43SRui Paulo 	VC_UNLOCK(sc);
1013b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
1014b9450e43SRui Paulo 
1015b9450e43SRui Paulo 	return (0);
1016b9450e43SRui Paulo }
1017b9450e43SRui Paulo 
1018b9450e43SRui Paulo static int
1019b9450e43SRui Paulo sysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS)
1020b9450e43SRui Paulo {
1021b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
1022b9450e43SRui Paulo 	int val;
1023b9450e43SRui Paulo 	int err;
1024b9450e43SRui Paulo 
1025b9450e43SRui Paulo 	/* get realtime value */
1026b9450e43SRui Paulo 	VC_LOCK(sc);
1027b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_temperature(sc);
1028b9450e43SRui Paulo 	VC_UNLOCK(sc);
1029b9450e43SRui Paulo 	if (val == MSG_ERROR)
1030b9450e43SRui Paulo 		return (EIO);
1031b9450e43SRui Paulo 
1032b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1033b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
1034b9450e43SRui Paulo 		return (err);
1035b9450e43SRui Paulo 
1036b9450e43SRui Paulo 	/* write request */
1037b9450e43SRui Paulo 	return (EINVAL);
1038b9450e43SRui Paulo }
1039b9450e43SRui Paulo 
1040b9450e43SRui Paulo static int
1041b9450e43SRui Paulo sysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS)
1042b9450e43SRui Paulo {
1043b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
1044b9450e43SRui Paulo 	int val;
1045b9450e43SRui Paulo 	int err;
1046b9450e43SRui Paulo 
1047b9450e43SRui Paulo 	/* get realtime value */
1048b9450e43SRui Paulo 	VC_LOCK(sc);
1049b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_temperature(sc);
1050b9450e43SRui Paulo 	VC_UNLOCK(sc);
1051b9450e43SRui Paulo 	if (val == MSG_ERROR)
1052b9450e43SRui Paulo 		return (EIO);
1053b9450e43SRui Paulo 
1054b9450e43SRui Paulo 	/* 1/1000 celsius (raw) to 1/10 kelvin */
1055e4b6eaf7SLuiz Otavio O Souza 	val = val / 100 + TZ_ZEROC;
1056b9450e43SRui Paulo 
1057b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1058b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
1059b9450e43SRui Paulo 		return (err);
1060b9450e43SRui Paulo 
1061b9450e43SRui Paulo 	/* write request */
1062b9450e43SRui Paulo 	return (EINVAL);
1063b9450e43SRui Paulo }
1064b9450e43SRui Paulo 
1065b9450e43SRui Paulo 
1066b9450e43SRui Paulo static void
1067b9450e43SRui Paulo bcm2835_cpufreq_init(void *arg)
1068b9450e43SRui Paulo {
1069b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg;
1070b9450e43SRui Paulo 	struct sysctl_ctx_list *ctx;
1071b9450e43SRui Paulo 	device_t cpu;
1072b9450e43SRui Paulo 	int arm_freq, core_freq, sdram_freq;
1073b9450e43SRui Paulo 	int arm_max_freq, arm_min_freq, core_max_freq, core_min_freq;
1074b9450e43SRui Paulo 	int sdram_max_freq, sdram_min_freq;
1075b9450e43SRui Paulo 	int voltage_core, voltage_sdram_c, voltage_sdram_i, voltage_sdram_p;
1076b9450e43SRui Paulo 	int max_voltage_core, min_voltage_core;
1077b9450e43SRui Paulo 	int max_voltage_sdram_c, min_voltage_sdram_c;
1078b9450e43SRui Paulo 	int max_voltage_sdram_i, min_voltage_sdram_i;
1079b9450e43SRui Paulo 	int max_voltage_sdram_p, min_voltage_sdram_p;
1080b9450e43SRui Paulo 	int turbo, temperature;
1081b9450e43SRui Paulo 
1082b9450e43SRui Paulo 	VC_LOCK(sc);
1083b9450e43SRui Paulo 
1084b9450e43SRui Paulo 	/* current clock */
1085b9450e43SRui Paulo 	arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
1086b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1087b9450e43SRui Paulo 	core_freq = bcm2835_cpufreq_get_clock_rate(sc,
1088b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_CORE);
1089b9450e43SRui Paulo 	sdram_freq = bcm2835_cpufreq_get_clock_rate(sc,
1090b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_SDRAM);
1091b9450e43SRui Paulo 
1092b9450e43SRui Paulo 	/* max/min clock */
1093b9450e43SRui Paulo 	arm_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
1094b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1095b9450e43SRui Paulo 	arm_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
1096b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1097b9450e43SRui Paulo 	core_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
1098b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_CORE);
1099b9450e43SRui Paulo 	core_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
1100b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_CORE);
1101b9450e43SRui Paulo 	sdram_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
1102b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_SDRAM);
1103b9450e43SRui Paulo 	sdram_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
1104b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_SDRAM);
1105b9450e43SRui Paulo 
1106b9450e43SRui Paulo 	/* turbo mode */
1107b9450e43SRui Paulo 	turbo = bcm2835_cpufreq_get_turbo(sc);
1108b9450e43SRui Paulo 	if (turbo > 0)
1109b9450e43SRui Paulo 		sc->turbo_mode = BCM2835_MBOX_TURBO_ON;
1110b9450e43SRui Paulo 	else
1111b9450e43SRui Paulo 		sc->turbo_mode = BCM2835_MBOX_TURBO_OFF;
1112b9450e43SRui Paulo 
1113b9450e43SRui Paulo 	/* voltage */
1114b9450e43SRui Paulo 	voltage_core = bcm2835_cpufreq_get_voltage(sc,
1115b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_CORE);
1116b9450e43SRui Paulo 	voltage_sdram_c = bcm2835_cpufreq_get_voltage(sc,
1117b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_C);
1118b9450e43SRui Paulo 	voltage_sdram_i = bcm2835_cpufreq_get_voltage(sc,
1119b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_I);
1120b9450e43SRui Paulo 	voltage_sdram_p = bcm2835_cpufreq_get_voltage(sc,
1121b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_P);
1122b9450e43SRui Paulo 
1123b9450e43SRui Paulo 	/* current values (offset from 1.2V) */
1124b9450e43SRui Paulo 	sc->voltage_core = voltage_core;
1125b9450e43SRui Paulo 	sc->voltage_sdram = voltage_sdram_c;
1126b9450e43SRui Paulo 	sc->voltage_sdram_c = voltage_sdram_c;
1127b9450e43SRui Paulo 	sc->voltage_sdram_i = voltage_sdram_i;
1128b9450e43SRui Paulo 	sc->voltage_sdram_p = voltage_sdram_p;
1129b9450e43SRui Paulo 
1130b9450e43SRui Paulo 	/* max/min voltage */
1131b9450e43SRui Paulo 	max_voltage_core = bcm2835_cpufreq_get_max_voltage(sc,
1132b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_CORE);
1133b9450e43SRui Paulo 	min_voltage_core = bcm2835_cpufreq_get_min_voltage(sc,
1134b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_CORE);
1135b9450e43SRui Paulo 	max_voltage_sdram_c = bcm2835_cpufreq_get_max_voltage(sc,
1136b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_C);
1137b9450e43SRui Paulo 	max_voltage_sdram_i = bcm2835_cpufreq_get_max_voltage(sc,
1138b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_I);
1139b9450e43SRui Paulo 	max_voltage_sdram_p = bcm2835_cpufreq_get_max_voltage(sc,
1140b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_P);
1141b9450e43SRui Paulo 	min_voltage_sdram_c = bcm2835_cpufreq_get_min_voltage(sc,
1142b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_C);
1143b9450e43SRui Paulo 	min_voltage_sdram_i = bcm2835_cpufreq_get_min_voltage(sc,
1144b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_I);
1145b9450e43SRui Paulo 	min_voltage_sdram_p = bcm2835_cpufreq_get_min_voltage(sc,
1146b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_P);
1147b9450e43SRui Paulo 
1148b9450e43SRui Paulo 	/* temperature */
1149b9450e43SRui Paulo 	temperature = bcm2835_cpufreq_get_temperature(sc);
1150b9450e43SRui Paulo 
1151b9450e43SRui Paulo 	/* show result */
1152b9450e43SRui Paulo 	if (cpufreq_verbose || bootverbose) {
1153b9450e43SRui Paulo 		device_printf(sc->dev, "Boot settings:\n");
1154b9450e43SRui Paulo 		device_printf(sc->dev,
1155b9450e43SRui Paulo 		    "current ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n",
1156b9450e43SRui Paulo 		    HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq),
1157b9450e43SRui Paulo 		    (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF");
1158b9450e43SRui Paulo 
1159b9450e43SRui Paulo 		device_printf(sc->dev,
1160b9450e43SRui Paulo 		    "max/min ARM %d/%dMHz, Core %d/%dMHz, SDRAM %d/%dMHz\n",
1161b9450e43SRui Paulo 		    HZ2MHZ(arm_max_freq), HZ2MHZ(arm_min_freq),
1162b9450e43SRui Paulo 		    HZ2MHZ(core_max_freq), HZ2MHZ(core_min_freq),
1163b9450e43SRui Paulo 		    HZ2MHZ(sdram_max_freq), HZ2MHZ(sdram_min_freq));
1164b9450e43SRui Paulo 
1165b9450e43SRui Paulo 		device_printf(sc->dev,
1166b9450e43SRui Paulo 		    "current Core %dmV, SDRAM_C %dmV, SDRAM_I %dmV, "
1167b9450e43SRui Paulo 		    "SDRAM_P %dmV\n",
1168b9450e43SRui Paulo 		    OFFSET2MVOLT(voltage_core), OFFSET2MVOLT(voltage_sdram_c),
1169b9450e43SRui Paulo 		    OFFSET2MVOLT(voltage_sdram_i),
1170b9450e43SRui Paulo 		    OFFSET2MVOLT(voltage_sdram_p));
1171b9450e43SRui Paulo 
1172b9450e43SRui Paulo 		device_printf(sc->dev,
1173b9450e43SRui Paulo 		    "max/min Core %d/%dmV, SDRAM_C %d/%dmV, SDRAM_I %d/%dmV, "
1174b9450e43SRui Paulo 		    "SDRAM_P %d/%dmV\n",
1175b9450e43SRui Paulo 		    OFFSET2MVOLT(max_voltage_core),
1176b9450e43SRui Paulo 		    OFFSET2MVOLT(min_voltage_core),
1177b9450e43SRui Paulo 		    OFFSET2MVOLT(max_voltage_sdram_c),
1178b9450e43SRui Paulo 		    OFFSET2MVOLT(min_voltage_sdram_c),
1179b9450e43SRui Paulo 		    OFFSET2MVOLT(max_voltage_sdram_i),
1180b9450e43SRui Paulo 		    OFFSET2MVOLT(min_voltage_sdram_i),
1181b9450e43SRui Paulo 		    OFFSET2MVOLT(max_voltage_sdram_p),
1182b9450e43SRui Paulo 		    OFFSET2MVOLT(min_voltage_sdram_p));
1183b9450e43SRui Paulo 
1184b9450e43SRui Paulo 		device_printf(sc->dev,
1185b9450e43SRui Paulo 		    "Temperature %d.%dC\n", (temperature / 1000),
1186b9450e43SRui Paulo 		    (temperature % 1000) / 100);
1187b9450e43SRui Paulo 	} else { /* !cpufreq_verbose && !bootverbose */
1188b9450e43SRui Paulo 		device_printf(sc->dev,
1189b9450e43SRui Paulo 		    "ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n",
1190b9450e43SRui Paulo 		    HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq),
1191b9450e43SRui Paulo 		    (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF");
1192b9450e43SRui Paulo 	}
1193b9450e43SRui Paulo 
1194b9450e43SRui Paulo 	/* keep in softc (MHz/mV) */
1195b9450e43SRui Paulo 	sc->arm_max_freq = HZ2MHZ(arm_max_freq);
1196b9450e43SRui Paulo 	sc->arm_min_freq = HZ2MHZ(arm_min_freq);
1197b9450e43SRui Paulo 	sc->core_max_freq = HZ2MHZ(core_max_freq);
1198b9450e43SRui Paulo 	sc->core_min_freq = HZ2MHZ(core_min_freq);
1199b9450e43SRui Paulo 	sc->sdram_max_freq = HZ2MHZ(sdram_max_freq);
1200b9450e43SRui Paulo 	sc->sdram_min_freq = HZ2MHZ(sdram_min_freq);
1201b9450e43SRui Paulo 	sc->max_voltage_core = OFFSET2MVOLT(max_voltage_core);
1202b9450e43SRui Paulo 	sc->min_voltage_core = OFFSET2MVOLT(min_voltage_core);
1203b9450e43SRui Paulo 
1204b9450e43SRui Paulo 	/* if turbo is on, set to max values */
1205b9450e43SRui Paulo 	if (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) {
1206b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM,
1207b9450e43SRui Paulo 		    arm_max_freq);
1208b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1209b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE,
1210b9450e43SRui Paulo 		    core_max_freq);
1211b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1212b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc,
1213b9450e43SRui Paulo 		    BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_max_freq);
1214b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1215b9450e43SRui Paulo 	} else {
1216b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM,
1217b9450e43SRui Paulo 		    arm_min_freq);
1218b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1219b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE,
1220b9450e43SRui Paulo 		    core_min_freq);
1221b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1222b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc,
1223b9450e43SRui Paulo 		    BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_min_freq);
1224b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1225b9450e43SRui Paulo 	}
1226b9450e43SRui Paulo 
1227b9450e43SRui Paulo 	VC_UNLOCK(sc);
1228b9450e43SRui Paulo 
1229b9450e43SRui Paulo 	/* add human readable temperature to dev.cpu node */
1230b9450e43SRui Paulo 	cpu = device_get_parent(sc->dev);
1231b9450e43SRui Paulo 	if (cpu != NULL) {
1232b9450e43SRui Paulo 		ctx = device_get_sysctl_ctx(cpu);
1233b9450e43SRui Paulo 		SYSCTL_ADD_PROC(ctx,
1234b9450e43SRui Paulo 		    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO,
1235b9450e43SRui Paulo 		    "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
1236b9450e43SRui Paulo 		    sysctl_bcm2835_devcpu_temperature, "IK",
1237b9450e43SRui Paulo 		    "Current SoC temperature");
1238b9450e43SRui Paulo 	}
1239b9450e43SRui Paulo 
1240b9450e43SRui Paulo 	/* release this hook (continue boot) */
1241b9450e43SRui Paulo 	config_intrhook_disestablish(&sc->init_hook);
1242b9450e43SRui Paulo }
1243b9450e43SRui Paulo 
1244b9450e43SRui Paulo static void
1245b9450e43SRui Paulo bcm2835_cpufreq_identify(driver_t *driver, device_t parent)
1246b9450e43SRui Paulo {
1247b9450e43SRui Paulo 
1248b9450e43SRui Paulo 	DPRINTF("driver=%p, parent=%p\n", driver, parent);
1249b9450e43SRui Paulo 	if (device_find_child(parent, "bcm2835_cpufreq", -1) != NULL)
1250b9450e43SRui Paulo 		return;
1251b9450e43SRui Paulo 	if (BUS_ADD_CHILD(parent, 0, "bcm2835_cpufreq", -1) == NULL)
1252b9450e43SRui Paulo 		device_printf(parent, "add child failed\n");
1253b9450e43SRui Paulo }
1254b9450e43SRui Paulo 
1255b9450e43SRui Paulo static int
1256b9450e43SRui Paulo bcm2835_cpufreq_probe(device_t dev)
1257b9450e43SRui Paulo {
1258b9450e43SRui Paulo 
1259962940ceSLuiz Otavio O Souza 	if (device_get_unit(dev) != 0)
1260962940ceSLuiz Otavio O Souza 		return (ENXIO);
1261b9450e43SRui Paulo 	device_set_desc(dev, "CPU Frequency Control");
1262962940ceSLuiz Otavio O Souza 
1263b9450e43SRui Paulo 	return (0);
1264b9450e43SRui Paulo }
1265b9450e43SRui Paulo 
1266b9450e43SRui Paulo static int
1267b9450e43SRui Paulo bcm2835_cpufreq_attach(device_t dev)
1268b9450e43SRui Paulo {
1269b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1270b9450e43SRui Paulo 	struct sysctl_oid *oid;
1271b9450e43SRui Paulo 
1272b9450e43SRui Paulo 	/* set self dev */
1273b9450e43SRui Paulo 	sc = device_get_softc(dev);
1274b9450e43SRui Paulo 	sc->dev = dev;
1275b9450e43SRui Paulo 
1276b9450e43SRui Paulo 	/* initial values */
1277b9450e43SRui Paulo 	sc->arm_max_freq = -1;
1278b9450e43SRui Paulo 	sc->arm_min_freq = -1;
1279b9450e43SRui Paulo 	sc->core_max_freq = -1;
1280b9450e43SRui Paulo 	sc->core_min_freq = -1;
1281b9450e43SRui Paulo 	sc->sdram_max_freq = -1;
1282b9450e43SRui Paulo 	sc->sdram_min_freq = -1;
1283b9450e43SRui Paulo 	sc->max_voltage_core = 0;
1284b9450e43SRui Paulo 	sc->min_voltage_core = 0;
1285b9450e43SRui Paulo 
1286b9450e43SRui Paulo 	/* setup sysctl at first device */
1287b9450e43SRui Paulo 	if (device_get_unit(dev) == 0) {
1288b9450e43SRui Paulo 		sysctl_ctx_init(&bcm2835_sysctl_ctx);
1289b9450e43SRui Paulo 		/* create node for hw.cpufreq */
1290b9450e43SRui Paulo 		oid = SYSCTL_ADD_NODE(&bcm2835_sysctl_ctx,
1291b9450e43SRui Paulo 		    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "cpufreq",
1292b9450e43SRui Paulo 		    CTLFLAG_RD, NULL, "");
1293b9450e43SRui Paulo 
1294b9450e43SRui Paulo 		/* Frequency (Hz) */
1295b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1296b9450e43SRui Paulo 		    OID_AUTO, "arm_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1297b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_arm_freq, "IU",
1298b9450e43SRui Paulo 		    "ARM frequency (Hz)");
1299b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1300b9450e43SRui Paulo 		    OID_AUTO, "core_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1301b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_core_freq, "IU",
1302b9450e43SRui Paulo 		    "Core frequency (Hz)");
1303b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1304b9450e43SRui Paulo 		    OID_AUTO, "sdram_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1305b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_sdram_freq, "IU",
1306b9450e43SRui Paulo 		    "SDRAM frequency (Hz)");
1307b9450e43SRui Paulo 
1308b9450e43SRui Paulo 		/* Turbo state */
1309b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1310b9450e43SRui Paulo 		    OID_AUTO, "turbo", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1311b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_turbo, "IU",
1312b9450e43SRui Paulo 		    "Disables dynamic clocking");
1313b9450e43SRui Paulo 
1314b9450e43SRui Paulo 		/* Voltage (offset from 1.2V in units of 0.025V) */
1315b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1316b9450e43SRui Paulo 		    OID_AUTO, "voltage_core", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1317b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_voltage_core, "I",
1318b9450e43SRui Paulo 		    "ARM/GPU core voltage"
1319b9450e43SRui Paulo 		    "(offset from 1.2V in units of 0.025V)");
1320b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1321b9450e43SRui Paulo 		    OID_AUTO, "voltage_sdram", CTLTYPE_INT | CTLFLAG_WR, sc,
1322b9450e43SRui Paulo 		    0, sysctl_bcm2835_cpufreq_voltage_sdram, "I",
1323b9450e43SRui Paulo 		    "SDRAM voltage (offset from 1.2V in units of 0.025V)");
1324b9450e43SRui Paulo 
1325b9450e43SRui Paulo 		/* Voltage individual SDRAM */
1326b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1327b9450e43SRui Paulo 		    OID_AUTO, "voltage_sdram_c", CTLTYPE_INT | CTLFLAG_RW, sc,
1328b9450e43SRui Paulo 		    0, sysctl_bcm2835_cpufreq_voltage_sdram_c, "I",
1329b9450e43SRui Paulo 		    "SDRAM controller voltage"
1330b9450e43SRui Paulo 		    "(offset from 1.2V in units of 0.025V)");
1331b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1332b9450e43SRui Paulo 		    OID_AUTO, "voltage_sdram_i", CTLTYPE_INT | CTLFLAG_RW, sc,
1333b9450e43SRui Paulo 		    0, sysctl_bcm2835_cpufreq_voltage_sdram_i, "I",
1334b9450e43SRui Paulo 		    "SDRAM I/O voltage (offset from 1.2V in units of 0.025V)");
1335b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1336b9450e43SRui Paulo 		    OID_AUTO, "voltage_sdram_p", CTLTYPE_INT | CTLFLAG_RW, sc,
1337b9450e43SRui Paulo 		    0, sysctl_bcm2835_cpufreq_voltage_sdram_p, "I",
1338b9450e43SRui Paulo 		    "SDRAM phy voltage (offset from 1.2V in units of 0.025V)");
1339b9450e43SRui Paulo 
1340b9450e43SRui Paulo 		/* Temperature */
1341b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1342b9450e43SRui Paulo 		    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
1343b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_temperature, "I",
1344b9450e43SRui Paulo 		    "SoC temperature (thousandths of a degree C)");
1345b9450e43SRui Paulo 	}
1346b9450e43SRui Paulo 
1347b9450e43SRui Paulo 	/* ARM->VC lock */
1348b9450e43SRui Paulo 	sema_init(&vc_sema, 1, "vcsema");
1349b9450e43SRui Paulo 
1350b9450e43SRui Paulo 	/* register callback for using mbox when interrupts are enabled */
1351b9450e43SRui Paulo 	sc->init_hook.ich_func = bcm2835_cpufreq_init;
1352b9450e43SRui Paulo 	sc->init_hook.ich_arg = sc;
1353b9450e43SRui Paulo 
1354b9450e43SRui Paulo 	if (config_intrhook_establish(&sc->init_hook) != 0) {
1355b9450e43SRui Paulo 		device_printf(dev, "config_intrhook_establish failed\n");
1356b9450e43SRui Paulo 		return (ENOMEM);
1357b9450e43SRui Paulo 	}
1358b9450e43SRui Paulo 
1359b9450e43SRui Paulo 	/* this device is controlled by cpufreq(4) */
1360b9450e43SRui Paulo 	cpufreq_register(dev);
1361b9450e43SRui Paulo 
1362b9450e43SRui Paulo 	return (0);
1363b9450e43SRui Paulo }
1364b9450e43SRui Paulo 
1365b9450e43SRui Paulo static int
1366b9450e43SRui Paulo bcm2835_cpufreq_detach(device_t dev)
1367b9450e43SRui Paulo {
1368b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1369b9450e43SRui Paulo 
1370b9450e43SRui Paulo 	sc = device_get_softc(dev);
1371b9450e43SRui Paulo 
1372b9450e43SRui Paulo 	sema_destroy(&vc_sema);
1373b9450e43SRui Paulo 
1374b9450e43SRui Paulo 	return (cpufreq_unregister(dev));
1375b9450e43SRui Paulo }
1376b9450e43SRui Paulo 
1377b9450e43SRui Paulo static int
1378b9450e43SRui Paulo bcm2835_cpufreq_set(device_t dev, const struct cf_setting *cf)
1379b9450e43SRui Paulo {
1380b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1381b9450e43SRui Paulo 	uint32_t rate_hz, rem;
1382b9450e43SRui Paulo 	int cur_freq, resp_freq, arm_freq, min_freq, core_freq;
1383b9450e43SRui Paulo 
1384b9450e43SRui Paulo 	if (cf == NULL || cf->freq < 0)
1385b9450e43SRui Paulo 		return (EINVAL);
1386b9450e43SRui Paulo 
1387b9450e43SRui Paulo 	sc = device_get_softc(dev);
1388b9450e43SRui Paulo 
1389b9450e43SRui Paulo 	/* setting clock (Hz) */
1390b9450e43SRui Paulo 	rate_hz = (uint32_t)MHZ2HZ(cf->freq);
1391b9450e43SRui Paulo 	rem = rate_hz % HZSTEP;
1392b9450e43SRui Paulo 	rate_hz -= rem;
1393b9450e43SRui Paulo 	if (rate_hz == 0)
1394b9450e43SRui Paulo 		return (EINVAL);
1395b9450e43SRui Paulo 
1396b9450e43SRui Paulo 	/* adjust min freq */
1397b9450e43SRui Paulo 	min_freq = sc->arm_min_freq;
1398b9450e43SRui Paulo 	if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON)
1399b9450e43SRui Paulo 		if (min_freq > cpufreq_lowest_freq)
1400b9450e43SRui Paulo 			min_freq = cpufreq_lowest_freq;
1401b9450e43SRui Paulo 
1402b9450e43SRui Paulo 	if (rate_hz < MHZ2HZ(min_freq) || rate_hz > MHZ2HZ(sc->arm_max_freq))
1403b9450e43SRui Paulo 		return (EINVAL);
1404b9450e43SRui Paulo 
1405b9450e43SRui Paulo 	/* set new value and verify it */
1406b9450e43SRui Paulo 	VC_LOCK(sc);
1407b9450e43SRui Paulo 	cur_freq = bcm2835_cpufreq_get_clock_rate(sc,
1408b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1409b9450e43SRui Paulo 	resp_freq = bcm2835_cpufreq_set_clock_rate(sc,
1410b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM, rate_hz);
1411b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
1412b9450e43SRui Paulo 	arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
1413b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1414b9450e43SRui Paulo 
1415b9450e43SRui Paulo 	/*
1416b9450e43SRui Paulo 	 * if non-turbo and lower than or equal min_freq,
1417b9450e43SRui Paulo 	 * clock down core and sdram to default first.
1418b9450e43SRui Paulo 	 */
1419b9450e43SRui Paulo 	if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) {
1420b9450e43SRui Paulo 		core_freq = bcm2835_cpufreq_get_clock_rate(sc,
1421b9450e43SRui Paulo 		    BCM2835_MBOX_CLOCK_ID_CORE);
1422b9450e43SRui Paulo 		if (rate_hz > MHZ2HZ(sc->arm_min_freq)) {
1423b9450e43SRui Paulo 			bcm2835_cpufreq_set_clock_rate(sc,
1424b9450e43SRui Paulo 			    BCM2835_MBOX_CLOCK_ID_CORE,
1425b9450e43SRui Paulo 			    MHZ2HZ(sc->core_max_freq));
1426b9450e43SRui Paulo 			DELAY(TRANSITION_LATENCY);
1427b9450e43SRui Paulo 			bcm2835_cpufreq_set_clock_rate(sc,
1428b9450e43SRui Paulo 			    BCM2835_MBOX_CLOCK_ID_SDRAM,
1429b9450e43SRui Paulo 			    MHZ2HZ(sc->sdram_max_freq));
1430b9450e43SRui Paulo 			DELAY(TRANSITION_LATENCY);
1431b9450e43SRui Paulo 		} else {
1432b9450e43SRui Paulo 			if (sc->core_min_freq < DEFAULT_CORE_FREQUENCY &&
1433b9450e43SRui Paulo 			    core_freq > DEFAULT_CORE_FREQUENCY) {
1434b9450e43SRui Paulo 				/* first, down to 250, then down to min */
1435b9450e43SRui Paulo 				DELAY(TRANSITION_LATENCY);
1436b9450e43SRui Paulo 				bcm2835_cpufreq_set_clock_rate(sc,
1437b9450e43SRui Paulo 				    BCM2835_MBOX_CLOCK_ID_CORE,
1438b9450e43SRui Paulo 				    MHZ2HZ(DEFAULT_CORE_FREQUENCY));
1439b9450e43SRui Paulo 				DELAY(TRANSITION_LATENCY);
1440b9450e43SRui Paulo 				/* reset core voltage */
1441b9450e43SRui Paulo 				bcm2835_cpufreq_set_voltage(sc,
1442b9450e43SRui Paulo 				    BCM2835_MBOX_VOLTAGE_ID_CORE, 0);
1443b9450e43SRui Paulo 				DELAY(TRANSITION_LATENCY);
1444b9450e43SRui Paulo 			}
1445b9450e43SRui Paulo 			bcm2835_cpufreq_set_clock_rate(sc,
1446b9450e43SRui Paulo 			    BCM2835_MBOX_CLOCK_ID_CORE,
1447b9450e43SRui Paulo 			    MHZ2HZ(sc->core_min_freq));
1448b9450e43SRui Paulo 			DELAY(TRANSITION_LATENCY);
1449b9450e43SRui Paulo 			bcm2835_cpufreq_set_clock_rate(sc,
1450b9450e43SRui Paulo 			    BCM2835_MBOX_CLOCK_ID_SDRAM,
1451b9450e43SRui Paulo 			    MHZ2HZ(sc->sdram_min_freq));
1452b9450e43SRui Paulo 			DELAY(TRANSITION_LATENCY);
1453b9450e43SRui Paulo 		}
1454b9450e43SRui Paulo 	}
1455b9450e43SRui Paulo 
1456b9450e43SRui Paulo 	VC_UNLOCK(sc);
1457b9450e43SRui Paulo 
1458b9450e43SRui Paulo 	if (resp_freq < 0 || arm_freq < 0 || resp_freq != arm_freq) {
1459b9450e43SRui Paulo 		device_printf(dev, "wrong freq\n");
1460b9450e43SRui Paulo 		return (EIO);
1461b9450e43SRui Paulo 	}
1462b9450e43SRui Paulo 	DPRINTF("cpufreq: %d -> %d\n", cur_freq, arm_freq);
1463b9450e43SRui Paulo 
1464b9450e43SRui Paulo 	return (0);
1465b9450e43SRui Paulo }
1466b9450e43SRui Paulo 
1467b9450e43SRui Paulo static int
1468b9450e43SRui Paulo bcm2835_cpufreq_get(device_t dev, struct cf_setting *cf)
1469b9450e43SRui Paulo {
1470b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1471b9450e43SRui Paulo 	int arm_freq;
1472b9450e43SRui Paulo 
1473b9450e43SRui Paulo 	if (cf == NULL)
1474b9450e43SRui Paulo 		return (EINVAL);
1475b9450e43SRui Paulo 
1476b9450e43SRui Paulo 	sc = device_get_softc(dev);
1477b9450e43SRui Paulo 	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
1478b9450e43SRui Paulo 	cf->dev = NULL;
1479b9450e43SRui Paulo 
1480b9450e43SRui Paulo 	/* get cuurent value */
1481b9450e43SRui Paulo 	VC_LOCK(sc);
1482b9450e43SRui Paulo 	arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
1483b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1484b9450e43SRui Paulo 	VC_UNLOCK(sc);
1485b9450e43SRui Paulo 	if (arm_freq < 0) {
1486b9450e43SRui Paulo 		device_printf(dev, "can't get clock\n");
1487b9450e43SRui Paulo 		return (EINVAL);
1488b9450e43SRui Paulo 	}
1489b9450e43SRui Paulo 
1490b9450e43SRui Paulo 	/* CPU clock in MHz or 100ths of a percent. */
1491b9450e43SRui Paulo 	cf->freq = HZ2MHZ(arm_freq);
1492b9450e43SRui Paulo 	/* Voltage in mV. */
1493b9450e43SRui Paulo 	cf->volts = CPUFREQ_VAL_UNKNOWN;
1494b9450e43SRui Paulo 	/* Power consumed in mW. */
1495b9450e43SRui Paulo 	cf->power = CPUFREQ_VAL_UNKNOWN;
1496b9450e43SRui Paulo 	/* Transition latency in us. */
1497b9450e43SRui Paulo 	cf->lat = TRANSITION_LATENCY;
1498b9450e43SRui Paulo 	/* Driver providing this setting. */
1499b9450e43SRui Paulo 	cf->dev = dev;
1500b9450e43SRui Paulo 
1501b9450e43SRui Paulo 	return (0);
1502b9450e43SRui Paulo }
1503b9450e43SRui Paulo 
1504b9450e43SRui Paulo static int
1505b9450e43SRui Paulo bcm2835_cpufreq_make_freq_list(device_t dev, struct cf_setting *sets,
1506b9450e43SRui Paulo     int *count)
1507b9450e43SRui Paulo {
1508b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1509b9450e43SRui Paulo 	int freq, min_freq, volts, rem;
1510b9450e43SRui Paulo 	int idx;
1511b9450e43SRui Paulo 
1512b9450e43SRui Paulo 	sc = device_get_softc(dev);
1513b9450e43SRui Paulo 	freq = sc->arm_max_freq;
1514b9450e43SRui Paulo 	min_freq = sc->arm_min_freq;
1515b9450e43SRui Paulo 
1516b9450e43SRui Paulo 	/* adjust head freq to STEP */
1517b9450e43SRui Paulo 	rem = freq % MHZSTEP;
1518b9450e43SRui Paulo 	freq -= rem;
1519b9450e43SRui Paulo 	if (freq < min_freq)
1520b9450e43SRui Paulo 		freq = min_freq;
1521b9450e43SRui Paulo 
1522b9450e43SRui Paulo 	/* if non-turbo, add extra low freq */
1523b9450e43SRui Paulo 	if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON)
1524b9450e43SRui Paulo 		if (min_freq > cpufreq_lowest_freq)
1525b9450e43SRui Paulo 			min_freq = cpufreq_lowest_freq;
1526b9450e43SRui Paulo 
152711cede48SLuiz Otavio O Souza #ifdef SOC_BCM2836
152811cede48SLuiz Otavio O Souza 	/* XXX RPi2 have only 900/600MHz */
152911cede48SLuiz Otavio O Souza 	idx = 0;
153011cede48SLuiz Otavio O Souza 	volts = sc->min_voltage_core;
153111cede48SLuiz Otavio O Souza 	sets[idx].freq = freq;
153211cede48SLuiz Otavio O Souza 	sets[idx].volts = volts;
153311cede48SLuiz Otavio O Souza 	sets[idx].lat = TRANSITION_LATENCY;
153411cede48SLuiz Otavio O Souza 	sets[idx].dev = dev;
153511cede48SLuiz Otavio O Souza 	idx++;
153611cede48SLuiz Otavio O Souza 	if (freq != min_freq) {
153711cede48SLuiz Otavio O Souza 		sets[idx].freq = min_freq;
153811cede48SLuiz Otavio O Souza 		sets[idx].volts = volts;
153911cede48SLuiz Otavio O Souza 		sets[idx].lat = TRANSITION_LATENCY;
154011cede48SLuiz Otavio O Souza 		sets[idx].dev = dev;
154111cede48SLuiz Otavio O Souza 		idx++;
154211cede48SLuiz Otavio O Souza 	}
154311cede48SLuiz Otavio O Souza #else
1544b9450e43SRui Paulo 	/* from freq to min_freq */
1545b9450e43SRui Paulo 	for (idx = 0; idx < *count && freq >= min_freq; idx++) {
1546b9450e43SRui Paulo 		if (freq > sc->arm_min_freq)
1547b9450e43SRui Paulo 			volts = sc->max_voltage_core;
1548b9450e43SRui Paulo 		else
1549b9450e43SRui Paulo 			volts = sc->min_voltage_core;
1550b9450e43SRui Paulo 		sets[idx].freq = freq;
1551b9450e43SRui Paulo 		sets[idx].volts = volts;
1552b9450e43SRui Paulo 		sets[idx].lat = TRANSITION_LATENCY;
1553b9450e43SRui Paulo 		sets[idx].dev = dev;
1554b9450e43SRui Paulo 		freq -= MHZSTEP;
1555b9450e43SRui Paulo 	}
155611cede48SLuiz Otavio O Souza #endif
155711cede48SLuiz Otavio O Souza 	*count = idx;
1558b9450e43SRui Paulo 
1559b9450e43SRui Paulo 	return (0);
1560b9450e43SRui Paulo }
1561b9450e43SRui Paulo 
1562b9450e43SRui Paulo static int
1563b9450e43SRui Paulo bcm2835_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
1564b9450e43SRui Paulo {
1565b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1566b9450e43SRui Paulo 
1567b9450e43SRui Paulo 	if (sets == NULL || count == NULL)
1568b9450e43SRui Paulo 		return (EINVAL);
1569b9450e43SRui Paulo 
1570b9450e43SRui Paulo 	sc = device_get_softc(dev);
1571b9450e43SRui Paulo 	if (sc->arm_min_freq < 0 || sc->arm_max_freq < 0) {
1572b9450e43SRui Paulo 		printf("device is not configured\n");
1573b9450e43SRui Paulo 		return (EINVAL);
1574b9450e43SRui Paulo 	}
1575b9450e43SRui Paulo 
1576b9450e43SRui Paulo 	/* fill data with unknown value */
1577b9450e43SRui Paulo 	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
1578b9450e43SRui Paulo 	/* create new array up to count */
1579b9450e43SRui Paulo 	bcm2835_cpufreq_make_freq_list(dev, sets, count);
1580b9450e43SRui Paulo 
1581b9450e43SRui Paulo 	return (0);
1582b9450e43SRui Paulo }
1583b9450e43SRui Paulo 
1584b9450e43SRui Paulo static int
1585b9450e43SRui Paulo bcm2835_cpufreq_type(device_t dev, int *type)
1586b9450e43SRui Paulo {
1587b9450e43SRui Paulo 
1588b9450e43SRui Paulo 	if (type == NULL)
1589b9450e43SRui Paulo 		return (EINVAL);
1590b9450e43SRui Paulo 	*type = CPUFREQ_TYPE_ABSOLUTE;
1591b9450e43SRui Paulo 
1592b9450e43SRui Paulo 	return (0);
1593b9450e43SRui Paulo }
1594b9450e43SRui Paulo 
1595b9450e43SRui Paulo static device_method_t bcm2835_cpufreq_methods[] = {
1596b9450e43SRui Paulo 	/* Device interface */
1597b9450e43SRui Paulo 	DEVMETHOD(device_identify,	bcm2835_cpufreq_identify),
1598b9450e43SRui Paulo 	DEVMETHOD(device_probe,		bcm2835_cpufreq_probe),
1599b9450e43SRui Paulo 	DEVMETHOD(device_attach,	bcm2835_cpufreq_attach),
1600b9450e43SRui Paulo 	DEVMETHOD(device_detach,	bcm2835_cpufreq_detach),
1601b9450e43SRui Paulo 
1602b9450e43SRui Paulo 	/* cpufreq interface */
1603b9450e43SRui Paulo 	DEVMETHOD(cpufreq_drv_set,	bcm2835_cpufreq_set),
1604b9450e43SRui Paulo 	DEVMETHOD(cpufreq_drv_get,	bcm2835_cpufreq_get),
1605b9450e43SRui Paulo 	DEVMETHOD(cpufreq_drv_settings,	bcm2835_cpufreq_settings),
1606b9450e43SRui Paulo 	DEVMETHOD(cpufreq_drv_type,	bcm2835_cpufreq_type),
1607b9450e43SRui Paulo 
1608b9450e43SRui Paulo 	DEVMETHOD_END
1609b9450e43SRui Paulo };
1610b9450e43SRui Paulo 
1611b9450e43SRui Paulo static devclass_t bcm2835_cpufreq_devclass;
1612b9450e43SRui Paulo static driver_t bcm2835_cpufreq_driver = {
1613b9450e43SRui Paulo 	"bcm2835_cpufreq",
1614b9450e43SRui Paulo 	bcm2835_cpufreq_methods,
1615b9450e43SRui Paulo 	sizeof(struct bcm2835_cpufreq_softc),
1616b9450e43SRui Paulo };
1617b9450e43SRui Paulo 
1618b9450e43SRui Paulo DRIVER_MODULE(bcm2835_cpufreq, cpu, bcm2835_cpufreq_driver,
1619b9450e43SRui Paulo     bcm2835_cpufreq_devclass, 0, 0);
1620