Hi,
Attached patch is for supporting serial flash of Netgear WNR3500L.
Regards,
Tathagata <[email protected]>
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
[email protected]
https://lists.openwrt.org/mailman/listinfo/openwrt-devel