mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
mtd: rawnand: Choose the best timings, NV-DDR included
Now that the necessary peaces to support the NV-DDR interface type have been contributed, let's add the relevant logic to make use of it. In particular, the core does not choose the best SDR timings anymore but calls a more generic helper instead. This helper checks if NV-DDR is supported by trying to find the best NV-DDR supported mode through a logic very close to what is being done for SDR timings. If no NV-DDR mode in common between the NAND controller and the NAND chip is found, the core will fallback to SDR. Side note: theoretically, the data clock speed in NV-DDR mode 0 is slower than in SDR mode 5. In the situation where we would get a working NV-DDR mode 0, we could also try if SDR mode 5 is supported and eventually fallback to it in order to get the fastest possible throughput. However, in the field, it looks like most of the devices supporting NV-DDR avoid implementing the fastest SDR modes (like 4 and 5 EDO modes, which are a bit more complicated to handle than the other SDR modes). So, we will stick to the simplest logic: try NV-DDR otherwise fallback to SDR. If someone else experiences strong differences because of that we may still implement the logic defined above. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20210505213750.257417-19-miquel.raynal@bootlin.com
This commit is contained in:
parent
9d3194bf2a
commit
a9ecc8c814
@ -95,6 +95,9 @@ onfi_find_closest_nvddr_mode(const struct nand_nvddr_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);
|
||||||
|
int nand_choose_best_nvddr_timings(struct nand_chip *chip,
|
||||||
|
struct nand_interface_config *iface,
|
||||||
|
struct nand_nvddr_timings *spec_timings);
|
||||||
const struct nand_interface_config *nand_get_reset_interface_config(void);
|
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);
|
||||||
|
@ -961,6 +961,80 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_choose_best_nvddr_timings - Pick up the best NVDDR 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, retrieve supported
|
||||||
|
* timing modes from ONFI information.
|
||||||
|
*/
|
||||||
|
int nand_choose_best_nvddr_timings(struct nand_chip *chip,
|
||||||
|
struct nand_interface_config *iface,
|
||||||
|
struct nand_nvddr_timings *spec_timings)
|
||||||
|
{
|
||||||
|
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||||
|
int best_mode = 0, mode, ret;
|
||||||
|
|
||||||
|
iface->type = NAND_NVDDR_IFACE;
|
||||||
|
|
||||||
|
if (spec_timings) {
|
||||||
|
iface->timings.nvddr = *spec_timings;
|
||||||
|
iface->timings.mode = onfi_find_closest_nvddr_mode(spec_timings);
|
||||||
|
|
||||||
|
/* Verify the controller supports the requested interface */
|
||||||
|
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||||
|
iface);
|
||||||
|
if (!ret) {
|
||||||
|
chip->best_interface_config = iface;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fallback to slower modes */
|
||||||
|
best_mode = iface->timings.mode;
|
||||||
|
} else if (chip->parameters.onfi) {
|
||||||
|
best_mode = fls(chip->parameters.onfi->nvddr_timing_modes) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (mode = best_mode; mode >= 0; mode--) {
|
||||||
|
onfi_fill_interface_config(chip, iface, NAND_NVDDR_IFACE, mode);
|
||||||
|
|
||||||
|
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||||
|
iface);
|
||||||
|
if (!ret) {
|
||||||
|
chip->best_interface_config = iface;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_choose_best_timings - Pick up the best NVDDR or SDR timings that both
|
||||||
|
* NAND controller and the NAND chip support
|
||||||
|
* @chip: the NAND chip
|
||||||
|
* @iface: the interface configuration (can eventually be updated)
|
||||||
|
*
|
||||||
|
* If specific timings are provided, use them. Otherwise, retrieve supported
|
||||||
|
* timing modes from ONFI information.
|
||||||
|
*/
|
||||||
|
static int nand_choose_best_timings(struct nand_chip *chip,
|
||||||
|
struct nand_interface_config *iface)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Try the fastest timings: NV-DDR */
|
||||||
|
ret = nand_choose_best_nvddr_timings(chip, iface, NULL);
|
||||||
|
if (!ret)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Fallback to SDR timings otherwise */
|
||||||
|
return nand_choose_best_sdr_timings(chip, iface, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_choose_interface_config - find the best data interface and timings
|
* nand_choose_interface_config - find the best data interface and timings
|
||||||
* @chip: The NAND chip
|
* @chip: The NAND chip
|
||||||
@ -989,7 +1063,7 @@ static int nand_choose_interface_config(struct nand_chip *chip)
|
|||||||
if (chip->ops.choose_interface_config)
|
if (chip->ops.choose_interface_config)
|
||||||
ret = chip->ops.choose_interface_config(chip, iface);
|
ret = chip->ops.choose_interface_config(chip, iface);
|
||||||
else
|
else
|
||||||
ret = nand_choose_best_sdr_timings(chip, iface, NULL);
|
ret = nand_choose_best_timings(chip, iface);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
kfree(iface);
|
kfree(iface);
|
||||||
|
Loading…
Reference in New Issue
Block a user