From patchwork Tue May 24 09:04:41 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naga Sureshkumar Relli X-Patchwork-Id: 9133219 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 85422607D3 for ; Tue, 24 May 2016 09:19:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 772F327CB2 for ; Tue, 24 May 2016 09:19:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6B5F928288; Tue, 24 May 2016 09:19:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DC68027CB2 for ; Tue, 24 May 2016 09:19:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932101AbcEXJT5 (ORCPT ); Tue, 24 May 2016 05:19:57 -0400 Received: from mail-cys01nam02on0089.outbound.protection.outlook.com ([104.47.37.89]:14529 "EHLO NAM02-CY1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752031AbcEXJTy (ORCPT ); Tue, 24 May 2016 05:19:54 -0400 X-Greylist: delayed 901 seconds by postgrey-1.27 at vger.kernel.org; Tue, 24 May 2016 05:19:54 EDT Received: from CY1NAM02FT008.eop-nam02.prod.protection.outlook.com (10.152.74.58) by CY1NAM02HT088.eop-nam02.prod.protection.outlook.com (10.152.74.74) with Microsoft SMTP Server (TLS) id 15.1.492.8; Tue, 24 May 2016 09:04:51 +0000 Authentication-Results: spf=pass (sender IP is 149.199.60.100) smtp.mailfrom=xilinx.com; vger.kernel.org; dkim=none (message not signed) header.d=none;vger.kernel.org; dmarc=bestguesspass action=none header.from=xilinx.com; Received-SPF: Pass (protection.outlook.com: domain of xilinx.com designates 149.199.60.100 as permitted sender) receiver=protection.outlook.com; client-ip=149.199.60.100; helo=xsj-pvapsmtpgw02; Received: from xsj-pvapsmtpgw02 (149.199.60.100) by CY1NAM02FT008.mail.protection.outlook.com (10.152.75.59) with Microsoft SMTP Server (TLS) id 15.1.497.8 via Frontend Transport; Tue, 24 May 2016 09:04:51 +0000 Received: from unknown-38-66.xilinx.com ([149.199.38.66]:50918 helo=xsj-pvapsmtp01) by xsj-pvapsmtpgw02 with esmtp (Exim 4.63) (envelope-from ) id 1b58Gf-0003PU-TZ; Tue, 24 May 2016 02:04:49 -0700 Received: from [127.0.0.1] (helo=localhost) by xsj-pvapsmtp01 with smtp (Exim 4.63) (envelope-from ) id 1b58Gg-0004Ft-NM; Tue, 24 May 2016 02:04:50 -0700 Received: from xsj-pvapsmtp01 (xsj-mail.xilinx.com [149.199.38.66]) by xsj-smtp-dlp2.xlnx.xilinx.com (8.13.8/8.13.1) with ESMTP id u4O94jeE012960; Tue, 24 May 2016 02:04:45 -0700 Received: from [172.23.64.208] (helo=xhdrdevl6.xilinx.com) by xsj-pvapsmtp01 with esmtp (Exim 4.63) (envelope-from ) id 1b58GZ-0004Eq-RU; Tue, 24 May 2016 02:04:45 -0700 Received: by xhdrdevl6.xilinx.com (Postfix, from userid 25026) id C9DB3F2000C; Tue, 24 May 2016 14:34:42 +0530 (IST) From: Naga Sureshkumar Relli To: , , , CC: , , "Naga Sureshkumar Relli" Subject: [PATCH 1/3] spi: xilinx: Remove bitbang and register with spi core Date: Tue, 24 May 2016 14:34:41 +0530 Message-ID: <1464080681-25109-1-git-send-email-nagasure@xilinx.com> X-Mailer: git-send-email 2.1.1 X-RCIS-Action: ALLOW X-TM-AS-Product-Ver: IMSS-7.1.0.1224-8.0.0.1202-22342.005 X-TM-AS-User-Approved-Sender: Yes;Yes X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:149.199.60.100; IPV:NLI; CTRY:US; EFV:NLI; SFV:NSPM; SFS:(10009020)(6009001)(2980300002)(438002)(199003)(189002)(9170700003)(42186005)(50466002)(19580405001)(52956003)(19580395003)(63266004)(33646002)(551934003)(87936001)(50226002)(5001770100001)(107886002)(106466001)(8936002)(86362001)(11100500001)(2201001)(2906002)(229853001)(575784001)(5003940100001)(46386002)(4001450100002)(48376002)(92566002)(45336002)(90966002)(5008740100001)(36756003)(8676002)(6806005)(47776003)(4326007)(586003)(189998001)(81166006)(103686003)(4001430100002)(1220700001)(50986999)(107986001); DIR:OUT; SFP:1101; SCL:1; SRVR:CY1NAM02HT088; H:xsj-pvapsmtpgw02; FPR:; SPF:Pass; MLV:sfv; A:1; MX:1; LANG:en; MIME-Version: 1.0 X-MS-Office365-Filtering-Correlation-Id: e08ab02c-c97e-43df-f1e6-08d383b2775a X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(8251501002); SRVR:CY1NAM02HT088; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(2401047)(8121501046)(13024025)(13018025)(13017025)(13015025)(5005006)(13023025)(10201501046)(3002001)(6055026); SRVR:CY1NAM02HT088; BCL:0; PCL:0; RULEID:; SRVR:CY1NAM02HT088; X-Forefront-PRVS: 09525C61DB X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 24 May 2016 09:04:51.5956 (UTC) X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c; Ip=[149.199.60.100]; Helo=[xsj-pvapsmtpgw02] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: CY1NAM02HT088 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch removes the bitbang layer registration. it directly register with spi core using spi_register_master and uses the call backs provided by spi_master struct. Signed-off-by: Naga Sureshkumar Relli --- drivers/spi/spi-xilinx.c | 733 +++++++++++++++++++++++++++-------------------- 1 file changed, 418 insertions(+), 315 deletions(-) diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 3009121..bc12e0c 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -18,10 +18,9 @@ #include #include #include -#include #include #include - +#include #define XILINX_SPI_MAX_CS 32 #define XILINX_SPI_NAME "xilinx_spi" @@ -29,8 +28,18 @@ /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) * Product Specification", DS464 */ -#define XSPI_CR_OFFSET 0x60 /* Control Register */ - +/* Register Offsets */ +#define XSPI_CR_OFFSET 0x60 +#define XSPI_SR_OFFSET 0x64 +#define XSPI_TXD_OFFSET 0x68 +#define XSPI_RXD_OFFSET 0x6c +#define XSPI_SSR_OFFSET 0x70 +#define XIPIF_V123B_DGIER_OFFSET 0x1c +#define XIPIF_V123B_IISR_OFFSET 0x20 +#define XIPIF_V123B_IIER_OFFSET 0x28 +#define XIPIF_V123B_RESETR_OFFSET 0x40 + +/* Register bit masks */ #define XSPI_CR_LOOP 0x01 #define XSPI_CR_ENABLE 0x02 #define XSPI_CR_MASTER_MODE 0x04 @@ -43,133 +52,188 @@ #define XSPI_CR_MANUAL_SSELECT 0x80 #define XSPI_CR_TRANS_INHIBIT 0x100 #define XSPI_CR_LSB_FIRST 0x200 - -#define XSPI_SR_OFFSET 0x64 /* Status Register */ - -#define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */ -#define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */ -#define XSPI_SR_TX_EMPTY_MASK 0x04 /* Transmit FIFO is empty */ -#define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */ -#define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */ - -#define XSPI_TXD_OFFSET 0x68 /* Data Transmit Register */ -#define XSPI_RXD_OFFSET 0x6c /* Data Receive Register */ - -#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */ - -/* Register definitions as per "OPB IPIF (v3.01c) Product Specification", DS414 - * IPIF registers are 32 bit - */ -#define XIPIF_V123B_DGIER_OFFSET 0x1c /* IPIF global int enable reg */ +#define XSPI_SR_RX_EMPTY_MASK 0x01 +#define XSPI_SR_RX_FULL_MASK 0x02 +#define XSPI_SR_TX_EMPTY_MASK 0x04 +#define XSPI_SR_TX_FULL_MASK 0x08 +#define XSPI_SR_MODE_FAULT_MASK 0x10 #define XIPIF_V123B_GINTR_ENABLE 0x80000000 - -#define XIPIF_V123B_IISR_OFFSET 0x20 /* IPIF interrupt status reg */ -#define XIPIF_V123B_IIER_OFFSET 0x28 /* IPIF interrupt enable reg */ - -#define XSPI_INTR_MODE_FAULT 0x01 /* Mode fault error */ -#define XSPI_INTR_SLAVE_MODE_FAULT 0x02 /* Selected as slave while - * disabled */ -#define XSPI_INTR_TX_EMPTY 0x04 /* TxFIFO is empty */ -#define XSPI_INTR_TX_UNDERRUN 0x08 /* TxFIFO was underrun */ -#define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */ -#define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */ -#define XSPI_INTR_TX_HALF_EMPTY 0x40 /* TxFIFO is half empty */ - -#define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */ -#define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */ - +#define XSPI_INTR_MODE_FAULT 0x01 +#define XSPI_INTR_SLAVE_MODE_FAULT 0x02 +#define XSPI_INTR_TX_EMPTY 0x04 +#define XSPI_INTR_TX_UNDERRUN 0x08 +#define XSPI_INTR_RX_FULL 0x10 +#define XSPI_INTR_RX_OVERRUN 0x20 +#define XSPI_INTR_TX_HALF_EMPTY 0x40 +#define XIPIF_V123B_RESET_MASK 0x0a + +/* Number of bits per word */ +#define XSPI_ONE_BITS_PER_WORD 1 +#define XSPI_TWO_BITS_PER_WORD 2 +#define XSPI_FOUR_BITS_PER_WORD 4 + +/* Number of data lines used to receive */ +#define XSPI_RX_ONE_WIRE 1 + +/** + * struct xilinx_spi - This definition define spi driver instance + * @regs: virt. address of the control registers + * @irq: IRQ number + * @rx_ptr: Pointer to the RX buffer + * @tx_ptr: Pointer to the TX buffer + * @bytes_per_word: Number of bytes in a word + * @buffer_size: Buffer size in words + * @cs_inactive: Level of the CS pins when inactive + * @read_fn: For reading data from SPI registers + * @write_fn: For writing data to SPI registers + * @bytes_to_transfer: Number of bytes left to transfer + * @bytes_to_receive: Number of bytes left to receive + * @rx_bus_width: Number of wires used to receive data + * @tx_fifo: For writing data to fifo + * @rx_fifo: For reading data from fifo + */ struct xilinx_spi { - /* bitbang has to be first */ - struct spi_bitbang bitbang; - struct completion done; - void __iomem *regs; /* virt. address of the control registers */ - - int irq; - - u8 *rx_ptr; /* pointer in the Tx buffer */ - const u8 *tx_ptr; /* pointer in the Rx buffer */ + void __iomem *regs; + int irq; + u8 *rx_ptr; + const u8 *tx_ptr; u8 bytes_per_word; - int buffer_size; /* buffer size in words */ - u32 cs_inactive; /* Level of the CS pins when inactive*/ + int buffer_size; + u32 cs_inactive; unsigned int (*read_fn)(void __iomem *); void (*write_fn)(u32, void __iomem *); + u32 bytes_to_transfer; + u32 bytes_to_receive; + u32 rx_bus_width; + void (*tx_fifo)(struct xilinx_spi *xqspi); + void (*rx_fifo)(struct xilinx_spi *xqspi); }; +/** + * XSPI_FIFO_READ - Generate xspi_read_rx_fifo_* functions + * @size: bits_per_word that are read from RX FIFO + * @type: C type of value argument + * + * Generates xspi_read_rx_fifo_* functions used to write + * data into RX FIFO for different transaction widths. + */ +#define XSPI_FIFO_READ(size, type) \ +static void xspi_read_rx_fifo_##size(struct xilinx_spi *xqspi) \ +{ \ + int i; \ + int count = (xqspi->bytes_to_receive > xqspi->buffer_size) ? \ + xqspi->buffer_size : xqspi->bytes_to_receive; \ + u32 data; \ + for (i = 0; i < count; i += (size/8)) { \ + data = readl_relaxed(xqspi->regs + XSPI_RXD_OFFSET); \ + if (xqspi->rx_ptr) \ + ((type *)xqspi->rx_ptr)[i] = (type)data; \ + } \ + xqspi->bytes_to_receive -= count; \ + if (xqspi->rx_ptr) \ + xqspi->rx_ptr += count; \ +} + +/** + * XSPI_FIFO_WRITE - Generate xspi_fill_tx_fifo_* functions + * @size: bits_per_word that are written into TX FIFO + * @type: C type of value argument + * + * Generates xspi_fill_tx_fifo_* functions used to write + * data into TX FIFO for different transaction widths. + */ +#define XSPI_FIFO_WRITE(size, type) \ +static void xspi_fill_tx_fifo_##size(struct xilinx_spi *xqspi) \ +{ \ + int i; \ + int count = (xqspi->bytes_to_transfer > xqspi->buffer_size) ? \ + xqspi->buffer_size : xqspi->bytes_to_transfer; \ + u32 data = 0; \ + for (i = 0; i < count; i += (size/8)) { \ + if (xqspi->tx_ptr) \ + data = (type)((u8 *)xqspi->tx_ptr)[i]; \ + writel_relaxed(data, (xqspi->regs + XSPI_TXD_OFFSET)); \ + } \ + xqspi->bytes_to_transfer -= count; \ + if (xqspi->tx_ptr) \ + xqspi->tx_ptr += count; \ +} + +XSPI_FIFO_READ(8, u8) +XSPI_FIFO_READ(16, u16) +XSPI_FIFO_READ(32, u32) +XSPI_FIFO_WRITE(8, u8) +XSPI_FIFO_WRITE(16, u16) +XSPI_FIFO_WRITE(32, u32) + +/** + * xspi_write32 - Write a value to the device register little endian + * @val: Value to write at the Register offset + * @addr: Register offset + * + * Write data to the paricular SPI register + */ static void xspi_write32(u32 val, void __iomem *addr) { iowrite32(val, addr); } +/** + * xspi_read32 - read a value from the device register little endian + * @addr: Register offset + * + * Read data from the paricular SPI register + * + * Return: return value from the SPI register. + */ static unsigned int xspi_read32(void __iomem *addr) { return ioread32(addr); } +/** + * xspi_write32_be - Write a value to the device register big endian + * @val: Value to write at the Register offset + * @addr: Register offset + * + * Write data to the paricular SPI register + */ static void xspi_write32_be(u32 val, void __iomem *addr) { iowrite32be(val, addr); } +/** + * xspi_read32_be - read a value from the device register big endian + * @addr: Register offset + * + * Read data from the paricular SPI register + * + * Return: return value from the SPI register. + */ static unsigned int xspi_read32_be(void __iomem *addr) { return ioread32be(addr); } -static void xilinx_spi_tx(struct xilinx_spi *xspi) -{ - u32 data = 0; - - if (!xspi->tx_ptr) { - xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); - return; - } - - switch (xspi->bytes_per_word) { - case 1: - data = *(u8 *)(xspi->tx_ptr); - break; - case 2: - data = *(u16 *)(xspi->tx_ptr); - break; - case 4: - data = *(u32 *)(xspi->tx_ptr); - break; - } - - xspi->write_fn(data, xspi->regs + XSPI_TXD_OFFSET); - xspi->tx_ptr += xspi->bytes_per_word; -} - -static void xilinx_spi_rx(struct xilinx_spi *xspi) -{ - u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); - - if (!xspi->rx_ptr) - return; - - switch (xspi->bytes_per_word) { - case 1: - *(u8 *)(xspi->rx_ptr) = data; - break; - case 2: - *(u16 *)(xspi->rx_ptr) = data; - break; - case 4: - *(u32 *)(xspi->rx_ptr) = data; - break; - } - - xspi->rx_ptr += xspi->bytes_per_word; -} - +/** + * xspi_init_hw - Initialize the hardware + * @xspi: Pointer to the zynqmp_qspi structure + * + * This function performs the following actions + * - Disable and clear all the interrupts + * - Enable manual slave select + * - Enable the SPI controller + */ static void xspi_init_hw(struct xilinx_spi *xspi) { void __iomem *regs_base = xspi->regs; /* Reset the SPI device */ xspi->write_fn(XIPIF_V123B_RESET_MASK, - regs_base + XIPIF_V123B_RESETR_OFFSET); - /* Enable the transmit empty interrupt, which we use to determine + regs_base + XIPIF_V123B_RESETR_OFFSET); + /* + * Enable the transmit empty interrupt, which we use to determine * progress on the transmission. */ xspi->write_fn(XSPI_INTR_TX_EMPTY, @@ -178,203 +242,223 @@ static void xspi_init_hw(struct xilinx_spi *xspi) xspi->write_fn(0, regs_base + XIPIF_V123B_DGIER_OFFSET); /* Deselect the slave on the SPI bus */ xspi->write_fn(0xffff, regs_base + XSPI_SSR_OFFSET); - /* Disable the transmitter, enable Manual Slave Select Assertion, + /* + * Disable the transmitter, enable Manual Slave Select Assertion, * put SPI controller into master mode, and enable it */ xspi->write_fn(XSPI_CR_MANUAL_SSELECT | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | XSPI_CR_RXFIFO_RESET, regs_base + XSPI_CR_OFFSET); } -static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) +/** + * xspi_chipselect - Select or deselect the chip select line + * @qspi: Pointer to the spi_device structure + * @is_high: Select(0) or deselect (1) the chip select line + * + */ +static void xspi_chipselect(struct spi_device *qspi, bool is_high) { - struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); - u16 cr; + struct xilinx_spi *xqspi = spi_master_get_devdata(qspi->master); u32 cs; - if (is_on == BITBANG_CS_INACTIVE) { - /* Deselect the slave on the SPI bus */ - xspi->write_fn(xspi->cs_inactive, xspi->regs + XSPI_SSR_OFFSET); - return; + if (is_high) { + /* Deselect the slave */ + xqspi->write_fn(xqspi->cs_inactive, + xqspi->regs + XSPI_SSR_OFFSET); + } else { + cs = xqspi->cs_inactive; + cs ^= BIT(qspi->chip_select); + /* Activate the chip select */ + xqspi->write_fn(cs, xqspi->regs + XSPI_SSR_OFFSET); } +} - /* Set the SPI clock phase and polarity */ - cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_MODE_MASK; - if (spi->mode & SPI_CPHA) - cr |= XSPI_CR_CPHA; - if (spi->mode & SPI_CPOL) - cr |= XSPI_CR_CPOL; - if (spi->mode & SPI_LSB_FIRST) - cr |= XSPI_CR_LSB_FIRST; - if (spi->mode & SPI_LOOP) - cr |= XSPI_CR_LOOP; - xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); - - /* We do not check spi->max_speed_hz here as the SPI clock - * frequency is not software programmable (the IP block design - * parameter) - */ +/** + * xilinx_spi_irq - Interrupt service routine of the SPI controller + * @irq: IRQ number + * @dev_id: Pointer to the xspi 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 xilinx_spi_irq(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct xilinx_spi *xspi = spi_master_get_devdata(dev_id); + u32 ipif_isr; + int status = IRQ_NONE; - cs = xspi->cs_inactive; - cs ^= BIT(spi->chip_select); + /* Get the IPIF interrupts, and clear them immediately */ + ipif_isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET); + xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET); + if (ipif_isr & XSPI_INTR_TX_EMPTY) { + /* Transmission completed */ + xspi->rx_fifo(xspi); + if (xspi->bytes_to_transfer) { + /* There is more data to send */ + xspi->tx_fifo(xspi); + } + status = IRQ_HANDLED; + } + + if (!xspi->bytes_to_receive && !xspi->bytes_to_transfer) { + spi_finalize_current_transfer(master); + /* Disable the interrupts here. */ + xspi->write_fn(0x0, xspi->regs + XIPIF_V123B_DGIER_OFFSET); + } - /* Activate the chip select */ - xspi->write_fn(cs, xspi->regs + XSPI_SSR_OFFSET); + return status; } -/* spi_bitbang requires custom setup_transfer() to be defined if there is a - * custom txrx_bufs(). +/** + * xspi_setup_transfer - Configure SPI controller for specified + * transfer + * @qspi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provides + * information about next transfer setup parameters + * + * Sets the operational mode of QSPI controller for the next QSPI + * transfer. + * + * Return: 0 always */ -static int xilinx_spi_setup_transfer(struct spi_device *spi, - struct spi_transfer *t) +static int xspi_setup_transfer(struct spi_device *qspi, + struct spi_transfer *transfer) { - struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); - - if (spi->mode & SPI_CS_HIGH) - xspi->cs_inactive &= ~BIT(spi->chip_select); + struct xilinx_spi *xqspi = spi_master_get_devdata(qspi->master); + u32 config_reg; + + config_reg = xqspi->read_fn(xqspi->regs + XSPI_CR_OFFSET); + /* Set the QSPI clock phase and clock polarity */ + config_reg &= ~(XSPI_CR_CPHA | XSPI_CR_CPOL); + if (qspi->mode & SPI_CPHA) + config_reg |= XSPI_CR_CPHA; + if (qspi->mode & SPI_CPOL) + config_reg |= XSPI_CR_CPOL; + if (qspi->mode & SPI_LSB_FIRST) + config_reg |= XSPI_CR_LSB_FIRST; + xqspi->write_fn(config_reg, xqspi->regs + XSPI_CR_OFFSET); + + if (qspi->mode & SPI_CS_HIGH) + xqspi->cs_inactive &= ~BIT(qspi->chip_select); else - xspi->cs_inactive |= BIT(spi->chip_select); + xqspi->cs_inactive |= BIT(qspi->chip_select); return 0; } -static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +/** + * xspi_setup - Configure the SPI controller + * @qspi: Pointer to the spi_device structure + * + * Sets the operational mode of QSPI controller for the next QSPI + * transfer. + * + * Return: 0 on success; error value otherwise. + */ +static int xspi_setup(struct spi_device *qspi) { - struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); - int remaining_words; /* the number of words left to transfer */ - bool use_irq = false; - u16 cr = 0; - - /* We get here with transmitter inhibited */ - - xspi->tx_ptr = t->tx_buf; - xspi->rx_ptr = t->rx_buf; - remaining_words = t->len / xspi->bytes_per_word; - - if (xspi->irq >= 0 && remaining_words > xspi->buffer_size) { - u32 isr; - use_irq = true; - /* Inhibit irq to avoid spurious irqs on tx_empty*/ - cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); - xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, - xspi->regs + XSPI_CR_OFFSET); - /* ACK old irqs (if any) */ - isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET); - if (isr) - xspi->write_fn(isr, - xspi->regs + XIPIF_V123B_IISR_OFFSET); - /* Enable the global IPIF interrupt */ - xspi->write_fn(XIPIF_V123B_GINTR_ENABLE, - xspi->regs + XIPIF_V123B_DGIER_OFFSET); - reinit_completion(&xspi->done); - } - - while (remaining_words) { - int n_words, tx_words, rx_words; - u32 sr; - - n_words = min(remaining_words, xspi->buffer_size); - - tx_words = n_words; - while (tx_words--) - xilinx_spi_tx(xspi); - - /* Start the transfer by not inhibiting the transmitter any - * longer - */ - - if (use_irq) { - xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); - wait_for_completion(&xspi->done); - /* A transmit has just completed. Process received data - * and check for more data to transmit. Always inhibit - * the transmitter while the Isr refills the transmit - * register/FIFO, or make sure it is stopped if we're - * done. - */ - xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, - xspi->regs + XSPI_CR_OFFSET); - sr = XSPI_SR_TX_EMPTY_MASK; - } else - sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); - - /* Read out all the data from the Rx FIFO */ - rx_words = n_words; - while (rx_words) { - if ((sr & XSPI_SR_TX_EMPTY_MASK) && (rx_words > 1)) { - xilinx_spi_rx(xspi); - rx_words--; - continue; - } - - sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); - if (!(sr & XSPI_SR_RX_EMPTY_MASK)) { - xilinx_spi_rx(xspi); - rx_words--; - } - } + if (qspi->master->busy) + return -EBUSY; - remaining_words -= n_words; - } + return xspi_setup_transfer(qspi, NULL); +} - if (use_irq) { - xspi->write_fn(0, xspi->regs + XIPIF_V123B_DGIER_OFFSET); - xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); - } +/** + * xspi_start_transfer - Initiates the SPI transfer + * @master: Pointer to the spi_master structure which provides + * information about the controller. + * @qspi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provide information + * about next transfer parameters + * + * This function fills the TX FIFO, starts the SPI transfer, and waits for the + * transfer to be completed. + * + * Return: Number of bytes transferred in the last transfer + */ - return t->len; +static int xspi_start_transfer(struct spi_master *master, + struct spi_device *qspi, + struct spi_transfer *transfer) +{ + struct xilinx_spi *xqspi = spi_master_get_devdata(master); + u32 cr; + + xqspi->tx_ptr = transfer->tx_buf; + xqspi->rx_ptr = transfer->rx_buf; + xqspi->bytes_to_transfer = transfer->len; + xqspi->bytes_to_receive = transfer->len; + + xspi_setup_transfer(qspi, transfer); + cr = xqspi->read_fn(xqspi->regs + XSPI_CR_OFFSET); + /* Enable master transaction inhibit */ + cr |= XSPI_CR_TRANS_INHIBIT; + xqspi->write_fn(cr, xqspi->regs + XSPI_CR_OFFSET); + xqspi->tx_fifo(xqspi); + /* Disable master transaction inhibit */ + cr &= ~XSPI_CR_TRANS_INHIBIT; + xqspi->write_fn(cr, xqspi->regs + XSPI_CR_OFFSET); + xqspi->write_fn(XIPIF_V123B_GINTR_ENABLE, + xqspi->regs + XIPIF_V123B_DGIER_OFFSET); + + return transfer->len; } - -/* This driver supports single master mode only. Hence Tx FIFO Empty - * is the only interrupt we care about. - * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, and Slave Mode - * Fault are not to happen. +/** + * xspi_prepare_transfer_hardware - Prepares hardware for transfer. + * @master: Pointer to the spi_master structure which provides + * information about the controller. + * + * This function enables SPI master controller. + * + * Return: 0 on success; error value otherwise */ -static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) +static int xspi_prepare_transfer_hardware(struct spi_master *master) { - struct xilinx_spi *xspi = dev_id; - u32 ipif_isr; + struct xilinx_spi *xqspi = spi_master_get_devdata(master); + u32 cr; - /* Get the IPIF interrupts, and clear them immediately */ - ipif_isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET); - xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET); + cr = xqspi->read_fn(xqspi->regs + XSPI_CR_OFFSET); + cr |= XSPI_CR_ENABLE; + xqspi->write_fn(cr, xqspi->regs + XSPI_CR_OFFSET); - if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */ - complete(&xspi->done); - } - - return IRQ_HANDLED; + return 0; } -static int xilinx_spi_find_buffer_size(struct xilinx_spi *xspi) +/** + * xspi_unprepare_transfer_hardware - Relaxes hardware after transfer + * @master: Pointer to the spi_master structure which provides + * information about the controller. + * + * This function disables the SPI master controller. + * + * Return: Always 0 + */ +static int xspi_unprepare_transfer_hardware(struct spi_master *master) { - u8 sr; - int n_words = 0; + struct xilinx_spi *xqspi = spi_master_get_devdata(master); + u32 cr; - /* - * Before the buffer_size detection we reset the core - * to make sure we start with a clean state. - */ - xspi->write_fn(XIPIF_V123B_RESET_MASK, - xspi->regs + XIPIF_V123B_RESETR_OFFSET); + cr = xqspi->read_fn(xqspi->regs + XSPI_CR_OFFSET); + cr &= ~XSPI_CR_ENABLE; + xqspi->write_fn(cr, xqspi->regs + XSPI_CR_OFFSET); - /* Fill the Tx FIFO with as many words as possible */ - do { - xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); - sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); - n_words++; - } while (!(sr & XSPI_SR_TX_FULL_MASK)); - - return n_words; + return 0; } -static const struct of_device_id xilinx_spi_of_match[] = { - { .compatible = "xlnx,xps-spi-2.00.a", }, - { .compatible = "xlnx,xps-spi-2.00.b", }, - {} -}; -MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); - +/** + * xilinx_spi_probe - Probe method for the SPI driver + * @pdev: Pointer to the platform_device structure + * + * This function initializes the driver data structures and the hardware. + * + * Return: 0 on success; error value otherwise + */ static int xilinx_spi_probe(struct platform_device *pdev) { struct xilinx_spi *xspi; @@ -382,23 +466,14 @@ static int xilinx_spi_probe(struct platform_device *pdev) struct resource *res; int ret, num_cs = 0, bits_per_word = 8; struct spi_master *master; - u32 tmp; - u8 i; + struct device_node *nc; + u32 tmp, rx_bus_width, fifo_size = 0; pdata = dev_get_platdata(&pdev->dev); - if (pdata) { - num_cs = pdata->num_chipselect; - bits_per_word = pdata->bits_per_word; - } else { - of_property_read_u32(pdev->dev.of_node, "xlnx,num-ss-bits", - &num_cs); - } - - if (!num_cs) { - dev_err(&pdev->dev, - "Missing slave select configuration data\n"); - return -EINVAL; - } + of_property_read_u32(pdev->dev.of_node, "num-cs", + &num_cs); + if (!num_cs) + num_cs = 1; if (num_cs > XILINX_SPI_MAX_CS) { dev_err(&pdev->dev, "Invalid number of spi slaves\n"); @@ -409,18 +484,9 @@ static int xilinx_spi_probe(struct platform_device *pdev) if (!master) return -ENODEV; - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | - SPI_CS_HIGH; - xspi = spi_master_get_devdata(master); - xspi->cs_inactive = 0xffffffff; - xspi->bitbang.master = master; - xspi->bitbang.chipselect = xilinx_spi_chipselect; - xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; - xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; - init_completion(&xspi->done); - + master->dev.of_node = pdev->dev.of_node; + platform_set_drvdata(pdev, master); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); xspi->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(xspi->regs)) { @@ -428,20 +494,28 @@ static int xilinx_spi_probe(struct platform_device *pdev) goto put_master; } - master->bus_num = pdev->id; - master->num_chipselect = num_cs; - master->dev.of_node = pdev->dev.of_node; - - /* - * Detect endianess on the IP via loop bit in CR. Detection - * must be done before reset is sent because incorrect reset - * value generates error interrupt. - * Setup little endian helper functions first and try to use them - * and check if bit was correctly setup or not. - */ + of_property_read_u32(pdev->dev.of_node, "fifo-size", + &fifo_size); + if (!fifo_size) { + dev_err(&pdev->dev, + "Missing fifo size\n"); + return -EINVAL; + } + of_property_read_u32(pdev->dev.of_node, "bits-per-word", + &bits_per_word); + + xspi->rx_bus_width = XSPI_ONE_BITS_PER_WORD; + for_each_available_child_of_node(pdev->dev.of_node, nc) { + ret = of_property_read_u32(nc, "spi-rx-bus-width", + &rx_bus_width); + if (!ret) { + xspi->rx_bus_width = rx_bus_width; + break; + } + } xspi->read_fn = xspi_read32; xspi->write_fn = xspi_write32; - + /* Detect endianness on the IP via loop bit in CR register*/ xspi->write_fn(XSPI_CR_LOOP, xspi->regs + XSPI_CR_OFFSET); tmp = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); tmp &= XSPI_CR_LOOP; @@ -450,15 +524,12 @@ static int xilinx_spi_probe(struct platform_device *pdev) xspi->write_fn = xspi_write32_be; } - master->bits_per_word_mask = SPI_BPW_MASK(bits_per_word); - xspi->bytes_per_word = bits_per_word / 8; - xspi->buffer_size = xilinx_spi_find_buffer_size(xspi); - + xspi->buffer_size = fifo_size; xspi->irq = platform_get_irq(pdev, 0); if (xspi->irq >= 0) { /* Register for SPI Interrupt */ - ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0, - dev_name(&pdev->dev), xspi); + ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, + 0, dev_name(&pdev->dev), master); if (ret) goto put_master; } @@ -466,22 +537,39 @@ static int xilinx_spi_probe(struct platform_device *pdev) /* SPI controller initializations */ xspi_init_hw(xspi); - ret = spi_bitbang_start(&xspi->bitbang); - if (ret) { - dev_err(&pdev->dev, "spi_bitbang_start FAILED\n"); + master->bus_num = pdev->id; + master->num_chipselect = num_cs; + master->setup = xspi_setup; + master->set_cs = xspi_chipselect; + master->transfer_one = xspi_start_transfer; + master->prepare_transfer_hardware = xspi_prepare_transfer_hardware; + master->unprepare_transfer_hardware = xspi_unprepare_transfer_hardware; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + + xspi->bytes_per_word = bits_per_word / 8; + xspi->tx_fifo = xspi_fill_tx_fifo_8; + xspi->rx_fifo = xspi_read_rx_fifo_8; + if (xspi->rx_bus_width == XSPI_RX_ONE_WIRE) { + if (xspi->bytes_per_word == XSPI_TWO_BITS_PER_WORD) { + xspi->tx_fifo = xspi_fill_tx_fifo_16; + xspi->rx_fifo = xspi_read_rx_fifo_16; + } else if (xspi->bytes_per_word == XSPI_FOUR_BITS_PER_WORD) { + xspi->tx_fifo = xspi_fill_tx_fifo_32; + xspi->rx_fifo = xspi_read_rx_fifo_32; + } + } else { + dev_err(&pdev->dev, "Dual/Quad Modes are not supported\n"); goto put_master; } - - dev_info(&pdev->dev, "at 0x%08llX mapped to 0x%p, irq=%d\n", - (unsigned long long)res->start, xspi->regs, xspi->irq); - - if (pdata) { - for (i = 0; i < pdata->num_devices; i++) - spi_new_device(master, pdata->devices + i); + xspi->cs_inactive = 0xffffffff; + ret = spi_register_master(master); + if (ret) { + dev_err(&pdev->dev, "spi_register_master failed\n"); + goto put_master; } - platform_set_drvdata(pdev, master); - return 0; + return ret; put_master: spi_master_put(master); @@ -489,20 +577,28 @@ put_master: return ret; } +/** + * xilinx_spi_remove - Remove method for the SPI 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 Always + */ static int xilinx_spi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct xilinx_spi *xspi = spi_master_get_devdata(master); void __iomem *regs_base = xspi->regs; - spi_bitbang_stop(&xspi->bitbang); - /* Disable all the interrupts just in case */ xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET); /* Disable the global IPIF interrupt */ xspi->write_fn(0, regs_base + XIPIF_V123B_DGIER_OFFSET); - spi_master_put(xspi->bitbang.master); + spi_unregister_master(master); return 0; } @@ -510,6 +606,13 @@ static int xilinx_spi_remove(struct platform_device *pdev) /* work with hotplug and coldplug */ MODULE_ALIAS("platform:" XILINX_SPI_NAME); +static const struct of_device_id xilinx_spi_of_match[] = { + { .compatible = "xlnx,xps-spi-2.00.a", }, + { .compatible = "xlnx,xps-spi-2.00.b", }, + {} +}; +MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); + static struct platform_driver xilinx_spi_driver = { .probe = xilinx_spi_probe, .remove = xilinx_spi_remove,