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/param.h>
29b9450e43SRui Paulo #include <sys/systm.h>
30b9450e43SRui Paulo #include <sys/bus.h>
31b9450e43SRui Paulo #include <sys/cpu.h>
32b9450e43SRui Paulo #include <sys/kernel.h>
33b9450e43SRui Paulo #include <sys/lock.h>
34b9450e43SRui Paulo #include <sys/malloc.h>
35b9450e43SRui Paulo #include <sys/module.h>
36b9450e43SRui Paulo #include <sys/mutex.h>
37b9450e43SRui Paulo #include <sys/sema.h>
38b9450e43SRui Paulo #include <sys/sysctl.h>
39b9450e43SRui Paulo
40b9450e43SRui Paulo #include <machine/bus.h>
41b9450e43SRui Paulo #include <machine/cpu.h>
42b9450e43SRui Paulo #include <machine/intr.h>
43b9450e43SRui Paulo
44e374c335SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
45e374c335SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
46e374c335SEmmanuel Vadot
477413ae0eSAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_firmware.h>
48b9450e43SRui Paulo #include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
49b9450e43SRui Paulo
50b9450e43SRui Paulo #include "cpufreq_if.h"
51b9450e43SRui Paulo
52b9450e43SRui Paulo #ifdef DEBUG
53b9450e43SRui Paulo #define DPRINTF(fmt, ...) do { \
54b9450e43SRui Paulo printf("%s:%u: ", __func__, __LINE__); \
55b9450e43SRui Paulo printf(fmt, ##__VA_ARGS__); \
56b9450e43SRui Paulo } while (0)
57b9450e43SRui Paulo #else
58b9450e43SRui Paulo #define DPRINTF(fmt, ...)
59b9450e43SRui Paulo #endif
60b9450e43SRui Paulo
61b9450e43SRui Paulo #define HZ2MHZ(freq) ((freq) / (1000 * 1000))
62b9450e43SRui Paulo #define MHZ2HZ(freq) ((freq) * (1000 * 1000))
6311cede48SLuiz Otavio O Souza
64f1f425aeSOleksandr Tymoshenko #define OFFSET2MVOLT(val) (((val) / 1000))
65f1f425aeSOleksandr Tymoshenko #define MVOLT2OFFSET(val) (((val) * 1000))
66f1f425aeSOleksandr Tymoshenko #define DEFAULT_ARM_FREQUENCY 600
67f1f425aeSOleksandr Tymoshenko #define DEFAULT_LOWEST_FREQ 600
68b9450e43SRui Paulo #define DEFAULT_CORE_FREQUENCY 250
69b9450e43SRui Paulo #define DEFAULT_SDRAM_FREQUENCY 400
70b9450e43SRui Paulo #define TRANSITION_LATENCY 1000
71b9450e43SRui Paulo #define MIN_OVER_VOLTAGE -16
72b9450e43SRui Paulo #define MAX_OVER_VOLTAGE 6
73b9450e43SRui Paulo #define MSG_ERROR -999999999
74b9450e43SRui Paulo #define MHZSTEP 100
75b9450e43SRui Paulo #define HZSTEP (MHZ2HZ(MHZSTEP))
769d6672e1SLuiz Otavio O Souza #define TZ_ZEROC 2731
77b9450e43SRui Paulo
78b9450e43SRui Paulo #define VC_LOCK(sc) do { \
79b9450e43SRui Paulo sema_wait(&vc_sema); \
80b9450e43SRui Paulo } while (0)
81b9450e43SRui Paulo #define VC_UNLOCK(sc) do { \
82b9450e43SRui Paulo sema_post(&vc_sema); \
83b9450e43SRui Paulo } while (0)
84b9450e43SRui Paulo
85b9450e43SRui Paulo /* ARM->VC mailbox property semaphore */
86b9450e43SRui Paulo static struct sema vc_sema;
87b9450e43SRui Paulo
88b9450e43SRui Paulo static struct sysctl_ctx_list bcm2835_sysctl_ctx;
89b9450e43SRui Paulo
90b9450e43SRui Paulo struct bcm2835_cpufreq_softc {
91b9450e43SRui Paulo device_t dev;
927413ae0eSAndrew Turner device_t firmware;
93b9450e43SRui Paulo int arm_max_freq;
94b9450e43SRui Paulo int arm_min_freq;
95b9450e43SRui Paulo int core_max_freq;
96b9450e43SRui Paulo int core_min_freq;
97b9450e43SRui Paulo int sdram_max_freq;
98b9450e43SRui Paulo int sdram_min_freq;
99b9450e43SRui Paulo int max_voltage_core;
100b9450e43SRui Paulo int min_voltage_core;
101b9450e43SRui Paulo
102b9450e43SRui Paulo /* the values written in mbox */
103b9450e43SRui Paulo int voltage_core;
104b9450e43SRui Paulo int voltage_sdram;
105b9450e43SRui Paulo int voltage_sdram_c;
106b9450e43SRui Paulo int voltage_sdram_i;
107b9450e43SRui Paulo int voltage_sdram_p;
108b9450e43SRui Paulo int turbo_mode;
109b9450e43SRui Paulo
110b9450e43SRui Paulo /* initial hook for waiting mbox intr */
111b9450e43SRui Paulo struct intr_config_hook init_hook;
112b9450e43SRui Paulo };
113b9450e43SRui Paulo
114e374c335SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
115e374c335SEmmanuel Vadot { "broadcom,bcm2835-vc", 1 },
116e374c335SEmmanuel Vadot { "broadcom,bcm2708-vc", 1 },
117e374c335SEmmanuel Vadot { "brcm,bcm2709", 1 },
118e446dd5dSPeter Jeremy { "brcm,bcm2835", 1 },
119f362a398SEmmanuel Vadot { "brcm,bcm2836", 1 },
120adcd9597SOleksandr Tymoshenko { "brcm,bcm2837", 1 },
121e245e555SKyle Evans { "brcm,bcm2711", 1 },
122e374c335SEmmanuel Vadot { NULL, 0 }
123e374c335SEmmanuel Vadot };
124e374c335SEmmanuel Vadot
125b9450e43SRui Paulo static int cpufreq_verbose = 0;
126b9450e43SRui Paulo TUNABLE_INT("hw.bcm2835.cpufreq.verbose", &cpufreq_verbose);
127b9450e43SRui Paulo static int cpufreq_lowest_freq = DEFAULT_LOWEST_FREQ;
128b9450e43SRui Paulo TUNABLE_INT("hw.bcm2835.cpufreq.lowest_freq", &cpufreq_lowest_freq);
129b9450e43SRui Paulo
130e9faba9dSLuiz Otavio O Souza #ifdef PROP_DEBUG
131b9450e43SRui Paulo static void
bcm2835_dump(const void * data,int len)132b9450e43SRui Paulo bcm2835_dump(const void *data, int len)
133b9450e43SRui Paulo {
134b9450e43SRui Paulo const uint8_t *p = (const uint8_t*)data;
135b9450e43SRui Paulo int i;
136b9450e43SRui Paulo
137b9450e43SRui Paulo printf("dump @ %p:\n", data);
138b9450e43SRui Paulo for (i = 0; i < len; i++) {
139b9450e43SRui Paulo printf("%2.2x ", p[i]);
140b9450e43SRui Paulo if ((i % 4) == 3)
141b9450e43SRui Paulo printf(" ");
142b9450e43SRui Paulo if ((i % 16) == 15)
143b9450e43SRui Paulo printf("\n");
144b9450e43SRui Paulo }
145b9450e43SRui Paulo printf("\n");
146b9450e43SRui Paulo }
147b9450e43SRui Paulo #endif
148b9450e43SRui Paulo
149b9450e43SRui Paulo static int
bcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc * sc,uint32_t clock_id)150b9450e43SRui Paulo bcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc *sc,
151b9450e43SRui Paulo uint32_t clock_id)
152b9450e43SRui Paulo {
1537413ae0eSAndrew Turner union msg_get_clock_rate_body msg;
154b9450e43SRui Paulo int rate;
155b9450e43SRui Paulo int err;
156b9450e43SRui Paulo
157b9450e43SRui Paulo /*
158b9450e43SRui Paulo * Get clock rate
159b9450e43SRui Paulo * Tag: 0x00030002
160b9450e43SRui Paulo * Request:
161b9450e43SRui Paulo * Length: 4
162b9450e43SRui Paulo * Value:
163b9450e43SRui Paulo * u32: clock id
164b9450e43SRui Paulo * Response:
165b9450e43SRui Paulo * Length: 8
166b9450e43SRui Paulo * Value:
167b9450e43SRui Paulo * u32: clock id
168b9450e43SRui Paulo * u32: rate (in Hz)
169b9450e43SRui Paulo */
170b9450e43SRui Paulo
171b9450e43SRui Paulo /* setup single tag buffer */
172cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
1737413ae0eSAndrew Turner msg.req.clock_id = clock_id;
174b9450e43SRui Paulo
175b9450e43SRui Paulo /* call mailbox property */
1767413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
1777413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_GET_CLOCK_RATE, &msg, sizeof(msg));
178b9450e43SRui Paulo if (err) {
179b9450e43SRui Paulo device_printf(sc->dev, "can't get clock rate (id=%u)\n",
180b9450e43SRui Paulo clock_id);
181b9450e43SRui Paulo return (MSG_ERROR);
182b9450e43SRui Paulo }
183b9450e43SRui Paulo
184b9450e43SRui Paulo /* result (Hz) */
1857413ae0eSAndrew Turner rate = (int)msg.resp.rate_hz;
186b9450e43SRui Paulo DPRINTF("clock = %d(Hz)\n", rate);
187b9450e43SRui Paulo return (rate);
188b9450e43SRui Paulo }
189b9450e43SRui Paulo
190b9450e43SRui Paulo static int
bcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc * sc,uint32_t clock_id)191b9450e43SRui Paulo bcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc *sc,
192b9450e43SRui Paulo uint32_t clock_id)
193b9450e43SRui Paulo {
1947413ae0eSAndrew Turner union msg_get_clock_rate_body msg;
195b9450e43SRui Paulo int rate;
196b9450e43SRui Paulo int err;
197b9450e43SRui Paulo
198b9450e43SRui Paulo /*
199b9450e43SRui Paulo * Get max clock rate
200b9450e43SRui Paulo * Tag: 0x00030004
201b9450e43SRui Paulo * Request:
202b9450e43SRui Paulo * Length: 4
203b9450e43SRui Paulo * Value:
204b9450e43SRui Paulo * u32: clock id
205b9450e43SRui Paulo * Response:
206b9450e43SRui Paulo * Length: 8
207b9450e43SRui Paulo * Value:
208b9450e43SRui Paulo * u32: clock id
209b9450e43SRui Paulo * u32: rate (in Hz)
210b9450e43SRui Paulo */
211b9450e43SRui Paulo
212b9450e43SRui Paulo /* setup single tag buffer */
213cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
2147413ae0eSAndrew Turner msg.req.clock_id = clock_id;
215b9450e43SRui Paulo
216b9450e43SRui Paulo /* call mailbox property */
2177413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
2187413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_GET_MAX_CLOCK_RATE, &msg, sizeof(msg));
219b9450e43SRui Paulo if (err) {
220b9450e43SRui Paulo device_printf(sc->dev, "can't get max clock rate (id=%u)\n",
221b9450e43SRui Paulo clock_id);
222b9450e43SRui Paulo return (MSG_ERROR);
223b9450e43SRui Paulo }
224b9450e43SRui Paulo
225b9450e43SRui Paulo /* result (Hz) */
2267413ae0eSAndrew Turner rate = (int)msg.resp.rate_hz;
227b9450e43SRui Paulo DPRINTF("clock = %d(Hz)\n", rate);
228b9450e43SRui Paulo return (rate);
229b9450e43SRui Paulo }
230b9450e43SRui Paulo
231b9450e43SRui Paulo static int
bcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc * sc,uint32_t clock_id)232b9450e43SRui Paulo bcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc *sc,
233b9450e43SRui Paulo uint32_t clock_id)
234b9450e43SRui Paulo {
2357413ae0eSAndrew Turner union msg_get_clock_rate_body msg;
236b9450e43SRui Paulo int rate;
237b9450e43SRui Paulo int err;
238b9450e43SRui Paulo
239b9450e43SRui Paulo /*
240b9450e43SRui Paulo * Get min clock rate
241b9450e43SRui Paulo * Tag: 0x00030007
242b9450e43SRui Paulo * Request:
243b9450e43SRui Paulo * Length: 4
244b9450e43SRui Paulo * Value:
245b9450e43SRui Paulo * u32: clock id
246b9450e43SRui Paulo * Response:
247b9450e43SRui Paulo * Length: 8
248b9450e43SRui Paulo * Value:
249b9450e43SRui Paulo * u32: clock id
250b9450e43SRui Paulo * u32: rate (in Hz)
251b9450e43SRui Paulo */
252b9450e43SRui Paulo
253b9450e43SRui Paulo /* setup single tag buffer */
254cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
2557413ae0eSAndrew Turner msg.req.clock_id = clock_id;
256b9450e43SRui Paulo
257b9450e43SRui Paulo /* call mailbox property */
2587413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
2597413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_GET_MIN_CLOCK_RATE, &msg, sizeof(msg));
260b9450e43SRui Paulo if (err) {
261b9450e43SRui Paulo device_printf(sc->dev, "can't get min clock rate (id=%u)\n",
262b9450e43SRui Paulo clock_id);
263b9450e43SRui Paulo return (MSG_ERROR);
264b9450e43SRui Paulo }
265b9450e43SRui Paulo
266b9450e43SRui Paulo /* result (Hz) */
2677413ae0eSAndrew Turner rate = (int)msg.resp.rate_hz;
268b9450e43SRui Paulo DPRINTF("clock = %d(Hz)\n", rate);
269b9450e43SRui Paulo return (rate);
270b9450e43SRui Paulo }
271b9450e43SRui Paulo
272b9450e43SRui Paulo static int
bcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc * sc,uint32_t clock_id,uint32_t rate_hz)273b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc *sc,
274b9450e43SRui Paulo uint32_t clock_id, uint32_t rate_hz)
275b9450e43SRui Paulo {
2767413ae0eSAndrew Turner union msg_set_clock_rate_body msg;
277b9450e43SRui Paulo int rate;
278b9450e43SRui Paulo int err;
279b9450e43SRui Paulo
280b9450e43SRui Paulo /*
281b9450e43SRui Paulo * Set clock rate
282b9450e43SRui Paulo * Tag: 0x00038002
283b9450e43SRui Paulo * Request:
284b9450e43SRui Paulo * Length: 8
285b9450e43SRui Paulo * Value:
286b9450e43SRui Paulo * u32: clock id
287b9450e43SRui Paulo * u32: rate (in Hz)
288b9450e43SRui Paulo * Response:
289b9450e43SRui Paulo * Length: 8
290b9450e43SRui Paulo * Value:
291b9450e43SRui Paulo * u32: clock id
292b9450e43SRui Paulo * u32: rate (in Hz)
293b9450e43SRui Paulo */
294b9450e43SRui Paulo
295b9450e43SRui Paulo /* setup single tag buffer */
296cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
2977413ae0eSAndrew Turner msg.req.clock_id = clock_id;
2987413ae0eSAndrew Turner msg.req.rate_hz = rate_hz;
299b9450e43SRui Paulo
300b9450e43SRui Paulo /* call mailbox property */
3017413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
3027413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_SET_CLOCK_RATE, &msg, sizeof(msg));
303b9450e43SRui Paulo if (err) {
304b9450e43SRui Paulo device_printf(sc->dev, "can't set clock rate (id=%u)\n",
305b9450e43SRui Paulo clock_id);
306b9450e43SRui Paulo return (MSG_ERROR);
307b9450e43SRui Paulo }
308b9450e43SRui Paulo
309b9450e43SRui Paulo /* workaround for core clock */
3107413ae0eSAndrew Turner if (clock_id == BCM2835_FIRMWARE_CLOCK_ID_CORE) {
311b9450e43SRui Paulo /* for safety (may change voltage without changing clock) */
312b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
313b9450e43SRui Paulo
314b9450e43SRui Paulo /*
315b9450e43SRui Paulo * XXX: the core clock is unable to change at once,
316b9450e43SRui Paulo * to change certainly, write it twice now.
317b9450e43SRui Paulo */
318b9450e43SRui Paulo
319b9450e43SRui Paulo /* setup single tag buffer */
320cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
3217413ae0eSAndrew Turner msg.req.clock_id = clock_id;
3227413ae0eSAndrew Turner msg.req.rate_hz = rate_hz;
323b9450e43SRui Paulo
324b9450e43SRui Paulo /* call mailbox property */
3257413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
3267413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_SET_CLOCK_RATE, &msg, sizeof(msg));
327b9450e43SRui Paulo if (err) {
328b9450e43SRui Paulo device_printf(sc->dev,
329b9450e43SRui Paulo "can't set clock rate (id=%u)\n", clock_id);
330b9450e43SRui Paulo return (MSG_ERROR);
331b9450e43SRui Paulo }
332b9450e43SRui Paulo }
333b9450e43SRui Paulo
334b9450e43SRui Paulo /* result (Hz) */
3357413ae0eSAndrew Turner rate = (int)msg.resp.rate_hz;
336b9450e43SRui Paulo DPRINTF("clock = %d(Hz)\n", rate);
337b9450e43SRui Paulo return (rate);
338b9450e43SRui Paulo }
339b9450e43SRui Paulo
340b9450e43SRui Paulo static int
bcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc * sc)341b9450e43SRui Paulo bcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc *sc)
342b9450e43SRui Paulo {
3437413ae0eSAndrew Turner union msg_get_turbo_body msg;
344b9450e43SRui Paulo int level;
345b9450e43SRui Paulo int err;
346b9450e43SRui Paulo
347b9450e43SRui Paulo /*
348b9450e43SRui Paulo * Get turbo
349b9450e43SRui Paulo * Tag: 0x00030009
350b9450e43SRui Paulo * Request:
351b9450e43SRui Paulo * Length: 4
352b9450e43SRui Paulo * Value:
353b9450e43SRui Paulo * u32: id
354b9450e43SRui Paulo * Response:
355b9450e43SRui Paulo * Length: 8
356b9450e43SRui Paulo * Value:
357b9450e43SRui Paulo * u32: id
358b9450e43SRui Paulo * u32: level
359b9450e43SRui Paulo */
360b9450e43SRui Paulo
361b9450e43SRui Paulo /* setup single tag buffer */
362cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
3637413ae0eSAndrew Turner msg.req.id = 0;
364b9450e43SRui Paulo
365b9450e43SRui Paulo /* call mailbox property */
3667413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
3677413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_GET_TURBO, &msg, sizeof(msg));
368b9450e43SRui Paulo if (err) {
369b9450e43SRui Paulo device_printf(sc->dev, "can't get turbo\n");
370b9450e43SRui Paulo return (MSG_ERROR);
371b9450e43SRui Paulo }
372b9450e43SRui Paulo
373b9450e43SRui Paulo /* result 0=non-turbo, 1=turbo */
3747413ae0eSAndrew Turner level = (int)msg.resp.level;
375b9450e43SRui Paulo DPRINTF("level = %d\n", level);
376b9450e43SRui Paulo return (level);
377b9450e43SRui Paulo }
378b9450e43SRui Paulo
379b9450e43SRui Paulo static int
bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc * sc,uint32_t level)380b9450e43SRui Paulo bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level)
381b9450e43SRui Paulo {
3827413ae0eSAndrew Turner union msg_set_turbo_body msg;
383b9450e43SRui Paulo int value;
384b9450e43SRui Paulo int err;
385b9450e43SRui Paulo
386b9450e43SRui Paulo /*
387b9450e43SRui Paulo * Set turbo
388b9450e43SRui Paulo * Tag: 0x00038009
389b9450e43SRui Paulo * Request:
390b9450e43SRui Paulo * Length: 8
391b9450e43SRui Paulo * Value:
392b9450e43SRui Paulo * u32: id
393b9450e43SRui Paulo * u32: level
394b9450e43SRui Paulo * Response:
395b9450e43SRui Paulo * Length: 8
396b9450e43SRui Paulo * Value:
397b9450e43SRui Paulo * u32: id
398b9450e43SRui Paulo * u32: level
399b9450e43SRui Paulo */
400b9450e43SRui Paulo
401b9450e43SRui Paulo /* replace unknown value to OFF */
4027413ae0eSAndrew Turner if (level != BCM2835_FIRMWARE_TURBO_ON &&
4037413ae0eSAndrew Turner level != BCM2835_FIRMWARE_TURBO_OFF)
4047413ae0eSAndrew Turner level = BCM2835_FIRMWARE_TURBO_OFF;
405b9450e43SRui Paulo
406b9450e43SRui Paulo /* setup single tag buffer */
407cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
4087413ae0eSAndrew Turner msg.req.id = 0;
4097413ae0eSAndrew Turner msg.req.level = level;
410b9450e43SRui Paulo
411b9450e43SRui Paulo /* call mailbox property */
4127413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
4137413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_SET_TURBO, &msg, sizeof(msg));
414b9450e43SRui Paulo if (err) {
415b9450e43SRui Paulo device_printf(sc->dev, "can't set turbo\n");
416b9450e43SRui Paulo return (MSG_ERROR);
417b9450e43SRui Paulo }
418b9450e43SRui Paulo
419b9450e43SRui Paulo /* result 0=non-turbo, 1=turbo */
4207413ae0eSAndrew Turner value = (int)msg.resp.level;
421b9450e43SRui Paulo DPRINTF("level = %d\n", value);
422b9450e43SRui Paulo return (value);
423b9450e43SRui Paulo }
424b9450e43SRui Paulo
425b9450e43SRui Paulo static int
bcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc * sc,uint32_t voltage_id)426b9450e43SRui Paulo bcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc *sc,
427b9450e43SRui Paulo uint32_t voltage_id)
428b9450e43SRui Paulo {
4297413ae0eSAndrew Turner union msg_get_voltage_body msg;
430b9450e43SRui Paulo int value;
431b9450e43SRui Paulo int err;
432b9450e43SRui Paulo
433b9450e43SRui Paulo /*
434b9450e43SRui Paulo * Get voltage
435b9450e43SRui Paulo * Tag: 0x00030003
436b9450e43SRui Paulo * Request:
437b9450e43SRui Paulo * Length: 4
438b9450e43SRui Paulo * Value:
439b9450e43SRui Paulo * u32: voltage id
440b9450e43SRui Paulo * Response:
441b9450e43SRui Paulo * Length: 8
442b9450e43SRui Paulo * Value:
443b9450e43SRui Paulo * u32: voltage id
444b9450e43SRui Paulo * u32: value (offset from 1.2V in units of 0.025V)
445b9450e43SRui Paulo */
446b9450e43SRui Paulo
447b9450e43SRui Paulo /* setup single tag buffer */
448cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
4497413ae0eSAndrew Turner msg.req.voltage_id = voltage_id;
450b9450e43SRui Paulo
451b9450e43SRui Paulo /* call mailbox property */
4527413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
4537413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_GET_VOLTAGE, &msg, sizeof(msg));
454b9450e43SRui Paulo if (err) {
455b9450e43SRui Paulo device_printf(sc->dev, "can't get voltage\n");
456b9450e43SRui Paulo return (MSG_ERROR);
457b9450e43SRui Paulo }
458b9450e43SRui Paulo
459b9450e43SRui Paulo /* result (offset from 1.2V) */
4607413ae0eSAndrew Turner value = (int)msg.resp.value;
461b9450e43SRui Paulo DPRINTF("value = %d\n", value);
462b9450e43SRui Paulo return (value);
463b9450e43SRui Paulo }
464b9450e43SRui Paulo
465b9450e43SRui Paulo static int
bcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc * sc,uint32_t voltage_id)466b9450e43SRui Paulo bcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc *sc,
467b9450e43SRui Paulo uint32_t voltage_id)
468b9450e43SRui Paulo {
4697413ae0eSAndrew Turner union msg_get_voltage_body msg;
470b9450e43SRui Paulo int value;
471b9450e43SRui Paulo int err;
472b9450e43SRui Paulo
473b9450e43SRui Paulo /*
474b9450e43SRui Paulo * Get voltage
475b9450e43SRui Paulo * Tag: 0x00030005
476b9450e43SRui Paulo * Request:
477b9450e43SRui Paulo * Length: 4
478b9450e43SRui Paulo * Value:
479b9450e43SRui Paulo * u32: voltage id
480b9450e43SRui Paulo * Response:
481b9450e43SRui Paulo * Length: 8
482b9450e43SRui Paulo * Value:
483b9450e43SRui Paulo * u32: voltage id
484b9450e43SRui Paulo * u32: value (offset from 1.2V in units of 0.025V)
485b9450e43SRui Paulo */
486b9450e43SRui Paulo
487b9450e43SRui Paulo /* setup single tag buffer */
488cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
4897413ae0eSAndrew Turner msg.req.voltage_id = voltage_id;
490b9450e43SRui Paulo
491b9450e43SRui Paulo /* call mailbox property */
4927413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
4937413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_GET_MAX_VOLTAGE, &msg, sizeof(msg));
494b9450e43SRui Paulo if (err) {
495b9450e43SRui Paulo device_printf(sc->dev, "can't get max voltage\n");
496b9450e43SRui Paulo return (MSG_ERROR);
497b9450e43SRui Paulo }
498b9450e43SRui Paulo
499b9450e43SRui Paulo /* result (offset from 1.2V) */
5007413ae0eSAndrew Turner value = (int)msg.resp.value;
501b9450e43SRui Paulo DPRINTF("value = %d\n", value);
502b9450e43SRui Paulo return (value);
503b9450e43SRui Paulo }
504b9450e43SRui Paulo static int
bcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc * sc,uint32_t voltage_id)505b9450e43SRui Paulo bcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc *sc,
506b9450e43SRui Paulo uint32_t voltage_id)
507b9450e43SRui Paulo {
5087413ae0eSAndrew Turner union msg_get_voltage_body msg;
509b9450e43SRui Paulo int value;
510b9450e43SRui Paulo int err;
511b9450e43SRui Paulo
512b9450e43SRui Paulo /*
513b9450e43SRui Paulo * Get voltage
514b9450e43SRui Paulo * Tag: 0x00030008
515b9450e43SRui Paulo * Request:
516b9450e43SRui Paulo * Length: 4
517b9450e43SRui Paulo * Value:
518b9450e43SRui Paulo * u32: voltage id
519b9450e43SRui Paulo * Response:
520b9450e43SRui Paulo * Length: 8
521b9450e43SRui Paulo * Value:
522b9450e43SRui Paulo * u32: voltage id
523b9450e43SRui Paulo * u32: value (offset from 1.2V in units of 0.025V)
524b9450e43SRui Paulo */
525b9450e43SRui Paulo
526b9450e43SRui Paulo /* setup single tag buffer */
527cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
5287413ae0eSAndrew Turner msg.req.voltage_id = voltage_id;
529b9450e43SRui Paulo
530b9450e43SRui Paulo /* call mailbox property */
5317413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
5327413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_GET_MIN_VOLTAGE, &msg, sizeof(msg));
533b9450e43SRui Paulo if (err) {
534b9450e43SRui Paulo device_printf(sc->dev, "can't get min voltage\n");
535b9450e43SRui Paulo return (MSG_ERROR);
536b9450e43SRui Paulo }
537b9450e43SRui Paulo
538b9450e43SRui Paulo /* result (offset from 1.2V) */
5397413ae0eSAndrew Turner value = (int)msg.resp.value;
540b9450e43SRui Paulo DPRINTF("value = %d\n", value);
541b9450e43SRui Paulo return (value);
542b9450e43SRui Paulo }
543b9450e43SRui Paulo
544b9450e43SRui Paulo static int
bcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc * sc,uint32_t voltage_id,int32_t value)545b9450e43SRui Paulo bcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc *sc,
546b9450e43SRui Paulo uint32_t voltage_id, int32_t value)
547b9450e43SRui Paulo {
5487413ae0eSAndrew Turner union msg_set_voltage_body msg;
549b9450e43SRui Paulo int err;
550b9450e43SRui Paulo
551b9450e43SRui Paulo /*
552b9450e43SRui Paulo * Set voltage
553b9450e43SRui Paulo * Tag: 0x00038003
554b9450e43SRui Paulo * Request:
555b9450e43SRui Paulo * Length: 4
556b9450e43SRui Paulo * Value:
557b9450e43SRui Paulo * u32: voltage id
558b9450e43SRui Paulo * u32: value (offset from 1.2V in units of 0.025V)
559b9450e43SRui Paulo * Response:
560b9450e43SRui Paulo * Length: 8
561b9450e43SRui Paulo * Value:
562b9450e43SRui Paulo * u32: voltage id
563b9450e43SRui Paulo * u32: value (offset from 1.2V in units of 0.025V)
564b9450e43SRui Paulo */
565b9450e43SRui Paulo
566b9450e43SRui Paulo /*
567b9450e43SRui Paulo * over_voltage:
568b9450e43SRui Paulo * 0 (1.2 V). Values above 6 are only allowed when force_turbo or
569b9450e43SRui Paulo * current_limit_override are specified (which set the warranty bit).
570b9450e43SRui Paulo */
571b9450e43SRui Paulo if (value > MAX_OVER_VOLTAGE || value < MIN_OVER_VOLTAGE) {
572b9450e43SRui Paulo /* currently not supported */
573b9450e43SRui Paulo device_printf(sc->dev, "not supported voltage: %d\n", value);
574b9450e43SRui Paulo return (MSG_ERROR);
575b9450e43SRui Paulo }
576b9450e43SRui Paulo
577b9450e43SRui Paulo /* setup single tag buffer */
578cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
5797413ae0eSAndrew Turner msg.req.voltage_id = voltage_id;
5807413ae0eSAndrew Turner msg.req.value = (uint32_t)value;
581b9450e43SRui Paulo
582b9450e43SRui Paulo /* call mailbox property */
5837413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
5847413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_SET_VOLTAGE, &msg, sizeof(msg));
585b9450e43SRui Paulo if (err) {
586b9450e43SRui Paulo device_printf(sc->dev, "can't set voltage\n");
587b9450e43SRui Paulo return (MSG_ERROR);
588b9450e43SRui Paulo }
589b9450e43SRui Paulo
590b9450e43SRui Paulo /* result (offset from 1.2V) */
5917413ae0eSAndrew Turner value = (int)msg.resp.value;
592b9450e43SRui Paulo DPRINTF("value = %d\n", value);
593b9450e43SRui Paulo return (value);
594b9450e43SRui Paulo }
595b9450e43SRui Paulo
596b9450e43SRui Paulo static int
bcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc * sc)597b9450e43SRui Paulo bcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc *sc)
598b9450e43SRui Paulo {
5997413ae0eSAndrew Turner union msg_get_temperature_body msg;
600b9450e43SRui Paulo int value;
601b9450e43SRui Paulo int err;
602b9450e43SRui Paulo
603b9450e43SRui Paulo /*
604b9450e43SRui Paulo * Get temperature
605b9450e43SRui Paulo * Tag: 0x00030006
606b9450e43SRui Paulo * Request:
607b9450e43SRui Paulo * Length: 4
608b9450e43SRui Paulo * Value:
609b9450e43SRui Paulo * u32: temperature id
610b9450e43SRui Paulo * Response:
611b9450e43SRui Paulo * Length: 8
612b9450e43SRui Paulo * Value:
613b9450e43SRui Paulo * u32: temperature id
614b9450e43SRui Paulo * u32: value
615b9450e43SRui Paulo */
616b9450e43SRui Paulo
617b9450e43SRui Paulo /* setup single tag buffer */
618cd3903c6SOleksandr Tymoshenko memset(&msg, 0, sizeof(msg));
6197413ae0eSAndrew Turner msg.req.temperature_id = 0;
620b9450e43SRui Paulo
621b9450e43SRui Paulo /* call mailbox property */
6227413ae0eSAndrew Turner err = bcm2835_firmware_property(sc->firmware,
6237413ae0eSAndrew Turner BCM2835_FIRMWARE_TAG_GET_TEMPERATURE, &msg, sizeof(msg));
624b9450e43SRui Paulo if (err) {
625b9450e43SRui Paulo device_printf(sc->dev, "can't get temperature\n");
626b9450e43SRui Paulo return (MSG_ERROR);
627b9450e43SRui Paulo }
628b9450e43SRui Paulo
629b9450e43SRui Paulo /* result (temperature of degree C) */
6307413ae0eSAndrew Turner value = (int)msg.resp.value;
631b9450e43SRui Paulo DPRINTF("value = %d\n", value);
632b9450e43SRui Paulo return (value);
633b9450e43SRui Paulo }
634b9450e43SRui Paulo
635b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_arm_freq(SYSCTL_HANDLER_ARGS)636b9450e43SRui Paulo sysctl_bcm2835_cpufreq_arm_freq(SYSCTL_HANDLER_ARGS)
637b9450e43SRui Paulo {
638b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
639b9450e43SRui Paulo int val;
640b9450e43SRui Paulo int err;
641b9450e43SRui Paulo
642b9450e43SRui Paulo /* get realtime value */
643b9450e43SRui Paulo VC_LOCK(sc);
6447413ae0eSAndrew Turner val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_FIRMWARE_CLOCK_ID_ARM);
645b9450e43SRui Paulo VC_UNLOCK(sc);
646b9450e43SRui Paulo if (val == MSG_ERROR)
647b9450e43SRui Paulo return (EIO);
648b9450e43SRui Paulo
649b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
650b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
651b9450e43SRui Paulo return (err);
652b9450e43SRui Paulo
653b9450e43SRui Paulo /* write request */
654b9450e43SRui Paulo VC_LOCK(sc);
6557413ae0eSAndrew Turner err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_FIRMWARE_CLOCK_ID_ARM,
656b9450e43SRui Paulo val);
657b9450e43SRui Paulo VC_UNLOCK(sc);
658b9450e43SRui Paulo if (err == MSG_ERROR) {
659b9450e43SRui Paulo device_printf(sc->dev, "set clock arm_freq error\n");
660b9450e43SRui Paulo return (EIO);
661b9450e43SRui Paulo }
662b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
663b9450e43SRui Paulo
664b9450e43SRui Paulo return (0);
665b9450e43SRui Paulo }
666b9450e43SRui Paulo
667b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS)668b9450e43SRui Paulo sysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS)
669b9450e43SRui Paulo {
670b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
671b9450e43SRui Paulo int val;
672b9450e43SRui Paulo int err;
673b9450e43SRui Paulo
674b9450e43SRui Paulo /* get realtime value */
675b9450e43SRui Paulo VC_LOCK(sc);
6767413ae0eSAndrew Turner val = bcm2835_cpufreq_get_clock_rate(sc,
6777413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE);
678b9450e43SRui Paulo VC_UNLOCK(sc);
679b9450e43SRui Paulo if (val == MSG_ERROR)
680b9450e43SRui Paulo return (EIO);
681b9450e43SRui Paulo
682b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
683b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
684b9450e43SRui Paulo return (err);
685b9450e43SRui Paulo
686b9450e43SRui Paulo /* write request */
687b9450e43SRui Paulo VC_LOCK(sc);
6887413ae0eSAndrew Turner err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_FIRMWARE_CLOCK_ID_CORE,
689b9450e43SRui Paulo val);
690b9450e43SRui Paulo if (err == MSG_ERROR) {
691b9450e43SRui Paulo VC_UNLOCK(sc);
692b9450e43SRui Paulo device_printf(sc->dev, "set clock core_freq error\n");
693b9450e43SRui Paulo return (EIO);
694b9450e43SRui Paulo }
695b9450e43SRui Paulo VC_UNLOCK(sc);
696b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
697b9450e43SRui Paulo
698b9450e43SRui Paulo return (0);
699b9450e43SRui Paulo }
700b9450e43SRui Paulo
701b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS)702b9450e43SRui Paulo sysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS)
703b9450e43SRui Paulo {
704b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
705b9450e43SRui Paulo int val;
706b9450e43SRui Paulo int err;
707b9450e43SRui Paulo
708b9450e43SRui Paulo /* get realtime value */
709b9450e43SRui Paulo VC_LOCK(sc);
7107413ae0eSAndrew Turner val = bcm2835_cpufreq_get_clock_rate(sc,
7117413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM);
712b9450e43SRui Paulo VC_UNLOCK(sc);
713b9450e43SRui Paulo if (val == MSG_ERROR)
714b9450e43SRui Paulo return (EIO);
715b9450e43SRui Paulo
716b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
717b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
718b9450e43SRui Paulo return (err);
719b9450e43SRui Paulo
720b9450e43SRui Paulo /* write request */
721b9450e43SRui Paulo VC_LOCK(sc);
7227413ae0eSAndrew Turner err = bcm2835_cpufreq_set_clock_rate(sc,
7237413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM, val);
724b9450e43SRui Paulo VC_UNLOCK(sc);
725b9450e43SRui Paulo if (err == MSG_ERROR) {
726b9450e43SRui Paulo device_printf(sc->dev, "set clock sdram_freq error\n");
727b9450e43SRui Paulo return (EIO);
728b9450e43SRui Paulo }
729b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
730b9450e43SRui Paulo
731b9450e43SRui Paulo return (0);
732b9450e43SRui Paulo }
733b9450e43SRui Paulo
734b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS)735b9450e43SRui Paulo sysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS)
736b9450e43SRui Paulo {
737b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
738b9450e43SRui Paulo int val;
739b9450e43SRui Paulo int err;
740b9450e43SRui Paulo
741b9450e43SRui Paulo /* get realtime value */
742b9450e43SRui Paulo VC_LOCK(sc);
743b9450e43SRui Paulo val = bcm2835_cpufreq_get_turbo(sc);
744b9450e43SRui Paulo VC_UNLOCK(sc);
745b9450e43SRui Paulo if (val == MSG_ERROR)
746b9450e43SRui Paulo return (EIO);
747b9450e43SRui Paulo
748b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
749b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
750b9450e43SRui Paulo return (err);
751b9450e43SRui Paulo
752b9450e43SRui Paulo /* write request */
753b9450e43SRui Paulo if (val > 0)
7547413ae0eSAndrew Turner sc->turbo_mode = BCM2835_FIRMWARE_TURBO_ON;
755b9450e43SRui Paulo else
7567413ae0eSAndrew Turner sc->turbo_mode = BCM2835_FIRMWARE_TURBO_OFF;
757b9450e43SRui Paulo
758b9450e43SRui Paulo VC_LOCK(sc);
759b9450e43SRui Paulo err = bcm2835_cpufreq_set_turbo(sc, sc->turbo_mode);
760b9450e43SRui Paulo VC_UNLOCK(sc);
761b9450e43SRui Paulo if (err == MSG_ERROR) {
762b9450e43SRui Paulo device_printf(sc->dev, "set turbo error\n");
763b9450e43SRui Paulo return (EIO);
764b9450e43SRui Paulo }
765b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
766b9450e43SRui Paulo
767b9450e43SRui Paulo return (0);
768b9450e43SRui Paulo }
769b9450e43SRui Paulo
770b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS)771b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS)
772b9450e43SRui Paulo {
773b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
774b9450e43SRui Paulo int val;
775b9450e43SRui Paulo int err;
776b9450e43SRui Paulo
777b9450e43SRui Paulo /* get realtime value */
778b9450e43SRui Paulo VC_LOCK(sc);
7797413ae0eSAndrew Turner val = bcm2835_cpufreq_get_voltage(sc, BCM2835_FIRMWARE_VOLTAGE_ID_CORE);
780b9450e43SRui Paulo VC_UNLOCK(sc);
781b9450e43SRui Paulo if (val == MSG_ERROR)
782b9450e43SRui Paulo return (EIO);
783b9450e43SRui Paulo
784b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
785b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
786b9450e43SRui Paulo return (err);
787b9450e43SRui Paulo
788b9450e43SRui Paulo /* write request */
789b9450e43SRui Paulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
790b9450e43SRui Paulo return (EINVAL);
791b9450e43SRui Paulo sc->voltage_core = val;
792b9450e43SRui Paulo
793b9450e43SRui Paulo VC_LOCK(sc);
7947413ae0eSAndrew Turner err = bcm2835_cpufreq_set_voltage(sc, BCM2835_FIRMWARE_VOLTAGE_ID_CORE,
795b9450e43SRui Paulo sc->voltage_core);
796b9450e43SRui Paulo VC_UNLOCK(sc);
797b9450e43SRui Paulo if (err == MSG_ERROR) {
798b9450e43SRui Paulo device_printf(sc->dev, "set voltage core error\n");
799b9450e43SRui Paulo return (EIO);
800b9450e43SRui Paulo }
801b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
802b9450e43SRui Paulo
803b9450e43SRui Paulo return (0);
804b9450e43SRui Paulo }
805b9450e43SRui Paulo
806b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS)807b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS)
808b9450e43SRui Paulo {
809b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
810b9450e43SRui Paulo int val;
811b9450e43SRui Paulo int err;
812b9450e43SRui Paulo
813b9450e43SRui Paulo /* get realtime value */
814b9450e43SRui Paulo VC_LOCK(sc);
8157413ae0eSAndrew Turner val = bcm2835_cpufreq_get_voltage(sc,
8167413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C);
817b9450e43SRui Paulo VC_UNLOCK(sc);
818b9450e43SRui Paulo if (val == MSG_ERROR)
819b9450e43SRui Paulo return (EIO);
820b9450e43SRui Paulo
821b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
822b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
823b9450e43SRui Paulo return (err);
824b9450e43SRui Paulo
825b9450e43SRui Paulo /* write request */
826b9450e43SRui Paulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
827b9450e43SRui Paulo return (EINVAL);
828b9450e43SRui Paulo sc->voltage_sdram_c = val;
829b9450e43SRui Paulo
830b9450e43SRui Paulo VC_LOCK(sc);
8317413ae0eSAndrew Turner err = bcm2835_cpufreq_set_voltage(sc,
8327413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C,
833b9450e43SRui Paulo sc->voltage_sdram_c);
834b9450e43SRui Paulo VC_UNLOCK(sc);
835b9450e43SRui Paulo if (err == MSG_ERROR) {
836b9450e43SRui Paulo device_printf(sc->dev, "set voltage sdram_c error\n");
837b9450e43SRui Paulo return (EIO);
838b9450e43SRui Paulo }
839b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
840b9450e43SRui Paulo
841b9450e43SRui Paulo return (0);
842b9450e43SRui Paulo }
843b9450e43SRui Paulo
844b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS)845b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS)
846b9450e43SRui Paulo {
847b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
848b9450e43SRui Paulo int val;
849b9450e43SRui Paulo int err;
850b9450e43SRui Paulo
851b9450e43SRui Paulo /* get realtime value */
852b9450e43SRui Paulo VC_LOCK(sc);
8537413ae0eSAndrew Turner val = bcm2835_cpufreq_get_voltage(sc,
8547413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I);
855b9450e43SRui Paulo VC_UNLOCK(sc);
856b9450e43SRui Paulo if (val == MSG_ERROR)
857b9450e43SRui Paulo return (EIO);
858b9450e43SRui Paulo
859b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
860b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
861b9450e43SRui Paulo return (err);
862b9450e43SRui Paulo
863b9450e43SRui Paulo /* write request */
864b9450e43SRui Paulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
865b9450e43SRui Paulo return (EINVAL);
866b9450e43SRui Paulo sc->voltage_sdram_i = val;
867b9450e43SRui Paulo
868b9450e43SRui Paulo VC_LOCK(sc);
8697413ae0eSAndrew Turner err = bcm2835_cpufreq_set_voltage(sc,
8707413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I, sc->voltage_sdram_i);
871b9450e43SRui Paulo VC_UNLOCK(sc);
872b9450e43SRui Paulo if (err == MSG_ERROR) {
873b9450e43SRui Paulo device_printf(sc->dev, "set voltage sdram_i error\n");
874b9450e43SRui Paulo return (EIO);
875b9450e43SRui Paulo }
876b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
877b9450e43SRui Paulo
878b9450e43SRui Paulo return (0);
879b9450e43SRui Paulo }
880b9450e43SRui Paulo
881b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS)882b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS)
883b9450e43SRui Paulo {
884b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
885b9450e43SRui Paulo int val;
886b9450e43SRui Paulo int err;
887b9450e43SRui Paulo
888b9450e43SRui Paulo /* get realtime value */
889b9450e43SRui Paulo VC_LOCK(sc);
8907413ae0eSAndrew Turner val = bcm2835_cpufreq_get_voltage(sc,
8917413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P);
892b9450e43SRui Paulo VC_UNLOCK(sc);
893b9450e43SRui Paulo if (val == MSG_ERROR)
894b9450e43SRui Paulo return (EIO);
895b9450e43SRui Paulo
896b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
897b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
898b9450e43SRui Paulo return (err);
899b9450e43SRui Paulo
900b9450e43SRui Paulo /* write request */
901b9450e43SRui Paulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
902b9450e43SRui Paulo return (EINVAL);
903b9450e43SRui Paulo sc->voltage_sdram_p = val;
904b9450e43SRui Paulo
905b9450e43SRui Paulo VC_LOCK(sc);
9067413ae0eSAndrew Turner err = bcm2835_cpufreq_set_voltage(sc,
9077413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P, sc->voltage_sdram_p);
908b9450e43SRui Paulo VC_UNLOCK(sc);
909b9450e43SRui Paulo if (err == MSG_ERROR) {
910b9450e43SRui Paulo device_printf(sc->dev, "set voltage sdram_p error\n");
911b9450e43SRui Paulo return (EIO);
912b9450e43SRui Paulo }
913b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
914b9450e43SRui Paulo
915b9450e43SRui Paulo return (0);
916b9450e43SRui Paulo }
917b9450e43SRui Paulo
918b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS)919b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS)
920b9450e43SRui Paulo {
921b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
922b9450e43SRui Paulo int val;
923b9450e43SRui Paulo int err;
924b9450e43SRui Paulo
925b9450e43SRui Paulo /* multiple write only */
926b9450e43SRui Paulo if (!req->newptr)
927b9450e43SRui Paulo return (EINVAL);
928b9450e43SRui Paulo val = 0;
929b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
930b9450e43SRui Paulo if (err)
931b9450e43SRui Paulo return (err);
932b9450e43SRui Paulo
933b9450e43SRui Paulo /* write request */
934b9450e43SRui Paulo if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE)
935b9450e43SRui Paulo return (EINVAL);
936b9450e43SRui Paulo sc->voltage_sdram = val;
937b9450e43SRui Paulo
938b9450e43SRui Paulo VC_LOCK(sc);
9397413ae0eSAndrew Turner err = bcm2835_cpufreq_set_voltage(sc,
9407413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C, val);
941b9450e43SRui Paulo if (err == MSG_ERROR) {
942b9450e43SRui Paulo VC_UNLOCK(sc);
943b9450e43SRui Paulo device_printf(sc->dev, "set voltage sdram_c error\n");
944b9450e43SRui Paulo return (EIO);
945b9450e43SRui Paulo }
9467413ae0eSAndrew Turner err = bcm2835_cpufreq_set_voltage(sc,
9477413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I, val);
948b9450e43SRui Paulo if (err == MSG_ERROR) {
949b9450e43SRui Paulo VC_UNLOCK(sc);
950b9450e43SRui Paulo device_printf(sc->dev, "set voltage sdram_i error\n");
951b9450e43SRui Paulo return (EIO);
952b9450e43SRui Paulo }
9537413ae0eSAndrew Turner err = bcm2835_cpufreq_set_voltage(sc,
9547413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P, val);
955b9450e43SRui Paulo if (err == MSG_ERROR) {
956b9450e43SRui Paulo VC_UNLOCK(sc);
957b9450e43SRui Paulo device_printf(sc->dev, "set voltage sdram_p error\n");
958b9450e43SRui Paulo return (EIO);
959b9450e43SRui Paulo }
960b9450e43SRui Paulo VC_UNLOCK(sc);
961b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
962b9450e43SRui Paulo
963b9450e43SRui Paulo return (0);
964b9450e43SRui Paulo }
965b9450e43SRui Paulo
966b9450e43SRui Paulo static int
sysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS)967b9450e43SRui Paulo sysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS)
968b9450e43SRui Paulo {
969b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
970b9450e43SRui Paulo int val;
971b9450e43SRui Paulo int err;
972b9450e43SRui Paulo
973b9450e43SRui Paulo /* get realtime value */
974b9450e43SRui Paulo VC_LOCK(sc);
975b9450e43SRui Paulo val = bcm2835_cpufreq_get_temperature(sc);
976b9450e43SRui Paulo VC_UNLOCK(sc);
977b9450e43SRui Paulo if (val == MSG_ERROR)
978b9450e43SRui Paulo return (EIO);
979b9450e43SRui Paulo
980b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
981b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
982b9450e43SRui Paulo return (err);
983b9450e43SRui Paulo
984b9450e43SRui Paulo /* write request */
985b9450e43SRui Paulo return (EINVAL);
986b9450e43SRui Paulo }
987b9450e43SRui Paulo
988b9450e43SRui Paulo static int
sysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS)989b9450e43SRui Paulo sysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS)
990b9450e43SRui Paulo {
991b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg1;
992b9450e43SRui Paulo int val;
993b9450e43SRui Paulo int err;
994b9450e43SRui Paulo
995b9450e43SRui Paulo /* get realtime value */
996b9450e43SRui Paulo VC_LOCK(sc);
997b9450e43SRui Paulo val = bcm2835_cpufreq_get_temperature(sc);
998b9450e43SRui Paulo VC_UNLOCK(sc);
999b9450e43SRui Paulo if (val == MSG_ERROR)
1000b9450e43SRui Paulo return (EIO);
1001b9450e43SRui Paulo
1002b9450e43SRui Paulo /* 1/1000 celsius (raw) to 1/10 kelvin */
1003e4b6eaf7SLuiz Otavio O Souza val = val / 100 + TZ_ZEROC;
1004b9450e43SRui Paulo
1005b9450e43SRui Paulo err = sysctl_handle_int(oidp, &val, 0, req);
1006b9450e43SRui Paulo if (err || !req->newptr) /* error || read request */
1007b9450e43SRui Paulo return (err);
1008b9450e43SRui Paulo
1009b9450e43SRui Paulo /* write request */
1010b9450e43SRui Paulo return (EINVAL);
1011b9450e43SRui Paulo }
1012b9450e43SRui Paulo
1013b9450e43SRui Paulo static void
bcm2835_cpufreq_init(void * arg)1014b9450e43SRui Paulo bcm2835_cpufreq_init(void *arg)
1015b9450e43SRui Paulo {
1016b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc = arg;
1017b9450e43SRui Paulo struct sysctl_ctx_list *ctx;
1018b9450e43SRui Paulo device_t cpu;
1019b9450e43SRui Paulo int arm_freq, core_freq, sdram_freq;
1020b9450e43SRui Paulo int arm_max_freq, arm_min_freq, core_max_freq, core_min_freq;
1021b9450e43SRui Paulo int sdram_max_freq, sdram_min_freq;
1022b9450e43SRui Paulo int voltage_core, voltage_sdram_c, voltage_sdram_i, voltage_sdram_p;
1023b9450e43SRui Paulo int max_voltage_core, min_voltage_core;
1024b9450e43SRui Paulo int max_voltage_sdram_c, min_voltage_sdram_c;
1025b9450e43SRui Paulo int max_voltage_sdram_i, min_voltage_sdram_i;
1026b9450e43SRui Paulo int max_voltage_sdram_p, min_voltage_sdram_p;
1027b9450e43SRui Paulo int turbo, temperature;
1028b9450e43SRui Paulo
1029b9450e43SRui Paulo VC_LOCK(sc);
1030b9450e43SRui Paulo
1031b9450e43SRui Paulo /* current clock */
1032b9450e43SRui Paulo arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
10337413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM);
1034b9450e43SRui Paulo core_freq = bcm2835_cpufreq_get_clock_rate(sc,
10357413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE);
1036b9450e43SRui Paulo sdram_freq = bcm2835_cpufreq_get_clock_rate(sc,
10377413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM);
1038b9450e43SRui Paulo
1039b9450e43SRui Paulo /* max/min clock */
1040b9450e43SRui Paulo arm_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
10417413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM);
1042b9450e43SRui Paulo arm_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
10437413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM);
1044b9450e43SRui Paulo core_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
10457413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE);
1046b9450e43SRui Paulo core_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
10477413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE);
1048b9450e43SRui Paulo sdram_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc,
10497413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM);
1050b9450e43SRui Paulo sdram_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc,
10517413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM);
1052b9450e43SRui Paulo
1053b9450e43SRui Paulo /* turbo mode */
1054b9450e43SRui Paulo turbo = bcm2835_cpufreq_get_turbo(sc);
1055b9450e43SRui Paulo if (turbo > 0)
10567413ae0eSAndrew Turner sc->turbo_mode = BCM2835_FIRMWARE_TURBO_ON;
1057b9450e43SRui Paulo else
10587413ae0eSAndrew Turner sc->turbo_mode = BCM2835_FIRMWARE_TURBO_OFF;
1059b9450e43SRui Paulo
1060b9450e43SRui Paulo /* voltage */
1061b9450e43SRui Paulo voltage_core = bcm2835_cpufreq_get_voltage(sc,
10627413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_CORE);
1063b9450e43SRui Paulo voltage_sdram_c = bcm2835_cpufreq_get_voltage(sc,
10647413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C);
1065b9450e43SRui Paulo voltage_sdram_i = bcm2835_cpufreq_get_voltage(sc,
10667413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I);
1067b9450e43SRui Paulo voltage_sdram_p = bcm2835_cpufreq_get_voltage(sc,
10687413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P);
1069b9450e43SRui Paulo
1070b9450e43SRui Paulo /* current values (offset from 1.2V) */
1071b9450e43SRui Paulo sc->voltage_core = voltage_core;
1072b9450e43SRui Paulo sc->voltage_sdram = voltage_sdram_c;
1073b9450e43SRui Paulo sc->voltage_sdram_c = voltage_sdram_c;
1074b9450e43SRui Paulo sc->voltage_sdram_i = voltage_sdram_i;
1075b9450e43SRui Paulo sc->voltage_sdram_p = voltage_sdram_p;
1076b9450e43SRui Paulo
1077b9450e43SRui Paulo /* max/min voltage */
1078b9450e43SRui Paulo max_voltage_core = bcm2835_cpufreq_get_max_voltage(sc,
10797413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_CORE);
1080b9450e43SRui Paulo min_voltage_core = bcm2835_cpufreq_get_min_voltage(sc,
10817413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_CORE);
1082b9450e43SRui Paulo max_voltage_sdram_c = bcm2835_cpufreq_get_max_voltage(sc,
10837413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C);
1084b9450e43SRui Paulo max_voltage_sdram_i = bcm2835_cpufreq_get_max_voltage(sc,
10857413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I);
1086b9450e43SRui Paulo max_voltage_sdram_p = bcm2835_cpufreq_get_max_voltage(sc,
10877413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P);
1088b9450e43SRui Paulo min_voltage_sdram_c = bcm2835_cpufreq_get_min_voltage(sc,
10897413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_C);
1090b9450e43SRui Paulo min_voltage_sdram_i = bcm2835_cpufreq_get_min_voltage(sc,
10917413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_I);
1092b9450e43SRui Paulo min_voltage_sdram_p = bcm2835_cpufreq_get_min_voltage(sc,
10937413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_SDRAM_P);
1094b9450e43SRui Paulo
1095b9450e43SRui Paulo /* temperature */
1096b9450e43SRui Paulo temperature = bcm2835_cpufreq_get_temperature(sc);
1097b9450e43SRui Paulo
1098b9450e43SRui Paulo /* show result */
1099b9450e43SRui Paulo if (cpufreq_verbose || bootverbose) {
1100b9450e43SRui Paulo device_printf(sc->dev, "Boot settings:\n");
1101b9450e43SRui Paulo device_printf(sc->dev,
1102b9450e43SRui Paulo "current ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n",
1103b9450e43SRui Paulo HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq),
11047413ae0eSAndrew Turner (sc->turbo_mode == BCM2835_FIRMWARE_TURBO_ON) ? "ON":"OFF");
1105b9450e43SRui Paulo
1106b9450e43SRui Paulo device_printf(sc->dev,
1107b9450e43SRui Paulo "max/min ARM %d/%dMHz, Core %d/%dMHz, SDRAM %d/%dMHz\n",
1108b9450e43SRui Paulo HZ2MHZ(arm_max_freq), HZ2MHZ(arm_min_freq),
1109b9450e43SRui Paulo HZ2MHZ(core_max_freq), HZ2MHZ(core_min_freq),
1110b9450e43SRui Paulo HZ2MHZ(sdram_max_freq), HZ2MHZ(sdram_min_freq));
1111b9450e43SRui Paulo
1112b9450e43SRui Paulo device_printf(sc->dev,
1113b9450e43SRui Paulo "current Core %dmV, SDRAM_C %dmV, SDRAM_I %dmV, "
1114b9450e43SRui Paulo "SDRAM_P %dmV\n",
1115b9450e43SRui Paulo OFFSET2MVOLT(voltage_core), OFFSET2MVOLT(voltage_sdram_c),
1116b9450e43SRui Paulo OFFSET2MVOLT(voltage_sdram_i),
1117b9450e43SRui Paulo OFFSET2MVOLT(voltage_sdram_p));
1118b9450e43SRui Paulo
1119b9450e43SRui Paulo device_printf(sc->dev,
1120b9450e43SRui Paulo "max/min Core %d/%dmV, SDRAM_C %d/%dmV, SDRAM_I %d/%dmV, "
1121b9450e43SRui Paulo "SDRAM_P %d/%dmV\n",
1122b9450e43SRui Paulo OFFSET2MVOLT(max_voltage_core),
1123b9450e43SRui Paulo OFFSET2MVOLT(min_voltage_core),
1124b9450e43SRui Paulo OFFSET2MVOLT(max_voltage_sdram_c),
1125b9450e43SRui Paulo OFFSET2MVOLT(min_voltage_sdram_c),
1126b9450e43SRui Paulo OFFSET2MVOLT(max_voltage_sdram_i),
1127b9450e43SRui Paulo OFFSET2MVOLT(min_voltage_sdram_i),
1128b9450e43SRui Paulo OFFSET2MVOLT(max_voltage_sdram_p),
1129b9450e43SRui Paulo OFFSET2MVOLT(min_voltage_sdram_p));
1130b9450e43SRui Paulo
1131b9450e43SRui Paulo device_printf(sc->dev,
1132b9450e43SRui Paulo "Temperature %d.%dC\n", (temperature / 1000),
1133b9450e43SRui Paulo (temperature % 1000) / 100);
1134b9450e43SRui Paulo } else { /* !cpufreq_verbose && !bootverbose */
1135b9450e43SRui Paulo device_printf(sc->dev,
1136b9450e43SRui Paulo "ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n",
1137b9450e43SRui Paulo HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq),
11387413ae0eSAndrew Turner (sc->turbo_mode == BCM2835_FIRMWARE_TURBO_ON) ? "ON":"OFF");
1139b9450e43SRui Paulo }
1140b9450e43SRui Paulo
1141b9450e43SRui Paulo /* keep in softc (MHz/mV) */
1142b9450e43SRui Paulo sc->arm_max_freq = HZ2MHZ(arm_max_freq);
1143b9450e43SRui Paulo sc->arm_min_freq = HZ2MHZ(arm_min_freq);
1144b9450e43SRui Paulo sc->core_max_freq = HZ2MHZ(core_max_freq);
1145b9450e43SRui Paulo sc->core_min_freq = HZ2MHZ(core_min_freq);
1146b9450e43SRui Paulo sc->sdram_max_freq = HZ2MHZ(sdram_max_freq);
1147b9450e43SRui Paulo sc->sdram_min_freq = HZ2MHZ(sdram_min_freq);
1148b9450e43SRui Paulo sc->max_voltage_core = OFFSET2MVOLT(max_voltage_core);
1149b9450e43SRui Paulo sc->min_voltage_core = OFFSET2MVOLT(min_voltage_core);
1150b9450e43SRui Paulo
1151b9450e43SRui Paulo /* if turbo is on, set to max values */
11527413ae0eSAndrew Turner if (sc->turbo_mode == BCM2835_FIRMWARE_TURBO_ON) {
11537413ae0eSAndrew Turner bcm2835_cpufreq_set_clock_rate(sc,
11547413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM, arm_max_freq);
1155b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1156b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(sc,
11577413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE, core_max_freq);
11587413ae0eSAndrew Turner DELAY(TRANSITION_LATENCY);
11597413ae0eSAndrew Turner bcm2835_cpufreq_set_clock_rate(sc,
11607413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM, sdram_max_freq);
1161b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1162b9450e43SRui Paulo } else {
11637413ae0eSAndrew Turner bcm2835_cpufreq_set_clock_rate(sc,
11647413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM, arm_min_freq);
1165b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1166b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(sc,
11677413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE, core_min_freq);
11687413ae0eSAndrew Turner DELAY(TRANSITION_LATENCY);
11697413ae0eSAndrew Turner bcm2835_cpufreq_set_clock_rate(sc,
11707413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM, sdram_min_freq);
1171b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1172b9450e43SRui Paulo }
1173b9450e43SRui Paulo
1174b9450e43SRui Paulo VC_UNLOCK(sc);
1175b9450e43SRui Paulo
1176b9450e43SRui Paulo /* add human readable temperature to dev.cpu node */
1177b9450e43SRui Paulo cpu = device_get_parent(sc->dev);
1178b9450e43SRui Paulo if (cpu != NULL) {
1179b9450e43SRui Paulo ctx = device_get_sysctl_ctx(cpu);
1180b9450e43SRui Paulo SYSCTL_ADD_PROC(ctx,
1181b9450e43SRui Paulo SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO,
11827029da5cSPawel Biernacki "temperature",
11837029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
1184b9450e43SRui Paulo sysctl_bcm2835_devcpu_temperature, "IK",
1185b9450e43SRui Paulo "Current SoC temperature");
1186b9450e43SRui Paulo }
1187b9450e43SRui Paulo
1188b9450e43SRui Paulo /* release this hook (continue boot) */
1189b9450e43SRui Paulo config_intrhook_disestablish(&sc->init_hook);
1190b9450e43SRui Paulo }
1191b9450e43SRui Paulo
1192b9450e43SRui Paulo static void
bcm2835_cpufreq_identify(driver_t * driver,device_t parent)1193b9450e43SRui Paulo bcm2835_cpufreq_identify(driver_t *driver, device_t parent)
1194b9450e43SRui Paulo {
1195e374c335SEmmanuel Vadot const struct ofw_compat_data *compat;
1196e374c335SEmmanuel Vadot phandle_t root;
1197e374c335SEmmanuel Vadot
1198e374c335SEmmanuel Vadot root = OF_finddevice("/");
1199e374c335SEmmanuel Vadot for (compat = compat_data; compat->ocd_str != NULL; compat++)
120087acb7f8SAndrew Turner if (ofw_bus_node_is_compatible(root, compat->ocd_str))
1201e374c335SEmmanuel Vadot break;
1202e374c335SEmmanuel Vadot
1203e374c335SEmmanuel Vadot if (compat->ocd_data == 0)
1204e374c335SEmmanuel Vadot return;
1205b9450e43SRui Paulo
1206b9450e43SRui Paulo DPRINTF("driver=%p, parent=%p\n", driver, parent);
1207b9450e43SRui Paulo if (device_find_child(parent, "bcm2835_cpufreq", -1) != NULL)
1208b9450e43SRui Paulo return;
1209b9450e43SRui Paulo if (BUS_ADD_CHILD(parent, 0, "bcm2835_cpufreq", -1) == NULL)
1210b9450e43SRui Paulo device_printf(parent, "add child failed\n");
1211b9450e43SRui Paulo }
1212b9450e43SRui Paulo
1213b9450e43SRui Paulo static int
bcm2835_cpufreq_probe(device_t dev)1214b9450e43SRui Paulo bcm2835_cpufreq_probe(device_t dev)
1215b9450e43SRui Paulo {
1216b9450e43SRui Paulo
1217962940ceSLuiz Otavio O Souza if (device_get_unit(dev) != 0)
1218962940ceSLuiz Otavio O Souza return (ENXIO);
1219b9450e43SRui Paulo device_set_desc(dev, "CPU Frequency Control");
1220962940ceSLuiz Otavio O Souza
1221b9450e43SRui Paulo return (0);
1222b9450e43SRui Paulo }
1223b9450e43SRui Paulo
1224b9450e43SRui Paulo static int
bcm2835_cpufreq_attach(device_t dev)1225b9450e43SRui Paulo bcm2835_cpufreq_attach(device_t dev)
1226b9450e43SRui Paulo {
1227b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc;
1228b9450e43SRui Paulo struct sysctl_oid *oid;
1229b9450e43SRui Paulo
1230b9450e43SRui Paulo /* set self dev */
1231b9450e43SRui Paulo sc = device_get_softc(dev);
1232b9450e43SRui Paulo sc->dev = dev;
12337413ae0eSAndrew Turner sc->firmware = devclass_get_device(
12347413ae0eSAndrew Turner devclass_find("bcm2835_firmware"), 0);
12357413ae0eSAndrew Turner if (sc->firmware == NULL) {
12367413ae0eSAndrew Turner device_printf(dev, "Unable to find firmware device\n");
12377413ae0eSAndrew Turner return (ENXIO);
12387413ae0eSAndrew Turner }
1239b9450e43SRui Paulo
1240b9450e43SRui Paulo /* initial values */
1241b9450e43SRui Paulo sc->arm_max_freq = -1;
1242b9450e43SRui Paulo sc->arm_min_freq = -1;
1243b9450e43SRui Paulo sc->core_max_freq = -1;
1244b9450e43SRui Paulo sc->core_min_freq = -1;
1245b9450e43SRui Paulo sc->sdram_max_freq = -1;
1246b9450e43SRui Paulo sc->sdram_min_freq = -1;
1247b9450e43SRui Paulo sc->max_voltage_core = 0;
1248b9450e43SRui Paulo sc->min_voltage_core = 0;
1249b9450e43SRui Paulo
1250b9450e43SRui Paulo /* setup sysctl at first device */
1251b9450e43SRui Paulo if (device_get_unit(dev) == 0) {
1252b9450e43SRui Paulo sysctl_ctx_init(&bcm2835_sysctl_ctx);
1253b9450e43SRui Paulo /* create node for hw.cpufreq */
1254b9450e43SRui Paulo oid = SYSCTL_ADD_NODE(&bcm2835_sysctl_ctx,
1255b9450e43SRui Paulo SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "cpufreq",
12567029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
1257b9450e43SRui Paulo
1258b9450e43SRui Paulo /* Frequency (Hz) */
1259b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
12607029da5cSPawel Biernacki OID_AUTO, "arm_freq",
12617029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
1262b9450e43SRui Paulo sysctl_bcm2835_cpufreq_arm_freq, "IU",
1263b9450e43SRui Paulo "ARM frequency (Hz)");
1264b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
12657029da5cSPawel Biernacki OID_AUTO, "core_freq",
12667029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
1267b9450e43SRui Paulo sysctl_bcm2835_cpufreq_core_freq, "IU",
1268b9450e43SRui Paulo "Core frequency (Hz)");
1269b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
12707029da5cSPawel Biernacki OID_AUTO, "sdram_freq",
12717029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
1272b9450e43SRui Paulo sysctl_bcm2835_cpufreq_sdram_freq, "IU",
1273b9450e43SRui Paulo "SDRAM frequency (Hz)");
1274b9450e43SRui Paulo
1275b9450e43SRui Paulo /* Turbo state */
1276b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
12777029da5cSPawel Biernacki OID_AUTO, "turbo",
12787029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
1279b9450e43SRui Paulo sysctl_bcm2835_cpufreq_turbo, "IU",
1280b9450e43SRui Paulo "Disables dynamic clocking");
1281b9450e43SRui Paulo
1282b9450e43SRui Paulo /* Voltage (offset from 1.2V in units of 0.025V) */
1283b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
12847029da5cSPawel Biernacki OID_AUTO, "voltage_core",
12857029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
1286b9450e43SRui Paulo sysctl_bcm2835_cpufreq_voltage_core, "I",
1287b9450e43SRui Paulo "ARM/GPU core voltage"
1288b9450e43SRui Paulo "(offset from 1.2V in units of 0.025V)");
1289b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
12907029da5cSPawel Biernacki OID_AUTO, "voltage_sdram",
12917029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT, sc,
1292b9450e43SRui Paulo 0, sysctl_bcm2835_cpufreq_voltage_sdram, "I",
1293b9450e43SRui Paulo "SDRAM voltage (offset from 1.2V in units of 0.025V)");
1294b9450e43SRui Paulo
1295b9450e43SRui Paulo /* Voltage individual SDRAM */
1296b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
12977029da5cSPawel Biernacki OID_AUTO, "voltage_sdram_c",
12987029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc,
1299b9450e43SRui Paulo 0, sysctl_bcm2835_cpufreq_voltage_sdram_c, "I",
1300b9450e43SRui Paulo "SDRAM controller voltage"
1301b9450e43SRui Paulo "(offset from 1.2V in units of 0.025V)");
1302b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
13037029da5cSPawel Biernacki OID_AUTO, "voltage_sdram_i",
13047029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc,
1305b9450e43SRui Paulo 0, sysctl_bcm2835_cpufreq_voltage_sdram_i, "I",
1306b9450e43SRui Paulo "SDRAM I/O voltage (offset from 1.2V in units of 0.025V)");
1307b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
13087029da5cSPawel Biernacki OID_AUTO, "voltage_sdram_p",
13097029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc,
1310b9450e43SRui Paulo 0, sysctl_bcm2835_cpufreq_voltage_sdram_p, "I",
1311b9450e43SRui Paulo "SDRAM phy voltage (offset from 1.2V in units of 0.025V)");
1312b9450e43SRui Paulo
1313b9450e43SRui Paulo /* Temperature */
1314b9450e43SRui Paulo SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid),
13157029da5cSPawel Biernacki OID_AUTO, "temperature",
13167029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
1317b9450e43SRui Paulo sysctl_bcm2835_cpufreq_temperature, "I",
1318b9450e43SRui Paulo "SoC temperature (thousandths of a degree C)");
1319b9450e43SRui Paulo }
1320b9450e43SRui Paulo
1321b9450e43SRui Paulo /* ARM->VC lock */
1322b9450e43SRui Paulo sema_init(&vc_sema, 1, "vcsema");
1323b9450e43SRui Paulo
1324b9450e43SRui Paulo /* register callback for using mbox when interrupts are enabled */
1325b9450e43SRui Paulo sc->init_hook.ich_func = bcm2835_cpufreq_init;
1326b9450e43SRui Paulo sc->init_hook.ich_arg = sc;
1327b9450e43SRui Paulo
1328b9450e43SRui Paulo if (config_intrhook_establish(&sc->init_hook) != 0) {
1329b9450e43SRui Paulo device_printf(dev, "config_intrhook_establish failed\n");
1330b9450e43SRui Paulo return (ENOMEM);
1331b9450e43SRui Paulo }
1332b9450e43SRui Paulo
1333b9450e43SRui Paulo /* this device is controlled by cpufreq(4) */
1334b9450e43SRui Paulo cpufreq_register(dev);
1335b9450e43SRui Paulo
1336b9450e43SRui Paulo return (0);
1337b9450e43SRui Paulo }
1338b9450e43SRui Paulo
1339b9450e43SRui Paulo static int
bcm2835_cpufreq_detach(device_t dev)1340b9450e43SRui Paulo bcm2835_cpufreq_detach(device_t dev)
1341b9450e43SRui Paulo {
1342b9450e43SRui Paulo
1343b9450e43SRui Paulo sema_destroy(&vc_sema);
1344b9450e43SRui Paulo
1345b9450e43SRui Paulo return (cpufreq_unregister(dev));
1346b9450e43SRui Paulo }
1347b9450e43SRui Paulo
1348b9450e43SRui Paulo static int
bcm2835_cpufreq_set(device_t dev,const struct cf_setting * cf)1349b9450e43SRui Paulo bcm2835_cpufreq_set(device_t dev, const struct cf_setting *cf)
1350b9450e43SRui Paulo {
1351b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc;
1352b9450e43SRui Paulo uint32_t rate_hz, rem;
1353151ba793SAlexander Kabaev int resp_freq, arm_freq, min_freq, core_freq;
1354151ba793SAlexander Kabaev #ifdef DEBUG
1355151ba793SAlexander Kabaev int cur_freq;
1356151ba793SAlexander Kabaev #endif
1357b9450e43SRui Paulo
1358b9450e43SRui Paulo if (cf == NULL || cf->freq < 0)
1359b9450e43SRui Paulo return (EINVAL);
1360b9450e43SRui Paulo
1361b9450e43SRui Paulo sc = device_get_softc(dev);
1362b9450e43SRui Paulo
1363b9450e43SRui Paulo /* setting clock (Hz) */
1364b9450e43SRui Paulo rate_hz = (uint32_t)MHZ2HZ(cf->freq);
1365b9450e43SRui Paulo rem = rate_hz % HZSTEP;
1366b9450e43SRui Paulo rate_hz -= rem;
1367b9450e43SRui Paulo if (rate_hz == 0)
1368b9450e43SRui Paulo return (EINVAL);
1369b9450e43SRui Paulo
1370b9450e43SRui Paulo /* adjust min freq */
1371b9450e43SRui Paulo min_freq = sc->arm_min_freq;
13727413ae0eSAndrew Turner if (sc->turbo_mode != BCM2835_FIRMWARE_TURBO_ON)
1373b9450e43SRui Paulo if (min_freq > cpufreq_lowest_freq)
1374b9450e43SRui Paulo min_freq = cpufreq_lowest_freq;
1375b9450e43SRui Paulo
1376b9450e43SRui Paulo if (rate_hz < MHZ2HZ(min_freq) || rate_hz > MHZ2HZ(sc->arm_max_freq))
1377b9450e43SRui Paulo return (EINVAL);
1378b9450e43SRui Paulo
1379b9450e43SRui Paulo /* set new value and verify it */
1380b9450e43SRui Paulo VC_LOCK(sc);
1381151ba793SAlexander Kabaev #ifdef DEBUG
1382b9450e43SRui Paulo cur_freq = bcm2835_cpufreq_get_clock_rate(sc,
13837413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM);
1384151ba793SAlexander Kabaev #endif
1385b9450e43SRui Paulo resp_freq = bcm2835_cpufreq_set_clock_rate(sc,
13867413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM, rate_hz);
1387b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1388b9450e43SRui Paulo arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
13897413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM);
1390b9450e43SRui Paulo
1391b9450e43SRui Paulo /*
1392b9450e43SRui Paulo * if non-turbo and lower than or equal min_freq,
1393b9450e43SRui Paulo * clock down core and sdram to default first.
1394b9450e43SRui Paulo */
13957413ae0eSAndrew Turner if (sc->turbo_mode != BCM2835_FIRMWARE_TURBO_ON) {
1396b9450e43SRui Paulo core_freq = bcm2835_cpufreq_get_clock_rate(sc,
13977413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE);
1398b9450e43SRui Paulo if (rate_hz > MHZ2HZ(sc->arm_min_freq)) {
1399b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(sc,
14007413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE,
1401b9450e43SRui Paulo MHZ2HZ(sc->core_max_freq));
1402b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1403b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(sc,
14047413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM,
1405b9450e43SRui Paulo MHZ2HZ(sc->sdram_max_freq));
1406b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1407b9450e43SRui Paulo } else {
1408b9450e43SRui Paulo if (sc->core_min_freq < DEFAULT_CORE_FREQUENCY &&
1409b9450e43SRui Paulo core_freq > DEFAULT_CORE_FREQUENCY) {
1410b9450e43SRui Paulo /* first, down to 250, then down to min */
1411b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1412b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(sc,
14137413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE,
1414b9450e43SRui Paulo MHZ2HZ(DEFAULT_CORE_FREQUENCY));
1415b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1416b9450e43SRui Paulo /* reset core voltage */
1417b9450e43SRui Paulo bcm2835_cpufreq_set_voltage(sc,
14187413ae0eSAndrew Turner BCM2835_FIRMWARE_VOLTAGE_ID_CORE, 0);
1419b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1420b9450e43SRui Paulo }
1421b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(sc,
14227413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_CORE,
1423b9450e43SRui Paulo MHZ2HZ(sc->core_min_freq));
1424b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1425b9450e43SRui Paulo bcm2835_cpufreq_set_clock_rate(sc,
14267413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_SDRAM,
1427b9450e43SRui Paulo MHZ2HZ(sc->sdram_min_freq));
1428b9450e43SRui Paulo DELAY(TRANSITION_LATENCY);
1429b9450e43SRui Paulo }
1430b9450e43SRui Paulo }
1431b9450e43SRui Paulo
1432b9450e43SRui Paulo VC_UNLOCK(sc);
1433b9450e43SRui Paulo
1434b9450e43SRui Paulo if (resp_freq < 0 || arm_freq < 0 || resp_freq != arm_freq) {
1435b9450e43SRui Paulo device_printf(dev, "wrong freq\n");
1436b9450e43SRui Paulo return (EIO);
1437b9450e43SRui Paulo }
1438b9450e43SRui Paulo DPRINTF("cpufreq: %d -> %d\n", cur_freq, arm_freq);
1439b9450e43SRui Paulo
1440b9450e43SRui Paulo return (0);
1441b9450e43SRui Paulo }
1442b9450e43SRui Paulo
1443b9450e43SRui Paulo static int
bcm2835_cpufreq_get(device_t dev,struct cf_setting * cf)1444b9450e43SRui Paulo bcm2835_cpufreq_get(device_t dev, struct cf_setting *cf)
1445b9450e43SRui Paulo {
1446b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc;
1447b9450e43SRui Paulo int arm_freq;
1448b9450e43SRui Paulo
1449b9450e43SRui Paulo if (cf == NULL)
1450b9450e43SRui Paulo return (EINVAL);
1451b9450e43SRui Paulo
1452b9450e43SRui Paulo sc = device_get_softc(dev);
1453b9450e43SRui Paulo memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
1454b9450e43SRui Paulo cf->dev = NULL;
1455b9450e43SRui Paulo
1456b9450e43SRui Paulo /* get cuurent value */
1457b9450e43SRui Paulo VC_LOCK(sc);
1458b9450e43SRui Paulo arm_freq = bcm2835_cpufreq_get_clock_rate(sc,
14597413ae0eSAndrew Turner BCM2835_FIRMWARE_CLOCK_ID_ARM);
1460b9450e43SRui Paulo VC_UNLOCK(sc);
1461b9450e43SRui Paulo if (arm_freq < 0) {
1462b9450e43SRui Paulo device_printf(dev, "can't get clock\n");
1463b9450e43SRui Paulo return (EINVAL);
1464b9450e43SRui Paulo }
1465b9450e43SRui Paulo
1466b9450e43SRui Paulo /* CPU clock in MHz or 100ths of a percent. */
1467b9450e43SRui Paulo cf->freq = HZ2MHZ(arm_freq);
1468b9450e43SRui Paulo /* Voltage in mV. */
1469b9450e43SRui Paulo cf->volts = CPUFREQ_VAL_UNKNOWN;
1470b9450e43SRui Paulo /* Power consumed in mW. */
1471b9450e43SRui Paulo cf->power = CPUFREQ_VAL_UNKNOWN;
1472b9450e43SRui Paulo /* Transition latency in us. */
1473b9450e43SRui Paulo cf->lat = TRANSITION_LATENCY;
1474b9450e43SRui Paulo /* Driver providing this setting. */
1475b9450e43SRui Paulo cf->dev = dev;
1476b9450e43SRui Paulo
1477b9450e43SRui Paulo return (0);
1478b9450e43SRui Paulo }
1479b9450e43SRui Paulo
1480b9450e43SRui Paulo static int
bcm2835_cpufreq_make_freq_list(device_t dev,struct cf_setting * sets,int * count)1481b9450e43SRui Paulo bcm2835_cpufreq_make_freq_list(device_t dev, struct cf_setting *sets,
1482b9450e43SRui Paulo int *count)
1483b9450e43SRui Paulo {
1484b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc;
1485b9450e43SRui Paulo int freq, min_freq, volts, rem;
1486b9450e43SRui Paulo int idx;
1487b9450e43SRui Paulo
1488b9450e43SRui Paulo sc = device_get_softc(dev);
1489b9450e43SRui Paulo freq = sc->arm_max_freq;
1490b9450e43SRui Paulo min_freq = sc->arm_min_freq;
1491b9450e43SRui Paulo
1492b9450e43SRui Paulo /* adjust head freq to STEP */
1493b9450e43SRui Paulo rem = freq % MHZSTEP;
1494b9450e43SRui Paulo freq -= rem;
1495b9450e43SRui Paulo if (freq < min_freq)
1496b9450e43SRui Paulo freq = min_freq;
1497b9450e43SRui Paulo
1498b9450e43SRui Paulo /* if non-turbo, add extra low freq */
14997413ae0eSAndrew Turner if (sc->turbo_mode != BCM2835_FIRMWARE_TURBO_ON)
1500b9450e43SRui Paulo if (min_freq > cpufreq_lowest_freq)
1501b9450e43SRui Paulo min_freq = cpufreq_lowest_freq;
1502b9450e43SRui Paulo
150311cede48SLuiz Otavio O Souza /* XXX RPi2 have only 900/600MHz */
150411cede48SLuiz Otavio O Souza idx = 0;
150511cede48SLuiz Otavio O Souza volts = sc->min_voltage_core;
150611cede48SLuiz Otavio O Souza sets[idx].freq = freq;
150711cede48SLuiz Otavio O Souza sets[idx].volts = volts;
150811cede48SLuiz Otavio O Souza sets[idx].lat = TRANSITION_LATENCY;
150911cede48SLuiz Otavio O Souza sets[idx].dev = dev;
151011cede48SLuiz Otavio O Souza idx++;
151111cede48SLuiz Otavio O Souza if (freq != min_freq) {
151211cede48SLuiz Otavio O Souza sets[idx].freq = min_freq;
151311cede48SLuiz Otavio O Souza sets[idx].volts = volts;
151411cede48SLuiz Otavio O Souza sets[idx].lat = TRANSITION_LATENCY;
151511cede48SLuiz Otavio O Souza sets[idx].dev = dev;
151611cede48SLuiz Otavio O Souza idx++;
151711cede48SLuiz Otavio O Souza }
151811cede48SLuiz Otavio O Souza *count = idx;
1519b9450e43SRui Paulo
1520b9450e43SRui Paulo return (0);
1521b9450e43SRui Paulo }
1522b9450e43SRui Paulo
1523b9450e43SRui Paulo static int
bcm2835_cpufreq_settings(device_t dev,struct cf_setting * sets,int * count)1524b9450e43SRui Paulo bcm2835_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
1525b9450e43SRui Paulo {
1526b9450e43SRui Paulo struct bcm2835_cpufreq_softc *sc;
1527b9450e43SRui Paulo
1528b9450e43SRui Paulo if (sets == NULL || count == NULL)
1529b9450e43SRui Paulo return (EINVAL);
1530b9450e43SRui Paulo
1531b9450e43SRui Paulo sc = device_get_softc(dev);
1532b9450e43SRui Paulo if (sc->arm_min_freq < 0 || sc->arm_max_freq < 0) {
1533b9450e43SRui Paulo printf("device is not configured\n");
1534b9450e43SRui Paulo return (EINVAL);
1535b9450e43SRui Paulo }
1536b9450e43SRui Paulo
1537b9450e43SRui Paulo /* fill data with unknown value */
1538b9450e43SRui Paulo memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
1539b9450e43SRui Paulo /* create new array up to count */
1540b9450e43SRui Paulo bcm2835_cpufreq_make_freq_list(dev, sets, count);
1541b9450e43SRui Paulo
1542b9450e43SRui Paulo return (0);
1543b9450e43SRui Paulo }
1544b9450e43SRui Paulo
1545b9450e43SRui Paulo static int
bcm2835_cpufreq_type(device_t dev,int * type)1546b9450e43SRui Paulo bcm2835_cpufreq_type(device_t dev, int *type)
1547b9450e43SRui Paulo {
1548b9450e43SRui Paulo
1549b9450e43SRui Paulo if (type == NULL)
1550b9450e43SRui Paulo return (EINVAL);
1551b9450e43SRui Paulo *type = CPUFREQ_TYPE_ABSOLUTE;
1552b9450e43SRui Paulo
1553b9450e43SRui Paulo return (0);
1554b9450e43SRui Paulo }
1555b9450e43SRui Paulo
1556b9450e43SRui Paulo static device_method_t bcm2835_cpufreq_methods[] = {
1557b9450e43SRui Paulo /* Device interface */
1558b9450e43SRui Paulo DEVMETHOD(device_identify, bcm2835_cpufreq_identify),
1559b9450e43SRui Paulo DEVMETHOD(device_probe, bcm2835_cpufreq_probe),
1560b9450e43SRui Paulo DEVMETHOD(device_attach, bcm2835_cpufreq_attach),
1561b9450e43SRui Paulo DEVMETHOD(device_detach, bcm2835_cpufreq_detach),
1562b9450e43SRui Paulo
1563b9450e43SRui Paulo /* cpufreq interface */
1564b9450e43SRui Paulo DEVMETHOD(cpufreq_drv_set, bcm2835_cpufreq_set),
1565b9450e43SRui Paulo DEVMETHOD(cpufreq_drv_get, bcm2835_cpufreq_get),
1566b9450e43SRui Paulo DEVMETHOD(cpufreq_drv_settings, bcm2835_cpufreq_settings),
1567b9450e43SRui Paulo DEVMETHOD(cpufreq_drv_type, bcm2835_cpufreq_type),
1568b9450e43SRui Paulo
1569b9450e43SRui Paulo DEVMETHOD_END
1570b9450e43SRui Paulo };
1571b9450e43SRui Paulo
1572b9450e43SRui Paulo static driver_t bcm2835_cpufreq_driver = {
1573b9450e43SRui Paulo "bcm2835_cpufreq",
1574b9450e43SRui Paulo bcm2835_cpufreq_methods,
1575b9450e43SRui Paulo sizeof(struct bcm2835_cpufreq_softc),
1576b9450e43SRui Paulo };
1577b9450e43SRui Paulo
1578*82d4dc06SJohn Baldwin DRIVER_MODULE(bcm2835_cpufreq, cpu, bcm2835_cpufreq_driver, 0, 0);
15797413ae0eSAndrew Turner MODULE_DEPEND(bcm2835_cpufreq, bcm2835_firmware, 1, 1, 1);
1580