Plebian-Linux / quartz64-images

GitHub Actions Repository for automatically generated images for the Quartz64 family of single board computers
https://plebian.org
GNU General Public License v3.0
41 stars 10 forks source link

I2C? #34

Closed hughsheehy closed 1 year ago

hughsheehy commented 1 year ago

With gerriko having solved the SPI problem - has anyone made similar progress on I2C?

Starting to read the kernel/overlays/etc.

But it's not something I'm familiar with.

CounterPillow commented 1 year ago

Hi,

on SOQuartz you'll have several choices of I2C buses to activate:

here's an example of how to enable I2C2 with pinmux M1 (I didn't test this on actual hardware, just test compiled):

/dts-v1/;
/plugin/;           /* Identifies this as an overlay */

&i2c2 {             /* We are modifying the i2c2 node of the base tree */
    /* Describe the pinmux as being pinmux m1 */
    pinctrl-0 = <&i2c2m1_xfer>;
    /* The next two are already set in the base tree we're overlaying onto,
     * but dtc doesn't know this, so it emits warnings. Either way, they
     * describe what the meaning of all the children's reg properties is. In
     * this case, that the only cell in reg describes what address the device
     * listens on.
     */
    #address-cells = <1>;
    #size-cells = <0>;
    status = "okay";    /* Enable the I2C2 node of rk356x.dtsi */
};

/* Copied from rk3568-pinctrl.dtsi in the Linux kernel tree, with constants resolved
 * We need to copy it from there because it gets omitted if there's no
 * reference to it.
 */
&pinctrl {
    pcfg_pull_none_smt: pcfg-pull-none-smt {
        bias-disable;
        input-schmitt-enable;
    };

    i2c2 {
        i2c2m1_xfer: i2c2m1-xfer {
            rockchip,pins =
                /* i2c2_sclm1 */
                <4 13 1 &pcfg_pull_none_smt>,
                /* i2c2_sdam1 */
                <4 12 1 &pcfg_pull_none_smt>;
        };
    };
};

Save the file as enable-i2c2.dts, you can then compile the overlay with dtc -I dts -O dtb -@ -o enable-i2c2.dtbo enable-i2c2.dts. Place the resulting file in /boot/dtbo/, run sudo u-boot-update and reboot.

If you now wish to implement an I2C driver in userspace, please see the Linux kernel documentation on how to do that. If that's all you want to do, you can stop here.

However, be aware that for many devices, you don't even need to do this! The kernel may have a driver already and can expose it like any built-in device.

For example, here's the same overlay, but now we add a BMM150 magnetometer device to the description so that the kernel can load its driver for it (assuming it was compiled with said driver, CONFIG_BMC150_MAGN_I2C, Debian's kernel is):

/dts-v1/;
/plugin/;           /* Identifies this as an overlay */

&i2c2 {             /* We are modifying the i2c2 node of the base tree */
    /* Describe the pinmux as being pinmux m1 */
    pinctrl-0 = <&i2c2m1_xfer>;
    /* The next two are already set in the base tree we're overlaying onto,
     * but dtc doesn't know this, so it emits warnings. Either way, they
     * describe what the meaning of all the children's reg properties is. In
     * this case, that the only cell in reg describes what address the device
     * listens on.
     */
    #address-cells = <1>;
    #size-cells = <0>;
    status = "okay";    /* Enable the I2C2 node of rk356x.dtsi */

    bmm150: magnetometer@10 {       /* New child node with label "bmm150", address 0x10 */
        compatible = "bosch,bmm150";    /* Identifier for the kernel to know what driver to load */
        reg = <0x10>;           /* Describe the I2C address as 0x10 (= decimal 16) */
        /* What follows is the (optional) "mount matrix", which describes how the sensor is
         * mounted in 3d space. In this example, we rotate it around z by 90°.
         * See https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/iio/mount-matrix.txt
         * for more details.
         */
        mount-matrix =  "0", "-1", "0",
                "1",  "0", "0",
                "0",  "0", "1";
    };
};

/* Copied from rk3568-pinctrl.dtsi in the Linux kernel tree, with constants resolved
 * We need to copy it from there because it gets omitted if there's no
 * reference to it.
 */
&pinctrl {
    pcfg_pull_none_smt: pcfg-pull-none-smt {
        bias-disable;
        input-schmitt-enable;
    };

    i2c2 {
        i2c2m1_xfer: i2c2m1-xfer {
            rockchip,pins =
                /* i2c2_sclm1 */
                <4 13 1 &pcfg_pull_none_smt>,
                /* i2c2_sdam1 */
                <4 12 1 &pcfg_pull_none_smt>;
        };
    };
};

The resulting magnetometer would then be handled by the IIO (industrial IO) subsystem, e.g. show up in /sys/bus/iio/devices.

This is of course just an example, for your I2C device you'd have to find the right compatible string and bindings. I can help you do that if you tell me what device it is.

Let me know if this resolves the issue for you, and if you need further assistance using your particular I2C device in the ideal way.

Gerriko commented 1 year ago

Thanks @hughsheehy for highlighting.

Thank you @CounterPillow for providing the overlay detail to enable I2C2 M1. That is very useful.

However, if I use SPI as a comparison, I was able to use (by binding) a spi-dev driver to the device-tree. The purpose of this was for prototyping and I could also use this with Python too.

I was trying to find the equivalent "spi-dev" driver for I2C, but had no luck.

If I look at the /sys/bus/i2c/drivers directory, there are multiple options listed but only 2 that looked likely, namely "dummy" and "rk808" (or maybe even pca953x).

drwxr-xr-x 2 root root 0 Jan  1  1970 dummy
drwxr-xr-x 2 root root 0 Apr  7 18:58 fan53555-regulator
drwxr-xr-x 2 root root 0 Jan  1  1970 max77620
drwxr-xr-x 2 root root 0 Jan  1  1970 pca953x
drwxr-xr-x 2 root root 0 Jan  1  1970 rk808
drwxr-xr-x 2 root root 0 Jan  1  1970 rtc-ds1307
drwxr-xr-x 2 root root 0 Jan  1  1970 rtc-pcf85063
drwxr-xr-x 2 root root 0 Jan  1  1970 rtc-pcf8563
drwxr-xr-x 2 root root 0 Jan  1  1970 si5341
drwxr-xr-x 2 root root 0 Jan  1  1970 simple-mfd-i2c

However, I could not find any documentation online that alludes to the use of these drivers for prototyping purposes. I did find a "i2c_rk3x.ko" on the device and wondered if this is another undocumented option.

I found that if I had a "bmm150: magnetometer@10", or equivalent, all this does is create and enable a device tree. Here is an example I did where I created an option for an I2C LCD device:

/proc/device-tree/i2c@fdd40000/pmic@20/regulators/SWITCH_REG2/status: disabled
/proc/device-tree/i2c@fdd40000/status: okay
/proc/device-tree/i2c@fe5a0000/status: okay
/proc/device-tree/i2c@fe5b0000/status: okay
/proc/device-tree/i2c@fe5b0000/mylcd@27/status: okay
/proc/device-tree/i2c@fe5c0000/status: disabled
/proc/device-tree/i2c@fe5d0000/status: disabled
/proc/device-tree/i2c@fe5e0000/status: disabled

It still does not enable an I2C driver in /dev/.

CounterPillow commented 1 year ago

@Gerriko did you look at https://www.kernel.org/doc/html/latest/i2c/dev-interface.html ? You don't need to add any dummy devices for i2c, it seems. You can just talk to the bus directly. For this, you need to sudo modprobe i2c-dev.

Gerriko commented 1 year ago

Thanks that worked!

crw-rw---- 1 root i2c 89, 0 Apr 10 21:04 /dev/i2c-0
crw-rw---- 1 root i2c 89, 1 Apr 10 21:04 /dev/i2c-1
crw-rw---- 1 root i2c 89, 2 Apr 10 21:04 /dev/i2c-2
crw-rw---- 1 root i2c 89, 6 Apr 10 21:04 /dev/i2c-6

PS. No I had missed that documentation page. So thanks for that.