SPI functionality


#1

Hello,

In the last couple of days I’ve had feedback that the bug I opened “Bug 464 - SPI bus chip select pin is going high between bytes” is not going to be fixed.

This is very disappointing that they are saying use the workarounds rather than fixing the behaviour at source. This means for the micromez board I have to redesign it to use an extra GPIO pin for the chip select or tell everyone that wants to use the micromez that they need to patch and rebuild the kernel.

This issue was originally found using the 96Boards starter kit with Linker mezzanine card and loft modules which as far as I’m aware still has the issue when using the C or Python bindings when using the mraa library.

These are meant to be “Starter Kits” which I had always imagined meant they would just-work™ on a default board and kernel.

Interested to hear what others in the community think.


#2

Before going further I’d just like to see if we agree on the following summary of the state of SPI for DB410C.

  1. By default SPI ships disabled on Debian for DB410C
  2. Documentation (supported by the dt-update scripts) allows SPIDEV to be enabled without chip select support. In other words when the SPI driver is in userspace and using spidev than that userspace driver must manage chip select.
  3. DT can be altered to allow kernel to manage chip select automatically in software
  4. DT can be altered to allow hardware to manage chip select automatically in hardware. There are known bugs with hardware management and we don’t recommend it be used.
  5. Kernel SPI drivers can be made to work correctly if (and only if) the chip select is controls based on #3 (in above list).
  6. #4 (in above list) is the subject of bug 464

Right now I don’t understand why there would be any need to use a custom chip select on a mezzanine board. The software chip select methods (whether its userspace or kernel space) are still able to use the same physical pin as the (not working) hardware method so there should be no need to modify the hardware design regardless of the chosen chip select approach.


#3

Thank you @danielt for the detailed response.

This brings some clarity and new information to the topic.

I had missed the documentation update that I believe you are referring to in #2. Is this the correct link to the documentation?

I will give this a test at some point over the next couple of days.

I am not sure this is going to work with the driver as there is the following documentation on the Python SPIDEV page:

no_cs - Set the “SPI_NO_CS” flag to disable use of the chip select (although the driver may still own the CS pin)

There are also a number of issues open on Python SPIDEV library around the no_cs setting:

For my test I’ll replace the previously used code of:

import spidev
spi=spidev.SpiDev()
spi.open(0,0)
while True:
    adc_data=spi.xfer2([0x01, 0x80, 0x00])

And replace it with:

import spidev
import mraa
cs_pin = mraa.Gpio(12)
cs_pin.dir(mraa.DIR_OUT)
spi=spidev.SpiDev()
spi.open(0,0)
spi.no_cs = True
while True:
    cs_pin.write(True)
    adc_data=spi.xfer([0x01, 0x80, 0x00])
    cs_pin.write(False)

Unless anyone has any other ideas…


#4

Yes.

If you set up spidev in this manner it is like SPI_NO_CS is set all the time (regardless of whether you set it or not in py-spidev).

The kernel docs regarding spidev are written somewhat passively but the essence is that spidev is only really intended for experimental work (prototyping/testing hardware, developing microcontroller SPI protocols, etc). Unfortunately for boards like DB410C (which is designed for hacking on) it is really hard to decide when our work is experimental!

A common kernel programmer’s view of the world is that once a device has an established SPI protocol normally we would expect a kernel driver to be written and spidev would no longer be needed. That is the reason for the scary warning when spidev is enabled in the device tree and why DB410C reacts to this warning by shipping with spidev disabled. As an example if you have an SPI ADC then the “right thing to do” is to enable an iio (industrical I/O) driver to control it and then let the kernel look after it.

What makes SPI so confusing is that not everyone views the world like this so there are many examples where SPI is written with userspace drivers. For example, whilst I don’t want to put words in their mouths I assume the mraa/upm guys see value in having a set of userspace SPI drivers otherwise they would not maintain them!

