Commit 35b6bcc9 authored by Miquel Raynal's avatar Miquel Raynal
Browse files

mtd: rawnand: Allocate the interface configurations dynamically



Instead of manipulating the statically allocated structure and copy
timings around, allocate one at identification time and save it in the
nand_chip structure once it has been initialized.

All NAND chips using the same interface configuration during reset and
startup, we define a helper to retrieve a single reset interface
configuration object, shared across all NAND chips.

We use a second pointer to always have a reference on the currently
applied interface configuration, which may either point to the "best
interface configuration" or to the "default reset interface
configuration".

Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Link: https://lore.kernel.org/linux-mtd/20200529111322.7184-29-miquel.raynal@bootlin.com
parent a69ad111
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings);
int nand_choose_best_sdr_timings(struct nand_chip *chip,
				 struct nand_interface_config *iface,
				 struct nand_sdr_timings *spec_timings);
const struct nand_interface_config *nand_get_reset_interface_config(void);
int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf,
+52 −32
Original line number Diff line number Diff line
@@ -928,9 +928,9 @@ static int nand_reset_interface(struct nand_chip *chip, int chipnr)
	 * timings to timing mode 0.
	 */

	onfi_fill_interface_config(chip, &chip->interface_config,
				   NAND_SDR_IFACE, 0);
	ret = ops->setup_interface(chip, chipnr, &chip->interface_config);
	chip->current_interface_config = nand_get_reset_interface_config();
	ret = ops->setup_interface(chip, chipnr,
				   chip->current_interface_config);
	if (ret)
		pr_err("Failed to configure data interface to SDR timing mode 0\n");

@@ -949,13 +949,25 @@ static int nand_reset_interface(struct nand_chip *chip, int chipnr)
 */
static int nand_setup_interface(struct nand_chip *chip, int chipnr)
{
	u8 mode = chip->interface_config.timings.mode;
	u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { mode, };
	const struct nand_controller_ops *ops = chip->controller->ops;
	u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
	int ret;

	if (!nand_controller_can_setup_interface(chip))
		return 0;

	/*
	 * A nand_reset_interface() put both the NAND chip and the NAND
	 * controller in timings mode 0. If the default mode for this chip is
	 * also 0, no need to proceed to the change again. Plus, at probe time,
	 * nand_setup_interface() uses ->set/get_features() which would
	 * fail anyway as the parameter page is not available yet.
	 */
	if (!chip->best_interface_config)
		return 0;

	tmode_param[0] = chip->best_interface_config->timings.mode;

	/* Change the mode on the chip side (if supported by the NAND chip) */
	if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
		nand_select_target(chip, chipnr);
@@ -967,14 +979,13 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
	}

	/* Change the mode on the controller side */
	ret = chip->controller->ops->setup_interface(chip, chipnr,
						     &chip->interface_config);
	ret = ops->setup_interface(chip, chipnr, chip->best_interface_config);
	if (ret)
		return ret;

	/* Check the mode has been accepted by the chip, if supported */
	if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE))
		return 0;
		goto update_interface_config;

	memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
	nand_select_target(chip, chipnr);
@@ -984,12 +995,15 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
	if (ret)
		goto err_reset_chip;

	if (tmode_param[0] != mode) {
	if (tmode_param[0] != chip->best_interface_config->timings.mode) {
		pr_warn("timing mode %d not acknowledged by the NAND chip\n",
			mode);
			chip->best_interface_config->timings.mode);
		goto err_reset_chip;
	}

update_interface_config:
	chip->current_interface_config = chip->best_interface_config;

	return 0;

err_reset_chip:
@@ -1031,8 +1045,10 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
		/* Verify the controller supports the requested interface */
		ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
					   iface);
		if (!ret)
		if (!ret) {
			chip->best_interface_config = iface;
			return ret;
		}

		/* Fallback to slower modes */
		best_mode = iface->timings.mode;
@@ -1046,9 +1062,11 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
		ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
					   iface);
		if (!ret)
			return 0;
			break;
	}

	chip->best_interface_config = iface;

	return 0;
}

@@ -1067,15 +1085,25 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
 */
static int nand_choose_interface_config(struct nand_chip *chip)
{
	struct nand_interface_config *iface;
	int ret;

	if (!nand_controller_can_setup_interface(chip))
		return 0;

	iface = kzalloc(sizeof(*iface), GFP_KERNEL);
	if (!iface)
		return -ENOMEM;

	if (chip->ops.choose_interface_config)
		return chip->ops.choose_interface_config(chip,
							 &chip->interface_config);
		ret = chip->ops.choose_interface_config(chip, iface);
	else
		ret = nand_choose_best_sdr_timings(chip, iface, NULL);

	if (ret)
		kfree(iface);

	return nand_choose_best_sdr_timings(chip, &chip->interface_config,
					    NULL);
	return ret;
}

