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/docg3.h') 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 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/docg3.h') 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 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/docg3.h') 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/docg3.h') 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 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/docg3.h') 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/docg3.h') 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/docg3.h') 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/docg3.h') 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 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/docg3.h') 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