I’ll also confess that, even as someone with lots of kernel programming experience, for some peripherals it can be easier to bring them up by taking a micro-controller driver and running it in userspace than by getting the kernel drivers. Displays are an interesting example: most of the drivers are in staging because they don’t fit perfectly into the Linux frameworks so they can be tricky to set up. This means getting the first image up is easier if you write everything in userspace… on the other hand if you use the kernel driver then X11/wayland will both work out-of-the-box and you don’t have to write the rest of the graphics stack yourself!

The TL;DR answer is that, using kernel controlled chip select would allow you to avoid changing your code, but that applying such a DT change is a bit tricker than the current documented spidev approach. If you want to use kernel drivers then you have to get used to modifying the DT anyway.


#5

This approach is worth-a-quick-try™ but as you said in your original post, the limitations of mraa mean you cannot use the “real” chip select pin unless you extend the GPIO lookup tables inside mraa (@Mani - did you ever discuss having an optional 13th pin on the LS connector to allow mraa to driver the chip-select like a GPIO if the user wants this?).

Note also that IIRC chip select is active low…


#6

Hi @danielt @barryb,

I think the proper way would be to declare the CS pin under spi_bus of MRAA. That would eliminate the need for using the spidev library in applications and let the MRAA handle SPI by itself. The reason why we don’t have SPI enabled for any of the 96Boards is that, as you mentioned we don’t enable SPIDEV in DTS.

If we use CS pin as regular GPIO then that would only work for DB410c, since other boards like HiKey configure mux for CS [1].

So we have 2 options:

  1. Enable SPI support in MRAA for all booards assuming that the user has enabled SPIDEV in DTS.
  2. Use CS pin as GPIO for letting the user toggle the CS pin manually by using MRAA Gpio API. Again, this would only work for DB410c.

Thanks,
Mani

[1] https://github.com/torvalds/linux/blob/master/arch/arm64/boot/dts/hisilicon/hikey960-pinctrl.dtsi#L306


#7

My bad… Ignore my previous statement (1). SPI is enabled by default for 96Boards in MRAA.


#8

Does that mean that if someone has followed instructions to enable spidev on DB410C then MRAA will work out of the box?


#9

Yes, it should work. Btw, I have added a commit for using SPI0_CS pin as GPIO on DB410c [1]. Still need to be tested on board.

[1] https://github.com/Mani-Sadhasivam/mraa/commit/fce203df93a8cdcf168a0e9ee581553e2e106ba2


#10

Hi @barryb,

I have submitted a PR [1] for toggling SPI0_CS pin on DB410c under MRAA. It should help your case.

Thanks,
Mani

[1] https://github.com/intel-iot-devkit/mraa/pull/922


#11

Thanks @Mani and @danielt.

I have downloaded and installed dragonboard-410c-sdcard-installer-buster-359.zip which was the latest on the downloads.
I have done sudo apt update and sudo apt upgrade on the system.

I followed the instrucitons for enabling SPI on the Dragonbaord.
On the reboot I saw the following messages appeared in the dmesg

[   10.065179] spidev spi1.0: buggy DT: spidev listed directly in DT
[   10.081636] WARNING: CPU: 3 PID: 1645 at drivers/spi/spidev.c:730 spidev_probe+0x1f4/0x280 [spidev]
[   10.519071] spidev spi0.0: buggy DT: spidev listed directly in DT
[   10.541951] WARNING: CPU: 3 PID: 1645 at drivers/spi/spidev.c:730 spidev_probe+0x1f4/0x280 [spidev]

When I tried running the test I got an error

$ sudo ./spidev_test -CHOv -D /dev/spidev0.0
can't set spi mode: Bad address
Aborted

Listing of the /dev directory revealed:

$ ll /dev/spidev*
crw------- 1 root root 153, 1 Aug 29 22:12 /dev/spidev0.0
crw------- 1 root root 153, 0 Aug 29 22:12 /dev/spidev1.0

