From nobody Sun Oct 5 01:50:06 2025 Received: from lelvem-ot01.ext.ti.com (lelvem-ot01.ext.ti.com [198.47.23.234]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 54F392D877D; Mon, 11 Aug 2025 19:56:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.234 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754942221; cv=none; b=PR5x2HBm3xw+1hHr2fvszmbqeq82cIP617thq+wA5ePpNalFfmnClbwdca2M3n8B82OgJlfPb+eYwzbIKrTEwIKh0i3CepFXh4mgqeeJB0rBFUEtj2J9s/iKjGzY/H9rcvIY3dX7O4wv4w6njzlWU7bOFNsHga3NdBbIvFdWvGc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754942221; c=relaxed/simple; bh=O4EaQU0HFChYr2mDzig97tIGwA8DlYGaFZRjsARsY+w=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=bp3qOKf9cyabhVN/mFDbPZ+UDRNLR7UmenD2vtzqLaXbvGvE3r9Ju31PQGW/cqBdEObW0HqK0teW9Geysyr72lNnncDuY67B1OgGaKxHtqOFPO6q3T46M0efjfX7Ms7ngXjMNTlze85n3rwS4/m8xfaLNeLV/U/GHLBIfhdqQjE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=J4NwimF9; arc=none smtp.client-ip=198.47.23.234 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="J4NwimF9" Received: from fllvem-sh03.itg.ti.com ([10.64.41.86]) by lelvem-ot01.ext.ti.com (8.15.2/8.15.2) with ESMTP id 57BJXbcf1227833; Mon, 11 Aug 2025 14:33:37 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1754940817; bh=SZ2XoSFLc+CUe4UMFp0hfnljwVtUhJbdR1fBYVPzBio=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=J4NwimF9nUvQJTe865YlrrNhPksDVigzg4slzjK5x1tvByoF5PiROotXjGj/eW1nk vlNfzo/+FgrpmVdOaovEZXRB3ybC2nZKv4vaihS0Qxa4BtXvkLzTj358De4KrlZnAj X+CN4CFaDb4JuziPz1EqQR6RiH9MiufF9zRZNG5Y= Received: from DFLE104.ent.ti.com (dfle104.ent.ti.com [10.64.6.25]) by fllvem-sh03.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 57BJXbX23273494 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL); Mon, 11 Aug 2025 14:33:37 -0500 Received: from DFLE103.ent.ti.com (10.64.6.24) by DFLE104.ent.ti.com (10.64.6.25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55; Mon, 11 Aug 2025 14:33:36 -0500 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) by DFLE103.ent.ti.com (10.64.6.24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55 via Frontend Transport; Mon, 11 Aug 2025 14:33:37 -0500 Received: from santhoshkumark.dhcp.ti.com (santhoshkumark.dhcp.ti.com [172.24.233.254]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 57BJWq1w3690681; Mon, 11 Aug 2025 14:33:33 -0500 From: Santhosh Kumar K To: , , , , , , , CC: , , , , , , Subject: [RFC PATCH 09/10] spi: cadence-quadspi: Implement PHY for higher frequencies in SDR mode Date: Tue, 12 Aug 2025 01:02:18 +0530 Message-ID: <20250811193219.731851-10-s-k6@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250811193219.731851-1-s-k6@ti.com> References: <20250811193219.731851-1-s-k6@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Content-Type: text/plain; charset="utf-8" With SDR mode, the controller can only process at 1/4th of the reference clock without PHY. So, add a SDR based tuning algorithm to enable higher speed operations. A known pattern (stress pattern) is flashed at a location in the flash device, which will be later used to read back and compare. Iterate through read_delay from 0 to 4, find first tuning point by setting tx=3D127. Find difference between rxlow and rxhigh and store it in window1. Find second tuning point with read_delay incremented by 1 and similarly calculate window2. Compare window1 and window2 to finalise the optimal tuning point. Write the final tuning point into PHY Configuration Register. With this OSPI can operate at up-to 200MHz SDR mode with internal loopback clocking scheme for sampling data inside the soft PHY Tuning for DDR mode with DQS will be added a later point. Signed-off-by: Santhosh Kumar K --- drivers/spi/spi-cadence-quadspi.c | 447 ++++++++++++++++++++++++++++++ 1 file changed, 447 insertions(+) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-qu= adspi.c index 6c1159435577..1626cb9a9700 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -78,6 +78,7 @@ struct cqspi_flash_pdata { u8 cs; bool use_phy; struct phy_setting phy_setting; + struct spi_mem_op phy_read_op; }; =20 struct cqspi_st { @@ -135,6 +136,7 @@ struct cqspi_driver_platdata { #define CQSPI_READ_TIMEOUT_MS 10 #define CQSPI_BUSYWAIT_TIMEOUT_US 500 #define CQSPI_PHY_FREQUENCY 166000000 +#define CQSPI_DLL_TIMEOUT_US 300 =20 /* Runtime_pm autosuspend delay */ #define CQSPI_AUTOSUSPEND_TIMEOUT 2000 @@ -194,6 +196,7 @@ struct cqspi_driver_platdata { #define CQSPI_REG_READCAPTURE_BYPASS BIT(0) #define CQSPI_REG_READCAPTURE_DELAY_LSB 1 #define CQSPI_REG_READCAPTURE_DELAY_MASK 0xF +#define CQSPI_REG_READCAPTURE_EDGE BIT(5) =20 #define CQSPI_REG_SIZE 0x14 #define CQSPI_REG_SIZE_ADDRESS_LSB 0 @@ -272,6 +275,26 @@ struct cqspi_driver_platdata { #define CQSPI_REG_POLLING_STATUS 0xB0 #define CQSPI_REG_POLLING_STATUS_DUMMY_LSB 16 =20 +#define CQSPI_REG_PHY_CONFIG 0xB4 +#define CQSPI_REG_PHY_CONFIG_RX_DEL_LSB 0 +#define CQSPI_REG_PHY_CONFIG_RX_DEL_MASK 0x7F +#define CQSPI_REG_PHY_CONFIG_TX_DEL_LSB 16 +#define CQSPI_REG_PHY_CONFIG_TX_DEL_MASK 0x7F +#define CQSPI_REG_PHY_CONFIG_DLL_RESET BIT(30) +#define CQSPI_REG_PHY_CONFIG_RESYNC BIT(31) + +#define CQSPI_REG_PHY_DLL_MASTER 0xB8 +#define CQSPI_REG_PHY_DLL_MASTER_INIT_DELAY_LSB 0 +#define CQSPI_REG_PHY_DLL_MASTER_INIT_DELAY_VAL 16 +#define CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_LEN 0x7 +#define CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_LSB 20 +#define CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_3 0x2 +#define CQSPI_REG_PHY_DLL_MASTER_BYPASS BIT(23) +#define CQSPI_REG_PHY_DLL_MASTER_CYCLE BIT(24) + +#define CQSPI_REG_DLL_OBS_LOW 0xBC +#define CQSPI_REG_DLL_OBS_LOW_DLL_LOCK BIT(0) +#define CQSPI_REG_DLL_OBS_LOW_LOOPBACK_LOCK BIT(15) #define CQSPI_REG_OP_EXT_LOWER 0xE0 #define CQSPI_REG_OP_EXT_READ_LSB 24 #define CQSPI_REG_OP_EXT_WRITE_LSB 16 @@ -317,6 +340,46 @@ struct cqspi_driver_platdata { =20 #define CQSPI_REG_VERSAL_DMA_VAL 0x602 =20 +#define CQSPI_PHY_INIT_RD 1 +#define CQSPI_PHY_MAX_RD 4 +#define CQSPI_PHY_MAX_DELAY 127 +#define CQSPI_PHY_DDR_SEARCH_STEP 4 +#define CQSPI_PHY_MAX_RX 63 +#define CQSPI_PHY_MAX_TX 63 +#define CQSPI_PHY_TX_LOOKUP_LOW_START 28 +#define CQSPI_PHY_TX_LOOKUP_LOW_END 48 +#define CQSPI_PHY_TX_LOOKUP_HIGH_START 60 +#define CQSPI_PHY_TX_LOOKUP_HIGH_END 96 +#define CQSPI_PHY_RX_LOW_SEARCH_START 0 +#define CQSPI_PHY_RX_LOW_SEARCH_END 40 +#define CQSPI_PHY_RX_HIGH_SEARCH_START 24 +#define CQSPI_PHY_RX_HIGH_SEARCH_END 127 +#define CQSPI_PHY_TX_LOW_SEARCH_START 0 +#define CQSPI_PHY_TX_LOW_SEARCH_END 64 +#define CQSPI_PHY_TX_HIGH_SEARCH_START 78 +#define CQSPI_PHY_TX_HIGH_SEARCH_END 127 +#define CQSPI_PHY_SEARCH_OFFSET 8 + +#define CQSPI_PHY_DEFAULT_TEMP 45 +#define CQSPI_PHY_MIN_TEMP -45 +#define CQSPI_PHY_MAX_TEMP 130 +#define CQSPI_PHY_MID_TEMP \ + (CQSPI_PHY_MIN_TEMP + ((CQSPI_PHY_MAX_TEMP - CQSPI_PHY_MIN_TEMP) / 2)) + +static u8 phy_tuning_pattern[] =3D { + 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xFE, 0xFE, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0xFE, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, + 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFE, 0x00, 0xFE, 0xFE, 0x01, + 0x01, 0x01, 0x01, 0xFE, 0x00, 0xFE, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFE, + 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0xFE, 0xFE, 0xFE, 0x01, + 0x01, 0x01, 0x01, 0x00, 0xFE, 0xFE, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, + 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFE, 0xFE, 0xFE, 0xFE, 0x01, + 0x01, 0x01, 0x01, 0xFE, 0xFE, 0xFE, 0xFE, 0x01, +}; + static int cqspi_wait_for_bit(const struct cqspi_driver_platdata *ddata, void __iomem *reg, const u32 mask, bool clr, bool busywait) @@ -455,6 +518,279 @@ static int cqspi_wait_idle(struct cqspi_st *cqspi) } } =20 +static void cqspi_set_tx_dll(void __iomem *reg_base, u8 dll) +{ + unsigned int reg; + + reg =3D readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg &=3D ~(CQSPI_REG_PHY_CONFIG_TX_DEL_MASK + << CQSPI_REG_PHY_CONFIG_TX_DEL_LSB); + reg |=3D (dll & CQSPI_REG_PHY_CONFIG_TX_DEL_MASK) + << CQSPI_REG_PHY_CONFIG_TX_DEL_LSB; + reg |=3D CQSPI_REG_PHY_CONFIG_RESYNC; + writel(reg, reg_base + CQSPI_REG_PHY_CONFIG); +} + +static void cqspi_set_rx_dll(void __iomem *reg_base, u8 dll) +{ + unsigned int reg; + + reg =3D readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg &=3D ~(CQSPI_REG_PHY_CONFIG_RX_DEL_MASK + << CQSPI_REG_PHY_CONFIG_RX_DEL_LSB); + reg |=3D (dll & CQSPI_REG_PHY_CONFIG_RX_DEL_MASK) + << CQSPI_REG_PHY_CONFIG_RX_DEL_LSB; + reg |=3D CQSPI_REG_PHY_CONFIG_RESYNC; + writel(reg, reg_base + CQSPI_REG_PHY_CONFIG); +} + +static int cqspi_resync_dll(struct cqspi_st *cqspi) +{ + void __iomem *reg_base =3D cqspi->iobase; + unsigned int reg; + int ret; + + ret =3D cqspi_wait_idle(cqspi); + + if (!ret) { + reg =3D readl(reg_base + CQSPI_REG_CONFIG); + reg &=3D ~(CQSPI_REG_CONFIG_ENABLE_MASK); + writel(reg, reg_base + CQSPI_REG_CONFIG); + + reg =3D readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg &=3D ~(CQSPI_REG_PHY_CONFIG_DLL_RESET | + CQSPI_REG_PHY_CONFIG_RESYNC); + writel(reg, reg_base + CQSPI_REG_PHY_CONFIG); + + reg =3D readl(reg_base + CQSPI_REG_PHY_DLL_MASTER); + reg |=3D (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 =3D readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg |=3D 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 &=3D CQSPI_REG_DLL_OBS_LOW_DLL_LOCK), 0, + CQSPI_DLL_TIMEOUT_US); + + readl_poll_timeout(reg_base + CQSPI_REG_DLL_OBS_LOW, reg, + !(reg &=3D CQSPI_REG_DLL_OBS_LOW_LOOPBACK_LOCK), + 0, CQSPI_DLL_TIMEOUT_US); + + reg =3D readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg |=3D CQSPI_REG_PHY_CONFIG_RESYNC; + writel(reg, reg_base + CQSPI_REG_PHY_CONFIG); + + reg =3D readl(reg_base + CQSPI_REG_CONFIG); + reg |=3D CQSPI_REG_CONFIG_ENABLE_MASK; + writel(reg, reg_base + CQSPI_REG_CONFIG); + } + + return ret; +} + +static int cqspi_phy_apply_setting(struct cqspi_flash_pdata *f_pdata, + struct phy_setting *phy) +{ + struct cqspi_st *cqspi =3D f_pdata->cqspi; + unsigned int reg; + + reg =3D readl(cqspi->iobase + CQSPI_REG_READCAPTURE); + reg |=3D CQSPI_REG_READCAPTURE_EDGE; + writel(reg, cqspi->iobase + CQSPI_REG_READCAPTURE); + + cqspi_set_rx_dll(cqspi->iobase, phy->rx); + cqspi_set_tx_dll(cqspi->iobase, phy->tx); + f_pdata->phy_setting.read_delay =3D phy->read_delay; + + return cqspi_resync_dll(cqspi); +} + +static int cqspi_phy_check_pattern(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem) +{ + struct spi_mem_op op =3D f_pdata->phy_read_op; + u8 *read_data; + int ret; + + read_data =3D kmalloc(sizeof(phy_tuning_pattern), GFP_KERNEL); + if (!read_data) + return -ENOMEM; + + op.data.buf.in =3D read_data; + op.data.nbytes =3D sizeof(phy_tuning_pattern); + + ret =3D spi_mem_exec_op(mem, &op); + if (ret) + goto out; + + if (memcmp(read_data, phy_tuning_pattern, + ARRAY_SIZE(phy_tuning_pattern))) { + ret =3D -EAGAIN; + goto out; + } + + ret =3D 0; + +out: + kfree(read_data); + return ret; +} + +static int cqspi_find_rx_low_sdr(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem, struct phy_setting *phy) +{ + struct device *dev =3D &f_pdata->cqspi->pdev->dev; + int ret; + + phy->rx =3D 0; + do { + ret =3D cqspi_phy_apply_setting(f_pdata, phy); + if (!ret) { + ret =3D cqspi_phy_check_pattern(f_pdata, mem); + if (!ret) + return 0; + } + phy->rx++; + } while (phy->rx < CQSPI_PHY_MAX_DELAY - 1); + + dev_dbg(dev, "Unable to find RX low\n"); + return -ENOENT; +} + +static int cqspi_find_rx_high_sdr(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem, struct phy_setting *phy, + u8 lowerbound) +{ + struct device *dev =3D &f_pdata->cqspi->pdev->dev; + int ret; + + phy->rx =3D CQSPI_PHY_MAX_DELAY; + do { + ret =3D cqspi_phy_apply_setting(f_pdata, phy); + if (!ret) { + ret =3D cqspi_phy_check_pattern(f_pdata, mem); + if (!ret) + return 0; + } + phy->rx--; + } while (phy->rx > lowerbound); + + dev_dbg(dev, "Unable to find RX high\n"); + return -ENOENT; +} + +static void cqspi_phy_reset_setting(struct phy_setting *phy) +{ + phy->rx =3D 0; + phy->tx =3D 127; + phy->read_delay =3D 0; +} + +static int cqspi_phy_tuning_sdr(struct cqspi_flash_pdata *f_pdata, + struct spi_mem *mem) +{ + struct cqspi_st *cqspi =3D f_pdata->cqspi; + struct device *dev =3D &cqspi->pdev->dev; + struct phy_setting rxlow, rxhigh, first, second, final; + char window1 =3D 0; + char window2 =3D 0; + int ret; + + f_pdata->use_phy =3D true; + cqspi_phy_reset_setting(&rxlow); + cqspi_phy_reset_setting(&rxhigh); + cqspi_phy_reset_setting(&first); + + do { + ret =3D cqspi_find_rx_low_sdr(f_pdata, mem, &rxlow); + + if (ret) + rxlow.read_delay++; + } while (ret && rxlow.read_delay <=3D CQSPI_PHY_MAX_RD); + + rxhigh.read_delay =3D rxlow.read_delay; + ret =3D cqspi_find_rx_high_sdr(f_pdata, mem, &rxhigh, rxlow.rx); + if (ret) + goto out; + + first.read_delay =3D rxlow.read_delay; + window1 =3D rxhigh.rx - rxlow.rx; + first.rx =3D rxlow.rx + (window1 / 2); + + dev_dbg(dev, "First tuning point: RX: %d TX: %d RD: %d\n", first.rx, + first.tx, first.read_delay); + ret =3D cqspi_phy_apply_setting(f_pdata, &first); + if (!ret) + ret =3D cqspi_phy_check_pattern(f_pdata, mem); + + if (ret || first.read_delay > CQSPI_PHY_MAX_RD) + goto out; + + cqspi_phy_reset_setting(&rxlow); + cqspi_phy_reset_setting(&rxhigh); + cqspi_phy_reset_setting(&second); + + rxlow.read_delay =3D first.read_delay + 1; + if (rxlow.read_delay > CQSPI_PHY_MAX_RD) + goto compare; + + ret =3D cqspi_find_rx_low_sdr(f_pdata, mem, &rxlow); + if (ret) + goto compare; + + rxhigh.read_delay =3D rxlow.read_delay; + ret =3D cqspi_find_rx_high_sdr(f_pdata, mem, &rxhigh, rxlow.rx); + if (ret) + goto compare; + + window2 =3D rxhigh.rx - rxlow.rx; + second.rx =3D rxlow.rx + (window2 / 2); + second.read_delay =3D rxlow.read_delay; + + dev_dbg(dev, "Second tuning point: RX: %d TX: %d RD: %d\n", second.rx, + second.tx, second.read_delay); + ret =3D cqspi_phy_apply_setting(f_pdata, &second); + if (!ret) + ret =3D cqspi_phy_check_pattern(f_pdata, mem); + + if (ret || second.read_delay > CQSPI_PHY_MAX_RD) + window2 =3D 0; + +compare: + cqspi_phy_reset_setting(&final); + if (window2 > window1) { + final.rx =3D second.rx; + final.read_delay =3D second.read_delay; + } else { + final.rx =3D first.rx; + final.read_delay =3D first.read_delay; + } + + dev_dbg(dev, "Final tuning point: RX: %d TX: %d RD: %d\n", final.rx, + final.tx, final.read_delay); + ret =3D cqspi_phy_apply_setting(f_pdata, &final); + if (!ret) + ret =3D cqspi_phy_check_pattern(f_pdata, mem); + + if (ret) { + ret =3D -EINVAL; + goto out; + } + + f_pdata->phy_setting.read_delay =3D final.read_delay; + f_pdata->phy_setting.rx =3D final.rx; + f_pdata->phy_setting.tx =3D final.tx; + +out: + if (ret) + f_pdata->use_phy =3D false; + + return ret; +} + static void cqspi_readdata_capture(struct cqspi_st *cqspi, const bool bypass, const unsigned int delay) @@ -1366,6 +1702,83 @@ static void cqspi_config_baudrate_div(struct cqspi_s= t *cqspi) writel(reg, reg_base + CQSPI_REG_CONFIG); } =20 +static void cqspi_phy_set_dll_master(struct cqspi_st *cqspi) +{ + void __iomem *reg_base =3D cqspi->iobase; + unsigned int reg; + + reg =3D readl(reg_base + CQSPI_REG_PHY_DLL_MASTER); + reg &=3D ~((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 |=3D ((CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_3 + << CQSPI_REG_PHY_DLL_MASTER_DLY_ELMTS_LSB) | + CQSPI_REG_PHY_DLL_MASTER_CYCLE); + + writel(reg, reg_base + CQSPI_REG_PHY_DLL_MASTER); +} + +static void cqspi_phy_pre_config(struct cqspi_st *cqspi, const bool bypass, + struct cqspi_flash_pdata *f_pdata) +{ + void __iomem *reg_base =3D cqspi->iobase; + unsigned int reg; + u8 dummy; + + cqspi_readdata_capture(cqspi, bypass, + f_pdata->phy_setting.read_delay); + + reg =3D readl(reg_base + CQSPI_REG_CONFIG); + reg &=3D ~(CQSPI_REG_CONFIG_PHY_EN | CQSPI_REG_CONFIG_PHY_PIPELINE); + reg |=3D CQSPI_REG_CONFIG_PHY_EN; + writel(reg, reg_base + CQSPI_REG_CONFIG); + + reg =3D readl(reg_base + CQSPI_REG_RD_INSTR); + dummy =3D FIELD_GET(CQSPI_REG_RD_INSTR_DUMMY_MASK + << CQSPI_REG_RD_INSTR_DUMMY_LSB, + reg); + dummy--; + reg &=3D ~(CQSPI_REG_RD_INSTR_DUMMY_MASK << CQSPI_REG_RD_INSTR_DUMMY_LSB); + reg |=3D FIELD_PREP(CQSPI_REG_RD_INSTR_DUMMY_MASK + << CQSPI_REG_RD_INSTR_DUMMY_LSB, + dummy); + writel(reg, reg_base + CQSPI_REG_RD_INSTR); + + cqspi_phy_set_dll_master(cqspi); +} + +static void cqspi_phy_post_config(struct cqspi_st *cqspi, + const unsigned int delay) +{ + void __iomem *reg_base =3D cqspi->iobase; + unsigned int reg; + u8 dummy; + + reg =3D readl(reg_base + CQSPI_REG_READCAPTURE); + reg &=3D ~(CQSPI_REG_READCAPTURE_DELAY_MASK + << CQSPI_REG_READCAPTURE_DELAY_LSB); + + reg |=3D (delay & CQSPI_REG_READCAPTURE_DELAY_MASK) + << CQSPI_REG_READCAPTURE_DELAY_LSB; + writel(reg, reg_base + CQSPI_REG_READCAPTURE); + + reg =3D readl(reg_base + CQSPI_REG_CONFIG); + reg &=3D ~(CQSPI_REG_CONFIG_PHY_EN | CQSPI_REG_CONFIG_PHY_PIPELINE); + writel(reg, reg_base + CQSPI_REG_CONFIG); + + reg =3D readl(reg_base + CQSPI_REG_RD_INSTR); + dummy =3D FIELD_GET(CQSPI_REG_RD_INSTR_DUMMY_MASK + << CQSPI_REG_RD_INSTR_DUMMY_LSB, + reg); + dummy++; + reg &=3D ~(CQSPI_REG_RD_INSTR_DUMMY_MASK << CQSPI_REG_RD_INSTR_DUMMY_LSB); + reg |=3D FIELD_PREP(CQSPI_REG_RD_INSTR_DUMMY_MASK + << CQSPI_REG_RD_INSTR_DUMMY_LSB, + dummy); + writel(reg, reg_base + CQSPI_REG_RD_INSTR); +} + static void cqspi_configure(struct cqspi_flash_pdata *f_pdata, unsigned long sclk) { @@ -1724,6 +2137,39 @@ static bool cqspi_supports_mem_op(struct spi_mem *me= m, return spi_mem_default_supports_op(mem, op); } =20 +static int cqspi_mem_execute_tuning(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct cqspi_st *cqspi =3D + spi_controller_get_devdata(mem->spi->controller); + struct cqspi_flash_pdata *f_pdata; + struct device *dev =3D &cqspi->pdev->dev; + int ret; + + f_pdata =3D &cqspi->f_pdata[spi_get_chipselect(mem->spi, 0)]; + f_pdata->phy_read_op =3D *op; + + if (!cqspi_phy_op_eligible_sdr(f_pdata, op)) { + dev_warn(dev, + "Given read_op not eligible. Skipping PHY Tuning.\n"); + return -EOPNOTSUPP; + } + + ret =3D cqspi_phy_check_pattern(f_pdata, mem); + if (ret) { + dev_warn(dev, "Pattern not found. Skipping PHY Tuning.\n"); + return -EINVAL; + } + + cqspi_phy_pre_config(cqspi, true, f_pdata); + ret =3D cqspi_phy_tuning_sdr(f_pdata, mem); + if (ret) + dev_info(&cqspi->pdev->dev, "PHY Tuning failed: %d\n", ret); + + cqspi_phy_post_config(cqspi, f_pdata->read_delay); + return ret; +} + static int cqspi_of_get_flash_pdata(struct platform_device *pdev, struct cqspi_flash_pdata *f_pdata, struct device_node *np) @@ -1898,6 +2344,7 @@ static const struct spi_controller_mem_ops cqspi_mem_= ops =3D { .exec_op =3D cqspi_exec_mem_op, .get_name =3D cqspi_get_name, .supports_op =3D cqspi_supports_mem_op, + .execute_tuning =3D cqspi_mem_execute_tuning, }; =20 static const struct spi_controller_mem_caps cqspi_mem_caps =3D { --=20 2.34.1