Received: by 2002:ac0:aed5:0:0:0:0:0 with SMTP id t21csp347509imb; Fri, 1 Mar 2019 02:13:55 -0800 (PST) X-Google-Smtp-Source: AHgI3IZSpz2AgRZ6BJNwz5sUMnyWfiwgY/9oG/C5T07Dzd4r9Wg1GKN/4CdMAl5iGPFCAMIGJHPT X-Received: by 2002:a62:b40b:: with SMTP id h11mr4730837pfn.108.1551435235827; Fri, 01 Mar 2019 02:13:55 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1551435235; cv=none; d=google.com; s=arc-20160816; b=QFVEMgr7avHAv7yiQYApq1mXLwjqmMwBfVO9LGvA9PEwNwb2QRhESQsQVJlgHHzGF5 XkR3py58yOuOx2yi2XFV1cq1df9u6o+jeZw4wAd9tzLxum5vAJAeyg8QHybFfCwEsQwV 03dCezzK3HbszplQxXNvz+yqRwepLl6EH0w0m0r8soTS4T7hFGeZ4wmMnak8/9jRKApX xM7nDRLq2w3/yNOvxTJbwrir+Jzn01oqcCSd7ok/3t5tpxEti496a/V/l3OpMCw72nIe YTk9MOV/q+/KGg5W6K6D5ijxHDF2xecNAtMVa2Dtp57j2q8wYwOBOpOKnoRzpoNl4woC Azqw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :content-language:in-reply-to:mime-version:user-agent:date :message-id:from:references:cc:to:subject:dkim-signature; bh=cZ6qTFMGSOCyss0MhiuuAiIbp+LKfCIHtGiReXWXgKY=; b=QfnimOLCD4CIy+b9gfhYwekgAEdxdoXqI8cKD6PICxtpgupqQ7Kj4gAhag1dpFq1T2 3FcGboI16HwX0p3nbDhQQHqHKeRfsmW223W7Y/W5E8i+mzhzgxO3krStJCj86gS5Tra4 dHobh1/otVYt+q6dE19yhJNRAapj837knUTUGu3qFfVEm7gvaMNx9fHNSzEoM2R9BdPL 4XRGK90pABJbzud1lcdVk293bcttQLX2t3sPkEZAqlGgaSQL0mbNqG3+ZjX/b+jhZb/C //KIuOj4jCQ+SWInwhpTMV9eyQbOIDQBbLu74KE1YyJamo0tqG6dZElMFMxXlC0LNHIp Ajwg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@ti.com header.s=ti-com-17Q1 header.b=HShg49i1; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 139si380676pga.219.2019.03.01.02.13.40; Fri, 01 Mar 2019 02:13:55 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@ti.com header.s=ti-com-17Q1 header.b=HShg49i1; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732784AbfCAKLj (ORCPT + 99 others); Fri, 1 Mar 2019 05:11:39 -0500 Received: from fllv0015.ext.ti.com ([198.47.19.141]:50882 "EHLO fllv0015.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731669AbfCAKLj (ORCPT ); Fri, 1 Mar 2019 05:11:39 -0500 Received: from fllv0035.itg.ti.com ([10.64.41.0]) by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id x21AAfAS064091; Fri, 1 Mar 2019 04:10:41 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1551435041; bh=cZ6qTFMGSOCyss0MhiuuAiIbp+LKfCIHtGiReXWXgKY=; h=Subject:To:CC:References:From:Date:In-Reply-To; b=HShg49i1BTWqqhyFmFLvYhG5/QgINEj0UPp8sB30G9S/5L+r6CN3TYwHsTnfzEEuQ uTGVuUdFQKrsStPhpOLFdV9mt6W3/X8enIyp76RRE9mIzf+QR+3BDhLFi0qzyoGKEM HOtjhHZt607T7TbqY7roKFTTnheV4rj6fuJ8g2Mg= Received: from DFLE100.ent.ti.com (dfle100.ent.ti.com [10.64.6.21]) by fllv0035.itg.ti.com (8.15.2/8.15.2) with ESMTPS id x21AAfYD010685 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 1 Mar 2019 04:10:41 -0600 Received: from DFLE108.ent.ti.com (10.64.6.29) by DFLE100.ent.ti.com (10.64.6.21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1591.10; Fri, 1 Mar 2019 04:10:41 -0600 Received: from dlep32.itg.ti.com (157.170.170.100) by DFLE108.ent.ti.com (10.64.6.29) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1591.10 via Frontend Transport; Fri, 1 Mar 2019 04:10:41 -0600 Received: from [172.24.190.89] (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id x21AAb1v021895; Fri, 1 Mar 2019 04:10:38 -0600 Subject: Re: [RFC PATCH 2/2] spi: spi-mem: Add support for Zynq QSPI controller To: Naga Sureshkumar Relli , , CC: , , , , , , , References: <1551337361-11665-1-git-send-email-naga.sureshkumar.relli@xilinx.com> From: Vignesh Raghavendra Message-ID: <48dafcc4-1b9d-9ce2-9228-78e73252781f@ti.com> Date: Fri, 1 Mar 2019 15:41:39 +0530 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.1 MIME-Version: 1.0 In-Reply-To: <1551337361-11665-1-git-send-email-naga.sureshkumar.relli@xilinx.com> Content-Type: text/plain; charset="utf-8" Content-Language: en-US Content-Transfer-Encoding: 7bit X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 > --- > 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 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* 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 > +/* > + * 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 > +/* > + * 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 > +#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 > +/* 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. > + /* > + * 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? > + 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 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"); >