1/*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
3 *
4 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: sflash.c 345826 2012-07-19 06:34:55Z $
19 */
20
21#include <bcm_cfg.h>
22#include <typedefs.h>
23#include <osl.h>
24#include <bcmutils.h>
25#include <siutils.h>
26#include <hndsoc.h>
27#include <sbhndcpu.h>
28#include <sbchipc.h>
29#include <bcmdevs.h>
30#include <sflash.h>
31
32#ifdef BCMDBG
33#define	SFL_MSG(args)	printf args
34#else
35#define	SFL_MSG(args)
36#endif	/* BCMDBG */
37
38/* Private global state */
39static struct sflash sflash;
40
41/* Issue a serial flash command */
42static INLINE void
43sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode)
44{
45	W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode);
46	while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY);
47}
48
49static bool firsttime = TRUE;
50
51/* Initialize serial flash access */
52struct sflash *
53sflash_init(si_t *sih, chipcregs_t *cc)
54{
55	uint32 id, id2;
56	const char *name = "";
57	osl_t *osh;
58
59	ASSERT(sih);
60
61	osh = si_osh(sih);
62
63	bzero(&sflash, sizeof(sflash));
64
65	sflash.type = sih->cccaps & CC_CAP_FLASH_MASK;
66
67	switch (sflash.type) {
68	case SFLASH_ST:
69		/* Probe for ST chips */
70		name = "ST compatible";
71		sflash_cmd(osh, cc, SFLASH_ST_DP);
72		W_REG(osh, &cc->flashaddress, 0);
73		sflash_cmd(osh, cc, SFLASH_ST_RES);
74		id = R_REG(osh, &cc->flashdata);
75		sflash.blocksize = 64 * 1024;
76		switch (id) {
77		case 0x11:
78			/* ST M25P20 2 Mbit Serial Flash */
79			sflash.numblocks = 4;
80			break;
81		case 0x12:
82			/* ST M25P40 4 Mbit Serial Flash */
83			sflash.numblocks = 8;
84			break;
85		case 0x13:
86			sflash_cmd(osh, cc, SFLASH_MXIC_RDID);
87			id = R_REG(osh, &cc->flashdata);
88			if (id == SFLASH_MXIC_MFID) {
89				/* MXIC MX25L8006E 8 Mbit Serial Flash */
90				sflash.blocksize = 4 * 1024;
91				sflash.numblocks = 16 * 16;
92			} else  {
93				/* ST M25P80 8 Mbit Serial Flash */
94				sflash.numblocks = 16;
95			}
96			break;
97		case 0x14:
98			/* ST M25P16 16 Mbit Serial Flash */
99			sflash.numblocks = 32;
100			break;
101		case 0x15:
102			/* ST M25P32 32 Mbit Serial Flash */
103			sflash.numblocks = 64;
104			break;
105		case 0x16:
106			/* ST M25P64 64 Mbit Serial Flash */
107			sflash.numblocks = 128;
108			break;
109		case 0x17:
110			/* ST M25FL128 128 Mbit Serial Flash */
111			sflash.numblocks = 256;
112			break;
113		case 0xbf:
114			/* All of the following flashes are SST with
115			 * 4KB subsectors. Others should be added but
116			 * We'll have to revamp the way we identify them
117			 * since RES is not eough to disambiguate them.
118			 */
119			name = "SST";
120			sflash.blocksize = 4 * 1024;
121			W_REG(osh, &cc->flashaddress, 1);
122			sflash_cmd(osh, cc, SFLASH_ST_RES);
123			id2 = R_REG(osh, &cc->flashdata);
124			switch (id2) {
125			case 1:
126				/* SST25WF512 512 Kbit Serial Flash */
127				sflash.numblocks = 16;
128				break;
129			case 0x48:
130				/* SST25VF512 512 Kbit Serial Flash */
131				sflash.numblocks = 16;
132				break;
133			case 2:
134				/* SST25WF010 1 Mbit Serial Flash */
135				sflash.numblocks = 32;
136				break;
137			case 0x49:
138				/* SST25VF010 1 Mbit Serial Flash */
139				sflash.numblocks = 32;
140				break;
141			case 3:
142				/* SST25WF020 2 Mbit Serial Flash */
143				sflash.numblocks = 64;
144				break;
145			case 0x43:
146				/* SST25VF020 2 Mbit Serial Flash */
147				sflash.numblocks = 64;
148				break;
149			case 4:
150				/* SST25WF040 4 Mbit Serial Flash */
151				sflash.numblocks = 128;
152				break;
153			case 0x44:
154				/* SST25VF040 4 Mbit Serial Flash */
155				sflash.numblocks = 128;
156				break;
157			case 0x8d:
158				/* SST25VF040B 4 Mbit Serial Flash */
159				sflash.numblocks = 128;
160				break;
161			case 5:
162				/* SST25WF080 8 Mbit Serial Flash */
163				sflash.numblocks = 256;
164				break;
165			case 0x8e:
166				/* SST25VF080B 8 Mbit Serial Flash */
167				sflash.numblocks = 256;
168				break;
169			case 0x41:
170				/* SST25VF016 16 Mbit Serial Flash */
171				sflash.numblocks = 512;
172				break;
173			case 0x4a:
174				/* SST25VF032 32 Mbit Serial Flash */
175				sflash.numblocks = 1024;
176				break;
177			case 0x4b:
178				/* SST25VF064 64 Mbit Serial Flash */
179				sflash.numblocks = 2048;
180				break;
181			}
182			break;
183		}
184		break;
185
186	case SFLASH_AT:
187		/* Probe for Atmel chips */
188		name = "Atmel";
189		sflash_cmd(osh, cc, SFLASH_AT_STATUS);
190		id = R_REG(osh, &cc->flashdata) & 0x3c;
191		switch (id) {
192		case 0xc:
193			/* Atmel AT45DB011 1Mbit Serial Flash */
194			sflash.blocksize = 256;
195			sflash.numblocks = 512;
196			break;
197		case 0x14:
198			/* Atmel AT45DB021 2Mbit Serial Flash */
199			sflash.blocksize = 256;
200			sflash.numblocks = 1024;
201			break;
202		case 0x1c:
203			/* Atmel AT45DB041 4Mbit Serial Flash */
204			sflash.blocksize = 256;
205			sflash.numblocks = 2048;
206			break;
207		case 0x24:
208			/* Atmel AT45DB081 8Mbit Serial Flash */
209			sflash.blocksize = 256;
210			sflash.numblocks = 4096;
211			break;
212		case 0x2c:
213			/* Atmel AT45DB161 16Mbit Serial Flash */
214			sflash.blocksize = 512;
215			sflash.numblocks = 4096;
216			break;
217		case 0x34:
218			/* Atmel AT45DB321 32Mbit Serial Flash */
219			sflash.blocksize = 512;
220			sflash.numblocks = 8192;
221			break;
222		case 0x3c:
223			/* Atmel AT45DB642 64Mbit Serial Flash */
224			sflash.blocksize = 1024;
225			sflash.numblocks = 8192;
226			break;
227		}
228		break;
229	}
230
231	sflash.size = sflash.blocksize * sflash.numblocks;
232	sflash.phybase = SI_FLASH2;
233
234	if (firsttime)
235		printf("Found an %s serial flash with %d %dKB blocks; total size %dMB\n",
236		       name, sflash.numblocks, sflash.blocksize / 1024,
237		       sflash.size / (1024 * 1024));
238
239	firsttime = FALSE;
240	return sflash.size ? &sflash : NULL;
241}
242
243/* Read len bytes starting at offset into buf. Returns number of bytes read. */
244int
245sflash_read(si_t *sih, chipcregs_t *cc, uint offset, uint len, uchar *buf)
246{
247	uint8 *from, *to;
248	int cnt, i;
249
250	ASSERT(sih);
251
252	if (!len)
253		return 0;
254
255	if ((offset + len) > sflash.size)
256		return -22;
257
258	if ((len >= 4) && (offset & 3))
259		cnt = 4 - (offset & 3);
260	else if ((len >= 4) && ((uintptr)buf & 3))
261		cnt = 4 - ((uintptr)buf & 3);
262	else
263		cnt = len;
264
265	if (sih->ccrev == 12)
266		from = (uint8 *)OSL_UNCACHED((void *)SI_FLASH2 + offset);
267	else
268		from = (uint8 *)OSL_CACHED((void *)SI_FLASH2 + offset);
269	to = (uint8 *)buf;
270
271	if (cnt < 4) {
272		for (i = 0; i < cnt; i ++) {
273			/* Cannot use R_REG because in bigendian that will
274			 * xor the address and we don't want that here.
275			 */
276			*to = *from;
277			from ++;
278			to ++;
279		}
280		return cnt;
281	}
282
283	while (cnt >= 4) {
284		*(uint32 *)to = *(uint32 *)from;
285		from += 4;
286		to += 4;
287		cnt -= 4;
288	}
289
290	return (len - cnt);
291}
292
293/* Poll for command completion. Returns zero when complete. */
294int
295sflash_poll(si_t *sih, chipcregs_t *cc, uint offset)
296{
297	osl_t *osh;
298
299	ASSERT(sih);
300
301	osh = si_osh(sih);
302
303	if (offset >= sflash.size)
304		return -22;
305
306	switch (sflash.type) {
307	case SFLASH_ST:
308		/* Check for ST Write In Progress bit */
309		sflash_cmd(osh, cc, SFLASH_ST_RDSR);
310		return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP;
311	case SFLASH_AT:
312		/* Check for Atmel Ready bit */
313		sflash_cmd(osh, cc, SFLASH_AT_STATUS);
314		return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY);
315	}
316
317	return 0;
318}
319
320/* Write len bytes starting at offset into buf. Returns number of bytes
321 * written. Caller should poll for completion.
322 */
323#define	ST_RETRIES	3
324
325#ifdef	IL_BIGENDIAN
326#ifdef	BCMHND74K
327#define	GET_BYTE(ptr)	(*(uint8 *)((uint32)(ptr) ^ 7))
328#else	/* !74K, bcm33xx */
329#define	GET_BYTE(ptr)	(*(uint8 *)((uint32)(ptr) ^ 3))
330#endif	/* BCMHND74K */
331#else	/* !IL_BIGENDIAN */
332#define	GET_BYTE(ptr)	(*(ptr))
333#endif	/* IL_BIGENDIAN */
334
335int
336sflash_write(si_t *sih, chipcregs_t *cc, uint offset, uint length, const uchar *buffer)
337{
338	struct sflash *sfl;
339	uint off = offset, len = length;
340	const uint8 *buf = buffer;
341	uint8 data;
342	int ret = 0, ntry = 0;
343	bool is4712b0;
344	uint32 page, byte, mask;
345	osl_t *osh;
346
347	ASSERT(sih);
348
349	osh = si_osh(sih);
350
351	if (!len)
352		return 0;
353
354	sfl = &sflash;
355	if ((off + len) > sfl->size)
356		return -22;
357
358	switch (sfl->type) {
359	case SFLASH_ST:
360		is4712b0 = (CHIPID(sih->chip) == BCM4712_CHIP_ID) && (CHIPREV(sih->chiprev) == 3);
361		/* Enable writes */
362retry:		sflash_cmd(osh, cc, SFLASH_ST_WREN);
363		off = offset;
364		len = length;
365		buf = buffer;
366		ntry++;
367		if (is4712b0) {
368			mask = 1 << 14;
369			W_REG(osh, &cc->flashaddress, off);
370			data = GET_BYTE(buf);
371			buf++;
372			W_REG(osh, &cc->flashdata, data);
373			/* Set chip select */
374			OR_REG(osh, &cc->gpioout, mask);
375			/* Issue a page program with the first byte */
376			sflash_cmd(osh, cc, SFLASH_ST_PP);
377			ret = 1;
378			off++;
379			len--;
380			while (len > 0) {
381				if ((off & 255) == 0) {
382					/* Page boundary, drop cs and return */
383					AND_REG(osh, &cc->gpioout, ~mask);
384					OSL_DELAY(1);
385					if (!sflash_poll(sih, cc, off)) {
386						/* Flash rejected command */
387						if (ntry <= ST_RETRIES)
388							goto retry;
389						else
390							return -11;
391					}
392					return ret;
393				} else {
394					/* Write single byte */
395					data = GET_BYTE(buf);
396					buf++;
397					sflash_cmd(osh, cc, data);
398				}
399				ret++;
400				off++;
401				len--;
402			}
403			/* All done, drop cs */
404			AND_REG(osh, &cc->gpioout, ~mask);
405			OSL_DELAY(1);
406			if (!sflash_poll(sih, cc, off)) {
407				/* Flash rejected command */
408				if (ntry <= ST_RETRIES)
409					goto retry;
410				else
411					return -12;
412			}
413		} else if (sih->ccrev >= 20) {
414			W_REG(osh, &cc->flashaddress, off);
415			data = GET_BYTE(buf);
416			buf++;
417			W_REG(osh, &cc->flashdata, data);
418			/* Issue a page program with CSA bit set */
419			sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP);
420			ret = 1;
421			off++;
422			len--;
423			while (len > 0) {
424				if ((off & 255) == 0) {
425					/* Page boundary, poll droping cs and return */
426					W_REG(NULL, &cc->flashcontrol, 0);
427					OSL_DELAY(1);
428					if (sflash_poll(sih, cc, off) == 0) {
429						/* Flash rejected command */
430						SFL_MSG(("sflash: pp rejected, ntry: %d,"
431						         " off: %d/%d, len: %d/%d, ret:"
432						         "%d\n", ntry, off, offset, len,
433						         length, ret));
434						if (ntry <= ST_RETRIES)
435							goto retry;
436						else
437							return -11;
438					}
439					return ret;
440				} else {
441					/* Write single byte */
442					data = GET_BYTE(buf);
443					buf++;
444					sflash_cmd(osh, cc, SFLASH_ST_CSA | data);
445				}
446				ret++;
447				off++;
448				len--;
449			}
450			/* All done, drop cs & poll */
451			W_REG(NULL, &cc->flashcontrol, 0);
452			OSL_DELAY(1);
453			if (sflash_poll(sih, cc, off) == 0) {
454				/* Flash rejected command */
455				SFL_MSG(("sflash: pp rejected, ntry: %d, off: %d/%d,"
456				         " len: %d/%d, ret: %d\n",
457				         ntry, off, offset, len, length, ret));
458				if (ntry <= ST_RETRIES)
459					goto retry;
460				else
461					return -12;
462			}
463		} else {
464			ret = 1;
465			W_REG(osh, &cc->flashaddress, off);
466			data = GET_BYTE(buf);
467			buf++;
468			W_REG(osh, &cc->flashdata, data);
469			/* Page program */
470			sflash_cmd(osh, cc, SFLASH_ST_PP);
471		}
472		break;
473	case SFLASH_AT:
474		mask = sfl->blocksize - 1;
475		page = (off & ~mask) << 1;
476		byte = off & mask;
477		/* Read main memory page into buffer 1 */
478		if (byte || (len < sfl->blocksize)) {
479			W_REG(osh, &cc->flashaddress, page);
480			sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD);
481			/* 250 us for AT45DB321B */
482			SPINWAIT(sflash_poll(sih, cc, off), 1000);
483			ASSERT(!sflash_poll(sih, cc, off));
484		}
485		/* Write into buffer 1 */
486		for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
487			W_REG(osh, &cc->flashaddress, byte++);
488			W_REG(osh, &cc->flashdata, *buf++);
489			sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE);
490		}
491		/* Write buffer 1 into main memory page */
492		W_REG(osh, &cc->flashaddress, page);
493		sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM);
494		break;
495	}
496
497	return ret;
498}
499
500/* Erase a region. Returns number of bytes scheduled for erasure.
501 * Caller should poll for completion.
502 */
503int
504sflash_erase(si_t *sih, chipcregs_t *cc, uint offset)
505{
506	struct sflash *sfl;
507	osl_t *osh;
508
509	ASSERT(sih);
510
511	osh = si_osh(sih);
512
513	sfl = &sflash;
514	if (offset >= sfl->size)
515		return -22;
516
517	switch (sfl->type) {
518	case SFLASH_ST:
519		sflash_cmd(osh, cc, SFLASH_ST_WREN);
520		W_REG(osh, &cc->flashaddress, offset);
521		/* Newer flashes have "sub-sectors" which can be erased independently
522		 * with a new command: ST_SSE. The ST_SE command erases 64KB just as
523		 * before.
524		 */
525		sflash_cmd(osh, cc, (sfl->blocksize < (64 * 1024)) ? SFLASH_ST_SSE : SFLASH_ST_SE);
526		return sfl->blocksize;
527	case SFLASH_AT:
528		W_REG(osh, &cc->flashaddress, offset << 1);
529		sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE);
530		return sfl->blocksize;
531	}
532
533	return 0;
534}
535
536/*
537 * writes the appropriate range of flash, a NULL buf simply erases
538 * the region of flash
539 */
540int
541sflash_commit(si_t *sih, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
542{
543	struct sflash *sfl;
544	uchar *block = NULL, *cur_ptr, *blk_ptr;
545	uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
546	uint blk_offset, blk_len, copied;
547	int bytes, ret = 0;
548	osl_t *osh;
549
550	ASSERT(sih);
551
552	osh = si_osh(sih);
553
554	/* Check address range */
555	if (len <= 0)
556		return 0;
557
558	sfl = &sflash;
559	if ((offset + len) > sfl->size)
560		return -1;
561
562	blocksize = sfl->blocksize;
563	mask = blocksize - 1;
564
565	/* Allocate a block of mem */
566	if (!(block = MALLOC(osh, blocksize)))
567		return -1;
568
569	while (len) {
570		/* Align offset */
571		cur_offset = offset & ~mask;
572		cur_length = blocksize;
573		cur_ptr = block;
574
575		remainder = blocksize - (offset & mask);
576		if (len < remainder)
577			cur_retlen = len;
578		else
579			cur_retlen = remainder;
580
581		/* buf == NULL means erase only */
582		if (buf) {
583			/* Copy existing data into holding block if necessary */
584			if ((offset & mask) || (len < blocksize)) {
585				blk_offset = cur_offset;
586				blk_len = cur_length;
587				blk_ptr = cur_ptr;
588
589				/* Copy entire block */
590				while (blk_len) {
591					copied = sflash_read(sih, cc, blk_offset, blk_len, blk_ptr);
592					blk_offset += copied;
593					blk_len -= copied;
594					blk_ptr += copied;
595				}
596			}
597
598			/* Copy input data into holding block */
599			memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
600		}
601
602		/* Erase block */
603		if ((ret = sflash_erase(sih, cc, (uint) cur_offset)) < 0)
604			goto done;
605		while (sflash_poll(sih, cc, (uint) cur_offset));
606
607		/* buf == NULL means erase only */
608		if (!buf) {
609			offset += cur_retlen;
610			len -= cur_retlen;
611			continue;
612		}
613
614		/* Write holding block */
615		while (cur_length > 0) {
616			if ((bytes = sflash_write(sih, cc,
617			                          (uint) cur_offset,
618			                          (uint) cur_length,
619			                          (uchar *) cur_ptr)) < 0) {
620				ret = bytes;
621				goto done;
622			}
623			while (sflash_poll(sih, cc, (uint) cur_offset));
624			cur_offset += bytes;
625			cur_length -= bytes;
626			cur_ptr += bytes;
627		}
628
629		offset += cur_retlen;
630		len -= cur_retlen;
631		buf += cur_retlen;
632	}
633
634	ret = len;
635done:
636	if (block)
637		MFREE(osh, block, blocksize);
638	return ret;
639}
640