0%

How to Manually Switch PCA954X I2C Mux Channels

The PCA954X is a family of I2C multiplexers/switches that allow you to control multiple I2C devices using a single I2C bus. In this section, we will explore how to manually switch between different channels of the PCA954X I2C mux.

Using the i2c-tools

The i2c-tools package provides a set of command-line tools for interacting with I2C devices in Linux. We will be using three main tools: i2cdetect, i2cdump, and i2cset. Make sure you have the i2c-tools package installed on your system before proceeding.

i2cdetect

The i2cdetect tool allows you to scan the I2C bus and detect devices connected to it. It provides a grid view with addresses and corresponding symbols indicating the presence of I2C devices. Here’s an example output:

1
2
3
4
5
6
7
8
9
10
root@albertlin:~# i2cdetect -y 4
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: UU -- -- -- -- -- 16 -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

The symbols used in the output have the following meanings:

  • “–”: The address was probed, but no chip responded.
  • “UU”: Probing was skipped because this address is currently in use by a driver, suggesting the presence of a chip.
  • Hexadecimal address: Indicates the presence of a chip at that address.

i2cdump

The i2cdump tool allows you to read and display the content of registers on an I2C device. You need to provide the bus number and the address of the device you want to dump. Here’s an example usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@albertlin:~# i2cdump -y 4 0x16
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................

This output displays the content of the registers starting from address 0x00.

i2cset

The i2cset tool allows you to set the content of registers on an I2C device. You need to provide the bus number, the address of the device, and the data you want to write. Here’s an example usage:

1
root@albertlin:~# i2cset -y 4 0x70 0x00 0x04

In this example, we are writing the value 0x04 to register address 0x00 of the device at address 0x70 on bus 4.

PCA954X I2C Mux

The PCA954X is a family of I2C multiplexers/switches. You can find the datasheet for PCA9545A/B/C here.

Changing DTS through Yocto

If you’re using Yocto to build your Linux image, you can customize the Linux kernel configuration by following these steps:

  1. Open a terminal and navigate to your Yocto build directory.

  2. Run the following command to enter the Linux kernel menuconfig:

1
bitbake -c menuconfig virtual/kernel
  1. Make the necessary changes in the menuconfig interface. Once you’re

done, save and exit.

  1. Additionally, you can generate a defconfig file by running the following command:
1
bitbake -c savedefconfig virtual/kernel

Writing I2C Mux Device Tree Bindings

To configure the PCA954X I2C mux in the device tree, you can refer to the following documentation:

You can also check out an example of using the PCA954X I2C mux in the device tree in the aspeed-bmc-facebook-minipack.dts file.

Using new_device and delete_device

To add or remove I2C devices dynamically, you can use the new_device and delete_device attributes in the sysfs interface. Here’s an example:

1
2
3
4
5
6
7
root@albertlin:/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a300.i2c-bus/i2c-5/i2c-16/i2c-20# ls
20-0049 20-0056 delete_device i2c-dev mux_device name new_device of_node power subsystem uevent
root@albertlin:/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a300.i2c-bus/i2c-5/i2c-16/i2c-20# echo "0x0049" > delete_device
[ 424.372524] i2c i2c-20: delete_device: Deleting device tmp75 at 0x49
root@albertlin:/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a300.i2c-bus/i2c-5/i2c-16/i2c-20# echo "tmp75 0x49" > new_device
[825617.903108] lm75 20-0049: hwmon2: sensor 'tmp75'
[825617.908643] i2c i2c-20: new_device: Instantiated device tmp75 at 0x49

In the above example, delete_device is used to remove the device with address 0x49, and new_device is used to add a device named “tmp75” at address 0x49.

Reading Kernel Configuration at Runtime

To view the kernel configuration at runtime, you can use the following command:

1
zcat /proc/config.gz

This command will display the configuration options of the running kernel.

Switching PCA954X Channels

To manually switch the channel of an I2C mux, you can use the i2cset command with the appropriate parameters. Here’s an example:

1
2
3
i2cset -y 5 0x70 0x01  # Switch to channel 0
i2cset -y 5 0x70 0x02 # Switch to channel 1
i2cset -y 5 0x70 0x04 # Switch to channel 2

In the above commands, -y 5 specifies the I2C bus number (5 in this case), 0x70 is the address of the I2C mux device, and 0x01, 0x02, and 0x04 are the channel values representing channel 0, channel 1, and channel 2, respectively.

Here’s an example of how you can use these commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
root@albertlin:~# i2cset -y 5 0x70 1  # Switch to channel 0
root@albertlin:~# i2cdetect -y 5 # Scan for devices on the I2C bus
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- UU -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 71 -- -- -- -- -- --
root@albertlin:~# i2cset -y 5 0x71 0 # Switch to channel 1
root@albertlin:~# i2cdetect -y 5 # Scan for devices on the I2C bus
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- UU -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- 49 -- -- -- -- -- --
50: -- -- -- -- -- -- 56 -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 71 -- -- -- -- -- --
root@albertlin:~# i2cset -y 5 0x71 2 # Switch to channel 2
root@albertlin:~# i2cdetect -y 5 # Scan for devices on the I2C bus
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- UU -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1f
20: -- -- -- -- -- -- -- -- -- -- -- -- --

-- -- --
30: -- -- 32 -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- 61 -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 71 -- -- -- -- -- --

In the above example, we first switch to channel 0 by using i2cset -y 5 0x70 1, then we scan for devices on the I2C bus using i2cdetect -y 5. After that, we switch to channel 1 with i2cset -y 5 0x71 0 and perform another scan using i2cdetect -y 5. Finally, we switch to channel 2 with i2cset -y 5 0x71 2 and perform a scan again.