GIT ff0dab64b4e9ce3a073365342297e76ddaae9697 git://git.infradead.org/mtd-2.6.git commit Author: Ricard Wanderlöf Date: Mon Oct 23 09:33:34 2006 +0200 [MTD] NAND: Fix nand_default_mark_blockbad() when flash-based BBT disabled When a flash-based BBT is not used, nand_default_mark_blockbad() is supposed to mark the block bad in the oob. However, it sets the wrong length variable so that no bad block marker is in fact written. This patch attempts to rectify that. (As note, it seems to be that logically, it shouldn't be necessary to set both length variables, as one appears to be for the main buffer, and one for the oob buffer, but this is how it is done in several places, including the code for the mtd character device MEMWRITEOOB and MEMREADOOB ioctls. I'm not sure if this is a temporary solution during some rework of the mtd infrastructure, or whether there is a deeper thought here.) Signed-off-by: Ricard Wanderlöf Signed-off-by: David Woodhouse commit 784f4d5e66ac1d962091e08fe5a4b238ed8c793d Author: David Woodhouse Date: Sun Oct 22 01:47:45 2006 +0100 [MTD] NAND: Correct setting of chip->oob_poi OOB buffer Signed-off-by: David Woodhouse commit 7dcdcbef5d2b60d1db68fd2c07351f7afd8ad376 Author: David Woodhouse Date: Sat Oct 21 17:09:53 2006 +0100 [MTD] NAND: Combined oob buffer so it's contiguous with data Ditch the separate oobrbuf and oobwbuf fields from the chip buffers, and use only a single buffer immediately after the data. This accommodates NAND controllers such as the OLPC CAFÉ chip, which can't do scatter/gather DMA so needs the OOB buffer to be contiguous with the data, for both read and write. Signed-off-by: David Woodhouse commit 82810b7b6cc7a74c68881a13b0eb66c7a6370fcc Author: Artem Bityutskiy Date: Fri Oct 20 11:23:56 2006 +0300 [MTD] NAND: nandsim: support subpage write As flash cannot do 0->1 bit transitions when programming, do not do this in the simulator too. This makes nandsim able to accept subpage writes. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit a86aaa6ddf32b0401e64e74174042866e0fb3e20 Author: David Anders Date: Thu Oct 19 19:33:19 2006 +0300 [MTD] NOR: leave Intel chips in read-array mode on suspend During some testing with several samsung s3c24xx based devices it was discovered that often the cfi_cmdset_0001.c would not leave the chip in read-array mode on suspend. this is an issue if the same flash chip is used for the bootloader that needs to be read on resume. Signed-off-by: David Anders Signed-off-by: Nicolas Pitre Signed-off-by: David Woodhouse commit d29ebdbee4c196adddf9f412e6ea1f211656744f Author: Artem Bityutskiy Date: Thu Oct 19 16:04:02 2006 +0300 [MTD] core: trivial comments fix Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9f Author: Vijay Kumar Date: Sat Oct 14 21:33:34 2006 +0530 [MTD] NAND: nandsim coding style fix Removes line break after return type in function definitions, to be consistent with the Linux coding style. Signed-off-by: Vijay Kumar Signed-off-by: David Woodhouse commit d086d43640a40dda7783f3c56724048685586d17 Author: Vijay Kumar Date: Sun Oct 8 22:02:31 2006 +0530 [MTD] NAND: nandsim page-wise allocation (2/2) For page wise allocation, an array of flash page pointers is allocated during initialization. The flash pages are themselves allocated when a write occurs to the page. The flash pages are deallocated when they are erased. Signed-off-by: Vijay Kumar Signed-off-by: David Woodhouse commit 47e37743381823a5c2d51ef88cfc3d6cff1f8580 Author: Vijay Kumar Date: Sun Oct 8 22:00:37 2006 +0530 [MTD] NAND: nandsim page-wise allocation (1/2) This patch removes code that does chip mapping. The chip mapping code is no longer used. Signed-off-by: Vijay Kumar Signed-off-by: David Woodhouse commit 6652018c829c26d6ab0524c5c74f70daa5ed478d Author: Yoichi Yuasa Date: Thu Oct 12 17:38:15 2006 +0900 [MTD] MAPS: Remove ITE 8172G and Globespan IVR MTD support This patch has removed ITE 8172G and Globespan IVR MTD support. These boards support have already been removed. Signed-off-by: Yoichi Yuasa Acked-by: Ralf Bächle Signed-off-by: David Woodhouse commit d25ade71ef80e6312b3e0b53583db518ebb11798 Author: Ricard Wanderlöf Date: Tue Oct 17 17:27:11 2006 +0200 [MTD] mtdchar: Fix MEMGETOOBSEL and ECCGETLAYOUT ioctls 1. The ECCGETLAYOUT ioctl copy_to_user() call has a superfluous '&' causing the resulting information to be garbage rather than the intended mtd->ecclayout. 2. The MEMGETOOBSEL misses copying mtd->ecclayout->eccbytes so the resulting field of the returned structure contains garbage. Signed-off-by: Ricard Wanderlöf Signed-off-by: David Woodhouse commit 42cb1403af8a755b3dfebeb9d2a5f73bc48832a1 Author: Andrew Victor Date: Thu Oct 19 18:24:35 2006 +0200 [MTD] NAND: AT91 NAND driver This version only differs from version posted by Savin Zlobec (20 Jun 2006) in that the AT91RM9200-specific chip-select / bus setup code has been moved from the at91_nand.c driver into the processor-specific file. From: Savin Zlobec Signed-off-by: Andrew Victor Signed-off-by: David Woodhouse commit c7cf0c68ea4e2020db7204eb0ed4412b2cbb167d Author: Qi Yong Date: Mon Oct 16 19:42:23 2006 +0800 [JFFS2] Fix jffs2_follow_link() typo typo fix: noticed this typo while reading the patch "jffs2: fix symlink error handling" Signed-off-by: David Woodhouse commit c7438d02b384e82261e28fc280167f4e7a65e822 Author: Alan Cox Date: Fri Oct 20 14:41:06 2006 -0700 [MTD] MAPS: esb2rom: use hotplug safe interfaces Fairly self explanatory. Keep a reference initially, drop it when we free up the driver resources. Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit f33686b5a79674bec0e1aa553d420485e3a12899 Author: Alexey Dobriyan Date: Fri Oct 20 14:41:05 2006 -0700 [MTD] JEDEC probe: fix comment typo (devic) Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit ea7415cca922389b4f3c0cf75e0af9fbf827880e Author: Akinobu Mita Date: Fri Oct 20 14:41:05 2006 -0700 [JFFS2] Use rb_first() and rb_last() cleanup Use rb_first() and rb_last() to implement frag_first() and frag_last(). Signed-off-by: Akinbou Mita Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 29175778b07aa60e7f8030bd95d69f70070cc1f7 Author: Lew Glendenning Date: Fri Oct 20 14:41:04 2006 -0700 [MTD] MAPS: Support for BIOS flash chips on Intel ESB2 southbridge Add MTD map driver for BIOS flash chips connected to the Intel ESB2 southbridge. [akpm@osdl.org: coding-style fixes, build fix] Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 89072ef99367cd6fab37b85d6a59a575084c2d2c Author: Ryan Jackson Date: Fri Oct 20 14:41:03 2006 -0700 [MTD] CHIPS: Support for SST 49LF040B flash chip Add chip driver and JEDEC probe support for the SST 49LF040B flash chip. Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit c9073ce02adfa273a3d6d53eac8c4c035510ad9c Author: Ryan Jackson Date: Fri Oct 20 14:41:01 2006 -0700 [MTD] MAPS: Add parameter to amd76xrom to override rom window size The 2 bits controlling the window size are often set to allow reading the BIOS, but too small to allow writing, since the lock registers are 4MiB lower in the address space than the data. This is intended to prevent flashing the bios, perhaps accidentally. The bits are 6 and 7. If both bits are set, it is a 5MiB window. If only the 7 Bit is set, it is a 4MiB window. Otherwise, it is a 64KiB window. This parameter allows the driver to override the BIOS settings. Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 1ef93a0f668c8736cb6b6c3a43a5b8101efa24af Author: Adrian Bunk Date: Mon Oct 9 01:16:38 2006 +0200 [MTD] SSFDC must depend on BLOCK This patch fixes the following compile error with CONFIG_SSFDC=m, CONFIG_BLOCK=n: <-- snip --> ... CC [M] drivers/mtd/mtd_blkdevs.o /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:40: warning: ‘struct request’ declared inside parameter list /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:40: warning: its scope is only this definition or declaration, which is probably not what you want /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c: In function ‘do_blktrans_request’: /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:45: error: dereferencing pointer to incomplete type ... make[3]: *** [drivers/mtd/mtd_blkdevs.o] Error 1 <-- snip --> Bug report by Jesper Juhl. This patch also removes a pointless "default n" from the SSFDC option. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse commit a6b1d82d079a99d09761ee5fbc66c49f33b42324 Author: Jeff Garzik Date: Wed Oct 4 07:57:18 2006 -0400 [JFFS2] kill warning RE debug-only variables gcc emits the following warning on a 'allmodconfig' build: fs/jffs2/xattr.c: In function ‘unrefer_xattr_datum’: fs/jffs2/xattr.c:402: warning: unused variable ‘version’ fs/jffs2/xattr.c:402: warning: unused variable ‘xid’ Given that these variables are only used in the debug printk, and they merely remove a deref, we can easily kill the warning by adding the derefs to the debug printk. Signed-off-by: Jeff Garzik Signed-off-by: David Woodhouse drivers/mtd/Kconfig | 3 drivers/mtd/chips/cfi_cmdset_0001.c | 2 drivers/mtd/chips/cfi_cmdset_0002.c | 8 - drivers/mtd/chips/jedec_probe.c | 17 + drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 drivers/mtd/maps/amd76xrom.c | 34 ++- drivers/mtd/maps/cstm_mips_ixx.c | 121 --------- drivers/mtd/maps/esb2rom.c | 450 +++++++++++++++++++++++++++++++++++ drivers/mtd/mtdchar.c | 3 drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 drivers/mtd/nand/at91_nand.c | 220 +++++++++++++++++ drivers/mtd/nand/nand_base.c | 29 +- drivers/mtd/nand/nandsim.c | 235 ++++++++++++------ fs/jffs2/nodelist.h | 10 - fs/jffs2/symlink.c | 2 fs/jffs2/xattr.c | 5 include/linux/mtd/nand.h | 6 19 files changed, 919 insertions(+), 244 deletions(-) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index a304b34..291660a 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -265,8 +265,7 @@ config RFD_FTL config SSFDC tristate "NAND SSFDC (SmartMedia) read only translation layer" - depends on MTD - default n + depends on MTD && BLOCK help This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 7ea49a0..e249736 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -2224,6 +2224,8 @@ static int cfi_intelext_suspend(struct m case FL_CFI_QUERY: case FL_JEDEC_QUERY: if (chip->oldstate == FL_READY) { + /* place the chip in a known state before suspend */ + map_write(map, CMD(0xFF), cfi->chips[i].start); chip->oldstate = chip->state; chip->state = FL_PM_SUSPENDED; /* No need to wake_up() on this state change - diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 702ae4c..ca0882b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -48,6 +48,7 @@ #define MANUFACTURER_AMD 0x0001 #define MANUFACTURER_ATMEL 0x001F #define MANUFACTURER_SST 0x00BF #define SST49LF004B 0x0060 +#define SST49LF040B 0x0050 #define SST49LF008A 0x005a #define AT49BV6416 0x00d6 @@ -233,6 +234,7 @@ #endif }; static struct cfi_fixup jedec_fixup_table[] = { { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -519,10 +521,12 @@ static int get_chip(struct map_info *map if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ goto sleep; - if (!(mode == FL_READY || mode == FL_POINT + if (!( mode == FL_READY + || mode == FL_POINT || !cfip || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) - || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) + || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1) + ))) goto sleep; /* We could check to see if we're trying to access the sector diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 1154dac..58e561e 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -154,6 +154,7 @@ #define SST39LF040 0x00D7 #define SST39SF010A 0x00B5 #define SST39SF020A 0x00B6 #define SST49LF004B 0x0060 +#define SST49LF040B 0x0050 #define SST49LF008A 0x005a #define SST49LF030A 0x001C #define SST49LF040A 0x0051 @@ -1401,6 +1402,20 @@ static const struct amd_flash_info jedec } }, { .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF040B, + .name = "SST 49LF040B", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,128), + } + }, { + + .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF004B, .name = "SST 49LF004B", .uaddr = { @@ -1874,7 +1889,7 @@ static int cfi_jedec_setup(struct cfi_pr /* - * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing + * There is a BIG problem properly ID'ing the JEDEC device and guaranteeing * the mapped address, unlock addresses, and proper chip ID. This function * attempts to minimize errors. It is doubtfull that this probe will ever * be perfect - consequently there should be some module parameters that diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 24747bd..7514a9b 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -184,6 +184,15 @@ config MTD_ICHXROM BE VERY CAREFUL. +config MTD_ESB2ROM + tristate "BIOS flash chip on Intel ESB Controller Hub 2" + depends on X86 && MTD_JEDECPROBE + help + Support for treating the BIOS flash chip on ESB2 motherboards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + config MTD_SCB2_FLASH tristate "BIOS flash chip on Intel SCB2 boards" depends on X86 && MTD_JEDECPROBE diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 191c192..9061432 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 797caff..78b6711 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,23 @@ struct amd76xrom_map_info { char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; +/* The 2 bits controlling the window size are often set to allow reading + * the BIOS, but too small to allow writing, since the lock registers are + * 4MiB lower in the address space than the data. + * + * This is intended to prevent flashing the bios, perhaps accidentally. + * + * This parameter allows the normal driver to over-ride the BIOS settings. + * + * The bits are 6 and 7. If both bits are set, it is a 5MiB window. + * If only the 7 Bit is set, it is a 4MiB window. Otherwise, a + * 64KiB window. + * + */ +static uint win_size_bits; +module_param(win_size_bits, uint, 0); +MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x43 byte, normally set by BIOS."); + static struct amd76xrom_window amd76xrom_window = { .maps = LIST_HEAD_INIT(amd76xrom_window.maps), }; @@ -95,6 +113,16 @@ static int __devinit amd76xrom_init_one /* Remember the pci dev I find the window in - already have a ref */ window->pdev = pdev; + /* Enable the selected rom window. This is often incorrectly + * set up by the BIOS, and the 4MiB offset for the lock registers + * requires the full 5MiB of window space. + * + * This 'write, then read' approach leaves the bits for + * other uses of the hardware info. + */ + pci_read_config_byte(pdev, 0x43, &byte); + pci_write_config_byte(pdev, 0x43, byte | win_size_bits ); + /* Assume the rom window is properly setup, and find it's size */ pci_read_config_byte(pdev, 0x43, &byte); if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) { @@ -129,12 +157,6 @@ static int __devinit amd76xrom_init_one (unsigned long long)window->rsrc.end); } -#if 0 - - /* Enable the selected rom window */ - pci_read_config_byte(pdev, 0x43, &byte); - pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits); -#endif /* Enable writes through the rom window */ pci_read_config_byte(pdev, 0x40, &byte); diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c index df2c38e..d57eba2 100644 --- a/drivers/mtd/maps/cstm_mips_ixx.c +++ b/drivers/mtd/maps/cstm_mips_ixx.c @@ -40,62 +40,6 @@ #include #include #include -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) -#define CC_GCR 0xB4013818 -#define CC_GPBCR 0xB401380A -#define CC_GPBDR 0xB4013808 -#define CC_M68K_DEVICE 1 -#define CC_M68K_FUNCTION 6 -#define CC_CONFADDR 0xB8004000 -#define CC_CONFDATA 0xB8004004 -#define CC_FC_FCR 0xB8002004 -#define CC_FC_DCR 0xB8002008 -#define CC_GPACR 0xB4013802 -#define CC_GPAICR 0xB4013804 -#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ - -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) -void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) -{ - static DEFINE_SPINLOCK(vpp_lock); - static int vpp_count = 0; - unsigned long flags; - - spin_lock_irqsave(&vpp_lock, flags); - - if (vpp) { - if (!vpp_count++) { - __u16 data; - __u8 data1; - static u8 first = 1; - - // Set GPIO port B pin3 to high - data = *(__u16 *)(CC_GPBCR); - data = (data & 0xff0f) | 0x0040; - *(__u16 *)CC_GPBCR = data; - *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08; - if (first) { - first = 0; - /* need to have this delay for first - enabling vpp after powerup */ - udelay(40); - } - } - } else { - if (!--vpp_count) { - __u16 data; - - // Set GPIO port B pin3 to high - data = *(__u16 *)(CC_GPBCR); - data = (data & 0xff3f) | 0x0040; - *(__u16 *)CC_GPBCR = data; - *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7; - } - } - spin_unlock_irqrestore(&vpp_lock, flags); -} -#endif - /* board and partition description */ #define MAX_PHYSMAP_PARTITIONS 8 @@ -107,29 +51,6 @@ struct cstm_mips_ixx_info { int num_partitions; }; -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) -#define PHYSMAP_NUMBER 1 // number of board desc structs needed, one per contiguous flash type -const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = -{ - { // 28F128J3A in 2x16 configuration - "big flash", // name - 0x08000000, // window_addr - 0x02000000, // window_size - 4, // bankwidth - 1, // num_partitions - } - -}; -static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { -{ // 28F128J3A in 2x16 configuration - { - .name = "main partition ", - .size = 0x02000000, // 128 x 2 x 128k byte sectors - .offset = 0, - }, -}, -}; -#else /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ #define PHYSMAP_NUMBER 1 // number of board desc structs needed, one per contiguous flash type const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = { @@ -151,7 +72,6 @@ static struct mtd_partition cstm_mips_ix }, }, }; -#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ struct map_info cstm_mips_ixx_map[PHYSMAP_NUMBER]; @@ -184,17 +104,10 @@ int __init init_cstm_mips_ixx(void) cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name; cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size; cstm_mips_ixx_map[i].bankwidth = cstm_mips_ixx_board_desc[i].bankwidth; -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) - cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp; -#endif simple_map_init(&cstm_mips_ixx_map[i]); //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt)); } -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) - setup_ITE_IVR_flash(); -#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ - for (i=0;i> 8 >>1; // Bug: we must shift one more bit - - /* need to set ITE flash to 32 bits instead of default 8 */ -#ifdef CONFIG_MIPS_IVR - *(__u32 *)CC_FC_FCR = 0x55; - *(__u32 *)CC_GPACR = 0xfffc; -#else - *(__u32 *)CC_FC_FCR = 0x77; -#endif - /* turn bursting off */ - *(__u32 *)CC_FC_DCR = 0x0; - - /* setup for one chip 4 byte PCI access */ - PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x60, size | base); - PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x64, 0x02); -} -#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ module_init(init_cstm_mips_ixx); module_exit(cleanup_cstm_mips_ixx); @@ -280,4 +161,4 @@ module_exit(cleanup_cstm_mips_ixx); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alice Hennessy "); -MODULE_DESCRIPTION("MTD map driver for ITE 8172G and Globespan IVR boards"); +MODULE_DESCRIPTION("MTD map driver for MIPS boards"); diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c new file mode 100644 index 0000000..a9d808a --- /dev/null +++ b/drivers/mtd/maps/esb2rom.c @@ -0,0 +1,450 @@ +/* + * esb2rom.c + * + * Normal mappings of flash chips in physical memory + * through the Intel ESB2 Southbridge. + * + * This was derived from ichxrom.c in May 2006 by + * Lew Glendenning + * + * Eric Biederman, of course, was a major help in this effort. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOD_NAME KBUILD_BASENAME + +#define ADDRESS_NAME_LEN 18 + +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ + +#define BIOS_CNTL 0xDC +#define BIOS_LOCK_ENABLE 0x02 +#define BIOS_WRITE_ENABLE 0x01 + +/* This became a 16-bit register, and EN2 has disappeared */ +#define FWH_DEC_EN1 0xD8 +#define FWH_F8_EN 0x8000 +#define FWH_F0_EN 0x4000 +#define FWH_E8_EN 0x2000 +#define FWH_E0_EN 0x1000 +#define FWH_D8_EN 0x0800 +#define FWH_D0_EN 0x0400 +#define FWH_C8_EN 0x0200 +#define FWH_C0_EN 0x0100 +#define FWH_LEGACY_F_EN 0x0080 +#define FWH_LEGACY_E_EN 0x0040 +/* reserved 0x0020 and 0x0010 */ +#define FWH_70_EN 0x0008 +#define FWH_60_EN 0x0004 +#define FWH_50_EN 0x0002 +#define FWH_40_EN 0x0001 + +/* these are 32-bit values */ +#define FWH_SEL1 0xD0 +#define FWH_SEL2 0xD4 + +#define FWH_8MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN | FWH_50_EN | FWH_40_EN) + +#define FWH_7MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN | FWH_50_EN) + +#define FWH_6MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN) + +#define FWH_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN) + +#define FWH_4MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN) + +#define FWH_3_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN) + +#define FWH_3MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN) + +#define FWH_2_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN) + +#define FWH_2MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN) + +#define FWH_1_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN) + +#define FWH_1MiB (FWH_F8_EN | FWH_F0_EN) + +#define FWH_0_5MiB (FWH_F8_EN) + + +struct esb2rom_window { + void __iomem* virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; + struct pci_dev *pdev; +}; + +struct esb2rom_map_info { + struct list_head list; + struct map_info map; + struct mtd_info *mtd; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; +}; + +static struct esb2rom_window esb2rom_window = { + .maps = LIST_HEAD_INIT(esb2rom_window.maps), +}; + +static void esb2rom_cleanup(struct esb2rom_window *window) +{ + struct esb2rom_map_info *map, *scratch; + u8 byte; + + /* Disable writes through the rom window */ + pci_read_config_byte(window->pdev, BIOS_CNTL, &byte); + pci_write_config_byte(window->pdev, BIOS_CNTL, + byte & ~BIOS_WRITE_ENABLE); + + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) + release_resource(&map->rsrc); + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + } + pci_dev_put(window->pdev); +} + +static int __devinit esb2rom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; + struct esb2rom_window *window = &esb2rom_window; + struct esb2rom_map_info *map = NULL; + unsigned long map_top; + u8 byte; + u16 word; + + /* For now I just handle the ecb2 and I assume there + * are not a lot of resources up at the top of the address + * space. It is possible to handle other devices in the + * top 16MiB but it is very painful. Also since + * you can only really attach a FWH to an ICHX there + * a number of simplifications you can make. + * + * Also you can page firmware hubs if an 8MiB window isn't enough + * but don't currently handle that case either. + */ + window->pdev = pci_dev_get(pdev); + + /* RLG: experiment 2. Force the window registers to the widest values */ + +/* + pci_read_config_word(pdev, FWH_DEC_EN1, &word); + printk(KERN_DEBUG "Original FWH_DEC_EN1 : %x\n", word); + pci_write_config_byte(pdev, FWH_DEC_EN1, 0xff); + pci_read_config_byte(pdev, FWH_DEC_EN1, &byte); + printk(KERN_DEBUG "New FWH_DEC_EN1 : %x\n", byte); + + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + printk(KERN_DEBUG "Original FWH_DEC_EN2 : %x\n", byte); + pci_write_config_byte(pdev, FWH_DEC_EN2, 0x0f); + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + printk(KERN_DEBUG "New FWH_DEC_EN2 : %x\n", byte); +*/ + + /* Find a region continuous to the end of the ROM window */ + window->phys = 0; + pci_read_config_word(pdev, FWH_DEC_EN1, &word); + printk(KERN_DEBUG "pci_read_config_byte : %x\n", word); + + if ((word & FWH_8MiB) == FWH_8MiB) + window->phys = 0xff400000; + else if ((word & FWH_7MiB) == FWH_7MiB) + window->phys = 0xff500000; + else if ((word & FWH_6MiB) == FWH_6MiB) + window->phys = 0xff600000; + else if ((word & FWH_5MiB) == FWH_5MiB) + window->phys = 0xFF700000; + else if ((word & FWH_4MiB) == FWH_4MiB) + window->phys = 0xffc00000; + else if ((word & FWH_3_5MiB) == FWH_3_5MiB) + window->phys = 0xffc80000; + else if ((word & FWH_3MiB) == FWH_3MiB) + window->phys = 0xffd00000; + else if ((word & FWH_2_5MiB) == FWH_2_5MiB) + window->phys = 0xffd80000; + else if ((word & FWH_2MiB) == FWH_2MiB) + window->phys = 0xffe00000; + else if ((word & FWH_1_5MiB) == FWH_1_5MiB) + window->phys = 0xffe80000; + else if ((word & FWH_1MiB) == FWH_1MiB) + window->phys = 0xfff00000; + else if ((word & FWH_0_5MiB) == FWH_0_5MiB) + window->phys = 0xfff80000; + + /* reserved 0x0020 and 0x0010 */ + window->phys -= 0x400000UL; + window->size = (0xffffffffUL - window->phys) + 1UL; + + /* Enable writes through the rom window */ + pci_read_config_byte(pdev, BIOS_CNTL, &byte); + if (!(byte & BIOS_WRITE_ENABLE) && (byte & (BIOS_LOCK_ENABLE))) { + /* The BIOS will generate an error if I enable + * this device, so don't even try. + */ + printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n"); + goto out; + } + pci_write_config_byte(pdev, BIOS_CNTL, byte | BIOS_WRITE_ENABLE); + + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to the window being "reseved" by the BIOS. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_DEBUG MOD_NAME + ": %s(): Unable to register resource" + " 0x%.08llx-0x%.08llx - kernel bug?\n", + __func__, + (unsigned long long)window->rsrc.start, + (unsigned long long)window->rsrc.end); + } + + /* Map the firmware hub into my address space. */ + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); + goto out; + } + + /* Get the first address to look for an rom chip at */ + map_top = window->phys; + if ((window->phys & 0x3fffff) != 0) { + /* if not aligned on 4MiB, look 4MiB lower in address space */ + map_top = window->phys + 0x400000; + } +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * (Insane hardware design, but most copied Intel's.) + * ==> Probe at most the last 4M of the address space. + */ + if (map_top < 0xffc00000) + map_top = 0xffc00000; +#endif + /* Loop through and look for rom chips */ + while ((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; + + if (!map) + map = kmalloc(sizeof(*map), GFP_KERNEL); + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto out; + } + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = (void __iomem *) + (((unsigned long)(window->virt)) + offset); + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* Firmware hubs only use vpp when being programmed + * in a factory setting. So in-place programming + * needs to use a different method. + */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; + } + } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; + } + if (window->rsrc.parent) { + /* + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. + */ + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + map->rsrc.parent = NULL; + } + } + + /* Make the whole region visible in the map */ + map->map.virt = window->virt; + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) + cfi->chips[i].start += offset; + + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + if (add_mtd_device(map->mtd)) { + map_destroy(map->mtd); + map->mtd = NULL; + goto out; + } + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = NULL; + } + + out: + /* Free any left over map structures */ + kfree(map); + + /* See if I have any map structures */ + if (list_empty(&window->maps)) { + esb2rom_cleanup(window); + return -ENODEV; + } + return 0; +} + +static void __devexit esb2rom_remove_one (struct pci_dev *pdev) +{ + struct esb2rom_window *window = &esb2rom_window; + esb2rom_cleanup(window); +} + +static struct pci_device_id esb2rom_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { 0, }, +}; + +#if 0 +MODULE_DEVICE_TABLE(pci, esb2rom_pci_tbl); + +static struct pci_driver esb2rom_driver = { + .name = MOD_NAME, + .id_table = esb2rom_pci_tbl, + .probe = esb2rom_init_one, + .remove = esb2rom_remove_one, +}; +#endif + +static int __init init_esb2rom(void) +{ + struct pci_dev *pdev; + struct pci_device_id *id; + int retVal; + + pdev = NULL; + for (id = esb2rom_pci_tbl; id->vendor; id++) { + printk(KERN_DEBUG "device id = %x\n", id->device); + pdev = pci_get_device(id->vendor, id->device, NULL); + if (pdev) { + printk(KERN_DEBUG "matched device = %x\n", id->device); + break; + } + } + if (pdev) { + printk(KERN_DEBUG "matched device id %x\n", id->device); + retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]); + pci_dev_put(pdev); + printk(KERN_DEBUG "retVal = %d\n", retVal); + return retVal; + } + return -ENXIO; +#if 0 + return pci_register_driver(&esb2rom_driver); +#endif +} + +static void __exit cleanup_esb2rom(void) +{ + esb2rom_remove_one(esb2rom_window.pdev); +} + +module_init(init_esb2rom); +module_exit(cleanup_esb2rom); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lew Glendenning "); +MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ESB2 southbridge"); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5b6acfc..866c8e0 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -616,6 +616,7 @@ static int mtd_ioctl(struct inode *inode memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); memcpy(&oi.oobfree, mtd->ecclayout->oobfree, sizeof(oi.oobfree)); + oi.eccbytes = mtd->ecclayout->eccbytes; if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) return -EFAULT; @@ -715,7 +716,7 @@ #endif if (!mtd->ecclayout) return -EOPNOTSUPP; - if (copy_to_user(argp, &mtd->ecclayout, + if (copy_to_user(argp, mtd->ecclayout, sizeof(struct nand_ecclayout))) return -EFAULT; break; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1831340..b4b1656 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -232,6 +232,13 @@ config MTD_NAND_CS553X If you say "m", the module will be called "cs553x_nand.ko". +config MTD_NAND_AT91 + bool "Support for NAND Flash / SmartMedia on AT91" + depends on MTD_NAND && ARCH_AT91 + help + Enables support for NAND Flash / Smart Media Card interface + on Atmel AT91 processors. + config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" depends on MTD_NAND && MTD_PARTITIONS diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f747593..27c9f0a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -22,5 +22,6 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250 obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o +obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o nand-objs = nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c new file mode 100644 index 0000000..a58ed37 --- /dev/null +++ b/drivers/mtd/nand/at91_nand.c @@ -0,0 +1,220 @@ +/* + * drivers/mtd/nand/at91_nand.c + * + * Copyright (C) 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +struct at91_nand_host { + struct nand_chip nand_chip; + struct mtd_info mtd; + void __iomem *io_base; + struct at91_nand_data *board; +}; + +/* + * Hardware specific access to control-lines + */ +static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at91_nand_host *host = nand_chip->priv; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writeb(cmd, host->io_base + (1 << host->board->cle)); + else + writeb(cmd, host->io_base + (1 << host->board->ale)); +} + +/* + * Read the Device Ready pin. + */ +static int at91_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at91_nand_host *host = nand_chip->priv; + + return at91_get_gpio_value(host->board->rdy_pin); +} + +/* + * Enable NAND. + */ +static void at91_nand_enable(struct at91_nand_host *host) +{ + if (host->board->enable_pin) + at91_set_gpio_value(host->board->enable_pin, 0); +} + +/* + * Disable NAND. + */ +static void at91_nand_disable(struct at91_nand_host *host) +{ + if (host->board->enable_pin) + at91_set_gpio_value(host->board->enable_pin, 1); +} + +/* + * Probe for the NAND device. + */ +static int __init at91_nand_probe(struct platform_device *pdev) +{ + struct at91_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + int res; + +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partitions = NULL; + int num_partitions = 0; +#endif + + /* Allocate memory for the device structure (and zero it) */ + host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL); + if (!host) { + printk(KERN_ERR "at91_nand: failed to allocate device structure.\n"); + return -ENOMEM; + } + + host->io_base = ioremap(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + if (host->io_base == NULL) { + printk(KERN_ERR "at91_nand: ioremap failed\n"); + kfree(host); + return -EIO; + } + + mtd = &host->mtd; + nand_chip = &host->nand_chip; + host->board = pdev->dev.platform_data; + + nand_chip->priv = host; /* link the private data structures */ + mtd->priv = nand_chip; + mtd->owner = THIS_MODULE; + + /* Set address of NAND IO lines */ + nand_chip->IO_ADDR_R = host->io_base; + nand_chip->IO_ADDR_W = host->io_base; + nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; + nand_chip->dev_ready = at91_nand_device_ready; + nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ + nand_chip->chip_delay = 20; /* 20us command delay time */ + + platform_set_drvdata(pdev, host); + at91_nand_enable(host); + + if (host->board->det_pin) { + if (at91_get_gpio_value(host->board->det_pin)) { + printk ("No SmartMedia card inserted.\n"); + res = ENXIO; + goto out; + } + } + + /* Scan to find existance of the device */ + if (nand_scan(mtd, 1)) { + res = -ENXIO; + goto out; + } + +#ifdef CONFIG_MTD_PARTITIONS + if (host->board->partition_info) + partitions = host->board->partition_info(mtd->size, &num_partitions); + + if ((!partitions) || (num_partitions == 0)) { + printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); + res = ENXIO; + goto release; + } + + res = add_mtd_partitions(mtd, partitions, num_partitions); +#else + res = add_mtd_device(mtd); +#endif + + if (!res) + return res; + +release: + nand_release(mtd); +out: + at91_nand_disable(host); + platform_set_drvdata(pdev, NULL); + iounmap(host->io_base); + kfree(host); + return res; +} + +/* + * Remove a NAND device. + */ +static int __devexit at91_nand_remove(struct platform_device *pdev) +{ + struct at91_nand_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = &host->mtd; + + nand_release(mtd); + + at91_nand_disable(host); + + iounmap(host->io_base); + kfree(host); + + return 0; +} + +static struct platform_driver at91_nand_driver = { + .probe = at91_nand_probe, + .remove = at91_nand_remove, + .driver = { + .name = "at91_nand", + .owner = THIS_MODULE, + }, +}; + +static int __init at91_nand_init(void) +{ + return platform_driver_register(&at91_nand_driver); +} + + +static void __exit at91_nand_exit(void) +{ + platform_driver_unregister(&at91_nand_driver); +} + + +module_init(at91_nand_init); +module_exit(at91_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rick Bronson"); +MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 41bfcae..f4d9ae0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -362,7 +362,7 @@ static int nand_default_block_markbad(st * access */ ofs += mtd->oobsize; - chip->ops.len = 2; + chip->ops.len = chip->ops.ooblen = 2; chip->ops.datbuf = NULL; chip->ops.oobbuf = buf; chip->ops.ooboffs = chip->badblockpos & ~0x01; @@ -755,7 +755,7 @@ static int nand_read_page_raw(struct mtd } /** - * nand_read_page_swecc - {REPLACABLE] software ecc based page read function + * nand_read_page_swecc - [REPLACABLE] software ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data @@ -795,7 +795,7 @@ static int nand_read_page_swecc(struct m } /** - * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function + * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data @@ -839,7 +839,7 @@ static int nand_read_page_hwecc(struct m } /** - * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read + * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data @@ -971,7 +971,6 @@ static int nand_do_read_ops(struct mtd_i page = realpage & chip->pagemask; col = (int)(from & (mtd->writesize - 1)); - chip->oob_poi = chip->buffers->oobrbuf; buf = ops->datbuf; oob = ops->oobbuf; @@ -1270,8 +1269,6 @@ static int nand_do_read_oob(struct mtd_i realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; - chip->oob_poi = chip->buffers->oobrbuf; - while(1) { sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); buf = nand_transfer_oob(chip, buf, ops); @@ -1375,7 +1372,7 @@ static void nand_write_page_raw(struct m } /** - * nand_write_page_swecc - {REPLACABLE] software ecc based page write function + * nand_write_page_swecc - [REPLACABLE] software ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer @@ -1401,7 +1398,7 @@ static void nand_write_page_swecc(struct } /** - * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function + * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer @@ -1429,7 +1426,7 @@ static void nand_write_page_hwecc(struct } /** - * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write + * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer @@ -1626,7 +1623,9 @@ static int nand_do_write_ops(struct mtd_ (chip->pagebuf << chip->page_shift) < (to + ops->len)) chip->pagebuf = -1; - chip->oob_poi = chip->buffers->oobwbuf; + /* If we're not given explicit OOB data, let it be 0xFF */ + if (likely(!oob)) + memset(chip->oob_poi, 0xff, mtd->oobsize); while(1) { int cached = writelen > bytes && page != blockmask; @@ -1655,9 +1654,6 @@ static int nand_do_write_ops(struct mtd_ } } - if (unlikely(oob)) - memset(chip->oob_poi, 0xff, mtd->oobsize); - ops->retlen = ops->len - writelen; return ret; } @@ -1745,7 +1741,6 @@ static int nand_do_write_oob(struct mtd_ if (page == chip->pagebuf) chip->pagebuf = -1; - chip->oob_poi = chip->buffers->oobwbuf; memset(chip->oob_poi, 0xff, mtd->oobsize); nand_fill_oob(chip, ops->oobbuf, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); @@ -2349,8 +2344,8 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->buffers) return -ENOMEM; - /* Preset the internal oob write buffer */ - memset(chip->buffers->oobwbuf, 0xff, mtd->oobsize); + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers->databuf + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 545ff25..abebcab 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -37,10 +37,6 @@ #include #include #include #include -#ifdef CONFIG_NS_ABS_POS -#include -#endif - /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -231,6 +227,14 @@ #define NS_STATE(x) ((x) & ~ACTION_MASK) #define NS_MAX_PREVSTATES 1 /* + * A union to represent flash memory contents and flash buffer. + */ +union ns_mem { + u_char *byte; /* for byte access */ + uint16_t *word; /* for 16-bit word access */ +}; + +/* * The structure which describes all the internal simulator data. */ struct nandsim { @@ -247,17 +251,11 @@ struct nandsim { uint16_t npstates; /* number of previous states saved */ uint16_t stateidx; /* current state index */ - /* The simulated NAND flash image */ - union flash_media { - u_char *byte; - uint16_t *word; - } mem; + /* The simulated NAND flash pages array */ + union ns_mem *pages; /* Internal buffer of page + OOB size bytes */ - union internal_buffer { - u_char *byte; /* for byte access */ - uint16_t *word; /* for 16-bit word access */ - } buf; + union ns_mem buf; /* NAND flash "geometry" */ struct nandsin_geometry { @@ -346,12 +344,49 @@ static struct mtd_info *nsmtd; static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; /* + * Allocate array of page pointers and initialize the array to NULL + * pointers. + * + * RETURNS: 0 if success, -ENOMEM if memory alloc fails. + */ +static int alloc_device(struct nandsim *ns) +{ + int i; + + ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); + if (!ns->pages) { + NS_ERR("alloc_map: unable to allocate page array\n"); + return -ENOMEM; + } + for (i = 0; i < ns->geom.pgnum; i++) { + ns->pages[i].byte = NULL; + } + + return 0; +} + +/* + * Free any allocated pages, and free the array of page pointers. + */ +static void free_device(struct nandsim *ns) +{ + int i; + + if (ns->pages) { + for (i = 0; i < ns->geom.pgnum; i++) { + if (ns->pages[i].byte) + kfree(ns->pages[i].byte); + } + vfree(ns->pages); + } +} + +/* * Initialize the nandsim structure. * * RETURNS: 0 if success, -ERRNO if failure. */ -static int -init_nandsim(struct mtd_info *mtd) +static int init_nandsim(struct mtd_info *mtd) { struct nand_chip *chip = (struct nand_chip *)mtd->priv; struct nandsim *ns = (struct nandsim *)(chip->priv); @@ -439,23 +474,8 @@ init_nandsim(struct mtd_info *mtd) printk("sector address bytes: %u\n", ns->geom.secaddrbytes); printk("options: %#x\n", ns->options); - /* Map / allocate and initialize the flash image */ -#ifdef CONFIG_NS_ABS_POS - ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob); - if (!ns->mem.byte) { - NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", - (void *)CONFIG_NS_ABS_POS); - return -ENOMEM; - } -#else - ns->mem.byte = vmalloc(ns->geom.totszoob); - if (!ns->mem.byte) { - NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n", - ns->geom.totszoob); - return -ENOMEM; - } - memset(ns->mem.byte, 0xFF, ns->geom.totszoob); -#endif + if (alloc_device(ns) != 0) + goto error; /* Allocate / initialize the internal buffer */ ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); @@ -474,11 +494,7 @@ #endif return 0; error: -#ifdef CONFIG_NS_ABS_POS - iounmap(ns->mem.byte); -#else - vfree(ns->mem.byte); -#endif + free_device(ns); return -ENOMEM; } @@ -486,16 +502,10 @@ #endif /* * Free the nandsim structure. */ -static void -free_nandsim(struct nandsim *ns) +static void free_nandsim(struct nandsim *ns) { kfree(ns->buf.byte); - -#ifdef CONFIG_NS_ABS_POS - iounmap(ns->mem.byte); -#else - vfree(ns->mem.byte); -#endif + free_device(ns); return; } @@ -503,8 +513,7 @@ #endif /* * Returns the string representation of 'state' state. */ -static char * -get_state_name(uint32_t state) +static char *get_state_name(uint32_t state) { switch (NS_STATE(state)) { case STATE_CMD_READ0: @@ -562,8 +571,7 @@ get_state_name(uint32_t state) * * RETURNS: 1 if wrong command, 0 if right. */ -static int -check_command(int cmd) +static int check_command(int cmd) { switch (cmd) { @@ -589,8 +597,7 @@ check_command(int cmd) /* * Returns state after command is accepted by command number. */ -static uint32_t -get_state_by_command(unsigned command) +static uint32_t get_state_by_command(unsigned command) { switch (command) { case NAND_CMD_READ0: @@ -626,8 +633,7 @@ get_state_by_command(unsigned command) /* * Move an address byte to the correspondent internal register. */ -static inline void -accept_addr_byte(struct nandsim *ns, u_char bt) +static inline void accept_addr_byte(struct nandsim *ns, u_char bt) { uint byte = (uint)bt; @@ -645,8 +651,7 @@ accept_addr_byte(struct nandsim *ns, u_c /* * Switch to STATE_READY state. */ -static inline void -switch_to_ready_state(struct nandsim *ns, u_char status) +static inline void switch_to_ready_state(struct nandsim *ns, u_char status) { NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); @@ -705,8 +710,7 @@ switch_to_ready_state(struct nandsim *ns * -1 - several matches. * 0 - operation is found. */ -static int -find_operation(struct nandsim *ns, uint32_t flag) +static int find_operation(struct nandsim *ns, uint32_t flag) { int opsfound = 0; int i, j, idx = 0; @@ -791,14 +795,93 @@ find_operation(struct nandsim *ns, uint3 } /* + * Returns a pointer to the current page. + */ +static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns) +{ + return &(ns->pages[ns->regs.row]); +} + +/* + * Retuns a pointer to the current byte, within the current page. + */ +static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) +{ + return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; +} + +/* + * Fill the NAND buffer with data read from the specified page. + */ +static void read_page(struct nandsim *ns, int num) +{ + union ns_mem *mypage; + + mypage = NS_GET_PAGE(ns); + if (mypage->byte == NULL) { + NS_DBG("read_page: page %d not allocated\n", ns->regs.row); + memset(ns->buf.byte, 0xFF, num); + } else { + NS_DBG("read_page: page %d allocated, reading from %d\n", + ns->regs.row, ns->regs.column + ns->regs.off); + memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); + } +} + +/* + * Erase all pages in the specified sector. + */ +static void erase_sector(struct nandsim *ns) +{ + union ns_mem *mypage; + int i; + + mypage = NS_GET_PAGE(ns); + for (i = 0; i < ns->geom.pgsec; i++) { + if (mypage->byte != NULL) { + NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); + kfree(mypage->byte); + mypage->byte = NULL; + } + mypage++; + } +} + +/* + * Program the specified page with the contents from the NAND buffer. + */ +static int prog_page(struct nandsim *ns, int num) +{ + int i; + union ns_mem *mypage; + u_char *pg_off; + + mypage = NS_GET_PAGE(ns); + if (mypage->byte == NULL) { + NS_DBG("prog_page: allocating page %d\n", ns->regs.row); + mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (mypage->byte == NULL) { + NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); + return -1; + } + memset(mypage->byte, 0xFF, ns->geom.pgszoob); + } + + pg_off = NS_PAGE_BYTE_OFF(ns); + for (i = 0; i < num; i++) + pg_off[i] &= ns->buf.byte[i]; + + return 0; +} + +/* * If state has any action bit, perform this action. * * RETURNS: 0 if success, -1 if error. */ -static int -do_state_action(struct nandsim *ns, uint32_t action) +static int do_state_action(struct nandsim *ns, uint32_t action) { - int i, num; + int num; int busdiv = ns->busw == 8 ? 1 : 2; action &= ACTION_MASK; @@ -822,7 +905,7 @@ do_state_action(struct nandsim *ns, uint break; } num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; - memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num); + read_page(ns, num); NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", num, NS_RAW_OFFSET(ns) + ns->regs.off); @@ -863,7 +946,7 @@ do_state_action(struct nandsim *ns, uint ns->regs.row, NS_RAW_OFFSET(ns)); NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); - memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob); + erase_sector(ns); NS_MDELAY(erase_delay); @@ -886,8 +969,8 @@ do_state_action(struct nandsim *ns, uint return -1; } - for (i = 0; i < num; i++) - ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i]; + if (prog_page(ns, num) == -1) + return -1; NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); @@ -928,8 +1011,7 @@ do_state_action(struct nandsim *ns, uint /* * Switch simulator's state. */ -static void -switch_state(struct nandsim *ns) +static void switch_state(struct nandsim *ns) { if (ns->op) { /* @@ -1070,8 +1152,7 @@ switch_state(struct nandsim *ns) } } -static u_char -ns_nand_read_byte(struct mtd_info *mtd) +static u_char ns_nand_read_byte(struct mtd_info *mtd) { struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; u_char outb = 0x00; @@ -1144,8 +1225,7 @@ ns_nand_read_byte(struct mtd_info *mtd) return outb; } -static void -ns_nand_write_byte(struct mtd_info *mtd, u_char byte) +static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) { struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; @@ -1308,15 +1388,13 @@ static void ns_hwcontrol(struct mtd_info ns_nand_write_byte(mtd, cmd); } -static int -ns_device_ready(struct mtd_info *mtd) +static int ns_device_ready(struct mtd_info *mtd) { NS_DBG("device_ready\n"); return 1; } -static uint16_t -ns_nand_read_word(struct mtd_info *mtd) +static uint16_t ns_nand_read_word(struct mtd_info *mtd) { struct nand_chip *chip = (struct nand_chip *)mtd->priv; @@ -1325,8 +1403,7 @@ ns_nand_read_word(struct mtd_info *mtd) return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); } -static void -ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; @@ -1353,8 +1430,7 @@ ns_nand_write_buf(struct mtd_info *mtd, } } -static void -ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; @@ -1407,8 +1483,7 @@ ns_nand_read_buf(struct mtd_info *mtd, u return; } -static int -ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +static int ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len); diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 0ddfd70..4178b4b 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -294,23 +294,21 @@ static inline int jffs2_encode_dev(union static inline struct jffs2_node_frag *frag_first(struct rb_root *root) { - struct rb_node *node = root->rb_node; + struct rb_node *node = rb_first(root); if (!node) return NULL; - while(node->rb_left) - node = node->rb_left; + return rb_entry(node, struct jffs2_node_frag, rb); } static inline struct jffs2_node_frag *frag_last(struct rb_root *root) { - struct rb_node *node = root->rb_node; + struct rb_node *node = rb_last(root); if (!node) return NULL; - while(node->rb_right) - node = node->rb_right; + return rb_entry(node, struct jffs2_node_frag, rb); } diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index fc211b6..b90d5aa 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -51,7 +51,7 @@ static void *jffs2_follow_link(struct de */ if (!p) { - printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n"); + printk(KERN_ERR "jffs2_follow_link(): can't find symlink target\n"); p = ERR_PTR(-EIO); } D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target)); diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 4da09ce..4bb3f18 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -399,8 +399,6 @@ static void unrefer_xattr_datum(struct j { /* must be called under down_write(xattr_sem) */ if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) { - uint32_t xid = xd->xid, version = xd->version; - unload_xattr_datum(c, xd); xd->flags |= JFFS2_XFLAGS_DEAD; if (xd->node == (void *)xd) { @@ -411,7 +409,8 @@ static void unrefer_xattr_datum(struct j } spin_unlock(&c->erase_completion_lock); - dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xid, version); + dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", + xd->xid, xd->version); } } diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 70420bb..6fc3e07 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -286,9 +286,7 @@ struct nand_ecc_ctrl { * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer for calculated ecc * @ecccode: buffer for ecc read from flash - * @oobwbuf: buffer for write oob data * @databuf: buffer for data - dynamically sized - * @oobrbuf: buffer to read oob data * * Do not change the order of buffers. databuf and oobrbuf must be in * consecutive order. @@ -296,9 +294,7 @@ struct nand_ecc_ctrl { struct nand_buffers { uint8_t ecccalc[NAND_MAX_OOBSIZE]; uint8_t ecccode[NAND_MAX_OOBSIZE]; - uint8_t oobwbuf[NAND_MAX_OOBSIZE]; - uint8_t databuf[NAND_MAX_PAGESIZE]; - uint8_t oobrbuf[NAND_MAX_OOBSIZE]; + uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE]; }; /**