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