X-Git-Url: http://git.rohieb.name/openwrt.git/blobdiff_plain/17c7b6c3fdc48301e50d22cc6138ede16bd1be24..2c5bea6134b3f2a601323b48c4d6c58ced2cf727:/target/linux/brcm47xx/files/drivers/mtd/maps/bcm47xx-flash.c diff --git a/target/linux/brcm47xx/files/drivers/mtd/maps/bcm47xx-flash.c b/target/linux/brcm47xx/files/drivers/mtd/maps/bcm47xx-flash.c index 6a82f362c..4585c4f9e 100644 --- a/target/linux/brcm47xx/files/drivers/mtd/maps/bcm47xx-flash.c +++ b/target/linux/brcm47xx/files/drivers/mtd/maps/bcm47xx-flash.c @@ -52,6 +52,8 @@ #include #endif #include +#include +#include #define TRX_MAGIC 0x30524448 /* "HDR0" */ @@ -69,18 +71,32 @@ struct trx_header { u32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ }; +/* for Edimax Print servers which use an additional header + * then the firmware on flash looks like : + * EDIMAX HEADER | TRX HEADER + * As this header is 12 bytes long we have to handle it + * and skip it to find the TRX header + */ +#define EDIMAX_PS_HEADER_MAGIC 0x36315350 /* "PS16" */ +#define EDIMAX_PS_HEADER_LEN 0xc /* 12 bytes long for edimax header */ + #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) #define NVRAM_SPACE 0x8000 #define WINDOW_ADDR 0x1fc00000 #define WINDOW_SIZE 0x400000 #define BUSWIDTH 2 +#define ROUTER_NETGEAR_WGR614L 1 +#define ROUTER_NETGEAR_WNR834B 2 +#define ROUTER_NETGEAR_WNDR3300 3 +#define ROUTER_NETGEAR_WNR3500L 4 + #ifdef CONFIG_SSB -extern struct ssb_bus ssb; +extern struct ssb_bus ssb_bcm47xx; #endif -static struct mtd_info *bcm947xx_mtd; +static struct mtd_info *bcm47xx_mtd; -static void bcm947xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static void bcm47xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { if (len==1) { memcpy_fromio(to, map->virt + from, len); @@ -96,7 +112,7 @@ static void bcm947xx_map_copy_from(struct map_info *map, void *to, unsigned long } } -static struct map_info bcm947xx_map = { +static struct map_info bcm47xx_map = { name: "Physically mapped flash", size: WINDOW_SIZE, bankwidth: BUSWIDTH, @@ -105,7 +121,7 @@ static struct map_info bcm947xx_map = { #ifdef CONFIG_MTD_PARTITIONS -static struct mtd_partition bcm947xx_parts[] = { +static struct mtd_partition bcm47xx_parts[] = { { name: "cfe", offset: 0, size: 0, mask_flags: MTD_WRITEABLE, }, { name: "linux", offset: 0, size: 0, }, { name: "rootfs", offset: 0, size: 0, }, @@ -138,6 +154,15 @@ find_cfe_size(struct mtd_info *mtd, size_t size) len != sizeof(buf)) continue; + if (le32_to_cpu(trx->magic) == EDIMAX_PS_HEADER_MAGIC) { + if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN, + sizeof(buf), &len, buf) || len != sizeof(buf)) { + continue; + } else { + printk(KERN_NOTICE"Found edimax header\n"); + } + } + /* found a TRX header */ if (le32_to_cpu(trx->magic) == TRX_MAGIC) { goto found; @@ -210,7 +235,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, remove_wait_queue(&wait_q, &wait); /* - * Next, writhe data to flash. + * Next, write data to flash. */ ret = mtd->write (mtd, pos, len, &retlen, buf); @@ -222,6 +247,37 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, } +static int __init +find_dual_image_off (struct mtd_info *mtd, size_t size) +{ + struct trx_header trx; + int off, blocksize; + size_t len; + + blocksize = mtd->erasesize; + if (blocksize < 0x10000) + blocksize = 0x10000; + + for (off = (128*1024); off < size; off += blocksize) { + memset(&trx, 0xe5, sizeof(trx)); + /* + * Read into buffer + */ + if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) || + len != sizeof(trx)) + continue; + /* found last TRX header */ + if (le32_to_cpu(trx.magic) == TRX_MAGIC){ + if (le32_to_cpu(trx.flag_version >> 16)==2){ + printk("dual image TRX header found\n"); + return size/2; + } else { + return 0; + } + } + } + return 0; +} static int __init @@ -229,10 +285,10 @@ find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) { struct trx_header trx, *trx2; unsigned char buf[512], *block; - int off, blocksize; + int off, blocksize, trxoff = 0; u32 i, crc = ~0; size_t len; - struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; + bool edimax = false; blocksize = mtd->erasesize; if (blocksize < 0x10000) @@ -248,6 +304,19 @@ find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) len != sizeof(trx)) continue; + /* found an edimax header */ + if (le32_to_cpu(trx.magic) == EDIMAX_PS_HEADER_MAGIC) { + /* read the correct trx header */ + if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN, + sizeof(trx), &len, (char *) &trx) || + len != sizeof(trx)) { + continue; + } else { + printk(KERN_NOTICE"Found an edimax ps header\n"); + edimax = true; + } + } + /* found a TRX header */ if (le32_to_cpu(trx.magic) == TRX_MAGIC) { part->offset = le32_to_cpu(trx.offsets[2]) ? : @@ -256,6 +325,10 @@ find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) part->size -= part->offset; part->offset += off; + if (edimax) { + off += EDIMAX_PS_HEADER_LEN; + trxoff = EDIMAX_PS_HEADER_LEN; + } goto found; } @@ -267,6 +340,7 @@ find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) return -1; found: + printk(KERN_NOTICE"TRX offset : %lx\n", trxoff); if (part->size == 0) return 0; @@ -291,7 +365,7 @@ find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) /* read first eraseblock from the trx */ block = kmalloc(mtd->erasesize, GFP_KERNEL); trx2 = (struct trx_header *) block; - if (mtd->read(mtd, off, mtd->erasesize, &len, block) || len != mtd->erasesize) { + if (mtd->read(mtd, off - trxoff, mtd->erasesize, &len, block) || len != mtd->erasesize) { printk("Error accessing the first trx eraseblock\n"); return 0; } @@ -301,10 +375,10 @@ find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) 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); /* Write updated trx header to the flash */ - memcpy(block, &trx, sizeof(trx)); + memcpy(block + trxoff, &trx, sizeof(trx)); if (mtd->unlock) - mtd->unlock(mtd, off, mtd->erasesize); - erase_write(mtd, off, mtd->erasesize, block); + mtd->unlock(mtd, off - trxoff, mtd->erasesize); + erase_write(mtd, off - trxoff, mtd->erasesize, block); if (mtd->sync) mtd->sync(mtd); kfree(block); @@ -314,55 +388,137 @@ find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) return part->size; } +static int get_router(void) +{ + char buf[20]; + u32 boardnum = 0; + u16 boardtype = 0; + u16 boardrev = 0; + u32 boardflags = 0; + u16 sdram_init = 0; + u16 cardbus = 0; + + if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0 || + cfe_getenv("boardnum", buf, sizeof(buf)) >= 0) + boardnum = simple_strtoul(buf, NULL, 0); + if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0 || + cfe_getenv("boardtype", buf, sizeof(buf)) >= 0) + boardtype = simple_strtoul(buf, NULL, 0); + if (nvram_getenv("boardrev", buf, sizeof(buf)) >= 0 || + cfe_getenv("boardrev", buf, sizeof(buf)) >= 0) + boardrev = simple_strtoul(buf, NULL, 0); + if (nvram_getenv("boardflags", buf, sizeof(buf)) >= 0 || + cfe_getenv("boardflags", buf, sizeof(buf)) >= 0) + boardflags = simple_strtoul(buf, NULL, 0); + if (nvram_getenv("sdram_init", buf, sizeof(buf)) >= 0 || + cfe_getenv("sdram_init", buf, sizeof(buf)) >= 0) + sdram_init = simple_strtoul(buf, NULL, 0); + if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0 || + cfe_getenv("cardbus", buf, sizeof(buf)) >= 0) + cardbus = simple_strtoul(buf, NULL, 0); + + if ((boardnum == 8 || boardnum == 01) + && boardtype == 0x0472 && cardbus == 1) { + /* Netgear WNR834B, Netgear WNR834Bv2 */ + return ROUTER_NETGEAR_WNR834B; + } + + if (boardnum == 01 && boardtype == 0x0472 && boardrev == 0x23) { + /* Netgear WNDR-3300 */ + return ROUTER_NETGEAR_WNDR3300; + } + + if ((boardnum == 83258 || boardnum == 01) + && boardtype == 0x048e + && (boardrev == 0x11 || boardrev == 0x10) + && boardflags == 0x750 + && sdram_init == 0x000A) { + /* Netgear WGR614v8/L/WW 16MB ram, cfe v1.3 or v1.5 */ + return ROUTER_NETGEAR_WGR614L; + } + + if ((boardnum == 1 || boardnum == 3500) + && boardtype == 0x04CF + && (boardrev == 0x1213 || boardrev == 02)) { + /* Netgear WNR3500v2/U/L */ + return ROUTER_NETGEAR_WNR3500L; + } + + return 0; +} + struct mtd_partition * __init init_mtd_partitions(struct mtd_info *mtd, size_t size) { int cfe_size; + int dual_image_offset = 0; + /* e.g Netgear 0x003e0000-0x003f0000 : "board_data", we exclude this + * part from our mapping to prevent overwriting len/checksum on e.g. + * Netgear WGR614v8/L/WW + */ + int board_data_size = 0; + + switch (get_router()) { + case ROUTER_NETGEAR_WGR614L: + case ROUTER_NETGEAR_WNR834B: + case ROUTER_NETGEAR_WNDR3300: + case ROUTER_NETGEAR_WNR3500L: + /* Netgear: checksum is @ 0x003AFFF8 for 4M flash or checksum + * is @ 0x007AFFF8 for 8M flash + */ + board_data_size = 4 * mtd->erasesize; + break; + } if ((cfe_size = find_cfe_size(mtd,size)) < 0) return NULL; /* boot loader */ - bcm947xx_parts[0].offset = 0; - bcm947xx_parts[0].size = cfe_size; + bcm47xx_parts[0].offset = 0; + bcm47xx_parts[0].size = cfe_size; /* nvram */ if (cfe_size != 384 * 1024) { - bcm947xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize); - bcm947xx_parts[3].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize); + bcm47xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize); + bcm47xx_parts[3].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize); } else { /* nvram (old 128kb config partition on netgear wgt634u) */ - bcm947xx_parts[3].offset = bcm947xx_parts[0].size; - bcm947xx_parts[3].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize); + bcm47xx_parts[3].offset = bcm47xx_parts[0].size; + bcm47xx_parts[3].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize); } + /* dual image offset*/ + printk("Looking for dual image\n"); + dual_image_offset=find_dual_image_off(mtd,size); /* linux (kernel and rootfs) */ if (cfe_size != 384 * 1024) { - bcm947xx_parts[1].offset = bcm947xx_parts[0].size; - bcm947xx_parts[1].size = bcm947xx_parts[3].offset - - bcm947xx_parts[1].offset; + bcm47xx_parts[1].offset = bcm47xx_parts[0].size; + bcm47xx_parts[1].size = bcm47xx_parts[3].offset - dual_image_offset - + bcm47xx_parts[1].offset - board_data_size; } else { /* do not count the elf loader, which is on one block */ - bcm947xx_parts[1].offset = bcm947xx_parts[0].size + - bcm947xx_parts[3].size + mtd->erasesize; - bcm947xx_parts[1].size = size - - bcm947xx_parts[0].size - - (2*bcm947xx_parts[3].size) - - mtd->erasesize; + bcm47xx_parts[1].offset = bcm47xx_parts[0].size + + bcm47xx_parts[3].size + mtd->erasesize; + bcm47xx_parts[1].size = size - + bcm47xx_parts[0].size - + (2*bcm47xx_parts[3].size) - + mtd->erasesize - board_data_size; } /* find and size rootfs */ - find_root(mtd,size,&bcm947xx_parts[2]); - bcm947xx_parts[2].size = size - bcm947xx_parts[2].offset - bcm947xx_parts[3].size; + find_root(mtd,size,&bcm47xx_parts[2]); + bcm47xx_parts[2].size = size - dual_image_offset - + bcm47xx_parts[2].offset - + bcm47xx_parts[3].size - board_data_size; - return bcm947xx_parts; + return bcm47xx_parts; } #endif -int __init init_bcm947xx_map(void) +int __init init_bcm47xx_map(void) { #ifdef CONFIG_SSB - struct ssb_mipscore *mcore = &ssb.mipscore; + struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore; #endif size_t size; int ret = 0; @@ -376,40 +532,41 @@ int __init init_bcm947xx_map(void) u32 window_size = mcore->flash_window_size; printk("flash init: 0x%08x 0x%08x\n", window, window_size); - bcm947xx_map.phys = window; - bcm947xx_map.size = window_size; - bcm947xx_map.virt = ioremap_nocache(window, window_size); + bcm47xx_map.phys = window; + bcm47xx_map.size = window_size; + bcm47xx_map.bankwidth = mcore->flash_buswidth; + bcm47xx_map.virt = ioremap_nocache(window, window_size); #else printk("flash init: 0x%08x 0x%08x\n", WINDOW_ADDR, WINDOW_SIZE); - bcm947xx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); + bcm47xx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); #endif - if (!bcm947xx_map.virt) { + if (!bcm47xx_map.virt) { printk("Failed to ioremap\n"); return -EIO; } - simple_map_init(&bcm947xx_map); + simple_map_init(&bcm47xx_map); - if (!(bcm947xx_mtd = do_map_probe("cfi_probe", &bcm947xx_map))) { + if (!(bcm47xx_mtd = do_map_probe("cfi_probe", &bcm47xx_map))) { printk("Failed to do_map_probe\n"); - iounmap((void *)bcm947xx_map.virt); + iounmap((void *)bcm47xx_map.virt); return -ENXIO; } /* override copy_from routine */ - bcm947xx_map.copy_from = bcm947xx_map_copy_from; + bcm47xx_map.copy_from = bcm47xx_map_copy_from; - bcm947xx_mtd->owner = THIS_MODULE; + bcm47xx_mtd->owner = THIS_MODULE; - size = bcm947xx_mtd->size; + size = bcm47xx_mtd->size; printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, WINDOW_ADDR); #ifdef CONFIG_MTD_PARTITIONS - parts = init_mtd_partitions(bcm947xx_mtd, size); + parts = init_mtd_partitions(bcm47xx_mtd, size); for (i = 0; parts[i].name; i++); - ret = add_mtd_partitions(bcm947xx_mtd, parts, i); + ret = add_mtd_partitions(bcm47xx_mtd, parts, i); if (ret) { printk(KERN_ERR "Flash: add_mtd_partitions failed\n"); goto fail; @@ -418,22 +575,22 @@ int __init init_bcm947xx_map(void) return 0; fail: - if (bcm947xx_mtd) - map_destroy(bcm947xx_mtd); - if (bcm947xx_map.virt) - iounmap((void *)bcm947xx_map.virt); - bcm947xx_map.virt = 0; + if (bcm47xx_mtd) + map_destroy(bcm47xx_mtd); + if (bcm47xx_map.virt) + iounmap((void *)bcm47xx_map.virt); + bcm47xx_map.virt = 0; return ret; } -void __exit cleanup_bcm947xx_map(void) +void __exit cleanup_bcm47xx_map(void) { #ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(bcm947xx_mtd); + del_mtd_partitions(bcm47xx_mtd); #endif - map_destroy(bcm947xx_mtd); - iounmap((void *)bcm947xx_map.virt); + map_destroy(bcm47xx_mtd); + iounmap((void *)bcm47xx_map.virt); } -module_init(init_bcm947xx_map); -module_exit(cleanup_bcm947xx_map); +module_init(init_bcm47xx_map); +module_exit(cleanup_bcm47xx_map);