Intel Edison: i2c-6 with vanilla Linux

2017-10-14

Thanks to the amazing work of Andy Shevchenko, it’s possible to run recent vanilla Linux kernels on Intel’s discontinued Edison module. There is a nice writeup about this available on the web. One of the drawbacks of this approach, however, is that one of the two user-accessible I2C bus controllers is not available: i2c-6. This is due to the fact, that initially it’s configured to be used by the SCU and there is no way to change this.

In order to use i2c-6, a pinctrl setting needs to be applied. This can be done using a pinctrl mapping in the board specific initialization code. That makes the pinctrl setting to be applied once the device in question (here i2c-6) is probed. When I did this, however, I could see that the pinmux was claimed by the i2c-6 device but the pins’ mode was still set to 2 which is wrong:

$ mount -t debugfs non /sys/kernel/debug/
$ cat /sys/kernel/debug/pinctrl/pinctrl-merrifield/pinmux-pins | grep I2C_6
pin 111 (GP27_I2C_6_SCL): 0000:00:09.1 (GPIO UNCLAIMED) function i2c6 group i2c6_grp
pin 112 (GP28_I2C_6_SDA): 0000:00:09.1 (GPIO UNCLAIMED) function i2c6 group i2c6_grp
$ cat /sys/kernel/debug/pinctrl/pinctrl-merrifield/pins | grep I2C_6
pin 111 (GP27_I2C_6_SCL) mode 2 0x00203512
pin 112 (GP28_I2C_6_SDA) mode 2 0x00203592

So I dug a little into the code and looked at the function mrfld_pinmux_set_mux in particular. The pinctrl settings for the pins in question are write-protected, which means that changing them requires an SCU IPC call, see mrfld_update_phys. That IPC call, however, returned -ENODEV because the SCU device is not probed by the time i2c-6 is probed which in turn triggers setting the pinmux.

Both the I2C controller and the SCU are PCI devices with vendor/device ID equal to 8086:1196 and 8086:11a0, respectively. Typically, the PCI bus is enumerated in order, which means that the I2C bus devices will be probed before the SCU device since the device ID is smaller. To circumvent this, I configured the I2C bus driver to be built as lodable kernel modules (CONFIG_I2C_DESIGNWARE_CORE=m and CONFIG_I2C_DESIGNWARE_PCI=m).

After recompiling the kernel, the LKMs can be inserted using modprobe or insmod. Not only will this succeed now, but the pins’ mode will change as well:

$ cat /sys/kernel/debug/pinctrl/pinctrl-merrifield/pins | grep I2C_6
pin 111 (GP27_I2C_6_SCL) mode 1 0x00000511
pin 112 (GP28_I2C_6_SDA) mode 1 0x00000591

The changes that were required in the Linux kernel can be found in my GitHub repo.