Commit 35ff96df authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 97d21167 69db4aa4
...@@ -35,6 +35,15 @@ Optional NAND chip properties: ...@@ -35,6 +35,15 @@ Optional NAND chip properties:
- nand-ecc-step-size: integer representing the number of data bytes - nand-ecc-step-size: integer representing the number of data bytes
that are covered by a single ECC step. 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 The ECC strength and ECC step size properties define the correction capability
of a controller. Together, they say a controller can correct "{strength} bit of a controller. Together, they say a controller can correct "{strength} bit
errors per {size} bytes". errors per {size} bytes".
......
...@@ -6142,6 +6142,12 @@ M: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com> ...@@ -6142,6 +6142,12 @@ M: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com>
S: Maintained S: Maintained
F: drivers/dma/dma-jz4780.c 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) INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
M: Mimi Zohar <zohar@linux.vnet.ibm.com> M: Mimi Zohar <zohar@linux.vnet.ibm.com>
M: Dmitry Kasatkin <dmitry.kasatkin@gmail.com> M: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
......
...@@ -375,6 +375,110 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state, ...@@ -375,6 +375,110 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
return NOTIFY_DONE; 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 * add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure * @mtd: pointer to new MTD device info structure
......
...@@ -317,6 +317,18 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) ...@@ -317,6 +317,18 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
return res; 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, static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion) struct mtd_oob_region *oobregion)
{ {
...@@ -397,6 +409,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, ...@@ -397,6 +409,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
slave->mtd.oobsize = master->oobsize; slave->mtd.oobsize = master->oobsize;
slave->mtd.oobavail = master->oobavail; slave->mtd.oobavail = master->oobavail;
slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.pairing = master->pairing;
slave->mtd.name = name; slave->mtd.name = name;
slave->mtd.owner = master->owner; slave->mtd.owner = master->owner;
...@@ -463,6 +476,12 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, ...@@ -463,6 +476,12 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
slave->mtd._block_isbad = part_block_isbad; slave->mtd._block_isbad = part_block_isbad;
if (master->_block_markbad) if (master->_block_markbad)
slave->mtd._block_markbad = part_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->mtd._erase = part_erase;
slave->master = master; slave->master = master;
slave->offset = part->offset; slave->offset = part->offset;
......
...@@ -88,11 +88,11 @@ config MTD_NAND_AMS_DELTA ...@@ -88,11 +88,11 @@ config MTD_NAND_AMS_DELTA
Support for NAND flash on Amstrad E3 (Delta). Support for NAND flash on Amstrad E3 (Delta).
config MTD_NAND_OMAP2 config MTD_NAND_OMAP2
tristate "NAND Flash device on OMAP2, OMAP3 and OMAP4" tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
depends on ARCH_OMAP2PLUS depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
help help
Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
platforms. and Keystone platforms.
config MTD_NAND_OMAP_BCH config MTD_NAND_OMAP_BCH
depends on MTD_NAND_OMAP2 depends on MTD_NAND_OMAP2
...@@ -428,7 +428,7 @@ config MTD_NAND_ORION ...@@ -428,7 +428,7 @@ config MTD_NAND_ORION
config MTD_NAND_FSL_ELBC config MTD_NAND_FSL_ELBC
tristate "NAND support for Freescale eLBC controllers" tristate "NAND support for Freescale eLBC controllers"
depends on PPC depends on FSL_SOC
select FSL_LBC select FSL_LBC
help help
Various Freescale chips, including the 8313, include a NAND Flash Various Freescale chips, including the 8313, include a NAND Flash
...@@ -438,7 +438,7 @@ config MTD_NAND_FSL_ELBC ...@@ -438,7 +438,7 @@ config MTD_NAND_FSL_ELBC
config MTD_NAND_FSL_IFC config MTD_NAND_FSL_IFC
tristate "NAND support for Freescale IFC controller" 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 FSL_IFC
select MEMORY select MEMORY
help help
......
...@@ -761,8 +761,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev) ...@@ -761,8 +761,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info); platform_set_drvdata(pdev, info);
spin_lock_init(&info->controller.lock); nand_hw_control_init(&info->controller);
init_waitqueue_head(&info->controller.wq);
info->device = &pdev->dev; info->device = &pdev->dev;
info->platform = plat; info->platform = plat;
......
...@@ -1336,7 +1336,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, ...@@ -1336,7 +1336,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
u32 *flash_cache = (u32 *)ctrl->flash_cache; u32 *flash_cache = (u32 *)ctrl->flash_cache;
int i; 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 * Must cache the FLASH_CACHE now, since changes in
...@@ -1349,7 +1349,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, ...@@ -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)); 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 */ /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
if (host->hwcfg.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, ...@@ -1565,12 +1565,12 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
brcmnand_waitfunc(mtd, chip); brcmnand_waitfunc(mtd, chip);
if (likely(buf)) { 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++) for (j = 0; j < FC_WORDS; j++, buf++)
*buf = brcmnand_read_fc(ctrl, j); *buf = brcmnand_read_fc(ctrl, j);
brcmnand_soc_data_bus_unprepare(ctrl->soc); brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
} }
if (oob) if (oob)
...@@ -1815,12 +1815,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -1815,12 +1815,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
if (buf) { 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++) for (j = 0; j < FC_WORDS; j++, buf++)
brcmnand_write_fc(ctrl, 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) { } else if (oob) {
for (j = 0; j < FC_WORDS; j++) for (j = 0; j < FC_WORDS; j++)
brcmnand_write_fc(ctrl, j, 0xffffffff); brcmnand_write_fc(ctrl, j, 0xffffffff);
...@@ -2370,8 +2370,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) ...@@ -2370,8 +2370,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
init_completion(&ctrl->done); init_completion(&ctrl->done);
init_completion(&ctrl->dma_done); init_completion(&ctrl->dma_done);
spin_lock_init(&ctrl->controller.lock); nand_hw_control_init(&ctrl->controller);
init_waitqueue_head(&ctrl->controller.wq);
INIT_LIST_HEAD(&ctrl->host_list); INIT_LIST_HEAD(&ctrl->host_list);
/* NAND register range */ /* NAND register range */
......
...@@ -23,19 +23,22 @@ struct dev_pm_ops; ...@@ -23,19 +23,22 @@ struct dev_pm_ops;
struct brcmnand_soc { struct brcmnand_soc {
bool (*ctlrdy_ack)(struct brcmnand_soc *soc); bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); 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) 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) 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) static inline u32 brcmnand_readl(void __iomem *addr)
......
...@@ -74,7 +74,8 @@ static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en) ...@@ -74,7 +74,8 @@ static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
spin_unlock_irqrestore(&priv->idm_lock, flags); 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 = struct iproc_nand_soc *priv =
container_of(soc, struct iproc_nand_soc, soc); container_of(soc, struct iproc_nand_soc, soc);
...@@ -86,10 +87,19 @@ static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare) ...@@ -86,10 +87,19 @@ static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
val = brcmnand_readl(mmio); val = brcmnand_readl(mmio);
if (prepare) /*
val |= IPROC_NAND_APB_LE_MODE; * In the case of BE or when dealing with NAND data, alway configure
else * 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; val &= ~IPROC_NAND_APB_LE_MODE;
}
brcmnand_writel(val, mmio); brcmnand_writel(val, mmio);
......
...@@ -1249,8 +1249,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) ...@@ -1249,8 +1249,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE;
nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA;
nand->controller = &nand->hwcontrol; nand->controller = &nand->hwcontrol;
spin_lock_init(&nand->controller->lock); nand_hw_control_init(nand->controller);
init_waitqueue_head(&nand->controller->wq);
/* methods */ /* methods */
nand->cmdfunc = docg4_command; nand->cmdfunc = docg4_command;
......
...@@ -879,8 +879,7 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev) ...@@ -879,8 +879,7 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
} }
elbc_fcm_ctrl->counter++; elbc_fcm_ctrl->counter++;
spin_lock_init(&elbc_fcm_ctrl->controller.lock); nand_hw_control_init(&elbc_fcm_ctrl->controller);
init_waitqueue_head(&elbc_fcm_ctrl->controller.wq);
fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl; fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
} else { } else {
elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
......
...@@ -987,8 +987,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) ...@@ -987,8 +987,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
ifc_nand_ctrl->addr = NULL; ifc_nand_ctrl->addr = NULL;
fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl;
spin_lock_init(&ifc_nand_ctrl->controller.lock); nand_hw_control_init(&ifc_nand_ctrl->controller);
init_waitqueue_head(&ifc_nand_ctrl->controller.wq);
} else { } else {
ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand;
} }
......
...@@ -318,7 +318,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) ...@@ -318,7 +318,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
return -EINVAL; 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; geo->payload_size = mtd->writesize;
/* /*
......
...@@ -368,9 +368,8 @@ static int jz4780_nand_probe(struct platform_device *pdev) ...@@ -368,9 +368,8 @@ static int jz4780_nand_probe(struct platform_device *pdev)
nfc->dev = dev; nfc->dev = dev;
nfc->num_banks = num_banks; nfc->num_banks = num_banks;
spin_lock_init(&nfc->controller.lock); nand_hw_control_init(&nfc->controller);
INIT_LIST_HEAD(&nfc->chips); INIT_LIST_HEAD(&nfc->chips);
init_waitqueue_head(&nfc->controller.wq);
ret = jz4780_nand_init_chips(nfc, pdev); ret = jz4780_nand_init_chips(nfc, pdev);
if (ret) { if (ret) {
......
...@@ -152,6 +152,9 @@ struct mxc_nand_devtype_data { ...@@ -152,6 +152,9 @@ struct mxc_nand_devtype_data {
void (*select_chip)(struct mtd_info *mtd, int chip); void (*select_chip)(struct mtd_info *mtd, int chip);
int (*correct_data)(struct mtd_info *mtd, u_char *dat, int (*correct_data)(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc); 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 * 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) ...@@ -1012,6 +1015,82 @@ static void preset_v1(struct mtd_info *mtd)
writew(0x4, NFC_V1_V2_WRPROT); writew(0x4, NFC_V1_V2_WRPROT);
} }
static int mxc_nand_v2_setup_data_interface(struct 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) static void preset_v2(struct mtd_info *mtd)
{ {
struct nand_chip *nand_chip = mtd_to_nand(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, ...@@ -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 * The generic flash bbt decriptors overlap with our ecc
* hardware, so define some i.MX specific ones. * hardware, so define some i.MX specific ones.
...@@ -1327,6 +1457,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { ...@@ -1327,6 +1457,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
.ooblayout = &mxc_v2_ooblayout_ops, .ooblayout = &mxc_v2_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v2, .select_chip = mxc_nand_select_chip_v2,
.correct_data = mxc_nand_correct_data_v2_v3, .correct_data = mxc_nand_correct_data_v2_v3,
.setup_data_interface = mxc_nand_v2_setup_data_interface,
.irqpending_quirk = 0, .irqpending_quirk = 0,
.needs_ip = 0, .needs_ip = 0,
.regs_offset = 0x1e00, .regs_offset = 0x1e00,
...@@ -1434,7 +1565,7 @@ static const struct platform_device_id mxcnd_devtype[] = { ...@@ -1434,7 +1565,7 @@ static const struct platform_device_id mxcnd_devtype[] = {
}; };
MODULE_DEVICE_TABLE(platform, mxcnd_devtype); MODULE_DEVICE_TABLE(platform, mxcnd_devtype);
#ifdef CONFIG_OF_MTD #ifdef CONFIG_OF
static const struct of_device_id mxcnd_dt_ids[] = { static const struct of_device_id mxcnd_dt_ids[] = {
{ {
.compatible = "fsl,imx21-nand", .compatible = "fsl,imx21-nand",
...@@ -1513,6 +1644,8 @@ static int mxcnd_probe(struct platform_device *pdev) ...@@ -1513,6 +1644,8 @@ static int mxcnd_probe(struct platform_device *pdev)
this->read_word = mxc_nand_read_word; this->read_word = mxc_nand_read_word;
this->write_buf = mxc_nand_write_buf; this->write_buf = mxc_nand_write_buf;
this->read_buf = mxc_nand_read_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); host->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) if (IS_ERR(host->clk))
...@@ -1533,6 +1666,8 @@ static int mxcnd_probe(struct platform_device *pdev) ...@@ -1533,6 +1666,8 @@ static int mxcnd_probe(struct platform_device *pdev)
if (err < 0) if (err < 0)
return err; return err;
this->setup_data_interface = host->devtype_data->setup_data_interface;
if (host->devtype_data->needs_ip) { if (host->devtype_data->needs_ip) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->regs_ip = devm_ioremap_resource(&pdev->dev, res); host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
......
This diff is collapsed.
...@@ -604,6 +604,100 @@ static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, ...@@ -604,6 +604,100 @@ static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
search_bbt(mtd, buf, md); 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 * write_bbt - [GENERIC] (Re)write the bad block table
* @mtd: MTD device structure * @mtd: MTD device structure
...@@ -621,7 +715,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, ...@@ -621,7 +715,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_chip *this = mtd_to_nand(mtd); struct nand_chip *this = mtd_to_nand(mtd);
struct erase_info einfo; struct erase_info einfo;
int i, res, chip = 0; 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; int nrchips, pageoffs, ooboffs;
uint8_t msk[4]; uint8_t msk[4];
uint8_t rcode = td->reserved_block_code; uint8_t rcode = td->reserved_block_code;
...@@ -652,46 +746,21 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, ...@@ -652,46 +746,21 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
} }
/* Loop through the chips */ /* Loop through the chips */
for (; chip < nrchips; chip++) { while (chip < nrchips) {
/* int block;
* There was already a version of the table, reuse the page
* This applies for absolute placement too, as we have the block = get_bbt_block(this, td, md, chip);
* page nr. in td->pages. if (block < 0) {
*/ pr_err("No space left to write bad block table\n");
if (td->pages[chip] != -1) { res = block;
page = td->pages[chip]; goto outerr;
goto write;
} }
/* /*
* Automatic placement of the bad block table. Search direction * get_bbt_block() returns a block number, shift the value to
* top -> down? * get a page number.
*/ */
if (td->options & NAND_BBT_LASTBLOCK) { page = block << (this->bbt_erase_shift - this->page_shift);
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:
/* Set up shift count and masks for the flash table */ /* Set up shift count and masks for the flash table */
bits = td->options & NAND_BBT_NRBITS_MSK; bits = td->options & NAND_BBT_NRBITS_MSK;
...@@ -787,20 +856,28 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, ...@@ -787,20 +856,28 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
einfo.addr = to; einfo.addr = to;
einfo.len = 1 << this->bbt_erase_shift; einfo.len = 1 << this->bbt_erase_shift;
res = nand_erase_nand(mtd, &einfo, 1); res = nand_erase_nand(mtd, &einfo, 1);
if (res < 0) if (res < 0) {
goto outerr; 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, res = scan_write_bbt(mtd, to, len, buf,
td->options & NAND_BBT_NO_OOB ? NULL : td->options & NAND_BBT_NO_OOB ? NULL :
&buf[len]); &buf[len]);
if (res < 0) if (res < 0) {
goto outerr; 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", pr_info("Bad block table written to 0x%012llx, version 0x%02X\n",
(unsigned long long)to, td->version[chip]); (unsigned long long)to, td->version[chip]);
/* Mark it as used */ /* Mark it as used */
td->pages[chip] = page; td->pages[chip++] = page;
} }
return 0; return 0;
......
This diff is collapsed.
...@@ -218,8 +218,7 @@ static int ndfc_probe(struct platform_device *ofdev) ...@@ -218,8 +218,7 @@ static int ndfc_probe(struct platform_device *ofdev)
ndfc = &ndfc_ctrl[cs]; ndfc = &ndfc_ctrl[cs];
ndfc->chip_select = cs; ndfc->chip_select = cs;
spin_lock_init(&ndfc->ndfc_control.lock); nand_hw_control_init(&ndfc->ndfc_control);
init_waitqueue_head(&ndfc->ndfc_control.wq);
ndfc->ofdev = ofdev; ndfc->ofdev = ofdev;
dev_set_drvdata(&ofdev->dev, ndfc); dev_set_drvdata(&ofdev->dev, ndfc);
......
...@@ -1810,8 +1810,7 @@ static int alloc_nand_resource(struct platform_device *pdev) ...@@ -1810,8 +1810,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
chip->cmdfunc = nand_cmdfunc; chip->cmdfunc = nand_cmdfunc;
} }
spin_lock_init(&chip->controller->lock); nand_hw_control_init(chip->controller);
init_waitqueue_head(&chip->controller->wq);
info->clk = devm_clk_get(&pdev->dev, NULL); info->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(info->clk)) { if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get nand clock\n"); dev_err(&pdev->dev, "failed to get nand clock\n");
......
...@@ -1957,8 +1957,7 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) ...@@ -1957,8 +1957,7 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
INIT_LIST_HEAD(&nandc->desc_list); INIT_LIST_HEAD(&nandc->desc_list);
INIT_LIST_HEAD(&nandc->host_list); INIT_LIST_HEAD(&nandc->host_list);
spin_lock_init(&nandc->controller.lock); nand_hw_control_init(&nandc->controller);
init_waitqueue_head(&nandc->controller.wq);
return 0; return 0;
} }
......
...@@ -180,7 +180,7 @@ struct s3c2410_nand_info { ...@@ -180,7 +180,7 @@ struct s3c2410_nand_info {
enum s3c_cpu_type cpu_type; enum s3c_cpu_type cpu_type;
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_ARM_S3C24XX_CPUFREQ
struct notifier_block freq_transition; struct notifier_block freq_transition;
#endif #endif
}; };
...@@ -701,7 +701,7 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, ...@@ -701,7 +701,7 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
/* cpufreq driver support */ /* cpufreq driver support */
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_ARM_S3C24XX_CPUFREQ
static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb,
unsigned long val, void *data) unsigned long val, void *data)
...@@ -977,8 +977,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) ...@@ -977,8 +977,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info); platform_set_drvdata(pdev, info);
spin_lock_init(&info->controller.lock); nand_hw_control_init(&info->controller);
init_waitqueue_head(&info->controller.wq);
/* get the clock source and enable it */ /* get the clock source and enable it */
......
...@@ -397,7 +397,7 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, ...@@ -397,7 +397,7 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
struct dma_chan *chan; struct dma_chan *chan;
enum dma_transfer_direction tr_dir; enum dma_transfer_direction tr_dir;
dma_addr_t dma_addr; dma_addr_t dma_addr;
dma_cookie_t cookie = -EINVAL; dma_cookie_t cookie;
uint32_t reg; uint32_t reg;
int ret; int ret;
...@@ -423,6 +423,12 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, ...@@ -423,6 +423,12 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
desc->callback = flctl_dma_complete; desc->callback = flctl_dma_complete;
desc->callback_param = flctl; desc->callback_param = flctl;
cookie = dmaengine_submit(desc); 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); dma_async_issue_pending(chan);
} else { } else {
......
...@@ -1572,14 +1572,22 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, ...@@ -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) \ #define sunxi_nand_lookup_timing(l, p, c) \
_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd,
const struct nand_sdr_timings *timings) 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); struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
const struct nand_sdr_timings *timings;
u32 min_clk_period = 0; u32 min_clk_period = 0;
s32 tWB, tADL, tWHR, tRHW, tCAD; s32 tWB, tADL, tWHR, tRHW, tCAD;
long real_clk_rate; long real_clk_rate;
timings = nand_get_sdr_timings(conf);
if (IS_ERR(timings))
return -ENOTSUPP;
/* T1 <=> tCLS */ /* T1 <=> tCLS */
if (timings->tCLS_min > min_clk_period) if (timings->tCLS_min > min_clk_period)
min_clk_period = timings->tCLS_min; min_clk_period = timings->tCLS_min;
...@@ -1679,6 +1687,9 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, ...@@ -1679,6 +1687,9 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
return tRHW; return tRHW;
} }
if (check_only)
return 0;
/* /*
* TODO: according to ONFI specs this value only applies for DDR NAND, * TODO: according to ONFI specs this value only applies for DDR NAND,
* but Allwinner seems to set this to 0x7. Mimic them for now. * 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, ...@@ -1712,44 +1723,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
return 0; 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, static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion) struct mtd_oob_region *oobregion)
{ {
...@@ -1814,6 +1787,35 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, ...@@ -1814,6 +1787,35 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
int ret; int ret;
int i; 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) if (ecc->size != 512 && ecc->size != 1024)
return -EINVAL; return -EINVAL;
...@@ -1975,7 +1977,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, ...@@ -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, static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
struct device_node *np) struct device_node *np)
{ {
const struct nand_sdr_timings *timings;
struct sunxi_nand_chip *chip; struct sunxi_nand_chip *chip;
struct mtd_info *mtd; struct mtd_info *mtd;
struct nand_chip *nand; struct nand_chip *nand;
...@@ -2065,25 +2066,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, ...@@ -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->read_buf = sunxi_nfc_read_buf;
nand->write_buf = sunxi_nfc_write_buf; nand->write_buf = sunxi_nfc_write_buf;
nand->read_byte = sunxi_nfc_read_byte; nand->read_byte = sunxi_nfc_read_byte;
nand->setup_data_interface = sunxi_nfc_setup_data_interface;
mtd = nand_to_mtd(nand); mtd = nand_to_mtd(nand);
mtd->dev.parent = dev; 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); ret = nand_scan_ident(mtd, nsels, NULL);
if (ret) if (ret)
return ret; return ret;
...@@ -2096,12 +2083,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, ...@@ -2096,12 +2083,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
nand->options |= NAND_SUBPAGE_READ; 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); ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
if (ret) { if (ret) {
dev_err(dev, "ECC init failed: %d\n", ret); dev_err(dev, "ECC init failed: %d\n", ret);
...@@ -2175,8 +2156,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev) ...@@ -2175,8 +2156,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
nfc->dev = dev; nfc->dev = dev;
spin_lock_init(&nfc->controller.lock); nand_hw_control_init(&nfc->controller);
init_waitqueue_head(&nfc->controller.wq);
INIT_LIST_HEAD(&nfc->chips); INIT_LIST_HEAD(&nfc->chips);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
......
...@@ -303,8 +303,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) ...@@ -303,8 +303,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n",
(gbusclk + 500000) / 1000000, hold, spw); (gbusclk + 500000) / 1000000, hold, spw);
spin_lock_init(&drvdata->hw_control.lock); nand_hw_control_init(&drvdata->hw_control);
init_waitqueue_head(&drvdata->hw_control.wq);
platform_set_drvdata(dev, drvdata); platform_set_drvdata(dev, drvdata);
txx9ndfmc_initialize(dev); txx9ndfmc_initialize(dev);
......
...@@ -90,10 +90,6 @@ config OF_PCI_IRQ ...@@ -90,10 +90,6 @@ config OF_PCI_IRQ
help help
OpenFirmware PCI IRQ routing helpers OpenFirmware PCI IRQ routing helpers
config OF_MTD
depends on MTD
def_bool y
config OF_RESERVED_MEM config OF_RESERVED_MEM
depends on OF_EARLY_FLATTREE depends on OF_EARLY_FLATTREE
bool bool
......
...@@ -127,6 +127,82 @@ struct mtd_ooblayout_ops { ...@@ -127,6 +127,82 @@ struct mtd_ooblayout_ops {
struct mtd_oob_region *oobfree); 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 module; /* only needed for owner field in mtd_info */
struct mtd_info { struct mtd_info {
...@@ -188,6 +264,9 @@ struct mtd_info { ...@@ -188,6 +264,9 @@ struct mtd_info {
/* OOB layout description */ /* OOB layout description */
const struct mtd_ooblayout_ops *ooblayout; 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. */ /* the ecc step size. */
unsigned int ecc_step_size; unsigned int ecc_step_size;
...@@ -296,6 +375,12 @@ static inline void mtd_set_ooblayout(struct mtd_info *mtd, ...@@ -296,6 +375,12 @@ static inline void mtd_set_ooblayout(struct mtd_info *mtd,
mtd->ooblayout = ooblayout; 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, static inline void mtd_set_of_node(struct mtd_info *mtd,
struct device_node *np) struct device_node *np)
{ {
...@@ -312,6 +397,11 @@ static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) ...@@ -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; 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_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, int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
void **virt, resource_size_t *phys); 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) ...@@ -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); 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) static inline int mtd_has_oob(const struct mtd_info *mtd)
{ {
return mtd->_read_oob && mtd->_write_oob; return mtd->_read_oob && mtd->_write_oob;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment