normally, a microprocessor board would be an i2c master

but if you want to use i2c to connect two such boards, then one would normally be a master, then all others slaves.

(there is also a concept of multi-master, but Id like to avoid this I think, if possible... as it only works if every master supports mulit-master)

Ive noticed that linux these days supports both master/slave, however its dependent on chipset support.
(just what Ive read... no expert/experience in this area really!))

so questions are :
- can Bela mini (preferably, but full-fat one too) be configured as an i2c slave?
- if it has multiple i2c buses, can these be configured as master/slave independently.
- is bela i2c all 3v3 (rather than 5v)

use-case:
I want to connect to another i2c master (which cannot be a slave, but potentially supports multi master) to Bela.

it would also be handy if Bela had another i2c bus, so I could run two independent i2c buses.
(I could then run Trill on a separate i2c bus to the mcu interconnection i2c bus)

bonus, is there any way I can get an i2c bus on Bela Salt...

the other alternative, whilst talking more TRILL than Bela....
perhaps I could use something like a teensy which has two i2c buses (sdl0, sdl1) , if they were both setup as i2c slave. then it could 'simply' copy data between the two buses.
the SCL would be out of sync, but if the teensy is dedicated to this task.. then a small buffering of messages should solve this....

this way, my Trill sensors could be potentially accessible by either i2c master, and also the two i2c masters could communicate....

