SPI Zynq driver

Introduction

This page provides information about the Zynq/ZynqMP SPI driver which can be found on Xilinx GIT and mainline as spi-cadence.c.
Zynq/ZynqMP has two SPI hard IP. This is a Cadence IP. Some minor properties in the cadence IP offer multiple options which were customized as desirable.
This driver supports master mode and slave modes.
Detailed below are its use two slaves - EEPROM and flash.

HW IP Features

  • Master mode
  • All SPI flash instructions
  • Support for 3 slaves - can be extended using a 3 to 8 decoder
  • Driver uses manual chip select and auto start options
  • Programmable clock frequency, polarity and phase
  • Interrupt support

Missing Features and known Issues/Limitations in Driver

  • None

Important AR links

SPI Linux driver Slave mode support failure - AR-Link

SPI slave devices are not created - AR-Link

Kernel Configuration Options

The following config options need to be enabled:
CONFIG_SPI_CADENCE
It depends on:
CONFIG_SPI_MASTER
CONFIG_ARM


Device-tree

Refer to Documentation/devicetree/bindings/spi/spi-cadence.txt for complete description.
These are some specific points to be noted about the properties:
- compatible string: This can be either cdns or xlnx compatible string.
- num-cs: This is SOC specific - Since this driver is for the cadence IP in the mainline, this option is made available in the devicetree.
- is-decoded-cs: This refers to whether a decoder is used to extend the existing chip selects.
- spi-max-frequency - This should reflect the maximum frequency supported by the slave/master (or) user intends to use.
- partitions - The size and number of these are user configurable and the sum should not exceed the total flash size.

Using SPI with EEPROM

Adding an SPI EEPROM to the devicetree

The following example shows adding an SPI EEPROM to a device tree
spi: spi@e0006000 {
     compatible = "xlnx,spi-zynq-r196";
     clock-names = "ref_clk", "pclk";
     clocks = <&&clkc 25>, <&&clkc 34>;
     interrupt-parent = <&&gic>;
     interrupts = <0 26 4>;
     num-cs = <4>;
     is-decoded-cs = <0>;
     reg = <0xe0006000 0x1000>;
     #address-cells = <1>;
     #size-cells = <0>;
     eeprom: at25@0 {
             compatible = "atmel,at25";
             at25,byte-len = <8192>;
             at25,addr-mode = <2>;
             at25,page-size = <32>;
             reg = <2>;
             spi-max-frequency = <1000000>;
     };
};

Using SPI with flash

Adding an SPI flash to the devicetree

Zynq:

Note: The interrupt number and the controller register space information are mentioned in Zynq-7000 TRM

spi: spi@e0007000 {
     compatible = "cdns,spi-r1p6";
     clock-names = "ref_clk", "pclk";
     clocks = <&&clkc 26>, <&&clkc 35>;
     interrupt-parent = <&&ps7_scugic_0>;
     interrupts = <0 49 4>;
     num-cs = <4>;
     is-decoded-cs = <0>;
     reg = <0xe0007000 0x1000>;
     #address-cells = <1>;
     #size-cells = <0>;
     flash@0 {
             compatible = "sst25wf080";
             reg = <1>;
             spi-max-frequency = <1000000>;
             #address-cells = <1>;
             #size-cells = <1>;
             partition@test {
                            label = "spi-flash";
                            reg = <0x0 0x100000>;
             };
     };
};

ZynqMP:

Note: The interrupt number and the controller register space information are mentioned in Zynq Ultrascale TRM

spi0: spi@ff040000 {
    compatible = "cdns,spi-r1p6";
    interrupt-parent = <&&gic>;
    interrupts = <0 19 4>;
    reg = <0x0 0xff040000 0x0 0x1000>;
    clock-names = "ref_clk", "pclk";
    #address-cells = <1>;
    #size-cells = <0>;
    power-domains = <&&pd_spi0>;
    num-cs = <1>;
    pinctrl-names = "default";
    pinctrl-0 = <&&pinctrl_spi0_default>;
 
    spi0_flash0: spi0_flash0@0 {
            compatible = "m25p80";
        #address-cells = <1>;
        #size-cells = <1>;
        spi-max-frequency = <50000000>;
        reg = <0>;
                spi0_flash0@00000000 {
          label = "spi0_flash0";
          reg = <0x0 0x100000>;
        };
       };
};


