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: Miquel Raynal <miquel.raynal@bootlin.com> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com> Link: https://lore.kernel.org/linux-mtd/20200529111322.7184-29-miquel.raynal@bootlin.com
This commit is contained in:
@@ -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,
|
int nand_choose_best_sdr_timings(struct nand_chip *chip,
|
||||||
struct nand_interface_config *iface,
|
struct nand_interface_config *iface,
|
||||||
struct nand_sdr_timings *spec_timings);
|
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_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_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
|
||||||
int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf,
|
int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf,
|
||||||
|
@@ -928,9 +928,9 @@ static int nand_reset_interface(struct nand_chip *chip, int chipnr)
|
|||||||
* timings to timing mode 0.
|
* timings to timing mode 0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onfi_fill_interface_config(chip, &chip->interface_config,
|
chip->current_interface_config = nand_get_reset_interface_config();
|
||||||
NAND_SDR_IFACE, 0);
|
ret = ops->setup_interface(chip, chipnr,
|
||||||
ret = ops->setup_interface(chip, chipnr, &chip->interface_config);
|
chip->current_interface_config);
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_err("Failed to configure data interface to SDR timing mode 0\n");
|
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)
|
static int nand_setup_interface(struct nand_chip *chip, int chipnr)
|
||||||
{
|
{
|
||||||
u8 mode = chip->interface_config.timings.mode;
|
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||||
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { mode, };
|
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!nand_controller_can_setup_interface(chip))
|
if (!nand_controller_can_setup_interface(chip))
|
||||||
return 0;
|
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) */
|
/* Change the mode on the chip side (if supported by the NAND chip) */
|
||||||
if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
|
if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
|
||||||
nand_select_target(chip, chipnr);
|
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 */
|
/* Change the mode on the controller side */
|
||||||
ret = chip->controller->ops->setup_interface(chip, chipnr,
|
ret = ops->setup_interface(chip, chipnr, chip->best_interface_config);
|
||||||
&chip->interface_config);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Check the mode has been accepted by the chip, if supported */
|
/* Check the mode has been accepted by the chip, if supported */
|
||||||
if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE))
|
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);
|
memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
|
||||||
nand_select_target(chip, chipnr);
|
nand_select_target(chip, chipnr);
|
||||||
@@ -984,12 +995,15 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err_reset_chip;
|
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",
|
pr_warn("timing mode %d not acknowledged by the NAND chip\n",
|
||||||
mode);
|
chip->best_interface_config->timings.mode);
|
||||||
goto err_reset_chip;
|
goto err_reset_chip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_interface_config:
|
||||||
|
chip->current_interface_config = chip->best_interface_config;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_reset_chip:
|
err_reset_chip:
|
||||||
@@ -1031,8 +1045,10 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
|
|||||||
/* Verify the controller supports the requested interface */
|
/* Verify the controller supports the requested interface */
|
||||||
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||||
iface);
|
iface);
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
|
chip->best_interface_config = iface;
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fallback to slower modes */
|
/* Fallback to slower modes */
|
||||||
best_mode = iface->timings.mode;
|
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,
|
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||||
iface);
|
iface);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return 0;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chip->best_interface_config = iface;
|
||||||
|
|
||||||
return 0;
|
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)
|
static int nand_choose_interface_config(struct nand_chip *chip)
|
||||||
{
|
{
|
||||||
|
struct nand_interface_config *iface;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!nand_controller_can_setup_interface(chip))
|
if (!nand_controller_can_setup_interface(chip))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (chip->ops.choose_interface_config)
|
iface = kzalloc(sizeof(*iface), GFP_KERNEL);
|
||||||
return chip->ops.choose_interface_config(chip,
|
if (!iface)
|
||||||
&chip->interface_config);
|
return -ENOMEM;
|
||||||
|
|
||||||
return nand_choose_best_sdr_timings(chip, &chip->interface_config,
|
if (chip->ops.choose_interface_config)
|
||||||
NULL);
|
ret = chip->ops.choose_interface_config(chip, iface);
|
||||||
|
else
|
||||||
|
ret = nand_choose_best_sdr_timings(chip, iface, NULL);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
kfree(iface);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2501,7 +2529,6 @@ EXPORT_SYMBOL_GPL(nand_subop_get_data_len);
|
|||||||
*/
|
*/
|
||||||
int nand_reset(struct nand_chip *chip, int chipnr)
|
int nand_reset(struct nand_chip *chip, int chipnr)
|
||||||
{
|
{
|
||||||
struct nand_interface_config saved_intf_config = chip->interface_config;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = nand_reset_interface(chip, chipnr);
|
ret = nand_reset_interface(chip, chipnr);
|
||||||
@@ -2519,18 +2546,6 @@ int nand_reset(struct nand_chip *chip, int chipnr)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return 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);
|
ret = nand_setup_interface(chip, chipnr);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -5198,7 +5213,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
|||||||
mutex_init(&chip->lock);
|
mutex_init(&chip->lock);
|
||||||
|
|
||||||
/* Enforce the right timings for reset/detection */
|
/* 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);
|
ret = nand_dt_init(chip);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -5994,7 +6009,7 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||||||
for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
|
for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
|
||||||
ret = nand_setup_interface(chip, i);
|
ret = nand_setup_interface(chip, i);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_nanddev_cleanup;
|
goto err_free_interface_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check, if we should skip the bad block table scan */
|
/* 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 */
|
/* Build bad block table */
|
||||||
ret = nand_create_bbt(chip);
|
ret = nand_create_bbt(chip);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_nanddev_cleanup;
|
goto err_free_interface_config;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_free_interface_config:
|
||||||
|
kfree(chip->best_interface_config);
|
||||||
|
|
||||||
err_nanddev_cleanup:
|
err_nanddev_cleanup:
|
||||||
nanddev_cleanup(&chip->base);
|
nanddev_cleanup(&chip->base);
|
||||||
@@ -6101,6 +6118,9 @@ void nand_cleanup(struct nand_chip *chip)
|
|||||||
& NAND_BBT_DYNAMICSTRUCT)
|
& NAND_BBT_DYNAMICSTRUCT)
|
||||||
kfree(chip->badblock_pattern);
|
kfree(chip->badblock_pattern);
|
||||||
|
|
||||||
|
/* Free the data interface */
|
||||||
|
kfree(chip->best_interface_config);
|
||||||
|
|
||||||
/* Free manufacturer priv data. */
|
/* Free manufacturer priv data. */
|
||||||
nand_manufacturer_cleanup(chip);
|
nand_manufacturer_cleanup(chip);
|
||||||
|
|
||||||
|
@@ -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
|
* onfi_find_closest_sdr_mode - Derive the closest ONFI SDR timing mode given a
|
||||||
* set of timings
|
* set of timings
|
||||||
|
@@ -1069,7 +1069,11 @@ struct nand_manufacturer {
|
|||||||
* @options: Various chip options. They can partly be set to inform nand_scan
|
* @options: Various chip options. They can partly be set to inform nand_scan
|
||||||
* about special functionality. See the defines for further
|
* about special functionality. See the defines for further
|
||||||
* explanation.
|
* 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_erase_shift: Number of address bits in a bbt entry
|
||||||
* @bbt_options: Bad block table specific options. All options used here must
|
* @bbt_options: Bad block table specific options. All options used here must
|
||||||
* come from bbm.h. By default, these options will be copied to
|
* come from bbm.h. By default, these options will be copied to
|
||||||
@@ -1116,7 +1120,8 @@ struct nand_chip {
|
|||||||
unsigned int options;
|
unsigned int options;
|
||||||
|
|
||||||
/* Data interface */
|
/* 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 */
|
/* Bad block information */
|
||||||
unsigned int bbt_erase_shift;
|
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 *
|
static inline const struct nand_interface_config *
|
||||||
nand_get_interface_config(struct nand_chip *chip)
|
nand_get_interface_config(struct nand_chip *chip)
|
||||||
{
|
{
|
||||||
return &chip->interface_config;
|
return chip->current_interface_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user