Zero Client: Boot kernel and root filesystem from network with a Raspberry Pi2 or Pi3

Note: This post is 6 years old. Some information may no longer be correct or even relevant. Please, keep this in mind while reading.

A Zero Client is a computer that has nothing on its permanent storage but a bootloader. Rather, it loads everything from the network.

With the method presented in this article, you will be able to boot a Raspberry Pi into a full Debian OS with nothing more on the SD card other than the Raspberry firmware files and the u-boot bootloader on a FAT file system. The Linux kernel and the actual OS will be served over the local ethernet network.

We will only focus on the Raspberry Pi 3, but the instructions should work with minor adaptations also on a Pi 2.

The following instructions assume that you have already built…

  1. a full root file system for the Raspberry
  2. a u-boot binary, and
  3. a Linux kernel

… based on my previous blog post. Thus, you should already have the following directory structure:

~/workspace
  |- rpi23-gen-image
  |- linux
  |- u-boot
  |- raspberry-firmware

We will do all the work inside of the ~/workspace directory.

Preparation of the SD card

You will only need a small SD card with a FAT filesystem on it. The actual storage of files in the running OS will be transparently done over the network. Mount the filesystem on /mnt/sdcard and do the following:

Copy firmware

cp ./raspberry-firmware/* /mnt/sdcard

Copy u-boot bootloader

cp ./u-boot/u-boot.bin /mnt/sdcard

Create config.txt

config.txt is the configuration file read by the Raspberry firmware blobs. Most importantly, it tells the firmware what kernel to load. “Kernel” is a misleading term here, since we will boot u-boot rather than the kernel.

Create /mnt/sdcard/config.txt with the following contents:

avoid_warnings=2

# boot u-boot kernel
kernel=u-boot.bin

# run in 64bit mode
arm_control=0x200

# enable serial console
enable_uart=1

Make an universal boot script for the u-boot bootloader

To achieve maximum flexibility — to avoid the repetitive dance of manually removing the SD card, copying files to it, and re-inserting it — we will make an universal u-boot startup script that does nothing else than loading yet another u-boot script from the network. This way, there is nothing specific about the to-be-loaded Kernel or OS on the SD card at all.

Create a file boot.scr.mkimage  with the following contents:

setenv autoload no
setenv autostart no
dhcp

setenv serverip 192.168.0.250

tftp 0x100000 /netboot-${serial#}.scr

imi
source 0x100000

Replace the server IP with the actual static IP of your server. Note that this script does nothing else other than loading yet another script called netboot-${serial#}.scr  from the server. serial# is the serial number which u-boot extracts from the Raspberry Pi hardware. This is usually the ethernet network device HW address. This way, you can have separate startup scripts for several Raspberry Pi’s if you have more than one. To keep the setup simple, set the file name to something predictable.

Compile the script into an u-boot readable image:

./u-boot/tools/mkimage -A arm64 -O linux -T script \
-C none -a 0x00 -e 0x00 \
-d boot.scr.mkimage \
boot.scr

Copy boot.scr to the SD card:

cp boot.scr /mnt/sdcard

The SD card preparation is complete at this point. We will now focus on the serving of the files necessary for boot.

Preparation of the file server

Do all of the following as ‘root’ user on a regular PC running Debian 9 (“Stretch”). This PC will act as the “server”.  This server will serve the files necessary to network-boot the Raspberry.

The directory /srv/tftp will hold …

  • an u-boot start script file
  • the kernel uImage file
  • and the binary device tree file.

… to be served by a TFTP server.

mkdir /srv/tftp

The directory /srv/rootfs_rpi3 will hold our entire root file system to be served by a NFS server:

mkdir /srv/rootfs_rpi3

You will find installation instructions of both TFTP and NFS servers further down.

Serve the root file system

Let’s copy the pre-built root file system into the directory from where it will be served by the NFS server:

rsync -a ./rpi23-gen-image/images/stretch/build/chroot/ /srv/rootfs_rpi3

(notice the slash at the end of the source directory)

Fix the root file system for network booting

Edit /srv/rootfs_rpi3/etc/fstab  and comment out all lines. We don’t need to mount anything from the SD card.

When network-booting the Linux kernel, the kernel will configure the network device for us (either with a static IP or DHCP). Any userspace programs attempting to re-configure the network device will cause problems, i.e. a loss of conncection to the NFS server. Thus, we need to prevent systemd-networkd from managing the Ethernet device. Make the device unmanaged by removing the folowing ethernet configuration file:

rm /srv/rootfs_rpi3/etc/systemd/network/eth.network

If you don’t do that, you’ll get the following kernel message during boot:

nfs: server not responding, still trying

That is because systemd has shut down and then re-started the ethernet device. Apparently NFS transfers are sensitive to that.

In case you want to log into the chroot to make additional changes that can only be done from within (e.g. running systemctl scripts etc.), you can do:

cp /usr/bin/qemu-aarch64-static /srv/rpi3fs/usr/bin
LANG=C LC_ALL=C chroot /srv/rpi3fs

Serve Kernel uImage

In this step, we create a Linux kernel uImage that can be directly read by the u-boot bootloader. We read Image.gz directly from the Kernel source directory, and output it into the /srv/tftp directory where a TFTP server will serve it to the Raspberry:

./u-boot/tools/mkimage -A arm64 -O linux -T kernel \
-C gzip -a 0x80000 -e 0x80000 \
-d ./linux/arch/arm64/boot/Image.gz \
/srv/tftp/linux-rpi3.uImage

Serve device tree binary

The u-boot bootloader will also need to load the device tree binary and pass it to the Linux kernel, so copy that too into the /srv/tftp directory.

cp ./linux/arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dtb /srv/tftp/

Serve secondary u-boot script loading the kernel

Create a file netboot-rpi3.scr.mkimage with the following contents:

setenv autoload no
setenv autostart no
dhcp

setenv serverip 192.168.0.250

setenv bootargs "earlyprintk console=tty1 dwc_otg.lpm_enable=0 root=/dev/nfs rw rootfstype=nfs nfsroot=192.168.0.250:/srv/rpi3fs,udp,vers=3 ip=dhcp nfsrootdebug smsc95xx.turbo_mode=N elevator=deadline rootdelay cma=256M@512M net.ifnames=1 init=/bin/systemd loglevel=7 systemd.log_level=debug systemd.log_target=console"

tftp ${kernel_addr_r} linux-rpi3.uImage
tftp ${fdt_addr_r} bcm2837-rpi-3-b.dtb
bootm ${kernel_addr_r} - ${fdt_addr_r}

Replace the server IP with the static IP of your server PC. Then compile this script into an u-boot readable image and output it directly to the /srv/tftp directory:

./u-boot/tools/mkimage -A arm64 -O linux -T script \
-C none -a 0x00 -e 0x00 \
-d netboot-rpi3.scr.mkimage \
/srv/tftp/netboot-0000000012345678.scr

Make sure that the filename of the .scr file matches with whatever file name you’ve set in the universal .scr script that we’ve prepared further above.

Install a NFS server

The NFS server will serve the root file system to the Raspberry and provide transparent storage.

apt-get install nfs-kernel-server

Edit /etc/exports and add:

/srv/rootfs_rpi3  *(rw,sync,no_root_squash,no_subtree_check,insecure)

To apply the changed ‘exports’ configuration, run

exportfs -rv

Useful to know about the NFS server:

You can restart the NFS server by running service nfs-kernel-server restart

Configuration files are /etc/default/nfs-kernel-server  and /etc/default/nfs-common

Test NFS server

If you want to be sure that the NFS server works correctly, do the following on another PC:

apt-get install nfs-common

Mount the root file system (fix the static IP for your server):

mkdir /tmp/testmount
mount 192.168.0.250:/srv/rootfs_rpi3 /tmp/testmount
ls -al /tmp/testmount

Install a TFTP server

To install:

apt-get install tftpd-hpa

After installation, check if the TFTP server is running:

ps -ejHf | grep ftp

This command will tell you the default serving directory (/srv/tftp):

/usr/sbin/in.tftpd --listen --user tftp --address 0.0.0.0:69 --secure /srv/tftp

Here is another command that tells you if the TFTP server is listening:

netstat -l -u | grep ftp

To get help about this server: man tftpd

Test TFTP

If you want to be sure that the TFTP server works correctly, do the following on another PC:

apt-get install tftp-hpa

Then see if the server serves the Linux kernel we’ve installed before:

tftp 192.168.0.250
tftp> get linux-rpi3.uImage
tftp> quit

You now should have a local copy of the linux-rpi3.uImage file.

Completion

If you’ve done all of the above correctly, you can insert the prepared SD card into your Raspberry Pi and reboot it. The following will happen:

  1. The Raspberry Pi GPU will load the firmware blobs from the SD card.
  2. The firmware blobs will boot the image specified in config.txt. In our case, this is the u-boot binary on the SD card.
  3. The u-boot bootloader will boot.
  4. The u-boot bootloader loads and runs the universal boot.scr script from the SD card.
  5. The boot.scr downloads the specified secondary boot script from the network and runs it.
  6. The secondary boot script …
    • downloads the device tree binary from the network and loads it into memory.
    • downloads the Linux kernel from the network and loads it into memory
    • passes the device tree binary to the kernel, and boots the kernel
  7. the Linux kernel will bring up the ethernet device, connect to the NFS server, and load the regular OS from there.

Many things can go wrong in this rather long sequence, so if you run into trouble, check the Raspberry boot messages output on an attached screen or serial console, and the log files of the NFS and TFTP servers on your server PC.

Resources

https://www.raspberrypi.org/documentation/linux/kernel/building.md

http://www.whaleblubber.ca/boot-raspberry-pi-nfs/

https://cellux.github.io/articles/moving-to-nfs-root/

http://billauer.co.il/blog/2011/01/diskless-boot-nfs-cobbler/

https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt

http://wiki.linux-nfs.org/wiki/index.php/General_troubleshooting_recommendations

https://wiki.archlinux.org/index.php/NFS

How to turn the Raspberry Pi into a Gateway to mobile phone internet

Note: This post is 6 years old. Some information may no longer be correct or even relevant. Please, keep this in mind while reading.

Your DSL internet connection is too slow? Want to set up an improvised office? You do not want to pay for a DSL internet plan when you already have a fast 4G mobile plan? If yes to one of the above, it is quite easy to configure a Raspberry Pi to share one mobile internet connection to an Ethernet network.

Strictly speaking, you don’t have to use a Raspberry Pi to do this. A laptop or desktop computer with any Operating System would work too, but the Raspberry is so small and consumes only 2-3 W of electrical power, and is so cool (quite literally!), so will will make use of this awesomeness!

Prerequisites

The following step-by-step guide is based on a pure Debian 9 (“Stretch”) distribution with a mainline/vanilla/unpatched Linux kernel built according to my previous blog post:

https://blog.michael.franzl.name/2016/10/31/raspberry-pi-debian-stretch/

  • We will not focus on the Raspbian OS nor on any other distribution, because documentation for these other setups exists in abundance.
  • You should not have a graphical interface installed. GUIs also install the NetworkManager service for systemd (Debian package “network-manager”), and I have not tested how NetworkManager interacts with the methods presented below. In addition, a bare-bone system is the preferred choice because it saves RAM and CPU resources.
  • In any case, you should attach a keyboard and screen to the Raspberry because you may temporarily lose network connectivity during the setup.
  • You also need a smart phone with an internet plan, supporting USB tethering. I have only tested recent Android based smartphones. Keep in mind during the following steps that, with most smart phones, you need to re-enable USB tethering after reboots or USB cable reconnects.

Goals

  • Computers in the LAN will be able to set the Raspberry Pi’s static IP address as internet Gateway and DNS server.
  • The Raspberry Pi will prefer a smart phone connection (tethered USB) to forward traffic.
  • If the smart phone is disconnected, the Rasbperry Pi will automatically fall back to an already existing gateway if present (i.e. a DSL modem)

Step 1: Install a DNS server

This ensures that cached DNS lookups are very fast when a DNS query has already been fetched.

apt-get install bind9

Tell “bind” to use Google’s public DNS servers (they are good). Edit /etc/bind/named.conf.options and change the “forward” block to:

forwarders {
    8.8.8.8;
    8.8.4.4;
};

Restart “bind”:

systemctl restart bind9

Step 2: Configure a static IP address for the Ethernet adapter

If you already have a DHCP server running in your local network (we will use the subnet 192.168.0.0 in this guide), give the Raspberry Pi a free static IP address in this existing subnet, e.g. 192.168.0.250.

If you don’t have an existing DHCP server running in your local network, we will set one up on the Raspberry (see Step 8 below).

In both cases, we will give our Rasberry the static IP address 192.168.0.250. Using systemd, change the config file of your ethernet connection /etc/systemd/network/eth.network:

[Match]
Name=eth0

[Network]
Address=192.168.0.250/24

If your LAN already has an internet gateway, e.g. a DSL modem with address 192.168.0.1, add the following (optional) section to the same config file:

[Route]
Gateway=192.168.0.1
Metric=9000

The large positive integer value of “Metric” ensures that other configured gateways with a lower Metric will be preferred. This will come in handy in the next step where the smart phone will be our preferred gateway with a Metric value of 1024.

Now reboot the Raspberry or run systemctl restart systemd-networkd.  You may lose network connectivity at this point if you are logged in via ssh.

Now, check that networkctl status eth0 matches our wanted static IP address:

Address: 192.168.0.250

Next, check the output of route -n (the kernel routing table). It should show:

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface 
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0

If you have added the optional [Route] section, you should also see the following as first line, which is our current default route to the internet:

0.0.0.0         192.168.0.1     0.0.0.0         UG    9000   0        0 eth0

Step 3: Set the smart phone connection as gateway

Plug in your phone’s USB cable into one of the Raspberry’s USB connectors. Then turn on USB tethering in the Settings UI of your smart phone.

Run networkctl. You should see the following entry amongst the other network connections (notice “off” and “unmanaged”).

usb0             ether              off         unmanaged

To have the “systemd-networkd” service manage the “usb0” network device, create a file /etc/systemd/network/mobile.network with the following contents:

[Match] 
Name=usb0 

[Network] 
DHCP=yes

To apply this config file, run systemctl restart systemd-networkd .  After a few seconds, networkctl should output (notice the “routable” and “configured” parts):

3 usb0             ether              routable         configured

You also can check networkctl status usb0  to see the dynamic IP address obtained from the DHCP server on the smart phone. For Android phones this is usually in the subnet 42.

Next, check the output of route -n. Now, the phone connection “usb0” should be on the top of the list thanks to the lower metric of 1024:

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.42.129  0.0.0.0         UG    1024   0        0 usb0
0.0.0.0         192.168.0.1     0.0.0.0         UG    9000   0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.42.0    0.0.0.0         255.255.255.0   U     0      0        0 usb0
192.168.42.129  0.0.0.0         255.255.255.255 UH    1024   0        0 usb0

Step 4: Check internet connectivity

With this routing table, we already can connect to the internet via the smart phone. To make sure that we are routed via the smart phone, we will ask the Linux kernel which gateway it would take first for traffic. ip route get 8.8.8.8  should ouput the IP address of the smart phone (192.168.42.129, subnet 42):

8.8.8.8 via 192.168.42.129 dev usb0 src 192.168.42.19

Let’s ping Google’s server a few times: ping 8.8.8.8  to see if we have an actual working route to the internet:

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 
64 bytes from 8.8.8.8: icmp_seq=1 ttl=51 time=1742 ms 
64 bytes from 8.8.8.8: icmp_seq=2 ttl=51 time=699 ms

The answer: Yes!

Check phone’s DNS server

Now let’s check if the phone’s DNS server is working. Type dig google.com (install Debian package “dnsutils” if not yet installed), and make sure that you’ve got an “ANSWER SECTION”:

;; ANSWER SECTION:
google.com.     2      IN      A  216.58.204.110

;; Query time: 567 msec
;; SERVER: 192.168.42.129#53(192.168.42.129)

Note that the response came from the phone’s IP. So, “systemd” has correctly configured the phone’s IP address as DNS server for the Raspberry (that information came from the phone’s DHCP server).

Run dig google.com again. This time the result should be cached and returned much faster (just 1ms):

...
;; Query time: 1 msec

Check local DNS server

Type dig @localhost google.com:

;; ANSWER SECTION:
google.com.     2      IN      A  216.58.204.110

;; Query time: 567 msec
;; SERVER: ::1#53(::1)

Note that this time, the response came from the “bind” DNS server which we have installed in Step 1. It, in turn, forwards queries via the phone connection. This server will be used for all requests via Ethernet.

Step 5: Turn on IP protocol forwarding for the Linux kernel

By default, this feature is turned off. Check the current status of this feature:

sysctl -a | grep net\.ipv4\.ip_forward  will output:

net.ipv4.ip_forward = 0

To permanently set this variable to 1, create /etc/sysctl.d/30-ipforward.conf and add the following:

net.ipv4.ip_forward=1

Reload all settings by typing sysctl --system. Now, and also after a reboot, the “ip_forward” variable should stay enabled.

Step 6: Turn on Network address translation (NAT) aka. “Masquerading” between Ethernet and USB Smart Phone network links

Create a shell script /usr/bin/startgateway.sh with the following contents and make it executable (chmod a+x):

#!/bin/sh
iptables -t nat -A POSTROUTING -o usb0 -j MASQUERADE
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eth0 -o usb0 -j ACCEPT

This will masquerade IP packets coming in through the Ethernet adapter as if they were coming from the Raspberry itself, forward them to the USB smart phone connection, and the incoming answers (from remote servers) will be re-written and forwarded back to whereever in the LAN they came from. That is the central purpose of the problem we’re trying to solve in this tutorial.

Run this script. Check the output of iptables -L -n -v:

Chain INPUT (policy ACCEPT 63 packets, 6443 bytes) 
pkts bytes target     prot opt in     out     source               destination          

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) 
pkts bytes target     prot opt in     out     source               destination          
   0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED 
   0     0 ACCEPT     all  --  eth0 usb0      0.0.0.0/0            0.0.0.0/0            

Chain OUTPUT (policy ACCEPT 23 packets, 3540 bytes) 
pkts bytes target     prot opt in     out     source               destination

To run this shell script at system boot, right after the network links have been brought up, create the following systemd service file:

/etc/systemd/system/multi-user.target.wants/startgateway.service

Add the following:

[Unit]
Description=Start Gateway
After=network-online.target
Requires=network-online.target

[Service]
Type=[oneshot]
ExecStart=/usr/bin/startgateway.sh

[Install]
WantedBy=multi-user.target

Step 7: Test the Raspberry Gateway!

On another machine in your LAN (can be Linux, Windows or Mac), configure the Ethernet connection manually. Set the following:

  • Static IP Address: 192.168.0.10 (or any other freely available address on this subnet)
  • Gateway: 192.168.0.250
  • DNS: 192.168.0.250

Then run traceroute 8.8.8.8  on that other machine. Truncated output:

1  gateway (192.168.0.250)
2  192.168.42.129

The route is correctly resolved. First traffic goes to the Raspberry Pi, then to the smart phone, and from there to the internet.

If you can’t run traceroute on that other machine, using a regular browser to browse the internet should work at this point!

Step 8: Running a DHCP server on the Raspberry

TODO

Conclusion

This tutorial may seem long, but the commands are few, and with a bit of practice you can turn your Raspberry Pi into a mobile phone Gateway in 10 minutes to enjoy faster 4G internet when your other modems are too slow.

Reading Raspberry Pi chip temperature with mainline Linux kernel

Note: This post is 7 years old. Some information may no longer be correct or even relevant. Please, keep this in mind while reading.

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. In this article, we will read the Raspberry Pi chip temperature. Here is my previous article:

https://blog.michael.franzl.name/2016/10/31/raspberry-pi-debian-stretch/

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 <office@blog.michael.franzl.name>
Tested-by: Michael Franzl <office@blog.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 {
 			compatible = "raspberrypi,bcm2835-power";
 			firmware = <&firmware>;
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;
+	} packet;
+	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