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

1368 lines
34 KiB
C

#ident "$Revision: 1.115 $"
#ifdef IP30 /* whole file */
#include <sys/types.h>
#include <sys/debug.h>
#include <sys/sbd.h>
#include <sys/cpu.h>
#include <sys/RACER/racermp.h>
#include <sys/immu.h>
#include <sys/ds1687clk.h>
#include <sys/ecc.h>
#include <libsc.h>
#include <libsk.h>
#include <sys/types.h>
#include <sys/param.h>
#include <arcs/folder.h>
#include <arcs/errno.h>
#include <arcs/hinv.h>
#include <arcs/io.h>
#include <arcs/cfgtree.h>
#include <arcs/time.h>
#include <sys/xtalk/hq4.h>
#include <sys/nic.h>
#include <sys/tpureg.h>
#include "sys/odsyhw.h"
#include <standcfg.h> /* for RAD stub */
#define data_eccsyns real_data_eccsyns
extern void ns16550cons_errputc(uchar_t);
#ifdef US_DELAY_DEBUG
static void check_us_delay(void);
#endif
/* exports */
void cpu_install(COMPONENT *);
__uint64_t cpu_get_memsize(void);
void cpu_acpanic(char *str);
void ecc_error_decode(u_int);
int processor_type;
int master_cpuid;
/* private */
static __uint32_t cpu_get_membase(void);
static void init_board_rev(void);
extern int _prom;
int ide_ecc_test = 0;
static unsigned char to_numcpus[] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
};
/* probe only active CPUs -- used by symmon also */
int
cpu_probe(void)
{
heartreg_t active_msk;
heartreg_t disabled;
active_msk = (HEART_PIU_K1PTR->h_status & HEART_STAT_PROC_ACTIVE_MSK) >>
HEART_STAT_PROC_ACTIVE_SHFT;
disabled = (HEART_PIU_K1PTR->h_mode & HM_PROC_DISABLE_MSK) >>
HM_PROC_DISABLE_SHFT;
return (int)to_numcpus[active_msk & ~disabled];
}
/* probe all (even disabled) CPUs */
int
cpu_probe_all(void)
{
heartreg_t active_msk;
active_msk = (HEART_PIU_K1PTR->h_status & HEART_STAT_PROC_ACTIVE_MSK) >>
HEART_STAT_PROC_ACTIVE_SHFT;
return (int)to_numcpus[active_msk];
}
int
cpu_is_active(int id)
{
heartreg_t active_msk;
heartreg_t disabled;
active_msk = (HEART_PIU_K1PTR->h_status & HEART_STAT_PROC_ACTIVE_MSK) >>
HEART_STAT_PROC_ACTIVE_SHFT;
disabled = (HEART_PIU_K1PTR->h_mode & HM_PROC_DISABLE_MSK) >>
HM_PROC_DISABLE_SHFT;
return active_msk & ~disabled & (0x1L << id);
}
/* setup and enable power related interrupts */
void
ip30_setup_power(void)
{
bridge_t *bridge = BRIDGE_K1PTR;
heart_piu_t *heart = HEART_PIU_K1PTR;
/* Initialize soft power interrupt in bridge and heart */
set_bridge_vector((void *)bridge, IP30_BVEC_POWER, IP30_HVEC_POWER);
heart->h_imr[master_cpuid] |= 1ull << IP30_HVEC_POWER;
/* Initialize AC interrupt in bridge for polling in stand */
if (heart_rev() > HEART_REV_A)
set_bridge_vector((void *)bridge, IP30_BVEC_ACFAIL,
IP30_HVEC_ACFAIL);
flushbus();
/* Enable interrupt at the CPU for those with SR_IE set.
*/
set_SR(get_SR() | SR_POWER);
}
/* unset and disable power button interrupt */
void
ip30_disable_power(void)
{
bridge_t *bridge = BRIDGE_K1PTR;
heart_piu_t *heart = HEART_PIU_K1PTR;
/* Disable interrupt at the CPU for those with SR_IE set.
*/
set_SR(get_SR() & ~SR_POWER);
/* unset and disable bridge interrupts
*/
disable_bridge_intr((void *)bridge, IP30_BVEC_POWER);
heart->h_imr[master_cpuid] &= ~(1ull << IP30_HVEC_POWER);
flushbus();
}
void
init_ip30_chips(void)
{
extern int flash_init(void);
/* do this early since chips initialization may need this info */
init_board_rev();
init_heart(HEART_PIU_K1PTR, HEART_CFG_K1PTR);
ip30_setup_power();
/* Make sure IOC3 outputs are enabled for IP30 LEDs, as the kernel
* has been known to reset it occasionally. Then make the LED
* green.
*/
((ioc3_mem_t *)IOC3_PCI_DEVIO_K1PTR)->gpcr_s =
GPCR_LED0_ON|GPCR_LED1_ON;
ip30_set_cpuled(LED_GREEN);
flash_init();
}
/* perform any board specific start up initialization */
void
cpu_reset(void)
{
extern int cachewrback;
extern int scsicnt;
u_char fan_setting;
cachewrback = 1;
processor_type = r4k_getprid() & ~0x0f;
/* i am executing this code therefore i must be the master processor */
master_cpuid = cpuid();
/* initialize the spinlock subsystem */
initsplocks();
/*
* If we're coming from the PROM,
* we've already done the early init.
*/
if (!_prom)
init_ip30_chips();
/* Go to nominal voltage */
if (MPCONF->fanloads <= 1)
fan_setting = FAN_SPEED_LO;
else if (MPCONF->fanloads >= 1000)
fan_setting = FAN_SPEED_HI;
else
fan_setting = 0x0;
*IP30_VOLTAGE_CTRL = fan_setting |
PWR_SUPPLY_MARGIN_LO_NORMAL | PWR_SUPPLY_MARGIN_HI_NORMAL;
scsicnt = 2; /* two ql chips in base system */
#ifdef US_DELAY_DEBUG
check_us_delay();
#endif
}
/* initialize keyboard bell */
void
kbdbell_init(void) /* XXX BUG 421254 */
{
}
/*ARGSUSED*/
void
ip30_set_cpuled(int color)
{
ioc3_mem_t *ioc3 = (ioc3_mem_t *)IOC3_PCI_DEVIO_K1PTR;
switch (color) {
case LED_OFF:
ioc3->gppr_0 = 0;
ioc3->gppr_1 = 0;
break;
case LED_GREEN:
ioc3->gppr_1 = 0;
ioc3->gppr_0 = 1;
break;
case LED_AMBER:
default:
ioc3->gppr_0 = 0;
ioc3->gppr_1 = 1;
break;
}
flushbus();
}
/* get memory configuration word for a bank */
/* NOTE: while heart has registers for 8 banks,
* this code knows that IP30 only has 4.
*/
static __uint32_t
get_mem_conf(int bank)
{
ASSERT(bank >=0 && bank <= 3);
return HEART_PIU_K1PTR->h_memcfg.bank[bank];
}
/* determine the size of a memory bank */
static __uint64_t
banksz(int bank)
{
__uint32_t memconfig = get_mem_conf(bank);
__uint32_t memcfg_ram;
if (!(memconfig & HEART_MEMCFG_VLD))
return 0;
memcfg_ram = (memconfig & HEART_MEMCFG_RAM_MSK) >> HEART_MEMCFG_RAM_SHFT;
return (memcfg_ram + 1) << HEART_MEMCFG_UNIT_SHFT;
}
#define INVALID_DIMM_BANK -1
/* find bank in which the given addr belongs to */
int
ip30_addr_to_bank(__uint64_t addr) /* phys addr is only 40 bits */
{
__uint64_t base, size;
__uint32_t memconfig;
int i;
if (addr >= ABS_SYSTEM_MEMORY_BASE)
addr -= ABS_SYSTEM_MEMORY_BASE;
for (i = 0; i < 4; i++) {
if (!(size = banksz(i)))
continue;
memconfig = get_mem_conf(i);
base = (memconfig & HEART_MEMCFG_ADDR_MSK) <<
HEART_MEMCFG_UNIT_SHFT;
if (addr >= base && addr < (base + size))
return i;
}
return INVALID_DIMM_BANK;
}
char *
cpu_memerr_to_dimm(heartreg_t memerr)
{
static char buf[16]; /* DIMM Sx[, (D|C)Bxx] */
paddr_t paddr = memerr & HME_PHYS_ADDR;
int syndrome = (memerr & HME_SYNDROME) >> HME_SYNDROME_SHFT;
sprintf(buf, "DIMM S%d",
(ip30_addr_to_bank(paddr) * 2) + 1 + ((paddr & 0x8) != 0));
if (data_eccsyns[syndrome].type == DB ||
data_eccsyns[syndrome].type == CB)
sprintf(&buf[7], ", %s%d",
etstrings[data_eccsyns[syndrome].type],
data_eccsyns[syndrome].value);
return buf;
}
int ip30_rev;
int
ip30_board_rev(void)
{
return ip30_rev;
}
/* Read the board revision info from NIC, and store formatted version # in
* memory.
*/
static void
init_board_rev(void)
{
nic_data_t mcr = (nic_data_t)&BRIDGE_K1PTR->b_nic;
int dash = 1, rev = 0;
char buf[256];
char *p;
cfg_get_nic_info(mcr, buf);
if (p = strstr(buf, "Part:")) {
p += 5;
if (p = strchr(p,'-')) {
p++;
if (p = strchr(p, '-')) {
p++;
dash = atoi(p);
}
}
}
if (p = strstr(buf, "Revision:"))
rev = *(p + 9);
#ifdef HEART_COHERENCY_WAR
if (heart_rev() == HEART_REV_B) {
extern int fibbed_heart;
fibbed_heart = strstr(buf, "Group:fe") != NULL || rev >= 'J';
}
#endif /* HEART_COHERENCY_WAR */
ip30_rev = (dash << 16) | rev;
}
/* IP30 specific hardware inventory routine */
/* calculate log2(x) */
static int
log2(int x)
{
int n, v;
for(n = 0, v = 1; v < x; v <<= 1, n++)
;
return n;
}
#define SYSBOARDIDLEN 9
#define SYSBOARDID "SGI-IP30"
static COMPONENT IP30tmpl = {
SystemClass, /* Class */
ARC, /* Type */
0, /* Flags */
SGI_ARCS_VERS, /* Version */
SGI_ARCS_REV, /* Revision */
0, /* Key */
0, /* Affinity */
0, /* ConfigurationDataSize */
SYSBOARDIDLEN, /* IdentifierLength */
SYSBOARDID /* Identifier */
};
cfgnode_t *
cpu_makecfgroot(void)
{
COMPONENT *root;
root = AddChild(NULL, &IP30tmpl, (void *)NULL);
if (root == (COMPONENT *)NULL)
cpu_acpanic("root");
return (cfgnode_t *)root;
}
#ifndef IP30_RPROM
static COMPONENT cputmpl = {
ProcessorClass, /* Class */
CPU, /* Type */
0, /* Flags */
SGI_ARCS_VERS, /* Version */
SGI_ARCS_REV, /* Revision */
0, /* Key */
0x01, /* Affinity */
0, /* ConfigurationDataSize */
12, /* IdentifierLength */
"MIPS-R10000" /* Identifier */
};
static COMPONENT fputmpl = {
ProcessorClass, /* Class */
FPU, /* Type */
0, /* Flags */
SGI_ARCS_VERS, /* Version */
SGI_ARCS_REV, /* Revision */
0, /* Key */
0x01, /* Affinity */
0, /* ConfigurationDataSize */
/* Must be longer than max string below */
15, /* IdentifierLength */
"MIPS-R10010FPC" /* Identifier */
};
static COMPONENT cachetmpl = {
CacheClass, /* Class */
PrimaryICache, /* Type */
0, /* Flags */
SGI_ARCS_VERS, /* Version */
SGI_ARCS_REV, /* Revision */
0, /* Key */
0x01, /* Affinity */
0, /* ConfigurationDataSize */
0, /* IdentifierLength */
NULL /* Identifier */
};
static COMPONENT memtmpl = {
MemoryClass, /* Class */
Memory, /* Type */
0, /* Flags */
SGI_ARCS_VERS, /* Version */
SGI_ARCS_REV, /* Revision */
0, /* Key */
0x01, /* Affinity */
0, /* ConfigurationDataSize */
0, /* Identifier Length */
NULL /* Identifier */
};
/* NOTE: assumes on IP30 that all R10K CPUs in an MP config are the same
* clock rate (they have to be due to the cluster bus) and same
* cache size (which is true as they are on the same board).
*/
static void
inventory_cpu(COMPONENT *root, int n)
{
extern unsigned _scache_linesize;
extern unsigned _dcache_linesize;
extern unsigned _icache_linesize;
extern unsigned _sidcache_size;
extern unsigned _dcache_size;
extern unsigned _icache_size;
int trex = ((r4k_getprid() & C0_IMPMASK) >> C0_IMPSHIFT) >=
C0_IMP_R12000;
COMPONENT *c, *tmp;
union key_u key;
c = AddChild(root, &cputmpl, (void *)NULL);
if (c == (COMPONENT *)NULL)
cpu_acpanic("cpu");
c->Key = n;
if (trex)
c->Identifier[7] = '2'; /* TREX == R12000 */
tmp = AddChild(c, &fputmpl, (void *)NULL);
if (tmp == (COMPONENT *)NULL)
cpu_acpanic("fpu");
tmp->Key = n;
if (trex)
tmp->Identifier[7] = '2'; /* TREX == R12000 */
tmp = AddChild(c, &cachetmpl, (void *)NULL);
if (tmp == (COMPONENT *)NULL)
cpu_acpanic("icache");
key.cache.c_bsize = 2;
key.cache.c_lsize = log2(_icache_linesize);
key.cache.c_size = log2(_icache_size >> 12);
tmp->Key = key.FullKey;
tmp = AddChild(c, &cachetmpl, (void *)NULL);
if (tmp == (COMPONENT *)NULL)
cpu_acpanic("dcache");
key.cache.c_bsize = 2;
key.cache.c_lsize = log2(_dcache_linesize);
key.cache.c_size = log2(_dcache_size >> 12);
tmp->Type = PrimaryDCache;
tmp->Key = key.FullKey;
if (_sidcache_size != 0) {
tmp = AddChild(c, &cachetmpl, (void *)NULL);
if (tmp == (COMPONENT *)NULL)
cpu_acpanic("s_cache");
key.cache.c_bsize = 2;
key.cache.c_lsize = log2(_scache_linesize);
key.cache.c_size = log2(_sidcache_size >> 12);
tmp->Type = SecondaryCache;
tmp->Key = key.FullKey;
}
}
void
cpu_install(COMPONENT *root)
{
heart_piu_t *heart = HEART_PIU_K1PTR;
heartreg_t stat = heart->h_status;
COMPONENT *tmp;
int i, n;
inventory_cpu(root,0);
/* count all (even disabled) cpus in inventory */
n = cpu_probe_all();
for (i=1; i < n; i++)
inventory_cpu(root,i);
tmp = AddChild(root, &memtmpl, (void *)NULL);
if (tmp == (COMPONENT *)NULL)
cpu_acpanic("main memory");
tmp->Key = cpu_get_memsize() >> ARCS_BPPSHIFT; /* number of 4k pages */
}
#define CLEAR_STATUS 1
/*ARGSUSED*/
void
cpu_show_fault(unsigned long saved_cause)
{
heartreg_t heart_isr;
int wid, errvec;
heart_isr= print_heart_status(CLEAR_STATUS);
(void)print_xbow_status((__psunsigned_t)XBOW_K1PTR, CLEAR_STATUS);
(void)print_bridge_status(BRIDGE_K1PTR, CLEAR_STATUS);
(void)print_ioc3_status(PHYS_TO_COMPATK1(IOC3_PCI_CFG_BASE),
CLEAR_STATUS);
/*
* handle the expansion XIO slots (ports 9,10,11,12,13,14)
* we start with heart intr bit 51 (port 9) and work our way
* to bit 56 (port 14).
* we will print the bridge status if widget is bridge
* otherwise minimum info and the heart intr mask will be
* cleared to prevent further intrs from this device.
*/
errvec = WIDGET_ERRVEC_BASE;
for (wid = HEART_ID+1; wid < BRIDGE_ID; errvec++, wid++) {
widget_cfg_t *widget;
widgetreg_t id;
int part;
if ((heart_isr & (HEARTCONST 1 << errvec)) == 0)
continue;
widget = (widget_cfg_t *) K1_MAIN_WIDGET(wid);
id = widget->w_id;
part = (id & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT;
if (part == BRIDGE_WIDGET_PART_NUM)
(void)print_bridge_status((bridge_t *)widget,
CLEAR_STATUS);
else {
printf("Widget(id=0x%x) : partno 0x%x\n",
widget->w_control & BRIDGE_CTRL_WIDGET_ID_MASK,
part);
}
HEART_PIU_K1PTR->h_imr[master_cpuid] &= ~(HEARTCONST 1 << errvec);
}
/* Re-enable power interrupt */
ip30_setup_power();
set_SR(SR_PROMBASE | SR_IE | SR_IBIT_BERR | SR_POWER);
}
#endif /* !IP30_RPROM */
/* hit the system reset bit on the cpu */
void
cpu_hardreset(void)
{
/*
* Or in HM_SOFTWARE_RST in case there are bits in the register
* that are preserved across a reset.
*/
HEART_PIU_K1PTR->h_mode |= HM_SW_RST;
flushbus();
}
__uint64_t memsize;
__uint64_t
cpu_get_memsize(void)
{
int bank;
if (!memsize) {
for (bank = 0; bank < 4; bank++)
memsize += banksz(bank); /* sum up bank sizes */
}
return memsize;
}
static __uint32_t
cpu_get_membase(void)
{
return (unsigned int)PHYS_RAMBASE;
}
char *
cpu_get_disp_str(void)
{
return "video()";
}
char *
cpu_get_kbd_str(void)
{
return "keyboard()";
}
char *
cpu_get_serial(void)
{
return "serial(0)";
}
#ifndef IP30_RPROM
void
cpu_errputc(char c)
{
ns16550cons_errputc(c);
}
void
cpu_errputs(char *s)
{
while (*s)
ns16550cons_errputc(*s++);
}
#endif /* !IP30_PROM */
void
cpu_mem_init(void)
{
/* Add all of main memory with the exception of four pages that
* will be mapped only to physical 0 (in rev B and later hearts).
*/
#define ONE_GIGABYTE (0x1 << 30)
#define NPHYS0_PAGES 2
#define NALIAS_PAGES (arcs_btop(SYSTEM_MEMORY_ALIAS_SIZE) - NPHYS0_PAGES)
#define NLOW_PAGES (NPHYS0_PAGES+NALIAS_PAGES)
if (cpu_get_memsize() <= ONE_GIGABYTE)
md_add(cpu_get_membase() + arcs_ptob(NLOW_PAGES),
arcs_btop(cpu_get_memsize()) - NLOW_PAGES, FreeMemory);
else {
/*
* since the bridge's PCI 32-bit direct mapped mode supports
* only up to 1.5/2GB and PROM loads relocatable program,
* ie sash, at the top of memory, system with > 1.5/2GB
* will fail. we can change all PCI device drivers to do
* 64 bits DMA, or restrict DMA to portion of memory, we
* opt for the latter. one gigabyte is more than enough
* for all DMAs the PROM need to do
*/
md_add(cpu_get_membase() + arcs_ptob(NLOW_PAGES),
arcs_btop(ONE_GIGABYTE) - NLOW_PAGES, FreeMemory);
md_add(cpu_get_membase() + ONE_GIGABYTE,
arcs_btop(cpu_get_memsize() - ONE_GIGABYTE),
FirmwarePermanent);
}
md_add(0, NPHYS0_PAGES, FreeMemory);
/* 8K-16K is not availiable for general use as it is mapped to
* 0 by the heart.
*/
md_add(arcs_ptob(NPHYS0_PAGES), NALIAS_PAGES, FirmwarePermanent);
}
void
cpu_acpanic(char *str)
{
printf("Cannot malloc %s component of config tree\n", str);
panic("Incomplete config tree -- cannot continue.\n");
}
/* IP30 cannot save configuration to NVRAM */
LONG
cpu_savecfg(void)
{
return ENOSPC;
}
char *
cpu_get_mouse(void)
{
return "pointer()";
}
/* Note: On multi-bit cache errors, we cannot figure out which chip is bad
* on the R10000.
*/
/*ARGSUSED1*/
void
ecc_error_decode(u_int ecc_error_reg)
{
}
void
cpu_checkpower(void)
{
heart_piu_t *heart = HEART_PIU_K1PTR;
if (heart->h_imsr & (1ull << IP30_HVEC_POWER)) {
cpu_soft_powerdown();
/*NOTREACHED*/
}
}
void
cpu_scandevs(void)
{
cpu_checkpower();
}
#ifndef IP30_RPROM
/* called from the fault handlers */
int
ip30_checkintrs(__psunsigned_t epc, __psunsigned_t ra)
{
heartreg_t h_cause, heart_mode;
__uint64_t badaddr;
heart_piu_t *heart;
bridge_t *bridge;
cpu_checkpower();
bridge = BRIDGE_K1PTR;
/* Check for AC power failures */
if (bridge->b_int_status & (1<<BRIDGE_ACFAIL)) {
restore_ioc3_sio(slow_ioc3_sio()); /* may stick slow */
powerspin();
}
/* Check base bridge for illegal addresses to the prom range. This
* happens when the T5 hits the d$ speculation problem to cache prom
* addresses. Or, of course when we have have bad code that accesses
* the prom.
*/
if (bridge->b_int_status & BRIDGE_ISR_INVLD_ADDR) {
badaddr = ((__uint64_t)bridge->b_wid_err_upper << 32) |
bridge->b_wid_err_lower;
if (badaddr >= 0xc00000 && badaddr < 0xffffff) {
static __uint64_t last_badaddr;
static __psunsigned_t last_epc;
static __psunsigned_t last_ra;
bridge->b_int_rst_stat = BRIDGE_IRR_REQ_DSP_GRP;
bridge->b_wid_tflush;
if (epc != last_epc || ra != last_ra ||
badaddr != last_badaddr) {
printf("\n*** PROM write error on cacheline 0x1f%x at PC=0x%x RA=0x%x\n",
badaddr, epc, ra);
last_badaddr = badaddr;
last_epc = epc;
last_ra = ra;
}
return 1;
}
}
/* handle single-bit, correctable errors in the prom */
heart = HEART_PIU_K1PTR;
h_cause = heart->h_cause;
if ((h_cause & (HC_COR_MEM_ERR | HC_NCOR_MEM_ERR)) == HC_COR_MEM_ERR) {
extern void __dcache_wb_inval(void *, int);
paddr_t badaddr;
int error_type = 0;
heartreg_t h_mem_req_arb;
heartreg_t h_memerr_err;
heartreg_t h_memerr_data;
extern struct reg_desc heart_mem_err_desc[];
paddr_t k0_badaddr;
h_memerr_err = heart->h_memerr_addr;
h_memerr_data = heart->h_memerr_data;
badaddr = h_memerr_err & HME_PHYS_ADDR;
/* clear the error and unlatch error logging registers */
heart->h_cause = HC_COR_MEM_ERR |
(h_cause & HC_PE_SYS_COR_ERR_MSK);
flushbus();
/*
* on a DSPW write with single bit error, the MIU
* will correct the data before writing it back
* into memory. in this case, if we correct the
* data blindly, we will end up corrupting the memory.
* re-reading the bad location should tell us whether the
* bad bit is already fixed or not
*/
k0_badaddr = PHYS_TO_K0(badaddr & ~0x7);
/* do not serve io to memory request temporary */
h_mem_req_arb = heart->h_mem_req_arb;
heart->h_mem_req_arb = h_mem_req_arb | HEART_MEMARB_IODIS;
flushbus();
/*
* use cached address for MP coherency, dirty the cacheline
* then write it back
*/
*(volatile __uint64_t *)k0_badaddr += 0;
__dcache_wb_inval((void *)k0_badaddr, 8);
/* ok to serve io to memory request */
heart->h_mem_req_arb = h_mem_req_arb;
flushbus();
if ((h_cause = heart->h_cause) & HC_COR_MEM_ERR) {
/* clear the error again */
heart->h_cause = HC_COR_MEM_ERR |
(h_cause & HC_PE_SYS_COR_ERR_MSK);
flushbus();
error_type = 1; /* definitely hard error */
}
printf("*** Fixed correctable %smemory error!\n"
" MemData 0x%016X %s\n"
" MemErr %R\n",
error_type ? "hard " : "",
h_memerr_data, cpu_memerr_to_dimm(h_memerr_err),
h_memerr_err, heart_mem_err_desc);
return 1;
}
/* Other processor reported correctable errors */
if (h_cause & HC_PE_SYS_COR_ERR_MSK) {
heart->h_cause = h_cause & HC_PE_SYS_COR_ERR_MSK;
flushbus();
}
return 0;
}
#endif /* !IP30_RPROM */
void
bell(void)
{
/* XXX BUG 421254 need to implement kbd bell */
}
/* check if address is inside a "protected" area */
/*ARGSUSED*/
int
is_protected_adrs(unsigned long low, unsigned long high)
{
return 0; /* not protected */
}
/*
* void launch_slave()
* set up the MPCONF entry for the given cpu and launches
* it. note that the routine checks to see if the vpid
* is equal to the vpid of the processor this function is
* running on. if it is, it just calls the launch and
* rendezvous function.
*/
int
launch_slave(int virt_id, void (*lnch_fn)(void *), void *lnch_parm,
void (*rndvz_fn)(void *), void *rndvz_parm, void *stack)
{
volatile mpconf_t *mpconf_ptr;
/* make sure virt_id is valid */
if (virt_id >= MAXCPU || virt_id < 0) {
printf("launch_slave: error: virt_id (%d) is too big.\n",
virt_id);
return -1;
}
/* check to see if virtual ID is current processor */
if (virt_id == cpuid()) {
/* call launch and rendezvous parameters */
lnch_fn(lnch_parm);
if (rndvz_fn)
rndvz_fn(rndvz_parm);
}
else {
mpconf_ptr = (volatile mpconf_t *)
(MPCONF_ADDR + MPCONF_SIZE * virt_id);
/* set up the MP Block entry */
mpconf_ptr->lnch_parm = lnch_parm;
mpconf_ptr->rendezvous = (void *)rndvz_fn;
mpconf_ptr->rndv_parm = rndvz_parm;
mpconf_ptr->stack = stack;
/* do this last! - it will launch immediately */
mpconf_ptr->launch = (void *)lnch_fn;
}
return 0;
}
/*
* void slave_loop(void)
* slave processor idle-loop. the slaves periodically check to
* see if anyone has set a launch address. if so, this routine
* executes the launched code, passing the launch parameter.
*/
int
slave_loop(void)
{
uint vpid = cpuid(); /* virt CPU ID of this CPU */
void (*launch)(void *); /* launch function */
void (*rndvz)(void *); /* rendezvous function */
volatile mpconf_t *mpconf_ptr; /* ptr to cpu's MPCONF block */
if (Debug)
printf("CPU %d in slave_loop...\n", vpid);
mpconf_ptr = (volatile mpconf_t *)(MPCONF_ADDR + MPCONF_SIZE * vpid);
mpconf_ptr->idle_flag = 1;
while (1) {
us_delay(1000);
if (!mpconf_ptr->launch)
continue;
if (Debug)
printf("\nReceived launch request (0x%x)\n",
mpconf_ptr->launch);
/* make sure this is a valid MPCONF block */
if (mpconf_ptr->mpconf_magic != MPCONF_MAGIC ||
mpconf_ptr->virt_id != vpid) {
mpconf_ptr->launch = 0;
printf("slave_loop: Error: Invalid MPCONF block\n");
continue;
}
/* call the launch routine */
launch = (void (*)())mpconf_ptr->launch;
mpconf_ptr->launch = 0;
mpconf_ptr->idle_flag = 0;
if (mpconf_ptr->stack == (void *)0)
launch((void *)mpconf_ptr->lnch_parm);
else
call_coroutine(launch,
(void *)mpconf_ptr->lnch_parm,
(void *)mpconf_ptr->stack);
/* call the rendezvous routine */
if (mpconf_ptr->rendezvous != 0) {
rndvz = (void (*)())mpconf_ptr->rendezvous;
mpconf_ptr->rendezvous = 0;
if (mpconf_ptr->stack == (void *)0)
rndvz((void *)mpconf_ptr->rndv_parm);
else
call_coroutine(rndvz,
(void *)mpconf_ptr->rndv_parm,
(void *)mpconf_ptr->stack);
}
mpconf_ptr->idle_flag = 1;
}
/*NOTREACHED*/
}
/*
* Translate a Bus IO address into virtual address (uncached io)
* input:
* xport: bridge xbow port number
* io_space: MainIO, LargeIO, HugeIO
* use MAIN_IO_SPACE, LARGE_IO_SPACE, HUGE_IO_SPACE
* defined in sys/RACER/heart.h
* devaddr: offset to device from the base of PCI/GIO PIO addr
* 0 - 0x3FFF_FFFF (1 GB)
* return
* kernel virtual address to the device suitable for PIO
*/
__psunsigned_t
bridge_to_kv_pio(int xport, iopaddr_t io_space, __uint32_t devaddr)
{
__psunsigned_t kvaddr;
if (devaddr > BRIDGE_MAX_PIO_ADDR_MEM)
panic("bridge_to_kv_pio: bad device addr %x\n", devaddr);
switch (io_space) {
case MAIN_IO_SPACE:
kvaddr = K1_MAIN_WIDGET(xport) + devaddr;
break;
case LARGE_IO_SPACE:
kvaddr = K1_LARGE_WIDGET(xport);
kvaddr += BRIDGE_PIO32_XTALK_ALIAS_BASE + devaddr;
break;
case HUGE_IO_SPACE:
kvaddr = K1_HUGE_WIDGET(xport);
kvaddr += BRIDGE_PIO32_XTALK_ALIAS_BASE + devaddr;
break;
default:
panic("bridge_to_kv_pio: bad io space 0x%x\n", io_space);
}
return(kvaddr);
}
/*
* Translate a virtual memory address to 32 bit direct-mapped address
* for use by the DMA master of the device on a Bridge Bus (PCI/GIO)
* based on the direct map configuration of the specified Bridge.
* input:
* pbridge: pointer to the bridge.
* kvaddr: kernel virtual address of memory location
* return:
* 32bit PCI/GIO address for DMA to/from host memory
*/
__psunsigned_t
kv_to_bridge32_dirmap(void *pbridge, __psunsigned_t kvaddr)
{
bridge_t *bridge = (bridge_t *)pbridge;
bridgereg_t dirmap_reg = bridge->b_dir_map;
__psunsigned_t physaddr;
__psunsigned_t xtalk_addr_low;
/*
* left shift 31 bit is equivalent to multiplying by
* BRIDGE_DIRECT_32_SEG_SIZE == 2^31, 2GB's
*/
xtalk_addr_low = (dirmap_reg & BRIDGE_DIRMAP_OFF) << 31;
if (xtalk_addr_low == 0 && dirmap_reg & BRIDGE_DIRMAP_ADD512)
xtalk_addr_low = ABS_SYSTEM_MEMORY_BASE; /* 512MB */
physaddr = KDM_TO_PHYS(kvaddr);
if (physaddr < xtalk_addr_low ||
physaddr >= xtalk_addr_low + BRIDGE_DIRECT_32_SEG_SIZE)
panic("Cannot map 0x%x into the current PCI-32 direct mapped space\n", kvaddr);
return(physaddr - xtalk_addr_low + BRIDGE_DMA_DIRECT_BASE);
}
/*
* Translate a 32bit PCI/GIO direct map dma address of a Bridge
* to physical memory address based on the dirmap configuration of
* the specified Bridge.
* input:
* pbridge: pointer to the bridge
* daddr: 32bit PCI/GIO direct mapped address
* return:
* host memory physical address
*/
__psunsigned_t
bridge32_dirmap_to_phys(void *pbridge, __uint32_t daddr)
{
bridge_t *bridge = (bridge_t *)pbridge;
bridgereg_t dirmap_reg = bridge->b_dir_map;
__psunsigned_t xtalk_addr_low;
/*
* left shift 31 bit is equivalent to multiplying by
* BRIDGE_DIRECT_32_SEG_SIZE == 2^31, 2GB
*/
xtalk_addr_low = (dirmap_reg & BRIDGE_DIRMAP_OFF) << 31;
if (xtalk_addr_low == 0 && dirmap_reg & BRIDGE_DIRMAP_ADD512)
xtalk_addr_low = ABS_SYSTEM_MEMORY_BASE; /* 512MB */
if (daddr < BRIDGE_DMA_DIRECT_BASE ||
daddr - BRIDGE_DMA_DIRECT_BASE >= BRIDGE_DIRECT_32_SEG_SIZE)
panic("Cannot map 0x%x into the CPU physical memory space\n", daddr);
return(daddr - BRIDGE_DMA_DIRECT_BASE + xtalk_addr_low);
}
/* If password jumper is off, return 1 (override passwd, force console=g)
*/
int
jumper_off(void)
{
return ((BRIDGE_K1PTR->b_int_status & PROM_PASSWD_DISABLE) != 0);
}
/* Needs address to find double word side of cache. Both datums
* need to be read from double aligned addresses.
*
* For modules with x18 IBM SSRAMS:
*
* SSRAM SSRAM SSRAM
* cache addr bytes PM10 PM20-0 PM20-1
* ----- ---- ----- ---- ------ ------
* 00-15 0x08 6/7 U517 U524 U529
* 16-31 0x08 4/5 U11 U11 U19
* 32-47 0x08 2/3 U518 U525 U530
* 48-63 0x08 0/1 U10 U10 U18
* 64-79 0x00 6/7 U516 U523 U528
* 80-95 0x00 4/5 U9 U9 U17
* 96-111 0x00 2/3 U515 U522 U527
* 112-127 0x00 0/1 U7 U7 U15
*
* For modules with x36 IBM SSRAMS:
*
* SSRAM SSRAM SSRAM
* cache addr bytes PM10 PM20-0 PM20-1
* ----- ---- ----- ---- ------ ------
* 00-31 0x08 4-7 U11 U11 U19
* 32-63 0x08 0-3 U10 U10 U18
* 64-95 0x00 4-7 U9 U9 U17
* 96-127 0x00 0-3 U7 U7 U15
*
* The data must be swizzled a bit to match the schematic:
* DDDDCCCCBBBBAAAA -> BBBBAAAADDDDCCCC
*
* ip30_ssram() assumes the data has been passed through ip30_ssram_swizzle()
* already.
*/
uint64_t
ip30_ssram_swizzle(uint64_t data)
{
return ((data & 0xffffffff00000000) >> 32) |
((data & 0x00000000ffffffff) << 32);
}
char *
ip30_ssram(void *addr, __uint64_t expected, __uint64_t actual, int cpu)
{
static char *ssrams_0[2][4] = { /* PM20 proc 0 */
{ "U524 ", "U11 ", "U525 ", "U10 " },
{ "U523 ", "U9 ", "U522 ", "U7 " }
};
static char *ssrams_1[2][4] = { /* PM20 proc 1 */
{ "U529 ", "U19 ", "U530 ", "U18 " },
{ "U528 ", "U17 ", "U527 ", "U15 " }
};
static char *ssrams_x[2][4] = { /* PM10 */
{ "U517 ", "U11 ", "U518 ", "U10 " },
{ "U516 ", "U9 ", "U515 ", "U7 " }
};
int i, side = ((__psunsigned_t)addr & 8) == 0; /* 8 -> 0, 0 -> 1 */
static char buf[(4*5)+1];
char **ssrams;
extern unsigned _sidcache_size;
*buf = '\0';
if (cpu_probe_all() == 2) {
/* XXX 431675: should depend on NIC value for PM20 */
ssrams = ((cpu == 0) ? ssrams_0 : ssrams_1)[side];
}
else
ssrams = ssrams_x[side];
if (_sidcache_size == 0x100000) { /* x18 SSRAMs - 1MB scache */
for (i=0; i < 4; i++) {
if ((expected ^ actual) & 0xffff) {
strcat(buf,ssrams[i]);
}
expected >>= 16;
actual >>= 16;
}
}
else { /* x36 SSRAMs - 2MB scache */
if ((expected ^ actual) & 0xffffffff)
strcat(buf,ssrams[1]);
if ((expected ^ actual) >> 32)
strcat(buf,ssrams[3]);
}
return buf;
}
char *
ip30_tag_ssram(int cpu)
{
/* XXX 431675: should depend on NIC value for PM20 */
if (cpu_probe_all() == 2)
return cpu ? "U14" : "U6";
return "U6";
}
#ifdef US_DELAY_DEBUG
/* rough check of us_delay. Is not too accurate as done in C. Caller needs
* to convert from C0_COUNT ticks @ 1/2 freqency of CPU to determine time.
*/
__uint32_t us_before, us_after;
static void
check_us_delay(void)
{
int i;
extern ulong uc_decinsperloop, decinsperloop;
us_delay(1); /* prime cache */
for (i = 0; i < 10; i++) {
us_delay(i);
printf("us_delay(%d) took a little less than 0x%x - 0x%x = "
"%u ticks.\n",
i,(__psunsigned_t)us_before,(__psunsigned_t)us_after,
us_after-us_before);
}
printf("uc_decinsperloop=%d decinsperloop=%d\n",
uc_decinsperloop, decinsperloop);
return;
}
#endif /* US_DELAY_DEBUG */
#ifndef IP30_RPROM
void
cpu_clearnofault(void)
{
bridge_clearnofault();
/* only for ide ecc test, postpone clearing the heart registers */
if (!ide_ecc_test) heart_clearnofault();
}
/* Framework to add 'misc' devices to the inventory list. With card ids
* hopefully we can support better naming easily and cheaply.
*/
static COMPONENT*
addmisc(COMPONENT *p, CONFIGCLASS class, CONFIGTYPE type, ULONG key, char *id)
{
COMPONENT tmpl;
tmpl.Class = class;
tmpl.Type = type;
tmpl.Flags = 0;
tmpl.Version = SGI_ARCS_VERS;
tmpl.Revision = SGI_ARCS_REV;
tmpl.Key = key;
tmpl.AffinityMask = 0x1;
tmpl.ConfigurationDataSize = 0;
tmpl.IdentifierLength = strlen(id) + 1;
tmpl.Identifier = id;
p = AddChild(p,(COMPONENT *)&tmpl,0);
if (p == (COMPONENT *)NULL) cpu_acpanic("misc");
return p;
}
#define MAXHINVS 8
static slotdrvr_id_t hinvids[MAXHINVS] = {
{BUS_PCI, 0x10a9, 0x5, REV_ANY}, /* 0 */
{BUS_XTALK,HQ4_WIDGET_MFGR_NUM,HQ4_WIDGET_PART_NUM,REV_ANY}, /* 1 */
{BUS_PCI, 0x10b6, 0x2, REV_ANY}, /* 2 */
{BUS_PCI, 0x10b6, 0x4, REV_ANY}, /* 3 */
{BUS_PCI, 0x1112, 0x2200, REV_ANY}, /* 4 */
{BUS_XTALK,TPU_WIDGET_MFGR_NUM,TPU_WIDGET_PART_NUM,REV_ANY}, /* 5 */
{BUS_XTALK,ODSY_XT_ID_MFG_NUM_VALUE,ODSY_XT_ID_PART_NUM_VALUE,REV_ANY}, /* 6 */
{BUS_NONE, 0, 0, 0} /* 7 */
};
drvrinfo_t hinv_drvrinfo = { hinvids, "hinv" };
/* Keep track of PCI bus, and hinv count per bus so hinv keys are right */
static COMPONENT *hinvparent[MAXHINVS];
static char hinvcnt[MAXHINVS];
int
hinv_install(COMPONENT *p, slotinfo_t *slot)
{
char pbuf[128];
if (slot->drvr_index < MAXHINVS) {
if (p != hinvparent[slot->drvr_index]) {
hinvparent[slot->drvr_index] = p;
hinvcnt[slot->drvr_index] = 0;
}
else
hinvcnt[slot->drvr_index]++;
}
/*
* the drvr_index is the index of the hinvids[]
* case ##: should match the static array initialization
*/
switch (slot->drvr_index) {
case 0:
addmisc(p, ControllerClass,AudioController,
hinvcnt[slot->drvr_index], "RAD Audio Processor");
break;
/* case 1: taken by mgras driver internal probe */
case 1: break;
case 2:
case 3:
sprintf(pbuf, "slot %d Madge PCI Token Ring part 0x%x rev %d",
slot->bus_slot, slot->id.part, slot->id.rev);
addmisc(p, PeripheralClass, OtherPeripheral,
hinvcnt[slot->drvr_index], pbuf);
break;
case 4:
sprintf(pbuf, "slot %d RNS %x PCI FDDI rev %d",
slot->bus_slot, slot->id.part, slot->id.rev);
addmisc(p, PeripheralClass, OtherPeripheral,
hinvcnt[slot->drvr_index], pbuf);
break;
case 5:
sprintf(pbuf, "slot %d TPU ASIC rev %d",
slot->bus_slot, slot->id.rev);
addmisc(p, AdapterClass, TPUAdapter,
slot->bus_slot, pbuf);
break;
/* case 6: taken by odsy driver internal probe */
case 6: break;
}
return 0;
}
#endif /* !IP30_RPROM */
/* Use heart as R10K counter does not free run in the kernel.
*/
ULONG
GetRelativeTime(void)
{
return (HEART_PIU_K1PTR->h_count/HEART_COUNT_RATE);
}
/* from irix/kern/ml/error.c */
char *etstrings[] = {"OK", "DB", "CB", "2 Bit", "3 Bit", "4 Bit", "Fatal"};
eccdesc_t data_eccsyns[] = {
/* 0|8 1|9 2|A 3|B 4|C 5|D 6|E 7|F */
/* 0*/ {OK, 0},{CB, 0},{CB, 1},{B2, 0},{CB, 2},{B2, 0},{B2, 0},{DB, 7},
/* 8*/ {CB, 3},{B2, 0},{B2, 0},{DB,54},{B2, 0},{DB, 6},{DB,55},{B2, 0},
/*10*/ {CB, 4},{B2, 0},{B2, 0},{DB, 0},{B2, 0},{DB,20},{DB,48},{B2, 0},
/*18*/ {B2, 0},{DB,24},{DB,28},{B2, 0},{DB,16},{B2, 0},{B2, 0},{DB,52},
/*20*/ {CB, 5},{B2, 0},{B2, 0},{DB, 1},{B2, 0},{DB,21},{DB,49},{B2, 0},
/*28*/ {B2, 0},{DB,25},{DB,29},{B2, 0},{DB,17},{B2, 0},{B2, 0},{DB, 4},
/*30*/ {B2, 0},{DB,44},{DB,45},{B2, 0},{DB,46},{B2, 0},{B2, 0},{B3, 0},
/*38*/ {DB,47},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*40*/ {CB, 6},{B2, 0},{B2, 0},{DB, 2},{B2, 0},{DB,22},{DB,50},{B2, 0},
/*48*/ {B2, 0},{DB,26},{DB,30},{B2, 0},{DB,18},{B2, 0},{B2, 0},{DB,10},
/*50*/ {B2, 0},{DB,32},{DB,33},{B2, 0},{DB,34},{B2, 0},{B2, 0},{B3, 0},
/*58*/ {DB,35},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*60*/ {B2, 0},{DB,12},{DB,13},{B2, 0},{DB,14},{B2, 0},{B2, 0},{B3, 0},
/*68*/ {DB,15},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*70*/ {DB, 9},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*78*/ {B2, 0},{B3, 0},{B3, 0},{B2, 0},{B3, 0},{B2, 0},{B2, 0},{UN, 0},
/*80*/ {CB, 7},{B2, 0},{B2, 0},{DB, 3},{B2, 0},{DB,23},{DB,51},{B2, 0},
/*88*/ {B2, 0},{DB,27},{DB,31},{B2, 0},{DB,19},{B2, 0},{B2, 0},{DB,58},
/*90*/ {B2, 0},{DB,36},{DB,37},{B2, 0},{DB,38},{B2, 0},{B2, 0},{B3, 0},
/*98*/ {DB,39},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*a0*/ {B2, 0},{DB,40},{DB,41},{B2, 0},{DB,42},{B2, 0},{B2, 0},{B3, 0},
/*a8*/ {DB,43},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*b0*/ {DB,56},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*b8*/ {B2, 0},{B3, 0},{B3, 0},{B2, 0},{B3, 0},{B2, 0},{B2, 0},{UN, 0},
/*c0*/ {B2, 0},{DB,60},{DB,61},{B2, 0},{DB,62},{B2, 0},{B2, 0},{B3, 0},
/*c8*/ {DB,63},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*d0*/ {DB, 8},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*d8*/ {B2, 0},{B3, 0},{B3, 0},{B2, 0},{B3, 0},{B2, 0},{B2, 0},{UN, 0},
/*e8*/ {DB,57},{B2, 0},{B2, 0},{B3, 0},{B2, 0},{B3, 0},{B3, 0},{B2, 0},
/*e8*/ {B2, 0},{B3, 0},{B3, 0},{B2, 0},{B3, 0},{B2, 0},{B2, 0},{UN, 0},
/*f8*/ {B2, 0},{DB, 5},{DB,53},{B2, 0},{DB,59},{B2, 0},{B2, 0},{UN, 0},
/*f8*/ {DB,11},{B2, 0},{B2, 0},{UN, 0},{B2, 0},{UN, 0},{UN, 0},{B2, 0},
};
#endif /* IP30 (whole file) */