1 /* 2 * Common Flash Interface support: 3 * Generic utility functions not dependant on command set 4 * 5 * Copyright (C) 2002 Red Hat 6 * Copyright (C) 2003 STMicroelectronics Limited 7 * 8 * This code is covered by the GPL. 9 * 10 * $Id: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $ 11 * 12 */ 13 14 #include <linux/module.h> 15 #include <linux/types.h> 16 #include <linux/kernel.h> 17 #include <linux/sched.h> 18 #include <asm/io.h> 19 #include <asm/byteorder.h> 20 21 #include <linux/errno.h> 22 #include <linux/slab.h> 23 #include <linux/delay.h> 24 #include <linux/interrupt.h> 25 #include <linux/mtd/xip.h> 26 #include <linux/mtd/mtd.h> 27 #include <linux/mtd/map.h> 28 #include <linux/mtd/cfi.h> 29 #include <linux/mtd/compatmac.h> 30 31 struct cfi_extquery * 32 __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) 33 { 34 struct cfi_private *cfi = map->fldrv_priv; 35 __u32 base = 0; // cfi->chips[0].start; 36 int ofs_factor = cfi->interleave * cfi->device_type; 37 int i; 38 struct cfi_extquery *extp = NULL; 39 40 printk(" %s Extended Query Table at 0x%4.4X\n", name, adr); 41 if (!adr) 42 goto out; 43 44 extp = kmalloc(size, GFP_KERNEL); 45 if (!extp) { 46 printk(KERN_ERR "Failed to allocate memory\n"); 47 goto out; 48 } 49 50 #ifdef CONFIG_MTD_XIP 51 local_irq_disable(); 52 #endif 53 54 /* Switch it into Query Mode */ 55 cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); 56 57 /* Read in the Extended Query Table */ 58 for (i=0; i<size; i++) { 59 ((unsigned char *)extp)[i] = 60 cfi_read_query(map, base+((adr+i)*ofs_factor)); 61 } 62 63 /* Make sure it returns to read mode */ 64 cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); 65 cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); 66 67 #ifdef CONFIG_MTD_XIP 68 (void) map_read(map, base); 69 asm volatile (".rep 8; nop; .endr"); 70 local_irq_enable(); 71 #endif 72 73 if (extp->MajorVersion != '1' || 74 (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { 75 printk(KERN_WARNING " Unknown %s Extended Query " 76 "version %c.%c.\n", name, extp->MajorVersion, 77 extp->MinorVersion); 78 kfree(extp); 79 extp = NULL; 80 } 81 82 out: return extp; 83 } 84 85 EXPORT_SYMBOL(cfi_read_pri); 86 87 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups) 88 { 89 struct map_info *map = mtd->priv; 90 struct cfi_private *cfi = map->fldrv_priv; 91 struct cfi_fixup *f; 92 93 for (f=fixups; f->fixup; f++) { 94 if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && 95 ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { 96 f->fixup(mtd, f->param); 97 } 98 } 99 } 100 101 EXPORT_SYMBOL(cfi_fixup); 102 103 int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, 104 loff_t ofs, size_t len, void *thunk) 105 { 106 struct map_info *map = mtd->priv; 107 struct cfi_private *cfi = map->fldrv_priv; 108 unsigned long adr; 109 int chipnum, ret = 0; 110 int i, first; 111 struct mtd_erase_region_info *regions = mtd->eraseregions; 112 113 if (ofs > mtd->size) 114 return -EINVAL; 115 116 if ((len + ofs) > mtd->size) 117 return -EINVAL; 118 119 /* Check that both start and end of the requested erase are 120 * aligned with the erasesize at the appropriate addresses. 121 */ 122 123 i = 0; 124 125 /* Skip all erase regions which are ended before the start of 126 the requested erase. Actually, to save on the calculations, 127 we skip to the first erase region which starts after the 128 start of the requested erase, and then go back one. 129 */ 130 131 while (i < mtd->numeraseregions && ofs >= regions[i].offset) 132 i++; 133 i--; 134 135 /* OK, now i is pointing at the erase region in which this 136 erase request starts. Check the start of the requested 137 erase range is aligned with the erase size which is in 138 effect here. 139 */ 140 141 if (ofs & (regions[i].erasesize-1)) 142 return -EINVAL; 143 144 /* Remember the erase region we start on */ 145 first = i; 146 147 /* Next, check that the end of the requested erase is aligned 148 * with the erase region at that address. 149 */ 150 151 while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset) 152 i++; 153 154 /* As before, drop back one to point at the region in which 155 the address actually falls 156 */ 157 i--; 158 159 if ((ofs + len) & (regions[i].erasesize-1)) 160 return -EINVAL; 161 162 chipnum = ofs >> cfi->chipshift; 163 adr = ofs - (chipnum << cfi->chipshift); 164 165 i=first; 166 167 while(len) { 168 int size = regions[i].erasesize; 169 170 ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); 171 172 if (ret) 173 return ret; 174 175 adr += size; 176 ofs += size; 177 len -= size; 178 179 if (ofs == regions[i].offset + size * regions[i].numblocks) 180 i++; 181 182 if (adr >> cfi->chipshift) { 183 adr = 0; 184 chipnum++; 185 186 if (chipnum >= cfi->numchips) 187 break; 188 } 189 } 190 191 return 0; 192 } 193 194 EXPORT_SYMBOL(cfi_varsize_frob); 195 196 MODULE_LICENSE("GPL"); 197