Reading Raspberry Pi chip temperature with mainline Linux kernel

This blog post was published 8 years ago and may or may not have aged well. While reading please keep in mind that it may no longer be accurate or even relevant.

In this article, we will read the Raspberry Pi chip temperature.

This tutorial is based on a previous article where we installed pure Debian 9 with a recent mainline/vanilla Linux kernel on a Raspberry Pi, and so differs from what would be done on a Raspbian Distribution with a Raspbian kernel.

As of mainline Linux Kernel 4.9.0-rc3, the sysfs entry /sys/class/thermal for the Broadcom BCM283x chip found on Raspberry Pi’s is empty. You can apply the following patch to Linux kernel 4.9.0-rc3, even though it will soon be superfluous because it seems that currently there is ongoing work by Linux Kernel developers to add in the missing functionalities.

The patch, which will get you the standard sysfs temperature node which you can read like this:

cat /sys/class/thermal/thermal_zone0/temp
40084

This is the chip temperature in thousandths of degrees cenigrade, i.e. 40.084 degrees Celsius.

The following patch will give you a new entry in the mainline Kernel config, under Drivers -> SoC -> BCM -> Raspberry Pi thermal sysfs driver, which you have to enable, then recompile your kernel.

I have actually submitted this patch to a Linux kernel developer, but the process to get code into the Linux kernel is quite elaborate, and he said that they are already working on it, so I let it drop and decided to write this blog post instead.

Copied drivers/thermal/bcm2835-thermal.c from Raspberry Pi
Foundation Linux kernel to drivers/soc/bcm/raspberry-thermal.c
and added respective entry to the device tree.

Tested and confirmed working on a Raspberry Pi 2 Model B V1.1 2014.

Signed-off-by: Michael Franzl <public@michael.franzl.name>
Tested-by: Michael Franzl <public@michael.franzl.name>

 arch/arm/boot/dts/bcm2835-rpi.dtsi    |   5 ++
 arch/arm/configs/multi\_v7\_defconfig   |   1 +
 drivers/soc/bcm/Kconfig               |   9 +++
 drivers/soc/bcm/Makefile              |   1 +
 drivers/soc/bcm/raspberrypi-thermal.c | 109 ++++++++++++++++++++++++++++++++++
 5 files changed, 125 insertions(+)
 create mode 100644 drivers/soc/bcm/raspberrypi-thermal.c

diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index e9b47b2..389ee83 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -22,6 +22,11 @@
 			mboxes = <&mailbox>;
 		}+		thermal: thermal {
+			compatible = "raspberrypi,bcm2835-thermal";
+			firmware = <&firmware>;
+		}
+
 		power: power {diff --git a/arch/arm/configs/multi\_v7\_defconfig b/arch/arm/configs/multi\_v7\_defconfig
index 11f37ed..1368e63 100644
--- a/arch/arm/configs/multi\_v7\_defconfig
+++ b/arch/arm/configs/multi\_v7\_defconfig
@@ -838,6 +838,7 @@ CONFIG\_XILINX\_XADC=y
 CONFIG\_CM36651=m
 CONFIG\_AK8975=y
 CONFIG\_RASPBERRYPI\_POWER=y
+CONFIG\_RASPBERRYPI\_THERMAL=y
 CONFIG\_PWM=y
 CONFIG\_PWM\_ATMEL=m
 CONFIG\_PWM\_ATMEL\_HLCDC\_PWM=m
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig
index a39b0d5..c951de4 100644
--- a/drivers/soc/bcm/Kconfig
+++ b/drivers/soc/bcm/Kconfig
@@ -20,4 +20,13 @@ config SOC\_BRCMSTB
 
 	  If unsure, say N.
 
+config RASPBERRYPI\_THERMAL
+	tristate "Raspberry Pi thermal sysfs driver"
+	depends on ARCH\_BCM2835 || (COMPILE\_TEST && OF)
+	depends on RASPBERRYPI\_FIRMWARE=y
+	depends on THERMAL
+	help
+	  This enables a thermal sysfs driver for the thermal sensor on
+	  Broadcom283x chips found on a Raspberry Pi.
+
 endmenu
diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile
index dc4fced..41734e1 100644
--- a/drivers/soc/bcm/Makefile
+++ b/drivers/soc/bcm/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG\_RASPBERRYPI\_POWER)	+= raspberrypi-power.o
 obj-$(CONFIG\_SOC\_BRCMSTB)	+= brcmstb/
