2005-04-16 15:20:36 -07:00
/* 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/module.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 <linux/sysrq.h>
# include <asm/io.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>
2005-06-30 02:58:55 -07:00
# include <asm/i8253.h>
2005-04-16 15:20:36 -07:00
/*
* Power off function , if any
*/
void ( * pm_power_off ) ( void ) ;
2005-07-13 09:38:05 -04:00
EXPORT_SYMBOL ( pm_power_off ) ;
2005-04-16 15:20:36 -07:00
int voyager_level = 0 ;
struct voyager_SUS * voyager_SUS = NULL ;
# ifdef CONFIG_SMP
static void
voyager_dump ( int dummy1 , struct pt_regs * dummy2 , struct tty_struct * dummy3 )
{
/* get here via a sysrq */
voyager_smp_dump ( ) ;
}
static struct sysrq_key_op sysrq_voyager_dump_op = {
. handler = voyager_dump ,
. help_msg = " Voyager " ,
. action_msg = " Dump Voyager Status " ,
} ;
# endif
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 ;
# ifdef CONFIG_SMP
register_sysrq_key ( ' v ' , & sysrq_voyager_dump_op ) ;
# endif
} 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 ;
}
/* 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 ;
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 ) ;
/* 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 , 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 ( ) ;
}