From e9f66ae23c209eec617130126a23bf547bf7a6d8 Mon Sep 17 00:00:00 2001 From: Sergio Prado Date: Thu, 20 Oct 2016 19:42:44 -0200 Subject: mtd: s3c2410: make ecc mode configurable via platform data Removing CONFIG_MTD_NAND_S3C2410_HWECC option and adding a ecc_mode field in the drivers's platform data structure so it can be selectable via platform data. Also setting this field to NAND_ECC_SOFT in all boards using this driver since none of them had CONFIG_MTD_NAND_S3C2410_HWECC enabled. Signed-off-by: Sergio Prado Acked-by: Krzysztof Kozlowski Signed-off-by: Boris Brezillon --- include/linux/platform_data/mtd-nand-s3c2410.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/mtd-nand-s3c2410.h b/include/linux/platform_data/mtd-nand-s3c2410.h index c55e42ee57fa..729af13d1773 100644 --- a/include/linux/platform_data/mtd-nand-s3c2410.h +++ b/include/linux/platform_data/mtd-nand-s3c2410.h @@ -12,9 +12,10 @@ #ifndef __MTD_NAND_S3C2410_H #define __MTD_NAND_S3C2410_H +#include + /** * struct s3c2410_nand_set - define a set of one or more nand chips - * @disable_ecc: Entirely disable ECC - Dangerous * @flash_bbt: Openmoko u-boot can create a Bad Block Table * Setting this flag will allow the kernel to * look for it at boot time and also skip the NAND @@ -31,7 +32,6 @@ * a warning at boot time. */ struct s3c2410_nand_set { - unsigned int disable_ecc:1; unsigned int flash_bbt:1; unsigned int options; @@ -51,6 +51,8 @@ struct s3c2410_platform_nand { unsigned int ignore_unset_ecc:1; + nand_ecc_modes_t ecc_mode; + int nr_sets; struct s3c2410_nand_set *sets; -- cgit v1.2.3 From 1c825ad1b8cfe12ccc145dcdba360c52c0272c04 Mon Sep 17 00:00:00 2001 From: Sergio Prado Date: Wed, 26 Oct 2016 21:59:55 -0200 Subject: mtd: s3c2410: parse the device configuration from OF node Allows configuring Samsung's s3c2410 memory controller using a devicetree. Signed-off-by: Sergio Prado Acked-by: Krzysztof Kozlowski Signed-off-by: Boris Brezillon --- drivers/mtd/nand/s3c2410.c | 163 ++++++++++++++++++++++--- include/linux/platform_data/mtd-nand-s3c2410.h | 1 + 2 files changed, 147 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index bb4ac3179d17..f0b030d44f71 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -185,6 +187,22 @@ struct s3c2410_nand_info { #endif }; +struct s3c24XX_nand_devtype_data { + enum s3c_cpu_type type; +}; + +static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = { + .type = TYPE_S3C2410, +}; + +static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = { + .type = TYPE_S3C2412, +}; + +static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = { + .type = TYPE_S3C2440, +}; + /* conversion functions */ static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) @@ -794,6 +812,30 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, return -ENODEV; } +static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd, + const struct nand_data_interface *conf, + bool check_only) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + struct s3c2410_platform_nand *pdata = info->platform; + const struct nand_sdr_timings *timings; + int tacls; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + tacls = timings->tCLS_min - timings->tWP_min; + if (tacls < 0) + tacls = 0; + + pdata->tacls = DIV_ROUND_UP(tacls, 1000); + pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000); + pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000); + + return s3c2410_nand_setrate(info); +} + /** * s3c2410_nand_init_chip - initialise a single instance of an chip * @info: The base NAND controller the chip is on. @@ -808,9 +850,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *nmtd, struct s3c2410_nand_set *set) { + struct device_node *np = info->device->of_node; struct nand_chip *chip = &nmtd->chip; void __iomem *regs = info->regs; + nand_set_flash_node(chip, set->of_node); + chip->write_buf = s3c2410_nand_write_buf; chip->read_buf = s3c2410_nand_read_buf; chip->select_chip = s3c2410_nand_select_chip; @@ -819,6 +864,13 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->options = set->options; chip->controller = &info->controller; + /* + * let's keep behavior unchanged for legacy boards booting via pdata and + * auto-detect timings only when booting with a device tree. + */ + if (np) + chip->setup_data_interface = s3c2410_nand_setup_data_interface; + switch (info->cpu_type) { case TYPE_S3C2410: chip->IO_ADDR_W = regs + S3C2410_NFDATA; @@ -858,13 +910,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->ecc.mode = info->platform->ecc_mode; - /* If you use u-boot BBT creation code, specifying this flag will - * let the kernel fish out the BBT from the NAND, and also skip the - * full NAND scan that can take 1/2s or so. Little things... */ - if (set->flash_bbt) { + /* + * If you use u-boot BBT creation code, specifying this flag will + * let the kernel fish out the BBT from the NAND. + */ + if (set->flash_bbt) chip->bbt_options |= NAND_BBT_USE_FLASH; - chip->options |= NAND_SKIP_BBTSCAN; - } } /** @@ -945,6 +996,78 @@ static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, return -EINVAL; } + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->options |= NAND_SKIP_BBTSCAN; + + return 0; +} + +static const struct of_device_id s3c24xx_nand_dt_ids[] = { + { + .compatible = "samsung,s3c2410-nand", + .data = &s3c2410_nand_devtype_data, + }, { + /* also compatible with s3c6400 */ + .compatible = "samsung,s3c2412-nand", + .data = &s3c2412_nand_devtype_data, + }, { + .compatible = "samsung,s3c2440-nand", + .data = &s3c2440_nand_devtype_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids); + +static int s3c24xx_nand_probe_dt(struct platform_device *pdev) +{ + const struct s3c24XX_nand_devtype_data *devtype_data; + struct s3c2410_platform_nand *pdata; + struct s3c2410_nand_info *info = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node, *child; + struct s3c2410_nand_set *sets; + + devtype_data = of_device_get_match_data(&pdev->dev); + if (!devtype_data) + return -ENODEV; + + info->cpu_type = devtype_data->type; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdev->dev.platform_data = pdata; + + pdata->nr_sets = of_get_child_count(np); + if (!pdata->nr_sets) + return 0; + + sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets, + GFP_KERNEL); + if (!sets) + return -ENOMEM; + + pdata->sets = sets; + + for_each_available_child_of_node(np, child) { + sets->name = (char *)child->name; + sets->of_node = child; + sets->nr_chips = 1; + + of_node_get(child); + + sets++; + } + + return 0; +} + +static int s3c24xx_nand_probe_pdata(struct platform_device *pdev) +{ + struct s3c2410_nand_info *info = platform_get_drvdata(pdev); + + info->cpu_type = platform_get_device_id(pdev)->driver_data; + return 0; } @@ -957,8 +1080,7 @@ static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, */ static int s3c24xx_nand_probe(struct platform_device *pdev) { - struct s3c2410_platform_nand *plat = to_nand_plat(pdev); - enum s3c_cpu_type cpu_type; + struct s3c2410_platform_nand *plat; struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_set *sets; @@ -968,8 +1090,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) int nr_sets; int setno; - cpu_type = platform_get_device_id(pdev)->driver_data; - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (info == NULL) { err = -ENOMEM; @@ -991,6 +1111,16 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); + if (pdev->dev.of_node) + err = s3c24xx_nand_probe_dt(pdev); + else + err = s3c24xx_nand_probe_pdata(pdev); + + if (err) + goto exit_error; + + plat = to_nand_plat(pdev); + /* allocate and map the resource */ /* currently we assume we have the one resource */ @@ -999,7 +1129,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) info->device = &pdev->dev; info->platform = plat; - info->cpu_type = cpu_type; info->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(info->regs)) { @@ -1009,12 +1138,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); - /* initialise the hardware */ - - err = s3c2410_nand_inithw(info); - if (err != 0) - goto exit_error; - sets = (plat != NULL) ? plat->sets : NULL; nr_sets = (plat != NULL) ? plat->nr_sets : 1; @@ -1058,6 +1181,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) sets++; } + /* initialise the hardware */ + err = s3c2410_nand_inithw(info); + if (err != 0) + goto exit_error; + err = s3c2410_nand_cpufreq_register(info); if (err < 0) { dev_err(&pdev->dev, "failed to init cpufreq support\n"); @@ -1158,6 +1286,7 @@ static struct platform_driver s3c24xx_nand_driver = { .id_table = s3c24xx_driver_ids, .driver = { .name = "s3c24xx-nand", + .of_match_table = s3c24xx_nand_dt_ids, }, }; diff --git a/include/linux/platform_data/mtd-nand-s3c2410.h b/include/linux/platform_data/mtd-nand-s3c2410.h index 729af13d1773..f01659026b26 100644 --- a/include/linux/platform_data/mtd-nand-s3c2410.h +++ b/include/linux/platform_data/mtd-nand-s3c2410.h @@ -40,6 +40,7 @@ struct s3c2410_nand_set { char *name; int *nr_map; struct mtd_partition *partitions; + struct device_node *of_node; }; struct s3c2410_platform_nand { -- cgit v1.2.3 From 204e7ecd47e26cc12d9e8e8a7e7a2eeb9573f0ba Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sat, 1 Oct 2016 10:24:02 +0200 Subject: mtd: nand: Add a few more timings to nand_sdr_timings Add the tR_max, tBERS_max, tPROG_max and tCCS_min timings to the nand_sdr_timings struct. Assign default/safe values for the statically defined timings, and extract them from the ONFI parameter table if the NAND is ONFI compliant. Signed-off-by: Boris Brezillon Tested-by: Marc Gonzalez --- drivers/mtd/nand/nand_timings.c | 26 +++++++++++++++++++++++++- include/linux/mtd/nand.h | 8 ++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c index 13a587407be3..f06312df3669 100644 --- a/drivers/mtd/nand/nand_timings.c +++ b/drivers/mtd/nand/nand_timings.c @@ -18,6 +18,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 20000, .tALS_min = 50000, @@ -58,6 +60,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 10000, .tALS_min = 25000, @@ -98,6 +102,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 10000, .tALS_min = 15000, @@ -138,6 +144,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 5000, .tALS_min = 10000, @@ -178,6 +186,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 5000, .tALS_min = 10000, @@ -218,6 +228,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 5000, .tALS_min = 10000, @@ -290,10 +302,22 @@ int onfi_init_data_interface(struct nand_chip *chip, *iface = onfi_sdr_timings[timing_mode]; /* - * TODO: initialize timings that cannot be deduced from timing mode: + * Initialize timings that cannot be deduced from timing mode: * tR, tPROG, tCCS, ... * These information are part of the ONFI parameter page. */ + if (chip->onfi_version) { + struct nand_onfi_params *params = &chip->onfi_params; + struct nand_sdr_timings *timings = &iface->timings.sdr; + + /* microseconds -> picoseconds */ + timings->tPROG_max = 1000000UL * le16_to_cpu(params->t_prog); + timings->tBERS_max = 1000000UL * le16_to_cpu(params->t_bers); + timings->tR_max = 1000000UL * le16_to_cpu(params->t_r); + + /* nanoseconds -> picoseconds */ + timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); + } return 0; } diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index c5d3d5024fc8..6fe83bce83a6 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -584,6 +584,10 @@ struct nand_buffers { * * All these timings are expressed in picoseconds. * + * @tBERS_max: Block erase time + * @tCCS_min: Change column setup time + * @tPROG_max: Page program time + * @tR_max: Page read time * @tALH_min: ALE hold time * @tADL_min: ALE to data loading time * @tALS_min: ALE setup time @@ -621,6 +625,10 @@ struct nand_buffers { * @tWW_min: WP# transition to WE# low */ struct nand_sdr_timings { + u32 tBERS_max; + u32 tCCS_min; + u32 tPROG_max; + u32 tR_max; u32 tALH_min; u32 tADL_min; u32 tALS_min; -- cgit v1.2.3 From 6ea40a3ba93e1b14ffb349e276f9dfefc4334b99 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sat, 1 Oct 2016 10:24:03 +0200 Subject: mtd: nand: Wait tCCS after a column change Drivers implementing ->cmd_ctrl() and relying on the default ->cmdfunc() implementation usually don't wait tCCS when a column change (RNDIN or RNDOUT) is requested. Add an option flag to ask the core to do so (note that we keep this as an opt-in to avoid breaking existing implementations), and make use of the ->data_interface information is available (otherwise, wait 500ns). Signed-off-by: Boris Brezillon Tested-by: Marc Gonzalez --- drivers/mtd/nand/nand_base.c | 26 +++++++++++++++++++++++++- include/linux/mtd/nand.h | 10 ++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e5718e5ecf92..0acb0070280a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -709,6 +709,25 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, nand_wait_ready(mtd); } +static void nand_ccs_delay(struct nand_chip *chip) +{ + /* + * The controller already takes care of waiting for tCCS when the RNDIN + * or RNDOUT command is sent, return directly. + */ + if (!(chip->options & NAND_WAIT_TCCS)) + return; + + /* + * Wait tCCS_min if it is correctly defined, otherwise wait 500ns + * (which should be safe for all NANDs). + */ + if (chip->data_interface && chip->data_interface->timings.sdr.tCCS_min) + ndelay(chip->data_interface->timings.sdr.tCCS_min / 1000); + else + ndelay(500); +} + /** * nand_command_lp - [DEFAULT] Send command to NAND large page device * @mtd: MTD device structure @@ -773,10 +792,13 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: case NAND_CMD_STATUS: return; + case NAND_CMD_RNDIN: + nand_ccs_delay(chip); + return; + case NAND_CMD_RESET: if (chip->dev_ready) break; @@ -795,6 +817,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + nand_ccs_delay(chip); return; case NAND_CMD_READ0: diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 6fe83bce83a6..970ceb948835 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -210,6 +210,16 @@ enum nand_ecc_algo { */ #define NAND_USE_BOUNCE_BUFFER 0x00100000 +/* + * In case your controller is implementing ->cmd_ctrl() and is relying on the + * default ->cmdfunc() implementation, you may want to let the core handle the + * tCCS delay which is required when a column change (RNDIN or RNDOUT) is + * requested. + * If your controller already takes care of this delay, you don't need to set + * this flag. + */ +#define NAND_WAIT_TCCS 0x00200000 + /* Options set by nand scan */ /* Nand scan has allocated controller struct */ #define NAND_CONTROLLER_ALLOC 0x80000000 -- cgit v1.2.3 From 3371d663bb4579f1b2003a92162edd6d90edd089 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 15 Nov 2016 10:56:20 +0100 Subject: mtd: nand: Support controllers with custom page If your controller already sends the required NAND commands when reading or writing a page, then the framework is not supposed to send READ0 and SEQIN/PAGEPROG respectively. Signed-off-by: Marc Gonzalez Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 34 +++++++++++++++++++++++++++++++--- include/linux/mtd/nand.h | 12 ++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 4d099d4f442c..96d242e2fe34 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1961,7 +1961,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, __func__, buf); read_retry: - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + if (nand_standard_page_accessors(&chip->ecc)) + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); /* * Now read the page into the buffer. Absent an error, @@ -2649,7 +2650,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, else subpage = 0; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + if (nand_standard_page_accessors(&chip->ecc)) + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) status = chip->ecc.write_page_raw(mtd, chip, buf, @@ -2672,7 +2674,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (!cached || !NAND_HAS_CACHEPROG(chip)) { - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + if (nand_standard_page_accessors(&chip->ecc)) + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are @@ -4511,6 +4514,26 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd) return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; } +static bool invalid_ecc_page_accessors(struct nand_chip *chip) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (nand_standard_page_accessors(ecc)) + return false; + + /* + * NAND_ECC_CUSTOM_PAGE_ACCESS flag is set, make sure the NAND + * controller driver implements all the page accessors because + * default helpers are not suitable when the core does not + * send the READ0/PAGEPROG commands. + */ + return (!ecc->read_page || !ecc->write_page || + !ecc->read_page_raw || !ecc->write_page_raw || + (NAND_HAS_SUBPAGE_READ(chip) && !ecc->read_subpage) || + (NAND_HAS_SUBPAGE_WRITE(chip) && !ecc->write_subpage && + ecc->hwctl && ecc->calculate)); +} + /** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure @@ -4531,6 +4554,11 @@ int nand_scan_tail(struct mtd_info *mtd) !(chip->bbt_options & NAND_BBT_USE_FLASH))) return -EINVAL; + if (invalid_ecc_page_accessors(chip)) { + pr_err("Invalid ECC page accessors setup\n"); + return -EINVAL; + } + if (!(chip->options & NAND_OWN_BUFFERS)) { nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + mtd->oobsize * 3, GFP_KERNEL); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 970ceb948835..ed6fd1993be1 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -142,6 +142,12 @@ enum nand_ecc_algo { */ #define NAND_ECC_GENERIC_ERASED_CHECK BIT(0) #define NAND_ECC_MAXIMIZE BIT(1) +/* + * If your controller already sends the required NAND commands when + * reading or writing a page, then the framework is not supposed to + * send READ0 and SEQIN/PAGEPROG respectively. + */ +#define NAND_ECC_CUSTOM_PAGE_ACCESS BIT(2) /* Bit mask for flags passed to do_nand_read_ecc */ #define NAND_GET_DEVICE 0x80 @@ -186,6 +192,7 @@ enum nand_ecc_algo { /* Macros to identify the above */ #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) #define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ)) +#define NAND_HAS_SUBPAGE_WRITE(chip) !((chip)->options & NAND_NO_SUBPAGE_WRITE) /* Non chip related options */ /* This option skips the bbt scan during initialization. */ @@ -568,6 +575,11 @@ struct nand_ecc_ctrl { int page); }; +static inline int nand_standard_page_accessors(struct nand_ecc_ctrl *ecc) +{ + return !(ecc->options & NAND_ECC_CUSTOM_PAGE_ACCESS); +} + /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. -- cgit v1.2.3