Core changes:

* Drop useless 'depends on' in Kconfig
 * Add an extra level in the Kconfig hierarchy
 * Trivial spellings
 * Dynamic allocation of the interface configurations
 * Dropping the default ONFI timing mode
 * Various cleanup (types, structures, naming, comments)
 * Hide the chip->data_interface indirection
 * Add the generic rb-gpios property
 * Add the ->choose_interface_config() hook
 * Introduce nand_choose_best_sdr_timings()
 * Use default values for tPROG_max and tBERS_max
 * Avoid redefining tR_max and tCCS_min
 * Add a helper to find the closest ONFI mode
 * bcm63xx MTD parsers: simplify CFE detection
 
 Raw NAND controller drivers changes:
 * fsl-upm: Deprecation of specific DT properties
 * fsl_upm: Driver rework and cleanup in favor of ->exec_op()
 * Ingenic: Cleanup ARRAY_SIZE() vs sizeof() use
 * brcmnand: ECC error handling on EDU transfers
 * brcmnand: Don't default to EDU transfers
 * qcom: Set BAM mode only if not set already
 * qcom: Avoid write to unavailable register
 * gpio: Driver rework in favor of ->exec_op()
 * tango: ->exec_op() conversion
 * mtk: ->exec_op() conversion
 
 Raw NAND chip drivers changes:
 * toshiba: Implement ->choose_interface_config() for TH58NVG2S3HBAI4
 * toshiba: Implement ->choose_interface_config() for TC58NVG0S3E
 * toshiba: Implement ->choose_interface_config() for TC58TEG5DCLTA00
 * hynix: Implement ->choose_interface_config() for H27UCG8T2ATR-BC
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAl8s85cACgkQJWrqGEe9
 VoTrdwgAqphDYjtYNaI+1F0YQdIy/l8ddVmc5NwhKpiitKh0APDFDrdV5CwQaENO
 9wWHJJPpT7xXPnEaq2+OWlPt2HpnEI4a+IRvySc9lr+MfkdY9ubo3B2wX7jBueKr
 WoVDAGe8Ua1FkPOeDy7Qe+H1bQPha8fvyfb7x2h8uCYrrIkDJaMilorzHVnuXfCg
 t5QJXRuznmYjUfMKOgLDjX3r79IJ+5mcrJIvMgHOyQIoG4Aao7lHIr2f01O50HWy
 JKqQD3QhPR8lJGaEW485s+H+gnnNQW8Y1+8u3bhauEg/6Ddx2oksUIBTME8HUUuK
 kAXrQ+KEs37l1a6pyaMBHaWU18T/oQ==
 =hn04
 -----END PGP SIGNATURE-----

Merge tag 'nand/for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux into mtd/next

Core changes:
* Drop useless 'depends on' in Kconfig
* Add an extra level in the Kconfig hierarchy
* Trivial spellings
* Dynamic allocation of the interface configurations
* Dropping the default ONFI timing mode
* Various cleanup (types, structures, naming, comments)
* Hide the chip->data_interface indirection
* Add the generic rb-gpios property
* Add the ->choose_interface_config() hook
* Introduce nand_choose_best_sdr_timings()
* Use default values for tPROG_max and tBERS_max
* Avoid redefining tR_max and tCCS_min
* Add a helper to find the closest ONFI mode
* bcm63xx MTD parsers: simplify CFE detection

Raw NAND controller drivers changes:
* fsl-upm: Deprecation of specific DT properties
* fsl_upm: Driver rework and cleanup in favor of ->exec_op()
* Ingenic: Cleanup ARRAY_SIZE() vs sizeof() use
* brcmnand: ECC error handling on EDU transfers
* brcmnand: Don't default to EDU transfers
* qcom: Set BAM mode only if not set already
* qcom: Avoid write to unavailable register
* gpio: Driver rework in favor of ->exec_op()
* tango: ->exec_op() conversion
* mtk: ->exec_op() conversion

Raw NAND chip drivers changes:
* toshiba: Implement ->choose_interface_config() for TH58NVG2S3HBAI4
* toshiba: Implement ->choose_interface_config() for TC58NVG0S3E
* toshiba: Implement ->choose_interface_config() for TC58TEG5DCLTA00
* hynix: Implement ->choose_interface_config() for H27UCG8T2ATR-BC
This commit is contained in:
Richard Weinberger 2020-08-07 08:54:16 +02:00
commit 6a1380271b
41 changed files with 1036 additions and 765 deletions

View File

@ -7,14 +7,16 @@ Required properties:
- fsl,upm-cmd-offset : UPM pattern offset for the command latch. - fsl,upm-cmd-offset : UPM pattern offset for the command latch.
Optional properties: Optional properties:
- fsl,upm-wait-flags : add chip-dependent short delays after running the
UPM pattern (0x1), after writing a data byte (0x2) or after
writing out a buffer (0x4).
- fsl,upm-addr-line-cs-offsets : address offsets for multi-chip support. - fsl,upm-addr-line-cs-offsets : address offsets for multi-chip support.
The corresponding address lines are used to select the chip. The corresponding address lines are used to select the chip.
- gpios : may specify optional GPIOs connected to the Ready-Not-Busy pins - gpios : may specify optional GPIOs connected to the Ready-Not-Busy pins
(R/B#). For multi-chip devices, "n" GPIO definitions are required (R/B#). For multi-chip devices, "n" GPIO definitions are required
according to the number of chips. according to the number of chips.
Deprecated properties:
- fsl,upm-wait-flags : add chip-dependent short delays after running the
UPM pattern (0x1), after writing a data byte (0x2) or after
writing out a buffer (0x4).
- chip-delay : chip dependent delay for transferring data from array to - chip-delay : chip dependent delay for transferring data from array to
read registers (tR). Required if property "gpios" is not used read registers (tR). Required if property "gpios" is not used
(R/B# pins not connected). (R/B# pins not connected).
@ -52,8 +54,6 @@ upm@3,0 {
fsl,upm-cmd-offset = <0x08>; fsl,upm-cmd-offset = <0x08>;
/* Multi-chip NAND device */ /* Multi-chip NAND device */
fsl,upm-addr-line-cs-offsets = <0x0 0x200>; fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
fsl,upm-wait-flags = <0x5>;
chip-delay = <25>; // in micro-seconds
nand@0 { nand@0 {
#address-cells = <1>; #address-cells = <1>;

View File

@ -114,6 +114,13 @@ patternProperties:
description: description:
Contains the native Ready/Busy IDs. Contains the native Ready/Busy IDs.
rb-gpios:
description:
Contains one or more GPIO descriptor (the numper of descriptor
depends on the number of R/B pins exposed by the flash) for the
Ready/Busy pins. Active state refers to the NAND ready state and
should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted.
required: required:
- reg - reg

View File

@ -1,7 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
menu "NAND"
config MTD_NAND_CORE config MTD_NAND_CORE
tristate tristate
source "drivers/mtd/nand/onenand/Kconfig" source "drivers/mtd/nand/onenand/Kconfig"
source "drivers/mtd/nand/raw/Kconfig" source "drivers/mtd/nand/raw/Kconfig"
source "drivers/mtd/nand/spi/Kconfig" source "drivers/mtd/nand/spi/Kconfig"
endmenu

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
menuconfig MTD_ONENAND menuconfig MTD_ONENAND
tristate "OneNAND Device Support" tristate "OneNAND Device Support"
depends on MTD
depends on HAS_IOMEM depends on HAS_IOMEM
help help
This enables support for accessing all type of OneNAND flash This enables support for accessing all type of OneNAND flash

View File

@ -12,7 +12,6 @@ config MTD_NAND_ECC_SW_HAMMING_SMC
menuconfig MTD_RAW_NAND menuconfig MTD_RAW_NAND
tristate "Raw/Parallel NAND Device Support" tristate "Raw/Parallel NAND Device Support"
depends on MTD
select MTD_NAND_CORE select MTD_NAND_CORE
select MTD_NAND_ECC_SW_HAMMING select MTD_NAND_ECC_SW_HAMMING
help help

View File

@ -191,8 +191,8 @@ static int gpio_nand_exec_op(struct nand_chip *this,
return ret; return ret;
} }
static int gpio_nand_setup_data_interface(struct nand_chip *this, int csline, static int gpio_nand_setup_interface(struct nand_chip *this, int csline,
const struct nand_data_interface *cf) const struct nand_interface_config *cf)
{ {
struct gpio_nand *priv = nand_get_controller_data(this); struct gpio_nand *priv = nand_get_controller_data(this);
const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf); const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf);
@ -217,7 +217,7 @@ static int gpio_nand_setup_data_interface(struct nand_chip *this, int csline,
static const struct nand_controller_ops gpio_nand_ops = { static const struct nand_controller_ops gpio_nand_ops = {
.exec_op = gpio_nand_exec_op, .exec_op = gpio_nand_exec_op,
.setup_data_interface = gpio_nand_setup_data_interface, .setup_interface = gpio_nand_setup_interface,
}; };
/* /*

View File

@ -854,8 +854,8 @@ static int anfc_exec_op(struct nand_chip *chip,
return nand_op_parser_exec_op(chip, &anfc_op_parser, op, check_only); return nand_op_parser_exec_op(chip, &anfc_op_parser, op, check_only);
} }
static int anfc_setup_data_interface(struct nand_chip *chip, int target, static int anfc_setup_interface(struct nand_chip *chip, int target,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct anand *anand = to_anand(chip); struct anand *anand = to_anand(chip);
struct arasan_nfc *nfc = to_anfc(chip->controller); struct arasan_nfc *nfc = to_anfc(chip->controller);
@ -1083,7 +1083,7 @@ static void anfc_detach_chip(struct nand_chip *chip)
static const struct nand_controller_ops anfc_ops = { static const struct nand_controller_ops anfc_ops = {
.exec_op = anfc_exec_op, .exec_op = anfc_exec_op,
.setup_data_interface = anfc_setup_data_interface, .setup_interface = anfc_setup_interface,
.attach_chip = anfc_attach_chip, .attach_chip = anfc_attach_chip,
.detach_chip = anfc_detach_chip, .detach_chip = anfc_detach_chip,
}; };

View File

@ -200,8 +200,8 @@ struct atmel_nand_controller_ops {
void (*nand_init)(struct atmel_nand_controller *nc, void (*nand_init)(struct atmel_nand_controller *nc,
struct atmel_nand *nand); struct atmel_nand *nand);
int (*ecc_init)(struct nand_chip *chip); int (*ecc_init)(struct nand_chip *chip);
int (*setup_data_interface)(struct atmel_nand *nand, int csline, int (*setup_interface)(struct atmel_nand *nand, int csline,
const struct nand_data_interface *conf); const struct nand_interface_config *conf);
}; };
struct atmel_nand_controller_caps { struct atmel_nand_controller_caps {
@ -1168,7 +1168,7 @@ static int atmel_hsmc_nand_ecc_init(struct nand_chip *chip)
} }
static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
const struct nand_data_interface *conf, const struct nand_interface_config *conf,
struct atmel_smc_cs_conf *smcconf) struct atmel_smc_cs_conf *smcconf)
{ {
u32 ncycles, totalcycles, timeps, mckperiodps; u32 ncycles, totalcycles, timeps, mckperiodps;
@ -1397,9 +1397,9 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
return 0; return 0;
} }
static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand, static int atmel_smc_nand_setup_interface(struct atmel_nand *nand,
int csline, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct atmel_nand_controller *nc; struct atmel_nand_controller *nc;
struct atmel_smc_cs_conf smcconf; struct atmel_smc_cs_conf smcconf;
@ -1422,9 +1422,9 @@ static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
return 0; return 0;
} }
static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand, static int atmel_hsmc_nand_setup_interface(struct atmel_nand *nand,
int csline, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct atmel_hsmc_nand_controller *nc; struct atmel_hsmc_nand_controller *nc;
struct atmel_smc_cs_conf smcconf; struct atmel_smc_cs_conf smcconf;
@ -1452,8 +1452,8 @@ static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
return 0; return 0;
} }
static int atmel_nand_setup_data_interface(struct nand_chip *chip, int csline, static int atmel_nand_setup_interface(struct nand_chip *chip, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct atmel_nand *nand = to_atmel_nand(chip); struct atmel_nand *nand = to_atmel_nand(chip);
struct atmel_nand_controller *nc; struct atmel_nand_controller *nc;
@ -1464,7 +1464,7 @@ static int atmel_nand_setup_data_interface(struct nand_chip *chip, int csline,
(csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY)) (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
return -EINVAL; return -EINVAL;
return nc->caps->ops->setup_data_interface(nand, csline, conf); return nc->caps->ops->setup_interface(nand, csline, conf);
} }
static void atmel_nand_init(struct atmel_nand_controller *nc, static void atmel_nand_init(struct atmel_nand_controller *nc,
@ -1483,7 +1483,7 @@ static void atmel_nand_init(struct atmel_nand_controller *nc,
chip->legacy.write_buf = atmel_nand_write_buf; chip->legacy.write_buf = atmel_nand_write_buf;
chip->legacy.select_chip = atmel_nand_select_chip; chip->legacy.select_chip = atmel_nand_select_chip;
if (!nc->mck || !nc->caps->ops->setup_data_interface) if (!nc->mck || !nc->caps->ops->setup_interface)
chip->options |= NAND_KEEP_TIMINGS; chip->options |= NAND_KEEP_TIMINGS;
/* Some NANDs require a longer delay than the default one (20us). */ /* Some NANDs require a longer delay than the default one (20us). */
@ -1956,7 +1956,7 @@ static int atmel_nand_attach_chip(struct nand_chip *chip)
static const struct nand_controller_ops atmel_nand_controller_ops = { static const struct nand_controller_ops atmel_nand_controller_ops = {
.attach_chip = atmel_nand_attach_chip, .attach_chip = atmel_nand_attach_chip,
.setup_data_interface = atmel_nand_setup_data_interface, .setup_interface = atmel_nand_setup_interface,
}; };
static int atmel_nand_controller_init(struct atmel_nand_controller *nc, static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
@ -2318,7 +2318,7 @@ static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
.remove = atmel_hsmc_nand_controller_remove, .remove = atmel_hsmc_nand_controller_remove,
.ecc_init = atmel_hsmc_nand_ecc_init, .ecc_init = atmel_hsmc_nand_ecc_init,
.nand_init = atmel_hsmc_nand_init, .nand_init = atmel_hsmc_nand_init,
.setup_data_interface = atmel_hsmc_nand_setup_data_interface, .setup_interface = atmel_hsmc_nand_setup_interface,
}; };
static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = { static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
@ -2375,10 +2375,10 @@ atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
/* /*
* The SMC reg layout of at91rm9200 is completely different which prevents us * The SMC reg layout of at91rm9200 is completely different which prevents us
* from re-using atmel_smc_nand_setup_data_interface() for the * from re-using atmel_smc_nand_setup_interface() for the
* ->setup_data_interface() hook. * ->setup_interface() hook.
* At this point, there's no support for the at91rm9200 SMC IP, so we leave * At this point, there's no support for the at91rm9200 SMC IP, so we leave
* ->setup_data_interface() unassigned. * ->setup_interface() unassigned.
*/ */
static const struct atmel_nand_controller_ops at91rm9200_nc_ops = { static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
.probe = atmel_smc_nand_controller_probe, .probe = atmel_smc_nand_controller_probe,
@ -2399,7 +2399,7 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
.remove = atmel_smc_nand_controller_remove, .remove = atmel_smc_nand_controller_remove,
.ecc_init = atmel_nand_ecc_init, .ecc_init = atmel_nand_ecc_init,
.nand_init = atmel_smc_nand_init, .nand_init = atmel_smc_nand_init,
.setup_data_interface = atmel_smc_nand_setup_data_interface, .setup_interface = atmel_smc_nand_setup_interface,
}; };
static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = { static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {

View File

@ -1918,6 +1918,22 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
edu_writel(ctrl, EDU_STOP, 0); /* force stop */ edu_writel(ctrl, EDU_STOP, 0); /* force stop */
edu_readl(ctrl, EDU_STOP); edu_readl(ctrl, EDU_STOP);
if (!ret && edu_cmd == EDU_CMD_READ) {
u64 err_addr = 0;
/*
* check for ECC errors here, subpage ECC errors are
* retained in ECC error address register
*/
err_addr = brcmnand_get_uncorrecc_addr(ctrl);
if (!err_addr) {
err_addr = brcmnand_get_correcc_addr(ctrl);
if (err_addr)
ret = -EUCLEAN;
} else
ret = -EBADMSG;
}
return ret; return ret;
} }
@ -2124,6 +2140,7 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
u64 err_addr = 0; u64 err_addr = 0;
int err; int err;
bool retry = true; bool retry = true;
bool edu_err = false;
dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
@ -2141,6 +2158,10 @@ try_dmaread:
else else
return -EIO; return -EIO;
} }
if (has_edu(ctrl) && err_addr)
edu_err = true;
} else { } else {
if (oob) if (oob)
memset(oob, 0x99, mtd->oobsize); memset(oob, 0x99, mtd->oobsize);
@ -2188,6 +2209,11 @@ try_dmaread:
if (mtd_is_bitflip(err)) { if (mtd_is_bitflip(err)) {
unsigned int corrected = brcmnand_count_corrected(ctrl); unsigned int corrected = brcmnand_count_corrected(ctrl);
/* in case of EDU correctable error we read again using PIO */
if (edu_err)
err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
oob, &err_addr);
dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
(unsigned long long)err_addr); (unsigned long long)err_addr);
mtd->ecc_stats.corrected += corrected; mtd->ecc_stats.corrected += corrected;
@ -3023,8 +3049,9 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
if (ret < 0) if (ret < 0)
goto err; goto err;
/* set edu transfer function to call */ if (has_edu(ctrl))
ctrl->dma_trans = brcmnand_edu_trans; /* set edu transfer function to call */
ctrl->dma_trans = brcmnand_edu_trans;
} }
/* Disable automatic device ID config, direct addressing */ /* Disable automatic device ID config, direct addressing */

View File

@ -2303,8 +2303,8 @@ static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min,
} }
static int static int
cadence_nand_setup_data_interface(struct nand_chip *chip, int chipnr, cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
const struct nand_sdr_timings *sdr; const struct nand_sdr_timings *sdr;
struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
@ -2690,7 +2690,7 @@ static int cadence_nand_attach_chip(struct nand_chip *chip)
static const struct nand_controller_ops cadence_nand_controller_ops = { static const struct nand_controller_ops cadence_nand_controller_ops = {
.attach_chip = cadence_nand_attach_chip, .attach_chip = cadence_nand_attach_chip,
.exec_op = cadence_nand_exec_op, .exec_op = cadence_nand_exec_op,
.setup_data_interface = cadence_nand_setup_data_interface, .setup_interface = cadence_nand_setup_interface,
}; };
static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl, static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,

View File

@ -761,8 +761,8 @@ static int denali_write_page(struct nand_chip *chip, const u8 *buf,
return denali_page_xfer(chip, (void *)buf, mtd->writesize, page, true); return denali_page_xfer(chip, (void *)buf, mtd->writesize, page, true);
} }
static int denali_setup_data_interface(struct nand_chip *chip, int chipnr, static int denali_setup_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
static const unsigned int data_setup_on_host = 10000; static const unsigned int data_setup_on_host = 10000;
struct denali_controller *denali = to_denali_controller(chip); struct denali_controller *denali = to_denali_controller(chip);
@ -1173,7 +1173,7 @@ static int denali_exec_op(struct nand_chip *chip,
static const struct nand_controller_ops denali_controller_ops = { static const struct nand_controller_ops denali_controller_ops = {
.attach_chip = denali_attach_chip, .attach_chip = denali_attach_chip,
.exec_op = denali_exec_op, .exec_op = denali_exec_op,
.setup_data_interface = denali_setup_data_interface, .setup_interface = denali_setup_interface,
}; };
int denali_chip_init(struct denali_controller *denali, int denali_chip_init(struct denali_controller *denali,
@ -1230,7 +1230,7 @@ int denali_chip_init(struct denali_controller *denali,
chip->buf_align = 16; chip->buf_align = 16;
} }
/* clk rate info is needed for setup_data_interface */ /* clk rate info is needed for setup_interface */
if (!denali->clk_rate || !denali->clk_x_rate) if (!denali->clk_rate || !denali->clk_x_rate)
chip->options |= NAND_KEEP_TIMINGS; chip->options |= NAND_KEEP_TIMINGS;

View File

@ -14,32 +14,23 @@
#include <linux/mtd/nand_ecc.h> #include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/of_address.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/fsl_lbc.h> #include <asm/fsl_lbc.h>
#define FSL_UPM_WAIT_RUN_PATTERN 0x1
#define FSL_UPM_WAIT_WRITE_BYTE 0x2
#define FSL_UPM_WAIT_WRITE_BUFFER 0x4
struct fsl_upm_nand { struct fsl_upm_nand {
struct nand_controller base;
struct device *dev; struct device *dev;
struct nand_chip chip; struct nand_chip chip;
int last_ctrl;
struct mtd_partition *parts;
struct fsl_upm upm; struct fsl_upm upm;
uint8_t upm_addr_offset; uint8_t upm_addr_offset;
uint8_t upm_cmd_offset; uint8_t upm_cmd_offset;
void __iomem *io_base; void __iomem *io_base;
int rnb_gpio[NAND_MAX_CHIPS]; struct gpio_desc *rnb_gpio[NAND_MAX_CHIPS];
uint32_t mchip_offsets[NAND_MAX_CHIPS]; uint32_t mchip_offsets[NAND_MAX_CHIPS];
uint32_t mchip_count; uint32_t mchip_count;
uint32_t mchip_number; uint32_t mchip_number;
int chip_delay;
uint32_t wait_flags;
}; };
static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo) static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
@ -48,106 +39,6 @@ static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
chip); chip);
} }
static int fun_chip_ready(struct nand_chip *chip)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
if (gpio_get_value(fun->rnb_gpio[fun->mchip_number]))
return 1;
dev_vdbg(fun->dev, "busy\n");
return 0;
}
static void fun_wait_rnb(struct fsl_upm_nand *fun)
{
if (fun->rnb_gpio[fun->mchip_number] >= 0) {
struct mtd_info *mtd = nand_to_mtd(&fun->chip);
int cnt = 1000000;
while (--cnt && !fun_chip_ready(&fun->chip))
cpu_relax();
if (!cnt)
dev_err(fun->dev, "tired waiting for RNB\n");
} else {
ndelay(100);
}
}
static void fun_cmd_ctrl(struct nand_chip *chip, int cmd, unsigned int ctrl)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
u32 mar;
if (!(ctrl & fun->last_ctrl)) {
fsl_upm_end_pattern(&fun->upm);
if (cmd == NAND_CMD_NONE)
return;
fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE);
}
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_ALE)
fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
else if (ctrl & NAND_CLE)
fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
}
mar = (cmd << (32 - fun->upm.width)) |
fun->mchip_offsets[fun->mchip_number];
fsl_upm_run_pattern(&fun->upm, chip->legacy.IO_ADDR_R, mar);
if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN)
fun_wait_rnb(fun);
}
static void fun_select_chip(struct nand_chip *chip, int mchip_nr)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
if (mchip_nr == -1) {
chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
} else if (mchip_nr >= 0 && mchip_nr < NAND_MAX_CHIPS) {
fun->mchip_number = mchip_nr;
chip->legacy.IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr];
chip->legacy.IO_ADDR_W = chip->legacy.IO_ADDR_R;
} else {
BUG();
}
}
static uint8_t fun_read_byte(struct nand_chip *chip)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
return in_8(fun->chip.legacy.IO_ADDR_R);
}
static void fun_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
int i;
for (i = 0; i < len; i++)
buf[i] = in_8(fun->chip.legacy.IO_ADDR_R);
}
static void fun_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
int i;
for (i = 0; i < len; i++) {
out_8(fun->chip.legacy.IO_ADDR_W, buf[i]);
if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE)
fun_wait_rnb(fun);
}
if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER)
fun_wait_rnb(fun);
}
static int fun_chip_init(struct fsl_upm_nand *fun, static int fun_chip_init(struct fsl_upm_nand *fun,
const struct device_node *upm_np, const struct device_node *upm_np,
const struct resource *io_res) const struct resource *io_res)
@ -156,21 +47,9 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
int ret; int ret;
struct device_node *flash_np; struct device_node *flash_np;
fun->chip.legacy.IO_ADDR_R = fun->io_base;
fun->chip.legacy.IO_ADDR_W = fun->io_base;
fun->chip.legacy.cmd_ctrl = fun_cmd_ctrl;
fun->chip.legacy.chip_delay = fun->chip_delay;
fun->chip.legacy.read_byte = fun_read_byte;
fun->chip.legacy.read_buf = fun_read_buf;
fun->chip.legacy.write_buf = fun_write_buf;
fun->chip.ecc.mode = NAND_ECC_SOFT; fun->chip.ecc.mode = NAND_ECC_SOFT;
fun->chip.ecc.algo = NAND_ECC_HAMMING; fun->chip.ecc.algo = NAND_ECC_HAMMING;
if (fun->mchip_count > 1) fun->chip.controller = &fun->base;
fun->chip.legacy.select_chip = fun_select_chip;
if (fun->rnb_gpio[0] >= 0)
fun->chip.legacy.dev_ready = fun_chip_ready;
mtd->dev.parent = fun->dev; mtd->dev.parent = fun->dev;
flash_np = of_get_next_child(upm_np, NULL); flash_np = of_get_next_child(upm_np, NULL);
@ -178,8 +57,9 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
return -ENODEV; return -ENODEV;
nand_set_flash_node(&fun->chip, flash_np); nand_set_flash_node(&fun->chip, flash_np);
mtd->name = kasprintf(GFP_KERNEL, "0x%llx.%pOFn", (u64)io_res->start, mtd->name = devm_kasprintf(fun->dev, GFP_KERNEL, "0x%llx.%pOFn",
flash_np); (u64)io_res->start,
flash_np);
if (!mtd->name) { if (!mtd->name) {
ret = -ENOMEM; ret = -ENOMEM;
goto err; goto err;
@ -192,51 +72,130 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
ret = mtd_device_register(mtd, NULL, 0); ret = mtd_device_register(mtd, NULL, 0);
err: err:
of_node_put(flash_np); of_node_put(flash_np);
if (ret)
kfree(mtd->name);
return ret; return ret;
} }
static int func_exec_instr(struct nand_chip *chip,
const struct nand_op_instr *instr)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
u32 mar, reg_offs = fun->mchip_offsets[fun->mchip_number];
unsigned int i;
const u8 *out;
u8 *in;
switch (instr->type) {
case NAND_OP_CMD_INSTR:
fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
mar = (instr->ctx.cmd.opcode << (32 - fun->upm.width)) |
reg_offs;
fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar);
fsl_upm_end_pattern(&fun->upm);
return 0;
case NAND_OP_ADDR_INSTR:
fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
for (i = 0; i < instr->ctx.addr.naddrs; i++) {
mar = (instr->ctx.addr.addrs[i] << (32 - fun->upm.width)) |
reg_offs;
fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar);
}
fsl_upm_end_pattern(&fun->upm);
return 0;
case NAND_OP_DATA_IN_INSTR:
in = instr->ctx.data.buf.in;
for (i = 0; i < instr->ctx.data.len; i++)
in[i] = in_8(fun->io_base + reg_offs);
return 0;
case NAND_OP_DATA_OUT_INSTR:
out = instr->ctx.data.buf.out;
for (i = 0; i < instr->ctx.data.len; i++)
out_8(fun->io_base + reg_offs, out[i]);
return 0;
case NAND_OP_WAITRDY_INSTR:
if (!fun->rnb_gpio[fun->mchip_number])
return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
return nand_gpio_waitrdy(chip, fun->rnb_gpio[fun->mchip_number],
instr->ctx.waitrdy.timeout_ms);
default:
return -EINVAL;
}
return 0;
}
static int fun_exec_op(struct nand_chip *chip, const struct nand_operation *op,
bool check_only)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
unsigned int i;
int ret;
if (op->cs > NAND_MAX_CHIPS)
return -EINVAL;
if (check_only)
return 0;
fun->mchip_number = op->cs;
for (i = 0; i < op->ninstrs; i++) {
ret = func_exec_instr(chip, &op->instrs[i]);
if (ret)
return ret;
if (op->instrs[i].delay_ns)
ndelay(op->instrs[i].delay_ns);
}
return 0;
}
static const struct nand_controller_ops fun_ops = {
.exec_op = fun_exec_op,
};
static int fun_probe(struct platform_device *ofdev) static int fun_probe(struct platform_device *ofdev)
{ {
struct fsl_upm_nand *fun; struct fsl_upm_nand *fun;
struct resource io_res; struct resource *io_res;
const __be32 *prop; const __be32 *prop;
int rnb_gpio;
int ret; int ret;
int size; int size;
int i; int i;
fun = kzalloc(sizeof(*fun), GFP_KERNEL); fun = devm_kzalloc(&ofdev->dev, sizeof(*fun), GFP_KERNEL);
if (!fun) if (!fun)
return -ENOMEM; return -ENOMEM;
ret = of_address_to_resource(ofdev->dev.of_node, 0, &io_res); io_res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
if (ret) { fun->io_base = devm_ioremap_resource(&ofdev->dev, io_res);
dev_err(&ofdev->dev, "can't get IO base\n"); if (IS_ERR(fun->io_base))
goto err1; return PTR_ERR(fun->io_base);
}
ret = fsl_upm_find(io_res.start, &fun->upm); ret = fsl_upm_find(io_res->start, &fun->upm);
if (ret) { if (ret) {
dev_err(&ofdev->dev, "can't find UPM\n"); dev_err(&ofdev->dev, "can't find UPM\n");
goto err1; return ret;
} }
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-addr-offset", prop = of_get_property(ofdev->dev.of_node, "fsl,upm-addr-offset",
&size); &size);
if (!prop || size != sizeof(uint32_t)) { if (!prop || size != sizeof(uint32_t)) {
dev_err(&ofdev->dev, "can't get UPM address offset\n"); dev_err(&ofdev->dev, "can't get UPM address offset\n");
ret = -EINVAL; return -EINVAL;
goto err1;
} }
fun->upm_addr_offset = *prop; fun->upm_addr_offset = *prop;
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size); prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size);
if (!prop || size != sizeof(uint32_t)) { if (!prop || size != sizeof(uint32_t)) {
dev_err(&ofdev->dev, "can't get UPM command offset\n"); dev_err(&ofdev->dev, "can't get UPM command offset\n");
ret = -EINVAL; return -EINVAL;
goto err1;
} }
fun->upm_cmd_offset = *prop; fun->upm_cmd_offset = *prop;
@ -246,7 +205,7 @@ static int fun_probe(struct platform_device *ofdev)
fun->mchip_count = size / sizeof(uint32_t); fun->mchip_count = size / sizeof(uint32_t);
if (fun->mchip_count >= NAND_MAX_CHIPS) { if (fun->mchip_count >= NAND_MAX_CHIPS) {
dev_err(&ofdev->dev, "too much multiple chips\n"); dev_err(&ofdev->dev, "too much multiple chips\n");
goto err1; return -EINVAL;
} }
for (i = 0; i < fun->mchip_count; i++) for (i = 0; i < fun->mchip_count; i++)
fun->mchip_offsets[i] = be32_to_cpu(prop[i]); fun->mchip_offsets[i] = be32_to_cpu(prop[i]);
@ -255,63 +214,26 @@ static int fun_probe(struct platform_device *ofdev)
} }
for (i = 0; i < fun->mchip_count; i++) { for (i = 0; i < fun->mchip_count; i++) {
fun->rnb_gpio[i] = -1; fun->rnb_gpio[i] = devm_gpiod_get_index_optional(&ofdev->dev,
rnb_gpio = of_get_gpio(ofdev->dev.of_node, i); NULL, i,
if (rnb_gpio >= 0) { GPIOD_IN);
ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev)); if (IS_ERR(fun->rnb_gpio[i])) {
if (ret) {
dev_err(&ofdev->dev,
"can't request RNB gpio #%d\n", i);
goto err2;
}
gpio_direction_input(rnb_gpio);
fun->rnb_gpio[i] = rnb_gpio;
} else if (rnb_gpio == -EINVAL) {
dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i); dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i);
goto err2; return PTR_ERR(fun->rnb_gpio[i]);
} }
} }
prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL); nand_controller_init(&fun->base);
if (prop) fun->base.ops = &fun_ops;
fun->chip_delay = be32_to_cpup(prop);
else
fun->chip_delay = 50;
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size);
if (prop && size == sizeof(uint32_t))
fun->wait_flags = be32_to_cpup(prop);
else
fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
FSL_UPM_WAIT_WRITE_BYTE;
fun->io_base = devm_ioremap(&ofdev->dev, io_res.start,
resource_size(&io_res));
if (!fun->io_base) {
ret = -ENOMEM;
goto err2;
}
fun->dev = &ofdev->dev; fun->dev = &ofdev->dev;
fun->last_ctrl = NAND_CLE;
ret = fun_chip_init(fun, ofdev->dev.of_node, &io_res); ret = fun_chip_init(fun, ofdev->dev.of_node, io_res);
if (ret) if (ret)
goto err2; return ret;
dev_set_drvdata(&ofdev->dev, fun); dev_set_drvdata(&ofdev->dev, fun);
return 0; return 0;
err2:
for (i = 0; i < fun->mchip_count; i++) {
if (fun->rnb_gpio[i] < 0)
break;
gpio_free(fun->rnb_gpio[i]);
}
err1:
kfree(fun);
return ret;
} }
static int fun_remove(struct platform_device *ofdev) static int fun_remove(struct platform_device *ofdev)
@ -319,20 +241,11 @@ static int fun_remove(struct platform_device *ofdev)
struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
struct nand_chip *chip = &fun->chip; struct nand_chip *chip = &fun->chip;
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
int ret, i; int ret;
ret = mtd_device_unregister(mtd); ret = mtd_device_unregister(mtd);
WARN_ON(ret); WARN_ON(ret);
nand_cleanup(chip); nand_cleanup(chip);
kfree(mtd->name);
for (i = 0; i < fun->mchip_count; i++) {
if (fun->rnb_gpio[i] < 0)
break;
gpio_free(fun->rnb_gpio[i]);
}
kfree(fun);
return 0; return 0;
} }

