1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  "New" Flash device driver		File: dev_newflash.c
5    *
6    *  This driver supports various types of flash
7    *  parts.
8    *
9    *  Author:  Mitch Lichtenberg
10    *
11    *********************************************************************
12    *
13    *  Copyright 2000,2001,2002,2003,2005
14    *  Broadcom Corporation. All rights reserved.
15    *
16    *  This software is furnished under license and may be used and
17    *  copied only in accordance with the following terms and
18    *  conditions.  Subject to these conditions, you may download,
19    *  copy, install, use, modify and distribute modified or unmodified
20    *  copies of this software in source and/or binary form.  No title
21    *  or ownership is transferred hereby.
22    *
23    *  1) Any source code used, modified or distributed must reproduce
24    *     and retain this copyright notice and list of conditions
25    *     as they appear in the source file.
26    *
27    *  2) No right is granted to use any trade name, trademark, or
28    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
29    *     name may not be used to endorse or promote products derived
30    *     from this software without the prior written permission of
31    *     Broadcom Corporation.
32    *
33    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
34    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
35    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
36    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
37    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
38    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
39    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
41    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
43    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
44    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
45    *     THE POSSIBILITY OF SUCH DAMAGE.
46    ********************************************************************* */
47
48
49#include "cfe.h"
50#include "dev_newflash.h"
51
52/*  *********************************************************************
53    *  Macros
54    ********************************************************************* */
55
56/* XXX In 8-bit modes, CFI data appears identically in both even and
57   odd bytes.  In 16-bit modes, the byte with the data depends on
58   both the byte-swapping properties of the expansion bus and the way
59   the flash part is attached to that bus.  The following is correct
60   for the wiring of current boards but needs to be revisited. */
61/* XXX This code is currently incorrect for DEV8/BUS8 devices (if any
62   such exist with CFI) */
63#if ENDIAN_BIG
64#define GETCFIBYTE(arr,x) (arr[(x)*2+1])
65#else
66#define GETCFIBYTE(arr,x) (arr[(x)*2])
67#endif
68#define GETCFIWORD(arr,x) ((unsigned int) (GETCFIBYTE((arr),(x))) | \
69                          ((unsigned int) (GETCFIBYTE((arr),(x)+1)) << 8))
70
71#define min(a,b) ((a) < (b) ? (a) : (b))
72#define max(a,b) ((a) > (b) ? (a) : (b))
73
74/*
75 * Get the address of the flash sector buffer from the
76 * config file.  Addresses are PHYSICAL.
77 */
78
79#ifndef CFG_FLASH_SECTOR_BUFFER_ADDR
80#define CFG_FLASH_SECTOR_BUFFER_ADDR	(100*1024*1024-128*1024)
81#endif
82
83#ifndef CFG_FLASH_SECTOR_BUFFER_SIZE
84#define CFG_FLASH_SECTOR_BUFFER_SIZE	(128*1024)
85#endif
86
87
88#ifndef _NEWFLASH_DEBUG_
89#define _NEWFLASH_DEBUG_  0
90#endif
91
92/*  *********************************************************************
93    *  Forward declarations
94    ********************************************************************* */
95
96
97static void flashdrv_probe(cfe_driver_t *drv,
98			   unsigned long probe_a, unsigned long probe_b,
99			   void *probe_ptr);
100
101
102static int flashdrv_open(cfe_devctx_t *ctx);
103static int flashdrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
104static int flashdrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat);
105static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
106static int flashdrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
107static int flashdrv_close(cfe_devctx_t *ctx);
108
109/*  *********************************************************************
110    *  Device dispatch
111    ********************************************************************* */
112
113const static cfe_devdisp_t flashdrv_dispatch = {
114    flashdrv_open,
115    flashdrv_read,
116    flashdrv_inpstat,
117    flashdrv_write,
118    flashdrv_ioctl,
119    flashdrv_close,
120    NULL,
121    NULL
122};
123
124const cfe_driver_t newflashdrv = {
125    "New CFI flash",
126    "flash",
127    CFE_DEV_FLASH,
128    &flashdrv_dispatch,
129    flashdrv_probe
130};
131
132
133/*  *********************************************************************
134    *  Externs
135    ********************************************************************* */
136
137extern void *flashop_engine_ptr;
138extern int flashop_engine_len;
139
140extern void _cfe_flushcache(int);
141
142static int flash_sector_query(flashdev_t *softc,flash_sector_t *sector);
143
144/*  *********************************************************************
145    *  Globals
146    ********************************************************************* */
147
148/*
149 * This is a pointer to a DRAM version of our flash subroutines.
150 * We make a global here so that it doesn't get copied multiple
151 * times for each flash we instantiate.
152 */
153
154static int (*flashop_engine_ram)(flashinstr_t *prog) = NULL;
155static uint8_t *flash_sector_buffer = NULL;
156
157/*  *********************************************************************
158    *  FLASH_SETUP_ENGINE()
159    *
160    *  Set up the "flash engine", copying the routine to DRAM
161    *  and flushing the cache so we can call it.
162    *
163    *  Input parameters:
164    *  	   nothing
165    *
166    *  Return value:
167    *  	   nothing
168    ********************************************************************* */
169
170static void flash_setup_engine(void)
171{
172#if ((CFG_RAMAPP) || (CFG_RELOC) || (CFG_ZIPSTART))
173    /* CFE is relocated, no need to copy flash engine to heap */
174    flashop_engine_ram = (void *) flashop_engine_ptr;
175#else
176    /* Copy flash engine to heap */
177    uint32_t *dst,*src;
178    int idx;
179
180    if (flashop_engine_ram) return;		/* already done */
181
182    /* Allocate space for engine  */
183    flashop_engine_ram = (void *) KMALLOC(flashop_engine_len,0);
184    if (!flashop_engine_ram) return;
185
186    /*
187     * Copy engine to RAM - do it 32-bits at a time to avoid
188     * a certain platform with broken byte reads (no, not the 1250)
189     */
190
191    dst = (uint32_t *) flashop_engine_ram;
192    src = (uint32_t *) flashop_engine_ptr;
193    for (idx = 0; idx < flashop_engine_len/sizeof(uint32_t); idx++) {
194	*dst++ = *src++;
195	}
196
197    /* Flush the d-cache, invalidate the I-cache. */
198
199    _cfe_flushcache(1);
200    _cfe_flushcache(2);
201#endif
202}
203
204
205/*  *********************************************************************
206    *  FLASH_OP_BEGIN(softc)
207    *
208    *  Reset the pointer to the flash operations so that we can
209    *  begin filling in new instructions to execute
210    *
211    *  Input parameters:
212    *  	   softc - our softc.
213    *
214    *  Return value:
215    *  	   nothing
216    ********************************************************************* */
217
218#define flash_op_begin(softc)  softc->fd_iptr = 0;
219
220/*  *********************************************************************
221    *  FLASH_OP_ADD(softc,op,dest,src,cnt)
222    *
223    *  Add an instruction to the flashop table
224    *
225    *  Input parameters:
226    *  	   softc - our flash
227    *  	   op,dest,src,cnt - data for the opcode
228    *
229    *  Return value:
230    *  	   nothing
231    ********************************************************************* */
232
233static void flash_op_add(flashdev_t *softc,long base,long op,long dest,long src,long cnt)
234{
235    flashinstr_t *fi = &(softc->fd_inst[softc->fd_iptr]);
236
237    fi->fi_op = op;
238    fi->fi_base = base;
239    fi->fi_dest = dest;
240    fi->fi_src = src;
241    fi->fi_cnt = cnt;
242
243    softc->fd_iptr++;
244}
245
246
247/*  *********************************************************************
248    *  FLASH_OP_EXECUTE(softc)
249    *
250    *  Execute the stored "flash operations"
251    *
252    *  Input parameters:
253    *  	   softc - our flash
254    *
255    *  Return value:
256    *  	   0 if ok, else # of failures (less than zero)
257    ********************************************************************* */
258
259static int flash_op_execute(flashdev_t *softc)
260{
261    flash_op_add(softc,softc->fd_probe.flash_phys,FEOP_RETURN,0,0,0);
262
263    if (_NEWFLASH_DEBUG_) {
264	int idx;
265	printf("---------------\nCalling engine @ %08X\n",flashop_engine_ram);
266	for (idx = 0; idx < softc->fd_iptr; idx++) {
267	    printf("%2d %08X %08X %08X %08X\n",
268		   softc->fd_inst[idx].fi_op,
269		   softc->fd_inst[idx].fi_base,
270		   softc->fd_inst[idx].fi_dest,
271		   softc->fd_inst[idx].fi_src,
272		   softc->fd_inst[idx].fi_cnt);
273	    }
274	}
275
276    /* If someone hooked the flashop engine, call the hook. */
277    if (softc->fd_probe.flash_engine_hook) {
278	return (*(softc->fd_probe.flash_engine_hook))(&(softc->fd_inst[0]));
279	}
280
281    /* Otherwise, call the standard one. */
282    if (!flashop_engine_ram) return CFE_ERR_UNSUPPORTED;
283    return (*flashop_engine_ram)(&(softc->fd_inst[0]));
284}
285
286
287/*  *********************************************************************
288    *  FLASH_ERASE_RANGE(softc,range)
289    *
290    *  Erase a range of sectors
291    *
292    *  Input parameters:
293    *  	   softc - our flash
294    *  	   range - range structure
295    *
296    *  Return value:
297    *  	   0 if ok
298    *  	   else error
299    ********************************************************************* */
300
301static int flash_erase_range(flashdev_t *softc,flash_range_t *range)
302{
303    flash_sector_t sector;
304    int res;
305
306    if (softc->fd_probe.flash_type != FLASH_TYPE_FLASH) {
307	return CFE_ERR_UNSUPPORTED;
308	}
309
310    if (range->range_base+range->range_length > softc->fd_probe.flash_size) {
311	return CFE_ERR_INV_PARAM;
312	}
313
314    res = 0;
315
316    sector.flash_sector_idx = 0;
317
318    for (;;) {
319	res = flash_sector_query(softc,&sector);
320	if (res != 0) break;
321	if (sector.flash_sector_status == FLASH_SECTOR_INVALID) {
322	    break;
323	    }
324
325	if ((sector.flash_sector_offset >= range->range_base) &&
326	    (sector.flash_sector_offset <
327	     (range->range_base+range->range_length-1))) {
328
329	    flash_op_begin(softc);
330	    flash_op_add(softc,softc->fd_probe.flash_phys,
331			 softc->fd_erasefunc,
332			 sector.flash_sector_offset,
333			 0,0);
334	    res = flash_op_execute(softc);
335
336	    if (res != 0) break;
337	    }
338	sector.flash_sector_idx++;
339	}
340
341    return res;
342
343}
344
345/*  *********************************************************************
346    *  FLASH_ERASE_ALL(softc)
347    *
348    *  Erase the entire flash device, except the NVRAM area,
349    *  sector-by-sector.
350    *
351    *  Input parameters:
352    *  	   softc - our flash
353    *
354    *  Return value:
355    *  	   0 if ok
356    *  	   else error code
357    ********************************************************************* */
358
359static int flash_erase_all(flashdev_t *softc)
360{
361    flash_range_t range;
362
363    range.range_base = 0;
364    range.range_length = softc->fd_probe.flash_size *
365	softc->fd_probe.flash_nchips;
366
367    return flash_erase_range(softc,&range);
368}
369
370
371/*  *********************************************************************
372    *  flash_range_intersection(sector,inrange,outrange)
373    *
374    *  Compute the intersection between a flash range and a
375    *  sector.
376    *
377    *  Input parameters:
378    *  	   sector - sector to examine
379    *  	   range - range we are checking
380    *  	   outrange - where to put resulting intersection range
381    *
382    *  Return value:
383    *  	   1 - range is an entire sector
384    *  	   0 - range is a partial sector
385    *  	   -1 - range has no intersection
386    ********************************************************************* */
387
388static int flash_range_intersection(flash_sector_t *sector,
389				    flash_range_t *inrange,
390				    flash_range_t *outrange)
391{
392    int start,end;
393
394    /* Compute the start and end pointers */
395    start = (int) (max(sector->flash_sector_offset,
396		       inrange->range_base));
397    end = (int) (min((sector->flash_sector_offset+sector->flash_sector_size),
398		     (inrange->range_base+inrange->range_length)));
399
400    /*
401     * if the end is in the right place wrt the start,
402     * there is an intersection.
403     */
404    if (end > start) {
405	outrange->range_base   = (unsigned int) start;
406	outrange->range_length = (unsigned int) (end-start);
407
408	if ((sector->flash_sector_offset == outrange->range_base) &&
409	    (sector->flash_sector_size == outrange->range_length)) {
410	    return 1;		/* intersection: entire sector */
411	    }
412	else {
413	    return 0;		/* intersection: partial sector */
414	    }
415	}
416    else {
417	outrange->range_base = (unsigned int) start;
418	outrange->range_length = 0;
419	return -1;		/* intersection: none */
420	}
421}
422
423
424/*  *********************************************************************
425    *  FLASH_SECTOR_QUERY(softc,sector)
426    *
427    *  Query the sector information about a particular sector.  You can
428    *  call this iteratively to find out about all of the sectors.
429    *
430    *  Input parameters:
431    *  	   softc - our flash info
432    *  	   sector - structure to receive sector information
433    *
434    *  Return value:
435    *  	   0 if ok
436    *  	   else error code
437    ********************************************************************* */
438
439static int flash_sector_query(flashdev_t *softc,flash_sector_t *sector)
440{
441    int idx;
442    int nblks;
443    int blksiz;
444    unsigned int offset;
445    int whichchip;
446    int secidx;
447    int curblk;
448
449    if (softc->fd_probe.flash_type != FLASH_TYPE_FLASH) {
450	return CFE_ERR_UNSUPPORTED;
451	}
452
453    if (softc->fd_probe.flash_nsectors == 0) {
454	return CFE_ERR_UNSUPPORTED;
455	}
456
457    /* Figure out which chip */
458    whichchip = sector->flash_sector_idx / softc->fd_ttlsect;
459    if (whichchip >= softc->fd_probe.flash_nchips) {
460	sector->flash_sector_status = FLASH_SECTOR_INVALID;
461	return 0;
462	}
463
464    /* Within that chip, get sector info */
465    offset = softc->fd_probe.flash_size * whichchip;
466    secidx = sector->flash_sector_idx % softc->fd_ttlsect;
467    curblk = 0;
468
469    for (idx = 0; idx < softc->fd_probe.flash_nsectors; idx++) {
470	nblks = FLASH_SECTOR_NBLKS(softc->fd_probe.flash_sectors[idx]);
471	blksiz = FLASH_SECTOR_SIZE(softc->fd_probe.flash_sectors[idx]);
472	if (secidx < curblk+nblks) {
473	    sector->flash_sector_status = FLASH_SECTOR_OK;
474	    sector->flash_sector_offset =
475		offset + (secidx-curblk)*blksiz;
476	    sector->flash_sector_size = blksiz;
477	    break;
478	    }
479
480	offset += (nblks)*blksiz;
481	curblk += nblks;
482	}
483
484
485    if (idx == softc->fd_probe.flash_nsectors) {
486	sector->flash_sector_status = FLASH_SECTOR_INVALID;
487	}
488
489    return 0;
490}
491
492
493/*  *********************************************************************
494    *  FLASH_SET_CMDSET(softc,cmdset,bus16,dev16)
495    *
496    *  Set the command-set that we'll honor for this flash.
497    *
498    *  Input parameters:
499    *  	   softc - our flash
500    *  	   cmdset - FLASH_CFI_CMDSET_xxx
501    *      bus16 - true if bus is 16 bits wide
502    *      dev16 - true if device supports 16-bit operation
503    *
504    *  So: bus16 && dev16 -> 16-bit commands
505    *      !bus16 && dev16 -> 8-bit commands to 16-bit flash with BYTE#
506    *      !bus16 && !dev16 -> 8-bit commands
507    *
508    *  Return value:
509    *  	   nothing
510    ********************************************************************* */
511
512static void flash_set_cmdset(flashdev_t *softc,int cmdset,int bus16,int dev16)
513{
514    switch (cmdset) {
515#if (FLASH_DRIVERS & FLASH_DRIVER_INTEL)
516	case FLASH_CFI_CMDSET_INTEL_ECS:
517	case FLASH_CFI_CMDSET_INTEL_STD:
518	    if (bus16) {
519		softc->fd_erasefunc = FEOP_INTEL_ERASE16;
520		softc->fd_pgmfunc   = FEOP_INTEL_PGM16;
521		softc->fd_readfunc  = FEOP_READ16;
522		}
523	    else {
524		softc->fd_erasefunc = FEOP_INTEL_ERASE8;
525		softc->fd_pgmfunc   = FEOP_INTEL_PGM32B;
526		softc->fd_readfunc  = FEOP_READ8;
527		}
528	    break;
529#endif
530#if (FLASH_DRIVERS & FLASH_DRIVER_AMD)
531	case FLASH_CFI_CMDSET_AMD_ECS:
532	case FLASH_CFI_CMDSET_AMD_STD:
533	    if (!bus16 && !dev16) {	/* 8-bit bus, 8-bit flash */
534		softc->fd_erasefunc = FEOP_AMD_ERASE8;
535		softc->fd_pgmfunc   = FEOP_AMD_PGM8;
536		softc->fd_readfunc  = FEOP_READ8;
537		}
538	    else if (bus16 && dev16) {	/* 16-bit bus, 16-bit flash */
539		softc->fd_erasefunc = FEOP_AMD_ERASE16;
540		softc->fd_pgmfunc   = FEOP_AMD_PGM16;
541		softc->fd_readfunc  = FEOP_READ16;
542		}
543	    else {			/* 8-bit bus, 16-bit flash w/BYTE# */
544		softc->fd_erasefunc = FEOP_AMD_ERASE16B;
545		softc->fd_pgmfunc   = FEOP_AMD_PGM16B;
546		softc->fd_readfunc  = FEOP_READ8;
547		}
548	    break;
549#endif
550	default:
551	    /* we don't understand the command set - treat it like ROM */
552	    if (softc->fd_probe.flash_type == FLASH_TYPE_FLASH)
553		softc->fd_probe.flash_type = FLASH_TYPE_ROM;
554	    softc->fd_erasefunc = FEOP_RETURN;
555	    softc->fd_pgmfunc   = FEOP_RETURN;
556	    softc->fd_readfunc  = bus16 ? FEOP_READ16 : FEOP_READ8;
557	    break;
558	}
559}
560
561#if (FLASH_DRIVERS & FLASH_DRIVER_CFI)
562/*  *********************************************************************
563    *  FLASH_CFI_PROBE(softc)
564    *
565    *  Try to do a CFI query on this device.  If we find the m
566    *  magic signature, extract some useful information from the
567    *  query structure.
568    *
569    *  Input parameters:
570    *  	   softc - out flash
571    *
572    *  Return value:
573    *  	   0 if successful, <0 if error
574    ********************************************************************* */
575static int flash_cfi_probe(flashdev_t *softc)
576{
577    uint8_t cfidata[FLASH_MAX_CFIDATA];
578    unsigned int cmdset;
579    unsigned int devif;
580    int bus16 = 0;
581    int dev16 = 0;
582    int idx;
583    int found = 0;
584    int regcnt;
585    int nblks;
586    int blksiz;
587    int reversed = 0;
588
589    if (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) {
590	bus16 = 1;
591	}
592
593    /* Do a CFI query. */
594    idx = FEOP_CFIQUERY8;
595    if (softc->fd_probe.flash_flags & FLASH_FLG_DEV16) {
596	idx = bus16 ? FEOP_CFIQUERY16 : FEOP_CFIQUERY16B;
597	}
598
599    flash_op_begin(softc);
600    flash_op_add(softc,softc->fd_probe.flash_phys,
601		 idx,(long)cfidata,0,FLASH_MAX_CFIDATA);
602    flash_op_execute(softc);
603
604    /* Look for signature. */
605    if (_NEWFLASH_DEBUG_) {
606	int i;
607
608	printf(" flash at %08x, CFI:", softc->fd_probe.flash_phys);
609	for (i=0;i<FLASH_MAX_CFIDATA;i++) {
610	    printf("%s%02x", (i%16)?" ":"\n  ", 0xFF & cfidata[i]);
611	    }
612	printf("\n");
613	}
614
615    /* Temporary work-around for certain ST flash in 16/8 mode. */
616    if (ENDIAN_BIG &&
617	(cfidata[2*FLASH_CFI_SIGNATURE] == 'Q') &&
618	(cfidata[2*FLASH_CFI_SIGNATURE+1] == 0)) {
619	int i;
620
621	/* This might be ST flash, which returns data only in even bytes. */
622	for (i = 0; i < FLASH_MAX_CFIDATA; i+=2)
623	    cfidata[i+1] = cfidata[i];
624	}
625
626    if ((GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+0) == 'Q') &&
627	(GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+1) == 'R') &&
628	(GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+2) == 'Y')) {
629	if (_NEWFLASH_DEBUG_) printf("CFI signature found\n");
630	found = 1;
631	}
632
633    /* No CFI, bail to try JEDEC. */
634    if (!found) {
635	if (_NEWFLASH_DEBUG_) printf("CFI signature not found\n");
636	return CFE_ERR_UNSUPPORTED;
637	}
638
639    softc->fd_probe.flash_type = FLASH_TYPE_FLASH;
640
641    /* Gather info from flash */
642    cmdset = GETCFIWORD(cfidata,FLASH_CFI_COMMAND_SET);
643    devif = GETCFIWORD(cfidata,FLASH_CFI_DEVICE_INTERFACE);
644    softc->fd_probe.flash_size =
645	(1 << (unsigned int)(GETCFIBYTE(cfidata,FLASH_CFI_DEVICE_SIZE)));
646
647    /*
648     * It's a 16-bit device if it is either always 16 bits or can be.
649     * we'll use "bus16" to decide if the BYTE# pin was strapped
650     */
651
652    dev16 = 0;
653    if ((devif == FLASH_CFI_DEVIF_X16) || (devif == FLASH_CFI_DEVIF_X8X16))
654	dev16 = 1;
655
656    regcnt = GETCFIBYTE(cfidata,FLASH_CFI_REGION_COUNT);
657
658    softc->fd_probe.flash_nsectors = regcnt;
659
660    /*
661     * Some AMD top-boot flash parts have broken CFI tables - they are
662     * backwards!  Do some extra probing to find it.
663     */
664
665    if (cmdset == FLASH_CFI_CMDSET_AMD_STD) {
666	uint8_t devcode;
667
668	idx = FEOP_AMD_DEVCODE8;
669	if (softc->fd_probe.flash_flags & FLASH_FLG_DEV16) {
670	    idx = (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) ?
671		FEOP_AMD_DEVCODE16 : FEOP_AMD_DEVCODE16B;
672	    }
673
674	flash_op_begin(softc);
675	flash_op_add(softc,softc->fd_probe.flash_phys,
676		     idx,(long)&devcode,0,0);
677	flash_op_execute(softc);
678	if (_NEWFLASH_DEBUG_) printf("Devcode = 0x%x\n", devcode);
679	devcode &= 0xFF;
680	if ((devcode == 0xC4)||(devcode == 0xF6)) {
681	    reversed = 1;
682	    if (_NEWFLASH_DEBUG_)
683		printf("Warning: insane AMD part, backwards CFI table!\n");
684	    }
685	}
686
687    for (idx = 0; idx < regcnt; idx++) {
688	nblks = GETCFIWORD(cfidata,FLASH_CFI_REGION_TABLE+idx*4) + 1;
689	blksiz = GETCFIWORD(cfidata,FLASH_CFI_REGION_TABLE+2+idx*4) * 256;
690
691	if (reversed) {
692	    /* Insane */
693	    softc->fd_probe.flash_sectors[((regcnt-1)-idx)] =
694		FLASH_SECTOR_RANGE(nblks,blksiz);
695	    }
696	else {
697	    /* Sane */
698	    softc->fd_probe.flash_sectors[idx] =
699		FLASH_SECTOR_RANGE(nblks,blksiz);
700	    }
701	}
702
703    /* Set the command set we're going to use. */
704    flash_set_cmdset(softc,cmdset,bus16,dev16);
705
706    return 0;
707}
708
709
710/*  *********************************************************************
711    *  FLASH_JEDEC_PROBE(softc)
712    *
713    *  Look for the JEDEC manufacturer and device code.  If we find one
714    *  that we recognize, extract some useful information from the
715    *  lookup table.
716    *
717    *  Input parameters:
718    *  	   softc - out flash
719    *
720    *  Return value:
721    *  	   0 if successful, <0 if error
722    ********************************************************************* */
723static int flash_jedec_probe(flashdev_t *softc)
724{
725    typedef struct {
726	uint8_t vendor;
727	uint8_t device;
728	unsigned int size;		/* total size in bytes */
729	unsigned int sectors;		/* assumed uniform */
730    } jedec_entry_t;
731    static const jedec_entry_t jedec_table[] = {
732	{FLASH_MFR_AMD, 0x4F, 0x80000, 8},	/* AMD Am29LV040B */
733	{FLASH_MFR_STM, 0xE3, 0x80000, 8},	/* STM M29W040B   */
734    };
735    int idx;
736    uint8_t vendor, device;
737    int bus16 = (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) != 0;
738    int dev16;
739    int i;
740    unsigned int cmdset;
741    int found = 0;
742    int nsectors;
743    int nblks;
744    int blksiz;
745
746    /* Do a JEDEC query. */
747    idx = FEOP_AMD_MANID8;
748    if (softc->fd_probe.flash_flags & FLASH_FLG_DEV16) {
749	idx = bus16 ? FEOP_AMD_MANID16 : FEOP_AMD_MANID16B;
750	}
751
752    flash_op_begin(softc);
753    flash_op_add(softc,softc->fd_probe.flash_phys,idx,(long)&vendor,0,0);
754    flash_op_execute(softc);
755
756    idx = FEOP_AMD_DEVCODE8;
757    if (softc->fd_probe.flash_flags & FLASH_FLG_DEV16) {
758	idx = bus16 ? FEOP_AMD_DEVCODE16 : FEOP_AMD_DEVCODE16B;
759	}
760
761    flash_op_begin(softc);
762    flash_op_add(softc,softc->fd_probe.flash_phys,idx,(long)&device,0,0);
763    flash_op_execute(softc);
764
765    if (_NEWFLASH_DEBUG_)
766	printf("JEDEC Vendor = 0x%x, Device = 0x%x\n", vendor, device);
767
768    /* Look up to see if known. */
769    found = 0;
770    for (i = 0; i < sizeof(jedec_table)/sizeof(jedec_entry_t); i++) {
771	if (vendor == jedec_table[i].vendor &&
772	    device == jedec_table[i].device) {
773	    found = 1;
774	    break;
775	    }
776	}
777
778    if (!found) {
779	if (_NEWFLASH_DEBUG_) printf("JEDEC code not found\n");
780	return CFE_ERR_UNSUPPORTED;
781	}
782
783    softc->fd_probe.flash_type = FLASH_TYPE_FLASH;
784
785    if (_NEWFLASH_DEBUG_) printf("JEDEC entry %d\n", i);
786
787    /* Gather info from table. */
788    softc->fd_probe.flash_size = jedec_table[i].size;
789    nsectors = jedec_table[i].sectors;
790
791    /* The following are assumed for non-CFI JEDEC flash (XXX ok?) */
792    cmdset = FLASH_CFI_CMDSET_AMD_STD;
793    dev16 = 0;
794    nblks = 1;
795    blksiz = softc->fd_probe.flash_size/nsectors;
796
797    /* Set up the sector table. */
798    softc->fd_probe.flash_nsectors = nsectors;
799    for (i = 0; i < nsectors; i++) {
800	softc->fd_probe.flash_sectors[i] = FLASH_SECTOR_RANGE(nblks,blksiz);
801	}
802
803    /* Set the command set we're going to use. */
804    flash_set_cmdset(softc,cmdset,bus16,dev16);
805
806    return 0;
807}
808
809
810/*  *********************************************************************
811    *  FLASH_DO_PROBE(softc)
812    *
813    *  Probe to see if we're ROM or RAM.  If ROM, see if we're flash.
814    *  If flash, do CFI query.
815    *
816    *  Input parameters:
817    *  	   softc - our structure
818    *
819    *  Return value:
820    *  	   FLASH_TYPE_xxx
821    ********************************************************************* */
822static int flash_do_probe(flashdev_t *softc)
823{
824    uint8_t test_byte0, test_byte1;
825    volatile uint8_t *ptr;
826    int found;
827
828    /*
829     * flash_do_probe is called before we open the device, so we
830     * need to allocate space for instructions so the flashop
831     * engine will work.
832     */
833
834    softc->fd_inst = KMALLOC(FLASH_MAX_INST*sizeof(flashinstr_t),0);
835    if (!softc->fd_inst) return FLASH_TYPE_ROM;
836
837    /*
838     * Attempt to read/write bytes zero and one.  If they are changable,
839     * this is SRAM (or maybe a ROM emulator with the write line hooked up)
840     */
841
842    if (_NEWFLASH_DEBUG_) printf("\nFlash type ");
843
844    ptr = (volatile uint8_t *) UNCADDR(softc->fd_probe.flash_phys);
845    test_byte0 = *ptr ^ 0xFF;
846    test_byte1 = *(ptr+1) ^ 0xFF;
847    *ptr = test_byte0;
848    *(ptr+1) = test_byte1;
849
850    if ((*ptr == test_byte0) && (*(ptr+1) == test_byte1)) {
851	softc->fd_probe.flash_type = FLASH_TYPE_SRAM;
852
853	/* Only restore values if it's RAM */
854	*ptr = test_byte0 ^ 0xFF;
855	*(ptr+1) = test_byte1 ^ 0xFF;
856
857	if (_NEWFLASH_DEBUG_) printf("SRAM\n");
858
859	found = 0;
860	}
861    else {
862	softc->fd_probe.flash_type = FLASH_TYPE_ROM;
863
864	if (_NEWFLASH_DEBUG_) printf("ROM\n");
865
866	/*
867	 * If we think it is ROM, try doing a CFI query
868	 * to see if it is flash.  This check is kind of kludgey
869	 * but should work.
870	 */
871
872	found = (flash_cfi_probe(softc) == 0);
873	if (!found)
874	    found = (flash_jedec_probe(softc) == 0);
875	}
876
877    if (!found) {
878	flash_set_cmdset(softc,-1,
879			 (softc->fd_probe.flash_flags & FLASH_FLG_BUS16),
880			 (softc->fd_probe.flash_flags & FLASH_FLG_DEV16));
881	}
882
883    KFREE(softc->fd_inst);
884    softc->fd_inst = NULL;
885
886    return softc->fd_probe.flash_type;
887}
888
889#endif  /* (FLASH_DRIVERS & FLASH_DRIVER_CFI) */
890
891
892/*  *********************************************************************
893    *  flash_do_parts(probe,parts)
894    *
895    *  Partition the flash into the sizes specified.  We use
896    *  the sizes in the table to generate a table of {offset,size}
897    *  pairs that eventually become partitions.
898    *
899    *  The only thing magical about this is that size "0" means
900    *  "fill to max" and that partitions beyond the "0" are aligned
901    *  to the top of the flash.  Therefore, if you had a 4MB
902    *  flash and listed four partitions, 512K, 0, 512K, 512K,
903    *  then there would be a 2.5M partition in the middle and two
904    *  512K partitions at the top.
905    *
906    *  Input parameters:
907    *  	   probe - flash probe data (user-supplied table)
908    *  	   parts - our partition table (output)
909    *
910    *  Return value:
911    *  	   nothing
912    ********************************************************************* */
913
914static void flash_do_parts(flashdev_t *softc)
915{
916    int idx;
917    int middlepart = -1;
918    int lobound = 0;
919    newflash_probe_t *probe = &(softc->fd_probe);
920    flashpart_t *parts = &(softc->fd_parts[0]);
921    int hibound = probe->flash_size*probe->flash_nchips;
922
923    for (idx = 0; idx < probe->flash_nparts; idx++) {
924	if (probe->flash_parts[idx].fp_size == 0) {
925	    middlepart = idx;
926	    break;
927	    }
928	parts[idx].fp_offset = lobound;
929	parts[idx].fp_size = probe->flash_parts[idx].fp_size;
930	lobound += probe->flash_parts[idx].fp_size;
931	}
932
933    if (idx != probe->flash_nparts) {
934	for (idx = probe->flash_nparts - 1; idx > middlepart;
935	     idx--) {
936	    parts[idx].fp_size = probe->flash_parts[idx].fp_size;
937	    hibound -= probe->flash_parts[idx].fp_size;
938	    parts[idx].fp_offset = hibound;
939	    }
940	}
941
942    if (middlepart != -1) {
943	parts[middlepart].fp_offset = lobound;
944	parts[middlepart].fp_size = hibound - lobound;
945	}
946
947    if (_NEWFLASH_DEBUG_) {
948	printf("Partition information:\n");
949	for (idx = 0; idx < probe->flash_nparts;idx++) {
950	    printf("#%02d   %08X -> %08X  (%d)\n",idx,
951		   parts[idx].fp_offset,
952		   parts[idx].fp_offset+parts[idx].fp_size-1,
953		   parts[idx].fp_size);
954	    }
955	}
956}
957
958
959/*  *********************************************************************
960    *  flashdrv_allocbuf(dev)
961    *
962    *  Allocate sector buffer for flash programming.  Use a global
963    *  buffer for all devices.
964    *
965    *  Input parameters:
966    *  	   dev - our device
967    *
968    *  Return value:
969    *  	   nothing
970    ********************************************************************* */
971static void flashdrv_allocbuf(flashdev_t *softc)
972{
973    if (!flash_sector_buffer) {
974#if CFG_FLASH_ALLOC_SECTOR_BUFFER
975	flash_sector_buffer = KMALLOC(CFG_FLASH_SECTOR_BUFFER_SIZE,0);
976	if (!flash_sector_buffer) {
977	    printf("FLASH: Could not allocate sector buffer, using default\n");
978	    flash_sector_buffer = (uint8_t *) KERNADDR(CFG_FLASH_SECTOR_BUFFER_ADDR);
979	    }
980#else
981	flash_sector_buffer = (uint8_t *) KERNADDR(CFG_FLASH_SECTOR_BUFFER_ADDR);
982#endif
983	}
984
985    softc->fd_sectorbuffer = flash_sector_buffer;
986}
987
988/*  *********************************************************************
989    *  flashdrv_probe(drv,probe_a,probe_b,probe_ptr)
990    *
991    *  Device probe routine.  Attach the flash device to
992    *  CFE's device table.
993    *
994    *  Input parameters:
995    *  	   drv - driver descriptor
996    *  	   probe_a - physical address of flash
997    *  	   probe_b - size of flash (bytes)
998    *  	   probe_ptr - pointer to prefilled probe structure, or NULL
999    *
1000    *  Return value:
1001    *  	   nothing
1002    ********************************************************************* */
1003
1004static void flashdrv_probe(cfe_driver_t *drv,
1005			   unsigned long probe_a, unsigned long probe_b,
1006			   void *probe_ptr)
1007{
1008    flashdev_t *softc;
1009    int idx;
1010    char descr[80];
1011    static int flashidx = 0;
1012    char *x;
1013    newflash_probe_t *probe = (newflash_probe_t *) probe_ptr;
1014
1015    /* First configure the flashop engine if not already done. */
1016    flash_setup_engine();
1017
1018    softc = (flashdev_t *) KMALLOC(sizeof(flashdev_t),0);
1019    if (softc) {
1020	memset(softc,0,sizeof(flashdev_t));
1021
1022	flashdrv_allocbuf(softc);
1023
1024	if (probe) {
1025	    /* Passed probe structure, do fancy stuff */
1026	    memcpy(&(softc->fd_probe),probe,sizeof(newflash_probe_t));
1027	    if (softc->fd_probe.flash_nchips == 0) {
1028		softc->fd_probe.flash_nchips = 1;
1029		}
1030	    }
1031	else {
1032	    /* Didn't pass probe structure, do the compatible thing */
1033	    softc->fd_probe.flash_phys = probe_a;
1034	    softc->fd_probe.flash_size = (probe_b & FLASH_SIZE_MASK);
1035	    softc->fd_probe.flash_flags = (probe_b & FLASH_FLG_MASK);
1036	    softc->fd_probe.flash_nchips = 1;
1037	    }
1038
1039	if (softc->fd_probe.flash_flags & FLASH_FLG_MANUAL) {
1040	    /* Manual probing, just set the command set. */
1041	    flash_set_cmdset(softc,softc->fd_probe.flash_cmdset,
1042			     (softc->fd_probe.flash_flags & FLASH_FLG_BUS16),
1043			     (softc->fd_probe.flash_flags & FLASH_FLG_DEV16));
1044	    }
1045	else {
1046	    /* Do automatic probing */
1047#if (FLASH_DRIVERS & FLASH_DRIVER_CFI)
1048	    flash_do_probe(softc);
1049#else
1050	    return;		/* No automatic probing, bail! */
1051#endif
1052	    }
1053
1054	/* Remember total size of all devices */
1055	softc->fd_ttlsize = softc->fd_probe.flash_nchips * softc->fd_probe.flash_size;
1056
1057	/* Set description */
1058	x = descr;
1059	x += xsprintf(x,"%s at %08X size %uKB",drv->drv_description,
1060		 softc->fd_probe.flash_phys,
1061		 softc->fd_ttlsize/1024);
1062	if (softc->fd_probe.flash_nchips > 1) {
1063	    xsprintf(x," (%d chips)",softc->fd_probe.flash_nchips);
1064	    }
1065
1066	/*
1067	 * If flash is not partitioned, just instantiate one
1068	 * device.   Otherwise, instantiate multiple flashes
1069	 * to cover the entire device.
1070	 */
1071	if (softc->fd_probe.flash_nparts == 0) {
1072	    softc->fd_parts[0].fp_dev = softc;
1073	    softc->fd_parts[0].fp_offset = 0;
1074	    softc->fd_parts[0].fp_size = softc->fd_probe.flash_size;
1075	    cfe_attach_idx(drv,flashidx,&(softc->fd_parts[0]),NULL,descr);
1076	    }
1077	else {
1078	    /* Partition flash into chunks */
1079	    flash_do_parts(softc);
1080
1081	    /* Instantiate devices for each piece */
1082	    for (idx = 0; idx < softc->fd_probe.flash_nparts; idx++) {
1083		char name[32];
1084		char *nptr;
1085
1086		xsprintf(descr,"%s at %08X offset %08X size %uKB",
1087			 drv->drv_description,
1088			 softc->fd_probe.flash_phys,
1089			 softc->fd_parts[idx].fp_offset,
1090			 (softc->fd_parts[idx].fp_size+1023)/1024);
1091
1092		softc->fd_parts[idx].fp_dev = softc;
1093		if (softc->fd_probe.flash_parts[idx].fp_name == NULL) {
1094		    sprintf(name,"%d",idx);
1095		    nptr = name;
1096		    }
1097		else {
1098		    nptr = softc->fd_probe.flash_parts[idx].fp_name;
1099		    }
1100		cfe_attach_idx(drv,
1101			       flashidx,
1102			       &(softc->fd_parts[idx]),
1103			       nptr,
1104			       descr);
1105		}
1106	    }
1107
1108	flashidx++;
1109
1110	/* Count total sectors on the device */
1111	softc->fd_ttlsect = 0;
1112	for (idx = 0; idx < softc->fd_probe.flash_nsectors; idx++) {
1113	    softc->fd_ttlsect += FLASH_SECTOR_NBLKS(softc->fd_probe.flash_sectors[idx]);
1114	    }
1115	}
1116}
1117
1118
1119/*  *********************************************************************
1120    *  flashdrv_open(ctx)
1121    *
1122    *  Called when the flash device is opened.
1123    *
1124    *  Input parameters:
1125    *  	   ctx - device context
1126    *
1127    *  Return value:
1128    *  	   0 if ok else error code
1129    ********************************************************************* */
1130
1131static int flashdrv_open(cfe_devctx_t *ctx)
1132{
1133    flashpart_t *part = ctx->dev_softc;
1134    flashdev_t *softc = part->fp_dev;
1135    int ttlsect = softc->fd_ttlsect;
1136
1137    /*
1138     * Calculate number of flashop instructions we'll need at most.
1139     * This will be two for each sector plus two more for the first
1140     * and last sectors, plus two extra
1141     */
1142
1143    ttlsect = (ttlsect * 2 * softc->fd_probe.flash_nchips) + 6;
1144
1145    /* Allocate memory for instructions. */
1146    if (_NEWFLASH_DEBUG_)
1147	printf("%s: allocating %d instructions\n",cfe_device_name(ctx),ttlsect);
1148    softc->fd_inst = KMALLOC(ttlsect*sizeof(flashinstr_t),0);
1149    if (!softc->fd_inst) return CFE_ERR_NOMEM;
1150
1151    return 0;
1152}
1153
1154
1155/*  *********************************************************************
1156    *  flashdrv_read(ctx,buffer)
1157    *
1158    *  Read data from the flash device.    The flash device is
1159    *  considered to be like a disk (you need to specify the offset).
1160    *
1161    *  Input parameters:
1162    *  	   ctx - device context
1163    *  	   buffer - buffer descriptor
1164    *
1165    *  Return value:
1166    *  	   0 if ok, else error code
1167    ********************************************************************* */
1168
1169static int flashdrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
1170{
1171    flashpart_t *part = ctx->dev_softc;
1172    flashdev_t *softc = part->fp_dev;
1173    int blen;
1174    int offset;
1175
1176    blen = buffer->buf_length;
1177    offset = (long)buffer->buf_offset;
1178
1179    if ((offset + blen) > part->fp_size) {
1180	blen = part->fp_size - offset;
1181	}
1182
1183    offset += part->fp_offset;
1184
1185    if (blen > 0) {
1186	flash_op_begin(softc);
1187	flash_op_add(softc,softc->fd_probe.flash_phys,
1188		     softc->fd_readfunc,(long)buffer->buf_ptr,offset,blen);
1189	flash_op_execute(softc);
1190	}
1191
1192    buffer->buf_retlen = blen;
1193
1194    return 0;
1195}
1196
1197/*  *********************************************************************
1198    *  flashdrv_inpstat(ctx,inpstat)
1199    *
1200    *  Return "input status".  For flash devices, we always return true.
1201    *
1202    *  Input parameters:
1203    *  	   ctx - device context
1204    *  	   inpstat - input status structure
1205    *
1206    *  Return value:
1207    *  	   0 if ok, else error code
1208    ********************************************************************* */
1209
1210static int flashdrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat)
1211{
1212    inpstat->inp_status = 1;
1213    return 0;
1214}
1215
1216
1217/*  *********************************************************************
1218    *  flashdrv_write(ctx,buffer)
1219    *
1220    *  Write data to the flash device.    The flash device is
1221    *  considered to be like a disk (you need to specify the offset).
1222    *
1223    *  Input parameters:
1224    *  	   ctx - device context
1225    *  	   buffer - buffer descriptor
1226    *
1227    *  Return value:
1228    *  	   0 if ok, else error code
1229    ********************************************************************* */
1230
1231static int flashdrv_write2(cfe_devctx_t *ctx,iocb_buffer_t *buffer,int reboot)
1232{
1233    flashpart_t *part = ctx->dev_softc;
1234    flashdev_t *softc = part->fp_dev;
1235    int blen;
1236    int res;
1237    int offset;
1238    int whichchip;
1239    long chipbase;
1240    flash_range_t outrange;
1241    flash_range_t inrange;
1242    flash_sector_t sector;
1243
1244    blen = buffer->buf_length;
1245    offset = (long)buffer->buf_offset;
1246
1247    /* Compute range within physical flash */
1248
1249    if ((offset + blen) > part->fp_size) {
1250	blen = part->fp_size - offset;
1251	}
1252
1253    offset += part->fp_offset;
1254
1255    /* Handle case of writing nothing */
1256
1257    if (blen == 0) {
1258	buffer->buf_retlen = blen;
1259	return (buffer->buf_length == blen) ? 0 : CFE_ERR_IOERR;
1260	}
1261
1262    /* now, offset/blen forms the range we want to write to. */
1263
1264    inrange.range_base = offset;
1265    inrange.range_length = blen;
1266
1267    sector.flash_sector_idx = 0;
1268
1269    flash_op_begin(softc);
1270
1271    for (;;) {
1272	res = flash_sector_query(softc,&sector);
1273	if (res != 0) break;
1274	if (sector.flash_sector_status == FLASH_SECTOR_INVALID) {
1275	    break;
1276	    }
1277
1278	whichchip = sector.flash_sector_idx / softc->fd_ttlsect;
1279	chipbase  = softc->fd_probe.flash_phys +
1280	    (long) (whichchip * softc->fd_probe.flash_size);
1281
1282	res = flash_range_intersection(&sector,&inrange,&outrange);
1283
1284#if FLASH_XOR_ADDR
1285	/* To make the engine's address sequencing work, both the
1286	   source and destination must be word-aligned.  Since all
1287	   updates are by sector, the destination is always aligned,
1288	   and the source is aligned when it is the sector buffer.
1289	   Force copying of a sector-sized source region only if it
1290	   needs alignment. */
1291	if (res == 1) {
1292	    if (((((long)buffer->buf_ptr) +
1293		  (outrange.range_base-inrange.range_base)) & 0x3) != 0) {
1294		res = 0;        /* Force copying. */
1295		}
1296	    }
1297#endif
1298
1299	switch (res) {
1300	    case 1:		/* Erase/program entire sector */
1301		flash_op_add(softc,chipbase,
1302			     softc->fd_erasefunc,
1303			     sector.flash_sector_offset,
1304			     0,0);
1305		flash_op_add(softc,chipbase,
1306			     softc->fd_pgmfunc,
1307			     outrange.range_base,
1308			     ((long)buffer->buf_ptr)+(outrange.range_base-inrange.range_base),
1309			     outrange.range_length);
1310		break;
1311
1312	    case 0:		/* Erase/reprogram partial sector */
1313		/* Save old sector */
1314		flash_op_add(softc,chipbase,
1315			     softc->fd_readfunc,
1316			     (long)(softc->fd_sectorbuffer),
1317			     sector.flash_sector_offset,
1318			     sector.flash_sector_size);
1319		/* Copy in new stuff */
1320		flash_op_add(softc,chipbase,
1321			     FEOP_MEMCPY,
1322			     ((long)(softc->fd_sectorbuffer))+(outrange.range_base-sector.flash_sector_offset),
1323			     ((long)buffer->buf_ptr)+(outrange.range_base-inrange.range_base),
1324			     outrange.range_length);
1325		/* Erase sector */
1326		flash_op_add(softc,chipbase,
1327			     softc->fd_erasefunc,
1328			     sector.flash_sector_offset,
1329			     0,0);
1330		/* Program sector */
1331		flash_op_add(softc,chipbase,
1332			     softc->fd_pgmfunc,
1333			     sector.flash_sector_offset,
1334			     (long)(softc->fd_sectorbuffer),
1335			     sector.flash_sector_size);
1336		break;
1337
1338	    case -1:	   	/* No intersection */
1339		break;
1340	    }
1341
1342	sector.flash_sector_idx++;
1343
1344	}
1345
1346    if (reboot) {
1347	flash_op_add(softc,softc->fd_probe.flash_phys,FEOP_REBOOT,0,0,0);
1348	}
1349
1350    res = flash_op_execute(softc);
1351
1352    buffer->buf_retlen = blen;
1353
1354    return (res == 0) ? 0 : CFE_ERR_IOERR;
1355}
1356
1357static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
1358{
1359    return flashdrv_write2(ctx,buffer,0);
1360}
1361
1362
1363/*  *********************************************************************
1364    *  flashdrv_ioctl(ctx,buffer)
1365    *
1366    *  Handle special IOCTL functions for the flash.  Flash devices
1367    *  support NVRAM information, sector and chip erase, and a
1368    *  special IOCTL for updating the running copy of CFE.
1369    *
1370    *  Input parameters:
1371    *  	   ctx - device context
1372    *  	   buffer - descriptor for IOCTL parameters
1373    *
1374    *  Return value:
1375    *  	   0 if ok else error
1376    ********************************************************************* */
1377static int flashdrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
1378{
1379    flashpart_t *part = ctx->dev_softc;
1380    flashdev_t *softc = part->fp_dev;
1381    nvram_info_t nvinfo;
1382    flash_info_t info;
1383    flash_sector_t fsect;
1384    flash_range_t range;
1385    int offset;
1386    int ret;
1387
1388    switch ((int)buffer->buf_ioctlcmd) {
1389	case IOCTL_NVRAM_GETINFO:
1390	    /*
1391	     * We only support NVRAM on flashes that have been partitioned
1392	     * into at least two partitions.  Every partition supports
1393	     * being an NVRAM in that case, but we'll only attach one
1394	     * of them to the environment subsystem.
1395	     */
1396	    if (softc->fd_probe.flash_nparts <= 1) {
1397		return CFE_ERR_UNSUPPORTED;
1398		}
1399	    if (buffer->buf_length != sizeof(nvram_info_t)) return CFE_ERR_INV_PARAM;
1400
1401	    nvinfo.nvram_offset = 0;
1402	    nvinfo.nvram_size = part->fp_size;
1403	    nvinfo.nvram_eraseflg = 1;
1404	    hs_memcpy_to_hs(buffer->buf_ptr,&nvinfo,sizeof(nvinfo));
1405	    buffer->buf_retlen = sizeof(nvram_info_t);
1406	    return 0;
1407	    break;
1408
1409	case IOCTL_FLASH_ERASE_SECTOR:
1410	    offset = (int) buffer->buf_offset;
1411	    offset += part->fp_offset;
1412	    if (offset >= softc->fd_probe.flash_size) return -1;
1413
1414	    flash_op_begin(softc);
1415	    flash_op_add(softc,
1416			 softc->fd_probe.flash_phys,
1417			 softc->fd_erasefunc,
1418			 offset,
1419			 0,0);
1420	    flash_op_execute(softc);
1421	    return 0;
1422
1423	case IOCTL_FLASH_ERASE_ALL:
1424	    offset = (int) buffer->buf_offset;
1425	    if (offset != 0) return -1;
1426	    flash_erase_all(softc);
1427	    return 0;
1428
1429	case IOCTL_FLASH_WRITE_ALL:
1430	    /* Write file and reboot */
1431	    flashdrv_write2(ctx,buffer,1);
1432	    return -1;		/* should not return */
1433
1434	case IOCTL_FLASH_GETINFO:
1435	    info.flash_base = softc->fd_probe.flash_phys;
1436	    info.flash_size = softc->fd_probe.flash_size;
1437	    info.flash_type = softc->fd_probe.flash_type;
1438	    info.flash_flags = FLASH_FLAG_NOERASE;
1439	    hs_memcpy_to_hs(buffer->buf_ptr,&info,sizeof(info));
1440	    return 0;
1441
1442	case IOCTL_FLASH_GETSECTORS:
1443	    hs_memcpy_from_hs(&fsect,buffer->buf_ptr,sizeof(flash_sector_t));
1444	    ret = flash_sector_query(softc,&fsect);
1445	    hs_memcpy_to_hs(buffer->buf_ptr,&fsect,sizeof(flash_sector_t));
1446	    return ret;
1447
1448	case IOCTL_FLASH_ERASE_RANGE:
1449	    hs_memcpy_from_hs(&range,buffer->buf_ptr,sizeof(flash_range_t));
1450	    range.range_base += part->fp_offset;
1451	    if (range.range_length > part->fp_size) {
1452		range.range_length = part->fp_size;
1453		}
1454	    ret = flash_erase_range(softc,&range);
1455	    hs_memcpy_to_hs(buffer->buf_ptr,&range,sizeof(flash_range_t));
1456	    return ret;
1457
1458
1459	default:
1460	    /* Call hook if present. */
1461	    if (softc->fd_probe.flash_ioctl_hook) {
1462		return (*(softc->fd_probe.flash_ioctl_hook))(ctx,buffer);
1463		}
1464	    return -1;
1465	}
1466
1467    return -1;
1468}
1469
1470
1471/*  *********************************************************************
1472    *  flashdrv_close(ctx)
1473    *
1474    *  Close the flash device.
1475    *
1476    *  Input parameters:
1477    *  	   ctx - device context
1478    *
1479    *  Return value:
1480    *  	   0
1481    ********************************************************************* */
1482static int flashdrv_close(cfe_devctx_t *ctx)
1483{
1484    flashpart_t *part = ctx->dev_softc;
1485    flashdev_t *softc = part->fp_dev;
1486
1487    if (softc->fd_inst) {
1488	KFREE(softc->fd_inst);
1489	}
1490
1491    softc->fd_inst = NULL;
1492
1493    /* XXX Invalidate the cache ?!?! */
1494
1495    return 0;
1496}
1497
1498
1499