How to enable spi and access it in Debian os

Can you also try adding the cs-gpios property to the blsp_spi5 (or whatever it is for the HS) node and see if that works? Something like below, but not sure if the number designation is exact or not.


num-cs = <1>;
cs-gpios = <&gpio18 0 1>;

Thanks for the suggestion. Unfortunately I tried all combinations I could think of with num-cs, cs-gpios, the lines I mentioned earlier, as well as removing the cs gpio from pinconf earlier in the dtsi file (since it’s called out explicitly in pinconf_cs a few lines later, I thought it might be causing a conflict), but none of them were able to get CS to move.

In general, it’d be good to start new threads for new problems. Can you please specify which spi you are trying to use on the system? Or which pins, on which header?

Given your answers, I’ll provide some snippets of DT that should work for you.

I figured it was suitable here since it still has to do with getting spi running on Debian, but I can see your point.

Ultimately I’m using spi3 on the HS header, but my configuration has both of them enabled and I’m using spi5 on the LS header for testing since it’s easier to probe. If I can see how to get CS running on either of them, I should be able to port the changes to the other without much hassle. Thanks.

I changed

                pinmux_cs {
                        function = "gpio";
                        pins = "gpio18";
                };

to

                pinmux_cs {
                        function = "blsp_spi5";
                        pins = "gpio18";
                };

and now it drives the chipselect.

Unfortunately it created a new problem. The chip select goes high between each byte of data transferred, but the device needs CS to stay low for all 3 bytes that are rw to the device.

I dug into the source of drivers/spi/spi-qup.c and found that the bit MX_CS_MODE is never set, hence the CS goes high between bytes. I added a line of code

      control |= SPI_IO_C_MX_CS_MODE;

At line 554 and now CS stays low for the entire transfer.

	spi@78b7000 {
	/* On High speed expansion */
		label = "HS-SPI1";
		status = "okay";
		cs-gpios = <&msmgpio 10>;

		spidev@0 {
			compatible = "spidev";
			reg = <0>;
			spi-max-frequency = <50000000>;
		};
	};

	spi@78b9000 {
	/* On Low speed expansion */
		label = "LS-SPI0";
		status = "okay";
		cs-gpios = <&msmgpio 18>;

		spidev@0 {
			compatible = "spidev";
			reg = <0>;
			spi-max-frequency = <50000000>;
		};
	};

That should get you working CS-0s on LS and HS. I’ll try to verify a little later.

Thanks you to both ljking and agross! Those look like two different solutions to the same problem (treating the CS line as part of SPI vs a SPI-controlled GPIO). I haven’t tested agross’s solution, but ljking’s worked great for me.

And actually, that “problem” you mentioned of CS going high between each byte is GREAT for my application, and solves another, unrelated problem I’ve been having. For some reason, approximately every 5e11 bits that’s transferred in my system, I get a corruption where a clock cycle doesn’t make it through. I don’t know if the 410c isn’t sending it, the CPLD isn’t reading it, or due to a flea coughing in a nearby state some interference squashes it, but either way when it happens it causes the entire remainder of the 32kB SPI transfer to get shifted by one bit and corrupted. The CS toggling every byte allows the 410c and CPLD to remained sync’d at a much lower level than I could have ever done in user-space, AND it doesn’t slow anything down, the CS toggles in the 70nS gap between bytes that was already there, perfect!

Slight update. I was helping someone else and found a slight error in my DT solution. I verified that the following toggles the CS line correctly:

            spi@78b9000 {
            /* On Low speed expansion */
                    label = "LS-SPI0";
                    cs-gpios = <&msmgpio 18 0>;
                    status = "okay";

                    spidev@0 {
                            compatible = "spidev";
                            reg = <0>;
                            spi-max-frequency = <50000000>;
                    };
            };

The error was leaving off the last cell in the gpios. msmgpio requires both the pin and the polarity.

Hi All,

Thanks to everyone for all the useful information. Are there any plans to have SPI Enabled in an image so users do not have rebuild the kernel to enable it?

Thanks,
Mike

