From patchwork Thu Jun 4 12:44:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 1303507 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=ti.com header.i=@ti.com header.a=rsa-sha256 header.s=ti-com-17Q1 header.b=N3HosGcM; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49d5CG4S0cz9sSc for ; Thu, 4 Jun 2020 22:47:22 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id BE1A381C73; Thu, 4 Jun 2020 14:45:36 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=ti.com header.i=@ti.com header.b="N3HosGcM"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id CFBC8819A6; Thu, 4 Jun 2020 14:45:27 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,SPF_HELO_NONE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from fllv0015.ext.ti.com (fllv0015.ext.ti.com [198.47.19.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 5771C818C7 for ; Thu, 4 Jun 2020 14:45:21 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=p.yadav@ti.com Received: from fllv0034.itg.ti.com ([10.64.40.246]) by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id 054CjBcX062898; Thu, 4 Jun 2020 07:45:11 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1591274711; bh=mqU6CilfrTE+17YNaPfmHWBzXh5dncp2MOM3NXz/t+Q=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=N3HosGcMrN+UcDQVNPMR/JRdzcO9dLf7LlfhBuSYmAp7taZ+9QEyNfHsJ4v4KJ81m LKg+P/SH62VnqkMDNRz2HqvdW+MlsQa4eqFpzJkb5VbxBbQzgf6CeLDdhtwQcwYeC9 V/JIrNmQmITstZdcZkBGMUjPvF0kf48CdxmOGhPY= Received: from DFLE112.ent.ti.com (dfle112.ent.ti.com [10.64.6.33]) by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 054CjBX8050015 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Thu, 4 Jun 2020 07:45:11 -0500 Received: from DFLE113.ent.ti.com (10.64.6.34) by DFLE112.ent.ti.com (10.64.6.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1979.3; Thu, 4 Jun 2020 07:45:11 -0500 Received: from fllv0040.itg.ti.com (10.64.41.20) by DFLE113.ent.ti.com (10.64.6.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1979.3 via Frontend Transport; Thu, 4 Jun 2020 07:45:11 -0500 Received: from pratyush-OptiPlex-790.dhcp.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by fllv0040.itg.ti.com (8.15.2/8.15.2) with ESMTP id 054CicBZ004409; Thu, 4 Jun 2020 07:45:08 -0500 From: Pratyush Yadav To: Chris Packham , Jagan Teki , Vignesh R , Ryder Lee , Weijie Gao , GSS_MTK_Uboot_upstream , CC: Pratyush Yadav , Sekhar Nori , "Tan, Ley Foon" Subject: [PATCH v5 09/21] mtd: spi-nor-core: Rework hwcaps selection Date: Thu, 4 Jun 2020 18:14:26 +0530 Message-ID: <20200604124438.16783-10-p.yadav@ti.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200604124438.16783-1-p.yadav@ti.com> References: <20200604124438.16783-1-p.yadav@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.30rc1 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.2 at phobos.denx.de X-Virus-Status: Clean The spi-mem layer provides a spi_mem_supports_op() function to check whether a specific operation is supported by the controller or not. This is much more accurate than the hwcaps selection logic based on SPI_{RX,TX}_ flags. Rework the hwcaps selection logic to use spi_mem_supports_op(). To make sure the build doesn't break for boards not using CONFIG_DM_SPI, add a simple SPI_{RX,TX}_ based hwcaps selection logic in spi-mem-nodm similar to spi_mem_default_supports_op(). This change is only compile-tested. To avoid SPL size problems on the x530 board, the old hwcaps selection is still kept around. Leaving the code in-place was getting difficult to read and understand, so the code is restructured to have it all in one isolated function. As a result of this, the parameter hwcaps to spi_nor_setup() is no longer needed. Remove it. Based on the Linux commit c76f5089796a (mtd: spi-nor: Rework hwcaps selection for the spi-mem case, 2019-08-06) Signed-off-by: Pratyush Yadav --- drivers/mtd/spi/Kconfig | 9 ++ drivers/mtd/spi/spi-nor-core.c | 244 ++++++++++++++++++++++++++------- drivers/spi/spi-mem-nodm.c | 62 +++++++++ include/linux/mtd/spi-nor.h | 17 ++- 4 files changed, 280 insertions(+), 52 deletions(-) diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 018e8c597e..09ec18cdf2 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -88,6 +88,15 @@ config SPI_FLASH_SFDP_SUPPORT SPI NOR flashes using Serial Flash Discoverable Parameters (SFDP) tables as per JESD216 standard. +config SPI_FLASH_SMART_HWCAPS + bool "Smart hardware capability detection based on SPI MEM supports_op() hook" + default y + help + Enable support for smart hardware capability detection based on SPI + MEM supports_op() hook that lets controllers express whether they + can support a type of operation in a much more refined way compared + to using flags like SPI_RX_DUAL, SPI_TX_QUAD, etc. + config SPI_FLASH_BAR bool "SPI flash Bank/Extended address register support" help diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index 2494c2e724..a50575ca8b 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -2294,6 +2294,194 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) ARRAY_SIZE(hwcaps_pp2cmd)); } +#ifdef CONFIG_SPI_FLASH_SMART_HWCAPS +/** + * spi_nor_check_op - check if the operation is supported by controller + * @nor: pointer to a 'struct spi_nor' + * @op: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_check_op(struct spi_nor *nor, + struct spi_mem_op *op) +{ + /* + * First test with 4 address bytes. The opcode itself might be a 3B + * addressing opcode but we don't care, because SPI controller + * implementation should not check the opcode, but just the sequence. + */ + op->addr.nbytes = 4; + if (!spi_mem_supports_op(nor->spi, op)) { + if (nor->mtd.size > SZ_16M) + return -ENOTSUPP; + + /* If flash size <= 16MB, 3 address bytes are sufficient */ + op->addr.nbytes = 3; + if (!spi_mem_supports_op(nor->spi, op)) + return -ENOTSUPP; + } + + return 0; +} + +/** + * spi_nor_check_readop - check if the read op is supported by controller + * @nor: pointer to a 'struct spi_nor' + * @read: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_check_readop(struct spi_nor *nor, + const struct spi_nor_read_command *read) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1), + SPI_MEM_OP_ADDR(3, 0, 1), + SPI_MEM_OP_DUMMY(0, 1), + SPI_MEM_OP_DATA_IN(0, NULL, 1)); + + op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto); + op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto); + op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto); + op.dummy.buswidth = op.addr.buswidth; + op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) * + op.dummy.buswidth / 8; + + return spi_nor_check_op(nor, &op); +} + +/** + * spi_nor_check_pp - check if the page program op is supported by controller + * @nor: pointer to a 'struct spi_nor' + * @pp: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_check_pp(struct spi_nor *nor, + const struct spi_nor_pp_command *pp) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1), + SPI_MEM_OP_ADDR(3, 0, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 1)); + + op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto); + op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto); + op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto); + + return spi_nor_check_op(nor, &op); +} + +/** + * spi_nor_adjust_hwcaps - Find optimal Read/Write protocol based on SPI + * controller capabilities + * @nor: pointer to a 'struct spi_nor' + * @params: pointer to the 'struct spi_nor_flash_parameter' + * representing SPI NOR flash capabilities + * @hwcaps: pointer to resulting capabilities after adjusting + * according to controller and flash's capability + * + * Discard caps based on what the SPI controller actually supports (using + * spi_mem_supports_op()). + */ +static void +spi_nor_adjust_hwcaps(struct spi_nor *nor, + const struct spi_nor_flash_parameter *params, + u32 *hwcaps) +{ + unsigned int cap; + + /* + * Enable all caps by default. We will mask them after checking what's + * really supported using spi_mem_supports_op(). + */ + *hwcaps = SNOR_HWCAPS_ALL; + + /* 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; + + if (!(*hwcaps & BIT(cap))) + continue; + + rdidx = spi_nor_hwcaps_read2cmd(BIT(cap)); + if (rdidx >= 0 && + spi_nor_check_readop(nor, ¶ms->reads[rdidx])) + *hwcaps &= ~BIT(cap); + + ppidx = spi_nor_hwcaps_pp2cmd(BIT(cap)); + if (ppidx < 0) + continue; + + if (spi_nor_check_pp(nor, ¶ms->page_programs[ppidx])) + *hwcaps &= ~BIT(cap); + } +} +#else +/** + * spi_nor_adjust_hwcaps - Find optimal Read/Write protocol based on SPI + * controller capabilities + * @nor: pointer to a 'struct spi_nor' + * @params: pointer to the 'struct spi_nor_flash_parameter' + * representing SPI NOR flash capabilities + * @hwcaps: pointer to resulting capabilities after adjusting + * according to controller and flash's capability + * + * Select caps based on what the SPI controller and SPI flash both support. + */ +static void +spi_nor_adjust_hwcaps(struct spi_nor *nor, + const struct spi_nor_flash_parameter *params, + u32 *hwcaps) +{ + struct spi_slave *spi = nor->spi; + u32 ignored_mask = (SNOR_HWCAPS_READ_2_2_2 | + SNOR_HWCAPS_READ_4_4_4 | + SNOR_HWCAPS_READ_8_8_8 | + SNOR_HWCAPS_PP_4_4_4 | + SNOR_HWCAPS_PP_8_8_8); + u32 spi_hwcaps = (SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST | + SNOR_HWCAPS_PP); + + /* Get the hardware capabilities the SPI controller supports. */ + if (spi->mode & SPI_RX_OCTAL) { + spi_hwcaps |= SNOR_HWCAPS_READ_1_1_8; + + if (spi->mode & SPI_TX_OCTAL) + spi_hwcaps |= (SNOR_HWCAPS_READ_1_8_8 | + SNOR_HWCAPS_PP_1_1_8 | + SNOR_HWCAPS_PP_1_8_8); + } else if (spi->mode & SPI_RX_QUAD) { + spi_hwcaps |= SNOR_HWCAPS_READ_1_1_4; + + if (spi->mode & SPI_TX_QUAD) + spi_hwcaps |= (SNOR_HWCAPS_READ_1_4_4 | + SNOR_HWCAPS_PP_1_1_4 | + SNOR_HWCAPS_PP_1_4_4); + } else if (spi->mode & SPI_RX_DUAL) { + spi_hwcaps |= SNOR_HWCAPS_READ_1_1_2; + + if (spi->mode & SPI_TX_DUAL) + spi_hwcaps |= SNOR_HWCAPS_READ_1_2_2; + } + + /* + * Keep only the hardware capabilities supported by both the SPI + * controller and the SPI flash memory. + */ + *hwcaps = spi_hwcaps & params->hwcaps.mask; + if (*hwcaps & ignored_mask) { + dev_dbg(nor->dev, + "SPI n-n-n protocols are not supported yet.\n"); + *hwcaps &= ~ignored_mask; + } +} +#endif /* CONFIG_SPI_FLASH_SMART_HWCAPS */ + static int spi_nor_select_read(struct spi_nor *nor, const struct spi_nor_flash_parameter *params, u32 shared_hwcaps) @@ -2374,30 +2562,13 @@ static int spi_nor_select_erase(struct spi_nor *nor, static int spi_nor_default_setup(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params, - const struct spi_nor_hwcaps *hwcaps) + const struct spi_nor_flash_parameter *params) { - u32 ignored_mask, shared_mask; + u32 shared_mask; bool enable_quad_io; int err; - /* - * Keep only the hardware capabilities supported by both the SPI - * controller and the SPI flash memory. - */ - shared_mask = hwcaps->mask & params->hwcaps.mask; - - /* SPI n-n-n protocols are not supported yet. */ - ignored_mask = (SNOR_HWCAPS_READ_2_2_2 | - SNOR_HWCAPS_READ_4_4_4 | - SNOR_HWCAPS_READ_8_8_8 | - SNOR_HWCAPS_PP_4_4_4 | - SNOR_HWCAPS_PP_8_8_8); - if (shared_mask & ignored_mask) { - dev_dbg(nor->dev, - "SPI n-n-n protocols are not supported yet.\n"); - shared_mask &= ~ignored_mask; - } + spi_nor_adjust_hwcaps(nor, params, &shared_mask); /* Select the (Fast) Read command. */ err = spi_nor_select_read(nor, params, shared_mask); @@ -2435,13 +2606,12 @@ static int spi_nor_default_setup(struct spi_nor *nor, } static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params, - const struct spi_nor_hwcaps *hwcaps) + const struct spi_nor_flash_parameter *params) { if (!nor->setup) return 0; - return nor->setup(nor, info, params, hwcaps); + return nor->setup(nor, info, params); } static int spi_nor_init(struct spi_nor *nor) @@ -2496,11 +2666,6 @@ int spi_nor_scan(struct spi_nor *nor) struct spi_nor_flash_parameter params; const struct flash_info *info = NULL; struct mtd_info *mtd = &nor->mtd; - struct spi_nor_hwcaps hwcaps = { - .mask = SNOR_HWCAPS_READ | - SNOR_HWCAPS_READ_FAST | - SNOR_HWCAPS_PP, - }; struct spi_slave *spi = nor->spi; int ret; @@ -2515,27 +2680,6 @@ int spi_nor_scan(struct spi_nor *nor) nor->setup = spi_nor_default_setup; - if (spi->mode & SPI_RX_OCTAL) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8; - - if (spi->mode & SPI_TX_OCTAL) - hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 | - SNOR_HWCAPS_PP_1_1_8 | - SNOR_HWCAPS_PP_1_8_8); - } else if (spi->mode & SPI_RX_QUAD) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; - - if (spi->mode & SPI_TX_QUAD) - hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 | - SNOR_HWCAPS_PP_1_1_4 | - SNOR_HWCAPS_PP_1_4_4); - } else if (spi->mode & SPI_RX_DUAL) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; - - if (spi->mode & SPI_TX_DUAL) - hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2; - } - info = spi_nor_read_id(nor); if (IS_ERR_OR_NULL(info)) return -ENOENT; @@ -2609,7 +2753,7 @@ int spi_nor_scan(struct spi_nor *nor) * - set the SPI protocols for register and memory accesses. * - set the Quad Enable bit if needed (required by SPI x-y-4 protos). */ - ret = spi_nor_setup(nor, info, ¶ms, &hwcaps); + ret = spi_nor_setup(nor, info, ¶ms); if (ret) return ret; diff --git a/drivers/spi/spi-mem-nodm.c b/drivers/spi/spi-mem-nodm.c index db54101383..050d25040c 100644 --- a/drivers/spi/spi-mem-nodm.c +++ b/drivers/spi/spi-mem-nodm.c @@ -105,3 +105,65 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, return 0; } + +static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx) +{ + u32 mode = slave->mode; + + switch (buswidth) { + case 1: + return 0; + + case 2: + if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) || + (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD)))) + return 0; + + break; + + case 4: + if ((tx && (mode & SPI_TX_QUAD)) || + (!tx && (mode & SPI_RX_QUAD))) + return 0; + + break; + case 8: + if ((tx && (mode & SPI_TX_OCTAL)) || + (!tx && (mode & SPI_RX_OCTAL))) + return 0; + + break; + + default: + break; + } + + return -ENOTSUPP; +} + +bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op) +{ + if (spi_check_buswidth_req(slave, op->cmd.buswidth, true)) + return false; + + if (op->addr.nbytes && + spi_check_buswidth_req(slave, op->addr.buswidth, true)) + return false; + + if (op->dummy.nbytes && + spi_check_buswidth_req(slave, op->dummy.buswidth, true)) + return false; + + if (op->data.dir != SPI_MEM_NO_DATA && + spi_check_buswidth_req(slave, op->data.buswidth, + 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; +} diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 5842e9d6ee..cca7919fdd 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -312,6 +312,20 @@ struct spi_nor_hwcaps { #define SNOR_HWCAPS_PP_1_8_8 BIT(21) #define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \ + SNOR_HWCAPS_READ_4_4_4 | \ + SNOR_HWCAPS_READ_8_8_8 | \ + SNOR_HWCAPS_PP_4_4_4 | \ + SNOR_HWCAPS_PP_8_8_8) + +#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) + +#define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \ + SNOR_HWCAPS_PP_MASK) + struct spi_nor_read_command { u8 num_mode_clocks; u8 num_wait_states; @@ -459,8 +473,7 @@ struct spi_nor { struct spi_nor_fixups *fixups; int (*setup)(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params, - const struct spi_nor_hwcaps *hwcaps); + const struct spi_nor_flash_parameter *params); int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);