From ac48e800c077738f18d3789d5eb8958bf52cf2d7 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:43 +0100 Subject: mtd: docg3: fix debug log verbosity Change the NOP debug log verbosity to very verbose to unburden log analysis. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index bdcf5df982e..6f5233da392 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -143,7 +143,7 @@ static void doc_delay(struct docg3 *docg3, int nbNOPs) { int i; - doc_dbg("NOP x %d\n", nbNOPs); + doc_vdbg("NOP x %d\n", nbNOPs); for (i = 0; i < nbNOPs; i++) doc_writeb(docg3, 0, DOC_NOP); } -- cgit v1.2.3-70-g09d2 From 84a930581ef45e81cff0450b19ce5a70f24d50bb Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:44 +0100 Subject: mtd: docg3: fix tracing of IO in writeb Writeb was incorrectly traced as a 16 bits write, instead of a 8 bits write. Fix it by tracing the correct width. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 6f5233da392..8942a751342 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -82,7 +82,7 @@ static inline u16 doc_readw(struct docg3 *docg3, u16 reg) static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg) { writeb(val, docg3->base + reg); - trace_docg3_io(1, 16, reg, val); + trace_docg3_io(1, 8, reg, val); } static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg) -- cgit v1.2.3-70-g09d2 From dbc26d98f8b4a3d1d2f25d997a0a0aba4ea46add Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:45 +0100 Subject: mtd: docg3: fix protection areas reading The protection areas boundaries were on 16bit registers, not 8bit. This is consistent with block numbers, which can extend up to 4096 on bigger chips (and is 2048 on the docg3). Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 8942a751342..5834e6e65d1 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -852,13 +852,15 @@ static int dbg_protection_show(struct seq_file *s, void *p) { struct docg3 *docg3 = (struct docg3 *)s->private; int pos = 0; - int protect = doc_register_readb(docg3, DOC_PROTECTION); - int dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); - int dps0_low = doc_register_readb(docg3, DOC_DPS0_ADDRLOW); - int dps0_high = doc_register_readb(docg3, DOC_DPS0_ADDRHIGH); - int dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); - int dps1_low = doc_register_readb(docg3, DOC_DPS1_ADDRLOW); - int dps1_high = doc_register_readb(docg3, DOC_DPS1_ADDRHIGH); + int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; + + protect = doc_register_readb(docg3, DOC_PROTECTION); + dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); + dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW); + dps0_high = doc_register_readw(docg3, DOC_DPS0_ADDRHIGH); + dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); + dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW); + dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH); pos += seq_printf(s, "Protection = 0x%02x (", protect); -- cgit v1.2.3-70-g09d2 From 34db8a5a72c5c5eb5d2811f237dcc9bf3c6425a9 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:46 +0100 Subject: mtd: docg3: fix BCH registers BCH registers are contiguous, not on every byte. Fix the register definitions. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 0d407be2459..62af5aaeec4 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -105,7 +105,7 @@ #define DOC_ECCCONF1 0x1042 #define DOC_ECCPRESET 0x1044 #define DOC_HAMMINGPARITY 0x1046 -#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 1)) +#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 0)) #define DOC_PROTECTION 0x1056 #define DOC_DPS0_ADDRLOW 0x1060 -- cgit v1.2.3-70-g09d2 From 32a50b3a457fda9606fd0946eb77ba28a520cd7f Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:47 +0100 Subject: mtd: docg3: fix reading oob+data without correction Fix the docg3 reads to be able to cope with all possible data buffer / oob buffer / file mode combinations from docg3_read_oob(). This especially ensures that raw reads do not use ECC corrections, and AUTOOOB and PLACEOOB do use ECC correction. The approach is to empty docg3_read() and make it a wrapper to docg3_read_oob(). As docg3_read_oob() handles all the funny cases (no data buffer but oob buffer, data buffer but no oob buffer, ...), docg3_read() is just a special use of docg3_read_oob(). Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 194 ++++++++++++++++++++++---------------------- 1 file changed, 95 insertions(+), 99 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 5834e6e65d1..1c2f54d23c7 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -196,8 +196,8 @@ static int doc_reset_seq(struct docg3 *docg3) /** * doc_read_data_area - Read data from data area * @docg3: the device - * @buf: the buffer to fill in - * @len: the lenght to read + * @buf: the buffer to fill in (might be NULL is dummy reads) + * @len: the length to read * @first: first time read, DOC_READADDRESS should be set * * Reads bytes from flash data. Handles the single byte / even bytes reads. @@ -218,8 +218,10 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, dst16 = buf; for (i = 0; i < len4; i += 2) { data16 = doc_readw(docg3, DOC_IOSPACE_DATA); - *dst16 = data16; - dst16++; + if (dst16) { + *dst16 = data16; + dst16++; + } } if (cdr) { @@ -229,8 +231,10 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, dst8 = (u8 *)dst16; for (i = 0; i < cdr; i++) { data8 = doc_readb(docg3, DOC_IOSPACE_DATA); - *dst8 = data8; - dst8++; + if (dst8) { + *dst8 = data8; + dst8++; + } } } } @@ -542,96 +546,109 @@ static void calc_block_sector(loff_t from, int *block0, int *block1, int *page, } /** - * doc_read - Read bytes from flash + * doc_read_oob - Read out of band bytes from flash * @mtd: the device * @from: the offset from first block and first page, in bytes, aligned on page * size - * @len: the number of bytes to read (must be a multiple of 4) - * @retlen: the number of bytes actually read - * @buf: the filled in buffer + * @ops: the mtd oob structure * - * Reads flash memory pages. This function does not read the OOB chunk, but only - * the page data. + * Reads flash memory OOB area of pages. * * Returns 0 if read successfull, of -EIO, -EINVAL if an error occured */ -static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int doc_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { struct docg3 *docg3 = mtd->priv; - int block0, block1, page, readlen, ret, ofs = 0; - int syn[DOC_ECC_BCH_SIZE], eccconf1; - u8 oob[DOC_LAYOUT_OOB_SIZE]; + int block0, block1, page, ret, ofs = 0; + u8 *oobbuf = ops->oobbuf; + u8 *buf = ops->datbuf; + size_t len, ooblen, nbdata, nboob; + u8 calc_ecc[DOC_ECC_BCH_SIZE], eccconf1; + + if (buf) + len = ops->len; + else + len = 0; + if (oobbuf) + ooblen = ops->ooblen; + else + ooblen = 0; + + if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) + oobbuf += ops->ooboffs; + + doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", + from, ops->mode, buf, len, oobbuf, ooblen); + if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % DOC_LAYOUT_OOB_SIZE) || + (from % DOC_LAYOUT_PAGE_SIZE)) + return -EINVAL; ret = -EINVAL; - doc_dbg("doc_read(from=%lld, len=%zu, buf=%p)\n", from, len, buf); - if (from % DOC_LAYOUT_PAGE_SIZE) - goto err; - if (len % 4) - goto err; - calc_block_sector(from, &block0, &block1, &page, &ofs); + calc_block_sector(from + len, &block0, &block1, &page, &ofs); if (block1 > docg3->max_block) goto err; - *retlen = 0; + ops->oobretlen = 0; + ops->retlen = 0; ret = 0; - readlen = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE); - while (!ret && len > 0) { - readlen = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE); + while (!ret && (len > 0 || ooblen > 0)) { + calc_block_sector(from, &block0, &block1, &page, &ofs); + nbdata = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE); + nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); if (ret < 0) goto err; ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES); if (ret < 0) goto err_in_read; - ret = doc_read_page_getbytes(docg3, readlen, buf, 1); - if (ret < readlen) + ret = doc_read_page_getbytes(docg3, nbdata, buf, 1); + if (ret < nbdata) goto err_in_read; - ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE, - oob, 0); - if (ret < DOC_LAYOUT_OOB_SIZE) + doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata, + NULL, 0); + ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0); + if (ret < nboob) goto err_in_read; + doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, + NULL, 0); - *retlen += readlen; - buf += readlen; - len -= readlen; - - ofs ^= DOC_LAYOUT_PAGE_OOB_SIZE; - if (ofs == 0) - page += 2; - if (page > DOC_ADDR_PAGE_MASK) { - page = 0; - block0 += 2; - block1 += 2; - } - - /* - * There should be a BCH bitstream fixing algorithm here ... - * By now, a page read failure is triggered by BCH error - */ - doc_get_hw_bch_syndroms(docg3, syn); + doc_get_hw_bch_syndroms(docg3, calc_ecc); eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); - doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - oob[0], oob[1], oob[2], oob[3], oob[4], - oob[5], oob[6]); - doc_dbg("OOB - HAMMING: %02x\n", oob[7]); - doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - oob[8], oob[9], oob[10], oob[11], oob[12], - oob[13], oob[14]); - doc_dbg("OOB - UNUSED: %02x\n", oob[15]); + if (nboob >= DOC_LAYOUT_OOB_SIZE) { + doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + oobbuf[0], oobbuf[1], oobbuf[2], oobbuf[3], + oobbuf[4], oobbuf[5], oobbuf[6]); + doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]); + doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + oobbuf[8], oobbuf[9], oobbuf[10], oobbuf[11], + oobbuf[12], oobbuf[13], oobbuf[14]); + doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]); + } doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); - doc_dbg("ECC BCH syndrom: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - syn[0], syn[1], syn[2], syn[3], syn[4], syn[5], syn[6]); + doc_dbg("ECC CALC_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + calc_ecc[0], calc_ecc[1], calc_ecc[2], + calc_ecc[3], calc_ecc[4], calc_ecc[5], + calc_ecc[6]); ret = -EBADMSG; if (block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) { - if (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) + if ((eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) && + (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN)) goto err_in_read; if (is_prot_seq_error(docg3)) goto err_in_read; } + doc_read_page_finish(docg3); + ops->retlen += nbdata; + ops->oobretlen += nboob; + buf += nbdata; + oobbuf += nboob; + len -= nbdata; + ooblen -= nboob; + from += DOC_LAYOUT_PAGE_SIZE; } return 0; @@ -642,54 +659,33 @@ err: } /** - * doc_read_oob - Read out of band bytes from flash + * doc_read - Read bytes from flash * @mtd: the device * @from: the offset from first block and first page, in bytes, aligned on page * size - * @ops: the mtd oob structure + * @len: the number of bytes to read (must be a multiple of 4) + * @retlen: the number of bytes actually read + * @buf: the filled in buffer * - * Reads flash memory OOB area of pages. + * Reads flash memory pages. This function does not read the OOB chunk, but only + * the page data. * * Returns 0 if read successfull, of -EIO, -EINVAL if an error occured */ -static int doc_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) +static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { - struct docg3 *docg3 = mtd->priv; - int block0, block1, page, ofs, ret; - u8 *buf = ops->oobbuf; - size_t len = ops->ooblen; + struct mtd_oob_ops ops; + size_t ret; - doc_dbg("doc_read_oob(from=%lld, buf=%p, len=%zu)\n", from, buf, len); - if (len != DOC_LAYOUT_OOB_SIZE) - return -EINVAL; + memset(&ops, 0, sizeof(ops)); + ops.datbuf = buf; + ops.len = len; + ops.mode = MTD_OPS_AUTO_OOB; - switch (ops->mode) { - case MTD_OPS_PLACE_OOB: - buf += ops->ooboffs; - break; - default: - break; - } - - calc_block_sector(from, &block0, &block1, &page, &ofs); - if (block1 > docg3->max_block) - return -EINVAL; - - ret = doc_read_page_prepare(docg3, block0, block1, page, - ofs + DOC_LAYOUT_PAGE_SIZE); - if (!ret) - ret = doc_read_page_ecc_init(docg3, DOC_LAYOUT_OOB_SIZE); - if (!ret) - ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE, - buf, 1); - doc_read_page_finish(docg3); - - if (ret > 0) - ops->oobretlen = ret; - else - ops->oobretlen = 0; - return (ret > 0) ? 0 : ret; + ret = doc_read_oob(mtd, from, &ops); + *retlen = ops.retlen; + return ret; } static int doc_reload_bbt(struct docg3 *docg3) -- cgit v1.2.3-70-g09d2 From ae9d4934b2d76a9fba21f5ad3692378d0e7fc24b Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:48 +0100 Subject: mtd: docg3: add multiple floor support Add support for multiple floors, ie. cascaded docg3 chips. There might be 4 docg3 chips cascaded, sharing the same address space, and providing up to 4 times the storage capacity of a unique chip. Each floor will be seen as an independant mtd device. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 181 ++++++++++++++++++++++++++++++-------------- drivers/mtd/devices/docg3.h | 1 + 2 files changed, 126 insertions(+), 56 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 1c2f54d23c7..6eca7f6c072 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -948,7 +948,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) switch (chip_id) { case DOC_CHIPID_G3: - mtd->name = "DiskOnChip G3"; + mtd->name = kasprintf(GFP_KERNEL, "DiskOnChip G3 floor %d", + docg3->device_id); docg3->max_block = 2047; break; } @@ -975,22 +976,24 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) } /** - * doc_probe - Probe the IO space for a DiskOnChip G3 chip - * @pdev: platform device + * doc_probe_device - Check if a device is available + * @base: the io space where the device is probed + * @floor: the floor of the probed device + * @dev: the device * - * Probes for a G3 chip at the specified IO space in the platform data - * ressources. + * Checks whether a device at the specified IO range, and floor is available. * - * Returns 0 on success, -ENOMEM, -ENXIO on error + * Returns a mtd_info struct if there is a device, ENODEV if none found, ENOMEM + * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is + * launched. */ -static int __init docg3_probe(struct platform_device *pdev) +static struct mtd_info *doc_probe_device(void __iomem *base, int floor, + struct device *dev) { - struct device *dev = &pdev->dev; - struct docg3 *docg3; - struct mtd_info *mtd; - struct resource *ress; int ret, bbt_nbpages; u16 chip_id, chip_id_inv; + struct docg3 *docg3; + struct mtd_info *mtd; ret = -ENOMEM; docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL); @@ -1000,69 +1003,132 @@ static int __init docg3_probe(struct platform_device *pdev) if (!mtd) goto nomem2; mtd->priv = docg3; + bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, + 8 * DOC_LAYOUT_PAGE_SIZE); + docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); + if (!docg3->bbt) + goto nomem3; - ret = -ENXIO; - ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!ress) { - dev_err(dev, "No I/O memory resource defined\n"); - goto noress; - } - docg3->base = ioremap(ress->start, DOC_IOSPACE_SIZE); - - docg3->dev = &pdev->dev; - docg3->device_id = 0; + docg3->dev = dev; + docg3->device_id = floor; + docg3->base = base; doc_set_device_id(docg3, docg3->device_id); - doc_set_asic_mode(docg3, DOC_ASICMODE_RESET); + if (!floor) + doc_set_asic_mode(docg3, DOC_ASICMODE_RESET); doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL); chip_id = doc_register_readw(docg3, DOC_CHIPID); chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV); - ret = -ENODEV; + ret = 0; if (chip_id != (u16)(~chip_id_inv)) { - doc_info("No device found at IO addr %p\n", - (void *)ress->start); - goto nochipfound; + goto nomem3; } switch (chip_id) { case DOC_CHIPID_G3: - doc_info("Found a G3 DiskOnChip at addr %p\n", - (void *)ress->start); + doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n", + base, floor); break; default: doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id); - goto nochipfound; + goto nomem3; } doc_set_driver_info(chip_id, mtd); - platform_set_drvdata(pdev, mtd); - ret = -ENOMEM; - bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, - 8 * DOC_LAYOUT_PAGE_SIZE); - docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); - if (!docg3->bbt) - goto nochipfound; doc_reload_bbt(docg3); + return mtd; - ret = mtd_device_parse_register(mtd, part_probes, - NULL, NULL, 0); - if (ret) - goto register_error; - - doc_dbg_register(docg3); - return 0; - -register_error: - kfree(docg3->bbt); -nochipfound: - iounmap(docg3->base); -noress: +nomem3: kfree(mtd); nomem2: kfree(docg3); nomem1: + return ERR_PTR(ret); +} + +/** + * doc_release_device - Release a docg3 floor + * @mtd: the device + */ +static void doc_release_device(struct mtd_info *mtd) +{ + struct docg3 *docg3 = mtd->priv; + + mtd_device_unregister(mtd); + kfree(docg3->bbt); + kfree(docg3); + kfree(mtd->name); + kfree(mtd); +} + +/** + * doc_probe - Probe the IO space for a DiskOnChip G3 chip + * @pdev: platform device + * + * Probes for a G3 chip at the specified IO space in the platform data + * ressources. The floor 0 must be available. + * + * Returns 0 on success, -ENOMEM, -ENXIO on error + */ +static int __init docg3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtd_info *mtd; + struct resource *ress; + void __iomem *base; + int ret, floor, found = 0; + struct mtd_info **docg3_floors; + + ret = -ENXIO; + ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ress) { + dev_err(dev, "No I/O memory resource defined\n"); + goto noress; + } + base = ioremap(ress->start, DOC_IOSPACE_SIZE); + + ret = -ENOMEM; + docg3_floors = kzalloc(sizeof(*docg3_floors) * DOC_MAX_NBFLOORS, + GFP_KERNEL); + if (!docg3_floors) + goto nomem; + + ret = 0; + for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { + mtd = doc_probe_device(base, floor, dev); + if (floor == 0 && !mtd) + goto notfound; + if (!IS_ERR_OR_NULL(mtd)) + ret = mtd_device_parse_register(mtd, part_probes, + NULL, NULL, 0); + else + ret = PTR_ERR(mtd); + docg3_floors[floor] = mtd; + if (ret) + goto err_probe; + if (mtd) + found++; + } + + if (!found) + goto notfound; + + platform_set_drvdata(pdev, docg3_floors); + doc_dbg_register(docg3_floors[0]->priv); + return 0; + +notfound: + ret = -ENODEV; + dev_info(dev, "No supported DiskOnChip found\n"); +err_probe: + for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) + if (docg3_floors[floor]) + doc_release_device(docg3_floors[floor]); +nomem: + iounmap(base); +noress: return ret; } @@ -1074,15 +1140,18 @@ nomem1: */ static int __exit docg3_release(struct platform_device *pdev) { - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct docg3 *docg3 = mtd->priv; + struct mtd_info **docg3_floors = platform_get_drvdata(pdev); + struct docg3 *docg3 = docg3_floors[0]->priv; + void __iomem *base = docg3->base; + int floor; doc_dbg_unregister(docg3); - mtd_device_unregister(mtd); - iounmap(docg3->base); - kfree(docg3->bbt); - kfree(docg3); - kfree(mtd); + for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) + if (docg3_floors[floor]) + doc_release_device(docg3_floors[floor]); + + kfree(docg3_floors); + iounmap(base); return 0; } diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 62af5aaeec4..75df3a1060f 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -80,6 +80,7 @@ #define DOC_CHIPID_G3 0x200 #define DOC_ERASE_MARK 0xaa +#define DOC_MAX_NBFLOORS 4 /* * Flash registers */ -- cgit v1.2.3-70-g09d2 From 732b63bd8c70bf8fbc50d3d3cd56c748edb8cfac Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:49 +0100 Subject: mtd: docg3: add OOB layout to mtdinfo Add OOB layout description for docg3, so that userspace can use this information to setup the data for write_oob(). Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 6eca7f6c072..27c4feae132 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -63,6 +63,20 @@ * */ +/** + * struct docg3_oobinfo - DiskOnChip G3 OOB layout + * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC) + * @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC) + * @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15 + * @oobavail: 8 available bytes remaining after ECC toll + */ +static struct nand_ecclayout docg3_oobinfo = { + .eccbytes = 8, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14}, + .oobfree = {{0, 7}, {15, 1} }, + .oobavail = 8, +}; + static inline u8 doc_readb(struct docg3 *docg3, u16 reg) { u8 val = readb(docg3->base + reg); @@ -973,6 +987,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) mtd->write_oob = NULL; mtd->sync = NULL; mtd->block_isbad = doc_block_isbad; + mtd->ecclayout = &docg3_oobinfo; } /** -- cgit v1.2.3-70-g09d2 From 376fbf208700f2105d4047b198ca43e1d9db0b33 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:50 +0100 Subject: mtd: docg3: add registers for erasing and writing Add the required registers and commands to erase and write flash pages / blocks. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 75df3a1060f..e9967aba80a 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -130,6 +130,8 @@ #define DOC_SEQ_SET_PLANE1 0x0e #define DOC_SEQ_SET_PLANE2 0x10 #define DOC_SEQ_PAGE_SETUP 0x1d +#define DOC_SEQ_ERASE 0x27 +#define DOC_SEQ_PLANES_STATUS 0x31 /* * Flash commands @@ -144,7 +146,10 @@ #define DOC_CMD_PROG_BLOCK_ADDR 0x60 #define DOC_CMD_PROG_CYCLE1 0x80 #define DOC_CMD_PROG_CYCLE2 0x10 +#define DOC_CMD_PROG_CYCLE3 0x11 #define DOC_CMD_ERASECYCLE2 0xd0 +#define DOC_CMD_READ_STATUS 0x70 +#define DOC_CMD_PLANES_STATUS 0x71 #define DOC_CMD_RELIABLE_MODE 0x22 #define DOC_CMD_FAST_MODE 0xa2 @@ -186,7 +191,7 @@ */ #define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80 #define DOC_ECCCONF1_UNKOWN1 0x40 -#define DOC_ECCCONF1_UNKOWN2 0x20 +#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20 #define DOC_ECCCONF1_UNKOWN3 0x10 #define DOC_ECCCONF1_HAMMING_BITS_MASK 0x0f @@ -224,6 +229,13 @@ #define DOC_READADDR_ONE_BYTE 0x4000 #define DOC_READADDR_ADDR_MASK 0x1fff +/* + * Status of erase and write operation + */ +#define DOC_PLANES_STATUS_FAIL 0x01 +#define DOC_PLANES_STATUS_PLANE0_KO 0x02 +#define DOC_PLANES_STATUS_PLANE1_KO 0x04 + /** * struct docg3 - DiskOnChip driver private data * @dev: the device currently under control -- cgit v1.2.3-70-g09d2 From 316e627edcd8186f199120931f5381615a0d9aa4 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:51 +0100 Subject: mtd: docg3: add OOB buffer to device structure Add OOB buffer area to store the OOB data until the actual page is written, so that it can be completed by hardware ECC generator. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index e9967aba80a..397e4616796 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -243,6 +243,11 @@ * @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3) * @if_cfg: if true, reads are on 16bits, else reads are on 8bits * @bbt: bad block table cache + * @oob_write_ofs: offset of the MTD where this OOB should belong (ie. in next + * page_write) + * @oob_autoecc: if 1, use only bytes 0-7, 15, and fill the others with HW ECC + * if 0, use all the 16 bytes. + * @oob_write_buf: prepared OOB for next page_write * @debugfs_root: debugfs root node */ struct docg3 { @@ -252,6 +257,9 @@ struct docg3 { unsigned int if_cfg:1; int max_block; u8 *bbt; + loff_t oob_write_ofs; + int oob_autoecc; + u8 oob_write_buf[DOC_LAYOUT_OOB_SIZE]; struct dentry *debugfs_root; }; -- cgit v1.2.3-70-g09d2 From fb50b58e488d5fb3e3600592646909ddad7bd1e5 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:52 +0100 Subject: mtd: docg3: add write functions Add write capability to the docg3 driver. The writes are possible on a single page (512 bytes + 16 bytes), even if that page is split on 2 physical pages on 2 blocks (each on one plane). Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 553 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 541 insertions(+), 12 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 27c4feae132..fd4d47bdeec 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -253,6 +253,40 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, } } +/** + * doc_write_data_area - Write data into data area + * @docg3: the device + * @buf: the buffer to get input bytes from + * @len: the length to write + * + * Writes bytes into flash data. Handles the single byte / even bytes writes. + */ +static void doc_write_data_area(struct docg3 *docg3, const void *buf, int len) +{ + int i, cdr, len4; + u16 *src16; + u8 *src8; + + doc_dbg("doc_write_data_area(buf=%p, len=%d)\n", buf, len); + cdr = len & 0x3; + len4 = len - cdr; + + doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); + src16 = (u16 *)buf; + for (i = 0; i < len4; i += 2) { + doc_writew(docg3, *src16, DOC_IOSPACE_DATA); + src16++; + } + + src8 = (u8 *)src16; + for (i = 0; i < cdr; i++) { + doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, + DOC_READADDRESS); + doc_writeb(docg3, *src8, DOC_IOSPACE_DATA); + src8++; + } +} + /** * doc_set_data_mode - Sets the flash to reliable data mode * @docg3: the device @@ -342,6 +376,37 @@ static int doc_set_extra_page_mode(struct docg3 *docg3) return 0; } +/** + * doc_setup_addr_sector - Setup blocks/page/ofs address for one plane + * @docg3: the device + * @sector: the sector + */ +static void doc_setup_addr_sector(struct docg3 *docg3, int sector) +{ + doc_delay(docg3, 1); + doc_flash_address(docg3, sector & 0xff); + doc_flash_address(docg3, (sector >> 8) & 0xff); + doc_flash_address(docg3, (sector >> 16) & 0xff); + doc_delay(docg3, 1); +} + +/** + * doc_setup_writeaddr_sector - Setup blocks/page/ofs address for one plane + * @docg3: the device + * @sector: the sector + * @ofs: the offset in the page, between 0 and (512 + 16 + 512) + */ +static void doc_setup_writeaddr_sector(struct docg3 *docg3, int sector, int ofs) +{ + ofs = ofs >> 2; + doc_delay(docg3, 1); + doc_flash_address(docg3, ofs & 0xff); + doc_flash_address(docg3, sector & 0xff); + doc_flash_address(docg3, (sector >> 8) & 0xff); + doc_flash_address(docg3, (sector >> 16) & 0xff); + doc_delay(docg3, 1); +} + /** * doc_seek - Set both flash planes to the specified block, page for reading * @docg3: the device @@ -378,27 +443,73 @@ static int doc_read_seek(struct docg3 *docg3, int block0, int block1, int page, if (ret) goto out; - sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); doc_flash_sequence(docg3, DOC_SEQ_READ); + sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); - doc_delay(docg3, 1); - doc_flash_address(docg3, sector & 0xff); - doc_flash_address(docg3, (sector >> 8) & 0xff); - doc_flash_address(docg3, (sector >> 16) & 0xff); - doc_delay(docg3, 1); + doc_setup_addr_sector(docg3, sector); sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); + doc_setup_addr_sector(docg3, sector); doc_delay(docg3, 1); - doc_flash_address(docg3, sector & 0xff); - doc_flash_address(docg3, (sector >> 8) & 0xff); - doc_flash_address(docg3, (sector >> 16) & 0xff); + +out: + return ret; +} + +/** + * doc_write_seek - Set both flash planes to the specified block, page for writing + * @docg3: the device + * @block0: the first plane block index + * @block1: the second plane block index + * @page: the page index within the block + * @ofs: offset in page to write + * + * Programs the flash even and odd planes to the specific block and page. + * Alternatively, programs the flash to the wear area of the specified page. + */ +static int doc_write_seek(struct docg3 *docg3, int block0, int block1, int page, + int ofs) +{ + int ret = 0, sector; + + doc_dbg("doc_write_seek(blocks=(%d,%d), page=%d, ofs=%d)\n", + block0, block1, page, ofs); + + doc_set_reliable_mode(docg3); + + if (ofs < 2 * DOC_LAYOUT_PAGE_SIZE) { + doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); + doc_flash_command(docg3, DOC_CMD_READ_PLANE1); + doc_delay(docg3, 2); + } else { + doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); + doc_flash_command(docg3, DOC_CMD_READ_PLANE2); + doc_delay(docg3, 2); + } + + doc_flash_sequence(docg3, DOC_SEQ_PAGE_SETUP); + doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); + + sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); + doc_setup_writeaddr_sector(docg3, sector, ofs); + + doc_flash_command(docg3, DOC_CMD_PROG_CYCLE3); doc_delay(docg3, 2); + ret = doc_wait_ready(docg3); + if (ret) + goto out; + + doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); + sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); + doc_setup_writeaddr_sector(docg3, sector, ofs); + doc_delay(docg3, 1); out: return ret; } + /** * doc_read_page_ecc_init - Initialize hardware ECC engine * @docg3: the device @@ -420,6 +531,58 @@ static int doc_read_page_ecc_init(struct docg3 *docg3, int len) return doc_wait_ready(docg3); } +/** + * doc_write_page_ecc_init - Initialize hardware BCH ECC engine + * @docg3: the device + * @len: the number of bytes covered by the ECC (BCH covered) + * + * The function does initialize the hardware ECC engine to compute the Hamming + * ECC (on 1 byte) and the BCH Syndroms (on 7 bytes). + * + * Return 0 if succeeded, -EIO on error + */ +static int doc_write_page_ecc_init(struct docg3 *docg3, int len) +{ + doc_writew(docg3, !DOC_ECCCONF0_READ_MODE + | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE + | (len & DOC_ECCCONF0_DATA_BYTES_MASK), + DOC_ECCCONF0); + doc_delay(docg3, 4); + doc_register_readb(docg3, DOC_FLASHCONTROL); + return doc_wait_ready(docg3); +} + +/** + * doc_ecc_disable - Disable Hamming and BCH ECC hardware calculator + * @docg3: the device + * + * Disables the hardware ECC generator and checker, for unchecked reads (as when + * reading OOB only or write status byte). + */ +static void doc_ecc_disable(struct docg3 *docg3) +{ + doc_writew(docg3, DOC_ECCCONF0_READ_MODE, DOC_ECCCONF0); + doc_delay(docg3, 4); +} + +/** + * doc_hamming_ecc_init - Initialize hardware Hamming ECC engine + * @docg3: the device + * @nb_bytes: the number of bytes covered by the ECC (Hamming covered) + * + * This function programs the ECC hardware to compute the hamming code on the + * last provided N bytes to the hardware generator. + */ +static void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes) +{ + u8 ecc_conf1; + + ecc_conf1 = doc_register_readb(docg3, DOC_ECCCONF1); + ecc_conf1 &= ~DOC_ECCCONF1_HAMMING_BITS_MASK; + ecc_conf1 |= (nb_bytes & DOC_ECCCONF1_HAMMING_BITS_MASK); + doc_writeb(docg3, ecc_conf1, DOC_ECCCONF1); +} + /** * doc_read_page_prepare - Prepares reading data from a flash page * @docg3: the device @@ -505,12 +668,26 @@ static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, return len; } +/** + * doc_write_page_putbytes - Writes bytes into a prepared page + * @docg3: the device + * @len: the number of bytes to be written + * @buf: the buffer of input bytes + * + */ +static void doc_write_page_putbytes(struct docg3 *docg3, int len, + const u_char *buf) +{ + doc_write_data_area(docg3, buf, len); + doc_delay(docg3, 2); +} + /** * doc_get_hw_bch_syndroms - Get hardware calculated BCH syndroms * @docg3: the device * @syns: the array of 7 integers where the syndroms will be stored */ -static void doc_get_hw_bch_syndroms(struct docg3 *docg3, int *syns) +static void doc_get_hw_bch_syndroms(struct docg3 *docg3, u8 *syns) { int i; @@ -518,6 +695,16 @@ static void doc_get_hw_bch_syndroms(struct docg3 *docg3, int *syns) syns[i] = doc_register_readb(docg3, DOC_BCH_SYNDROM(i)); } +/** + * doc_page_finish - Ends reading/writing of a flash page + * @docg3: the device + */ +static void doc_page_finish(struct docg3 *docg3) +{ + doc_writeb(docg3, 0, DOC_DATAEND); + doc_delay(docg3, 2); +} + /** * doc_read_page_finish - Ends reading of a flash page * @docg3: the device @@ -528,8 +715,7 @@ static void doc_get_hw_bch_syndroms(struct docg3 *docg3, int *syns) */ static void doc_read_page_finish(struct docg3 *docg3) { - doc_writeb(docg3, 0, DOC_DATAEND); - doc_delay(docg3, 2); + doc_page_finish(docg3); doc_set_device_id(docg3, 0); } @@ -791,6 +977,348 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from) return max(plane1_erase_count, plane2_erase_count); } +/** + * doc_get_op_status - get erase/write operation status + * @docg3: the device + * + * Queries the status from the chip, and returns it + * + * Returns the status (bits DOC_PLANES_STATUS_*) + */ +static int doc_get_op_status(struct docg3 *docg3) +{ + u8 status; + + doc_flash_sequence(docg3, DOC_SEQ_PLANES_STATUS); + doc_flash_command(docg3, DOC_CMD_PLANES_STATUS); + doc_delay(docg3, 5); + + doc_ecc_disable(docg3); + doc_read_data_area(docg3, &status, 1, 1); + return status; +} + +/** + * doc_write_erase_wait_status - wait for write or erase completion + * @docg3: the device + * + * Wait for the chip to be ready again after erase or write operation, and check + * erase/write status. + * + * Returns 0 if erase successfull, -EIO if erase/write issue, -ETIMEOUT if + * timeout + */ +static int doc_write_erase_wait_status(struct docg3 *docg3) +{ + int status, ret = 0; + + if (!doc_is_ready(docg3)) + usleep_range(3000, 3000); + if (!doc_is_ready(docg3)) { + doc_dbg("Timeout reached and the chip is still not ready\n"); + ret = -EAGAIN; + goto out; + } + + status = doc_get_op_status(docg3); + if (status & DOC_PLANES_STATUS_FAIL) { + doc_dbg("Erase/Write failed on (a) plane(s), status = %x\n", + status); + ret = -EIO; + } + +out: + doc_page_finish(docg3); + return ret; +} + +/** + * doc_write_page - Write a single page to the chip + * @docg3: the device + * @to: the offset from first block and first page, in bytes, aligned on page + * size + * @buf: buffer to get bytes from + * @oob: buffer to get out of band bytes from (can be NULL if no OOB should be + * written) + * @autoecc: if 0, all 16 bytes from OOB are taken, regardless of HW Hamming or + * BCH computations. If 1, only bytes 0-7 and byte 15 are taken, + * remaining ones are filled with hardware Hamming and BCH + * computations. Its value is not meaningfull is oob == NULL. + * + * Write one full page (ie. 1 page split on two planes), of 512 bytes, with the + * OOB data. The OOB ECC is automatically computed by the hardware Hamming and + * BCH generator if autoecc is not null. + * + * Returns 0 if write successful, -EIO if write error, -EAGAIN if timeout + */ +static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, + const u_char *oob, int autoecc) +{ + int block0, block1, page, ret, ofs = 0; + u8 syn[DOC_ECC_BCH_SIZE], hamming; + + doc_dbg("doc_write_page(to=%lld)\n", to); + calc_block_sector(to, &block0, &block1, &page, &ofs); + + doc_set_device_id(docg3, docg3->device_id); + ret = doc_reset_seq(docg3); + if (ret) + goto err; + + /* Program the flash address block and page */ + ret = doc_write_seek(docg3, block0, block1, page, ofs); + if (ret) + goto err; + + doc_write_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES); + doc_delay(docg3, 2); + doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf); + + if (oob && autoecc) { + doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ, oob); + doc_delay(docg3, 2); + oob += DOC_LAYOUT_OOB_UNUSED_OFS; + + hamming = doc_register_readb(docg3, DOC_HAMMINGPARITY); + doc_delay(docg3, 2); + doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_HAMMING_SZ, + &hamming); + doc_delay(docg3, 2); + + doc_get_hw_bch_syndroms(docg3, syn); + doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, syn); + doc_delay(docg3, 2); + + doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_UNUSED_SZ, oob); + } + if (oob && !autoecc) + doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_SIZE, oob); + + doc_delay(docg3, 2); + doc_page_finish(docg3); + doc_delay(docg3, 2); + doc_flash_command(docg3, DOC_CMD_PROG_CYCLE2); + doc_delay(docg3, 2); + + /* + * The wait status will perform another doc_page_finish() call, but that + * seems to please the docg3, so leave it. + */ + ret = doc_write_erase_wait_status(docg3); + return ret; +err: + doc_read_page_finish(docg3); + return ret; +} + +/** + * doc_guess_autoecc - Guess autoecc mode from mbd_oob_ops + * @ops: the oob operations + * + * Returns 0 or 1 if success, -EINVAL if invalid oob mode + */ +static int doc_guess_autoecc(struct mtd_oob_ops *ops) +{ + int autoecc; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + autoecc = 1; + break; + case MTD_OPS_RAW: + autoecc = 0; + break; + default: + autoecc = -EINVAL; + } + return autoecc; +} + +/** + * doc_fill_autooob - Fill a 16 bytes OOB from 8 non-ECC bytes + * @dst: the target 16 bytes OOB buffer + * @oobsrc: the source 8 bytes non-ECC OOB buffer + * + */ +static void doc_fill_autooob(u8 *dst, u8 *oobsrc) +{ + memcpy(dst, oobsrc, DOC_LAYOUT_OOB_PAGEINFO_SZ); + dst[DOC_LAYOUT_OOB_UNUSED_OFS] = oobsrc[DOC_LAYOUT_OOB_PAGEINFO_SZ]; +} + +/** + * doc_backup_oob - Backup OOB into docg3 structure + * @docg3: the device + * @to: the page offset in the chip + * @ops: the OOB size and buffer + * + * As the docg3 should write a page with its OOB in one pass, and some userland + * applications do write_oob() to setup the OOB and then write(), store the OOB + * into a temporary storage. This is very dangerous, as 2 concurrent + * applications could store an OOB, and then write their pages (which will + * result into one having its OOB corrupted). + * + * The only reliable way would be for userland to call doc_write_oob() with both + * the page data _and_ the OOB area. + * + * Returns 0 if success, -EINVAL if ops content invalid + */ +static int doc_backup_oob(struct docg3 *docg3, loff_t to, + struct mtd_oob_ops *ops) +{ + int ooblen = ops->ooblen, autoecc; + + if (ooblen != DOC_LAYOUT_OOB_SIZE) + return -EINVAL; + autoecc = doc_guess_autoecc(ops); + if (autoecc < 0) + return autoecc; + + docg3->oob_write_ofs = to; + docg3->oob_autoecc = autoecc; + if (ops->mode == MTD_OPS_AUTO_OOB) { + doc_fill_autooob(docg3->oob_write_buf, ops->oobbuf); + ops->oobretlen = 8; + } else { + memcpy(docg3->oob_write_buf, ops->oobbuf, DOC_LAYOUT_OOB_SIZE); + ops->oobretlen = DOC_LAYOUT_OOB_SIZE; + } + return 0; +} + +/** + * doc_write_oob - Write out of band bytes to flash + * @mtd: the device + * @ofs: the offset from first block and first page, in bytes, aligned on page + * size + * @ops: the mtd oob structure + * + * Either write OOB data into a temporary buffer, for the subsequent write + * page. The provided OOB should be 16 bytes long. If a data buffer is provided + * as well, issue the page write. + * Or provide data without OOB, and then a all zeroed OOB will be used (ECC will + * still be filled in if asked for). + * + * Returns 0 is successfull, EINVAL if length is not 14 bytes + */ +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) +{ + struct docg3 *docg3 = mtd->priv; + int block0, block1, page, ret, pofs = 0, autoecc, oobdelta; + u8 *oobbuf = ops->oobbuf; + u8 *buf = ops->datbuf; + size_t len, ooblen; + u8 oob[DOC_LAYOUT_OOB_SIZE]; + + if (buf) + len = ops->len; + else + len = 0; + if (oobbuf) + ooblen = ops->ooblen; + else + ooblen = 0; + + if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) + oobbuf += ops->ooboffs; + + doc_dbg("doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", + ofs, ops->mode, buf, len, oobbuf, ooblen); + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + oobdelta = mtd->oobsize; + break; + case MTD_OPS_AUTO_OOB: + oobdelta = mtd->ecclayout->oobavail; + break; + default: + oobdelta = 0; + } + if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % oobdelta) || + (ofs % DOC_LAYOUT_PAGE_SIZE)) + return -EINVAL; + if (len && ooblen && + (len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta)) + return -EINVAL; + + ret = -EINVAL; + calc_block_sector(ofs + len, &block0, &block1, &page, &pofs); + if (block1 > docg3->max_block) + goto err; + + ops->oobretlen = 0; + ops->retlen = 0; + ret = 0; + if (len == 0 && ooblen == 0) + return -EINVAL; + if (len == 0 && ooblen > 0) + return doc_backup_oob(docg3, ofs, ops); + + autoecc = doc_guess_autoecc(ops); + if (autoecc < 0) + return autoecc; + + while (!ret && len > 0) { + memset(oob, 0, sizeof(oob)); + if (ofs == docg3->oob_write_ofs) + memcpy(oob, docg3->oob_write_buf, DOC_LAYOUT_OOB_SIZE); + else if (ooblen > 0 && ops->mode == MTD_OPS_AUTO_OOB) + doc_fill_autooob(oob, oobbuf); + else if (ooblen > 0) + memcpy(oob, oobbuf, DOC_LAYOUT_OOB_SIZE); + ret = doc_write_page(docg3, ofs, buf, oob, autoecc); + + ofs += DOC_LAYOUT_PAGE_SIZE; + len -= DOC_LAYOUT_PAGE_SIZE; + buf += DOC_LAYOUT_PAGE_SIZE; + if (ooblen) { + oobbuf += oobdelta; + ooblen -= oobdelta; + ops->oobretlen += oobdelta; + } + ops->retlen += DOC_LAYOUT_PAGE_SIZE; + } +err: + doc_set_device_id(docg3, 0); + return ret; +} + +/** + * doc_write - Write a buffer to the chip + * @mtd: the device + * @to: the offset from first block and first page, in bytes, aligned on page + * size + * @len: the number of bytes to write (must be a full page size, ie. 512) + * @retlen: the number of bytes actually written (0 or 512) + * @buf: the buffer to get bytes from + * + * Writes data to the chip. + * + * Returns 0 if write successful, -EIO if write error + */ +static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct docg3 *docg3 = mtd->priv; + int ret; + struct mtd_oob_ops ops; + + doc_dbg("doc_write(to=%lld, len=%zu)\n", to, len); + ops.datbuf = (char *)buf; + ops.len = len; + ops.mode = MTD_OPS_PLACE_OOB; + ops.oobbuf = NULL; + ops.ooblen = 0; + ops.ooboffs = 0; + + ret = doc_write_oob(mtd, to, &ops); + *retlen = ops.retlen; + return ret; +} + /* * Debug sysfs entries */ @@ -1052,6 +1580,7 @@ static struct mtd_info *doc_probe_device(void __iomem *base, int floor, doc_set_driver_info(chip_id, mtd); + doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ); doc_reload_bbt(docg3); return mtd; -- cgit v1.2.3-70-g09d2 From de03cd716ba7b8103ef9bef425be1ac72b81aeca Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:53 +0100 Subject: mtd: docg3: add erase functions Add erase capability to the docg3 driver. The erase block is made of 2 physical blocks, as both share all 64 pages. That makes an erase block of at least 64 kBytes. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index fd4d47bdeec..3087b1564a4 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1032,6 +1032,96 @@ out: return ret; } +/** + * doc_erase_block - Erase a couple of blocks + * @docg3: the device + * @block0: the first block to erase (leftmost plane) + * @block1: the second block to erase (rightmost plane) + * + * Erase both blocks, and return operation status + * + * Returns 0 if erase successful, -EIO if erase issue, -ETIMEOUT if chip not + * ready for too long + */ +static int doc_erase_block(struct docg3 *docg3, int block0, int block1) +{ + int ret, sector; + + doc_dbg("doc_erase_block(blocks=(%d,%d))\n", block0, block1); + ret = doc_reset_seq(docg3); + if (ret) + return -EIO; + + doc_set_reliable_mode(docg3); + doc_flash_sequence(docg3, DOC_SEQ_ERASE); + + sector = block0 << DOC_ADDR_BLOCK_SHIFT; + doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); + doc_setup_addr_sector(docg3, sector); + sector = block1 << DOC_ADDR_BLOCK_SHIFT; + doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); + doc_setup_addr_sector(docg3, sector); + doc_delay(docg3, 1); + + doc_flash_command(docg3, DOC_CMD_ERASECYCLE2); + doc_delay(docg3, 2); + + if (is_prot_seq_error(docg3)) { + doc_err("Erase blocks %d,%d error\n", block0, block1); + return -EIO; + } + + return doc_write_erase_wait_status(docg3); +} + +/** + * doc_erase - Erase a portion of the chip + * @mtd: the device + * @info: the erase info + * + * Erase a bunch of contiguous blocks, by pairs, as a "mtd" page of 1024 is + * split into 2 pages of 512 bytes on 2 contiguous blocks. + * + * Returns 0 if erase successful, -EINVAL if adressing error, -EIO if erase + * issue + */ +static int doc_erase(struct mtd_info *mtd, struct erase_info *info) +{ + struct docg3 *docg3 = mtd->priv; + uint64_t len; + int block0, block1, page, ret, ofs = 0; + + doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len); + doc_set_device_id(docg3, docg3->device_id); + + info->state = MTD_ERASE_PENDING; + calc_block_sector(info->addr + info->len, + &block0, &block1, &page, &ofs); + ret = -EINVAL; + if (block1 > docg3->max_block || page || ofs) + goto reset_err; + + ret = 0; + calc_block_sector(info->addr, &block0, &block1, &page, &ofs); + doc_set_reliable_mode(docg3); + for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { + info->state = MTD_ERASING; + ret = doc_erase_block(docg3, block0, block1); + block0 += 2; + block1 += 2; + } + + if (ret) + goto reset_err; + + info->state = MTD_ERASE_DONE; + return 0; + +reset_err: + info->state = MTD_ERASE_FAILED; + return ret; +} + /** * doc_write_page - Write a single page to the chip * @docg3: the device -- cgit v1.2.3-70-g09d2 From 7a7fcf14021bc0f0728dd0aaa2635475de0db54c Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:54 +0100 Subject: mtd: docg3: map erase and write functions Map the developped write and erase functions into the mtd structure. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 3087b1564a4..1f949eac635 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -41,8 +41,6 @@ * * As no specification is available from M-Systems/Sandisk, this drivers lacks * several functions available on the chip, as : - * - block erase - * - page write * - IPL write * - ECC fixing (lack of BCH algorith understanding) * - powerdown / powerup @@ -1586,23 +1584,19 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) break; } mtd->type = MTD_NANDFLASH; - /* - * Once write methods are added, the correct flags will be set. - * mtd->flags = MTD_CAP_NANDFLASH; - */ - mtd->flags = MTD_CAP_ROM; + mtd->flags = MTD_CAP_NANDFLASH; mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE; mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES; mtd->writesize = DOC_LAYOUT_PAGE_SIZE; mtd->oobsize = DOC_LAYOUT_OOB_SIZE; mtd->owner = THIS_MODULE; - mtd->erase = NULL; + mtd->erase = doc_erase; mtd->point = NULL; mtd->unpoint = NULL; mtd->read = doc_read; - mtd->write = NULL; + mtd->write = doc_write; mtd->read_oob = doc_read_oob; - mtd->write_oob = NULL; + mtd->write_oob = doc_write_oob; mtd->sync = NULL; mtd->block_isbad = doc_block_isbad; mtd->ecclayout = &docg3_oobinfo; -- cgit v1.2.3-70-g09d2 From d13d19ece39f20bf097782e1812a9c31a5a4fcf1 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:55 +0100 Subject: mtd: docg3: add ECC correction code Credit for discovering the BCH algorith parameters, and bit reversing algorithm is to be give to Mike Dunn and Ivan Djelic. The BCH correction code relied upon the BCH library, where all data and ECC is bit-reversed. The BCH library works correctly when each input byte is bit-reversed, and accordingly ECC output is also bit-reversed. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/Kconfig | 9 ++++ drivers/mtd/devices/docg3.c | 116 +++++++++++++++++++++++++++++++++++--------- drivers/mtd/devices/docg3.h | 11 ++++- 3 files changed, 113 insertions(+), 23 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 283d887f782..952e956ef01 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -251,6 +251,8 @@ config MTD_DOC2001PLUS config MTD_DOCG3 tristate "M-Systems Disk-On-Chip G3" + select BCH + select BCH_CONST_PARAMS ---help--- This provides an MTD device driver for the M-Systems DiskOnChip G3 devices. @@ -259,6 +261,13 @@ config MTD_DOCG3 M-Systems and now Sandisk. The support is very experimental, and doesn't give access to any write operations. +if MTD_DOCG3 +config BCH_CONST_M + default 14 +config BCH_CONST_T + default 4 +endif + config MTD_DOCPROBE tristate select MTD_DOCECC diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 1f949eac635..26cc17909b1 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include #include @@ -42,7 +45,6 @@ * As no specification is available from M-Systems/Sandisk, this drivers lacks * several functions available on the chip, as : * - IPL write - * - ECC fixing (lack of BCH algorith understanding) * - powerdown / powerup * * The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and @@ -51,8 +53,7 @@ * DocG3 relies on 2 ECC algorithms, which are handled in hardware : * - a 1 byte Hamming code stored in the OOB for each page * - a 7 bytes BCH code stored in the OOB for each page - * The BCH part is only used for check purpose, no correction is available as - * some information is missing. What is known is that : + * The BCH ECC is : * - BCH is in GF(2^14) * - BCH is over data of 520 bytes (512 page + 7 page_info bytes * + 1 hamming byte) @@ -75,6 +76,11 @@ static struct nand_ecclayout docg3_oobinfo = { .oobavail = 8, }; +/** + * struct docg3_bch - BCH engine + */ +static struct bch_control *docg3_bch; + static inline u8 doc_readb(struct docg3 *docg3, u16 reg) { u8 val = readb(docg3->base + reg); @@ -581,6 +587,54 @@ static void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes) doc_writeb(docg3, ecc_conf1, DOC_ECCCONF1); } +/** + * doc_correct_data - Fix if need be read data from flash + * @docg3: the device + * @buf: the buffer of read data (512 + 7 + 1 bytes) + * @hwecc: the hardware calculated ECC. + * It's in fact recv_ecc ^ calc_ecc, where recv_ecc was read from OOB + * area data, and calc_ecc the ECC calculated by the hardware generator. + * + * Checks if the received data matches the ECC, and if an error is detected, + * tries to fix the bit flips (at most 4) in the buffer buf. As the docg3 + * understands the (data, ecc, syndroms) in an inverted order in comparison to + * the BCH library, the function reverses the order of bits (ie. bit7 and bit0, + * bit6 and bit 1, ...) for all ECC data. + * + * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch + * algorithm is used to decode this. However the hw operates on page + * data in a bit order that is the reverse of that of the bch alg, + * requiring that the bits be reversed on the result. Thanks to Ivan + * Djelic for his analysis. + * + * Returns number of fixed bits (0, 1, 2, 3, 4) or -EBADMSG if too many bit + * errors were detected and cannot be fixed. + */ +static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc) +{ + u8 ecc[DOC_ECC_BCH_SIZE]; + int errorpos[DOC_ECC_BCH_T], i, numerrs; + + for (i = 0; i < DOC_ECC_BCH_SIZE; i++) + ecc[i] = bitrev8(hwecc[i]); + numerrs = decode_bch(docg3_bch, NULL, DOC_ECC_BCH_COVERED_BYTES, + NULL, ecc, NULL, errorpos); + BUG_ON(numerrs == -EINVAL); + if (numerrs < 0) + goto out; + + for (i = 0; i < numerrs; i++) + errorpos[i] = (errorpos[i] & ~7) | (7 - (errorpos[i] & 7)); + for (i = 0; i < numerrs; i++) + if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8) + /* error is located in data, correct it */ + change_bit(errorpos[i], buf); +out: + doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs); + return numerrs; +} + + /** * doc_read_page_prepare - Prepares reading data from a flash page * @docg3: the device @@ -762,7 +816,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, u8 *oobbuf = ops->oobbuf; u8 *buf = ops->datbuf; size_t len, ooblen, nbdata, nboob; - u8 calc_ecc[DOC_ECC_BCH_SIZE], eccconf1; + u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; if (buf) len = ops->len; @@ -797,7 +851,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); if (ret < 0) goto err; - ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES); + ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); if (ret < 0) goto err_in_read; ret = doc_read_page_getbytes(docg3, nbdata, buf, 1); @@ -811,7 +865,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, NULL, 0); - doc_get_hw_bch_syndroms(docg3, calc_ecc); + doc_get_hw_bch_syndroms(docg3, hwecc); eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); if (nboob >= DOC_LAYOUT_OOB_SIZE) { @@ -825,18 +879,28 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]); } doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); - doc_dbg("ECC CALC_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - calc_ecc[0], calc_ecc[1], calc_ecc[2], - calc_ecc[3], calc_ecc[4], calc_ecc[5], - calc_ecc[6]); - - ret = -EBADMSG; - if (block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) { - if ((eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) && - (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN)) - goto err_in_read; - if (is_prot_seq_error(docg3)) - goto err_in_read; + doc_dbg("ECC HW_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + hwecc[0], hwecc[1], hwecc[2], hwecc[3], hwecc[4], + hwecc[5], hwecc[6]); + + ret = -EIO; + if (is_prot_seq_error(docg3)) + goto err_in_read; + ret = 0; + if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) && + (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) && + (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) && + (ops->mode != MTD_OPS_RAW) && + (nbdata == DOC_LAYOUT_PAGE_SIZE)) { + ret = doc_ecc_bch_fix_data(docg3, buf, hwecc); + if (ret < 0) { + mtd->ecc_stats.failed++; + ret = -EBADMSG; + } + if (ret > 0) { + mtd->ecc_stats.corrected += ret; + ret = -EUCLEAN; + } } doc_read_page_finish(docg3); @@ -849,7 +913,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, from += DOC_LAYOUT_PAGE_SIZE; } - return 0; + return ret; err_in_read: doc_read_page_finish(docg3); err: @@ -1158,7 +1222,7 @@ static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, if (ret) goto err; - doc_write_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES); + doc_write_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); doc_delay(docg3, 2); doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf); @@ -1721,7 +1785,11 @@ static int __init docg3_probe(struct platform_device *pdev) docg3_floors = kzalloc(sizeof(*docg3_floors) * DOC_MAX_NBFLOORS, GFP_KERNEL); if (!docg3_floors) - goto nomem; + goto nomem1; + docg3_bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T, + DOC_ECC_BCH_PRIMPOLY); + if (!docg3_bch) + goto nomem2; ret = 0; for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { @@ -1751,10 +1819,13 @@ notfound: ret = -ENODEV; dev_info(dev, "No supported DiskOnChip found\n"); err_probe: + free_bch(docg3_bch); for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) if (docg3_floors[floor]) doc_release_device(docg3_floors[floor]); -nomem: +nomem2: + kfree(docg3_floors); +nomem1: iounmap(base); noress: return ret; @@ -1779,6 +1850,7 @@ static int __exit docg3_release(struct platform_device *pdev) doc_release_device(docg3_floors[floor]); kfree(docg3_floors); + free_bch(docg3_bch); iounmap(base); return 0; } diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 397e4616796..33db7272c46 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -51,10 +51,19 @@ #define DOC_LAYOUT_WEAR_OFFSET (DOC_LAYOUT_PAGE_OOB_SIZE * 2) #define DOC_LAYOUT_BLOCK_SIZE \ (DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_PAGE_SIZE) + +/* + * ECC related constants + */ +#define DOC_ECC_BCH_M 14 +#define DOC_ECC_BCH_T 4 +#define DOC_ECC_BCH_PRIMPOLY 0x4443 #define DOC_ECC_BCH_SIZE 7 #define DOC_ECC_BCH_COVERED_BYTES \ (DOC_LAYOUT_PAGE_SIZE + DOC_LAYOUT_OOB_PAGEINFO_SZ + \ - DOC_LAYOUT_OOB_HAMMING_SZ + DOC_LAYOUT_OOB_BCH_SZ) + DOC_LAYOUT_OOB_HAMMING_SZ) +#define DOC_ECC_BCH_TOTAL_BYTES \ + (DOC_ECC_BCH_COVERED_BYTES + DOC_LAYOUT_OOB_BCH_SZ) /* * Blocks distribution -- cgit v1.2.3-70-g09d2 From e4b2a96aeb2b3dfee8d19d0335c6151d4cca4631 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:56 +0100 Subject: mtd: docg3: add suspend and resume Add functions to powerdown and powerup from suspend, in order to save power. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 75 ++++++++++++++++++++++++++++++++++++++++++++- drivers/mtd/devices/docg3.h | 6 ++++ 2 files changed, 80 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 26cc17909b1..d94c759d3ea 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -45,7 +45,6 @@ * As no specification is available from M-Systems/Sandisk, this drivers lacks * several functions available on the chip, as : * - IPL write - * - powerdown / powerup * * The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and * the driver assumes a 16bits data bus. @@ -1755,6 +1754,78 @@ static void doc_release_device(struct mtd_info *mtd) kfree(mtd); } +/** + * docg3_resume - Awakens docg3 floor + * @pdev: platfrom device + * + * Returns 0 (always successfull) + */ +static int docg3_resume(struct platform_device *pdev) +{ + int i; + struct mtd_info **docg3_floors, *mtd; + struct docg3 *docg3; + + docg3_floors = platform_get_drvdata(pdev); + mtd = docg3_floors[0]; + docg3 = mtd->priv; + + doc_dbg("docg3_resume()\n"); + for (i = 0; i < 12; i++) + doc_readb(docg3, DOC_IOSPACE_IPL); + return 0; +} + +/** + * docg3_suspend - Put in low power mode the docg3 floor + * @pdev: platform device + * @state: power state + * + * Shuts off most of docg3 circuitery to lower power consumption. + * + * Returns 0 if suspend succeeded, -EIO if chip refused suspend + */ +static int docg3_suspend(struct platform_device *pdev, pm_message_t state) +{ + int floor, i; + struct mtd_info **docg3_floors, *mtd; + struct docg3 *docg3; + u8 ctrl, pwr_down; + + docg3_floors = platform_get_drvdata(pdev); + for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { + mtd = docg3_floors[floor]; + if (!mtd) + continue; + docg3 = mtd->priv; + + doc_writeb(docg3, floor, DOC_DEVICESELECT); + ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); + ctrl &= ~DOC_CTRL_VIOLATION & ~DOC_CTRL_CE; + doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); + + for (i = 0; i < 10; i++) { + usleep_range(3000, 4000); + pwr_down = doc_register_readb(docg3, DOC_POWERMODE); + if (pwr_down & DOC_POWERDOWN_READY) + break; + } + if (pwr_down & DOC_POWERDOWN_READY) { + doc_dbg("docg3_suspend(): floor %d powerdown ok\n", + floor); + } else { + doc_err("docg3_suspend(): floor %d powerdown failed\n", + floor); + return -EIO; + } + } + + mtd = docg3_floors[0]; + docg3 = mtd->priv; + doc_set_asic_mode(docg3, DOC_ASICMODE_POWERDOWN); + return 0; +} + /** * doc_probe - Probe the IO space for a DiskOnChip G3 chip * @pdev: platform device @@ -1860,6 +1931,8 @@ static struct platform_driver g3_driver = { .name = "docg3", .owner = THIS_MODULE, }, + .suspend = docg3_suspend, + .resume = docg3_resume, .remove = __exit_p(docg3_release), }; diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 33db7272c46..cd70b181f79 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -127,6 +127,7 @@ #define DOC_ASICMODECONFIRM 0x1072 #define DOC_CHIPID_INV 0x1074 +#define DOC_POWERMODE 0x107c /* * Flash sequences @@ -238,6 +239,11 @@ #define DOC_READADDR_ONE_BYTE 0x4000 #define DOC_READADDR_ADDR_MASK 0x1fff +/* + * Flash register : DOC_POWERMODE + */ +#define DOC_POWERDOWN_READY 0x80 + /* * Status of erase and write operation */ -- cgit v1.2.3-70-g09d2 From c3de8a8a5a28603f8d318245992dbcda2e88a007 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:57 +0100 Subject: mtd: docg3: add fast mode Docg3 chips can work in 3 modes : normal MLC mode, fast mode and reliable mode. Normally, as docg3 is a MLC chip, it should be configured to work in normal mode. In both normal mode, each page is distinct. This means that writing to page 12 of blocks 14,15 writes only to that page, and reading from page 12 of blocks 14,15 reads only from that page. In reliable and fast modes, pages are coupled by pairs, and are clones one of each other. This means that the available capacity of the chip is halved. Pages are coupled in each block, and page of index 2*n contains the same data as page 2*n+1 of the same block. In fast mode, the reads occur a bit faster, but are a bit less reliable that in normal mode. When reading from page 2*n, the chip reads bytes from both page 2*n and page 2*n+1, makes a logical and for each byte, and returns the result. As programming a page means "clearing bits", even if a bit was not cleared on one page because the flash is worn out, the other page has the bit cleared, and the result of the "AND" gives a correct result. When writing to page 2*n, the chip writes data to both page 2*n and page 2*n+1. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 86 +++++++++++++++++++++++++++++++++++---------- drivers/mtd/devices/docg3.h | 9 ++++- 2 files changed, 75 insertions(+), 20 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index d94c759d3ea..35df3778f8f 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -61,6 +61,11 @@ * */ +static unsigned int reliable_mode = 0; +module_param(reliable_mode, uint, 0); +MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, " + "2=reliable) : MLC normal operations are in normal mode"); + /** * struct docg3_oobinfo - DiskOnChip G3 OOB layout * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC) @@ -291,19 +296,41 @@ static void doc_write_data_area(struct docg3 *docg3, const void *buf, int len) } /** - * doc_set_data_mode - Sets the flash to reliable data mode + * doc_set_data_mode - Sets the flash to normal or reliable data mode * @docg3: the device * * The reliable data mode is a bit slower than the fast mode, but less errors * occur. Entering the reliable mode cannot be done without entering the fast * mode first. + * + * In reliable mode, pages 2*n and 2*n+1 are clones. Writing to page 0 of blocks + * (4,5) make the hardware write also to page 1 of blocks blocks(4,5). Reading + * from page 0 of blocks (4,5) or from page 1 of blocks (4,5) gives the same + * result, which is a logical and between bytes from page 0 and page 1 (which is + * consistent with the fact that writing to a page is _clearing_ bits of that + * page). */ static void doc_set_reliable_mode(struct docg3 *docg3) { - doc_dbg("doc_set_reliable_mode()\n"); - doc_flash_sequence(docg3, DOC_SEQ_SET_MODE); - doc_flash_command(docg3, DOC_CMD_FAST_MODE); - doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE); + static char *strmode[] = { "normal", "fast", "reliable", "invalid" }; + + doc_dbg("doc_set_reliable_mode(%s)\n", strmode[docg3->reliable]); + switch (docg3->reliable) { + case 0: + break; + case 1: + doc_flash_sequence(docg3, DOC_SEQ_SET_FASTMODE); + doc_flash_command(docg3, DOC_CMD_FAST_MODE); + break; + case 2: + doc_flash_sequence(docg3, DOC_SEQ_SET_RELIABLEMODE); + doc_flash_command(docg3, DOC_CMD_FAST_MODE); + doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE); + break; + default: + doc_err("doc_set_reliable_mode(): invalid mode\n"); + break; + } doc_delay(docg3, 2); } @@ -778,18 +805,29 @@ static void doc_read_page_finish(struct docg3 *docg3) * @block1: second plane block index calculated * @page: page calculated * @ofs: offset in page + * @reliable: 0 if docg3 in normal mode, 1 if docg3 in fast mode, 2 if docg3 in + * reliable mode. + * + * The calculation is based on the reliable/normal mode. In normal mode, the 64 + * pages of a block are available. In reliable mode, as pages 2*n and 2*n+1 are + * clones, only 32 pages per block are available. */ static void calc_block_sector(loff_t from, int *block0, int *block1, int *page, - int *ofs) + int *ofs, int reliable) { - uint sector; + uint sector, pages_biblock; + + pages_biblock = DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES; + if (reliable == 1 || reliable == 2) + pages_biblock /= 2; sector = from / DOC_LAYOUT_PAGE_SIZE; - *block0 = sector / (DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES) - * DOC_LAYOUT_NBPLANES; + *block0 = sector / pages_biblock * DOC_LAYOUT_NBPLANES; *block1 = *block0 + 1; - *page = sector % (DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES); + *page = sector % pages_biblock; *page /= DOC_LAYOUT_NBPLANES; + if (reliable == 1 || reliable == 2) + *page *= 2; if (sector % 2) *ofs = DOC_LAYOUT_PAGE_OOB_SIZE; else @@ -836,7 +874,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; ret = -EINVAL; - calc_block_sector(from + len, &block0, &block1, &page, &ofs); + calc_block_sector(from + len, &block0, &block1, &page, &ofs, + docg3->reliable); if (block1 > docg3->max_block) goto err; @@ -844,7 +883,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ops->retlen = 0; ret = 0; while (!ret && (len > 0 || ooblen > 0)) { - calc_block_sector(from, &block0, &block1, &page, &ofs); + calc_block_sector(from, &block0, &block1, &page, &ofs, + docg3->reliable); nbdata = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE); nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); @@ -983,7 +1023,8 @@ static int doc_block_isbad(struct mtd_info *mtd, loff_t from) struct docg3 *docg3 = mtd->priv; int block0, block1, page, ofs, is_good; - calc_block_sector(from, &block0, &block1, &page, &ofs); + calc_block_sector(from, &block0, &block1, &page, &ofs, + docg3->reliable); doc_dbg("doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d\n", from, block0, block1, page, ofs); @@ -1015,7 +1056,7 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from) doc_dbg("doc_get_erase_count(from=%lld, buf=%p)\n", from, buf); if (from % DOC_LAYOUT_PAGE_SIZE) return -EINVAL; - calc_block_sector(from, &block0, &block1, &page, &ofs); + calc_block_sector(from, &block0, &block1, &page, &ofs, docg3->reliable); if (block1 > docg3->max_block) return -EINVAL; @@ -1156,14 +1197,15 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info) doc_set_device_id(docg3, docg3->device_id); info->state = MTD_ERASE_PENDING; - calc_block_sector(info->addr + info->len, - &block0, &block1, &page, &ofs); + calc_block_sector(info->addr + info->len, &block0, &block1, &page, + &ofs, docg3->reliable); ret = -EINVAL; if (block1 > docg3->max_block || page || ofs) goto reset_err; ret = 0; - calc_block_sector(info->addr, &block0, &block1, &page, &ofs); + calc_block_sector(info->addr, &block0, &block1, &page, &ofs, + docg3->reliable); doc_set_reliable_mode(docg3); for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { info->state = MTD_ERASING; @@ -1209,7 +1251,7 @@ static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, u8 syn[DOC_ECC_BCH_SIZE], hamming; doc_dbg("doc_write_page(to=%lld)\n", to); - calc_block_sector(to, &block0, &block1, &page, &ofs); + calc_block_sector(to, &block0, &block1, &page, &ofs, docg3->reliable); doc_set_device_id(docg3, docg3->device_id); ret = doc_reset_seq(docg3); @@ -1396,7 +1438,8 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, return -EINVAL; ret = -EINVAL; - calc_block_sector(ofs + len, &block0, &block1, &page, &pofs); + calc_block_sector(ofs + len, &block0, &block1, &page, &pofs, + docg3->reliable); if (block1 > docg3->max_block) goto err; @@ -1638,6 +1681,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) cfg = doc_register_readb(docg3, DOC_CONFIGURATION); docg3->if_cfg = (cfg & DOC_CONF_IF_CFG ? 1 : 0); + docg3->reliable = reliable_mode; switch (chip_id) { case DOC_CHIPID_G3: @@ -1649,7 +1693,11 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE; + if (docg3->reliable == 2) + mtd->size /= 2; mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES; + if (docg3->reliable == 2) + mtd->erasesize /= 2; mtd->writesize = DOC_LAYOUT_PAGE_SIZE; mtd->oobsize = DOC_LAYOUT_OOB_SIZE; mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index cd70b181f79..07182f9d484 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -135,7 +135,8 @@ */ #define DOC_SEQ_RESET 0x00 #define DOC_SEQ_PAGE_SIZE_532 0x03 -#define DOC_SEQ_SET_MODE 0x09 +#define DOC_SEQ_SET_FASTMODE 0x05 +#define DOC_SEQ_SET_RELIABLEMODE 0x09 #define DOC_SEQ_READ 0x12 #define DOC_SEQ_SET_PLANE1 0x0e #define DOC_SEQ_SET_PLANE2 0x10 @@ -257,6 +258,11 @@ * @base: mapped IO space * @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3) * @if_cfg: if true, reads are on 16bits, else reads are on 8bits + + * @reliable: if 0, docg3 in normal mode, if 1 docg3 in fast mode, if 2 in + * reliable mode + * Fast mode implies more errors than normal mode. + * Reliable mode implies that page 2*n and 2*n+1 are clones. * @bbt: bad block table cache * @oob_write_ofs: offset of the MTD where this OOB should belong (ie. in next * page_write) @@ -270,6 +276,7 @@ struct docg3 { void __iomem *base; unsigned int device_id:4; unsigned int if_cfg:1; + unsigned int reliable:2; int max_block; u8 *bbt; loff_t oob_write_ofs; -- cgit v1.2.3-70-g09d2 From 0f769d3f9e2ef3e88930ff190a20cfbfe6206d3a Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 19 Nov 2011 16:02:58 +0100 Subject: mtd: docg3: add protection areas sysfs access As each docg3 chip has 2 protection areas (DPS0 and DPS1), and because theses areas can prevent user access to the chip data, add for each floor the sysfs entries which insert the protection key into the right DPS. Signed-off-by: Robert Jarzmik Reviewed-by: Ivan Djelic Reviewed-by: Mike Dunn Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 121 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/devices/docg3.h | 13 +++++ 2 files changed, 134 insertions(+) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 35df3778f8f..d7df3114aa1 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1513,6 +1513,123 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } +static struct docg3 *sysfs_dev2docg3(struct device *dev, + struct device_attribute *attr) +{ + int floor; + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info **docg3_floors = platform_get_drvdata(pdev); + + floor = attr->attr.name[1] - '0'; + if (floor < 0 || floor >= DOC_MAX_NBFLOORS) + return NULL; + else + return docg3_floors[floor]->priv; +} + +static ssize_t dps0_is_key_locked(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); + int dps0; + + doc_set_device_id(docg3, docg3->device_id); + dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); + doc_set_device_id(docg3, 0); + + return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK)); +} + +static ssize_t dps1_is_key_locked(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); + int dps1; + + doc_set_device_id(docg3, docg3->device_id); + dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); + doc_set_device_id(docg3, 0); + + return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK)); +} + +static ssize_t dps0_insert_key(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); + int i; + + if (count != DOC_LAYOUT_DPS_KEY_LENGTH) + return -EINVAL; + + doc_set_device_id(docg3, docg3->device_id); + for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) + doc_writeb(docg3, buf[i], DOC_DPS0_KEY); + doc_set_device_id(docg3, 0); + return count; +} + +static ssize_t dps1_insert_key(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); + int i; + + if (count != DOC_LAYOUT_DPS_KEY_LENGTH) + return -EINVAL; + + doc_set_device_id(docg3, docg3->device_id); + for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) + doc_writeb(docg3, buf[i], DOC_DPS1_KEY); + doc_set_device_id(docg3, 0); + return count; +} + +#define FLOOR_SYSFS(id) { \ + __ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \ + __ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \ + __ATTR(f##id##_dps0_protection_key, S_IWUGO, NULL, dps0_insert_key), \ + __ATTR(f##id##_dps1_protection_key, S_IWUGO, NULL, dps1_insert_key), \ +} + +static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = { + FLOOR_SYSFS(0), FLOOR_SYSFS(1), FLOOR_SYSFS(2), FLOOR_SYSFS(3) +}; + +static int doc_register_sysfs(struct platform_device *pdev, + struct mtd_info **floors) +{ + int ret = 0, floor, i = 0; + struct device *dev = &pdev->dev; + + for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && floors[floor]; + floor++) + for (i = 0; !ret && i < 4; i++) + ret = device_create_file(dev, &doc_sys_attrs[floor][i]); + if (!ret) + return 0; + do { + while (--i >= 0) + device_remove_file(dev, &doc_sys_attrs[floor][i]); + i = 4; + } while (--floor >= 0); + return ret; +} + +static void doc_unregister_sysfs(struct platform_device *pdev, + struct mtd_info **floors) +{ + struct device *dev = &pdev->dev; + int floor, i; + + for (floor = 0; floor < DOC_MAX_NBFLOORS && floors[floor]; + floor++) + for (i = 0; i < 4; i++) + device_remove_file(dev, &doc_sys_attrs[floor][i]); +} + /* * Debug sysfs entries */ @@ -1927,6 +2044,9 @@ static int __init docg3_probe(struct platform_device *pdev) found++; } + ret = doc_register_sysfs(pdev, docg3_floors); + if (ret) + goto err_probe; if (!found) goto notfound; @@ -1963,6 +2083,7 @@ static int __exit docg3_release(struct platform_device *pdev) void __iomem *base = docg3->base; int floor; + doc_unregister_sysfs(pdev, docg3_floors); doc_dbg_unregister(docg3); for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) if (docg3_floors[floor]) diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 07182f9d484..a349915da77 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -118,6 +118,8 @@ #define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 0)) #define DOC_PROTECTION 0x1056 +#define DOC_DPS0_KEY 0x105c +#define DOC_DPS1_KEY 0x105e #define DOC_DPS0_ADDRLOW 0x1060 #define DOC_DPS0_ADDRHIGH 0x1062 #define DOC_DPS1_ADDRLOW 0x1064 @@ -252,6 +254,17 @@ #define DOC_PLANES_STATUS_PLANE0_KO 0x02 #define DOC_PLANES_STATUS_PLANE1_KO 0x04 +/* + * DPS key management + * + * Each floor of docg3 has 2 protection areas: DPS0 and DPS1. These areas span + * across block boundaries, and define whether these blocks can be read or + * written. + * The definition is dynamically stored in page 0 of blocks (2,3) for DPS0, and + * page 0 of blocks (4,5) for DPS1. + */ +#define DOC_LAYOUT_DPS_KEY_LENGTH 8 + /** * struct docg3 - DiskOnChip driver private data * @dev: the device currently under control -- cgit v1.2.3-70-g09d2 From 5d3667eee40a88f79f7f826f0b4c3c9647d7ea7a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 24 Nov 2011 16:29:16 +0100 Subject: mtd: Remove redundant spi driver bus initialization In ancient times it was necessary to manually initialize the bus field of an spi_driver to spi_bus_type. These days this is done in spi_driver_register(), so we can drop the manual assignment. The patch was generated using the following coccinelle semantic patch: // @@ identifier _driver; @@ struct spi_driver _driver = { .driver = { - .bus = &spi_bus_type, }, }; // Signed-off-by: Lars-Peter Clausen Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 1 - drivers/mtd/devices/mtd_dataflash.c | 1 - drivers/mtd/devices/sst25l.c | 1 - 3 files changed, 3 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 884904d3f9d..7c60dddbefc 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -992,7 +992,6 @@ static int __devexit m25p_remove(struct spi_device *spi) static struct spi_driver m25p80_driver = { .driver = { .name = "m25p80", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .id_table = m25p_ids, diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index d75c7af18a6..236057ead0d 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -936,7 +936,6 @@ static int __devexit dataflash_remove(struct spi_device *spi) static struct spi_driver dataflash_driver = { .driver = { .name = "mtd_dataflash", - .bus = &spi_bus_type, .owner = THIS_MODULE, .of_match_table = dataflash_dt_ids, }, diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index d38ef3bffe8..e45f62e8f19 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -444,7 +444,6 @@ static int __devexit sst25l_remove(struct spi_device *spi) static struct spi_driver sst25l_driver = { .driver = { .name = "sst25l", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = sst25l_probe, -- cgit v1.2.3-70-g09d2 From b49e345e61a2e0c4decbe9b1bd670ed5599fac6e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 28 Nov 2011 16:53:13 +0300 Subject: mtd: docg3: dereferencing an ERR_PTR() in docg3_probe() If doc_probe_device() returned an ERR_PTR, then we accidentally saved that to docg3_floors[floor] = mtd; which gets derefenced in the error handling when we call doc_release_device(). I've reworked the error handling to take care of that and hopefully make it a little simpler. Signed-off-by: Dan Carpenter Acked-by: Robert Jarzmik Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index d7df3114aa1..f7490a014e7 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -2027,21 +2027,24 @@ static int __init docg3_probe(struct platform_device *pdev) if (!docg3_bch) goto nomem2; - ret = 0; for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { mtd = doc_probe_device(base, floor, dev); - if (floor == 0 && !mtd) - goto notfound; - if (!IS_ERR_OR_NULL(mtd)) - ret = mtd_device_parse_register(mtd, part_probes, - NULL, NULL, 0); - else + if (IS_ERR(mtd)) { ret = PTR_ERR(mtd); + goto err_probe; + } + if (!mtd) { + if (floor == 0) + goto notfound; + else + continue; + } docg3_floors[floor] = mtd; + ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL, + 0); if (ret) goto err_probe; - if (mtd) - found++; + found++; } ret = doc_register_sysfs(pdev, docg3_floors); -- cgit v1.2.3-70-g09d2 From b604436cb06626363f0da357759797a5e1894baf Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Fri, 2 Dec 2011 20:00:12 +0100 Subject: mtd: docg3: fixes and cleanups This patch takes into account checkpatch, sparse and ECC comments. Signed-off-by: Robert Jarzmik Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 26 +++++++++++++------------- drivers/mtd/devices/docg3.h | 3 ++- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index f7490a014e7..2a32072c790 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -61,7 +61,7 @@ * */ -static unsigned int reliable_mode = 0; +static unsigned int reliable_mode; module_param(reliable_mode, uint, 0); MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, " "2=reliable) : MLC normal operations are in normal mode"); @@ -546,7 +546,7 @@ out: * @len: the number of bytes covered by the ECC (BCH covered) * * The function does initialize the hardware ECC engine to compute the Hamming - * ECC (on 1 byte) and the BCH Syndroms (on 7 bytes). + * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). * * Return 0 if succeeded, -EIO on error */ @@ -567,13 +567,13 @@ static int doc_read_page_ecc_init(struct docg3 *docg3, int len) * @len: the number of bytes covered by the ECC (BCH covered) * * The function does initialize the hardware ECC engine to compute the Hamming - * ECC (on 1 byte) and the BCH Syndroms (on 7 bytes). + * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). * * Return 0 if succeeded, -EIO on error */ static int doc_write_page_ecc_init(struct docg3 *docg3, int len) { - doc_writew(docg3, !DOC_ECCCONF0_READ_MODE + doc_writew(docg3, DOC_ECCCONF0_WRITE_MODE | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE | (len & DOC_ECCCONF0_DATA_BYTES_MASK), DOC_ECCCONF0); @@ -614,7 +614,7 @@ static void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes) } /** - * doc_correct_data - Fix if need be read data from flash + * doc_ecc_bch_fix_data - Fix if need be read data from flash * @docg3: the device * @buf: the buffer of read data (512 + 7 + 1 bytes) * @hwecc: the hardware calculated ECC. @@ -761,16 +761,16 @@ static void doc_write_page_putbytes(struct docg3 *docg3, int len, } /** - * doc_get_hw_bch_syndroms - Get hardware calculated BCH syndroms + * doc_get_bch_hw_ecc - Get hardware calculated BCH ECC * @docg3: the device - * @syns: the array of 7 integers where the syndroms will be stored + * @hwecc: the array of 7 integers where the hardware ecc will be stored */ -static void doc_get_hw_bch_syndroms(struct docg3 *docg3, u8 *syns) +static void doc_get_bch_hw_ecc(struct docg3 *docg3, u8 *hwecc) { int i; for (i = 0; i < DOC_ECC_BCH_SIZE; i++) - syns[i] = doc_register_readb(docg3, DOC_BCH_SYNDROM(i)); + hwecc[i] = doc_register_readb(docg3, DOC_BCH_HW_ECC(i)); } /** @@ -904,7 +904,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, NULL, 0); - doc_get_hw_bch_syndroms(docg3, hwecc); + doc_get_bch_hw_ecc(docg3, hwecc); eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); if (nboob >= DOC_LAYOUT_OOB_SIZE) { @@ -1248,7 +1248,7 @@ static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, const u_char *oob, int autoecc) { int block0, block1, page, ret, ofs = 0; - u8 syn[DOC_ECC_BCH_SIZE], hamming; + u8 hwecc[DOC_ECC_BCH_SIZE], hamming; doc_dbg("doc_write_page(to=%lld)\n", to); calc_block_sector(to, &block0, &block1, &page, &ofs, docg3->reliable); @@ -1278,8 +1278,8 @@ static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, &hamming); doc_delay(docg3, 2); - doc_get_hw_bch_syndroms(docg3, syn); - doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, syn); + doc_get_bch_hw_ecc(docg3, hwecc); + doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, hwecc); doc_delay(docg3, 2); doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_UNUSED_SZ, oob); diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index a349915da77..db0da436b49 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -115,7 +115,7 @@ #define DOC_ECCCONF1 0x1042 #define DOC_ECCPRESET 0x1044 #define DOC_HAMMINGPARITY 0x1046 -#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 0)) +#define DOC_BCH_HW_ECC(idx) (0x1048 + idx) #define DOC_PROTECTION 0x1056 #define DOC_DPS0_KEY 0x105c @@ -193,6 +193,7 @@ /* * Flash register : DOC_ECCCONF0 */ +#define DOC_ECCCONF0_WRITE_MODE 0x0000 #define DOC_ECCCONF0_READ_MODE 0x8000 #define DOC_ECCCONF0_AUTO_ECC_ENABLE 0x4000 #define DOC_ECCCONF0_HAMMING_ENABLE 0x1000 -- cgit v1.2.3-70-g09d2 From e10019bce9cbeb7675d5d08221012a2cfee0d498 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Fri, 16 Dec 2011 23:25:23 +0100 Subject: mtd: docg3: remove unused function As the MTD api has no use for the number of erase cycles each block has endured, remove the function which calculated that value. If one day MTD api finds it usefull for wear levelling algorithms to have this information, the function should be put back in place. Signed-off-by: Robert Jarzmik Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/docg3.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 2a32072c790..22d5099f778 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1037,6 +1037,7 @@ static int doc_block_isbad(struct mtd_info *mtd, loff_t from) return !is_good; } +#if 0 /** * doc_get_erase_count - Get block erase count * @docg3: the device @@ -1078,6 +1079,7 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from) return max(plane1_erase_count, plane2_erase_count); } +#endif /** * doc_get_op_status - get erase/write operation status -- cgit v1.2.3-70-g09d2 From 122f81d773a464220d64f1a382bde073df281d5a Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 29 Dec 2011 11:36:19 +0200 Subject: mtd: docprobe: use kzalloc instead Instead of calling 'kmalloc()' and them 'memeset(0)', use 'kzalloc()'. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/docprobe.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index 45116bb3029..706b847b46b 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -241,8 +241,7 @@ static void __init DoC_Probe(unsigned long physadr) return; } docfound = 1; - mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); - + mtd = kzalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); if (!mtd) { printk(KERN_WARNING "Cannot allocate memory for data structures. Dropping.\n"); iounmap(docptr); @@ -250,10 +249,6 @@ static void __init DoC_Probe(unsigned long physadr) } this = (struct DiskOnChip *)(&mtd[1]); - - memset((char *)mtd,0, sizeof(struct mtd_info)); - memset((char *)this, 0, sizeof(struct DiskOnChip)); - mtd->priv = this; this->virtadr = docptr; this->physadr = physadr; -- cgit v1.2.3-70-g09d2 From bea7fe031e5b81629f264f48335f1af74900f4b9 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 29 Dec 2011 11:40:00 +0200 Subject: mtd: doc: do not initialize mtd_info fields to 0 or NULL The 'struct mtd_info' object is allocated with 'kzalloc()', so it contains only zeroes - no need to initialize various fields to 0 or NULL. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/doc2000.c | 9 --------- drivers/mtd/devices/doc2001.c | 8 -------- drivers/mtd/devices/doc2001plus.c | 9 --------- drivers/mtd/devices/docg3.c | 3 --- 4 files changed, 29 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index e9fad915121..b1cdf647901 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -562,23 +562,14 @@ void DoC2k_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->size = 0; - mtd->erasesize = 0; mtd->writesize = 512; mtd->oobsize = 16; mtd->owner = THIS_MODULE; mtd->erase = doc_erase; - mtd->point = NULL; - mtd->unpoint = NULL; mtd->read = doc_read; mtd->write = doc_write; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; - mtd->sync = NULL; - - this->totlen = 0; - this->numchips = 0; - this->curfloor = -1; this->curchip = -1; mutex_init(&this->lock); diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index a3f7a27499b..7543b98f46c 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -343,25 +343,17 @@ void DoCMil_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->size = 0; /* FIXME: erase size is not always 8KiB */ mtd->erasesize = 0x2000; - mtd->writesize = 512; mtd->oobsize = 16; mtd->owner = THIS_MODULE; mtd->erase = doc_erase; - mtd->point = NULL; - mtd->unpoint = NULL; mtd->read = doc_read; mtd->write = doc_write; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; - mtd->sync = NULL; - - this->totlen = 0; - this->numchips = 0; this->curfloor = -1; this->curchip = -1; diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 99351bc3e0e..177510d0e7e 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -467,23 +467,14 @@ void DoCMilPlus_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->size = 0; - - mtd->erasesize = 0; mtd->writesize = 512; mtd->oobsize = 16; mtd->owner = THIS_MODULE; mtd->erase = doc_erase; - mtd->point = NULL; - mtd->unpoint = NULL; mtd->read = doc_read; mtd->write = doc_write; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; - mtd->sync = NULL; - - this->totlen = 0; - this->numchips = 0; this->curfloor = -1; this->curchip = -1; diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 22d5099f778..ad11ef0a81f 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1821,13 +1821,10 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) mtd->oobsize = DOC_LAYOUT_OOB_SIZE; mtd->owner = THIS_MODULE; mtd->erase = doc_erase; - mtd->point = NULL; - mtd->unpoint = NULL; mtd->read = doc_read; mtd->write = doc_write; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; - mtd->sync = NULL; mtd->block_isbad = doc_block_isbad; mtd->ecclayout = &docg3_oobinfo; } -- cgit v1.2.3-70-g09d2 From d81a32f2c16a3c42cf26f2216765c520630daa4e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 29 Dec 2011 18:06:01 +0200 Subject: mtd: sst25l: kill unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following gcc warning: drivers/mtd/devices/sst25l.c: In function ‘sst25l_probe’: drivers/mtd/devices/sst25l.c:381:11: warning: unused variable ‘i’ [-Wunused-variable] Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/sst25l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index e45f62e8f19..5fc198350b9 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -378,7 +378,7 @@ static int __devinit sst25l_probe(struct spi_device *spi) struct flash_info *flash_info; struct sst25l_flash *flash; struct flash_platform_data *data; - int ret, i; + int ret; flash_info = sst25l_match_device(spi); if (!flash_info) -- cgit v1.2.3-70-g09d2 From 1dbebd32562b3c2caeca35960e5cb00bfcc12900 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 30 Dec 2011 16:23:41 +0200 Subject: mtd: harmonize mtd_writev usage This patch makes the 'mtd_writev()' function more usable and logical. We first teach it to fall-back to the 'default_mtd_writev()' function if the MTD driver does not define its own '->writev()' method. Then we make block2mtd and JFFS2 just 'mtd_writev()' instead of 'default_mtd_writev()' function. This means we can now stop exporting 'default_mtd_writev()' and instead, export 'mtd_writev()'. This is much cleaner and more logical, as well as allows us to get read of another direct 'mtd->writev' access in JFFS2. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/block2mtd.c | 2 +- drivers/mtd/mtdcore.c | 26 +++++++++++++++++++++++--- fs/jffs2/writev.c | 6 +----- include/linux/mtd/mtd.h | 16 ++-------------- 4 files changed, 27 insertions(+), 23 deletions(-) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index b78f23169d4..c16f6b4e893 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -288,7 +288,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) dev->mtd.flags = MTD_CAP_RAM; dev->mtd.erase = block2mtd_erase; dev->mtd.write = block2mtd_write; - dev->mtd.writev = default_mtd_writev; + dev->mtd.writev = mtd_writev; dev->mtd.sync = block2mtd_sync; dev->mtd.read = block2mtd_read; dev->mtd.priv = dev; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 53a200f722b..4d0f3e557bd 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -696,8 +696,8 @@ EXPORT_SYMBOL_GPL(__put_mtd_device); * This function returns zero in case of success and a negative error code in * case of failure. */ -int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) +static int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) { unsigned long i; size_t totlen = 0, thislen; @@ -716,7 +716,27 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, *retlen = totlen; return ret; } -EXPORT_SYMBOL_GPL(default_mtd_writev); + +/* + * mtd_writev - the vector-based MTD write method + * @mtd: mtd device description object pointer + * @vecs: the vectors to write + * @count: count of vectors in @vecs + * @to: the MTD device offset to write to + * @retlen: on exit contains the count of bytes written to the MTD device. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + *retlen = 0; + if (!mtd->writev) + return default_mtd_writev(mtd, vecs, count, to, retlen); + return mtd->writev(mtd, vecs, count, to, retlen); +} +EXPORT_SYMBOL_GPL(mtd_writev); /** * mtd_kmalloc_up_to - allocate a contiguous buffer up to the specified size diff --git a/fs/jffs2/writev.c b/fs/jffs2/writev.c index 8d704073f8b..a1bda9dab3f 100644 --- a/fs/jffs2/writev.c +++ b/fs/jffs2/writev.c @@ -26,11 +26,7 @@ int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, } } - if (c->mtd->writev) - return mtd_writev(c->mtd, vecs, count, to, retlen); - else { - return default_mtd_writev(c->mtd, vecs, count, to, retlen); - } + return mtd_writev(c->mtd, vecs, count, to, retlen); } int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index a994129ede5..a58ecf4d1f8 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -394,16 +394,8 @@ static inline int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, return mtd->lock_user_prot_reg(mtd, from, len); } -/* - * kvec-based read/write method. NB: The 'count' parameter is the number of - * _vectors_, each of which contains an (ofs, len) tuple. - */ -static inline int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) -{ - *retlen = 0; - return mtd->writev(mtd, vecs, count, to, retlen); -} +int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen); static inline void mtd_sync(struct mtd_info *mtd) { @@ -510,10 +502,6 @@ struct mtd_notifier { extern void register_mtd_user (struct mtd_notifier *new); extern int unregister_mtd_user (struct mtd_notifier *old); - -int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen); - void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size); void mtd_erase_callback(struct erase_info *instr); -- cgit v1.2.3-70-g09d2 From b60ef99c1164a8ad346cf41f9e71acfffb6d25a6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 3 Jan 2012 16:35:25 -0200 Subject: mtd: Fix dependency for MTD_DOC200x Fix the following build warning: warning: (MTD_DOC2000 && MTD_DOC2001 && MTD_DOC2001PLUS) selects MTD_NAND_IDS which has unmet direct dependencies (MTD && MTD_NAND) Signed-off-by: Fabio Estevam Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/Kconfig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mtd/devices') diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 952e956ef01..37b05c3f279 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -191,6 +191,7 @@ comment "Disk-On-Chip Device Drivers" config MTD_DOC2000 tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)" + depends on MTD_NAND select MTD_DOCPROBE select MTD_NAND_IDS ---help--- @@ -213,6 +214,7 @@ config MTD_DOC2000 config MTD_DOC2001 tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)" + depends on MTD_NAND select MTD_DOCPROBE select MTD_NAND_IDS ---help--- @@ -234,6 +236,7 @@ config MTD_DOC2001 config MTD_DOC2001PLUS tristate "M-Systems Disk-On-Chip Millennium Plus" + depends on MTD_NAND select MTD_DOCPROBE select MTD_NAND_IDS ---help--- -- cgit v1.2.3-70-g09d2