summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/raw/nand_macronix.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-06-29 00:02:03 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2023-06-29 00:02:03 +0300
commit1364b4068a421d99fb4da8b570e54525096b1cef (patch)
tree1d4aa6f1fc50637e2e9365e93bec226a320abbd4 /drivers/mtd/nand/raw/nand_macronix.c
parent84fccbba93103b22044617e419ba20e1403b4a65 (diff)
parentcf431a5998326a0468532a188a188ac2c8e9c55d (diff)
downloadlinux-1364b4068a421d99fb4da8b570e54525096b1cef.tar.xz
Merge tag 'mtd/for-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull mtd updates from "Core MTD changes: - otp: - Put factory OTP/NVRAM into the entropy pool - Clean up on error in mtd_otp_nvmem_add() MTD devices changes: - sm_ftl: Fix typos in comments - Use SPDX license headers - pismo: Switch back to use i2c_driver's .probe() - mtdpart: Drop useless LIST_HEAD - st_spi_fsm: Use the devm_clk_get_enabled() helper function DT binding changes: - partitions: - Include TP-Link SafeLoader in allowed list - Add missing type for "linux,rootfs" - Extend the nand node names filter - Create a file for raw NAND chip properties - Mark nand-ecc-placement deprecated - Describe nand-ecc-mode - Prevent NAND chip unevaluated properties in all NAND bindings with a NAND chip reference. - Qcom: Fix a property position - Marvell: Convert to YAML DT schema Raw NAND chip drivers changes: - Macronix: OTP access for MX30LFxG18AC - Add basic Sandisk manufacturer ops - Add support for Sandisk SDTNQGAMA Raw NAND controller driver changes: - Meson: - Replace integer consts with proper defines - Allow waiting w/o wired ready/busy pin - Check buffer length validity - Fix unaligned DMA buffers handling - dt-bindings: Fix 'nand-rb' property - Arasan: Revert "mtd: rawnand: arasan: Prevent an unsupported configuration" as this limitation is no longer true thanks to the recent efforts in improving the clocks support in this driver SPI-NAND changes: - Gigadevice: add support for GD5F2GQ5xExxH - Macronix: Add support for serial NAND flashes" * tag 'mtd/for-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (38 commits) dt-bindings: mtd: marvell-nand: Convert to YAML DT scheme dt-bindings: mtd: ti,am654: Prevent unevaluated properties dt-bindings: mtd: mediatek: Prevent NAND chip unevaluated properties dt-bindings: mtd: mediatek: Reference raw-nand-chip.yaml dt-bindings: mtd: stm32: Prevent NAND chip unevaluated properties dt-bindings: mtd: rockchip: Prevent NAND chip unevaluated properties dt-bindings: mtd: intel: Prevent NAND chip unevaluated properties dt-bindings: mtd: denali: Prevent NAND chip unevaluated properties dt-bindings: mtd: brcmnand: Prevent NAND chip unevaluated properties dt-bindings: mtd: meson: Prevent NAND chip unevaluated properties dt-bindings: mtd: sunxi: Prevent NAND chip unevaluated properties dt-bindings: mtd: ingenic: Prevent NAND chip unevaluated properties dt-bindings: mtd: qcom: Prevent NAND chip unevaluated properties dt-bindings: mtd: qcom: Fix a property position dt-bindings: mtd: Describe nand-ecc-mode dt-bindings: mtd: Mark nand-ecc-placement deprecated dt-bindings: mtd: Create a file for raw NAND chip properties dt-bindings: mtd: Accept nand related node names mtd: sm_ftl: Fix typos in comments mtd: otp: clean up on error in mtd_otp_nvmem_add() ...
Diffstat (limited to 'drivers/mtd/nand/raw/nand_macronix.c')
-rw-r--r--drivers/mtd/nand/raw/nand_macronix.c167
1 files changed, 167 insertions, 0 deletions
diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c
index 385957eb6762..e229de32ff50 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,16 @@
#define MXIC_CMD_POWER_DOWN 0xB9
+#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90
+#define MACRONIX_30LFXG18AC_OTP_START_PAGE 2
+#define MACRONIX_30LFXG18AC_OTP_PAGES 30
+#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112
+#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \
+ (MACRONIX_30LFXG18AC_OTP_PAGES * \
+ MACRONIX_30LFXG18AC_OTP_PAGE_SIZE)
+
+#define MACRONIX_30LFXG18AC_OTP_EN BIT(0)
+
struct nand_onfi_vendor_macronix {
u8 reserved;
u8 reliability_func;
@@ -315,6 +326,161 @@ 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;
+
+ /* Always report that OTP is unlocked. Reason is that this
+ * type of flash chip doesn't provide way to check that OTP
+ * is locked or not: subfeature parameter is implemented as
+ * volatile register. Technically OTP region could be locked
+ * and become readonly, but as there is no way to check it,
+ * don't allow to lock it ('_lock_user_prot_reg' callback
+ * always returns -EOPNOTSUPP) and thus we report that OTP
+ * is unlocked.
+ */
+ buf->locked = 0;
+ buf->start = 0;
+ 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;
+ u64 page;
+ int ret;
+
+ 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;
+ u64 phys_page = page + MACRONIX_30LFXG18AC_OTP_START_PAGE;
+
+ bytes_to_handle = min_t(size_t, len - bytes_handled,
+ MACRONIX_30LFXG18AC_OTP_PAGE_SIZE -
+ offs_in_page);
+
+ if (write)
+ ret = nand_prog_page_op(nand, phys_page, offs_in_page,
+ &buf[bytes_handled], bytes_to_handle);
+ else
+ ret = nand_read_page_op(nand, phys_page, offs_in_page,
+ &buf[bytes_handled], 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);
+
+ 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)
+{
+ /* See comment in 'macronix_30lfxg18ac_get_otp_info()'. */
+ return -EOPNOTSUPP;
+}
+
+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_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))
@@ -324,6 +490,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;
}