Commit 6b3dcb38 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] core voyager arch/i386/machine support

parent 12de40fe
#
# 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
/* 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;
}
/* -*- 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);
/* -*- 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 */
/*
* 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);
}
/* 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;
}
#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);
/* 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("\nVOYAGER: 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();
}
This diff is collapsed.
This diff is collapsed.
/* -*- 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);
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment