Hi, Attached patch is for supporting serial flash of Netgear WNR3500L.
Regards, Tathagata <tathag...@alumnux.com>
diff -Naur a/arch/mips/Kconfig b/arch/mips/Kconfig --- a/arch/mips/Kconfig 2010-05-13 18:22:35.000000000 +0530 +++ b/arch/mips/Kconfig 2010-05-13 18:19:37.000000000 +0530 @@ -9,6 +9,10 @@ select EMBEDDED select RTC_LIB if !LEMOTE_FULOONG2E +config WNR3500L + bool "Netgear WNR3500L" + default y + mainmenu "Linux/MIPS Kernel Configuration" menu "Machine selection" diff -Naur a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig --- a/drivers/mtd/devices/Kconfig 2010-05-13 18:23:09.000000000 +0530 +++ b/drivers/mtd/devices/Kconfig 2010-05-13 18:20:36.000000000 +0530 @@ -3,6 +3,9 @@ menu "Self-contained MTD device drivers" depends on MTD!=n +config MTD_SFLASH + bool "Broadcom Chipcommon Serial Flash support" + config MTD_PMC551 tristate "Ramix PMC551 PCI Mezzanine RAM card support" depends on PCI diff -Naur a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile --- a/drivers/mtd/devices/Makefile 2010-05-13 18:23:09.000000000 +0530 +++ b/drivers/mtd/devices/Makefile 2010-05-13 18:20:36.000000000 +0530 @@ -2,6 +2,7 @@ # linux/drivers/devices/Makefile # +obj-$(CONFIG_MTD_SFLASH) += sflash.o obj-$(CONFIG_MTD_DOC2000) += doc2000.o obj-$(CONFIG_MTD_DOC2001) += doc2001.o obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o diff -Naur a/drivers/mtd/devices/sflash.c b/drivers/mtd/devices/sflash.c --- a/drivers/mtd/devices/sflash.c 1970-01-01 05:30:00.000000000 +0530 +++ b/drivers/mtd/devices/sflash.c 2010-05-26 15:38:38.000000000 +0530 @@ -0,0 +1,769 @@ +/* + * Broadcom SiliconBackplane chipcommon serial flash interface + * + * Copyright 2007, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/mtd/compatmac.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <linux/ssb/ssb.h> +#include <linux/ssb/ssb_embedded.h> +#include <linux/jiffies.h> +#include <linux/sched.h> + +#define BCM4712_CHIP_ID 0x4712 /* 4712 chipcommon chipid */ + +/* flashcontrol action+opcodes for ST flashes */ +#define SFLASH_ST_CSA 0x1000 /* Keep chip select asserted */ + +#ifndef MIN +#define MIN(a, b) (((a) < (b))?(a):(b)) +#endif /* MIN */ + +void osl_delay(uint usec); +/* + * * Spin at most 'us' microseconds while 'exp' is true. + * * Caller should explicitly test 'exp' when this completes + * * and take appropriate error action if 'exp' is still true. + * */ +#define SPINWAIT(exp, us) { \ + uint countdown = (us) + 9; \ + while ((exp) && (countdown >= 10)) {\ + osl_delay(10); \ + countdown -= 10; \ + } \ +} + +/* Write len bytes starting at offset into buf. Returns number of bytes + * written. Caller should poll for completion. + */ +#define ST_RETRIES 3 + +#define GET_BYTE(ptr) (*(u8 *)((u32)(ptr) ^ 7)) + +extern struct ssb_bus ssb_bcm47xx; +#ifdef CONFIG_MTD_PARTITIONS +extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size); +#endif + +struct sflash_mtd { + struct semaphore lock; + struct mtd_info mtd; + struct mtd_erase_region_info region; +}; + +struct sflash { + u32 blocksize; /* Block size */ + u32 numblocks; /* Number of blocks */ + u32 type; /* Type */ + u32 size; /* Total size in bytes */ +}; + +static struct sflash wnr3500l_sflash; + +/* Private global state */ +static struct sflash_mtd sflash; + +#define OSL_UNCACHED(va) ((void *)KSEG1ADDR(va)) +#define OSL_CACHED(va) ((void *)KSEG0ADDR(va)) + +typedef unsigned long long int uintptr; + +/* Issue a serial flash command */ +static inline void sflash_cmd(u32 opcode) +{ + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHCTL, SSB_CHIPCO_FLASHCTL_START | opcode); + while ((chipco_read32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHCTL) & SSB_CHIPCO_FLASHCTL_BUSY)); +} + +int sflash_read(u32 offset, u32 len, unsigned char *buf) +{ + u8 *from, *to; + int cnt, i; + + if (!len) + return 0; + + if ((offset + len) > wnr3500l_sflash.size) + return -22; + + if ((len >= 4) && (offset & 3)) + cnt = 4 - (offset & 3); + else if ((len >= 4) && ((uintptr)buf & 3)) + cnt = 4 - ((uintptr)buf & 3); + else + cnt = len; + + if (ssb_bcm47xx.chipco.dev->id.revision == 12) + from = (u8 *)OSL_UNCACHED(SSB_FLASH2 + offset); + else + from = (u8 *)OSL_CACHED(SSB_FLASH2 + offset); + to = (u8 *)buf; + + if (cnt < 4) { + for (i = 0; i < cnt; i ++) { + /* Cannot use R_REG because in bigendian that will + * xor the address and we don't want that here. + */ + *to = *from; + from ++; + to ++; + } + return cnt; + } + + while (cnt >= 4) { + *(u32 *)to = *(u32 *)from; + from += 4; + to += 4; + cnt -= 4; + } + + return (len - cnt); +} + +/* Poll for command completion. Returns zero when complete. */ +int sflash_poll(u32 offset) +{ + u32 val; + + if (offset >= wnr3500l_sflash.size) + return -22; + + switch (wnr3500l_sflash.type) { + case SSB_CHIPCO_FLASHT_STSER: + /* Check for ST Write In Progress bit */ + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_RDSR); + val = chipco_read32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA) & SSB_CHIPCO_FLASHSTA_ST_WIP; + return val; + case SSB_CHIPCO_FLASHT_ATSER: + /* Check for Atmel Ready bit */ + sflash_cmd(SSB_CHIPCO_FLASHCTL_AT_STATUS); + val = chipco_read32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA) & SSB_CHIPCO_FLASHSTA_AT_READY; + return !val; + } + + return 0; +} + +void osl_delay(uint usec) +{ + uint d; + + while (usec > 0) { + d = MIN(usec, 1000); + udelay(d); + usec -= d; + } +} + +int sflash_write(u32 offset, u32 length, const unsigned char *buffer) +{ + struct sflash *sfl; + u32 off = offset, len = length; + const u8 *buf = buffer; + u8 data; + int ret = 0, ntry = 0; + bool is4712b0; + u32 page, byte, mask; + + if (!len) + return 0; + + sfl = &wnr3500l_sflash; + if ((off + len) > sfl->size) + return -22; + + switch (sfl->type) { + case SSB_CHIPCO_FLASHT_STSER: + is4712b0 = (ssb_bcm47xx.chip_id == BCM4712_CHIP_ID) && (ssb_bcm47xx.chipco.dev->id.revision == 3); + /* Enable writes */ + +retry: + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_WREN); + off = offset; + len = length; + buf = buffer; + ntry++; + if (is4712b0) { + mask = 1 << 14; + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, off); + data = GET_BYTE(buf); + buf++; + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA, data); + /* Set chip select */ + chipco_set32(&ssb_bcm47xx.chipco, SSB_CHIPCO_GPIOOUT, mask); + /* Issue a page program with the first byte */ + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_PP); + ret = 1; + off++; + len--; + while (len > 0) { + if ((off & 255) == 0) { + /* Page boundary, drop cs and return */ + chipco_mask32(&ssb_bcm47xx.chipco, SSB_CHIPCO_GPIOOUT, ~mask); + osl_delay(1); + if (!sflash_poll(off)) { + /* Flash rejected command */ + if (ntry <= ST_RETRIES) + goto retry; + else + return -11; + } + return ret; + } else { + /* Write single byte */ + data = GET_BYTE(buf); + buf++; + sflash_cmd(data); + } + ret++; + off++; + len--; + } + /* All done, drop cs */ + chipco_mask32(&ssb_bcm47xx.chipco, SSB_CHIPCO_GPIOOUT, ~mask); + osl_delay(1); + if (!sflash_poll(off)) { + /* Flash rejected command */ + if (ntry <= ST_RETRIES) + goto retry; + else + return -12; + } + } else if (ssb_bcm47xx.chipco.dev->id.revision >= 20) { + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, off); + data = GET_BYTE(buf); + buf++; + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA, data); + /* Issue a page program with CSA bit set */ + sflash_cmd(SFLASH_ST_CSA | SSB_CHIPCO_FLASHCTL_ST_PP); + ret = 1; + off++; + len--; + while (len > 0) { + if ((off & 255) == 0) { + /* Page boundary, poll droping cs and return */ + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHCTL, 0); + osl_delay(1); + if (sflash_poll(off) == 0) { + /* Flash rejected command */ + if (ntry <= ST_RETRIES) + goto retry; + else + return -11; + } + return ret; + } else { + /* Write single byte */ + data = GET_BYTE(buf); + buf++; + sflash_cmd(SFLASH_ST_CSA | data); + } + ret++; + off++; + len--; + } + /* All done, drop cs & poll */ + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHCTL, 0); + osl_delay(1); + if (sflash_poll(off) == 0) { + /* Flash rejected command */ + if (ntry <= ST_RETRIES) + goto retry; + else + return -12; + } + } else { + ret = 1; + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, off); + data = GET_BYTE(buf); + buf++; + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA, data); + /* Page program */ + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_PP); + } + break; + case SSB_CHIPCO_FLASHT_ATSER: + mask = sfl->blocksize - 1; + page = (off & ~mask) << 1; + byte = off & mask; + /* Read main memory page into buffer 1 */ + if (byte || (len < sfl->blocksize)) { + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, page); + sflash_cmd(SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD); + /* 250 us for AT45DB321B */ + SPINWAIT(sflash_poll(off), 1000); + } + /* Write into buffer 1 */ + for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, byte++); + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA, *buf++); + sflash_cmd(SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE); + } + /* Write buffer 1 into main memory page */ + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, page); + sflash_cmd(SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM); + break; + } + + return ret; +} + +/* Erase a region. Returns number of bytes scheduled for erasure. + * Caller should poll for completion. + */ +int sflash_erase(uint offset) +{ + struct sflash *sfl; + + sfl = &wnr3500l_sflash; + if (offset >= sfl->size) + return -22; + + switch (sfl->type) { + case SSB_CHIPCO_FLASHT_STSER: + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_WREN); + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, offset); + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_SE); + return sfl->blocksize; + case SSB_CHIPCO_FLASHT_ATSER: + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, offset << 1); + sflash_cmd(SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE); + return sfl->blocksize; + } + + return 0; +} + +/* + * writes the appropriate range of flash, a NULL buf simply erases + * the region of flash + */ +int sflash_commit(uint offset, uint len, const u8 *buf) +{ + struct sflash *sfl; + u8 *block = NULL, *cur_ptr, *blk_ptr; + uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder; + uint blk_offset, blk_len, copied; + int bytes, ret = 0; + + /* Check address range */ + if (len <= 0) + return 0; + + sfl = &wnr3500l_sflash; + if ((offset + len) > sfl->size) + return -1; + + blocksize = sfl->blocksize; + mask = blocksize - 1; + + /* Allocate a block of mem */ + if (!(block = kmalloc(blocksize, GFP_ATOMIC))) + return -1; + + while (len) { + /* Align offset */ + cur_offset = offset & ~mask; + cur_length = blocksize; + cur_ptr = block; + + remainder = blocksize - (offset & mask); + if (len < remainder) + cur_retlen = len; + else + cur_retlen = remainder; + + /* buf == NULL means erase only */ + if (buf) { + /* Copy existing data into holding block if necessary */ + if ((offset & mask) || (len < blocksize)) { + blk_offset = cur_offset; + blk_len = cur_length; + blk_ptr = cur_ptr; + + /* Copy entire block */ + while (blk_len) { + copied = sflash_read(blk_offset, blk_len, blk_ptr); + blk_offset += copied; + blk_len -= copied; + blk_ptr += copied; + } + } + + /* Copy input data into holding block */ + memcpy(cur_ptr + (offset & mask), buf, cur_retlen); + } + + /* Erase block */ + if ((ret = sflash_erase((uint) cur_offset)) < 0) + goto done; + while (sflash_poll((uint) cur_offset)); + + /* buf == NULL means erase only */ + if (!buf) { + offset += cur_retlen; + len -= cur_retlen; + continue; + } + + /* Write holding block */ + while (cur_length > 0) { + if ((bytes = sflash_write((uint) cur_offset, (uint) cur_length, (u8 *) cur_ptr)) < 0) { + ret = bytes; + goto done; + } + while (sflash_poll((uint) cur_offset)); + cur_offset += bytes; + cur_length -= bytes; + cur_ptr += bytes; + } + + offset += cur_retlen; + len -= cur_retlen; + buf += cur_retlen; + } + + ret = len; +done: + if (block) + kfree(block); + return ret; +} + +static int sflash_mtd_poll(u32 offset, int timeout) +{ + int now = jiffies; + int ret = 0; + + for (;;) { + if (!sflash_poll(offset)) { + ret = 0; + break; + } + if (time_after((unsigned long)jiffies, (unsigned long)(now + timeout))) { + printk(KERN_ERR "%s(): timeout\n", __func__); + ret = -ETIMEDOUT; + break; + } + udelay(1); + } + + return ret; +} + +static int +sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; + int bytes, ret = 0; + + if (retlen) + *retlen = 0; + + /* Check address range */ + if (!len) + return 0; + if ((from + len) > mtd->size) + return -EINVAL; + + down(&sflash->lock); + + while (len) { + if ((bytes = sflash_read((u32) from, len, buf)) < 0) { + ret = bytes; + break; + } + from += (loff_t) bytes; + len -= bytes; + buf += bytes; + if (retlen) + *retlen += bytes; + } + + up(&sflash->lock); + + return ret; +} + +static int +sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; + int bytes, ret = 0; + + if (retlen) + *retlen = 0; + + /* Check address range */ + if (!len) + return 0; + if ((to + len) > mtd->size) + return -EINVAL; + + down(&sflash->lock); + + while (len) { + if ((bytes = sflash_write((u32) to, len, buf)) < 0) { + ret = bytes; + break; + } + if ((ret = sflash_mtd_poll((u32) to, HZ / 10))) + break; + to += (loff_t) bytes; + len -= bytes; + buf += bytes; + if (retlen) + *retlen += bytes; + } + + up(&sflash->lock); + + return ret; +} + +static int +sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; + int i, j, ret = 0; + u32 addr, len; + + /* Check address range */ + if (!erase->len) + return 0; + if ((erase->addr + erase->len) > mtd->size) + return -EINVAL; + + addr = erase->addr; + len = erase->len; + + down(&sflash->lock); + + /* Ensure that requested region is aligned */ + for (i = 0; i < mtd->numeraseregions; i++) { + for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { + if (addr == mtd->eraseregions[i].offset + mtd->eraseregions[i].erasesize * j && len >= mtd->eraseregions[i].erasesize) { + if ((ret = sflash_erase(addr)) < 0) + break; + if ((ret = sflash_mtd_poll(addr, 10 * HZ))) + break; + addr += mtd->eraseregions[i].erasesize; + len -= mtd->eraseregions[i].erasesize; + } + } + if (ret) + break; + } + + up(&sflash->lock); + + /* Set erase status */ + if (ret) + erase->state = MTD_ERASE_FAILED; + else + erase->state = MTD_ERASE_DONE; + + /* Call erase callback */ + if (erase->callback) + erase->callback(erase); + + return ret; +} + +u32 sflash_init(void) +{ + u32 id, id2; + + memset(&wnr3500l_sflash, 0, sizeof(wnr3500l_sflash)); + + wnr3500l_sflash.type = ssb_bcm47xx.chipco.capabilities & SSB_CHIPCO_CAP_FLASHT; + + switch (wnr3500l_sflash.type) { + case SSB_CHIPCO_FLASHT_STSER: + /* Probe for ST chips */ + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_DP); + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_RSIG); + id = chipco_read32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA); + switch (id) { + case 0x11: + /* ST M25P20 2 Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 64 * 1024; + wnr3500l_sflash.numblocks = 4; + break; + case 0x12: + /* ST M25P40 4 Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 64 * 1024; + wnr3500l_sflash.numblocks = 8; + break; + case 0x13: + /* ST M25P80 8 Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 64 * 1024; + wnr3500l_sflash.numblocks = 16; + break; + case 0x14: + /* ST M25P16 16 Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 64 * 1024; + wnr3500l_sflash.numblocks = 32; + break; + case 0x15: + /* ST M25P32 32 Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 64 * 1024; + wnr3500l_sflash.numblocks = 64; + break; + case 0x16: + /* ST M25P64 64 Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 64 * 1024; + wnr3500l_sflash.numblocks = 128; + break; + case 0xbf: + chipco_write32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHADDR, 0x1); + sflash_cmd(SSB_CHIPCO_FLASHCTL_ST_RSIG); + id2 = chipco_read32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA); + if (id2 == 0x44) { + /* SST M25VF80 4 Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 64 * 1024; + wnr3500l_sflash.numblocks = 8; + } + break; + } + break; + + case SSB_CHIPCO_FLASHT_ATSER: + /* Probe for Atmel chips */ + sflash_cmd(SSB_CHIPCO_FLASHCTL_AT_STATUS); + id = chipco_read32(&ssb_bcm47xx.chipco, SSB_CHIPCO_FLASHDATA) & 0x3C; + switch (id) { + case 0xc: + /* Atmel AT45DB011 1Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 256; + wnr3500l_sflash.numblocks = 512; + break; + case 0x14: + /* Atmel AT45DB021 2Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 256; + wnr3500l_sflash.numblocks = 1024; + break; + case 0x1c: + /* Atmel AT45DB041 4Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 256; + wnr3500l_sflash.numblocks = 2048; + break; + case 0x24: + /* Atmel AT45DB081 8Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 256; + wnr3500l_sflash.numblocks = 4096; + break; + case 0x2c: + /* Atmel AT45DB161 16Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 512; + wnr3500l_sflash.numblocks = 4096; + break; + case 0x34: + /* Atmel AT45DB321 32Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 512; + wnr3500l_sflash.numblocks = 8192; + break; + case 0x3c: + /* Atmel AT45DB642 64Mbit Serial Flash */ + wnr3500l_sflash.blocksize = 1024; + wnr3500l_sflash.numblocks = 8192; + break; + } + break; + } + + wnr3500l_sflash.size = wnr3500l_sflash.blocksize * wnr3500l_sflash.numblocks; + return wnr3500l_sflash.size; +} + +int __init sflash_mtd_init(void) +{ + int ret = 0; + u32 i; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif + + memset(&sflash, 0, sizeof(struct sflash_mtd)); + init_MUTEX(&sflash.lock); + + if (ssb_bcm47xx.mmio == NULL) { + printk(KERN_ERR "sflash: error mapping registers\n"); + ret = -EIO; + goto fail; + } + + /* Initialize serial flash access */ + if (0 == sflash_init()) { + printk(KERN_ERR "%s(): found no supported devices\n", __func__); + ret = -ENODEV; + goto fail; + } + + /* Setup region info */ + sflash.region.offset = 0; + sflash.region.erasesize = wnr3500l_sflash.blocksize; + sflash.region.numblocks = wnr3500l_sflash.numblocks; + if (sflash.region.erasesize > sflash.mtd.erasesize) + sflash.mtd.erasesize = sflash.region.erasesize; + sflash.mtd.size = wnr3500l_sflash.size; + sflash.mtd.numeraseregions = 1; + + /* Register with MTD */ + sflash.mtd.name = "sflash"; + sflash.mtd.type = MTD_NORFLASH; + sflash.mtd.flags = MTD_CAP_NORFLASH; + sflash.mtd.eraseregions = &sflash.region; + sflash.mtd.erase = sflash_mtd_erase; + sflash.mtd.read = sflash_mtd_read; + sflash.mtd.write = sflash_mtd_write; + sflash.mtd.writesize = 1; + sflash.mtd.priv = &sflash; + sflash.mtd.owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size); + for (i = 0; parts[i].name; i++); + ret = add_mtd_partitions(&sflash.mtd, parts, i); +#else + ret = add_mtd_device(&sflash.mtd); +#endif + if (ret) { + printk(KERN_ERR "sflash: add_mtd failed\n"); + goto fail; + } + + return 0; + + fail: + return ret; +} + +void __exit sflash_mtd_exit(void) +{ +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(&sflash.mtd); +#else + del_mtd_device(&sflash.mtd); +#endif +} + +module_init(sflash_mtd_init); +module_exit(sflash_mtd_exit); diff -Naur a/drivers/mtd/maps/bcm47xx-flash.c b/drivers/mtd/maps/bcm47xx-flash.c --- a/drivers/mtd/maps/bcm47xx-flash.c 2010-05-13 18:23:10.000000000 +0530 +++ b/drivers/mtd/maps/bcm47xx-flash.c 2010-05-26 15:33:20.000000000 +0530 @@ -53,6 +53,11 @@ #endif #include <asm/io.h> +#ifdef CONFIG_WNR3500L +#include <linux/jffs2.h> +#include <linux/magic.h> +#include "../../../fs/squashfs/squashfs_fs.h" +#endif #define TRX_MAGIC 0x30524448 /* "HDR0" */ #define TRX_VERSION 1 @@ -273,6 +278,19 @@ if (mtd->read(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf)) return 0; +#ifdef CONFIG_WNR3500L + if (*((__u32 *) buf) == SQUASHFS_MAGIC) { + printk("%s: Filesystem type: squashfs\n", mtd->name); + + /* Update the squashfs partition size based on the superblock info */ + part->size = sb->bytes_used; + len = part->offset + part->size; + len += (mtd->erasesize - 1); + len &= ~(mtd->erasesize - 1); + part->size = len - part->offset; + } + else +#endif /* Move the fs outside of the trx */ part->size = 0; @@ -355,6 +373,10 @@ find_root(mtd,size,&bcm47xx_parts[2]); bcm47xx_parts[2].size = size - bcm47xx_parts[2].offset - bcm47xx_parts[3].size; +#ifdef CONFIG_WNR3500L + bcm47xx_parts[2].size = bcm47xx_parts[2].size - 0xB0000; +#endif + return bcm47xx_parts; } #endif @@ -385,6 +407,18 @@ bcm47xx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); #endif +#ifdef CONFIG_WNR3500L + if (ssb_bcm47xx.chipco.dev != NULL) { + u32 fltype; + fltype = ssb_bcm47xx.chipco.capabilities & SSB_CHIPCO_CAP_FLASHT; + if (fltype != SSB_CHIPCO_FLASHT_PARA) { + printk("pflash: found no supported devices\n"); + ret = -ENODEV; + goto fail; + } + } +#endif + if (!bcm47xx_map.virt) { printk("Failed to ioremap\n"); return -EIO;
_______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel