1
0
Files
irix-657m-src/stand/arcs/lib/libsk/ml/IP12nvram.c
2022-09-29 17:59:04 +03:00

517 lines
11 KiB
C

/* IP20/22/26/IP28 nvram code
* EEPROM specific code.
*
* Ideally the common routines that aren't different between this file and
* dallas.c should be segrated into a 3rd file, but I didn't want to disturb
* the other functionalities too much.
*/
#if IP20 || IP22 || IP26 || IP28 /* whole file */
#ident "$Revision: 1.22 $"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sbd.h>
#include <sys/cpu.h>
#include <libsk.h>
#include <libsc.h>
#include <arcs/folder.h>
#include <arcs/errno.h>
struct string_list;
#if defined(IP22)
/* These define the EEPROM specific functions from their generic names,
* to an eeprom specific name. This allows co-existance with the dallas
* routines for the same functions.
*/
#define cpu_get_nvram_offset eeprom_cpu_get_nvram_offset
#define cpu_get_nvram_buf eeprom_cpu_get_nvram_buf
#define cpu_set_nvram_offset eeprom_cpu_set_nvram_offset
#define cpu_set_nvram eeprom_cpu_set_nvram
#define cpu_is_nvvalid eeprom_cpu_is_nvvalid
#define cpu_set_nvvalid eeprom_cpu_set_nvvalid
#define cpu_nv_lock eeprom_cpu_nv_lock
#define cpu_get_eaddr eeprom_cpu_get_eaddr
#define cpu_nv_unlock eeprom_cpu_nv_unlock
#define nvram_is_protected eeprom_nvram_is_protected
#define cpu_get_nvram_persist eeprom_cpu_get_nvram_persist
#define cpu_set_nvram_persist eeprom_cpu_set_nvram_persist
#define cpu_nvram_init eeprom_cpu_nvram_init
#endif /* IP22 */
/* Exports */
#define NVRAM_FUNC(type,name,args,pargs,cargs,return) type eeprom_ ## name pargs;
#include <PROTOnvram.h>
/* Private */
static ushort get_nvreg(int);
static int set_nvreg(int, unsigned short);
static void nvram_cs_on(void);
static void nvram_cs_off(void);
static void nvram_cmd(unsigned, unsigned);
static int nvram_hold(void);
static unsigned short nvram_content[NVLEN_MAX / 2];
static int nvram_read;
volatile unsigned char *cpu_auxctl =
(unsigned char *) PHYS_TO_K1( CPU_AUX_CONTROL );
/*
* nvchecksum -- calculate new checksum for non-volatile RAM
*/
char
nvchecksum(void)
{
register int nv_reg;
/*
* Seed the checksum so all-zeroes (all-ones) nvram doesn't have a zero
* (all-ones) checksum.
*/
register signed char checksum = 0xa5;
/* save the whole nvram in memory for speed */
if (!nvram_read) {
int i;
for (i = 0; i < NVLEN_MAX / 2; i++)
nvram_content[i] = get_nvreg(i);
nvram_read = 1;
}
for (nv_reg = 0; nv_reg < NVLEN_MAX / 2; nv_reg++) {
if (nv_reg == (NVOFF_CHECKSUM / 2))
if (NVOFF_CHECKSUM & 0x01)
checksum ^= nvram_content[nv_reg] >> 8;
else
checksum ^= nvram_content[nv_reg] & 0xff;
else
checksum ^= (nvram_content[nv_reg] >> 8) ^
(nvram_content[nv_reg] & 0xff);
/* following is a tricky way to rotate */
checksum = (checksum << 1) | (checksum < 0);
}
return (char)checksum;
}
/*
* get_nvreg -- read a 16 bit register from non-volatile memory. Bytes
* are stored in this string in big-endian order in each 16 bit word.
*/
static ushort
get_nvreg(int nv_regnum)
{
if (nvram_read)
return nvram_content[nv_regnum];
else {
ushort ser_read;
int i;
#ifdef CONSOLE_LED
unsigned char health_led_off = *cpu_auxctl & CONSOLE_LED;
#endif
*cpu_auxctl &= ~NVRAM_PRE;
nvram_cs_on(); /* enable chip select */
nvram_cmd(SER_READ, nv_regnum);
/* clock the data out of serial mem */
for (i = 0; i < 16; i++) {
*cpu_auxctl &= ~SERCLK;
*cpu_auxctl |= SERCLK;
ser_read <<= 1;
ser_read |= (*cpu_auxctl & SER_TO_CPU) ? 1 : 0;
}
nvram_cs_off();
#ifdef CONSOLE_LED
*cpu_auxctl = (*cpu_auxctl & ~CONSOLE_LED) | health_led_off;
#endif
return ser_read;
}
}
/*
* set_nvreg -- writes a 16 bit word into non-volatile memory. Bytes
* are stored in this register in big-endian order in each 16 bit word.
*/
static int
set_nvreg(int nv_regnum, unsigned short val)
{
int error;
int i;
#ifdef CONSOLE_LED
unsigned char health_led_off = *cpu_auxctl & CONSOLE_LED;
#endif
unsigned short data = val;
*cpu_auxctl &= ~NVRAM_PRE;
nvram_cs_on();
nvram_cmd(SER_WEN, 0);
nvram_cs_off();
nvram_cs_on();
nvram_cmd(SER_WRITE, nv_regnum);
/*
* clock the data into serial mem
*/
for (i = 0; i < 16; i++) {
if (val & 0x8000) /* pull the bit out of high order pos */
*cpu_auxctl |= CPU_TO_SER;
else
*cpu_auxctl &= ~CPU_TO_SER;
*cpu_auxctl &= ~SERCLK;
*cpu_auxctl |= SERCLK;
val <<= 1;
}
*cpu_auxctl &= ~CPU_TO_SER; /* see data sheet timing diagram */
nvram_cs_off();
error = nvram_hold();
nvram_cs_on();
nvram_cmd(SER_WDS, 0);
nvram_cs_off();
#ifdef CONSOLE_LED
*cpu_auxctl = (*cpu_auxctl & ~CONSOLE_LED) | health_led_off;
#endif
if (!error)
nvram_content[nv_regnum] = data;
return (error);
}
/*
* enable the serial memory by setting the console chip select
*/
static void
nvram_cs_on(void)
{
*cpu_auxctl &= NVRAM_PRE;
*cpu_auxctl |= CONSOLE_CS;
*cpu_auxctl |= SERCLK;
}
/*
* turn off the chip select
*/
static void
nvram_cs_off(void)
{
*cpu_auxctl &= ~SERCLK;
*cpu_auxctl &= ~CONSOLE_CS;
*cpu_auxctl |= SERCLK;
}
#define BITS_IN_COMMAND 11
/*
* clock in the nvram command and the register number. For the
* natl semi conducter nv ram chip the op code is 3 bits and
* the address is 6/8 bits.
*/
static void
nvram_cmd(unsigned int cmd, unsigned int reg)
{
ushort ser_cmd;
int i;
ser_cmd = cmd | (reg << (16 - BITS_IN_COMMAND)); /* left justified */
for (i = 0; i < BITS_IN_COMMAND; i++) {
if (ser_cmd & 0x8000) /* if high order bit set */
*cpu_auxctl |= CPU_TO_SER;
else
*cpu_auxctl &= ~CPU_TO_SER;
*cpu_auxctl &= ~SERCLK;
*cpu_auxctl |= SERCLK;
ser_cmd <<= 1;
}
*cpu_auxctl &= ~CPU_TO_SER; /* see data sheet timing diagram */
}
/*
* after write/erase commands, we must wait for the command to complete
* write cycle time is 10 ms max (~5 ms nom); we timeout after ~20 ms
* NVDELAY_TIME * NVDELAY_LIMIT = 20 ms
*/
#define NVDELAY_TIME 100 /* 100 us delay times */
#define NVDELAY_LIMIT 200 /* 200 delay limit */
static int
nvram_hold(void)
{
int error;
int timeout = NVDELAY_LIMIT;
nvram_cs_on();
while (!(*cpu_auxctl & SER_TO_CPU) && timeout--)
DELAY(NVDELAY_TIME);
if (!(*cpu_auxctl & SER_TO_CPU))
error = -1;
else
error = 0;
nvram_cs_off();
return (error);
}
/*
* cpu_get_nvram -- read string from nvram at an offset
*/
char *
cpu_get_nvram_offset(int nv_off, int nv_len)
{
static char buf[128];
cpu_get_nvram_buf(nv_off, nv_len, buf);
return buf;
}
/*
* cpu_get_nvram_buf -- the same as cpu_get_nvram, but put it in a buffer
* of our choice
*/
void
cpu_get_nvram_buf(int nv_off, int nv_len, char buf[])
{
char *bufptr;
int i;
unsigned short nvreg;
bufptr = buf;
if (nv_off % 2 == 1) {
nvreg = get_nvreg(nv_off / 2);
*bufptr++ = nvreg & 0xff;
nv_off++;
nv_len--;
}
for (i = 0; i < nv_len / 2; i++) {
nvreg = get_nvreg(nv_off / 2 + i);
*bufptr++ = (char)(nvreg >> 8);
*bufptr++ = (char)(nvreg & 0xff);
}
if (nv_len % 2 == 1) {
nvreg = get_nvreg((nv_off + nv_len) / 2);
*bufptr++ = nvreg >> 8;
}
*bufptr = 0;
}
/*
* cpu_set_nvram_offset -- write a string to nvram at an offset
*/
int
cpu_set_nvram_offset(int nv_off, int nv_len, char *string)
{
unsigned short curval;
char checksum[2];
int nv_off_save;
int i;
nv_off_save = nv_off;
if (nv_off % 2 == 1 && nv_len > 0) {
curval = get_nvreg(nv_off / 2);
curval &= 0xff00;
curval |= *string;
if (set_nvreg(nv_off / 2, curval))
return (-1);
string++;
nv_off++;
nv_len--;
}
for (i = 0; i < nv_len / 2; i++) {
curval = (unsigned short) *string++ << 8;
curval |= *string;
string++;
if (set_nvreg(nv_off / 2 + i, curval))
return (-1);
}
if (nv_len % 2 == 1) {
curval = get_nvreg((nv_off + nv_len) / 2);
curval &= 0x00ff;
curval |= (unsigned short) *string << 8;
if (set_nvreg((nv_off + nv_len) / 2, curval))
return (-1);
}
if (nv_off_save != NVOFF_CHECKSUM) {
checksum[0] = nvchecksum();
checksum[1] = 0;
return cpu_set_nvram_offset(NVOFF_CHECKSUM, NVLEN_CHECKSUM,
checksum);
}
else
return (0);
}
/*
* cpu_set_nvram - set nvram string to value string
* XXX reset of nvram code needs to move out of prom area
* to someplace reasonable (here?)
*/
int
cpu_set_nvram(char *match, char *newstring)
{
extern int _prom;
if (_prom)
return setenv_nvram (match, newstring);
return 0;
}
int
cpu_is_nvvalid(void)
{
/* try twice */
if (nvchecksum() != *cpu_get_nvram_offset(NVOFF_CHECKSUM,NVLEN_CHECKSUM) &&
nvchecksum() != *cpu_get_nvram_offset(NVOFF_CHECKSUM,NVLEN_CHECKSUM) )
return 0;
if (NV_CURRENT_REV !=*cpu_get_nvram_offset(NVOFF_REVISION, NVLEN_REVISION))
return 0;
return 1;
}
/*ARGSUSED*/
void
cpu_set_nvvalid(void (*delstr)(char *,struct string_list *),
struct string_list *env)
{
char csum = NV_CURRENT_REV;
cpu_set_nvram_offset(NVOFF_REVISION, NVLEN_REVISION, &csum);
csum = nvchecksum();
cpu_set_nvram_offset(NVOFF_CHECKSUM, NVLEN_CHECKSUM, &csum);
}
void
cpu_nv_lock(int lock)
{
#ifdef CONSOLE_LED
unsigned char health_led_off = *cpu_auxctl & CONSOLE_LED;
#endif
*cpu_auxctl &= ~NVRAM_PRE;
nvram_cs_on();
nvram_cmd(SER_WEN, 0);
nvram_cs_off();
*cpu_auxctl |= NVRAM_PRE;
nvram_cs_on();
nvram_cmd(SER_PREN, 0);
nvram_cs_off();
nvram_cs_on();
nvram_cmd(SER_PRCLEAR, 0xff);
nvram_cs_off();
if (nvram_hold())
printf("Error -- NVRAM did not complete PRCLEAR cycle\n");
if (lock) {
nvram_cs_on();
nvram_cmd(SER_PREN, 0);
nvram_cs_off();
nvram_cs_on();
nvram_cmd(SER_PRWRITE, NVFUSE_START / 2);
nvram_cs_off();
if (nvram_hold())
printf("Error -- NVRAM did not complete PRWRITE cycle\n");
}
*cpu_auxctl &= ~NVRAM_PRE;
nvram_cs_on();
nvram_cmd(SER_WDS, 0);
nvram_cs_off();
#ifdef CONSOLE_LED
*cpu_auxctl = (*cpu_auxctl & ~CONSOLE_LED) | health_led_off;
#endif
}
int
nvram_is_protected(void)
{
ushort ser_read = 0;
int i;
#ifdef CONSOLE_LED
unsigned char health_led_off = *cpu_auxctl & CONSOLE_LED;
#endif
*cpu_auxctl |= NVRAM_PRE;
nvram_cs_on(); /* enable chip select */
nvram_cmd(SER_PRREAD, 0);
/* clock the data out of serial mem */
for (i = 0; i < 8; i++) {
*cpu_auxctl &= ~SERCLK;
*cpu_auxctl |= SERCLK;
ser_read <<= 1;
ser_read |= (*cpu_auxctl & SER_TO_CPU) ? 1 : 0;
}
nvram_cs_off();
*cpu_auxctl &= ~NVRAM_PRE;
#ifdef CONSOLE_LED
*cpu_auxctl = (*cpu_auxctl & ~CONSOLE_LED) | health_led_off;
#endif
return ser_read != 0xff;
}
/* NVRAM users */
/* the PROM uses this routine for the PROM_EADDR entry point */
void
cpu_get_eaddr(u_char eaddr[])
{
cpu_get_nvram_buf(NVOFF_ENET, NVLEN_ENET, (char *)eaddr);
}
/* These are all stubs for EEPROM based systems */
/* unconditionally unlock dallas lock bit - nop in IP22 */
void
cpu_nv_unlock(void) { }
/* get and set persistent environment variables */
/*ARGSUSED*/
void
cpu_get_nvram_persist(int (*putstr)(char *,char *,struct string_list *),
struct string_list *env) { }
/*ARGSUSED*/
void
cpu_set_nvram_persist(char *a,char *b) { }
/* initialize factory fresh nvram parts */
void
cpu_nvram_init(void) { }
#endif /* IPXX */