Major cleanup :
[openwrt.git] / target / linux / brcm63xx-2.6 / files / drivers / mtd / maps / bcm963xx-flash.c
1 /*
2  *  Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
3  *  Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org>
4  *  Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org)
5  *
6  *  original functions for finding root filesystem from Mike Baker 
7  *
8  *  This program is free software; you can redistribute  it and/or modify it
9  *  under  the terms of  the GNU General  Public License as published by the
10  *  Free Software Foundation;  either version 2 of the  License, or (at your
11  *  option) any later version.
12  *
13  *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
14  *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
15  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
16  *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
17  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
19  *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
21  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  *  You should have received a copy of the  GNU General Public License along
25  *  with this program; if not, write  to the Free Software Foundation, Inc.,
26  *  675 Mass Ave, Cambridge, MA 02139, USA.
27  * 
28  *  Copyright 2001-2003, Broadcom Corporation
29  *  All Rights Reserved.
30  * 
31  *  THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
32  *  KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
33  *  SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
34  *  FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
35  *
36  *  Flash mapping for BCM963XX boards
37  */
38
39 #include <linux/init.h>
40 #include <linux/module.h>
41 #include <linux/types.h>
42 #include <linux/kernel.h>
43 #include <linux/wait.h>
44 #include <linux/mtd/mtd.h>
45 #include <linux/mtd/map.h>
46 #ifdef CONFIG_MTD_PARTITIONS
47 #include <linux/mtd/partitions.h>
48 #endif
49 #include <linux/squashfs_fs.h>
50 #include <linux/jffs2.h>
51 #include <linux/crc32.h>
52 #include <asm/io.h>
53
54 #include <asm/mach-bcm963xx/bootloaders.h>
55
56 extern int boot_loader_type;
57 extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts, unsigned long fis_origin);
58 static struct mtd_partition *parsed_parts;
59
60
61 #define TRX_MAGIC       0x30524448      /* "HDR0" */
62 #define TRX_VERSION     1
63 #define TRX_MAX_LEN     0x3A0000
64 #define TRX_NO_HEADER   1               /* Do not write TRX header */   
65 #define TRX_GZ_FILES    0x2     /* Contains up to TRX_MAX_OFFSET individual gzip files */
66 #define TRX_MAX_OFFSET  3
67
68 struct trx_header {
69         u32 magic;              /* "HDR0" */
70         u32 len;                /* Length of file including header */
71         u32 crc32;              /* 32-bit CRC from flag_version to end of file */
72         u32 flag_version;       /* 0:15 flags, 16:31 version */
73         u32 offsets[TRX_MAX_OFFSET];    /* Offsets of partitions from start of header */
74 };
75
76 #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
77 #define NVRAM_SPACE 0x8000
78 #define WINDOW_ADDR 0x1FC00000
79 #define WINDOW_SIZE 0x400000
80 #define BUSWIDTH 2
81
82 #define EXTENDED_SIZE 0xBFC00000       /* Extended flash address */
83
84 static struct mtd_info *bcm963xx_mtd;
85
86 static struct map_info bcm963xx_map = {
87         .name           = "Physically mapped flash",
88         .size           = WINDOW_SIZE,
89         .bankwidth      = BUSWIDTH,
90         .phys           = WINDOW_ADDR,
91 };
92
93 #ifdef CONFIG_MTD_PARTITIONS
94
95 static struct mtd_partition bcm963xx_parts[] = {
96         { name: "cfe",  offset: 0, size: 0, mask_flags: MTD_WRITEABLE, },
97         { name: "linux", offset: 0, size: 0, },
98         { name: "rootfs", offset: 0, size: 0, },
99         { name: "nvram", offset: 0, size: 0, },
100         { name: "OpenWrt", offset: 0, size: 0, },
101         { name: NULL, },
102 };
103
104 static int __init
105 find_cfe_size(struct mtd_info *mtd, size_t size)
106 {
107         struct trx_header *trx;
108         unsigned char buf[512];
109         int off;
110         size_t len;
111         int blocksize;
112
113         trx = (struct trx_header *) buf;
114
115         blocksize = mtd->erasesize;
116         if (blocksize < 0x10000)
117                 blocksize = 0x10000;
118
119         for (off = (128*1024); off < size; off += blocksize) {
120                 memset(buf, 0xe5, sizeof(buf));
121
122                 /*
123                  * Read into buffer 
124                  */
125                 if (mtd->read(mtd, off, sizeof(buf), &len, buf) ||
126                     len != sizeof(buf))
127                         continue;
128
129                 /* found a TRX header */
130                 if (le32_to_cpu(trx->magic) == TRX_MAGIC) {
131                         goto found;
132                 }
133         }
134
135         printk(KERN_NOTICE
136                "%s: Couldn't find bootloader size\n",
137                mtd->name);
138         return -1;
139
140  found:
141         printk(KERN_NOTICE "bootloader size: %d\n", off);
142         return off;
143
144 }
145
146 /*
147  * Copied from mtdblock.c
148  *
149  * Cache stuff...
150  * 
151  * Since typical flash erasable sectors are much larger than what Linux's
152  * buffer cache can handle, we must implement read-modify-write on flash
153  * sectors for each block write requests.  To avoid over-erasing flash sectors
154  * and to speed things up, we locally cache a whole flash sector while it is
155  * being written to until a different sector is required.
156  */
157
158 static void erase_callback(struct erase_info *done)
159 {
160         wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
161         wake_up(wait_q);
162 }
163
164 static int erase_write (struct mtd_info *mtd, unsigned long pos, 
165                         int len, const char *buf)
166 {
167         struct erase_info erase;
168         DECLARE_WAITQUEUE(wait, current);
169         wait_queue_head_t wait_q;
170         size_t retlen;
171         int ret;
172
173         /*
174          * First, let's erase the flash block.
175          */
176
177         init_waitqueue_head(&wait_q);
178         erase.mtd = mtd;
179         erase.callback = erase_callback;
180         erase.addr = pos;
181         erase.len = len;
182         erase.priv = (u_long)&wait_q;
183
184         set_current_state(TASK_INTERRUPTIBLE);
185         add_wait_queue(&wait_q, &wait);
186
187         ret = mtd->erase(mtd, &erase);
188         if (ret) {
189                 set_current_state(TASK_RUNNING);
190                 remove_wait_queue(&wait_q, &wait);
191                 printk (KERN_WARNING "erase of region [0x%lx, 0x%x] "
192                                      "on \"%s\" failed\n",
193                         pos, len, mtd->name);
194                 return ret;
195         }
196
197         schedule();  /* Wait for erase to finish. */
198         remove_wait_queue(&wait_q, &wait);
199
200         /*
201          * Next, writhe data to flash.
202          */
203
204         ret = mtd->write (mtd, pos, len, &retlen, buf);
205         if (ret)
206                 return ret;
207         if (retlen != len)
208                 return -EIO;
209         return 0;
210 }
211
212
213
214
215 static int __init
216 find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part)
217 {
218         struct trx_header trx, *trx2;
219         unsigned char buf[512], *block;
220         int off, blocksize;
221         u32 i, crc = ~0;
222         size_t len;
223         struct squashfs_super_block *sb = (struct squashfs_super_block *) buf;
224
225         blocksize = mtd->erasesize;
226         if (blocksize < 0x10000)
227                 blocksize = 0x10000;
228
229         for (off = (128*1024); off < size; off += blocksize) {
230                 memset(&trx, 0xe5, sizeof(trx));
231
232                 /*
233                  * Read into buffer 
234                  */
235                 if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) ||
236                     len != sizeof(trx))
237                         continue;
238
239                 /* found a TRX header */
240                 if (le32_to_cpu(trx.magic) == TRX_MAGIC) {
241                         part->offset = le32_to_cpu(trx.offsets[2]) ? : 
242                                 le32_to_cpu(trx.offsets[1]);
243                         part->size = le32_to_cpu(trx.len); 
244
245                         part->size -= part->offset;
246                         part->offset += off;
247
248                         goto found;
249                 }
250         }
251
252         printk(KERN_NOTICE
253                "%s: Couldn't find root filesystem\n",
254                mtd->name);
255         return -1;
256
257  found:
258         if (part->size == 0)
259                 return 0;
260         
261         if (mtd->read(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf))
262                 return 0;
263
264         if (*((__u32 *) buf) == SQUASHFS_MAGIC) {
265                 printk(KERN_INFO "%s: Filesystem type: squashfs, size=0x%x\n", mtd->name, (u32) sb->bytes_used);
266
267                 /* Update the squashfs partition size based on the superblock info */
268                 part->size = sb->bytes_used;
269                 len = part->offset + part->size;
270                 len +=  (mtd->erasesize - 1);
271                 len &= ~(mtd->erasesize - 1);
272                 part->size = len - part->offset;
273         } else if (*((__u16 *) buf) == JFFS2_MAGIC_BITMASK) {
274                 printk(KERN_INFO "%s: Filesystem type: jffs2\n", mtd->name);
275
276                 /* Move the squashfs outside of the trx */
277                 part->size = 0;
278         } else {
279                 printk(KERN_INFO "%s: Filesystem type: unknown\n", mtd->name);
280                 return 0;
281         }
282
283         if (trx.len != part->offset + part->size - off) {
284                 /* Update the trx offsets and length */
285                 trx.len = part->offset + part->size - off;
286         
287                 /* Update the trx crc32 */
288                 for (i = (u32) &(((struct trx_header *)NULL)->flag_version); i <= trx.len; i += sizeof(buf)) {
289                         if (mtd->read(mtd, off + i, sizeof(buf), &len, buf) || len != sizeof(buf))
290                                 return 0;
291                         crc = crc32_le(crc, buf, min(sizeof(buf), trx.len - i));
292                 }
293                 trx.crc32 = crc;
294
295                 /* read first eraseblock from the trx */
296                 block = kmalloc(mtd->erasesize, GFP_KERNEL);
297                 trx2 = (struct trx_header *) block;
298                 if (mtd->read(mtd, off, mtd->erasesize, &len, block) || len != mtd->erasesize) {
299                         printk("Error accessing the first trx eraseblock\n");
300                         return 0;
301                 }
302                 
303                 printk("Updating TRX offsets and length:\n");
304                 printk("old trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx2->offsets[0], trx2->offsets[1], trx2->offsets[2], trx2->len, trx2->crc32);
305                 printk("new trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n",   trx.offsets[0],   trx.offsets[1],   trx.offsets[2],   trx.len, trx.crc32);
306
307                 /* Write updated trx header to the flash */
308                 memcpy(block, &trx, sizeof(trx));
309                 if (mtd->unlock)
310                         mtd->unlock(mtd, off, mtd->erasesize);
311                 erase_write(mtd, off, mtd->erasesize, block);
312                 if (mtd->sync)
313                         mtd->sync(mtd);
314                 kfree(block);
315                 printk("Done\n");
316         }
317         
318         return part->size;
319 }
320
321 struct mtd_partition * __init
322 init_mtd_partitions(struct mtd_info *mtd, size_t size)
323 {
324         int cfe_size;
325
326         if ((cfe_size = find_cfe_size(mtd,size)) < 0)
327                 return NULL;
328
329         /* boot loader */
330         bcm963xx_parts[0].offset = 0;
331         bcm963xx_parts[0].size   = cfe_size;
332
333         /* nvram */
334         if (cfe_size != 384 * 1024) {
335                 bcm963xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize);
336                 bcm963xx_parts[3].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
337         } else {
338                 /* nvram (old 128kb config partition on netgear wgt634u) */
339                 bcm963xx_parts[3].offset = bcm963xx_parts[0].size;
340                 bcm963xx_parts[3].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
341         }
342
343         /* linux (kernel and rootfs) */
344         if (cfe_size != 384 * 1024) {
345                 bcm963xx_parts[1].offset = bcm963xx_parts[0].size;
346                 bcm963xx_parts[1].size   = bcm963xx_parts[3].offset - 
347                         bcm963xx_parts[1].offset;
348         } else {
349                 /* do not count the elf loader, which is on one block */
350                 bcm963xx_parts[1].offset = bcm963xx_parts[0].size + 
351                         bcm963xx_parts[3].size + mtd->erasesize;
352                 bcm963xx_parts[1].size   = size - 
353                         bcm963xx_parts[0].size - 
354                         (2*bcm963xx_parts[3].size) - 
355                         mtd->erasesize;
356         }
357
358         /* find and size rootfs */
359         if (find_root(mtd,size,&bcm963xx_parts[2])==0) {
360                 /* entirely jffs2 */
361                 bcm963xx_parts[4].name = NULL;
362                 bcm963xx_parts[2].size = size - bcm963xx_parts[2].offset - 
363                                 bcm963xx_parts[3].size;
364         } else {
365                 /* legacy setup */
366                 /* calculate leftover flash, and assign it to the jffs2 partition */
367                 if (cfe_size != 384 * 1024) {
368                         bcm963xx_parts[4].offset = bcm963xx_parts[2].offset + 
369                                 bcm963xx_parts[2].size;
370                         if ((bcm963xx_parts[4].offset % mtd->erasesize) > 0) {
371                                 bcm963xx_parts[4].offset += mtd->erasesize - 
372                                         (bcm963xx_parts[4].offset % mtd->erasesize);
373                         }
374                         bcm963xx_parts[4].size = bcm963xx_parts[3].offset - 
375                                 bcm963xx_parts[4].offset;
376                 } else {
377                         bcm963xx_parts[4].offset = bcm963xx_parts[2].offset + 
378                                 bcm963xx_parts[2].size;
379                         if ((bcm963xx_parts[4].offset % mtd->erasesize) > 0) {
380                                 bcm963xx_parts[4].offset += mtd->erasesize - 
381                                         (bcm963xx_parts[4].offset % mtd->erasesize);
382                         }
383                         bcm963xx_parts[4].size = size - bcm963xx_parts[3].size - 
384                                 bcm963xx_parts[4].offset;
385                 }
386         }
387
388         return bcm963xx_parts;
389 }
390 #endif
391
392 int __init init_bcm963xx_map(void)
393 {
394         size_t size;
395         int ret = 0;
396 #ifdef CONFIG_MTD_PARTITIONS
397         struct mtd_partition *parts;
398         int i;
399 #endif
400
401         printk("BCM963xx flash init: 0x%08x 0x%08x\n", WINDOW_ADDR, WINDOW_SIZE);
402         bcm963xx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
403
404         if (!bcm963xx_map.virt) {
405                 printk("Failed to ioremap\n");
406                 return -EIO;
407         }
408         simple_map_init(&bcm963xx_map);
409         
410         if (!(bcm963xx_mtd = do_map_probe("cfi_probe", &bcm963xx_map))) {
411                 printk("Failed to do_map_probe\n");
412                 iounmap((void *)bcm963xx_map.virt);
413                 return -ENXIO;
414         }
415
416         bcm963xx_mtd->owner = THIS_MODULE;
417
418         size = bcm963xx_mtd->size;
419
420         printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, WINDOW_ADDR);
421
422 #ifdef CONFIG_MTD_PARTITIONS
423         if (boot_loader_type == BOOT_LOADER_CFE) {
424                 parts = init_mtd_partitions(bcm963xx_mtd, size);
425                 for (i = 0; parts[i].name; i++);
426                         ret = add_mtd_partitions(bcm963xx_mtd, parts, i);
427                 if (ret) {
428                         printk(KERN_ERR "Flash: add_mtd_partitions failed\n");
429                         goto fail;
430                 }
431         }
432         else {
433                 int parsed_nr_parts = 0;
434                 char * part_type;
435
436                 if (bcm963xx_mtd->size > 0x00400000) {
437                         printk("Support for extended flash memory size : 0x%08X ; ONLY 64MBIT SUPPORT\n", bcm963xx_mtd->size);
438                         bcm963xx_map.virt = (unsigned long)EXTENDED_SIZE;
439                 }
440
441 #ifdef CONFIG_MTD_REDBOOT_PARTS
442                 if (parsed_nr_parts == 0) {
443                         int ret = parse_redboot_partitions(bcm963xx_mtd, &parsed_parts, 0);
444                         if (ret > 0) {
445                                 part_type = "RedBoot";
446                                 parsed_nr_parts = ret;
447                         }
448                 }
449 #endif
450                 add_mtd_partitions(bcm963xx_mtd, parsed_parts, parsed_nr_parts);        
451 #endif
452         }
453         return 0;
454
455  fail:
456         if (bcm963xx_mtd)
457                 map_destroy(bcm963xx_mtd);
458         if (bcm963xx_map.virt)
459                 iounmap((void *)bcm963xx_map.virt);
460         bcm963xx_map.virt = 0;
461         return ret;
462 }
463
464 void __exit cleanup_bcm963xx_map(void)
465 {
466 #ifdef CONFIG_MTD_PARTITIONS
467         del_mtd_partitions(bcm963xx_mtd);
468 #endif
469         map_destroy(bcm963xx_mtd);
470         iounmap((void *)bcm963xx_map.virt);
471 }
472
473 module_init(init_bcm963xx_map);
474 module_exit(cleanup_bcm963xx_map);