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