Setting I2C bus speed on a Raspberry Pi via Device Tree

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 set the I2C bus speed on a Raspberry Pi using the Linux kernel.

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

Device Trees

The I2C bus on the Broadcom BCM283x chips found on Raspberry Pi’s is well and directly supported by the mainline/vanilla Linux kernel. Since with the Raspberry Pi we’re dealing with a System on a Chip (SoC), and not a regular PC, the hardware is configured with so-called device trees, which is a low-level description of the chip hardware compiled from text into binary format.

The rpi23-gen-image script mentioned in my previous tutorial installs the binary device tree into /boot/firmware/bcm2836-rpi-2-b.dtb. The U-Boot bootloader can read this file and pass it to the Linux kernel which interprets it and enables all the mentioned features in it.

The clock frequency for the I2C bus is configured in this .dtb file, and the default is 100kHz. There is a tool which allows you to inspect the .dtb file, outputting regular text. With this tool you also can make changes to the device configuration. Nowadays, this is the proper way to configure low-level devices on SoC’s!

Read the device tree

apt-get install device-tree-compiler
fdtdump /boot/firmware/bcm2836-rpi-2-b.dtb

This will output the decoded device tree as text. Regarding I2C, you will find i2c@-entries like this:

i2c@7e205000 {
    compatible = "brcm,bcm2835-i2c";
    reg = <0x7e205000 0x0000005c>;
    interrupts = <0x00000002 0x000000e9>;
    clocks = <0x00000008 0x00000022>;
    #address-cells = <0x00000001>;
    #size-cells = <0x00000000>;
    status = "okay";
    clock-frequency = <0x000186a0>;
};
i2c@7e804000 {
    compatible = "brcm,bcm2835-i2c";
    reg = <0x7e804000 0x0000005c>;
    interrupts = <0x00000002 0x000000e9>;
    clocks = <0x00000008 0x00000022>;
    #address-cells = <0x00000001>;
    #size-cells = <0x00000000>;
    status = "okay";
    clock-frequency = <0x000186a0>;
};

Change the device tree

The clock-frequency value is what we want to change. The value is a raw binary unsigned 32-bit int stored big-endian, unreadable for humans.

But you can use another tool fdtget to read just this value decoded:

fdtget /boot/firmware/bcm2836-rpi-2-b.dtb /soc/i2c@7e205000 clock-frequency

This will output 100000.

In my case, I wanted to set the I2C bus to the slowest frequency, to compensate for long cable lengths. I found that one of the lowest supported I2C clock frequencies is 4kHz. With fdtput you can set the clock-frequency property for each i2c device (there are 3 on the RPi):

fdtput --type u /boot/firmware/bcm2836-rpi-2-b.dtb /soc/i2c@7e205000 clock-frequency 4000

And that’s it. You have to reboot for these settings to take effect.

I used an oscilloscope to verify that the SCL pin of the GPIO of the RPi indeed toggled with 4kHz, and it did!

If you found a mistake in this blog post, or would like to suggest an improvement to this blog post, please me an e-mail to michael@franzl.name; as subject please use the prefix "Comment to blog post" and append the post title.
 
Copyright © 2023 Michael Franzl