xref: /openbmc/linux/drivers/mtd/chips/cfi_util.c (revision e8e0929d)
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 
11 #include <linux/module.h>
12 #include <linux/types.h>
13 #include <linux/kernel.h>
14 #include <asm/io.h>
15 #include <asm/byteorder.h>
16 
17 #include <linux/errno.h>
18 #include <linux/slab.h>
19 #include <linux/delay.h>
20 #include <linux/interrupt.h>
21 #include <linux/mtd/xip.h>
22 #include <linux/mtd/mtd.h>
23 #include <linux/mtd/map.h>
24 #include <linux/mtd/cfi.h>
25 #include <linux/mtd/compatmac.h>
26 
27 int __xipram cfi_qry_present(struct map_info *map, __u32 base,
28 			     struct cfi_private *cfi)
29 {
30 	int osf = cfi->interleave * cfi->device_type;	/* scale factor */
31 	map_word val[3];
32 	map_word qry[3];
33 
34 	qry[0] = cfi_build_cmd('Q', map, cfi);
35 	qry[1] = cfi_build_cmd('R', map, cfi);
36 	qry[2] = cfi_build_cmd('Y', map, cfi);
37 
38 	val[0] = map_read(map, base + osf*0x10);
39 	val[1] = map_read(map, base + osf*0x11);
40 	val[2] = map_read(map, base + osf*0x12);
41 
42 	if (!map_word_equal(map, qry[0], val[0]))
43 		return 0;
44 
45 	if (!map_word_equal(map, qry[1], val[1]))
46 		return 0;
47 
48 	if (!map_word_equal(map, qry[2], val[2]))
49 		return 0;
50 
51 	return 1; 	/* "QRY" found */
52 }
53 EXPORT_SYMBOL_GPL(cfi_qry_present);
54 
55 int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
56 			     struct cfi_private *cfi)
57 {
58 	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
59 	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
60 	if (cfi_qry_present(map, base, cfi))
61 		return 1;
62 	/* QRY not found probably we deal with some odd CFI chips */
63 	/* Some revisions of some old Intel chips? */
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 	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
67 	if (cfi_qry_present(map, base, cfi))
68 		return 1;
69 	/* ST M29DW chips */
70 	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
71 	cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
72 	if (cfi_qry_present(map, base, cfi))
73 		return 1;
74 	/* QRY not found */
75 	return 0;
76 }
77 EXPORT_SYMBOL_GPL(cfi_qry_mode_on);
78 
79 void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
80 			       struct cfi_private *cfi)
81 {
82 	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
83 	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
84 	/* M29W128G flashes require an additional reset command
85 	   when exit qry mode */
86 	if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
87 		cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
88 }
89 EXPORT_SYMBOL_GPL(cfi_qry_mode_off);
90 
91 struct cfi_extquery *
92 __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
93 {
94 	struct cfi_private *cfi = map->fldrv_priv;
95 	__u32 base = 0; // cfi->chips[0].start;
96 	int ofs_factor = cfi->interleave * cfi->device_type;
97 	int i;
98 	struct cfi_extquery *extp = NULL;
99 
100 	printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
101 	if (!adr)
102 		goto out;
103 
104 	extp = kmalloc(size, GFP_KERNEL);
105 	if (!extp) {
106 		printk(KERN_ERR "Failed to allocate memory\n");
107 		goto out;
108 	}
109 
110 #ifdef CONFIG_MTD_XIP
111 	local_irq_disable();
112 #endif
113 
114 	/* Switch it into Query Mode */
115 	cfi_qry_mode_on(base, map, cfi);
116 	/* Read in the Extended Query Table */
117 	for (i=0; i<size; i++) {
118 		((unsigned char *)extp)[i] =
119 			cfi_read_query(map, base+((adr+i)*ofs_factor));
120 	}
121 
122 	/* Make sure it returns to read mode */
123 	cfi_qry_mode_off(base, map, cfi);
124 
125 #ifdef CONFIG_MTD_XIP
126 	(void) map_read(map, base);
127 	xip_iprefetch();
128 	local_irq_enable();
129 #endif
130 
131  out:	return extp;
132 }
133 
134 EXPORT_SYMBOL(cfi_read_pri);
135 
136 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
137 {
138 	struct map_info *map = mtd->priv;
139 	struct cfi_private *cfi = map->fldrv_priv;
140 	struct cfi_fixup *f;
141 
142 	for (f=fixups; f->fixup; f++) {
143 		if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
144 		    ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
145 			f->fixup(mtd, f->param);
146 		}
147 	}
148 }
149 
150 EXPORT_SYMBOL(cfi_fixup);
151 
152 int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
153 				     loff_t ofs, size_t len, void *thunk)
154 {
155 	struct map_info *map = mtd->priv;
156 	struct cfi_private *cfi = map->fldrv_priv;
157 	unsigned long adr;
158 	int chipnum, ret = 0;
159 	int i, first;
160 	struct mtd_erase_region_info *regions = mtd->eraseregions;
161 
162 	if (ofs > mtd->size)
163 		return -EINVAL;
164 
165 	if ((len + ofs) > mtd->size)
166 		return -EINVAL;
167 
168 	/* Check that both start and end of the requested erase are
169 	 * aligned with the erasesize at the appropriate addresses.
170 	 */
171 
172 	i = 0;
173 
174 	/* Skip all erase regions which are ended before the start of
175 	   the requested erase. Actually, to save on the calculations,
176 	   we skip to the first erase region which starts after the
177 	   start of the requested erase, and then go back one.
178 	*/
179 
180 	while (i < mtd->numeraseregions && ofs >= regions[i].offset)
181 	       i++;
182 	i--;
183 
184 	/* OK, now i is pointing at the erase region in which this
185 	   erase request starts. Check the start of the requested
186 	   erase range is aligned with the erase size which is in
187 	   effect here.
188 	*/
189 
190 	if (ofs & (regions[i].erasesize-1))
191 		return -EINVAL;
192 
193 	/* Remember the erase region we start on */
194 	first = i;
195 
196 	/* Next, check that the end of the requested erase is aligned
197 	 * with the erase region at that address.
198 	 */
199 
200 	while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
201 		i++;
202 
203 	/* As before, drop back one to point at the region in which
204 	   the address actually falls
205 	*/
206 	i--;
207 
208 	if ((ofs + len) & (regions[i].erasesize-1))
209 		return -EINVAL;
210 
211 	chipnum = ofs >> cfi->chipshift;
212 	adr = ofs - (chipnum << cfi->chipshift);
213 
214 	i=first;
215 
216 	while(len) {
217 		int size = regions[i].erasesize;
218 
219 		ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
220 
221 		if (ret)
222 			return ret;
223 
224 		adr += size;
225 		ofs += size;
226 		len -= size;
227 
228 		if (ofs == regions[i].offset + size * regions[i].numblocks)
229 			i++;
230 
231 		if (adr >> cfi->chipshift) {
232 			adr = 0;
233 			chipnum++;
234 
235 			if (chipnum >= cfi->numchips)
236 			break;
237 		}
238 	}
239 
240 	return 0;
241 }
242 
243 EXPORT_SYMBOL(cfi_varsize_frob);
244 
245 MODULE_LICENSE("GPL");
246