![](https://static.wixstatic.com/media/3b5532_877bfd0ced8c482b9cb70e4e05f9b95a~mv2.png/v1/fill/w_225,h_225,al_c,q_85,enc_auto/3b5532_877bfd0ced8c482b9cb70e4e05f9b95a~mv2.png)
This post walks through part 1 of a complete integration of a QSPI connected to a Zynq UltraScale+ MPSoC into a Linux kernel using PetaLinux Tools 2017.4.
It traces the connection from a QSPI chip to the QSPI controller on the Zynq UltraScale+ MPSoC (ZU+). Then its demonstrates checking the Linux kernel software layers to ensure the right configuration has been set up.
I posted the solution to https://www.zachpfeffer.com/single-post/Integrate-a-QSPI-using-PetaLinux-Tools-Part-2.
QSPI to ZU+ Connection
Detail
MT25QL01GBBB8E12 -0AA connected to a XCZU9EG-FFVC900
Connections (QSPI connected to ZU+)
D3 connected to G18
D2 connected to H17
C4 connected to E18
D4 connected to F18
C2 connected to J17
B2 connected to C18
Figure Out Pins
QSPI Pins
Per this figure from the MT25QL01GBBB datasheet, the package code is 12 (MT25QL01GBBB8E12 -0AA): a 12 = 24-ball T-PBGA, 05/6 x 8mm (5 x 5 array)
![](https://static.wixstatic.com/media/3b5532_5dc5701175494df2a2109ceb0c22fb02~mv2.png/v1/fill/w_842,h_574,al_c,q_90,enc_auto/3b5532_5dc5701175494df2a2109ceb0c22fb02~mv2.png)
Pins
MT25QXXXXXXX8E12-XXXX Balls Down
![](https://static.wixstatic.com/media/3b5532_8fc2de66cba64dcaba38178ba034ab43~mv2.png/v1/fill/w_668,h_722,al_c,q_90,enc_auto/3b5532_8fc2de66cba64dcaba38178ba034ab43~mv2.png)
Connections
D3 - DQ0,
D2 - DQ1,
C4 - W#/DQ2,
D4 - DQ3/HOLD#,
C2 - S#,
B2 - C
From the QSPI data sheet:
DQ[3:0] (I/O)
Serial I/O: The bidirectional DQ signals transfer address, data, and command information.
When using legacy (x1) SPI commands in extended I/O protocol (XIO-SPI), DQ0 is an input and DQ1 is an output. DQ[3:2] are not used.
When using dual commands in XIO-SPI or when using DIO-SPI, DQ[1:0] are I/O. DQ[3:2] are not used.
When using quad commands in XIO-SPI or when using QIO-SPI, DQ[3:0] are I/O.
C (Input)
Clock: Provides the timing of the serial interface. Command inputs are latched on the rising edge of the clock. In STR commands or protocol, address and data inputs are latched on the rising edge of the clock, while data is output on the falling edge of the clock. In DTR commands or protocol, address and data inputs are latched on both edges of the clock, and data is output on both edges of the clock.
S#
Chip select: When S# is driven HIGH, the device will enter standby mode, unless an internal PROGRAM, ERASE, or WRITE STATUS REGISTER cycle is in progress. All other input pins are ignored and the output pins are tri-stated. On parts with the pin configuration offering a dedicated RESET# pin, however, the RESET# input pin remains active even when S# is HIGH.
Driving S# LOW enables the device, placing it in the active mode.
After power-up, a falling edge on S# is required prior to the start of any command.
ZU+ Pins
Per this figure from the XCZU9EG-FFVC900 datasheet, we're using the FFVC900 package.
G18, H17, E18, F18, J17, C18 are all in PS Bank 500
![](https://static.wixstatic.com/media/3b5532_5e572e150b254f33bf75ec319a350495~mv2.png/v1/fill/w_853,h_957,al_c,q_90,enc_auto/3b5532_5e572e150b254f33bf75ec319a350495~mv2.png)
Pin Out
Pin Pin Name Memory Byte Group Bank I/O Type Super Logic Region
G18 PS_MIO4 NA 500 PSMIO NA
H17 PS_MIO1 NA 500 PSMIO NA
E18 PS_MIO2 NA 500 PSMIO NA
F18 PS_MIO3 NA 500 PSMIO NA
J17 PS_MIO5 NA 500 PSMIO NA C18 PS_MIO0 NA 500 PSMIO NA
Overlayed
PS_MIO4 - DQ0,
PS_MIO1 - DQ1,
PS_MIO2 - W#/DQ2,
PS_MIO3 - DQ3/HOLD#,
PS_MIO5 - S#,
PS_MIO0 - C
From Table 28-3 MIO Interfaces (qspi column) of the Zynq UltraScale+ Device TRM:
PS_MIO4 - DQ0 - si_mio[0]
PS_MIO1 - DQ1 - io[1]
PS_MIO2 - W#/DQ2 - io[2]
PS_MIO3 - DQ3/HOLD#, - io[3]
PS_MIO5 - S# - n_ss_out
PS_MIO0 - C - sclk_out
From page 646 of the Zynq UltraScale+ Device TRM:
![](https://static.wixstatic.com/media/3b5532_4a00b2cf529f4eba843b63d7a6474635~mv2.png/v1/fill/w_881,h_661,al_c,q_90,enc_auto/3b5532_4a00b2cf529f4eba843b63d7a6474635~mv2.png)
Also from page 646 of the Zynq UltraScale+ Device TRM:
The following interfaces are used by the generic Quad-SPI controller.
• The APB slave read/write interface is used to read/write the registers and also to write the TX generic FIFO data.
• The AXI master write interface is used to issue DMA write requests on the AXI interface. The data read from flash memory is written into the RXFIFO. The data from the RXFIFO is transferred into external memory (for example, DDR) using this interface. The AXI address bus is 44 bits wide and the data is 32 bits wide.
Base Address of QSPI
From Table 10-6 (p232): I/O Peripherals Register Map (LPD) Base Address of the QSPI controller is: 0xFF0F_0000
Am I Using LQSPI vs. GQSPI?
Using GQSPI as Linux boots up. Register is unchanged.
Detail
From page 640 from the Zynq UltraScale+ Device TRM:
At Boot
The CSU BootROM uses the GQSPI controller for system boot. The Width Detection parameter in the boot header selects between 4- and 8-bit I/O. If the XIP FSBL is selected (FSBL length = 0 in the boot header), the BootROM switches to the LQSPI controller before handing-off the system to the FSBL code.
From page 643 from the Zynq UltraScale+ Device TRM:
Controller Selection
One controller is selected at a time using the LQSPI_CFG [LQ_MODE] bit. The generic controller is selected by setting the bit = 0 and the legacy linear controller is selected by setting the bit = 1. The active controller must be quiescent before switching from one controller to the other.
Check the Register Spec
Register Name LQSPI_CFG Relative Address 0x000000A0 Absolute Address 0xFF0F00A0 (QSPI) Width 32 Type rw Reset Value 0x000002EB Description Configuration Specifically for the Linear Quad-SPI Controller
Decode 0x000002EB
0x000002EB (Reset value)
0b0000_0000_0000_0000_0000_0010_1110_1011
LQ_MODE: Controller Select 0: Generic Quad-SPI
TWO_MEM: I/O Configuration: 0: One memory device
SEP_BUS: I/O Configuration: 0: Single memory interface
U_PAGE: N/A
ADDR_32BIT: Flash Memory Address based on AXI address: 0: Lower 24 bits of AXI address on linear port are used as address to the flash.
MODE_EN: MODE_ON and MODE_BITS are disabled
INST_CODE: Fast read quad I/O
Can my Kernel See the MT25QL01GBBB8E12 -0AA?
In a PetaLinux Tools managed build the Linux kernel code is at build/tmp/work-shared/plnx_aarch64/kernel-source/.
The SPI-NOR driver is at drivers/mtd/spi-nor/spi-nor.c
The specific spi-nor.c used in PetaLinux Tools 2017.4 is at: https://github.com/Xilinx/linux-xlnx/blob/b450e900fdb473a53613ad014f31eedbc80b1c90/drivers/mtd/spi-nor/spi-nor.c
SPI NOR framework
Check Kconfig @ https://github.com/Xilinx/linux-xlnx/blob/b450e900fdb473a53613ad014f31eedbc80b1c90/drivers/mtd/spi-nor/Kconfig
menuconfig MTD_SPI_NOR tristate "SPI-NOR device support" depends on MTD help This is the framework for the SPI NOR which can be used by the SPI device drivers and the SPI-NOR device driver.
Check the PetaLinux kernel config:
![](https://static.wixstatic.com/media/3b5532_a84079cb292f4729b2472b6deeba05e4~mv2.png/v1/fill/w_718,h_432,al_c,q_85,enc_auto/3b5532_a84079cb292f4729b2472b6deeba05e4~mv2.png)
Note: this command will wipe out the kernel source in build/tmp/work-shared/plnx_aarch64/kernel-source
Note 2: PetaLinux 2017.4 uses https://github.com/Xilinx/linux-xlnx/tree/b450e900fdb473a53613ad014f31eedbc80b1c90 See this link to for a method to figure this out.
Type '/' to search for MTD_SPI_NOR
![](https://static.wixstatic.com/media/3b5532_ce059a039d154b129152fbffd46c0172~mv2.png/v1/fill/w_722,h_435,al_c,q_85,enc_auto/3b5532_ce059a039d154b129152fbffd46c0172~mv2.png)
Its on:
![](https://static.wixstatic.com/media/3b5532_ed350cf603a54fa8aa4229db97290d28~mv2.png/v1/fill/w_717,h_568,al_c,q_90,enc_auto/3b5532_ed350cf603a54fa8aa4229db97290d28~mv2.png)
Look at dmesg
[ 1.636157] mtdoops: mtd device (mtddev=name/number) must be supplied
Check device tree overlay
Hmmm.. this is looking for zynq-qspi-1.0, I'm passing in: zynqmp-qspi-1.0
Check the original (non-overlay) device tree
system-top.dts pulls in all the dtsi components:
Contents:
So zynqmp-qspi-1.0 seems okay.
Configure flash through petalinux-config
![](https://static.wixstatic.com/media/3b5532_a9cc56db3db940318ef1fe459f58a17a~mv2.png/v1/fill/w_669,h_516,al_c,q_85,enc_auto/3b5532_a9cc56db3db940318ef1fe459f58a17a~mv2.png)
Select Subsystem AUTO Hardware Settings
![](https://static.wixstatic.com/media/3b5532_192d50a7680b410f8c9a7956d5b42fa7~mv2.png/v1/fill/w_664,h_515,al_c,q_85,enc_auto/3b5532_192d50a7680b410f8c9a7956d5b42fa7~mv2.png)
Select Flash Settings --->
![](https://static.wixstatic.com/media/3b5532_5fb315ca695544999b77c94c75908f48~mv2.png/v1/fill/w_665,h_521,al_c,q_85,enc_auto/3b5532_5fb315ca695544999b77c94c75908f48~mv2.png)
Configure
![](https://static.wixstatic.com/media/3b5532_6f3818f6e61448e8b8393350d6eff037~mv2.png/v1/fill/w_664,h_524,al_c,q_85,enc_auto/3b5532_6f3818f6e61448e8b8393350d6eff037~mv2.png)
Use some printks
Launch devshell
petalinux-build -c kernel -x devshell
(this command is not documented anywhere - it also doesn't seem to work: it executed for 2 hours)
cat /proc/devices
Printk Based Debug
Go through the SPI-NOR Linux kernel doc and see if functions are getting called (via printks)
SPI NOR framework ============================================
Part I - Why do we need this framework? ---------------------------------------
SPI bus controllers (drivers/spi/) only deal with streams of bytes; the bus
The Xilinx Zynq UltraScale+ MPSoC Quad-SPI (QSPI) controller driver (master mode only) is @ link.
Which Kconfig is needed?
Look at the Makefile
Found: Is the Kconfig on?
Output the current .config with
Grep it: Yup.
Does probe get called (and succeed)?
Add a printk and a dump_stack():
Code added:
Rebuild the kernel with:
Checked kernel log. No output.
Did the kernel get rebuilt?
No:
Should be May 13th.
Check the reference guide. Nothing.
Check the workflow guide. Nothing.
Try petalinux-build --help. Saw:
Output from command:
Output:
its in there:
Hmm,.. need to do a petalinux-build again?
Hmmmm...petalinux-build is building a lot of stuff:
Comment: It is surprising that petalinux-build -c kernel -x compile -f didn't put an Image into images/linux/Image
Comment: It is surprising that a subsequent petalinux-build took another few minutes to run.
Yes, probe gets called and succeeds.
controller operates agnostic of the specific device attached. However, some controllers (such as Freescale's QuadSPI controller) cannot easily handle arbitrary streams of bytes, but rather are designed specifically for SPI NOR.
In particular, Freescale's QuadSPI controller must know the NOR commands to find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of opcodes, addresses, or data payloads; a SPI controller simply knows to send or receive bytes (Tx and Rx). Therefore, we must define a new layering scheme under which the controller driver is aware of the opcodes, addressing, and other details of the SPI NOR protocol.
Part II - How does the framework work? --------------------------------------
This framework just adds a new layer between the MTD and the SPI bus driver. With this new layer, the SPI NOR controller driver does not depend on the m25p80 code anymore.
Before this framework, the layer is like:
MTD ------------------------ m25p80 ------------------------ SPI bus driver ------------------------ SPI NOR chip
After this framework, the layer is like:
MTD ------------------------ SPI NOR framework ------------------------ m25p80 ------------------------ SPI bus driver ------------------------ SPI NOR chip
With the SPI NOR controller driver (Freescale QuadSPI), it looks like:
MTD ------------------------ SPI NOR framework ------------------------ fsl-quadSPI ------------------------ SPI NOR chip
Part III - How can drivers use the framework? ---------------------------------------------
The main API is spi_nor_scan(). Before you call the hook, a driver should initialize the necessary fields for spi_nor{}. Please see
Does spi_nor_scan() get called?
Look for it:
Output:
Who calls it:
Output:
Are any of these built in?
Kconfigs?
mtk-quadspi|fsl-quadspi|nxp-spifi|atmel-quadspi|hisi-sfc|cadence-quadspi|m25p80
Check the .config:
Output:
So nothing will call spi_nor_scan()
drivers/mtd/spi-nor/spi-nor.c for detail. Please also refer to fsl-quadspi.c when you want to write a new driver for a SPI NOR controller. Another API is spi_nor_restore(), this is used to restore the status of SPI flash chip such as addressing mode. Call it whenever detach the driver from device or reboot the system.
Do I need a special driver / aren't all these JEDEC? drivers? Wait? I don't need a new SPI NOR controller, do I?
Look for n25q
Output:
jedec,spi-nor
It looks like we should just use the string "jedec,spi-nor" from this:
Kconfig for m25p80:
Open:
Find:
Check if CONFIG_MTD_M25P80 is in our .config, Yes!
Find it:
Edit:
Change to:
Some output:
Check if system.dtb got updated:
Got it!
Now I see the following in the dmesg log:
Partitions?
I don't think I need the following, since it simply finds? the actual partitions:
Check in doc at
I don't see anything about partitions. Try without:
Check that the dtb was updated:
Boot the unit.
Examine log:
Without the specific partitions, it seems to have found up just the partitions on the device.
Does it find the partitions or are the partitions picked up in another dts?
Look at docs again. Partitions are documented at link.
Look in our directory:
Look for "partitions" in "dt*" files:
Found a few:
partition@0x00000000
How are all the dtsi's composed?
system-top.dts pulls in all the dtsi components:
Contents:
zynqmp.dtsi
./components/plnx_workspace/device-tree/device-tree-generation/zynqmp.dtsi ./build/tmp/work-shared/plnx_aarch64/kernel-source/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
These ^^^^ differ, e.g. iommus are commented out in ./build. Which one is used?
zynqmp-clk-ccf.dtsi
./components/plnx_workspace/device-tree/device-tree-generation/zynqmp.dtsi
./build/tmp/work-shared/plnx_aarch64/kernel-source/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
These ^^^^ differ, e.g. iommus are commented out in ./build. Which one is used?
pl.dtsi
./components/plnx_workspace/device-tree/device-tree-generation/pl.dtsi
pcw.dtsi
./components/plnx_workspace/device-tree/device-tree-generation/pcw.dtsi
system-user.dtsi
./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
Look at the flattened device tree (PETALINUX is your install dir)
Find dtc
Print dtb
Hmmm... it looks like my dtb changes were not picked up:
Complete:
Try "petalinux-build" again
Didn't work (should be May 14th):
Hack to try it out:
Hmmm, still present.
How does it get in?
Its not in the top level of any of these:
Found here among other places:
File contents:
This file gets included at the top of ./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi:
Look at device-tree.bb
Remove via petalinux-config
petalinux-config
![](https://static.wixstatic.com/media/3b5532_6ee7bdc40c7747f2b5297f8e77e33eb7~mv2.png/v1/fill/w_622,h_363,al_c,q_85,enc_auto/3b5532_6ee7bdc40c7747f2b5297f8e77e33eb7~mv2.png)
![](https://static.wixstatic.com/media/3b5532_b6703c3b234e4ecd91cd35eb635a2843~mv2.png/v1/fill/w_653,h_376,al_c,q_85,enc_auto/3b5532_b6703c3b234e4ecd91cd35eb635a2843~mv2.png)
Change the name to "boot_me" to test how this flows through:
![](https://static.wixstatic.com/media/3b5532_185d255337424f5f86afc9ca0fed0762~mv2.png/v1/fill/w_353,h_286,al_c,q_85,enc_auto/3b5532_185d255337424f5f86afc9ca0fed0762~mv2.png)
Examine it:
Saw "boot_me"
So I "can" and "should" use petalinux-config to specify the partitions.
How do I figure out the name's and sizes of the partitions from the BOOT.bin or bootimage.bif?
bootimage.bif:
Can I look in the BOOT.bin?
I could match the first bytes of each image. No,
I can get all the partitions by passing -log trace to bootgen:
How can I get rid of PetaLinux managed partitions?
Select Manual:
![](https://static.wixstatic.com/media/3b5532_6ee7bdc40c7747f2b5297f8e77e33eb7~mv2.png/v1/fill/w_622,h_363,al_c,q_85,enc_auto/3b5532_6ee7bdc40c7747f2b5297f8e77e33eb7~mv2.png)
![](https://static.wixstatic.com/media/3b5532_b6703c3b234e4ecd91cd35eb635a2843~mv2.png/v1/fill/w_653,h_376,al_c,q_85,enc_auto/3b5532_b6703c3b234e4ecd91cd35eb635a2843~mv2.png)
![](https://static.wixstatic.com/media/3b5532_507f26e6da3b452a8baec232f2faee56~mv2.png/v1/fill/w_654,h_258,al_c,q_85,enc_auto/3b5532_507f26e6da3b452a8baec232f2faee56~mv2.png)
![](https://static.wixstatic.com/media/3b5532_83e2aac9201d48d091b0683df2efc245~mv2.png/v1/fill/w_651,h_303,al_c,q_85,enc_auto/3b5532_83e2aac9201d48d091b0683df2efc245~mv2.png)
Run: petalinux-build
Check with:
Output:
Partitions are gone!
My kernel now fails to boot!
Its hung at:
Add the following:
To: ./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
Force build the device-tree:
Run petalinux-build again so that the system.dtb gets updated:
Check
Boot
Still hung.
Try turning partitions back on. Maybe bootenv needs to be defined?
After using:
The kernel boots again. Why?
We see:
General flash / mtd debug:
Leaving things here.
References
Zynq UltraScale+ MPSoC Register Reference UG1087 (v1.5) December 21, 2017 @ link
Zynq UltraScale+ MPSoC Technical Reference Manual @ link
Zynq UltraScale+ Device Packaging and Pinouts Product Specification User Guide @ link
Zynq UltraScale+ MPSoC Package Device Pinout Files @ link
Micron's MT25QL01GBBB datasheet @ link
MTD SPI NOR framework Linux kernel documentation @ link
MTD device tree bindings @ link
Xilinx logo from https://twitter.com/xilinxinc at link
printk-formats @ link