diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 9 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/sm_common.c | 107 | ||||
-rw-r--r-- | drivers/mtd/nand/sm_common.h | 61 |
4 files changed, 178 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 8a7ecfab4fe7..5010344f4bb2 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -18,6 +18,10 @@ config MTD_NAND_VERIFY_WRITE | |||
18 | device thinks the write was successful, a bit could have been | 18 | device thinks the write was successful, a bit could have been |
19 | flipped accidentally due to device wear or something else. | 19 | flipped accidentally due to device wear or something else. |
20 | 20 | ||
21 | config MTD_NAND_SMARTMEDIA | ||
22 | boolean | ||
23 | default n | ||
24 | |||
21 | config MTD_NAND_ECC_SMC | 25 | config MTD_NAND_ECC_SMC |
22 | bool "NAND ECC Smart Media byte order" | 26 | bool "NAND ECC Smart Media byte order" |
23 | default n | 27 | default n |
@@ -25,6 +29,11 @@ config MTD_NAND_ECC_SMC | |||
25 | Software ECC according to the Smart Media Specification. | 29 | Software ECC according to the Smart Media Specification. |
26 | The original Linux implementation had byte 0 and 1 swapped. | 30 | The original Linux implementation had byte 0 and 1 swapped. |
27 | 31 | ||
32 | config MTD_SM_COMMON | ||
33 | select MTD_NAND_SMARTMEDIA | ||
34 | tristate | ||
35 | default n | ||
36 | |||
28 | config MTD_NAND_MUSEUM_IDS | 37 | config MTD_NAND_MUSEUM_IDS |
29 | bool "Enable chip ids for obsolete ancient NAND devices" | 38 | bool "Enable chip ids for obsolete ancient NAND devices" |
30 | depends on MTD_NAND | 39 | depends on MTD_NAND |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1d19b7ab903a..d9257961a6ee 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o | 5 | obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o |
6 | obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o | 6 | obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o |
7 | obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o | ||
7 | 8 | ||
8 | obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o | 9 | obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o |
9 | obj-$(CONFIG_MTD_NAND_SPIA) += spia.o | 10 | obj-$(CONFIG_MTD_NAND_SPIA) += spia.o |
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c new file mode 100644 index 000000000000..07b6f725723f --- /dev/null +++ b/drivers/mtd/nand/sm_common.c | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright © 2009 - Maxim Levitsky | ||
3 | * Common routines & support for xD format | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/mtd/nand.h> | ||
11 | #include "sm_common.h" | ||
12 | |||
13 | static struct nand_ecclayout nand_oob_sm = { | ||
14 | .eccbytes = 6, | ||
15 | .eccpos = {8, 9, 10, 13, 14, 15}, | ||
16 | .oobfree = { | ||
17 | {.offset = 0 , .length = 4}, /* reserved */ | ||
18 | {.offset = 6 , .length = 2}, /* LBA1 */ | ||
19 | {.offset = 11, .length = 2} /* LBA2 */ | ||
20 | } | ||
21 | }; | ||
22 | |||
23 | /* NOTE: This layout is is not compatabable with SmartMedia, */ | ||
24 | /* because the 256 byte devices have page depenent oob layout */ | ||
25 | /* However it does preserve the bad block markers */ | ||
26 | /* If you use smftl, it will bypass this and work correctly */ | ||
27 | /* If you not, then you break SmartMedia compliance anyway */ | ||
28 | |||
29 | static struct nand_ecclayout nand_oob_sm_small = { | ||
30 | .eccbytes = 3, | ||
31 | .eccpos = {0, 1, 2}, | ||
32 | .oobfree = { | ||
33 | {.offset = 3 , .length = 2}, /* reserved */ | ||
34 | {.offset = 6 , .length = 2}, /* LBA1 */ | ||
35 | } | ||
36 | }; | ||
37 | |||
38 | |||
39 | static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) | ||
40 | { | ||
41 | struct mtd_oob_ops ops; | ||
42 | struct sm_oob oob; | ||
43 | int ret, error = 0; | ||
44 | |||
45 | memset(&oob, -1, SM_OOB_SIZE); | ||
46 | oob.block_status = 0x0F; | ||
47 | |||
48 | /* As long as this function is called on erase block boundaries | ||
49 | it will work correctly for 256 byte nand */ | ||
50 | ops.mode = MTD_OOB_PLACE; | ||
51 | ops.ooboffs = 0; | ||
52 | ops.ooblen = mtd->oobsize; | ||
53 | ops.oobbuf = (void *)&oob; | ||
54 | ops.datbuf = NULL; | ||
55 | |||
56 | |||
57 | ret = mtd->write_oob(mtd, ofs, &ops); | ||
58 | if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { | ||
59 | printk(KERN_NOTICE | ||
60 | "sm_common: can't mark sector at %i as bad\n", | ||
61 | (int)ofs); | ||
62 | error = -EIO; | ||
63 | } else | ||
64 | mtd->ecc_stats.badblocks++; | ||
65 | |||
66 | return error; | ||
67 | } | ||
68 | |||
69 | |||
70 | int sm_register_device(struct mtd_info *mtd) | ||
71 | { | ||
72 | struct nand_chip *chip = (struct nand_chip *)mtd->priv; | ||
73 | int ret; | ||
74 | |||
75 | chip->options |= NAND_SKIP_BBTSCAN | NAND_SMARTMEDIA; | ||
76 | |||
77 | /* Scan for card properties */ | ||
78 | ret = nand_scan_ident(mtd, 1); | ||
79 | |||
80 | if (ret) | ||
81 | return ret; | ||
82 | |||
83 | /* Bad block marker postion */ | ||
84 | chip->badblockpos = 0x05; | ||
85 | chip->badblockbits = 7; | ||
86 | chip->block_markbad = sm_block_markbad; | ||
87 | |||
88 | /* ECC layout */ | ||
89 | if (mtd->writesize == SM_SECTOR_SIZE) | ||
90 | chip->ecc.layout = &nand_oob_sm; | ||
91 | else if (mtd->writesize == SM_SMALL_PAGE) | ||
92 | chip->ecc.layout = &nand_oob_sm_small; | ||
93 | else | ||
94 | return -ENODEV; | ||
95 | |||
96 | ret = nand_scan_tail(mtd); | ||
97 | |||
98 | if (ret) | ||
99 | return ret; | ||
100 | |||
101 | return add_mtd_device(mtd); | ||
102 | } | ||
103 | EXPORT_SYMBOL_GPL(sm_register_device); | ||
104 | |||
105 | MODULE_LICENSE("GPL"); | ||
106 | MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); | ||
107 | MODULE_DESCRIPTION("Common SmartMedia/xD functions"); | ||
diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h new file mode 100644 index 000000000000..7c03314bdac5 --- /dev/null +++ b/drivers/mtd/nand/sm_common.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * Copyright © 2009 - Maxim Levitsky | ||
3 | * Common routines & support for SmartMedia/xD format | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #include <linux/bitops.h> | ||
10 | #include <linux/mtd/mtd.h> | ||
11 | |||
12 | /* Full oob structure as written on the flash */ | ||
13 | struct sm_oob { | ||
14 | uint32_t reserved; | ||
15 | uint8_t data_status; | ||
16 | uint8_t block_status; | ||
17 | uint8_t lba_copy1[2]; | ||
18 | uint8_t ecc2[3]; | ||
19 | uint8_t lba_copy2[2]; | ||
20 | uint8_t ecc1[3]; | ||
21 | } __attribute__((packed)); | ||
22 | |||
23 | |||
24 | /* one sector is always 512 bytes, but it can consist of two nand pages */ | ||
25 | #define SM_SECTOR_SIZE 512 | ||
26 | |||
27 | /* oob area is also 16 bytes, but might be from two pages */ | ||
28 | #define SM_OOB_SIZE 16 | ||
29 | |||
30 | /* This is maximum zone size, and all devices that have more that one zone | ||
31 | have this size */ | ||
32 | #define SM_MAX_ZONE_SIZE 1024 | ||
33 | |||
34 | /* support for small page nand */ | ||
35 | #define SM_SMALL_PAGE 256 | ||
36 | #define SM_SMALL_OOB_SIZE 8 | ||
37 | |||
38 | |||
39 | extern int sm_register_device(struct mtd_info *mtd); | ||
40 | |||
41 | |||
42 | inline int sm_sector_valid(struct sm_oob *oob) | ||
43 | { | ||
44 | return hweight16(oob->data_status) >= 5; | ||
45 | } | ||
46 | |||
47 | inline int sm_block_valid(struct sm_oob *oob) | ||
48 | { | ||
49 | return hweight16(oob->block_status) >= 7; | ||
50 | } | ||
51 | |||
52 | inline int sm_block_erased(struct sm_oob *oob) | ||
53 | { | ||
54 | static const uint32_t erased_pattern[4] = { | ||
55 | 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; | ||
56 | |||
57 | /* First test for erased block */ | ||
58 | if (!memcmp(oob, erased_pattern, sizeof(*oob))) | ||
59 | return 1; | ||
60 | return 0; | ||
61 | } | ||