Test Procedure

Testing using sysfs

#/
#SPI - eeprom testing
#*/
#check whether eeprom device is present or not .
dmesg | grep eeprom
#read from the EEPROM
cat sys/bus/spi/devices/spi0.2/eeprom
#write to the EEPROM
echo "Hi this is testing the eeprom device through spi interface" > sys/bus/spi/devices/spi0.2/eeprom
#read from the EEPROM to verify
cat sys/bus/spi/devices/spi0.2/eeprom
#/**/

Testing using flashcp

#/*
#*SPI flash testing with flashcp
#*/
#To know MTD partion(partitions) is(are) present and size of partition(partitions)in flash
cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00100000 00001000 "spi-flash"

#Creating a file to be written to the flash
dd if=/dev/urandom of=./sample.bin bs=1024 count=900
900+0 records in
900+0 records out
921600 bytes (900.0KB) copied, 0.706526 seconds, 1.2MB/s

#Write the file to the partition - this erases the partition, writes the file and verifies
flashcp -v ./smaple.bin /dev/mtd0
Erasing block: 225/225 (100%)
Writing kb: 896/900 (99%)
Verifying kb: 896/900 (99%)

Testing using jffs2

#
#/*
#*SPI flash testing with jffs2
#*/
#Erase a the whole partition with jffs2 markers
flash_eraseall -j /dev/mtd0
Erasing 4 Kibyte @ ff000 - 97% complete. Cleanmarker written at ff0000.
Erasing 4 Kibyte @ 100000 - 100% complete
#create a directory
mkdir spi_flash0
#Mount the partition to spi_flash0
mount -t jffs2 /dev/mtdblock0 /spi_flash0
#Creating a file to be written to the flash
dd if=/dev/urandom of=./sample.bin bs=1024 count=900
900+0 records in
900+0 records out
921600 bytes (900.0KB) copied, 0.706526 seconds, 1.2MB/s
#Write the file to the flash
cp ./sample.bin /spi_flash0/
#Check the presence of the file(s) in spi_flash0
ls /spi_flash0
sample.bin
#Unmount
umount spi_flash0
#Mount again - MTD 0 to spi_flash0
mount -t jffs2 /dev/mtdblock0 /spi_flash0
#Compare the files - there should be no differences
diff ./sample.bin /spi_flash0/sample.bin
#Unmount
umount spi_flash0

#The data can be verified again after a power on reset if desired.

Expected Output

jffs2 log:
 
#flash_eraseall -j /dev/mtd0
Erasing 4 Kibyte @ ff000 - 97% complete. Cleanmarker written at ff0000.
Erasing 4 Kibyte @ 100000 - 100% complete
#mkdir spi_flash0
#mount -t jffs2 /dev/mtdblock0 /spi_flash0
#dd if=/dev/urandom of=./sample.bin bs=1024 count=900
900+0 records in
900+0 records out
921600 bytes (900.0KB) copied, 0.706526 seconds, 1.2MB/s
#cp ./sample.bin /spi_flash0/
#ls /spi_flash0
sample.bin
#umount spi_flash0
#mount -t jffs2 /dev/mtdblock0 /spi_flash0
#diff ./sample.bin /spi_flash0/sample.bin
 
 

SPI Slave Support

See the following page: Linux PS SPI Slave Mode.

Mainline Status

In sync with mainline driver.

Change Log

  • 2024.1

    • Summary
      • Reverse the order of interleaved operations
    • Commits
  • 2023.2

    • Summary
      • Add support for Slave mode
      • Fix transfer timeout issue
      • Update irq sequence for read opeation
    • Commits
  • 2023.1

    • Summary
      • Replace all spi->chip_select and spi->cs_gpiod references with function call
    • Commits
  • 2022.2

    • Summary
      • Fix SPI NO Slave Select macro definition
    • Commits
  • 2022.1

    • Summary
      • None
    • Commits
      • None
  • 2021.2

    • Summary
      • Fix SPI CS gets toggling sporadically
      • Align function names
      • Fix kernel-doc format for resume/suspend
    • Commits
  • 2021.1

    • Summary
      • None
    • Commits
      • None

Related Links


© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy