[PATCH 7/7] spi: spi-zynqmp-gqspi: Add tap delay support for GQSPI controller on Versal platform

From: Amit Kumar Mahapatra
Date: Thu Sep 01 2022 - 01:48:56 EST


This patch adds tap delay support for GQSPI controller on Versal
platform.

Signed-off-by: Naga Sureshkumar Relli <nagasure@xxxxxxxxxx>
Signed-off-by: Michal Simek <michal.simek@xxxxxxx>
Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@xxxxxxxxxx>
---
drivers/spi/spi-zynqmp-gqspi.c | 90 +++++++++++++++++++++++++++-------
1 file changed, 71 insertions(+), 19 deletions(-)

diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
index 0e6423ff33f4..825a2d072536 100644
--- a/drivers/spi/spi-zynqmp-gqspi.c
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
@@ -34,6 +35,7 @@
#define GQSPI_RXD_OFST 0x00000120
#define GQSPI_TX_THRESHOLD_OFST 0x00000128
#define GQSPI_RX_THRESHOLD_OFST 0x0000012C
+#define IOU_TAPDLY_BYPASS_OFST 0x0000003C
#define GQSPI_LPBK_DLY_ADJ_OFST 0x00000138
#define GQSPI_GEN_FIFO_OFST 0x00000140
#define GQSPI_SEL_OFST 0x00000144
@@ -134,10 +136,17 @@
#define GQSPI_SELECT_MODE_QUADSPI 0x4
#define GQSPI_DMA_UNALIGN 0x3
#define GQSPI_DEFAULT_NUM_CS 1 /* Default number of chip selects */
+#define GQSPI_LPBK_DLY_ADJ_DLY_1 0x1
+#define GQSPI_LPBK_DLY_ADJ_DLY_1_SHIFT 3
#define GQSPI_USE_DATA_DLY 0x1
#define GQSPI_USE_DATA_DLY_SHIFT 31
#define GQSPI_DATA_DLY_ADJ_VALUE 0x2
#define GQSPI_DATA_DLY_ADJ_SHIFT 28
+#define TAP_DLY_BYPASS_LQSPI_RX_VALUE 0x1
+#define TAP_DLY_BYPASS_LQSPI_RX_SHIFT 2
+
+/* set to differentiate versal from zynqmp, 1=versal, 0=zynqmp */
+#define QSPI_QUIRK_HAS_TAPDELAY BIT(0)

#define GQSPI_FREQ_37_5MHZ 37500000
#define GQSPI_FREQ_40MHZ 40000000
@@ -147,6 +156,14 @@
#define SPI_AUTOSUSPEND_TIMEOUT 3000
enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};

