diff options
Diffstat (limited to 'drivers/mtd/onenand')
-rw-r--r-- | drivers/mtd/onenand/Kconfig | 38 | ||||
-rw-r--r-- | drivers/mtd/onenand/Makefile | 11 | ||||
-rw-r--r-- | drivers/mtd/onenand/generic.c | 147 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 1588 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 246 |
5 files changed, 2030 insertions, 0 deletions
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig new file mode 100644 index 000000000000..126ff6bf63d5 --- /dev/null +++ b/drivers/mtd/onenand/Kconfig | |||
@@ -0,0 +1,38 @@ | |||
1 | # | ||
2 | # linux/drivers/mtd/onenand/Kconfig | ||
3 | # | ||
4 | |||
5 | menu "OneNAND Flash Device Drivers" | ||
6 | depends on MTD != n | ||
7 | |||
8 | config MTD_ONENAND | ||
9 | tristate "OneNAND Device Support" | ||
10 | depends on MTD | ||
11 | help | ||
12 | This enables support for accessing all type of OneNAND flash | ||
13 | devices. For further information see | ||
14 | <http://www.samsung.com/Products/Semiconductor/Flash/OneNAND_TM/index.htm>. | ||
15 | |||
16 | config MTD_ONENAND_VERIFY_WRITE | ||
17 | bool "Verify OneNAND page writes" | ||
18 | depends on MTD_ONENAND | ||
19 | help | ||
20 | This adds an extra check when data is written to the flash. The | ||
21 | OneNAND flash device internally checks only bits transitioning | ||
22 | from 1 to 0. There is a rare possibility that even though the | ||
23 | device thinks the write was successful, a bit could have been | ||
24 | flipped accidentaly due to device wear or something else. | ||
25 | |||
26 | config MTD_ONENAND_GENERIC | ||
27 | tristate "OneNAND Flash device via platform device driver" | ||
28 | depends on MTD_ONENAND && ARM | ||
29 | help | ||
30 | Support for OneNAND flash via platform device driver. | ||
31 | |||
32 | config MTD_ONENAND_SYNC_READ | ||
33 | bool "OneNAND Sync. Burst Read Support" | ||
34 | depends on ARCH_OMAP | ||
35 | help | ||
36 | This enables support for Sync. Burst Read. | ||
37 | |||
38 | endmenu | ||
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile new file mode 100644 index 000000000000..269cfe467345 --- /dev/null +++ b/drivers/mtd/onenand/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # | ||
2 | # Makefile for the OneNAND MTD | ||
3 | # | ||
4 | |||
5 | # Core functionality. | ||
6 | obj-$(CONFIG_MTD_ONENAND) += onenand.o | ||
7 | |||
8 | # Board specific. | ||
9 | obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o | ||
10 | |||
11 | onenand-objs = onenand_base.o onenand_bbt.o | ||
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c new file mode 100644 index 000000000000..48cce431f89f --- /dev/null +++ b/drivers/mtd/onenand/generic.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * linux/drivers/mtd/onenand/generic.c | ||
3 | * | ||
4 | * Copyright (c) 2005 Samsung Electronics | ||
5 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * Overview: | ||
12 | * This is a device driver for the OneNAND flash for generic boards. | ||
13 | */ | ||
14 | |||
15 | #include <linux/device.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/mtd/mtd.h> | ||
19 | #include <linux/mtd/onenand.h> | ||
20 | #include <linux/mtd/partitions.h> | ||
21 | |||
22 | #include <asm/io.h> | ||
23 | #include <asm/mach/flash.h> | ||
24 | |||
25 | #define DRIVER_NAME "onenand" | ||
26 | |||
27 | |||
28 | #ifdef CONFIG_MTD_PARTITIONS | ||
29 | static const char *part_probes[] = { "cmdlinepart", NULL, }; | ||
30 | #endif | ||
31 | |||
32 | struct onenand_info { | ||
33 | struct mtd_info mtd; | ||
34 | struct mtd_partition *parts; | ||
35 | struct onenand_chip onenand; | ||
36 | }; | ||
37 | |||
38 | static int __devinit generic_onenand_probe(struct device *dev) | ||
39 | { | ||
40 | struct onenand_info *info; | ||
41 | struct platform_device *pdev = to_platform_device(dev); | ||
42 | struct onenand_platform_data *pdata = pdev->dev.platform_data; | ||
43 | struct resource *res = pdev->resource; | ||
44 | unsigned long size = res->end - res->start + 1; | ||
45 | int err; | ||
46 | |||
47 | info = kmalloc(sizeof(struct onenand_info), GFP_KERNEL); | ||
48 | if (!info) | ||
49 | return -ENOMEM; | ||
50 | |||
51 | memset(info, 0, sizeof(struct onenand_info)); | ||
52 | |||
53 | if (!request_mem_region(res->start, size, dev->driver->name)) { | ||
54 | err = -EBUSY; | ||
55 | goto out_free_info; | ||
56 | } | ||
57 | |||
58 | info->onenand.base = ioremap(res->start, size); | ||
59 | if (!info->onenand.base) { | ||
60 | err = -ENOMEM; | ||
61 | goto out_release_mem_region; | ||
62 | } | ||
63 | |||
64 | info->onenand.mmcontrol = pdata->mmcontrol; | ||
65 | |||
66 | info->mtd.name = pdev->dev.bus_id; | ||
67 | info->mtd.priv = &info->onenand; | ||
68 | info->mtd.owner = THIS_MODULE; | ||
69 | |||
70 | if (onenand_scan(&info->mtd, 1)) { | ||
71 | err = -ENXIO; | ||
72 | goto out_iounmap; | ||
73 | } | ||
74 | |||
75 | #ifdef CONFIG_MTD_PARTITIONS | ||
76 | err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); | ||
77 | if (err > 0) | ||
78 | add_mtd_partitions(&info->mtd, info->parts, err); | ||
79 | else if (err < 0 && pdata->parts) | ||
80 | add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); | ||
81 | else | ||
82 | #endif | ||
83 | err = add_mtd_device(&info->mtd); | ||
84 | |||
85 | dev_set_drvdata(&pdev->dev, info); | ||
86 | |||
87 | return 0; | ||
88 | |||
89 | out_iounmap: | ||
90 | iounmap(info->onenand.base); | ||
91 | out_release_mem_region: | ||
92 | release_mem_region(res->start, size); | ||
93 | out_free_info: | ||
94 | kfree(info); | ||
95 | |||
96 | return err; | ||
97 | } | ||
98 | |||
99 | static int __devexit generic_onenand_remove(struct device *dev) | ||
100 | { | ||
101 | struct platform_device *pdev = to_platform_device(dev); | ||
102 | struct onenand_info *info = dev_get_drvdata(&pdev->dev); | ||
103 | struct resource *res = pdev->resource; | ||
104 | unsigned long size = res->end - res->start + 1; | ||
105 | |||
106 | dev_set_drvdata(&pdev->dev, NULL); | ||
107 | |||
108 | if (info) { | ||
109 | if (info->parts) | ||
110 | del_mtd_partitions(&info->mtd); | ||
111 | else | ||
112 | del_mtd_device(&info->mtd); | ||
113 | |||
114 | onenand_release(&info->mtd); | ||
115 | release_mem_region(res->start, size); | ||
116 | iounmap(info->onenand.base); | ||
117 | kfree(info); | ||
118 | } | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static struct device_driver generic_onenand_driver = { | ||
124 | .name = DRIVER_NAME, | ||
125 | .bus = &platform_bus_type, | ||
126 | .probe = generic_onenand_probe, | ||
127 | .remove = __devexit_p(generic_onenand_remove), | ||
128 | }; | ||
129 | |||
130 | MODULE_ALIAS(DRIVER_NAME); | ||
131 | |||
132 | static int __init generic_onenand_init(void) | ||
133 | { | ||
134 | return driver_register(&generic_onenand_driver); | ||
135 | } | ||
136 | |||
137 | static void __exit generic_onenand_exit(void) | ||
138 | { | ||
139 | driver_unregister(&generic_onenand_driver); | ||
140 | } | ||
141 | |||
142 | module_init(generic_onenand_init); | ||
143 | module_exit(generic_onenand_exit); | ||
144 | |||
145 | MODULE_LICENSE("GPL"); | ||
146 | MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); | ||
147 | MODULE_DESCRIPTION("Glue layer for OneNAND flash on generic boards"); | ||
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c new file mode 100644 index 000000000000..cc38fa0d45c6 --- /dev/null +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -0,0 +1,1588 @@ | |||
1 | /* | ||
2 | * linux/drivers/mtd/onenand/onenand_base.c | ||
3 | * | ||
4 | * Copyright (C) 2005 Samsung Electronics | ||
5 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/mtd/mtd.h> | ||
16 | #include <linux/mtd/onenand.h> | ||
17 | #include <linux/mtd/partitions.h> | ||
18 | |||
19 | #include <asm/io.h> | ||
20 | |||
21 | /** | ||
22 | * onenand_oob_64 - oob info for large (2KB) page | ||
23 | */ | ||
24 | static struct nand_oobinfo onenand_oob_64 = { | ||
25 | .useecc = MTD_NANDECC_AUTOPLACE, | ||
26 | .eccbytes = 20, | ||
27 | .eccpos = { | ||
28 | 8, 9, 10, 11, 12, | ||
29 | 24, 25, 26, 27, 28, | ||
30 | 40, 41, 42, 43, 44, | ||
31 | 56, 57, 58, 59, 60, | ||
32 | }, | ||
33 | .oobfree = { | ||
34 | {2, 3}, {14, 2}, {18, 3}, {30, 2}, | ||
35 | {24, 3}, {46, 2}, {40, 3}, {62, 2} } | ||
36 | }; | ||
37 | |||
38 | /** | ||
39 | * onenand_oob_32 - oob info for middle (1KB) page | ||
40 | */ | ||
41 | static struct nand_oobinfo onenand_oob_32 = { | ||
42 | .useecc = MTD_NANDECC_AUTOPLACE, | ||
43 | .eccbytes = 10, | ||
44 | .eccpos = { | ||
45 | 8, 9, 10, 11, 12, | ||
46 | 24, 25, 26, 27, 28, | ||
47 | }, | ||
48 | .oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} } | ||
49 | }; | ||
50 | |||
51 | static const unsigned char ffchars[] = { | ||
52 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
53 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ | ||
54 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
55 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ | ||
56 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
57 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ | ||
58 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
59 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ | ||
60 | }; | ||
61 | |||
62 | /** | ||
63 | * onenand_readw - [OneNAND Interface] Read OneNAND register | ||
64 | * @param addr address to read | ||
65 | * | ||
66 | * Read OneNAND register | ||
67 | */ | ||
68 | static unsigned short onenand_readw(void __iomem *addr) | ||
69 | { | ||
70 | return readw(addr); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * onenand_writew - [OneNAND Interface] Write OneNAND register with value | ||
75 | * @param value value to write | ||
76 | * @param addr address to write | ||
77 | * | ||
78 | * Write OneNAND register with value | ||
79 | */ | ||
80 | static void onenand_writew(unsigned short value, void __iomem *addr) | ||
81 | { | ||
82 | writew(value, addr); | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * onenand_block_address - [DEFAULT] Get block address | ||
87 | * @param this onenand chip data structure | ||
88 | * @param block the block | ||
89 | * @return translated block address if DDP, otherwise same | ||
90 | * | ||
91 | * Setup Start Address 1 Register (F100h) | ||
92 | */ | ||
93 | static int onenand_block_address(struct onenand_chip *this, int block) | ||
94 | { | ||
95 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | ||
96 | /* Device Flash Core select, NAND Flash Block Address */ | ||
97 | int dfs = 0; | ||
98 | |||
99 | if (block & this->density_mask) | ||
100 | dfs = 1; | ||
101 | |||
102 | return (dfs << ONENAND_DDP_SHIFT) | | ||
103 | (block & (this->density_mask - 1)); | ||
104 | } | ||
105 | |||
106 | return block; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * onenand_bufferram_address - [DEFAULT] Get bufferram address | ||
111 | * @param this onenand chip data structure | ||
112 | * @param block the block | ||
113 | * @return set DBS value if DDP, otherwise 0 | ||
114 | * | ||
115 | * Setup Start Address 2 Register (F101h) for DDP | ||
116 | */ | ||
117 | static int onenand_bufferram_address(struct onenand_chip *this, int block) | ||
118 | { | ||
119 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | ||
120 | /* Device BufferRAM Select */ | ||
121 | int dbs = 0; | ||
122 | |||
123 | if (block & this->density_mask) | ||
124 | dbs = 1; | ||
125 | |||
126 | return (dbs << ONENAND_DDP_SHIFT); | ||
127 | } | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * onenand_page_address - [DEFAULT] Get page address | ||
134 | * @param page the page address | ||
135 | * @param sector the sector address | ||
136 | * @return combined page and sector address | ||
137 | * | ||
138 | * Setup Start Address 8 Register (F107h) | ||
139 | */ | ||
140 | static int onenand_page_address(int page, int sector) | ||
141 | { | ||
142 | /* Flash Page Address, Flash Sector Address */ | ||
143 | int fpa, fsa; | ||
144 | |||
145 | fpa = page & ONENAND_FPA_MASK; | ||
146 | fsa = sector & ONENAND_FSA_MASK; | ||
147 | |||
148 | return ((fpa << ONENAND_FPA_SHIFT) | fsa); | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * onenand_buffer_address - [DEFAULT] Get buffer address | ||
153 | * @param dataram1 DataRAM index | ||
154 | * @param sectors the sector address | ||
155 | * @param count the number of sectors | ||
156 | * @return the start buffer value | ||
157 | * | ||
158 | * Setup Start Buffer Register (F200h) | ||
159 | */ | ||
160 | static int onenand_buffer_address(int dataram1, int sectors, int count) | ||
161 | { | ||
162 | int bsa, bsc; | ||
163 | |||
164 | /* BufferRAM Sector Address */ | ||
165 | bsa = sectors & ONENAND_BSA_MASK; | ||
166 | |||
167 | if (dataram1) | ||
168 | bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ | ||
169 | else | ||
170 | bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ | ||
171 | |||
172 | /* BufferRAM Sector Count */ | ||
173 | bsc = count & ONENAND_BSC_MASK; | ||
174 | |||
175 | return ((bsa << ONENAND_BSA_SHIFT) | bsc); | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * onenand_command - [DEFAULT] Send command to OneNAND device | ||
180 | * @param mtd MTD device structure | ||
181 | * @param cmd the command to be sent | ||
182 | * @param addr offset to read from or write to | ||
183 | * @param len number of bytes to read or write | ||
184 | * | ||
185 | * Send command to OneNAND device. This function is used for middle/large page | ||
186 | * devices (1KB/2KB Bytes per page) | ||
187 | */ | ||
188 | static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) | ||
189 | { | ||
190 | struct onenand_chip *this = mtd->priv; | ||
191 | int value, readcmd = 0; | ||
192 | int block, page; | ||
193 | /* Now we use page size operation */ | ||
194 | int sectors = 4, count = 4; | ||
195 | |||
196 | /* Address translation */ | ||
197 | switch (cmd) { | ||
198 | case ONENAND_CMD_UNLOCK: | ||
199 | case ONENAND_CMD_LOCK: | ||
200 | case ONENAND_CMD_LOCK_TIGHT: | ||
201 | block = -1; | ||
202 | page = -1; | ||
203 | break; | ||
204 | |||
205 | case ONENAND_CMD_ERASE: | ||
206 | case ONENAND_CMD_BUFFERRAM: | ||
207 | block = (int) (addr >> this->erase_shift); | ||
208 | page = -1; | ||
209 | break; | ||
210 | |||
211 | default: | ||
212 | block = (int) (addr >> this->erase_shift); | ||
213 | page = (int) (addr >> this->page_shift); | ||
214 | page &= this->page_mask; | ||
215 | break; | ||
216 | } | ||
217 | |||
218 | /* NOTE: The setting order of the registers is very important! */ | ||
219 | if (cmd == ONENAND_CMD_BUFFERRAM) { | ||
220 | /* Select DataRAM for DDP */ | ||
221 | value = onenand_bufferram_address(this, block); | ||
222 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | ||
223 | |||
224 | /* Switch to the next data buffer */ | ||
225 | ONENAND_SET_NEXT_BUFFERRAM(this); | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | if (block != -1) { | ||
231 | /* Write 'DFS, FBA' of Flash */ | ||
232 | value = onenand_block_address(this, block); | ||
233 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); | ||
234 | } | ||
235 | |||
236 | if (page != -1) { | ||
237 | int dataram; | ||
238 | |||
239 | switch (cmd) { | ||
240 | case ONENAND_CMD_READ: | ||
241 | case ONENAND_CMD_READOOB: | ||
242 | dataram = ONENAND_SET_NEXT_BUFFERRAM(this); | ||
243 | readcmd = 1; | ||
244 | break; | ||
245 | |||
246 | default: | ||
247 | dataram = ONENAND_CURRENT_BUFFERRAM(this); | ||
248 | break; | ||
249 | } | ||
250 | |||
251 | /* Write 'FPA, FSA' of Flash */ | ||
252 | value = onenand_page_address(page, sectors); | ||
253 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS8); | ||
254 | |||
255 | /* Write 'BSA, BSC' of DataRAM */ | ||
256 | value = onenand_buffer_address(dataram, sectors, count); | ||
257 | this->write_word(value, this->base + ONENAND_REG_START_BUFFER); | ||
258 | |||
259 | if (readcmd) { | ||
260 | /* Select DataRAM for DDP */ | ||
261 | value = onenand_bufferram_address(this, block); | ||
262 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | ||
263 | } | ||
264 | } | ||
265 | |||
266 | /* Interrupt clear */ | ||
267 | this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); | ||
268 | |||
269 | /* Write command */ | ||
270 | this->write_word(cmd, this->base + ONENAND_REG_COMMAND); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * onenand_wait - [DEFAULT] wait until the command is done | ||
277 | * @param mtd MTD device structure | ||
278 | * @param state state to select the max. timeout value | ||
279 | * | ||
280 | * Wait for command done. This applies to all OneNAND command | ||
281 | * Read can take up to 30us, erase up to 2ms and program up to 350us | ||
282 | * according to general OneNAND specs | ||
283 | */ | ||
284 | static int onenand_wait(struct mtd_info *mtd, int state) | ||
285 | { | ||
286 | struct onenand_chip * this = mtd->priv; | ||
287 | unsigned long timeout; | ||
288 | unsigned int flags = ONENAND_INT_MASTER; | ||
289 | unsigned int interrupt = 0; | ||
290 | unsigned int ctrl, ecc; | ||
291 | |||
292 | /* The 20 msec is enough */ | ||
293 | timeout = jiffies + msecs_to_jiffies(20); | ||
294 | while (time_before(jiffies, timeout)) { | ||
295 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
296 | |||
297 | if (interrupt & flags) | ||
298 | break; | ||
299 | |||
300 | if (state != FL_READING) | ||
301 | cond_resched(); | ||
302 | } | ||
303 | /* To get correct interrupt status in timeout case */ | ||
304 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
305 | |||
306 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | ||
307 | |||
308 | if (ctrl & ONENAND_CTRL_ERROR) { | ||
309 | /* It maybe occur at initial bad block */ | ||
310 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); | ||
311 | /* Clear other interrupt bits for preventing ECC error */ | ||
312 | interrupt &= ONENAND_INT_MASTER; | ||
313 | } | ||
314 | |||
315 | if (ctrl & ONENAND_CTRL_LOCK) { | ||
316 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x\n", ctrl); | ||
317 | return -EACCES; | ||
318 | } | ||
319 | |||
320 | if (interrupt & ONENAND_INT_READ) { | ||
321 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | ||
322 | if (ecc & ONENAND_ECC_2BIT_ALL) { | ||
323 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); | ||
324 | return -EBADMSG; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * onenand_bufferram_offset - [DEFAULT] BufferRAM offset | ||
333 | * @param mtd MTD data structure | ||
334 | * @param area BufferRAM area | ||
335 | * @return offset given area | ||
336 | * | ||
337 | * Return BufferRAM offset given area | ||
338 | */ | ||
339 | static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) | ||
340 | { | ||
341 | struct onenand_chip *this = mtd->priv; | ||
342 | |||
343 | if (ONENAND_CURRENT_BUFFERRAM(this)) { | ||
344 | if (area == ONENAND_DATARAM) | ||
345 | return mtd->oobblock; | ||
346 | if (area == ONENAND_SPARERAM) | ||
347 | return mtd->oobsize; | ||
348 | } | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | /** | ||
354 | * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area | ||
355 | * @param mtd MTD data structure | ||
356 | * @param area BufferRAM area | ||
357 | * @param buffer the databuffer to put/get data | ||
358 | * @param offset offset to read from or write to | ||
359 | * @param count number of bytes to read/write | ||
360 | * | ||
361 | * Read the BufferRAM area | ||
362 | */ | ||
363 | static int onenand_read_bufferram(struct mtd_info *mtd, int area, | ||
364 | unsigned char *buffer, int offset, size_t count) | ||
365 | { | ||
366 | struct onenand_chip *this = mtd->priv; | ||
367 | void __iomem *bufferram; | ||
368 | |||
369 | bufferram = this->base + area; | ||
370 | |||
371 | bufferram += onenand_bufferram_offset(mtd, area); | ||
372 | |||
373 | memcpy(buffer, bufferram + offset, count); | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | /** | ||
379 | * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode | ||
380 | * @param mtd MTD data structure | ||
381 | * @param area BufferRAM area | ||
382 | * @param buffer the databuffer to put/get data | ||
383 | * @param offset offset to read from or write to | ||
384 | * @param count number of bytes to read/write | ||
385 | * | ||
386 | * Read the BufferRAM area with Sync. Burst Mode | ||
387 | */ | ||
388 | static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area, | ||
389 | unsigned char *buffer, int offset, size_t count) | ||
390 | { | ||
391 | struct onenand_chip *this = mtd->priv; | ||
392 | void __iomem *bufferram; | ||
393 | |||
394 | bufferram = this->base + area; | ||
395 | |||
396 | bufferram += onenand_bufferram_offset(mtd, area); | ||
397 | |||
398 | this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); | ||
399 | |||
400 | memcpy(buffer, bufferram + offset, count); | ||
401 | |||
402 | this->mmcontrol(mtd, 0); | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | /** | ||
408 | * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area | ||
409 | * @param mtd MTD data structure | ||
410 | * @param area BufferRAM area | ||
411 | * @param buffer the databuffer to put/get data | ||
412 | * @param offset offset to read from or write to | ||
413 | * @param count number of bytes to read/write | ||
414 | * | ||
415 | * Write the BufferRAM area | ||
416 | */ | ||
417 | static int onenand_write_bufferram(struct mtd_info *mtd, int area, | ||
418 | const unsigned char *buffer, int offset, size_t count) | ||
419 | { | ||
420 | struct onenand_chip *this = mtd->priv; | ||
421 | void __iomem *bufferram; | ||
422 | |||
423 | bufferram = this->base + area; | ||
424 | |||
425 | bufferram += onenand_bufferram_offset(mtd, area); | ||
426 | |||
427 | memcpy(bufferram + offset, buffer, count); | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | /** | ||
433 | * onenand_check_bufferram - [GENERIC] Check BufferRAM information | ||
434 | * @param mtd MTD data structure | ||
435 | * @param addr address to check | ||
436 | * @return 1 if there are valid data, otherwise 0 | ||
437 | * | ||
438 | * Check bufferram if there is data we required | ||
439 | */ | ||
440 | static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | ||
441 | { | ||
442 | struct onenand_chip *this = mtd->priv; | ||
443 | int block, page; | ||
444 | int i; | ||
445 | |||
446 | block = (int) (addr >> this->erase_shift); | ||
447 | page = (int) (addr >> this->page_shift); | ||
448 | page &= this->page_mask; | ||
449 | |||
450 | i = ONENAND_CURRENT_BUFFERRAM(this); | ||
451 | |||
452 | /* Is there valid data? */ | ||
453 | if (this->bufferram[i].block == block && | ||
454 | this->bufferram[i].page == page && | ||
455 | this->bufferram[i].valid) | ||
456 | return 1; | ||
457 | |||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | /** | ||
462 | * onenand_update_bufferram - [GENERIC] Update BufferRAM information | ||
463 | * @param mtd MTD data structure | ||
464 | * @param addr address to update | ||
465 | * @param valid valid flag | ||
466 | * | ||
467 | * Update BufferRAM information | ||
468 | */ | ||
469 | static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, | ||
470 | int valid) | ||
471 | { | ||
472 | struct onenand_chip *this = mtd->priv; | ||
473 | int block, page; | ||
474 | int i; | ||
475 | |||
476 | block = (int) (addr >> this->erase_shift); | ||
477 | page = (int) (addr >> this->page_shift); | ||
478 | page &= this->page_mask; | ||
479 | |||
480 | /* Invalidate BufferRAM */ | ||
481 | for (i = 0; i < MAX_BUFFERRAM; i++) { | ||
482 | if (this->bufferram[i].block == block && | ||
483 | this->bufferram[i].page == page) | ||
484 | this->bufferram[i].valid = 0; | ||
485 | } | ||
486 | |||
487 | /* Update BufferRAM */ | ||
488 | i = ONENAND_CURRENT_BUFFERRAM(this); | ||
489 | this->bufferram[i].block = block; | ||
490 | this->bufferram[i].page = page; | ||
491 | this->bufferram[i].valid = valid; | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | /** | ||
497 | * onenand_get_device - [GENERIC] Get chip for selected access | ||
498 | * @param mtd MTD device structure | ||
499 | * @param new_state the state which is requested | ||
500 | * | ||
501 | * Get the device and lock it for exclusive access | ||
502 | */ | ||
503 | static int onenand_get_device(struct mtd_info *mtd, int new_state) | ||
504 | { | ||
505 | struct onenand_chip *this = mtd->priv; | ||
506 | DECLARE_WAITQUEUE(wait, current); | ||
507 | |||
508 | /* | ||
509 | * Grab the lock and see if the device is available | ||
510 | */ | ||
511 | while (1) { | ||
512 | spin_lock(&this->chip_lock); | ||
513 | if (this->state == FL_READY) { | ||
514 | this->state = new_state; | ||
515 | spin_unlock(&this->chip_lock); | ||
516 | break; | ||
517 | } | ||
518 | if (new_state == FL_PM_SUSPENDED) { | ||
519 | spin_unlock(&this->chip_lock); | ||
520 | return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; | ||
521 | } | ||
522 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
523 | add_wait_queue(&this->wq, &wait); | ||
524 | spin_unlock(&this->chip_lock); | ||
525 | schedule(); | ||
526 | remove_wait_queue(&this->wq, &wait); | ||
527 | } | ||
528 | |||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * onenand_release_device - [GENERIC] release chip | ||
534 | * @param mtd MTD device structure | ||
535 | * | ||
536 | * Deselect, release chip lock and wake up anyone waiting on the device | ||
537 | */ | ||
538 | static void onenand_release_device(struct mtd_info *mtd) | ||
539 | { | ||
540 | struct onenand_chip *this = mtd->priv; | ||
541 | |||
542 | /* Release the chip */ | ||
543 | spin_lock(&this->chip_lock); | ||
544 | this->state = FL_READY; | ||
545 | wake_up(&this->wq); | ||
546 | spin_unlock(&this->chip_lock); | ||
547 | } | ||
548 | |||
549 | /** | ||
550 | * onenand_read_ecc - [MTD Interface] Read data with ECC | ||
551 | * @param mtd MTD device structure | ||
552 | * @param from offset to read from | ||
553 | * @param len number of bytes to read | ||
554 | * @param retlen pointer to variable to store the number of read bytes | ||
555 | * @param buf the databuffer to put data | ||
556 | * @param oob_buf filesystem supplied oob data buffer | ||
557 | * @param oobsel oob selection structure | ||
558 | * | ||
559 | * OneNAND read with ECC | ||
560 | */ | ||
561 | static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | ||
562 | size_t *retlen, u_char *buf, | ||
563 | u_char *oob_buf, struct nand_oobinfo *oobsel) | ||
564 | { | ||
565 | struct onenand_chip *this = mtd->priv; | ||
566 | int read = 0, column; | ||
567 | int thislen; | ||
568 | int ret = 0; | ||
569 | |||
570 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | ||
571 | |||
572 | /* Do not allow reads past end of device */ | ||
573 | if ((from + len) > mtd->size) { | ||
574 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: Attempt read beyond end of device\n"); | ||
575 | *retlen = 0; | ||
576 | return -EINVAL; | ||
577 | } | ||
578 | |||
579 | /* Grab the lock and see if the device is available */ | ||
580 | onenand_get_device(mtd, FL_READING); | ||
581 | |||
582 | /* TODO handling oob */ | ||
583 | |||
584 | while (read < len) { | ||
585 | thislen = min_t(int, mtd->oobblock, len - read); | ||
586 | |||
587 | column = from & (mtd->oobblock - 1); | ||
588 | if (column + thislen > mtd->oobblock) | ||
589 | thislen = mtd->oobblock - column; | ||
590 | |||
591 | if (!onenand_check_bufferram(mtd, from)) { | ||
592 | this->command(mtd, ONENAND_CMD_READ, from, mtd->oobblock); | ||
593 | |||
594 | ret = this->wait(mtd, FL_READING); | ||
595 | /* First copy data and check return value for ECC handling */ | ||
596 | onenand_update_bufferram(mtd, from, 1); | ||
597 | } | ||
598 | |||
599 | this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); | ||
600 | |||
601 | read += thislen; | ||
602 | |||
603 | if (read == len) | ||
604 | break; | ||
605 | |||
606 | if (ret) { | ||
607 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: read failed = %d\n", ret); | ||
608 | goto out; | ||
609 | } | ||
610 | |||
611 | from += thislen; | ||
612 | buf += thislen; | ||
613 | } | ||
614 | |||
615 | out: | ||
616 | /* Deselect and wake up anyone waiting on the device */ | ||
617 | onenand_release_device(mtd); | ||
618 | |||
619 | /* | ||
620 | * Return success, if no ECC failures, else -EBADMSG | ||
621 | * fs driver will take care of that, because | ||
622 | * retlen == desired len and result == -EBADMSG | ||
623 | */ | ||
624 | *retlen = read; | ||
625 | return ret; | ||
626 | } | ||
627 | |||
628 | /** | ||
629 | * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc | ||
630 | * @param mtd MTD device structure | ||
631 | * @param from offset to read from | ||
632 | * @param len number of bytes to read | ||
633 | * @param retlen pointer to variable to store the number of read bytes | ||
634 | * @param buf the databuffer to put data | ||
635 | * | ||
636 | * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL | ||
637 | */ | ||
638 | static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
639 | size_t *retlen, u_char *buf) | ||
640 | { | ||
641 | return onenand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); | ||
642 | } | ||
643 | |||
644 | /** | ||
645 | * onenand_read_oob - [MTD Interface] OneNAND read out-of-band | ||
646 | * @param mtd MTD device structure | ||
647 | * @param from offset to read from | ||
648 | * @param len number of bytes to read | ||
649 | * @param retlen pointer to variable to store the number of read bytes | ||
650 | * @param buf the databuffer to put data | ||
651 | * | ||
652 | * OneNAND read out-of-band data from the spare area | ||
653 | */ | ||
654 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | ||
655 | size_t *retlen, u_char *buf) | ||
656 | { | ||
657 | struct onenand_chip *this = mtd->priv; | ||
658 | int read = 0, thislen, column; | ||
659 | int ret = 0; | ||
660 | |||
661 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | ||
662 | |||
663 | /* Initialize return length value */ | ||
664 | *retlen = 0; | ||
665 | |||
666 | /* Do not allow reads past end of device */ | ||
667 | if (unlikely((from + len) > mtd->size)) { | ||
668 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); | ||
669 | return -EINVAL; | ||
670 | } | ||
671 | |||
672 | /* Grab the lock and see if the device is available */ | ||
673 | onenand_get_device(mtd, FL_READING); | ||
674 | |||
675 | column = from & (mtd->oobsize - 1); | ||
676 | |||
677 | while (read < len) { | ||
678 | thislen = mtd->oobsize - column; | ||
679 | thislen = min_t(int, thislen, len); | ||
680 | |||
681 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | ||
682 | |||
683 | onenand_update_bufferram(mtd, from, 0); | ||
684 | |||
685 | ret = this->wait(mtd, FL_READING); | ||
686 | /* First copy data and check return value for ECC handling */ | ||
687 | |||
688 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
689 | |||
690 | read += thislen; | ||
691 | |||
692 | if (read == len) | ||
693 | break; | ||
694 | |||
695 | if (ret) { | ||
696 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = %d\n", ret); | ||
697 | goto out; | ||
698 | } | ||
699 | |||
700 | buf += thislen; | ||
701 | |||
702 | /* Read more? */ | ||
703 | if (read < len) { | ||
704 | /* Page size */ | ||
705 | from += mtd->oobblock; | ||
706 | column = 0; | ||
707 | } | ||
708 | } | ||
709 | |||
710 | out: | ||
711 | /* Deselect and wake up anyone waiting on the device */ | ||
712 | onenand_release_device(mtd); | ||
713 | |||
714 | *retlen = read; | ||
715 | return ret; | ||
716 | } | ||
717 | |||
718 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE | ||
719 | /** | ||
720 | * onenand_verify_page - [GENERIC] verify the chip contents after a write | ||
721 | * @param mtd MTD device structure | ||
722 | * @param buf the databuffer to verify | ||
723 | * | ||
724 | * Check DataRAM area directly | ||
725 | */ | ||
726 | static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) | ||
727 | { | ||
728 | struct onenand_chip *this = mtd->priv; | ||
729 | void __iomem *dataram0, *dataram1; | ||
730 | int ret = 0; | ||
731 | |||
732 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock); | ||
733 | |||
734 | ret = this->wait(mtd, FL_READING); | ||
735 | if (ret) | ||
736 | return ret; | ||
737 | |||
738 | onenand_update_bufferram(mtd, addr, 1); | ||
739 | |||
740 | /* Check, if the two dataram areas are same */ | ||
741 | dataram0 = this->base + ONENAND_DATARAM; | ||
742 | dataram1 = dataram0 + mtd->oobblock; | ||
743 | |||
744 | if (memcmp(dataram0, dataram1, mtd->oobblock)) | ||
745 | return -EBADMSG; | ||
746 | |||
747 | return 0; | ||
748 | } | ||
749 | #else | ||
750 | #define onenand_verify_page(...) (0) | ||
751 | #endif | ||
752 | |||
753 | #define NOTALIGNED(x) ((x & (mtd->oobblock - 1)) != 0) | ||
754 | |||
755 | /** | ||
756 | * onenand_write_ecc - [MTD Interface] OneNAND write with ECC | ||
757 | * @param mtd MTD device structure | ||
758 | * @param to offset to write to | ||
759 | * @param len number of bytes to write | ||
760 | * @param retlen pointer to variable to store the number of written bytes | ||
761 | * @param buf the data to write | ||
762 | * @param eccbuf filesystem supplied oob data buffer | ||
763 | * @param oobsel oob selection structure | ||
764 | * | ||
765 | * OneNAND write with ECC | ||
766 | */ | ||
767 | static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, | ||
768 | size_t *retlen, const u_char *buf, | ||
769 | u_char *eccbuf, struct nand_oobinfo *oobsel) | ||
770 | { | ||
771 | struct onenand_chip *this = mtd->priv; | ||
772 | int written = 0; | ||
773 | int ret = 0; | ||
774 | |||
775 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | ||
776 | |||
777 | /* Initialize retlen, in case of early exit */ | ||
778 | *retlen = 0; | ||
779 | |||
780 | /* Do not allow writes past end of device */ | ||
781 | if (unlikely((to + len) > mtd->size)) { | ||
782 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt write to past end of device\n"); | ||
783 | return -EINVAL; | ||
784 | } | ||
785 | |||
786 | /* Reject writes, which are not page aligned */ | ||
787 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { | ||
788 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt to write not page aligned data\n"); | ||
789 | return -EINVAL; | ||
790 | } | ||
791 | |||
792 | /* Grab the lock and see if the device is available */ | ||
793 | onenand_get_device(mtd, FL_WRITING); | ||
794 | |||
795 | /* Loop until all data write */ | ||
796 | while (written < len) { | ||
797 | int thislen = min_t(int, mtd->oobblock, len - written); | ||
798 | |||
799 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); | ||
800 | |||
801 | this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen); | ||
802 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | ||
803 | |||
804 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); | ||
805 | |||
806 | onenand_update_bufferram(mtd, to, 1); | ||
807 | |||
808 | ret = this->wait(mtd, FL_WRITING); | ||
809 | if (ret) { | ||
810 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: write filaed %d\n", ret); | ||
811 | goto out; | ||
812 | } | ||
813 | |||
814 | written += thislen; | ||
815 | |||
816 | /* Only check verify write turn on */ | ||
817 | ret = onenand_verify_page(mtd, (u_char *) buf, to); | ||
818 | if (ret) { | ||
819 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: verify failed %d\n", ret); | ||
820 | goto out; | ||
821 | } | ||
822 | |||
823 | if (written == len) | ||
824 | break; | ||
825 | |||
826 | to += thislen; | ||
827 | buf += thislen; | ||
828 | } | ||
829 | |||
830 | out: | ||
831 | /* Deselect and wake up anyone waiting on the device */ | ||
832 | onenand_release_device(mtd); | ||
833 | |||
834 | *retlen = written; | ||
835 | |||
836 | return ret; | ||
837 | } | ||
838 | |||
839 | /** | ||
840 | * onenand_write - [MTD Interface] compability function for onenand_write_ecc | ||
841 | * @param mtd MTD device structure | ||
842 | * @param to offset to write to | ||
843 | * @param len number of bytes to write | ||
844 | * @param retlen pointer to variable to store the number of written bytes | ||
845 | * @param buf the data to write | ||
846 | * | ||
847 | * This function simply calls onenand_write_ecc | ||
848 | * with oob buffer and oobsel = NULL | ||
849 | */ | ||
850 | static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
851 | size_t *retlen, const u_char *buf) | ||
852 | { | ||
853 | return onenand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL); | ||
854 | } | ||
855 | |||
856 | /** | ||
857 | * onenand_write_oob - [MTD Interface] OneNAND write out-of-band | ||
858 | * @param mtd MTD device structure | ||
859 | * @param to offset to write to | ||
860 | * @param len number of bytes to write | ||
861 | * @param retlen pointer to variable to store the number of written bytes | ||
862 | * @param buf the data to write | ||
863 | * | ||
864 | * OneNAND write out-of-band | ||
865 | */ | ||
866 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | ||
867 | size_t *retlen, const u_char *buf) | ||
868 | { | ||
869 | struct onenand_chip *this = mtd->priv; | ||
870 | int column, status; | ||
871 | int written = 0; | ||
872 | |||
873 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | ||
874 | |||
875 | /* Initialize retlen, in case of early exit */ | ||
876 | *retlen = 0; | ||
877 | |||
878 | /* Do not allow writes past end of device */ | ||
879 | if (unlikely((to + len) > mtd->size)) { | ||
880 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); | ||
881 | return -EINVAL; | ||
882 | } | ||
883 | |||
884 | /* Grab the lock and see if the device is available */ | ||
885 | onenand_get_device(mtd, FL_WRITING); | ||
886 | |||
887 | /* Loop until all data write */ | ||
888 | while (written < len) { | ||
889 | int thislen = min_t(int, mtd->oobsize, len - written); | ||
890 | |||
891 | column = to & (mtd->oobsize - 1); | ||
892 | |||
893 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); | ||
894 | |||
895 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | ||
896 | this->write_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
897 | |||
898 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | ||
899 | |||
900 | onenand_update_bufferram(mtd, to, 0); | ||
901 | |||
902 | status = this->wait(mtd, FL_WRITING); | ||
903 | if (status) | ||
904 | goto out; | ||
905 | |||
906 | written += thislen; | ||
907 | |||
908 | if (written == len) | ||
909 | break; | ||
910 | |||
911 | to += thislen; | ||
912 | buf += thislen; | ||
913 | } | ||
914 | |||
915 | out: | ||
916 | /* Deselect and wake up anyone waiting on the device */ | ||
917 | onenand_release_device(mtd); | ||
918 | |||
919 | *retlen = written; | ||
920 | |||
921 | return 0; | ||
922 | } | ||
923 | |||
924 | /** | ||
925 | * onenand_writev_ecc - [MTD Interface] write with iovec with ecc | ||
926 | * @param mtd MTD device structure | ||
927 | * @param vecs the iovectors to write | ||
928 | * @param count number of vectors | ||
929 | * @param to offset to write to | ||
930 | * @param retlen pointer to variable to store the number of written bytes | ||
931 | * @param eccbuf filesystem supplied oob data buffer | ||
932 | * @param oobsel oob selection structure | ||
933 | * | ||
934 | * OneNAND write with iovec with ecc | ||
935 | */ | ||
936 | static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, | ||
937 | unsigned long count, loff_t to, size_t *retlen, | ||
938 | u_char *eccbuf, struct nand_oobinfo *oobsel) | ||
939 | { | ||
940 | struct onenand_chip *this = mtd->priv; | ||
941 | unsigned char buffer[MAX_ONENAND_PAGESIZE], *pbuf; | ||
942 | size_t total_len, len; | ||
943 | int i, written = 0; | ||
944 | int ret = 0; | ||
945 | |||
946 | /* Preset written len for early exit */ | ||
947 | *retlen = 0; | ||
948 | |||
949 | /* Calculate total length of data */ | ||
950 | total_len = 0; | ||
951 | for (i = 0; i < count; i++) | ||
952 | total_len += vecs[i].iov_len; | ||
953 | |||
954 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_writev_ecc: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); | ||
955 | |||
956 | /* Do not allow write past end of the device */ | ||
957 | if (unlikely((to + total_len) > mtd->size)) { | ||
958 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempted write past end of device\n"); | ||
959 | return -EINVAL; | ||
960 | } | ||
961 | |||
962 | /* Reject writes, which are not page aligned */ | ||
963 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(total_len))) { | ||
964 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempt to write not page aligned data\n"); | ||
965 | return -EINVAL; | ||
966 | } | ||
967 | |||
968 | /* Grab the lock and see if the device is available */ | ||
969 | onenand_get_device(mtd, FL_WRITING); | ||
970 | |||
971 | /* TODO handling oob */ | ||
972 | |||
973 | /* Loop until all keve's data has been written */ | ||
974 | len = 0; | ||
975 | while (count) { | ||
976 | pbuf = buffer; | ||
977 | /* | ||
978 | * If the given tuple is >= pagesize then | ||
979 | * write it out from the iov | ||
980 | */ | ||
981 | if ((vecs->iov_len - len) >= mtd->oobblock) { | ||
982 | pbuf = vecs->iov_base + len; | ||
983 | |||
984 | len += mtd->oobblock; | ||
985 | |||
986 | /* Check, if we have to switch to the next tuple */ | ||
987 | if (len >= (int) vecs->iov_len) { | ||
988 | vecs++; | ||
989 | len = 0; | ||
990 | count--; | ||
991 | } | ||
992 | } else { | ||
993 | int cnt = 0, thislen; | ||
994 | while (cnt < mtd->oobblock) { | ||
995 | thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len); | ||
996 | memcpy(buffer + cnt, vecs->iov_base + len, thislen); | ||
997 | cnt += thislen; | ||
998 | len += thislen; | ||
999 | |||
1000 | /* Check, if we have to switch to the next tuple */ | ||
1001 | if (len >= (int) vecs->iov_len) { | ||
1002 | vecs++; | ||
1003 | len = 0; | ||
1004 | count--; | ||
1005 | } | ||
1006 | } | ||
1007 | } | ||
1008 | |||
1009 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); | ||
1010 | |||
1011 | this->write_bufferram(mtd, ONENAND_DATARAM, pbuf, 0, mtd->oobblock); | ||
1012 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | ||
1013 | |||
1014 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); | ||
1015 | |||
1016 | onenand_update_bufferram(mtd, to, 1); | ||
1017 | |||
1018 | ret = this->wait(mtd, FL_WRITING); | ||
1019 | if (ret) { | ||
1020 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: write failed %d\n", ret); | ||
1021 | goto out; | ||
1022 | } | ||
1023 | |||
1024 | |||
1025 | /* Only check verify write turn on */ | ||
1026 | ret = onenand_verify_page(mtd, (u_char *) pbuf, to); | ||
1027 | if (ret) { | ||
1028 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: verify failed %d\n", ret); | ||
1029 | goto out; | ||
1030 | } | ||
1031 | |||
1032 | written += mtd->oobblock; | ||
1033 | |||
1034 | to += mtd->oobblock; | ||
1035 | } | ||
1036 | |||
1037 | out: | ||
1038 | /* Deselect and wakt up anyone waiting on the device */ | ||
1039 | onenand_release_device(mtd); | ||
1040 | |||
1041 | *retlen = written; | ||
1042 | |||
1043 | return 0; | ||
1044 | } | ||
1045 | |||
1046 | /** | ||
1047 | * onenand_writev - [MTD Interface] compabilty function for onenand_writev_ecc | ||
1048 | * @param mtd MTD device structure | ||
1049 | * @param vecs the iovectors to write | ||
1050 | * @param count number of vectors | ||
1051 | * @param to offset to write to | ||
1052 | * @param retlen pointer to variable to store the number of written bytes | ||
1053 | * | ||
1054 | * OneNAND write with kvec. This just calls the ecc function | ||
1055 | */ | ||
1056 | static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs, | ||
1057 | unsigned long count, loff_t to, size_t *retlen) | ||
1058 | { | ||
1059 | return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL); | ||
1060 | } | ||
1061 | |||
1062 | /** | ||
1063 | * onenand_block_checkbad - [GENERIC] Check if a block is marked bad | ||
1064 | * @param mtd MTD device structure | ||
1065 | * @param ofs offset from device start | ||
1066 | * @param getchip 0, if the chip is already selected | ||
1067 | * @param allowbbt 1, if its allowed to access the bbt area | ||
1068 | * | ||
1069 | * Check, if the block is bad. Either by reading the bad block table or | ||
1070 | * calling of the scan function. | ||
1071 | */ | ||
1072 | static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) | ||
1073 | { | ||
1074 | struct onenand_chip *this = mtd->priv; | ||
1075 | struct bbm_info *bbm = this->bbm; | ||
1076 | |||
1077 | /* Return info from the table */ | ||
1078 | return bbm->isbad_bbt(mtd, ofs, allowbbt); | ||
1079 | } | ||
1080 | |||
1081 | /** | ||
1082 | * onenand_erase - [MTD Interface] erase block(s) | ||
1083 | * @param mtd MTD device structure | ||
1084 | * @param instr erase instruction | ||
1085 | * | ||
1086 | * Erase one ore more blocks | ||
1087 | */ | ||
1088 | static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
1089 | { | ||
1090 | struct onenand_chip *this = mtd->priv; | ||
1091 | unsigned int block_size; | ||
1092 | loff_t addr; | ||
1093 | int len; | ||
1094 | int ret = 0; | ||
1095 | |||
1096 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); | ||
1097 | |||
1098 | block_size = (1 << this->erase_shift); | ||
1099 | |||
1100 | /* Start address must align on block boundary */ | ||
1101 | if (unlikely(instr->addr & (block_size - 1))) { | ||
1102 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); | ||
1103 | return -EINVAL; | ||
1104 | } | ||
1105 | |||
1106 | /* Length must align on block boundary */ | ||
1107 | if (unlikely(instr->len & (block_size - 1))) { | ||
1108 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); | ||
1109 | return -EINVAL; | ||
1110 | } | ||
1111 | |||
1112 | /* Do not allow erase past end of device */ | ||
1113 | if (unlikely((instr->len + instr->addr) > mtd->size)) { | ||
1114 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); | ||
1115 | return -EINVAL; | ||
1116 | } | ||
1117 | |||
1118 | instr->fail_addr = 0xffffffff; | ||
1119 | |||
1120 | /* Grab the lock and see if the device is available */ | ||
1121 | onenand_get_device(mtd, FL_ERASING); | ||
1122 | |||
1123 | /* Loop throught the pages */ | ||
1124 | len = instr->len; | ||
1125 | addr = instr->addr; | ||
1126 | |||
1127 | instr->state = MTD_ERASING; | ||
1128 | |||
1129 | while (len) { | ||
1130 | |||
1131 | /* Check if we have a bad block, we do not erase bad blocks */ | ||
1132 | if (onenand_block_checkbad(mtd, addr, 0, 0)) { | ||
1133 | printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr); | ||
1134 | instr->state = MTD_ERASE_FAILED; | ||
1135 | goto erase_exit; | ||
1136 | } | ||
1137 | |||
1138 | this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); | ||
1139 | |||
1140 | ret = this->wait(mtd, FL_ERASING); | ||
1141 | /* Check, if it is write protected */ | ||
1142 | if (ret) { | ||
1143 | if (ret == -EPERM) | ||
1144 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Device is write protected!!!\n"); | ||
1145 | else | ||
1146 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); | ||
1147 | instr->state = MTD_ERASE_FAILED; | ||
1148 | instr->fail_addr = addr; | ||
1149 | goto erase_exit; | ||
1150 | } | ||
1151 | |||
1152 | len -= block_size; | ||
1153 | addr += block_size; | ||
1154 | } | ||
1155 | |||
1156 | instr->state = MTD_ERASE_DONE; | ||
1157 | |||
1158 | erase_exit: | ||
1159 | |||
1160 | ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; | ||
1161 | /* Do call back function */ | ||
1162 | if (!ret) | ||
1163 | mtd_erase_callback(instr); | ||
1164 | |||
1165 | /* Deselect and wake up anyone waiting on the device */ | ||
1166 | onenand_release_device(mtd); | ||
1167 | |||
1168 | return ret; | ||
1169 | } | ||
1170 | |||
1171 | /** | ||
1172 | * onenand_sync - [MTD Interface] sync | ||
1173 | * @param mtd MTD device structure | ||
1174 | * | ||
1175 | * Sync is actually a wait for chip ready function | ||
1176 | */ | ||
1177 | static void onenand_sync(struct mtd_info *mtd) | ||
1178 | { | ||
1179 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); | ||
1180 | |||
1181 | /* Grab the lock and see if the device is available */ | ||
1182 | onenand_get_device(mtd, FL_SYNCING); | ||
1183 | |||
1184 | /* Release it and go back */ | ||
1185 | onenand_release_device(mtd); | ||
1186 | } | ||
1187 | |||
1188 | |||
1189 | /** | ||
1190 | * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad | ||
1191 | * @param mtd MTD device structure | ||
1192 | * @param ofs offset relative to mtd start | ||
1193 | * | ||
1194 | * Check whether the block is bad | ||
1195 | */ | ||
1196 | static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) | ||
1197 | { | ||
1198 | /* Check for invalid offset */ | ||
1199 | if (ofs > mtd->size) | ||
1200 | return -EINVAL; | ||
1201 | |||
1202 | return onenand_block_checkbad(mtd, ofs, 1, 0); | ||
1203 | } | ||
1204 | |||
1205 | /** | ||
1206 | * onenand_default_block_markbad - [DEFAULT] mark a block bad | ||
1207 | * @param mtd MTD device structure | ||
1208 | * @param ofs offset from device start | ||
1209 | * | ||
1210 | * This is the default implementation, which can be overridden by | ||
1211 | * a hardware specific driver. | ||
1212 | */ | ||
1213 | static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | ||
1214 | { | ||
1215 | struct onenand_chip *this = mtd->priv; | ||
1216 | struct bbm_info *bbm = this->bbm; | ||
1217 | u_char buf[2] = {0, 0}; | ||
1218 | size_t retlen; | ||
1219 | int block; | ||
1220 | |||
1221 | /* Get block number */ | ||
1222 | block = ((int) ofs) >> bbm->bbt_erase_shift; | ||
1223 | if (bbm->bbt) | ||
1224 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); | ||
1225 | |||
1226 | /* We write two bytes, so we dont have to mess with 16 bit access */ | ||
1227 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | ||
1228 | return mtd->write_oob(mtd, ofs , 2, &retlen, buf); | ||
1229 | } | ||
1230 | |||
1231 | /** | ||
1232 | * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad | ||
1233 | * @param mtd MTD device structure | ||
1234 | * @param ofs offset relative to mtd start | ||
1235 | * | ||
1236 | * Mark the block as bad | ||
1237 | */ | ||
1238 | static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) | ||
1239 | { | ||
1240 | struct onenand_chip *this = mtd->priv; | ||
1241 | int ret; | ||
1242 | |||
1243 | ret = onenand_block_isbad(mtd, ofs); | ||
1244 | if (ret) { | ||
1245 | /* If it was bad already, return success and do nothing */ | ||
1246 | if (ret > 0) | ||
1247 | return 0; | ||
1248 | return ret; | ||
1249 | } | ||
1250 | |||
1251 | return this->block_markbad(mtd, ofs); | ||
1252 | } | ||
1253 | |||
1254 | /** | ||
1255 | * onenand_unlock - [MTD Interface] Unlock block(s) | ||
1256 | * @param mtd MTD device structure | ||
1257 | * @param ofs offset relative to mtd start | ||
1258 | * @param len number of bytes to unlock | ||
1259 | * | ||
1260 | * Unlock one or more blocks | ||
1261 | */ | ||
1262 | static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | ||
1263 | { | ||
1264 | struct onenand_chip *this = mtd->priv; | ||
1265 | int start, end, block, value, status; | ||
1266 | |||
1267 | start = ofs >> this->erase_shift; | ||
1268 | end = len >> this->erase_shift; | ||
1269 | |||
1270 | /* Continuous lock scheme */ | ||
1271 | if (this->options & ONENAND_CONT_LOCK) { | ||
1272 | /* Set start block address */ | ||
1273 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | ||
1274 | /* Set end block address */ | ||
1275 | this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); | ||
1276 | /* Write unlock command */ | ||
1277 | this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); | ||
1278 | |||
1279 | /* There's no return value */ | ||
1280 | this->wait(mtd, FL_UNLOCKING); | ||
1281 | |||
1282 | /* Sanity check */ | ||
1283 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) | ||
1284 | & ONENAND_CTRL_ONGO) | ||
1285 | continue; | ||
1286 | |||
1287 | /* Check lock status */ | ||
1288 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); | ||
1289 | if (!(status & ONENAND_WP_US)) | ||
1290 | printk(KERN_ERR "wp status = 0x%x\n", status); | ||
1291 | |||
1292 | return 0; | ||
1293 | } | ||
1294 | |||
1295 | /* Block lock scheme */ | ||
1296 | for (block = start; block < end; block++) { | ||
1297 | /* Set start block address */ | ||
1298 | this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | ||
1299 | /* Write unlock command */ | ||
1300 | this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); | ||
1301 | |||
1302 | /* There's no return value */ | ||
1303 | this->wait(mtd, FL_UNLOCKING); | ||
1304 | |||
1305 | /* Sanity check */ | ||
1306 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) | ||
1307 | & ONENAND_CTRL_ONGO) | ||
1308 | continue; | ||
1309 | |||
1310 | /* Set block address for read block status */ | ||
1311 | value = onenand_block_address(this, block); | ||
1312 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); | ||
1313 | |||
1314 | /* Check lock status */ | ||
1315 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); | ||
1316 | if (!(status & ONENAND_WP_US)) | ||
1317 | printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); | ||
1318 | } | ||
1319 | |||
1320 | return 0; | ||
1321 | } | ||
1322 | |||
1323 | /** | ||
1324 | * onenand_print_device_info - Print device ID | ||
1325 | * @param device device ID | ||
1326 | * | ||
1327 | * Print device ID | ||
1328 | */ | ||
1329 | static void onenand_print_device_info(int device) | ||
1330 | { | ||
1331 | int vcc, demuxed, ddp, density; | ||
1332 | |||
1333 | vcc = device & ONENAND_DEVICE_VCC_MASK; | ||
1334 | demuxed = device & ONENAND_DEVICE_IS_DEMUX; | ||
1335 | ddp = device & ONENAND_DEVICE_IS_DDP; | ||
1336 | density = device >> ONENAND_DEVICE_DENSITY_SHIFT; | ||
1337 | printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", | ||
1338 | demuxed ? "" : "Muxed ", | ||
1339 | ddp ? "(DDP)" : "", | ||
1340 | (16 << density), | ||
1341 | vcc ? "2.65/3.3" : "1.8", | ||
1342 | device); | ||
1343 | } | ||
1344 | |||
1345 | static const struct onenand_manufacturers onenand_manuf_ids[] = { | ||
1346 | {ONENAND_MFR_SAMSUNG, "Samsung"}, | ||
1347 | {ONENAND_MFR_UNKNOWN, "Unknown"} | ||
1348 | }; | ||
1349 | |||
1350 | /** | ||
1351 | * onenand_check_maf - Check manufacturer ID | ||
1352 | * @param manuf manufacturer ID | ||
1353 | * | ||
1354 | * Check manufacturer ID | ||
1355 | */ | ||
1356 | static int onenand_check_maf(int manuf) | ||
1357 | { | ||
1358 | int i; | ||
1359 | |||
1360 | for (i = 0; onenand_manuf_ids[i].id; i++) { | ||
1361 | if (manuf == onenand_manuf_ids[i].id) | ||
1362 | break; | ||
1363 | } | ||
1364 | |||
1365 | printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", | ||
1366 | onenand_manuf_ids[i].name, manuf); | ||
1367 | |||
1368 | return (i != ONENAND_MFR_UNKNOWN); | ||
1369 | } | ||
1370 | |||
1371 | /** | ||
1372 | * onenand_probe - [OneNAND Interface] Probe the OneNAND device | ||
1373 | * @param mtd MTD device structure | ||
1374 | * | ||
1375 | * OneNAND detection method: | ||
1376 | * Compare the the values from command with ones from register | ||
1377 | */ | ||
1378 | static int onenand_probe(struct mtd_info *mtd) | ||
1379 | { | ||
1380 | struct onenand_chip *this = mtd->priv; | ||
1381 | int bram_maf_id, bram_dev_id, maf_id, dev_id; | ||
1382 | int version_id; | ||
1383 | int density; | ||
1384 | |||
1385 | /* Send the command for reading device ID from BootRAM */ | ||
1386 | this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); | ||
1387 | |||
1388 | /* Read manufacturer and device IDs from BootRAM */ | ||
1389 | bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); | ||
1390 | bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); | ||
1391 | |||
1392 | /* Check manufacturer ID */ | ||
1393 | if (onenand_check_maf(bram_maf_id)) | ||
1394 | return -ENXIO; | ||
1395 | |||
1396 | /* Reset OneNAND to read default register values */ | ||
1397 | this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); | ||
1398 | |||
1399 | /* Read manufacturer and device IDs from Register */ | ||
1400 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); | ||
1401 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); | ||
1402 | |||
1403 | /* Check OneNAND device */ | ||
1404 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) | ||
1405 | return -ENXIO; | ||
1406 | |||
1407 | /* Flash device information */ | ||
1408 | onenand_print_device_info(dev_id); | ||
1409 | this->device_id = dev_id; | ||
1410 | |||
1411 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; | ||
1412 | this->chipsize = (16 << density) << 20; | ||
1413 | /* Set density mask. it is used for DDP */ | ||
1414 | this->density_mask = (1 << (density + 6)); | ||
1415 | |||
1416 | /* OneNAND page size & block size */ | ||
1417 | /* The data buffer size is equal to page size */ | ||
1418 | mtd->oobblock = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); | ||
1419 | mtd->oobsize = mtd->oobblock >> 5; | ||
1420 | /* Pagers per block is always 64 in OneNAND */ | ||
1421 | mtd->erasesize = mtd->oobblock << 6; | ||
1422 | |||
1423 | this->erase_shift = ffs(mtd->erasesize) - 1; | ||
1424 | this->page_shift = ffs(mtd->oobblock) - 1; | ||
1425 | this->ppb_shift = (this->erase_shift - this->page_shift); | ||
1426 | this->page_mask = (mtd->erasesize / mtd->oobblock) - 1; | ||
1427 | |||
1428 | /* REVIST: Multichip handling */ | ||
1429 | |||
1430 | mtd->size = this->chipsize; | ||
1431 | |||
1432 | /* Version ID */ | ||
1433 | version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); | ||
1434 | printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id); | ||
1435 | |||
1436 | /* Lock scheme */ | ||
1437 | if (density <= ONENAND_DEVICE_DENSITY_512Mb && | ||
1438 | !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) { | ||
1439 | printk(KERN_INFO "Lock scheme is Continues Lock\n"); | ||
1440 | this->options |= ONENAND_CONT_LOCK; | ||
1441 | } | ||
1442 | |||
1443 | return 0; | ||
1444 | } | ||
1445 | |||
1446 | /** | ||
1447 | * onenand_suspend - [MTD Interface] Suspend the OneNAND flash | ||
1448 | * @param mtd MTD device structure | ||
1449 | */ | ||
1450 | static int onenand_suspend(struct mtd_info *mtd) | ||
1451 | { | ||
1452 | return onenand_get_device(mtd, FL_PM_SUSPENDED); | ||
1453 | } | ||
1454 | |||
1455 | /** | ||
1456 | * onenand_resume - [MTD Interface] Resume the OneNAND flash | ||
1457 | * @param mtd MTD device structure | ||
1458 | */ | ||
1459 | static void onenand_resume(struct mtd_info *mtd) | ||
1460 | { | ||
1461 | struct onenand_chip *this = mtd->priv; | ||
1462 | |||
1463 | if (this->state == FL_PM_SUSPENDED) | ||
1464 | onenand_release_device(mtd); | ||
1465 | else | ||
1466 | printk(KERN_ERR "resume() called for the chip which is not" | ||
1467 | "in suspended state\n"); | ||
1468 | } | ||
1469 | |||
1470 | |||
1471 | /** | ||
1472 | * onenand_scan - [OneNAND Interface] Scan for the OneNAND device | ||
1473 | * @param mtd MTD device structure | ||
1474 | * @param maxchips Number of chips to scan for | ||
1475 | * | ||
1476 | * This fills out all the not initialized function pointers | ||
1477 | * with the defaults. | ||
1478 | * The flash ID is read and the mtd/chip structures are | ||
1479 | * filled with the appropriate values. | ||
1480 | */ | ||
1481 | int onenand_scan(struct mtd_info *mtd, int maxchips) | ||
1482 | { | ||
1483 | struct onenand_chip *this = mtd->priv; | ||
1484 | |||
1485 | if (!this->read_word) | ||
1486 | this->read_word = onenand_readw; | ||
1487 | if (!this->write_word) | ||
1488 | this->write_word = onenand_writew; | ||
1489 | |||
1490 | if (!this->command) | ||
1491 | this->command = onenand_command; | ||
1492 | if (!this->wait) | ||
1493 | this->wait = onenand_wait; | ||
1494 | |||
1495 | if (!this->read_bufferram) | ||
1496 | this->read_bufferram = onenand_read_bufferram; | ||
1497 | if (!this->write_bufferram) | ||
1498 | this->write_bufferram = onenand_write_bufferram; | ||
1499 | |||
1500 | if (!this->block_markbad) | ||
1501 | this->block_markbad = onenand_default_block_markbad; | ||
1502 | if (!this->scan_bbt) | ||
1503 | this->scan_bbt = onenand_default_bbt; | ||
1504 | |||
1505 | if (onenand_probe(mtd)) | ||
1506 | return -ENXIO; | ||
1507 | |||
1508 | /* Set Sync. Burst Read after probing */ | ||
1509 | if (this->mmcontrol) { | ||
1510 | printk(KERN_INFO "OneNAND Sync. Burst Read support\n"); | ||
1511 | this->read_bufferram = onenand_sync_read_bufferram; | ||
1512 | } | ||
1513 | |||
1514 | this->state = FL_READY; | ||
1515 | init_waitqueue_head(&this->wq); | ||
1516 | spin_lock_init(&this->chip_lock); | ||
1517 | |||
1518 | switch (mtd->oobsize) { | ||
1519 | case 64: | ||
1520 | this->autooob = &onenand_oob_64; | ||
1521 | break; | ||
1522 | |||
1523 | case 32: | ||
1524 | this->autooob = &onenand_oob_32; | ||
1525 | break; | ||
1526 | |||
1527 | default: | ||
1528 | printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", | ||
1529 | mtd->oobsize); | ||
1530 | /* To prevent kernel oops */ | ||
1531 | this->autooob = &onenand_oob_32; | ||
1532 | break; | ||
1533 | } | ||
1534 | |||
1535 | memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); | ||
1536 | |||
1537 | /* Fill in remaining MTD driver data */ | ||
1538 | mtd->type = MTD_NANDFLASH; | ||
1539 | mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; | ||
1540 | mtd->ecctype = MTD_ECC_SW; | ||
1541 | mtd->erase = onenand_erase; | ||
1542 | mtd->point = NULL; | ||
1543 | mtd->unpoint = NULL; | ||
1544 | mtd->read = onenand_read; | ||
1545 | mtd->write = onenand_write; | ||
1546 | mtd->read_ecc = onenand_read_ecc; | ||
1547 | mtd->write_ecc = onenand_write_ecc; | ||
1548 | mtd->read_oob = onenand_read_oob; | ||
1549 | mtd->write_oob = onenand_write_oob; | ||
1550 | mtd->readv = NULL; | ||
1551 | mtd->readv_ecc = NULL; | ||
1552 | mtd->writev = onenand_writev; | ||
1553 | mtd->writev_ecc = onenand_writev_ecc; | ||
1554 | mtd->sync = onenand_sync; | ||
1555 | mtd->lock = NULL; | ||
1556 | mtd->unlock = onenand_unlock; | ||
1557 | mtd->suspend = onenand_suspend; | ||
1558 | mtd->resume = onenand_resume; | ||
1559 | mtd->block_isbad = onenand_block_isbad; | ||
1560 | mtd->block_markbad = onenand_block_markbad; | ||
1561 | mtd->owner = THIS_MODULE; | ||
1562 | |||
1563 | /* Unlock whole block */ | ||
1564 | mtd->unlock(mtd, 0x0, this->chipsize); | ||
1565 | |||
1566 | return this->scan_bbt(mtd); | ||
1567 | } | ||
1568 | |||
1569 | /** | ||
1570 | * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device | ||
1571 | * @param mtd MTD device structure | ||
1572 | */ | ||
1573 | void onenand_release(struct mtd_info *mtd) | ||
1574 | { | ||
1575 | #ifdef CONFIG_MTD_PARTITIONS | ||
1576 | /* Deregister partitions */ | ||
1577 | del_mtd_partitions (mtd); | ||
1578 | #endif | ||
1579 | /* Deregister the device */ | ||
1580 | del_mtd_device (mtd); | ||
1581 | } | ||
1582 | |||
1583 | EXPORT_SYMBOL_GPL(onenand_scan); | ||
1584 | EXPORT_SYMBOL_GPL(onenand_release); | ||
1585 | |||
1586 | MODULE_LICENSE("GPL"); | ||
1587 | MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); | ||
1588 | MODULE_DESCRIPTION("Generic OneNAND flash driver code"); | ||
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c new file mode 100644 index 000000000000..f40190f499e1 --- /dev/null +++ b/drivers/mtd/onenand/onenand_bbt.c | |||
@@ -0,0 +1,246 @@ | |||
1 | /* | ||
2 | * linux/drivers/mtd/onenand/onenand_bbt.c | ||
3 | * | ||
4 | * Bad Block Table support for the OneNAND driver | ||
5 | * | ||
6 | * Copyright(c) 2005 Samsung Electronics | ||
7 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
8 | * | ||
9 | * Derived from nand_bbt.c | ||
10 | * | ||
11 | * TODO: | ||
12 | * Split BBT core and chip specific BBT. | ||
13 | */ | ||
14 | |||
15 | #include <linux/slab.h> | ||
16 | #include <linux/mtd/mtd.h> | ||
17 | #include <linux/mtd/onenand.h> | ||
18 | #include <linux/mtd/compatmac.h> | ||
19 | |||
20 | /** | ||
21 | * check_short_pattern - [GENERIC] check if a pattern is in the buffer | ||
22 | * @param buf the buffer to search | ||
23 | * @param len the length of buffer to search | ||
24 | * @param paglen the pagelength | ||
25 | * @param td search pattern descriptor | ||
26 | * | ||
27 | * Check for a pattern at the given place. Used to search bad block | ||
28 | * tables and good / bad block identifiers. Same as check_pattern, but | ||
29 | * no optional empty check and the pattern is expected to start | ||
30 | * at offset 0. | ||
31 | * | ||
32 | */ | ||
33 | static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) | ||
34 | { | ||
35 | int i; | ||
36 | uint8_t *p = buf; | ||
37 | |||
38 | /* Compare the pattern */ | ||
39 | for (i = 0; i < td->len; i++) { | ||
40 | if (p[i] != td->pattern[i]) | ||
41 | return -1; | ||
42 | } | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * create_bbt - [GENERIC] Create a bad block table by scanning the device | ||
48 | * @param mtd MTD device structure | ||
49 | * @param buf temporary buffer | ||
50 | * @param bd descriptor for the good/bad block search pattern | ||
51 | * @param chip create the table for a specific chip, -1 read all chips. | ||
52 | * Applies only if NAND_BBT_PERCHIP option is set | ||
53 | * | ||
54 | * Create a bad block table by scanning the device | ||
55 | * for the given good/bad block identify pattern | ||
56 | */ | ||
57 | static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) | ||
58 | { | ||
59 | struct onenand_chip *this = mtd->priv; | ||
60 | struct bbm_info *bbm = this->bbm; | ||
61 | int i, j, numblocks, len, scanlen; | ||
62 | int startblock; | ||
63 | loff_t from; | ||
64 | size_t readlen, ooblen; | ||
65 | |||
66 | printk(KERN_INFO "Scanning device for bad blocks\n"); | ||
67 | |||
68 | len = 1; | ||
69 | |||
70 | /* We need only read few bytes from the OOB area */ | ||
71 | scanlen = ooblen = 0; | ||
72 | readlen = bd->len; | ||
73 | |||
74 | /* chip == -1 case only */ | ||
75 | /* Note that numblocks is 2 * (real numblocks) here; | ||
76 | * see i += 2 below as it makses shifting and masking less painful | ||
77 | */ | ||
78 | numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); | ||
79 | startblock = 0; | ||
80 | from = 0; | ||
81 | |||
82 | for (i = startblock; i < numblocks; ) { | ||
83 | int ret; | ||
84 | |||
85 | for (j = 0; j < len; j++) { | ||
86 | size_t retlen; | ||
87 | |||
88 | /* No need to read pages fully, | ||
89 | * just read required OOB bytes */ | ||
90 | ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs, | ||
91 | readlen, &retlen, &buf[0]); | ||
92 | |||
93 | if (ret) | ||
94 | return ret; | ||
95 | |||
96 | if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { | ||
97 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); | ||
98 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | ||
99 | i >> 1, (unsigned int) from); | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | i += 2; | ||
104 | from += (1 << bbm->bbt_erase_shift); | ||
105 | } | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | |||
111 | /** | ||
112 | * onenand_memory_bbt - [GENERIC] create a memory based bad block table | ||
113 | * @param mtd MTD device structure | ||
114 | * @param bd descriptor for the good/bad block search pattern | ||
115 | * | ||
116 | * The function creates a memory based bbt by scanning the device | ||
117 | * for manufacturer / software marked good / bad blocks | ||
118 | */ | ||
119 | static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) | ||
120 | { | ||
121 | unsigned char data_buf[MAX_ONENAND_PAGESIZE]; | ||
122 | |||
123 | bd->options &= ~NAND_BBT_SCANEMPTY; | ||
124 | return create_bbt(mtd, data_buf, bd, -1); | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad | ||
129 | * @param mtd MTD device structure | ||
130 | * @param offs offset in the device | ||
131 | * @param allowbbt allow access to bad block table region | ||
132 | */ | ||
133 | static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) | ||
134 | { | ||
135 | struct onenand_chip *this = mtd->priv; | ||
136 | struct bbm_info *bbm = this->bbm; | ||
137 | int block; | ||
138 | uint8_t res; | ||
139 | |||
140 | /* Get block number * 2 */ | ||
141 | block = (int) (offs >> (bbm->bbt_erase_shift - 1)); | ||
142 | res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; | ||
143 | |||
144 | DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", | ||
145 | (unsigned int) offs, block >> 1, res); | ||
146 | |||
147 | switch ((int) res) { | ||
148 | case 0x00: return 0; | ||
149 | case 0x01: return 1; | ||
150 | case 0x02: return allowbbt ? 0 : 1; | ||
151 | } | ||
152 | |||
153 | return 1; | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s) | ||
158 | * @param mtd MTD device structure | ||
159 | * @param bd descriptor for the good/bad block search pattern | ||
160 | * | ||
161 | * The function checks, if a bad block table(s) is/are already | ||
162 | * available. If not it scans the device for manufacturer | ||
163 | * marked good / bad blocks and writes the bad block table(s) to | ||
164 | * the selected place. | ||
165 | * | ||
166 | * The bad block table memory is allocated here. It must be freed | ||
167 | * by calling the onenand_free_bbt function. | ||
168 | * | ||
169 | */ | ||
170 | int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) | ||
171 | { | ||
172 | struct onenand_chip *this = mtd->priv; | ||
173 | struct bbm_info *bbm = this->bbm; | ||
174 | int len, ret = 0; | ||
175 | |||
176 | len = mtd->size >> (this->erase_shift + 2); | ||
177 | /* Allocate memory (2bit per block) */ | ||
178 | bbm->bbt = kmalloc(len, GFP_KERNEL); | ||
179 | if (!bbm->bbt) { | ||
180 | printk(KERN_ERR "onenand_scan_bbt: Out of memory\n"); | ||
181 | return -ENOMEM; | ||
182 | } | ||
183 | /* Clear the memory bad block table */ | ||
184 | memset(bbm->bbt, 0x00, len); | ||
185 | |||
186 | /* Set the bad block position */ | ||
187 | bbm->badblockpos = ONENAND_BADBLOCK_POS; | ||
188 | |||
189 | /* Set erase shift */ | ||
190 | bbm->bbt_erase_shift = this->erase_shift; | ||
191 | |||
192 | if (!bbm->isbad_bbt) | ||
193 | bbm->isbad_bbt = onenand_isbad_bbt; | ||
194 | |||
195 | /* Scan the device to build a memory based bad block table */ | ||
196 | if ((ret = onenand_memory_bbt(mtd, bd))) { | ||
197 | printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n"); | ||
198 | kfree(bbm->bbt); | ||
199 | bbm->bbt = NULL; | ||
200 | } | ||
201 | |||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * Define some generic bad / good block scan pattern which are used | ||
207 | * while scanning a device for factory marked good / bad blocks. | ||
208 | */ | ||
209 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | ||
210 | |||
211 | static struct nand_bbt_descr largepage_memorybased = { | ||
212 | .options = 0, | ||
213 | .offs = 0, | ||
214 | .len = 2, | ||
215 | .pattern = scan_ff_pattern, | ||
216 | }; | ||
217 | |||
218 | /** | ||
219 | * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device | ||
220 | * @param mtd MTD device structure | ||
221 | * | ||
222 | * This function selects the default bad block table | ||
223 | * support for the device and calls the onenand_scan_bbt function | ||
224 | */ | ||
225 | int onenand_default_bbt(struct mtd_info *mtd) | ||
226 | { | ||
227 | struct onenand_chip *this = mtd->priv; | ||
228 | struct bbm_info *bbm; | ||
229 | |||
230 | this->bbm = kmalloc(sizeof(struct bbm_info), GFP_KERNEL); | ||
231 | if (!this->bbm) | ||
232 | return -ENOMEM; | ||
233 | |||
234 | bbm = this->bbm; | ||
235 | |||
236 | memset(bbm, 0, sizeof(struct bbm_info)); | ||
237 | |||
238 | /* 1KB page has same configuration as 2KB page */ | ||
239 | if (!bbm->badblock_pattern) | ||
240 | bbm->badblock_pattern = &largepage_memorybased; | ||
241 | |||
242 | return onenand_scan_bbt(mtd, bbm->badblock_pattern); | ||
243 | } | ||
244 | |||
245 | EXPORT_SYMBOL(onenand_scan_bbt); | ||
246 | EXPORT_SYMBOL(onenand_default_bbt); | ||