@mvartini Hi Mike,

I think it’s enabled as a module. Can you try ‘modprobe spidev’ or ‘insmod spidev’ and see if something like ‘/dev/spidev0.0’ appears?

Hi @vchong,

Thank you for your reply.

I downloaded the latest Debian SD image from http://builds.96boards.org/releases/dragonboard410c/linaro/debian/latest/ and it does not appear to be working.

/dev/spidev0.0 does not appear after running the two commands above as a root user.

sudo insmod spidev generates the following error message:

insmod: ERROR: could not load module spidev: No such file or directory

Thanks,
Mike

The SPI driver is in the kernel (as a module), but not in the device tree. you will need to rebuild the device tree to get it to work. Additionally if you want hardware chip select to work, you will need to make a small change to the device driver in the kernel. If a proper device driver exists for the device you are attaching to the SPI bus, then you should use it if possible (instead of spidev). This ‘patch’ will enable the generic /dev/spidev0.0 and fix the HW chip select. I have tested this on build #202 this week.


diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
index f6d2bcb6dbd1..874150a412a2 100644
--- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
@@ -208,8 +208,14 @@
                /* On Low speed expansion */
                        label = "LS-SPI0";
                        status = "okay";
+                       spidev@0 {
+                               compatible = "spidev";
+                               spi-max-frequency = <100000>;
+                               reg = <0>;
+                       };
                };

                leds {
diff --git a/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi b/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
index 899f2b28a9c9..1c22b4414edf 100644
--- a/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
@@ -208,7 +208,7 @@
                        pins = "gpio16", "gpio17", "gpio19";
                };
                pinmux_cs {
-                       function = "gpio";
+                       function = "blsp_spi5";
                        pins = "gpio18";
                };
                pinconf {
@@ -218,7 +218,7 @@
                };
                pinconf_cs {
                        pins = "gpio18";
-                       drive-strength = <2>;
+                       drive-strength = <12>;
                        bias-disable;
                        output-high;
                };

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 68f95acf7971..aed71ef7e3fd 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -580,6 +580,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
        else
                control &= ~SPI_IO_C_CLK_IDLE_HIGH;

+       config |= SPI_IO_C_MX_CS_MODE;
        writel_relaxed(control, controller->base + SPI_IO_CONTROL);

        config = readl_relaxed(controller->base + SPI_CONFIG);
@@ -928,7 +929,7 @@ static int spi_qup_probe(struct platform_device *pdev)
                        base + QUP_ERROR_FLAGS_EN);

        writel_relaxed(0, base + SPI_CONFIG);
-       writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
+       writel_relaxed(SPI_IO_C_NO_TRI_STATE|SPI_IO_C_MX_CS_MODE, base + SPI_IO_CONTROL);

        ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
                               IRQF_TRIGGER_HIGH, pdev->name, controller);


if you don’t want to use hardware chip select, and would rather use software chip select then you don’t need to patch the drivers/spi/spi-qup.c file, instead, leave pinmux_cs as gpio, and add the line:
cs-gpios = <&msmgpio 18>;
in front of the spidev@0 in the file arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi

Instructions for rebuilding the kernel and devicetree are here: http://builds.96boards.org/releases/dragonboard410c/linaro/debian/latest/ although you will need to use a slightly different git checkout command if you want to rebuild #202.

git checkout -b build-202 0ea6924010269ba065848b2e450673e96bceffd9

Hi @ljking,

Thanks for the reply and detailed information. I am able to rebuild the image as well which is great but I guess I was wondering what the reason for not including a common feature such as SPI in the actual image without needing to rebuild the kernel? Is it a memory issue?

I would think that having SPI enabled by default on the latest image would improve the out of box experience for many users that want to use SPI. Rebuilding the kernel and requiring a separate box to do the fastboot might be difficult for some users… But I could missing something obvious as to why it is not included…

Thanks again for your help,
Mike

Hi @ljking

I also try your patch for a specific SPI mtd device (m25p80), but hardware chip select does not work. I always get failed for reading JEDEC id in probe.

