Hi Vignesh, Thanks for the review. > -----Original Message----- > From: Vignesh Raghavendra <vigneshr@xxxxxx> > Sent: Friday, March 1, 2019 3:42 PM > To: Naga Sureshkumar Relli <nagasure@xxxxxxxxxx>; broonie@xxxxxxxxxx; > bbrezillon@xxxxxxxxxx > Cc: linux-spi@xxxxxxxxxxxxxxx; dwmw2@xxxxxxxxxxxxx; marek.vasut@xxxxxxxxx; > richard@xxxxxx; linux-mtd@xxxxxxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; Michal Simek > <michals@xxxxxxxxxx>; nagasuresh12@xxxxxxxxx > Subject: Re: [RFC PATCH 2/2] spi: spi-mem: Add support for Zynq QSPI controller > > > > On 28/02/19 12:32 PM, Naga Sureshkumar Relli wrote: > > Add support for QSPI controller driver used by Xilinx Zynq SOC. > > > > Signed-off-by: Naga Sureshkumar Relli > > <naga.sureshkumar.relli@xxxxxxxxxx> > > --- > > drivers/spi/Kconfig | 8 + > > drivers/spi/Makefile | 1 + > > drivers/spi/spi-zynq-qspi.c | 780 > > ++++++++++++++++++++++++++++++++++++++++++++ > > 3 files changed, 789 insertions(+) > > create mode 100644 drivers/spi/spi-zynq-qspi.c > > > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index > > 9f89cb1..f12c880 100644 > > --- a/drivers/spi/Kconfig > > +++ b/drivers/spi/Kconfig > > @@ -816,6 +816,14 @@ config SPI_XTENSA_XTFPGA > > 16 bit words in SPI mode 0, automatically asserting CS on transfer > > start and deasserting on end. > > > > +config SPI_ZYNQ_QSPI > > + tristate "Xilinx Zynq QSPI controller" > > + depends on ARCH_ZYNQ > > + help > > + This enables support for the Zynq Quad SPI controller > > + in master mode. > > + This controller only supports SPI memory interface. > > + > > config SPI_ZYNQMP_GQSPI > > tristate "Xilinx ZynqMP GQSPI controller" > > depends on SPI_MASTER && HAS_DMA > > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index > > f296270..565db4d 100644 > > --- a/drivers/spi/Makefile > > +++ b/drivers/spi/Makefile > > @@ -115,6 +115,7 @@ obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o > > obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o > > obj-$(CONFIG_SPI_XLP) += spi-xlp.o > > obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o > > +obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o > > obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o > > > > # SPI slave protocol handlers > > diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c > > new file mode 100644 index 0000000..87c9ec2 > > --- /dev/null > > +++ b/drivers/spi/spi-zynq-qspi.c > > @@ -0,0 +1,780 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > +/* > > + * Copyright (C) 2019 Xilinx, Inc. > > + * > > + * Author: Naga Sureshkumar Relli <nagasure@xxxxxxxxxx> */ > > + > > +#include <linux/clk.h> > > +#include <linux/delay.h> > > +#include <linux/gpio.h> > > +#include <linux/interrupt.h> > > +#include <linux/io.h> > > +#include <linux/module.h> > > +#include <linux/of_irq.h> > > +#include <linux/of_address.h> > > +#include <linux/platform_device.h> > > +#include <linux/spi/spi.h> > > +#include <linux/workqueue.h> > > +#include <linux/spi/spi-mem.h> > > + > > +/* Register offset definitions */ > > +#define ZYNQ_QSPI_CONFIG_OFFSET 0x00 /* Configuration Register, RW > */ > > +#define ZYNQ_QSPI_STATUS_OFFSET 0x04 /* Interrupt Status Register, RO > */ > > +#define ZYNQ_QSPI_IEN_OFFSET 0x08 /* Interrupt Enable Register, > WO */ > > +#define ZYNQ_QSPI_IDIS_OFFSET 0x0C /* Interrupt Disable Reg, WO > */ > > +#define ZYNQ_QSPI_IMASK_OFFSET 0x10 /* Interrupt Enabled Mask > Reg,RO */ > > +#define ZYNQ_QSPI_ENABLE_OFFSET 0x14 /* Enable/Disable Register, RW > */ > > +#define ZYNQ_QSPI_DELAY_OFFSET 0x18 /* Delay Register, RW */ > > +#define ZYNQ_QSPI_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst, WO */ > > +#define ZYNQ_QSPI_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst, WO */ > > +#define ZYNQ_QSPI_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst, WO */ > > +#define ZYNQ_QSPI_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst, WO */ > > +#define ZYNQ_QSPI_RXD_OFFSET 0x20 /* Data Receive Register, RO */ > > +#define ZYNQ_QSPI_SIC_OFFSET 0x24 /* Slave Idle Count Register, > RW */ > > +#define ZYNQ_QSPI_TX_THRESH_OFFSET 0x28 /* TX FIFO Watermark Reg, > RW */ > > +#define ZYNQ_QSPI_RX_THRESH_OFFSET 0x2C /* RX FIFO Watermark Reg, > RW */ > > +#define ZYNQ_QSPI_GPIO_OFFSET 0x30 /* GPIO Register, RW */ > > +#define ZYNQ_QSPI_LINEAR_CFG_OFFSET 0xA0 /* Linear Adapter Config Ref, > RW */ > > +#define ZYNQ_QSPI_MOD_ID_OFFSET 0xFC /* Module ID Register, RO */ > > + > > +/* > > + * QSPI Configuration Register bit Masks > > + * > > + * This register contains various control bits that effect the > > +operation > > + * of the QSPI controller > > + */ > > +#define ZYNQ_QSPI_CONFIG_IFMODE_MASK 0x80000000 /* Flash Memory > Interface */ > > +#define ZYNQ_QSPI_CONFIG_MANSRT_MASK 0x00010000 /* Manual TX > Start */ > > +#define ZYNQ_QSPI_CONFIG_MANSRTEN_MASK 0x00008000 /* Enable > Manual TX Mode */ > > +#define ZYNQ_QSPI_CONFIG_SSFORCE_MASK 0x00004000 /* Manual Chip > Select */ > > +#define ZYNQ_QSPI_CONFIG_BDRATE_MASK 0x00000038 /* Baud Rate Divisor > Mask */ > > +#define ZYNQ_QSPI_CONFIG_CPHA_MASK 0x00000004 /* Clock Phase Control > */ > > +#define ZYNQ_QSPI_CONFIG_CPOL_MASK 0x00000002 /* Clock Polarity Control > */ > > +#define ZYNQ_QSPI_CONFIG_SSCTRL_MASK 0x00000400 /* Slave Select Mask */ > > +#define ZYNQ_QSPI_CONFIG_FWIDTH_MASK 0x000000C0 /* FIFO width > */ > > +#define ZYNQ_QSPI_CONFIG_MSTREN_MASK 0x00000001 /* Master Mode > */ > > + > > Convert to GENMASK() and BIT() macros Ok, I will update it. > > > +/* > > + * QSPI Configuration Register - Baud rate and slave select > > + * > > + * These are the values used in the calculation of baud rate divisor > > +and > > + * setting the slave select. > > + */ > > +#define ZYNQ_QSPI_BAUD_DIV_MAX 7 /* Baud rate divisor maximum */ > > +#define ZYNQ_QSPI_BAUD_DIV_SHIFT 3 /* Baud rate divisor shift in CR */ > > +#define ZYNQ_QSPI_SS_SHIFT 10 /* Slave Select field shift in CR */ > > + > > +/* > > + * QSPI Interrupt Registers bit Masks > > + * > > + * All the four interrupt registers (Status/Mask/Enable/Disable) have > > +the same > > + * bit definitions. > > + */ > > +#define ZYNQ_QSPI_IXR_RX_OVERFLOW_MASK 0x00000001 /* QSPI RX > FIFO Overflow */ > > +#define ZYNQ_QSPI_IXR_TXNFULL_MASK 0x00000004 /* QSPI TX FIFO > Overflow */ > > +#define ZYNQ_QSPI_IXR_TXFULL_MASK 0x00000008 /* QSPI TX FIFO is full > */ > > +#define ZYNQ_QSPI_IXR_RXNEMTY_MASK 0x00000010 /* QSPI RX FIFO Not > Empty */ > > +#define ZYNQ_QSPI_IXR_RXF_FULL_MASK 0x00000020 /* QSPI RX FIFO is full > */ > > +#define ZYNQ_QSPI_IXR_TXF_UNDRFLOW_MASK 0x00000040 /* QSPI TX > FIFO Underflow */ > > +#define ZYNQ_QSPI_IXR_ALL_MASK > (ZYNQ_QSPI_IXR_RX_OVERFLOW_MASK | \ > > + ZYNQ_QSPI_IXR_TXNFULL_MASK | \ > > + ZYNQ_QSPI_IXR_TXFULL_MASK | \ > > + ZYNQ_QSPI_IXR_RXNEMTY_MASK | \ > > + ZYNQ_QSPI_IXR_RXF_FULL_MASK | \ > > + ZYNQ_QSPI_IXR_TXF_UNDRFLOW_MASK) > > +#define ZYNQ_QSPI_IXR_RXTX_MASK > (ZYNQ_QSPI_IXR_TXNFULL_MASK | \ > > + ZYNQ_QSPI_IXR_RXNEMTY_MASK) > > + > > Convert to GENMASK() and BIT() macros Ok, will change it. > > > +/* > > + * QSPI Enable Register bit Masks > > + * > > + * This register is used to enable or disable the QSPI controller */ > > +#define ZYNQ_QSPI_ENABLE_ENABLE_MASK 0x00000001 /* QSPI Enable > Bit Mask */ > > + > > +/* > > + * QSPI Linear Configuration Register > > + * > > + * It is named Linear Configuration but it controls other modes when > > +not in > > + * linear mode also. > > + */ > > +#define ZYNQ_QSPI_LCFG_TWO_MEM_MASK 0x40000000 /* LQSPI Two > memories Mask */ > > +#define ZYNQ_QSPI_LCFG_SEP_BUS_MASK 0x20000000 /* LQSPI Separate bus > Mask */ > > +#define ZYNQ_QSPI_LCFG_U_PAGE_MASK 0x10000000 /* LQSPI Upper Page > Mask */ > > + > > Convert to GENMASK() and BIT() macros Ok, will change it > > > +#define ZYNQ_QSPI_LCFG_DUMMY_SHIFT 8 > > + > > +#define ZYNQ_QSPI_FAST_READ_QOUT_CODE 0x6B /* read instruction code > */ > > +#define ZYNQ_QSPI_FIFO_DEPTH 63 /* FIFO depth in words */ > > +#define ZYNQ_QSPI_RX_THRESHOLD 32 /* Rx FIFO threshold level */ > > +#define ZYNQ_QSPI_TX_THRESHOLD 1 /* Tx FIFO threshold level */ > > + > > +/* > > + * The modebits configurable by the driver to make the SPI support > > +different > > + * data formats > > + */ > > +#define MODEBITS (SPI_CPOL | SPI_CPHA) > > + > > Rename as ZYNQ_QSPI_MODEBITS to avoid namespace conflicts Ok, will update. > > > +/* Default number of chip selects */ > > +#define ZYNQ_QSPI_DEFAULT_NUM_CS 1 > > + > > +/** > > + * struct zynq_qspi - Defines qspi driver instance > > + * @regs: Virtual address of the QSPI controller registers > > + * @refclk: Pointer to the peripheral clock > > + * @pclk: Pointer to the APB clock > > + * @irq: IRQ number > > + * @txbuf: Pointer to the TX buffer > > + * @rxbuf: Pointer to the RX buffer > > + * @tx_bytes: Number of bytes left to transfer > > + * @rx_bytes: Number of bytes left to receive > > + * @data_completion: completion structure > > + */ > > +struct zynq_qspi { > > + struct device *dev; > > + void __iomem *regs; > > + struct clk *refclk; > > + struct clk *pclk; > > + int irq; > > + u8 *txbuf; > > + u8 *rxbuf; > > + int tx_bytes; > > + int rx_bytes; > > + struct completion data_completion; > > +}; > > + > > +/* > > + * Inline functions for the QSPI controller read/write */ static > > +inline u32 zynq_qspi_read(struct zynq_qspi *xqspi, u32 offset) { > > + return readl_relaxed(xqspi->regs + offset); } > > + > > +static inline void zynq_qspi_write(struct zynq_qspi *xqspi, u32 offset, > > + u32 val) > > +{ > > + writel_relaxed(val, xqspi->regs + offset); } > > + > > +/** > > + * zynq_qspi_init_hw - Initialize the hardware > > + * @xqspi: Pointer to the zynq_qspi structure > > + * > > + * The default settings of the QSPI controller's configurable > > +parameters on > > + * reset are > > + * - Master mode > > + * - Baud rate divisor is set to 2 > > + * - Tx threshold set to 1l Rx threshold set to 32 > > + * - Flash memory interface mode enabled > > + * - Size of the word to be transferred as 8 bit > > + * This function performs the following actions > > + * - Disable and clear all the interrupts > > + * - Enable manual slave select > > + * - Enable manual start > > + * - Deselect all the chip select lines > > + * - Set the size of the word to be transferred as 32 bit > > + * - Set the little endian mode of TX FIFO and > > + * - Enable the QSPI controller > > + */ > > +static void zynq_qspi_init_hw(struct zynq_qspi *xqspi) { > > + u32 config_reg; > > + > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_IDIS_OFFSET, > > +ZYNQ_QSPI_IXR_ALL_MASK); > > + > > + /* Disable linear mode as the boot loader may have used it */ > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, 0); > > + > > + /* Clear the RX FIFO */ > > + while (zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET) & > > + ZYNQ_QSPI_IXR_RXNEMTY_MASK) > > + zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET); > > + > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, > ZYNQ_QSPI_IXR_ALL_MASK); > > + config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET); > > + config_reg &= ~(ZYNQ_QSPI_CONFIG_MSTREN_MASK | > > + ZYNQ_QSPI_CONFIG_CPOL_MASK | > > + ZYNQ_QSPI_CONFIG_CPHA_MASK | > > + ZYNQ_QSPI_CONFIG_BDRATE_MASK | > > + ZYNQ_QSPI_CONFIG_SSFORCE_MASK | > > + ZYNQ_QSPI_CONFIG_MANSRTEN_MASK | > > + ZYNQ_QSPI_CONFIG_MANSRT_MASK); > > + config_reg |= (ZYNQ_QSPI_CONFIG_MSTREN_MASK | > > + ZYNQ_QSPI_CONFIG_SSFORCE_MASK | > > + ZYNQ_QSPI_CONFIG_FWIDTH_MASK | > > + ZYNQ_QSPI_CONFIG_IFMODE_MASK); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg); > > + > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_RX_THRESH_OFFSET, > > + ZYNQ_QSPI_RX_THRESHOLD); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_TX_THRESH_OFFSET, > > + ZYNQ_QSPI_TX_THRESHOLD); > > + > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, > > + ZYNQ_QSPI_ENABLE_ENABLE_MASK); > > +} > > + > > +static int zynq_qspi_check_buswidth(u8 width) { > > + switch (width) { > > + case 1: > > + case 2: > > + case 4: > > + return 0; > > + } > > + > > + return -ENOTSUPP; > > +} > > + > > +static bool zynq_qspi_supports_op(struct spi_mem *mem, > > + const struct spi_mem_op *op) > > +{ > > + int ret; > > + > > + ret = zynq_qspi_check_buswidth(op->cmd.buswidth); > > + > > + if (op->addr.nbytes) > > + ret |= zynq_qspi_check_buswidth(op->addr.buswidth); > > + > > + if (op->dummy.nbytes) > > + ret |= zynq_qspi_check_buswidth(op->dummy.buswidth); > > + > > + if (op->data.nbytes) > > + ret |= zynq_qspi_check_buswidth(op->data.buswidth); > > + > > + if (ret) > > + return false; > > + > > spi_mem_default_supports_op() already has this code. > Could you change, spi_mem_supports_op() to call > spi_mem_default_supports_op() first before controller specific > ->supports_op()? So that, above code can be dropped. Ok, I will update it. > > > + /* > > + * The number of address bytes should be equal to or less than 3 bytes. > > + */ > > + if (op->addr.nbytes > 3) > > + return false; > > + > > Hmm, how does the driver handle flash devices >16MB in size? Not supported? Zynq QSPI controller doesn't support 4 byte addressing. So to support > 16MB size, we have to use EAR(extended address register). As it is initial version of driver, I haven't added this code. This needs to be added in spi-nor.c. Previously I have sent an RFC patch to support all these. But Boris suggested to upstream the basic one first under spi-mem frame work. Not only this support, there are other features like dual parallel and dual stacked. All these features require additional support from spi-nor framework. We have another QSPI controller on ZynqMP SOC, which is also similar(but it supports 4Byte addressing). So by consolidating all these, I sent this patch. Please let me know your suggestion on this. > > > + return true; > > +} > > + > > +/** > > + * zynq_qspi_rxfifo_op - Read 1..4 bytes from RxFIFO to RX buffer > > + * @xqspi: Pointer to the zynq_qspi structure > > + * @size: Number of bytes to be read (1..4) > > + */ > > +static void zynq_qspi_rxfifo_op(struct zynq_qspi *xqspi, unsigned int > > +size) { > > + u32 data; > > + > > + data = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET); > > + > > + if (xqspi->rxbuf) { > > + memcpy(xqspi->rxbuf, ((u8 *)&data) + 4 - size, size); > > + xqspi->rxbuf += size; > > + } > > + > > + xqspi->rx_bytes -= size; > > + if (xqspi->rx_bytes < 0) > > + xqspi->rx_bytes = 0; > > +} > > + > > +/** > > + * zynq_qspi_txfifo_op - Write 1..4 bytes from TX buffer to TxFIFO > > + * @xqspi: Pointer to the zynq_qspi structure > > + * @size: Number of bytes to be written (1..4) > > + */ > > +static void zynq_qspi_txfifo_op(struct zynq_qspi *xqspi, unsigned int > > +size) { > > + static const unsigned int offset[4] = { > > + ZYNQ_QSPI_TXD_00_01_OFFSET, ZYNQ_QSPI_TXD_00_10_OFFSET, > > + ZYNQ_QSPI_TXD_00_11_OFFSET, ZYNQ_QSPI_TXD_00_00_OFFSET > }; > > + u32 data; > > + > > + if (xqspi->txbuf) { > > + data = 0xffffffff; > > + memcpy(&data, xqspi->txbuf, size); > > + xqspi->txbuf += size; > > + } else { > > + data = 0; > > + } > > + > > + xqspi->tx_bytes -= size; > > + zynq_qspi_write(xqspi, offset[size - 1], data); } > > + > > +/** > > + * zynq_qspi_chipselect - Select or deselect the chip select line > > + * @spi: Pointer to the spi_device structure > > + * @assert: 1 for select or 0 for deselect the chip select line > > + */ > > +static void zynq_qspi_chipselect(struct spi_device *spi, bool assert) > > +{ > > + struct spi_controller *ctrl = spi->master; > > + struct zynq_qspi *xqspi = spi_controller_get_devdata(ctrl); > > + u32 config_reg; > > + > > + config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET); > > + if (assert) { > > + /* Select the slave */ > > + config_reg &= ~ZYNQ_QSPI_CONFIG_SSCTRL_MASK; > > + config_reg |= (((~(BIT(spi->chip_select))) << > > + ZYNQ_QSPI_SS_SHIFT) & > > + ZYNQ_QSPI_CONFIG_SSCTRL_MASK); > > + } else { > > + config_reg |= ZYNQ_QSPI_CONFIG_SSCTRL_MASK; > > + } > > + > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg); } > > + > > +/** > > + * zynq_qspi_config_op - Configure QSPI controller for specified transfer > > + * @xqspi: Pointer to the zynq_qspi structure > > + * @qspi: Pointer to the spi_device structure > > + * > > + * Sets the operational mode of QSPI controller for the next QSPI > > +transfer and > > + * sets the requested clock frequency. > > + * > > + * Return: 0 on success and -EINVAL on invalid input parameter > > + * > > + * Note: If the requested frequency is not an exact match with what > > +can be > > + * obtained using the prescalar value, the driver sets the clock > > +frequency which > > + * is lower than the requested frequency (maximum lower) for the > > +transfer. If > > + * the requested frequency is higher or lower than that is supported > > +by the QSPI > > + * controller the driver will set the highest or lowest frequency > > +supported by > > + * controller. > > + */ > > +static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct > > +spi_device *spi) { > > + u32 config_reg, baud_rate_val = 0; > > + > > + /* Set the clock frequency */ > > + while ((baud_rate_val < ZYNQ_QSPI_BAUD_DIV_MAX) && > > + (clk_get_rate(xqspi->refclk) / (2 << baud_rate_val)) > > > + spi->max_speed_hz) > > + baud_rate_val++; > > This can be simplified by using DIV_ROUND_UP() to find baud_rate_val Yes, I will modify it. Thanks, Naga Sureshkumar Relli > > > > Regards > Vignesh > > > + config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET); > > + > > + /* Set the QSPI clock phase and clock polarity */ > > + config_reg &= (~ZYNQ_QSPI_CONFIG_CPHA_MASK) & > > + (~ZYNQ_QSPI_CONFIG_CPOL_MASK); > > + if (spi->mode & SPI_CPHA) > > + config_reg |= ZYNQ_QSPI_CONFIG_CPHA_MASK; > > + if (spi->mode & SPI_CPOL) > > + config_reg |= ZYNQ_QSPI_CONFIG_CPOL_MASK; > > + > > + config_reg &= ~ZYNQ_QSPI_CONFIG_BDRATE_MASK; > > + config_reg |= (baud_rate_val << ZYNQ_QSPI_BAUD_DIV_SHIFT); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg); > > + > > + return 0; > > +} > > + > > +/** > > + * zynq_qspi_setup - Configure the QSPI controller > > + * @spi: Pointer to the spi_device structure > > + * > > + * Sets the operational mode of QSPI controller for the next QSPI > > +transfer, baud > > + * rate and divisor value to setup the requested qspi clock. > > + * > > + * Return: 0 on success and error value on failure > > + */ > > +static int zynq_qspi_setup_op(struct spi_device *spi) { > > + struct spi_controller *ctrl = spi->master; > > + struct zynq_qspi *qspi = spi_controller_get_devdata(ctrl); > > + > > + if (ctrl->busy) > > + return -EBUSY; > > + > > + clk_enable(qspi->refclk); > > + clk_enable(qspi->pclk); > > + zynq_qspi_write(qspi, ZYNQ_QSPI_ENABLE_OFFSET, > > + ZYNQ_QSPI_ENABLE_ENABLE_MASK); > > + > > + return 0; > > +} > > + > > +/** > > + * zynq_qspi_write_op - Fills the TX FIFO with as many bytes as possible > > + * @xqspi: Pointer to the zynq_qspi structure > > + * @txcount: Maximum number of words to write > > + * @txempty: Indicates that TxFIFO is empty > > + */ > > +static void zynq_qspi_write_op(struct zynq_qspi *xqspi, int txcount, > > + bool txempty) > > +{ > > + int count, len, k; > > + > > + len = xqspi->tx_bytes; > > + if (len && len < 4) { > > + /* > > + * We must empty the TxFIFO between accesses to TXD0, > > + * TXD1, TXD2, TXD3. > > + */ > > + if (txempty) > > + zynq_qspi_txfifo_op(xqspi, len); > > + > > + return; > > + } > > + > > + count = len / 4; > > + if (count > txcount) > > + count = txcount; > > + > > + if (xqspi->txbuf) { > > + writesl(xqspi->regs + ZYNQ_QSPI_TXD_00_00_OFFSET, > > + xqspi->txbuf, count); > > + xqspi->txbuf += count * 4; > > + } else { > > + for (k = 0; k < count; k++) > > + writel_relaxed(0, xqspi->regs + > > + ZYNQ_QSPI_TXD_00_00_OFFSET); > > + } > > + > > + xqspi->tx_bytes -= count * 4; > > +} > > + > > +/** > > + * zynq_qspi_read_op - Drains the RX FIFO by as many bytes as possible > > + * @xqspi: Pointer to the zynq_qspi structure > > + * @rxcount: Maximum number of words to read > > + */ > > +static void zynq_qspi_read_op(struct zynq_qspi *xqspi, int rxcount) { > > + int count, len, k; > > + > > + len = xqspi->rx_bytes - xqspi->tx_bytes; > > + count = len / 4; > > + if (count > rxcount) > > + count = rxcount; > > + if (xqspi->rxbuf) { > > + readsl(xqspi->regs + ZYNQ_QSPI_RXD_OFFSET, > > + xqspi->rxbuf, count); > > + xqspi->rxbuf += count * 4; > > + } else { > > + for (k = 0; k < count; k++) > > + readl_relaxed(xqspi->regs + ZYNQ_QSPI_RXD_OFFSET); > > + } > > + xqspi->rx_bytes -= count * 4; > > + len -= count * 4; > > + > > + if (len && len < 4 && count < rxcount) > > + zynq_qspi_rxfifo_op(xqspi, len); > > +} > > + > > +/** > > + * zynq_qspi_irq - Interrupt service routine of the QSPI controller > > + * @irq: IRQ number > > + * @dev_id: Pointer to the xqspi structure > > + * > > + * This function handles TX empty only. > > + * On TX empty interrupt this function reads the received data from > > +RX FIFO and > > + * fills the TX FIFO if there is any data remaining to be transferred. > > + * > > + * Return: IRQ_HANDLED when interrupt is handled; IRQ_NONE otherwise. > > + */ > > +static irqreturn_t zynq_qspi_irq(int irq, void *dev_id) { > > + u32 intr_status; > > + bool txempty; > > + struct zynq_qspi *xqspi = (struct zynq_qspi *)dev_id; > > + > > + intr_status = zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, intr_status); > > + > > + if ((intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK) || > > + (intr_status & ZYNQ_QSPI_IXR_RXNEMTY_MASK)) { > > + /* > > + * This bit is set when Tx FIFO has < THRESHOLD entries. > > + * We have the THRESHOLD value set to 1, > > + * so this bit indicates Tx FIFO is empty. > > + */ > > + txempty = !!(intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK); > > + /* Read out the data from the RX FIFO */ > > + zynq_qspi_read_op(xqspi, ZYNQ_QSPI_RX_THRESHOLD); > > + if (xqspi->tx_bytes) { > > + /* There is more data to send */ > > + zynq_qspi_write_op(xqspi, ZYNQ_QSPI_RX_THRESHOLD, > > + txempty); > > + } else { > > + /* > > + * If transfer and receive is completed then only send > > + * complete signal. > > + */ > > + if (!xqspi->rx_bytes) { > > + zynq_qspi_write(xqspi, > > + ZYNQ_QSPI_IDIS_OFFSET, > > + ZYNQ_QSPI_IXR_RXTX_MASK); > > + complete(&xqspi->data_completion); > > + } > > + } > > + return IRQ_HANDLED; > > + } > > + > > + return IRQ_NONE; > > +} > > + > > +/** > > + * zynq_qspi_exec_mem_op() - Initiates the QSPI transfer > > + * @mem: the SPI memory > > + * @op: the memory operation to execute > > + * > > + * Executes a memory operation. > > + * > > + * This function first selects the chip and starts the memory operation. > > + * > > + * Return: 0 in case of success, a negative error code otherwise. > > + */ > > +static int zynq_qspi_exec_mem_op(struct spi_mem *mem, > > + const struct spi_mem_op *op) > > +{ > > + struct zynq_qspi *xqspi = spi_controller_get_devdata(mem->spi->master); > > + int err = 0, i; > > + u8 *tmpbuf; > > + > > + dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n", > > + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, > > + op->dummy.buswidth, op->data.buswidth); > > + > > + zynq_qspi_chipselect(mem->spi, true); > > + zynq_qspi_config_op(xqspi, mem->spi); > > + > > + if (op->cmd.opcode) { > > + reinit_completion(&xqspi->data_completion); > > + xqspi->txbuf = (u8 *)&op->cmd.opcode; > > + xqspi->rxbuf = NULL; > > + xqspi->tx_bytes = sizeof(op->cmd.opcode); > > + xqspi->rx_bytes = sizeof(op->cmd.opcode); > > + zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, > > + ZYNQ_QSPI_IXR_RXTX_MASK); > > + if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion, > > + msecs_to_jiffies(1000))) { > > + err = -ETIMEDOUT; > > + } > > + } > > + zynq_qspi_config_op(xqspi, mem->spi); > > + if (op->addr.nbytes) { > > + for (i = 0; i < op->addr.nbytes; i++) { > > + xqspi->txbuf[i] = op->addr.val >> > > + (8 * (op->addr.nbytes - i - 1)); > > + } > > + > > + reinit_completion(&xqspi->data_completion); > > + xqspi->rxbuf = NULL; > > + xqspi->tx_bytes = op->addr.nbytes; > > + xqspi->rx_bytes = op->addr.nbytes; > > + zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, > > + ZYNQ_QSPI_IXR_RXTX_MASK); > > + if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion, > > + msecs_to_jiffies(1000))) { > > + err = -ETIMEDOUT; > > + } > > + } > > + zynq_qspi_config_op(xqspi, mem->spi); > > + if (op->dummy.nbytes) { > > + tmpbuf = kzalloc(op->dummy.nbytes, GFP_KERNEL); > > + memset(tmpbuf, 0xff, op->dummy.nbytes); > > + reinit_completion(&xqspi->data_completion); > > + xqspi->txbuf = tmpbuf; > > + xqspi->rxbuf = NULL; > > + xqspi->tx_bytes = op->dummy.nbytes; > > + xqspi->rx_bytes = op->dummy.nbytes; > > + zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, > > + ZYNQ_QSPI_IXR_RXTX_MASK); > > + if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion, > > + msecs_to_jiffies(1000))) { > > + err = -ETIMEDOUT; > > + } > > + kfree(tmpbuf); > > + } > > + zynq_qspi_config_op(xqspi, mem->spi); > > + if (op->data.nbytes) { > > + reinit_completion(&xqspi->data_completion); > > + if (op->data.dir == SPI_MEM_DATA_OUT) { > > + xqspi->txbuf = (u8 *)op->data.buf.out; > > + xqspi->tx_bytes = op->data.nbytes; > > + xqspi->rxbuf = NULL; > > + xqspi->rx_bytes = op->data.nbytes; > > + } else { > > + xqspi->txbuf = NULL; > > + xqspi->rxbuf = (u8 *)op->data.buf.in; > > + xqspi->rx_bytes = op->data.nbytes; > > + xqspi->tx_bytes = op->data.nbytes; > > + } > > + > > + zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, > > + ZYNQ_QSPI_IXR_RXTX_MASK); > > + if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion, > > + msecs_to_jiffies(1000))) { > > + err = -ETIMEDOUT; > > + } > > + } > > + zynq_qspi_chipselect(mem->spi, false); > > + > > + return err; > > +} > > + > > +static const struct spi_controller_mem_ops zynq_qspi_mem_ops = { > > + .supports_op = zynq_qspi_supports_op, > > + .exec_op = zynq_qspi_exec_mem_op, > > +}; > > + > > +/** > > + * zynq_qspi_probe - Probe method for the QSPI driver > > + * @pdev: Pointer to the platform_device structure > > + * > > + * This function initializes the driver data structures and the hardware. > > + * > > + * Return: 0 on success and error value on failure > > + */ > > +static int zynq_qspi_probe(struct platform_device *pdev) { > > + int ret = 0; > > + struct spi_controller *ctlr; > > + struct device *dev = &pdev->dev; > > + struct device_node *np = dev->of_node; > > + struct zynq_qspi *xqspi; > > + struct resource *res; > > + u32 num_cs; > > + > > + ctlr = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); > > + if (!ctlr) > > + return -ENOMEM; > > + > > + xqspi = spi_controller_get_devdata(ctlr); > > + xqspi->dev = dev; > > + platform_set_drvdata(pdev, xqspi); > > + ctlr->mem_ops = &zynq_qspi_mem_ops; > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + xqspi->regs = devm_ioremap_resource(&pdev->dev, res); > > + if (IS_ERR(xqspi->regs)) { > > + ret = PTR_ERR(xqspi->regs); > > + goto remove_master; > > + } > > + > > + xqspi->pclk = devm_clk_get(&pdev->dev, "pclk"); > > + if (IS_ERR(xqspi->pclk)) { > > + dev_err(&pdev->dev, "pclk clock not found.\n"); > > + ret = PTR_ERR(xqspi->pclk); > > + goto remove_master; > > + } > > + > > + init_completion(&xqspi->data_completion); > > + > > + xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk"); > > + if (IS_ERR(xqspi->refclk)) { > > + dev_err(&pdev->dev, "ref_clk clock not found.\n"); > > + ret = PTR_ERR(xqspi->refclk); > > + goto remove_master; > > + } > > + > > + ret = clk_prepare_enable(xqspi->pclk); > > + if (ret) { > > + dev_err(&pdev->dev, "Unable to enable APB clock.\n"); > > + goto remove_master; > > + } > > + > > + ret = clk_prepare_enable(xqspi->refclk); > > + if (ret) { > > + dev_err(&pdev->dev, "Unable to enable device clock.\n"); > > + goto clk_dis_pclk; > > + } > > + > > + /* QSPI controller initializations */ > > + zynq_qspi_init_hw(xqspi); > > + > > + xqspi->irq = platform_get_irq(pdev, 0); > > + if (xqspi->irq <= 0) { > > + ret = -ENXIO; > > + dev_err(&pdev->dev, "irq resource not found\n"); > > + goto remove_master; > > + } > > + ret = devm_request_irq(&pdev->dev, xqspi->irq, zynq_qspi_irq, > > + 0, pdev->name, xqspi); > > + if (ret != 0) { > > + ret = -ENXIO; > > + dev_err(&pdev->dev, "request_irq failed\n"); > > + goto remove_master; > > + } > > + > > + ret = of_property_read_u32(pdev->dev.of_node, "num-cs", > > + &num_cs); > > + if (ret < 0) > > + ctlr->num_chipselect = ZYNQ_QSPI_DEFAULT_NUM_CS; > > + else > > + ctlr->num_chipselect = num_cs; > > + ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | > > + SPI_TX_DUAL | SPI_TX_QUAD; > > + ctlr->mem_ops = &zynq_qspi_mem_ops; > > + ctlr->setup = zynq_qspi_setup_op; > > + ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2; > > + ctlr->dev.of_node = np; > > + ret = spi_register_controller(ctlr); > > + if (ret) { > > + dev_err(&pdev->dev, "spi_register_master failed\n"); > > + goto clk_dis_all; > > + } > > + > > + return ret; > > + > > +clk_dis_all: > > + clk_disable_unprepare(xqspi->refclk); > > +clk_dis_pclk: > > + clk_disable_unprepare(xqspi->pclk); > > +remove_master: > > + spi_controller_put(ctlr); > > + > > + return ret; > > +} > > + > > +/** > > + * zynq_qspi_remove - Remove method for the QSPI driver > > + * @pdev: Pointer to the platform_device structure > > + * > > + * This function is called if a device is physically removed from the > > +system or > > + * if the driver module is being unloaded. It frees all resources > > +allocated to > > + * the device. > > + * > > + * Return: 0 on success and error value on failure > > + */ > > +static int zynq_qspi_remove(struct platform_device *pdev) { > > + struct zynq_qspi *xqspi = platform_get_drvdata(pdev); > > + > > + zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0); > > + > > + clk_disable_unprepare(xqspi->refclk); > > + clk_disable_unprepare(xqspi->pclk); > > + > > + return 0; > > +} > > + > > +static const struct of_device_id zynq_qspi_of_match[] = { > > + { .compatible = "xlnx,zynq-qspi-1.0", }, > > + { /* end of table */ } > > +}; > > + > > +MODULE_DEVICE_TABLE(of, zynq_qspi_of_match); > > + > > +/* > > + * zynq_qspi_driver - This structure defines the QSPI platform driver > > +*/ static struct platform_driver zynq_qspi_driver = { > > + .probe = zynq_qspi_probe, > > + .remove = zynq_qspi_remove, > > + .driver = { > > + .name = "zynq-qspi", > > + .of_match_table = zynq_qspi_of_match, > > + }, > > +}; > > + > > +module_platform_driver(zynq_qspi_driver); > > + > > +MODULE_AUTHOR("Xilinx, Inc."); > > +MODULE_DESCRIPTION("Xilinx Zynq QSPI driver"); MODULE_LICENSE("GPL"); > > ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/