Date: | 2010-08-29 06:00:55 (13 years 7 months ago) |
---|---|
Author: | Maarten ter Huurne |
Commit: | f2a4e73d3618fc5ff8d9872ef804e01481e5f182 |
Message: | mtd: cc_ftl: New FTL driver for media players using China Chip
firmware. Tested on Dingoo A320: the FAT partition on the NAND is mounted successfully. Read-only for now. Not robust against bad FTL admin data yet: the driver won't crash, but it might return unnecessary I/O errors. Squashed version of development done in jz-2.6.35 branch. |
Files: |
drivers/mtd/Kconfig (2 diffs) drivers/mtd/Makefile (1 diff) drivers/mtd/cc_ftl.c (1 diff) |
Change Details
drivers/mtd/Kconfig | ||
---|---|---|
304 | 304 | This enables read only access to SmartMedia formatted NAND |
305 | 305 | flash. You can mount it with FAT file system. |
306 | 306 | |
307 | ||
308 | 307 | config SM_FTL |
309 | 308 | tristate "SmartMedia/xD new translation layer" |
310 | 309 | depends on EXPERIMENTAL && BLOCK |
... | ... | |
320 | 319 | If you only need R/O access, you can use older R/O driver |
321 | 320 | (CONFIG_SSFDC) |
322 | 321 | |
322 | config CC_FTL | |
323 | tristate "China Chip Flash Translation Layer support" | |
324 | depends on BLOCK | |
325 | select MTD_BLKDEVS | |
326 | ---help--- | |
327 | This provides support for the flash translation layer used by | |
328 | media players that run firmware from China Chip. | |
329 | ||
323 | 330 | config MTD_OOPS |
324 | 331 | tristate "Log panic/oops to an MTD buffer" |
325 | 332 | help |
drivers/mtd/Makefile | ||
---|---|---|
19 | 19 | obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o |
20 | 20 | obj-$(CONFIG_MTD_BLOCK) += mtdblock.o |
21 | 21 | obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o |
22 | obj-$(CONFIG_CC_FTL) += cc_ftl.o | |
22 | 23 | obj-$(CONFIG_FTL) += ftl.o |
23 | 24 | obj-$(CONFIG_NFTL) += nftl.o |
24 | 25 | obj-$(CONFIG_INFTL) += inftl.o |
drivers/mtd/cc_ftl.c | ||
---|---|---|
1 | /* | |
2 | * Copyright (C) 2010, Maarten ter Huurne <maarten@treewalker.org> | |
3 | * Flash Translation Layer for media players using firmware from China Chip. | |
4 | * | |
5 | * This initial implementation provides read-only access. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License along | |
13 | * with this program; if not, write to the Free Software Foundation, Inc., | |
14 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/errno.h> | |
19 | #include <linux/hdreg.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/mtd/blktrans.h> | |
23 | #include <linux/mtd/mtd.h> | |
24 | #include <linux/mtd/nand.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/types.h> | |
27 | #include <mtd/mtd-abi.h> | |
28 | ||
29 | ||
30 | #define SECTOR_SIZE 512 | |
31 | ||
32 | struct cc_ftl_partition { | |
33 | struct mtd_blktrans_dev mbd; | |
34 | ||
35 | uint32_t *map; | |
36 | }; | |
37 | ||
38 | static int cc_ftl_readsect(struct mtd_blktrans_dev *dev, unsigned long block, | |
39 | char *buffer) | |
40 | { | |
41 | struct cc_ftl_partition *partition = (struct cc_ftl_partition *)dev; | |
42 | struct mtd_info *mtd = dev->mtd; | |
43 | uint64_t log_offs, phy_offs; | |
44 | uint32_t log_blk, phy_blk; | |
45 | size_t retlen; | |
46 | int ret; | |
47 | ||
48 | /* Find physical location. */ | |
49 | if (block >= dev->size) | |
50 | return -EIO; | |
51 | log_offs = ((uint64_t)block) * SECTOR_SIZE; | |
52 | log_blk = mtd_div_by_eb(log_offs, mtd); | |
53 | phy_blk = partition->map[log_blk]; | |
54 | if (phy_blk == (uint32_t)-1) | |
55 | return -EIO; | |
56 | phy_offs = (uint64_t)phy_blk * mtd->erasesize; | |
57 | phy_offs += mtd_mod_by_eb(log_offs, mtd); | |
58 | ||
59 | /* Read data. */ | |
60 | ret = mtd->read(mtd, phy_offs, SECTOR_SIZE, &retlen, buffer); | |
61 | if (ret == -EUCLEAN) /* sector contains correctable errors */ | |
62 | ret = 0; | |
63 | if (ret) | |
64 | return ret; | |
65 | if (retlen != SECTOR_SIZE) | |
66 | return -EIO; | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | uint32_t *cc_ftl_build_block_map(struct mtd_info *mtd) | |
72 | { | |
73 | uint32_t num_blk = mtd_div_by_eb(mtd->size, mtd); | |
74 | uint32_t blk_found; | |
75 | uint32_t pages_per_blk = mtd_div_by_ws(mtd->erasesize, mtd); | |
76 | uint32_t phy_blk; | |
77 | uint8_t oob_buf[mtd->oobsize]; | |
78 | unsigned int seq_offs = mtd->oobsize - 4; | |
79 | uint32_t *map; | |
80 | ||
81 | // TODO: Is it worth reading multiple oobs at once? | |
82 | // Reading two will at least help against bit errors. | |
83 | struct mtd_oob_ops oob_ops = { | |
84 | .mode = MTD_OOB_RAW, | |
85 | .len = 0, | |
86 | .ooblen = mtd->oobsize, | |
87 | .datbuf = NULL, | |
88 | .oobbuf = oob_buf, | |
89 | }; | |
90 | ||
91 | map = kmalloc(sizeof(uint32_t) * num_blk, GFP_KERNEL); | |
92 | if (!map) | |
93 | return NULL; | |
94 | memset(map, 0xFF, sizeof(uint32_t) * num_blk); | |
95 | ||
96 | blk_found = 0; | |
97 | for (phy_blk = 0; phy_blk < num_blk; phy_blk++) { | |
98 | loff_t ofs = (loff_t)phy_blk << mtd->erasesize_shift; | |
99 | uint16_t signature; | |
100 | uint16_t last_page; | |
101 | uint32_t log_blk; | |
102 | int err; | |
103 | ||
104 | if (mtd->block_isbad(mtd, ofs)) | |
105 | continue; | |
106 | err = mtd->read_oob(mtd, ofs, &oob_ops); | |
107 | if (err) | |
108 | continue; | |
109 | signature = oob_buf[0] | ((uint16_t)oob_buf[1] << 8); | |
110 | if (signature != 0x00FF) | |
111 | continue; | |
112 | last_page = oob_buf[2] | ((uint16_t)oob_buf[3] << 8); | |
113 | if (last_page >= pages_per_blk) | |
114 | continue; | |
115 | log_blk = oob_buf[seq_offs] | | |
116 | ((uint32_t)oob_buf[seq_offs + 1] << 8) | | |
117 | ((uint32_t)oob_buf[seq_offs + 2] << 16) | | |
118 | ((uint32_t)oob_buf[seq_offs + 3] << 24); | |
119 | if (log_blk == 0xFFFFFFFF) | |
120 | continue; | |
121 | if (log_blk >= num_blk) { | |
122 | printk(KERN_WARNING "physical block %d claims " | |
123 | "logical block %d which is beyond " | |
124 | "partition end %d\n", | |
125 | phy_blk, log_blk, num_blk | |
126 | ); | |
127 | continue; | |
128 | } | |
129 | if (map[log_blk] != 0xFFFFFFFF) { | |
130 | // TODO: Version number might sort this out. | |
131 | printk(KERN_WARNING "physical block %d and %d both " | |
132 | "claim logical block %d\n", | |
133 | map[log_blk], phy_blk, log_blk | |
134 | ); | |
135 | continue; | |
136 | } | |
137 | map[log_blk] = phy_blk; | |
138 | blk_found++; | |
139 | } | |
140 | ||
141 | if (blk_found == 0) { | |
142 | kfree(map); | |
143 | return NULL; | |
144 | } | |
145 | ||
146 | if (0) { | |
147 | uint32_t log_blk; | |
148 | for (log_blk = 0; log_blk < num_blk; log_blk++) { | |
149 | if (map[log_blk] != 0xFFFFFFFF) { | |
150 | printk("%04X:%04X ", log_blk, map[log_blk]); | |
151 | } | |
152 | } | |
153 | printk("\n"); | |
154 | } | |
155 | ||
156 | return map; | |
157 | } | |
158 | ||
159 | static void cc_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |
160 | { | |
161 | struct cc_ftl_partition *partition; | |
162 | uint32_t *map; | |
163 | ||
164 | /* Check for NAND first, so we know we can use the "chip" pointer. */ | |
165 | if (mtd->type != MTD_NANDFLASH) | |
166 | return; | |
167 | ||
168 | /* A bad block table is expected. */ | |
169 | if (!mtd->block_isbad) | |
170 | return; | |
171 | ||
172 | /* Erase size must be a power of two. */ | |
173 | if (mtd->erasesize_shift == 0) | |
174 | return; | |
175 | ||
176 | /* Erase size must be a multiple of sector size. */ | |
177 | if ((mtd->erasesize & (SECTOR_SIZE - 1)) != 0) | |
178 | return; | |
179 | ||
180 | /* Probably this translation layer can work with different oob sizes, | |
181 | * but for now we only accept the layout it was tested with. | |
182 | */ | |
183 | if (mtd->oobsize != 128) | |
184 | return; | |
185 | ||
186 | map = cc_ftl_build_block_map(mtd); | |
187 | if (!map) | |
188 | return; | |
189 | ||
190 | partition = kzalloc(sizeof(struct cc_ftl_partition), GFP_KERNEL); | |
191 | if (!partition) | |
192 | goto err_map; | |
193 | ||
194 | partition->mbd.mtd = mtd; | |
195 | // TODO: More reasonable guess. | |
196 | partition->mbd.size = mtd->size / SECTOR_SIZE; | |
197 | partition->mbd.tr = tr; | |
198 | partition->mbd.devnum = -1; | |
199 | partition->map = map; | |
200 | ||
201 | if (add_mtd_blktrans_dev((struct mtd_blktrans_dev *)partition)) | |
202 | goto err_partition; | |
203 | ||
204 | return; | |
205 | ||
206 | err_partition: | |
207 | kfree(partition); | |
208 | err_map: | |
209 | kfree(map); | |
210 | } | |
211 | ||
212 | static void cc_ftl_remove_dev(struct mtd_blktrans_dev *dev) | |
213 | { | |
214 | struct cc_ftl_partition *partition = (struct cc_ftl_partition *)dev; | |
215 | ||
216 | if (del_mtd_blktrans_dev(dev)) | |
217 | return; | |
218 | ||
219 | kfree(partition->map); | |
220 | kfree(partition); | |
221 | } | |
222 | ||
223 | static struct mtd_blktrans_ops cc_ftl_tr = { | |
224 | .name = "ccnand", | |
225 | .major = 242, /* TODO: Register an official major number. */ | |
226 | .part_bits = 4, | |
227 | .blksize = SECTOR_SIZE, | |
228 | ||
229 | .readsect = cc_ftl_readsect, | |
230 | .add_mtd = cc_ftl_add_mtd, | |
231 | .remove_dev = cc_ftl_remove_dev, | |
232 | ||
233 | .owner = THIS_MODULE, | |
234 | }; | |
235 | ||
236 | static int __init cc_ftl_init(void) | |
237 | { | |
238 | return register_mtd_blktrans(&cc_ftl_tr); | |
239 | } | |
240 | module_init(cc_ftl_init); | |
241 | ||
242 | static void __exit cc_ftl_exit(void) | |
243 | { | |
244 | deregister_mtd_blktrans(&cc_ftl_tr); | |
245 | } | |
246 | module_exit(cc_ftl_exit); | |
247 | ||
248 | MODULE_LICENSE("GPL"); | |
249 | MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>"); | |
250 | MODULE_DESCRIPTION("Flash Translation Layer for media players " | |
251 | "using firmware from China Chip"); |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9