Remote I/O bus

Luca Ceresoli luca at lucaceresoli.net
Fri Oct 4 07:04:56 EDT 2019


Hi,

on an embedded system I currently have a standard platform device:

.-----.  data  .--------.
| CPU |--------| DEVICE |
'-----'   bus  '--------'

The driver is a standard platform driver that uses ioread32() and
iowrite32() to access registers.

So far, so good.

Now in a new design I have the same device in an FPGA, external to the
SoC. The external FPGA is not reachable via an I/O bus, but via SPI (or
I2C). A microprocessor in the FPGA acts as a bridge: as an SPI client it
receives register read/write requests from the CPU, forwards them to the
devices on the in-FPGA data bus as a master, then sends back the replies
over SPI.

                       SoC <- | -> FPGA

.-----.  data   .---------.       .--------.  data   .--------.
| CPU |---------| SPI CTL |-------| BRIDGE |---------| DEVICE |
'-----'  bus A  '---------'  SPI  '--------'  bus B  '--------'


What would be a proper way to model this in the Linux kernel?

Of course I can hack the drivers to hijack them on SPI, but I'm trying
to solve the problem in a better way. IMO "a proper way" implies that
the platform driver does not need to be aware of the existence of the
bridge.

Note: in the real case there is more than one device to handle.

At first sight I think this should be modeled with a "bridge" device that:

 * is a SPI device
 * implements a "platform bus" where regular platform devices can be
   instantiated, similar to a "simple-bus"

In device tree terms:

&amba { /* data bus A in picture */

    spi0: spi at 42000000 {
        reg = <0x42000000 0x1000>;
        #address-cells = <1>;

        io-over-spi-bridge at 1 { /* data bus B */
            reg = <1>; /* slave select pin 1 */
            compatible = "linux,io-over-spi-bridge";
            #address-cells = <1>;
            #size-cells = <1>;

            mydevice at 4000 {
                /* 1 kB I/O space at 0x4000 on bus B */
                reg = <0x4000 0x1000>;
            };
        };
    };
};

The io-over-spi driver is supposed to request allocation of a virtual
memory area that:
 1. is as large as the address space on bus B
 2. is __iomem (non cached, etc)
 3. is not mapped on the physical CPU address space (bus A)
 4. page faults at every read/write access, triggering a callback
    that starts an SPI transaction, waits for the result and returns

After some research I haven't found how this could be implemented,
mostly due to my newbieness about kernel memory management. Also, as
drivers might access the bus in IRQ handlers, I suspect there is no way
to do an SPI transaction in IRQ context, but this could be handled
differently (threaded IRQ...).

Does this look like a good approach to handle the problem?
If it does, how would you implement the iomem access and handle IRQ context?
Otherwise, which way would you suggest?

Many thanks in advance,
-- 
Luca



More information about the Kernelnewbies mailing list