Unverified Commit f74d2182 authored by Mark Brown's avatar Mark Brown
Browse files

spi: spi-zyqnmp-gqspi: Add tap delay and Versal platform support

Merge series from Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>:

A bunch of improvements to the driver:

- Fix kernel-doc warnings in GQSPI driver.
- Avoid setting CPOL, CPHA & baud rate multiple times.
- Add Versal platform support in GQSPI driver.
- Add tap delay support in GQSPI driver.
parents a977c3a9 29f4d95b
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -14,7 +14,9 @@ allOf:

properties:
  compatible:
    const: xlnx,zynqmp-qspi-1.0
    enum:
      - xlnx,versal-qspi-1.0
      - xlnx,zynqmp-qspi-1.0

  reg:
    maxItems: 2
+7 −0
Original line number Diff line number Diff line
@@ -843,6 +843,13 @@ int zynqmp_pm_read_pggs(u32 index, u32 *value)
}
EXPORT_SYMBOL_GPL(zynqmp_pm_read_pggs);

int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value)
{
	return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_TAPDELAY_BYPASS,
				   index, value, NULL);
}
EXPORT_SYMBOL_GPL(zynqmp_pm_set_tapdelay_bypass);

/**
 * zynqmp_pm_set_boot_health_status() - PM API for setting healthy boot status
 * @value:	Status value to be written
+155 −36
Original line number Diff line number Diff line
@@ -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
@@ -48,6 +50,7 @@
#define GQSPI_QSPIDMA_DST_I_MASK_OFST	0x00000820
#define GQSPI_QSPIDMA_DST_ADDR_OFST	0x00000800
#define GQSPI_QSPIDMA_DST_ADDR_MSB_OFST 0x00000828
#define GQSPI_DATA_DLY_ADJ_OFST         0x000001F8

/* GQSPI register bit masks */
#define GQSPI_SEL_MASK				0x00000001
@@ -136,11 +139,37 @@

#define GQSPI_MAX_NUM_CS	2	/* Maximum number of chip selects */

#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 GQSPI_LPBK_DLY_ADJ_DLY_1	0x1
#define GQSPI_LPBK_DLY_ADJ_DLY_1_SHIFT	0x3
#define TAP_DLY_BYPASS_LQSPI_RX_VALUE	0x1
#define TAP_DLY_BYPASS_LQSPI_RX_SHIFT	0x2

/* 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
#define GQSPI_FREQ_100MHZ	100000000
#define GQSPI_FREQ_150MHZ	150000000

#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
 * @regs:		Virtual address of the QSPI controller registers
 * @refclk:		Pointer to the peripheral clock
 * @pclk:		Pointer to the APB clock
@@ -157,6 +186,9 @@ enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};
 * @genfifoentry:	Used for storing the genfifoentry instruction.
 * @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;
@@ -177,6 +209,8 @@ struct zynqmp_qspi {
	enum mode_type mode;
	struct completion data_completion;
	struct mutex op_lock;
	u32 speed_hz;
	bool has_tapdelay;
};

/**
@@ -249,6 +283,56 @@ static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr,
	}
}

/**
 * zynqmp_qspi_set_tapdelay:   To configure qspi tap delays
 * @xqspi:             Pointer to the zynqmp_qspi structure
 * @baudrateval:       Buadrate to configure
 */
static void zynqmp_qspi_set_tapdelay(struct zynqmp_qspi *xqspi, u32 baudrateval)
{
	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 (!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);
}

/**
 * zynqmp_qspi_init_hw - Initialize the hardware
 * @xqspi:	Pointer to the zynqmp_qspi structure
@@ -264,12 +348,15 @@ static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr,
 *	- Enable manual slave select
 *	- Enable manual start
 *	- Deselect all the chip select lines
 *	- Set the little endian mode of TX FIFO and
 *	- Set the little endian mode of TX FIFO
 *	- Set clock phase
 *	- Set clock polarity and
 *	- Enable the QSPI controller
 */
