diff options
author | Hauke Mehrtens <hauke@hauke-m.de> | 2012-03-14 22:59:39 +0000 |
---|---|---|
committer | Hauke Mehrtens <hauke@hauke-m.de> | 2012-03-14 22:59:39 +0000 |
commit | cee31ca2385b5af98d1813df106887042709b7b1 (patch) | |
tree | b3f9c9cc56610ce5fc0b882249af00f5df3120b2 /target/linux/brcm47xx/patches-3.2/030-bcm47xx-bcma-nandflash.patch | |
parent | 7fee567dc04fe5614504e0d33f7c8aa46718ce2e (diff) | |
download | mtk-20170518-cee31ca2385b5af98d1813df106887042709b7b1.zip mtk-20170518-cee31ca2385b5af98d1813df106887042709b7b1.tar.gz mtk-20170518-cee31ca2385b5af98d1813df106887042709b7b1.tar.bz2 |
brcm47xx: add support for NAND flash on devices using bcma
This patch add support for the NAND flash on BCM5357 based devices like the WNR3500Lv2.
Thank you Tathagata Das <tathagata@alumnux.com> for the patch
SVN-Revision: 30943
Diffstat (limited to 'target/linux/brcm47xx/patches-3.2/030-bcm47xx-bcma-nandflash.patch')
-rw-r--r-- | target/linux/brcm47xx/patches-3.2/030-bcm47xx-bcma-nandflash.patch | 1138 |
1 files changed, 1138 insertions, 0 deletions
diff --git a/target/linux/brcm47xx/patches-3.2/030-bcm47xx-bcma-nandflash.patch b/target/linux/brcm47xx/patches-3.2/030-bcm47xx-bcma-nandflash.patch new file mode 100644 index 0000000..4f4386e --- /dev/null +++ b/target/linux/brcm47xx/patches-3.2/030-bcm47xx-bcma-nandflash.patch @@ -0,0 +1,1138 @@ +--- a/arch/mips/bcm47xx/bus.c ++++ b/arch/mips/bcm47xx/bus.c +@@ -2,6 +2,7 @@ + * BCM947xx nvram variable access + * + * Copyright (C) 2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -92,3 +93,9 @@ void bcm47xx_sflash_struct_ssb_init(stru + sflash->numblocks = scc->sflash.numblocks; + sflash->size = scc->sflash.size; + } ++ ++void bcm47xx_nflash_struct_bcma_init(struct bcm47xx_nflash *nflash, struct bcma_drv_cc *bcc) ++{ ++ nflash->nflash_type = BCM47XX_BUS_TYPE_BCMA; ++ nflash->bcc = bcc; ++} +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2005 Broadcom Corporation + * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2010-2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -21,6 +22,7 @@ + #include <asm/mach-bcm47xx/nvram.h> + #include <asm/mach-bcm47xx/bcm47xx.h> + #include <asm/mach-bcm47xx/bus.h> ++#include <linux/mtd/bcm47xx_nand.h> + + static char nvram_buf[NVRAM_SPACE]; + +@@ -137,6 +139,51 @@ found: + return 0; + } + ++static int early_nvram_init_nflash(void) ++{ ++ struct nvram_header *header; ++ u32 off; ++ int ret; ++ int len; ++ u32 flash_size = bcm47xx_nflash.size; ++ u8 tmpbuf[NFL_SECTOR_SIZE]; ++ int i; ++ u32 *src, *dst; ++ ++ /* check if the struct is already initilized */ ++ if (!flash_size) ++ return -1; ++ ++ cfe_env = 0; ++ ++ off = FLASH_MIN; ++ while (off <= flash_size) { ++ ret = bcma_nflash_read(bcm47xx_nflash.bcc, off, NFL_SECTOR_SIZE, tmpbuf); ++ if (ret != NFL_SECTOR_SIZE) ++ goto done; ++ header = (struct nvram_header *)tmpbuf; ++ if (header->magic == NVRAM_HEADER) ++ goto found; ++ off <<= 1; ++ } ++ ++ ret = -1; ++ goto done; ++ ++found: ++ len = header->len; ++ header = (struct nvram_header *) KSEG1ADDR(NAND_FLASH1 + off); ++ src = (u32 *) header; ++ dst = (u32 *) nvram_buf; ++ for (i = 0; i < sizeof(struct nvram_header); i += 4) ++ *dst++ = *src++; ++ for (; i < len && i < NVRAM_SPACE; i += 4) ++ *dst++ = *src++; ++ ret = 0; ++done: ++ return ret; ++} ++ + static void early_nvram_init(void) + { + int err = 0; +@@ -163,6 +210,10 @@ static void early_nvram_init(void) + err = early_nvram_init_sflash(); + if (err < 0) + printk(KERN_WARNING "can not read from flash: %i\n", err); ++ } else if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_NFLASH) { ++ err = early_nvram_init_nflash(); ++ if (err < 0) ++ printk(KERN_WARNING "can not read from nflash: %i\n", err); + } else { + printk(KERN_WARNING "unknow flash type\n"); + } +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2006 Michael Buesch <m@bues.ch> + * Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org> + * Copyright (C) 2010-2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -46,6 +47,7 @@ enum bcm47xx_bus_type bcm47xx_bus_type; + EXPORT_SYMBOL(bcm47xx_bus_type); + + struct bcm47xx_sflash bcm47xx_sflash; ++struct bcm47xx_nflash bcm47xx_nflash; + + static void bcm47xx_machine_restart(char *command) + { +@@ -325,6 +327,9 @@ static void __init bcm47xx_register_bcma + + if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_SFLASH) + bcm47xx_sflash_struct_bcma_init(&bcm47xx_sflash, &bcm47xx_bus.bcma.bus.drv_cc); ++ ++ if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_NFLASH) ++ bcm47xx_nflash_struct_bcma_init(&bcm47xx_nflash, &bcm47xx_bus.bcma.bus.drv_cc); + } + #endif + +@@ -395,6 +400,19 @@ static struct platform_device bcm47xx_sf + .num_resources = 1, + }; + ++static struct resource bcm47xx_nflash_resource = { ++ .name = "bcm47xx_nflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++static struct platform_device bcm47xx_nflash_dev = { ++ .name = "bcm47xx_nflash", ++ .resource = &bcm47xx_nflash_resource, ++ .num_resources = 1, ++}; ++ + static int __init bcm47xx_register_flash(void) + { + #ifdef CONFIG_BCM47XX_SSB +@@ -429,6 +447,9 @@ static int __init bcm47xx_register_flash + } else if (drv_cc->flash_type == BCMA_SFLASH) { + bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash; + return platform_device_register(&bcm47xx_sflash_dev); ++ } else if (drv_cc->flash_type == BCMA_NFLASH) { ++ bcm47xx_nflash_dev.dev.platform_data = &bcm47xx_nflash; ++ return platform_device_register(&bcm47xx_nflash_dev); + } else { + printk(KERN_ERR "No flash device found\n"); + return -1; +--- a/arch/mips/include/asm/mach-bcm47xx/bus.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bus.h +@@ -2,6 +2,7 @@ + * BCM947xx nvram variable access + * + * Copyright (C) 2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -13,6 +14,7 @@ + #include <linux/bcma/bcma.h> + #include <linux/mtd/mtd.h> + #include <bcm47xx.h> ++#include <linux/mtd/nand.h> + + struct bcm47xx_sflash { + enum bcm47xx_bus_type sflash_type; +@@ -38,3 +40,18 @@ void bcm47xx_sflash_struct_bcma_init(str + void bcm47xx_sflash_struct_ssb_init(struct bcm47xx_sflash *sflash, struct ssb_chipcommon *scc); + + extern struct bcm47xx_sflash bcm47xx_sflash; ++ ++struct bcm47xx_nflash { ++ enum bcm47xx_bus_type nflash_type; ++ struct bcma_drv_cc *bcc; ++ ++ u32 size; /* Total size in bytes */ ++ u32 next_opcode; /* Next expected command from upper NAND layer */ ++ ++ struct mtd_info mtd; ++ struct nand_chip nand; ++}; ++ ++void bcm47xx_nflash_struct_bcma_init(struct bcm47xx_nflash *nflash, struct bcma_drv_cc *bcc); ++ ++extern struct bcm47xx_nflash bcm47xx_nflash; +--- a/drivers/bcma/bcma_private.h ++++ b/drivers/bcma/bcma_private.h +@@ -46,6 +46,11 @@ u32 bcma_pmu_get_clockcpu(struct bcma_dr + int bcma_sflash_init(struct bcma_drv_cc *cc); + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++/* driver_chipcommon_nflash.c */ ++int bcma_nflash_init(struct bcma_drv_cc *cc); ++#endif /* CONFIG_BCMA_NFLASH */ ++ + #ifdef CONFIG_BCMA_HOST_PCI + /* host_pci.c */ + extern int __init bcma_host_pci_init(void); +--- /dev/null ++++ b/drivers/bcma/driver_chipcommon_nflash.c +@@ -0,0 +1,154 @@ ++/* ++ * BCMA nand flash interface ++ * ++ * Copyright 2011, Tathagata Das <tathagata@alumnux.com> ++ * Copyright 2010, Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include <linux/bcma/bcma.h> ++#include <linux/bcma/bcma_driver_chipcommon.h> ++#include <linux/delay.h> ++#include <linux/mtd/bcm47xx_nand.h> ++#include <linux/mtd/nand.h> ++ ++#include "bcma_private.h" ++ ++/* Issue a nand flash command */ ++static inline void bcma_nflash_cmd(struct bcma_drv_cc *cc, u32 opcode) ++{ ++ bcma_cc_write32(cc, NAND_CMD_START, opcode); ++ bcma_cc_read32(cc, NAND_CMD_START); ++} ++ ++/* Check offset and length */ ++static int bcma_nflash_offset_is_valid(struct bcma_drv_cc *cc, u32 offset, u32 len, u32 mask) ++{ ++ if ((offset & mask) != 0 || (len & mask) != 0) { ++ pr_err("%s(): Address is not aligned. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask); ++ return 1; ++ } ++ ++ if ((((offset + len) >> 20) >= cc->nflash.size) && ++ (((offset + len) & ((1 << 20) - 1)) != 0)) { ++ pr_err("%s(): Address is outside Flash memory region. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++int bcma_nflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf) ++{ ++ u32 mask; ++ int i; ++ u32 *to, val, res; ++ ++ mask = NFL_SECTOR_SIZE - 1; ++ if (bcma_nflash_offset_is_valid(cc, offset, len, mask)) ++ return 0; ++ ++ to = (u32 *)buf; ++ res = len; ++ while (res > 0) { ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset); ++ bcma_nflash_cmd(cc, NCMD_PAGE_RD); ++ if (bcma_nflash_poll(cc) < 0) ++ break; ++ val = bcma_cc_read32(cc, NAND_INTFC_STATUS); ++ if ((val & NIST_CACHE_VALID) == 0) ++ break; ++ bcma_cc_write32(cc, NAND_CACHE_ADDR, 0); ++ for (i = 0; i < NFL_SECTOR_SIZE; i += 4, to++) { ++ *to = bcma_cc_read32(cc, NAND_CACHE_DATA); ++ } ++ res -= NFL_SECTOR_SIZE; ++ offset += NFL_SECTOR_SIZE; ++ } ++ return (len - res); ++} ++ ++#define NF_RETRIES 1000000 ++ ++/* Poll for command completion. Returns zero when complete. */ ++int bcma_nflash_poll(struct bcma_drv_cc *cc) ++{ ++ u32 retries = NF_RETRIES; ++ u32 pollmask = NIST_CTRL_READY|NIST_FLASH_READY; ++ u32 mask; ++ ++ while (retries--) { ++ mask = bcma_cc_read32(cc, NAND_INTFC_STATUS) & pollmask; ++ if (mask == pollmask) ++ return 0; ++ cpu_relax(); ++ } ++ ++ if (!retries) { ++ pr_err("bcma_nflash_poll: not ready\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Write len bytes starting at offset into buf. Returns success (0) or failure (!0). ++ * Should poll for completion. ++ */ ++int bcma_nflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ u32 mask; ++ int i; ++ u32 *from, res, reg; ++ ++ mask = cc->nflash.pagesize - 1; ++ if (bcma_nflash_offset_is_valid(cc, offset, len, mask)) ++ return 1; ++ ++ /* disable partial page enable */ ++ reg = bcma_cc_read32(cc, NAND_ACC_CONTROL); ++ reg &= ~NAC_PARTIAL_PAGE_EN; ++ bcma_cc_write32(cc, NAND_ACC_CONTROL, reg); ++ ++ from = (u32 *)buf; ++ res = len; ++ while (res > 0) { ++ bcma_cc_write32(cc, NAND_CACHE_ADDR, 0); ++ for (i = 0; i < cc->nflash.pagesize; i += 4, from++) { ++ if (i % 512 == 0) ++ bcma_cc_write32(cc, NAND_CMD_ADDR, i); ++ bcma_cc_write32(cc, NAND_CACHE_DATA, *from); ++ } ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset + cc->nflash.pagesize - 512); ++ bcma_nflash_cmd(cc, NCMD_PAGE_PROG); ++ if (bcma_nflash_poll(cc) < 0) ++ break; ++ res -= cc->nflash.pagesize; ++ offset += cc->nflash.pagesize; ++ } ++ ++ if (res <= 0) ++ return 0; ++ else ++ return (len - res); ++} ++ ++/* Erase a region. Returns success (0) or failure (-1). ++ * Poll for completion. ++ */ ++int bcma_nflash_erase(struct bcma_drv_cc *cc, u32 offset) ++{ ++ if ((offset >> 20) >= cc->nflash.size) ++ return -1; ++ if ((offset & (cc->nflash.blocksize - 1)) != 0) ++ return -1; ++ ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset); ++ bcma_nflash_cmd(cc, NCMD_BLOCK_ERASE); ++ if (bcma_nflash_poll(cc) < 0) ++ return -1; ++ return 0; ++} +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -6,6 +6,7 @@ + * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de> + * Copyright 2010, Bernhard Loos <bernhardloos@googlemail.com> + * Copyright 2011, Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * Licensed under the GNU/GPL. See COPYING for details. + */ +@@ -182,6 +183,17 @@ static void bcma_core_mips_flash_detect( + { + struct bcma_bus *bus = mcore->core->bus; + ++ if (bus->drv_cc.core->id.rev == 38 ++ && (bus->drv_cc.status & (1 << 4)) != 0) { ++#ifdef CONFIG_BCMA_NFLASH ++ pr_info("found nand flash.\n"); ++ bus->drv_cc.flash_type = BCMA_NFLASH; ++#else ++ pr_info("NAND flash not supported.\n"); ++#endif ++ return; ++ } ++ + switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) { + case BCMA_CC_FLASHT_STSER: + case BCMA_CC_FLASHT_ATSER: +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -43,6 +43,11 @@ config BCMA_SFLASH + depends on BCMA_DRIVER_MIPS + default y + ++config BCMA_NFLASH ++ bool ++ depends on BCMA_DRIVER_MIPS ++ default y ++ + config BCMA_DRIVER_MIPS + bool "BCMA Broadcom MIPS core driver" + depends on BCMA && MIPS +--- a/drivers/bcma/Makefile ++++ b/drivers/bcma/Makefile +@@ -1,6 +1,7 @@ + bcma-y += main.o scan.o core.o sprom.o + bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o + bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o ++bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o + bcma-y += driver_pci.o + bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o + bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o +--- /dev/null ++++ b/drivers/mtd/nand/bcm47xx_nand.c +@@ -0,0 +1,506 @@ ++/* ++ * BCMA nand flash interface ++ * ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> ++ * Copyright 2010, Broadcom Corporation ++ * ++ * 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. ++ * ++ */ ++ ++#define pr_fmt(fmt) "bcm47xx_nflash: " fmt ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/ioport.h> ++#include <linux/sched.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <linux/errno.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <bcm47xx.h> ++#include <bus.h> ++#include <linux/cramfs_fs.h> ++#include <linux/romfs_fs.h> ++#include <linux/magic.h> ++#include <linux/byteorder/generic.h> ++#include <linux/mtd/bcm47xx_nand.h> ++#include <linux/mtd/nand.h> ++ ++static int bcm47xx_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip); ++static int bcm47xx_erase(struct mtd_info *mtd, unsigned int addr, unsigned int len); ++ ++/* Private Global variable */ ++static u32 read_offset = 0; ++static u32 write_offset; ++ ++static int ++nflash_mtd_poll(struct bcm47xx_nflash *nflash, unsigned int offset, int timeout) ++{ ++ unsigned long now = jiffies; ++ int ret = 0; ++ ++ for (;;) { ++ if (!bcma_nflash_poll(nflash->bcc)) { ++ ret = 0; ++ break; ++ } ++ if (time_after(jiffies, now + timeout)) { ++ pr_err("timeout while polling\n"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ udelay(1); ++ } ++ ++ return ret; ++} ++ ++static int ++bcm47xx_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ int bytes, ret = 0; ++ u32 extra = 0; ++ u8 *tmpbuf = NULL; ++ int size; ++ u32 offset, blocksize, mask, off; ++ u32 skip_bytes = 0; ++ int need_copy = 0; ++ u8 *ptr = NULL; ++ ++ /* Check address range */ ++ if (!len) ++ return 0; ++ if ((from + len) > mtd->size) ++ return -EINVAL; ++ offset = from; ++ if ((offset & (NFL_SECTOR_SIZE - 1)) != 0) { ++ extra = offset & (NFL_SECTOR_SIZE - 1); ++ offset -= extra; ++ len += extra; ++ need_copy = 1; ++ } ++ size = (len + (NFL_SECTOR_SIZE - 1)) & ~(NFL_SECTOR_SIZE - 1); ++ if (size != len) { ++ need_copy = 1; ++ } ++ if (!need_copy) { ++ ptr = buf; ++ } else { ++ tmpbuf = (u8 *)kmalloc(size, GFP_KERNEL); ++ ptr = tmpbuf; ++ } ++ ++ blocksize = mtd->erasesize; ++ mask = blocksize - 1; ++ *retlen = 0; ++ while (len > 0) { ++ off = offset + skip_bytes; ++ if ((bytes = bcma_nflash_read(nflash->bcc, off, NFL_SECTOR_SIZE, ptr)) < 0) { ++ ret = bytes; ++ goto done; ++ } ++ if (bytes > len) ++ bytes = len; ++ offset += bytes; ++ len -= bytes; ++ ptr += bytes; ++ *retlen += bytes; ++ } ++ ++done: ++ if (tmpbuf) { ++ *retlen -= extra; ++ memcpy(buf, tmpbuf+extra, *retlen); ++ kfree(tmpbuf); ++ } ++ ++ return ret; ++} ++ ++static void bcm47xx_write(struct mtd_info *mtd, u32 to, const u_char *buf, u32 len) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ u32 offset, blocksize, mask, off; ++ int read_len; ++ u32 copy_len, write_len, from; ++ u_char *write_ptr, *block; ++ const u_char *ptr; ++ int ret, bytes; ++ ++ /* Check address range */ ++ if (!len) { ++ pr_err("Error: Attempted to write too small data\n"); ++ return; ++ } ++ ++ if (!to) ++ return; ++ ++ if ((to + len) > mtd->size) { ++ pr_err("Error: Attempted to write too large data\n"); ++ return; ++ } ++ ++ ptr = buf; ++ block = NULL; ++ offset = to; ++ blocksize = mtd->erasesize; ++ if (!(block = kmalloc(blocksize, GFP_KERNEL))) ++ return; ++ mask = blocksize - 1; ++ while (len) { ++ /* Align offset */ ++ from = offset & ~mask; ++ /* Copy existing data into holding block if necessary */ ++ if (((offset & (blocksize-1)) != 0) || (len < blocksize)) { ++ if ((ret = bcm47xx_read(mtd, from, blocksize, &read_len, block))) ++ goto done; ++ if (read_len != blocksize) { ++ ret = -EINVAL; ++ goto done; ++ } ++ } ++ ++ /* Copy input data into holding block */ ++ copy_len = min(len, blocksize - (offset & mask)); ++ memcpy(block + (offset & mask), ptr, copy_len); ++ off = (uint) from; ++ /* Erase block */ ++ if ((ret = bcm47xx_erase(mtd, off, blocksize)) < 0) ++ goto done; ++ /* Write holding block */ ++ write_ptr = block; ++ write_len = blocksize; ++ if ((bytes = bcma_nflash_write(nflash->bcc, (uint)from, (uint)write_len, (u8 *) write_ptr)) != 0) { ++ ret = bytes; ++ goto done; ++ } ++ offset += copy_len; ++ if (len < copy_len) ++ len = 0; ++ else ++ len -= copy_len; ++ ptr += copy_len; ++ } ++ ++done: ++ if (block) ++ kfree(block); ++ return; ++} ++ ++static int bcm47xx_erase(struct mtd_info *mtd, unsigned int addr, unsigned int len) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ ++ /* Check address range */ ++ if (!len) ++ return 1; ++ if ((addr + len) > mtd->size) ++ return 1; ++ ++ if (bcma_nflash_erase(nflash->bcc, addr)) { ++ pr_err("ERASE: nflash erase error\n"); ++ return 1; ++ } ++ ++ if (nflash_mtd_poll(nflash, addr, 10 * HZ)) { ++ pr_err("ERASE: nflash_mtd_poll error\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* This functions is used by upper layer to checks if device is ready */ ++static int bcm47xx_dev_ready(struct mtd_info *mtd) ++{ ++ return 1; ++} ++ ++/* Issue a nand flash command */ ++static inline void bcm47xx_nflash_cmd(struct bcma_drv_cc *cc, u32 opcode) ++{ ++ bcma_cc_write32(cc, NAND_CMD_START, opcode); ++ bcma_cc_read32(cc, NAND_CMD_START); ++} ++ ++static void bcm47xx_command(struct mtd_info *mtd, unsigned command, ++ int column, int page_addr) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ u32 pagesize = 1 << nchip->page_shift; ++ ++ /* Command pre-processing step */ ++ switch (command) { ++ case NAND_CMD_RESET: ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_FLASH_RESET); ++ break; ++ ++ case NAND_CMD_STATUS: ++ nflash->next_opcode = NAND_CMD_STATUS; ++ read_offset = 0; ++ write_offset = 0; ++ break; ++ ++ case NAND_CMD_READ0: ++ read_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_READOOB: ++ read_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_SEQIN: ++ write_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_READID: ++ read_offset = column; ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_ID_RD); ++ nflash->next_opcode = NAND_DEVID; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ nflash->next_opcode = 0; ++ bcm47xx_erase(mtd, page_addr*pagesize, pagesize); ++ break; ++ ++ case NAND_CMD_ERASE2: ++ break; ++ ++ case NAND_CMD_RNDOUT: ++ if (column > mtd->writesize) ++ read_offset += (column - mtd->writesize); ++ else ++ read_offset += column; ++ break; ++ ++ default: ++ pr_err("COMMAND not supported %x\n", command); ++ nflash->next_opcode = 0; ++ break; ++ } ++} ++ ++/* This function is used by upper layer for select and ++ * deselect of the NAND chip. ++ * It is dummy function. */ ++static void bcm47xx_select_chip(struct mtd_info *mtd, int chip) ++{ ++} ++ ++static u_char bcm47xx_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *nchip = mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ uint8_t ret = 0; ++ static u32 id; ++ ++ if (nflash->next_opcode == 0) ++ return ret; ++ ++ if (nflash->next_opcode == NAND_CMD_STATUS) ++ return NAND_STATUS_WP; ++ ++ id = bcma_cc_read32(nflash->bcc, nflash->next_opcode); ++ ++ if (nflash->next_opcode == NAND_DEVID) { ++ ret = (id >> (8*read_offset)) & 0xff; ++ read_offset++; ++ } ++ ++ return ret; ++} ++ ++static uint16_t bcm47xx_read_word(struct mtd_info *mtd) ++{ ++ loff_t from = read_offset; ++ uint16_t buf = 0; ++ int bytes; ++ ++ bcm47xx_read(mtd, from, sizeof(buf), &bytes, (u_char *)&buf); ++ return buf; ++} ++ ++/* Write data of length len to buffer buf. The data to be ++ * written on NAND Flash is first copied to RAMbuffer. After the Data Input ++ * Operation by the NFC, the data is written to NAND Flash */ ++static void bcm47xx_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ bcm47xx_write(mtd, write_offset, buf, len); ++} ++ ++/* Read the data buffer from the NAND Flash. To read the data from NAND ++ * Flash first the data output cycle is initiated by the NFC, which copies ++ * the data to RAMbuffer. This data of length len is then copied to buffer buf. ++ */ ++static void bcm47xx_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ loff_t from = read_offset; ++ int bytes; ++ ++ bcm47xx_read(mtd, from, len, &bytes, buf); ++} ++ ++/* Used by the upper layer to verify the data in NAND Flash ++ * with the data in the buf. */ ++static int bcm47xx_verify_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ return -EFAULT; ++} ++ ++static int bcm47xx_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) ++{ ++ struct nand_chip *nchip = mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ int i; ++ uint off; ++ u32 pagesize = 1 << nchip->page_shift; ++ u32 blocksize = mtd->erasesize; ++ ++ if ((ofs >> 20) >= nflash->size) ++ return 1; ++ if ((ofs & (blocksize - 1)) != 0) ++ return 1; ++ ++ for (i = 0; i < 2; i++) { ++ off = ofs + pagesize; ++ bcma_cc_write32(nflash->bcc, NAND_CMD_ADDR, off); ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_SPARE_RD); ++ if (bcma_nflash_poll(nflash->bcc) < 0) ++ break; ++ if ((bcma_cc_read32(nflash->bcc, NAND_INTFC_STATUS) & NIST_SPARE_VALID) != NIST_SPARE_VALID) ++ return 1; ++ if ((bcma_cc_read32(nflash->bcc, NAND_SPARE_RD0) & 0xff) != 0xff) ++ return 1; ++ } ++ return 0; ++} ++ ++const char *part_probes[] = { "cmdlinepart", NULL }; ++static int bcm47xx_probe(struct platform_device *pdev) ++{ ++ struct nand_chip *nchip; ++ struct mtd_info *mtd; ++ struct bcm47xx_nflash *nflash = dev_get_platdata(&pdev->dev); ++ int ret = 0; ++ ++ mtd = &nflash->mtd; ++ nchip = &nflash->nand; ++ ++ /* Register with MTD */ ++ mtd->priv = nchip; ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = &pdev->dev; ++ ++ /* 50 us command delay time */ ++ nchip->chip_delay = 50; ++ ++ nchip->priv = nflash; ++ nchip->dev_ready = bcm47xx_dev_ready; ++ nchip->cmdfunc = bcm47xx_command; ++ nchip->select_chip = bcm47xx_select_chip; ++ nchip->read_byte = bcm47xx_read_byte; ++ nchip->read_word = bcm47xx_read_word; ++ nchip->write_buf = bcm47xx_write_buf; ++ nchip->read_buf = bcm47xx_read_buf; ++ nchip->verify_buf = bcm47xx_verify_buf; ++ nchip->block_bad = bcm47xx_block_bad; ++ nchip->options = NAND_SKIP_BBTSCAN; ++ ++ /* Not known */ ++ nchip->ecc.mode = NAND_ECC_NONE; ++ ++ /* first scan to find the device and get the page size */ ++ if (nand_scan_ident(mtd, 1, NULL)) { ++ pr_err("nand_scan_ident failed\n"); ++ ret = -ENXIO; ++ goto done; ++ } ++ nflash->bcc->nflash.size = mtd->size; ++ nflash->bcc->nflash.pagesize = 1 << nchip->page_shift; ++ nflash->bcc->nflash.blocksize = mtd->erasesize; ++ bcm47xx_nflash.size = mtd->size; ++ ++ /* second phase scan */ ++ if (nand_scan_tail(mtd)) { ++ pr_err("nand_scan_tail failed\n"); ++ ret = -ENXIO; ++ goto done; ++ } ++ ++ mtd->name = "bcm47xx-nflash"; ++ mtd->flags |= MTD_WRITEABLE; ++ ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); ++ ++ if (ret) { ++ pr_err("mtd_device_register failed\n"); ++ return ret; ++ } ++ ++ return 0; ++ ++done: ++ return ret; ++} ++ ++static int __devexit bcm47xx_remove(struct platform_device *pdev) ++{ ++ struct bcm47xx_nflash *nflash = dev_get_platdata(&pdev->dev); ++ struct mtd_info *mtd = &nflash->mtd; ++ ++ if (nflash) { ++ /* Release resources, unregister device */ ++ nand_release(mtd); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver bcm47xx_driver = { ++ .remove = __devexit_p(bcm47xx_remove), ++ .driver = { ++ .name = "bcm47xx_nflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_nflash(void) ++{ ++ int ret = platform_driver_probe(&bcm47xx_driver, bcm47xx_probe); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_nflash(void) ++{ ++ platform_driver_unregister(&bcm47xx_driver); ++} ++ ++module_init(init_bcm47xx_nflash); ++module_exit(exit_bcm47xx_nflash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX NAND flash driver"); +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -537,4 +537,12 @@ config MTD_NAND_FSMC + Enables support for NAND Flash chips on the ST Microelectronics + Flexible Static Memory Controller (FSMC) + ++config MTD_NAND_BCM47XX ++ tristate "bcm47xx nand flash support" ++ default y ++ depends on BCM47XX ++ select MTD_PARTITIONS ++ help ++ Support for bcm47xx nand flash ++ + endif # MTD_NAND +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mp + obj-$(CONFIG_MTD_NAND_RICOH) += r852.o + obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o + obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ ++obj-$(CONFIG_MTD_NAND_BCM47XX) += bcm47xx_nand.o + + nand-objs := nand_base.o nand_bbt.o +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -376,6 +376,7 @@ struct bcma_chipcommon_pmu { + enum bcma_flash_type { + BCMA_PFLASH, + BCMA_SFLASH, ++ BCMA_NFLASH, + }; + + struct bcma_pflash { +@@ -392,6 +393,14 @@ struct bcma_sflash { + }; + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++struct bcma_nflash { ++ u32 blocksize; /* Block size */ ++ u32 pagesize; /* Page size */ ++ u32 size; /* Total size in bytes */ ++}; ++#endif ++ + struct bcma_serial_port { + void *regs; + unsigned long clockspeed; +@@ -417,6 +426,9 @@ struct bcma_drv_cc { + #ifdef CONFIG_BCMA_SFLASH + struct bcma_sflash sflash; + #endif /* CONFIG_BCMA_SFLASH */ ++#ifdef CONFIG_BCMA_NFLASH ++ struct bcma_nflash nflash; ++#endif + }; + + int nr_serial_ports; +@@ -483,4 +495,13 @@ int bcma_sflash_commit(struct bcma_drv_c + const u8 *buf); + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++/* Chipcommon nflash support. */ ++int bcma_nflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf); ++int bcma_nflash_poll(struct bcma_drv_cc *cc); ++int bcma_nflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, const u8 *buf); ++int bcma_nflash_erase(struct bcma_drv_cc *cc, u32 offset); ++int bcma_nflash_commit(struct bcma_drv_cc *cc, u32 offset, u32 len, const u8 *buf); ++#endif ++ + #endif /* LINUX_BCMA_DRIVER_CC_H_ */ +--- /dev/null ++++ b/include/linux/mtd/bcm47xx_nand.h +@@ -0,0 +1,134 @@ ++/* ++ * Broadcom chipcommon NAND flash interface ++ * ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> ++ * Copyright (C) 2009, 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. ++ * ++ */ ++ ++#ifndef _nflash_h_ ++#define _nflash_h_ ++ ++#define NAND_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ ++ ++/* nand_cmd_start commands */ ++#define NCMD_NULL 0 ++#define NCMD_PAGE_RD 1 ++#define NCMD_SPARE_RD 2 ++#define NCMD_STATUS_RD 3 ++#define NCMD_PAGE_PROG 4 ++#define NCMD_SPARE_PROG 5 ++#define NCMD_COPY_BACK 6 ++#define NCMD_ID_RD 7 ++#define NCMD_BLOCK_ERASE 8 ++#define NCMD_FLASH_RESET 9 ++#define NCMD_LOCK 0xa ++#define NCMD_LOCK_DOWN 0xb ++#define NCMD_UNLOCK 0xc ++#define NCMD_LOCK_STATUS 0xd ++ ++/* nand_acc_control */ ++#define NAC_RD_ECC_EN 0x80000000 ++#define NAC_WR_ECC_EN 0x40000000 ++#define NAC_RD_ECC_BLK0_EN 0x20000000 ++#define NAC_FAST_PGM_RDIN 0x10000000 ++#define NAC_RD_ERASED_ECC_EN 0x08000000 ++#define NAC_PARTIAL_PAGE_EN 0x04000000 ++#define NAC_PAGE_HIT_EN 0x01000000 ++#define NAC_ECC_LEVEL0 0x00f00000 ++#define NAC_ECC_LEVEL 0x000f0000 ++#define NAC_SPARE_SIZE0 0x00003f00 ++#define NAC_SPARE_SIZE 0x0000003f ++ ++/* nand_config */ ++#define NCF_CONFIG_LOCK 0x80000000 ++#define NCF_BLOCK_SIZE_MASK 0x70000000 ++#define NCF_BLOCK_SIZE_SHIFT 28 ++#define NCF_DEVICE_SIZE_MASK 0x0f000000 ++#define NCF_DEVICE_SIZE_SHIFT 24 ++#define NCF_DEVICE_WIDTH 0x00800000 ++#define NCF_PAGE_SIZE_MASK 0x00300000 ++#define NCF_PAGE_SIZE_SHIFT 20 ++#define NCF_FULL_ADDR_BYTES_MASK 0x00070000 ++#define NCF_FULL_ADDR_BYTES_SHIFT 16 ++#define NCF_COL_ADDR_BYTES_MASK 0x00007000 ++#define NCF_COL_ADDR_BYTES_SHIFT 12 ++#define NCF_BLK_ADDR_BYTES_MASK 0x00000700 ++#define NCF_BLK_ADDR_BYTES_SHIFT 8 ++ ++/* nand_intfc_status */ ++#define NIST_CTRL_READY 0x80000000 ++#define NIST_FLASH_READY 0x40000000 ++#define NIST_CACHE_VALID 0x20000000 ++#define NIST_SPARE_VALID 0x10000000 ++#define NIST_ERASED 0x08000000 ++#define NIST_STATUS 0x000000ff ++ ++#define NFL_SECTOR_SIZE 512 ++ ++#define NFL_TABLE_END 0xffffffff ++#define NFL_BOOT_SIZE 0x200000 ++#define NFL_BOOT_OS_SIZE 0x2000000 ++ ++/* Nand flash MLC controller registers (corerev >= 38) */ ++#define NAND_REVISION 0xC00 ++#define NAND_CMD_START 0xC04 ++#define NAND_CMD_ADDR_X 0xC08 ++#define NAND_CMD_ADDR 0xC0C ++#define NAND_CMD_END_ADDR 0xC10 ++#define NAND_CS_NAND_SELECT 0xC14 ++#define NAND_CS_NAND_XOR 0xC18 ++#define NAND_SPARE_RD0 0xC20 ++#define NAND_SPARE_RD4 0xC24 ++#define NAND_SPARE_RD8 0xC28 ++#define NAND_SPARE_RD12 0xC2C ++#define NAND_SPARE_WR0 0xC30 ++#define NAND_SPARE_WR4 0xC34 ++#define NAND_SPARE_WR8 0xC38 ++#define NAND_SPARE_WR12 0xC3C ++#define NAND_ACC_CONTROL 0xC40 ++#define NAND_CONFIG 0xC48 ++#define NAND_TIMING_1 0xC50 ++#define NAND_TIMING_2 0xC54 ++#define NAND_SEMAPHORE 0xC58 ++#define NAND_DEVID 0xC60 ++#define NAND_DEVID_X 0xC64 ++#define NAND_BLOCK_LOCK_STATUS 0xC68 ++#define NAND_INTFC_STATUS 0xC6C ++#define NAND_ECC_CORR_ADDR_X 0xC70 ++#define NAND_ECC_CORR_ADDR 0xC74 ++#define NAND_ECC_UNC_ADDR_X 0xC78 ++#define NAND_ECC_UNC_ADDR 0xC7C ++#define NAND_READ_ERROR_COUNT 0xC80 ++#define NAND_CORR_STAT_THRESHOLD 0xC84 ++#define NAND_READ_ADDR_X 0xC90 ++#define NAND_READ_ADDR 0xC94 ++#define NAND_PAGE_PROGRAM_ADDR_X 0xC98 ++#define NAND_PAGE_PROGRAM_ADDR 0xC9C ++#define NAND_COPY_BACK_ADDR_X 0xCA0 ++#define NAND_COPY_BACK_ADDR 0xCA4 ++#define NAND_BLOCK_ERASE_ADDR_X 0xCA8 ++#define NAND_BLOCK_ERASE_ADDR 0xCAC ++#define NAND_INV_READ_ADDR_X 0xCB0 ++#define NAND_INV_READ_ADDR 0xCB4 ++#define NAND_BLK_WR_PROTECT 0xCC0 ++#define NAND_ACC_CONTROL_CS1 0xCD0 ++#define NAND_CONFIG_CS1 0xCD4 ++#define NAND_TIMING_1_CS1 0xCD8 ++#define NAND_TIMING_2_CS1 0xCDC ++#define NAND_SPARE_RD16 0xD30 ++#define NAND_SPARE_RD20 0xD34 ++#define NAND_SPARE_RD24 0xD38 ++#define NAND_SPARE_RD28 0xD3C ++#define NAND_CACHE_ADDR 0xD40 ++#define NAND_CACHE_DATA 0xD44 ++#define NAND_CTRL_CONFIG 0xD48 ++#define NAND_CTRL_STATUS 0xD4C ++ ++#endif /* _nflash_h_ */ |