1/*
2 * flashutl.c - Flash Read/write/Erase routines
3 *
4 * Copyright 2007, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
11 *
12 * $Id: flashutl.c,v 1.1.1.1 2008/10/15 03:31:34 james26_jang Exp $
13 */
14
15#include <typedefs.h>
16#include <osl.h>
17
18#define DECLARE_FLASHES
19#include <bcmutils.h>
20#include <sbutils.h>
21#include <sbconfig.h>
22#include <flash.h>
23#include <sflash.h>
24#include <flashutl.h>
25#include <bcmnvram.h>
26
27#define DPRINT(x)
28
29#define ERR2	0x30 /* Mask for err UNUSED */
30#define DONE	0x80 /* Mask for done */
31#define WBUFSIZE 32  /* Write Buffer size */
32#define FLASH_TRIES 4000000 /* retry count */
33#define CMD_ADDR ((unsigned long)0xFFFFFFFF)
34
35/* 'which' param for block() */
36#define BLOCK_BASE	0  /* Base of block */
37#define BLOCK_LIM	1  /* Limit of block */
38
39#define FLASH_ADDR(off) ((unsigned long)flashutl_base + (off))
40
41/* Local vars */
42static sb_t *sbh = NULL;
43static chipcregs_t *cc = NULL;
44
45/* Global vars */
46uint8		*flashutl_base	= NULL;
47flash_desc_t	*flashutl_desc	= NULL;
48flash_cmds_t	*flashutl_cmd	= NULL;
49uint8 flashutl_wsz = sizeof(uint16);
50
51static void		scmd(uint16 cmd, unsigned long off);
52static void		cmd(uint16 cmd, unsigned long off);
53static void		flash_reset(void);
54static int		flash_poll(unsigned long off, uint16 data);
55static unsigned long	block(unsigned long addr, int which);
56static int	flash_eraseblk(unsigned long off);
57static int	flash_write(unsigned long off, uint8 *src, uint nbytes);
58static uint16 INLINE flash_readword(unsigned long addr);
59static void INLINE flash_writeword(unsigned long addr, uint16 data);
60
61int sysFlashErase(uint off, unsigned int numbytes);
62
63/* Read the flash ID and set the globals */
64int
65sysFlashInit(char *flash_str)
66{
67	osl_t *osh;
68	uint32 fltype = PFLASH;
69	uint16 flash_vendid = 0;
70	uint16 flash_devid = 0;
71	int idx;
72	struct sflash *sflash;
73
74	/*
75	 * Check for serial flash.
76	 */
77	sbh = sb_kattach(SB_OSH);
78	ASSERT(sbh);
79
80	osh = sb_osh(sbh);
81
82	flashutl_base = (uint8*)OSL_UNCACHED(SB_FLASH1);
83	flashutl_wsz = sizeof(uint16);
84	cc = (chipcregs_t *)sb_setcore(sbh, SB_CC, 0);
85	if (cc) {
86		flashutl_base = (uint8*)OSL_UNCACHED(SB_FLASH2);
87		flashutl_wsz = (R_REG(osh, &cc->flash_config) & CC_CFG_DS) ?
88		        sizeof(uint16) : sizeof(uint8);
89		/* Select SFLASH ? */
90		fltype = R_REG(osh, &cc->capabilities) & CC_CAP_FLASH_MASK;
91		if (fltype == SFLASH_ST || fltype == SFLASH_AT) {
92			sflash = sflash_init(sbh, cc);
93			flashutl_cmd = &sflash_cmd_t;
94			flashutl_desc = &sflash_desc;
95			flashutl_desc->size = sflash->size;
96			if (flash_str)
97				sprintf(flash_str, "SFLASH %d kB", sflash->size/1024);
98			return (0);
99		}
100	}
101
102	ASSERT(flashutl_wsz == sizeof(uint8) || flashutl_wsz == sizeof(uint16));
103
104	/*
105	 * Parallel flash support
106	 *  Some flashes have different unlock addresses, try each it turn
107	 */
108	for (idx = 0;
109	     fltype == PFLASH && idx < ARRAYSIZE(flash_cmds);
110	     idx ++) {
111		flashutl_cmd = &flash_cmds[idx];
112		if (flashutl_cmd->type == OLD)
113			continue;
114
115		if (flashutl_cmd->read_id)
116			cmd(flashutl_cmd->read_id, CMD_ADDR);
117
118#ifdef MIPSEB
119		flash_vendid = flash_readword(FLASH_ADDR(2));
120		flash_devid = flash_readword(FLASH_ADDR(0));
121#else
122		flash_vendid = flash_readword(FLASH_ADDR(0));
123		flash_devid = flash_readword(FLASH_ADDR(2));
124#endif /* MIPSEB */
125
126		/* Funky AMD, uses 3 byte device ID so use first byte (4th addr) to
127		 * identify it is a 3-byte ID and use the next two bytes (5th & 6th addr)
128		 * to form a word for unique identification of format xxyy, where
129		 * xx = 5th addr and yy = 6th addr
130		 */
131		if ((flash_vendid == 1) && (flash_devid == 0x227e)) {
132			/* Get real devid */
133			uint16 flash_devid_5th;
134#ifdef MIPSEB
135			flash_devid_5th = flash_readword(FLASH_ADDR(0x1e)) << 8;
136			flash_devid = (flash_readword(FLASH_ADDR(0x1c)) & 0xff) | flash_devid_5th;
137#else
138			flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)) << 8;
139			flash_devid = (flash_readword(FLASH_ADDR(0x1e)) & 0xff) | flash_devid_5th;
140#endif /* MIPSEB */
141		}
142
143		flashutl_desc = flashes;
144		while (flashutl_desc->mfgid != 0 &&
145		       !(flashutl_desc->mfgid == flash_vendid &&
146		         flashutl_desc->devid == flash_devid)) {
147			flashutl_desc++;
148		}
149		if (flashutl_desc->mfgid != 0)
150			break;
151	}
152
153	if (flashutl_desc->mfgid == 0) {
154		flashutl_desc = NULL;
155		flashutl_cmd = NULL;
156	} else {
157		flashutl_cmd = flash_cmds;
158		while (flashutl_cmd->type != 0 && flashutl_cmd->type != flashutl_desc->type)
159			flashutl_cmd++;
160		if (flashutl_cmd->type == 0)
161			flashutl_cmd = NULL;
162	}
163
164	if (flashutl_cmd != NULL) {
165		flash_reset();
166	}
167
168	if (flashutl_desc == NULL) {
169		if (flash_str)
170			sprintf(flash_str, "UNKNOWN 0x%x 0x%x", flash_vendid, flash_devid);
171		DPRINT(("Flash type UNKNOWN\n"));
172		return 1;
173	}
174
175	if (flash_str)
176		strcpy(flash_str, flashutl_desc->desc);
177	DPRINT(("Flash type \"%s\"\n", flashutl_desc->desc));
178
179	return 0;
180}
181
182static int
183flash_eraseblk(unsigned long addr)
184{
185	unsigned long a;
186	uint16 st;
187
188	a = (unsigned long)addr;
189	if (a >= flashutl_desc->size)
190		return 1;
191
192	a = block(a, BLOCK_BASE);
193
194	/* Ensure blocks are unlocked (for intel chips) */
195	if (flashutl_cmd->type == BSC) {
196		scmd((unsigned char)INTEL_UNLOCK1, a);
197		scmd((unsigned char)INTEL_UNLOCK2, a);
198	}
199
200	if (flashutl_cmd->pre_erase)
201		cmd(flashutl_cmd->pre_erase, CMD_ADDR);
202	if (flashutl_cmd->erase_block)
203		cmd(flashutl_cmd->erase_block, a);
204	if (flashutl_cmd->confirm)
205		scmd(flashutl_cmd->confirm, a);
206
207	if (flashutl_wsz == sizeof(uint8))
208		st = flash_poll(a, 0xff);
209	else
210		st = flash_poll(a, 0xffff);
211
212	flash_reset();
213
214	if (st) {
215		DPRINT(("Erase of block 0x%08lx-0x%08lx failed\n",
216			a, block((unsigned long)addr, BLOCK_LIM)));
217		return st;
218	}
219
220	DPRINT(("Erase of block 0x%08lx-0x%08lx done\n", a, block((unsigned long)addr, BLOCK_LIM)));
221
222	return 0;
223}
224
225static int
226flash_write(unsigned long off, uint8 *src, uint nbytes)
227{
228	uint8 *dest;
229	uint16 st, data;
230	uint i, len;
231
232	ASSERT(flashutl_desc != NULL);
233
234	if (off >= flashutl_desc->size)
235		return 1;
236
237	ASSERT(!(off & (flashutl_wsz - 1)));
238
239	dest = (uint8*)FLASH_ADDR(off);
240	st = 0;
241
242	while (nbytes) {
243		if ((flashutl_desc->type == SCS) &&
244		    flashutl_cmd->write_buf &&
245		    ((off & (WBUFSIZE - 1)) == 0)) {
246			/* issue write command */
247			if (flashutl_cmd->write_buf)
248				cmd(flashutl_cmd->write_buf, off);
249			if ((st = flash_poll(off, DONE)))
250				continue;
251
252			len = MIN(nbytes, WBUFSIZE);
253
254#ifndef MIPSEB
255			/* write (length - 1) */
256			cmd(len / sizeof(uint16) - 1, off);
257
258			/* write data */
259			for (i = 0; i < len; i += sizeof(uint16),
260			             dest += sizeof(uint16), src += sizeof(uint16))
261				*(uint16 *)dest = *(uint16 *)src;
262#else
263			/*
264			 * BCM4710 endianness is word consistent but
265			 * byte/short scrambled. This write buffer
266			 * mechanism appears to be sensitive to the
267			 * order of the addresses hence we need to
268			 * unscramble them. We may also need to pad
269			 * the source with two bytes of 0xffff in case
270			 * an odd number of shorts are presented.
271			 */
272
273			/* write (padded length - 1) */
274			cmd((ROUNDUP(len, sizeof(uint32)) / sizeof(uint16)) - 1, off);
275
276			/* write data (plus pad if necessary) */
277			for (i = 0; i < ROUNDUP(len, sizeof(uint32)); i += sizeof(uint32),
278			             dest += sizeof(uint32), src += sizeof(uint32)) {
279				*((uint16 *)dest + 1) = ((i + sizeof(uint16)) < len) ?
280				        *((uint16 *)src + 1) : 0xffff;
281				*(uint16 *)dest = *(uint16 *)src;
282			}
283#endif /* MIPSEB */
284
285			/* write confirm */
286			if (flashutl_cmd->confirm)
287				cmd(flashutl_cmd->confirm, off);
288
289			if ((st = flash_poll(off, DONE)))
290				break;
291		} else {
292			/* issue write command */
293			if (flashutl_cmd->write_word)
294				cmd(flashutl_cmd->write_word, CMD_ADDR);
295
296			/* write data */
297			data = flash_readword((unsigned long)src);
298			flash_writeword((unsigned long)dest, data);
299
300			/* poll for done */
301			if ((st = flash_poll(off, data)))
302				break;
303
304			len = MIN(nbytes, flashutl_wsz);
305			dest += len;
306			src += len;
307		}
308
309		nbytes -= len;
310		off += len;
311	}
312
313	flash_reset();
314
315	return st;
316}
317
318static uint16 INLINE
319flash_readword(unsigned long addr)
320{
321	if (flashutl_wsz == sizeof(uint8))
322		return *(uint8*)addr;
323	else
324		return *(uint16*)addr;
325}
326
327static void INLINE
328flash_writeword(unsigned long addr, uint16 data)
329{
330	if (flashutl_wsz == sizeof(uint8))
331		*(uint8*)addr = (uint8)data;
332	else
333		*(uint16*)addr = data;
334}
335
336/* Writes a single command to the flash. */
337static void
338scmd(uint16 cmd, unsigned long off)
339{
340	/*  cmd |= cmd << 8; */
341
342	flash_writeword(FLASH_ADDR(off), cmd);
343}
344
345/* Writes a command to flash, performing an unlock if needed. */
346static void
347cmd(uint16 cmd, unsigned long off)
348{
349	int i;
350	unlock_cmd_t *ul = NULL;
351
352	ASSERT(flashutl_cmd != NULL);
353
354	switch (flashutl_cmd->type) {
355	case AMD:
356		ul = &unlock_cmd_amd;
357		break;
358	case SST:
359		ul = &unlock_cmd_sst;
360		break;
361	default:
362		break;
363	}
364
365	if (flashutl_cmd->need_unlock) {
366		ASSERT(ul);
367		for (i = 0; i < UNLOCK_CMD_WORDS; i++)
368			flash_writeword(FLASH_ADDR(ul->addr[i]), ul->cmd[i]);
369	}
370
371	/* cmd |= cmd << 8; */
372
373	if (off == CMD_ADDR) {
374		switch (flashutl_cmd->type) {
375		case AMD:
376			off = AMD_CMD;
377			break;
378		case SST:
379			off = SST_CMD;
380			break;
381		default:
382			off = 0;
383			break;
384		}
385	}
386
387#ifdef MIPSEB
388	off ^= 2;
389#endif /* MIPSEB */
390
391	flash_writeword(FLASH_ADDR(off), cmd);
392}
393
394static void
395flash_reset()
396{
397	ASSERT(flashutl_desc != NULL);
398
399	if (flashutl_cmd->clear_csr)
400		scmd(flashutl_cmd->clear_csr, 0);
401	if (flashutl_cmd->read_array)
402		scmd(flashutl_cmd->read_array, 0);
403}
404
405static int
406flash_poll(unsigned long off, uint16 data)
407{
408	unsigned long addr;
409	int cnt = FLASH_TRIES;
410	uint16 st;
411
412	ASSERT(flashutl_desc != NULL);
413
414	if (flashutl_desc->type == AMD || flashutl_desc->type == SST) {
415		/* AMD style poll checkes the address being written */
416		addr = FLASH_ADDR(off);
417		while ((st = flash_readword(addr)) != data && cnt != 0)
418			cnt--;
419		if (cnt == 0) {
420			DPRINT(("flash_poll: timeout, off %lx, read 0x%x, expected 0x%x\n",
421			        off, st, data));
422			return -1;
423		}
424	} else {
425		/* INTEL style poll is at second word of the block being written */
426		addr = FLASH_ADDR(block(off, BLOCK_BASE)+sizeof(uint16));
427		while (((st = flash_readword(addr)) & DONE) == 0 && cnt != 0)
428			cnt--;
429		if (cnt == 0) {
430			DPRINT(("flash_poll: timeout, error status = 0x%x\n", st));
431			return -1;
432		}
433	}
434
435	return 0;
436}
437
438static unsigned long
439block(unsigned long addr, int which)
440{
441	unsigned long b, l, sb;
442	uint* sblocks;
443	int i;
444
445	ASSERT(flashutl_desc != NULL);
446
447	ASSERT(addr < (unsigned long)flashutl_desc->size);
448
449	b = addr / flashutl_desc->bsize;
450	/* check for an address a full size block */
451	if (b >= flashutl_desc->ff && b <= flashutl_desc->lf) {
452		if (which == BLOCK_LIM) b++;
453		return (b * flashutl_desc->bsize);
454	}
455
456	/* search for the sub-block */
457	if (flashutl_desc->ff == 0) {
458		/* sub blocks are at the end of the flash */
459		sb = flashutl_desc->bsize * (flashutl_desc->lf + 1);
460	} else {
461		/* sub blocks are at the start of the flash */
462		sb = 0;
463	}
464
465	sblocks = flashutl_desc->subblocks;
466	for (i = 0; i < flashutl_desc->nsub; i++) {
467		b = sb + sblocks[i];
468		l = sb + sblocks[i+1];
469		if (addr >= b && addr < l) {
470			if (which == BLOCK_BASE)
471				return b;
472			else
473				return l;
474		}
475	}
476
477	return 0;
478}
479
480void
481nvWrite(unsigned short *data, unsigned int len)
482{
483	uint off = flashutl_desc->size - NVRAM_SPACE;
484	sysFlashWrite(off, (uchar*)data, len);
485}
486
487void
488nvWriteChars(unsigned char *data, unsigned int len)
489{
490	uint off = flashutl_desc->size - NVRAM_SPACE;
491	int err;
492
493	if (flashutl_cmd->type == SFLASH)
494		err = sflash_commit(sbh, cc, off, len, data);
495	else /* PFLASH */
496		err = flash_write(off, data, len);
497
498	if (err)
499		DPRINT(("nvWriteChars failed\n"));
500	else
501		DPRINT(("nvWriteChars succeeded\n"));
502}
503
504int
505sysFlashErase(uint off, unsigned int numbytes)
506{
507	unsigned long end = off + numbytes;
508	int err = 0;
509
510	if (flashutl_cmd->type == SFLASH) {
511		err = sflash_commit(sbh, cc, off, numbytes, NULL);
512	} else {
513		while (off < end) {
514			err = flash_eraseblk(off);
515			if (err)
516				break;
517			off = block(off, BLOCK_LIM);
518		}
519	}
520
521	if (err)
522		DPRINT(("Block erase at 0x%x failed\n", off));
523	else
524		DPRINT(("Done\n"));
525
526	return !err;
527}
528
529int
530sysFlashWrite(uint off, uchar *src, uint numbytes)
531{
532	int err;
533
534	DPRINT(("Writing 0x%x bytes to flash @0x%x ...\n", (unsigned int)numbytes, off));
535
536	if (flashutl_cmd->type == SFLASH)
537		err = sflash_commit(sbh, cc, off, numbytes, src);
538	else {
539		if (!sysFlashErase(off, numbytes))
540			return 0;
541		err = flash_write(off, src, numbytes);
542	}
543
544	if (err)
545		DPRINT(("Flash write failed\n"));
546	else
547		DPRINT(("Flash write succeeded\n"));
548
549	return !err;
550}
551
552int
553sysFlashRead(uint off, uchar *buf, uint numbytes)
554{
555	uint read, total_read = 0;
556
557	if (flashutl_cmd->type == SFLASH) {
558		while (numbytes) {
559			read = sflash_read(sbh, cc, off, numbytes, buf);
560			numbytes -= read;
561			buf += read;
562			off += read;
563			total_read += read;
564		}
565	} else {
566		ASSERT(!(off & (flashutl_wsz - 1)));
567		ASSERT(!(numbytes & (flashutl_wsz - 1)));
568
569		while (numbytes) {
570			flash_writeword((unsigned long)buf, flash_readword(FLASH_ADDR(off)));
571			numbytes -= flashutl_wsz;
572			buf += flashutl_wsz;
573			off += flashutl_wsz;
574			total_read += flashutl_wsz;
575		}
576	}
577
578	return (total_read);
579}
580