source: src/linux/universal/linux-4.4/drivers/mtd/devices/ar7100_flash.c @ 31660

Last change on this file since 31660 was 31660, checked in by brainslayer, 2 months ago

use new squashfs in all kernels

File size: 16.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(void)
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
185
186/* read value from spi flash reg */
187unsigned int ar7100_spi_read_cmd(u32 cmd)
188{
189        u32 ret = 0;
190       
191        ar7100_reg_wr_nf(AR7100_SPI_FS, 1);
192        ar7100_reg_wr_nf(AR7100_SPI_WRITE, AR7100_SPI_CS_DIS);  /* Chip Select */
193       
194        ar7100_spi_bit_banger(cmd);
195        ret = ar7100_spi_raw_input_u32();
196       
197        ar7100_spi_done();
198
199        return ret;
200}
201
202/* write a value to spi flash reg */
203void ar7100_spi_write_cmd(u32 cmd, u8 val)
204{
205        ar7100_spi_write_enable();
206       
207        ar7100_spi_bit_banger(cmd);
208        ar7100_spi_bit_banger(val);
209
210        ar7100_spi_go();
211    ar7100_spi_poll();
212
213        ar7100_spi_done();
214}
215
216static void ar7100_flash_unlock(void)
217{
218        u32 flashid;
219        u32 stat;
220
221        flashid = ar7100_spi_read_cmd(0x9f);
222        printk("->Oops: flash id 0x%x . \n", flashid >> 8);
223       
224
225        if (flashid >> 8 == 0x898912)
226        {
227                printk("->Oops: an Intel Flash. \n");
228               
229                stat = ar7100_spi_read_cmd(AR7100_SPI_CMD_RD_STATUS);
230                stat = stat >> 24;
231                printk("->Oops: old stat 0x%x . \n", stat);
232               
233                ar7100_spi_write_cmd(0x01, stat & (~0x1c));
234        }
235}
236
237
238static int zcom=0;
239static unsigned int zcomoffset = 0;
240int guessbootsize(void *offset, unsigned int maxscan)
241{
242        unsigned int i,a;
243        unsigned int *ofs = (unsigned int *)offset;
244        maxscan -= 65536;
245        maxscan /= 4;
246        zcom=0;
247        for (i = 0; i < maxscan; i += 16384) {
248                if (ofs[i] == 0x6d000080) {
249                        printk(KERN_EMERG "redboot or compatible detected\n");
250                        return i * 4;   // redboot, lzma image
251                }
252                if (ofs[i] == 0x27051956) {
253                        printk(KERN_EMERG "uboot detected\n");
254                        return i * 4;   // uboot, lzma image
255                }
256                if (ofs[i] == 0x33373030) {
257                        printk(KERN_EMERG "WNDR3700 uboot detected\n");
258                        return 0x70000; // uboot, lzma image
259                }
260                if (ofs[i] == 0x33373031) {
261                        printk(KERN_EMERG "WNDR3700v2 uboot detected\n");
262                        return 0x70000; // uboot, lzma image
263                }
264                if (ofs[i] == 0x01000000 && ofs[i+1] == 0x44442d57) {
265                        printk(KERN_EMERG "tplink uboot detected\n");
266                        return i * 4;   // uboot, lzma image
267                }
268                if (ofs[i + 15] == 0x27051956) {
269                        printk(KERN_EMERG "WRT160NL/E2100L uboot detected\n");
270                        return i * 4;   // uboot, lzma image
271                }
272                if (ofs[i] == SQUASHFS_MAGIC_SWAP) {
273                        printk(KERN_EMERG "ZCom quirk found\n");
274                        zcom=1;
275                        for (a = i; a < maxscan; a += 16384) {
276                                        if (ofs[a] == 0x27051956) {
277                                            printk(KERN_EMERG "ZCom quirk kernel offset %d\n",a*4);
278                                            zcomoffset = a * 4;
279                                        }
280       
281                        }
282                        return i * 4;   // filesys starts earlier
283                }
284               
285        }
286        return -1;
287}
288
289static unsigned int guessflashsize(void *base)
290{
291        unsigned int size;
292        unsigned int *guess = (unsigned int *)base;
293        unsigned int max = 16 << 20;
294//check 3 patterns since we can't write.
295        unsigned int p1 = guess[0];
296        unsigned int p2 = guess[4096];
297        unsigned int p3 = guess[8192];
298        unsigned int c1;
299        unsigned int c2;
300        unsigned int c3;
301        for (size = 2 << 20; size <= (max >> 1); size <<= 1) {
302                unsigned int ofs = size / 4;
303                c1 = guess[ofs];
304                c2 = guess[ofs + 4096];
305                c3 = guess[ofs + 8192];
306                if (p1 == c1 && p2 == c2 && p3 == c3)   // mirror found
307                {
308                        break;
309                }
310        }
311        printk(KERN_EMERG "guessed flashsize = %dM\n", size >> 20);
312        return size;
313
314}
315
316static struct mtd_partition dir_parts[] = {
317#ifdef CONFIG_MTD_FLASH_16MB
318      {name: "RedBoot", offset: 0, size:0x40000,},
319        //, mask_flags: MTD_WRITEABLE, },
320      {name: "linux", offset: 0x30000, size:0xf90000,},
321#elif CONFIG_MTD_FLASH_8MB
322#ifdef CONFIG_AR7100_LOW
323      {name: "RedBoot", offset: 0, size:0x50000,},
324      {name: "linux", offset: 0x50000, size:0x770000,},
325#elif CONFIG_AR9100             //, mask_flags: MTD_WRITEABLE, }
326      {name: "RedBoot", offset: 0, size:0x40000,},
327      {name: "linux", offset: 0x40000, size:0x7a0000,},
328#else                           //, mask_flags: MTD_WRITEABLE, },
329      {name: "RedBoot", offset: 0, size:0x30000,},
330      {name: "linux", offset: 0x30000, size:0x790000,},
331#endif
332
333#else
334      {name: "RedBoot", offset: 0, size:0x40000,},
335        //, mask_flags: MTD_WRITEABLE, },
336      {name: "linux", offset: 0x40000, size:0x390000,},
337#endif
338      {name: "rootfs", offset: 0x0, size:0x2b0000,},
339        //must be detected
340      {name: "ddwrt", offset: 0x0, size:0x2b0000,},
341        //must be detected
342      {name: "nvram", offset: 0x3d0000, size:0x10000,},
343      {name: "FIS directory", offset: 0x3e0000, size:0x10000,},
344      {name: "board_config", offset: 0x3f0000, size:0x10000,},
345      {name: "fullflash", offset: 0x3f0000, size:0x10000,},
346      {name:NULL,},
347};
348
349unsigned int compex=0;
350
351EXPORT_SYMBOL(compex);
352
353struct fis_image_desc {
354        unsigned char name[16]; // Null terminated name
355        unsigned long flash_base;       // Address within FLASH of image
356        unsigned long mem_base; // Address in memory where it executes
357        unsigned long size;     // Length of image
358        unsigned long entry_point;      // Execution entry point
359        unsigned long data_length;      // Length of actual data
360        unsigned char _pad[256 - (16 + 7 * sizeof(unsigned long))];
361        unsigned long desc_cksum;       // Checksum over image descriptor
362        unsigned long file_cksum;       // Checksum over image data
363};
364
365/*
366 * sets up flash_info and returns size of FLASH (bytes)
367 */
368static int __init ar7100_flash_init(void)
369{
370        int i, np;
371        ar7100_flash_geom_t *geom;
372        struct mtd_info *mtd;
373        struct mtd_partition *mtd_parts;
374        uint8_t index;
375        int result = -1;
376        char *buf;
377        struct fis_image_desc *fis;
378        unsigned char *p;
379        int offset = 0;
380        struct squashfs_super_block *sb;
381        size_t rootsize;
382        size_t len;
383
384        init_MUTEX(&ar7100_flash_sem);
385
386        ar7100_reg_wr_nf(AR7100_SPI_CLOCK, 0x43);
387
388        buf = 0xbf000000;
389        int fsize = guessflashsize(buf);
390
391        for (i = 0; i < AR7100_FLASH_MAX_BANKS; i++) {
392
393                index = ar7100_flash_probe();
394                geom = &flash_geom_tbl[index];
395
396                /* set flash size to value from bootloader if it passed valid value */
397                /* otherwise use the default 4MB.                                   */
398                //if (__ath_flash_size >= 4 && __ath_flash_size <= 16)
399                //    geom->size = __ath_flash_size * 1024 * 1024;
400
401                mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
402                if (!mtd) {
403                        printk("Cant allocate mtd stuff\n");
404                        return -1;
405                }
406                memset(mtd, 0, sizeof(struct mtd_info));
407
408                mtd->name = AR7100_FLASH_NAME;
409                mtd->type = MTD_NORFLASH;
410                mtd->flags = (MTD_CAP_NORFLASH | MTD_WRITEABLE);
411                mtd->size = fsize;;
412                mtd->erasesize = geom->sector_size;
413                mtd->numeraseregions = 0;
414                mtd->eraseregions = NULL;
415                mtd->owner = THIS_MODULE;
416                mtd->_erase = ar7100_flash_erase;
417                mtd->_read = ar7100_flash_read;
418                mtd->_write = ar7100_flash_write;
419
420                printk(KERN_EMERG "scanning for root partition\n");
421
422                offset = 0;
423
424                if (!strncmp((char *)(buf + 0x295a), "myloram.bin", 11)) {
425                        printk(KERN_EMERG "Compex WP543 device detected\n");
426                        dir_parts[0].size = 0x30000;
427                        dir_parts[0].offset = 0;
428                        dir_parts[7].size = mtd->size;
429                        dir_parts[7].offset = 0;
430
431                        dir_parts[6].size = mtd->erasesize;
432                        dir_parts[6].offset = mtd->size - mtd->erasesize;
433                        compex = 1;
434                } else {
435                        int guess = guessbootsize(buf, mtd->size);
436                        if (guess > 0) {
437                                printk(KERN_EMERG "bootloader size = %X\n",
438                                       guess);
439                                dir_parts[0].size = guess;
440                                dir_parts[0].offset = 0;
441                                dir_parts[1].offset = guess;
442                                dir_parts[1].size = 0;
443                        }
444
445                }
446
447                while ((offset + mtd->erasesize) < mtd->size) {
448                        //printk(KERN_EMERG "[0x%08X] = [0x%08X]!=[0x%08X]\n",offset,*((unsigned int *) buf),SQUASHFS_MAGIC);
449                        if (*((__u32 *)buf) == SQUASHFS_MAGIC_SWAP) {
450                                printk(KERN_EMERG "\nfound squashfs at %X\n",
451                                       offset);
452                                sb = (struct squashfs_super_block *)buf;
453                                dir_parts[2].offset = offset;
454
455                                dir_parts[2].size = sb->bytes_used;
456                                len = dir_parts[2].offset + dir_parts[2].size;
457                                len += (mtd->erasesize - 1);
458                                len &= ~(mtd->erasesize - 1);
459                                dir_parts[2].size =
460                                    (len & 0x1ffffff) - dir_parts[2].offset;
461#if defined(CONFIG_DIR825)
462                                dir_parts[3].offset = 0x670000;
463#else
464                                dir_parts[3].offset =
465                                    dir_parts[2].offset + dir_parts[2].size;
466#endif
467                                dir_parts[6].offset = mtd->size - mtd->erasesize;       // board config
468                                dir_parts[6].size = mtd->erasesize;
469#ifdef CONFIG_MTD_FLASH_16MB
470#ifdef CONFIG_MTD_PB44
471                                dir_parts[5].offset = dir_parts[6].offset - mtd->erasesize;     //fis config
472#else
473                                dir_parts[5].offset = dir_parts[6].offset;      //fis config
474#endif
475                                dir_parts[5].offset = mtd->size - mtd->erasesize;       //fis config
476                                dir_parts[5].size = mtd->erasesize;
477#elif CONFIG_AR9100
478                                dir_parts[5].offset = dir_parts[6].offset;      //fis config
479                                dir_parts[5].size = mtd->erasesize;
480#elif CONFIG_MTD_FLASH_8MB
481                                dir_parts[5].offset = dir_parts[6].offset;      //fis config
482                                dir_parts[5].size = mtd->erasesize;
483#else
484                                dir_parts[5].offset = dir_parts[6].offset - mtd->erasesize;     //fis config
485                                dir_parts[5].size = mtd->erasesize;
486#endif
487                                if (compex)
488                                        dir_parts[4].offset = mtd->size - mtd->erasesize;       //nvram
489                                else
490                                        dir_parts[4].offset = dir_parts[5].offset - mtd->erasesize;     //nvram
491                                dir_parts[4].size = mtd->erasesize;
492                                dir_parts[3].size = dir_parts[4].offset - dir_parts[3].offset;
493                                rootsize = dir_parts[4].offset - offset;        //size of rootfs aligned to nvram offset
494#ifdef CONFIG_AR9100
495//                                      dir_parts[1].offset = 0x40000;
496                                dir_parts[1].size = (dir_parts[2].offset -dir_parts[1].offset) + rootsize;
497                                if (zcom)
498                                    {
499                                    dir_parts[1].size = 0x7d0000 - 0x50000;
500                                    dir_parts[1].offset = 0x50000;
501                                    dir_parts[2].size = sb->bytes_used;
502                                    dir_parts[2].offset = 0x50000;
503                                    len = dir_parts[2].offset + dir_parts[2].size;
504                                    len += (mtd->erasesize - 1);
505                                    len &= ~(mtd->erasesize - 1);
506                                    dir_parts[2].size = (len & 0x1ffffff) - dir_parts[2].offset;
507                                    dir_parts[3].offset = dir_parts[2].offset + dir_parts[2].size;
508                                    dir_parts[3].size = 0x7d0000 - dir_parts[3].offset;
509                                    dir_parts[6].offset = mtd->size - mtd->erasesize;   // board config
510                                    dir_parts[6].size = mtd->erasesize;
511                                    dir_parts[5].offset = dir_parts[6].offset;  //fis config
512                                    dir_parts[4].offset = dir_parts[5].offset - mtd->erasesize; //nvram                             
513                                    }
514                                break;
515#else
516                                //now scan for linux offset
517                                if (compex) {
518                                        dir_parts[1].offset = 0x30000;
519                                        dir_parts[1].size =
520                                            (dir_parts[2].offset -
521                                             dir_parts[1].offset) + rootsize;
522                                        break;
523                                } else {
524                                        p = (unsigned char *)(0xbf000000 +
525                                                              dir_parts
526                                                              [5].offset);
527                                        fis = (struct fis_image_desc *)p;
528                                        while (1) {
529                                                if (fis->name[0] == 0xff) {
530                                                        goto def;
531                                                }
532                                                if (!strcmp
533                                                    (fis->name, "RedBoot")) {
534                                                        printk(KERN_EMERG
535                                                               "found RedBoot partition at [0x%08lX]\n",
536                                                               fis->flash_base);
537                                                        dir_parts[0].size =
538                                                            fis->size;
539                                                        dir_parts[7].offset =
540                                                            dir_parts[0].size;
541                                                }
542                                                if (!strncmp
543                                                    (fis->name, "linux", 5)
544                                                    || !strncmp(fis->name,
545                                                                "vmlinux", 7)
546                                                    || !strncmp(fis->name,
547                                                                "kernel", 6)) {
548                                                        printk(KERN_EMERG
549                                                               "found linux partition at [0x%08lX]\n",
550                                                               fis->flash_base);
551                                                        dir_parts[1].offset =
552                                                            fis->flash_base &
553                                                            (mtd->size - 1);
554                                                        dir_parts[1].size =
555                                                            (dir_parts[2].offset
556                                                             -
557                                                             dir_parts
558                                                             [1].offset) +
559                                                            rootsize;
560                                                }
561                                                p += sizeof(struct
562                                                            fis_image_desc);
563                                                fis =
564                                                    (struct fis_image_desc *)p;
565                                        }
566                                        break;
567                                }
568#endif
569                        }
570                        offset += 4096;
571                        buf += 4096;
572                }
573              def:;
574                dir_parts[7].offset = 0;        // linux + nvram = phy size
575                dir_parts[7].size = mtd->size;  // linux + nvram = phy size
576                result = add_mtd_partitions(mtd, dir_parts, 8);
577        }
578        ar7100_flash_unlock();
579
580        return 0;
581}
582
583static void __exit ar7100_flash_exit(void)
584{
585        /*
586         * nothing to do
587         */
588}
589
590/*
591 * Primitives to implement flash operations
592 */
593static void ar7100_spi_write_enable()
594{
595        ar7100_reg_wr_nf(AR7100_SPI_FS, 1);
596        ar7100_reg_wr_nf(AR7100_SPI_WRITE, AR7100_SPI_CS_DIS);
597        ar7100_spi_bit_banger(AR7100_SPI_CMD_WREN);
598        ar7100_spi_go();
599}
600
601static void ar7100_spi_poll()
602{
603        int rd;
604
605        do {
606                ar7100_reg_wr_nf(AR7100_SPI_WRITE, AR7100_SPI_CS_DIS);
607                ar7100_spi_bit_banger(AR7100_SPI_CMD_RD_STATUS);
608                ar7100_spi_delay_8();
609                rd = (ar7100_reg_rd(AR7100_SPI_RD_STATUS) & 1);
610        } while (rd);
611}
612
613static void ar7100_spi_write_page(uint32_t addr, uint8_t * data, int len)
614{
615        int i;
616        uint8_t ch;
617
618        ar7100_spi_write_enable();
619        ar7100_spi_bit_banger(AR7100_SPI_CMD_PAGE_PROG);
620        ar7100_spi_send_addr(addr);
621
622        for (i = 0; i < len; i++) {
623                ch = *(data + i);
624                ar7100_spi_bit_banger(ch);
625        }
626
627        ar7100_spi_go();
628        ar7100_spi_poll();
629}
630
631static void ar7100_spi_sector_erase(uint32_t addr)
632{
633        ar7100_spi_write_enable();
634        ar7100_spi_bit_banger(AR7100_SPI_CMD_SECTOR_ERASE);
635        ar7100_spi_send_addr(addr);
636        ar7100_spi_go();
637//    display(0x7d);
638        ar7100_spi_poll();
639}
640
641module_init(ar7100_flash_init);
642module_exit(ar7100_flash_exit);
Note: See TracBrowser for help on using the repository browser.