mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
MTD updates for 4.9-rc1
NAND: * Add the infrastructure to automate NAND timings configuration * Provide a generic DT property to maximize ECC strength * Some refactoring in the core bad block table handling, to help with improving some of the logic in error cases. * Minor cleanups and fixes MTD: * Add APIs for handling page pairing; this is necessary for reliably supporting MLC and TLC NAND flash, where paired-page disturbance affects reliability. Upper layers (e.g., UBI) should make use of these in the near future. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJX+cr9AAoJEFySrpd9RFgtSmcP/AvaNRXlrmKbZKg07kpJj3Ja XtxhftUwz7ncbjls99TD6ObGxWThIJ8U3oLsI5yoofJWiik5KaUk4jXUIVkGF5hm m1cDUX4biCwctdJzG03jboquFgwKP/atxxFCvEigauW3EafmUL4KrkrQ/bqOu7qN TDDyDL2K+v96lR2lYhCxWMZHcwK2ORGxbxdxfTqVE/NMLk217gHcrJEfJISPodfb A9dU/h7gLYF49E5L04Cko1I5HTnyhGPjQGIB/h8dIUlxtrzy1NRGG3IYo5gkdbve 7yRSzbQB0jokcFdz1kg2SLXJZRArs9pYWUkFGGnYFaDGuFentyySaKgs+SO9gJHG wY48IL+RFlR0PF2PKVSdXLf9vgcjoVg9Oi2X5Ap4QJDZaTQqf0P0uz4aRTUbphQx /zY6X4Z6DWUXmLncz2tJ+ruwGoEZaUdXvX3/2ov0UnDjZ+w8hGuNNscE6xrrnKGf S9qiGOkxamS1Sg+jy2IWb/KBkkZgDXRkt02HecPJtV6kkA0fyLe281FiQU2/BHsb +aPA2zavaNMY+UGSkPci2kMZ0lMuIWxTDmH1L1XnscLsowHGrPd9D04zdeZNEP74 mSnMrldlCt8xWD3xKV6Knh9AgPkXCB9MrsumrG9/RTpplnvEAPfic2YU7hRftKkn BJZXNhX1pg4qqhcHT0O5 =/PuY -----END PGP SIGNATURE----- Merge tag 'for-linus-20161008' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: "I've not been very active this cycle, so these are mostly from Boris, for the NAND flash subsystem. NAND: - Add the infrastructure to automate NAND timings configuration - Provide a generic DT property to maximize ECC strength - Some refactoring in the core bad block table handling, to help with improving some of the logic in error cases. - Minor cleanups and fixes MTD: - Add APIs for handling page pairing; this is necessary for reliably supporting MLC and TLC NAND flash, where paired-page disturbance affects reliability. Upper layers (e.g., UBI) should make use of these in the near future" * tag 'for-linus-20161008' of git://git.infradead.org/linux-mtd: (35 commits) mtd: nand: fix trivial spelling error mtdpart: Propagate _get/put_device() mtd: nand: Provide nand_cleanup() function to free NAND related resources mtd: Kill the OF_MTD Kconfig option mtd: nand: mxc: Test CONFIG_OF instead of CONFIG_OF_MTD mtd: nand: Fix nand_command_lp() for 8bits opcodes mtd: nand: sunxi: Support ECC maximization mtd: nand: Support maximizing ECC when using software BCH mtd: nand: Add an option to maximize the ECC strength mtd: nand: mxc: Add timing setup for v2 controllers mtd: nand: mxc: implement onfi get/set features mtd: nand: sunxi: switch from manual to automated timing config mtd: nand: automate NAND timings selection mtd: nand: Expose data interface for ONFI mode 0 mtd: nand: Add function to convert ONFI mode to data_interface mtd: nand: convert ONFI mode into data interface mtd: nand: Introduce nand_data_interface mtd: nand: Create a NAND reset function mtd: nand: remove unnecessary 'extern' from function declarations MAINTAINERS: Add maintainer entry for Ingenic JZ4780 NAND driver ...
This commit is contained in:
commit
35ff96dfd3
@ -35,6 +35,15 @@ Optional NAND chip properties:
|
||||
- nand-ecc-step-size: integer representing the number of data bytes
|
||||
that are covered by a single ECC step.
|
||||
|
||||
- nand-ecc-maximize: boolean used to specify that you want to maximize ECC
|
||||
strength. The maximum ECC strength is both controller and
|
||||
chip dependent. The controller side has to select the ECC
|
||||
config providing the best strength and taking the OOB area
|
||||
size constraint into account.
|
||||
This is particularly useful when only the in-band area is
|
||||
used by the upper layers, and you want to make your NAND
|
||||
as reliable as possible.
|
||||
|
||||
The ECC strength and ECC step size properties define the correction capability
|
||||
of a controller. Together, they say a controller can correct "{strength} bit
|
||||
errors per {size} bytes".
|
||||
|
@ -6142,6 +6142,12 @@ M: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com>
|
||||
S: Maintained
|
||||
F: drivers/dma/dma-jz4780.c
|
||||
|
||||
INGENIC JZ4780 NAND DRIVER
|
||||
M: Harvey Hunt <harveyhuntnexus@gmail.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Maintained
|
||||
F: drivers/mtd/nand/jz4780_*
|
||||
|
||||
INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
|
||||
M: Mimi Zohar <zohar@linux.vnet.ibm.com>
|
||||
M: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
|
||||
|
@ -375,6 +375,110 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* mtd_wunit_to_pairing_info - get pairing information of a wunit
|
||||
* @mtd: pointer to new MTD device info structure
|
||||
* @wunit: write unit we are interested in
|
||||
* @info: returned pairing information
|
||||
*
|
||||
* Retrieve pairing information associated to the wunit.
|
||||
* This is mainly useful when dealing with MLC/TLC NANDs where pages can be
|
||||
* paired together, and where programming a page may influence the page it is
|
||||
* paired with.
|
||||
* The notion of page is replaced by the term wunit (write-unit) to stay
|
||||
* consistent with the ->writesize field.
|
||||
*
|
||||
* The @wunit argument can be extracted from an absolute offset using
|
||||
* mtd_offset_to_wunit(). @info is filled with the pairing information attached
|
||||
* to @wunit.
|
||||
*
|
||||
* From the pairing info the MTD user can find all the wunits paired with
|
||||
* @wunit using the following loop:
|
||||
*
|
||||
* for (i = 0; i < mtd_pairing_groups(mtd); i++) {
|
||||
* info.pair = i;
|
||||
* mtd_pairing_info_to_wunit(mtd, &info);
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
|
||||
struct mtd_pairing_info *info)
|
||||
{
|
||||
int npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd);
|
||||
|
||||
if (wunit < 0 || wunit >= npairs)
|
||||
return -EINVAL;
|
||||
|
||||
if (mtd->pairing && mtd->pairing->get_info)
|
||||
return mtd->pairing->get_info(mtd, wunit, info);
|
||||
|
||||
info->group = 0;
|
||||
info->pair = wunit;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info);
|
||||
|
||||
/**
|
||||
* mtd_wunit_to_pairing_info - get wunit from pairing information
|
||||
* @mtd: pointer to new MTD device info structure
|
||||
* @info: pairing information struct
|
||||
*
|
||||
* Returns a positive number representing the wunit associated to the info
|
||||
* struct, or a negative error code.
|
||||
*
|
||||
* This is the reverse of mtd_wunit_to_pairing_info(), and can help one to
|
||||
* iterate over all wunits of a given pair (see mtd_wunit_to_pairing_info()
|
||||
* doc).
|
||||
*
|
||||
* It can also be used to only program the first page of each pair (i.e.
|
||||
* page attached to group 0), which allows one to use an MLC NAND in
|
||||
* software-emulated SLC mode:
|
||||
*
|
||||
* info.group = 0;
|
||||
* npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd);
|
||||
* for (info.pair = 0; info.pair < npairs; info.pair++) {
|
||||
* wunit = mtd_pairing_info_to_wunit(mtd, &info);
|
||||
* mtd_write(mtd, mtd_wunit_to_offset(mtd, blkoffs, wunit),
|
||||
* mtd->writesize, &retlen, buf + (i * mtd->writesize));
|
||||
* }
|
||||
*/
|
||||
int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
|
||||
const struct mtd_pairing_info *info)
|
||||
{
|
||||
int ngroups = mtd_pairing_groups(mtd);
|
||||
int npairs = mtd_wunit_per_eb(mtd) / ngroups;
|
||||
|
||||
if (!info || info->pair < 0 || info->pair >= npairs ||
|
||||
info->group < 0 || info->group >= ngroups)
|
||||
return -EINVAL;
|
||||
|
||||
if (mtd->pairing && mtd->pairing->get_wunit)
|
||||
return mtd->pairing->get_wunit(mtd, info);
|
||||
|
||||
return info->pair;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_pairing_info_to_wunit);
|
||||
|
||||
/**
|
||||
* mtd_pairing_groups - get the number of pairing groups
|
||||
* @mtd: pointer to new MTD device info structure
|
||||
*
|
||||
* Returns the number of pairing groups.
|
||||
*
|
||||
* This number is usually equal to the number of bits exposed by a single
|
||||
* cell, and can be used in conjunction with mtd_pairing_info_to_wunit()
|
||||
* to iterate over all pages of a given pair.
|
||||
*/
|
||||
int mtd_pairing_groups(struct mtd_info *mtd)
|
||||
{
|
||||
if (!mtd->pairing || !mtd->pairing->ngroups)
|
||||
return 1;
|
||||
|
||||
return mtd->pairing->ngroups;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_pairing_groups);
|
||||
|
||||
/**
|
||||
* add_mtd_device - register an MTD device
|
||||
* @mtd: pointer to new MTD device info structure
|
||||
|
@ -317,6 +317,18 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int part_get_device(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part = mtd_to_part(mtd);
|
||||
return part->master->_get_device(part->master);
|
||||
}
|
||||
|
||||
static void part_put_device(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part = mtd_to_part(mtd);
|
||||
part->master->_put_device(part->master);
|
||||
}
|
||||
|
||||
static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
@ -397,6 +409,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
|
||||
slave->mtd.oobsize = master->oobsize;
|
||||
slave->mtd.oobavail = master->oobavail;
|
||||
slave->mtd.subpage_sft = master->subpage_sft;
|
||||
slave->mtd.pairing = master->pairing;
|
||||
|
||||
slave->mtd.name = name;
|
||||
slave->mtd.owner = master->owner;
|
||||
@ -463,6 +476,12 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
|
||||
slave->mtd._block_isbad = part_block_isbad;
|
||||
if (master->_block_markbad)
|
||||
slave->mtd._block_markbad = part_block_markbad;
|
||||
|
||||
if (master->_get_device)
|
||||
slave->mtd._get_device = part_get_device;
|
||||
if (master->_put_device)
|
||||
slave->mtd._put_device = part_put_device;
|
||||
|
||||
slave->mtd._erase = part_erase;
|
||||
slave->master = master;
|
||||
slave->offset = part->offset;
|
||||
|
@ -88,11 +88,11 @@ config MTD_NAND_AMS_DELTA
|
||||
Support for NAND flash on Amstrad E3 (Delta).
|
||||
|
||||
config MTD_NAND_OMAP2
|
||||
tristate "NAND Flash device on OMAP2, OMAP3 and OMAP4"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
|
||||
depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
|
||||
help
|
||||
Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4
|
||||
platforms.
|
||||
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
|
||||
and Keystone platforms.
|
||||
|
||||
config MTD_NAND_OMAP_BCH
|
||||
depends on MTD_NAND_OMAP2
|
||||
@ -428,7 +428,7 @@ config MTD_NAND_ORION
|
||||
|
||||
config MTD_NAND_FSL_ELBC
|
||||
tristate "NAND support for Freescale eLBC controllers"
|
||||
depends on PPC
|
||||
depends on FSL_SOC
|
||||
select FSL_LBC
|
||||
help
|
||||
Various Freescale chips, including the 8313, include a NAND Flash
|
||||
@ -438,7 +438,7 @@ config MTD_NAND_FSL_ELBC
|
||||
|
||||
config MTD_NAND_FSL_IFC
|
||||
tristate "NAND support for Freescale IFC controller"
|
||||
depends on MTD_NAND && (FSL_SOC || ARCH_LAYERSCAPE)
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE
|
||||
select FSL_IFC
|
||||
select MEMORY
|
||||
help
|
||||
|
@ -761,8 +761,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
spin_lock_init(&info->controller.lock);
|
||||
init_waitqueue_head(&info->controller.wq);
|
||||
nand_hw_control_init(&info->controller);
|
||||
|
||||
info->device = &pdev->dev;
|
||||
info->platform = plat;
|
||||
|
@ -1336,7 +1336,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
||||
u32 *flash_cache = (u32 *)ctrl->flash_cache;
|
||||
int i;
|
||||
|
||||
brcmnand_soc_data_bus_prepare(ctrl->soc);
|
||||
brcmnand_soc_data_bus_prepare(ctrl->soc, true);
|
||||
|
||||
/*
|
||||
* Must cache the FLASH_CACHE now, since changes in
|
||||
@ -1349,7 +1349,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
||||
*/
|
||||
flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
|
||||
|
||||
brcmnand_soc_data_bus_unprepare(ctrl->soc);
|
||||
brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
|
||||
|
||||
/* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
|
||||
if (host->hwcfg.sector_size_1k)
|
||||
@ -1565,12 +1565,12 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
brcmnand_waitfunc(mtd, chip);
|
||||
|
||||
if (likely(buf)) {
|
||||
brcmnand_soc_data_bus_prepare(ctrl->soc);
|
||||
brcmnand_soc_data_bus_prepare(ctrl->soc, false);
|
||||
|
||||
for (j = 0; j < FC_WORDS; j++, buf++)
|
||||
*buf = brcmnand_read_fc(ctrl, j);
|
||||
|
||||
brcmnand_soc_data_bus_unprepare(ctrl->soc);
|
||||
brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
|
||||
}
|
||||
|
||||
if (oob)
|
||||
@ -1815,12 +1815,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
|
||||
|
||||
if (buf) {
|
||||
brcmnand_soc_data_bus_prepare(ctrl->soc);
|
||||
brcmnand_soc_data_bus_prepare(ctrl->soc, false);
|
||||
|
||||
for (j = 0; j < FC_WORDS; j++, buf++)
|
||||
brcmnand_write_fc(ctrl, j, *buf);
|
||||
|
||||
brcmnand_soc_data_bus_unprepare(ctrl->soc);
|
||||
brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
|
||||
} else if (oob) {
|
||||
for (j = 0; j < FC_WORDS; j++)
|
||||
brcmnand_write_fc(ctrl, j, 0xffffffff);
|
||||
@ -2370,8 +2370,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
||||
|
||||
init_completion(&ctrl->done);
|
||||
init_completion(&ctrl->dma_done);
|
||||
spin_lock_init(&ctrl->controller.lock);
|
||||
init_waitqueue_head(&ctrl->controller.wq);
|
||||
nand_hw_control_init(&ctrl->controller);
|
||||
INIT_LIST_HEAD(&ctrl->host_list);
|
||||
|
||||
/* NAND register range */
|
||||
|
@ -23,19 +23,22 @@ struct dev_pm_ops;
|
||||
struct brcmnand_soc {
|
||||
bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
|
||||
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
|
||||
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare);
|
||||
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare,
|
||||
bool is_param);
|
||||
};
|
||||
|
||||
static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc)
|
||||
static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc,
|
||||
bool is_param)
|
||||
{
|
||||
if (soc && soc->prepare_data_bus)
|
||||
soc->prepare_data_bus(soc, true);
|
||||
soc->prepare_data_bus(soc, true, is_param);
|
||||
}
|
||||
|
||||
static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc)
|
||||
static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc,
|
||||
bool is_param)
|
||||
{
|
||||
if (soc && soc->prepare_data_bus)
|
||||
soc->prepare_data_bus(soc, false);
|
||||
soc->prepare_data_bus(soc, false, is_param);
|
||||
}
|
||||
|
||||
static inline u32 brcmnand_readl(void __iomem *addr)
|
||||
|
@ -74,7 +74,8 @@ static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||
spin_unlock_irqrestore(&priv->idm_lock, flags);
|
||||
}
|
||||
|
||||
static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
|
||||
static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare,
|
||||
bool is_param)
|
||||
{
|
||||
struct iproc_nand_soc *priv =
|
||||
container_of(soc, struct iproc_nand_soc, soc);
|
||||
@ -86,10 +87,19 @@ static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
|
||||
|
||||
val = brcmnand_readl(mmio);
|
||||
|
||||
if (prepare)
|
||||
val |= IPROC_NAND_APB_LE_MODE;
|
||||
else
|
||||
/*
|
||||
* In the case of BE or when dealing with NAND data, alway configure
|
||||
* the APB bus to LE mode before accessing the FIFO and back to BE mode
|
||||
* after the access is done
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) || !is_param) {
|
||||
if (prepare)
|
||||
val |= IPROC_NAND_APB_LE_MODE;
|
||||
else
|
||||
val &= ~IPROC_NAND_APB_LE_MODE;
|
||||
} else { /* when in LE accessing the parameter page, keep APB in BE */
|
||||
val &= ~IPROC_NAND_APB_LE_MODE;
|
||||
}
|
||||
|
||||
brcmnand_writel(val, mmio);
|
||||
|
||||
|
@ -1249,8 +1249,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
|
||||
nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE;
|
||||
nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA;
|
||||
nand->controller = &nand->hwcontrol;
|
||||
spin_lock_init(&nand->controller->lock);
|
||||
init_waitqueue_head(&nand->controller->wq);
|
||||
nand_hw_control_init(nand->controller);
|
||||
|
||||
/* methods */
|
||||
nand->cmdfunc = docg4_command;
|
||||
|
@ -879,8 +879,7 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
|
||||
}
|
||||
elbc_fcm_ctrl->counter++;
|
||||
|
||||
spin_lock_init(&elbc_fcm_ctrl->controller.lock);
|
||||
init_waitqueue_head(&elbc_fcm_ctrl->controller.wq);
|
||||
nand_hw_control_init(&elbc_fcm_ctrl->controller);
|
||||
fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
|
||||
} else {
|
||||
elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
|
||||
|
@ -987,8 +987,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
|
||||
ifc_nand_ctrl->addr = NULL;
|
||||
fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl;
|
||||
|
||||
spin_lock_init(&ifc_nand_ctrl->controller.lock);
|
||||
init_waitqueue_head(&ifc_nand_ctrl->controller.wq);
|
||||
nand_hw_control_init(&ifc_nand_ctrl->controller);
|
||||
} else {
|
||||
ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand;
|
||||
}
|
||||
|
@ -318,7 +318,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
geo->page_size = mtd->writesize + mtd->oobsize;
|
||||
geo->page_size = mtd->writesize + geo->metadata_size +
|
||||
(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
|
||||
geo->payload_size = mtd->writesize;
|
||||
|
||||
/*
|
||||
|
@ -368,9 +368,8 @@ static int jz4780_nand_probe(struct platform_device *pdev)
|
||||
nfc->dev = dev;
|
||||
nfc->num_banks = num_banks;
|
||||
|
||||
spin_lock_init(&nfc->controller.lock);
|
||||
nand_hw_control_init(&nfc->controller);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
init_waitqueue_head(&nfc->controller.wq);
|
||||
|
||||
ret = jz4780_nand_init_chips(nfc, pdev);
|
||||
if (ret) {
|
||||
|
@ -152,6 +152,9 @@ struct mxc_nand_devtype_data {
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*correct_data)(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc);
|
||||
int (*setup_data_interface)(struct mtd_info *mtd,
|
||||
const struct nand_data_interface *conf,
|
||||
bool check_only);
|
||||
|
||||
/*
|
||||
* On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
|
||||
@ -1012,6 +1015,82 @@ static void preset_v1(struct mtd_info *mtd)
|
||||
writew(0x4, NFC_V1_V2_WRPROT);
|
||||
}
|
||||
|
||||
static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd,
|
||||
const struct nand_data_interface *conf,
|
||||
bool check_only)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd_to_nand(mtd);
|
||||
struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
|
||||
int tRC_min_ns, tRC_ps, ret;
|
||||
unsigned long rate, rate_round;
|
||||
const struct nand_sdr_timings *timings;
|
||||
u16 config1;
|
||||
|
||||
timings = nand_get_sdr_timings(conf);
|
||||
if (IS_ERR(timings))
|
||||
return -ENOTSUPP;
|
||||
|
||||
config1 = readw(NFC_V1_V2_CONFIG1);
|
||||
|
||||
tRC_min_ns = timings->tRC_min / 1000;
|
||||
rate = 1000000000 / tRC_min_ns;
|
||||
|
||||
/*
|
||||
* For tRC < 30ns we have to use EDO mode. In this case the controller
|
||||
* does one access per clock cycle. Otherwise the controller does one
|
||||
* access in two clock cycles, thus we have to double the rate to the
|
||||
* controller.
|
||||
*/
|
||||
if (tRC_min_ns < 30) {
|
||||
rate_round = clk_round_rate(host->clk, rate);
|
||||
config1 |= NFC_V2_CONFIG1_ONE_CYCLE;
|
||||
tRC_ps = 1000000000 / (rate_round / 1000);
|
||||
} else {
|
||||
rate *= 2;
|
||||
rate_round = clk_round_rate(host->clk, rate);
|
||||
config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE;
|
||||
tRC_ps = 1000000000 / (rate_round / 1000 / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* The timing values compared against are from the i.MX25 Automotive
|
||||
* datasheet, Table 50. NFC Timing Parameters
|
||||
*/
|
||||
if (timings->tCLS_min > tRC_ps - 1000 ||
|
||||
timings->tCLH_min > tRC_ps - 2000 ||
|
||||
timings->tCS_min > tRC_ps - 1000 ||
|
||||
timings->tCH_min > tRC_ps - 2000 ||
|
||||
timings->tWP_min > tRC_ps - 1500 ||
|
||||
timings->tALS_min > tRC_ps ||
|
||||
timings->tALH_min > tRC_ps - 3000 ||
|
||||
timings->tDS_min > tRC_ps ||
|
||||
timings->tDH_min > tRC_ps - 5000 ||
|
||||
timings->tWC_min > 2 * tRC_ps ||
|
||||
timings->tWH_min > tRC_ps - 2500 ||
|
||||
timings->tRR_min > 6 * tRC_ps ||
|
||||
timings->tRP_min > 3 * tRC_ps / 2 ||
|
||||
timings->tRC_min > 2 * tRC_ps ||
|
||||
timings->tREH_min > (tRC_ps / 2) - 2500) {
|
||||
dev_dbg(host->dev, "Timing out of bounds\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (check_only)
|
||||
return 0;
|
||||
|
||||
ret = clk_set_rate(host->clk, rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writew(config1, NFC_V1_V2_CONFIG1);
|
||||
|
||||
dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round,
|
||||
config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" :
|
||||
"normal");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void preset_v2(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd_to_nand(mtd);
|
||||
@ -1239,6 +1318,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
||||
}
|
||||
}
|
||||
|
||||
static int mxc_nand_onfi_set_features(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, int addr,
|
||||
u8 *subfeature_param)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd_to_nand(mtd);
|
||||
struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
|
||||
int i;
|
||||
|
||||
if (!chip->onfi_version ||
|
||||
!(le16_to_cpu(chip->onfi_params.opt_cmd)
|
||||
& ONFI_OPT_CMD_SET_GET_FEATURES))
|
||||
return -EINVAL;
|
||||
|
||||
host->buf_start = 0;
|
||||
|
||||
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
|
||||
chip->write_byte(mtd, subfeature_param[i]);
|
||||
|
||||
memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
|
||||
host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
|
||||
mxc_do_addr_cycle(mtd, addr, -1);
|
||||
host->devtype_data->send_page(mtd, NFC_INPUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_nand_onfi_get_features(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, int addr,
|
||||
u8 *subfeature_param)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd_to_nand(mtd);
|
||||
struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
|
||||
int i;
|
||||
|
||||
if (!chip->onfi_version ||
|
||||
!(le16_to_cpu(chip->onfi_params.opt_cmd)
|
||||
& ONFI_OPT_CMD_SET_GET_FEATURES))
|
||||
return -EINVAL;
|
||||
|
||||
host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
|
||||
mxc_do_addr_cycle(mtd, addr, -1);
|
||||
host->devtype_data->send_page(mtd, NFC_OUTPUT);
|
||||
memcpy32_fromio(host->data_buf, host->main_area0, 512);
|
||||
host->buf_start = 0;
|
||||
|
||||
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
|
||||
*subfeature_param++ = chip->read_byte(mtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The generic flash bbt decriptors overlap with our ecc
|
||||
* hardware, so define some i.MX specific ones.
|
||||
@ -1327,6 +1457,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
|
||||
.ooblayout = &mxc_v2_ooblayout_ops,
|
||||
.select_chip = mxc_nand_select_chip_v2,
|
||||
.correct_data = mxc_nand_correct_data_v2_v3,
|
||||
.setup_data_interface = mxc_nand_v2_setup_data_interface,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 0,
|
||||
.regs_offset = 0x1e00,
|
||||
@ -1434,7 +1565,7 @@ static const struct platform_device_id mxcnd_devtype[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mxcnd_devtype);
|
||||
|
||||
#ifdef CONFIG_OF_MTD
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mxcnd_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx21-nand",
|
||||
@ -1513,6 +1644,8 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||
this->read_word = mxc_nand_read_word;
|
||||
this->write_buf = mxc_nand_write_buf;
|
||||
this->read_buf = mxc_nand_read_buf;
|
||||
this->onfi_set_features = mxc_nand_onfi_set_features;
|
||||
this->onfi_get_features = mxc_nand_onfi_get_features;
|
||||
|
||||
host->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(host->clk))
|
||||
@ -1533,6 +1666,8 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
this->setup_data_interface = host->devtype_data->setup_data_interface;
|
||||
|
||||
if (host->devtype_data->needs_ip) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -745,7 +745,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
||||
column >>= 1;
|
||||
chip->cmd_ctrl(mtd, column, ctrl);
|
||||
ctrl &= ~NAND_CTRL_CHANGE;
|
||||
chip->cmd_ctrl(mtd, column >> 8, ctrl);
|
||||
|
||||
/* Only output a single addr cycle for 8bits opcodes. */
|
||||
if (!nand_opcode_8bits(command))
|
||||
chip->cmd_ctrl(mtd, column >> 8, ctrl);
|
||||
}
|
||||
if (page_addr != -1) {
|
||||
chip->cmd_ctrl(mtd, page_addr, ctrl);
|
||||
@ -947,6 +950,172 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_reset_data_interface - Reset data interface and timings
|
||||
* @chip: The NAND chip
|
||||
*
|
||||
* Reset the Data interface and timings to ONFI mode 0.
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_reset_data_interface(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_data_interface *conf;
|
||||
int ret;
|
||||
|
||||
if (!chip->setup_data_interface)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The ONFI specification says:
|
||||
* "
|
||||
* To transition from NV-DDR or NV-DDR2 to the SDR data
|
||||
* interface, the host shall use the Reset (FFh) command
|
||||
* using SDR timing mode 0. A device in any timing mode is
|
||||
* required to recognize Reset (FFh) command issued in SDR
|
||||
* timing mode 0.
|
||||
* "
|
||||
*
|
||||
* Configure the data interface in SDR mode and set the
|
||||
* timings to timing mode 0.
|
||||
*/
|
||||
|
||||
conf = nand_get_default_data_interface();
|
||||
ret = chip->setup_data_interface(mtd, conf, false);
|
||||
if (ret)
|
||||
pr_err("Failed to configure data interface to SDR timing mode 0\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_setup_data_interface - Setup the best data interface and timings
|
||||
* @chip: The NAND chip
|
||||
*
|
||||
* Find and configure the best data interface and NAND timings supported by
|
||||
* the chip and the driver.
|
||||
* First tries to retrieve supported timing modes from ONFI information,
|
||||
* and if the NAND chip does not support ONFI, relies on the
|
||||
* ->onfi_timing_mode_default specified in the nand_ids table.
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_setup_data_interface(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
if (!chip->setup_data_interface || !chip->data_interface)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Ensure the timing mode has been changed on the chip side
|
||||
* before changing timings on the controller side.
|
||||
*/
|
||||
if (chip->onfi_version) {
|
||||
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
|
||||
chip->onfi_timing_mode_default,
|
||||
};
|
||||
|
||||
ret = chip->onfi_set_features(mtd, chip,
|
||||
ONFI_FEATURE_ADDR_TIMING_MODE,
|
||||
tmode_param);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = chip->setup_data_interface(mtd, chip->data_interface, false);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_init_data_interface - find the best data interface and timings
|
||||
* @chip: The NAND chip
|
||||
*
|
||||
* Find the best data interface and NAND timings supported by the chip
|
||||
* and the driver.
|
||||
* First tries to retrieve supported timing modes from ONFI information,
|
||||
* and if the NAND chip does not support ONFI, relies on the
|
||||
* ->onfi_timing_mode_default specified in the nand_ids table. After this
|
||||
* function nand_chip->data_interface is initialized with the best timing mode
|
||||
* available.
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_init_data_interface(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int modes, mode, ret;
|
||||
|
||||
if (!chip->setup_data_interface)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* First try to identify the best timings from ONFI parameters and
|
||||
* if the NAND does not support ONFI, fallback to the default ONFI
|
||||
* timing mode.
|
||||
*/
|
||||
modes = onfi_get_async_timing_mode(chip);
|
||||
if (modes == ONFI_TIMING_MODE_UNKNOWN) {
|
||||
if (!chip->onfi_timing_mode_default)
|
||||
return 0;
|
||||
|
||||
modes = GENMASK(chip->onfi_timing_mode_default, 0);
|
||||
}
|
||||
|
||||
chip->data_interface = kzalloc(sizeof(*chip->data_interface),
|
||||
GFP_KERNEL);
|
||||
if (!chip->data_interface)
|
||||
return -ENOMEM;
|
||||
|
||||
for (mode = fls(modes) - 1; mode >= 0; mode--) {
|
||||
ret = onfi_init_data_interface(chip, chip->data_interface,
|
||||
NAND_SDR_IFACE, mode);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ret = chip->setup_data_interface(mtd, chip->data_interface,
|
||||
true);
|
||||
if (!ret) {
|
||||
chip->onfi_timing_mode_default = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nand_release_data_interface(struct nand_chip *chip)
|
||||
{
|
||||
kfree(chip->data_interface);
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_reset - Reset and initialize a NAND device
|
||||
* @chip: The NAND chip
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise
|
||||
*/
|
||||
int nand_reset(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
ret = nand_reset_data_interface(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
|
||||
ret = nand_setup_data_interface(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
|
||||
* @mtd: mtd info
|
||||
@ -1025,7 +1194,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
* some operation can also clear the bit 7 of status register
|
||||
* eg. erase/program a locked block
|
||||
*/
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
nand_reset(chip);
|
||||
|
||||
/* Check, if it is write protected */
|
||||
if (nand_check_wp(mtd)) {
|
||||
@ -1084,7 +1253,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
* some operation can also clear the bit 7 of status register
|
||||
* eg. erase/program a locked block
|
||||
*/
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
nand_reset(chip);
|
||||
|
||||
/* Check, if it is write protected */
|
||||
if (nand_check_wp(mtd)) {
|
||||
@ -2162,7 +2331,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
int ret = -ENOTSUPP;
|
||||
int ret;
|
||||
|
||||
ops->retlen = 0;
|
||||
|
||||
@ -2173,24 +2342,18 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ops->mode != MTD_OPS_PLACE_OOB &&
|
||||
ops->mode != MTD_OPS_AUTO_OOB &&
|
||||
ops->mode != MTD_OPS_RAW)
|
||||
return -ENOTSUPP;
|
||||
|
||||
nand_get_device(mtd, FL_READING);
|
||||
|
||||
switch (ops->mode) {
|
||||
case MTD_OPS_PLACE_OOB:
|
||||
case MTD_OPS_AUTO_OOB:
|
||||
case MTD_OPS_RAW:
|
||||
break;
|
||||
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ops->datbuf)
|
||||
ret = nand_do_read_oob(mtd, from, ops);
|
||||
else
|
||||
ret = nand_do_read_ops(mtd, from, ops);
|
||||
|
||||
out:
|
||||
nand_release_device(mtd);
|
||||
return ret;
|
||||
}
|
||||
@ -2788,7 +2951,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
* if we don't do this. I have no clue why, but I seem to have 'fixed'
|
||||
* it in the doc2000 driver in August 1999. dwmw2.
|
||||
*/
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
nand_reset(chip);
|
||||
|
||||
/* Check, if it is write protected */
|
||||
if (nand_check_wp(mtd)) {
|
||||
@ -3191,8 +3354,7 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||
|
||||
if (!chip->controller) {
|
||||
chip->controller = &chip->hwcontrol;
|
||||
spin_lock_init(&chip->controller->lock);
|
||||
init_waitqueue_head(&chip->controller->wq);
|
||||
nand_hw_control_init(chip->controller);
|
||||
}
|
||||
|
||||
}
|
||||
@ -3829,7 +3991,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
|
||||
* after power-up.
|
||||
*/
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
nand_reset(chip);
|
||||
|
||||
/* Send the command for reading device ID */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
@ -4113,6 +4275,9 @@ static int nand_dt_init(struct nand_chip *chip)
|
||||
if (ecc_step > 0)
|
||||
chip->ecc.size = ecc_step;
|
||||
|
||||
if (of_property_read_bool(dn, "nand-ecc-maximize"))
|
||||
chip->ecc.options |= NAND_ECC_MAXIMIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4141,6 +4306,15 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
if (!mtd->name && mtd->dev.parent)
|
||||
mtd->name = dev_name(mtd->dev.parent);
|
||||
|
||||
if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) {
|
||||
/*
|
||||
* Default functions assigned for chip_select() and
|
||||
* cmdfunc() both expect cmd_ctrl() to be populated,
|
||||
* so we need to check that that's the case
|
||||
*/
|
||||
pr_err("chip.cmd_ctrl() callback is not provided");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Set the default functions */
|
||||
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
|
||||
|
||||
@ -4155,13 +4329,17 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
return PTR_ERR(type);
|
||||
}
|
||||
|
||||
ret = nand_init_data_interface(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->select_chip(mtd, -1);
|
||||
|
||||
/* Check for a chip array */
|
||||
for (i = 1; i < maxchips; i++) {
|
||||
chip->select_chip(mtd, i);
|
||||
/* See comment in nand_get_flash_type for reset */
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
nand_reset(chip);
|
||||
/* Send the command for reading device ID */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
/* Read manufacturer and device IDs */
|
||||
@ -4221,6 +4399,7 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
|
||||
ecc->write_page_raw = nand_write_page_raw;
|
||||
ecc->read_oob = nand_read_oob_std;
|
||||
ecc->write_oob = nand_write_oob_std;
|
||||
|
||||
/*
|
||||
* Board driver should supply ecc.size and ecc.strength
|
||||
* values to select how many bits are correctable.
|
||||
@ -4243,6 +4422,25 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
|
||||
}
|
||||
|
||||
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* We can only maximize ECC config when the default layout is
|
||||
* used, otherwise we don't know how many bytes can really be
|
||||
* used.
|
||||
*/
|
||||
if (mtd->ooblayout == &nand_ooblayout_lp_ops &&
|
||||
ecc->options & NAND_ECC_MAXIMIZE) {
|
||||
int steps, bytes;
|
||||
|
||||
/* Always prefer 1k blocks over 512bytes ones */
|
||||
ecc->size = 1024;
|
||||
steps = mtd->writesize / ecc->size;
|
||||
|
||||
/* Reserve 2 bytes for the BBM */
|
||||
bytes = (mtd->oobsize - 2) / steps;
|
||||
ecc->strength = bytes * 8 / fls(8 * ecc->size);
|
||||
}
|
||||
|
||||
/* See nand_bch_init() for details. */
|
||||
@ -4601,18 +4799,16 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
|
||||
EXPORT_SYMBOL(nand_scan);
|
||||
|
||||
/**
|
||||
* nand_release - [NAND Interface] Free resources held by the NAND device
|
||||
* @mtd: MTD device structure
|
||||
* nand_cleanup - [NAND Interface] Free resources held by the NAND device
|
||||
* @chip: NAND chip object
|
||||
*/
|
||||
void nand_release(struct mtd_info *mtd)
|
||||
void nand_cleanup(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_SOFT &&
|
||||
chip->ecc.algo == NAND_ECC_BCH)
|
||||
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
|
||||
|
||||
mtd_device_unregister(mtd);
|
||||
nand_release_data_interface(chip);
|
||||
|
||||
/* Free bad block table memory */
|
||||
kfree(chip->bbt);
|
||||
@ -4624,6 +4820,18 @@ void nand_release(struct mtd_info *mtd)
|
||||
& NAND_BBT_DYNAMICSTRUCT)
|
||||
kfree(chip->badblock_pattern);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_cleanup);
|
||||
|
||||
/**
|
||||
* nand_release - [NAND Interface] Unregister the MTD device and free resources
|
||||
* held by the NAND device
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
void nand_release(struct mtd_info *mtd)
|
||||
{
|
||||
mtd_device_unregister(mtd);
|
||||
nand_cleanup(mtd_to_nand(mtd));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_release);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -604,6 +604,100 @@ static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
|
||||
search_bbt(mtd, buf, md);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_bbt_block - Get the first valid eraseblock suitable to store a BBT
|
||||
* @this: the NAND device
|
||||
* @td: the BBT description
|
||||
* @md: the mirror BBT descriptor
|
||||
* @chip: the CHIP selector
|
||||
*
|
||||
* This functions returns a positive block number pointing a valid eraseblock
|
||||
* suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if
|
||||
* all blocks are already used of marked bad. If td->pages[chip] was already
|
||||
* pointing to a valid block we re-use it, otherwise we search for the next
|
||||
* valid one.
|
||||
*/
|
||||
static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
|
||||
struct nand_bbt_descr *md, int chip)
|
||||
{
|
||||
int startblock, dir, page, numblocks, i;
|
||||
|
||||
/*
|
||||
* There was already a version of the table, reuse the page. This
|
||||
* applies for absolute placement too, as we have the page number in
|
||||
* td->pages.
|
||||
*/
|
||||
if (td->pages[chip] != -1)
|
||||
return td->pages[chip] >>
|
||||
(this->bbt_erase_shift - this->page_shift);
|
||||
|
||||
numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
|
||||
if (!(td->options & NAND_BBT_PERCHIP))
|
||||
numblocks *= this->numchips;
|
||||
|
||||
/*
|
||||
* Automatic placement of the bad block table. Search direction
|
||||
* top -> down?
|
||||
*/
|
||||
if (td->options & NAND_BBT_LASTBLOCK) {
|
||||
startblock = numblocks * (chip + 1) - 1;
|
||||
dir = -1;
|
||||
} else {
|
||||
startblock = chip * numblocks;
|
||||
dir = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < td->maxblocks; i++) {
|
||||
int block = startblock + dir * i;
|
||||
|
||||
/* Check, if the block is bad */
|
||||
switch (bbt_get_entry(this, block)) {
|
||||
case BBT_BLOCK_WORN:
|
||||
case BBT_BLOCK_FACTORY_BAD:
|
||||
continue;
|
||||
}
|
||||
|
||||
page = block << (this->bbt_erase_shift - this->page_shift);
|
||||
|
||||
/* Check, if the block is used by the mirror table */
|
||||
if (!md || md->pages[chip] != page)
|
||||
return block;
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/**
|
||||
* mark_bbt_block_bad - Mark one of the block reserved for BBT bad
|
||||
* @this: the NAND device
|
||||
* @td: the BBT description
|
||||
* @chip: the CHIP selector
|
||||
* @block: the BBT block to mark
|
||||
*
|
||||
* Blocks reserved for BBT can become bad. This functions is an helper to mark
|
||||
* such blocks as bad. It takes care of updating the in-memory BBT, marking the
|
||||
* block as bad using a bad block marker and invalidating the associated
|
||||
* td->pages[] entry.
|
||||
*/
|
||||
static void mark_bbt_block_bad(struct nand_chip *this,
|
||||
struct nand_bbt_descr *td,
|
||||
int chip, int block)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
loff_t to;
|
||||
int res;
|
||||
|
||||
bbt_mark_entry(this, block, BBT_BLOCK_WORN);
|
||||
|
||||
to = (loff_t)block << this->bbt_erase_shift;
|
||||
res = this->block_markbad(mtd, to);
|
||||
if (res)
|
||||
pr_warn("nand_bbt: error %d while marking block %d bad\n",
|
||||
res, block);
|
||||
|
||||
td->pages[chip] = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_bbt - [GENERIC] (Re)write the bad block table
|
||||
* @mtd: MTD device structure
|
||||
@ -621,7 +715,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
struct erase_info einfo;
|
||||
int i, res, chip = 0;
|
||||
int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
|
||||
int bits, page, offs, numblocks, sft, sftmsk;
|
||||
int nrchips, pageoffs, ooboffs;
|
||||
uint8_t msk[4];
|
||||
uint8_t rcode = td->reserved_block_code;
|
||||
@ -652,46 +746,21 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
}
|
||||
|
||||
/* Loop through the chips */
|
||||
for (; chip < nrchips; chip++) {
|
||||
/*
|
||||
* There was already a version of the table, reuse the page
|
||||
* This applies for absolute placement too, as we have the
|
||||
* page nr. in td->pages.
|
||||
*/
|
||||
if (td->pages[chip] != -1) {
|
||||
page = td->pages[chip];
|
||||
goto write;
|
||||
while (chip < nrchips) {
|
||||
int block;
|
||||
|
||||
block = get_bbt_block(this, td, md, chip);
|
||||
if (block < 0) {
|
||||
pr_err("No space left to write bad block table\n");
|
||||
res = block;
|
||||
goto outerr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Automatic placement of the bad block table. Search direction
|
||||
* top -> down?
|
||||
* get_bbt_block() returns a block number, shift the value to
|
||||
* get a page number.
|
||||
*/
|
||||
if (td->options & NAND_BBT_LASTBLOCK) {
|
||||
startblock = numblocks * (chip + 1) - 1;
|
||||
dir = -1;
|
||||
} else {
|
||||
startblock = chip * numblocks;
|
||||
dir = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < td->maxblocks; i++) {
|
||||
int block = startblock + dir * i;
|
||||
/* Check, if the block is bad */
|
||||
switch (bbt_get_entry(this, block)) {
|
||||
case BBT_BLOCK_WORN:
|
||||
case BBT_BLOCK_FACTORY_BAD:
|
||||
continue;
|
||||
}
|
||||
page = block <<
|
||||
(this->bbt_erase_shift - this->page_shift);
|
||||
/* Check, if the block is used by the mirror table */
|
||||
if (!md || md->pages[chip] != page)
|
||||
goto write;
|
||||
}
|
||||
pr_err("No space left to write bad block table\n");
|
||||
return -ENOSPC;
|
||||
write:
|
||||
page = block << (this->bbt_erase_shift - this->page_shift);
|
||||
|
||||
/* Set up shift count and masks for the flash table */
|
||||
bits = td->options & NAND_BBT_NRBITS_MSK;
|
||||
@ -787,20 +856,28 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
einfo.addr = to;
|
||||
einfo.len = 1 << this->bbt_erase_shift;
|
||||
res = nand_erase_nand(mtd, &einfo, 1);
|
||||
if (res < 0)
|
||||
goto outerr;
|
||||
if (res < 0) {
|
||||
pr_warn("nand_bbt: error while erasing BBT block %d\n",
|
||||
res);
|
||||
mark_bbt_block_bad(this, td, chip, block);
|
||||
continue;
|
||||
}
|
||||
|
||||
res = scan_write_bbt(mtd, to, len, buf,
|
||||
td->options & NAND_BBT_NO_OOB ? NULL :
|
||||
&buf[len]);
|
||||
if (res < 0)
|
||||
goto outerr;
|
||||
if (res < 0) {
|
||||
pr_warn("nand_bbt: error while writing BBT block %d\n",
|
||||
res);
|
||||
mark_bbt_block_bad(this, td, chip, block);
|
||||
continue;
|
||||
}
|
||||
|
||||
pr_info("Bad block table written to 0x%012llx, version 0x%02X\n",
|
||||
(unsigned long long)to, td->version[chip]);
|
||||
|
||||
/* Mark it as used */
|
||||
td->pages[chip] = page;
|
||||
td->pages[chip++] = page;
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
@ -13,228 +13,246 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
static const struct nand_sdr_timings onfi_sdr_timings[] = {
|
||||
static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
/* Mode 0 */
|
||||
{
|
||||
.tADL_min = 200000,
|
||||
.tALH_min = 20000,
|
||||
.tALS_min = 50000,
|
||||
.tAR_min = 25000,
|
||||
.tCEA_max = 100000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 20000,
|
||||
.tCHZ_max = 100000,
|
||||
.tCLH_min = 20000,
|
||||
.tCLR_min = 20000,
|
||||
.tCLS_min = 50000,
|
||||
.tCOH_min = 0,
|
||||
.tCS_min = 70000,
|
||||
.tDH_min = 20000,
|
||||
.tDS_min = 40000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 10000,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 100000,
|
||||
.tREA_max = 40000,
|
||||
.tREH_min = 30000,
|
||||
.tRHOH_min = 0,
|
||||
.tRHW_min = 200000,
|
||||
.tRHZ_max = 200000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 50000,
|
||||
.tRST_max = 250000000000ULL,
|
||||
.tWB_max = 200000,
|
||||
.tRR_min = 40000,
|
||||
.tWC_min = 100000,
|
||||
.tWH_min = 30000,
|
||||
.tWHR_min = 120000,
|
||||
.tWP_min = 50000,
|
||||
.tWW_min = 100000,
|
||||
.type = NAND_SDR_IFACE,
|
||||
.timings.sdr = {
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 20000,
|
||||
.tALS_min = 50000,
|
||||
.tAR_min = 25000,
|
||||
.tCEA_max = 100000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 20000,
|
||||
.tCHZ_max = 100000,
|
||||
.tCLH_min = 20000,
|
||||
.tCLR_min = 20000,
|
||||
.tCLS_min = 50000,
|
||||
.tCOH_min = 0,
|
||||
.tCS_min = 70000,
|
||||
.tDH_min = 20000,
|
||||
.tDS_min = 40000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 10000,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 100000,
|
||||
.tREA_max = 40000,
|
||||
.tREH_min = 30000,
|
||||
.tRHOH_min = 0,
|
||||
.tRHW_min = 200000,
|
||||
.tRHZ_max = 200000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 50000,
|
||||
.tRR_min = 40000,
|
||||
.tRST_max = 250000000000ULL,
|
||||
.tWB_max = 200000,
|
||||
.tWC_min = 100000,
|
||||
.tWH_min = 30000,
|
||||
.tWHR_min = 120000,
|
||||
.tWP_min = 50000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 1 */
|
||||
{
|
||||
.tADL_min = 100000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 25000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 45000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 10000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 10000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 25000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 35000,
|
||||
.tDH_min = 10000,
|
||||
.tDS_min = 20000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 50000,
|
||||
.tREA_max = 30000,
|
||||
.tREH_min = 15000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 25000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 45000,
|
||||
.tWH_min = 15000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 25000,
|
||||
.tWW_min = 100000,
|
||||
.type = NAND_SDR_IFACE,
|
||||
.timings.sdr = {
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 25000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 45000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 10000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 10000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 25000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 35000,
|
||||
.tDH_min = 10000,
|
||||
.tDS_min = 20000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 50000,
|
||||
.tREA_max = 30000,
|
||||
.tREH_min = 15000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 25000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 45000,
|
||||
.tWH_min = 15000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 25000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 2 */
|
||||
{
|
||||
.tADL_min = 100000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 15000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 30000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 10000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 10000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 15000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 25000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 15000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 35000,
|
||||
.tREA_max = 25000,
|
||||
.tREH_min = 15000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tRP_min = 17000,
|
||||
.tWC_min = 35000,
|
||||
.tWH_min = 15000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 17000,
|
||||
.tWW_min = 100000,
|
||||
.type = NAND_SDR_IFACE,
|
||||
.timings.sdr = {
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 15000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 30000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 10000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 10000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 15000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 25000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 15000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 35000,
|
||||
.tREA_max = 25000,
|
||||
.tREH_min = 15000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tRP_min = 17000,
|
||||
.tWC_min = 35000,
|
||||
.tWH_min = 15000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 17000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 3 */
|
||||
{
|
||||
.tADL_min = 100000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 25000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 10000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 30000,
|
||||
.tREA_max = 20000,
|
||||
.tREH_min = 10000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 15000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 30000,
|
||||
.tWH_min = 10000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 15000,
|
||||
.tWW_min = 100000,
|
||||
.type = NAND_SDR_IFACE,
|
||||
.timings.sdr = {
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 25000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 10000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 30000,
|
||||
.tREA_max = 20000,
|
||||
.tREH_min = 10000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 15000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 30000,
|
||||
.tWH_min = 10000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 15000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 4 */
|
||||
{
|
||||
.tADL_min = 70000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 30000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 20000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 10000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 25000,
|
||||
.tREA_max = 20000,
|
||||
.tREH_min = 10000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 5000,
|
||||
.tRP_min = 12000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 25000,
|
||||
.tWH_min = 10000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 12000,
|
||||
.tWW_min = 100000,
|
||||
.type = NAND_SDR_IFACE,
|
||||
.timings.sdr = {
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 30000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 20000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 10000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 25000,
|
||||
.tREA_max = 20000,
|
||||
.tREH_min = 10000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 5000,
|
||||
.tRP_min = 12000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 25000,
|
||||
.tWH_min = 10000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 12000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 5 */
|
||||
{
|
||||
.tADL_min = 70000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 30000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 7000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 20000,
|
||||
.tREA_max = 16000,
|
||||
.tREH_min = 7000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 5000,
|
||||
.tRP_min = 10000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 20000,
|
||||
.tWH_min = 7000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 10000,
|
||||
.tWW_min = 100000,
|
||||
.type = NAND_SDR_IFACE,
|
||||
.timings.sdr = {
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 30000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 7000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 20000,
|
||||
.tREA_max = 16000,
|
||||
.tREH_min = 7000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 5000,
|
||||
.tRP_min = 10000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 20000,
|
||||
.tWH_min = 7000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 10000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -248,6 +266,46 @@ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
|
||||
if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return &onfi_sdr_timings[mode];
|
||||
return &onfi_sdr_timings[mode].timings.sdr;
|
||||
}
|
||||
EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
|
||||
|
||||
/**
|
||||
* onfi_init_data_interface - [NAND Interface] Initialize a data interface from
|
||||
* given ONFI mode
|
||||
* @iface: The data interface to be initialized
|
||||
* @mode: The ONFI timing mode
|
||||
*/
|
||||
int onfi_init_data_interface(struct nand_chip *chip,
|
||||
struct nand_data_interface *iface,
|
||||
enum nand_data_interface_type type,
|
||||
int timing_mode)
|
||||
{
|
||||
if (type != NAND_SDR_IFACE)
|
||||
return -EINVAL;
|
||||
|
||||
if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
|
||||
return -EINVAL;
|
||||
|
||||
*iface = onfi_sdr_timings[timing_mode];
|
||||
|
||||
/*
|
||||
* TODO: initialize timings that cannot be deduced from timing mode:
|
||||
* tR, tPROG, tCCS, ...
|
||||
* These information are part of the ONFI parameter page.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(onfi_init_data_interface);
|
||||
|
||||
/**
|
||||
* nand_get_default_data_interface - [NAND Interface] Retrieve NAND
|
||||
* data interface for mode 0. This is used as default timing after
|
||||
* reset.
|
||||
*/
|
||||
const struct nand_data_interface *nand_get_default_data_interface(void)
|
||||
{
|
||||
return &onfi_sdr_timings[0];
|
||||
}
|
||||
EXPORT_SYMBOL(nand_get_default_data_interface);
|
||||
|
@ -218,8 +218,7 @@ static int ndfc_probe(struct platform_device *ofdev)
|
||||
ndfc = &ndfc_ctrl[cs];
|
||||
ndfc->chip_select = cs;
|
||||
|
||||
spin_lock_init(&ndfc->ndfc_control.lock);
|
||||
init_waitqueue_head(&ndfc->ndfc_control.wq);
|
||||
nand_hw_control_init(&ndfc->ndfc_control);
|
||||
ndfc->ofdev = ofdev;
|
||||
dev_set_drvdata(&ofdev->dev, ndfc);
|
||||
|
||||
|
@ -1810,8 +1810,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||
chip->cmdfunc = nand_cmdfunc;
|
||||
}
|
||||
|
||||
spin_lock_init(&chip->controller->lock);
|
||||
init_waitqueue_head(&chip->controller->wq);
|
||||
nand_hw_control_init(chip->controller);
|
||||
info->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get nand clock\n");
|
||||
|
@ -1957,8 +1957,7 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
|
||||
INIT_LIST_HEAD(&nandc->desc_list);
|
||||
INIT_LIST_HEAD(&nandc->host_list);
|
||||
|
||||
spin_lock_init(&nandc->controller.lock);
|
||||
init_waitqueue_head(&nandc->controller.wq);
|
||||
nand_hw_control_init(&nandc->controller);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ struct s3c2410_nand_info {
|
||||
|
||||
enum s3c_cpu_type cpu_type;
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
|
||||
struct notifier_block freq_transition;
|
||||
#endif
|
||||
};
|
||||
@ -701,7 +701,7 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
|
||||
|
||||
/* cpufreq driver support */
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
|
||||
|
||||
static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb,
|
||||
unsigned long val, void *data)
|
||||
@ -977,8 +977,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
spin_lock_init(&info->controller.lock);
|
||||
init_waitqueue_head(&info->controller.wq);
|
||||
nand_hw_control_init(&info->controller);
|
||||
|
||||
/* get the clock source and enable it */
|
||||
|
||||
|
@ -397,7 +397,7 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
|
||||
struct dma_chan *chan;
|
||||
enum dma_transfer_direction tr_dir;
|
||||
dma_addr_t dma_addr;
|
||||
dma_cookie_t cookie = -EINVAL;
|
||||
dma_cookie_t cookie;
|
||||
uint32_t reg;
|
||||
int ret;
|
||||
|
||||
@ -423,6 +423,12 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
|
||||
desc->callback = flctl_dma_complete;
|
||||
desc->callback_param = flctl;
|
||||
cookie = dmaengine_submit(desc);
|
||||
if (dma_submit_error(cookie)) {
|
||||
ret = dma_submit_error(cookie);
|
||||
dev_warn(&flctl->pdev->dev,
|
||||
"DMA submit failed, falling back to PIO\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dma_async_issue_pending(chan);
|
||||
} else {
|
||||
|
@ -1572,14 +1572,22 @@ 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_nand_chip_set_timings(struct sunxi_nand_chip *chip,
|
||||
const struct nand_sdr_timings *timings)
|
||||
static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd,
|
||||
const struct nand_data_interface *conf,
|
||||
bool check_only)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
struct sunxi_nand_chip *chip = to_sunxi_nand(nand);
|
||||
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
|
||||
const struct nand_sdr_timings *timings;
|
||||
u32 min_clk_period = 0;
|
||||
s32 tWB, tADL, tWHR, tRHW, tCAD;
|
||||
long real_clk_rate;
|
||||
|
||||
timings = nand_get_sdr_timings(conf);
|
||||
if (IS_ERR(timings))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* T1 <=> tCLS */
|
||||
if (timings->tCLS_min > min_clk_period)
|
||||
min_clk_period = timings->tCLS_min;
|
||||
@ -1679,6 +1687,9 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
|
||||
return tRHW;
|
||||
}
|
||||
|
||||
if (check_only)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* TODO: according to ONFI specs this value only applies for DDR NAND,
|
||||
* but Allwinner seems to set this to 0x7. Mimic them for now.
|
||||
@ -1712,44 +1723,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(&chip->nand);
|
||||
const struct nand_sdr_timings *timings;
|
||||
int ret;
|
||||
int mode;
|
||||
|
||||
mode = onfi_get_async_timing_mode(&chip->nand);
|
||||
if (mode == ONFI_TIMING_MODE_UNKNOWN) {
|
||||
mode = chip->nand.onfi_timing_mode_default;
|
||||
} else {
|
||||
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
|
||||
int i;
|
||||
|
||||
mode = fls(mode) - 1;
|
||||
if (mode < 0)
|
||||
mode = 0;
|
||||
|
||||
feature[0] = mode;
|
||||
for (i = 0; i < chip->nsels; i++) {
|
||||
chip->nand.select_chip(mtd, i);
|
||||
ret = chip->nand.onfi_set_features(mtd, &chip->nand,
|
||||
ONFI_FEATURE_ADDR_TIMING_MODE,
|
||||
feature);
|
||||
chip->nand.select_chip(mtd, -1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
timings = onfi_async_timing_mode_to_sdr_timings(mode);
|
||||
if (IS_ERR(timings))
|
||||
return PTR_ERR(timings);
|
||||
|
||||
return sunxi_nand_chip_set_timings(chip, timings);
|
||||
}
|
||||
|
||||
static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
@ -1814,6 +1787,35 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (ecc->options & NAND_ECC_MAXIMIZE) {
|
||||
int bytes;
|
||||
|
||||
ecc->size = 1024;
|
||||
nsectors = mtd->writesize / ecc->size;
|
||||
|
||||
/* Reserve 2 bytes for the BBM */
|
||||
bytes = (mtd->oobsize - 2) / nsectors;
|
||||
|
||||
/* 4 non-ECC bytes are added before each ECC bytes section */
|
||||
bytes -= 4;
|
||||
|
||||
/* and bytes has to be even. */
|
||||
if (bytes % 2)
|
||||
bytes--;
|
||||
|
||||
ecc->strength = bytes * 8 / fls(8 * ecc->size);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(strengths); i++) {
|
||||
if (strengths[i] > ecc->strength)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!i)
|
||||
ecc->strength = 0;
|
||||
else
|
||||
ecc->strength = strengths[i - 1];
|
||||
}
|
||||
|
||||
if (ecc->size != 512 && ecc->size != 1024)
|
||||
return -EINVAL;
|
||||
|
||||
@ -1975,7 +1977,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
|
||||
static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
|
||||
struct device_node *np)
|
||||
{
|
||||
const struct nand_sdr_timings *timings;
|
||||
struct sunxi_nand_chip *chip;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *nand;
|
||||
@ -2065,25 +2066,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
|
||||
nand->read_buf = sunxi_nfc_read_buf;
|
||||
nand->write_buf = sunxi_nfc_write_buf;
|
||||
nand->read_byte = sunxi_nfc_read_byte;
|
||||
nand->setup_data_interface = sunxi_nfc_setup_data_interface;
|
||||
|
||||
mtd = nand_to_mtd(nand);
|
||||
mtd->dev.parent = dev;
|
||||
|
||||
timings = onfi_async_timing_mode_to_sdr_timings(0);
|
||||
if (IS_ERR(timings)) {
|
||||
ret = PTR_ERR(timings);
|
||||
dev_err(dev,
|
||||
"could not retrieve timings for ONFI mode 0: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sunxi_nand_chip_set_timings(chip, timings);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not configure chip timings: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nand_scan_ident(mtd, nsels, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -2096,12 +2083,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
|
||||
|
||||
nand->options |= NAND_SUBPAGE_READ;
|
||||
|
||||
ret = sunxi_nand_chip_init_timings(chip, np);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not configure chip timings: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
|
||||
if (ret) {
|
||||
dev_err(dev, "ECC init failed: %d\n", ret);
|
||||
@ -2175,8 +2156,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
nfc->dev = dev;
|
||||
spin_lock_init(&nfc->controller.lock);
|
||||
init_waitqueue_head(&nfc->controller.wq);
|
||||
nand_hw_control_init(&nfc->controller);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -303,8 +303,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
|
||||
dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n",
|
||||
(gbusclk + 500000) / 1000000, hold, spw);
|
||||
|
||||
spin_lock_init(&drvdata->hw_control.lock);
|
||||
init_waitqueue_head(&drvdata->hw_control.wq);
|
||||
nand_hw_control_init(&drvdata->hw_control);
|
||||
|
||||
platform_set_drvdata(dev, drvdata);
|
||||
txx9ndfmc_initialize(dev);
|
||||
|
@ -90,10 +90,6 @@ config OF_PCI_IRQ
|
||||
help
|
||||
OpenFirmware PCI IRQ routing helpers
|
||||
|
||||
config OF_MTD
|
||||
depends on MTD
|
||||
def_bool y
|
||||
|
||||
config OF_RESERVED_MEM
|
||||
depends on OF_EARLY_FLATTREE
|
||||
bool
|
||||
|
@ -127,6 +127,82 @@ struct mtd_ooblayout_ops {
|
||||
struct mtd_oob_region *oobfree);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mtd_pairing_info - page pairing information
|
||||
*
|
||||
* @pair: pair id
|
||||
* @group: group id
|
||||
*
|
||||
* The term "pair" is used here, even though TLC NANDs might group pages by 3
|
||||
* (3 bits in a single cell). A pair should regroup all pages that are sharing
|
||||
* the same cell. Pairs are then indexed in ascending order.
|
||||
*
|
||||
* @group is defining the position of a page in a given pair. It can also be
|
||||
* seen as the bit position in the cell: page attached to bit 0 belongs to
|
||||
* group 0, page attached to bit 1 belongs to group 1, etc.
|
||||
*
|
||||
* Example:
|
||||
* The H27UCG8T2BTR-BC datasheet describes the following pairing scheme:
|
||||
*
|
||||
* group-0 group-1
|
||||
*
|
||||
* pair-0 page-0 page-4
|
||||
* pair-1 page-1 page-5
|
||||
* pair-2 page-2 page-8
|
||||
* ...
|
||||
* pair-127 page-251 page-255
|
||||
*
|
||||
*
|
||||
* Note that the "group" and "pair" terms were extracted from Samsung and
|
||||
* Hynix datasheets, and might be referenced under other names in other
|
||||
* datasheets (Micron is describing this concept as "shared pages").
|
||||
*/
|
||||
struct mtd_pairing_info {
|
||||
int pair;
|
||||
int group;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mtd_pairing_scheme - page pairing scheme description
|
||||
*
|
||||
* @ngroups: number of groups. Should be related to the number of bits
|
||||
* per cell.
|
||||
* @get_info: converts a write-unit (page number within an erase block) into
|
||||
* mtd_pairing information (pair + group). This function should
|
||||
* fill the info parameter based on the wunit index or return
|
||||
* -EINVAL if the wunit parameter is invalid.
|
||||
* @get_wunit: converts pairing information into a write-unit (page) number.
|
||||
* This function should return the wunit index pointed by the
|
||||
* pairing information described in the info argument. It should
|
||||
* return -EINVAL, if there's no wunit corresponding to the
|
||||
* passed pairing information.
|
||||
*
|
||||
* See mtd_pairing_info documentation for a detailed explanation of the
|
||||
* pair and group concepts.
|
||||
*
|
||||
* The mtd_pairing_scheme structure provides a generic solution to represent
|
||||
* NAND page pairing scheme. Instead of exposing two big tables to do the
|
||||
* write-unit <-> (pair + group) conversions, we ask the MTD drivers to
|
||||
* implement the ->get_info() and ->get_wunit() functions.
|
||||
*
|
||||
* MTD users will then be able to query these information by using the
|
||||
* mtd_pairing_info_to_wunit() and mtd_wunit_to_pairing_info() helpers.
|
||||
*
|
||||
* @ngroups is here to help MTD users iterating over all the pages in a
|
||||
* given pair. This value can be retrieved by MTD users using the
|
||||
* mtd_pairing_groups() helper.
|
||||
*
|
||||
* Examples are given in the mtd_pairing_info_to_wunit() and
|
||||
* mtd_wunit_to_pairing_info() documentation.
|
||||
*/
|
||||
struct mtd_pairing_scheme {
|
||||
int ngroups;
|
||||
int (*get_info)(struct mtd_info *mtd, int wunit,
|
||||
struct mtd_pairing_info *info);
|
||||
int (*get_wunit)(struct mtd_info *mtd,
|
||||
const struct mtd_pairing_info *info);
|
||||
};
|
||||
|
||||
struct module; /* only needed for owner field in mtd_info */
|
||||
|
||||
struct mtd_info {
|
||||
@ -188,6 +264,9 @@ struct mtd_info {
|
||||
/* OOB layout description */
|
||||
const struct mtd_ooblayout_ops *ooblayout;
|
||||
|
||||
/* NAND pairing scheme, only provided for MLC/TLC NANDs */
|
||||
const struct mtd_pairing_scheme *pairing;
|
||||
|
||||
/* the ecc step size. */
|
||||
unsigned int ecc_step_size;
|
||||
|
||||
@ -296,6 +375,12 @@ static inline void mtd_set_ooblayout(struct mtd_info *mtd,
|
||||
mtd->ooblayout = ooblayout;
|
||||
}
|
||||
|
||||
static inline void mtd_set_pairing_scheme(struct mtd_info *mtd,
|
||||
const struct mtd_pairing_scheme *pairing)
|
||||
{
|
||||
mtd->pairing = pairing;
|
||||
}
|
||||
|
||||
static inline void mtd_set_of_node(struct mtd_info *mtd,
|
||||
struct device_node *np)
|
||||
{
|
||||
@ -312,6 +397,11 @@ static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
|
||||
return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
|
||||
}
|
||||
|
||||
int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
|
||||
struct mtd_pairing_info *info);
|
||||
int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
|
||||
const struct mtd_pairing_info *info);
|
||||
int mtd_pairing_groups(struct mtd_info *mtd);
|
||||
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
|
||||
int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||
void **virt, resource_size_t *phys);
|
||||
@ -397,6 +487,23 @@ static inline uint32_t mtd_mod_by_ws(uint64_t sz, struct mtd_info *mtd)
|
||||
return do_div(sz, mtd->writesize);
|
||||
}
|
||||
|
||||
static inline int mtd_wunit_per_eb(struct mtd_info *mtd)
|
||||
{
|
||||
return mtd->erasesize / mtd->writesize;
|
||||
}
|
||||
|
||||
static inline int mtd_offset_to_wunit(struct mtd_info *mtd, loff_t offs)
|
||||
{
|
||||
return mtd_div_by_ws(mtd_mod_by_eb(offs, mtd), mtd);
|
||||
}
|
||||
|
||||
static inline loff_t mtd_wunit_to_offset(struct mtd_info *mtd, loff_t base,
|
||||
int wunit)
|
||||
{
|
||||
return base + (wunit * mtd->writesize);
|
||||
}
|
||||
|
||||
|
||||
static inline int mtd_has_oob(const struct mtd_info *mtd)
|
||||
{
|
||||
return mtd->_read_oob && mtd->_write_oob;
|
||||
|
@ -29,26 +29,26 @@ struct nand_flash_dev;
|
||||
struct device_node;
|
||||
|
||||
/* Scan and identify a NAND device */
|
||||
extern int nand_scan(struct mtd_info *mtd, int max_chips);
|
||||
int nand_scan(struct mtd_info *mtd, int max_chips);
|
||||
/*
|
||||
* Separate phases of nand_scan(), allowing board driver to intervene
|
||||
* and override command or ECC setup according to flash type.
|
||||
*/
|
||||
extern int nand_scan_ident(struct mtd_info *mtd, int max_chips,
|
||||
int nand_scan_ident(struct mtd_info *mtd, int max_chips,
|
||||
struct nand_flash_dev *table);
|
||||
extern int nand_scan_tail(struct mtd_info *mtd);
|
||||
int nand_scan_tail(struct mtd_info *mtd);
|
||||
|
||||
/* Free resources held by the NAND device */
|
||||
extern void nand_release(struct mtd_info *mtd);
|
||||
/* Unregister the MTD device and free resources held by the NAND device */
|
||||
void nand_release(struct mtd_info *mtd);
|
||||
|
||||
/* Internal helper for board drivers which need to override command function */
|
||||
extern void nand_wait_ready(struct mtd_info *mtd);
|
||||
void nand_wait_ready(struct mtd_info *mtd);
|
||||
|
||||
/* locks all blocks present in the device */
|
||||
extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
|
||||
/* unlocks specified locked blocks */
|
||||
extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
|
||||
/* The maximum number of NAND chips in an array */
|
||||
#define NAND_MAX_CHIPS 8
|
||||
@ -141,6 +141,7 @@ enum nand_ecc_algo {
|
||||
* pages and you want to rely on the default implementation.
|
||||
*/
|
||||
#define NAND_ECC_GENERIC_ERASED_CHECK BIT(0)
|
||||
#define NAND_ECC_MAXIMIZE BIT(1)
|
||||
|
||||
/* Bit mask for flags passed to do_nand_read_ecc */
|
||||
#define NAND_GET_DEVICE 0x80
|
||||
@ -460,6 +461,13 @@ struct nand_hw_control {
|
||||
wait_queue_head_t wq;
|
||||
};
|
||||
|
||||
static inline void nand_hw_control_init(struct nand_hw_control *nfc)
|
||||
{
|
||||
nfc->active = NULL;
|
||||
spin_lock_init(&nfc->lock);
|
||||
init_waitqueue_head(&nfc->wq);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct nand_ecc_ctrl - Control structure for ECC
|
||||
* @mode: ECC mode
|
||||
@ -565,6 +573,123 @@ struct nand_buffers {
|
||||
uint8_t *databuf;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_sdr_timings - SDR NAND chip timings
|
||||
*
|
||||
* This struct defines the timing requirements of a SDR NAND chip.
|
||||
* These information can be found in every NAND datasheets and the timings
|
||||
* meaning are described in the ONFI specifications:
|
||||
* www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
|
||||
* Parameters)
|
||||
*
|
||||
* All these timings are expressed in picoseconds.
|
||||
*
|
||||
* @tALH_min: ALE hold time
|
||||
* @tADL_min: ALE to data loading time
|
||||
* @tALS_min: ALE setup time
|
||||
* @tAR_min: ALE to RE# delay
|
||||
* @tCEA_max: CE# access time
|
||||
* @tCEH_min:
|
||||
* @tCH_min: CE# hold time
|
||||
* @tCHZ_max: CE# high to output hi-Z
|
||||
* @tCLH_min: CLE hold time
|
||||
* @tCLR_min: CLE to RE# delay
|
||||
* @tCLS_min: CLE setup time
|
||||
* @tCOH_min: CE# high to output hold
|
||||
* @tCS_min: CE# setup time
|
||||
* @tDH_min: Data hold time
|
||||
* @tDS_min: Data setup time
|
||||
* @tFEAT_max: Busy time for Set Features and Get Features
|
||||
* @tIR_min: Output hi-Z to RE# low
|
||||
* @tITC_max: Interface and Timing Mode Change time
|
||||
* @tRC_min: RE# cycle time
|
||||
* @tREA_max: RE# access time
|
||||
* @tREH_min: RE# high hold time
|
||||
* @tRHOH_min: RE# high to output hold
|
||||
* @tRHW_min: RE# high to WE# low
|
||||
* @tRHZ_max: RE# high to output hi-Z
|
||||
* @tRLOH_min: RE# low to output hold
|
||||
* @tRP_min: RE# pulse width
|
||||
* @tRR_min: Ready to RE# low (data only)
|
||||
* @tRST_max: Device reset time, measured from the falling edge of R/B# to the
|
||||
* rising edge of R/B#.
|
||||
* @tWB_max: WE# high to SR[6] low
|
||||
* @tWC_min: WE# cycle time
|
||||
* @tWH_min: WE# high hold time
|
||||
* @tWHR_min: WE# high to RE# low
|
||||
* @tWP_min: WE# pulse width
|
||||
* @tWW_min: WP# transition to WE# low
|
||||
*/
|
||||
struct nand_sdr_timings {
|
||||
u32 tALH_min;
|
||||
u32 tADL_min;
|
||||
u32 tALS_min;
|
||||
u32 tAR_min;
|
||||
u32 tCEA_max;
|
||||
u32 tCEH_min;
|
||||
u32 tCH_min;
|
||||
u32 tCHZ_max;
|
||||
u32 tCLH_min;
|
||||
u32 tCLR_min;
|
||||
u32 tCLS_min;
|
||||
u32 tCOH_min;
|
||||
u32 tCS_min;
|
||||
u32 tDH_min;
|
||||
u32 tDS_min;
|
||||
u32 tFEAT_max;
|
||||
u32 tIR_min;
|
||||
u32 tITC_max;
|
||||
u32 tRC_min;
|
||||
u32 tREA_max;
|
||||
u32 tREH_min;
|
||||
u32 tRHOH_min;
|
||||
u32 tRHW_min;
|
||||
u32 tRHZ_max;
|
||||
u32 tRLOH_min;
|
||||
u32 tRP_min;
|
||||
u32 tRR_min;
|
||||
u64 tRST_max;
|
||||
u32 tWB_max;
|
||||
u32 tWC_min;
|
||||
u32 tWH_min;
|
||||
u32 tWHR_min;
|
||||
u32 tWP_min;
|
||||
u32 tWW_min;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nand_data_interface_type - NAND interface timing type
|
||||
* @NAND_SDR_IFACE: Single Data Rate interface
|
||||
*/
|
||||
enum nand_data_interface_type {
|
||||
NAND_SDR_IFACE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_data_interface - NAND interface timing
|
||||
* @type: type of the timing
|
||||
* @timings: The timing, type according to @type
|
||||
*/
|
||||
struct nand_data_interface {
|
||||
enum nand_data_interface_type type;
|
||||
union {
|
||||
struct nand_sdr_timings sdr;
|
||||
} timings;
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_get_sdr_timings - get SDR timing from data interface
|
||||
* @conf: The data interface
|
||||
*/
|
||||
static inline const struct nand_sdr_timings *
|
||||
nand_get_sdr_timings(const struct nand_data_interface *conf)
|
||||
{
|
||||
if (conf->type != NAND_SDR_IFACE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return &conf->timings.sdr;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct nand_chip - NAND Private Flash Chip Data
|
||||
* @mtd: MTD device registered to the MTD framework
|
||||
@ -627,10 +752,9 @@ struct nand_buffers {
|
||||
* also from the datasheet. It is the recommended ECC step
|
||||
* size, if known; if unknown, set to zero.
|
||||
* @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
|
||||
* either deduced from the datasheet if the NAND
|
||||
* chip is not ONFI compliant or set to 0 if it is
|
||||
* (an ONFI chip is always configured in mode 0
|
||||
* after a NAND reset)
|
||||
* 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.
|
||||
* @numchips: [INTERN] number of physical chips
|
||||
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
||||
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
||||
@ -650,6 +774,7 @@ struct nand_buffers {
|
||||
* @read_retries: [INTERN] the number of read retry modes supported
|
||||
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
|
||||
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
|
||||
* @setup_data_interface: [OPTIONAL] setup the data interface and timing
|
||||
* @bbt: [INTERN] bad block table pointer
|
||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
|
||||
* lookup.
|
||||
@ -696,6 +821,10 @@ struct nand_chip {
|
||||
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int feature_addr, uint8_t *subfeature_para);
|
||||
int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
|
||||
int (*setup_data_interface)(struct mtd_info *mtd,
|
||||
const struct nand_data_interface *conf,
|
||||
bool check_only);
|
||||
|
||||
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
@ -725,6 +854,8 @@ struct nand_chip {
|
||||
struct nand_jedec_params jedec_params;
|
||||
};
|
||||
|
||||
struct nand_data_interface *data_interface;
|
||||
|
||||
int read_retries;
|
||||
|
||||
flstate_t state;
|
||||
@ -893,14 +1024,14 @@ struct nand_manufacturers {
|
||||
extern struct nand_flash_dev nand_flash_ids[];
|
||||
extern struct nand_manufacturers nand_manuf_ids[];
|
||||
|
||||
extern int nand_default_bbt(struct mtd_info *mtd);
|
||||
extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
extern int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
|
||||
extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
int allowbbt);
|
||||
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, uint8_t *buf);
|
||||
int nand_default_bbt(struct mtd_info *mtd);
|
||||
int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
|
||||
int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
int allowbbt);
|
||||
int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, uint8_t *buf);
|
||||
|
||||
/**
|
||||
* struct platform_nand_chip - chip level device structure
|
||||
@ -988,6 +1119,11 @@ static inline int onfi_get_sync_timing_mode(struct nand_chip *chip)
|
||||
return le16_to_cpu(chip->onfi_params.src_sync_timing_mode);
|
||||
}
|
||||
|
||||
int onfi_init_data_interface(struct nand_chip *chip,
|
||||
struct nand_data_interface *iface,
|
||||
enum nand_data_interface_type type,
|
||||
int timing_mode);
|
||||
|
||||
/*
|
||||
* Check if it is a SLC nand.
|
||||
* The !nand_is_slc() can be used to check the MLC/TLC nand chips.
|
||||
@ -1023,57 +1159,10 @@ static inline int jedec_feature(struct nand_chip *chip)
|
||||
: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* struct nand_sdr_timings - SDR NAND chip timings
|
||||
*
|
||||
* This struct defines the timing requirements of a SDR NAND chip.
|
||||
* These informations can be found in every NAND datasheets and the timings
|
||||
* meaning are described in the ONFI specifications:
|
||||
* www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
|
||||
* Parameters)
|
||||
*
|
||||
* All these timings are expressed in picoseconds.
|
||||
*/
|
||||
|
||||
struct nand_sdr_timings {
|
||||
u32 tALH_min;
|
||||
u32 tADL_min;
|
||||
u32 tALS_min;
|
||||
u32 tAR_min;
|
||||
u32 tCEA_max;
|
||||
u32 tCEH_min;
|
||||
u32 tCH_min;
|
||||
u32 tCHZ_max;
|
||||
u32 tCLH_min;
|
||||
u32 tCLR_min;
|
||||
u32 tCLS_min;
|
||||
u32 tCOH_min;
|
||||
u32 tCS_min;
|
||||
u32 tDH_min;
|
||||
u32 tDS_min;
|
||||
u32 tFEAT_max;
|
||||
u32 tIR_min;
|
||||
u32 tITC_max;
|
||||
u32 tRC_min;
|
||||
u32 tREA_max;
|
||||
u32 tREH_min;
|
||||
u32 tRHOH_min;
|
||||
u32 tRHW_min;
|
||||
u32 tRHZ_max;
|
||||
u32 tRLOH_min;
|
||||
u32 tRP_min;
|
||||
u32 tRR_min;
|
||||
u64 tRST_max;
|
||||
u32 tWB_max;
|
||||
u32 tWC_min;
|
||||
u32 tWH_min;
|
||||
u32 tWHR_min;
|
||||
u32 tWP_min;
|
||||
u32 tWW_min;
|
||||
};
|
||||
|
||||
/* get timing characteristics from ONFI timing mode. */
|
||||
const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
|
||||
/* get data interface from ONFI timing mode 0, used after reset. */
|
||||
const struct nand_data_interface *nand_get_default_data_interface(void);
|
||||
|
||||
int nand_check_erased_ecc_chunk(void *data, int datalen,
|
||||
void *ecc, int ecclen,
|
||||
@ -1093,4 +1182,11 @@ int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);
|
||||
/* Default read_oob syndrome implementation */
|
||||
int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page);
|
||||
|
||||
/* Reset and initialize a NAND device */
|
||||
int nand_reset(struct nand_chip *chip);
|
||||
|
||||
/* Free resources held by the NAND device */
|
||||
void nand_cleanup(struct nand_chip *chip);
|
||||
|
||||
#endif /* __LINUX_MTD_NAND_H */
|
||||
|
Loading…
Reference in New Issue
Block a user