Initial commit

This commit is contained in:
Ole André Vadla Ravnås 2022-05-07 01:01:45 +02:00
commit 169c65d57e
51358 changed files with 23120455 additions and 0 deletions

View file

@ -0,0 +1,15 @@
# arch/ia64/sn/kernel/sn2/Makefile
#
# 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) 1999,2001-2002 Silicon Graphics, Inc. All rights reserved.
#
# sn2 specific kernel files
#
ccflags-y := -Iarch/ia64/sn/include
obj-y += cache.o io.o ptc_deadlock.o sn2_smp.o sn_proc_fs.o \
prominfo_proc.o timer.o timer_interrupt.o sn_hwperf.o

View file

@ -0,0 +1,41 @@
/*
* 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-2003, 2006 Silicon Graphics, Inc. All rights reserved.
*
*/
#include <linux/module.h>
#include <asm/pgalloc.h>
#include <asm/sn/arch.h>
/**
* sn_flush_all_caches - flush a range of address from all caches (incl. L4)
* @flush_addr: identity mapped region 7 address to start flushing
* @bytes: number of bytes to flush
*
* Flush a range of addresses from all caches including L4.
* All addresses fully or partially contained within
* @flush_addr to @flush_addr + @bytes are flushed
* from all caches.
*/
void
sn_flush_all_caches(long flush_addr, long bytes)
{
unsigned long addr = flush_addr;
/* SHub1 requires a cached address */
if (is_shub1() && (addr & RGN_BITS) == RGN_BASE(RGN_UNCACHED))
addr = (addr - RGN_BASE(RGN_UNCACHED)) + RGN_BASE(RGN_KERNEL);
flush_icache_range(addr, addr + bytes);
/*
* The last call may have returned before the caches
* were actually flushed, so we call it again to make
* sure.
*/
flush_icache_range(addr, addr + bytes);
mb();
}
EXPORT_SYMBOL(sn_flush_all_caches);

View file

@ -0,0 +1,101 @@
/*
* 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) 2003 Silicon Graphics, Inc. All rights reserved.
*
* The generic kernel requires function pointers to these routines, so
* we wrap the inlines from asm/ia64/sn/sn2/io.h here.
*/
#include <asm/sn/io.h>
#ifdef CONFIG_IA64_GENERIC
#undef __sn_inb
#undef __sn_inw
#undef __sn_inl
#undef __sn_outb
#undef __sn_outw
#undef __sn_outl
#undef __sn_readb
#undef __sn_readw
#undef __sn_readl
#undef __sn_readq
#undef __sn_readb_relaxed
#undef __sn_readw_relaxed
#undef __sn_readl_relaxed
#undef __sn_readq_relaxed
unsigned int __sn_inb(unsigned long port)
{
return ___sn_inb(port);
}
unsigned int __sn_inw(unsigned long port)
{
return ___sn_inw(port);
}
unsigned int __sn_inl(unsigned long port)
{
return ___sn_inl(port);
}
void __sn_outb(unsigned char val, unsigned long port)
{
___sn_outb(val, port);
}
void __sn_outw(unsigned short val, unsigned long port)
{
___sn_outw(val, port);
}
void __sn_outl(unsigned int val, unsigned long port)
{
___sn_outl(val, port);
}
unsigned char __sn_readb(void __iomem *addr)
{
return ___sn_readb(addr);
}
unsigned short __sn_readw(void __iomem *addr)
{
return ___sn_readw(addr);
}
unsigned int __sn_readl(void __iomem *addr)
{
return ___sn_readl(addr);
}
unsigned long __sn_readq(void __iomem *addr)
{
return ___sn_readq(addr);
}
unsigned char __sn_readb_relaxed(void __iomem *addr)
{
return ___sn_readb_relaxed(addr);
}
unsigned short __sn_readw_relaxed(void __iomem *addr)
{
return ___sn_readw_relaxed(addr);
}
unsigned int __sn_readl_relaxed(void __iomem *addr)
{
return ___sn_readl_relaxed(addr);
}
unsigned long __sn_readq_relaxed(void __iomem *addr)
{
return ___sn_readq_relaxed(addr);
}
#endif

View file

@ -0,0 +1,231 @@
/*
* 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) 1999,2001-2004, 2006 Silicon Graphics, Inc. All Rights Reserved.
*
* Module to export the system's Firmware Interface Tables, including
* PROM revision numbers and banners, in /proc
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/nodemask.h>
#include <asm/io.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/addrs.h>
MODULE_DESCRIPTION("PROM version reporting for /proc");
MODULE_AUTHOR("Chad Talbott");
MODULE_LICENSE("GPL");
/* Standard Intel FIT entry types */
#define FIT_ENTRY_FIT_HEADER 0x00 /* FIT header entry */
#define FIT_ENTRY_PAL_B 0x01 /* PAL_B entry */
/* Entries 0x02 through 0x0D reserved by Intel */
#define FIT_ENTRY_PAL_A_PROC 0x0E /* Processor-specific PAL_A entry */
#define FIT_ENTRY_PAL_A 0x0F /* PAL_A entry, same as... */
#define FIT_ENTRY_PAL_A_GEN 0x0F /* ...Generic PAL_A entry */
#define FIT_ENTRY_UNUSED 0x7F /* Unused (reserved by Intel?) */
/* OEM-defined entries range from 0x10 to 0x7E. */
#define FIT_ENTRY_SAL_A 0x10 /* SAL_A entry */
#define FIT_ENTRY_SAL_B 0x11 /* SAL_B entry */
#define FIT_ENTRY_SALRUNTIME 0x12 /* SAL runtime entry */
#define FIT_ENTRY_EFI 0x1F /* EFI entry */
#define FIT_ENTRY_FPSWA 0x20 /* embedded fpswa entry */
#define FIT_ENTRY_VMLINUX 0x21 /* embedded vmlinux entry */
#define FIT_MAJOR_SHIFT (32 + 8)
#define FIT_MAJOR_MASK ((1 << 8) - 1)
#define FIT_MINOR_SHIFT 32
#define FIT_MINOR_MASK ((1 << 8) - 1)
#define FIT_MAJOR(q) \
((unsigned) ((q) >> FIT_MAJOR_SHIFT) & FIT_MAJOR_MASK)
#define FIT_MINOR(q) \
((unsigned) ((q) >> FIT_MINOR_SHIFT) & FIT_MINOR_MASK)
#define FIT_TYPE_SHIFT (32 + 16)
#define FIT_TYPE_MASK ((1 << 7) - 1)
#define FIT_TYPE(q) \
((unsigned) ((q) >> FIT_TYPE_SHIFT) & FIT_TYPE_MASK)
struct fit_type_map_t {
unsigned char type;
const char *name;
};
static const struct fit_type_map_t fit_entry_types[] = {
{FIT_ENTRY_FIT_HEADER, "FIT Header"},
{FIT_ENTRY_PAL_A_GEN, "Generic PAL_A"},
{FIT_ENTRY_PAL_A_PROC, "Processor-specific PAL_A"},
{FIT_ENTRY_PAL_A, "PAL_A"},
{FIT_ENTRY_PAL_B, "PAL_B"},
{FIT_ENTRY_SAL_A, "SAL_A"},
{FIT_ENTRY_SAL_B, "SAL_B"},
{FIT_ENTRY_SALRUNTIME, "SAL runtime"},
{FIT_ENTRY_EFI, "EFI"},
{FIT_ENTRY_VMLINUX, "Embedded Linux"},
{FIT_ENTRY_FPSWA, "Embedded FPSWA"},
{FIT_ENTRY_UNUSED, "Unused"},
{0xff, "Error"},
};
static const char *fit_type_name(unsigned char type)
{
struct fit_type_map_t const *mapp;
for (mapp = fit_entry_types; mapp->type != 0xff; mapp++)
if (type == mapp->type)
return mapp->name;
if ((type > FIT_ENTRY_PAL_A) && (type < FIT_ENTRY_UNUSED))
return "OEM type";
if ((type > FIT_ENTRY_PAL_B) && (type < FIT_ENTRY_PAL_A))
return "Reserved";
return "Unknown type";
}
static int
get_fit_entry(unsigned long nasid, int index, unsigned long *fentry,
char *banner, int banlen)
{
return ia64_sn_get_fit_compt(nasid, index, fentry, banner, banlen);
}
/*
* These two routines display the FIT table for each node.
*/
static void dump_fit_entry(struct seq_file *m, unsigned long *fentry)
{
unsigned type;
type = FIT_TYPE(fentry[1]);
seq_printf(m, "%02x %-25s %x.%02x %016lx %u\n",
type,
fit_type_name(type),
FIT_MAJOR(fentry[1]), FIT_MINOR(fentry[1]),
fentry[0],
/* mult by sixteen to get size in bytes */
(unsigned)(fentry[1] & 0xffffff) * 16);
}
/*
* We assume that the fit table will be small enough that we can print
* the whole thing into one page. (This is true for our default 16kB
* pages -- each entry is about 60 chars wide when printed.) I read
* somewhere that the maximum size of the FIT is 128 entries, so we're
* OK except for 4kB pages (and no one is going to do that on SN
* anyway).
*/
static int proc_fit_show(struct seq_file *m, void *v)
{
unsigned long nasid = (unsigned long)m->private;
unsigned long fentry[2];
int index;
for (index=0;;index++) {
BUG_ON(index * 60 > PAGE_SIZE);
if (get_fit_entry(nasid, index, fentry, NULL, 0))
break;
dump_fit_entry(m, fentry);
}
return 0;
}
static int proc_fit_open(struct inode *inode, struct file *file)
{
return single_open(file, proc_fit_show, PDE_DATA(inode));
}
static const struct file_operations proc_fit_fops = {
.open = proc_fit_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int proc_version_show(struct seq_file *m, void *v)
{
unsigned long nasid = (unsigned long)m->private;
unsigned long fentry[2];
char banner[128];
int index;
for (index = 0; ; index++) {
if (get_fit_entry(nasid, index, fentry, banner,
sizeof(banner)))
return 0;
if (FIT_TYPE(fentry[1]) == FIT_ENTRY_SAL_A)
break;
}
seq_printf(m, "%x.%02x\n", FIT_MAJOR(fentry[1]), FIT_MINOR(fentry[1]));
if (banner[0])
seq_printf(m, "%s\n", banner);
return 0;
}
static int proc_version_open(struct inode *inode, struct file *file)
{
return single_open(file, proc_version_show, PDE_DATA(inode));
}
static const struct file_operations proc_version_fops = {
.open = proc_version_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/* module entry points */
int __init prominfo_init(void);
void __exit prominfo_exit(void);
module_init(prominfo_init);
module_exit(prominfo_exit);
#define NODE_NAME_LEN 11
int __init prominfo_init(void)
{
struct proc_dir_entry *sgi_prominfo_entry;
cnodeid_t cnodeid;
if (!ia64_platform_is("sn2"))
return 0;
sgi_prominfo_entry = proc_mkdir("sgi_prominfo", NULL);
if (!sgi_prominfo_entry)
return -ENOMEM;
for_each_online_node(cnodeid) {
struct proc_dir_entry *dir;
unsigned long nasid;
char name[NODE_NAME_LEN];
sprintf(name, "node%d", cnodeid);
dir = proc_mkdir(name, sgi_prominfo_entry);
if (!dir)
continue;
nasid = cnodeid_to_nasid(cnodeid);
proc_create_data("fit", 0, dir,
&proc_fit_fops, (void *)nasid);
proc_create_data("version", 0, dir,
&proc_version_fops, (void *)nasid);
}
return 0;
}
void __exit prominfo_exit(void)
{
remove_proc_subtree("sgi_prominfo", NULL);
}

View file

@ -0,0 +1,92 @@
/*
* 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-2005 Silicon Graphics, Inc. All rights reserved.
*/
#include <asm/types.h>
#include <asm/sn/shub_mmr.h>
#define DEADLOCKBIT SH_PIO_WRITE_STATUS_WRITE_DEADLOCK_SHFT
#define WRITECOUNTMASK SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK
#define ALIAS_OFFSET 8
.global sn2_ptc_deadlock_recovery_core
.proc sn2_ptc_deadlock_recovery_core
sn2_ptc_deadlock_recovery_core:
.regstk 6,0,0,0
ptc0 = in0
data0 = in1
ptc1 = in2
data1 = in3
piowc = in4
zeroval = in5
piowcphy = r30
psrsave = r2
scr1 = r16
scr2 = r17
mask = r18
extr.u piowcphy=piowc,0,61;; // Convert piowc to uncached physical address
dep piowcphy=-1,piowcphy,63,1
movl mask=WRITECOUNTMASK
mov r8=r0
1:
cmp.ne p8,p9=r0,ptc1 // Test for shub type (ptc1 non-null on shub1)
// p8 = 1 if shub1, p9 = 1 if shub2
add scr2=ALIAS_OFFSET,piowc // Address of WRITE_STATUS alias register
mov scr1=7;; // Clear DEADLOCK, WRITE_ERROR, MULTI_WRITE_ERROR
(p8) st8.rel [scr2]=scr1;;
(p9) ld8.acq scr1=[scr2];;
5: ld8.acq scr1=[piowc];; // Wait for PIOs to complete.
hint @pause
and scr2=scr1,mask;; // mask of writecount bits
cmp.ne p6,p0=zeroval,scr2
(p6) br.cond.sptk 5b
////////////// BEGIN PHYSICAL MODE ////////////////////
mov psrsave=psr // Disable IC (no PMIs)
rsm psr.i | psr.dt | psr.ic;;
srlz.i;;
st8.rel [ptc0]=data0 // Write PTC0 & wait for completion.
5: ld8.acq scr1=[piowcphy];; // Wait for PIOs to complete.
hint @pause
and scr2=scr1,mask;; // mask of writecount bits
cmp.ne p6,p0=zeroval,scr2
(p6) br.cond.sptk 5b;;
tbit.nz p8,p7=scr1,DEADLOCKBIT;;// Test for DEADLOCK
(p7) cmp.ne p7,p0=r0,ptc1;; // Test for non-null ptc1
(p7) st8.rel [ptc1]=data1;; // Now write PTC1.
5: ld8.acq scr1=[piowcphy];; // Wait for PIOs to complete.
hint @pause
and scr2=scr1,mask;; // mask of writecount bits
cmp.ne p6,p0=zeroval,scr2
(p6) br.cond.sptk 5b
tbit.nz p8,p0=scr1,DEADLOCKBIT;;// Test for DEADLOCK
mov psr.l=psrsave;; // Reenable IC
srlz.i;;
////////////// END PHYSICAL MODE ////////////////////
(p8) add r8=1,r8
(p8) br.cond.spnt 1b;; // Repeat if DEADLOCK occurred.
br.ret.sptk rp
.endp sn2_ptc_deadlock_recovery_core

View file

@ -0,0 +1,572 @@
/*
* SN2 Platform specific SMP Support
*
* 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-2006 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/threads.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mmzone.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/nodemask.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/processor.h>
#include <asm/irq.h>
#include <asm/sal.h>
#include <asm/delay.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/tlb.h>
#include <asm/numa.h>
#include <asm/hw_irq.h>
#include <asm/current.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/addrs.h>
#include <asm/sn/shub_mmr.h>
#include <asm/sn/nodepda.h>
#include <asm/sn/rw_mmr.h>
#include <asm/sn/sn_feature_sets.h>
DEFINE_PER_CPU(struct ptc_stats, ptcstats);
DECLARE_PER_CPU(struct ptc_stats, ptcstats);
static __cacheline_aligned DEFINE_SPINLOCK(sn2_global_ptc_lock);
/* 0 = old algorithm (no IPI flushes), 1 = ipi deadlock flush, 2 = ipi instead of SHUB ptc, >2 = always ipi */
static int sn2_flush_opt = 0;
extern unsigned long
sn2_ptc_deadlock_recovery_core(volatile unsigned long *, unsigned long,
volatile unsigned long *, unsigned long,
volatile unsigned long *, unsigned long);
void
sn2_ptc_deadlock_recovery(short *, short, short, int,
volatile unsigned long *, unsigned long,
volatile unsigned long *, unsigned long);
/*
* Note: some is the following is captured here to make degugging easier
* (the macros make more sense if you see the debug patch - not posted)
*/
#define sn2_ptctest 0
#define local_node_uses_ptc_ga(sh1) ((sh1) ? 1 : 0)
#define max_active_pio(sh1) ((sh1) ? 32 : 7)
#define reset_max_active_on_deadlock() 1
#define PTC_LOCK(sh1) ((sh1) ? &sn2_global_ptc_lock : &sn_nodepda->ptc_lock)
struct ptc_stats {
unsigned long ptc_l;
unsigned long change_rid;
unsigned long shub_ptc_flushes;
unsigned long nodes_flushed;
unsigned long deadlocks;
unsigned long deadlocks2;
unsigned long lock_itc_clocks;
unsigned long shub_itc_clocks;
unsigned long shub_itc_clocks_max;
unsigned long shub_ptc_flushes_not_my_mm;
unsigned long shub_ipi_flushes;
unsigned long shub_ipi_flushes_itc_clocks;
};
#define sn2_ptctest 0
static inline unsigned long wait_piowc(void)
{
volatile unsigned long *piows;
unsigned long zeroval, ws;
piows = pda->pio_write_status_addr;
zeroval = pda->pio_write_status_val;
do {
cpu_relax();
} while (((ws = *piows) & SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK) != zeroval);
return (ws & SH_PIO_WRITE_STATUS_WRITE_DEADLOCK_MASK) != 0;
}
/**
* sn_migrate - SN-specific task migration actions
* @task: Task being migrated to new CPU
*
* SN2 PIO writes from separate CPUs are not guaranteed to arrive in order.
* Context switching user threads which have memory-mapped MMIO may cause
* PIOs to issue from separate CPUs, thus the PIO writes must be drained
* from the previous CPU's Shub before execution resumes on the new CPU.
*/
void sn_migrate(struct task_struct *task)
{
pda_t *last_pda = pdacpu(task_thread_info(task)->last_cpu);
volatile unsigned long *adr = last_pda->pio_write_status_addr;
unsigned long val = last_pda->pio_write_status_val;
/* Drain PIO writes from old CPU's Shub */
while (unlikely((*adr & SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK)
!= val))
cpu_relax();
}
void sn_tlb_migrate_finish(struct mm_struct *mm)
{
/* flush_tlb_mm is inefficient if more than 1 users of mm */
if (mm == current->mm && mm && atomic_read(&mm->mm_users) == 1)
flush_tlb_mm(mm);
}
static void
sn2_ipi_flush_all_tlb(struct mm_struct *mm)
{
unsigned long itc;
itc = ia64_get_itc();
smp_flush_tlb_cpumask(*mm_cpumask(mm));
itc = ia64_get_itc() - itc;
__get_cpu_var(ptcstats).shub_ipi_flushes_itc_clocks += itc;
__get_cpu_var(ptcstats).shub_ipi_flushes++;
}
/**
* sn2_global_tlb_purge - globally purge translation cache of virtual address range
* @mm: mm_struct containing virtual address range
* @start: start of virtual address range
* @end: end of virtual address range
* @nbits: specifies number of bytes to purge per instruction (num = 1<<(nbits & 0xfc))
*
* Purges the translation caches of all processors of the given virtual address
* range.
*
* Note:
* - cpu_vm_mask is a bit mask that indicates which cpus have loaded the context.
* - cpu_vm_mask is converted into a nodemask of the nodes containing the
* cpus in cpu_vm_mask.
* - if only one bit is set in cpu_vm_mask & it is the current cpu & the
* process is purging its own virtual address range, then only the
* local TLB needs to be flushed. This flushing can be done using
* ptc.l. This is the common case & avoids the global spinlock.
* - if multiple cpus have loaded the context, then flushing has to be
* done with ptc.g/MMRs under protection of the global ptc_lock.
*/
void
sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start,
unsigned long end, unsigned long nbits)
{
int i, ibegin, shub1, cnode, mynasid, cpu, lcpu = 0, nasid;
int mymm = (mm == current->active_mm && mm == current->mm);
int use_cpu_ptcga;
volatile unsigned long *ptc0, *ptc1;
unsigned long itc, itc2, flags, data0 = 0, data1 = 0, rr_value, old_rr = 0;
short nasids[MAX_NUMNODES], nix;
nodemask_t nodes_flushed;
int active, max_active, deadlock, flush_opt = sn2_flush_opt;
if (flush_opt > 2) {
sn2_ipi_flush_all_tlb(mm);
return;
}
nodes_clear(nodes_flushed);
i = 0;
for_each_cpu(cpu, mm_cpumask(mm)) {
cnode = cpu_to_node(cpu);
node_set(cnode, nodes_flushed);
lcpu = cpu;
i++;
}
if (i == 0)
return;
preempt_disable();
if (likely(i == 1 && lcpu == smp_processor_id() && mymm)) {
do {
ia64_ptcl(start, nbits << 2);
start += (1UL << nbits);
} while (start < end);
ia64_srlz_i();
__get_cpu_var(ptcstats).ptc_l++;
preempt_enable();
return;
}
if (atomic_read(&mm->mm_users) == 1 && mymm) {
flush_tlb_mm(mm);
__get_cpu_var(ptcstats).change_rid++;
preempt_enable();
return;
}
if (flush_opt == 2) {
sn2_ipi_flush_all_tlb(mm);
preempt_enable();
return;
}
itc = ia64_get_itc();
nix = 0;
for_each_node_mask(cnode, nodes_flushed)
nasids[nix++] = cnodeid_to_nasid(cnode);
rr_value = (mm->context << 3) | REGION_NUMBER(start);
shub1 = is_shub1();
if (shub1) {
data0 = (1UL << SH1_PTC_0_A_SHFT) |
(nbits << SH1_PTC_0_PS_SHFT) |
(rr_value << SH1_PTC_0_RID_SHFT) |
(1UL << SH1_PTC_0_START_SHFT);
ptc0 = (long *)GLOBAL_MMR_PHYS_ADDR(0, SH1_PTC_0);
ptc1 = (long *)GLOBAL_MMR_PHYS_ADDR(0, SH1_PTC_1);
} else {
data0 = (1UL << SH2_PTC_A_SHFT) |
(nbits << SH2_PTC_PS_SHFT) |
(1UL << SH2_PTC_START_SHFT);
ptc0 = (long *)GLOBAL_MMR_PHYS_ADDR(0, SH2_PTC +
(rr_value << SH2_PTC_RID_SHFT));
ptc1 = NULL;
}
mynasid = get_nasid();
use_cpu_ptcga = local_node_uses_ptc_ga(shub1);
max_active = max_active_pio(shub1);
itc = ia64_get_itc();
spin_lock_irqsave(PTC_LOCK(shub1), flags);
itc2 = ia64_get_itc();
__get_cpu_var(ptcstats).lock_itc_clocks += itc2 - itc;
__get_cpu_var(ptcstats).shub_ptc_flushes++;
__get_cpu_var(ptcstats).nodes_flushed += nix;
if (!mymm)
__get_cpu_var(ptcstats).shub_ptc_flushes_not_my_mm++;
if (use_cpu_ptcga && !mymm) {
old_rr = ia64_get_rr(start);
ia64_set_rr(start, (old_rr & 0xff) | (rr_value << 8));
ia64_srlz_d();
}
wait_piowc();
do {
if (shub1)
data1 = start | (1UL << SH1_PTC_1_START_SHFT);
else
data0 = (data0 & ~SH2_PTC_ADDR_MASK) | (start & SH2_PTC_ADDR_MASK);
deadlock = 0;
active = 0;
for (ibegin = 0, i = 0; i < nix; i++) {
nasid = nasids[i];
if (use_cpu_ptcga && unlikely(nasid == mynasid)) {
ia64_ptcga(start, nbits << 2);
ia64_srlz_i();
} else {
ptc0 = CHANGE_NASID(nasid, ptc0);
if (ptc1)
ptc1 = CHANGE_NASID(nasid, ptc1);
pio_atomic_phys_write_mmrs(ptc0, data0, ptc1, data1);
active++;
}
if (active >= max_active || i == (nix - 1)) {
if ((deadlock = wait_piowc())) {
if (flush_opt == 1)
goto done;
sn2_ptc_deadlock_recovery(nasids, ibegin, i, mynasid, ptc0, data0, ptc1, data1);
if (reset_max_active_on_deadlock())
max_active = 1;
}
active = 0;
ibegin = i + 1;
}
}
start += (1UL << nbits);
} while (start < end);
done:
itc2 = ia64_get_itc() - itc2;
__get_cpu_var(ptcstats).shub_itc_clocks += itc2;
if (itc2 > __get_cpu_var(ptcstats).shub_itc_clocks_max)
__get_cpu_var(ptcstats).shub_itc_clocks_max = itc2;
if (old_rr) {
ia64_set_rr(start, old_rr);
ia64_srlz_d();
}
spin_unlock_irqrestore(PTC_LOCK(shub1), flags);
if (flush_opt == 1 && deadlock) {
__get_cpu_var(ptcstats).deadlocks++;
sn2_ipi_flush_all_tlb(mm);
}
preempt_enable();
}
/*
* sn2_ptc_deadlock_recovery
*
* Recover from PTC deadlocks conditions. Recovery requires stepping thru each
* TLB flush transaction. The recovery sequence is somewhat tricky & is
* coded in assembly language.
*/
void
sn2_ptc_deadlock_recovery(short *nasids, short ib, short ie, int mynasid,
volatile unsigned long *ptc0, unsigned long data0,
volatile unsigned long *ptc1, unsigned long data1)
{
short nasid, i;
unsigned long *piows, zeroval, n;
__get_cpu_var(ptcstats).deadlocks++;
piows = (unsigned long *) pda->pio_write_status_addr;
zeroval = pda->pio_write_status_val;
for (i=ib; i <= ie; i++) {
nasid = nasids[i];
if (local_node_uses_ptc_ga(is_shub1()) && nasid == mynasid)
continue;
ptc0 = CHANGE_NASID(nasid, ptc0);
if (ptc1)
ptc1 = CHANGE_NASID(nasid, ptc1);
n = sn2_ptc_deadlock_recovery_core(ptc0, data0, ptc1, data1, piows, zeroval);
__get_cpu_var(ptcstats).deadlocks2 += n;
}
}
/**
* sn_send_IPI_phys - send an IPI to a Nasid and slice
* @nasid: nasid to receive the interrupt (may be outside partition)
* @physid: physical cpuid to receive the interrupt.
* @vector: command to send
* @delivery_mode: delivery mechanism
*
* Sends an IPI (interprocessor interrupt) to the processor specified by
* @physid
*
* @delivery_mode can be one of the following
*
* %IA64_IPI_DM_INT - pend an interrupt
* %IA64_IPI_DM_PMI - pend a PMI
* %IA64_IPI_DM_NMI - pend an NMI
* %IA64_IPI_DM_INIT - pend an INIT interrupt
*/
void sn_send_IPI_phys(int nasid, long physid, int vector, int delivery_mode)
{
long val;
unsigned long flags = 0;
volatile long *p;
p = (long *)GLOBAL_MMR_PHYS_ADDR(nasid, SH_IPI_INT);
val = (1UL << SH_IPI_INT_SEND_SHFT) |
(physid << SH_IPI_INT_PID_SHFT) |
((long)delivery_mode << SH_IPI_INT_TYPE_SHFT) |
((long)vector << SH_IPI_INT_IDX_SHFT) |
(0x000feeUL << SH_IPI_INT_BASE_SHFT);
mb();
if (enable_shub_wars_1_1()) {
spin_lock_irqsave(&sn2_global_ptc_lock, flags);
}
pio_phys_write_mmr(p, val);
if (enable_shub_wars_1_1()) {
wait_piowc();
spin_unlock_irqrestore(&sn2_global_ptc_lock, flags);
}
}
EXPORT_SYMBOL(sn_send_IPI_phys);
/**
* sn2_send_IPI - send an IPI to a processor
* @cpuid: target of the IPI
* @vector: command to send
* @delivery_mode: delivery mechanism
* @redirect: redirect the IPI?
*
* Sends an IPI (InterProcessor Interrupt) to the processor specified by
* @cpuid. @vector specifies the command to send, while @delivery_mode can
* be one of the following
*
* %IA64_IPI_DM_INT - pend an interrupt
* %IA64_IPI_DM_PMI - pend a PMI
* %IA64_IPI_DM_NMI - pend an NMI
* %IA64_IPI_DM_INIT - pend an INIT interrupt
*/
void sn2_send_IPI(int cpuid, int vector, int delivery_mode, int redirect)
{
long physid;
int nasid;
physid = cpu_physical_id(cpuid);
nasid = cpuid_to_nasid(cpuid);
/* the following is used only when starting cpus at boot time */
if (unlikely(nasid == -1))
ia64_sn_get_sapic_info(physid, &nasid, NULL, NULL);
sn_send_IPI_phys(nasid, physid, vector, delivery_mode);
}
#ifdef CONFIG_HOTPLUG_CPU
/**
* sn_cpu_disable_allowed - Determine if a CPU can be disabled.
* @cpu - CPU that is requested to be disabled.
*
* CPU disable is only allowed on SHub2 systems running with a PROM
* that supports CPU disable. It is not permitted to disable the boot processor.
*/
bool sn_cpu_disable_allowed(int cpu)
{
if (is_shub2() && sn_prom_feature_available(PRF_CPU_DISABLE_SUPPORT)) {
if (cpu != 0)
return true;
else
printk(KERN_WARNING
"Disabling the boot processor is not allowed.\n");
} else
printk(KERN_WARNING
"CPU disable is not supported on this system.\n");
return false;
}
#endif /* CONFIG_HOTPLUG_CPU */
#ifdef CONFIG_PROC_FS
#define PTC_BASENAME "sgi_sn/ptc_statistics"
static void *sn2_ptc_seq_start(struct seq_file *file, loff_t * offset)
{
if (*offset < nr_cpu_ids)
return offset;
return NULL;
}
static void *sn2_ptc_seq_next(struct seq_file *file, void *data, loff_t * offset)
{
(*offset)++;
if (*offset < nr_cpu_ids)
return offset;
return NULL;
}
static void sn2_ptc_seq_stop(struct seq_file *file, void *data)
{
}
static int sn2_ptc_seq_show(struct seq_file *file, void *data)
{
struct ptc_stats *stat;
int cpu;
cpu = *(loff_t *) data;
if (!cpu) {
seq_printf(file,
"# cpu ptc_l newrid ptc_flushes nodes_flushed deadlocks lock_nsec shub_nsec shub_nsec_max not_my_mm deadlock2 ipi_fluches ipi_nsec\n");
seq_printf(file, "# ptctest %d, flushopt %d\n", sn2_ptctest, sn2_flush_opt);
}
if (cpu < nr_cpu_ids && cpu_online(cpu)) {
stat = &per_cpu(ptcstats, cpu);
seq_printf(file, "cpu %d %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n", cpu, stat->ptc_l,
stat->change_rid, stat->shub_ptc_flushes, stat->nodes_flushed,
stat->deadlocks,
1000 * stat->lock_itc_clocks / per_cpu(ia64_cpu_info, cpu).cyc_per_usec,
1000 * stat->shub_itc_clocks / per_cpu(ia64_cpu_info, cpu).cyc_per_usec,
1000 * stat->shub_itc_clocks_max / per_cpu(ia64_cpu_info, cpu).cyc_per_usec,
stat->shub_ptc_flushes_not_my_mm,
stat->deadlocks2,
stat->shub_ipi_flushes,
1000 * stat->shub_ipi_flushes_itc_clocks / per_cpu(ia64_cpu_info, cpu).cyc_per_usec);
}
return 0;
}
static ssize_t sn2_ptc_proc_write(struct file *file, const char __user *user, size_t count, loff_t *data)
{
int cpu;
char optstr[64];
if (count == 0 || count > sizeof(optstr))
return -EINVAL;
if (copy_from_user(optstr, user, count))
return -EFAULT;
optstr[count - 1] = '\0';
sn2_flush_opt = simple_strtoul(optstr, NULL, 0);
for_each_online_cpu(cpu)
memset(&per_cpu(ptcstats, cpu), 0, sizeof(struct ptc_stats));
return count;
}
static const struct seq_operations sn2_ptc_seq_ops = {
.start = sn2_ptc_seq_start,
.next = sn2_ptc_seq_next,
.stop = sn2_ptc_seq_stop,
.show = sn2_ptc_seq_show
};
static int sn2_ptc_proc_open(struct inode *inode, struct file *file)
{
return seq_open(file, &sn2_ptc_seq_ops);
}
static const struct file_operations proc_sn2_ptc_operations = {
.open = sn2_ptc_proc_open,
.read = seq_read,
.write = sn2_ptc_proc_write,
.llseek = seq_lseek,
.release = seq_release,
};
static struct proc_dir_entry *proc_sn2_ptc;
static int __init sn2_ptc_init(void)
{
if (!ia64_platform_is("sn2"))
return 0;
proc_sn2_ptc = proc_create(PTC_BASENAME, 0444,
NULL, &proc_sn2_ptc_operations);
if (!proc_sn2_ptc) {
printk(KERN_ERR "unable to create %s proc entry", PTC_BASENAME);
return -EINVAL;
}
spin_lock_init(&sn2_global_ptc_lock);
return 0;
}
static void __exit sn2_ptc_exit(void)
{
remove_proc_entry(PTC_BASENAME, NULL);
}
module_init(sn2_ptc_init);
module_exit(sn2_ptc_exit);
#endif /* CONFIG_PROC_FS */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,117 @@
/*
* 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-2005 Silicon Graphics, Inc. All rights reserved.
*/
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <asm/sn/sn_sal.h>
static int partition_id_show(struct seq_file *s, void *p)
{
seq_printf(s, "%d\n", sn_partition_id);
return 0;
}
static int partition_id_open(struct inode *inode, struct file *file)
{
return single_open(file, partition_id_show, NULL);
}
static int system_serial_number_show(struct seq_file *s, void *p)
{
seq_printf(s, "%s\n", sn_system_serial_number());
return 0;
}
static int system_serial_number_open(struct inode *inode, struct file *file)
{
return single_open(file, system_serial_number_show, NULL);
}
static int licenseID_show(struct seq_file *s, void *p)
{
seq_printf(s, "0x%llx\n", sn_partition_serial_number_val());
return 0;
}
static int licenseID_open(struct inode *inode, struct file *file)
{
return single_open(file, licenseID_show, NULL);
}
static int coherence_id_show(struct seq_file *s, void *p)
{
seq_printf(s, "%d\n", partition_coherence_id());
return 0;
}
static int coherence_id_open(struct inode *inode, struct file *file)
{
return single_open(file, coherence_id_show, NULL);
}
/* /proc/sgi_sn/sn_topology uses seq_file, see sn_hwperf.c */
extern int sn_topology_open(struct inode *, struct file *);
extern int sn_topology_release(struct inode *, struct file *);
static const struct file_operations proc_partition_id_fops = {
.open = partition_id_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations proc_system_sn_fops = {
.open = system_serial_number_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations proc_license_id_fops = {
.open = licenseID_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations proc_coherence_id_fops = {
.open = coherence_id_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations proc_sn_topo_fops = {
.open = sn_topology_open,
.read = seq_read,
.llseek = seq_lseek,
.release = sn_topology_release,
};
void register_sn_procfs(void)
{
static struct proc_dir_entry *sgi_proc_dir = NULL;
BUG_ON(sgi_proc_dir != NULL);
if (!(sgi_proc_dir = proc_mkdir("sgi_sn", NULL)))
return;
proc_create("partition_id", 0444, sgi_proc_dir,
&proc_partition_id_fops);
proc_create("system_serial_number", 0444, sgi_proc_dir,
&proc_system_sn_fops);
proc_create("licenseID", 0444, sgi_proc_dir, &proc_license_id_fops);
proc_create("coherence_id", 0444, sgi_proc_dir,
&proc_coherence_id_fops);
proc_create("sn_topology", 0444, sgi_proc_dir, &proc_sn_topo_fops);
}
#endif /* CONFIG_PROC_FS */

View file

@ -0,0 +1,60 @@
/*
* linux/arch/ia64/sn/kernel/sn2/timer.c
*
* Copyright (C) 2003 Silicon Graphics, Inc.
* Copyright (C) 2003 Hewlett-Packard Co
* David Mosberger <davidm@hpl.hp.com>: updated for new timer-interpolation infrastructure
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/clocksource.h>
#include <asm/hw_irq.h>
#include <asm/timex.h>
#include <asm/sn/leds.h>
#include <asm/sn/shub_mmr.h>
#include <asm/sn/clksupport.h>
extern unsigned long sn_rtc_cycles_per_second;
static cycle_t read_sn2(struct clocksource *cs)
{
return (cycle_t)readq(RTC_COUNTER_ADDR);
}
static struct clocksource clocksource_sn2 = {
.name = "sn2_rtc",
.rating = 450,
.read = read_sn2,
.mask = (1LL << 55) - 1,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
/*
* sn udelay uses the RTC instead of the ITC because the ITC is not
* synchronized across all CPUs, and the thread may migrate to another CPU
* if preemption is enabled.
*/
static void
ia64_sn_udelay (unsigned long usecs)
{
unsigned long start = rtc_time();
unsigned long end = start +
usecs * sn_rtc_cycles_per_second / 1000000;
while (time_before((unsigned long)rtc_time(), end))
cpu_relax();
}
void __init sn_timer_init(void)
{
clocksource_sn2.archdata.fsys_mmio = RTC_COUNTER_ADDR;
clocksource_register_hz(&clocksource_sn2, sn_rtc_cycles_per_second);
ia64_udelay = &ia64_sn_udelay;
}

View file

@ -0,0 +1,60 @@
/*
*
*
* Copyright (c) 2005, 2006 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan
*/
#include <linux/interrupt.h>
#include <asm/sn/pda.h>
#include <asm/sn/leds.h>
extern void sn_lb_int_war_check(void);
extern irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs);
#define SN_LB_INT_WAR_INTERVAL 100
void sn_timer_interrupt(int irq, void *dev_id)
{
/* LED blinking */
if (!pda->hb_count--) {
pda->hb_count = HZ / 2;
set_led_bits(pda->hb_state ^=
LED_CPU_HEARTBEAT, LED_CPU_HEARTBEAT);
}
if (is_shub1()) {
if (enable_shub_wars_1_1()) {
/* Bugfix code for SHUB 1.1 */
if (pda->pio_shub_war_cam_addr)
*pda->pio_shub_war_cam_addr = 0x8000000000000010UL;
}
if (pda->sn_lb_int_war_ticks == 0)
sn_lb_int_war_check();
pda->sn_lb_int_war_ticks++;
if (pda->sn_lb_int_war_ticks >= SN_LB_INT_WAR_INTERVAL)
pda->sn_lb_int_war_ticks = 0;
}
}