Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752991AbbFHMza (ORCPT ); Mon, 8 Jun 2015 08:55:30 -0400 Received: from mail-by2on0064.outbound.protection.outlook.com ([207.46.100.64]:10972 "EHLO na01-by2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752182AbbFHMzV (ORCPT ); Mon, 8 Jun 2015 08:55:21 -0400 Authentication-Results: spf=pass (sender IP is 149.199.60.100) smtp.mailfrom=xilinx.com; lists.infradead.org; dkim=none (message not signed) header.d=none; From: Ranjit Abhimanyu Waghmode To: Shubhrajyoti Datta CC: "robh+dt@kernel.org" , "pawel.moll@arm.com" , "mark.rutland@arm.com" , "ijc+devicetree@hellion.org.uk" , Kumar Gala , Michal Simek , Soren Brinkmann , Mark Brown , "devicetree@vger.kernel.org" , Harini Katakam , Linux Kernel Mailing List , "linux-spi@vger.kernel.org" , Punnaiah Choudary Kalluri , "ran27jit@gmail.com" , "linux-arm-kernel@lists.infradead.org" Subject: RE: [LINUX RFC V2 2/2] spi: Add support for Zynq Ultrascale+ MPSoC GQSPI controller Thread-Topic: [LINUX RFC V2 2/2] spi: Add support for Zynq Ultrascale+ MPSoC GQSPI controller Thread-Index: AQHQn5D7aOIT7dmJCkitvCiH9fm+k52ejv6AgAP5ReA= Date: Mon, 8 Jun 2015 12:55:13 +0000 Message-ID: <7CFCFE83B8145347A1D424EC939F1C3CA71150@XAP-PVEXMBX01.xlnx.xilinx.com> References: <1433509653-15142-1-git-send-email-ranjit.waghmode@xilinx.com> <1433509653-15142-2-git-send-email-ranjit.waghmode@xilinx.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [172.23.97.182] Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 X-RCIS-Action: ALLOW X-TM-AS-Product-Ver: IMSS-7.1.0.1224-8.0.0.1202-21600.005 X-TM-AS-User-Approved-Sender: Yes;Yes X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11FD025;1:tzqmb9yh5ZFglYw/Bocm9QSdbxVDtxL/e3f7Xe4bkZoVo1ZyftmnAjNrfeWx+wt8Ej+X807W1pBNUjHMoIIAZuosfC5behwMIjSMgNvfxlL4lx7Ic6CAQI2kJ3YIXoH5d97QbWdmP0VJJ7iUjhsfZZEZfqgr4bgqDb9+P4NfQ39FUAIthmF7ibSBJy2vv5iVXdItKOl0mcVju2pHyKPJvkEw1cTo/7vC6rllF9D7Nprt8yWg493fM+18/BTTEXpIoQMpFoLp7se0XK9evIb9WfDFV+OuWThVBbfRnl+uOhtlCrz4sEgmj8pCn1KlB4vY X-Forefront-Antispam-Report: CIP:149.199.60.100;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10009020)(6009001)(438002)(189002)(51704005)(199003)(63266004)(86362001)(19580395003)(2950100001)(2900100001)(106466001)(2656002)(87936001)(50466002)(47776003)(102836002)(33656002)(54356999)(76176999)(46102003)(50986999)(23676002)(19580405001)(77156002)(62966003)(55846006)(92566002)(6806004)(1411001)(106116001)(110136002)(5001960100002)(5250100002)(5890100001)(189998001)(107986001)(217873001);DIR:OUT;SFP:1101;SCL:1;SRVR:BL2FFO11HUB049;H:xsj-pvapsmtpgw02;FPR:;SPF:Pass;MLV:sfv;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11HUB049;2:iu26alcWPP2hswGZda0xLMrAMk3WObCbms/wW/y03ZpAriFPRmsAMVDky9Oad51G;2:JZ4ATSYsgPsYdzNrZ+A7m2x178Q4Zt6j1FsQlsNfgxJ3WsucvLPq2EiThdLCfMj/8ayl181ld0cbssjndL/62wCy2rEqIqZEenEHGuGLtY1oB4SHf0EZ7gSYvBlSdqn4n4pGJNSwqcHF+Qy2fEukXulBOAEGpkhzUYp714xDx85N/VYEYxC81rFScMu29D9t134lHCkxYgUEM4skIc5xMKguqUDOESmNw0tIy1dR2XFFTVbFw7JT5hhiksBrOQQB;6:+ORayoAZapuZ1jhlYR0E9pEIiuFY88ySlJHHD00Cqsq2LXcKJuDS95a4rpPIDyFeHxn2PLEGmzB4kXa+6JmUcC6/1OdHSqhb1cMvtphSMSJEcVb4lthCV89aD3gsQlhbiwNJFD/9DyPSf3l8HO01/UvqCWUtgB2TrqkPWEMXwKKXW0tya7CAlD0fSkDCgYuaBtjiwjobaGMmz+ItdyHvKTwZV61fNw4JcVe/3Fl4/coyewWA2AfPmCi8Bv9/SalKrv3V9Fnva4tDHxjDvCyOWCPZdzSRQKPzoZbE4rLPkNSeiJ2lbIWDz3/gJxMKvyrSueXJ30b54WurV3IgmmkP7eFJD71BCyMD0kjTSEZEE2BVcJwja/PGOWdtDtDkOQCm3PTQgkky43P2c+xxyknL9gjiF5A52opQlvpJz27DFwwt0aMQMZUcDz4HgrxMqPiCPmDYpwY4qdLP3dVTnfV9M4NXKnSdPZepQ/Zwem2/0hNdts1mbIM81G9bRrajPBwb X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BL2FFO11HUB049; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(520003)(5005006)(3002001);SRVR:BL2FFO11HUB049;BCL:0;PCL:0;RULEID:;SRVR:BL2FFO11HUB049; X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11HUB049;3:9e0eYh5X5tS6nsjev/52IGFPbDq4vco+rLKBMzr8cr+w0gJ0Pfc9hl7sUDXgwTtQr0mNpkYcr6PKqE5kw5TzbWRr1SjIiSkpgv5/xl1FinoozA9UlavZyZRVSzBdZdGXlfN3lCRuny43Sn2lX7ObbxqNijTZLc3cfZYE8JVjviCpJ0vcTt+ZEvowIpDsGph2FKd6DauLlxY+lS3wEkzqmY80IYd6vUyV48iWGpMjeWgTOoaP5Ix0wWqruywLG6wi/jWv3tZKyClG9jRV+Mr1Pesn1nLWmuL64o0kCt6ztOFk+FejwvfH3KlN6Cksns0i X-Forefront-PRVS: 060166847D X-Microsoft-Exchange-Diagnostics: =?utf-8?B?MTtCTDJGRk8xMUhVQjA0OTs5OkhMWERsc1Q0Q3dyak1wYnhjYXNQN0JqVEl4?= =?utf-8?B?MTh6SXJBb25pNlRlR0pxT3UzUEw3VHZJeW9qODhlZzQ4bGZYVlFIU2wrcmo0?= =?utf-8?B?UlluRmk0VXBtM2hsYmpRdFo1VGU0L3RCYjBxc3Babi9Xa3hhUnNyRmdhL0JD?= =?utf-8?B?SWdxUkYwRm9oM08yOW9pZzhPVzFwNGc1and5MmxOM0Vnb2V6YThNcW5QaE9p?= =?utf-8?B?VGRJdU1KZytSUzhSTDRpcWU4eWl1OGkrL3ZGVVZralREdXYrTTNyQ3U5TkU5?= =?utf-8?B?MkMxNkhsN2VtNFgvRFVqTStLM0h5bUxobG8yNkJhQzI5VEdKamJxZEJYRHc1?= =?utf-8?B?Wi9NTDdOQUtYbURqZUppSHhzNElwaFFNbm5zZGxTOVRVSjBzd1hzOGhFZWt5?= =?utf-8?B?UUJJMXdDNTRiZVBKeitiTlJXSjZNYm5JVVljZGpGM09xSFVIeDFwa2RvMzlr?= =?utf-8?B?eHR0cGIvRml1eHlRRWJYZHZwQUZPcjRBRXd4MWtSNFdJTFBVbVQvdXNGeWc3?= =?utf-8?B?cEl4ZzNNamxNV1p0azROeGsxbWZmSTMwcWFWT2pXT2p5QkwybkwzOEFnNTNL?= =?utf-8?B?LzI0SGVNcXczSGlYaVJ4MTVPS1FKZUFKeUI0YmhGUU5zNStvK3ZlSHJ4Wlk2?= =?utf-8?B?UHQxcXpqbXNNdHR2TGNXWWdKRlF5WjZVY3lqb3RrenpmRi8zcEs1TjZxQkVh?= =?utf-8?B?VitVME1CbGQrdGl1YlcrdkYvOXdUYm8rbjRiT1l2aW83YnpJOEhKR3RsZW8r?= =?utf-8?B?V0VMSGI4N3JvSCttOFdkSURxclRTNlphVldPUW5OK1lIbDlpZzFLaXVoY2pr?= =?utf-8?B?a1QxVGlnQ0F3MytWLys4NTRzVnFBWWRMK3pHMWdHR1RSdVloSHNoV3hxMFlk?= =?utf-8?B?cEM5TGhjOSszOG9KZnkwUVQ4U3R3QVZ3L21zZDI3MDQydnMzN1QwR1BnMjZm?= =?utf-8?B?bXNJd3FvQVViQ2JGZkU5N054WkJhNVVQdFVZVlRhWTNYbmJjaWU1ZFkzQmta?= =?utf-8?B?ajR6eWFMWmZuOU5xSXpZZ3Z1NGg5WThsRDZITU95eVhGZ2xaeGxORTZicnN2?= =?utf-8?B?T2NTWi9GTEhRaVRTZDl6M1RGa0hvaUVacVZoMjNiOXRjSThlZnphWWFVb1Nt?= =?utf-8?B?RjFvWnRaem9KbmpzK0tmcXYxenFaamhUZjJEY2JvcXUxMzZLWEQ2cVg2T0ZT?= =?utf-8?B?cGMrdTh4NnA0Z3p6c09OeHFYSjBLREJpcHlweThnN1pPcEw1d0orbUF0UE0y?= =?utf-8?B?RldFZEk2N3RqZkk0TjNadWN6N3ZKTmhIK01UTmI4WjdyNFpkYUxOMTdnbmNQ?= =?utf-8?B?L2JtRmxiS2J3a3c9PQ==?= X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11HUB049;3:mTDoRGvY7io8Ea/c/9QXAIRuUZBzcoNzpmCizALKNhwb1dIJhUQtiBJ2DrXUGJHiV8G22DqwcqBYVhP5yDMV42xXAjg1kL+xivyinSxh4EEBJzLwR2IhdBipE2lBzGl3os7DaIhJ03oABpGwPnv0Cg==;10:/NIQvO8e+8EDFWWOuwPhOZ56eSwaHMJbfYcMnEeHKLSKLF9Z/ssnoOKJ6OQ+x5OJ15OTy9DJAW+627sxtlqpTd1atYBXJyAgp9WlmICmkBY= X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 08 Jun 2015 12:55:18.5118 (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: BL2FFO11HUB049 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from base64 to 8bit by nfs id t58Ctcq3021056 Content-Length: 30340 Lines: 749 Hi, > > > > + */ > > +static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool > > +is_high) { > > + struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master); > > + u32 genfifoentry = 0x0, statusreg, timeout; > > + > > + genfifoentry |= GQSPI_GENFIFO_MODE_SPI; > > + genfifoentry |= xqspi->genfifobus; > > + > > + if (!is_high) { > > + genfifoentry |= xqspi->genfifocs; > > + genfifoentry |= GQSPI_GENFIFO_CS_SETUP; > > + } else { > > + genfifoentry |= GQSPI_GENFIFO_CS_HOLD; > > + } > > + > > + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry); > > + > > + /* Dummy generic FIFO entry */ > > + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0); > > + > > + /* Manually start the generic FIFO command */ > > + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, > > + zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | > > + GQSPI_CFG_START_GEN_FIFO_MASK); > > + timeout = 10000; > > + /* Wait until the generic FIFO command is empty */ > > + do { > > + statusreg = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST); > > + timeout--; > > Can this be not busy. Ok, will try to see the other way by which it can be implemented. > > > + } while (!(statusreg & > > + GQSPI_ISR_GENFIFOEMPTY_MASK) && > > + (statusreg & GQSPI_ISR_TXEMPTY_MASK) && timeout); > > + if (!timeout) > > + dev_err(xqspi->dev, "Chip select timed out\n"); } > > + > > +/** > > + * zynqmp_qspi_setup_transfer: Configure QSPI 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 and > > + * sets the requested clock frequency. > > + * > > + * Return: Always 0 > > + * > > + * Note: > > + * If the requested frequency is not an exact match with what can be > > + * obtained using the pre-scalar 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 zynqmp_qspi_setup_transfer(struct spi_device *qspi, > > + struct spi_transfer *transfer) { > > + struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master); > > + ulong clk_rate; > > + u32 config_reg, req_hz, baud_rate_val = 0; > > + > > + if (transfer) > > + req_hz = transfer->speed_hz; > > + else > > + req_hz = qspi->max_speed_hz; > > + > > + /* Set the clock frequency */ > > + /* If req_hz == 0, default to lowest speed */ > > + clk_rate = clk_get_rate(xqspi->refclk); > > + > > + while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) && > > + (clk_rate / > > + (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > req_hz) > > + baud_rate_val++; > > + > > + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); > > + > > + /* Set the QSPI clock phase and clock polarity */ > > + config_reg &= (~GQSPI_CFG_CLK_PHA_MASK) & > > + (~GQSPI_CFG_CLK_POL_MASK); > > + > > + if (qspi->mode & SPI_CPHA) > > + config_reg |= GQSPI_CFG_CLK_PHA_MASK; > > + if (qspi->mode & SPI_CPOL) > > + config_reg |= GQSPI_CFG_CLK_POL_MASK; > > + > > + config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; > > + config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT); > > + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); > > + return 0; > > +} > > + > > +/** > > + * zynqmp_qspi_setup: Configure the QSPI controller > > + * @qspi: 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 Always > > doesnt seem to be true Will update this. > > > + */ > > +static int zynqmp_qspi_setup(struct spi_device *qspi) { > > + if (qspi->master->busy) > > + return -EBUSY; > > + return 0; > > +} > > + > > +/** > > + * zynqmp_qspi_filltxfifo: Fills the TX FIFO as long as there is room in > > + * the FIFO or the bytes required to be > > + * transmitted. > > + * @xqspi: Pointer to the zynqmp_qspi structure > > + * @size: Number of bytes to be copied from TX buffer to TX FIFO > > + */ > > +static void zynqmp_qspi_filltxfifo(struct zynqmp_qspi *xqspi, int > > +size) { > > + u32 count = 0, intermediate; > > + > > + while ((xqspi->bytes_to_transfer > 0) && (count < size)) { > > + memcpy(&intermediate, xqspi->txbuf, 4); > > + zynqmp_gqspi_write(xqspi, GQSPI_TXD_OFST, > > + intermediate); > > + > > + if (xqspi->bytes_to_transfer >= 4) { > > + xqspi->txbuf += 4; > > + xqspi->bytes_to_transfer -= 4; > > + } else { > > + xqspi->txbuf += xqspi->bytes_to_transfer; > > + xqspi->bytes_to_transfer = 0; > > + } > > + count++; > > + } > > +} > > + > > +/** > > + * zynqmp_qspi_readrxfifo: Fills the RX FIFO as long as there is room in > > + * the FIFO. > > + * @xqspi: Pointer to the zynqmp_qspi structure > > + * @size: Number of bytes to be copied from RX buffer to RX FIFO > > + */ > > +static void zynqmp_qspi_readrxfifo(struct zynqmp_qspi *xqspi, u32 > > +size) { > > + ulong data; > > + int count = 0; > > + > > + while ((count < size) && (xqspi->bytes_to_receive > 0)) { > > + if (xqspi->bytes_to_receive >= 4) { > > + (*(u32 *) xqspi->rxbuf) = > > + zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST); > > + xqspi->rxbuf += 4; > > + xqspi->bytes_to_receive -= 4; > > + count += 4; > > + } else { > > + data = zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST); > > + count += xqspi->bytes_to_receive; > > + zynqmp_qspi_copy_read_data(xqspi, data, > > + xqspi->bytes_to_receive); > > + xqspi->bytes_to_receive = 0; > > + } > > + } > > +} > > + > > +/** > > + * zynqmp_process_dma_irq: Handler for DMA done interrupt of QSPI > > + * controller > > + * @xqspi: zynqmp_qspi instance pointer > > + * > > + * This function handles DMA interrupt only. > > + */ > > +static void zynqmp_process_dma_irq(struct zynqmp_qspi *xqspi) { > > + u32 config_reg, genfifoentry; > > + > > + dma_unmap_single(xqspi->dev, xqspi->dma_addr, > > + xqspi->dma_rx_bytes, DMA_FROM_DEVICE); > > + xqspi->rxbuf += xqspi->dma_rx_bytes; > > + xqspi->bytes_to_receive -= xqspi->dma_rx_bytes; > > + xqspi->dma_rx_bytes = 0; > > + > > + /* Disabling the DMA interrupts */ > why? Since logic is shifting to IO mode for remaining bytes to transfer, we are not depending on DMA. > > > + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_DIS_OFST, > > + > > + GQSPI_QSPIDMA_DST_I_EN_DONE_MASK); > > + > > + if (xqspi->bytes_to_receive > 0) { > > + /* Switch to IO mode,for remaining bytes to receive */ > > + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); > > + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; > > + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, > > + config_reg); > > + > > + /* Initiate the transfer of remaining bytes */ > > + genfifoentry = xqspi->genfifoentry; > > + genfifoentry |= xqspi->bytes_to_receive; > > + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, > > + genfifoentry); > > + > > + /* Dummy generic FIFO entry */ > > + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0); > > + > > + /* Manual start */ > > + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, > > + (zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | > > + GQSPI_CFG_START_GEN_FIFO_MASK)); > > + > > + /* Enable the RX interrupts for IO mode */ > > + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, > > + GQSPI_IER_GENFIFOEMPTY_MASK | > > + GQSPI_IER_RXNEMPTY_MASK | > > + GQSPI_IER_RXEMPTY_MASK); > > + } > > +} > > + > > +/** > > + * zynqmp_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 zynqmp_qspi_irq(int irq, void *dev_id) { > > + struct spi_master *master = dev_id; > > + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); > > + int ret = IRQ_NONE; > > + u32 status, mask, dma_status = 0; > > + > > + status = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST); > > + zynqmp_gqspi_write(xqspi, GQSPI_ISR_OFST, status); > > + mask = (status & ~(zynqmp_gqspi_read(xqspi, > > + GQSPI_IMASK_OFST))); > > + > > + /* Read and clear DMA status */ > > + if (xqspi->mode == GQSPI_MODE_DMA) { > > + dma_status = > > + zynqmp_gqspi_read(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST); > > + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST, > > + dma_status); > > + } > > + > > + if (mask & GQSPI_ISR_TXNOT_FULL_MASK) { > > + zynqmp_qspi_filltxfifo(xqspi, GQSPI_TX_FIFO_FILL); > > + ret = IRQ_HANDLED; > > + } > > + > > + if (dma_status & GQSPI_QSPIDMA_DST_I_STS_DONE_MASK) { > > + zynqmp_process_dma_irq(xqspi); > > + ret = IRQ_HANDLED; > > + } else if (!(mask & GQSPI_IER_RXEMPTY_MASK) && > > + (mask & GQSPI_IER_GENFIFOEMPTY_MASK)) { > > + zynqmp_qspi_readrxfifo(xqspi, GQSPI_RX_FIFO_FILL); > > + ret = IRQ_HANDLED; > > + } > > + > > + if ((xqspi->bytes_to_receive == 0) && (xqspi->bytes_to_transfer == 0) > > + && ((status & GQSPI_IRQ_MASK) == GQSPI_IRQ_MASK)) { > > + zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, > GQSPI_ISR_IDR_MASK); > > + spi_finalize_current_transfer(master); > > + ret = IRQ_HANDLED; > > + } > > + return ret; > > +} > > + > > +/** > > + * zynqmp_qspi_selectspimode: Selects SPI mode - x1 or x2 or x4. > > + * @xqspi: xqspi is a pointer to the GQSPI instance > > + * @spimode: spimode - SPI or DUAL or QUAD. > > + * Return: Mask to set desired SPI mode in GENFIFO entry. > > + */ > > +static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi, > > + u8 spimode) { > > + u32 mask = 0; > > + > > + switch (spimode) { > > + case GQSPI_SELECT_MODE_DUALSPI: > > + mask = GQSPI_GENFIFO_MODE_DUALSPI; > > + break; > > + case GQSPI_SELECT_MODE_QUADSPI: > > + mask = GQSPI_GENFIFO_MODE_QUADSPI; > > + break; > > + case GQSPI_SELECT_MODE_SPI: > > + mask = GQSPI_GENFIFO_MODE_SPI; > > + break; > > + default: > > + dev_warn(xqspi->dev, "Invalid SPI mode\n"); > > + } > > + > > + return mask; > > +} > > + > > +/** > > + * zynq_qspi_setuprxdma: This function sets up the RX DMA operation > > + * @xqspi: xqspi is a pointer to the GQSPI instance. > > + */ > > +static void zynq_qspi_setuprxdma(struct zynqmp_qspi *xqspi) { > > + u32 rx_bytes, rx_rem, config_reg; > > + dma_addr_t addr; > > + u64 dma_align = (u64)(uintptr_t)xqspi->rxbuf; > > + > > + if ((xqspi->bytes_to_receive < 8) || > > + ((dma_align & GQSPI_DMA_UNALIGN) != 0x0)) { > > + /* Setting to IO mode */ > > + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); > > + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; > > + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); > > + xqspi->mode = GQSPI_MODE_IO; > > + xqspi->dma_rx_bytes = 0; > > + return; > > + } > > + > > + rx_rem = xqspi->bytes_to_receive % 4; > > + rx_bytes = (xqspi->bytes_to_receive - rx_rem); > > + > > + addr = dma_map_single(xqspi->dev, (void *)xqspi->rxbuf, > > + rx_bytes, DMA_FROM_DEVICE); > > + if (dma_mapping_error(xqspi->dev, addr)) > > + dev_err(xqspi->dev, "ERR:rxdma:memory not mapped\n"); > > + > > + xqspi->dma_rx_bytes = rx_bytes; > > + xqspi->dma_addr = addr; > > + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_OFST, > > + (u32)(addr & 0xffffffff)); > > + addr = ((addr >> 16) >> 16); > > + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_MSB_OFST, > > + ((u32)addr) & 0xfff); > > + > > + /* Enabling the DMA mode */ > > + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); > > + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; > > + config_reg |= GQSPI_CFG_MODE_EN_DMA_MASK; > > + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); > > + > > + /* Switch to DMA mode */ > > + xqspi->mode = GQSPI_MODE_DMA; > > + > > + /* Write the number of bytes to transfer */ > > + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_SIZE_OFST, > > +rx_bytes); } > > + > > +/** > > + * zynqmp_qspi_txrxsetup: This function checks the TX/RX buffers in > > + * the transfer and sets up the GENFIFO entries, > > + * TX FIFO as required. > > + * @xqspi: xqspi is a pointer to the GQSPI instance. > > + * @transfer: It is a pointer to the structure containing transfer data. > > + * @genfifoentry: genfifoentry is pointer to the variable in which > > + * GENFIFO mask is returned to calling function > > + */ > > +static void zynqmp_qspi_txrxsetup(struct zynqmp_qspi *xqspi, > > + struct spi_transfer *transfer, > > + u32 *genfifoentry) { > > + u32 config_reg; > > + > > + /* Transmit */ > > + if ((xqspi->txbuf != NULL) && (xqspi->rxbuf == NULL)) { > > + /* Setup data to be TXed */ > > + *genfifoentry &= ~GQSPI_GENFIFO_RX; > > + *genfifoentry |= GQSPI_GENFIFO_DATA_XFER; > > + *genfifoentry |= GQSPI_GENFIFO_TX; > > + *genfifoentry |= > > + zynqmp_qspi_selectspimode(xqspi, transfer->tx_nbits); > > + xqspi->bytes_to_transfer = transfer->len; > > + if (xqspi->mode == GQSPI_MODE_DMA) { > > + config_reg = zynqmp_gqspi_read(xqspi, > > + GQSPI_CONFIG_OFST); > > + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; > > + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, > > + config_reg); > > + xqspi->mode = GQSPI_MODE_IO; > > + } > > + zynqmp_qspi_filltxfifo(xqspi, GQSPI_TXD_DEPTH); > > + /* Discard RX data */ > > + xqspi->bytes_to_receive = 0; > > + } else if ((xqspi->txbuf == NULL) && (xqspi->rxbuf != NULL)) { > > + /* Receive */ > > + > > + /* TX auto fill */ > > + *genfifoentry &= ~GQSPI_GENFIFO_TX; > > + /* Setup RX */ > > + *genfifoentry |= GQSPI_GENFIFO_DATA_XFER; > > + *genfifoentry |= GQSPI_GENFIFO_RX; > > + *genfifoentry |= > > + zynqmp_qspi_selectspimode(xqspi, transfer->rx_nbits); > > + xqspi->bytes_to_transfer = 0; > > + xqspi->bytes_to_receive = transfer->len; > > + zynq_qspi_setuprxdma(xqspi); > > + } > > Not a comment rather a query what happens in case of both rx and tx. SPI core is sending either TX or RX transfer at a time. > > > > +} > > + > > +/** > > + * zynqmp_qspi_start_transfer: Initiates the QSPI 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 QSPI transfer, and > > +waits for the > > + * transfer to be completed. > > + * > > + * Return: Number of bytes transferred in the last transfer > > + */ > > +static int zynqmp_qspi_start_transfer(struct spi_master *master, > > + struct spi_device *qspi, > > + struct spi_transfer *transfer) { > > + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); > > + u32 genfifoentry = 0x0, transfer_len; > > + > > + xqspi->txbuf = transfer->tx_buf; > > + xqspi->rxbuf = transfer->rx_buf; > > + > > + zynqmp_qspi_setup_transfer(qspi, transfer); > > + > > + genfifoentry |= xqspi->genfifocs; > > + genfifoentry |= xqspi->genfifobus; > > + > > + zynqmp_qspi_txrxsetup(xqspi, transfer, &genfifoentry); > > + > > + if (xqspi->mode == GQSPI_MODE_DMA) > > + transfer_len = xqspi->dma_rx_bytes; > > + else > > + transfer_len = transfer->len; > > + > > + xqspi->genfifoentry = genfifoentry; > > + if ((transfer_len) < GQSPI_GENFIFO_IMM_DATA_MASK) { > > + genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK; > > + genfifoentry |= transfer_len; > > + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry); > > + } else { > > + int tempcount = transfer_len; > > + u32 exponent = 8; /* 2^8 = 256 */ > > + u8 imm_data = tempcount & 0xFF; > > + > > + tempcount &= ~(tempcount & 0xFF); > > + /* Immediate entry */ > > + if (tempcount != 0) { > > + /* Exponent entries */ > > + genfifoentry |= GQSPI_GENFIFO_EXP; > > + while (tempcount != 0) { > > + if (tempcount & GQSPI_GENFIFO_EXP_START) { > > + genfifoentry &= > > + ~GQSPI_GENFIFO_IMM_DATA_MASK; > > + genfifoentry |= exponent; > > + zynqmp_gqspi_write(xqspi, > > + GQSPI_GEN_FIFO_OFST, > > + genfifoentry); > > + } > > + tempcount = tempcount >> 1; > > + exponent++; > > + } > > + } > > + if (imm_data != 0) { > > + genfifoentry &= ~GQSPI_GENFIFO_EXP; > > + genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK; > > + genfifoentry |= (u8) (imm_data & 0xFF); > > + zynqmp_gqspi_write(xqspi, > > + GQSPI_GEN_FIFO_OFST, genfifoentry); > > + } > > + } > > + > > + if ((xqspi->mode == GQSPI_MODE_IO) && > > + (xqspi->rxbuf != NULL)) { > > + /* Dummy generic FIFO entry */ > > + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0); > > + } > > + > > + /* Since we are using manual mode */ > > + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, > > + zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | > > + GQSPI_CFG_START_GEN_FIFO_MASK); > > + > > + if (xqspi->txbuf != NULL) > > + /* Enable interrupts for TX */ > > + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, > > + GQSPI_IER_TXEMPTY_MASK | > > + GQSPI_IER_GENFIFOEMPTY_MASK | > > + GQSPI_IER_TXNOT_FULL_MASK); > > + > > + if (xqspi->rxbuf != NULL) { > > + /* Enable interrupts for RX */ > > + if (xqspi->mode == GQSPI_MODE_DMA) { > > + /* Enable DMA interrupts */ > > + zynqmp_gqspi_write(xqspi, > > + GQSPI_QSPIDMA_DST_I_EN_OFST, > > + GQSPI_QSPIDMA_DST_I_EN_DONE_MASK); > > + } else { > > + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, > > + GQSPI_IER_GENFIFOEMPTY_MASK | > > + GQSPI_IER_RXNEMPTY_MASK | > > + GQSPI_IER_RXEMPTY_MASK); > > + } > > + } > > + > > + return transfer->len; > > +} > > + > > +/** > > + * zynqmp_qspi_suspend: Suspend method for the QSPI driver > > + * @_dev: Address of the platform_device structure > > + * > > + * This function stops the QSPI driver queue and disables the QSPI > > +controller > > + * > > + * Return: Always 0 > > + */ > > +static int __maybe_unused zynqmp_qspi_suspend(struct device *dev) { > > + struct platform_device *pdev = container_of(dev, > > + struct platform_device, > > + dev); > > + struct spi_master *master = platform_get_drvdata(pdev); > > + > > + spi_master_suspend(master); > > + > > + zynqmp_unprepare_transfer_hardware(master); > > + > > + return 0; > > +} > > + > > +/** > > + * zynqmp_qspi_resume: Resume method for the QSPI driver > > + * @dev: Address of the platform_device structure > > + * > > + * The function starts the QSPI driver queue and initializes the QSPI > > + * controller > > + * > > + * Return: 0 on success and error value on error > > + */ > > +static int __maybe_unused zynqmp_qspi_resume(struct device *dev) { > > + struct platform_device *pdev = container_of(dev, > > + struct platform_device, > > + dev); > > + struct spi_master *master = platform_get_drvdata(pdev); > > + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); > > + int ret = 0; > > is the initialisation required I will update this. > > > + > > + ret = clk_enable(xqspi->pclk); > > + if (ret) { > > + dev_err(dev, "Cannot enable APB clock.\n"); > > + return ret; > > + } > > + > > + ret = clk_enable(xqspi->refclk); > > + if (ret) { > > + dev_err(dev, "Cannot enable device clock.\n"); > > + clk_disable(xqspi->pclk); > > + return ret; > > + } > > + > > + spi_master_resume(master); > > + > > + return 0; > > +} > > + > > +static SIMPLE_DEV_PM_OPS(zynqmp_qspi_dev_pm_ops, > zynqmp_qspi_suspend, > > + zynqmp_qspi_resume); > > + > > +/** > > + * zynqmp_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 zynqmp_qspi_probe(struct platform_device *pdev) { > > + int ret = 0; > > + struct spi_master *master; > > + struct zynqmp_qspi *xqspi; > > + struct resource *res; > > + struct device *dev = &pdev->dev; > > + > > + master = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); > > + if (!master) > > + return -ENOMEM; > > + > > + xqspi = spi_master_get_devdata(master); > > + master->dev.of_node = pdev->dev.of_node; > > + platform_set_drvdata(pdev, master); > > + > > + 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->dev = dev; > > + xqspi->pclk = devm_clk_get(&pdev->dev, "pclk"); > > + if (IS_ERR(xqspi->pclk)) { > > + dev_err(dev, "pclk clock not found.\n"); > > + ret = PTR_ERR(xqspi->pclk); > > + goto remove_master; > > + } > > + > > + xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk"); > > + if (IS_ERR(xqspi->refclk)) { > > + dev_err(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(dev, "Unable to enable APB clock.\n"); > > + goto remove_master; > > + } > > + > > + ret = clk_prepare_enable(xqspi->refclk); > > + if (ret) { > > + dev_err(dev, "Unable to enable device clock.\n"); > > + goto clk_dis_pclk; > > + } > > + > > + /* QSPI controller initializations */ > > + zynqmp_qspi_init_hw(xqspi); > > + > > + xqspi->irq = platform_get_irq(pdev, 0); > > + if (xqspi->irq <= 0) { > > + ret = -ENXIO; > > + dev_err(dev, "irq resource not found\n"); > > + goto remove_master; > > + } > > + ret = devm_request_irq(&pdev->dev, xqspi->irq, zynqmp_qspi_irq, > > + 0, pdev->name, master); > > + if (ret != 0) { > > + ret = -ENXIO; > > + dev_err(dev, "request_irq failed\n"); > > + goto remove_master; > > + } > > Shouldnt the clk disable be called here. Will update this too. > > > > + if (master->dev.parent == NULL) > > + master->dev.parent = &master->dev; > > + > > + ret = spi_register_master(master); > > + if (ret) > > + goto clk_dis_all; > > + > > + return 0; > > + > > +clk_dis_all: > > + clk_disable_unprepare(xqspi->refclk); > > +clk_dis_pclk: > > + clk_disable_unprepare(xqspi->pclk); > > +remove_master: > > + spi_master_put(master); > > + > > + return ret; > > +} > > + > > +/** > > + * zynqmp_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 Always > > + */ > > +static int zynqmp_qspi_remove(struct platform_device *pdev) { > > + struct spi_master *master = platform_get_drvdata(pdev); > > + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); > > + > > + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0); > > + clk_disable_unprepare(xqspi->refclk); > > + clk_disable_unprepare(xqspi->pclk); > > + > > + spi_unregister_master(master); > > + > > + return 0; > > +} > > + > > +static const struct of_device_id zynqmp_qspi_of_match[] = { > > + { .compatible = "xlnx,zynqmp-qspi-1.0", }, > > + { /* End of table */ } > > +}; > > + > > +MODULE_DEVICE_TABLE(of, zynqmp_qspi_of_match); > > + > > +static struct platform_driver zynqmp_qspi_driver = { > > + .probe = zynqmp_qspi_probe, > > + .remove = zynqmp_qspi_remove, > > + .driver = { > > + .name = "zynqmp-qspi", > > + .of_match_table = zynqmp_qspi_of_match, > > + .pm = &zynqmp_qspi_dev_pm_ops, > > + }, > > +}; > > + > > +module_platform_driver(zynqmp_qspi_driver); > > + > > +MODULE_AUTHOR("Xilinx, Inc."); > > +MODULE_DESCRIPTION("Xilinx Zynqmp QSPI driver"); > > +MODULE_LICENSE("GPL"); > > -- > > 2.1.2 > > Thanks for your review. Regards, Ranjit Abhimanyu Waghmode, ranjit.waghmode@xilinx.com This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately. ????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?