1
0
Files
2022-09-29 17:59:04 +03:00

985 lines
23 KiB
C

/***********************************************************************\
* File: fprom.c *
* *
* Support for Am29F080 and Am29LV800 1-megabyte flash PROMs *
* *
* NOTE: Flash PROM cannot be programmed while running out of it. *
* *
* The Hub Flash PROM may only be read via double-word or word *
* accesses. The Hub translates these into 8 and 4 byte reads, *
* respectively. Fortunately, this doesn't cause problems with *
* programming. The 8-bit PROM command register may be written *
* via double-word writes only. Only the LSByte is used, and *
* the PROM address offset must be multiplied by 8. *
* *
* The IO6 Flash PROM may only be read and written via half-word *
* accesses. In each case, the upper byte of each half-word is *
* unused, and the PROM address offset must be multiplied by 2. *
* *
* Programming is doubly verified. The PROM does its own *
* hardware programming verification, plus this module reads *
* back and verifies flashed or programmed data. *
* *
* NOTE: Please keep in sync with kernel version in *
* kern/ml/SN/fprom.c *
* *
\***********************************************************************/
#ident "$Revision: 1.34 $"
#include <sys/types.h>
#include <sys/SN/fprom.h>
#define ABORT (f->afn ? (*f->afn)(f) : 0)
#define AMD29F080_MANU_CODE1 0x01
#define AMD29F080_DEV_CODE1 0xd5
#define AMD29F080_MANU_CODE2 0x01
#define AMD29F080_DEV_CODE2 0xad
#define HY29F080_MANU_CODE 0xad
#define HY29F080_DEV_CODE 0xd5
#define M29F080A_MANU_CODE 0x20
#define M29F080A_DEV_CODE 0xf1
#define AMD29LV800_MANU_CODE 0x01
#define AMD29LV800_DEV_CODE 0x22DA
#define LBYTEU(caddr) \
(uchar_t) ((*(uint *) ((__psunsigned_t) (caddr) & ~3) << \
((__psunsigned_t) (caddr) & 3) * 8) >> 24)
#define LBYTEUV(caddr) \
(uchar_t) ((*(volatile uint *) ((__psunsigned_t) (caddr) & ~3) << \
((__psunsigned_t) (caddr) & 3) * 8) >> 24)
#define LB_HUB(_base, _offset) \
LBYTEUV((uchar_t *) (_base) + (_offset))
#define SB_HUB(_base, _offset, _byte) \
(*((volatile __uint64_t *) (_base) + (_offset)) = (_byte))
#define LD_HUB(_base, _offset) \
(*(volatile __uint64_t *) ((__psunsigned_t) (_base) + (_offset)))
#define LB_IO6_P0(_base, _offset) \
(*((volatile ushort_t *) (_base) + (_offset)))
#define SB_IO6_P0(_base, _offset, _byte) \
(*((volatile ushort_t *) (_base) + (_offset)) = (_byte))
/* base + offset should be aligned. Checkit. */
#define LB_IO6_P1(_base, _offset) \
(*((volatile uchar_t *) (_base) + (_offset)))
#define LH_IO6_P1(_base, _offset) \
(*((volatile ushort_t *) (((uchar_t *)_base + _offset))))
#define SHCMD_IO6_P1(_base, _offset, _short) \
(*((volatile ushort_t *) (_base) + (_offset)) = (_short))
#define SHDATA_IO6_P1(_base, _offset, _short) \
(*((volatile ushort_t *) ((uchar_t *)_base + _offset)) = (_short))
#define SN00_ADDR_SWIZZLE(A) ( \
(((A) & 0xfff00000)) | \
(((A) & 0x0000f)) | \
(((A) & 0x00010) << 11) | \
(((A) & 0x00020) << 9) | \
(((A) & 0x00040) << 7) | \
(((A) & 0x00080) << 5) | \
(((A) & 0x00100) << 3) | \
(((A) & 0x00200) << 1) | \
(((A) & 0x00400) >> 1) | \
(((A) & 0x00800) >> 3) | \
(((A) & 0x01000) >> 5) | \
(((A) & 0x02000) >> 7) | \
(((A) & 0x04000) >> 9) | \
(((A) & 0x08000) >> 11) | \
(((A) & 0xf0000)))
#define SN00_DATA_SWIZZLE(D) ( \
(((D) & 0x01) << 7) | \
(((D) & 0x02) << 5) | \
(((D) & 0x04) << 3) | \
(((D) & 0x08) << 1) | \
(((D) & 0x10) >> 1) | \
(((D) & 0x20) >> 3) | \
(((D) & 0x40) >> 5) | \
(((D) & 0x80) >> 7))
/*
* cmd: Device-specific command write
*/
static
void
cmd(fprom_t *f, fprom_off_t offset, uchar_t byte)
{
#ifndef EMULATE
switch (f->dev) {
case FPROM_DEV_HUB:
if (f->swizzle)
SB_HUB(f->base, SN00_ADDR_SWIZZLE(offset),
SN00_DATA_SWIZZLE(byte));
else
SB_HUB(f->base, offset, byte);
break;
case FPROM_DEV_IO6_P0:
SB_IO6_P0(f->base, offset, byte);
break;
case FPROM_DEV_IO6_P1:
SHCMD_IO6_P1(f->base, offset, (short)byte);
break ;
}
#endif /* !defined(EMULATE) */
}
/*
* fprom_wait
*
* Wait for the PROM to finish programming or time out.
* We allow aborting via the abort function.
*/
static int fprom_wait(fprom_t *f, fprom_off_t offset, uchar_t byte)
{
uchar_t status;
int i;
uchar_t timeout_mask;
if (f->swizzle)
timeout_mask = SN00_DATA_SWIZZLE(0x20);
else
timeout_mask = 0x20;
switch (f->dev) {
case FPROM_DEV_HUB:
while (! ABORT) {
#ifdef EMULATE
status = byte;
#else /* EMULATE */
status = LB_HUB(f->base, offset);
#endif /* EMULATE */
if (status == byte)
return 0; /* Programming complete */
if (status & timeout_mask) {
status = LB_HUB(f->base, offset);
if (status == byte)
return 0; /* Programming complete */
return FPROM_ERROR_TIMEOUT;
}
}
break;
case FPROM_DEV_IO6_P0:
while (! ABORT) {
#ifdef EMULATE
status = byte;
#else /* EMULATE */
status = LB_IO6_P0(f->base, offset);
#endif /* EMULATE */
if (status == byte)
return 0; /* Programming complete */
if (status & timeout_mask) {
status = LB_IO6_P0(f->base, offset);
if (status == byte)
return 0; /* Programming complete */
return FPROM_ERROR_TIMEOUT;
}
}
break;
case FPROM_DEV_IO6_P1:
/* XXX - Bring this back in, as flash does not seem to work. */
for (i=0; i<64; i++) /* P1 flash needs lots of delay */
LB_IO6_P1(f->base, 0);
while (! ABORT) {
#ifdef EMULATE
status = byte;
#else /* EMULATE */
status = LB_IO6_P1(f->base, offset);
#endif /* EMULATE */
if (status == byte)
return 0; /* Programming complete */
if (status & timeout_mask) {
status = LB_IO6_P1(f->base, offset);
if (status == byte)
return 0; /* Programming complete */
return FPROM_ERROR_TIMEOUT;
}
}
break;
}
return FPROM_ERROR_ABORT;
}
/*
* fprom_reset
*
* Puts a PROM into regular read mode, in case it's not already, which
* often happens if a PROM programming or probing operation takes an
* exception.
*/
void fprom_reset(fprom_t *f)
{
cmd(f, 0x0000, 0xf0); /* Reset to synchronize */
}
/*
* fprom_probe
*
* Physically resets the PROM to get it into the read state. Verifies
* the PROM is connected and well-behaved by reading its manufacturer and
* device codes. Places it into normal read mode.
*
* *** NOTE ***
*
* This call MUST be used before any call that attempts to WRITE to
* the flash PROM (fprom_flash_sectors, fprom_write, etc). It sets
* the appropriate swizzle state in the fprom_t structure.
*
* Returns FPROM_ERROR_code.
*/
static int do_probe(fprom_t *f, int *manu_code, int *dev_code)
{
fprom_reset(f); /* Reset to synchronize */
cmd(f, 0x5555, 0xaa); /* Enter autoselect mode */
cmd(f, 0x2aaa, 0x55);
cmd(f, 0x5555, 0x90);
/* Retrieve manufacturer and device codes */
switch (f->dev) {
case FPROM_DEV_HUB:
*manu_code = LB_HUB(f->base, 0);
if (f->swizzle) {
*manu_code = SN00_DATA_SWIZZLE(*manu_code);
*dev_code = LB_HUB(f->base, SN00_ADDR_SWIZZLE(1));
*dev_code = SN00_DATA_SWIZZLE(*dev_code);
} else
*dev_code = LB_HUB(f->base, 1);
break;
case FPROM_DEV_IO6_P0:
*manu_code = LB_IO6_P0(f->base, 0);
*dev_code = LB_IO6_P0(f->base, 1);
break;
case FPROM_DEV_IO6_P1:
*manu_code = LH_IO6_P1(f->base, 0);
*dev_code = LH_IO6_P1(f->base, 2);
break;
}
#ifdef EMULATE
*manu_code = AMD29F080_MANU_CODE1;
*dev_code = AMD29F080_DEV_CODE1;
#endif /* EMULATE */
fprom_reset(f); /* Reset to exit autoselect mode */
if (*manu_code == AMD29LV800_MANU_CODE &&
*dev_code == AMD29LV800_DEV_CODE) {
f->dev = FPROM_DEV_IO6_P1 ;
return FPROM_ERROR_NONE;
}
if (*manu_code == HY29F080_MANU_CODE &&
*dev_code == HY29F080_DEV_CODE)
return FPROM_ERROR_NONE;
if (*manu_code == M29F080A_MANU_CODE &&
*dev_code == M29F080A_DEV_CODE)
return FPROM_ERROR_NONE;
if (*manu_code == AMD29F080_MANU_CODE1 &&
(*dev_code == AMD29F080_DEV_CODE1 ||
*dev_code == AMD29F080_DEV_CODE2))
return FPROM_ERROR_NONE;
return FPROM_ERROR_DEVICE;
}
int
fprom_probe(fprom_t *f, int *manu_code, int *dev_code)
{
int r1, r2;
f->swizzle = 0;
r1 = do_probe(f, manu_code, dev_code);
/*
* P0 SN00 have a problem with swizzled address and data
* Here we try and figure out if we have a problem.
*/
if (r1 != FPROM_ERROR_NONE && SN0) {
f->swizzle = 1;
r2 = do_probe(f, manu_code, dev_code);
if (r2 != FPROM_ERROR_NONE) {
f->swizzle = 0;
return(r1);
}
return (r2);
}
return r1;
}
/*
* fprom_flash_verify
*
* Verifies that a specified sector of the PROM is all 1's.
* Returns FPROM_ERROR_code.
*/
#ifdef SABLE
#define INCR 16
#else /* SABLE */
#define INCR 1
#endif
int fprom_flash_verify(fprom_t *f, int sector)
{
fprom_off_t istart, iend, i;
if (ABORT)
return FPROM_ERROR_ABORT;
istart = (sector + 0) * FPROM_SECTOR_SIZE;
iend = (sector + 1) * FPROM_SECTOR_SIZE;
switch (f->dev) {
case FPROM_DEV_HUB:
for (i = istart; i < iend; i += 8)
if (LD_HUB(f->base, i) != ~0ULL) /* Fast native read size */
return FPROM_ERROR_VERIFY;
break;
case FPROM_DEV_IO6_P0:
for (i = istart; i < iend; i++)
if (LB_IO6_P0(f->base, i) != 0xff) /* Oh well */
return FPROM_ERROR_VERIFY;
break;
case FPROM_DEV_IO6_P1:
for (i = istart; i < iend; i++)
if (LB_IO6_P1(f->base, i) != 0xff) /* Oh well */
return FPROM_ERROR_VERIFY;
break;
}
return FPROM_ERROR_NONE;
}
#ifndef SN0 /* SN0 never uses this case, and needs to save code space */
#if 0
/*
* fprom_flash_all
*
* Flashes the entire PROM. Verifies that every byte of the PROM is
* 0xff. Returns FPROM_ERROR_code.
*/
int fprom_flash_all(fprom_t *f)
{
int i, r;
cmd(f, 0x5555, 0xaa); /* Chip Erase */
cmd(f, 0x2aaa, 0x55);
cmd(f, 0x5555, 0x80);
cmd(f, 0x5555, 0xaa);
cmd(f, 0x2aaa, 0x55);
cmd(f, 0x5555, 0x10);
#ifdef EMULATE
memset(f->base, 0xff, FPROM_SIZE);
#endif /* EMULATE */
if ((r = fprom_wait(f, 0, (uchar_t) 0xff)) < 0)
return r;
for (i = 0; i < FPROM_SECTOR_COUNT; i++)
if ((r = fprom_flash_verify(f, i)) < 0)
return r;
return FPROM_ERROR_NONE;
}
#endif
#endif
/*
* do_flash
*
* Erase a single (sub-)sector, and wait for the chip to finish.
*/
static int do_flash(fprom_t *f, fprom_off_t cmdaddr, fprom_off_t waitaddr)
{
cmd(f, 0x5555, 0xaa); /* Sector Erase prefix */
cmd(f, 0x2aaa, 0x55);
cmd(f, 0x5555, 0x80);
cmd(f, 0x5555, 0xaa);
cmd(f, 0x2aaa, 0x55);
cmd(f, cmdaddr, 0x30); /* Sector Erase command */
return fprom_wait(f, waitaddr, (uchar_t) 0xff);
}
/*
* fprom_flash_sectors
*
* *** NOTE *** fprom_probe must be called first; see comment there.
*
* Flashes any combination of 64K PROM sectors.
* Sector_mask contains a mask of 64K sectors to erased: bit 0 erases
* sector 0, etc. Waits for the operation to complete, then verifies that
* the erased sectors contain 0xff. Returns FPROM_ERROR_code.
* Sectors are flashed one at a time, because we can't guarantee
* that we will meet the PROM chip's multi-sector setup timeout when
* running in UNCAC or DEX mode.
*/
int fprom_flash_sectors(fprom_t *f, int sector_mask)
{
int i, r;
fprom_reset(f); /* Reset */
if ((sector_mask &= 0xffff) == 0)
return FPROM_ERROR_NONE; /* Handle empty sector_mask */
#ifndef SN0 /* SN0 never uses this case, and needs to save code space */
#if 0
/* When flashing the entire chip, use the full-chip erase sequence */
if (sector_mask == FPROM_SECTOR_MASK)
return fprom_flash_all(f);
#endif
#endif
/*
* Write erase commands to target sectors
*/
for (i = 0; i < FPROM_SECTOR_COUNT; i++)
if (sector_mask & 1 << i) {
#ifdef EMULATE
memset((char *) f->base + i * FPROM_SECTOR_SIZE,
0xff,
FPROM_SECTOR_SIZE);
#endif /* EMULATE */
switch (f->dev) {
case FPROM_DEV_HUB:
do_flash(f, i * FPROM_SECTOR_SIZE, i * FPROM_SECTOR_SIZE);
break;
case FPROM_DEV_IO6_P0:
if (i < 15)
do_flash(f, i * FPROM_SECTOR_SIZE, i * FPROM_SECTOR_SIZE);
else {
do_flash(f, 0xf0000, 0xf0000);
do_flash(f, 0xf8000, 0xf8000);
do_flash(f, 0xfa000, 0xfa000);
do_flash(f, 0xfc000, 0xfc000);
}
break ;
case FPROM_DEV_IO6_P1:
if (i < 15)
do_flash(f, i * (FPROM_SECTOR_SIZE / 2),
i * FPROM_SECTOR_SIZE);
else {
do_flash(f, 0xf0000 / 2, 0xf0000);
do_flash(f, 0xf8000 / 2, 0xf8000);
do_flash(f, 0xfa000 / 2, 0xfa000);
do_flash(f, 0xfc000 / 2, 0xfc000);
}
break ;
#ifdef SN1
case FPROM_DEV_BEDROCK:
/* The last 64k sector is made up of 4 smaller sectors.
* Divide the byte offsets by 2 to get the word offsets
* needed by the chip */
if (i < 31)
do_flash(f, (i * FPROM_SECTOR_SIZE) / 2,
i * FPROM_SECTOR_SIZE);
else {
do_flash(f, (i * FPROM_SECTOR_SIZE + 0*1024) / 2,
i * FPROM_SECTOR_SIZE + 0*1024 );
do_flash(f, (i * FPROM_SECTOR_SIZE + 32*1024) / 2,
i * FPROM_SECTOR_SIZE + 32*1024 );
do_flash(f, (i * FPROM_SECTOR_SIZE + 40*1024) / 2,
i * FPROM_SECTOR_SIZE + 40*1024 );
do_flash(f, (i * FPROM_SECTOR_SIZE + 48*1024) / 2,
i * FPROM_SECTOR_SIZE + 48*1024 );
}
break;
#endif /* SN1 */
}
}
for (i = 0; i < FPROM_SECTOR_COUNT; i++)
if (sector_mask & 1 << i)
if ((r = fprom_flash_verify(f, i)) < 0)
return r;
return FPROM_ERROR_NONE;
}
/*
* fprom_read
*
* Copy characters from the PROM into a buffer, using only the kind
* of reads that are allowed from the PROM. Returns FPROM_ERROR_code.
*/
int fprom_read(fprom_t *f, fprom_off_t offset, char *buf, int len)
{
if (len == 0)
return FPROM_ERROR_NONE;
switch (f->dev) {
case FPROM_DEV_HUB:
{
__uint64_t *p, t, s;
p = (__uint64_t *) (((__psunsigned_t) f->base + offset) & ~7);
t = (__uint64_t ) (((__psunsigned_t) f->base + offset) & 7);
s = (*(__uint64_t *) p) << 8 * t;
for (; t < 8 && len-- > 0; t++) {
*buf++ = (char) (s >> 56);
s <<= 8;
}
while (len > 0) {
s = *(__uint64_t *) ++p;
for (t = 0; t < 8 && len-- > 0; t++) {
*buf++ = (char) (s >> 56);
s <<= 8;
}
}
}
break;
case FPROM_DEV_IO6_P0:
{
fprom_off_t soff = offset;
fprom_off_t eoff = offset + (fprom_off_t) len;
fprom_off_t off;
for (off = soff; off < eoff; off++)
*buf++ = LB_IO6_P0(f->base, off);
}
break;
case FPROM_DEV_IO6_P1:
{
fprom_off_t soff = offset;
fprom_off_t eoff = offset + (fprom_off_t) len;
fprom_off_t off;
for (off = soff; off < eoff; off++)
*buf++ = LB_IO6_P1(f->base, off);
}
break;
}
return FPROM_ERROR_NONE;
}
/*
* fprom_validate
*
* Compare part of the PROM with the contents of the buffer. If copying
* the buffer to that part of the PROM would be illegal (because 0s would
* have to be programmed into 1s) returns FPROM_ERROR_CONFLICT.
*/
int fprom_validate(fprom_t *f, fprom_off_t offset, char *buf, int len)
{
if (ABORT)
return FPROM_ERROR_ABORT;
if (len == 0)
return FPROM_ERROR_NONE;
switch (f->dev) {
case FPROM_DEV_HUB:
{
__uint64_t *p, t, s;
p = (__uint64_t *) (((__psunsigned_t) f->base + offset) & ~7);
t = (__uint64_t ) (((__psunsigned_t) f->base + offset) & 7);
s = (*(__uint64_t *) p) << 8 * t;
for (; t < 8 && len-- > 0; t++, buf++) {
if (~s >> 56 & LBYTEU(buf))
return FPROM_ERROR_CONFLICT;
s <<= 8;
}
while (len > 0) {
s = *(__uint64_t *) ++p;
for (t = 0; t < 8 && len-- > 0; t++, buf++) {
if (~s >> 56 & LBYTEU(buf))
return FPROM_ERROR_CONFLICT;
s <<= 8;
}
}
}
break;
case FPROM_DEV_IO6_P0:
{
fprom_off_t soff = offset;
fprom_off_t eoff = offset + (fprom_off_t) len;
fprom_off_t off;
for (off = soff; off < eoff; off++, buf++)
if (~(LB_IO6_P0(f->base, off)) & LBYTEU(buf))
return FPROM_ERROR_CONFLICT;
}
break;
case FPROM_DEV_IO6_P1:
{
fprom_off_t soff = offset;
fprom_off_t eoff = offset + (fprom_off_t) len;
fprom_off_t off;
for (off = soff; off < eoff; off++, buf++)
if (~(LB_IO6_P1(f->base, off)) & LBYTEU(buf))
return FPROM_ERROR_CONFLICT;
}
break;
}
return FPROM_ERROR_NONE;
}
/*
* fprom_verify
*
* Compare part of the PROM with the contents of the buffer.
* If they differ, returns FPROM_ERROR_VERIFY.
*/
int fprom_verify(fprom_t *f, fprom_off_t offset, char *buf, int len)
{
if (len == 0)
return FPROM_ERROR_NONE;
switch (f->dev) {
case FPROM_DEV_HUB:
{
__uint64_t *p, t, s;
p = (__uint64_t *) (((__psunsigned_t) f->base + offset) & ~7);
t = (__uint64_t ) (((__psunsigned_t) f->base + offset) & 7);
s = (*(__uint64_t *) p) << 8 * t;
for (; t < 8 && len-- > 0; t++, buf++) {
if (s >> 56 != LBYTEU(buf))
return FPROM_ERROR_VERIFY;
s <<= 8;
}
while (len > 0) {
s = *(__uint64_t *) ++p;
for (t = 0; t < 8 && len-- > 0; t++, buf++) {
if (s >> 56 != LBYTEU(buf))
return FPROM_ERROR_VERIFY;
s <<= 8;
}
}
}
break;
case FPROM_DEV_IO6_P0:
{
fprom_off_t soff = offset;
fprom_off_t eoff = offset + (fprom_off_t) len;
fprom_off_t off;
for (off = soff; off < eoff; off++, buf++)
if (LB_IO6_P0(f->base, off) != LBYTEU(buf))
return FPROM_ERROR_VERIFY;
}
break;
case FPROM_DEV_IO6_P1:
{
fprom_off_t soff = offset;
fprom_off_t eoff = offset + (fprom_off_t) len;
fprom_off_t off;
for (off = soff; off < eoff; off++, buf++)
if (LB_IO6_P1(f->base, off) != LBYTEU(buf))
return FPROM_ERROR_VERIFY;
}
break;
}
return FPROM_ERROR_NONE;
}
/*
* do_write (INTERNAL)
*
* Programs characters from a buffer into the PROM.
* Does not validate or verify. Returns FPROM_ERROR_code.
*/
static int do_write(fprom_t *f, fprom_off_t offset, char *buf, int len)
{
int r;
uchar_t old, new;
#ifdef EMULATE
for (r = 0; r < len; r++)
if (~*((char *) f->base + offset + r) & LBYTEU(&buf[r]))
printf("Conflict, offset 0x%x\n", offset + r);
else
*((char *) f->base + offset + r) = LBYTEU(&buf[r]);
return 0;
#else /* EMULATE */
switch (f->dev) {
case FPROM_DEV_HUB:
for (; len-- > 0; buf++, offset++) {
old = LB_HUB(f->base, offset);
new = LBYTEU(buf);
if (~old & new)
return FPROM_ERROR_CONFLICT;
else if (old != new) {
/* printf("Trying to store byte, old is %d new is %d \n",old,new); */
cmd(f, 0x5555, 0xaa);
cmd(f, 0x2aaa, 0x55);
cmd(f, 0x5555, 0xa0);
SB_HUB(f->base, offset, new);
if ((r = fprom_wait(f, offset, new)) < 0)
return r;
}
}
break;
case FPROM_DEV_IO6_P0:
for (; len-- > 0; buf++, offset++) {
old = LB_IO6_P0(f->base, offset);
new = LBYTEU(buf);
if (~old & new)
return FPROM_ERROR_CONFLICT;
else if (old != new) {
SB_IO6_P0(f->base, 0x5555, 0xaa);
SB_IO6_P0(f->base, 0x2aaa, 0x55);
SB_IO6_P0(f->base, 0x5555, 0xa0);
SB_IO6_P0(f->base, offset, new);
if ((r = fprom_wait(f, offset, new)) < 0)
return r;
}
}
break;
case FPROM_DEV_IO6_P1:
{
ushort_t *sp = (ushort_t *) buf, old_s;
int length = len;
if (((__psunsigned_t) buf | (__psunsigned_t) length) & 1)
return FPROM_ERROR_ODDIO6;
for (; length > 0; sp++, (offset += 2), (length-=2)) {
old_s = LH_IO6_P1(f->base, offset);
if (~old_s & (ushort_t) *sp)
return FPROM_ERROR_CONFLICT;
else if (old_s != (ushort_t) *sp) {
SHCMD_IO6_P1(f->base, 0x5555, 0xaa);
SHCMD_IO6_P1(f->base, 0x2aaa, 0x55);
SHCMD_IO6_P1(f->base, 0x5555, 0xa0);
SHDATA_IO6_P1(f->base, offset, (ushort_t) *sp);
if ((r = fprom_wait(f, offset, (uchar_t) (*sp >> 8))) < 0)
return r;
}
}
}
break;
#ifdef SN1
case FPROM_DEV_BEDROCK:
{
/* Bedrock only writes 16 bits at a time */
ushort_t old, new;
/* WARNING: endian-dependent code! */
if (offset & 1) {
/* set the low byte of the overlapping word */
offset &= ~1;
old = LH_BR(f->base, offset);
new = old & 0xff00 | buf[0];
if (~old & new) {
/* printf("err: old=0x%x, new=0x%x, offset=0x%x\n", old, new, offset); */
return FPROM_ERROR_CONFLICT;
}
else if (old != new) {
/* printf("Trying to store halfword, old is %d new is %d \n",old,new); */
cmd(f, 0x5555, 0xaa);
cmd(f, 0x2aaa, 0x55);
cmd(f, 0x5555, 0xa0);
SHDATA_BR(f->base, offset, new);
if ((r = fprom_wait(f, offset, new & 0xff)) < 0)
return r;
}
offset += 2; buf++; len--;
}
for (; len > 1; buf +=2, offset +=2, len -= 2) {
old = LH_BR(f->base, offset);
new = buf[0] << 8 | buf[1];
if (~old & new) {
/* printf("err: old=0x%x, new=0x%x, offset=0x%x\n", old, new, offset); */
return FPROM_ERROR_CONFLICT;
}
else if (old != new) {
/* printf("Trying to store halfword, old is %d new is %d \n",old,new); */
cmd(f, 0x5555, 0xaa);
cmd(f, 0x2aaa, 0x55);
cmd(f, 0x5555, 0xa0);
SHDATA_BR(f->base, offset, new);
if ((r = fprom_wait(f, offset, new & 0xff)) < 0)
return r;
}
}
if (len) {
/* set the high byte of the overlapping word */
old = LH_BR(f->base, offset);
new = old & 0x00ff | buf[0] << 8;
if (~old & new) {
/* printf("err: old=0x%x, new=0x%x, offset=0x%x\n", old, new, offset); */
return FPROM_ERROR_CONFLICT;
}
else if (old != new) {
/* printf("Trying to store halfword, old is %d new is %d \n",old,new); */
cmd(f, 0x5555, 0xaa);
cmd(f, 0x2aaa, 0x55);
cmd(f, 0x5555, 0xa0);
SHDATA_BR(f->base, offset, new);
if ((r = fprom_wait(f, offset, new & 0xff)) < 0)
return r;
}
}
}
break;
#endif /* SN1 */
}
return FPROM_ERROR_NONE;
#endif /* EMULATE */
}
/*
* fprom_write
*
* *** NOTE *** fprom_probe must be called first; see comment there.
*
* Programs characters from a buffer into the PROM, then verifies.
* Returns FPROM_ERROR_code.
*/
int fprom_write(fprom_t *f, fprom_off_t offset, char *buf, int len)
{
int r;
if ((r = do_write(f, offset, buf, len)) < 0)
return r;
if ((r = fprom_verify(f, offset, buf, len)) < 0)
return r;
return FPROM_ERROR_NONE;
}
/*
* fprom_errmsg
*
* Translates an FPROM_ERROR_code into an appropriate message string.
*/
char *fprom_errmsg(int rc)
{
switch (rc) {
case FPROM_ERROR_NONE:
return "No error";
case FPROM_ERROR_RESPOND:
return "Chip not responding";
case FPROM_ERROR_TIMEOUT:
return "Programming operation timed out";
case FPROM_ERROR_CONFLICT:
return "Cannot program a 0 into a 1";
case FPROM_ERROR_VERIFY:
return "Data verify failed";
case FPROM_ERROR_ABORT:
return "Operation externally aborted";
case FPROM_ERROR_DEVICE:
return "Unknown manufacturer/device ID";
case FPROM_ERROR_ODDIO6:
return "Odd buffer or length not allowed with IO6 P1";
default:
return "Undefined error code";
}
}
#ifndef _STANDALONE
/*
* memset
*/
void *
memset(void *base, int value, size_t len)
{
char *dst = base;
if (len >= 8) {
__uint64_t rep;
rep = (__uint64_t) value & 0xff;
rep |= rep << 8;
rep |= rep << 16;
rep |= rep << 32;
while (len > 0 && (__psunsigned_t) dst & 7) {
*dst++ = value;
len--;
}
while (len >= 32) {
((__uint64_t *) dst)[0] = rep;
((__uint64_t *) dst)[1] = rep;
((__uint64_t *) dst)[2] = rep;
((__uint64_t *) dst)[3] = rep;
dst += 32;
len -= 32;
}
while (len >= 8) {
*(__uint64_t *) dst = rep;
dst += 8;
len -= 8;
}
}
while (len--)
*dst++ = value;
return base;
}
#endif /* !_STANDALONE */