/**
@@ -2501,7 +2529,6 @@ EXPORT_SYMBOL_GPL(nand_subop_get_data_len);
 */
int nand_reset(struct nand_chip *chip, int chipnr)
{
	struct nand_interface_config saved_intf_config = chip->interface_config;
	int ret;

	ret = nand_reset_interface(chip, chipnr);
@@ -2519,18 +2546,6 @@ int nand_reset(struct nand_chip *chip, int chipnr)
	if (ret)
		return ret;

	/*
	 * A nand_reset_interface() put both the NAND chip and the NAND
	 * controller in timings mode 0. If the default mode for this chip is
	 * also 0, no need to proceed to the change again. Plus, at probe time,
	 * nand_setup_interface() uses ->set/get_features() which would
	 * fail anyway as the parameter page is not available yet.
	 */
	if (!memcmp(&chip->interface_config, &saved_intf_config,
		    sizeof(saved_intf_config)))
		return 0;

	chip->interface_config = saved_intf_config;
	ret = nand_setup_interface(chip, chipnr);
	if (ret)
		return ret;
@@ -5198,7 +5213,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
	mutex_init(&chip->lock);

	/* Enforce the right timings for reset/detection */
	onfi_fill_interface_config(chip, &chip->interface_config, NAND_SDR_IFACE, 0);
	chip->current_interface_config = nand_get_reset_interface_config();

	ret = nand_dt_init(chip);
	if (ret)
@@ -5994,7 +6009,7 @@ static int nand_scan_tail(struct nand_chip *chip)
	for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
		ret = nand_setup_interface(chip, i);
		if (ret)
			goto err_nanddev_cleanup;
			goto err_free_interface_config;
	}

	/* Check, if we should skip the bad block table scan */
@@ -6004,10 +6019,12 @@ static int nand_scan_tail(struct nand_chip *chip)
	/* Build bad block table */
	ret = nand_create_bbt(chip);
	if (ret)
		goto err_nanddev_cleanup;
		goto err_free_interface_config;

	return 0;

err_free_interface_config:
	kfree(chip->best_interface_config);

err_nanddev_cleanup:
	nanddev_cleanup(&chip->base);
@@ -6101,6 +6118,9 @@ void nand_cleanup(struct nand_chip *chip)
			& NAND_BBT_DYNAMICSTRUCT)
		kfree(chip->badblock_pattern);

	/* Free the data interface */
	kfree(chip->best_interface_config);

	/* Free manufacturer priv data. */
	nand_manufacturer_cleanup(chip);

+6 −0
Original line number Diff line number Diff line
@@ -292,6 +292,12 @@ static const struct nand_interface_config onfi_sdr_timings[] = {
	},
};

/* All NAND chips share the same reset data interface: SDR mode 0 */
const struct nand_interface_config *nand_get_reset_interface_config(void)
{
	return &onfi_sdr_timings[0];
}

/**
 * onfi_find_closest_sdr_mode - Derive the closest ONFI SDR timing mode given a
 *                              set of timings
+8 −3
Original line number Diff line number Diff line
@@ -1069,7 +1069,11 @@ struct nand_manufacturer {
 * @options: Various chip options. They can partly be set to inform nand_scan
 *           about special functionality. See the defines for further
 *           explanation.
 * @interface_config: NAND interface timing information
 * @current_interface_config: The currently used NAND interface configuration
 * @best_interface_config: The best NAND interface configuration which fits both
 *                         the NAND chip and NAND controller constraints. If
 *                         unset, the default reset interface configuration must
 *                         be used.
 * @bbt_erase_shift: Number of address bits in a bbt entry
 * @bbt_options: Bad block table specific options. All options used here must
 *               come from bbm.h. By default, these options will be copied to
@@ -1116,7 +1120,8 @@ struct nand_chip {
	unsigned int options;

	/* Data interface */
	struct nand_interface_config interface_config;
	const struct nand_interface_config *current_interface_config;
	struct nand_interface_config *best_interface_config;

	/* Bad block information */
	unsigned int bbt_erase_shift;
@@ -1209,7 +1214,7 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
static inline const struct nand_interface_config *
nand_get_interface_config(struct nand_chip *chip)
{
	return &chip->interface_config;
	return chip->current_interface_config;
}

/*