static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi)
{
	u32 config_reg;
	u32 config_reg, baud_rate_val = 0;
	ulong clk_rate;

	/* Select the GQSPI mode */
	zynqmp_gqspi_write(xqspi, GQSPI_SEL_OFST, GQSPI_SEL_MASK);
@@ -303,21 +390,37 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi)
	config_reg |= GQSPI_CFG_WP_HOLD_MASK;
	/* Clear pre-scalar by default */
	config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK;
	/* CPHA 0 */
	/* Set CPHA */
	if (xqspi->ctlr->mode_bits & SPI_CPHA)
		config_reg |= GQSPI_CFG_CLK_PHA_MASK;
	else
		config_reg &= ~GQSPI_CFG_CLK_PHA_MASK;
	/* CPOL 0 */
	/* Set CPOL */
	if (xqspi->ctlr->mode_bits & SPI_CPOL)
		config_reg |= GQSPI_CFG_CLK_POL_MASK;
	else
		config_reg &= ~GQSPI_CFG_CLK_POL_MASK;

	/* Set the clock frequency */
	clk_rate = clk_get_rate(xqspi->refclk);
	while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) &&
	       (clk_rate /
		(GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > xqspi->speed_hz)
		baud_rate_val++;

	config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK;
	config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT);

	zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);

	/* Set the tapdelay for clock frequency */
	zynqmp_qspi_set_tapdelay(xqspi, baud_rate_val);

	/* Clear the TX and RX FIFO */
	zynqmp_gqspi_write(xqspi, GQSPI_FIFO_CTRL_OFST,
			   GQSPI_FIFO_CTRL_RST_RX_FIFO_MASK |
			   GQSPI_FIFO_CTRL_RST_TX_FIFO_MASK |
			   GQSPI_FIFO_CTRL_RST_GEN_FIFO_MASK);
	/* Set by default to allow for high frequencies */
	zynqmp_gqspi_write(xqspi, GQSPI_LPBK_DLY_ADJ_OFST,
			   zynqmp_gqspi_read(xqspi, GQSPI_LPBK_DLY_ADJ_OFST) |
			   GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK);
	/* Reset thresholds */
	zynqmp_gqspi_write(xqspi, GQSPI_TX_THRESHOLD_OFST,
			   GQSPI_TX_FIFO_THRESHOLD_RESET_VAL);
@@ -455,30 +558,30 @@ static int zynqmp_qspi_config_op(struct zynqmp_qspi *xqspi,
				 struct spi_device *qspi)
{
	ulong clk_rate;
	u32 config_reg, baud_rate_val = 0;
	u32 config_reg, req_speed_hz, baud_rate_val = 0;

	req_speed_hz = qspi->max_speed_hz;

	if (xqspi->speed_hz != req_speed_hz) {
		xqspi->speed_hz = req_speed_hz;

		/* Set the clock frequency */
	/* If req_hz == 0, default to lowest speed */
		/* If req_speed_hz == 0, default to lowest speed */
		clk_rate = clk_get_rate(xqspi->refclk);

		while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) &&
		       (clk_rate /
		(GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > qspi->max_speed_hz)
			(GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) >
		       req_speed_hz)
			baud_rate_val++;

		config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);

	/* Set the QSPI clock phase and clock polarity */
	config_reg &= (~GQSPI_CFG_CLK_PHA_MASK) & (~GQSPI_CFG_CLK_POL_MASK);

	if (qspi->mode & SPI_CPHA)
		config_reg |= GQSPI_CFG_CLK_PHA_MASK;
	if (qspi->mode & SPI_CPOL)
		config_reg |= GQSPI_CFG_CLK_POL_MASK;

		config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK;
		config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT);
		zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
		zynqmp_qspi_set_tapdelay(xqspi, baud_rate_val);
	}
	return 0;
}

