source: src/linux/universal/linux-3.2/drivers/mtd/devices/ar7100_flash.c @ 18404

Last change on this file since 18404 was 18404, checked in by BrainSlayer, 16 months ago

WNR2000 uboot detection and platform mac

File size: 15.3 KB
Line 
1/*
2 * This file contains glue for Atheros ar7100 spi flash interface
3 * Primitives are ar7100_spi_*
4 * mtd flash implements are ar7100_flash_*
5 */
6#include <linux/kernel.h>
7#include <linux/module.h>
8#include <linux/types.h>
9#include <linux/errno.h>
10#include <linux/slab.h>
11#include <linux/mtd/mtd.h>
12#include <linux/mtd/partitions.h>
13#include "../mtdcore.h"
14#include <asm/delay.h>
15#include <asm/io.h>
16#include <linux/semaphore.h>
17#include <linux/squashfs_fs.h>
18
19#include "ar7100.h"
20#include "ar7100_flash.h"
21
22/* this is passed in as a boot parameter by bootloader */
23//extern int __ath_flash_size;
24
25/*
26 * statics
27 */
28static void ar7100_spi_write_enable(void);
29static void ar7100_spi_poll(void);
30static void ar7100_spi_write_page(uint32_t addr, uint8_t * data, int len);
31static void ar7100_spi_sector_erase(uint32_t addr);
32
33static const char *part_probes[] __initdata =
34    { "cmdlinepart", "RedBoot", NULL };
35
36#define down mutex_lock
37#define up mutex_unlock
38#define init_MUTEX mutex_init
39#define DECLARE_MUTEX(a) struct mutex a
40
41static DECLARE_MUTEX(ar7100_flash_sem);
42
43/* GLOBAL FUNCTIONS */
44void ar7100_flash_spi_down(void)
45{
46//printk(KERN_EMERG "spi down\n");
47        down(&ar7100_flash_sem);
48}
49
50void ar7100_flash_spi_up(void)
51{
52//printk(KERN_EMERG "spi up\n");
53        up(&ar7100_flash_sem);
54}
55
56EXPORT_SYMBOL(ar7100_flash_spi_down);
57EXPORT_SYMBOL(ar7100_flash_spi_up);
58
59#define AR7100_FLASH_SIZE_2MB          (2*1024*1024)
60#define AR7100_FLASH_SIZE_4MB          (4*1024*1024)
61#define AR7100_FLASH_SIZE_8MB          (8*1024*1024)
62#define AR7100_FLASH_SIZE_16MB          (16*1024*1024)
63#define AR7100_FLASH_SECTOR_SIZE_64KB  (64*1024)
64#define AR7100_FLASH_PG_SIZE_256B       256
65#define AR7100_FLASH_NAME               "ar7100-nor0"
66
67/*
68 * bank geometry
69 */
70typedef struct ar7100_flash_geom {
71        uint32_t size;
72        uint32_t sector_size;
73        uint32_t nsectors;
74        uint32_t pgsize;
75} ar7100_flash_geom_t;
76
77ar7100_flash_geom_t flash_geom_tbl[AR7100_FLASH_MAX_BANKS] = {
78        {
79#ifdef CONFIG_MTD_FLASH_16MB
80         .size = AR7100_FLASH_SIZE_16MB,
81#elif CONFIG_MTD_FLASH_8MB
82         .size = AR7100_FLASH_SIZE_8MB,
83#else
84         .size = AR7100_FLASH_SIZE_4MB,
85#endif
86         .sector_size = AR7100_FLASH_SECTOR_SIZE_64KB,
87         .pgsize = AR7100_FLASH_PG_SIZE_256B}
88};
89
90static int ar7100_flash_probe()
91{
92        return 0;
93}
94
95static int ar7100_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
96{
97        int nsect, s_curr, s_last;
98        uint64_t  res;
99        if (instr->addr + instr->len > mtd->size)
100                return (-EINVAL);
101
102        ar7100_flash_spi_down();
103
104        res = instr->len;
105        do_div(res, mtd->erasesize);
106        nsect = res;
107
108        if (((uint32_t)instr->len) % mtd->erasesize)
109                nsect++;
110
111        res = instr->addr;
112        do_div(res,mtd->erasesize);
113        s_curr = res;
114        s_last = s_curr + nsect;
115
116        do {
117                ar7100_spi_sector_erase(s_curr * AR7100_SPI_SECTOR_SIZE);
118        } while (++s_curr < s_last);
119
120        ar7100_spi_done();
121
122        ar7100_flash_spi_up();
123
124        if (instr->callback) {
125                instr->state = MTD_ERASE_DONE;
126                instr->callback(instr);
127        }
128        //printk(KERN_EMERG "done\n");
129        return 0;
130}
131
132static int
133ar7100_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
134                  size_t *retlen, u_char * buf)
135{
136        uint32_t addr = from | 0xbf000000;
137
138//      printk(KERN_EMERG "read block %X:%X\n",from,len);
139        if (!len)
140                return (0);
141        if (from + len > mtd->size)
142                return (-EINVAL);
143
144//      ar7100_flash_spi_down();
145
146        memcpy(buf, (uint8_t *) (addr), len);
147        *retlen = len;
148
149//      ar7100_flash_spi_up();
150//      printk(KERN_EMERG "read block %X:%X done\n",from,len);
151
152        return 0;
153}
154
155static int
156ar7100_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
157                   size_t *retlen, const u_char * buf)
158{
159        int total = 0, len_this_lp, bytes_this_page;
160        uint32_t addr = 0;
161        u_char *mem;
162//      printk(KERN_EMERG "write block %X:%X\n",to,len);
163
164        ar7100_flash_spi_down();
165
166        while (total < len) {
167                mem = buf + total;
168                addr = to + total;
169                bytes_this_page =
170                    AR7100_SPI_PAGE_SIZE - (addr % AR7100_SPI_PAGE_SIZE);
171                len_this_lp = min((len - total), bytes_this_page);
172
173                ar7100_spi_write_page(addr, mem, len_this_lp);
174                total += len_this_lp;
175        }
176
177        ar7100_spi_done();
178
179        ar7100_flash_spi_up();
180
181        *retlen = len;
182        return 0;
183}
184
185static int zcom=0;
186static unsigned int zcomoffset = 0;
187int guessbootsize(void *offset, unsigned int maxscan)
188{
189        unsigned int i,a;
190        unsigned int *ofs = (unsigned int *)offset;
191        maxscan -= 65536;
192        maxscan /= 4;
193        zcom=0;
194        for (i = 0; i < maxscan; i += 16384) {
195                if (ofs[i] == 0x6d000080) {
196                        printk(KERN_EMERG "redboot or compatible detected\n");
197                        return i * 4;   // redboot, lzma image
198                }
199                if (ofs[i] == 0x27051956) {
200                        printk(KERN_EMERG "uboot detected\n");
201                        return i * 4;   // uboot, lzma image
202                }
203                if (ofs[i] == 0x33373030) {
204                        printk(KERN_EMERG "WNDR3700 uboot detected\n");
205                        return 0x70000; // uboot, lzma image
206                }
207                if (ofs[i] == 0x33373031) {
208                        printk(KERN_EMERG "WNDR3700v2 uboot detected\n");
209                        return 0x70000; // uboot, lzma image
210                }
211                if (ofs[i] == 0x01000000 && ofs[i+1] == 0x44442d57) {
212                        printk(KERN_EMERG "tplink uboot detected\n");
213                        return i * 4;   // uboot, lzma image
214                }
215                if (ofs[i + 15] == 0x27051956) {
216                        printk(KERN_EMERG "WRT160NL/E2100L uboot detected\n");
217                        return i * 4;   // uboot, lzma image
218                }
219                if (ofs[i] == SQUASHFS_MAGIC) {
220                        printk(KERN_EMERG "ZCom quirk found\n");
221                        zcom=1;
222                        for (a = i; a < maxscan; a += 16384) {
223                                        if (ofs[a] == 0x27051956) {
224                                            printk(KERN_EMERG "ZCom quirk kernel offset %d\n",a*4);
225                                            zcomoffset = a * 4;
226                                        }
227       
228                        }
229                        return i * 4;   // filesys starts earlier
230                }
231               
232        }
233        return -1;
234}
235
236static unsigned int guessflashsize(void *base)
237{
238        unsigned int size;
239        unsigned int *guess = (unsigned int *)base;
240        unsigned int max = 16 << 20;
241//check 3 patterns since we can't write.
242        unsigned int p1 = guess[0];
243        unsigned int p2 = guess[4096];
244        unsigned int p3 = guess[8192];
245        unsigned int c1;
246        unsigned int c2;
247        unsigned int c3;
248        for (size = 2 << 20; size <= (max >> 1); size <<= 1) {
249                unsigned int ofs = size / 4;
250                c1 = guess[ofs];
251                c2 = guess[ofs + 4096];
252                c3 = guess[ofs + 8192];
253                if (p1 == c1 && p2 == c2 && p3 == c3)   // mirror found
254                {
255                        break;
256                }
257        }
258        printk(KERN_EMERG "guessed flashsize = %dM\n", size >> 20);
259        return size;
260
261}
262
263static struct mtd_partition dir_parts[] = {
264#ifdef CONFIG_MTD_FLASH_16MB
265      {name: "RedBoot", offset: 0, size:0x40000,},
266        //, mask_flags: MTD_WRITEABLE, },
267      {name: "linux", offset: 0x30000, size:0xf90000,},
268#elif CONFIG_MTD_FLASH_8MB
269#ifdef CONFIG_AR7100_LOW
270      {name: "RedBoot", offset: 0, size:0x50000,},
271      {name: "linux", offset: 0x50000, size:0x770000,},
272#elif CONFIG_AR9100             //, mask_flags: MTD_WRITEABLE, }
273      {name: "RedBoot", offset: 0, size:0x40000,},
274      {name: "linux", offset: 0x40000, size:0x7a0000,},
275#else                           //, mask_flags: MTD_WRITEABLE, },
276      {name: "RedBoot", offset: 0, size:0x30000,},
277      {name: "linux", offset: 0x30000, size:0x790000,},
278#endif
279
280#else
281      {name: "RedBoot", offset: 0, size:0x40000,},
282        //, mask_flags: MTD_WRITEABLE, },
283      {name: "linux", offset: 0x40000, size:0x390000,},
284#endif
285      {name: "rootfs", offset: 0x0, size:0x2b0000,},
286        //must be detected
287      {name: "ddwrt", offset: 0x0, size:0x2b0000,},
288        //must be detected
289      {name: "nvram", offset: 0x3d0000, size:0x10000,},
290      {name: "FIS directory", offset: 0x3e0000, size:0x10000,},
291      {name: "board_config", offset: 0x3f0000, size:0x10000,},
292      {name: "fullflash", offset: 0x3f0000, size:0x10000,},
293      {name:NULL,},
294};
295
296struct fis_image_desc {
297        unsigned char name[16]; // Null terminated name
298        unsigned long flash_base;       // Address within FLASH of image
299        unsigned long mem_base; // Address in memory where it executes
300        unsigned long size;     // Length of image
301        unsigned long entry_point;      // Execution entry point
302        unsigned long data_length;      // Length of actual data
303        unsigned char _pad[256 - (16 + 7 * sizeof(unsigned long))];
304        unsigned long desc_cksum;       // Checksum over image descriptor
305        unsigned long file_cksum;       // Checksum over image data
306};
307
308/*
309 * sets up flash_info and returns size of FLASH (bytes)
310 */
311static int __init ar7100_flash_init(void)
312{
313        int i, np;
314        ar7100_flash_geom_t *geom;
315        struct mtd_info *mtd;
316        struct mtd_partition *mtd_parts;
317        uint8_t index;
318        int result = -1;
319        char *buf;
320        struct fis_image_desc *fis;
321        unsigned char *p;
322        int offset = 0;
323        struct squashfs_super_block *sb;
324        size_t rootsize;
325        size_t len;
326
327        init_MUTEX(&ar7100_flash_sem);
328
329        ar7100_reg_wr_nf(AR7100_SPI_CLOCK, 0x43);
330
331        buf = 0xbf000000;
332        int fsize = guessflashsize(buf);
333
334        for (i = 0; i < AR7100_FLASH_MAX_BANKS; i++) {
335
336                index = ar7100_flash_probe();
337                geom = &flash_geom_tbl[index];
338
339                /* set flash size to value from bootloader if it passed valid value */
340                /* otherwise use the default 4MB.                                   */
341                //if (__ath_flash_size >= 4 && __ath_flash_size <= 16)
342                //    geom->size = __ath_flash_size * 1024 * 1024;
343
344                mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
345                if (!mtd) {
346                        printk("Cant allocate mtd stuff\n");
347                        return -1;
348                }
349                memset(mtd, 0, sizeof(struct mtd_info));
350
351                mtd->name = AR7100_FLASH_NAME;
352                mtd->type = MTD_NORFLASH;
353                mtd->flags = (MTD_CAP_NORFLASH | MTD_WRITEABLE);
354                mtd->size = fsize;;
355                mtd->erasesize = geom->sector_size;
356                mtd->numeraseregions = 0;
357                mtd->eraseregions = NULL;
358                mtd->owner = THIS_MODULE;
359                mtd->erase = ar7100_flash_erase;
360                mtd->read = ar7100_flash_read;
361                mtd->write = ar7100_flash_write;
362
363                printk(KERN_EMERG "scanning for root partition\n");
364
365                offset = 0;
366
367                int compex = 0;
368                if (!strncmp((char *)(buf + 0x295a), "myloram.bin", 11)) {
369                        printk(KERN_EMERG "Compex WP543 device detected\n");
370                        dir_parts[0].size = 0x30000;
371                        dir_parts[0].offset = 0;
372                        dir_parts[7].size = mtd->size;
373                        dir_parts[7].offset = 0;
374
375                        dir_parts[6].size = mtd->erasesize;
376                        dir_parts[6].offset = mtd->size - mtd->erasesize;
377                        compex = 1;
378                } else {
379                        int guess = guessbootsize(buf, mtd->size);
380                        if (guess > 0) {
381                                printk(KERN_EMERG "bootloader size = %X\n",
382                                       guess);
383                                dir_parts[0].size = guess;
384                                dir_parts[0].offset = 0;
385                                dir_parts[1].offset = guess;
386                                dir_parts[1].size = 0;
387                        }
388
389                }
390
391                while ((offset + mtd->erasesize) < mtd->size) {
392                        //printk(KERN_EMERG "[0x%08X] = [0x%08X]!=[0x%08X]\n",offset,*((unsigned int *) buf),SQUASHFS_MAGIC);
393                        if (*((__u32 *)buf) == SQUASHFS_MAGIC) {
394                                printk(KERN_EMERG "\nfound squashfs at %X\n",
395                                       offset);
396                                sb = (struct squashfs_super_block *)buf;
397                                dir_parts[2].offset = offset;
398
399                                dir_parts[2].size = sb->bytes_used;
400                                len = dir_parts[2].offset + dir_parts[2].size;
401                                len += (mtd->erasesize - 1);
402                                len &= ~(mtd->erasesize - 1);
403                                dir_parts[2].size =
404                                    (len & 0x1ffffff) - dir_parts[2].offset;
405#if defined(CONFIG_DIR825)
406                                dir_parts[3].offset = 0x670000;
407#else
408                                dir_parts[3].offset =
409                                    dir_parts[2].offset + dir_parts[2].size;
410#endif
411                                dir_parts[6].offset = mtd->size - mtd->erasesize;       // board config
412                                dir_parts[6].size = mtd->erasesize;
413#ifdef CONFIG_MTD_FLASH_16MB
414#ifdef CONFIG_MTD_PB44
415                                dir_parts[5].offset = dir_parts[6].offset - mtd->erasesize;     //fis config
416#else
417                                dir_parts[5].offset = dir_parts[6].offset;      //fis config
418#endif
419                                dir_parts[5].offset = mtd->size - mtd->erasesize;       //fis config
420                                dir_parts[5].size = mtd->erasesize;
421#elif CONFIG_AR9100
422                                dir_parts[5].offset = dir_parts[6].offset;      //fis config
423                                dir_parts[5].size = mtd->erasesize;
424#elif CONFIG_MTD_FLASH_8MB
425                                dir_parts[5].offset = dir_parts[6].offset;      //fis config
426                                dir_parts[5].size = mtd->erasesize;
427#else
428                                dir_parts[5].offset = dir_parts[6].offset - mtd->erasesize;     //fis config
429                                dir_parts[5].size = mtd->erasesize;
430#endif
431                                if (compex)
432                                        dir_parts[4].offset = mtd->size - mtd->erasesize;       //nvram
433                                else
434                                        dir_parts[4].offset = dir_parts[5].offset - mtd->erasesize;     //nvram
435                                dir_parts[4].size = mtd->erasesize;
436                                dir_parts[3].size = dir_parts[4].offset - dir_parts[3].offset;
437                                rootsize = dir_parts[4].offset - offset;        //size of rootfs aligned to nvram offset
438#ifdef CONFIG_AR9100
439//                                      dir_parts[1].offset = 0x40000;
440                                dir_parts[1].size = (dir_parts[2].offset -dir_parts[1].offset) + rootsize;
441                                if (zcom)
442                                    {
443                                    dir_parts[1].size = 0x7d0000 - 0x50000;
444                                    dir_parts[1].offset = 0x50000;
445                                    dir_parts[2].size = sb->bytes_used;
446                                    dir_parts[2].offset = 0x50000;
447                                    len = dir_parts[2].offset + dir_parts[2].size;
448                                    len += (mtd->erasesize - 1);
449                                    len &= ~(mtd->erasesize - 1);
450                                    dir_parts[2].size = (len & 0x1ffffff) - dir_parts[2].offset;
451                                    dir_parts[3].offset = dir_parts[2].offset + dir_parts[2].size;
452                                    dir_parts[3].size = 0x7d0000 - dir_parts[3].offset;
453                                    dir_parts[6].offset = mtd->size - mtd->erasesize;   // board config
454                                    dir_parts[6].size = mtd->erasesize;
455                                    dir_parts[5].offset = dir_parts[6].offset;  //fis config
456                                    dir_parts[4].offset = dir_parts[5].offset - mtd->erasesize; //nvram                             
457                                    }
458                                break;
459#else
460                                //now scan for linux offset
461                                if (compex) {
462                                        dir_parts[1].offset = 0x30000;
463                                        dir_parts[1].size =
464                                            (dir_parts[2].offset -
465                                             dir_parts[1].offset) + rootsize;
466                                        break;
467                                } else {
468                                        p = (unsigned char *)(0xbf000000 +
469                                                              dir_parts
470                                                              [5].offset);
471                                        fis = (struct fis_image_desc *)p;
472                                        while (1) {
473                                                if (fis->name[0] == 0xff) {
474                                                        goto def;
475                                                }
476                                                if (!strcmp
477                                                    (fis->name, "RedBoot")) {
478                                                        printk(KERN_EMERG
479                                                               "found RedBoot partition at [0x%08lX]\n",
480                                                               fis->flash_base);
481                                                        dir_parts[0].size =
482                                                            fis->size;
483                                                        dir_parts[7].offset =
484                                                            dir_parts[0].size;
485                                                }
486                                                if (!strncmp
487                                                    (fis->name, "linux", 5)
488                                                    || !strncmp(fis->name,
489                                                                "vmlinux", 7)
490                                                    || !strncmp(fis->name,
491                                                                "kernel", 6)) {
492                                                        printk(KERN_EMERG
493                                                               "found linux partition at [0x%08lX]\n",
494                                                               fis->flash_base);
495                                                        dir_parts[1].offset =
496                                                            fis->flash_base &
497                                                            (mtd->size - 1);
498                                                        dir_parts[1].size =
499                                                            (dir_parts[2].offset
500                                                             -
501                                                             dir_parts
502                                                             [1].offset) +
503                                                            rootsize;
504                                                }
505                                                p += sizeof(struct
506                                                            fis_image_desc);
507                                                fis =
508                                                    (struct fis_image_desc *)p;
509                                        }
510                                        break;
511                                }
512#endif
513                        }
514                        offset += 4096;
515                        buf += 4096;
516                }
517              def:;
518                dir_parts[7].offset = 0;        // linux + nvram = phy size
519                dir_parts[7].size = mtd->size;  // linux + nvram = phy size
520                result = add_mtd_partitions(mtd, dir_parts, 8);
521        }
522
523        return 0;
524}
525
526static void __exit ar7100_flash_exit(void)
527{
528        /*
529         * nothing to do
530         */
531}
532
533/*
534 * Primitives to implement flash operations
535 */
536static void ar7100_spi_write_enable()
537{
538        ar7100_reg_wr_nf(AR7100_SPI_FS, 1);
539        ar7100_reg_wr_nf(AR7100_SPI_WRITE, AR7100_SPI_CS_DIS);
540        ar7100_spi_bit_banger(AR7100_SPI_CMD_WREN);
541        ar7100_spi_go();
542}
543
544static void ar7100_spi_poll()
545{
546        int rd;
547
548        do {
549                ar7100_reg_wr_nf(AR7100_SPI_WRITE, AR7100_SPI_CS_DIS);
550                ar7100_spi_bit_banger(AR7100_SPI_CMD_RD_STATUS);
551                ar7100_spi_delay_8();
552                rd = (ar7100_reg_rd(AR7100_SPI_RD_STATUS) & 1);
553        } while (rd);
554}
555
556static void ar7100_spi_write_page(uint32_t addr, uint8_t * data, int len)
557{
558        int i;
559        uint8_t ch;
560
561        ar7100_spi_write_enable();
562        ar7100_spi_bit_banger(AR7100_SPI_CMD_PAGE_PROG);
563        ar7100_spi_send_addr(addr);
564
565        for (i = 0; i < len; i++) {
566                ch = *(data + i);
567                ar7100_spi_bit_banger(ch);
568        }
569
570        ar7100_spi_go();
571        ar7100_spi_poll();
572}
573
574static void ar7100_spi_sector_erase(uint32_t addr)
575{
576        ar7100_spi_write_enable();
577        ar7100_spi_bit_banger(AR7100_SPI_CMD_SECTOR_ERASE);
578        ar7100_spi_send_addr(addr);
579        ar7100_spi_go();
580//    display(0x7d);
581        ar7100_spi_poll();
582}
583
584module_init(ar7100_flash_init);
585module_exit(ar7100_flash_exit);
Note: See TracBrowser for help on using the repository browser.