1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2007
4 * Michael Schwingen, <michael@schwingen.org>
5 *
6 * based in great part on jedec_probe.c from linux kernel:
7 * (C) 2000 Red Hat. GPL'd.
8 * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
9 */
10
11/* The DEBUG define must be before common to enable debugging */
12/*#define DEBUG*/
13
14#include <flash.h>
15#include <log.h>
16#include <asm/processor.h>
17#include <asm/io.h>
18#include <asm/byteorder.h>
19
20#define P_ID_AMD_STD CFI_CMDSET_AMD_LEGACY
21
22/* AMD */
23#define AM29DL800BB	0x22CB
24#define AM29DL800BT	0x224A
25
26#define AM29F400BB	0x22AB
27#define AM29F800BB	0x2258
28#define AM29F800BT	0x22D6
29#define AM29LV400BB	0x22BA
30#define AM29LV400BT	0x22B9
31#define AM29LV800BB	0x225B
32#define AM29LV800BT	0x22DA
33#define AM29LV160DT	0x22C4
34#define AM29LV160DB	0x2249
35#define AM29F017D	0x003D
36#define AM29F016D	0x00AD
37#define AM29F080	0x00D5
38#define AM29F040	0x00A4
39#define AM29LV040B	0x004F
40#define AM29F032B	0x0041
41#define AM29F002T	0x00B0
42
43/* SST */
44#define SST39LF800	0x2781
45#define SST39LF160	0x2782
46#define SST39VF1601	0x234b
47#define SST39LF512	0x00D4
48#define SST39LF010	0x00D5
49#define SST39LF020	0x00D6
50#define SST39LF040	0x00D7
51#define SST39SF010A	0x00B5
52#define SST39SF020A	0x00B6
53
54/* STM */
55#define STM29F400BB	0x00D6
56
57/* MXIC */
58#define MX29LV040	0x004F
59
60/* WINBOND */
61#define W39L040A	0x00D6
62
63/* AMIC */
64#define A29L040		0x0092
65
66/* EON */
67#define EN29LV040A	0x004F
68
69/*
70 * Unlock address sets for AMD command sets.
71 * Intel command sets use the MTD_UADDR_UNNECESSARY.
72 * Each identifier, except MTD_UADDR_UNNECESSARY, and
73 * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[].
74 * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure
75 * initialization need not require initializing all of the
76 * unlock addresses for all bit widths.
77 */
78enum uaddr {
79	MTD_UADDR_NOT_SUPPORTED = 0,	/* data width not supported */
80	MTD_UADDR_0x0555_0x02AA,
81	MTD_UADDR_0x0555_0x0AAA,
82	MTD_UADDR_0x5555_0x2AAA,
83	MTD_UADDR_0x0AAA_0x0555,
84	MTD_UADDR_DONT_CARE,		/* Requires an arbitrary address */
85	MTD_UADDR_UNNECESSARY,		/* Does not require any address */
86};
87
88
89struct unlock_addr {
90	u32 addr1;
91	u32 addr2;
92};
93
94
95/*
96 * I don't like the fact that the first entry in unlock_addrs[]
97 * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore,
98 * should not be used.  The  problem is that structures with
99 * initializers have extra fields initialized to 0.  It is _very_
100 * desireable to have the unlock address entries for unsupported
101 * data widths automatically initialized - that means that
102 * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here
103 * must go unused.
104 */
105static const struct unlock_addr  unlock_addrs[] = {
106	[MTD_UADDR_NOT_SUPPORTED] = {
107		.addr1 = 0xffff,
108		.addr2 = 0xffff
109	},
110
111	[MTD_UADDR_0x0555_0x02AA] = {
112		.addr1 = 0x0555,
113		.addr2 = 0x02aa
114	},
115
116	[MTD_UADDR_0x0555_0x0AAA] = {
117		.addr1 = 0x0555,
118		.addr2 = 0x0aaa
119	},
120
121	[MTD_UADDR_0x5555_0x2AAA] = {
122		.addr1 = 0x5555,
123		.addr2 = 0x2aaa
124	},
125
126	[MTD_UADDR_0x0AAA_0x0555] = {
127		.addr1 = 0x0AAA,
128		.addr2 = 0x0555
129	},
130
131	[MTD_UADDR_DONT_CARE] = {
132		.addr1 = 0x0000,      /* Doesn't matter which address */
133		.addr2 = 0x0000       /* is used - must be last entry */
134	},
135
136	[MTD_UADDR_UNNECESSARY] = {
137		.addr1 = 0x0000,
138		.addr2 = 0x0000
139	}
140};
141
142
143struct amd_flash_info {
144	const __u16 mfr_id;
145	const __u16 dev_id;
146	const char *name;
147	const int DevSize;
148	const int NumEraseRegions;
149	const int CmdSet;
150	const __u8 uaddr[4];		/* unlock addrs for 8, 16, 32, 64 */
151	const ulong regions[6];
152};
153
154#define ERASEINFO(size,blocks) (size<<8)|(blocks-1)
155
156#define SIZE_64KiB  16
157#define SIZE_128KiB 17
158#define SIZE_256KiB 18
159#define SIZE_512KiB 19
160#define SIZE_1MiB   20
161#define SIZE_2MiB   21
162#define SIZE_4MiB   22
163#define SIZE_8MiB   23
164
165static const struct amd_flash_info jedec_table[] = {
166#ifdef CONFIG_SYS_FLASH_LEGACY_256Kx8
167	{
168		.mfr_id		= (u16)SST_MANUFACT,
169		.dev_id		= SST39LF020,
170		.name		= "SST 39LF020",
171		.uaddr		= {
172			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
173		},
174		.DevSize	= SIZE_256KiB,
175		.CmdSet		= P_ID_AMD_STD,
176		.NumEraseRegions= 1,
177		.regions	= {
178			ERASEINFO(0x01000,64),
179		}
180	},
181#endif
182#ifdef CONFIG_SYS_FLASH_LEGACY_512Kx8
183	{
184		.mfr_id		= (u16)AMD_MANUFACT,
185		.dev_id		= AM29LV040B,
186		.name		= "AMD AM29LV040B",
187		.uaddr		= {
188			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
189		},
190		.DevSize	= SIZE_512KiB,
191		.CmdSet		= P_ID_AMD_STD,
192		.NumEraseRegions= 1,
193		.regions	= {
194			ERASEINFO(0x10000,8),
195		}
196	},
197	{
198		.mfr_id		= (u16)SST_MANUFACT,
199		.dev_id		= SST39LF040,
200		.name		= "SST 39LF040",
201		.uaddr		= {
202			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
203		},
204		.DevSize	= SIZE_512KiB,
205		.CmdSet		= P_ID_AMD_STD,
206		.NumEraseRegions= 1,
207		.regions	= {
208			ERASEINFO(0x01000,128),
209		}
210	},
211	{
212		.mfr_id		= (u16)STM_MANUFACT,
213		.dev_id		= STM_ID_M29W040B,
214		.name		= "ST Micro M29W040B",
215		.uaddr		= {
216			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
217		},
218		.DevSize	= SIZE_512KiB,
219		.CmdSet		= P_ID_AMD_STD,
220		.NumEraseRegions= 1,
221		.regions	= {
222			ERASEINFO(0x10000,8),
223		}
224	},
225	{
226		.mfr_id		= (u16)MX_MANUFACT,
227		.dev_id		= MX29LV040,
228		.name		= "MXIC MX29LV040",
229		.uaddr		= {
230			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
231		},
232		.DevSize	= SIZE_512KiB,
233		.CmdSet		= P_ID_AMD_STD,
234		.NumEraseRegions= 1,
235		.regions	= {
236			ERASEINFO(0x10000, 8),
237		}
238	},
239	{
240		.mfr_id		= (u16)WINB_MANUFACT,
241		.dev_id		= W39L040A,
242		.name		= "WINBOND W39L040A",
243		.uaddr		= {
244			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
245		},
246		.DevSize	= SIZE_512KiB,
247		.CmdSet		= P_ID_AMD_STD,
248		.NumEraseRegions= 1,
249		.regions	= {
250			ERASEINFO(0x10000, 8),
251		}
252	},
253	{
254		.mfr_id		= (u16)AMIC_MANUFACT,
255		.dev_id		= A29L040,
256		.name		= "AMIC A29L040",
257		.uaddr		= {
258			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
259		},
260		.DevSize	= SIZE_512KiB,
261		.CmdSet		= P_ID_AMD_STD,
262		.NumEraseRegions= 1,
263		.regions	= {
264			ERASEINFO(0x10000, 8),
265		}
266	},
267	{
268		.mfr_id		= (u16)EON_MANUFACT,
269		.dev_id		= EN29LV040A,
270		.name		= "EON EN29LV040A",
271		.uaddr		= {
272			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
273		},
274		.DevSize	= SIZE_512KiB,
275		.CmdSet		= P_ID_AMD_STD,
276		.NumEraseRegions= 1,
277		.regions	= {
278			ERASEINFO(0x10000, 8),
279		}
280	},
281#endif
282#ifdef CONFIG_SYS_FLASH_LEGACY_512Kx16
283	{
284		.mfr_id		= (u16)AMD_MANUFACT,
285		.dev_id		= AM29F400BB,
286		.name		= "AMD AM29F400BB",
287		.uaddr		= {
288			[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
289		},
290		.DevSize	= SIZE_512KiB,
291		.CmdSet		= CFI_CMDSET_AMD_LEGACY,
292		.NumEraseRegions= 4,
293		.regions	= {
294			ERASEINFO(0x04000, 1),
295			ERASEINFO(0x02000, 2),
296			ERASEINFO(0x08000, 1),
297			ERASEINFO(0x10000, 7),
298		}
299	},
300	{
301		.mfr_id		= (u16)AMD_MANUFACT,
302		.dev_id		= AM29LV400BB,
303		.name		= "AMD AM29LV400BB",
304		.uaddr		= {
305			[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
306		},
307		.DevSize	= SIZE_512KiB,
308		.CmdSet		= CFI_CMDSET_AMD_LEGACY,
309		.NumEraseRegions= 4,
310		.regions	= {
311			ERASEINFO(0x04000,1),
312			ERASEINFO(0x02000,2),
313			ERASEINFO(0x08000,1),
314			ERASEINFO(0x10000,7),
315		}
316	},
317	{
318		.mfr_id		= (u16)AMD_MANUFACT,
319		.dev_id		= AM29LV800BB,
320		.name		= "AMD AM29LV800BB",
321		.uaddr		= {
322			[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
323		},
324		.DevSize	= SIZE_1MiB,
325		.CmdSet		= CFI_CMDSET_AMD_LEGACY,
326		.NumEraseRegions= 4,
327		.regions	= {
328			ERASEINFO(0x04000, 1),
329			ERASEINFO(0x02000, 2),
330			ERASEINFO(0x08000, 1),
331			ERASEINFO(0x10000, 15),
332		}
333	},
334	{
335		.mfr_id		= (u16)AMD_MANUFACT,
336		.dev_id		= AM29LV800BT,
337		.name		= "AMD AM29LV800BT",
338		.uaddr		= {
339			[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
340		},
341		.DevSize	= SIZE_1MiB,
342		.CmdSet		= CFI_CMDSET_AMD_LEGACY,
343		.NumEraseRegions= 4,
344		.regions	= {
345			ERASEINFO(0x10000, 15),
346			ERASEINFO(0x08000, 1),
347			ERASEINFO(0x02000, 2),
348			ERASEINFO(0x04000, 1),
349		}
350	},
351	{
352		.mfr_id		= (u16)MX_MANUFACT,
353		.dev_id		= AM29LV800BT,
354		.name		= "MXIC MX29LV800BT",
355		.uaddr		= {
356			[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
357		},
358		.DevSize	= SIZE_1MiB,
359		.CmdSet		= CFI_CMDSET_AMD_LEGACY,
360		.NumEraseRegions= 4,
361		.regions	= {
362			ERASEINFO(0x10000, 15),
363			ERASEINFO(0x08000, 1),
364			ERASEINFO(0x02000, 2),
365			ERASEINFO(0x04000, 1),
366		}
367	},
368	{
369		.mfr_id		= (u16)EON_ALT_MANU,
370		.dev_id		= AM29LV800BT,
371		.name		= "EON EN29LV800BT",
372		.uaddr		= {
373			[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
374		},
375		.DevSize	= SIZE_1MiB,
376		.CmdSet		= CFI_CMDSET_AMD_LEGACY,
377		.NumEraseRegions= 4,
378		.regions	= {
379			ERASEINFO(0x10000, 15),
380			ERASEINFO(0x08000, 1),
381			ERASEINFO(0x02000, 2),
382			ERASEINFO(0x04000, 1),
383		}
384	},
385	{
386		.mfr_id		= (u16)STM_MANUFACT,
387		.dev_id		= STM29F400BB,
388		.name		= "ST Micro M29F400BB",
389		.uaddr		= {
390			[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
391		},
392		.DevSize		= SIZE_512KiB,
393		.CmdSet			= CFI_CMDSET_AMD_LEGACY,
394		.NumEraseRegions	= 4,
395		.regions		= {
396			ERASEINFO(0x04000, 1),
397			ERASEINFO(0x02000, 2),
398			ERASEINFO(0x08000, 1),
399			ERASEINFO(0x10000, 7),
400		}
401	},
402#endif
403};
404
405static inline void fill_info(flash_info_t *info, const struct amd_flash_info *jedec_entry, ulong base)
406{
407	int i,j;
408	int sect_cnt;
409	int size_ratio;
410	int total_size;
411	enum uaddr uaddr_idx;
412
413	size_ratio = info->portwidth / info->chipwidth;
414
415	debug("Found JEDEC Flash: %s\n", jedec_entry->name);
416	info->vendor = jedec_entry->CmdSet;
417	/* Todo: do we need device-specific timeouts? */
418	info->erase_blk_tout = 30000;
419	info->buffer_write_tout = 1000;
420	info->write_tout = 100;
421	info->name = jedec_entry->name;
422
423	/* copy unlock addresses from device table to CFI info struct. This
424	   is just here because the addresses are in the table anyway - if
425	   the flash is not detected due to wrong unlock addresses,
426	   flash_detect_legacy would have to try all of them before we even
427	   get here. */
428	switch(info->chipwidth) {
429	case FLASH_CFI_8BIT:
430		uaddr_idx = jedec_entry->uaddr[0];
431		break;
432	case FLASH_CFI_16BIT:
433		uaddr_idx = jedec_entry->uaddr[1];
434		break;
435	case FLASH_CFI_32BIT:
436		uaddr_idx = jedec_entry->uaddr[2];
437		break;
438	default:
439		uaddr_idx = MTD_UADDR_NOT_SUPPORTED;
440		break;
441	}
442
443	debug("unlock address index %d\n", uaddr_idx);
444	info->addr_unlock1 = unlock_addrs[uaddr_idx].addr1;
445	info->addr_unlock2 = unlock_addrs[uaddr_idx].addr2;
446	debug("unlock addresses are 0x%lx/0x%lx\n",
447		info->addr_unlock1, info->addr_unlock2);
448
449	sect_cnt = 0;
450	total_size = 0;
451	for (i = 0; i < jedec_entry->NumEraseRegions; i++) {
452		ulong erase_region_size = jedec_entry->regions[i] >> 8;
453		ulong erase_region_count = (jedec_entry->regions[i] & 0xff) + 1;
454
455		total_size += erase_region_size * erase_region_count;
456		debug("erase_region_count = %ld erase_region_size = %ld\n",
457		       erase_region_count, erase_region_size);
458		for (j = 0; j < erase_region_count; j++) {
459			if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) {
460				printf("ERROR: too many flash sectors\n");
461				break;
462			}
463			info->start[sect_cnt] = base;
464			base += (erase_region_size * size_ratio);
465			sect_cnt++;
466		}
467	}
468	info->sector_count = sect_cnt;
469	info->size = total_size * size_ratio;
470}
471
472/*-----------------------------------------------------------------------
473 * match jedec ids against table. If a match is found, fill flash_info entry
474 */
475int jedec_flash_match(flash_info_t *info, ulong base)
476{
477	int ret = 0;
478	int i;
479	ulong mask = 0xFFFF;
480	if (info->chipwidth == 1)
481		mask = 0xFF;
482
483	for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {
484		if ((jedec_table[i].mfr_id & mask) == (info->manufacturer_id & mask) &&
485		    (jedec_table[i].dev_id & mask) == (info->device_id & mask)) {
486			fill_info(info, &jedec_table[i], base);
487			ret = 1;
488			break;
489		}
490	}
491	return ret;
492}
493