[2.496337] spi_qup 78b9000.spi: IN:block:16, fifo:64, OUT:block:16, fifo:64
[2.512441] m25p80 spi0.0: unrecognized JEDEC id bytes: 00,  0,  0

But it works well by using software chip select. (define cs-gpios in DTS)

I don’t know if hardware chip select issues are fixed now, but there seems some reasons to disable it in spi-qup.c.
https://git.linaro.org/landing-teams/working/qualcomm/kernel.git/commit/drivers/spi/spi-qup.c?h=release/qcomlt-4.4&id=4a8573abe965115bc5b064401fd669b74e985258


In addition, I also encoutner problems in block mode. To dump block for /dev/mtdblock0 will cause crash in DMA.

$ hexdump -C /dev/mtdblock0 -n 64

Call trace:
 [<ffffffc0003cef40>] bam_start_dma+0x1f0/0x350
 [<ffffffc0003cf124>] bam_issue_pending+0x84/0x90
 [<ffffffc00050d334>] spi_qup_transfer_one+0x34c/0x6a8
 [<ffffffc000507a70>] spi_transfer_one_message+0xc0/0x320
 [<ffffffc00050828c>] __spi_pump_messages+0x34c/0x4a0
 [<ffffffc000508614>] __spi_sync+0x214/0x230
 [<ffffffc000508640>] spi_sync+0x10/0x18
 [<ffffffbffc0073d0>] m25p80_read+0x148/0x180 [m25p80]
 [<ffffffc000504124>] spi_nor_read+0x7c/0xd8
 [<ffffffc0004fc9a8>] mtd_read+0x48/0x80
 [<ffffffc000503904>] mtdblock_readsect+0x64/0x190
 [<ffffffc0005029c0>] mtd_blktrans_work+0x130/0x308
 [<ffffffc0000cc8ac>] process_one_work+0x11c/0x378
 [<ffffffc0000ccc2c>] worker_thread+0x124/0x498
 [<ffffffc0000d2924>] kthread+0xdc/0xf0
 [<ffffffc000085dd0>] ret_from_fork+0x10/0x40

If I disable DMA in DTS, it shows OUTPUT_OVER_RUN problem.

$ hexdump -C /dev/mtdblock0 -n 64

 spi_qup 78b9000.spi: OUTPUT_OVER_RUN
 m25p80 spi0.0: SPI transfer failed: -5
 spi_qup 78b9000.spi: INPUT_UNDER_RUN
 spi_master spi0: failed to transfer one message from queue
 blk_update_request: I/O error, dev mtdblock0, sector 0
 spi_qup 78b9000.spi: OUTPUT_OVER_RUN

Finally, I apply @agross ’ patch and it work well.
https://git.kernel.org/cgit/linux/kernel/git/agross/linux.git/commit/drivers/spi/spi-qup.c?h=spi-wip&id=698179c6148035da04bbeb129fd24880827f5d1f

Does that mean DMA should be disabled, if I want to use SPI for block devices?
Thanks!

Hi @daniel-hung

First thing to note is I was only testing the SPI ADC that is on the Linker mezzanine board, it only sends/receives 3 bytes at a time so I definitely didn’t stress the DMA capacity.

When you are using ‘software’ chip selects, a transaction takes a lot longer that when using hardware chip selects. I measured an extra 100 to 500 microSeconds per transaction.

You really need an Oscilloscope to take a look at the SPI bus to verify what you are really getting. It stuns me the number of times I see programmers trying multiple things to get a device to work when it would have been trivial to just look at the signal and see what the problem is.

These two files show Hardware chip select with and without the SPI_IO_C_MX_CS_MODE bit set. As you can see the chip select does not stay low for the entire transaction if the SPI_IO_C_MX_CS_MODE bit is not set. The Yellow trace is clock, the green trace is the data sent from the 410c to the device, and the Magenta trace is the chip select. With the Oscilloscope traces in hand it was trivial to debug the problem.

Hi @daniel-hung