@@ -739,6 +842,8 @@ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id)
/**
 * zynqmp_qspi_setuprxdma - This function sets up the RX DMA operation
 * @xqspi:	xqspi is a pointer to the GQSPI instance.
 *
 * Return:	0 on success; error value otherwise.
 */
static int zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi)
{
@@ -823,6 +928,8 @@ static void zynqmp_qspi_write_op(struct zynqmp_qspi *xqspi, u8 tx_nbits,
 * @rx_nbits:	Receive buswidth.
 * @genfifoentry:	genfifoentry is pointer to the variable in which
 *			GENFIFO	mask is returned to calling function
 *
 * Return:	0 on success; error value otherwise.
 */
static int zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits,
				u32 genfifoentry)
@@ -1087,6 +1194,16 @@ 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,
};
@@ -1107,6 +1224,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	u32 num_cs;
	const struct qspi_platform_data *p_data;

	ctlr = spi_alloc_master(&pdev->dev, sizeof(*xqspi));
	if (!ctlr)
@@ -1117,6 +1235,10 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
	xqspi->ctlr = ctlr;
	platform_set_drvdata(pdev, xqspi);

	p_data = of_device_get_match_data(&pdev->dev);
	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);
@@ -1164,6 +1286,11 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
		goto clk_dis_all;
	}

	ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
		SPI_TX_DUAL | SPI_TX_QUAD;
	ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
	xqspi->speed_hz = ctlr->max_speed_hz;

	/* QSPI controller initializations */
	zynqmp_qspi_init_hw(xqspi);

@@ -1199,10 +1326,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
	ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
	ctlr->mem_ops = &zynqmp_qspi_mem_ops;
	ctlr->setup = zynqmp_qspi_setup_op;
	ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
	ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
	ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
			    SPI_TX_DUAL | SPI_TX_QUAD;
	ctlr->dev.of_node = np;
	ctlr->auto_runtime_pm = true;

@@ -1253,11 +1377,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 = {
+19 −0
Original line number Diff line number Diff line
@@ -135,6 +135,7 @@ enum pm_ret_status {
};

enum pm_ioctl_id {
	IOCTL_SET_TAPDELAY_BYPASS = 4,
	IOCTL_SD_DLL_RESET = 6,
	IOCTL_SET_SD_TAPDELAY = 7,
	IOCTL_SET_PLL_FRAC_MODE = 8,
@@ -389,6 +390,18 @@ enum zynqmp_pm_shutdown_subtype {
	ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM = 2,
};

enum tap_delay_signal_type {
	PM_TAPDELAY_NAND_DQS_IN = 0,
	PM_TAPDELAY_NAND_DQS_OUT = 1,
	PM_TAPDELAY_QSPI = 2,
	PM_TAPDELAY_MAX = 3,
};

enum tap_delay_bypass_ctrl {
	PM_TAPDELAY_BYPASS_DISABLE = 0,
	PM_TAPDELAY_BYPASS_ENABLE = 1,
};

enum ospi_mux_select_type {
	PM_OSPI_MUX_SEL_DMA = 0,
	PM_OSPI_MUX_SEL_LINEAR = 1,
@@ -484,6 +497,7 @@ int zynqmp_pm_write_ggs(u32 index, u32 value);
int zynqmp_pm_read_ggs(u32 index, u32 *value);
int zynqmp_pm_write_pggs(u32 index, u32 value);
int zynqmp_pm_read_pggs(u32 index, u32 *value);
int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value);
int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype);
int zynqmp_pm_set_boot_health_status(u32 value);
int zynqmp_pm_pinctrl_request(const u32 pin);
@@ -696,6 +710,11 @@ static inline int zynqmp_pm_read_pggs(u32 index, u32 *value)
	return -ENODEV;
}

static inline int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value)
{
	return -ENODEV;
}

static inline int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype)
{
	return -ENODEV;