Index | Thread | Search

From:
Ron Manosuthi <rman401@proton.me>
Subject:
Re: Initial Raspberry Pi CPU frequency driver (apm -A)
To:
"tech@openbsd.org" <tech@openbsd.org>
Cc:
Vitaliy Makkoveev <mvs@openbsd.org>
Date:
Wed, 21 Jan 2026 22:22:13 +0000

Download raw body.

Thread
Hi Vitaliy,

Diff at the end. Only changed manpage, so you can ignore it Jan :)
Thanks for the feedback so far.

Questions:

1. For the manpage, do I have to handle the RCS ID myself?
This question also extends to code in general.
2. How are new features requiring a lot of changes handled around here?
I looked through a couple commits and they follow this format:

- asks "ok?"
- waits
- commits them upon "ok name@"

but they're mostly core devs with write access to the repo, and the
commits are small.

Anyway, fixed all manpage lints except for

mandoc: share/man/man4/man4.arm64/rpicpu.4: STYLE: RCS id missing: (OpenBSD)

It looks like drivers don't really have man9,
so removed reference to bcmclock_set_frequency(9).

> Also. Does the `sc_post_lock' really required to be rwlock? Mutex seems
> to be more reasonable here.

bcmmbox_post(9) calls bcmmbox_read(9) which sleeps when !cold.
Can't hold a mutex while sleeping, no?

panic: assertwaitok: non-zero mutex count: 1

I could unlock it before sleeping but that would negate the whole goal of
"only one post at a time".

----

re: temp

> I see the temperature is much higher with your diff. Even while my RPI
> does nothing and hw.perfpolicy is set to 'auto' I see the lowest
> temperature is about 49 degrees. Without your diff this idling RPI is
> around 42 degrees. I don't think this is an expected behavior.

I see minimal temperature difference with/without the patch.
It took Pi 4B a while for temperature to stabilize from unused-for-days start.
I saw up to a 7C difference.

Methodology: Let it rest for at least 10 minutes after boot.
22C room, no sunlight. 5 samples each.

while true; do sysctl hw.sensors.bcmtmon0.temp0; sleep 60; done

-- Pi 4B, small heatsink

no patch

hw.sensors.bcmtmon0.temp0=56.97 degC
hw.sensors.bcmtmon0.temp0=56.48 degC
hw.sensors.bcmtmon0.temp0=56.97 degC
hw.sensors.bcmtmon0.temp0=56.48 degC
hw.sensors.bcmtmon0.temp0=56.48 degC

patch

hw.sensors.bcmtmon0.temp0=56.48 degC
hw.sensors.bcmtmon0.temp0=56.97 degC
hw.sensors.bcmtmon0.temp0=56.48 degC
hw.sensors.bcmtmon0.temp0=55.99 degC
hw.sensors.bcmtmon0.temp0=56.48 degC

-- Pi 400

no patch

hw.sensors.bcmtmon0.temp0=37.97 degC
hw.sensors.bcmtmon0.temp0=38.46 degC
hw.sensors.bcmtmon0.temp0=37.97 degC
hw.sensors.bcmtmon0.temp0=37.00 degC
hw.sensors.bcmtmon0.temp0=37.48 degC

patch

hw.sensors.bcmtmon0.temp0=37.97 degC
hw.sensors.bcmtmon0.temp0=37.97 degC
hw.sensors.bcmtmon0.temp0=37.97 degC
hw.sensors.bcmtmon0.temp0=38.46 degC
hw.sensors.bcmtmon0.temp0=38.46 degC

- Ron

----

diff --git share/man/man4/man4.arm64/Makefile share/man/man4/man4.arm64/Makefile
index f85efe499e0..8de25a14a1c 100644
--- share/man/man4/man4.arm64/Makefile
+++ share/man/man4/man4.arm64/Makefile
@@ -6,7 +6,7 @@ MAN=	agintc.4 agtimer.4 ampchwm.4 ampintc.4 \
 	aplhidev.4 apliic.4 aplintc.4 aplmbox.4 aplmca.4 aplnco.4 aplns.4 \
 	aplpcie.4 aplpinctrl.4 aplpmgr.4 aplpmu.4 aplpwm.4 \
 	aplsart.4 aplsmc.4 aplspi.4 aplspmi.4 apm.4 \
-	intro.4 rpiclock.4 rpipwm.4 rpirtc.4 rpone.4 smbios.4
+	intro.4 rpiclock.4 rpicpu.4 rpipwm.4 rpirtc.4 rpone.4 smbios.4
 
 MANSUBDIR=arm64
 
diff --git share/man/man4/man4.arm64/rpicpu.4 share/man/man4/man4.arm64/rpicpu.4
new file mode 100644
index 00000000000..e90d3f88466
--- /dev/null
+++ share/man/man4/man4.arm64/rpicpu.4
@@ -0,0 +1,59 @@
+.\"	RCS id goes here
+.\"
+.\" Copyright (c) 2026 Ron Manosuthi <rman401@proton.me>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: January 7 2026 $
+.Dt RPICPU 4 arm64
+.Os
+.Sh NAME
+.Nm rpicpu
+.Nd Raspberry Pi CPU frequency driver
+.Sh SYNOPSIS
+.Cd "rpicpu* at fdt?"
+.Sh DESCRIPTION
+The
+.Nm
+driver enables support for CPU frequency reclocking on Raspberry Pi devices.
+.Pp
+Currently, the driver is only enabled for the following tested devices:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+Raspberry Pi 4B
+.It
+Raspberry Pi 400
+.El
+.Sh SEE ALSO
+.Xr bcmclock 4 ,
+.Xr intro 4 ,
+.Xr apm 8 ,
+.Xr apmd 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 7.8 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Ron Manosuthi Aq Mt rman401@proton.me .
+.Sh CAVEATS
+The
+.Nm
+driver does not handle voltage adjustments or thermal throttling.
+.Pp
+Sensor reporting has not been implemented.
diff --git sys/arch/arm64/conf/GENERIC sys/arch/arm64/conf/GENERIC
index bc1aebe0610..100f7687744 100644
--- sys/arch/arm64/conf/GENERIC
+++ sys/arch/arm64/conf/GENERIC
@@ -255,6 +255,7 @@ dwctwo*		at fdt?
 usb*		at dwctwo?
 rpone*		at pci?
 rpiclock*	at fdt? early 1
+rpicpu*		at fdt?
 rpigpio*	at fdt? early 1
 rpipwm*		at fdt?
 rpirtc*		at fdt?
diff --git sys/arch/arm64/conf/files.arm64 sys/arch/arm64/conf/files.arm64
index 34d9f5d35f5..6635665cb94 100644
--- sys/arch/arm64/conf/files.arm64
+++ sys/arch/arm64/conf/files.arm64
@@ -272,6 +272,10 @@ device	rpiclock
 attach	rpiclock at fdt
 file	arch/arm64/dev/rpiclock.c		rpiclock
 
+device	rpicpu
+attach	rpicpu at fdt
+file	arch/arm64/dev/rpicpu.c			rpicpu
+
 device	rpigpio
 attach	rpigpio at fdt
 file	arch/arm64/dev/rpigpio.c		rpigpio
diff --git sys/arch/arm64/dev/rpicpu.c sys/arch/arm64/dev/rpicpu.c
new file mode 100644
index 00000000000..e426bdb1185
--- /dev/null
+++ sys/arch/arm64/dev/rpicpu.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2026 Ron Manosuthi <rman401@proton.me>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/task.h>
+#include <sys/atomic.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ic/bcm2835_clock.h>
+#include <dev/ic/bcm2835_mbox.h>
+#include <dev/ic/bcm2835_vcprop.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/fdt.h>
+
+#define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
+#define HZ_TO_MHZ(hz) ((hz) / 1000000)
+
+struct rpicpu_softc {
+	struct device	sc_dev;
+
+	uint32_t	sc_min_clk_hz;
+	uint32_t	sc_max_clk_hz;
+	uint32_t	sc_target_clk_hz;
+
+	struct task	sc_task_reclk;
+};
+
+static struct rpicpu_softc *rpicpu_sc;
+
+int	rpicpu_match(struct device *, void *, void *);
+void	rpicpu_attach(struct device *, struct device *, void *);
+int	rpicpu_get_board_rev(uint32_t *);
+int	rpicpu_clockspeed(int *);
+void	rpicpu_setperf(int);
+void	rpicpu_reclock(void *);
+
+const struct cfattach rpicpu_ca = {
+	sizeof (struct rpicpu_softc), rpicpu_match, rpicpu_attach
+};
+
+struct cfdriver rpicpu_cd = {
+	NULL, "rpicpu", DV_DULL
+};
+
+int
+rpicpu_match(struct device *parent, void *match, void *aux)
+{
+	struct fdt_attach_args *faa = aux;
+
+	/* XXX find a better node */
+	return OF_is_compatible(faa->fa_node, "raspberrypi,bcm2835-firmware");
+}
+
+void
+rpicpu_attach(struct device *parent, struct device *self, void *aux)
+{
+	struct rpicpu_softc *sc = (struct rpicpu_softc *)self;
+	int error;
+	uint32_t board_rev;
+
+	error = rpicpu_get_board_rev(&board_rev);
+	if (error) {
+		printf(": failed to get board revision: %d\n", error);
+		return;
+	}
+	board_rev &= VCPROP_REV_MODEL;
+	board_rev >>= 4;
+	if (board_rev != RPI_MODEL_4B &&
+	    board_rev != RPI_MODEL_400) {
+		printf(": unsupported board revision %#x\n", board_rev);
+		return;
+	}
+
+	error = bcmclock_get_frequency(&sc->sc_min_clk_hz,
+	    VCPROP_CLK_ARM, VCPROPTAG_GET_MIN_CLOCKRATE);
+	if (error) {
+		printf(": failed to get min clock frequency: %d\n", error);
+		return;
+	}
+
+	error = bcmclock_get_frequency(&sc->sc_max_clk_hz,
+	    VCPROP_CLK_ARM, VCPROPTAG_GET_MAX_CLOCKRATE);
+	if (error) {
+		printf(": failed to get max clock frequency: %d\n", error);
+		return;
+	}
+
+	cpu_cpuspeed = rpicpu_clockspeed;
+	cpu_setperf = rpicpu_setperf;
+
+	task_set(&sc->sc_task_reclk, rpicpu_reclock, sc);
+	rpicpu_sc = sc;
+
+	printf(": %d-%d MHz\n",
+	    HZ_TO_MHZ(sc->sc_min_clk_hz),
+	    HZ_TO_MHZ(sc->sc_max_clk_hz));
+
+	/* XXX sensordev */
+}
+
+int
+rpicpu_get_board_rev(uint32_t *board_rev)
+{
+	struct request {
+		struct vcprop_buffer_hdr vb_hdr;
+		struct vcprop_tag_boardrev vbt_br;
+		struct vcprop_tag end;
+	} __packed;
+
+	int error;
+	uint32_t result;
+	struct request req = {
+		.vb_hdr = {
+			.vpb_len = sizeof(req),
+			.vpb_rcode = VCPROP_PROCESS_REQUEST,
+		},
+		.vbt_br = {
+			.tag = {
+				.vpt_tag = VCPROPTAG_GET_BOARDREVISION,
+				.vpt_len = VCPROPTAG_LEN(req.vbt_br),
+				.vpt_rcode = VCPROPTAG_REQUEST
+			},
+			.rev = 0,
+		},
+		.end = {
+			.vpt_tag = VCPROPTAG_NULL
+		}
+	};
+
+	error = bcmmbox_post(BCMMBOX_CHANARM2VC, &req, sizeof(req), &result);
+	if (error) {
+		printf("%s: post failed, error %d\n", __func__, error);
+		return error;
+	}
+
+	if (vcprop_tag_success_p(&req.vbt_br.tag)) {
+		*board_rev = req.vbt_br.rev;
+		return 0;
+	} else {
+		return (EINVAL);
+	}
+}
+
+int
+rpicpu_clockspeed(int *freq)
+{
+	struct rpicpu_softc *sc = rpicpu_sc;
+	uint32_t clk_hz;
+
+	if (sc == NULL)
+		return (ENXIO);
+
+	clk_hz = atomic_load_int(&sc->sc_target_clk_hz);
+	*freq = HZ_TO_MHZ(clk_hz);
+	return 0;
+}
+
+void
+rpicpu_setperf(int level)
+{
+	struct rpicpu_softc *sc = rpicpu_sc;
+	uint64_t target_clk_hz, min_hz, max_hz;
+
+	if (sc == NULL)
+		return;
+
+	min_hz = sc->sc_min_clk_hz;
+	max_hz = sc->sc_max_clk_hz;
+	target_clk_hz = min_hz + (((max_hz - min_hz) * level) / 100);
+	atomic_store_int(&sc->sc_target_clk_hz, (uint32_t)target_clk_hz);
+
+	/* Defer work to task queue so bcmmbox_post(9) can sleep. */
+	task_add(systqmp, &sc->sc_task_reclk);
+}
+
+void
+rpicpu_reclock(void *cookie)
+{
+	struct rpicpu_softc *sc = (struct rpicpu_softc *)cookie;
+	uint32_t target_clk_hz;
+
+	target_clk_hz = atomic_load_int(&sc->sc_target_clk_hz);
+	KASSERT(target_clk_hz >= sc->sc_min_clk_hz &&
+	    target_clk_hz <= sc->sc_max_clk_hz);
+
+	bcmclock_set_frequency(VCPROP_CLK_ARM, target_clk_hz);
+}
diff --git sys/dev/fdt/bcm2835_clock.c sys/dev/fdt/bcm2835_clock.c
index 8927f8968af..08493e30725 100644
--- sys/dev/fdt/bcm2835_clock.c
+++ sys/dev/fdt/bcm2835_clock.c
@@ -53,6 +53,7 @@
 #include <dev/ofw/ofw_clock.h>
 #include <dev/ofw/openfirm.h>
 
+#include <dev/ic/bcm2835_clock.h>
 #include <dev/ic/bcm2835_mbox.h>
 #include <dev/ic/bcm2835_vcprop.h>
 
@@ -84,14 +85,14 @@ struct bcmclock_softc {
 int bcmclock_match(struct device *, void *, void *);
 void bcmclock_attach(struct device *, struct device *, void *);
 
+uint32_t bcmclock_cd_get_frequency(void *, uint32_t *);
+
 const struct cfattach bcmclock_ca = {
 	sizeof(struct bcmclock_softc),
 	bcmclock_match,
 	bcmclock_attach,
 };
 
-uint32_t bcmclock_get_frequency(void *, uint32_t *);
-
 struct cfdriver bcmclock_cd = { NULL, "bcmclock", DV_DULL };
 
 int
@@ -113,21 +114,80 @@ bcmclock_attach(struct device *parent, struct device *self, void *aux)
 
 	sc->sc_cd.cd_node = faa->fa_node;
 	sc->sc_cd.cd_cookie = sc;
-	sc->sc_cd.cd_get_frequency = bcmclock_get_frequency;
+	sc->sc_cd.cd_get_frequency = bcmclock_cd_get_frequency;
 
 	clock_register(&sc->sc_cd);
 }
 
-uint32_t
-bcmclock_get_frequency(void *cookie, uint32_t *cells)
+int
+bcmclock_get_frequency(uint32_t *res, uint32_t clk_vc_id, uint32_t clk_vc_type)
 {
 	struct request {
 		struct vcprop_buffer_hdr vb_hdr;
 		struct vcprop_tag_clockrate vbt_clkrate;
 		struct vcprop_tag end;
 	} __packed;
+	uint32_t result;
+	int error;
+
+	if (clk_vc_id == 0) {
+		printf("%s: unknown clock id %d\n",
+		    __func__, clk_vc_id);
+		return (ENOENT);
+	}
 
+	struct request req = {
+		.vb_hdr = {
+			.vpb_len = sizeof(req),
+			.vpb_rcode = VCPROP_PROCESS_REQUEST,
+		},
+		.vbt_clkrate = {
+			.tag = {
+				.vpt_tag = clk_vc_type,
+				.vpt_len = VCPROPTAG_LEN(req.vbt_clkrate),
+				.vpt_rcode = VCPROPTAG_REQUEST
+			},
+			.id = clk_vc_id,
+		},
+		.end = {
+			.vpt_tag = VCPROPTAG_NULL
+		}
+	};
+
+	error = bcmmbox_post(BCMMBOX_CHANARM2VC, &req, sizeof(req), &result);
+	if (error) {
+		printf("%s: post error %d\n", __func__, error);
+		return error;
+	}
+
+	if (vcprop_tag_success_p(&req.vbt_clkrate.tag)) {
+		*res = req.vbt_clkrate.rate;
+		return 0;
+	} else {
+		printf("%s: vcprop result %d:%d\n",
+		    __func__, req.vb_hdr.vpb_rcode,
+		    req.vbt_clkrate.tag.vpt_rcode);
+		return (EINVAL);
+	}
+}
+
+int
+bcmclock_set_frequency(uint32_t clk_vc_id, uint32_t clk_hz)
+{
+	struct request {
+		struct vcprop_buffer_hdr vb_hdr;
+		struct vcprop_tag_clockrate vbt_clkrate;
+		struct vcprop_tag end;
+	} __packed;
 	uint32_t result;
+	int error;
+
+	if (clk_vc_id == 0) {
+		printf("%s: unknown clock id %d\n",
+		    __func__, clk_vc_id);
+		return (ENOENT);
+	}
+
 	struct request req = {
 		.vb_hdr = {
 			.vpb_len = sizeof(req),
@@ -135,68 +195,90 @@ bcmclock_get_frequency(void *cookie, uint32_t *cells)
 		},
 		.vbt_clkrate = {
 			.tag = {
-				.vpt_tag = VCPROPTAG_GET_CLOCKRATE,
+				.vpt_tag = VCPROPTAG_SET_CLOCKRATE,
 				.vpt_len = VCPROPTAG_LEN(req.vbt_clkrate),
 				.vpt_rcode = VCPROPTAG_REQUEST
 			},
+			.id = clk_vc_id,
+			.rate = clk_hz,
 		},
 		.end = {
 			.vpt_tag = VCPROPTAG_NULL
 		}
 	};
 
+	error = bcmmbox_post(BCMMBOX_CHANARM2VC, &req, sizeof(req), &result);
+	if (error) {
+		printf("%s: post error %d\n", __func__, error);
+		return error;
+	}
+
+	if (vcprop_tag_success_p(&req.vbt_clkrate.tag)) {
+		return 0;
+	} else {
+		printf("%s: vcprop result %d:%d\n",
+		    __func__, req.vb_hdr.vpb_rcode,
+		    req.vbt_clkrate.tag.vpt_rcode);
+		return (EINVAL);
+	}
+}
+
+uint32_t
+bcmclock_cd_get_frequency(void *cookie, uint32_t *cells)
+{
+	uint32_t clk_id = 0;
+	uint32_t res;
+	int error;
+
 	switch (cells[0]) {
 	case BCMCLOCK_CLOCK_TIMER:
 		break;
 	case BCMCLOCK_CLOCK_UART:
-		req.vbt_clkrate.id = VCPROP_CLK_UART;
+		clk_id = VCPROP_CLK_UART;
 		break;
 	case BCMCLOCK_CLOCK_VPU:
-		req.vbt_clkrate.id = VCPROP_CLK_CORE;
+		clk_id = VCPROP_CLK_CORE;
 		break;
 	case BCMCLOCK_CLOCK_V3D:
-		req.vbt_clkrate.id = VCPROP_CLK_V3D;
+		clk_id = VCPROP_CLK_V3D;
 		break;
 	case BCMCLOCK_CLOCK_ISP:
-		req.vbt_clkrate.id = VCPROP_CLK_ISP;
+		clk_id = VCPROP_CLK_ISP;
 		break;
 	case BCMCLOCK_CLOCK_H264:
-		req.vbt_clkrate.id = VCPROP_CLK_H264;
+		clk_id = VCPROP_CLK_H264;
 		break;
 	case BCMCLOCK_CLOCK_VEC:
 		break;
 	case BCMCLOCK_CLOCK_HSM:
 		break;
 	case BCMCLOCK_CLOCK_SDRAM:
-		req.vbt_clkrate.id = VCPROP_CLK_SDRAM;
+		clk_id = VCPROP_CLK_SDRAM;
 		break;
 	case BCMCLOCK_CLOCK_TSENS:
 		break;
 	case BCMCLOCK_CLOCK_EMMC:
-		req.vbt_clkrate.id = VCPROP_CLK_EMMC;
+		clk_id = VCPROP_CLK_EMMC;
 		break;
 	case BCMCLOCK_CLOCK_PERIIMAGE:
 		break;
 	case BCMCLOCK_CLOCK_PWM:
-		req.vbt_clkrate.id = VCPROP_CLK_PWM;
+		clk_id = VCPROP_CLK_PWM;
 		break;
 	case BCMCLOCK_CLOCK_PCM:
 		break;
 	}
-
-	if (req.vbt_clkrate.id == 0) {
-		printf("bcmclock[unknown]: request to unknown clock type %d\n",
-		       cells[0]);
+	if (clk_id == 0) {
+		printf("%s: unknown clock device %d\n", __func__, cells[0]);
 		return 0;
 	}
 
-	bcmmbox_post(BCMMBOX_CHANARM2VC, &req, sizeof(req), &result);
-
-	if (vcprop_tag_success_p(&req.vbt_clkrate.tag))
-		return req.vbt_clkrate.rate;
-
-	printf("%s: vcprop result %x:%x\n", __func__, req.vb_hdr.vpb_rcode,
-	       req.vbt_clkrate.tag.vpt_rcode);
+	error = bcmclock_get_frequency(&res, clk_id, VCPROPTAG_GET_CLOCKRATE);
+	if (error) {
+		printf("%s: failed to get frequency for clock device %d: \
+		    error %d\n", __func__, cells[0], error);
+		return 0;
+	}
 
-	return 0;
+	return res;
 }
diff --git sys/dev/fdt/bcm2835_mbox.c sys/dev/fdt/bcm2835_mbox.c
index 6987529f916..2835d90260a 100644
--- sys/dev/fdt/bcm2835_mbox.c
+++ sys/dev/fdt/bcm2835_mbox.c
@@ -76,7 +76,14 @@ struct bcmmbox_softc {
 
 	void *sc_ih;
 
-	struct mutex sc_intr_lock;
+	/*
+	 * We only have one DMA buffer.
+	 * Serialize bcmmbox_post(9) to avoid overwriting it.
+	 */
+	struct rwlock	sc_post_lock;
+
+	struct mutex	sc_intr_lock;
+
 	int sc_chan[BCMMBOX_NUM_CHANNELS];
 	uint32_t sc_mbox[BCMMBOX_NUM_CHANNELS];
 };
@@ -122,6 +129,7 @@ bcmmbox_attach(struct device *parent, struct device *self, void *aux)
 	bcmmbox_sc = sc;
 
 	mtx_init(&sc->sc_intr_lock, IPL_VM);
+	rw_init(&sc->sc_post_lock, "bcmmbox_post");
 
 	if (faa->fa_nreg < 1) {
 		printf(": no registers\n");
@@ -162,8 +170,8 @@ bcmmbox_attach(struct device *parent, struct device *self, void *aux)
 		goto clean_dmamap_destroy;
 	}
 
-	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_VM, bcmmbox_intr, sc,
-	    DEVNAME(sc));
+	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_VM,
+	    bcmmbox_intr, sc, DEVNAME(sc));
 	if (sc->sc_ih == NULL) {
 		printf(": failed to establish interrupt\n");
 		goto clean_dmamap;
@@ -227,7 +235,9 @@ bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast)
 
 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
 
-	while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX0_STATUS), BCMMBOX_STATUS_EMPTY)) {
+	while (!ISSET(
+	    bcmmbox_reg_read(sc, BCMMBOX0_STATUS),
+	    BCMMBOX_STATUS_EMPTY)) {
 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
 
 		chan = mbox & BCMMBOX_CHANNEL_MASK;
@@ -318,6 +328,7 @@ bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res)
 	if (sc == NULL)
 		return ENXIO;
 
+	rw_enter_write(&sc->sc_post_lock);
 	memcpy(sc->sc_dmabuf, buf, len);
 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0, len,
 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
@@ -328,6 +339,7 @@ bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res)
 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0, len,
 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
 	memcpy(buf, sc->sc_dmabuf, len);
+	rw_exit_write(&sc->sc_post_lock);
 
 	return 0;
 }
diff --git sys/dev/ic/bcm2835_clock.h sys/dev/ic/bcm2835_clock.h
new file mode 100644
index 00000000000..f9d6d9e7ac9
--- /dev/null
+++ sys/dev/ic/bcm2835_clock.h
@@ -0,0 +1,7 @@
+#ifndef BCM2835_CLOCK_H
+#define BCM2835_CLOCK_H
+
+int bcmclock_get_frequency(uint32_t *, uint32_t, uint32_t);
+int bcmclock_set_frequency(uint32_t, uint32_t);
+
+#endif
diff --git sys/dev/ic/bcm2835_vcprop.h sys/dev/ic/bcm2835_vcprop.h
index 09350fc94c5..1488947ebba 100644
--- sys/dev/ic/bcm2835_vcprop.h
+++ sys/dev/ic/bcm2835_vcprop.h
@@ -152,27 +152,48 @@ struct vcprop_tag_boardmodel {
 	uint32_t model;
 };
 
+/*
+ * Revision codes:
+ * <https://github.com/raspberrypi/documentation/blob/master/documentation/asciidoc/computers/raspberry-pi/revision-codes.adoc>
+ */
 struct vcprop_tag_boardrev {
 	struct vcprop_tag tag;
 	uint32_t rev;
 };
 #define	VCPROP_REV_PCBREV	15
 #define	VCPROP_REV_MODEL	(255 << 4)
-#define	 RPI_MODEL_A		0
-#define	 RPI_MODEL_B		1
-#define	 RPI_MODEL_A_PLUS	2
-#define	 RPI_MODEL_B_PLUS	3
-#define	 RPI_MODEL_B_PI2	4
-#define	 RPI_MODEL_ALPHA	5
-#define	 RPI_MODEL_COMPUTE	6
-#define	 RPI_MODEL_ZERO		7
-#define	 RPI_MODEL_B_PI3	8
-#define	 RPI_MODEL_COMPUTE_PI3	9
-#define	 RPI_MODEL_ZERO_W	10
+#define	 RPI_MODEL_A		0x00
+#define	 RPI_MODEL_B		0x01
+#define	 RPI_MODEL_A_PLUS	0x02
+#define	 RPI_MODEL_B_PLUS	0x03
+#define	 RPI_MODEL_2B		0x04
+#define	 RPI_MODEL_ALPHA	0x05
+#define	 RPI_MODEL_CM1		0x06
+#define	 RPI_MODEL_3B		0x08
+#define	 RPI_MODEL_ZERO		0x09
+#define	 RPI_MODEL_CM3		0x0a
+#define	 RPI_MODEL_ZERO_W	0x0c
+#define	 RPI_MODEL_3B_PLUS	0x0d
+#define	 RPI_MODEL_3A_PLUS	0x0e
+/* #define	 RPI_MODEL_INTERNAL	0x0f */
+#define	 RPI_MODEL_CM3_PLUS	0x10
+#define	 RPI_MODEL_4B		0x11
+#define	 RPI_MODEL_ZERO_2W	0x12
+#define	 RPI_MODEL_400		0x13
+#define	 RPI_MODEL_CM4		0x14
+#define	 RPI_MODEL_CM4S		0x15
+/* #define	 RPI_MODEL_INTERNAL	0x16 */
+#define	 RPI_MODEL_5		0x17
+#define	 RPI_MODEL_CM5		0x18
+#define	 RPI_MODEL_500		0x19
+#define	 RPI_MODEL_CM5_LITE	0x1a
+
 #define	VCPROP_REV_PROCESSOR	(15 << 12)
 #define	 RPI_PROCESSOR_BCM2835	0
 #define	 RPI_PROCESSOR_BCM2836	1
 #define	 RPI_PROCESSOR_BCM2837	2
+#define	 RPI_PROCESSOR_BCM2711	3
+#define	 RPI_PROCESSOR_BCM2712	4
 #define	VCPROP_REV_MANUF	(15 << 16)
 #define	VCPROP_REV_MEMSIZE	(7 << 20)
 #define	VCPROP_REV_ENCFLAG	(1 << 23)