diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-19 16:34:11 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-19 16:34:11 -0500 |
commit | 4935361766cc73949fe032cd157d314f288922ba (patch) | |
tree | 1584f81525ae05a04d515b13a4787cd8eed46029 /drivers/mtd/nand | |
parent | 2874b391bd78a5b8cb84be67297a345fbdec4ac8 (diff) | |
parent | 4f65992381112acd7d2732665a9eae492c2c9de6 (diff) |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (49 commits)
[MTD] [NAND] S3C2412 fix hw ecc
[MTD] [NAND] Work around false compiler warning in CAFÉ driver
[JFFS2] printk warning fixes
[MTD] [MAPS] ichxrom warning fix
[MTD] [MAPS] amd76xrom warning fix
[MTD] [MAPS] esb2rom warning fixes
[MTD] [MAPS] ck804xrom warning fix
[MTD] [MAPS] netsc520 warning fix
[MTD] [MAPS] sc520cdp warning fix
[MTD] [ONENAND] onenand_base warning fix
[MTD] [NAND] eXcite nand flash driver
[MTD] Improve heuristic for detecting wrong-endian RedBoot partition table
[MTD] Fix RedBoot partition parsing regression harder.
[MTD] [NAND] S3C2410: Hardware ECC correction code
[JFFS2] Use MTD_OOB_AUTO to automatically place cleanmarker on NAND
[MTD] Clarify OOB-operation interface comments
[MTD] remove unused ecctype,eccsize fields from struct mtd_info
[MTD] [NOR] Intel: remove ugly PROGREGION macros
[MTD] [NOR] STAA: use writesize instead off eccsize to represent ECC block
[MTD] OneNAND: Invalidate bufferRAM after erase
...
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 14 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/cafe.c | 89 | ||||
-rw-r--r-- | drivers/mtd/nand/cafe_ecc.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/excite_nandflash.c | 248 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 47 | ||||
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 96 |
7 files changed, 424 insertions, 73 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 358f55a82dbe..2d12dcdd740c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC | |||
126 | incorrect ECC generation, and if using these, the default of | 126 | incorrect ECC generation, and if using these, the default of |
127 | software ECC is preferable. | 127 | software ECC is preferable. |
128 | 128 | ||
129 | If you lay down a device with the hardware ECC, then you will | ||
130 | currently not be able to switch to software, as there is no | ||
131 | implementation for ECC method used by the S3C2410 | ||
132 | |||
133 | config MTD_NAND_NDFC | 129 | config MTD_NAND_NDFC |
134 | tristate "NDFC NanD Flash Controller" | 130 | tristate "NDFC NanD Flash Controller" |
135 | depends on MTD_NAND && 44x | 131 | depends on MTD_NAND && 44x |
@@ -221,9 +217,17 @@ config MTD_NAND_SHARPSL | |||
221 | tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" | 217 | tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" |
222 | depends on MTD_NAND && ARCH_PXA | 218 | depends on MTD_NAND && ARCH_PXA |
223 | 219 | ||
220 | config MTD_NAND_BASLER_EXCITE | ||
221 | tristate "Support for NAND Flash on Basler eXcite" | ||
222 | depends on MTD_NAND && BASLER_EXCITE | ||
223 | help | ||
224 | This enables the driver for the NAND flash device found on the | ||
225 | Basler eXcite Smart Camera. If built as a module, the driver | ||
226 | will be named "excite_nandflash.ko". | ||
227 | |||
224 | config MTD_NAND_CAFE | 228 | config MTD_NAND_CAFE |
225 | tristate "NAND support for OLPC CAFÉ chip" | 229 | tristate "NAND support for OLPC CAFÉ chip" |
226 | depends on PCI | 230 | depends on MTD_NAND && PCI |
227 | help | 231 | help |
228 | Use NAND flash attached to the CAFÉ chip designed for the $100 | 232 | Use NAND flash attached to the CAFÉ chip designed for the $100 |
229 | laptop. | 233 | laptop. |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f7a53f0b7017..80f1dfc77949 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o | |||
24 | obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o | 24 | obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o |
25 | obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o | 25 | obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o |
26 | obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o | 26 | obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o |
27 | obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o | ||
27 | 28 | ||
28 | nand-objs := nand_base.o nand_bbt.o | 29 | nand-objs := nand_base.o nand_bbt.o |
29 | cafe_nand-objs := cafe.o cafe_ecc.o | 30 | cafe_nand-objs := cafe.o cafe_ecc.o |
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 08cb060dfa3d..fd6bb3ed40df 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c | |||
@@ -78,8 +78,9 @@ module_param(regdebug, int, 0644); | |||
78 | static int checkecc = 1; | 78 | static int checkecc = 1; |
79 | module_param(checkecc, int, 0644); | 79 | module_param(checkecc, int, 0644); |
80 | 80 | ||
81 | static int slowtiming = 0; | 81 | static int numtimings; |
82 | module_param(slowtiming, int, 0644); | 82 | static int timing[3]; |
83 | module_param_array(timing, int, &numtimings, 0644); | ||
83 | 84 | ||
84 | /* Hrm. Why isn't this already conditional on something in the struct device? */ | 85 | /* Hrm. Why isn't this already conditional on something in the struct device? */ |
85 | #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) | 86 | #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) |
@@ -264,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | |||
264 | ndelay(100); | 265 | ndelay(100); |
265 | 266 | ||
266 | if (1) { | 267 | if (1) { |
267 | int c = 500000; | 268 | int c; |
268 | uint32_t irqs; | 269 | uint32_t irqs; |
269 | 270 | ||
270 | while (c--) { | 271 | for (c = 500000; c != 0; c--) { |
271 | irqs = cafe_readl(cafe, NAND_IRQ); | 272 | irqs = cafe_readl(cafe, NAND_IRQ); |
272 | if (irqs & doneint) | 273 | if (irqs & doneint) |
273 | break; | 274 | break; |
@@ -529,6 +530,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
529 | { | 530 | { |
530 | struct mtd_info *mtd; | 531 | struct mtd_info *mtd; |
531 | struct cafe_priv *cafe; | 532 | struct cafe_priv *cafe; |
533 | uint32_t timing1, timing2, timing3; | ||
532 | uint32_t ctrl; | 534 | uint32_t ctrl; |
533 | int err = 0; | 535 | int err = 0; |
534 | 536 | ||
@@ -580,31 +582,45 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
580 | cafe->nand.block_bad = cafe_nand_block_bad; | 582 | cafe->nand.block_bad = cafe_nand_block_bad; |
581 | } | 583 | } |
582 | 584 | ||
585 | if (numtimings && numtimings != 3) { | ||
586 | dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); | ||
587 | } | ||
588 | |||
589 | if (numtimings == 3) { | ||
590 | timing1 = timing[0]; | ||
591 | timing2 = timing[1]; | ||
592 | timing3 = timing[2]; | ||
593 | cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", | ||
594 | timing1, timing2, timing3); | ||
595 | } else { | ||
596 | timing1 = cafe_readl(cafe, NAND_TIMING1); | ||
597 | timing2 = cafe_readl(cafe, NAND_TIMING2); | ||
598 | timing3 = cafe_readl(cafe, NAND_TIMING3); | ||
599 | |||
600 | if (timing1 | timing2 | timing3) { | ||
601 | cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3); | ||
602 | } else { | ||
603 | dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); | ||
604 | timing1 = timing2 = timing3 = 0xffffffff; | ||
605 | } | ||
606 | } | ||
607 | |||
583 | /* Start off by resetting the NAND controller completely */ | 608 | /* Start off by resetting the NAND controller completely */ |
584 | cafe_writel(cafe, 1, NAND_RESET); | 609 | cafe_writel(cafe, 1, NAND_RESET); |
585 | cafe_writel(cafe, 0, NAND_RESET); | 610 | cafe_writel(cafe, 0, NAND_RESET); |
586 | 611 | ||
587 | cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); | 612 | cafe_writel(cafe, timing1, NAND_TIMING1); |
613 | cafe_writel(cafe, timing2, NAND_TIMING2); | ||
614 | cafe_writel(cafe, timing3, NAND_TIMING3); | ||
588 | 615 | ||
589 | /* Timings from Marvell's test code (not verified or calculated by us) */ | ||
590 | if (!slowtiming) { | ||
591 | cafe_writel(cafe, 0x01010a0a, NAND_TIMING1); | ||
592 | cafe_writel(cafe, 0x24121212, NAND_TIMING2); | ||
593 | cafe_writel(cafe, 0x11000000, NAND_TIMING3); | ||
594 | } else { | ||
595 | cafe_writel(cafe, 0xffffffff, NAND_TIMING1); | ||
596 | cafe_writel(cafe, 0xffffffff, NAND_TIMING2); | ||
597 | cafe_writel(cafe, 0xffffffff, NAND_TIMING3); | ||
598 | } | ||
599 | cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); | 616 | cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); |
600 | err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, | 617 | err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, |
601 | "CAFE NAND", mtd); | 618 | "CAFE NAND", mtd); |
602 | if (err) { | 619 | if (err) { |
603 | dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); | 620 | dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); |
604 | |||
605 | goto out_free_dma; | 621 | goto out_free_dma; |
606 | } | 622 | } |
607 | #if 1 | 623 | |
608 | /* Disable master reset, enable NAND clock */ | 624 | /* Disable master reset, enable NAND clock */ |
609 | ctrl = cafe_readl(cafe, GLOBAL_CTRL); | 625 | ctrl = cafe_readl(cafe, GLOBAL_CTRL); |
610 | ctrl &= 0xffffeff0; | 626 | ctrl &= 0xffffeff0; |
@@ -631,32 +647,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
631 | cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); | 647 | cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); |
632 | cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", | 648 | cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", |
633 | cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); | 649 | cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); |
634 | #endif | 650 | |
635 | #if 1 | 651 | /* Scan to find existence of the device */ |
636 | mtd->writesize=2048; | ||
637 | mtd->oobsize = 0x40; | ||
638 | memset(cafe->dmabuf, 0x5a, 2112); | ||
639 | cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); | ||
640 | cafe->nand.read_byte(mtd); | ||
641 | cafe->nand.read_byte(mtd); | ||
642 | cafe->nand.read_byte(mtd); | ||
643 | cafe->nand.read_byte(mtd); | ||
644 | cafe->nand.read_byte(mtd); | ||
645 | #endif | ||
646 | #if 0 | ||
647 | cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); | ||
648 | // nand_wait_ready(mtd); | ||
649 | cafe->nand.read_byte(mtd); | ||
650 | cafe->nand.read_byte(mtd); | ||
651 | cafe->nand.read_byte(mtd); | ||
652 | cafe->nand.read_byte(mtd); | ||
653 | #endif | ||
654 | #if 0 | ||
655 | writel(0x84600070, cafe->mmio); | ||
656 | udelay(10); | ||
657 | cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM)); | ||
658 | #endif | ||
659 | /* Scan to find existance of the device */ | ||
660 | if (nand_scan_ident(mtd, 1)) { | 652 | if (nand_scan_ident(mtd, 1)) { |
661 | err = -ENXIO; | 653 | err = -ENXIO; |
662 | goto out_irq; | 654 | goto out_irq; |
@@ -760,13 +752,4 @@ module_exit(cafe_nand_exit); | |||
760 | 752 | ||
761 | MODULE_LICENSE("GPL"); | 753 | MODULE_LICENSE("GPL"); |
762 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 754 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
763 | MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); | 755 | MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); |
764 | |||
765 | /* Correct ECC for 2048 bytes of 0xff: | ||
766 | 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ | ||
767 | |||
768 | /* dwmw2's B-test board, in case of completely screwing it: | ||
769 | Bad eraseblock 2394 at 0x12b40000 | ||
770 | Bad eraseblock 2627 at 0x14860000 | ||
771 | Bad eraseblock 3349 at 0x1a2a0000 | ||
772 | */ | ||
diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 1b9fa05a4474..ea5c8491d2c5 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c | |||
@@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = { | |||
1045 | 1045 | ||
1046 | static unsigned short err_pos(unsigned short din) | 1046 | static unsigned short err_pos(unsigned short din) |
1047 | { | 1047 | { |
1048 | BUG_ON(din > 4096); | 1048 | BUG_ON(din >= ARRAY_SIZE(err_pos_lut)); |
1049 | return err_pos_lut[din]; | 1049 | return err_pos_lut[din]; |
1050 | } | 1050 | } |
1051 | static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) | 1051 | static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) |
diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c new file mode 100644 index 000000000000..7e9afc4c7757 --- /dev/null +++ b/drivers/mtd/nand/excite_nandflash.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005 - 2007 by Basler Vision Technologies AG | ||
3 | * Author: Thomas Koeller <thomas.koeller.qbaslerweb.com> | ||
4 | * Original code by Thies Moeller <thies.moeller@baslerweb.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/string.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/err.h> | ||
30 | #include <linux/kernel.h> | ||
31 | |||
32 | #include <linux/mtd/mtd.h> | ||
33 | #include <linux/mtd/nand.h> | ||
34 | #include <linux/mtd/nand_ecc.h> | ||
35 | #include <linux/mtd/partitions.h> | ||
36 | |||
37 | #include <asm/io.h> | ||
38 | #include <asm/rm9k-ocd.h> | ||
39 | |||
40 | #include <excite_nandflash.h> | ||
41 | |||
42 | #define EXCITE_NANDFLASH_VERSION "0.1" | ||
43 | |||
44 | /* I/O register offsets */ | ||
45 | #define EXCITE_NANDFLASH_DATA_BYTE 0x00 | ||
46 | #define EXCITE_NANDFLASH_STATUS_BYTE 0x0c | ||
47 | #define EXCITE_NANDFLASH_ADDR_BYTE 0x10 | ||
48 | #define EXCITE_NANDFLASH_CMD_BYTE 0x14 | ||
49 | |||
50 | /* prefix for debug output */ | ||
51 | static const char module_id[] = "excite_nandflash"; | ||
52 | |||
53 | /* | ||
54 | * partition definition | ||
55 | */ | ||
56 | static const struct mtd_partition partition_info[] = { | ||
57 | { | ||
58 | .name = "eXcite RootFS", | ||
59 | .offset = 0, | ||
60 | .size = MTDPART_SIZ_FULL | ||
61 | } | ||
62 | }; | ||
63 | |||
64 | static inline const struct resource * | ||
65 | excite_nand_get_resource(struct platform_device *d, unsigned long flags, | ||
66 | const char *basename) | ||
67 | { | ||
68 | char buf[80]; | ||
69 | |||
70 | if (snprintf(buf, sizeof buf, "%s_%u", basename, d->id) >= sizeof buf) | ||
71 | return NULL; | ||
72 | return platform_get_resource_byname(d, flags, buf); | ||
73 | } | ||
74 | |||
75 | static inline void __iomem * | ||
76 | excite_nand_map_regs(struct platform_device *d, const char *basename) | ||
77 | { | ||
78 | void *result = NULL; | ||
79 | const struct resource *const r = | ||
80 | excite_nand_get_resource(d, IORESOURCE_MEM, basename); | ||
81 | |||
82 | if (r) | ||
83 | result = ioremap_nocache(r->start, r->end + 1 - r->start); | ||
84 | return result; | ||
85 | } | ||
86 | |||
87 | /* controller and mtd information */ | ||
88 | struct excite_nand_drvdata { | ||
89 | struct mtd_info board_mtd; | ||
90 | struct nand_chip board_chip; | ||
91 | void __iomem *regs; | ||
92 | void __iomem *tgt; | ||
93 | }; | ||
94 | |||
95 | /* Control function */ | ||
96 | static void excite_nand_control(struct mtd_info *mtd, int cmd, | ||
97 | unsigned int ctrl) | ||
98 | { | ||
99 | struct excite_nand_drvdata * const d = | ||
100 | container_of(mtd, struct excite_nand_drvdata, board_mtd); | ||
101 | |||
102 | switch (ctrl) { | ||
103 | case NAND_CTRL_CHANGE | NAND_CTRL_CLE: | ||
104 | d->tgt = d->regs + EXCITE_NANDFLASH_CMD_BYTE; | ||
105 | break; | ||
106 | case NAND_CTRL_CHANGE | NAND_CTRL_ALE: | ||
107 | d->tgt = d->regs + EXCITE_NANDFLASH_ADDR_BYTE; | ||
108 | break; | ||
109 | case NAND_CTRL_CHANGE | NAND_NCE: | ||
110 | d->tgt = d->regs + EXCITE_NANDFLASH_DATA_BYTE; | ||
111 | break; | ||
112 | } | ||
113 | |||
114 | if (cmd != NAND_CMD_NONE) | ||
115 | __raw_writeb(cmd, d->tgt); | ||
116 | } | ||
117 | |||
118 | /* Return 0 if flash is busy, 1 if ready */ | ||
119 | static int excite_nand_devready(struct mtd_info *mtd) | ||
120 | { | ||
121 | struct excite_nand_drvdata * const drvdata = | ||
122 | container_of(mtd, struct excite_nand_drvdata, board_mtd); | ||
123 | |||
124 | return __raw_readb(drvdata->regs + EXCITE_NANDFLASH_STATUS_BYTE); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Called by device layer to remove the driver. | ||
129 | * The binding to the mtd and all allocated | ||
130 | * resources are released. | ||
131 | */ | ||
132 | static int __exit excite_nand_remove(struct device *dev) | ||
133 | { | ||
134 | struct excite_nand_drvdata * const this = dev_get_drvdata(dev); | ||
135 | |||
136 | dev_set_drvdata(dev, NULL); | ||
137 | |||
138 | if (unlikely(!this)) { | ||
139 | printk(KERN_ERR "%s: called %s without private data!!", | ||
140 | module_id, __func__); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | /* first thing we need to do is release our mtd | ||
145 | * then go through freeing the resource used | ||
146 | */ | ||
147 | nand_release(&this->board_mtd); | ||
148 | |||
149 | /* free the common resources */ | ||
150 | iounmap(this->regs); | ||
151 | kfree(this); | ||
152 | |||
153 | DEBUG(MTD_DEBUG_LEVEL1, "%s: removed\n", module_id); | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * Called by device layer when it finds a device matching | ||
159 | * one our driver can handle. This code checks to see if | ||
160 | * it can allocate all necessary resources then calls the | ||
161 | * nand layer to look for devices. | ||
162 | */ | ||
163 | static int __init excite_nand_probe(struct device *dev) | ||
164 | { | ||
165 | struct platform_device * const pdev = to_platform_device(dev); | ||
166 | struct excite_nand_drvdata *drvdata; /* private driver data */ | ||
167 | struct nand_chip *board_chip; /* private flash chip data */ | ||
168 | struct mtd_info *board_mtd; /* mtd info for this board */ | ||
169 | int scan_res; | ||
170 | |||
171 | drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); | ||
172 | if (unlikely(!drvdata)) { | ||
173 | printk(KERN_ERR "%s: no memory for drvdata\n", | ||
174 | module_id); | ||
175 | return -ENOMEM; | ||
176 | } | ||
177 | |||
178 | /* bind private data into driver */ | ||
179 | dev_set_drvdata(dev, drvdata); | ||
180 | |||
181 | /* allocate and map the resource */ | ||
182 | drvdata->regs = | ||
183 | excite_nand_map_regs(pdev, EXCITE_NANDFLASH_RESOURCE_REGS); | ||
184 | |||
185 | if (unlikely(!drvdata->regs)) { | ||
186 | printk(KERN_ERR "%s: cannot reserve register region\n", | ||
187 | module_id); | ||
188 | kfree(drvdata); | ||
189 | return -ENXIO; | ||
190 | } | ||
191 | |||
192 | drvdata->tgt = drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; | ||
193 | |||
194 | /* initialise our chip */ | ||
195 | board_chip = &drvdata->board_chip; | ||
196 | board_chip->IO_ADDR_R = board_chip->IO_ADDR_W = | ||
197 | drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; | ||
198 | board_chip->cmd_ctrl = excite_nand_control; | ||
199 | board_chip->dev_ready = excite_nand_devready; | ||
200 | board_chip->chip_delay = 25; | ||
201 | board_chip->ecc.mode = NAND_ECC_SOFT; | ||
202 | |||
203 | /* link chip to mtd */ | ||
204 | board_mtd = &drvdata->board_mtd; | ||
205 | board_mtd->priv = board_chip; | ||
206 | |||
207 | DEBUG(MTD_DEBUG_LEVEL2, "%s: device scan\n", module_id); | ||
208 | scan_res = nand_scan(&drvdata->board_mtd, 1); | ||
209 | |||
210 | if (likely(!scan_res)) { | ||
211 | DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id); | ||
212 | add_mtd_partitions(&drvdata->board_mtd, partition_info, | ||
213 | sizeof partition_info / sizeof partition_info[0]); | ||
214 | } else { | ||
215 | iounmap(drvdata->regs); | ||
216 | kfree(drvdata); | ||
217 | printk(KERN_ERR "%s: device scan failed\n", module_id); | ||
218 | return -EIO; | ||
219 | } | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static struct device_driver excite_nand_driver = { | ||
224 | .name = "excite_nand", | ||
225 | .bus = &platform_bus_type, | ||
226 | .probe = excite_nand_probe, | ||
227 | .remove = __exit_p(excite_nand_remove) | ||
228 | }; | ||
229 | |||
230 | static int __init excite_nand_init(void) | ||
231 | { | ||
232 | pr_info("Basler eXcite nand flash driver Version " | ||
233 | EXCITE_NANDFLASH_VERSION "\n"); | ||
234 | return driver_register(&excite_nand_driver); | ||
235 | } | ||
236 | |||
237 | static void __exit excite_nand_exit(void) | ||
238 | { | ||
239 | driver_unregister(&excite_nand_driver); | ||
240 | } | ||
241 | |||
242 | module_init(excite_nand_init); | ||
243 | module_exit(excite_nand_exit); | ||
244 | |||
245 | MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>"); | ||
246 | MODULE_DESCRIPTION("Basler eXcite NAND-Flash driver"); | ||
247 | MODULE_LICENSE("GPL"); | ||
248 | MODULE_VERSION(EXCITE_NANDFLASH_VERSION) | ||
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dfe56e03e48b..acaf97bc80d1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
1272 | DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", | 1272 | DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", |
1273 | (unsigned long long)from, readlen); | 1273 | (unsigned long long)from, readlen); |
1274 | 1274 | ||
1275 | if (ops->mode == MTD_OOB_RAW) | 1275 | if (ops->mode == MTD_OOB_AUTO) |
1276 | len = mtd->oobsize; | ||
1277 | else | ||
1278 | len = chip->ecc.layout->oobavail; | 1276 | len = chip->ecc.layout->oobavail; |
1277 | else | ||
1278 | len = mtd->oobsize; | ||
1279 | |||
1280 | if (unlikely(ops->ooboffs >= len)) { | ||
1281 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
1282 | "Attempt to start read outside oob\n"); | ||
1283 | return -EINVAL; | ||
1284 | } | ||
1285 | |||
1286 | /* Do not allow reads past end of device */ | ||
1287 | if (unlikely(from >= mtd->size || | ||
1288 | ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - | ||
1289 | (from >> chip->page_shift)) * len)) { | ||
1290 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
1291 | "Attempt read beyond end of device\n"); | ||
1292 | return -EINVAL; | ||
1293 | } | ||
1279 | 1294 | ||
1280 | chipnr = (int)(from >> chip->chip_shift); | 1295 | chipnr = (int)(from >> chip->chip_shift); |
1281 | chip->select_chip(mtd, chipnr); | 1296 | chip->select_chip(mtd, chipnr); |
@@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1742 | static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | 1757 | static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, |
1743 | struct mtd_oob_ops *ops) | 1758 | struct mtd_oob_ops *ops) |
1744 | { | 1759 | { |
1745 | int chipnr, page, status; | 1760 | int chipnr, page, status, len; |
1746 | struct nand_chip *chip = mtd->priv; | 1761 | struct nand_chip *chip = mtd->priv; |
1747 | 1762 | ||
1748 | DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", | 1763 | DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", |
1749 | (unsigned int)to, (int)ops->ooblen); | 1764 | (unsigned int)to, (int)ops->ooblen); |
1750 | 1765 | ||
1766 | if (ops->mode == MTD_OOB_AUTO) | ||
1767 | len = chip->ecc.layout->oobavail; | ||
1768 | else | ||
1769 | len = mtd->oobsize; | ||
1770 | |||
1751 | /* Do not allow write past end of page */ | 1771 | /* Do not allow write past end of page */ |
1752 | if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) { | 1772 | if ((ops->ooboffs + ops->ooblen) > len) { |
1753 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 1773 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " |
1754 | "Attempt to write past end of page\n"); | 1774 | "Attempt to write past end of page\n"); |
1755 | return -EINVAL; | 1775 | return -EINVAL; |
1756 | } | 1776 | } |
1757 | 1777 | ||
1778 | if (unlikely(ops->ooboffs >= len)) { | ||
1779 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
1780 | "Attempt to start write outside oob\n"); | ||
1781 | return -EINVAL; | ||
1782 | } | ||
1783 | |||
1784 | /* Do not allow reads past end of device */ | ||
1785 | if (unlikely(to >= mtd->size || | ||
1786 | ops->ooboffs + ops->ooblen > | ||
1787 | ((mtd->size >> chip->page_shift) - | ||
1788 | (to >> chip->page_shift)) * len)) { | ||
1789 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
1790 | "Attempt write beyond end of device\n"); | ||
1791 | return -EINVAL; | ||
1792 | } | ||
1793 | |||
1758 | chipnr = (int)(to >> chip->chip_shift); | 1794 | chipnr = (int)(to >> chip->chip_shift); |
1759 | chip->select_chip(mtd, chipnr); | 1795 | chip->select_chip(mtd, chipnr); |
1760 | 1796 | ||
@@ -2530,7 +2566,6 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2530 | /* Fill in remaining MTD driver data */ | 2566 | /* Fill in remaining MTD driver data */ |
2531 | mtd->type = MTD_NANDFLASH; | 2567 | mtd->type = MTD_NANDFLASH; |
2532 | mtd->flags = MTD_CAP_NANDFLASH; | 2568 | mtd->flags = MTD_CAP_NANDFLASH; |
2533 | mtd->ecctype = MTD_ECC_SW; | ||
2534 | mtd->erase = nand_erase; | 2569 | mtd->erase = nand_erase; |
2535 | mtd->point = NULL; | 2570 | mtd->point = NULL; |
2536 | mtd->unpoint = NULL; | 2571 | mtd->unpoint = NULL; |
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8b3203571eeb..0ddfd6de75c5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c | |||
@@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) | |||
337 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, | 337 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, |
338 | u_char *read_ecc, u_char *calc_ecc) | 338 | u_char *read_ecc, u_char *calc_ecc) |
339 | { | 339 | { |
340 | pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc); | 340 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
341 | unsigned int diff0, diff1, diff2; | ||
342 | unsigned int bit, byte; | ||
341 | 343 | ||
342 | pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", | 344 | pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); |
343 | read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]); | ||
344 | 345 | ||
345 | if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2]) | 346 | diff0 = read_ecc[0] ^ calc_ecc[0]; |
346 | return 0; | 347 | diff1 = read_ecc[1] ^ calc_ecc[1]; |
348 | diff2 = read_ecc[2] ^ calc_ecc[2]; | ||
349 | |||
350 | pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", | ||
351 | __func__, | ||
352 | read_ecc[0], read_ecc[1], read_ecc[2], | ||
353 | calc_ecc[0], calc_ecc[1], calc_ecc[2], | ||
354 | diff0, diff1, diff2); | ||
355 | |||
356 | if (diff0 == 0 && diff1 == 0 && diff2 == 0) | ||
357 | return 0; /* ECC is ok */ | ||
358 | |||
359 | /* Can we correct this ECC (ie, one row and column change). | ||
360 | * Note, this is similar to the 256 error code on smartmedia */ | ||
361 | |||
362 | if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && | ||
363 | ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && | ||
364 | ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { | ||
365 | /* calculate the bit position of the error */ | ||
366 | |||
367 | bit = (diff2 >> 2) & 1; | ||
368 | bit |= (diff2 >> 3) & 2; | ||
369 | bit |= (diff2 >> 4) & 4; | ||
370 | |||
371 | /* calculate the byte position of the error */ | ||
372 | |||
373 | byte = (diff1 << 1) & 0x80; | ||
374 | byte |= (diff1 << 2) & 0x40; | ||
375 | byte |= (diff1 << 3) & 0x20; | ||
376 | byte |= (diff1 << 4) & 0x10; | ||
377 | |||
378 | byte |= (diff0 >> 3) & 0x08; | ||
379 | byte |= (diff0 >> 2) & 0x04; | ||
380 | byte |= (diff0 >> 1) & 0x02; | ||
381 | byte |= (diff0 >> 0) & 0x01; | ||
347 | 382 | ||
348 | /* we curently have no method for correcting the error */ | 383 | byte |= (diff2 << 8) & 0x100; |
349 | 384 | ||
350 | return -1; | 385 | dev_dbg(info->device, "correcting error bit %d, byte %d\n", |
386 | bit, byte); | ||
387 | |||
388 | dat[byte] ^= (1 << bit); | ||
389 | return 1; | ||
390 | } | ||
391 | |||
392 | /* if there is only one bit difference in the ECC, then | ||
393 | * one of only a row or column parity has changed, which | ||
394 | * means the error is most probably in the ECC itself */ | ||
395 | |||
396 | diff0 |= (diff1 << 8); | ||
397 | diff0 |= (diff2 << 16); | ||
398 | |||
399 | if ((diff0 & ~(1<<fls(diff0))) == 0) | ||
400 | return 1; | ||
401 | |||
402 | return 0; | ||
351 | } | 403 | } |
352 | 404 | ||
353 | /* ECC functions | 405 | /* ECC functions |
@@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) | |||
366 | writel(ctrl, info->regs + S3C2410_NFCONF); | 418 | writel(ctrl, info->regs + S3C2410_NFCONF); |
367 | } | 419 | } |
368 | 420 | ||
421 | static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) | ||
422 | { | ||
423 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | ||
424 | unsigned long ctrl; | ||
425 | |||
426 | ctrl = readl(info->regs + S3C2440_NFCONT); | ||
427 | writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT); | ||
428 | } | ||
429 | |||
369 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) | 430 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) |
370 | { | 431 | { |
371 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 432 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
@@ -383,6 +444,21 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u | |||
383 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); | 444 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); |
384 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); | 445 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); |
385 | 446 | ||
447 | pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, | ||
448 | ecc_code[0], ecc_code[1], ecc_code[2]); | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) | ||
454 | { | ||
455 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | ||
456 | unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); | ||
457 | |||
458 | ecc_code[0] = ecc; | ||
459 | ecc_code[1] = ecc >> 8; | ||
460 | ecc_code[2] = ecc >> 16; | ||
461 | |||
386 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); | 462 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); |
387 | 463 | ||
388 | return 0; | 464 | return 0; |
@@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u | |||
397 | ecc_code[1] = ecc >> 8; | 473 | ecc_code[1] = ecc >> 8; |
398 | ecc_code[2] = ecc >> 16; | 474 | ecc_code[2] = ecc >> 16; |
399 | 475 | ||
400 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); | 476 | pr_debug("%s: returning ecc %06x\n", __func__, ecc); |
401 | 477 | ||
402 | return 0; | 478 | return 0; |
403 | } | 479 | } |
@@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | |||
565 | break; | 641 | break; |
566 | 642 | ||
567 | case TYPE_S3C2412: | 643 | case TYPE_S3C2412: |
644 | chip->ecc.hwctl = s3c2412_nand_enable_hwecc; | ||
645 | chip->ecc.calculate = s3c2412_nand_calculate_ecc; | ||
646 | break; | ||
647 | |||
568 | case TYPE_S3C2440: | 648 | case TYPE_S3C2440: |
569 | chip->ecc.hwctl = s3c2440_nand_enable_hwecc; | 649 | chip->ecc.hwctl = s3c2440_nand_enable_hwecc; |
570 | chip->ecc.calculate = s3c2440_nand_calculate_ecc; | 650 | chip->ecc.calculate = s3c2440_nand_calculate_ecc; |