| 
									
										
										
										
											2010-08-08 20:58:20 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org> et al. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Overhauled routines for dealing with different mmap regions of flash */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef __LINUX_MTD_MAP_H__
 | 
					
						
							|  |  |  | #define __LINUX_MTD_MAP_H__
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/list.h>
 | 
					
						
							| 
									
										
										
										
											2005-10-30 15:03:48 -08:00
										 |  |  | #include <linux/string.h>
 | 
					
						
							| 
									
										
										
										
											2010-05-04 12:51:34 -07:00
										 |  |  | #include <linux/bug.h>
 | 
					
						
							| 
									
										
										
										
											2011-12-30 18:28:01 +02:00
										 |  |  | #include <linux/kernel.h>
 | 
					
						
							| 
									
										
										
										
											2005-10-30 15:03:48 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <asm/unaligned.h>
 | 
					
						
							|  |  |  | #include <asm/io.h>
 | 
					
						
							| 
									
										
										
										
											2012-03-28 18:30:03 +01:00
										 |  |  | #include <asm/barrier.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
 | 
					
						
							|  |  |  | #define map_bankwidth(map) 1
 | 
					
						
							|  |  |  | #define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
 | 
					
						
							|  |  |  | #define map_bankwidth_is_large(map) (0)
 | 
					
						
							|  |  |  | #define map_words(map) (1)
 | 
					
						
							|  |  |  | #define MAX_MAP_BANKWIDTH 1
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define map_bankwidth_is_1(map) (0)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
 | 
					
						
							|  |  |  | # ifdef map_bankwidth
 | 
					
						
							|  |  |  | #  undef map_bankwidth
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) ((map)->bankwidth)
 | 
					
						
							|  |  |  | # else
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) 2
 | 
					
						
							|  |  |  | #  define map_bankwidth_is_large(map) (0)
 | 
					
						
							|  |  |  | #  define map_words(map) (1)
 | 
					
						
							|  |  |  | # endif
 | 
					
						
							|  |  |  | #define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
 | 
					
						
							|  |  |  | #undef MAX_MAP_BANKWIDTH
 | 
					
						
							|  |  |  | #define MAX_MAP_BANKWIDTH 2
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define map_bankwidth_is_2(map) (0)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
 | 
					
						
							|  |  |  | # ifdef map_bankwidth
 | 
					
						
							|  |  |  | #  undef map_bankwidth
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) ((map)->bankwidth)
 | 
					
						
							|  |  |  | # else
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) 4
 | 
					
						
							|  |  |  | #  define map_bankwidth_is_large(map) (0)
 | 
					
						
							|  |  |  | #  define map_words(map) (1)
 | 
					
						
							|  |  |  | # endif
 | 
					
						
							|  |  |  | #define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
 | 
					
						
							|  |  |  | #undef MAX_MAP_BANKWIDTH
 | 
					
						
							|  |  |  | #define MAX_MAP_BANKWIDTH 4
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define map_bankwidth_is_4(map) (0)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ensure we never evaluate anything shorted than an unsigned long
 | 
					
						
							|  |  |  |  * to zero, and ensure we'll never miss the end of an comparison (bjd) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
 | 
					
						
							|  |  |  | # ifdef map_bankwidth
 | 
					
						
							|  |  |  | #  undef map_bankwidth
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) ((map)->bankwidth)
 | 
					
						
							|  |  |  | #  if BITS_PER_LONG < 64
 | 
					
						
							|  |  |  | #   undef map_bankwidth_is_large
 | 
					
						
							|  |  |  | #   define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
 | 
					
						
							|  |  |  | #   undef map_words
 | 
					
						
							|  |  |  | #   define map_words(map) map_calc_words(map)
 | 
					
						
							|  |  |  | #  endif
 | 
					
						
							|  |  |  | # else
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) 8
 | 
					
						
							|  |  |  | #  define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
 | 
					
						
							|  |  |  | #  define map_words(map) map_calc_words(map)
 | 
					
						
							|  |  |  | # endif
 | 
					
						
							|  |  |  | #define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
 | 
					
						
							|  |  |  | #undef MAX_MAP_BANKWIDTH
 | 
					
						
							|  |  |  | #define MAX_MAP_BANKWIDTH 8
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define map_bankwidth_is_8(map) (0)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
 | 
					
						
							|  |  |  | # ifdef map_bankwidth
 | 
					
						
							|  |  |  | #  undef map_bankwidth
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) ((map)->bankwidth)
 | 
					
						
							|  |  |  | #  undef map_bankwidth_is_large
 | 
					
						
							|  |  |  | #  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
 | 
					
						
							|  |  |  | #  undef map_words
 | 
					
						
							|  |  |  | #  define map_words(map) map_calc_words(map)
 | 
					
						
							|  |  |  | # else
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) 16
 | 
					
						
							|  |  |  | #  define map_bankwidth_is_large(map) (1)
 | 
					
						
							|  |  |  | #  define map_words(map) map_calc_words(map)
 | 
					
						
							|  |  |  | # endif
 | 
					
						
							|  |  |  | #define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
 | 
					
						
							|  |  |  | #undef MAX_MAP_BANKWIDTH
 | 
					
						
							|  |  |  | #define MAX_MAP_BANKWIDTH 16
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define map_bankwidth_is_16(map) (0)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
 | 
					
						
							|  |  |  | # ifdef map_bankwidth
 | 
					
						
							|  |  |  | #  undef map_bankwidth
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) ((map)->bankwidth)
 | 
					
						
							|  |  |  | #  undef map_bankwidth_is_large
 | 
					
						
							|  |  |  | #  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
 | 
					
						
							|  |  |  | #  undef map_words
 | 
					
						
							|  |  |  | #  define map_words(map) map_calc_words(map)
 | 
					
						
							|  |  |  | # else
 | 
					
						
							|  |  |  | #  define map_bankwidth(map) 32
 | 
					
						
							|  |  |  | #  define map_bankwidth_is_large(map) (1)
 | 
					
						
							|  |  |  | #  define map_words(map) map_calc_words(map)
 | 
					
						
							|  |  |  | # endif
 | 
					
						
							|  |  |  | #define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
 | 
					
						
							|  |  |  | #undef MAX_MAP_BANKWIDTH
 | 
					
						
							|  |  |  | #define MAX_MAP_BANKWIDTH 32
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define map_bankwidth_is_32(map) (0)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef map_bankwidth
 | 
					
						
							| 
									
										
										
										
											2007-09-06 09:40:21 +01:00
										 |  |  | #warning "No CONFIG_MTD_MAP_BANK_WIDTH_xx selected. No NOR chip support can work"
 | 
					
						
							|  |  |  | static inline int map_bankwidth(void *map) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BUG(); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #define map_bankwidth_is_large(map) (0)
 | 
					
						
							|  |  |  | #define map_words(map) (0)
 | 
					
						
							|  |  |  | #define MAX_MAP_BANKWIDTH 1
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int map_bankwidth_supported(int w) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (w) { | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
 | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
 | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
 | 
					
						
							|  |  |  | 	case 4: | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
 | 
					
						
							|  |  |  | 	case 8: | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
 | 
					
						
							|  |  |  | 	case 16: | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
 | 
					
						
							|  |  |  | 	case 32: | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef union { | 
					
						
							|  |  |  | 	unsigned long x[MAX_MAP_LONGS]; | 
					
						
							|  |  |  | } map_word; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* The map stuff is very simple. You fill in your struct map_info with
 | 
					
						
							|  |  |  |    a handful of routines for accessing the device, making sure they handle | 
					
						
							|  |  |  |    paging etc. correctly if your device needs it. Then you pass it off | 
					
						
							|  |  |  |    to a chip probe routine -- either JEDEC or CFI probe or both -- via | 
					
						
							|  |  |  |    do_map_probe(). If a chip is recognised, the probe code will invoke the | 
					
						
							|  |  |  |    appropriate chip driver (if present) and return a struct mtd_info. | 
					
						
							| 
									
										
										
										
											2005-11-07 11:15:31 +00:00
										 |  |  |    At which point, you fill in the mtd->module with your own module | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |    address, and register it with the MTD core code. Or you could partition | 
					
						
							|  |  |  |    it and register the partitions instead, or keep it for your own private | 
					
						
							|  |  |  |    use; whatever. | 
					
						
							| 
									
										
										
										
											2005-11-07 11:15:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |    The mtd->priv field will point to the struct map_info, and any further | 
					
						
							| 
									
										
										
										
											2005-11-07 11:15:31 +00:00
										 |  |  |    private data required by the chip driver is linked from the | 
					
						
							|  |  |  |    mtd->priv->fldrv_priv field. This allows the map driver to get at | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |    the destructor function map->fldrv_destroy() when it's tired | 
					
						
							|  |  |  |    of living. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct map_info { | 
					
						
							| 
									
										
										
										
											2008-07-02 12:46:22 -07:00
										 |  |  | 	const char *name; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	unsigned long size; | 
					
						
							| 
									
										
										
										
											2007-01-19 08:40:04 +01:00
										 |  |  | 	resource_size_t phys; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #define NO_XIP (-1UL)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	void __iomem *virt; | 
					
						
							|  |  |  | 	void *cached; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-14 18:44:34 -06:00
										 |  |  | 	int swap; /* this mapping's byte-swapping requirement */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int bankwidth; /* in octets. This isn't necessarily the width
 | 
					
						
							|  |  |  | 		       of actual bus cycles -- it's the repeat interval | 
					
						
							|  |  |  | 		      in bytes, before you are talking to the first chip again. | 
					
						
							|  |  |  | 		      */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_COMPLEX_MAPPINGS
 | 
					
						
							|  |  |  | 	map_word (*read)(struct map_info *, unsigned long); | 
					
						
							|  |  |  | 	void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	void (*write)(struct map_info *, const map_word, unsigned long); | 
					
						
							|  |  |  | 	void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* We can perhaps put in 'point' and 'unpoint' methods, if we really
 | 
					
						
							|  |  |  | 	   want to enable XIP for non-linear mappings. Not yet though. */ | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	/* It's possible for the map driver to use cached memory in its
 | 
					
						
							|  |  |  | 	   copy_from implementation (and _only_ with copy_from).  However, | 
					
						
							|  |  |  | 	   when the chip driver knows some flash area has changed contents, | 
					
						
							|  |  |  | 	   it will signal it to the map driver through this routine to let | 
					
						
							|  |  |  | 	   the map driver invalidate the corresponding cache as needed. | 
					
						
							|  |  |  | 	   If there is no cache to care about this can be set to NULL. */ | 
					
						
							|  |  |  | 	void (*inval_cache)(struct map_info *, unsigned long, ssize_t); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-07 11:15:31 +00:00
										 |  |  | 	/* set_vpp() must handle being reentered -- enable, enable, disable
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	   must leave it enabled. */ | 
					
						
							|  |  |  | 	void (*set_vpp)(struct map_info *, int); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-16 18:21:10 +00:00
										 |  |  | 	unsigned long pfow_base; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	unsigned long map_priv_1; | 
					
						
							|  |  |  | 	unsigned long map_priv_2; | 
					
						
							| 
									
										
										
										
											2013-01-18 13:10:05 +01:00
										 |  |  | 	struct device_node *device_node; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	void *fldrv_priv; | 
					
						
							|  |  |  | 	struct mtd_chip_driver *fldrv; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct mtd_chip_driver { | 
					
						
							|  |  |  | 	struct mtd_info *(*probe)(struct map_info *map); | 
					
						
							|  |  |  | 	void (*destroy)(struct mtd_info *); | 
					
						
							|  |  |  | 	struct module *module; | 
					
						
							|  |  |  | 	char *name; | 
					
						
							|  |  |  | 	struct list_head list; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void register_mtd_chip_driver(struct mtd_chip_driver *); | 
					
						
							|  |  |  | void unregister_mtd_chip_driver(struct mtd_chip_driver *); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct mtd_info *do_map_probe(const char *name, struct map_info *map); | 
					
						
							|  |  |  | void map_destroy(struct mtd_info *mtd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
 | 
					
						
							|  |  |  | #define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define INVALIDATE_CACHED_RANGE(map, from, size) \
 | 
					
						
							|  |  |  | 	do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	for (i=0; i<map_words(map); i++) { | 
					
						
							|  |  |  | 		if (val1.x[i] != val2.x[i]) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	map_word r; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i=0; i<map_words(map); i++) { | 
					
						
							|  |  |  | 		r.x[i] = val1.x[i] & val2.x[i]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-02-08 17:11:19 +00:00
										 |  |  | static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	map_word r; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i=0; i<map_words(map); i++) { | 
					
						
							|  |  |  | 		r.x[i] = val1.x[i] & ~val2.x[i]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	map_word r; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i=0; i<map_words(map); i++) { | 
					
						
							|  |  |  | 		r.x[i] = val1.x[i] | val2.x[i]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return r; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-02-08 17:11:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i=0; i<map_words(map); i++) { | 
					
						
							|  |  |  | 		if (val1.x[i] & val2.x[i]) | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline map_word map_word_load(struct map_info *map, const void *ptr) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-31 21:06:34 +04:00
										 |  |  | 	map_word r; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (map_bankwidth_is_1(map)) | 
					
						
							|  |  |  | 		r.x[0] = *(unsigned char *)ptr; | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_2(map)) | 
					
						
							|  |  |  | 		r.x[0] = get_unaligned((uint16_t *)ptr); | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_4(map)) | 
					
						
							|  |  |  | 		r.x[0] = get_unaligned((uint32_t *)ptr); | 
					
						
							|  |  |  | #if BITS_PER_LONG >= 64
 | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_8(map)) | 
					
						
							|  |  |  | 		r.x[0] = get_unaligned((uint64_t *)ptr); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_large(map)) | 
					
						
							|  |  |  | 		memcpy(r.x, ptr, map->bankwidth); | 
					
						
							| 
									
										
										
										
											2013-01-31 21:06:34 +04:00
										 |  |  | 	else | 
					
						
							|  |  |  | 		BUG(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (map_bankwidth_is_large(map)) { | 
					
						
							|  |  |  | 		char *dest = (char *)&orig; | 
					
						
							|  |  |  | 		memcpy(dest+start, buf, len); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		for (i=start; i < start+len; i++) { | 
					
						
							|  |  |  | 			int bitpos; | 
					
						
							|  |  |  | #ifdef __LITTLE_ENDIAN
 | 
					
						
							|  |  |  | 			bitpos = i*8; | 
					
						
							|  |  |  | #else /* __BIG_ENDIAN */
 | 
					
						
							|  |  |  | 			bitpos = (map_bankwidth(map)-1-i)*8; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 			orig.x[0] &= ~(0xff << bitpos); | 
					
						
							| 
									
										
											  
											
												mtd: map: fixed bug in 64-bit systems
Hardware:
	CPU: XLP832,the 64-bit OS
	NOR Flash:S29GL128S 128M
Software:
	Kernel:2.6.32.41
	Filesystem:JFFS2
When writing files, errors appear:
	Write len 182  but return retlen 180
	Write of 182 bytes at 0x072c815c failed. returned -5, retlen 180
	Write len 186  but return retlen 184
	Write of 186 bytes at 0x072caff4 failed. returned -5, retlen 184
These errors exist only in 64-bit systems,not in 32-bit systems. After analysis, we
found that the left shift operation is wrong in map_word_load_partial. For instance:
	unsigned char buf[3] ={0x9e,0x3a,0xea};
	map_bankwidth(map) is 4;
	for (i=0; i < 3; i++) {
		int bitpos;
		bitpos = (map_bankwidth(map)-1-i)*8;
		orig.x[0] &= ~(0xff << bitpos);
		orig.x[0] |= buf[i] << bitpos;
	}
The value of orig.x[0] is expected to be 0x9e3aeaff, but in this situation(64-bit
System) we'll get the wrong value of 0xffffffff9e3aeaff due to the 64-bit sign
extension:
buf[i] is defined as "unsigned char" and the left-shift operation will convert it
to the type of "signed int", so when left-shift buf[i] by 24 bits, the final result
will get the wrong value: 0xffffffff9e3aeaff.
If the left-shift bits are less than 24, then sign extension will not occur. Whereas
the bankwidth of the nor flash we used is 4, therefore this BUG emerges.
Signed-off-by: Pang Xunlei <pang.xunlei@zte.com.cn>
Signed-off-by: Zhang Yi <zhang.yi20@zte.com.cn>
Signed-off-by: Lu Zhongjun <lu.zhongjun@zte.com.cn>
Cc: <stable@vger.kernel.org>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
											
										 
											2013-08-22 19:32:38 +08:00
										 |  |  | 			orig.x[0] |= (unsigned long)buf[i-start] << bitpos; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return orig; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-24 20:50:18 +02:00
										 |  |  | #if BITS_PER_LONG < 64
 | 
					
						
							|  |  |  | #define MAP_FF_LIMIT 4
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define MAP_FF_LIMIT 8
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static inline map_word map_word_ff(struct map_info *map) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	map_word r; | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2005-11-07 11:15:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-25 12:20:29 +02:00
										 |  |  | 	if (map_bankwidth(map) < MAP_FF_LIMIT) { | 
					
						
							| 
									
										
										
										
											2005-05-25 12:32:37 +02:00
										 |  |  | 		int bw = 8 * map_bankwidth(map); | 
					
						
							| 
									
										
											  
											
												mtd: map: fixed bug in 64-bit systems
Hardware:
	CPU: XLP832,the 64-bit OS
	NOR Flash:S29GL128S 128M
Software:
	Kernel:2.6.32.41
	Filesystem:JFFS2
When writing files, errors appear:
	Write len 182  but return retlen 180
	Write of 182 bytes at 0x072c815c failed. returned -5, retlen 180
	Write len 186  but return retlen 184
	Write of 186 bytes at 0x072caff4 failed. returned -5, retlen 184
These errors exist only in 64-bit systems,not in 32-bit systems. After analysis, we
found that the left shift operation is wrong in map_word_load_partial. For instance:
	unsigned char buf[3] ={0x9e,0x3a,0xea};
	map_bankwidth(map) is 4;
	for (i=0; i < 3; i++) {
		int bitpos;
		bitpos = (map_bankwidth(map)-1-i)*8;
		orig.x[0] &= ~(0xff << bitpos);
		orig.x[0] |= buf[i] << bitpos;
	}
The value of orig.x[0] is expected to be 0x9e3aeaff, but in this situation(64-bit
System) we'll get the wrong value of 0xffffffff9e3aeaff due to the 64-bit sign
extension:
buf[i] is defined as "unsigned char" and the left-shift operation will convert it
to the type of "signed int", so when left-shift buf[i] by 24 bits, the final result
will get the wrong value: 0xffffffff9e3aeaff.
If the left-shift bits are less than 24, then sign extension will not occur. Whereas
the bankwidth of the nor flash we used is 4, therefore this BUG emerges.
Signed-off-by: Pang Xunlei <pang.xunlei@zte.com.cn>
Signed-off-by: Zhang Yi <zhang.yi20@zte.com.cn>
Signed-off-by: Lu Zhongjun <lu.zhongjun@zte.com.cn>
Cc: <stable@vger.kernel.org>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
											
										 
											2013-08-22 19:32:38 +08:00
										 |  |  | 		r.x[0] = (1UL << bw) - 1; | 
					
						
							| 
									
										
										
										
											2005-05-24 20:50:18 +02:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		for (i=0; i<map_words(map); i++) | 
					
						
							|  |  |  | 			r.x[i] = ~0UL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return r; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-02-08 17:11:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-31 21:06:34 +04:00
										 |  |  | 	map_word r; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (map_bankwidth_is_1(map)) | 
					
						
							|  |  |  | 		r.x[0] = __raw_readb(map->virt + ofs); | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_2(map)) | 
					
						
							|  |  |  | 		r.x[0] = __raw_readw(map->virt + ofs); | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_4(map)) | 
					
						
							|  |  |  | 		r.x[0] = __raw_readl(map->virt + ofs); | 
					
						
							|  |  |  | #if BITS_PER_LONG >= 64
 | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_8(map)) | 
					
						
							|  |  |  | 		r.x[0] = __raw_readq(map->virt + ofs); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_large(map)) | 
					
						
							|  |  |  | 		memcpy_fromio(r.x, map->virt+ofs, map->bankwidth); | 
					
						
							| 
									
										
										
										
											2010-04-10 11:18:58 -07:00
										 |  |  | 	else | 
					
						
							|  |  |  | 		BUG(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (map_bankwidth_is_1(map)) | 
					
						
							|  |  |  | 		__raw_writeb(datum.x[0], map->virt + ofs); | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_2(map)) | 
					
						
							|  |  |  | 		__raw_writew(datum.x[0], map->virt + ofs); | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_4(map)) | 
					
						
							|  |  |  | 		__raw_writel(datum.x[0], map->virt + ofs); | 
					
						
							|  |  |  | #if BITS_PER_LONG >= 64
 | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_8(map)) | 
					
						
							|  |  |  | 		__raw_writeq(datum.x[0], map->virt + ofs); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	else if (map_bankwidth_is_large(map)) | 
					
						
							|  |  |  | 		memcpy_toio(map->virt+ofs, datum.x, map->bankwidth); | 
					
						
							| 
									
										
										
										
											2013-01-31 21:06:34 +04:00
										 |  |  | 	else | 
					
						
							|  |  |  | 		BUG(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	mb(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (map->cached) | 
					
						
							|  |  |  | 		memcpy(to, (char *)map->cached + from, len); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		memcpy_fromio(to, map->virt + from, len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	memcpy_toio(map->virt + to, from, len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_MTD_COMPLEX_MAPPINGS
 | 
					
						
							|  |  |  | #define map_read(map, ofs) (map)->read(map, ofs)
 | 
					
						
							|  |  |  | #define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
 | 
					
						
							|  |  |  | #define map_write(map, datum, ofs) (map)->write(map, datum, ofs)
 | 
					
						
							|  |  |  | #define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extern void simple_map_init(struct map_info *); | 
					
						
							|  |  |  | #define map_is_linear(map) (map->phys != NO_XIP)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define map_read(map, ofs) inline_map_read(map, ofs)
 | 
					
						
							|  |  |  | #define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
 | 
					
						
							|  |  |  | #define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
 | 
					
						
							|  |  |  | #define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
 | 
					
						
							| 
									
										
										
										
											2005-02-16 15:55:03 +00:00
										 |  |  | #define map_is_linear(map) ({ (void)(map); 1; })
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif /* __LINUX_MTD_MAP_H__ */
 |