View File

@ -327,8 +327,8 @@ static int fsmc_calc_timings(struct fsmc_nand_data *host,
return 0; return 0;
} }
static int fsmc_setup_data_interface(struct nand_chip *nand, int csline, static int fsmc_setup_interface(struct nand_chip *nand, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct fsmc_nand_data *host = nand_to_fsmc(nand); struct fsmc_nand_data *host = nand_to_fsmc(nand);
struct fsmc_nand_timings tims; struct fsmc_nand_timings tims;
@ -951,7 +951,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand)
static const struct nand_controller_ops fsmc_nand_controller_ops = { static const struct nand_controller_ops fsmc_nand_controller_ops = {
.attach_chip = fsmc_nand_attach_chip, .attach_chip = fsmc_nand_attach_chip,
.exec_op = fsmc_exec_op, .exec_op = fsmc_exec_op,
.setup_data_interface = fsmc_setup_data_interface, .setup_interface = fsmc_setup_interface,
}; };
/** /**

View File

@ -25,8 +25,11 @@
#include <linux/mtd/nand-gpio.h> #include <linux/mtd/nand-gpio.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/delay.h>
struct gpiomtd { struct gpiomtd {
struct nand_controller base;
void __iomem *io;
void __iomem *io_sync; void __iomem *io_sync;
struct nand_chip nand_chip; struct nand_chip nand_chip;
struct gpio_nand_platdata plat; struct gpio_nand_platdata plat;
@ -69,34 +72,99 @@ static void gpio_nand_dosync(struct gpiomtd *gpiomtd)
static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {}
#endif #endif
static void gpio_nand_cmd_ctrl(struct nand_chip *chip, int cmd, static int gpio_nand_exec_instr(struct nand_chip *chip,
unsigned int ctrl) const struct nand_op_instr *instr)
{ {
struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip)); struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip));
unsigned int i;
gpio_nand_dosync(gpiomtd); switch (instr->type) {
case NAND_OP_CMD_INSTR:
if (ctrl & NAND_CTRL_CHANGE) {
if (gpiomtd->nce)
gpiod_set_value(gpiomtd->nce, !(ctrl & NAND_NCE));
gpiod_set_value(gpiomtd->cle, !!(ctrl & NAND_CLE));
gpiod_set_value(gpiomtd->ale, !!(ctrl & NAND_ALE));
gpio_nand_dosync(gpiomtd); gpio_nand_dosync(gpiomtd);
} gpiod_set_value(gpiomtd->cle, 1);
if (cmd == NAND_CMD_NONE) gpio_nand_dosync(gpiomtd);
return; writeb(instr->ctx.cmd.opcode, gpiomtd->io);
gpio_nand_dosync(gpiomtd);
gpiod_set_value(gpiomtd->cle, 0);
return 0;
writeb(cmd, gpiomtd->nand_chip.legacy.IO_ADDR_W); case NAND_OP_ADDR_INSTR:
gpio_nand_dosync(gpiomtd); gpio_nand_dosync(gpiomtd);
gpiod_set_value(gpiomtd->ale, 1);
gpio_nand_dosync(gpiomtd);
for (i = 0; i < instr->ctx.addr.naddrs; i++)
writeb(instr->ctx.addr.addrs[i], gpiomtd->io);
gpio_nand_dosync(gpiomtd);
gpiod_set_value(gpiomtd->ale, 0);
return 0;
case NAND_OP_DATA_IN_INSTR:
gpio_nand_dosync(gpiomtd);
if ((chip->options & NAND_BUSWIDTH_16) &&
!instr->ctx.data.force_8bit)
ioread16_rep(gpiomtd->io, instr->ctx.data.buf.in,
instr->ctx.data.len / 2);
else
ioread8_rep(gpiomtd->io, instr->ctx.data.buf.in,
instr->ctx.data.len);
return 0;
case NAND_OP_DATA_OUT_INSTR:
gpio_nand_dosync(gpiomtd);
if ((chip->options & NAND_BUSWIDTH_16) &&
!instr->ctx.data.force_8bit)
iowrite16_rep(gpiomtd->io, instr->ctx.data.buf.out,
instr->ctx.data.len / 2);
else
iowrite8_rep(gpiomtd->io, instr->ctx.data.buf.out,
instr->ctx.data.len);
return 0;
case NAND_OP_WAITRDY_INSTR:
if (!gpiomtd->rdy)
return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
return nand_gpio_waitrdy(chip, gpiomtd->rdy,
instr->ctx.waitrdy.timeout_ms);
default:
return -EINVAL;
}
return 0;
} }
static int gpio_nand_devready(struct nand_chip *chip) static int gpio_nand_exec_op(struct nand_chip *chip,
const struct nand_operation *op,
bool check_only)
{ {
struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip)); struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip));
unsigned int i;
int ret = 0;
return gpiod_get_value(gpiomtd->rdy); if (check_only)
return 0;
gpio_nand_dosync(gpiomtd);
gpiod_set_value(gpiomtd->nce, 0);
for (i = 0; i < op->ninstrs; i++) {
ret = gpio_nand_exec_instr(chip, &op->instrs[i]);
if (ret)
break;
if (op->instrs[i].delay_ns)
ndelay(op->instrs[i].delay_ns);
}
gpio_nand_dosync(gpiomtd);
gpiod_set_value(gpiomtd->nce, 1);
return ret;
} }
static const struct nand_controller_ops gpio_nand_ops = {
.exec_op = gpio_nand_exec_op,
};
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id gpio_nand_id_table[] = { static const struct of_device_id gpio_nand_id_table[] = {
{ .compatible = "gpio-control-nand" }, { .compatible = "gpio-control-nand" },
@ -225,9 +293,9 @@ static int gpio_nand_probe(struct platform_device *pdev)
chip = &gpiomtd->nand_chip; chip = &gpiomtd->nand_chip;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
chip->legacy.IO_ADDR_R = devm_ioremap_resource(dev, res); gpiomtd->io = devm_ioremap_resource(dev, res);
if (IS_ERR(chip->legacy.IO_ADDR_R)) if (IS_ERR(gpiomtd->io))
return PTR_ERR(chip->legacy.IO_ADDR_R); return PTR_ERR(gpiomtd->io);
res = gpio_nand_get_io_sync(pdev); res = gpio_nand_get_io_sync(pdev);
if (res) { if (res) {
@ -269,17 +337,15 @@ static int gpio_nand_probe(struct platform_device *pdev)
ret = PTR_ERR(gpiomtd->rdy); ret = PTR_ERR(gpiomtd->rdy);
goto out_ce; goto out_ce;
} }
/* Using RDY pin */
if (gpiomtd->rdy) nand_controller_init(&gpiomtd->base);
chip->legacy.dev_ready = gpio_nand_devready; gpiomtd->base.ops = &gpio_nand_ops;
nand_set_flash_node(chip, pdev->dev.of_node); nand_set_flash_node(chip, pdev->dev.of_node);
chip->legacy.IO_ADDR_W = chip->legacy.IO_ADDR_R;
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING; chip->ecc.algo = NAND_ECC_HAMMING;
chip->options = gpiomtd->plat.options; chip->options = gpiomtd->plat.options;
chip->legacy.chip_delay = gpiomtd->plat.chip_delay; chip->controller = &gpiomtd->base;
chip->legacy.cmd_ctrl = gpio_nand_cmd_ctrl;
mtd = nand_to_mtd(chip); mtd = nand_to_mtd(chip);
mtd->dev.parent = dev; mtd->dev.parent = dev;

View File

@ -736,8 +736,8 @@ static void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
udelay(dll_wait_time_us); udelay(dll_wait_time_us);
} }
static int gpmi_setup_data_interface(struct nand_chip *chip, int chipnr, static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct gpmi_nand_data *this = nand_get_controller_data(chip); struct gpmi_nand_data *this = nand_get_controller_data(chip);
const struct nand_sdr_timings *sdr; const struct nand_sdr_timings *sdr;
@ -2400,7 +2400,7 @@ unmap:
static const struct nand_controller_ops gpmi_nand_controller_ops = { static const struct nand_controller_ops gpmi_nand_controller_ops = {
.attach_chip = gpmi_nand_attach_chip, .attach_chip = gpmi_nand_attach_chip,
.setup_data_interface = gpmi_setup_data_interface, .setup_interface = gpmi_setup_interface,
.exec_op = gpmi_nfc_exec_op, .exec_op = gpmi_nfc_exec_op,
}; };

View File

@ -90,8 +90,8 @@ static int jz4740_ecc_calculate(struct ingenic_ecc *ecc,
* If the written data is completely 0xff, we also want to write 0xff as * If the written data is completely 0xff, we also want to write 0xff as
* ECC, otherwise we will get in trouble when doing subpage writes. * ECC, otherwise we will get in trouble when doing subpage writes.
*/ */
if (memcmp(ecc_code, empty_block_ecc, ARRAY_SIZE(empty_block_ecc)) == 0) if (memcmp(ecc_code, empty_block_ecc, sizeof(empty_block_ecc)) == 0)
memset(ecc_code, 0xff, ARRAY_SIZE(empty_block_ecc)); memset(ecc_code, 0xff, sizeof(empty_block_ecc));
return 0; return 0;
} }

View File

@ -53,12 +53,12 @@ struct nand_manufacturer_ops {
}; };
/** /**
* struct nand_manufacturer - NAND Flash Manufacturer structure * struct nand_manufacturer_desc - NAND Flash Manufacturer descriptor
* @name: Manufacturer name * @name: Manufacturer name
* @id: manufacturer ID code of device. * @id: manufacturer ID code of device.
* @ops: manufacturer operations * @ops: manufacturer operations
*/ */
struct nand_manufacturer { struct nand_manufacturer_desc {
int id; int id;
char *name; char *name;
const struct nand_manufacturer_ops *ops; const struct nand_manufacturer_ops *ops;
@ -79,14 +79,21 @@ extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops;
extern const struct mtd_pairing_scheme dist3_pairing_scheme; extern const struct mtd_pairing_scheme dist3_pairing_scheme;
/* Core functions */ /* Core functions */
const struct nand_manufacturer *nand_get_manufacturer(u8 id); const struct nand_manufacturer_desc *nand_get_manufacturer_desc(u8 id);
int nand_bbm_get_next_page(struct nand_chip *chip, int page); int nand_bbm_get_next_page(struct nand_chip *chip, int page);
int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs); int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs);
int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
int allowbbt); int allowbbt);
int onfi_fill_data_interface(struct nand_chip *chip, void onfi_fill_interface_config(struct nand_chip *chip,
enum nand_data_interface_type type, struct nand_interface_config *iface,
int timing_mode); enum nand_interface_type type,
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);
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,
@ -130,10 +137,10 @@ static inline int nand_exec_op(struct nand_chip *chip,
return chip->controller->ops->exec_op(chip, op, false); return chip->controller->ops->exec_op(chip, op, false);
} }
static inline bool nand_has_setup_data_iface(struct nand_chip *chip) static inline bool nand_controller_can_setup_interface(struct nand_chip *chip)
{ {
if (!chip->controller || !chip->controller->ops || if (!chip->controller || !chip->controller->ops ||
!chip->controller->ops->setup_data_interface) !chip->controller->ops->setup_interface)
return false; return false;
if (chip->options & NAND_KEEP_TIMINGS) if (chip->options & NAND_KEEP_TIMINGS)

View File

@ -1096,6 +1096,8 @@ static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip,
const u8 *oob_buf, bool raw, const u8 *oob_buf, bool raw,
int page) int page)
{ {
const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(nand_get_interface_config(chip));
struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
@ -1141,7 +1143,7 @@ static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip,
return ret; return ret;
ret = marvell_nfc_wait_op(chip, ret = marvell_nfc_wait_op(chip,
PSEC_TO_MSEC(chip->data_interface.timings.sdr.tPROG_max)); PSEC_TO_MSEC(sdr->tPROG_max));
return ret; return ret;
} }
@ -1562,6 +1564,8 @@ static int marvell_nfc_hw_ecc_bch_write_page(struct nand_chip *chip,
const u8 *buf, const u8 *buf,
int oob_required, int page) int oob_required, int page)
{ {
const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(nand_get_interface_config(chip));
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
const u8 *data = buf; const u8 *data = buf;
@ -1598,8 +1602,7 @@ static int marvell_nfc_hw_ecc_bch_write_page(struct nand_chip *chip,
marvell_nfc_wait_ndrun(chip); marvell_nfc_wait_ndrun(chip);
} }
ret = marvell_nfc_wait_op(chip, ret = marvell_nfc_wait_op(chip, PSEC_TO_MSEC(sdr->tPROG_max));
PSEC_TO_MSEC(chip->data_interface.timings.sdr.tPROG_max));
marvell_nfc_disable_hw_ecc(chip); marvell_nfc_disable_hw_ecc(chip);
@ -2305,9 +2308,8 @@ static struct nand_bbt_descr bbt_mirror_descr = {
.pattern = bbt_mirror_pattern .pattern = bbt_mirror_pattern
}; };
static int marvell_nfc_setup_data_interface(struct nand_chip *chip, int chipnr, static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface const struct nand_interface_config *conf)
*conf)
{ {
struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
@ -2508,7 +2510,7 @@ static int marvell_nand_attach_chip(struct nand_chip *chip)
static const struct nand_controller_ops marvell_nand_controller_ops = { static const struct nand_controller_ops marvell_nand_controller_ops = {
.attach_chip = marvell_nand_attach_chip, .attach_chip = marvell_nand_attach_chip,
.exec_op = marvell_nfc_exec_op, .exec_op = marvell_nfc_exec_op,
.setup_data_interface = marvell_nfc_setup_data_interface, .setup_interface = marvell_nfc_setup_interface,
}; };
static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
@ -2644,7 +2646,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
/* /*
* Save a reference value for timing registers before * Save a reference value for timing registers before
* ->setup_data_interface() is called. * ->setup_interface() is called.
*/ */
marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0); marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0);
marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1); marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1);

View File

