Hello Tathagata, On 01/10/12 13:00, Tathagata Das wrote:
Hi, Attached is the updated kernel patch to support brcm47xx BCMA NAND flash. This patch uses driver/mtd/nand/nand_base.c. I have used latest trunk source code to create this patch.
I have inlined some comments, how much throughput do you get out of the driver in read/write?
Regards, Tathagata <tathag...@alumnux.com> 9991-bcm47xx-bcma-nandflash.patch diff -Naur a/arch/mips/bcm47xx/bus.c b/arch/mips/bcm47xx/bus.c --- a/arch/mips/bcm47xx/bus.c 2011-12-26 13:09:35.028170411 +0530 +++ b/arch/mips/bcm47xx/bus.c 2012-01-05 11:41:55.781832993 +0530 @@ -92,3 +92,46 @@ sflash->numblocks = scc->sflash.numblocks; sflash->size = scc->sflash.size; } + +#ifdef CONFIG_BCMA_NFLASH +static int bcm47xx_nflash_bcma_read(struct bcm47xx_nflash *dev, u32 offset, u32 len, u8 *buf) +{ + return bcma_nflash_read(dev->bcc, offset, len, buf); +} + +static int bcm47xx_nflash_bcma_poll(struct bcm47xx_nflash *dev, u32 offset) +{ + return bcma_nflash_poll(dev->bcc); +} + +static int bcm47xx_nflash_bcma_write(struct bcm47xx_nflash *dev, u32 offset, u32 len, const u8 *buf) +{ + return bcma_nflash_write(dev->bcc, offset, len, buf); +} + +static int bcm47xx_nflash_bcma_erase(struct bcm47xx_nflash *dev, u32 offset) +{ + return bcma_nflash_erase(dev->bcc, offset); +} + +void bcm47xx_nflash_struct_bcma_init(struct bcm47xx_nflash *nflash, struct bcma_drv_cc *bcc) +{ + int i; + + nflash->nflash_type = BCM47XX_BUS_TYPE_BCMA; + nflash->bcc = bcc; + + nflash->read = bcm47xx_nflash_bcma_read; + nflash->poll = bcm47xx_nflash_bcma_poll; + nflash->write = bcm47xx_nflash_bcma_write; + nflash->erase = bcm47xx_nflash_bcma_erase; + + nflash->blocksize = bcc->nflash.blocksize; + nflash->numblocks = bcc->nflash.numblocks; + nflash->pagesize = bcc->nflash.pagesize; + nflash->size = bcc->nflash.size; + + for (i=0; i<5; i++) + nflash->id[i] = bcc->nflash.id[i];
Do you really need to read the flash id that early?
+} +#endif /* CONFIG_BCMA_NFLASH */ diff -Naur a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c --- a/arch/mips/bcm47xx/nvram.c 2011-12-26 13:09:36.274170411 +0530 +++ b/arch/mips/bcm47xx/nvram.c 2012-01-10 14:48:15.981647409 +0530 @@ -21,6 +21,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> char nvram_buf[NVRAM_SPACE]; EXPORT_SYMBOL(nvram_buf); @@ -159,6 +160,55 @@ 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 * 1024 * 1024; + u8 *tmpbuf = NULL; + int i; + u32 *src, *dst; + + /* check if the struct is already initilized */ + if (!flash_size) + return -1; + + cfe_env = 0; + + tmpbuf = (u8 *)kmalloc(NFL_SECTOR_SIZE, GFP_KERNEL);
Is not that a little early for using kmalloc? NFL_SECTOR_SIZE is only 512 bytes, so either using a buffer marked with __initdata or allocating it on the stack should be okay.
+ off = FLASH_MIN; + while (off<= flash_size) { + ret = bcm47xx_nflash.read(&bcm47xx_nflash, 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: + kfree(tmpbuf); + return ret; +}
The identation of these lines is not coherent with the rest.
+ static void early_nvram_init(void) { int err = 0; @@ -185,6 +235,10 @@ 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");
Consider using a switch() case() statement here, two cases to handle is enough to start using it.
} diff -Naur a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c --- a/arch/mips/bcm47xx/setup.c 2011-12-26 13:09:36.247170411 +0530 +++ b/arch/mips/bcm47xx/setup.c 2012-01-05 11:41:55.880832992 +0530 @@ -4,6 +4,7 @@ * Copyright (C) 2006 Michael Buesch<m...@bu3sch.de> * Copyright (C) 2010 Waldemar Brodkorb<w...@openadk.org> * Copyright (C) 2010-2011 Hauke Mehrtens<ha...@hauke-m.de> + * Copyright (C) 2010-2011 Tathagata Das<tathag...@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 @@ -47,6 +48,9 @@ EXPORT_SYMBOL(bcm47xx_bus_type); struct bcm47xx_sflash bcm47xx_sflash; +#ifdef CONFIG_MTD_NAND_BCM47XX +struct bcm47xx_nflash bcm47xx_nflash; +#endif
I don't think you need to have this #ifdef'd at all, just like the *_flash counterpart.
static void bcm47xx_machine_restart(char *command) { @@ -359,6 +363,9 @@ 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 @@ -429,6 +436,19 @@ .num_resources = 1,
[snip]
+#ifdef CONFIG_MTD_NAND_BCM47XX +struct bcm47xx_nflash { + enum bcm47xx_bus_type nflash_type; + struct bcma_drv_cc *bcc; + + int (*read)(struct bcm47xx_nflash *dev, u32 offset, u32 len, u8 *buf); + int (*poll)(struct bcm47xx_nflash *dev, u32 offset); + int (*write)(struct bcm47xx_nflash *dev, u32 offset, u32 len, const u8 *buf); + int (*erase)(struct bcm47xx_nflash *dev, u32 offset); + int (*commit)(struct bcm47xx_nflash *dev, u32 offset, u32 len, const u8 *buf); + + u32 blocksize; /* Block size */ + u32 numblocks; /* Number of blocks */ + u32 pagesize; /* Page size */ + u32 size; /* Total size in bytes */
I think you should rather use the same properties provided by the nand_chip structure.
+ u8 id[5]; + u32 next_opcode; /* Next expected command from upper NAND layer */ + + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_erase_region_info region; + unsigned char *map; +}; + +void bcm47xx_nflash_struct_bcma_init(struct bcm47xx_nflash *nflash, struct bcma_drv_cc *bcc); + +extern struct bcm47xx_nflash bcm47xx_nflash; +#endif /* CONFIG_MTD_NAND_BCM47XX */ diff -Naur a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h --- a/drivers/bcma/bcma_private.h 2011-12-26 13:09:34.927170411 +0530 +++ b/drivers/bcma/bcma_private.h 2012-01-05 11:41:56.115832992 +0530 @@ -46,6 +46,11 @@ 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); diff -Naur a/drivers/bcma/driver_chipcommon_nflash.c b/drivers/bcma/driver_chipcommon_nflash.c --- a/drivers/bcma/driver_chipcommon_nflash.c 1970-01-01 05:30:00.000000000 +0530 +++ b/drivers/bcma/driver_chipcommon_nflash.c 2012-01-10 14:49:06.638647400 +0530 @@ -0,0 +1,244 @@ +/* + * BCMA nand flash interface + * + * Copyright 2011, Tathagata Das<tathag...@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" + +static int bcma_firsttime = 0; + +/* 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); +} + +/* Initialize serial flash access */ +int bcma_nflash_init(struct bcma_drv_cc *cc) +{ + u32 id, id2; + char *name = ""; + int i; + u32 ncf, val; + + if (!bcma_firsttime&& cc->nflash.size) + return cc->nflash.size; + + memset(&cc->nflash, 0, sizeof(struct bcma_nflash)); + + /* Read flash id */ + bcma_nflash_cmd(cc, NCMD_ID_RD); + if (bcma_nflash_poll(cc)< 0) + return -ENODEV; + id = bcma_cc_read32(cc, NAND_DEVID); + id2 = bcma_cc_read32(cc, NAND_DEVID_X); + for (i = 0; i< 5; i++) { + if (i< 4) + cc->nflash.id[i] = (id>> (8*i))& 0xff; + else + cc->nflash.id[i] = id2& 0xff; + } + cc->nflash.type = cc->nflash.id[0]; + switch (cc->nflash.type) { + case NAND_MFR_AMD: + name = "AMD"; + break; + case NAND_MFR_STMICRO: + name = "Numonyx"; + break; + case NAND_MFR_MICRON: + name = "Micron"; + break; + case NAND_MFR_TOSHIBA: + name = "Toshiba"; + break; + case NAND_MFR_HYNIX: + name = "Hynix"; + break; + case NAND_MFR_SAMSUNG: + name = "Samsung"; + break; + } + ncf = bcma_cc_read32(cc, NAND_CONFIG); + /* Page size (# of bytes) */ + val = (ncf& NCF_PAGE_SIZE_MASK)>> NCF_PAGE_SIZE_SHIFT; + switch (val) { + case 0: + cc->nflash.pagesize = 512; + break; + case 1: + cc->nflash.pagesize = (1<< 10) * 2; + break; + case 2: + cc->nflash.pagesize = (1<< 10) * 4; + break; + case 3: + cc->nflash.pagesize = (1<< 10) * 8; + break; + } + /* Block size (# of bytes) */ + val = (ncf& NCF_BLOCK_SIZE_MASK)>> NCF_BLOCK_SIZE_SHIFT; + switch (val) { + case 0: + cc->nflash.blocksize = (1<< 10) * 16; + break; + case 1: + cc->nflash.blocksize = (1<< 10) * 128; + break; + case 2: + cc->nflash.blocksize = (1<< 10) * 8; + break; + case 3: + cc->nflash.blocksize = (1<< 10) * 512; + break; + case 4: + cc->nflash.blocksize = (1<< 10) * 256; + break; + default: + printk("Unknown block size\n"); + return -ENODEV; + } + /* NAND flash size in MBytes */ + val = (ncf& NCF_DEVICE_SIZE_MASK)>> NCF_DEVICE_SIZE_SHIFT; + if (val == 0) { + printk("Unknown flash size\n"); + return -ENODEV; + } + cc->nflash.size = (1<< (val - 1)) * 8; + cc->nflash.numblocks = (cc->nflash.size * (1<< 10)) / (cc->nflash.blocksize>> 10); + bcma_firsttime = 1; + if (bcma_firsttime) + printk("Found a %s NAND flash with %uB pages or %dKB blocks; total size %dMB\n", + name, cc->nflash.pagesize, (cc->nflash.blocksize>> 10), cc->nflash.size); + return cc->nflash.size ? 0 : -ENODEV;
This should be left to the NAND subsystem, maintaing two places where there is NAND flash identification does not scale.
+} + +/* 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 ((offset& mask) != 0 || (len& mask) != 0) { + printk("%s(): Address is not aligned. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask); + panic("Unaliged address access"); + return 0; + }
panic() here sounds a little too hard, BUG_ON() might be more appropriate.
+ + if ((((offset + len)>> 20)> cc->nflash.size) || + ((((offset + len)>> 20) == cc->nflash.size)&& + (((offset + len)& ((1<< 20) - 1)) != 0))) { + printk("%s(): Address is outside Flash memory region. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask); + return 0; + }
Factor this is in a function like bcma_nflash_offset_is_valid() since you are reusing this for the write() counterpart.
+ + 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) +{ + int i; + u32 pollmask; + + pollmask = NIST_CTRL_READY|NIST_FLASH_READY; + for (i = 0; i< NF_RETRIES; i++) { + if ((bcma_cc_read32(cc, NAND_INTFC_STATUS)& pollmask) == pollmask) { + return 0; + } + } + printk("bcma_nflash_poll: not ready\n"); + return -1; +}
Cannot you use interrupts instead of polling? Otherwise, you'd better replace this busy-loop with:
u32 retries = NF_RETRIES; while (retries--) { mask = bcma_cc_read32(cc, NAND_INTFC_STATUS; if (mask == pollmask) break; cpu_relax(); } if (!retries) return -1; return 0;
+ +/* Write len bytes starting at offset into buf. Returns number of bytes + * written. Caller 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; + /* Check offset and length */ + if ((offset& mask) != 0 || (len& mask) != 0) + return 0; + if ((((offset + len)>> 20)> cc->nflash.size) || + ((((offset + len)>> 20) == cc->nflash.size)&& + (((offset + len)& ((1<< 20) - 1)) != 0))) + return 0; + + /* 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; + } + return (len - res); +} + +/* Erase a region. Returns number of bytes scheduled for erasure. + * Caller should 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; +}
The comment is a little misleading, sice flash_erase() already polls for the completion, at first I understood it as "bcma_nflash_erase() caller should poll for completion".
diff -Naur a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c --- a/drivers/bcma/driver_mips.c 2011-12-26 13:09:34.930170411 +0530 +++ b/drivers/bcma/driver_mips.c 2012-01-05 11:41:56.449832992 +0530 @@ -182,9 +182,17 @@ { struct bcma_bus *bus = mcore->core->bus; +printk("BUS Capability: %x\n", bus->drv_cc.capabilities); switch (bus->drv_cc.capabilities& BCMA_CC_CAP_FLASHT) { case BCMA_CC_FLASHT_STSER: case BCMA_CC_FLASHT_ATSER: +#ifdef CONFIG_BCMA_NFLASH + bus->drv_cc.flash_type = BCMA_NFLASH; + if (!bcma_nflash_init(&bus->drv_cc)) { + printk("found nand flash.\n"); + return; + } +#endif #ifdef CONFIG_BCMA_SFLASH pr_info("found serial flash.\n"); bus->drv_cc.flash_type = BCMA_SFLASH; diff -Naur a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig --- a/drivers/bcma/Kconfig 2011-12-26 13:09:35.774170411 +0530 +++ b/drivers/bcma/Kconfig 2012-01-05 11:41:56.487832974 +0530 @@ -44,6 +44,11 @@ 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 diff -Naur a/drivers/bcma/Makefile b/drivers/bcma/Makefile --- a/drivers/bcma/Makefile 2011-12-26 13:09:34.926170411 +0530 +++ b/drivers/bcma/Makefile 2012-01-05 11:41:56.618832972 +0530 @@ -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 diff -Naur a/drivers/mtd/nand/bcm47xx_nand.c b/drivers/mtd/nand/bcm47xx_nand.c --- a/drivers/mtd/nand/bcm47xx_nand.c 1970-01-01 05:30:00.000000000 +0530 +++ b/drivers/mtd/nand/bcm47xx_nand.c 2012-01-10 14:49:43.432647393 +0530 @@ -0,0 +1,833 @@ +/* + * BCMA nand flash interface + * + * Copyright 2011, Tathagata Das<tathag...@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. + * + * $Id$ + */ + +#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> + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_VERSION 1 +#define TRX_MAX_LEN 0x3A0000 +#define TRX_NO_HEADER 1 /* Do not write TRX header */ +#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */ +#define TRX_MAX_OFFSET 3 + +#define SI_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ + +/* A boot/binary may have an embedded block that describes its size */ +#define BISZ_OFFSET 0x3e0 /* At this offset into the binary */ +#define BISZ_MAGIC 0x4249535a /* Marked with this value: 'BISZ' */ +#define BISZ_MAGIC_IDX 0 /* Word 0: magic */ +#define BISZ_TXTST_IDX 1 /* 1: text start */ +#define BISZ_TXTEND_IDX 2 /* 2: text end */ +#define BISZ_DATAST_IDX 3 /* 3: data start */ +#define BISZ_DATAEND_IDX 4 /* 4: data end */ +#define BISZ_BSSST_IDX 5 /* 5: bss start */ +#define BISZ_BSSEND_IDX 6 /* 6: bss end */ +#define BISZ_SIZE 7 /* descriptor size in 32-bit intergers */ + +static int bcm47xx_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip); + +struct squashfs_super_block { + __le32 s_magic; + __le32 pad0[9]; + __le64 bytes_used; +}; + +struct trx_header { + u32 magic; /* "HDR0" */ + u32 len; /* Length of file including header */ + u32 crc32; /* 32-bit CRC from flag_version to end of file */ + u32 flag_version; /* 0:15 flags, 16:31 version */ + u32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ +}; + +/* Private Global variable */ +static u32 read_offset = 0; +static u32 write_offset = 0; + +static struct mtd_partition bcm947xx_nflash_parts[] = +{ + { + .name = "boot", + .size = 0, + .offset = 0, + .mask_flags = MTD_WRITEABLE + }, + { + .name = "nvram", + .size = 0, + .offset = 0 + }, + { + .name = "board_data", + .offset = 0, + .size = 0, + }, + { + .name = "POT1", + .offset = 0, + .size = 0, + }, + { + .name = "POT2", + .offset = 0, + .size = 0, + }, + { + .name = "T_Meter1", + .offset = 0, + .size = 0, + }, + { + .name = "T_Meter2", + .offset = 0, + .size = 0, + }, + { + .name = "ML1", + .offset = 0, + .size = 0, + }, + { + .name = "ML2", + .offset = 0, + .size = 0, + }, + { + .name = "ML3", + .offset = 0, + .size = 0, + }, + { + .name = "ML4", + .offset = 0, + .size = 0, + }, + { + .name = "ML5", + .offset = 0, + .size = 0, + }, + { + .name = "ML6", + .offset = 0, + .size = 0, + }, + { + .name = "ML7", + .offset = 0, + .size = 0, + }, + { + .name = "linux", + .size = 0, + .offset = 0 + }, + { + .name = "rootfs", + .size = 0, + .offset = 0, + .mask_flags = MTD_WRITEABLE + }, + { + .name = "rootfs_data", + .size = 0xC0000, + .offset = NFL_BOOT_OS_SIZE - 0xC0000, + } +};
There is a mix between the pure NAND driver here and partitions/data which deserve a separate partition parser, that can be done later.
+ +static int +nflash_mtd_poll(struct bcm47xx_nflash *nflash, unsigned int offset, int timeout) +{ + unsigned long now = jiffies; + int ret = 0; + + for (;;) { + if (!nflash->poll(nflash, offset)) { + 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; + struct mtd_partition *part = NULL; + u32 extra = 0; + u8 *tmpbuf = NULL; + int size; + u32 offset, blocksize, mask, blk_offset, off; + u32 skip_bytes = 0, good_bytes = 0; + int blk_idx, i; + int need_copy = 0; + u8 *ptr = NULL; + + /* Locate the part */ + for (i = 0; bcm947xx_nflash_parts[i].name; i++) { + if (from>= bcm947xx_nflash_parts[i].offset&& + ((bcm947xx_nflash_parts[i+1].name == NULL) || (from< bcm947xx_nflash_parts[i+1].offset))) { + part =&bcm947xx_nflash_parts[i]; + break; + } + } + if (!part) + return -EINVAL; + /* 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; + blk_offset = offset& ~mask; + good_bytes = part->offset& ~mask; + /* Check and skip bad blocks */ + for (blk_idx = good_bytes/blocksize; blk_idx< mtd->eraseregions->numblocks; blk_idx++) { + if (nflash->map[blk_idx] != 0) { + skip_bytes += blocksize; + nflash->map[blk_idx] = 1; + } else { + if (good_bytes == blk_offset) + break; + good_bytes += blocksize; + } + } + + if (blk_idx == mtd->eraseregions->numblocks) { + ret = -EINVAL; + goto done; + } + blk_offset = blocksize * blk_idx; + *retlen = 0; + while (len> 0) { + off = offset + skip_bytes; + + /* Check and skip bad blocks */ + if (off>= (blk_offset + blocksize)) { + blk_offset += blocksize; + blk_idx++; + while (((nflash->map[blk_idx] != 0))&& (blk_offset< mtd->size)) { + skip_bytes += blocksize; + nflash->map[blk_idx] = 1; + blk_offset += blocksize; + blk_idx++; + } + if (blk_offset>= mtd->size) { + ret = -EINVAL; + goto done; + } + off = offset + skip_bytes; + } + + if ((bytes = nflash->read(nflash, 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 int bcm47xx_write(struct mtd_info *mtd, loff_t to, const u_char *buf, 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 0; + + if ((to + len)> mtd->size) + return -EINVAL; + + while (len> 0) { + int ret; + + if (len> nflash->pagesize) + ret = nflash->write(nflash, to, nflash->pagesize, buf); + else + ret = nflash->write(nflash, to, len, buf); + if (ret) + return ret; + + ret = nflash_mtd_poll(nflash, (unsigned int) to, HZ / 10); + if (ret) + return ret; + + to += (loff_t) nflash->pagesize; + len -= nflash->pagesize; + buf += nflash->pagesize; + } + + return 0; +} + +static void 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; + int i, j, ret = 0; + + /* Check address range */ + if (!len) + return; + if ((addr + len)> mtd->size) + return; + + /* Ensure that requested regions are aligned */ + for (i = 0; i< mtd->numeraseregions; i++) { + for (j = 0; j< mtd->eraseregions[i].numblocks; j++) { + if (addr == mtd->eraseregions[i].offset + + mtd->eraseregions[i].erasesize * j&& + len>= mtd->eraseregions[i].erasesize) { + ret = nflash->erase(nflash, addr); + if (ret< 0) + break; + ret = nflash_mtd_poll(nflash, addr, 10 * HZ); + if (ret) + break; + addr += mtd->eraseregions[i].erasesize; + len -= mtd->eraseregions[i].erasesize; + } + } + if (ret) + break; + } + + return; +} + +struct mtd_partition * init_nflash_mtd_partitions(struct mtd_info *mtd, size_t size) +{ + struct romfs_super_block *romfsb; + struct cramfs_super *cramfsb; + struct squashfs_super_block *squashfsb; + struct trx_header *trx; + unsigned char buf[NFL_SECTOR_SIZE]; + u32 blocksize, mask, blk_offset, off, shift = 0; + u32 bootsz, *bisz; + int ret, i; + u32 top; + int idx; + struct nand_chip *nchip = (struct nand_chip *)mtd->priv; + struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; + + romfsb = (struct romfs_super_block *) buf; + cramfsb = (struct cramfs_super *) buf; + squashfsb = (struct squashfs_super_block *) buf; + trx = (struct trx_header *) buf; + + /* Look at every block boundary till 32MB; higher space is reserved for application data. */ + blocksize = mtd->erasesize; + for (off = NFL_BOOT_SIZE; off< NFL_BOOT_OS_SIZE; off += blocksize) { + mask = blocksize - 1; + blk_offset = off& ~mask; + if (bcm47xx_block_bad(mtd, blk_offset, 1) != 0) + continue; + memset(buf, 0xe5, sizeof(buf)); + if ((ret = nflash->read(nflash, off, sizeof(buf), buf)) != sizeof(buf)) { + printk("%s: nflash_read return %d\n", mtd->name, ret); + continue; + } + + /* Try looking at TRX header for rootfs offset */ + if (le32_to_cpu(trx->magic) == TRX_MAGIC&& off>= 0x500000) { + mask = NFL_SECTOR_SIZE - 1; + off = off + (le32_to_cpu(trx->offsets[1])& ~mask) - blocksize; + shift = (le32_to_cpu(trx->offsets[1])& mask); + romfsb = (struct romfs_super_block *)((unsigned char *)romfsb + shift); + cramfsb = (struct cramfs_super *)((unsigned char *)cramfsb + shift); + squashfsb = (struct squashfs_super_block *)((unsigned char *)squashfsb + shift); + continue; + } + + /* romfs is at block zero too */ + if (romfsb->word0 == ROMSB_WORD0&& + romfsb->word1 == ROMSB_WORD1) { + printk(KERN_NOTICE + "%s: romfs filesystem found at block %d\n", + mtd->name, off / BLOCK_SIZE); + goto done; + } + + /* so is cramfs */ + if (cramfsb->magic == CRAMFS_MAGIC) { + printk(KERN_NOTICE + "%s: cramfs filesystem found at block %d\n", + mtd->name, off / BLOCK_SIZE); + goto done; + } + + if (squashfsb->s_magic == SQUASHFS_MAGIC) { + printk(KERN_NOTICE + "%s: squash filesystem with lzma found at block %x, offset: %x\n", + mtd->name, off / BLOCK_SIZE, off); + goto done; + } + } + + printk(KERN_NOTICE + "%s: Couldn't find valid ROM disk image\n", + mtd->name); + +done: + /* Default is 256K boot partition */ + bootsz = 256 * 1024; + + /* Do we have a self-describing binary image? */ + bisz = (u32 *)KSEG1ADDR(SI_FLASH1 + BISZ_OFFSET); + if (bisz[BISZ_MAGIC_IDX] == BISZ_MAGIC) { + int isz = bisz[BISZ_DATAEND_IDX] - bisz[BISZ_TXTST_IDX]; + + if (isz> (1024 * 1024)) + bootsz = 2048 * 1024; + else if (isz> (512 * 1024)) + bootsz = 1024 * 1024; + else if (isz> (256 * 1024)) + bootsz = 512 * 1024; + else if (isz<= (128 * 1024)) + bootsz = 128 * 1024; + } + if (bootsz> mtd->erasesize) { + /* Prepare double space in case of bad blocks */ + bootsz = (bootsz<< 1); + } else { + /* CFE occupies at least one block */ + bootsz = mtd->erasesize; + } + + printk("Boot partition size = %d(0x%x)\n", bootsz, bootsz); + + /* Size pmon */ + bcm947xx_nflash_parts[0].size = bootsz; + + /* Setup NVRAM MTD partition */ + bcm947xx_nflash_parts[1].offset = bootsz; + bcm947xx_nflash_parts[1].size = NFL_BOOT_SIZE - bootsz; + + i = (sizeof(bcm947xx_nflash_parts)/sizeof(struct mtd_partition)) - 2; + top = NFL_BOOT_OS_SIZE; + + for (idx = 2; idx<= i - 2; idx++) + { + if (strncmp(bcm947xx_nflash_parts[idx].name, "board_data", 10) == 0) + bcm947xx_nflash_parts[idx].size = 0x40000; /* 256K */ + else if (strncmp(bcm947xx_nflash_parts[idx].name, "POT", 3) == 0) + bcm947xx_nflash_parts[idx].size = 0x40000; /* 256K */ + else if (strncmp(bcm947xx_nflash_parts[idx].name, "T_Meter", 7) == 0) + bcm947xx_nflash_parts[idx].size = 0x40000; /* 256K */ + else if (strncmp(bcm947xx_nflash_parts[idx].name, "ML", 2) == 0) + bcm947xx_nflash_parts[idx].size = 0x40000; /* 256K */ + else if (strncmp(bcm947xx_nflash_parts[idx].name, "linux", 5) == 0) + break; + else if (strncmp(bcm947xx_nflash_parts[idx].name, "rootfs", 6) == 0) + break; + else + { + printk(KERN_ERR "%s: Unknow MTD name %s\n", + __FUNCTION__, bcm947xx_nflash_parts[idx].name); + break; + } + + bcm947xx_nflash_parts[idx].offset = bcm947xx_nflash_parts[idx - 1].offset + + bcm947xx_nflash_parts[idx - 1].size; + } + + /* Find and size rootfs */ + if (off< size) { + bcm947xx_nflash_parts[i].offset = off + shift; + bcm947xx_nflash_parts[i].size = + top - bcm947xx_nflash_parts[i].offset; + } + + /* Size linux (kernel and rootfs) */ + bcm947xx_nflash_parts[i - 1].offset = + bcm947xx_nflash_parts[i - 2].offset + bcm947xx_nflash_parts[i - 2].size; + bcm947xx_nflash_parts[i - 1].size = + top - bcm947xx_nflash_parts[i - 1].offset; + + return bcm947xx_nflash_parts; +} + +/* 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; + + /* 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 * nflash->pagesize; + nflash->next_opcode = 0; + break; + + case NAND_CMD_READOOB: + read_offset = page_addr * nflash->pagesize; + nflash->next_opcode = 0; + break; + + case NAND_CMD_SEQIN: + write_offset = page_addr * nflash->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*nflash->pagesize, nflash->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: + printk("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;
read_byte() is not used only for chip identification, so you should not hijack it, and rather use bcm47xx_mtd_read() with the proper size/buffer.
+} + +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) +{ + printk("##### %s:%s:%d ####\n", __FILE__, __func__, __LINE__); + 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; + + if ((ofs>> 20)>= nflash->size) + return -1; + if ((ofs& (nflash->blocksize - 1)) != 0) { + return -1; + } + for (i = 0; i< 2; i++) { + off = ofs + nflash->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;
This will not necessarily work when marking blocks is done outside of the OOB/spare area.
+} + +extern int add_mtd_partitions(struct mtd_info *master, + const struct mtd_partition *parts, + int nbparts); +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, nr_part = 0; + struct mtd_partition *parts; + struct nand_flash_dev bcm47xx_flash_ids; + + printk("FOUND NAND flash: blocksize=%dKB, numblocks=%d, size=%dMB, pagesize:%x\n", + nflash->blocksize/1024, nflash->numblocks, nflash->size, nflash->pagesize); + + mtd =&nflash->mtd; + + /* Setup region info */ + nflash->region.offset = 0; + nflash->region.erasesize = nflash->blocksize; + nflash->region.numblocks = nflash->numblocks; + if (nflash->region.erasesize> nflash->mtd.erasesize) + nflash->mtd.erasesize = nflash->region.erasesize; + mtd->size = (nflash->size>= (1<< 11)) ? (1<< 31) : (nflash->size<< 20); + mtd->numeraseregions = 1; + nflash->map = (unsigned char *)kmalloc(nflash->numblocks, GFP_KERNEL); + if (nflash->map) + memset(nflash->map, 0, nflash->numblocks);
You should use nand_scan_ident() instead of using the platform_data here.
+ + nchip =&nflash->nand; + + /* Register with MTD */ + mtd->name = "bcm47xx-nflash"; + 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; + + bcm47xx_flash_ids.name = mtd->name; + bcm47xx_flash_ids.id = nflash->id[1]; + bcm47xx_flash_ids.pagesize = nflash->pagesize; + bcm47xx_flash_ids.chipsize = (NFL_BOOT_OS_SIZE>> 20); + bcm47xx_flash_ids.erasesize = nflash->mtd.erasesize; + bcm47xx_flash_ids.options = 0; + + nchip->ecc.mode = NAND_ECC_NONE; + + parts = init_nflash_mtd_partitions(mtd, mtd->size); + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1,&bcm47xx_flash_ids)) { + printk("nand_scan_ident failed\n"); + ret = -ENXIO; + goto done; + } + + /* second phase scan */ + if (nand_scan_tail(mtd)) { + printk("nand_scan_tail failed\n"); + ret = -ENXIO; + goto done; + } + + /* Over write mtd related functions */ + mtd->eraseregions =&nflash->region;; + mtd->flags |= MTD_WRITEABLE; + + nr_part = (sizeof(bcm947xx_nflash_parts)/sizeof(struct mtd_partition)); + ret = mtd_device_register(mtd, parts, nr_part); + + if (ret) { + printk("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); + kfree(nflash->map); + } + + 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"); diff -Naur a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig --- a/drivers/mtd/nand/Kconfig 2011-11-29 04:17:43.000000000 +0530 +++ b/drivers/mtd/nand/Kconfig 2012-01-05 11:41:56.823832992 +0530 @@ -530,4 +530,14 @@ 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 + select MTD_BCM47XX_PARTS + help + Support for bcm47xx nand flash + + endif # MTD_NAND diff -Naur a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile --- a/drivers/mtd/nand/Makefile 2011-11-29 04:17:43.000000000 +0530 +++ b/drivers/mtd/nand/Makefile 2012-01-05 11:41:56.926832980 +0530 @@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o +obj-$(CONFIG_MTD_NAND_BCM47XX) += bcm47xx_nand.o nand-objs := nand_base.o nand_bbt.o diff -Naur a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h --- a/include/linux/bcma/bcma_driver_chipcommon.h 2011-12-26 13:09:34.932170411 +0530 +++ b/include/linux/bcma/bcma_driver_chipcommon.h 2012-01-05 11:41:56.931832993 +0530 @@ -376,6 +376,7 @@ enum bcma_flash_type { BCMA_PFLASH, BCMA_SFLASH, + BCMA_NFLASH, }; struct bcma_pflash { @@ -392,6 +393,17 @@ }; #endif /* CONFIG_BCMA_SFLASH */ +#ifdef CONFIG_BCMA_NFLASH +struct bcma_nflash { + u32 blocksize; /* Block size */ + u32 pagesize; /* Page size */ + u32 numblocks; /* Number of blocks */ + u32 type; /* Type */ + u32 size; /* Total size in bytes */ + u8 id[5]; +}; +#endif + struct bcma_serial_port { void *regs; unsigned long clockspeed; @@ -417,6 +429,9 @@ #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 +498,13 @@ 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_ */ diff -Naur a/include/linux/mtd/bcm47xx_nand.h b/include/linux/mtd/bcm47xx_nand.h --- a/include/linux/mtd/bcm47xx_nand.h 1970-01-01 05:30:00.000000000 +0530 +++ b/include/linux/mtd/bcm47xx_nand.h 2012-01-10 14:50:16.270647389 +0530 @@ -0,0 +1,134 @@ +/* + * Broadcom chipcommon NAND flash interface + * + * 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. + * + * $Id: nflash.h,v 1.1.2.3 2010/10/08 03:20:05 Exp $ + */ + +#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_ */ _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel
_______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel