|  |  | 
|  | /* Overhauled routines for dealing with different mmap regions of flash */ | 
|  | /* $Id: map.h,v 1.54 2005/11/07 11:14:54 gleixner Exp $ */ | 
|  |  | 
|  | #ifndef __LINUX_MTD_MAP_H__ | 
|  | #define __LINUX_MTD_MAP_H__ | 
|  |  | 
|  | #include <linux/types.h> | 
|  | #include <linux/list.h> | 
|  | #include <linux/string.h> | 
|  |  | 
|  | #include <linux/mtd/compatmac.h> | 
|  |  | 
|  | #include <asm/unaligned.h> | 
|  | #include <asm/system.h> | 
|  | #include <asm/io.h> | 
|  |  | 
|  | #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 | 
|  | #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 | 
|  | #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. | 
|  | At which point, you fill in the mtd->module with your own module | 
|  | 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. | 
|  |  | 
|  | The mtd->priv field will point to the struct map_info, and any further | 
|  | private data required by the chip driver is linked from the | 
|  | mtd->priv->fldrv_priv field. This allows the map driver to get at | 
|  | the destructor function map->fldrv_destroy() when it's tired | 
|  | of living. | 
|  | */ | 
|  |  | 
|  | struct map_info { | 
|  | char *name; | 
|  | unsigned long size; | 
|  | resource_size_t phys; | 
|  | #define NO_XIP (-1UL) | 
|  |  | 
|  | void __iomem *virt; | 
|  | void *cached; | 
|  |  | 
|  | 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); | 
|  |  | 
|  | /* set_vpp() must handle being reentered -- enable, enable, disable | 
|  | must leave it enabled. */ | 
|  | void (*set_vpp)(struct map_info *, int); | 
|  |  | 
|  | unsigned long map_priv_1; | 
|  | unsigned long map_priv_2; | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #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) | 
|  | { | 
|  | map_word r; | 
|  |  | 
|  | 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); | 
|  |  | 
|  | 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); | 
|  | orig.x[0] |= buf[i-start] << bitpos; | 
|  | } | 
|  | } | 
|  | return orig; | 
|  | } | 
|  |  | 
|  | #if BITS_PER_LONG < 64 | 
|  | #define MAP_FF_LIMIT 4 | 
|  | #else | 
|  | #define MAP_FF_LIMIT 8 | 
|  | #endif | 
|  |  | 
|  | static inline map_word map_word_ff(struct map_info *map) | 
|  | { | 
|  | map_word r; | 
|  | int i; | 
|  |  | 
|  | if (map_bankwidth(map) < MAP_FF_LIMIT) { | 
|  | int bw = 8 * map_bankwidth(map); | 
|  | r.x[0] = (1 << bw) - 1; | 
|  | } else { | 
|  | for (i=0; i<map_words(map); i++) | 
|  | r.x[i] = ~0UL; | 
|  | } | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) | 
|  | { | 
|  | map_word r; | 
|  |  | 
|  | 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); | 
|  |  | 
|  | 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); | 
|  | 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)) | 
|  | #define map_is_linear(map) ({ (void)(map); 1; }) | 
|  |  | 
|  | #endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */ | 
|  |  | 
|  | #endif /* __LINUX_MTD_MAP_H__ */ |