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>, "Christian König" <christian.koenig@amd.com>, "Arseniy Krasnov" <AVKrasnov@sberdevices.ru> Cc: <oxffffaa@gmail.com>, <kernel@sberdevices.ru>, <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 v2] mtd: rawnand: macronix: OTP access for MX30LFxG18AC Date: Wed, 26 Apr 2023 20:25:14 +0300 [thread overview] Message-ID: <20230426172520.2004711-1-AVKrasnov@sberdevices.ru> (raw) This adds support for OTP area access on MX30LFxG18AC chip series. Changelog: v1 -> v2: * Add slab.h include due to kernel test robot error. Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru> --- drivers/mtd/nand/raw/nand_macronix.c | 213 +++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 1472f925f386..5232cf48c350 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,203 @@ 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) +{ + uint8_t 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) +{ + uint8_t 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; + unsigned long page; + off_t offs_in_page; + void *dma_buf; + 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); + WARN(ret, "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) +{ + uint8_t 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); + WARN(ret, "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 (!chip->parameters.supports_set_get_features) + return; + + if (match_string(supported_otp_models, + ARRAY_SIZE(supported_otp_models), + chip->parameters.model) < 0) + 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 +537,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; } -- 2.35.0
WARNING: multiple messages have this Message-ID (diff)
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>, "Christian König" <christian.koenig@amd.com>, "Arseniy Krasnov" <AVKrasnov@sberdevices.ru> Cc: <oxffffaa@gmail.com>, <kernel@sberdevices.ru>, <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 v2] mtd: rawnand: macronix: OTP access for MX30LFxG18AC Date: Wed, 26 Apr 2023 20:25:14 +0300 [thread overview] Message-ID: <20230426172520.2004711-1-AVKrasnov@sberdevices.ru> (raw) This adds support for OTP area access on MX30LFxG18AC chip series. Changelog: v1 -> v2: * Add slab.h include due to kernel test robot error. Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru> --- drivers/mtd/nand/raw/nand_macronix.c | 213 +++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 1472f925f386..5232cf48c350 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,203 @@ 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) +{ + uint8_t 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) +{ + uint8_t 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; + unsigned long page; + off_t offs_in_page; + void *dma_buf; + 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); + WARN(ret, "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) +{ + uint8_t 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); + WARN(ret, "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 (!chip->parameters.supports_set_get_features) + return; + + if (match_string(supported_otp_models, + ARRAY_SIZE(supported_otp_models), + chip->parameters.model) < 0) + 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 +537,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; } -- 2.35.0 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/
next reply other threads:[~2023-04-26 17:30 UTC|newest] Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top 2023-04-26 17:25 Arseniy Krasnov [this message] 2023-04-26 17:25 ` [PATCH v2] mtd: rawnand: macronix: OTP access for MX30LFxG18AC Arseniy Krasnov 2023-04-30 16:49 ` kernel test robot 2023-04-30 16:49 ` kernel test robot 2023-04-30 16:49 ` kernel test robot
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20230426172520.2004711-1-AVKrasnov@sberdevices.ru \ --to=avkrasnov@sberdevices.ru \ --cc=christian.koenig@amd.com \ --cc=dri-devel@lists.freedesktop.org \ --cc=kernel@sberdevices.ru \ --cc=linaro-mm-sig@lists.linaro.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-media@vger.kernel.org \ --cc=linux-mtd@lists.infradead.org \ --cc=miquel.raynal@bootlin.com \ --cc=oxffffaa@gmail.com \ --cc=richard@nod.at \ --cc=sumit.semwal@linaro.org \ --cc=vigneshr@ti.com \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.