1/*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
3 *
4 * Copyright 2006, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
8 * the contents of this file may not be disclosed to third parties, copied
9 * or duplicated in any form, in whole or in part, without the prior
10 * written permission of Broadcom Corporation.
11 *
12 * $Id: dev_sflash.c,v 1.1.1.1 2008/10/15 03:25:53 james26_jang Exp $
13 */
14
15#include "lib_types.h"
16#include "lib_malloc.h"
17#include "lib_printf.h"
18#include "lib_string.h"
19#include "addrspace.h"
20#include "cfe_iocb.h"
21#include "cfe_device.h"
22#include "cfe_ioctl.h"
23#include "cfe_error.h"
24#include "dev_newflash.h"
25
26#include "bsp_config.h"
27
28#include <typedefs.h>
29#include <osl.h>
30#include <bcmutils.h>
31#include <sbutils.h>
32#include <sflash.h>
33
34#define isaligned(x, y) (((x) % (y)) == 0)
35#define min(a,b) ((a) < (b) ? (a) : (b))
36#define max(a,b) ((a) > (b) ? (a) : (b))
37
38struct sflash_cfe {
39	sb_t *sbh;
40	chipcregs_t *cc;
41	struct sflash *info;
42	flashpart_t parts[FLASH_MAX_PARTITIONS];
43};
44
45static int sflashidx = 0;
46
47static int
48sflash_cfe_open(cfe_devctx_t *ctx)
49{
50	return 0;
51}
52
53static int
54sflash_cfe_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
55{
56	flashpart_t *part = (flashpart_t *) ctx->dev_softc;
57	struct sflash_cfe *sflash = (struct sflash_cfe *) part->fp_dev;
58	uint offset = (uint) buffer->buf_offset + part->fp_offset;
59	uint len = (uint) buffer->buf_length;
60	uchar *buf = (uchar *) buffer->buf_ptr;
61	int bytes, ret = 0;
62
63	buffer->buf_retlen = 0;
64
65	/* Check address range */
66	if (!len)
67		return 0;
68	if ((offset + len) > sflash->info->size)
69		return CFE_ERR_IOERR;
70
71	while (len) {
72		if ((bytes = sflash_read(sflash->sbh, sflash->cc, offset, len, buf)) < 0) {
73			ret = bytes;
74			goto done;
75		}
76		offset += bytes;
77		len -= bytes;
78		buf += bytes;
79		buffer->buf_retlen += bytes;
80	}
81
82 done:
83	return ret;
84}
85
86static int
87sflash_cfe_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat)
88{
89	inpstat->inp_status = 1;
90	return 0;
91}
92
93static int
94sflash_cfe_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
95{
96	flashpart_t *part = (flashpart_t *) ctx->dev_softc;
97	struct sflash_cfe *sflash = (struct sflash_cfe *) part->fp_dev;
98	uint offset = (uint) buffer->buf_offset + part->fp_offset;
99	uint len = (uint) buffer->buf_length;
100	uchar *buf = (uchar *) buffer->buf_ptr;
101	uchar *block = NULL;
102	uint blocksize = 0, mask;
103	iocb_buffer_t cur;
104	int bytes, ret = 0;
105
106	buffer->buf_retlen = 0;
107
108	/* Check address range */
109	if (!len)
110		return 0;
111
112	if ((offset + len) > sflash->info->size)
113		return CFE_ERR_IOERR;
114
115	blocksize = sflash->info->blocksize;
116	mask = blocksize - 1;
117
118	if (block)
119		KFREE(block);
120	if (!(block = KMALLOC(blocksize, 0)))
121		return CFE_ERR_NOMEM;
122
123	/* Backup and erase one block at a time */
124	while (len) {
125		/* Align offset */
126		cur.buf_offset = offset & ~mask;
127		cur.buf_length = blocksize;
128		cur.buf_ptr = block;
129
130		/* Copy existing data into holding block if necessary */
131		if (!isaligned(offset, blocksize) || (len < blocksize)) {
132			cur.buf_offset -= part->fp_offset;
133			if ((ret = sflash_cfe_read(ctx, &cur)))
134				goto done;
135			if (cur.buf_retlen != cur.buf_length) {
136				ret = CFE_ERR_IOERR;
137				goto done;
138			}
139			cur.buf_offset += part->fp_offset;
140		}
141
142		/* Copy input data into holding block */
143		cur.buf_retlen = min(len, blocksize - (offset & mask));
144		memcpy(cur.buf_ptr + (offset & mask), buf, cur.buf_retlen);
145
146		/* Erase block */
147		if ((ret = sflash_erase(sflash->sbh, sflash->cc, (uint) cur.buf_offset)) < 0)
148			goto done;
149		while (sflash_poll(sflash->sbh, sflash->cc, (uint) cur.buf_offset));
150
151		/* Write holding block */
152		while (cur.buf_length) {
153			if ((bytes = sflash_write(sflash->sbh, sflash->cc,
154						  (uint) cur.buf_offset,
155						  (uint) cur.buf_length,
156						  (uchar *) cur.buf_ptr)) < 0) {
157				ret = bytes;
158				goto done;
159			}
160			while (sflash_poll(sflash->sbh, sflash->cc, (uint) cur.buf_offset));
161
162			cur.buf_offset += bytes;
163			cur.buf_length -= bytes;
164			cur.buf_ptr += bytes;
165		}
166
167		offset += cur.buf_retlen;
168		len -= cur.buf_retlen;
169		buf += cur.buf_retlen;
170		buffer->buf_retlen += cur.buf_retlen;
171	}
172
173 done:
174	if (block)
175		KFREE(block);
176	return ret;
177}
178
179static int
180sflash_cfe_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
181{
182	flashpart_t *part = (flashpart_t *) ctx->dev_softc;
183	struct sflash_cfe *sflash = (struct sflash_cfe *) part->fp_dev;
184	flash_info_t *info;
185
186	switch (buffer->buf_ioctlcmd) {
187	case IOCTL_FLASH_WRITE_ALL:
188		sflash_cfe_write(ctx, buffer);
189		break;
190	case IOCTL_FLASH_GETINFO:
191		info = (flash_info_t *) buffer->buf_ptr;
192		info->flash_base = 0;
193		info->flash_size = sflash->info->size;
194		info->flash_type = sflash->info->type;
195		info->flash_flags = FLASH_FLAG_NOERASE;
196		break;
197	default:
198		return CFE_ERR_INV_COMMAND;
199	}
200
201	return 0;
202}
203
204static int
205sflash_cfe_close(cfe_devctx_t *ctx)
206{
207	return 0;
208}
209
210static const cfe_devdisp_t sflash_cfe_dispatch = {
211	sflash_cfe_open,
212	sflash_cfe_read,
213	sflash_cfe_inpstat,
214	sflash_cfe_write,
215	sflash_cfe_ioctl,
216	sflash_cfe_close,
217	NULL,
218	NULL
219};
220
221static void
222sflash_do_parts(struct sflash_cfe *sflash, newflash_probe_t *probe)
223{
224	int idx;
225	int middlepart = -1;
226	int lobound = 0;
227	flashpart_t *parts = sflash->parts;
228	int hibound = sflash->info->size;
229
230	for (idx = 0; idx < probe->flash_nparts; idx++) {
231		if (probe->flash_parts[idx].fp_size == 0) {
232			middlepart = idx;
233			break;
234		}
235		parts[idx].fp_offset = lobound;
236		parts[idx].fp_size = probe->flash_parts[idx].fp_size;
237		lobound += probe->flash_parts[idx].fp_size;
238	}
239
240	if (idx != probe->flash_nparts) {
241		for (idx = probe->flash_nparts - 1; idx > middlepart; idx--) {
242			parts[idx].fp_size = probe->flash_parts[idx].fp_size;
243			hibound -= probe->flash_parts[idx].fp_size;
244			parts[idx].fp_offset = hibound;
245		}
246	}
247
248	if (middlepart != -1) {
249		parts[middlepart].fp_offset = lobound;
250		parts[middlepart].fp_size = hibound - lobound;
251	}
252}
253
254static void
255sflash_cfe_probe(cfe_driver_t *drv,
256		 unsigned long probe_a, unsigned long probe_b,
257		 void *probe_ptr)
258{
259	newflash_probe_t *probe = (newflash_probe_t *) probe_ptr;
260	struct sflash_cfe *sflash;
261	char type[80], descr[80], name[80];
262	int idx;
263
264	if (!(sflash = (struct sflash_cfe *) KMALLOC(sizeof(struct sflash_cfe), 0)))
265		return;
266	memset(sflash, 0, sizeof(struct sflash_cfe));
267
268	/* Attach to the backplane and map to chipc */
269	sflash->sbh = sb_kattach(SB_OSH);
270
271	sflash->cc = (chipcregs_t *)probe->flash_cmdset;
272
273	/* Initialize serial flash access */
274	if (!(sflash->info = sflash_init(sflash->sbh, sflash->cc))) {
275		xprintf("sflash: found no supported devices\n");
276		KFREE(sflash);
277		return;
278	}
279
280	/* Set description */
281	switch (sflash->info->type) {
282	case SFLASH_ST:
283		sprintf(type, "ST");
284		break;
285	case SFLASH_AT:
286		sprintf(type, "Atmel");
287		break;
288	default:
289		sprintf(type, "Unknown type %d", sflash->info->type);
290		break;
291	}
292
293	if (probe->flash_nparts == 0) {
294		/* Just instantiate one device */
295		sflash->parts[0].fp_dev = (flashdev_t *) sflash;
296		sflash->parts[0].fp_offset = 0;
297		sflash->parts[0].fp_size = sflash->info->size;
298		sprintf(descr, "%s %s size %uKB",
299			type, drv->drv_description,
300			(sflash->info->size + 1023) / 1024);
301		cfe_attach(drv, &sflash->parts[0], NULL, descr);
302	} else {
303		/* Partition flash into chunks */
304		sflash_do_parts(sflash, probe);
305
306		/* Instantiate devices for each piece */
307		for (idx = 0; idx < probe->flash_nparts; idx++) {
308			sprintf(descr, "%s %s offset %08X size %uKB",
309				type, drv->drv_description,
310				sflash->parts[idx].fp_offset,
311				(sflash->parts[idx].fp_size + 1023) / 1024);
312			sflash->parts[idx].fp_dev = (flashdev_t *) sflash;
313			if (probe->flash_parts[idx].fp_name)
314				strcpy(name, probe->flash_parts[idx].fp_name);
315			else
316				sprintf(name, "%d", idx);
317			cfe_attach_idx(drv, sflashidx, &sflash->parts[idx], name, descr);
318		}
319	}
320
321	sflashidx++;
322}
323
324const cfe_driver_t sflashdrv = {
325	"Serial flash",
326	"flash",
327	CFE_DEV_FLASH,
328	&sflash_cfe_dispatch,
329	sflash_cfe_probe
330};
331