162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Free Electrons 462306a36Sopenharmony_ci * Copyright (C) 2017 NextThing Co 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@free-electrons.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include "linux/delay.h" 1162306a36Sopenharmony_ci#include "internals.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define MACRONIX_READ_RETRY_BIT BIT(0) 1462306a36Sopenharmony_ci#define MACRONIX_NUM_READ_RETRY_MODES 6 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define ONFI_FEATURE_ADDR_MXIC_PROTECTION 0xA0 1762306a36Sopenharmony_ci#define MXIC_BLOCK_PROTECTION_ALL_LOCK 0x38 1862306a36Sopenharmony_ci#define MXIC_BLOCK_PROTECTION_ALL_UNLOCK 0x0 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define ONFI_FEATURE_ADDR_MXIC_RANDOMIZER 0xB0 2162306a36Sopenharmony_ci#define MACRONIX_RANDOMIZER_BIT BIT(1) 2262306a36Sopenharmony_ci#define MACRONIX_RANDOMIZER_ENPGM BIT(0) 2362306a36Sopenharmony_ci#define MACRONIX_RANDOMIZER_RANDEN BIT(1) 2462306a36Sopenharmony_ci#define MACRONIX_RANDOMIZER_RANDOPT BIT(2) 2562306a36Sopenharmony_ci#define MACRONIX_RANDOMIZER_MODE_ENTER \ 2662306a36Sopenharmony_ci (MACRONIX_RANDOMIZER_ENPGM | \ 2762306a36Sopenharmony_ci MACRONIX_RANDOMIZER_RANDEN | \ 2862306a36Sopenharmony_ci MACRONIX_RANDOMIZER_RANDOPT) 2962306a36Sopenharmony_ci#define MACRONIX_RANDOMIZER_MODE_EXIT \ 3062306a36Sopenharmony_ci (MACRONIX_RANDOMIZER_RANDEN | \ 3162306a36Sopenharmony_ci MACRONIX_RANDOMIZER_RANDOPT) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define MXIC_CMD_POWER_DOWN 0xB9 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 3662306a36Sopenharmony_ci#define MACRONIX_30LFXG18AC_OTP_START_PAGE 2 3762306a36Sopenharmony_ci#define MACRONIX_30LFXG18AC_OTP_PAGES 30 3862306a36Sopenharmony_ci#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 3962306a36Sopenharmony_ci#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ 4062306a36Sopenharmony_ci (MACRONIX_30LFXG18AC_OTP_PAGES * \ 4162306a36Sopenharmony_ci MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct nand_onfi_vendor_macronix { 4662306a36Sopenharmony_ci u8 reserved; 4762306a36Sopenharmony_ci u8 reliability_func; 4862306a36Sopenharmony_ci} __packed; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int macronix_nand_setup_read_retry(struct nand_chip *chip, int mode) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!chip->parameters.supports_set_get_features || 5562306a36Sopenharmony_ci !test_bit(ONFI_FEATURE_ADDR_READ_RETRY, 5662306a36Sopenharmony_ci chip->parameters.set_feature_list)) 5762306a36Sopenharmony_ci return -ENOTSUPP; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci feature[0] = mode; 6062306a36Sopenharmony_ci return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int macronix_nand_randomizer_check_enable(struct nand_chip *chip) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; 6662306a36Sopenharmony_ci int ret; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 6962306a36Sopenharmony_ci feature); 7062306a36Sopenharmony_ci if (ret < 0) 7162306a36Sopenharmony_ci return ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (feature[0]) 7462306a36Sopenharmony_ci return feature[0]; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci feature[0] = MACRONIX_RANDOMIZER_MODE_ENTER; 7762306a36Sopenharmony_ci ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 7862306a36Sopenharmony_ci feature); 7962306a36Sopenharmony_ci if (ret < 0) 8062306a36Sopenharmony_ci return ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* RANDEN and RANDOPT OTP bits are programmed */ 8362306a36Sopenharmony_ci feature[0] = 0x0; 8462306a36Sopenharmony_ci ret = nand_prog_page_op(chip, 0, 0, feature, 1); 8562306a36Sopenharmony_ci if (ret < 0) 8662306a36Sopenharmony_ci return ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 8962306a36Sopenharmony_ci feature); 9062306a36Sopenharmony_ci if (ret < 0) 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci feature[0] &= MACRONIX_RANDOMIZER_MODE_EXIT; 9462306a36Sopenharmony_ci ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 9562306a36Sopenharmony_ci feature); 9662306a36Sopenharmony_ci if (ret < 0) 9762306a36Sopenharmony_ci return ret; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void macronix_nand_onfi_init(struct nand_chip *chip) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct nand_parameters *p = &chip->parameters; 10562306a36Sopenharmony_ci struct nand_onfi_vendor_macronix *mxic; 10662306a36Sopenharmony_ci struct device_node *dn = nand_get_flash_node(chip); 10762306a36Sopenharmony_ci int rand_otp; 10862306a36Sopenharmony_ci int ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!p->onfi) 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci rand_otp = of_property_read_bool(dn, "mxic,enable-randomizer-otp"); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci mxic = (struct nand_onfi_vendor_macronix *)p->onfi->vendor; 11662306a36Sopenharmony_ci /* Subpage write is prohibited in randomizer operatoin */ 11762306a36Sopenharmony_ci if (rand_otp && chip->options & NAND_NO_SUBPAGE_WRITE && 11862306a36Sopenharmony_ci mxic->reliability_func & MACRONIX_RANDOMIZER_BIT) { 11962306a36Sopenharmony_ci if (p->supports_set_get_features) { 12062306a36Sopenharmony_ci bitmap_set(p->set_feature_list, 12162306a36Sopenharmony_ci ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 1); 12262306a36Sopenharmony_ci bitmap_set(p->get_feature_list, 12362306a36Sopenharmony_ci ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 1); 12462306a36Sopenharmony_ci ret = macronix_nand_randomizer_check_enable(chip); 12562306a36Sopenharmony_ci if (ret < 0) { 12662306a36Sopenharmony_ci bitmap_clear(p->set_feature_list, 12762306a36Sopenharmony_ci ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 12862306a36Sopenharmony_ci 1); 12962306a36Sopenharmony_ci bitmap_clear(p->get_feature_list, 13062306a36Sopenharmony_ci ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 13162306a36Sopenharmony_ci 1); 13262306a36Sopenharmony_ci pr_info("Macronix NAND randomizer failed\n"); 13362306a36Sopenharmony_ci } else { 13462306a36Sopenharmony_ci pr_info("Macronix NAND randomizer enabled\n"); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if ((mxic->reliability_func & MACRONIX_READ_RETRY_BIT) == 0) 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci chip->read_retries = MACRONIX_NUM_READ_RETRY_MODES; 14362306a36Sopenharmony_ci chip->ops.setup_read_retry = macronix_nand_setup_read_retry; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (p->supports_set_get_features) { 14662306a36Sopenharmony_ci bitmap_set(p->set_feature_list, 14762306a36Sopenharmony_ci ONFI_FEATURE_ADDR_READ_RETRY, 1); 14862306a36Sopenharmony_ci bitmap_set(p->get_feature_list, 14962306a36Sopenharmony_ci ONFI_FEATURE_ADDR_READ_RETRY, 1); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * Macronix AC series does not support using SET/GET_FEATURES to change 15562306a36Sopenharmony_ci * the timings unlike what is declared in the parameter page. Unflag 15662306a36Sopenharmony_ci * this feature to avoid unnecessary downturns. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistatic void macronix_nand_fix_broken_get_timings(struct nand_chip *chip) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int i; 16162306a36Sopenharmony_ci static const char * const broken_get_timings[] = { 16262306a36Sopenharmony_ci "MX30LF1G18AC", 16362306a36Sopenharmony_ci "MX30LF1G28AC", 16462306a36Sopenharmony_ci "MX30LF2G18AC", 16562306a36Sopenharmony_ci "MX30LF2G28AC", 16662306a36Sopenharmony_ci "MX30LF4G18AC", 16762306a36Sopenharmony_ci "MX30LF4G28AC", 16862306a36Sopenharmony_ci "MX60LF8G18AC", 16962306a36Sopenharmony_ci "MX30UF1G18AC", 17062306a36Sopenharmony_ci "MX30UF1G16AC", 17162306a36Sopenharmony_ci "MX30UF2G18AC", 17262306a36Sopenharmony_ci "MX30UF2G16AC", 17362306a36Sopenharmony_ci "MX30UF4G18AC", 17462306a36Sopenharmony_ci "MX30UF4G16AC", 17562306a36Sopenharmony_ci "MX30UF4G28AC", 17662306a36Sopenharmony_ci }; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!chip->parameters.supports_set_get_features) 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci i = match_string(broken_get_timings, ARRAY_SIZE(broken_get_timings), 18262306a36Sopenharmony_ci chip->parameters.model); 18362306a36Sopenharmony_ci if (i < 0) 18462306a36Sopenharmony_ci return; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci bitmap_clear(chip->parameters.get_feature_list, 18762306a36Sopenharmony_ci ONFI_FEATURE_ADDR_TIMING_MODE, 1); 18862306a36Sopenharmony_ci bitmap_clear(chip->parameters.set_feature_list, 18962306a36Sopenharmony_ci ONFI_FEATURE_ADDR_TIMING_MODE, 1); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * Macronix NAND supports Block Protection by Protectoin(PT) pin; 19462306a36Sopenharmony_ci * active high at power-on which protects the entire chip even the #WP is 19562306a36Sopenharmony_ci * disabled. Lock/unlock protection area can be partition according to 19662306a36Sopenharmony_ci * protection bits, i.e. upper 1/2 locked, upper 1/4 locked and so on. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_cistatic int mxic_nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; 20162306a36Sopenharmony_ci int ret; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci feature[0] = MXIC_BLOCK_PROTECTION_ALL_LOCK; 20462306a36Sopenharmony_ci nand_select_target(chip, 0); 20562306a36Sopenharmony_ci ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, 20662306a36Sopenharmony_ci feature); 20762306a36Sopenharmony_ci nand_deselect_target(chip); 20862306a36Sopenharmony_ci if (ret) 20962306a36Sopenharmony_ci pr_err("%s all blocks failed\n", __func__); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int mxic_nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; 21762306a36Sopenharmony_ci int ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK; 22062306a36Sopenharmony_ci nand_select_target(chip, 0); 22162306a36Sopenharmony_ci ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, 22262306a36Sopenharmony_ci feature); 22362306a36Sopenharmony_ci nand_deselect_target(chip); 22462306a36Sopenharmony_ci if (ret) 22562306a36Sopenharmony_ci pr_err("%s all blocks failed\n", __func__); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return ret; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void macronix_nand_block_protection_support(struct nand_chip *chip) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; 23362306a36Sopenharmony_ci int ret; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci bitmap_set(chip->parameters.get_feature_list, 23662306a36Sopenharmony_ci ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK; 23962306a36Sopenharmony_ci nand_select_target(chip, 0); 24062306a36Sopenharmony_ci ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, 24162306a36Sopenharmony_ci feature); 24262306a36Sopenharmony_ci nand_deselect_target(chip); 24362306a36Sopenharmony_ci if (ret || feature[0] != MXIC_BLOCK_PROTECTION_ALL_LOCK) { 24462306a36Sopenharmony_ci if (ret) 24562306a36Sopenharmony_ci pr_err("Block protection check failed\n"); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci bitmap_clear(chip->parameters.get_feature_list, 24862306a36Sopenharmony_ci ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci bitmap_set(chip->parameters.set_feature_list, 25362306a36Sopenharmony_ci ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci chip->ops.lock_area = mxic_nand_lock; 25662306a36Sopenharmony_ci chip->ops.unlock_area = mxic_nand_unlock; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int nand_power_down_op(struct nand_chip *chip) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (nand_has_exec_op(chip)) { 26462306a36Sopenharmony_ci struct nand_op_instr instrs[] = { 26562306a36Sopenharmony_ci NAND_OP_CMD(MXIC_CMD_POWER_DOWN, 0), 26662306a36Sopenharmony_ci }; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ret = nand_exec_op(chip, &op); 27162306a36Sopenharmony_ci if (ret) 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci chip->legacy.cmdfunc(chip, MXIC_CMD_POWER_DOWN, -1, -1); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int mxic_nand_suspend(struct nand_chip *chip) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci int ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci nand_select_target(chip, 0); 28662306a36Sopenharmony_ci ret = nand_power_down_op(chip); 28762306a36Sopenharmony_ci if (ret < 0) 28862306a36Sopenharmony_ci pr_err("Suspending MXIC NAND chip failed (%d)\n", ret); 28962306a36Sopenharmony_ci nand_deselect_target(chip); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void mxic_nand_resume(struct nand_chip *chip) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * Toggle #CS pin to resume NAND device and don't care 29862306a36Sopenharmony_ci * of the others CLE, #WE, #RE pins status. 29962306a36Sopenharmony_ci * A NAND controller ensure it is able to assert/de-assert #CS 30062306a36Sopenharmony_ci * by sending any byte over the NAND bus. 30162306a36Sopenharmony_ci * i.e., 30262306a36Sopenharmony_ci * NAND power down command or reset command w/o R/B# status checking. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci nand_select_target(chip, 0); 30562306a36Sopenharmony_ci nand_power_down_op(chip); 30662306a36Sopenharmony_ci /* The minimum of a recovery time tRDP is 35 us */ 30762306a36Sopenharmony_ci usleep_range(35, 100); 30862306a36Sopenharmony_ci nand_deselect_target(chip); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic void macronix_nand_deep_power_down_support(struct nand_chip *chip) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci int i; 31462306a36Sopenharmony_ci static const char * const deep_power_down_dev[] = { 31562306a36Sopenharmony_ci "MX30UF1G28AD", 31662306a36Sopenharmony_ci "MX30UF2G28AD", 31762306a36Sopenharmony_ci "MX30UF4G28AD", 31862306a36Sopenharmony_ci }; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci i = match_string(deep_power_down_dev, ARRAY_SIZE(deep_power_down_dev), 32162306a36Sopenharmony_ci chip->parameters.model); 32262306a36Sopenharmony_ci if (i < 0) 32362306a36Sopenharmony_ci return; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci chip->ops.suspend = mxic_nand_suspend; 32662306a36Sopenharmony_ci chip->ops.resume = mxic_nand_resume; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, 33062306a36Sopenharmony_ci size_t *retlen, 33162306a36Sopenharmony_ci struct otp_info *buf) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci if (len < sizeof(*buf)) 33462306a36Sopenharmony_ci return -EINVAL; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Always report that OTP is unlocked. Reason is that this 33762306a36Sopenharmony_ci * type of flash chip doesn't provide way to check that OTP 33862306a36Sopenharmony_ci * is locked or not: subfeature parameter is implemented as 33962306a36Sopenharmony_ci * volatile register. Technically OTP region could be locked 34062306a36Sopenharmony_ci * and become readonly, but as there is no way to check it, 34162306a36Sopenharmony_ci * don't allow to lock it ('_lock_user_prot_reg' callback 34262306a36Sopenharmony_ci * always returns -EOPNOTSUPP) and thus we report that OTP 34362306a36Sopenharmony_ci * is unlocked. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci buf->locked = 0; 34662306a36Sopenharmony_ci buf->start = 0; 34762306a36Sopenharmony_ci buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci *retlen = sizeof(*buf); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; 35962306a36Sopenharmony_ci return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 36062306a36Sopenharmony_ci feature_buf); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 36862306a36Sopenharmony_ci feature_buf); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, 37262306a36Sopenharmony_ci loff_t offs_in_flash, 37362306a36Sopenharmony_ci size_t len, size_t *retlen, 37462306a36Sopenharmony_ci u_char *buf, bool write) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct nand_chip *nand; 37762306a36Sopenharmony_ci size_t bytes_handled; 37862306a36Sopenharmony_ci off_t offs_in_page; 37962306a36Sopenharmony_ci u64 page; 38062306a36Sopenharmony_ci int ret; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci nand = mtd_to_nand(mtd); 38362306a36Sopenharmony_ci nand_select_target(nand, 0); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ret = macronix_30lfxg18ac_otp_enable(nand); 38662306a36Sopenharmony_ci if (ret) 38762306a36Sopenharmony_ci goto out_otp; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci page = offs_in_flash; 39062306a36Sopenharmony_ci /* 'page' will be result of division. */ 39162306a36Sopenharmony_ci offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); 39262306a36Sopenharmony_ci bytes_handled = 0; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci while (bytes_handled < len && 39562306a36Sopenharmony_ci page < MACRONIX_30LFXG18AC_OTP_PAGES) { 39662306a36Sopenharmony_ci size_t bytes_to_handle; 39762306a36Sopenharmony_ci u64 phys_page = page + MACRONIX_30LFXG18AC_OTP_START_PAGE; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci bytes_to_handle = min_t(size_t, len - bytes_handled, 40062306a36Sopenharmony_ci MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - 40162306a36Sopenharmony_ci offs_in_page); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (write) 40462306a36Sopenharmony_ci ret = nand_prog_page_op(nand, phys_page, offs_in_page, 40562306a36Sopenharmony_ci &buf[bytes_handled], bytes_to_handle); 40662306a36Sopenharmony_ci else 40762306a36Sopenharmony_ci ret = nand_read_page_op(nand, phys_page, offs_in_page, 40862306a36Sopenharmony_ci &buf[bytes_handled], bytes_to_handle); 40962306a36Sopenharmony_ci if (ret) 41062306a36Sopenharmony_ci goto out_otp; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci bytes_handled += bytes_to_handle; 41362306a36Sopenharmony_ci offs_in_page = 0; 41462306a36Sopenharmony_ci page++; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci *retlen = bytes_handled; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciout_otp: 42062306a36Sopenharmony_ci if (ret) 42162306a36Sopenharmony_ci dev_err(&mtd->dev, "failed to perform OTP IO: %i\n", ret); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = macronix_30lfxg18ac_otp_disable(nand); 42462306a36Sopenharmony_ci if (ret) 42562306a36Sopenharmony_ci dev_err(&mtd->dev, "failed to leave OTP mode after %s\n", 42662306a36Sopenharmony_ci write ? "write" : "read"); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci nand_deselect_target(nand); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, 43462306a36Sopenharmony_ci size_t len, size_t *rlen, 43562306a36Sopenharmony_ci const u_char *buf) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, 43862306a36Sopenharmony_ci true); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, 44262306a36Sopenharmony_ci size_t len, size_t *rlen, 44362306a36Sopenharmony_ci u_char *buf) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, 44962306a36Sopenharmony_ci size_t len) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci /* See comment in 'macronix_30lfxg18ac_get_otp_info()'. */ 45262306a36Sopenharmony_ci return -EOPNOTSUPP; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic void macronix_nand_setup_otp(struct nand_chip *chip) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci static const char * const supported_otp_models[] = { 45862306a36Sopenharmony_ci "MX30LF1G18AC", 45962306a36Sopenharmony_ci "MX30LF2G18AC", 46062306a36Sopenharmony_ci "MX30LF4G18AC", 46162306a36Sopenharmony_ci }; 46262306a36Sopenharmony_ci struct mtd_info *mtd; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (match_string(supported_otp_models, 46562306a36Sopenharmony_ci ARRAY_SIZE(supported_otp_models), 46662306a36Sopenharmony_ci chip->parameters.model) < 0) 46762306a36Sopenharmony_ci return; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!chip->parameters.supports_set_get_features) 47062306a36Sopenharmony_ci return; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci bitmap_set(chip->parameters.get_feature_list, 47362306a36Sopenharmony_ci ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); 47462306a36Sopenharmony_ci bitmap_set(chip->parameters.set_feature_list, 47562306a36Sopenharmony_ci ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci mtd = nand_to_mtd(chip); 47862306a36Sopenharmony_ci mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; 47962306a36Sopenharmony_ci mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; 48062306a36Sopenharmony_ci mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; 48162306a36Sopenharmony_ci mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int macronix_nand_init(struct nand_chip *chip) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci if (nand_is_slc(chip)) 48762306a36Sopenharmony_ci chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci macronix_nand_fix_broken_get_timings(chip); 49062306a36Sopenharmony_ci macronix_nand_onfi_init(chip); 49162306a36Sopenharmony_ci macronix_nand_block_protection_support(chip); 49262306a36Sopenharmony_ci macronix_nand_deep_power_down_support(chip); 49362306a36Sopenharmony_ci macronix_nand_setup_otp(chip); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ciconst struct nand_manufacturer_ops macronix_nand_manuf_ops = { 49962306a36Sopenharmony_ci .init = macronix_nand_init, 50062306a36Sopenharmony_ci}; 501