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