diff options
Diffstat (limited to 'drivers/spi/spi-zynqmp-gqspi.c')
-rw-r--r-- | drivers/spi/spi-zynqmp-gqspi.c | 191 |
1 files changed, 155 insertions, 36 deletions
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index c760aac070e5..95ff15665d44 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 @@ -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; }; /** @@ -250,6 +284,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 */ - config_reg &= ~GQSPI_CFG_CLK_PHA_MASK; - /* CPOL 0 */ - config_reg &= ~GQSPI_CFG_CLK_POL_MASK; + /* Set CPHA */ + if (xqspi->ctlr->mode_bits & SPI_CPHA) + config_reg |= GQSPI_CFG_CLK_PHA_MASK; + else + config_reg &= ~GQSPI_CFG_CLK_PHA_MASK; + /* 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; - /* Set the clock frequency */ - /* If req_hz == 0, default to lowest speed */ - clk_rate = clk_get_rate(xqspi->refclk); + req_speed_hz = qspi->max_speed_hz; - while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) && - (clk_rate / - (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > qspi->max_speed_hz) - baud_rate_val++; + if (xqspi->speed_hz != req_speed_hz) { + xqspi->speed_hz = req_speed_hz; - config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); + /* Set the clock frequency */ + /* If req_speed_hz == 0, default to lowest speed */ + clk_rate = clk_get_rate(xqspi->refclk); - /* Set the QSPI clock phase and clock polarity */ - config_reg &= (~GQSPI_CFG_CLK_PHA_MASK) & (~GQSPI_CFG_CLK_POL_MASK); + while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) && + (clk_rate / + (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > + req_speed_hz) + baud_rate_val++; - 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 = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); - 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); + 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 = { |