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