or is this just crazy talk 😉

    thetechnobear Ive noticed that linux these days supports both master/slave, however its dependent on chipset support.
    (just what Ive read... no expert/experience in this area really!))

    I don't have any experience either, but I read the same thing following your question. The AM3358 SoC has support for I2C slave according to its datasheet. However, most linux docs I found related to i2c slave behaiour are for kernel-space interaction. The only mention of user space is via sysfs.

    According to https://www.kernel.org/doc/html/latest/i2c/slave-interface.html, "User manual" section:

    I2C slave backends behave like standard I2C clients. So, you can instantiate them as described in the document ‘instantiating-devices’. The only difference is that i2c slave backends have their own address space. So, you have to add 0x1000 to the address you would originally request. An example for instantiating the slave-eeprom driver from userspace at the 7 bit address 0x64 on bus 1:

    # echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-1/new_device

    Each backend should come with separate documentation to describe its specific behaviour and setup.

    ... I tried that and I can indeed create a device at that bus address:

    cd /sys/bus/i2c/devices/i2c-1
    echo mydevice 0x1064 > new_device

    results in a new device being created. dmesg gives i2c i2c-1: new_device: Instantiated device mydevice at 0x64 and a new folder comes up at /sys/bus/i2c/devices/i2c-1/1-1064

    ... however I don't know how to interact with it. It seems like the usual i2c-dev or smbus user-space libraries do not have any explicit support for slave operation ... but maybe you can just call read() or write() and these will block until an external master performs a start condition on the bus and starts driving the clock? Who knows ... the "instantiating-devices" page the above section refers to seems to indicate that this is about manually instantiating https://www.kernel.org/doc/html/latest/i2c/instantiating-devices.html kernel drivers from user space but that then the operation would be entirely down to the driver, so this is not particularly useful unless you are into writing kernel code ...
    It may even make sense that Linux can only run in kernel space for i2c slaves: the i2c slave needs to respond pretty fast when the i2c master generates a start condition and it may well be that Linux thinks that no one would attempt to meet a sort-of hard deadline by going to user space. "sort-of-hard deadline": there are things like "clock stretching" that would allow the slave to take more time than expected to respond, but at some point the master will decide to time out.

    thetechnobear - if it has multiple i2c buses, can these be configured as master/slave independently.

    From the little I could infer from the above, I think the master/slave behaviour could even be not only bus-specific but even address-specific. To be confirmed

    thetechnobear - is bela i2c all 3v3 (rather than 5v)

    Yes, like all its digital pins.

    thetechnobear it would also be handy if Bela had another i2c bus, so I could run two independent i2c buses.

    It has two: one is i2c1 (default for Trill), one is i2c2 (used for the Bela codec, but Trill could be moved to this because the addresses don't conflict and they both support 400kHz. Note: on non last-rev Bela/Mini boards you'll need pullup resistors on this bus).

    thetechnobear bonus, is there any way I can get an i2c bus on Bela Salt...

    I2C2 is broken out to one of the grove connectors on the BBG. If you need I2C1, that one is on a molex header on the Bela cape, and you could try to somehow stick a connector in there, but it'd need to be pretty short ... you can also solder to the pins at the back of the BBG.

    thetechnobear I want to connect to another i2c master (which cannot be a slave, but potentially supports multi master) to Bela.

    is there not a UART connection available? That would make things easier for the purpose of communicating to Bela without resorting to understanding I2C slave on Linux ...

    thetechnobear this way, my Trill sensors could be potentially accessible by either i2c master, and also the two i2c masters could communicate....

    Do you need the two masters to access the devices alternatively (e.g.: Bela reads, other device reads, Bela reads..) or do you need to be able to switch between them occasionally at some point (e.g.: one patch Bela reads, other patch other device reads)? I am wondering whether an I2c multiplexer could help with that ...

    thetechnobear or is this just crazy talk 😉

    Here's some crazy talk, just for making things complicated: if you don'y really need to communicate between Bela and the other device and you want Bela to read from Trill at the same time as the other device, you could have a little PRU program that sniffs the I2C transaction between the other two devices. It would be super fun! Actually at that point you could even have the other device send dummy messages to Trill only for the purpose of sending them to Bela!

    thetechnobear perhaps I could use something like a teensy which has two i2c buses (sdl0, sdl1) , if they were both setup as i2c slave. then it could 'simply' copy data between the two buses.

    what's the connection diagram here?

      even more crazy/fun:

      • one could have one PRU take over one I2C bus and implement a slave device
      • one could implement a I2C slave device on the PRU (i.e.: bitbanging the SCL and SDA lines), leaving the other two busses available for other use by Linux.

      thanks for the great info giuliomoro , fantastic as always !

      so, to step back a bit ... why? all this stuff at all?

      I2C is starting to see a bit of growth in Eurorack, basically as a way to provide 'behind' the panel connections between digital modules. (this post on lines lists a few modules etc)

      so , some modules are only slaves, some are master (only) , some can be configured as either.
      to slightly complicate, i2c can often be 5v on eurorack... probably due to the 5v on the bus... but thats easy enough to solve with a cheap level converter.

      so I was thinking about a module thats a 'master' only, and how to connect to Bela (possibly Salt).
      hence the master to master 'issue', and needing to get one (Bela 😉 ) to act as a slave.

      with trill theres many options, one might be I have all the code sitting on Bela/Salt.. then I communicate with the eurorack modules via i2c (unfortunately UART is not supported, and again this is back to i2c becoming a kind of 'standard' on eurorack)
      but of course, I could connect trill directly as well... so there are choices here.


      Interesting what you say about user-space vs kernel...
      hmm, I'd the coding was rather similar for master/slave, once the initialisation is done, that this slave kernel device would handle the differences.

      however, if I look at i2c-dev.h, indeed we can see I2C_SLAVE is defined for ioctl, and there is nothing that looks similar for flipping things the other way around (e.g. an i2c-master.h, or I2C_MASTER define)

      so, it could well be that this has to be done as a kernel driver 🙁
      I definitely need to do some more digging in this area 😉

      btw: are you/bela team coming to superbooth this year?

        11 days later

        thetechnobear btw: are you/bela team coming to superbooth this year?

        Unfortunately not. Hopefully next year!

        thetechnobear however, if I look at i2c-dev.h, indeed we can see I2C_SLAVE is defined for ioctl, and there is nothing that looks similar for flipping things the other way around (e.g. an i2c-master.h, or I2C_MASTER define)

        Yeah I observed the same. Perhaps it could be a good time to do some C programming on the PRU? Not sure how much time you have to spend on this ... 🙂

        As you said, it may well be that having a Teensy or something with two or more I2C interfaces that you can poll read / write from both I2C master devices (Salt and the other one) is an easier and more general solution ... It's even possible that the kernel driver allows multi-master mode? Don't know ...

        Ah! Got it: I2C_SLAVE and I2C_SLAVE_EEPROM are disabled in the current kernel build. Rebuilding right now ... I guess if this works you could just use the EEPROM driver as it is: the external device would write/read blocks of this virtual device and you can read/write it at /sys/bus/i2c/devices/<device-directory>/slave-eeprom.

        As of 2015, Linux doesn’t support poll on binary sysfs files, so there is no notification when another master changed the content.

        This would mean that you don't get notified when data is written, so you'd have to repeatedly read it ...

        Slightly more info here: https://www.kernel.org/doc/html/latest/i2c/slave-eeprom-backend.html

        kernel rebuilt, now when I do echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-1/new_device I get in dmesg :

        [   33.542402] i2c i2c-1: new_device: Instantiated device slave-24c02 at 0x64
        [   33.557827] i2c-slave-eeprom 1-1064: i2c_slave_register: not supported by adapter
        [   33.565952] i2c-slave-eeprom: probe of 1-1064 failed with error -95

        so it would seem that slave support is missing in the TI i2c driver?

        The printed error comes from https://elixir.bootlin.com/linux/v4.14.108/source/drivers/i2c/i2c-core-slave.c#L44 : the drivers/busses/i2c-omap.c (used on the BBB) does not support I2C slave.
        By grepping in KERNEL/drivers/i2c/ I get

        $ grep -RI reg_slave *
        busses/i2c-emev2.c:static int em_i2c_reg_slave(struct i2c_client *slave)
        busses/i2c-emev2.c:static int em_i2c_unreg_slave(struct i2c_client *slave)
        busses/i2c-emev2.c:	.reg_slave      = em_i2c_reg_slave,
        busses/i2c-emev2.c:	.unreg_slave    = em_i2c_unreg_slave,
        busses/i2c-aspeed.c:static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr)
        busses/i2c-aspeed.c:static int aspeed_i2c_reg_slave(struct i2c_client *client)
        busses/i2c-aspeed.c:	__aspeed_i2c_reg_slave(bus, client->addr);
        busses/i2c-aspeed.c:static int aspeed_i2c_unreg_slave(struct i2c_client *client)
        busses/i2c-aspeed.c:	.reg_slave	= aspeed_i2c_reg_slave,
        busses/i2c-aspeed.c:	.unreg_slave	= aspeed_i2c_unreg_slave,
        busses/i2c-aspeed.c:		__aspeed_i2c_reg_slave(bus, bus->slave->addr);
        busses/i2c-designware-slave.c:static int i2c_dw_reg_slave(struct i2c_client *slave)
        busses/i2c-designware-slave.c:static int i2c_dw_unreg_slave(struct i2c_client *slave)
        busses/i2c-designware-slave.c:	.reg_slave = i2c_dw_reg_slave,
        busses/i2c-designware-slave.c:	.unreg_slave = i2c_dw_unreg_slave,
        busses/i2c-rcar.c:static int rcar_reg_slave(struct i2c_client *slave)
        busses/i2c-rcar.c:static int rcar_unreg_slave(struct i2c_client *slave)
        busses/i2c-rcar.c:	.reg_slave	= rcar_reg_slave,
        busses/i2c-rcar.c:	.unreg_slave	= rcar_unreg_slave,

        so only a handful of bus drivers support slave operation. One could try to draw inspirations from those and the Technical Reference Manual to add slave support to the i2c-omap.c. Chapter 21 of the TRM is only 69 pages including register map. So the task should be doable, with enough time!

        Interestingly, the chapter also mentions that multimaster mode is supported by the hardware and the i2c-omap.c driver has some notes about multimaster, so it may be that it is supported?

          giuliomoro Chapter 21 of the TRM is only 69 pages including register map. So the task should be doable, with enough time!

          weeell ... it turns out one of the reasons it's so short is that it is underdocumented and buggy. See https://patchwork.kernel.org/project/linux-omap/patch/20160525141119.14486-2-rk@ti.com/ for a proposed driver and subsequent discussion. I don't see an updated version of the driver there ensuing the discussion, but it may be that this works fine as is for your relatively simple purpose. Let me see if I can build it.

          OK some progress. That patch applies and runs. I bridge I2C1-SCL with I2C2-SCL and I2C1-SDA with I2C2-SDA. When booting the board with the EEPROM dtbo (which tells i2c-2 of a 24c02-type external eeprom
          at address 0x50) enabled, patched with

          diff --git a/src/arm/EEPROM.dts b/src/arm/EEPROM.dts
          index 064ac92..58b63fb 100644
          --- a/src/arm/EEPROM.dts
          +++ b/src/arm/EEPROM.dts
          @@ -20,7 +20,7 @@
                                  #size-cells = <0>;
                                  clock-frequency = <400000>;
                                  eeprom: eeprom@50 {
          -                               compatible = "24c04";
          +                               compatible = "24c02";
                                          reg = <0x50>;
                                          pagesize = <16>;
                                          #address-cells = <1>;

          and running this file

          #!/bin/bash
          set -euo pipefail
          
          MASTER=/sys/bus/i2c/devices/2-0050/eeprom
          SLAVE=/sys/bus/i2c/devices/1-1050/slave-eeprom
          [ -e $SLAVE ] || echo slave-24c02 0x1050 > /sys/bus/i2c/devices/i2c-1/new_device
          
          function read_master()
          {
          	printf "READ MASTER "
          	sync $MASTER
          	cat $MASTER
          	echo "\======="
          }
          function read_slave()
          {
          	printf "READ SLAVE "
          	sync $SLAVE
          	cat $SLAVE
          	echo "\======="
          }
          function write_master()
          {
          	echo "WRITE MASTER $1"
          	echo $1 > $MASTER
          	sync $MASTER
          }
          function write_slave()
          {
          	echo "WRITE SLAVE $1"
          	echo $1 > $SLAVE
          	sync $SLAVE
          }
          
          read_master
          read_slave
          write_slave "1234"
          read_slave
          read_master
          
          read_master
          read_slave
          write_master "fghciw5678ij"
          read_master
          read_slave

          after a reboot I get

          READ MASTER =======
          READ SLAVE =======
          WRITE SLAVE 1234
          READ SLAVE 1234
          =======
          READ MASTER 1234
          =======
          READ MASTER 1234
          =======
          READ SLAVE 1234
          =======
          WRITE MASTER fghciw5678ij
          READ MASTER 1234
          ih=======
          READ SLAVE 1234
          ih=======

          So the slave side seems to work fine (i.e.: what you write is what you then read back, but this doesn't actually do anything on the I2C bus, it's just an internal representation). The master side can read the data that the slave put there. At all times, both master and slave always read the same content. That's good. However, the master only successfully writes a few bytes get written (and with an offset?).

          I then tested that if you write them enough times you will eventually get more bytes written by the master ... Note that the above printout is missing all \0 characters in the eeprom (hexdump would have probably been a better choice).

          If it was just the above, you could probably do some more work and fix it (it may be something in the i2c slave eeprom driver), however I also get this occasionally (i2c2 bus timing out), which indicates the slave driver is unreliable:

          [  150.536510] omap_i2c 4819c000.i2c: controller timed out
          [  154.092656] omap_i2c 4819c000.i2c: timeout waiting for bus ready

          unfortunately, the only way I know to recover from this is to reboot the board, however may be unbinding/rebinding the virtual device or i2c bus would be good enough (if at all possible)? Also, you (Bela, the slave) should be able to detect that, which I don't think is currently the case.

          @thetechnobear I can provide a built kernel for this if you are interested in doing some more tests, though you would eventually be better off getting a kernel build setup for yourself if you are going to try to make any changes.

          giuliomoro Interestingly, the chapter also mentions that multimaster mode is supported by the hardware and the i2c-omap.c driver has some notes about multimaster, so it may be that it is supported?

          thats interesting, as the percussa Im using is also supports multimaster (but not slave) ...

          hmm, think I'll have to come back to this as, Im thinking of doing something for superbooth, and timing is getting a bit tight now 😉