^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * Common Flash Interface support:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Generic utility functions not dependent on command set
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2002 Red Hat
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2003 STMicroelectronics Limited
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * This code is covered by the GPL.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <asm/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <asm/byteorder.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/mtd/xip.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/mtd/mtd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/mtd/map.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/mtd/cfi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) void cfi_udelay(int us)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) if (us >= 1000) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) msleep(DIV_ROUND_UP(us, 1000));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) udelay(us);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) cond_resched();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) EXPORT_SYMBOL(cfi_udelay);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * Returns the command address according to the given geometry.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) struct map_info *map, struct cfi_private *cfi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) unsigned bankwidth = map_bankwidth(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) unsigned interleave = cfi_interleave(cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) unsigned type = cfi->device_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) uint32_t addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) addr = (cmd_ofs * type) * interleave;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) /* Modify the unlock address if we are in compatibility mode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) * For 16bit devices on 8 bit busses
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) * and 32bit devices on 16 bit busses
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) * set the low bit of the alternating bit sequence of the address.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) addr |= (type >> 1)*interleave;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) return addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) EXPORT_SYMBOL(cfi_build_cmd_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * Transforms the CFI command for the given geometry (bus width & interleave).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * It looks too long to be inline, but in the common case it should almost all
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * get optimised away.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) map_word val = { {0} };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) int wordwidth, words_per_bus, chip_mode, chips_per_word;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) unsigned long onecmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) /* We do it this way to give the compiler a fighting chance
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) of optimising away all the crap for 'bankwidth' larger than
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) an unsigned long, in the common case where that support is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) disabled */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) if (map_bankwidth_is_large(map)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) wordwidth = sizeof(unsigned long);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) wordwidth = map_bankwidth(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) words_per_bus = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) /* First, determine what the bit-pattern should be for a single
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) device, according to chip mode and endianness... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) switch (chip_mode) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) default: BUG();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) onecmd = cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) case 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) onecmd = cpu_to_cfi16(map, cmd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) case 4:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) onecmd = cpu_to_cfi32(map, cmd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) /* Now replicate it across the size of an unsigned long, or
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) just to the bus width as appropriate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) switch (chips_per_word) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) default: BUG();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) #if BITS_PER_LONG >= 64
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) case 8:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) onecmd |= (onecmd << (chip_mode * 32));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) case 4:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) onecmd |= (onecmd << (chip_mode * 16));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) case 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) onecmd |= (onecmd << (chip_mode * 8));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) /* And finally, for the multi-word case, replicate it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) in all words in the structure */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) for (i=0; i < words_per_bus; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) val.x[i] = onecmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) return val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) EXPORT_SYMBOL(cfi_build_cmd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) unsigned long cfi_merge_status(map_word val, struct map_info *map,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) struct cfi_private *cfi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) int wordwidth, words_per_bus, chip_mode, chips_per_word;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) unsigned long onestat, res = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) /* We do it this way to give the compiler a fighting chance
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) of optimising away all the crap for 'bankwidth' larger than
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) an unsigned long, in the common case where that support is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) disabled */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) if (map_bankwidth_is_large(map)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) wordwidth = sizeof(unsigned long);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) wordwidth = map_bankwidth(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) words_per_bus = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) onestat = val.x[0];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) /* Or all status words together */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) for (i=1; i < words_per_bus; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) onestat |= val.x[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) res = onestat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) switch(chips_per_word) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) default: BUG();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) #if BITS_PER_LONG >= 64
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) case 8:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) res |= (onestat >> (chip_mode * 32));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) case 4:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) res |= (onestat >> (chip_mode * 16));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) case 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) res |= (onestat >> (chip_mode * 8));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) /* Last, determine what the bit-pattern should be for a single
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) device, according to chip mode and endianness... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) switch (chip_mode) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) case 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) res = cfi16_to_cpu(map, res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) case 4:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) res = cfi32_to_cpu(map, res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) default: BUG();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) return res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) EXPORT_SYMBOL(cfi_merge_status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) * Sends a CFI command to a bank of flash for the given geometry.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) * Returns the offset in flash where the command was written.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) * If prev_val is non-null, it will be set to the value at the command address,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) * before the command was written.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) struct map_info *map, struct cfi_private *cfi,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) int type, map_word *prev_val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) map_word val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, map, cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) val = cfi_build_cmd(cmd, map, cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) if (prev_val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) *prev_val = map_read(map, addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) map_write(map, val, addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) return addr - base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) EXPORT_SYMBOL(cfi_send_gen_cmd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) int __xipram cfi_qry_present(struct map_info *map, __u32 base,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) struct cfi_private *cfi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) int osf = cfi->interleave * cfi->device_type; /* scale factor */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) map_word val[3];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) map_word qry[3];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) qry[0] = cfi_build_cmd('Q', map, cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) qry[1] = cfi_build_cmd('R', map, cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) qry[2] = cfi_build_cmd('Y', map, cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) val[0] = map_read(map, base + osf*0x10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) val[1] = map_read(map, base + osf*0x11);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) val[2] = map_read(map, base + osf*0x12);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) if (!map_word_equal(map, qry[0], val[0]))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) if (!map_word_equal(map, qry[1], val[1]))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) if (!map_word_equal(map, qry[2], val[2]))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) return 1; /* "QRY" found */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) EXPORT_SYMBOL_GPL(cfi_qry_present);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) struct cfi_private *cfi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (cfi_qry_present(map, base, cfi))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) /* QRY not found probably we deal with some odd CFI chips */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) /* Some revisions of some old Intel chips? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) if (cfi_qry_present(map, base, cfi))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) /* ST M29DW chips */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) if (cfi_qry_present(map, base, cfi))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) /* some old SST chips, e.g. 39VF160x/39VF320x */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) if (cfi_qry_present(map, base, cfi))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) /* SST 39VF640xB */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) cfi_send_gen_cmd(0xAA, 0x555, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) cfi_send_gen_cmd(0x55, 0x2AA, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) if (cfi_qry_present(map, base, cfi))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) /* QRY not found */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) EXPORT_SYMBOL_GPL(cfi_qry_mode_on);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) struct cfi_private *cfi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) /* M29W128G flashes require an additional reset command
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) when exit qry mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) EXPORT_SYMBOL_GPL(cfi_qry_mode_off);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) struct cfi_extquery *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) struct cfi_private *cfi = map->fldrv_priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) __u32 base = 0; // cfi->chips[0].start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) int ofs_factor = cfi->interleave * cfi->device_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) struct cfi_extquery *extp = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) if (!adr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) extp = kmalloc(size, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) if (!extp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) #ifdef CONFIG_MTD_XIP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) local_irq_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) /* Switch it into Query Mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) cfi_qry_mode_on(base, map, cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) /* Read in the Extended Query Table */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) for (i=0; i<size; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) ((unsigned char *)extp)[i] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) cfi_read_query(map, base+((adr+i)*ofs_factor));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) /* Make sure it returns to read mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) cfi_qry_mode_off(base, map, cfi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) #ifdef CONFIG_MTD_XIP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) (void) map_read(map, base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) xip_iprefetch();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) local_irq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) out: return extp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) EXPORT_SYMBOL(cfi_read_pri);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) struct map_info *map = mtd->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) struct cfi_private *cfi = map->fldrv_priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) struct cfi_fixup *f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) for (f=fixups; f->fixup; f++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) f->fixup(mtd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) EXPORT_SYMBOL(cfi_fixup);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) loff_t ofs, size_t len, void *thunk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) struct map_info *map = mtd->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) struct cfi_private *cfi = map->fldrv_priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) unsigned long adr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) int chipnum, ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) int i, first;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) struct mtd_erase_region_info *regions = mtd->eraseregions;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) /* Check that both start and end of the requested erase are
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) * aligned with the erasesize at the appropriate addresses.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) /* Skip all erase regions which are ended before the start of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) the requested erase. Actually, to save on the calculations,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) we skip to the first erase region which starts after the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) start of the requested erase, and then go back one.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) while (i < mtd->numeraseregions && ofs >= regions[i].offset)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) i--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) /* OK, now i is pointing at the erase region in which this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) erase request starts. Check the start of the requested
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) erase range is aligned with the erase size which is in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) effect here.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) if (ofs & (regions[i].erasesize-1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) /* Remember the erase region we start on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) first = i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) /* Next, check that the end of the requested erase is aligned
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) * with the erase region at that address.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) /* As before, drop back one to point at the region in which
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) the address actually falls
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) i--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) if ((ofs + len) & (regions[i].erasesize-1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) chipnum = ofs >> cfi->chipshift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) adr = ofs - (chipnum << cfi->chipshift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) i=first;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) while(len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) int size = regions[i].erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) adr += size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) ofs += size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) len -= size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) if (ofs == regions[i].offset + size * regions[i].numblocks)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) if (adr >> cfi->chipshift) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) adr = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) chipnum++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) if (chipnum >= cfi->numchips)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) EXPORT_SYMBOL(cfi_varsize_frob);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) MODULE_LICENSE("GPL");