I managed to buy a M25P80 breakout board and hook it up to my 410c. I see the same thing you do, the device is recognized when using SW chip selects and not recognized when using HW chip selects. When SW chip selects are used there are ‘two’ transactions, first the command byte READ_ID is sent to the device, then the data transfer sequence of several bytes to read back the ID information from the chip. The chip select stays low between pair of transactions. When using HW chip selects the Chipselect goes high between the two transactions, this of course resets the state machine in the M25P80 and it doesn’t return a device ID.

This is trivial to debug with an oscilloscope.

I can now see why HW chip selects are causing grief and probably why @agross disabled them. I am surprised that the patches have not been upstreamed into the mainline, it has been a full year since they were completed.,

Hi @ljking

Sorry for late reply because we have a vocation here. Thank you very much for the test & suggestions. Actually, I also check the signals with an oscilloscope, and see the strange behavior of HW chip select. It looks similar with and without the SPI_IO_C_MX_CS_MODE bit set you mentioned. Then, I try to revise your patch (it may be a typo for control & config variables in yours, or I misunderstand something?) and confirm SPI_IO_C_MX_CS_MODE bit is set correctly, but HW chip select still doesn’t work.

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 68f95acf7971..aed71ef7e3fd 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -580,6 +580,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
        else
                control &= ~SPI_IO_C_CLK_IDLE_HIGH;

+       control |= SPI_IO_C_MX_CS_MODE;
        writel_relaxed(control, controller-&gt;base + SPI_IO_CONTROL);

        config = readl_relaxed(controller-&gt;base + SPI_CONFIG);

I still do not know yet why this strange symptom happens, but at least SW chip select can work.

Hi everybody,
I have tried this HowTo Enable SPI with SPIDEV ( I used the prebuilt image) and the test in the bottom was successful but I noted that chip select does not toggle at all. After a quick search I found this thread and I would like to ask whether the problem of the chip select is still active? Or did I miss something?
I assumed the HowTo to be a working solution.

Thank you. If this is a different topic I would be happy to open a new thread :slight_smile:

There are three ways that the chip select can be operated:

  1. having the user space program open the chip select gpio and drive it low before each transaction and high after. This works, but is cumbersome on the user. to get this to work you need to enable just the spidev in the dtsi file

  2. having the driver toggle the chipselect gpio. This is better and seems to work in all cases but you need to tell the driver which gpio to toggle in the dtsi file. This hides the gpio from the user and is a better solution.

  3. having the hardware toggle the chip select gpio. This would, at first, seem to be the best solution, but there are issues in the driver which make this not work all the time. You need to add the code mentioned above (SPI_IO_C_MX_CS_MODE). This works great for short transactions (like for the SPI ADC on the Linker Mezzanine board), but fails if you build up a long transaction (like reading a block from a SPI EEPROM).

I would recommend you use method 2) but the steps in the how-to are for method 1). In the file kernel/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi add

    &blsp_spi5 {
        /* On Low speed expansion */
            label = "LS-SPI0";
            status = "okay";
   +         cs-gpios = <&msmgpio 18 0>;
            spidev@0 {
                compatible = "spidev";
                spi-max-frequency = <100000>;
                reg = <0>;
            };
    };

Note the addition of the cs-gpios line. You can set the frequency to anything you like.

If you want to try 3) then remove the cs-gpios line, make the kernel change for SPI_IO_C_MX_CS_MODE, and change the following in kernel/arch/arm64/boots/dts/qcom/msm8916-pins.dtsi

        pins = "gpio16", "gpio17", "gpio19";
    };
        pinmux_cs {
 -                       function = "gpio";
 +                       function = "blsp_spi5";
                         pins = "gpio18";
   };

Full disclosure: I am an employee of Qualcomm Canada. Any opinions I may have expressed in this or any other post may not reflect the opinions of my employer.

1 Like

Hello Srini, so where exactly should I add that patch? I’m using the latest Debian (but on Genitech DeveloperBoard4 which is why the DB410c script won’t work).
Can’t find the appropriate position in the device tree and I don’t know why diff --git won’t work -.-
Greatful for any advice :slight_smile:
Miriam