cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Observer
Observer
1,721 Views
Registered: ‎10-02-2015

Configure SPI managed by Zynq PS

Jump to solution

Hi,

I'm using Linux kernel 3.14 on Yocto Fido with meta-xilinx receipes-kernel. I would like to configure the SPI bus managed by the PS of the Zynq.

So far, I described it in the device tree:

ps7_spi_1: ps7-spi@e0007000 {
    spidev0: spidev@0 {
        compatible = "linux,spidev";
        spi-max-frequency = <100000>;
        reg = <0>;
    };
    spidev1: spidev@1 {
        compatible = "linux,spidev";
        spi-max-frequency = <100000>;
        reg = <1>;
    };
    spidev2: spidev@2 {
        compatible = "linux,spidev";
        spi-max-frequency = <100000>;
        reg = <2>;
    };
};

I tested the driver by sending the content of a test file:

cat test_file > /dev/spidev32766.0

and it is working. The only problems are the following:

1) The actual clock frequency is 500kHz, which is greater than the 100kHz specified with spi-max-frequency.

2) The driver is using CPOL = 1 and CPHA = 1 (clock with inverted polarity and data shifted out at the falling edge) and I don't know how to change this settings. I would like to set CPOL = 0 and CPHA = 0. I looked at the documentation in (https://github.com/Xilinx/linux-xlnx/tree/master/Documentation/devicetree/bindings/spi) and tried adding the properties spi-cpol = <0> and spi-cpha = <0>, but it didn't work out.

The clock frequency is not a real issue, even if I would like to be able to fix it (maybe increase it). My main concern is: how can I change CPOL and CPHA settings?

Thanks in advance,

Pietro

0 Kudos
1 Solution

Accepted Solutions
Highlighted
7 Replies
Highlighted
Scholar
Scholar
1,693 Views
Registered: ‎05-28-2013

Prior to writing your data, an ioctl() call should be performed, to configure speed, CPOL/CPHA, and so on.

 

For details have a look at the transfer() function in the spidev_test.c sample code, which is included in the linux kernel source code, despite being a userspace program. https://github.com/torvalds/linux/blob/master/tools/spi/spidev_test.c#L113

 

See also the ioctls for setting spi mode/bits/speed in the same program, starting on line 415. https://github.com/torvalds/linux/blob/master/tools/spi/spidev_test.c#L415

 

(edit: added 2nd ioctl info)

 

0 Kudos
Highlighted
Observer
Observer
1,663 Views
Registered: ‎10-02-2015

Thanks @rfs613  for the quick reply.

Here is a small peice of code of what I did:

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define IO_EXT_OUTPUTS_PATH "/dev/spidev32766.0"  // IC91

int io_ext_new_init (int fd){
    int ret = 0;
    __u8 mode_only = SPI_MODE_0; // 0
    uint32_t speed = 100000;
    if (ret) return ret;
    ret = ioctl(fd, SPI_IOC_WR_MODE, &mode_only);
    if (ret) return ret;
    ret = ioctl(fd, SPI_IOC_RD_MODE, &mode_only);
    if (ret) return ret;
    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret) return ret;
    ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (ret) return ret;
    return ret;

}

int io_ext_write(void) {
	int ret;
	char buf[2];

	int fd = open(IO_EXT_OUTPUTS_PATH, O_RDWR);

	if (fd < 0) {
		return -1;
	}

    ret = io_ext_new_init(fd);
    if (ret) return ret;

	buf[0] = 0xAA;
	buf[1] = 0xFF;

    struct spi_ioc_transfer tr;

    tr.tx_buf = (unsigned long)buf;
    tr.len = 2;
    tr.speed_hz = 100000;

    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

    if (ret != 2) {
        close(fd);
        return -3;
    }

	close(fd);

	return 0;
}

In the main function I call io_ext_write().

The SPI is working, but at 500kHz and with CPOL=1;

