1/*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
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: sflash.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#include <bcmutils.h>
18#include <sbutils.h>
19#include <sbconfig.h>
20#include <sbchipc.h>
21#include <bcmdevs.h>
22#include <sflash.h>
23
24#define	SFL_MSG(args)
25
26/* Private global state */
27static struct sflash sflash;
28
29/* Issue a serial flash command */
30static INLINE void
31sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode)
32{
33	W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode);
34	while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY);
35}
36
37/* Initialize serial flash access */
38struct sflash *
39sflash_init(sb_t *sbh, chipcregs_t *cc)
40{
41	uint32 id, id2;
42	osl_t *osh;
43
44	ASSERT(sbh);
45
46	osh = sb_osh(sbh);
47
48	bzero(&sflash, sizeof(sflash));
49
50	sflash.type = sbh->cccaps & CC_CAP_FLASH_MASK;
51
52	switch (sflash.type) {
53	case SFLASH_ST:
54		/* Probe for ST chips */
55		sflash_cmd(osh, cc, SFLASH_ST_DP);
56		sflash_cmd(osh, cc, SFLASH_ST_RES);
57		id = R_REG(osh, &cc->flashdata);
58		switch (id) {
59		case 0x11:
60			/* ST M25P20 2 Mbit Serial Flash */
61			sflash.blocksize = 64 * 1024;
62			sflash.numblocks = 4;
63			break;
64		case 0x12:
65			/* ST M25P40 4 Mbit Serial Flash */
66			sflash.blocksize = 64 * 1024;
67			sflash.numblocks = 8;
68			break;
69		case 0x13:
70			/* ST M25P80 8 Mbit Serial Flash */
71			sflash.blocksize = 64 * 1024;
72			sflash.numblocks = 16;
73			break;
74		case 0x14:
75			/* ST M25P16 16 Mbit Serial Flash */
76			sflash.blocksize = 64 * 1024;
77			sflash.numblocks = 32;
78			break;
79		case 0x15:
80			/* ST M25P32 32 Mbit Serial Flash */
81			sflash.blocksize = 64 * 1024;
82			sflash.numblocks = 64;
83			break;
84		case 0x16:
85			/* ST M25P64 64 Mbit Serial Flash */
86			sflash.blocksize = 64 * 1024;
87			sflash.numblocks = 128;
88			break;
89		case 0xbf:
90			W_REG(osh, &cc->flashaddress, 1);
91			sflash_cmd(osh, cc, SFLASH_ST_RES);
92			id2 = R_REG(osh, &cc->flashdata);
93			if (id2 == 0x44) {
94				/* SST M25VF80 4 Mbit Serial Flash */
95				sflash.blocksize = 64 * 1024;
96				sflash.numblocks = 8;
97			}
98			break;
99		}
100		break;
101
102	case SFLASH_AT:
103		/* Probe for Atmel chips */
104		sflash_cmd(osh, cc, SFLASH_AT_STATUS);
105		id = R_REG(osh, &cc->flashdata) & 0x3c;
106		switch (id) {
107		case 0xc:
108			/* Atmel AT45DB011 1Mbit Serial Flash */
109			sflash.blocksize = 256;
110			sflash.numblocks = 512;
111			break;
112		case 0x14:
113			/* Atmel AT45DB021 2Mbit Serial Flash */
114			sflash.blocksize = 256;
115			sflash.numblocks = 1024;
116			break;
117		case 0x1c:
118			/* Atmel AT45DB041 4Mbit Serial Flash */
119			sflash.blocksize = 256;
120			sflash.numblocks = 2048;
121			break;
122		case 0x24:
123			/* Atmel AT45DB081 8Mbit Serial Flash */
124			sflash.blocksize = 256;
125			sflash.numblocks = 4096;
126			break;
127		case 0x2c:
128			/* Atmel AT45DB161 16Mbit Serial Flash */
129			sflash.blocksize = 512;
130			sflash.numblocks = 4096;
131			break;
132		case 0x34:
133			/* Atmel AT45DB321 32Mbit Serial Flash */
134			sflash.blocksize = 512;
135			sflash.numblocks = 8192;
136			break;
137		case 0x3c:
138			/* Atmel AT45DB642 64Mbit Serial Flash */
139			sflash.blocksize = 1024;
140			sflash.numblocks = 8192;
141			break;
142		}
143		break;
144	}
145
146	sflash.size = sflash.blocksize * sflash.numblocks;
147	return sflash.size ? &sflash : NULL;
148}
149
150/* Read len bytes starting at offset into buf. Returns number of bytes read. */
151int
152sflash_read(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, uchar *buf)
153{
154	uint8 *from, *to;
155	int cnt, i;
156	osl_t *osh;
157
158	ASSERT(sbh);
159
160	if (!len)
161		return 0;
162
163	if ((offset + len) > sflash.size)
164		return -22;
165
166	if ((len >= 4) && (offset & 3))
167		cnt = 4 - (offset & 3);
168	else if ((len >= 4) && ((uintptr)buf & 3))
169		cnt = 4 - ((uintptr)buf & 3);
170	else
171		cnt = len;
172
173	osh = sb_osh(sbh);
174
175	from = (uint8 *)(uintptr)OSL_UNCACHED(SB_FLASH2 + offset);
176	to = (uint8 *)buf;
177
178	if (cnt < 4) {
179		for (i = 0; i < cnt; i ++) {
180			*to = R_REG(osh, from);
181			from ++;
182			to ++;
183		}
184		return cnt;
185	}
186
187	while (cnt >= 4) {
188		*(uint32 *)to = R_REG(osh, (uint32 *)from);
189		from += 4;
190		to += 4;
191		cnt -= 4;
192	}
193
194	return (len - cnt);
195}
196
197/* Poll for command completion. Returns zero when complete. */
198int
199sflash_poll(sb_t *sbh, chipcregs_t *cc, uint offset)
200{
201	osl_t *osh;
202
203	ASSERT(sbh);
204
205	osh = sb_osh(sbh);
206
207	if (offset >= sflash.size)
208		return -22;
209
210	switch (sflash.type) {
211	case SFLASH_ST:
212		/* Check for ST Write In Progress bit */
213		sflash_cmd(osh, cc, SFLASH_ST_RDSR);
214		return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP;
215	case SFLASH_AT:
216		/* Check for Atmel Ready bit */
217		sflash_cmd(osh, cc, SFLASH_AT_STATUS);
218		return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY);
219	}
220
221	return 0;
222}
223
224/* Write len bytes starting at offset into buf. Returns number of bytes
225 * written. Caller should poll for completion.
226 */
227#define	ST_RETRIES	3
228
229int
230sflash_write(sb_t *sbh, chipcregs_t *cc, uint offset, uint length, const uchar *buffer)
231{
232	struct sflash *sfl;
233	uint off = offset, len = length;
234	const uchar *buf = buffer;
235	int ret = 0, try = 0;
236	bool is4712b0;
237	uint32 page, byte, mask;
238	osl_t *osh;
239
240	ASSERT(sbh);
241
242	osh = sb_osh(sbh);
243
244	if (!len)
245		return 0;
246
247	sfl = &sflash;
248	if ((off + len) > sfl->size)
249		return -22;
250
251	switch (sfl->type) {
252	case SFLASH_ST:
253		is4712b0 = (sbh->chip == BCM4712_CHIP_ID) && (sbh->chiprev == 3);
254		/* Enable writes */
255retry:		sflash_cmd(osh, cc, SFLASH_ST_WREN);
256		off = offset;
257		len = length;
258		buf = buffer;
259		try++;
260		if (is4712b0) {
261			mask = 1 << 14;
262			W_REG(osh, &cc->flashaddress, off);
263			W_REG(osh, &cc->flashdata, *buf++);
264			/* Set chip select */
265			OR_REG(osh, &cc->gpioout, mask);
266			/* Issue a page program with the first byte */
267			sflash_cmd(osh, cc, SFLASH_ST_PP);
268			ret = 1;
269			off++;
270			len--;
271			while (len > 0) {
272				if ((off & 255) == 0) {
273					/* Page boundary, drop cs and return */
274					AND_REG(osh, &cc->gpioout, ~mask);
275					OSL_DELAY(1);
276					if (!sflash_poll(sbh, cc, off)) {
277						/* Flash rejected command */
278						if (try <= ST_RETRIES)
279							goto retry;
280						else
281							return -11;
282					}
283					return ret;
284				} else {
285					/* Write single byte */
286					sflash_cmd(osh, cc, *buf++);
287				}
288				ret++;
289				off++;
290				len--;
291			}
292			/* All done, drop cs */
293			AND_REG(osh, &cc->gpioout, ~mask);
294			OSL_DELAY(1);
295			if (!sflash_poll(sbh, cc, off)) {
296				/* Flash rejected command */
297				if (try <= ST_RETRIES)
298					goto retry;
299				else
300					return -12;
301			}
302		} else if (sbh->ccrev >= 20) {
303			W_REG(NULL, &cc->flashaddress, off);
304			W_REG(NULL, &cc->flashdata, *buf++);
305			/* Issue a page program with CSA bit set */
306			sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP);
307			ret = 1;
308			off++;
309			len--;
310			while (len > 0) {
311				if ((off & 255) == 0) {
312					/* Page boundary, poll droping cs and return */
313					W_REG(NULL, &cc->flashcontrol, 0);
314					OSL_DELAY(1);
315					if (sflash_poll(sbh, cc, off) == 0) {
316						/* Flash rejected command */
317						SFL_MSG(("sflash: pp rejected, try: %d,"
318						         " off: %d/%d, len: %d/%d, ret:"
319						         "%d\n", try, off, offset, len,
320						         length, ret));
321						if (try <= ST_RETRIES)
322							goto retry;
323						else
324							return -11;
325					}
326					return ret;
327				} else {
328					/* Write single byte */
329					sflash_cmd(osh, cc, SFLASH_ST_CSA | *buf++);
330				}
331				ret++;
332				off++;
333				len--;
334			}
335			/* All done, drop cs & poll */
336			W_REG(NULL, &cc->flashcontrol, 0);
337			OSL_DELAY(1);
338			if (sflash_poll(sbh, cc, off) == 0) {
339				/* Flash rejected command */
340				SFL_MSG(("sflash: pp rejected, try: %d, off: %d/%d,"
341				         " len: %d/%d, ret: %d\n",
342				         try, off, offset, len, length, ret));
343				if (try <= ST_RETRIES)
344					goto retry;
345				else
346					return -12;
347			}
348		} else {
349			ret = 1;
350			W_REG(osh, &cc->flashaddress, off);
351			W_REG(osh, &cc->flashdata, *buf);
352			/* Page program */
353			sflash_cmd(osh, cc, SFLASH_ST_PP);
354		}
355		break;
356	case SFLASH_AT:
357		mask = sfl->blocksize - 1;
358		page = (off & ~mask) << 1;
359		byte = off & mask;
360		/* Read main memory page into buffer 1 */
361		if (byte || (len < sfl->blocksize)) {
362			W_REG(osh, &cc->flashaddress, page);
363			sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD);
364			/* 250 us for AT45DB321B */
365			SPINWAIT(sflash_poll(sbh, cc, off), 1000);
366			ASSERT(!sflash_poll(sbh, cc, off));
367		}
368		/* Write into buffer 1 */
369		for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
370			W_REG(osh, &cc->flashaddress, byte++);
371			W_REG(osh, &cc->flashdata, *buf++);
372			sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE);
373		}
374		/* Write buffer 1 into main memory page */
375		W_REG(osh, &cc->flashaddress, page);
376		sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM);
377		break;
378	}
379
380	return ret;
381}
382
383/* Erase a region. Returns number of bytes scheduled for erasure.
384 * Caller should poll for completion.
385 */
386int
387sflash_erase(sb_t *sbh, chipcregs_t *cc, uint offset)
388{
389	struct sflash *sfl;
390	osl_t *osh;
391
392	ASSERT(sbh);
393
394	osh = sb_osh(sbh);
395
396	sfl = &sflash;
397	if (offset >= sfl->size)
398		return -22;
399
400	switch (sfl->type) {
401	case SFLASH_ST:
402		sflash_cmd(osh, cc, SFLASH_ST_WREN);
403		W_REG(osh, &cc->flashaddress, offset);
404		sflash_cmd(osh, cc, SFLASH_ST_SE);
405		return sfl->blocksize;
406	case SFLASH_AT:
407		W_REG(osh, &cc->flashaddress, offset << 1);
408		sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE);
409		return sfl->blocksize;
410	}
411
412	return 0;
413}
414
415/*
416 * writes the appropriate range of flash, a NULL buf simply erases
417 * the region of flash
418 */
419int
420sflash_commit(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
421{
422	struct sflash *sfl;
423	uchar *block = NULL, *cur_ptr, *blk_ptr;
424	uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
425	uint blk_offset, blk_len, copied;
426	int bytes, ret = 0;
427	osl_t *osh;
428
429	ASSERT(sbh);
430
431	osh = sb_osh(sbh);
432
433	/* Check address range */
434	if (len <= 0)
435		return 0;
436
437	sfl = &sflash;
438	if ((offset + len) > sfl->size)
439		return -1;
440
441	blocksize = sfl->blocksize;
442	mask = blocksize - 1;
443
444	/* Allocate a block of mem */
445	if (!(block = MALLOC(osh, blocksize)))
446		return -1;
447
448	while (len) {
449		/* Align offset */
450		cur_offset = offset & ~mask;
451		cur_length = blocksize;
452		cur_ptr = block;
453
454		remainder = blocksize - (offset & mask);
455		if (len < remainder)
456			cur_retlen = len;
457		else
458			cur_retlen = remainder;
459
460		/* buf == NULL means erase only */
461		if (buf) {
462			/* Copy existing data into holding block if necessary */
463			if ((offset & mask) || (len < blocksize)) {
464				blk_offset = cur_offset;
465				blk_len = cur_length;
466				blk_ptr = cur_ptr;
467
468				/* Copy entire block */
469				while (blk_len) {
470					copied = sflash_read(sbh, cc, blk_offset, blk_len, blk_ptr);
471					blk_offset += copied;
472					blk_len -= copied;
473					blk_ptr += copied;
474				}
475			}
476
477			/* Copy input data into holding block */
478			memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
479		}
480
481		/* Erase block */
482		if ((ret = sflash_erase(sbh, cc, (uint) cur_offset)) < 0)
483			goto done;
484		while (sflash_poll(sbh, cc, (uint) cur_offset));
485
486		/* buf == NULL means erase only */
487		if (!buf) {
488			offset += cur_retlen;
489			len -= cur_retlen;
490			continue;
491		}
492
493		/* Write holding block */
494		while (cur_length > 0) {
495			if ((bytes = sflash_write(sbh, cc,
496			                          (uint) cur_offset,
497			                          (uint) cur_length,
498			                          (uchar *) cur_ptr)) < 0) {
499				ret = bytes;
500				goto done;
501			}
502			while (sflash_poll(sbh, cc, (uint) cur_offset));
503			cur_offset += bytes;
504			cur_length -= bytes;
505			cur_ptr += bytes;
506		}
507
508		offset += cur_retlen;
509		len -= cur_retlen;
510		buf += cur_retlen;
511	}
512
513	ret = len;
514done:
515	if (block)
516		MFREE(osh, block, blocksize);
517	return ret;
518}
519