bc94454d0adc36bfc5e2c73aecce5860206c2bf5
[openwrt.git] / target / linux / rdc / files / drivers / mtd / maps / rdc3210.c
1 /*******************************************************************
2  * Simple Flash mapping for RDC3210                                *
3  *                                                                 *
4  *                                                     2005.03.23  *
5  *                              Dante Su (dante_su@gemtek.com.tw)  *
6  *                          Copyright (C) 2005 Gemtek Corporation  *
7  *******************************************************************/
8
9 #include <linux/module.h>
10 #include <linux/types.h>
11 #include <linux/kernel.h>
12 #include <asm/io.h>
13 #include <linux/mtd/mtd.h>
14 #include <linux/mtd/map.h>
15 #include <linux/mtd/partitions.h>
16 #include <linux/autoconf.h>
17 #include <linux/sched.h>
18
19 #define SQUASHFS_MAGIC               0x73717368
20
21 static struct mtd_info          *rdc3210_mtd;
22
23 struct map_info rdc3210_map = 
24 {
25         .name =         "RDC3210 Flash",
26         .size =         CONFIG_MTD_RDC3210_SIZE,
27         .bankwidth =    CONFIG_MTD_RDC3210_BUSWIDTH,
28 };
29
30 /* Dante: This is the default static mapping, however this is nothing but a hint. (Say dynamic mapping) */
31 static struct mtd_partition rdc3210_parts[] = 
32 {
33 #if CONFIG_MTD_RDC3210_SIZE == 0x400000
34         { name: "linux",   offset:  0,          size: 0x003C0000 },     /* 3840 KB = (Kernel + ROMFS) = (768 KB + 3072 KB) */
35         { name: "romfs",   offset:  0x000C0000, size: 0x00300000 },     /* 3072 KB */
36         { name: "nvram",   offset:  0x003C0000, size: 0x00010000 },     /*   64 KB */
37 #ifdef CONFIG_MTD_RDC3210_FACTORY_PRESENT
38         { name: "factory", offset:  0x003D0000, size: 0x00010000 },     /*   64 KB */
39 #endif
40         { name: "bootldr", offset:  0x003E0000, size: 0x00020000 },     /*  128 KB */
41 #elif CONFIG_MTD_RDC3210_SIZE == 0x200000
42         { name: "linux",   offset:  0x00008000, size: 0x001E8000 },
43         { name: "romfs",   offset:  0x000C8000, size: 0x00128000 },
44         { name: "nvram",   offset:  0x00000000, size: 0x00008000 },     /*   64 KB */
45 #ifdef CONFIG_MTD_RDC3210_FACTORY_PRESENT
46 #error Unsupported configuration!
47 #endif
48         { name: "bootldr", offset:  0x001F0000, size: 0x00010000 },
49
50 #elif CONFIG_MTD_RDC3210_SIZE == 0x800000
51         { name: "linux",   offset:  0,          size: 0x001F0000 },     /* 1984 KB */
52         { name: "config",  offset:  0x001F0000, size: 0x00010000 },     /*   64 KB */
53         { name: "romfs",   offset:  0x00200000, size: 0x005D0000 },     /* 5952 KB */
54 #ifdef CONFIG_MTD_RDC3210_FACTORY_PRESENT
55         { name: "factory", offset:  0x007D0000, size: 0x00010000 },     /*   64 KB */
56 #endif
57         { name: "bootldr", offset:  0x007E0000, size: 0x00010000 },     /*   64 KB */
58 #else
59 #error Unsupported configuration!
60 #endif
61 };
62
63 static __u32 crctab[257] = {
64         0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
65         0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
66         0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
67         0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
68         0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
69         0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
70         0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
71         0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
72         0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
73         0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
74         0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
75         0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
76         0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
77         0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
78         0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
79         0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
80         0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
81         0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
82         0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
83         0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
84         0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
85         0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
86         0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
87         0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
88         0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
89         0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
90         0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
91         0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
92         0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
93         0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
94         0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
95         0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
96         0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
97         0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
98         0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
99         0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
100         0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
101         0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
102         0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
103         0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
104         0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
105         0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
106         0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
107         0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
108         0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
109         0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
110         0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
111         0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
112         0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
113         0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
114         0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
115         0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
116         0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
117         0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
118         0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
119         0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
120         0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
121         0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
122         0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
123         0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
124         0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
125         0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
126         0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
127         0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
128         0
129 };
130
131 static __u32 crc32(__u8 * buf, __u32 len)
132 {
133         register int i;
134         __u32 sum;
135         register __u32 s0;
136         s0 = ~0;
137         for (i = 0; i < len; i++) {
138                 s0 = (s0 >> 8) ^ crctab[(__u8) (s0 & 0xFF) ^ buf[i]];
139         }
140         sum = ~s0;
141         return sum;
142 }
143
144 static void erase_callback(struct erase_info *done)
145 {
146         wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
147         wake_up(wait_q);
148 }
149
150 static int erase_write (struct mtd_info *mtd, unsigned long pos, 
151                         int len, const char *buf)
152 {
153         struct erase_info erase;
154         DECLARE_WAITQUEUE(wait, current);
155         wait_queue_head_t wait_q;
156         size_t retlen;
157         int ret;
158
159         /*
160          * First, let's erase the flash block.
161          */
162
163         init_waitqueue_head(&wait_q);
164         erase.mtd = mtd;
165         erase.callback = erase_callback;
166         erase.addr = pos;
167         erase.len = len;
168         erase.priv = (u_long)&wait_q;
169
170         set_current_state(TASK_INTERRUPTIBLE);
171         add_wait_queue(&wait_q, &wait);
172
173         ret = mtd->erase(mtd, &erase);
174         if (ret) {
175                 set_current_state(TASK_RUNNING);
176                 remove_wait_queue(&wait_q, &wait);
177                 printk (KERN_WARNING "erase of region [0x%lx, 0x%x] "
178                                      "on \"%s\" failed\n",
179                         pos, len, mtd->name);
180                 return ret;
181         }
182
183         schedule();  /* Wait for erase to finish. */
184         remove_wait_queue(&wait_q, &wait);
185
186         /*
187          * Next, writhe data to flash.
188          */
189
190         ret = mtd->write (mtd, pos, len, &retlen, buf);
191         if (ret)
192                 return ret;
193         if (retlen != len)
194                 return -EIO;
195         return 0;
196 }
197
198 static int __init init_rdc3210_map(void)
199 {
200         rdc3210_map.phys = -rdc3210_map.size;
201         printk(KERN_NOTICE "flash device: %lx at %x\n", rdc3210_map.size, rdc3210_map.phys);
202
203 #if CONFIG_MTD_RDC3210_SIZE == 0x800000
204         simple_map_init(&rdc3210_map);
205 #endif
206         
207         rdc3210_map.map_priv_1 = (unsigned long)(rdc3210_map.virt = ioremap_nocache(rdc3210_map.phys, rdc3210_map.size));
208
209         if (!rdc3210_map.map_priv_1) 
210         {
211                 printk("Failed to ioremap\n");
212                 return -EIO;
213         }
214         rdc3210_mtd = do_map_probe("cfi_probe", &rdc3210_map);
215 #ifdef CONFIG_MTD_RDC3210_STATIC_MAP    /* Dante: This is for fixed map */
216         if (rdc3210_mtd) 
217         {
218                 rdc3210_mtd->owner = THIS_MODULE;
219                 add_mtd_partitions(rdc3210_mtd, rdc3210_parts, sizeof(rdc3210_parts)/sizeof(rdc3210_parts[0]));
220                 return 0;
221         }
222 #else   /* Dante: This is for dynamic mapping */
223
224 #include "imghdr.h"
225
226         typedef struct {
227                 u8      magic[4];
228                 u32     kernelsz, ramdisksz;
229                 u8      magic2[4];
230                 u32     sz2;
231         }sc_imghdr_t;
232
233         if (rdc3210_mtd) 
234         {       // Dante
235                 sc_imghdr_t     *hdr2= (sc_imghdr_t *)(rdc3210_map.map_priv_1);
236                 gt_imghdr_t     *hdr = (gt_imghdr_t *)hdr2
237 #ifdef CONFIG_MTD_RDC3210_ALLOW_JFFS2
238                         , *ptmp
239 #endif
240                         ;
241                 int     len, tmp, tmp2, tmp3, tmp4, hdr_type = 0;
242                 
243                 if(!memcmp(hdr->magic, GTIMG_MAGIC, 4))
244                 {
245                         hdr_type = 1;
246                         tmp = hdr->kernelsz + sizeof(gt_imghdr_t);
247                         tmp2 = rdc3210_mtd->erasesize;
248                         tmp3 = ((tmp / 32) + ((tmp % 32) ? 1 : 0)) * 32;
249                         tmp4 = ((tmp / tmp2) + ((tmp % tmp2) ? 1 : 0)) * tmp2;
250                 }
251 #ifndef CONFIG_MTD_RDC3210_ALLOW_JFFS2
252                 else if (!memcmp(hdr2->magic, "CSYS", 4))
253                 {
254                         hdr_type = 2;
255                         tmp = hdr2->ramdisksz + hdr2->kernelsz + sizeof(sc_imghdr_t);
256                         tmp2 = rdc3210_mtd->erasesize;
257                         tmp3 = ((tmp / 32) + ((tmp % 32) ? 1 : 0)) * 32;
258                         tmp4 = ((tmp / tmp2) + ((tmp % tmp2) ? 1 : 0)) * tmp2;
259                 }
260 #endif
261                 else
262                 {
263                         iounmap((void *)rdc3210_map.map_priv_1);
264                         rdc3210_map.map_priv_1 = 0L;
265                         rdc3210_map.virt = NULL;
266                         printk("Invalid MAGIC for Firmware Image!!!\n");
267                         return -EIO;
268                 }
269 #ifdef CONFIG_MTD_RDC3210_ALLOW_JFFS2
270                 tmp = (tmp3 == tmp4) ? tmp4 + tmp2 : tmp4;
271                 if ((ptmp = (gt_imghdr_t *)vmalloc(tmp)) == NULL)
272                 {
273                         iounmap((void *)rdc3210_map.map_priv_1);
274                         rdc3210_map.map_priv_1 = 0L;
275                         rdc3210_map.virt = NULL;
276                         printk("Can't allocate 0x%08x for flash-reading buffer!\n", tmp);
277                         return -ENOMEM;
278                 }
279                 if (rdc3210_mtd->read(rdc3210_mtd, 0, tmp, &len, (__u8 *)ptmp) || len != tmp)
280                 {
281                         vfree(ptmp);
282                         iounmap((void *)rdc3210_map.map_priv_1);
283                         rdc3210_map.map_priv_1 = 0L;
284                         rdc3210_map.virt = NULL;
285                         printk("Can't read that much flash! Read 0x%08x of it.\n", len);
286                         return -EIO;
287                 }
288 #endif
289 #ifdef CONFIG_MTD_RDC3210_FACTORY_PRESENT
290                 /* 1. Adjust Redboot */
291                 tmp = rdc3210_mtd->size - rdc3210_parts[4].size;
292                 rdc3210_parts[4].offset = tmp - (tmp % tmp2);
293                 rdc3210_parts[4].size   = rdc3210_mtd->size - rdc3210_parts[4].offset;
294                 
295                 /* 2. Adjust Factory Default */
296                 tmp -= rdc3210_parts[3].size;
297                 rdc3210_parts[3].offset = tmp - (tmp % tmp2);
298                 rdc3210_parts[3].size   = rdc3210_parts[4].offset - rdc3210_parts[3].offset;
299 #else
300                 /* 1. Adjust Redboot */
301                 tmp = rdc3210_mtd->size - rdc3210_parts[3].size;
302                 rdc3210_parts[3].offset = tmp - (tmp % tmp2);
303                 rdc3210_parts[3].size   = rdc3210_mtd->size - rdc3210_parts[3].offset;
304 #endif
305                 if (hdr_type == 1) {
306                 /* 3. Adjust NVRAM */
307 #ifdef CONFIG_MTD_RDC3210_ALLOW_JFFS2
308                 if (*(__u32 *)(((unsigned char *)ptmp)+tmp3) == SQUASHFS_MAGIC)
309                 {
310                         len = 1;
311                         tmp4 = tmp3;
312                         tmp = hdr->imagesz;
313                 rdc3210_parts[2].name   = "rootfs_data";
314                 rdc3210_parts[2].offset = rdc3210_parts[0].offset + (((tmp / tmp2) + ((tmp % tmp2) ? 1 : 0)) * tmp2);
315                 }
316                 else
317 #else
318                         tmp4 = tmp3;
319 #endif
320                 {
321                         len = 0;
322                 tmp -= rdc3210_parts[2].size;
323                 rdc3210_parts[2].offset = tmp - (tmp % tmp2);
324                 }
325                 rdc3210_parts[2].size   = rdc3210_parts[3].offset - rdc3210_parts[2].offset;
326                 }
327                 else if (hdr_type == 2)
328                 {
329                         len = 0;
330                         tmp4 = tmp3;
331                 }
332                 
333                 /* 4. Adjust Linux (Kernel + ROMFS) */
334                 rdc3210_parts[0].size   = rdc3210_parts[len + hdr_type + 1].offset - rdc3210_parts[0].offset;
335
336                 /* 5. Adjust ROMFS */
337                 rdc3210_parts[1].offset = rdc3210_parts[0].offset + tmp4;
338                 rdc3210_parts[1].size   = rdc3210_parts[hdr_type + 1].offset - rdc3210_parts[1].offset;
339 #ifdef CONFIG_MTD_RDC3210_ALLOW_JFFS2
340                 if (!(hdr->reserved || len))
341                 {
342                         __u8    buf[1024];
343                         ptmp->reserved = hdr->imagesz;
344                         ptmp->imagesz  = tmp4;
345                         ptmp->checksum = ptmp->fastcksum = 0;
346                         memcpy(buf, ptmp, 0x100);
347                         memcpy(buf + 0x100, ((__u8 *)ptmp) + ((tmp4 >> 1) - ((tmp4 & 0x6) >> 1)), 0x100);
348                         memcpy(buf + 0x200, ((__u8 *)ptmp) + (tmp4 - 0x200), 0x200);
349                         ptmp->fastcksum = crc32(buf, sizeof(buf));
350                         ptmp->checksum = crc32((__u8 *)ptmp, tmp4);
351                         if (rdc3210_mtd->unlock) rdc3210_mtd->unlock(rdc3210_mtd, 0, tmp2);
352                         if ((len = erase_write(rdc3210_mtd, 0, tmp2, (char *)ptmp)))
353                         {
354                                 vfree(ptmp);
355                                 iounmap((void *)rdc3210_map.map_priv_1);
356                                 rdc3210_map.map_priv_1 = 0L;
357                                 rdc3210_map.virt = NULL;
358                                 printk("Couldn't erase! Got %d.\n", len);
359                                 return len;
360                         }
361                         if (rdc3210_mtd->sync) rdc3210_mtd->sync(rdc3210_mtd);
362                 }
363                 vfree(ptmp);
364 #endif
365                 rdc3210_mtd->owner = THIS_MODULE;
366                 add_mtd_partitions(rdc3210_mtd, rdc3210_parts, sizeof(rdc3210_parts)/sizeof(rdc3210_parts[0]));
367                 return 0;
368         }
369 #endif
370         iounmap((void *)rdc3210_map.map_priv_1);
371         rdc3210_map.map_priv_1 = 0L;
372         rdc3210_map.virt = NULL;
373         return -ENXIO;
374 }
375
376 static void __exit cleanup_rdc3210_map(void)
377 {
378         if (rdc3210_mtd) 
379         {
380                 del_mtd_partitions(rdc3210_mtd);
381                 map_destroy(rdc3210_mtd);
382         }
383         
384         if (rdc3210_map.map_priv_1) 
385         {
386                 iounmap((void *)rdc3210_map.map_priv_1);
387                 rdc3210_map.map_priv_1 = 0L;
388                 rdc3210_map.virt = NULL;
389         }
390 }
391
392 module_init(init_rdc3210_map);
393 module_exit(cleanup_rdc3210_map);