xref: /freebsd/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c (revision 962940ce6c69ef515f614a3bdf7534f518c593f7)
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))
85e4b6eaf7SLuiz Otavio O Souza #define	TZ_ZEROC		2732
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 	/* mbox buffer (physical address) */
119b9450e43SRui Paulo 	bus_dma_tag_t	dma_tag;
120b9450e43SRui Paulo 	bus_dmamap_t	dma_map;
121b9450e43SRui Paulo 	bus_size_t	dma_size;
122b9450e43SRui Paulo 	void		*dma_buf;
123b9450e43SRui Paulo 	bus_addr_t	dma_phys;
124b9450e43SRui Paulo 
125b9450e43SRui Paulo 	/* initial hook for waiting mbox intr */
126b9450e43SRui Paulo 	struct intr_config_hook	init_hook;
127b9450e43SRui Paulo };
128b9450e43SRui Paulo 
129b9450e43SRui Paulo static int cpufreq_verbose = 0;
130b9450e43SRui Paulo TUNABLE_INT("hw.bcm2835.cpufreq.verbose", &cpufreq_verbose);
131b9450e43SRui Paulo static int cpufreq_lowest_freq = DEFAULT_LOWEST_FREQ;
132b9450e43SRui Paulo TUNABLE_INT("hw.bcm2835.cpufreq.lowest_freq", &cpufreq_lowest_freq);
133b9450e43SRui Paulo 
134e9faba9dSLuiz Otavio O Souza #ifdef PROP_DEBUG
135b9450e43SRui Paulo static void
136b9450e43SRui Paulo bcm2835_dump(const void *data, int len)
137b9450e43SRui Paulo {
138b9450e43SRui Paulo 	const uint8_t *p = (const uint8_t*)data;
139b9450e43SRui Paulo 	int i;
140b9450e43SRui Paulo 
141b9450e43SRui Paulo 	printf("dump @ %p:\n", data);
142b9450e43SRui Paulo 	for (i = 0; i < len; i++) {
143b9450e43SRui Paulo 		printf("%2.2x ", p[i]);
144b9450e43SRui Paulo 		if ((i % 4) == 3)
145b9450e43SRui Paulo 			printf(" ");
146b9450e43SRui Paulo 		if ((i % 16) == 15)
147b9450e43SRui Paulo 			printf("\n");
148b9450e43SRui Paulo 	}
149b9450e43SRui Paulo 	printf("\n");
150b9450e43SRui Paulo }
151b9450e43SRui Paulo #endif
152b9450e43SRui Paulo 
153b9450e43SRui Paulo static int
154b9450e43SRui Paulo bcm2835_mbox_call_prop(struct bcm2835_cpufreq_softc *sc)
155b9450e43SRui Paulo {
156b9450e43SRui Paulo 	struct bcm2835_mbox_hdr *msg = (struct bcm2835_mbox_hdr *)sc->dma_buf;
157b9450e43SRui Paulo 	struct bcm2835_mbox_tag_hdr *tag, *last;
158b9450e43SRui Paulo 	uint8_t *up;
159b9450e43SRui Paulo 	device_t mbox;
160b9450e43SRui Paulo 	size_t hdr_size;
161b9450e43SRui Paulo 	int idx;
162b9450e43SRui Paulo 	int err;
163b9450e43SRui Paulo 
164b9450e43SRui Paulo 	/*
165b9450e43SRui Paulo 	 * For multiple calls, locking is not here. The caller must have
166b9450e43SRui Paulo 	 * VC semaphore.
167b9450e43SRui Paulo 	 */
168b9450e43SRui Paulo 
169b9450e43SRui Paulo 	/* get mbox device */
170b9450e43SRui Paulo 	mbox = devclass_get_device(devclass_find("mbox"), 0);
171b9450e43SRui Paulo 	if (mbox == NULL) {
172b9450e43SRui Paulo 		device_printf(sc->dev, "can't find mbox\n");
173b9450e43SRui Paulo 		return (-1);
174b9450e43SRui Paulo 	}
175b9450e43SRui Paulo 
176b9450e43SRui Paulo 	/* go mailbox property */
177b9450e43SRui Paulo #ifdef PROP_DEBUG
178b9450e43SRui Paulo 	bcm2835_dump(msg, 64);
179b9450e43SRui Paulo #endif
180b9450e43SRui Paulo 	bus_dmamap_sync(sc->dma_tag, sc->dma_map,
181b9450e43SRui Paulo 	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
182b9450e43SRui Paulo 	MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)sc->dma_phys);
183b9450e43SRui Paulo 	MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, &err);
184b9450e43SRui Paulo 	bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_POSTREAD);
185b9450e43SRui Paulo #ifdef PROP_DEBUG
186b9450e43SRui Paulo 	bcm2835_dump(msg, 64);
187b9450e43SRui Paulo #endif
188b9450e43SRui Paulo 
189b9450e43SRui Paulo 	/* check response code */
190b9450e43SRui Paulo 	if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) {
191b9450e43SRui Paulo 		device_printf(sc->dev, "mbox response error\n");
192b9450e43SRui Paulo 		return (-1);
193b9450e43SRui Paulo 	}
194b9450e43SRui Paulo 
195b9450e43SRui Paulo 	/* tag = first tag */
196b9450e43SRui Paulo 	up = (uint8_t *)msg;
197b9450e43SRui Paulo 	hdr_size = sizeof(struct bcm2835_mbox_hdr);
198b9450e43SRui Paulo 	tag = (struct bcm2835_mbox_tag_hdr *)(up + hdr_size);
199b9450e43SRui Paulo 	/* last = end of buffer specified by header */
200b9450e43SRui Paulo 	last = (struct bcm2835_mbox_tag_hdr *)(up + msg->buf_size);
201b9450e43SRui Paulo 
202b9450e43SRui Paulo 	/* loop unitl end tag (=0x0) */
203b9450e43SRui Paulo 	hdr_size = sizeof(struct bcm2835_mbox_tag_hdr);
204b9450e43SRui Paulo 	for (idx = 0; tag->tag != 0; idx++) {
205b9450e43SRui Paulo 		if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) {
206b9450e43SRui Paulo 			device_printf(sc->dev, "tag%d response error\n", idx);
207b9450e43SRui Paulo 			return (-1);
208b9450e43SRui Paulo 		}
209b9450e43SRui Paulo 		/* clear response bit */
210b9450e43SRui Paulo 		tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE;
211b9450e43SRui Paulo 
212b9450e43SRui Paulo 		/* get next tag */
213b9450e43SRui Paulo 		up = (uint8_t *)tag;
214b9450e43SRui Paulo 		tag = (struct bcm2835_mbox_tag_hdr *)(up + hdr_size +
215b9450e43SRui Paulo 		    tag->val_buf_size);
216b9450e43SRui Paulo 
217b9450e43SRui Paulo 		/* check buffer size of header */
218b9450e43SRui Paulo 		if (tag > last) {
219b9450e43SRui Paulo 			device_printf(sc->dev, "mbox buffer size error\n");
220b9450e43SRui Paulo 			return (-1);
221b9450e43SRui Paulo 		}
222b9450e43SRui Paulo 	}
223b9450e43SRui Paulo 
224b9450e43SRui Paulo 	return (0);
225b9450e43SRui Paulo }
226b9450e43SRui Paulo 
227b9450e43SRui Paulo static int
228b9450e43SRui Paulo bcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc *sc,
229b9450e43SRui Paulo     uint32_t clock_id)
230b9450e43SRui Paulo {
231b9450e43SRui Paulo 	struct msg_get_clock_rate *msg;
232b9450e43SRui Paulo 	int rate;
233b9450e43SRui Paulo 	int err;
234b9450e43SRui Paulo 
235b9450e43SRui Paulo 	/*
236b9450e43SRui Paulo 	 * Get clock rate
237b9450e43SRui Paulo 	 *   Tag: 0x00030002
238b9450e43SRui Paulo 	 *   Request:
239b9450e43SRui Paulo 	 *     Length: 4
240b9450e43SRui Paulo 	 *     Value:
241b9450e43SRui Paulo 	 *       u32: clock id
242b9450e43SRui Paulo 	 *   Response:
243b9450e43SRui Paulo 	 *     Length: 8
244b9450e43SRui Paulo 	 *     Value:
245b9450e43SRui Paulo 	 *       u32: clock id
246b9450e43SRui Paulo 	 *       u32: rate (in Hz)
247b9450e43SRui Paulo 	 */
248b9450e43SRui Paulo 
249b9450e43SRui Paulo 	/* using DMA buffer for VC */
250b9450e43SRui Paulo 	msg = (struct msg_get_clock_rate *)sc->dma_buf;
251b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
252b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
253b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
254b9450e43SRui Paulo 		return (MSG_ERROR);
255b9450e43SRui Paulo 	}
256b9450e43SRui Paulo 
257b9450e43SRui Paulo 	/* setup single tag buffer */
258b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
259b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
260b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
261b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE;
262b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
263b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
264b9450e43SRui Paulo 	msg->body.req.clock_id = clock_id;
265b9450e43SRui Paulo 	msg->end_tag = 0;
266b9450e43SRui Paulo 
267b9450e43SRui Paulo 	/* call mailbox property */
268b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
269b9450e43SRui Paulo 	if (err) {
270b9450e43SRui Paulo 		device_printf(sc->dev, "can't get clock rate (id=%u)\n",
271b9450e43SRui Paulo 		    clock_id);
272b9450e43SRui Paulo 		return (MSG_ERROR);
273b9450e43SRui Paulo 	}
274b9450e43SRui Paulo 
275b9450e43SRui Paulo 	/* result (Hz) */
276b9450e43SRui Paulo 	rate = (int)msg->body.resp.rate_hz;
277b9450e43SRui Paulo 	DPRINTF("clock = %d(Hz)\n", rate);
278b9450e43SRui Paulo 	return (rate);
279b9450e43SRui Paulo }
280b9450e43SRui Paulo 
281b9450e43SRui Paulo static int
282b9450e43SRui Paulo bcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc *sc,
283b9450e43SRui Paulo     uint32_t clock_id)
284b9450e43SRui Paulo {
285b9450e43SRui Paulo 	struct msg_get_max_clock_rate *msg;
286b9450e43SRui Paulo 	int rate;
287b9450e43SRui Paulo 	int err;
288b9450e43SRui Paulo 
289b9450e43SRui Paulo 	/*
290b9450e43SRui Paulo 	 * Get max clock rate
291b9450e43SRui Paulo 	 *   Tag: 0x00030004
292b9450e43SRui Paulo 	 *   Request:
293b9450e43SRui Paulo 	 *     Length: 4
294b9450e43SRui Paulo 	 *     Value:
295b9450e43SRui Paulo 	 *       u32: clock id
296b9450e43SRui Paulo 	 *   Response:
297b9450e43SRui Paulo 	 *     Length: 8
298b9450e43SRui Paulo 	 *     Value:
299b9450e43SRui Paulo 	 *       u32: clock id
300b9450e43SRui Paulo 	 *       u32: rate (in Hz)
301b9450e43SRui Paulo 	 */
302b9450e43SRui Paulo 
303b9450e43SRui Paulo 	/* using DMA buffer for VC */
304b9450e43SRui Paulo 	msg = (struct msg_get_max_clock_rate *)sc->dma_buf;
305b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
306b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
307b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
308b9450e43SRui Paulo 		return (MSG_ERROR);
309b9450e43SRui Paulo 	}
310b9450e43SRui Paulo 
311b9450e43SRui Paulo 	/* setup single tag buffer */
312b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
313b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
314b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
315b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE;
316b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
317b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
318b9450e43SRui Paulo 	msg->body.req.clock_id = clock_id;
319b9450e43SRui Paulo 	msg->end_tag = 0;
320b9450e43SRui Paulo 
321b9450e43SRui Paulo 	/* call mailbox property */
322b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
323b9450e43SRui Paulo 	if (err) {
324b9450e43SRui Paulo 		device_printf(sc->dev, "can't get max clock rate (id=%u)\n",
325b9450e43SRui Paulo 		    clock_id);
326b9450e43SRui Paulo 		return (MSG_ERROR);
327b9450e43SRui Paulo 	}
328b9450e43SRui Paulo 
329b9450e43SRui Paulo 	/* result (Hz) */
330b9450e43SRui Paulo 	rate = (int)msg->body.resp.rate_hz;
331b9450e43SRui Paulo 	DPRINTF("clock = %d(Hz)\n", rate);
332b9450e43SRui Paulo 	return (rate);
333b9450e43SRui Paulo }
334b9450e43SRui Paulo 
335b9450e43SRui Paulo static int
336b9450e43SRui Paulo bcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc *sc,
337b9450e43SRui Paulo     uint32_t clock_id)
338b9450e43SRui Paulo {
339b9450e43SRui Paulo 	struct msg_get_min_clock_rate *msg;
340b9450e43SRui Paulo 	int rate;
341b9450e43SRui Paulo 	int err;
342b9450e43SRui Paulo 
343b9450e43SRui Paulo 	/*
344b9450e43SRui Paulo 	 * Get min clock rate
345b9450e43SRui Paulo 	 *   Tag: 0x00030007
346b9450e43SRui Paulo 	 *   Request:
347b9450e43SRui Paulo 	 *     Length: 4
348b9450e43SRui Paulo 	 *     Value:
349b9450e43SRui Paulo 	 *       u32: clock id
350b9450e43SRui Paulo 	 *   Response:
351b9450e43SRui Paulo 	 *     Length: 8
352b9450e43SRui Paulo 	 *     Value:
353b9450e43SRui Paulo 	 *       u32: clock id
354b9450e43SRui Paulo 	 *       u32: rate (in Hz)
355b9450e43SRui Paulo 	 */
356b9450e43SRui Paulo 
357b9450e43SRui Paulo 	/* using DMA buffer for VC */
358b9450e43SRui Paulo 	msg = (struct msg_get_min_clock_rate *)sc->dma_buf;
359b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
360b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
361b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
362b9450e43SRui Paulo 		return (MSG_ERROR);
363b9450e43SRui Paulo 	}
364b9450e43SRui Paulo 
365b9450e43SRui Paulo 	/* setup single tag buffer */
366b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
367b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
368b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
369b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE;
370b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
371b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
372b9450e43SRui Paulo 	msg->body.req.clock_id = clock_id;
373b9450e43SRui Paulo 	msg->end_tag = 0;
374b9450e43SRui Paulo 
375b9450e43SRui Paulo 	/* call mailbox property */
376b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
377b9450e43SRui Paulo 	if (err) {
378b9450e43SRui Paulo 		device_printf(sc->dev, "can't get min clock rate (id=%u)\n",
379b9450e43SRui Paulo 		    clock_id);
380b9450e43SRui Paulo 		return (MSG_ERROR);
381b9450e43SRui Paulo 	}
382b9450e43SRui Paulo 
383b9450e43SRui Paulo 	/* result (Hz) */
384b9450e43SRui Paulo 	rate = (int)msg->body.resp.rate_hz;
385b9450e43SRui Paulo 	DPRINTF("clock = %d(Hz)\n", rate);
386b9450e43SRui Paulo 	return (rate);
387b9450e43SRui Paulo }
388b9450e43SRui Paulo 
389b9450e43SRui Paulo static int
390b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc *sc,
391b9450e43SRui Paulo     uint32_t clock_id, uint32_t rate_hz)
392b9450e43SRui Paulo {
393b9450e43SRui Paulo 	struct msg_set_clock_rate *msg;
394b9450e43SRui Paulo 	int rate;
395b9450e43SRui Paulo 	int err;
396b9450e43SRui Paulo 
397b9450e43SRui Paulo 	/*
398b9450e43SRui Paulo 	 * Set clock rate
399b9450e43SRui Paulo 	 *   Tag: 0x00038002
400b9450e43SRui Paulo 	 *   Request:
401b9450e43SRui Paulo 	 *     Length: 8
402b9450e43SRui Paulo 	 *     Value:
403b9450e43SRui Paulo 	 *       u32: clock id
404b9450e43SRui Paulo 	 *       u32: rate (in Hz)
405b9450e43SRui Paulo 	 *   Response:
406b9450e43SRui Paulo 	 *     Length: 8
407b9450e43SRui Paulo 	 *     Value:
408b9450e43SRui Paulo 	 *       u32: clock id
409b9450e43SRui Paulo 	 *       u32: rate (in Hz)
410b9450e43SRui Paulo 	 */
411b9450e43SRui Paulo 
412b9450e43SRui Paulo 	/* using DMA buffer for VC */
413b9450e43SRui Paulo 	msg = (struct msg_set_clock_rate *)sc->dma_buf;
414b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
415b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
416b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
417b9450e43SRui Paulo 		return (MSG_ERROR);
418b9450e43SRui Paulo 	}
419b9450e43SRui Paulo 
420b9450e43SRui Paulo 	/* setup single tag buffer */
421b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
422b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
423b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
424b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE;
425b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
426b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
427b9450e43SRui Paulo 	msg->body.req.clock_id = clock_id;
428b9450e43SRui Paulo 	msg->body.req.rate_hz = rate_hz;
429b9450e43SRui Paulo 	msg->end_tag = 0;
430b9450e43SRui Paulo 
431b9450e43SRui Paulo 	/* call mailbox property */
432b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
433b9450e43SRui Paulo 	if (err) {
434b9450e43SRui Paulo 		device_printf(sc->dev, "can't set clock rate (id=%u)\n",
435b9450e43SRui Paulo 		    clock_id);
436b9450e43SRui Paulo 		return (MSG_ERROR);
437b9450e43SRui Paulo 	}
438b9450e43SRui Paulo 
439b9450e43SRui Paulo 	/* workaround for core clock */
440b9450e43SRui Paulo 	if (clock_id == BCM2835_MBOX_CLOCK_ID_CORE) {
441b9450e43SRui Paulo 		/* for safety (may change voltage without changing clock) */
442b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
443b9450e43SRui Paulo 
444b9450e43SRui Paulo 		/*
445b9450e43SRui Paulo 		 * XXX: the core clock is unable to change at once,
446b9450e43SRui Paulo 		 * to change certainly, write it twice now.
447b9450e43SRui Paulo 		 */
448b9450e43SRui Paulo 
449b9450e43SRui Paulo 		/* setup single tag buffer */
450b9450e43SRui Paulo 		memset(msg, 0, sizeof(*msg));
451b9450e43SRui Paulo 		msg->hdr.buf_size = sizeof(*msg);
452b9450e43SRui Paulo 		msg->hdr.code = BCM2835_MBOX_CODE_REQ;
453b9450e43SRui Paulo 		msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE;
454b9450e43SRui Paulo 		msg->tag_hdr.val_buf_size = sizeof(msg->body);
455b9450e43SRui Paulo 		msg->tag_hdr.val_len = sizeof(msg->body.req);
456b9450e43SRui Paulo 		msg->body.req.clock_id = clock_id;
457b9450e43SRui Paulo 		msg->body.req.rate_hz = rate_hz;
458b9450e43SRui Paulo 		msg->end_tag = 0;
459b9450e43SRui Paulo 
460b9450e43SRui Paulo 		/* call mailbox property */
461b9450e43SRui Paulo 		err = bcm2835_mbox_call_prop(sc);
462b9450e43SRui Paulo 		if (err) {
463b9450e43SRui Paulo 			device_printf(sc->dev,
464b9450e43SRui Paulo 			    "can't set clock rate (id=%u)\n", clock_id);
465b9450e43SRui Paulo 			return (MSG_ERROR);
466b9450e43SRui Paulo 		}
467b9450e43SRui Paulo 	}
468b9450e43SRui Paulo 
469b9450e43SRui Paulo 	/* result (Hz) */
470b9450e43SRui Paulo 	rate = (int)msg->body.resp.rate_hz;
471b9450e43SRui Paulo 	DPRINTF("clock = %d(Hz)\n", rate);
472b9450e43SRui Paulo 	return (rate);
473b9450e43SRui Paulo }
474b9450e43SRui Paulo 
475b9450e43SRui Paulo static int
476b9450e43SRui Paulo bcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc *sc)
477b9450e43SRui Paulo {
478b9450e43SRui Paulo 	struct msg_get_turbo *msg;
479b9450e43SRui Paulo 	int level;
480b9450e43SRui Paulo 	int err;
481b9450e43SRui Paulo 
482b9450e43SRui Paulo 	/*
483b9450e43SRui Paulo 	 * Get turbo
484b9450e43SRui Paulo 	 *   Tag: 0x00030009
485b9450e43SRui Paulo 	 *   Request:
486b9450e43SRui Paulo 	 *     Length: 4
487b9450e43SRui Paulo 	 *     Value:
488b9450e43SRui Paulo 	 *       u32: id
489b9450e43SRui Paulo 	 *   Response:
490b9450e43SRui Paulo 	 *     Length: 8
491b9450e43SRui Paulo 	 *     Value:
492b9450e43SRui Paulo 	 *       u32: id
493b9450e43SRui Paulo 	 *       u32: level
494b9450e43SRui Paulo 	 */
495b9450e43SRui Paulo 
496b9450e43SRui Paulo 	/* using DMA buffer for VC */
497b9450e43SRui Paulo 	msg = (struct msg_get_turbo *)sc->dma_buf;
498b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
499b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
500b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
501b9450e43SRui Paulo 		return (MSG_ERROR);
502b9450e43SRui Paulo 	}
503b9450e43SRui Paulo 
504b9450e43SRui Paulo 	/* setup single tag buffer */
505b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
506b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
507b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
508b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_TURBO;
509b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
510b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
511b9450e43SRui Paulo 	msg->body.req.id = 0;
512b9450e43SRui Paulo 	msg->end_tag = 0;
513b9450e43SRui Paulo 
514b9450e43SRui Paulo 	/* call mailbox property */
515b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
516b9450e43SRui Paulo 	if (err) {
517b9450e43SRui Paulo 		device_printf(sc->dev, "can't get turbo\n");
518b9450e43SRui Paulo 		return (MSG_ERROR);
519b9450e43SRui Paulo 	}
520b9450e43SRui Paulo 
521b9450e43SRui Paulo 	/* result 0=non-turbo, 1=turbo */
522b9450e43SRui Paulo 	level = (int)msg->body.resp.level;
523b9450e43SRui Paulo 	DPRINTF("level = %d\n", level);
524b9450e43SRui Paulo 	return (level);
525b9450e43SRui Paulo }
526b9450e43SRui Paulo 
527b9450e43SRui Paulo static int
528b9450e43SRui Paulo bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level)
529b9450e43SRui Paulo {
530b9450e43SRui Paulo 	struct msg_set_turbo *msg;
531b9450e43SRui Paulo 	int value;
532b9450e43SRui Paulo 	int err;
533b9450e43SRui Paulo 
534b9450e43SRui Paulo 	/*
535b9450e43SRui Paulo 	 * Set turbo
536b9450e43SRui Paulo 	 *   Tag: 0x00038009
537b9450e43SRui Paulo 	 *   Request:
538b9450e43SRui Paulo 	 *     Length: 8
539b9450e43SRui Paulo 	 *     Value:
540b9450e43SRui Paulo 	 *       u32: id
541b9450e43SRui Paulo 	 *       u32: level
542b9450e43SRui Paulo 	 *   Response:
543b9450e43SRui Paulo 	 *     Length: 8
544b9450e43SRui Paulo 	 *     Value:
545b9450e43SRui Paulo 	 *       u32: id
546b9450e43SRui Paulo 	 *       u32: level
547b9450e43SRui Paulo 	 */
548b9450e43SRui Paulo 
549b9450e43SRui Paulo 	/* using DMA buffer for VC */
550b9450e43SRui Paulo 	msg = (struct msg_set_turbo *)sc->dma_buf;
551b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
552b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
553b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
554b9450e43SRui Paulo 		return (MSG_ERROR);
555b9450e43SRui Paulo 	}
556b9450e43SRui Paulo 
557b9450e43SRui Paulo 	/* replace unknown value to OFF */
558b9450e43SRui Paulo 	if (level != BCM2835_MBOX_TURBO_ON && level != BCM2835_MBOX_TURBO_OFF)
559b9450e43SRui Paulo 		level = BCM2835_MBOX_TURBO_OFF;
560b9450e43SRui Paulo 
561b9450e43SRui Paulo 	/* setup single tag buffer */
562b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
563b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
564b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
565b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_TURBO;
566b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
567b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
568b9450e43SRui Paulo 	msg->body.req.id = 0;
569b9450e43SRui Paulo 	msg->body.req.level = level;
570b9450e43SRui Paulo 	msg->end_tag = 0;
571b9450e43SRui Paulo 
572b9450e43SRui Paulo 	/* call mailbox property */
573b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
574b9450e43SRui Paulo 	if (err) {
575b9450e43SRui Paulo 		device_printf(sc->dev, "can't set turbo\n");
576b9450e43SRui Paulo 		return (MSG_ERROR);
577b9450e43SRui Paulo 	}
578b9450e43SRui Paulo 
579b9450e43SRui Paulo 	/* result 0=non-turbo, 1=turbo */
580b9450e43SRui Paulo 	value = (int)msg->body.resp.level;
581b9450e43SRui Paulo 	DPRINTF("level = %d\n", value);
582b9450e43SRui Paulo 	return (value);
583b9450e43SRui Paulo }
584b9450e43SRui Paulo 
585b9450e43SRui Paulo static int
586b9450e43SRui Paulo bcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc *sc,
587b9450e43SRui Paulo     uint32_t voltage_id)
588b9450e43SRui Paulo {
589b9450e43SRui Paulo 	struct msg_get_voltage *msg;
590b9450e43SRui Paulo 	int value;
591b9450e43SRui Paulo 	int err;
592b9450e43SRui Paulo 
593b9450e43SRui Paulo 	/*
594b9450e43SRui Paulo 	 * Get voltage
595b9450e43SRui Paulo 	 *   Tag: 0x00030003
596b9450e43SRui Paulo 	 *   Request:
597b9450e43SRui Paulo 	 *     Length: 4
598b9450e43SRui Paulo 	 *     Value:
599b9450e43SRui Paulo 	 *       u32: voltage id
600b9450e43SRui Paulo 	 *   Response:
601b9450e43SRui Paulo 	 *     Length: 8
602b9450e43SRui Paulo 	 *     Value:
603b9450e43SRui Paulo 	 *       u32: voltage id
604b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
605b9450e43SRui Paulo 	 */
606b9450e43SRui Paulo 
607b9450e43SRui Paulo 	/* using DMA buffer for VC */
608b9450e43SRui Paulo 	msg = (struct msg_get_voltage *)sc->dma_buf;
609b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
610b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
611b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
612b9450e43SRui Paulo 		return (MSG_ERROR);
613b9450e43SRui Paulo 	}
614b9450e43SRui Paulo 
615b9450e43SRui Paulo 	/* setup single tag buffer */
616b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
617b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
618b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
619b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_VOLTAGE;
620b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
621b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
622b9450e43SRui Paulo 	msg->body.req.voltage_id = voltage_id;
623b9450e43SRui Paulo 	msg->end_tag = 0;
624b9450e43SRui Paulo 
625b9450e43SRui Paulo 	/* call mailbox property */
626b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
627b9450e43SRui Paulo 	if (err) {
628b9450e43SRui Paulo 		device_printf(sc->dev, "can't get voltage\n");
629b9450e43SRui Paulo 		return (MSG_ERROR);
630b9450e43SRui Paulo 	}
631b9450e43SRui Paulo 
632b9450e43SRui Paulo 	/* result (offset from 1.2V) */
633b9450e43SRui Paulo 	value = (int)msg->body.resp.value;
634b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
635b9450e43SRui Paulo 	return (value);
636b9450e43SRui Paulo }
637b9450e43SRui Paulo 
638b9450e43SRui Paulo static int
639b9450e43SRui Paulo bcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc *sc,
640b9450e43SRui Paulo     uint32_t voltage_id)
641b9450e43SRui Paulo {
642b9450e43SRui Paulo 	struct msg_get_max_voltage *msg;
643b9450e43SRui Paulo 	int value;
644b9450e43SRui Paulo 	int err;
645b9450e43SRui Paulo 
646b9450e43SRui Paulo 	/*
647b9450e43SRui Paulo 	 * Get voltage
648b9450e43SRui Paulo 	 *   Tag: 0x00030005
649b9450e43SRui Paulo 	 *   Request:
650b9450e43SRui Paulo 	 *     Length: 4
651b9450e43SRui Paulo 	 *     Value:
652b9450e43SRui Paulo 	 *       u32: voltage id
653b9450e43SRui Paulo 	 *   Response:
654b9450e43SRui Paulo 	 *     Length: 8
655b9450e43SRui Paulo 	 *     Value:
656b9450e43SRui Paulo 	 *       u32: voltage id
657b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
658b9450e43SRui Paulo 	 */
659b9450e43SRui Paulo 
660b9450e43SRui Paulo 	/* using DMA buffer for VC */
661b9450e43SRui Paulo 	msg = (struct msg_get_max_voltage *)sc->dma_buf;
662b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
663b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
664b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
665b9450e43SRui Paulo 		return (MSG_ERROR);
666b9450e43SRui Paulo 	}
667b9450e43SRui Paulo 
668b9450e43SRui Paulo 	/* setup single tag buffer */
669b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
670b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
671b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
672b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_VOLTAGE;
673b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
674b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
675b9450e43SRui Paulo 	msg->body.req.voltage_id = voltage_id;
676b9450e43SRui Paulo 	msg->end_tag = 0;
677b9450e43SRui Paulo 
678b9450e43SRui Paulo 	/* call mailbox property */
679b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
680b9450e43SRui Paulo 	if (err) {
681b9450e43SRui Paulo 		device_printf(sc->dev, "can't get max voltage\n");
682b9450e43SRui Paulo 		return (MSG_ERROR);
683b9450e43SRui Paulo 	}
684b9450e43SRui Paulo 
685b9450e43SRui Paulo 	/* result (offset from 1.2V) */
686b9450e43SRui Paulo 	value = (int)msg->body.resp.value;
687b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
688b9450e43SRui Paulo 	return (value);
689b9450e43SRui Paulo }
690b9450e43SRui Paulo static int
691b9450e43SRui Paulo bcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc *sc,
692b9450e43SRui Paulo     uint32_t voltage_id)
693b9450e43SRui Paulo {
694b9450e43SRui Paulo 	struct msg_get_min_voltage *msg;
695b9450e43SRui Paulo 	int value;
696b9450e43SRui Paulo 	int err;
697b9450e43SRui Paulo 
698b9450e43SRui Paulo 	/*
699b9450e43SRui Paulo 	 * Get voltage
700b9450e43SRui Paulo 	 *   Tag: 0x00030008
701b9450e43SRui Paulo 	 *   Request:
702b9450e43SRui Paulo 	 *     Length: 4
703b9450e43SRui Paulo 	 *     Value:
704b9450e43SRui Paulo 	 *       u32: voltage id
705b9450e43SRui Paulo 	 *   Response:
706b9450e43SRui Paulo 	 *     Length: 8
707b9450e43SRui Paulo 	 *     Value:
708b9450e43SRui Paulo 	 *       u32: voltage id
709b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
710b9450e43SRui Paulo 	 */
711b9450e43SRui Paulo 
712b9450e43SRui Paulo 	/* using DMA buffer for VC */
713b9450e43SRui Paulo 	msg = (struct msg_get_min_voltage *)sc->dma_buf;
714b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
715b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
716b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
717b9450e43SRui Paulo 		return (MSG_ERROR);
718b9450e43SRui Paulo 	}
719b9450e43SRui Paulo 
720b9450e43SRui Paulo 	/* setup single tag buffer */
721b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
722b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
723b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
724b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_VOLTAGE;
725b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
726b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
727b9450e43SRui Paulo 	msg->body.req.voltage_id = voltage_id;
728b9450e43SRui Paulo 	msg->end_tag = 0;
729b9450e43SRui Paulo 
730b9450e43SRui Paulo 	/* call mailbox property */
731b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
732b9450e43SRui Paulo 	if (err) {
733b9450e43SRui Paulo 		device_printf(sc->dev, "can't get min voltage\n");
734b9450e43SRui Paulo 		return (MSG_ERROR);
735b9450e43SRui Paulo 	}
736b9450e43SRui Paulo 
737b9450e43SRui Paulo 	/* result (offset from 1.2V) */
738b9450e43SRui Paulo 	value = (int)msg->body.resp.value;
739b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
740b9450e43SRui Paulo 	return (value);
741b9450e43SRui Paulo }
742b9450e43SRui Paulo 
743b9450e43SRui Paulo static int
744b9450e43SRui Paulo bcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc *sc,
745b9450e43SRui Paulo     uint32_t voltage_id, int32_t value)
746b9450e43SRui Paulo {
747b9450e43SRui Paulo 	struct msg_set_voltage *msg;
748b9450e43SRui Paulo 	int err;
749b9450e43SRui Paulo 
750b9450e43SRui Paulo 	/*
751b9450e43SRui Paulo 	 * Set voltage
752b9450e43SRui Paulo 	 *   Tag: 0x00038003
753b9450e43SRui Paulo 	 *   Request:
754b9450e43SRui Paulo 	 *     Length: 4
755b9450e43SRui Paulo 	 *     Value:
756b9450e43SRui Paulo 	 *       u32: voltage id
757b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
758b9450e43SRui Paulo 	 *   Response:
759b9450e43SRui Paulo 	 *     Length: 8
760b9450e43SRui Paulo 	 *     Value:
761b9450e43SRui Paulo 	 *       u32: voltage id
762b9450e43SRui Paulo 	 *       u32: value (offset from 1.2V in units of 0.025V)
763b9450e43SRui Paulo 	 */
764b9450e43SRui Paulo 
765b9450e43SRui Paulo 	/*
766b9450e43SRui Paulo 	 * over_voltage:
767b9450e43SRui Paulo 	 * 0 (1.2 V). Values above 6 are only allowed when force_turbo or
768b9450e43SRui Paulo 	 * current_limit_override are specified (which set the warranty bit).
769b9450e43SRui Paulo 	 */
770b9450e43SRui Paulo 	if (value > MAX_OVER_VOLTAGE || value < MIN_OVER_VOLTAGE) {
771b9450e43SRui Paulo 		/* currently not supported */
772b9450e43SRui Paulo 		device_printf(sc->dev, "not supported voltage: %d\n", value);
773b9450e43SRui Paulo 		return (MSG_ERROR);
774b9450e43SRui Paulo 	}
775b9450e43SRui Paulo 
776b9450e43SRui Paulo 	/* using DMA buffer for VC */
777b9450e43SRui Paulo 	msg = (struct msg_set_voltage *)sc->dma_buf;
778b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
779b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
780b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
781b9450e43SRui Paulo 		return (MSG_ERROR);
782b9450e43SRui Paulo 	}
783b9450e43SRui Paulo 
784b9450e43SRui Paulo 	/* setup single tag buffer */
785b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
786b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
787b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
788b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_VOLTAGE;
789b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
790b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
791b9450e43SRui Paulo 	msg->body.req.voltage_id = voltage_id;
792b9450e43SRui Paulo 	msg->body.req.value = (uint32_t)value;
793b9450e43SRui Paulo 	msg->end_tag = 0;
794b9450e43SRui Paulo 
795b9450e43SRui Paulo 	/* call mailbox property */
796b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
797b9450e43SRui Paulo 	if (err) {
798b9450e43SRui Paulo 		device_printf(sc->dev, "can't set voltage\n");
799b9450e43SRui Paulo 		return (MSG_ERROR);
800b9450e43SRui Paulo 	}
801b9450e43SRui Paulo 
802b9450e43SRui Paulo 	/* result (offset from 1.2V) */
803b9450e43SRui Paulo 	value = (int)msg->body.resp.value;
804b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
805b9450e43SRui Paulo 	return (value);
806b9450e43SRui Paulo }
807b9450e43SRui Paulo 
808b9450e43SRui Paulo static int
809b9450e43SRui Paulo bcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc *sc)
810b9450e43SRui Paulo {
811b9450e43SRui Paulo 	struct msg_get_temperature *msg;
812b9450e43SRui Paulo 	int value;
813b9450e43SRui Paulo 	int err;
814b9450e43SRui Paulo 
815b9450e43SRui Paulo 	/*
816b9450e43SRui Paulo 	 * Get temperature
817b9450e43SRui Paulo 	 *   Tag: 0x00030006
818b9450e43SRui Paulo 	 *   Request:
819b9450e43SRui Paulo 	 *     Length: 4
820b9450e43SRui Paulo 	 *     Value:
821b9450e43SRui Paulo 	 *       u32: temperature id
822b9450e43SRui Paulo 	 *   Response:
823b9450e43SRui Paulo 	 *     Length: 8
824b9450e43SRui Paulo 	 *     Value:
825b9450e43SRui Paulo 	 *       u32: temperature id
826b9450e43SRui Paulo 	 *       u32: value
827b9450e43SRui Paulo 	 */
828b9450e43SRui Paulo 
829b9450e43SRui Paulo 	/* using DMA buffer for VC */
830b9450e43SRui Paulo 	msg = (struct msg_get_temperature *)sc->dma_buf;
831b9450e43SRui Paulo 	if (sizeof(*msg) > sc->dma_size) {
832b9450e43SRui Paulo 		device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n",
833b9450e43SRui Paulo 		    sizeof(*msg), sc->dma_size);
834b9450e43SRui Paulo 		return (MSG_ERROR);
835b9450e43SRui Paulo 	}
836b9450e43SRui Paulo 
837b9450e43SRui Paulo 	/* setup single tag buffer */
838b9450e43SRui Paulo 	memset(msg, 0, sizeof(*msg));
839b9450e43SRui Paulo 	msg->hdr.buf_size = sizeof(*msg);
840b9450e43SRui Paulo 	msg->hdr.code = BCM2835_MBOX_CODE_REQ;
841b9450e43SRui Paulo 	msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_TEMPERATURE;
842b9450e43SRui Paulo 	msg->tag_hdr.val_buf_size = sizeof(msg->body);
843b9450e43SRui Paulo 	msg->tag_hdr.val_len = sizeof(msg->body.req);
844b9450e43SRui Paulo 	msg->body.req.temperature_id = 0;
845b9450e43SRui Paulo 	msg->end_tag = 0;
846b9450e43SRui Paulo 
847b9450e43SRui Paulo 	/* call mailbox property */
848b9450e43SRui Paulo 	err = bcm2835_mbox_call_prop(sc);
849b9450e43SRui Paulo 	if (err) {
850b9450e43SRui Paulo 		device_printf(sc->dev, "can't get temperature\n");
851b9450e43SRui Paulo 		return (MSG_ERROR);
852b9450e43SRui Paulo 	}
853b9450e43SRui Paulo 
854b9450e43SRui Paulo 	/* result (temperature of degree C) */
855b9450e43SRui Paulo 	value = (int)msg->body.resp.value;
856b9450e43SRui Paulo 	DPRINTF("value = %d\n", value);
857b9450e43SRui Paulo 	return (value);
858b9450e43SRui Paulo }
859b9450e43SRui Paulo 
860b9450e43SRui Paulo 
861b9450e43SRui Paulo 
862b9450e43SRui Paulo static int
863b9450e43SRui Paulo sysctl_bcm2835_cpufreq_arm_freq(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_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM);
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 	VC_LOCK(sc);
882b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM,
883b9450e43SRui Paulo 	    val);
884b9450e43SRui Paulo 	VC_UNLOCK(sc);
885b9450e43SRui Paulo 	if (err == MSG_ERROR) {
886b9450e43SRui Paulo 		device_printf(sc->dev, "set clock arm_freq error\n");
887b9450e43SRui Paulo 		return (EIO);
888b9450e43SRui Paulo 	}
889b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
890b9450e43SRui Paulo 
891b9450e43SRui Paulo 	return (0);
892b9450e43SRui Paulo }
893b9450e43SRui Paulo 
894b9450e43SRui Paulo static int
895b9450e43SRui Paulo sysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS)
896b9450e43SRui Paulo {
897b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
898b9450e43SRui Paulo 	int val;
899b9450e43SRui Paulo 	int err;
900b9450e43SRui Paulo 
901b9450e43SRui Paulo 	/* get realtime value */
902b9450e43SRui Paulo 	VC_LOCK(sc);
903b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE);
904b9450e43SRui Paulo 	VC_UNLOCK(sc);
905b9450e43SRui Paulo 	if (val == MSG_ERROR)
906b9450e43SRui Paulo 		return (EIO);
907b9450e43SRui Paulo 
908b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
909b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
910b9450e43SRui Paulo 		return (err);
911b9450e43SRui Paulo 
912b9450e43SRui Paulo 	/* write request */
913b9450e43SRui Paulo 	VC_LOCK(sc);
914b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE,
915b9450e43SRui Paulo 	    val);
916b9450e43SRui Paulo 	if (err == MSG_ERROR) {
917b9450e43SRui Paulo 		VC_UNLOCK(sc);
918b9450e43SRui Paulo 		device_printf(sc->dev, "set clock core_freq error\n");
919b9450e43SRui Paulo 		return (EIO);
920b9450e43SRui Paulo 	}
921b9450e43SRui Paulo 	VC_UNLOCK(sc);
922b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
923b9450e43SRui Paulo 
924b9450e43SRui Paulo 	return (0);
925b9450e43SRui Paulo }
926b9450e43SRui Paulo 
927b9450e43SRui Paulo static int
928b9450e43SRui Paulo sysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS)
929b9450e43SRui Paulo {
930b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
931b9450e43SRui Paulo 	int val;
932b9450e43SRui Paulo 	int err;
933b9450e43SRui Paulo 
934b9450e43SRui Paulo 	/* get realtime value */
935b9450e43SRui Paulo 	VC_LOCK(sc);
936b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM);
937b9450e43SRui Paulo 	VC_UNLOCK(sc);
938b9450e43SRui Paulo 	if (val == MSG_ERROR)
939b9450e43SRui Paulo 		return (EIO);
940b9450e43SRui Paulo 
941b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
942b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
943b9450e43SRui Paulo 		return (err);
944b9450e43SRui Paulo 
945b9450e43SRui Paulo 	/* write request */
946b9450e43SRui Paulo 	VC_LOCK(sc);
947b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM,
948b9450e43SRui Paulo 	    val);
949b9450e43SRui Paulo 	VC_UNLOCK(sc);
950b9450e43SRui Paulo 	if (err == MSG_ERROR) {
951b9450e43SRui Paulo 		device_printf(sc->dev, "set clock sdram_freq error\n");
952b9450e43SRui Paulo 		return (EIO);
953b9450e43SRui Paulo 	}
954b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
955b9450e43SRui Paulo 
956b9450e43SRui Paulo 	return (0);
957b9450e43SRui Paulo }
958b9450e43SRui Paulo 
959b9450e43SRui Paulo static int
960b9450e43SRui Paulo sysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS)
961b9450e43SRui Paulo {
962b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
963b9450e43SRui Paulo 	int val;
964b9450e43SRui Paulo 	int err;
965b9450e43SRui Paulo 
966b9450e43SRui Paulo 	/* get realtime value */
967b9450e43SRui Paulo 	VC_LOCK(sc);
968b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_turbo(sc);
969b9450e43SRui Paulo 	VC_UNLOCK(sc);
970b9450e43SRui Paulo 	if (val == MSG_ERROR)
971b9450e43SRui Paulo 		return (EIO);
972b9450e43SRui Paulo 
973b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
974b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
975b9450e43SRui Paulo 		return (err);
976b9450e43SRui Paulo 
977b9450e43SRui Paulo 	/* write request */
978b9450e43SRui Paulo 	if (val > 0)
979b9450e43SRui Paulo 		sc->turbo_mode = BCM2835_MBOX_TURBO_ON;
980b9450e43SRui Paulo 	else
981b9450e43SRui Paulo 		sc->turbo_mode = BCM2835_MBOX_TURBO_OFF;
982b9450e43SRui Paulo 
983b9450e43SRui Paulo 	VC_LOCK(sc);
984b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_turbo(sc, sc->turbo_mode);
985b9450e43SRui Paulo 	VC_UNLOCK(sc);
986b9450e43SRui Paulo 	if (err == MSG_ERROR) {
987b9450e43SRui Paulo 		device_printf(sc->dev, "set turbo error\n");
988b9450e43SRui Paulo 		return (EIO);
989b9450e43SRui Paulo 	}
990b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
991b9450e43SRui Paulo 
992b9450e43SRui Paulo 	return (0);
993b9450e43SRui Paulo }
994b9450e43SRui Paulo 
995b9450e43SRui Paulo static int
996b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS)
997b9450e43SRui Paulo {
998b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
999b9450e43SRui Paulo 	int val;
1000b9450e43SRui Paulo 	int err;
1001b9450e43SRui Paulo 
1002b9450e43SRui Paulo 	/* get realtime value */
1003b9450e43SRui Paulo 	VC_LOCK(sc);
1004b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE);
1005b9450e43SRui Paulo 	VC_UNLOCK(sc);
1006b9450e43SRui Paulo 	if (val == MSG_ERROR)
1007b9450e43SRui Paulo 		return (EIO);
1008b9450e43SRui Paulo 
1009b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1010b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
1011b9450e43SRui Paulo 		return (err);
1012b9450e43SRui Paulo 
1013b9450e43SRui Paulo 	/* write request */
1014b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
1015b9450e43SRui Paulo 		return (EINVAL);
1016b9450e43SRui Paulo 	sc->voltage_core = val;
1017b9450e43SRui Paulo 
1018b9450e43SRui Paulo 	VC_LOCK(sc);
1019b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE,
1020b9450e43SRui Paulo 	    sc->voltage_core);
1021b9450e43SRui Paulo 	VC_UNLOCK(sc);
1022b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1023b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage core error\n");
1024b9450e43SRui Paulo 		return (EIO);
1025b9450e43SRui Paulo 	}
1026b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
1027b9450e43SRui Paulo 
1028b9450e43SRui Paulo 	return (0);
1029b9450e43SRui Paulo }
1030b9450e43SRui Paulo 
1031b9450e43SRui Paulo static int
1032b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS)
1033b9450e43SRui Paulo {
1034b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
1035b9450e43SRui Paulo 	int val;
1036b9450e43SRui Paulo 	int err;
1037b9450e43SRui Paulo 
1038b9450e43SRui Paulo 	/* get realtime value */
1039b9450e43SRui Paulo 	VC_LOCK(sc);
1040b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C);
1041b9450e43SRui Paulo 	VC_UNLOCK(sc);
1042b9450e43SRui Paulo 	if (val == MSG_ERROR)
1043b9450e43SRui Paulo 		return (EIO);
1044b9450e43SRui Paulo 
1045b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1046b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
1047b9450e43SRui Paulo 		return (err);
1048b9450e43SRui Paulo 
1049b9450e43SRui Paulo 	/* write request */
1050b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
1051b9450e43SRui Paulo 		return (EINVAL);
1052b9450e43SRui Paulo 	sc->voltage_sdram_c = val;
1053b9450e43SRui Paulo 
1054b9450e43SRui Paulo 	VC_LOCK(sc);
1055b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C,
1056b9450e43SRui Paulo 	   sc->voltage_sdram_c);
1057b9450e43SRui Paulo 	VC_UNLOCK(sc);
1058b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1059b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_c error\n");
1060b9450e43SRui Paulo 		return (EIO);
1061b9450e43SRui Paulo 	}
1062b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
1063b9450e43SRui Paulo 
1064b9450e43SRui Paulo 	return (0);
1065b9450e43SRui Paulo }
1066b9450e43SRui Paulo 
1067b9450e43SRui Paulo static int
1068b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS)
1069b9450e43SRui Paulo {
1070b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
1071b9450e43SRui Paulo 	int val;
1072b9450e43SRui Paulo 	int err;
1073b9450e43SRui Paulo 
1074b9450e43SRui Paulo 	/* get realtime value */
1075b9450e43SRui Paulo 	VC_LOCK(sc);
1076b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I);
1077b9450e43SRui Paulo 	VC_UNLOCK(sc);
1078b9450e43SRui Paulo 	if (val == MSG_ERROR)
1079b9450e43SRui Paulo 		return (EIO);
1080b9450e43SRui Paulo 
1081b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1082b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
1083b9450e43SRui Paulo 		return (err);
1084b9450e43SRui Paulo 
1085b9450e43SRui Paulo 	/* write request */
1086b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
1087b9450e43SRui Paulo 		return (EINVAL);
1088b9450e43SRui Paulo 	sc->voltage_sdram_i = val;
1089b9450e43SRui Paulo 
1090b9450e43SRui Paulo 	VC_LOCK(sc);
1091b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I,
1092b9450e43SRui Paulo 	    sc->voltage_sdram_i);
1093b9450e43SRui Paulo 	VC_UNLOCK(sc);
1094b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1095b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_i error\n");
1096b9450e43SRui Paulo 		return (EIO);
1097b9450e43SRui Paulo 	}
1098b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
1099b9450e43SRui Paulo 
1100b9450e43SRui Paulo 	return (0);
1101b9450e43SRui Paulo }
1102b9450e43SRui Paulo 
1103b9450e43SRui Paulo static int
1104b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS)
1105b9450e43SRui Paulo {
1106b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
1107b9450e43SRui Paulo 	int val;
1108b9450e43SRui Paulo 	int err;
1109b9450e43SRui Paulo 
1110b9450e43SRui Paulo 	/* get realtime value */
1111b9450e43SRui Paulo 	VC_LOCK(sc);
1112b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P);
1113b9450e43SRui Paulo 	VC_UNLOCK(sc);
1114b9450e43SRui Paulo 	if (val == MSG_ERROR)
1115b9450e43SRui Paulo 		return (EIO);
1116b9450e43SRui Paulo 
1117b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1118b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
1119b9450e43SRui Paulo 		return (err);
1120b9450e43SRui Paulo 
1121b9450e43SRui Paulo 	/* write request */
1122b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
1123b9450e43SRui Paulo 		return (EINVAL);
1124b9450e43SRui Paulo 	sc->voltage_sdram_p = val;
1125b9450e43SRui Paulo 
1126b9450e43SRui Paulo 	VC_LOCK(sc);
1127b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P,
1128b9450e43SRui Paulo 	    sc->voltage_sdram_p);
1129b9450e43SRui Paulo 	VC_UNLOCK(sc);
1130b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1131b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_p error\n");
1132b9450e43SRui Paulo 		return (EIO);
1133b9450e43SRui Paulo 	}
1134b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
1135b9450e43SRui Paulo 
1136b9450e43SRui Paulo 	return (0);
1137b9450e43SRui Paulo }
1138b9450e43SRui Paulo 
1139b9450e43SRui Paulo static int
1140b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS)
1141b9450e43SRui Paulo {
1142b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
1143b9450e43SRui Paulo 	int val;
1144b9450e43SRui Paulo 	int err;
1145b9450e43SRui Paulo 
1146b9450e43SRui Paulo 	/* multiple write only */
1147b9450e43SRui Paulo 	if (!req->newptr)
1148b9450e43SRui Paulo 		return (EINVAL);
1149b9450e43SRui Paulo 	val = 0;
1150b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1151b9450e43SRui Paulo 	if (err)
1152b9450e43SRui Paulo 		return (err);
1153b9450e43SRui Paulo 
1154b9450e43SRui Paulo 	/* write request */
1155b9450e43SRui Paulo 	if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
1156b9450e43SRui Paulo 		return (EINVAL);
1157b9450e43SRui Paulo 	sc->voltage_sdram = val;
1158b9450e43SRui Paulo 
1159b9450e43SRui Paulo 	VC_LOCK(sc);
1160b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C,
1161b9450e43SRui Paulo 	    val);
1162b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1163b9450e43SRui Paulo 		VC_UNLOCK(sc);
1164b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_c error\n");
1165b9450e43SRui Paulo 		return (EIO);
1166b9450e43SRui Paulo 	}
1167b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I,
1168b9450e43SRui Paulo 	    val);
1169b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1170b9450e43SRui Paulo 		VC_UNLOCK(sc);
1171b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_i error\n");
1172b9450e43SRui Paulo 		return (EIO);
1173b9450e43SRui Paulo 	}
1174b9450e43SRui Paulo 	err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P,
1175b9450e43SRui Paulo 	    val);
1176b9450e43SRui Paulo 	if (err == MSG_ERROR) {
1177b9450e43SRui Paulo 		VC_UNLOCK(sc);
1178b9450e43SRui Paulo 		device_printf(sc->dev, "set voltage sdram_p error\n");
1179b9450e43SRui Paulo 		return (EIO);
1180b9450e43SRui Paulo 	}
1181b9450e43SRui Paulo 	VC_UNLOCK(sc);
1182b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
1183b9450e43SRui Paulo 
1184b9450e43SRui Paulo 	return (0);
1185b9450e43SRui Paulo }
1186b9450e43SRui Paulo 
1187b9450e43SRui Paulo static int
1188b9450e43SRui Paulo sysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS)
1189b9450e43SRui Paulo {
1190b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
1191b9450e43SRui Paulo 	int val;
1192b9450e43SRui Paulo 	int err;
1193b9450e43SRui Paulo 
1194b9450e43SRui Paulo 	/* get realtime value */
1195b9450e43SRui Paulo 	VC_LOCK(sc);
1196b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_temperature(sc);
1197b9450e43SRui Paulo 	VC_UNLOCK(sc);
1198b9450e43SRui Paulo 	if (val == MSG_ERROR)
1199b9450e43SRui Paulo 		return (EIO);
1200b9450e43SRui Paulo 
1201b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1202b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
1203b9450e43SRui Paulo 		return (err);
1204b9450e43SRui Paulo 
1205b9450e43SRui Paulo 	/* write request */
1206b9450e43SRui Paulo 	return (EINVAL);
1207b9450e43SRui Paulo }
1208b9450e43SRui Paulo 
1209b9450e43SRui Paulo static int
1210b9450e43SRui Paulo sysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS)
1211b9450e43SRui Paulo {
1212b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg1;
1213b9450e43SRui Paulo 	int val;
1214b9450e43SRui Paulo 	int err;
1215b9450e43SRui Paulo 
1216b9450e43SRui Paulo 	/* get realtime value */
1217b9450e43SRui Paulo 	VC_LOCK(sc);
1218b9450e43SRui Paulo 	val = bcm2835_cpufreq_get_temperature(sc);
1219b9450e43SRui Paulo 	VC_UNLOCK(sc);
1220b9450e43SRui Paulo 	if (val == MSG_ERROR)
1221b9450e43SRui Paulo 		return (EIO);
1222b9450e43SRui Paulo 
1223b9450e43SRui Paulo 	/* 1/1000 celsius (raw) to 1/10 kelvin */
1224e4b6eaf7SLuiz Otavio O Souza 	val = val / 100 + TZ_ZEROC;
1225b9450e43SRui Paulo 
1226b9450e43SRui Paulo 	err = sysctl_handle_int(oidp, &val, 0, req);
1227b9450e43SRui Paulo 	if (err || !req->newptr) /* error || read request */
1228b9450e43SRui Paulo 		return (err);
1229b9450e43SRui Paulo 
1230b9450e43SRui Paulo 	/* write request */
1231b9450e43SRui Paulo 	return (EINVAL);
1232b9450e43SRui Paulo }
1233b9450e43SRui Paulo 
1234b9450e43SRui Paulo 
1235b9450e43SRui Paulo static void
1236b9450e43SRui Paulo bcm2835_cpufreq_init(void *arg)
1237b9450e43SRui Paulo {
1238b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc = arg;
1239b9450e43SRui Paulo 	struct sysctl_ctx_list *ctx;
1240b9450e43SRui Paulo 	device_t cpu;
1241b9450e43SRui Paulo 	int arm_freq, core_freq, sdram_freq;
1242b9450e43SRui Paulo 	int arm_max_freq, arm_min_freq, core_max_freq, core_min_freq;
1243b9450e43SRui Paulo 	int sdram_max_freq, sdram_min_freq;
1244b9450e43SRui Paulo 	int voltage_core, voltage_sdram_c, voltage_sdram_i, voltage_sdram_p;
1245b9450e43SRui Paulo 	int max_voltage_core, min_voltage_core;
1246b9450e43SRui Paulo 	int max_voltage_sdram_c, min_voltage_sdram_c;
1247b9450e43SRui Paulo 	int max_voltage_sdram_i, min_voltage_sdram_i;
1248b9450e43SRui Paulo 	int max_voltage_sdram_p, min_voltage_sdram_p;
1249b9450e43SRui Paulo 	int turbo, temperature;
1250b9450e43SRui Paulo 
1251b9450e43SRui Paulo 	VC_LOCK(sc);
1252b9450e43SRui Paulo 
1253b9450e43SRui Paulo 	/* current clock */
1254b9450e43SRui Paulo 	arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
1255b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1256b9450e43SRui Paulo 	core_freq = bcm2835_cpufreq_get_clock_rate(sc,
1257b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_CORE);
1258b9450e43SRui Paulo 	sdram_freq = bcm2835_cpufreq_get_clock_rate(sc,
1259b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_SDRAM);
1260b9450e43SRui Paulo 
1261b9450e43SRui Paulo 	/* max/min clock */
1262b9450e43SRui Paulo 	arm_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
1263b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1264b9450e43SRui Paulo 	arm_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
1265b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1266b9450e43SRui Paulo 	core_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
1267b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_CORE);
1268b9450e43SRui Paulo 	core_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
1269b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_CORE);
1270b9450e43SRui Paulo 	sdram_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
1271b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_SDRAM);
1272b9450e43SRui Paulo 	sdram_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
1273b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_SDRAM);
1274b9450e43SRui Paulo 
1275b9450e43SRui Paulo 	/* turbo mode */
1276b9450e43SRui Paulo 	turbo = bcm2835_cpufreq_get_turbo(sc);
1277b9450e43SRui Paulo 	if (turbo > 0)
1278b9450e43SRui Paulo 		sc->turbo_mode = BCM2835_MBOX_TURBO_ON;
1279b9450e43SRui Paulo 	else
1280b9450e43SRui Paulo 		sc->turbo_mode = BCM2835_MBOX_TURBO_OFF;
1281b9450e43SRui Paulo 
1282b9450e43SRui Paulo 	/* voltage */
1283b9450e43SRui Paulo 	voltage_core = bcm2835_cpufreq_get_voltage(sc,
1284b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_CORE);
1285b9450e43SRui Paulo 	voltage_sdram_c = bcm2835_cpufreq_get_voltage(sc,
1286b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_C);
1287b9450e43SRui Paulo 	voltage_sdram_i = bcm2835_cpufreq_get_voltage(sc,
1288b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_I);
1289b9450e43SRui Paulo 	voltage_sdram_p = bcm2835_cpufreq_get_voltage(sc,
1290b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_P);
1291b9450e43SRui Paulo 
1292b9450e43SRui Paulo 	/* current values (offset from 1.2V) */
1293b9450e43SRui Paulo 	sc->voltage_core = voltage_core;
1294b9450e43SRui Paulo 	sc->voltage_sdram = voltage_sdram_c;
1295b9450e43SRui Paulo 	sc->voltage_sdram_c = voltage_sdram_c;
1296b9450e43SRui Paulo 	sc->voltage_sdram_i = voltage_sdram_i;
1297b9450e43SRui Paulo 	sc->voltage_sdram_p = voltage_sdram_p;
1298b9450e43SRui Paulo 
1299b9450e43SRui Paulo 	/* max/min voltage */
1300b9450e43SRui Paulo 	max_voltage_core = bcm2835_cpufreq_get_max_voltage(sc,
1301b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_CORE);
1302b9450e43SRui Paulo 	min_voltage_core = bcm2835_cpufreq_get_min_voltage(sc,
1303b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_CORE);
1304b9450e43SRui Paulo 	max_voltage_sdram_c = bcm2835_cpufreq_get_max_voltage(sc,
1305b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_C);
1306b9450e43SRui Paulo 	max_voltage_sdram_i = bcm2835_cpufreq_get_max_voltage(sc,
1307b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_I);
1308b9450e43SRui Paulo 	max_voltage_sdram_p = bcm2835_cpufreq_get_max_voltage(sc,
1309b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_P);
1310b9450e43SRui Paulo 	min_voltage_sdram_c = bcm2835_cpufreq_get_min_voltage(sc,
1311b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_C);
1312b9450e43SRui Paulo 	min_voltage_sdram_i = bcm2835_cpufreq_get_min_voltage(sc,
1313b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_I);
1314b9450e43SRui Paulo 	min_voltage_sdram_p = bcm2835_cpufreq_get_min_voltage(sc,
1315b9450e43SRui Paulo 	    BCM2835_MBOX_VOLTAGE_ID_SDRAM_P);
1316b9450e43SRui Paulo 
1317b9450e43SRui Paulo 	/* temperature */
1318b9450e43SRui Paulo 	temperature = bcm2835_cpufreq_get_temperature(sc);
1319b9450e43SRui Paulo 
1320b9450e43SRui Paulo 	/* show result */
1321b9450e43SRui Paulo 	if (cpufreq_verbose || bootverbose) {
1322b9450e43SRui Paulo 		device_printf(sc->dev, "Boot settings:\n");
1323b9450e43SRui Paulo 		device_printf(sc->dev,
1324b9450e43SRui Paulo 		    "current ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n",
1325b9450e43SRui Paulo 		    HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq),
1326b9450e43SRui Paulo 		    (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF");
1327b9450e43SRui Paulo 
1328b9450e43SRui Paulo 		device_printf(sc->dev,
1329b9450e43SRui Paulo 		    "max/min ARM %d/%dMHz, Core %d/%dMHz, SDRAM %d/%dMHz\n",
1330b9450e43SRui Paulo 		    HZ2MHZ(arm_max_freq), HZ2MHZ(arm_min_freq),
1331b9450e43SRui Paulo 		    HZ2MHZ(core_max_freq), HZ2MHZ(core_min_freq),
1332b9450e43SRui Paulo 		    HZ2MHZ(sdram_max_freq), HZ2MHZ(sdram_min_freq));
1333b9450e43SRui Paulo 
1334b9450e43SRui Paulo 		device_printf(sc->dev,
1335b9450e43SRui Paulo 		    "current Core %dmV, SDRAM_C %dmV, SDRAM_I %dmV, "
1336b9450e43SRui Paulo 		    "SDRAM_P %dmV\n",
1337b9450e43SRui Paulo 		    OFFSET2MVOLT(voltage_core), OFFSET2MVOLT(voltage_sdram_c),
1338b9450e43SRui Paulo 		    OFFSET2MVOLT(voltage_sdram_i),
1339b9450e43SRui Paulo 		    OFFSET2MVOLT(voltage_sdram_p));
1340b9450e43SRui Paulo 
1341b9450e43SRui Paulo 		device_printf(sc->dev,
1342b9450e43SRui Paulo 		    "max/min Core %d/%dmV, SDRAM_C %d/%dmV, SDRAM_I %d/%dmV, "
1343b9450e43SRui Paulo 		    "SDRAM_P %d/%dmV\n",
1344b9450e43SRui Paulo 		    OFFSET2MVOLT(max_voltage_core),
1345b9450e43SRui Paulo 		    OFFSET2MVOLT(min_voltage_core),
1346b9450e43SRui Paulo 		    OFFSET2MVOLT(max_voltage_sdram_c),
1347b9450e43SRui Paulo 		    OFFSET2MVOLT(min_voltage_sdram_c),
1348b9450e43SRui Paulo 		    OFFSET2MVOLT(max_voltage_sdram_i),
1349b9450e43SRui Paulo 		    OFFSET2MVOLT(min_voltage_sdram_i),
1350b9450e43SRui Paulo 		    OFFSET2MVOLT(max_voltage_sdram_p),
1351b9450e43SRui Paulo 		    OFFSET2MVOLT(min_voltage_sdram_p));
1352b9450e43SRui Paulo 
1353b9450e43SRui Paulo 		device_printf(sc->dev,
1354b9450e43SRui Paulo 		    "Temperature %d.%dC\n", (temperature / 1000),
1355b9450e43SRui Paulo 		    (temperature % 1000) / 100);
1356b9450e43SRui Paulo 	} else { /* !cpufreq_verbose && !bootverbose */
1357b9450e43SRui Paulo 		device_printf(sc->dev,
1358b9450e43SRui Paulo 		    "ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n",
1359b9450e43SRui Paulo 		    HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq),
1360b9450e43SRui Paulo 		    (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF");
1361b9450e43SRui Paulo 	}
1362b9450e43SRui Paulo 
1363b9450e43SRui Paulo 	/* keep in softc (MHz/mV) */
1364b9450e43SRui Paulo 	sc->arm_max_freq = HZ2MHZ(arm_max_freq);
1365b9450e43SRui Paulo 	sc->arm_min_freq = HZ2MHZ(arm_min_freq);
1366b9450e43SRui Paulo 	sc->core_max_freq = HZ2MHZ(core_max_freq);
1367b9450e43SRui Paulo 	sc->core_min_freq = HZ2MHZ(core_min_freq);
1368b9450e43SRui Paulo 	sc->sdram_max_freq = HZ2MHZ(sdram_max_freq);
1369b9450e43SRui Paulo 	sc->sdram_min_freq = HZ2MHZ(sdram_min_freq);
1370b9450e43SRui Paulo 	sc->max_voltage_core = OFFSET2MVOLT(max_voltage_core);
1371b9450e43SRui Paulo 	sc->min_voltage_core = OFFSET2MVOLT(min_voltage_core);
1372b9450e43SRui Paulo 
1373b9450e43SRui Paulo 	/* if turbo is on, set to max values */
1374b9450e43SRui Paulo 	if (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) {
1375b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM,
1376b9450e43SRui Paulo 		    arm_max_freq);
1377b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1378b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE,
1379b9450e43SRui Paulo 		    core_max_freq);
1380b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1381b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc,
1382b9450e43SRui Paulo 		    BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_max_freq);
1383b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1384b9450e43SRui Paulo 	} else {
1385b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM,
1386b9450e43SRui Paulo 		    arm_min_freq);
1387b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1388b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE,
1389b9450e43SRui Paulo 		    core_min_freq);
1390b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1391b9450e43SRui Paulo 		bcm2835_cpufreq_set_clock_rate(sc,
1392b9450e43SRui Paulo 		    BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_min_freq);
1393b9450e43SRui Paulo 		DELAY(TRANSITION_LATENCY);
1394b9450e43SRui Paulo 	}
1395b9450e43SRui Paulo 
1396b9450e43SRui Paulo 	VC_UNLOCK(sc);
1397b9450e43SRui Paulo 
1398b9450e43SRui Paulo 	/* add human readable temperature to dev.cpu node */
1399b9450e43SRui Paulo 	cpu = device_get_parent(sc->dev);
1400b9450e43SRui Paulo 	if (cpu != NULL) {
1401b9450e43SRui Paulo 		ctx = device_get_sysctl_ctx(cpu);
1402b9450e43SRui Paulo 		SYSCTL_ADD_PROC(ctx,
1403b9450e43SRui Paulo 		    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO,
1404b9450e43SRui Paulo 		    "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
1405b9450e43SRui Paulo 		    sysctl_bcm2835_devcpu_temperature, "IK",
1406b9450e43SRui Paulo 		    "Current SoC temperature");
1407b9450e43SRui Paulo 	}
1408b9450e43SRui Paulo 
1409b9450e43SRui Paulo 	/* release this hook (continue boot) */
1410b9450e43SRui Paulo 	config_intrhook_disestablish(&sc->init_hook);
1411b9450e43SRui Paulo }
1412b9450e43SRui Paulo 
1413b9450e43SRui Paulo static void
1414b9450e43SRui Paulo bcm2835_cpufreq_identify(driver_t *driver, device_t parent)
1415b9450e43SRui Paulo {
1416b9450e43SRui Paulo 
1417b9450e43SRui Paulo 	DPRINTF("driver=%p, parent=%p\n", driver, parent);
1418b9450e43SRui Paulo 	if (device_find_child(parent, "bcm2835_cpufreq", -1) != NULL)
1419b9450e43SRui Paulo 		return;
1420b9450e43SRui Paulo 	if (BUS_ADD_CHILD(parent, 0, "bcm2835_cpufreq", -1) == NULL)
1421b9450e43SRui Paulo 		device_printf(parent, "add child failed\n");
1422b9450e43SRui Paulo }
1423b9450e43SRui Paulo 
1424b9450e43SRui Paulo static int
1425b9450e43SRui Paulo bcm2835_cpufreq_probe(device_t dev)
1426b9450e43SRui Paulo {
1427b9450e43SRui Paulo 
1428*962940ceSLuiz Otavio O Souza 	if (device_get_unit(dev) != 0)
1429*962940ceSLuiz Otavio O Souza 		return (ENXIO);
1430b9450e43SRui Paulo 	device_set_desc(dev, "CPU Frequency Control");
1431*962940ceSLuiz Otavio O Souza 
1432b9450e43SRui Paulo 	return (0);
1433b9450e43SRui Paulo }
1434b9450e43SRui Paulo 
1435b9450e43SRui Paulo static void
1436b9450e43SRui Paulo bcm2835_cpufreq_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
1437b9450e43SRui Paulo {
1438b9450e43SRui Paulo 	bus_addr_t *addr;
1439b9450e43SRui Paulo 
1440b9450e43SRui Paulo 	if (err)
1441b9450e43SRui Paulo 		return;
1442b9450e43SRui Paulo 	addr = (bus_addr_t *)arg;
1443b9450e43SRui Paulo 	*addr = PHYS_TO_VCBUS(segs[0].ds_addr);
1444b9450e43SRui Paulo }
1445b9450e43SRui Paulo 
1446b9450e43SRui Paulo static int
1447b9450e43SRui Paulo bcm2835_cpufreq_attach(device_t dev)
1448b9450e43SRui Paulo {
1449b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1450b9450e43SRui Paulo 	struct sysctl_oid *oid;
1451b9450e43SRui Paulo 	int err;
1452b9450e43SRui Paulo 
1453b9450e43SRui Paulo 	/* set self dev */
1454b9450e43SRui Paulo 	sc = device_get_softc(dev);
1455b9450e43SRui Paulo 	sc->dev = dev;
1456b9450e43SRui Paulo 
1457b9450e43SRui Paulo 	/* initial values */
1458b9450e43SRui Paulo 	sc->arm_max_freq = -1;
1459b9450e43SRui Paulo 	sc->arm_min_freq = -1;
1460b9450e43SRui Paulo 	sc->core_max_freq = -1;
1461b9450e43SRui Paulo 	sc->core_min_freq = -1;
1462b9450e43SRui Paulo 	sc->sdram_max_freq = -1;
1463b9450e43SRui Paulo 	sc->sdram_min_freq = -1;
1464b9450e43SRui Paulo 	sc->max_voltage_core = 0;
1465b9450e43SRui Paulo 	sc->min_voltage_core = 0;
1466b9450e43SRui Paulo 
1467b9450e43SRui Paulo 	/* create VC mbox buffer */
1468b9450e43SRui Paulo 	sc->dma_size = PAGE_SIZE;
1469b9450e43SRui Paulo 	err = bus_dma_tag_create(
1470b9450e43SRui Paulo 	    bus_get_dma_tag(sc->dev),
1471b9450e43SRui Paulo 	    PAGE_SIZE, 0,		/* alignment, boundary */
1472b9450e43SRui Paulo 	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1473b9450e43SRui Paulo 	    BUS_SPACE_MAXADDR,		/* highaddr */
1474b9450e43SRui Paulo 	    NULL, NULL,			/* filter, filterarg */
1475b9450e43SRui Paulo 	    sc->dma_size, 1,		/* maxsize, nsegments */
1476b9450e43SRui Paulo 	    sc->dma_size, 0,		/* maxsegsize, flags */
1477b9450e43SRui Paulo 	    NULL, NULL,			/* lockfunc, lockarg */
1478b9450e43SRui Paulo 	    &sc->dma_tag);
1479b9450e43SRui Paulo 	if (err) {
1480b9450e43SRui Paulo 		device_printf(dev, "can't create DMA tag\n");
1481b9450e43SRui Paulo 		return (ENXIO);
1482b9450e43SRui Paulo 	}
1483b9450e43SRui Paulo 
1484b9450e43SRui Paulo 	err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->dma_buf, 0,
1485b9450e43SRui Paulo 	    &sc->dma_map);
1486b9450e43SRui Paulo 	if (err) {
1487b9450e43SRui Paulo 		bus_dma_tag_destroy(sc->dma_tag);
1488b9450e43SRui Paulo 		device_printf(dev, "can't allocate dmamem\n");
1489b9450e43SRui Paulo 		return (ENXIO);
1490b9450e43SRui Paulo 	}
1491b9450e43SRui Paulo 
1492b9450e43SRui Paulo 	err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->dma_buf,
1493b9450e43SRui Paulo 	    sc->dma_size, bcm2835_cpufreq_cb, &sc->dma_phys, 0);
1494b9450e43SRui Paulo 	if (err) {
1495b9450e43SRui Paulo 		bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map);
1496b9450e43SRui Paulo 		bus_dma_tag_destroy(sc->dma_tag);
1497b9450e43SRui Paulo 		device_printf(dev, "can't load DMA map\n");
1498b9450e43SRui Paulo 		return (ENXIO);
1499b9450e43SRui Paulo 	}
1500b9450e43SRui Paulo 	/* OK, ready to use VC buffer */
1501b9450e43SRui Paulo 
1502b9450e43SRui Paulo 	/* setup sysctl at first device */
1503b9450e43SRui Paulo 	if (device_get_unit(dev) == 0) {
1504b9450e43SRui Paulo 		sysctl_ctx_init(&bcm2835_sysctl_ctx);
1505b9450e43SRui Paulo 		/* create node for hw.cpufreq */
1506b9450e43SRui Paulo 		oid = SYSCTL_ADD_NODE(&bcm2835_sysctl_ctx,
1507b9450e43SRui Paulo 		    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "cpufreq",
1508b9450e43SRui Paulo 		    CTLFLAG_RD, NULL, "");
1509b9450e43SRui Paulo 
1510b9450e43SRui Paulo 		/* Frequency (Hz) */
1511b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1512b9450e43SRui Paulo 		    OID_AUTO, "arm_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1513b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_arm_freq, "IU",
1514b9450e43SRui Paulo 		    "ARM frequency (Hz)");
1515b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1516b9450e43SRui Paulo 		    OID_AUTO, "core_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1517b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_core_freq, "IU",
1518b9450e43SRui Paulo 		    "Core frequency (Hz)");
1519b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1520b9450e43SRui Paulo 		    OID_AUTO, "sdram_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1521b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_sdram_freq, "IU",
1522b9450e43SRui Paulo 		    "SDRAM frequency (Hz)");
1523b9450e43SRui Paulo 
1524b9450e43SRui Paulo 		/* Turbo state */
1525b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1526b9450e43SRui Paulo 		    OID_AUTO, "turbo", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1527b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_turbo, "IU",
1528b9450e43SRui Paulo 		    "Disables dynamic clocking");
1529b9450e43SRui Paulo 
1530b9450e43SRui Paulo 		/* Voltage (offset from 1.2V in units of 0.025V) */
1531b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1532b9450e43SRui Paulo 		    OID_AUTO, "voltage_core", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
1533b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_voltage_core, "I",
1534b9450e43SRui Paulo 		    "ARM/GPU core voltage"
1535b9450e43SRui Paulo 		    "(offset from 1.2V in units of 0.025V)");
1536b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1537b9450e43SRui Paulo 		    OID_AUTO, "voltage_sdram", CTLTYPE_INT | CTLFLAG_WR, sc,
1538b9450e43SRui Paulo 		    0, sysctl_bcm2835_cpufreq_voltage_sdram, "I",
1539b9450e43SRui Paulo 		    "SDRAM voltage (offset from 1.2V in units of 0.025V)");
1540b9450e43SRui Paulo 
1541b9450e43SRui Paulo 		/* Voltage individual SDRAM */
1542b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1543b9450e43SRui Paulo 		    OID_AUTO, "voltage_sdram_c", CTLTYPE_INT | CTLFLAG_RW, sc,
1544b9450e43SRui Paulo 		    0, sysctl_bcm2835_cpufreq_voltage_sdram_c, "I",
1545b9450e43SRui Paulo 		    "SDRAM controller voltage"
1546b9450e43SRui Paulo 		    "(offset from 1.2V in units of 0.025V)");
1547b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1548b9450e43SRui Paulo 		    OID_AUTO, "voltage_sdram_i", CTLTYPE_INT | CTLFLAG_RW, sc,
1549b9450e43SRui Paulo 		    0, sysctl_bcm2835_cpufreq_voltage_sdram_i, "I",
1550b9450e43SRui Paulo 		    "SDRAM I/O voltage (offset from 1.2V in units of 0.025V)");
1551b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1552b9450e43SRui Paulo 		    OID_AUTO, "voltage_sdram_p", CTLTYPE_INT | CTLFLAG_RW, sc,
1553b9450e43SRui Paulo 		    0, sysctl_bcm2835_cpufreq_voltage_sdram_p, "I",
1554b9450e43SRui Paulo 		    "SDRAM phy voltage (offset from 1.2V in units of 0.025V)");
1555b9450e43SRui Paulo 
1556b9450e43SRui Paulo 		/* Temperature */
1557b9450e43SRui Paulo 		SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
1558b9450e43SRui Paulo 		    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
1559b9450e43SRui Paulo 		    sysctl_bcm2835_cpufreq_temperature, "I",
1560b9450e43SRui Paulo 		    "SoC temperature (thousandths of a degree C)");
1561b9450e43SRui Paulo 	}
1562b9450e43SRui Paulo 
1563b9450e43SRui Paulo 	/* ARM->VC lock */
1564b9450e43SRui Paulo 	sema_init(&vc_sema, 1, "vcsema");
1565b9450e43SRui Paulo 
1566b9450e43SRui Paulo 	/* register callback for using mbox when interrupts are enabled */
1567b9450e43SRui Paulo 	sc->init_hook.ich_func = bcm2835_cpufreq_init;
1568b9450e43SRui Paulo 	sc->init_hook.ich_arg = sc;
1569b9450e43SRui Paulo 
1570b9450e43SRui Paulo 	if (config_intrhook_establish(&sc->init_hook) != 0) {
1571b9450e43SRui Paulo 		bus_dmamap_unload(sc->dma_tag, sc->dma_map);
1572b9450e43SRui Paulo 		bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map);
1573b9450e43SRui Paulo 		bus_dma_tag_destroy(sc->dma_tag);
1574b9450e43SRui Paulo 		device_printf(dev, "config_intrhook_establish failed\n");
1575b9450e43SRui Paulo 		return (ENOMEM);
1576b9450e43SRui Paulo 	}
1577b9450e43SRui Paulo 
1578b9450e43SRui Paulo 	/* this device is controlled by cpufreq(4) */
1579b9450e43SRui Paulo 	cpufreq_register(dev);
1580b9450e43SRui Paulo 
1581b9450e43SRui Paulo 	return (0);
1582b9450e43SRui Paulo }
1583b9450e43SRui Paulo 
1584b9450e43SRui Paulo static int
1585b9450e43SRui Paulo bcm2835_cpufreq_detach(device_t dev)
1586b9450e43SRui Paulo {
1587b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1588b9450e43SRui Paulo 
1589b9450e43SRui Paulo 	sc = device_get_softc(dev);
1590b9450e43SRui Paulo 
1591b9450e43SRui Paulo 	sema_destroy(&vc_sema);
1592b9450e43SRui Paulo 
1593b9450e43SRui Paulo 	if (sc->dma_phys != 0)
1594b9450e43SRui Paulo 		bus_dmamap_unload(sc->dma_tag, sc->dma_map);
1595b9450e43SRui Paulo 	if (sc->dma_buf != NULL)
1596b9450e43SRui Paulo 		bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map);
1597b9450e43SRui Paulo 	if (sc->dma_tag != NULL)
1598b9450e43SRui Paulo 		bus_dma_tag_destroy(sc->dma_tag);
1599b9450e43SRui Paulo 
1600b9450e43SRui Paulo 	return (cpufreq_unregister(dev));
1601b9450e43SRui Paulo }
1602b9450e43SRui Paulo 
1603b9450e43SRui Paulo static int
1604b9450e43SRui Paulo bcm2835_cpufreq_set(device_t dev, const struct cf_setting *cf)
1605b9450e43SRui Paulo {
1606b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1607b9450e43SRui Paulo 	uint32_t rate_hz, rem;
1608b9450e43SRui Paulo 	int cur_freq, resp_freq, arm_freq, min_freq, core_freq;
1609b9450e43SRui Paulo 
1610b9450e43SRui Paulo 	if (cf == NULL || cf->freq < 0)
1611b9450e43SRui Paulo 		return (EINVAL);
1612b9450e43SRui Paulo 
1613b9450e43SRui Paulo 	sc = device_get_softc(dev);
1614b9450e43SRui Paulo 
1615b9450e43SRui Paulo 	/* setting clock (Hz) */
1616b9450e43SRui Paulo 	rate_hz = (uint32_t)MHZ2HZ(cf->freq);
1617b9450e43SRui Paulo 	rem = rate_hz % HZSTEP;
1618b9450e43SRui Paulo 	rate_hz -= rem;
1619b9450e43SRui Paulo 	if (rate_hz == 0)
1620b9450e43SRui Paulo 		return (EINVAL);
1621b9450e43SRui Paulo 
1622b9450e43SRui Paulo 	/* adjust min freq */
1623b9450e43SRui Paulo 	min_freq = sc->arm_min_freq;
1624b9450e43SRui Paulo 	if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON)
1625b9450e43SRui Paulo 		if (min_freq > cpufreq_lowest_freq)
1626b9450e43SRui Paulo 			min_freq = cpufreq_lowest_freq;
1627b9450e43SRui Paulo 
1628b9450e43SRui Paulo 	if (rate_hz < MHZ2HZ(min_freq) || rate_hz > MHZ2HZ(sc->arm_max_freq))
1629b9450e43SRui Paulo 		return (EINVAL);
1630b9450e43SRui Paulo 
1631b9450e43SRui Paulo 	/* set new value and verify it */
1632b9450e43SRui Paulo 	VC_LOCK(sc);
1633b9450e43SRui Paulo 	cur_freq = bcm2835_cpufreq_get_clock_rate(sc,
1634b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1635b9450e43SRui Paulo 	resp_freq = bcm2835_cpufreq_set_clock_rate(sc,
1636b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM, rate_hz);
1637b9450e43SRui Paulo 	DELAY(TRANSITION_LATENCY);
1638b9450e43SRui Paulo 	arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
1639b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1640b9450e43SRui Paulo 
1641b9450e43SRui Paulo 	/*
1642b9450e43SRui Paulo 	 * if non-turbo and lower than or equal min_freq,
1643b9450e43SRui Paulo 	 * clock down core and sdram to default first.
1644b9450e43SRui Paulo 	 */
1645b9450e43SRui Paulo 	if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) {
1646b9450e43SRui Paulo 		core_freq = bcm2835_cpufreq_get_clock_rate(sc,
1647b9450e43SRui Paulo 		    BCM2835_MBOX_CLOCK_ID_CORE);
1648b9450e43SRui Paulo 		if (rate_hz > MHZ2HZ(sc->arm_min_freq)) {
1649b9450e43SRui Paulo 			bcm2835_cpufreq_set_clock_rate(sc,
1650b9450e43SRui Paulo 			    BCM2835_MBOX_CLOCK_ID_CORE,
1651b9450e43SRui Paulo 			    MHZ2HZ(sc->core_max_freq));
1652b9450e43SRui Paulo 			DELAY(TRANSITION_LATENCY);
1653b9450e43SRui Paulo 			bcm2835_cpufreq_set_clock_rate(sc,
1654b9450e43SRui Paulo 			    BCM2835_MBOX_CLOCK_ID_SDRAM,
1655b9450e43SRui Paulo 			    MHZ2HZ(sc->sdram_max_freq));
1656b9450e43SRui Paulo 			DELAY(TRANSITION_LATENCY);
1657b9450e43SRui Paulo 		} else {
1658b9450e43SRui Paulo 			if (sc->core_min_freq < DEFAULT_CORE_FREQUENCY &&
1659b9450e43SRui Paulo 			    core_freq > DEFAULT_CORE_FREQUENCY) {
1660b9450e43SRui Paulo 				/* first, down to 250, then down to min */
1661b9450e43SRui Paulo 				DELAY(TRANSITION_LATENCY);
1662b9450e43SRui Paulo 				bcm2835_cpufreq_set_clock_rate(sc,
1663b9450e43SRui Paulo 				    BCM2835_MBOX_CLOCK_ID_CORE,
1664b9450e43SRui Paulo 				    MHZ2HZ(DEFAULT_CORE_FREQUENCY));
1665b9450e43SRui Paulo 				DELAY(TRANSITION_LATENCY);
1666b9450e43SRui Paulo 				/* reset core voltage */
1667b9450e43SRui Paulo 				bcm2835_cpufreq_set_voltage(sc,
1668b9450e43SRui Paulo 				    BCM2835_MBOX_VOLTAGE_ID_CORE, 0);
1669b9450e43SRui Paulo 				DELAY(TRANSITION_LATENCY);
1670b9450e43SRui Paulo 			}
1671b9450e43SRui Paulo 			bcm2835_cpufreq_set_clock_rate(sc,
1672b9450e43SRui Paulo 			    BCM2835_MBOX_CLOCK_ID_CORE,
1673b9450e43SRui Paulo 			    MHZ2HZ(sc->core_min_freq));
1674b9450e43SRui Paulo 			DELAY(TRANSITION_LATENCY);
1675b9450e43SRui Paulo 			bcm2835_cpufreq_set_clock_rate(sc,
1676b9450e43SRui Paulo 			    BCM2835_MBOX_CLOCK_ID_SDRAM,
1677b9450e43SRui Paulo 			    MHZ2HZ(sc->sdram_min_freq));
1678b9450e43SRui Paulo 			DELAY(TRANSITION_LATENCY);
1679b9450e43SRui Paulo 		}
1680b9450e43SRui Paulo 	}
1681b9450e43SRui Paulo 
1682b9450e43SRui Paulo 	VC_UNLOCK(sc);
1683b9450e43SRui Paulo 
1684b9450e43SRui Paulo 	if (resp_freq < 0 || arm_freq < 0 || resp_freq != arm_freq) {
1685b9450e43SRui Paulo 		device_printf(dev, "wrong freq\n");
1686b9450e43SRui Paulo 		return (EIO);
1687b9450e43SRui Paulo 	}
1688b9450e43SRui Paulo 	DPRINTF("cpufreq: %d -> %d\n", cur_freq, arm_freq);
1689b9450e43SRui Paulo 
1690b9450e43SRui Paulo 	return (0);
1691b9450e43SRui Paulo }
1692b9450e43SRui Paulo 
1693b9450e43SRui Paulo static int
1694b9450e43SRui Paulo bcm2835_cpufreq_get(device_t dev, struct cf_setting *cf)
1695b9450e43SRui Paulo {
1696b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1697b9450e43SRui Paulo 	int arm_freq;
1698b9450e43SRui Paulo 
1699b9450e43SRui Paulo 	if (cf == NULL)
1700b9450e43SRui Paulo 		return (EINVAL);
1701b9450e43SRui Paulo 
1702b9450e43SRui Paulo 	sc = device_get_softc(dev);
1703b9450e43SRui Paulo 	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
1704b9450e43SRui Paulo 	cf->dev = NULL;
1705b9450e43SRui Paulo 
1706b9450e43SRui Paulo 	/* get cuurent value */
1707b9450e43SRui Paulo 	VC_LOCK(sc);
1708b9450e43SRui Paulo 	arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
1709b9450e43SRui Paulo 	    BCM2835_MBOX_CLOCK_ID_ARM);
1710b9450e43SRui Paulo 	VC_UNLOCK(sc);
1711b9450e43SRui Paulo 	if (arm_freq < 0) {
1712b9450e43SRui Paulo 		device_printf(dev, "can't get clock\n");
1713b9450e43SRui Paulo 		return (EINVAL);
1714b9450e43SRui Paulo 	}
1715b9450e43SRui Paulo 
1716b9450e43SRui Paulo 	/* CPU clock in MHz or 100ths of a percent. */
1717b9450e43SRui Paulo 	cf->freq = HZ2MHZ(arm_freq);
1718b9450e43SRui Paulo 	/* Voltage in mV. */
1719b9450e43SRui Paulo 	cf->volts = CPUFREQ_VAL_UNKNOWN;
1720b9450e43SRui Paulo 	/* Power consumed in mW. */
1721b9450e43SRui Paulo 	cf->power = CPUFREQ_VAL_UNKNOWN;
1722b9450e43SRui Paulo 	/* Transition latency in us. */
1723b9450e43SRui Paulo 	cf->lat = TRANSITION_LATENCY;
1724b9450e43SRui Paulo 	/* Driver providing this setting. */
1725b9450e43SRui Paulo 	cf->dev = dev;
1726b9450e43SRui Paulo 
1727b9450e43SRui Paulo 	return (0);
1728b9450e43SRui Paulo }
1729b9450e43SRui Paulo 
1730b9450e43SRui Paulo static int
1731b9450e43SRui Paulo bcm2835_cpufreq_make_freq_list(device_t dev, struct cf_setting *sets,
1732b9450e43SRui Paulo     int *count)
1733b9450e43SRui Paulo {
1734b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1735b9450e43SRui Paulo 	int freq, min_freq, volts, rem;
1736b9450e43SRui Paulo 	int idx;
1737b9450e43SRui Paulo 
1738b9450e43SRui Paulo 	sc = device_get_softc(dev);
1739b9450e43SRui Paulo 	freq = sc->arm_max_freq;
1740b9450e43SRui Paulo 	min_freq = sc->arm_min_freq;
1741b9450e43SRui Paulo 
1742b9450e43SRui Paulo 	/* adjust head freq to STEP */
1743b9450e43SRui Paulo 	rem = freq % MHZSTEP;
1744b9450e43SRui Paulo 	freq -= rem;
1745b9450e43SRui Paulo 	if (freq < min_freq)
1746b9450e43SRui Paulo 		freq = min_freq;
1747b9450e43SRui Paulo 
1748b9450e43SRui Paulo 	/* if non-turbo, add extra low freq */
1749b9450e43SRui Paulo 	if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON)
1750b9450e43SRui Paulo 		if (min_freq > cpufreq_lowest_freq)
1751b9450e43SRui Paulo 			min_freq = cpufreq_lowest_freq;
1752b9450e43SRui Paulo 
175311cede48SLuiz Otavio O Souza #ifdef SOC_BCM2836
175411cede48SLuiz Otavio O Souza 	/* XXX RPi2 have only 900/600MHz */
175511cede48SLuiz Otavio O Souza 	idx = 0;
175611cede48SLuiz Otavio O Souza 	volts = sc->min_voltage_core;
175711cede48SLuiz Otavio O Souza 	sets[idx].freq = freq;
175811cede48SLuiz Otavio O Souza 	sets[idx].volts = volts;
175911cede48SLuiz Otavio O Souza 	sets[idx].lat = TRANSITION_LATENCY;
176011cede48SLuiz Otavio O Souza 	sets[idx].dev = dev;
176111cede48SLuiz Otavio O Souza 	idx++;
176211cede48SLuiz Otavio O Souza 	if (freq != min_freq) {
176311cede48SLuiz Otavio O Souza 		sets[idx].freq = min_freq;
176411cede48SLuiz Otavio O Souza 		sets[idx].volts = volts;
176511cede48SLuiz Otavio O Souza 		sets[idx].lat = TRANSITION_LATENCY;
176611cede48SLuiz Otavio O Souza 		sets[idx].dev = dev;
176711cede48SLuiz Otavio O Souza 		idx++;
176811cede48SLuiz Otavio O Souza 	}
176911cede48SLuiz Otavio O Souza #else
1770b9450e43SRui Paulo 	/* from freq to min_freq */
1771b9450e43SRui Paulo 	for (idx = 0; idx < *count && freq >= min_freq; idx++) {
1772b9450e43SRui Paulo 		if (freq > sc->arm_min_freq)
1773b9450e43SRui Paulo 			volts = sc->max_voltage_core;
1774b9450e43SRui Paulo 		else
1775b9450e43SRui Paulo 			volts = sc->min_voltage_core;
1776b9450e43SRui Paulo 		sets[idx].freq = freq;
1777b9450e43SRui Paulo 		sets[idx].volts = volts;
1778b9450e43SRui Paulo 		sets[idx].lat = TRANSITION_LATENCY;
1779b9450e43SRui Paulo 		sets[idx].dev = dev;
1780b9450e43SRui Paulo 		freq -= MHZSTEP;
1781b9450e43SRui Paulo 	}
178211cede48SLuiz Otavio O Souza #endif
178311cede48SLuiz Otavio O Souza 	*count = idx;
1784b9450e43SRui Paulo 
1785b9450e43SRui Paulo 	return (0);
1786b9450e43SRui Paulo }
1787b9450e43SRui Paulo 
1788b9450e43SRui Paulo static int
1789b9450e43SRui Paulo bcm2835_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
1790b9450e43SRui Paulo {
1791b9450e43SRui Paulo 	struct bcm2835_cpufreq_softc *sc;
1792b9450e43SRui Paulo 
1793b9450e43SRui Paulo 	if (sets == NULL || count == NULL)
1794b9450e43SRui Paulo 		return (EINVAL);
1795b9450e43SRui Paulo 
1796b9450e43SRui Paulo 	sc = device_get_softc(dev);
1797b9450e43SRui Paulo 	if (sc->arm_min_freq < 0 || sc->arm_max_freq < 0) {
1798b9450e43SRui Paulo 		printf("device is not configured\n");
1799b9450e43SRui Paulo 		return (EINVAL);
1800b9450e43SRui Paulo 	}
1801b9450e43SRui Paulo 
1802b9450e43SRui Paulo 	/* fill data with unknown value */
1803b9450e43SRui Paulo 	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
1804b9450e43SRui Paulo 	/* create new array up to count */
1805b9450e43SRui Paulo 	bcm2835_cpufreq_make_freq_list(dev, sets, count);
1806b9450e43SRui Paulo 
1807b9450e43SRui Paulo 	return (0);
1808b9450e43SRui Paulo }
1809b9450e43SRui Paulo 
1810b9450e43SRui Paulo static int
1811b9450e43SRui Paulo bcm2835_cpufreq_type(device_t dev, int *type)
1812b9450e43SRui Paulo {
1813b9450e43SRui Paulo 
1814b9450e43SRui Paulo 	if (type == NULL)
1815b9450e43SRui Paulo 		return (EINVAL);
1816b9450e43SRui Paulo 	*type = CPUFREQ_TYPE_ABSOLUTE;
1817b9450e43SRui Paulo 
1818b9450e43SRui Paulo 	return (0);
1819b9450e43SRui Paulo }
1820b9450e43SRui Paulo 
1821b9450e43SRui Paulo static device_method_t bcm2835_cpufreq_methods[] = {
1822b9450e43SRui Paulo 	/* Device interface */
1823b9450e43SRui Paulo 	DEVMETHOD(device_identify,	bcm2835_cpufreq_identify),
1824b9450e43SRui Paulo 	DEVMETHOD(device_probe,		bcm2835_cpufreq_probe),
1825b9450e43SRui Paulo 	DEVMETHOD(device_attach,	bcm2835_cpufreq_attach),
1826b9450e43SRui Paulo 	DEVMETHOD(device_detach,	bcm2835_cpufreq_detach),
1827b9450e43SRui Paulo 
1828b9450e43SRui Paulo 	/* cpufreq interface */
1829b9450e43SRui Paulo 	DEVMETHOD(cpufreq_drv_set,	bcm2835_cpufreq_set),
1830b9450e43SRui Paulo 	DEVMETHOD(cpufreq_drv_get,	bcm2835_cpufreq_get),
1831b9450e43SRui Paulo 	DEVMETHOD(cpufreq_drv_settings,	bcm2835_cpufreq_settings),
1832b9450e43SRui Paulo 	DEVMETHOD(cpufreq_drv_type,	bcm2835_cpufreq_type),
1833b9450e43SRui Paulo 
1834b9450e43SRui Paulo 	DEVMETHOD_END
1835b9450e43SRui Paulo };
1836b9450e43SRui Paulo 
1837b9450e43SRui Paulo static devclass_t bcm2835_cpufreq_devclass;
1838b9450e43SRui Paulo static driver_t bcm2835_cpufreq_driver = {
1839b9450e43SRui Paulo 	"bcm2835_cpufreq",
1840b9450e43SRui Paulo 	bcm2835_cpufreq_methods,
1841b9450e43SRui Paulo 	sizeof(struct bcm2835_cpufreq_softc),
1842b9450e43SRui Paulo };
1843b9450e43SRui Paulo 
1844b9450e43SRui Paulo DRIVER_MODULE(bcm2835_cpufreq, cpu, bcm2835_cpufreq_driver,
1845b9450e43SRui Paulo     bcm2835_cpufreq_devclass, 0, 0);
1846