@ -573,10 +573,10 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand, static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
int page, bool in) int page, bool in)
{ {
const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(nand_get_interface_config(nand));
struct mtd_info *mtd = nand_to_mtd(nand); struct mtd_info *mtd = nand_to_mtd(nand);
struct meson_nfc *nfc = nand_get_controller_data(nand); struct meson_nfc *nfc = nand_get_controller_data(nand);
const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&nand->data_interface);
u32 *addrs = nfc->cmdfifo.rw.addrs; u32 *addrs = nfc->cmdfifo.rw.addrs;
u32 cs = nfc->param.chip_select; u32 cs = nfc->param.chip_select;
u32 cmd0, cmd_num, row_start; u32 cmd0, cmd_num, row_start;
@ -626,9 +626,9 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
static int meson_nfc_write_page_sub(struct nand_chip *nand, static int meson_nfc_write_page_sub(struct nand_chip *nand,
int page, int raw) int page, int raw)
{ {
struct mtd_info *mtd = nand_to_mtd(nand);
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&nand->data_interface); nand_get_sdr_timings(nand_get_interface_config(nand));
struct mtd_info *mtd = nand_to_mtd(nand);
struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
struct meson_nfc *nfc = nand_get_controller_data(nand); struct meson_nfc *nfc = nand_get_controller_data(nand);
int data_len, info_len; int data_len, info_len;
@ -1097,8 +1097,8 @@ static int meson_chip_buffer_init(struct nand_chip *nand)
} }
static static
int meson_nfc_setup_data_interface(struct nand_chip *nand, int csline, int meson_nfc_setup_interface(struct nand_chip *nand, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
const struct nand_sdr_timings *timings; const struct nand_sdr_timings *timings;
@ -1222,7 +1222,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
static const struct nand_controller_ops meson_nand_controller_ops = { static const struct nand_controller_ops meson_nand_controller_ops = {
.attach_chip = meson_nand_attach_chip, .attach_chip = meson_nand_attach_chip,
.detach_chip = meson_nand_detach_chip, .detach_chip = meson_nand_detach_chip,
.setup_data_interface = meson_nfc_setup_data_interface, .setup_interface = meson_nfc_setup_interface,
.exec_op = meson_nfc_exec_op, .exec_op = meson_nfc_exec_op,
}; };

View File

@ -387,44 +387,6 @@ static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
return 0; return 0;
} }
static void mtk_nfc_select_chip(struct nand_chip *nand, int chip)
{
struct mtk_nfc *nfc = nand_get_controller_data(nand);
struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand);
if (chip < 0)
return;
mtk_nfc_hw_runtime_config(nand_to_mtd(nand));
nfi_writel(nfc, mtk_nand->sels[chip], NFI_CSEL);
}
static int mtk_nfc_dev_ready(struct nand_chip *nand)
{
struct mtk_nfc *nfc = nand_get_controller_data(nand);
if (nfi_readl(nfc, NFI_STA) & STA_BUSY)
return 0;
return 1;
}
static void mtk_nfc_cmd_ctrl(struct nand_chip *chip, int dat,
unsigned int ctrl)
{
struct mtk_nfc *nfc = nand_get_controller_data(chip);
if (ctrl & NAND_ALE) {
mtk_nfc_send_address(nfc, dat);
} else if (ctrl & NAND_CLE) {
mtk_nfc_hw_reset(nfc);
nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG);
mtk_nfc_send_command(nfc, dat);
}
}
static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc) static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc)
{ {
int rc; int rc;
@ -501,8 +463,76 @@ static void mtk_nfc_write_buf(struct nand_chip *chip, const u8 *buf, int len)
mtk_nfc_write_byte(chip, buf[i]); mtk_nfc_write_byte(chip, buf[i]);
} }
static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline, static int mtk_nfc_exec_instr(struct nand_chip *chip,
const struct nand_data_interface *conf) const struct nand_op_instr *instr)
{
struct mtk_nfc *nfc = nand_get_controller_data(chip);
unsigned int i;
u32 status;
switch (instr->type) {
case NAND_OP_CMD_INSTR:
mtk_nfc_send_command(nfc, instr->ctx.cmd.opcode);
return 0;
case NAND_OP_ADDR_INSTR:
for (i = 0; i < instr->ctx.addr.naddrs; i++)
mtk_nfc_send_address(nfc, instr->ctx.addr.addrs[i]);
return 0;
case NAND_OP_DATA_IN_INSTR:
mtk_nfc_read_buf(chip, instr->ctx.data.buf.in,
instr->ctx.data.len);
return 0;
case NAND_OP_DATA_OUT_INSTR:
mtk_nfc_write_buf(chip, instr->ctx.data.buf.out,
instr->ctx.data.len);
return 0;
case NAND_OP_WAITRDY_INSTR:
return readl_poll_timeout(nfc->regs + NFI_STA, status,
status & STA_BUSY, 20,
instr->ctx.waitrdy.timeout_ms);
default:
break;
}
return -EINVAL;
}
static void mtk_nfc_select_target(struct nand_chip *nand, unsigned int cs)
{
struct mtk_nfc *nfc = nand_get_controller_data(nand);
struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand);
mtk_nfc_hw_runtime_config(nand_to_mtd(nand));
nfi_writel(nfc, mtk_nand->sels[cs], NFI_CSEL);
}
static int mtk_nfc_exec_op(struct nand_chip *chip,
const struct nand_operation *op,
bool check_only)
{
struct mtk_nfc *nfc = nand_get_controller_data(chip);
unsigned int i;
int ret = 0;
if (check_only)
return 0;
mtk_nfc_hw_reset(nfc);
nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG);
mtk_nfc_select_target(chip, op->cs);
for (i = 0; i < op->ninstrs; i++) {
ret = mtk_nfc_exec_instr(chip, &op->instrs[i]);
if (ret)
break;
}
return ret;
}
static int mtk_nfc_setup_interface(struct nand_chip *chip, int csline,
const struct nand_interface_config *conf)
{ {
struct mtk_nfc *nfc = nand_get_controller_data(chip); struct mtk_nfc *nfc = nand_get_controller_data(chip);
const struct nand_sdr_timings *timings; const struct nand_sdr_timings *timings;
@ -803,6 +833,7 @@ static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
u32 reg; u32 reg;
int ret; int ret;
mtk_nfc_select_target(chip, chip->cur_cs);
nand_prog_page_begin_op(chip, page, 0, NULL, 0); nand_prog_page_begin_op(chip, page, 0, NULL, 0);
if (!raw) { if (!raw) {
@ -920,6 +951,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
u8 *buf; u8 *buf;
int rc; int rc;
mtk_nfc_select_target(chip, chip->cur_cs);
start = data_offs / chip->ecc.size; start = data_offs / chip->ecc.size;
end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size);
@ -1325,7 +1357,8 @@ static int mtk_nfc_attach_chip(struct nand_chip *chip)
static const struct nand_controller_ops mtk_nfc_controller_ops = { static const struct nand_controller_ops mtk_nfc_controller_ops = {
.attach_chip = mtk_nfc_attach_chip, .attach_chip = mtk_nfc_attach_chip,
.setup_data_interface = mtk_nfc_setup_data_interface, .setup_interface = mtk_nfc_setup_interface,
.exec_op = mtk_nfc_exec_op,
}; };
static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
@ -1381,13 +1414,6 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
nand_set_controller_data(nand, nfc); nand_set_controller_data(nand, nfc);
nand->options |= NAND_USES_DMA | NAND_SUBPAGE_READ; nand->options |= NAND_USES_DMA | NAND_SUBPAGE_READ;
nand->legacy.dev_ready = mtk_nfc_dev_ready;
nand->legacy.select_chip = mtk_nfc_select_chip;
nand->legacy.write_byte = mtk_nfc_write_byte;
nand->legacy.write_buf = mtk_nfc_write_buf;
nand->legacy.read_byte = mtk_nfc_read_byte;
nand->legacy.read_buf = mtk_nfc_read_buf;
nand->legacy.cmd_ctrl = mtk_nfc_cmd_ctrl;
/* set default mode in case dt entry is missing */ /* set default mode in case dt entry is missing */
nand->ecc.mode = NAND_ECC_HW; nand->ecc.mode = NAND_ECC_HW;

View File

@ -137,8 +137,8 @@ struct mxc_nand_devtype_data {
u32 (*get_ecc_status)(struct mxc_nand_host *); u32 (*get_ecc_status)(struct mxc_nand_host *);
const struct mtd_ooblayout_ops *ooblayout; const struct mtd_ooblayout_ops *ooblayout;
void (*select_chip)(struct nand_chip *chip, int cs); void (*select_chip)(struct nand_chip *chip, int cs);
int (*setup_data_interface)(struct nand_chip *chip, int csline, int (*setup_interface)(struct nand_chip *chip, int csline,
const struct nand_data_interface *conf); const struct nand_interface_config *conf);
void (*enable_hwecc)(struct nand_chip *chip, bool enable); void (*enable_hwecc)(struct nand_chip *chip, bool enable);
/* /*
@ -1139,8 +1139,8 @@ static void preset_v1(struct mtd_info *mtd)
writew(0x4, NFC_V1_V2_WRPROT); writew(0x4, NFC_V1_V2_WRPROT);
} }
static int mxc_nand_v2_setup_data_interface(struct nand_chip *chip, int csline, static int mxc_nand_v2_setup_interface(struct nand_chip *chip, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct mxc_nand_host *host = nand_get_controller_data(chip); struct mxc_nand_host *host = nand_get_controller_data(chip);
int tRC_min_ns, tRC_ps, ret; int tRC_min_ns, tRC_ps, ret;
@ -1432,7 +1432,7 @@ static int mxc_nand_get_features(struct nand_chip *chip, int addr,
} }
/* /*
* The generic flash bbt decriptors overlap with our ecc * The generic flash bbt descriptors overlap with our ecc
* hardware, so define some i.MX specific ones. * hardware, so define some i.MX specific ones.
*/ */
static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
@ -1521,7 +1521,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
.get_ecc_status = get_ecc_status_v2, .get_ecc_status = get_ecc_status_v2,
.ooblayout = &mxc_v2_ooblayout_ops, .ooblayout = &mxc_v2_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v2, .select_chip = mxc_nand_select_chip_v2,
.setup_data_interface = mxc_nand_v2_setup_data_interface, .setup_interface = mxc_nand_v2_setup_interface,
.enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
.irqpending_quirk = 0, .irqpending_quirk = 0,
.needs_ip = 0, .needs_ip = 0,
@ -1738,17 +1738,17 @@ static int mxcnd_attach_chip(struct nand_chip *chip)
return 0; return 0;
} }
static int mxcnd_setup_data_interface(struct nand_chip *chip, int chipnr, static int mxcnd_setup_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct mxc_nand_host *host = nand_get_controller_data(chip); struct mxc_nand_host *host = nand_get_controller_data(chip);
return host->devtype_data->setup_data_interface(chip, chipnr, conf); return host->devtype_data->setup_interface(chip, chipnr, conf);
} }
static const struct nand_controller_ops mxcnd_controller_ops = { static const struct nand_controller_ops mxcnd_controller_ops = {
.attach_chip = mxcnd_attach_chip, .attach_chip = mxcnd_attach_chip,
.setup_data_interface = mxcnd_setup_data_interface, .setup_interface = mxcnd_setup_interface,
}; };
static int mxcnd_probe(struct platform_device *pdev) static int mxcnd_probe(struct platform_device *pdev)
@ -1809,7 +1809,7 @@ static int mxcnd_probe(struct platform_device *pdev)
if (err < 0) if (err < 0)
return err; return err;
if (!host->devtype_data->setup_data_interface) if (!host->devtype_data->setup_interface)
this->options |= NAND_KEEP_TIMINGS; this->options |= NAND_KEEP_TIMINGS;
if (host->devtype_data->needs_ip) { if (host->devtype_data->needs_ip) {

View File

@ -451,8 +451,8 @@ static int mxic_nfc_exec_op(struct nand_chip *chip,
return ret; return ret;
} }
static int mxic_nfc_setup_data_interface(struct nand_chip *chip, int chipnr, static int mxic_nfc_setup_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip); struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip);
const struct nand_sdr_timings *sdr; const struct nand_sdr_timings *sdr;
@ -480,7 +480,7 @@ static int mxic_nfc_setup_data_interface(struct nand_chip *chip, int chipnr,
static const struct nand_controller_ops mxic_nand_controller_ops = { static const struct nand_controller_ops mxic_nand_controller_ops = {
.exec_op = mxic_nfc_exec_op, .exec_op = mxic_nfc_exec_op,
.setup_data_interface = mxic_nfc_setup_data_interface, .setup_interface = mxic_nfc_setup_interface,
}; };
static int mxic_nfc_probe(struct platform_device *pdev) static int mxic_nfc_probe(struct platform_device *pdev)

View File

@ -773,7 +773,7 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
return -ENOTSUPP; return -ENOTSUPP;
/* Wait tWB before polling the STATUS reg. */ /* Wait tWB before polling the STATUS reg. */
timings = nand_get_sdr_timings(&chip->data_interface); timings = nand_get_sdr_timings(nand_get_interface_config(chip));
ndelay(PSEC_TO_NSEC(timings->tWB_max)); ndelay(PSEC_TO_NSEC(timings->tWB_max));
ret = nand_status_op(chip, NULL); ret = nand_status_op(chip, NULL);
@ -898,7 +898,7 @@ static bool nand_supports_set_features(struct nand_chip *chip, int addr)
} }
/** /**
* nand_reset_data_interface - Reset data interface and timings * nand_reset_interface - Reset data interface and timings
* @chip: The NAND chip * @chip: The NAND chip
* @chipnr: Internal die id * @chipnr: Internal die id
* *
@ -906,11 +906,12 @@ static bool nand_supports_set_features(struct nand_chip *chip, int addr)
* *
* Returns 0 for success or negative error code otherwise. * Returns 0 for success or negative error code otherwise.
*/ */
static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) static int nand_reset_interface(struct nand_chip *chip, int chipnr)
{ {
const struct nand_controller_ops *ops = chip->controller->ops;
int ret; int ret;
if (!nand_has_setup_data_iface(chip)) if (!nand_controller_can_setup_interface(chip))
return 0; return 0;
/* /*
@ -927,9 +928,9 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
* timings to timing mode 0. * timings to timing mode 0.
*/ */
onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); chip->current_interface_config = nand_get_reset_interface_config();
ret = chip->controller->ops->setup_data_interface(chip, chipnr, ret = ops->setup_interface(chip, chipnr,
&chip->data_interface); 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");
@ -937,28 +938,36 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
} }
/** /**
* nand_setup_data_interface - Setup the best data interface and timings * nand_setup_interface - Setup the best data interface and timings
* @chip: The NAND chip * @chip: The NAND chip
* @chipnr: Internal die id * @chipnr: Internal die id
* *
* Find and configure the best data interface and NAND timings supported by * Configure what has been reported to be the best data interface and NAND
* the chip and the driver. * 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. * Returns 0 for success or negative error code otherwise.
*/ */
static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) static int nand_setup_interface(struct nand_chip *chip, int chipnr)
{ {
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { const struct nand_controller_ops *ops = chip->controller->ops;
chip->onfi_timing_mode_default, u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
};
int ret; int ret;
if (!nand_has_setup_data_iface(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);
@ -970,14 +979,13 @@ static int nand_setup_data_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_data_interface(chip, chipnr, ret = ops->setup_interface(chip, chipnr, chip->best_interface_config);
&chip->data_interface);
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);
@ -987,12 +995,15 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
if (ret) if (ret)
goto err_reset_chip; goto err_reset_chip;
if (tmode_param[0] != chip->onfi_timing_mode_default) { 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",
chip->onfi_timing_mode_default); 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:
@ -1000,7 +1011,7 @@ err_reset_chip:
* Fallback to mode 0 if the chip explicitly did not ack the chosen * Fallback to mode 0 if the chip explicitly did not ack the chosen
* timing mode. * timing mode.
*/ */
nand_reset_data_interface(chip, chipnr); nand_reset_interface(chip, chipnr);
nand_select_target(chip, chipnr); nand_select_target(chip, chipnr);
nand_reset_op(chip); nand_reset_op(chip);
nand_deselect_target(chip); nand_deselect_target(chip);
@ -1009,59 +1020,90 @@ err_reset_chip:
} }
/** /**
* nand_init_data_interface - find the best data interface and timings * 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, retrieve supported
* timing modes from ONFI information.
*/
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) {
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->async_timing_mode) - 1;
}
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)
break;
}
chip->best_interface_config = iface;
return 0;
}
/**
* nand_choose_interface_config - find the best data interface and timings
* @chip: The NAND chip * @chip: The NAND chip
* *
* Find the best data interface and NAND timings supported by the chip * Find the best data interface and NAND timings supported by the chip
* and the driver. * and the driver. Eventually let the NAND manufacturer driver propose his own
* First tries to retrieve supported timing modes from ONFI information, * set of timings.
* and if the NAND chip does not support ONFI, relies on the *
* ->onfi_timing_mode_default specified in the nand_ids table. After this * After this function nand_chip->interface_config is initialized with the best
* function nand_chip->data_interface is initialized with the best timing mode * timing mode available.
* available.
* *
* Returns 0 for success or negative error code otherwise. * Returns 0 for success or negative error code otherwise.
*/ */
static int nand_init_data_interface(struct nand_chip *chip) static int nand_choose_interface_config(struct nand_chip *chip)
{ {
int modes, mode, ret; struct nand_interface_config *iface;
int ret;
if (!nand_has_setup_data_iface(chip)) if (!nand_controller_can_setup_interface(chip))
return 0; return 0;
/* iface = kzalloc(sizeof(*iface), GFP_KERNEL);
* First try to identify the best timings from ONFI parameters and if (!iface)
* if the NAND does not support ONFI, fallback to the default ONFI return -ENOMEM;
* 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); if (chip->ops.choose_interface_config)
} ret = chip->ops.choose_interface_config(chip, iface);
else
ret = nand_choose_best_sdr_timings(chip, iface, NULL);
for (mode = fls(modes) - 1; mode >= 0; mode--) { if (ret)
ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode); kfree(iface);
if (ret)
continue;
/* return ret;
* Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the
* controller supports the requested timings.
*/
ret = chip->controller->ops->setup_data_interface(chip,
NAND_DATA_IFACE_CHECK_ONLY,
&chip->data_interface);
if (!ret) {
chip->onfi_timing_mode_default = mode;
break;
}
}
return 0;
} }
/** /**
@ -1122,9 +1164,9 @@ static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
unsigned int offset_in_page, void *buf, unsigned int offset_in_page, void *buf,
unsigned int len) unsigned int len)
{ {
struct mtd_info *mtd = nand_to_mtd(chip);
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct mtd_info *mtd = nand_to_mtd(chip);
u8 addrs[4]; u8 addrs[4];
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_READ0, 0), NAND_OP_CMD(NAND_CMD_READ0, 0),
@ -1166,7 +1208,7 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
unsigned int len) unsigned int len)
{ {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
u8 addrs[5]; u8 addrs[5];
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_READ0, 0), NAND_OP_CMD(NAND_CMD_READ0, 0),
@ -1263,7 +1305,7 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_PARAM, 0), NAND_OP_CMD(NAND_CMD_PARAM, 0),
NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)),
@ -1318,7 +1360,7 @@ int nand_change_read_column_op(struct nand_chip *chip,
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
u8 addrs[2] = {}; u8 addrs[2] = {};
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_RNDOUT, 0), NAND_OP_CMD(NAND_CMD_RNDOUT, 0),
@ -1392,9 +1434,9 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
unsigned int offset_in_page, const void *buf, unsigned int offset_in_page, const void *buf,
unsigned int len, bool prog) unsigned int len, bool prog)
{ {
struct mtd_info *mtd = nand_to_mtd(chip);
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct mtd_info *mtd = nand_to_mtd(chip);
u8 addrs[5] = {}; u8 addrs[5] = {};
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
/* /*
@ -1517,7 +1559,7 @@ int nand_prog_page_end_op(struct nand_chip *chip)
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_PAGEPROG, NAND_OP_CMD(NAND_CMD_PAGEPROG,
PSEC_TO_NSEC(sdr->tWB_max)), PSEC_TO_NSEC(sdr->tWB_max)),
@ -1624,7 +1666,7 @@ int nand_change_write_column_op(struct nand_chip *chip,
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
u8 addrs[2]; u8 addrs[2];
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_RNDIN, 0), NAND_OP_CMD(NAND_CMD_RNDIN, 0),
@ -1679,7 +1721,7 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_READID, 0), NAND_OP_CMD(NAND_CMD_READID, 0),
NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)), NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)),
@ -1718,7 +1760,7 @@ int nand_status_op(struct nand_chip *chip, u8 *status)
{ {
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_STATUS, NAND_OP_CMD(NAND_CMD_STATUS,
PSEC_TO_NSEC(sdr->tADL_min)), PSEC_TO_NSEC(sdr->tADL_min)),
@ -1787,7 +1829,7 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
u8 addrs[3] = { page, page >> 8, page >> 16 }; u8 addrs[3] = { page, page >> 8, page >> 16 };
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_ERASE1, 0), NAND_OP_CMD(NAND_CMD_ERASE1, 0),
@ -1846,7 +1888,7 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0), NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0),
NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)), NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)),
@ -1893,7 +1935,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature,
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0), NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0),
NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)),
@ -1950,7 +1992,7 @@ int nand_reset_op(struct nand_chip *chip)
{ {
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)),
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0),
@ -2480,17 +2522,16 @@ EXPORT_SYMBOL_GPL(nand_subop_get_data_len);
* @chipnr: Internal die id * @chipnr: Internal die id
* *
* Save the timings data structure, then apply SDR timings mode 0 (see * Save the timings data structure, then apply SDR timings mode 0 (see
* nand_reset_data_interface for details), do the reset operation, and * nand_reset_interface for details), do the reset operation, and apply
* apply back the previous timings. * back the previous timings.
* *
* Returns 0 on success, a negative error code otherwise. * Returns 0 on success, a negative error code otherwise.
*/ */
int nand_reset(struct nand_chip *chip, int chipnr) int nand_reset(struct nand_chip *chip, int chipnr)
{ {
struct nand_data_interface saved_data_intf = chip->data_interface;
int ret; int ret;
ret = nand_reset_data_interface(chip, chipnr); ret = nand_reset_interface(chip, chipnr);
if (ret) if (ret)
return ret; return ret;
@ -2505,18 +2546,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
if (ret) if (ret)
return ret; return ret;
/* ret = nand_setup_interface(chip, chipnr);
* A nand_reset_data_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_data_interface() uses ->set/get_features() which would
* fail anyway as the parameter page is not available yet.
*/
if (!chip->onfi_timing_mode_default)
return 0;
chip->data_interface = saved_data_intf;
ret = nand_setup_data_interface(chip, chipnr);
if (ret) if (ret)
return ret; return ret;
@ -3215,10 +3245,10 @@ static int nand_setup_read_retry(struct nand_chip *chip, int retry_mode)
if (retry_mode >= chip->read_retries) if (retry_mode >= chip->read_retries)
return -EINVAL; return -EINVAL;
if (!chip->setup_read_retry) if (!chip->ops.setup_read_retry)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return chip->setup_read_retry(chip, retry_mode); return chip->ops.setup_read_retry(chip, retry_mode);
} }
static void nand_wait_readrdy(struct nand_chip *chip) static void nand_wait_readrdy(struct nand_chip *chip)
@ -3228,7 +3258,7 @@ static void nand_wait_readrdy(struct nand_chip *chip)
if (!(chip->options & NAND_NEED_READRDY)) if (!(chip->options & NAND_NEED_READRDY))
return; return;
sdr = nand_get_sdr_timings(&chip->data_interface); sdr = nand_get_sdr_timings(nand_get_interface_config(chip));
WARN_ON(nand_wait_rdy_op(chip, PSEC_TO_MSEC(sdr->tR_max), 0)); WARN_ON(nand_wait_rdy_op(chip, PSEC_TO_MSEC(sdr->tR_max), 0));
} }
@ -4462,8 +4492,8 @@ static int nand_suspend(struct mtd_info *mtd)
int ret = 0; int ret = 0;
mutex_lock(&chip->lock); mutex_lock(&chip->lock);
if (chip->suspend) if (chip->ops.suspend)
ret = chip->suspend(chip); ret = chip->ops.suspend(chip);
if (!ret) if (!ret)
chip->suspended = 1; chip->suspended = 1;
mutex_unlock(&chip->lock); mutex_unlock(&chip->lock);
@ -4481,8 +4511,8 @@ static void nand_resume(struct mtd_info *mtd)
mutex_lock(&chip->lock); mutex_lock(&chip->lock);
if (chip->suspended) { if (chip->suspended) {
if (chip->resume) if (chip->ops.resume)
chip->resume(chip); chip->ops.resume(chip);
chip->suspended = 0; chip->suspended = 0;
} else { } else {
pr_err("%s called for a chip which is not in suspended state\n", pr_err("%s called for a chip which is not in suspended state\n",
@ -4511,10 +4541,10 @@ static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{ {
struct nand_chip *chip = mtd_to_nand(mtd); struct nand_chip *chip = mtd_to_nand(mtd);
if (!chip->lock_area) if (!chip->ops.lock_area)
return -ENOTSUPP; return -ENOTSUPP;
return chip->lock_area(chip, ofs, len); return chip->ops.lock_area(chip, ofs, len);
} }
/** /**
@ -4527,10 +4557,10 @@ static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{ {
struct nand_chip *chip = mtd_to_nand(mtd); struct nand_chip *chip = mtd_to_nand(mtd);
if (!chip->unlock_area) if (!chip->ops.unlock_area)
return -ENOTSUPP; return -ENOTSUPP;
return chip->unlock_area(chip, ofs, len); return chip->ops.unlock_area(chip, ofs, len);
} }
/* Set default functions */ /* Set default functions */
@ -4743,8 +4773,6 @@ static bool find_full_id_nand(struct nand_chip *chip,
chip->options |= type->options; chip->options |= type->options;
chip->base.eccreq.strength = NAND_ECC_STRENGTH(type); chip->base.eccreq.strength = NAND_ECC_STRENGTH(type);
chip->base.eccreq.step_size = NAND_ECC_STEP(type); chip->base.eccreq.step_size = NAND_ECC_STEP(type);
chip->onfi_timing_mode_default =
type->onfi_timing_mode_default;
chip->parameters.model = kstrdup(type->name, GFP_KERNEL); chip->parameters.model = kstrdup(type->name, GFP_KERNEL);
if (!chip->parameters.model) if (!chip->parameters.model)
@ -4810,9 +4838,9 @@ static void nand_manufacturer_cleanup(struct nand_chip *chip)
} }
static const char * static const char *
nand_manufacturer_name(const struct nand_manufacturer *manufacturer) nand_manufacturer_name(const struct nand_manufacturer_desc *manufacturer_desc)
{ {
return manufacturer ? manufacturer->name : "Unknown"; return manufacturer_desc ? manufacturer_desc->name : "Unknown";
} }
/* /*
@ -4820,7 +4848,7 @@ nand_manufacturer_name(const struct nand_manufacturer *manufacturer)
*/ */
static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
{ {
const struct nand_manufacturer *manufacturer; const struct nand_manufacturer_desc *manufacturer_desc;
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_memory_organization *memorg; struct nand_memory_organization *memorg;
int busw, ret; int busw, ret;
@ -4877,8 +4905,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data)); chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data));
/* Try to identify manufacturer */ /* Try to identify manufacturer */
manufacturer = nand_get_manufacturer(maf_id); manufacturer_desc = nand_get_manufacturer_desc(maf_id);
chip->manufacturer.desc = manufacturer; chip->manufacturer.desc = manufacturer_desc;
if (!type) if (!type)
type = nand_flash_ids; type = nand_flash_ids;
@ -4957,7 +4985,7 @@ ident_done:
*/ */
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
maf_id, dev_id); maf_id, dev_id);
pr_info("%s %s\n", nand_manufacturer_name(manufacturer), pr_info("%s %s\n", nand_manufacturer_name(manufacturer_desc),
mtd->name); mtd->name);
pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8, pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8,
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8); (chip->options & NAND_BUSWIDTH_16) ? 16 : 8);
@ -4992,7 +5020,7 @@ ident_done:
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
maf_id, dev_id); maf_id, dev_id);
pr_info("%s %s\n", nand_manufacturer_name(manufacturer), pr_info("%s %s\n", nand_manufacturer_name(manufacturer_desc),
chip->parameters.model); chip->parameters.model);
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
(int)(targetsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", (int)(targetsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
@ -5185,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_data_interface(chip, 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)
@ -5972,16 +6000,16 @@ static int nand_scan_tail(struct nand_chip *chip)
if (!mtd->bitflip_threshold) if (!mtd->bitflip_threshold)
mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
/* Initialize the ->data_interface field. */ /* Find the fastest data interface for this chip */
ret = nand_init_data_interface(chip); ret = nand_choose_interface_config(chip);
if (ret) if (ret)
goto err_nanddev_cleanup; goto err_nanddev_cleanup;
/* Enter fastest possible mode on all dies. */ /* Enter fastest possible mode on all dies. */
for (i = 0; i < nanddev_ntargets(&chip->base); i++) { for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
ret = nand_setup_data_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 */
@ -5991,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);
@ -6088,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);

