cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
jjcarrier
Visitor
Visitor
7,318 Views
Registered: ‎01-03-2013

Using Zynq's SPI0 with "is-decoded-cs" in zynq-linux

Hello,
I am tring to configure a Zynq xc7z010-1 to use the SPI0 peripheral in decoder mode so that I can control upto 8 SPI slaves with the SPI0 slave select signals. My target is running zynq-linux with the 3.15.0-xilinx kernel. I have also enabled the use of the spidev kernel module. Also for what it is worth I am running Vivado 2014.4

 

I have a basic 3-to-8 decoder hdl module (with tri-state) which connects to the processing system's SPI_0_ss_o/SPI_0_ss1_o/SPI_0_ss2_o and the SPI_0_ss_t signals. I also made sure I followed AR# 47511 (though I am not sure if this still applies in Vivado 2014.4)

 

I have updated the zynq-linux device-tree to contain the node below:

 

spi0: spi@e0006000 {
compatible = "xlnx,zynq-spi-r1p6";
reg = <0xe0006000 0x1000>;
status = "okay";
interrupt-parent = <&intc>;
interrupts = <0x0 0x1a 0x4>;
clocks = <&clkc 0x19 &clkc 0x22>;
clock-names = "ref_clk", "pclk";
#address-cells = <0x1>;
#size-cells = <0x0>;
num-cs = <8>;
is-decoded-cs = <1>;
spidev@0 {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <50000000>;
};
spidev@1 {
compatible = "spidev";
reg = <1>;
spi-max-frequency = <50000000>;
};
spidev@2 {
compatible = "spidev";
reg = <2>;
spi-max-frequency = <50000000>;
};
spidev@3 {
compatible = "spidev";
reg = <3>;
spi-max-frequency = <50000000>;
};
spidev@4 {
compatible = "spidev";
reg = <4>;
spi-max-frequency = <50000000>;
};
spidev@5 {
compatible = "spidev";
reg = <5>;
spi-max-frequency = <50000000>;
};
spidev@6 {
compatible = "spidev";
reg = <6>;
spi-max-frequency = <50000000>;

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

 

Using the kernel module for "spidev" and the standard spidev demo found in the kernel sources I tested out the SS functionality. I added debug printk's to the spi-cadence.c so that I could monitor the control register values (CDNS_SPI_CR_OFFSET) which contain the 4-bit chip select field. Here is what I observed:

 

Writing to spidev:
- spidev@0 asserts the decoded SS6 (CDNS_SPI_CR_OFFSET reads: 0x00004029)
- spidev@1 asserts the decoded SS5 (CDNS_SPI_CR_OFFSET reads: 0x00004429)
- spidev@2 asserts the decoded SS6 (CDNS_SPI_CR_OFFSET reads: 0x00004829)
- spidev@3 asserts the decoded SS3 (CDNS_SPI_CR_OFFSET reads: 0x00004C29)
- spidev@4 asserts the decoded SS6 (CDNS_SPI_CR_OFFSET reads: 0x00005029)
- spidev@5 asserts the decoded SS5 (CDNS_SPI_CR_OFFSET reads: 0x00005429)
- spidev@6 asserts the decoded SS6 (CDNS_SPI_CR_OFFSET reads: 0x00005829)
- spidev@7 asserts the decoded SS7 (CDNS_SPI_CR_OFFSET reads: 0x00005C29)
- when the spi bus is inactive CDNS_SPI_CR_OFFSET reads 0x00007C29

As you can see this behavior is not correct.


I have also verified my 3-to-8 decoder in this design by using a VIO core and multiplexing between letting the SPI controller drive the decoder or letting the VIO outputs drive the decoder. But clearly because the control registers mentioned above do not correlate correctly with the /dev/spidevX.Y devices it definatly seems to be more of a software related issue.

 

The Zynq's TRM states that it supports this functionality but I have not seen much information on the actual implementation of it. Has anyone successully used the Zynq's SPI (spi-cadence) controller in "is-decoded-cs = <1>;" mode?

 

SIDENOTE #1:

Also one other observation I notice is that it seems the SPI's SS tristate output signal is deasserted before the slave select signals actually change. This seems problematic to me. Consider the SS is to be decoded externally to SS5; because of this behavior the following transitions will be observed:


SPI_0_ss_t ,SPI_0_ss_o,SPI_0_ss1_o,SPI_0_ss2_o

1,1,1,1 //SPI bus idle
0,1,1,1 //Controller un-intentionally selects SS7
0,1,0,1 //Controller selects SS5
0,1,1,1 //Controller un-intentionally selects SS7
1,1,1,1 //SPI bus idle

Is there a recommended way to mitigate this behavior?

 

SIDENOTE #2:

Also I have used "is-decoded-cs = <0>;" and can say that this mode works correctly for all 3 slave selects (though I did not pay attention to the timing compared to SPI_0_ss_t).

0 Kudos
2 Replies
trenz-al
Scholar
Scholar
7,309 Views
Registered: ‎11-09-2013

we are about to check this, but not yet.

 

I am not surprised, it could be the software is never fully tested for the decoded case

0 Kudos
jjcarrier
Visitor
Visitor
7,263 Views
Registered: ‎01-03-2013

Update:

I have determined a fix for my issue.

 

After succesfully driving the SPI controller in bare-metal using the decoded cs feature, I was able to determine that the issue is likely a logical issue in the way the controller is initialize/re-initialized.

 

The kernel uses the following default config/control register:

 

#define CDNS_SPI_CR_DEFAULT_MASK (CDNS_SPI_CR_MSTREN_MASK | \
CDNS_SPI_CR_SSCTRL_MASK | \
CDNS_SPI_CR_SSFORCE_MASK | \
CDNS_SPI_CR_BAUD_DIV_4_MASK)

 

By changing this definition to:

#define CDNS_SPI_CR_DEFAULT_MASK (CDNS_SPI_CR_MSTREN_MASK | \
CDNS_SPI_CR_SSCTRL_MASK | \
CDNS_SPI_CR_SSFORCE_MASK | \
CDNS_SPI_CR_SSDECEN_MASK | \
CDNS_SPI_CR_BAUD_DIV_4_MASK)

 

where "CDNS_SPI_CR_SSDECEN_MASK" is defined as:

#define CDNS_SPI_CR_SSDECEN_MASK 0x00000200 /* Slave Select Decode Enable */

 

I am able to get the controller to function properly in decoded cs mode.

 

In comparison to the previous post, when the spi bus is "inactive" CDNS_SPI_CR_OFFSET now reads 0x00007E39 instead of 0x00007C29.

 

I can also say that after making this change I can now switch between is-decoded-cs = 0 and 1.