linux/arch/e2k/kernel/e2k.c

514 lines
11 KiB
C

#include <linux/ptrace.h>
#include <linux/hardirq.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/seq_file.h>
#include <linux/smp.h>
#include <linux/utsname.h>
#include <linux/pci.h>
#include <asm/e2k_api.h>
#include <asm/e2k_debug.h>
#include <asm/boot_recovery.h>
#include <asm/e2k.h>
#include <asm/e2k_sic.h>
#include <asm/e3m_iohub.h>
#include <asm/e3m_iohub_lms.h>
#include <asm/e3s.h>
#include <asm/e3s_lms.h>
#include <asm/es2.h>
#include <asm/es2_lms.h>
#include <asm/e2s.h>
#include <asm/e2s_lms.h>
#include <asm/e8c.h>
#include <asm/e8c_lms.h>
#include <asm/e1cp.h>
#include <asm/e1cp_lms.h>
#include <asm/e8c2.h>
#include <asm/e8c2_lms.h>
#include <asm/byteorder.h>
#include <asm/machdep.h>
#include <asm/smp.h>
#include <asm/io.h>
#include <asm/apic.h>
#include <asm/iommu.h>
#include <asm-l/i2c-spi.h>
#ifdef CONFIG_PROTECTED_MODE
#include <asm/3p.h>
#endif /* CONFIG_PROTECTED_MODE */
#ifdef CONFIG_RECOVERY
#include <asm/cnt_point.h>
#endif /* CONFIG_RECOVERY */
#undef DEBUG_IRQ_MODE
#undef DebugIRQ
#define DEBUG_IRQ_MODE 0 /* interrupts */
#define DebugIRQ(...) DebugPrint(DEBUG_IRQ_MODE ,##__VA_ARGS__)
extern void e2k_safe_infinite_loop(void);
extern void e2k_do_safe_reset_machine(u64, u32);
extern void obsolete_i8259_init(void);
#ifdef CONFIG_RECOVERY
extern void obsolete_i8259_recovery(void);
#endif /* CONFIG_RECOVERY */
/*
* Get CPU information for use by the procfs.
*/
#define DBG_HANG 0
#if DBG_HANG
extern char *execute_command;
extern void coredump_in_future(void);
#endif
extern char *get_mach_type_name(void);
static int soft_reset_off = 0;
static int __init
disable_soft_reset(char *str)
{
soft_reset_off = 1;
return 1;
}
__setup("softresetoff", disable_soft_reset);
static int __init
enable_soft_reset(char *str)
{
soft_reset_off = 0;
return 1;
}
__setup("softreseton", enable_soft_reset);
static int __init
setup_soft_reset(char *str)
{
if (strcmp(str, "on") == 0) {
soft_reset_off = 0;
} else if (strcmp(str, "off") == 0) {
soft_reset_off = 1;
} else {
printk("SOFT RESET enable/disable is not changed and is %s\n",
(soft_reset_off) ? "off" : "on");
}
return 1;
}
__setup("softreset=", setup_soft_reset);
int
e2k_show_cpuinfo(struct seq_file *m, void *v)
{
struct cpuinfo_e2k *c = v;
u8 cputype;
#ifdef CONFIG_SMP
# define lpj c->loops_per_jiffy
# define cpunum c->cpu
#else
# define lpj loops_per_jiffy
# define cpunum 0
#endif
#ifdef CONFIG_SMP
if (!cpu_online(cpunum))
return 0;
#endif
/*
* Boot is brain-dead and takes cpu_type from RAM, so one should use
* cpu_type from boot in borderline case only ("virtual" cpu).
*/
cputype = (HAS_MACHINE_VIRT_CPU) ?
bootblock_virt->info.bios.cpu_type : c->model;
seq_printf(m,
"processor\t: %d\n"
"vendor_id\t: %s\n"
"cpu family\t: %d\n"
"model\t\t: %d\n"
"model name\t: %s\n"
"revision\t: %u\n"
"cpu MHz\t\t: %lu.%02lu\n"
"L1 cache size\t: %d KB\n"
"L1 cache line length\t: %d bytes\n"
"L2 cache size\t: %d KB\n"
"L2 cache line length\t: %d bytes\n",
cpunum,
/* c->vendor, */
mcst_mb_name,
c->family,
c->model,
GET_CPU_TYPE_NAME(cputype),
/* get_mach_type_name(), */
c->revision,
c->proc_freq / 1000000, c->proc_freq % 1000000,
c->L1_size,
c->L1_bytes,
c->L2_size,
c->L2_bytes);
if (c->L3_size) {
seq_printf(m,
"L3 cache size\t: %d KB\n"
"L3 cache line length\t: %d bytes\n",
c->L3_size,
c->L3_bytes);
}
seq_printf(m,
"bogomips\t: %lu.%02lu\n\n",
lpj/(500000/HZ),
(lpj/(5000/HZ)) % 100);
#if 0 /* Not implemented yet */
#ifdef CONFIG_SMP
smp_bogo(m);
#endif
mmu_info(m);
#ifdef CONFIG_SMP
smp_info(m);
#endif
#endif
return 0;
}
/* Add for rdma_sic module */
int rdma_present = 0;
EXPORT_SYMBOL(rdma_present);
#if IS_ENABLED(CONFIG_ELDSP)
void (*eldsp_interrupt_p)(struct pt_regs *regs) = NULL;
EXPORT_SYMBOL(eldsp_interrupt_p);
static void eldsp_interrupt(struct pt_regs *regs)
{
static int int_eldsp_error = 0;
ack_APIC_irq();
irq_enter();
if (eldsp_interrupt_p) {
eldsp_interrupt_p(regs);
} else {
if (!int_eldsp_error)
printk("eldsp: attempt calling null handler\n");
int_eldsp_error++;
}
inc_irq_stat(irq_eldsp_count);
irq_exit();
}
#endif
#define L_IOMMU_MLT_HIT 0x8
#define L_IOMMU_PROT_VIOL_RD 0x4
#define L_IOMMU_PROT_VIOL_WR 0x2
#define L_IOMMU_MMU_ERR_ADDR 0x1
static void iommu_interrupt(struct pt_regs *regs)
{
int node = numa_node_id(), link;
int cpu = smp_processor_id();
unsigned long fsr = 0, fsr2 = 0;
char *err;
ack_APIC_irq();
irq_enter();
for (link = 0; link < MACH_MAX_NUMIOLINKS; link++) {
fsr = l_iommu_read(node, link, L_IOMMU_ERROR);
fsr2 = l_iommu_read(node, link, L_IOMMU_ERROR1);
if (fsr)
break;
}
err = fsr & L_IOMMU_MLT_HIT ? "Multihit"
: fsr & L_IOMMU_PROT_VIOL_WR ? "Write protection error"
: fsr & L_IOMMU_MMU_ERR_ADDR ? "Page miss"
: fsr & L_IOMMU_PROT_VIOL_RD ? "Write protection error"
: "Unknown error";
panic("IOMMU:%d:%d: error on %d cpu:"
"\t\t%s (error regs:%lx,%lx) at address 0x%lx.\n",
node, link, cpu,
err, fsr, fsr2, (fsr & (~0xf)) << (IO_PAGE_SHIFT - 4));
irq_exit();
}
static void sic_error_interrupt(struct pt_regs *regs)
{
int node;
pr_err("SIC error interrupt received on CPU%d:\n",
smp_processor_id());
for_each_online_node(node) {
pr_err("\tNODE%d SIC_INT=0x%x\n",
node,
sic_read_node_nbsr_reg(node, SIC_sic_int));
}
pr_err("Dazed and confused, but trying to continue\n");
}
void __init_recv
e2k_init_IRQ(int recovery_flag)
{
init_bsp_APIC();
/*
* Currently we don't have to initialize
* interrupt[] array on recovery path.
*/
if (!recovery_flag) {
/* Initialize interrupt[] array of system interrupts' handlers. */
l_init_system_handlers_table();
if(l_iommu_supported())
setup_APIC_vector_handler(LVT3_INTERRUPT_VECTOR,
iommu_interrupt, 1,
"iommu_interrupt");
#if defined(CONFIG_ELDSP) || defined(CONFIG_ELDSP_MODULE)
else if(IS_MACHINE_ES2)
setup_APIC_vector_handler(LVT3_INTERRUPT_VECTOR,
eldsp_interrupt, 1,
"eldsp_interrupt");
#endif
if (IS_MACHINE_E2S || IS_MACHINE_E8C || IS_MACHINE_E8C2)
setup_APIC_vector_handler(LVT4_INTERRUPT_VECTOR,
sic_error_interrupt, 1,
"sic_error_interrupt");
else if (IS_MACHINE_E1CP) {
pr_err("setup APIC LVT vectors is not yet implemented "
"for e1c+\n");
}
}
if (!recovery_flag) {
default_setup_apic_routing();
if (!verify_local_APIC())
pr_emerg("LAPIC is broken, trying to continue...\n");
}
connect_bsp_APIC();
setup_local_APIC();
if (!recovery_flag)
/* Enable IO APIC before setting up error vector. */
enable_IO_APIC();
bsp_end_local_APIC_setup();
if (apic->setup_portio_remap)
apic->setup_portio_remap();
#ifdef CONFIG_RECOVERY
setup_IO_APIC(recovery_flag);
#else
setup_IO_APIC();
#endif
}
void write_back_cache_ipi(void *unused)
{
unsigned long mmu_cr;
unsigned long flags;
raw_local_irq_save(flags);
mmu_cr = READ_MMU_CR();
mmu_cr &= ~_MMU_CR_CD_MASK;
mmu_cr |= _MMU_CD_DIS;
write_back_CACHE_all();
WRITE_MMU_CR(__mmu_reg(mmu_cr));
raw_local_irq_restore(flags);
}
void e2k_safe_reset_machine(void *pci_dev)
{
struct pci_dev *dev = (struct pci_dev *) pci_dev;
static atomic_t cpus = ATOMIC_INIT(0);
u64 addr;
raw_local_irq_disable();
atomic_inc(&cpus);
while (atomic_read(&cpus) != num_online_cpus());
/*
* All cpus reach this point simultaneously. After that BSP processor
* performes softreset, other cpus wait in infinite loop. While doing
* softreset and waiting in infinite loop memory is not used. It is
* needed to workaround hardware bug #54222.
*/
if (IS_BOOT_STRAP_CPU()) {
addr = CONFIG_CMD(
dev->bus->number,
dev->devfn,
PCI_SOFT_RESET_CONTROL) +
domain_pci_conf_base(pci_domain_nr(dev->bus));
e2k_do_safe_reset_machine(addr, L_SOFTWARE_RESET);
} else
e2k_safe_infinite_loop();
}
void
e2k_reset_machine(void)
{
/* Reset dump analyze mode before reset */
#if defined (CONFIG_RECOVERY) && (CONFIG_CNT_POINTS_NUM < 2)
if (read_bootblock_flags(bootblock_phys) & DUMP_ANALYZE_BB_FLAG)
reset_bootblock_flags(bootblock_phys, RECOVERY_BB_FLAG |
CNT_POINT_BB_FLAG | DUMP_ANALYZE_BB_FLAG);
#endif /* CONFIG_RECOVERY && (CONFIG_CNT_POINTS_NUM < 2) */
if (machine.arch_reset) {
machine.arch_reset();
}
}
/* static int ide_irqs = 0; */
void e2k_restart(char *cmd)
{
while (soft_reset_off) {
E2K_CMD_SEPARATOR;
}
e2k_reset_machine();
/* Never reached */
printk("System did not restart, so it can be done only by hands\n");
}
void e2k_power_off(void)
{
printk("System power off...\n");
while (soft_reset_off) {
E2K_CMD_SEPARATOR;
}
if (machine.arch_halt) {
machine.arch_halt();
}
E2K_HALT_OK();
}
void e2k_halt(void)
{
printk("System halted.\n");
while (soft_reset_off) {
E2K_CMD_SEPARATOR;
}
if (machine.arch_halt) {
machine.arch_halt();
}
E2K_HALT_OK();
}
/*
* Power off function, if any
*/
void (*pm_power_off)(void) = e2k_power_off;
EXPORT_SYMBOL(pm_power_off);
#ifndef CONFIG_E2K_MACHINE
void __init
e2k_setup_machine(void)
{
switch (machine.id)
{
case MACHINE_ID_E3M_LMS:
e3m_lms_setup_arch();
break;
case MACHINE_ID_E3M:
e3m_setup_arch();
break;
case MACHINE_ID_E3M_IOHUB_LMS:
e3m_iohub_lms_setup_machine();
break;
case MACHINE_ID_E3M_IOHUB:
e3m_iohub_setup_machine();
break;
case MACHINE_ID_E3S_LMS:
e3s_lms_setup_machine();
break;
case MACHINE_ID_E3S:
e3s_setup_machine();
break;
case MACHINE_ID_ES2_DSP_LMS:
case MACHINE_ID_ES2_RU_LMS:
es2_lms_setup_machine();
break;
case MACHINE_ID_ES2_DSP:
case MACHINE_ID_ES2_RU:
es2_setup_machine();
break;
case MACHINE_ID_E2S_LMS:
e2s_lms_setup_machine();
break;
case MACHINE_ID_E2S:
e2s_setup_machine();
break;
case MACHINE_ID_E8C_LMS:
e8c_lms_setup_machine();
break;
case MACHINE_ID_E8C:
e8c_setup_machine();
break;
case MACHINE_ID_E1CP_LMS:
e1cp_lms_setup_machine();
break;
case MACHINE_ID_E1CP:
e1cp_setup_machine();
break;
case MACHINE_ID_E8C2_LMS:
e8c2_lms_setup_machine();
break;
case MACHINE_ID_E8C2:
e8c2_setup_machine();
break;
default:
panic("setup_arch(): !!! UNKNOWN MACHINE TYPE !!!\n");
machine.setup_arch = NULL;
break;
}
}
#endif /* CONFIG_E2K_MACHINE */
#if defined CONFIG_E2K_E3M || defined CONFIG_E2K_E3M_SIM \
|| defined CONFIG_E2K_E3M_IOHUB || defined CONFIG_E2K_E3M_IOHUB_SIM \
|| !defined CONFIG_E2K_MACHINE
int e3m_get_vector(void)
{
return APIC_VECT_VECTOR(arch_apic_read(APIC_VECT));
}
#endif
/*
* machine structure is constant structure so can has own copy
* on each node in the case of NUMA
* Copy the structure to all nodes
*/
void __init
e2k_setup_arch(void)
{
int nid;
for_each_node_has_dup_kernel(nid) {
the_node_machine(nid)->show_cpuinfo = e2k_show_cpuinfo;
the_node_machine(nid)->init_IRQ = e2k_init_IRQ;
the_node_machine(nid)->restart = e2k_restart;
the_node_machine(nid)->power_off = e2k_power_off;
the_node_machine(nid)->halt = e2k_halt;
}
}