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.
|
||||
|
||||
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.
|
||||
The corresponding address lines are used to select the chip.
|
||||
- gpios : may specify optional GPIOs connected to the Ready-Not-Busy pins
|
||||
(R/B#). For multi-chip devices, "n" GPIO definitions are required
|
||||
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
|
||||
read registers (tR). Required if property "gpios" is not used
|
||||
(R/B# pins not connected).
|
||||
|
@ -52,8 +54,6 @@ upm@3,0 {
|
|||
fsl,upm-cmd-offset = <0x08>;
|
||||
/* Multi-chip NAND device */
|
||||
fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
|
||||
fsl,upm-wait-flags = <0x5>;
|
||||
chip-delay = <25>; // in micro-seconds
|
||||
|
||||
nand@0 {
|
||||
#address-cells = <1>;
|
||||
|
|
|
@ -114,6 +114,13 @@ patternProperties:
|
|||
description:
|
||||
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:
|
||||
- reg
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
menu "NAND"
|
||||
|
||||
config MTD_NAND_CORE
|
||||
tristate
|
||||
|
||||
source "drivers/mtd/nand/onenand/Kconfig"
|
||||
source "drivers/mtd/nand/raw/Kconfig"
|
||||
source "drivers/mtd/nand/spi/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menuconfig MTD_ONENAND
|
||||
tristate "OneNAND Device Support"
|
||||
depends on MTD
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
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
|
||||
tristate "Raw/Parallel NAND Device Support"
|
||||
depends on MTD
|
||||
select MTD_NAND_CORE
|
||||
select MTD_NAND_ECC_SW_HAMMING
|
||||
help
|
||||
|
|
|
@ -191,8 +191,8 @@ static int gpio_nand_exec_op(struct nand_chip *this,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_nand_setup_data_interface(struct nand_chip *this, int csline,
|
||||
const struct nand_data_interface *cf)
|
||||
static int gpio_nand_setup_interface(struct nand_chip *this, int csline,
|
||||
const struct nand_interface_config *cf)
|
||||
{
|
||||
struct gpio_nand *priv = nand_get_controller_data(this);
|
||||
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 = {
|
||||
.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);
|
||||
}
|
||||
|
||||
static int anfc_setup_data_interface(struct nand_chip *chip, int target,
|
||||
const struct nand_data_interface *conf)
|
||||
static int anfc_setup_interface(struct nand_chip *chip, int target,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct anand *anand = to_anand(chip);
|
||||
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 = {
|
||||
.exec_op = anfc_exec_op,
|
||||
.setup_data_interface = anfc_setup_data_interface,
|
||||
.setup_interface = anfc_setup_interface,
|
||||
.attach_chip = anfc_attach_chip,
|
||||
.detach_chip = anfc_detach_chip,
|
||||
};
|
||||
|
|
|
@ -200,8 +200,8 @@ struct atmel_nand_controller_ops {
|
|||
void (*nand_init)(struct atmel_nand_controller *nc,
|
||||
struct atmel_nand *nand);
|
||||
int (*ecc_init)(struct nand_chip *chip);
|
||||
int (*setup_data_interface)(struct atmel_nand *nand, int csline,
|
||||
const struct nand_data_interface *conf);
|
||||
int (*setup_interface)(struct atmel_nand *nand, int csline,
|
||||
const struct nand_interface_config *conf);
|
||||
};
|
||||
|
||||
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,
|
||||
const struct nand_data_interface *conf,
|
||||
const struct nand_interface_config *conf,
|
||||
struct atmel_smc_cs_conf *smcconf)
|
||||
{
|
||||
u32 ncycles, totalcycles, timeps, mckperiodps;
|
||||
|
@ -1397,9 +1397,9 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
|
|||
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,
|
||||
const struct nand_data_interface *conf)
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct atmel_nand_controller *nc;
|
||||
struct atmel_smc_cs_conf smcconf;
|
||||
|
@ -1422,9 +1422,9 @@ static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
|
|||
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,
|
||||
const struct nand_data_interface *conf)
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct atmel_hsmc_nand_controller *nc;
|
||||
struct atmel_smc_cs_conf smcconf;
|
||||
|
@ -1452,8 +1452,8 @@ static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_nand_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int atmel_nand_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct atmel_nand *nand = to_atmel_nand(chip);
|
||||
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))
|
||||
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,
|
||||
|
@ -1483,7 +1483,7 @@ static void atmel_nand_init(struct atmel_nand_controller *nc,
|
|||
chip->legacy.write_buf = atmel_nand_write_buf;
|
||||
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;
|
||||
|
||||
/* 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 = {
|
||||
.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,
|
||||
|
@ -2318,7 +2318,7 @@ static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
|
|||
.remove = atmel_hsmc_nand_controller_remove,
|
||||
.ecc_init = atmel_hsmc_nand_ecc_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 = {
|
||||
|
@ -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
|
||||
* from re-using atmel_smc_nand_setup_data_interface() for the
|
||||
* ->setup_data_interface() hook.
|
||||
* from re-using atmel_smc_nand_setup_interface() for the
|
||||
* ->setup_interface() hook.
|
||||
* 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 = {
|
||||
.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,
|
||||
.ecc_init = atmel_nand_ecc_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 = {
|
||||
|
|
|
@ -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_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;
|
||||
}
|
||||
|
||||
|
@ -2124,6 +2140,7 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
u64 err_addr = 0;
|
||||
int err;
|
||||
bool retry = true;
|
||||
bool edu_err = false;
|
||||
|
||||
dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
|
||||
|
||||
|
@ -2141,6 +2158,10 @@ try_dmaread:
|
|||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (has_edu(ctrl) && err_addr)
|
||||
edu_err = true;
|
||||
|
||||
} else {
|
||||
if (oob)
|
||||
memset(oob, 0x99, mtd->oobsize);
|
||||
|
@ -2188,6 +2209,11 @@ try_dmaread:
|
|||
if (mtd_is_bitflip(err)) {
|
||||
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",
|
||||
(unsigned long long)err_addr);
|
||||
mtd->ecc_stats.corrected += corrected;
|
||||
|
@ -3023,8 +3049,9 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* set edu transfer function to call */
|
||||
ctrl->dma_trans = brcmnand_edu_trans;
|
||||
if (has_edu(ctrl))
|
||||
/* set edu transfer function to call */
|
||||
ctrl->dma_trans = brcmnand_edu_trans;
|
||||
}
|
||||
|
||||
/* 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
|
||||
cadence_nand_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr;
|
||||
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 = {
|
||||
.attach_chip = cadence_nand_attach_chip,
|
||||
.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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
static int denali_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
static const unsigned int data_setup_on_host = 10000;
|
||||
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 = {
|
||||
.attach_chip = denali_attach_chip,
|
||||
.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,
|
||||
|
@ -1230,7 +1230,7 @@ int denali_chip_init(struct denali_controller *denali,
|
|||
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)
|
||||
chip->options |= NAND_KEEP_TIMINGS;
|
||||
|
||||
|
|
|
@ -14,32 +14,23 @@
|
|||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.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 nand_controller base;
|
||||
struct device *dev;
|
||||
struct nand_chip chip;
|
||||
int last_ctrl;
|
||||
struct mtd_partition *parts;
|
||||
struct fsl_upm upm;
|
||||
uint8_t upm_addr_offset;
|
||||
uint8_t upm_cmd_offset;
|
||||
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_count;
|
||||
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)
|
||||
|
@ -48,106 +39,6 @@ static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
|
|||
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,
|
||||
const struct device_node *upm_np,
|
||||
const struct resource *io_res)
|
||||
|
@ -156,21 +47,9 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
|
|||
int ret;
|
||||
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.algo = NAND_ECC_HAMMING;
|
||||
if (fun->mchip_count > 1)
|
||||
fun->chip.legacy.select_chip = fun_select_chip;
|
||||
|
||||
if (fun->rnb_gpio[0] >= 0)
|
||||
fun->chip.legacy.dev_ready = fun_chip_ready;
|
||||
|
||||
fun->chip.controller = &fun->base;
|
||||
mtd->dev.parent = fun->dev;
|
||||
|
||||
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;
|
||||
|
||||
nand_set_flash_node(&fun->chip, flash_np);
|
||||
mtd->name = kasprintf(GFP_KERNEL, "0x%llx.%pOFn", (u64)io_res->start,
|
||||
flash_np);
|
||||
mtd->name = devm_kasprintf(fun->dev, GFP_KERNEL, "0x%llx.%pOFn",
|
||||
(u64)io_res->start,
|
||||
flash_np);
|
||||
if (!mtd->name) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
|
@ -192,51 +72,130 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
|
|||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
err:
|
||||
of_node_put(flash_np);
|
||||
if (ret)
|
||||
kfree(mtd->name);
|
||||
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)
|
||||
{
|
||||
struct fsl_upm_nand *fun;
|
||||
struct resource io_res;
|
||||
struct resource *io_res;
|
||||
const __be32 *prop;
|
||||
int rnb_gpio;
|
||||
int ret;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
fun = kzalloc(sizeof(*fun), GFP_KERNEL);
|
||||
fun = devm_kzalloc(&ofdev->dev, sizeof(*fun), GFP_KERNEL);
|
||||
if (!fun)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_address_to_resource(ofdev->dev.of_node, 0, &io_res);
|
||||
if (ret) {
|
||||
dev_err(&ofdev->dev, "can't get IO base\n");
|
||||
goto err1;
|
||||
}
|
||||
io_res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
|
||||
fun->io_base = devm_ioremap_resource(&ofdev->dev, io_res);
|
||||
if (IS_ERR(fun->io_base))
|
||||
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) {
|
||||
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",
|
||||
&size);
|
||||
if (!prop || size != sizeof(uint32_t)) {
|
||||
dev_err(&ofdev->dev, "can't get UPM address offset\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
return -EINVAL;
|
||||
}
|
||||
fun->upm_addr_offset = *prop;
|
||||
|
||||
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size);
|
||||
if (!prop || size != sizeof(uint32_t)) {
|
||||
dev_err(&ofdev->dev, "can't get UPM command offset\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
return -EINVAL;
|
||||
}
|
||||
fun->upm_cmd_offset = *prop;
|
||||
|
||||
|
@ -246,7 +205,7 @@ static int fun_probe(struct platform_device *ofdev)
|
|||
fun->mchip_count = size / sizeof(uint32_t);
|
||||
if (fun->mchip_count >= NAND_MAX_CHIPS) {
|
||||
dev_err(&ofdev->dev, "too much multiple chips\n");
|
||||
goto err1;
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < fun->mchip_count; 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++) {
|
||||
fun->rnb_gpio[i] = -1;
|
||||
rnb_gpio = of_get_gpio(ofdev->dev.of_node, i);
|
||||
if (rnb_gpio >= 0) {
|
||||
ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev));
|
||||
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) {
|
||||
fun->rnb_gpio[i] = devm_gpiod_get_index_optional(&ofdev->dev,
|
||||
NULL, i,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(fun->rnb_gpio[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);
|
||||
if (prop)
|
||||
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;
|
||||
}
|
||||
|
||||
nand_controller_init(&fun->base);
|
||||
fun->base.ops = &fun_ops;
|
||||
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)
|
||||
goto err2;
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, fun);
|
||||
|
||||
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)
|
||||
|
@ -319,20 +241,11 @@ static int fun_remove(struct platform_device *ofdev)
|
|||
struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
|
||||
struct nand_chip *chip = &fun->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
ret = mtd_device_unregister(mtd);
|
||||
WARN_ON(ret);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -327,8 +327,8 @@ static int fsmc_calc_timings(struct fsmc_nand_data *host,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsmc_setup_data_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int fsmc_setup_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct fsmc_nand_data *host = nand_to_fsmc(nand);
|
||||
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 = {
|
||||
.attach_chip = fsmc_nand_attach_chip,
|
||||
.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/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
struct gpiomtd {
|
||||
struct nand_controller base;
|
||||
void __iomem *io;
|
||||
void __iomem *io_sync;
|
||||
struct nand_chip nand_chip;
|
||||
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) {}
|
||||
#endif
|
||||
|
||||
static void gpio_nand_cmd_ctrl(struct nand_chip *chip, int cmd,
|
||||
unsigned int ctrl)
|
||||
static int gpio_nand_exec_instr(struct nand_chip *chip,
|
||||
const struct nand_op_instr *instr)
|
||||
{
|
||||
struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip));
|
||||
unsigned int i;
|
||||
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
|
||||
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));
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
}
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
gpiod_set_value(gpiomtd->cle, 1);
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
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);
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
case NAND_OP_ADDR_INSTR:
|
||||
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));
|
||||
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
|
||||
static const struct of_device_id gpio_nand_id_table[] = {
|
||||
{ .compatible = "gpio-control-nand" },
|
||||
|
@ -225,9 +293,9 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
|||
chip = &gpiomtd->nand_chip;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
chip->legacy.IO_ADDR_R = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(chip->legacy.IO_ADDR_R))
|
||||
return PTR_ERR(chip->legacy.IO_ADDR_R);
|
||||
gpiomtd->io = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(gpiomtd->io))
|
||||
return PTR_ERR(gpiomtd->io);
|
||||
|
||||
res = gpio_nand_get_io_sync(pdev);
|
||||
if (res) {
|
||||
|
@ -269,17 +337,15 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
|||
ret = PTR_ERR(gpiomtd->rdy);
|
||||
goto out_ce;
|
||||
}
|
||||
/* Using RDY pin */
|
||||
if (gpiomtd->rdy)
|
||||
chip->legacy.dev_ready = gpio_nand_devready;
|
||||
|
||||
nand_controller_init(&gpiomtd->base);
|
||||
gpiomtd->base.ops = &gpio_nand_ops;
|
||||
|
||||
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.algo = NAND_ECC_HAMMING;
|
||||
chip->options = gpiomtd->plat.options;
|
||||
chip->legacy.chip_delay = gpiomtd->plat.chip_delay;
|
||||
chip->legacy.cmd_ctrl = gpio_nand_cmd_ctrl;
|
||||
chip->controller = &gpiomtd->base;
|
||||
|
||||
mtd = nand_to_mtd(chip);
|
||||
mtd->dev.parent = dev;
|
||||
|
|
|
@ -736,8 +736,8 @@ static void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
|
|||
udelay(dll_wait_time_us);
|
||||
}
|
||||
|
||||
static int gpmi_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
||||
const struct nand_sdr_timings *sdr;
|
||||
|
@ -2400,7 +2400,7 @@ unmap:
|
|||
|
||||
static const struct nand_controller_ops gpmi_nand_controller_ops = {
|
||||
.attach_chip = gpmi_nand_attach_chip,
|
||||
.setup_data_interface = gpmi_setup_data_interface,
|
||||
.setup_interface = gpmi_setup_interface,
|
||||
.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
|
||||
* ECC, otherwise we will get in trouble when doing subpage writes.
|
||||
*/
|
||||
if (memcmp(ecc_code, empty_block_ecc, ARRAY_SIZE(empty_block_ecc)) == 0)
|
||||
memset(ecc_code, 0xff, ARRAY_SIZE(empty_block_ecc));
|
||||
if (memcmp(ecc_code, empty_block_ecc, sizeof(empty_block_ecc)) == 0)
|
||||
memset(ecc_code, 0xff, sizeof(empty_block_ecc));
|
||||
|
||||
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
|
||||
* @id: manufacturer ID code of device.
|
||||
* @ops: manufacturer operations
|
||||
*/
|
||||
struct nand_manufacturer {
|
||||
struct nand_manufacturer_desc {
|
||||
int id;
|
||||
char *name;
|
||||
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;
|
||||
|
||||
/* 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_markbad_bbm(struct nand_chip *chip, loff_t ofs);
|
||||
int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
|
||||
int allowbbt);
|
||||
int onfi_fill_data_interface(struct nand_chip *chip,
|
||||
enum nand_data_interface_type type,
|
||||
int 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);
|
||||
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_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
|
||||
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);
|
||||
}
|
||||
|
||||
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 ||
|
||||
!chip->controller->ops->setup_data_interface)
|
||||
!chip->controller->ops->setup_interface)
|
||||
return false;
|
||||
|
||||
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,
|
||||
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_nfc *nfc = to_marvell_nfc(chip->controller);
|
||||
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;
|
||||
|
||||
ret = marvell_nfc_wait_op(chip,
|
||||
PSEC_TO_MSEC(chip->data_interface.timings.sdr.tPROG_max));
|
||||
PSEC_TO_MSEC(sdr->tPROG_max));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1562,6 +1564,8 @@ static int marvell_nfc_hw_ecc_bch_write_page(struct nand_chip *chip,
|
|||
const u8 *buf,
|
||||
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);
|
||||
const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
|
||||
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);
|
||||
}
|
||||
|
||||
ret = marvell_nfc_wait_op(chip,
|
||||
PSEC_TO_MSEC(chip->data_interface.timings.sdr.tPROG_max));
|
||||
ret = marvell_nfc_wait_op(chip, PSEC_TO_MSEC(sdr->tPROG_max));
|
||||
|
||||
marvell_nfc_disable_hw_ecc(chip);
|
||||
|
||||
|
@ -2305,9 +2308,8 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
|||
.pattern = bbt_mirror_pattern
|
||||
};
|
||||
|
||||
static int marvell_nfc_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface
|
||||
*conf)
|
||||
static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
|
||||
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 = {
|
||||
.attach_chip = marvell_nand_attach_chip,
|
||||
.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,
|
||||
|
@ -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
|
||||
* ->setup_data_interface() is called.
|
||||
* ->setup_interface() is called.
|
||||
*/
|
||||
marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0);
|
||||
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,
|
||||
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 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 cs = nfc->param.chip_select;
|
||||
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,
|
||||
int page, int raw)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
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 *nfc = nand_get_controller_data(nand);
|
||||
int data_len, info_len;
|
||||
|
@ -1097,8 +1097,8 @@ static int meson_chip_buffer_init(struct nand_chip *nand)
|
|||
}
|
||||
|
||||
static
|
||||
int meson_nfc_setup_data_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
int meson_nfc_setup_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
|
||||
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 = {
|
||||
.attach_chip = meson_nand_attach_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,
|
||||
};
|
||||
|
||||
|
|
|
@ -387,44 +387,6 @@ static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
|
|||
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)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
|
||||
static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int mtk_nfc_exec_instr(struct nand_chip *chip,
|
||||
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);
|
||||
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;
|
||||
int ret;
|
||||
|
||||
mtk_nfc_select_target(chip, chip->cur_cs);
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
|
||||
if (!raw) {
|
||||
|
@ -920,6 +951,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
u8 *buf;
|
||||
int rc;
|
||||
|
||||
mtk_nfc_select_target(chip, chip->cur_cs);
|
||||
start = data_offs / 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 = {
|
||||
.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,
|
||||
|
@ -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->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 */
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
|
|
|
@ -137,8 +137,8 @@ struct mxc_nand_devtype_data {
|
|||
u32 (*get_ecc_status)(struct mxc_nand_host *);
|
||||
const struct mtd_ooblayout_ops *ooblayout;
|
||||
void (*select_chip)(struct nand_chip *chip, int cs);
|
||||
int (*setup_data_interface)(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf);
|
||||
int (*setup_interface)(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf);
|
||||
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);
|
||||
}
|
||||
|
||||
static int mxc_nand_v2_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int mxc_nand_v2_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct mxc_nand_host *host = nand_get_controller_data(chip);
|
||||
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.
|
||||
*/
|
||||
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,
|
||||
.ooblayout = &mxc_v2_ooblayout_ops,
|
||||
.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,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 0,
|
||||
|
@ -1738,17 +1738,17 @@ static int mxcnd_attach_chip(struct nand_chip *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mxcnd_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
static int mxcnd_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
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 = {
|
||||
.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)
|
||||
|
@ -1809,7 +1809,7 @@ static int mxcnd_probe(struct platform_device *pdev)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!host->devtype_data->setup_data_interface)
|
||||
if (!host->devtype_data->setup_interface)
|
||||
this->options |= NAND_KEEP_TIMINGS;
|
||||
|
||||
if (host->devtype_data->needs_ip) {
|
||||
|
|
|
@ -451,8 +451,8 @@ static int mxic_nfc_exec_op(struct nand_chip *chip,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int mxic_nfc_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
static int mxic_nfc_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip);
|
||||
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 = {
|
||||
.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)
|
||||
|
|
|
@ -773,7 +773,7 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
|
|||
return -ENOTSUPP;
|
||||
|
||||
/* 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));
|
||||
|
||||
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
|
||||
* @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.
|
||||
*/
|
||||
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;
|
||||
|
||||
if (!nand_has_setup_data_iface(chip))
|
||||
if (!nand_controller_can_setup_interface(chip))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -927,9 +928,9 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
|
|||
* timings to timing mode 0.
|
||||
*/
|
||||
|
||||
onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
|
||||
ret = chip->controller->ops->setup_data_interface(chip, chipnr,
|
||||
&chip->data_interface);
|
||||
chip->current_interface_config = nand_get_reset_interface_config();
|
||||
ret = ops->setup_interface(chip, chipnr,
|
||||
chip->current_interface_config);
|
||||
if (ret)
|
||||
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
|
||||
* @chipnr: Internal die id
|
||||
*
|
||||
* Find and configure the best data interface and NAND timings supported by
|
||||
* the chip and the driver.
|
||||
* First tries to retrieve supported timing modes from ONFI information,
|
||||
* and if the NAND chip does not support ONFI, relies on the
|
||||
* ->onfi_timing_mode_default specified in the nand_ids table.
|
||||
* Configure what has been reported to be the best data interface and NAND
|
||||
* timings supported by the chip and the driver.
|
||||
*
|
||||
* 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] = {
|
||||
chip->onfi_timing_mode_default,
|
||||
};
|
||||
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
|
||||
int ret;
|
||||
|
||||
if (!nand_has_setup_data_iface(chip))
|
||||
if (!nand_controller_can_setup_interface(chip))
|
||||
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) */
|
||||
if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
|
||||
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 */
|
||||
ret = chip->controller->ops->setup_data_interface(chip, chipnr,
|
||||
&chip->data_interface);
|
||||
ret = ops->setup_interface(chip, chipnr, chip->best_interface_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Check the mode has been accepted by the chip, if supported */
|
||||
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);
|
||||
nand_select_target(chip, chipnr);
|
||||
|
@ -987,12 +995,15 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
|
|||
if (ret)
|
||||
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",
|
||||
chip->onfi_timing_mode_default);
|
||||
chip->best_interface_config->timings.mode);
|
||||
goto err_reset_chip;
|
||||
}
|
||||
|
||||
update_interface_config:
|
||||
chip->current_interface_config = chip->best_interface_config;
|
||||
|
||||
return 0;
|
||||
|
||||
err_reset_chip:
|
||||
|
@ -1000,7 +1011,7 @@ err_reset_chip:
|
|||
* Fallback to mode 0 if the chip explicitly did not ack the chosen
|
||||
* timing mode.
|
||||
*/
|
||||
nand_reset_data_interface(chip, chipnr);
|
||||
nand_reset_interface(chip, chipnr);
|
||||
nand_select_target(chip, chipnr);
|
||||
nand_reset_op(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
|
||||
*
|
||||
* Find the best data interface and NAND timings supported by the chip
|
||||
* and the driver.
|
||||
* First tries to retrieve supported timing modes from ONFI information,
|
||||
* and if the NAND chip does not support ONFI, relies on the
|
||||
* ->onfi_timing_mode_default specified in the nand_ids table. After this
|
||||
* function nand_chip->data_interface is initialized with the best timing mode
|
||||
* available.
|
||||
* and the driver. Eventually let the NAND manufacturer driver propose his own
|
||||
* set of timings.
|
||||
*
|
||||
* After this function nand_chip->interface_config is initialized with the best
|
||||
* timing mode available.
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_init_data_interface(struct nand_chip *chip)
|
||||
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;
|
||||
|
||||
/*
|
||||
* First try to identify the best timings from ONFI parameters and
|
||||
* if the NAND does not support ONFI, fallback to the default ONFI
|
||||
* timing mode.
|
||||
*/
|
||||
if (chip->parameters.onfi) {
|
||||
modes = chip->parameters.onfi->async_timing_mode;
|
||||
} else {
|
||||
if (!chip->onfi_timing_mode_default)
|
||||
return 0;
|
||||
iface = kzalloc(sizeof(*iface), GFP_KERNEL);
|
||||
if (!iface)
|
||||
return -ENOMEM;
|
||||
|
||||
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--) {
|
||||
ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode);
|
||||
if (ret)
|
||||
continue;
|
||||
if (ret)
|
||||
kfree(iface);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 len)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
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];
|
||||
struct nand_op_instr instrs[] = {
|
||||
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)
|
||||
{
|
||||
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];
|
||||
struct nand_op_instr instrs[] = {
|
||||
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)) {
|
||||
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[] = {
|
||||
NAND_OP_CMD(NAND_CMD_PARAM, 0),
|
||||
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)) {
|
||||
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] = {};
|
||||
struct nand_op_instr instrs[] = {
|
||||
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 len, bool prog)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
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] = {};
|
||||
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)) {
|
||||
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[] = {
|
||||
NAND_OP_CMD(NAND_CMD_PAGEPROG,
|
||||
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)) {
|
||||
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];
|
||||
struct nand_op_instr instrs[] = {
|
||||
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)) {
|
||||
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[] = {
|
||||
NAND_OP_CMD(NAND_CMD_READID, 0),
|
||||
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)) {
|
||||
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[] = {
|
||||
NAND_OP_CMD(NAND_CMD_STATUS,
|
||||
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)) {
|
||||
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 };
|
||||
struct nand_op_instr instrs[] = {
|
||||
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)) {
|
||||
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[] = {
|
||||
NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0),
|
||||
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)) {
|
||||
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[] = {
|
||||
NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0),
|
||||
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)) {
|
||||
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[] = {
|
||||
NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
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
|
||||
*
|
||||
* Save the timings data structure, then apply SDR timings mode 0 (see
|
||||
* nand_reset_data_interface for details), do the reset operation, and
|
||||
* apply back the previous timings.
|
||||
* nand_reset_interface for details), do the reset operation, and apply
|
||||
* back the previous timings.
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
int nand_reset(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
struct nand_data_interface saved_data_intf = chip->data_interface;
|
||||
int ret;
|
||||
|
||||
ret = nand_reset_data_interface(chip, chipnr);
|
||||
ret = nand_reset_interface(chip, chipnr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -2505,18 +2546,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
ret = nand_setup_interface(chip, chipnr);
|
||||
if (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)
|
||||
return -EINVAL;
|
||||
|
||||
if (!chip->setup_read_retry)
|
||||
if (!chip->ops.setup_read_retry)
|
||||
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)
|
||||
|
@ -3228,7 +3258,7 @@ static void nand_wait_readrdy(struct nand_chip *chip)
|
|||
if (!(chip->options & NAND_NEED_READRDY))
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -4462,8 +4492,8 @@ static int nand_suspend(struct mtd_info *mtd)
|
|||
int ret = 0;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspend)
|
||||
ret = chip->suspend(chip);
|
||||
if (chip->ops.suspend)
|
||||
ret = chip->ops.suspend(chip);
|
||||
if (!ret)
|
||||
chip->suspended = 1;
|
||||
mutex_unlock(&chip->lock);
|
||||
|
@ -4481,8 +4511,8 @@ static void nand_resume(struct mtd_info *mtd)
|
|||
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspended) {
|
||||
if (chip->resume)
|
||||
chip->resume(chip);
|
||||
if (chip->ops.resume)
|
||||
chip->ops.resume(chip);
|
||||
chip->suspended = 0;
|
||||
} else {
|
||||
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);
|
||||
|
||||
if (!chip->lock_area)
|
||||
if (!chip->ops.lock_area)
|
||||
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);
|
||||
|
||||
if (!chip->unlock_area)
|
||||
if (!chip->ops.unlock_area)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return chip->unlock_area(chip, ofs, len);
|
||||
return chip->ops.unlock_area(chip, ofs, len);
|
||||
}
|
||||
|
||||
/* Set default functions */
|
||||
|
@ -4743,8 +4773,6 @@ static bool find_full_id_nand(struct nand_chip *chip,
|
|||
chip->options |= type->options;
|
||||
chip->base.eccreq.strength = NAND_ECC_STRENGTH(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);
|
||||
if (!chip->parameters.model)
|
||||
|
@ -4810,9 +4838,9 @@ static void nand_manufacturer_cleanup(struct nand_chip *chip)
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const struct nand_manufacturer *manufacturer;
|
||||
const struct nand_manufacturer_desc *manufacturer_desc;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
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));
|
||||
|
||||
/* Try to identify manufacturer */
|
||||
manufacturer = nand_get_manufacturer(maf_id);
|
||||
chip->manufacturer.desc = manufacturer;
|
||||
manufacturer_desc = nand_get_manufacturer_desc(maf_id);
|
||||
chip->manufacturer.desc = manufacturer_desc;
|
||||
|
||||
if (!type)
|
||||
type = nand_flash_ids;
|
||||
|
@ -4957,7 +4985,7 @@ ident_done:
|
|||
*/
|
||||
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
|
||||
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);
|
||||
pr_warn("bus width %d instead of %d bits\n", busw ? 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",
|
||||
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);
|
||||
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",
|
||||
|
@ -5185,7 +5213,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
|||
mutex_init(&chip->lock);
|
||||
|
||||
/* 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);
|
||||
if (ret)
|
||||
|
@ -5972,16 +6000,16 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||
if (!mtd->bitflip_threshold)
|
||||
mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
|
||||
|
||||
/* Initialize the ->data_interface field. */
|
||||
ret = nand_init_data_interface(chip);
|
||||
/* Find the fastest data interface for this chip */
|
||||
ret = nand_choose_interface_config(chip);
|
||||
if (ret)
|
||||
goto err_nanddev_cleanup;
|
||||
|
||||
/* Enter fastest possible mode on all dies. */
|
||||
for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
|
||||
ret = nand_setup_data_interface(chip, i);
|
||||
ret = nand_setup_interface(chip, i);
|
||||
if (ret)
|
||||
goto err_nanddev_cleanup;
|
||||
goto err_free_interface_config;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
ret = nand_create_bbt(chip);
|
||||
if (ret)
|
||||
goto err_nanddev_cleanup;
|
||||
goto err_free_interface_config;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_interface_config:
|
||||
kfree(chip->best_interface_config);
|
||||
|
||||
err_nanddev_cleanup:
|
||||
nanddev_cleanup(&chip->base);
|
||||
|
@ -6088,6 +6118,9 @@ void nand_cleanup(struct nand_chip *chip)
|
|||
& NAND_BBT_DYNAMICSTRUCT)
|
||||
kfree(chip->badblock_pattern);
|
||||
|
||||
/* Free the data interface */
|
||||
kfree(chip->best_interface_config);
|
||||
|
||||
/* Free manufacturer priv data. */
|
||||
nand_manufacturer_cleanup(chip);
|
||||
|
||||
|
|
|
@ -1226,7 +1226,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
|
|||
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.
|
||||
*/
|
||||
if (!td) {
|
||||
|
|
|
@ -337,7 +337,7 @@ static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip,
|
|||
rr->nregs = nregs;
|
||||
rr->regs = hynix_1xnm_mlc_read_retry_regs;
|
||||
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;
|
||||
|
||||
out:
|
||||
|
@ -673,6 +673,15 @@ static void hynix_nand_cleanup(struct nand_chip *chip)
|
|||
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)
|
||||
{
|
||||
struct hynix_nand *hynix;
|
||||
|
@ -689,6 +698,11 @@ static int hynix_nand_init(struct nand_chip *chip)
|
|||
|
||||
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);
|
||||
if (ret)
|
||||
hynix_nand_cleanup(chip);
|
||||
|
|
|
@ -28,8 +28,7 @@ struct nand_flash_dev nand_flash_ids[] = {
|
|||
*/
|
||||
{"TC58NVG0S3E 1G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
|
||||
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512),
|
||||
2 },
|
||||
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
|
||||
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
||||
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",
|
||||
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
|
||||
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 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
|
||||
|
@ -166,7 +168,7 @@ struct nand_flash_dev nand_flash_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_ATO, "ATO"},
|
||||
{NAND_MFR_EON, "Eon"},
|
||||
|
@ -186,20 +188,20 @@ static const struct nand_manufacturer nand_manufacturers[] = {
|
|||
};
|
||||
|
||||
/**
|
||||
* nand_get_manufacturer - Get manufacturer information from the manufacturer
|
||||
* ID
|
||||
* nand_get_manufacturer_desc - Get manufacturer information from the
|
||||
* 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.
|
||||
*/
|
||||
const struct nand_manufacturer *nand_get_manufacturer(u8 id)
|
||||
const struct nand_manufacturer_desc *nand_get_manufacturer_desc(u8 id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nand_manufacturers); i++)
|
||||
if (nand_manufacturers[i].id == id)
|
||||
return &nand_manufacturers[i];
|
||||
for (i = 0; i < ARRAY_SIZE(nand_manufacturer_descs); i++)
|
||||
if (nand_manufacturer_descs[i].id == id)
|
||||
return &nand_manufacturer_descs[i];
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
* 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
|
||||
* (which should be safe for all NANDs).
|
||||
*/
|
||||
if (nand_has_setup_data_iface(chip))
|
||||
ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000);
|
||||
if (nand_controller_can_setup_interface(chip))
|
||||
ndelay(sdr->tCCS_min / 1000);
|
||||
else
|
||||
ndelay(500);
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ static void macronix_nand_onfi_init(struct nand_chip *chip)
|
|||
return;
|
||||
|
||||
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) {
|
||||
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,
|
||||
ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1);
|
||||
|
||||
chip->lock_area = mxic_nand_lock;
|
||||
chip->unlock_area = mxic_nand_unlock;
|
||||
chip->ops.lock_area = mxic_nand_lock;
|
||||
chip->ops.unlock_area = mxic_nand_unlock;
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
chip->suspend = mxic_nand_suspend;
|
||||
chip->resume = mxic_nand_resume;
|
||||
chip->ops.suspend = mxic_nand_suspend;
|
||||
chip->ops.resume = mxic_nand_resume;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
|
|
|
@ -12,7 +12,14 @@
|
|||
|
||||
#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 */
|
||||
{
|
||||
.type = NAND_SDR_IFACE,
|
||||
|
@ -20,6 +27,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
|||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 20000,
|
||||
.tALS_min = 50000,
|
||||
|
@ -63,6 +72,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
|||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 25000,
|
||||
|
@ -106,6 +117,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
|||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 15000,
|
||||
|
@ -149,6 +162,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
|||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
|
@ -192,6 +207,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
|||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
|
@ -235,6 +252,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
|||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
|
@ -273,23 +292,79 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
|||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* onfi_fill_data_interface - [NAND Interface] Initialize a data interface from
|
||||
* given ONFI mode
|
||||
* @mode: The ONFI timing mode
|
||||
*/
|
||||
int onfi_fill_data_interface(struct nand_chip *chip,
|
||||
enum nand_data_interface_type type,
|
||||
int timing_mode)
|
||||
/* All NAND chips share the same reset data interface: SDR mode 0 */
|
||||
const struct nand_interface_config *nand_get_reset_interface_config(void)
|
||||
{
|
||||
return &onfi_sdr_timings[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
if (type != NAND_SDR_IFACE)
|
||||
return -EINVAL;
|
||||
if (WARN_ON(type != NAND_SDR_IFACE))
|
||||
return;
|
||||
|
||||
if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_sdr_timings)))
|
||||
return;
|
||||
|
||||
*iface = onfi_sdr_timings[timing_mode];
|
||||
|
||||
|
@ -308,22 +383,5 @@ int onfi_fill_data_interface(struct nand_chip *chip,
|
|||
|
||||
/* nanoseconds -> picoseconds */
|
||||
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)) {
|
||||
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[] = {
|
||||
NAND_OP_CMD(TOSHIBA_NAND_CMD_ECC_STATUS_READ,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
mtd_set_pairing_scheme(mtd, &dist3_pairing_scheme);
|
||||
|
||||
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)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
|
@ -217,6 +279,12 @@ static int toshiba_nand_init(struct nand_chip *chip)
|
|||
|
||||
if (!strcmp("TC58TEG5DCLTA00", chip->parameters.model))
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -459,11 +459,13 @@ struct qcom_nand_host {
|
|||
* among different NAND controllers.
|
||||
* @ecc_modes - ecc mode for NAND
|
||||
* @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
|
||||
*/
|
||||
struct qcom_nandc_props {
|
||||
u32 ecc_modes;
|
||||
bool is_bam;
|
||||
bool is_qpic;
|
||||
u32 dev_cmd_reg_start;
|
||||
};
|
||||
|
||||
|
@ -2774,14 +2776,24 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
|||
u32 nand_ctrl;
|
||||
|
||||
/* 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),
|
||||
NAND_DEV_CMD_VLD_VAL);
|
||||
|
||||
/* enable ADM or BAM DMA */
|
||||
if (nandc->props->is_bam) {
|
||||
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 {
|
||||
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 = {
|
||||
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
|
||||
.is_bam = true,
|
||||
.is_qpic = true,
|
||||
.dev_cmd_reg_start = 0x0,
|
||||
};
|
||||
|
||||
static const struct qcom_nandc_props ipq8074_nandc_props = {
|
||||
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
|
||||
.is_bam = true,
|
||||
.is_qpic = true,
|
||||
.dev_cmd_reg_start = 0x7000,
|
||||
};
|
||||
|
||||
|
|
|
@ -808,8 +808,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int s3c2410_nand_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int s3c2410_nand_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
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 = {
|
||||
.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[] = {
|
||||
|
|
|
@ -1277,7 +1277,7 @@ static int stm32_fmc2_nfc_waitrdy(struct nand_chip *chip,
|
|||
dev_warn(nfc->dev, "Waitrdy timeout\n");
|
||||
|
||||
/* 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));
|
||||
|
||||
/* 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,
|
||||
const struct nand_data_interface *conf)
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
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 = {
|
||||
.attach_chip = stm32_fmc2_nfc_attach_chip,
|
||||
.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,
|
||||
|
|
|
@ -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) \
|
||||
_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
|
||||
|
||||
static int sunxi_nfc_setup_data_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
|
||||
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 = {
|
||||
.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,
|
||||
};
|
||||
|
||||
|
|
|
@ -113,53 +113,11 @@ struct tango_chip {
|
|||
|
||||
#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)
|
||||
{
|
||||
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)
|
||||
static void tango_select_target(struct nand_chip *chip, unsigned int cs)
|
||||
{
|
||||
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||
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->timing2, nfc->reg_base + NFC_TIMING2);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
* 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);
|
||||
int err, res, len = mtd->writesize;
|
||||
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
if (oob_required)
|
||||
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 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 */
|
||||
if (oob_required)
|
||||
return -ENOTSUPP;
|
||||
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
writel_relaxed(0xffffffff, nfc->mem_base + METADATA);
|
||||
err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
status = chip->legacy.waitfunc(chip);
|
||||
if (status & NAND_STATUS_FAIL)
|
||||
return -EIO;
|
||||
timings = nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
err = tango_waitrdy(chip, PSEC_TO_MSEC(timings->tR_max));
|
||||
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)
|
||||
|
@ -326,7 +356,9 @@ static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
|
|||
/* skip over "len" bytes */
|
||||
nand_change_read_column_op(chip, *pos, NULL, 0, false);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
@ -339,7 +371,9 @@ static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)
|
|||
/* skip over "len" bytes */
|
||||
nand_change_write_column_op(chip, *pos, NULL, 0, false);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
int oob_required, int page)
|
||||
{
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
raw_read(chip, buf, chip->oob_poi);
|
||||
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,
|
||||
int oob_required, int page)
|
||||
{
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
raw_write(chip, buf, chip->oob_poi);
|
||||
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)
|
||||
{
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
raw_read(chip, NULL, chip->oob_poi);
|
||||
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)
|
||||
{
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
raw_write(chip, NULL, chip->oob_poi);
|
||||
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,
|
||||
const struct nand_data_interface *conf)
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
|
||||
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 = {
|
||||
.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)
|
||||
|
@ -562,12 +601,6 @@ static int chip_init(struct device *dev, struct device_node *np)
|
|||
ecc = &chip->ecc;
|
||||
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 |
|
||||
NAND_NO_SUBPAGE_WRITE |
|
||||
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);
|
||||
}
|
||||
|
||||
static int tegra_nand_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int tegra_nand_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
|
||||
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 = {
|
||||
.attach_chip = &tegra_nand_attach_chip,
|
||||
.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,
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
#include <linux/mtd/partitions.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_MAGIC_OFFSET 0x4e0
|
||||
|
@ -32,28 +37,15 @@
|
|||
#define STR_NULL_TERMINATE(x) \
|
||||
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;
|
||||
size_t retlen;
|
||||
int ret = 0;
|
||||
|
||||
ret = mtd_read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen,
|
||||
(void *)buf);
|
||||
buf[retlen] = 0;
|
||||
#ifdef CONFIG_MIPS
|
||||
ret = (fw_arg3 == CFE_EPTSEAL);
|
||||
#endif /* CONFIG_MIPS */
|
||||
|
||||
if (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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
if (bcm63xx_detect_cfe(master))
|
||||
if (!bcm63xx_detect_cfe())
|
||||
return -EINVAL;
|
||||
|
||||
nvram = vzalloc(sizeof(*nvram));
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
struct nand_device;
|
||||
|
||||
/**
|
||||
* struct nand_memory_organization - Memory organization structure
|
||||
* @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
|
||||
* @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 step_size;
|
||||
};
|
||||
|
@ -133,8 +135,6 @@ struct nand_bbt {
|
|||
unsigned long *cache;
|
||||
};
|
||||
|
||||
struct nand_device;
|
||||
|
||||
/**
|
||||
* struct nand_ops - NAND operations
|
||||
* @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 mtd_info mtd;
|
||||
struct nand_memory_organization memorg;
|
||||
struct nand_ecc_req eccreq;
|
||||
struct nand_ecc_props eccreq;
|
||||
struct nand_row_converter rowconv;
|
||||
struct nand_bbt bbt;
|
||||
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
|
||||
*/
|
||||
enum nand_data_interface_type {
|
||||
enum nand_interface_type {
|
||||
NAND_SDR_IFACE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_data_interface - NAND interface timing
|
||||
* struct nand_interface_config - NAND interface timing
|
||||
* @type: type of the timing
|
||||
* @timings: The timing information
|
||||
* @timings.mode: Timing mode as defined in the specification
|
||||
* @timings.sdr: Use it when @type is %NAND_SDR_IFACE.
|
||||
*/
|
||||
struct nand_data_interface {
|
||||
enum nand_data_interface_type type;
|
||||
struct nand_interface_config {
|
||||
enum nand_interface_type type;
|
||||
struct nand_timings {
|
||||
unsigned int mode;
|
||||
union {
|
||||
|
@ -521,7 +521,7 @@ struct nand_data_interface {
|
|||
* @conf: The data interface
|
||||
*/
|
||||
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)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -944,11 +944,10 @@ static inline void nand_op_trace(const char *prefix,
|
|||
* This method replaces chip->legacy.cmdfunc(),
|
||||
* chip->legacy.{read,write}_{buf,byte,word}(),
|
||||
* chip->legacy.dev_ready() and chip->legacy.waifunc().
|
||||
* @setup_data_interface: setup the data interface and timing. If
|
||||
* chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this
|
||||
* means the configuration should not be applied but
|
||||
* only checked.
|
||||
* This hook is optional.
|
||||
* @setup_interface: setup the data interface and timing. If chipnr is set to
|
||||
* %NAND_DATA_IFACE_CHECK_ONLY this means the configuration
|
||||
* should not be applied but only checked.
|
||||
* This hook is optional.
|
||||
*/
|
||||
struct nand_controller_ops {
|
||||
int (*attach_chip)(struct nand_chip *chip);
|
||||
|
@ -956,8 +955,8 @@ struct nand_controller_ops {
|
|||
int (*exec_op)(struct nand_chip *chip,
|
||||
const struct nand_operation *op,
|
||||
bool check_only);
|
||||
int (*setup_data_interface)(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf);
|
||||
int (*setup_interface)(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1028,140 +1027,138 @@ struct nand_legacy {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct nand_chip - NAND Private Flash Chip Data
|
||||
* @base: Inherit from the generic NAND device
|
||||
* @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
|
||||
* avoid using them.
|
||||
* @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 - NAND chip operations
|
||||
* @suspend: Suspend operation
|
||||
* @resume: Resume operation
|
||||
* @lock_area: Lock operation
|
||||
* @unlock_area: Unlock operation
|
||||
* @setup_read_retry: Set the read-retry mode (mostly needed for MLC NANDs)
|
||||
* @choose_interface_config: Choose the best interface configuration
|
||||
*/
|
||||
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_device base;
|
||||
|
||||
struct nand_id id;
|
||||
struct nand_parameters parameters;
|
||||
struct nand_manufacturer manufacturer;
|
||||
struct nand_chip_ops ops;
|
||||
struct nand_legacy legacy;
|
||||
|
||||
int (*setup_read_retry)(struct nand_chip *chip, int retry_mode);
|
||||
|
||||
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 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;
|
||||
int phys_erase_shift;
|
||||
int bbt_erase_shift;
|
||||
int chip_shift;
|
||||
int pagemask;
|
||||
/* Device internal layout */
|
||||
unsigned int page_shift;
|
||||
unsigned int phys_erase_shift;
|
||||
unsigned int chip_shift;
|
||||
unsigned int pagemask;
|
||||
unsigned int subpagesize;
|
||||
|
||||
/* Buffers */
|
||||
u8 *data_buf;
|
||||
|
||||
u8 *oob_poi;
|
||||
struct {
|
||||
unsigned int bitflips;
|
||||
int page;
|
||||
} 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;
|
||||
|
||||
uint8_t *bbt;
|
||||
struct nand_bbt_descr *bbt_td;
|
||||
struct nand_bbt_descr *bbt_md;
|
||||
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
/* Internals */
|
||||
struct mutex lock;
|
||||
unsigned int suspended : 1;
|
||||
int cur_cs;
|
||||
int read_retries;
|
||||
|
||||
/* Externals */
|
||||
struct nand_controller *controller;
|
||||
struct nand_ecc_ctrl ecc;
|
||||
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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
* For example, the "4bit ECC for each 512Byte" can be set with
|
||||
* 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 {
|
||||
char *name;
|
||||
|
@ -1285,7 +1289,6 @@ struct nand_flash_dev {
|
|||
uint16_t strength_ds;
|
||||
uint16_t step_ds;
|
||||
} ecc;
|
||||
int onfi_timing_mode_default;
|
||||
};
|
||||
|
||||
int nand_create_bbt(struct nand_chip *chip);
|
||||
|
|
|
@ -309,7 +309,7 @@ struct spinand_info {
|
|||
struct spinand_devid devid;
|
||||
u32 flags;
|
||||
struct nand_memory_organization memorg;
|
||||
struct nand_ecc_req eccreq;
|
||||
struct nand_ecc_props eccreq;
|
||||
struct spinand_ecc_info eccinfo;
|
||||
struct {
|
||||
const struct spinand_op_variants *read_cache;
|
||||
|
|
Loading…
Reference in New Issue