From patchwork Fri Jun 28 14:03:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Erez Geva X-Patchwork-Id: 1953956 X-Patchwork-Delegate: tudor.ambarus@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=patchwork.ozlabs.org) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4W9cd23rqYz20b0 for ; Sat, 29 Jun 2024 00:04:37 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=+8DDHFo0qeTyou784iQETnexoIN4Vvzc0oReTS6I+Ns=; b=ujUHJFNnEDOS1H 7TsbmzT4TOl6VyuHaV6K/Zr4drpKWy58RaIUM/BDXgiCznDxB+mBFJt8XA4eASIcrvquja6mKmLBD VQ3xwqnLjLlx4AuV9fowGvy5vTdg3DYlAB45Y9XkLsLb+qIIWEe3pwc0u9dG3ISukfYNi6OWC8HHb UHcXpugqY4iUaONJrlOmDdcBZVwyYfHM6QCfRm1jY9VmfKKo0bzNr+VkFrI/WxE+EMAAIFZPsirBf y8by92yJN+ujNjKS8PfD8YsCDdB5OUutwvpoeKNpM0OLht3JWN4+HROeCHjSOkKX9ZJTMFsylqOl5 YvVBLLhy4DagJOwQ8sSg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1sNCDA-0000000Dv31-2Pjp; Fri, 28 Jun 2024 14:04:24 +0000 Received: from chessie.fmt1.pfcs.com ([66.220.13.234] helo=chessie.everett.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1sNCD5-0000000Dv0H-2rp9 for linux-mtd@lists.infradead.org; Fri, 28 Jun 2024 14:04:21 +0000 Received: from localhost.localdomain (unknown [31.16.248.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by chessie.everett.org (Postfix) with ESMTPSA id 4W9ccR4gSBzMQLJ; Fri, 28 Jun 2024 14:04:07 +0000 (UTC) From: Erez Geva To: linux-mtd@lists.infradead.org, Tudor Ambarus , Pratyush Yadav , Michael Walle Cc: linux-kernel@vger.kernel.org, Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , devicetree@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , Erez Geva Subject: [PATCH 2/4] Add support for SPI-NOR Macronix OTP. Date: Fri, 28 Jun 2024 16:03:26 +0200 Message-Id: <20240628140328.279792-3-erezgeva@nwtime.org> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240628140328.279792-1-erezgeva@nwtime.org> References: <20240628140328.279792-1-erezgeva@nwtime.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240628_070419_805189_2B600DAC X-CRM114-Status: GOOD ( 17.98 ) X-Spam-Score: -0.0 (/) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Erez Geva Macronix SPI-NOR support OTP. Add callbacks to read, write and lock the OTP. Notice Macronix OTP do not support erase. Every bit written with '0', can not be changed further. Content analysis details: (-0.0 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org From: Erez Geva Macronix SPI-NOR support OTP. Add callbacks to read, write and lock the OTP. Notice Macronix OTP do not support erase. Every bit written with '0', can not be changed further. Signed-off-by: Erez Geva --- drivers/mtd/spi-nor/macronix.c | 185 +++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 10 ++ 2 files changed, 195 insertions(+) diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index ea6be95e75a5..f210231468a6 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -8,6 +8,180 @@ #include "core.h" +/** + * macronix_nor_otp_enter() - Send Enter Secured OTP instruction to the chip. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ +static int macronix_nor_otp_enter(struct spi_nor *nor) +{ + int error; + + error = spi_nor_send_cmd(nor, SPINOR_OP_ENSO); + + if (error) + dev_dbg(nor->dev, "error %d on Macronix Enter Secured OTP\n", error); + + return error; +} + +/** + * macronix_nor_otp_exit() - Send Exit Secured OTP instruction to the chip. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ +static int macronix_nor_otp_exit(struct spi_nor *nor) +{ + int error; + + error = spi_nor_send_cmd(nor, SPINOR_OP_EXSO); + + if (error) + dev_dbg(nor->dev, "error %d on Macronix Enter Secured OTP\n", error); + + return error; +} + +/** + * macronix_nor_otp_read() - read security register + * @nor: pointer to 'struct spi_nor' + * @addr: offset to read from + * @len: number of bytes to read + * @buf: pointer to dst buffer + * + * Return: number of bytes read successfully, -errno otherwise + */ +static int macronix_nor_otp_read(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) +{ + int ret, error; + + error = macronix_nor_otp_enter(nor); + if (error) + return error; + + ret = spi_nor_read_data(nor, addr, len, buf); + + error = macronix_nor_otp_exit(nor); + + if (ret < 0) + dev_dbg(nor->dev, "error %d on Macronix read OTP data\n", ret); + else if (error) + return error; + + return ret; +} + +/** + * macronix_nor_otp_write() - write security register + * @nor: pointer to 'struct spi_nor' + * @addr: offset to write to + * @len: number of bytes to write + * @buf: pointer to src buffer + * + * Return: number of bytes written successfully, -errno otherwise + */ +static int macronix_nor_otp_write(struct spi_nor *nor, loff_t addr, size_t len, const u8 *buf) +{ + int error, ret = 0; + + error = macronix_nor_otp_enter(nor); + if (error) + return error; + + error = spi_nor_write_enable(nor); + if (error) + goto otp_write_err; + + ret = spi_nor_write_data(nor, addr, len, buf); + if (ret < 0) { + dev_dbg(nor->dev, "error %d on Macronix write OTP data\n", ret); + goto otp_write_err; + } + + error = spi_nor_wait_till_ready(nor); + if (error) + dev_dbg(nor->dev, "error %d on Macronix waiting write OTP finish\n", error); + +otp_write_err: + + error = macronix_nor_otp_exit(nor); + + return ret; +} + +/** + * macronix_nor_otp_lock() - lock the OTP region + * @nor: pointer to 'struct spi_nor' + * @region: OTP region + * + * Return: 0 on success, -errno otherwise. + */ +static int macronix_nor_otp_lock(struct spi_nor *nor, unsigned int region) +{ + int error; + u8 *rdscur = nor->bouncebuf; + + error = spi_nor_read_reg(nor, SPINOR_OP_RDSCUR, 1); + if (error) { + dev_dbg(nor->dev, "error %d on read security register\n", error); + return error; + } + + switch (region) { + case 0: /* Lock 1st 4K-bit region */ + if (rdscur[0] & SEC_REG_LDSO) + return 0; /* Already locked */ + rdscur[0] |= SEC_REG_LDSO; + break; + case 1: /* Lock 2nd 4K-bit region */ + if (rdscur[0] & SEC_REG_LDS1) + return 0; /* Already locked */ + rdscur[0] |= SEC_REG_LDS1; + break; + default: + return 0; /* Unknown region */ + } + + error = spi_nor_write_reg(nor, SPINOR_OP_WRSCUR, 1); + if (error) + dev_dbg(nor->dev, "error %d on update security register\n", error); + + return error; +} + +/** + * macronix_nor_otp_is_locked() - get the OTP region lock status + * @nor: pointer to 'struct spi_nor' + * @region: OTP region + * + * Return: 0 on success, -errno otherwise. + */ +static int macronix_nor_otp_is_locked(struct spi_nor *nor, unsigned int region) +{ + int ret; + u8 *rdscur = nor->bouncebuf; + + ret = spi_nor_read_reg(nor, SPINOR_OP_RDSCUR, 1); + if (ret) { + dev_dbg(nor->dev, "error %d on read security register\n", ret); + return ret; + } + + switch (region) { + case 0: /* 1st 4K-bit region */ + ret = (rdscur[0] & SEC_REG_LDSO) > 0; + break; + case 1: /* 2nd 4K-bit region */ + ret = (rdscur[0] & SEC_REG_LDS1) > 0; + break; + default: /* Unknown region */ + break; + } + return ret; +} + static int mx25l25635_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -190,8 +364,19 @@ static void macronix_nor_default_init(struct spi_nor *nor) nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; } +static const struct spi_nor_otp_ops macronix_nor_otp_ops = { + .read = macronix_nor_otp_read, + .write = macronix_nor_otp_write, + /* .erase = Macronix OTP do not support erase, */ + .lock = macronix_nor_otp_lock, + .is_locked = macronix_nor_otp_is_locked, +}; + static int macronix_nor_late_init(struct spi_nor *nor) { + if (nor->params->otp.org->n_regions) + nor->params->otp.ops = ¯onix_nor_otp_ops; + if (!nor->params->set_4byte_addr_mode) nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index cdcfe0fd2e7d..f5965f90f51e 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -81,6 +81,16 @@ #define SPINOR_OP_BP 0x02 /* Byte program */ #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ +/* Used by Macronix OTP. */ +#define SPINOR_OP_RDSCUR 0x2b /* read security register */ +#define SPINOR_OP_WRSCUR 0x2f /* write security register */ +#define SPINOR_OP_ENSO 0xb1 /* enter secured OTP */ +#define SPINOR_OP_EXSO 0xc1 /* exit secured OTP */ + +/* Security register */ +#define SEC_REG_LDSO BIT(1) /* lock-down bit 1st 4K-bit */ +#define SEC_REG_LDS1 BIT(0) /* lock-down bit 2nd 4K-bit */ + /* Used for Macronix and Winbond flashes. */ #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */