From patchwork Tue Apr 21 06:39:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mason Yang X-Patchwork-Id: 11500593 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9B3B881 for ; Tue, 21 Apr 2020 07:09:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8C5FE206F4 for ; Tue, 21 Apr 2020 07:09:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726600AbgDUHJL (ORCPT ); Tue, 21 Apr 2020 03:09:11 -0400 Received: from twhmllg3.macronix.com ([122.147.135.201]:50588 "EHLO TWHMLLG3.macronix.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728083AbgDUHJL (ORCPT ); Tue, 21 Apr 2020 03:09:11 -0400 Received: from TWHMLLG3.macronix.com (localhost [127.0.0.2] (may be forged)) by TWHMLLG3.macronix.com with ESMTP id 03L6efia046638 for ; Tue, 21 Apr 2020 14:40:41 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) Received: from localhost.localdomain ([172.17.195.96]) by TWHMLLG3.macronix.com with ESMTP id 03L6dnMX045498; Tue, 21 Apr 2020 14:39:50 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) From: Mason Yang To: broonie@kernel.org, tudor.ambarus@microchip.com, miquel.raynal@bootlin.com, richard@nod.at, vigneshr@ti.com, boris.brezillon@collabora.com Cc: juliensu@mxic.com.tw, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, linux-spi@vger.kernel.org, Mason Yang , Boris Brezillon Subject: [PATCH v2 1/5] mtd: spi-nor: Add support for Octal 8D-8D-8D mode Date: Tue, 21 Apr 2020 14:39:43 +0800 Message-Id: <1587451187-6889-2-git-send-email-masonccyang@mxic.com.tw> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> References: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> X-MAIL: TWHMLLG3.macronix.com 03L6dnMX045498 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org According to JEDEC216C SPI NORs are using 2 bytes opcodes when operated in OPI (Octal Peripheral Interface). Add extension command, command bytes number and dtr fields to the spi_mem_op struct and make sure all DTR operations are rejected for now. Signed-off-by: Boris Brezillon Signed-off-by: Mason Yang --- drivers/spi/spi-mem.c | 8 +++++++- include/linux/spi/spi-mem.h | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index adaa0c4..de682c5 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -154,6 +154,12 @@ bool spi_mem_default_supports_op(struct spi_mem *mem, op->data.dir == SPI_MEM_DATA_OUT)) return false; + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) + return false; + + if (op->cmd.nbytes != 1) + return false; + return true; } EXPORT_SYMBOL_GPL(spi_mem_default_supports_op); @@ -168,7 +174,7 @@ static bool spi_mem_buswidth_is_valid(u8 buswidth) static int spi_mem_check_op(const struct spi_mem_op *op) { - if (!op->cmd.buswidth) + if (!op->cmd.buswidth || op->cmd.nbytes < 1 || op->cmd.nbytes > 2) return -EINVAL; if ((op->addr.nbytes && !op->addr.buswidth) || diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index af9ff2f..bf54079 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -17,6 +17,7 @@ { \ .buswidth = __buswidth, \ .opcode = __opcode, \ + .nbytes = 1, \ } #define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \ @@ -69,11 +70,15 @@ enum spi_mem_data_dir { /** * struct spi_mem_op - describes a SPI memory operation + * @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid) * @cmd.buswidth: number of IO lines used to transmit the command + * @cmd.dtr: set true to transfer opcode in double transfer rate mode * @cmd.opcode: operation opcode + * @cmd.ext_opcode: extension operation opcode * @addr.nbytes: number of address bytes to send. Can be zero if the operation * does not need to send an address * @addr.buswidth: number of IO lines used to transmit the address cycles + * @addr.dtr: set true to transfer address bytes in double transfer rate mode * @addr.val: address value. This value is always sent MSB first on the bus. * Note that only @addr.nbytes are taken into account in this * address value, so users should make sure the value fits in the @@ -81,34 +86,42 @@ enum spi_mem_data_dir { * @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can * be zero if the operation does not require dummy bytes * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes + * @dummy.dtr: set true to transfer dummy bytes in double transfer rate mode * @data.buswidth: number of IO lanes used to send/receive the data * @data.dir: direction of the transfer * @data.nbytes: number of data bytes to send/receive. Can be zero if the * operation does not involve transferring data + * @data.dtr: set true to transfer data bytes in double transfer rate mode * @data.buf.in: input buffer (must be DMA-able) * @data.buf.out: output buffer (must be DMA-able) */ struct spi_mem_op { struct { + u8 nbytes; u8 buswidth; + bool dtr; u8 opcode; + u8 ext_opcode; } cmd; struct { u8 nbytes; u8 buswidth; + bool dtr; u64 val; } addr; struct { u8 nbytes; u8 buswidth; + bool dtr; } dummy; struct { u8 buswidth; enum spi_mem_data_dir dir; unsigned int nbytes; + bool dtr; union { void *in; const void *out; From patchwork Tue Apr 21 06:39:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mason Yang X-Patchwork-Id: 11500597 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 48DFB112C for ; Tue, 21 Apr 2020 07:09:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3B292206A5 for ; Tue, 21 Apr 2020 07:09:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727814AbgDUHJP (ORCPT ); Tue, 21 Apr 2020 03:09:15 -0400 Received: from twhmllg3.macronix.com ([122.147.135.201]:39894 "EHLO TWHMLLG3.macronix.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727043AbgDUHJO (ORCPT ); Tue, 21 Apr 2020 03:09:14 -0400 Received: from TWHMLLG3.macronix.com (localhost [127.0.0.2] (may be forged)) by TWHMLLG3.macronix.com with ESMTP id 03L6egwa046660 for ; Tue, 21 Apr 2020 14:40:42 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) Received: from localhost.localdomain ([172.17.195.96]) by TWHMLLG3.macronix.com with ESMTP id 03L6dnMY045498; Tue, 21 Apr 2020 14:39:51 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) From: Mason Yang To: broonie@kernel.org, tudor.ambarus@microchip.com, miquel.raynal@bootlin.com, richard@nod.at, vigneshr@ti.com, boris.brezillon@collabora.com Cc: juliensu@mxic.com.tw, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, linux-spi@vger.kernel.org, Mason Yang Subject: [PATCH v2 2/5] mtd: spi-nor: sfdp: Add support for xSPI profile 1.0 table Date: Tue, 21 Apr 2020 14:39:44 +0800 Message-Id: <1587451187-6889-3-git-send-email-masonccyang@mxic.com.tw> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> References: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> X-MAIL: TWHMLLG3.macronix.com 03L6dnMY045498 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org xSPI(eXpanded Serial Peripheral Interface) is for Non Volatile Memory Devices supports Octal DTR mode. Extract information like: Read Fast command, the number of dummy cycles and address bytes for Read Status Register command. Read/Write volatile Register command for Configuration(CFG) Register 2. The dummy cycless used for various frequencies. Signed-off-by: Mason Yang --- drivers/mtd/spi-nor/core.h | 14 ++++++ drivers/mtd/spi-nor/sfdp.c | 106 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 6f2f6b2..2ea11fa 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -208,6 +208,12 @@ struct spi_nor_locking_ops { * e.g. different opcodes, specific address calculation, * page size, etc. * @locking_ops: SPI NOR locking methods. + * @dtr_read_cmd: xSPI Octal DTR Read Fast command. + * @rdsr_addr_nbytes: xSPI Octal address bytes for read status register. + * @rdsr_dummy_cycles: xSPI Octal dummy cycles for read status register. + * @rd_reg_cmd: xSPI Octal read volatile register command. + * @wr_reg_cmd: xSPI Octal write volatile register command. + * @dummy_cycles: xSPI Octal dummy cycles used for various frequencies. */ struct spi_nor_flash_parameter { u64 size; @@ -225,6 +231,14 @@ struct spi_nor_flash_parameter { int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps); const struct spi_nor_locking_ops *locking_ops; + + /* xSPI profile 1.0 parameter for Octal 8S-8S-8S/8D-8D-8D */ + u8 dtr_read_cmd; + u8 rdsr_addr_nbytes; + u8 rdsr_dummy_cycles; + u8 rd_reg_cmd; + u8 wr_reg_cmd; + u8 dummy_cycles; }; /** diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index f6038d3..26814a1 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "core.h" @@ -19,6 +20,7 @@ #define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ #define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ #define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */ +#define SFDP_XSPI_PF1_ID 0xff05 /* xSPI Profile 1.0 table. */ #define SFDP_SIGNATURE 0x50444653U #define SFDP_JESD216_MAJOR 1 @@ -26,6 +28,28 @@ #define SFDP_JESD216A_MINOR 5 #define SFDP_JESD216B_MINOR 6 +/* xSPI Profile 1.0 table (from JESD216D.01). */ +#define XSPI_PF1_DWORD1_RD_CMD GENMASK(15, 8) +#define XSPI_PF1_DWORD1_RDSR_ADDR_BYTES BIT(29) +#define XSPI_PF1_DWORD1_RDSR_DUMMY_CYCLES BIT(28) + +#define XSPI_PF1_DWORD2_RD_REG_CMD GENMASK(31, 24) +#define XSPI_PF1_DWORD2_WR_REG_CMD GENMASK(15, 8) + +#define XSPI_DWORD(x) ((x) - 1) +#define XSPI_DWORD_MAX 5 + +struct sfdp_xspi { + u32 dwords[XSPI_DWORD_MAX]; +}; + +struct xspi_dummy_cycles { + u16 speed_hz; /* Speed MHz */ + u8 dwords; /* Dwords index */ + u32 mask; /* Mask */ + u8 shift; /* Bit shift */ +}; + struct sfdp_header { u32 signature; /* Ox50444653U <=> "SFDP" */ u8 minor; @@ -1081,6 +1105,83 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, } /** + * spi_nor_parse_xspi_pf1() - parse the xSPI Profile 1.0 table + * @nor: pointer to a 'struct spi_nor' + * @param_header: pointer to the 'struct sfdp_parameter_header' describing + * the 4-Byte Address Instruction Table length and version. + * @params: pointer to the 'struct spi_nor_flash_parameter' to be. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_xspi_pf1(struct spi_nor *nor, + const struct sfdp_parameter_header *header, + struct spi_nor_flash_parameter *params) +{ + struct sfdp_xspi pfile1; + u32 i, addr; + size_t len; + int ret; + static const struct xspi_dummy_cycles dummy[] = { + /* {MHz, Dwords index, Mask, Bit shift} */ + { 200, 4, GENMASK(11, 7), 7}, + { 166, 5, GENMASK(31, 27), 27}, + { 133, 5, GENMASK(21, 17), 17}, + { 100, 5, GENMASK(11, 7), 7}, + }; + + if (header->major != SFDP_JESD216_MAJOR || + header->length < XSPI_DWORD_MAX) + return -EINVAL; + + len = min_t(size_t, sizeof(pfile1), + header->length * sizeof(u32)); + + memset(&pfile1, 0, sizeof(pfile1)); + + addr = SFDP_PARAM_HEADER_PTP(header); + ret = spi_nor_read_sfdp(nor, addr, len, &pfile1); + if (ret) + goto out; + + /* Fix endianness of the xSPI 1.0 DWORDs. */ + le32_to_cpu_array(pfile1.dwords, XSPI_DWORD_MAX); + + /* Get 8D-8D-8D fast read opcode and dummy cycles. */ + params->dtr_read_cmd = FIELD_GET(XSPI_PF1_DWORD1_RD_CMD, + pfile1.dwords[XSPI_DWORD(1)]); + + if (pfile1.dwords[XSPI_DWORD(1)] & XSPI_PF1_DWORD1_RDSR_ADDR_BYTES) + params->rdsr_addr_nbytes = 4; + else + params->rdsr_addr_nbytes = 0; + + if (pfile1.dwords[XSPI_DWORD(1)] & XSPI_PF1_DWORD1_RDSR_DUMMY_CYCLES) + params->rdsr_dummy_cycles = 8; + else + params->rdsr_dummy_cycles = 4; + + params->rd_reg_cmd = FIELD_GET(XSPI_PF1_DWORD2_RD_REG_CMD, + pfile1.dwords[XSPI_DWORD(2)]); + params->wr_reg_cmd = FIELD_GET(XSPI_PF1_DWORD2_WR_REG_CMD, + pfile1.dwords[XSPI_DWORD(2)]); + + for (i = 0; i < ARRAY_SIZE(dummy); i++) { + if (params->octal_max_speed == dummy[i].speed_hz) { + params->dummy_cycles = + (dummy[i].mask & + pfile1.dwords[XSPI_DWORD(dummy[i].dwords)]) >> + dummy[i].shift; + break; + } + } + if (i == ARRAY_SIZE(dummy)) + params->dummy_cycles = 20; + +out: + return ret; +} + +/** * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. * @nor: pointer to a 'struct spi_nor' * @params: pointer to the 'struct spi_nor_flash_parameter' to be @@ -1171,7 +1272,6 @@ int spi_nor_parse_sfdp(struct spi_nor *nor, /* Parse optional parameter tables. */ for (i = 0; i < header.nph; i++) { param_header = ¶m_headers[i]; - switch (SFDP_PARAM_HEADER_ID(param_header)) { case SFDP_SECTOR_MAP_ID: err = spi_nor_parse_smpt(nor, param_header, params); @@ -1181,6 +1281,10 @@ int spi_nor_parse_sfdp(struct spi_nor *nor, err = spi_nor_parse_4bait(nor, param_header, params); break; + case SFDP_XSPI_PF1_ID: + err = spi_nor_parse_xspi_pf1(nor, param_header, params); + break; + default: break; } From patchwork Tue Apr 21 06:39:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mason Yang X-Patchwork-Id: 11500595 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0D3F0112C for ; Tue, 21 Apr 2020 07:09:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E8666206A5 for ; Tue, 21 Apr 2020 07:09:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728091AbgDUHJN (ORCPT ); Tue, 21 Apr 2020 03:09:13 -0400 Received: from twhmllg3.macronix.com ([122.147.135.201]:50590 "EHLO TWHMLLG3.macronix.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728083AbgDUHJN (ORCPT ); Tue, 21 Apr 2020 03:09:13 -0400 Received: from TWHMLLG3.macronix.com (localhost [127.0.0.2] (may be forged)) by TWHMLLG3.macronix.com with ESMTP id 03L6efEx046656 for ; Tue, 21 Apr 2020 14:40:41 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) Received: from localhost.localdomain ([172.17.195.96]) by TWHMLLG3.macronix.com with ESMTP id 03L6dnMZ045498; Tue, 21 Apr 2020 14:39:52 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) From: Mason Yang To: broonie@kernel.org, tudor.ambarus@microchip.com, miquel.raynal@bootlin.com, richard@nod.at, vigneshr@ti.com, boris.brezillon@collabora.com Cc: juliensu@mxic.com.tw, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, linux-spi@vger.kernel.org, Mason Yang Subject: [PATCH v2 3/5] mtd: spi-nor: Parse BFPT DWORD-18,19 and 20 for Octal 8D-8D-8D mode Date: Tue, 21 Apr 2020 14:39:45 +0800 Message-Id: <1587451187-6889-4-git-send-email-masonccyang@mxic.com.tw> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> References: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> X-MAIL: TWHMLLG3.macronix.com 03L6dnMZ045498 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org Based on JESD216C BFPT(Basic Flash Parameter Table) to add: 18th DWORD: Octal DTR(8D-8D-8D) command and command extension (00b: same, 01b: inverse) - Get extension command type 19th DWORD: Octal mode enable sequences by two instructions or write CFG Reg2 - Get enable Octal mode sequences - implemented Read/write CFG Reg 2 function - setup the call-back function for octal mode enable 20th DWORD: Maximum operation speed of device in Octal mode - Get the Octal maximum operation speed Also defined the relevant macros and enum to add such modes and make sure op->xxx.dtr fields, command nbytes and extension command are properly filled and unmask DTR and X-X-X modes in spi_nor_spimem_adjust_hwcaps() so that DTR and X-X-X support detection is done through spi_mem_supports_op(). Signed-off-by: Mason Yang --- drivers/mtd/spi-nor/core.c | 220 ++++++++++++++++++++++++++++++++++++++++++-- drivers/mtd/spi-nor/core.h | 17 ++++ drivers/mtd/spi-nor/sfdp.c | 116 +++++++++++++++++++++++ drivers/mtd/spi-nor/sfdp.h | 16 +++- include/linux/mtd/spi-nor.h | 51 +++++++++- 5 files changed, 404 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index cc68ea8..b67c65d 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -40,6 +40,47 @@ #define SPI_NOR_MAX_ADDR_WIDTH 4 +#define SET_SPIMEM_OP_FULL_OCTAL_READ_BUSWIDTH() \ + { \ + op.cmd.buswidth = 8; \ + op.addr.buswidth = 8; \ + op.dummy.buswidth = 8; \ + op.data.buswidth = 8; \ + op.cmd.nbytes = 2; \ + } + +#define SET_SPIMEM_OP_FULL_OCTAL_WRITE_BUSWIDTH() \ + { \ + op.cmd.buswidth = 8; \ + op.addr.buswidth = 8; \ + op.data.buswidth = 8; \ + op.cmd.nbytes = 2; \ + } + +#define SET_SPIMEM_OP_DTR_READ() \ + { \ + op.dummy.nbytes *= 2; \ + op.cmd.dtr = true; \ + op.addr.dtr = true; \ + op.dummy.dtr = true; \ + op.data.dtr = true; \ + } + +#define SET_SPIMEM_OP_DTR_WRITE() \ + { \ + op.cmd.dtr = true; \ + op.addr.dtr = true; \ + op.data.dtr = true; \ + } + +#define SET_OCTAL_EXTION_OPCODE() \ + { \ + if (nor->ext_cmd_mode == EXT_CMD_IS_INVERSE) \ + op.cmd.ext_opcode = ~op.cmd.opcode; \ + else \ + op.cmd.ext_opcode = op.cmd.opcode; \ + } + /** * spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data * transfer @@ -113,6 +154,16 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, /* convert the dummy cycles to the number of bytes */ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + if (spi_nor_protocol_is_8_8_8(nor->read_proto)) { + op.cmd.nbytes = 2; + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->read_proto)) + SET_SPIMEM_OP_DTR_READ(); + + memcpy(&nor->dirmap.rdesc->info.op_tmpl, &op, sizeof(op)); + } + usebouncebuf = spi_nor_spimem_bounce(nor, &op); if (nor->dirmap.rdesc) { @@ -176,6 +227,16 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op.addr.nbytes = 0; + if (spi_nor_protocol_is_8_8_8(nor->write_proto)) { + op.cmd.nbytes = 2; + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto)) + SET_SPIMEM_OP_DTR_WRITE(); + + memcpy(&nor->dirmap.wdesc->info.op_tmpl, &op, sizeof(op)); + } + if (spi_nor_spimem_bounce(nor, &op)) memcpy(nor->bouncebuf, buf, op.data.nbytes); @@ -227,6 +288,15 @@ int spi_nor_write_enable(struct spi_nor *nor) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + if (spi_nor_protocol_is_8_8_8(nor->write_proto)) { + op.cmd.buswidth = 8; + op.cmd.nbytes = 2; + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto)) + op.cmd.dtr = true; + } + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN, @@ -256,6 +326,15 @@ int spi_nor_write_disable(struct spi_nor *nor) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + if (spi_nor_protocol_is_8_8_8(nor->write_proto)) { + op.cmd.buswidth = 8; + op.cmd.nbytes = 2; + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto)) + op.cmd.dtr = true; + } + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRDI, @@ -287,6 +366,16 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_IN(1, sr, 1)); + if (spi_nor_protocol_is_8_8_8(nor->read_proto)) { + SET_SPIMEM_OP_FULL_OCTAL_READ_BUSWIDTH(); + op.addr.nbytes = nor->params->rdsr_addr_nbytes; + op.dummy.nbytes = nor->params->rdsr_dummy_cycles; + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->read_proto)) + SET_SPIMEM_OP_DTR_READ(); + } + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR, @@ -300,6 +389,92 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) } /** + * spi_nor_read_cr2() - Read the Configuration Register 2. + * @nor: pointer to 'struct spi_nor'. + * @addr: offset address to read. + * @cr2: pointer to a DMA-able buffer where the value of the + * Configuration Register 2 will be written. + * + * Return: 0 on success, -errno otherwise. + */ +int spi_nor_read_cr2(struct spi_nor *nor, u32 addr, u8 *cr2) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->params->rd_reg_cmd, 1), + SPI_MEM_OP_ADDR(4, addr, 1), + SPI_MEM_OP_DUMMY(4, 1), + SPI_MEM_OP_DATA_IN(1, cr2, 1)); + + if (spi_nor_protocol_is_8_8_8(nor->read_proto)) { + SET_SPIMEM_OP_FULL_OCTAL_READ_BUSWIDTH(); + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->read_proto)) + SET_SPIMEM_OP_DTR_READ(); + } + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->read_reg(nor, + nor->params->rd_reg_cmd, + cr2, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d reading CR2\n", ret); + + return ret; +} + +/** + * spi_nor_write_cr2() - Write the Configuration Register 2. + * @nor: pointer to 'struct spi_nor'. + * @addr: offset address to write. + * @cr2: pointer to a DMA-able buffer where the value of the + * Configuratin Register 2 will be read. + * + * Return: 0 on success, -errno otherwise. + */ +int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2) +{ + int ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->params->wr_reg_cmd, 1), + SPI_MEM_OP_ADDR(4, addr, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, cr2, 1)); + + if (spi_nor_protocol_is_8_8_8(nor->write_proto)) { + SET_SPIMEM_OP_FULL_OCTAL_WRITE_BUSWIDTH(); + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto)) + SET_SPIMEM_OP_DTR_WRITE(); + } + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, + nor->params->wr_reg_cmd, + cr2, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d write CFG Reg 2\n", ret); + + return ret; +} + +/** * spi_nor_read_fsr() - Read the Flag Status Register. * @nor: pointer to 'struct spi_nor' * @fsr: pointer to a DMA-able buffer where the value of the @@ -1002,6 +1177,15 @@ static int spi_nor_erase_chip(struct spi_nor *nor) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + if (spi_nor_protocol_is_8_8_8(nor->write_proto)) { + op.cmd.buswidth = 8; + op.cmd.nbytes = 2; + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto)) + op.cmd.dtr = true; + } + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CHIP_ERASE, @@ -1144,6 +1328,18 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + if (spi_nor_protocol_is_8_8_8(nor->write_proto)) { + op.cmd.buswidth = 8; + op.addr.buswidth = 8; + op.cmd.nbytes = 2; + SET_OCTAL_EXTION_OPCODE(); + + if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto)) { + op.cmd.dtr = true; + op.addr.dtr = true; + } + } + return spi_mem_exec_op(nor->spimem, &op); } else if (nor->controller_ops->erase) { return nor->controller_ops->erase(nor, addr); @@ -2204,7 +2400,7 @@ static int spi_nor_check(struct spi_nor *nor) return 0; } -static void +void spi_nor_set_read_settings(struct spi_nor_read_command *read, u8 num_mode_clocks, u8 num_wait_states, @@ -2253,6 +2449,7 @@ int spi_nor_hwcaps_read2cmd(u32 hwcaps) { SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 }, { SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 }, { SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR }, + { SNOR_HWCAPS_READ_8_8_8_DTR, SNOR_CMD_READ_8_8_8_DTR }, }; return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd, @@ -2269,6 +2466,7 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) { SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 }, { SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 }, { SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 }, + { SNOR_HWCAPS_PP_8_8_8_DTR, SNOR_CMD_PP_8_8_8_DTR }, }; return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd, @@ -2368,12 +2566,6 @@ static int spi_nor_spimem_check_pp(struct spi_nor *nor, struct spi_nor_flash_parameter *params = nor->params; unsigned int cap; - /* DTR modes are not supported yet, mask them all. */ - *hwcaps &= ~SNOR_HWCAPS_DTR; - - /* X-X-X modes are not supported yet, mask them all. */ - *hwcaps &= ~SNOR_HWCAPS_X_X_X; - for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) { int rdidx, ppidx; @@ -2614,7 +2806,6 @@ static int spi_nor_default_setup(struct spi_nor *nor, * controller and the SPI flash memory. */ shared_mask = hwcaps->mask & params->hwcaps.mask; - if (nor->spimem) { /* * When called from spi_nor_probe(), all caps are set and we @@ -2831,12 +3022,21 @@ static void spi_nor_post_sfdp_fixups(struct spi_nor *nor) */ static void spi_nor_late_init_params(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; + int ret = 0; + /* * NOR protection support. When locking_ops are not provided, we pick * the default ones. */ if (nor->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops) nor->params->locking_ops = &spi_nor_sr_locking_ops; + + if (nor->params->xspi_enable) + ret = nor->params->xspi_enable(nor, + (params->dtr_read_cmd) ? 1 : 0); + if (ret) + dev_err(nor->dev, " enable xSPI mode failed %d\n", ret); } /** @@ -2886,8 +3086,8 @@ static int spi_nor_init_params(struct spi_nor *nor) spi_nor_manufacturer_init_params(nor); - if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) && - !(nor->info->flags & SPI_NOR_SKIP_SFDP)) + if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_OCTAL_RD_WR)) && !(nor->info->flags & SPI_NOR_SKIP_SFDP)) spi_nor_sfdp_init_params(nor); spi_nor_post_sfdp_fixups(nor); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 2ea11fa..ef32b86 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -62,6 +62,7 @@ enum spi_nor_read_command_index { SNOR_CMD_READ_1_8_8, SNOR_CMD_READ_8_8_8, SNOR_CMD_READ_1_8_8_DTR, + SNOR_CMD_READ_8_8_8_DTR, SNOR_CMD_READ_MAX }; @@ -78,6 +79,7 @@ enum spi_nor_pp_command_index { SNOR_CMD_PP_1_1_8, SNOR_CMD_PP_1_8_8, SNOR_CMD_PP_8_8_8, + SNOR_CMD_PP_8_8_8_DTR, SNOR_CMD_PP_MAX }; @@ -208,6 +210,8 @@ struct spi_nor_locking_ops { * e.g. different opcodes, specific address calculation, * page size, etc. * @locking_ops: SPI NOR locking methods. + * @xspi_enable: enables xSPI Octal mode for 8S-8S-8S/8D-8D-8D. + * @octal_max_speed: xSPI Octal maximum operation speed (MHz). * @dtr_read_cmd: xSPI Octal DTR Read Fast command. * @rdsr_addr_nbytes: xSPI Octal address bytes for read status register. * @rdsr_dummy_cycles: xSPI Octal dummy cycles for read status register. @@ -232,6 +236,11 @@ struct spi_nor_flash_parameter { const struct spi_nor_locking_ops *locking_ops; + /* BFPT DWORD19 Octal mode enable sequences */ + int (*xspi_enable)(struct spi_nor *nor, bool dtr); + /* BFPT DWORD20 Maximum operation speed of device in Octal mode */ + u16 octal_max_speed; + /* xSPI profile 1.0 parameter for Octal 8S-8S-8S/8D-8D-8D */ u8 dtr_read_cmd; u8 rdsr_addr_nbytes; @@ -325,6 +334,7 @@ struct flash_info { * BP3 is bit 6 of status register. * Must be used with SPI_NOR_4BIT_BP. */ +#define SPI_NOR_OCTAL_RD_WR BIT(19) /* Flash supports Octal Read & Write */ /* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; @@ -413,6 +423,8 @@ struct spi_nor_manufacturer { extern const struct spi_nor_manufacturer spi_nor_xilinx; extern const struct spi_nor_manufacturer spi_nor_xmc; +int spi_nor_read_cr2(struct spi_nor *nor, u32 addr, u8 *cr2); +int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2); int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable); @@ -432,6 +444,11 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, int spi_nor_hwcaps_read2cmd(u32 hwcaps); u8 spi_nor_convert_3to4_read(u8 opcode); +void spi_nor_set_read_settings(struct spi_nor_read_command *read, + u8 num_mode_clocks, + u8 num_wait_states, + u8 opcode, + enum spi_nor_protocol proto); void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode, enum spi_nor_protocol proto); diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 26814a1..85a8509 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -50,6 +50,12 @@ struct xspi_dummy_cycles { u8 shift; /* Bit shift */ }; +/* Basic Flash Parameter Table 20th DWORD, Max operation speed of device */ +struct octal_max_speed { + u8 idx; /* Bits value */ + u16 hz; /* MHz */ +}; + struct sfdp_header { u32 signature; /* Ox50444653U <=> "SFDP" */ u8 minor; @@ -423,6 +429,64 @@ static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) } /** + * spi_nor_cfg_reg2_octal_enable() - enable octal mode by CFG Reg 2 + * @nor: pointer to a 'struct spi_nor' + * @dtr: true for DTR mode + * + * The Basic Flash Parameter Table 19th DWORD defined the Octal mode + * enable sequences by writing CFG Reg 2, 1: 8S-8S-8S, 2: 8D-8D-8D. + * + * Set Octal mode read/PP command, protocol and read dummy cycles. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_cfg_reg2_octal_enable(struct spi_nor *nor, bool dtr) +{ + struct spi_nor_flash_parameter *p = nor->params; + int ret; + u8 cr2 = 0; + + if (!(nor->spimem->spi->mode & (SPI_RX_OCTAL | SPI_TX_OCTAL))) + return -ENOTSUPP; + + if (dtr) { + cr2 |= CR2_OCTAL_MODE_DTR; + ret = spi_nor_write_cr2(nor, CR2_OCTAL_MODE_ADDR, &cr2); + if (ret) + return ret; + + /* Octal 8D-8D-8D mode */ + p->hwcaps.mask |= SNOR_HWCAPS_OPI_FULL_DTR; + spi_nor_set_read_settings(&p->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, p->dummy_cycles, + p->dtr_read_cmd, + SNOR_PROTO_8_8_8_DTR); + + spi_nor_set_pp_settings(&p->page_programs + [SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP_8D_8D_8D, + SNOR_PROTO_8_8_8_DTR); + } else { + cr2 |= CR2_OCTAL_MODE_STR; + ret = spi_nor_write_cr2(nor, CR2_OCTAL_MODE_ADDR, &cr2); + if (ret) + return ret; + + /* Octal 8S-8S-8S mode */ + p->hwcaps.mask |= SNOR_HWCAPS_OPI_FULL_STR; + spi_nor_set_read_settings(&p->reads[SNOR_CMD_READ_8_8_8], + 0, p->dummy_cycles, + SPINOR_OP_READ_8_8_8, + SNOR_PROTO_8_8_8); + + spi_nor_set_pp_settings(&p->page_programs[SNOR_CMD_PP_8_8_8], + SPINOR_OP_PP_8_8_8, + SNOR_PROTO_8_8_8); + } + return 0; +} + +/** * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table. * @nor: pointer to a 'struct spi_nor' * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing @@ -464,6 +528,22 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, u32 addr; u16 half; u8 erase_mask; + static const struct octal_max_speed max_hz[] = { + /* Bits value, MHz */ + { 0x0c, 400 }, + { 0x0b, 333 }, + { 0x0a, 266 }, + { 0x09, 250 }, + { 0x08, 200 }, + { 0x07, 166 }, + { 0x06, 133 }, + { 0x05, 100 }, + { 0x04, 80 }, + { 0x03, 66 }, + { 0x02, 50 }, + { 0x01, 33 }, + }; + u8 idx; /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */ if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) @@ -628,6 +708,42 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, return -EINVAL; } + /* 8D-8D-8D command extension. */ + switch (bfpt.dwords[BFPT_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { + case BFPT_DWORD18_CMD_EXT_REP: + nor->ext_cmd_mode = EXT_CMD_IS_REPEAT; + break; + case BFPT_DWORD18_CMD_EXT_INV: + nor->ext_cmd_mode = EXT_CMD_IS_INVERSE; + break; + default: + break; + } + + /* Octal mode enable sequences. */ + switch (bfpt.dwords[BFPT_DWORD(19)] & BFPT_DWORD19_OCTAL_SEQ_MASK) { + case BFPT_DWORD19_TWO_INST: + break; + case BFPT_DWORD19_CFG_REG2: + params->xspi_enable = spi_nor_cfg_reg2_octal_enable; + break; + default: + break; + } + + /* Octal mode max speed */ + idx = max(FIELD_GET(BFPT_DWORD20_OCTAL_DTR_MAX_SPEED, + bfpt.dwords[BFPT_DWORD(20)]), + FIELD_GET(BFPT_DWORD20_OCTAL_STR_MAX_SPEED, + bfpt.dwords[BFPT_DWORD(20)])); + + for (i = 0; i < ARRAY_SIZE(max_hz); i++) { + if (max_hz[i].idx == idx) { + params->octal_max_speed = max_hz[i].hz; + break; + } + } + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); } diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index e0a8ded..7a55b4d 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -10,11 +10,11 @@ /* Basic Flash Parameter Table */ /* - * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs. + * JESD216 rev D defines a Basic Flash Parameter Table of 20 DWORDs. * They are indexed from 1 but C arrays are indexed from 0. */ #define BFPT_DWORD(i) ((i) - 1) -#define BFPT_DWORD_MAX 16 +#define BFPT_DWORD_MAX 20 struct sfdp_bfpt { u32 dwords[BFPT_DWORD_MAX]; @@ -83,6 +83,18 @@ struct sfdp_bfpt { #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ +#define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29) +#define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */ +#define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */ + +#define BFPT_DWORD19_OCTAL_SEQ_MASK GENMASK(6, 5) +#define BFPT_DWORD19_TWO_INST (0x1UL << 5) /* Two Inst. */ +#define BFPT_DWORD19_CFG_REG2 (0x2UL << 5) /* CFG Reg2 */ + +#define BFPT_DWORD20_OCTAL_MAX_SPEED_MASK GENMASK(31, 16) +#define BFPT_DWORD20_OCTAL_DTR_MAX_SPEED GENMASK(31, 28) +#define BFPT_DWORD20_OCTAL_STR_MAX_SPEED GENMASK(19, 16) + struct sfdp_parameter_header { u8 id_lsb; u8 minor; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 1e2af0e..778b52e 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -68,6 +68,9 @@ #define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */ #define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */ #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ +#define SPINOR_OP_READ_8_8_8 SPINOR_OP_READ_1_4_4_4B +#define SPINOR_OP_PP_8_8_8 SPINOR_OP_PP_4B +#define SPINOR_OP_PP_8D_8D_8D SPINOR_OP_PP_4B /* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */ #define SPINOR_OP_READ_1_1_1_DTR 0x0d @@ -91,7 +94,6 @@ #define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ #define XSR_RDY BIT(7) /* Ready */ - /* Used for Macronix and Winbond flashes. */ #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ @@ -137,6 +139,13 @@ #define SR2_QUAD_EN_BIT1 BIT(1) #define SR2_QUAD_EN_BIT7 BIT(7) +/* Configuration Register 2, JEDEC216-D */ +#define CR2_OCTAL_MODE_ADDR 0x0 +#define CR2_OCTAL_MODE_MASK GENMASK(1, 0) +#define CR2_SPI_MODE 0 +#define CR2_OCTAL_MODE_STR 1 +#define CR2_OCTAL_MODE_DTR 2 + /* Supported SPI protocols */ #define SNOR_PROTO_INST_MASK GENMASK(23, 16) #define SNOR_PROTO_INST_SHIFT 16 @@ -157,15 +166,21 @@ SNOR_PROTO_DATA_MASK) #define SNOR_PROTO_IS_DTR BIT(24) /* Double Transfer Rate */ +#define SNOR_PROTO_IS_FULL_DTR BIT(25) /* Full Double Transfer Rate */ #define SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits) \ (SNOR_PROTO_INST(_inst_nbits) | \ SNOR_PROTO_ADDR(_addr_nbits) | \ SNOR_PROTO_DATA(_data_nbits)) + #define SNOR_PROTO_DTR(_inst_nbits, _addr_nbits, _data_nbits) \ (SNOR_PROTO_IS_DTR | \ SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits)) +#define SNOR_PROTO_8D_8D_8D(_inst_nbits, _addr_nbits, _data_nbits) \ + (SNOR_PROTO_IS_FULL_DTR | \ + SNOR_PROTO_DTR(_inst_nbits, _addr_nbits, _data_nbits)) + enum spi_nor_protocol { SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(1, 1, 1), SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(1, 1, 2), @@ -182,6 +197,7 @@ enum spi_nor_protocol { SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(1, 2, 2), SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(1, 4, 4), SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(1, 8, 8), + SNOR_PROTO_8_8_8_DTR = SNOR_PROTO_8D_8D_8D(8, 8, 8), }; static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto) @@ -189,6 +205,16 @@ static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto) return !!(proto & SNOR_PROTO_IS_DTR); } +static inline bool spi_nor_protocol_is_8_8_8(enum spi_nor_protocol proto) +{ + return !!(proto & SNOR_PROTO_8_8_8); +} + +static inline bool spi_nor_protocol_is_8D_8D_8D(enum spi_nor_protocol proto) +{ + return !!(proto & SNOR_PROTO_IS_FULL_DTR); +} + static inline u8 spi_nor_get_protocol_inst_nbits(enum spi_nor_protocol proto) { return ((unsigned long)(proto & SNOR_PROTO_INST_MASK)) >> @@ -228,7 +254,7 @@ struct spi_nor_hwcaps { * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly * (Slow) Read. */ -#define SNOR_HWCAPS_READ_MASK GENMASK(14, 0) +#define SNOR_HWCAPS_READ_MASK GENMASK(15, 0) #define SNOR_HWCAPS_READ BIT(0) #define SNOR_HWCAPS_READ_FAST BIT(1) #define SNOR_HWCAPS_READ_1_1_1_DTR BIT(2) @@ -250,6 +276,7 @@ struct spi_nor_hwcaps { #define SNOR_HWCAPS_READ_1_8_8 BIT(12) #define SNOR_HWCAPS_READ_8_8_8 BIT(13) #define SNOR_HWCAPS_READ_1_8_8_DTR BIT(14) +#define SNOR_HWCAPS_READ_8_8_8_DTR BIT(15) /* * Page Program capabilities. @@ -260,7 +287,7 @@ struct spi_nor_hwcaps { * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory * implements such commands. */ -#define SNOR_HWCAPS_PP_MASK GENMASK(22, 16) +#define SNOR_HWCAPS_PP_MASK GENMASK(23, 16) #define SNOR_HWCAPS_PP BIT(16) #define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17) @@ -272,6 +299,13 @@ struct spi_nor_hwcaps { #define SNOR_HWCAPS_PP_1_1_8 BIT(20) #define SNOR_HWCAPS_PP_1_8_8 BIT(21) #define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_PP_8_8_8_DTR BIT(23) + +#define SNOR_HWCAPS_OPI_FULL_STR (SNOR_HWCAPS_READ_8_8_8 | \ + SNOR_HWCAPS_PP_8_8_8) + +#define SNOR_HWCAPS_OPI_FULL_DTR (SNOR_HWCAPS_READ_8_8_8_DTR | \ + SNOR_HWCAPS_PP_8_8_8_DTR) #define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \ SNOR_HWCAPS_READ_4_4_4 | \ @@ -282,7 +316,9 @@ struct spi_nor_hwcaps { #define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \ SNOR_HWCAPS_READ_1_2_2_DTR | \ SNOR_HWCAPS_READ_1_4_4_DTR | \ - SNOR_HWCAPS_READ_1_8_8_DTR) + SNOR_HWCAPS_READ_1_8_8_DTR | \ + SNOR_HWCAPS_READ_8_8_8_DTR | \ + SNOR_HWCAPS_PP_8_8_8_DTR) #define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \ SNOR_HWCAPS_PP_MASK) @@ -326,6 +362,11 @@ struct spi_nor_controller_ops { struct spi_nor_manufacturer; struct spi_nor_flash_parameter; +enum extension_cmd_mode { + EXT_CMD_IS_REPEAT, + EXT_CMD_IS_INVERSE, +}; + /** * struct spi_nor - Structure for defining a the SPI NOR layer * @mtd: point to a mtd_info structure @@ -335,6 +376,7 @@ struct spi_nor_controller_ops { * @bouncebuf: bounce buffer used when the buffer passed by the MTD * layer is not DMA-able * @bouncebuf_size: size of the bounce buffer + * @ext_cmd_mode: xspi extension command mode, 0: repeat, 1:inverse * @info: spi-nor part JDEC MFR id and other info * @manufacturer: spi-nor manufacturer * @page_size: the page size of the SPI NOR @@ -363,6 +405,7 @@ struct spi_nor { struct spi_mem *spimem; u8 *bouncebuf; size_t bouncebuf_size; + enum extension_cmd_mode ext_cmd_mode; const struct flash_info *info; const struct spi_nor_manufacturer *manufacturer; u32 page_size; From patchwork Tue Apr 21 06:39:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mason Yang X-Patchwork-Id: 11500591 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 57E1081 for ; Tue, 21 Apr 2020 07:09:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 407232087E for ; Tue, 21 Apr 2020 07:09:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726961AbgDUHJJ (ORCPT ); Tue, 21 Apr 2020 03:09:09 -0400 Received: from twhmllg3.macronix.com ([122.147.135.201]:50586 "EHLO TWHMLLG3.macronix.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726600AbgDUHJI (ORCPT ); Tue, 21 Apr 2020 03:09:08 -0400 Received: from TWHMLLG3.macronix.com (localhost [127.0.0.2] (may be forged)) by TWHMLLG3.macronix.com with ESMTP id 03L6efsP046635 for ; Tue, 21 Apr 2020 14:40:41 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) Received: from localhost.localdomain ([172.17.195.96]) by TWHMLLG3.macronix.com with ESMTP id 03L6dnMa045498; Tue, 21 Apr 2020 14:39:53 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) From: Mason Yang To: broonie@kernel.org, tudor.ambarus@microchip.com, miquel.raynal@bootlin.com, richard@nod.at, vigneshr@ti.com, boris.brezillon@collabora.com Cc: juliensu@mxic.com.tw, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, linux-spi@vger.kernel.org, Mason Yang Subject: [PATCH v2 4/5] mtd: spi-nor: macronix: Add Octal 8D-8D-8D supports for Macronix mx25uw51245g Date: Tue, 21 Apr 2020 14:39:46 +0800 Message-Id: <1587451187-6889-5-git-send-email-masonccyang@mxic.com.tw> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> References: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> X-MAIL: TWHMLLG3.macronix.com 03L6dnMa045498 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org Macronix mx25uw51245g is a SPI NOR that supports 1-1-1/8-8-8 mode and JEDEC216D spec. included BFPT DWORD-18,19, 20 and xSPI profile 1.0 table. Correct the dummy cycles for various frequency after xSPI 1.0 table parsed. Enable mx25uw51245g to Octal 8D-8D-8D mode by writing CFG Reg2 in the late initialization of default flash parameters spi_nor_late_init_params(); Signed-off-by: Mason Yang --- drivers/mtd/spi-nor/macronix.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index ab0f963..46bcfe5 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -8,6 +8,43 @@ #include "core.h" +#define MXIC_CR2_DUMMY_SET_ADDR 0x300 + +/* Fixup the dummy cycles after SFDP xSPI 1.0 parsed */ +static void mx25uw51245g_post_sfdp_fixups(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + int ret; + u8 rdc, wdc; + + ret = spi_nor_read_cr2(nor, MXIC_CR2_DUMMY_SET_ADDR, &rdc); + if (ret) + return; + + /* Refer to dummy cycle and frequency table(MHz) */ + switch (params->dummy_cycles) { + case 10: /* 10 dummy cycles for 104 MHz */ + wdc = 5; + break; + case 12: /* 12 dummy cycles for 133 MHz */ + wdc = 4; + break; + case 16: /* 16 dummy cycles for 166 MHz */ + wdc = 2; + break; + case 20: /* 20 dummy cycles for 200 MHz */ + default: + wdc = 0; + } + + if (rdc != wdc) + spi_nor_write_cr2(nor, MXIC_CR2_DUMMY_SET_ADDR, &wdc); +} + +static struct spi_nor_fixups mx25uw51245g_fixups = { + .post_sfdp = mx25uw51245g_post_sfdp_fixups, +}; + static int mx25l25635_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -78,6 +115,10 @@ SPI_NOR_QUAD_READ) }, { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, + { "mx25uw51245g", INFO(0xc2813a, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_4B_OPCODES | + SPI_NOR_OCTAL_RD_WR) + .fixups = &mx25uw51245g_fixups }, }; static void macronix_default_init(struct spi_nor *nor) From patchwork Tue Apr 21 06:39:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mason Yang X-Patchwork-Id: 11500599 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BBC6C112C for ; Tue, 21 Apr 2020 07:09:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AE853206F4 for ; Tue, 21 Apr 2020 07:09:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726123AbgDUHJQ (ORCPT ); Tue, 21 Apr 2020 03:09:16 -0400 Received: from twhmllg3.macronix.com ([122.147.135.201]:49200 "EHLO TWHMLLG3.macronix.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727043AbgDUHJQ (ORCPT ); Tue, 21 Apr 2020 03:09:16 -0400 Received: from TWHMLLG3.macronix.com (localhost [127.0.0.2] (may be forged)) by TWHMLLG3.macronix.com with ESMTP id 03L6eio3046686 for ; Tue, 21 Apr 2020 14:40:44 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) Received: from localhost.localdomain ([172.17.195.96]) by TWHMLLG3.macronix.com with ESMTP id 03L6dnMb045498; Tue, 21 Apr 2020 14:39:55 +0800 (GMT-8) (envelope-from masonccyang@mxic.com.tw) From: Mason Yang To: broonie@kernel.org, tudor.ambarus@microchip.com, miquel.raynal@bootlin.com, richard@nod.at, vigneshr@ti.com, boris.brezillon@collabora.com Cc: juliensu@mxic.com.tw, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, linux-spi@vger.kernel.org, Mason Yang Subject: [PATCH v2 5/5] spi: mxic: Patch for Octal 8D-8D-8D mode support Date: Tue, 21 Apr 2020 14:39:47 +0800 Message-Id: <1587451187-6889-6-git-send-email-masonccyang@mxic.com.tw> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> References: <1587451187-6889-1-git-send-email-masonccyang@mxic.com.tw> X-MAIL: TWHMLLG3.macronix.com 03L6dnMb045498 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org Driver patch for Octal 8S-8S-8S and 8D-8D-8D mode support. Signed-off-by: Mason Yang Reported-by: kbuild test robot --- drivers/spi/spi-mxic.c | 101 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index 69491f3..8054f2c 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -280,10 +280,58 @@ static void mxic_spi_hw_init(struct mxic_spi *mxic) mxic->regs + HC_CFG); } +static u32 mxic_spi_mem_prep_op_cfg(const struct spi_mem_op *op) +{ + u32 cfg = OP_CMD_BYTES(op->cmd.nbytes) | + OP_CMD_BUSW(fls(op->cmd.buswidth) - 1) | + (op->cmd.dtr ? OP_CMD_DDR : 0); + + if (op->addr.nbytes) + cfg |= OP_ADDR_BYTES(op->addr.nbytes) | + OP_ADDR_BUSW(fls(op->addr.buswidth) - 1) | + (op->addr.dtr ? OP_ADDR_DDR : 0); + + if (op->dummy.nbytes) + cfg |= OP_DUMMY_CYC(op->dummy.nbytes); + + if (op->data.nbytes) { + cfg |= OP_DATA_BUSW(fls(op->data.buswidth) - 1) | + (op->data.dtr ? OP_DATA_DDR : 0); + if (op->data.dir == SPI_MEM_DATA_IN) { + cfg |= OP_READ; + if (op->data.dtr == OP_DATA_DDR) + cfg |= OP_DQS_EN; + } + } + + return cfg; +} + +static void mxic_spi_set_hc_cfg(struct spi_device *spi, u32 flags) +{ + struct mxic_spi *mxic = spi_master_get_devdata(spi->master); + int nio = 1; + + if (spi->mode & (SPI_RX_OCTAL | SPI_TX_OCTAL)) + nio = 8; + else if (spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) + nio = 4; + else if (spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) + nio = 2; + + writel(flags | HC_CFG_NIO(nio) | + HC_CFG_TYPE(spi->chip_select, HC_CFG_TYPE_SPI_NOR) | + HC_CFG_SLV_ACT(spi->chip_select) | HC_CFG_IDLE_SIO_LVL(1), + mxic->regs + HC_CFG); +} + static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf, void *rxbuf, unsigned int len) { unsigned int pos = 0; + bool dtr_enabled; + + dtr_enabled = (readl(mxic->regs + SS_CTRL(0)) & OP_DATA_DDR); while (pos < len) { unsigned int nbytes = len - pos; @@ -302,6 +350,9 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf, if (ret) return ret; + if (dtr_enabled && len & 0x1) + nbytes++; + writel(data, mxic->regs + TXD(nbytes % 4)); if (rxbuf) { @@ -319,6 +370,8 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf, data = readl(mxic->regs + RXD); data >>= (8 * (4 - nbytes)); + if (dtr_enabled && len & 0x1) + nbytes++; memcpy(rxbuf + pos, &data, nbytes); WARN_ON(readl(mxic->regs + INT_STS) & INT_RX_NOT_EMPTY); } else { @@ -335,8 +388,8 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf, static bool mxic_spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { - if (op->data.buswidth > 4 || op->addr.buswidth > 4 || - op->dummy.buswidth > 4 || op->cmd.buswidth > 4) + if (op->data.buswidth > 8 || op->addr.buswidth > 8 || + op->dummy.buswidth > 8 || op->cmd.buswidth > 8) return false; if (op->data.nbytes && op->dummy.nbytes && @@ -346,6 +399,9 @@ static bool mxic_spi_mem_supports_op(struct spi_mem *mem, if (op->addr.nbytes > 7) return false; + if (op->cmd.buswidth == 8 && op->cmd.nbytes == 2) + return true; + return spi_mem_default_supports_op(mem, op); } @@ -353,47 +409,29 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct mxic_spi *mxic = spi_master_get_devdata(mem->spi->master); - int nio = 1, i, ret; - u32 ss_ctrl; + int i, ret; u8 addr[8]; + u8 cmd[2]; ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz); if (ret) return ret; - if (mem->spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) - nio = 4; - else if (mem->spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) - nio = 2; + mxic_spi_set_hc_cfg(mem->spi, HC_CFG_MAN_CS_EN); - writel(HC_CFG_NIO(nio) | - HC_CFG_TYPE(mem->spi->chip_select, HC_CFG_TYPE_SPI_NOR) | - HC_CFG_SLV_ACT(mem->spi->chip_select) | HC_CFG_IDLE_SIO_LVL(1) | - HC_CFG_MAN_CS_EN, - mxic->regs + HC_CFG); writel(HC_EN_BIT, mxic->regs + HC_EN); - ss_ctrl = OP_CMD_BYTES(1) | OP_CMD_BUSW(fls(op->cmd.buswidth) - 1); - - if (op->addr.nbytes) - ss_ctrl |= OP_ADDR_BYTES(op->addr.nbytes) | - OP_ADDR_BUSW(fls(op->addr.buswidth) - 1); - - if (op->dummy.nbytes) - ss_ctrl |= OP_DUMMY_CYC(op->dummy.nbytes); - - if (op->data.nbytes) { - ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1); - if (op->data.dir == SPI_MEM_DATA_IN) - ss_ctrl |= OP_READ; - } - - writel(ss_ctrl, mxic->regs + SS_CTRL(mem->spi->chip_select)); + writel(mxic_spi_mem_prep_op_cfg(op), + mxic->regs + SS_CTRL(mem->spi->chip_select)); writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT, mxic->regs + HC_CFG); - ret = mxic_spi_data_xfer(mxic, &op->cmd.opcode, NULL, 1); + cmd[0] = op->cmd.opcode; + if (op->cmd.nbytes == 2) + cmd[1] = op->cmd.ext_opcode; + + ret = mxic_spi_data_xfer(mxic, cmd, NULL, op->cmd.nbytes); if (ret) goto out; @@ -566,7 +604,8 @@ static int mxic_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(8); master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_TX_DUAL | - SPI_RX_QUAD | SPI_TX_QUAD; + SPI_RX_QUAD | SPI_TX_QUAD | + SPI_RX_OCTAL | SPI_TX_OCTAL; mxic_spi_hw_init(mxic);