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 | 1157 | 
1 files changed, 1157 insertions, 0 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 new file mode 100644 index 000000000..03dd321b1 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch @@ -0,0 +1,1157 @@ +--- a/arch/mips/bcm47xx/Kconfig ++++ b/arch/mips/bcm47xx/Kconfig +@@ -24,6 +24,7 @@ config BCM47XX_BCMA + 	select BCMA_DRIVER_MIPS + 	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 +@@ -80,3 +81,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]; +  +@@ -134,6 +136,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; ++} ++ + #ifdef CONFIG_BCM47XX_SSB + static void early_nvram_init_ssb(void) + { +@@ -168,6 +215,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-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 struct resource bcm47xx_pflash_resource = { + 	.name	= "bcm47xx_pflash", +@@ -73,6 +75,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 void bcm47xx_machine_restart(char *command) + { + 	printk(KERN_ALERT "Please stand by while rebooting the system...\n"); +@@ -369,6 +384,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); + } +  + static int __init bcm47xx_register_flash_bcma(void) +@@ -383,6 +401,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 +@@ -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; +@@ -29,11 +31,24 @@ struct bcm47xx_sflash { + 	u32 blocksize;		/* Block size */ + 	u32 numblocks;		/* Number of blocks */ + 	u32 size;		/* Total size in bytes */ +- +-	struct mtd_info *mtd; + }; +  + void bcm47xx_sflash_struct_bcma_init(struct bcm47xx_sflash *sflash, struct bcma_drv_cc *bcc); + 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 +@@ -47,6 +47,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 ++	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 +@@ -400,6 +400,7 @@ struct bcma_chipcommon_pmu { + enum bcma_flash_type { + 	BCMA_PFLASH, + 	BCMA_SFLASH, ++	BCMA_NFLASH, + }; +  + struct bcma_pflash { +@@ -416,6 +417,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; +@@ -441,6 +450,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; +@@ -505,4 +517,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_ */  | 