I have cloned Mani’s github fork and built it:

$ sudo python3
Python 3.6.6 (default, Jun 27 2018, 14:44:17) 
[GCC 8.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import mraa
>>> mraa.Spi(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/dist-packages/mraa.py", line 630, in __init__
    this = _mraa.new_Spi(*args)
ValueError: Error initialising SPI bus
>>> 

What did I do wrong?

Thanks,
Barry


#12

Hi @barryb,

I have just tested the 18.01 release and SPI works fine. The steps are exactly same as yours and for the spidev_test utility I got the below expected output (with no loopback):

spi mode: 0x7
bits per word: 8
max speed: 500000 Hz (500 KHz)
TX | FF FF FF FF FF FF 40 00 00 00 00 95 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF F0 0D  | ......@....�..................�.
RX | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | ................................

Then I cloned MRAA, built and installed. At last, I successfully executed the examples/python/spi_loopback.py script by shorting the MISO and MOSI pins (5,7) on LS header.

Not sure what is going wrong in your case. Can you please show the dmesg output after executing spidev_test?

Thanks,
Mani


#13

Thanks @mani for taking a look at this.

I have put the content of dmesg in a gist at:


#14

Hi @barryb,

Nothing looks suspicious in dmesg output. Just to verify, can you please flash the new release image onto eMMC and try the steps again? It wouldn’t make much difference but good to try!

Thanks,
Mani


#15

Curious, you are both using the same release (as am I). The only noteworthy difference is that @barryb mentioned being fully apt upgraded.

My board started with 18.01 and has been fully upgraded (and dist-upgraded). I haven’t tried attaching “real” hardware but the spidev_test works are expected for me.

Like @Mani I’ve not really got any clue why this isn’t working for you. Other than spidev_test the userspace doesn’t really do much here and I can see from the logs you are running the same kernel binary that I am.


#16

Thanks for the updates. The update from my side is:

If I put the latest release on to the eMMC and the first thing I do is enable SPIDEV then spidev_test passes.

When I have run some other experiments where enabling the SPIDEV isn’t the first thing I do then I hit issues.

I’m doing some more rebuilds at the moment to see if there is something specific I can identify. I also haven’t tried doing a full update/upgrade after enabling SPIDEV to see if spidev_test still passes.

I’ll report back if there is a specific recipe that works including installing and using mraa.

Thanks,
Barry


#17

Thanks for your help @Mani and @danielt.

I have been able to successfully access the ADC via SPI on the Linker Mezzanine card for 96board using the DB410c.

The order of things seems to make the difference. Below is the order that worked for me:

<install dragonboard-410c-sdcard-installer-buster-359.zip release>
git clone https://github.com/96boards/dt-update
cd dt-update
make
sudo scripts/db410c/enable-spidev.sh
<reboot>
sudo apt update
sudo apt upgrade
sudo apt dist-upgrade

sudo apt install git build-essential swig3.0 python3-dev cmake

git clone https://github.com/intel-iot-devkit/mraa.git
cd mraa
mkdir build
cd build/
cmake -DBUILDSWIG=ON \
      -DBUILDSWIGPYTHON=ON \
      -DBUILDSWIGNODE=OFF \
      -DBUILDSWIGJAVA=OFF \
      -DCMAKE_INSTALL_PREFIX=/usr \
      ..
make
sudo make install

sudo python3 test_script.py

<test_script.py>
from time import sleep
import mraa
cs_pin = mraa.Gpio(12)
cs_pin.dir(mraa.DIR_OUT)
dev = mraa.Spi(0)

txbuf = bytearray([0x01, 0x80, 0x00])

while True:
    cs_pin.write(False)
    rxbuf = dev.write(txbuf)
    cs_pin.write(True)
    print(((rxbuf[1] & 0x03) << 8) | rxbuf[2])
    sleep(1)
</test_script.py>

Thanks for your help.