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:
commit
6a1380271b
@ -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>;
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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 = {
|
||||||
|
@ -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 */
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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[] = {
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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));
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user