diff options
Diffstat (limited to 'target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch')
-rw-r--r-- | target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch | 1146 |
1 files changed, 0 insertions, 1146 deletions
diff --git a/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch b/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch deleted file mode 100644 index 53f6e98d15..0000000000 --- a/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch +++ /dev/null @@ -1,1146 +0,0 @@ ---- a/arch/mips/bcm47xx/Kconfig -+++ b/arch/mips/bcm47xx/Kconfig -@@ -25,6 +25,7 @@ config BCM47XX_BCMA - select BCMA_HOST_PCI if PCI - select BCMA_DRIVER_PCI_HOSTMODE if PCI - select BCMA_SFLASH -+ select BCMA_NFLASH - default y - help - Add support for new Broadcom BCM47xx boards with Broadcom specific Advanced Microcontroller Bus. ---- 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 -@@ -46,6 +47,12 @@ void bcm47xx_sflash_struct_bcma_init(str - sflash->numblocks = bcc->sflash.numblocks; - sflash->size = bcc->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; -+} - #endif - - #ifdef CONFIG_BCM47XX_SSB ---- 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]; - -@@ -160,6 +162,51 @@ static void early_nvram_init_ssb(void) - #endif - - #ifdef CONFIG_BCM47XX_BCMA -+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_bcma(void) - { - int err; -@@ -173,6 +220,11 @@ static void early_nvram_init_bcma(void) - if (err < 0) - printk(KERN_WARNING "can not read from flash: %i\n", err); - break; -+ case BCMA_NFLASH: -+ err = early_nvram_init_nflash(); -+ if (err < 0) -+ printk(KERN_WARNING "can not read from nflash: %i\n", err); -+ break; - default: - 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-2012 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 -@@ -234,6 +235,21 @@ static int bcm47xx_get_sprom_bcma(struct - } - } - -+struct bcm47xx_nflash bcm47xx_nflash; -+ -+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 void __init bcm47xx_register_bcma(void) - { - int err; -@@ -248,6 +264,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); - - bcm47xx_fill_bcma_boardinfo(&bcm47xx_bus.bcma.bus.boardinfo, NULL); - } -@@ -264,6 +283,9 @@ static int __init bcm47xx_register_flash - case BCMA_SFLASH: - bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash; - return platform_device_register(&bcm47xx_sflash_dev); -+ case BCMA_NFLASH: -+ bcm47xx_nflash_dev.dev.platform_data = &bcm47xx_nflash; -+ return platform_device_register(&bcm47xx_nflash_dev); - default: - 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 -@@ -12,6 +13,7 @@ - #include <linux/ssb/ssb.h> - #include <linux/bcma/bcma.h> - #include <bcm47xx.h> -+#include <linux/mtd/nand.h> - - struct bcm47xx_sflash { - enum bcm47xx_bus_type sflash_type; -@@ -34,3 +36,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/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 ---- a/drivers/bcma/bcma_private.h -+++ b/drivers/bcma/bcma_private.h -@@ -56,6 +56,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/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -536,4 +536,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 && BCMA_NFLASH -+ 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 ---- /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/include/linux/bcma/bcma_driver_chipcommon.h -+++ b/include/linux/bcma/bcma_driver_chipcommon.h -@@ -436,6 +436,7 @@ struct bcma_chipcommon_pmu { - enum bcma_flash_type { - BCMA_PFLASH, - BCMA_SFLASH, -+ BCMA_NFLASH, - }; - - struct bcma_pflash { -@@ -452,6 +453,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; -@@ -477,6 +486,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; -@@ -542,4 +554,13 @@ int bcma_sflash_write(struct bcma_drv_cc - int bcma_sflash_erase(struct bcma_drv_cc *cc, u32 offset); - #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_ */ |