Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
6b3dcb38
Commit
6b3dcb38
authored
Oct 27, 2002
by
Alan Cox
Committed by
Linus Torvalds
Oct 27, 2002
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] core voyager arch/i386/machine support
parent
12de40fe
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
4441 additions
and
0 deletions
+4441
-0
arch/i386/mach-voyager/Makefile
arch/i386/mach-voyager/Makefile
+17
-0
arch/i386/mach-voyager/do_timer.h
arch/i386/mach-voyager/do_timer.h
+22
-0
arch/i386/mach-voyager/entry_arch.h
arch/i386/mach-voyager/entry_arch.h
+26
-0
arch/i386/mach-voyager/irq_vectors.h
arch/i386/mach-voyager/irq_vectors.h
+71
-0
arch/i386/mach-voyager/setup.c
arch/i386/mach-voyager/setup.c
+43
-0
arch/i386/mach-voyager/setup_arch_post.h
arch/i386/mach-voyager/setup_arch_post.h
+73
-0
arch/i386/mach-voyager/setup_arch_pre.h
arch/i386/mach-voyager/setup_arch_pre.h
+10
-0
arch/i386/mach-voyager/voyager_basic.c
arch/i386/mach-voyager/voyager_basic.c
+312
-0
arch/i386/mach-voyager/voyager_cat.c
arch/i386/mach-voyager/voyager_cat.c
+1178
-0
arch/i386/mach-voyager/voyager_smp.c
arch/i386/mach-voyager/voyager_smp.c
+1989
-0
arch/i386/mach-voyager/voyager_thread.c
arch/i386/mach-voyager/voyager_thread.c
+178
-0
include/asm-i386/voyager.h
include/asm-i386/voyager.h
+522
-0
No files found.
arch/i386/mach-voyager/Makefile
0 → 100644
View file @
6b3dcb38
#
# Makefile for the linux kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
EXTRA_CFLAGS
+=
-I
../kernel
export-objs
:=
obj-y
:=
setup.o voyager_basic.o voyager_thread.o
obj-$(CONFIG_SMP)
+=
voyager_smp.o voyager_cat.o
include
$(TOPDIR)/Rules.make
arch/i386/mach-voyager/do_timer.h
0 → 100644
View file @
6b3dcb38
/* defines for inline arch setup functions */
#include <asm/voyager.h>
static
inline
void
do_timer_interrupt_hook
(
struct
pt_regs
*
regs
)
{
do_timer
(
regs
);
voyager_timer_interrupt
(
regs
);
}
static
inline
int
do_timer_overflow
(
int
count
)
{
/* can't read the ISR, just assume 1 tick
overflow */
if
(
count
>
LATCH
||
count
<
0
)
{
printk
(
KERN_ERR
"VOYAGER PROBLEM: count is %d, latch is %d
\n
"
,
count
,
LATCH
);
count
=
LATCH
;
}
count
-=
LATCH
;
return
count
;
}
arch/i386/mach-voyager/entry_arch.h
0 → 100644
View file @
6b3dcb38
/* -*- mode: c; c-basic-offset: 8 -*- */
/* Copyright (C) 2002
*
* Author: James.Bottomley@HansenPartnership.com
*
* linux/arch/i386/voyager/entry_arch.h
*
* This file builds the VIC and QIC CPI gates
*/
/* initialise the voyager interrupt gates
*
* This uses the macros in irq.h to set up assembly jump gates. The
* calls are then redirected to the same routine with smp_ prefixed */
BUILD_INTERRUPT
(
vic_sys_interrupt
,
VIC_SYS_INT
)
BUILD_INTERRUPT
(
vic_cmn_interrupt
,
VIC_CMN_INT
)
BUILD_INTERRUPT
(
vic_cpi_interrupt
,
VIC_CPI_LEVEL0
);
/* do all the QIC interrupts */
BUILD_INTERRUPT
(
qic_timer_interrupt
,
QIC_TIMER_CPI
);
BUILD_INTERRUPT
(
qic_invalidate_interrupt
,
QIC_INVALIDATE_CPI
);
BUILD_INTERRUPT
(
qic_reschedule_interrupt
,
QIC_RESCHEDULE_CPI
);
BUILD_INTERRUPT
(
qic_enable_irq_interrupt
,
QIC_ENABLE_IRQ_CPI
);
BUILD_INTERRUPT
(
qic_call_function_interrupt
,
QIC_CALL_FUNCTION_CPI
);
arch/i386/mach-voyager/irq_vectors.h
0 → 100644
View file @
6b3dcb38
/* -*- mode: c; c-basic-offset: 8 -*- */
/* Copyright (C) 2002
*
* Author: James.Bottomley@HansenPartnership.com
*
* linux/arch/i386/voyager/irq_vectors.h
*
* This file provides definitions for the VIC and QIC CPIs
*/
#ifndef _ASM_IRQ_VECTORS_H
#define _ASM_IRQ_VECTORS_H
/*
* IDT vectors usable for external interrupt sources start
* at 0x20:
*/
#define FIRST_EXTERNAL_VECTOR 0x20
#define SYSCALL_VECTOR 0x80
/*
* Vectors 0x20-0x2f are used for ISA interrupts.
*/
/* These define the CPIs we use in linux */
#define VIC_CPI_LEVEL0 0
#define VIC_CPI_LEVEL1 1
/* now the fake CPIs */
#define VIC_TIMER_CPI 2
#define VIC_INVALIDATE_CPI 3
#define VIC_RESCHEDULE_CPI 4
#define VIC_ENABLE_IRQ_CPI 5
#define VIC_CALL_FUNCTION_CPI 6
/* Now the QIC CPIs: Since we don't need the two initial levels,
* these are 2 less than the VIC CPIs */
#define QIC_CPI_OFFSET 1
#define QIC_TIMER_CPI (VIC_TIMER_CPI - QIC_CPI_OFFSET)
#define QIC_INVALIDATE_CPI (VIC_INVALIDATE_CPI - QIC_CPI_OFFSET)
#define QIC_RESCHEDULE_CPI (VIC_RESCHEDULE_CPI - QIC_CPI_OFFSET)
#define QIC_ENABLE_IRQ_CPI (VIC_ENABLE_IRQ_CPI - QIC_CPI_OFFSET)
#define QIC_CALL_FUNCTION_CPI (VIC_CALL_FUNCTION_CPI - QIC_CPI_OFFSET)
#define VIC_START_FAKE_CPI VIC_TIMER_CPI
#define VIC_END_FAKE_CPI VIC_CALL_FUNCTION_CPI
/* this is the SYS_INT CPI. */
#define VIC_SYS_INT 8
#define VIC_CMN_INT 15
/* This is the boot CPI for alternate processors. It gets overwritten
* by the above once the system has activated all available processors */
#define VIC_CPU_BOOT_CPI VIC_CPI_LEVEL0
#define VIC_CPU_BOOT_ERRATA_CPI (VIC_CPI_LEVEL0 + 8)
#define NR_IRQS 224
#ifndef __ASSEMBLY__
extern
asmlinkage
void
vic_cpi_interrupt
(
void
);
extern
asmlinkage
void
vic_sys_interrupt
(
void
);
extern
asmlinkage
void
vic_cmn_interrupt
(
void
);
extern
asmlinkage
void
qic_timer_interrupt
(
void
);
extern
asmlinkage
void
qic_invalidate_interrupt
(
void
);
extern
asmlinkage
void
qic_reschedule_interrupt
(
void
);
extern
asmlinkage
void
qic_enable_irq_interrupt
(
void
);
extern
asmlinkage
void
qic_call_function_interrupt
(
void
);
#endif
/* !__ASSEMBLY__ */
#endif
/* _ASM_IRQ_VECTORS_H */
arch/i386/mach-voyager/setup.c
0 → 100644
View file @
6b3dcb38
/*
* Machine specific setup for generic
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/arch_hooks.h>
void
__init
pre_intr_init_hook
(
void
)
{
init_ISA_irqs
();
}
/*
* IRQ2 is cascade interrupt to second interrupt controller
*/
static
struct
irqaction
irq2
=
{
no_action
,
0
,
0
,
"cascade"
,
NULL
,
NULL
};
void
__init
intr_init_hook
(
void
)
{
#ifdef CONFIG_SMP
smp_intr_init
();
#endif
setup_irq
(
2
,
&
irq2
);
}
void
__init
pre_setup_arch_hook
(
void
)
{
}
void
__init
trap_init_hook
(
void
)
{
}
static
struct
irqaction
irq0
=
{
timer_interrupt
,
SA_INTERRUPT
,
0
,
"timer"
,
NULL
,
NULL
};
void
__init
time_init_hook
(
void
)
{
setup_irq
(
0
,
&
irq0
);
}
arch/i386/mach-voyager/setup_arch_post.h
0 → 100644
View file @
6b3dcb38
/* Hook for machine specific memory setup.
*
* This is included late in kernel/setup.c so that it can make use of all of
* the static functions. */
static
inline
char
*
__init
machine_specific_memory_setup
(
void
)
{
char
*
who
;
who
=
"NOT VOYAGER"
;
if
(
voyager_level
==
5
)
{
__u32
addr
,
length
;
int
i
;
who
=
"Voyager-SUS"
;
e820
.
nr_map
=
0
;
for
(
i
=
0
;
voyager_memory_detect
(
i
,
&
addr
,
&
length
);
i
++
)
{
add_memory_region
(
addr
,
length
,
E820_RAM
);
}
return
who
;
}
else
if
(
voyager_level
==
4
)
{
__u32
tom
;
__u16
catbase
=
inb
(
VOYAGER_SSPB_RELOCATION_PORT
)
<<
8
;
/* select the DINO config space */
outb
(
VOYAGER_DINO
,
VOYAGER_CAT_CONFIG_PORT
);
/* Read DINO top of memory register */
tom
=
((
inb
(
catbase
+
0x4
)
&
0xf0
)
<<
16
)
+
((
inb
(
catbase
+
0x5
)
&
0x7f
)
<<
24
);
if
(
inb
(
catbase
)
!=
VOYAGER_DINO
)
{
printk
(
KERN_ERR
"Voyager: Failed to get DINO for L4, setting tom to EXT_MEM_K
\n
"
);
tom
=
(
EXT_MEM_K
)
<<
10
;
}
who
=
"Voyager-TOM"
;
add_memory_region
(
0
,
0x9f000
,
E820_RAM
);
/* map from 1M to top of memory */
add_memory_region
(
1
*
1024
*
1024
,
tom
-
1
*
1024
*
1024
,
E820_RAM
);
/* FIXME: Should check the ASICs to see if I need to
* take out the 8M window. Just do it at the moment
* */
add_memory_region
(
8
*
1024
*
1024
,
8
*
1024
*
1024
,
E820_RESERVED
);
return
who
;
}
who
=
"BIOS-e820"
;
/*
* Try to copy the BIOS-supplied E820-map.
*
* Otherwise fake a memory map; one section from 0k->640k,
* the next section from 1mb->appropriate_mem_k
*/
sanitize_e820_map
(
E820_MAP
,
&
E820_MAP_NR
);
if
(
copy_e820_map
(
E820_MAP
,
E820_MAP_NR
)
<
0
)
{
unsigned
long
mem_size
;
/* compare results from other methods and take the greater */
if
(
ALT_MEM_K
<
EXT_MEM_K
)
{
mem_size
=
EXT_MEM_K
;
who
=
"BIOS-88"
;
}
else
{
mem_size
=
ALT_MEM_K
;
who
=
"BIOS-e801"
;
}
e820
.
nr_map
=
0
;
add_memory_region
(
0
,
LOWMEMSIZE
(),
E820_RAM
);
add_memory_region
(
HIGH_MEMORY
,
mem_size
<<
10
,
E820_RAM
);
}
return
who
;
}
arch/i386/mach-voyager/setup_arch_pre.h
0 → 100644
View file @
6b3dcb38
#include <asm/voyager.h>
#define VOYAGER_BIOS_INFO ((struct voyager_bios_info *)(PARAM+0x40))
/* Hook to call BIOS initialisation function */
/* for voyager, pass the voyager BIOS/SUS info area to the detection
* routines */
#define ARCH_SETUP voyager_detect(VOYAGER_BIOS_INFO);
arch/i386/mach-voyager/voyager_basic.c
0 → 100644
View file @
6b3dcb38
/* Copyright (C) 1999,2001
*
* Author: J.E.J.Bottomley@HansenPartnership.com
*
* linux/arch/i386/kernel/voyager.c
*
* This file contains all the voyager specific routines for getting
* initialisation of the architecture to function. For additional
* features see:
*
* voyager_cat.c - Voyager CAT bus interface
* voyager_smp.c - Voyager SMP hal (emulates linux smp.c)
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <asm/io.h>
#include <asm/pgalloc.h>
#include <asm/voyager.h>
#include <asm/vic.h>
#include <linux/pm.h>
#include <linux/irq.h>
#include <asm/tlbflush.h>
#include <asm/arch_hooks.h>
/*
* Power off function, if any
*/
void
(
*
pm_power_off
)(
void
);
int
reboot_thru_bios
;
int
voyager_level
=
0
;
struct
voyager_SUS
*
voyager_SUS
=
NULL
;
void
voyager_detect
(
struct
voyager_bios_info
*
bios
)
{
if
(
bios
->
len
!=
0xff
)
{
int
class
=
(
bios
->
class_1
<<
8
)
|
(
bios
->
class_2
&
0xff
);
printk
(
"Voyager System detected.
\n
"
" Class %x, Revision %d.%d
\n
"
,
class
,
bios
->
major
,
bios
->
minor
);
if
(
class
==
VOYAGER_LEVEL4
)
voyager_level
=
4
;
else
if
(
class
<
VOYAGER_LEVEL5_AND_ABOVE
)
voyager_level
=
3
;
else
voyager_level
=
5
;
printk
(
" Architecture Level %d
\n
"
,
voyager_level
);
if
(
voyager_level
<
4
)
printk
(
"
\n
**WARNING**: Voyager HAL only supports Levels 4 and 5 Architectures at the moment
\n\n
"
);
/* install the power off handler */
pm_power_off
=
voyager_power_off
;
}
else
{
printk
(
"
\n\n
**WARNING**: No Voyager Subsystem Found
\n
"
);
}
}
void
voyager_system_interrupt
(
int
cpl
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
printk
(
"Voyager: detected system interrupt
\n
"
);
}
/* Routine to read information from the extended CMOS area */
__u8
voyager_extended_cmos_read
(
__u16
addr
)
{
outb
(
addr
&
0xff
,
0x74
);
outb
((
addr
>>
8
)
&
0xff
,
0x75
);
return
inb
(
0x76
);
}
/* internal definitions for the SUS Click Map of memory */
#define CLICK_ENTRIES 16
#define CLICK_SIZE 4096
/* click to byte conversion for Length */
typedef
struct
ClickMap
{
struct
Entry
{
__u32
Address
;
__u32
Length
;
}
Entry
[
CLICK_ENTRIES
];
}
ClickMap_t
;
/* This routine is pretty much an awful hack to read the bios clickmap by
* mapping it into page 0. There are usually three regions in the map:
* Base Memory
* Extended Memory
* zero length marker for end of map
*
* Returns are 0 for failure and 1 for success on extracting region.
*/
int
__init
voyager_memory_detect
(
int
region
,
__u32
*
start
,
__u32
*
length
)
{
int
i
;
int
retval
=
0
;
__u8
cmos
[
4
];
ClickMap_t
*
map
;
unsigned
long
map_addr
;
unsigned
long
old
;
if
(
region
>=
CLICK_ENTRIES
)
{
printk
(
"Voyager: Illegal ClickMap region %d
\n
"
,
region
);
return
0
;
}
for
(
i
=
0
;
i
<
sizeof
(
cmos
);
i
++
)
cmos
[
i
]
=
voyager_extended_cmos_read
(
VOYAGER_MEMORY_CLICKMAP
+
i
);
map_addr
=
*
(
unsigned
long
*
)
cmos
;
/* steal page 0 for this */
old
=
pg0
[
0
];
pg0
[
0
]
=
((
map_addr
&
PAGE_MASK
)
|
_PAGE_RW
|
_PAGE_PRESENT
);
local_flush_tlb
();
/* now clear everything out but page 0 */
map
=
(
ClickMap_t
*
)(
map_addr
&
(
~
PAGE_MASK
));
/* zero length is the end of the clickmap */
if
(
map
->
Entry
[
region
].
Length
!=
0
)
{
*
length
=
map
->
Entry
[
region
].
Length
*
CLICK_SIZE
;
*
start
=
map
->
Entry
[
region
].
Address
;
retval
=
1
;
}
/* replace the mapping */
pg0
[
0
]
=
old
;
local_flush_tlb
();
return
retval
;
}
void
voyager_dump
()
{
/* get here via a sysrq */
#ifdef CONFIG_SMP
voyager_smp_dump
();
#endif
}
/* voyager specific handling code for timer interrupts. Used to hand
* off the timer tick to the SMP code, since the VIC doesn't have an
* internal timer (The QIC does, but that's another story). */
void
voyager_timer_interrupt
(
struct
pt_regs
*
regs
)
{
if
((
jiffies
&
0x3ff
)
==
0
)
{
/* There seems to be something flaky in either
* hardware or software that is resetting the timer 0
* count to something much higher than it should be
* This seems to occur in the boot sequence, just
* before root is mounted. Therefore, every 10
* seconds or so, we sanity check the timer zero count
* and kick it back to where it should be.
*
* FIXME: This is the most awful hack yet seen. I
* should work out exactly what is interfering with
* the timer count settings early in the boot sequence
* and swiftly introduce it to something sharp and
* pointy. */
__u16
val
;
extern
spinlock_t
i8253_lock
;
spin_lock
(
&
i8253_lock
);
outb_p
(
0x00
,
0x43
);
val
=
inb_p
(
0x40
);
val
|=
inb
(
0x40
)
<<
8
;
spin_unlock
(
&
i8253_lock
);
if
(
val
>
LATCH
)
{
printk
(
"
\n
VOYAGER: countdown timer value too high (%d), resetting
\n\n
"
,
val
);
spin_lock
(
&
i8253_lock
);
outb
(
0x34
,
0x43
);
outb_p
(
LATCH
&
0xff
,
0x40
);
/* LSB */
outb
(
LATCH
>>
8
,
0x40
);
/* MSB */
spin_unlock
(
&
i8253_lock
);
}
}
#ifdef CONFIG_SMP
smp_vic_timer_interrupt
(
regs
);
#endif
}
void
voyager_power_off
(
void
)
{
printk
(
"VOYAGER Power Off
\n
"
);
if
(
voyager_level
==
5
)
{
voyager_cat_power_off
();
}
else
if
(
voyager_level
==
4
)
{
/* This doesn't apparently work on most L4 machines,
* but the specs say to do this to get automatic power
* off. Unfortunately, if it doesn't power off the
* machine, it ends up doing a cold restart, which
* isn't really intended, so comment out the code */
#if 0
int port;
/* enable the voyager Configuration Space */
outb((inb(VOYAGER_MC_SETUP) & 0xf0) | 0x8,
VOYAGER_MC_SETUP);
/* the port for the power off flag is an offset from the
floating base */
port = (inb(VOYAGER_SSPB_RELOCATION_PORT) << 8) + 0x21;
/* set the power off flag */
outb(inb(port) | 0x1, port);
#endif
}
/* and wait for it to happen */
for
(;;)
{
__asm
(
"cli"
);
__asm
(
"hlt"
);
}
}
/* copied from process.c */
static
inline
void
kb_wait
(
void
)
{
int
i
;
for
(
i
=
0
;
i
<
0x10000
;
i
++
)
if
((
inb_p
(
0x64
)
&
0x02
)
==
0
)
break
;
}
void
machine_restart
(
char
*
cmd
)
{
printk
(
"Voyager Warm Restart
\n
"
);
kb_wait
();
if
(
voyager_level
==
5
)
{
/* write magic values to the RTC to inform system that
* shutdown is beginning */
outb
(
0x8f
,
0x70
);
outb
(
0x5
,
0x71
);
udelay
(
50
);
outb
(
0xfe
,
0x64
);
/* pull reset low */
}
else
if
(
voyager_level
==
4
)
{
__u16
catbase
=
inb
(
VOYAGER_SSPB_RELOCATION_PORT
)
<<
8
;
__u8
basebd
=
inb
(
VOYAGER_MC_SETUP
);
outb
(
basebd
|
0x08
,
VOYAGER_MC_SETUP
);
outb
(
0x02
,
catbase
+
0x21
);
}
for
(;;)
{
asm
(
"cli"
);
asm
(
"hlt"
);
}
}
void
mca_nmi_hook
(
void
)
{
__u8
dumpval
__attribute__
((
unused
))
=
inb
(
0xf823
);
__u8
swnmi
__attribute__
((
unused
))
=
inb
(
0xf813
);
extern
void
show_stack
(
unsigned
long
*
);
/* FIXME: assume dump switch pressed */
/* check to see if the dump switch was pressed */
VDEBUG
((
"VOYAGER: dumpval = 0x%x, swnmi = 0x%x
\n
"
,
dumpval
,
swnmi
));
/* clear swnmi */
outb
(
0xff
,
0xf813
);
/* tell SUS to ignore dump */
if
(
voyager_level
==
5
&&
voyager_SUS
!=
NULL
)
{
if
(
voyager_SUS
->
SUS_mbox
==
VOYAGER_DUMP_BUTTON_NMI
)
{
voyager_SUS
->
kernel_mbox
=
VOYAGER_NO_COMMAND
;
voyager_SUS
->
kernel_flags
|=
VOYAGER_OS_IN_PROGRESS
;
udelay
(
1000
);
voyager_SUS
->
kernel_mbox
=
VOYAGER_IGNORE_DUMP
;
voyager_SUS
->
kernel_flags
&=
~
VOYAGER_OS_IN_PROGRESS
;
}
}
printk
(
KERN_ERR
"VOYAGER: Dump switch pressed, printing CPU%d tracebacks
\n
"
,
smp_processor_id
());
show_stack
(
NULL
);
show_state
();
}
void
machine_halt
(
void
)
{
/* treat a halt like a power off */
machine_power_off
();
}
void
machine_power_off
(
void
)
{
if
(
pm_power_off
)
pm_power_off
();
}
arch/i386/mach-voyager/voyager_cat.c
0 → 100644
View file @
6b3dcb38
/* -*- mode: c; c-basic-offset: 8 -*- */
/* Copyright (C) 1999,2001
*
* Author: J.E.J.Bottomley@HansenPartnership.com
*
* linux/arch/i386/kernel/voyager_cat.c
*
* This file contains all the logic for manipulating the CAT bus
* in a level 5 machine.
*
* The CAT bus is a serial configuration and test bus. Its primary
* uses are to probe the initial configuration of the system and to
* diagnose error conditions when a system interrupt occurs. The low
* level interface is fairly primitive, so most of this file consists
* of bit shift manipulations to send and receive packets on the
* serial bus */
#include <linux/config.h>
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/sched.h>
#include <asm/voyager.h>
#include <asm/vic.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/io.h>
#ifdef VOYAGER_CAT_DEBUG
#define CDEBUG(x) printk x
#else
#define CDEBUG(x)
#endif
/* the CAT command port */
#define CAT_CMD (sspb + 0xe)
/* the CAT data port */
#define CAT_DATA (sspb + 0xd)
/* the internal cat functions */
static
void
cat_pack
(
__u8
*
msg
,
__u16
start_bit
,
__u8
*
data
,
__u16
num_bits
);
static
void
cat_unpack
(
__u8
*
msg
,
__u16
start_bit
,
__u8
*
data
,
__u16
num_bits
);
static
void
cat_build_header
(
__u8
*
header
,
const
__u16
len
,
const
__u16
smallest_reg_bits
,
const
__u16
longest_reg_bits
);
static
int
cat_sendinst
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
op
);
static
int
cat_getdata
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
*
value
);
static
int
cat_shiftout
(
__u8
*
data
,
__u16
data_bytes
,
__u16
header_bytes
,
__u8
pad_bits
);
static
int
cat_write
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
value
);
static
int
cat_read
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
*
value
);
static
int
cat_subread
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u16
offset
,
__u16
len
,
void
*
buf
);
static
int
cat_senddata
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
value
);
static
int
cat_disconnect
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
);
static
int
cat_connect
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
);
static
inline
const
char
*
cat_module_name
(
int
module_id
)
{
switch
(
module_id
)
{
case
0x10
:
return
"Processor Slot 0"
;
case
0x11
:
return
"Processor Slot 1"
;
case
0x12
:
return
"Processor Slot 2"
;
case
0x13
:
return
"Processor Slot 4"
;
case
0x14
:
return
"Memory Slot 0"
;
case
0x15
:
return
"Memory Slot 1"
;
case
0x18
:
return
"Primary Microchannel"
;
case
0x19
:
return
"Secondary Microchannel"
;
case
0x1a
:
return
"Power Supply Interface"
;
case
0x1c
:
return
"Processor Slot 5"
;
case
0x1d
:
return
"Processor Slot 6"
;
case
0x1e
:
return
"Processor Slot 7"
;
case
0x1f
:
return
"Processor Slot 8"
;
default:
return
"Unknown Module"
;
}
}
static
int
sspb
=
0
;
/* stores the super port location */
int
voyager_8slot
=
0
;
/* set to true if a 51xx monster */
voyager_module_t
*
voyager_cat_list
;
/* the I/O port assignments for the VIC and QIC */
static
struct
resource
vic_res
=
{
"Voyager Interrupt Controller"
,
0xFC00
,
0xFC6F
};
static
struct
resource
qic_res
=
{
"Quad Interrupt Controller"
,
0xFC70
,
0xFCFF
};
/* This function is used to pack a data bit stream inside a message.
* It writes num_bits of the data buffer in msg starting at start_bit.
* Note: This function assumes that any unused bit in the data stream
* is set to zero so that the ors will work correctly */
#define BITS_PER_BYTE 8
static
void
cat_pack
(
__u8
*
msg
,
const
__u16
start_bit
,
__u8
*
data
,
const
__u16
num_bits
)
{
/* compute initial shift needed */
const
__u16
offset
=
start_bit
%
BITS_PER_BYTE
;
__u16
len
=
num_bits
/
BITS_PER_BYTE
;
__u16
byte
=
start_bit
/
BITS_PER_BYTE
;
__u16
residue
=
(
num_bits
%
BITS_PER_BYTE
)
+
offset
;
int
i
;
/* adjust if we have more than a byte of residue */
if
(
residue
>=
BITS_PER_BYTE
)
{
residue
-=
BITS_PER_BYTE
;
len
++
;
}
/* clear out the bits. We assume here that if len==0 then
* residue >= offset. This is always true for the catbus
* operations */
msg
[
byte
]
&=
0xff
<<
(
BITS_PER_BYTE
-
offset
);
msg
[
byte
++
]
|=
data
[
0
]
>>
offset
;
if
(
len
==
0
)
return
;
for
(
i
=
1
;
i
<
len
;
i
++
)
msg
[
byte
++
]
=
(
data
[
i
-
1
]
<<
(
BITS_PER_BYTE
-
offset
))
|
(
data
[
i
]
>>
offset
);
if
(
residue
!=
0
)
{
__u8
mask
=
0xff
>>
residue
;
__u8
last_byte
=
data
[
i
-
1
]
<<
(
BITS_PER_BYTE
-
offset
)
|
(
data
[
i
]
>>
offset
);
last_byte
&=
~
mask
;
msg
[
byte
]
&=
mask
;
msg
[
byte
]
|=
last_byte
;
}
return
;
}
/* unpack the data again (same arguments as cat_pack()). data buffer
* must be zero populated.
*
* Function: given a message string move to start_bit and copy num_bits into
* data (starting at bit 0 in data).
*/
static
void
cat_unpack
(
__u8
*
msg
,
const
__u16
start_bit
,
__u8
*
data
,
const
__u16
num_bits
)
{
/* compute initial shift needed */
const
__u16
offset
=
start_bit
%
BITS_PER_BYTE
;
__u16
len
=
num_bits
/
BITS_PER_BYTE
;
const
__u8
last_bits
=
num_bits
%
BITS_PER_BYTE
;
__u16
byte
=
start_bit
/
BITS_PER_BYTE
;
int
i
;
if
(
last_bits
!=
0
)
len
++
;
/* special case: want < 8 bits from msg and we can get it from
* a single byte of the msg */
if
(
len
==
0
&&
BITS_PER_BYTE
-
offset
>=
num_bits
)
{
data
[
0
]
=
msg
[
byte
]
<<
offset
;
data
[
0
]
&=
0xff
>>
(
BITS_PER_BYTE
-
num_bits
);
return
;
}
for
(
i
=
0
;
i
<
len
;
i
++
)
{
/* this annoying if has to be done just in case a read of
* msg one beyond the array causes a panic */
if
(
offset
!=
0
)
{
data
[
i
]
=
msg
[
byte
++
]
<<
offset
;
data
[
i
]
|=
msg
[
byte
]
>>
(
BITS_PER_BYTE
-
offset
);
}
else
{
data
[
i
]
=
msg
[
byte
++
];
}
}
/* do we need to truncate the final byte */
if
(
last_bits
!=
0
)
{
data
[
i
-
1
]
&=
0xff
<<
(
BITS_PER_BYTE
-
last_bits
);
}
return
;
}
static
void
cat_build_header
(
__u8
*
header
,
const
__u16
len
,
const
__u16
smallest_reg_bits
,
const
__u16
longest_reg_bits
)
{
int
i
;
__u16
start_bit
=
(
smallest_reg_bits
-
1
)
%
BITS_PER_BYTE
;
__u8
*
last_byte
=
&
header
[
len
-
1
];
if
(
start_bit
==
0
)
start_bit
=
1
;
/* must have at least one bit in the hdr */
for
(
i
=
0
;
i
<
len
;
i
++
)
header
[
i
]
=
0
;
for
(
i
=
start_bit
;
i
>
0
;
i
--
)
*
last_byte
=
((
*
last_byte
)
<<
1
)
+
1
;
}
static
int
cat_sendinst
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
op
)
{
__u8
parity
,
inst
,
inst_buf
[
4
]
=
{
0
};
__u8
iseq
[
VOYAGER_MAX_SCAN_PATH
],
hseq
[
VOYAGER_MAX_REG_SIZE
];
__u16
ibytes
,
hbytes
,
padbits
;
int
i
;
/*
* Parity is the parity of the register number + 1 (READ_REGISTER
* and WRITE_REGISTER always add '1' to the number of bits == 1)
*/
parity
=
(
__u8
)(
1
+
(
reg
&
0x01
)
+
((
__u8
)(
reg
&
0x02
)
>>
1
)
+
((
__u8
)(
reg
&
0x04
)
>>
2
)
+
((
__u8
)(
reg
&
0x08
)
>>
3
))
%
2
;
inst
=
((
parity
<<
7
)
|
(
reg
<<
2
)
|
op
);
outb
(
VOYAGER_CAT_IRCYC
,
CAT_CMD
);
if
(
!
modp
->
scan_path_connected
)
{
if
(
asicp
->
asic_id
!=
VOYAGER_CAT_ID
)
{
printk
(
"**WARNING***: cat_sendinst has disconnected scan path not to CAT asic
\n
"
);
return
1
;
}
outb
(
VOYAGER_CAT_HEADER
,
CAT_DATA
);
outb
(
inst
,
CAT_DATA
);
if
(
inb
(
CAT_DATA
)
!=
VOYAGER_CAT_HEADER
)
{
CDEBUG
((
"VOYAGER CAT: cat_sendinst failed to get CAT_HEADER
\n
"
));
return
1
;
}
return
0
;
}
ibytes
=
modp
->
inst_bits
/
BITS_PER_BYTE
;
if
((
padbits
=
modp
->
inst_bits
%
BITS_PER_BYTE
)
!=
0
)
{
padbits
=
BITS_PER_BYTE
-
padbits
;
ibytes
++
;
}
hbytes
=
modp
->
largest_reg
/
BITS_PER_BYTE
;
if
(
modp
->
largest_reg
%
BITS_PER_BYTE
)
hbytes
++
;
CDEBUG
((
"cat_sendinst: ibytes=%d, hbytes=%d
\n
"
,
ibytes
,
hbytes
));
/* initialise the instruction sequence to 0xff */
for
(
i
=
0
;
i
<
ibytes
+
hbytes
;
i
++
)
iseq
[
i
]
=
0xff
;
cat_build_header
(
hseq
,
hbytes
,
modp
->
smallest_reg
,
modp
->
largest_reg
);
cat_pack
(
iseq
,
modp
->
inst_bits
,
hseq
,
hbytes
*
BITS_PER_BYTE
);
inst_buf
[
0
]
=
inst
;
inst_buf
[
1
]
=
0xFF
>>
(
modp
->
largest_reg
%
BITS_PER_BYTE
);
cat_pack
(
iseq
,
asicp
->
bit_location
,
inst_buf
,
asicp
->
ireg_length
);
#ifdef VOYAGER_CAT_DEBUG
printk
(
"ins = 0x%x, iseq: "
,
inst
);
for
(
i
=
0
;
i
<
ibytes
+
hbytes
;
i
++
)
printk
(
"0x%x "
,
iseq
[
i
]);
printk
(
"
\n
"
);
#endif
if
(
cat_shiftout
(
iseq
,
ibytes
,
hbytes
,
padbits
))
{
CDEBUG
((
"VOYAGER CAT: cat_sendinst: cat_shiftout failed
\n
"
));
return
1
;
}
CDEBUG
((
"CAT SHIFTOUT DONE
\n
"
));
return
0
;
}
static
int
cat_getdata
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
*
value
)
{
if
(
!
modp
->
scan_path_connected
)
{
if
(
asicp
->
asic_id
!=
VOYAGER_CAT_ID
)
{
CDEBUG
((
"VOYAGER CAT: ERROR: cat_getdata to CAT asic with scan path connected
\n
"
));
return
1
;
}
if
(
reg
>
VOYAGER_SUBADDRHI
)
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
outb
(
VOYAGER_CAT_DRCYC
,
CAT_CMD
);
outb
(
VOYAGER_CAT_HEADER
,
CAT_DATA
);
*
value
=
inb
(
CAT_DATA
);
outb
(
0xAA
,
CAT_DATA
);
if
(
inb
(
CAT_DATA
)
!=
VOYAGER_CAT_HEADER
)
{
CDEBUG
((
"cat_getdata: failed to get VOYAGER_CAT_HEADER
\n
"
));
return
1
;
}
return
0
;
}
else
{
__u16
sbits
=
modp
->
num_asics
-
1
+
asicp
->
ireg_length
;
__u16
sbytes
=
sbits
/
BITS_PER_BYTE
;
__u16
tbytes
;
__u8
string
[
VOYAGER_MAX_SCAN_PATH
],
trailer
[
VOYAGER_MAX_REG_SIZE
];
__u8
padbits
;
int
i
;
outb
(
VOYAGER_CAT_DRCYC
,
CAT_CMD
);
if
((
padbits
=
sbits
%
BITS_PER_BYTE
)
!=
0
)
{
padbits
=
BITS_PER_BYTE
-
padbits
;
sbytes
++
;
}
tbytes
=
asicp
->
ireg_length
/
BITS_PER_BYTE
;
if
(
asicp
->
ireg_length
%
BITS_PER_BYTE
)
tbytes
++
;
CDEBUG
((
"cat_getdata: tbytes = %d, sbytes = %d, padbits = %d
\n
"
,
tbytes
,
sbytes
,
padbits
));
cat_build_header
(
trailer
,
tbytes
,
1
,
asicp
->
ireg_length
);
for
(
i
=
tbytes
-
1
;
i
>=
0
;
i
--
)
{
outb
(
trailer
[
i
],
CAT_DATA
);
string
[
sbytes
+
i
]
=
inb
(
CAT_DATA
);
}
for
(
i
=
sbytes
-
1
;
i
>=
0
;
i
--
)
{
outb
(
0xaa
,
CAT_DATA
);
string
[
i
]
=
inb
(
CAT_DATA
);
}
*
value
=
0
;
cat_unpack
(
string
,
padbits
+
(
tbytes
*
BITS_PER_BYTE
)
+
asicp
->
asic_location
,
value
,
asicp
->
ireg_length
);
#ifdef VOYAGER_CAT_DEBUG
printk
(
"value=0x%x, string: "
,
*
value
);
for
(
i
=
0
;
i
<
tbytes
+
sbytes
;
i
++
)
printk
(
"0x%x "
,
string
[
i
]);
printk
(
"
\n
"
);
#endif
/* sanity check the rest of the return */
for
(
i
=
0
;
i
<
tbytes
;
i
++
)
{
__u8
input
=
0
;
cat_unpack
(
string
,
padbits
+
(
i
*
BITS_PER_BYTE
),
&
input
,
BITS_PER_BYTE
);
if
(
trailer
[
i
]
!=
input
)
{
CDEBUG
((
"cat_getdata: failed to sanity check rest of ret(%d) 0x%x != 0x%x
\n
"
,
i
,
input
,
trailer
[
i
]));
return
1
;
}
}
CDEBUG
((
"cat_getdata DONE
\n
"
));
return
0
;
}
}
static
int
cat_shiftout
(
__u8
*
data
,
__u16
data_bytes
,
__u16
header_bytes
,
__u8
pad_bits
)
{
int
i
;
for
(
i
=
data_bytes
+
header_bytes
-
1
;
i
>=
header_bytes
;
i
--
)
outb
(
data
[
i
],
CAT_DATA
);
for
(
i
=
header_bytes
-
1
;
i
>=
0
;
i
--
)
{
__u8
header
=
0
;
__u8
input
;
outb
(
data
[
i
],
CAT_DATA
);
input
=
inb
(
CAT_DATA
);
CDEBUG
((
"cat_shiftout: returned 0x%x
\n
"
,
input
));
cat_unpack
(
data
,
((
data_bytes
+
i
)
*
BITS_PER_BYTE
)
-
pad_bits
,
&
header
,
BITS_PER_BYTE
);
if
(
input
!=
header
)
{
CDEBUG
((
"VOYAGER CAT: cat_shiftout failed to return header 0x%x != 0x%x
\n
"
,
input
,
header
));
return
1
;
}
}
return
0
;
}
static
int
cat_senddata
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
value
)
{
outb
(
VOYAGER_CAT_DRCYC
,
CAT_CMD
);
if
(
!
modp
->
scan_path_connected
)
{
if
(
asicp
->
asic_id
!=
VOYAGER_CAT_ID
)
{
CDEBUG
((
"VOYAGER CAT: ERROR: scan path disconnected when asic != CAT
\n
"
));
return
1
;
}
outb
(
VOYAGER_CAT_HEADER
,
CAT_DATA
);
outb
(
value
,
CAT_DATA
);
if
(
inb
(
CAT_DATA
)
!=
VOYAGER_CAT_HEADER
)
{
CDEBUG
((
"cat_senddata: failed to get correct header response to sent data
\n
"
));
return
1
;
}
if
(
reg
>
VOYAGER_SUBADDRHI
)
{
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
}
return
0
;
}
else
{
__u16
hbytes
=
asicp
->
ireg_length
/
BITS_PER_BYTE
;
__u16
dbytes
=
(
modp
->
num_asics
-
1
+
asicp
->
ireg_length
)
/
BITS_PER_BYTE
;
__u8
padbits
,
dseq
[
VOYAGER_MAX_SCAN_PATH
],
hseq
[
VOYAGER_MAX_REG_SIZE
];
int
i
;
if
((
padbits
=
(
modp
->
num_asics
-
1
+
asicp
->
ireg_length
)
%
BITS_PER_BYTE
)
!=
0
)
{
padbits
=
BITS_PER_BYTE
-
padbits
;
dbytes
++
;
}
if
(
asicp
->
ireg_length
%
BITS_PER_BYTE
)
hbytes
++
;
cat_build_header
(
hseq
,
hbytes
,
1
,
asicp
->
ireg_length
);
for
(
i
=
0
;
i
<
dbytes
+
hbytes
;
i
++
)
dseq
[
i
]
=
0xff
;
CDEBUG
((
"cat_senddata: dbytes=%d, hbytes=%d, padbits=%d
\n
"
,
dbytes
,
hbytes
,
padbits
));
cat_pack
(
dseq
,
modp
->
num_asics
-
1
+
asicp
->
ireg_length
,
hseq
,
hbytes
*
BITS_PER_BYTE
);
cat_pack
(
dseq
,
asicp
->
asic_location
,
&
value
,
asicp
->
ireg_length
);
#ifdef VOYAGER_CAT_DEBUG
printk
(
"dseq "
);
for
(
i
=
0
;
i
<
hbytes
+
dbytes
;
i
++
)
{
printk
(
"0x%x "
,
dseq
[
i
]);
}
printk
(
"
\n
"
);
#endif
return
cat_shiftout
(
dseq
,
dbytes
,
hbytes
,
padbits
);
}
}
static
int
cat_write
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
value
)
{
if
(
cat_sendinst
(
modp
,
asicp
,
reg
,
VOYAGER_WRITE_CONFIG
))
return
1
;
return
cat_senddata
(
modp
,
asicp
,
reg
,
value
);
}
static
int
cat_read
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u8
reg
,
__u8
*
value
)
{
if
(
cat_sendinst
(
modp
,
asicp
,
reg
,
VOYAGER_READ_CONFIG
))
return
1
;
return
cat_getdata
(
modp
,
asicp
,
reg
,
value
);
}
static
int
cat_subaddrsetup
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u16
offset
,
__u16
len
)
{
__u8
val
;
if
(
len
>
1
)
{
/* set auto increment */
__u8
newval
;
if
(
cat_read
(
modp
,
asicp
,
VOYAGER_AUTO_INC_REG
,
&
val
))
{
CDEBUG
((
"cat_subaddrsetup: read of VOYAGER_AUTO_INC_REG failed
\n
"
));
return
1
;
}
CDEBUG
((
"cat_subaddrsetup: VOYAGER_AUTO_INC_REG = 0x%x
\n
"
,
val
));
newval
=
val
|
VOYAGER_AUTO_INC
;
if
(
newval
!=
val
)
{
if
(
cat_write
(
modp
,
asicp
,
VOYAGER_AUTO_INC_REG
,
val
))
{
CDEBUG
((
"cat_subaddrsetup: write to VOYAGER_AUTO_INC_REG failed
\n
"
));
return
1
;
}
}
}
if
(
cat_write
(
modp
,
asicp
,
VOYAGER_SUBADDRLO
,
(
__u8
)(
offset
&
0xff
)))
{
CDEBUG
((
"cat_subaddrsetup: write to SUBADDRLO failed
\n
"
));
return
1
;
}
if
(
asicp
->
subaddr
>
VOYAGER_SUBADDR_LO
)
{
if
(
cat_write
(
modp
,
asicp
,
VOYAGER_SUBADDRHI
,
(
__u8
)(
offset
>>
8
)))
{
CDEBUG
((
"cat_subaddrsetup: write to SUBADDRHI failed
\n
"
));
return
1
;
}
cat_read
(
modp
,
asicp
,
VOYAGER_SUBADDRHI
,
&
val
);
CDEBUG
((
"cat_subaddrsetup: offset = %d, hi = %d
\n
"
,
offset
,
val
));
}
cat_read
(
modp
,
asicp
,
VOYAGER_SUBADDRLO
,
&
val
);
CDEBUG
((
"cat_subaddrsetup: offset = %d, lo = %d
\n
"
,
offset
,
val
));
return
0
;
}
static
int
cat_subwrite
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u16
offset
,
__u16
len
,
void
*
buf
)
{
int
i
,
retval
;
/* FIXME: need special actions for VOYAGER_CAT_ID here */
if
(
asicp
->
asic_id
==
VOYAGER_CAT_ID
)
{
CDEBUG
((
"cat_subwrite: ATTEMPT TO WRITE TO CAT ASIC
\n
"
));
/* FIXME -- This is supposed to be handled better
* There is a problem writing to the cat asic in the
* PSI. The 30us delay seems to work, though */
udelay
(
30
);
}
if
((
retval
=
cat_subaddrsetup
(
modp
,
asicp
,
offset
,
len
))
!=
0
)
{
printk
(
"cat_subwrite: cat_subaddrsetup FAILED
\n
"
);
return
retval
;
}
if
(
cat_sendinst
(
modp
,
asicp
,
VOYAGER_SUBADDRDATA
,
VOYAGER_WRITE_CONFIG
))
{
printk
(
"cat_subwrite: cat_sendinst FAILED
\n
"
);
return
1
;
}
for
(
i
=
0
;
i
<
len
;
i
++
)
{
if
(
cat_senddata
(
modp
,
asicp
,
0xFF
,
((
__u8
*
)
buf
)[
i
]))
{
printk
(
"cat_subwrite: cat_sendata element at %d FAILED
\n
"
,
i
);
return
1
;
}
}
return
0
;
}
static
int
cat_subread
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
,
__u16
offset
,
__u16
len
,
void
*
buf
)
{
int
i
,
retval
;
if
((
retval
=
cat_subaddrsetup
(
modp
,
asicp
,
offset
,
len
))
!=
0
)
{
CDEBUG
((
"cat_subread: cat_subaddrsetup FAILED
\n
"
));
return
retval
;
}
if
(
cat_sendinst
(
modp
,
asicp
,
VOYAGER_SUBADDRDATA
,
VOYAGER_READ_CONFIG
))
{
CDEBUG
((
"cat_subread: cat_sendinst failed
\n
"
));
return
1
;
}
for
(
i
=
0
;
i
<
len
;
i
++
)
{
if
(
cat_getdata
(
modp
,
asicp
,
0xFF
,
&
((
__u8
*
)
buf
)[
i
]))
{
CDEBUG
((
"cat_subread: cat_getdata element %d failed
\n
"
,
i
));
return
1
;
}
}
return
0
;
}
/* buffer for storing EPROM data read in during initialisation */
static
__initdata
__u8
eprom_buf
[
0xFFFF
];
static
voyager_module_t
*
voyager_initial_module
;
/* Initialise the cat bus components. We assume this is called by the
* boot cpu *after* all memory initialisation has been done (so we can
* use kmalloc) but before smp initialisation, so we can probe the SMP
* configuration and pick up necessary information. */
void
voyager_cat_init
(
void
)
{
voyager_module_t
**
modpp
=
&
voyager_initial_module
;
voyager_asic_t
**
asicpp
;
voyager_asic_t
*
qabc_asic
=
NULL
;
int
i
,
j
;
unsigned
long
qic_addr
=
0
;
__u8
qabc_data
[
0x20
];
__u8
num_submodules
,
val
;
voyager_eprom_hdr_t
*
eprom_hdr
=
(
voyager_eprom_hdr_t
*
)
&
eprom_buf
[
0
];
__u8
cmos
[
4
];
unsigned
long
addr
;
/* initiallise the SUS mailbox */
for
(
i
=
0
;
i
<
sizeof
(
cmos
);
i
++
)
cmos
[
i
]
=
voyager_extended_cmos_read
(
VOYAGER_DUMP_LOCATION
+
i
);
addr
=
*
(
unsigned
long
*
)
cmos
;
if
((
addr
&
0xff000000
)
!=
0xff000000
)
{
printk
(
KERN_ERR
"Voyager failed to get SUS mailbox (addr = 0x%lx
\n
"
,
addr
);
}
else
{
static
struct
resource
res
;
res
.
name
=
"voyager SUS"
;
res
.
start
=
addr
;
res
.
end
=
addr
+
0x3ff
;
request_resource
(
&
iomem_resource
,
&
res
);
voyager_SUS
=
(
struct
voyager_SUS
*
)
ioremap
(
addr
,
0x400
);
printk
(
KERN_NOTICE
"Voyager SUS mailbox version 0x%x
\n
"
,
voyager_SUS
->
SUS_version
);
voyager_SUS
->
kernel_version
=
VOYAGER_MAILBOX_VERSION
;
voyager_SUS
->
kernel_flags
=
VOYAGER_OS_HAS_SYSINT
;
}
/* clear the processor counts */
voyager_extended_vic_processors
=
0
;
voyager_quad_processors
=
0
;
printk
(
"VOYAGER: beginning CAT bus probe
\n
"
);
/* set up the SuperSet Port Block which tells us where the
* CAT communication port is */
sspb
=
inb
(
VOYAGER_SSPB_RELOCATION_PORT
)
*
0x100
;
VDEBUG
((
"VOYAGER DEBUG: sspb = 0x%x
\n
"
,
sspb
));
/* now find out if were 8 slot or normal */
if
((
inb
(
VIC_PROC_WHO_AM_I
)
&
EIGHT_SLOT_IDENTIFIER
)
==
EIGHT_SLOT_IDENTIFIER
)
{
voyager_8slot
=
1
;
printk
(
KERN_NOTICE
"Voyager: Eight slot 51xx configuration detected
\n
"
);
}
for
(
i
=
VOYAGER_MIN_MODULE
;
i
<=
VOYAGER_MAX_MODULE
;
i
++
)
{
__u8
input
;
int
asic
;
__u16
eprom_size
;
__u16
sp_offset
;
outb
(
VOYAGER_CAT_DESELECT
,
VOYAGER_CAT_CONFIG_PORT
);
outb
(
i
,
VOYAGER_CAT_CONFIG_PORT
);
/* check the presence of the module */
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
outb
(
VOYAGER_CAT_IRCYC
,
CAT_CMD
);
outb
(
VOYAGER_CAT_HEADER
,
CAT_DATA
);
/* stream series of alternating 1's and 0's to stimulate
* response */
outb
(
0xAA
,
CAT_DATA
);
input
=
inb
(
CAT_DATA
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
if
(
input
!=
VOYAGER_CAT_HEADER
)
{
continue
;
}
CDEBUG
((
"VOYAGER DEBUG: found module id 0x%x, %s
\n
"
,
i
,
cat_module_name
(
i
)));
*
modpp
=
kmalloc
(
sizeof
(
voyager_module_t
),
GFP_KERNEL
);
/*&voyager_module_storage[cat_count++];*/
if
(
*
modpp
==
NULL
)
{
printk
(
"**WARNING** kmalloc failure in cat_init
\n
"
);
continue
;
}
memset
(
*
modpp
,
0
,
sizeof
(
voyager_module_t
));
/* need temporary asic for cat_subread. It will be
* filled in correctly later */
(
*
modpp
)
->
asic
=
kmalloc
(
sizeof
(
voyager_asic_t
),
GFP_KERNEL
);
/*&voyager_asic_storage[asic_count];*/
if
((
*
modpp
)
->
asic
==
NULL
)
{
printk
(
"**WARNING** kmalloc failure in cat_init
\n
"
);
continue
;
}
memset
((
*
modpp
)
->
asic
,
0
,
sizeof
(
voyager_asic_t
));
(
*
modpp
)
->
asic
->
asic_id
=
VOYAGER_CAT_ID
;
(
*
modpp
)
->
asic
->
subaddr
=
VOYAGER_SUBADDR_HI
;
(
*
modpp
)
->
module_addr
=
i
;
(
*
modpp
)
->
scan_path_connected
=
0
;
if
(
i
==
VOYAGER_PSI
)
{
/* Exception leg for modules with no EEPROM */
printk
(
"Module
\"
%s
\"\n
"
,
cat_module_name
(
i
));
continue
;
}
CDEBUG
((
"cat_init: Reading eeprom for module 0x%x at offset %d
\n
"
,
i
,
VOYAGER_XSUM_END_OFFSET
));
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_disconnect
(
*
modpp
,
(
*
modpp
)
->
asic
);
if
(
cat_subread
(
*
modpp
,
(
*
modpp
)
->
asic
,
VOYAGER_XSUM_END_OFFSET
,
sizeof
(
eprom_size
),
&
eprom_size
))
{
printk
(
"**WARNING**: Voyager couldn't read EPROM size for module 0x%x
\n
"
,
i
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
continue
;
}
if
(
eprom_size
>
sizeof
(
eprom_buf
))
{
printk
(
"**WARNING**: Voyager insufficient size to read EPROM data, module 0x%x. Need %d
\n
"
,
i
,
eprom_size
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
continue
;
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
CDEBUG
((
"cat_init: module 0x%x, eeprom_size %d
\n
"
,
i
,
eprom_size
));
if
(
cat_subread
(
*
modpp
,
(
*
modpp
)
->
asic
,
0
,
eprom_size
,
eprom_buf
))
{
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
continue
;
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
printk
(
"Module
\"
%s
\"
, version 0x%x, tracer 0x%x, asics %d
\n
"
,
cat_module_name
(
i
),
eprom_hdr
->
version_id
,
*
((
__u32
*
)
eprom_hdr
->
tracer
),
eprom_hdr
->
num_asics
);
(
*
modpp
)
->
ee_size
=
eprom_hdr
->
ee_size
;
(
*
modpp
)
->
num_asics
=
eprom_hdr
->
num_asics
;
asicpp
=
&
((
*
modpp
)
->
asic
);
sp_offset
=
eprom_hdr
->
scan_path_offset
;
/* All we really care about are the Quad cards. We
* identify them because they are in a processor slot
* and have only four asics */
if
((
i
<
0x10
||
(
i
>=
0x14
&&
i
<
0x1c
)
||
i
>
0x1f
))
{
modpp
=
&
((
*
modpp
)
->
next
);
continue
;
}
/* Now we know it's in a processor slot, does it have
* a quad baseboard submodule */
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_read
(
*
modpp
,
(
*
modpp
)
->
asic
,
VOYAGER_SUBMODPRESENT
,
&
num_submodules
);
/* lowest two bits, active low */
num_submodules
=
~
(
0xfc
|
num_submodules
);
CDEBUG
((
"VOYAGER CAT: %d submodules present
\n
"
,
num_submodules
));
if
(
num_submodules
==
0
)
{
/* fill in the dyadic extended processors */
__u8
cpu
=
i
&
0x07
;
printk
(
"Module
\"
%s
\"
: Dyadic Processor Card
\n
"
,
cat_module_name
(
i
));
voyager_extended_vic_processors
|=
(
1
<<
cpu
);
cpu
+=
4
;
voyager_extended_vic_processors
|=
(
1
<<
cpu
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
continue
;
}
/* now we want to read the asics on the first submodule,
* which should be the quad base board */
cat_read
(
*
modpp
,
(
*
modpp
)
->
asic
,
VOYAGER_SUBMODSELECT
,
&
val
);
CDEBUG
((
"cat_init: SUBMODSELECT value = 0x%x
\n
"
,
val
));
val
=
(
val
&
0x7c
)
|
VOYAGER_QUAD_BASEBOARD
;
cat_write
(
*
modpp
,
(
*
modpp
)
->
asic
,
VOYAGER_SUBMODSELECT
,
val
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
CDEBUG
((
"cat_init: Reading eeprom for module 0x%x at offset %d
\n
"
,
i
,
VOYAGER_XSUM_END_OFFSET
));
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_disconnect
(
*
modpp
,
(
*
modpp
)
->
asic
);
if
(
cat_subread
(
*
modpp
,
(
*
modpp
)
->
asic
,
VOYAGER_XSUM_END_OFFSET
,
sizeof
(
eprom_size
),
&
eprom_size
))
{
printk
(
"**WARNING**: Voyager couldn't read EPROM size for module 0x%x
\n
"
,
i
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
continue
;
}
if
(
eprom_size
>
sizeof
(
eprom_buf
))
{
printk
(
"**WARNING**: Voyager insufficient size to read EPROM data, module 0x%x. Need %d
\n
"
,
i
,
eprom_size
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
continue
;
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
CDEBUG
((
"cat_init: module 0x%x, eeprom_size %d
\n
"
,
i
,
eprom_size
));
if
(
cat_subread
(
*
modpp
,
(
*
modpp
)
->
asic
,
0
,
eprom_size
,
eprom_buf
))
{
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
continue
;
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
/* Now do everything for the QBB submodule 1 */
(
*
modpp
)
->
ee_size
=
eprom_hdr
->
ee_size
;
(
*
modpp
)
->
num_asics
=
eprom_hdr
->
num_asics
;
asicpp
=
&
((
*
modpp
)
->
asic
);
sp_offset
=
eprom_hdr
->
scan_path_offset
;
/* get rid of the dummy CAT asic and read the real one */
kfree
((
*
modpp
)
->
asic
);
for
(
asic
=
0
;
asic
<
(
*
modpp
)
->
num_asics
;
asic
++
)
{
int
j
;
voyager_asic_t
*
asicp
=
*
asicpp
=
kmalloc
(
sizeof
(
voyager_asic_t
),
GFP_KERNEL
);
/*&voyager_asic_storage[asic_count++];*/
voyager_sp_table_t
*
sp_table
;
voyager_at_t
*
asic_table
;
voyager_jtt_t
*
jtag_table
;
if
(
asicp
==
NULL
)
{
printk
(
"**WARNING** kmalloc failure in cat_init
\n
"
);
continue
;
}
memset
(
asicp
,
0
,
sizeof
(
voyager_asic_t
));
asicpp
=
&
(
asicp
->
next
);
asicp
->
asic_location
=
asic
;
sp_table
=
(
voyager_sp_table_t
*
)(
eprom_buf
+
sp_offset
);
asicp
->
asic_id
=
sp_table
->
asic_id
;
asic_table
=
(
voyager_at_t
*
)(
eprom_buf
+
sp_table
->
asic_data_offset
);
for
(
j
=
0
;
j
<
4
;
j
++
)
asicp
->
jtag_id
[
j
]
=
asic_table
->
jtag_id
[
j
];
jtag_table
=
(
voyager_jtt_t
*
)(
eprom_buf
+
asic_table
->
jtag_offset
);
asicp
->
ireg_length
=
jtag_table
->
ireg_len
;
asicp
->
bit_location
=
(
*
modpp
)
->
inst_bits
;
(
*
modpp
)
->
inst_bits
+=
asicp
->
ireg_length
;
if
(
asicp
->
ireg_length
>
(
*
modpp
)
->
largest_reg
)
(
*
modpp
)
->
largest_reg
=
asicp
->
ireg_length
;
if
(
asicp
->
ireg_length
<
(
*
modpp
)
->
smallest_reg
||
(
*
modpp
)
->
smallest_reg
==
0
)
(
*
modpp
)
->
smallest_reg
=
asicp
->
ireg_length
;
CDEBUG
((
"asic 0x%x, ireg_length=%d, bit_location=%d
\n
"
,
asicp
->
asic_id
,
asicp
->
ireg_length
,
asicp
->
bit_location
));
if
(
asicp
->
asic_id
==
VOYAGER_QUAD_QABC
)
{
CDEBUG
((
"VOYAGER CAT: QABC ASIC found
\n
"
));
qabc_asic
=
asicp
;
}
sp_offset
+=
sizeof
(
voyager_sp_table_t
);
}
CDEBUG
((
"Module inst_bits = %d, largest_reg = %d, smallest_reg=%d
\n
"
,
(
*
modpp
)
->
inst_bits
,
(
*
modpp
)
->
largest_reg
,
(
*
modpp
)
->
smallest_reg
));
/* OK, now we have the QUAD ASICs set up, use them.
* we need to:
*
* 1. Find the Memory area for the Quad CPIs.
* 2. Find the Extended VIC processor
* 3. Configure a second extended VIC processor (This
* cannot be done for the 51xx.
* */
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_connect
(
*
modpp
,
(
*
modpp
)
->
asic
);
CDEBUG
((
"CAT CONNECTED!!
\n
"
));
cat_subread
(
*
modpp
,
qabc_asic
,
0
,
sizeof
(
qabc_data
),
qabc_data
);
qic_addr
=
qabc_data
[
5
]
<<
8
;
qic_addr
=
(
qic_addr
|
qabc_data
[
6
])
<<
8
;
qic_addr
=
(
qic_addr
|
qabc_data
[
7
])
<<
8
;
printk
(
"Module
\"
%s
\"
: Quad Processor Card; CPI 0x%lx, SET=0x%x
\n
"
,
cat_module_name
(
i
),
qic_addr
,
qabc_data
[
8
]);
#if 0 /* plumbing fails---FIXME */
if((qabc_data[8] & 0xf0) == 0) {
/* FIXME: 32 way 8 CPU slot monster cannot be
* plumbed this way---need to check for it */
printk("Plumbing second Extended Quad Processor\n");
/* second VIC line hardwired to Quad CPU 1 */
qabc_data[8] |= 0x20;
cat_subwrite(*modpp, qabc_asic, 8, 1, &qabc_data[8]);
#ifdef VOYAGER_CAT_DEBUG
/* verify plumbing */
cat_subread(*modpp, qabc_asic, 8, 1, &qabc_data[8]);
if((qabc_data[8] & 0xf0) == 0) {
CDEBUG(("PLUMBING FAILED: 0x%x\n", qabc_data[8]));
}
#endif
}
#endif
{
struct
resource
*
res
=
kmalloc
(
sizeof
(
struct
resource
),
GFP_KERNEL
);
memset
(
res
,
0
,
sizeof
(
struct
resource
));
res
->
name
=
kmalloc
(
128
,
GFP_KERNEL
);
sprintf
((
char
*
)
res
->
name
,
"Voyager %s Quad CPI"
,
cat_module_name
(
i
));
res
->
start
=
qic_addr
;
res
->
end
=
qic_addr
+
0x3ff
;
request_resource
(
&
iomem_resource
,
res
);
}
qic_addr
=
(
unsigned
long
)
ioremap
(
qic_addr
,
0x400
);
for
(
j
=
0
;
j
<
4
;
j
++
)
{
__u8
cpu
;
if
(
voyager_8slot
)
{
/* 8 slot has a different mapping,
* each slot has only one vic line, so
* 1 cpu in each slot must be < 8 */
cpu
=
(
i
&
0x07
)
+
j
*
8
;
}
else
{
cpu
=
(
i
&
0x03
)
+
j
*
4
;
}
if
(
(
qabc_data
[
8
]
&
(
1
<<
j
)))
{
voyager_extended_vic_processors
|=
(
1
<<
cpu
);
}
if
(
qabc_data
[
8
]
&
(
1
<<
(
j
+
4
))
)
{
/* Second SET register plumbed: Quad
* card has two VIC connected CPUs.
* Secondary cannot be booted as a VIC
* CPU */
voyager_extended_vic_processors
|=
(
1
<<
cpu
);
voyager_allowed_boot_processors
&=
(
~
(
1
<<
cpu
));
}
voyager_quad_processors
|=
(
1
<<
cpu
);
voyager_quad_cpi_addr
[
cpu
]
=
(
struct
voyager_qic_cpi
*
)
(
qic_addr
+
(
j
<<
8
));
CDEBUG
((
"CPU%d: CPI address 0x%lx
\n
"
,
cpu
,
(
unsigned
long
)
voyager_quad_cpi_addr
[
cpu
]));
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
*
asicpp
=
NULL
;
modpp
=
&
((
*
modpp
)
->
next
);
}
*
modpp
=
NULL
;
printk
(
"CAT Bus Initialisation finished: extended procs 0x%x, quad procs 0x%x, allowed vic boot = 0x%x
\n
"
,
voyager_extended_vic_processors
,
voyager_quad_processors
,
voyager_allowed_boot_processors
);
request_resource
(
&
ioport_resource
,
&
vic_res
);
if
(
voyager_quad_processors
)
request_resource
(
&
ioport_resource
,
&
qic_res
);
/* set up the front power switch */
}
int
voyager_cat_readb
(
__u8
module
,
__u8
asic
,
int
reg
)
{
return
0
;
}
static
int
cat_disconnect
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
)
{
__u8
val
;
int
err
=
0
;
if
(
!
modp
->
scan_path_connected
)
return
0
;
if
(
asicp
->
asic_id
!=
VOYAGER_CAT_ID
)
{
CDEBUG
((
"cat_disconnect: ASIC is not CAT
\n
"
));
return
1
;
}
err
=
cat_read
(
modp
,
asicp
,
VOYAGER_SCANPATH
,
&
val
);
if
(
err
)
{
CDEBUG
((
"cat_disconnect: failed to read SCANPATH
\n
"
));
return
err
;
}
val
&=
VOYAGER_DISCONNECT_ASIC
;
err
=
cat_write
(
modp
,
asicp
,
VOYAGER_SCANPATH
,
val
);
if
(
err
)
{
CDEBUG
((
"cat_disconnect: failed to write SCANPATH
\n
"
));
return
err
;
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
modp
->
scan_path_connected
=
0
;
return
0
;
}
static
int
cat_connect
(
voyager_module_t
*
modp
,
voyager_asic_t
*
asicp
)
{
__u8
val
;
int
err
=
0
;
if
(
modp
->
scan_path_connected
)
return
0
;
if
(
asicp
->
asic_id
!=
VOYAGER_CAT_ID
)
{
CDEBUG
((
"cat_connect: ASIC is not CAT
\n
"
));
return
1
;
}
err
=
cat_read
(
modp
,
asicp
,
VOYAGER_SCANPATH
,
&
val
);
if
(
err
)
{
CDEBUG
((
"cat_connect: failed to read SCANPATH
\n
"
));
return
err
;
}
val
|=
VOYAGER_CONNECT_ASIC
;
err
=
cat_write
(
modp
,
asicp
,
VOYAGER_SCANPATH
,
val
);
if
(
err
)
{
CDEBUG
((
"cat_connect: failed to write SCANPATH
\n
"
));
return
err
;
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
modp
->
scan_path_connected
=
1
;
return
0
;
}
void
voyager_cat_power_off
(
void
)
{
/* Power the machine off by writing to the PSI over the CAT
* bus */
__u8
data
;
voyager_module_t
psi
=
{
0
};
voyager_asic_t
psi_asic
=
{
0
};
psi
.
asic
=
&
psi_asic
;
psi
.
asic
->
asic_id
=
VOYAGER_CAT_ID
;
psi
.
asic
->
subaddr
=
VOYAGER_SUBADDR_HI
;
psi
.
module_addr
=
VOYAGER_PSI
;
psi
.
scan_path_connected
=
0
;
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
/* Connect the PSI to the CAT Bus */
outb
(
VOYAGER_CAT_DESELECT
,
VOYAGER_CAT_CONFIG_PORT
);
outb
(
VOYAGER_PSI
,
VOYAGER_CAT_CONFIG_PORT
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_disconnect
(
&
psi
,
&
psi_asic
);
/* Read the status */
cat_subread
(
&
psi
,
&
psi_asic
,
VOYAGER_PSI_GENERAL_REG
,
1
,
&
data
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
CDEBUG
((
"PSI STATUS 0x%x
\n
"
,
data
));
/* These two writes are power off prep and perform */
data
=
PSI_CLEAR
;
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_subwrite
(
&
psi
,
&
psi_asic
,
VOYAGER_PSI_GENERAL_REG
,
1
,
&
data
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
data
=
PSI_POWER_DOWN
;
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_subwrite
(
&
psi
,
&
psi_asic
,
VOYAGER_PSI_GENERAL_REG
,
1
,
&
data
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
}
struct
voyager_status
voyager_status
=
{
0
};
void
voyager_cat_psi
(
__u8
cmd
,
__u16
reg
,
__u8
*
data
)
{
voyager_module_t
psi
=
{
0
};
voyager_asic_t
psi_asic
=
{
0
};
psi
.
asic
=
&
psi_asic
;
psi
.
asic
->
asic_id
=
VOYAGER_CAT_ID
;
psi
.
asic
->
subaddr
=
VOYAGER_SUBADDR_HI
;
psi
.
module_addr
=
VOYAGER_PSI
;
psi
.
scan_path_connected
=
0
;
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
/* Connect the PSI to the CAT Bus */
outb
(
VOYAGER_CAT_DESELECT
,
VOYAGER_CAT_CONFIG_PORT
);
outb
(
VOYAGER_PSI
,
VOYAGER_CAT_CONFIG_PORT
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_disconnect
(
&
psi
,
&
psi_asic
);
switch
(
cmd
)
{
case
VOYAGER_PSI_READ
:
cat_read
(
&
psi
,
&
psi_asic
,
reg
,
data
);
break
;
case
VOYAGER_PSI_WRITE
:
cat_write
(
&
psi
,
&
psi_asic
,
reg
,
*
data
);
break
;
case
VOYAGER_PSI_SUBREAD
:
cat_subread
(
&
psi
,
&
psi_asic
,
reg
,
1
,
data
);
break
;
case
VOYAGER_PSI_SUBWRITE
:
cat_subwrite
(
&
psi
,
&
psi_asic
,
reg
,
1
,
data
);
break
;
default:
printk
(
KERN_ERR
"Voyager PSI, unrecognised command %d
\n
"
,
cmd
);
break
;
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
}
void
voyager_cat_do_common_interrupt
(
void
)
{
/* This is caused either by a memory parity error or something
* in the PSI */
__u8
data
;
voyager_module_t
psi
=
{
0
};
voyager_asic_t
psi_asic
=
{
0
};
struct
voyager_psi
psi_reg
;
int
i
;
re_read:
psi
.
asic
=
&
psi_asic
;
psi
.
asic
->
asic_id
=
VOYAGER_CAT_ID
;
psi
.
asic
->
subaddr
=
VOYAGER_SUBADDR_HI
;
psi
.
module_addr
=
VOYAGER_PSI
;
psi
.
scan_path_connected
=
0
;
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
/* Connect the PSI to the CAT Bus */
outb
(
VOYAGER_CAT_DESELECT
,
VOYAGER_CAT_CONFIG_PORT
);
outb
(
VOYAGER_PSI
,
VOYAGER_CAT_CONFIG_PORT
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_disconnect
(
&
psi
,
&
psi_asic
);
/* Read the status. NOTE: Need to read *all* the PSI regs here
* otherwise the cmn int will be reasserted */
for
(
i
=
0
;
i
<
sizeof
(
psi_reg
.
regs
);
i
++
)
{
cat_read
(
&
psi
,
&
psi_asic
,
i
,
&
((
__u8
*
)
&
psi_reg
.
regs
)[
i
]);
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
if
((
psi_reg
.
regs
.
checkbit
&
0x02
)
==
0
)
{
psi_reg
.
regs
.
checkbit
|=
0x02
;
cat_write
(
&
psi
,
&
psi_asic
,
5
,
psi_reg
.
regs
.
checkbit
);
printk
(
"VOYAGER RE-READ PSI
\n
"
);
goto
re_read
;
}
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
for
(
i
=
0
;
i
<
sizeof
(
psi_reg
.
subregs
);
i
++
)
{
/* This looks strange, but the PSI doesn't do auto increment
* correctly */
cat_subread
(
&
psi
,
&
psi_asic
,
VOYAGER_PSI_SUPPLY_REG
+
i
,
1
,
&
((
__u8
*
)
&
psi_reg
.
subregs
)[
i
]);
}
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
#ifdef VOYAGER_CAT_DEBUG
printk
(
"VOYAGER PSI: "
);
for
(
i
=
0
;
i
<
sizeof
(
psi_reg
.
regs
);
i
++
)
printk
(
"%02x "
,
((
__u8
*
)
&
psi_reg
.
regs
)[
i
]);
printk
(
"
\n
"
);
for
(
i
=
0
;
i
<
sizeof
(
psi_reg
.
subregs
);
i
++
)
printk
(
"%02x "
,
((
__u8
*
)
&
psi_reg
.
subregs
)[
i
]);
printk
(
"
\n
"
);
#endif
if
(
psi_reg
.
regs
.
intstatus
&
PSI_MON
)
{
/* switch off or power fail */
if
(
psi_reg
.
subregs
.
supply
&
PSI_SWITCH_OFF
)
{
if
(
voyager_status
.
switch_off
)
{
printk
(
KERN_ERR
"Voyager front panel switch turned off again---Immediate power off!
\n
"
);
voyager_cat_power_off
();
/* not reached */
}
else
{
printk
(
KERN_ERR
"Voyager front panel switch turned off
\n
"
);
voyager_status
.
switch_off
=
1
;
voyager_status
.
request_from_kernel
=
1
;
up
(
&
kvoyagerd_sem
);
}
/* Tell the hardware we're taking care of the
* shutdown, otherwise it will power the box off
* within 3 seconds of the switch being pressed and,
* which is much more important to us, continue to
* assert the common interrupt */
data
=
PSI_CLR_SWITCH_OFF
;
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_subwrite
(
&
psi
,
&
psi_asic
,
VOYAGER_PSI_SUPPLY_REG
,
1
,
&
data
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
}
else
{
VDEBUG
((
"Voyager ac fail reg 0x%x
\n
"
,
psi_reg
.
subregs
.
ACfail
));
if
((
psi_reg
.
subregs
.
ACfail
&
AC_FAIL_STAT_CHANGE
)
==
0
)
{
/* No further update */
return
;
}
#if 0
/* Don't bother trying to find out who failed.
* FIXME: This probably makes the code incorrect on
* anything other than a 345x */
for(i=0; i< 5; i++) {
if( psi_reg.subregs.ACfail &(1<<i)) {
break;
}
}
printk(KERN_NOTICE "AC FAIL IN SUPPLY %d\n", i);
#endif
/* DON'T do this: it shuts down the AC PSI
outb(VOYAGER_CAT_RUN, CAT_CMD);
data = PSI_MASK_MASK | i;
cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_MASK,
1, &data);
outb(VOYAGER_CAT_END, CAT_CMD);
*/
printk
(
KERN_ERR
"Voyager AC power failure
\n
"
);
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
data
=
PSI_COLD_START
;
cat_subwrite
(
&
psi
,
&
psi_asic
,
VOYAGER_PSI_GENERAL_REG
,
1
,
&
data
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
voyager_status
.
power_fail
=
1
;
voyager_status
.
request_from_kernel
=
1
;
up
(
&
kvoyagerd_sem
);
}
}
else
if
(
psi_reg
.
regs
.
intstatus
&
PSI_FAULT
)
{
/* Major fault! */
printk
(
KERN_ERR
"Voyager PSI Detected major fault, immediate power off!
\n
"
);
voyager_cat_power_off
();
/* not reached */
}
else
if
(
psi_reg
.
regs
.
intstatus
&
(
PSI_DC_FAIL
|
PSI_ALARM
|
PSI_CURRENT
|
PSI_DVM
|
PSI_PSCFAULT
|
PSI_STAT_CHG
))
{
/* other psi fault */
printk
(
KERN_WARNING
"Voyager PSI status 0x%x
\n
"
,
data
);
/* clear the PSI fault */
outb
(
VOYAGER_CAT_RUN
,
CAT_CMD
);
cat_write
(
&
psi
,
&
psi_asic
,
VOYAGER_PSI_STATUS_REG
,
0
);
outb
(
VOYAGER_CAT_END
,
CAT_CMD
);
}
}
arch/i386/mach-voyager/voyager_smp.c
0 → 100644
View file @
6b3dcb38
/* -*- mode: c; c-basic-offset: 8 -*- */
/* Copyright (C) 1999,2001
*
* Author: J.E.J.Bottomley@HansenPartnership.com
*
* linux/arch/i386/kernel/voyager_smp.c
*
* This file provides all the same external entries as smp.c but uses
* the voyager hal to provide the functionality
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
#include <linux/mc146818rtc.h>
#include <linux/cache.h>
#include <linux/interrupt.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/bootmem.h>
#include <linux/completion.h>
#include <asm/desc.h>
#include <asm/voyager.h>
#include <asm/vic.h>
#include <asm/pgalloc.h>
#include <asm/mtrr.h>
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
#include <asm/smpboot.h>
#include <asm/desc.h>
#include <asm/arch_hooks.h>
#include <linux/irq.h>
int
reboot_smp
=
0
;
/* TLB state -- visible externally, indexed physically */
struct
tlb_state
cpu_tlbstate
[
NR_CPUS
]
__cacheline_aligned
=
{[
0
...
NR_CPUS
-
1
]
=
{
&
init_mm
,
0
}};
/* CPU IRQ affinity -- set to all ones initially */
static
unsigned
long
cpu_irq_affinity
[
NR_CPUS
]
__cacheline_aligned
=
{
[
0
...
NR_CPUS
-
1
]
=
~
0UL
};
/* Set when the idlers are all forked - Set in main.c but not actually
* used by any other parts of the kernel */
int
smp_threads_ready
=
0
;
/* per CPU data structure (for /proc/cpuinfo et al), visible externally
* indexed physically */
struct
cpuinfo_x86
cpu_data
[
NR_CPUS
]
__cacheline_aligned
;
/* Per CPU interrupt stacks */
extern
union
thread_union
init_irq_union
;
union
thread_union
*
irq_stacks
[
NR_CPUS
]
__cacheline_aligned
=
{
&
init_irq_union
,
};
/* physical ID of the CPU used to boot the system */
unsigned
char
boot_cpu_id
;
/* The memory line addresses for the Quad CPIs */
struct
voyager_qic_cpi
*
voyager_quad_cpi_addr
[
NR_CPUS
]
__cacheline_aligned
;
/* The masks for the Extended VIC processors, filled in by cat_init */
__u32
voyager_extended_vic_processors
=
0
;
/* Masks for the extended Quad processors which cannot be VIC booted */
__u32
voyager_allowed_boot_processors
=
0
;
/* The mask for the Quad Processors (both extended and non-extended) */
__u32
voyager_quad_processors
=
0
;
/* Total count of live CPUs, used in process.c to display
* the CPU information and in irq.c for the per CPU irq
* activity count. Finally exported by i386_ksyms.c */
static
int
voyager_extended_cpus
=
1
;
/* Have we found an SMP box - used by time.c to do the profiling
interrupt for timeslicing; do not set to 1 until the per CPU timer
interrupt is active */
int
smp_found_config
=
0
;
/* Used for the invalidate map that's also checked in the spinlock */
volatile
unsigned
long
smp_invalidate_needed
;
/* Bitmask of currently online CPUs - used by setup.c for
/proc/cpuinfo, visible externally but still physical */
unsigned
long
cpu_online_map
=
0
;
/* Bitmask of CPUs present in the system - exported by i386_syms.c, used
* by scheduler but indexed physically */
unsigned
long
phys_cpu_present_map
=
0
;
/* estimate of time used to flush the SMP-local cache - used in
* processor affinity calculations */
cycles_t
cacheflush_time
=
0
;
/* cache decay ticks for scheduler---a fairly useless quantity for the
voyager system with its odd affinity and huge L3 cache */
unsigned
long
cache_decay_ticks
=
20
;
/* The internal functions */
static
void
send_CPI
(
__u32
cpuset
,
__u8
cpi
);
static
void
ack_CPI
(
__u8
cpi
);
static
int
ack_QIC_CPI
(
__u8
cpi
);
static
void
ack_special_QIC_CPI
(
__u8
cpi
);
static
void
ack_VIC_CPI
(
__u8
cpi
);
static
void
send_CPI_allbutself
(
__u8
cpi
);
static
void
enable_vic_irq
(
unsigned
int
irq
);
static
void
disable_vic_irq
(
unsigned
int
irq
);
static
unsigned
int
startup_vic_irq
(
unsigned
int
irq
);
static
void
enable_local_vic_irq
(
unsigned
int
irq
);
static
void
disable_local_vic_irq
(
unsigned
int
irq
);
static
void
before_handle_vic_irq
(
unsigned
int
irq
);
static
void
after_handle_vic_irq
(
unsigned
int
irq
);
static
void
set_vic_irq_affinity
(
unsigned
int
irq
,
unsigned
long
mask
);
static
void
ack_vic_irq
(
unsigned
int
irq
);
static
void
vic_enable_cpi
(
void
);
static
void
do_boot_cpu
(
__u8
cpuid
);
static
void
do_quad_bootstrap
(
void
);
static
inline
void
wrapper_smp_local_timer_interrupt
(
struct
pt_regs
*
);
int
hard_smp_processor_id
(
void
);
/* Inline functions */
static
inline
void
send_one_QIC_CPI
(
__u8
cpu
,
__u8
cpi
)
{
voyager_quad_cpi_addr
[
cpu
]
->
qic_cpi
[
cpi
].
cpi
=
(
smp_processor_id
()
<<
16
)
+
cpi
;
}
static
inline
void
send_QIC_CPI
(
__u32
cpuset
,
__u8
cpi
)
{
int
mask
;
__u8
cpu
;
for_each_cpu
(
cpu
,
mask
)
{
if
(
cpuset
&
(
1
<<
cpu
))
{
#ifdef VOYAGER_DEBUG
if
(
!
test_bit
(
cpu
,
cpu_online_map
))
VDEBUG
((
"CPU%d sending cpi %d to CPU%d not in cpu_online_map
\n
"
,
hard_smp_processor_id
(),
cpi
,
cpu
));
#endif
send_one_QIC_CPI
(
cpu
,
cpi
-
QIC_CPI_OFFSET
);
}
}
}
static
inline
void
send_one_CPI
(
__u8
cpu
,
__u8
cpi
)
{
if
(
voyager_quad_processors
&
(
1
<<
cpu
))
send_one_QIC_CPI
(
cpu
,
cpi
-
QIC_CPI_OFFSET
);
else
send_CPI
(
1
<<
cpu
,
cpi
);
}
static
inline
void
send_CPI_allbutself
(
__u8
cpi
)
{
__u8
cpu
=
smp_processor_id
();
__u32
mask
=
(
cpu_online_map
&
(
~
(
1
<<
cpu
)));
send_CPI
(
mask
,
cpi
);
}
static
inline
int
is_cpu_quad
(
void
)
{
__u8
cpumask
=
inb
(
VIC_PROC_WHO_AM_I
);
return
((
cpumask
&
QUAD_IDENTIFIER
)
==
QUAD_IDENTIFIER
);
}
static
inline
int
is_cpu_extended
(
void
)
{
__u8
cpu
=
hard_smp_processor_id
();
return
(
voyager_extended_vic_processors
&
(
1
<<
cpu
));
}
static
inline
int
is_cpu_vic_boot
(
void
)
{
__u8
cpu
=
hard_smp_processor_id
();
return
(
voyager_extended_vic_processors
&
voyager_allowed_boot_processors
&
(
1
<<
cpu
));
}
static
inline
void
ack_CPI
(
__u8
cpi
)
{
switch
(
cpi
)
{
case
VIC_CPU_BOOT_CPI
:
if
(
is_cpu_quad
()
&&
!
is_cpu_vic_boot
())
ack_QIC_CPI
(
cpi
);
else
ack_VIC_CPI
(
cpi
);
break
;
case
VIC_SYS_INT
:
case
VIC_CMN_INT
:
/* These are slightly strange. Even on the Quad card,
* They are vectored as VIC CPIs */
if
(
is_cpu_quad
())
ack_special_QIC_CPI
(
cpi
);
else
ack_VIC_CPI
(
cpi
);
break
;
default:
printk
(
"VOYAGER ERROR: CPI%d is in common CPI code
\n
"
,
cpi
);
break
;
}
}
/* local variables */
/* The VIC IRQ descriptors -- these look almost identical to the
* 8259 IRQs except that masks and things must be kept per processor
*/
static
struct
hw_interrupt_type
vic_irq_type
=
{
"VIC-level"
,
startup_vic_irq
,
/* startup */
disable_vic_irq
,
/* shutdown */
enable_vic_irq
,
/* enable */
disable_vic_irq
,
/* disable */
before_handle_vic_irq
,
/* ack */
after_handle_vic_irq
,
/* end */
set_vic_irq_affinity
,
/* affinity */
};
/* used to count up as CPUs are brought on line (starts at 0) */
static
int
cpucount
=
0
;
/* steal a page from the bottom of memory for the trampoline and
* squirrel its address away here. This will be in kernel virtual
* space */
static
__u32
trampoline_base
;
/* The per cpu profile stuff - used in smp_local_timer_interrupt */
static
unsigned
int
prof_multiplier
[
NR_CPUS
]
__cacheline_aligned
=
{
1
,
};
static
unsigned
int
prof_old_multiplier
[
NR_CPUS
]
__cacheline_aligned
=
{
1
,
};
static
unsigned
int
prof_counter
[
NR_CPUS
]
__cacheline_aligned
=
{
1
,
};
/* the map used to check if a CPU has booted */
static
__u32
cpu_booted_map
;
/* the synchronize flag used to hold all secondary CPUs spinning in
* a tight loop until the boot sequence is ready for them */
static
unsigned
long
smp_commenced_mask
=
0
;
/* This is for the new dynamic CPU boot code */
volatile
unsigned
long
cpu_callin_map
=
0
;
unsigned
long
cpu_callout_map
=
0
;
/* The per processor IRQ masks (these are usually kept in sync) */
static
__u16
vic_irq_mask
[
NR_CPUS
]
__cacheline_aligned
;
/* the list of IRQs to be enabled by the VIC_ENABLE_IRQ_CPI */
static
__u16
vic_irq_enable_mask
[
NR_CPUS
]
__cacheline_aligned
=
{
0
};
/* Lock for enable/disable of VIC interrupts */
static
spinlock_t
vic_irq_lock
__cacheline_aligned
=
SPIN_LOCK_UNLOCKED
;
/* The boot processor is correctly set up in PC mode when it
* comes up, but the secondaries need their master/slave 8259
* pairs initializing correctly */
/* Interrupt counters (per cpu) and total - used to try to
* even up the interrupt handling routines */
static
long
vic_intr_total
=
0
;
static
long
vic_intr_count
[
NR_CPUS
]
__cacheline_aligned
=
{
0
};
static
unsigned
long
vic_tick
[
NR_CPUS
]
__cacheline_aligned
=
{
0
};
/* Since we can only use CPI0, we fake all the other CPIs */
static
unsigned
long
vic_cpi_mailbox
[
NR_CPUS
]
__cacheline_aligned
;
/* debugging routine to read the isr of the cpu's pic */
static
inline
__u16
vic_read_isr
(
void
)
{
__u16
isr
;
outb
(
0x0b
,
0xa0
);
isr
=
inb
(
0xa0
)
<<
8
;
outb
(
0x0b
,
0x20
);
isr
|=
inb
(
0x20
);
return
isr
;
}
static
__init
void
qic_setup
(
void
)
{
if
(
!
is_cpu_quad
())
{
/* not a quad, no setup */
return
;
}
outb
(
QIC_DEFAULT_MASK0
,
QIC_MASK_REGISTER0
);
outb
(
QIC_CPI_ENABLE
,
QIC_MASK_REGISTER1
);
if
(
is_cpu_extended
())
{
/* the QIC duplicate of the VIC base register */
outb
(
VIC_DEFAULT_CPI_BASE
,
QIC_VIC_CPI_BASE_REGISTER
);
outb
(
QIC_DEFAULT_CPI_BASE
,
QIC_CPI_BASE_REGISTER
);
/* FIXME: should set up the QIC timer and memory parity
* error vectors here */
}
}
static
__init
void
vic_setup_pic
(
void
)
{
outb
(
1
,
VIC_REDIRECT_REGISTER_1
);
/* clear the claim registers for dynamic routing */
outb
(
0
,
VIC_CLAIM_REGISTER_0
);
outb
(
0
,
VIC_CLAIM_REGISTER_1
);
outb
(
0
,
VIC_PRIORITY_REGISTER
);
/* Set the Primary and Secondary Microchannel vector
* bases to be the same as the ordinary interrupts
*
* FIXME: This would be more efficient using separate
* vectors. */
outb
(
FIRST_EXTERNAL_VECTOR
,
VIC_PRIMARY_MC_BASE
);
outb
(
FIRST_EXTERNAL_VECTOR
,
VIC_SECONDARY_MC_BASE
);
/* Now initiallise the master PIC belonging to this CPU by
* sending the four ICWs */
/* ICW1: level triggered, ICW4 needed */
outb
(
0x19
,
0x20
);
/* ICW2: vector base */
outb
(
FIRST_EXTERNAL_VECTOR
,
0x21
);
/* ICW3: slave at line 2 */
outb
(
0x04
,
0x21
);
/* ICW4: 8086 mode */
outb
(
0x01
,
0x21
);
/* now the same for the slave PIC */
/* ICW1: level trigger, ICW4 needed */
outb
(
0x19
,
0xA0
);
/* ICW2: slave vector base */
outb
(
FIRST_EXTERNAL_VECTOR
+
8
,
0xA1
);
/* ICW3: slave ID */
outb
(
0x02
,
0xA1
);
/* ICW4: 8086 mode */
outb
(
0x01
,
0xA1
);
}
static
void
do_quad_bootstrap
(
void
)
{
if
(
is_cpu_quad
()
&&
is_cpu_vic_boot
())
{
int
i
;
unsigned
long
flags
;
__u8
cpuid
=
hard_smp_processor_id
();
local_irq_save
(
flags
);
for
(
i
=
0
;
i
<
4
;
i
++
)
{
/* FIXME: this would be >>3 &0x7 on the 32 way */
if
(((
cpuid
>>
2
)
&
0x03
)
==
i
)
/* don't lower our own mask! */
continue
;
/* masquerade as local Quad CPU */
outb
(
QIC_CPUID_ENABLE
|
i
,
QIC_PROCESSOR_ID
);
/* enable the startup CPI */
outb
(
QIC_BOOT_CPI_MASK
,
QIC_MASK_REGISTER1
);
/* restore cpu id */
outb
(
0
,
QIC_PROCESSOR_ID
);
}
local_irq_restore
(
flags
);
}
}
/* Set up all the basic stuff: read the SMP config and make all the
* SMP information reflect only the boot cpu. All others will be
* brought on-line later. */
void
__init
find_smp_config
(
void
)
{
int
i
;
boot_cpu_id
=
hard_smp_processor_id
();
printk
(
"VOYAGER SMP: Boot cpu is %d
\n
"
,
boot_cpu_id
);
/* initialize the CPU structures (moved from smp_boot_cpus) */
for
(
i
=
0
;
i
<
NR_CPUS
;
i
++
)
{
prof_counter
[
i
]
=
1
;
prof_old_multiplier
[
i
]
=
1
;
prof_multiplier
[
i
]
=
1
;
cpu_irq_affinity
[
i
]
=
~
0
;
}
cpu_online_map
=
(
1
<<
boot_cpu_id
);
/* The boot CPU must be extended */
voyager_extended_vic_processors
=
1
<<
boot_cpu_id
;
/* initially, all of the first 8 cpu's can boot */
voyager_allowed_boot_processors
=
0xff
;
/* set up everything for just this CPU, we can alter
* this as we start the other CPUs later */
/* now get the CPU disposition from the extended CMOS */
phys_cpu_present_map
=
voyager_extended_cmos_read
(
VOYAGER_PROCESSOR_PRESENT_MASK
);
phys_cpu_present_map
|=
voyager_extended_cmos_read
(
VOYAGER_PROCESSOR_PRESENT_MASK
+
1
)
<<
8
;
phys_cpu_present_map
|=
voyager_extended_cmos_read
(
VOYAGER_PROCESSOR_PRESENT_MASK
+
2
)
<<
16
;
phys_cpu_present_map
|=
voyager_extended_cmos_read
(
VOYAGER_PROCESSOR_PRESENT_MASK
+
3
)
<<
24
;
printk
(
"VOYAGER SMP: phys_cpu_present_map = 0x%lx
\n
"
,
phys_cpu_present_map
);
/* Here we set up the VIC to enable SMP */
/* enable the CPIs by writing the base vector to their register */
outb
(
VIC_DEFAULT_CPI_BASE
,
VIC_CPI_BASE_REGISTER
);
outb
(
1
,
VIC_REDIRECT_REGISTER_1
);
/* set the claim registers for static routing --- Boot CPU gets
* all interrupts untill all other CPUs started */
outb
(
0xff
,
VIC_CLAIM_REGISTER_0
);
outb
(
0xff
,
VIC_CLAIM_REGISTER_1
);
/* Set the Primary and Secondary Microchannel vector
* bases to be the same as the ordinary interrupts
*
* FIXME: This would be more efficient using separate
* vectors. */
outb
(
FIRST_EXTERNAL_VECTOR
,
VIC_PRIMARY_MC_BASE
);
outb
(
FIRST_EXTERNAL_VECTOR
,
VIC_SECONDARY_MC_BASE
);
/* Finally tell the firmware that we're driving */
outb
(
inb
(
VOYAGER_SUS_IN_CONTROL_PORT
)
|
VOYAGER_IN_CONTROL_FLAG
,
VOYAGER_SUS_IN_CONTROL_PORT
);
current_thread_info
()
->
cpu
=
boot_cpu_id
;
}
/*
* The bootstrap kernel entry code has set these up. Save them
* for a given CPU, id is physical */
void
__init
smp_store_cpu_info
(
int
id
)
{
struct
cpuinfo_x86
*
c
=&
cpu_data
[
id
];
*
c
=
boot_cpu_data
;
identify_cpu
(
c
);
}
/* set up the trampoline and return the physical address of the code */
static
__u32
__init
setup_trampoline
(
void
)
{
/* these two are global symbols in trampoline.S */
extern
__u8
trampoline_end
[];
extern
__u8
trampoline_data
[];
memcpy
((
__u8
*
)
trampoline_base
,
trampoline_data
,
trampoline_end
-
trampoline_data
);
return
virt_to_phys
((
__u8
*
)
trampoline_base
);
}
/* Routine initially called when a non-boot CPU is brought online */
int
__init
start_secondary
(
void
*
unused
)
{
__u8
cpuid
=
hard_smp_processor_id
();
/* external functions not defined in the headers */
extern
void
calibrate_delay
(
void
);
extern
int
cpu_idle
(
void
);
cpu_init
();
/* OK, we're in the routine */
ack_CPI
(
VIC_CPU_BOOT_CPI
);
/* setup the 8259 master slave pair belonging to this CPU ---
* we won't actually receive any until the boot CPU
* relinquishes it's static routing mask */
vic_setup_pic
();
qic_setup
();
if
(
is_cpu_quad
()
&&
!
is_cpu_vic_boot
())
{
/* clear the boot CPI */
__u8
dummy
;
dummy
=
voyager_quad_cpi_addr
[
cpuid
]
->
qic_cpi
[
VIC_CPU_BOOT_CPI
].
cpi
;
printk
(
"read dummy %d
\n
"
,
dummy
);
}
/* lower the mask to receive CPIs */
vic_enable_cpi
();
VDEBUG
((
"VOYAGER SMP: CPU%d, stack at about %p
\n
"
,
cpuid
,
&
cpuid
));
/* enable interrupts */
local_irq_enable
();
/* get our bogomips */
calibrate_delay
();
/* save our processor parameters */
smp_store_cpu_info
(
cpuid
);
/* if we're a quad, we may need to bootstrap other CPUs */
do_quad_bootstrap
();
set_bit
(
cpuid
,
&
cpu_callin_map
);
/* signal that we're done */
cpu_booted_map
=
1
;
while
(
!
test_bit
(
cpuid
,
&
smp_commenced_mask
))
rep_nop
();
local_flush_tlb
();
set_bit
(
cpuid
,
&
cpu_online_map
);
wmb
();
return
cpu_idle
();
}
static
struct
task_struct
*
__init
fork_by_hand
(
void
)
{
struct
pt_regs
regs
;
/* don't care about the eip and regs settings since we'll
* never reschedule the forked task. */
return
do_fork
(
CLONE_VM
|
CLONE_IDLETASK
,
0
,
&
regs
,
0
,
NULL
);
}
static
void
__init
setup_irq_stack
(
struct
task_struct
*
p
,
int
cpu
)
{
unsigned
long
stk
;
stk
=
__get_free_pages
(
GFP_KERNEL
,
THREAD_ORDER
+
1
);
if
(
!
stk
)
panic
(
"I can't seem to allocate my irq stack. Oh well, giving up."
);
irq_stacks
[
cpu
]
=
(
void
*
)
stk
;
memset
(
irq_stacks
[
cpu
],
0
,
THREAD_SIZE
);
irq_stacks
[
cpu
]
->
thread_info
.
cpu
=
cpu
;
irq_stacks
[
cpu
]
->
thread_info
.
preempt_count
=
1
;
/* interrupts are not preemptable */
p
->
thread_info
->
irq_stack
=
irq_stacks
[
cpu
];
/* If we want to make the irq stack more than one unit
* deep, we can chain then off of the irq_stack pointer
* here.
*/
}
/* Routine to kick start the given CPU and wait for it to report ready
* (or timeout in startup). When this routine returns, the requested
* CPU is either fully running and configured or known to be dead.
*
* We call this routine sequentially 1 CPU at a time, so no need for
* locking */
static
void
__init
do_boot_cpu
(
__u8
cpu
)
{
struct
task_struct
*
idle
;
int
timeout
;
unsigned
long
flags
;
int
quad_boot
=
(
1
<<
cpu
)
&
voyager_quad_processors
&
~
(
voyager_extended_vic_processors
&
voyager_allowed_boot_processors
);
/* For the 486, we can't use the 4Mb page table trick, so
* must map a region of memory */
#ifdef CONFIG_M486
int
i
;
unsigned
long
*
page_table_copies
=
(
unsigned
long
*
)
__get_free_page
(
GFP_KERNEL
);
#endif
pgd_t
orig_swapper_pg_dir0
;
/* This is an area in head.S which was used to set up the
* initial kernel stack. We need to alter this to give the
* booting CPU a new stack (taken from its idle process) */
extern
struct
{
__u8
*
esp
;
unsigned
short
ss
;
}
stack_start
;
/* This is the format of the CPI IDT gate (in real mode) which
* we're hijacking to boot the CPU */
union
IDTFormat
{
struct
seg
{
__u16
Offset
;
__u16
Segment
;
}
idt
;
__u32
val
;
}
hijack_source
;
__u32
*
hijack_vector
;
__u32
start_phys_address
=
setup_trampoline
();
/* There's a clever trick to this: The linux trampoline is
* compiled to begin at absolute location zero, so make the
* address zero but have the data segment selector compensate
* for the actual address */
hijack_source
.
idt
.
Offset
=
start_phys_address
&
0x000F
;
hijack_source
.
idt
.
Segment
=
(
start_phys_address
>>
4
)
&
0xFFFF
;
cpucount
++
;
idle
=
fork_by_hand
();
if
(
IS_ERR
(
idle
))
panic
(
"failed fork for CPU%d"
,
cpu
);
setup_irq_stack
(
idle
,
cpu
);
init_idle
(
idle
,
cpu
);
idle
->
thread
.
eip
=
(
unsigned
long
)
start_secondary
;
unhash_process
(
idle
);
/* The -4 is to correct for the fact that the stack pointer
* is used to find the location of the thread_info structure
* by masking off several of the LSBs. Without the -4, esp
* is pointing to the page after the one the stack is on.
*/
stack_start
.
esp
=
(
void
*
)(
THREAD_SIZE
-
4
+
(
char
*
)
idle
->
thread_info
);
/* Note: Don't modify initial ss override */
VDEBUG
((
"VOYAGER SMP: Booting CPU%d at 0x%lx[%x:%x], stack %p
\n
"
,
cpu
,
(
unsigned
long
)
hijack_source
.
val
,
hijack_source
.
idt
.
Segment
,
hijack_source
.
idt
.
Offset
,
stack_start
.
esp
));
/* set the original swapper_pg_dir[0] to map 0 to 4Mb transparently
* (so that the booting CPU can find start_32 */
orig_swapper_pg_dir0
=
swapper_pg_dir
[
0
];
#ifdef CONFIG_M486
if
(
page_table_copies
==
NULL
)
panic
(
"No free memory for 486 page tables
\n
"
);
for
(
i
=
0
;
i
<
PAGE_SIZE
/
sizeof
(
unsigned
long
);
i
++
)
page_table_copies
[
i
]
=
(
i
*
PAGE_SIZE
)
|
_PAGE_RW
|
_PAGE_USER
|
_PAGE_PRESENT
;
((
unsigned
long
*
)
swapper_pg_dir
)[
0
]
=
((
virt_to_phys
(
page_table_copies
))
&
PAGE_MASK
)
|
_PAGE_RW
|
_PAGE_USER
|
_PAGE_PRESENT
;
#else
((
unsigned
long
*
)
swapper_pg_dir
)[
0
]
=
0x102007
;
#endif
if
(
quad_boot
)
{
printk
(
"CPU %d: non extended Quad boot
\n
"
,
cpu
);
hijack_vector
=
(
__u32
*
)
phys_to_virt
((
VIC_CPU_BOOT_CPI
+
QIC_DEFAULT_CPI_BASE
)
*
4
);
*
hijack_vector
=
hijack_source
.
val
;
}
else
{
printk
(
"CPU%d: extended VIC boot
\n
"
,
cpu
);
hijack_vector
=
(
__u32
*
)
phys_to_virt
((
VIC_CPU_BOOT_CPI
+
VIC_DEFAULT_CPI_BASE
)
*
4
);
*
hijack_vector
=
hijack_source
.
val
;
/* VIC errata, may also receive interrupt at this address */
hijack_vector
=
(
__u32
*
)
phys_to_virt
((
VIC_CPU_BOOT_ERRATA_CPI
+
VIC_DEFAULT_CPI_BASE
)
*
4
);
*
hijack_vector
=
hijack_source
.
val
;
}
/* All non-boot CPUs start with interrupts fully masked. Need
* to lower the mask of the CPI we're about to send. We do
* this in the VIC by masquerading as the processor we're
* about to boot and lowering its interrupt mask */
local_irq_save
(
flags
);
if
(
quad_boot
)
{
send_one_QIC_CPI
(
cpu
,
VIC_CPU_BOOT_CPI
);
}
else
{
outb
(
VIC_CPU_MASQUERADE_ENABLE
|
cpu
,
VIC_PROCESSOR_ID
);
/* here we're altering registers belonging to `cpu' */
outb
(
VIC_BOOT_INTERRUPT_MASK
,
0x21
);
/* now go back to our original identity */
outb
(
boot_cpu_id
,
VIC_PROCESSOR_ID
);
/* and boot the CPU */
send_CPI
((
1
<<
cpu
),
VIC_CPU_BOOT_CPI
);
}
cpu_booted_map
=
0
;
local_irq_restore
(
flags
);
/* now wait for it to become ready (or timeout) */
for
(
timeout
=
0
;
timeout
<
50000
;
timeout
++
)
{
if
(
cpu_booted_map
)
break
;
udelay
(
100
);
}
/* reset the page table */
swapper_pg_dir
[
0
]
=
orig_swapper_pg_dir0
;
local_flush_tlb
();
#ifdef CONFIG_M486
free_page
((
unsigned
long
)
page_table_copies
);
#endif
if
(
cpu_booted_map
)
{
VDEBUG
((
"CPU%d: Booted successfully, back in CPU %d
\n
"
,
cpu
,
smp_processor_id
()));
printk
(
"CPU%d: "
,
cpu
);
print_cpu_info
(
&
cpu_data
[
cpu
]);
wmb
();
set_bit
(
cpu
,
&
cpu_callout_map
);
}
else
{
printk
(
"CPU%d FAILED TO BOOT: "
,
cpu
);
if
(
*
((
volatile
unsigned
char
*
)
phys_to_virt
(
start_phys_address
))
==
0xA5
)
printk
(
"Stuck.
\n
"
);
else
printk
(
"Not responding.
\n
"
);
cpucount
--
;
}
}
void
__init
smp_boot_cpus
(
void
)
{
int
i
;
/* CAT BUS initialisation must be done after the memory */
/* FIXME: The L4 has a catbus too, it just needs to be
* accessed in a totally different way */
if
(
voyager_level
==
5
)
{
voyager_cat_init
();
/* now that the cat has probed the Voyager System Bus, sanity
* check the cpu map */
if
(
((
voyager_quad_processors
|
voyager_extended_vic_processors
)
&
phys_cpu_present_map
)
!=
phys_cpu_present_map
)
{
/* should panic */
printk
(
"
\n\n
***WARNING*** Sanity check of CPU present map FAILED
\n
"
);
}
}
else
if
(
voyager_level
==
4
)
{
voyager_extended_vic_processors
=
phys_cpu_present_map
;
}
/* this sets up the idle task to run on the current cpu */
voyager_extended_cpus
=
1
;
/* Remove the global_irq_holder setting, it triggers a BUG() on
* schedule at the moment */
//global_irq_holder = boot_cpu_id;
/* FIXME: Need to do something about this but currently only works
* on CPUs with a tsc which none of mine have.
smp_tune_scheduling();
*/
smp_store_cpu_info
(
boot_cpu_id
);
printk
(
"CPU%d: "
,
boot_cpu_id
);
print_cpu_info
(
&
cpu_data
[
boot_cpu_id
]);
if
(
is_cpu_quad
())
{
/* booting on a Quad CPU */
printk
(
"VOYAGER SMP: Boot CPU is Quad
\n
"
);
qic_setup
();
do_quad_bootstrap
();
}
/* enable our own CPIs */
vic_enable_cpi
();
/* loop over all the extended VIC CPUs and boot them. The
* Quad CPUs must be bootstrapped by their extended VIC cpu */
for
(
i
=
0
;
i
<
NR_CPUS
;
i
++
)
{
if
(
i
==
boot_cpu_id
||
((
1
<<
i
)
&
(
phys_cpu_present_map
)
)
==
0
)
continue
;
do_boot_cpu
(
i
);
/* This udelay seems to be needed for the Quad boots
* don't remove unless you know what you're doing */
udelay
(
1000
);
}
/* we could compute the total bogomips here, but why bother?,
* Code added from smpboot.c */
{
unsigned
long
bogosum
=
0
;
for
(
i
=
0
;
i
<
NR_CPUS
;
i
++
)
if
(
cpu_online_map
&
(
1
<<
i
))
bogosum
+=
cpu_data
[
i
].
loops_per_jiffy
;
printk
(
KERN_INFO
"Total of %d processors activated (%lu.%02lu BogoMIPS).
\n
"
,
cpucount
+
1
,
bogosum
/
(
500000
/
HZ
),
(
bogosum
/
(
5000
/
HZ
))
%
100
);
}
voyager_extended_cpus
=
hweight32
(
voyager_extended_vic_processors
);
printk
(
"VOYAGER: Extended (interrupt handling CPUs): %d, non-extended: %d
\n
"
,
voyager_extended_cpus
,
num_booting_cpus
()
-
voyager_extended_cpus
);
/* that's it, switch to symmetric mode */
outb
(
0
,
VIC_PRIORITY_REGISTER
);
outb
(
0
,
VIC_CLAIM_REGISTER_0
);
outb
(
0
,
VIC_CLAIM_REGISTER_1
);
VDEBUG
((
"VOYAGER SMP: Booted with %d CPUs
\n
"
,
num_booting_cpus
()));
}
/* Reload the secondary CPUs task structure (this function does not
* return ) */
void
__init
initialize_secondary
(
void
)
{
#if 0
// AC kernels only
set_current(hard_get_current());
#endif
/*
* We don't actually need to load the full TSS,
* basically just the stack pointer and the eip.
*/
asm
volatile
(
"movl %0,%%esp
\n\t
"
"jmp *%1"
:
:
"r"
(
current
->
thread
.
esp
),
"r"
(
current
->
thread
.
eip
));
}
/* handle a Voyager SYS_INT -- If we don't, the base board will
* panic the system.
*
* System interrupts occur because some problem was detected on the
* various busses. To find out what you have to probe all the
* hardware via the CAT bus. FIXME: At the moment we do nothing. */
asmlinkage
void
smp_vic_sys_interrupt
(
void
)
{
ack_CPI
(
VIC_SYS_INT
);
printk
(
"Voyager SYSTEM INTERRUPT
\n
"
);
}
/* Handle a voyager CMN_INT; These interrupts occur either because of
* a system status change or because a single bit memory error
* occurred. FIXME: At the moment, ignore all this. */
asmlinkage
void
smp_vic_cmn_interrupt
(
void
)
{
static
__u8
in_cmn_int
=
0
;
static
spinlock_t
cmn_int_lock
=
SPIN_LOCK_UNLOCKED
;
/* common ints are broadcast, so make sure we only do this once */
_raw_spin_lock
(
&
cmn_int_lock
);
if
(
in_cmn_int
)
goto
unlock_end
;
in_cmn_int
++
;
_raw_spin_unlock
(
&
cmn_int_lock
);
VDEBUG
((
"Voyager COMMON INTERRUPT
\n
"
));
if
(
voyager_level
==
5
)
voyager_cat_do_common_interrupt
();
_raw_spin_lock
(
&
cmn_int_lock
);
in_cmn_int
=
0
;
unlock_end:
_raw_spin_unlock
(
&
cmn_int_lock
);
ack_CPI
(
VIC_CMN_INT
);
}
/*
* Reschedule call back. Nothing to do, all the work is done
* automatically when we return from the interrupt. */
asmlinkage
void
smp_reschedule_interrupt
(
void
)
{
/* do nothing */
}
static
struct
mm_struct
*
flush_mm
;
static
unsigned
long
flush_va
;
static
spinlock_t
tlbstate_lock
=
SPIN_LOCK_UNLOCKED
;
#define FLUSH_ALL 0xffffffff
/*
* We cannot call mmdrop() because we are in interrupt context,
* instead update mm->cpu_vm_mask.
*
* We need to reload %cr3 since the page tables may be going
* away from under us..
*/
static
void
inline
leave_mm
(
unsigned
long
cpu
)
{
if
(
cpu_tlbstate
[
cpu
].
state
==
TLBSTATE_OK
)
BUG
();
clear_bit
(
cpu
,
&
cpu_tlbstate
[
cpu
].
active_mm
->
cpu_vm_mask
);
load_cr3
(
swapper_pg_dir
);
}
/*
* Invalidate call-back
*/
asmlinkage
void
smp_invalidate_interrupt
(
void
)
{
__u8
cpu
=
get_cpu
();
if
(
!
test_bit
(
cpu
,
&
smp_invalidate_needed
))
goto
out
;
/* This will flood messages. Don't uncomment unless you see
* Problems with cross cpu invalidation
VDEBUG(("VOYAGER SMP: CPU%d received INVALIDATE_CPI\n",
smp_processor_id()));
*/
if
(
flush_mm
==
cpu_tlbstate
[
cpu
].
active_mm
)
{
if
(
cpu_tlbstate
[
cpu
].
state
==
TLBSTATE_OK
)
{
if
(
flush_va
==
FLUSH_ALL
)
local_flush_tlb
();
else
__flush_tlb_one
(
flush_va
);
}
else
leave_mm
(
cpu
);
}
clear_bit
(
cpu
,
&
smp_invalidate_needed
);
out:
put_cpu_no_resched
();
}
/* All the new flush operations for 2.4 */
/* This routine is called with a physical cpu mask */
static
void
flush_tlb_others
(
unsigned
long
cpumask
,
struct
mm_struct
*
mm
,
unsigned
long
va
)
{
int
stuck
=
50000
;
if
(
!
cpumask
)
BUG
();
if
((
cpumask
&
cpu_online_map
)
!=
cpumask
)
BUG
();
if
(
cpumask
&
(
1
<<
smp_processor_id
()))
BUG
();
if
(
!
mm
)
BUG
();
spin_lock
(
&
tlbstate_lock
);
flush_mm
=
mm
;
flush_va
=
va
;
atomic_set_mask
(
cpumask
,
&
smp_invalidate_needed
);
/*
* We have to send the CPI only to
* CPUs affected.
*/
send_CPI
(
cpumask
,
VIC_INVALIDATE_CPI
);
while
(
smp_invalidate_needed
)
{
if
(
--
stuck
==
0
)
{
printk
(
"***WARNING*** Stuck doing invalidate CPI (CPU%d)
\n
"
,
smp_processor_id
());
break
;
}
}
/* Uncomment only to debug invalidation problems
VDEBUG(("VOYAGER SMP: Completed invalidate CPI (CPU%d)\n", cpu));
*/
flush_mm
=
NULL
;
flush_va
=
0
;
spin_unlock
(
&
tlbstate_lock
);
}
void
flush_tlb_current_task
(
void
)
{
struct
mm_struct
*
mm
=
current
->
mm
;
unsigned
long
cpu_mask
;
preempt_disable
();
cpu_mask
=
mm
->
cpu_vm_mask
&
~
(
1
<<
smp_processor_id
());
local_flush_tlb
();
if
(
cpu_mask
)
flush_tlb_others
(
cpu_mask
,
mm
,
FLUSH_ALL
);
preempt_enable
();
}
void
flush_tlb_mm
(
struct
mm_struct
*
mm
)
{
unsigned
long
cpu_mask
;
preempt_disable
();
cpu_mask
=
mm
->
cpu_vm_mask
&
~
(
1
<<
smp_processor_id
());
if
(
current
->
active_mm
==
mm
)
{
if
(
current
->
mm
)
local_flush_tlb
();
else
leave_mm
(
smp_processor_id
());
}
if
(
cpu_mask
)
flush_tlb_others
(
cpu_mask
,
mm
,
FLUSH_ALL
);
preempt_enable
();
}
void
flush_tlb_page
(
struct
vm_area_struct
*
vma
,
unsigned
long
va
)
{
struct
mm_struct
*
mm
=
vma
->
vm_mm
;
unsigned
long
cpu_mask
;
preempt_disable
();
cpu_mask
=
mm
->
cpu_vm_mask
&
~
(
1
<<
smp_processor_id
());
if
(
current
->
active_mm
==
mm
)
{
if
(
current
->
mm
)
__flush_tlb_one
(
va
);
else
leave_mm
(
smp_processor_id
());
}
if
(
cpu_mask
)
flush_tlb_others
(
cpu_mask
,
mm
,
va
);
preempt_enable
();
}
/* enable the requested IRQs */
asmlinkage
void
smp_enable_irq_interrupt
(
void
)
{
__u8
irq
;
__u8
cpu
=
get_cpu
();
VDEBUG
((
"VOYAGER SMP: CPU%d enabling irq mask 0x%x
\n
"
,
cpu
,
vic_irq_enable_mask
[
cpu
]));
spin_lock
(
&
vic_irq_lock
);
for
(
irq
=
0
;
irq
<
16
;
irq
++
)
{
if
(
vic_irq_enable_mask
[
cpu
]
&
(
1
<<
irq
))
enable_local_vic_irq
(
irq
);
}
vic_irq_enable_mask
[
cpu
]
=
0
;
spin_unlock
(
&
vic_irq_lock
);
put_cpu_no_resched
();
}
/*
* CPU halt call-back
*/
static
void
smp_stop_cpu_function
(
void
*
dummy
)
{
VDEBUG
((
"VOYAGER SMP: CPU%d is STOPPING
\n
"
,
smp_processor_id
()));
clear_bit
(
smp_processor_id
(),
&
cpu_online_map
);
local_irq_disable
();
for
(;;)
__asm__
(
"hlt"
);
}
static
spinlock_t
call_lock
=
SPIN_LOCK_UNLOCKED
;
struct
call_data_struct
{
void
(
*
func
)
(
void
*
info
);
void
*
info
;
volatile
unsigned
long
started
;
volatile
unsigned
long
finished
;
int
wait
;
};
static
struct
call_data_struct
*
call_data
;
/* execute a thread on a new CPU. The function to be called must be
* previously set up. This is used to schedule a function for
* execution on all CPU's - set up the function then broadcast a
* function_interrupt CPI to come here on each CPU */
asmlinkage
void
smp_call_function_interrupt
(
void
)
{
void
(
*
func
)
(
void
*
info
)
=
call_data
->
func
;
void
*
info
=
call_data
->
info
;
/* must take copy of wait because call_data may be replaced
* unless the function is waiting for us to finish */
int
wait
=
call_data
->
wait
;
__u8
cpu
=
smp_processor_id
();
/*
* Notify initiating CPU that I've grabbed the data and am
* about to execute the function
*/
mb
();
if
(
!
test_and_clear_bit
(
cpu
,
&
call_data
->
started
))
{
/* If the bit wasn't set, this could be a replay */
printk
(
KERN_WARNING
"VOYAGER SMP: CPU %d received call funtion with no call pending
\n
"
,
cpu
);
return
;
}
/*
* At this point the info structure may be out of scope unless wait==1
*/
irq_enter
();
(
*
func
)(
info
);
irq_exit
();
if
(
wait
)
{
mb
();
clear_bit
(
cpu
,
&
call_data
->
finished
);
}
}
/* Call this function on all CPUs using the function_interrupt above
<func> The function to run. This must be fast and non-blocking.
<info> An arbitrary pointer to pass to the function.
<retry> If true, keep retrying until ready.
<wait> If true, wait until function has completed on other CPUs.
[RETURNS] 0 on success, else a negative status code. Does not return until
remote CPUs are nearly ready to execute <<func>> or are or have executed.
*/
int
smp_call_function
(
void
(
*
func
)
(
void
*
info
),
void
*
info
,
int
retry
,
int
wait
)
{
struct
call_data_struct
data
;
__u32
mask
=
cpu_online_map
;
mask
&=
~
(
1
<<
smp_processor_id
());
if
(
!
mask
)
return
0
;
data
.
func
=
func
;
data
.
info
=
info
;
data
.
started
=
mask
;
data
.
wait
=
wait
;
if
(
wait
)
data
.
finished
=
mask
;
spin_lock
(
&
call_lock
);
call_data
=
&
data
;
wmb
();
/* Send a message to all other CPUs and wait for them to respond */
send_CPI_allbutself
(
VIC_CALL_FUNCTION_CPI
);
/* Wait for response */
while
(
data
.
started
)
barrier
();
if
(
wait
)
while
(
data
.
finished
)
barrier
();
spin_unlock
(
&
call_lock
);
return
0
;
}
/* Sorry about the name. In an APIC based system, the APICs
* themselves are programmed to send a timer interrupt. This is used
* by linux to reschedule the processor. Voyager doesn't have this,
* so we use the system clock to interrupt one processor, which in
* turn, broadcasts a timer CPI to all the others --- we receive that
* CPI here. We don't use this actually for counting so losing
* ticks doesn't matter
*
* FIXME: For those CPU's which actually have a local APIC, we could
* try to use it to trigger this interrupt instead of having to
* broadcast the timer tick. Unfortunately, all my pentium DYADs have
* no local APIC, so I can't do this
*
* This function is currently a placeholder and is unused in the code */
asmlinkage
void
smp_apic_timer_interrupt
(
struct
pt_regs
regs
)
{
wrapper_smp_local_timer_interrupt
(
&
regs
);
}
/* All of the QUAD interrupt GATES */
asmlinkage
void
smp_qic_timer_interrupt
(
struct
pt_regs
regs
)
{
ack_QIC_CPI
(
QIC_TIMER_CPI
);
wrapper_smp_local_timer_interrupt
(
&
regs
);
}
asmlinkage
void
smp_qic_invalidate_interrupt
(
void
)
{
ack_QIC_CPI
(
QIC_INVALIDATE_CPI
);
smp_invalidate_interrupt
();
}
asmlinkage
void
smp_qic_reschedule_interrupt
(
void
)
{
ack_QIC_CPI
(
QIC_RESCHEDULE_CPI
);
smp_reschedule_interrupt
();
}
asmlinkage
void
smp_qic_enable_irq_interrupt
(
void
)
{
ack_QIC_CPI
(
QIC_ENABLE_IRQ_CPI
);
smp_enable_irq_interrupt
();
}
asmlinkage
void
smp_qic_call_function_interrupt
(
void
)
{
ack_QIC_CPI
(
QIC_CALL_FUNCTION_CPI
);
smp_call_function_interrupt
();
}
asmlinkage
void
smp_vic_cpi_interrupt
(
struct
pt_regs
regs
)
{
__u8
cpu
=
smp_processor_id
();
if
(
is_cpu_quad
())
ack_QIC_CPI
(
VIC_CPI_LEVEL0
);
else
ack_VIC_CPI
(
VIC_CPI_LEVEL0
);
if
(
test_and_clear_bit
(
VIC_TIMER_CPI
,
&
vic_cpi_mailbox
[
cpu
]))
wrapper_smp_local_timer_interrupt
(
&
regs
);
if
(
test_and_clear_bit
(
VIC_INVALIDATE_CPI
,
&
vic_cpi_mailbox
[
cpu
]))
smp_invalidate_interrupt
();
if
(
test_and_clear_bit
(
VIC_RESCHEDULE_CPI
,
&
vic_cpi_mailbox
[
cpu
]))
smp_reschedule_interrupt
();
if
(
test_and_clear_bit
(
VIC_ENABLE_IRQ_CPI
,
&
vic_cpi_mailbox
[
cpu
]))
smp_enable_irq_interrupt
();
if
(
test_and_clear_bit
(
VIC_CALL_FUNCTION_CPI
,
&
vic_cpi_mailbox
[
cpu
]))
smp_call_function_interrupt
();
}
static
inline
void
do_flush_tlb_all_local
(
void
)
{
unsigned
long
cpu
=
smp_processor_id
();
__flush_tlb_all
();
if
(
cpu_tlbstate
[
cpu
].
state
==
TLBSTATE_LAZY
)
leave_mm
(
cpu
);
}
static
void
flush_tlb_all_function
(
void
*
info
)
{
do_flush_tlb_all_local
();
}
/* flush the TLB of every active CPU in the system */
void
flush_tlb_all
(
void
)
{
smp_call_function
(
flush_tlb_all_function
,
0
,
1
,
1
);
do_flush_tlb_all_local
();
}
/* used to set up the trampoline for other CPUs when the memory manager
* is sorted out */
void
__init
smp_alloc_memory
(
void
)
{
trampoline_base
=
(
__u32
)
alloc_bootmem_low_pages
(
PAGE_SIZE
);
if
(
__pa
(
trampoline_base
)
>=
0x93000
)
BUG
();
}
/* send a reschedule CPI to one CPU by physical CPU number*/
void
smp_send_reschedule
(
int
cpu
)
{
send_one_CPI
(
cpu
,
VIC_RESCHEDULE_CPI
);
}
int
hard_smp_processor_id
(
void
)
{
__u8
i
;
__u8
cpumask
=
inb
(
VIC_PROC_WHO_AM_I
);
if
((
cpumask
&
QUAD_IDENTIFIER
)
==
QUAD_IDENTIFIER
)
return
cpumask
&
0x1F
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
if
(
cpumask
&
(
1
<<
i
))
return
i
;
}
printk
(
"** WARNING ** Illegal cpuid returned by VIC: %d"
,
cpumask
);
return
0
;
}
/* broadcast a halt to all other CPUs */
void
smp_send_stop
(
void
)
{
smp_call_function
(
smp_stop_cpu_function
,
NULL
,
1
,
1
);
}
/* this function is triggered in time.c when a clock tick fires
* we need to re-broadcast the tick to all CPUs */
void
smp_vic_timer_interrupt
(
struct
pt_regs
*
regs
)
{
send_CPI_allbutself
(
VIC_TIMER_CPI
);
smp_local_timer_interrupt
(
regs
);
}
static
inline
void
wrapper_smp_local_timer_interrupt
(
struct
pt_regs
*
regs
)
{
__u8
cpu
=
smp_processor_id
();
irq_enter
();
smp_local_timer_interrupt
(
regs
);
irq_exit
();
}
/* local (per CPU) timer interrupt. It does both profiling and
* process statistics/rescheduling.
*
* We do profiling in every local tick, statistics/rescheduling
* happen only every 'profiling multiplier' ticks. The default
* multiplier is 1 and it can be changed by writing the new multiplier
* value into /proc/profile.
*/
void
smp_local_timer_interrupt
(
struct
pt_regs
*
regs
)
{
int
cpu
=
smp_processor_id
();
long
weight
;
x86_do_profile
(
regs
);
if
(
--
prof_counter
[
cpu
]
<=
0
)
{
/*
* The multiplier may have changed since the last time we got
* to this point as a result of the user writing to
* /proc/profile. In this case we need to adjust the APIC
* timer accordingly.
*
* Interrupts are already masked off at this point.
*/
prof_counter
[
cpu
]
=
prof_multiplier
[
cpu
];
if
(
prof_counter
[
cpu
]
!=
prof_old_multiplier
[
cpu
])
{
/* FIXME: need to update the vic timer tick here */
prof_old_multiplier
[
cpu
]
=
prof_counter
[
cpu
];
}
update_process_times
(
user_mode
(
regs
));
}
if
(
((
1
<<
cpu
)
&
voyager_extended_vic_processors
)
==
0
)
/* only extended VIC processors participate in
* interrupt distribution */
return
;
/*
* We take the 'long' return path, and there every subsystem
* grabs the apropriate locks (kernel lock/ irq lock).
*
* we might want to decouple profiling from the 'long path',
* and do the profiling totally in assembly.
*
* Currently this isn't too much of an issue (performance wise),
* we can take more than 100K local irqs per second on a 100 MHz P5.
*/
if
((
++
vic_tick
[
cpu
]
&
0x7
)
!=
0
)
return
;
/* get here every 16 ticks (about every 1/6 of a second) */
/* Change our priority to give someone else a chance at getting
* the IRQ. The algorithm goes like this:
*
* In the VIC, the dynamically routed interrupt is always
* handled by the lowest priority eligible (i.e. receiving
* interrupts) CPU. If >1 eligible CPUs are equal lowest, the
* lowest processor number gets it.
*
* The priority of a CPU is controlled by a special per-CPU
* VIC priority register which is 3 bits wide 0 being lowest
* and 7 highest priority..
*
* Therefore we subtract the average number of interrupts from
* the number we've fielded. If this number is negative, we
* lower the activity count and if it is positive, we raise
* it.
*
* I'm afraid this still leads to odd looking interrupt counts:
* the totals are all roughly equal, but the individual ones
* look rather skewed.
*
* FIXME: This algorithm is total crap when mixed with SMP
* affinity code since we now try to even up the interrupt
* counts when an affinity binding is keeping them on a
* particular CPU*/
weight
=
(
vic_intr_count
[
cpu
]
*
voyager_extended_cpus
-
vic_intr_total
)
>>
4
;
weight
+=
4
;
if
(
weight
>
7
)
weight
=
7
;
if
(
weight
<
0
)
weight
=
0
;
outb
((
__u8
)
weight
,
VIC_PRIORITY_REGISTER
);
#ifdef VOYAGER_DEBUG
if
((
vic_tick
[
cpu
]
&
0xFFF
)
==
0
)
{
/* print this message roughly every 25 secs */
printk
(
"VOYAGER SMP: vic_tick[%d] = %lu, weight = %ld
\n
"
,
cpu
,
vic_tick
[
cpu
],
weight
);
}
#endif
}
/* setup the profiling timer */
int
setup_profiling_timer
(
unsigned
int
multiplier
)
{
int
i
;
if
(
(
!
multiplier
))
return
-
EINVAL
;
/*
* Set the new multiplier for each CPU. CPUs don't start using the
* new values until the next timer interrupt in which they do process
* accounting.
*/
for
(
i
=
0
;
i
<
NR_CPUS
;
++
i
)
prof_multiplier
[
i
]
=
multiplier
;
return
0
;
}
/* The CPIs are handled in the per cpu 8259s, so they must be
* enabled to be received: FIX: enabling the CPIs in the early
* boot sequence interferes with bug checking; enable them later
* on in smp_init */
#define VIC_SET_GATE(cpi, vector) \
set_intr_gate((cpi) + VIC_DEFAULT_CPI_BASE, (vector))
#define QIC_SET_GATE(cpi, vector) \
set_intr_gate((cpi) + QIC_DEFAULT_CPI_BASE, (vector))
void
__init
smp_intr_init
(
void
)
{
int
i
;
/* initialize the per cpu irq mask to all disabled */
for
(
i
=
0
;
i
<
NR_CPUS
;
i
++
)
vic_irq_mask
[
i
]
=
0xFFFF
;
VIC_SET_GATE
(
VIC_CPI_LEVEL0
,
vic_cpi_interrupt
);
VIC_SET_GATE
(
VIC_SYS_INT
,
vic_sys_interrupt
);
VIC_SET_GATE
(
VIC_CMN_INT
,
vic_cmn_interrupt
);
QIC_SET_GATE
(
QIC_TIMER_CPI
,
qic_timer_interrupt
);
QIC_SET_GATE
(
QIC_INVALIDATE_CPI
,
qic_invalidate_interrupt
);
QIC_SET_GATE
(
QIC_RESCHEDULE_CPI
,
qic_reschedule_interrupt
);
QIC_SET_GATE
(
QIC_ENABLE_IRQ_CPI
,
qic_enable_irq_interrupt
);
QIC_SET_GATE
(
QIC_CALL_FUNCTION_CPI
,
qic_call_function_interrupt
);
/* now put the VIC descriptor into the first 48 IRQs
*
* This is for later: first 16 correspond to PC IRQs; next 16
* are Primary MC IRQs and final 16 are Secondary MC IRQs */
for
(
i
=
0
;
i
<
48
;
i
++
)
irq_desc
[
i
].
handler
=
&
vic_irq_type
;
}
/* send a CPI at level cpi to a set of cpus in cpuset (set 1 bit per
* processor to recieve CPI */
static
void
send_CPI
(
__u32
cpuset
,
__u8
cpi
)
{
int
mask
;
__u8
cpu
;
__u32
quad_cpuset
=
(
cpuset
&
voyager_quad_processors
);
if
(
cpi
<
VIC_START_FAKE_CPI
)
{
/* fake CPI are only used for booting, so send to the
* extended quads as well---Quads must be VIC booted */
outb
((
__u8
)(
cpuset
),
VIC_CPI_Registers
[
cpi
]);
return
;
}
if
(
quad_cpuset
)
send_QIC_CPI
(
quad_cpuset
,
cpi
);
cpuset
&=
~
quad_cpuset
;
cpuset
&=
0xff
;
/* only first 8 CPUs vaild for VIC CPI */
if
(
cpuset
==
0
)
return
;
for_each_cpu
(
cpu
,
mask
)
{
if
(
cpuset
&
(
1
<<
cpu
))
set_bit
(
cpi
,
&
vic_cpi_mailbox
[
cpu
]);
}
if
(
cpuset
)
outb
((
__u8
)
cpuset
,
VIC_CPI_Registers
[
VIC_CPI_LEVEL0
]);
}
/* Acknowlege receipt of CPI in the QIC, clear in QIC hardware and
* set the cache line to shared by reading it.
*
* DON'T make this inline otherwise the cache line read will be
* optimised away
* */
static
int
ack_QIC_CPI
(
__u8
cpi
)
{
__u8
cpu
=
hard_smp_processor_id
();
cpi
&=
7
;
outb
(
1
<<
cpi
,
QIC_INTERRUPT_CLEAR1
);
return
voyager_quad_cpi_addr
[
cpu
]
->
qic_cpi
[
cpi
].
cpi
;
}
static
void
ack_special_QIC_CPI
(
__u8
cpi
)
{
switch
(
cpi
)
{
case
VIC_CMN_INT
:
outb
(
QIC_CMN_INT
,
QIC_INTERRUPT_CLEAR0
);
break
;
case
VIC_SYS_INT
:
outb
(
QIC_SYS_INT
,
QIC_INTERRUPT_CLEAR0
);
break
;
}
/* also clear at the VIC, just in case (nop for non-extended proc) */
ack_VIC_CPI
(
cpi
);
}
/* Acknowledge receipt of CPI in the VIC (essentially an EOI) */
static
void
ack_VIC_CPI
(
__u8
cpi
)
{
#ifdef VOYAGER_DEBUG
unsigned
long
flags
;
__u16
isr
;
__u8
cpu
=
smp_processor_id
();
local_irq_save
(
flags
);
isr
=
vic_read_isr
();
if
((
isr
&
(
1
<<
(
cpi
&
7
)))
==
0
)
{
printk
(
"VOYAGER SMP: CPU%d lost CPI%d
\n
"
,
cpu
,
cpi
);
}
#endif
/* send specific EOI; the two system interrupts have
* bit 4 set for a separate vector but behave as the
* corresponding 3 bit intr */
outb_p
(
0x60
|
(
cpi
&
7
),
0x20
);
#ifdef VOYAGER_DEBUG
if
((
vic_read_isr
()
&
(
1
<<
(
cpi
&
7
)))
!=
0
)
{
printk
(
"VOYAGER SMP: CPU%d still asserting CPI%d
\n
"
,
cpu
,
cpi
);
}
local_irq_restore
(
flags
);
#endif
}
/* cribbed with thanks from irq.c */
#define __byte(x,y) (((unsigned char *)&(y))[x])
#define cached_21(cpu) (__byte(0,vic_irq_mask[cpu]))
#define cached_A1(cpu) (__byte(1,vic_irq_mask[cpu]))
static
unsigned
int
startup_vic_irq
(
unsigned
int
irq
)
{
enable_vic_irq
(
irq
);
return
0
;
}
/* The enable and disable routines. This is where we run into
* conflicting architectural philosophy. Fundamentally, the voyager
* architecture does not expect to have to disable interrupts globally
* (the IRQ controllers belong to each CPU). The processor masquerade
* which is used to start the system shouldn't be used in a running OS
* since it will cause great confusion if two separate CPUs drive to
* the same IRQ controller (I know, I've tried it).
*
* The solution is a variant on the NCR lazy SPL design:
*
* 1) To disable an interrupt, do nothing (other than set the
* IRQ_DISABLED flag). This dares the interrupt actually to arrive.
*
* 2) If the interrupt dares to come in, raise the local mask against
* it (this will result in all the CPU masks being raised
* eventually).
*
* 3) To enable the interrupt, lower the mask on the local CPU and
* broadcast an Interrupt enable CPI which causes all other CPUs to
* adjust their masks accordingly. */
static
void
enable_vic_irq
(
unsigned
int
irq
)
{
int
tmpmask
;
/* linux doesn't to processor-irq affinity, so enable on
* all CPUs we know about */
__u8
cpu
=
smp_processor_id
(),
real_cpu
;
__u16
mask
=
(
1
<<
irq
);
__u32
processorList
=
0
;
unsigned
long
flags
;
VDEBUG
((
"VOYAGER: enable_vic_irq(%d) CPU%d affinity 0x%lx
\n
"
,
irq
,
cpu
,
cpu_irq_affinity
[
cpu
]));
spin_lock_irqsave
(
&
vic_irq_lock
,
flags
);
for_each_cpu
(
real_cpu
,
tmpmask
)
{
if
(
!
(
voyager_extended_vic_processors
&
(
1
<<
real_cpu
)))
continue
;
if
(
!
(
cpu_irq_affinity
[
real_cpu
]
&
mask
))
{
/* irq has no affinity for this CPU, ignore */
continue
;
}
if
(
real_cpu
==
cpu
)
{
enable_local_vic_irq
(
irq
);
}
else
if
(
vic_irq_mask
[
real_cpu
]
&
mask
)
{
vic_irq_enable_mask
[
real_cpu
]
|=
mask
;
processorList
|=
(
1
<<
real_cpu
);
}
}
spin_unlock_irqrestore
(
&
vic_irq_lock
,
flags
);
if
(
processorList
)
send_CPI
(
processorList
,
VIC_ENABLE_IRQ_CPI
);
}
static
void
disable_vic_irq
(
unsigned
int
irq
)
{
/* lazy disable, do nothing */
}
static
void
enable_local_vic_irq
(
unsigned
int
irq
)
{
__u8
cpu
=
smp_processor_id
();
__u16
mask
=
~
(
1
<<
irq
);
__u16
old_mask
=
vic_irq_mask
[
cpu
];
vic_irq_mask
[
cpu
]
&=
mask
;
if
(
vic_irq_mask
[
cpu
]
==
old_mask
)
return
;
VDEBUG
((
"VOYAGER DEBUG: Enabling irq %d in hardware on CPU %d
\n
"
,
irq
,
cpu
));
if
(
irq
&
8
)
{
outb_p
(
cached_A1
(
cpu
),
0xA1
);
(
void
)
inb_p
(
0xA1
);
}
else
{
outb_p
(
cached_21
(
cpu
),
0x21
);
(
void
)
inb_p
(
0x21
);
}
}
static
void
disable_local_vic_irq
(
unsigned
int
irq
)
{
__u8
cpu
=
smp_processor_id
();
__u16
mask
=
(
1
<<
irq
);
__u16
old_mask
=
vic_irq_mask
[
cpu
];
if
(
irq
==
7
)
return
;
vic_irq_mask
[
cpu
]
|=
mask
;
if
(
old_mask
==
vic_irq_mask
[
cpu
])
return
;
VDEBUG
((
"VOYAGER DEBUG: Disabling irq %d in hardware on CPU %d
\n
"
,
irq
,
cpu
));
if
(
irq
&
8
)
{
outb_p
(
cached_A1
(
cpu
),
0xA1
);
(
void
)
inb_p
(
0xA1
);
}
else
{
outb_p
(
cached_21
(
cpu
),
0x21
);
(
void
)
inb_p
(
0x21
);
}
}
/* The VIC is level triggered, so the ack can only be issued after the
* interrupt completes. However, we do Voyager lazy interrupt
* handling here: It is an extremely expensive operation to mask an
* interrupt in the vic, so we merely set a flag (IRQ_DISABLED). If
* this interrupt actually comes in, then we mask and ack here to push
* the interrupt off to another CPU */
static
void
before_handle_vic_irq
(
unsigned
int
irq
)
{
irq_desc_t
*
desc
=
irq_desc
+
irq
;
__u8
cpu
=
smp_processor_id
();
_raw_spin_lock
(
&
vic_irq_lock
);
vic_intr_total
++
;
vic_intr_count
[
cpu
]
++
;
if
(
!
(
cpu_irq_affinity
[
cpu
]
&
(
1
<<
irq
)))
{
/* The irq is not in our affinity mask, push it off
* onto another CPU */
VDEBUG
((
"VOYAGER DEBUG: affinity triggered disable of irq %d on cpu %d
\n
"
,
irq
,
cpu
));
disable_local_vic_irq
(
irq
);
/* set IRQ_INPROGRESS to prevent the handler in irq.c from
* actually calling the interrupt routine */
desc
->
status
|=
IRQ_REPLAY
|
IRQ_INPROGRESS
;
}
else
if
(
desc
->
status
&
IRQ_DISABLED
)
{
/* Damn, the interrupt actually arrived, do the lazy
* disable thing. The interrupt routine in irq.c will
* not handle a IRQ_DISABLED interrupt, so nothing more
* need be done here */
VDEBUG
((
"VOYAGER DEBUG: lazy disable of irq %d on CPU %d
\n
"
,
irq
,
cpu
));
disable_local_vic_irq
(
irq
);
desc
->
status
|=
IRQ_REPLAY
;
}
else
{
desc
->
status
&=
~
IRQ_REPLAY
;
}
_raw_spin_unlock
(
&
vic_irq_lock
);
}
/* Finish the VIC interrupt: basically mask */
static
void
after_handle_vic_irq
(
unsigned
int
irq
)
{
irq_desc_t
*
desc
=
irq_desc
+
irq
;
_raw_spin_lock
(
&
vic_irq_lock
);
{
unsigned
int
status
=
desc
->
status
&
~
IRQ_INPROGRESS
;
#ifdef VOYAGER_DEBUG
__u16
isr
;
#endif
desc
->
status
=
status
;
if
((
status
&
IRQ_DISABLED
))
disable_local_vic_irq
(
irq
);
#ifdef VOYAGER_DEBUG
/* DEBUG: before we ack, check what's in progress */
isr
=
vic_read_isr
();
if
((
isr
&
(
1
<<
irq
)
&&
!
(
status
&
IRQ_REPLAY
))
==
0
)
{
int
i
;
__u8
cpu
=
smp_processor_id
();
__u8
real_cpu
;
int
mask
;
printk
(
"VOYAGER SMP: CPU%d lost interrupt %d
\n
"
,
cpu
,
irq
);
for_each_cpu
(
real_cpu
,
mask
)
{
outb
(
VIC_CPU_MASQUERADE_ENABLE
|
real_cpu
,
VIC_PROCESSOR_ID
);
isr
=
vic_read_isr
();
if
(
isr
&
(
1
<<
irq
))
{
printk
(
"VOYAGER SMP: CPU%d ack irq %d
\n
"
,
real_cpu
,
irq
);
ack_vic_irq
(
irq
);
}
outb
(
cpu
,
VIC_PROCESSOR_ID
);
}
}
#endif
/* VOYAGER_DEBUG */
/* as soon as we ack, the interrupt is eligible for
* receipt by another CPU so everything must be in
* order here */
ack_vic_irq
(
irq
);
if
(
status
&
IRQ_REPLAY
)
{
/* replay is set if we disable the interrupt
* in the before_handle_vic_irq() routine, so
* clear the in progress bit here to allow the
* next CPU to handle this correctly */
desc
->
status
&=
~
(
IRQ_REPLAY
|
IRQ_INPROGRESS
);
}
#ifdef VOYAGER_DEBUG
isr
=
vic_read_isr
();
if
((
isr
&
(
1
<<
irq
))
!=
0
)
printk
(
"VOYAGER SMP: after_handle_vic_irq() after ack irq=%d, isr=0x%x
\n
"
,
irq
,
isr
);
#endif
/* VOYAGER_DEBUG */
}
_raw_spin_unlock
(
&
vic_irq_lock
);
/* All code after this point is out of the main path - the IRQ
* may be intercepted by another CPU if reasserted */
}
/* Linux processor - interrupt affinity manipulations.
*
* For each processor, we maintain a 32 bit irq affinity mask.
* Initially it is set to all 1's so every processor accepts every
* interrupt. In this call, we change the processor's affinity mask:
*
* Change from enable to disable:
*
* If the interrupt ever comes in to the processor, we will disable it
* and ack it to push it off to another CPU, so just accept the mask here.
*
* Change from disable to enable:
*
* change the mask and then do an interrupt enable CPI to re-enable on
* the selected processors */
void
set_vic_irq_affinity
(
unsigned
int
irq
,
unsigned
long
mask
)
{
/* Only extended processors handle interrupts */
unsigned
long
real_mask
=
mask
&
voyager_extended_vic_processors
;
unsigned
long
irq_mask
=
(
1
<<
irq
);
int
tmpmask
;
__u8
cpu
;
if
(
mask
==
0
)
/* can't have no cpu's to accept the interrupt -- extremely
* bad things will happen */
return
;
if
(
irq
==
0
)
/* can't change the affinity of the timer IRQ. This
* is due to the constraint in the voyager
* architecture that the CPI also comes in on and IRQ
* line and we have chosen IRQ0 for this. If you
* raise the mask on this interrupt, the processor
* will no-longer be able to accept VIC CPIs */
return
;
if
(
irq
>=
32
)
/* You can only have 32 interrupts in a voyager system
* (and 32 only if you have a secondary microchannel
* bus) */
return
;
for_each_cpu
(
cpu
,
tmpmask
)
{
unsigned
long
cpu_mask
=
(
1
<<
cpu
);
if
(
cpu_mask
&
real_mask
)
{
/* enable the interrupt for this cpu */
cpu_irq_affinity
[
cpu
]
|=
irq_mask
;
}
else
{
/* disable the interrupt for this cpu */
cpu_irq_affinity
[
cpu
]
&=
~
irq_mask
;
}
}
/* this is magic, we now have the correct affinity maps, so
* enable the interrupt. This will send an enable CPI to
* those cpu's who need to enable it in their local masks,
* causing them to correct for the new affinity . If the
* interrupt is currently globally disabled, it will simply be
* disabled again as it comes in (voyager lazy disable). If
* the affinity map is tightened to disable the interrupt on a
* cpu, it will be pushed off when it comes in */
enable_vic_irq
(
irq
);
}
static
void
ack_vic_irq
(
unsigned
int
irq
)
{
if
(
irq
&
8
)
{
outb
(
0x62
,
0x20
);
/* Specific EOI to cascade */
outb
(
0x60
|
(
irq
&
7
),
0xA0
);
}
else
{
outb
(
0x60
|
(
irq
&
7
),
0x20
);
}
}
/* enable the CPIs. In the VIC, the CPIs are delivered by the 8259
* but are not vectored by it. This means that the 8259 mask must be
* lowered to receive them */
static
__init
void
vic_enable_cpi
(
void
)
{
__u8
cpu
=
smp_processor_id
();
/* just take a copy of the current mask (nop for boot cpu) */
vic_irq_mask
[
cpu
]
=
vic_irq_mask
[
boot_cpu_id
];
enable_local_vic_irq
(
VIC_CPI_LEVEL0
);
enable_local_vic_irq
(
VIC_CPI_LEVEL1
);
/* for sys int and cmn int */
enable_local_vic_irq
(
7
);
if
(
is_cpu_quad
())
{
outb
(
QIC_DEFAULT_MASK0
,
QIC_MASK_REGISTER0
);
outb
(
QIC_CPI_ENABLE
,
QIC_MASK_REGISTER1
);
VDEBUG
((
"VOYAGER SMP: QIC ENABLE CPI: CPU%d: MASK 0x%x
\n
"
,
cpu
,
QIC_CPI_ENABLE
));
}
VDEBUG
((
"VOYAGER SMP: ENABLE CPI: CPU%d: MASK 0x%x
\n
"
,
cpu
,
vic_irq_mask
[
cpu
]));
}
void
voyager_smp_dump
()
{
int
mask
;
__u8
old_cpu
=
smp_processor_id
(),
cpu
;
/* dump the interrupt masks of each processor */
for_each_cpu
(
cpu
,
mask
)
{
__u16
imr
,
isr
,
irr
;
unsigned
long
flags
;
local_irq_save
(
flags
);
outb
(
VIC_CPU_MASQUERADE_ENABLE
|
cpu
,
VIC_PROCESSOR_ID
);
imr
=
(
inb
(
0xa1
)
<<
8
)
|
inb
(
0x21
);
outb
(
0x0a
,
0xa0
);
irr
=
inb
(
0xa0
)
<<
8
;
outb
(
0x0a
,
0x20
);
irr
|=
inb
(
0x20
);
outb
(
0x0b
,
0xa0
);
isr
=
inb
(
0xa0
)
<<
8
;
outb
(
0x0b
,
0x20
);
isr
|=
inb
(
0x20
);
outb
(
old_cpu
,
VIC_PROCESSOR_ID
);
local_irq_restore
(
flags
);
printk
(
"
\t
CPU%d: mask=0x%x, IMR=0x%x, IRR=0x%x, ISR=0x%x
\n
"
,
cpu
,
vic_irq_mask
[
cpu
],
imr
,
irr
,
isr
);
#if 0
/* These lines are put in to try to unstick an un ack'd irq */
if(isr != 0) {
int irq;
for(irq=0; irq<16; irq++) {
if(isr & (1<<irq)) {
printk("\tCPU%d: ack irq %d\n",
cpu, irq);
local_irq_save(flags);
outb(VIC_CPU_MASQUERADE_ENABLE | cpu,
VIC_PROCESSOR_ID);
ack_vic_irq(irq);
outb(old_cpu, VIC_PROCESSOR_ID);
local_irq_restore(flags);
}
}
}
#endif
}
}
void
smp_voyager_power_off
(
void
*
dummy
)
{
if
(
smp_processor_id
()
==
boot_cpu_id
)
voyager_power_off
();
else
smp_stop_cpu_function
(
NULL
);
}
void
__init
smp_prepare_cpus
(
unsigned
int
max_cpus
)
{
/* FIXME: ignore max_cpus for now */
smp_boot_cpus
();
}
int
__devinit
__cpu_up
(
unsigned
int
cpu
)
{
/* This only works at boot for x86. See "rewrite" above. */
if
(
test_bit
(
cpu
,
&
smp_commenced_mask
))
return
-
ENOSYS
;
/* In case one didn't come up */
if
(
!
test_bit
(
cpu
,
&
cpu_callin_map
))
return
-
EIO
;
/* Unleash the CPU! */
set_bit
(
cpu
,
&
smp_commenced_mask
);
while
(
!
test_bit
(
cpu
,
&
cpu_online_map
))
mb
();
return
0
;
}
void
__init
smp_cpus_done
(
unsigned
int
max_cpus
)
{
zap_low_mappings
();
}
arch/i386/mach-voyager/voyager_thread.c
0 → 100644
View file @
6b3dcb38
/* -*- mode: c; c-basic-offset: 8 -*- */
/* Copyright (C) 2001
*
* Author: J.E.J.Bottomley@HansenPartnership.com
*
* linux/arch/i386/kernel/voyager_thread.c
*
* This module provides the machine status monitor thread for the
* voyager architecture. This allows us to monitor the machine
* environment (temp, voltage, fan function) and the front panel and
* internal UPS. If a fault is detected, this thread takes corrective
* action (usually just informing init)
* */
#include <linux/module.h>
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
#include <linux/mc146818rtc.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <linux/kmod.h>
#include <linux/completion.h>
#include <linux/sched.h>
#include <asm/desc.h>
#include <asm/voyager.h>
#include <asm/vic.h>
#include <asm/pgalloc.h>
#include <asm/mtrr.h>
#include <asm/msr.h>
#include <linux/irq.h>
#define THREAD_NAME "kvoyagerd"
/* external variables */
int
kvoyagerd_running
=
0
;
DECLARE_MUTEX_LOCKED
(
kvoyagerd_sem
);
static
int
thread
(
void
*
);
static
__u8
set_timeout
=
0
;
/* Start the machine monitor thread. Return 1 if OK, 0 if fail */
static
int
__init
voyager_thread_start
(
void
)
{
if
(
kernel_thread
(
thread
,
NULL
,
CLONE_KERNEL
)
<
0
)
{
/* This is serious, but not fatal */
printk
(
KERN_ERR
"Voyager: Failed to create system monitor thread!!!
\n
"
);
return
1
;
}
return
0
;
}
static
int
execute_helper
(
void
*
string
)
{
int
ret
;
char
*
envp
[]
=
{
"HOME=/"
,
"TERM=linux"
,
"PATH=/sbin:/usr/sbin:/bin:/usr/bin"
,
NULL
,
};
char
*
argv
[]
=
{
"/bin/bash"
,
"-c"
,
(
char
*
)
string
,
NULL
,
};
if
((
ret
=
exec_usermodehelper
(
argv
[
0
],
argv
,
envp
))
<
0
)
{
printk
(
KERN_ERR
"Voyager failed to execute
\"
%s
\"\n
"
,
(
char
*
)
string
);
}
return
ret
;
}
static
void
execute
(
char
*
string
)
{
if
(
kernel_thread
(
execute_helper
,
(
void
*
)
string
,
CLONE_VFORK
|
SIGCHLD
)
<
0
)
{
printk
(
KERN_ERR
"Voyager failed to fork before exec of
\"
%s
\"\n
"
,
string
);
}
}
static
void
check_from_kernel
(
void
)
{
if
(
voyager_status
.
switch_off
)
{
/* FIXME: This should be configureable via proc */
execute
(
"umask 600; echo 0 > /etc/initrunlvl; kill -HUP 1"
);
}
else
if
(
voyager_status
.
power_fail
)
{
VDEBUG
((
"Voyager daemon detected AC power failure
\n
"
));
/* FIXME: This should be configureable via proc */
execute
(
"umask 600; echo F > /etc/powerstatus; kill -PWR 1"
);
set_timeout
=
1
;
}
}
static
void
check_continuing_condition
(
void
)
{
if
(
voyager_status
.
power_fail
)
{
__u8
data
;
voyager_cat_psi
(
VOYAGER_PSI_SUBREAD
,
VOYAGER_PSI_AC_FAIL_REG
,
&
data
);
if
((
data
&
0x1f
)
==
0
)
{
/* all power restored */
printk
(
KERN_NOTICE
"VOYAGER AC power restored, cancelling shutdown
\n
"
);
/* FIXME: should be user configureable */
execute
(
"umask 600; echo O > /etc/powerstatus; kill -PWR 1"
);
set_timeout
=
0
;
}
}
}
static
void
wakeup
(
unsigned
long
unused
)
{
up
(
&
kvoyagerd_sem
);
}
static
int
thread
(
void
*
unused
)
{
struct
timer_list
wakeup_timer
;
kvoyagerd_running
=
1
;
reparent_to_init
();
daemonize
();
set_timeout
=
0
;
init_timer
(
&
wakeup_timer
);
strcpy
(
current
->
comm
,
THREAD_NAME
);
sigfillset
(
&
current
->
blocked
);
current
->
tty
=
NULL
;
/* get rid of controlling tty */
printk
(
KERN_NOTICE
"Voyager starting monitor thread
\n
"
);
for
(;;)
{
down_interruptible
(
&
kvoyagerd_sem
);
VDEBUG
((
"Voyager Daemon awoken
\n
"
));
if
(
voyager_status
.
request_from_kernel
==
0
)
{
/* probably awoken from timeout */
check_continuing_condition
();
}
else
{
check_from_kernel
();
voyager_status
.
request_from_kernel
=
0
;
}
if
(
set_timeout
)
{
del_timer
(
&
wakeup_timer
);
wakeup_timer
.
expires
=
HZ
+
jiffies
;
wakeup_timer
.
function
=
wakeup
;
add_timer
(
&
wakeup_timer
);
}
}
}
static
void
__exit
voyager_thread_stop
(
void
)
{
/* FIXME: do nothing at the moment */
}
module_init
(
voyager_thread_start
);
//module_exit(voyager_thread_stop);
include/asm-i386/voyager.h
0 → 100644
View file @
6b3dcb38
/* Copyright (C) 1999,2001
*
* Author: J.E.J.Bottomley@HansenPartnership.com
*
* Standard include definitions for the NCR Voyager system */
#undef VOYAGER_DEBUG
#undef VOYAGER_CAT_DEBUG
#ifdef VOYAGER_DEBUG
#define VDEBUG(x) printk x
#else
#define VDEBUG(x)
#endif
/* There are three levels of voyager machine: 3,4 and 5. The rule is
* if it's less than 3435 it's a Level 3 except for a 3360 which is
* a level 4. A 3435 or above is a Level 5 */
#define VOYAGER_LEVEL5_AND_ABOVE 0x3435
#define VOYAGER_LEVEL4 0x3360
/* The L4 DINO ASIC */
#define VOYAGER_DINO 0x43
/* voyager ports in standard I/O space */
#define VOYAGER_MC_SETUP 0x96
#define VOYAGER_CAT_CONFIG_PORT 0x97
# define VOYAGER_CAT_DESELECT 0xff
#define VOYAGER_SSPB_RELOCATION_PORT 0x98
/* Valid CAT controller commands */
/* start instruction register cycle */
#define VOYAGER_CAT_IRCYC 0x01
/* start data register cycle */
#define VOYAGER_CAT_DRCYC 0x02
/* move to execute state */
#define VOYAGER_CAT_RUN 0x0F
/* end operation */
#define VOYAGER_CAT_END 0x80
/* hold in idle state */
#define VOYAGER_CAT_HOLD 0x90
/* single step an "intest" vector */
#define VOYAGER_CAT_STEP 0xE0
/* return cat controller to CLEMSON mode */
#define VOYAGER_CAT_CLEMSON 0xFF
/* the default cat command header */
#define VOYAGER_CAT_HEADER 0x7F
/* the range of possible CAT module ids in the system */
#define VOYAGER_MIN_MODULE 0x10
#define VOYAGER_MAX_MODULE 0x1f
/* The voyager registers per asic */
#define VOYAGER_ASIC_ID_REG 0x00
#define VOYAGER_ASIC_TYPE_REG 0x01
/* the sub address registers can be made auto incrementing on reads */
#define VOYAGER_AUTO_INC_REG 0x02
# define VOYAGER_AUTO_INC 0x04
# define VOYAGER_NO_AUTO_INC 0xfb
#define VOYAGER_SUBADDRDATA 0x03
#define VOYAGER_SCANPATH 0x05
# define VOYAGER_CONNECT_ASIC 0x01
# define VOYAGER_DISCONNECT_ASIC 0xfe
#define VOYAGER_SUBADDRLO 0x06
#define VOYAGER_SUBADDRHI 0x07
#define VOYAGER_SUBMODSELECT 0x08
#define VOYAGER_SUBMODPRESENT 0x09
#define VOYAGER_SUBADDR_LO 0xff
#define VOYAGER_SUBADDR_HI 0xffff
/* the maximum size of a scan path -- used to form instructions */
#define VOYAGER_MAX_SCAN_PATH 0x100
/* the biggest possible register size (in bytes) */
#define VOYAGER_MAX_REG_SIZE 4
/* Total number of possible modules (including submodules) */
#define VOYAGER_MAX_MODULES 16
/* Largest number of asics per module */
#define VOYAGER_MAX_ASICS_PER_MODULE 7
/* the CAT asic of each module is always the first one */
#define VOYAGER_CAT_ID 0
#define VOYAGER_PSI 0x1a
/* voyager instruction operations and registers */
#define VOYAGER_READ_CONFIG 0x1
#define VOYAGER_WRITE_CONFIG 0x2
#define VOYAGER_BYPASS 0xff
typedef
struct
voyager_asic
{
__u8
asic_addr
;
/* ASIC address; Level 4 */
__u8
asic_type
;
/* ASIC type */
__u8
asic_id
;
/* ASIC id */
__u8
jtag_id
[
4
];
/* JTAG id */
__u8
asic_location
;
/* Location within scan path; start w/ 0 */
__u8
bit_location
;
/* Location within bit stream; start w/ 0 */
__u8
ireg_length
;
/* Instruction register length */
__u16
subaddr
;
/* Amount of sub address space */
struct
voyager_asic
*
next
;
/* Next asic in linked list */
}
voyager_asic_t
;
typedef
struct
voyager_module
{
__u8
module_addr
;
/* Module address */
__u8
scan_path_connected
;
/* Scan path connected */
__u16
ee_size
;
/* Size of the EEPROM */
__u16
num_asics
;
/* Number of Asics */
__u16
inst_bits
;
/* Instruction bits in the scan path */
__u16
largest_reg
;
/* Largest register in the scan path */
__u16
smallest_reg
;
/* Smallest register in the scan path */
voyager_asic_t
*
asic
;
/* First ASIC in scan path (CAT_I) */
struct
voyager_module
*
submodule
;
/* Submodule pointer */
struct
voyager_module
*
next
;
/* Next module in linked list */
}
voyager_module_t
;
typedef
struct
voyager_eeprom_hdr
{
__u8
module_id
[
4
]
__attribute__
((
packed
));
__u8
version_id
__attribute__
((
packed
));
__u8
config_id
__attribute__
((
packed
));
__u16
boundry_id
__attribute__
((
packed
));
/* boundary scan id */
__u16
ee_size
__attribute__
((
packed
));
/* size of EEPROM */
__u8
assembly
[
11
]
__attribute__
((
packed
));
/* assembly # */
__u8
assembly_rev
__attribute__
((
packed
));
/* assembly rev */
__u8
tracer
[
4
]
__attribute__
((
packed
));
/* tracer number */
__u16
assembly_cksum
__attribute__
((
packed
));
/* asm checksum */
__u16
power_consump
__attribute__
((
packed
));
/* pwr requirements */
__u16
num_asics
__attribute__
((
packed
));
/* number of asics */
__u16
bist_time
__attribute__
((
packed
));
/* min. bist time */
__u16
err_log_offset
__attribute__
((
packed
));
/* error log offset */
__u16
scan_path_offset
__attribute__
((
packed
));
/* scan path offset */
__u16
cct_offset
__attribute__
((
packed
));
__u16
log_length
__attribute__
((
packed
));
/* length of err log */
__u16
xsum_end
__attribute__
((
packed
));
/* offset to end of
checksum */
__u8
reserved
[
4
]
__attribute__
((
packed
));
__u8
sflag
__attribute__
((
packed
));
/* starting sentinal */
__u8
part_number
[
13
]
__attribute__
((
packed
));
/* prom part number */
__u8
version
[
10
]
__attribute__
((
packed
));
/* version number */
__u8
signature
[
8
]
__attribute__
((
packed
));
__u16
eeprom_chksum
__attribute__
((
packed
));
__u32
data_stamp_offset
__attribute__
((
packed
));
__u8
eflag
__attribute__
((
packed
));
/* ending sentinal */
}
voyager_eprom_hdr_t
;
#define VOYAGER_EPROM_SIZE_OFFSET ((__u16)(&(((voyager_eprom_hdr_t *)0)->ee_size)))
#define VOYAGER_XSUM_END_OFFSET 0x2a
/* the following three definitions are for internal table layouts
* in the module EPROMs. We really only care about the IDs and
* offsets */
typedef
struct
voyager_sp_table
{
__u8
asic_id
__attribute__
((
packed
));
__u8
bypass_flag
__attribute__
((
packed
));
__u16
asic_data_offset
__attribute__
((
packed
));
__u16
config_data_offset
__attribute__
((
packed
));
}
voyager_sp_table_t
;
typedef
struct
voyager_jtag_table
{
__u8
icode
[
4
]
__attribute__
((
packed
));
__u8
runbist
[
4
]
__attribute__
((
packed
));
__u8
intest
[
4
]
__attribute__
((
packed
));
__u8
samp_preld
[
4
]
__attribute__
((
packed
));
__u8
ireg_len
__attribute__
((
packed
));
}
voyager_jtt_t
;
typedef
struct
voyager_asic_data_table
{
__u8
jtag_id
[
4
]
__attribute__
((
packed
));
__u16
length_bsr
__attribute__
((
packed
));
__u16
length_bist_reg
__attribute__
((
packed
));
__u32
bist_clk
__attribute__
((
packed
));
__u16
subaddr_bits
__attribute__
((
packed
));
__u16
seed_bits
__attribute__
((
packed
));
__u16
sig_bits
__attribute__
((
packed
));
__u16
jtag_offset
__attribute__
((
packed
));
}
voyager_at_t
;
/* Voyager Interrupt Controller (VIC) registers */
/* Base to add to Cross Processor Interrupts (CPIs) when triggering
* the CPU IRQ line */
/* register defines for the WCBICs (one per processor) */
#define VOYAGER_WCBIC0 0x41
/* bus A node P1 processor 0 */
#define VOYAGER_WCBIC1 0x49
/* bus A node P1 processor 1 */
#define VOYAGER_WCBIC2 0x51
/* bus A node P2 processor 0 */
#define VOYAGER_WCBIC3 0x59
/* bus A node P2 processor 1 */
#define VOYAGER_WCBIC4 0x61
/* bus B node P1 processor 0 */
#define VOYAGER_WCBIC5 0x69
/* bus B node P1 processor 1 */
#define VOYAGER_WCBIC6 0x71
/* bus B node P2 processor 0 */
#define VOYAGER_WCBIC7 0x79
/* bus B node P2 processor 1 */
/* top of memory registers */
#define VOYAGER_WCBIC_TOM_L 0x4
#define VOYAGER_WCBIC_TOM_H 0x5
/* register defines for Voyager Memory Contol (VMC)
* these are present on L4 machines only */
#define VOYAGER_VMC1 0x81
#define VOYAGER_VMC2 0x91
#define VOYAGER_VMC3 0xa1
#define VOYAGER_VMC4 0xb1
/* VMC Ports */
#define VOYAGER_VMC_MEMORY_SETUP 0x9
# define VMC_Interleaving 0x01
# define VMC_4Way 0x02
# define VMC_EvenCacheLines 0x04
# define VMC_HighLine 0x08
# define VMC_Start0_Enable 0x20
# define VMC_Start1_Enable 0x40
# define VMC_Vremap 0x80
#define VOYAGER_VMC_BANK_DENSITY 0xa
# define VMC_BANK_EMPTY 0
# define VMC_BANK_4MB 1
# define VMC_BANK_16MB 2
# define VMC_BANK_64MB 3
# define VMC_BANK0_MASK 0x03
# define VMC_BANK1_MASK 0x0C
# define VMC_BANK2_MASK 0x30
# define VMC_BANK3_MASK 0xC0
/* Magellan Memory Controller (MMC) defines - present on L5 */
#define VOYAGER_MMC_ASIC_ID 1
/* the two memory modules corresponding to memory cards in the system */
#define VOYAGER_MMC_MEMORY0_MODULE 0x14
#define VOYAGER_MMC_MEMORY1_MODULE 0x15
/* the Magellan Memory Address (MMA) defines */
#define VOYAGER_MMA_ASIC_ID 2
/* Submodule number for the Quad Baseboard */
#define VOYAGER_QUAD_BASEBOARD 1
/* ASIC defines for the Quad Baseboard */
#define VOYAGER_QUAD_QDATA0 1
#define VOYAGER_QUAD_QDATA1 2
#define VOYAGER_QUAD_QABC 3
/* Useful areas in extended CMOS */
#define VOYAGER_PROCESSOR_PRESENT_MASK 0x88a
#define VOYAGER_MEMORY_CLICKMAP 0xa23
#define VOYAGER_DUMP_LOCATION 0xb1a
/* SUS In Control bit - used to tell SUS that we don't need to be
* babysat anymore */
#define VOYAGER_SUS_IN_CONTROL_PORT 0x3ff
# define VOYAGER_IN_CONTROL_FLAG 0x80
/* Voyager PSI defines */
#define VOYAGER_PSI_STATUS_REG 0x08
# define PSI_DC_FAIL 0x01
# define PSI_MON 0x02
# define PSI_FAULT 0x04
# define PSI_ALARM 0x08
# define PSI_CURRENT 0x10
# define PSI_DVM 0x20
# define PSI_PSCFAULT 0x40
# define PSI_STAT_CHG 0x80
#define VOYAGER_PSI_SUPPLY_REG 0x8000
/* read */
# define PSI_FAIL_DC 0x01
# define PSI_FAIL_AC 0x02
# define PSI_MON_INT 0x04
# define PSI_SWITCH_OFF 0x08
# define PSI_HX_OFF 0x10
# define PSI_SECURITY 0x20
# define PSI_CMOS_BATT_LOW 0x40
# define PSI_CMOS_BATT_FAIL 0x80
/* write */
# define PSI_CLR_SWITCH_OFF 0x13
# define PSI_CLR_HX_OFF 0x14
# define PSI_CLR_CMOS_BATT_FAIL 0x17
#define VOYAGER_PSI_MASK 0x8001
# define PSI_MASK_MASK 0x10
#define VOYAGER_PSI_AC_FAIL_REG 0x8004
#define AC_FAIL_STAT_CHANGE 0x80
#define VOYAGER_PSI_GENERAL_REG 0x8007
/* read */
# define PSI_SWITCH_ON 0x01
# define PSI_SWITCH_ENABLED 0x02
# define PSI_ALARM_ENABLED 0x08
# define PSI_SECURE_ENABLED 0x10
# define PSI_COLD_RESET 0x20
# define PSI_COLD_START 0x80
/* write */
# define PSI_POWER_DOWN 0x10
# define PSI_SWITCH_DISABLE 0x01
# define PSI_SWITCH_ENABLE 0x11
# define PSI_CLEAR 0x12
# define PSI_ALARM_DISABLE 0x03
# define PSI_ALARM_ENABLE 0x13
# define PSI_CLEAR_COLD_RESET 0x05
# define PSI_SET_COLD_RESET 0x15
# define PSI_CLEAR_COLD_START 0x07
# define PSI_SET_COLD_START 0x17
struct
voyager_bios_info
{
__u8
len
;
__u8
major
;
__u8
minor
;
__u8
debug
;
__u8
num_classes
;
__u8
class_1
;
__u8
class_2
;
};
/* The following structures and definitions are for the Kernel/SUS
* interface these are needed to find out how SUS initialised any Quad
* boards in the system */
#define NUMBER_OF_MC_BUSSES 2
#define SLOTS_PER_MC_BUS 8
#define MAX_CPUS 16
/* 16 way CPU system */
#define MAX_PROCESSOR_BOARDS 4
/* 4 processor slot system */
#define MAX_CACHE_LEVELS 4
/* # of cache levels supported */
#define MAX_SHARED_CPUS 4
/* # of CPUs that can share a LARC */
#define NUMBER_OF_POS_REGS 8
typedef
struct
{
__u8
MC_Slot
__attribute__
((
packed
));
__u8
POS_Values
[
NUMBER_OF_POS_REGS
]
__attribute__
((
packed
));
}
MC_SlotInformation_t
;
struct
QuadDescription
{
__u8
Type
__attribute__
((
packed
));
/* for type 0 (DYADIC or MONADIC) all fields
* will be zero except for slot */
__u8
StructureVersion
__attribute__
((
packed
));
__u32
CPI_BaseAddress
__attribute__
((
packed
));
__u32
LARC_BankSize
__attribute__
((
packed
));
__u32
LocalMemoryStateBits
__attribute__
((
packed
));
__u8
Slot
__attribute__
((
packed
));
/* Processor slots 1 - 4 */
};
struct
ProcBoardInfo
{
__u8
Type
__attribute__
((
packed
));
__u8
StructureVersion
__attribute__
((
packed
));
__u8
NumberOfBoards
__attribute__
((
packed
));
struct
QuadDescription
QuadData
[
MAX_PROCESSOR_BOARDS
]
__attribute__
((
packed
));
};
struct
CacheDescription
{
__u8
Level
__attribute__
((
packed
));
__u32
TotalSize
__attribute__
((
packed
));
__u16
LineSize
__attribute__
((
packed
));
__u8
Associativity
__attribute__
((
packed
));
__u8
CacheType
__attribute__
((
packed
));
__u8
WriteType
__attribute__
((
packed
));
__u8
Number_CPUs_SharedBy
__attribute__
((
packed
));
__u8
Shared_CPUs_Hardware_IDs
[
MAX_SHARED_CPUS
]
__attribute__
((
packed
));
};
struct
CPU_Description
{
__u8
CPU_HardwareId
__attribute__
((
packed
));
char
*
FRU_String
__attribute__
((
packed
));
__u8
NumberOfCacheLevels
__attribute__
((
packed
));
struct
CacheDescription
CacheLevelData
[
MAX_CACHE_LEVELS
]
__attribute__
((
packed
));
};
struct
CPU_Info
{
__u8
Type
__attribute__
((
packed
));
__u8
StructureVersion
__attribute__
((
packed
));
__u8
NumberOf_CPUs
__attribute__
((
packed
));
struct
CPU_Description
CPU_Data
[
MAX_CPUS
]
__attribute__
((
packed
));
};
/*
* This structure will be used by SUS and the OS.
* The assumption about this structure is that no blank space is
* packed in it by our friend the compiler.
*/
typedef
struct
{
__u8
Mailbox_SUS
;
/* Written to by SUS to give commands/response to the OS */
__u8
Mailbox_OS
;
/* Written to by the OS to give commands/response to SUS */
__u8
SUS_MailboxVersion
;
/* Tells the OS which iteration of the interface SUS supports */
__u8
OS_MailboxVersion
;
/* Tells SUS which iteration of the interface the OS supports */
__u32
OS_Flags
;
/* Flags set by the OS as info for SUS */
__u32
SUS_Flags
;
/* Flags set by SUS as info for the OS */
__u32
WatchDogPeriod
;
/* Watchdog period (in seconds) which the DP uses to see if the OS is dead */
__u32
WatchDogCount
;
/* Updated by the OS on every tic. */
__u32
MemoryFor_SUS_ErrorLog
;
/* Flat 32 bit address which tells SUS where to stuff the SUS error log on a dump */
MC_SlotInformation_t
MC_SlotInfo
[
NUMBER_OF_MC_BUSSES
*
SLOTS_PER_MC_BUS
];
/* Storage for MCA POS data */
/* All new SECOND_PASS_INTERFACE fields added from this point */
struct
ProcBoardInfo
*
BoardData
;
struct
CPU_Info
*
CPU_Data
;
/* All new fields must be added from this point */
}
Voyager_KernelSUS_Mbox_t
;
/* structure for finding the right memory address to send a QIC CPI to */
struct
voyager_qic_cpi
{
/* Each cache line (32 bytes) can trigger a cpi. The cpi
* read/write may occur anywhere in the cache line---pick the
* middle to be safe */
struct
{
__u32
pad1
[
3
];
__u32
cpi
;
__u32
pad2
[
4
];
}
qic_cpi
[
8
];
};
struct
voyager_status
{
__u32
power_fail
:
1
;
__u32
switch_off
:
1
;
__u32
request_from_kernel
:
1
;
};
struct
voyager_psi_regs
{
__u8
cat_id
;
__u8
cat_dev
;
__u8
cat_control
;
__u8
subaddr
;
__u8
dummy4
;
__u8
checkbit
;
__u8
subaddr_low
;
__u8
subaddr_high
;
__u8
intstatus
;
__u8
stat1
;
__u8
stat3
;
__u8
fault
;
__u8
tms
;
__u8
gen
;
__u8
sysconf
;
__u8
dummy15
;
};
struct
voyager_psi_subregs
{
__u8
supply
;
__u8
mask
;
__u8
present
;
__u8
DCfail
;
__u8
ACfail
;
__u8
fail
;
__u8
UPSfail
;
__u8
genstatus
;
};
struct
voyager_psi
{
struct
voyager_psi_regs
regs
;
struct
voyager_psi_subregs
subregs
;
};
struct
voyager_SUS
{
#define VOYAGER_DUMP_BUTTON_NMI 0x1
#define VOYAGER_SUS_VALID 0x2
#define VOYAGER_SYSINT_COMPLETE 0x3
__u8
SUS_mbox
;
#define VOYAGER_NO_COMMAND 0x0
#define VOYAGER_IGNORE_DUMP 0x1
#define VOYAGER_DO_DUMP 0x2
#define VOYAGER_SYSINT_HANDSHAKE 0x3
#define VOYAGER_DO_MEM_DUMP 0x4
#define VOYAGER_SYSINT_WAS_RECOVERED 0x5
__u8
kernel_mbox
;
#define VOYAGER_MAILBOX_VERSION 0x10
__u8
SUS_version
;
__u8
kernel_version
;
#define VOYAGER_OS_HAS_SYSINT 0x1
#define VOYAGER_OS_IN_PROGRESS 0x2
#define VOYAGER_UPDATING_WDPERIOD 0x4
__u32
kernel_flags
;
#define VOYAGER_SUS_BOOTING 0x1
#define VOYAGER_SUS_IN_PROGRESS 0x2
__u32
SUS_flags
;
__u32
watchdog_period
;
__u32
watchdog_count
;
__u32
SUS_errorlog
;
/* lots of system configuration stuff under here */
};
/* Variables exported by voyager_smp */
extern
__u32
voyager_extended_vic_processors
;
extern
__u32
voyager_allowed_boot_processors
;
extern
__u32
voyager_quad_processors
;
extern
struct
voyager_qic_cpi
*
voyager_quad_cpi_addr
[
NR_CPUS
];
extern
struct
voyager_SUS
*
voyager_SUS
;
/* variables exported always */
extern
int
voyager_level
;
extern
int
kvoyagerd_running
;
extern
struct
semaphore
kvoyagerd_sem
;
extern
struct
voyager_status
voyager_status
;
/* functions exported by the voyager and voyager_smp modules */
extern
int
voyager_cat_readb
(
__u8
module
,
__u8
asic
,
int
reg
);
extern
void
voyager_cat_init
(
void
);
extern
void
voyager_detect
(
struct
voyager_bios_info
*
);
extern
void
voyager_trap_init
(
void
);
extern
void
voyager_setup_irqs
(
void
);
extern
int
voyager_memory_detect
(
int
region
,
__u32
*
addr
,
__u32
*
length
);
extern
void
voyager_smp_intr_init
(
void
);
extern
__u8
voyager_extended_cmos_read
(
__u16
cmos_address
);
extern
void
voyager_dump
(
void
);
extern
void
voyager_smp_dump
(
void
);
extern
void
voyager_timer_interrupt
(
struct
pt_regs
*
regs
);
extern
void
smp_local_timer_interrupt
(
struct
pt_regs
*
regs
);
extern
void
voyager_power_off
(
void
);
extern
void
smp_voyager_power_off
(
void
*
dummy
);
extern
void
voyager_restart
(
void
);
extern
void
voyager_cat_power_off
(
void
);
extern
void
voyager_cat_do_common_interrupt
(
void
);
extern
void
voyager_handle_nmi
(
void
);
/* Commands for the following are */
#define VOYAGER_PSI_READ 0
#define VOYAGER_PSI_WRITE 1
#define VOYAGER_PSI_SUBREAD 2
#define VOYAGER_PSI_SUBWRITE 3
extern
void
voyager_cat_psi
(
__u8
,
__u16
,
__u8
*
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment