167dca5e5SNaga Sureshkumar Relli // SPDX-License-Identifier: GPL-2.0+
267dca5e5SNaga Sureshkumar Relli /*
367dca5e5SNaga Sureshkumar Relli * Copyright (C) 2019 Xilinx, Inc.
467dca5e5SNaga Sureshkumar Relli *
567dca5e5SNaga Sureshkumar Relli * Author: Naga Sureshkumar Relli <nagasure@xilinx.com>
667dca5e5SNaga Sureshkumar Relli */
767dca5e5SNaga Sureshkumar Relli
867dca5e5SNaga Sureshkumar Relli #include <linux/clk.h>
967dca5e5SNaga Sureshkumar Relli #include <linux/delay.h>
1067dca5e5SNaga Sureshkumar Relli #include <linux/interrupt.h>
1167dca5e5SNaga Sureshkumar Relli #include <linux/io.h>
1267dca5e5SNaga Sureshkumar Relli #include <linux/module.h>
1367dca5e5SNaga Sureshkumar Relli #include <linux/of_irq.h>
1467dca5e5SNaga Sureshkumar Relli #include <linux/of_address.h>
1567dca5e5SNaga Sureshkumar Relli #include <linux/platform_device.h>
1667dca5e5SNaga Sureshkumar Relli #include <linux/spi/spi.h>
1767dca5e5SNaga Sureshkumar Relli #include <linux/workqueue.h>
1867dca5e5SNaga Sureshkumar Relli #include <linux/spi/spi-mem.h>
1967dca5e5SNaga Sureshkumar Relli
2067dca5e5SNaga Sureshkumar Relli /* Register offset definitions */
2167dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_OFFSET 0x00 /* Configuration Register, RW */
2267dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_STATUS_OFFSET 0x04 /* Interrupt Status Register, RO */
2367dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IEN_OFFSET 0x08 /* Interrupt Enable Register, WO */
2467dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IDIS_OFFSET 0x0C /* Interrupt Disable Reg, WO */
2567dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IMASK_OFFSET 0x10 /* Interrupt Enabled Mask Reg,RO */
2667dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_ENABLE_OFFSET 0x14 /* Enable/Disable Register, RW */
2767dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_DELAY_OFFSET 0x18 /* Delay Register, RW */
2867dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst, WO */
2967dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst, WO */
3067dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst, WO */
3167dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst, WO */
3267dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_RXD_OFFSET 0x20 /* Data Receive Register, RO */
3367dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_SIC_OFFSET 0x24 /* Slave Idle Count Register, RW */
3467dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_TX_THRESH_OFFSET 0x28 /* TX FIFO Watermark Reg, RW */
3567dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_RX_THRESH_OFFSET 0x2C /* RX FIFO Watermark Reg, RW */
3667dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_GPIO_OFFSET 0x30 /* GPIO Register, RW */
3767dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_LINEAR_CFG_OFFSET 0xA0 /* Linear Adapter Config Ref, RW */
3867dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_MOD_ID_OFFSET 0xFC /* Module ID Register, RO */
3967dca5e5SNaga Sureshkumar Relli
4067dca5e5SNaga Sureshkumar Relli /*
4167dca5e5SNaga Sureshkumar Relli * QSPI Configuration Register bit Masks
4267dca5e5SNaga Sureshkumar Relli *
4367dca5e5SNaga Sureshkumar Relli * This register contains various control bits that effect the operation
4467dca5e5SNaga Sureshkumar Relli * of the QSPI controller
4567dca5e5SNaga Sureshkumar Relli */
4667dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_IFMODE_MASK BIT(31) /* Flash Memory Interface */
4767dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_MANSRT_MASK BIT(16) /* Manual TX Start */
4867dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_MANSRTEN_MASK BIT(15) /* Enable Manual TX Mode */
4967dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_SSFORCE_MASK BIT(14) /* Manual Chip Select */
5067dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_BDRATE_MASK GENMASK(5, 3) /* Baud Rate Mask */
5167dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_CPHA_MASK BIT(2) /* Clock Phase Control */
5267dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_CPOL_MASK BIT(1) /* Clock Polarity Control */
5367dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_FWIDTH_MASK GENMASK(7, 6) /* FIFO width */
5467dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_CONFIG_MSTREN_MASK BIT(0) /* Master Mode */
5567dca5e5SNaga Sureshkumar Relli
5667dca5e5SNaga Sureshkumar Relli /*
5767dca5e5SNaga Sureshkumar Relli * QSPI Configuration Register - Baud rate and slave select
5867dca5e5SNaga Sureshkumar Relli *
5967dca5e5SNaga Sureshkumar Relli * These are the values used in the calculation of baud rate divisor and
6067dca5e5SNaga Sureshkumar Relli * setting the slave select.
6167dca5e5SNaga Sureshkumar Relli */
62941be723SMiquel Raynal #define ZYNQ_QSPI_CONFIG_BAUD_DIV_MAX GENMASK(2, 0) /* Baud rate maximum */
63941be723SMiquel Raynal #define ZYNQ_QSPI_CONFIG_BAUD_DIV_SHIFT 3 /* Baud rate divisor shift */
64dffaf743SMiquel Raynal #define ZYNQ_QSPI_CONFIG_PCS BIT(10) /* Peripheral Chip Select */
6567dca5e5SNaga Sureshkumar Relli
6667dca5e5SNaga Sureshkumar Relli /*
6767dca5e5SNaga Sureshkumar Relli * QSPI Interrupt Registers bit Masks
6867dca5e5SNaga Sureshkumar Relli *
6967dca5e5SNaga Sureshkumar Relli * All the four interrupt registers (Status/Mask/Enable/Disable) have the same
7067dca5e5SNaga Sureshkumar Relli * bit definitions.
7167dca5e5SNaga Sureshkumar Relli */
7267dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IXR_RX_OVERFLOW_MASK BIT(0) /* QSPI RX FIFO Overflow */
7367dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IXR_TXNFULL_MASK BIT(2) /* QSPI TX FIFO Overflow */
7467dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IXR_TXFULL_MASK BIT(3) /* QSPI TX FIFO is full */
7567dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IXR_RXNEMTY_MASK BIT(4) /* QSPI RX FIFO Not Empty */
7667dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IXR_RXF_FULL_MASK BIT(5) /* QSPI RX FIFO is full */
7767dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IXR_TXF_UNDRFLOW_MASK BIT(6) /* QSPI TX FIFO Underflow */
7867dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IXR_ALL_MASK (ZYNQ_QSPI_IXR_RX_OVERFLOW_MASK | \
7967dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_TXNFULL_MASK | \
8067dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_TXFULL_MASK | \
8167dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXNEMTY_MASK | \
8267dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXF_FULL_MASK | \
8367dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_TXF_UNDRFLOW_MASK)
8467dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_IXR_RXTX_MASK (ZYNQ_QSPI_IXR_TXNFULL_MASK | \
8567dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXNEMTY_MASK)
8667dca5e5SNaga Sureshkumar Relli
8767dca5e5SNaga Sureshkumar Relli /*
8867dca5e5SNaga Sureshkumar Relli * QSPI Enable Register bit Masks
8967dca5e5SNaga Sureshkumar Relli *
9067dca5e5SNaga Sureshkumar Relli * This register is used to enable or disable the QSPI controller
9167dca5e5SNaga Sureshkumar Relli */
9267dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_ENABLE_ENABLE_MASK BIT(0) /* QSPI Enable Bit Mask */
9367dca5e5SNaga Sureshkumar Relli
9467dca5e5SNaga Sureshkumar Relli /*
9567dca5e5SNaga Sureshkumar Relli * QSPI Linear Configuration Register
9667dca5e5SNaga Sureshkumar Relli *
9767dca5e5SNaga Sureshkumar Relli * It is named Linear Configuration but it controls other modes when not in
9867dca5e5SNaga Sureshkumar Relli * linear mode also.
9967dca5e5SNaga Sureshkumar Relli */
100044ac826SMiquel Raynal #define ZYNQ_QSPI_LCFG_TWO_MEM BIT(30) /* LQSPI Two memories */
101044ac826SMiquel Raynal #define ZYNQ_QSPI_LCFG_SEP_BUS BIT(29) /* LQSPI Separate bus */
102044ac826SMiquel Raynal #define ZYNQ_QSPI_LCFG_U_PAGE BIT(28) /* LQSPI Upper Page */
10367dca5e5SNaga Sureshkumar Relli
10467dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_LCFG_DUMMY_SHIFT 8
10567dca5e5SNaga Sureshkumar Relli
10667dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_FAST_READ_QOUT_CODE 0x6B /* read instruction code */
10767dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_FIFO_DEPTH 63 /* FIFO depth in words */
10867dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_RX_THRESHOLD 32 /* Rx FIFO threshold level */
10967dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_TX_THRESHOLD 1 /* Tx FIFO threshold level */
11067dca5e5SNaga Sureshkumar Relli
11167dca5e5SNaga Sureshkumar Relli /*
11267dca5e5SNaga Sureshkumar Relli * The modebits configurable by the driver to make the SPI support different
11367dca5e5SNaga Sureshkumar Relli * data formats
11467dca5e5SNaga Sureshkumar Relli */
11567dca5e5SNaga Sureshkumar Relli #define ZYNQ_QSPI_MODEBITS (SPI_CPOL | SPI_CPHA)
11667dca5e5SNaga Sureshkumar Relli
117d575c9b7SMiquel Raynal /* Maximum number of chip selects */
118d575c9b7SMiquel Raynal #define ZYNQ_QSPI_MAX_NUM_CS 2
11967dca5e5SNaga Sureshkumar Relli
12067dca5e5SNaga Sureshkumar Relli /**
12167dca5e5SNaga Sureshkumar Relli * struct zynq_qspi - Defines qspi driver instance
122e867feecSLee Jones * @dev: Pointer to the this device's information
12367dca5e5SNaga Sureshkumar Relli * @regs: Virtual address of the QSPI controller registers
12467dca5e5SNaga Sureshkumar Relli * @refclk: Pointer to the peripheral clock
12567dca5e5SNaga Sureshkumar Relli * @pclk: Pointer to the APB clock
12667dca5e5SNaga Sureshkumar Relli * @irq: IRQ number
12767dca5e5SNaga Sureshkumar Relli * @txbuf: Pointer to the TX buffer
12867dca5e5SNaga Sureshkumar Relli * @rxbuf: Pointer to the RX buffer
12967dca5e5SNaga Sureshkumar Relli * @tx_bytes: Number of bytes left to transfer
13067dca5e5SNaga Sureshkumar Relli * @rx_bytes: Number of bytes left to receive
13167dca5e5SNaga Sureshkumar Relli * @data_completion: completion structure
13267dca5e5SNaga Sureshkumar Relli */
13367dca5e5SNaga Sureshkumar Relli struct zynq_qspi {
13467dca5e5SNaga Sureshkumar Relli struct device *dev;
13567dca5e5SNaga Sureshkumar Relli void __iomem *regs;
13667dca5e5SNaga Sureshkumar Relli struct clk *refclk;
13767dca5e5SNaga Sureshkumar Relli struct clk *pclk;
13867dca5e5SNaga Sureshkumar Relli int irq;
13967dca5e5SNaga Sureshkumar Relli u8 *txbuf;
14067dca5e5SNaga Sureshkumar Relli u8 *rxbuf;
14167dca5e5SNaga Sureshkumar Relli int tx_bytes;
14267dca5e5SNaga Sureshkumar Relli int rx_bytes;
14367dca5e5SNaga Sureshkumar Relli struct completion data_completion;
14467dca5e5SNaga Sureshkumar Relli };
14567dca5e5SNaga Sureshkumar Relli
14667dca5e5SNaga Sureshkumar Relli /*
14767dca5e5SNaga Sureshkumar Relli * Inline functions for the QSPI controller read/write
14867dca5e5SNaga Sureshkumar Relli */
zynq_qspi_read(struct zynq_qspi * xqspi,u32 offset)14967dca5e5SNaga Sureshkumar Relli static inline u32 zynq_qspi_read(struct zynq_qspi *xqspi, u32 offset)
15067dca5e5SNaga Sureshkumar Relli {
15167dca5e5SNaga Sureshkumar Relli return readl_relaxed(xqspi->regs + offset);
15267dca5e5SNaga Sureshkumar Relli }
15367dca5e5SNaga Sureshkumar Relli
zynq_qspi_write(struct zynq_qspi * xqspi,u32 offset,u32 val)15467dca5e5SNaga Sureshkumar Relli static inline void zynq_qspi_write(struct zynq_qspi *xqspi, u32 offset,
15567dca5e5SNaga Sureshkumar Relli u32 val)
15667dca5e5SNaga Sureshkumar Relli {
15767dca5e5SNaga Sureshkumar Relli writel_relaxed(val, xqspi->regs + offset);
15867dca5e5SNaga Sureshkumar Relli }
15967dca5e5SNaga Sureshkumar Relli
16067dca5e5SNaga Sureshkumar Relli /**
16167dca5e5SNaga Sureshkumar Relli * zynq_qspi_init_hw - Initialize the hardware
16267dca5e5SNaga Sureshkumar Relli * @xqspi: Pointer to the zynq_qspi structure
163d575c9b7SMiquel Raynal * @num_cs: Number of connected CS (to enable dual memories if needed)
16467dca5e5SNaga Sureshkumar Relli *
16567dca5e5SNaga Sureshkumar Relli * The default settings of the QSPI controller's configurable parameters on
16667dca5e5SNaga Sureshkumar Relli * reset are
16767dca5e5SNaga Sureshkumar Relli * - Master mode
16867dca5e5SNaga Sureshkumar Relli * - Baud rate divisor is set to 2
16967dca5e5SNaga Sureshkumar Relli * - Tx threshold set to 1l Rx threshold set to 32
17067dca5e5SNaga Sureshkumar Relli * - Flash memory interface mode enabled
17167dca5e5SNaga Sureshkumar Relli * - Size of the word to be transferred as 8 bit
17267dca5e5SNaga Sureshkumar Relli * This function performs the following actions
17367dca5e5SNaga Sureshkumar Relli * - Disable and clear all the interrupts
17467dca5e5SNaga Sureshkumar Relli * - Enable manual slave select
17567dca5e5SNaga Sureshkumar Relli * - Enable manual start
17667dca5e5SNaga Sureshkumar Relli * - Deselect all the chip select lines
17767dca5e5SNaga Sureshkumar Relli * - Set the size of the word to be transferred as 32 bit
17867dca5e5SNaga Sureshkumar Relli * - Set the little endian mode of TX FIFO and
17967dca5e5SNaga Sureshkumar Relli * - Enable the QSPI controller
18067dca5e5SNaga Sureshkumar Relli */
zynq_qspi_init_hw(struct zynq_qspi * xqspi,unsigned int num_cs)181d575c9b7SMiquel Raynal static void zynq_qspi_init_hw(struct zynq_qspi *xqspi, unsigned int num_cs)
18267dca5e5SNaga Sureshkumar Relli {
18367dca5e5SNaga Sureshkumar Relli u32 config_reg;
18467dca5e5SNaga Sureshkumar Relli
18567dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0);
18667dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_IDIS_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK);
18767dca5e5SNaga Sureshkumar Relli
18867dca5e5SNaga Sureshkumar Relli /* Disable linear mode as the boot loader may have used it */
189d575c9b7SMiquel Raynal config_reg = 0;
190d575c9b7SMiquel Raynal /* At the same time, enable dual mode if more than 1 CS is available */
191d575c9b7SMiquel Raynal if (num_cs > 1)
192d575c9b7SMiquel Raynal config_reg |= ZYNQ_QSPI_LCFG_TWO_MEM;
193d575c9b7SMiquel Raynal
194d575c9b7SMiquel Raynal zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, config_reg);
19567dca5e5SNaga Sureshkumar Relli
19667dca5e5SNaga Sureshkumar Relli /* Clear the RX FIFO */
19767dca5e5SNaga Sureshkumar Relli while (zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET) &
19867dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXNEMTY_MASK)
19967dca5e5SNaga Sureshkumar Relli zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
20067dca5e5SNaga Sureshkumar Relli
20167dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK);
20267dca5e5SNaga Sureshkumar Relli config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
20367dca5e5SNaga Sureshkumar Relli config_reg &= ~(ZYNQ_QSPI_CONFIG_MSTREN_MASK |
20467dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_CPOL_MASK |
20567dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_CPHA_MASK |
20667dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_BDRATE_MASK |
20767dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_SSFORCE_MASK |
20867dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_MANSRTEN_MASK |
20967dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_MANSRT_MASK);
21067dca5e5SNaga Sureshkumar Relli config_reg |= (ZYNQ_QSPI_CONFIG_MSTREN_MASK |
21167dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_SSFORCE_MASK |
21267dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_FWIDTH_MASK |
21367dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_CONFIG_IFMODE_MASK);
21467dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
21567dca5e5SNaga Sureshkumar Relli
21667dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_RX_THRESH_OFFSET,
21767dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_RX_THRESHOLD);
21867dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_TX_THRESH_OFFSET,
21967dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_TX_THRESHOLD);
22067dca5e5SNaga Sureshkumar Relli
22167dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET,
22267dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_ENABLE_ENABLE_MASK);
22367dca5e5SNaga Sureshkumar Relli }
22467dca5e5SNaga Sureshkumar Relli
zynq_qspi_supports_op(struct spi_mem * mem,const struct spi_mem_op * op)22567dca5e5SNaga Sureshkumar Relli static bool zynq_qspi_supports_op(struct spi_mem *mem,
22667dca5e5SNaga Sureshkumar Relli const struct spi_mem_op *op)
22767dca5e5SNaga Sureshkumar Relli {
22867dca5e5SNaga Sureshkumar Relli if (!spi_mem_default_supports_op(mem, op))
22967dca5e5SNaga Sureshkumar Relli return false;
23067dca5e5SNaga Sureshkumar Relli
23167dca5e5SNaga Sureshkumar Relli /*
23267dca5e5SNaga Sureshkumar Relli * The number of address bytes should be equal to or less than 3 bytes.
23367dca5e5SNaga Sureshkumar Relli */
23467dca5e5SNaga Sureshkumar Relli if (op->addr.nbytes > 3)
23567dca5e5SNaga Sureshkumar Relli return false;
23667dca5e5SNaga Sureshkumar Relli
23767dca5e5SNaga Sureshkumar Relli return true;
23867dca5e5SNaga Sureshkumar Relli }
23967dca5e5SNaga Sureshkumar Relli
24067dca5e5SNaga Sureshkumar Relli /**
24167dca5e5SNaga Sureshkumar Relli * zynq_qspi_rxfifo_op - Read 1..4 bytes from RxFIFO to RX buffer
24267dca5e5SNaga Sureshkumar Relli * @xqspi: Pointer to the zynq_qspi structure
24367dca5e5SNaga Sureshkumar Relli * @size: Number of bytes to be read (1..4)
24467dca5e5SNaga Sureshkumar Relli */
zynq_qspi_rxfifo_op(struct zynq_qspi * xqspi,unsigned int size)24567dca5e5SNaga Sureshkumar Relli static void zynq_qspi_rxfifo_op(struct zynq_qspi *xqspi, unsigned int size)
24667dca5e5SNaga Sureshkumar Relli {
24767dca5e5SNaga Sureshkumar Relli u32 data;
24867dca5e5SNaga Sureshkumar Relli
24967dca5e5SNaga Sureshkumar Relli data = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
25067dca5e5SNaga Sureshkumar Relli
25167dca5e5SNaga Sureshkumar Relli if (xqspi->rxbuf) {
25267dca5e5SNaga Sureshkumar Relli memcpy(xqspi->rxbuf, ((u8 *)&data) + 4 - size, size);
25367dca5e5SNaga Sureshkumar Relli xqspi->rxbuf += size;
25467dca5e5SNaga Sureshkumar Relli }
25567dca5e5SNaga Sureshkumar Relli
25667dca5e5SNaga Sureshkumar Relli xqspi->rx_bytes -= size;
25767dca5e5SNaga Sureshkumar Relli if (xqspi->rx_bytes < 0)
25867dca5e5SNaga Sureshkumar Relli xqspi->rx_bytes = 0;
25967dca5e5SNaga Sureshkumar Relli }
26067dca5e5SNaga Sureshkumar Relli
26167dca5e5SNaga Sureshkumar Relli /**
26267dca5e5SNaga Sureshkumar Relli * zynq_qspi_txfifo_op - Write 1..4 bytes from TX buffer to TxFIFO
26367dca5e5SNaga Sureshkumar Relli * @xqspi: Pointer to the zynq_qspi structure
26467dca5e5SNaga Sureshkumar Relli * @size: Number of bytes to be written (1..4)
26567dca5e5SNaga Sureshkumar Relli */
zynq_qspi_txfifo_op(struct zynq_qspi * xqspi,unsigned int size)26667dca5e5SNaga Sureshkumar Relli static void zynq_qspi_txfifo_op(struct zynq_qspi *xqspi, unsigned int size)
26767dca5e5SNaga Sureshkumar Relli {
26867dca5e5SNaga Sureshkumar Relli static const unsigned int offset[4] = {
26967dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_TXD_00_01_OFFSET, ZYNQ_QSPI_TXD_00_10_OFFSET,
27067dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_TXD_00_11_OFFSET, ZYNQ_QSPI_TXD_00_00_OFFSET };
27167dca5e5SNaga Sureshkumar Relli u32 data;
27267dca5e5SNaga Sureshkumar Relli
27367dca5e5SNaga Sureshkumar Relli if (xqspi->txbuf) {
27467dca5e5SNaga Sureshkumar Relli data = 0xffffffff;
27567dca5e5SNaga Sureshkumar Relli memcpy(&data, xqspi->txbuf, size);
27667dca5e5SNaga Sureshkumar Relli xqspi->txbuf += size;
27767dca5e5SNaga Sureshkumar Relli } else {
27867dca5e5SNaga Sureshkumar Relli data = 0;
27967dca5e5SNaga Sureshkumar Relli }
28067dca5e5SNaga Sureshkumar Relli
28167dca5e5SNaga Sureshkumar Relli xqspi->tx_bytes -= size;
28267dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, offset[size - 1], data);
28367dca5e5SNaga Sureshkumar Relli }
28467dca5e5SNaga Sureshkumar Relli
28567dca5e5SNaga Sureshkumar Relli /**
28667dca5e5SNaga Sureshkumar Relli * zynq_qspi_chipselect - Select or deselect the chip select line
28767dca5e5SNaga Sureshkumar Relli * @spi: Pointer to the spi_device structure
28867dca5e5SNaga Sureshkumar Relli * @assert: 1 for select or 0 for deselect the chip select line
28967dca5e5SNaga Sureshkumar Relli */
zynq_qspi_chipselect(struct spi_device * spi,bool assert)29067dca5e5SNaga Sureshkumar Relli static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
29167dca5e5SNaga Sureshkumar Relli {
2929b10fa36SMiquel Raynal struct spi_controller *ctlr = spi->master;
2939b10fa36SMiquel Raynal struct zynq_qspi *xqspi = spi_controller_get_devdata(ctlr);
29467dca5e5SNaga Sureshkumar Relli u32 config_reg;
29567dca5e5SNaga Sureshkumar Relli
296d575c9b7SMiquel Raynal /* Select the lower (CS0) or upper (CS1) memory */
297d575c9b7SMiquel Raynal if (ctlr->num_chipselect > 1) {
298d575c9b7SMiquel Raynal config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET);
2999e264f3fSAmit Kumar Mahapatra via Alsa-devel if (!spi_get_chipselect(spi, 0))
300d575c9b7SMiquel Raynal config_reg &= ~ZYNQ_QSPI_LCFG_U_PAGE;
301d575c9b7SMiquel Raynal else
302d575c9b7SMiquel Raynal config_reg |= ZYNQ_QSPI_LCFG_U_PAGE;
303d575c9b7SMiquel Raynal
304d575c9b7SMiquel Raynal zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, config_reg);
305d575c9b7SMiquel Raynal }
306d575c9b7SMiquel Raynal
307dffaf743SMiquel Raynal /* Ground the line to assert the CS */
30867dca5e5SNaga Sureshkumar Relli config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
309dffaf743SMiquel Raynal if (assert)
310dffaf743SMiquel Raynal config_reg &= ~ZYNQ_QSPI_CONFIG_PCS;
311dffaf743SMiquel Raynal else
312dffaf743SMiquel Raynal config_reg |= ZYNQ_QSPI_CONFIG_PCS;
31367dca5e5SNaga Sureshkumar Relli
31467dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
31567dca5e5SNaga Sureshkumar Relli }
31667dca5e5SNaga Sureshkumar Relli
31767dca5e5SNaga Sureshkumar Relli /**
31867dca5e5SNaga Sureshkumar Relli * zynq_qspi_config_op - Configure QSPI controller for specified transfer
31967dca5e5SNaga Sureshkumar Relli * @xqspi: Pointer to the zynq_qspi structure
320e867feecSLee Jones * @spi: Pointer to the spi_device structure
32167dca5e5SNaga Sureshkumar Relli *
32267dca5e5SNaga Sureshkumar Relli * Sets the operational mode of QSPI controller for the next QSPI transfer and
32367dca5e5SNaga Sureshkumar Relli * sets the requested clock frequency.
32467dca5e5SNaga Sureshkumar Relli *
32567dca5e5SNaga Sureshkumar Relli * Return: 0 on success and -EINVAL on invalid input parameter
32667dca5e5SNaga Sureshkumar Relli *
32767dca5e5SNaga Sureshkumar Relli * Note: If the requested frequency is not an exact match with what can be
32867dca5e5SNaga Sureshkumar Relli * obtained using the prescalar value, the driver sets the clock frequency which
32967dca5e5SNaga Sureshkumar Relli * is lower than the requested frequency (maximum lower) for the transfer. If
33067dca5e5SNaga Sureshkumar Relli * the requested frequency is higher or lower than that is supported by the QSPI
33167dca5e5SNaga Sureshkumar Relli * controller the driver will set the highest or lowest frequency supported by
33267dca5e5SNaga Sureshkumar Relli * controller.
33367dca5e5SNaga Sureshkumar Relli */
zynq_qspi_config_op(struct zynq_qspi * xqspi,struct spi_device * spi)33467dca5e5SNaga Sureshkumar Relli static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi)
33567dca5e5SNaga Sureshkumar Relli {
33667dca5e5SNaga Sureshkumar Relli u32 config_reg, baud_rate_val = 0;
33767dca5e5SNaga Sureshkumar Relli
33867dca5e5SNaga Sureshkumar Relli /*
33967dca5e5SNaga Sureshkumar Relli * Set the clock frequency
34067dca5e5SNaga Sureshkumar Relli * The baud rate divisor is not a direct mapping to the value written
34167dca5e5SNaga Sureshkumar Relli * into the configuration register (config_reg[5:3])
34267dca5e5SNaga Sureshkumar Relli * i.e. 000 - divide by 2
34367dca5e5SNaga Sureshkumar Relli * 001 - divide by 4
34467dca5e5SNaga Sureshkumar Relli * ----------------
34567dca5e5SNaga Sureshkumar Relli * 111 - divide by 256
34667dca5e5SNaga Sureshkumar Relli */
347941be723SMiquel Raynal while ((baud_rate_val < ZYNQ_QSPI_CONFIG_BAUD_DIV_MAX) &&
34867dca5e5SNaga Sureshkumar Relli (clk_get_rate(xqspi->refclk) / (2 << baud_rate_val)) >
34967dca5e5SNaga Sureshkumar Relli spi->max_speed_hz)
35067dca5e5SNaga Sureshkumar Relli baud_rate_val++;
35167dca5e5SNaga Sureshkumar Relli
35267dca5e5SNaga Sureshkumar Relli config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
35367dca5e5SNaga Sureshkumar Relli
35467dca5e5SNaga Sureshkumar Relli /* Set the QSPI clock phase and clock polarity */
35567dca5e5SNaga Sureshkumar Relli config_reg &= (~ZYNQ_QSPI_CONFIG_CPHA_MASK) &
35667dca5e5SNaga Sureshkumar Relli (~ZYNQ_QSPI_CONFIG_CPOL_MASK);
35767dca5e5SNaga Sureshkumar Relli if (spi->mode & SPI_CPHA)
35867dca5e5SNaga Sureshkumar Relli config_reg |= ZYNQ_QSPI_CONFIG_CPHA_MASK;
35967dca5e5SNaga Sureshkumar Relli if (spi->mode & SPI_CPOL)
36067dca5e5SNaga Sureshkumar Relli config_reg |= ZYNQ_QSPI_CONFIG_CPOL_MASK;
36167dca5e5SNaga Sureshkumar Relli
36267dca5e5SNaga Sureshkumar Relli config_reg &= ~ZYNQ_QSPI_CONFIG_BDRATE_MASK;
363941be723SMiquel Raynal config_reg |= (baud_rate_val << ZYNQ_QSPI_CONFIG_BAUD_DIV_SHIFT);
36467dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
36567dca5e5SNaga Sureshkumar Relli
36667dca5e5SNaga Sureshkumar Relli return 0;
36767dca5e5SNaga Sureshkumar Relli }
36867dca5e5SNaga Sureshkumar Relli
36967dca5e5SNaga Sureshkumar Relli /**
370121271f0SAmit Kumar Mahapatra * zynq_qspi_setup_op - Configure the QSPI controller
37167dca5e5SNaga Sureshkumar Relli * @spi: Pointer to the spi_device structure
37267dca5e5SNaga Sureshkumar Relli *
37367dca5e5SNaga Sureshkumar Relli * Sets the operational mode of QSPI controller for the next QSPI transfer, baud
37467dca5e5SNaga Sureshkumar Relli * rate and divisor value to setup the requested qspi clock.
37567dca5e5SNaga Sureshkumar Relli *
37667dca5e5SNaga Sureshkumar Relli * Return: 0 on success and error value on failure
37767dca5e5SNaga Sureshkumar Relli */
zynq_qspi_setup_op(struct spi_device * spi)37867dca5e5SNaga Sureshkumar Relli static int zynq_qspi_setup_op(struct spi_device *spi)
37967dca5e5SNaga Sureshkumar Relli {
3809b10fa36SMiquel Raynal struct spi_controller *ctlr = spi->master;
3819b10fa36SMiquel Raynal struct zynq_qspi *qspi = spi_controller_get_devdata(ctlr);
382*6bb87d88SMingwei Zheng int ret;
38367dca5e5SNaga Sureshkumar Relli
3849b10fa36SMiquel Raynal if (ctlr->busy)
38567dca5e5SNaga Sureshkumar Relli return -EBUSY;
38667dca5e5SNaga Sureshkumar Relli
387*6bb87d88SMingwei Zheng ret = clk_enable(qspi->refclk);
388*6bb87d88SMingwei Zheng if (ret)
389*6bb87d88SMingwei Zheng return ret;
390*6bb87d88SMingwei Zheng
391*6bb87d88SMingwei Zheng ret = clk_enable(qspi->pclk);
392*6bb87d88SMingwei Zheng if (ret) {
393*6bb87d88SMingwei Zheng clk_disable(qspi->refclk);
394*6bb87d88SMingwei Zheng return ret;
395*6bb87d88SMingwei Zheng }
396*6bb87d88SMingwei Zheng
39767dca5e5SNaga Sureshkumar Relli zynq_qspi_write(qspi, ZYNQ_QSPI_ENABLE_OFFSET,
39867dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_ENABLE_ENABLE_MASK);
39967dca5e5SNaga Sureshkumar Relli
40067dca5e5SNaga Sureshkumar Relli return 0;
40167dca5e5SNaga Sureshkumar Relli }
40267dca5e5SNaga Sureshkumar Relli
40367dca5e5SNaga Sureshkumar Relli /**
40467dca5e5SNaga Sureshkumar Relli * zynq_qspi_write_op - Fills the TX FIFO with as many bytes as possible
40567dca5e5SNaga Sureshkumar Relli * @xqspi: Pointer to the zynq_qspi structure
40667dca5e5SNaga Sureshkumar Relli * @txcount: Maximum number of words to write
40767dca5e5SNaga Sureshkumar Relli * @txempty: Indicates that TxFIFO is empty
40867dca5e5SNaga Sureshkumar Relli */
zynq_qspi_write_op(struct zynq_qspi * xqspi,int txcount,bool txempty)40967dca5e5SNaga Sureshkumar Relli static void zynq_qspi_write_op(struct zynq_qspi *xqspi, int txcount,
41067dca5e5SNaga Sureshkumar Relli bool txempty)
41167dca5e5SNaga Sureshkumar Relli {
41267dca5e5SNaga Sureshkumar Relli int count, len, k;
41367dca5e5SNaga Sureshkumar Relli
41467dca5e5SNaga Sureshkumar Relli len = xqspi->tx_bytes;
41567dca5e5SNaga Sureshkumar Relli if (len && len < 4) {
41667dca5e5SNaga Sureshkumar Relli /*
41767dca5e5SNaga Sureshkumar Relli * We must empty the TxFIFO between accesses to TXD0,
41867dca5e5SNaga Sureshkumar Relli * TXD1, TXD2, TXD3.
41967dca5e5SNaga Sureshkumar Relli */
42067dca5e5SNaga Sureshkumar Relli if (txempty)
42167dca5e5SNaga Sureshkumar Relli zynq_qspi_txfifo_op(xqspi, len);
42267dca5e5SNaga Sureshkumar Relli
42367dca5e5SNaga Sureshkumar Relli return;
42467dca5e5SNaga Sureshkumar Relli }
42567dca5e5SNaga Sureshkumar Relli
42667dca5e5SNaga Sureshkumar Relli count = len / 4;
42767dca5e5SNaga Sureshkumar Relli if (count > txcount)
42867dca5e5SNaga Sureshkumar Relli count = txcount;
42967dca5e5SNaga Sureshkumar Relli
43067dca5e5SNaga Sureshkumar Relli if (xqspi->txbuf) {
431ba3ce8cbSNaga Sureshkumar Relli iowrite32_rep(xqspi->regs + ZYNQ_QSPI_TXD_00_00_OFFSET,
43267dca5e5SNaga Sureshkumar Relli xqspi->txbuf, count);
43367dca5e5SNaga Sureshkumar Relli xqspi->txbuf += count * 4;
43467dca5e5SNaga Sureshkumar Relli } else {
43567dca5e5SNaga Sureshkumar Relli for (k = 0; k < count; k++)
43667dca5e5SNaga Sureshkumar Relli writel_relaxed(0, xqspi->regs +
43767dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_TXD_00_00_OFFSET);
43867dca5e5SNaga Sureshkumar Relli }
43967dca5e5SNaga Sureshkumar Relli
44067dca5e5SNaga Sureshkumar Relli xqspi->tx_bytes -= count * 4;
44167dca5e5SNaga Sureshkumar Relli }
44267dca5e5SNaga Sureshkumar Relli
44367dca5e5SNaga Sureshkumar Relli /**
44467dca5e5SNaga Sureshkumar Relli * zynq_qspi_read_op - Drains the RX FIFO by as many bytes as possible
44567dca5e5SNaga Sureshkumar Relli * @xqspi: Pointer to the zynq_qspi structure
44667dca5e5SNaga Sureshkumar Relli * @rxcount: Maximum number of words to read
44767dca5e5SNaga Sureshkumar Relli */
zynq_qspi_read_op(struct zynq_qspi * xqspi,int rxcount)44867dca5e5SNaga Sureshkumar Relli static void zynq_qspi_read_op(struct zynq_qspi *xqspi, int rxcount)
44967dca5e5SNaga Sureshkumar Relli {
45067dca5e5SNaga Sureshkumar Relli int count, len, k;
45167dca5e5SNaga Sureshkumar Relli
45267dca5e5SNaga Sureshkumar Relli len = xqspi->rx_bytes - xqspi->tx_bytes;
45367dca5e5SNaga Sureshkumar Relli count = len / 4;
45467dca5e5SNaga Sureshkumar Relli if (count > rxcount)
45567dca5e5SNaga Sureshkumar Relli count = rxcount;
45667dca5e5SNaga Sureshkumar Relli if (xqspi->rxbuf) {
457ba3ce8cbSNaga Sureshkumar Relli ioread32_rep(xqspi->regs + ZYNQ_QSPI_RXD_OFFSET,
45867dca5e5SNaga Sureshkumar Relli xqspi->rxbuf, count);
45967dca5e5SNaga Sureshkumar Relli xqspi->rxbuf += count * 4;
46067dca5e5SNaga Sureshkumar Relli } else {
46167dca5e5SNaga Sureshkumar Relli for (k = 0; k < count; k++)
46267dca5e5SNaga Sureshkumar Relli readl_relaxed(xqspi->regs + ZYNQ_QSPI_RXD_OFFSET);
46367dca5e5SNaga Sureshkumar Relli }
46467dca5e5SNaga Sureshkumar Relli xqspi->rx_bytes -= count * 4;
46567dca5e5SNaga Sureshkumar Relli len -= count * 4;
46667dca5e5SNaga Sureshkumar Relli
46767dca5e5SNaga Sureshkumar Relli if (len && len < 4 && count < rxcount)
46867dca5e5SNaga Sureshkumar Relli zynq_qspi_rxfifo_op(xqspi, len);
46967dca5e5SNaga Sureshkumar Relli }
47067dca5e5SNaga Sureshkumar Relli
47167dca5e5SNaga Sureshkumar Relli /**
47267dca5e5SNaga Sureshkumar Relli * zynq_qspi_irq - Interrupt service routine of the QSPI controller
47367dca5e5SNaga Sureshkumar Relli * @irq: IRQ number
47467dca5e5SNaga Sureshkumar Relli * @dev_id: Pointer to the xqspi structure
47567dca5e5SNaga Sureshkumar Relli *
47667dca5e5SNaga Sureshkumar Relli * This function handles TX empty only.
47767dca5e5SNaga Sureshkumar Relli * On TX empty interrupt this function reads the received data from RX FIFO and
47867dca5e5SNaga Sureshkumar Relli * fills the TX FIFO if there is any data remaining to be transferred.
47967dca5e5SNaga Sureshkumar Relli *
48067dca5e5SNaga Sureshkumar Relli * Return: IRQ_HANDLED when interrupt is handled; IRQ_NONE otherwise.
48167dca5e5SNaga Sureshkumar Relli */
zynq_qspi_irq(int irq,void * dev_id)48267dca5e5SNaga Sureshkumar Relli static irqreturn_t zynq_qspi_irq(int irq, void *dev_id)
48367dca5e5SNaga Sureshkumar Relli {
48467dca5e5SNaga Sureshkumar Relli u32 intr_status;
48567dca5e5SNaga Sureshkumar Relli bool txempty;
48667dca5e5SNaga Sureshkumar Relli struct zynq_qspi *xqspi = (struct zynq_qspi *)dev_id;
48767dca5e5SNaga Sureshkumar Relli
48867dca5e5SNaga Sureshkumar Relli intr_status = zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET);
48967dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, intr_status);
49067dca5e5SNaga Sureshkumar Relli
49167dca5e5SNaga Sureshkumar Relli if ((intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK) ||
49267dca5e5SNaga Sureshkumar Relli (intr_status & ZYNQ_QSPI_IXR_RXNEMTY_MASK)) {
49367dca5e5SNaga Sureshkumar Relli /*
49467dca5e5SNaga Sureshkumar Relli * This bit is set when Tx FIFO has < THRESHOLD entries.
49567dca5e5SNaga Sureshkumar Relli * We have the THRESHOLD value set to 1,
49667dca5e5SNaga Sureshkumar Relli * so this bit indicates Tx FIFO is empty.
49767dca5e5SNaga Sureshkumar Relli */
49867dca5e5SNaga Sureshkumar Relli txempty = !!(intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK);
49967dca5e5SNaga Sureshkumar Relli /* Read out the data from the RX FIFO */
50067dca5e5SNaga Sureshkumar Relli zynq_qspi_read_op(xqspi, ZYNQ_QSPI_RX_THRESHOLD);
50167dca5e5SNaga Sureshkumar Relli if (xqspi->tx_bytes) {
50267dca5e5SNaga Sureshkumar Relli /* There is more data to send */
50367dca5e5SNaga Sureshkumar Relli zynq_qspi_write_op(xqspi, ZYNQ_QSPI_RX_THRESHOLD,
50467dca5e5SNaga Sureshkumar Relli txempty);
50567dca5e5SNaga Sureshkumar Relli } else {
50667dca5e5SNaga Sureshkumar Relli /*
50767dca5e5SNaga Sureshkumar Relli * If transfer and receive is completed then only send
50867dca5e5SNaga Sureshkumar Relli * complete signal.
50967dca5e5SNaga Sureshkumar Relli */
51067dca5e5SNaga Sureshkumar Relli if (!xqspi->rx_bytes) {
51167dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi,
51267dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IDIS_OFFSET,
51367dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXTX_MASK);
51467dca5e5SNaga Sureshkumar Relli complete(&xqspi->data_completion);
51567dca5e5SNaga Sureshkumar Relli }
51667dca5e5SNaga Sureshkumar Relli }
51767dca5e5SNaga Sureshkumar Relli return IRQ_HANDLED;
51867dca5e5SNaga Sureshkumar Relli }
51967dca5e5SNaga Sureshkumar Relli
52067dca5e5SNaga Sureshkumar Relli return IRQ_NONE;
52167dca5e5SNaga Sureshkumar Relli }
52267dca5e5SNaga Sureshkumar Relli
52367dca5e5SNaga Sureshkumar Relli /**
52467dca5e5SNaga Sureshkumar Relli * zynq_qspi_exec_mem_op() - Initiates the QSPI transfer
52567dca5e5SNaga Sureshkumar Relli * @mem: the SPI memory
52667dca5e5SNaga Sureshkumar Relli * @op: the memory operation to execute
52767dca5e5SNaga Sureshkumar Relli *
52867dca5e5SNaga Sureshkumar Relli * Executes a memory operation.
52967dca5e5SNaga Sureshkumar Relli *
53067dca5e5SNaga Sureshkumar Relli * This function first selects the chip and starts the memory operation.
53167dca5e5SNaga Sureshkumar Relli *
53267dca5e5SNaga Sureshkumar Relli * Return: 0 in case of success, a negative error code otherwise.
53367dca5e5SNaga Sureshkumar Relli */
zynq_qspi_exec_mem_op(struct spi_mem * mem,const struct spi_mem_op * op)53467dca5e5SNaga Sureshkumar Relli static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
53567dca5e5SNaga Sureshkumar Relli const struct spi_mem_op *op)
53667dca5e5SNaga Sureshkumar Relli {
53767dca5e5SNaga Sureshkumar Relli struct zynq_qspi *xqspi = spi_controller_get_devdata(mem->spi->master);
53867dca5e5SNaga Sureshkumar Relli int err = 0, i;
53967dca5e5SNaga Sureshkumar Relli u8 *tmpbuf;
54067dca5e5SNaga Sureshkumar Relli
54167dca5e5SNaga Sureshkumar Relli dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n",
5426d5ff8e6SKaren Dombroski op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
54367dca5e5SNaga Sureshkumar Relli op->dummy.buswidth, op->data.buswidth);
54467dca5e5SNaga Sureshkumar Relli
54567dca5e5SNaga Sureshkumar Relli zynq_qspi_chipselect(mem->spi, true);
54667dca5e5SNaga Sureshkumar Relli zynq_qspi_config_op(xqspi, mem->spi);
54767dca5e5SNaga Sureshkumar Relli
5486d5ff8e6SKaren Dombroski if (op->cmd.opcode) {
54967dca5e5SNaga Sureshkumar Relli reinit_completion(&xqspi->data_completion);
5506d5ff8e6SKaren Dombroski xqspi->txbuf = (u8 *)&op->cmd.opcode;
55167dca5e5SNaga Sureshkumar Relli xqspi->rxbuf = NULL;
552caf72df4SPratyush Yadav xqspi->tx_bytes = op->cmd.nbytes;
553caf72df4SPratyush Yadav xqspi->rx_bytes = op->cmd.nbytes;
55467dca5e5SNaga Sureshkumar Relli zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
55567dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
55667dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXTX_MASK);
55726cfc0dbSQuanyang Wang if (!wait_for_completion_timeout(&xqspi->data_completion,
55867dca5e5SNaga Sureshkumar Relli msecs_to_jiffies(1000)))
55967dca5e5SNaga Sureshkumar Relli err = -ETIMEDOUT;
56067dca5e5SNaga Sureshkumar Relli }
56167dca5e5SNaga Sureshkumar Relli
56267dca5e5SNaga Sureshkumar Relli if (op->addr.nbytes) {
56367dca5e5SNaga Sureshkumar Relli for (i = 0; i < op->addr.nbytes; i++) {
56467dca5e5SNaga Sureshkumar Relli xqspi->txbuf[i] = op->addr.val >>
56567dca5e5SNaga Sureshkumar Relli (8 * (op->addr.nbytes - i - 1));
56667dca5e5SNaga Sureshkumar Relli }
56767dca5e5SNaga Sureshkumar Relli
56867dca5e5SNaga Sureshkumar Relli reinit_completion(&xqspi->data_completion);
56967dca5e5SNaga Sureshkumar Relli xqspi->rxbuf = NULL;
57067dca5e5SNaga Sureshkumar Relli xqspi->tx_bytes = op->addr.nbytes;
57167dca5e5SNaga Sureshkumar Relli xqspi->rx_bytes = op->addr.nbytes;
57267dca5e5SNaga Sureshkumar Relli zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
57367dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
57467dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXTX_MASK);
57526cfc0dbSQuanyang Wang if (!wait_for_completion_timeout(&xqspi->data_completion,
57667dca5e5SNaga Sureshkumar Relli msecs_to_jiffies(1000)))
57767dca5e5SNaga Sureshkumar Relli err = -ETIMEDOUT;
57867dca5e5SNaga Sureshkumar Relli }
57967dca5e5SNaga Sureshkumar Relli
58067dca5e5SNaga Sureshkumar Relli if (op->dummy.nbytes) {
58167dca5e5SNaga Sureshkumar Relli tmpbuf = kzalloc(op->dummy.nbytes, GFP_KERNEL);
582ab382442SZhou Qingyang if (!tmpbuf)
583ab382442SZhou Qingyang return -ENOMEM;
584ab382442SZhou Qingyang
58567dca5e5SNaga Sureshkumar Relli memset(tmpbuf, 0xff, op->dummy.nbytes);
58667dca5e5SNaga Sureshkumar Relli reinit_completion(&xqspi->data_completion);
58767dca5e5SNaga Sureshkumar Relli xqspi->txbuf = tmpbuf;
58867dca5e5SNaga Sureshkumar Relli xqspi->rxbuf = NULL;
58967dca5e5SNaga Sureshkumar Relli xqspi->tx_bytes = op->dummy.nbytes;
59067dca5e5SNaga Sureshkumar Relli xqspi->rx_bytes = op->dummy.nbytes;
59167dca5e5SNaga Sureshkumar Relli zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
59267dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
59367dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXTX_MASK);
59426cfc0dbSQuanyang Wang if (!wait_for_completion_timeout(&xqspi->data_completion,
59567dca5e5SNaga Sureshkumar Relli msecs_to_jiffies(1000)))
59667dca5e5SNaga Sureshkumar Relli err = -ETIMEDOUT;
59767dca5e5SNaga Sureshkumar Relli
59867dca5e5SNaga Sureshkumar Relli kfree(tmpbuf);
59967dca5e5SNaga Sureshkumar Relli }
60067dca5e5SNaga Sureshkumar Relli
60167dca5e5SNaga Sureshkumar Relli if (op->data.nbytes) {
60267dca5e5SNaga Sureshkumar Relli reinit_completion(&xqspi->data_completion);
60367dca5e5SNaga Sureshkumar Relli if (op->data.dir == SPI_MEM_DATA_OUT) {
60467dca5e5SNaga Sureshkumar Relli xqspi->txbuf = (u8 *)op->data.buf.out;
60567dca5e5SNaga Sureshkumar Relli xqspi->tx_bytes = op->data.nbytes;
60667dca5e5SNaga Sureshkumar Relli xqspi->rxbuf = NULL;
60767dca5e5SNaga Sureshkumar Relli xqspi->rx_bytes = op->data.nbytes;
60867dca5e5SNaga Sureshkumar Relli } else {
60967dca5e5SNaga Sureshkumar Relli xqspi->txbuf = NULL;
61067dca5e5SNaga Sureshkumar Relli xqspi->rxbuf = (u8 *)op->data.buf.in;
61167dca5e5SNaga Sureshkumar Relli xqspi->rx_bytes = op->data.nbytes;
61267dca5e5SNaga Sureshkumar Relli xqspi->tx_bytes = op->data.nbytes;
61367dca5e5SNaga Sureshkumar Relli }
61467dca5e5SNaga Sureshkumar Relli
61567dca5e5SNaga Sureshkumar Relli zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
61667dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
61767dca5e5SNaga Sureshkumar Relli ZYNQ_QSPI_IXR_RXTX_MASK);
61826cfc0dbSQuanyang Wang if (!wait_for_completion_timeout(&xqspi->data_completion,
61967dca5e5SNaga Sureshkumar Relli msecs_to_jiffies(1000)))
62067dca5e5SNaga Sureshkumar Relli err = -ETIMEDOUT;
62167dca5e5SNaga Sureshkumar Relli }
62267dca5e5SNaga Sureshkumar Relli zynq_qspi_chipselect(mem->spi, false);
62367dca5e5SNaga Sureshkumar Relli
62467dca5e5SNaga Sureshkumar Relli return err;
62567dca5e5SNaga Sureshkumar Relli }
62667dca5e5SNaga Sureshkumar Relli
62767dca5e5SNaga Sureshkumar Relli static const struct spi_controller_mem_ops zynq_qspi_mem_ops = {
62867dca5e5SNaga Sureshkumar Relli .supports_op = zynq_qspi_supports_op,
62967dca5e5SNaga Sureshkumar Relli .exec_op = zynq_qspi_exec_mem_op,
63067dca5e5SNaga Sureshkumar Relli };
63167dca5e5SNaga Sureshkumar Relli
63267dca5e5SNaga Sureshkumar Relli /**
63367dca5e5SNaga Sureshkumar Relli * zynq_qspi_probe - Probe method for the QSPI driver
63467dca5e5SNaga Sureshkumar Relli * @pdev: Pointer to the platform_device structure
63567dca5e5SNaga Sureshkumar Relli *
63667dca5e5SNaga Sureshkumar Relli * This function initializes the driver data structures and the hardware.
63767dca5e5SNaga Sureshkumar Relli *
63867dca5e5SNaga Sureshkumar Relli * Return: 0 on success and error value on failure
63967dca5e5SNaga Sureshkumar Relli */
zynq_qspi_probe(struct platform_device * pdev)64067dca5e5SNaga Sureshkumar Relli static int zynq_qspi_probe(struct platform_device *pdev)
64167dca5e5SNaga Sureshkumar Relli {
64267dca5e5SNaga Sureshkumar Relli int ret = 0;
64367dca5e5SNaga Sureshkumar Relli struct spi_controller *ctlr;
64467dca5e5SNaga Sureshkumar Relli struct device *dev = &pdev->dev;
64567dca5e5SNaga Sureshkumar Relli struct device_node *np = dev->of_node;
64667dca5e5SNaga Sureshkumar Relli struct zynq_qspi *xqspi;
64767dca5e5SNaga Sureshkumar Relli u32 num_cs;
64867dca5e5SNaga Sureshkumar Relli
64967dca5e5SNaga Sureshkumar Relli ctlr = spi_alloc_master(&pdev->dev, sizeof(*xqspi));
65067dca5e5SNaga Sureshkumar Relli if (!ctlr)
65167dca5e5SNaga Sureshkumar Relli return -ENOMEM;
65267dca5e5SNaga Sureshkumar Relli
65367dca5e5SNaga Sureshkumar Relli xqspi = spi_controller_get_devdata(ctlr);
65467dca5e5SNaga Sureshkumar Relli xqspi->dev = dev;
65567dca5e5SNaga Sureshkumar Relli platform_set_drvdata(pdev, xqspi);
656ae91a439SYueHaibing xqspi->regs = devm_platform_ioremap_resource(pdev, 0);
65767dca5e5SNaga Sureshkumar Relli if (IS_ERR(xqspi->regs)) {
65867dca5e5SNaga Sureshkumar Relli ret = PTR_ERR(xqspi->regs);
65967dca5e5SNaga Sureshkumar Relli goto remove_master;
66067dca5e5SNaga Sureshkumar Relli }
66167dca5e5SNaga Sureshkumar Relli
66267dca5e5SNaga Sureshkumar Relli xqspi->pclk = devm_clk_get(&pdev->dev, "pclk");
66367dca5e5SNaga Sureshkumar Relli if (IS_ERR(xqspi->pclk)) {
66467dca5e5SNaga Sureshkumar Relli dev_err(&pdev->dev, "pclk clock not found.\n");
66567dca5e5SNaga Sureshkumar Relli ret = PTR_ERR(xqspi->pclk);
66667dca5e5SNaga Sureshkumar Relli goto remove_master;
66767dca5e5SNaga Sureshkumar Relli }
66867dca5e5SNaga Sureshkumar Relli
66967dca5e5SNaga Sureshkumar Relli init_completion(&xqspi->data_completion);
67067dca5e5SNaga Sureshkumar Relli
67167dca5e5SNaga Sureshkumar Relli xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk");
67267dca5e5SNaga Sureshkumar Relli if (IS_ERR(xqspi->refclk)) {
67367dca5e5SNaga Sureshkumar Relli dev_err(&pdev->dev, "ref_clk clock not found.\n");
67467dca5e5SNaga Sureshkumar Relli ret = PTR_ERR(xqspi->refclk);
67567dca5e5SNaga Sureshkumar Relli goto remove_master;
67667dca5e5SNaga Sureshkumar Relli }
67767dca5e5SNaga Sureshkumar Relli
67867dca5e5SNaga Sureshkumar Relli ret = clk_prepare_enable(xqspi->pclk);
67967dca5e5SNaga Sureshkumar Relli if (ret) {
68067dca5e5SNaga Sureshkumar Relli dev_err(&pdev->dev, "Unable to enable APB clock.\n");
68167dca5e5SNaga Sureshkumar Relli goto remove_master;
68267dca5e5SNaga Sureshkumar Relli }
68367dca5e5SNaga Sureshkumar Relli
68467dca5e5SNaga Sureshkumar Relli ret = clk_prepare_enable(xqspi->refclk);
68567dca5e5SNaga Sureshkumar Relli if (ret) {
68667dca5e5SNaga Sureshkumar Relli dev_err(&pdev->dev, "Unable to enable device clock.\n");
68767dca5e5SNaga Sureshkumar Relli goto clk_dis_pclk;
68867dca5e5SNaga Sureshkumar Relli }
68967dca5e5SNaga Sureshkumar Relli
69067dca5e5SNaga Sureshkumar Relli xqspi->irq = platform_get_irq(pdev, 0);
6913182d49aSRuan Jinjie if (xqspi->irq < 0) {
6923182d49aSRuan Jinjie ret = xqspi->irq;
693f131767eSzpershuai goto clk_dis_all;
69467dca5e5SNaga Sureshkumar Relli }
69567dca5e5SNaga Sureshkumar Relli ret = devm_request_irq(&pdev->dev, xqspi->irq, zynq_qspi_irq,
69667dca5e5SNaga Sureshkumar Relli 0, pdev->name, xqspi);
69767dca5e5SNaga Sureshkumar Relli if (ret != 0) {
69867dca5e5SNaga Sureshkumar Relli ret = -ENXIO;
69967dca5e5SNaga Sureshkumar Relli dev_err(&pdev->dev, "request_irq failed\n");
700f131767eSzpershuai goto clk_dis_all;
70167dca5e5SNaga Sureshkumar Relli }
70267dca5e5SNaga Sureshkumar Relli
70367dca5e5SNaga Sureshkumar Relli ret = of_property_read_u32(np, "num-cs",
70467dca5e5SNaga Sureshkumar Relli &num_cs);
705087622d0SMiquel Raynal if (ret < 0) {
706d575c9b7SMiquel Raynal ctlr->num_chipselect = 1;
707d575c9b7SMiquel Raynal } else if (num_cs > ZYNQ_QSPI_MAX_NUM_CS) {
708f131767eSzpershuai ret = -EINVAL;
709d575c9b7SMiquel Raynal dev_err(&pdev->dev, "only 2 chip selects are available\n");
710f131767eSzpershuai goto clk_dis_all;
711087622d0SMiquel Raynal } else {
71267dca5e5SNaga Sureshkumar Relli ctlr->num_chipselect = num_cs;
713087622d0SMiquel Raynal }
71467dca5e5SNaga Sureshkumar Relli
71567dca5e5SNaga Sureshkumar Relli ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
71667dca5e5SNaga Sureshkumar Relli SPI_TX_DUAL | SPI_TX_QUAD;
71767dca5e5SNaga Sureshkumar Relli ctlr->mem_ops = &zynq_qspi_mem_ops;
71867dca5e5SNaga Sureshkumar Relli ctlr->setup = zynq_qspi_setup_op;
71967dca5e5SNaga Sureshkumar Relli ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
72067dca5e5SNaga Sureshkumar Relli ctlr->dev.of_node = np;
7218f16292dSMiquel Raynal
7228f16292dSMiquel Raynal /* QSPI controller initializations */
723d575c9b7SMiquel Raynal zynq_qspi_init_hw(xqspi, ctlr->num_chipselect);
7248f16292dSMiquel Raynal
7258eb2fd00SAxel Lin ret = devm_spi_register_controller(&pdev->dev, ctlr);
72667dca5e5SNaga Sureshkumar Relli if (ret) {
72767dca5e5SNaga Sureshkumar Relli dev_err(&pdev->dev, "spi_register_master failed\n");
72867dca5e5SNaga Sureshkumar Relli goto clk_dis_all;
72967dca5e5SNaga Sureshkumar Relli }
73067dca5e5SNaga Sureshkumar Relli
73167dca5e5SNaga Sureshkumar Relli return ret;
73267dca5e5SNaga Sureshkumar Relli
73367dca5e5SNaga Sureshkumar Relli clk_dis_all:
73467dca5e5SNaga Sureshkumar Relli clk_disable_unprepare(xqspi->refclk);
73567dca5e5SNaga Sureshkumar Relli clk_dis_pclk:
73667dca5e5SNaga Sureshkumar Relli clk_disable_unprepare(xqspi->pclk);
73767dca5e5SNaga Sureshkumar Relli remove_master:
73867dca5e5SNaga Sureshkumar Relli spi_controller_put(ctlr);
73967dca5e5SNaga Sureshkumar Relli
74067dca5e5SNaga Sureshkumar Relli return ret;
74167dca5e5SNaga Sureshkumar Relli }
74267dca5e5SNaga Sureshkumar Relli
74367dca5e5SNaga Sureshkumar Relli /**
74467dca5e5SNaga Sureshkumar Relli * zynq_qspi_remove - Remove method for the QSPI driver
74567dca5e5SNaga Sureshkumar Relli * @pdev: Pointer to the platform_device structure
74667dca5e5SNaga Sureshkumar Relli *
74767dca5e5SNaga Sureshkumar Relli * This function is called if a device is physically removed from the system or
74867dca5e5SNaga Sureshkumar Relli * if the driver module is being unloaded. It frees all resources allocated to
74967dca5e5SNaga Sureshkumar Relli * the device.
75067dca5e5SNaga Sureshkumar Relli *
75167dca5e5SNaga Sureshkumar Relli * Return: 0 on success and error value on failure
75267dca5e5SNaga Sureshkumar Relli */
zynq_qspi_remove(struct platform_device * pdev)753ae9084b6SUwe Kleine-König static void zynq_qspi_remove(struct platform_device *pdev)
75467dca5e5SNaga Sureshkumar Relli {
75567dca5e5SNaga Sureshkumar Relli struct zynq_qspi *xqspi = platform_get_drvdata(pdev);
75667dca5e5SNaga Sureshkumar Relli
75767dca5e5SNaga Sureshkumar Relli zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0);
75867dca5e5SNaga Sureshkumar Relli
75967dca5e5SNaga Sureshkumar Relli clk_disable_unprepare(xqspi->refclk);
76067dca5e5SNaga Sureshkumar Relli clk_disable_unprepare(xqspi->pclk);
76167dca5e5SNaga Sureshkumar Relli }
76267dca5e5SNaga Sureshkumar Relli
76367dca5e5SNaga Sureshkumar Relli static const struct of_device_id zynq_qspi_of_match[] = {
76467dca5e5SNaga Sureshkumar Relli { .compatible = "xlnx,zynq-qspi-1.0", },
76567dca5e5SNaga Sureshkumar Relli { /* end of table */ }
76667dca5e5SNaga Sureshkumar Relli };
76767dca5e5SNaga Sureshkumar Relli
76867dca5e5SNaga Sureshkumar Relli MODULE_DEVICE_TABLE(of, zynq_qspi_of_match);
76967dca5e5SNaga Sureshkumar Relli
77067dca5e5SNaga Sureshkumar Relli /*
77167dca5e5SNaga Sureshkumar Relli * zynq_qspi_driver - This structure defines the QSPI platform driver
77267dca5e5SNaga Sureshkumar Relli */
77367dca5e5SNaga Sureshkumar Relli static struct platform_driver zynq_qspi_driver = {
77467dca5e5SNaga Sureshkumar Relli .probe = zynq_qspi_probe,
775ae9084b6SUwe Kleine-König .remove_new = zynq_qspi_remove,
77667dca5e5SNaga Sureshkumar Relli .driver = {
77767dca5e5SNaga Sureshkumar Relli .name = "zynq-qspi",
77867dca5e5SNaga Sureshkumar Relli .of_match_table = zynq_qspi_of_match,
77967dca5e5SNaga Sureshkumar Relli },
78067dca5e5SNaga Sureshkumar Relli };
78167dca5e5SNaga Sureshkumar Relli
78267dca5e5SNaga Sureshkumar Relli module_platform_driver(zynq_qspi_driver);
78367dca5e5SNaga Sureshkumar Relli
78467dca5e5SNaga Sureshkumar Relli MODULE_AUTHOR("Xilinx, Inc.");
78567dca5e5SNaga Sureshkumar Relli MODULE_DESCRIPTION("Xilinx Zynq QSPI driver");
78667dca5e5SNaga Sureshkumar Relli MODULE_LICENSE("GPL");
787