Initial commit
This commit is contained in:
commit
169c65d57e
51358 changed files with 23120455 additions and 0 deletions
15
arch/ia64/sn/kernel/sn2/Makefile
Normal file
15
arch/ia64/sn/kernel/sn2/Makefile
Normal 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
|
41
arch/ia64/sn/kernel/sn2/cache.c
Normal file
41
arch/ia64/sn/kernel/sn2/cache.c
Normal 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);
|
101
arch/ia64/sn/kernel/sn2/io.c
Normal file
101
arch/ia64/sn/kernel/sn2/io.c
Normal 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
|
231
arch/ia64/sn/kernel/sn2/prominfo_proc.c
Normal file
231
arch/ia64/sn/kernel/sn2/prominfo_proc.c
Normal 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);
|
||||
}
|
92
arch/ia64/sn/kernel/sn2/ptc_deadlock.S
Normal file
92
arch/ia64/sn/kernel/sn2/ptc_deadlock.S
Normal 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
|
572
arch/ia64/sn/kernel/sn2/sn2_smp.c
Normal file
572
arch/ia64/sn/kernel/sn2/sn2_smp.c
Normal 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 */
|
||||
|
1003
arch/ia64/sn/kernel/sn2/sn_hwperf.c
Normal file
1003
arch/ia64/sn/kernel/sn2/sn_hwperf.c
Normal file
File diff suppressed because it is too large
Load diff
117
arch/ia64/sn/kernel/sn2/sn_proc_fs.c
Normal file
117
arch/ia64/sn/kernel/sn2/sn_proc_fs.c
Normal 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 */
|
60
arch/ia64/sn/kernel/sn2/timer.c
Normal file
60
arch/ia64/sn/kernel/sn2/timer.c
Normal 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;
|
||||
}
|
60
arch/ia64/sn/kernel/sn2/timer_interrupt.c
Normal file
60
arch/ia64/sn/kernel/sn2/timer_interrupt.c
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue