Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0
choice
prompt "Node addressing mode"
depends on SGI_IP27
default SGI_SN_M_MODE
config SGI_SN_M_MODE
bool "IP27 M-Mode"
help
The nodes of Origin, Onyx, Fuel and Tezro systems can be configured
in either N-Modes which allows for more nodes or M-Mode which allows
for more memory. Your hardware is almost certainly running in
M-Mode, so choose M-mode here.
config SGI_SN_N_MODE
bool "IP27 N-Mode"
help
The nodes of Origin, Onyx, Fuel and Tezro systems can be configured
in either N-Modes which allows for more nodes or M-Mode which allows
for more memory. Your hardware is almost certainly running in
M-Mode, so choose M-mode here.
endchoice
config MAPPED_KERNEL
bool "Mapped kernel support"
depends on SGI_IP27
help
Change the way a Linux kernel is loaded into memory on a MIPS64
machine. This is required in order to support text replication on
NUMA. If you need to understand it, read the source code.
config REPLICATE_KTEXT
bool "Kernel text replication support"
depends on SGI_IP27
select MAPPED_KERNEL
help
Say Y here to enable replicating the kernel text across multiple
nodes in a NUMA cluster. This trades memory for speed.
+11
View File
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the IP27 specific kernel interface routines under Linux.
#
obj-y := ip27-berr.o ip27-irq.o ip27-init.o ip27-klconfig.o \
ip27-klnuma.o ip27-memory.o ip27-nmi.o ip27-reset.o ip27-timer.o \
ip27-xtalk.o
obj-$(CONFIG_EARLY_PRINTK) += ip27-console.o
obj-$(CONFIG_SMP) += ip27-smp.o
+16
View File
@@ -0,0 +1,16 @@
#
# SGI-IP27 (Origin200/2000)
#
# Set the load address to >= 0xc000000000300000 if you want to leave space for
# symmon, 0xc00000000001c000 for production kernels. Note that the value must
# be 16kb aligned or the handling of the current variable will break.
#
cflags-$(CONFIG_SGI_IP27) += -I$(srctree)/arch/mips/include/asm/mach-ip27
ifdef CONFIG_MAPPED_KERNEL
load-$(CONFIG_SGI_IP27) += 0xc00000004001c000
OBJCOPYFLAGS := --change-addresses=0x3fffffff80000000
dataoffset-$(CONFIG_SGI_IP27) += 0x01000000
else
load-$(CONFIG_SGI_IP27) += 0xa80000000001c000
OBJCOPYFLAGS := --change-addresses=0x57ffffff80000000
endif
@@ -0,0 +1,98 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1994, 1995, 1996, 1999, 2000 by Ralf Baechle
* Copyright (C) 1999, 2000 by Silicon Graphics
* Copyright (C) 2002 Maciej W. Rozycki
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/signal.h> /* for SIGBUS */
#include <linux/sched.h> /* schow_regs(), force_sig() */
#include <linux/sched/debug.h>
#include <linux/sched/signal.h>
#include <asm/ptrace.h>
#include <asm/sn/addrs.h>
#include <asm/sn/agent.h>
#include <asm/sn/arch.h>
#include <asm/tlbdebug.h>
#include <asm/traps.h>
#include <linux/uaccess.h>
#include "ip27-common.h"
static void dump_hub_information(unsigned long errst0, unsigned long errst1)
{
static char *err_type[2][8] = {
{ NULL, "Uncached Partial Read PRERR", "DERR", "Read Timeout",
NULL, NULL, NULL, NULL },
{ "WERR", "Uncached Partial Write", "PWERR", "Write Timeout",
NULL, NULL, NULL, NULL }
};
union pi_err_stat0 st0;
union pi_err_stat1 st1;
st0.pi_stat0_word = errst0;
st1.pi_stat1_word = errst1;
if (!st0.pi_stat0_fmt.s0_valid) {
pr_info("Hub does not contain valid error information\n");
return;
}
pr_info("Hub has valid error information:\n");
if (st0.pi_stat0_fmt.s0_ovr_run)
pr_info("Overrun is set. Error stack may contain additional "
"information.\n");
pr_info("Hub error address is %08lx\n",
(unsigned long)st0.pi_stat0_fmt.s0_addr);
pr_info("Incoming message command 0x%lx\n",
(unsigned long)st0.pi_stat0_fmt.s0_cmd);
pr_info("Supplemental field of incoming message is 0x%lx\n",
(unsigned long)st0.pi_stat0_fmt.s0_supl);
pr_info("T5 Rn (for RRB only) is 0x%lx\n",
(unsigned long)st0.pi_stat0_fmt.s0_t5_req);
pr_info("Error type is %s\n", err_type[st1.pi_stat1_fmt.s1_rw_rb]
[st0.pi_stat0_fmt.s0_err_type] ? : "invalid");
}
static int ip27_be_handler(struct pt_regs *regs, int is_fixup)
{
unsigned long errst0, errst1;
int data = regs->cp0_cause & 4;
int cpu = LOCAL_HUB_L(PI_CPU_NUM);
if (is_fixup)
return MIPS_BE_FIXUP;
printk("Slice %c got %cbe at 0x%lx\n", 'A' + cpu, data ? 'd' : 'i',
regs->cp0_epc);
printk("Hub information:\n");
printk("ERR_INT_PEND = 0x%06llx\n", LOCAL_HUB_L(PI_ERR_INT_PEND));
errst0 = LOCAL_HUB_L(cpu ? PI_ERR_STATUS0_B : PI_ERR_STATUS0_A);
errst1 = LOCAL_HUB_L(cpu ? PI_ERR_STATUS1_B : PI_ERR_STATUS1_A);
dump_hub_information(errst0, errst1);
show_regs(regs);
dump_tlb_all();
while(1);
force_sig(SIGBUS);
}
void __init ip27_be_init(void)
{
/* XXX Initialize all the Hub & Bridge error handling here. */
int cpu = LOCAL_HUB_L(PI_CPU_NUM);
int cpuoff = cpu << 8;
mips_set_be_handler(ip27_be_handler);
LOCAL_HUB_S(PI_ERR_INT_PEND,
cpu ? PI_ERR_CLEAR_ALL_B : PI_ERR_CLEAR_ALL_A);
LOCAL_HUB_S(PI_ERR_INT_MASK_A + cpuoff, 0);
LOCAL_HUB_S(PI_ERR_STACK_ADDR_A + cpuoff, 0);
LOCAL_HUB_S(PI_ERR_STACK_SIZE, 0); /* Disable error stack */
LOCAL_HUB_S(PI_SYSAD_ERRCHK_EN, PI_SYSAD_CHECK_ALL);
}
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __IP27_COMMON_H
#define __IP27_COMMON_H
extern nasid_t master_nasid;
extern void cpu_node_probe(void);
extern void hub_rt_clock_event_init(void);
extern void hub_rtc_init(nasid_t nasid);
extern void install_cpu_nmi_handler(int slice);
extern void install_ipi(void);
extern void ip27_be_init(void);
extern void ip27_reboot_setup(void);
extern const struct plat_smp_ops ip27_smp_ops;
extern unsigned long node_getfirstfree(nasid_t nasid);
extern void per_cpu_init(void);
extern void replicate_kernel_text(void);
extern void setup_replication_mask(void);
#endif /* __IP27_COMMON_H */
@@ -0,0 +1,42 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001, 2002 Ralf Baechle
*/
#include <asm/page.h>
#include <asm/setup.h>
#include <asm/sn/addrs.h>
#include <asm/sn/agent.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/ioc3.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include "ip27-common.h"
#define IOC3_CLK (22000000 / 3)
#define IOC3_FLAGS (0)
static inline struct ioc3_uartregs *console_uart(void)
{
struct ioc3 *ioc3;
nasid_t nasid;
nasid = (master_nasid == INVALID_NASID) ? get_nasid() : master_nasid;
ioc3 = (struct ioc3 *)KL_CONFIG_CH_CONS_INFO(nasid)->memory_base;
return &ioc3->sregs.uarta;
}
void prom_putchar(char c)
{
struct ioc3_uartregs *uart = console_uart();
while ((readb(&uart->iu_lsr) & 0x20) == 0)
;
writeb(c, &uart->iu_thr);
}
+147
View File
@@ -0,0 +1,147 @@
/*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* Copyright (C) 2000 - 2001 by Kanoj Sarcar (kanoj@sgi.com)
* Copyright (C) 2000 - 2001 by Silicon Graphics, Inc.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/export.h>
#include <linux/cpumask.h>
#include <asm/bootinfo.h>
#include <asm/cpu.h>
#include <asm/io.h>
#include <asm/sgialib.h>
#include <asm/time.h>
#include <asm/sn/agent.h>
#include <asm/sn/types.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/ioc3.h>
#include <asm/mipsregs.h>
#include <asm/sn/gda.h>
#include <asm/sn/intr.h>
#include <asm/current.h>
#include <asm/processor.h>
#include <asm/mmu_context.h>
#include <asm/thread_info.h>
#include <asm/sn/launch.h>
#include <asm/sn/mapped_kernel.h>
#include "ip27-common.h"
#define CPU_NONE (cpuid_t)-1
static DECLARE_BITMAP(hub_init_mask, MAX_NUMNODES);
nasid_t master_nasid = INVALID_NASID;
struct cpuinfo_ip27 sn_cpu_info[NR_CPUS];
EXPORT_SYMBOL_GPL(sn_cpu_info);
static void per_hub_init(nasid_t nasid)
{
struct hub_data *hub = hub_data(nasid);
cpumask_set_cpu(smp_processor_id(), &hub->h_cpus);
if (test_and_set_bit(nasid, hub_init_mask))
return;
/*
* Set CRB timeout at 5ms, (< PI timeout of 10ms)
*/
REMOTE_HUB_S(nasid, IIO_ICTP, 0x800);
REMOTE_HUB_S(nasid, IIO_ICTO, 0xff);
hub_rtc_init(nasid);
if (nasid) {
/* copy exception handlers from first node to current node */
memcpy((void *)NODE_OFFSET_TO_K0(nasid, 0),
(void *)CKSEG0, 0x200);
__flush_cache_all();
/* switch to node local exception handlers */
REMOTE_HUB_S(nasid, PI_CALIAS_SIZE, PI_CALIAS_SIZE_8K);
}
}
void per_cpu_init(void)
{
int cpu = smp_processor_id();
nasid_t nasid = get_nasid();
clear_c0_status(ST0_IM);
per_hub_init(nasid);
pr_info("CPU %d clock is %dMHz.\n", cpu, sn_cpu_info[cpu].p_speed);
install_ipi();
/* Install our NMI handler if symmon hasn't installed one. */
install_cpu_nmi_handler(cputoslice(cpu));
enable_percpu_irq(IP27_HUB_PEND0_IRQ, IRQ_TYPE_NONE);
enable_percpu_irq(IP27_HUB_PEND1_IRQ, IRQ_TYPE_NONE);
}
void __init plat_mem_setup(void)
{
u64 p, e, n_mode;
nasid_t nid;
register_smp_ops(&ip27_smp_ops);
ip27_reboot_setup();
/*
* hub_rtc init and cpu clock intr enabled for later calibrate_delay.
*/
nid = get_nasid();
printk("IP27: Running on node %d.\n", nid);
p = LOCAL_HUB_L(PI_CPU_PRESENT_A) & 1;
e = LOCAL_HUB_L(PI_CPU_ENABLE_A) & 1;
printk("Node %d has %s primary CPU%s.\n", nid,
p ? "a" : "no",
e ? ", CPU is running" : "");
p = LOCAL_HUB_L(PI_CPU_PRESENT_B) & 1;
e = LOCAL_HUB_L(PI_CPU_ENABLE_B) & 1;
printk("Node %d has %s secondary CPU%s.\n", nid,
p ? "a" : "no",
e ? ", CPU is running" : "");
/*
* Try to catch kernel missconfigurations and give user an
* indication what option to select.
*/
n_mode = LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_MORENODES_MASK;
printk("Machine is in %c mode.\n", n_mode ? 'N' : 'M');
#ifdef CONFIG_SGI_SN_N_MODE
if (!n_mode)
panic("Kernel compiled for M mode.");
#else
if (n_mode)
panic("Kernel compiled for N mode.");
#endif
ioport_resource.start = 0;
ioport_resource.end = ~0UL;
set_io_port_base(IO_BASE);
}
const char *get_system_type(void)
{
return "SGI Origin";
}
void __init prom_init(void)
{
prom_init_cmdline(fw_arg0, (LONG *)fw_arg1);
prom_meminit();
}
+308
View File
@@ -0,0 +1,308 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ip27-irq.c: Highlevel interrupt handling for IP27 architecture.
*
* Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
* Copyright (C) 1999 - 2001 Kanoj Sarcar
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/irq_cpu.h>
#include <asm/sn/addrs.h>
#include <asm/sn/agent.h>
#include <asm/sn/arch.h>
#include <asm/sn/intr.h>
#include <asm/sn/irq_alloc.h>
#include "ip27-common.h"
struct hub_irq_data {
u64 *irq_mask[2];
cpuid_t cpu;
};
static DECLARE_BITMAP(hub_irq_map, IP27_HUB_IRQ_COUNT);
static DEFINE_PER_CPU(unsigned long [2], irq_enable_mask);
static inline int alloc_level(void)
{
int level;
again:
level = find_first_zero_bit(hub_irq_map, IP27_HUB_IRQ_COUNT);
if (level >= IP27_HUB_IRQ_COUNT)
return -ENOSPC;
if (test_and_set_bit(level, hub_irq_map))
goto again;
return level;
}
static void enable_hub_irq(struct irq_data *d)
{
struct hub_irq_data *hd = irq_data_get_irq_chip_data(d);
unsigned long *mask = per_cpu(irq_enable_mask, hd->cpu);
set_bit(d->hwirq, mask);
__raw_writeq(mask[0], hd->irq_mask[0]);
__raw_writeq(mask[1], hd->irq_mask[1]);
}
static void disable_hub_irq(struct irq_data *d)
{
struct hub_irq_data *hd = irq_data_get_irq_chip_data(d);
unsigned long *mask = per_cpu(irq_enable_mask, hd->cpu);
clear_bit(d->hwirq, mask);
__raw_writeq(mask[0], hd->irq_mask[0]);
__raw_writeq(mask[1], hd->irq_mask[1]);
}
static void setup_hub_mask(struct hub_irq_data *hd, const struct cpumask *mask)
{
nasid_t nasid;
int cpu;
cpu = cpumask_first_and(mask, cpu_online_mask);
if (cpu >= nr_cpu_ids)
cpu = cpumask_any(cpu_online_mask);
nasid = cpu_to_node(cpu);
hd->cpu = cpu;
if (!cputoslice(cpu)) {
hd->irq_mask[0] = REMOTE_HUB_PTR(nasid, PI_INT_MASK0_A);
hd->irq_mask[1] = REMOTE_HUB_PTR(nasid, PI_INT_MASK1_A);
} else {
hd->irq_mask[0] = REMOTE_HUB_PTR(nasid, PI_INT_MASK0_B);
hd->irq_mask[1] = REMOTE_HUB_PTR(nasid, PI_INT_MASK1_B);
}
}
static int set_affinity_hub_irq(struct irq_data *d, const struct cpumask *mask,
bool force)
{
struct hub_irq_data *hd = irq_data_get_irq_chip_data(d);
if (!hd)
return -EINVAL;
if (irqd_is_started(d))
disable_hub_irq(d);
setup_hub_mask(hd, mask);
if (irqd_is_started(d))
enable_hub_irq(d);
irq_data_update_effective_affinity(d, cpumask_of(hd->cpu));
return 0;
}
static struct irq_chip hub_irq_type = {
.name = "HUB",
.irq_mask = disable_hub_irq,
.irq_unmask = enable_hub_irq,
.irq_set_affinity = set_affinity_hub_irq,
};
static int hub_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
struct irq_alloc_info *info = arg;
struct hub_irq_data *hd;
struct hub_data *hub;
struct irq_desc *desc;
int swlevel;
if (nr_irqs > 1 || !info)
return -EINVAL;
hd = kzalloc_obj(*hd);
if (!hd)
return -ENOMEM;
swlevel = alloc_level();
if (unlikely(swlevel < 0)) {
kfree(hd);
return -EAGAIN;
}
irq_domain_set_info(domain, virq, swlevel, &hub_irq_type, hd,
handle_level_irq, NULL, NULL);
/* use CPU connected to nearest hub */
hub = hub_data(info->nasid);
setup_hub_mask(hd, &hub->h_cpus);
info->nasid = cpu_to_node(hd->cpu);
/* Make sure it's not already pending when we connect it. */
REMOTE_HUB_CLR_INTR(info->nasid, swlevel);
desc = irq_to_desc(virq);
desc->irq_common_data.node = info->nasid;
cpumask_copy(desc->irq_common_data.affinity, &hub->h_cpus);
return 0;
}
static void hub_domain_free(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs)
{
struct irq_data *irqd;
if (nr_irqs > 1)
return;
irqd = irq_domain_get_irq_data(domain, virq);
if (irqd)
kfree(irqd->chip_data);
}
static const struct irq_domain_ops hub_domain_ops = {
.alloc = hub_domain_alloc,
.free = hub_domain_free,
};
/*
* This code is unnecessarily complex, because we do
* intr enabling. Basically, once we grab the set of intrs we need
* to service, we must mask _all_ these interrupts; firstly, to make
* sure the same intr does not intr again, causing recursion that
* can lead to stack overflow. Secondly, we can not just mask the
* one intr we are do_IRQing, because the non-masked intrs in the
* first set might intr again, causing multiple servicings of the
* same intr. This effect is mostly seen for intercpu intrs.
* Kanoj 05.13.00
*/
static void ip27_do_irq_mask0(struct irq_desc *desc)
{
cpuid_t cpu = smp_processor_id();
unsigned long *mask = per_cpu(irq_enable_mask, cpu);
struct irq_domain *domain;
u64 pend0;
int ret;
/* copied from Irix intpend0() */
pend0 = LOCAL_HUB_L(PI_INT_PEND0);
pend0 &= mask[0]; /* Pick intrs we should look at */
if (!pend0)
return;
#ifdef CONFIG_SMP
if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) {
LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ);
scheduler_ipi();
} else if (pend0 & (1UL << CPU_RESCHED_B_IRQ)) {
LOCAL_HUB_CLR_INTR(CPU_RESCHED_B_IRQ);
scheduler_ipi();
} else if (pend0 & (1UL << CPU_CALL_A_IRQ)) {
LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ);
generic_smp_call_function_interrupt();
} else if (pend0 & (1UL << CPU_CALL_B_IRQ)) {
LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ);
generic_smp_call_function_interrupt();
} else
#endif
{
domain = irq_desc_get_handler_data(desc);
ret = generic_handle_domain_irq(domain, __ffs(pend0));
if (ret)
spurious_interrupt();
}
LOCAL_HUB_L(PI_INT_PEND0);
}
static void ip27_do_irq_mask1(struct irq_desc *desc)
{
cpuid_t cpu = smp_processor_id();
unsigned long *mask = per_cpu(irq_enable_mask, cpu);
struct irq_domain *domain;
u64 pend1;
int ret;
/* copied from Irix intpend0() */
pend1 = LOCAL_HUB_L(PI_INT_PEND1);
pend1 &= mask[1]; /* Pick intrs we should look at */
if (!pend1)
return;
domain = irq_desc_get_handler_data(desc);
ret = generic_handle_domain_irq(domain, __ffs(pend1) + 64);
if (ret)
spurious_interrupt();
LOCAL_HUB_L(PI_INT_PEND1);
}
void install_ipi(void)
{
int cpu = smp_processor_id();
unsigned long *mask = per_cpu(irq_enable_mask, cpu);
int slice = LOCAL_HUB_L(PI_CPU_NUM);
int resched, call;
resched = CPU_RESCHED_A_IRQ + slice;
set_bit(resched, mask);
LOCAL_HUB_CLR_INTR(resched);
call = CPU_CALL_A_IRQ + slice;
set_bit(call, mask);
LOCAL_HUB_CLR_INTR(call);
if (slice == 0) {
LOCAL_HUB_S(PI_INT_MASK0_A, mask[0]);
LOCAL_HUB_S(PI_INT_MASK1_A, mask[1]);
} else {
LOCAL_HUB_S(PI_INT_MASK0_B, mask[0]);
LOCAL_HUB_S(PI_INT_MASK1_B, mask[1]);
}
}
void __init arch_init_irq(void)
{
struct irq_domain *domain;
struct fwnode_handle *fn;
mips_cpu_irq_init();
/*
* Some interrupts are reserved by hardware or by software convention.
* Mark these as reserved right away so they won't be used accidentally
* later.
*/
bitmap_set(hub_irq_map, 0, CPU_CALL_B_IRQ + 1);
bitmap_set(hub_irq_map, NI_BRDCAST_ERR_A, MSC_PANIC_INTR - NI_BRDCAST_ERR_A + 1);
fn = irq_domain_alloc_named_fwnode("HUB");
if (WARN_ON(fn == NULL))
return;
domain = irq_domain_create_linear(fn, IP27_HUB_IRQ_COUNT,
&hub_domain_ops, NULL);
if (WARN_ON(domain == NULL))
return;
irq_set_default_domain(domain);
irq_set_percpu_devid(IP27_HUB_PEND0_IRQ);
irq_set_chained_handler_and_data(IP27_HUB_PEND0_IRQ, ip27_do_irq_mask0,
domain);
irq_set_percpu_devid(IP27_HUB_PEND1_IRQ);
irq_set_chained_handler_and_data(IP27_HUB_PEND1_IRQ, ip27_do_irq_mask1,
domain);
}
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/param.h>
#include <linux/timex.h>
#include <linux/mm.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/arch.h>
#include <asm/sn/gda.h>
klinfo_t *find_component(lboard_t *brd, klinfo_t *kli, unsigned char struct_type)
{
int index, j;
if (kli == (klinfo_t *)NULL) {
index = 0;
} else {
for (j = 0; j < KLCF_NUM_COMPS(brd); j++)
if (kli == KLCF_COMP(brd, j))
break;
index = j;
if (index == KLCF_NUM_COMPS(brd)) {
printk("find_component: Bad pointer: 0x%p\n", kli);
return (klinfo_t *)NULL;
}
index++; /* next component */
}
for (; index < KLCF_NUM_COMPS(brd); index++) {
kli = KLCF_COMP(brd, index);
if (KLCF_COMP_TYPE(kli) == struct_type)
return kli;
}
/* Didn't find it. */
return (klinfo_t *)NULL;
}
klinfo_t *find_first_component(lboard_t *brd, unsigned char struct_type)
{
return find_component(brd, (klinfo_t *)NULL, struct_type);
}
lboard_t *find_lboard(lboard_t *start, unsigned char brd_type)
{
/* Search all boards stored on this node. */
while (start) {
if (start->brd_type == brd_type)
return start;
start = KLCF_NEXT(start);
}
/* Didn't find it. */
return (lboard_t *)NULL;
}
lboard_t *find_lboard_class(lboard_t *start, unsigned char brd_type)
{
/* Search all boards stored on this node. */
while (start) {
if (KLCLASS(start->brd_type) == KLCLASS(brd_type))
return start;
start = KLCF_NEXT(start);
}
/* Didn't find it. */
return (lboard_t *)NULL;
}
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Ported from IRIX to Linux by Kanoj Sarcar, 06/08/00.
* Copyright 2000 - 2001 Silicon Graphics, Inc.
* Copyright 2000 - 2001 Kanoj Sarcar (kanoj@sgi.com)
*/
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/mmzone.h>
#include <linux/kernel.h>
#include <linux/nodemask.h>
#include <linux/string.h>
#include <asm/page.h>
#include <asm/sections.h>
#include <asm/sn/types.h>
#include <asm/sn/arch.h>
#include <asm/sn/gda.h>
#include <asm/sn/mapped_kernel.h>
#include "ip27-common.h"
static nodemask_t ktext_repmask;
/*
* XXX - This needs to be much smarter about where it puts copies of the
* kernel. For example, we should never put a copy on a headless node,
* and we should respect the topology of the machine.
*/
void __init setup_replication_mask(void)
{
/* Set only the master cnode's bit. The master cnode is always 0. */
nodes_clear(ktext_repmask);
node_set(0, ktext_repmask);
#ifdef CONFIG_REPLICATE_KTEXT
#ifndef CONFIG_MAPPED_KERNEL
#error Kernel replication works with mapped kernel support. No calias support.
#endif
{
nasid_t nasid;
for_each_online_node(nasid) {
if (nasid == 0)
continue;
/* Advertise that we have a copy of the kernel */
node_set(nasid, ktext_repmask);
}
}
#endif
/* Set up a GDA pointer to the replication mask. */
GDA->g_ktext_repmask = &ktext_repmask;
}
static __init void set_ktext_source(nasid_t client_nasid, nasid_t server_nasid)
{
kern_vars_t *kvp;
kvp = &hub_data(client_nasid)->kern_vars;
KERN_VARS_ADDR(client_nasid) = (unsigned long)kvp;
kvp->kv_magic = KV_MAGIC;
kvp->kv_ro_nasid = server_nasid;
kvp->kv_rw_nasid = master_nasid;
kvp->kv_ro_baseaddr = NODE_CAC_BASE(server_nasid);
kvp->kv_rw_baseaddr = NODE_CAC_BASE(master_nasid);
printk("REPLICATION: ON nasid %d, ktext from nasid %d, kdata from nasid %d\n", client_nasid, server_nasid, master_nasid);
}
/* XXX - When the BTE works, we should use it instead of this. */
static __init void copy_kernel(nasid_t dest_nasid)
{
unsigned long dest_kern_start, source_start, source_end, kern_size;
source_start = (unsigned long) _stext;
source_end = (unsigned long) _etext;
kern_size = source_end - source_start;
dest_kern_start = CHANGE_ADDR_NASID(MAPPED_KERN_RO_TO_K0(source_start),
dest_nasid);
memcpy((void *)dest_kern_start, (void *)source_start, kern_size);
}
void __init replicate_kernel_text(void)
{
nasid_t client_nasid;
nasid_t server_nasid;
server_nasid = master_nasid;
/* Record where the master node should get its kernel text */
set_ktext_source(master_nasid, master_nasid);
for_each_online_node(client_nasid) {
if (client_nasid == 0)
continue;
/* Check if this node should get a copy of the kernel */
if (node_isset(client_nasid, ktext_repmask)) {
server_nasid = client_nasid;
copy_kernel(server_nasid);
}
/* Record where this node should get its kernel text */
set_ktext_source(client_nasid, server_nasid);
}
}
/*
* Return pfn of first free page of memory on a node. PROM may allocate
* data structures on the first couple of pages of the first slot of each
* node. If this is the case, getfirstfree(node) > getslotstart(node, 0).
*/
unsigned long node_getfirstfree(nasid_t nasid)
{
unsigned long loadbase = REP_BASE;
unsigned long offset;
#ifdef CONFIG_MAPPED_KERNEL
loadbase += 16777216;
#endif
offset = PAGE_ALIGN((unsigned long)(&_end)) - loadbase;
if ((nasid == 0) || (node_isset(nasid, ktext_repmask)))
return TO_NODE(nasid, offset) >> PAGE_SHIFT;
else
return KDM_TO_PHYS(PAGE_ALIGN(SYMMON_STK_ADDR(nasid, 0))) >> PAGE_SHIFT;
}
@@ -0,0 +1,412 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000, 05 by Ralf Baechle (ralf@linux-mips.org)
* Copyright (C) 2000 by Silicon Graphics, Inc.
* Copyright (C) 2004 by Christoph Hellwig
*
* On SGI IP27 the ARC memory configuration data is completely bogus but
* alternate easier to use mechanisms are available.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/mmzone.h>
#include <linux/export.h>
#include <linux/nodemask.h>
#include <linux/swap.h>
#include <linux/pfn.h>
#include <linux/highmem.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/sections.h>
#include <asm/sgialib.h>
#include <asm/sn/arch.h>
#include <asm/sn/agent.h>
#include <asm/sn/klconfig.h>
#include "ip27-common.h"
#define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT)
#define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT)
struct node_data *__node_data[MAX_NUMNODES];
EXPORT_SYMBOL(__node_data);
static u64 gen_region_mask(void)
{
int region_shift;
u64 region_mask;
nasid_t nasid;
region_shift = get_region_shift();
region_mask = 0;
for_each_online_node(nasid)
region_mask |= BIT_ULL(nasid >> region_shift);
return region_mask;
}
#define rou_rflag rou_flags
static int router_distance;
static void router_recurse(klrou_t *router_a, klrou_t *router_b, int depth)
{
klrou_t *router;
lboard_t *brd;
int port;
if (router_a->rou_rflag == 1)
return;
if (depth >= router_distance)
return;
router_a->rou_rflag = 1;
for (port = 1; port <= MAX_ROUTER_PORTS; port++) {
if (router_a->rou_port[port].port_nasid == INVALID_NASID)
continue;
brd = (lboard_t *)NODE_OFFSET_TO_K0(
router_a->rou_port[port].port_nasid,
router_a->rou_port[port].port_offset);
if (brd->brd_type == KLTYPE_ROUTER) {
router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]);
if (router == router_b) {
if (depth < router_distance)
router_distance = depth;
}
else
router_recurse(router, router_b, depth + 1);
}
}
router_a->rou_rflag = 0;
}
unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
EXPORT_SYMBOL(__node_distances);
static int __init compute_node_distance(nasid_t nasid_a, nasid_t nasid_b)
{
klrou_t *router, *router_a = NULL, *router_b = NULL;
lboard_t *brd, *dest_brd;
nasid_t nasid;
int port;
/* Figure out which routers nodes in question are connected to */
for_each_online_node(nasid) {
brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
KLTYPE_ROUTER);
if (!brd)
continue;
do {
if (brd->brd_flags & DUPLICATE_BOARD)
continue;
router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]);
router->rou_rflag = 0;
for (port = 1; port <= MAX_ROUTER_PORTS; port++) {
if (router->rou_port[port].port_nasid == INVALID_NASID)
continue;
dest_brd = (lboard_t *)NODE_OFFSET_TO_K0(
router->rou_port[port].port_nasid,
router->rou_port[port].port_offset);
if (dest_brd->brd_type == KLTYPE_IP27) {
if (dest_brd->brd_nasid == nasid_a)
router_a = router;
if (dest_brd->brd_nasid == nasid_b)
router_b = router;
}
}
} while ((brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER)));
}
if (nasid_a == nasid_b)
return LOCAL_DISTANCE;
if (router_a == router_b)
return LOCAL_DISTANCE + 1;
if (router_a == NULL) {
pr_info("node_distance: router_a NULL\n");
return 255;
}
if (router_b == NULL) {
pr_info("node_distance: router_b NULL\n");
return 255;
}
router_distance = 100;
router_recurse(router_a, router_b, 2);
return LOCAL_DISTANCE + router_distance;
}
static void __init init_topology_matrix(void)
{
nasid_t row, col;
for (row = 0; row < MAX_NUMNODES; row++)
for (col = 0; col < MAX_NUMNODES; col++)
__node_distances[row][col] = -1;
for_each_online_node(row) {
for_each_online_node(col) {
__node_distances[row][col] =
compute_node_distance(row, col);
}
}
}
static void __init dump_topology(void)
{
nasid_t nasid;
lboard_t *brd, *dest_brd;
int port;
int router_num = 0;
klrou_t *router;
nasid_t row, col;
pr_info("************** Topology ********************\n");
pr_info(" ");
for_each_online_node(col)
pr_cont("%02d ", col);
pr_cont("\n");
for_each_online_node(row) {
pr_info("%02d ", row);
for_each_online_node(col)
pr_cont("%2d ", node_distance(row, col));
pr_cont("\n");
}
for_each_online_node(nasid) {
brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
KLTYPE_ROUTER);
if (!brd)
continue;
do {
if (brd->brd_flags & DUPLICATE_BOARD)
continue;
pr_cont("Router %d:", router_num);
router_num++;
router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]);
for (port = 1; port <= MAX_ROUTER_PORTS; port++) {
if (router->rou_port[port].port_nasid == INVALID_NASID)
continue;
dest_brd = (lboard_t *)NODE_OFFSET_TO_K0(
router->rou_port[port].port_nasid,
router->rou_port[port].port_offset);
if (dest_brd->brd_type == KLTYPE_IP27)
pr_cont(" %d", dest_brd->brd_nasid);
if (dest_brd->brd_type == KLTYPE_ROUTER)
pr_cont(" r");
}
pr_cont("\n");
} while ( (brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER)) );
}
}
static unsigned long __init slot_getbasepfn(nasid_t nasid, int slot)
{
return ((unsigned long)nasid << PFN_NASIDSHFT) | (slot << SLOT_PFNSHIFT);
}
static unsigned long __init slot_psize_compute(nasid_t nasid, int slot)
{
lboard_t *brd;
klmembnk_t *banks;
unsigned long size;
/* Find the node board */
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27);
if (!brd)
return 0;
/* Get the memory bank structure */
banks = (klmembnk_t *) find_first_component(brd, KLSTRUCT_MEMBNK);
if (!banks)
return 0;
/* Size in _Megabytes_ */
size = (unsigned long)banks->membnk_bnksz[slot/4];
/* hack for 128 dimm banks */
if (size <= 128) {
if (slot % 4 == 0) {
size <<= 20; /* size in bytes */
return size >> PAGE_SHIFT;
} else
return 0;
} else {
size /= 4;
size <<= 20;
return size >> PAGE_SHIFT;
}
}
static void __init mlreset(void)
{
u64 region_mask;
nasid_t nasid;
master_nasid = get_nasid();
/*
* Probe for all CPUs - this creates the cpumask and sets up the
* mapping tables. We need to do this as early as possible.
*/
#ifdef CONFIG_SMP
cpu_node_probe();
#endif
init_topology_matrix();
dump_topology();
region_mask = gen_region_mask();
setup_replication_mask();
/*
* Set all nodes' calias sizes to 8k
*/
for_each_online_node(nasid) {
/*
* Always have node 0 in the region mask, otherwise
* CALIAS accesses get exceptions since the hub
* thinks it is a node 0 address.
*/
REMOTE_HUB_S(nasid, PI_REGION_PRESENT, (region_mask | 1));
REMOTE_HUB_S(nasid, PI_CALIAS_SIZE, PI_CALIAS_SIZE_0);
#ifdef LATER
/*
* Set up all hubs to have a big window pointing at
* widget 0. Memory mode, widget 0, offset 0
*/
REMOTE_HUB_S(nasid, IIO_ITTE(SWIN0_BIGWIN),
((HUB_PIO_MAP_TO_MEM << IIO_ITTE_IOSP_SHIFT) |
(0 << IIO_ITTE_WIDGET_SHIFT)));
#endif
}
}
static void __init szmem(void)
{
unsigned long slot_psize, slot0sz = 0, nodebytes; /* Hack to detect problem configs */
int slot;
nasid_t node;
for_each_online_node(node) {
nodebytes = 0;
for (slot = 0; slot < MAX_MEM_SLOTS; slot++) {
slot_psize = slot_psize_compute(node, slot);
if (slot == 0)
slot0sz = slot_psize;
/*
* We need to refine the hack when we have replicated
* kernel text.
*/
nodebytes += (1LL << SLOT_SHIFT);
if (!slot_psize)
continue;
if ((nodebytes >> PAGE_SHIFT) * (sizeof(struct page)) >
(slot0sz << PAGE_SHIFT)) {
pr_info("Ignoring slot %d onwards on node %d\n",
slot, node);
slot = MAX_MEM_SLOTS;
continue;
}
memblock_add_node(PFN_PHYS(slot_getbasepfn(node, slot)),
PFN_PHYS(slot_psize), node,
MEMBLOCK_NONE);
}
}
}
static void __init node_mem_init(nasid_t node)
{
unsigned long slot_firstpfn = slot_getbasepfn(node, 0);
unsigned long slot_freepfn = node_getfirstfree(node);
unsigned long start_pfn, end_pfn;
get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
/*
* Allocate the node data structures on the node first.
*/
__node_data[node] = __va(slot_freepfn << PAGE_SHIFT);
memset(__node_data[node], 0, PAGE_SIZE);
node_data[node] = &__node_data[node]->pglist;
NODE_DATA(node)->node_start_pfn = start_pfn;
NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn;
cpumask_clear(&hub_data(node)->h_cpus);
slot_freepfn += PFN_UP(sizeof(struct pglist_data) +
sizeof(struct hub_data));
memblock_reserve(slot_firstpfn << PAGE_SHIFT,
((slot_freepfn - slot_firstpfn) << PAGE_SHIFT));
}
/*
* A node with nothing. We use it to avoid any special casing in
* cpumask_of_node
*/
static struct node_data null_node = {
.hub = {
.h_cpus = CPU_MASK_NONE
}
};
/*
* Currently, the intranode memory hole support assumes that each slot
* contains at least 32 MBytes of memory. We assume all bootmem data
* fits on the first slot.
*/
void __init prom_meminit(void)
{
nasid_t node;
mlreset();
szmem();
max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
for (node = 0; node < MAX_NUMNODES; node++) {
if (node_online(node)) {
node_mem_init(node);
continue;
}
__node_data[node] = &null_node;
}
}
void __init arch_zone_limits_init(unsigned long *max_zone_pfns)
{
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
}
+231
View File
@@ -0,0 +1,231 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/mmzone.h>
#include <linux/nodemask.h>
#include <linux/spinlock.h>
#include <linux/smp.h>
#include <linux/atomic.h>
#include <asm/sn/types.h>
#include <asm/sn/addrs.h>
#include <asm/sn/nmi.h>
#include <asm/sn/arch.h>
#include <asm/sn/agent.h>
#include "ip27-common.h"
#if 0
#define NODE_NUM_CPUS(n) CNODE_NUM_CPUS(n)
#else
#define NODE_NUM_CPUS(n) CPUS_PER_NODE
#endif
#define SEND_NMI(_nasid, _slice) \
REMOTE_HUB_S((_nasid), (PI_NMI_A + ((_slice) * PI_NMI_OFFSET)), 1)
typedef unsigned long machreg_t;
static arch_spinlock_t nmi_lock = __ARCH_SPIN_LOCK_UNLOCKED;
static void nmi_dump(void);
void install_cpu_nmi_handler(int slice)
{
nmi_t *nmi_addr;
nmi_addr = (nmi_t *)NMI_ADDR(get_nasid(), slice);
if (nmi_addr->call_addr)
return;
nmi_addr->magic = NMI_MAGIC;
nmi_addr->call_addr = (void *)nmi_dump;
nmi_addr->call_addr_c =
(void *)(~((unsigned long)(nmi_addr->call_addr)));
nmi_addr->call_parm = 0;
}
/*
* Copy the cpu registers which have been saved in the IP27prom format
* into the eframe format for the node under consideration.
*/
static void nmi_cpu_eframe_save(nasid_t nasid, int slice)
{
struct reg_struct *nr;
int i;
/* Get the pointer to the current cpu's register set. */
nr = (struct reg_struct *)
(TO_UNCAC(TO_NODE(nasid, IP27_NMI_KREGS_OFFSET)) +
slice * IP27_NMI_KREGS_CPU_SIZE);
pr_emerg("NMI nasid %d: slice %d\n", nasid, slice);
/*
* Saved main processor registers
*/
for (i = 0; i < 32; ) {
if ((i % 4) == 0)
pr_emerg("$%2d :", i);
pr_cont(" %016lx", nr->gpr[i]);
i++;
if ((i % 4) == 0)
pr_cont("\n");
}
pr_emerg("Hi : (value lost)\n");
pr_emerg("Lo : (value lost)\n");
/*
* Saved cp0 registers
*/
pr_emerg("epc : %016lx %pS\n", nr->epc, (void *)nr->epc);
pr_emerg("%s\n", print_tainted());
pr_emerg("ErrEPC: %016lx %pS\n", nr->error_epc, (void *)nr->error_epc);
pr_emerg("ra : %016lx %pS\n", nr->gpr[31], (void *)nr->gpr[31]);
pr_emerg("Status: %08lx ", nr->sr);
if (nr->sr & ST0_KX)
pr_cont("KX ");
if (nr->sr & ST0_SX)
pr_cont("SX ");
if (nr->sr & ST0_UX)
pr_cont("UX ");
switch (nr->sr & ST0_KSU) {
case KSU_USER:
pr_cont("USER ");
break;
case KSU_SUPERVISOR:
pr_cont("SUPERVISOR ");
break;
case KSU_KERNEL:
pr_cont("KERNEL ");
break;
default:
pr_cont("BAD_MODE ");
break;
}
if (nr->sr & ST0_ERL)
pr_cont("ERL ");
if (nr->sr & ST0_EXL)
pr_cont("EXL ");
if (nr->sr & ST0_IE)
pr_cont("IE ");
pr_cont("\n");
pr_emerg("Cause : %08lx\n", nr->cause);
pr_emerg("PrId : %08x\n", read_c0_prid());
pr_emerg("BadVA : %016lx\n", nr->badva);
pr_emerg("CErr : %016lx\n", nr->cache_err);
pr_emerg("NMI_SR: %016lx\n", nr->nmi_sr);
pr_emerg("\n");
}
static void nmi_dump_hub_irq(nasid_t nasid, int slice)
{
u64 mask0, mask1, pend0, pend1;
if (slice == 0) { /* Slice A */
mask0 = REMOTE_HUB_L(nasid, PI_INT_MASK0_A);
mask1 = REMOTE_HUB_L(nasid, PI_INT_MASK1_A);
} else { /* Slice B */
mask0 = REMOTE_HUB_L(nasid, PI_INT_MASK0_B);
mask1 = REMOTE_HUB_L(nasid, PI_INT_MASK1_B);
}
pend0 = REMOTE_HUB_L(nasid, PI_INT_PEND0);
pend1 = REMOTE_HUB_L(nasid, PI_INT_PEND1);
pr_emerg("PI_INT_MASK0: %16llx PI_INT_MASK1: %16llx\n", mask0, mask1);
pr_emerg("PI_INT_PEND0: %16llx PI_INT_PEND1: %16llx\n", pend0, pend1);
pr_emerg("\n\n");
}
/*
* Copy the cpu registers which have been saved in the IP27prom format
* into the eframe format for the node under consideration.
*/
static void nmi_node_eframe_save(nasid_t nasid)
{
int slice;
if (nasid == INVALID_NASID)
return;
/* Save the registers into eframe for each cpu */
for (slice = 0; slice < NODE_NUM_CPUS(slice); slice++) {
nmi_cpu_eframe_save(nasid, slice);
nmi_dump_hub_irq(nasid, slice);
}
}
/*
* Save the nmi cpu registers for all cpus in the system.
*/
static void nmi_eframes_save(void)
{
nasid_t nasid;
for_each_online_node(nasid)
nmi_node_eframe_save(nasid);
}
static void nmi_dump(void)
{
#ifndef REAL_NMI_SIGNAL
static atomic_t nmied_cpus = ATOMIC_INIT(0);
atomic_inc(&nmied_cpus);
#endif
/*
* Only allow 1 cpu to proceed
*/
arch_spin_lock(&nmi_lock);
#ifdef REAL_NMI_SIGNAL
/*
* Wait up to 15 seconds for the other cpus to respond to the NMI.
* If a cpu has not responded after 10 sec, send it 1 additional NMI.
* This is for 2 reasons:
* - sometimes a MMSC fail to NMI all cpus.
* - on 512p SN0 system, the MMSC will only send NMIs to
* half the cpus. Unfortunately, we don't know which cpus may be
* NMIed - it depends on how the site chooses to configure.
*
* Note: it has been measure that it takes the MMSC up to 2.3 secs to
* send NMIs to all cpus on a 256p system.
*/
for (i=0; i < 1500; i++) {
for_each_online_node(node)
if (NODEPDA(node)->dump_count == 0)
break;
if (node == MAX_NUMNODES)
break;
if (i == 1000) {
for_each_online_node(node)
if (NODEPDA(node)->dump_count == 0) {
cpu = cpumask_first(cpumask_of_node(node));
for (n=0; n < CNODE_NUM_CPUS(node); cpu++, n++) {
CPUMASK_SETB(nmied_cpus, cpu);
/*
* cputonasid, cputoslice
* needs kernel cpuid
*/
SEND_NMI((cputonasid(cpu)), (cputoslice(cpu)));
}
}
}
udelay(10000);
}
#else
while (atomic_read(&nmied_cpus) != num_online_cpus());
#endif
/*
* Save the nmi cpu registers for all cpu in the eframe format.
*/
nmi_eframes_save();
LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
}
@@ -0,0 +1,81 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Reset an IP27.
*
* Copyright (C) 1997, 1998, 1999, 2000, 06 by Ralf Baechle
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
*/
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/smp.h>
#include <linux/mmzone.h>
#include <linux/nodemask.h>
#include <linux/pm.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/reboot.h>
#include <asm/sgialib.h>
#include <asm/sn/addrs.h>
#include <asm/sn/agent.h>
#include <asm/sn/arch.h>
#include <asm/sn/gda.h>
#include "ip27-common.h"
void machine_restart(char *command) __noreturn;
void machine_halt(void) __noreturn;
void machine_power_off(void) __noreturn;
#define noreturn while(1); /* Silence gcc. */
/* XXX How to pass the reboot command to the firmware??? */
static void ip27_machine_restart(char *command)
{
#if 0
int i;
#endif
printk("Reboot started from CPU %d\n", smp_processor_id());
#ifdef CONFIG_SMP
smp_send_stop();
#endif
#if 0
for_each_online_node(i)
REMOTE_HUB_S(i, PROMOP_REG, PROMOP_REBOOT);
#else
LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
#endif
noreturn;
}
static void ip27_machine_halt(void)
{
int i;
#ifdef CONFIG_SMP
smp_send_stop();
#endif
for_each_online_node(i)
REMOTE_HUB_S(i, PROMOP_REG, PROMOP_RESTART);
LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
noreturn;
}
static void ip27_machine_power_off(void)
{
/* To do ... */
noreturn;
}
void ip27_reboot_setup(void)
{
_machine_restart = ip27_machine_restart;
_machine_halt = ip27_machine_halt;
pm_power_off = ip27_machine_power_off;
}
+191
View File
@@ -0,0 +1,191 @@
/*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* Copyright (C) 2000 - 2001 by Kanoj Sarcar (kanoj@sgi.com)
* Copyright (C) 2000 - 2001 by Silicon Graphics, Inc.
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/topology.h>
#include <linux/nodemask.h>
#include <asm/page.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/sn/agent.h>
#include <asm/sn/arch.h>
#include <asm/sn/gda.h>
#include <asm/sn/intr.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/launch.h>
#include <asm/sn/mapped_kernel.h>
#include <asm/sn/types.h>
#include "ip27-common.h"
static int node_scan_cpus(nasid_t nasid, int highest)
{
static int cpus_found;
lboard_t *brd;
klcpu_t *acpu;
cpuid_t cpuid;
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27);
do {
acpu = (klcpu_t *)find_first_component(brd, KLSTRUCT_CPU);
while (acpu) {
cpuid = acpu->cpu_info.virtid;
/* Only let it join in if it's marked enabled */
if ((acpu->cpu_info.flags & KLINFO_ENABLE) &&
(cpus_found != NR_CPUS)) {
if (cpuid > highest)
highest = cpuid;
set_cpu_possible(cpuid, true);
cputonasid(cpus_found) = nasid;
cputoslice(cpus_found) = acpu->cpu_info.physid;
sn_cpu_info[cpus_found].p_speed =
acpu->cpu_speed;
cpus_found++;
}
acpu = (klcpu_t *)find_component(brd, (klinfo_t *)acpu,
KLSTRUCT_CPU);
}
brd = KLCF_NEXT(brd);
if (!brd)
break;
brd = find_lboard(brd, KLTYPE_IP27);
} while (brd);
return highest;
}
void cpu_node_probe(void)
{
int i, highest = 0;
gda_t *gdap = GDA;
nodes_clear(node_online_map);
nodes_clear(node_possible_map);
for (i = 0; i < MAX_NUMNODES; i++) {
nasid_t nasid = gdap->g_nasidtable[i];
if (nasid == INVALID_NASID)
break;
node_set_online(nasid);
node_set(nasid, node_possible_map);
highest = node_scan_cpus(nasid, highest);
}
printk("Discovered %d cpus on %d nodes\n", highest + 1, num_online_nodes());
}
static __init void intr_clear_all(nasid_t nasid)
{
int i;
REMOTE_HUB_S(nasid, PI_INT_MASK0_A, 0);
REMOTE_HUB_S(nasid, PI_INT_MASK0_B, 0);
REMOTE_HUB_S(nasid, PI_INT_MASK1_A, 0);
REMOTE_HUB_S(nasid, PI_INT_MASK1_B, 0);
for (i = 0; i < 128; i++)
REMOTE_HUB_CLR_INTR(nasid, i);
}
static void ip27_send_ipi_single(int destid, unsigned int action)
{
int irq;
switch (action) {
case SMP_RESCHEDULE_YOURSELF:
irq = CPU_RESCHED_A_IRQ;
break;
case SMP_CALL_FUNCTION:
irq = CPU_CALL_A_IRQ;
break;
default:
panic("sendintr");
}
irq += cputoslice(destid);
/*
* Set the interrupt bit associated with the CPU we want to
* send the interrupt to.
*/
REMOTE_HUB_SEND_INTR(cpu_to_node(destid), irq);
}
static void ip27_send_ipi_mask(const struct cpumask *mask, unsigned int action)
{
unsigned int i;
for_each_cpu(i, mask)
ip27_send_ipi_single(i, action);
}
static void ip27_init_cpu(void)
{
per_cpu_init();
}
static void ip27_smp_finish(void)
{
hub_rt_clock_event_init();
local_irq_enable();
}
/*
* Launch a slave into smp_bootstrap(). It doesn't take an argument, and we
* set sp to the kernel stack of the newly created idle process, gp to the proc
* struct so that current_thread_info() will work.
*/
static int ip27_boot_secondary(int cpu, struct task_struct *idle)
{
unsigned long gp = (unsigned long)task_thread_info(idle);
unsigned long sp = __KSTK_TOS(idle);
LAUNCH_SLAVE(cputonasid(cpu), cputoslice(cpu),
(launch_proc_t)MAPPED_KERN_RW_TO_K0(smp_bootstrap),
0, (void *) sp, (void *) gp);
return 0;
}
static void __init ip27_smp_setup(void)
{
nasid_t nasid;
for_each_online_node(nasid) {
if (nasid == 0)
continue;
intr_clear_all(nasid);
}
replicate_kernel_text();
/*
* PROM sets up system, that boot cpu is always first CPU on nasid 0
*/
cputonasid(0) = 0;
cputoslice(0) = LOCAL_HUB_L(PI_CPU_NUM);
}
static void __init ip27_prepare_cpus(unsigned int max_cpus)
{
/* We already did everything necessary earlier */
}
const struct plat_smp_ops ip27_smp_ops = {
.send_ipi_single = ip27_send_ipi_single,
.send_ipi_mask = ip27_send_ipi_mask,
.init_secondary = ip27_init_cpu,
.smp_finish = ip27_smp_finish,
.boot_secondary = ip27_boot_secondary,
.smp_setup = ip27_smp_setup,
.prepare_cpus = ip27_prepare_cpus,
.prepare_boot_cpu = ip27_init_cpu,
};
+154
View File
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 1999, 2000, 05, 06 Ralf Baechle (ralf@linux-mips.org)
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
*/
#include <linux/bcd.h>
#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched_clock.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/param.h>
#include <linux/smp.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/mm.h>
#include <linux/platform_device.h>
#include <asm/time.h>
#include <asm/sgialib.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/arch.h>
#include <asm/sn/addrs.h>
#include <asm/sn/agent.h>
#include "ip27-common.h"
static int rt_next_event(unsigned long delta, struct clock_event_device *evt)
{
unsigned int cpu = smp_processor_id();
int slice = cputoslice(cpu);
unsigned long cnt;
cnt = LOCAL_HUB_L(PI_RT_COUNT);
cnt += delta;
LOCAL_HUB_S(PI_RT_COMPARE_A + PI_COUNT_OFFSET * slice, cnt);
return LOCAL_HUB_L(PI_RT_COUNT) >= cnt ? -ETIME : 0;
}
static DEFINE_PER_CPU(struct clock_event_device, hub_rt_clockevent);
static DEFINE_PER_CPU(char [11], hub_rt_name);
static irqreturn_t hub_rt_counter_handler(int irq, void *dev_id)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu);
int slice = cputoslice(cpu);
/*
* Ack
*/
LOCAL_HUB_S(PI_RT_PEND_A + PI_COUNT_OFFSET * slice, 0);
cd->event_handler(cd);
return IRQ_HANDLED;
}
/*
* This is a hack; we really need to figure these values out dynamically
*
* Since 800 ns works very well with various HUB frequencies, such as
* 360, 380, 390 and 400 MHZ, we use 800 ns rtc cycle time.
*
* Ralf: which clock rate is used to feed the counter?
*/
#define NSEC_PER_CYCLE 800
#define CYCLES_PER_SEC (NSEC_PER_SEC / NSEC_PER_CYCLE)
void hub_rt_clock_event_init(void)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu);
unsigned char *name = per_cpu(hub_rt_name, cpu);
sprintf(name, "hub-rt %d", cpu);
cd->name = name;
cd->features = CLOCK_EVT_FEAT_ONESHOT;
clockevent_set_clock(cd, CYCLES_PER_SEC);
cd->max_delta_ns = clockevent_delta2ns(0xfffffffffffff, cd);
cd->max_delta_ticks = 0xfffffffffffff;
cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
cd->min_delta_ticks = 0x300;
cd->rating = 200;
cd->irq = IP27_RT_TIMER_IRQ;
cd->cpumask = cpumask_of(cpu);
cd->set_next_event = rt_next_event;
clockevents_register_device(cd);
enable_percpu_irq(IP27_RT_TIMER_IRQ, IRQ_TYPE_NONE);
}
static void __init hub_rt_clock_event_global_init(void)
{
irq_set_handler(IP27_RT_TIMER_IRQ, handle_percpu_devid_irq);
irq_set_percpu_devid(IP27_RT_TIMER_IRQ);
WARN_ON(request_percpu_irq(IP27_RT_TIMER_IRQ, hub_rt_counter_handler,
"hub-rt", &hub_rt_clockevent));
}
static u64 hub_rt_read(struct clocksource *cs)
{
return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
}
struct clocksource hub_rt_clocksource = {
.name = "HUB-RT",
.rating = 200,
.read = hub_rt_read,
.mask = CLOCKSOURCE_MASK(52),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static u64 notrace hub_rt_read_sched_clock(void)
{
return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
}
static void __init hub_rt_clocksource_init(void)
{
struct clocksource *cs = &hub_rt_clocksource;
clocksource_register_hz(cs, CYCLES_PER_SEC);
sched_clock_register(hub_rt_read_sched_clock, 52, CYCLES_PER_SEC);
}
void __init plat_time_init(void)
{
hub_rt_clocksource_init();
hub_rt_clock_event_global_init();
hub_rt_clock_event_init();
}
void hub_rtc_init(nasid_t nasid)
{
/*
* We only need to initialize the current node.
* If this is not the current node then it is a cpuless
* node and timeouts will not happen there.
*/
if (get_nasid() == nasid) {
LOCAL_HUB_S(PI_RT_EN_A, 1);
LOCAL_HUB_S(PI_RT_EN_B, 1);
LOCAL_HUB_S(PI_PROF_EN_A, 0);
LOCAL_HUB_S(PI_PROF_EN_B, 0);
LOCAL_HUB_S(PI_RT_COUNT, 0);
LOCAL_HUB_S(PI_RT_PEND_A, 0);
LOCAL_HUB_S(PI_RT_PEND_B, 0);
}
}
+242
View File
@@ -0,0 +1,242 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
* Copyright (C) 1999, 2000 Silcon Graphics, Inc.
* Copyright (C) 2004 Christoph Hellwig.
*
* Generic XTALK initialization code
*/
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/platform_device.h>
#include <linux/platform_data/sgi-w1.h>
#include <linux/platform_data/xtalk-bridge.h>
#include <asm/sn/addrs.h>
#include <asm/sn/types.h>
#include <asm/sn/klconfig.h>
#include <asm/pci/bridge.h>
#include <asm/xtalk/xtalk.h>
#define XBOW_WIDGET_PART_NUM 0x0
#define XXBOW_WIDGET_PART_NUM 0xd000 /* Xbow in Xbridge */
#define BASE_XBOW_PORT 8 /* Lowest external port */
static void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
{
struct xtalk_bridge_platform_data *bd;
struct sgi_w1_platform_data *wd;
struct platform_device *pdev_wd;
struct platform_device *pdev_bd;
struct resource w1_res;
unsigned long offset;
offset = NODE_OFFSET(nasid);
wd = kzalloc_obj(*wd);
if (!wd) {
pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
return;
}
snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx",
offset + (widget << SWIN_SIZE_BITS));
memset(&w1_res, 0, sizeof(w1_res));
w1_res.start = offset + (widget << SWIN_SIZE_BITS) +
offsetof(struct bridge_regs, b_nic);
w1_res.end = w1_res.start + 3;
w1_res.flags = IORESOURCE_MEM;
pdev_wd = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO);
if (!pdev_wd) {
pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
goto err_kfree_wd;
}
if (platform_device_add_resources(pdev_wd, &w1_res, 1)) {
pr_warn("xtalk:n%d/%x bridge failed to add platform resources.\n", nasid, widget);
goto err_put_pdev_wd;
}
if (platform_device_add_data(pdev_wd, wd, sizeof(*wd))) {
pr_warn("xtalk:n%d/%x bridge failed to add platform data.\n", nasid, widget);
goto err_put_pdev_wd;
}
if (platform_device_add(pdev_wd)) {
pr_warn("xtalk:n%d/%x bridge failed to add platform device.\n", nasid, widget);
goto err_put_pdev_wd;
}
/* platform_device_add_data() duplicates the data */
kfree(wd);
bd = kzalloc_obj(*bd);
if (!bd) {
pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
goto err_unregister_pdev_wd;
}
pdev_bd = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO);
if (!pdev_bd) {
pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
goto err_kfree_bd;
}
bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget);
bd->intr_addr = BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD;
bd->nasid = nasid;
bd->masterwid = masterwid;
bd->mem.name = "Bridge PCI MEM";
bd->mem.start = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
bd->mem.end = offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
bd->mem.flags = IORESOURCE_MEM;
bd->mem_offset = offset;
bd->io.name = "Bridge PCI IO";
bd->io.start = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
bd->io.end = offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
bd->io.flags = IORESOURCE_IO;
bd->io_offset = offset;
if (platform_device_add_data(pdev_bd, bd, sizeof(*bd))) {
pr_warn("xtalk:n%d/%x bridge failed to add platform data.\n", nasid, widget);
goto err_put_pdev_bd;
}
if (platform_device_add(pdev_bd)) {
pr_warn("xtalk:n%d/%x bridge failed to add platform device.\n", nasid, widget);
goto err_put_pdev_bd;
}
/* platform_device_add_data() duplicates the data */
kfree(bd);
pr_info("xtalk:n%d/%x bridge widget\n", nasid, widget);
return;
err_put_pdev_bd:
platform_device_put(pdev_bd);
err_kfree_bd:
kfree(bd);
err_unregister_pdev_wd:
platform_device_unregister(pdev_wd);
return;
err_put_pdev_wd:
platform_device_put(pdev_wd);
err_kfree_wd:
kfree(wd);
return;
}
static int probe_one_port(nasid_t nasid, int widget, int masterwid)
{
widgetreg_t widget_id;
xwidget_part_num_t partnum;
widget_id = *(volatile widgetreg_t *)
(RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID);
partnum = XWIDGET_PART_NUM(widget_id);
switch (partnum) {
case BRIDGE_WIDGET_PART_NUM:
case XBRIDGE_WIDGET_PART_NUM:
bridge_platform_create(nasid, widget, masterwid);
break;
default:
pr_info("xtalk:n%d/%d unknown widget (0x%x)\n",
nasid, widget, partnum);
break;
}
return 0;
}
static int xbow_probe(nasid_t nasid)
{
lboard_t *brd;
klxbow_t *xbow_p;
unsigned masterwid, i;
/*
* found xbow, so may have multiple bridges
* need to probe xbow
*/
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8);
if (!brd)
return -ENODEV;
xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW);
if (!xbow_p)
return -ENODEV;
/*
* Okay, here's a xbow. Let's arbitrate and find
* out if we should initialize it. Set enabled
* hub connected at highest or lowest widget as
* master.
*/
#ifdef WIDGET_A
i = HUB_WIDGET_ID_MAX + 1;
do {
i--;
} while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) ||
(!XBOW_PORT_IS_ENABLED(xbow_p, i)));
#else
i = HUB_WIDGET_ID_MIN - 1;
do {
i++;
} while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) ||
(!XBOW_PORT_IS_ENABLED(xbow_p, i)));
#endif
masterwid = i;
if (nasid != XBOW_PORT_NASID(xbow_p, i))
return 1;
for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) {
if (XBOW_PORT_IS_ENABLED(xbow_p, i) &&
XBOW_PORT_TYPE_IO(xbow_p, i))
probe_one_port(nasid, i, masterwid);
}
return 0;
}
static void xtalk_probe_node(nasid_t nasid)
{
volatile u64 hubreg;
xwidget_part_num_t partnum;
widgetreg_t widget_id;
hubreg = REMOTE_HUB_L(nasid, IIO_LLP_CSR);
/* check whether the link is up */
if (!(hubreg & IIO_LLP_CSR_IS_UP))
return;
widget_id = *(volatile widgetreg_t *)
(RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID);
partnum = XWIDGET_PART_NUM(widget_id);
switch (partnum) {
case BRIDGE_WIDGET_PART_NUM:
bridge_platform_create(nasid, 0x8, 0xa);
break;
case XBOW_WIDGET_PART_NUM:
case XXBOW_WIDGET_PART_NUM:
pr_info("xtalk:n%d/0 xbow widget\n", nasid);
xbow_probe(nasid);
break;
default:
pr_info("xtalk:n%d/0 unknown widget (0x%x)\n", nasid, partnum);
break;
}
}
static int __init xtalk_init(void)
{
nasid_t nasid;
for_each_online_node(nasid)
xtalk_probe_node(nasid);
return 0;
}
arch_initcall(xtalk_init);