Message ID | 20230523101637.3009746-1-AVKrasnov@sberdevices.ru |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp2030889vqo; Tue, 23 May 2023 03:22:39 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4MIDbMraqt6fm/qAqL7zCdAjuTcUwSEEYKO69cS/qZiDz0UZUeHNGVPpRHaiXMv/y6we8p X-Received: by 2002:a17:90b:4003:b0:253:3e9d:f92a with SMTP id ie3-20020a17090b400300b002533e9df92amr11869791pjb.29.1684837359700; Tue, 23 May 2023 03:22:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684837359; cv=none; d=google.com; s=arc-20160816; b=I6b/IV2HkY2C0Ey3BPhczOqfx+Clkzy4TAgmTLj0RmutNG2egBdBdbdU/DqhZ7ZoX8 TQxa4VYDPJOijuiHmWjM2xAOCZ8gMeWPSO6JXs2CmXhwGFKuyBlAQv7GSZPSFT5eIpM6 2iZpbsYBqoxP1FPEB3twIL5rN8afIEeUqRx0QXwr7P7ho9yYk6rKIWbyW2pFCx406BQJ GJEmX1yhJY19xSpHfV3QqPd0EeWv5lmQHw2sNoMPqipxsoAZFrTRl6enMq1gq+lBNe1S ODZlgBtk4nKlFVELjznqU5AKOBhuJFrthGkGxGf/cZQap8Fe6N/QTinCLR1DjCun6xWQ K9EA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=Bk3y/ekW1k32shYNnUtpGVBff1mL1/GErdcGcnG133Y=; b=KaiRkS+SPb1QzwBLH40PBqIgaG/88d2jdhghcCI/Vm/Eivl7B+mdfBIGuy7Xo1EOH3 PUAa7SDWgPNQjbsHm6AkQmz7Y+r8WNKxm3dvedDon7FwxV/taEjueSbhX/bZ+Bf3V3ly 1ANUe+vndbXJA7lfy1j008egOU9D8Z1Nc1nTFpe++uxu1D53Sayh3C0pcscPxXnvM9QO VIDH611c3ItNv1ohGjrBxedLCYGiJ8YSI0kO6GCkFShlXQnscsd4b+D5v32D0wvQvueT /TuL8/Wq/xuoSLx0JC9SZw/Nef+sreX3O8CT3loM9AfDd7o2MZMVmilfemJOyazAMce6 d3zQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@sberdevices.ru header.s=mail header.b=lwKXK7oV; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=sberdevices.ru Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id iq12-20020a17090afb4c00b00250c1397245si4628743pjb.121.2023.05.23.03.22.26; Tue, 23 May 2023 03:22:39 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@sberdevices.ru header.s=mail header.b=lwKXK7oV; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=sberdevices.ru Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236511AbjEWKVV (ORCPT <rfc822;ahmedalshaiji.dev@gmail.com> + 99 others); Tue, 23 May 2023 06:21:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51270 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232621AbjEWKVT (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Tue, 23 May 2023 06:21:19 -0400 Received: from mx.sberdevices.ru (mx.sberdevices.ru [45.89.227.171]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 42289100; Tue, 23 May 2023 03:21:16 -0700 (PDT) Received: from s-lin-edge02.sberdevices.ru (localhost [127.0.0.1]) by mx.sberdevices.ru (Postfix) with ESMTP id 43CB05FD0D; Tue, 23 May 2023 13:21:14 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sberdevices.ru; s=mail; t=1684837274; bh=Bk3y/ekW1k32shYNnUtpGVBff1mL1/GErdcGcnG133Y=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type; b=lwKXK7oVPa99pFr5ap9b7ja++6PtDMIXjDCpGU/xv4LmfZ2hR/K6JfTWEjCYWHv6J hcMRdn9plF+Wm+iqMZSjrugYxKYP18B6zg91Sb/2EAD0jMOJdv5dTtwaCp/16vZdLT HWTnQaqTUK5sRkM1sYKbHBQfGXqKp5GDRIsxBu9WjRY508kYm4YCJxw1kg2cyJdRec LqQLH1bsu3BLVi8f7gV0+UV31PoJbGorJuipU2Yf2HHjAWmap92xEMLPQuZTDavWPo PZdqdPHs8WMglGgtdJeml2rzofoHoZHnDcGjR8k1TA5EXq7ER34zBb8hiYgyC0aP5W w8aif/SqwgkfQ== Received: from S-MS-EXCH01.sberdevices.ru (S-MS-EXCH01.sberdevices.ru [172.16.1.4]) by mx.sberdevices.ru (Postfix) with ESMTP; Tue, 23 May 2023 13:21:10 +0300 (MSK) From: Arseniy Krasnov <AVKrasnov@sberdevices.ru> To: Miquel Raynal <miquel.raynal@bootlin.com>, Richard Weinberger <richard@nod.at>, Vignesh Raghavendra <vigneshr@ti.com>, Sumit Semwal <sumit.semwal@linaro.org>, =?utf-8?q?Christian_K=C3=B6nig?= <christian.koenig@amd.com>, Arseniy Krasnov <AVKrasnov@sberdevices.ru> CC: <oxffffaa@gmail.com>, <kernel@sberdevices.ru>, Boris Brezillon <boris.brezillon@collabora.com>, Jaime Liao <jaimeliao.tw@gmail.com>, Jaime Liao <jaimeliao@mxic.com.tw>, Mason Yang <masonccyang@mxic.com.tw>, <linux-mtd@lists.infradead.org>, <linux-kernel@vger.kernel.org>, <linux-media@vger.kernel.org>, <dri-devel@lists.freedesktop.org>, <linaro-mm-sig@lists.linaro.org> Subject: [PATCH v4] mtd: rawnand: macronix: OTP access for MX30LFxG18AC Date: Tue, 23 May 2023 13:16:34 +0300 Message-ID: <20230523101637.3009746-1-AVKrasnov@sberdevices.ru> X-Mailer: git-send-email 2.35.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Originating-IP: [172.16.1.6] X-ClientProxiedBy: S-MS-EXCH01.sberdevices.ru (172.16.1.4) To S-MS-EXCH01.sberdevices.ru (172.16.1.4) X-KSMG-Rule-ID: 4 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Status: not scanned, disabled by settings X-KSMG-AntiSpam-Interceptor-Info: not scanned X-KSMG-AntiPhishing: not scanned, disabled by settings X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 1.1.2.30, bases: 2023/05/23 05:11:00 #21371280 X-KSMG-AntiVirus-Status: Clean, skipped X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_NONE, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1766680019236461416?= X-GMAIL-MSGID: =?utf-8?q?1766680019236461416?= |
Series |
[v4] mtd: rawnand: macronix: OTP access for MX30LFxG18AC
|
|
Commit Message
Arseniy Krasnov
May 23, 2023, 10:16 a.m. UTC
This adds support for OTP area access on MX30LFxG18AC chip series.
Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
v1 -> v2:
* Add slab.h include due to kernel test robot error.
v2 -> v3:
* Use 'uint64_t' as input argument for 'do_div()' instead
of 'unsigned long' due to kernel test robot error.
v3 -> v4:
* Use 'dev_err()' instead of 'WARN()'.
* Call 'match_string()' before checking 'supports_set_get_features'
in 'macronix_nand_setup_otp().
* Use 'u8' instead of 'uint8_t' as ./checkpatch.pl wants.
drivers/mtd/nand/raw/nand_macronix.c | 216 +++++++++++++++++++++++++++
1 file changed, 216 insertions(+)
Comments
Hello, AVKrasnov@sberdevices.ru wrote on Tue, 23 May 2023 13:16:34 +0300: > This adds support for OTP area access on MX30LFxG18AC chip series. Jaime, any feedback on this? Will you test it? How are we supposed to test the OTP is locked? I see this is still an open point. > > Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru> > --- > v1 -> v2: > * Add slab.h include due to kernel test robot error. > v2 -> v3: > * Use 'uint64_t' as input argument for 'do_div()' instead > of 'unsigned long' due to kernel test robot error. > v3 -> v4: > * Use 'dev_err()' instead of 'WARN()'. > * Call 'match_string()' before checking 'supports_set_get_features' > in 'macronix_nand_setup_otp(). > * Use 'u8' instead of 'uint8_t' as ./checkpatch.pl wants. > > drivers/mtd/nand/raw/nand_macronix.c | 216 +++++++++++++++++++++++++++ > 1 file changed, 216 insertions(+) > > diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c > index 1472f925f386..be1ffa93bebb 100644 > --- a/drivers/mtd/nand/raw/nand_macronix.c > +++ b/drivers/mtd/nand/raw/nand_macronix.c > @@ -6,6 +6,7 @@ > * Author: Boris Brezillon <boris.brezillon@free-electrons.com> > */ > > +#include <linux/slab.h> > #include "linux/delay.h" > #include "internals.h" > > @@ -31,6 +32,20 @@ > > #define MXIC_CMD_POWER_DOWN 0xB9 > > +#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 > +#define MACRONIX_30LFXG18AC_OTP_START_PAGE 0 > +#define MACRONIX_30LFXG18AC_OTP_PAGES 30 > +#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 > +#define MACRONIX_30LFXG18AC_OTP_START_BYTE \ > + (MACRONIX_30LFXG18AC_OTP_START_PAGE * \ > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) > +#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ > + (MACRONIX_30LFXG18AC_OTP_PAGES * \ > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) > + > +#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) > +#define MACRONIX_30LFXG18AC_OTP_LOCKED BIT(1) > + > struct nand_onfi_vendor_macronix { > u8 reserved; > u8 reliability_func; > @@ -316,6 +331,206 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip) > chip->ops.resume = mxic_nand_resume; > } > > +static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, > + size_t *retlen, > + struct otp_info *buf) > +{ > + if (len < sizeof(*buf)) > + return -EINVAL; > + > + /* Don't know how to check that OTP is locked. */ > + buf->locked = 0; > + buf->start = MACRONIX_30LFXG18AC_OTP_START_BYTE; > + buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; > + > + *retlen = sizeof(*buf); > + > + return 0; > +} > + > +static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) > +{ > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > + > + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; > + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > + feature_buf); > +} > + > +static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) > +{ > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > + > + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > + feature_buf); > +} > + > +static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, > + loff_t offs_in_flash, > + size_t len, size_t *retlen, > + u_char *buf, bool write) > +{ > + struct nand_chip *nand; > + size_t bytes_handled; > + off_t offs_in_page; > + void *dma_buf; > + u64 page; > + int ret; > + > + /* 'nand_prog/read_page_op()' may use 'buf' as DMA buffer, > + * so allocate properly aligned memory for it. This is > + * needed because cross page accesses may lead to unaligned > + * buffer address for DMA. > + */ > + dma_buf = kmalloc(MACRONIX_30LFXG18AC_OTP_PAGE_SIZE, GFP_KERNEL); > + if (!dma_buf) > + return -ENOMEM; > + > + nand = mtd_to_nand(mtd); > + nand_select_target(nand, 0); > + > + ret = macronix_30lfxg18ac_otp_enable(nand); > + if (ret) > + goto out_otp; > + > + page = offs_in_flash; > + /* 'page' will be result of division. */ > + offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); > + bytes_handled = 0; > + > + while (bytes_handled < len && > + page < MACRONIX_30LFXG18AC_OTP_PAGES) { > + size_t bytes_to_handle; > + > + bytes_to_handle = min_t(size_t, len - bytes_handled, > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - > + offs_in_page); > + > + if (write) { > + memcpy(dma_buf, &buf[bytes_handled], bytes_to_handle); > + ret = nand_prog_page_op(nand, page, offs_in_page, > + dma_buf, bytes_to_handle); > + } else { > + ret = nand_read_page_op(nand, page, offs_in_page, > + dma_buf, bytes_to_handle); > + if (!ret) > + memcpy(&buf[bytes_handled], dma_buf, > + bytes_to_handle); > + } > + if (ret) > + goto out_otp; > + > + bytes_handled += bytes_to_handle; > + offs_in_page = 0; > + page++; > + } > + > + *retlen = bytes_handled; > + > +out_otp: > + if (ret) > + dev_err(&mtd->dev, "failed to perform OTP IO: %i\n", ret); > + > + ret = macronix_30lfxg18ac_otp_disable(nand); > + if (ret) > + dev_err(&mtd->dev, "failed to leave OTP mode after %s\n", > + write ? "write" : "read"); > + > + nand_deselect_target(nand); > + kfree(dma_buf); > + > + return ret; > +} > + > +static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, > + size_t len, size_t *rlen, > + const u_char *buf) > +{ > + return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, > + true); > +} > + > +static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, > + size_t len, size_t *rlen, > + u_char *buf) > +{ > + return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); > +} > + > +static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, > + size_t len) > +{ > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > + struct nand_chip *nand; > + int ret; > + > + if (from != MACRONIX_30LFXG18AC_OTP_START_BYTE || > + len != MACRONIX_30LFXG18AC_OTP_SIZE_BYTES) > + return -EINVAL; > + > + dev_dbg(&mtd->dev, "locking OTP\n"); > + > + nand = mtd_to_nand(mtd); > + nand_select_target(nand, 0); > + > + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN | > + MACRONIX_30LFXG18AC_OTP_LOCKED; > + ret = nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > + feature_buf); > + if (ret) { > + dev_err(&mtd->dev, > + "failed to lock OTP (set features): %i\n", ret); > + nand_deselect_target(nand); > + return ret; > + } > + > + /* Do dummy page prog with zero address. */ > + feature_buf[0] = 0; > + ret = nand_prog_page_op(nand, 0, 0, feature_buf, 1); > + if (ret) > + dev_err(&mtd->dev, > + "failed to lock OTP (page prog): %i\n", ret); > + > + ret = macronix_30lfxg18ac_otp_disable(nand); > + if (ret) > + dev_err(&mtd->dev, "failed to leave OTP mode after lock\n"); > + > + nand_deselect_target(nand); > + > + return ret; > +} > + > +static void macronix_nand_setup_otp(struct nand_chip *chip) > +{ > + static const char * const supported_otp_models[] = { > + "MX30LF1G18AC", > + "MX30LF2G18AC", > + "MX30LF4G18AC", > + }; > + struct mtd_info *mtd; > + > + if (match_string(supported_otp_models, > + ARRAY_SIZE(supported_otp_models), > + chip->parameters.model) < 0) > + return; > + > + if (!chip->parameters.supports_set_get_features) > + return; > + > + bitmap_set(chip->parameters.get_feature_list, > + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); > + bitmap_set(chip->parameters.set_feature_list, > + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); > + > + mtd = nand_to_mtd(chip); > + mtd->_get_fact_prot_info = macronix_30lfxg18ac_get_otp_info; > + mtd->_read_fact_prot_reg = macronix_30lfxg18ac_read_otp; > + mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; > + mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; > + mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; > + mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; > +} > + > static int macronix_nand_init(struct nand_chip *chip) > { > if (nand_is_slc(chip)) > @@ -325,6 +540,7 @@ static int macronix_nand_init(struct nand_chip *chip) > macronix_nand_onfi_init(chip); > macronix_nand_block_protection_support(chip); > macronix_nand_deep_power_down_support(chip); > + macronix_nand_setup_otp(chip); > > return 0; > } Thanks, Miquèl
Hi Miquel > > Hello, > > AVKrasnov@sberdevices.ru wrote on Tue, 23 May 2023 13:16:34 +0300: > > > This adds support for OTP area access on MX30LFxG18AC chip series. > > Jaime, any feedback on this? Will you test it? > > How are we supposed to test the OTP is locked? I see this is still an > open point. After checking with internal, sub feature parameter are volatile register. It could be change after enter/exit OTP region or power cycle even OTP region have been locked. OTP operation mode still could be enter/exit and region is read only after OTP in protect mode. #program command could execute but no use after setting OTP region in protect mode. So that we can't check whether OTP region is locked via get feature. And we don't have region for checking status of OTP locked. > > > > > Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru> > > --- > > v1 -> v2: > > * Add slab.h include due to kernel test robot error. > > v2 -> v3: > > * Use 'uint64_t' as input argument for 'do_div()' instead > > of 'unsigned long' due to kernel test robot error. > > v3 -> v4: > > * Use 'dev_err()' instead of 'WARN()'. > > * Call 'match_string()' before checking 'supports_set_get_features' > > in 'macronix_nand_setup_otp(). > > * Use 'u8' instead of 'uint8_t' as ./checkpatch.pl wants. > > > > drivers/mtd/nand/raw/nand_macronix.c | 216 +++++++++++++++++++++++++++ > > 1 file changed, 216 insertions(+) > > > > diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c > > index 1472f925f386..be1ffa93bebb 100644 > > --- a/drivers/mtd/nand/raw/nand_macronix.c > > +++ b/drivers/mtd/nand/raw/nand_macronix.c > > @@ -6,6 +6,7 @@ > > * Author: Boris Brezillon <boris.brezillon@free-electrons.com> > > */ > > > > +#include <linux/slab.h> > > #include "linux/delay.h" > > #include "internals.h" > > > > @@ -31,6 +32,20 @@ > > > > #define MXIC_CMD_POWER_DOWN 0xB9 > > > > +#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 > > +#define MACRONIX_30LFXG18AC_OTP_START_PAGE 0 > > +#define MACRONIX_30LFXG18AC_OTP_PAGES 30 > > +#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 > > +#define MACRONIX_30LFXG18AC_OTP_START_BYTE \ > > + (MACRONIX_30LFXG18AC_OTP_START_PAGE * \ > > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) > > +#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ > > + (MACRONIX_30LFXG18AC_OTP_PAGES * \ > > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) > > + > > +#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) > > +#define MACRONIX_30LFXG18AC_OTP_LOCKED BIT(1) > > + > > struct nand_onfi_vendor_macronix { > > u8 reserved; > > u8 reliability_func; > > @@ -316,6 +331,206 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip) > > chip->ops.resume = mxic_nand_resume; > > } > > > > +static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, > > + size_t *retlen, > > + struct otp_info *buf) > > +{ > > + if (len < sizeof(*buf)) > > + return -EINVAL; > > + > > + /* Don't know how to check that OTP is locked. */ > > + buf->locked = 0; > > + buf->start = MACRONIX_30LFXG18AC_OTP_START_BYTE; > > + buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; > > + > > + *retlen = sizeof(*buf); > > + > > + return 0; > > +} > > + > > +static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) > > +{ > > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > > + > > + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; > > + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > > + feature_buf); > > +} > > + > > +static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) > > +{ > > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > > + > > + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > > + feature_buf); > > +} > > + > > +static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, > > + loff_t offs_in_flash, > > + size_t len, size_t *retlen, > > + u_char *buf, bool write) > > +{ > > + struct nand_chip *nand; > > + size_t bytes_handled; > > + off_t offs_in_page; > > + void *dma_buf; > > + u64 page; > > + int ret; > > + > > + /* 'nand_prog/read_page_op()' may use 'buf' as DMA buffer, > > + * so allocate properly aligned memory for it. This is > > + * needed because cross page accesses may lead to unaligned > > + * buffer address for DMA. > > + */ > > + dma_buf = kmalloc(MACRONIX_30LFXG18AC_OTP_PAGE_SIZE, GFP_KERNEL); > > + if (!dma_buf) > > + return -ENOMEM; > > + > > + nand = mtd_to_nand(mtd); > > + nand_select_target(nand, 0); > > + > > + ret = macronix_30lfxg18ac_otp_enable(nand); > > + if (ret) > > + goto out_otp; > > + > > + page = offs_in_flash; > > + /* 'page' will be result of division. */ > > + offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); > > + bytes_handled = 0; > > + > > + while (bytes_handled < len && > > + page < MACRONIX_30LFXG18AC_OTP_PAGES) { > > + size_t bytes_to_handle; > > + > > + bytes_to_handle = min_t(size_t, len - bytes_handled, > > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - > > + offs_in_page); > > + > > + if (write) { > > + memcpy(dma_buf, &buf[bytes_handled], bytes_to_handle); > > + ret = nand_prog_page_op(nand, page, offs_in_page, > > + dma_buf, bytes_to_handle); > > + } else { > > + ret = nand_read_page_op(nand, page, offs_in_page, > > + dma_buf, bytes_to_handle); > > + if (!ret) > > + memcpy(&buf[bytes_handled], dma_buf, > > + bytes_to_handle); > > + } > > + if (ret) > > + goto out_otp; > > + > > + bytes_handled += bytes_to_handle; > > + offs_in_page = 0; > > + page++; > > + } > > + > > + *retlen = bytes_handled; > > + > > +out_otp: > > + if (ret) > > + dev_err(&mtd->dev, "failed to perform OTP IO: %i\n", ret); > > + > > + ret = macronix_30lfxg18ac_otp_disable(nand); > > + if (ret) > > + dev_err(&mtd->dev, "failed to leave OTP mode after %s\n", > > + write ? "write" : "read"); > > + > > + nand_deselect_target(nand); > > + kfree(dma_buf); > > + > > + return ret; > > +} > > + > > +static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, > > + size_t len, size_t *rlen, > > + const u_char *buf) > > +{ > > + return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, > > + true); > > +} > > + > > +static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, > > + size_t len, size_t *rlen, > > + u_char *buf) > > +{ > > + return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); > > +} > > + > > +static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, > > + size_t len) > > +{ > > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > > + struct nand_chip *nand; > > + int ret; > > + > > + if (from != MACRONIX_30LFXG18AC_OTP_START_BYTE || > > + len != MACRONIX_30LFXG18AC_OTP_SIZE_BYTES) > > + return -EINVAL; > > + > > + dev_dbg(&mtd->dev, "locking OTP\n"); > > + > > + nand = mtd_to_nand(mtd); > > + nand_select_target(nand, 0); > > + > > + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN | > > + MACRONIX_30LFXG18AC_OTP_LOCKED; > > + ret = nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > > + feature_buf); > > + if (ret) { > > + dev_err(&mtd->dev, > > + "failed to lock OTP (set features): %i\n", ret); > > + nand_deselect_target(nand); > > + return ret; > > + } > > + > > + /* Do dummy page prog with zero address. */ > > + feature_buf[0] = 0; > > + ret = nand_prog_page_op(nand, 0, 0, feature_buf, 1); > > + if (ret) > > + dev_err(&mtd->dev, > > + "failed to lock OTP (page prog): %i\n", ret); > > + > > + ret = macronix_30lfxg18ac_otp_disable(nand); > > + if (ret) > > + dev_err(&mtd->dev, "failed to leave OTP mode after lock\n"); > > + > > + nand_deselect_target(nand); > > + > > + return ret; > > +} > > + > > +static void macronix_nand_setup_otp(struct nand_chip *chip) > > +{ > > + static const char * const supported_otp_models[] = { > > + "MX30LF1G18AC", > > + "MX30LF2G18AC", > > + "MX30LF4G18AC", > > + }; > > + struct mtd_info *mtd; > > + > > + if (match_string(supported_otp_models, > > + ARRAY_SIZE(supported_otp_models), > > + chip->parameters.model) < 0) > > + return; > > + > > + if (!chip->parameters.supports_set_get_features) > > + return; > > + > > + bitmap_set(chip->parameters.get_feature_list, > > + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); > > + bitmap_set(chip->parameters.set_feature_list, > > + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); > > + > > + mtd = nand_to_mtd(chip); > > + mtd->_get_fact_prot_info = macronix_30lfxg18ac_get_otp_info; > > + mtd->_read_fact_prot_reg = macronix_30lfxg18ac_read_otp; > > + mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; > > + mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; > > + mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; > > + mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; > > +} > > + > > static int macronix_nand_init(struct nand_chip *chip) > > { > > if (nand_is_slc(chip)) > > @@ -325,6 +540,7 @@ static int macronix_nand_init(struct nand_chip *chip) > > macronix_nand_onfi_init(chip); > > macronix_nand_block_protection_support(chip); > > macronix_nand_deep_power_down_support(chip); > > + macronix_nand_setup_otp(chip); > > > > return 0; > > } > > > Thanks, > Miquèl Thanks Jaime
Hi liao, jaimeliao.tw@gmail.com wrote on Wed, 14 Jun 2023 17:06:16 +0800: > Hi Miquel > > > > > > Hello, > > > > AVKrasnov@sberdevices.ru wrote on Tue, 23 May 2023 13:16:34 +0300: > > > > > This adds support for OTP area access on MX30LFxG18AC chip series. > > > > Jaime, any feedback on this? Will you test it? > > > > How are we supposed to test the OTP is locked? I see this is still an > > open point. > After checking with internal, sub feature parameter are volatile register. > > It could be change after enter/exit OTP region or power cycle even OTP > > region have been locked. > > OTP operation mode still could be enter/exit and region is read only > after OTP in protect mode. > > #program command could execute but no use after setting OTP region in > protect mode. > > So that we can't check whether OTP region is locked via get feature. > > And we don't have region for checking status of OTP locked. Ah, too bad. But thanks a lot for the explanation. Arseniy, can you please change your comment to explain that the bit is volatile and thus there is no way to check if an otp region is locked? I would return EOPNOTSUPP in this case and verify that the core cleanly handles the situation. Thanks, Miquèl > > > > > > > > > Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru> > > > --- > > > v1 -> v2: > > > * Add slab.h include due to kernel test robot error. > > > v2 -> v3: > > > * Use 'uint64_t' as input argument for 'do_div()' instead > > > of 'unsigned long' due to kernel test robot error. > > > v3 -> v4: > > > * Use 'dev_err()' instead of 'WARN()'. > > > * Call 'match_string()' before checking 'supports_set_get_features' > > > in 'macronix_nand_setup_otp(). > > > * Use 'u8' instead of 'uint8_t' as ./checkpatch.pl wants. > > > > > > drivers/mtd/nand/raw/nand_macronix.c | 216 +++++++++++++++++++++++++++ > > > 1 file changed, 216 insertions(+) > > > > > > diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c > > > index 1472f925f386..be1ffa93bebb 100644 > > > --- a/drivers/mtd/nand/raw/nand_macronix.c > > > +++ b/drivers/mtd/nand/raw/nand_macronix.c > > > @@ -6,6 +6,7 @@ > > > * Author: Boris Brezillon <boris.brezillon@free-electrons.com> > > > */ > > > > > > +#include <linux/slab.h> > > > #include "linux/delay.h" > > > #include "internals.h" > > > > > > @@ -31,6 +32,20 @@ > > > > > > #define MXIC_CMD_POWER_DOWN 0xB9 > > > > > > +#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 > > > +#define MACRONIX_30LFXG18AC_OTP_START_PAGE 0 > > > +#define MACRONIX_30LFXG18AC_OTP_PAGES 30 > > > +#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 > > > +#define MACRONIX_30LFXG18AC_OTP_START_BYTE \ > > > + (MACRONIX_30LFXG18AC_OTP_START_PAGE * \ > > > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) > > > +#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ > > > + (MACRONIX_30LFXG18AC_OTP_PAGES * \ > > > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) > > > + > > > +#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) > > > +#define MACRONIX_30LFXG18AC_OTP_LOCKED BIT(1) > > > + > > > struct nand_onfi_vendor_macronix { > > > u8 reserved; > > > u8 reliability_func; > > > @@ -316,6 +331,206 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip) > > > chip->ops.resume = mxic_nand_resume; > > > } > > > > > > +static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, > > > + size_t *retlen, > > > + struct otp_info *buf) > > > +{ > > > + if (len < sizeof(*buf)) > > > + return -EINVAL; > > > + > > > + /* Don't know how to check that OTP is locked. */ > > > + buf->locked = 0; > > > + buf->start = MACRONIX_30LFXG18AC_OTP_START_BYTE; > > > + buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; > > > + > > > + *retlen = sizeof(*buf); > > > + > > > + return 0; > > > +} > > > + > > > +static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) > > > +{ > > > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > > > + > > > + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; > > > + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > > > + feature_buf); > > > +} > > > + > > > +static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) > > > +{ > > > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > > > + > > > + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > > > + feature_buf); > > > +} > > > + > > > +static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, > > > + loff_t offs_in_flash, > > > + size_t len, size_t *retlen, > > > + u_char *buf, bool write) > > > +{ > > > + struct nand_chip *nand; > > > + size_t bytes_handled; > > > + off_t offs_in_page; > > > + void *dma_buf; > > > + u64 page; > > > + int ret; > > > + > > > + /* 'nand_prog/read_page_op()' may use 'buf' as DMA buffer, > > > + * so allocate properly aligned memory for it. This is > > > + * needed because cross page accesses may lead to unaligned > > > + * buffer address for DMA. > > > + */ > > > + dma_buf = kmalloc(MACRONIX_30LFXG18AC_OTP_PAGE_SIZE, GFP_KERNEL); > > > + if (!dma_buf) > > > + return -ENOMEM; > > > + > > > + nand = mtd_to_nand(mtd); > > > + nand_select_target(nand, 0); > > > + > > > + ret = macronix_30lfxg18ac_otp_enable(nand); > > > + if (ret) > > > + goto out_otp; > > > + > > > + page = offs_in_flash; > > > + /* 'page' will be result of division. */ > > > + offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); > > > + bytes_handled = 0; > > > + > > > + while (bytes_handled < len && > > > + page < MACRONIX_30LFXG18AC_OTP_PAGES) { > > > + size_t bytes_to_handle; > > > + > > > + bytes_to_handle = min_t(size_t, len - bytes_handled, > > > + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - > > > + offs_in_page); > > > + > > > + if (write) { > > > + memcpy(dma_buf, &buf[bytes_handled], bytes_to_handle); > > > + ret = nand_prog_page_op(nand, page, offs_in_page, > > > + dma_buf, bytes_to_handle); > > > + } else { > > > + ret = nand_read_page_op(nand, page, offs_in_page, > > > + dma_buf, bytes_to_handle); > > > + if (!ret) > > > + memcpy(&buf[bytes_handled], dma_buf, > > > + bytes_to_handle); > > > + } > > > + if (ret) > > > + goto out_otp; > > > + > > > + bytes_handled += bytes_to_handle; > > > + offs_in_page = 0; > > > + page++; > > > + } > > > + > > > + *retlen = bytes_handled; > > > + > > > +out_otp: > > > + if (ret) > > > + dev_err(&mtd->dev, "failed to perform OTP IO: %i\n", ret); > > > + > > > + ret = macronix_30lfxg18ac_otp_disable(nand); > > > + if (ret) > > > + dev_err(&mtd->dev, "failed to leave OTP mode after %s\n", > > > + write ? "write" : "read"); > > > + > > > + nand_deselect_target(nand); > > > + kfree(dma_buf); > > > + > > > + return ret; > > > +} > > > + > > > +static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, > > > + size_t len, size_t *rlen, > > > + const u_char *buf) > > > +{ > > > + return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, > > > + true); > > > +} > > > + > > > +static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, > > > + size_t len, size_t *rlen, > > > + u_char *buf) > > > +{ > > > + return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); > > > +} > > > + > > > +static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, > > > + size_t len) > > > +{ > > > + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; > > > + struct nand_chip *nand; > > > + int ret; > > > + > > > + if (from != MACRONIX_30LFXG18AC_OTP_START_BYTE || > > > + len != MACRONIX_30LFXG18AC_OTP_SIZE_BYTES) > > > + return -EINVAL; > > > + > > > + dev_dbg(&mtd->dev, "locking OTP\n"); > > > + > > > + nand = mtd_to_nand(mtd); > > > + nand_select_target(nand, 0); > > > + > > > + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN | > > > + MACRONIX_30LFXG18AC_OTP_LOCKED; > > > + ret = nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, > > > + feature_buf); > > > + if (ret) { > > > + dev_err(&mtd->dev, > > > + "failed to lock OTP (set features): %i\n", ret); > > > + nand_deselect_target(nand); > > > + return ret; > > > + } > > > + > > > + /* Do dummy page prog with zero address. */ > > > + feature_buf[0] = 0; > > > + ret = nand_prog_page_op(nand, 0, 0, feature_buf, 1); > > > + if (ret) > > > + dev_err(&mtd->dev, > > > + "failed to lock OTP (page prog): %i\n", ret); > > > + > > > + ret = macronix_30lfxg18ac_otp_disable(nand); > > > + if (ret) > > > + dev_err(&mtd->dev, "failed to leave OTP mode after lock\n"); > > > + > > > + nand_deselect_target(nand); > > > + > > > + return ret; > > > +} > > > + > > > +static void macronix_nand_setup_otp(struct nand_chip *chip) > > > +{ > > > + static const char * const supported_otp_models[] = { > > > + "MX30LF1G18AC", > > > + "MX30LF2G18AC", > > > + "MX30LF4G18AC", > > > + }; > > > + struct mtd_info *mtd; > > > + > > > + if (match_string(supported_otp_models, > > > + ARRAY_SIZE(supported_otp_models), > > > + chip->parameters.model) < 0) > > > + return; > > > + > > > + if (!chip->parameters.supports_set_get_features) > > > + return; > > > + > > > + bitmap_set(chip->parameters.get_feature_list, > > > + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); > > > + bitmap_set(chip->parameters.set_feature_list, > > > + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); > > > + > > > + mtd = nand_to_mtd(chip); > > > + mtd->_get_fact_prot_info = macronix_30lfxg18ac_get_otp_info; > > > + mtd->_read_fact_prot_reg = macronix_30lfxg18ac_read_otp; > > > + mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; > > > + mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; > > > + mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; > > > + mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; > > > +} > > > + > > > static int macronix_nand_init(struct nand_chip *chip) > > > { > > > if (nand_is_slc(chip)) > > > @@ -325,6 +540,7 @@ static int macronix_nand_init(struct nand_chip *chip) > > > macronix_nand_onfi_init(chip); > > > macronix_nand_block_protection_support(chip); > > > macronix_nand_deep_power_down_support(chip); > > > + macronix_nand_setup_otp(chip); > > > > > > return 0; > > > } > > > > > > Thanks, > > Miquèl > > Thanks > Jaime
Hello Miquel and Jaime! On 14.06.2023 12:10, Miquel Raynal wrote: > Hi liao, > > jaimeliao.tw@gmail.com wrote on Wed, 14 Jun 2023 17:06:16 +0800: > >> Hi Miquel >> >> >>> >>> Hello, >>> >>> AVKrasnov@sberdevices.ru wrote on Tue, 23 May 2023 13:16:34 +0300: >>> >>>> This adds support for OTP area access on MX30LFxG18AC chip series. >>> >>> Jaime, any feedback on this? Will you test it? >>> >>> How are we supposed to test the OTP is locked? I see this is still an >>> open point. >> After checking with internal, sub feature parameter are volatile register. >> >> It could be change after enter/exit OTP region or power cycle even OTP >> >> region have been locked. >> >> OTP operation mode still could be enter/exit and region is read only >> after OTP in protect mode. >> >> #program command could execute but no use after setting OTP region in >> protect mode. >> >> So that we can't check whether OTP region is locked via get feature. >> >> And we don't have region for checking status of OTP locked. > > Ah, too bad. But thanks a lot for the explanation. Arseniy, can you > please change your comment to explain that the bit is volatile and thus > there is no way to check if an otp region is locked? I would return > EOPNOTSUPP in this case and verify that the core cleanly handles the > situation. Ok, thanks for details. @Miquel, ok, I'll change comment from "don't know..." to this explanation. About EOPNOTSUPP, IIUC I think it is not good way to suppress '_get_fact_prot_info' and '_get_user_prot_info' callbacks with this return code as it is the only way to know size of OTP region. Thanks, Arseniy > > Thanks, > Miquèl > >> >>> >>>> >>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru> >>>> --- >>>> v1 -> v2: >>>> * Add slab.h include due to kernel test robot error. >>>> v2 -> v3: >>>> * Use 'uint64_t' as input argument for 'do_div()' instead >>>> of 'unsigned long' due to kernel test robot error. >>>> v3 -> v4: >>>> * Use 'dev_err()' instead of 'WARN()'. >>>> * Call 'match_string()' before checking 'supports_set_get_features' >>>> in 'macronix_nand_setup_otp(). >>>> * Use 'u8' instead of 'uint8_t' as ./checkpatch.pl wants. >>>> >>>> drivers/mtd/nand/raw/nand_macronix.c | 216 +++++++++++++++++++++++++++ >>>> 1 file changed, 216 insertions(+) >>>> >>>> diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c >>>> index 1472f925f386..be1ffa93bebb 100644 >>>> --- a/drivers/mtd/nand/raw/nand_macronix.c >>>> +++ b/drivers/mtd/nand/raw/nand_macronix.c >>>> @@ -6,6 +6,7 @@ >>>> * Author: Boris Brezillon <boris.brezillon@free-electrons.com> >>>> */ >>>> >>>> +#include <linux/slab.h> >>>> #include "linux/delay.h" >>>> #include "internals.h" >>>> >>>> @@ -31,6 +32,20 @@ >>>> >>>> #define MXIC_CMD_POWER_DOWN 0xB9 >>>> >>>> +#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 >>>> +#define MACRONIX_30LFXG18AC_OTP_START_PAGE 0 >>>> +#define MACRONIX_30LFXG18AC_OTP_PAGES 30 >>>> +#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 >>>> +#define MACRONIX_30LFXG18AC_OTP_START_BYTE \ >>>> + (MACRONIX_30LFXG18AC_OTP_START_PAGE * \ >>>> + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) >>>> +#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ >>>> + (MACRONIX_30LFXG18AC_OTP_PAGES * \ >>>> + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) >>>> + >>>> +#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) >>>> +#define MACRONIX_30LFXG18AC_OTP_LOCKED BIT(1) >>>> + >>>> struct nand_onfi_vendor_macronix { >>>> u8 reserved; >>>> u8 reliability_func; >>>> @@ -316,6 +331,206 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip) >>>> chip->ops.resume = mxic_nand_resume; >>>> } >>>> >>>> +static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, >>>> + size_t *retlen, >>>> + struct otp_info *buf) >>>> +{ >>>> + if (len < sizeof(*buf)) >>>> + return -EINVAL; >>>> + >>>> + /* Don't know how to check that OTP is locked. */ >>>> + buf->locked = 0; >>>> + buf->start = MACRONIX_30LFXG18AC_OTP_START_BYTE; >>>> + buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; >>>> + >>>> + *retlen = sizeof(*buf); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) >>>> +{ >>>> + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; >>>> + >>>> + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; >>>> + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, >>>> + feature_buf); >>>> +} >>>> + >>>> +static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) >>>> +{ >>>> + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; >>>> + >>>> + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, >>>> + feature_buf); >>>> +} >>>> + >>>> +static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, >>>> + loff_t offs_in_flash, >>>> + size_t len, size_t *retlen, >>>> + u_char *buf, bool write) >>>> +{ >>>> + struct nand_chip *nand; >>>> + size_t bytes_handled; >>>> + off_t offs_in_page; >>>> + void *dma_buf; >>>> + u64 page; >>>> + int ret; >>>> + >>>> + /* 'nand_prog/read_page_op()' may use 'buf' as DMA buffer, >>>> + * so allocate properly aligned memory for it. This is >>>> + * needed because cross page accesses may lead to unaligned >>>> + * buffer address for DMA. >>>> + */ >>>> + dma_buf = kmalloc(MACRONIX_30LFXG18AC_OTP_PAGE_SIZE, GFP_KERNEL); >>>> + if (!dma_buf) >>>> + return -ENOMEM; >>>> + >>>> + nand = mtd_to_nand(mtd); >>>> + nand_select_target(nand, 0); >>>> + >>>> + ret = macronix_30lfxg18ac_otp_enable(nand); >>>> + if (ret) >>>> + goto out_otp; >>>> + >>>> + page = offs_in_flash; >>>> + /* 'page' will be result of division. */ >>>> + offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); >>>> + bytes_handled = 0; >>>> + >>>> + while (bytes_handled < len && >>>> + page < MACRONIX_30LFXG18AC_OTP_PAGES) { >>>> + size_t bytes_to_handle; >>>> + >>>> + bytes_to_handle = min_t(size_t, len - bytes_handled, >>>> + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - >>>> + offs_in_page); >>>> + >>>> + if (write) { >>>> + memcpy(dma_buf, &buf[bytes_handled], bytes_to_handle); >>>> + ret = nand_prog_page_op(nand, page, offs_in_page, >>>> + dma_buf, bytes_to_handle); >>>> + } else { >>>> + ret = nand_read_page_op(nand, page, offs_in_page, >>>> + dma_buf, bytes_to_handle); >>>> + if (!ret) >>>> + memcpy(&buf[bytes_handled], dma_buf, >>>> + bytes_to_handle); >>>> + } >>>> + if (ret) >>>> + goto out_otp; >>>> + >>>> + bytes_handled += bytes_to_handle; >>>> + offs_in_page = 0; >>>> + page++; >>>> + } >>>> + >>>> + *retlen = bytes_handled; >>>> + >>>> +out_otp: >>>> + if (ret) >>>> + dev_err(&mtd->dev, "failed to perform OTP IO: %i\n", ret); >>>> + >>>> + ret = macronix_30lfxg18ac_otp_disable(nand); >>>> + if (ret) >>>> + dev_err(&mtd->dev, "failed to leave OTP mode after %s\n", >>>> + write ? "write" : "read"); >>>> + >>>> + nand_deselect_target(nand); >>>> + kfree(dma_buf); >>>> + >>>> + return ret; >>>> +} >>>> + >>>> +static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, >>>> + size_t len, size_t *rlen, >>>> + const u_char *buf) >>>> +{ >>>> + return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, >>>> + true); >>>> +} >>>> + >>>> +static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, >>>> + size_t len, size_t *rlen, >>>> + u_char *buf) >>>> +{ >>>> + return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); >>>> +} >>>> + >>>> +static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, >>>> + size_t len) >>>> +{ >>>> + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; >>>> + struct nand_chip *nand; >>>> + int ret; >>>> + >>>> + if (from != MACRONIX_30LFXG18AC_OTP_START_BYTE || >>>> + len != MACRONIX_30LFXG18AC_OTP_SIZE_BYTES) >>>> + return -EINVAL; >>>> + >>>> + dev_dbg(&mtd->dev, "locking OTP\n"); >>>> + >>>> + nand = mtd_to_nand(mtd); >>>> + nand_select_target(nand, 0); >>>> + >>>> + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN | >>>> + MACRONIX_30LFXG18AC_OTP_LOCKED; >>>> + ret = nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, >>>> + feature_buf); >>>> + if (ret) { >>>> + dev_err(&mtd->dev, >>>> + "failed to lock OTP (set features): %i\n", ret); >>>> + nand_deselect_target(nand); >>>> + return ret; >>>> + } >>>> + >>>> + /* Do dummy page prog with zero address. */ >>>> + feature_buf[0] = 0; >>>> + ret = nand_prog_page_op(nand, 0, 0, feature_buf, 1); >>>> + if (ret) >>>> + dev_err(&mtd->dev, >>>> + "failed to lock OTP (page prog): %i\n", ret); >>>> + >>>> + ret = macronix_30lfxg18ac_otp_disable(nand); >>>> + if (ret) >>>> + dev_err(&mtd->dev, "failed to leave OTP mode after lock\n"); >>>> + >>>> + nand_deselect_target(nand); >>>> + >>>> + return ret; >>>> +} >>>> + >>>> +static void macronix_nand_setup_otp(struct nand_chip *chip) >>>> +{ >>>> + static const char * const supported_otp_models[] = { >>>> + "MX30LF1G18AC", >>>> + "MX30LF2G18AC", >>>> + "MX30LF4G18AC", >>>> + }; >>>> + struct mtd_info *mtd; >>>> + >>>> + if (match_string(supported_otp_models, >>>> + ARRAY_SIZE(supported_otp_models), >>>> + chip->parameters.model) < 0) >>>> + return; >>>> + >>>> + if (!chip->parameters.supports_set_get_features) >>>> + return; >>>> + >>>> + bitmap_set(chip->parameters.get_feature_list, >>>> + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); >>>> + bitmap_set(chip->parameters.set_feature_list, >>>> + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); >>>> + >>>> + mtd = nand_to_mtd(chip); >>>> + mtd->_get_fact_prot_info = macronix_30lfxg18ac_get_otp_info; >>>> + mtd->_read_fact_prot_reg = macronix_30lfxg18ac_read_otp; >>>> + mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; >>>> + mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; >>>> + mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; >>>> + mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; >>>> +} >>>> + >>>> static int macronix_nand_init(struct nand_chip *chip) >>>> { >>>> if (nand_is_slc(chip)) >>>> @@ -325,6 +540,7 @@ static int macronix_nand_init(struct nand_chip *chip) >>>> macronix_nand_onfi_init(chip); >>>> macronix_nand_block_protection_support(chip); >>>> macronix_nand_deep_power_down_support(chip); >>>> + macronix_nand_setup_otp(chip); >>>> >>>> return 0; >>>> } >>> >>> >>> Thanks, >>> Miquèl >> >> Thanks >> Jaime > >
On 14.06.2023 12:22, Arseniy Krasnov wrote: > Hello Miquel and Jaime! > > On 14.06.2023 12:10, Miquel Raynal wrote: >> Hi liao, >> >> jaimeliao.tw@gmail.com wrote on Wed, 14 Jun 2023 17:06:16 +0800: >> >>> Hi Miquel >>> >>> >>>> >>>> Hello, >>>> >>>> AVKrasnov@sberdevices.ru wrote on Tue, 23 May 2023 13:16:34 +0300: >>>> >>>>> This adds support for OTP area access on MX30LFxG18AC chip series. >>>> >>>> Jaime, any feedback on this? Will you test it? >>>> >>>> How are we supposed to test the OTP is locked? I see this is still an >>>> open point. >>> After checking with internal, sub feature parameter are volatile register. >>> >>> It could be change after enter/exit OTP region or power cycle even OTP >>> >>> region have been locked. >>> >>> OTP operation mode still could be enter/exit and region is read only >>> after OTP in protect mode. >>> >>> #program command could execute but no use after setting OTP region in >>> protect mode. >>> >>> So that we can't check whether OTP region is locked via get feature. >>> >>> And we don't have region for checking status of OTP locked. >> >> Ah, too bad. But thanks a lot for the explanation. Arseniy, can you >> please change your comment to explain that the bit is volatile and thus >> there is no way to check if an otp region is locked? I would return >> EOPNOTSUPP in this case and verify that the core cleanly handles the >> situation. > > Ok, thanks for details. @Miquel, ok, I'll change comment from "don't know..." > to this explanation. About EOPNOTSUPP, IIUC I think it is not good way to > suppress '_get_fact_prot_info' and '_get_user_prot_info' callbacks with this > return code as it is the only way to know size of OTP region. > > Thanks, Arseniy Ahh sorry, there is '_lock_user_prot_reg', it will return EOPNOTSUPP. Now I see:) Thanks, Arseniy > >> >> Thanks, >> Miquèl >> >>> >>>> >>>>> >>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru> >>>>> --- >>>>> v1 -> v2: >>>>> * Add slab.h include due to kernel test robot error. >>>>> v2 -> v3: >>>>> * Use 'uint64_t' as input argument for 'do_div()' instead >>>>> of 'unsigned long' due to kernel test robot error. >>>>> v3 -> v4: >>>>> * Use 'dev_err()' instead of 'WARN()'. >>>>> * Call 'match_string()' before checking 'supports_set_get_features' >>>>> in 'macronix_nand_setup_otp(). >>>>> * Use 'u8' instead of 'uint8_t' as ./checkpatch.pl wants. >>>>> >>>>> drivers/mtd/nand/raw/nand_macronix.c | 216 +++++++++++++++++++++++++++ >>>>> 1 file changed, 216 insertions(+) >>>>> >>>>> diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c >>>>> index 1472f925f386..be1ffa93bebb 100644 >>>>> --- a/drivers/mtd/nand/raw/nand_macronix.c >>>>> +++ b/drivers/mtd/nand/raw/nand_macronix.c >>>>> @@ -6,6 +6,7 @@ >>>>> * Author: Boris Brezillon <boris.brezillon@free-electrons.com> >>>>> */ >>>>> >>>>> +#include <linux/slab.h> >>>>> #include "linux/delay.h" >>>>> #include "internals.h" >>>>> >>>>> @@ -31,6 +32,20 @@ >>>>> >>>>> #define MXIC_CMD_POWER_DOWN 0xB9 >>>>> >>>>> +#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 >>>>> +#define MACRONIX_30LFXG18AC_OTP_START_PAGE 0 >>>>> +#define MACRONIX_30LFXG18AC_OTP_PAGES 30 >>>>> +#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 >>>>> +#define MACRONIX_30LFXG18AC_OTP_START_BYTE \ >>>>> + (MACRONIX_30LFXG18AC_OTP_START_PAGE * \ >>>>> + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) >>>>> +#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ >>>>> + (MACRONIX_30LFXG18AC_OTP_PAGES * \ >>>>> + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) >>>>> + >>>>> +#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) >>>>> +#define MACRONIX_30LFXG18AC_OTP_LOCKED BIT(1) >>>>> + >>>>> struct nand_onfi_vendor_macronix { >>>>> u8 reserved; >>>>> u8 reliability_func; >>>>> @@ -316,6 +331,206 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip) >>>>> chip->ops.resume = mxic_nand_resume; >>>>> } >>>>> >>>>> +static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, >>>>> + size_t *retlen, >>>>> + struct otp_info *buf) >>>>> +{ >>>>> + if (len < sizeof(*buf)) >>>>> + return -EINVAL; >>>>> + >>>>> + /* Don't know how to check that OTP is locked. */ >>>>> + buf->locked = 0; >>>>> + buf->start = MACRONIX_30LFXG18AC_OTP_START_BYTE; >>>>> + buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; >>>>> + >>>>> + *retlen = sizeof(*buf); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) >>>>> +{ >>>>> + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; >>>>> + >>>>> + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; >>>>> + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, >>>>> + feature_buf); >>>>> +} >>>>> + >>>>> +static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) >>>>> +{ >>>>> + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; >>>>> + >>>>> + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, >>>>> + feature_buf); >>>>> +} >>>>> + >>>>> +static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, >>>>> + loff_t offs_in_flash, >>>>> + size_t len, size_t *retlen, >>>>> + u_char *buf, bool write) >>>>> +{ >>>>> + struct nand_chip *nand; >>>>> + size_t bytes_handled; >>>>> + off_t offs_in_page; >>>>> + void *dma_buf; >>>>> + u64 page; >>>>> + int ret; >>>>> + >>>>> + /* 'nand_prog/read_page_op()' may use 'buf' as DMA buffer, >>>>> + * so allocate properly aligned memory for it. This is >>>>> + * needed because cross page accesses may lead to unaligned >>>>> + * buffer address for DMA. >>>>> + */ >>>>> + dma_buf = kmalloc(MACRONIX_30LFXG18AC_OTP_PAGE_SIZE, GFP_KERNEL); >>>>> + if (!dma_buf) >>>>> + return -ENOMEM; >>>>> + >>>>> + nand = mtd_to_nand(mtd); >>>>> + nand_select_target(nand, 0); >>>>> + >>>>> + ret = macronix_30lfxg18ac_otp_enable(nand); >>>>> + if (ret) >>>>> + goto out_otp; >>>>> + >>>>> + page = offs_in_flash; >>>>> + /* 'page' will be result of division. */ >>>>> + offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); >>>>> + bytes_handled = 0; >>>>> + >>>>> + while (bytes_handled < len && >>>>> + page < MACRONIX_30LFXG18AC_OTP_PAGES) { >>>>> + size_t bytes_to_handle; >>>>> + >>>>> + bytes_to_handle = min_t(size_t, len - bytes_handled, >>>>> + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - >>>>> + offs_in_page); >>>>> + >>>>> + if (write) { >>>>> + memcpy(dma_buf, &buf[bytes_handled], bytes_to_handle); >>>>> + ret = nand_prog_page_op(nand, page, offs_in_page, >>>>> + dma_buf, bytes_to_handle); >>>>> + } else { >>>>> + ret = nand_read_page_op(nand, page, offs_in_page, >>>>> + dma_buf, bytes_to_handle); >>>>> + if (!ret) >>>>> + memcpy(&buf[bytes_handled], dma_buf, >>>>> + bytes_to_handle); >>>>> + } >>>>> + if (ret) >>>>> + goto out_otp; >>>>> + >>>>> + bytes_handled += bytes_to_handle; >>>>> + offs_in_page = 0; >>>>> + page++; >>>>> + } >>>>> + >>>>> + *retlen = bytes_handled; >>>>> + >>>>> +out_otp: >>>>> + if (ret) >>>>> + dev_err(&mtd->dev, "failed to perform OTP IO: %i\n", ret); >>>>> + >>>>> + ret = macronix_30lfxg18ac_otp_disable(nand); >>>>> + if (ret) >>>>> + dev_err(&mtd->dev, "failed to leave OTP mode after %s\n", >>>>> + write ? "write" : "read"); >>>>> + >>>>> + nand_deselect_target(nand); >>>>> + kfree(dma_buf); >>>>> + >>>>> + return ret; >>>>> +} >>>>> + >>>>> +static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, >>>>> + size_t len, size_t *rlen, >>>>> + const u_char *buf) >>>>> +{ >>>>> + return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, >>>>> + true); >>>>> +} >>>>> + >>>>> +static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, >>>>> + size_t len, size_t *rlen, >>>>> + u_char *buf) >>>>> +{ >>>>> + return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); >>>>> +} >>>>> + >>>>> +static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, >>>>> + size_t len) >>>>> +{ >>>>> + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; >>>>> + struct nand_chip *nand; >>>>> + int ret; >>>>> + >>>>> + if (from != MACRONIX_30LFXG18AC_OTP_START_BYTE || >>>>> + len != MACRONIX_30LFXG18AC_OTP_SIZE_BYTES) >>>>> + return -EINVAL; >>>>> + >>>>> + dev_dbg(&mtd->dev, "locking OTP\n"); >>>>> + >>>>> + nand = mtd_to_nand(mtd); >>>>> + nand_select_target(nand, 0); >>>>> + >>>>> + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN | >>>>> + MACRONIX_30LFXG18AC_OTP_LOCKED; >>>>> + ret = nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, >>>>> + feature_buf); >>>>> + if (ret) { >>>>> + dev_err(&mtd->dev, >>>>> + "failed to lock OTP (set features): %i\n", ret); >>>>> + nand_deselect_target(nand); >>>>> + return ret; >>>>> + } >>>>> + >>>>> + /* Do dummy page prog with zero address. */ >>>>> + feature_buf[0] = 0; >>>>> + ret = nand_prog_page_op(nand, 0, 0, feature_buf, 1); >>>>> + if (ret) >>>>> + dev_err(&mtd->dev, >>>>> + "failed to lock OTP (page prog): %i\n", ret); >>>>> + >>>>> + ret = macronix_30lfxg18ac_otp_disable(nand); >>>>> + if (ret) >>>>> + dev_err(&mtd->dev, "failed to leave OTP mode after lock\n"); >>>>> + >>>>> + nand_deselect_target(nand); >>>>> + >>>>> + return ret; >>>>> +} >>>>> + >>>>> +static void macronix_nand_setup_otp(struct nand_chip *chip) >>>>> +{ >>>>> + static const char * const supported_otp_models[] = { >>>>> + "MX30LF1G18AC", >>>>> + "MX30LF2G18AC", >>>>> + "MX30LF4G18AC", >>>>> + }; >>>>> + struct mtd_info *mtd; >>>>> + >>>>> + if (match_string(supported_otp_models, >>>>> + ARRAY_SIZE(supported_otp_models), >>>>> + chip->parameters.model) < 0) >>>>> + return; >>>>> + >>>>> + if (!chip->parameters.supports_set_get_features) >>>>> + return; >>>>> + >>>>> + bitmap_set(chip->parameters.get_feature_list, >>>>> + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); >>>>> + bitmap_set(chip->parameters.set_feature_list, >>>>> + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); >>>>> + >>>>> + mtd = nand_to_mtd(chip); >>>>> + mtd->_get_fact_prot_info = macronix_30lfxg18ac_get_otp_info; >>>>> + mtd->_read_fact_prot_reg = macronix_30lfxg18ac_read_otp; >>>>> + mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; >>>>> + mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; >>>>> + mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; >>>>> + mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; >>>>> +} >>>>> + >>>>> static int macronix_nand_init(struct nand_chip *chip) >>>>> { >>>>> if (nand_is_slc(chip)) >>>>> @@ -325,6 +540,7 @@ static int macronix_nand_init(struct nand_chip *chip) >>>>> macronix_nand_onfi_init(chip); >>>>> macronix_nand_block_protection_support(chip); >>>>> macronix_nand_deep_power_down_support(chip); >>>>> + macronix_nand_setup_otp(chip); >>>>> >>>>> return 0; >>>>> } >>>> >>>> >>>> Thanks, >>>> Miquèl >>> >>> Thanks >>> Jaime >> >>
diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 1472f925f386..be1ffa93bebb 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -6,6 +6,7 @@ * Author: Boris Brezillon <boris.brezillon@free-electrons.com> */ +#include <linux/slab.h> #include "linux/delay.h" #include "internals.h" @@ -31,6 +32,20 @@ #define MXIC_CMD_POWER_DOWN 0xB9 +#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 +#define MACRONIX_30LFXG18AC_OTP_START_PAGE 0 +#define MACRONIX_30LFXG18AC_OTP_PAGES 30 +#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 +#define MACRONIX_30LFXG18AC_OTP_START_BYTE \ + (MACRONIX_30LFXG18AC_OTP_START_PAGE * \ + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) +#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ + (MACRONIX_30LFXG18AC_OTP_PAGES * \ + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) + +#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) +#define MACRONIX_30LFXG18AC_OTP_LOCKED BIT(1) + struct nand_onfi_vendor_macronix { u8 reserved; u8 reliability_func; @@ -316,6 +331,206 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip) chip->ops.resume = mxic_nand_resume; } +static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, + struct otp_info *buf) +{ + if (len < sizeof(*buf)) + return -EINVAL; + + /* Don't know how to check that OTP is locked. */ + buf->locked = 0; + buf->start = MACRONIX_30LFXG18AC_OTP_START_BYTE; + buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; + + *retlen = sizeof(*buf); + + return 0; +} + +static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) +{ + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; + + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, + feature_buf); +} + +static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) +{ + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; + + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, + feature_buf); +} + +static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, + loff_t offs_in_flash, + size_t len, size_t *retlen, + u_char *buf, bool write) +{ + struct nand_chip *nand; + size_t bytes_handled; + off_t offs_in_page; + void *dma_buf; + u64 page; + int ret; + + /* 'nand_prog/read_page_op()' may use 'buf' as DMA buffer, + * so allocate properly aligned memory for it. This is + * needed because cross page accesses may lead to unaligned + * buffer address for DMA. + */ + dma_buf = kmalloc(MACRONIX_30LFXG18AC_OTP_PAGE_SIZE, GFP_KERNEL); + if (!dma_buf) + return -ENOMEM; + + nand = mtd_to_nand(mtd); + nand_select_target(nand, 0); + + ret = macronix_30lfxg18ac_otp_enable(nand); + if (ret) + goto out_otp; + + page = offs_in_flash; + /* 'page' will be result of division. */ + offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); + bytes_handled = 0; + + while (bytes_handled < len && + page < MACRONIX_30LFXG18AC_OTP_PAGES) { + size_t bytes_to_handle; + + bytes_to_handle = min_t(size_t, len - bytes_handled, + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - + offs_in_page); + + if (write) { + memcpy(dma_buf, &buf[bytes_handled], bytes_to_handle); + ret = nand_prog_page_op(nand, page, offs_in_page, + dma_buf, bytes_to_handle); + } else { + ret = nand_read_page_op(nand, page, offs_in_page, + dma_buf, bytes_to_handle); + if (!ret) + memcpy(&buf[bytes_handled], dma_buf, + bytes_to_handle); + } + if (ret) + goto out_otp; + + bytes_handled += bytes_to_handle; + offs_in_page = 0; + page++; + } + + *retlen = bytes_handled; + +out_otp: + if (ret) + dev_err(&mtd->dev, "failed to perform OTP IO: %i\n", ret); + + ret = macronix_30lfxg18ac_otp_disable(nand); + if (ret) + dev_err(&mtd->dev, "failed to leave OTP mode after %s\n", + write ? "write" : "read"); + + nand_deselect_target(nand); + kfree(dma_buf); + + return ret; +} + +static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, + size_t len, size_t *rlen, + const u_char *buf) +{ + return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, + true); +} + +static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, + size_t len, size_t *rlen, + u_char *buf) +{ + return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); +} + +static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, + size_t len) +{ + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; + struct nand_chip *nand; + int ret; + + if (from != MACRONIX_30LFXG18AC_OTP_START_BYTE || + len != MACRONIX_30LFXG18AC_OTP_SIZE_BYTES) + return -EINVAL; + + dev_dbg(&mtd->dev, "locking OTP\n"); + + nand = mtd_to_nand(mtd); + nand_select_target(nand, 0); + + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN | + MACRONIX_30LFXG18AC_OTP_LOCKED; + ret = nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, + feature_buf); + if (ret) { + dev_err(&mtd->dev, + "failed to lock OTP (set features): %i\n", ret); + nand_deselect_target(nand); + return ret; + } + + /* Do dummy page prog with zero address. */ + feature_buf[0] = 0; + ret = nand_prog_page_op(nand, 0, 0, feature_buf, 1); + if (ret) + dev_err(&mtd->dev, + "failed to lock OTP (page prog): %i\n", ret); + + ret = macronix_30lfxg18ac_otp_disable(nand); + if (ret) + dev_err(&mtd->dev, "failed to leave OTP mode after lock\n"); + + nand_deselect_target(nand); + + return ret; +} + +static void macronix_nand_setup_otp(struct nand_chip *chip) +{ + static const char * const supported_otp_models[] = { + "MX30LF1G18AC", + "MX30LF2G18AC", + "MX30LF4G18AC", + }; + struct mtd_info *mtd; + + if (match_string(supported_otp_models, + ARRAY_SIZE(supported_otp_models), + chip->parameters.model) < 0) + return; + + if (!chip->parameters.supports_set_get_features) + return; + + bitmap_set(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); + bitmap_set(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); + + mtd = nand_to_mtd(chip); + mtd->_get_fact_prot_info = macronix_30lfxg18ac_get_otp_info; + mtd->_read_fact_prot_reg = macronix_30lfxg18ac_read_otp; + mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; + mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; + mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; + mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; +} + static int macronix_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -325,6 +540,7 @@ static int macronix_nand_init(struct nand_chip *chip) macronix_nand_onfi_init(chip); macronix_nand_block_protection_support(chip); macronix_nand_deep_power_down_support(chip); + macronix_nand_setup_otp(chip); return 0; }