git: fa28ede31dac - main - jh7110_temp: CPU temperature sensor for JH7110

From: Mitchell Horne <mhorne_at_FreeBSD.org>
Date: Tue, 22 Sep 2026 13:52:53 UTC
The branch main has been updated by mhorne:

URL: https://cgit.FreeBSD.org/src/commit/?id=fa28ede31dac57fdb191afd114c70439b9b41e4f

commit fa28ede31dac57fdb191afd114c70439b9b41e4f
Author:     Brian Scott <bscott@bunyatech.com.au>
AuthorDate: 2026-09-22 13:31:47 +0000
Commit:     Mitchell Horne <mhorne@FreeBSD.org>
CommitDate: 2026-09-22 13:52:24 +0000

    jh7110_temp: CPU temperature sensor for JH7110
    
    This patch adds a driver for the CPU temperature sensor on the jh7110
    SoC.
    
    The calibration numbers come from the OpenBSD driver but are reworked to
    produce a result in K rather than C.
    
    The temperature is exposed as a sysctl, dev.jh7110_temp.0.temperature
    but I have also exposed it as dev.cpu.0.temperature because that's where
    you find it on a RaspberryPi and amdtemp(4), so it's a lot more obvious.
    
    (mhorne: Added 'starfive,jh7100-temp' compatible.)
    
    Reviewed by:    mhorne, bnovkov
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D59478
---
 sys/dev/clk/starfive/jh7110_clk_sys.c |  16 ++
 sys/riscv/starfive/files.starfive     |   1 +
 sys/riscv/starfive/jh7110_temp.c      | 269 ++++++++++++++++++++++++++++++++++
 3 files changed, 286 insertions(+)

diff --git a/sys/dev/clk/starfive/jh7110_clk_sys.c b/sys/dev/clk/starfive/jh7110_clk_sys.c
index 4bc29b20bc91..0fbde920e250 100644
--- a/sys/dev/clk/starfive/jh7110_clk_sys.c
+++ b/sys/dev/clk/starfive/jh7110_clk_sys.c
@@ -49,6 +49,7 @@ static const char *cpu_core_p[] = { "cpu_root" };
 static const char *cpu_bus_p[] = { "cpu_core" };
 static const char *perh_root_p[] = { "pll0_out", "pll2_out" };
 static const char *bus_root_p[] = { "osc", "pll2_out" };
+static const char *snd_root_p[] = { "pll2_out" };
 
 static const char *apb_bus_p[] = { "stg_axiahb" };
 static const char *apb0_p[] = { "apb_bus" };
@@ -88,6 +89,10 @@ static const char *gmac1_tx_inv_p[] = { "gmac1_tx" };
 static const char *gmac1_tx_p[] = { "gmac1_gtxclk", "gmac1_rmii_rtx" };
 static const char *gmac1_rx_p[] = { "gmac1_rgmii_rxin", "gmac1_rmii_rtx" };
 static const char *gmac1_rx_inv_p[] = { "gmac1_rx" };
