From patchwork Wed Jan 25 17:49:28 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cyrille Pitchen X-Patchwork-Id: 719779 X-Patchwork-Delegate: cyrille.pitchen@atmel.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3v7t0f1m0Pz9s3T for ; Thu, 26 Jan 2017 04:52:02 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1cWRjk-0000aC-Pt; Wed, 25 Jan 2017 17:52:00 +0000 Received: from exsmtp01.microchip.com ([198.175.253.37] helo=email.microchip.com) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1cWRhl-0006fB-7y for linux-mtd@lists.infradead.org; Wed, 25 Jan 2017 17:49:59 +0000 Received: from tenerife.corp.atmel.com (10.10.76.4) by CHN-SV-EXCH01.mchp-main.com (10.10.76.37) with Microsoft SMTP Server id 14.3.181.6; Wed, 25 Jan 2017 10:49:33 -0700 From: Cyrille Pitchen To: Subject: [PATCH 3/3] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols Date: Wed, 25 Jan 2017 18:49:28 +0100 Message-ID: X-Mailer: git-send-email 2.7.4 In-Reply-To: References: MIME-Version: 1.0 X-Brightmail-Tracker: H4sIAAAAAAAAC+NgFtrHKsWRWlGSWpSXmKPExsXCxeXDoqv7tCPCYNv83QtYLQ68WMhiceTCWmaLe9/PM1tc3jWHzWJ30zJ2i6N77jFbTN75htGB3WPBr60sHk82XWT02DnrLrvH5iX1HjfnFXp83iQXwBbFmpmXlF+RwJqxceU+5oLV1hV9fZuZGxjP6ncxcnEICaxjlLg+bylTFyMnB5uAocTbB0dZQWwRAUmJDScuMIIUMQscZ5RYsOI1G0hCWMBPYvfHP0ANHBwsAqoSE7qqQcK8AvESv2+8BZsjISAncfNcJzOIzSlgK3Gk+xNYq5CAjcSUcydZIeoFJU7OfMICYjMLSEgcfPGCGaJGTWJhywpmiDmBEhfWPmOEsJ0kXh59yQZh20kcnn6RHcK2lzi7ZjM7TM3PXTtYIWxtie2v9kHZOhLbDvazQNi2EntmTIS6013iwaPlULavxKyHDVA1URINC04wTWCUmIXk1FlITl3AyLSKUdrZw083OEzXNcLZw8BQLzc5o0A3NzEzTy85P3cTIyQyVXcw3voXfohRkoNJSZT31KmOCCG+pPyUyozE4oz4otKc1OJDjDIcHEoSvMqPgXKCRanpqRVpmTnAFAGTZuLgPMQowcGjJMKbCFLDW1yQmFucmQ6RP8UoKSXOa/EIKCEAksgozYPrvcQoKiXMe/YOUI6nILUoN7MEIn6LUZjjIZMQS15+XqoU0J0MQKDB+IpRnINRSZj3D8gSnsy8Ergdr4DWMwGtv8DcDrK+JBEhJdXAaB4y++7F9fIyO6Y6iFl775h/2GN+zAb+y/viDq+K4NuxrFVjzupjUX/nnvxipLUzUOPPxBjRjeaTf+6TO2MQ+8Vubvfx17O/dxirxOlzrUpaN1GiXjto08FAhS2rtwa5shWo1LGcEPzfeWT+iyBOxjtuh1XqT+5SzTXwyXVtFDFfc0GkxS+xQImlOCPRUIu5qDgRAB7HRtpRAwAA X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170125_094957_320257_479F8643 X-CRM114-Status: GOOD ( 19.30 ) X-Spam-Score: -1.2 (-) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-1.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [198.175.253.37 listed in list.dnswl.org] 0.7 SPF_SOFTFAIL SPF: sender does not match SPF record (softfail) -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: boris.brezillon@free-electrons.com, richard@nod.at, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, Cyrille Pitchen , computersforpeace@gmail.com Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Before this patch, m25p80_read() supported few SPI protocols: - regular SPI 1-1-1 - SPI Dual Output 1-1-2 - SPI Quad Output 1-1-4 On the other hand, m25p80_write() only supported SPI 1-1-1. This patch updates both m25p80_read() and m25p80_write() functions to let them support SPI 1-2-2 and SPI 1-4-4 protocols for Fast Read and Page Program SPI commands. It adopts a conservative approach to avoid regressions. Hence the new implementations try to be as close as possible to the old implementations, so the main differences are: - the tx_nbits values now being set properly for the spi_transfer structures carrying the (op code + address/dummy) bytes - and the spi_transfer structure being split into 2 spi_transfer structures when the numbers of I/O lines are different for op code and for address/dummy byte transfers on the SPI bus. Besides, the current spi-nor framework supports neither the SPI 2-2-2 nor the SPI 4-4-4 protocols. So, for now, we don't need to update the m25p80_{read|write}_reg() functions as SPI 1-1-1 is the only one possible protocol. Signed-off-by: Cyrille Pitchen --- drivers/mtd/devices/m25p80.c | 122 ++++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 30 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index f56b38b1f57b..cdc0a007400a 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -34,6 +34,19 @@ struct m25p { u8 command[MAX_CMD_SIZE]; }; +static inline void m25p80_proto2nbits(enum spi_nor_protocol proto, + unsigned int *code_nbits, + unsigned int *addr_nbits, + unsigned int *data_nbits) +{ + if (code_nbits) + *code_nbits = SNOR_PROTO_CODE_FROM_PROTO(proto); + if (addr_nbits) + *addr_nbits = SNOR_PROTO_ADDR_FROM_PROTO(proto); + if (data_nbits) + *data_nbits = SNOR_PROTO_DATA_FROM_PROTO(proto); +} + static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) { struct m25p *flash = nor->priv; @@ -78,11 +91,16 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len, { struct m25p *flash = nor->priv; struct spi_device *spi = flash->spi; - struct spi_transfer t[2] = {}; + unsigned int code_nbits, addr_nbits, data_nbits, data_idx; + struct spi_transfer t[3] = {}; struct spi_message m; int cmd_sz = m25p_cmdsz(nor); ssize_t ret; + /* get transfer protocols. */ + m25p80_proto2nbits(nor->write_proto, &code_nbits, + &addr_nbits, &data_nbits); + spi_message_init(&m); if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) @@ -92,12 +110,27 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len, m25p_addr2cmd(nor, to, flash->command); t[0].tx_buf = flash->command; + t[0].tx_nbits = code_nbits; t[0].len = cmd_sz; spi_message_add_tail(&t[0], &m); - t[1].tx_buf = buf; - t[1].len = len; - spi_message_add_tail(&t[1], &m); + /* split the op code and address bytes into two transfers if needed. */ + data_idx = 1; + if (addr_nbits != code_nbits) { + t[0].len = 1; + + t[1].tx_buf = &flash->command[1]; + t[1].tx_nbits = addr_nbits; + t[1].len = cmd_sz - 1; + spi_message_add_tail(&t[1], &m); + + data_idx = 2; + } + + t[data_idx].tx_buf = buf; + t[data_idx].tx_nbits = data_nbits; + t[data_idx].len = len; + spi_message_add_tail(&t[data_idx], &m); ret = spi_sync(spi, &m); if (ret) @@ -109,18 +142,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len, return ret; } -static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor) -{ - switch (nor->read_proto) { - case SNOR_PROTO_1_1_2: - return 2; - case SNOR_PROTO_1_1_4: - return 4; - default: - return 0; - } -} - /* * Read an address range from the nor chip. The address range * may be any size provided it is within the physical boundaries. @@ -130,13 +151,19 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len, { struct m25p *flash = nor->priv; struct spi_device *spi = flash->spi; - struct spi_transfer t[2]; + unsigned int code_nbits, addr_nbits, data_nbits, data_idx; + struct spi_transfer t[3]; struct spi_message m; unsigned int dummy = nor->read_dummy; ssize_t ret; + int cmd_sz; + + /* get transfer protocols. */ + m25p80_proto2nbits(nor->read_proto, &code_nbits, + &addr_nbits, &data_nbits); /* convert the dummy cycles to the number of bytes */ - dummy /= 8; + dummy = (dummy * addr_nbits) / 8; if (spi_flash_read_supported(spi)) { struct spi_flash_read_message msg; @@ -149,10 +176,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len, msg.read_opcode = nor->read_opcode; msg.addr_width = nor->addr_width; msg.dummy_bytes = dummy; - /* TODO: Support other combinations */ - msg.opcode_nbits = SPI_NBITS_SINGLE; - msg.addr_nbits = SPI_NBITS_SINGLE; - msg.data_nbits = m25p80_rx_nbits(nor); + msg.opcode_nbits = code_nbits; + msg.addr_nbits = addr_nbits; + msg.data_nbits = data_nbits; ret = spi_flash_read(spi, &msg); if (ret < 0) @@ -167,20 +193,45 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len, m25p_addr2cmd(nor, from, flash->command); t[0].tx_buf = flash->command; + t[0].tx_nbits = code_nbits; t[0].len = m25p_cmdsz(nor) + dummy; spi_message_add_tail(&t[0], &m); - t[1].rx_buf = buf; - t[1].rx_nbits = m25p80_rx_nbits(nor); - t[1].len = min3(len, spi_max_transfer_size(spi), - spi_max_message_size(spi) - t[0].len); - spi_message_add_tail(&t[1], &m); + /* + * Set all dummy/mode cycle bits to avoid sending some manufacturer + * specific pattern, which might make the memory enter its Continuous + * Read mode by mistake. + * Based on the different mode cycle bit patterns listed and described + * in the JESD216B speficication, the 0xff value works for all memories + * and all manufacturers. + */ + cmd_sz = t[0].len; + memset(flash->command + cmd_sz - dummy, 0xff, dummy); + + /* split the op code and address bytes into two transfers if needed. */ + data_idx = 1; + if (addr_nbits != code_nbits) { + t[0].len = 1; + + t[1].tx_buf = &flash->command[1]; + t[1].tx_nbits = addr_nbits; + t[1].len = cmd_sz - 1; + spi_message_add_tail(&t[1], &m); + + data_idx = 2; + } + + t[data_idx].rx_buf = buf; + t[data_idx].rx_nbits = data_nbits; + t[data_idx].len = min3(len, spi_max_transfer_size(spi), + spi_max_message_size(spi) - cmd_sz); + spi_message_add_tail(&t[data_idx], &m); ret = spi_sync(spi, &m); if (ret) return ret; - ret = m.actual_length - m25p_cmdsz(nor) - dummy; + ret = m.actual_length - cmd_sz; if (ret < 0) return -EIO; return ret; @@ -224,11 +275,22 @@ static int m25p_probe(struct spi_device *spi) spi_set_drvdata(spi, flash); flash->spi = spi; - if (spi->mode & SPI_RX_QUAD) + if (spi->mode & SPI_RX_QUAD) { modes.rd_modes |= SNOR_MODE_1_1_4; - else if (spi->mode & SPI_RX_DUAL) + + if (spi->mode & SPI_TX_QUAD) { + modes.rd_modes |= SNOR_MODE_1_4_4; + modes.wr_modes |= (SNOR_MODE_1_1_4 | SNOR_MODE_1_4_4); + } + } else if (spi->mode & SPI_RX_DUAL) { modes.rd_modes |= SNOR_MODE_1_1_2; + if (spi->mode & SPI_TX_DUAL) { + modes.rd_modes |= SNOR_MODE_1_2_2; + modes.wr_modes |= (SNOR_MODE_1_1_2 | SNOR_MODE_1_2_2); + } + } + if (data && data->name) nor->mtd.name = data->name;