+obj-$(CONFIG\_RASPBERRYPI\_THERMAL)	+= raspberrypi-thermal.o
diff --git a/drivers/soc/bcm/raspberrypi-thermal.c b/drivers/soc/bcm/raspberrypi-thermal.c
new file mode 100644
index 0000000..34a829f
--- /dev/null
+++ b/drivers/soc/bcm/raspberrypi-thermal.c
@@ -0,0 +1,109 @@
+/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\* Copyright 2011 Broadcom Corporation.  All rights reserved.
+\*
+\* Unless you and Broadcom execute a separate written software license
+\* agreement governing use of this software, this software is licensed to you
+\* under the terms of the GNU General Public License version 2, available at
+\* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+\*
+\* Notwithstanding the above, under no circumstances may you combine this
+\* software in any way with any other Broadcom software provided under a
+\* license other than the GPL, without Broadcom's express prior written
+\* consent.
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
+
+#include <linux/module.h>
+#include <linux/platform\_device.h>
+#include <linux/thermal.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+static int bcm2835\_thermal\_get\_property(struct thermal\_zone\_device \*tz,
+					int \*temp, u32 tag)
+{
+	struct rpi\_firmware \*fw = tz->devdata;
+	struct {
+		u32 id;
+		u32 val;
+	}
+	int ret;
+
+	\*temp = 0;
+	packet.id = 0;
+	ret = rpi\_firmware\_property(fw, tag, &packet, sizeof(packet));
+	if (ret) {
+		dev\_err(&tz->device, "Failed to get temperature\\n");
+		return ret;
+	}
+
+	\*temp = packet.val;
+	dev\_dbg(&tz->device, "%stemp=%d\\n",
+		tag == RPI\_FIRMWARE\_GET\_MAX\_TEMPERATURE ? "max" : "", \*temp);
+
+	return 0;
+}
+
+static int bcm2835\_thermal\_get\_temp(struct thermal\_zone\_device \*tz,
+				    int \*temp)
+{
+	return bcm2835\_thermal\_get\_property(tz, temp,
+					    RPI\_FIRMWARE\_GET\_TEMPERATURE);
+}
+
+static struct thermal\_zone\_device\_ops ops  = {
+	.get\_temp = bcm2835\_thermal\_get\_temp,
+}
+
+static int bcm2835\_thermal\_probe(struct platform\_device \*pdev)
+{
+	struct device\_node \*fw\_np;
+	struct rpi\_firmware \*fw;
+	struct thermal\_zone\_device \*tz;
+
+	fw\_np = of\_parse\_phandle(pdev->dev.of\_node, "firmware", 0);
+	if (!fw\_np) {
+		dev\_err(&pdev->dev, "Missing firmware node\\n");
+		return -ENOENT;
+	}
+	fw = rpi\_firmware\_get(fw\_np);
+	if (!fw)
+		return -EPROBE\_DEFER;
+
+	tz = thermal\_zone\_device\_register("bcm2835\_thermal", 0, 0, fw, &ops,
+					  NULL, 0, 0);
+	if (IS\_ERR(tz)) {
+		dev\_err(&pdev->dev, "Failed to register the thermal device\\n");
+		return PTR\_ERR(tz);
+	}
+
+	platform\_set\_drvdata(pdev, tz);
+
+	return 0;
+}
+
+static int bcm2835\_thermal\_remove(struct platform\_device \*pdev)
+{
+	thermal\_zone\_device\_unregister(platform\_get\_drvdata(pdev));
+
+	return 0;
+}
+
+static const struct of\_device\_id bcm2835\_thermal\_of\_match\_table\[\] = {
+	{ .compatible = "raspberrypi,bcm2835-thermal", }
+	{}
+}
+MODULE\_DEVICE\_TABLE(of, bcm2835\_thermal\_of\_match\_table);
+
+static struct platform\_driver bcm2835\_thermal\_driver = {
+	.probe = bcm2835\_thermal\_probe,
+	.remove = bcm2835\_thermal\_remove,
+	.driver = {
+		.name = "bcm2835\_thermal",
+		.of\_match\_table = bcm2835\_thermal\_of\_match\_table,
+	}
+}
+module\_platform\_driver(bcm2835\_thermal\_driver);
+
+MODULE\_AUTHOR("Dorian Peake");
+MODULE\_AUTHOR("Noralf Trønnes");
+MODULE\_DESCRIPTION("Thermal driver for bcm2835 chip");
+MODULE\_LICENSE("GPL");
-- 
2.1.4
If you think you found a mistake in this blog post, or would like to suggest an improvement to this blog post, you can write me an e-mail to the address public dot michael at franzl dot name; as subject please use the prefix "Comment to blog post" and append the post title.
 
Copyright © 2023 Michael Franzl