Source to src/rtcnvram.c
/* Previous - rtcnvram.c
This file is distributed under the GNU Public License, version 2 or at
your option any later version. Read the file gpl.txt for details.
Emulation of Real Time Clock including NVRAM.
Old systems use MC68HC68T1 chip, new systems use MCCS1850 chip.
*/
#include "ioMem.h"
#include "ioMemTables.h"
#include "m68000.h"
#include "configuration.h"
#include "dimension.hpp"
#include "sysReg.h"
#include "rtcnvram.h"
#include <time.h>
#define LOG_RTC_LEVEL LOG_DEBUG
/* RTC interface */
#define RTC_ADDR_WRITE 0x80
#define RTC_ADDR_CLOCK 0x20
#define RTC_ADDR_MASK 0x7F
Uint8 rtc_addr = 0;
Uint8 rtc_val = 0;
int phase = 0;
int oldrtc_interface_io(Uint8 rtdatabit);
int newrtc_interface_io(Uint8 rtdatabit);
int rtc_interface_io(Uint8 rtdatabit) {
switch (ConfigureParams.System.nRTC) {
case MC68HC68T1: return oldrtc_interface_io(rtdatabit);
case MCCS1850: return newrtc_interface_io(rtdatabit);
default:
Log_Printf(LOG_WARN, "[RTC] error: no I/O function for this chip!");
return oldrtc_interface_io(rtdatabit); /* trying old chip */
}
}
void rtc_interface_reset(void) {
phase = 0;
rtc_addr = 0;
rtc_val = 0;
}
/* RTC power down request */
void oldrtc_request_power_down(void);
void newrtc_request_power_down(void);
void oldrtc_stop_pdown_request(void);
void newrtc_stop_pdown_request(void);
void rtc_request_power_down(void) {
switch (ConfigureParams.System.nRTC) {
case MC68HC68T1: oldrtc_request_power_down(); return;
case MCCS1850: newrtc_request_power_down(); return;
default:
Log_Printf(LOG_WARN, "[RTC] error: no power down function for this chip!");
oldrtc_request_power_down(); return; /* trying old chip */
}
}
void rtc_stop_pdown_request(void) {
switch (ConfigureParams.System.nRTC) {
case MC68HC68T1: oldrtc_stop_pdown_request(); return;
case MCCS1850: newrtc_stop_pdown_request(); return;
default:
Log_Printf(LOG_WARN, "[RTC] error: no power down function for this chip!");
oldrtc_stop_pdown_request(); return; /* trying old chip */
}
}
/* --------------------- MC68HC68T1 --------------------- */
/* RTC NVRAM is located at address 0x00 to 0x1F (32 bytes),
* time registers, alarm registers and control/status
* registers are located at address 0x20 to 0x32.
*/
Uint8 rtc_get_clock(Uint8 addr);
void rtc_put_clock(Uint8 addr, Uint8 val);
/* All time values in RTC clock are in packed decimal format */
typedef struct {
Uint8 sec; /* 00 - 59 */
Uint8 min; /* 00 - 59 */
Uint8 hour; /* 01 - 12 or 00 - 24; bit 7: 1 = 12 hr, 0 = 24 hr; bit 5: 1 = pm, 0 = am */
Uint8 wday; /* 01 - 07; 1 = sunday */
Uint8 mday; /* 01 - 31 */
Uint8 month; /* 01 - 12; 1 = january */
Uint8 year; /* 00 - 99 */
} RTC_TIME;
RTC_TIME get_rtc_time(void);
typedef struct {
Uint8 sec; /* 00 - 59 */
Uint8 min; /* 00 - 59 */
Uint8 hour; /* 01 - 12 or 00 - 24; bit 5: 1 = pm, 0 = am in 24 hr mode */
} RTC_ALARM;
/* There are 3 status and control registers inside the chip */
/* RTC Status Register at 0x30 (r only)
*
* 0-0- ---- always 0 (0 in bit 7 identifies MC68HC68T1 chip)
* -x-- ---- watchdog detected cpu failure
* ---x ---- first time up
* ---- x--- interrupt true (one of following interrupts is valid)
* ---- -x-- power sense interrupt
* ---- --x- alarm interrupt
* ---- ---x clock interrupt
*/
#define RTC_CPUFAIL 0x40
#define RTC_FIRSTUP 0x10
#define RTC_INT 0x08
#define RTC_INT_SENSE 0x04
#define RTC_INT_ALARM 0x02
#define RTC_INT_CLOCK 0x01
/* RTC Clock Control Register at 0x31 (r) or 0xB1 (w)
*
* x--- ---- 1 = start, 0 = stop counter
* -x-- ---- 1 = enable line input, 0 = enable chrystal input
* --xx ---- chrystal select: 0 = 4.194304 MHz, 1 = 2.097152, 2 = 1.048576, 3 = 32.768 kHz
* ---- x--- 1 = line input 50 Hz, 0 = line input 60 Hz
* ---- -xxx clock out frequency:
*
* 0 = chrystal
* 1 = chrystal/2
* 2 = chrystal/4
* 3 = chrystal/8
* 4 = disable
* 5 = 1 Hz
* 6 = 2 Hz
* 7 = 50/60 Hz for line operation; 64 Hz for chrystal operation
*/
#define RTC_START 0x80
#define RTC_STOP 0x00
#define RTC_LINE 0x40
#define RTC_XTAL 0x30
#define RTC_L50HZ 0x08
#define RTC_FREQ 0x07
/* RTC Interrupt Control Register at 0x32 (r) or 0xB2 (w)
*
* x--- ---- watchdog enable
* -x-- ---- initiate power down
* --x- ---- power sense
* ---x ---- enable alarm
* ---- xxxx select frequency of periodic interrupt:
*
* 0 = diable
* chrystal:
* 1 = 2048 Hz, 2 = 1024 Hz, ... , C = 1 Hz
* D = 1 per min, E = 1 per hour, F = 1 per day
* line:
* 6 = 50 or 60 Hz, B = 2 Hz, C = 1 Hz
* D = 1 per min, E = 1 per hour, F = 1 per day
*/
#define RTC_WATCHDOG 0x80
#define RTC_POWERDOWN 0x40
#define RTC_PWRSENSE 0x20
#define RTC_ENABLEALRM 0x10
#define RTC_PERIODIC 0x0F
struct {
Uint8 ram[32]; /* 0x00 - 0x1F (r), 0x80 - 0x9F (w) */
RTC_TIME time; /* 0x20 - 0x26 (r), 0xA0 - 0xA6 (w) */
RTC_ALARM alarm; /* 0xA8 - 0xAA (w) */
Uint8 status; /* 0x30 (r) */
Uint8 clkctrl; /* 0x31 (r), 0xB1 (w) */
Uint8 intctrl; /* 0x32 (r), 0xB2 (w) */
} rtc;
int oldrtc_interface_io(Uint8 rtdatabit) {
phase++;
if (phase<=8) {
rtc_addr = (rtc_addr<<1)|(rtdatabit?1:0);
} else {
if (phase==9) {
if (!(rtc_addr&RTC_ADDR_WRITE)) {
if (rtc_addr&RTC_ADDR_CLOCK) {
rtc_val = rtc_get_clock(rtc_addr);
} else {
rtc_val = rtc.ram[rtc_addr&RTC_ADDR_MASK];
}
Log_Printf(LOG_RTC_LEVEL,"[RTC] reading val $%02X from addr $%02X at PC=$%08x\n",
rtc_val,rtc_addr,m68k_getpc());
}
}
if (rtc_addr&RTC_ADDR_WRITE) {
rtc_val = (rtc_val<<1)|(rtdatabit?1:0);
} else {
rtdatabit = (rtc_val&(1<<(16-phase)))?1:0;
}
if (phase==16) {
if (rtc_addr&RTC_ADDR_WRITE) {
Log_Printf(LOG_RTC_LEVEL,"[RTC] writing val $%02X to addr $%02X at PC=$%08x\n",
rtc_val,rtc_addr,m68k_getpc());
if (rtc_addr&RTC_ADDR_CLOCK) {
rtc_put_clock(rtc_addr, rtc_val);
} else {
rtc.ram[rtc_addr&RTC_ADDR_MASK] = rtc_val;
}
}
switch (rtc_addr) {
case 0x1F:
case 0x9F: rtc_addr = 0x00; break;
case 0x32:
case 0xB2: rtc_addr = 0x20; break;
default: rtc_addr++; break;
}
phase-=8;
}
}
/* RTC returns 0 or 1 */
return rtdatabit;
}
static Uint8 toBCD(int val) {
return (((val/10)%10)<<4)|(val%10);
}
/* Year is supported up to 2050 through overflow of decimal decade */
static Uint8 toBCDyr(int val) {
return (((val/10)&0xF)<<4)|(val%10);
}
static int fromBCD(Uint8 bcd) {
return ((bcd&0xF0)>>4)*10+(bcd&0xF);
}
static void my_get_rtc_time(void) {
time_t tmp = host_unix_time();
struct tm t =*gmtime(&tmp);
rtc.time.sec = toBCD(t.tm_sec);
rtc.time.min = toBCD(t.tm_min);
rtc.time.hour = toBCD(t.tm_hour);
rtc.time.wday = toBCD(t.tm_wday+1);
rtc.time.mday = toBCD(t.tm_mday);
rtc.time.month = toBCD(t.tm_mon+1);
rtc.time.year = toBCDyr(t.tm_year);
}
static void my_set_rtc_time(int which,int val) {
static struct tm t;
t.tm_sec = fromBCD(rtc.time.sec);
t.tm_min = fromBCD(rtc.time.min);
t.tm_hour = fromBCD(rtc.time.hour);
t.tm_wday = fromBCD(rtc.time.wday) - 1;
t.tm_mday = fromBCD(rtc.time.mday);
t.tm_mon = fromBCD(rtc.time.month) - 1;
t.tm_year = fromBCD(rtc.time.year);
val = fromBCD(val);
switch (which) {
case 0:
t.tm_sec=val;
break;
case 1:
t.tm_min=val;
break;
case 2:
t.tm_hour=val;
break;
case 3:
t.tm_mday=val;
break;
case 4:
t.tm_mon=val-1;
break;
case 5:
t.tm_year=val;
break;
}
Log_Printf(LOG_WARN,"setting %d to %x",which,val);
host_set_unix_time(mktime(&t));
}
Uint8 rtc_get_clock(Uint8 addr) {
Uint8 val = 0x00;
my_get_rtc_time();
switch (rtc_addr&RTC_ADDR_MASK) {
case 0x20: /* seconds */
val = rtc.time.sec; break;
case 0x21: /* minutes */
val = rtc.time.min; break;
case 0x22: /* hours */
val = rtc.time.hour; break;
case 0x23: /* day of week (sunday = 1) */
val = rtc.time.wday; break;
case 0x24: /* day of month */
val = rtc.time.mday; break;
case 0x25: /* month */
val = rtc.time.month; break;
case 0x26: /* year (0 - 99) */
val = rtc.time.year ; break;
case 0x30: /* status register */
val = rtc.status;
rtc.status &= RTC_INT_SENSE;
break;
case 0x31: /* clock control register */
val = rtc.clkctrl; break;
case 0x32: /* interrupt control register */
val = rtc.intctrl; break;
default: break;
}
return val;
}
void my_set_rtc_time(int which,int val);
void rtc_put_clock(Uint8 addr, Uint8 val) {
switch (rtc_addr&RTC_ADDR_MASK) {
case 0x20: /* seconds */
my_set_rtc_time(0,val);
break;
case 0x21: /* minutes */
my_set_rtc_time(1,val);
break;
case 0x22: /* hours */
my_set_rtc_time(2,val);
break;
case 0x23: /* day of week (sunday = 1) */
break;
case 0x24: /* day of month */
my_set_rtc_time(3,val);
break;
case 0x25: /* month */
my_set_rtc_time(4,val);
break;
case 0x26: /* year (0 - 99) */
my_set_rtc_time(5,val);
break;
case 0x28: /* alarm: seconds */
case 0x29: /* alarm: minutes */
case 0x2A: /* alarm: hours */
Log_Printf(LOG_WARN,"Trying to program alarm (not implemented) %x",val);
break; /* not yet! */
case 0x31: /* clock control register */
rtc.clkctrl = val;
break;
case 0x32: /* interrupt control register */
rtc.intctrl = val;
if (rtc.intctrl&RTC_POWERDOWN) {
Log_Printf(LOG_WARN, "[RTC] Power down!");
M68000_Stop();
}
break;
default: break;
}
}
void oldrtc_request_power_down(void) {
set_interrupt(INT_POWER, SET_INT);
}
void oldrtc_stop_pdown_request(void) {
set_interrupt(INT_POWER, RELEASE_INT);
}
/* ------------------------- MCCS1850 ------------------------- */
/* RTC NVRAM (64 bytes) is located at address 0x00 to 0x1F
* and 0x40 to 0x5F, time registers, alarm registers and
* control/status registers are located at address 0x20 to 0x31.
*/
Uint8 newrtc_get_clock(Uint8 addr);
void newrtc_put_clock(Uint8 addr, Uint8 val);
/* New RTC has two 32 bit counters, one for time and one for alarm */
/* There are 3 status and control registers inside the chip */
/* RTC Status Register at 0x30 (r only)
*
* 1--- ---- always 1 (identifies MCCS1850 chip)
* -0-- ---- always 0
* --x- ---- test mode status
* ---x ---- first time up
* ---- x--- interrupt true (one of following interrupts is valid)
* ---- -x-- low battery interrupt
* ---- --x- alarm interrupt
* ---- ---x power down interrupt
*/
#define NRTC_NEWCHIP 0x80
#define NRTC_TMODE 0x20
#define NRTC_FIRSTUP 0x10
#define NRTC_INT 0x08
#define NRTC_INT_LBAT 0x04
#define NRTC_INT_ALARM 0x02
#define NRTC_INT_PDOWN 0x01
/* RTC Control Register at 0x31 (r) or 0xB1 (w)
*
* x--- ---- 1 = start, 0 = stop counter
* -x-- ---- initiate power down
* --x- ---- enable auto restart sequence
* ---x ---- enable alarm
* ---- x--- alarm clear (clear alarm int bit in status)
* ---- -x-- first time up clear (clear first up bit in status)
* ---- --x- enable low battery interrupting
* ---- ---x request power down clear (clear power down int bit in status)
*
* ---- xx-x always read as 0
*/
#define NRTC_CTRL_0 0x0D
#define NRTC_START 0x80
#define NRTC_STOP 0x00
#define NRTC_POWERDOWN 0x40
#define NRTC_AR 0x20
#define NRTC_ENABLEALRM 0x10
#define NRTC_CLRALARM 0x08
#define NRTC_CLRFTU 0x04
#define NRTC_LBE 0x02
#define NRTC_CLRPDOWN 0x01
struct {
/* --> see old chip * 0x00 - 0x1F (r), 0x80 - 0x9F (w) */
Uint32 timecntr; /* 0x20 - 0x23 (r), 0xA0 - 0xA3 (w) */
Uint32 alarmcntr; /* 0x24 - 0x27 (r), 0xA4 - 0xA7 (w) */
Uint8 status; /* 0x30 (r) */
Uint8 control; /* 0x31 (r), 0xB1 (w) */
Uint8 ram2[32]; /* 0x40 - 0x5F (r), 0xC0 - 0xDF (w) */
} newrtc;
#define RTC_ADDR_NEWRAM 0x40
int newrtc_interface_io(Uint8 rtdatabit) {
phase++;
if (phase<=8) {
rtc_addr = (rtc_addr<<1)|(rtdatabit?1:0);
} else {
if (phase==9) {
if (!(rtc_addr&RTC_ADDR_WRITE)) {
if (rtc_addr&RTC_ADDR_CLOCK) {
rtc_val = newrtc_get_clock(rtc_addr);
} else {
if (rtc_addr&RTC_ADDR_NEWRAM) {
rtc_val = newrtc.ram2[rtc_addr&0x1F];
} else {
rtc_val = rtc.ram[rtc_addr&0x1F];
}
}
Log_Printf(LOG_RTC_LEVEL,"[newRTC] reading val $%02X from addr $%02X at PC=$%08x\n",
rtc_val,rtc_addr,m68k_getpc());
}
}
if (rtc_addr&RTC_ADDR_WRITE) {
rtc_val = (rtc_val<<1)|(rtdatabit?1:0);
} else {
rtdatabit = (rtc_val&(1<<(16-phase)))?1:0;
}
if (phase==16) {
if (rtc_addr&RTC_ADDR_WRITE) {
Log_Printf(LOG_RTC_LEVEL,"[newRTC] writing val $%02X to addr $%02X at PC=$%08x\n",
rtc_val,rtc_addr,m68k_getpc());
if (rtc_addr&RTC_ADDR_CLOCK) {
newrtc_put_clock(rtc_addr, rtc_val);
} else {
if (rtc_addr&RTC_ADDR_NEWRAM) {
newrtc.ram2[rtc_addr&0x1F] = rtc_val;
} else {
rtc.ram[rtc_addr&0x1F] = rtc_val;
}
}
}
switch (rtc_addr) {
case 0x7F: rtc_addr = 0x00; break;
case 0xFF: rtc_addr = 0x80; break;
default: rtc_addr++; break;
}
phase-=8;
}
}
/* RTC returns 0 or 1 */
return rtdatabit;
}
Uint8 newrtc_get_clock(Uint8 addr) {
Uint8 val = 0x00;
newrtc.timecntr = host_unix_time();
switch (rtc_addr&RTC_ADDR_MASK) {
case 0x20:
val = (newrtc.timecntr>>24);
break;
case 0x21:
val = (newrtc.timecntr>>16);
break;
case 0x22:
val = (newrtc.timecntr>>8);
break;
case 0x23:
val = newrtc.timecntr;
break;
case 0x24:
val = (newrtc.alarmcntr>>24);
break;
case 0x25:
val = (newrtc.alarmcntr>>16);
break;
case 0x26:
val = (newrtc.alarmcntr>>8);
break;
case 0x27:
val = newrtc.alarmcntr;
break;
case 0x30: /* status register */
val = newrtc.status|NRTC_NEWCHIP;
break;
case 0x31: /* control register */
val = newrtc.control&~NRTC_CTRL_0;
break;
default:
break;
}
return val;
}
void newrtc_put_clock(Uint8 addr, Uint8 val) {
switch (rtc_addr&RTC_ADDR_MASK) {
case 0x20:
newrtc.timecntr &= 0x00FFFFFF;
newrtc.timecntr |= val << 24;
host_set_unix_time(newrtc.timecntr);
break;
case 0x21:
newrtc.timecntr &= 0xFF00FFFF;
newrtc.timecntr |= val << 16;
host_set_unix_time(newrtc.timecntr);
break;
case 0x22:
newrtc.timecntr &= 0xFFFF00FF;
newrtc.timecntr |= val << 8;
host_set_unix_time(newrtc.timecntr);
break;
case 0x23:
newrtc.timecntr &= 0xFFFFFF00;
newrtc.timecntr |= val;
host_set_unix_time(newrtc.timecntr);
break;
case 0x24:
newrtc.alarmcntr &= 0x00FFFFFF;
newrtc.alarmcntr |= val << 24;
break;
case 0x25:
newrtc.alarmcntr &= 0xFF00FFFF;
newrtc.alarmcntr |= val << 16;
break;
case 0x26:
newrtc.alarmcntr &= 0xFFFF00FF;
newrtc.alarmcntr |= val << 8;
break;
case 0x27:
newrtc.alarmcntr &= 0xFFFFFF00;
newrtc.alarmcntr |= val;
break;
case 0x31: /* control register */
newrtc.control = val;
if (newrtc.control&NRTC_CLRFTU) {
newrtc.status&= ~NRTC_FIRSTUP;
}
if (newrtc.control&NRTC_CLRALARM) {
newrtc.status&= ~NRTC_INT_ALARM;
}
if (newrtc.control&NRTC_CLRPDOWN) {
newrtc.status&= ~NRTC_INT_PDOWN;
}
if (newrtc.control&NRTC_POWERDOWN) {
Log_Printf(LOG_WARN, "[newRTC] Power down!");
M68000_Stop();
}
break;
default: break;
}
}
void newrtc_request_power_down(void) {
newrtc.status |= (NRTC_INT|NRTC_INT_PDOWN);
set_interrupt(INT_POWER, SET_INT);
}
void newrtc_stop_pdown_request(void) {
set_interrupt(INT_POWER, RELEASE_INT);
}
/* ---------------------- RTC NVRAM ---------------------- */
// file mon/nvram.h
// struct nvram_info {
// #define NI_RESET 9
// u_int ni_reset : 4,
// #define SCC_ALT_CONS 0x08000000
// ni_alt_cons : 1,
// #define ALLOW_EJECT 0x04000000
// ni_allow_eject : 1,
// ni_vol_r : 6,
// ni_brightness : 6,
// #define HW_PWD 0x6
// ni_hw_pwd : 4,
// ni_vol_l : 6,
// ni_spkren : 1,
// ni_lowpass : 1,
// #define BOOT_ANY 0x00000002
// ni_boot_any : 1,
// #define ANY_CMD 0x00000001
// ni_any_cmd : 1;
// #define NVRAM_HW_PASSWD 6
// u_char ni_ep[NVRAM_HW_PASSWD];
// #define ni_enetaddr ni_ep
// #define ni_hw_passwd ni_ep
// u_short ni_simm; /* 4 SIMMs, 4 bits per SIMM */
// char ni_adobe[2];
// u_char ni_pot[3];
// u_char ni_new_clock_chip : 1,
// ni_auto_poweron : 1,
// ni_use_console_slot : 1, /* Console slot was set by user. */
// ni_console_slot : 2, /* Preferred console dev slot>>1 */
// ni_use_parity_mem : 1, /* Use parity RAM if available? */
// : 2;
// #define NVRAM_BOOTCMD 12
// char ni_bootcmd[NVRAM_BOOTCMD];
// u_short ni_cksum;
// };
// #define N_brightness 0
// #define N_volume_l 1
// #define N_volume_r 2
/* nominal values during self test */
// #define BRIGHT_NOM 20
// #define VOL_NOM 0
/* bits in ni_pot[0] */
#define POT_ON 0x01
#define EXTENDED_POT 0x02
#define LOOP_POT 0x04
#define VERBOSE_POT 0x08
#define TEST_DRAM_POT 0x10
#define BOOT_POT 0x20
#define TEST_MONITOR_POT 0x40
/* bits in byte 17 */
#define NEW_CLOCK_CHIP 0x80
#define AUTO_POWERON 0x40
#define USE_CONSOLE_SLOT 0x20
#define CONSOLE_SLOT 0x18
#define USE_PARITY_MEM 0x04
/* bits in ni_simm (rtc ram byte 10 and 11) *
* -------- -----xxx bit 0 - 2: 1st simm: bit 1+2 define size, bit 3 defines page mode
* -------- --xxx--- bit 3 - 5: 2nd simm: bit 1+2 define size, bit 3 defines page mode
* -------x xx------ bit 6 - 8: 3rd simm: bit 1+2 define size, bit 3 defines page mode
* ----xxx- -------- bit 9 -11: 4th simm: bit 1+2 define size, bit 3 defines page mode
* xxxx---- -------- bit 12-15: defines parity, 1 bit for each simm (ignored on 68030)
*/
/* for 68030 and monochrome non-turbo systems */
#define SIMM_EMPTY 0x0
#define SIMM_16MB 0x1 /* Group of four 4 Mbyte SIMMs */
#define SIMM_4MB 0x2 /* Group of four 1 Mbyte SIMMs */
#define SIMM_1MB 0x3 /* Group of four 256 KByte SIMMs */
#define SIMM_PAGE_MODE 0x4 /* SIMM type is page mode, else nibble mode */
/* for all 68040 systems */
#define SIMM_PARITY 0x8 /* SIMMs support parity */
/* for non-turbo color systems */
#define SIMM_8MB_C 0x1 /* Pair of 4 Mbyte SIMMs */
#define SIMM_2MB_C 0x2 /* Pair of 1 Mbyte SIMMs */
#define SIMM_EMPTY2 0x3 /* reserved */
/* for turbo systems */
#define SIMM_32MB_T 0x1 /* Pair of 16 or 32 MByte SIMMs (front or back) */
#define SIMM_8MB_T 0x2 /* Pair of 4 or 8 MByte SIMMs (front or back) */
#define SIMM_2MB_T 0x3 /* Pair of 1 or 2 MByte SIMMs (front or back) */
/* bits in ni_reset (rtc ram byte 0 to 3) *
* -------- -------- -------- -------x bit 0: any cmd
* -------- -------- -------- ------x- bit 1: boot any
* -------- -------- -------- -----x-- bit 2: enable lowpass filter
* -------- -------- -------- ----x--- bit 3: disable speaker
* -------- -------- ------xx xxxx---- bit 4-9: volume left (max 0, min 0x2B)
* -------- -------- --xxxx-- -------- bit 10-13: hardware password
* -------- ----xxxx xx------ -------- bit 14-19: brightness (max 0x3D, min 0)
* ------xx xxxx---- -------- -------- bit 20-25: volume right (max 0, min 0x2B)
* -----x-- -------- -------- -------- bit 26: allow eject
* ----x--- -------- -------- -------- bit 27: alt cons
* xxxx---- -------- -------- -------- bit 28-31: reset
*/
/* RTC RAM */
Uint8 nvram_default[32]={
0x94,0x0f,0x40,0x00, // byte 0 - 3: volume, brightness, ...
0x00,0x00,0x00,0x00,0x00,0x00, // byte 4 - 9: hardware password, ethernet address (?)
0x00,0x00, // byte 10, 11: simm type and size (4 simms, 4 bits per simm), see bits in ni_simm above
0x00,0x00, // byte 12, 13: adobe (?)
0x4b,0x00,0x00, // byte 14: POT, byte 15: oldest ..., byte 16: most recent selftest error code
0x00, // byte 17: bit7:clock chip; 6:auto poweron; 5:enable console slot; 3,4:console slot; 2:parity mem
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // byte 18 - 29: boot command
0x0F,0x13 // byte 30, 31: checksum
};
void nvram_init(void) {
/* Reset RTC RAM */
memset(rtc.ram, 0, 32);
/* Build configuration bytes */
Uint32 config = 0x94000000; /* reset = 9, allow eject = 1 */
config |= 0x3D<<14; /* brightness */
rtc.ram[0] = config>>24;
rtc.ram[1] = config>>16;
rtc.ram[2] = config>>8;
rtc.ram[3] = config;
/* Build boot command */
switch (ConfigureParams.Boot.nBootDevice) {
case BOOT_ROM:
rtc.ram[18] = 0x00;
rtc.ram[19] = 0x00;
break;
case BOOT_SCSI:
rtc.ram[18] = 's';
rtc.ram[19] = 'd';
break;
case BOOT_ETHERNET:
rtc.ram[18] = 'e';
rtc.ram[19] = 'n';
break;
case BOOT_MO:
rtc.ram[18] = 'o';
rtc.ram[19] = 'd';
break;
case BOOT_FLOPPY:
rtc.ram[18] = 'f';
rtc.ram[19] = 'd';
break;
default: break;
}
/* Copy ethernet address from ROM to RTC RAM */
int i;
for (i = 0; i<6; i++) {
rtc.ram[i+4]=NEXTRom[i+8];
}
/* Build SIMM bytes */
Uint16 SIMMconfig = 0x0000;
Uint8 simm[4];
Uint8 parity = 0xF0;
if (ConfigureParams.System.bTurbo) {
parity = 0x00;
for (i = 0; i<4; i++) {
switch (ConfigureParams.Memory.nMemoryBankSize[i]) {
case 0: simm[i] = SIMM_EMPTY; break;
case 2: simm[i] = SIMM_2MB_T; break;
case 8: simm[i] = SIMM_8MB_T; break;
case 32: simm[i] = SIMM_32MB_T; break;
default: simm[i] = SIMM_EMPTY; break;
}
}
} else if (ConfigureParams.System.bColor) {
for (i = 0; i<4; i++) {
switch (ConfigureParams.Memory.nMemoryBankSize[i]) {
case 0: simm[i] = SIMM_EMPTY; parity &= ~(0x10<<i); break;
case 2: simm[i] = SIMM_2MB_C; break;
case 8: simm[i] = SIMM_8MB_C; break;
default: simm[i] = SIMM_EMPTY; break;
}
}
} else {
for (i = 0; i<4; i++) {
switch (ConfigureParams.Memory.nMemoryBankSize[i]) {
case 0: simm[i] = SIMM_EMPTY; parity &= ~(0x10<<i); break;
case 1: simm[i] = SIMM_1MB | SIMM_PAGE_MODE; break;
case 4: simm[i] = SIMM_4MB | SIMM_PAGE_MODE; break;
case 16: simm[i] = SIMM_16MB | SIMM_PAGE_MODE; break;
default: simm[i] = SIMM_EMPTY | SIMM_PAGE_MODE; break;
}
}
}
SIMMconfig = ((parity&0xF0)<<8) | (simm[3]<<9) | (simm[2]<<6) | (simm[1]<<3) | simm[0];
rtc.ram[10] = (SIMMconfig>>8)&0xFF;
rtc.ram[11] = SIMMconfig&0xFF;
/* Build POT byte[0] */
rtc.ram[14] = 0x00;
if (ConfigureParams.Boot.bEnableDRAMTest)
rtc.ram[14] |= TEST_DRAM_POT;
if (ConfigureParams.Boot.bEnablePot)
rtc.ram[14] |= POT_ON;
if (ConfigureParams.Boot.bEnableSoundTest)
rtc.ram[14] |= TEST_MONITOR_POT;
if (ConfigureParams.Boot.bEnableSCSITest)
rtc.ram[14] |= EXTENDED_POT;
if (ConfigureParams.Boot.bLoopPot)
rtc.ram[14] |= LOOP_POT;
if (ConfigureParams.Boot.bVerbose)
rtc.ram[14] |= VERBOSE_POT;
if (ConfigureParams.Boot.bExtendedPot)
rtc.ram[14] |= BOOT_POT;
/* Set clock chip bit */
switch (ConfigureParams.System.nRTC) {
case MCCS1850: rtc.ram[17] |= NEW_CLOCK_CHIP; break;
case MC68HC68T1: rtc.ram[17] &= ~NEW_CLOCK_CHIP; break;
default: break;
}
/* Set prefered console slot */
rtc.ram[17] |= USE_CONSOLE_SLOT;
for (i = 0; i < ND_MAX_BOARDS; i++) {
if (ConfigureParams.Dimension.bMainDisplay &&
(ConfigureParams.Dimension.nMainDisplay == i) &&
ConfigureParams.Dimension.board[i].bEnabled) {
rtc.ram[17] |= (ND_SLOT(i)>>1)<<3;
break;
}
}
/* Re-calculate checksum */
nvram_checksum(1);
}
void nvram_checksum(int force) {
int sum,i;
sum=0;
for (i=0;i<30;i+=2) {
sum+=(rtc.ram[i]<<8)|(rtc.ram[i+1]);
if (sum>=0x10000) {
sum-=0x10000;
sum+=1;
}
}
sum=0xFFFF-sum;
if (force) {
rtc.ram[30]=(sum&0xFF00)>>8;
rtc.ram[31]=(sum&0xFF);
Log_Printf(LOG_WARN,"Forcing RTC checksum to %x %x",rtc.ram[30],rtc.ram[31]);
} else {
Log_Printf(LOG_WARN,"Check RTC checksum to %x %x %x %x",
rtc.ram[30],(sum&0xFF00)>>8,
rtc.ram[31],(sum&0xFF));
}
}
#if 1
static char rtc_ram_info[1024];
char * get_rtc_ram_info(void) {
char buf[256];
int sum;
int i;
int ni_vol_l,ni_vol_r,ni_brightness;
int ni_hw_pwd;
sprintf(buf,"Rtc info:\n");
strcpy(rtc_ram_info,buf);
// struct nvram_info {
// #define NI_RESET 9
// u_int ni_reset : 4,
sprintf(buf,"RTC RESET:x%1X ",rtc.ram[0]>>4);
strcat(rtc_ram_info,buf);
// #define SCC_ALT_CONS 0x08000000
// ni_alt_cons : 1,
if (rtc.ram[0]&0x08) strcat(rtc_ram_info,"ALT_CONS ");
// #define ALLOW_EJECT 0x04000000
// ni_allow_eject : 1,
if (rtc.ram[0]&0x04) strcat(rtc_ram_info,"ALLOW_EJECT ");
// ni_vol_r : 6,
// ni_brightness : 6,
// #define HW_PWD 0x6
// ni_hw_pwd : 4,
// ni_vol_l : 6,
// ni_spkren : 1,
// ni_lowpass : 1,
// #define BOOT_ANY 0x00000002
// ni_boot_any : 1,
// #define ANY_CMD 0x00000001
// ni_any_cmd : 1;
ni_vol_r=(((rtc.ram[0]&0x3)<<4)|((rtc.ram[1]&0xF0)>>4));
ni_brightness=(((rtc.ram[1]&0xF)<<2)|((rtc.ram[2]&0xC0)>>6));
ni_vol_l=((rtc.ram[2]&0x3F)<<2);
ni_hw_pwd=(rtc.ram[3]&0xF0)>>4;
sprintf(buf,"VOL_R:x%1X BRIGHT:x%1X HWPWD:x%1X VOL_L:x%1X",ni_vol_r,ni_brightness,ni_vol_l,ni_hw_pwd);
strcat(rtc_ram_info,buf);
if (rtc.ram[3]&0x08) strcat(rtc_ram_info,"SPK_ENABLE ");
if (rtc.ram[3]&0x04) strcat(rtc_ram_info,"LOW_PASS ");
if (rtc.ram[3]&0x02) strcat(rtc_ram_info,"BOOT_ANY ");
if (rtc.ram[3]&0x01) strcat(rtc_ram_info,"ANY_CMD ");
// #define NVRAM_HW_PASSWD 6
// u_char ni_ep[NVRAM_HW_PASSWD];
sprintf(buf,"NVRAM_HW_PASSWD:%2X %2X %2X %2X %2X %2X ",rtc.ram[4],rtc.ram[5],rtc.ram[6],rtc.ram[7],rtc.ram[8],rtc.ram[9]);
strcat(rtc_ram_info,buf);
// #define ni_enetaddr ni_ep
// #define ni_hw_passwd ni_ep
// u_short ni_simm; /* 4 SIMMs, 4 bits per SIMM */
sprintf(buf,"SIMM:%1X %1X %1X %1X ",rtc.ram[10]>>4,rtc.ram[10]&0x0F,rtc.ram[11]>>4,rtc.ram[11]&0x0F);
strcat(rtc_ram_info,buf);
// char ni_adobe[2];
sprintf(buf,"ADOBE:%2X %2X ",rtc.ram[12],rtc.ram[13]);
strcat(rtc_ram_info,buf);
// u_char ni_pot[3];
sprintf(buf,"POT:%2X %2X %2X ",rtc.ram[14],rtc.ram[15],rtc.ram[16]);
strcat(rtc_ram_info,buf);
// u_char ni_new_clock_chip : 1,
// ni_auto_poweron : 1,
// ni_use_console_slot : 1, /* Console slot was set by user. */
// ni_console_slot : 2, /* Preferred console dev slot>>1 */
// ni_use_parity_mem : 1, /* Use parity RAM if available? */
// : 2;
if (rtc.ram[17]&0x80) strcat(rtc_ram_info,"NEW_CLOCK_CHIP ");
if (rtc.ram[17]&0x40) strcat(rtc_ram_info,"AUTO_POWERON ");
if (rtc.ram[17]&0x20) strcat(rtc_ram_info,"CONSOLE_SLOT ");
sprintf(buf,"console_slot:%X ",(rtc.ram[17]&0x18)>>3);
strcat(rtc_ram_info,buf);
if (rtc.ram[17]&0x04) strcat(rtc_ram_info,"USE_PARITY ");
strcat(rtc_ram_info,"boot_command:");
for (i=0;i<12;i++) {
if ((rtc.ram[18+i]>=0x20) && (rtc.ram[18+i]<=0x7F)) {
sprintf(buf,"%c",rtc.ram[18+i]);
strcat(rtc_ram_info,buf);
}
}
strcat(rtc_ram_info," ");
sprintf(buf,"CKSUM:%2X %2X ",rtc.ram[30],rtc.ram[31]);
strcat(rtc_ram_info,buf);
sum=0;
for (i=0;i<30;i+=2) {
sum+=(rtc.ram[i]<<8)|(rtc.ram[i+1]);
if (sum>=0x10000) { sum-=0x10000;
sum+=1;
}
}
sum=0xFFFF-sum;
sprintf(buf,"CALC_CKSUM:%04X ",sum&0xFFFF);
strcat(rtc_ram_info,buf);
// #define NVRAM_BOOTCMD 12
// char ni_bootcmd[NVRAM_BOOTCMD];
// u_short ni_cksum;
// };
return rtc_ram_info;
}
#endif