diff options
Diffstat (limited to 'drivers/mtd/nand')
| -rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/au1550nd.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/bf5xx_nand.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/davinci_nand.c | 6 | ||||
| -rw-r--r-- | drivers/mtd/nand/gpio.c | 231 | ||||
| -rw-r--r-- | drivers/mtd/nand/jz4740_nand.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/nand/lpc32xx_mlc.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/lpc32xx_slc.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 33 | ||||
| -rw-r--r-- | drivers/mtd/nand/omap2.c | 662 | ||||
| -rw-r--r-- | drivers/mtd/nand/orion_nand.c | 5 | ||||
| -rw-r--r-- | drivers/mtd/nand/plat_nand.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/nand/pxa3xx_nand.c | 124 | ||||
| -rw-r--r-- | drivers/mtd/nand/s3c2410.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/sh_flctl.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/nand/sharpsl.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/tmio_nand.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/txx9ndfmc.c | 8 |
19 files changed, 447 insertions, 653 deletions
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 2d23d292943..023541d7a30 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1502,7 +1502,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if (res) goto err_ecc_ioremap; } else { - memcpy(&host->board, pdev->dev.platform_data, + memcpy(&host->board, dev_get_platdata(&pdev->dev), sizeof(struct atmel_nand_data)); } diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 217459d02b2..ae8dd7c4103 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -411,7 +411,7 @@ static int au1550nd_probe(struct platform_device *pdev) struct resource *r; int ret, cs; - pd = pdev->dev.platform_data; + pd = dev_get_platdata(&pdev->dev); if (!pd) { dev_err(&pdev->dev, "missing platform data\n"); return -ENODEV; diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 776df3694f7..d7f7df3cd96 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -171,7 +171,7 @@ static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) { - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } /* diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index c3e15a55817..758a111a3bc 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, davinci_nand_of_match); static struct davinci_nand_pdata *nand_davinci_get_pdata(struct platform_device *pdev) { - if (!pdev->dev.platform_data && pdev->dev.of_node) { + if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) { struct davinci_nand_pdata *pdata; const char *mode; u32 prop; @@ -575,13 +575,13 @@ static struct davinci_nand_pdata pdata->bbt_options = NAND_BBT_USE_FLASH; } - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } #else static struct davinci_nand_pdata *nand_davinci_get_pdata(struct platform_device *pdev) { - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } #endif diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 89065dd83d6..e826f898241 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -17,6 +17,7 @@ */ #include <linux/kernel.h> +#include <linux/err.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> @@ -86,59 +87,11 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) gpio_nand_dosync(gpiomtd); } -static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - iowrite8_rep(this->IO_ADDR_W, buf, len); -} - -static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - ioread8_rep(this->IO_ADDR_R, buf, len); -} - -static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, - int len) -{ - struct nand_chip *this = mtd->priv; - - if (IS_ALIGNED((unsigned long)buf, 2)) { - iowrite16_rep(this->IO_ADDR_W, buf, len>>1); - } else { - int i; - unsigned short *ptr = (unsigned short *)buf; - - for (i = 0; i < len; i += 2, ptr++) - writew(*ptr, this->IO_ADDR_W); - } -} - -static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - if (IS_ALIGNED((unsigned long)buf, 2)) { - ioread16_rep(this->IO_ADDR_R, buf, len>>1); - } else { - int i; - unsigned short *ptr = (unsigned short *)buf; - - for (i = 0; i < len; i += 2, ptr++) - *ptr = readw(this->IO_ADDR_R); - } -} - static int gpio_nand_devready(struct mtd_info *mtd) { struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - return gpio_get_value(gpiomtd->plat.gpio_rdy); - - return 1; + return gpio_get_value(gpiomtd->plat.gpio_rdy); } #ifdef CONFIG_OF @@ -153,6 +106,9 @@ static int gpio_nand_get_config_of(const struct device *dev, { u32 val; + if (!dev->of_node) + return -ENODEV; + if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { if (val == 2) { plat->options |= NAND_BUSWIDTH_16; @@ -211,8 +167,8 @@ static inline int gpio_nand_get_config(const struct device *dev, if (!ret) return ret; - if (dev->platform_data) { - memcpy(plat, dev->platform_data, sizeof(*plat)); + if (dev_get_platdata(dev)) { + memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); return 0; } @@ -230,145 +186,100 @@ gpio_nand_get_io_sync(struct platform_device *pdev) return platform_get_resource(pdev, IORESOURCE_MEM, 1); } -static int gpio_nand_remove(struct platform_device *dev) +static int gpio_nand_remove(struct platform_device *pdev) { - struct gpiomtd *gpiomtd = platform_get_drvdata(dev); - struct resource *res; + struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); nand_release(&gpiomtd->mtd_info); - res = gpio_nand_get_io_sync(dev); - iounmap(gpiomtd->io_sync); - if (res) - release_mem_region(res->start, resource_size(res)); - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res->start, resource_size(res)); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); gpio_set_value(gpiomtd->plat.gpio_nce, 1); - gpio_free(gpiomtd->plat.gpio_cle); - gpio_free(gpiomtd->plat.gpio_ale); - gpio_free(gpiomtd->plat.gpio_nce); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_free(gpiomtd->plat.gpio_nwp); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - gpio_free(gpiomtd->plat.gpio_rdy); - return 0; } -static void __iomem *request_and_remap(struct resource *res, size_t size, - const char *name, int *err) -{ - void __iomem *ptr; - - if (!request_mem_region(res->start, resource_size(res), name)) { - *err = -EBUSY; - return NULL; - } - - ptr = ioremap(res->start, size); - if (!ptr) { - release_mem_region(res->start, resource_size(res)); - *err = -ENOMEM; - } - return ptr; -} - -static int gpio_nand_probe(struct platform_device *dev) +static int gpio_nand_probe(struct platform_device *pdev) { struct gpiomtd *gpiomtd; - struct nand_chip *this; - struct resource *res0, *res1; + struct nand_chip *chip; + struct resource *res; struct mtd_part_parser_data ppdata = {}; int ret = 0; - if (!dev->dev.of_node && !dev->dev.platform_data) - return -EINVAL; - - res0 = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res0) + if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev)) return -EINVAL; - gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL); - if (gpiomtd == NULL) { - dev_err(&dev->dev, "failed to create NAND MTD\n"); + gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); + if (!gpiomtd) { + dev_err(&pdev->dev, "failed to create NAND MTD\n"); return -ENOMEM; } - this = &gpiomtd->nand_chip; - this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret); - if (!this->IO_ADDR_R) { - dev_err(&dev->dev, "unable to map NAND\n"); - goto err_map; - } + chip = &gpiomtd->nand_chip; - res1 = gpio_nand_get_io_sync(dev); - if (res1) { - gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret); - if (!gpiomtd->io_sync) { - dev_err(&dev->dev, "unable to map sync NAND\n"); - goto err_sync; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(chip->IO_ADDR_R)) + return PTR_ERR(chip->IO_ADDR_R); + + res = gpio_nand_get_io_sync(pdev); + if (res) { + gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpiomtd->io_sync)) + return PTR_ERR(gpiomtd->io_sync); } - ret = gpio_nand_get_config(&dev->dev, &gpiomtd->plat); + ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat); if (ret) - goto err_nce; + return ret; - ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE"); if (ret) - goto err_nce; + return ret; gpio_direction_output(gpiomtd->plat.gpio_nce, 1); + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { - ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp, + "NAND NWP"); if (ret) - goto err_nwp; - gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); + return ret; } - ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE"); + + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); if (ret) - goto err_ale; + return ret; gpio_direction_output(gpiomtd->plat.gpio_ale, 0); - ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE"); + + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); if (ret) - goto err_cle; + return ret; gpio_direction_output(gpiomtd->plat.gpio_cle, 0); + if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { - ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy, + "NAND RDY"); if (ret) - goto err_rdy; + return ret; gpio_direction_input(gpiomtd->plat.gpio_rdy); + chip->dev_ready = gpio_nand_devready; } + chip->IO_ADDR_W = chip->IO_ADDR_R; + chip->ecc.mode = NAND_ECC_SOFT; + chip->options = gpiomtd->plat.options; + chip->chip_delay = gpiomtd->plat.chip_delay; + chip->cmd_ctrl = gpio_nand_cmd_ctrl; - this->IO_ADDR_W = this->IO_ADDR_R; - this->ecc.mode = NAND_ECC_SOFT; - this->options = gpiomtd->plat.options; - this->chip_delay = gpiomtd->plat.chip_delay; - - /* install our routines */ - this->cmd_ctrl = gpio_nand_cmd_ctrl; - this->dev_ready = gpio_nand_devready; + gpiomtd->mtd_info.priv = chip; + gpiomtd->mtd_info.owner = THIS_MODULE; - if (this->options & NAND_BUSWIDTH_16) { - this->read_buf = gpio_nand_readbuf16; - this->write_buf = gpio_nand_writebuf16; - } else { - this->read_buf = gpio_nand_readbuf; - this->write_buf = gpio_nand_writebuf; - } + platform_set_drvdata(pdev, gpiomtd); - /* set the mtd private data for the nand driver */ - gpiomtd->mtd_info.priv = this; - gpiomtd->mtd_info.owner = THIS_MODULE; + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); if (nand_scan(&gpiomtd->mtd_info, 1)) { - dev_err(&dev->dev, "no nand chips found?\n"); ret = -ENXIO; goto err_wp; } @@ -377,38 +288,17 @@ static int gpio_nand_probe(struct platform_device *dev) gpiomtd->plat.adjust_parts(&gpiomtd->plat, gpiomtd->mtd_info.size); - ppdata.of_node = dev->dev.of_node; + ppdata.of_node = pdev->dev.of_node; ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata, gpiomtd->plat.parts, gpiomtd->plat.num_parts); - if (ret) - goto err_wp; - platform_set_drvdata(dev, gpiomtd); - - return 0; + if (!ret) + return 0; err_wp: if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - gpio_free(gpiomtd->plat.gpio_rdy); -err_rdy: - gpio_free(gpiomtd->plat.gpio_cle); -err_cle: - gpio_free(gpiomtd->plat.gpio_ale); -err_ale: - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_free(gpiomtd->plat.gpio_nwp); -err_nwp: - gpio_free(gpiomtd->plat.gpio_nce); -err_nce: - iounmap(gpiomtd->io_sync); - if (res1) - release_mem_region(res1->start, resource_size(res1)); -err_sync: - iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res0->start, resource_size(res0)); -err_map: + return ret; } @@ -417,6 +307,7 @@ static struct platform_driver gpio_nand_driver = { .remove = gpio_nand_remove, .driver = { .name = "gpio-nand", + .owner = THIS_MODULE, .of_match_table = of_match_ptr(gpio_nand_id_table), }, }; diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index b76460eeaf2..976d5662c3b 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -411,7 +411,7 @@ static int jz_nand_probe(struct platform_device *pdev) struct jz_nand *nand; struct nand_chip *chip; struct mtd_info *mtd; - struct jz_nand_platform_data *pdata = pdev->dev.platform_data; + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); size_t chipnr, bank_idx; uint8_t nand_maf_id = 0, nand_dev_id = 0; @@ -549,7 +549,7 @@ err_free: static int jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); - struct jz_nand_platform_data *pdata = pdev->dev.platform_data; + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); size_t i; nand_release(&nand->mtd); diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index fd1df5e13ae..a379c39dab0 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -696,7 +696,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } lpc32xx_wp_disable(host); - host->pdata = pdev->dev.platform_data; + host->pdata = dev_get_platdata(&pdev->dev); nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index be94ed5abef..81cd0719f03 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -798,7 +798,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } lpc32xx_wp_disable(host); - host->pdata = pdev->dev.platform_data; + host->pdata = dev_get_platdata(&pdev->dev); mtd = &host->mtd; chip = &host->nand_chip; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 07e5784e5cd..302c676c7ec 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1432,7 +1432,8 @@ static int mxcnd_probe(struct platform_device *pdev) err = mxcnd_probe_dt(host); if (err > 0) { - struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; + struct mxc_nand_platform_data *pdata = + dev_get_platdata(&pdev->dev); if (pdata) { host->pdata = *pdata; host->devtype_data = (struct mxc_nand_devtype_data *) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 267264320e0..b5208d1c790 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -86,33 +86,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) * @td: search pattern descriptor * * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if - * all bytes except the pattern area contain 0xff. + * good / bad block identifiers. */ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int end = 0; - uint8_t *p = buf; - if (td->options & NAND_BBT_NO_OOB) return check_pattern_no_oob(buf, td); - end = paglen + td->offs; - if (td->options & NAND_BBT_SCANEMPTY) - if (memchr_inv(p, 0xff, end)) - return -1; - p += end; - /* Compare the pattern */ - if (memcmp(p, td->pattern, td->len)) + if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) return -1; - if (td->options & NAND_BBT_SCANEMPTY) { - p += td->len; - end += td->len; - if (memchr_inv(p, 0xff, len - end)) - return -1; - } return 0; } @@ -478,15 +462,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, else numpages = 1; - if (!(bd->options & NAND_BBT_SCANEMPTY)) { - /* We need only read few bytes from the OOB area */ - scanlen = 0; - readlen = bd->len; - } else { - /* Full page content should be read */ - scanlen = mtd->writesize + mtd->oobsize; - readlen = numpages * mtd->writesize; - } + /* We need only read few bytes from the OOB area */ + scanlen = 0; + readlen = bd->len; if (chip == -1) { /* @@ -865,7 +843,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b { struct nand_chip *this = mtd->priv; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, this->buffers->databuf, bd, -1); } diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 81b80af5587..64d8e32b6ca 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -25,10 +25,8 @@ #include <linux/of.h> #include <linux/of_device.h> -#ifdef CONFIG_MTD_NAND_OMAP_BCH -#include <linux/bch.h> +#include <linux/mtd/nand_bch.h> #include <linux/platform_data/elm.h> -#endif #include <linux/platform_data/mtd-nand-omap2.h> @@ -141,6 +139,8 @@ #define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ #define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ +#define BADBLOCK_MARKER_LENGTH 2 + #ifdef CONFIG_MTD_NAND_OMAP_BCH static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, 0xac, 0x6b, 0xff, 0x99, 0x7b}; @@ -149,17 +149,6 @@ static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; /* oob info generated runtime depending on ecc algorithm and layout selected */ static struct nand_ecclayout omap_oobinfo; -/* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - */ -static uint8_t scan_ff_pattern[] = { 0xff }; -static struct nand_bbt_descr bb_descrip_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, - .offs = 0, - .len = 1, - .pattern = scan_ff_pattern, -}; - struct omap_nand_info { struct nand_hw_control controller; @@ -182,14 +171,10 @@ struct omap_nand_info { u_char *buf; int buf_len; struct gpmc_nand_regs reg; - -#ifdef CONFIG_MTD_NAND_OMAP_BCH - struct bch_control *bch; - struct nand_ecclayout ecclayout; + /* fields specific for BCHx_HW ECC scheme */ bool is_elm_used; struct device *elm_dev; struct device_node *of_node; -#endif }; /** @@ -948,7 +933,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u32 val; val = readl(info->reg.gpmc_ecc_config); - if (((val >> ECC_CONFIG_CS_SHIFT) & ~CS_MASK) != info->gpmc_cs) + if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs) return -EINVAL; /* read ecc result */ @@ -1058,8 +1043,7 @@ static int omap_dev_ready(struct mtd_info *mtd) } } -#ifdef CONFIG_MTD_NAND_OMAP_BCH - +#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH) /** * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction * @mtd: MTD device structure @@ -1140,7 +1124,9 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) /* Clear ecc and enable bits */ writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); } +#endif +#ifdef CONFIG_MTD_NAND_ECC_BCH /** * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes * @mtd: MTD device structure @@ -1225,7 +1211,9 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, return 0; } +#endif /* CONFIG_MTD_NAND_ECC_BCH */ +#ifdef CONFIG_MTD_NAND_OMAP_BCH /** * omap3_calculate_ecc_bch - Generate bytes of ECC bytes * @mtd: MTD device structure @@ -1463,7 +1451,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, /* Check if any error reported */ if (!is_error_reported) - return 0; + return stat; /* Decode BCH error using ELM module */ elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); @@ -1519,38 +1507,6 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, } /** - * omap3_correct_data_bch - Decode received data and correct errors - * @mtd: MTD device structure - * @data: page data - * @read_ecc: ecc read from nand flash - * @calc_ecc: ecc read from HW ECC registers - */ -static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data, - u_char *read_ecc, u_char *calc_ecc) -{ - int i, count; - /* cannot correct more than 8 errors */ - unsigned int errloc[8]; - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - - count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL, - errloc); - if (count > 0) { - /* correct errors */ - for (i = 0; i < count; i++) { - /* correct data only, not ecc bytes */ - if (errloc[i] < 8*512) - data[errloc[i]/8] ^= 1 << (errloc[i] & 7); - pr_debug("corrected bitflip %u\n", errloc[i]); - } - } else if (count < 0) { - pr_err("ecc unrecoverable error\n"); - } - return count; -} - -/** * omap_write_page_bch - BCH ecc based write page function for entire page * @mtd: mtd info structure * @chip: nand chip info structure @@ -1637,207 +1593,58 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, } /** - * omap3_free_bch - Release BCH ecc resources - * @mtd: MTD device structure - */ -static void omap3_free_bch(struct mtd_info *mtd) -{ - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - if (info->bch) { - free_bch(info->bch); - info->bch = NULL; - } -} - -/** - * omap3_init_bch - Initialize BCH ECC - * @mtd: MTD device structure - * @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW) + * is_elm_present - checks for presence of ELM module by scanning DT nodes + * @omap_nand_info: NAND device structure containing platform data + * @bch_type: 0x0=BCH4, 0x1=BCH8, 0x2=BCH16 */ -static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) +static int is_elm_present(struct omap_nand_info *info, + struct device_node *elm_node, enum bch_ecc bch_type) { - int max_errors; - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); -#ifdef CONFIG_MTD_NAND_OMAP_BCH8 - const int hw_errors = BCH8_MAX_ERROR; -#else - const int hw_errors = BCH4_MAX_ERROR; -#endif - enum bch_ecc bch_type; - const __be32 *parp; - int lenp; - struct device_node *elm_node; - - info->bch = NULL; - - max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? - BCH8_MAX_ERROR : BCH4_MAX_ERROR; - if (max_errors != hw_errors) { - pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", - max_errors, hw_errors); - goto fail; - } - - info->nand.ecc.size = 512; - info->nand.ecc.hwctl = omap3_enable_hwecc_bch; - info->nand.ecc.mode = NAND_ECC_HW; - info->nand.ecc.strength = max_errors; - - if (hw_errors == BCH8_MAX_ERROR) - bch_type = BCH8_ECC; - else - bch_type = BCH4_ECC; - - /* Detect availability of ELM module */ - parp = of_get_property(info->of_node, "elm_id", &lenp); - if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) { - pr_err("Missing elm_id property, fall back to Software BCH\n"); - info->is_elm_used = false; - } else { - struct platform_device *pdev; - - elm_node = of_find_node_by_phandle(be32_to_cpup(parp)); - pdev = of_find_device_by_node(elm_node); - info->elm_dev = &pdev->dev; - - if (elm_config(info->elm_dev, bch_type) == 0) - info->is_elm_used = true; - } - - if (info->is_elm_used && (mtd->writesize <= 4096)) { - - if (hw_errors == BCH8_MAX_ERROR) - info->nand.ecc.bytes = BCH8_SIZE; - else - info->nand.ecc.bytes = BCH4_SIZE; - - info->nand.ecc.correct = omap_elm_correct_data; - info->nand.ecc.calculate = omap3_calculate_ecc_bch; - info->nand.ecc.read_page = omap_read_page_bch; - info->nand.ecc.write_page = omap_write_page_bch; - } else { - /* - * software bch library is only used to detect and - * locate errors - */ - info->bch = init_bch(13, max_errors, - 0x201b /* hw polynomial */); - if (!info->bch) - goto fail; - - info->nand.ecc.correct = omap3_correct_data_bch; - - /* - * The number of corrected errors in an ecc block that will - * trigger block scrubbing defaults to the ecc strength (4 or 8) - * Set mtd->bitflip_threshold here to define a custom threshold. - */ - - if (max_errors == 8) { - info->nand.ecc.bytes = 13; - info->nand.ecc.calculate = omap3_calculate_ecc_bch8; - } else { - info->nand.ecc.bytes = 7; - info->nand.ecc.calculate = omap3_calculate_ecc_bch4; - } - } - - pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); - return 0; -fail: - omap3_free_bch(mtd); - return -1; -} - -/** - * omap3_init_bch_tail - Build an oob layout for BCH ECC correction. - * @mtd: MTD device structure - */ -static int omap3_init_bch_tail(struct mtd_info *mtd) -{ - int i, steps, offset; - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - struct nand_ecclayout *layout = &info->ecclayout; - - /* build oob layout */ - steps = mtd->writesize/info->nand.ecc.size; - layout->eccbytes = steps*info->nand.ecc.bytes; - - /* do not bother creating special oob layouts for small page devices */ - if (mtd->oobsize < 64) { - pr_err("BCH ecc is not supported on small page devices\n"); - goto fail; + struct platform_device *pdev; + info->is_elm_used = false; + /* check whether elm-id is passed via DT */ + if (!elm_node) { + pr_err("nand: error: ELM DT node not found\n"); + return -ENODEV; } - - /* reserve 2 bytes for bad block marker */ - if (layout->eccbytes+2 > mtd->oobsize) { - pr_err("no oob layout available for oobsize %d eccbytes %u\n", - mtd->oobsize, layout->eccbytes); - goto fail; + pdev = of_find_device_by_node(elm_node); + /* check whether ELM device is registered */ + if (!pdev) { + pr_err("nand: error: ELM device not found\n"); + return -ENODEV; } - - /* ECC layout compatible with RBL for BCH8 */ - if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) - offset = 2; - else - offset = mtd->oobsize - layout->eccbytes; - - /* put ecc bytes at oob tail */ - for (i = 0; i < layout->eccbytes; i++) - layout->eccpos[i] = offset + i; - - if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) - layout->oobfree[0].offset = 2 + layout->eccbytes * steps; - else - layout->oobfree[0].offset = 2; - - layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; - info->nand.ecc.layout = layout; - - if (!(info->nand.options & NAND_BUSWIDTH_16)) - info->nand.badblock_pattern = &bb_descrip_flashbased; + /* ELM module available, now configure it */ + info->elm_dev = &pdev->dev; + if (elm_config(info->elm_dev, bch_type)) + return -ENODEV; + info->is_elm_used = true; return 0; -fail: - omap3_free_bch(mtd); - return -1; -} - -#else -static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) -{ - pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n"); - return -1; -} -static int omap3_init_bch_tail(struct mtd_info *mtd) -{ - return -1; -} -static void omap3_free_bch(struct mtd_info *mtd) -{ } -#endif /* CONFIG_MTD_NAND_OMAP_BCH */ +#endif /* CONFIG_MTD_NAND_ECC_BCH */ static int omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; struct omap_nand_platform_data *pdata; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + struct nand_ecclayout *ecclayout; int err; - int i, offset; - dma_cap_mask_t mask; - unsigned sig; + int i; + dma_cap_mask_t mask; + unsigned sig; + unsigned oob_index; struct resource *res; struct mtd_part_parser_data ppdata = {}; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (pdata == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENODEV; } - info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), + GFP_KERNEL); if (!info) return -ENOMEM; @@ -1846,47 +1653,45 @@ static int omap_nand_probe(struct platform_device *pdev) spin_lock_init(&info->controller.lock); init_waitqueue_head(&info->controller.wq); - info->pdev = pdev; - + info->pdev = pdev; info->gpmc_cs = pdata->cs; info->reg = pdata->reg; - - info->mtd.priv = &info->nand; - info->mtd.name = dev_name(&pdev->dev); - info->mtd.owner = THIS_MODULE; - - info->nand.options = pdata->devsize; - info->nand.options |= NAND_SKIP_BBTSCAN; -#ifdef CONFIG_MTD_NAND_OMAP_BCH info->of_node = pdata->of_node; -#endif + mtd = &info->mtd; + mtd->priv = &info->nand; + mtd->name = dev_name(&pdev->dev); + mtd->owner = THIS_MODULE; + nand_chip = &info->nand; + nand_chip->ecc.priv = NULL; + nand_chip->options |= NAND_SKIP_BBTSCAN; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { err = -EINVAL; dev_err(&pdev->dev, "error getting memory resource\n"); - goto out_free_info; + goto return_error; } info->phys_base = res->start; info->mem_size = resource_size(res); - if (!request_mem_region(info->phys_base, info->mem_size, - pdev->dev.driver->name)) { + if (!devm_request_mem_region(&pdev->dev, info->phys_base, + info->mem_size, pdev->dev.driver->name)) { err = -EBUSY; - goto out_free_info; + goto return_error; } - info->nand.IO_ADDR_R = ioremap(info->phys_base, info->mem_size); - if (!info->nand.IO_ADDR_R) { + nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base, + info->mem_size); + if (!nand_chip->IO_ADDR_R) { err = -ENOMEM; - goto out_release_mem_region; + goto return_error; } - info->nand.controller = &info->controller; + nand_chip->controller = &info->controller; - info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; - info->nand.cmd_ctrl = omap_hwcontrol; + nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; + nand_chip->cmd_ctrl = omap_hwcontrol; /* * If RDY/BSY line is connected to OMAP then use the omap ready @@ -1896,27 +1701,37 @@ static int omap_nand_probe(struct platform_device *pdev) * device and read status register until you get a failure or success */ if (pdata->dev_ready) { - info->nand.dev_ready = omap_dev_ready; - info->nand.chip_delay = 0; + nand_chip->dev_ready = omap_dev_ready; + nand_chip->chip_delay = 0; } else { - info->nand.waitfunc = omap_wait; - info->nand.chip_delay = 50; + nand_chip->waitfunc = omap_wait; + nand_chip->chip_delay = 50; } + /* scan NAND device connected to chip controller */ + nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16; + if (nand_scan_ident(mtd, 1, NULL)) { + pr_err("nand device scan failed, may be bus-width mismatch\n"); + err = -ENXIO; + goto return_error; + } + + /* check for small page devices */ + if ((mtd->oobsize < 64) && (pdata->ecc_opt != OMAP_ECC_HAM1_CODE_HW)) { + pr_err("small page devices are not supported\n"); + err = -EINVAL; + goto return_error; + } + + /* re-populate low-level callbacks based on xfer modes */ switch (pdata->xfer_type) { case NAND_OMAP_PREFETCH_POLLED: - info->nand.read_buf = omap_read_buf_pref; - info->nand.write_buf = omap_write_buf_pref; + nand_chip->read_buf = omap_read_buf_pref; + nand_chip->write_buf = omap_write_buf_pref; break; case NAND_OMAP_POLLED: - if (info->nand.options & NAND_BUSWIDTH_16) { - info->nand.read_buf = omap_read_buf16; - info->nand.write_buf = omap_write_buf16; - } else { - info->nand.read_buf = omap_read_buf8; - info->nand.write_buf = omap_write_buf8; - } + /* Use nand_base defaults for {read,write}_buf */ break; case NAND_OMAP_PREFETCH_DMA: @@ -1927,7 +1742,7 @@ static int omap_nand_probe(struct platform_device *pdev) if (!info->dma) { dev_err(&pdev->dev, "DMA engine request failed\n"); err = -ENXIO; - goto out_release_mem_region; + goto return_error; } else { struct dma_slave_config cfg; @@ -1942,10 +1757,10 @@ static int omap_nand_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", err); - goto out_release_mem_region; + goto return_error; } - info->nand.read_buf = omap_read_buf_dma_pref; - info->nand.write_buf = omap_write_buf_dma_pref; + nand_chip->read_buf = omap_read_buf_dma_pref; + nand_chip->write_buf = omap_write_buf_dma_pref; } break; @@ -1954,34 +1769,36 @@ static int omap_nand_probe(struct platform_device *pdev) if (info->gpmc_irq_fifo <= 0) { dev_err(&pdev->dev, "error getting fifo irq\n"); err = -ENODEV; - goto out_release_mem_region; + goto return_error; } - err = request_irq(info->gpmc_irq_fifo, omap_nand_irq, - IRQF_SHARED, "gpmc-nand-fifo", info); + err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-fifo", info); if (err) { dev_err(&pdev->dev, "requesting irq(%d) error:%d", info->gpmc_irq_fifo, err); info->gpmc_irq_fifo = 0; - goto out_release_mem_region; + goto return_error; } info->gpmc_irq_count = platform_get_irq(pdev, 1); if (info->gpmc_irq_count <= 0) { dev_err(&pdev->dev, "error getting count irq\n"); err = -ENODEV; - goto out_release_mem_region; + goto return_error; } - err = request_irq(info->gpmc_irq_count, omap_nand_irq, - IRQF_SHARED, "gpmc-nand-count", info); + err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-count", info); if (err) { dev_err(&pdev->dev, "requesting irq(%d) error:%d", info->gpmc_irq_count, err); info->gpmc_irq_count = 0; - goto out_release_mem_region; + goto return_error; } - info->nand.read_buf = omap_read_buf_irq_pref; - info->nand.write_buf = omap_write_buf_irq_pref; + nand_chip->read_buf = omap_read_buf_irq_pref; + nand_chip->write_buf = omap_write_buf_irq_pref; break; @@ -1989,118 +1806,241 @@ static int omap_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "xfer_type(%d) not supported!\n", pdata->xfer_type); err = -EINVAL; - goto out_release_mem_region; + goto return_error; } - /* select the ecc type */ - if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT) - info->nand.ecc.mode = NAND_ECC_SOFT; - else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) || - (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) { - info->nand.ecc.bytes = 3; - info->nand.ecc.size = 512; - info->nand.ecc.strength = 1; - info->nand.ecc.calculate = omap_calculate_ecc; - info->nand.ecc.hwctl = omap_enable_hwecc; - info->nand.ecc.correct = omap_correct_data; - info->nand.ecc.mode = NAND_ECC_HW; - } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || - (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { - err = omap3_init_bch(&info->mtd, pdata->ecc_opt); - if (err) { + /* populate MTD interface based on ECC scheme */ + nand_chip->ecc.layout = &omap_oobinfo; + ecclayout = &omap_oobinfo; + switch (pdata->ecc_opt) { + case OMAP_ECC_HAM1_CODE_HW: + pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.bytes = 3; + nand_chip->ecc.size = 512; + nand_chip->ecc.strength = 1; + nand_chip->ecc.calculate = omap_calculate_ecc; + nand_chip->ecc.hwctl = omap_enable_hwecc; + nand_chip->ecc.correct = omap_correct_data; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + if (nand_chip->options & NAND_BUSWIDTH_16) + oob_index = BADBLOCK_MARKER_LENGTH; + else + oob_index = 1; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) + ecclayout->eccpos[i] = oob_index; + /* no reserved-marker in ecclayout for this ecc-scheme */ + ecclayout->oobfree->offset = + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + break; + + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: +#ifdef CONFIG_MTD_NAND_ECC_BCH + pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 7; + nand_chip->ecc.strength = 4; + nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.correct = nand_bch_correct_data; + nand_chip->ecc.calculate = omap3_calculate_ecc_bch4; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) { + ecclayout->eccpos[i] = oob_index; + if (((i + 1) % nand_chip->ecc.bytes) == 0) + oob_index++; + } + /* include reserved-marker in ecclayout->oobfree calculation */ + ecclayout->oobfree->offset = 1 + + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + /* software bch library is used for locating errors */ + nand_chip->ecc.priv = nand_bch_init(mtd, + nand_chip->ecc.size, + nand_chip->ecc.bytes, + &nand_chip->ecc.layout); + if (!nand_chip->ecc.priv) { + pr_err("nand: error: unable to use s/w BCH library\n"); err = -EINVAL; - goto out_release_mem_region; } - } + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif - /* DIP switches on some boards change between 8 and 16 bit - * bus widths for flash. Try the other width if the first try fails. - */ - if (nand_scan_ident(&info->mtd, 1, NULL)) { - info->nand.options ^= NAND_BUSWIDTH_16; - if (nand_scan_ident(&info->mtd, 1, NULL)) { - err = -ENXIO; - goto out_release_mem_region; + case OMAP_ECC_BCH4_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH + pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + nand_chip->ecc.bytes = 7 + 1; + nand_chip->ecc.strength = 4; + nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.calculate = omap3_calculate_ecc_bch; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) + ecclayout->eccpos[i] = oob_index; + /* reserved marker already included in ecclayout->eccbytes */ + ecclayout->oobfree->offset = + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + /* This ECC scheme requires ELM H/W block */ + if (is_elm_present(info, pdata->elm_of_node, BCH4_ECC) < 0) { + pr_err("nand: error: could not initialize ELM\n"); + err = -ENODEV; + goto return_error; } - } + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif - /* rom code layout */ - if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) { + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#ifdef CONFIG_MTD_NAND_ECC_BCH + pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 13; + nand_chip->ecc.strength = 8; + nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.correct = nand_bch_correct_data; + nand_chip->ecc.calculate = omap3_calculate_ecc_bch8; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) { + ecclayout->eccpos[i] = oob_index; + if (((i + 1) % nand_chip->ecc.bytes) == 0) + oob_index++; + } + /* include reserved-marker in ecclayout->oobfree calculation */ + ecclayout->oobfree->offset = 1 + + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + /* software bch library is used for locating errors */ + nand_chip->ecc.priv = nand_bch_init(mtd, + nand_chip->ecc.size, + nand_chip->ecc.bytes, + &nand_chip->ecc.layout); + if (!nand_chip->ecc.priv) { + pr_err("nand: error: unable to use s/w BCH library\n"); + err = -EINVAL; + goto return_error; + } + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif - if (info->nand.options & NAND_BUSWIDTH_16) - offset = 2; - else { - offset = 1; - info->nand.badblock_pattern = &bb_descrip_flashbased; + case OMAP_ECC_BCH8_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH + pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + nand_chip->ecc.bytes = 13 + 1; + nand_chip->ecc.strength = 8; + nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.calculate = omap3_calculate_ecc_bch; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + /* This ECC scheme requires ELM H/W block */ + err = is_elm_present(info, pdata->elm_of_node, BCH8_ECC); + if (err < 0) { + pr_err("nand: error: could not initialize ELM\n"); + goto return_error; } - omap_oobinfo.eccbytes = 3 * (info->mtd.oobsize/16); - for (i = 0; i < omap_oobinfo.eccbytes; i++) - omap_oobinfo.eccpos[i] = i+offset; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) + ecclayout->eccpos[i] = oob_index; + /* reserved marker already included in ecclayout->eccbytes */ + ecclayout->oobfree->offset = + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif - omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes; - omap_oobinfo.oobfree->length = info->mtd.oobsize - - (offset + omap_oobinfo.eccbytes); + default: + pr_err("nand: error: invalid or unsupported ECC scheme\n"); + err = -EINVAL; + goto return_error; + } - info->nand.ecc.layout = &omap_oobinfo; - } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || - (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { - /* build OOB layout for BCH ECC correction */ - err = omap3_init_bch_tail(&info->mtd); - if (err) { - err = -EINVAL; - goto out_release_mem_region; - } + /* all OOB bytes from oobfree->offset till end off OOB are free */ + ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset; + /* check if NAND device's OOB is enough to store ECC signatures */ + if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) { + pr_err("not enough OOB bytes required = %d, available=%d\n", + ecclayout->eccbytes, mtd->oobsize); + err = -EINVAL; + goto return_error; } /* second phase scan */ - if (nand_scan_tail(&info->mtd)) { + if (nand_scan_tail(mtd)) { err = -ENXIO; - goto out_release_mem_region; + goto return_error; } ppdata.of_node = pdata->of_node; - mtd_device_parse_register(&info->mtd, NULL, &ppdata, pdata->parts, + mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts, pdata->nr_parts); - platform_set_drvdata(pdev, &info->mtd); + platform_set_drvdata(pdev, mtd); return 0; -out_release_mem_region: +return_error: if (info->dma) dma_release_channel(info->dma); - if (info->gpmc_irq_count > 0) - free_irq(info->gpmc_irq_count, info); - if (info->gpmc_irq_fifo > 0) - free_irq(info->gpmc_irq_fifo, info); - release_mem_region(info->phys_base, info->mem_size); -out_free_info: - kfree(info); - + if (nand_chip->ecc.priv) { + nand_bch_free(nand_chip->ecc.priv); + nand_chip->ecc.priv = NULL; + } return err; } static int omap_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nand_chip = mtd->priv; struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - omap3_free_bch(&info->mtd); - - platform_set_drvdata(pdev, NULL); + if (nand_chip->ecc.priv) { + nand_bch_free(nand_chip->ecc.priv); + nand_chip->ecc.priv = NULL; + } if (info->dma) dma_release_channel(info->dma); - - if (info->gpmc_irq_count > 0) - free_irq(info->gpmc_irq_count, info); - if (info->gpmc_irq_fifo > 0) - free_irq(info->gpmc_irq_fifo, info); - - /* Release NAND device, its internal structures and partitions */ - nand_release(&info->mtd); - iounmap(info->nand.IO_ADDR_R); - release_mem_region(info->phys_base, info->mem_size); - kfree(info); + nand_release(mtd); return 0; } diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 8fbd0020861..834cb3541b8 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -130,8 +130,9 @@ static int __init orion_nand_probe(struct platform_device *pdev) if (!of_property_read_u32(pdev->dev.of_node, "chip-delay", &val)) board->chip_delay = (u8)val; - } else - board = pdev->dev.platform_data; + } else { + board = dev_get_platdata(&pdev->dev); + } mtd->priv = nc; mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index c004566a9ad..08766d1ea44 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -30,7 +30,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL }; */ static int plat_nand_probe(struct platform_device *pdev) { - struct platform_nand_data *pdata = pdev->dev.platform_data; + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); struct mtd_part_parser_data ppdata; struct plat_nand_data *data; struct resource *res; @@ -137,7 +137,7 @@ out_free: static int plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); - struct platform_nand_data *pdata = pdev->dev.platform_data; + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index dec80ca6a5c..501e380211e 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -506,6 +506,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, info->buf_count = 0; info->oob_size = 0; info->use_ecc = 0; + info->use_dma = (use_dma) ? 1 : 0; info->is_ready = 0; info->retcode = ERR_NONE; if (info->cs != 0) @@ -589,12 +590,23 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, | addr_cycle; break; + case NAND_CMD_PARAM: + cmd = NAND_CMD_PARAM; + info->buf_count = 256; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | NDCB0_ADDR_CYC(1) + | cmd; + info->ndcb1 = (column & 0xFF); + info->data_size = 256; + break; + case NAND_CMD_READID: cmd = host->cmdset->read_id; info->buf_count = host->read_id_bytes; info->ndcb0 |= NDCB0_CMD_TYPE(3) | NDCB0_ADDR_CYC(1) | cmd; + info->ndcb1 = (column & 0xFF); info->data_size = 8; break; @@ -803,7 +815,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, const struct pxa3xx_nand_flash *f) { struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); struct pxa3xx_nand_host *host = info->host[info->cs]; uint32_t ndcr = 0x0; /* enable all interrupts */ @@ -912,6 +924,18 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) return 0; } +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ + struct platform_device *pdev = info->pdev; + if (use_dma) { + pxa_free_dma(info->data_dma_ch); + dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE, + info->data_buff, info->data_buff_phys); + } else { + kfree(info->data_buff); + } +} + static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info) { struct mtd_info *mtd; @@ -934,7 +958,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) struct pxa3xx_nand_host *host = mtd->priv; struct pxa3xx_nand_info *info = host->info_data; struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL; const struct pxa3xx_nand_flash *f = NULL; struct nand_chip *chip = mtd->priv; @@ -1034,13 +1058,11 @@ static int alloc_nand_resource(struct platform_device *pdev) struct resource *r; int ret, irq, cs; - pdata = pdev->dev.platform_data; - info = kzalloc(sizeof(*info) + (sizeof(*mtd) + - sizeof(*host)) * pdata->num_cs, GFP_KERNEL); - if (!info) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + pdata = dev_get_platdata(&pdev->dev); + info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) + + sizeof(*host)) * pdata->num_cs, GFP_KERNEL); + if (!info) return -ENOMEM; - } info->pdev = pdev; for (cs = 0; cs < pdata->num_cs; cs++) { @@ -1069,20 +1091,21 @@ static int alloc_nand_resource(struct platform_device *pdev) spin_lock_init(&chip->controller->lock); init_waitqueue_head(&chip->controller->wq); - info->clk = clk_get(&pdev->dev, NULL); + info->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to get nand clock\n"); - ret = PTR_ERR(info->clk); - goto fail_free_mtd; + return PTR_ERR(info->clk); } - clk_enable(info->clk); + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; /* * This is a dirty hack to make this driver work from devicetree * bindings. It can be removed once we have a prober DMA controller * framework for DT. */ - if (pdev->dev.of_node && cpu_is_pxa3xx()) { + if (pdev->dev.of_node && of_machine_is_compatible("marvell,pxa3xx")) { info->drcmr_dat = 97; info->drcmr_cmd = 99; } else { @@ -1090,7 +1113,7 @@ static int alloc_nand_resource(struct platform_device *pdev) if (r == NULL) { dev_err(&pdev->dev, "no resource defined for data DMA\n"); ret = -ENXIO; - goto fail_put_clk; + goto fail_disable_clk; } info->drcmr_dat = r->start; @@ -1098,7 +1121,7 @@ static int alloc_nand_resource(struct platform_device *pdev) if (r == NULL) { dev_err(&pdev->dev, "no resource defined for command DMA\n"); ret = -ENXIO; - goto fail_put_clk; + goto fail_disable_clk; } info->drcmr_cmd = r->start; } @@ -1107,34 +1130,20 @@ static int alloc_nand_resource(struct platform_device *pdev) if (irq < 0) { dev_err(&pdev->dev, "no IRQ resource defined\n"); ret = -ENXIO; - goto fail_put_clk; + goto fail_disable_clk; } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IO memory resource defined\n"); - ret = -ENODEV; - goto fail_put_clk; - } - - r = request_mem_region(r->start, resource_size(r), pdev->name); - if (r == NULL) { - dev_err(&pdev->dev, "failed to request memory resource\n"); - ret = -EBUSY; - goto fail_put_clk; - } - - info->mmio_base = ioremap(r->start, resource_size(r)); - if (info->mmio_base == NULL) { - dev_err(&pdev->dev, "ioremap() failed\n"); - ret = -ENODEV; - goto fail_free_res; + info->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(info->mmio_base)) { + ret = PTR_ERR(info->mmio_base); + goto fail_disable_clk; } info->mmio_phys = r->start; ret = pxa3xx_nand_init_buff(info); if (ret) - goto fail_free_io; + goto fail_disable_clk; /* initialize all interrupts to be disabled */ disable_int(info, NDSR_MASK); @@ -1152,21 +1161,9 @@ static int alloc_nand_resource(struct platform_device *pdev) fail_free_buf: free_irq(irq, info); - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); -fail_free_io: - iounmap(info->mmio_base); -fail_free_res: - release_mem_region(r->start, resource_size(r)); -fail_put_clk: - clk_disable(info->clk); - clk_put(info->clk); -fail_free_mtd: - kfree(info); + pxa3xx_nand_free_buff(info); +fail_disable_clk: + clk_disable_unprepare(info->clk); return ret; } @@ -1174,35 +1171,22 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) { struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); struct pxa3xx_nand_platform_data *pdata; - struct resource *r; int irq, cs; if (!info) return 0; - pdata = pdev->dev.platform_data; - platform_set_drvdata(pdev, NULL); + pdata = dev_get_platdata(&pdev->dev); irq = platform_get_irq(pdev, 0); if (irq >= 0) free_irq(irq, info); - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); - - iounmap(info->mmio_base); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); + pxa3xx_nand_free_buff(info); - clk_disable(info->clk); - clk_put(info->clk); + clk_disable_unprepare(info->clk); for (cs = 0; cs < pdata->num_cs; cs++) nand_release(info->host[cs]->mtd); - kfree(info); return 0; } @@ -1211,7 +1195,7 @@ static struct of_device_id pxa3xx_nand_dt_ids[] = { { .compatible = "marvell,pxa3xx-nand" }, {} }; -MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids); +MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); static int pxa3xx_nand_probe_dt(struct platform_device *pdev) { @@ -1255,7 +1239,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) if (ret) return ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no platform data defined\n"); return -ENODEV; @@ -1302,7 +1286,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) struct mtd_info *mtd; int cs; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (info->state) { dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); return -EAGAIN; @@ -1323,7 +1307,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev) struct mtd_info *mtd; int cs; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); /* We don't want to handle interrupt without calling mtd routine */ disable_int(info, NDCR_INT_MASK); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d65afd23e17..b707d0f8247 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -150,7 +150,7 @@ static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) { - return dev->dev.platform_data; + return dev_get_platdata(&dev->dev); } static inline int allow_clk_suspend(struct s3c2410_nand_info *info) diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index e57e18e8c28..a3c84ebbe39 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -137,7 +137,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl) dma_cap_mask_t mask; struct dma_slave_config cfg; struct platform_device *pdev = flctl->pdev; - struct sh_flctl_platform_data *pdata = pdev->dev.platform_data; + struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret; if (!pdata) @@ -1131,7 +1131,7 @@ static int flctl_probe(struct platform_device *pdev) if (pdev->dev.of_node) pdata = flctl_parse_dt(&pdev->dev); else - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no setup data defined\n"); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 127bc427182..26b27201520 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -112,7 +112,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev) struct resource *r; int err = 0; struct sharpsl_nand *sharpsl; - struct sharpsl_nand_platform_data *data = pdev->dev.platform_data; + struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); if (!data) { dev_err(&pdev->dev, "no platform data!\n"); diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 508e9e04b09..396530d87ec 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -357,7 +357,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct tmio_nand_data *data = dev->dev.platform_data; + struct tmio_nand_data *data = dev_get_platdata(&dev->dev); struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *ccr = platform_get_resource(dev, diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 7ed654c68b0..388b13a6d7e 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -87,7 +87,7 @@ static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) { struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); return drvdata->base + (reg << plat->shift); } @@ -138,7 +138,7 @@ static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, struct nand_chip *chip = mtd->priv; struct txx9ndfmc_priv *txx9_priv = chip->priv; struct platform_device *dev = txx9_priv->dev; - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); if (ctrl & NAND_CTRL_CHANGE) { u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); @@ -225,7 +225,7 @@ static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) static void txx9ndfmc_initialize(struct platform_device *dev) { - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int tmout = 100; @@ -274,7 +274,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) static int __init txx9ndfmc_probe(struct platform_device *dev) { - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); int hold, spw; int i; struct txx9ndfmc_drvdata *drvdata; |