+/**
+ * struct qspi_platform_data - zynqmp qspi platform data structure
+ * @quirks: Flags is used to identify the platform
+ */
+struct qspi_platform_data {
+ u32 quirks;
+};
+
/**
* struct zynqmp_qspi - Defines qspi driver instance
* @ctlr: Pointer to the spi controller information
@@ -167,7 +184,9 @@ enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};
* @mode: Defines the mode in which QSPI is operating
* @data_completion: completion structure
* @op_lock: Operational lock
+
* @speed_hz: Current SPI bus clock speed in hz
+ * @has_tapdelay: Used for tapdelay register available in qspi
*/
struct zynqmp_qspi {
struct spi_controller *ctlr;
@@ -189,6 +208,7 @@ struct zynqmp_qspi {
struct completion data_completion;
struct mutex op_lock;
u32 speed_hz;
+ bool has_tapdelay;
};

/**
@@ -268,26 +288,46 @@ static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr,
*/
static void zynqmp_qspi_set_tapdelay(struct zynqmp_qspi *xqspi, u32 baudrateval)
{
- u32 lpbkdlyadj = 0, datadlyadj = 0, clk_rate;
+ u32 tapdlybypass = 0, lpbkdlyadj = 0, datadlyadj = 0, clk_rate;
u32 reqhz = 0;

clk_rate = clk_get_rate(xqspi->refclk);
reqhz = (clk_rate / (GQSPI_BAUD_DIV_SHIFT << baudrateval));

- if (reqhz <= GQSPI_FREQ_40MHZ) {
- zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI,
- PM_TAPDELAY_BYPASS_ENABLE);
- } else if (reqhz <= GQSPI_FREQ_100MHZ) {
- zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI,
- PM_TAPDELAY_BYPASS_ENABLE);
- lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK);
- datadlyadj |= ((GQSPI_USE_DATA_DLY <<
- GQSPI_USE_DATA_DLY_SHIFT)
- | (GQSPI_DATA_DLY_ADJ_VALUE <<
- GQSPI_DATA_DLY_ADJ_SHIFT));
- } else if (reqhz <= GQSPI_FREQ_150MHZ) {
- lpbkdlyadj |= GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK;
+ if (!xqspi->has_tapdelay) {
+ if (reqhz <= GQSPI_FREQ_40MHZ) {
+ zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI,
+ PM_TAPDELAY_BYPASS_ENABLE);
+ } else if (reqhz <= GQSPI_FREQ_100MHZ) {
+ zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI,
+ PM_TAPDELAY_BYPASS_ENABLE);
+ lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK);
+ datadlyadj |= ((GQSPI_USE_DATA_DLY <<
+ GQSPI_USE_DATA_DLY_SHIFT)
+ | (GQSPI_DATA_DLY_ADJ_VALUE <<
+ GQSPI_DATA_DLY_ADJ_SHIFT));
+ } else if (reqhz <= GQSPI_FREQ_150MHZ) {
+ lpbkdlyadj |= GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK;
+ }
+ } else {
+ if (reqhz <= GQSPI_FREQ_37_5MHZ) {
+ tapdlybypass |= (TAP_DLY_BYPASS_LQSPI_RX_VALUE <<
+ TAP_DLY_BYPASS_LQSPI_RX_SHIFT);
+ } else if (reqhz <= GQSPI_FREQ_100MHZ) {
+ tapdlybypass |= (TAP_DLY_BYPASS_LQSPI_RX_VALUE <<
+ TAP_DLY_BYPASS_LQSPI_RX_SHIFT);
+ lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK);
+ datadlyadj |= (GQSPI_USE_DATA_DLY <<
+ GQSPI_USE_DATA_DLY_SHIFT);
+ } else if (reqhz <= GQSPI_FREQ_150MHZ) {
+ lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK
+ | (GQSPI_LPBK_DLY_ADJ_DLY_1 <<
+ GQSPI_LPBK_DLY_ADJ_DLY_1_SHIFT));
+ }
+ zynqmp_gqspi_write(xqspi,
+ IOU_TAPDLY_BYPASS_OFST, tapdlybypass);
}
+
zynqmp_gqspi_write(xqspi, GQSPI_LPBK_DLY_ADJ_OFST, lpbkdlyadj);
zynqmp_gqspi_write(xqspi, GQSPI_DATA_DLY_ADJ_OFST, datadlyadj);
}
@@ -1147,6 +1187,15 @@ static const struct dev_pm_ops zynqmp_qspi_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(zynqmp_qspi_suspend, zynqmp_qspi_resume)
};

+static const struct qspi_platform_data versal_qspi_def = {
+ .quirks = QSPI_QUIRK_HAS_TAPDELAY,
+};
+
+static const struct of_device_id zynqmp_qspi_of_match[] = {
+ { .compatible = "xlnx,zynqmp-qspi-1.0"},
+ { .compatible = "xlnx,versal-qspi-1.0", .data = &versal_qspi_def },
+ { /* End of table */ }
+};
static const struct spi_controller_mem_ops zynqmp_qspi_mem_ops = {
.exec_op = zynqmp_qspi_exec_op,
};
@@ -1165,6 +1214,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
struct spi_controller *ctlr;
struct zynqmp_qspi *xqspi;
struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
struct device_node *np = dev->of_node;

ctlr = spi_alloc_master(&pdev->dev, sizeof(*xqspi));
@@ -1176,6 +1226,13 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
xqspi->ctlr = ctlr;
platform_set_drvdata(pdev, xqspi);

+ match = of_match_node(zynqmp_qspi_of_match, pdev->dev.of_node);
+ if (match) {
+ const struct qspi_platform_data *p_data = match->data;
+
+ if (p_data && (p_data->quirks & QSPI_QUIRK_HAS_TAPDELAY))
+ xqspi->has_tapdelay = true;
+ }
xqspi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(xqspi->regs)) {
ret = PTR_ERR(xqspi->regs);
@@ -1303,11 +1360,6 @@ static int zynqmp_qspi_remove(struct platform_device *pdev)
return 0;
}

-static const struct of_device_id zynqmp_qspi_of_match[] = {
- { .compatible = "xlnx,zynqmp-qspi-1.0", },
- { /* End of table */ }
-};
-
MODULE_DEVICE_TABLE(of, zynqmp_qspi_of_match);

static struct platform_driver zynqmp_qspi_driver = {
--
2.17.1