GIT 12f77c9eed0d2a9f598500d9c1e3dd48883f1d0c git://git.infradead.org/mtd-2.6.git commit Author: Kyungmin Park Date: Thu Aug 30 09:36:05 2007 +0900 [MTD] [OneNAND] Use mtd_oob_ops at oob functions To enable the main read/write at oob ops Next time we will commit the main read/write support for yaffs2 Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse commit e208520ed664db0f7584048ae09e5d2afda43714 Author: Jörn Engel Date: Wed Aug 29 17:57:11 2007 +0200 [MTD] [NAND] Driver for Olympus MAUSB-10 and Fujifilm DPC-R1 card readers Unlike most stuff on the market the chip inside these two allows raw flash access and doesn't implement and FTL, leaving that functionality to the device driver. Raw flash access in a cheap USB cardreader! An MTD test device one can attach to a PC! What a deal! The command set of the chip is not documented, so information was obtained from the existing mass-storage driver (drivers/usb/storage/alauda.c), its documentation (http://alauda.sourceforge.net/wikka.php?wakka=BulkCommandReference), additional reverse engineering and comparison with a vendor driver for a related chip (http://www.ratocsystems.com/english/download/driver/linux/sma03u.html). Signed-off-by: Joern Engel Signed-off-by: David Woodhouse commit 34a27ffdf59f80fe165f5c56a0be57d55b6da63d Author: Kumar Gala Date: Wed Aug 22 22:53:39 2007 -0500 [MTD] Remove dead maps The tqm834x map Kconfig options depends on TQM834x which does not exist anywhere else in the kernel. The pq2fads map Kconfig/makefile support was removed a while ago but the actual file persisted. Signed-off-by: Kumar Gala commit 3feb0ff1607ab3b7dcd36b2347f00c41774464c8 Author: Josh Boyer Date: Wed Aug 22 14:30:47 2007 -0500 [MTD] 4xx: Don't build arch/ppc dependent drivers in arch/powerpc These drivers are specific to 4xx support in arch/ppc at the moment. Make sure they don't get built on arch/powerpc. Signed-off-by: Josh Boyer Signed-off-by: Kumar Gala commit 68d09b1b6780415d82160f6b6d88e82bd724e691 Author: Andrew Morton Date: Fri Aug 10 14:01:31 2007 -0700 [MTD] mtdoops printk warning fixes drivers/mtd/mtdoops.c: In function 'mtdoops_inc_counter': drivers/mtd/mtdoops.c:109: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' drivers/mtd/mtdoops.c: In function 'mtdoops_console_sync': drivers/mtd/mtdoops.c:277: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' someone buy Dave an x86_64 box. Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit f4e35647f5c0ac24ca1ba14f84bf817ec0483b72 Author: Andrew Morton Date: Fri Aug 10 14:01:30 2007 -0700 [JFFS2] fix printk warning in jffs2_block_check_erase() fs/jffs2/erase.c: In function 'jffs2_block_check_erase': fs/jffs2/erase.c:355: warning: format '%08x' expects type 'unsigned int', but argument 3 has type 'long unsigned int' and fs/jffs2/erase.c: In function 'jffs2_erase_pending_blocks': fs/jffs2/erase.c:404: warning: 'bad_offset' may be used uninitialized in this function Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 9ed437c50d89eabae763dd422579f73fdebf288d Author: David Woodhouse Date: Wed Aug 22 12:39:19 2007 +0100 [JFFS2] Fix ACL vs. mode handling. When POSIX ACL support was enabled, we weren't writing correct legacy modes to the medium on inode creation, or when the ACL was set. This meant that the permissions would be incorrect after the file system was remounted. Signed-off-by: David Woodhouse commit 09b3fba562ce366312b90a6f71d0b727b4d93ba9 Author: David Woodhouse Date: Thu Aug 9 17:28:20 2007 +0800 [JFFS2] Correct cleanmarker checks -- we should use only 8 bytes Commit a7a6ace1406f95c3edb8365788f85984377f3832 revamped the OOB handling but accidentally switched to 12-byte cleanmarkers, which is incompatible with what 'flash_eraseall -j' will do. So using flash_eraseall -j and then trying to mount the 'empty' flash will fail, because the cleanmarkers aren't recognised. Signed-off-by: David Woodhouse commit 471f717a48d25afcb9428c9523cd0557738b7115 Author: David Woodhouse Date: Fri Aug 3 23:02:02 2007 +0100 Revert "[MTD] Driver for AT26Fxxx dataflash devices" This reverts commit 340ea370c2ce89d1c15fbf785460f2f74314ce58. It's not needed given the other m25p80 patch (which now handles at26 "dataflash" as well as most other standard SPI flash chips), and requires a controller driver that won't be merged upstream (supplanted by drivers/spi/atmel_spi.c) ... the submitter of that at91_dataflash26.c driver concurred. Requested by David Brownell Signed-off-by: David Woodhouse commit 3ca135e16a393e5c5cf6490ce751e43c59011dde Author: David Woodhouse Date: Thu Aug 2 16:32:02 2007 +0100 [JFFS2] LZO compression should default off for compatibility. Signed-off-by: David Woodhouse commit 8b2b403ce0f1a816b7a6a4f47c8798003b26c07a Author: maximilian attems Date: Sat Jul 28 13:07:16 2007 +0200 [MTD] [NAND] nand_base.c fix broken link replace with working link from nand Kconfig help text fixes bugzilla 7815 Signed-off-by: maximilian attems Signed-off-by: David Woodhouse commit 73ff007537e591e53d070aafbfd90369c3580332 Author: Adrian Bunk Date: Sun Jul 29 16:58:12 2007 +0200 [MTD] [ONENAND] onenand_sim.c: make struct info static This patch makes the needlessly global struct info static. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse commit d9b0744d6c75f7b268b728cdd1843f31bdeba311 Author: Mariusz Kozlowski Date: Wed Aug 1 00:02:10 2007 +0200 [UBI] drivers/mtd/ubi/scan.c: kmalloc + memset conversion to kzalloc To be able to convert kmalloc + memset(..., 1, ...) to kzalloc this patch reverses the logic around 'buf'. Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse commit 4fb4caa639a27a3cb0b3bfbf2041524af43efff9 Author: Mariusz Kozlowski Date: Tue Jul 31 23:49:06 2007 +0200 [MTD] pmcmsp-flash.c: kmalloc + memset conversion to k[cz]alloc Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse commit 40562f812bb4b0211da07ad1927af030b089d3d3 Author: Mariusz Kozlowski Date: Tue Jul 31 23:22:18 2007 +0200 Documentation/DocBook/mtdnand.tmpl: kmalloc + memset conversion to kzalloc Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse commit d67d1d7fc344e44b51296327a0b09a9419ca307f Author: Mariusz Kozlowski Date: Tue Jul 31 20:03:14 2007 +0200 [MTD] drivers/mtd/inftlmount.c: kmalloc + memset conversion to kcalloc Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse commit 8f0f23f97adbd833000e25e41c1310e49ace9ba8 Author: Mariusz Kozlowski Date: Fri Jul 27 17:29:32 2007 +0200 [MTD] remove redundant/dead code from physmap_of.c This patch removes redundant memset() and dead return line from of_physmap_probe(). No functional change. Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse commit c0b8ba7bfe7b4c1f11b3356b65520405b005bd33 Author: Artem Bityutskiy Date: Mon Jul 23 16:06:50 2007 +0300 [MTD] [NAND] fix race in nand_base.c When we mark block bad we have to get chip because this involves writing to the page's OOB. We hit this bug in UBI - we observed random obscure crashes when it marks block bad from the background thread and there is some parallel task which utilizes flash. This patch also adds a TODO note about BBT table protection which it seems does not exist. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit 0bf9733d0d65ebb413d62204ad8e328e0a0b9407 Author: David Woodhouse Date: Mon Jul 23 13:07:06 2007 +0100 [MTD] Fix do_div() type warning in mtdconcat It expects a uint64_t; give it one. Signed-off-by: David Woodhouse commit e733450b675dfc07940c21b4832207c79059246f Author: Robert P. J. Day Date: Wed Jul 11 17:04:02 2007 -0400 [MTD] Remove useless references to MIPS_HYDROGEN3 and MIPS_MIRAGE_WHY. Signed-off-by: Robert P. J. Day Acked-by: Ralf Baechle Signed-off-by: David Woodhouse commit 4a32cfa0f5c3f9e8a0da610002e4555173226650 Author: Roland Stigge Date: Wed Jul 18 14:56:11 2007 +0200 [MTD] [NAND] Fix refactoring of EDB7312 hwcontrol function. The patch ensures that the current code (kernel 2.6.22) uses the bits like the code prior to the refactoring. The variable "bits" is employed in a useful way now. Signed-off-by: Roland Stigge Signed-off-by: David Woodhouse commit 030f9e13bec9aaae1def86c23963a1a825ccdab5 Author: akpm@linux-foundation.org Date: Fri Jul 20 11:56:19 2007 -0700 [MTD] Remove embedded return in RFD FTL. embedded returns are evil. Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 1050643431c74d29ac972141cb24df1d9aca65cd Author: Florin Malita Date: Thu Jul 19 16:45:18 2007 -0400 [MTD] Fix potential leak in rfd_ftl_add_mtd This fixes a leak in the !mtd->erasesize error path (Coverity 1765). Signed-off-by: Florin Malita Signed-off-by: David Woodhouse commit 4cfff0db3a60a775c29abd9cbc300fc0f616904b Author: Jesper Juhl Date: Sat Jul 21 17:02:36 2007 +0200 [MTD] Clean up duplicate includes in drivers/mtd/ This patch cleans up duplicate includes in drivers/mtd/ Signed-off-by: Jesper Juhl Signed-off-by: David Woodhouse commit 30eb0db07d67b9211da7f506220184df827e425d Author: Steven J. Hill Date: Wed Jul 18 23:29:46 2007 -0500 [MTD] [NAND] Add NAND manufacturer AMD. This patch adds the manufacturer ID for AMD flash. Signed-off-by: Steven J. Hill Signed-off-by: David Woodhouse commit 0fc72b81d3111d114ab378935b1cf07680ca1289 Author: David Woodhouse Date: Wed Jul 11 15:33:14 2007 +0100 [JFFS2] Add declaration of jffs2_lzo_{init,exit} to compr.h fs/jffs2/compr.c: In function ‘jffs2_compressors_init’: fs/jffs2/compr.c:320: warning: implicit declaration of function ‘jffs2_lzo_init’ fs/jffs2/compr.c: In function ‘jffs2_compressors_exit’: fs/jffs2/compr.c:346: warning: implicit declaration of function ‘jffs2_lzo_exit’ Signed-off-by: David Woodhouse commit 3b23c1f5fa7e4c99e9451958a1004ef7434a71f0 Author: Richard Purdie Date: Tue Jul 10 10:28:42 2007 +0100 [JFFS2] Add a "favourlzo" compression mode Add a "favourlzo" compression mode to jffs2 which tries to optimise by size but gives lzo an advantage when comparing sizes. This means the faster lzo algorithm can be preferred when there isn't much difference in compressed size (the exact threshold can be changed). Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse commit c799aca31bfe61ba3a91133acf5a13a0773087d4 Author: Richard Purdie Date: Tue Jul 10 10:28:36 2007 +0100 [JFFS2] Add LZO compression support. Add LZO1X compression/decompression support to jffs2. LZO's interface doesn't entirely match that required by jffs2 so a buffer and memcpy is unavoidable. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse commit 8691a729a2a3d739ee40a577053157393450aabd Author: Richard Purdie Date: Tue Jul 10 20:33:54 2007 +0100 [MTD] Add sync/unblank function to mtdoops mtdoops wasn't ensuring data was flushed to flash in crash situations after recent changes in mainline kernels as tracking the oops_in_progress variable was no longer enough. We can use the "unblank" console call as a sync call to tell us to write out the buffer though. Therefore add a sync function to mtdoops and call this when console unblank events occur. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse commit a2e96b627b58efe2d618a415f34b9b9d9f829bd6 Author: Yoichi Yuasa Date: Wed Jul 11 11:36:56 2007 +0900 [MTD] Remove Ocelot G support from DiskOnChip drivers. This patch has removed Ocelot G support from MTD. Ocelot G support has already removed since May 2007. Signed-off-by: Yoichi Yuasa Signed-off-by: David Woodhouse commit a6bc432e296dfa1f05d4b586ca5ca3085a2d42d7 Author: David Woodhouse Date: Wed Jul 11 14:23:54 2007 +0100 [JFFS2] Add support for write-buffer verification. We've seen some evil corruption issues, where the corruption seems to be introduced after the JFFS2 crc32 is calculated but before the NAND controller calculates the ECC. So it's in RAM or in the PCI DMA transfer; not on the flash. Attempt to catch it earlier by (optionally) reading back from the flash immediately after writing it. Signed-off-by: David Woodhouse commit ef53cb02ffee8ceb44ea75f778f77eace6b9c89a Author: David Woodhouse Date: Tue Jul 10 10:01:22 2007 +0100 [JFFS2] Whitespace cleanups. Convert many spaces to tabs; one or two other minor cosmetic fixes. Signed-off-by: David Woodhouse commit 16adce7b6f4dab015d0b93274b41f8aae6fe07a5 Author: David Woodhouse Date: Tue Jul 10 09:08:26 2007 +0100 [MTD] [ONENAND] Make onenand_sim compile on non-ARM platforms. The whole point of a sim is that it should run almost anywhere. Gratuitously depending on '#define SZ_128K 131072' from an ARM-specific header isn't really a good idea. Signed-off-by: David Woodhouse commit c9856e39e0c3b4d260e4614b66b0a7e0e79ec0ac Author: Philippe De Muyter Date: Thu Jul 5 17:05:47 2007 +0200 [MTD] [NOR] add FUJITSU MBM29F800BA and ST M29F800AB descriptions Add descriptions for Fujitsu MBM29F800BA and ST M29F800AB flash chips. Those chips are compatible (except for the ids) with the AMD AM29F800BB. Signed-off-by: Philippe De Muyter Signed-off-by: David Woodhouse commit b2e25235fe8b4b6cd8cd6a792cbe883d7417eaac Author: David Woodhouse Date: Thu Jul 5 01:57:26 2007 -0400 [JFFS2] Print correct node offset when complaining about broken data CRC Debugging the hardware problems in OLPC trac #1905 would be a whole lot easier if the correct node offsets were printed for the offending nodes. Signed-off-by: David Woodhouse commit 8f1a866fc6831f13593fae6194e3150d45976628 Author: Jesper Juhl Date: Thu Jul 5 02:18:34 2007 +0200 [MTD] [CHIPS] fix tiny spelling error in comment in cfi_cmdset_0001.c Trivial fix of a spelling error in a comment in cfi_cmdset_0001.c s/ships/chips/ Signed-off-by: Jesper Juhl Signed-off-by: David Woodhouse commit 14c6381ee46b32b9b38073cd023b618ab4f60629 Author: David Woodhouse Date: Tue Jul 3 16:51:19 2007 -0400 [JFFS2] Fix readinode failure when read_dnode() detects CRC failure. We should have stopped returning 1 from read_dnode() to indicate failure. We can just mark the damn thing obsolete immediately. But I missed a case where we don't. Signed-off-by: David Woodhouse commit 8dab169b8bdea3bcbc08b15fdbd9a21526fdbb77 Author: Kyungmin Park Date: Sat Jun 30 14:14:43 2007 +0900 [MTD] OneNAND Simulator support This simulate various OneNAND flash chips for the MTD onenand layer. It's simple implementation, only basic operations. It don't support the recent changes in NANDSIM such as lazy block allocation, bitflip, and so on. Note: This passed nand-tests. Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse commit ee9745fcf214272b7cdd9d320d044cf433ee958e Author: Kyungmin Park Date: Sat Jun 30 13:57:49 2007 +0900 [MTD] [OneNAND] 2X program support The 2X Program is an extension of Program Operation. Since the device is equipped with two DataRAMs, and two-plane NAND Flash memory array, these two component enables simultaneous program of 4KiB. Plane1 has only even blocks such as block0, block2, block4 while Plane2 has only odd blocks such as block1, block3, block5. So MTD regards it as 4KiB page size and 256KiB block size Now the following chips support it. (KFXXX16Q2M) Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M, Mux: KFM2G16Q2M, KFN4G16Q2M, And more recent chips Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse commit 1bddb9a8ba30dffa963c76283bc482b67fb90d8a Author: David Woodhouse Date: Fri Jun 29 14:46:49 2007 +0100 Revert accidental commit to scripts/Makefile.headersinst Commit 43dfa07fbb6b8bd5b6173a5bab48470f578c8e5b accidentally included a fix to scripts/Makefile.headersinst which it should not have done. Revert. Signed-off-by: David Woodhouse commit a97145c22408865579be397460ca998301ab63a3 Author: Adrian Bunk Date: Sun Jun 3 22:54:00 2007 +0200 [MTD] remove dead MTD_PMC551_APERTURE_SIZE option The CONFIG_MTD_PMC551_APERTURE_SIZE option seems to never have existed, so there's no reason for carrying an #ifdef for it. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse commit 8e0aedc5bcb9da4f613af097295a02a57aa0d41c Author: David Woodhouse Date: Fri Jun 29 14:18:22 2007 +0100 [MTD] Use proper binary multiple prefixes in pmc551 driver Signed-off-by: David Woodhouse commit 8f46c527acdb84f98d48809ba29173f515de1080 Author: Adrian Bunk Date: Fri Jun 22 01:52:08 2007 +0200 [MTD] [NAND] cafe_nand.c: the OLPC laptop is not available for $100 The price might drop to $100 in a few years. But currently, a more reasonable name might be "$175 laptop". Let's simply call it "OLPC laptop" without any price tag. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse commit 43dfa07fbb6b8bd5b6173a5bab48470f578c8e5b Author: David Woodhouse Date: Fri Jun 29 13:39:57 2007 +0100 [JFFS2] Deletion dirents should be REF_NORMAL, not REF_PRISTINE. Otherwise they'll never actually get garbage-collected. Noted by Jonathan Larmour. Signed-off-by: David Woodhouse commit 663d77a7ccfd407cf7491dbd53c7c17eef58c96a Author: Adrian Bunk Date: Thu Jun 28 23:02:59 2007 +0100 [MTD] [MAPS] Cleanup nettel map driver - make 2 needlessly global functions static - remove the unused nettel_eraseconfig() Signed-off-by: Adrian Bunk Cc: Greg Ungerer Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 1da1caf8d80e953d0ff0c2131b04ccc609c3baa8 Author: Randy Dunlap Date: Thu Jun 28 23:00:09 2007 +0100 [MTD] [NOR] Use NULL instead of 0 for pointer to shut sparse up. Use NULL instead of 0 for pointer: drivers/mtd/chips/cfi_cmdset_0001.c:2258:43: warning: Using plain integer as NULL pointer Other changes by inspection. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit ec9ce52ef8f8496c83bc36a9b19f22e83575f890 Author: Matthias Kaehlcke Date: Thu Jun 28 22:58:42 2007 +0100 [MTD] Use mutex instead of semaphore in dataflash driver The MTD DataFlash driver uses a semaphore as mutex. Use the mutex API instead of the (binary) semaphore. Signed-off-by: Matthias Kaehlcke Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 02d087dbc8cfade63205ee78612c5fd3762afc03 Author: David Woodhouse Date: Thu Jun 28 22:38:38 2007 +0100 [MTD] m25p80: Use correct units for binary multiples Signed-off-by: David Woodhouse commit fa0a8c71f352d89c54f2d3a92f7a8a97cdb7d9a4 Author: David Brownell Date: Sun Jun 24 15:12:35 2007 -0700 [MTD] m25p80 handles more chips, uses JEDEC ids and small eraseblocks Update chip ID tables in m25p80 to handle more SPI flash chips, matching datasheets. All of these can use the same core operations and are newer chips that support the JEDEC "read id" instruction: - Atmel AT25 and AT26 (seven chips) - Spansion S25SL (five chips) - SST 25VF (four chips) - ST M25, M45 (five more chips) - Winbond W25X series (seven chips) That JEDEC instruction is now used, either to support a sanity check on the platform data holding board configuration data, or to determine chip type when it's not included in platform data. In fact, boards that don't need a standard partition table may not need that platform data any more. For chips that support 4KiB erase units, use that smaller block size instead of the larger size (usually 64KiB); it's less wasteful. (Tested on W25X80.) Signed-off-by: David Brownell Signed-off-by: David Woodhouse commit 7d5230ea3987ea3eaa03601fe429cb69f87de3e3 Author: David Brownell Date: Sun Jun 24 15:09:13 2007 -0700 [MTD] m25p80 converted to mutex Convert semaphore usage in m25p80 driver to mutex; mention another kind of SPI flash chip that should be able to use this driver (given minor tweaks). Signed-off-by: David Brownell Signed-off-by: David Woodhouse commit 02d929187414f2095b30ebf4acee1ce6617add45 Author: Stefan Roese Date: Tue May 22 11:23:45 2007 +0200 [MTD] [NAND] Change NDFC driver to support 405 platforms too This patch adds 405 platform support to the 440 NDFC driver. The new AMCC 405EZ PPC is equipped with the same NDFC core as the 440EP(x) and other will follow soon. Signed-off-by: Stefan Roese Signed-off-by: David Woodhouse commit 356d70f19d949a11320ab626037b2d197a8e8b51 Author: Ben Dooks Date: Mon May 28 20:28:34 2007 +0100 [MTD] mtdcore.c: share syms with mtd_blkdev.c Fix the sparse warnings generated by the implicit dependency of mtd_blkdevs.c and mtd_core.c for the two symbols mtd_table and mtd_table_mutex. This is done by adding an local header file mtdcore.h to define these (including the warning about the non-proliferation of these symbols). Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse commit 029a9eb1f667ca71fe7eb3bf8962f01ac4d252a5 Author: Ben Dooks Date: Mon May 28 20:11:37 2007 +0100 [MTD] [NOR] cfi_cmdset_0001.c: sparse fixes Fix sparse warnings generated from cfi_cmdset_0001.c. drivers/mtd/chips/cfi_cmdset_0001.c:1783:5: warning: symbol 'cfi_intelext_erase_varsize' was not declared. Should it be static? drivers/mtd/chips/cfi_cmdset_0001.c:2258:43: warning: Using plain integer as NULL pointer Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse commit ce0f33adec9737bdfe59a10d03fed0c674136ffe Author: Ben Dooks Date: Mon May 28 19:59:00 2007 +0100 [MTD] [NOR] cfi_cmdset_0002.c: fix 'cfi_amdstd_erase_varsize' to be static Make cfi_amdstd_erase_varsize static, as declared at the top of the file to ensure sparse does not print a warning for an undeclared function, as so: drivers/mtd/chips/cfi_cmdset_0002.c:1612:5: warning: symbol 'cfi_amdstd_erase_varsize' was not declared. Should it be static? Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse commit 8f026f75accb8c9f1179a4b57a5019af36471046 Author: Ben Dooks Date: Mon May 28 19:51:59 2007 +0100 [MTD] mtd_blkdevs.c: do not export 'mtd_blktrans_ops' The mtd_blktrans_ops is not defined in any of the headers and is indeed not used elsewhere in the kernel, so mark it as static to reduce the warnings from sparse drivers/mtd/mtd_blkdevs.c:204:32: warning: symbol 'mtd_blktrans_ops' was not declared. Should it be static? Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse commit 8b099a390defd294a3832fe73626c25072967316 Author: Ben Dooks Date: Mon May 28 19:17:54 2007 +0100 [MTD] [NAND] nand_base.c: fix type of eccpos pointer The nand_base.c driver implicitly casts the uint32_t eccpos array to 'int *', which is not only not guaranteed to be the same sign as the source, but is not guaranteed to be the same size. Fix by changing nand_base.c to use uint32_t referencing the eccpos fields. Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse commit 4b23aff083649eafa141ed69ad0ff59ba639edf8 Author: Richard Purdie Date: Tue May 29 13:31:42 2007 +0100 [MTD] oops and panic message logging to MTD device Kernel oops and panic messages are invaluable when debugging crashes. These messages often don't make it to flash based logging methods (say a syslog on jffs2) due to the overheads involved in writing to flash. This patch allows you to turn an MTD partition into a circular log buffer where kernel oops and panic messages are written to. The messages are obtained by registering a console driver and checking oops_in_progress. Erases are performed in advance to maximise the chances of a saving messages. To activate it, add console=ttyMTDx to the kernel commandline (where x is the mtd device number to use). Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse commit 1100b47ea557e1cd6d9b71fe7ba1a189ed8bf276 Author: Ivan Kuten Date: Thu May 24 14:35:58 2007 +0300 [MTD] [NAND] at91_nand rdy_pin fix The patch below fixes nand driver for AT91 boards which do not have NAND R/B signal connected to gpio (rdy_pin is not connected). Signed-off-by: Ivan Kuten Acked-by: Andrew Victor Signed-off-by: David Woodhouse commit 4854b75c7795604ea6a0b6b7f549f145813343d0 Author: David Woodhouse Date: Thu Jun 28 20:13:27 2007 +0100 [MTD] Remove references to ROOT_DEV from map drivers. Signed-off-by: David Woodhouse commit e716dd3644c2a79d6ffa4359bac06f57479dcb34 Author: David Woodhouse Date: Thu Jun 28 19:49:36 2007 +0100 [JFFS2] Fix suspend failure with JFFS2 GC thread. The try_to_freeze() call was in the wrong place; we need it in the signal-pending loop now that a pending freeze also makes signal_pending() return true. Signed-off-by: David Woodhouse commit 66bfaeaa90432a585d7e9e70605ee4df3ede9890 Author: David Woodhouse Date: Thu Jun 28 19:03:11 2007 +0100 [JFFS2] Improve diagnostic output for 'node added in wrong place' check Jocke has seen this fail. We want to know why. Signed-off-by: David Woodhouse commit d364fb18cd991734eb54aa8840e70030b0c9f699 Author: Joakim Tjernlund Date: Wed Jun 27 01:24:09 2007 +0200 [JFFS2] Reduce time for which erase_free_sem is held during erase. With current desing erase_free_sem is locked every time the flash block is being erased. For NOR flashes - ~1 second is needed to erase single flash block. In the worst case scenario erase_free_sem may be locked for a couple of seconds when the number of blocks is being erased (e.g. after large file was removed). When erase_free_sem is locked all read/write operations for given JFFS2 partition are locked too - in effect from time to time access to the JFFS2 partition is locked for a number of seconds. This fix makes critical section in flash erasing procedure shorter - now erase_free_sem is locked around erase_completion_lock spinlock only. Originally from Radoslaw Bisewski Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse commit f79c44980aae3f50fe73e50789641df265953cc6 Author: Joakim Tjernlund Date: Sun Jun 24 19:22:29 2007 +0200 [JFFS2] Prevent oops after 'node added in wrong place' debug check jffs2_add_physical_node_ref() should never really return error -- it's an internal debugging check which triggered. We really need to work out why and stop it happening. But in the meantime, let's make the failure mode a little less nasty. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse commit fab2c399129273713b7dcc6a54cef17ca124a47f Author: Joakim Tjernlund Date: Fri Jun 1 15:14:09 2007 +0200 [JFFS2] Use point(), if available, to check newly erased blocks. Faster and won't trash the D-cache. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse commit 7eafaed55f4b8599cfe55449a6ed88d3693954de Author: Joakim Tjernlund Date: Wed Jun 27 00:56:40 2007 +0200 [MTD] Only call mtd->sync() method in mtdchar close if opened for write. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse commit fd5324909e410a3202c1b01bd507c2dfba58fca5 Author: Joakim Tjernlund Date: Tue Jun 26 23:32:10 2007 +0200 [JFFS2] Fix hanging close for /dev/mtd character device. When pdflush is erasing lots of sectors, drivers calling mtd->sync will hang until all blocks are erased. Be nicer. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse Documentation/DocBook/mtdnand.tmpl | 5 drivers/mtd/Kconfig | 8 drivers/mtd/Makefile | 1 drivers/mtd/chips/cfi_cmdset_0001.c | 14 - drivers/mtd/chips/cfi_cmdset_0002.c | 2 drivers/mtd/chips/jedec_probe.c | 37 ++ drivers/mtd/devices/Kconfig | 25 + drivers/mtd/devices/Makefile | 1 drivers/mtd/devices/at91_dataflash26.c | 485 --------------------- drivers/mtd/devices/docprobe.c | 4 drivers/mtd/devices/m25p80.c | 271 +++++++++--- drivers/mtd/devices/mtd_dataflash.c | 17 - drivers/mtd/devices/pmc551.c | 27 - drivers/mtd/inftlmount.c | 3 drivers/mtd/maps/Kconfig | 12 - drivers/mtd/maps/Makefile | 1 drivers/mtd/maps/alchemy-flash.c | 14 - drivers/mtd/maps/nettel.c | 65 --- drivers/mtd/maps/physmap_of.c | 5 drivers/mtd/maps/pmcmsp-flash.c | 22 + drivers/mtd/maps/pmcmsp-ramroot.c | 1 drivers/mtd/maps/pq2fads.c | 88 ---- drivers/mtd/maps/tqm834x.c | 286 ------------ drivers/mtd/mtd_blkdevs.c | 7 drivers/mtd/mtdchar.c | 3 drivers/mtd/mtdconcat.c | 2 drivers/mtd/mtdcore.c | 2 drivers/mtd/mtdcore.h | 11 drivers/mtd/mtdoops.c | 376 ++++++++++++++++ drivers/mtd/nand/Kconfig | 12 - drivers/mtd/nand/Makefile | 1 drivers/mtd/nand/alauda.c | 742 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/diskonchip.c | 2 drivers/mtd/nand/excite_nandflash.c | 1 drivers/mtd/nand/nand_base.c | 2 drivers/mtd/nand/nand_ids.c | 1 drivers/mtd/nand/ndfc.c | 8 drivers/mtd/onenand/Kconfig | 23 + drivers/mtd/onenand/Makefile | 3 drivers/mtd/onenand/onenand_base.c | 190 ++++++-- drivers/mtd/onenand/onenand_sim.c | 495 +++++++++++++++++++++ drivers/mtd/rfd_ftl.c | 8 drivers/mtd/ubi/scan.c | 17 - fs/Kconfig | 81 ++- fs/jffs2/Makefile | 1 fs/jffs2/acl.c | 23 - fs/jffs2/acl.h | 4 fs/jffs2/background.c | 4 fs/jffs2/compr.c | 422 ++++++++++-------- fs/jffs2/compr.h | 54 +- fs/jffs2/compr_lzo.c | 108 +++++ fs/jffs2/compr_rtime.c | 2 fs/jffs2/compr_rubin.c | 4 fs/jffs2/compr_zlib.c | 6 fs/jffs2/dir.c | 37 +- fs/jffs2/erase.c | 57 ++ fs/jffs2/fs.c | 32 + fs/jffs2/gc.c | 4 fs/jffs2/jffs2_fs_sb.h | 3 fs/jffs2/nodelist.h | 2 fs/jffs2/nodemgmt.c | 9 fs/jffs2/os-linux.h | 5 fs/jffs2/readinode.c | 6 fs/jffs2/scan.c | 2 fs/jffs2/security.c | 6 fs/jffs2/summary.c | 8 fs/jffs2/summary.h | 6 fs/jffs2/wbuf.c | 81 +++ fs/jffs2/xattr.h | 2 fs/jffs2/xattr_user.c | 4 include/linux/jffs2.h | 1 include/linux/mtd/nand.h | 1 include/linux/mtd/onenand.h | 12 + include/linux/mtd/onenand_regs.h | 4 74 files changed, 2811 insertions(+), 1480 deletions(-) diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl index a8c8cce..6fbc41d 100644 --- a/Documentation/DocBook/mtdnand.tmpl +++ b/Documentation/DocBook/mtdnand.tmpl @@ -275,16 +275,13 @@ int __init board_init (void) int err = 0; /* Allocate memory for MTD device structure and private data */ - board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); + board_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!board_mtd) { printk ("Unable to allocate NAND MTD device structure.\n"); err = -ENOMEM; goto out; } - /* Initialize structures */ - memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip)); - /* map physical adress */ baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024); if(!baseaddr){ diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index fbec8cd..8848e8a 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -278,6 +278,14 @@ config SSFDC This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. +config MTD_OOPS + tristate "Log panic/oops to an MTD buffer" + depends on MTD + help + This enables panic and oops messages to be logged to a circular + buffer in a flash partition where it can be read back at some + later point. + source "drivers/mtd/chips/Kconfig" source "drivers/mtd/maps/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 6d958a4..7f0b04b 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_NFTL) += nftl.o obj-$(CONFIG_INFTL) += inftl.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o obj-$(CONFIG_SSFDC) += ssfdc.o +obj-$(CONFIG_MTD_OOPS) += mtdoops.o nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 2f19fa7..39eff9f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -526,7 +526,7 @@ static int cfi_intelext_partition_fixup( struct cfi_pri_intelext *extp = cfi->cmdset_priv; /* - * Probing of multi-partition flash ships. + * Probing of multi-partition flash chips. * * To support multiple partitions when available, we simply arrange * for each of them to have their own flchip structure even if they @@ -1780,7 +1780,7 @@ static int __xipram do_erase_oneblock(st return ret; } -int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) { unsigned long ofs, len; int ret; @@ -1930,7 +1930,7 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + ofs, len, NULL); #endif ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, @@ -1940,7 +1940,7 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + ofs, len, NULL); #endif return ret; @@ -1954,7 +1954,7 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + ofs, len, NULL); #endif ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, @@ -1964,7 +1964,7 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + ofs, len, NULL); #endif return ret; @@ -2255,7 +2255,7 @@ static void cfi_intelext_save_locks(stru adr = region->offset + block * len; status = cfi_varsize_frob(mtd, - do_getlockstatus_oneblock, adr, len, 0); + do_getlockstatus_oneblock, adr, len, NULL); if (status) set_bit(block, region->lockmap); else diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 1f64458..389acc6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -1609,7 +1609,7 @@ static int __xipram do_erase_oneblock(st } -int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) { unsigned long ofs, len; int ret; diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 58e561e..a67b23b 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -17,7 +17,6 @@ #include #include #include #include -#include #include #include @@ -70,6 +69,7 @@ #define AT49BV32XT 0x00C9 /* Fujitsu */ #define MBM29F040C 0x00A4 +#define MBM29F800BA 0x2258 #define MBM29LV650UE 0x22D7 #define MBM29LV320TE 0x22F6 #define MBM29LV320BE 0x22F9 @@ -129,6 +129,7 @@ #define PM49FL008 0x006A #define LH28F640BF 0x00b0 /* ST - www.st.com */ +#define M29F800AB 0x0058 #define M29W800DT 0x00D7 #define M29W800DB 0x005B #define M29W160DT 0x22C4 @@ -646,6 +647,23 @@ static const struct amd_flash_info jedec } }, { .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29F800BA, + .name = "Fujitsu MBM29F800BA", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15), + } + }, { + .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV650UE, .name = "Fujitsu MBM29LV650UE", .uaddr = { @@ -1510,6 +1528,23 @@ static const struct amd_flash_info jedec ERASEINFO(0x1000,256) } + }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M29F800AB, + .name = "ST M29F800AB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15), + } }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index ff642f8..811d56f 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -60,21 +60,22 @@ config MTD_DATAFLASH Sometimes DataFlash chips are packaged inside MMC-format cards; at this writing, the MMC stack won't handle those. -config MTD_DATAFLASH26 - tristate "AT91RM9200 DataFlash AT26xxx" - depends on MTD && ARCH_AT91RM9200 && AT91_SPI - help - This enables access to the DataFlash chip (AT26xxx) on an - AT91RM9200-based board. - If you have such a board and such a DataFlash, say 'Y'. - config MTD_M25P80 - tristate "Support for M25 SPI Flash" + tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" depends on SPI_MASTER && EXPERIMENTAL help - This enables access to ST M25P80 and similar SPI flash chips, - used for program and data storage. Set up your spi devices - with the right board-specific platform data. + This enables access to most modern SPI flash chips, used for + program and data storage. Series supported include Atmel AT26DF, + Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips + are supported as well. See the driver source for the current list, + or to add other chips. + + Note that the original DataFlash chips (AT45 series, not AT26DF), + need an entirely different driver. + + Set up your spi devices with the right board-specific platform data, + if you want to specify device partitioning or to use a device which + doesn't support the JEDEC ID instruction. config MTD_SLRAM tristate "Uncached system RAM" diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 8ab568b..0f788d5 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,5 +16,4 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o -obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c deleted file mode 100644 index 64ce37f..0000000 --- a/drivers/mtd/devices/at91_dataflash26.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) - * This is a largely modified version of at91_dataflash.c that - * supports AT26xxx dataflash chips. The original driver supports - * AT45xxx chips. - * - * Note: This driver was only tested with an AT26F004. It should be - * easy to make it work with other AT26xxx dataflash devices, though. - * - * Copyright (C) 2007 Hans J. Koch - * original Copyright (C) SAN People (Pty) Ltd - * - * 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 - -#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ - -#define MANUFACTURER_ID_ATMEL 0x1F - -/* command codes */ - -#define AT26_OP_READ_STATUS 0x05 -#define AT26_OP_READ_DEV_ID 0x9F -#define AT26_OP_ERASE_PAGE_4K 0x20 -#define AT26_OP_READ_ARRAY_FAST 0x0B -#define AT26_OP_SEQUENTIAL_WRITE 0xAF -#define AT26_OP_WRITE_ENABLE 0x06 -#define AT26_OP_WRITE_DISABLE 0x04 -#define AT26_OP_SECTOR_PROTECT 0x36 -#define AT26_OP_SECTOR_UNPROTECT 0x39 - -/* status register bits */ - -#define AT26_STATUS_BUSY 0x01 -#define AT26_STATUS_WRITE_ENABLE 0x02 - -struct dataflash_local -{ - int spi; /* SPI chip-select number */ - unsigned int page_size; /* number of bytes per page */ -}; - - -/* Detected DataFlash devices */ -static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; -static int nr_devices = 0; - -/* Allocate a single SPI transfer descriptor. We're assuming that if multiple - SPI transfers occur at the same time, spi_access_bus() will serialize them. - If this is not valid, then either (i) each dataflash 'priv' structure - needs it's own transfer descriptor, (ii) we lock this one, or (iii) use - another mechanism. */ -static struct spi_transfer_list* spi_transfer_desc; - -/* - * Perform a SPI transfer to access the DataFlash device. - */ -static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, - char* txnext, int txnext_len, char* rxnext, int rxnext_len) -{ - struct spi_transfer_list* list = spi_transfer_desc; - - list->tx[0] = tx; list->txlen[0] = tx_len; - list->rx[0] = rx; list->rxlen[0] = rx_len; - - list->tx[1] = txnext; list->txlen[1] = txnext_len; - list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; - - list->nr_transfers = nr; - /* Note: spi_transfer() always returns 0, there are no error checks */ - return spi_transfer(list); -} - -/* - * Return the status of the DataFlash device. - */ -static unsigned char at91_dataflash26_status(void) -{ - unsigned char command[2]; - - command[0] = AT26_OP_READ_STATUS; - command[1] = 0; - - do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); - - return command[1]; -} - -/* - * Poll the DataFlash device until it is READY. - */ -static unsigned char at91_dataflash26_waitready(void) -{ - unsigned char status; - - while (1) { - status = at91_dataflash26_status(); - if (!(status & AT26_STATUS_BUSY)) - return status; - } -} - -/* - * Enable/disable write access - */ - static void at91_dataflash26_write_enable(int enable) -{ - unsigned char cmd[2]; - - DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable); - - if (enable) - cmd[0] = AT26_OP_WRITE_ENABLE; - else - cmd[0] = AT26_OP_WRITE_DISABLE; - cmd[1] = 0; - - do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); -} - -/* - * Protect/unprotect sector - */ - static void at91_dataflash26_sector_protect(loff_t addr, int protect) -{ - unsigned char cmd[4]; - - DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n", - addr, protect); - - if (protect) - cmd[0] = AT26_OP_SECTOR_PROTECT; - else - cmd[0] = AT26_OP_SECTOR_UNPROTECT; - cmd[1] = (addr & 0x00FF0000) >> 16; - cmd[2] = (addr & 0x0000FF00) >> 8; - cmd[3] = (addr & 0x000000FF); - - do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); -} - -/* - * Erase blocks of flash. - */ -static int at91_dataflash26_erase(struct mtd_info *mtd, - struct erase_info *instr) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned char cmd[4]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n", - instr->addr, instr->len); - - /* Sanity checks */ - if (priv->page_size != 4096) - return -EINVAL; /* Can't handle other sizes at the moment */ - - if ( ((instr->len % mtd->erasesize) != 0) - || ((instr->len % priv->page_size) != 0) - || ((instr->addr % priv->page_size) != 0) - || ((instr->addr + instr->len) > mtd->size)) - return -EINVAL; - - spi_access_bus(priv->spi); - - while (instr->len > 0) { - at91_dataflash26_write_enable(1); - at91_dataflash26_sector_protect(instr->addr, 0); - at91_dataflash26_write_enable(1); - cmd[0] = AT26_OP_ERASE_PAGE_4K; - cmd[1] = (instr->addr & 0x00FF0000) >> 16; - cmd[2] = (instr->addr & 0x0000FF00) >> 8; - cmd[3] = (instr->addr & 0x000000FF); - - DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x" - "0x%02x\n", - cmd[0], cmd[1], cmd[2], cmd[3]); - - do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); - at91_dataflash26_waitready(); - - instr->addr += priv->page_size; /* next page */ - instr->len -= priv->page_size; - } - - at91_dataflash26_write_enable(0); - spi_release_bus(priv->spi); - - /* Inform MTD subsystem that erase is complete */ - instr->state = MTD_ERASE_DONE; - if (instr->callback) - instr->callback(instr); - - return 0; -} - -/* - * Read from the DataFlash device. - * from : Start offset in flash device - * len : Number of bytes to read - * retlen : Number of bytes actually read - * buf : Buffer that will receive data - */ -static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned char cmd[5]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n", - from, from+len); - - *retlen = 0; - - /* Sanity checks */ - if (!len) - return 0; - if (from + len > mtd->size) - return -EINVAL; - - cmd[0] = AT26_OP_READ_ARRAY_FAST; - cmd[1] = (from & 0x00FF0000) >> 16; - cmd[2] = (from & 0x0000FF00) >> 8; - cmd[3] = (from & 0x000000FF); - /* cmd[4] is a "Don't care" byte */ - - DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n", - cmd[0], cmd[1], cmd[2], cmd[3]); - - spi_access_bus(priv->spi); - do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len); - spi_release_bus(priv->spi); - - *retlen = len; - return 0; -} - -/* - * Write to the DataFlash device. - * to : Start offset in flash device - * len : Number of bytes to write - * retlen : Number of bytes actually written - * buf : Buffer containing the data - */ -static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned int addr, buf_index = 0; - int ret = -EIO, sector, last_sector; - unsigned char status, cmd[5]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len); - - *retlen = 0; - - /* Sanity checks */ - if (!len) - return 0; - if (to + len > mtd->size) - return -EINVAL; - - spi_access_bus(priv->spi); - - addr = to; - last_sector = -1; - - while (buf_index < len) { - sector = addr / priv->page_size; - /* Write first byte if a new sector begins */ - if (sector != last_sector) { - at91_dataflash26_write_enable(1); - at91_dataflash26_sector_protect(addr, 0); - at91_dataflash26_write_enable(1); - - /* Program first byte of a new sector */ - cmd[0] = AT26_OP_SEQUENTIAL_WRITE; - cmd[1] = (addr & 0x00FF0000) >> 16; - cmd[2] = (addr & 0x0000FF00) >> 8; - cmd[3] = (addr & 0x000000FF); - cmd[4] = buf[buf_index++]; - do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); - status = at91_dataflash26_waitready(); - addr++; - /* On write errors, the chip resets the write enable - flag. This also happens after the last byte of a - sector is successfully programmed. */ - if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) - && ((addr % priv->page_size) != 0) ) { - DEBUG(MTD_DEBUG_LEVEL1, - "write error1: addr=0x%06x, " - "status=0x%02x\n", addr, status); - goto write_err; - } - (*retlen)++; - last_sector = sector; - } - - /* Write subsequent bytes in the same sector */ - cmd[0] = AT26_OP_SEQUENTIAL_WRITE; - cmd[1] = buf[buf_index++]; - do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); - status = at91_dataflash26_waitready(); - addr++; - - if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) - && ((addr % priv->page_size) != 0) ) { - DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, " - "status=0x%02x\n", addr, status); - goto write_err; - } - - (*retlen)++; - } - - ret = 0; - at91_dataflash26_write_enable(0); -write_err: - spi_release_bus(priv->spi); - return ret; -} - -/* - * Initialize and register DataFlash device with MTD subsystem. - */ -static int __init add_dataflash(int channel, char *name, int nr_pages, - int pagesize) -{ - struct mtd_info *device; - struct dataflash_local *priv; - - if (nr_devices >= DATAFLASH_MAX_DEVICES) { - printk(KERN_ERR "at91_dataflash26: Too many devices " - "detected\n"); - return 0; - } - - device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8, - GFP_KERNEL); - if (!device) - return -ENOMEM; - - device->name = (char *)&device[1]; - sprintf(device->name, "%s.spi%d", name, channel); - device->size = nr_pages * pagesize; - device->erasesize = pagesize; - device->owner = THIS_MODULE; - device->type = MTD_DATAFLASH; - device->flags = MTD_CAP_NORFLASH; - device->erase = at91_dataflash26_erase; - device->read = at91_dataflash26_read; - device->write = at91_dataflash26_write; - - priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local), - GFP_KERNEL); - if (!priv) { - kfree(device); - return -ENOMEM; - } - - priv->spi = channel; - priv->page_size = pagesize; - device->priv = priv; - - mtd_devices[nr_devices] = device; - nr_devices++; - printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n", - name, channel, device->size); - - return add_mtd_device(device); -} - -/* - * Detect and initialize DataFlash device connected to specified SPI channel. - * - */ - -struct dataflash26_types { - unsigned char id0; - unsigned char id1; - char *name; - int pagesize; - int nr_pages; -}; - -struct dataflash26_types df26_types[] = { - { - .id0 = 0x04, - .id1 = 0x00, - .name = "AT26F004", - .pagesize = 4096, - .nr_pages = 128, - }, - { - .id0 = 0x45, - .id1 = 0x01, - .name = "AT26DF081A", /* Not tested ! */ - .pagesize = 4096, - .nr_pages = 256, - }, -}; - -static int __init at91_dataflash26_detect(int channel) -{ - unsigned char status, cmd[5]; - int i; - - spi_access_bus(channel); - status = at91_dataflash26_status(); - - if (status == 0 || status == 0xff) { - printk(KERN_ERR "at91_dataflash26_detect: status error %d\n", - status); - spi_release_bus(channel); - return -ENODEV; - } - - cmd[0] = AT26_OP_READ_DEV_ID; - do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); - spi_release_bus(channel); - - if (cmd[1] != MANUFACTURER_ID_ATMEL) - return -ENODEV; - - for (i = 0; i < ARRAY_SIZE(df26_types); i++) { - if ( cmd[2] == df26_types[i].id0 - && cmd[3] == df26_types[i].id1) - return add_dataflash(channel, - df26_types[i].name, - df26_types[i].nr_pages, - df26_types[i].pagesize); - } - - printk(KERN_ERR "at91_dataflash26_detect: Unsupported device " - "(0x%02x/0x%02x)\n", cmd[2], cmd[3]); - return -ENODEV; -} - -static int __init at91_dataflash26_init(void) -{ - spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), - GFP_KERNEL); - if (!spi_transfer_desc) - return -ENOMEM; - - /* DataFlash (SPI chip select 0) */ - at91_dataflash26_detect(0); - -#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD - /* DataFlash card (SPI chip select 3) */ - at91_dataflash26_detect(3); -#endif - return 0; -} - -static void __exit at91_dataflash26_exit(void) -{ - int i; - - for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { - if (mtd_devices[i]) { - del_mtd_device(mtd_devices[i]); - kfree(mtd_devices[i]->priv); - kfree(mtd_devices[i]); - } - } - nr_devices = 0; - kfree(spi_transfer_desc); -} - -module_init(at91_dataflash26_init); -module_exit(at91_dataflash26_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Hans J. Koch"); -MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200"); diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index 54aa759..d8cc94e 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -81,9 +81,7 @@ #else /* CONFIG_MTD_DOCPROBE_HIGH */ #endif /* CONFIG_MTD_DOCPROBE_HIGH */ #elif defined(__PPC__) 0xe4000000, -#elif defined(CONFIG_MOMENCO_OCELOT_G) - 0xff000000, -##else +#else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif 0xffffffff }; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 78c2511..98df5bc 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1,5 +1,5 @@ /* - * MTD SPI driver for ST M25Pxx flash chips + * MTD SPI driver for ST M25Pxx (and similar) serial flash chips * * Author: Mike Lavender, mike@steroidmicros.com * @@ -19,33 +19,32 @@ #include #include #include #include -#include +#include + #include #include + #include #include -#include - - -/* NOTE: AT 25F and SST 25LF series are very similar, - * but commands for sector erase and chip id differ... - */ #define FLASH_PAGESIZE 256 /* Flash opcodes. */ -#define OPCODE_WREN 6 /* Write enable */ -#define OPCODE_RDSR 5 /* Read status register */ -#define OPCODE_READ 3 /* Read data bytes */ -#define OPCODE_PP 2 /* Page program */ -#define OPCODE_SE 0xd8 /* Sector erase */ -#define OPCODE_RES 0xab /* Read Electronic Signature */ +#define OPCODE_WREN 0x06 /* Write enable */ +#define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ +#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ #define SR_WEL 2 /* Write enable latch */ +/* meaning of other SR_* bits may differ between vendors */ #define SR_BP0 4 /* Block protect 0 */ #define SR_BP1 8 /* Block protect 1 */ #define SR_BP2 0x10 /* Block protect 2 */ @@ -65,9 +64,10 @@ #endif struct m25p { struct spi_device *spi; - struct semaphore lock; + struct mutex lock; struct mtd_info mtd; - unsigned partitioned; + unsigned partitioned:1; + u8 erase_opcode; u8 command[4]; }; @@ -150,8 +150,9 @@ static int wait_till_ready(struct m25p * */ static int erase_sector(struct m25p *flash, u32 offset) { - DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id, - __FUNCTION__, offset); + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", + flash->spi->dev.bus_id, __FUNCTION__, + flash->mtd.erasesize / 1024, offset); /* Wait until finished previous write command. */ if (wait_till_ready(flash)) @@ -161,7 +162,7 @@ static int erase_sector(struct m25p *fla write_enable(flash); /* Set up command buffer. */ - flash->command[0] = OPCODE_SE; + flash->command[0] = flash->erase_opcode; flash->command[1] = offset >> 16; flash->command[2] = offset >> 8; flash->command[3] = offset; @@ -201,13 +202,17 @@ static int m25p80_erase(struct mtd_info addr = instr->addr; len = instr->len; - down(&flash->lock); + mutex_lock(&flash->lock); + + /* REVISIT in some cases we could speed up erasing large regions + * by using OPCODE_SE instead of OPCODE_BE_4K + */ /* now erase those sectors */ while (len) { if (erase_sector(flash, addr)) { instr->state = MTD_ERASE_FAILED; - up(&flash->lock); + mutex_unlock(&flash->lock); return -EIO; } @@ -215,7 +220,7 @@ static int m25p80_erase(struct mtd_info len -= mtd->erasesize; } - up(&flash->lock); + mutex_unlock(&flash->lock); instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); @@ -260,16 +265,19 @@ static int m25p80_read(struct mtd_info * if (retlen) *retlen = 0; - down(&flash->lock); + mutex_lock(&flash->lock); /* Wait till previous write/erase is done. */ if (wait_till_ready(flash)) { /* REVISIT status return?? */ - up(&flash->lock); + mutex_unlock(&flash->lock); return 1; } - /* NOTE: OPCODE_FAST_READ (if available) is faster... */ + /* FIXME switch to OPCODE_FAST_READ. It's required for higher + * clocks; and at this writing, every chip this driver handles + * supports that opcode. + */ /* Set up the write data buffer. */ flash->command[0] = OPCODE_READ; @@ -281,7 +289,7 @@ static int m25p80_read(struct mtd_info * *retlen = m.actual_length - sizeof(flash->command); - up(&flash->lock); + mutex_unlock(&flash->lock); return 0; } @@ -323,7 +331,7 @@ static int m25p80_write(struct mtd_info t[1].tx_buf = buf; spi_message_add_tail(&t[1], &m); - down(&flash->lock); + mutex_lock(&flash->lock); /* Wait until finished previous write command. */ if (wait_till_ready(flash)) @@ -381,10 +389,10 @@ static int m25p80_write(struct mtd_info if (retlen) *retlen += m.actual_length - sizeof(flash->command); - } - } + } + } - up(&flash->lock); + mutex_unlock(&flash->lock); return 0; } @@ -398,24 +406,118 @@ static int m25p80_write(struct mtd_info struct flash_info { char *name; - u8 id; - u16 jedec_id; + + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ unsigned sector_size; - unsigned n_sectors; + u16 n_sectors; + + u16 flags; +#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ }; + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ static struct flash_info __devinitdata m25p_data [] = { - /* REVISIT: fill in JEDEC ids, for parts that have them */ - { "m25p05", 0x05, 0x2010, 32 * 1024, 2 }, - { "m25p10", 0x10, 0x2011, 32 * 1024, 4 }, - { "m25p20", 0x11, 0x2012, 64 * 1024, 4 }, - { "m25p40", 0x12, 0x2013, 64 * 1024, 8 }, - { "m25p80", 0x13, 0x0000, 64 * 1024, 16 }, - { "m25p16", 0x14, 0x2015, 64 * 1024, 32 }, - { "m25p32", 0x15, 0x2016, 64 * 1024, 64 }, - { "m25p64", 0x16, 0x2017, 64 * 1024, 128 }, + + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", 0x1f6601, 32 * 1024, 4, SECT_4K, }, + { "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, }, + + { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, }, + + { "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, }, + { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, }, + { "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, }, + { "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl004a", 0x010212, 64 * 1024, 8, }, + { "s25sl008a", 0x010213, 64 * 1024, 16, }, + { "s25sl016a", 0x010214, 64 * 1024, 32, }, + { "s25sl032a", 0x010215, 64 * 1024, 64, }, + { "s25sl064a", 0x010216, 64 * 1024, 128, }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, }, + { "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, }, + { "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, }, + { "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", 0x202010, 32 * 1024, 2, }, + { "m25p10", 0x202011, 32 * 1024, 4, }, + { "m25p20", 0x202012, 64 * 1024, 4, }, + { "m25p40", 0x202013, 64 * 1024, 8, }, + { "m25p80", 0, 64 * 1024, 16, }, + { "m25p16", 0x202015, 64 * 1024, 32, }, + { "m25p32", 0x202016, 64 * 1024, 64, }, + { "m25p64", 0x202017, 64 * 1024, 128, }, + { "m25p128", 0x202018, 256 * 1024, 64, }, + + { "m45pe80", 0x204014, 64 * 1024, 16, }, + { "m45pe16", 0x204015, 64 * 1024, 32, }, + + { "m25pe80", 0x208014, 64 * 1024, 16, }, + { "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, }, + { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, }, + { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, }, + { "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, }, + { "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, }, + { "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, }, + { "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, }, }; +static struct flash_info *__devinit jedec_probe(struct spi_device *spi) +{ + int tmp; + u8 code = OPCODE_RDID; + u8 id[3]; + u32 jedec; + struct flash_info *info; + + /* JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + tmp = spi_write_then_read(spi, &code, 1, id, 3); + if (tmp < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", + spi->dev.bus_id, tmp); + return NULL; + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + for (tmp = 0, info = m25p_data; + tmp < ARRAY_SIZE(m25p_data); + tmp++, info++) { + if (info->jedec_id == jedec) + return info; + } + dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); + return NULL; +} + + /* * board specific setup should have ensured the SPI clock used here * matches what the READ command supports, at least until this driver @@ -429,37 +531,51 @@ static int __devinit m25p_probe(struct s unsigned i; /* Platform data helps sort out which chip type we have, as - * well as how this board partitions it. + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. */ data = spi->dev.platform_data; - if (!data || !data->type) { - /* FIXME some chips can identify themselves with RES - * or JEDEC get-id commands. Try them ... - */ - DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n", - spi->dev.bus_id); - return -ENODEV; - } + if (data && data->type) { + for (i = 0, info = m25p_data; + i < ARRAY_SIZE(m25p_data); + i++, info++) { + if (strcmp(data->type, info->name) == 0) + break; + } - for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) { - if (strcmp(data->type, info->name) == 0) - break; - } - if (i == ARRAY_SIZE(m25p_data)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n", - spi->dev.bus_id, data->type); + /* unrecognized chip? */ + if (i == ARRAY_SIZE(m25p_data)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", + spi->dev.bus_id, data->type); + info = NULL; + + /* recognized; is that chip really what's there? */ + } else if (info->jedec_id) { + struct flash_info *chip = jedec_probe(spi); + + if (!chip || chip != info) { + dev_warn(&spi->dev, "found %s, expected %s\n", + chip ? chip->name : "UNKNOWN", + info->name); + info = NULL; + } + } + } else + info = jedec_probe(spi); + + if (!info) return -ENODEV; - } flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) return -ENOMEM; flash->spi = spi; - init_MUTEX(&flash->lock); + mutex_init(&flash->lock); dev_set_drvdata(&spi->dev, flash); - if (data->name) + if (data && data->name) flash->mtd.name = data->name; else flash->mtd.name = spi->dev.bus_id; @@ -468,17 +584,25 @@ static int __devinit m25p_probe(struct s flash->mtd.writesize = 1; flash->mtd.flags = MTD_CAP_NORFLASH; flash->mtd.size = info->sector_size * info->n_sectors; - flash->mtd.erasesize = info->sector_size; flash->mtd.erase = m25p80_erase; flash->mtd.read = m25p80_read; flash->mtd.write = m25p80_write; + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + flash->erase_opcode = OPCODE_BE_4K; + flash->mtd.erasesize = 4096; + } else { + flash->erase_opcode = OPCODE_SE; + flash->mtd.erasesize = info->sector_size; + } + dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, flash->mtd.size / 1024); DEBUG(MTD_DEBUG_LEVEL2, - "mtd .name = %s, .size = 0x%.8x (%uM) " - ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n", + "mtd .name = %s, .size = 0x%.8x (%uMiB) " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", flash->mtd.name, flash->mtd.size, flash->mtd.size / (1024*1024), flash->mtd.erasesize, flash->mtd.erasesize / 1024, @@ -488,7 +612,7 @@ static int __devinit m25p_probe(struct s for (i = 0; i < flash->mtd.numeraseregions; i++) DEBUG(MTD_DEBUG_LEVEL2, "mtd.eraseregions[%d] = { .offset = 0x%.8x, " - ".erasesize = 0x%.8x (%uK), " + ".erasesize = 0x%.8x (%uKiB), " ".numblocks = %d }\n", i, flash->mtd.eraseregions[i].offset, flash->mtd.eraseregions[i].erasesize, @@ -516,14 +640,14 @@ #endif } if (nr_parts > 0) { - for (i = 0; i < data->nr_parts; i++) { + for (i = 0; i < nr_parts; i++) { DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " "{.name = %s, .offset = 0x%.8x, " - ".size = 0x%.8x (%uK) }\n", - i, data->parts[i].name, - data->parts[i].offset, - data->parts[i].size, - data->parts[i].size / 1024); + ".size = 0x%.8x (%uKiB) }\n", + i, parts[i].name, + parts[i].offset, + parts[i].size, + parts[i].size / 1024); } flash->partitioned = 1; return add_mtd_partitions(&flash->mtd, parts, nr_parts); @@ -560,6 +684,11 @@ static struct spi_driver m25p80_driver = }, .probe = m25p_probe, .remove = __devexit_p(m25p_remove), + + /* REVISIT: many of these chips have deep power-down modes, which + * should clearly be entered on suspend() to minimize power use. + * And also when they're otherwise idle... + */ }; diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index a987e91..a5ed6d2 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -14,6 +14,7 @@ #include #include #include #include +#include #include #include @@ -89,7 +90,7 @@ struct dataflash { unsigned short page_offset; /* offset in flash address */ unsigned int page_size; /* of bytes per page */ - struct semaphore lock; + struct mutex lock; struct spi_device *spi; struct mtd_info mtd; @@ -167,7 +168,7 @@ static int dataflash_erase(struct mtd_in x.len = 4; spi_message_add_tail(&x, &msg); - down(&priv->lock); + mutex_lock(&priv->lock); while (instr->len > 0) { unsigned int pageaddr; int status; @@ -210,7 +211,7 @@ static int dataflash_erase(struct mtd_in instr->len -= priv->page_size; } } - up(&priv->lock); + mutex_unlock(&priv->lock); /* Inform MTD subsystem that erase is complete */ instr->state = MTD_ERASE_DONE; @@ -266,7 +267,7 @@ static int dataflash_read(struct mtd_inf x[1].len = len; spi_message_add_tail(&x[1], &msg); - down(&priv->lock); + mutex_lock(&priv->lock); /* Continuous read, max clock = f(car) which may be less than * the peak rate available. Some chips support commands with @@ -279,7 +280,7 @@ static int dataflash_read(struct mtd_inf /* plus 4 "don't care" bytes */ status = spi_sync(priv->spi, &msg); - up(&priv->lock); + mutex_unlock(&priv->lock); if (status >= 0) { *retlen = msg.actual_length - 8; @@ -336,7 +337,7 @@ static int dataflash_write(struct mtd_in else writelen = len; - down(&priv->lock); + mutex_lock(&priv->lock); while (remaining > 0) { DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n", pageaddr, offset, writelen); @@ -441,7 +442,7 @@ #endif /* CONFIG_DATAFLASH_WRITE_VERIFY else writelen = remaining; } - up(&priv->lock); + mutex_unlock(&priv->lock); return status; } @@ -463,7 +464,7 @@ add_dataflash(struct spi_device *spi, ch if (!priv) return -ENOMEM; - init_MUTEX(&priv->lock); + mutex_init(&priv->lock); priv->spi = spi; priv->page_size = pagesize; priv->page_offset = pageoffset; diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index e8f686f..7060a08 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -30,8 +30,8 @@ * * Notes: * Due to what I assume is more buggy SROM, the 64M PMC551 I - * have available claims that all 4 of it's DRAM banks have 64M - * of ram configured (making a grand total of 256M onboard). + * have available claims that all 4 of its DRAM banks have 64MiB + * of ram configured (making a grand total of 256MiB onboard). * This is slightly annoying since the BAR0 size reflects the * aperture size, not the dram size, and the V370PDC supplies no * other method for memory size discovery. This problem is @@ -70,7 +70,7 @@ * made the memory unusable, added a fix to code to touch up * the DRAM some. * - * Bugs/FIXME's: + * Bugs/FIXMEs: * * MUST fix the init function to not spin on a register * waiting for it to set .. this does not safely handle busted * devices that never reset the register correctly which will @@ -562,10 +562,10 @@ #ifdef CONFIG_MTD_PMC551_DEBUG /* * Some screen fun */ - printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at " + printk(KERN_DEBUG "pmc551: %d%sB (0x%x) of %sprefetchable memory at " "0x%llx\n", (size < 1024) ? size : (size < 1048576) ? size >> 10 : size >> 20, - (size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size, + (size < 1024) ? "" : (size < 1048576) ? "Ki" : "Mi", size, ((dcmd & (0x1 << 3)) == 0) ? "non-" : "", (unsigned long long)pci_resource_start(dev, 0)); @@ -649,14 +649,10 @@ MODULE_DESCRIPTION(PMC551_VERSION); * Stuff these outside the ifdef so as to not bust compiled in driver support */ static int msize = 0; -#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE) -static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE; -#else static int asize = 0; -#endif module_param(msize, int, 0); -MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]"); +MODULE_PARM_DESC(msize, "memory size in MiB [1 - 1024]"); module_param(asize, int, 0); MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]"); @@ -799,8 +795,7 @@ #endif mtd->owner = THIS_MODULE; if (add_mtd_device(mtd)) { - printk(KERN_NOTICE "pmc551: Failed to register new " - "device\n"); + printk(KERN_NOTICE "pmc551: Failed to register new device\n"); pci_iounmap(PCI_Device, priv->start); kfree(mtd->priv); kfree(mtd); @@ -811,13 +806,13 @@ #endif pci_dev_get(PCI_Device); printk(KERN_NOTICE "Registered pmc551 memory device.\n"); - printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n", + printk(KERN_NOTICE "Mapped %dMiB of memory from 0x%p to 0x%p\n", priv->asize >> 20, priv->start, priv->start + priv->asize); - printk(KERN_NOTICE "Total memory is %d%c\n", + printk(KERN_NOTICE "Total memory is %d%sB\n", (length < 1024) ? length : (length < 1048576) ? length >> 10 : length >> 20, - (length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M'); + (length < 1024) ? "" : (length < 1048576) ? "Ki" : "Mi"); priv->nextpmc551 = pmc551list; pmc551list = mtd; found++; @@ -850,7 +845,7 @@ static void __exit cleanup_pmc551(void) pmc551list = priv->nextpmc551; if (priv->start) { - printk(KERN_DEBUG "pmc551: unmapping %dM starting at " + printk(KERN_DEBUG "pmc551: unmapping %dMiB starting at " "0x%p\n", priv->asize >> 20, priv->start); pci_iounmap(priv->dev, priv->start); } diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index ecac0e4..b8917be 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -580,14 +580,13 @@ int INFTL_mount(struct INFTLrecord *s) logical_block = block = BLOCK_NIL; /* Temporary buffer to store ANAC numbers. */ - ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); + ANACtable = kcalloc(s->nb_blocks, sizeof(u8), GFP_KERNEL); if (!ANACtable) { printk(KERN_WARNING "INFTL: allocation of ANACtable " "failed (%zd bytes)\n", s->nb_blocks * sizeof(u8)); return -ENOMEM; } - memset(ANACtable, 0, s->nb_blocks); /* * First pass is to explore each physical unit, and construct the diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index cc6c734..5850ccd 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -354,7 +354,7 @@ config MTD_CFI_FLAGADM config MTD_WALNUT tristate "Flash device mapped on IBM 405GP Walnut" - depends on MTD_JEDECPROBE && WALNUT + depends on MTD_JEDECPROBE && WALNUT && !PPC_MERGE help This enables access routines for the flash chips on the IBM 405GP Walnut board. If you have one of these boards and would like to @@ -370,7 +370,7 @@ config MTD_EBONY config MTD_OCOTEA tristate "Flash devices mapped on IBM 440GX Ocotea" - depends on MTD_CFI && OCOTEA + depends on MTD_CFI && OCOTEA && !PPC_MERGE help This enables access routines for the flash chips on the IBM 440GX Ocotea board. If you have one of these boards and would like to @@ -384,14 +384,6 @@ config MTD_REDWOOD Redwood board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. -config MTD_TQM834x - tristate "Flash device mapped on TQ Components TQM834x Boards" - depends on MTD_CFI && TQM834x - help - This enables access routines for the flash chips on the - TQ Components TQM834x boards. If you have one of these boards - and would like to use the flash chips on it, say 'Y'. - config MTD_OCELOT tristate "Momenco Ocelot boot flash device" depends on MOMENCO_OCELOT diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 970b189..b9019b1 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -70,4 +70,3 @@ obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-fl obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o -obj-$(CONFIG_MTD_TQM834x) += tqm834x.o diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index 84fbe0e..82811bc 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c @@ -75,13 +75,6 @@ #define BOARD_FLASH_SIZE 0x04000000 /* 6 #define BOARD_FLASH_WIDTH 2 /* 16-bits */ #endif -#ifdef CONFIG_MIPS_HYDROGEN3 -#define BOARD_MAP_NAME "Hydrogen3 Flash" -#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#define USE_LOCAL_ACCESSORS /* why? */ -#endif - #ifdef CONFIG_MIPS_BOSPORUS #define BOARD_MAP_NAME "Bosporus Flash" #define BOARD_FLASH_SIZE 0x01000000 /* 16MB */ @@ -130,13 +123,6 @@ int __init alchemy_mtd_init(void) window_addr = 0x20000000 - BOARD_FLASH_SIZE; window_size = BOARD_FLASH_SIZE; -#ifdef CONFIG_MIPS_MIRAGE_WHY - /* Boot ROM flash bank only; no user bank */ - window_addr = 0x1C000000; - window_size = 0x04000000; - /* USERFS from 0x1C00 0000 to 0x1FC00000 */ - alchemy_partitions[0].size = 0x03C00000; -#endif /* * Static partition definition selection diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 7b96cd0..0c9b305 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -158,68 +158,11 @@ static struct notifier_block nettel_noti nettel_reboot_notifier, NULL, 0 }; -/* - * Erase the configuration file system. - * Used to support the software reset button. - */ -static void nettel_erasecallback(struct erase_info *done) -{ - wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; - wake_up(wait_q); -} - -static struct erase_info nettel_erase; - -int nettel_eraseconfig(void) -{ - struct mtd_info *mtd; - DECLARE_WAITQUEUE(wait, current); - wait_queue_head_t wait_q; - int ret; - - init_waitqueue_head(&wait_q); - mtd = get_mtd_device(NULL, 2); - if (!IS_ERR(mtd)) { - nettel_erase.mtd = mtd; - nettel_erase.callback = nettel_erasecallback; - nettel_erase.callback = NULL; - nettel_erase.addr = 0; - nettel_erase.len = mtd->size; - nettel_erase.priv = (u_long) &wait_q; - nettel_erase.priv = 0; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&wait_q, &wait); - - ret = mtd->erase(mtd, &nettel_erase); - if (ret) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&wait_q, &wait); - put_mtd_device(mtd); - return(ret); - } - - schedule(); /* Wait for erase to finish. */ - remove_wait_queue(&wait_q, &wait); - - put_mtd_device(mtd); - } - - return(0); -} - -#else - -int nettel_eraseconfig(void) -{ - return(0); -} - #endif /****************************************************************************/ -int __init nettel_init(void) +static int __init nettel_init(void) { volatile unsigned long *amdpar; unsigned long amdaddr, maxsize; @@ -421,10 +364,6 @@ #ifdef CONFIG_MTD_CFI_INTELEXT intel_mtd->owner = THIS_MODULE; -#ifndef CONFIG_BLK_DEV_INITRD - ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1); -#endif - num_intel_partitions = sizeof(nettel_intel_partitions) / sizeof(nettel_intel_partitions[0]); @@ -477,7 +416,7 @@ out_unmap2: /****************************************************************************/ -void __exit nettel_cleanup(void) +static void __exit nettel_cleanup(void) { #ifdef CONFIG_MTD_CFI_INTELEXT unregister_reboot_notifier(&nettel_notifier_block); diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index bbb42c3..fbd6139 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -141,7 +141,6 @@ static int __devinit of_physmap_probe(st err = -ENOMEM; goto err_out; } - memset(info, 0, sizeof(*info)); dev_set_drvdata(&dev->dev, info); @@ -213,10 +212,6 @@ #endif err_out: of_physmap_remove(dev); return err; - - return 0; - - } static struct of_device_id of_physmap_match[] = { diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 7e0377e..02bde8c 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -73,13 +73,16 @@ int __init init_msp_flash(void) return -ENXIO; printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); - msp_flash = (struct mtd_info **)kmalloc( - fcnt * sizeof(struct map_info *), GFP_KERNEL); - msp_parts = (struct mtd_partition **)kmalloc( - fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); - msp_maps = (struct map_info *)kmalloc( - fcnt * sizeof(struct mtd_info), GFP_KERNEL); - memset(msp_maps, 0, fcnt * sizeof(struct mtd_info)); + + msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); + msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); + msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); + if (!msp_flash || !msp_parts || !msp_maps) { + kfree(msp_maps); + kfree(msp_parts); + kfree(msp_flash); + return -ENOMEM; + } /* loop over the flash devices, initializing each */ for (i = 0; i < fcnt; i++) { @@ -95,9 +98,8 @@ int __init init_msp_flash(void) continue; } - msp_parts[i] = (struct mtd_partition *)kmalloc( - pcnt * sizeof(struct mtd_partition), GFP_KERNEL); - memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition)); + msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), + GFP_KERNEL); /* now initialize the devices proper */ flash_name[5] = '0' + i; diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c index 18049bc..30de5c0 100644 --- a/drivers/mtd/maps/pmcmsp-ramroot.c +++ b/drivers/mtd/maps/pmcmsp-ramroot.c @@ -79,7 +79,6 @@ static int __init init_rrmap(void) rr_mtd->owner = THIS_MODULE; add_mtd_device(rr_mtd); - ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index); return 0; } diff --git a/drivers/mtd/maps/pq2fads.c b/drivers/mtd/maps/pq2fads.c deleted file mode 100644 index fb78d87..0000000 --- a/drivers/mtd/maps/pq2fads.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * drivers/mtd/maps/pq2fads.c - * - * Mapping for the flash SIMM on 8272ADS and PQ2FADS board - * - * Author: Vitaly Bordug - * - * 2005 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - NOTE: bank width and interleave relative to the installed flash - should have been chosen within MTD_CFI_GEOMETRY options. - */ -#define PQ2FADS_BANK_WIDTH 4 - -static struct mtd_partition pq2fads_partitions[] = { - { -#ifdef CONFIG_ADS8272 - .name = "HRCW", - .size = 0x40000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "User FS", - .size = 0x5c0000, - .offset = 0x40000, -#else - .name = "User FS", - .size = 0x600000, - .offset = 0, -#endif - }, { - .name = "uImage", - .size = 0x100000, - .offset = 0x600000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "bootloader", - .size = 0x40000, - .offset = 0x700000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "bootloader env", - .size = 0x40000, - .offset = 0x740000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - } -}; - - -/* pointer to MPC885ADS board info data */ -extern unsigned char __res[]; - -static int __init init_pq2fads_mtd(void) -{ - bd_t *bd = (bd_t *)__res; - physmap_configure(bd->bi_flashstart, bd->bi_flashsize, PQ2FADS_BANK_WIDTH, NULL); - - physmap_set_partitions(pq2fads_partitions, - sizeof (pq2fads_partitions) / - sizeof (pq2fads_partitions[0])); - return 0; -} - -static void __exit cleanup_pq2fads_mtd(void) -{ -} - -module_init(init_pq2fads_mtd); -module_exit(cleanup_pq2fads_mtd); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("MTD map and partitions for MPC8272ADS boards"); diff --git a/drivers/mtd/maps/tqm834x.c b/drivers/mtd/maps/tqm834x.c deleted file mode 100644 index 9adc970..0000000 --- a/drivers/mtd/maps/tqm834x.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * drivers/mtd/maps/tqm834x.c - * - * MTD mapping driver for TQM834x boards - * - * Copyright 2005 Wolfgang Denk, DENX Software Engineering, . - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define FLASH_BANK_MAX 2 - -extern unsigned char __res[]; - -/* trivial struct to describe partition information */ -struct mtd_part_def -{ - int nums; - unsigned char *type; - struct mtd_partition* mtd_part; -}; - -static struct mtd_info* mtd_banks[FLASH_BANK_MAX]; -static struct map_info* map_banks[FLASH_BANK_MAX]; -static struct mtd_part_def part_banks[FLASH_BANK_MAX]; - -static unsigned long num_banks; -static unsigned long start_scan_addr; - -#ifdef CONFIG_MTD_PARTITIONS -/* - * The following defines the partition layout of TQM834x boards. - * - * See include/linux/mtd/partitions.h for definition of the - * mtd_partition structure. - * - * Assume minimal initial size of 4 MiB per bank, will be updated - * later in init_tqm834x_mtd() routine. - */ - -/* Partition definition for the first flash bank which is always present. */ -static struct mtd_partition tqm834x_partitions_bank1[] = { - { - .name = "u-boot", /* u-boot firmware */ - .offset = 0x00000000, - .size = 0x00040000, /* 256 KiB */ - /*mask_flags: MTD_WRITEABLE, * force read-only */ - }, - { - .name = "env", /* u-boot environment */ - .offset = 0x00040000, - .size = 0x00020000, /* 128 KiB */ - /*mask_flags: MTD_WRITEABLE, * force read-only */ - }, - { - .name = "kernel", /* linux kernel image */ - .offset = 0x00060000, - .size = 0x00100000, /* 1 MiB */ - /*mask_flags: MTD_WRITEABLE, * force read-only */ - }, - { - .name = "initrd", /* ramdisk image */ - .offset = 0x00160000, - .size = 0x00200000, /* 2 MiB */ - }, - { - .name = "user", /* user data */ - .offset = 0x00360000, - .size = 0x000a0000, /* remaining space */ - /* NOTE: this parttion size is re-calcated in */ - /* init_tqm834x_mtd() to cover actual remaining space. */ - }, -}; - -/* Partition definition for the second flash bank which may be present on some - * TQM834x boards. - */ -static struct mtd_partition tqm834x_partitions_bank2[] = { - { - .name = "jffs2", /* jffs2 filesystem */ - .offset = 0x00000000, - .size = 0x00400000, /* whole device */ - /* NOTE: this parttion size is re-calcated in */ - /* init_tqm834x_mtd() to cover actual device size. */ - }, -}; - -#endif /* CONFIG_MTD_PARTITIONS */ - -static int __init init_tqm834x_mtd(void) -{ - int idx = 0, ret = 0; - unsigned long flash_addr, flash_size, mtd_size = 0; - - /* pointer to TQM834x board info data */ - bd_t *bd = (bd_t *)__res; -#ifdef CONFIG_MTD_CMDLINE_PARTS - int n; - char mtdid[4]; - const char *part_probes[] = { "cmdlinepart", NULL }; -#endif - - flash_addr = bd->bi_flashstart; - flash_size = bd->bi_flashsize; - - /* request maximum flash size address space */ - start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size); - if (!start_scan_addr) { - printk("%s: Failed to ioremap address: 0x%lx\n", - __FUNCTION__, flash_addr); - return -EIO; - } - - for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { - if (mtd_size >= flash_size) - break; - - pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx); - - map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL); - if (map_banks[idx] == NULL) { - ret = -ENOMEM; - goto error_mem; - } - map_banks[idx]->name = kzalloc(16, GFP_KERNEL); - if (map_banks[idx]->name == NULL) { - ret = -ENOMEM; - goto error_mem; - } - - sprintf(map_banks[idx]->name, "TQM834x-%d", idx); - map_banks[idx]->size = flash_size; - map_banks[idx]->bankwidth = 4; - - simple_map_init(map_banks[idx]); - - map_banks[idx]->virt = (void __iomem *) - (start_scan_addr + ((idx > 0) ? - (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0)); - map_banks[idx]->phys = - flash_addr + ((idx > 0) ? - (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0); - - /* start to probe flash chips */ - mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]); - if (mtd_banks[idx]) { - mtd_banks[idx]->owner = THIS_MODULE; - mtd_size += mtd_banks[idx]->size; - num_banks++; - pr_debug("%s: bank %ld, name: %s, size: %d bytes \n", - __FUNCTION__, num_banks, - mtd_banks[idx]->name, mtd_banks[idx]->size); - } - } - - /* no supported flash chips found */ - if (!num_banks) { - printk("TQM834x: No supported flash chips found!\n"); - ret = -ENXIO; - goto error_mem; - } - -#ifdef CONFIG_MTD_PARTITIONS - /* - * Select static partition definitions - */ - n = ARRAY_SIZE(tqm834x_partitions_bank1); - part_banks[0].mtd_part = tqm834x_partitions_bank1; - part_banks[0].type = "static image bank1"; - part_banks[0].nums = n; - - /* update last partition size to cover actual remaining space */ - tqm834x_partitions_bank1[n - 1].size = - mtd_banks[0]->size - - tqm834x_partitions_bank1[n - 1].offset; - - /* check if we have second bank? */ - if (num_banks == 2) { - n = ARRAY_SIZE(tqm834x_partitions_bank2); - part_banks[1].mtd_part = tqm834x_partitions_bank2; - part_banks[1].type = "static image bank2"; - part_banks[1].nums = n; - - /* update last partition size to cover actual remaining space */ - tqm834x_partitions_bank2[n - 1].size = - mtd_banks[1]->size - - tqm834x_partitions_bank2[n - 1].offset; - } - - for(idx = 0; idx < num_banks ; idx++) { -#ifdef CONFIG_MTD_CMDLINE_PARTS - sprintf(mtdid, "%d", idx); - n = parse_mtd_partitions(mtd_banks[idx], - part_probes, - &part_banks[idx].mtd_part, - 0); - pr_debug("%s: %d command line partitions on bank %s\n", - __FUNCTION__, n, mtdid); - if (n > 0) { - part_banks[idx].type = "command line"; - part_banks[idx].nums = n; - } -#endif /* CONFIG_MTD_CMDLINE_PARTS */ - if (part_banks[idx].nums == 0) { - printk(KERN_NOTICE - "TQM834x flash bank %d: no partition info " - "available, registering whole device\n", idx); - add_mtd_device(mtd_banks[idx]); - } else { - printk(KERN_NOTICE - "TQM834x flash bank %d: Using %s partition " - "definition\n", idx, part_banks[idx].type); - add_mtd_partitions(mtd_banks[idx], - part_banks[idx].mtd_part, - part_banks[idx].nums); - } - } -#else /* ! CONFIG_MTD_PARTITIONS */ - printk(KERN_NOTICE "TQM834x flash: registering %d flash banks " - "at once\n", num_banks); - - for(idx = 0 ; idx < num_banks ; idx++) - add_mtd_device(mtd_banks[idx]); - -#endif /* CONFIG_MTD_PARTITIONS */ - - return 0; -error_mem: - for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { - if (map_banks[idx] != NULL) { - if (map_banks[idx]->name != NULL) { - kfree(map_banks[idx]->name); - map_banks[idx]->name = NULL; - } - kfree(map_banks[idx]); - map_banks[idx] = NULL; - } - } - - iounmap((void *)start_scan_addr); - - return ret; -} - -static void __exit cleanup_tqm834x_mtd(void) -{ - unsigned int idx = 0; - for(idx = 0 ; idx < num_banks ; idx++) { - /* destroy mtd_info previously allocated */ - if (mtd_banks[idx]) { - del_mtd_partitions(mtd_banks[idx]); - map_destroy(mtd_banks[idx]); - } - - /* release map_info not used anymore */ - kfree(map_banks[idx]->name); - kfree(map_banks[idx]); - } - - if (start_scan_addr) { - iounmap((void *)start_scan_addr); - start_scan_addr = 0; - } -} - -module_init(init_tqm834x_mtd); -module_exit(cleanup_tqm834x_mtd); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Wolfgang Denk "); -MODULE_DESCRIPTION("MTD map driver for TQM834x boards"); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index ef89780..74d9d30 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -24,10 +24,9 @@ #include #include #include -static LIST_HEAD(blktrans_majors); +#include "mtdcore.h" -extern struct mutex mtd_table_mutex; -extern struct mtd_info *mtd_table[]; +static LIST_HEAD(blktrans_majors); struct mtd_blkcore_priv { struct task_struct *thread; @@ -202,7 +201,7 @@ static int blktrans_ioctl(struct inode * } } -struct block_device_operations mtd_blktrans_ops = { +static struct block_device_operations mtd_blktrans_ops = { .owner = THIS_MODULE, .open = blktrans_open, .release = blktrans_release, diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index d091b24..22ed96c 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -136,7 +136,8 @@ static int mtd_close(struct inode *inode DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - if (mtd->sync) + /* Only sync if opened RW */ + if ((file->f_mode & 2) && mtd->sync) mtd->sync(mtd); put_mtd_device(mtd); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 41844ea..96be7ef 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -178,7 +178,7 @@ concat_writev(struct mtd_info *mtd, cons /* Check alignment */ if (mtd->writesize > 1) { - loff_t __to = to; + uint64_t __to = to; if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize)) return -EINVAL; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c153b64..6c2645e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -22,6 +22,8 @@ #include #include +#include "mtdcore.h" + /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ DEFINE_MUTEX(mtd_table_mutex); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h new file mode 100644 index 0000000..a33251f --- /dev/null +++ b/drivers/mtd/mtdcore.h @@ -0,0 +1,11 @@ +/* linux/drivers/mtd/mtdcore.h + * + * Header file for driver private mtdcore exports + * + */ + +/* These are exported solely for the purpose of mtd_blkdevs.c. You + should not use them for _anything_ else */ + +extern struct mutex mtd_table_mutex; +extern struct mtd_info *mtd_table[MAX_MTD_DEVICES]; diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c new file mode 100644 index 0000000..f8af627 --- /dev/null +++ b/drivers/mtd/mtdoops.c @@ -0,0 +1,376 @@ +/* + * MTD Oops/Panic logger + * + * Copyright (C) 2007 Nokia Corporation. All rights reserved. + * + * Author: Richard Purdie + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define OOPS_PAGE_SIZE 4096 + +static struct mtdoops_context { + int mtd_index; + struct work_struct work; + struct mtd_info *mtd; + int oops_pages; + int nextpage; + int nextcount; + + void *oops_buf; + int ready; + int writecount; +} oops_cxt; + +static void mtdoops_erase_callback(struct erase_info *done) +{ + wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; + wake_up(wait_q); +} + +static int mtdoops_erase_block(struct mtd_info *mtd, int offset) +{ + struct erase_info erase; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + int ret; + + init_waitqueue_head(&wait_q); + erase.mtd = mtd; + erase.callback = mtdoops_erase_callback; + erase.addr = offset; + if (mtd->erasesize < OOPS_PAGE_SIZE) + erase.len = OOPS_PAGE_SIZE; + else + erase.len = mtd->erasesize; + erase.priv = (u_long)&wait_q; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + ret = mtd->erase(mtd, &erase); + if (ret) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] " + "on \"%s\" failed\n", + erase.addr, erase.len, mtd->name); + return ret; + } + + schedule(); /* Wait for erase to finish. */ + remove_wait_queue(&wait_q, &wait); + + return 0; +} + +static int mtdoops_inc_counter(struct mtdoops_context *cxt) +{ + struct mtd_info *mtd = cxt->mtd; + size_t retlen; + u32 count; + int ret; + + cxt->nextpage++; + if (cxt->nextpage > cxt->oops_pages) + cxt->nextpage = 0; + cxt->nextcount++; + if (cxt->nextcount == 0xffffffff) + cxt->nextcount = 0; + + ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, + &retlen, (u_char *) &count); + if ((retlen != 4) || (ret < 0)) { + printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" + ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, + retlen, ret); + return 1; + } + + /* See if we need to erase the next block */ + if (count != 0xffffffff) + return 1; + + printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", + cxt->nextpage, cxt->nextcount); + cxt->ready = 1; + return 0; +} + +static void mtdoops_prepare(struct mtdoops_context *cxt) +{ + struct mtd_info *mtd = cxt->mtd; + int i = 0, j, ret, mod; + + /* We were unregistered */ + if (!mtd) + return; + + mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize; + if (mod != 0) { + cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE); + if (cxt->nextpage > cxt->oops_pages) + cxt->nextpage = 0; + } + + while (mtd->block_isbad && + mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) { +badblock: + printk(KERN_WARNING "mtdoops: Bad block at %08x\n", + cxt->nextpage * OOPS_PAGE_SIZE); + i++; + cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE); + if (cxt->nextpage > cxt->oops_pages) + cxt->nextpage = 0; + if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) { + printk(KERN_ERR "mtdoops: All blocks bad!\n"); + return; + } + } + + for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) + ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + + if (ret < 0) { + if (mtd->block_markbad) + mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + goto badblock; + } + + printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); + + cxt->ready = 1; +} + +static void mtdoops_workfunc(struct work_struct *work) +{ + struct mtdoops_context *cxt = + container_of(work, struct mtdoops_context, work); + + mtdoops_prepare(cxt); +} + +static int find_next_position(struct mtdoops_context *cxt) +{ + struct mtd_info *mtd = cxt->mtd; + int page, maxpos = 0; + u32 count, maxcount = 0xffffffff; + size_t retlen; + + for (page = 0; page < cxt->oops_pages; page++) { + mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); + if (count == 0xffffffff) + continue; + if (maxcount == 0xffffffff) { + maxcount = count; + maxpos = page; + } else if ((count < 0x40000000) && (maxcount > 0xc0000000)) { + maxcount = count; + maxpos = page; + } else if ((count > maxcount) && (count < 0xc0000000)) { + maxcount = count; + maxpos = page; + } else if ((count > maxcount) && (count > 0xc0000000) + && (maxcount > 0x80000000)) { + maxcount = count; + maxpos = page; + } + } + if (maxcount == 0xffffffff) { + cxt->nextpage = 0; + cxt->nextcount = 1; + cxt->ready = 1; + printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", + cxt->nextpage, cxt->nextcount); + return 0; + } + + cxt->nextpage = maxpos; + cxt->nextcount = maxcount; + + return mtdoops_inc_counter(cxt); +} + + +static void mtdoops_notify_add(struct mtd_info *mtd) +{ + struct mtdoops_context *cxt = &oops_cxt; + int ret; + + if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) + return; + + if (mtd->size < (mtd->erasesize * 2)) { + printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n", + mtd->index); + return; + } + + cxt->mtd = mtd; + cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; + + ret = find_next_position(cxt); + if (ret == 1) + mtdoops_prepare(cxt); + + printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index); +} + +static void mtdoops_notify_remove(struct mtd_info *mtd) +{ + struct mtdoops_context *cxt = &oops_cxt; + + if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) + return; + + cxt->mtd = NULL; + flush_scheduled_work(); +} + +static void mtdoops_console_sync(void) +{ + struct mtdoops_context *cxt = &oops_cxt; + struct mtd_info *mtd = cxt->mtd; + size_t retlen; + int ret; + + if (!cxt->ready || !mtd) + return; + + if (cxt->writecount == 0) + return; + + if (cxt->writecount < OOPS_PAGE_SIZE) + memset(cxt->oops_buf + cxt->writecount, 0xff, + OOPS_PAGE_SIZE - cxt->writecount); + + ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, + OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); + cxt->ready = 0; + cxt->writecount = 0; + + if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) + printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", + cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); + + ret = mtdoops_inc_counter(cxt); + if (ret == 1) + schedule_work(&cxt->work); +} + +static void +mtdoops_console_write(struct console *co, const char *s, unsigned int count) +{ + struct mtdoops_context *cxt = co->data; + struct mtd_info *mtd = cxt->mtd; + int i; + + if (!oops_in_progress) { + mtdoops_console_sync(); + return; + } + + if (!cxt->ready || !mtd) + return; + + if (cxt->writecount == 0) { + u32 *stamp = cxt->oops_buf; + *stamp = cxt->nextcount; + cxt->writecount = 4; + } + + if ((count + cxt->writecount) > OOPS_PAGE_SIZE) + count = OOPS_PAGE_SIZE - cxt->writecount; + + for (i = 0; i < count; i++, s++) + *((char *)(cxt->oops_buf) + cxt->writecount + i) = *s; + + cxt->writecount = cxt->writecount + count; +} + +static int __init mtdoops_console_setup(struct console *co, char *options) +{ + struct mtdoops_context *cxt = co->data; + + if (cxt->mtd_index != -1) + return -EBUSY; + if (co->index == -1) + return -EINVAL; + + cxt->mtd_index = co->index; + return 0; +} + +static struct mtd_notifier mtdoops_notifier = { + .add = mtdoops_notify_add, + .remove = mtdoops_notify_remove, +}; + +static struct console mtdoops_console = { + .name = "ttyMTD", + .write = mtdoops_console_write, + .setup = mtdoops_console_setup, + .unblank = mtdoops_console_sync, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &oops_cxt, +}; + +static int __init mtdoops_console_init(void) +{ + struct mtdoops_context *cxt = &oops_cxt; + + cxt->mtd_index = -1; + cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); + + if (!cxt->oops_buf) { + printk(KERN_ERR "Failed to allocate oops buffer workspace\n"); + return -ENOMEM; + } + + INIT_WORK(&cxt->work, mtdoops_workfunc); + + register_console(&mtdoops_console); + register_mtd_user(&mtdoops_notifier); + return 0; +} + +static void __exit mtdoops_console_exit(void) +{ + struct mtdoops_context *cxt = &oops_cxt; + + unregister_mtd_user(&mtdoops_notifier); + unregister_console(&mtdoops_console); + vfree(cxt->oops_buf); +} + + +subsys_initcall(mtdoops_console_init); +module_exit(mtdoops_console_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver"); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f1d60b6..d3ec309 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -134,10 +134,10 @@ config MTD_NAND_S3C2410_HWECC config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" - depends on 44x + depends on 4xx && !PPC_MERGE select MTD_NAND_ECC_SMC help - NDFC Nand Flash Controllers are integrated in EP44x SoCs + NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs config MTD_NAND_S3C2410_CLKSTOP bool "S3C2410 NAND IDLE clock stop" @@ -237,7 +237,7 @@ config MTD_NAND_CAFE select REED_SOLOMON select REED_SOLOMON_DEC16 help - Use NAND flash attached to the CAFÉ chip designed for the $100 + Use NAND flash attached to the CAFÉ chip designed for the OLPC laptop. config MTD_NAND_CS553X @@ -280,5 +280,11 @@ config MTD_NAND_PLATFORM devices. You will need to provide platform-specific functions via platform_data. +config MTD_ALAUDA + tristate "MTD driver for Olympus MAUSB-10 and Fijufilm DPC-R1" + depends on MTD_NAND && USB + help + These two (and possibly other) Alauda-based cardreaders for + SmartMedia and xD allow raw flash access. endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index edba1db..7412561 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -27,5 +27,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nan obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o +obj-$(CONFIG_MTD_ALAUDA) += alauda.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c new file mode 100644 index 0000000..2c29854 --- /dev/null +++ b/drivers/mtd/nand/alauda.c @@ -0,0 +1,742 @@ +/* + * MTD driver for Alauda chips + * + * Copyright (C) 2007 Joern Engel + * + * Based on drivers/usb/usb-skeleton.c which is: + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * and on drivers/usb/storage/alauda.c, which is: + * (c) 2005 Daniel Drake + * + * Idea and initial work by Arnd Bergmann + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control commands */ +#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 +#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a +#define ALAUDA_GET_XD_MEDIA_SIG 0x86 + +/* Common prefix */ +#define ALAUDA_BULK_CMD 0x40 + +/* The two ports */ +#define ALAUDA_PORT_XD 0x00 +#define ALAUDA_PORT_SM 0x01 + +/* Bulk commands */ +#define ALAUDA_BULK_READ_PAGE 0x84 +#define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */ +#define ALAUDA_BULK_READ_BLOCK 0x94 +#define ALAUDA_BULK_ERASE_BLOCK 0xa3 +#define ALAUDA_BULK_WRITE_PAGE 0xa4 +#define ALAUDA_BULK_WRITE_BLOCK 0xb4 +#define ALAUDA_BULK_RESET_MEDIA 0xe0 + +/* Address shifting */ +#define PBA_LO(pba) ((pba & 0xF) << 5) +#define PBA_HI(pba) (pba >> 3) +#define PBA_ZONE(pba) (pba >> 11) + +#define TIMEOUT HZ + +static struct usb_device_id alauda_table [] = { + { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ + { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ + { } +}; +MODULE_DEVICE_TABLE(usb, alauda_table); + +struct alauda_card { + u8 id; /* id byte */ + u8 chipshift; /* 1<id; card++) + if (card->id == id) + return card; + return NULL; +} + +static void alauda_delete(struct kref *kref) +{ + struct alauda *al = container_of(kref, struct alauda, kref); + + if (al->mtd) { + del_mtd_device(al->mtd); + kfree(al->mtd); + } + usb_put_dev(al->dev); + kfree(al); +} + +static int alauda_get_media_status(struct alauda *al, void *buf) +{ + int ret; + + mutex_lock(&al->card_mutex); + ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), + ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ); + mutex_unlock(&al->card_mutex); + return ret; +} + +static int alauda_ack_media(struct alauda *al) +{ + int ret; + + mutex_lock(&al->card_mutex); + ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0), + ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ); + mutex_unlock(&al->card_mutex); + return ret; +} + +static int alauda_get_media_signatures(struct alauda *al, void *buf) +{ + int ret; + + mutex_lock(&al->card_mutex); + ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), + ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ); + mutex_unlock(&al->card_mutex); + return ret; +} + +static void alauda_reset(struct alauda *al) +{ + u8 command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0, + 0, 0, 0, 0, al->port + }; + mutex_lock(&al->card_mutex); + usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ); + mutex_unlock(&al->card_mutex); +} + +static void correct_data(void *buf, void *read_ecc, + int *corrected, int *uncorrected) +{ + u8 calc_ecc[3]; + int err; + + nand_calculate_ecc(NULL, buf, calc_ecc); + err = nand_correct_data(NULL, buf, read_ecc, calc_ecc); + if (err) { + if (err > 0) + (*corrected)++; + else + (*uncorrected)++; + } +} + +struct alauda_sg_request { + struct urb *urb[3]; + struct completion comp; +}; + +static void alauda_complete(struct urb *urb) +{ + struct completion *comp = urb->context; + + if (comp) + complete(comp); +} + +static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf, + void *oob) +{ + struct alauda_sg_request sg; + struct alauda *al = mtd->priv; + u32 pba = from >> al->card->blockshift; + u32 page = (from >> al->card->pageshift) & al->pagemask; + u8 command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port + }; + int i, err; + + for (i=0; i<3; i++) + sg.urb[i] = NULL; + + err = -ENOMEM; + for (i=0; i<3; i++) { + sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); + if (!sg.urb[i]) + goto out; + } + init_completion(&sg.comp); + usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16, + alauda_complete, &sg.comp); + + mutex_lock(&al->card_mutex); + for (i=0; i<3; i++) { + err = usb_submit_urb(sg.urb[i], GFP_NOIO); + if (err) + goto cancel; + } + if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { + err = -ETIMEDOUT; +cancel: + for (i=0; i<3; i++) { + usb_kill_urb(sg.urb[i]); + } + } + mutex_unlock(&al->card_mutex); + +out: + usb_free_urb(sg.urb[0]); + usb_free_urb(sg.urb[1]); + usb_free_urb(sg.urb[2]); + return err; +} + +static int alauda_read_page(struct mtd_info *mtd, loff_t from, + void *buf, u8 *oob, int *corrected, int *uncorrected) +{ + int err; + + err = __alauda_read_page(mtd, from, buf, oob); + if (err) + return err; + correct_data(buf, oob+13, corrected, uncorrected); + correct_data(buf+256, oob+8, corrected, uncorrected); + return 0; +} + +static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf, + void *oob) +{ + struct alauda_sg_request sg; + struct alauda *al = mtd->priv; + u32 pba = to >> al->card->blockshift; + u32 page = (to >> al->card->pageshift) & al->pagemask; + u8 command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port + }; + int i, err; + + for (i=0; i<3; i++) + sg.urb[i] = NULL; + + err = -ENOMEM; + for (i=0; i<3; i++) { + sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); + if (!sg.urb[i]) + goto out; + } + init_completion(&sg.comp); + usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16, + alauda_complete, &sg.comp); + + mutex_lock(&al->card_mutex); + for (i=0; i<3; i++) { + err = usb_submit_urb(sg.urb[i], GFP_NOIO); + if (err) + goto cancel; + } + if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { + err = -ETIMEDOUT; +cancel: + for (i=0; i<3; i++) { + usb_kill_urb(sg.urb[i]); + } + } + mutex_unlock(&al->card_mutex); + +out: + usb_free_urb(sg.urb[0]); + usb_free_urb(sg.urb[1]); + usb_free_urb(sg.urb[2]); + return err; +} + +static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs) +{ + struct alauda_sg_request sg; + struct alauda *al = mtd->priv; + u32 pba = ofs >> al->card->blockshift; + u8 command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port + }; + u8 buf[2]; + int i, err; + + for (i=0; i<2; i++) + sg.urb[i] = NULL; + + err = -ENOMEM; + for (i=0; i<2; i++) { + sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); + if (!sg.urb[i]) + goto out; + } + init_completion(&sg.comp); + usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2, + alauda_complete, &sg.comp); + + mutex_lock(&al->card_mutex); + for (i=0; i<2; i++) { + err = usb_submit_urb(sg.urb[i], GFP_NOIO); + if (err) + goto cancel; + } + if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { + err = -ETIMEDOUT; +cancel: + for (i=0; i<2; i++) { + usb_kill_urb(sg.urb[i]); + } + } + mutex_unlock(&al->card_mutex); + +out: + usb_free_urb(sg.urb[0]); + usb_free_urb(sg.urb[1]); + return err; +} + +static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob) +{ + static u8 ignore_buf[512]; /* write only */ + + return __alauda_read_page(mtd, from, ignore_buf, oob); +} + +static int popcount8(u8 c) +{ + int ret = 0; + + for ( ; c; c>>=1) + ret += c & 1; + return ret; +} + +static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) +{ + u8 oob[16]; + int err; + + err = alauda_read_oob(mtd, ofs, oob); + if (err) + return err; + + /* A block is marked bad if two or more bits are zero */ + return popcount8(oob[5]) >= 7 ? 0 : 1; +} + +static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct alauda *al = mtd->priv; + void *bounce_buf; + int err, corrected=0, uncorrected=0; + + bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!bounce_buf) + return -ENOMEM; + + *retlen = len; + while (len) { + u8 oob[16]; + u32 byte = from & al->bytemask; + size_t cplen = min(len, mtd->writesize - byte); + + err = alauda_read_page(mtd, from, bounce_buf, oob, + &corrected, &uncorrected); + if (err) + goto out; + + memcpy(buf, bounce_buf + byte, cplen); + buf += cplen; + from += cplen; + len -= cplen; + } + err = 0; + if (corrected) + err = -EUCLEAN; + if (uncorrected) + err = -EBADMSG; +out: + kfree(bounce_buf); + return err; +} + +static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct alauda *al = mtd->priv; + int err, corrected=0, uncorrected=0; + + if ((from & al->bytemask) || (len & al->bytemask)) + return alauda_bounce_read(mtd, from, len, retlen, buf); + + *retlen = len; + while (len) { + u8 oob[16]; + + err = alauda_read_page(mtd, from, buf, oob, + &corrected, &uncorrected); + if (err) + return err; + + buf += mtd->writesize; + from += mtd->writesize; + len -= mtd->writesize; + } + err = 0; + if (corrected) + err = -EUCLEAN; + if (uncorrected) + err = -EBADMSG; + return err; +} + +static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct alauda *al = mtd->priv; + int err; + + if ((to & al->bytemask) || (len & al->bytemask)) + return -EINVAL; + + *retlen = len; + while (len) { + u32 page = (to >> al->card->pageshift) & al->pagemask; + u8 oob[16] = { 'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + /* don't write to bad blocks */ + if (page == 0) { + err = alauda_isbad(mtd, to); + if (err) { + return -EIO; + } + } + nand_calculate_ecc(mtd, buf, &oob[13]); + nand_calculate_ecc(mtd, buf+256, &oob[8]); + + err = alauda_write_page(mtd, to, (void*)buf, oob); + if (err) + return err; + + buf += mtd->writesize; + to += mtd->writesize; + len -= mtd->writesize; + } + return 0; +} + +static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct alauda *al = mtd->priv; + u32 ofs = instr->addr; + u32 len = instr->len; + int err; + + if ((ofs & al->blockmask) || (len & al->blockmask)) + return -EINVAL; + + while (len) { + /* don't erase bad blocks */ + err = alauda_isbad(mtd, ofs); + if (err > 0) + err = -EIO; + if (err < 0) + return err; + + err = alauda_erase_block(mtd, ofs); + if (err < 0) + return err; + + ofs += mtd->erasesize; + len -= mtd->erasesize; + } + return 0; +} + +static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + int err; + + err = __alauda_erase(mtd, instr); + instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE; + mtd_erase_callback(instr); + return err; +} + +static int alauda_init_media(struct alauda *al) +{ + u8 buf[4], *b0=buf, *b1=buf+1; + struct alauda_card *card; + struct mtd_info *mtd; + int err; + + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) + return -ENOMEM; + + for (;;) { + err = alauda_get_media_status(al, buf); + if (err < 0) + goto error; + if (*b0 & 0x10) + break; + msleep(20); + } + + err = alauda_ack_media(al); + if (err) + goto error; + + msleep(10); + + err = alauda_get_media_status(al, buf); + if (err < 0) + goto error; + + if (*b0 != 0x14) { + /* media not ready */ + err = -EIO; + goto error; + } + err = alauda_get_media_signatures(al, buf); + if (err < 0) + goto error; + + card = get_card(*b1); + if (!card) { + printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1); + err = -EIO; + goto error; + } + printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n", + 1<pageshift, 1<blockshift, + 1<<(card->chipshift-20)); + al->card = card; + al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1; + al->bytemask = (1 << card->pageshift) - 1; + al->blockmask = (1 << card->blockshift) - 1; + + mtd->name = "alauda"; + mtd->size = 1<chipshift; + mtd->erasesize = 1<blockshift; + mtd->writesize = 1<pageshift; + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->read = alauda_read; + mtd->write = alauda_write; + mtd->erase = alauda_erase; + mtd->block_isbad = alauda_isbad; + mtd->priv = al; + mtd->owner = THIS_MODULE; + + err = add_mtd_device(mtd); + if (err) { + err = -ENFILE; + goto error; + } + + al->mtd = mtd; + alauda_reset(al); /* no clue whether this is necessary */ + return 0; +error: + kfree(mtd); + return err; +} + +static int alauda_check_media(struct alauda *al) +{ + u8 buf[2], *b0 = buf, *b1 = buf+1; + int err; + + err = alauda_get_media_status(al, buf); + if (err < 0) + return err; + + if ((*b1 & 0x01) == 0) { + /* door open */ + return -EIO; + } + if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) { + /* no media ? */ + return -EIO; + } + if (*b0 & 0x08) { + /* media change ? */ + return alauda_init_media(al); + } + return 0; +} + +static int alauda_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct alauda *al; + struct usb_host_interface *iface; + struct usb_endpoint_descriptor *ep, + *ep_in=NULL, *ep_out=NULL, *ep_wr=NULL; + int i, err = -ENOMEM; + + al = kzalloc(2*sizeof(*al), GFP_KERNEL); + if (!al) + goto error; + + kref_init(&al->kref); + usb_set_intfdata(interface, al); + + al->dev = usb_get_dev(interface_to_usbdev(interface)); + al->interface = interface; + + iface = interface->cur_altsetting; + for (i = 0; i < iface->desc.bNumEndpoints; ++i) { + ep = &iface->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(ep)) { + ep_in = ep; + } else if (usb_endpoint_is_bulk_out(ep)) { + if (i==0) + ep_wr = ep; + else + ep_out = ep; + } + } + err = -EIO; + if (!ep_wr || !ep_in || !ep_out) + goto error; + + al->write_out = usb_sndbulkpipe(al->dev, + ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + al->bulk_in = usb_rcvbulkpipe(al->dev, + ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + al->bulk_out = usb_sndbulkpipe(al->dev, + ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + + /* second device is identical up to now */ + memcpy(al+1, al, sizeof(*al)); + + mutex_init(&al[0].card_mutex); + mutex_init(&al[1].card_mutex); + + al[0].port = ALAUDA_PORT_XD; + al[1].port = ALAUDA_PORT_SM; + + info("alauda probed"); + alauda_check_media(al); + alauda_check_media(al+1); + + return 0; + +error: + if (al) + kref_put(&al->kref, alauda_delete); + return err; +} + +static void alauda_disconnect(struct usb_interface *interface) +{ + struct alauda *al; + + al = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* FIXME: prevent more I/O from starting */ + + /* decrement our usage count */ + if (al) + kref_put(&al->kref, alauda_delete); + + info("alauda gone"); +} + +static struct usb_driver alauda_driver = { + .name = "alauda", + .probe = alauda_probe, + .disconnect = alauda_disconnect, + .id_table = alauda_table, +}; + +static int __init alauda_init(void) +{ + return usb_register(&alauda_driver); +} + +static void __exit alauda_exit(void) +{ + usb_deregister(&alauda_driver); +} + +module_init(alauda_init); +module_exit(alauda_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e96259f..ab9f5c5 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -56,8 +56,6 @@ #else /* CONFIG_MTD_DOCPROBE_HIGH */ #endif /* CONFIG_MTD_DOCPROBE_HIGH */ #elif defined(__PPC__) 0xe4000000, -#elif defined(CONFIG_MOMENCO_OCELOT_G) - 0xff000000, #else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index 7e9afc4..bed8729 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c @@ -27,7 +27,6 @@ #include #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 24ac677..d569121 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -7,7 +7,7 @@ * Basic support for AG-AND chips is provided. * * Additional technical information is available on - * http://www.linux-mtd.infradead.org/tech/nand.html + * http://www.linux-mtd.infradead.org/doc/nand.html * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * 2002-2006 Thomas Gleixner (tglx@linutronix.de) diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 2fc674a..a3e3ab0 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -141,6 +141,7 @@ struct nand_manufacturers nand_manuf_ids {NAND_MFR_STMICRO, "ST Micro"}, {NAND_MFR_HYNIX, "Hynix"}, {NAND_MFR_MICRON, "Micron"}, + {NAND_MFR_AMD, "AMD"}, {0x0, "Unknown"} }; diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index fd7a8d5..1c0e89f 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -24,7 +24,11 @@ #include #include #include +#ifdef CONFIG_40x +#include +#else #include +#endif struct ndfc_nand_mtd { struct mtd_info mtd; @@ -230,7 +234,11 @@ static int ndfc_nand_probe(struct platfo struct ndfc_controller *ndfc = &ndfc_ctrl; unsigned long long phys = settings->ndfc_erpn | res->start; +#ifndef CONFIG_PHYS_64BIT + ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1); +#else ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); +#endif if (!ndfc->ndfcbase) { printk(KERN_ERR "NDFC: ioremap failed\n"); return -EIO; diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index c257d39..cb41cbc 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -40,4 +40,27 @@ config MTD_ONENAND_OTP OTP block is fully-guaranteed to be a valid block. +config MTD_ONENAND_2X_PROGRAM + bool "OneNAND 2X program support" + help + The 2X Program is an extension of Program Operation. + Since the device is equipped with two DataRAMs, and two-plane NAND + Flash memory array, these two component enables simultaneous program + of 4KiB. Plane1 has only even blocks such as block0, block2, block4 + while Plane2 has only odd blocks such as block1, block3, block5. + So MTD regards it as 4KiB page size and 256KiB block size + + Now the following chips support it. (KFXXX16Q2M) + Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M, + Mux: KFM2G16Q2M, KFN4G16Q2M, + + And more recent chips + +config MTD_ONENAND_SIM + tristate "OneNAND simulator support" + depends on MTD_PARTITIONS + help + The simulator may simulate various OneNAND flash chips for the + OneNAND MTD layer. + endif # MTD_ONENAND diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 269cfe4..4d2eacf 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -8,4 +8,7 @@ obj-$(CONFIG_MTD_ONENAND) += onenand.o # Board specific. obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o +# Simulator +obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o + onenand-objs = onenand_base.o onenand_bbt.o diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0537fac..45bc143 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -206,6 +206,15 @@ static int onenand_command(struct mtd_in default: block = (int) (addr >> this->erase_shift); page = (int) (addr >> this->page_shift); + + if (ONENAND_IS_2PLANE(this)) { + /* Make the even block number */ + block &= ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page >>= 1; + } page &= this->page_mask; break; } @@ -216,8 +225,12 @@ static int onenand_command(struct mtd_in value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - /* Switch to the next data buffer */ - ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_2PLANE(this)) + /* It is always BufferRAM0 */ + ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this); return 0; } @@ -247,6 +260,8 @@ static int onenand_command(struct mtd_in break; default: + if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) + cmd = ONENAND_CMD_2X_PROG; dataram = ONENAND_CURRENT_BUFFERRAM(this); break; } @@ -445,8 +460,9 @@ static inline int onenand_bufferram_offs struct onenand_chip *this = mtd->priv; if (ONENAND_CURRENT_BUFFERRAM(this)) { + /* Note: the 'this->writesize' is a real page size */ if (area == ONENAND_DATARAM) - return mtd->writesize; + return this->writesize; if (area == ONENAND_SPARERAM) return mtd->oobsize; } @@ -572,6 +588,30 @@ static int onenand_write_bufferram(struc } /** + * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode + * @param mtd MTD data structure + * @param addr address to check + * @return blockpage address + * + * Get blockpage address at 2x program mode + */ +static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) +{ + struct onenand_chip *this = mtd->priv; + int blockpage, block, page; + + /* Calculate the even block number */ + block = (int) (addr >> this->erase_shift) & ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; + blockpage = (block << 7) | page; + + return blockpage; +} + +/** * onenand_check_bufferram - [GENERIC] Check BufferRAM information * @param mtd MTD data structure * @param addr address to check @@ -585,7 +625,10 @@ static int onenand_check_bufferram(struc int blockpage, found = 0; unsigned int i; - blockpage = (int) (addr >> this->page_shift); + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); /* Is there valid data? */ i = ONENAND_CURRENT_BUFFERRAM(this); @@ -625,7 +668,10 @@ static void onenand_update_bufferram(str int blockpage; unsigned int i; - blockpage = (int) (addr >> this->page_shift); + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); /* Invalidate another BufferRAM */ i = ONENAND_NEXT_BUFFERRAM(this); @@ -734,6 +780,7 @@ static int onenand_read(struct mtd_info int read = 0, column; int thislen; int ret = 0, boundary = 0; + int writesize = this->writesize; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); @@ -754,22 +801,22 @@ static int onenand_read(struct mtd_info /* Do first load to bufferRAM */ if (read < len) { if (!onenand_check_bufferram(mtd, from)) { - this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); } } - thislen = min_t(int, mtd->writesize, len - read); - column = from & (mtd->writesize - 1); - if (column + thislen > mtd->writesize) - thislen = mtd->writesize - column; + thislen = min_t(int, writesize, len - read); + column = from & (writesize - 1); + if (column + thislen > writesize) + thislen = writesize - column; while (!ret) { /* If there is more to load then start next load */ from += thislen; if (read + thislen < len) { - this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, from, writesize); /* * Chip boundary handling in DDP * Now we issued chip 1 read and pointed chip 1 @@ -794,7 +841,7 @@ static int onenand_read(struct mtd_info this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; - thislen = min_t(int, mtd->writesize, len - read); + thislen = min_t(int, writesize, len - read); column = 0; cond_resched(); /* Now wait for load */ @@ -874,17 +921,22 @@ static int onenand_transfer_auto_oob(str * * OneNAND read out-of-band data from the spare area */ -static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, mtd_oob_mode_t mode) +static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column, oobsize; + size_t len = ops->ooblen; + mtd_oob_mode_t mode = ops->mode; + u_char *buf = ops->oobbuf; int ret = 0; + from += ops->ooboffs; + DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Initialize return length value */ - *retlen = 0; + ops->oobretlen = 0; if (mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; @@ -950,7 +1002,7 @@ static int onenand_do_read_oob(struct mt /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); - *retlen = read; + ops->oobretlen = read; return ret; } @@ -972,8 +1024,7 @@ static int onenand_read_oob(struct mtd_i default: return -EINVAL; } - return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf, ops->mode); + return onenand_do_read_oob(mtd, from, ops); } /** @@ -1079,7 +1130,7 @@ int onenand_bbt_read_oob(struct mtd_info /* Read more? */ if (read < len) { /* Update Page size */ - from += mtd->writesize; + from += this->writesize; column = 0; } } @@ -1135,12 +1186,12 @@ static int onenand_verify(struct mtd_inf int thislen, column; while (len != 0) { - thislen = min_t(int, mtd->writesize, len); - column = addr & (mtd->writesize - 1); - if (column + thislen > mtd->writesize) - thislen = mtd->writesize - column; + thislen = min_t(int, this->writesize, len); + column = addr & (this->writesize - 1); + if (column + thislen > this->writesize) + thislen = this->writesize - column; - this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); onenand_update_bufferram(mtd, addr, 0); @@ -1236,6 +1287,10 @@ static int onenand_write(struct mtd_info /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, to, !ret && !subpage); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); + } if (ret) { printk(KERN_ERR "onenand_write: write filaed %d\n", ret); @@ -1319,18 +1374,23 @@ static int onenand_fill_auto_oob(struct * * OneNAND write out-of-band */ -static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) +static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; int written = 0; u_char *oobbuf; + size_t len = ops->ooblen; + const u_char *buf = ops->oobbuf; + mtd_oob_mode_t mode = ops->mode; + + to += ops->ooboffs; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ - *retlen = 0; + ops->oobretlen = 0; if (mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; @@ -1384,6 +1444,10 @@ static int onenand_do_write_oob(struct m this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, 0); + } ret = this->wait(mtd, FL_WRITING); if (ret) { @@ -1409,7 +1473,7 @@ static int onenand_do_write_oob(struct m /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); - *retlen = written; + ops->oobretlen = written; return ret; } @@ -1432,8 +1496,7 @@ static int onenand_write_oob(struct mtd_ default: return -EINVAL; } - return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf, ops->mode); + return onenand_do_write_oob(mtd, to, ops); } /** @@ -1591,7 +1654,12 @@ static int onenand_default_block_markbad struct onenand_chip *this = mtd->priv; struct bbm_info *bbm = this->bbm; u_char buf[2] = {0, 0}; - size_t retlen; + struct mtd_oob_ops ops = { + .mode = MTD_OOB_PLACE, + .ooblen = 2, + .oobbuf = buf, + .ooboffs = 0, + }; int block; /* Get block number */ @@ -1601,7 +1669,7 @@ static int onenand_default_block_markbad /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); - return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); + return onenand_do_write_oob(mtd, ofs, &ops); } /** @@ -1890,13 +1958,21 @@ static int do_otp_lock(struct mtd_info * size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; + struct mtd_oob_ops ops = { + .mode = MTD_OOB_PLACE, + .ooblen = len, + .oobbuf = buf, + .ooboffs = 0, + }; int ret; /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); + ret = onenand_do_write_oob(mtd, from, &ops); + + *retlen = ops.oobretlen; /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2107,6 +2183,7 @@ #endif /* CONFIG_MTD_ONENAND_OTP */ * * Check and set OneNAND features * - lock scheme + * - two plane */ static void onenand_check_features(struct mtd_info *mtd) { @@ -2118,19 +2195,35 @@ static void onenand_check_features(struc process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; /* Lock scheme */ - if (density >= ONENAND_DEVICE_DENSITY_1Gb) { + switch (density) { + case ONENAND_DEVICE_DENSITY_4Gb: + this->options |= ONENAND_HAS_2PLANE; + + case ONENAND_DEVICE_DENSITY_2Gb: + /* 2Gb DDP don't have 2 plane */ + if (!ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + this->options |= ONENAND_HAS_UNLOCK_ALL; + + case ONENAND_DEVICE_DENSITY_1Gb: /* A-Die has all block unlock */ - if (process) { - printk(KERN_DEBUG "Chip support all block unlock\n"); + if (process) this->options |= ONENAND_HAS_UNLOCK_ALL; - } - } else { - /* Some OneNAND has continues lock scheme */ - if (!process) { - printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); + break; + + default: + /* Some OneNAND has continuous lock scheme */ + if (!process) this->options |= ONENAND_HAS_CONT_LOCK; - } + break; } + + if (this->options & ONENAND_HAS_CONT_LOCK) + printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); + if (this->options & ONENAND_HAS_UNLOCK_ALL) + printk(KERN_DEBUG "Chip support all block unlock\n"); + if (this->options & ONENAND_HAS_2PLANE) + printk(KERN_DEBUG "Chip has 2 plane\n"); } /** @@ -2257,6 +2350,8 @@ static int onenand_probe(struct mtd_info this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; + /* It's real page size */ + this->writesize = mtd->writesize; /* REVIST: Multichip handling */ @@ -2265,6 +2360,17 @@ static int onenand_probe(struct mtd_info /* Check OneNAND features */ onenand_check_features(mtd); + /* + * We emulate the 4KiB page and 256KiB erase block size + * But oobsize is still 64 bytes. + * It is only valid if you turn on 2X program support, + * Otherwise it will be ignored by compiler. + */ + if (ONENAND_IS_2PLANE(this)) { + mtd->writesize <<= 1; + mtd->erasesize <<= 1; + } + return 0; } diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c new file mode 100644 index 0000000..0d89ad5 --- /dev/null +++ b/drivers/mtd/onenand/onenand_sim.c @@ -0,0 +1,495 @@ +/* + * linux/drivers/mtd/onenand/onenand_sim.c + * + * The OneNAND simulator + * + * Copyright © 2005-2007 Samsung Electronics + * Kyungmin Park + * + * 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 + +#ifndef CONFIG_ONENAND_SIM_MANUFACTURER +#define CONFIG_ONENAND_SIM_MANUFACTURER 0xec +#endif +#ifndef CONFIG_ONENAND_SIM_DEVICE_ID +#define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 +#endif +#ifndef CONFIG_ONENAND_SIM_VERSION_ID +#define CONFIG_ONENAND_SIM_VERSION_ID 0x1e +#endif + +static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER; +static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID; +static int version_id = CONFIG_ONENAND_SIM_VERSION_ID; + +struct onenand_flash { + void __iomem *base; + void __iomem *data; +}; + +#define ONENAND_CORE(flash) (flash->data) +#define ONENAND_CORE_SPARE(flash, this, offset) \ + ((flash->data) + (this->chipsize) + (offset >> 5)) + +#define ONENAND_MAIN_AREA(this, offset) \ + (this->base + ONENAND_DATARAM + offset) + +#define ONENAND_SPARE_AREA(this, offset) \ + (this->base + ONENAND_SPARERAM + offset) + +#define ONENAND_GET_WP_STATUS(this) \ + (readw(this->base + ONENAND_REG_WP_STATUS)) + +#define ONENAND_SET_WP_STATUS(v, this) \ + (writew(v, this->base + ONENAND_REG_WP_STATUS)) + +/* It has all 0xff chars */ +#define MAX_ONENAND_PAGESIZE (2048 + 64) +static unsigned char *ffchars; + +static struct mtd_partition os_partitions[] = { + { + .name = "OneNAND simulator partition", + .offset = 0, + .size = MTDPART_SIZ_FULL, + }, +}; + +/* + * OneNAND simulator mtd + */ +struct onenand_info { + struct mtd_info mtd; + struct mtd_partition *parts; + struct onenand_chip onenand; + struct onenand_flash flash; +}; + +static struct onenand_info *info; + +#define DPRINTK(format, args...) \ +do { \ + printk(KERN_DEBUG "%s[%d]: " format "\n", __func__, \ + __LINE__, ##args); \ +} while (0) + +/** + * onenand_lock_handle - Handle Lock scheme + * @param this OneNAND device structure + * @param cmd The command to be sent + * + * Send lock command to OneNAND device. + * The lock scheme is depends on chip type. + */ +static void onenand_lock_handle(struct onenand_chip *this, int cmd) +{ + int block_lock_scheme; + int status; + + status = ONENAND_GET_WP_STATUS(this); + block_lock_scheme = !(this->options & ONENAND_HAS_CONT_LOCK); + + switch (cmd) { + case ONENAND_CMD_UNLOCK: + if (block_lock_scheme) + ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); + else + ONENAND_SET_WP_STATUS(status | ONENAND_WP_US, this); + break; + + case ONENAND_CMD_LOCK: + if (block_lock_scheme) + ONENAND_SET_WP_STATUS(ONENAND_WP_LS, this); + else + ONENAND_SET_WP_STATUS(status | ONENAND_WP_LS, this); + break; + + case ONENAND_CMD_LOCK_TIGHT: + if (block_lock_scheme) + ONENAND_SET_WP_STATUS(ONENAND_WP_LTS, this); + else + ONENAND_SET_WP_STATUS(status | ONENAND_WP_LTS, this); + break; + + default: + break; + } +} + +/** + * onenand_bootram_handle - Handle BootRAM area + * @param this OneNAND device structure + * @param cmd The command to be sent + * + * Emulate BootRAM area. It is possible to do basic operation using BootRAM. + */ +static void onenand_bootram_handle(struct onenand_chip *this, int cmd) +{ + switch (cmd) { + case ONENAND_CMD_READID: + writew(manuf_id, this->base); + writew(device_id, this->base + 2); + writew(version_id, this->base + 4); + break; + + default: + /* REVIST: Handle other commands */ + break; + } +} + +/** + * onenand_update_interrupt - Set interrupt register + * @param this OneNAND device structure + * @param cmd The command to be sent + * + * Update interrupt register. The status is depends on command. + */ +static void onenand_update_interrupt(struct onenand_chip *this, int cmd) +{ + int interrupt = ONENAND_INT_MASTER; + + switch (cmd) { + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + interrupt |= ONENAND_INT_READ; + break; + + case ONENAND_CMD_PROG: + case ONENAND_CMD_PROGOOB: + interrupt |= ONENAND_INT_WRITE; + break; + + case ONENAND_CMD_ERASE: + interrupt |= ONENAND_INT_ERASE; + break; + + case ONENAND_CMD_RESET: + interrupt |= ONENAND_INT_RESET; + break; + + default: + break; + } + + writew(interrupt, this->base + ONENAND_REG_INTERRUPT); +} + +/** + * onenand_check_overwrite - Check over-write if happend + * @param dest The destination pointer + * @param src The source pointer + * @param count The length to be check + * @return 0 on same, otherwise 1 + * + * Compare the source with destination + */ +static int onenand_check_overwrite(void *dest, void *src, size_t count) +{ + unsigned int *s = (unsigned int *) src; + unsigned int *d = (unsigned int *) dest; + int i; + + count >>= 2; + for (i = 0; i < count; i++) + if ((*s++ ^ *d++) != 0) + return 1; + + return 0; +} + +/** + * onenand_data_handle - Handle OneNAND Core and DataRAM + * @param this OneNAND device structure + * @param cmd The command to be sent + * @param dataram Which dataram used + * @param offset The offset to OneNAND Core + * + * Copy data from OneNAND Core to DataRAM (read) + * Copy data from DataRAM to OneNAND Core (write) + * Erase the OneNAND Core (erase) + */ +static void onenand_data_handle(struct onenand_chip *this, int cmd, + int dataram, unsigned int offset) +{ + struct mtd_info *mtd = &info->mtd; + struct onenand_flash *flash = this->priv; + int main_offset, spare_offset; + void __iomem *src; + void __iomem *dest; + unsigned int i; + + if (dataram) { + main_offset = mtd->writesize; + spare_offset = mtd->oobsize; + } else { + main_offset = 0; + spare_offset = 0; + } + + switch (cmd) { + case ONENAND_CMD_READ: + src = ONENAND_CORE(flash) + offset; + dest = ONENAND_MAIN_AREA(this, main_offset); + memcpy(dest, src, mtd->writesize); + /* Fall through */ + + case ONENAND_CMD_READOOB: + src = ONENAND_CORE_SPARE(flash, this, offset); + dest = ONENAND_SPARE_AREA(this, spare_offset); + memcpy(dest, src, mtd->oobsize); + break; + + case ONENAND_CMD_PROG: + src = ONENAND_MAIN_AREA(this, main_offset); + dest = ONENAND_CORE(flash) + offset; + /* To handle partial write */ + for (i = 0; i < (1 << mtd->subpage_sft); i++) { + int off = i * this->subpagesize; + if (!memcmp(src + off, ffchars, this->subpagesize)) + continue; + if (memcmp(dest + off, ffchars, this->subpagesize) && + onenand_check_overwrite(dest + off, src + off, this->subpagesize)) + printk(KERN_ERR "over-write happend at 0x%08x\n", offset); + memcpy(dest + off, src + off, this->subpagesize); + } + /* Fall through */ + + case ONENAND_CMD_PROGOOB: + src = ONENAND_SPARE_AREA(this, spare_offset); + /* Check all data is 0xff chars */ + if (!memcmp(src, ffchars, mtd->oobsize)) + break; + + dest = ONENAND_CORE_SPARE(flash, this, offset); + if (memcmp(dest, ffchars, mtd->oobsize) && + onenand_check_overwrite(dest, src, mtd->oobsize)) + printk(KERN_ERR "OOB: over-write happend at 0x%08x\n", + offset); + memcpy(dest, src, mtd->oobsize); + break; + + case ONENAND_CMD_ERASE: + memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize); + memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, + (mtd->erasesize >> 5)); + break; + + default: + break; + } +} + +/** + * onenand_command_handle - Handle command + * @param this OneNAND device structure + * @param cmd The command to be sent + * + * Emulate OneNAND command. + */ +static void onenand_command_handle(struct onenand_chip *this, int cmd) +{ + unsigned long offset = 0; + int block = -1, page = -1, bufferram = -1; + int dataram = 0; + + switch (cmd) { + case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_LOCK: + case ONENAND_CMD_LOCK_TIGHT: + case ONENAND_CMD_UNLOCK_ALL: + onenand_lock_handle(this, cmd); + break; + + case ONENAND_CMD_BUFFERRAM: + /* Do nothing */ + return; + + default: + block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1); + if (block & (1 << ONENAND_DDP_SHIFT)) { + block &= ~(1 << ONENAND_DDP_SHIFT); + /* The half of chip block */ + block += this->chipsize >> (this->erase_shift + 1); + } + if (cmd == ONENAND_CMD_ERASE) + break; + + page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8); + page = (page >> ONENAND_FPA_SHIFT); + bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER); + bufferram >>= ONENAND_BSA_SHIFT; + bufferram &= ONENAND_BSA_DATARAM1; + dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0; + break; + } + + if (block != -1) + offset += block << this->erase_shift; + + if (page != -1) + offset += page << this->page_shift; + + onenand_data_handle(this, cmd, dataram, offset); + + onenand_update_interrupt(this, cmd); +} + +/** + * onenand_writew - [OneNAND Interface] Emulate write operation + * @param value value to write + * @param addr address to write + * + * Write OneNAND register with value + */ +static void onenand_writew(unsigned short value, void __iomem * addr) +{ + struct onenand_chip *this = info->mtd.priv; + + /* BootRAM handling */ + if (addr < this->base + ONENAND_DATARAM) { + onenand_bootram_handle(this, value); + return; + } + /* Command handling */ + if (addr == this->base + ONENAND_REG_COMMAND) + onenand_command_handle(this, value); + + writew(value, addr); +} + +/** + * flash_init - Initialize OneNAND simulator + * @param flash OneNAND simulaotr data strucutres + * + * Initialize OneNAND simulator. + */ +static int __init flash_init(struct onenand_flash *flash) +{ + int density, size; + int buffer_size; + + flash->base = kzalloc(131072, GFP_KERNEL); + if (!flash->base) { + printk(KERN_ERR "Unable to allocate base address.\n"); + return -ENOMEM; + } + + density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + size = ((16 << 20) << density); + + ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); + if (!ONENAND_CORE(flash)) { + printk(KERN_ERR "Unable to allocate nand core address.\n"); + kfree(flash->base); + return -ENOMEM; + } + + memset(ONENAND_CORE(flash), 0xff, size + (size >> 5)); + + /* Setup registers */ + writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); + writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); + writew(version_id, flash->base + ONENAND_REG_VERSION_ID); + + if (density < 2) + buffer_size = 0x0400; /* 1KiB page */ + else + buffer_size = 0x0800; /* 2KiB page */ + writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE); + + return 0; +} + +/** + * flash_exit - Clean up OneNAND simulator + * @param flash OneNAND simulaotr data strucutres + * + * Clean up OneNAND simulator. + */ +static void flash_exit(struct onenand_flash *flash) +{ + vfree(ONENAND_CORE(flash)); + kfree(flash->base); + kfree(flash); +} + +static int __init onenand_sim_init(void) +{ + /* Allocate all 0xff chars pointer */ + ffchars = kmalloc(MAX_ONENAND_PAGESIZE, GFP_KERNEL); + if (!ffchars) { + printk(KERN_ERR "Unable to allocate ff chars.\n"); + return -ENOMEM; + } + memset(ffchars, 0xff, MAX_ONENAND_PAGESIZE); + + /* Allocate OneNAND simulator mtd pointer */ + info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "Unable to allocate core structures.\n"); + kfree(ffchars); + return -ENOMEM; + } + + /* Override write_word function */ + info->onenand.write_word = onenand_writew; + + if (flash_init(&info->flash)) { + printk(KERN_ERR "Unable to allocat flash.\n"); + kfree(ffchars); + kfree(info); + return -ENOMEM; + } + + info->parts = os_partitions; + + info->onenand.base = info->flash.base; + info->onenand.priv = &info->flash; + + info->mtd.name = "OneNAND simulator"; + info->mtd.priv = &info->onenand; + info->mtd.owner = THIS_MODULE; + + if (onenand_scan(&info->mtd, 1)) { + flash_exit(&info->flash); + kfree(ffchars); + kfree(info); + return -ENXIO; + } + + add_mtd_partitions(&info->mtd, info->parts, ARRAY_SIZE(os_partitions)); + + return 0; +} + +static void __exit onenand_sim_exit(void) +{ + struct onenand_chip *this = info->mtd.priv; + struct onenand_flash *flash = this->priv; + + onenand_release(&info->mtd); + flash_exit(flash); + kfree(ffchars); + kfree(info); +} + +module_init(onenand_sim_init); +module_exit(onenand_sim_exit); + +MODULE_AUTHOR("Kyungmin Park "); +MODULE_DESCRIPTION("The OneNAND flash simulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 006c03a..823fba4 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -779,10 +779,8 @@ static void rfd_ftl_add_mtd(struct mtd_b else { if (!mtd->erasesize) { printk(KERN_WARNING PREFIX "please provide block_size"); - kfree(part); - return; - } - else + goto out; + } else part->block_size = mtd->erasesize; } @@ -804,7 +802,7 @@ static void rfd_ftl_add_mtd(struct mtd_b if (!add_mtd_blktrans_dev((void*)part)) return; } - +out: kfree(part); } diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 94ee549..29c41ee 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -1314,11 +1314,10 @@ static int paranoid_check_si(const struc * Make sure that all the physical eraseblocks are in one of the lists * or trees. */ - buf = kmalloc(ubi->peb_count, GFP_KERNEL); + buf = kzalloc(ubi->peb_count, GFP_KERNEL); if (!buf) return -ENOMEM; - memset(buf, 1, ubi->peb_count); for (pnum = 0; pnum < ubi->peb_count; pnum++) { err = ubi_io_is_bad(ubi, pnum); if (err < 0) { @@ -1326,28 +1325,28 @@ static int paranoid_check_si(const struc return err; } else if (err) - buf[pnum] = 0; + buf[pnum] = 1; } ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->free, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->corr, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->erase, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->alien, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; err = 0; for (pnum = 0; pnum < ubi->peb_count; pnum++) - if (buf[pnum]) { + if (!buf[pnum]) { ubi_err("PEB %d is not referred", pnum); err = 1; } diff --git a/fs/Kconfig b/fs/Kconfig index 58a0650..84fb842 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1228,6 +1228,14 @@ config JFFS2_FS_WRITEBUFFER - NOR flash with transparent ECC - DataFlash +config JFFS2_FS_WBUF_VERIFY + bool "Verify JFFS2 write-buffer reads" + depends on JFFS2_FS_WRITEBUFFER + default n + help + This causes JFFS2 to read back every page written through the + write-buffer, and check for errors. + config JFFS2_SUMMARY bool "JFFS2 summary support (EXPERIMENTAL)" depends on JFFS2_FS && EXPERIMENTAL @@ -1298,52 +1306,71 @@ config JFFS2_ZLIB select ZLIB_DEFLATE depends on JFFS2_FS default y - help - Zlib is designed to be a free, general-purpose, legally unencumbered, - lossless data-compression library for use on virtually any computer - hardware and operating system. See for - further information. + help + Zlib is designed to be a free, general-purpose, legally unencumbered, + lossless data-compression library for use on virtually any computer + hardware and operating system. See for + further information. - Say 'Y' if unsure. + Say 'Y' if unsure. + +config JFFS2_LZO + bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS + select LZO_COMPRESS + select LZO_DECOMPRESS + depends on JFFS2_FS + default n + help + minilzo-based compression. Generally works better than Zlib. + + This feature was added in July, 2007. Say 'N' if you need + compatibility with older bootloaders or kernels. config JFFS2_RTIME bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS depends on JFFS2_FS default y - help - Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. + help + Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. config JFFS2_RUBIN bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS depends on JFFS2_FS default n - help - RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. + help + RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. choice - prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS - default JFFS2_CMODE_PRIORITY - depends on JFFS2_FS - help - You can set here the default compression mode of JFFS2 from - the available compression modes. Don't touch if unsure. + prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS + default JFFS2_CMODE_PRIORITY + depends on JFFS2_FS + help + You can set here the default compression mode of JFFS2 from + the available compression modes. Don't touch if unsure. config JFFS2_CMODE_NONE - bool "no compression" - help - Uses no compression. + bool "no compression" + help + Uses no compression. config JFFS2_CMODE_PRIORITY - bool "priority" - help - Tries the compressors in a predefined order and chooses the first - successful one. + bool "priority" + help + Tries the compressors in a predefined order and chooses the first + successful one. config JFFS2_CMODE_SIZE - bool "size (EXPERIMENTAL)" - help - Tries all compressors and chooses the one which has the smallest - result. + bool "size (EXPERIMENTAL)" + help + Tries all compressors and chooses the one which has the smallest + result. + +config JFFS2_CMODE_FAVOURLZO + bool "Favour LZO" + help + Tries all compressors and chooses the one which has the smallest + result but gives some preference to LZO (which has faster + decompression) at the expense of size. endchoice diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile index c32b241..60e5d49 100644 --- a/fs/jffs2/Makefile +++ b/fs/jffs2/Makefile @@ -17,4 +17,5 @@ jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += ac jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o +jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 65b3a1b..8ec9323 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -176,7 +176,7 @@ static void jffs2_iset_acl(struct inode spin_unlock(&inode->i_lock); } -static struct posix_acl *jffs2_get_acl(struct inode *inode, int type) +struct posix_acl *jffs2_get_acl(struct inode *inode, int type) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct posix_acl *acl; @@ -247,8 +247,13 @@ static int jffs2_set_acl(struct inode *i if (rc < 0) return rc; if (inode->i_mode != mode) { - inode->i_mode = mode; - jffs2_dirty_inode(inode); + struct iattr attr; + + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode; + rc = jffs2_do_setattr(inode, &attr); + if (rc < 0) + return rc; } if (rc == 0) acl = NULL; @@ -307,22 +312,16 @@ int jffs2_permission(struct inode *inode return generic_permission(inode, mask, jffs2_check_acl); } -int jffs2_init_acl(struct inode *inode, struct inode *dir) +int jffs2_init_acl(struct inode *inode, struct posix_acl *acl) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); - struct posix_acl *acl = NULL, *clone; + struct posix_acl *clone; mode_t mode; int rc = 0; f->i_acl_access = JFFS2_ACL_NOT_CACHED; f->i_acl_default = JFFS2_ACL_NOT_CACHED; - if (!S_ISLNK(inode->i_mode)) { - acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT); - if (IS_ERR(acl)) - return PTR_ERR(acl); - if (!acl) - inode->i_mode &= ~current->fs->umask; - } + if (acl) { if (S_ISDIR(inode->i_mode)) { rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl); diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index c84378c..90a2dbf 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h @@ -28,9 +28,10 @@ #ifdef CONFIG_JFFS2_FS_POSIX_ACL #define JFFS2_ACL_NOT_CACHED ((void *)-1) +extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type); extern int jffs2_permission(struct inode *, int, struct nameidata *); extern int jffs2_acl_chmod(struct inode *); -extern int jffs2_init_acl(struct inode *, struct inode *); +extern int jffs2_init_acl(struct inode *, struct posix_acl *); extern void jffs2_clear_acl(struct jffs2_inode_info *); extern struct xattr_handler jffs2_acl_access_xattr_handler; @@ -38,6 +39,7 @@ extern struct xattr_handler jffs2_acl_de #else +#define jffs2_get_acl(inode, type) (NULL) #define jffs2_permission NULL #define jffs2_acl_chmod(inode) (0) #define jffs2_init_acl(inode,dir) (0) diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 504643f..d568ae8 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -23,8 +23,8 @@ static int jffs2_garbage_collect_thread( void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { spin_lock(&c->erase_completion_lock); - if (c->gc_task && jffs2_thread_should_wake(c)) - send_sig(SIGHUP, c->gc_task, 1); + if (c->gc_task && jffs2_thread_should_wake(c)) + send_sig(SIGHUP, c->gc_task, 1); spin_unlock(&c->erase_completion_lock); } diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index 485d065..86739ee 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c @@ -5,7 +5,7 @@ * Created by Arjan van de Ven * * Copyright © 2004 Ferenc Havasi , - * University of Szeged, Hungary + * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * @@ -24,6 +24,34 @@ static int jffs2_compression_mode = JFFS /* Statistics for blocks stored without compression */ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; + +/* + * Return 1 to use this compression + */ +static int jffs2_is_best_compression(struct jffs2_compressor *this, + struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) +{ + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_SIZE: + if (bestsize > size) + return 1; + return 0; + case JFFS2_COMPR_MODE_FAVOURLZO: + if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) + return 1; + if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) + return 1; + if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) + return 1; + if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) + return 1; + + return 0; + } + /* Shouldn't happen */ + return 0; +} + /* jffs2_compress: * @data: Pointer to uncompressed data * @cdata: Pointer to returned pointer to buffer for compressed data @@ -43,121 +71,124 @@ static uint32_t none_stat_compr_blocks=0 * *datalen accordingly to show the amount of data which were compressed. */ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - unsigned char *data_in, unsigned char **cpage_out, - uint32_t *datalen, uint32_t *cdatalen) + unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen) { int ret = JFFS2_COMPR_NONE; - int compr_ret; - struct jffs2_compressor *this, *best=NULL; - unsigned char *output_buf = NULL, *tmp_buf; - uint32_t orig_slen, orig_dlen; - uint32_t best_slen=0, best_dlen=0; + int compr_ret; + struct jffs2_compressor *this, *best=NULL; + unsigned char *output_buf = NULL, *tmp_buf; + uint32_t orig_slen, orig_dlen; + uint32_t best_slen=0, best_dlen=0; - switch (jffs2_compression_mode) { - case JFFS2_COMPR_MODE_NONE: - break; - case JFFS2_COMPR_MODE_PRIORITY: - output_buf = kmalloc(*cdatalen,GFP_KERNEL); - if (!output_buf) { - printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); - goto out; - } - orig_slen = *datalen; - orig_dlen = *cdatalen; - spin_lock(&jffs2_compressor_list_lock); - list_for_each_entry(this, &jffs2_compressor_list, list) { - /* Skip decompress-only backwards-compatibility and disabled modules */ - if ((!this->compress)||(this->disabled)) - continue; + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + break; + case JFFS2_COMPR_MODE_PRIORITY: + output_buf = kmalloc(*cdatalen,GFP_KERNEL); + if (!output_buf) { + printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); + goto out; + } + orig_slen = *datalen; + orig_dlen = *cdatalen; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; - this->usecount++; - spin_unlock(&jffs2_compressor_list_lock); - *datalen = orig_slen; - *cdatalen = orig_dlen; - compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); - spin_lock(&jffs2_compressor_list_lock); - this->usecount--; - if (!compr_ret) { - ret = this->compr; - this->stat_compr_blocks++; - this->stat_compr_orig_size += *datalen; - this->stat_compr_new_size += *cdatalen; - break; - } - } - spin_unlock(&jffs2_compressor_list_lock); - if (ret == JFFS2_COMPR_NONE) kfree(output_buf); - break; - case JFFS2_COMPR_MODE_SIZE: - orig_slen = *datalen; - orig_dlen = *cdatalen; - spin_lock(&jffs2_compressor_list_lock); - list_for_each_entry(this, &jffs2_compressor_list, list) { - /* Skip decompress-only backwards-compatibility and disabled modules */ - if ((!this->compress)||(this->disabled)) - continue; - /* Allocating memory for output buffer if necessary */ - if ((this->compr_buf_sizecompr_buf)) { - spin_unlock(&jffs2_compressor_list_lock); - kfree(this->compr_buf); - spin_lock(&jffs2_compressor_list_lock); - this->compr_buf_size=0; - this->compr_buf=NULL; - } - if (!this->compr_buf) { - spin_unlock(&jffs2_compressor_list_lock); - tmp_buf = kmalloc(orig_dlen,GFP_KERNEL); - spin_lock(&jffs2_compressor_list_lock); - if (!tmp_buf) { - printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); - continue; - } - else { - this->compr_buf = tmp_buf; - this->compr_buf_size = orig_dlen; - } - } - this->usecount++; - spin_unlock(&jffs2_compressor_list_lock); - *datalen = orig_slen; - *cdatalen = orig_dlen; - compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); - spin_lock(&jffs2_compressor_list_lock); - this->usecount--; - if (!compr_ret) { - if ((!best_dlen)||(best_dlen>*cdatalen)) { - best_dlen = *cdatalen; - best_slen = *datalen; - best = this; - } - } - } - if (best_dlen) { - *cdatalen = best_dlen; - *datalen = best_slen; - output_buf = best->compr_buf; - best->compr_buf = NULL; - best->compr_buf_size = 0; - best->stat_compr_blocks++; - best->stat_compr_orig_size += best_slen; - best->stat_compr_new_size += best_dlen; - ret = best->compr; - } - spin_unlock(&jffs2_compressor_list_lock); - break; - default: - printk(KERN_ERR "JFFS2: unknow compression mode.\n"); - } + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + this->usecount--; + if (!compr_ret) { + ret = this->compr; + this->stat_compr_blocks++; + this->stat_compr_orig_size += *datalen; + this->stat_compr_new_size += *cdatalen; + break; + } + } + spin_unlock(&jffs2_compressor_list_lock); + if (ret == JFFS2_COMPR_NONE) + kfree(output_buf); + break; + case JFFS2_COMPR_MODE_SIZE: + case JFFS2_COMPR_MODE_FAVOURLZO: + orig_slen = *datalen; + orig_dlen = *cdatalen; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + /* Allocating memory for output buffer if necessary */ + if ((this->compr_buf_size < orig_slen) && (this->compr_buf)) { + spin_unlock(&jffs2_compressor_list_lock); + kfree(this->compr_buf); + spin_lock(&jffs2_compressor_list_lock); + this->compr_buf_size=0; + this->compr_buf=NULL; + } + if (!this->compr_buf) { + spin_unlock(&jffs2_compressor_list_lock); + tmp_buf = kmalloc(orig_slen, GFP_KERNEL); + spin_lock(&jffs2_compressor_list_lock); + if (!tmp_buf) { + printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n", orig_slen); + continue; + } + else { + this->compr_buf = tmp_buf; + this->compr_buf_size = orig_slen; + } + } + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + this->usecount--; + if (!compr_ret) { + if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) + && (*cdatalen < *datalen)) { + best_dlen = *cdatalen; + best_slen = *datalen; + best = this; + } + } + } + if (best_dlen) { + *cdatalen = best_dlen; + *datalen = best_slen; + output_buf = best->compr_buf; + best->compr_buf = NULL; + best->compr_buf_size = 0; + best->stat_compr_blocks++; + best->stat_compr_orig_size += best_slen; + best->stat_compr_new_size += best_dlen; + ret = best->compr; + } + spin_unlock(&jffs2_compressor_list_lock); + break; + default: + printk(KERN_ERR "JFFS2: unknow compression mode.\n"); + } out: - if (ret == JFFS2_COMPR_NONE) { - *cpage_out = data_in; - *datalen = *cdatalen; - none_stat_compr_blocks++; - none_stat_compr_size += *datalen; - } - else { - *cpage_out = output_buf; - } + if (ret == JFFS2_COMPR_NONE) { + *cpage_out = data_in; + *datalen = *cdatalen; + none_stat_compr_blocks++; + none_stat_compr_size += *datalen; + } + else { + *cpage_out = output_buf; + } return ret; } @@ -165,8 +196,8 @@ int jffs2_decompress(struct jffs2_sb_inf uint16_t comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) { - struct jffs2_compressor *this; - int ret; + struct jffs2_compressor *this; + int ret; /* Older code had a bug where it would write non-zero 'usercompr' fields. Deal with it. */ @@ -177,32 +208,32 @@ int jffs2_decompress(struct jffs2_sb_inf case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ memcpy(data_out, cdata_in, datalen); - none_stat_decompr_blocks++; + none_stat_decompr_blocks++; break; case JFFS2_COMPR_ZERO: memset(data_out, 0, datalen); break; default: - spin_lock(&jffs2_compressor_list_lock); - list_for_each_entry(this, &jffs2_compressor_list, list) { - if (comprtype == this->compr) { - this->usecount++; - spin_unlock(&jffs2_compressor_list_lock); - ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); - spin_lock(&jffs2_compressor_list_lock); - if (ret) { - printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); - } - else { - this->stat_decompr_blocks++; - } - this->usecount--; - spin_unlock(&jffs2_compressor_list_lock); - return ret; - } - } + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (comprtype == this->compr) { + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + if (ret) { + printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); + } + else { + this->stat_decompr_blocks++; + } + this->usecount--; + spin_unlock(&jffs2_compressor_list_lock); + return ret; + } + } printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); - spin_unlock(&jffs2_compressor_list_lock); + spin_unlock(&jffs2_compressor_list_lock); return -EIO; } return 0; @@ -210,108 +241,119 @@ int jffs2_decompress(struct jffs2_sb_inf int jffs2_register_compressor(struct jffs2_compressor *comp) { - struct jffs2_compressor *this; + struct jffs2_compressor *this; - if (!comp->name) { - printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); - return -1; - } - comp->compr_buf_size=0; - comp->compr_buf=NULL; - comp->usecount=0; - comp->stat_compr_orig_size=0; - comp->stat_compr_new_size=0; - comp->stat_compr_blocks=0; - comp->stat_decompr_blocks=0; - D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); + if (!comp->name) { + printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); + return -1; + } + comp->compr_buf_size=0; + comp->compr_buf=NULL; + comp->usecount=0; + comp->stat_compr_orig_size=0; + comp->stat_compr_new_size=0; + comp->stat_compr_blocks=0; + comp->stat_decompr_blocks=0; + D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); - spin_lock(&jffs2_compressor_list_lock); + spin_lock(&jffs2_compressor_list_lock); - list_for_each_entry(this, &jffs2_compressor_list, list) { - if (this->priority < comp->priority) { - list_add(&comp->list, this->list.prev); - goto out; - } - } - list_add_tail(&comp->list, &jffs2_compressor_list); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + goto out; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); out: - D2(list_for_each_entry(this, &jffs2_compressor_list, list) { - printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); - }) + D2(list_for_each_entry(this, &jffs2_compressor_list, list) { + printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); + }) - spin_unlock(&jffs2_compressor_list_lock); + spin_unlock(&jffs2_compressor_list_lock); - return 0; + return 0; } int jffs2_unregister_compressor(struct jffs2_compressor *comp) { - D2(struct jffs2_compressor *this;) + D2(struct jffs2_compressor *this;) - D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); + D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); - spin_lock(&jffs2_compressor_list_lock); + spin_lock(&jffs2_compressor_list_lock); - if (comp->usecount) { - spin_unlock(&jffs2_compressor_list_lock); - printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); - return -1; - } - list_del(&comp->list); + if (comp->usecount) { + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); + return -1; + } + list_del(&comp->list); - D2(list_for_each_entry(this, &jffs2_compressor_list, list) { - printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); - }) - spin_unlock(&jffs2_compressor_list_lock); - return 0; + D2(list_for_each_entry(this, &jffs2_compressor_list, list) { + printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); + }) + spin_unlock(&jffs2_compressor_list_lock); + return 0; } void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) { - if (orig != comprbuf) - kfree(comprbuf); + if (orig != comprbuf) + kfree(comprbuf); } int __init jffs2_compressors_init(void) { /* Registering compressors */ #ifdef CONFIG_JFFS2_ZLIB - jffs2_zlib_init(); + jffs2_zlib_init(); #endif #ifdef CONFIG_JFFS2_RTIME - jffs2_rtime_init(); + jffs2_rtime_init(); #endif #ifdef CONFIG_JFFS2_RUBIN - jffs2_rubinmips_init(); - jffs2_dynrubin_init(); + jffs2_rubinmips_init(); + jffs2_dynrubin_init(); +#endif +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_init(); #endif /* Setting default compression mode */ #ifdef CONFIG_JFFS2_CMODE_NONE - jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; - D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; + D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) #else #ifdef CONFIG_JFFS2_CMODE_SIZE - jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; - D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) + jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; + D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) +#else +#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO + jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; + D1(printk(KERN_INFO "JFFS2: default compression mode: favourlzo\n");) #else - D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) + D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) +#endif #endif #endif - return 0; + return 0; } int jffs2_compressors_exit(void) { /* Unregistering compressors */ +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_exit(); +#endif #ifdef CONFIG_JFFS2_RUBIN - jffs2_dynrubin_exit(); - jffs2_rubinmips_exit(); + jffs2_dynrubin_exit(); + jffs2_rubinmips_exit(); #endif #ifdef CONFIG_JFFS2_RTIME - jffs2_rtime_exit(); + jffs2_rtime_exit(); #endif #ifdef CONFIG_JFFS2_ZLIB - jffs2_zlib_exit(); + jffs2_zlib_exit(); #endif - return 0; + return 0; } diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h index 68cc701..7d1d72f 100644 --- a/fs/jffs2/compr.h +++ b/fs/jffs2/compr.h @@ -2,7 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2004 Ferenc Havasi , - * University of Szeged, Hungary + * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * @@ -27,34 +27,38 @@ #include "nodelist.h" #define JFFS2_RUBINMIPS_PRIORITY 10 #define JFFS2_DYNRUBIN_PRIORITY 20 #define JFFS2_LZARI_PRIORITY 30 -#define JFFS2_LZO_PRIORITY 40 #define JFFS2_RTIME_PRIORITY 50 #define JFFS2_ZLIB_PRIORITY 60 +#define JFFS2_LZO_PRIORITY 80 + #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ -#define JFFS2_DYNRUBIN_DISABLED /* for decompression */ +#define JFFS2_DYNRUBIN_DISABLED /* for decompression */ #define JFFS2_COMPR_MODE_NONE 0 #define JFFS2_COMPR_MODE_PRIORITY 1 #define JFFS2_COMPR_MODE_SIZE 2 +#define JFFS2_COMPR_MODE_FAVOURLZO 3 + +#define FAVOUR_LZO_PERCENT 80 struct jffs2_compressor { - struct list_head list; - int priority; /* used by prirority comr. mode */ - char *name; - char compr; /* JFFS2_COMPR_XXX */ - int (*compress)(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *srclen, uint32_t *destlen, void *model); - int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, - uint32_t cdatalen, uint32_t datalen, void *model); - int usecount; - int disabled; /* if seted the compressor won't compress */ - unsigned char *compr_buf; /* used by size compr. mode */ - uint32_t compr_buf_size; /* used by size compr. mode */ - uint32_t stat_compr_orig_size; - uint32_t stat_compr_new_size; - uint32_t stat_compr_blocks; - uint32_t stat_decompr_blocks; + struct list_head list; + int priority; /* used by prirority comr. mode */ + char *name; + char compr; /* JFFS2_COMPR_XXX */ + int (*compress)(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *srclen, uint32_t *destlen, void *model); + int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, + uint32_t cdatalen, uint32_t datalen, void *model); + int usecount; + int disabled; /* if set the compressor won't compress */ + unsigned char *compr_buf; /* used by size compr. mode */ + uint32_t compr_buf_size; /* used by size compr. mode */ + uint32_t stat_compr_orig_size; + uint32_t stat_compr_new_size; + uint32_t stat_compr_blocks; + uint32_t stat_decompr_blocks; }; int jffs2_register_compressor(struct jffs2_compressor *comp); @@ -64,12 +68,12 @@ int jffs2_compressors_init(void); int jffs2_compressors_exit(void); uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - unsigned char *data_in, unsigned char **cpage_out, - uint32_t *datalen, uint32_t *cdatalen); + unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen); int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - uint16_t comprtype, unsigned char *cdata_in, - unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); + uint16_t comprtype, unsigned char *cdata_in, + unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); @@ -90,5 +94,9 @@ #ifdef CONFIG_JFFS2_ZLIB int jffs2_zlib_init(void); void jffs2_zlib_exit(void); #endif +#ifdef CONFIG_JFFS2_LZO +int jffs2_lzo_init(void); +void jffs2_lzo_exit(void); +#endif #endif /* __JFFS2_COMPR_H__ */ diff --git a/fs/jffs2/compr_lzo.c b/fs/jffs2/compr_lzo.c new file mode 100644 index 0000000..47b0457 --- /dev/null +++ b/fs/jffs2/compr_lzo.c @@ -0,0 +1,108 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright © 2007 Nokia Corporation. All rights reserved. + * + * Created by Richard Purdie + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ + +#include +#include +#include +#include +#include +#include +#include "compr.h" + +static void *lzo_mem; +static void *lzo_compress_buf; +static DEFINE_MUTEX(deflate_mutex); + +static void free_workspace(void) +{ + vfree(lzo_mem); + vfree(lzo_compress_buf); +} + +static int __init alloc_workspace(void) +{ + lzo_mem = vmalloc(LZO1X_MEM_COMPRESS); + lzo_compress_buf = vmalloc(lzo1x_worst_compress(PAGE_SIZE)); + + if (!lzo_mem || !lzo_compress_buf) { + printk(KERN_WARNING "Failed to allocate lzo deflate workspace\n"); + free_workspace(); + return -ENOMEM; + } + + return 0; +} + +static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, void *model) +{ + size_t compress_size; + int ret; + + mutex_lock(&deflate_mutex); + ret = lzo1x_1_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); + mutex_unlock(&deflate_mutex); + + if (ret != LZO_E_OK) + return -1; + + if (compress_size > *dstlen) + return -1; + + memcpy(cpage_out, lzo_compress_buf, compress_size); + *dstlen = compress_size; + + return 0; +} + +static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, void *model) +{ + size_t dl = destlen; + int ret; + + ret = lzo1x_decompress_safe(data_in, srclen, cpage_out, &dl); + + if (ret != LZO_E_OK || dl != destlen) + return -1; + + return 0; +} + +static struct jffs2_compressor jffs2_lzo_comp = { + .priority = JFFS2_LZO_PRIORITY, + .name = "lzo", + .compr = JFFS2_COMPR_LZO, + .compress = &jffs2_lzo_compress, + .decompress = &jffs2_lzo_decompress, + .disabled = 0, +}; + +int __init jffs2_lzo_init(void) +{ + int ret; + + ret = alloc_workspace(); + if (ret < 0) + return ret; + + ret = jffs2_register_compressor(&jffs2_lzo_comp); + if (ret) + free_workspace(); + + return ret; +} + +void jffs2_lzo_exit(void) +{ + jffs2_unregister_compressor(&jffs2_lzo_comp); + free_workspace(); +} diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index 0d0bfd2..546d153 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -104,7 +104,7 @@ static int jffs2_rtime_decompress(unsign } } } - return 0; + return 0; } static struct jffs2_compressor jffs2_rtime_comp = { diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index ea0431e..c73fa89 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c @@ -384,7 +384,7 @@ static int jffs2_rubinmips_decompress(un void *model) { rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); - return 0; + return 0; } static int jffs2_dynrubin_decompress(unsigned char *data_in, @@ -399,7 +399,7 @@ static int jffs2_dynrubin_decompress(uns bits[c] = data_in[c]; rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen); - return 0; + return 0; } static struct jffs2_compressor jffs2_rubinmips_comp = { diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index 2b87fcc..cfd301a 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -181,7 +181,7 @@ static int jffs2_zlib_decompress(unsigne } zlib_inflateEnd(&inf_strm); mutex_unlock(&inflate_mutex); - return 0; + return 0; } static struct jffs2_compressor jffs2_zlib_comp = { @@ -203,11 +203,11 @@ int __init jffs2_zlib_init(void) ret = alloc_workspaces(); if (ret) - return ret; + return ret; ret = jffs2_register_compressor(&jffs2_zlib_comp); if (ret) - free_workspaces(); + free_workspaces(); return ret; } diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index c1dfca3..8353eb9 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -32,7 +32,7 @@ static int jffs2_mkdir (struct inode *,s static int jffs2_rmdir (struct inode *,struct dentry *); static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t); static int jffs2_rename (struct inode *, struct dentry *, - struct inode *, struct dentry *); + struct inode *, struct dentry *); const struct file_operations jffs2_dir_operations = { @@ -182,6 +182,7 @@ static int jffs2_create(struct inode *di struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; + struct posix_acl *acl; int ret; ri = jffs2_alloc_raw_inode(); @@ -192,7 +193,7 @@ static int jffs2_create(struct inode *di D1(printk(KERN_DEBUG "jffs2_create()\n")); - inode = jffs2_new_inode(dir_i, mode, ri); + inode = jffs2_new_inode(dir_i, mode, ri, &acl); if (IS_ERR(inode)) { D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); @@ -212,12 +213,12 @@ static int jffs2_create(struct inode *di dentry->d_name.name, dentry->d_name.len); if (ret) - goto fail; + goto fail_acl; ret = jffs2_init_security(inode, dir_i); if (ret) - goto fail; - ret = jffs2_init_acl(inode, dir_i); + goto fail_acl; + ret = jffs2_init_acl(inode, acl); if (ret) goto fail; @@ -230,6 +231,8 @@ static int jffs2_create(struct inode *di inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); return 0; + fail_acl: + posix_acl_release(acl); fail: make_bad_inode(inode); iput(inode); @@ -306,6 +309,7 @@ static int jffs2_symlink (struct inode * struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen; + struct posix_acl *acl; int ret, targetlen = strlen(target); /* FIXME: If you care. We'd need to use frags for the target @@ -332,7 +336,7 @@ static int jffs2_symlink (struct inode * return ret; } - inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri); + inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri, &acl); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); @@ -362,6 +366,7 @@ static int jffs2_symlink (struct inode * up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); + posix_acl_release(acl); return PTR_ERR(fn); } @@ -372,6 +377,7 @@ static int jffs2_symlink (struct inode * up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); + posix_acl_release(acl); return -ENOMEM; } @@ -389,9 +395,10 @@ static int jffs2_symlink (struct inode * ret = jffs2_init_security(inode, dir_i); if (ret) { jffs2_clear_inode(inode); + posix_acl_release(acl); return ret; } - ret = jffs2_init_acl(inode, dir_i); + ret = jffs2_init_acl(inode, acl); if (ret) { jffs2_clear_inode(inode); return ret; @@ -469,6 +476,7 @@ static int jffs2_mkdir (struct inode *di struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen; + struct posix_acl *acl; int ret; mode |= S_IFDIR; @@ -491,7 +499,7 @@ static int jffs2_mkdir (struct inode *di return ret; } - inode = jffs2_new_inode(dir_i, mode, ri); + inode = jffs2_new_inode(dir_i, mode, ri, &acl); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); @@ -518,6 +526,7 @@ static int jffs2_mkdir (struct inode *di up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); + posix_acl_release(acl); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -531,9 +540,10 @@ static int jffs2_mkdir (struct inode *di ret = jffs2_init_security(inode, dir_i); if (ret) { jffs2_clear_inode(inode); + posix_acl_release(acl); return ret; } - ret = jffs2_init_acl(inode, dir_i); + ret = jffs2_init_acl(inode, acl); if (ret) { jffs2_clear_inode(inode); return ret; @@ -629,6 +639,7 @@ static int jffs2_mknod (struct inode *di union jffs2_device_node dev; int devlen = 0; uint32_t alloclen; + struct posix_acl *acl; int ret; if (!new_valid_dev(rdev)) @@ -655,7 +666,7 @@ static int jffs2_mknod (struct inode *di return ret; } - inode = jffs2_new_inode(dir_i, mode, ri); + inode = jffs2_new_inode(dir_i, mode, ri, &acl); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); @@ -684,6 +695,7 @@ static int jffs2_mknod (struct inode *di up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); + posix_acl_release(acl); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -697,9 +709,10 @@ static int jffs2_mknod (struct inode *di ret = jffs2_init_security(inode, dir_i); if (ret) { jffs2_clear_inode(inode); + posix_acl_release(acl); return ret; } - ret = jffs2_init_acl(inode, dir_i); + ret = jffs2_init_acl(inode, acl); if (ret) { jffs2_clear_inode(inode); return ret; @@ -770,7 +783,7 @@ static int jffs2_mknod (struct inode *di } static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, - struct inode *new_dir_i, struct dentry *new_dentry) + struct inode *new_dir_i, struct dentry *new_dentry) { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 66e7c2f..addd3fc 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -38,8 +38,8 @@ static void jffs2_erase_block(struct jff #ifdef __ECOS ret = jffs2_flash_erase(c, jeb); if (!ret) { - jffs2_erase_succeeded(c, jeb); - return; + jffs2_erase_succeeded(c, jeb); + return; } bad_offset = jeb->offset; #else /* Linux */ @@ -50,12 +50,14 @@ #else /* Linux */ instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); return; } @@ -82,12 +84,14 @@ #endif /* __ECOS */ if (ret == -ENOMEM || ret == -EAGAIN) { /* Erase failed immediately. Refile it on the list */ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); return; } @@ -114,6 +118,7 @@ void jffs2_erase_pending_blocks(struct j jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); list_del(&jeb->list); spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); jffs2_mark_erased_block(c, jeb); if (!--count) { @@ -134,6 +139,7 @@ void jffs2_erase_pending_blocks(struct j jffs2_free_jeb_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); jffs2_erase_block(c, jeb); @@ -142,23 +148,25 @@ void jffs2_erase_pending_blocks(struct j } /* Be nice */ - cond_resched(); + yield(); + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); } spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); - - up(&c->erase_free_sem); } static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); } @@ -172,22 +180,26 @@ static void jffs2_erase_failed(struct jf failed too many times. */ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { /* We'd like to give this block another try. */ + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); return; } } + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->bad_size += c->sector_size; list_move(&jeb->list, &c->bad_list); c->nr_erasing_blocks--; spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); wake_up(&c->erase_wait); } @@ -317,6 +329,33 @@ static int jffs2_block_check_erase(struc size_t retlen; int ret = -EIO; + if (c->mtd->point) { + unsigned long *wordebuf; + + ret = c->mtd->point(c->mtd, jeb->offset, c->sector_size, &retlen, (unsigned char **)&ebuf); + if (ret) { + D1(printk(KERN_DEBUG "MTD point failed %d\n", ret)); + goto do_flash_read; + } + if (retlen < c->sector_size) { + /* Don't muck about if it won't let us point to the whole erase sector */ + D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen)); + c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size); + goto do_flash_read; + } + wordebuf = ebuf-sizeof(*wordebuf); + retlen /= sizeof(*wordebuf); + do { + if (*++wordebuf != ~0) + break; + } while(--retlen); + c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size); + if (retlen) + printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n", + *wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf)); + return 0; + } + do_flash_read: ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!ebuf) { printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset); @@ -362,7 +401,7 @@ static void jffs2_mark_erased_block(stru { size_t retlen; int ret; - uint32_t bad_offset; + uint32_t uninitialized_var(bad_offset); switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { case -EAGAIN: goto refile; @@ -417,6 +456,7 @@ static void jffs2_mark_erased_block(stru jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); } + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->free_size += jeb->free_size; @@ -429,23 +469,28 @@ static void jffs2_mark_erased_block(stru c->nr_erasing_blocks--; c->nr_free_blocks++; spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); wake_up(&c->erase_wait); return; filebad: + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); /* Stick it on a list (any list) so erase_failed can take it right off again. Silly, but shouldn't happen often. */ list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); jffs2_erase_failed(c, jeb, bad_offset); return; refile: /* Stick it back on the list from whence it came and come back later */ jffs2_erase_pending_trigger(c); + down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_add(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); + up(&c->erase_free_sem); return; } diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 1d3b7a9..dd64ddc 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -24,7 +24,7 @@ #include "nodelist.h" static int jffs2_flash_setup(struct jffs2_sb_info *c); -static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) +int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) { struct jffs2_full_dnode *old_metadata, *new_metadata; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); @@ -36,10 +36,8 @@ static int jffs2_do_setattr (struct inod unsigned int ivalid; uint32_t alloclen; int ret; + D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); - ret = inode_change_ok(inode, iattr); - if (ret) - return ret; /* Special cases - we don't want more than one data node for these types on the medium at any time. So setattr @@ -183,9 +181,14 @@ int jffs2_setattr(struct dentry *dentry, { int rc; + rc = inode_change_ok(dentry->d_inode, iattr); + if (rc) + return rc; + rc = jffs2_do_setattr(dentry->d_inode, iattr); if (!rc && (iattr->ia_valid & ATTR_MODE)) rc = jffs2_acl_chmod(dentry->d_inode); + return rc; } @@ -399,7 +402,8 @@ void jffs2_write_super (struct super_blo /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, fill in the raw_inode while you're at it. */ -struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) +struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri, + struct posix_acl **acl) { struct inode *inode; struct super_block *sb = dir_i->i_sb; @@ -431,7 +435,23 @@ struct inode *jffs2_new_inode (struct in } else { ri->gid = cpu_to_je16(current->fsgid); } - ri->mode = cpu_to_jemode(mode); + + /* POSIX ACLs have to be processed now, at least partly. + The umask is only applied if there's no default ACL */ + if (!S_ISLNK(mode)) { + *acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT); + if (IS_ERR(*acl)) { + make_bad_inode(inode); + iput(inode); + inode = (void *)*acl; + *acl = NULL; + return inode; + } + if (!(*acl)) + mode &= ~current->fs->umask; + } else { + *acl = NULL; + } ret = jffs2_do_new_inode (c, f, mode, ri); if (ret) { make_bad_inode(inode); diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 2d99e06..eded819 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -556,7 +556,7 @@ static int jffs2_garbage_collect_pristin node = kmalloc(rawlen, GFP_KERNEL); if (!node) - return -ENOMEM; + return -ENOMEM; ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); if (!ret && retlen != rawlen) @@ -624,7 +624,7 @@ static int jffs2_garbage_collect_pristin if (ret || (retlen != rawlen)) { printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", - rawlen, phys_ofs, ret, retlen); + rawlen, phys_ofs, ret, retlen); if (retlen) { jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL); } else { diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index b13298a..ae99cd7 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -106,6 +106,9 @@ struct jffs2_sb_info { uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + unsigned char *wbuf_verify; /* read-back buffer for verification */ +#endif #ifdef CONFIG_JFFS2_FS_WRITEBUFFER unsigned char *wbuf; /* Write-behind buffer for NAND flash */ uint32_t wbuf_ofs; diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index bc5509f..ec1aae9 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -127,7 +127,7 @@ static inline struct jffs2_inode_cache * return ((struct jffs2_inode_cache *)raw); } - /* flash_offset & 3 always has to be zero, because nodes are + /* flash_offset & 3 always has to be zero, because nodes are always aligned at 4 bytes. So we have a couple of extra bits to play with, which indicate the node's status; see below: */ #define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index dbc908a..5b49bff 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -154,7 +154,7 @@ int jffs2_reserve_space_gc(struct jffs2_ while(ret == -EAGAIN) { ret = jffs2_do_reserve_space(c, minsize, len, sumsize); if (ret) { - D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); + D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); @@ -423,7 +423,12 @@ #if 1 even after refiling c->nextblock */ if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE)) && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) { - printk(KERN_WARNING "argh. node added in wrong place\n"); + printk(KERN_WARNING "argh. node added in wrong place at 0x%08x(%d)\n", ofs & ~3, ofs & 3); + if (c->nextblock) + printk(KERN_WARNING "nextblock 0x%08x", c->nextblock->offset); + else + printk(KERN_WARNING "No nextblock"); + printk(", expected at %08x\n", jeb->offset + (c->sector_size - jeb->free_size)); return ERR_PTR(-EINVAL); } #endif diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 80daea9..f6743a9 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -173,12 +173,15 @@ int jffs2_ioctl(struct inode *, struct f extern const struct inode_operations jffs2_symlink_inode_operations; /* fs.c */ +struct posix_acl; + int jffs2_setattr (struct dentry *, struct iattr *); +int jffs2_do_setattr (struct inode *, struct iattr *); void jffs2_read_inode (struct inode *); void jffs2_clear_inode (struct inode *); void jffs2_dirty_inode(struct inode *inode); struct inode *jffs2_new_inode (struct inode *dir_i, int mode, - struct jffs2_raw_inode *ri); + struct jffs2_raw_inode *ri, struct posix_acl **acl); int jffs2_statfs (struct dentry *, struct kstatfs *); void jffs2_write_super (struct super_block *); int jffs2_remount_fs (struct super_block *, int *, char *); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index b5baa35..8d4319c 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -211,7 +211,7 @@ static void jffs2_kill_tn(struct jffs2_s * ordering. * * Returns 0 if the node was handled (including marking it obsolete) - * < 0 an if error occurred + * < 0 an if error occurred */ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, struct jffs2_readinode_info *rii, @@ -862,8 +862,8 @@ static inline int read_unknown(struct jf JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n", ref_offset(ref)); JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n", - je16_to_cpu(un->magic), je16_to_cpu(un->nodetype), - je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)); + je16_to_cpu(un->magic), je16_to_cpu(un->nodetype), + je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)); jffs2_mark_node_obsolete(c, ref); return 0; } diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 6c75cd4..59dd408 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -863,7 +863,7 @@ #endif /* CONFIG_JFFS2_FS_XATTR */ switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_ROCOMPAT: printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); - c->flags |= JFFS2_SB_FLAG_RO; + c->flags |= JFFS2_SB_FLAG_RO; if (!(jffs2_is_readonly(c))) return -EROFS; if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c index bc9f6ba..02c39c6 100644 --- a/fs/jffs2/security.c +++ b/fs/jffs2/security.c @@ -38,9 +38,9 @@ int jffs2_init_security(struct inode *in } rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0); - kfree(name); - kfree(value); - return rc; + kfree(name); + kfree(value); + return rc; } /* ---- XATTR Handler for "security.*" ----------------- */ diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index d828b29..2a77d3f 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c @@ -2,10 +2,10 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2004 Ferenc Havasi , - * Zoltan Sogor , - * Patrik Kluba , - * University of Szeged, Hungary - * 2006 KaiGai Kohei + * Zoltan Sogor , + * Patrik Kluba , + * University of Szeged, Hungary + * 2006 KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h index 0c6669e..8bf34f2 100644 --- a/fs/jffs2/summary.h +++ b/fs/jffs2/summary.h @@ -2,9 +2,9 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2004 Ferenc Havasi , - * Zoltan Sogor , - * Patrik Kluba , - * University of Szeged, Hungary + * Zoltan Sogor , + * Patrik Kluba , + * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 91d1d0f..d1d4f27 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -220,6 +220,47 @@ static struct jffs2_raw_node_ref **jffs2 return NULL; } +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY +static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf, + uint32_t ofs) +{ + int ret; + size_t retlen; + char *eccstr; + + ret = c->mtd->read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify); + if (ret && ret != -EUCLEAN && ret != -EBADMSG) { + printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret); + return ret; + } else if (retlen != c->wbuf_pagesize) { + printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize); + return -EIO; + } + if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize)) + return 0; + + if (ret == -EUCLEAN) + eccstr = "corrected"; + else if (ret == -EBADMSG) + eccstr = "correction failed"; + else + eccstr = "OK or unused"; + + printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n", + eccstr, c->wbuf_ofs); + print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, + c->wbuf, c->wbuf_pagesize, 0); + + printk(KERN_WARNING "Read back:\n"); + print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, + c->wbuf_verify, c->wbuf_pagesize, 0); + + return -EIO; +} +#else +#define jffs2_verify_write(c,b,o) (0) +#endif + /* Recover from failure to write wbuf. Recover the nodes up to the * wbuf, not the one which we were starting to try to write. */ @@ -380,7 +421,7 @@ #endif ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf); - if (ret || retlen != towrite) { + if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) { /* Argh. We tried. Really we did. */ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); kfree(buf); @@ -587,15 +628,16 @@ #endif ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); - if (ret || retlen != c->wbuf_pagesize) { - if (ret) - printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret); - else { - printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", - retlen, c->wbuf_pagesize); - ret = -EIO; - } - + if (ret) { + printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret); + goto wfail; + } else if (retlen != c->wbuf_pagesize) { + printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", + retlen, c->wbuf_pagesize); + ret = -EIO; + goto wfail; + } else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) { + wfail: jffs2_wbuf_recover(c); return ret; @@ -966,8 +1008,8 @@ exit: #define NR_OOB_SCAN_PAGES 4 -/* For historical reasons we use only 12 bytes for OOB clean marker */ -#define OOB_CM_SIZE 12 +/* For historical reasons we use only 8 bytes for OOB clean marker */ +#define OOB_CM_SIZE 8 static const struct jffs2_unknown_node oob_cleanmarker = { @@ -1021,8 +1063,8 @@ int jffs2_check_oob_empty(struct jffs2_s /* * Check for a valid cleanmarker. * Returns: 0 if a valid cleanmarker was found - * 1 if no cleanmarker was found - * negative error code if an error occurred + * 1 if no cleanmarker was found + * negative error code if an error occurred */ int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) @@ -1138,11 +1180,22 @@ int jffs2_nand_flash_setup(struct jffs2_ return -ENOMEM; } +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->oobbuf); + kfree(c->wbuf); + return -ENOMEM; + } +#endif return 0; } void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) { +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif kfree(c->wbuf); kfree(c->oobbuf); } diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 3b0ff29..6e3b5dd 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -75,7 +75,7 @@ extern void jffs2_build_xattr_subsystem( extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c); extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, - uint32_t xid, uint32_t version); + uint32_t xid, uint32_t version); extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c index 40942bc..8bbeab9 100644 --- a/fs/jffs2/xattr_user.c +++ b/fs/jffs2/xattr_user.c @@ -17,7 +17,7 @@ #include #include "nodelist.h" static int jffs2_user_getxattr(struct inode *inode, const char *name, - void *buffer, size_t size) + void *buffer, size_t size) { if (!strcmp(name, "")) return -EINVAL; @@ -25,7 +25,7 @@ static int jffs2_user_getxattr(struct in } static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer, - size_t size, int flags) + size_t size, int flags) { if (!strcmp(name, "")) return -EINVAL; diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 840631f..6b563ca 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -46,6 +46,7 @@ #define JFFS2_COMPR_RUBINMIPS 0x03 #define JFFS2_COMPR_COPY 0x04 #define JFFS2_COMPR_DYNRUBIN 0x05 #define JFFS2_COMPR_ZLIB 0x06 +#define JFFS2_COMPR_LZO 0x07 /* Compatibility flags. */ #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ #define JFFS2_NODE_ACCURATE 0x2000 diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index d2365c8..c42bc7f 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -432,6 +432,7 @@ #define NAND_MFR_RENESAS 0x07 #define NAND_MFR_STMICRO 0x20 #define NAND_MFR_HYNIX 0xad #define NAND_MFR_MICRON 0x2c +#define NAND_MFR_AMD 0x01 /** * struct nand_flash_dev - NAND Flash Device ID Structure diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index a56d24a..fd0a260 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -60,6 +60,7 @@ struct onenand_bufferram { * @erase_shift: [INTERN] number of address bits in a block * @page_shift: [INTERN] number of address bits in a page * @page_mask: [INTERN] a page per block mask + * @writesize: [INTERN] a real page size * @bufferram_index: [INTERN] BufferRAM index * @bufferram: [INTERN] BufferRAM info * @readw: [REPLACEABLE] hardware specific function for read short @@ -100,6 +101,7 @@ struct onenand_chip { unsigned int erase_shift; unsigned int page_shift; unsigned int page_mask; + unsigned int writesize; unsigned int bufferram_index; struct onenand_bufferram bufferram[MAX_BUFFERRAM]; @@ -140,6 +142,8 @@ #define ONENAND_CURRENT_BUFFERRAM(this) #define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1) #define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1) #define ONENAND_SET_PREV_BUFFERRAM(this) (this->bufferram_index ^= 1) +#define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) +#define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1) #define ONENAND_GET_SYS_CFG1(this) \ (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) @@ -149,6 +153,13 @@ #define ONENAND_SET_SYS_CFG1(v, this) #define ONENAND_IS_DDP(this) \ (this->device_id & ONENAND_DEVICE_IS_DDP) +#ifdef CONFIG_MTD_ONENAND_2X_PROGRAM +#define ONENAND_IS_2PLANE(this) \ + (this->options & ONENAND_HAS_2PLANE) +#else +#define ONENAND_IS_2PLANE(this) (0) +#endif + /* Check byte access in OneNAND */ #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) @@ -157,6 +168,7 @@ #define ONENAND_CHECK_BYTE_ACCESS(addr) */ #define ONENAND_HAS_CONT_LOCK (0x0001) #define ONENAND_HAS_UNLOCK_ALL (0x0002) +#define ONENAND_HAS_2PLANE (0x0004) #define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_OOBBUF_ALLOC (0x2000) diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index af94719..c46161f 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -74,6 +74,8 @@ #define ONENAND_DEVICE_VCC_MASK (0x3) #define ONENAND_DEVICE_DENSITY_512Mb (0x002) #define ONENAND_DEVICE_DENSITY_1Gb (0x003) +#define ONENAND_DEVICE_DENSITY_2Gb (0x004) +#define ONENAND_DEVICE_DENSITY_4Gb (0x005) /* * Version ID Register F002h (R) @@ -111,6 +113,8 @@ #define ONENAND_CMD_READ (0x00) #define ONENAND_CMD_READOOB (0x13) #define ONENAND_CMD_PROG (0x80) #define ONENAND_CMD_PROGOOB (0x1A) +#define ONENAND_CMD_2X_PROG (0x7D) +#define ONENAND_CMD_2X_CACHE_PROG (0x7F) #define ONENAND_CMD_UNLOCK (0x23) #define ONENAND_CMD_LOCK (0x2A) #define ONENAND_CMD_LOCK_TIGHT (0x2C)