View File

@ -1226,7 +1226,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
return -ENOMEM; return -ENOMEM;
/* /*
* If no primary table decriptor is given, scan the device to build a * If no primary table descriptor is given, scan the device to build a
* memory based bad block table. * memory based bad block table.
*/ */
if (!td) { if (!td) {

View File

@ -337,7 +337,7 @@ static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip,
rr->nregs = nregs; rr->nregs = nregs;
rr->regs = hynix_1xnm_mlc_read_retry_regs; rr->regs = hynix_1xnm_mlc_read_retry_regs;
hynix->read_retry = rr; hynix->read_retry = rr;
chip->setup_read_retry = hynix_nand_setup_read_retry; chip->ops.setup_read_retry = hynix_nand_setup_read_retry;
chip->read_retries = nmodes; chip->read_retries = nmodes;
out: out:
@ -673,6 +673,15 @@ static void hynix_nand_cleanup(struct nand_chip *chip)
nand_set_manufacturer_data(chip, NULL); nand_set_manufacturer_data(chip, NULL);
} }
static int
h27ucg8t2atrbc_choose_interface_config(struct nand_chip *chip,
struct nand_interface_config *iface)
{
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 4);
return nand_choose_best_sdr_timings(chip, iface, NULL);
}
static int hynix_nand_init(struct nand_chip *chip) static int hynix_nand_init(struct nand_chip *chip)
{ {
struct hynix_nand *hynix; struct hynix_nand *hynix;
@ -689,6 +698,11 @@ static int hynix_nand_init(struct nand_chip *chip)
nand_set_manufacturer_data(chip, hynix); nand_set_manufacturer_data(chip, hynix);
if (!strncmp("H27UCG8T2ATR-BC", chip->parameters.model,
sizeof("H27UCG8T2ATR-BC") - 1))
chip->ops.choose_interface_config =
h27ucg8t2atrbc_choose_interface_config;
ret = hynix_nand_rr_init(chip); ret = hynix_nand_rr_init(chip);
if (ret) if (ret)
hynix_nand_cleanup(chip); hynix_nand_cleanup(chip);

View File

@ -28,8 +28,7 @@ struct nand_flash_dev nand_flash_ids[] = {
*/ */
{"TC58NVG0S3E 1G 3.3V 8-bit", {"TC58NVG0S3E 1G 3.3V 8-bit",
{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
2 },
{"TC58NVG2S0F 4G 3.3V 8-bit", {"TC58NVG2S0F 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
@ -51,7 +50,10 @@ struct nand_flash_dev nand_flash_ids[] = {
{"H27UCG8T2ATR-BC 64G 3.3V 8-bit", {"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
NAND_ECC_INFO(40, SZ_1K), 4 }, NAND_ECC_INFO(40, SZ_1K) },
{"TH58NVG2S3HBAI4 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x91, 0x15, 0x76} },
SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
@ -166,7 +168,7 @@ struct nand_flash_dev nand_flash_ids[] = {
}; };
/* Manufacturer IDs */ /* Manufacturer IDs */
static const struct nand_manufacturer nand_manufacturers[] = { static const struct nand_manufacturer_desc nand_manufacturer_descs[] = {
{NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops}, {NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops},
{NAND_MFR_ATO, "ATO"}, {NAND_MFR_ATO, "ATO"},
{NAND_MFR_EON, "Eon"}, {NAND_MFR_EON, "Eon"},
@ -186,20 +188,20 @@ static const struct nand_manufacturer nand_manufacturers[] = {
}; };
/** /**
* nand_get_manufacturer - Get manufacturer information from the manufacturer * nand_get_manufacturer_desc - Get manufacturer information from the
* ID * manufacturer ID
* @id: manufacturer ID * @id: manufacturer ID
* *
* Returns a pointer a nand_manufacturer object if the manufacturer is defined * Returns a nand_manufacturer_desc object if the manufacturer is defined
* in the NAND manufacturers database, NULL otherwise. * in the NAND manufacturers database, NULL otherwise.
*/ */
const struct nand_manufacturer *nand_get_manufacturer(u8 id) const struct nand_manufacturer_desc *nand_get_manufacturer_desc(u8 id)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(nand_manufacturers); i++) for (i = 0; i < ARRAY_SIZE(nand_manufacturer_descs); i++)
if (nand_manufacturers[i].id == id) if (nand_manufacturer_descs[i].id == id)
return &nand_manufacturers[i]; return &nand_manufacturer_descs[i];
return NULL; return NULL;
} }

View File

@ -354,6 +354,9 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
static void nand_ccs_delay(struct nand_chip *chip) static void nand_ccs_delay(struct nand_chip *chip)
{ {
const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(nand_get_interface_config(chip));
/* /*
* The controller already takes care of waiting for tCCS when the RNDIN * The controller already takes care of waiting for tCCS when the RNDIN
* or RNDOUT command is sent, return directly. * or RNDOUT command is sent, return directly.
@ -365,8 +368,8 @@ static void nand_ccs_delay(struct nand_chip *chip)
* Wait tCCS_min if it is correctly defined, otherwise wait 500ns * Wait tCCS_min if it is correctly defined, otherwise wait 500ns
* (which should be safe for all NANDs). * (which should be safe for all NANDs).
*/ */
if (nand_has_setup_data_iface(chip)) if (nand_controller_can_setup_interface(chip))
ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000); ndelay(sdr->tCCS_min / 1000);
else else
ndelay(500); ndelay(500);
} }

View File

@ -130,7 +130,7 @@ static void macronix_nand_onfi_init(struct nand_chip *chip)
return; return;
chip->read_retries = MACRONIX_NUM_READ_RETRY_MODES; chip->read_retries = MACRONIX_NUM_READ_RETRY_MODES;
chip->setup_read_retry = macronix_nand_setup_read_retry; chip->ops.setup_read_retry = macronix_nand_setup_read_retry;
if (p->supports_set_get_features) { if (p->supports_set_get_features) {
bitmap_set(p->set_feature_list, bitmap_set(p->set_feature_list,
@ -242,8 +242,8 @@ static void macronix_nand_block_protection_support(struct nand_chip *chip)
bitmap_set(chip->parameters.set_feature_list, bitmap_set(chip->parameters.set_feature_list,
ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1);
chip->lock_area = mxic_nand_lock; chip->ops.lock_area = mxic_nand_lock;
chip->unlock_area = mxic_nand_unlock; chip->ops.unlock_area = mxic_nand_unlock;
} }
static int nand_power_down_op(struct nand_chip *chip) static int nand_power_down_op(struct nand_chip *chip)
@ -312,8 +312,8 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip)
if (i < 0) if (i < 0)
return; return;
chip->suspend = mxic_nand_suspend; chip->ops.suspend = mxic_nand_suspend;
chip->resume = mxic_nand_resume; chip->ops.resume = mxic_nand_resume;
} }
static int macronix_nand_init(struct nand_chip *chip) static int macronix_nand_init(struct nand_chip *chip)

View File

@ -84,7 +84,7 @@ static int micron_nand_onfi_init(struct nand_chip *chip)
struct nand_onfi_vendor_micron *micron = (void *)p->onfi->vendor; struct nand_onfi_vendor_micron *micron = (void *)p->onfi->vendor;
chip->read_retries = micron->read_retry_options; chip->read_retries = micron->read_retry_options;
chip->setup_read_retry = micron_nand_setup_read_retry; chip->ops.setup_read_retry = micron_nand_setup_read_retry;
} }
if (p->supports_set_get_features) { if (p->supports_set_get_features) {

View File

@ -12,7 +12,14 @@
#define ONFI_DYN_TIMING_MAX U16_MAX #define ONFI_DYN_TIMING_MAX U16_MAX
static const struct nand_data_interface onfi_sdr_timings[] = { /*
* For non-ONFI chips we use the highest possible value for tPROG and tBERS.
* tR and tCCS will take the default values precised in the ONFI specification
* for timing mode 0, respectively 200us and 500ns.
*
* These four values are tweaked to be more accurate in the case of ONFI chips.
*/
static const struct nand_interface_config onfi_sdr_timings[] = {
/* Mode 0 */ /* Mode 0 */
{ {
.type = NAND_SDR_IFACE, .type = NAND_SDR_IFACE,
@ -20,6 +27,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
.timings.sdr = { .timings.sdr = {
.tCCS_min = 500000, .tCCS_min = 500000,
.tR_max = 200000000, .tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tADL_min = 400000, .tADL_min = 400000,
.tALH_min = 20000, .tALH_min = 20000,
.tALS_min = 50000, .tALS_min = 50000,
@ -63,6 +72,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
.timings.sdr = { .timings.sdr = {
.tCCS_min = 500000, .tCCS_min = 500000,
.tR_max = 200000000, .tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tADL_min = 400000, .tADL_min = 400000,
.tALH_min = 10000, .tALH_min = 10000,
.tALS_min = 25000, .tALS_min = 25000,
@ -106,6 +117,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
.timings.sdr = { .timings.sdr = {
.tCCS_min = 500000, .tCCS_min = 500000,
.tR_max = 200000000, .tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tADL_min = 400000, .tADL_min = 400000,
.tALH_min = 10000, .tALH_min = 10000,
.tALS_min = 15000, .tALS_min = 15000,
@ -149,6 +162,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
.timings.sdr = { .timings.sdr = {
.tCCS_min = 500000, .tCCS_min = 500000,
.tR_max = 200000000, .tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tADL_min = 400000, .tADL_min = 400000,
.tALH_min = 5000, .tALH_min = 5000,
.tALS_min = 10000, .tALS_min = 10000,
@ -192,6 +207,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
.timings.sdr = { .timings.sdr = {
.tCCS_min = 500000, .tCCS_min = 500000,
.tR_max = 200000000, .tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tADL_min = 400000, .tADL_min = 400000,
.tALH_min = 5000, .tALH_min = 5000,
.tALS_min = 10000, .tALS_min = 10000,
@ -235,6 +252,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
.timings.sdr = { .timings.sdr = {
.tCCS_min = 500000, .tCCS_min = 500000,
.tR_max = 200000000, .tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tADL_min = 400000, .tADL_min = 400000,
.tALH_min = 5000, .tALH_min = 5000,
.tALS_min = 10000, .tALS_min = 10000,
@ -273,23 +292,79 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
}, },
}; };
/** /* All NAND chips share the same reset data interface: SDR mode 0 */
* onfi_fill_data_interface - [NAND Interface] Initialize a data interface from const struct nand_interface_config *nand_get_reset_interface_config(void)
* given ONFI mode {
* @mode: The ONFI timing mode return &onfi_sdr_timings[0];
*/ }
int onfi_fill_data_interface(struct nand_chip *chip,
enum nand_data_interface_type type, /**
int timing_mode) * onfi_find_closest_sdr_mode - Derive the closest ONFI SDR timing mode given a
* set of timings
* @spec_timings: the timings to challenge
*/
unsigned int
onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings)
{
const struct nand_sdr_timings *onfi_timings;
int mode;
for (mode = ARRAY_SIZE(onfi_sdr_timings) - 1; mode > 0; mode--) {
onfi_timings = &onfi_sdr_timings[mode].timings.sdr;
if (spec_timings->tCCS_min <= onfi_timings->tCCS_min &&
spec_timings->tADL_min <= onfi_timings->tADL_min &&
spec_timings->tALH_min <= onfi_timings->tALH_min &&
spec_timings->tALS_min <= onfi_timings->tALS_min &&
spec_timings->tAR_min <= onfi_timings->tAR_min &&
spec_timings->tCEH_min <= onfi_timings->tCEH_min &&
spec_timings->tCH_min <= onfi_timings->tCH_min &&
spec_timings->tCLH_min <= onfi_timings->tCLH_min &&
spec_timings->tCLR_min <= onfi_timings->tCLR_min &&
spec_timings->tCLS_min <= onfi_timings->tCLS_min &&
spec_timings->tCOH_min <= onfi_timings->tCOH_min &&
spec_timings->tCS_min <= onfi_timings->tCS_min &&
spec_timings->tDH_min <= onfi_timings->tDH_min &&
spec_timings->tDS_min <= onfi_timings->tDS_min &&
spec_timings->tIR_min <= onfi_timings->tIR_min &&
spec_timings->tRC_min <= onfi_timings->tRC_min &&
spec_timings->tREH_min <= onfi_timings->tREH_min &&
spec_timings->tRHOH_min <= onfi_timings->tRHOH_min &&
spec_timings->tRHW_min <= onfi_timings->tRHW_min &&
spec_timings->tRLOH_min <= onfi_timings->tRLOH_min &&
spec_timings->tRP_min <= onfi_timings->tRP_min &&
spec_timings->tRR_min <= onfi_timings->tRR_min &&
spec_timings->tWC_min <= onfi_timings->tWC_min &&
spec_timings->tWH_min <= onfi_timings->tWH_min &&
spec_timings->tWHR_min <= onfi_timings->tWHR_min &&
spec_timings->tWP_min <= onfi_timings->tWP_min &&
spec_timings->tWW_min <= onfi_timings->tWW_min)
return mode;
}
return 0;
}
/**
* onfi_fill_interface_config - Initialize an interface config from a given
* ONFI mode
* @chip: The NAND chip
* @iface: The interface configuration to fill
* @type: The interface type
* @timing_mode: The ONFI timing mode
*/
void onfi_fill_interface_config(struct nand_chip *chip,
struct nand_interface_config *iface,
enum nand_interface_type type,
unsigned int timing_mode)
{ {
struct nand_data_interface *iface = &chip->data_interface;
struct onfi_params *onfi = chip->parameters.onfi; struct onfi_params *onfi = chip->parameters.onfi;
if (type != NAND_SDR_IFACE) if (WARN_ON(type != NAND_SDR_IFACE))
return -EINVAL; return;
if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings)) if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_sdr_timings)))
return -EINVAL; return;
*iface = onfi_sdr_timings[timing_mode]; *iface = onfi_sdr_timings[timing_mode];
@ -308,22 +383,5 @@ int onfi_fill_data_interface(struct nand_chip *chip,
/* nanoseconds -> picoseconds */ /* nanoseconds -> picoseconds */
timings->tCCS_min = 1000UL * onfi->tCCS; timings->tCCS_min = 1000UL * onfi->tCCS;
} else {
struct nand_sdr_timings *timings = &iface->timings.sdr;
/*
* For non-ONFI chips we use the highest possible value for
* tPROG and tBERS. tR and tCCS will take the default values
* precised in the ONFI specification for timing mode 0,
* respectively 200us and 500ns.
*/
/* microseconds -> picoseconds */
timings->tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX;
timings->tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX;
timings->tR_max = 200000000;
timings->tCCS_min = 500000;
} }
return 0;
} }

View File

@ -33,7 +33,7 @@ static int toshiba_nand_benand_read_eccstatus_op(struct nand_chip *chip,
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
const struct nand_sdr_timings *sdr = const struct nand_sdr_timings *sdr =
nand_get_sdr_timings(&chip->data_interface); nand_get_sdr_timings(nand_get_interface_config(chip));
struct nand_op_instr instrs[] = { struct nand_op_instr instrs[] = {
NAND_OP_CMD(TOSHIBA_NAND_CMD_ECC_STATUS_READ, NAND_OP_CMD(TOSHIBA_NAND_CMD_ECC_STATUS_READ,
PSEC_TO_NSEC(sdr->tADL_min)), PSEC_TO_NSEC(sdr->tADL_min)),
@ -194,17 +194,79 @@ static void toshiba_nand_decode_id(struct nand_chip *chip)
} }
} }
static int
tc58teg5dclta00_choose_interface_config(struct nand_chip *chip,
struct nand_interface_config *iface)
{
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 5);
return nand_choose_best_sdr_timings(chip, iface, NULL);
}
static int
tc58nvg0s3e_choose_interface_config(struct nand_chip *chip,
struct nand_interface_config *iface)
{
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 2);
return nand_choose_best_sdr_timings(chip, iface, NULL);
}
static int
th58nvg2s3hbai4_choose_interface_config(struct nand_chip *chip,
struct nand_interface_config *iface)
{
struct nand_sdr_timings *sdr = &iface->timings.sdr;
/* Start with timings from the closest timing mode, mode 4. */
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 4);
/* Patch timings that differ from mode 4. */
sdr->tALS_min = 12000;
sdr->tCHZ_max = 20000;
sdr->tCLS_min = 12000;
sdr->tCOH_min = 0;
sdr->tDS_min = 12000;
sdr->tRHOH_min = 25000;
sdr->tRHW_min = 30000;
sdr->tRHZ_max = 60000;
sdr->tWHR_min = 60000;
/* Patch timings not part of onfi timing mode. */
sdr->tPROG_max = 700000000;
sdr->tBERS_max = 5000000000;
return nand_choose_best_sdr_timings(chip, iface, sdr);
}
static int tc58teg5dclta00_init(struct nand_chip *chip) static int tc58teg5dclta00_init(struct nand_chip *chip)
{ {
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
chip->onfi_timing_mode_default = 5; chip->ops.choose_interface_config =
&tc58teg5dclta00_choose_interface_config;
chip->options |= NAND_NEED_SCRAMBLING; chip->options |= NAND_NEED_SCRAMBLING;
mtd_set_pairing_scheme(mtd, &dist3_pairing_scheme); mtd_set_pairing_scheme(mtd, &dist3_pairing_scheme);
return 0; return 0;
} }
static int tc58nvg0s3e_init(struct nand_chip *chip)
{
chip->ops.choose_interface_config =
&tc58nvg0s3e_choose_interface_config;
return 0;
}
static int th58nvg2s3hbai4_init(struct nand_chip *chip)
{
chip->ops.choose_interface_config =
&th58nvg2s3hbai4_choose_interface_config;
return 0;
}
static int toshiba_nand_init(struct nand_chip *chip) static int toshiba_nand_init(struct nand_chip *chip)
{ {
if (nand_is_slc(chip)) if (nand_is_slc(chip))
@ -217,6 +279,12 @@ static int toshiba_nand_init(struct nand_chip *chip)
if (!strcmp("TC58TEG5DCLTA00", chip->parameters.model)) if (!strcmp("TC58TEG5DCLTA00", chip->parameters.model))
tc58teg5dclta00_init(chip); tc58teg5dclta00_init(chip);
if (!strncmp("TC58NVG0S3E", chip->parameters.model,
sizeof("TC58NVG0S3E") - 1))
tc58nvg0s3e_init(chip);
if (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
sizeof("TH58NVG2S3HBAI4") - 1))
th58nvg2s3hbai4_init(chip);
return 0; return 0;
} }

View File

@ -459,11 +459,13 @@ struct qcom_nand_host {
* among different NAND controllers. * among different NAND controllers.
* @ecc_modes - ecc mode for NAND * @ecc_modes - ecc mode for NAND
* @is_bam - whether NAND controller is using BAM * @is_bam - whether NAND controller is using BAM
* @is_qpic - whether NAND CTRL is part of qpic IP
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
*/ */
struct qcom_nandc_props { struct qcom_nandc_props {
u32 ecc_modes; u32 ecc_modes;
bool is_bam; bool is_bam;
bool is_qpic;
u32 dev_cmd_reg_start; u32 dev_cmd_reg_start;
}; };
@ -2774,14 +2776,24 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
u32 nand_ctrl; u32 nand_ctrl;
/* kill onenand */ /* kill onenand */
nandc_write(nandc, SFLASHC_BURST_CFG, 0); if (!nandc->props->is_qpic)
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
NAND_DEV_CMD_VLD_VAL); NAND_DEV_CMD_VLD_VAL);
/* enable ADM or BAM DMA */ /* enable ADM or BAM DMA */
if (nandc->props->is_bam) { if (nandc->props->is_bam) {
nand_ctrl = nandc_read(nandc, NAND_CTRL); nand_ctrl = nandc_read(nandc, NAND_CTRL);
nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
/*
*NAND_CTRL is an operational registers, and CPU
* access to operational registers are read only
* in BAM mode. So update the NAND_CTRL register
* only if it is not in BAM mode. In most cases BAM
* mode will be enabled in bootloader
*/
if (!(nand_ctrl & BAM_MODE_EN))
nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
} else { } else {
nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
} }
@ -3035,12 +3047,14 @@ static const struct qcom_nandc_props ipq806x_nandc_props = {
static const struct qcom_nandc_props ipq4019_nandc_props = { static const struct qcom_nandc_props ipq4019_nandc_props = {
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
.is_bam = true, .is_bam = true,
.is_qpic = true,
.dev_cmd_reg_start = 0x0, .dev_cmd_reg_start = 0x0,
}; };
static const struct qcom_nandc_props ipq8074_nandc_props = { static const struct qcom_nandc_props ipq8074_nandc_props = {
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
.is_bam = true, .is_bam = true,
.is_qpic = true,
.dev_cmd_reg_start = 0x7000, .dev_cmd_reg_start = 0x7000,
}; };

View File

@ -808,8 +808,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
return -ENODEV; return -ENODEV;
} }
static int s3c2410_nand_setup_data_interface(struct nand_chip *chip, int csline, static int s3c2410_nand_setup_interface(struct nand_chip *chip, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
@ -999,7 +999,7 @@ static int s3c2410_nand_attach_chip(struct nand_chip *chip)
static const struct nand_controller_ops s3c24xx_nand_controller_ops = { static const struct nand_controller_ops s3c24xx_nand_controller_ops = {
.attach_chip = s3c2410_nand_attach_chip, .attach_chip = s3c2410_nand_attach_chip,
.setup_data_interface = s3c2410_nand_setup_data_interface, .setup_interface = s3c2410_nand_setup_interface,
}; };
static const struct of_device_id s3c24xx_nand_dt_ids[] = { static const struct of_device_id s3c24xx_nand_dt_ids[] = {

View File

@ -1277,7 +1277,7 @@ static int stm32_fmc2_nfc_waitrdy(struct nand_chip *chip,
dev_warn(nfc->dev, "Waitrdy timeout\n"); dev_warn(nfc->dev, "Waitrdy timeout\n");
/* Wait tWB before R/B# signal is low */ /* Wait tWB before R/B# signal is low */
timings = nand_get_sdr_timings(&chip->data_interface); timings = nand_get_sdr_timings(nand_get_interface_config(chip));
ndelay(PSEC_TO_NSEC(timings->tWB_max)); ndelay(PSEC_TO_NSEC(timings->tWB_max));
/* R/B# signal is low, clear high level flag */ /* R/B# signal is low, clear high level flag */
@ -1517,7 +1517,7 @@ static void stm32_fmc2_nfc_calc_timings(struct nand_chip *chip,
} }
static int stm32_fmc2_nfc_setup_interface(struct nand_chip *chip, int chipnr, static int stm32_fmc2_nfc_setup_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
const struct nand_sdr_timings *sdrt; const struct nand_sdr_timings *sdrt;
@ -1735,7 +1735,7 @@ static int stm32_fmc2_nfc_attach_chip(struct nand_chip *chip)
static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = { static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = {
.attach_chip = stm32_fmc2_nfc_attach_chip, .attach_chip = stm32_fmc2_nfc_attach_chip,
.exec_op = stm32_fmc2_nfc_exec_op, .exec_op = stm32_fmc2_nfc_exec_op,
.setup_data_interface = stm32_fmc2_nfc_setup_interface, .setup_interface = stm32_fmc2_nfc_setup_interface,
}; };
static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc, static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,

View File

@ -1376,8 +1376,8 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
#define sunxi_nand_lookup_timing(l, p, c) \ #define sunxi_nand_lookup_timing(l, p, c) \
_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
static int sunxi_nfc_setup_data_interface(struct nand_chip *nand, int csline, static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
@ -1920,7 +1920,7 @@ static int sunxi_nfc_exec_op(struct nand_chip *nand,
static const struct nand_controller_ops sunxi_nand_controller_ops = { static const struct nand_controller_ops sunxi_nand_controller_ops = {
.attach_chip = sunxi_nand_attach_chip, .attach_chip = sunxi_nand_attach_chip,
.setup_data_interface = sunxi_nfc_setup_data_interface, .setup_interface = sunxi_nfc_setup_interface,
.exec_op = sunxi_nfc_exec_op, .exec_op = sunxi_nfc_exec_op,
}; };

View File

@ -113,53 +113,11 @@ struct tango_chip {
#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3)) #define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3))
static void tango_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl) static void tango_select_target(struct nand_chip *chip, unsigned int cs)
{
struct tango_chip *tchip = to_tango_chip(chip);
if (ctrl & NAND_CLE)
writeb_relaxed(dat, tchip->base + PBUS_CMD);
if (ctrl & NAND_ALE)
writeb_relaxed(dat, tchip->base + PBUS_ADDR);
}
static int tango_dev_ready(struct nand_chip *chip)
{
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY;
}
static u8 tango_read_byte(struct nand_chip *chip)
{
struct tango_chip *tchip = to_tango_chip(chip);
return readb_relaxed(tchip->base + PBUS_DATA);
}
static void tango_read_buf(struct nand_chip *chip, u8 *buf, int len)
{
struct tango_chip *tchip = to_tango_chip(chip);
ioread8_rep(tchip->base + PBUS_DATA, buf, len);
}
static void tango_write_buf(struct nand_chip *chip, const u8 *buf, int len)
{
struct tango_chip *tchip = to_tango_chip(chip);
iowrite8_rep(tchip->base + PBUS_DATA, buf, len);
}
static void tango_select_chip(struct nand_chip *chip, int idx)
{ {
struct tango_nfc *nfc = to_tango_nfc(chip->controller); struct tango_nfc *nfc = to_tango_nfc(chip->controller);
struct tango_chip *tchip = to_tango_chip(chip); struct tango_chip *tchip = to_tango_chip(chip);
if (idx < 0)
return; /* No "chip unselect" function */
writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1); writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1);
writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2); writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2);
writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG); writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG);
@ -168,6 +126,69 @@ static void tango_select_chip(struct nand_chip *chip, int idx)
writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG); writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG);
} }
static int tango_waitrdy(struct nand_chip *chip, unsigned int timeout_ms)
{
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
u32 status;
return readl_relaxed_poll_timeout(nfc->pbus_base + PBUS_CS_CTRL,
status, status & PBUS_IORDY, 20,
timeout_ms);
}
static int tango_exec_instr(struct nand_chip *chip,
const struct nand_op_instr *instr)
{
struct tango_chip *tchip = to_tango_chip(chip);
unsigned int i;
switch (instr->type) {
case NAND_OP_CMD_INSTR:
writeb_relaxed(instr->ctx.cmd.opcode, tchip->base + PBUS_CMD);
return 0;
case NAND_OP_ADDR_INSTR:
for (i = 0; i < instr->ctx.addr.naddrs; i++)
writeb_relaxed(instr->ctx.addr.addrs[i],
tchip->base + PBUS_ADDR);
return 0;
case NAND_OP_DATA_IN_INSTR:
ioread8_rep(tchip->base + PBUS_DATA, instr->ctx.data.buf.in,
instr->ctx.data.len);
return 0;
case NAND_OP_DATA_OUT_INSTR:
iowrite8_rep(tchip->base + PBUS_DATA, instr->ctx.data.buf.out,
instr->ctx.data.len);
return 0;
case NAND_OP_WAITRDY_INSTR:
return tango_waitrdy(chip,
instr->ctx.waitrdy.timeout_ms);
default:
break;
}
return -EINVAL;
}
static int tango_exec_op(struct nand_chip *chip,
const struct nand_operation *op,
bool check_only)
{
unsigned int i;
int ret = 0;
if (check_only)
return 0;
tango_select_target(chip, op->cs);
for (i = 0; i < op->ninstrs; i++) {
ret = tango_exec_instr(chip, &op->instrs[i]);
if (ret)
break;
}
return ret;
}
/* /*
* The controller does not check for bitflips in erased pages, * The controller does not check for bitflips in erased pages,
* therefore software must check instead. * therefore software must check instead.
@ -279,6 +300,7 @@ static int tango_read_page(struct nand_chip *chip, u8 *buf,
struct tango_nfc *nfc = to_tango_nfc(chip->controller); struct tango_nfc *nfc = to_tango_nfc(chip->controller);
int err, res, len = mtd->writesize; int err, res, len = mtd->writesize;
tango_select_target(chip, chip->cur_cs);
if (oob_required) if (oob_required)
chip->ecc.read_oob(chip, page); chip->ecc.read_oob(chip, page);
@ -300,22 +322,30 @@ static int tango_write_page(struct nand_chip *chip, const u8 *buf,
{ {
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
struct tango_nfc *nfc = to_tango_nfc(chip->controller); struct tango_nfc *nfc = to_tango_nfc(chip->controller);
int err, status, len = mtd->writesize; const struct nand_sdr_timings *timings;
int err, len = mtd->writesize;
u8 status;
/* Calling tango_write_oob() would send PAGEPROG twice */ /* Calling tango_write_oob() would send PAGEPROG twice */
if (oob_required) if (oob_required)
return -ENOTSUPP; return -ENOTSUPP;
tango_select_target(chip, chip->cur_cs);
writel_relaxed(0xffffffff, nfc->mem_base + METADATA); writel_relaxed(0xffffffff, nfc->mem_base + METADATA);
err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page); err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page);
if (err) if (err)
return err; return err;
status = chip->legacy.waitfunc(chip); timings = nand_get_sdr_timings(nand_get_interface_config(chip));
if (status & NAND_STATUS_FAIL) err = tango_waitrdy(chip, PSEC_TO_MSEC(timings->tR_max));
return -EIO; if (err)
return err;
return 0; err = nand_status_op(chip, &status);
if (err)
return err;
return (status & NAND_STATUS_FAIL) ? -EIO : 0;
} }
static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos) static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
@ -326,7 +356,9 @@ static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
/* skip over "len" bytes */ /* skip over "len" bytes */
nand_change_read_column_op(chip, *pos, NULL, 0, false); nand_change_read_column_op(chip, *pos, NULL, 0, false);
} else { } else {
tango_read_buf(chip, *buf, len); struct tango_chip *tchip = to_tango_chip(chip);
ioread8_rep(tchip->base + PBUS_DATA, *buf, len);
*buf += len; *buf += len;
} }
} }
@ -339,7 +371,9 @@ static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)
/* skip over "len" bytes */ /* skip over "len" bytes */
nand_change_write_column_op(chip, *pos, NULL, 0, false); nand_change_write_column_op(chip, *pos, NULL, 0, false);
} else { } else {
tango_write_buf(chip, *buf, len); struct tango_chip *tchip = to_tango_chip(chip);
iowrite8_rep(tchip->base + PBUS_DATA, *buf, len);
*buf += len; *buf += len;
} }
} }
@ -420,6 +454,7 @@ static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob)
static int tango_read_page_raw(struct nand_chip *chip, u8 *buf, static int tango_read_page_raw(struct nand_chip *chip, u8 *buf,
int oob_required, int page) int oob_required, int page)
{ {
tango_select_target(chip, chip->cur_cs);
nand_read_page_op(chip, page, 0, NULL, 0); nand_read_page_op(chip, page, 0, NULL, 0);
raw_read(chip, buf, chip->oob_poi); raw_read(chip, buf, chip->oob_poi);
return 0; return 0;
@ -428,6 +463,7 @@ static int tango_read_page_raw(struct nand_chip *chip, u8 *buf,
static int tango_write_page_raw(struct nand_chip *chip, const u8 *buf, static int tango_write_page_raw(struct nand_chip *chip, const u8 *buf,
int oob_required, int page) int oob_required, int page)
{ {
tango_select_target(chip, chip->cur_cs);
nand_prog_page_begin_op(chip, page, 0, NULL, 0); nand_prog_page_begin_op(chip, page, 0, NULL, 0);
raw_write(chip, buf, chip->oob_poi); raw_write(chip, buf, chip->oob_poi);
return nand_prog_page_end_op(chip); return nand_prog_page_end_op(chip);
@ -435,6 +471,7 @@ static int tango_write_page_raw(struct nand_chip *chip, const u8 *buf,
static int tango_read_oob(struct nand_chip *chip, int page) static int tango_read_oob(struct nand_chip *chip, int page)
{ {
tango_select_target(chip, chip->cur_cs);
nand_read_page_op(chip, page, 0, NULL, 0); nand_read_page_op(chip, page, 0, NULL, 0);
raw_read(chip, NULL, chip->oob_poi); raw_read(chip, NULL, chip->oob_poi);
return 0; return 0;
@ -442,6 +479,7 @@ static int tango_read_oob(struct nand_chip *chip, int page)
static int tango_write_oob(struct nand_chip *chip, int page) static int tango_write_oob(struct nand_chip *chip, int page)
{ {
tango_select_target(chip, chip->cur_cs);
nand_prog_page_begin_op(chip, page, 0, NULL, 0); nand_prog_page_begin_op(chip, page, 0, NULL, 0);
raw_write(chip, NULL, chip->oob_poi); raw_write(chip, NULL, chip->oob_poi);
return nand_prog_page_end_op(chip); return nand_prog_page_end_op(chip);
@ -477,7 +515,7 @@ static u32 to_ticks(int kHz, int ps)
} }
static int tango_set_timings(struct nand_chip *chip, int csline, static int tango_set_timings(struct nand_chip *chip, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf); const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
struct tango_nfc *nfc = to_tango_nfc(chip->controller); struct tango_nfc *nfc = to_tango_nfc(chip->controller);
@ -527,7 +565,8 @@ static int tango_attach_chip(struct nand_chip *chip)
static const struct nand_controller_ops tango_controller_ops = { static const struct nand_controller_ops tango_controller_ops = {
.attach_chip = tango_attach_chip, .attach_chip = tango_attach_chip,
.setup_data_interface = tango_set_timings, .setup_interface = tango_set_timings,
.exec_op = tango_exec_op,
}; };
static int chip_init(struct device *dev, struct device_node *np) static int chip_init(struct device *dev, struct device_node *np)
@ -562,12 +601,6 @@ static int chip_init(struct device *dev, struct device_node *np)
ecc = &chip->ecc; ecc = &chip->ecc;
mtd = nand_to_mtd(chip); mtd = nand_to_mtd(chip);
chip->legacy.read_byte = tango_read_byte;
chip->legacy.write_buf = tango_write_buf;
chip->legacy.read_buf = tango_read_buf;
chip->legacy.select_chip = tango_select_chip;
chip->legacy.cmd_ctrl = tango_cmd_ctrl;
chip->legacy.dev_ready = tango_dev_ready;
chip->options = NAND_USES_DMA | chip->options = NAND_USES_DMA |
NAND_NO_SUBPAGE_WRITE | NAND_NO_SUBPAGE_WRITE |
NAND_WAIT_TCCS; NAND_WAIT_TCCS;

View File

@ -813,8 +813,8 @@ static void tegra_nand_setup_timing(struct tegra_nand_controller *ctrl,
writel_relaxed(reg, ctrl->regs + TIMING_2); writel_relaxed(reg, ctrl->regs + TIMING_2);
} }
static int tegra_nand_setup_data_interface(struct nand_chip *chip, int csline, static int tegra_nand_setup_interface(struct nand_chip *chip, int csline,
const struct nand_data_interface *conf) const struct nand_interface_config *conf)
{ {
struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
const struct nand_sdr_timings *timings; const struct nand_sdr_timings *timings;
@ -1053,7 +1053,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
static const struct nand_controller_ops tegra_nand_controller_ops = { static const struct nand_controller_ops tegra_nand_controller_ops = {
.attach_chip = &tegra_nand_attach_chip, .attach_chip = &tegra_nand_attach_chip,
.exec_op = tegra_nand_exec_op, .exec_op = tegra_nand_exec_op,
.setup_data_interface = tegra_nand_setup_data_interface, .setup_interface = tegra_nand_setup_interface,
}; };
static int tegra_nand_chips_init(struct device *dev, static int tegra_nand_chips_init(struct device *dev,

View File

@ -22,6 +22,11 @@
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/of.h> #include <linux/of.h>
#ifdef CONFIG_MIPS
#include <asm/bootinfo.h>
#include <asm/fw/cfe/cfe_api.h>
#endif /* CONFIG_MIPS */
#define BCM963XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */ #define BCM963XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */
#define BCM963XX_CFE_MAGIC_OFFSET 0x4e0 #define BCM963XX_CFE_MAGIC_OFFSET 0x4e0
@ -32,28 +37,15 @@
#define STR_NULL_TERMINATE(x) \ #define STR_NULL_TERMINATE(x) \
do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0) do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0)
static int bcm63xx_detect_cfe(struct mtd_info *master) static inline int bcm63xx_detect_cfe(void)
{ {
char buf[9]; int ret = 0;
int ret;
size_t retlen;
ret = mtd_read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen, #ifdef CONFIG_MIPS
(void *)buf); ret = (fw_arg3 == CFE_EPTSEAL);
buf[retlen] = 0; #endif /* CONFIG_MIPS */
if (ret) return ret;
return ret;
if (strncmp("cfe-v", buf, 5) == 0)
return 0;
/* very old CFE's do not have the cfe-v string, so check for magic */
ret = mtd_read(master, BCM963XX_CFE_MAGIC_OFFSET, 8, &retlen,
(void *)buf);
buf[retlen] = 0;
return strncmp("CFE1CFE1", buf, 8);
} }
static int bcm63xx_read_nvram(struct mtd_info *master, static int bcm63xx_read_nvram(struct mtd_info *master,
@ -138,7 +130,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
struct bcm963xx_nvram *nvram = NULL; struct bcm963xx_nvram *nvram = NULL;
int ret; int ret;
if (bcm63xx_detect_cfe(master)) if (!bcm63xx_detect_cfe())
return -EINVAL; return -EINVAL;
nvram = vzalloc(sizeof(*nvram)); nvram = vzalloc(sizeof(*nvram));

View File

@ -12,6 +12,8 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
struct nand_device;
/** /**
* struct nand_memory_organization - Memory organization structure * struct nand_memory_organization - Memory organization structure
* @bits_per_cell: number of bits per NAND cell * @bits_per_cell: number of bits per NAND cell
@ -114,11 +116,11 @@ struct nand_page_io_req {
}; };
/** /**
* struct nand_ecc_req - NAND ECC requirements * struct nand_ecc_props - NAND ECC properties
* @strength: ECC strength * @strength: ECC strength
* @step_size: ECC step/block size * @step_size: Number of bytes per step
*/ */
struct nand_ecc_req { struct nand_ecc_props {
unsigned int strength; unsigned int strength;
unsigned int step_size; unsigned int step_size;
}; };
@ -133,8 +135,6 @@ struct nand_bbt {
unsigned long *cache; unsigned long *cache;
}; };
struct nand_device;
/** /**
* struct nand_ops - NAND operations * struct nand_ops - NAND operations
* @erase: erase a specific block. No need to check if the block is bad before * @erase: erase a specific block. No need to check if the block is bad before
@ -179,7 +179,7 @@ struct nand_ops {
struct nand_device { struct nand_device {
struct mtd_info mtd; struct mtd_info mtd;
struct nand_memory_organization memorg; struct nand_memory_organization memorg;
struct nand_ecc_req eccreq; struct nand_ecc_props eccreq;
struct nand_row_converter rowconv; struct nand_row_converter rowconv;
struct nand_bbt bbt; struct nand_bbt bbt;
const struct nand_ops *ops; const struct nand_ops *ops;

View File

@ -492,22 +492,22 @@ struct nand_sdr_timings {
}; };
/** /**
* enum nand_data_interface_type - NAND interface timing type * enum nand_interface_type - NAND interface type
* @NAND_SDR_IFACE: Single Data Rate interface * @NAND_SDR_IFACE: Single Data Rate interface
*/ */
enum nand_data_interface_type { enum nand_interface_type {
NAND_SDR_IFACE, NAND_SDR_IFACE,
}; };
/** /**
* struct nand_data_interface - NAND interface timing * struct nand_interface_config - NAND interface timing
* @type: type of the timing * @type: type of the timing
* @timings: The timing information * @timings: The timing information
* @timings.mode: Timing mode as defined in the specification * @timings.mode: Timing mode as defined in the specification
* @timings.sdr: Use it when @type is %NAND_SDR_IFACE. * @timings.sdr: Use it when @type is %NAND_SDR_IFACE.
*/ */
struct nand_data_interface { struct nand_interface_config {
enum nand_data_interface_type type; enum nand_interface_type type;
struct nand_timings { struct nand_timings {
unsigned int mode; unsigned int mode;
union { union {
@ -521,7 +521,7 @@ struct nand_data_interface {
* @conf: The data interface * @conf: The data interface
*/ */
static inline const struct nand_sdr_timings * static inline const struct nand_sdr_timings *
nand_get_sdr_timings(const struct nand_data_interface *conf) nand_get_sdr_timings(const struct nand_interface_config *conf)
{ {
if (conf->type != NAND_SDR_IFACE) if (conf->type != NAND_SDR_IFACE)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
@ -944,11 +944,10 @@ static inline void nand_op_trace(const char *prefix,
* This method replaces chip->legacy.cmdfunc(), * This method replaces chip->legacy.cmdfunc(),
* chip->legacy.{read,write}_{buf,byte,word}(), * chip->legacy.{read,write}_{buf,byte,word}(),
* chip->legacy.dev_ready() and chip->legacy.waifunc(). * chip->legacy.dev_ready() and chip->legacy.waifunc().
* @setup_data_interface: setup the data interface and timing. If * @setup_interface: setup the data interface and timing. If chipnr is set to
* chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this * %NAND_DATA_IFACE_CHECK_ONLY this means the configuration
* means the configuration should not be applied but * should not be applied but only checked.
* only checked. * This hook is optional.
* This hook is optional.
*/ */
struct nand_controller_ops { struct nand_controller_ops {
int (*attach_chip)(struct nand_chip *chip); int (*attach_chip)(struct nand_chip *chip);
@ -956,8 +955,8 @@ struct nand_controller_ops {
int (*exec_op)(struct nand_chip *chip, int (*exec_op)(struct nand_chip *chip,
const struct nand_operation *op, const struct nand_operation *op,
bool check_only); bool check_only);
int (*setup_data_interface)(struct nand_chip *chip, int chipnr, int (*setup_interface)(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf); const struct nand_interface_config *conf);
}; };
/** /**
@ -1028,140 +1027,138 @@ struct nand_legacy {
}; };
/** /**
* struct nand_chip - NAND Private Flash Chip Data * struct nand_chip_ops - NAND chip operations
* @base: Inherit from the generic NAND device * @suspend: Suspend operation
* @legacy: All legacy fields/hooks. If you develop a new driver, * @resume: Resume operation
* don't even try to use any of these fields/hooks, and if * @lock_area: Lock operation
* you're modifying an existing driver that is using those * @unlock_area: Unlock operation
* fields/hooks, you should consider reworking the driver * @setup_read_retry: Set the read-retry mode (mostly needed for MLC NANDs)
* avoid using them. * @choose_interface_config: Choose the best interface configuration
* @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for
* setting the read-retry mode. Mostly needed for MLC NAND.
* @ecc: [BOARDSPECIFIC] ECC control structure
* @buf_align: minimum buffer alignment required by a platform
* @oob_poi: "poison value buffer," used for laying out OOB data
* before writing
* @page_shift: [INTERN] number of address bits in a page (column
* address bits).
* @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
* @chip_shift: [INTERN] number of address bits in one chip
* @options: [BOARDSPECIFIC] various chip options. They can partly
* be set to inform nand_scan about special functionality.
* See the defines for further explanation.
* @bbt_options: [INTERN] bad block specific options. All options used
* here must come from bbm.h. By default, these options
* will be copied to the appropriate nand_bbt_descr's.
* @badblockpos: [INTERN] position of the bad block marker in the oob
* area.
* @badblockbits: [INTERN] minimum number of set bits in a good block's
* bad block marker position; i.e., BBM == 11110111b is
* not bad when badblockbits == 7
* @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
* set to the actually used ONFI mode if the chip is
* ONFI compliant or deduced from the datasheet if
* the NAND chip is not ONFI compliant.
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
* @data_buf: [INTERN] buffer for data, size is (page size + oobsize).
* @pagecache: Structure containing page cache related fields
* @pagecache.bitflips: Number of bitflips of the cached page
* @pagecache.page: Page number currently in the cache. -1 means no page is
* currently cached
* @subpagesize: [INTERN] holds the subpagesize
* @id: [INTERN] holds NAND ID
* @parameters: [INTERN] holds generic parameters under an easily
* readable form.
* @data_interface: [INTERN] NAND interface timing information
* @cur_cs: currently selected target. -1 means no target selected,
* otherwise we should always have cur_cs >= 0 &&
* cur_cs < nanddev_ntargets(). NAND Controller drivers
* should not modify this value, but they're allowed to
* read it.
* @read_retries: [INTERN] the number of read retry modes supported
* @lock: lock protecting the suspended field. Also used to
* serialize accesses to the NAND device.
* @suspended: set to 1 when the device is suspended, 0 when it's not.
* @suspend: [REPLACEABLE] specific NAND device suspend operation
* @resume: [REPLACEABLE] specific NAND device resume operation
* @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
* lookup.
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
* bad block scan.
* @controller: [REPLACEABLE] a pointer to a hardware controller
* structure which is shared among multiple independent
* devices.
* @priv: [OPTIONAL] pointer to private chip data
* @manufacturer: [INTERN] Contains manufacturer information
* @manufacturer.desc: [INTERN] Contains manufacturer's description
* @manufacturer.priv: [INTERN] Contains manufacturer private information
* @lock_area: [REPLACEABLE] specific NAND chip lock operation
* @unlock_area: [REPLACEABLE] specific NAND chip unlock operation
*/ */
struct nand_chip_ops {
int (*suspend)(struct nand_chip *chip);
void (*resume)(struct nand_chip *chip);
int (*lock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
int (*unlock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
int (*setup_read_retry)(struct nand_chip *chip, int retry_mode);
int (*choose_interface_config)(struct nand_chip *chip,
struct nand_interface_config *iface);
};
/**
* struct nand_manufacturer - NAND manufacturer structure
* @desc: The manufacturer description
* @priv: Private information for the manufacturer driver
*/
struct nand_manufacturer {
const struct nand_manufacturer_desc *desc;
void *priv;
};
/**
* struct nand_chip - NAND Private Flash Chip Data
* @base: Inherit from the generic NAND device
* @id: Holds NAND ID
* @parameters: Holds generic parameters under an easily readable form
* @manufacturer: Manufacturer information
* @ops: NAND chip operations
* @legacy: All legacy fields/hooks. If you develop a new driver, don't even try
* to use any of these fields/hooks, and if you're modifying an
* existing driver that is using those fields/hooks, you should
* consider reworking the driver and avoid using them.
* @options: Various chip options. They can partly be set to inform nand_scan
* about special functionality. See the defines for further
* explanation.
* @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
* the appropriate nand_bbt_descr's.
* @badblockpos: Bad block marker position in the oob area
* @badblockbits: Minimum number of set bits in a good block's bad block marker
* position; i.e., BBM = 11110111b is good when badblockbits = 7
* @bbt_td: Bad block table descriptor for flash lookup
* @bbt_md: Bad block table mirror descriptor
* @badblock_pattern: Bad block scan pattern used for initial bad block scan
* @bbt: Bad block table pointer
* @page_shift: Number of address bits in a page (column address bits)
* @phys_erase_shift: Number of address bits in a physical eraseblock
* @chip_shift: Number of address bits in one chip
* @pagemask: Page number mask = number of (pages / chip) - 1
* @subpagesize: Holds the subpagesize
* @data_buf: Buffer for data, size is (page size + oobsize)
* @oob_poi: pointer on the OOB area covered by data_buf
* @pagecache: Structure containing page cache related fields
* @pagecache.bitflips: Number of bitflips of the cached page
* @pagecache.page: Page number currently in the cache. -1 means no page is
* currently cached
* @buf_align: Minimum buffer alignment required by a platform
* @lock: Lock protecting the suspended field. Also used to serialize accesses
* to the NAND device
* @suspended: Set to 1 when the device is suspended, 0 when it's not
* @cur_cs: Currently selected target. -1 means no target selected, otherwise we
* should always have cur_cs >= 0 && cur_cs < nanddev_ntargets().
* NAND Controller drivers should not modify this value, but they're
* allowed to read it.
* @read_retries: The number of read retry modes supported
* @controller: The hardware controller structure which is shared among multiple
* independent devices
* @ecc: The ECC controller structure
* @priv: Chip private data
*/
struct nand_chip { struct nand_chip {
struct nand_device base; struct nand_device base;
struct nand_id id;
struct nand_parameters parameters;
struct nand_manufacturer manufacturer;
struct nand_chip_ops ops;
struct nand_legacy legacy; struct nand_legacy legacy;
int (*setup_read_retry)(struct nand_chip *chip, int retry_mode);
unsigned int options; unsigned int options;
/* Data interface */
const struct nand_interface_config *current_interface_config;
struct nand_interface_config *best_interface_config;
/* Bad block information */
unsigned int bbt_erase_shift;
unsigned int bbt_options; unsigned int bbt_options;
unsigned int badblockpos;
unsigned int badblockbits;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
u8 *bbt;
int page_shift; /* Device internal layout */
int phys_erase_shift; unsigned int page_shift;
int bbt_erase_shift; unsigned int phys_erase_shift;
int chip_shift; unsigned int chip_shift;
int pagemask; unsigned int pagemask;
unsigned int subpagesize;
/* Buffers */
u8 *data_buf; u8 *data_buf;
u8 *oob_poi;
struct { struct {
unsigned int bitflips; unsigned int bitflips;
int page; int page;
} pagecache; } pagecache;
int subpagesize;
int onfi_timing_mode_default;
unsigned int badblockpos;
int badblockbits;
struct nand_id id;
struct nand_parameters parameters;
struct nand_data_interface data_interface;
int cur_cs;
int read_retries;
struct mutex lock;
unsigned int suspended : 1;
int (*suspend)(struct nand_chip *chip);
void (*resume)(struct nand_chip *chip);
uint8_t *oob_poi;
struct nand_controller *controller;
struct nand_ecc_ctrl ecc;
unsigned long buf_align; unsigned long buf_align;
uint8_t *bbt; /* Internals */
struct nand_bbt_descr *bbt_td; struct mutex lock;
struct nand_bbt_descr *bbt_md; unsigned int suspended : 1;
int cur_cs;
struct nand_bbt_descr *badblock_pattern; int read_retries;
/* Externals */
struct nand_controller *controller;
struct nand_ecc_ctrl ecc;
void *priv; void *priv;
struct {
const struct nand_manufacturer *desc;
void *priv;
} manufacturer;
int (*lock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
int (*unlock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
}; };
extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;
@ -1209,6 +1206,17 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
return mtd_get_of_node(nand_to_mtd(chip)); return mtd_get_of_node(nand_to_mtd(chip));
} }
/**
* nand_get_interface_config - Retrieve the current interface configuration
* of a NAND chip
* @chip: The NAND chip
*/
static inline const struct nand_interface_config *
nand_get_interface_config(struct nand_chip *chip)
{
return chip->current_interface_config;
}
/* /*
* A helper for defining older NAND chips where the second ID byte fully * A helper for defining older NAND chips where the second ID byte fully
* defined the chip, including the geometry (chip size, eraseblock size, page * defined the chip, including the geometry (chip size, eraseblock size, page
@ -1261,10 +1269,6 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
* @ecc_step_ds in nand_chip{}, also from the datasheet. * @ecc_step_ds in nand_chip{}, also from the datasheet.
* For example, the "4bit ECC for each 512Byte" can be set with * For example, the "4bit ECC for each 512Byte" can be set with
* NAND_ECC_INFO(4, 512). * NAND_ECC_INFO(4, 512).
* @onfi_timing_mode_default: the default ONFI timing mode entered after a NAND
* reset. Should be deduced from timings described
* in the datasheet.
*
*/ */
struct nand_flash_dev { struct nand_flash_dev {
char *name; char *name;
@ -1285,7 +1289,6 @@ struct nand_flash_dev {
uint16_t strength_ds; uint16_t strength_ds;
uint16_t step_ds; uint16_t step_ds;
} ecc; } ecc;
int onfi_timing_mode_default;
}; };
int nand_create_bbt(struct nand_chip *chip); int nand_create_bbt(struct nand_chip *chip);

View File

@ -309,7 +309,7 @@ struct spinand_info {
struct spinand_devid devid; struct spinand_devid devid;
u32 flags; u32 flags;
struct nand_memory_organization memorg; struct nand_memory_organization memorg;
struct nand_ecc_req eccreq; struct nand_ecc_props eccreq;
struct spinand_ecc_info eccinfo; struct spinand_ecc_info eccinfo;
struct { struct {
const struct spinand_op_variants *read_cache; const struct spinand_op_variants *read_cache;