mtd: nand: automate NAND timings selection
The NAND framework provides several helpers to query timing modes supported by a NAND chip, but this implies that all NAND controller drivers have to implement the same timings selection dance. Also currently NAND devices can be resetted at arbitrary places which also resets the timing for ONFI chips to timing mode 0. Provide a common logic to select the best timings based on ONFI or ->onfi_timing_mode_default information. Hook this into nand_reset() to make sure the new timing is applied each time during a reset. NAND controller willing to support timings adjustment should just implement the ->setup_data_interface() method. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Родитель
6e1f9708db
Коммит
d8e725dd83
|
@ -947,6 +947,148 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_reset_data_interface - Reset data interface and timings
|
||||||
|
* @chip: The NAND chip
|
||||||
|
*
|
||||||
|
* Reset the Data interface and timings to ONFI mode 0.
|
||||||
|
*
|
||||||
|
* Returns 0 for success or negative error code otherwise.
|
||||||
|
*/
|
||||||
|
static int nand_reset_data_interface(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
const struct nand_data_interface *conf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!chip->setup_data_interface)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The ONFI specification says:
|
||||||
|
* "
|
||||||
|
* To transition from NV-DDR or NV-DDR2 to the SDR data
|
||||||
|
* interface, the host shall use the Reset (FFh) command
|
||||||
|
* using SDR timing mode 0. A device in any timing mode is
|
||||||
|
* required to recognize Reset (FFh) command issued in SDR
|
||||||
|
* timing mode 0.
|
||||||
|
* "
|
||||||
|
*
|
||||||
|
* Configure the data interface in SDR mode and set the
|
||||||
|
* timings to timing mode 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
conf = nand_get_default_data_interface();
|
||||||
|
ret = chip->setup_data_interface(mtd, conf, false);
|
||||||
|
if (ret)
|
||||||
|
pr_err("Failed to configure data interface to SDR timing mode 0\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_setup_data_interface - Setup the best data interface and timings
|
||||||
|
* @chip: The NAND chip
|
||||||
|
*
|
||||||
|
* Find and configure the best data interface and NAND timings supported by
|
||||||
|
* the chip and the driver.
|
||||||
|
* First tries to retrieve supported timing modes from ONFI information,
|
||||||
|
* and if the NAND chip does not support ONFI, relies on the
|
||||||
|
* ->onfi_timing_mode_default specified in the nand_ids table.
|
||||||
|
*
|
||||||
|
* Returns 0 for success or negative error code otherwise.
|
||||||
|
*/
|
||||||
|
static int nand_setup_data_interface(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!chip->setup_data_interface || !chip->data_interface)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the timing mode has been changed on the chip side
|
||||||
|
* before changing timings on the controller side.
|
||||||
|
*/
|
||||||
|
if (chip->onfi_version) {
|
||||||
|
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
|
||||||
|
chip->onfi_timing_mode_default,
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = chip->onfi_set_features(mtd, chip,
|
||||||
|
ONFI_FEATURE_ADDR_TIMING_MODE,
|
||||||
|
tmode_param);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = chip->setup_data_interface(mtd, chip->data_interface, false);
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_init_data_interface - find the best data interface and timings
|
||||||
|
* @chip: The NAND chip
|
||||||
|
*
|
||||||
|
* Find the best data interface and NAND timings supported by the chip
|
||||||
|
* and the driver.
|
||||||
|
* First tries to retrieve supported timing modes from ONFI information,
|
||||||
|
* and if the NAND chip does not support ONFI, relies on the
|
||||||
|
* ->onfi_timing_mode_default specified in the nand_ids table. After this
|
||||||
|
* function nand_chip->data_interface is initialized with the best timing mode
|
||||||
|
* available.
|
||||||
|
*
|
||||||
|
* Returns 0 for success or negative error code otherwise.
|
||||||
|
*/
|
||||||
|
static int nand_init_data_interface(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
int modes, mode, ret;
|
||||||
|
|
||||||
|
if (!chip->setup_data_interface)
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
modes = onfi_get_async_timing_mode(chip);
|
||||||
|
if (modes == ONFI_TIMING_MODE_UNKNOWN) {
|
||||||
|
if (!chip->onfi_timing_mode_default)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
modes = GENMASK(chip->onfi_timing_mode_default, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->data_interface = kzalloc(sizeof(*chip->data_interface),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!chip->data_interface)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (mode = fls(modes) - 1; mode >= 0; mode--) {
|
||||||
|
ret = onfi_init_data_interface(chip, chip->data_interface,
|
||||||
|
NAND_SDR_IFACE, mode);
|
||||||
|
if (ret)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = chip->setup_data_interface(mtd, chip->data_interface,
|
||||||
|
true);
|
||||||
|
if (!ret) {
|
||||||
|
chip->onfi_timing_mode_default = mode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nand_release_data_interface(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
kfree(chip->data_interface);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_reset - Reset and initialize a NAND device
|
* nand_reset - Reset and initialize a NAND device
|
||||||
* @chip: The NAND chip
|
* @chip: The NAND chip
|
||||||
|
@ -956,9 +1098,18 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
int nand_reset(struct nand_chip *chip)
|
int nand_reset(struct nand_chip *chip)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = nand_reset_data_interface(chip);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||||
|
|
||||||
|
ret = nand_setup_data_interface(chip);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4172,6 +4323,10 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||||
return PTR_ERR(type);
|
return PTR_ERR(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = nand_init_data_interface(chip);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
chip->select_chip(mtd, -1);
|
chip->select_chip(mtd, -1);
|
||||||
|
|
||||||
/* Check for a chip array */
|
/* Check for a chip array */
|
||||||
|
@ -4631,6 +4786,8 @@ void nand_release(struct mtd_info *mtd)
|
||||||
|
|
||||||
mtd_device_unregister(mtd);
|
mtd_device_unregister(mtd);
|
||||||
|
|
||||||
|
nand_release_data_interface(chip);
|
||||||
|
|
||||||
/* Free bad block table memory */
|
/* Free bad block table memory */
|
||||||
kfree(chip->bbt);
|
kfree(chip->bbt);
|
||||||
if (!(chip->options & NAND_OWN_BUFFERS))
|
if (!(chip->options & NAND_OWN_BUFFERS))
|
||||||
|
|
|
@ -751,10 +751,9 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
|
||||||
* also from the datasheet. It is the recommended ECC step
|
* also from the datasheet. It is the recommended ECC step
|
||||||
* size, if known; if unknown, set to zero.
|
* size, if known; if unknown, set to zero.
|
||||||
* @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
|
* @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
|
||||||
* either deduced from the datasheet if the NAND
|
* set to the actually used ONFI mode if the chip is
|
||||||
* chip is not ONFI compliant or set to 0 if it is
|
* ONFI compliant or deduced from the datasheet if
|
||||||
* (an ONFI chip is always configured in mode 0
|
* the NAND chip is not ONFI compliant.
|
||||||
* after a NAND reset)
|
|
||||||
* @numchips: [INTERN] number of physical chips
|
* @numchips: [INTERN] number of physical chips
|
||||||
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
||||||
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
||||||
|
@ -774,6 +773,7 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
|
||||||
* @read_retries: [INTERN] the number of read retry modes supported
|
* @read_retries: [INTERN] the number of read retry modes supported
|
||||||
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
|
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
|
||||||
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
|
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
|
||||||
|
* @setup_data_interface: [OPTIONAL] setup the data interface and timing
|
||||||
* @bbt: [INTERN] bad block table pointer
|
* @bbt: [INTERN] bad block table pointer
|
||||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
|
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
|
||||||
* lookup.
|
* lookup.
|
||||||
|
@ -820,6 +820,10 @@ struct nand_chip {
|
||||||
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
int feature_addr, uint8_t *subfeature_para);
|
int feature_addr, uint8_t *subfeature_para);
|
||||||
int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
|
int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
|
||||||
|
int (*setup_data_interface)(struct mtd_info *mtd,
|
||||||
|
const struct nand_data_interface *conf,
|
||||||
|
bool check_only);
|
||||||
|
|
||||||
|
|
||||||
int chip_delay;
|
int chip_delay;
|
||||||
unsigned int options;
|
unsigned int options;
|
||||||
|
@ -849,6 +853,8 @@ struct nand_chip {
|
||||||
struct nand_jedec_params jedec_params;
|
struct nand_jedec_params jedec_params;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct nand_data_interface *data_interface;
|
||||||
|
|
||||||
int read_retries;
|
int read_retries;
|
||||||
|
|
||||||
flstate_t state;
|
flstate_t state;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче