git: fa28ede31dac - main - jh7110_temp: CPU temperature sensor for JH7110
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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);