If I read register at 0xe0007000, which is the SPI control register, I get 0x7C39, which means that CPOL and CPHA are both 0 (https://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf). Nevertheless, the clock line is high.

If I try to manually manage the SPI, i.e. by directly writing to the registers 0xe0007000-0xe000701C, I can get the correct transmission, with the clock idle low.

After a "manual" transmission, the clock is low, but if I then disable the manual start (by clearing bits 14 and 15 of control register) and call again io_ext_write(), the clock idle state is high again.

0 Kudos
Highlighted
Observer
Observer
1,634 Views
Registered: ‎10-02-2015

UPDATE:

I cloned the github project (https://github.com/Xilinx/linux-xlnx) and checkout the branch xlnx_3.14, which is the actual version of the Kernel I'm using. I compiled the spidev_test.c file and run it. I found that the problem was not related to the CPOL settings, as I was thinking before, but it was due to the SPI Controller Enable (bit 0 @0xE0007014). During the transmission, the clock polarity is correct, but after the transmission, the clock line is pulled-up (I don't  know why) by a roughly 15kohm resistor.

During the manual tests I did few days ago, I was keeping the SPI Controller always enabled, therefore I didn't see that behaviour. Today I repeated the manual test and disabled the SPI Controller after the transmission. As soon as I disabled it, the clock line went high again. If I toggle the enable bit, I can see the clock line toggling.

So, the problem now is: why does this line go high when the SPI controller is disabled? How can I avoid this?

Since I'm using the Zynq SPI to configure an FPGA, this high state on the clock line between different groups  of words makes the FPGA see an unwanted rising edge, which causes a 1-bit shift in the bitstream.

Possible solutions/workarounds can be:

1) Use an external 1kohm pull-down resistor on the clock line. I did this and it worked out: I could configure the FPGA.

2) Find a way to tell the software not to disable the SPI controller during the transmission. All the software methods I used (cat / write() / ioctl()) split the transmission into groups of words and disable the SPI controller between different groups. This would be the preferred solution, but I don't know how to do it.

Does someone know how to solve this?

0 Kudos
Highlighted
Scholar
Scholar
1,622 Views
Registered: ‎05-28-2013

During the transmission, the clock polarity is correct, but after the transmission, the clock line is pulled-up (I don't  know why) by a roughly 15kohm resistor.

There is a configurable pull-up on each of the MIO pins, perhaps it is set incorrectly? You can check it via software, read from register 0xF8000700 + 4 * MIO (one register for each MIO pin). Look at bit[12] which controls the pullup.

0 Kudos
Highlighted
Observer
Observer
1,615 Views
Registered: ‎10-02-2015

There is a configurable pull-up on each of the MIO pins, perhaps it is set incorrectly? You can check it via software, read from register 0xF8000700 + 4 * MIO (one register for each MIO pin). Look at bit[12] which controls the pullup.


I checked bit[12] and it is 0, so no pull-up is enabled.

I've just done another test leaving the clock pin floating, i.e. without connecting this line to the Spartan 7 FPGA that I need to program (the clock line was connected to the CCLK_0 pin). This time I do not see the strange behaviour, therefore I think that it is a problem related to the Spartan 7 and not to the Zynq.

The Spartan 7 has the M[2:0] pins set to '111' for the Slave Serial Interface. In UG 470 (https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf), in the Overview section I found:

  • In Master configuration modes, the 7 series device drives CCLK from an internal oscillator. To select the desired frequency, the bitstream -g ConfigRate option is used. The BitGen section of UG628, ISE Command Line Tools User Guide provides more information for the ISE Design Suite. The Device Configuration Bitstream Settings section of UG908, Vivado Programming and Debugging User Guide provides more information for the Vivado Design Suite. After configuration, the CCLK is turned OFF unless the persist option is selected or SEU detection is used. See Persist Option in Chapter 6. The CCLK pin is 3-stated with a weak pull-up.
  • In Slave configuration modes, CCLK is an input.

Even if the Spartan 7 is in Slave configuration mode (M[2:0] = SlaveSerial) and CCLK should be an input, I think this pin has an internal weak pull-up (about 15 kohm). This could explain all the thing that I saw.

0 Kudos
Highlighted
Scholar
Scholar
1,609 Views
Registered: ‎05-28-2013

Even if the Spartan 7 is in Slave configuration mode (M[2:0] = SlaveSerial) and CCLK should be an input, I think this pin has an internal weak pull-up (about 15 kohm). This could explain all the thing that I saw.


Sounds like a possible explanation... although it does seem unusual, especially if CCLK must remain low when inactive.


Maybe check in one of the more hardware-oriented forums...

0 Kudos
Highlighted