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