+static const char *temp_sensor_clk_apb_p[] = { "apb0" };
+static const char *temp_sensor_clk_core_p[] = { "osc" };
+static const char *pwmdac_clk_apb_p[] = { "apb0" };
+static const char *pwmdac_clk_core_p[] = { "snd_root" };
 
 /* non-pll SYS clocks */
 static const struct jh7110_clk_def sys_clks[] = {
@@ -96,6 +101,7 @@ static const struct jh7110_clk_def sys_clks[] = {
 	JH7110_DIV(JH7110_SYSCLK_CPU_BUS, "cpu_bus", cpu_bus_p, 2),
 	JH7110_GATEDIV(JH7110_SYSCLK_PERH_ROOT, "perh_root", perh_root_p, 2),
 	JH7110_MUX(JH7110_SYSCLK_BUS_ROOT, "bus_root", bus_root_p),
+	JH7110_DIV(JH7110_SYSCLK_AUDIO_ROOT, "snd_root", snd_root_p, 8),
 
 	JH7110_GATE(JH7110_SYSCLK_APB0, "apb0", apb0_p),
 	JH7110_GATE(JH7110_SYSCLK_IOMUX_APB, "u0_sys_iomux_apb",
@@ -154,6 +160,16 @@ static const struct jh7110_clk_def sys_clks[] = {
 	JH7110_GATE(JH7110_SYSCLK_GMAC1_GTXC, "gmac1_gtxc", gmac1_gtxc_p),
 	JH7110_DIV(JH7110_SYSCLK_GMAC1_RMII_RTX, "gmac1_rmii_rtx",
 	    gmac1_rmii_rtx_p, 30),
+
+	JH7110_GATE(JH7110_SYSCLK_TEMP_APB, "temp_sensor_clk_apb",
+	    temp_sensor_clk_apb_p),
+	JH7110_GATE(JH7110_SYSCLK_TEMP_CORE, "temp_sensor_clk_core",
+	    temp_sensor_clk_core_p),
+
+	JH7110_GATE(JH7110_SYSCLK_PWMDAC_APB, "pwmdac_clk_apb",
+	    pwmdac_clk_apb_p),
+	JH7110_GATEDIV(JH7110_SYSCLK_PWMDAC_CORE, "pwmdac_clk_core",
+	    pwmdac_clk_core_p, 256),
 };
 
 static int
diff --git a/sys/riscv/starfive/files.starfive b/sys/riscv/starfive/files.starfive
index 4db35e8cb351..1ecb89085ed2 100644
--- a/sys/riscv/starfive/files.starfive
+++ b/sys/riscv/starfive/files.starfive
@@ -11,4 +11,5 @@ dev/eqos/if_eqos_starfive.c		optional	eqos
 
 riscv/starfive/jh7110_gpio.c		standard
 riscv/starfive/jh7110_pcie.c		optional	pci fdt
+riscv/starfive/jh7110_temp.c		standard
 riscv/starfive/starfive_syscon.c	standard
diff --git a/sys/riscv/starfive/jh7110_temp.c b/sys/riscv/starfive/jh7110_temp.c
new file mode 100644
index 000000000000..c537f9da2e4f
--- /dev/null
+++ b/sys/riscv/starfive/jh7110_temp.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
+ * Copyright (c) 2023-2024 Jari Sihvola <jsihv@gmx.com>
+ * Copyright (c) 2026 Brian Scott <bscott@bunyatech.com.au>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/clk/clk.h>
+#include <dev/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+/* Registers */
+#define	TEMP		0x0000
+#define	TEMP_RSTN	(1 << 0)
+#define	TEMP_PD		(1 << 1)
+#define	TEMP_RUN	(1 << 2)
+#define	TEMP_DOUT_MASK	0x0fff0000
+#define	TEMP_DOUT_SHIFT	16
+
+/*
+ * These calibration numbers are based on the openbsd driver.
+ *
+ * The y intercept is -81.1C (192.05K). The slope is roughly 0.058012C/bit
+ * (expressed so integer arrithmetic can work).
+ *
+ * The result is in milliKelvin hence the numerator and offset being factored
+ * up by 1000. The 4094 looks like an error (i.e. should be 4095) but could be
+ * an artifact from the methods used to calculate the slope value.
+ */
+#define	TEMP_Y_INTERCEPT	192050
+#define	TEMP_SLOPE		237500
+#define	TEMP_RANGE		4094
+
+struct jh7110_temp_softc {
+	device_t		dev;
+	struct resource		*res;
+	struct mtx		mtx;
+	clk_t			bus;
+	hwreset_t		rst_bus;
+	clk_t			sense;
+	hwreset_t		rst_sense;
+	struct sysctl_oid	*sysctl[2];
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{ "starfive,jh7100-temp", 1 },
+	{ "starfive,jh7110-temp", 1 },
+	{ NULL, 0 }
+};
+
+static struct resource_spec jh7110_temp_spec[] = {
+	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
+	{ -1, 0 }
+};
+
+#define	RD4(sc, reg)		bus_read_4((sc)->res, (reg))
+#define	WR4(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
+
+#define	JH7110_TEMP_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	JH7110_TEMP_UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
+#define	JH7110_TEMP_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
+
+static int32_t
+jh7110_temp_get_temperature(struct jh7110_temp_softc *sc)
+{
+	int32_t value;
+
+	JH7110_TEMP_LOCK(sc);
+	value = RD4(sc, TEMP);
+	value = (value & TEMP_DOUT_MASK) >> TEMP_DOUT_SHIFT;
+	JH7110_TEMP_UNLOCK(sc);
+
+	return (value * TEMP_SLOPE) / TEMP_RANGE + TEMP_Y_INTERCEPT;
+}
+
+static int
+jh7110_temperature(SYSCTL_HANDLER_ARGS)
+{
+	struct jh7110_temp_softc *sc = arg1;
+	int val;
+	int err;
+	/* get realtime value */
+	val = jh7110_temp_get_temperature(sc);
+
+	err = sysctl_handle_int(oidp, &val, 0, req);
+	if (err != 0 || !req->newptr) /* error || read request */
+		return (err);
+
+	return (0);
+}
+
+static int
+jh7110_temp_probe(device_t dev)
+{
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	device_set_desc(dev, "StarFive JH7110 Temperature sensor");
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jh7110_temp_detach(device_t dev)
+{
+	struct jh7110_temp_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	bus_release_resources(dev, jh7110_temp_spec, &sc->res);
+	if (sc->bus != NULL)
+		clk_release(sc->bus);
+	if (sc->sense != NULL)
+		clk_release(sc->sense);
+	if (sc->rst_bus != NULL)
+		hwreset_release(sc->rst_bus);
+	if (sc->rst_sense != NULL)
+		hwreset_release(sc->rst_sense);
+	if (sc->sysctl[0] != NULL)
+		sysctl_remove_oid(sc->sysctl[0],1,0);
+	if (sc->sysctl[1] != NULL)
+		sysctl_remove_oid(sc->sysctl[1],1,0);
+
+	mtx_destroy(&sc->mtx);
+
+	return (0);
+}
+
+static int
+jh7110_temp_attach(device_t dev)
+{
+	struct jh7110_temp_softc *sc;
+	device_t cpu;
+	struct sysctl_ctx_list *ctx;
+	int rv;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+	if ((rv = bus_alloc_resources(dev, jh7110_temp_spec, &sc->res)) != 0) {
+		device_printf(dev, "Could not allocate resources: %d\n", rv);
+		mtx_destroy(&sc->mtx);
+		return (ENXIO);
+	}
+
+	/* Find the clocks and set them up */
+	if ((rv = clk_get_by_ofw_name(dev, 0, "bus", &sc->bus)) != 0) {
+		device_printf(dev, "Cannot get bus clock: %d\n", rv);
+		jh7110_temp_detach(dev);
+		return (ENXIO);
+	}
+	if ((rv = clk_enable(sc->bus)) != 0) {
+		device_printf(dev, "Could not enable bus clock %s: %d\n",
+			clk_get_name(sc->bus), rv);
+		jh7110_temp_detach(dev);
+		return (ENXIO);
+	}
+
+	if ((rv = clk_get_by_ofw_name(dev, 0, "sense", &sc->sense)) != 0) {
+		device_printf(dev, "Cannot get sense clock: %d\n", rv);
+		jh7110_temp_detach(dev);
+		return (ENXIO);
+	}
+	if ((rv = clk_enable(sc->sense)) != 0) {
+		device_printf(dev, "Could not enable sense clock %s: %d\n",
+			clk_get_name(sc->sense), rv);
+		jh7110_temp_detach(dev);
+		return (ENXIO);
+	}
+
+	/* Find the clock resets and de-assert them */
+	if ((rv = hwreset_get_by_ofw_name(dev, 0, "bus", &sc->rst_bus)) != 0) {
+		device_printf(sc->dev, "cannot get 'rst_bus' reset: %d\n", rv);
+		jh7110_temp_detach(dev);
+		return (ENXIO);
+	}
+	if ((rv = hwreset_deassert(sc->rst_bus)) != 0) {
+		device_printf(sc->dev, "cannot deassert 'bus' reset: %d\n", rv);
+		jh7110_temp_detach(dev);
+		return (ENXIO);
+	}
+	if ((rv = hwreset_get_by_ofw_name(dev, 0, "sense",
+	    &sc->rst_sense)) != 0) {
+		device_printf(sc->dev, "cannot get 'rst_sense' reset: %d\n",
+		    rv);
+		jh7110_temp_detach(dev);
+		return (ENXIO);
+        }
+	if ((rv = hwreset_deassert(sc->rst_sense)) != 0) {
+		device_printf(sc->dev, "cannot deassert 'sense' reset: %d\n",rv);
+		jh7110_temp_detach(dev);
+		return (ENXIO);
+	}
+
+	/* (uncritically) from the openbsd driver */
+
+	JH7110_TEMP_LOCK(sc);
+	/* Power down */
+	WR4(sc, TEMP, TEMP_PD);
+	DELAY(1);
+
+	/* Power up with reset asserted */
+	WR4(sc, TEMP, 0);
+	DELAY(60);
+
+	/* Deassert reset */
+	WR4(sc, TEMP, TEMP_RSTN);
+	DELAY(1);
+
+	/* Start measuring */
+	WR4(sc, TEMP, TEMP_RSTN | TEMP_RUN);
+	JH7110_TEMP_UNLOCK(sc);
+
+	/* add human readable temperature to dev.cpu node */
+	ctx = device_get_sysctl_ctx(dev);
+	sc->sysctl[0] = SYSCTL_ADD_PROC(ctx,
+	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+	    "temperature",
+	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
+	    jh7110_temperature, "IK3",
+	    "Current SoC temperature");
+	/* Make a copy where the raspberry pi puts the temperature */
+	cpu = device_lookup_by_name("cpu0");
+	if (cpu != NULL) {
+		ctx = device_get_sysctl_ctx(cpu);
+		sc->sysctl[1] = SYSCTL_ADD_PROC(ctx,
+		    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO,
+		    "temperature",
+		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
+		    jh7110_temperature, "IK3",
+		    "Current SoC temperature");
+	}
+
+	return (0);
+}
+
+static device_method_t jh7110_temp_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		jh7110_temp_probe),
+	DEVMETHOD(device_attach,	jh7110_temp_attach),
+	DEVMETHOD(device_detach,	jh7110_temp_detach),
+
+	DEVMETHOD_END
+};
+
+DEFINE_CLASS(jh7110_temp, jh7110_temp_methods, sizeof(struct jh7110_temp_softc));
+DRIVER_MODULE(jh7110_temp, simplebus, jh7110_temp_class, NULL, NULL);