int imc_watchdog_cb(void *v, int period) { uint32_t reg; if (period == 0) { /* reset... */ imc_write(IMC_WDOG, 0); /* ...and disable */ reg = imc_read(IMC_CPUCTRL0); reg &= ~(IMC_CPUCTRL0_WDOG); imc_write(IMC_CPUCTRL0, reg); return 0; } else { /* enable... */ reg = imc_read(IMC_CPUCTRL0); reg |= IMC_CPUCTRL0_WDOG; imc_write(IMC_CPUCTRL0, reg); /* ...and reset */ imc_write(IMC_WDOG, 0); /* * The watchdog period is not controllable; it will fire * when the 20 bit counter, running on a 64 usec clock, * overflows. */ return (64 << 20) / 1000000; } }
uint32_t imc_bus_error(uint32_t hwpend, struct trap_frame *tf) { uint32_t cpustat, giostat; int quiet = 0; cpustat = imc_read(IMC_CPU_ERRSTAT); giostat = imc_read(IMC_GIO_ERRSTAT); switch (sys_config.system_type) { case SGI_IP28: /* * R10000 speculative execution may attempt to access * non-existing memory when in the kernel. We do not * want to flood the console about those. */ if ((cpustat & IMC_CPU_ERRSTAT_ADDR) && IS_XKPHYS((vaddr_t)tf->pc)) quiet = 1; /* This happens. No idea why. */ if (cpustat == 0 && giostat == 0) quiet = 1; break; } if (quiet == 0) { printf("bus error:"); if (cpustat != 0) { vaddr_t pc = tf->pc; uint32_t insn = 0xffffffff; if (tf->pc < 0) guarded_read_4(pc, &insn); else copyin((void *)pc, &insn, sizeof insn); printf(" cpu_stat %08x addr %08x pc %p insn %08x", cpustat, imc_read(IMC_CPU_ERRADDR), pc, insn); } if (giostat != 0) printf(" gio_stat %08x addr %08x", giostat, imc_read(IMC_GIO_ERRADDR)); printf("\n"); } if (cpustat != 0) imc_write(IMC_CPU_ERRSTAT, 0); if (giostat != 0) imc_write(IMC_GIO_ERRSTAT, 0); return hwpend; }
int imc_is_sysad_parity_enabled(void) { uint32_t reg; if (ip22_ecc) return 0; reg = imc_read(IMC_CPUCTRL0); return ~reg & IMC_CPUCTRL0_NCHKMEMPAR; }
void imc_enable_sysad_parity(void) { uint32_t reg; if (ip22_ecc) return; reg = imc_read(IMC_CPUCTRL0); reg &= ~IMC_CPUCTRL0_NCHKMEMPAR; imc_write(IMC_CPUCTRL0, reg); }
void imc_attach(struct device *parent, struct device *self, void *aux) { struct imc_attach_args iaa; #if NEISA > 0 struct eisabus_attach_args eba; #endif uint32_t reg, lastreg; uint32_t id, rev; int have_eisa; id = imc_read(IMC_SYSID); rev = id & IMC_SYSID_REVMASK; /* EISA exists on Indigo2 only */ if (sys_config.system_type != SGI_IP20 && sys_config.system_subtype == IP22_INDIGO2) have_eisa = (id & IMC_SYSID_HAVEISA) != 0; else have_eisa = 0; printf(": revision %d\n", rev); /* Clear CPU/GIO error status registers to clear any leftover bits. */ imc_bus_reset(); /* Disable watchdog if leftover from previous reboot */ imc_watchdog_cb(self, 0); /* Hook the bus error handler into the ISR */ set_intr(INTPRI_BUSERR, CR_INT_4, imc_bus_error); /* * Enable parity reporting on GIO/main memory transactions, except * on systems with the ECC memory controller, where enabling parity * interferes with regular operation and causes sticky false errors. * * Disable parity checking on CPU bus transactions (as turning * it on seems to cause spurious bus errors), but enable parity * checking on CPU reads from main memory (note that this bit * has the opposite sense... Turning it on turns the checks off!). * * Finally, turn on interrupt writes to the CPU from the MC. */ reg = imc_read(IMC_CPUCTRL0); if (ip22_ecc) reg &= ~(IMC_CPUCTRL0_GPR | IMC_CPUCTRL0_MPR); else reg |= IMC_CPUCTRL0_GPR | IMC_CPUCTRL0_MPR; reg &= ~IMC_CPUCTRL0_NCHKMEMPAR; reg |= IMC_CPUCTRL0_INTENA; imc_write(IMC_CPUCTRL0, reg); /* Setup the MC write buffer depth */ /* * XXX This hardcoded value is not documented anywhere, and can be * XXX traced back to DaveM's internship at SGI in 1996, so it can * XXX be considered correct at least for IP24 (and, to a lesser * XXX extent, IP22). IP20 and IP28 systems seem to run happy with * XXX this value as well. */ reg = imc_read(IMC_CPUCTRL1); reg = (reg & ~IMC_CPUCTRL1_MCHWMSK) | 13; /* * Force endianness on the onboard HPC and both slots. * This should be safe for Fullhouse, but leave it conditional * for now. */ switch (sys_config.system_type) { case SGI_IP22: if (sys_config.system_subtype == IP22_INDIGO2) break; /* FALLTHROUGH */ case SGI_IP20: reg |= IMC_CPUCTRL1_HPCFX; reg |= IMC_CPUCTRL1_EXP0FX; reg |= IMC_CPUCTRL1_EXP1FX; reg &= ~IMC_CPUCTRL1_HPCLITTLE; reg &= ~IMC_CPUCTRL1_EXP0LITTLE; reg &= ~IMC_CPUCTRL1_EXP1LITTLE; break; } imc_write(IMC_CPUCTRL1, reg); /* * Try and read the GIO64 arbitrator configuration register value. * See comments above the declaration of imc_arb_value for why we * are doing this. */ reg = 0; lastreg = ~reg; while (reg != lastreg || (reg & ~0xffff) != 0) { lastreg = reg; reg = imc_read(IMC_GIO64ARB); /* read another harmless register */ (void)imc_read(IMC_CPUCTRL0); } /* * Set GIO64 arbitrator configuration register: * * Preserve PROM-set graphics-related bits, as they seem to depend * on the graphics variant present and I'm not sure how to figure * that out or 100% sure what the correct settings are for each. */ reg &= (IMC_GIO64ARB_GRX64 | IMC_GIO64ARB_GRXRT | IMC_GIO64ARB_GRXMST); /* * Rest of settings are machine/board dependent */ switch (sys_config.system_type) { case SGI_IP20: reg |= IMC_GIO64ARB_ONEGIO; reg |= IMC_GIO64ARB_EXP0RT | IMC_GIO64ARB_EXP1RT; reg |= IMC_GIO64ARB_EXP0MST | IMC_GIO64ARB_EXP1MST; reg &= ~(IMC_GIO64ARB_HPC64 | IMC_GIO64ARB_HPCEXP64 | IMC_GIO64ARB_EISA64 | IMC_GIO64ARB_EXP064 | IMC_GIO64ARB_EXP164 | IMC_GIO64ARB_EXP0PIPE | IMC_GIO64ARB_EXP1PIPE); break; default: /* * GIO64 invariant for all IP22 platforms: one GIO bus, * HPC1 @ 64 */ reg |= IMC_GIO64ARB_ONEGIO | IMC_GIO64ARB_HPC64; switch (sys_config.system_subtype) { default: case IP22_INDY: case IP22_CHALLS: /* XXX is MST mutually exclusive? */ reg |= IMC_GIO64ARB_EXP0RT | IMC_GIO64ARB_EXP1RT; reg |= IMC_GIO64ARB_EXP0MST | IMC_GIO64ARB_EXP1MST; /* EISA (VINO, really) can bus-master, is 64-bit */ reg |= IMC_GIO64ARB_EISAMST | IMC_GIO64ARB_EISA64; break; case IP22_INDIGO2: /* * All Fullhouse boards have a 64-bit HPC2 and pipelined * EXP0 slot. */ reg |= IMC_GIO64ARB_HPCEXP64 | IMC_GIO64ARB_EXP0PIPE; /* * The EISA bus is the real thing, and is a 32-bit bus. */ reg &= ~IMC_GIO64ARB_EISA64; if (rev < 2) { /* EXP0 realtime, EXP1 can master */ reg |= IMC_GIO64ARB_EXP0RT | IMC_GIO64ARB_EXP1MST; } else { /* EXP1 pipelined as well, EISA masters */ reg |= IMC_GIO64ARB_EXP1PIPE | IMC_GIO64ARB_EISAMST; } break; } } imc_write(IMC_GIO64ARB, reg); imc_arb_value = reg; memset(&iaa, 0, sizeof(iaa)); iaa.iaa_name = "gio"; iaa.iaa_st = &imcbus_tag; iaa.iaa_dmat = &imc_bus_dma_tag; config_found(self, &iaa, imc_print); #if NEISA > 0 if (have_eisa) { memset(&eba, 0, sizeof(eba)); eba.eba_busname = "eisa"; eba.eba_iot = &imcbus_eisa_io_tag; eba.eba_memt = &imcbus_eisa_mem_tag; eba.eba_dmat = &imc_bus_dma_tag; eba.eba_ec = NULL; config_found(self, &eba, imc_print); } #endif /* Register watchdog */ wdog_register(imc_watchdog_cb, self); }
uint32_t imc_bus_error(uint32_t hwpend, struct trap_frame *tf) { uint32_t cpustat, giostat; paddr_t cpuaddr, gioaddr; int cpuquiet = 0, gioquiet = 0; cpustat = imc_read(IMC_CPU_ERRSTAT); cpuaddr = imc_read(IMC_CPU_ERRADDR); giostat = imc_read(IMC_GIO_ERRSTAT); gioaddr = imc_read(IMC_GIO_ERRADDR); switch (sys_config.system_type) { case SGI_IP28: /* * R10000 speculative execution may attempt to access * non-existing memory when in the kernel. We do not * want to flood the console about those. */ if (cpustat & IMC_CPU_ERRSTAT_ADDR) { if (IS_XKPHYS((vaddr_t)tf->pc)) cpuquiet = 1; } if (giostat != 0) { /* * Ignore speculative writes to interrupt controller * registers. */ if ((giostat & IMC_ECC_ERRSTAT_FUW) && (gioaddr & ~0x3f) == INT2_IP22) gioquiet = 1; /* XXX is it wise to hide these? */ if ((giostat & IMC_GIO_ERRSTAT_TMO) && !IS_GIO_ADDRESS(gioaddr)) gioquiet = 1; } break; } if (cpustat != 0 && cpuquiet == 0) { vaddr_t pc = tf->pc; uint32_t insn = 0xffffffff; if (tf->pc < 0) guarded_read_4(pc, &insn); else copyin((void *)pc, &insn, sizeof insn); printf("bus error: cpu_stat %08x addr %08lx pc %p insn %08x\n", cpustat, cpuaddr, (void *)pc, insn); } if (giostat != 0 && gioquiet == 0) { printf("bus error: gio_stat %08x addr %08lx\n", giostat, gioaddr); } if (cpustat != 0) imc_write(IMC_CPU_ERRSTAT, 0); if (giostat != 0) imc_write(IMC_GIO_ERRSTAT, 0); return hwpend; }