AM62P: AM62P SKEVM OSPI BOOT fail , stop at QSPI: QSPI is still busy after poll for 5000 ms

Part Number: AM62P

Tool/software:

Hi  Ti

Now I use OSPI boot mode 

1、I use dfu  flash the spi flash 

# These first set of steps are optional if you have u-boot running on the board already
HOST $ sudo dfu-util -l
HOST $ sudo dfu-util -R -a bootloader -D <PATH_TO_BIN>/tiboot3.bin
HOST $ sudo dfu-util -R -a tispl.bin -D <PATH_TO_BIN>/tispl.bin
HOST $ sudo dfu-util -R -a u-boot.img -D <PATH_TO_BIN>/u-boot.img
# At this point, the u-boot will start executing. Halt at the u-boot prompt (u-boot logs will
appear on the MAIN UART 1st instance)
TARGET => env default -f -a
TARGET => saveenv
TARGET => setenv dfu_alt_info ${dfu_alt_info_ospi}
TARGET => dfu 0 sf 0:0
# This does the actual flashing to OSPI flash
HOST $ sudo dfu-util -l
HOST $ sudo dfu-util -a tiboot3.bin -D <PATH_TO_BIN>/tiboot3.bin
HOST $ sudo dfu-util -a tispl.bin -D <PATH_TO_BIN>/tispl.bin
HOST $ sudo dfu-util -a u-boot.img -D <PATH_TO_BIN>/u-boot.img

it is ok 

2、 Then  I change boot mode use OSPI boot mode 

the board boot failed , it stop at QSPI: QSPI is still busy after poll for 5000 ms.

3 、there are the flash log and boot log , you  can check it 

U-Boot SPL 2025.01-gd2a72467939e (Aug 04 2025 - 12:05:11 +0800)
SYSFW ABI: 4.0 (firmware rev 0x000b '11.1.2--v11.01.02 (Fancy Rat)')
SPL initial stack usage: 17088 bytes
Trying to boot from DFU
dwc3-am62 usb@f900000: unable to get ti,syscon-phy-pll-refclk regmap
#######################################################################DOWNLOAD ... OK
Ctrl+C to exit ...
#######################################################################DOWNLOAD ... OK
Ctrl+C to exit ...
Authentication passed
Authentication passed
Authentication passed
Loading Environment from nowhere... OK
init_env from device 10 not supported!
Authentication passed
Authentication passed
Starting ATF on ARM64 core...

NOTICE:  BL31: v2.13.0(release):v2.13.0-240-gd90bb650fe-dirty
NOTICE:  BL31: Built : 21:37:18, Jun 23 2025

U-Boot SPL 2025.01-gd2a72467939e (Aug 04 2025 - 12:05:43 +0800)
SYSFW ABI: 4.0 (firmware rev 0x000b '11.1.2--v11.01.02 (Fancy Rat)')
DM ABI: 3.0 (firmware ver 0x000b 'MSDK.11.01.00.05-dirty--v11.01.02' patch_ver: 2)
SPL initial stack usage: 1984 bytes
MMC: no card present
** Bad device specification mmc 1 **
Couldn't find partition mmc 1:1
Error: could not access storage.
Trying to boot from DFU
dwc3-am62 usb@f900000: unable to get ti,syscon-phy-pll-refclk regmap
######DOWNLOAD ... OK
Ctrl+C to exit ...
Authentication passed
Authentication passed


U-Boot 2025.01-gd2a72467939e (Aug 04 2025 - 12:05:43 +0800)

SoC:   AM62PX SR1.0 HS-FS
Model: Texas Instruments AM62P5 SK
DRAM:  2 GiB (total 8 GiB)
Core:  97 devices, 32 uclasses, devicetree: separate
MMC:   mmc@fa10000: 0, mmc@fa00000: 1
Loading Environment from nowhere... OK
In:    serial
Out:   serial
Err:   serial
Net:   eth0: ethernet@8000000port@1
Warning: ethernet@8000000port@2 (eth1) using random MAC address - 9e:c7:8b:2f:1a:a3
, eth1: ethernet@8000000port@2

Hit any key to stop autoboot:  0
=>
=>
=>
=>
=> env default -f -a
## Resetting to default environment
=> sf erase 0x0 0x3800000
No SPI flash selected. Please run `sf probe'
=> sf probe
SF: Detected s28hs512t with page size 256 Bytes, erase size 256 KiB, total 64 MiB
=> sf erase 0x0 0x3800000
SF: 58720256 bytes @ 0x0 Erased: OK
=> setenv dfu_alt_info ${dfu_alt_info_ospi}
=> print dfu_alt_info_ospi
dfu_alt_info_ospi=tiboot3.bin raw 0x0 0x080000; tispl.bin raw 0x080000 0x200000;                                                                                                                                                              u-boot.img raw 0x280000 0x400000; u-boot-env raw 0x680000 0x020000; sysfw.itb r                                                                                                                                                             aw 0x6c0000 0x100000; rootfs raw 0x800000 0x3800000
=> dfu 0 sf 0:0
##DOWNLOAD ... OK
Ctrl+C to exit ...
######DOWNLOAD ... OK
Ctrl+C to exit ...
######DOWNLOAD ... OK
Ctrl+C to exit ...
=>
U-Boot SPL 2025.01-00535-g827c35b4d141-dirty (Jul 03 2025 - 17:34:00 +0000)
SYSFW ABI: 4.0 (firmware rev 0x000b '11.1.2--v11.01.02 (Fancy Rat)')
SPL initial stack usage: 17088 bytes
Trying to boot from SPI
QSPI: QSPI is still busy after poll for 5000 ms.

Thanks

  • Hi Sui, some questions below:

    Trying to understand if there is any setup configuration in which OSPI booting works OK for you, and also which changes have been done. 

    Thank you,

    Paula

  • Hi 

    1、YES , I use default images (tiboot3.bin, tispl.bin, u-boot.img)

    2、I use SDK version is 11_01_05_03

    3、I have no deletd Phy pattern (0x3FC0000)

    4、I am sure that I flashing images in OSPI NOR 

    I use dfu flashing it , you can check the OSPI_BOOT_LOG.txt  log , 

    => sf probe
    SF: Detected s28hs512t with page size 256 Bytes, erase size 256 KiB, total 64 MiB
    => sf erase 0x0 0x3800000
    SF: 58720256 bytes @ 0x0 Erased: OK
    => setenv dfu_alt_info ${dfu_alt_info_ospi}

    => print dfu_alt_info_ospi
    dfu_alt_info_ospi=tiboot3.bin raw 0x0 0x080000; tispl.bin raw 0x080000 0x200000; u-boot.img raw 0x280000 0x400000; u-boot-env raw 0x680000 0x020000; sysfw.itb r aw 0x6c0000 0x100000; rootfs raw 0x800000 0x3800000
    => dfu 0 sf 0:0
    ##DOWNLOAD ... OK
    Ctrl+C to exit ...
    ######DOWNLOAD ... OK
    Ctrl+C to exit ...
    ######DOWNLOAD ... OK
    Ctrl+C to exit ...
    =>

    Thank you,

    SUI

  • Hi 

    There is my uboot modify patch ,

    tisdk@tisdk-YangTianM4000e-16:~/oe-layersetup/build/arago-tmp-default-glibc/work/am62pxx_evm-oe-linux/u-boot-ti-staging/2025.01+git/git$ git diff
    diff --git a/board/ti/am62px/am62px.env b/board/ti/am62px/am62px.env
    index 60674b21a9c..3933e732623 100644
    --- a/board/ti/am62px/am62px.env
    +++ b/board/ti/am62px/am62px.env
    @@ -10,14 +10,16 @@ rproc_fw_binaries= 0 /lib/firmware/am62p-mcu-r5f0_0-fw
    
     name_kern=Image
     console=ttyS2,115200n8
    -args_all=setenv optargs ${optargs} earlycon=ns16550a,mmio32,0x02800000
    +args_all=setenv optargs ${optargs} quiet earlycon=ns16550a,mmio32,0x02800000
            ${mtdparts}
     run_kern=booti ${loadaddr} ${rd_spec} ${fdtaddr}
    
     boot_targets=mmc1 mmc0 usb pxe dhcp
     boot=mmc
    -mmcdev=1
    -bootpart=1:2
    +mmcdev=0
    +bootpart=0:1
    +name_overlays=ti/k3-am62p5-sk-microtips-mf101hie-panel.dtbo ti/k3-am62p5-sk-dss-shared-mode.dtbo
    +
     bootdir=/boot
     rd_spec=-
    
    

    you can check it

  • Hi  

    If I flash R5 image , QSPI boot is ok , If I only flash tiboot3.bin  tispl.bin  u-boot.img , OSPI boot is failed 

    Thanks

  • Hi Sui, comments/questions/request below

    1) with my question about "If flashing images in OSPI NOR does board boots?" I wanted to check if you can flash the same images using other mechanism  as mentioned here 3.1.2.5. OSPI/QSPI NOR/NAND — Processor SDK AM62Px Documentation .. for example TFTP or SD card? To help us to rule out any issues with the binaries or the DFU flashing procedure

    2) Did you try patches mentioned by Vaibhav? just curious if you last comment was after applying patches

    3) Is the issue reproduceable with an TI's AM62P board and OOB binaries? I believe it is, if so, and you can share any steps/tips to reproduce it, it would be really helpful.  

    thank you,

    Paula

  • Hi  Paula

    1) we want to use DFU flash images , the dfu  env is  

     print dfu_alt_info_ospi
    dfu_alt_info_ospi=tiboot3.bin raw 0x0 0x080000; tispl.bin raw 0x080000 0x200000; u-boot.img raw 0x280000 0x400000; u-boot-env raw 0x680000 0x020000; sysfw.itb r aw 0x6c0000 0x100000; rootfs raw 0x800000 0x3800000

    and I flash tiboot3.bin  tispl.bin  u-boot.img

    Then I test use dfu_alt_info_emmc  , then  EMMC boot is ok , I think DFU flash  is ok

    2) I found the new SDK 11_01_05_03 have already use  the patch 

    you can check the file 

    /*
     * Copyright (C) 2012 Altera Corporation <www.altera.com>
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *  - Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *  - Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *  - Neither the name of the Altera Corporation nor the
     *    names of its contributors may be used to endorse or promote products
     *    derived from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED. IN NO EVENT SHALL ALTERA CORPORATION BE LIABLE FOR ANY
     * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include <log.h>
    #include <asm/io.h>
    #include <dma.h>
    #include <linux/iopoll.h>
    #include <linux/bitops.h>
    #include <linux/delay.h>
    #include <linux/errno.h>
    #include <linux/sizes.h>
    #include <wait_bit.h>
    #include <spi.h>
    #include <spi-mem.h>
    #include <malloc.h>
    #include "cadence_qspi.h"
    
    __weak void cadence_qspi_apb_enable_linear_mode(bool enable)
    {
    	return;
    }
    
    void cadence_qspi_apb_set_tx_dll(void *reg_base, u8 dll)
    {
    	unsigned int reg;
    
    	reg = readl(reg_base + CQSPI_REG_PHY_CONFIG);
    	reg &= ~(CQSPI_REG_PHY_CONFIG_TX_DEL_MASK <<
    		CQSPI_REG_PHY_CONFIG_TX_DEL_LSB);
    	reg |= (dll & CQSPI_REG_PHY_CONFIG_TX_DEL_MASK) <<
    		CQSPI_REG_PHY_CONFIG_TX_DEL_LSB;
    	reg |= CQSPI_REG_PHY_CONFIG_RESYNC;
    	writel(reg, reg_base + CQSPI_REG_PHY_CONFIG);
    }
    
    void cadence_qspi_apb_set_rx_dll(void *reg_base, u8 dll)
    {
    	unsigned int reg;
    
    	reg = readl(reg_base + CQSPI_REG_PHY_CONFIG);
    	reg &= ~(CQSPI_REG_PHY_CONFIG_RX_DEL_MASK <<
    		CQSPI_REG_PHY_CONFIG_RX_DEL_LSB);
    	reg |= (dll & CQSPI_REG_PHY_CONFIG_RX_DEL_MASK) <<
    		CQSPI_REG_PHY_CONFIG_RX_DEL_LSB;
    	reg |= CQSPI_REG_PHY_CONFIG_RESYNC;
    	writel(reg, reg_base + CQSPI_REG_PHY_CONFIG);
    }
    
    void cadence_qspi_apb_controller_enable(void *reg_base)
    {
    	unsigned int reg;
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	reg |= CQSPI_REG_CONFIG_ENABLE;
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    }
    
    void cadence_qspi_apb_controller_disable(void *reg_base)
    {
    	unsigned int reg;
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	reg &= ~CQSPI_REG_CONFIG_ENABLE;
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    }
    
    void cadence_qspi_apb_dac_mode_enable(void *reg_base)
    {
    	unsigned int reg;
    
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	reg |= CQSPI_REG_CONFIG_DIRECT;
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    }
    
    static unsigned int cadence_qspi_calc_dummy(const struct spi_mem_op *op,
    					    bool dtr)
    {
    	unsigned int dummy_clk;
    
    	if (!op->dummy.nbytes || !op->dummy.buswidth)
    		return 0;
    
    	dummy_clk = op->dummy.nbytes * (8 / op->dummy.buswidth);
    	if (dtr)
    		dummy_clk /= 2;
    
    	return dummy_clk;
    }
    
    bool cadence_qspi_apb_op_eligible(const struct spi_mem_op *op)
    {
    	/* PHY is only tuned for 8D-8D-8D. */
    	if (!(op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr))
    		return false;
    	if (op->cmd.buswidth != 8)
    		return false;
    	if (!(op->addr.nbytes) || op->addr.buswidth != 8)
    		return false;
    	if (!(op->dummy.nbytes) || op->dummy.buswidth != 8)
    		return false;
    	if (!(op->data.nbytes) || op->data.buswidth != 8)
    		return false;
    
    	return true;
    }
    
    bool cadence_qspi_apb_op_eligible_sdr(const struct spi_mem_op *op)
    {
    	if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
    		return false;
    	if (!(op->addr.nbytes) || op->addr.buswidth < 1)
    		return false;
    	if (!(op->dummy.nbytes) || op->dummy.buswidth < 1)
    		return false;
    	if (!(op->data.nbytes) || op->data.buswidth < 1)
    		return false;
    
    	return true;
    }
    
    /*
     * Check if we can use PHY on the given op. This is assuming it will be a DAC
     * mode read, since PHY won't work on any other type of operation anyway.
     */
    static bool cadence_qspi_apb_use_phy(struct cadence_spi_priv *priv,
    				     const struct spi_mem_op *op)
    {
    	if (!priv->use_phy)
    		return false;
    
    	if (op->data.nbytes < 16)
    		return false;
    
    	if (priv->use_dqs)
    		return cadence_qspi_apb_op_eligible(op);
    	else
    		return cadence_qspi_apb_op_eligible_sdr(op);
    }
    
    static u32 cadence_qspi_calc_rdreg(struct cadence_spi_priv *priv)
    {
    	u32 rdreg = 0;
    
    	rdreg |= priv->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB;
    	rdreg |= priv->addr_width << CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB;
    	rdreg |= priv->data_width << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
    
    	return rdreg;
    }
    
    static int cadence_qspi_buswidth_to_inst_type(u8 buswidth)
    {
    	switch (buswidth) {
    	case 0:
    	case 1:
    		return CQSPI_INST_TYPE_SINGLE;
    
    	case 2:
    		return CQSPI_INST_TYPE_DUAL;
    
    	case 4:
    		return CQSPI_INST_TYPE_QUAD;
    
    	case 8:
    		return CQSPI_INST_TYPE_OCTAL;
    
    	default:
    		return -ENOTSUPP;
    	}
    }
    
    static int cadence_qspi_set_protocol(struct cadence_spi_priv *priv,
    				     const struct spi_mem_op *op)
    {
    	int ret;
    
    	/*
    	 * For an op to be DTR, cmd phase along with every other non-empty
    	 * phase should have dtr field set to 1. If an op phase has zero
    	 * nbytes, ignore its dtr field; otherwise, check its dtr field.
    	 * Also, dummy checks not performed here Since supports_op()
    	 * already checks that all or none of the fields are DTR.
    	 */
    	priv->dtr = op->cmd.dtr &&
    		    (!op->addr.nbytes || op->addr.dtr) &&
    		    (!op->data.nbytes || op->data.dtr);
    
    	ret = cadence_qspi_buswidth_to_inst_type(op->cmd.buswidth);
    	if (ret < 0)
    		return ret;
    	priv->inst_width = ret;
    
    	ret = cadence_qspi_buswidth_to_inst_type(op->addr.buswidth);
    	if (ret < 0)
    		return ret;
    	priv->addr_width = ret;
    
    	ret = cadence_qspi_buswidth_to_inst_type(op->data.buswidth);
    	if (ret < 0)
    		return ret;
    	priv->data_width = ret;
    
    	return 0;
    }
    
    /* Return 1 if idle, otherwise return 0 (busy). */
    static unsigned int cadence_qspi_wait_idle(void *reg_base)
    {
    	unsigned long start, count = 0;
    	/* timeout in unit of ms */
    	unsigned long timeout = 5000;
    
    	start = get_timer(0);
    	for ( ; get_timer(start) < timeout ; ) {
    		if (CQSPI_REG_IS_IDLE(reg_base))
    			count++;
    		else
    			count = 0;
    		/*
    		 * Ensure the QSPI controller is in true idle state after
    		 * reading back the same idle status consecutively
    		 */
    		if (count >= CQSPI_POLL_IDLE_RETRY)
    			return 0;
    	}
    
    	/* Timeout, still in busy mode. */
    	printf("QSPI: QSPI is still busy after poll for %lu ms.\n", timeout);
    	return -ETIMEDOUT;
    }
    
    int cadence_qspi_apb_resync_dll(void *reg_base)
    {
    	unsigned int reg;
    	int ret;
    
    	ret = cadence_qspi_wait_idle(reg_base);
    
    	if (!ret) {
    		reg = readl(reg_base + CQSPI_REG_CONFIG);
    		reg &= ~(CQSPI_REG_CONFIG_ENABLE);
    		writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    		reg = readl(reg_base + CQSPI_REG_PHY_CONFIG);
    		reg &= ~(CQSPI_REG_PHY_CONFIG_DLL_RESET | CQSPI_REG_PHY_CONFIG_RESYNC);
    		writel(reg, reg_base + CQSPI_REG_PHY_CONFIG);
    
    		reg = readl(reg_base + CQSPI_REG_PHY_DLL_MASTER);
    		reg |= (CQSPI_REG_PHY_DLL_MASTER_INIT_DELAY_VAL
    			<< CQSPI_REG_PHY_DLL_MASTER_INIT_DELAY_LSB);
    		writel(reg, reg_base + CQSPI_REG_PHY_DLL_MASTER);
    
    		reg = readl(reg_base + CQSPI_REG_PHY_CONFIG);
    		reg |= CQSPI_REG_PHY_CONFIG_DLL_RESET;
    		writel(reg, reg_base + CQSPI_REG_PHY_CONFIG);
    
    		readl_poll_timeout(reg_base + CQSPI_REG_DLL_OBS_LOW, reg,
    				   !(reg &= (1 << CQSPI_REG_DLL_OBS_LOW_DLL_LOCK_LSB)),
    				   CQSPI_DLL_TIMEOUT_US);
    
    		readl_poll_timeout(reg_base + CQSPI_REG_DLL_OBS_LOW, reg,
    				   !(reg &= (1 << CQSPI_REG_DLL_OBS_LOW_LOOPBACK_LOCK_LSB)),
    				   CQSPI_DLL_TIMEOUT_US);
    
    		reg = readl(reg_base + CQSPI_REG_PHY_CONFIG);
    		reg |= CQSPI_REG_PHY_CONFIG_RESYNC;
    		writel(reg, reg_base + CQSPI_REG_PHY_CONFIG);
    
    		reg = readl(reg_base + CQSPI_REG_CONFIG);
    		reg |= CQSPI_REG_CONFIG_ENABLE;
    		writel(reg, reg_base + CQSPI_REG_CONFIG);
    	}
    
    	return ret;
    }
    
    void cadence_qspi_apb_readdata_capture(void *reg_base,
    				       unsigned int bypass,
    				       const bool dqs,
    				       unsigned int delay)
    {
    	unsigned int reg;
    	cadence_qspi_apb_controller_disable(reg_base);
    
    	reg = readl(reg_base + CQSPI_REG_RD_DATA_CAPTURE);
    
    	/* Disable Rising edge sampling */
    	reg &= ~CQSPI_REG_RD_DATA_CAPTURE_SMPL_EDGE;
    
    	if (bypass)
    		reg |= CQSPI_REG_RD_DATA_CAPTURE_BYPASS;
    	else
    		reg &= ~CQSPI_REG_RD_DATA_CAPTURE_BYPASS;
    
    	reg &= ~(CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK
    		<< CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB);
    
    	reg |= (delay & CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK)
    		<< CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB;
    
    	if (dqs)
    		reg |= CQSPI_REG_READCAPTURE_DQS_ENABLE;
    	else
    		reg &= ~(CQSPI_REG_READCAPTURE_DQS_ENABLE);
    
    	writel(reg, reg_base + CQSPI_REG_RD_DATA_CAPTURE);
    
    	cadence_qspi_apb_controller_enable(reg_base);
    }
    
    static void cadence_qspi_apb_phy_enable(struct cadence_spi_priv *priv,
    					bool enable)
    {
    	void *reg_base = priv->regbase;
    	unsigned int reg;
    	u8 dummy;
    
    	if (enable) {
    		cadence_qspi_apb_readdata_capture(priv->regbase, 1,
    						  priv->use_dqs,
    						  priv->phy_read_delay);
    
    		reg = readl(reg_base + CQSPI_REG_CONFIG);
    		reg |= CQSPI_REG_CONFIG_PHY_ENABLE_MASK |
    		       CQSPI_REG_CONFIG_PHY_PIPELINE;
    		writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    		/* Reduce dummy cycle by 1. */
    		reg = readl(reg_base + CQSPI_REG_RD_INSTR);
    		dummy = (reg >> CQSPI_REG_RD_INSTR_DUMMY_LSB) &
    			CQSPI_REG_RD_INSTR_DUMMY_MASK;
    		dummy--;
    		reg &= ~(CQSPI_REG_RD_INSTR_DUMMY_MASK <<
    			 CQSPI_REG_RD_INSTR_DUMMY_LSB);
    
    		reg |= (dummy & CQSPI_REG_RD_INSTR_DUMMY_MASK)
    		       << CQSPI_REG_RD_INSTR_DUMMY_LSB;
    		writel(reg, reg_base + CQSPI_REG_RD_INSTR);
    	} else {
    		cadence_qspi_apb_readdata_capture(priv->regbase, 1, false,
    						  priv->read_delay);
    
    		reg = readl(reg_base + CQSPI_REG_CONFIG);
    		reg &= ~(CQSPI_REG_CONFIG_PHY_ENABLE_MASK |
    			 CQSPI_REG_CONFIG_PHY_PIPELINE);
    		writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    		/* Increment dummy cycle by 1. */
    		reg = readl(reg_base + CQSPI_REG_RD_INSTR);
    		dummy = (reg >> CQSPI_REG_RD_INSTR_DUMMY_LSB) &
    			CQSPI_REG_RD_INSTR_DUMMY_MASK;
    		dummy++;
    		reg &= ~(CQSPI_REG_RD_INSTR_DUMMY_MASK <<
    			 CQSPI_REG_RD_INSTR_DUMMY_LSB);
    
    		reg |= (dummy & CQSPI_REG_RD_INSTR_DUMMY_MASK)
    		       << CQSPI_REG_RD_INSTR_DUMMY_LSB;
    		writel(reg, reg_base + CQSPI_REG_RD_INSTR);
    	}
    
    	cadence_qspi_wait_idle(reg_base);
    }
    
    void cadence_qspi_apb_phy_pre_config(struct cadence_spi_priv *priv,
    				     const bool bypass, const bool dqs)
    {
    	void *reg_base = priv->regbase;
    	unsigned int reg;
    	u8 dummy;
    
    	cadence_qspi_apb_readdata_capture(reg_base, bypass, dqs,
    					  priv->phy_read_delay);
    
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	reg &= ~(CQSPI_REG_CONFIG_PHY_ENABLE_MASK |
    		 CQSPI_REG_CONFIG_PHY_PIPELINE);
    	reg |= CQSPI_REG_CONFIG_PHY_ENABLE_MASK;
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    	reg = readl(reg_base + CQSPI_REG_RD_INSTR);
    	dummy = (reg >> CQSPI_REG_RD_INSTR_DUMMY_LSB) &
    		CQSPI_REG_RD_INSTR_DUMMY_MASK;
    	dummy--;
    	reg &= ~(CQSPI_REG_RD_INSTR_DUMMY_MASK
    		 << CQSPI_REG_RD_INSTR_DUMMY_LSB);
    
    	reg |= (dummy & CQSPI_REG_RD_INSTR_DUMMY_MASK)
    	       << CQSPI_REG_RD_INSTR_DUMMY_LSB;
    	writel(reg, reg_base + CQSPI_REG_RD_INSTR);
    
    	reg = readl(reg_base + CQSPI_REG_PHY_DLL_MASTER);
    	reg &= ~((CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_LEN
    		  << CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_LSB) |
    		 CQSPI_REG_PHY_DLL_MASTER_BYPASS |
    		 CQSPI_REG_PHY_DLL_MASTER_CYCLE);
    	reg |= ((priv->phase_detect_selector
    		 << CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_LSB) |
    		CQSPI_REG_PHY_DLL_MASTER_CYCLE);
    
    	writel(reg, reg_base + CQSPI_REG_PHY_DLL_MASTER);
    }
    
    void cadence_qspi_apb_phy_post_config(struct cadence_spi_priv *priv,
    				      const unsigned int delay)
    {
    	void *reg_base = priv->regbase;
    	unsigned int reg;
    	u8 dummy;
    
    	reg = readl(reg_base + CQSPI_REG_RD_DATA_CAPTURE);
    	reg &= ~(CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK
    		 << CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB);
    
    	reg |= (delay & CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK)
    	       << CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB;
    	writel(reg, reg_base + CQSPI_REG_RD_DATA_CAPTURE);
    
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	reg &= ~(CQSPI_REG_CONFIG_PHY_ENABLE_MASK |
    		 CQSPI_REG_CONFIG_PHY_PIPELINE);
    	reg &= ~(CQSPI_REG_CONFIG_PHY_ENABLE_MASK);
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    	reg = readl(reg_base + CQSPI_REG_RD_INSTR);
    	dummy = (reg >> CQSPI_REG_RD_INSTR_DUMMY_LSB) &
    		CQSPI_REG_RD_INSTR_DUMMY_MASK;
    	dummy++;
    	reg &= ~(CQSPI_REG_RD_INSTR_DUMMY_MASK
    		 << CQSPI_REG_RD_INSTR_DUMMY_LSB);
    
    	reg |= (dummy & CQSPI_REG_RD_INSTR_DUMMY_MASK)
    	       << CQSPI_REG_RD_INSTR_DUMMY_LSB;
    	writel(reg, reg_base + CQSPI_REG_RD_INSTR);
    }
    
    void cadence_qspi_apb_config_baudrate_div(void *reg_base,
    	unsigned int ref_clk_hz, unsigned int sclk_hz)
    {
    	unsigned int reg;
    	unsigned int div;
    
    	cadence_qspi_apb_controller_disable(reg_base);
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB);
    
    	/*
    	 * The baud_div field in the config reg is 4 bits, and the ref clock is
    	 * divided by 2 * (baud_div + 1). Round up the divider to ensure the
    	 * SPI clock rate is less than or equal to the requested clock rate.
    	 */
    	div = DIV_ROUND_UP(ref_clk_hz, sclk_hz * 2) - 1;
    
    	/* ensure the baud rate doesn't exceed the max value */
    	if (div > CQSPI_REG_CONFIG_BAUD_MASK)
    		div = CQSPI_REG_CONFIG_BAUD_MASK;
    
    	debug("%s: ref_clk %dHz sclk %dHz Div 0x%x, actual %dHz\n", __func__,
    	      ref_clk_hz, sclk_hz, div, ref_clk_hz / (2 * (div + 1)));
    
    	reg |= (div << CQSPI_REG_CONFIG_BAUD_LSB);
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    	cadence_qspi_apb_controller_enable(reg_base);
    }
    
    void cadence_qspi_apb_set_clk_mode(void *reg_base, uint mode)
    {
    	unsigned int reg;
    
    	cadence_qspi_apb_controller_disable(reg_base);
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	reg &= ~(CQSPI_REG_CONFIG_CLK_POL | CQSPI_REG_CONFIG_CLK_PHA);
    
    	if (mode & SPI_CPOL)
    		reg |= CQSPI_REG_CONFIG_CLK_POL;
    	if (mode & SPI_CPHA)
    		reg |= CQSPI_REG_CONFIG_CLK_PHA;
    
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    	cadence_qspi_apb_controller_enable(reg_base);
    }
    
    void cadence_qspi_apb_chipselect(void *reg_base,
    	unsigned int chip_select, unsigned int decoder_enable)
    {
    	unsigned int reg;
    
    	cadence_qspi_apb_controller_disable(reg_base);
    
    	debug("%s : chipselect %d decode %d\n", __func__, chip_select,
    	      decoder_enable);
    
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	/* docoder */
    	if (decoder_enable) {
    		reg |= CQSPI_REG_CONFIG_DECODE;
    	} else {
    		reg &= ~CQSPI_REG_CONFIG_DECODE;
    		/* Convert CS if without decoder.
    		 * CS0 to 4b'1110
    		 * CS1 to 4b'1101
    		 * CS2 to 4b'1011
    		 * CS3 to 4b'0111
    		 */
    		chip_select = 0xF & ~(1 << chip_select);
    	}
    
    	reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
    			<< CQSPI_REG_CONFIG_CHIPSELECT_LSB);
    	reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
    			<< CQSPI_REG_CONFIG_CHIPSELECT_LSB;
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    	cadence_qspi_apb_controller_enable(reg_base);
    }
    
    void cadence_qspi_apb_delay(void *reg_base,
    	unsigned int ref_clk, unsigned int sclk_hz,
    	unsigned int tshsl_ns, unsigned int tsd2d_ns,
    	unsigned int tchsh_ns, unsigned int tslch_ns)
    {
    	unsigned int ref_clk_ns;
    	unsigned int sclk_ns;
    	unsigned int tshsl, tchsh, tslch, tsd2d;
    	unsigned int reg;
    
    	cadence_qspi_apb_controller_disable(reg_base);
    
    	/* Convert to ns. */
    	ref_clk_ns = DIV_ROUND_UP(1000000000, ref_clk);
    
    	/* Convert to ns. */
    	sclk_ns = DIV_ROUND_UP(1000000000, sclk_hz);
    
    	/* The controller adds additional delay to that programmed in the reg */
    	if (tshsl_ns >= sclk_ns + ref_clk_ns)
    		tshsl_ns -= sclk_ns + ref_clk_ns;
    	if (tchsh_ns >= sclk_ns + 3 * ref_clk_ns)
    		tchsh_ns -= sclk_ns + 3 * ref_clk_ns;
    	tshsl = DIV_ROUND_UP(tshsl_ns, ref_clk_ns);
    	tchsh = DIV_ROUND_UP(tchsh_ns, ref_clk_ns);
    	tslch = DIV_ROUND_UP(tslch_ns, ref_clk_ns);
    	tsd2d = DIV_ROUND_UP(tsd2d_ns, ref_clk_ns);
    
    	reg = ((tshsl & CQSPI_REG_DELAY_TSHSL_MASK)
    			<< CQSPI_REG_DELAY_TSHSL_LSB);
    	reg |= ((tchsh & CQSPI_REG_DELAY_TCHSH_MASK)
    			<< CQSPI_REG_DELAY_TCHSH_LSB);
    	reg |= ((tslch & CQSPI_REG_DELAY_TSLCH_MASK)
    			<< CQSPI_REG_DELAY_TSLCH_LSB);
    	reg |= ((tsd2d & CQSPI_REG_DELAY_TSD2D_MASK)
    			<< CQSPI_REG_DELAY_TSD2D_LSB);
    	writel(reg, reg_base + CQSPI_REG_DELAY);
    
    	cadence_qspi_apb_controller_enable(reg_base);
    }
    
    void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv)
    {
    	unsigned reg;
    
    	cadence_qspi_apb_controller_disable(priv->regbase);
    
    	/* Configure the device size and address bytes */
    	reg = readl(priv->regbase + CQSPI_REG_SIZE);
    	/* Clear the previous value */
    	reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB);
    	reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB);
    	reg |= (priv->page_size << CQSPI_REG_SIZE_PAGE_LSB);
    	reg |= (priv->block_size << CQSPI_REG_SIZE_BLOCK_LSB);
    	writel(reg, priv->regbase + CQSPI_REG_SIZE);
    
    	/* Configure the remap address register, no remap */
    	writel(0, priv->regbase + CQSPI_REG_REMAP);
    
    	/* Indirect mode configurations */
    	writel(priv->fifo_depth / 2, priv->regbase + CQSPI_REG_SRAMPARTITION);
    
    	/* Disable all interrupts */
    	writel(0, priv->regbase + CQSPI_REG_IRQMASK);
    
    	cadence_qspi_apb_controller_enable(priv->regbase);
    }
    
    int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg)
    {
    	unsigned int retry = CQSPI_REG_RETRY;
    
    	/* Write the CMDCTRL without start execution. */
    	writel(reg, reg_base + CQSPI_REG_CMDCTRL);
    	/* Start execute */
    	reg |= CQSPI_REG_CMDCTRL_EXECUTE;
    	writel(reg, reg_base + CQSPI_REG_CMDCTRL);
    
    	while (retry--) {
    		reg = readl(reg_base + CQSPI_REG_CMDCTRL);
    		if ((reg & CQSPI_REG_CMDCTRL_INPROGRESS) == 0)
    			break;
    		udelay(1);
    	}
    
    	if (!retry) {
    		printf("QSPI: flash command execution timeout\n");
    		return -EIO;
    	}
    
    	/* Polling QSPI idle status. */
    	if (cadence_qspi_wait_idle(reg_base))
    		return -ETIMEDOUT;
    
    	/* Flush the CMDCTRL reg after the execution */
    	writel(0, reg_base + CQSPI_REG_CMDCTRL);
    
    	return 0;
    }
    
    static int cadence_qspi_setup_opcode_ext(struct cadence_spi_priv *priv,
    					 const struct spi_mem_op *op,
    					 unsigned int shift)
    {
    	unsigned int reg;
    	u8 ext;
    
    	if (op->cmd.nbytes != 2)
    		return -EINVAL;
    
    	/* Opcode extension is the LSB. */
    	ext = op->cmd.opcode & 0xff;
    
    	reg = readl(priv->regbase + CQSPI_REG_OP_EXT_LOWER);
    	reg &= ~(0xff << shift);
    	reg |= ext << shift;
    	writel(reg, priv->regbase + CQSPI_REG_OP_EXT_LOWER);
    
    	return 0;
    }
    
    static int cadence_qspi_enable_dtr(struct cadence_spi_priv *priv,
    				   const struct spi_mem_op *op,
    				   unsigned int shift,
    				   bool enable)
    {
    	unsigned int reg;
    	int ret;
    
    	reg = readl(priv->regbase + CQSPI_REG_CONFIG);
    
    	if (enable) {
    		reg |= CQSPI_REG_CONFIG_DTR_PROTO;
    		reg |= CQSPI_REG_CONFIG_DUAL_OPCODE;
    
    		/* Set up command opcode extension. */
    		ret = cadence_qspi_setup_opcode_ext(priv, op, shift);
    		if (ret)
    			return ret;
    	} else {
    		reg &= ~CQSPI_REG_CONFIG_DTR_PROTO;
    		reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE;
    	}
    
    	writel(reg, priv->regbase + CQSPI_REG_CONFIG);
    
    	return 0;
    }
    
    int cadence_qspi_apb_command_read_setup(struct cadence_spi_priv *priv,
    					const struct spi_mem_op *op)
    {
    	int ret;
    	unsigned int reg;
    
    	ret = cadence_qspi_set_protocol(priv, op);
    	if (ret)
    		return ret;
    
    	ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_STIG_LSB,
    				      priv->dtr);
    	if (ret)
    		return ret;
    
    	reg = cadence_qspi_calc_rdreg(priv);
    	writel(reg, priv->regbase + CQSPI_REG_RD_INSTR);
    
    	return 0;
    }
    
    /* For command RDID, RDSR. */
    int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv,
    				  const struct spi_mem_op *op)
    {
    	void *reg_base = priv->regbase;
    	unsigned int reg;
    	unsigned int read_len;
    	int status;
    	unsigned int rxlen = op->data.nbytes;
    	void *rxbuf = op->data.buf.in;
    	unsigned int dummy_clk;
    	u8 opcode;
    
    	if (priv->dtr)
    		opcode = op->cmd.opcode >> 8;
    	else
    		opcode = op->cmd.opcode;
    
    	if (opcode == CMD_4BYTE_OCTAL_READ && !priv->dtr)
    		opcode = CMD_4BYTE_FAST_READ;
    
    	reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
    
    	/* Set up dummy cycles. */
    	dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr);
    	if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
    		return -ENOTSUPP;
    
    	if (dummy_clk)
    		reg |= (dummy_clk & CQSPI_REG_CMDCTRL_DUMMY_MASK)
    		     << CQSPI_REG_CMDCTRL_DUMMY_LSB;
    
    	reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB);
    
    	/* 0 means 1 byte. */
    	reg |= (((rxlen - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK)
    		<< CQSPI_REG_CMDCTRL_RD_BYTES_LSB);
    
    	/* setup ADDR BIT field */
    	if (op->addr.nbytes) {
    		writel(op->addr.val, priv->regbase + CQSPI_REG_CMDADDRESS);
    		/*
    		 * address bytes are zero indexed
    		 */
    		reg |= (((op->addr.nbytes - 1) &
    			  CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) <<
    			  CQSPI_REG_CMDCTRL_ADD_BYTES_LSB);
    		reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
    	}
    
    	status = cadence_qspi_apb_exec_flash_cmd(reg_base, reg);
    	if (status != 0)
    		return status;
    
    	reg = readl(reg_base + CQSPI_REG_CMDREADDATALOWER);
    
    	/* Put the read value into rx_buf */
    	read_len = (rxlen > 4) ? 4 : rxlen;
    	memcpy(rxbuf, &reg, read_len);
    	rxbuf += read_len;
    
    	if (rxlen > 4) {
    		reg = readl(reg_base + CQSPI_REG_CMDREADDATAUPPER);
    
    		read_len = rxlen - read_len;
    		memcpy(rxbuf, &reg, read_len);
    	}
    	return 0;
    }
    
    int cadence_qspi_apb_command_write_setup(struct cadence_spi_priv *priv,
    					 const struct spi_mem_op *op)
    {
    	int ret;
    	unsigned int reg;
    
    	ret = cadence_qspi_set_protocol(priv, op);
    	if (ret)
    		return ret;
    
    	ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_STIG_LSB,
    				      priv->dtr);
    	if (ret)
    		return ret;
    
    	reg = cadence_qspi_calc_rdreg(priv);
    	writel(reg, priv->regbase + CQSPI_REG_RD_INSTR);
    
    	return 0;
    }
    
    /* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */
    int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv,
    				   const struct spi_mem_op *op)
    {
    	unsigned int reg = 0;
    	unsigned int wr_data;
    	unsigned int wr_len;
    	unsigned int dummy_clk;
    	unsigned int txlen = op->data.nbytes;
    	const void *txbuf = op->data.buf.out;
    	void *reg_base = priv->regbase;
    	u8 opcode;
    
    	if (priv->dtr)
    		opcode = op->cmd.opcode >> 8;
    	else
    		opcode = op->cmd.opcode;
    
    	reg |= opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
    
    	/* setup ADDR BIT field */
    	if (op->addr.nbytes) {
    		writel(op->addr.val, priv->regbase + CQSPI_REG_CMDADDRESS);
    		/*
    		 * address bytes are zero indexed
    		 */
    		reg |= (((op->addr.nbytes - 1) &
    			  CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) <<
    			  CQSPI_REG_CMDCTRL_ADD_BYTES_LSB);
    		reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
    	}
    
    	/* Set up dummy cycles. */
    	dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr);
    	if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
    		return -EOPNOTSUPP;
    
    	if (dummy_clk)
    		reg |= (dummy_clk & CQSPI_REG_CMDCTRL_DUMMY_MASK)
    		     << CQSPI_REG_CMDCTRL_DUMMY_LSB;
    
    	if (txlen) {
    		/* writing data = yes */
    		reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB);
    		reg |= ((txlen - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK)
    			<< CQSPI_REG_CMDCTRL_WR_BYTES_LSB;
    
    		wr_len = txlen > 4 ? 4 : txlen;
    		memcpy(&wr_data, txbuf, wr_len);
    		writel(wr_data, reg_base +
    			CQSPI_REG_CMDWRITEDATALOWER);
    
    		if (txlen > 4) {
    			txbuf += wr_len;
    			wr_len = txlen - wr_len;
    			memcpy(&wr_data, txbuf, wr_len);
    			writel(wr_data, reg_base +
    				CQSPI_REG_CMDWRITEDATAUPPER);
    		}
    	}
    
    	/* Execute the command */
    	return cadence_qspi_apb_exec_flash_cmd(reg_base, reg);
    }
    
    /* Opcode + Address (3/4 bytes) + dummy bytes (0-4 bytes) */
    int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv,
    				const struct spi_mem_op *op)
    {
    	unsigned int reg;
    	unsigned int rd_reg;
    	unsigned int dummy_clk;
    	unsigned int dummy_bytes = op->dummy.nbytes;
    	int ret;
    	u8 opcode;
    
    	ret = cadence_qspi_set_protocol(priv, op);
    	if (ret)
    		return ret;
    
    	ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_READ_LSB,
    				      priv->dtr);
    	if (ret)
    		return ret;
    
    	/* Setup the indirect trigger address */
    	writel(priv->trigger_address,
    	       priv->regbase + CQSPI_REG_INDIRECTTRIGGER);
    
    	/* Configure the opcode */
    	if (priv->dtr)
    		opcode = op->cmd.opcode >> 8;
    	else
    		opcode = op->cmd.opcode;
    
    	rd_reg = opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB;
    	rd_reg |= cadence_qspi_calc_rdreg(priv);
    
    	writel(op->addr.val, priv->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
    
    	if (dummy_bytes) {
    		/* Convert to clock cycles. */
    		dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr);
    
    		if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
    			return -ENOTSUPP;
    
    		if (dummy_clk)
    			rd_reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
    				<< CQSPI_REG_RD_INSTR_DUMMY_LSB;
    	}
    
    	writel(rd_reg, priv->regbase + CQSPI_REG_RD_INSTR);
    
    	/* set device size */
    	reg = readl(priv->regbase + CQSPI_REG_SIZE);
    	reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
    	reg |= (op->addr.nbytes - 1);
    	writel(reg, priv->regbase + CQSPI_REG_SIZE);
    	return 0;
    }
    
    static u32 cadence_qspi_get_rd_sram_level(struct cadence_spi_priv *priv)
    {
    	u32 reg = readl(priv->regbase + CQSPI_REG_SDRAMLEVEL);
    	reg >>= CQSPI_REG_SDRAMLEVEL_RD_LSB;
    	return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK;
    }
    
    static int cadence_qspi_wait_for_data(struct cadence_spi_priv *priv)
    {
    	unsigned int timeout = 10000;
    	u32 reg;
    
    	while (timeout--) {
    		reg = cadence_qspi_get_rd_sram_level(priv);
    		if (reg)
    			return reg;
    		udelay(1);
    	}
    
    	return -ETIMEDOUT;
    }
    
    static int
    cadence_qspi_apb_indirect_read_execute(struct cadence_spi_priv *priv,
    				       unsigned int n_rx, u8 *rxbuf)
    {
    	unsigned int remaining = n_rx;
    	unsigned int bytes_to_read = 0;
    	int ret;
    
    	writel(n_rx, priv->regbase + CQSPI_REG_INDIRECTRDBYTES);
    
    	/* Start the indirect read transfer */
    	writel(CQSPI_REG_INDIRECTRD_START,
    	       priv->regbase + CQSPI_REG_INDIRECTRD);
    
    	while (remaining > 0) {
    		ret = cadence_qspi_wait_for_data(priv);
    		if (ret < 0) {
    			printf("Indirect write timed out (%i)\n", ret);
    			goto failrd;
    		}
    
    		bytes_to_read = ret;
    
    		while (bytes_to_read != 0) {
    			bytes_to_read *= priv->fifo_width;
    			bytes_to_read = bytes_to_read > remaining ?
    					remaining : bytes_to_read;
    			/*
    			 * Handle non-4-byte aligned access to avoid
    			 * data abort.
    			 */
    			if (((uintptr_t)rxbuf % 4) || (bytes_to_read % 4))
    				readsb(priv->ahbbase, rxbuf, bytes_to_read);
    			else
    				readsl(priv->ahbbase, rxbuf,
    				       bytes_to_read >> 2);
    			rxbuf += bytes_to_read;
    			remaining -= bytes_to_read;
    			bytes_to_read = cadence_qspi_get_rd_sram_level(priv);
    		}
    	}
    
    	/* Check indirect done status */
    	ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTRD,
    				CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0);
    	if (ret) {
    		printf("Indirect read completion error (%i)\n", ret);
    		goto failrd;
    	}
    
    	/* Clear indirect completion status */
    	writel(CQSPI_REG_INDIRECTRD_DONE,
    	       priv->regbase + CQSPI_REG_INDIRECTRD);
    
    	/* Check indirect done status */
    	ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTRD,
    				CQSPI_REG_INDIRECTRD_DONE, 0, 10, 0);
    	if (ret) {
    		printf("Indirect read clear completion error (%i)\n", ret);
    		goto failrd;
    	}
    
    	return 0;
    
    failrd:
    	/* Cancel the indirect read */
    	writel(CQSPI_REG_INDIRECTRD_CANCEL,
    	       priv->regbase + CQSPI_REG_INDIRECTRD);
    	return ret;
    }
    
    static int
    cadence_qspi_apb_direct_read_execute(struct cadence_spi_priv *priv,
    				     const struct spi_mem_op *op)
    {
    	loff_t from = op->addr.val;
    	loff_t from_aligned, to_aligned;
    	size_t len = op->data.nbytes;
    	size_t len_aligned;
    	u_char *buf = op->data.buf.in;
    	int ret;
    
    	cadence_qspi_apb_enable_linear_mode(true);
    
    	if (len < 16) {
    		memcpy_fromio(buf, priv->ahbbase + from, len);
    		if (cadence_qspi_wait_idle(priv->regbase))
    			return -ETIMEDOUT;
    		return 0;
    	}
    
    	if (!cadence_qspi_apb_use_phy(priv, op)) {
    		if (dma_memcpy(buf, priv->ahbbase + from, len) < 0)
    			memcpy_fromio(buf, priv->ahbbase + from, len);
    
    		if (cadence_qspi_wait_idle(priv->regbase))
    			return -ETIMEDOUT;
    		return 0;
    	}
    
    	/*
    	 * PHY reads must be 16-byte aligned, and they must be a multiple of 16
    	 * bytes.
    	 */
    	from_aligned = (from + 0xF) & ~0xF;
    	to_aligned = (from + len) & ~0xF;
    	len_aligned = to_aligned - from_aligned;
    
    	/* Read the unaligned part at the start. */
    	if (from != from_aligned) {
    		ret = dma_memcpy(buf, priv->ahbbase + from,
    				 from_aligned - from);
    		if (ret)
    			return ret;
    		buf += from_aligned - from;
    	}
    
    	if (len_aligned) {
    		cadence_qspi_apb_phy_enable(priv, true);
    		ret = dma_memcpy(buf, priv->ahbbase + from_aligned,
    				 len_aligned);
    		cadence_qspi_apb_phy_enable(priv, false);
    		if (ret)
    			return ret;
    		buf += len_aligned;
    	}
    
    	/* Now read the remaining part, if any. */
    	if (to_aligned != (from + len)) {
    		ret = dma_memcpy(buf, priv->ahbbase + to_aligned,
    				 (from + len) - to_aligned);
    		if (ret)
    			return ret;
    		buf += (from + len) - to_aligned;
    	}
    
    	return 0;
    }
    
    int cadence_qspi_apb_read_execute(struct cadence_spi_priv *priv,
    				  const struct spi_mem_op *op)
    {
    	u64 from = op->addr.val;
    	void *buf = op->data.buf.in;
    	size_t len = op->data.nbytes;
    
    	if (priv->use_dac_mode && (from + len < priv->ahbsize))
    		return cadence_qspi_apb_direct_read_execute(priv, op);
    
    	return cadence_qspi_apb_indirect_read_execute(priv, len, buf);
    }
    
    /* Opcode + Address (3/4 bytes) */
    int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv,
    				 const struct spi_mem_op *op)
    {
    	unsigned int reg;
    	int ret;
    	u8 opcode;
    
    	ret = cadence_qspi_set_protocol(priv, op);
    	if (ret)
    		return ret;
    
    	ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_WRITE_LSB,
    				      priv->dtr);
    	if (ret)
    		return ret;
    
    	/* Setup the indirect trigger address */
    	writel(priv->trigger_address,
    	       priv->regbase + CQSPI_REG_INDIRECTTRIGGER);
    
    	/* Configure the opcode */
    	if (priv->dtr)
    		opcode = op->cmd.opcode >> 8;
    	else
    		opcode = op->cmd.opcode;
    
    	reg = opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB;
    	reg |= priv->data_width << CQSPI_REG_WR_INSTR_TYPE_DATA_LSB;
    	reg |= priv->addr_width << CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB;
    	writel(reg, priv->regbase + CQSPI_REG_WR_INSTR);
    
    	reg = cadence_qspi_calc_rdreg(priv);
    	writel(reg, priv->regbase + CQSPI_REG_RD_INSTR);
    
    	writel(op->addr.val, priv->regbase + CQSPI_REG_INDIRECTWRSTARTADDR);
    
    	/*
    	 * SPI NAND flashes require the address of the status register to be
    	 * passed in the Read SR command. Also, some SPI NOR flashes like the
    	 * cypress Semper flash expect a 4-byte dummy address in the Read SR
    	 * command in DTR mode.
    	 *
    	 * But this controller does not support address phase in the Read SR
    	 * command when doing auto-HW polling. So, disable write completion
    	 * polling on the controller's side. spinand and spi-nor will take
    	 * care of polling the status register.
    	 */
    	reg = readl(priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
    	reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL;
    	writel(reg, priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
    
    	reg = readl(priv->regbase + CQSPI_REG_SIZE);
    	reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
    	reg |= (op->addr.nbytes - 1);
    	writel(reg, priv->regbase + CQSPI_REG_SIZE);
    	return 0;
    }
    
    static int
    cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
    					unsigned int n_tx, const u8 *txbuf)
    {
    	unsigned int page_size = priv->page_size;
    	unsigned int remaining = n_tx;
    	const u8 *bb_txbuf = txbuf;
    	void *bounce_buf = NULL;
    	unsigned int write_bytes;
    	int ret;
    
    	/*
    	 * Use bounce buffer for non 32 bit aligned txbuf to avoid data
    	 * aborts
    	 */
    	if ((uintptr_t)txbuf % 4) {
    		bounce_buf = malloc(n_tx);
    		if (!bounce_buf)
    			return -ENOMEM;
    		memcpy(bounce_buf, txbuf, n_tx);
    		bb_txbuf = bounce_buf;
    	}
    
    	/* Configure the indirect read transfer bytes */
    	writel(n_tx, priv->regbase + CQSPI_REG_INDIRECTWRBYTES);
    
    	/* Start the indirect write transfer */
    	writel(CQSPI_REG_INDIRECTWR_START,
    	       priv->regbase + CQSPI_REG_INDIRECTWR);
    
    	/*
    	 * Some delay is required for the above bit to be internally
    	 * synchronized by the QSPI module.
    	 */
    	ndelay(priv->wr_delay);
    
    	while (remaining > 0) {
    		write_bytes = remaining > page_size ? page_size : remaining;
    		writesl(priv->ahbbase, bb_txbuf, write_bytes >> 2);
    		if (write_bytes % 4)
    			writesb(priv->ahbbase,
    				bb_txbuf + rounddown(write_bytes, 4),
    				write_bytes % 4);
    
    		ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_SDRAMLEVEL,
    					CQSPI_REG_SDRAMLEVEL_WR_MASK <<
    					CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0);
    		if (ret) {
    			printf("Indirect write timed out (%i)\n", ret);
    			goto failwr;
    		}
    
    		bb_txbuf += write_bytes;
    		remaining -= write_bytes;
    	}
    
    	/* Check indirect done status */
    	ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTWR,
    				CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0);
    	if (ret) {
    		printf("Indirect write completion error (%i)\n", ret);
    		goto failwr;
    	}
    
    	/* Clear indirect completion status */
    	writel(CQSPI_REG_INDIRECTWR_DONE,
    	       priv->regbase + CQSPI_REG_INDIRECTWR);
    
    	/* Check indirect done status */
    	ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTWR,
    				CQSPI_REG_INDIRECTWR_DONE, 0, 10, 0);
    	if (ret) {
    		printf("Indirect write clear completion error (%i)\n", ret);
    		goto failwr;
    	}
    
    	if (bounce_buf)
    		free(bounce_buf);
    	return 0;
    
    failwr:
    	/* Cancel the indirect write */
    	writel(CQSPI_REG_INDIRECTWR_CANCEL,
    	       priv->regbase + CQSPI_REG_INDIRECTWR);
    	if (bounce_buf)
    		free(bounce_buf);
    	return ret;
    }
    
    int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv,
    				   const struct spi_mem_op *op)
    {
    	u32 to = op->addr.val;
    	const void *buf = op->data.buf.out;
    	size_t len = op->data.nbytes;
    
    	/*
    	 * Some flashes like the Cypress Semper flash expect a dummy 4-byte
    	 * address (all 0s) with the read status register command in DTR mode.
    	 * But this controller does not support sending dummy address bytes to
    	 * the flash when it is polling the write completion register in DTR
    	 * mode. So, we can not use direct mode when in DTR mode for writing
    	 * data.
    	 */
    	cadence_qspi_apb_enable_linear_mode(true);
    	if (!priv->dtr && priv->use_dac_mode && (to + len < priv->ahbsize)) {
    		if (len >= SZ_1K && priv->use_phy)
    			cadence_qspi_apb_phy_enable(priv, true);
    		memcpy_toio(priv->ahbbase + to, buf, len);
    		if (cadence_qspi_wait_idle(priv->regbase))
    			return -ETIMEDOUT;
    		if (len >= SZ_1K && priv->use_phy)
    			cadence_qspi_apb_phy_enable(priv, false);
    		return 0;
    	}
    
    	return cadence_qspi_apb_indirect_write_execute(priv, len, buf);
    }
    
    void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy)
    {
    	unsigned int reg;
    
    	/* enter XiP mode immediately and enable direct mode */
    	reg = readl(reg_base + CQSPI_REG_CONFIG);
    	reg |= CQSPI_REG_CONFIG_ENABLE;
    	reg |= CQSPI_REG_CONFIG_DIRECT;
    	reg |= CQSPI_REG_CONFIG_XIP_IMM;
    	writel(reg, reg_base + CQSPI_REG_CONFIG);
    
    	/* keep the XiP mode */
    	writel(xip_dummy, reg_base + CQSPI_REG_MODE_BIT);
    
    	/* Enable mode bit at devrd */
    	reg = readl(reg_base + CQSPI_REG_RD_INSTR);
    	reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB);
    	writel(reg, reg_base + CQSPI_REG_RD_INSTR);
    }
    

    // SPDX-License-Identifier: GPL-2.0+
    /*
     * Copyright (C) 2012
     * Altera Corporation <www.altera.com>
     */
    
    #include <clk.h>
    #include <log.h>
    #include <dm.h>
    #include <fdtdec.h>
    #include <malloc.h>
    #include <reset.h>
    #include <spi.h>
    #include <spi-mem.h>
    #include <dm/device_compat.h>
    #include <linux/err.h>
    #include <thermal.h>
    #include <linux/errno.h>
    #include <linux/io.h>
    #include <linux/sizes.h>
    #include <linux/time.h>
    #include <zynqmp_firmware.h>
    #include "cadence_qspi.h"
    #include <dt-bindings/power/xlnx-versal-power.h>
    
    #define CQSPI_STIG_READ			0
    #define CQSPI_STIG_WRITE		1
    #define CQSPI_READ			2
    #define CQSPI_WRITE			3
    
    __weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
    				     const struct spi_mem_op *op)
    {
    	return 0;
    }
    
    __weak int cadence_qspi_versal_flash_reset(struct udevice *dev)
    {
    	return 0;
    }
    
    __weak ofnode cadence_qspi_get_subnode(struct udevice *dev)
    {
    	return dev_read_first_subnode(dev);
    }
    
    static int cadence_spi_get_temp(int *temp)
    {
    	struct udevice *dev;
    	int ret;
    
    	ret = uclass_first_device_err(UCLASS_THERMAL, &dev);
    	if (ret)
    		return ret;
    
    	ret = thermal_get_temp(dev, temp);
    	if (ret)
    		return ret;
    
    	/* The temperature we get is in milicelsius. Change it to Celsius. */
    	*temp /= 1000;
    
    	return 0;
    }
    
    static int cadence_spi_phy_apply_setting(struct cadence_spi_priv *priv,
    					 struct phy_setting *phy)
    {
    	unsigned int reg;
    
    	reg = readl(priv->regbase + CQSPI_REG_RD_DATA_CAPTURE);
    	reg |= CQSPI_REG_RD_DATA_CAPTURE_SMPL_EDGE;
    	writel(reg, priv->regbase + CQSPI_REG_RD_DATA_CAPTURE);
    
    	cadence_qspi_apb_set_rx_dll(priv->regbase, phy->rx);
    	cadence_qspi_apb_set_tx_dll(priv->regbase, phy->tx);
    	priv->phy_read_delay = phy->read_delay;
    
    	return cadence_qspi_apb_resync_dll(priv->regbase);
    }
    
    static int cadence_spi_phy_check_pattern(struct cadence_spi_priv *priv,
    					 struct spi_slave *spi)
    {
    	struct spi_mem_op op = priv->phy_read_op;
    	u8 *read_data;
    	int ret;
    
    	read_data = kmalloc(ARRAY_SIZE(phy_tuning_pattern), 0);
    	if (!read_data)
    		return -ENOMEM;
    
    	op.data.buf.in = read_data;
    	op.addr.val = priv->phy_pattern_start;
    	op.data.nbytes = ARRAY_SIZE(phy_tuning_pattern);
    
    	ret = spi_mem_exec_op(spi, &op);
    	if (ret)
    		goto out;
    
    	if (memcmp(read_data, phy_tuning_pattern,
    		   ARRAY_SIZE(phy_tuning_pattern))) {
    		ret = -EAGAIN;
    		goto out;
    	}
    
    	ret = 0;
    out:
    	kfree(read_data);
    	return ret;
    }
    
    static int cadence_spi_find_rx_low(struct cadence_spi_priv *priv,
    				   struct spi_slave *spi,
    				   struct phy_setting *phy)
    {
    	int ret;
    
    	do {
    		phy->rx = CQSPI_PHY_RX_LOW_SEARCH_START;
    		do {
    			ret = cadence_spi_phy_apply_setting(priv, phy);
    			if (!ret) {
    				ret = cadence_spi_phy_check_pattern(priv, spi);
    				if (!ret)
    					return 0;
    			}
    			phy->rx += CQSPI_PHY_DDR_SEARCH_STEP;
    		} while (phy->rx <= CQSPI_PHY_RX_LOW_SEARCH_END);
    
    		phy->read_delay++;
    	} while (phy->read_delay <= CQSPI_PHY_MAX_RD);
    
    	debug("Unable to find RX low\n");
    	return -ENOENT;
    }
    
    static int cadence_spi_find_rx_low_sdr(struct cadence_spi_priv *priv,
    				       struct spi_slave *spi,
    				       struct phy_setting *phy)
    {
    	int ret;
    
    	phy->rx = 0;
    	do {
    		ret = cadence_spi_phy_apply_setting(priv, phy);
    		if (!ret) {
    			ret = cadence_spi_phy_check_pattern(priv, spi);
    			if (!ret)
    				return 0;
    		}
    		phy->rx++;
    	} while (phy->rx < CQSPI_PHY_MAX_DELAY - 1);
    
    	debug("Unable to find RX low\n");
    	return -ENOENT;
    }
    
    static int cadence_spi_find_rx_high(struct cadence_spi_priv *priv,
    				    struct spi_slave *spi,
    				    struct phy_setting *phy)
    {
    	int ret;
    
    	do {
    		phy->rx = CQSPI_PHY_RX_HIGH_SEARCH_END;
    		do {
    			ret = cadence_spi_phy_apply_setting(priv, phy);
    			if (!ret) {
    				ret = cadence_spi_phy_check_pattern(priv, spi);
    				if (!ret)
    					return 0;
    			}
    			phy->rx -= CQSPI_PHY_DDR_SEARCH_STEP;
    		} while (phy->rx >= CQSPI_PHY_RX_HIGH_SEARCH_START);
    
    		phy->read_delay--;
    	} while (phy->read_delay >= CQSPI_PHY_INIT_RD);
    
    	debug("Unable to find RX high\n");
    	return -ENOENT;
    }
    
    static int cadence_spi_find_rx_high_sdr(struct cadence_spi_priv *priv,
    					struct spi_slave *spi,
    					struct phy_setting *phy,
    					u8 lowerbound)
    {
    	int ret;
    
    	phy->rx = CQSPI_PHY_MAX_DELAY;
    	do {
    		ret = cadence_spi_phy_apply_setting(priv, phy);
    		if (!ret) {
    			ret = cadence_spi_phy_check_pattern(priv, spi);
    			if (!ret)
    				return 0;
    		}
    		phy->rx--;
    	} while (phy->rx > lowerbound);
    
    	debug("Unable to find RX high\n");
    	return -ENOENT;
    }
    
    static int cadence_spi_find_tx_low(struct cadence_spi_priv *priv,
    				   struct spi_slave *spi,
    				   struct phy_setting *phy)
    {
    	int ret;
    
    	do {
    		phy->tx = CQSPI_PHY_TX_LOW_SEARCH_START;
    		do {
    			ret = cadence_spi_phy_apply_setting(priv, phy);
    			if (!ret) {
    				ret = cadence_spi_phy_check_pattern(priv, spi);
    				if (!ret)
    					return 0;
    			}
    			phy->tx += CQSPI_PHY_DDR_SEARCH_STEP;
    		} while (phy->tx <= CQSPI_PHY_TX_LOW_SEARCH_END);
    
    		phy->read_delay++;
    	} while (phy->read_delay <= CQSPI_PHY_MAX_RD);
    
    	debug("Unable to find TX low\n");
    	return -ENOENT;
    }
    
    static int cadence_spi_find_tx_high(struct cadence_spi_priv *priv,
    				    struct spi_slave *spi,
    				    struct phy_setting *phy)
    {
    	int ret;
    
    	do {
    		phy->tx = CQSPI_PHY_TX_HIGH_SEARCH_END;
    		do {
    			ret = cadence_spi_phy_apply_setting(priv, phy);
    			if (!ret) {
    				ret = cadence_spi_phy_check_pattern(priv, spi);
    				if (!ret)
    					return 0;
    			}
    			phy->tx -= CQSPI_PHY_DDR_SEARCH_STEP;
    		} while (phy->tx >= CQSPI_PHY_TX_HIGH_SEARCH_START);
    
    		phy->read_delay--;
    	} while (phy->read_delay >= CQSPI_PHY_INIT_RD);
    
    	debug("Unable to find TX high\n");
    	return -ENOENT;
    }
    
    static int cadence_spi_phy_find_gaplow(struct cadence_spi_priv *priv,
    				       struct spi_slave *spi,
    				       struct phy_setting *bottomleft,
    				       struct phy_setting *topright,
    				       struct phy_setting *gaplow)
    {
    	struct phy_setting left, right, mid;
    	int ret;
    
    	left = *bottomleft;
    	right = *topright;
    
    	mid.tx = left.tx + ((right.tx - left.tx) / 2);
    	mid.rx = left.rx + ((right.rx - left.rx) / 2);
    	mid.read_delay = left.read_delay;
    
    	do {
    		ret = cadence_spi_phy_apply_setting(priv, &mid);
    		if (!ret)
    			ret = cadence_spi_phy_check_pattern(priv, spi);
    
    		if (ret) {
    			/*
    			 * Since we couldn't find the pattern, we need to go to
    			 * the lower half.
    			 */
    			right.tx = mid.tx;
    			right.rx = mid.rx;
    
    			mid.tx = left.tx + ((mid.tx - left.tx) / 2);
    			mid.rx = left.rx + ((mid.rx - left.rx) / 2);
    		} else {
    			/*
    			 * Since we found the pattern, we need to go the upper
    			 * half.
    			 */
    			left.tx = mid.tx;
    			left.rx = mid.rx;
    
    			mid.tx = mid.tx + ((right.tx - mid.tx) / 2);
    			mid.rx = mid.rx + ((right.rx - mid.rx) / 2);
    		}
    
    	/* Break the loop if the window has closed. */
    	} while ((right.tx - left.tx >= 2) && (right.rx - left.rx >= 2));
    
    	*gaplow = mid;
    	return 0;
    }
    
    static int cadence_spi_phy_find_gaphigh(struct cadence_spi_priv *priv,
    					struct spi_slave *spi,
    					struct phy_setting *bottomleft,
    					struct phy_setting *topright,
    					struct phy_setting *gaphigh)
    {
    	struct phy_setting left, right, mid;
    	int ret;
    
    	left = *bottomleft;
    	right = *topright;
    
    	mid.tx = left.tx + ((right.tx - left.tx) / 2);
    	mid.rx = left.rx + ((right.rx - left.rx) / 2);
    	mid.read_delay = right.read_delay;
    
    	do {
    		ret = cadence_spi_phy_apply_setting(priv, &mid);
    		if (!ret)
    			ret = cadence_spi_phy_check_pattern(priv, spi);
    
    		if (ret) {
    			/*
    			 * Since we couldn't find the pattern, we need to go the
    			 * upper half.
    			 */
    			left.tx = mid.tx;
    			left.rx = mid.rx;
    
    			mid.tx = mid.tx + ((right.tx - mid.tx) / 2);
    			mid.rx = mid.rx + ((right.rx - mid.rx) / 2);
    		} else {
    			/*
    			 * Since we found the pattern, we need to go to the
    			 * lower half.
    			 */
    			right.tx = mid.tx;
    			right.rx = mid.rx;
    
    			mid.tx = left.tx + ((mid.tx - left.tx) / 2);
    			mid.rx = left.rx + ((mid.rx - left.rx) / 2);
    		}
    
    	/* Break the loop if the window has closed. */
    	} while ((right.tx - left.tx >= 2) && (right.rx - left.rx >= 2));
    
    	*gaphigh = mid;
    	return 0;
    }
    
    static int cadence_spi_phy_calibrate(struct cadence_spi_priv *priv,
    				     struct spi_slave *spi)
    {
    	struct phy_setting rxlow, rxhigh, txlow, txhigh;
    	struct phy_setting srxlow, srxhigh;
    	struct phy_setting bottomleft, topright, searchpoint;
    	struct phy_setting gaplow, gaphigh;
    	struct phy_setting backuppoint, backupcornerpoint;
    	struct udevice *bus = spi->dev->parent;
    	int ret, tmp;
    	bool primary = 1, secondary = 1;
    
    	priv->use_phy = true;
    
    	/*
    	 * Finding rx fails at some of the tx values based on the H/W platform.
    	 * A window of tx values is used to find the rx without errors. This
    	 * can increase the number of CPU cycles taken for the PHY tuning in
    	 * the cases where more tx values need to be parsed to find a stable
    	 * rx.
    	 */
    
    	/* ***********************Golden rxlow search*********************** */
    
    	/*
    	 *
    	 *		rx
    	 *	    127	^
    	 *		|
    	 *		|	xxxxx     ++++++++++++++++++++
    	 *		|	xxxxxx     +++++++++++++++++++
    	 *		|	xxxxxxx     ++++++++++++++++++
    	 *		|	xxxxxxxx     +++++++++++++++++
    	 *		|	xxxxxxxxx     ++++++++++++++++
    	 *		|	xxxxxxxxxx     +++++++++++++++
    	 *		|	xxxxxxxxxxx     ++++++++++++++
    	 *		|	|xxxxx|xxxxx     +++++++++++++
    	 *		|	|xxxxx|xxxxxx     ++++++++++++
    	 *	search	|	|xxxxx|xxxxxxx     +++++++++++
    	 *	rxlow --------->|xxxxx|xxxxxxxx     ++++++++++
    	 *		|	|xxxxx|xxxxxxxxx     +++++++++
    	 *		|	|xxxxx|xxxxxxxxxx     ++++++++
    	 *		|	|xxxxx|xxxxxxxxxxx     +++++++
    	 *		|	|     |
    	 *		--------|-----|----------------------------> tx
    	 *		0	|     |				 127
    	 *		    txlow     txlow
    	 *		    start     end
    	 *
    	 */
    
    	/*
    	 *	|----------------------------------------------------------|
    	 *	| Primary | Secondary | Final                              |
    	 *	| Search  | Search    | Point                              |
    	 *	|---------|-----------|------------------------------------|
    	 *	| Fail    | Fail      | Return Fail                        |
    	 *	|---------|-----------|------------------------------------|
    	 *	| Fail    | Pass      | Return Fail                        |
    	 *	|---------|-----------|------------------------------------|
    	 *	| Pass    | Fail      | Return Fail                        |
    	 *	|---------|-----------|------------------------------------|
    	 *	| Pass    | Pass      | rx = min(primary.rx, secondary.rx) |
    	 *	|         |           | tx = primary.tx                    |
    	 *	|         |           | read_delay =                       |
    	 *	|	  |	      |		min(primary.read_delay,    |
    	 *	|	  |	      |		    secondary.read_delay)  |
    	 *	|----------------------------------------------------------|
    	 */
    
    	/* *******************Golden Primary rxlow search******************* */
    	/*
    	 * To find the rx boundaries, we fix a valid tx and search through rx
    	 * range, read_delay values. As we are not sure of a valid tx we use a
    	 * window of tx values to find the rx boundaries.
    	 */
    
    	rxlow.tx = CQSPI_PHY_TX_LOOKUP_LOW_START;
    	do {
    		dev_dbg(bus, "Searching for Godlen Primary rxlow on TX = %d\n",
    			rxlow.tx);
    		rxlow.read_delay = CQSPI_PHY_INIT_RD;
    		ret = cadence_spi_find_rx_low(priv, spi, &rxlow);
    		rxlow.tx += CQSPI_PHY_DDR_SEARCH_STEP;
    	} while (ret && rxlow.tx <= CQSPI_PHY_TX_LOOKUP_LOW_END);
    	if (ret)
    		goto out;
    	dev_dbg(bus, "Golden Primary rxlow: RX: %d TX: %d RD: %d\n", rxlow.rx,
    		rxlow.tx, rxlow.read_delay);
    
    	/* ******************Golden Secondary rxlow search****************** */
    	/* Search for one more rxlow at different tx */
    
    	if (rxlow.tx <= (CQSPI_PHY_TX_LOOKUP_LOW_END -
    			 CQSPI_PHY_SEARCH_OFFSET))
    		srxlow.tx = rxlow.tx + CQSPI_PHY_SEARCH_OFFSET;
    	else
    		srxlow.tx = CQSPI_PHY_TX_LOOKUP_LOW_END;
    	dev_dbg(bus, "Searching for Golden Secondary rxlow on TX = %d\n",
    		srxlow.tx);
    	srxlow.read_delay = CQSPI_PHY_INIT_RD;
    	ret = cadence_spi_find_rx_low(priv, spi, &srxlow);
    	if (ret)
    		goto out;
    	dev_dbg(bus, "Golden Secondary rxlow: RX: %d TX: %d RD: %d\n",
    		srxlow.rx, srxlow.tx, srxlow.read_delay);
    
    	rxlow.rx = min(rxlow.rx, srxlow.rx);
    	rxlow.read_delay = min(rxlow.read_delay, srxlow.read_delay);
    	dev_dbg(bus, "Golden Final rxlow: RX: %d TX: %d RD: %d\n", rxlow.rx,
    		rxlow.tx, rxlow.read_delay);
    
    	/* **********************Golden rxhigh search********************** */
    
    	/*
    	 *
    	 *		rx
    	 *	    127	^
    	 *		|
    	 *		|	|xxxx     ++++++++++++++++++++
    	 *		|	|xxxxx     +++++++++++++++++++
    	 *    search	|	|xxxxxx     ++++++++++++++++++
    	 *    rxhigh  --------->|xxxxxxx     +++++++++++++++++
    	 *    on fixed  |	|xxxxxxxx     ++++++++++++++++
    	 *    tx	|	|xxxxxxxxx     +++++++++++++++
    	 *		|	|xxxxxxxxxx     ++++++++++++++
    	 *		|	xxxxxxxxxxxx     +++++++++++++
    	 *		|	xxxxxxxxxxxxx     ++++++++++++
    	 *		|	xxxxxxxxxxxxxx     +++++++++++
    	 *		|	xxxxxxxxxxxxxxx     ++++++++++
    	 *		|	xxxxxxxxxxxxxxxx     +++++++++
    	 *		|	xxxxxxxxxxxxxxxxx     ++++++++
    	 *		|	xxxxxxxxxxxxxxxxxx     +++++++
    	 *		|
    	 *		-------------------------------------------> tx
    	 *		0					 127
    	 *
    	 */
    
    	/*
    	 *	|----------------------------------------------------------|
    	 *	| Primary | Secondary | Final                              |
    	 *	| Search  | Search    | Point                              |
    	 *	|---------|-----------|------------------------------------|
    	 *	| Fail    | Fail      | Return Fail                        |
    	 *	|---------|-----------|------------------------------------|
    	 *	| Fail    | Pass      | Choose Secondary                   |
    	 *	|---------|-----------|------------------------------------|
    	 *	| Pass    | Fail      | Choose Primary                     |
    	 *	|---------|-----------|------------------------------------|
    	 *	| Pass    | Pass      | if (secondary.rx > primary.rx)     |
    	 *	|         |           |		Choose Secondary           |
    	 *	|         |           | else                               |
    	 *	|	  |	      |		Choose Primary             |
    	 *	|----------------------------------------------------------|
    	 */
    
    	/* ******************Golden Primary rxhigh search****************** */
    	/*
    	 * To find rxhigh we use the tx values of rxlow. Start the read_delay
    	 * from maximum and decrement it. As these are valid values and rxhigh
    	 * read_delay is always greater than or equal to rxlow read_delay.
    	 */
    
    	rxhigh.tx = rxlow.tx;
    	dev_dbg(bus, "Searching for Golden Primary rxhigh on TX = %d\n",
    		rxhigh.tx);
    	rxhigh.read_delay = CQSPI_PHY_MAX_RD;
    	ret = cadence_spi_find_rx_high(priv, spi, &rxhigh);
    	if (ret)
    		primary = 0;
    	dev_dbg(bus, "Golden Primary rxhigh: RX: %d TX: %d RD: %d\n", rxhigh.rx,
    		rxhigh.tx, rxhigh.read_delay);
    
    	/* *****************Golden Secondary rxhigh search***************** */
    	/* Search for one more rxhigh at different tx */
    
    	if (rxhigh.tx <=
    	    (CQSPI_PHY_TX_LOOKUP_LOW_END - CQSPI_PHY_SEARCH_OFFSET))
    		srxhigh.tx = rxhigh.tx + CQSPI_PHY_SEARCH_OFFSET;
    	else
    		srxhigh.tx = CQSPI_PHY_TX_LOOKUP_LOW_END;
    	dev_dbg(bus, "Searching for Golden Secondary rxhigh on TX = %d\n",
    		srxhigh.tx);
    	srxhigh.read_delay = CQSPI_PHY_MAX_RD;
    	ret = cadence_spi_find_rx_high(priv, spi, &srxhigh);
    	if (ret)
    		secondary = 0;
    	dev_dbg(bus, "Golden Secondary rxhigh: RX: %d TX: %d RD: %d\n",
    		srxhigh.rx, srxhigh.tx, srxhigh.read_delay);
    
    	if (primary || secondary) {
    		if (srxhigh.rx > rxhigh.rx)
    			rxhigh = srxhigh;
    	} else {
    		goto out;
    	}
    	dev_dbg(bus, "Golden Final rxhigh: RX: %d TX: %d RD: %d\n", rxhigh.rx,
    		rxhigh.tx, rxhigh.read_delay);
    
    	primary = 1;
    	secondary = 1;
    
    	/*
    	 * Check a different point if rxlow and rxhigh are on the same read
    	 * delay. This avoids mistaking the failing region for an RX boundary.
    	 */
    
    	if (rxlow.read_delay == rxhigh.read_delay) {
    		dev_dbg(bus, "rxlow and rxhigh at the same read delay.\n");
    
    		/* *******************Backup rxlow search******************* */
    
    		/* Look for RX boundaries at upper TX range. */
    
    		/*
    		 *
    		 *		rx
    		 *	    127	^
    		 *		|
    		 *		|	xxxxx     ++++++++++++++++++++
    		 *		|	xxxxxx     +++++++++++++++++++
    		 *		|	xxxxxxx     ++++++++++++++++++
    		 *		|	xxxxxxxx     +++++++++++++++++
    		 *		|	xxxxxxxxx     ++++++++++++++++
    		 *		|	xxxxxxxxxx     +++++++++++++++
    		 *		|	xxxxxxxxxxx     ++++++++++++++
    		 *		|	xxxxxxxxxxxx     +++++++|++++|
    		 *		|	xxxxxxxxxxxxx     ++++++|++++|
    		 *	search	|	xxxxxxxxxxxxxx     +++++|++++|
    		 *	rxlow --------------------------------->|++++|
    		 *		|	xxxxxxxxxxxxxxxx     +++|++++|
    		 *		|	xxxxxxxxxxxxxxxxx     ++|++++|
    		 *		|	xxxxxxxxxxxxxxxxxx     +|++++|
    		 *		|				|    |
    		 *		--------------------------------|----|-----> tx
    		 *		0				|    |	 127
    		 *`					   txhigh    txhigh
    		 *					    start    end
    		 *
    		 */
    
    		/*
    		 *	|-----------------------------------------------------|
    		 *	| Primary | Secondary | Final                         |
    		 *	| Search  | Search    | Point                         |
    		 *	|---------|-----------|-------------------------------|
    		 *	| Fail    | Fail      | Return Fail                   |
    		 *	|---------|-----------|-------------------------------|
    		 *	| Fail    | Pass      | Return Fail                   |
    		 *	|---------|-----------|-------------------------------|
    		 *	| Pass    | Fail      | Return Fail                   |
    		 *	|---------|-----------|-------------------------------|
    		 *	| Pass    | Pass      | rx =			      |
    		 *	|	  |	      |	 min(primary.rx, secondary.rx)|
    		 *	|         |           | tx = primary.tx               |
    		 *	|         |           | read_delay =                  |
    		 *	|	  |	      |	 min(primary.read_delay,      |
    		 *	|	  |	      |	     secondary.read_delay)    |
    		 *	|-----------------------------------------------------|
    		 */
    
    		/* ***************Backup Primary rxlow search*************** */
    		/*
    		 * Find the rx boundaries using the tx window at the higher
    		 * end. We start at the window end and decrement the tx value
    		 * until we find the valid point.
    		 */
    
    		backuppoint.tx = CQSPI_PHY_TX_LOOKUP_HIGH_END;
    		do {
    			dev_dbg(bus, "Searching for Backup Primary rxlow on TX = %d\n",
    				backuppoint.tx);
    			backuppoint.read_delay = CQSPI_PHY_INIT_RD;
    			ret = cadence_spi_find_rx_low(priv, spi, &backuppoint);
    			backuppoint.tx -= CQSPI_PHY_DDR_SEARCH_STEP;
    		} while (ret &&
    			 backuppoint.tx >= CQSPI_PHY_TX_LOOKUP_HIGH_START);
    		if (ret)
    			goto out;
    		dev_dbg(bus, "Backup Primary rxlow: RX: %d TX: %d RD: %d\n",
    			backuppoint.rx, backuppoint.tx,
    			backuppoint.read_delay);
    
    		/* **************Backup Secondary rxlow search************** */
    		/* Search for one more rxlow at different tx */
    
    		if (backuppoint.tx >=
    		    (CQSPI_PHY_TX_LOOKUP_HIGH_START + CQSPI_PHY_SEARCH_OFFSET))
    			srxlow.tx = backuppoint.tx - CQSPI_PHY_SEARCH_OFFSET;
    		else
    			srxlow.tx = CQSPI_PHY_TX_LOOKUP_HIGH_START;
    		dev_dbg(bus,
    			"Searching for Backup Secondary rxlow on TX = %d\n",
    			srxlow.tx);
    		srxlow.read_delay = CQSPI_PHY_INIT_RD;
    		ret = cadence_spi_find_rx_low(priv, spi, &srxlow);
    		if (ret)
    			goto out;
    		dev_dbg(bus, "Backup Secondary rxlow: RX: %d TX: %d RD: %d\n",
    			srxlow.rx, srxlow.tx, srxlow.read_delay);
    
    		backuppoint.rx = min(backuppoint.rx, srxlow.rx);
    		backuppoint.read_delay =
    		    min(backuppoint.read_delay, srxlow.read_delay);
    		dev_dbg(bus, "Backup Final rxlow: RX: %d TX: %d RD: %d\n",
    			backuppoint.rx, backuppoint.tx,
    			backuppoint.read_delay);
    
    		if (backuppoint.rx < rxlow.rx) {
    			rxlow = backuppoint;
    			dev_dbg(bus, "Updating rxlow to the one at TX = %d\n",
    				backuppoint.tx);
    		}
    		dev_dbg(bus, "Final rxlow: RX: %d TX: %d RD: %d\n", rxlow.rx,
    			rxlow.tx, rxlow.read_delay);
    
    		/* ******************Backup rxhigh search****************** */
    
    		/*
    		 *
    		 *		rx
    		 *	    127	^
    		 *		|
    		 *		|	xxxxx     +++++++++++++++++++|
    		 *		|	xxxxxx     ++++++++++++++++++|
    		 *    search	|	xxxxxxx     +++++++++++++++++|
    		 *    rxhigh  -------------------------------------->|
    		 *    on fixed	|	xxxxxxxxx     +++++++++++++++|
    		 *    tx	|	xxxxxxxxxx     ++++++++++++++|
    		 *		|	xxxxxxxxxxx     +++++++++++++|
    		 *		|	xxxxxxxxxxxx     +++++++++++++
    		 *		|	xxxxxxxxxxxxx     ++++++++++++
    		 *		|	xxxxxxxxxxxxxx     +++++++++++
    		 *		|	xxxxxxxxxxxxxxx     ++++++++++
    		 *		|	xxxxxxxxxxxxxxxx     +++++++++
    		 *		|	xxxxxxxxxxxxxxxxx     ++++++++
    		 *		|	xxxxxxxxxxxxxxxxxx     +++++++
    		 *		|
    		 *		-------------------------------------------> tx
    		 *		0					 127
    		 *
    		 */
    
    		/*
    		 *	|-----------------------------------------------------|
    		 *	| Primary | Secondary | Final                         |
    		 *	| Search  | Search    | Point                         |
    		 *	|---------|-----------|-------------------------------|
    		 *	| Fail    | Fail      | Return Fail                   |
    		 *	|---------|-----------|-------------------------------|
    		 *	| Fail    | Pass      | Choose Secondary              |
    		 *	|---------|-----------|-------------------------------|
    		 *	| Pass    | Fail      | Choose Primary                |
    		 *	|---------|-----------|-------------------------------|
    		 *	| Pass    | Pass      | if (secondary.rx > primary.rx)|
    		 *	|         |           |		Choose Secondary      |
    		 *	|         |           | else                          |
    		 *	|	  |	      |		Choose Primary        |
    		 *	|-----------------------------------------------------|
    		 */
    
    		/* **************Backup Primary rxhigh search************** */
    		/*
    		 * To find rxhigh we use the tx values of backuppoint. Start
    		 * the read_delay from maximum and decrement it. As these are
    		 * valid values and rxhigh read_delay is always greater than or
    		 * equal to rxlow read_delay.
    		 */
    
    		dev_dbg(bus, "Searching for Backup Primary rxhigh on TX = %d\n",
    			backuppoint.tx);
    		backuppoint.read_delay = CQSPI_PHY_MAX_RD;
    		ret = cadence_spi_find_rx_high(priv, spi, &backuppoint);
    		if (ret)
    			primary = 0;
    		dev_dbg(bus, " Backup Primary rxhigh: RX: %d TX: %d RD: %d\n",
    			backuppoint.rx, backuppoint.tx,
    			backuppoint.read_delay);
    
    		/* *************Backup Secondary rxhigh search************* */
    		/* Search for one more rxhigh at different tx */
    
    		if (backuppoint.tx >=
    		    (CQSPI_PHY_TX_LOOKUP_HIGH_START + CQSPI_PHY_SEARCH_OFFSET))
    			srxhigh.tx = backuppoint.tx - CQSPI_PHY_SEARCH_OFFSET;
    		else
    			srxhigh.tx = CQSPI_PHY_TX_LOOKUP_HIGH_START;
    		dev_dbg(bus,
    			"Searching for Backup Secondary rxhigh on TX = %d\n",
    			srxhigh.tx);
    		srxhigh.read_delay = CQSPI_PHY_MAX_RD;
    		ret = cadence_spi_find_rx_high(priv, spi, &srxhigh);
    		if (ret)
    			secondary = 0;
    		dev_dbg(bus, "Backup Secondary rxhigh: RX: %d TX: %d RD: %d\n",
    			srxhigh.rx, srxhigh.tx, srxhigh.read_delay);
    
    		if (primary || secondary) {
    			if (srxhigh.rx > backuppoint.rx)
    				backuppoint = srxhigh;
    		} else {
    			goto out;
    		}
    		dev_dbg(bus, "Backup Final rxhigh: RX: %d TX: %d RD: %d\n",
    			backuppoint.rx, backuppoint.tx,
    			backuppoint.read_delay);
    
    		if (backuppoint.rx > rxhigh.rx) {
    			rxhigh = backuppoint;
    			dev_dbg(bus, "Updating rxhigh to the one at TX = %d\n",
    				backuppoint.tx);
    		}
    		dev_dbg(bus, "Final rxhigh: RX: %d TX: %d RD: %d\n", rxhigh.rx,
    			rxhigh.tx, rxhigh.read_delay);
    	}
    
    	/* ***********************Golden txlow search*********************** */
    	/* Look for TX boundaries at 1/4 of RX window. */
    
    	/*
    	 *
    	 *		rx
    	 *	    127	^
    	 *		|
    	 *     rxhigh --------->xxxxx     ++++++++++++++++++++
    	 *		|	xxxxxx     +++++++++++++++++++
    	 *		|	xxxxxxx     ++++++++++++++++++
    	 *		|	xxxxxxxx     +++++++++++++++++
    	 *		|	xxxxxxxxx     ++++++++++++++++
    	 *		|	xxxxxxxxxx     +++++++++++++++
    	 *		|	xxxxxxxxxxx     ++++++++++++++
    	 *		|	xxxxxxxxxxxx     +++++++++++++
    	 *    fix rx	|	xxxxxxxxxxxxx     ++++++++++++
    	 *    1/4 b/w ---------><------->xxxxx     +++++++++++
    	 *    rxlow and	|	xxxx|xxxxxxxxxx     ++++++++++
    	 *    rxhigh	|	xxxx|xxxxxxxxxxx     +++++++++
    	 *		|	xxxx|xxxxxxxxxxxx     ++++++++
    	 *	rxlow --------->xxxx|xxxxxxxxxxxxx     +++++++
    	 *		|	    |
    	 *		------------|------------------------------> tx
    	 *		0	    |				 127
    	 *		       search
    	 *			txlow
    	 *
    	 */
    
    	txlow.rx = rxlow.rx + ((rxhigh.rx - rxlow.rx) / 4);
    	dev_dbg(bus, "Searching for Golden txlow on RX = %d\n", txlow.rx);
    	txlow.read_delay = CQSPI_PHY_INIT_RD;
    	ret = cadence_spi_find_tx_low(priv, spi, &txlow);
    	if (ret)
    		goto out;
    	dev_dbg(bus, "Golden txlow: RX: %d TX: %d RD: %d\n", txlow.rx,
    		txlow.tx, txlow.read_delay);
    
    	/* **********************Golden txhigh search********************** */
    	/* Start from maximum read_delay and decrememt it */
    
    	/*
    	 *
    	 *		rx
    	 *	    127	^
    	 *		|
    	 *     rxhigh --------->xxxxx     ++++++++++++++++++++
    	 *		|	xxxxxx     +++++++++++++++++++
    	 *		|	xxxxxxx     ++++++++++++++++++
    	 *		|	xxxxxxxx     +++++++++++++++++
    	 *		|	xxxxxxxxx     ++++++++++++++++
    	 *		|	xxxxxxxxxx     +++++++++++++++
    	 *		|	xxxxxxxxxxx     ++++++++++++++
    	 *		|	xxxxxxxxxxxx     +++++++++++++
    	 *    fix rx	|	xxxxxxxxxxxxx     ++++++++++++
    	 *    1/4 b/w --------------------------------><----->
    	 *    rxlow and	|	xxxxxxxxxxxxxxx     ++++++|+++
    	 *    rxhigh	|	xxxxxxxxxxxxxxxx     +++++|+++
    	 *		|	xxxxxxxxxxxxxxxxx     ++++|+++
    	 *	rxlow --------->xxxxxxxxxxxxxxxxxx     +++|+++
    	 *		|				  |
    	 *		----------------------------------|--------> tx
    	 *		0				  |	 127
    	 *					     search
    	 *					     txhigh
    	 *
    	 */
    
    	txhigh.rx = txlow.rx;
    	dev_dbg(bus, "Searching for Golden txhigh on RX = %d\n", txhigh.rx);
    	txhigh.read_delay = CQSPI_PHY_MAX_RD;
    	ret = cadence_spi_find_tx_high(priv, spi, &txhigh);
    	if (ret)
    		goto out;
    	dev_dbg(bus, "Golden txhigh: RX: %d TX: %d RD: %d\n", txhigh.rx,
    		txhigh.tx, txhigh.read_delay);
    
    	/*
    	 * Check a different point if txlow and txhigh are on the same read
    	 * delay. This avoids mistaking the failing region for an TX boundary.
    	 */
    	if (txlow.read_delay == txhigh.read_delay) {
    		/* *******************Backup txlow search******************* */
    		/* Look for TX boundaries at 3/4 of RX window. */
    
    		/*
    		 *
    		 *		rx
    		 *	    127	^
    		 *		|
    		 *     rxhigh --------->xxxxx     ++++++++++++++++++++
    		 *		|	xxxxxx     +++++++++++++++++++
    		 *    fix rx	|	xxxxxxx     ++++++++++++++++++
    		 *    3/4 b/w ---------><----->x     +++++++++++++++++
    		 *    rxlow and	|	xxxx|xxxx     ++++++++++++++++
    		 *    rxhigh	|	xxxx|xxxxx     +++++++++++++++
    		 *		|	xxxx|xxxxxx     ++++++++++++++
    		 *		|	xxxx|xxxxxxx     +++++++++++++
    		 *		|	xxxx|xxxxxxxx     ++++++++++++
    		 *		|	xxxx|xxxxxxxxx     +++++++++++
    		 *		|	xxxx|xxxxxxxxxx     ++++++++++
    		 *		|	xxxx|xxxxxxxxxxx     +++++++++
    		 *		|	xxxx|xxxxxxxxxxxx     ++++++++
    		 *	rxlow --------->xxxx|xxxxxxxxxxxxx     +++++++
    		 *		|	    |
    		 *		------------|------------------------------> tx
    		 *		0	    |				 127
    		 *		       search
    		 *			txlow
    		 *
    		 */
    
    		dev_dbg(bus, "txlow and txhigh at the same read delay.\n");
    		backuppoint.rx = rxlow.rx + (3 * (rxhigh.rx - rxlow.rx) / 4);
    		dev_dbg(bus, "Searching for Backup txlow on RX = %d\n",
    			backuppoint.rx);
    		backuppoint.read_delay = CQSPI_PHY_INIT_RD;
    		ret = cadence_spi_find_tx_low(priv, spi, &backuppoint);
    		if (ret)
    			goto out;
    		dev_dbg(bus, "Backup txlow: RX: %d TX: %d RD: %d\n",
    			backuppoint.rx, backuppoint.tx,
    			backuppoint.read_delay);
    
    		if (backuppoint.tx < txlow.tx) {
    			txlow = backuppoint;
    			dev_dbg(bus, "Updating txlow with the one at RX = %d\n",
    				backuppoint.rx);
    		}
    		dev_dbg(bus, "Final txlow: RX: %d TX: %d RD: %d\n", txlow.rx,
    			txlow.tx, txlow.read_delay);
    
    		/* ******************Backup txhigh search****************** */
    		/* Start from maximum read_delay and decrememt it */
    
    		/*
    		 *
    		 *		rx
    		 *	    127	^
    		 *		|
    		 *     rxhigh --------->xxxxx     ++++++++++++++++++++
    		 *		|	xxxxxx     +++++++++++++++++++
    		 *    fix rx	|	xxxxxxx     ++++++++++++++++++
    		 *    3/4 b/w ------------------------------><------->
    		 *    rxlow and	|	xxxxxxxxx     +++++++++++|++++
    		 *    rxhigh	|	xxxxxxxxxx     ++++++++++|++++
    		 *		|	xxxxxxxxxxx     +++++++++|++++
    		 *		|	xxxxxxxxxxxx     ++++++++|++++
    		 *		|	xxxxxxxxxxxxx     +++++++|++++
    		 *		|	xxxxxxxxxxxxxx     ++++++|++++
    		 *		|	xxxxxxxxxxxxxxx     +++++|++++
    		 *		|	xxxxxxxxxxxxxxxx     ++++|++++
    		 *		|	xxxxxxxxxxxxxxxxx     +++|++++
    		 *	rxlow --------->xxxxxxxxxxxxxxxxxx     ++|++++
    		 *		|				 |
    		 *		---------------------------------|---------> tx
    		 *		0				 |	 127
    		 *						 search
    		 *						 txhigh
    		 *
    		 */
    
    		dev_dbg(bus, "Searching for Backup txhigh on RX = %d\n",
    			backuppoint.rx);
    		backuppoint.read_delay = CQSPI_PHY_MAX_RD;
    		ret = cadence_spi_find_tx_high(priv, spi, &backuppoint);
    		if (ret)
    			goto out;
    		dev_dbg(bus, "Backup txhigh: RX: %d TX: %d RD: %d\n",
    			backuppoint.rx, backuppoint.tx,
    			backuppoint.read_delay);
    
    		if (backuppoint.tx > txhigh.tx) {
    			txhigh = backuppoint;
    			dev_dbg(bus,
    				"Updating txhigh with the one at RX = %d\n",
    				backuppoint.rx);
    		}
    		dev_dbg(bus, "Final txhigh: RX: %d TX: %d RD: %d\n", txhigh.rx,
    			txhigh.tx, txhigh.read_delay);
    	}
    
    	/*
    	 * Set bottom left and top right corners. These are theoretical
    	 * corners. They may not actually be "good" points. But the longest
    	 * diagonal will be between these corners.
    	 */
    
    	bottomleft.tx = txlow.tx;
    	bottomleft.rx = rxlow.rx;
    	if (txlow.read_delay <= rxlow.read_delay)
    		bottomleft.read_delay = txlow.read_delay;
    	else
    		bottomleft.read_delay = rxlow.read_delay;
    
    	backupcornerpoint = bottomleft;
    	backupcornerpoint.tx += 4;
    	backupcornerpoint.rx += 4;
    	ret = cadence_spi_phy_apply_setting(priv, &backupcornerpoint);
    	if (!ret)
    		ret = cadence_spi_phy_check_pattern(priv, spi);
    
    	if (ret) {
    		backupcornerpoint.read_delay--;
    		ret = cadence_spi_phy_apply_setting(priv, &backupcornerpoint);
    		if (!ret)
    			ret = cadence_spi_phy_check_pattern(priv, spi);
    	}
    
    	/* TODO: if (ret) */
    
    	if (!ret)
    		bottomleft.read_delay = backupcornerpoint.read_delay;
    
    	topright.tx = txhigh.tx;
    	topright.rx = rxhigh.rx;
    	if (txhigh.read_delay >= rxhigh.read_delay)
    		topright.read_delay = txhigh.read_delay;
    	else
    		topright.read_delay = rxhigh.read_delay;
    
    	backupcornerpoint = topright;
    	backupcornerpoint.tx -= 4;
    	backupcornerpoint.rx -= 4;
    	ret = cadence_spi_phy_apply_setting(priv, &backupcornerpoint);
    	if (!ret)
    		ret = cadence_spi_phy_check_pattern(priv, spi);
    
    	if (ret) {
    		backupcornerpoint.read_delay++;
    		ret = cadence_spi_phy_apply_setting(priv, &backupcornerpoint);
    		if (!ret)
    			ret = cadence_spi_phy_check_pattern(priv, spi);
    	}
    
    	/* TODO: if (ret) */
    
    	if (!ret)
    		topright.read_delay = backupcornerpoint.read_delay;
    
    	dev_dbg(bus, "topright: RX: %d TX: %d RD: %d\n", topright.rx,
    		topright.tx, topright.read_delay);
    	dev_dbg(bus, "bottomleft: RX: %d TX: %d RD: %d\n", bottomleft.rx,
    		bottomleft.tx, bottomleft.read_delay);
    
    	ret = cadence_spi_phy_find_gaplow(priv, spi, &bottomleft, &topright,
    					  &gaplow);
    	if (ret)
    		goto out;
    	dev_dbg(bus, "gaplow: RX: %d TX: %d RD: %d\n", gaplow.rx, gaplow.tx,
    		gaplow.read_delay);
    
    	if (bottomleft.read_delay == topright.read_delay) {
    		/*
    		 * If there is only one passing region, it means that the
    		 * "true" topright is too small to find, so the start of the
    		 * failing region is a good approximation. Put the tuning point
    		 * in the middle and adjust for temperature.
    		 */
    
    		dev_dbg(bus,
    			"bottomleft and topright at the same read delay.\n");
    		topright = gaplow;
    		searchpoint.read_delay = bottomleft.read_delay;
    		searchpoint.tx = bottomleft.tx +
    				 ((topright.tx - bottomleft.tx) / 2);
    		searchpoint.rx = bottomleft.rx +
    				 ((topright.rx - bottomleft.rx) / 2);
    
    		ret = cadence_spi_get_temp(&tmp);
    		if (ret) {
    			/*
    			 * Assume room temperature if we couldn't get it from
    			 * the thermal sensor.
    			 */
    
    			dev_dbg(bus,
    				"Unable to get temperature. Assuming room temperature\n");
    			tmp = CQSPI_PHY_DEFAULT_TEMP;
    		}
    
    		if (tmp < CQSPI_PHY_MIN_TEMP || tmp > CQSPI_PHY_MAX_TEMP) {
    			printf("ERROR: temperature outside operating range: %dC\n",
    			       tmp);
    			return -EINVAL;
    		}
    
    		/* Avoid a divide-by-zero. */
    
    		if (tmp == CQSPI_PHY_MID_TEMP)
    			tmp++;
    		dev_dbg(bus, "Temperature: %dC\n", tmp);
    
    		searchpoint.tx += (topright.tx - bottomleft.tx) /
    				  (330 / (tmp - CQSPI_PHY_MID_TEMP));
    		searchpoint.rx += (topright.rx - bottomleft.rx) /
    				  (330 / (tmp - CQSPI_PHY_MID_TEMP));
    	} else {
    		/*
    		 * If there are two passing regions, find the start and end of
    		 * the second one.
    		 */
    
    		ret = cadence_spi_phy_find_gaphigh(priv, spi, &bottomleft,
    						   &topright, &gaphigh);
    		if (ret)
    			return ret;
    		dev_dbg(bus, "gaphigh: RX: %d TX: %d RD: %d\n", gaphigh.rx,
    			gaphigh.tx, gaphigh.read_delay);
    
    		/*
    		 * Place the final tuning point in the corner furthest from the
    		 * failing region but leave some margin for temperature
    		 * changes.
    		 */
    
    		if ((abs(gaplow.tx - bottomleft.tx) +
    		     abs(gaplow.rx - bottomleft.rx)) <
    		    (abs(gaphigh.tx - topright.tx) +
    		     abs(gaphigh.rx - topright.rx))) {
    			searchpoint = topright;
    			searchpoint.tx -= 16;
    			searchpoint.rx -= (16 * (topright.rx - bottomleft.rx))
    					  / (topright.tx - bottomleft.tx);
    		} else {
    			searchpoint = bottomleft;
    			searchpoint.tx += 16;
    			searchpoint.rx += (16 * (topright.rx - bottomleft.rx))
    					  / (topright.tx - bottomleft.tx);
    		}
    	}
    
    	/* Set the final PHY settings we found. */
    
    	dev_dbg(bus, "Final tuning point: RX: %d TX: %d RD: %d\n",
    		searchpoint.rx, searchpoint.tx, searchpoint.read_delay);
    	ret = cadence_spi_phy_apply_setting(priv, &searchpoint);
    	if (!ret)
    		ret = cadence_spi_phy_check_pattern(priv, spi);
    
    	if (ret) {
    		debug("Failed to find pattern at final calibration point\n");
    		ret = -EINVAL;
    		goto out;
    	}
    
    	ret = 0;
    	priv->phy_read_delay = searchpoint.read_delay;
    out:
    	if (ret)
    		priv->use_phy = false;
    	return ret;
    }
    
    static void cadence_spi_phy_reset_setting(struct phy_setting *phy)
    {
    	phy->rx = 0;
    	phy->tx = 127;
    	phy->read_delay = 0;
    }
    
    static int cadence_spi_phy_calibrate_sdr(struct cadence_spi_priv *priv,
    					 struct spi_slave *spi)
    {
    	struct udevice *bus = spi->dev->parent;
    	struct phy_setting rxlow, rxhigh, first, second, final;
    	char window1 = 0;
    	char window2 = 0;
    	int ret;
    
    	priv->use_phy = true;
    	cadence_spi_phy_reset_setting(&rxlow);
    	cadence_spi_phy_reset_setting(&rxhigh);
    	cadence_spi_phy_reset_setting(&first);
    
    	do {
    		ret = cadence_spi_find_rx_low_sdr(priv, spi, &rxlow);
    
    		if (ret)
    			rxlow.read_delay++;
    	} while (ret && rxlow.read_delay <= CQSPI_PHY_MAX_RD);
    
    	rxhigh.read_delay = rxlow.read_delay;
    	ret = cadence_spi_find_rx_high_sdr(priv, spi, &rxhigh, rxlow.rx);
    	if (ret)
    		goto out;
    
    	first.read_delay = rxlow.read_delay;
    	window1 = rxhigh.rx - rxlow.rx;
    	first.rx = rxlow.rx + (window1 / 2);
    
    	dev_dbg(bus, "First tuning point: RX: %d TX: %d RD: %d\n", first.rx,
    		first.tx, first.read_delay);
    	ret = cadence_spi_phy_apply_setting(priv, &first);
    	if (!ret)
    		ret = cadence_spi_phy_check_pattern(priv, spi);
    
    	if (ret || first.read_delay > CQSPI_PHY_MAX_RD)
    		goto out;
    
    	cadence_spi_phy_reset_setting(&rxlow);
    	cadence_spi_phy_reset_setting(&rxhigh);
    	cadence_spi_phy_reset_setting(&second);
    
    	rxlow.read_delay = first.read_delay + 1;
    	if (rxlow.read_delay > CQSPI_PHY_MAX_RD)
    		goto compare;
    
    	ret = cadence_spi_find_rx_low_sdr(priv, spi, &rxlow);
    
    	if (ret)
    		goto compare;
    
    	rxhigh.read_delay = rxlow.read_delay;
    	ret = cadence_spi_find_rx_high_sdr(priv, spi, &rxhigh, rxlow.rx);
    	if (ret)
    		goto compare;
    
    	window2 = rxhigh.rx - rxlow.rx;
    	second.rx = rxlow.rx + (window2 / 2);
    	second.read_delay = rxlow.read_delay;
    
    	dev_dbg(bus, "Second tuning point: RX: %d TX: %d RD: %d\n", second.rx,
    		second.tx, second.read_delay);
    	ret = cadence_spi_phy_apply_setting(priv, &second);
    	if (!ret)
    		ret = cadence_spi_phy_check_pattern(priv, spi);
    
    	if (ret || second.read_delay > CQSPI_PHY_MAX_RD)
    		window2 = 0;
    
    compare:
    	cadence_spi_phy_reset_setting(&final);
    	if (window2 > window1) {
    		final.rx = second.rx;
    		final.read_delay = second.read_delay;
    	} else {
    		final.rx = first.rx;
    		final.read_delay = first.read_delay;
    	}
    
    	dev_dbg(bus, "Final tuning point: RX: %d TX: %d RD: %d\n", final.rx,
    		final.tx, final.read_delay);
    	ret = cadence_spi_phy_apply_setting(priv, &final);
    	if (!ret)
    		ret = cadence_spi_phy_check_pattern(priv, spi);
    
    	if (ret) {
    		ret = -EINVAL;
    		goto out;
    	}
    
    out:
    	if (ret)
    		priv->use_phy = false;
    
    	return ret;
    }
    
    static int cadence_spi_write_speed(struct udevice *bus, uint hz)
    {
    	struct cadence_spi_priv *priv = dev_get_priv(bus);
    
    	cadence_qspi_apb_config_baudrate_div(priv->regbase,
    					     priv->ref_clk_hz, hz);
    
    	/* Reconfigure delay timing if speed is changed. */
    	cadence_qspi_apb_delay(priv->regbase, priv->ref_clk_hz, hz,
    			       priv->tshsl_ns, priv->tsd2d_ns,
    			       priv->tchsh_ns, priv->tslch_ns);
    
    	return 0;
    }
    
    static int cadence_spi_read_id(struct cadence_spi_priv *priv, u8 len,
    			       u8 *idcode)
    {
    	int err;
    
    	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1),
    					  SPI_MEM_OP_NO_ADDR,
    					  SPI_MEM_OP_NO_DUMMY,
    					  SPI_MEM_OP_DATA_IN(len, idcode, 1));
    
    	err = cadence_qspi_apb_command_read_setup(priv, &op);
    	if (!err)
    		err = cadence_qspi_apb_command_read(priv, &op);
    
    	return err;
    }
    
    /* Calibration sequence to determine the read data capture delay register */
    static int spi_calibration(struct udevice *bus, uint hz)
    {
    	struct cadence_spi_priv *priv = dev_get_priv(bus);
    	void *base = priv->regbase;
    	unsigned int idcode = 0, temp = 0;
    	int err = 0, i, range_lo = -1, range_hi = -1;
    
    	/* start with slowest clock (1 MHz) */
    	cadence_spi_write_speed(bus, 1000000);
    
    	/* configure the read data capture delay register to 0 */
    	cadence_qspi_apb_readdata_capture(base, 1, true, 0);
    
    	/* Enable QSPI */
    	cadence_qspi_apb_controller_enable(base);
    
    	/* read the ID which will be our golden value */
    	err = cadence_spi_read_id(priv, 3, (u8 *)&idcode);
    	if (err) {
    		puts("SF: Calibration failed (read)\n");
    		return err;
    	}
    
    	/* use back the intended clock and find low range */
    	cadence_spi_write_speed(bus, hz);
    	for (i = 0; i < CQSPI_READ_CAPTURE_MAX_DELAY; i++) {
    		/* Disable QSPI */
    		cadence_qspi_apb_controller_disable(base);
    
    		/* reconfigure the read data capture delay register */
    		cadence_qspi_apb_readdata_capture(base, 1, true, i);
    
    		/* Enable back QSPI */
    		cadence_qspi_apb_controller_enable(base);
    
    		/* issue a RDID to get the ID value */
    		err = cadence_spi_read_id(priv, 3, (u8 *)&temp);
    		if (err) {
    			puts("SF: Calibration failed (read)\n");
    			return err;
    		}
    
    		/* search for range lo */
    		if (range_lo == -1 && temp == idcode) {
    			range_lo = i;
    			continue;
    		}
    
    		/* search for range hi */
    		if (range_lo != -1 && temp != idcode) {
    			range_hi = i - 1;
    			break;
    		}
    		range_hi = i;
    	}
    
    	if (range_lo == -1) {
    		puts("SF: Calibration failed (low range)\n");
    		return err;
    	}
    
    	/* Disable QSPI for subsequent initialization */
    	cadence_qspi_apb_controller_disable(base);
    
    	/* configure the final value for read data capture delay register */
    	cadence_qspi_apb_readdata_capture(base, 1, true, (range_hi + range_lo) / 2);
    	debug("SF: Read data capture delay calibrated to %i (%i - %i)\n",
    	      (range_hi + range_lo) / 2, range_lo, range_hi);
    
    	/* just to ensure we do once only when speed or chip select change */
    	priv->qspi_calibrated_hz = hz;
    	priv->qspi_calibrated_cs = spi_chip_select(bus);
    
    	return 0;
    }
    
    static int cadence_spi_set_speed(struct udevice *bus, uint hz)
    {
    	struct cadence_spi_priv *priv = dev_get_priv(bus);
    	int err;
    
    	if (!hz || hz > priv->max_hz)
    		hz = priv->max_hz;
    	/* Disable QSPI */
    	cadence_qspi_apb_controller_disable(priv->regbase);
    
    	/*
    	 * If the device tree already provides a read delay value, use that
    	 * instead of calibrating.
    	 */
    	if (priv->read_delay >= 0) {
    		cadence_spi_write_speed(bus, hz);
    		cadence_qspi_apb_readdata_capture(priv->regbase, 1, true,
    						  priv->read_delay);
    	} else if (priv->previous_hz != hz ||
    		   priv->qspi_calibrated_hz != hz ||
    		   priv->qspi_calibrated_cs != spi_chip_select(bus)) {
    		/*
    		 * Calibration required for different current SCLK speed,
    		 * requested SCLK speed or chip select
    		 */
    		err = spi_calibration(bus, hz);
    		if (err)
    			return err;
    
    		/* prevent calibration run when same as previous request */
    		priv->previous_hz = hz;
    	}
    
    	/* Enable QSPI */
    	cadence_qspi_apb_controller_enable(priv->regbase);
    
    	debug("%s: speed=%d\n", __func__, hz);
    
    	return 0;
    }
    
    static int cadence_spi_probe(struct udevice *bus)
    {
    	struct cadence_spi_plat *plat = dev_get_plat(bus);
    	struct cadence_spi_priv *priv = dev_get_priv(bus);
    	struct clk clk;
    	int ret;
    
    	priv->regbase		= plat->regbase;
    	priv->ahbbase		= plat->ahbbase;
    	priv->is_dma		= plat->is_dma;
    	priv->is_decoded_cs	= plat->is_decoded_cs;
    	priv->fifo_depth	= plat->fifo_depth;
    	priv->fifo_width	= plat->fifo_width;
    	priv->trigger_address	= plat->trigger_address;
    	priv->read_delay	= plat->read_delay;
    	priv->phase_detect_selector = plat->phase_detect_selector;
    	priv->phy_pattern_start = plat->phy_pattern_start;
    	priv->phy_tx_start	= plat->phy_tx_start;
    	priv->phy_tx_end	= plat->phy_tx_end;
    	priv->ahbsize		= plat->ahbsize;
    	priv->max_hz		= plat->max_hz;
    
    	priv->page_size		= plat->page_size;
    	priv->block_size	= plat->block_size;
    	priv->tshsl_ns		= plat->tshsl_ns;
    	priv->tsd2d_ns		= plat->tsd2d_ns;
    	priv->tchsh_ns		= plat->tchsh_ns;
    	priv->tslch_ns		= plat->tslch_ns;
    
    	if (IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE))
    		xilinx_pm_request(PM_REQUEST_NODE, PM_DEV_OSPI,
    				  ZYNQMP_PM_CAPABILITY_ACCESS, ZYNQMP_PM_MAX_QOS,
    				  ZYNQMP_PM_REQUEST_ACK_NO, NULL);
    
    	if (priv->ref_clk_hz == 0) {
    		ret = clk_get_by_index(bus, 0, &clk);
    		if (ret) {
    #ifdef CONFIG_HAS_CQSPI_REF_CLK
    			priv->ref_clk_hz = CONFIG_CQSPI_REF_CLK;
    #elif defined(CONFIG_ARCH_SOCFPGA)
    			priv->ref_clk_hz = cm_get_qspi_controller_clk_hz();
    #else
    			return ret;
    #endif
    		} else {
    			priv->ref_clk_hz = clk_get_rate(&clk);
    			if (IS_ERR_VALUE(priv->ref_clk_hz))
    				return priv->ref_clk_hz;
    		}
    	}
    
    	priv->resets = devm_reset_bulk_get_optional(bus);
    	if (priv->resets)
    		reset_deassert_bulk(priv->resets);
    
    	if (!priv->qspi_is_init) {
    		cadence_qspi_apb_controller_init(priv);
    		priv->qspi_is_init = 1;
    	}
    
    	priv->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, priv->ref_clk_hz);
    
    	/* Reset ospi flash device */
    	return cadence_qspi_versal_flash_reset(bus);
    }
    
    static int cadence_spi_remove(struct udevice *dev)
    {
    	struct cadence_spi_priv *priv = dev_get_priv(dev);
    	int ret = 0;
    
    	if (priv->resets)
    		ret = reset_release_bulk(priv->resets);
    
    	return ret;
    }
    
    static int cadence_spi_set_mode(struct udevice *bus, uint mode)
    {
    	struct cadence_spi_priv *priv = dev_get_priv(bus);
    
    	/* Disable QSPI */
    	cadence_qspi_apb_controller_disable(priv->regbase);
    
    	/* Set SPI mode */
    	cadence_qspi_apb_set_clk_mode(priv->regbase, mode);
    
    	/* Enable Direct Access Controller */
    	if (priv->use_dac_mode)
    		cadence_qspi_apb_dac_mode_enable(priv->regbase);
    
    	/* Enable QSPI */
    	cadence_qspi_apb_controller_enable(priv->regbase);
    
    	return 0;
    }
    
    static int cadence_spi_mem_exec_op(struct spi_slave *spi,
    				   const struct spi_mem_op *op)
    {
    	struct udevice *bus = spi->dev->parent;
    	struct cadence_spi_priv *priv = dev_get_priv(bus);
    	void *base = priv->regbase;
    	int err = 0;
    	u32 mode;
    
    	/* Set Chip select */
    	cadence_qspi_apb_chipselect(base, spi_chip_select(spi->dev),
    				    priv->is_decoded_cs);
    
    	if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) {
    		/*
    		 * Performing reads in DAC mode forces to read minimum 4 bytes
    		 * which is unsupported on some flash devices during register
    		 * reads, prefer STIG mode for such small reads.
    		 */
    		if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX)
    			mode = CQSPI_STIG_READ;
    		else
    			mode = CQSPI_READ;
    	} else {
    		if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX)
    			mode = CQSPI_STIG_WRITE;
    		else
    			mode = CQSPI_WRITE;
    	}
    
    	switch (mode) {
    	case CQSPI_STIG_READ:
    		err = cadence_qspi_apb_command_read_setup(priv, op);
    		if (!err)
    			err = cadence_qspi_apb_command_read(priv, op);
    		break;
    	case CQSPI_STIG_WRITE:
    		err = cadence_qspi_apb_command_write_setup(priv, op);
    		if (!err)
    			err = cadence_qspi_apb_command_write(priv, op);
    		break;
    	case CQSPI_READ:
    		err = cadence_qspi_apb_read_setup(priv, op);
    		if (!err) {
    			if (priv->is_dma)
    				err = cadence_qspi_apb_dma_read(priv, op);
    			else
    				err = cadence_qspi_apb_read_execute(priv, op);
    		}
    		break;
    	case CQSPI_WRITE:
    		err = cadence_qspi_apb_write_setup(priv, op);
    		if (!err)
    			err = cadence_qspi_apb_write_execute(priv, op);
    		break;
    	default:
    		err = -1;
    		break;
    	}
    
    	return err;
    }
    
    static bool cadence_spi_mem_supports_op(struct spi_slave *slave,
    					const struct spi_mem_op *op)
    {
    	bool all_true, all_false;
    
    	/*
    	 * op->dummy.dtr is required for converting nbytes into ncycles.
    	 * Also, don't check the dtr field of the op phase having zero nbytes.
    	 */
    	all_true = op->cmd.dtr &&
    		   (!op->addr.nbytes || op->addr.dtr) &&
    		   (!op->dummy.nbytes || op->dummy.dtr) &&
    		   (!op->data.nbytes || op->data.dtr);
    
    	all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr &&
    		    !op->data.dtr;
    
    	/* Mixed DTR modes not supported. */
    	if (!(all_true || all_false))
    		return false;
    
    	if (all_true)
    		return spi_mem_dtr_supports_op(slave, op);
    	else
    		return spi_mem_default_supports_op(slave, op);
    }
    
    static void cadence_spi_mem_do_calibration(struct spi_slave *spi,
    					   struct spi_mem_op *op)
    {
    	struct udevice *bus = spi->dev->parent;
    	struct cadence_spi_priv *priv = dev_get_priv(bus);
    	int ret;
    
    	if (!IS_ENABLED(CONFIG_CADENCE_QSPI_PHY))
    		return;
    
    	priv->phy_read_op = *op;
    
    	if (priv->phase_detect_selector >
    	    CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_LEN) {
    		dev_warn(bus,
    			 "Phase Detect Selector should be in between [0, 7]. Skipping Calibration\n");
    		return;
    	}
    
    	ret = cadence_spi_phy_check_pattern(priv, spi);
    	if (ret) {
    		dev_warn(bus, "Pattern not found. Skipping calibration\n");
    		return;
    	}
    
    	if (cadence_qspi_apb_op_eligible(op)) {
    		priv->use_dqs = true;
    
    		cadence_qspi_apb_phy_pre_config(priv, 0, 1);
    
    		ret = cadence_spi_phy_calibrate(priv, spi);
    		if (ret)
    			dev_warn(bus,
    				 "PHY calibration failed: %d. Falling back to slower clock speeds.\n",
    				 ret);
    	} else if (cadence_qspi_apb_op_eligible_sdr(op)) {
    		priv->use_dqs = false;
    
    		cadence_qspi_apb_phy_pre_config(priv, 1, 0);
    
    		ret = cadence_spi_phy_calibrate_sdr(priv, spi);
    		if (ret)
    			dev_warn(bus,
    				 "PHY calibration failed: %d. Falling back to slower clock speeds.\n",
    				 ret);
    
    	} else {
    		dev_warn(bus,
    			 "Given read_op not eligible. Skipping Calibration.\n");
    		return;
    	}
    
    	cadence_qspi_apb_phy_post_config(priv, priv->read_delay);
    }
    
    static int cadence_spi_ofdata_phy_pattern(ofnode flash_node)
    {
    	ofnode subnode;
    	const char *label;
    	u32 start;
    
    	subnode = ofnode_find_subnode(flash_node, "partitions");
    	if (!ofnode_valid(subnode))
    		/*
    		 * Maybe the node has legacy style partitions, listed directly
    		 * under flash node.
    		 */
    		subnode = ofnode_first_subnode(flash_node);
    	else
    		subnode = ofnode_first_subnode(subnode);
    
    	while (ofnode_valid(subnode)) {
    		label = ofnode_read_string(subnode, "label");
    		if (label && strcmp(label, "ospi.phypattern") == 0) {
    			if (!ofnode_read_u32_array(subnode, "reg", &start, 1))
    				return start;
    			break;
    		} else if (label && strcmp(label, "ospi_nand.phypattern") == 0)
    			return 0;
    
    		subnode = ofnode_next_subnode(subnode);
    	}
    
    	return -ENOENT;
    }
    
    static int cadence_spi_of_to_plat(struct udevice *bus)
    {
    	struct cadence_spi_plat *plat = dev_get_plat(bus);
    	struct cadence_spi_priv *priv = dev_get_priv(bus);
    	ofnode subnode;
    	int ret;
    
    	plat->regbase = devfdt_get_addr_index_ptr(bus, 0);
    	plat->ahbbase = devfdt_get_addr_size_index_ptr(bus, 1, &plat->ahbsize);
    	plat->is_decoded_cs = dev_read_bool(bus, "cdns,is-decoded-cs");
    	plat->fifo_depth = dev_read_u32_default(bus, "cdns,fifo-depth", 128);
    	plat->fifo_width = dev_read_u32_default(bus, "cdns,fifo-width", 4);
    	plat->trigger_address = dev_read_u32_default(bus,
    						     "cdns,trigger-address",
    						     0);
    	plat->phase_detect_selector = dev_read_u32_default(bus,
    							   "cdns,phase-detect-selector",
    							   CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_LEN + 1);
    	/* Use DAC mode only when MMIO window is at least 8M wide */
    	if (plat->ahbsize >= SZ_8M)
    		priv->use_dac_mode = true;
    
    	plat->is_dma = dev_read_bool(bus, "cdns,is-dma");
    
    	/* All other parameters are embedded in the child node */
    	subnode = cadence_qspi_get_subnode(bus);
    	if (!ofnode_valid(subnode)) {
    		printf("Error: subnode with SPI flash config missing!\n");
    		return -ENODEV;
    	}
    
    	/* Use 500 KHz as a suitable default */
    	plat->max_hz = ofnode_read_u32_default(subnode, "spi-max-frequency",
    					       500000);
    
    	/* Read other parameters from DT */
    	plat->page_size = ofnode_read_u32_default(subnode, "page-size", 256);
    	plat->block_size = ofnode_read_u32_default(subnode, "block-size", 16);
    	plat->tshsl_ns = ofnode_read_u32_default(subnode, "cdns,tshsl-ns",
    						 200);
    	plat->tsd2d_ns = ofnode_read_u32_default(subnode, "cdns,tsd2d-ns",
    						 255);
    	plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20);
    	plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-ns", 20);
    	/*
    	 * Read delay should be an unsigned value but we use a signed integer
    	 * so that negative values can indicate that the device tree did not
    	 * specify any signed values and we need to perform the calibration
    	 * sequence to find it out.
    	 */
    	plat->read_delay = ofnode_read_s32_default(subnode, "cdns,read-delay",
    						   -1);
    
    	plat->phy_tx_start = ofnode_read_u32_default(subnode,
    						     "cdns,phy-tx-start",
    						     16);
    	plat->phy_tx_end = ofnode_read_u32_default(subnode,
    						   "cdns,phy-tx-end",
    						   48);
    
    	/* Find the PHY tuning pattern partition. */
    	ret = cadence_spi_ofdata_phy_pattern(subnode);
    	if (ret < 0)
    		dev_dbg(bus, "Unable to find PHY pattern partition\n");
    	else
    		plat->phy_pattern_start = ret;
    
    	debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n",
    	      __func__, plat->regbase, plat->ahbbase, plat->max_hz,
    	      plat->page_size);
    
    	return 0;
    }
    
    static const struct spi_controller_mem_ops cadence_spi_mem_ops = {
    	.exec_op = cadence_spi_mem_exec_op,
    	.supports_op = cadence_spi_mem_supports_op,
    	.do_calibration = cadence_spi_mem_do_calibration,
    };
    
    static const struct dm_spi_ops cadence_spi_ops = {
    	.set_speed	= cadence_spi_set_speed,
    	.set_mode	= cadence_spi_set_mode,
    	.mem_ops	= &cadence_spi_mem_ops,
    	/*
    	 * cs_info is not needed, since we require all chip selects to be
    	 * in the device tree explicitly
    	 */
    };
    
    static const struct udevice_id cadence_spi_ids[] = {
    	{ .compatible = "cdns,qspi-nor" },
    	{ .compatible = "ti,am654-ospi" },
    	{ }
    };
    
    U_BOOT_DRIVER(cadence_spi) = {
    	.name = "cadence_spi",
    	.id = UCLASS_SPI,
    	.of_match = cadence_spi_ids,
    	.ops = &cadence_spi_ops,
    	.of_to_plat = cadence_spi_of_to_plat,
    	.plat_auto	= sizeof(struct cadence_spi_plat),
    	.priv_auto	= sizeof(struct cadence_spi_priv),
    	.probe = cadence_spi_probe,
    	.remove = cadence_spi_remove,
    	.flags = DM_FLAG_OS_PREPARE,
    };
    

    3) YES I use TI's AM62P board and OOB binaries

    Thanks

  • Hi Ti 

    I have modify  uboot dts  k3-am62p5-r5-sk.dts 

    git diff 

    diff --git a/arch/arm/dts/k3-am62p5-r5-sk.dts b/arch/arm/dts/k3-am62p5-r5-sk.dts
    index ecd6cd02c88..94d6e9e0a21 100644
    --- a/arch/arm/dts/k3-am62p5-r5-sk.dts
    +++ b/arch/arm/dts/k3-am62p5-r5-sk.dts
    @@ -97,8 +97,41 @@
     };
    
     &ospi0 {
    -       reg = <0x00 0x0fc40000 0x00 0x100>,
    -             <0x00 0x60000000 0x00 0x08000000>;
    +
    +   flash@0{
    +      compatible = "jedec,spi-nor";
    +      reg = <0x0>;
    +      spi-tx-bus-width = <8>;
    +      spi-rx-bus-width = <8>;
    +      spi-max-frequency = <25000000>;
    +      cdns,tshsl-ns = <60>;
    +      cdns,tsd2d-ns = <60>;
    +      cdns,tchsh-ns = <60>;
    +      cdns,tslch-ns = <60>;
    +      cdns,read-delay = <4>;
    +
    +      partitions {
    +         compatible = "fixed-partitions";
    +         #address-cells = <1>;
    +         #size-cells = <1>;
    +         bootph-all;
    +
    +         partition@0 {
    +            label = "tiboot3.bin";
    +            reg = <0x00 0x80000>;
    +         };
    +
    +         partition@80000 {
    +            label = "tispl.bin";
    +            reg = <0x80000 0x200000>;
    +         };
    +
    +         partition@0x280000 {
    +            label = "u-boot.img";
    +            reg = <0x0x280000 0x680000>;
    +         };
    +      };
    +   };
     };
    
     &memorycontroller {

    Then I use boot EMMC , it boot is ok , I copy tiboot3.bin  tispl.bin  u-boot.img  file into EMMC 

    am62pxx-evm login: root
    root@am62pxx-evm:~# ls
    root@am62pxx-evm:~# cd /
    root@am62pxx-evm:/# ls
    bin          dev          home         lost+found   mnt          root         sbin         sys          tispl.bin    u-boot.img   var
    boot         etc          lib          media        proc         run          srv          tiboot3.bin  tmp          usr
    root@am62pxx-evm:/#

    Then reboot , stop at uboot

    U-Boot SPL 2025.01-00535-g827c35b4d141-dirty (Jul 03 2025 - 17:34:00 +0000)
    SYSFW ABI: 4.0 (firmware rev 0x000b '11.1.2--v11.01.02 (Fancy Rat)')
    SPL initial stack usage: 17088 bytes
    Trying to boot from MMC1
    Authentication passed
    Authentication passed
    Authentication passed
    Authentication passed
    Authentication passed
    Starting ATF on ARM64 core...
    
    NOTICE:  BL31: v2.13.0(release):v2.13.0-259-ge0c4d3903b-dirty
    NOTICE:  BL31: Built : 07:01:36, Jul  1 2025
    
    U-Boot SPL 2025.01-00535-g827c35b4d141-dirty (Jul 03 2025 - 17:34:00 +0000)
    SYSFW ABI: 4.0 (firmware rev 0x000b '11.1.2--v11.01.02 (Fancy Rat)')
    DM ABI: 3.0 (firmware ver 0x000b 'MSDK.11.01.00.05-dirty--v11.01.02' patch_ver: 2)
    SPL initial stack usage: 1984 bytes
    MMC: no card present
    ** Bad device specification mmc 1 **
    Couldn't find partition mmc 1:1
    Error: could not access storage.
    Trying to boot from MMC1
    Authentication passed
    Authentication passed
    
    
    U-Boot 2025.01-00535-g827c35b4d141-dirty (Jul 03 2025 - 17:34:00 +0000)
    
    SoC:   AM62PX SR1.0 HS-FS
    Model: Texas Instruments AM62P5 SK
    DRAM:  2 GiB (total 8 GiB)
    Core:  97 devices, 32 uclasses, devicetree: separate
    MMC:   mmc@fa10000: 0, mmc@fa00000: 1
    Loading Environment from nowhere... OK
    In:    serial
    Out:   serial
    Err:   serial
    Net:   eth0: ethernet@8000000port@1
    Warning: ethernet@8000000port@2 (eth1) using random MAC address - 26:63:21:21:71:2b
    , eth1: ethernet@8000000port@2
    
    Hit any key to stop autoboot:  0
    =>
    =>
    =>
    => ls mmc 0
                ./
                ../
                lost+found/
        <SYM>   bin
                boot/
                dev/
                etc/
                home/
        <SYM>   lib
                media/
                mnt/
                proc/
                root/
                run/
        <SYM>   sbin
                srv/
                sys/
                tmp/
                usr/
                var/
       314237   tiboot3.bin
      1443391   tispl.bin
      1468279   u-boot.img
    
    6 file(s), 17 dir(s)
    
    =>

    Then I flash tiboot3.bin  tispl.bin  u-boot.img into QSPI

    => sf probe
    SF: Detected s28hs512t with page size 256 Bytes, erase size 256 KiB, total 64 MiB
    => ext4load mmc 0 ${loadaddr} tiboot3.bin
    314237 bytes read in 3 ms (99.9 MiB/s)
    => sf update $loadaddr 0x0 $filesize
    device 0 offset 0x0, size 0x4cb7d
    0 bytes written, 314237 bytes skipped in 0.4s, speed 45968384 B/s
    => ext4load mmc 0 ${loadaddr} tispl.bin
    1443391 bytes read in 9 ms (152.9 MiB/s)
    => sf update $loadaddr 0x80000 $filesize
    device 0 offset 0x80000, size 0x16063f
    919103 bytes written, 524288 bytes skipped in 5.291s, speed 279137 B/s
    => ext4load mmc 0 ${loadaddr} u-boot.img
    1468279 bytes read in 9 ms (155.6 MiB/s)
    => sf update $loadaddr 0x280000 $filesize
    device 0 offset 0x280000, size 0x166777
    419703 bytes written, 1048576 bytes skipped in 2.579s, speed 582307 B/s
    => <INTERRUPT>
    =>
    =>
    

    Then I change boot mode QSPI boot

    the AM62P SKEVM can not OSPI BOOT

    There is the boot log

    U-Boot SPL 2025.01-00535-g827c35b4d141-dirty (Jul 03 2025 - 17:34:00 +0000)
    SYSFW ABI: 4.0 (firmware rev 0x000b '11.1.2--v11.01.02 (Fancy Rat)')
    SPL initial stack usage: 17088 bytes
    Trying to boot from SPI
    QSPI: QSPI is still busy after poll for 5000 ms.

    Thanks

  • Thank you, please give me some time to reproduce it. 

    Paula

  • Hi Sui, checking your last post please help me to understand. Why did you add "&opsi0" in "arch\arm\dts\k3-am62p5-r5-sk.dts"? This is already defined in 

    "arch\arm64\boot\dts\ti\k3-am62p5-sk.dts" and some sizes and properties are different between both files (e.g. u-boot, phy-mode)

    https://git.ti.com/cgit/ti-linux-kernel/ti-linux-kernel/tree/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts?h=ti-linux-6.12.y#n713

    Also, just to understand, without changes in "k3-am62p5-r5-sk.dts", and using DFU, does board boots OK?

    Thank you,

    Paula

  • Dear 

    I add &ospi0  in UBOOT   dts  k3-am62p5-r5-sk.dts       , it is not in linux kernel  

    But  I also can remove &ospi0  it , I don't know why I flash tiboot3.bin  tispl.bin  u-boot.img , OSPI boot is failed ?

    Thanks

  • Sui, could you please share with me your linux kernel dts (k3-am62p5-sk.dts)? and maybe also k3-am62p5-r5-sk.dts (you shared already changes there so optional)

    Also, let me confirm something, to clarify, without changes in "k3-am62p5-r5-sk.dts", and using DFU, does board boots OK?. I want to understand if there is any scenario in which your board boots OK after flashing with DFU

    thank you,

    Paula

  • Hi  

    There is my linux kernel dts k3-am62p5-sk.dts

    // SPDX-License-Identifier: GPL-2.0-only OR MIT
    /*
     * Device Tree file for the AM62P5-SK
     * Copyright (C) 2023-2024 Texas Instruments Incorporated - https://www.ti.com/
     *
     * Schematics: https://www.ti.com/lit/zip/sprr487
     */
    
    /dts-v1/;
    
    #include <dt-bindings/leds/common.h>
    #include <dt-bindings/gpio/gpio.h>
    #include <dt-bindings/net/ti-dp83867.h>
    #include "k3-am62p5.dtsi"
    
    / {
    	compatible = "ti,am62p5-sk", "ti,am62p5";
    	model = "Texas Instruments AM62P5 SK";
    
    	aliases {
    		serial0 = &wkup_uart0;
    		serial1 = &mcu_uart0;
    		serial2 = &main_uart0;
    		serial3 = &main_uart1;
    		mmc0 = &sdhci0;
    		mmc1 = &sdhci1;
    		mmc2 = &sdhci2;
    		spi0 = &ospi0;
    		ethernet0 = &cpsw_port1;
    		ethernet1 = &cpsw_port2;
    		usb0 = &usb0;
    		usb1 = &usb1;
    	};
    
    	chosen {
    		#address-cells = <2>;
    		#size-cells = <2>;
    		ranges;
                    bootargs = "console=ttyS2,115200n8 quiet earlycon=ns16550a,mmio32,0x02800000 root=/dev/mmcblk0p1 rw rootfstype=ext4 rootwait";
    		stdout-path = &main_uart0;
    
    		framebuffer0: framebuffer@0 {
    			compatible = "simple-framebuffer";
    			power-domains = <&k3_pds 186 TI_SCI_PD_EXCLUSIVE>;
    			clocks = <&k3_clks 186 6>,
    				 <&dss0_vp1_clk>,
    				 <&k3_clks 186 2>;
    			display = <&dss0>;
    			status = "disabled";
    		};
    	};
    
    	memory@80000000 {
    		/* 8G RAM */
    		reg = <0x00000000 0x80000000 0x00000000 0x80000000>,
    		      <0x00000008 0x80000000 0x00000001 0x80000000>;
    		device_type = "memory";
    		bootph-pre-ram;
    	};
    
    	reserved_memory: reserved-memory {
    		#address-cells = <2>;
    		#size-cells = <2>;
    		ranges;
    
    		linux,cma {
    			compatible = "shared-dma-pool";
    			reusable;
    			size = <0x00 0x24000000>;
    			linux,cma-default;
    		};
    
    		rtos_ipc_memory_region: rtos-ipc-memory@9b500000 {
    			compatible = "shared-dma-pool";
    			reg = <0x00 0x9b500000 0x00 0x00300000>;
    			no-map;
    		};
    
    		mcu_r5fss0_core0_dma_memory_region: mcu-r5fss-dma-memory-region@9b800000 {
    			compatible = "shared-dma-pool";
    			reg = <0x00 0x9b800000 0x00 0x100000>;
    			no-map;
    		};
    
    		mcu_r5fss0_core0_memory_region: mcu-r5fss-memory-region@9b900000 {
    			compatible = "shared-dma-pool";
    			reg = <0x00 0x9b900000 0x00 0xf00000>;
    			no-map;
    		};
    
    		wkup_r5fss0_core0_dma_memory_region: r5f-dma-memory@9c800000 {
    			compatible = "shared-dma-pool";
    			reg = <0x00 0x9c800000 0x00 0x100000>;
    			no-map;
    		};
    
    		wkup_r5fss0_core0_memory_region: r5f-memory@9c900000 {
    			compatible = "shared-dma-pool";
    			reg = <0x00 0x9c900000 0x00 0x1e00000>;
    			no-map;
    		};
    
    		secure_tfa_ddr: tfa@9e780000 {
    			reg = <0x00 0x9e780000 0x00 0x80000>;
    			no-map;
    		};
    
    		secure_ddr: optee@9e800000 {
    			reg = <0x00 0x9e800000 0x00 0x01800000>; /* for OP-TEE */
    			no-map;
    		};
    	};
    
    	vmain_pd: regulator-0 {
    		/* TPS65988 PD CONTROLLER OUTPUT */
    		compatible = "regulator-fixed";
    		regulator-name = "vmain_pd";
    		regulator-min-microvolt = <5000000>;
    		regulator-max-microvolt = <5000000>;
    		regulator-always-on;
    		regulator-boot-on;
    		bootph-all;
    	};
    
    	vcc_5v0: regulator-1 {
    		/* Output of TPS630702RNMR */
    		compatible = "regulator-fixed";
    		regulator-name = "vcc_5v0";
    		regulator-min-microvolt = <5000000>;
    		regulator-max-microvolt = <5000000>;
    		vin-supply = <&vmain_pd>;
    		regulator-always-on;
    		regulator-boot-on;
    		bootph-all;
    	};
    
    	vdd_mmc1: regulator-2 {
    		/* TPS22918DBVR */
    		compatible = "regulator-fixed";
    		regulator-name = "vdd_mmc1";
    		regulator-min-microvolt = <3300000>;
    		regulator-max-microvolt = <3300000>;
    		regulator-boot-on;
    		enable-active-high;
    		gpio = <&exp1 3 GPIO_ACTIVE_HIGH>;
    		bootph-all;
    	};
    
    	vddshv_sdio: regulator-3 {
    		compatible = "regulator-gpio";
    		regulator-name = "vddshv_sdio";
    		pinctrl-names = "default";
    		pinctrl-0 = <&vddshv_sdio_pins_default>;
    		regulator-min-microvolt = <1800000>;
    		regulator-max-microvolt = <3300000>;
    		regulator-boot-on;
    		gpios = <&main_gpio0 31 GPIO_ACTIVE_HIGH>;
    		states = <1800000 0x0>,
    			 <3300000 0x1>;
    		bootph-all;
    	};
    
    	leds {
    		compatible = "gpio-leds";
    		pinctrl-names = "default";
    		pinctrl-0 = <&usr_led_pins_default>;
    
    		led-0 {
    			label = "am62-sk:green:heartbeat";
    			gpios = <&main_gpio1 49 GPIO_ACTIVE_HIGH>;
    			linux,default-trigger = "heartbeat";
    			function = LED_FUNCTION_HEARTBEAT;
    			default-state = "off";
    		};
    	};
    
    	opp-table {
    		/* Requires VDD_CORE at 0v85 */
    		opp-1400000000 {
    			opp-hz = /bits/ 64 <1400000000>;
    			opp-supported-hw = <0x01 0x0004>;
    			clock-latency-ns = <6000000>;
    		};
    	};
    
    	tlv320_mclk: clk-0 {
    		#clock-cells = <0>;
    		compatible = "fixed-clock";
    		clock-frequency = <12288000>;
    	};
    
    	codec_audio: sound {
    		compatible = "simple-audio-card";
    		simple-audio-card,name = "AM62x-SKEVM";
    		simple-audio-card,widgets =
    			"Headphone",	"Headphone Jack",
    			"Line",		"Line In",
    			"Microphone",	"Microphone Jack";
    		simple-audio-card,routing =
    			"Headphone Jack",	"HPLOUT",
    			"Headphone Jack",	"HPROUT",
    			"LINE1L",		"Line In",
    			"LINE1R",		"Line In",
    			"MIC3R",		"Microphone Jack",
    			"Microphone Jack",	"Mic Bias";
    		simple-audio-card,format = "dsp_b";
    		simple-audio-card,bitclock-master = <&sound_master>;
    		simple-audio-card,frame-master = <&sound_master>;
    		simple-audio-card,bitclock-inversion;
    
    		simple-audio-card,cpu {
    			sound-dai = <&mcasp1>;
    		};
    
    		sound_master: simple-audio-card,codec {
    			sound-dai = <&tlv320aic3106>;
    			clocks = <&tlv320_mclk>;
    		};
    	};
    
    	hdmi0: connector-hdmi {
    		compatible = "hdmi-connector";
    		label = "hdmi";
    		type = "a";
    		port {
    			hdmi_connector_in: endpoint {
    				remote-endpoint = <&sii9022_out>;
    			};
    		};
    	};
    };
    
    &main_gpio0 {
    	bootph-all;
    };
    
    &main_gpio1 {
    	bootph-all;
    };
    
    &main_pmx0 {
    	bootph-all;
    
    	main_i2c0_pins_default: main-i2c0-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x01e0, PIN_INPUT_PULLUP, 0) /* (B25) I2C0_SCL */
    			AM62PX_IOPAD(0x01e4, PIN_INPUT_PULLUP, 0) /* (A24) I2C0_SDA */
    		>;
    	};
    
    	main_i2c1_pins_default: main-i2c1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x01e8, PIN_INPUT_PULLUP, 0) /* (C24) I2C1_SCL */
    			AM62PX_IOPAD(0x01ec, PIN_INPUT_PULLUP, 0) /* (B24) I2C1_SDA */
    		>;
    		bootph-all;
    	};
    
    	main_i2c2_pins_default: main-i2c2-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x00b0, PIN_INPUT_PULLUP, 1) /* (T22) GPMC0_CSn2.I2C2_SCL */
    			AM62PX_IOPAD(0x00b4, PIN_INPUT_PULLUP, 1) /* (U25) GPMC0_CSn3.I2C2_SDA */
    		>;
    	};
    
    	main_gpio1_ioexp_intr_pins_default: main-gpio1-ioexp-intr-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x01d4, PIN_INPUT, 7) /* (C22) UART0_RTSn.GPIO1_23 */
    		>;
    	};
    
    	main_mcasp1_pins_default: main-mcasp1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0090, PIN_INPUT, 2) /* (U24) GPMC0_BE0n_CLE.MCASP1_ACLKX */
    			AM62PX_IOPAD(0x0098, PIN_INPUT, 2) /* (AA24) GPMC0_WAIT0.MCASP1_AFSX */
    			AM62PX_IOPAD(0x008c, PIN_OUTPUT, 2) /* (T25) GPMC0_WEn.MCASP1_AXR0 */
    			AM62PX_IOPAD(0x0084, PIN_INPUT, 2) /* (R25) GPMC0_ADVn_ALE.MCASP1_AXR2 */
    		>;
    	};
    
    	main_mdio1_pins_default: main-mdio1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0160, PIN_OUTPUT, 0) /* (F17) MDIO0_MDC */
    			AM62PX_IOPAD(0x015c, PIN_INPUT, 0) /* (F16) MDIO0_MDIO */
    		>;
    		bootph-all;
    	};
    
    	main_mmc1_pins_default: main-mmc1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x023c, PIN_INPUT, 0) /* (H20) MMC1_CMD */
    			AM62PX_IOPAD(0x0234, PIN_OUTPUT, 0) /* (J24) MMC1_CLK */
    			AM62PX_IOPAD(0x0230, PIN_INPUT, 0) /* (H21) MMC1_DAT0 */
    			AM62PX_IOPAD(0x022c, PIN_INPUT_PULLUP, 0) /* (H23) MMC1_DAT1 */
    			AM62PX_IOPAD(0x0228, PIN_INPUT_PULLUP, 0) /* (H22) MMC1_DAT2 */
    			AM62PX_IOPAD(0x0224, PIN_INPUT_PULLUP, 0) /* (H25) MMC1_DAT3 */
    			AM62PX_IOPAD(0x0240, PIN_INPUT, 0) /* (D23) MMC1_SDCD */
    		>;
    		bootph-all;
    	};
    
    	main_mmc2_pins_default: main-mmc2-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0120, PIN_INPUT, 0) /* (K24) MMC2_CMD */
    			AM62PX_IOPAD(0x0118, PIN_OUTPUT, 0) /* (K21) MMC2_CLK */
    			AM62PX_IOPAD(0x011C, PIN_INPUT, 0) /* () MMC2_CLKLB */
    			AM62PX_IOPAD(0x0114, PIN_INPUT, 0) /* (K23) MMC2_DAT0 */
    			AM62PX_IOPAD(0x0110, PIN_INPUT_PULLUP, 0) /* (K22) MMC2_DAT1 */
    			AM62PX_IOPAD(0x010c, PIN_INPUT_PULLUP, 0) /* (L20) MMC2_DAT2 */
    			AM62PX_IOPAD(0x0108, PIN_INPUT_PULLUP, 0) /* (L21) MMC2_DAT3 */
    		>;
    		bootph-all;
    	};
    
    	main_rgmii1_pins_default: main-rgmii1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x014c, PIN_INPUT, 0) /* (B15) RGMII1_RD0 */
    			AM62PX_IOPAD(0x0150, PIN_INPUT, 0) /* (B16) RGMII1_RD1 */
    			AM62PX_IOPAD(0x0154, PIN_INPUT, 0) /* (A14) RGMII1_RD2 */
    			AM62PX_IOPAD(0x0158, PIN_INPUT, 0) /* (B14) RGMII1_RD3 */
    			AM62PX_IOPAD(0x0148, PIN_INPUT, 0) /* (A16) RGMII1_RXC */
    			AM62PX_IOPAD(0x0144, PIN_INPUT, 0) /* (A15) RGMII1_RX_CTL */
    			AM62PX_IOPAD(0x0134, PIN_INPUT, 0) /* (A18) RGMII1_TD0 */
    			AM62PX_IOPAD(0x0138, PIN_INPUT, 0) /* (C17) RGMII1_TD1 */
    			AM62PX_IOPAD(0x013c, PIN_INPUT, 0) /* (A17) RGMII1_TD2 */
    			AM62PX_IOPAD(0x0140, PIN_INPUT, 0) /* (C16) RGMII1_TD3 */
    			AM62PX_IOPAD(0x0130, PIN_INPUT, 0) /* (B17) RGMII1_TXC */
    			AM62PX_IOPAD(0x012c, PIN_INPUT, 0) /* (B18) RGMII1_TX_CTL */
    		>;
    		bootph-all;
    	};
    
    	main_rgmii2_pins_default: main-rgmii2-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0184, PIN_INPUT, 0) /* (E19) RGMII2_RD0 */
    			AM62PX_IOPAD(0x0188, PIN_INPUT, 0) /* (E16) RGMII2_RD1 */
    			AM62PX_IOPAD(0x018c, PIN_INPUT, 0) /* (E17) RGMII2_RD2 */
    			AM62PX_IOPAD(0x0190, PIN_INPUT, 0) /* (C19) RGMII2_RD3 */
    			AM62PX_IOPAD(0x0180, PIN_INPUT, 0) /* (D19) RGMII2_RXC */
    			AM62PX_IOPAD(0x017c, PIN_INPUT, 0) /* (F19) RGMII2_RX_CTL */
    			AM62PX_IOPAD(0x016c, PIN_INPUT, 0) /* (B19) RGMII2_TD0 */
    			AM62PX_IOPAD(0x0170, PIN_INPUT, 0) /* (A21) RGMII2_TD1 */
    			AM62PX_IOPAD(0x0174, PIN_INPUT, 0) /* (D17) RGMII2_TD2 */
    			AM62PX_IOPAD(0x0178, PIN_INPUT, 0) /* (A19) RGMII2_TD3 */
    			AM62PX_IOPAD(0x0168, PIN_INPUT, 0) /* (D16) RGMII2_TXC */
    			AM62PX_IOPAD(0x0164, PIN_INPUT, 0) /* (A20) RGMII2_TX_CTL */
    		>;
    		bootph-all;
    	};
    
    	main_uart0_pins_default: main-uart0-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x1c8, PIN_INPUT, 0)	/* (A22) UART0_RXD */
    			AM62PX_IOPAD(0x1cc, PIN_OUTPUT, 0)	/* (B22) UART0_TXD */
    		>;
    		bootph-all;
    	};
    
    	main_uart1_pins_default: main-uart1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0194, PIN_INPUT, 2) /* (D25) MCASP0_AXR3.UART1_CTSn */
    			AM62PX_IOPAD(0x0198, PIN_OUTPUT, 2) /* (E25) MCASP0_AXR2.UART1_RTSn */
    			AM62PX_IOPAD(0x01ac, PIN_INPUT, 2) /* (G23) MCASP0_AFSR.UART1_RXD */
    			AM62PX_IOPAD(0x01b0, PIN_OUTPUT, 2) /* (G20) MCASP0_ACLKR.UART1_TXD */
    		>;
    		bootph-all;
    	};
    
    	main_usb1_pins_default: main-usb1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0258, PIN_INPUT | PIN_DS_PULLUD_ENABLE | PIN_DS_PULL_UP, 0) /* (G21) USB1_DRVVBUS */
    		>;
    	};
    
    	main_wlirq_pins_default: main-wlirq-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0128, PIN_INPUT, 7) /* (K25) MMC2_SDWP.GPIO0_72 */
    		>;
    	};
    
    	ospi0_pins_default: ospi0-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0000, PIN_OUTPUT, 0) /* (P23) OSPI0_CLK */
    			AM62PX_IOPAD(0x002c, PIN_OUTPUT, 0) /* (M25) OSPI0_CSn0 */
    			AM62PX_IOPAD(0x000c, PIN_INPUT, 0) /* (L25) OSPI0_D0 */
    			AM62PX_IOPAD(0x0010, PIN_INPUT, 0) /* (N24) OSPI0_D1 */
    			AM62PX_IOPAD(0x0014, PIN_INPUT, 0) /* (N25) OSPI0_D2 */
    			AM62PX_IOPAD(0x0018, PIN_INPUT, 0) /* (M24) OSPI0_D3 */
    			AM62PX_IOPAD(0x001c, PIN_INPUT, 0) /* (N21) OSPI0_D4 */
    			AM62PX_IOPAD(0x0020, PIN_INPUT, 0) /* (N22) OSPI0_D5 */
    			AM62PX_IOPAD(0x0024, PIN_INPUT, 0) /* (P21) OSPI0_D6 */
    			AM62PX_IOPAD(0x0028, PIN_INPUT, 0) /* (N20) OSPI0_D7 */
    			AM62PX_IOPAD(0x0008, PIN_INPUT, 0) /* (P22) OSPI0_DQS */
    		>;
    		bootph-all;
    	};
    
    	usr_led_pins_default: usr-led-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0244, PIN_INPUT, 7) /* (D24) MMC1_SDWP.GPIO1_49 */
    		>;
    	};
    
    	vddshv_sdio_pins_default: vddshvr-sdio-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x007c, PIN_INPUT, 7) /* (Y25) GPMC0_CLK.GPIO0_31 */
    		>;
    		bootph-all;
    	};
    
    	wlan_en_pins_default: wlan-en-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0124, PIN_INPUT, 7) /* (J25) MMC2_SDCD.GPIO0_71 */
    		>;
    	};
    
    	main_dpi_pins_default: main-dpi-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x0100, PIN_OUTPUT, 0) /* (W20) VOUT0_VSYNC */
    			AM62PX_IOPAD(0x00f8, PIN_OUTPUT, 0) /* (AC20) VOUT0_HSYNC */
    			AM62PX_IOPAD(0x0104, PIN_OUTPUT, 0) /* (Y21) VOUT0_PCLK */
    			AM62PX_IOPAD(0x00fc, PIN_OUTPUT, 0) /* (W21) VOUT0_DE */
    			AM62PX_IOPAD(0x00b8, PIN_OUTPUT, 0) /* (AE24) VOUT0_DATA0 */
    			AM62PX_IOPAD(0x00bc, PIN_OUTPUT, 0) /* (W23) VOUT0_DATA1 */
    			AM62PX_IOPAD(0x00c0, PIN_OUTPUT, 0) /* (AA23) VOUT0_DATA2 */
    			AM62PX_IOPAD(0x00c4, PIN_OUTPUT, 0) /* (Y23) VOUT0_DATA3 */
    			AM62PX_IOPAD(0x00c8, PIN_OUTPUT, 0) /* (AB23) VOUT0_DATA4 */
    			AM62PX_IOPAD(0x00cc, PIN_OUTPUT, 0) /* (AD23) VOUT0_DATA5 */
    			AM62PX_IOPAD(0x00d0, PIN_OUTPUT, 0) /* (AC23) VOUT0_DATA6 */
    			AM62PX_IOPAD(0x00d4, PIN_OUTPUT, 0) /* (AE23) VOUT0_DATA7 */
    			AM62PX_IOPAD(0x00d8, PIN_OUTPUT, 0) /* (AE22) VOUT0_DATA8 */
    			AM62PX_IOPAD(0x00dc, PIN_OUTPUT, 0) /* (AC22) VOUT0_DATA9 */
    			AM62PX_IOPAD(0x00e0, PIN_OUTPUT, 0) /* (W22) VOUT0_DATA10 */
    			AM62PX_IOPAD(0x00e4, PIN_OUTPUT, 0) /* (AE21) VOUT0_DATA11 */
    			AM62PX_IOPAD(0x00e8, PIN_OUTPUT, 0) /* (AD21) VOUT0_DATA12 */
    			AM62PX_IOPAD(0x00ec, PIN_OUTPUT, 0) /* (AC21) VOUT0_DATA13 */
    			AM62PX_IOPAD(0x00f0, PIN_OUTPUT, 0) /* (AA20) VOUT0_DATA14 */
    			AM62PX_IOPAD(0x00f4, PIN_OUTPUT, 0) /* (Y20) VOUT0_DATA15 */
    			AM62PX_IOPAD(0x005c, PIN_OUTPUT, 1) /* (AC25) GPMC0_AD8.VOUT0_DATA16 */
    			AM62PX_IOPAD(0x0060, PIN_OUTPUT, 1) /* (AB25) GPMC0_AD9.VOUT0_DATA17 */
    			AM62PX_IOPAD(0x0064, PIN_OUTPUT, 1) /* (AA25) GPMC0_AD10.VOUT0_DATA18 */
    			AM62PX_IOPAD(0x0068, PIN_OUTPUT, 1) /* (W24) GPMC0_AD11.VOUT0_DATA19 */
    			AM62PX_IOPAD(0x006c, PIN_OUTPUT, 1) /* (Y24) GPMC0_AD12.VOUT0_DATA20 */
    			AM62PX_IOPAD(0x0070, PIN_OUTPUT, 1) /* (AD25) GPMC0_AD13.VOUT0_DATA21 */
    			AM62PX_IOPAD(0x0074, PIN_OUTPUT, 1) /* (AB24) GPMC0_AD14.VOUT0_DATA22 */
    			AM62PX_IOPAD(0x0078, PIN_OUTPUT, 1) /* (AC24) GPMC0_AD15.VOUT0_DATA23 */
    			AM62PX_IOPAD(0x009c, PIN_OUTPUT, 1) /* (AD24) GPMC0_WAIT1.VOUT0_EXTPCLKIN */
    		>;
    	};
    
    	main_epwm0_pins_default: main_epwm0-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x01b4, PIN_OUTPUT, 2) /* (D20) SPI0_CS0.EHRPWM0_A */
    			AM62PX_IOPAD(0x01b8, PIN_OUTPUT, 2) /* (E20) SPI0_CS1.EHRPWM0_B */
    		>;
    	};
    
    	main_epwm1_pins_default: main_epwm1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x01bc, PIN_OUTPUT, 2) /* (B21) SPI0_CLK.EHRPWM1_A */
    			AM62PX_IOPAD(0x01c0, PIN_OUTPUT, 2) /* (B20) SPI0_D0.EHRPWM1_B */
    		>;
    	};
    
    	main_ecap1_pins_default: main_ecap1-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x019c, PIN_OUTPUT, 2) /* (E24) MCASP0_AXR1.ECAP1_IN_APWM_OUT */
    		>;
    	};
    
    	main_ecap2_pins_default: main-ecap2-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_IOPAD(0x01a4, PIN_OUTPUT, 2) /* (F24) MCASP0_ACLKX.ECAP2_IN_APWM_OUT */
    		>;
    	};
    };
    
    &main_i2c0 {
    	status = "okay";
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_i2c0_pins_default>;
    	clock-frequency = <400000>;
    
    	typec_pd0: usb-power-controller@3f {
    		compatible = "ti,tps6598x";
    		reg = <0x3f>;
    
    		connector {
    			compatible = "usb-c-connector";
    			label = "USB-C";
    			self-powered;
    			data-role = "dual";
    			power-role = "sink";
    			port {
    				usb_con_hs: endpoint {
    				remote-endpoint = <&usb0_hs_ep>;
    				};
    			};
    		};
    	};
    };
    
    &main_i2c1 {
    	status = "okay";
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_i2c1_pins_default>;
    	clock-frequency = <100000>;
    	bootph-all;
    
    	tlv320aic3106: audio-codec@1b {
    		#sound-dai-cells = <0>;
    		compatible = "ti,tlv320aic3106";
    		reg = <0x1b>;
    		ai3x-micbias-vg = <1>;  /* 2.0V */
    	};
    
    	exp1: gpio@22 {
    		compatible = "ti,tca6424";
    		reg = <0x22>;
    		gpio-controller;
    		#gpio-cells = <2>;
    		gpio-line-names = "OLDI_INT#", "x8_NAND_DETECT",
    				   "UART1_FET_SEL", "MMC1_SD_EN",
    				   "VPP_EN", "EXP_PS_3V3_EN",
    				   "UART1_FET_BUF_EN", "EXP_HAT_DETECT",
    				   "DSI_GPIO0", "DSI_GPIO1",
    				   "OLDI_EDID", "BT_UART_WAKE_SOC_3V3",
    				   "USB_TYPEA_OC_INDICATION", "CSI_GPIO0",
    				   "CSI_GPIO1", "WLAN_ALERTn",
    				   "HDMI_INTn", "TEST_GPIO2",
    				   "MCASP1_FET_EN", "MCASP1_BUF_BT_EN",
    				   "MCASP1_FET_SEL", "DSI_EDID",
    				   "PD_I2C_IRQ", "IO_EXP_TEST_LED";
    
    		interrupt-parent = <&main_gpio1>;
    		interrupts = <23 IRQ_TYPE_EDGE_FALLING>;
    		interrupt-controller;
    		#interrupt-cells = <2>;
    
    		pinctrl-names = "default";
    		pinctrl-0 = <&main_gpio1_ioexp_intr_pins_default>;
    		bootph-all;
    	};
    
    	exp2: gpio@23 {
    		compatible = "ti,tca6424";
    		reg = <0x23>;
    		gpio-controller;
    		#gpio-cells = <2>;
    		gpio-line-names = "BT_EN_SOC", "EXP_PS_5V0_EN",
    				   "", "",
    				   "", "",
    				   "", "",
    				   "WL_LT_EN", "",
    				   "TP3", "TP6",
    				   "TP4", "TP7",
    				   "TP5", "TP8",
    				   "SoC_I2C2_MCAN_SEL", "GPIO_HDMI_RSTn",
    				   "GPIO_CPSW2_RST", "GPIO_CPSW1_RST",
    				   "GPIO_OLDI_RSTn", "GPIO_AUD_RSTn",
    				   "GPIO_eMMC_RSTn", "SoC_WLAN_SDIO_RST";
    	};
    
    	sii9022: bridge-hdmi@3b {
    		compatible = "sil,sii9022";
    		reg = <0x3b>;
    		interrupt-parent = <&exp1>;
    		interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
    		#sound-dai-cells = <0>;
    		sil,i2s-data-lanes = < 0 >;
    
    		hdmi_tx_ports: ports {
    			#address-cells = <1>;
    			#size-cells = <0>;
    
    			/*
    			 * HDMI can be serviced with 3 potential VPs -
    			 * (DSS0 VP1 / DSS1 VP0 / DSS1 VP1).
    			 * For now, we will service it with DSS0 VP1.
    			 */
    			port@0 {
    				reg = <0>;
    
    				sii9022_in: endpoint {
    					remote-endpoint = <&dss0_dpi1_out>;
    				};
    			};
    
    			port@1 {
    				reg = <1>;
    
    				sii9022_out: endpoint {
    					remote-endpoint = <&hdmi_connector_in>;
    				};
    			};
    		};
    	};
    };
    
    &main_i2c2 {
    	status = "okay";
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_i2c2_pins_default>;
    	clock-frequency = <400000>;
    };
    
    &sdhci0 {
    	status = "okay";
    	ti,driver-strength-ohm = <50>;
    	disable-wp;
    	bootph-all;
    };
    
    &sdhci1 {
    	/* SD/MMC */
    	status = "okay";
    	vmmc-supply = <&vdd_mmc1>;
    	vqmmc-supply = <&vddshv_sdio>;
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_mmc1_pins_default>;
    	disable-wp;
    	bootph-all;
    };
    
    &cpsw3g {
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_rgmii1_pins_default>,
    		    <&main_rgmii2_pins_default>;
    	status = "okay";
    };
    
    &cpsw_port1 {
    	phy-mode = "rgmii-rxid";
    	phy-handle = <&cpsw3g_phy0>;
    	status = "okay";
    };
    
    &cpsw_port2 {
    	phy-mode = "rgmii-rxid";
    	phy-handle = <&cpsw3g_phy1>;
    	status = "okay";
    };
    
    &cpsw3g_mdio {
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_mdio1_pins_default>;
    	status = "okay";
    
    	cpsw3g_phy0: ethernet-phy@0 {
    		reg = <0>;
    		bootph-all;
    		ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
    		ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
    		ti,min-output-impedance;
    	};
    
    	cpsw3g_phy1: ethernet-phy@1 {
    		reg = <1>;
    		ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
    		ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
    		ti,min-output-impedance;
    	};
    };
    
    &usbss0 {
    	status = "okay";
    	ti,vbus-divider;
    };
    
    &usbss1 {
    	status = "okay";
    	ti,vbus-divider;
    };
    
    &usb0 {
    	usb-role-switch;
    
    	port {
    		usb0_hs_ep: endpoint {
    			remote-endpoint = <&usb_con_hs>;
    		};
    	};
    };
    
    &usb1 {
    	dr_mode = "host";
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_usb1_pins_default>;
    };
    
    &mcasp1 {
    	status = "okay";
    	#sound-dai-cells = <0>;
    
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_mcasp1_pins_default>;
    
    	op-mode = <0>;          /* MCASP_IIS_MODE */
    	tdm-slots = <2>;
    
    	serial-dir = <  /* 0: INACTIVE, 1: TX, 2: RX */
    	       1 0 2 0
    	       0 0 0 0
    	       0 0 0 0
    	       0 0 0 0
    	>;
    };
    
    &fss {
    	bootph-all;
    };
    
    &ospi0 {
    	status = "okay";
    	pinctrl-names = "default";
    	pinctrl-0 = <&ospi0_pins_default>;
    	bootph-all;
    
    	flash@0 {
    		compatible = "jedec,spi-nor";
    		reg = <0x0>;
    		spi-tx-bus-width = <8>;
    		spi-rx-bus-width = <8>;
    		spi-max-frequency = <25000000>;
    		cdns,tshsl-ns = <60>;
    		cdns,tsd2d-ns = <60>;
    		cdns,tchsh-ns = <60>;
    		cdns,tslch-ns = <60>;
    		cdns,read-delay = <4>;
    		cdns,phy-mode;
    		bootph-all;
    
    		partitions {
    			compatible = "fixed-partitions";
    			#address-cells = <1>;
    			#size-cells = <1>;
    			bootph-all;
    
    			partition@0 {
    				label = "ospi.tiboot3";
    				reg = <0x00 0x80000>;
    			};
    
    			partition@80000 {
    				label = "ospi.tispl";
    				reg = <0x80000 0x200000>;
    			};
    
    			partition@280000 {
    				label = "ospi.u-boot";
    				reg = <0x280000 0x400000>;
    			};
    
    			partition@680000 {
    				label = "ospi.env";
    				reg = <0x680000 0x40000>;
    			};
    
    			partition@6c0000 {
    				label = "ospi.env.backup";
    				reg = <0x6c0000 0x40000>;
    			};
    
    			partition@800000 {
    				label = "ospi.rootfs";
    				reg = <0x800000 0x37c0000>;
    			};
    
    			partition@3fc0000 {
    				label = "ospi.phypattern";
    				reg = <0x3fc0000 0x40000>;
    				bootph-all;
    			};
    		};
    	};
    };
    
    &mailbox0_cluster0 {
    	status = "okay";
    
    	mbox_r5_0: mbox-r5-0 {
    		ti,mbox-rx = <0 0 0>;
    		ti,mbox-tx = <1 0 0>;
    	};
    };
    
    &mailbox0_cluster1 {
    	status = "okay";
    
    	mbox_mcu_r5_0: mbox-mcu-r5-0 {
    		ti,mbox-rx = <0 0 0>;
    		ti,mbox-tx = <1 0 0>;
    	};
    };
    
    &wkup_r5fss0 {
    	status = "okay";
    };
    
    &wkup_r5fss0_core0 {
    	mboxes = <&mailbox0_cluster0 &mbox_r5_0>;
    	memory-region = <&wkup_r5fss0_core0_dma_memory_region>,
    			<&wkup_r5fss0_core0_memory_region>;
    };
    
    &mcu_r5fss0 {
    	status = "okay";
    };
    
    &mcu_r5fss0_core0 {
    	mboxes = <&mailbox0_cluster1 &mbox_mcu_r5_0>;
    	memory-region = <&mcu_r5fss0_core0_dma_memory_region>,
    			<&mcu_r5fss0_core0_memory_region>;
    };
    
    &main_uart0 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_uart0_pins_default>;
    	interrupts-extended = <&gic500 GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>,
    			<&main_pmx0 0x1c8>; /* (D14) UART0_RXD PADCONFIG114 */
    	interrupt-names = "irq", "wakeup";
    	status = "okay";
    	bootph-all;
    };
    
    &main_uart1 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_uart1_pins_default>;
    	/* Main UART1 is used by TIFS firmware */
    	status = "reserved";
    	bootph-all;
    };
    
    &mcu_pmx0 {
    	bootph-all;
    
    	wkup_uart0_pins_default: wkup-uart0-default-pins {
    		pinctrl-single,pins = <
    			AM62PX_MCU_IOPAD(0x024, PIN_INPUT, 0)	/* (D8) WKUP_UART0_RXD */
    			AM62PX_MCU_IOPAD(0x028, PIN_OUTPUT, 0)	/* (D7) WKUP_UART0_TXD */
    		>;
    		bootph-all;
    	};
    };
    
    &wkup_uart0 {
    	/* WKUP UART0 is used by DM firmware */
    	pinctrl-names = "default";
    	pinctrl-0 = <&wkup_uart0_pins_default>;
    	status = "reserved";
    	bootph-all;
    };
    
    /* mcu_gpio0 and mcu_gpio_intr are reserved for mcu firmware usage */
    &mcu_gpio0 {
    	status = "reserved";
    };
    
    &mcu_gpio_intr {
    	status = "reserved";
    };
    
    &dss_oldi_io_ctrl {
    	bootph-all;
    };
    
    &dss0 {
    	bootph-all;
    	status = "okay";
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_dpi_pins_default>;
    };
    
    &dss0_ports {
    	/* DSS0-VP2: DPI/HDMI Output */
    	hdmi0_dss: port@1 {
    		reg = <1>;
    
    		dss0_dpi1_out: endpoint {
    			remote-endpoint = <&sii9022_in>;
    		};
    	};
    };
    
    &epwm0 {
    	/* Pin 24/26 of J4 */
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_epwm0_pins_default>;
    	status = "okay";
    };
    
    &epwm1 {
    	/* Pin 23/19 of J4 */
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_epwm1_pins_default>;
    	status = "okay";
    };
    
    &ecap1 {
    	/* ECAP1 in APWM mode */
    	/* Pin 36 of J4 */
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_ecap1_pins_default>;
    	status = "okay";
    };
    
    &ecap2 {
    	/* ECAP2 in APWM mode */
    	/* Pin 11 of J4 */
    	pinctrl-names = "default";
    	pinctrl-0 = <&main_ecap2_pins_default>;
    	status = "okay";
    };
    

    Thanks

  • Thanks Sui, please alos help me to answer my previous question:

    Also, let me confirm something, to clarify, without changes in "k3-am62p5-r5-sk.dts", and using DFU, does board boots OK?. I want to understand if there is any scenario in which your board boots OK after flashing with DFU

    thanks

    Paula

  • Hi  

    I use DFU flash image into board , the board boot is ok , I use EMMC boot mode 

    Thanks

    SUI

  • Sui, sorry for my late reply. I haven't been able to reproduce the issue due to time. But I took another look at the files and logs you shared, and I would like to suggest few changes in k3-am62p5-r5-sk.dts

    List below:

    - Adding back OSPI controller register 

    - Adding "bootph-all" on flash mode and on individual partitions

    - Changed u-boot partition size to 4MB (as I saw it is in arch\arm64\boot\dts\ti\k3-am62p5-sk.dts)

    - Added OSPI pinx-mux (as I saw it is in arch\arm64\boot\dts\ti\k3-am62p5-sk.dts)

    Attached file haven't been tested, so please double check I didn't introduce any errors. We can delete "phy-mode" as well.. In anycase, please take a look and see if suggestions help your board to boot OK

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/791/k3_2D00_am62p5_2D00_r5_2D00_sk_5F00_modif.dts

    thank you,

    Paula