mirror of
https://github.com/torvalds/linux.git
synced 2024-12-14 23:25:54 +00:00
mtd: rawnand: Introduce nand_choose_best_sdr_timings()
Extract the logic out of nand_choose_interface_config() to create a public helper that can be reused by manufacturer drivers. Add the possibility to provide a specific set of timings. 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-22-miquel.raynal@bootlin.com
This commit is contained in:
parent
42a9ad050e
commit
b5b39f640c
@ -90,6 +90,9 @@ void onfi_fill_interface_config(struct nand_chip *chip,
|
||||
unsigned int timing_mode);
|
||||
unsigned int
|
||||
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);
|
||||
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,
|
||||
|
@ -1005,6 +1005,62 @@ err_reset_chip:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_choose_best_sdr_timings - Pick up the best SDR timings that both the
|
||||
* NAND controller and the NAND chip support
|
||||
* @chip: the NAND chip
|
||||
* @iface: the interface configuration (can eventually be updated)
|
||||
* @spec_timings: specific timings, when not fitting the ONFI specification
|
||||
*
|
||||
* If specific timings are provided, use them. Otherwise, try to retrieve
|
||||
* supported timing modes from ONFI information. Finally, if the NAND chip does
|
||||
* not follow the ONFI specification, rely on the ->default_timing_mode
|
||||
* specified in the nand_ids table.
|
||||
*/
|
||||
int nand_choose_best_sdr_timings(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
struct nand_sdr_timings *spec_timings)
|
||||
{
|
||||
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||
int best_mode = 0, mode, ret;
|
||||
|
||||
iface->type = NAND_SDR_IFACE;
|
||||
|
||||
if (spec_timings) {
|
||||
iface->timings.sdr = *spec_timings;
|
||||
iface->timings.mode = onfi_find_closest_sdr_mode(spec_timings);
|
||||
|
||||
/* Verify the controller supports the requested interface */
|
||||
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||
iface);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
/* Fallback to slower modes */
|
||||
best_mode = iface->timings.mode;
|
||||
} else {
|
||||
if (chip->parameters.onfi) {
|
||||
unsigned int onfi_modes;
|
||||
|
||||
onfi_modes = chip->parameters.onfi->async_timing_mode;
|
||||
best_mode = fls(onfi_modes) - 1;
|
||||
} else {
|
||||
best_mode = chip->onfi_timing_mode_default;
|
||||
}
|
||||
}
|
||||
|
||||
for (mode = best_mode; mode >= 0; mode--) {
|
||||
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, mode);
|
||||
|
||||
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||
iface);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_choose_interface_config - find the best data interface and timings
|
||||
* @chip: The NAND chip
|
||||
@ -1016,48 +1072,14 @@ err_reset_chip:
|
||||
* ->onfi_timing_mode_default specified in the nand_ids table. After this
|
||||
* function nand_chip->interface_ is initialized with the best timing mode
|
||||
* available.
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_choose_interface_config(struct nand_chip *chip)
|
||||
{
|
||||
int modes, mode, ret;
|
||||
|
||||
if (!nand_controller_can_setup_interface(chip))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* First try to identify the best timings from ONFI parameters and
|
||||
* if the NAND does not support ONFI, fallback to the default ONFI
|
||||
* timing mode.
|
||||
*/
|
||||
if (chip->parameters.onfi) {
|
||||
modes = chip->parameters.onfi->async_timing_mode;
|
||||
} else {
|
||||
if (!chip->onfi_timing_mode_default)
|
||||
return 0;
|
||||
|
||||
modes = GENMASK(chip->onfi_timing_mode_default, 0);
|
||||
}
|
||||
|
||||
for (mode = fls(modes) - 1; mode >= 0; mode--) {
|
||||
onfi_fill_interface_config(chip, &chip->interface_config,
|
||||
NAND_SDR_IFACE, mode);
|
||||
|
||||
/*
|
||||
* Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the
|
||||
* controller supports the requested timings.
|
||||
*/
|
||||
ret = chip->controller->ops->setup_interface(chip,
|
||||
NAND_DATA_IFACE_CHECK_ONLY,
|
||||
&chip->interface_config);
|
||||
if (!ret) {
|
||||
chip->onfi_timing_mode_default = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nand_choose_best_sdr_timings(chip, &chip->interface_config,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user