OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k_old/trunk/rc203soc/sw/uClinux/arch/alpha
    from Rev 1765 to Rev 1782
    Reverse comparison

Rev 1765 → Rev 1782

/kernel/time.c
0,0 → 1,265
/*
* linux/arch/alpha/kernel/time.c
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
*
* This file contains the PC-specific time handling details:
* reading the RTC at bootup, etc..
* 1994-07-02 Alan Modra
* fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
* 1995-03-26 Markus Kuhn
* fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
* precision CMOS clock update
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/mm.h>
 
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
 
#include <linux/mc146818rtc.h>
#include <linux/timex.h>
 
#define TIMER_IRQ 0
 
extern struct hwrpb_struct *hwrpb;
 
static int set_rtc_mmss(unsigned long);
 
 
/*
* Shift amount by which scaled_ticks_per_cycle is scaled. Shifting
* by 48 gives us 16 bits for HZ while keeping the accuracy good even
* for large CPU clock rates.
*/
#define FIX_SHIFT 48
 
/* lump static variables together for more efficient access: */
static struct {
__u32 last_time; /* cycle counter last time it got invoked */
__u32 max_cycles_per_tick; /* more makes us think we lost an interrupt */
unsigned long scaled_ticks_per_cycle; /* ticks/cycle * 2^48 */
long last_rtc_update; /* last time the cmos clock got updated */
} state;
 
 
static inline __u32 rpcc(void)
{
__u32 result;
 
asm volatile ("rpcc %0" : "r="(result));
return result;
}
 
 
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
void timer_interrupt(struct pt_regs * regs)
{
__u32 delta, now;
 
now = rpcc();
delta = now - state.last_time;
state.last_time = now;
if (delta > state.max_cycles_per_tick) {
int i, missed_ticks;
 
missed_ticks = ((delta * state.scaled_ticks_per_cycle) >> FIX_SHIFT) - 1;
for (i = 0; i < missed_ticks; ++i) {
do_timer(regs);
}
}
do_timer(regs);
 
/*
* If we have an externally synchronized Linux clock, then update
* CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
* called as close as possible to 500 ms before the new second starts.
*/
if (time_state != TIME_BAD && xtime.tv_sec > state.last_rtc_update + 660 &&
xtime.tv_usec > 500000 - (tick >> 1) &&
xtime.tv_usec < 500000 + (tick >> 1))
if (set_rtc_mmss(xtime.tv_sec) == 0)
state.last_rtc_update = xtime.tv_sec;
else
state.last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
}
 
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* WARNING: this function will overflow on 2106-02-07 06:28:16 on
* machines were long is 32-bit! (However, as time_t is signed, we
* will already get problems at other places on 2038-01-19 03:14:08)
*/
static inline unsigned long mktime(unsigned int year, unsigned int mon,
unsigned int day, unsigned int hour,
unsigned int min, unsigned int sec)
{
if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return (((
(unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
 
void time_init(void)
{
unsigned int year, mon, day, hour, min, sec;
int i;
 
/* The Linux interpretation of the CMOS clock register contents:
* When the Update-In-Progress (UIP) flag goes from 1 to 0, the
* RTC registers show the second which has precisely just started.
* Let's hope other operating systems interpret the RTC the same way.
*/
/* read RTC exactly on falling edge of update flag */
for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
break;
for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
break;
do { /* Isn't this overkill ? UIP above should guarantee consistency */
sec = CMOS_READ(RTC_SECONDS);
min = CMOS_READ(RTC_MINUTES);
hour = CMOS_READ(RTC_HOURS);
day = CMOS_READ(RTC_DAY_OF_MONTH);
mon = CMOS_READ(RTC_MONTH);
year = CMOS_READ(RTC_YEAR);
} while (sec != CMOS_READ(RTC_SECONDS));
if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
{
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
}
#ifdef ALPHA_PRE_V1_2_SRM_CONSOLE
/*
* The meaning of life, the universe, and everything. Plus
* this makes the year come out right on SRM consoles earlier
* than v1.2.
*/
year -= 42;
#endif
if ((year += 1900) < 1970)
year += 100;
xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
xtime.tv_usec = 0;
 
if (HZ > (1<<16)) {
extern void __you_loose (void);
__you_loose();
}
state.last_time = rpcc();
state.scaled_ticks_per_cycle = ((unsigned long) HZ << FIX_SHIFT) / hwrpb->cycle_freq;
state.max_cycles_per_tick = (2 * hwrpb->cycle_freq) / HZ;
state.last_rtc_update = 0;
}
 
/*
* We could get better timer accuracy by using the alpha
* time counters or something. Now this is limited to
* the HZ clock frequency.
*/
void do_gettimeofday(struct timeval *tv)
{
unsigned long flags;
 
save_flags(flags);
cli();
*tv = xtime;
restore_flags(flags);
}
 
void do_settimeofday(struct timeval *tv)
{
cli();
xtime = *tv;
time_state = TIME_BAD;
time_maxerror = 0x70000000;
time_esterror = 0x70000000;
sti();
}
 
 
/*
* In order to set the CMOS clock precisely, set_rtc_mmss has to be
* called 500 ms after the second nowtime has started, because when
* nowtime is written into the registers of the CMOS clock, it will
* jump to the next second precisely 500 ms later. Check the Motorola
* MC146818A or Dallas DS12887 data sheet for details.
*/
static int set_rtc_mmss(unsigned long nowtime)
{
int retval = 0;
int real_seconds, real_minutes, cmos_minutes;
unsigned char save_control, save_freq_select;
 
save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 
save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
 
cmos_minutes = CMOS_READ(RTC_MINUTES);
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
BCD_TO_BIN(cmos_minutes);
 
/*
* since we're only adjusting minutes and seconds,
* don't interfere with hour overflow. This avoids
* messing with unknown time zones but requires your
* RTC not to be off by more than 15 minutes
*/
real_seconds = nowtime % 60;
real_minutes = nowtime / 60;
if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
real_minutes += 30; /* correct for half hour time zone */
real_minutes %= 60;
 
if (abs(real_minutes - cmos_minutes) < 30) {
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
BIN_TO_BCD(real_seconds);
BIN_TO_BCD(real_minutes);
}
CMOS_WRITE(real_seconds,RTC_SECONDS);
CMOS_WRITE(real_minutes,RTC_MINUTES);
} else
retval = -1;
 
/* The following flags have to be released exactly in this order,
* otherwise the DS12887 (popular MC146818A clone with integrated
* battery and quartz) will not reset the oscillator and will not
* update precisely 500 ms later. You won't find this mentioned in
* the Dallas Semiconductor data sheets, but who believes data
* sheets anyway ... -- Markus Kuhn
*/
CMOS_WRITE(save_control, RTC_CONTROL);
CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 
return retval;
}
/kernel/lca.c
0,0 → 1,551
/*
* Code common to all LCA chips.
*
* Written by David Mosberger (davidm@cs.arizona.edu) with some code
* taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit
* bios code.
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/bios32.h>
#include <linux/pci.h>
 
#include <asm/ptrace.h>
#include <asm/system.h>
#include <asm/io.h>
 
/*
* BIOS32-style PCI interface:
*/
 
#ifdef CONFIG_ALPHA_LCA
 
#define vulp volatile unsigned long *
#define vuip volatile unsigned int *
 
/*
* Machine check reasons. Defined according to PALcode sources
* (osf.h and platform.h).
*/
#define MCHK_K_TPERR 0x0080
#define MCHK_K_TCPERR 0x0082
#define MCHK_K_HERR 0x0084
#define MCHK_K_ECC_C 0x0086
#define MCHK_K_ECC_NC 0x0088
#define MCHK_K_UNKNOWN 0x008A
#define MCHK_K_CACKSOFT 0x008C
#define MCHK_K_BUGCHECK 0x008E
#define MCHK_K_OS_BUGCHECK 0x0090
#define MCHK_K_DCPERR 0x0092
#define MCHK_K_ICPERR 0x0094
 
/*
* Platform-specific machine-check reasons:
*/
#define MCHK_K_SIO_SERR 0x204 /* all platforms so far */
#define MCHK_K_SIO_IOCHK 0x206 /* all platforms so far */
#define MCHK_K_DCSR 0x208 /* all but Noname */
 
#ifdef CONFIG_ALPHA_SRM_SETUP
unsigned int LCA_DMA_WIN_BASE = LCA_DMA_WIN_BASE_DEFAULT;
unsigned int LCA_DMA_WIN_SIZE = LCA_DMA_WIN_SIZE_DEFAULT;
#endif /* SRM_SETUP */
 
/*
* Given a bus, device, and function number, compute resulting
* configuration space address and setup the LCA_IOC_CONF register
* accordingly. It is therefore not safe to have concurrent
* invocations to configuration space access routines, but there
* really shouldn't be any need for this.
*
* Type 0:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:11 Device select bit.
* 10:8 Function number
* 7:2 Register number
*
* Type 1:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:24 reserved
* 23:16 bus number (8 bits = 128 possible buses)
* 15:11 Device number (5 bits)
* 10:8 function number
* 7:2 register number
*
* Notes:
* The function number selects which function of a multi-function device
* (e.g., scsi and ethernet).
*
* The register selects a DWORD (32 bit) register offset. Hence it
* doesn't get shifted by 2 bits as we want to "drop" the bottom two
* bits.
*/
static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned long *pci_addr)
{
unsigned long addr;
 
if (bus == 0) {
int device = device_fn >> 3;
int func = device_fn & 0x7;
 
/* type 0 configuration cycle: */
 
if (device > 12) {
return -1;
}
 
*((volatile unsigned long*) LCA_IOC_CONF) = 0;
addr = (1 << (11 + device)) | (func << 8) | where;
} else {
/* type 1 configuration cycle: */
*((volatile unsigned long*) LCA_IOC_CONF) = 1;
addr = (bus << 16) | (device_fn << 8) | where;
}
*pci_addr = addr;
return 0;
}
 
 
static unsigned int conf_read(unsigned long addr)
{
unsigned long flags, code, stat0;
unsigned int value;
 
save_flags(flags);
cli();
 
/* reset status register to avoid loosing errors: */
stat0 = *((volatile unsigned long*)LCA_IOC_STAT0);
*((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
mb();
 
/* access configuration space: */
 
value = *((volatile unsigned int*)addr);
draina();
 
stat0 = *((unsigned long*)LCA_IOC_STAT0);
if (stat0 & LCA_IOC_STAT0_ERR) {
code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
& LCA_IOC_STAT0_CODE_MASK);
if (code != 1) {
printk("lca.c:conf_read: got stat0=%lx\n", stat0);
}
 
/* reset error status: */
*((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
mb();
wrmces(0x7); /* reset machine check */
 
value = 0xffffffff;
}
restore_flags(flags);
return value;
}
 
 
static void conf_write(unsigned long addr, unsigned int value)
{
unsigned long flags, code, stat0;
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
/* reset status register to avoid loosing errors: */
stat0 = *((volatile unsigned long*)LCA_IOC_STAT0);
*((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
mb();
 
/* access configuration space: */
 
*((volatile unsigned int*)addr) = value;
draina();
 
stat0 = *((unsigned long*)LCA_IOC_STAT0);
if (stat0 & LCA_IOC_STAT0_ERR) {
code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
& LCA_IOC_STAT0_CODE_MASK);
if (code != 1) {
printk("lca.c:conf_write: got stat0=%lx\n", stat0);
}
 
/* reset error status: */
*((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
mb();
wrmces(0x7); /* reset machine check */
}
restore_flags(flags);
}
 
 
int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char *value)
{
unsigned long addr = LCA_CONF;
unsigned long pci_addr;
 
*value = 0xff;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x00;
*value = conf_read(addr) >> ((where & 3) * 8);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short *value)
{
unsigned long addr = LCA_CONF;
unsigned long pci_addr;
 
*value = 0xffff;
 
if (where & 0x1) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
if (mk_conf_addr(bus, device_fn, where, &pci_addr)) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x08;
*value = conf_read(addr) >> ((where & 3) * 8);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int *value)
{
unsigned long addr = LCA_CONF;
unsigned long pci_addr;
 
*value = 0xffffffff;
if (where & 0x3) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
if (mk_conf_addr(bus, device_fn, where, &pci_addr)) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
*value = conf_read(addr);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char value)
{
unsigned long addr = LCA_CONF;
unsigned long pci_addr;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x00;
conf_write(addr, value << ((where & 3) * 8));
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short value)
{
unsigned long addr = LCA_CONF;
unsigned long pci_addr;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x08;
conf_write(addr, value << ((where & 3) * 8));
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int value)
{
unsigned long addr = LCA_CONF;
unsigned long pci_addr;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
conf_write(addr, value << ((where & 3) * 8));
return PCIBIOS_SUCCESSFUL;
}
 
 
unsigned long lca_init(unsigned long mem_start, unsigned long mem_end)
{
#ifdef CONFIG_ALPHA_SRM_SETUP
/* check window 0 for enabled and mapped to 0 */
if ((*(vulp)LCA_IOC_W_BASE0 & (1UL<<33)) &&
(*(vulp)LCA_IOC_T_BASE0 == 0))
{
LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE0 & 0xffffffffUL;
LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK0 & 0xffffffffUL;
LCA_DMA_WIN_SIZE += 1;
#if 1
printk("lca_init: using Window 0 settings\n");
printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
*(vulp)LCA_IOC_W_BASE0,
*(vulp)LCA_IOC_W_MASK0,
*(vulp)LCA_IOC_T_BASE0);
#endif
}
else /* check window 2 for enabled and mapped to 0 */
if ((*(vulp)LCA_IOC_W_BASE1 & (1UL<<33)) &&
(*(vulp)LCA_IOC_T_BASE1 == 0))
{
LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE1 & 0xffffffffUL;
LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK1 & 0xffffffffUL;
LCA_DMA_WIN_SIZE += 1;
#if 1
printk("lca_init: using Window 1 settings\n");
printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
*(vulp)LCA_IOC_W_BASE1,
*(vulp)LCA_IOC_W_MASK1,
*(vulp)LCA_IOC_T_BASE1);
#endif
}
else /* we must use our defaults... */
#endif /* SRM_SETUP */
{
/*
* Set up the PCI->physical memory translation windows.
* For now, window 1 is disabled. In the future, we may
* want to use it to do scatter/gather DMA. Window 0
* goes at 1 GB and is 1 GB large.
*/
*(vulp)LCA_IOC_W_BASE1 = 0UL<<33;
 
*(vulp)LCA_IOC_W_BASE0 = 1UL<<33 | LCA_DMA_WIN_BASE;
*(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1;
*(vulp)LCA_IOC_T_BASE0 = 0;
}
 
/*
* Disable PCI parity for now. The NCR53c810 chip has
* troubles meeting the PCI spec which results in
* data parity errors.
*/
*(vulp)LCA_IOC_PAR_DIS = 1UL<<5;
return mem_start;
}
 
 
 
 
/*
* Constants used during machine-check handling. I suppose these
* could be moved into lca.h but I don't see much reason why anybody
* else would want to use them.
*/
#define ESR_EAV (1UL<< 0) /* error address valid */
#define ESR_CEE (1UL<< 1) /* correctable error */
#define ESR_UEE (1UL<< 2) /* uncorrectable error */
#define ESR_WRE (1UL<< 3) /* write-error */
#define ESR_SOR (1UL<< 4) /* error source */
#define ESR_CTE (1UL<< 7) /* cache-tag error */
#define ESR_MSE (1UL<< 9) /* multiple soft errors */
#define ESR_MHE (1UL<<10) /* multiple hard errors */
#define ESR_NXM (1UL<<12) /* non-existent memory */
 
#define IOC_ERR ( 1<<4) /* ioc logs an error */
#define IOC_CMD_SHIFT 0
#define IOC_CMD (0xf<<IOC_CMD_SHIFT)
#define IOC_CODE_SHIFT 8
#define IOC_CODE (0xf<<IOC_CODE_SHIFT)
#define IOC_LOST ( 1<<5)
#define IOC_P_NBR ((__u32) ~((1<<13) - 1))
 
 
void mem_error (unsigned long esr, unsigned long ear)
{
printk(" %s %s error to %s occurred at address %x\n",
(esr & ESR_CEE) ? "Correctable" : ((esr & ESR_UEE) ? "Uncorrectable" : "A"),
(esr & ESR_WRE) ? "write" : "read",
(esr & ESR_SOR) ? "memory" : "b-cache",
(unsigned) (ear & 0x1ffffff8));
if (esr & ESR_CTE) {
printk(" A b-cache tag parity error was detected.\n");
}
if (esr & ESR_MSE) {
printk(" Several other correctable errors occurred.\n");
}
if (esr & ESR_MHE) {
printk(" Several other uncorrectable errors occurred.\n");
}
if (esr & ESR_NXM) {
printk(" Attempted to access non-existent memory.\n");
}
}
 
 
void ioc_error (__u32 stat0, __u32 stat1)
{
const char *pci_cmd[] = {
"Interrupt Acknowledge", "Special", "I/O Read", "I/O Write",
"Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3", "Rsvd4",
"Configuration Read", "Configuration Write", "Memory Read Multiple",
"Dual Address", "Memory Read Line", "Memory Write and Invalidate"
};
const char *err_name[] = {
"exceeded retry limit", "no device", "bad data parity", "target abort",
"bad address parity", "page table read error", "invalid page", "data error"
};
unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT;
unsigned cmd = (stat0 & IOC_CMD) >> IOC_CMD_SHIFT;
 
printk(" %s initiated PCI %s cycle to address %x failed due to %s.\n",
code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]);
if (code == 5 || code == 6) {
printk(" (Error occurred at PCI memory address %x.)\n", (stat0 & ~IOC_P_NBR));
}
if (stat0 & IOC_LOST) {
printk(" Other PCI errors occurred simultaneously.\n");
}
}
 
 
void lca_machine_check (unsigned long vector, unsigned long la, struct pt_regs *regs)
{
unsigned long * ptr;
const char * reason;
union el_lca el;
char buf[128];
long i;
 
printk(KERN_CRIT "lca: machine check (la=0x%lx,pc=0x%lx)\n",
la, regs->pc);
el.c = (struct el_common *) la;
/*
* The first quadword after the common header always seems to
* be the machine check reason---don't know why this isn't
* part of the common header instead. In the case of a long
* logout frame, the upper 32 bits is the machine check
* revision level, which we ignore for now.
*/
switch (el.c->code & 0xffffffff) {
case MCHK_K_TPERR: reason = "tag parity error"; break;
case MCHK_K_TCPERR: reason = "tag control parity error"; break;
case MCHK_K_HERR: reason = "access to non-existent memory"; break;
case MCHK_K_ECC_C: reason = "correctable ECC error"; break;
case MCHK_K_ECC_NC: reason = "non-correctable ECC error"; break;
case MCHK_K_CACKSOFT: reason = "MCHK_K_CACKSOFT"; break; /* what's this? */
case MCHK_K_BUGCHECK: reason = "illegal exception in PAL mode"; break;
case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break;
case MCHK_K_DCPERR: reason = "d-cache parity error"; break;
case MCHK_K_ICPERR: reason = "i-cache parity error"; break;
case MCHK_K_SIO_SERR: reason = "SIO SERR occurred on PCI bus"; break;
case MCHK_K_SIO_IOCHK: reason = "SIO IOCHK occurred on ISA bus"; break;
case MCHK_K_DCSR: reason = "MCHK_K_DCSR"; break;
case MCHK_K_UNKNOWN:
default:
sprintf(buf, "reason for machine-check unknown (0x%lx)",
el.c->code & 0xffffffff);
reason = buf;
break;
}
 
wrmces(rdmces()); /* reset machine check pending flag */
 
switch (el.c->size) {
case sizeof(struct el_lca_mcheck_short):
printk(KERN_CRIT
" Reason: %s (short frame%s, dc_stat=%lx):\pn",
reason, el.c->retry ? ", retryable" : "", el.s->dc_stat);
if (el.s->esr & ESR_EAV) {
mem_error(el.s->esr, el.s->ear);
}
if (el.s->ioc_stat0 & IOC_ERR) {
ioc_error(el.s->ioc_stat0, el.s->ioc_stat1);
}
break;
 
case sizeof(struct el_lca_mcheck_long):
printk(KERN_CRIT " Reason: %s (long frame%s):\n",
reason, el.c->retry ? ", retryable" : "");
printk(KERN_CRIT
" reason: %lx exc_addr: %lx dc_stat: %lx\n",
el.l->pt[0], el.l->exc_addr, el.l->dc_stat);
printk(KERN_CRIT " car: %lx\n", el.l->car);
if (el.l->esr & ESR_EAV) {
mem_error(el.l->esr, el.l->ear);
}
if (el.l->ioc_stat0 & IOC_ERR) {
ioc_error(el.l->ioc_stat0, el.l->ioc_stat1);
}
break;
 
default:
printk(KERN_CRIT " Unknown errorlog size %d\n", el.c->size);
}
 
/* dump the logout area to give all info: */
 
ptr = (unsigned long *) la;
for (i = 0; i < el.c->size / sizeof(long); i += 2) {
printk(KERN_CRIT " +%8lx %016lx %016lx\n",
i*sizeof(long), ptr[i], ptr[i+1]);
}
}
 
void
lca_clock_print(void)
{
long pmr_reg;
 
pmr_reg = READ_PMR;
 
printk("Status of clock control:\n");
printk("\tPrimary clock divisor\t0x%x\n", GET_PRIMARY(pmr_reg));
printk("\tOverride clock divisor\t0x%x\n", GET_OVERRIDE(pmr_reg));
printk("\tInterrupt override is %s\n",
(pmr_reg & LCA_PMR_INTO) ? "on" : "off");
printk("\tDMA override is %s\n",
(pmr_reg & LCA_PMR_DMAO) ? "on" : "off");
 
}
 
int
lca_get_clock(void)
{
long pmr_reg;
 
pmr_reg = READ_PMR;
return(GET_PRIMARY(pmr_reg));
 
}
 
void
lca_clock_fiddle(int divisor)
{
long pmr_reg;
 
pmr_reg = READ_PMR;
SET_PRIMARY_CLOCK(pmr_reg, divisor);
/* lca_norm_clock = divisor; */
WRITE_PMR(pmr_reg);
mb();
}
 
#endif /* CONFIG_ALPHA_LCA */
/kernel/setup.c
0,0 → 1,376
/*
* linux/arch/alpha/kernel/setup.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
/*
* bootup setup stuff..
*/
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/ldt.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/tty.h>
#include <linux/delay.h>
#include <linux/config.h> /* CONFIG_ALPHA_LCA etc */
#include <linux/mc146818rtc.h>
 
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/hwrpb.h>
#include <asm/dma.h>
#include <asm/io.h>
 
struct hae hae = {
0,
(unsigned long*) HAE_ADDRESS
};
 
struct hwrpb_struct *hwrpb;
 
unsigned char aux_device_present = 0xaa;
 
/*
* This is setup by the secondary bootstrap loader. Because
* the zero page is zeroed out as soon as the vm system is
* initialized, we need to copy things out into a more permanent
* place.
*/
#define PARAM ZERO_PAGE
#define COMMAND_LINE ((char*)(PARAM + 0x0000))
#define COMMAND_LINE_SIZE 256
 
static char command_line[COMMAND_LINE_SIZE] = { 0, };
char saved_command_line[COMMAND_LINE_SIZE];
 
/*
* The format of "screen_info" is strange, and due to early
* i386-setup code. This is just enough to make the console
* code think we're on a VGA color display.
*/
struct screen_info screen_info = {
#if defined(CONFIG_ALPHA_BOOK1)
/* the AlphaBook1 has LCD video fixed at 800x600, 37 rows and 100 cols */
0, 37, /* orig-x, orig-y */
#else
0, 25, /* orig-x, orig-y */
#endif
{ 0, 0 }, /* unused */
0, /* orig-video-page */
0, /* orig-video-mode */
#if defined(CONFIG_ALPHA_BOOK1)
100, /* orig-video-cols */
#else
80, /* orig-video-cols */
#endif
0,0,0, /* ega_ax, ega_bx, ega_cx */
#if defined(CONFIG_ALPHA_BOOK1)
37, /* orig-video-lines */
#else
25, /* orig-video-lines */
#endif
1, /* orig-video-isVGA */
16 /* orig-video-points */
};
 
/*
* Initialize Programmable Interval Timers with standard values. Some
* drivers depend on them being initialized (e.g., joystick driver).
*/
static void init_pit (void)
{
#if 0
/*
* Leave refresh timer alone---nobody should depend on
* a particular value anyway.
*/
outb(0x54, 0x43); /* counter 1: refresh timer */
outb(0x18, 0x41);
#endif
 
#if !defined(CONFIG_ALPHA_RUFFIAN)
/* Ruffian depends on the system timer established in MILO!! */
outb(0x36, 0x43); /* counter 0: system timer */
outb(0x00, 0x40);
outb(0x00, 0x40);
#endif /* RUFFIAN */
 
outb(0xb6, 0x43); /* counter 2: speaker */
outb(0x31, 0x42);
outb(0x13, 0x42);
}
 
static unsigned long find_end_memory(void)
{
int i;
unsigned long high = 0;
struct memclust_struct * cluster;
struct memdesc_struct * memdesc;
 
memdesc = (struct memdesc_struct *)
(INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB);
cluster = memdesc->cluster;
for (i = memdesc->numclusters ; i > 0; i--, cluster++) {
unsigned long tmp;
tmp = (cluster->start_pfn + cluster->numpages) << PAGE_SHIFT;
if (tmp > high)
high = tmp;
}
/* round it up to an even number of pages.. */
high = (high + PAGE_SIZE) & (PAGE_MASK*2);
return PAGE_OFFSET + high;
}
 
void setup_arch(char **cmdline_p,
unsigned long * memory_start_p, unsigned long * memory_end_p)
{
extern int _end;
 
init_pit();
 
if ((CMOS_READ(RTC_FREQ_SELECT) & 0x3f) != 0x26) {
#if 1
printk("init_timers: setting RTC_FREQ to 1024/sec\n");
#endif
CMOS_WRITE(0x26, RTC_FREQ_SELECT);
}
 
hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr);
 
#ifndef CONFIG_ALPHA_SRM_SETUP
set_hae(hae.cache); /* sync HAE register w/hae_cache */
#endif /* !SRM_SETUP */
 
wrmces(0x7); /* reset enable correctable error reports */
 
ROOT_DEV = to_kdev_t(0x0802); /* sda2 */
command_line[COMMAND_LINE_SIZE - 1] = '\0';
 
/* Hack for Jensen... since we're restricted to 8 or 16
* chars for boot flags depending on the boot mode,
* we need some shorthand. This should do for
* installation. Later we'll add other abbreviations
* as well...
*/
if (strcmp(COMMAND_LINE, "INSTALL") == 0) {
strcpy(command_line, "root=/dev/fd0 load_ramdisk=1");
strcpy(saved_command_line, command_line);
} else {
strcpy(command_line, COMMAND_LINE);
strcpy(saved_command_line, COMMAND_LINE);
}
printk("Command line: %s\n", command_line);
 
*cmdline_p = command_line;
*memory_start_p = (unsigned long) &_end;
*memory_end_p = find_end_memory();
 
#if defined(CONFIG_ALPHA_LCA)
*memory_start_p = lca_init(*memory_start_p, *memory_end_p);
#elif defined(CONFIG_ALPHA_APECS)
*memory_start_p = apecs_init(*memory_start_p, *memory_end_p);
#elif defined(CONFIG_ALPHA_CIA)
*memory_start_p = cia_init(*memory_start_p, *memory_end_p);
#elif defined(CONFIG_ALPHA_PYXIS)
*memory_start_p = pyxis_init(*memory_start_p, *memory_end_p);
#elif defined(CONFIG_ALPHA_T2)
*memory_start_p = t2_init(*memory_start_p, *memory_end_p);
#endif
}
 
# define N(a) (sizeof(a)/sizeof(a[0]))
 
/* A change was made to the HWRPB via an ECO and the following code tracks
* a part of the ECO. The HWRPB version must be 5 or higher or the ECO
* was not implemented in the console firmware. If its at rev 5 or greater
* we can get the platform ascii string name from the HWRPB. Thats what this
* function does. It checks the rev level and if the string is in the HWRPB
* it returns the addtess of the string ... a pointer to the platform name.
*
* Returns:
* - Pointer to a ascii string if its in the HWRPB
* - Pointer to a blank string if the data is not in the HWRPB.
*/
static char *
platform_string(void)
{
struct dsr_struct *dsr;
static char unk_system_string[] = "N/A";
 
/* Go to the console for the string pointer.
* If the rpb_vers is not 5 or greater the rpb
* is old and does not have this data in it.
*/
if (hwrpb->revision < 5)
return (unk_system_string);
else {
/* The Dynamic System Recognition struct
* has the system platform name starting
* after the character count of the string.
*/
dsr = ((struct dsr_struct *)
((char *)hwrpb + hwrpb->dsr_offset));
return ((char *)dsr + (dsr->sysname_off +
sizeof(long)));
}
}
 
static void
get_sysnames(long type, long variation,
char **type_name, char **variation_name)
{
static char *sys_unknown = "Unknown";
static char *systype_names[] = {
"0",
"ADU", "Cobra", "Ruby", "Flamingo", "Mannequin", "Jensen",
"Pelican", "Morgan", "Sable", "Medulla", "Noname",
"Turbolaser", "Avanti", "14", "Alcor", "Tradewind",
"Mikasa", "EB64", "EB66", "EB64+", "AlphaBook1",
"Rawhide", "K2", "Lynx", "XL", "EB164", "Noritake",
"Cortex", "29", "Miata", "XXM", "Takara", "Yukon",
"Tsunami", "Wildfire", "CUSCO"
};
 
static char *unofficial_names[] = {
"100",
"Ruffian"
};
 
static char * eb164_names[] = {"EB164", "PC164", "LX164", "SX164"};
static int eb164_indices[] = {0,0,0,1,1,1,1,1,2,2,2,2,3,3,3,3};
 
static char * alcor_names[] = {"Alcor", "Maverick", "Bret"};
static int alcor_indices[] = {0,0,0,1,1,1,0,0,0,0,0,0,2,2,2,2,2,2};
 
static char * eb64p_names[] = {"EB64+", "Cabriolet", "AlphaPCI64"};
static int eb64p_indices[] = {0,0,1.2};
 
static char * eb66_names[] = {"EB66", "EB66+"};
static int eb66_indices[] = {0,0,1};
 
static char * rawhide_names[] = {"Dodge", "Wrangler", "Durango",
"Tincup", "DaVinci"};
static int rawhide_indices[] = {0,0,0,1,1,2,2,3,3,4,4};
 
long member;
 
/* restore real CABRIO and EB66+ family names, ie EB64+ and EB66 */
if (type < 0) type = -type;
 
/* if not in the tables, make it UNKNOWN */
/* else set type name to family */
if (type < N(systype_names)) {
*type_name = systype_names[type];
} else
if ((type > ST_UNOFFICIAL_BIAS) &&
(type - ST_UNOFFICIAL_BIAS) < N(unofficial_names)) {
*type_name = unofficial_names[type - ST_UNOFFICIAL_BIAS];
} else {
*type_name = sys_unknown;
*variation_name = sys_unknown;
return;
}
 
/* set variation to "0"; if variation is zero, done */
*variation_name = systype_names[0];
if (variation == 0) {
return;
}
 
member = (variation >> 10) & 0x3f; /* member ID is a bit-field */
 
switch (type) { /* select by family */
default: /* default to variation "0" ????FIXME???? */
break;
case ST_DEC_EB164:
if (member < N(eb164_indices))
*variation_name = eb164_names[eb164_indices[member]];
break;
case ST_DEC_ALCOR:
if (member < N(alcor_indices))
*variation_name = alcor_names[alcor_indices[member]];
break;
case ST_DEC_EB64P:
if (member < N(eb64p_indices))
*variation_name = eb64p_names[eb64p_indices[member]];
break;
case ST_DEC_EB66:
if (member < N(eb66_indices))
*variation_name = eb66_names[eb66_indices[member]];
break;
case ST_DEC_RAWHIDE:
if (member < N(rawhide_indices))
*variation_name = rawhide_names[rawhide_indices[member]];
break;
} /* end family switch */
return;
}
 
int get_cpuinfo(char *buffer)
/* BUFFER is PAGE_SIZE bytes long. */
{
const char *cpu_name[] = {
"EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45", "EV56",
"EV6", "PCA56"
};
struct percpu_struct *cpu;
unsigned int cpu_index;
char *systype_name;
char *sysvariation_name;
extern struct unaligned_stat {
unsigned long count, va, pc;
} unaligned[2];
 
cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset);
cpu_index = (unsigned) (cpu->type - 1);
get_sysnames(hwrpb->sys_type, hwrpb->sys_variation,
&systype_name, &sysvariation_name);
 
return sprintf(buffer,
"cpu\t\t\t: Alpha\n"
"cpu model\t\t: %s\n"
"cpu variation\t\t: %ld\n"
"cpu revision\t\t: %ld\n"
"cpu serial number\t: %s\n"
"system type\t\t: %s\n"
"system variation\t: %s\n"
"system revision\t\t: %ld\n"
"system serial number\t: %s\n"
"cycle frequency [Hz]\t: %lu\n"
"timer frequency [Hz]\t: %lu.%02lu\n"
"page size [bytes]\t: %ld\n"
"phys. address bits\t: %ld\n"
"max. addr. space #\t: %ld\n"
"BogoMIPS\t\t: %lu.%02lu\n"
"kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
"user unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
"platform string\t\t: %s\n",
 
(cpu_index < N(cpu_name)
? cpu_name[cpu_index] : "Unknown"),
cpu->variation, cpu->revision, (char*)cpu->serial_no,
systype_name, sysvariation_name, hwrpb->sys_revision,
(char*)hwrpb->ssn,
hwrpb->cycle_freq,
hwrpb->intr_freq / 4096,
(100 * hwrpb->intr_freq / 4096) % 100,
hwrpb->pagesize,
hwrpb->pa_bits,
hwrpb->max_asn,
loops_per_sec / 500000, (loops_per_sec / 5000) % 100,
unaligned[0].count, unaligned[0].pc, unaligned[0].va,
unaligned[1].count, unaligned[1].pc, unaligned[1].va,
platform_string());
}
# undef N
/kernel/bios32.c
0,0 → 1,2293
/*
* bios32.c - PCI BIOS functions for Alpha systems not using BIOS
* emulation code.
*
* Written by Dave Rusling (david.rusling@reo.mts.dec.com)
*
* Adapted to 64-bit kernel and then rewritten by David Mosberger
* (davidm@cs.arizona.edu)
*
* For more information, please consult
*
* PCI BIOS Specification Revision
* PCI Local Bus Specification
* PCI System Design Guide
*
* PCI Special Interest Group
* M/S HF3-15A
* 5200 N.E. Elam Young Parkway
* Hillsboro, Oregon 97124-6497
* +1 (503) 696-2000
* +1 (800) 433-5177
*
* Manuals are $25 each or $50 for all three, plus $7 shipping
* within the United States, $35 abroad.
*/
#include <linux/config.h>
#include <linux/kernel.h>
 
#if 0
# define DBG_DEVS(args) printk args
#else
# define DBG_DEVS(args)
#endif
 
#ifndef CONFIG_PCI
 
int pcibios_present(void)
{
return 0;
}
asmlinkage int sys_pciconfig_read()
{
return 0;
}
asmlinkage int sys_pciconfig_write()
{
return 0;
}
 
#else /* CONFIG_PCI */
 
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/malloc.h>
#include <linux/mm.h>
 
#include <asm/hwrpb.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/dma.h>
 
#define KB 1024
#define MB (1024*KB)
#define GB (1024*MB)
 
#define MAJOR_REV 0
#define MINOR_REV 3
 
/*
* Align VAL to ALIGN, which must be a power of two.
*/
#define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
 
 
/*
* PCI_MODIFY
*
* Temporary internal macro. If this 0, then do not write to any of
* the PCI registers, merely read them (i.e., use configuration as
* determined by SRM). The SRM seem do be doing a less than perfect
* job in configuring PCI devices, so for now we do it ourselves.
* Reconfiguring PCI devices breaks console (RPB) callbacks, but
* those don't work properly with 64 bit addresses anyways.
*
* The accepted convention seems to be that the console (POST
* software) should fully configure boot devices and configure the
* interrupt routing of *all* devices. In particular, the base
* addresses of non-boot devices need not be initialized. For
* example, on the AXPpci33 board, the base address a #9 GXE PCI
* graphics card reads as zero (this may, however, be due to a bug in
* the graphics card---there have been some rumor that the #9 BIOS
* incorrectly resets that address to 0...).
*/
#ifdef CONFIG_ALPHA_SRM_SETUP
#define PCI_MODIFY 0
static struct pci_dev *irq_dev_to_reset[16];
static unsigned char irq_to_reset[16];
static int irq_reset_count = 0;
static struct pci_dev *io_dev_to_reset[16];
static unsigned char io_reg_to_reset[16];
static unsigned int io_to_reset[16];
static int io_reset_count = 0;
#else /* SRM_SETUP */
#define PCI_MODIFY 1
#endif /* SRM_SETUP */
 
extern struct hwrpb_struct *hwrpb;
 
#if PCI_MODIFY
 
/* NOTE: we can't just blindly use 64K for machines with EISA busses; they
may also have PCI-PCI bridges present, and then we'd configure the bridge
incorrectly */
/* NOTE also that we may need this stuff for SRM_SETUP also, since certain
SRM consoles screw up and allocate I/O space addresses > 64K behind
PCI-to_PCI bridges, which can't pass addresses larger than 64K... */
#if defined(CONFIG_ALPHA_EISA)
static unsigned int io_base = 0x9000; /* start above 8th slot */
#else
static unsigned int io_base = 0x8000;
#endif
 
#if defined(CONFIG_ALPHA_XL)
/*
An XL is AVANTI (APECS) family, *but* it has only 27 bits of ISA address
that get passed through the PCI<->ISA bridge chip. Although this causes
us to set the PCI->Mem window bases lower than normal, we still allocate
PCI bus devices' memory addresses *below* the low DMA mapping window,
and hope they fit below 64Mb (to avoid conflicts), and so that they can
be accessed via SPARSE space.
 
We accept the risk that a broken Myrinet card will be put into a true XL
and thus can more easily run into the problem described below.
*/
static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */
 
#elif defined(CONFIG_ALPHA_LCA) || defined(CONFIG_ALPHA_APECS)
/*
We try to make this address *always* have more than 1 bit set.
this is so that devices like the broken Myrinet card will always have
a PCI memory address that will never match a IDSEL address in
PCI Config space, which can cause problems with early rev cards.
 
However, APECS and LCA have only 34 bits for physical addresses, thus
limiting PCI bus memory addresses for SPARSE access to be less than 128Mb.
*/
static unsigned int mem_base = 64*MB + 2*MB;
 
#else
/*
We try to make this address *always* have more than 1 bit set.
this is so that devices like the broken Myrinet card will always have
a PCI memory address that will never match a IDSEL address in
PCI Config space, which can cause problems with early rev cards.
 
Because CIA and PYXIS and T2 have more bits for physical addresses,
they support an expanded range of SPARSE memory addresses.
*/
static unsigned int mem_base = 128*MB + 16*MB;
#endif /* CONFIG_ALPHA_XL */
 
/*
* Disable PCI device DEV so that it does not respond to I/O or memory
* accesses.
*/
static void disable_dev(struct pci_dev *dev)
{
struct pci_bus *bus;
unsigned short cmd;
 
#if defined(CONFIG_ALPHA_EISA)
/*
* HACK: the PCI-to-EISA bridge does not seem to identify
* itself as a bridge... :-(
*/
if (dev->vendor == 0x8086 && dev->device == 0x0482) {
DBG_DEVS(("disable_dev: ignoring PCEB...\n"));
return;
}
#endif
 
/*
* we don't have code that will init the CYPRESS bridge correctly
* so we do the next best thing, and depend on the previous
* console code to do the right thing, and ignore it here... :-\
*/
if ((dev->vendor == 0x1080) && (dev->device == 0xC693)) {
#if 0
printk("disable_dev: ignoring CYPRESS bridge...\n");
#endif
return;
}
 
bus = dev->bus;
pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
 
/* hack, turn it off first... */
cmd &= (~PCI_COMMAND_IO & ~PCI_COMMAND_MEMORY & ~PCI_COMMAND_MASTER);
pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND, cmd);
}
 
 
/*
* Layout memory and I/O for a device:
*/
#define MAX(val1, val2) ( ((val1) > (val2)) ? val1 : val2)
 
static void layout_dev(struct pci_dev *dev)
{
struct pci_bus *bus;
unsigned short cmd;
unsigned int base, mask, size, reg;
unsigned int alignto;
 
#if defined(CONFIG_ALPHA_EISA)
/*
* HACK: the PCI-to-EISA bridge does not seem to identify
* itself as a bridge... :-(
*/
if (dev->vendor == 0x8086 && dev->device == 0x0482) {
DBG_DEVS(("layout_dev: ignoring PCEB...\n"));
return;
}
#endif
 
/*
* we don't have code that will init the CYPRESS bridge correctly
* so we do the next best thing, and depend on the previous
* console code to do the right thing, and ignore it here... :-\
*/
if ((dev->vendor == 0x1080) && (dev->device == 0xC693)) {
#if 0
printk("layout_dev: ignoring CYPRESS bridge...\n");
#endif
return;
}
 
bus = dev->bus;
pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
 
for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
 
#ifdef NOT_NOW
if ((dev->vendor == 0x1080) && (dev->device == 0xC693) &&
((PCI_FUNC(dev->devfn) == 1) || (PCI_FUNC(dev->devfn) == 2)))
{ /* if primary or secondary IDE on the CYPRESS bridge */
if ((reg == PCI_BASE_ADDRESS_0) || (reg == PCI_BASE_ADDRESS_1)) {
#if 0
printk("layout_dev: ignoring CYPRESS IDE base reg 0/1\n");
#endif
continue; /* ignore first two registers */
}
}
#endif /* NOT_NOW */
 
/*
* Figure out how much space and of what type this
* device wants.
*/
pcibios_write_config_dword(bus->number, dev->devfn, reg,
0xffffffff);
pcibios_read_config_dword(bus->number, dev->devfn, reg, &base);
#if 0
printk("layout_dev: slot %d fn %d off 0x%x base 0x%x\n",
PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), reg, base);
#endif
if (!base) {
/* this base-address register is unused */
continue;
}
 
/*
* We've read the base address register back after
* writing all ones and so now we must decode it.
*/
if (base & PCI_BASE_ADDRESS_SPACE_IO) {
/*
* I/O space base address register.
*/
cmd |= PCI_COMMAND_IO;
 
base &= PCI_BASE_ADDRESS_IO_MASK;
mask = (~base << 1) | 0x1;
size = (mask & base) & 0xffffffff;
/* align to multiple of size of minimum base */
#if 0
alignto = MAX(0x400, size) ;
#else
/*
If we align to 0x800 bounds, we probably avoid
having devices in any 0xzCzz range, which is
where the DE4X5 driver probes for EISA cards.
Adaptecs, especially, resent such intrusions. :-(
*/
alignto = MAX(0x800, size) ;
#endif
base = ALIGN(io_base, alignto );
io_base = base + size;
pcibios_write_config_dword(bus->number, dev->devfn,
reg, base | 0x1);
DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%x (0x%x)\n",
dev->device, base, size));
} else {
unsigned int type;
/*
* Memory space base address register.
*/
cmd |= PCI_COMMAND_MEMORY;
type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
base &= PCI_BASE_ADDRESS_MEM_MASK;
mask = (~base << 1) | 0x1;
size = (mask & base) & 0xffffffff;
switch (type) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
break;
 
case PCI_BASE_ADDRESS_MEM_TYPE_64:
printk("bios32 WARNING: "
"ignoring 64-bit device in "
"slot %d, function %d: \n",
PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));
reg += 4; /* skip extra 4 bytes */
continue;
 
case PCI_BASE_ADDRESS_MEM_TYPE_1M:
/*
* Allocating memory below 1MB is *very*
* tricky, as there may be all kinds of
* ISA devices lurking that we don't know
* about. For now, we just cross fingers
* and hope nobody tries to do this on an
* Alpha (or that the console has set it
* up properly).
*/
printk("bios32 WARNING: slot %d, function %d "
"requests memory below 1MB---don't "
"know how to do that.\n",
PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));
continue;
}
/*
* The following holds at least for the Low Cost
* Alpha implementation of the PCI interface:
*
* In sparse memory address space, the first
* octant (16 MB) of every 128 MB segment is
* aliased to the very first 16 MB of the
* address space (i.e., it aliases the ISA
* memory address space). Thus, we try to
* avoid allocating PCI devices in that range.
* Can be allocated in 2nd-7th octant only.
* Devices that need more than 112 MB of
* address space must be accessed through
* dense memory space only!
*/
/* align to multiple of size of minimum base */
alignto = MAX(0x1000, size) ;
base = ALIGN(mem_base, alignto);
if (size > 7 * 16*MB) {
printk("bios32 WARNING: slot %d, function %d "
"requests 0x%x bytes of contig address "
" space---don't use sparse memory "
" accesses on this device!!\n",
PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn), size);
} else {
if (((base / (16*MB)) & 0x7) == 0) {
base &= ~(128*MB - 1);
base += 16*MB;
base = ALIGN(base, alignto);
}
if (base / (128*MB) != (base + size) / (128*MB))
{
base &= ~(128*MB - 1);
base += (128 + 16)*MB;
base = ALIGN(base, alignto);
}
}
mem_base = base + size;
pcibios_write_config_dword(bus->number, dev->devfn,
reg, base);
DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%x (0x%x)\n",
dev->device, base, size));
}
} /* end for-loop */
/* enable device: */
if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED ||
dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA ||
dev->class >> 8 == PCI_CLASS_STORAGE_IDE ||
dev->class >> 16 == PCI_BASE_CLASS_DISPLAY)
{
/*
* All of these (may) have I/O scattered all around
* and may not use i/o-base address registers at all.
* So we just have to always enable I/O to these
* devices.
*/
cmd |= PCI_COMMAND_IO;
}
 
pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND,
cmd | PCI_COMMAND_MASTER);
 
DBG_DEVS(("layout_dev: bus %d slot %d VID 0x%x DID 0x%x class 0x%x cmd 0x%x\n",
bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device,
dev->class, cmd|PCI_COMMAND_MASTER));
}
 
 
static int layout_bus(struct pci_bus *bus)
{
unsigned int l, tio, bio, tmem, bmem;
struct pci_bus *child;
struct pci_dev *dev;
int found_vga = 0;
 
DBG_DEVS(("layout_bus: starting bus %d\n", bus->number));
 
if (!bus->devices && !bus->children)
return 0;
 
/*
* Align the current bases on appropriate boundaries (4K for
* IO and 1MB for memory).
*/
bio = io_base = ALIGN(io_base, 4*KB);
bmem = mem_base = ALIGN(mem_base, 1*MB);
 
/*
* There are times when the PCI devices have already been
* setup (e.g., by MILO or SRM). In these cases there is a
* window during which two devices may have an overlapping
* address range. To avoid this causing trouble, we first
* turn off the I/O and memory address decoders for all PCI
* devices. They'll be re-enabled only once all address
* decoders are programmed consistently.
*/
for (dev = bus->devices; dev; dev = dev->sibling) {
if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
#ifdef CONFIG_ALPHA_BOOK1
|| (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)
#endif /* CONFIG_ALPHA_BOOK1 */
) {
disable_dev(dev) ;
}
}
 
/*
* Allocate space to each device:
*/
DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number));
 
for (dev = bus->devices; dev; dev = dev->sibling) {
if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
#ifdef CONFIG_ALPHA_BOOK1
|| (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)
#endif /* CONFIG_ALPHA_BOOK1 */
) {
layout_dev(dev);
}
if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
found_vga = 1;
}
/*
* Recursively allocate space for all of the sub-buses:
*/
DBG_DEVS(("layout_bus: starting bus %d children\n", bus->number));
 
for (child = bus->children; child; child = child->next) {
found_vga += layout_bus(child);
}
/*
* Align the current bases on 4K and 1MB boundaries:
*/
tio = io_base = ALIGN(io_base, 4*KB);
tmem = mem_base = ALIGN(mem_base, 1*MB);
 
if (bus->self) {
struct pci_dev *bridge = bus->self;
/*
* Set up the top and bottom of the PCI I/O segment
* for this bus.
*/
pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
0x1c, &l);
l = (l & 0xffff0000) | ((bio >> 8) & 0x00f0) |
((tio - 1) & 0xf000);
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x1c, l);
/*
* Set up the top and bottom of the PCI Memory segment
* for this bus.
*/
l = ((bmem & 0xfff00000) >> 16) | ((tmem - 1) & 0xfff00000);
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x20, l);
/*
* Turn off downstream PF memory address range:
*/
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x24, 0x0000ffff);
/*
* Tell bridge that there is an ISA bus in the system,
* and (possibly) a VGA as well.
*/
l = 0x00040000; /* ISA present */
if (found_vga) l |= 0x00080000; /* VGA present */
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x3c, l);
/*
* Clear status bits, enable I/O (for downstream I/O),
* turn on master enable (for upstream I/O), turn on
* memory enable (for downstream memory), turn on
* master enable (for upstream memory and I/O).
*/
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x4, 0xffff0007);
}
return found_vga;
}
 
#endif /* !PCI_MODIFY */
 
 
/*
* Given the vendor and device ids, find the n'th instance of that device
* in the system.
*/
int pcibios_find_device (unsigned short vendor, unsigned short device_id,
unsigned short index, unsigned char *bus,
unsigned char *devfn)
{
unsigned int curr = 0;
struct pci_dev *dev;
 
for (dev = pci_devices; dev; dev = dev->next) {
if (dev->vendor == vendor && dev->device == device_id) {
if (curr == index) {
*devfn = dev->devfn;
*bus = dev->bus->number;
return PCIBIOS_SUCCESSFUL;
}
++curr;
}
}
return PCIBIOS_DEVICE_NOT_FOUND;
}
 
 
/*
* Given the class, find the n'th instance of that device
* in the system.
*/
int pcibios_find_class (unsigned int class_code, unsigned short index,
unsigned char *bus, unsigned char *devfn)
{
unsigned int curr = 0;
struct pci_dev *dev;
 
for (dev = pci_devices; dev; dev = dev->next) {
if (dev->class == class_code) {
if (curr == index) {
*devfn = dev->devfn;
*bus = dev->bus->number;
return PCIBIOS_SUCCESSFUL;
}
++curr;
}
}
return PCIBIOS_DEVICE_NOT_FOUND;
}
 
 
int pcibios_present(void)
{
return 1;
}
 
 
unsigned long pcibios_init(unsigned long mem_start,
unsigned long mem_end)
{
printk("Alpha PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
 
#if !PCI_MODIFY
printk("...NOT modifying existing (SRM) PCI configuration\n");
#endif
return mem_start;
}
 
/*
* The SRM console *disables* the IDE interface, this code ensures it's
* enabled.
*
* This code bangs on a control register of the 87312 Super I/O chip
* that implements parallel port/serial ports/IDE/FDI. Depending on
* the motherboard, the Super I/O chip can be configured through a
* pair of registers that are located either at I/O ports 0x26e/0x26f
* or 0x398/0x399. Unfortunately, autodetecting which base address is
* in use works only once (right after a reset). The Super I/O chip
* has the additional quirk that configuration register data must be
* written twice (I believe this is a safety feature to prevent
* accidental modification---fun, isn't it?).
*/
static inline void enable_ide(long ide_base)
{
int data;
 
outb(0, ide_base); /* set the index register for reg #0 */
data = inb(ide_base+1); /* read the current contents */
outb(0, ide_base); /* set the index register for reg #0 */
outb(data | 0x40, ide_base+1); /* turn on IDE */
outb(data | 0x40, ide_base+1); /* turn on IDE, really! */
}
 
/*
* A small note about bridges and interrupts. The DC 21050 (and later chips)
* adheres to the PCI-PCI bridge specification. This says that the interrupts
* on the other side of a bridge are swizzled in the following manner:
*
* Dev Interrupt Interrupt
* Pin on Pin on
* Device Connector
*
* 4 A A
* B B
* C C
* D D
*
* 5 A B
* B C
* C D
* D A
*
* 6 A C
* B D
* C A
* D B
*
* 7 A D
* B A
* C B
* D C
*
* Where A = pin 1, B = pin 2 and so on and pin=0 = default = A.
* Thus, each swizzle is ((pin-1) + (device#-4)) % 4
*
* The following code is somewhat simplistic as it assumes only one bridge.
* I will fix it later (david.rusling@reo.mts.dec.com).
*/
static inline unsigned char bridge_swizzle(unsigned char pin, unsigned int slot)
{
/* swizzle */
return (((pin-1) + slot) % 4) + 1 ;
}
 
#ifdef CONFIG_ALPHA_SRM_SETUP
/* look for mis-configured devices' IO space addresses behind bridges */
static void check_behind_io(struct pci_dev *dev)
{
struct pci_bus *bus = dev->bus;
unsigned int reg, orig_base, new_base, found_one = 0;
 
for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
/* read the current setting, check for I/O space and >= 64K */
pcibios_read_config_dword(bus->number, dev->devfn, reg, &orig_base);
if (!orig_base || !(orig_base & PCI_BASE_ADDRESS_SPACE_IO))
continue; /* unused or non-IO */
if (orig_base < 64*1024) {
#if 1
printk("check_behind_io: ALREADY OK! bus %d slot %d base 0x%x\n",
bus->number, PCI_SLOT(dev->devfn), orig_base);
#endif
if (orig_base & ~1)
continue; /* OK! */
orig_base = 0x12001; /* HACK! FIXME!! */
}
 
/* HACK ALERT! for now, just subtract 32K from the
original address, which should give us addresses
in the range 0x8000 and up */
new_base = orig_base - 0x8000;
#if 1
printk("check_behind_io: ALERT! bus %d slot %d old 0x%x new 0x%x\n",
bus->number, PCI_SLOT(dev->devfn), orig_base, new_base);
#endif
pcibios_write_config_dword(bus->number, dev->devfn,
reg, new_base);
 
io_dev_to_reset[io_reset_count] = dev;
io_reg_to_reset[io_reset_count] = reg;
io_to_reset[io_reset_count] = orig_base;
io_reset_count++;
found_one++;
} /* end for-loop */
 
/* if any were modified, gotta hack the bridge IO limits too... */
if (found_one) {
if (bus->self) {
struct pci_dev *bridge = bus->self;
unsigned int l;
/*
* Set up the top and bottom of the PCI I/O segment
* for this bus.
*/
pcibios_read_config_dword(bridge->bus->number,
bridge->devfn, 0x1c, &l);
#if 1
printk("check_behind_io: ALERT! bus %d slot %d oldLIM 0x%x\n",
bus->number, PCI_SLOT(bridge->devfn), l);
#endif
l = (l & 0xffff0000U) | 0xf080U; /* give it ALL */
pcibios_write_config_dword(bridge->bus->number,
bridge->devfn, 0x1c, l);
pcibios_write_config_dword(bridge->bus->number,
bridge->devfn,
0x3c, 0x00040000);
pcibios_write_config_dword(bridge->bus->number,
bridge->devfn,
0x4, 0xffff0007);
} else
printk("check_behind_io: WARNING! bus->self NULL\n");
}
}
#endif /* CONFIG_ALPHA_SRM_SETUP */
 
/*
* Most evaluation boards share most of the fixup code, which is isolated here.
* This function is declared "inline" as only one platform will ever be selected
* in any given kernel. If that platform doesn't need this code, we don't want
* it around as dead code.
*/
static inline void common_fixup(
long min_idsel,
long max_idsel,
long irqs_per_slot,
char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot],
long ide_base)
{
struct pci_dev *dev;
unsigned char pin;
unsigned char slot ;
 
/*
* Go through all devices, fixing up irqs as we see fit:
*/
for (dev = pci_devices; dev; dev = dev->next) {
if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
#if defined(CONFIG_ALPHA_EISA)
/* PCEB (PCI to EISA bridge) does not identify
itself as a bridge... :-( */
&& !((dev->vendor==0x8086) && (dev->device==0x482))
#endif
#ifdef CONFIG_ALPHA_BOOK1
|| (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)
#endif /* CONFIG_ALPHA_BOOK1 */
) {
dev->irq = 0;
/*
* This device is not on the primary bus,
* we need to figure out which
* interrupt pin it will come in on.
* We know which slot it will come
* in on 'cuz that slot is where the bridge is.
* Each time the interrupt
* line passes through a PCI-PCI bridge
* we must apply the swizzle function
* (see the inline static routine above).
*/
if (dev->bus->number != 0) {
/* read the pin and do the PCI-PCI bridge
interrupt pin swizzle */
pcibios_read_config_byte(dev->bus->number,
dev->devfn,
PCI_INTERRUPT_PIN,
&pin);
/* cope with 0 */
if (pin == 0) pin = 1 ;
/* follow the chain of bridges,
swizzling as we go */
#if defined(CONFIG_ALPHA_MIATA)
/* check first for the built-in bridge */
if ((PCI_SLOT(dev->bus->self->devfn) == 8) ||
(PCI_SLOT(dev->bus->self->devfn) == 20)) {
slot = PCI_SLOT(dev->devfn) + 9;
#if 0
printk("MIATA: bus 1 slot %d pin %d irq %d "
"min_idsel %d\n",
PCI_SLOT(dev->devfn), pin,
irq_tab[slot - min_idsel][pin],
min_idsel);
#endif
}
else /* must be a card-based bridge */
{
struct pci_dev *curr = dev ;
do {
if ((PCI_SLOT(curr->bus->self->devfn) == 8) ||
(PCI_SLOT(curr->bus->self->devfn) == 20))
{
slot = PCI_SLOT(curr->devfn) + 5;
break;
}
/* swizzle */
pin = bridge_swizzle(
pin, PCI_SLOT(curr->devfn)) ;
/* move up the chain of bridges */
curr = curr->bus->self ;
/* slot of the next bridge. */
slot = PCI_SLOT(curr->devfn);
} while (curr->bus->self) ;
}
#elif defined(CONFIG_ALPHA_NORITAKE)
/* check first for the built-in bridge */
if (PCI_SLOT(dev->bus->self->devfn) == 8) {
slot = PCI_SLOT(dev->devfn) + 15; /* WAG! */
#if 0
printk("NORITAKE: bus 1 slot %d pin %d "
"irq %d min_idsel %d\n",
PCI_SLOT(dev->devfn), pin,
irq_tab[slot - min_idsel][pin],
min_idsel);
#endif
}
else /* must be a card-based bridge */
{
struct pci_dev *curr = dev ;
do {
if (PCI_SLOT(curr->bus->self->devfn) == 8) {
slot = PCI_SLOT(curr->devfn) + 15;
break;
}
/* swizzle */
pin = bridge_swizzle(
pin, PCI_SLOT(curr->devfn)) ;
/* move up the chain of bridges */
curr = curr->bus->self ;
/* slot of the next bridge. */
slot = PCI_SLOT(curr->devfn);
} while (curr->bus->self) ;
}
#else /* not MIATA or NORITAKE */
{
struct pci_dev *curr = dev ;
do {
/* swizzle */
pin = bridge_swizzle(
pin, PCI_SLOT(curr->devfn)) ;
/* move up the chain of bridges */
curr = curr->bus->self ;
} while (curr->bus->self) ;
/* The slot is the slot of the last bridge. */
slot = PCI_SLOT(curr->devfn) ;
}
#endif /* not MIATA or NORITAKE */
#ifdef CONFIG_ALPHA_SRM_SETUP
/*
must make sure that SRM didn't screw up
and allocate an address > 64K for I/O
space behind a PCI-PCI bridge
*/
check_behind_io(dev);
#endif /* CONFIG_ALPHA_SRM_SETUP */
} else {
/* work out the slot */
slot = PCI_SLOT(dev->devfn) ;
/* read the pin */
pcibios_read_config_byte(dev->bus->number,
dev->devfn,
PCI_INTERRUPT_PIN,
&pin);
}
if (irq_tab[slot - min_idsel][pin] != -1)
dev->irq = irq_tab[slot - min_idsel][pin];
 
#ifdef CONFIG_ALPHA_SRM
{
unsigned char irq_orig;
/* read the original SRM-set IRQ and tell */
pcibios_read_config_byte(dev->bus->number,
dev->devfn,
PCI_INTERRUPT_LINE,
&irq_orig);
if (irq_orig != dev->irq) {
printk("common_fixup: bus %d slot 0x%x "
"SRM IRQ 0x%x changed to 0x%x\n",
dev->bus->number,PCI_SLOT(dev->devfn),
irq_orig, dev->irq);
#ifdef CONFIG_ALPHA_SRM_SETUP
irq_dev_to_reset[irq_reset_count] = dev;
irq_to_reset[irq_reset_count] = irq_orig;
irq_reset_count++;
#endif /* CONFIG_ALPHA_SRM_SETUP */
}
}
#endif /* SRM */
 
/* always tell the device, so the driver knows */
pcibios_write_config_byte(dev->bus->number, dev->devfn,
PCI_INTERRUPT_LINE, dev->irq);
 
DBG_DEVS(("common_fixup: bus %d slot 0x%x VID 0x%x DID 0x%x\n"
" int_slot 0x%x pin 0x%x irq 0x%x\n",
dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor,
dev->device, slot, pin, dev->irq));
 
/*
* if it's a VGA, enable its BIOS ROM at C0000
*/
if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
/* but if its a Cirrus 543x/544x DISABLE it, */
/* since enabling ROM disables the memory... */
if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) &&
(dev->device >= 0x00a0) &&
(dev->device <= 0x00ac)) {
pcibios_write_config_dword(
dev->bus->number,
dev->devfn,
PCI_ROM_ADDRESS,
0x00000000);
} else {
pcibios_write_config_dword(
dev->bus->number,
dev->devfn,
PCI_ROM_ADDRESS,
0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
}
}
/*
* if it's a SCSI, disable its BIOS ROM
*/
if ((dev->class >> 8) == PCI_CLASS_STORAGE_SCSI) {
pcibios_write_config_dword(dev->bus->number,
dev->devfn,
PCI_ROM_ADDRESS,
0x0000000);
}
#ifdef NOT_NOW
if ((dev->vendor == 0x1080) && (dev->device == 0xC693)
&& (PCI_FUNC(dev->devfn) == 1))
{
#if 0
{
int i; unsigned char b; unsigned short w; unsigned int d;
pcibios_read_config_word(dev->bus->number, dev->devfn, 4, &w);
printk("common_fixup: CYPRESS fn 1: PCI CMD reg = 0x%x\n", w);
pcibios_read_config_word(dev->bus->number, dev->devfn, 6, &w);
printk("common_fixup: CYPRESS fn 1: PCI STS reg = 0x%x\n", w);
for (i = 0x10; i <= 0x14; i+=4) {
pcibios_read_config_dword(dev->bus->number, dev->devfn, i, &d);
printk("common_fixup: CYPRESS fn 1: PCI register offset 0x%x = 0x%x\n",i,d);
}
pcibios_read_config_dword(dev->bus->number, dev->devfn, 0x20, &d);
printk("common_fixup: CYPRESS fn 1: PCI register offset 0x20 = 0x%x\n", d);
for (i = 0x3c; i <= 0x3d; i++) {
pcibios_read_config_byte(dev->bus->number, dev->devfn, i, &b);
printk("common_fixup: CYPRESS fn 1: PCI register offset 0x%x = 0x%x\n",i,b);
}
pcibios_read_config_dword(dev->bus->number, dev->devfn, 0x40, &d);
printk("common_fixup: CYPRESS fn 1: PCI register offset 0x40 = 0x%x\n",d);
}
#endif
}
if ((dev->vendor == 0x1080) && (dev->device == 0xC693)
&& (PCI_FUNC(dev->devfn) == 2))
{
#if 0
{
int i; unsigned char b; unsigned short w; unsigned int d;
pcibios_read_config_word(dev->bus->number, dev->devfn, 4, &w);
printk("common_fixup: CYPRESS fn 2: PCI CMD reg = 0x%x\n", w);
pcibios_read_config_word(dev->bus->number, dev->devfn, 6, &w);
printk("common_fixup: CYPRESS fn 2: PCI STS reg = 0x%x\n", w);
for (i = 0x10; i <= 0x14; i+=4) {
pcibios_read_config_dword(dev->bus->number, dev->devfn, i, &d);
printk("common_fixup: CYPRESS fn 2: PCI register offset 0x%x = 0x%x\n",i,d);
}
pcibios_read_config_dword(dev->bus->number, dev->devfn, 0x20, &d);
printk("common_fixup: CYPRESS fn 2: PCI register offset 0x20 = 0x%x\n", d);
for (i = 0x3c; i <= 0x3d; i++) {
pcibios_read_config_byte(dev->bus->number, dev->devfn, i, &b);
printk("common_fixup: CYPRESS fn 2: PCI register offset 0x%x = 0x%x\n",i,b);
}
pcibios_read_config_dword(dev->bus->number, dev->devfn, 0x40, &d);
printk("common_fixup: CYPRESS fn 2: PCI register offset 0x40 = 0x%x\n",d);
}
#endif
}
#endif /* NOT_NOW */
} else { /* it *is* a bridge... */
#ifdef NOT_NOW
/*
* if it's CYPRESS PCI-ISA bridge, disable IDE
* interrupt routing through PCI (ie do through PIC)
*/
if ((dev->vendor == 0x1080) && (dev->device == 0xC693)
&& (PCI_FUNC(dev->devfn) == 0))
{
#if 0
{
int i; unsigned char d; unsigned short w;
pcibios_read_config_word(dev->bus->number, dev->devfn, 4, &w);
printk("common_fixup: CYPRESS fn 0: PCI CMD reg = 0x%x\n", w);
for (i = 0x40; i < 0x50; i++) {
pcibios_read_config_byte(dev->bus->number, dev->devfn, i, &d);
printk("common_fixup: CYPRESS fn 0: PCI register offset 0x%x = 0x%x\n",
i, d);
}
for (i=1; i <= 5; i++) {
outb(i, 0x22);
printk("CY control reg %d: 0x%02x\n", i, inb(0x23));
}
}
#endif
#if 0
pcibios_write_config_word(dev->bus->number,
dev->devfn, 0x04, 0x0007);
 
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x40, 0x80);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x41, 0x80);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x42, 0x80);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x43, 0x80);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x44, 0x27);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x45, 0xe0);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x48, 0xf0);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x49, 0x40);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x4a, 0x00);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x4b, 0x80);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x4c, 0x80);
pcibios_write_config_byte(dev->bus->number,
dev->devfn, 0x4d, 0x70);
#endif
outb(0, DMA1_RESET_REG);
outb(0, DMA2_RESET_REG);
outb(DMA_MODE_CASCADE, DMA2_MODE_REG);
outb(0, DMA2_MASK_REG);
#if 0
outb(0, DMA1_CLR_MASK_REG);
outb(0, DMA2_CLR_MASK_REG);
#endif
}
#endif /* NOT_NOW */
}
}
if (ide_base) {
enable_ide(ide_base);
}
}
 
 
/*
* The EB66+ is very similar to the EB66 except that it does not have
* the on-board NCR and Tulip chips. In the code below, I have used
* slot number to refer to the id select line and *not* the slot
* number used in the EB66+ documentation. However, in the table,
* I've given the slot number, the id select line and the Jxx number
* that's printed on the board. The interrupt pins from the PCI slots
* are wired into 3 interrupt summary registers at 0x804, 0x805 and
* 0x806 ISA.
*
* In the table, -1 means don't assign an IRQ number. This is usually
* because it is the Saturn IO (SIO) PCI/ISA Bridge Chip.
*/
static inline void eb66p_fixup(void)
{
char irq_tab[5][5] = {
/*INT INTA INTB INTC INTD */
{16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J25 */
{16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J26 */
{ -1, -1, -1, -1, -1}, /* IdSel 8, SIO */
{16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 9, slot 2, J27 */
{16+3, 16+3, 16+8, 16+12, 16+6} /* IdSel 10, slot 3, J28 */
};
common_fixup(6, 10, 5, irq_tab, 0x398);
}
 
 
/*
* The PC164/LX164 have 19 PCI interrupts, four from each of the four PCI
* slots, the SIO, PCI/IDE, and USB.
*
* Each of the interrupts can be individually masked. This is
* accomplished by setting the appropriate bit in the mask register.
* A bit is set by writing a "1" to the desired position in the mask
* register and cleared by writing a "0". There are 3 mask registers
* located at ISA address 804h, 805h and 806h.
*
* An I/O read at ISA address 804h, 805h, 806h will return the
* state of the 11 PCI interrupts and not the state of the MASKED
* interrupts.
*
* Note: A write to I/O 804h, 805h, and 806h the mask register will be
* updated.
*
*
* ISA DATA<7:0>
* ISA +--------------------------------------------------------------+
* ADDRESS | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* +==============================================================+
* 0x804 | INTB0 | USB | IDE | SIO | INTA3 |INTA2 | INTA1 | INTA0 |
* +--------------------------------------------------------------+
* 0x805 | INTD0 | INTC3 | INTC2 | INTC1 | INTC0 |INTB3 | INTB2 | INTB1 |
* +--------------------------------------------------------------+
* 0x806 | Rsrv | Rsrv | Rsrv | Rsrv | Rsrv |INTD3 | INTD2 | INTD1 |
* +--------------------------------------------------------------+
* * Rsrv = reserved bits
* Note: The mask register is write-only.
*
* IdSel
* 5 32 bit PCI option slot 2
* 6 64 bit PCI option slot 0
* 7 64 bit PCI option slot 1
* 8 Saturn I/O
* 9 32 bit PCI option slot 3
* 10 USB
* 11 IDE
*
*/
 
static inline void alphapc164_fixup(void)
{
extern void SMC93X_Init(void);
char irq_tab[7][5] = {
/*INT INTA INTB INTC INTD */
{ 16+2, 16+2, 16+9, 16+13, 16+17}, /* IdSel 5, slot 2, J20 */
{ 16+0, 16+0, 16+7, 16+11, 16+15}, /* IdSel 6, slot 0, J29 */
{ 16+1, 16+1, 16+8, 16+12, 16+16}, /* IdSel 7, slot 1, J26 */
{ -1, -1, -1, -1, -1}, /* IdSel 8, SIO */
{ 16+3, 16+3, 16+10, 16+14, 16+18}, /* IdSel 9, slot 3, J19 */
{ 16+6, 16+6, 16+6, 16+6, 16+6}, /* IdSel 10, USB */
{ 16+5, 16+5, 16+5, 16+5, 16+5} /* IdSel 11, IDE */
};
 
common_fixup(5, 11, 5, irq_tab, 0);
 
SMC93X_Init();
}
 
/*
* The AlphaPC64 is very similar to the EB66+ except that its slots
* are numbered differently. In the code below, I have used slot
* number to refer to the id select line and *not* the slot number
* used in the AlphaPC64 documentation. However, in the table, I've
* given the slot number, the id select line and the Jxx number that's
* printed on the board. The interrupt pins from the PCI slots are
* wired into 3 interrupt summary registers at 0x804, 0x805 and 0x806
* ISA.
*
* In the table, -1 means don't assign an IRQ number. This is usually
* because it is the Saturn IO (SIO) PCI/ISA Bridge Chip.
*/
static inline void cabriolet_fixup(void)
{
char irq_tab[5][5] = {
/*INT INTA INTB INTC INTD */
{ 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5, slot 2, J21 */
{ 16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J19 */
{ 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J20 */
{ -1, -1, -1, -1, -1}, /* IdSel 8, SIO */
{ 16+3, 16+3, 16+8, 16+12, 16+16} /* IdSel 9, slot 3, J22 */
};
 
common_fixup(5, 9, 5, irq_tab, 0x398);
}
 
 
/*
* Fixup configuration for EB66/EB64+ boards.
*
* Both these boards use the same interrupt summary scheme. There are
* two 8 bit external summary registers as follows:
*
* Summary @ 0x26:
* Bit Meaning
* 0 Interrupt Line A from slot 0
* 1 Interrupt Line A from slot 1
* 2 Interrupt Line B from slot 0
* 3 Interrupt Line B from slot 1
* 4 Interrupt Line C from slot 0
* 5 Interrupt line from the two ISA PICs
* 6 Tulip (slot
* 7 NCR SCSI
*
* Summary @ 0x27
* Bit Meaning
* 0 Interrupt Line C from slot 1
* 1 Interrupt Line D from slot 0
* 2 Interrupt Line D from slot 1
* 3 RAZ
* 4 RAZ
* 5 RAZ
* 6 RAZ
* 7 RAZ
*
* The device to slot mapping looks like:
*
* Slot Device
* 5 NCR SCSI controller
* 6 PCI on board slot 0
* 7 PCI on board slot 1
* 8 Intel SIO PCI-ISA bridge chip
* 9 Tulip - DECchip 21040 ethernet controller
*
*
* This two layered interrupt approach means that we allocate IRQ 16 and
* above for PCI interrupts. The IRQ relates to which bit the interrupt
* comes in on. This makes interrupt processing much easier.
*/
static inline void eb66_and_eb64p_fixup(void)
{
char irq_tab[5][5] = {
/*INT INTA INTB INTC INTD */
{16+7, 16+7, 16+7, 16+7, 16+7}, /* IdSel 5, slot ?, ?? */
{16+0, 16+0, 16+2, 16+4, 16+9}, /* IdSel 6, slot ?, ?? */
{16+1, 16+1, 16+3, 16+8, 16+10}, /* IdSel 7, slot ?, ?? */
{ -1, -1, -1, -1, -1}, /* IdSel 8, SIO */
{16+6, 16+6, 16+6, 16+6, 16+6}, /* IdSel 9, TULIP */
};
common_fixup(5, 9, 5, irq_tab, 0);
}
 
 
/*
* Fixup configuration for MIKASA
*
* Summary @ 0x536:
* Bit Meaning
* 0 Interrupt Line A from slot 0
* 1 Interrupt Line B from slot 0
* 2 Interrupt Line C from slot 0
* 3 Interrupt Line D from slot 0
* 4 Interrupt Line A from slot 1
* 5 Interrupt line B from slot 1
* 6 Interrupt Line C from slot 1
* 7 Interrupt Line D from slot 1
* 8 Interrupt Line A from slot 2
* 9 Interrupt Line B from slot 2
*10 Interrupt Line C from slot 2
*11 Interrupt Line D from slot 2
*12 NCR 810 SCSI
*13 Power Supply Fail
*14 Temperature Warn
*15 Reserved
*
* The device to slot mapping looks like:
*
* Slot Device
* 6 NCR SCSI controller
* 7 Intel PCI-EISA bridge chip
* 11 PCI on board slot 0
* 12 PCI on board slot 1
* 13 PCI on board slot 2
*
*
* This two layered interrupt approach means that we allocate IRQ 16 and
* above for PCI interrupts. The IRQ relates to which bit the interrupt
* comes in on. This makes interrupt processing much easier.
*/
static inline void mikasa_fixup(void)
{
char irq_tab[8][5] = {
/*INT INTA INTB INTC INTD */
{16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 17, SCSI */
{ -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */
{ -1, -1, -1, -1, -1}, /* IdSel 19, ???? */
{ -1, -1, -1, -1, -1}, /* IdSel 20, ???? */
{ -1, -1, -1, -1, -1}, /* IdSel 21, ???? */
{ 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 0 */
{ 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */
{ 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 24, slot 2 */
};
common_fixup(6, 13, 5, irq_tab, 0);
}
 
/*
* Fixup configuration for NORITAKE
*
* Summary @ 0x542, summary register #1:
* Bit Meaning
* 0 All valid ints from summary regs 2 & 3
* 1 QLOGIC ISP1020A SCSI
* 2 Interrupt Line A from slot 0
* 3 Interrupt Line B from slot 0
* 4 Interrupt Line A from slot 1
* 5 Interrupt line B from slot 1
* 6 Interrupt Line A from slot 2
* 7 Interrupt Line B from slot 2
* 8 Interrupt Line A from slot 3
* 9 Interrupt Line B from slot 3
*10 Interrupt Line A from slot 4
*11 Interrupt Line B from slot 4
*12 Interrupt Line A from slot 5
*13 Interrupt Line B from slot 5
*14 Interrupt Line A from slot 6
*15 Interrupt Line B from slot 6
*
* Summary @ 0x544, summary register #2:
* Bit Meaning
* 0 OR of all unmasked ints in SR #2
* 1 OR of secondary bus ints
* 2 Interrupt Line C from slot 0
* 3 Interrupt Line D from slot 0
* 4 Interrupt Line C from slot 1
* 5 Interrupt line D from slot 1
* 6 Interrupt Line C from slot 2
* 7 Interrupt Line D from slot 2
* 8 Interrupt Line C from slot 3
* 9 Interrupt Line D from slot 3
*10 Interrupt Line C from slot 4
*11 Interrupt Line D from slot 4
*12 Interrupt Line C from slot 5
*13 Interrupt Line D from slot 5
*14 Interrupt Line C from slot 6
*15 Interrupt Line D from slot 6
*
* The device to slot mapping looks like:
*
* Slot Device
* 7 Intel PCI-EISA bridge chip
* 8 DEC PCI-PCI bridge chip
* 11 PCI on board slot 0
* 12 PCI on board slot 1
* 13 PCI on board slot 2
*
*
* This two layered interrupt approach means that we allocate IRQ 16 and
* above for PCI interrupts. The IRQ relates to which bit the interrupt
* comes in on. This makes interrupt processing much easier.
*/
static inline void noritake_fixup(void)
{
char irq_tab[15][5] = {
/*INT INTA INTB INTC INTD */
/* note: IDSELs 16, 17, and 25 are CORELLE only */
{ 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */
{ -1, -1, -1, -1, -1}, /* IdSel 17, S3 Trio64 */
{ -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */
{ -1, -1, -1, -1, -1}, /* IdSel 19, PPB */
{ -1, -1, -1, -1, -1}, /* IdSel 20, ???? */
{ -1, -1, -1, -1, -1}, /* IdSel 21, ???? */
{ 16+2, 16+2, 16+3, 32+2, 32+3}, /* IdSel 22, slot 0 */
{ 16+4, 16+4, 16+5, 32+4, 32+5}, /* IdSel 23, slot 1 */
{ 16+6, 16+6, 16+7, 32+6, 32+7}, /* IdSel 24, slot 2 */
{ 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 25, slot 3 */
/* the following 5 are actually on PCI bus 1, across the bridge */
{ 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */
{ 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 17, slot 3 */
{16+10, 16+10, 16+11, 32+10, 32+11}, /* IdSel 18, slot 4 */
{16+12, 16+12, 16+13, 32+12, 32+13}, /* IdSel 19, slot 5 */
{16+14, 16+14, 16+15, 32+14, 32+15}, /* IdSel 20, slot 6 */
};
common_fixup(5, 19, 5, irq_tab, 0);
}
 
/*
* Fixup configuration for ALCOR
*
* Summary @ GRU_INT_REQ:
* Bit Meaning
* 0 Interrupt Line A from slot 2
* 1 Interrupt Line B from slot 2
* 2 Interrupt Line C from slot 2
* 3 Interrupt Line D from slot 2
* 4 Interrupt Line A from slot 1
* 5 Interrupt line B from slot 1
* 6 Interrupt Line C from slot 1
* 7 Interrupt Line D from slot 1
* 8 Interrupt Line A from slot 0
* 9 Interrupt Line B from slot 0
*10 Interrupt Line C from slot 0
*11 Interrupt Line D from slot 0
*12 Interrupt Line A from slot 4
*13 Interrupt Line B from slot 4
*14 Interrupt Line C from slot 4
*15 Interrupt Line D from slot 4
*16 Interrupt Line D from slot 3
*17 Interrupt Line D from slot 3
*18 Interrupt Line D from slot 3
*19 Interrupt Line D from slot 3
*20-30 Reserved
*31 EISA interrupt
*
* The device to slot mapping looks like:
*
* Slot Device
* 7 PCI on board slot 0
* 8 PCI on board slot 3
* 9 PCI on board slot 4
* 10 PCEB (PCI-EISA bridge)
* 11 PCI on board slot 2
* 12 PCI on board slot 1
*
*
* This two layered interrupt approach means that we allocate IRQ 16 and
* above for PCI interrupts. The IRQ relates to which bit the interrupt
* comes in on. This makes interrupt processing much easier.
*/
static inline void alcor_fixup(void)
{
char irq_tab[7][5] = {
/*INT INTA INTB INTC INTD */
/* note: IDSEL 17 is XLT only */
{16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */
{ 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */
{16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 19, slot 3 */
{16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 20, slot 4 */
{ -1, -1, -1, -1, -1}, /* IdSel 21, PCEB */
{ 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */
{ 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */
};
common_fixup(6, 12, 5, irq_tab, 0);
}
 
#if 0
/*
* Fixup configuration for ALPHA XLT (EV5/EV56)
*
* Summary @ GRU_INT_REQ:
* Bit Meaning
* 0 Interrupt Line A from slot 2
* 1 Interrupt Line B from slot 2
* 2 Interrupt Line C from slot 2
* 3 Interrupt Line D from slot 2
* 4 Interrupt Line A from slot 1
* 5 Interrupt line B from slot 1
* 6 Interrupt Line C from slot 1
* 7 Interrupt Line D from slot 1
* 8 Interrupt Line A from slot 0
* 9 Interrupt Line B from slot 0
*10 Interrupt Line C from slot 0
*11 Interrupt Line D from slot 0
*12 NCR810 SCSI in slot 9
*13 DC-21040 (TULIP) in slot 6
*14-19 Reserved
*20-23 Jumpers (interrupt)
*24-27 Module revision
*28-30 Reserved
*31 EISA interrupt
*
* The device to slot mapping looks like:
*
* Slot Device
* 6 TULIP
* 7 PCI on board slot 0
* 8 none
* 9 SCSI
* 10 PCI-ISA bridge
* 11 PCI on board slot 2
* 12 PCI on board slot 1
*
*
* This two layered interrupt approach means that we allocate IRQ 16 and
* above for PCI interrupts. The IRQ relates to which bit the interrupt
* comes in on. This makes interrupt processing much easier.
*/
static inline void xlt_fixup(void)
{
char irq_tab[7][5] = {
/*INT INTA INTB INTC INTD */
{16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */
{ 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */
{ -1, -1, -1, -1, -1}, /* IdSel 19, none */
{16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 20, SCSI */
{ -1, -1, -1, -1, -1}, /* IdSel 21, SIO */
{ 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */
{ 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */
};
common_fixup(6, 12, 5, irq_tab, 0);
}
#endif /* 0 */
 
/*
* Fixup configuration for ALPHA SABLE (2100) - 2100A is different ??
*
* Summary Registers (536/53a/53c):
* Bit Meaning
*-----------------
* 0 PCI slot 0
* 1 NCR810 (builtin)
* 2 TULIP (builtin)
* 3 mouse
* 4 PCI slot 1
* 5 PCI slot 2
* 6 keyboard
* 7 floppy
* 8 COM2
* 9 parallel port
*10 EISA irq 3
*11 EISA irq 4
*12 EISA irq 5
*13 EISA irq 6
*14 EISA irq 7
*15 COM1
*16 EISA irq 9
*17 EISA irq 10
*18 EISA irq 11
*19 EISA irq 12
*20 EISA irq 13
*21 EISA irq 14
*22 NC
*23 IIC
*
* The device to slot mapping looks like:
*
* Slot Device
* 0 TULIP
* 1 SCSI
* 2 PCI-EISA bridge
* 3 none
* 4 none
* 5 none
* 6 PCI on board slot 0
* 7 PCI on board slot 1
* 8 PCI on board slot 2
*
*
* This two layered interrupt approach means that we allocate IRQ 16 and
* above for PCI interrupts. The IRQ relates to which bit the interrupt
* comes in on. This makes interrupt processing much easier.
*/
/* NOTE: the IRQ assignments below are arbitrary, but need to be consistent
with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables
in irq.c
*/
static inline void sable_fixup(void)
{
char irq_tab[9][5] = {
/*INT INTA INTB INTC INTD */
{ 32+0, 32+0, 32+0, 32+0, 32+0}, /* IdSel 0, TULIP */
{ 32+1, 32+1, 32+1, 32+1, 32+1}, /* IdSel 1, SCSI */
{ -1, -1, -1, -1, -1}, /* IdSel 2, SIO */
{ -1, -1, -1, -1, -1}, /* IdSel 3, none */
{ -1, -1, -1, -1, -1}, /* IdSel 4, none */
{ -1, -1, -1, -1, -1}, /* IdSel 5, none */
{ 32+2, 32+2, 32+2, 32+2, 32+2}, /* IdSel 6, slot 0 */
{ 32+3, 32+3, 32+3, 32+3, 32+3}, /* IdSel 7, slot 1 */
{ 32+4, 32+4, 32+4, 32+4, 32+4}, /* IdSel 8, slot 2 */
};
common_fixup(0, 8, 5, irq_tab, 0);
}
 
/*
* Fixup configuration for MIATA (EV56+PYXIS)
*
* Summary @ PYXIS_INT_REQ:
* Bit Meaning
* 0 Fan Fault
* 1 NMI
* 2 Halt/Reset switch
* 3 none
* 4 CID0 (Riser ID)
* 5 CID1 (Riser ID)
* 6 Interval timer
* 7 PCI-ISA Bridge
* 8 Ethernet
* 9 EIDE (deprecated, ISA 14/15 used)
*10 none
*11 USB
*12 Interrupt Line A from slot 4
*13 Interrupt Line B from slot 4
*14 Interrupt Line C from slot 4
*15 Interrupt Line D from slot 4
*16 Interrupt Line A from slot 5
*17 Interrupt line B from slot 5
*18 Interrupt Line C from slot 5
*19 Interrupt Line D from slot 5
*20 Interrupt Line A from slot 1
*21 Interrupt Line B from slot 1
*22 Interrupt Line C from slot 1
*23 Interrupt Line D from slot 1
*24 Interrupt Line A from slot 2
*25 Interrupt Line B from slot 2
*26 Interrupt Line C from slot 2
*27 Interrupt Line D from slot 2
*27 Interrupt Line A from slot 3
*29 Interrupt Line B from slot 3
*30 Interrupt Line C from slot 3
*31 Interrupt Line D from slot 3
*
* The device to slot mapping looks like:
*
* Slot Device
* 3 DC21142 Ethernet
* 4 EIDE CMD646
* 5 none
* 6 USB
* 7 PCI-ISA bridge
* 8 PCI-PCI Bridge (SBU Riser)
* 9 none
* 10 none
* 11 PCI on board slot 4 (SBU Riser)
* 12 PCI on board slot 5 (SBU Riser)
*
* These are behind the bridge, so I'm not sure what to do...
*
* 13 PCI on board slot 1 (SBU Riser)
* 14 PCI on board slot 2 (SBU Riser)
* 15 PCI on board slot 3 (SBU Riser)
*
*
* This two layered interrupt approach means that we allocate IRQ 16 and
* above for PCI interrupts. The IRQ relates to which bit the interrupt
* comes in on. This makes interrupt processing much easier.
*/
static inline void miata_fixup(void)
{
extern int es1888_init(void);
extern void SMC669_Init(void); /* might be a MiataGL */
char irq_tab[18][5] = {
/*INT INTA INTB INTC INTD */
{16+ 8, 16+ 8, 16+ 8, 16+ 8, 16+ 8}, /* IdSel 14, DC21142/3 */
{ -1, -1, -1, -1, -1}, /* IdSel 15, EIDE */
{ -1, -1, -1, -1, -1}, /* IdSel 16, none */
{ -1, -1, -1, -1, -1}, /* IdSel 17, none */
{ -1, -1, -1, -1, -1}, /* IdSel 18, PCI-ISA */
{ -1, -1, -1, -1, -1}, /* IdSel 19, PCI-PCI old */
{ -1, -1, -1, -1, -1}, /* IdSel 20, none */
{ -1, -1, -1, -1, -1}, /* IdSel 21, none */
{16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 22, slot 4 */
{16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 23, slot 5 */
/* the following 7 are actually on PCI bus 1, across the bridge */
{16+11, 16+11, 16+11, 16+11, 16+11}, /* IdSel 24, QLISP on GL */
{ -1, -1, -1, -1, -1}, /* IdSel 25, none */
{ -1, -1, -1, -1, -1}, /* IdSel 26, none */
{ -1, -1, -1, -1, -1}, /* IdSel 27, none */
{16+20, 16+20, 16+21, 16+22, 16+23}, /* IdSel 28, slot 1 */
{16+24, 16+24, 16+25, 16+26, 16+27}, /* IdSel 29, slot 2 */
{16+28, 16+28, 16+29, 16+30, 16+31}, /* IdSel 30, slot 3 */
/* this bridge is on the main bus of the later original MIATA */
{ -1, -1, -1, -1, -1}, /* IdSel 31, PCI-PCI new */
};
common_fixup(3, 20, 5, irq_tab, 0);
SMC669_Init(); /* might be a MiataGL, so try to find one of these */
es1888_init();
}
 
/*
* Fixup configuration for SX164 (PCA56+PYXIS)
*
* Summary @ PYXIS_INT_REQ:
* Bit Meaning
* 0 RSVD
* 1 NMI
* 2 Halt/Reset switch
* 3 MBZ
* 4 RAZ
* 5 RAZ
* 6 Interval timer (RTC)
* 7 PCI-ISA Bridge
* 8 Interrupt Line A from slot 3
* 9 Interrupt Line A from slot 2
*10 Interrupt Line A from slot 1
*11 Interrupt Line A from slot 0
*12 Interrupt Line B from slot 3
*13 Interrupt Line B from slot 2
*14 Interrupt Line B from slot 1
*15 Interrupt line B from slot 0
*16 Interrupt Line C from slot 3
*17 Interrupt Line C from slot 2
*18 Interrupt Line C from slot 1
*19 Interrupt Line C from slot 0
*20 Interrupt Line D from slot 3
*21 Interrupt Line D from slot 2
*22 Interrupt Line D from slot 1
*23 Interrupt Line D from slot 0
*
* IdSel
* 5 32 bit PCI option slot 2
* 6 64 bit PCI option slot 0
* 7 64 bit PCI option slot 1
* 8 Cypress I/O
* 9 32 bit PCI option slot 3
*
*/
 
static inline void sx164_fixup(void)
{
extern void SMC669_Init(void);
char irq_tab[5][5] = {
/*INT INTA INTB INTC INTD */
{ 16+ 9, 16+ 9, 16+13, 16+17, 16+21}, /* IdSel 5 slot 2 J17 */
{ 16+11, 16+11, 16+15, 16+19, 16+23}, /* IdSel 6 slot 0 J19 */
{ 16+10, 16+10, 16+14, 16+18, 16+22}, /* IdSel 7 slot 1 J18 */
{ -1, -1, -1, -1, -1}, /* IdSel 8 SIO */
{ 16+ 8, 16+ 8, 16+12, 16+16, 16+20} /* IdSel 9 slot 3 J15 */
};
 
common_fixup(5, 9, 5, irq_tab, 0);
 
SMC669_Init();
}
 
static inline void ruffian_fixup(void)
{
struct pci_dev *dev;
 
/*
* Go through all devices
*/
for (dev = pci_devices; dev; dev = dev->next) {
if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) {
/*
* if it's a VGA, enable its BIOS ROM at C0000
*/
if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
/* but if its a Cirrus 543x/544x DISABLE it, */
/* since enabling ROM disables the memory... */
if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) &&
(dev->device >= 0x00a0) &&
(dev->device <= 0x00ac)) {
pcibios_write_config_dword(
dev->bus->number,
dev->devfn,
PCI_ROM_ADDRESS,
0x00000000);
} else {
pcibios_write_config_dword(
dev->bus->number,
dev->devfn,
PCI_ROM_ADDRESS,
0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
}
}
/*
* if it's a SCSI, disable its BIOS ROM
*/
if ((dev->class >> 8) == PCI_CLASS_STORAGE_SCSI) {
pcibios_write_config_dword(dev->bus->number,
dev->devfn,
PCI_ROM_ADDRESS,
0x0000000);
}
}
}
}
 
/*
* The Takara has PCI devices 1, 2, and 3 configured to slots 20,
* 19, and 18 respectively, in the default configuration. They can
* also be jumpered to slots 8, 7, and 6 respectively, which is fun
* because the SIO ISA bridge can also be slot 7. However, the SIO
* doesn't explicitly generate PCI-type interrupts, so we can
* assign it whatever the hell IRQ we like and it doesn't matter.
*/
static inline void takara_fixup(void)
{
char irq_tab[15][5] = {
{ 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 6 == device 3 */
{ 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 7 == device 2 */
{ 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 8 == device 1 */
{ -1, -1, -1, -1, -1}, /* slot 9 == nothing */
{ -1, -1, -1, -1, -1}, /* slot 10 == nothing */
{ -1, -1, -1, -1, -1}, /* slot 11 == nothing */
{ -1, -1, -1, -1, -1}, /* slot 12 == nothing */
{ -1, -1, -1, -1, -1}, /* slot 13 == nothing */
{ -1, -1, -1, -1, -1}, /* slot 14 == nothing */
{ -1, -1, -1, -1, -1}, /* slot 15 == nothing */
{ -1, -1, -1, -1, -1}, /* slot 16 == nothing */
{ -1, -1, -1, -1, -1}, /* slot 17 == nothing */
{ 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 18 == device 3 */
{ 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 19 == device 2 */
{ 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 20 == device 1 */
};
 
common_fixup(6, 20, 5, irq_tab, 0x26e);
}
 
/*
* Fixup configuration for all boards that route the PCI interrupts
* through the SIO PCI/ISA bridge. This includes Noname (AXPpci33),
* Avanti (AlphaStation) and Kenetics's Platform 2000.
*/
static inline void sio_fixup(void)
{
struct pci_dev *dev;
/*
* The Noname board has 5 PCI slots with each of the 4
* interrupt pins routed to different pins on the PCI/ISA
* bridge (PIRQ0-PIRQ3). The table below is based on
* information available at:
*
* http://ftp.digital.com/pub/DEC/axppci/ref_interrupts.txt
*
* I have no information on the Avanti interrupt routing, but
* the routing seems to be identical to the Noname except
* that the Avanti has an additional slot whose routing I'm
* unsure of.
*
* pirq_tab[0] is a fake entry to deal with old PCI boards
* that have the interrupt pin number hardwired to 0 (meaning
* that they use the default INTA line, if they are interrupt
* driven at all).
*/
static const char pirq_tab[][5] = {
#ifdef CONFIG_ALPHA_P2K
{ 0, 0, -1, -1, -1}, /* idsel 6 (53c810) */
{-1, -1, -1, -1, -1}, /* idsel 7 (SIO: PCI/ISA bridge) */
{ 1, 1, 2, 3, 0}, /* idsel 8 (slot A) */
{ 2, 2, 3, 0, 1}, /* idsel 9 (slot B) */
{-1, -1, -1, -1, -1}, /* idsel 10 (unused) */
{-1, -1, -1, -1, -1}, /* idsel 11 (unused) */
{ 3, 3, -1, -1, -1}, /* idsel 12 (CMD0646) */
#else
{ 3, 3, 3, 3, 3}, /* idsel 6 (53c810) */
{-1, -1, -1, -1, -1}, /* idsel 7 (SIO: PCI/ISA bridge) */
{ 2, 2, -1, -1, -1}, /* idsel 8 (Noname hack: slot closest to ISA) */
{-1, -1, -1, -1, -1}, /* idsel 9 (unused) */
{-1, -1, -1, -1, -1}, /* idsel 10 (unused) */
{ 0, 0, 2, 1, 0}, /* idsel 11 KN25_PCI_SLOT0 */
{ 1, 1, 0, 2, 1}, /* idsel 12 KN25_PCI_SLOT1 */
{ 2, 2, 1, 0, 2}, /* idsel 13 KN25_PCI_SLOT2 */
{ 0, 0, 0, 0, 0}, /* idsel 14 AS255 TULIP */
#endif
};
/*
* route_tab selects irq routing in PCI/ISA bridge so that:
* PIRQ0 -> irq 15
* PIRQ1 -> irq 9
* PIRQ2 -> irq 10
* PIRQ3 -> irq 11
*
* This probably ought to be configurable via MILO. For
* example, sound boards seem to like using IRQ 9.
*/
#ifdef CONFIG_ALPHA_NONAME
/*
* For UDB, the only available PCI slot must not map to IRQ 9,
* since that's the builtin MSS sound chip. That PCI slot
* will map to PIRQ1 (for INTA at least), so we give it IRQ 15
* instead.
*
* Unfortunately we have to do this for NONAME as well, since
* they are co-indicated when the platform type "Noname" is
* selected... :-(
*/
#ifdef CONFIG_ALPHA_BOOK1
/* for the AlphaBook1, NCR810 SCSI is 14, PCMCIA controller is 15 */
const unsigned int route_tab = 0x0e0f0a0a;
#else /* CONFIG_ALPHA_BOOK1 */
const unsigned int route_tab = 0x0b0a0f09;
#endif /* CONFIG_ALPHA_BOOK1 */
#else /* CONFIG_ALPHA_NONAME */
const unsigned int route_tab = 0x0b0a090f;
#endif /* CONFIG_ALPHA_NONAME */
unsigned int level_bits;
unsigned char pin, slot;
int pirq;
 
pcibios_write_config_dword(0, PCI_DEVFN(7, 0), 0x60, route_tab);
 
/*
* Go through all devices, fixing up irqs as we see fit:
*/
level_bits = 0;
for (dev = pci_devices; dev; dev = dev->next) {
if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE)
#ifdef CONFIG_ALPHA_BOOK1
&& (dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA)
#endif /* CONFIG_ALPHA_BOOK1 */
)
continue;
dev->irq = 0;
if (dev->bus->number != 0) {
struct pci_dev *curr = dev ;
/*
* read the pin and do the PCI-PCI bridge
* interrupt pin swizzle
*/
pcibios_read_config_byte(dev->bus->number, dev->devfn,
PCI_INTERRUPT_PIN, &pin);
/* cope with 0 */
if (pin == 0) pin = 1 ;
/* follow the chain of bridges, swizzling as we go */
do {
/* swizzle */
pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)) ;
/* move up the chain of bridges */
curr = curr->bus->self ;
} while (curr->bus->self) ;
/* The slot is the slot of the last bridge. */
slot = PCI_SLOT(curr->devfn) ;
} else {
/* work out the slot */
slot = PCI_SLOT(dev->devfn) ;
/* read the pin */
pcibios_read_config_byte(dev->bus->number, dev->devfn,
PCI_INTERRUPT_PIN, &pin);
}
 
if (slot < 6 || slot >= 6 + sizeof(pirq_tab)/sizeof(pirq_tab[0])) {
printk("bios32.sio_fixup: "
"weird, found device %04x:%04x "
"in non-existent slot %d!!\n",
dev->vendor, dev->device, slot);
continue;
}
pirq = pirq_tab[slot - 6][pin];
 
DBG_DEVS(("sio_fixup: bus %d slot 0x%x VID 0x%x DID 0x%x\n"
" int_slot 0x%x pin 0x%x pirq 0x%x\n",
dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor,
dev->device, slot, pin, pirq));
/*
* if it's a VGA, enable its BIOS ROM at C0000
*/
if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
pcibios_write_config_dword(dev->bus->number, dev->devfn,
PCI_ROM_ADDRESS,
0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
}
if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
continue; /* for now, displays get no IRQ */
}
 
if (pirq < 0) {
printk("bios32.sio_fixup: "
"weird, device %04x:%04x coming in on slot %d has no irq line!!\n",
dev->vendor, dev->device, slot);
continue;
}
 
dev->irq = (route_tab >> (8 * pirq)) & 0xff;
 
#ifndef CONFIG_ALPHA_BOOK1
/* do not set *ANY* level triggers for AlphaBook1 */
/* must set the PCI IRQs to level triggered */
level_bits |= (1 << dev->irq);
#endif /* !CONFIG_ALPHA_BOOK1 */
 
#if PCI_MODIFY
/* tell the device: */
pcibios_write_config_byte(dev->bus->number, dev->devfn,
PCI_INTERRUPT_LINE, dev->irq);
#endif
 
#ifdef CONFIG_ALPHA_BOOK1
/*
* on the AlphaBook1, the PCMCIA chip (Cirrus 6729)
* is sensitive to PCI bus bursts, so we must DISABLE
* burst mode for the NCR 8xx SCSI... :-(
*
* Note that the NCR810 SCSI driver must preserve the
* setting of the bit in order for this to work. At the
* moment (2.0.29), ncr53c8xx.c does NOT do this, but
* 53c7,8xx.c DOES...
*/
if ((dev->vendor == PCI_VENDOR_ID_NCR) &&
((dev->device == PCI_DEVICE_ID_NCR_53C810) ||
(dev->device == PCI_DEVICE_ID_NCR_53C815) ||
(dev->device == PCI_DEVICE_ID_NCR_53C820) ||
(dev->device == PCI_DEVICE_ID_NCR_53C825)
)) {
unsigned int io_port;
unsigned char ctest4;
 
pcibios_read_config_dword(dev->bus->number, dev->devfn,
PCI_BASE_ADDRESS_0, &io_port);
io_port &= PCI_BASE_ADDRESS_IO_MASK;
ctest4 = inb(io_port+0x21);
if (!(ctest4 & 0x80)) {
printk("AlphaBook1 NCR init: setting burst disable\n");
outb(ctest4 | 0x80, io_port+0x21);
}
}
#endif /* CONFIG_ALPHA_BOOK1 */
 
} /* end for devs */
 
/*
* Now, make all PCI interrupts level sensitive. Notice:
* these registers must be accessed byte-wise. inw()/outw()
* don't work.
*
* Make sure to turn off any level bits set for IRQs 9,10,11,15,
* so that the only bits getting set are for devices actually found.
* Note that we do preserve the remainder of the bits, which we hope
* will be set correctly by ARC/SRM.
*
* Note: we at least preserve any level-set bits on AlphaBook1
*/
level_bits |= ((inb(0x4d0) | (inb(0x4d1) << 8)) & 0x71ff);
outb((level_bits >> 0) & 0xff, 0x4d0);
outb((level_bits >> 8) & 0xff, 0x4d1);
 
#ifdef CONFIG_ALPHA_BOOK1
{
unsigned char orig, config;
/* on the AlphaBook1, make sure that register PR1 indicates 1Mb mem */
outb(0x0f, 0x3ce); orig = inb(0x3cf); /* read PR5 */
outb(0x0f, 0x3ce); outb(0x05, 0x3cf); /* unlock PR0-4 */
outb(0x0b, 0x3ce); config = inb(0x3cf); /* read PR1 */
if ((config & 0xc0) != 0xc0) {
printk("AlphaBook1 VGA init: setting 1Mb memory\n");
config |= 0xc0;
outb(0x0b, 0x3ce); outb(config, 0x3cf); /* write PR1 */
}
outb(0x0f, 0x3ce); outb(orig, 0x3cf); /* (re)lock PR0-4 */
}
#endif /* CONFIG_ALPHA_BOOK1 */
 
#ifndef CONFIG_ALPHA_BOOK1
/* do not do IDE init for AlphaBook1 */
enable_ide(0x26e);
#endif /* !CONFIG_ALPHA_BOOK1 */
}
 
 
#ifdef CONFIG_TGA_CONSOLE
extern void tga_console_find(void);
#endif /* CONFIG_TGA_CONSOLE */
 
unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
{
#if PCI_MODIFY && !defined(CONFIG_ALPHA_RUFFIAN)
/*
* Scan the tree, allocating PCI memory and I/O space.
*/
layout_bus(&pci_root);
#endif
/*
* Now is the time to do all those dirty little deeds...
*/
#if defined(CONFIG_ALPHA_NONAME) || defined(CONFIG_ALPHA_AVANTI) || defined(CONFIG_ALPHA_P2K)
sio_fixup();
#elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB164)
cabriolet_fixup();
#elif defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)
alphapc164_fixup();
#elif defined(CONFIG_ALPHA_EB66P)
eb66p_fixup();
#elif defined(CONFIG_ALPHA_EB66)
eb66_and_eb64p_fixup();
#elif defined(CONFIG_ALPHA_EB64P)
eb66_and_eb64p_fixup();
#elif defined(CONFIG_ALPHA_MIKASA)
mikasa_fixup();
#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
alcor_fixup();
#elif defined(CONFIG_ALPHA_SABLE)
sable_fixup();
#elif defined(CONFIG_ALPHA_MIATA)
miata_fixup();
#elif defined(CONFIG_ALPHA_NORITAKE)
noritake_fixup();
#elif defined(CONFIG_ALPHA_SX164)
sx164_fixup();
#elif defined(CONFIG_ALPHA_TAKARA)
takara_fixup();
#elif defined(CONFIG_ALPHA_RUFFIAN)
ruffian_fixup();
#else
# error You must tell me what kind of platform you want.
#endif
 
#ifdef CONFIG_TGA_CONSOLE
tga_console_find();
#endif /* CONFIG_TGA_CONSOLE */
 
return mem_start;
}
 
 
const char *pcibios_strerror (int error)
{
static char buf[80];
 
switch (error) {
case PCIBIOS_SUCCESSFUL:
return "SUCCESSFUL";
 
case PCIBIOS_FUNC_NOT_SUPPORTED:
return "FUNC_NOT_SUPPORTED";
 
case PCIBIOS_BAD_VENDOR_ID:
return "SUCCESSFUL";
 
case PCIBIOS_DEVICE_NOT_FOUND:
return "DEVICE_NOT_FOUND";
 
case PCIBIOS_BAD_REGISTER_NUMBER:
return "BAD_REGISTER_NUMBER";
 
default:
sprintf (buf, "UNKNOWN RETURN 0x%x", error);
return buf;
}
}
 
asmlinkage int sys_pciconfig_read(
unsigned long bus,
unsigned long dfn,
unsigned long off,
unsigned long len,
unsigned char *buf)
{
unsigned char ubyte;
unsigned short ushort;
unsigned int uint;
long err = 0;
 
switch (len) {
case 1:
err = pcibios_read_config_byte(bus, dfn, off, &ubyte);
if (err != PCIBIOS_SUCCESSFUL)
ubyte = 0xff;
put_user(ubyte, buf);
break;
case 2:
err = pcibios_read_config_word(bus, dfn, off, &ushort);
if (err != PCIBIOS_SUCCESSFUL)
ushort = 0xffff;
put_user(ushort, (unsigned short *)buf);
break;
case 4:
err = pcibios_read_config_dword(bus, dfn, off, &uint);
if (err != PCIBIOS_SUCCESSFUL)
uint = 0xffffffff;
put_user(uint, (unsigned int *)buf);
break;
default:
err = -EINVAL;
break;
}
return err;
}
asmlinkage int sys_pciconfig_write(
unsigned long bus,
unsigned long dfn,
unsigned long off,
unsigned long len,
unsigned char *buf)
{
unsigned char ubyte;
unsigned short ushort;
unsigned int uint;
long err = 0;
 
switch (len) {
case 1:
ubyte = get_user(buf);
err = pcibios_write_config_byte(bus, dfn, off, ubyte);
if (err != PCIBIOS_SUCCESSFUL) {
err = -EFAULT;
}
break;
case 2:
ushort = get_user((unsigned short *)buf);
err = pcibios_write_config_word(bus, dfn, off, ushort);
if (err != PCIBIOS_SUCCESSFUL) {
err = -EFAULT;
}
break;
case 4:
uint = get_user((unsigned int *)buf);
err = pcibios_write_config_dword(bus, dfn, off, uint);
if (err != PCIBIOS_SUCCESSFUL) {
err = -EFAULT;
}
break;
default:
err = -EINVAL;
break;
}
return err;
}
 
#if (defined(CONFIG_ALPHA_PC164) || \
defined(CONFIG_ALPHA_LX164) || \
defined(CONFIG_ALPHA_SX164) || \
defined(CONFIG_ALPHA_EB164) || \
defined(CONFIG_ALPHA_EB66P) || \
defined(CONFIG_ALPHA_CABRIOLET)) && defined(CONFIG_ALPHA_SRM)
 
/*
on the above machines, under SRM console, we must use the CSERVE PALcode
routine to manage the interrupt mask for us, otherwise, the kernel/HW get
out of sync with what the PALcode thinks it needs to deliver/ignore
*/
void
cserve_update_hw(unsigned long irq, unsigned long mask)
{
extern void cserve_ena(unsigned long);
extern void cserve_dis(unsigned long);
 
if (mask & (1UL << irq))
/* disable */
cserve_dis(irq - 16);
else
/* enable */
cserve_ena(irq - 16);
return;
}
#endif /* (PC164 || LX164 || SX164 || EB164 || CABRIO) && SRM */
 
#ifdef CONFIG_ALPHA_MIATA
 
/* init the built-in ES1888 sound chip (SB16 compatible) */
int es1888_init(void)
{
/* sequence of IO reads to init the audio controller */
inb(0x0229);
inb(0x0229);
inb(0x0229);
inb(0x022b);
inb(0x0229);
inb(0x022b);
inb(0x0229);
inb(0x0229);
inb(0x022b);
inb(0x0229);
inb(0x0220); /* this sets the base address to 0x220 */
 
/* sequence to set DMA channels */
outb(0x01, 0x0226); /* reset */
inb(0x0226); /* pause */
outb(0x00, 0x0226); /* release reset */
while (!(inb(0x022e) & 0x80)) /* wait for bit 7 to assert*/
continue;
inb(0x022a); /* pause */
outb(0xc6, 0x022c); /* enable extended mode */
while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */
continue;
outb(0xb1, 0x022c); /* setup for write to Interrupt CR */
while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */
continue;
outb(0x14, 0x022c); /* set IRQ 5 */
while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */
continue;
outb(0xb2, 0x022c); /* setup for write to DMA CR */
while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */
continue;
outb(0x18, 0x022c); /* set DMA channel 1 */
 
return 0;
}
 
#endif /* CONFIG_ALPHA_MIATA */
 
#ifdef CONFIG_ALPHA_SRM_SETUP
void reset_for_srm(void)
{
extern void scrreset(void);
struct pci_dev *dev;
int i;
 
/* reset any IRQs that we changed */
for (i = 0; i < irq_reset_count; i++) {
dev = irq_dev_to_reset[i];
 
pcibios_write_config_byte(dev->bus->number, dev->devfn,
PCI_INTERRUPT_LINE, irq_to_reset[i]);
#if 1
printk("reset_for_srm: bus %d slot 0x%x "
"SRM IRQ 0x%x changed back from 0x%x\n",
dev->bus->number, PCI_SLOT(dev->devfn),
irq_to_reset[i], dev->irq);
#endif
}
 
/* reset any IO addresses that we changed */
for (i = 0; i < io_reset_count; i++) {
dev = io_dev_to_reset[i];
 
pcibios_write_config_byte(dev->bus->number, dev->devfn,
io_reg_to_reset[i], io_to_reset[i]);
#if 1
printk("reset_for_srm: bus %d slot 0x%x "
"SRM IO restored to 0x%x\n",
dev->bus->number, PCI_SLOT(dev->devfn),
io_to_reset[i]);
#endif
}
 
/* reset the visible screen to the top of display memory */
scrreset();
}
#endif /* CONFIG_ALPHA_SRM_SETUP */
 
#endif /* CONFIG_PCI */
/kernel/ksyms.c
0,0 → 1,121
/*
* linux/arch/alpha/kernel/ksyms.c
*
* Export the alpha-specific functions that are needed for loadable
* modules.
*/
 
#include <linux/string.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/user.h>
#include <linux/elfcore.h>
#include <asm/io.h>
#include <asm/checksum.h>
#include <asm/hwrpb.h>
 
extern void bcopy (const char *src, char *dst, int len);
extern struct hwrpb_struct *hwrpb;
 
/* these are C runtime functions with special calling conventions: */
extern void __divl (void);
extern void __reml (void);
extern void __divq (void);
extern void __remq (void);
extern void __divlu (void);
extern void __remlu (void);
extern void __divqu (void);
extern void __remqu (void);
 
extern void start_thread(struct pt_regs *, unsigned long, unsigned long);
extern void dump_thread(struct pt_regs *, struct user *);
extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
 
 
static struct symbol_table arch_symbol_table = {
#include <linux/symtab_begin.h>
/* platform dependent support */
 
X(_inb),
X(_inw),
X(_inl),
X(_outb),
X(_outw),
X(_outl),
X(_readb),
X(_readw),
X(_readl),
X(_writeb),
X(_writew),
X(_writel),
X(__divl),
X(__reml),
X(__divq),
X(__remq),
X(__divlu),
X(__remlu),
X(__divqu),
X(__remqu),
X(insl),
X(insw),
X(insb),
X(outsl),
X(outsw),
X(outsb),
X(strcat),
X(strncat),
X(strcmp),
X(strcpy),
X(strlen),
X(strncmp),
X(strncpy),
X(strnlen),
X(strstr),
X(strtok),
X(strchr),
X(strrchr),
X(memcmp),
X(memmove),
X(__memcpy),
X(__constant_c_memset),
 
X(csum_tcpudp_magic),
X(ip_fast_csum),
X(ip_compute_csum),
 
X(start_thread),
X(dump_thread),
X(dump_fpu),
X(hwrpb),
X(wrusp),
 
/*
* The following are special because they're not called
* explicitly (the C compiler or assembler generates them in
* response to division operations). Fortunately, their
* interface isn't gonna change any time soon now, so it's OK
* to leave it out of version control.
*/
# undef bcopy
# undef memcpy
# undef memset
XNOVERS(__divl),
XNOVERS(__divlu),
XNOVERS(__divq),
XNOVERS(__divqu),
XNOVERS(__reml),
XNOVERS(__remlu),
XNOVERS(__remq),
XNOVERS(__remqu),
XNOVERS(memcpy),
XNOVERS(memset),
/* these shouldn't be necessary---they should be versioned: */
XNOVERS(__memcpy),
XNOVERS(__memset),
#include <linux/symtab_end.h>
};
 
void arch_syms_export(void)
{
register_symtab(&arch_symbol_table);
}
/kernel/pyxis.c
0,0 → 1,670
/*
* Code common to all PYXIS chips.
*
* Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
*
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/sched.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/ptrace.h>
#include <asm/mmu_context.h>
 
extern struct hwrpb_struct *hwrpb;
extern asmlinkage void wrmces(unsigned long mces);
 
/*
* BIOS32-style PCI interface:
*/
 
#ifdef CONFIG_ALPHA_PYXIS
 
#ifdef DEBUG
# define DBG(args) printk args
#else
# define DBG(args)
#endif
 
#define DEBUG_MCHECK
#ifdef DEBUG_MCHECK
# define DBG_MCK(args) printk args
/* #define DEBUG_MCHECK_DUMP */
#else
# define DBG_MCK(args)
#endif
 
#define vuip volatile unsigned int *
#define vulp volatile unsigned long *
 
static volatile unsigned int PYXIS_mcheck_expected = 0;
static volatile unsigned int PYXIS_mcheck_taken = 0;
static unsigned int PYXIS_jd;
 
#ifdef CONFIG_ALPHA_SRM_SETUP
unsigned int PYXIS_DMA_WIN_BASE = PYXIS_DMA_WIN_BASE_DEFAULT;
unsigned int PYXIS_DMA_WIN_SIZE = PYXIS_DMA_WIN_SIZE_DEFAULT;
unsigned long pyxis_sm_base_r1, pyxis_sm_base_r2, pyxis_sm_base_r3;
#endif /* SRM_SETUP */
 
/*
* Given a bus, device, and function number, compute resulting
* configuration space address and setup the PYXIS_HAXR2 register
* accordingly. It is therefore not safe to have concurrent
* invocations to configuration space access routines, but there
* really shouldn't be any need for this.
*
* Type 0:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:11 Device select bit.
* 10:8 Function number
* 7:2 Register number
*
* Type 1:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:24 reserved
* 23:16 bus number (8 bits = 128 possible buses)
* 15:11 Device number (5 bits)
* 10:8 function number
* 7:2 register number
*
* Notes:
* The function number selects which function of a multi-function device
* (e.g., scsi and ethernet).
*
* The register selects a DWORD (32 bit) register offset. Hence it
* doesn't get shifted by 2 bits as we want to "drop" the bottom two
* bits.
*/
static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned long *pci_addr,
unsigned char *type1)
{
unsigned long addr;
 
DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n",
bus, device_fn, where, pci_addr, type1));
 
if (bus == 0) {
int device;
 
device = device_fn >> 3;
/* type 0 configuration cycle: */
#if NOT_NOW
if (device > 20) {
DBG(("mk_conf_addr: device (%d) > 20, returning -1\n",
device));
return -1;
}
#endif
*type1 = 0;
addr = (device_fn << 8) | (where);
} else {
/* type 1 configuration cycle: */
*type1 = 1;
addr = (bus << 16) | (device_fn << 8) | (where);
}
*pci_addr = addr;
DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
return 0;
}
 
 
static unsigned int conf_read(unsigned long addr, unsigned char type1)
{
unsigned long flags;
unsigned int stat0, value, temp;
unsigned int pyxis_cfg = 0; /* to keep gcc quiet */
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
 
/* reset status register to avoid losing errors: */
stat0 = *((vuip)PYXIS_ERR);
*((vuip)PYXIS_ERR) = stat0; mb();
temp = *((vuip)PYXIS_ERR);
DBG(("conf_read: PYXIS ERR was 0x%x\n", stat0));
/* if Type1 access, must set PYXIS CFG */
if (type1) {
pyxis_cfg = *((vuip)PYXIS_CFG);
*((vuip)PYXIS_CFG) = pyxis_cfg | 1; mb();
temp = *((vuip)PYXIS_CFG);
DBG(("conf_read: TYPE1 access\n"));
}
 
mb();
draina();
PYXIS_mcheck_expected = 1;
PYXIS_mcheck_taken = 0;
mb();
/* access configuration space: */
value = *((vuip)addr);
mb();
mb();
if (PYXIS_mcheck_taken) {
PYXIS_mcheck_taken = 0;
value = 0xffffffffU;
mb();
}
PYXIS_mcheck_expected = 0;
mb();
 
/* if Type1 access, must reset IOC CFG so normal IO space ops work */
if (type1) {
*((vuip)PYXIS_CFG) = pyxis_cfg & ~1; mb();
temp = *((vuip)PYXIS_CFG);
}
 
DBG(("conf_read(): finished\n"));
 
restore_flags(flags);
return value;
}
 
 
static void conf_write(unsigned long addr, unsigned int value, unsigned char type1)
{
unsigned long flags;
unsigned int stat0, temp;
unsigned int pyxis_cfg = 0; /* to keep gcc quiet */
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
/* reset status register to avoid losing errors: */
stat0 = *((vuip)PYXIS_ERR);
*((vuip)PYXIS_ERR) = stat0; mb();
temp = *((vuip)PYXIS_ERR);
DBG(("conf_write: PYXIS ERR was 0x%x\n", stat0));
/* if Type1 access, must set PYXIS CFG */
if (type1) {
pyxis_cfg = *((vuip)PYXIS_CFG);
*((vuip)PYXIS_CFG) = pyxis_cfg | 1; mb();
temp = *((vuip)PYXIS_CFG);
DBG(("conf_read: TYPE1 access\n"));
}
 
draina();
PYXIS_mcheck_expected = 1;
mb();
/* access configuration space: */
*((vuip)addr) = value;
mb();
mb();
temp = *((vuip)PYXIS_ERR); /* do a PYXIS read to force the write */
PYXIS_mcheck_expected = 0;
mb();
 
/* if Type1 access, must reset IOC CFG so normal IO space ops work */
if (type1) {
*((vuip)PYXIS_CFG) = pyxis_cfg & ~1; mb();
temp = *((vuip)PYXIS_CFG);
}
 
DBG(("conf_write(): finished\n"));
restore_flags(flags);
}
 
 
int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char *value)
{
unsigned long addr = PYXIS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xff;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
 
addr |= (pci_addr << 5) + 0x00;
 
*value = conf_read(addr, type1) >> ((where & 3) * 8);
 
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short *value)
{
unsigned long addr = PYXIS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xffff;
 
if (where & 0x1) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
return PCIBIOS_SUCCESSFUL;
}
 
addr |= (pci_addr << 5) + 0x08;
 
*value = conf_read(addr, type1) >> ((where & 3) * 8);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int *value)
{
unsigned long addr = PYXIS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xffffffff;
if (where & 0x3) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
*value = conf_read(addr, type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char value)
{
unsigned long addr = PYXIS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x00;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short value)
{
unsigned long addr = PYXIS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x08;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int value)
{
unsigned long addr = PYXIS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end)
{
unsigned int pyxis_err;
 
#if 0
printk("pyxis_init: PYXIS_ERR_MASK 0x%x\n", *(vuip)PYXIS_ERR_MASK);
printk("pyxis_init: PYXIS_ERR 0x%x\n", *(vuip)PYXIS_ERR);
 
printk("pyxis_init: PYXIS_INT_REQ 0x%lx\n", *(vulp)PYXIS_INT_REQ);
printk("pyxis_init: PYXIS_INT_MASK 0x%lx\n", *(vulp)PYXIS_INT_MASK);
printk("pyxis_init: PYXIS_INT_ROUTE 0x%lx\n", *(vulp)PYXIS_INT_ROUTE);
printk("pyxis_init: PYXIS_INT_HILO 0x%lx\n", *(vulp)PYXIS_INT_HILO);
printk("pyxis_init: PYXIS_INT_CNFG 0x%x\n", *(vuip)PYXIS_INT_CNFG);
printk("pyxis_init: PYXIS_RT_COUNT 0x%lx\n", *(vulp)PYXIS_RT_COUNT);
#endif
 
#if 0
printk("pyxis_init: W0 BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)PYXIS_W0_BASE, *(vuip)PYXIS_W0_MASK, *(vuip)PYXIS_T0_BASE);
printk("pyxis_init: W1 BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)PYXIS_W1_BASE, *(vuip)PYXIS_W1_MASK, *(vuip)PYXIS_T1_BASE);
printk("pyxis_init: W2 BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)PYXIS_W2_BASE, *(vuip)PYXIS_W2_MASK, *(vuip)PYXIS_T2_BASE);
printk("pyxis_init: W3 BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)PYXIS_W3_BASE, *(vuip)PYXIS_W3_MASK, *(vuip)PYXIS_T3_BASE);
#endif
 
/*
* Set up error reporting. Make sure CPU_PE is OFF in the mask.
*/
pyxis_err = *(vuip)PYXIS_ERR_MASK;
pyxis_err &= ~4;
*(vuip)PYXIS_ERR_MASK = pyxis_err;
mb();
pyxis_err = *(vuip)PYXIS_ERR_MASK;
 
pyxis_err = *(vuip)PYXIS_ERR ;
pyxis_err |= 0x180; /* master/target abort */
*(vuip)PYXIS_ERR = pyxis_err ;
mb() ;
pyxis_err = *(vuip)PYXIS_ERR ;
 
#ifdef CONFIG_ALPHA_SRM_SETUP
/* check window 0 for enabled and mapped to 0 */
if (((*(vuip)PYXIS_W0_BASE & 3) == 1) &&
(*(vuip)PYXIS_T0_BASE == 0) &&
((*(vuip)PYXIS_W0_MASK & 0xfff00000U) > 0x0ff00000U))
{
PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W0_BASE & 0xfff00000U;
PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W0_MASK & 0xfff00000U;
PYXIS_DMA_WIN_SIZE += 0x00100000U;
#if 1
printk("pyxis_init: using Window 0 settings\n");
printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)PYXIS_W0_BASE,
*(vuip)PYXIS_W0_MASK,
*(vuip)PYXIS_T0_BASE);
#endif
}
else /* check window 1 for enabled and mapped to 0 */
if (((*(vuip)PYXIS_W1_BASE & 3) == 1) &&
(*(vuip)PYXIS_T1_BASE == 0) &&
((*(vuip)PYXIS_W1_MASK & 0xfff00000U) > 0x0ff00000U))
{
PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W1_BASE & 0xfff00000U;
PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W1_MASK & 0xfff00000U;
PYXIS_DMA_WIN_SIZE += 0x00100000U;
#if 1
printk("pyxis_init: using Window 1 settings\n");
printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)PYXIS_W1_BASE,
*(vuip)PYXIS_W1_MASK,
*(vuip)PYXIS_T1_BASE);
#endif
}
else /* check window 2 for enabled and mapped to 0 */
if (((*(vuip)PYXIS_W2_BASE & 3) == 1) &&
(*(vuip)PYXIS_T2_BASE == 0) &&
((*(vuip)PYXIS_W2_MASK & 0xfff00000U) > 0x0ff00000U))
{
PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W2_BASE & 0xfff00000U;
PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W2_MASK & 0xfff00000U;
PYXIS_DMA_WIN_SIZE += 0x00100000U;
#if 1
printk("pyxis_init: using Window 2 settings\n");
printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)PYXIS_W2_BASE,
*(vuip)PYXIS_W2_MASK,
*(vuip)PYXIS_T2_BASE);
#endif
}
else /* check window 3 for enabled and mapped to 0 */
if (((*(vuip)PYXIS_W3_BASE & 3) == 1) &&
(*(vuip)PYXIS_T3_BASE == 0) &&
((*(vuip)PYXIS_W3_MASK & 0xfff00000U) > 0x0ff00000U))
{
PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W3_BASE & 0xfff00000U;
PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W3_MASK & 0xfff00000U;
PYXIS_DMA_WIN_SIZE += 0x00100000U;
#if 1
printk("pyxis_init: using Window 3 settings\n");
printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)PYXIS_W3_BASE,
*(vuip)PYXIS_W3_MASK,
*(vuip)PYXIS_T3_BASE);
#endif
}
else /* we must use our defaults which were pre-initialized... */
#endif /* SRM_SETUP */
{
#if defined(CONFIG_ALPHA_RUFFIAN)
#if 1
printk("pyxis_init: skipping window register rewrites... "
"trust DeskStation firmware!\n");
#endif
#else /* RUFFIAN */
/*
* Set up the PCI->physical memory translation windows.
* For now, windows 1,2 and 3 are disabled. In the future, we may
* want to use them to do scatter/gather DMA. Window 0
* goes at 1 GB and is 1 GB large.
*/
 
*(vuip)PYXIS_W0_BASE = 1U | (PYXIS_DMA_WIN_BASE & 0xfff00000U);
*(vuip)PYXIS_W0_MASK = (PYXIS_DMA_WIN_SIZE - 1) & 0xfff00000U;
*(vuip)PYXIS_T0_BASE = 0;
 
*(vuip)PYXIS_W1_BASE = 0x0 ;
*(vuip)PYXIS_W2_BASE = 0x0 ;
*(vuip)PYXIS_W3_BASE = 0x0 ;
mb();
#endif /* RUFFIAN */
}
 
/*
* check ASN in HWRPB for validity, report if bad
*/
if (hwrpb->max_asn != MAX_ASN) {
printk("PYXIS_init: max ASN from HWRPB is bad (0x%lx)\n",
hwrpb->max_asn);
hwrpb->max_asn = MAX_ASN;
}
 
/*
* Next, clear the PYXIS_CFG register, which gets used
* for PCI Config Space accesses. That is the way
* we want to use it, and we do not want to depend on
* what ARC or SRM might have left behind...
*/
{
unsigned int pyxis_cfg, temp;
pyxis_cfg = *((vuip)PYXIS_CFG); mb();
if (pyxis_cfg != 0) {
#if 1
printk("PYXIS_init: CFG was 0x%x\n", pyxis_cfg);
#endif
*((vuip)PYXIS_CFG) = 0; mb();
temp = *((vuip)PYXIS_CFG);
}
}
{
unsigned int pyxis_hae_mem = *((vuip)PYXIS_HAE_MEM);
unsigned int pyxis_hae_io = *((vuip)PYXIS_HAE_IO);
#if 0
printk("PYXIS_init: HAE_MEM was 0x%x\n", pyxis_hae_mem);
printk("PYXIS_init: HAE_IO was 0x%x\n", pyxis_hae_io);
#endif
#ifdef CONFIG_ALPHA_SRM_SETUP
/*
sigh... For the SRM setup, unless we know apriori what the HAE
contents will be, we need to setup the arbitrary region bases
so we can test against the range of addresses and tailor the
region chosen for the SPARSE memory access.
 
see include/asm-alpha/pyxis.h for the SPARSE mem read/write
*/
pyxis_sm_base_r1 = (pyxis_hae_mem ) & 0xe0000000UL;/* region 1 */
pyxis_sm_base_r2 = (pyxis_hae_mem << 16) & 0xf8000000UL;/* region 2 */
pyxis_sm_base_r3 = (pyxis_hae_mem << 24) & 0xfc000000UL;/* region 3 */
#else /* SRM_SETUP */
*((vuip)PYXIS_HAE_MEM) = 0U; mb();
pyxis_hae_mem = *((vuip)PYXIS_HAE_MEM);
*((vuip)PYXIS_HAE_IO) = 0; mb();
pyxis_hae_io = *((vuip)PYXIS_HAE_IO);
#endif /* SRM_SETUP */
}
 
/*
* Finally, check that the PYXIS_CTRL1 has IOA_BEN set for
* enabling byte/word PCI bus space(s) access.
*/
{
unsigned int ctrl1;
ctrl1 = *((vuip) PYXIS_CTRL1);
if (!(ctrl1 & 1)) {
#if 0
printk("PYXIS_init: enabling byte/word PCI space\n");
#endif
*((vuip) PYXIS_CTRL1) = ctrl1 | 1; mb();
ctrl1 = *((vuip)PYXIS_CTRL1);
}
}
 
return mem_start;
}
 
int pyxis_pci_clr_err(void)
{
PYXIS_jd = *((vuip)PYXIS_ERR);
DBG(("PYXIS_pci_clr_err: PYXIS ERR after read 0x%x\n", PYXIS_jd));
*((vuip)PYXIS_ERR) = 0x0180; mb();
PYXIS_jd = *((vuip)PYXIS_ERR);
return 0;
}
 
void pyxis_machine_check(unsigned long vector, unsigned long la_ptr,
struct pt_regs * regs)
{
#if 0
printk("PYXIS machine check ignored\n") ;
#else
struct el_common *mchk_header;
struct el_PYXIS_sysdata_mcheck *mchk_sysdata;
 
mchk_header = (struct el_common *)la_ptr;
 
mchk_sysdata =
(struct el_PYXIS_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset);
 
#if 0
DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n",
vector, la_ptr));
DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
regs->pc, mchk_header->size, mchk_header->proc_offset,
mchk_header->sys_offset));
DBG_MCK(("pyxis_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n",
PYXIS_mcheck_expected, mchk_sysdata->epic_dcsr,
mchk_sysdata->epic_pear));
#endif
#ifdef DEBUG_MCHECK_DUMP
{
unsigned long *ptr;
int i;
 
ptr = (unsigned long *)la_ptr;
for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]);
}
}
#endif /* DEBUG_MCHECK_DUMP */
/*
* Check if machine check is due to a badaddr() and if so,
* ignore the machine check.
*/
mb();
mb();
if (PYXIS_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
DBG(("PYXIS machine check expected\n"));
PYXIS_mcheck_expected = 0;
PYXIS_mcheck_taken = 1;
mb();
mb();
draina();
pyxis_pci_clr_err();
wrmces(0x7);
mb();
}
#if 1
else {
printk("PYXIS machine check NOT expected\n") ;
DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n",
vector, la_ptr));
DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
regs->pc, mchk_header->size, mchk_header->proc_offset,
mchk_header->sys_offset));
PYXIS_mcheck_expected = 0;
PYXIS_mcheck_taken = 1;
mb();
mb();
draina();
pyxis_pci_clr_err();
wrmces(0x7);
mb();
}
#endif
#endif
}
 
#if defined(CONFIG_ALPHA_RUFFIAN)
/* NOTE: this is only used by MILO, AFAIK... */
/*
* The DeskStation Ruffian motherboard firmware does not place
* the memory size in the PALimpure area. Therefore, it uses
* the Bank Configuration Registers in PYXIS to obtain the size.
*/
unsigned long pyxis_get_bank_size(unsigned long offset)
{
unsigned long bank_addr;
/* valid offsets are: 0x800, 0x840 and 0x880
* since Ruffian only uses three banks
*/
bank_addr = (unsigned long)PYXIS_MCR + offset;
/* check BANK_ENABLE */
if (*((unsigned long *)bank_addr) && 0x01)
{
/* do case on BANK_SIZE bits */
switch (*((unsigned long *)bank_addr) & 0x01e)
{
case 0x00: return 0x40000000UL; break; /* 1G */
case 0x02: return 0x20000000UL; break; /* 512M */
case 0x04: return 0x10000000UL; break; /* 256M */
case 0x06: return 0x08000000UL; break; /* 128M */
case 0x08: return 0x04000000UL; break; /* 64M */
case 0x0a: return 0x02000000UL; break; /* 32M */
case 0x0c: return 0x01000000UL; break; /* 16M */
case 0x0e: return 0x00800000UL; break; /* 8M */
case 0x10: return 0x80000000UL; break; /* 2G */
default : return 0x00000000UL; break; /* ERROR*/
}
} else
return 0x00UL;
}
#endif /* CONFIG_ALPHA_RUFFIAN */
 
#endif /* CONFIG_ALPHA_PYXIS */
/kernel/signal.c
0,0 → 1,374
/*
* linux/arch/alpha/kernel/signal.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/mm.h>
 
#include <asm/bitops.h>
#include <asm/segment.h>
 
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
 
asmlinkage int sys_wait4(int, int *, int, struct rusage *);
asmlinkage void ret_from_sys_call(void);
asmlinkage int do_signal(unsigned long, struct pt_regs *, struct switch_stack *,
unsigned long, unsigned long);
 
extern int ptrace_set_bpt (struct task_struct *child);
extern int ptrace_cancel_bpt (struct task_struct *child);
 
/*
* The OSF/1 sigprocmask calling sequence is different from the
* C sigprocmask() sequence..
*
* how:
* 1 - SIG_BLOCK
* 2 - SIG_UNBLOCK
* 3 - SIG_SETMASK
*
* We change the range to -1 .. 1 in order to let gcc easily
* use the conditional move instructions.
*/
asmlinkage unsigned long osf_sigprocmask(int how, unsigned long newmask,
long a2, long a3, long a4, long a5, struct pt_regs regs)
{
unsigned long ok, oldmask;
struct task_struct * tsk;
 
ok = how-1; /* 0 .. 2 */
tsk = current;
ok = ok <= 2;
oldmask = -EINVAL;
if (ok) {
long sign; /* -1 .. 1 */
unsigned long block, unblock;
 
oldmask = tsk->blocked;
newmask &= _BLOCKABLE;
sign = how-2;
unblock = oldmask & ~newmask;
block = oldmask | newmask;
if (!sign)
block = unblock;
regs.r0 = 0; /* special no error return */
if (sign <= 0)
newmask = block;
tsk->blocked = newmask;
}
return oldmask;
}
 
/*
* atomically swap in the new signal mask, and wait for a signal.
*/
asmlinkage int do_sigsuspend(unsigned long mask, struct pt_regs * regs, struct switch_stack * sw)
{
unsigned long oldmask = current->blocked;
current->blocked = mask & _BLOCKABLE;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
if (do_signal(oldmask,regs, sw, 0, 0))
return -EINTR;
}
}
 
/*
* Do a signal return; undo the signal stack.
*/
asmlinkage void do_sigreturn(struct sigcontext_struct * sc,
struct pt_regs * regs, struct switch_stack * sw)
{
unsigned long mask;
int i;
 
/* verify that it's a good sigcontext before using it */
if (verify_area(VERIFY_READ, sc, sizeof(*sc)))
do_exit(SIGSEGV);
if (get_fs_quad(&sc->sc_ps) != 8)
do_exit(SIGSEGV);
mask = get_fs_quad(&sc->sc_mask);
if (mask & ~_BLOCKABLE)
do_exit(SIGSEGV);
 
/* ok, looks fine, start restoring */
wrusp(get_fs_quad(sc->sc_regs+30));
regs->pc = get_fs_quad(&sc->sc_pc);
sw->r26 = (unsigned long) ret_from_sys_call;
current->blocked = mask;
 
regs->r0 = get_fs_quad(sc->sc_regs+0);
regs->r1 = get_fs_quad(sc->sc_regs+1);
regs->r2 = get_fs_quad(sc->sc_regs+2);
regs->r3 = get_fs_quad(sc->sc_regs+3);
regs->r4 = get_fs_quad(sc->sc_regs+4);
regs->r5 = get_fs_quad(sc->sc_regs+5);
regs->r6 = get_fs_quad(sc->sc_regs+6);
regs->r7 = get_fs_quad(sc->sc_regs+7);
regs->r8 = get_fs_quad(sc->sc_regs+8);
sw->r9 = get_fs_quad(sc->sc_regs+9);
sw->r10 = get_fs_quad(sc->sc_regs+10);
sw->r11 = get_fs_quad(sc->sc_regs+11);
sw->r12 = get_fs_quad(sc->sc_regs+12);
sw->r13 = get_fs_quad(sc->sc_regs+13);
sw->r14 = get_fs_quad(sc->sc_regs+14);
sw->r15 = get_fs_quad(sc->sc_regs+15);
regs->r16 = get_fs_quad(sc->sc_regs+16);
regs->r17 = get_fs_quad(sc->sc_regs+17);
regs->r18 = get_fs_quad(sc->sc_regs+18);
regs->r19 = get_fs_quad(sc->sc_regs+19);
regs->r20 = get_fs_quad(sc->sc_regs+20);
regs->r21 = get_fs_quad(sc->sc_regs+21);
regs->r22 = get_fs_quad(sc->sc_regs+22);
regs->r23 = get_fs_quad(sc->sc_regs+23);
regs->r24 = get_fs_quad(sc->sc_regs+24);
regs->r25 = get_fs_quad(sc->sc_regs+25);
regs->r26 = get_fs_quad(sc->sc_regs+26);
regs->r27 = get_fs_quad(sc->sc_regs+27);
regs->r28 = get_fs_quad(sc->sc_regs+28);
regs->gp = get_fs_quad(sc->sc_regs+29);
for (i = 0; i < 31; i++)
sw->fp[i] = get_fs_quad(sc->sc_fpregs+i);
 
/* send SIGTRAP if we're single-stepping: */
if (ptrace_cancel_bpt (current))
send_sig(SIGTRAP, current, 1);
}
 
/*
* Set up a signal frame...
*/
static void setup_frame(struct sigaction * sa,
struct pt_regs * regs,
struct switch_stack * sw, int signr,
unsigned long oldmask)
{
int i;
unsigned long oldsp;
struct sigcontext_struct * sc;
 
oldsp = rdusp();
sc = ((struct sigcontext_struct *) oldsp) - 1;
 
/* check here if we would need to switch stacks.. */
if (verify_area(VERIFY_WRITE, sc, sizeof(*sc)))
do_exit(SIGSEGV);
 
wrusp((unsigned long) sc);
 
put_fs_quad(oldmask, &sc->sc_mask);
put_fs_quad(8, &sc->sc_ps);
put_fs_quad(regs->pc, &sc->sc_pc);
put_fs_quad(oldsp, sc->sc_regs+30);
 
put_fs_quad(regs->r0 , sc->sc_regs+0);
put_fs_quad(regs->r1 , sc->sc_regs+1);
put_fs_quad(regs->r2 , sc->sc_regs+2);
put_fs_quad(regs->r3 , sc->sc_regs+3);
put_fs_quad(regs->r4 , sc->sc_regs+4);
put_fs_quad(regs->r5 , sc->sc_regs+5);
put_fs_quad(regs->r6 , sc->sc_regs+6);
put_fs_quad(regs->r7 , sc->sc_regs+7);
put_fs_quad(regs->r8 , sc->sc_regs+8);
put_fs_quad(sw->r9 , sc->sc_regs+9);
put_fs_quad(sw->r10 , sc->sc_regs+10);
put_fs_quad(sw->r11 , sc->sc_regs+11);
put_fs_quad(sw->r12 , sc->sc_regs+12);
put_fs_quad(sw->r13 , sc->sc_regs+13);
put_fs_quad(sw->r14 , sc->sc_regs+14);
put_fs_quad(sw->r15 , sc->sc_regs+15);
put_fs_quad(regs->r16, sc->sc_regs+16);
put_fs_quad(regs->r17, sc->sc_regs+17);
put_fs_quad(regs->r18, sc->sc_regs+18);
put_fs_quad(regs->r19, sc->sc_regs+19);
put_fs_quad(regs->r20, sc->sc_regs+20);
put_fs_quad(regs->r21, sc->sc_regs+21);
put_fs_quad(regs->r22, sc->sc_regs+22);
put_fs_quad(regs->r23, sc->sc_regs+23);
put_fs_quad(regs->r24, sc->sc_regs+24);
put_fs_quad(regs->r25, sc->sc_regs+25);
put_fs_quad(regs->r26, sc->sc_regs+26);
put_fs_quad(regs->r27, sc->sc_regs+27);
put_fs_quad(regs->r28, sc->sc_regs+28);
put_fs_quad(regs->gp , sc->sc_regs+29);
for (i = 0; i < 31; i++)
put_fs_quad(sw->fp[i], sc->sc_fpregs+i);
put_fs_quad(regs->trap_a0, &sc->sc_traparg_a0);
put_fs_quad(regs->trap_a1, &sc->sc_traparg_a1);
put_fs_quad(regs->trap_a2, &sc->sc_traparg_a2);
 
/*
* The following is:
*
* bis $30,$30,$16
* addq $31,0x67,$0
* call_pal callsys
*
* ie, "sigreturn(stack-pointer)"
*/
put_fs_quad(0x43ecf40047de0410, sc->sc_retcode+0);
put_fs_quad(0x0000000000000083, sc->sc_retcode+1);
imb();
 
/* "return" to the handler */
regs->r27 = regs->pc = (unsigned long) sa->sa_handler;
regs->r26 = (unsigned long) sc->sc_retcode;
regs->r16 = signr; /* a0: signal number */
regs->r17 = 0; /* a1: exception code; see gentrap.h */
regs->r18 = (unsigned long) sc; /* a2: sigcontext pointer */
}
 
/*
* OK, we're invoking a handler
*/
static inline void handle_signal(unsigned long signr, struct sigaction *sa,
unsigned long oldmask, struct pt_regs * regs, struct switch_stack *sw)
{
setup_frame(sa,regs,sw,signr,oldmask);
 
if (sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
if (!(sa->sa_flags & SA_NOMASK))
current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
}
 
static inline void syscall_restart(unsigned long r0, unsigned long r19,
struct pt_regs * regs, struct sigaction * sa)
{
switch (regs->r0) {
case ERESTARTNOHAND:
no_system_call_restart:
regs->r0 = EINTR;
break;
case ERESTARTSYS:
if (!(sa->sa_flags & SA_RESTART))
goto no_system_call_restart;
/* fallthrough */
case ERESTARTNOINTR:
regs->r0 = r0; /* reset v0 and a3 and replay syscall */
regs->r19 = r19;
regs->pc -= 4;
}
}
 
 
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* Note that we go through the signals twice: once to check the signals that
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*
* "r0" and "r19" are the registers we need to restore for system call
* restart. "r0" is also used as an indicator whether we can restart at
* all (if we get here from anything but a syscall return, it will be 0)
*/
asmlinkage int do_signal(unsigned long oldmask,
struct pt_regs * regs,
struct switch_stack * sw,
unsigned long r0, unsigned long r19)
{
unsigned long mask = ~current->blocked;
unsigned long signr, single_stepping;
struct sigaction * sa;
 
single_stepping = ptrace_cancel_bpt(current);
 
while ((signr = current->signal & mask) != 0) {
signr = ffz(~signr);
clear_bit(signr, &current->signal);
sa = current->sig->action + signr;
signr++;
if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
current->exit_code = signr;
current->state = TASK_STOPPED;
notify_parent(current, SIGCHLD);
schedule();
single_stepping |= ptrace_cancel_bpt(current);
if (!(signr = current->exit_code))
continue;
current->exit_code = 0;
if (signr == SIGSTOP)
continue;
if (_S(signr) & current->blocked) {
current->signal |= _S(signr);
continue;
}
sa = current->sig->action + signr - 1;
}
if (sa->sa_handler == SIG_IGN) {
if (signr != SIGCHLD)
continue;
/* check for SIGCHLD: it's special */
while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
/* nothing */;
continue;
}
if (sa->sa_handler == SIG_DFL) {
if (current->pid == 1)
continue;
switch (signr) {
case SIGCONT: case SIGCHLD: case SIGWINCH:
continue;
 
case SIGTSTP: case SIGTTIN: case SIGTTOU:
if (is_orphaned_pgrp(current->pgrp))
continue;
case SIGSTOP:
if (current->flags & PF_PTRACED)
continue;
current->state = TASK_STOPPED;
current->exit_code = signr;
if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
SA_NOCLDSTOP))
notify_parent(current, SIGCHLD);
schedule();
single_stepping |= ptrace_cancel_bpt(current);
continue;
 
case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGABRT: case SIGFPE: case SIGSEGV:
if (current->binfmt && current->binfmt->core_dump) {
if (current->binfmt->core_dump(signr, regs))
signr |= 0x80;
}
/* fall through */
default:
current->signal |= _S(signr & 0x7f);
current->flags |= PF_SIGNALED;
do_exit(signr);
}
}
if (r0)
syscall_restart(r0, r19, regs, sa);
handle_signal(signr, sa, oldmask, regs, sw);
if (single_stepping) {
ptrace_set_bpt(current); /* re-set breakpoint */
}
return 1;
}
if (r0 &&
(regs->r0 == ERESTARTNOHAND ||
regs->r0 == ERESTARTSYS ||
regs->r0 == ERESTARTNOINTR)) {
regs->r0 = r0; /* reset v0 and a3 and replay syscall */
regs->r19 = r19;
regs->pc -= 4;
}
if (single_stepping) {
ptrace_set_bpt(current); /* re-set breakpoint */
}
return 0;
}
/kernel/process.c
0,0 → 1,289
/*
* linux/arch/alpha/kernel/process.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
/*
* This file handles the architecture-dependent parts of process handling..
*/
 
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/ldt.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/utsname.h>
#include <linux/time.h>
#include <linux/major.h>
#include <linux/stat.h>
#include <linux/mman.h>
#include <linux/elfcore.h>
 
#include <asm/reg.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/io.h>
 
asmlinkage int sys_sethae(unsigned long hae, unsigned long a1,
unsigned long a2, unsigned long a3,
unsigned long a4, unsigned long a5,
struct pt_regs regs)
{
(&regs)->hae = hae;
return 0;
}
 
asmlinkage int sys_idle(void)
{
if (current->pid != 0)
return -EPERM;
 
/* endless idle loop with no priority at all */
current->counter = -100;
for (;;) {
schedule();
}
}
 
#include <asm/hwrpb.h>
 
static void swap_context(struct thread_struct * pcb)
{
__asm__ __volatile__(
"bis %0,%0,$16\n\t"
"call_pal %1\n\t"
: /* no outputs */
: "r" (pcb), "i" (PAL_swpctx)
: "$0", "$1", "$16", "$22", "$23", "$24", "$25");
}
 
void hard_reset_now(void)
{
#if defined(CONFIG_ALPHA_SRM_SETUP)
extern void reset_for_srm(void);
extern struct hwrpb_struct *hwrpb;
extern struct thread_struct *original_pcb_ptr;
struct percpu_struct *cpup;
unsigned long flags;
 
cpup = (struct percpu_struct *)
((unsigned long)hwrpb + hwrpb->processor_offset);
flags = cpup->flags;
#if 1
printk("hard_reset_now: flags 0x%lx\n", flags);
#endif
flags &= ~0x0000000000ff0001UL; /* clear reason to "default" */
flags |= 0x0000000000020000UL; /* this is "cold bootstrap" */
/* flags |= 0x0000000000030000UL; *//* this is "warm bootstrap" */
/* flags |= 0x0000000000040000UL; *//* this is "remain halted" */
cpup->flags = flags;
mb();
reset_for_srm();
swap_context(original_pcb_ptr);
#endif
#if defined(CONFIG_ALPHA_SRM) && defined(CONFIG_ALPHA_ALCOR)
/* who said DEC engineer's have no sense of humor? ;-)) */
*(int *) GRU_RESET = 0x0000dead;
mb();
#endif
halt();
}
 
void show_regs(struct pt_regs * regs)
{
printk("\nps: %04lx pc: [<%016lx>]\n", regs->ps, regs->pc);
printk("rp: [<%016lx>] sp: %p\n", regs->r26, regs+1);
printk(" r0: %016lx r1: %016lx r2: %016lx r3: %016lx\n",
regs->r0, regs->r1, regs->r2, regs->r3);
printk(" r4: %016lx r5: %016lx r6: %016lx r7: %016lx\n",
regs->r4, regs->r5, regs->r6, regs->r7);
printk(" r8: %016lx r16: %016lx r17: %016lx r18: %016lx\n",
regs->r8, regs->r16, regs->r17, regs->r18);
printk("r19: %016lx r20: %016lx r21: %016lx r22: %016lx\n",
regs->r19, regs->r20, regs->r21, regs->r22);
printk("r23: %016lx r24: %016lx r25: %016lx r26: %016lx\n",
regs->r23, regs->r24, regs->r25, regs->r26);
printk("r27: %016lx r28: %016lx r29: %016lx hae: %016lx\n",
regs->r27, regs->r28, regs->gp, regs->hae);
}
 
/*
* Re-start a thread when doing execve()
*/
void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
{
set_fs(USER_DS);
regs->pc = pc;
regs->ps = 8;
wrusp(sp);
}
 
/*
* Free current thread data structures etc..
*/
void exit_thread(void)
{
}
 
void flush_thread(void)
{
}
 
void release_thread(struct task_struct *dead_task)
{
}
 
/*
* "alpha_clone()".. By the time we get here, the
* non-volatile registers have also been saved on the
* stack. We do some ugly pointer stuff here.. (see
* also copy_thread)
*
* Notice that "fork()" is implemented in terms of clone,
* with parameters (SIGCHLD, 0).
*/
int alpha_clone(unsigned long clone_flags, unsigned long usp,
struct switch_stack * swstack)
{
if (!usp)
usp = rdusp();
return do_fork(clone_flags, usp, (struct pt_regs *) (swstack+1));
}
 
extern void ret_from_sys_call(void);
/*
* Copy an alpha thread..
*
* Note the "stack_offset" stuff: when returning to kernel mode, we need
* to have some extra stack-space for the kernel stack that still exists
* after the "ret_from_sys_call". When returning to user mode, we only
* want the space needed by the syscall stack frame (ie "struct pt_regs").
* Use the passed "regs" pointer to determine how much space we need
* for a kernel fork().
*/
void copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
struct switch_stack * childstack, *stack;
unsigned long stack_offset;
 
stack_offset = PAGE_SIZE - sizeof(struct pt_regs);
if (!(regs->ps & 8))
stack_offset = (PAGE_SIZE-1) & (unsigned long) regs;
childregs = (struct pt_regs *) (p->kernel_stack_page + stack_offset);
*childregs = *regs;
childregs->r0 = 0;
childregs->r19 = 0;
childregs->r20 = 1; /* OSF/1 has some strange fork() semantics.. */
regs->r20 = 0;
stack = ((struct switch_stack *) regs) - 1;
childstack = ((struct switch_stack *) childregs) - 1;
*childstack = *stack;
childstack->r26 = (unsigned long) ret_from_sys_call;
p->tss.usp = usp;
p->tss.ksp = (unsigned long) childstack;
p->tss.pal_flags = 1; /* set FEN, clear everything else */
p->tss.flags = current->tss.flags;
p->mm->context = 0;
}
 
/*
* fill in the user structure for a core dump..
*/
void dump_thread(struct pt_regs * pt, struct user * dump)
{
/* switch stack follows right below pt_regs: */
struct switch_stack * sw = ((struct switch_stack *) pt) - 1;
 
dump->magic = CMAGIC;
dump->start_code = current->mm->start_code;
dump->start_data = current->mm->start_data;
dump->start_stack = rdusp() & ~(PAGE_SIZE - 1);
dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT;
dump->u_dsize = (current->mm->brk + (PAGE_SIZE - 1) - dump->start_data) >> PAGE_SHIFT;
dump->u_ssize =
(current->mm->start_stack - dump->start_stack + PAGE_SIZE - 1) >> PAGE_SHIFT;
 
/*
* We store the registers in an order/format that is
* compatible with DEC Unix/OSF/1 as this makes life easier
* for gdb.
*/
dump->regs[EF_V0] = pt->r0;
dump->regs[EF_T0] = pt->r1;
dump->regs[EF_T1] = pt->r2;
dump->regs[EF_T2] = pt->r3;
dump->regs[EF_T3] = pt->r4;
dump->regs[EF_T4] = pt->r5;
dump->regs[EF_T5] = pt->r6;
dump->regs[EF_T6] = pt->r7;
dump->regs[EF_T7] = pt->r8;
dump->regs[EF_S0] = sw->r9;
dump->regs[EF_S1] = sw->r10;
dump->regs[EF_S2] = sw->r11;
dump->regs[EF_S3] = sw->r12;
dump->regs[EF_S4] = sw->r13;
dump->regs[EF_S5] = sw->r14;
dump->regs[EF_S6] = sw->r15;
dump->regs[EF_A3] = pt->r19;
dump->regs[EF_A4] = pt->r20;
dump->regs[EF_A5] = pt->r21;
dump->regs[EF_T8] = pt->r22;
dump->regs[EF_T9] = pt->r23;
dump->regs[EF_T10] = pt->r24;
dump->regs[EF_T11] = pt->r25;
dump->regs[EF_RA] = pt->r26;
dump->regs[EF_T12] = pt->r27;
dump->regs[EF_AT] = pt->r28;
dump->regs[EF_SP] = rdusp();
dump->regs[EF_PS] = pt->ps;
dump->regs[EF_PC] = pt->pc;
dump->regs[EF_GP] = pt->gp;
dump->regs[EF_A0] = pt->r16;
dump->regs[EF_A1] = pt->r17;
dump->regs[EF_A2] = pt->r18;
memcpy((char *)dump->regs + EF_SIZE, sw->fp, 32 * 8);
}
 
int dump_fpu (struct pt_regs * regs, elf_fpregset_t *r)
{
/* switch stack follows right below pt_regs: */
struct switch_stack * sw = ((struct switch_stack *) regs) - 1;
memcpy(r, sw->fp, 32 * 8);
return 1;
}
 
/*
* sys_execve() executes a new program.
*
* This works due to the alpha calling sequence: the first 6 args
* are gotten from registers, while the rest is on the stack, so
* we get a0-a5 for free, and then magically find "struct pt_regs"
* on the stack for us..
*
* Don't do this at home.
*/
asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4, unsigned long a5,
struct pt_regs regs)
{
int error;
char * filename;
 
error = getname((char *) a0, &filename);
if (error)
return error;
error = do_execve(filename, (char **) a1, (char **) a2, &regs);
putname(filename);
return error;
}
/kernel/ptrace.c
0,0 → 1,654
/* ptrace.c */
/* By Ross Biro 1/23/92 */
/* edited by Linus Torvalds */
/* mangled further by Bob Manson (manson@santafe.edu) */
/* more mutilation by David Mosberger (davidm@azstarnet.com) */
 
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/debugreg.h>
 
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/system.h>
 
#undef DEBUG
 
#ifdef DEBUG
 
enum {
DBG_MEM = (1<<0),
DBG_BPT = (1<<1),
DBG_MEM_ALL = (1<<2)
};
 
int debug_mask = DBG_BPT;
 
# define DBG(fac,args) {if ((fac) & debug_mask) printk args;}
 
#else
# define DBG(fac,args)
#endif
 
#define BREAKINST 0x00000080 /* call_pal bpt */
 
/*
* does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
*/
 
/*
* Processes always block with the following stack-layout:
*
* +================================+ --------------------------
* | PALcode saved frame (ps, pc, | ^ ^
* | gp, a0, a1, a2) | | |
* +================================+ | struct pt_regs |
* | | | |
* | frame generated by SAVE_ALL | | |
* | | v | P
* +================================+ | A
* | | ^ | G
* | frame saved by do_switch_stack | | struct switch_stack | E
* | | v | _
* +================================+ | S
* | | | I
* | | | Z
* / / | E
* / / |
* | | |
* | | |
* | | v
* +================================+ <-------------------------
* task->kernel_stack_page
*/
#define PT_REG(reg) (PAGE_SIZE - sizeof(struct pt_regs) \
+ (long)&((struct pt_regs *)0)->reg)
#define SW_REG(reg) (PAGE_SIZE - sizeof(struct pt_regs) \
- sizeof(struct switch_stack) \
+ (long)&((struct switch_stack *)0)->reg)
/*
* The following table maps a register index into the stack offset at
* which the register is saved. Register indices are 0-31 for integer
* regs, 32-63 for fp regs, and 64 for the pc. Notice that sp and
* zero have no stack-slot and need to be treated specially (see
* get_reg/put_reg below).
*/
enum {
REG_R0 = 0, REG_F0 = 32, REG_PC = 64
};
 
static unsigned short regoff[] = {
PT_REG( r0), PT_REG( r1), PT_REG( r2), PT_REG( r3),
PT_REG( r4), PT_REG( r5), PT_REG( r6), PT_REG( r7),
PT_REG( r8), SW_REG( r9), SW_REG( r10), SW_REG( r11),
SW_REG( r12), SW_REG( r13), SW_REG( r14), SW_REG( r15),
PT_REG( r16), PT_REG( r17), PT_REG( r18), PT_REG( r19),
PT_REG( r20), PT_REG( r21), PT_REG( r22), PT_REG( r23),
PT_REG( r24), PT_REG( r25), PT_REG( r26), PT_REG( r27),
PT_REG( r28), PT_REG( gp), -1, -1,
SW_REG(fp[ 0]), SW_REG(fp[ 1]), SW_REG(fp[ 2]), SW_REG(fp[ 3]),
SW_REG(fp[ 4]), SW_REG(fp[ 5]), SW_REG(fp[ 6]), SW_REG(fp[ 7]),
SW_REG(fp[ 8]), SW_REG(fp[ 9]), SW_REG(fp[10]), SW_REG(fp[11]),
SW_REG(fp[12]), SW_REG(fp[13]), SW_REG(fp[14]), SW_REG(fp[15]),
SW_REG(fp[16]), SW_REG(fp[17]), SW_REG(fp[18]), SW_REG(fp[19]),
SW_REG(fp[20]), SW_REG(fp[21]), SW_REG(fp[22]), SW_REG(fp[23]),
SW_REG(fp[24]), SW_REG(fp[25]), SW_REG(fp[26]), SW_REG(fp[27]),
SW_REG(fp[28]), SW_REG(fp[29]), SW_REG(fp[30]), SW_REG(fp[31]),
PT_REG( pc)
};
 
static long zero;
 
 
/* change a pid into a task struct. */
static inline struct task_struct * get_task(int pid)
{
int i;
 
for (i = 1; i < NR_TASKS; i++) {
if (task[i] != NULL && (task[i]->pid == pid))
return task[i];
}
return NULL;
}
 
/*
* Get contents of register REGNO in task TASK.
*/
static inline long get_reg(struct task_struct * task, long regno)
{
long *addr;
 
if (regno == 30) {
addr = &task->tss.usp;
} else if (regno == 31) {
zero = 0;
addr = &zero;
} else {
addr = (long *) (task->kernel_stack_page + regoff[regno]);
}
return *addr;
}
 
/*
* Write contents of register REGNO in task TASK.
*/
static inline int put_reg(struct task_struct *task, long regno, long data)
{
long *addr, zero;
 
if (regno == 30) {
addr = &task->tss.usp;
} else if (regno == 31) {
addr = &zero;
} else {
addr = (long *) (task->kernel_stack_page + regoff[regno]);
}
*addr = data;
return 0;
}
 
/*
* This routine gets a long from any process space by following the page
* tables. NOTE! You should check that the long isn't on a page boundary,
* and that it is in the task area before calling this: this routine does
* no checking.
*/
static unsigned long get_long(struct task_struct * tsk,
struct vm_area_struct * vma, unsigned long addr)
{
pgd_t * pgdir;
pmd_t * pgmiddle;
pte_t * pgtable;
unsigned long page;
 
DBG(DBG_MEM_ALL, ("getting long at 0x%lx\n", addr));
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (pgd_none(*pgdir)) {
do_no_page(tsk, vma, addr, 0);
goto repeat;
}
if (pgd_bad(*pgdir)) {
printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
pgd_clear(pgdir);
return 0;
}
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle)) {
do_no_page(tsk, vma, addr, 0);
goto repeat;
}
if (pmd_bad(*pgmiddle)) {
printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
pmd_clear(pgmiddle);
return 0;
}
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
do_no_page(tsk, vma, addr, 0);
goto repeat;
}
page = pte_page(*pgtable);
/* this is a hack for non-kernel-mapped video buffers and similar */
if (page >= high_memory)
return 0;
page += addr & ~PAGE_MASK;
return *(unsigned long *) page;
}
 
/*
* This routine puts a long into any process space by following the page
* tables. NOTE! You should check that the long isn't on a page boundary,
* and that it is in the task area before calling this: this routine does
* no checking.
*
* Now keeps R/W state of page so that a text page stays readonly
* even if a debugger scribbles breakpoints into it. -M.U-
*/
static void put_long(struct task_struct * tsk, struct vm_area_struct * vma,
unsigned long addr, unsigned long data)
{
pgd_t *pgdir;
pmd_t *pgmiddle;
pte_t *pgtable;
unsigned long page;
 
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (!pgd_present(*pgdir)) {
do_no_page(tsk, vma, addr, 1);
goto repeat;
}
if (pgd_bad(*pgdir)) {
printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
pgd_clear(pgdir);
return;
}
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle)) {
do_no_page(tsk, vma, addr, 1);
goto repeat;
}
if (pmd_bad(*pgmiddle)) {
printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
pmd_clear(pgmiddle);
return;
}
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
do_no_page(tsk, vma, addr, 1);
goto repeat;
}
page = pte_page(*pgtable);
if (!pte_write(*pgtable)) {
do_wp_page(tsk, vma, addr, 1);
goto repeat;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
if (page < high_memory)
*(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
/* this should also re-instate whatever read-only mode there was before */
set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
flush_tlb();
}
 
static struct vm_area_struct * find_extend_vma(struct task_struct * tsk,
unsigned long addr)
{
struct vm_area_struct * vma;
 
addr &= PAGE_MASK;
vma = find_vma(tsk->mm,addr);
if (!vma)
return NULL;
if (vma->vm_start <= addr)
return vma;
if (!(vma->vm_flags & VM_GROWSDOWN))
return NULL;
if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
return NULL;
vma->vm_offset -= vma->vm_start - addr;
vma->vm_start = addr;
return vma;
}
 
/*
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls get_long() to read a long.
*/
static int read_long(struct task_struct * tsk, unsigned long addr,
unsigned long * result)
{
struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
DBG(DBG_MEM_ALL, ("in read_long\n"));
if (!vma) {
printk("Unable to find vma for addr 0x%lx\n",addr);
return -EIO;
}
if ((addr & ~PAGE_MASK) > (PAGE_SIZE - sizeof(long))) {
struct vm_area_struct * vma_high = vma;
unsigned long low, align;
 
if (addr + sizeof(long) >= vma->vm_end) {
vma_high = vma->vm_next;
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
align = addr & (sizeof(long) - 1);
addr -= align;
low = get_long(tsk, vma, addr);
if (align) {
unsigned long high;
 
high = get_long(tsk, vma_high, addr + sizeof(long));
low >>= align * 8;
low |= high << (64 - align * 8);
}
*result = low;
} else {
long l = get_long(tsk, vma, addr);
 
DBG(DBG_MEM_ALL, ("value is 0x%lx\n", l));
*result = l;
}
return 0;
}
 
/*
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls put_long() to write a long.
*/
static int write_long(struct task_struct * tsk, unsigned long addr,
unsigned long data)
{
struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
if (!vma)
return -EIO;
if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
unsigned long low, high, align;
struct vm_area_struct * vma_high = vma;
 
if (addr + sizeof(long) >= vma->vm_end) {
vma_high = vma->vm_next;
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
align = addr & (sizeof(long) - 1);
addr -= align;
low = get_long(tsk, vma, addr);
high = get_long(tsk, vma_high, addr + sizeof(long));
low &= ~0UL >> (64 - align * 8);
high &= ~0UL << (align * 8);
low |= data << (align * 8);
high |= data >> (64 - align * 8);
put_long(tsk, vma, addr, low);
put_long(tsk, vma_high, addr + sizeof(long), high);
} else
put_long(tsk, vma, addr, data);
return 0;
}
 
/*
* Read a 32bit int from address space TSK.
*/
static int read_int(struct task_struct * tsk, unsigned long addr,
unsigned int *data)
{
unsigned long l, align;
int res;
 
align = addr & 0x7;
addr &= ~0x7;
 
res = read_long(tsk, addr, &l);
if (res < 0)
return res;
 
if (align == 0) {
*data = l;
} else {
*data = l >> 32;
}
return 0;
}
 
/*
* Write a 32bit word to address space TSK.
*
* For simplicity, do a read-modify-write of the 64bit word that
* contains the 32bit word that we are about to write.
*/
static int write_int(struct task_struct * tsk, unsigned long addr,
unsigned int data)
{
unsigned long l, align;
int res;
 
align = addr & 0x7;
addr &= ~0x7;
 
res = read_long(tsk, addr, &l);
if (res < 0)
return res;
 
if (align == 0) {
l = (l & 0xffffffff00000000UL) | ((unsigned long) data << 0);
} else {
l = (l & 0x00000000ffffffffUL) | ((unsigned long) data << 32);
}
return write_long(tsk, addr, l);
}
 
/*
* Set breakpoint.
*/
int ptrace_set_bpt(struct task_struct * child)
{
int displ, i, res, reg_b, nsaved = 0;
u32 insn, op_code;
unsigned long pc;
 
pc = get_reg(child, REG_PC);
res = read_int(child, pc, &insn);
if (res < 0)
return res;
 
op_code = insn >> 26;
if (op_code >= 0x30) {
/*
* It's a branch: instead of trying to figure out
* whether the branch will be taken or not, we'll put
* a breakpoint at either location. This is simpler,
* more reliable, and probably not a whole lot slower
* than the alternative approach of emulating the
* branch (emulation can be tricky for fp branches).
*/
displ = ((s32)(insn << 11)) >> 9;
child->debugreg[nsaved++] = pc + 4;
if (displ) /* guard against unoptimized code */
child->debugreg[nsaved++] = pc + 4 + displ;
DBG(DBG_BPT, ("execing branch\n"));
} else if (op_code == 0x1a) {
reg_b = (insn >> 16) & 0x1f;
child->debugreg[nsaved++] = get_reg(child, reg_b);
DBG(DBG_BPT, ("execing jump\n"));
} else {
child->debugreg[nsaved++] = pc + 4;
DBG(DBG_BPT, ("execing normal insn\n"));
}
 
/* install breakpoints: */
for (i = 0; i < nsaved; ++i) {
res = read_int(child, child->debugreg[i], &insn);
if (res < 0)
return res;
child->debugreg[i + 2] = insn;
DBG(DBG_BPT, (" -> next_pc=%lx\n", child->debugreg[i]));
res = write_int(child, child->debugreg[i], BREAKINST);
if (res < 0)
return res;
}
child->debugreg[4] = nsaved;
return 0;
}
 
/*
* Ensure no single-step breakpoint is pending. Returns non-zero
* value if child was being single-stepped.
*/
int ptrace_cancel_bpt(struct task_struct * child)
{
int i, nsaved = child->debugreg[4];
 
child->debugreg[4] = 0;
 
if (nsaved > 2) {
printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
nsaved = 2;
}
 
for (i = 0; i < nsaved; ++i) {
write_int(child, child->debugreg[i], child->debugreg[i + 2]);
}
return (nsaved != 0);
}
 
asmlinkage long sys_ptrace(long request, long pid, long addr, long data,
int a4, int a5, struct pt_regs regs)
{
struct task_struct *child;
struct user * dummy;
 
dummy = NULL;
 
DBG(DBG_MEM, ("request=%ld pid=%ld addr=0x%lx data=0x%lx\n",
request, pid, addr, data));
if (request == PTRACE_TRACEME) {
/* are we already being traced? */
if (current->flags & PF_PTRACED)
return -EPERM;
/* set the ptrace bit in the process flags. */
current->flags |= PF_PTRACED;
return 0;
}
if (pid == 1) /* you may not mess with init */
return -EPERM;
if (!(child = get_task(pid)))
return -ESRCH;
if (request == PTRACE_ATTACH) {
if (child == current)
return -EPERM;
if ((!child->dumpable ||
(current->uid != child->euid) ||
(current->uid != child->suid) ||
(current->uid != child->uid) ||
(current->gid != child->egid) ||
(current->gid != child->sgid) ||
(current->gid != child->gid)) && !suser())
return -EPERM;
/* the same process cannot be attached many times */
if (child->flags & PF_PTRACED)
return -EPERM;
child->flags |= PF_PTRACED;
if (child->p_pptr != current) {
REMOVE_LINKS(child);
child->p_pptr = current;
SET_LINKS(child);
}
send_sig(SIGSTOP, child, 1);
return 0;
}
if (!(child->flags & PF_PTRACED)) {
DBG(DBG_MEM, ("child not traced\n"));
return -ESRCH;
}
if (child->state != TASK_STOPPED) {
DBG(DBG_MEM, ("child process not stopped\n"));
if (request != PTRACE_KILL)
return -ESRCH;
}
if (child->p_pptr != current) {
DBG(DBG_MEM, ("child not parent of this process\n"));
return -ESRCH;
}
 
switch (request) {
/* when I and D space are separate, these will need to be fixed. */
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA: {
unsigned long tmp;
int res;
 
res = read_long(child, addr, &tmp);
DBG(DBG_MEM, ("peek %#lx->%#lx\n", addr, tmp));
if (res < 0)
return res;
regs.r0 = 0; /* special return: no errors */
return tmp;
}
 
/* read register number ADDR. */
case PTRACE_PEEKUSR:
regs.r0 = 0; /* special return: no errors */
DBG(DBG_MEM, ("peek $%ld=%#lx\n", addr, regs.r0));
return get_reg(child, addr);
 
/* when I and D space are separate, this will have to be fixed. */
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
DBG(DBG_MEM, ("poke %#lx<-%#lx\n", addr, data));
return write_long(child, addr, data);
 
case PTRACE_POKEUSR: /* write the specified register */
DBG(DBG_MEM, ("poke $%ld<-%#lx\n", addr, data));
return put_reg(child, addr, data);
 
case PTRACE_SYSCALL: /* continue and stop at next
(return from) syscall */
case PTRACE_CONT: { /* restart after signal. */
if ((unsigned long) data > NSIG)
return -EIO;
if (request == PTRACE_SYSCALL)
child->flags |= PF_TRACESYS;
else
child->flags &= ~PF_TRACESYS;
child->exit_code = data;
wake_up_process(child);
/* make sure single-step breakpoint is gone. */
ptrace_cancel_bpt(child);
return data;
}
 
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
case PTRACE_KILL: {
if (child->state != TASK_ZOMBIE) {
wake_up_process(child);
child->exit_code = SIGKILL;
}
/* make sure single-step breakpoint is gone. */
ptrace_cancel_bpt(child);
return 0;
}
 
case PTRACE_SINGLESTEP: { /* execute single instruction. */
if ((unsigned long) data > NSIG)
return -EIO;
child->debugreg[4] = -1; /* mark single-stepping */
child->flags &= ~PF_TRACESYS;
wake_up_process(child);
child->exit_code = data;
/* give it a chance to run. */
return 0;
}
 
case PTRACE_DETACH: { /* detach a process that was attached. */
if ((unsigned long) data > NSIG)
return -EIO;
child->flags &= ~(PF_PTRACED|PF_TRACESYS);
wake_up_process(child);
child->exit_code = data;
REMOVE_LINKS(child);
child->p_pptr = child->p_opptr;
SET_LINKS(child);
/* make sure single-step breakpoint is gone. */
ptrace_cancel_bpt(child);
return 0;
}
 
default:
return -EIO;
}
}
 
asmlinkage void syscall_trace(void)
{
if ((current->flags & (PF_PTRACED|PF_TRACESYS))
!= (PF_PTRACED|PF_TRACESYS))
return;
current->exit_code = SIGTRAP;
current->state = TASK_STOPPED;
notify_parent(current, SIGCHLD);
schedule();
/*
* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
if (current->exit_code)
current->signal |= (1 << (current->exit_code - 1));
current->exit_code = 0;
}
/kernel/entry.S
0,0 → 1,921
/*
* alpha/entry.S
*
* kernel entry-points
*/
 
#include <linux/config.h>
#include <asm/system.h>
 
#define halt .long PAL_halt
#define rti .long PAL_rti
#define SIGCHLD 20
 
#define NR_SYSCALLS 350
#define osf_vfork sys_fork
 
/*
* These offsets must match with "struct hae" in io.h:
*/
#define HAE_CACHE 0
#define HAE_REG 8
 
/*
* stack offsets
*/
#define SP_OFF 184
 
#define SWITCH_STACK_SIZE 320
 
/*
* task structure offsets
*/
#define TASK_STATE 0
#define TASK_COUNTER 8
#define TASK_PRIORITY 16
#define TASK_SIGNAL 24
#define TASK_BLOCKED 32
#define TASK_FLAGS 40
 
/*
* task flags (must match include/linux/sched.h):
*/
#define PF_PTRACED 0x00000010
 
/*
* This defines the normal kernel pt-regs layout.
*
* regs 9-15 preserved by C code
* regs 16-18 saved by PAL-code
* regs 29-30 saved and set up by PAL-code
* JRP - Save regs 16-18 in a special area of the stack, so that
* the palcode-provided values are available to the signal handler.
*/
#define SAVE_ALL \
subq $30,184,$30; \
stq $0,0($30); \
stq $1,8($30); \
stq $2,16($30); \
stq $3,24($30); \
stq $4,32($30); \
stq $5,40($30); \
stq $6,48($30); \
stq $7,56($30); \
stq $8,64($30); \
stq $19,72($30); \
stq $20,80($30); \
stq $21,88($30); \
stq $22,96($30); \
stq $23,104($30); \
stq $24,112($30); \
stq $25,120($30); \
stq $26,128($30); \
stq $27,136($30); \
stq $28,144($30); \
lda $2,hae; \
ldq $2,HAE_CACHE($2); \
stq $2,152($30); \
stq $16,160($30); \
stq $17,168($30); \
stq $18,176($30)
 
#define RESTORE_ALL \
lda $8,hae; \
ldq $7,HAE_CACHE($8); \
ldq $6,152($30); \
subq $7,$6,$5; \
beq $5,99f; \
ldq $7,HAE_REG($8); \
addq $31,7,$16; \
call_pal PAL_swpipl; \
stq $6,HAE_CACHE($8); \
stq $6,0($7); \
mb; \
bis $0,$0,$16; \
call_pal PAL_swpipl; \
99:; \
ldq $0,0($30); \
ldq $1,8($30); \
ldq $2,16($30); \
ldq $3,24($30); \
ldq $4,32($30); \
ldq $5,40($30); \
ldq $6,48($30); \
ldq $7,56($30); \
ldq $8,64($30); \
ldq $19,72($30); \
ldq $20,80($30); \
ldq $21,88($30); \
ldq $22,96($30); \
ldq $23,104($30); \
ldq $24,112($30); \
ldq $25,120($30); \
ldq $26,128($30); \
ldq $27,136($30); \
ldq $28,144($30); \
addq $30,184,$30
 
.text
.set noat
#if defined(__linux__) && !defined(__ELF__)
.set singlegp
#endif
 
.align 3
.globl entInt
.ent entInt
entInt:
SAVE_ALL
/* start atomic operation with respect to software interrupts */
lda $0,intr_count
ldq $1,0($0)
addq $1,1,$1
stq $1,0($0)
/* set up the arguments to the C interrupt handler */
lda $27,do_entInt
jsr $26,($27),do_entInt
/* ok, return */
lda $0,intr_count
ldq $1,0($0)
subq $1,1,$1
stq $1,0($0)
br $31,ret_from_sys_call
.end entInt
 
.align 3
.globl entMM
.ent entMM
entMM:
SAVE_ALL
lda $27,do_page_fault
lda $26,ret_from_sys_call
jsr $31,($27),do_page_fault
.end entMM
 
.align 3
.globl entArith
.ent entArith
entArith:
SAVE_ALL
lda $27,do_entArith
lda $26,ret_from_sys_call
jsr $31,($27),do_entArith
.end entArith
 
.align 3
.globl entIF
.ent entIF
entIF:
#ifdef CONFIG_KGDB
bne $16,1f /* not a bpt trap -> */
/*
* Call kgdb if it's enabled and if "current" is not being
* traced or if we get a bpt in kernel mode (the architecture
* manual defines the values of $17 and $18 as "unpredictable",
* so they are fair game).
*/
lda $17,kgdb_enabled
ldl $17,0($17)
beq $17,1f /* kgdb not enabled -> */
 
#ifndef CONFIG_ALPHA_CABRIOLET
/*
* MILO on Cabriolet doesn't seem to setup the PS in the
* PALframe correctly. (davidm@azstarnet.com)
*/
ldq $18,0($30) /* get ps */
and $18,8,$18
beq $18,entKGDB
#endif
 
lda $17,current_set
ldq $17,0($17)
bis $31,PF_PTRACED,$18
ldq $17,TASK_FLAGS($17)
and $17,$18,$17
beq $17,entKGDB
1:
#endif
SAVE_ALL
lda $27,do_entIF
lda $26,ret_from_sys_call
jsr $31,($27),do_entIF
.end entIF
 
/*
* Fork() is one of the special system calls: it needs to
* save the callee-saved regs so that the regs can be found
* for the new process.. We save them in the "context switch"
* stack format (see arch/alpha/kernel/process.c).
*
* Also, for the kernel fork, we need to fake the system call
* stack buildup, as we can't do system calls from kernel space.
*/
.align 3
.ent kernel_clone
kernel_clone:
subq $30,6*8,$30
stq $31,0($30)
stq $26,8($30)
stq $29,16($30)
stq $16,24($30)
stq $17,32($30)
stq $18,40($30)
bis $31,2,$0 /* Register v0: syscall nr for fork() */
SAVE_ALL
lda $27,sys_clone
jsr $26,($27),sys_clone
stq $0,0($30)
br $31,ret_from_sys_call
.end kernel_clone
 
/*
* __kernel_thread(clone_flags, fn, arg)
*/
.align 3
.globl __kernel_thread
.ent __kernel_thread
__kernel_thread:
subq $30,4*8,$30
stq $9,0($30)
stq $10,8($30)
stq $26,16($30)
bis $17,$17,$9 /* save fn */
bis $18,$18,$10 /* save arg */
bsr $26,kernel_clone
bne $20,1f /* $20 is non-zero in child */
ldq $9,0($30)
ldq $10,8($30)
ldq $26,16($30)
addq $30,4*8,$30
ret $31,($26),1
/* this is in child: look out as we don't have any stack here.. */
1: bis $9,$9,$27 /* get fn */
bis $10,$10,$16 /* get arg */
jsr $26,($27)
bis $0,$0,$16
lda $27,sys_exit
jsr $26,($27),sys_exit
call_pal PAL_halt
.end __kernel_thread
 
.align 3
.ent do_switch_stack
do_switch_stack:
lda $30,-SWITCH_STACK_SIZE($30)
stq $9,0($30)
stq $10,8($30)
stq $11,16($30)
stq $12,24($30)
stq $13,32($30)
stq $14,40($30)
stq $15,48($30)
stq $26,56($30)
stt $f0,64($30)
stt $f1,72($30)
stt $f2,80($30)
stt $f3,88($30)
stt $f4,96($30)
stt $f5,104($30)
stt $f6,112($30)
stt $f7,120($30)
stt $f8,128($30)
stt $f9,136($30)
stt $f10,144($30)
stt $f11,152($30)
stt $f12,160($30)
stt $f13,168($30)
stt $f14,176($30)
stt $f15,184($30)
stt $f16,192($30)
stt $f17,200($30)
stt $f18,208($30)
stt $f19,216($30)
stt $f20,224($30)
stt $f21,232($30)
stt $f22,240($30)
stt $f23,248($30)
stt $f24,256($30)
stt $f25,264($30)
stt $f26,272($30)
stt $f27,280($30)
mf_fpcr $f0 # get fpcr
stt $f28,288($30)
stt $f29,296($30)
stt $f30,304($30)
stt $f0,312($30) # save fpcr in slot of $f31
ret $31,($1),1
.end do_switch_stack
 
.align 3
.ent undo_switch_stack
undo_switch_stack:
ldq $9,0($30)
ldq $10,8($30)
ldq $11,16($30)
ldq $12,24($30)
ldq $13,32($30)
ldq $14,40($30)
ldq $15,48($30)
ldq $26,56($30)
ldt $f30,312($30) # get saved fpcr
ldt $f0,64($30)
ldt $f1,72($30)
ldt $f2,80($30)
ldt $f3,88($30)
mt_fpcr $f30 # install saved fpcr
ldt $f4,96($30)
ldt $f5,104($30)
ldt $f6,112($30)
ldt $f7,120($30)
ldt $f8,128($30)
ldt $f9,136($30)
ldt $f10,144($30)
ldt $f11,152($30)
ldt $f12,160($30)
ldt $f13,168($30)
ldt $f14,176($30)
ldt $f15,184($30)
ldt $f16,192($30)
ldt $f17,200($30)
ldt $f18,208($30)
ldt $f19,216($30)
ldt $f20,224($30)
ldt $f21,232($30)
ldt $f22,240($30)
ldt $f23,248($30)
ldt $f24,256($30)
ldt $f25,264($30)
ldt $f26,272($30)
ldt $f27,280($30)
ldt $f28,288($30)
ldt $f29,296($30)
ldt $f30,304($30)
lda $30,SWITCH_STACK_SIZE($30)
ret $31,($1),1
.end undo_switch_stack
 
.align 3
.globl entUna
.ent entUna
entUna:
lda $30,-256($30)
stq $0,0($30)
ldq $0,256($30) /* get PS */
stq $1,8($30)
stq $2,16($30)
stq $3,24($30)
and $0,8,$0 /* user mode? */
stq $4,32($30)
bne $0,entUnaUser /* yup -> do user-level unaligned fault */
stq $5,40($30)
stq $6,48($30)
stq $7,56($30)
stq $8,64($30)
stq $9,72($30)
stq $10,80($30)
stq $11,88($30)
stq $12,96($30)
stq $13,104($30)
stq $14,112($30)
stq $15,120($30)
/* 16-18 PAL-saved */
stq $19,152($30)
stq $20,160($30)
stq $21,168($30)
stq $22,176($30)
stq $23,184($30)
stq $24,192($30)
stq $25,200($30)
stq $26,208($30)
stq $27,216($30)
stq $28,224($30)
stq $29,232($30)
stq $30,240($30)
stq $31,248($30)
lda $27,do_entUna
jsr $26,($27),do_entUna
ldq $0,0($30)
ldq $1,8($30)
ldq $2,16($30)
ldq $3,24($30)
ldq $4,32($30)
ldq $5,40($30)
ldq $6,48($30)
ldq $7,56($30)
ldq $8,64($30)
ldq $9,72($30)
ldq $10,80($30)
ldq $11,88($30)
ldq $12,96($30)
ldq $13,104($30)
ldq $14,112($30)
ldq $15,120($30)
/* 16-18 PAL-saved */
ldq $19,152($30)
ldq $20,160($30)
ldq $21,168($30)
ldq $22,176($30)
ldq $23,184($30)
ldq $24,192($30)
ldq $25,200($30)
ldq $26,208($30)
ldq $27,216($30)
ldq $28,224($30)
ldq $29,232($30)
ldq $30,240($30)
lda $30,256($30)
rti
.end entUna
 
.align 3
.ent entUnaUser
entUnaUser:
ldq $0,0($30) /* restore original $0 */
lda $30,256($30) /* pop entUna's stack frame */
SAVE_ALL /* setup normal kernel stack */
lda $30,-56($30)
stq $9,0($30)
stq $10,8($30)
stq $11,16($30)
stq $12,24($30)
stq $13,32($30)
stq $14,40($30)
stq $15,48($30)
lda $27,do_entUnaUser
bis $31,$30,$19
jsr $26,($27),do_entUnaUser
ldq $9,0($30)
ldq $10,8($30)
ldq $11,16($30)
ldq $12,24($30)
ldq $13,32($30)
ldq $14,40($30)
ldq $15,48($30)
lda $30,56($30)
br $31,ret_from_sys_call
 
.end entUnaUser
 
/*
* A fork is the same as clone(SIGCHLD, 0);
*/
.align 3
.globl sys_fork
.ent sys_fork
sys_fork:
bsr $1,do_switch_stack
bis $31,SIGCHLD,$16
bis $31,$31,$17
bis $30,$30,$18
lda $27,alpha_clone
jsr $26,($27),alpha_clone
bsr $1,undo_switch_stack
ret $31,($26),1
.end sys_fork
 
.align 3
.globl sys_clone
.ent sys_clone
sys_clone:
bsr $1,do_switch_stack
/* arg1 and arg2 come from the user */
bis $30,$30,$18
lda $27,alpha_clone
jsr $26,($27),alpha_clone
bsr $1,undo_switch_stack
ret $31,($26),1
.end sys_clone
 
.align 3
.globl alpha_switch_to
.ent alpha_switch_to
alpha_switch_to:
bsr $1,do_switch_stack
call_pal PAL_swpctx
bsr $1,undo_switch_stack
ret $31,($26),1
.end alpha_switch_to
 
/*
* Oh, well.. Disassembling OSF/1 binaries to find out how the
* system calls work isn't much fun.
*
* entSys is special in that the PAL-code doesn't save a0-a2, so
* we start off by doing that by hand.
*/
.align 3
.globl entSys
.globl ret_from_sys_call
.ent entSys
entSys:
stq $16,24($30)
stq $17,32($30)
stq $18,40($30)
SAVE_ALL
/* FIXME: optimize */
lda $1,current_set
ldq $2,0($1)
ldq $3,TASK_FLAGS($2)
and $3,PF_PTRACED,$3
bne $3,strace
/* end of strace */
lda $1,NR_SYSCALLS($31)
lda $2,sys_call_table
lda $27,do_entSys
cmpult $0,$1,$1
s8addq $0,$2,$2
beq $1,1f
ldq $27,0($2)
1: jsr $26,($27),do_entSys
blt $0,syscall_error /* the call failed */
stq $0,0($30)
stq $31,72($30) /* a3=0 => no error */
 
.align 3
ret_from_sys_call:
cmovne $26,0,$19 /* $19 = 0 => non-restartable */
/* check bottom half interrupts */
lda $0,intr_count
ldq $1,0($0)
bne $1,ret_from_handle_bh
lda $2,bh_active
ldq $3,0($2)
lda $2,bh_mask
ldq $4,0($2)
addq $1,1,$1
and $3,$4,$2
bne $2,handle_bottom_half
ret_from_handle_bh:
ldq $0,SP_OFF($30)
and $0,8,$0
beq $0,restore_all
ret_from_reschedule:
lda $0,need_resched
lda $1,current_set
ldl $2,0($0)
lda $4,init_task
ldq $3,0($1)
bne $2,reschedule
subq $4,$3,$4
beq $4,restore_all
ldq $4,TASK_SIGNAL($3)
ldq $16,TASK_BLOCKED($3)
bic $4,$16,$4
bne $4,signal_return
restore_all:
RESTORE_ALL
rti
/* PTRACE syscall handler */
.align 3
strace:
/* set up signal stack, call syscall_trace */
bsr $1,do_switch_stack
lda $27,syscall_trace
jsr $26,($27),syscall_trace
bsr $1,undo_switch_stack
 
/* get the system call number and the arguments back.. */
ldq $0,0($30)
ldq $16,SP_OFF+24($30)
ldq $17,SP_OFF+32($30)
ldq $18,SP_OFF+40($30)
ldq $19,72($30)
ldq $20,80($30)
ldq $21,88($30)
 
/* get the system call pointer.. */
lda $1,NR_SYSCALLS($31)
lda $2,sys_call_table
lda $27,do_entSys
cmpult $0,$1,$1
s8addq $0,$2,$2
beq $1,1f
ldq $27,0($2)
1: jsr $26,($27),do_entSys
 
/* check return.. */
blt $0,strace_error /* the call failed */
stq $31,72($30) /* a3=0 => no error */
strace_success:
stq $0,0($30) /* save return value */
 
bsr $1,do_switch_stack
lda $27,syscall_trace
jsr $26,($27),syscall_trace
bsr $1,undo_switch_stack
br $31,ret_from_sys_call
 
.align 3
strace_error:
ldq $19,0($30) /* old syscall nr (zero if success) */
beq $19,strace_success
ldq $20,72($30) /* .. and this a3 */
 
subq $31,$0,$0 /* with error in v0 */
addq $31,1,$1 /* set a3 for errno return */
stq $0,0($30)
stq $1,72($30) /* a3 for return */
 
bsr $1,do_switch_stack
bis $19,$19,$9 /* save old syscall number */
bis $20,$20,$10 /* save old a3 */
lda $27,syscall_trace
jsr $26,($27),syscall_trace
bis $9,$9,$19
bis $10,$10,$20
bsr $1,undo_switch_stack
 
bis $31,$31,$26 /* tell "ret_from_sys_call" that we can restart */
br $31,ret_from_sys_call
 
.align 3
handle_bottom_half:
/*
* We're called with $0 containing the address of
* 'intr_count' and $1 containing 'intr_count+1'
*/
stq $1,0($0) /* intr_count = 1 */
subq $30,16,$30
stq $19,0($30) /* save syscall nr */
stq $20,8($30) /* and error indication (a3) */
lda $27,do_bottom_half
jsr $26,($27),do_bottom_half
lda $0,intr_count
ldq $19,0($30)
ldq $20,8($30)
addq $30,16,$30
stq $31,0($0) /* intr_count = 0 */
br $31,ret_from_handle_bh
 
.align 3
syscall_error:
/*
* Some system calls (e.g., ptrace) can return arbitrary
* values which might normally be mistaken as error numbers.
* Those functions must zero $0 (v0) directly in the stack
* frame to indicate that a negative return value wasn't an
* error number..
*/
ldq $19,0($30) /* old syscall nr (zero if success) */
beq $19,ret_success
 
ldq $20,72($30) /* .. and this a3 */
subq $31,$0,$0 /* with error in v0 */
addq $31,1,$1 /* set a3 for errno return */
bis $31,$31,$26 /* tell "ret_from_sys_call" that we can restart */
stq $1,72($30) /* a3 for return */
ret_success:
stq $0,0($30)
br $31,ret_from_sys_call
 
.align 3
signal_return:
bis $30,$30,$17
br $1,do_switch_stack
bis $30,$30,$18
lda $27,do_signal
jsr $26,($27),do_signal
lda $30,SWITCH_STACK_SIZE($30)
br $31,restore_all
.end entSys
 
.align 3
.ent reschedule
reschedule:
subq $30,16,$30
stq $19,0($30) /* save syscall nr */
stq $20,8($30) /* and error indication (a3) */
lda $27,schedule
jsr $26,($27),schedule
ldq $19,0($30)
ldq $20,8($30)
addq $30,16,$30
br $31,ret_from_reschedule
.end reschedule
 
.align 3
.ent sys_sigreturn
sys_sigreturn:
bis $30,$30,$17
lda $30,-SWITCH_STACK_SIZE($30)
bis $30,$30,$18
lda $27,do_sigreturn
jsr $26,($27),do_sigreturn
br $1,undo_switch_stack
br $31,ret_from_sys_call
.end sys_sigreturn
 
.align 3
.ent sys_sigsuspend
sys_sigsuspend:
bis $30,$30,$17
br $1,do_switch_stack
bis $30,$30,$18
lda $27,do_sigsuspend
jsr $26,($27),do_sigsuspend
lda $30,SWITCH_STACK_SIZE($30)
br $31,ret_from_sys_call
.end sys_sigsuspend
 
.align 3
.globl sys_call_table
sys_call_table:
/*0*/ .quad do_entSys, sys_exit, sys_fork, sys_read, sys_write
.quad do_entSys, sys_close, sys_wait4, do_entSys, sys_link
.quad sys_unlink, do_entSys, sys_chdir, sys_fchdir, sys_mknod
.quad sys_chmod, sys_chown, sys_brk, do_entSys, sys_lseek
.quad sys_getxpid, osf_mount, osf_umount, sys_setuid, sys_getxuid
.quad do_entSys, sys_ptrace, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, sys_access, do_entSys
.quad do_entSys, sys_sync, sys_kill, do_entSys, sys_setpgid
.quad do_entSys, sys_dup, sys_pipe, do_entSys, do_entSys
.quad sys_open, do_entSys, sys_getxgid, osf_sigprocmask, do_entSys
/*50*/ .quad do_entSys, sys_acct, sys_sigpending, do_entSys, sys_ioctl
.quad do_entSys, do_entSys, sys_symlink, sys_readlink, sys_execve
.quad sys_umask, sys_chroot, do_entSys, sys_getpgrp, sys_getpagesize
.quad do_entSys, osf_vfork, sys_newstat, sys_newlstat, do_entSys
.quad do_entSys, osf_mmap, do_entSys, sys_munmap, sys_mprotect
.quad sys_madvise, sys_vhangup, do_entSys, do_entSys, sys_getgroups
/* map BSD's setpgrp to sys_setpgid for binary compatibility: */
.quad sys_setgroups, do_entSys, sys_setpgid, sys_setitimer, do_entSys
.quad do_entSys, sys_getitimer, sys_gethostname, sys_sethostname, sys_getdtablesize
.quad sys_dup2, sys_newfstat, sys_fcntl, sys_select, do_entSys
.quad sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept
/*100*/ .quad osf_getpriority, sys_send, sys_recv, sys_sigreturn, sys_bind
.quad sys_setsockopt, sys_listen, do_entSys, do_entSys, do_entSys
.quad do_entSys, sys_sigsuspend, do_entSys, sys_recvmsg, sys_sendmsg
.quad do_entSys, sys_gettimeofday, sys_getrusage, sys_getsockopt, do_entSys
.quad sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod
.quad sys_recvfrom, sys_setreuid, sys_setregid, sys_rename, sys_truncate
.quad sys_ftruncate, sys_flock, sys_setgid, sys_sendto, sys_shutdown
.quad sys_socketpair, sys_mkdir, sys_rmdir, sys_utimes, do_entSys
.quad do_entSys, sys_getpeername, do_entSys, do_entSys, sys_getrlimit
.quad sys_setrlimit, do_entSys, sys_setsid, sys_quotactl, do_entSys
/*150*/ .quad sys_getsockname, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, sys_sigaction, do_entSys, do_entSys, osf_getdirentries
.quad osf_statfs, osf_fstatfs, do_entSys, do_entSys, do_entSys
.quad osf_getdomainname, sys_setdomainname, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, osf_swapon
/*200*/ .quad sys_msgctl, sys_msgget, sys_msgrcv, sys_msgsnd, sys_semctl
.quad sys_semget, sys_semop, osf_utsname, do_entSys, osf_shmat
.quad sys_shmctl, sys_shmdt, sys_shmget, do_entSys, do_entSys
.quad do_entSys, do_entSys, sys_msync, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, sys_getpgid, sys_getsid
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, osf_proplist_syscall
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
/*250*/ .quad do_entSys, osf_usleep_thread, do_entSys, do_entSys, sys_sysfs
.quad do_entSys, osf_getsysinfo, osf_setsysinfo, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
/* linux-specific system calls start at 300 */
/*300*/ .quad sys_bdflush, sys_sethae, sys_mount, sys_adjtimex, sys_swapoff
.quad sys_getdents, alpha_create_module, sys_init_module, sys_delete_module, sys_get_kernel_syms
.quad sys_syslog, sys_reboot, sys_clone, sys_uselib, sys_mlock
.quad sys_munlock, sys_mlockall, sys_munlockall, sys_sysinfo, sys_sysctl
.quad sys_idle, sys_umount, sys_swapon, sys_times, sys_personality
.quad sys_setfsuid, sys_setfsgid, sys_ustat, sys_statfs, sys_fstatfs
.quad sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler, sys_sched_yield
.quad sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, do_entSys /* sys_afs_syscall */, sys_newuname
.quad sys_nanosleep, sys_mremap, do_entSys, do_entSys, do_entSys
.quad sys_pciconfig_read, sys_pciconfig_write, do_entSys, do_entSys, do_entSys
 
 
#ifdef CONFIG_KGDB
 
#define KGDB_STACK_SIZE (8*1024)
 
.lcomm kgdb_stack_bottom, KGDB_STACK_SIZE
 
/*
* The following is the nasty part of entering KGDB. GDB assumes that
* it can access the memory below the interrupted thread's stack
* pointer (SP). This means that the stub cannot run on this stack
* (interrupts could overwrite changes made by GDB). Similarly, we do
* not want to bother the GDB user with the call chain leading from
* the interrupt handler to this function. That is, the SP reported
* to GDB should be the thread's SP at the point it was interrupted. We
* achieve all this by switching to KGDB's own stack before saving any
* registers (of course, the PAL-generated frame is already on the stack and
* we copy that part explicitly). GDB does whatever it wants to the
* thread's stack; and when it continues execution, we restore the
* registers from the private stack and return from the interrupt.
*
* This is not re-entrant---gdb on the host probably would get confused
* anyway, but we probably ought to detect that case at the least...
*/
.align 3
.globl entKGDB
.ent entKGDB
entKGDB:
lda $16,kgdb_stack_bottom
lda $16,(KGDB_STACK_SIZE-48)($16)
bis $31,$30,$17 /* save real sp in a1 */
bis $31,$16,$30 /* update sp atomically! */
SAVE_ALL /* save regular stuff */
/*
* Only now can we disable interrupts (swpipl may step on
* t0,t8..t11, and a0):
*/
bis $31,7,$16
call_pal PAL_swpipl
 
/* copy PAL frame to kgdb stack: */
 
ldq $1,0($17)
ldq $2,8($17)
ldq $3,16($17)
ldq $4,24($17)
ldq $5,32($17)
ldq $6,40($17)
 
stq $1,0+184($30)
stq $2,8+184($30)
stq $3,16+184($30)
stq $4,24+184($30)
stq $5,32+184($30)
stq $6,40+184($30)
 
/* allocate space for and save caller saved regs as well as orig sp: */
 
lda $30,-320($30)
addq $17,48,$17 /* pop PAL frame */
stq $17,0($30) /* save original sp */
 
stq $9,0x08($30); stq $10,0x10($30); stq $11,0x18($30)
stq $12,0x20($30); stq $13,0x28($30); stq $14,0x30($30)
stq $15,0x38($30)
 
stt $f0,0x40($30); stt $f1,0x48($30); stt $f2,0x50($30); stt $f3,0x58($30)
stt $f4,0x60($30); stt $f5,0x68($30); stt $f6,0x70($30); stt $f7,0x78($30)
stt $f8,0x80($30); stt $f9,0x88($30); stt $f10,0x90($30); stt $f11,0x98($30)
stt $f12,0xa0($30); stt $f13,0xa8($30); stt $f14,0xb0($30); stt $f15,0xb8($30)
stt $f16,0xc0($30); stt $f17,0xc8($30); stt $f18,0xd0($30); stt $f19,0xd8($30)
stt $f20,0xe0($30); stt $f21,0xe8($30); stt $f22,0xf0($30); stt $f23,0xf8($30)
stt $f24,0x100($30); stt $f25,0x108($30); stt $f26,0x110($30); stt $f27,0x118($30)
stt $f28,0x120($30); stt $f29,0x128($30); stt $f30,0x130($30); stt $f31,0x138($30)
 
bis $31,$30,$16
 
lda $27,kgdb_handle_exception
jsr $26,($27),kgdb_handle_exception
 
ldq $17,0($30) /* load new sp and caller-saved registers */
 
ldq $9,0x08($30); ldq $10,0x10($30); ldq $11,0x18($30)
ldq $12,0x20($30); ldq $13,0x28($30); ldq $14,0x30($30)
ldq $15,0x38($30)
 
ldt $f0,0x40($30); ldt $f1,0x48($30); ldt $f2,0x50($30); ldt $f3,0x58($30)
ldt $f4,0x60($30); ldt $f5,0x68($30); ldt $f6,0x70($30); ldt $f7,0x78($30)
ldt $f8,0x80($30); ldt $f9,0x88($30); ldt $f10,0x90($30); ldt $f11,0x98($30)
ldt $f12,0xa0($30); ldt $f13,0xa8($30); ldt $f14,0xb0($30); ldt $f15,0xb8($30)
ldt $f16,0xc0($30); ldt $f17,0xc8($30); ldt $f18,0xd0($30); ldt $f19,0xd8($30)
ldt $f20,0xe0($30); ldt $f21,0xe8($30); ldt $f22,0xf0($30); ldt $f23,0xf8($30)
ldt $f24,0x100($30); ldt $f25,0x108($30); ldt $f26,0x110($30); ldt $f27,0x118($30)
ldt $f28,0x120($30); ldt $f29,0x128($30); ldt $f30,0x130($30); ldt $f31,0x138($30)
lda $30,320($30) /* pop extra register frame */
 
/*
* Copy PAL frame from kgdb stack to new stack so we can rti
* to it:
*/
ldq $1,0+184($30)
ldq $2,8+184($30)
ldq $3,16+184($30)
ldq $4,24+184($30)
ldq $5,32+184($30)
ldq $6,40+184($30)
 
subq $17,48,$17 /* alloc space for PAL frame on new stack */
 
stq $1,0($17)
stq $2,8($17)
stq $3,16($17)
stq $4,24($17)
stq $5,32($17)
stq $6,40($17)
 
RESTORE_ALL
bis $31,$17,$30 /* establish new sp */
rti
 
.end entKGDB
 
#endif /* CONFIG_KGDB */
/kernel/head.S
0,0 → 1,171
/*
* alpha/boot/head.S
*
* initial boot stuff.. At this point, the bootloader has already
* switched into OSF/1 PAL-code, and loaded us at the correct address
* (START_ADDR). So there isn't much left for us to do: just set up
* the kernel global pointer and jump to the kernel entry-point.
*/
 
#define __ASSEMBLY__
#include <asm/system.h>
 
#define halt call_pal PAL_halt
 
.globl swapper_pg_dir
.globl _stext
swapper_pg_dir=SWAPPER_PGD
 
.set noreorder
.globl __start
.ent __start
_stext:
__start:
br $27,1f
1: ldgp $29,0($27)
lda $27,start_kernel
jsr $26,($27),start_kernel
halt
.end __start
 
.align 3
.globl wrent
.ent wrent
wrent:
call_pal PAL_wrent
ret ($26)
.end wrent
 
.align 3
.globl wrkgp
.ent wrkgp
wrkgp:
call_pal PAL_wrkgp
ret ($26)
.end wrkgp
 
.align 3
.globl wrusp
.ent wrusp
wrusp:
call_pal PAL_wrusp
ret ($26)
.end wrusp
 
.align 3
.globl rdusp
.ent rdusp
rdusp:
call_pal PAL_rdusp
ret ($26)
.end rdusp
 
.align 3
.globl tbi
.ent tbi
tbi:
call_pal PAL_tbi
ret ($26)
.end tbi
 
.align 3
.globl imb
.ent imb
imb:
call_pal PAL_imb
ret ($26)
.end imb
 
.align 3
.globl rdmces
.ent rdmces
rdmces:
call_pal PAL_rdmces
ret ($26)
.end rdmces
 
.align 3
.globl wrmces
.ent wrmces
wrmces:
call_pal PAL_wrmces
ret ($26)
.end wrmces
 
.align 3
.globl whami
.ent whami
whami:
call_pal PAL_whami
ret ($26)
.end whami
.align 3
.globl wripir
.ent wripir
wripir:
call_pal PAL_wripir
ret ($26)
.end wripir
 
.align 3
.globl cserve_ena
.ent cserve_ena
cserve_ena:
lda $30,-0x08($30)
stq $17,0($30)
bis $16,$16,$17
lda $16,52($31)
call_pal PAL_cserve
ldq $17,0($30)
lda $30,0x08($30)
ret ($26)
.end cserve_ena
 
.align 3
.globl cserve_dis
.ent cserve_dis
cserve_dis:
lda $30,-0x08($30)
stq $17,0($30)
bis $16,$16,$17
lda $16,53($31)
call_pal PAL_cserve
ldq $17,0($30)
lda $30,0x08($30)
ret ($26)
.end cserve_dis
 
#
# The following two functions don't need trapb/excb instructions
# around the mf_fpcr/mt_fpcr instructions because (a) the kernel
# never generates arithmetic faults and (b) call_pal instructions
# are implied trap barriers.
#
.align 3
.globl rdfpcr
.ent rdfpcr
rdfpcr:
lda $30,-0x10($30)
stt $f0,0($30)
mf_fpcr $f0
stt $f0,8($30)
ldt $f0,0($30)
ldq $0,8($30)
lda $30,0x10($30)
ret ($26)
.end rdfpcr
 
.align 3
.globl wrfpcr
.ent wrfpcr
wrfpcr:
lda $30,-0x10($30)
stt $f0,0($30)
stq $16,8($30)
ldt $f0,8($30)
mt_fpcr $f0
ldt $f0,0($30)
lda $30,0x10($30)
ret ($26)
.end wrfpcr
/kernel/smc.c
0,0 → 1,2842
/*
* SMC 37C93X and 37C669 initialization code
*/
#include <linux/config.h>
#include <linux/kernel.h>
 
#if 0
# define DBG_DEVS(args) printk args
#else
# define DBG_DEVS(args)
#endif
 
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/malloc.h>
#include <linux/mm.h>
 
#include <asm/hwrpb.h>
#include <asm/io.h>
#include <asm/segment.h>
 
#define KB 1024
#define MB (1024*KB)
#define GB (1024*MB)
 
#if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)
 
/* device "activate" register contents */
#define DEVICE_ON 1
#define DEVICE_OFF 0
 
/* configuration on/off keys */
#define CONFIG_ON_KEY 0x55
#define CONFIG_OFF_KEY 0xaa
 
/* configuration space device definitions */
#define FDC 0
#define IDE1 1
#define IDE2 2
#define PARP 3
#define SER1 4
#define SER2 5
#define RTCL 6
#define KYBD 7
#define AUXIO 8
 
/* Chip register offsets from base */
#define CONFIG_CONTROL 0x02
#define INDEX_ADDRESS 0x03
#define LOGICAL_DEVICE_NUMBER 0x07
#define DEVICE_ID 0x20
#define DEVICE_REV 0x21
#define POWER_CONTROL 0x22
#define POWER_MGMT 0x23
#define OSC 0x24
 
#define ACTIVATE 0x30
#define ADDR_HI 0x60
#define ADDR_LO 0x61
#define INTERRUPT_SEL 0x70
#define INTERRUPT_SEL_2 0x72 /* KYBD/MOUS only */
#define DMA_CHANNEL_SEL 0x74 /* FDC/PARP only */
 
#define FDD_MODE_REGISTER 0x90
#define FDD_OPTION_REGISTER 0x91
 
/* values that we read back that are expected ... */
#define VALID_DEVICE_ID 2
 
/* default device addresses */
#define KYBD_INTERRUPT 1
#define MOUS_INTERRUPT 12
#define COM2_BASE 0x2f8
#define COM2_INTERRUPT 3
#define COM1_BASE 0x3f8
#define COM1_INTERRUPT 4
#define PARP_BASE 0x3bc
#define PARP_INTERRUPT 7
 
#define SMC_DEBUG 0
 
unsigned long SMCConfigState( unsigned long baseAddr )
{
unsigned char devId;
unsigned char devRev;
 
unsigned long configPort;
unsigned long indexPort;
unsigned long dataPort;
 
configPort = indexPort = baseAddr;
dataPort = ( unsigned long )( ( char * )configPort + 1 );
 
outb(CONFIG_ON_KEY, configPort);
outb(CONFIG_ON_KEY, configPort);
outb(DEVICE_ID, indexPort);
devId = inb(dataPort);
if ( devId == VALID_DEVICE_ID ) {
outb(DEVICE_REV, indexPort);
devRev = inb(dataPort);
}
else {
baseAddr = 0;
}
return( baseAddr );
}
 
void SMCRunState( unsigned long baseAddr )
{
outb(CONFIG_OFF_KEY, baseAddr);
}
 
unsigned long SMCDetectUltraIO(void)
{
unsigned long baseAddr;
 
baseAddr = 0x3F0;
if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) {
return( baseAddr );
}
baseAddr = 0x370;
if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) {
return( baseAddr );
}
return( ( unsigned long )0 );
}
 
void SMCEnableDevice( unsigned long baseAddr,
unsigned long device,
unsigned long portaddr,
unsigned long interrupt)
{
unsigned long indexPort;
unsigned long dataPort;
 
indexPort = baseAddr;
dataPort = ( unsigned long )( ( char * )baseAddr + 1 );
 
outb(LOGICAL_DEVICE_NUMBER, indexPort);
outb(device, dataPort);
 
outb(ADDR_LO, indexPort);
outb(( portaddr & 0xFF ), dataPort);
 
outb(ADDR_HI, indexPort);
outb(( ( portaddr >> 8 ) & 0xFF ), dataPort);
 
outb(INTERRUPT_SEL, indexPort);
outb(interrupt, dataPort);
 
outb(ACTIVATE, indexPort);
outb(DEVICE_ON, dataPort);
}
 
void SMCEnableKYBD( unsigned long baseAddr )
{
unsigned long indexPort;
unsigned long dataPort;
 
indexPort = baseAddr;
dataPort = ( unsigned long )( ( char * )baseAddr + 1 );
 
outb(LOGICAL_DEVICE_NUMBER, indexPort);
outb(KYBD, dataPort);
 
outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */
outb(KYBD_INTERRUPT, dataPort);
 
outb(INTERRUPT_SEL_2, indexPort);/* Secondary interrupt select */
outb(MOUS_INTERRUPT, dataPort);
 
outb(ACTIVATE, indexPort);
outb(DEVICE_ON, dataPort);
}
 
void SMCEnableFDC( unsigned long baseAddr )
{
unsigned long indexPort;
unsigned long dataPort;
 
unsigned char oldValue;
 
indexPort = baseAddr;
dataPort = ( unsigned long )( ( char * )baseAddr + 1 );
 
outb(LOGICAL_DEVICE_NUMBER, indexPort);
outb(FDC, dataPort);
 
outb(FDD_MODE_REGISTER, indexPort);
oldValue = inb(dataPort);
 
oldValue |= 0x0E; /* Enable burst mode */
outb(oldValue, dataPort);
 
outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */
outb(0x06, dataPort );
 
outb(DMA_CHANNEL_SEL, indexPort); /* DMA channel select */
outb(0x02, dataPort);
 
outb(ACTIVATE, indexPort);
outb(DEVICE_ON, dataPort);
}
 
#if SMC_DEBUG
void SMCReportDeviceStatus( unsigned long baseAddr )
{
unsigned long indexPort;
unsigned long dataPort;
unsigned char currentControl;
 
indexPort = baseAddr;
dataPort = ( unsigned long )( ( char * )baseAddr + 1 );
 
outb(POWER_CONTROL, indexPort);
currentControl = inb(dataPort);
 
if ( currentControl & ( 1 << FDC ) )
printk( "\t+FDC Enabled\n" );
else
printk( "\t-FDC Disabled\n" );
 
if ( currentControl & ( 1 << IDE1 ) )
printk( "\t+IDE1 Enabled\n" );
else
printk( "\t-IDE1 Disabled\n" );
 
if ( currentControl & ( 1 << IDE2 ) )
printk( "\t+IDE2 Enabled\n" );
else
printk( "\t-IDE2 Disabled\n" );
 
if ( currentControl & ( 1 << PARP ) )
printk( "\t+PARP Enabled\n" );
else
printk( "\t-PARP Disabled\n" );
 
if ( currentControl & ( 1 << SER1 ) )
printk( "\t+SER1 Enabled\n" );
else
printk( "\t-SER1 Disabled\n" );
 
if ( currentControl & ( 1 << SER2 ) )
printk( "\t+SER2 Enabled\n" );
else
printk( "\t-SER2 Disabled\n" );
 
printk( "\n" );
}
#endif
 
void SMC93X_Init(void)
{
unsigned long SMCUltraBase;
 
if ( ( SMCUltraBase = SMCDetectUltraIO( ) ) != ( unsigned long )0 ) {
printk( "SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n",
SMCUltraBase );
#if SMC_DEBUG
SMCReportDeviceStatus( SMCUltraBase );
#endif
SMCEnableDevice( SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT );
SMCEnableDevice( SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT );
SMCEnableDevice( SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT );
/* IDE on the SMC is not enabled; CMD646 (PCI) on MB */
SMCEnableKYBD( SMCUltraBase );
SMCEnableFDC( SMCUltraBase );
#if SMC_DEBUG
SMCReportDeviceStatus( SMCUltraBase );
#endif
SMCRunState( SMCUltraBase );
}
else {
#if SMC_DEBUG
printk( "No SMC FDC37C93X Ultra I/O Controller found\n" );
#endif
}
}
 
#endif /* PC164 || LX164 */
 
/* we include MIATA because the GL has the SMC669 on board */
#if defined(CONFIG_ALPHA_SX164) || defined(CONFIG_ALPHA_MIATA)
 
#define SMC_DEBUG 0
 
/* File: smcc669_def.h
*
* Copyright (C) 1997 by
* Digital Equipment Corporation, Maynard, Massachusetts.
* All rights reserved.
*
* This software is furnished under a license and may be used and copied
* only in accordance of the terms of such license and with the
* inclusion of the above copyright notice. This software or any other
* copies thereof may not be provided or otherwise made available to any
* other person. No title to and ownership of the software is hereby
* transferred.
*
* The information in this software is subject to change without notice
* and should not be construed as a commitment by Digital Equipment
* Corporation.
*
* Digital assumes no responsibility for the use or reliability of its
* software on equipment which is not supplied by Digital.
*
*
* Abstract:
*
* This file contains header definitions for the SMC37c669
* Super I/O controller.
*
* Author:
*
* Eric Rasmussen
*
* Modification History:
*
* er 28-Jan-1997 Initial Entry
*/
 
#ifndef __SMC37c669_H
#define __SMC37c669_H
 
/*
** Macros for handling device IRQs
**
** The mask acts as a flag used in mapping actual ISA IRQs (0 - 15)
** to device IRQs (A - H).
*/
#define SMC37c669_DEVICE_IRQ_MASK 0x80000000
#define SMC37c669_DEVICE_IRQ( __i ) \
((SMC37c669_DEVICE_IRQ_MASK) | (__i))
#define SMC37c669_IS_DEVICE_IRQ(__i) \
(((__i) & (SMC37c669_DEVICE_IRQ_MASK)) == (SMC37c669_DEVICE_IRQ_MASK))
#define SMC37c669_RAW_DEVICE_IRQ(__i) \
((__i) & ~(SMC37c669_DEVICE_IRQ_MASK))
 
/*
** Macros for handling device DRQs
**
** The mask acts as a flag used in mapping actual ISA DMA
** channels to device DMA channels (A - C).
*/
#define SMC37c669_DEVICE_DRQ_MASK 0x80000000
#define SMC37c669_DEVICE_DRQ(__d) \
((SMC37c669_DEVICE_DRQ_MASK) | (__d))
#define SMC37c669_IS_DEVICE_DRQ(__d) \
(((__d) & (SMC37c669_DEVICE_DRQ_MASK)) == (SMC37c669_DEVICE_DRQ_MASK))
#define SMC37c669_RAW_DEVICE_DRQ(__d) \
((__d) & ~(SMC37c669_DEVICE_DRQ_MASK))
 
#define SMC37c669_DEVICE_ID 0x3
 
/*
** SMC37c669 Device Function Definitions
*/
#define SERIAL_0 0
#define SERIAL_1 1
#define PARALLEL_0 2
#define FLOPPY_0 3
#define IDE_0 4
#define NUM_FUNCS 5
 
/*
** Default Device Function Mappings
*/
#define COM1_BASE 0x3F8
#define COM1_IRQ 4
#define COM2_BASE 0x2F8
#define COM2_IRQ 3
#define PARP_BASE 0x3BC
#define PARP_IRQ 7
#define PARP_DRQ 3
#define FDC_BASE 0x3F0
#define FDC_IRQ 6
#define FDC_DRQ 2
 
/*
** Configuration On/Off Key Definitions
*/
#define SMC37c669_CONFIG_ON_KEY 0x55
#define SMC37c669_CONFIG_OFF_KEY 0xAA
 
/*
** SMC 37c669 Device IRQs
*/
#define SMC37c669_DEVICE_IRQ_A ( SMC37c669_DEVICE_IRQ( 0x01 ) )
#define SMC37c669_DEVICE_IRQ_B ( SMC37c669_DEVICE_IRQ( 0x02 ) )
#define SMC37c669_DEVICE_IRQ_C ( SMC37c669_DEVICE_IRQ( 0x03 ) )
#define SMC37c669_DEVICE_IRQ_D ( SMC37c669_DEVICE_IRQ( 0x04 ) )
#define SMC37c669_DEVICE_IRQ_E ( SMC37c669_DEVICE_IRQ( 0x05 ) )
#define SMC37c669_DEVICE_IRQ_F ( SMC37c669_DEVICE_IRQ( 0x06 ) )
/* SMC37c669_DEVICE_IRQ_G *** RESERVED ***/
#define SMC37c669_DEVICE_IRQ_H ( SMC37c669_DEVICE_IRQ( 0x08 ) )
 
/*
** SMC 37c669 Device DMA Channel Definitions
*/
#define SMC37c669_DEVICE_DRQ_A ( SMC37c669_DEVICE_DRQ( 0x01 ) )
#define SMC37c669_DEVICE_DRQ_B ( SMC37c669_DEVICE_DRQ( 0x02 ) )
#define SMC37c669_DEVICE_DRQ_C ( SMC37c669_DEVICE_DRQ( 0x03 ) )
 
/*
** Configuration Register Index Definitions
*/
#define SMC37c669_CR00_INDEX 0x00
#define SMC37c669_CR01_INDEX 0x01
#define SMC37c669_CR02_INDEX 0x02
#define SMC37c669_CR03_INDEX 0x03
#define SMC37c669_CR04_INDEX 0x04
#define SMC37c669_CR05_INDEX 0x05
#define SMC37c669_CR06_INDEX 0x06
#define SMC37c669_CR07_INDEX 0x07
#define SMC37c669_CR08_INDEX 0x08
#define SMC37c669_CR09_INDEX 0x09
#define SMC37c669_CR0A_INDEX 0x0A
#define SMC37c669_CR0B_INDEX 0x0B
#define SMC37c669_CR0C_INDEX 0x0C
#define SMC37c669_CR0D_INDEX 0x0D
#define SMC37c669_CR0E_INDEX 0x0E
#define SMC37c669_CR0F_INDEX 0x0F
#define SMC37c669_CR10_INDEX 0x10
#define SMC37c669_CR11_INDEX 0x11
#define SMC37c669_CR12_INDEX 0x12
#define SMC37c669_CR13_INDEX 0x13
#define SMC37c669_CR14_INDEX 0x14
#define SMC37c669_CR15_INDEX 0x15
#define SMC37c669_CR16_INDEX 0x16
#define SMC37c669_CR17_INDEX 0x17
#define SMC37c669_CR18_INDEX 0x18
#define SMC37c669_CR19_INDEX 0x19
#define SMC37c669_CR1A_INDEX 0x1A
#define SMC37c669_CR1B_INDEX 0x1B
#define SMC37c669_CR1C_INDEX 0x1C
#define SMC37c669_CR1D_INDEX 0x1D
#define SMC37c669_CR1E_INDEX 0x1E
#define SMC37c669_CR1F_INDEX 0x1F
#define SMC37c669_CR20_INDEX 0x20
#define SMC37c669_CR21_INDEX 0x21
#define SMC37c669_CR22_INDEX 0x22
#define SMC37c669_CR23_INDEX 0x23
#define SMC37c669_CR24_INDEX 0x24
#define SMC37c669_CR25_INDEX 0x25
#define SMC37c669_CR26_INDEX 0x26
#define SMC37c669_CR27_INDEX 0x27
#define SMC37c669_CR28_INDEX 0x28
#define SMC37c669_CR29_INDEX 0x29
 
/*
** Configuration Register Alias Definitions
*/
#define SMC37c669_DEVICE_ID_INDEX SMC37c669_CR0D_INDEX
#define SMC37c669_DEVICE_REVISION_INDEX SMC37c669_CR0E_INDEX
#define SMC37c669_FDC_BASE_ADDRESS_INDEX SMC37c669_CR20_INDEX
#define SMC37c669_IDE_BASE_ADDRESS_INDEX SMC37c669_CR21_INDEX
#define SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX SMC37c669_CR22_INDEX
#define SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX SMC37c669_CR23_INDEX
#define SMC37c669_SERIAL0_BASE_ADDRESS_INDEX SMC37c669_CR24_INDEX
#define SMC37c669_SERIAL1_BASE_ADDRESS_INDEX SMC37c669_CR25_INDEX
#define SMC37c669_PARALLEL_FDC_DRQ_INDEX SMC37c669_CR26_INDEX
#define SMC37c669_PARALLEL_FDC_IRQ_INDEX SMC37c669_CR27_INDEX
#define SMC37c669_SERIAL_IRQ_INDEX SMC37c669_CR28_INDEX
 
/*
** Configuration Register Definitions
**
** The INDEX (write only) and DATA (read/write) ports are effective
** only when the chip is in the Configuration State.
*/
typedef struct _SMC37c669_CONFIG_REGS {
unsigned char index_port;
unsigned char data_port;
} SMC37c669_CONFIG_REGS;
 
/*
** CR00 - default value 0x28
**
** IDE_EN (CR00<1:0>):
** 0x - 30ua pull-ups on nIDEEN, nHDCS0, NHDCS1
** 11 - IRQ_H available as IRQ output,
** IRRX2, IRTX2 available as alternate IR pins
** 10 - nIDEEN, nHDCS0, nHDCS1 used to control IDE
**
** VALID (CR00<7>):
** A high level on this software controlled bit can
** be used to indicate that a valid configuration
** cycle has occurred. The control software must
** take care to set this bit at the appropriate times.
** Set to zero after power up. This bit has no
** effect on any other hardware in the chip.
**
*/
typedef union _SMC37c669_CR00 {
unsigned char as_uchar;
struct {
unsigned ide_en : 2; /* See note above */
unsigned reserved1 : 1; /* RAZ */
unsigned fdc_pwr : 1; /* 1 = supply power to FDC */
unsigned reserved2 : 3; /* Read as 010b */
unsigned valid : 1; /* See note above */
} by_field;
} SMC37c669_CR00;
 
/*
** CR01 - default value 0x9C
*/
typedef union _SMC37c669_CR01 {
unsigned char as_uchar;
struct {
unsigned reserved1 : 2; /* RAZ */
unsigned ppt_pwr : 1; /* 1 = supply power to PPT */
unsigned ppt_mode : 1; /* 1 = Printer mode, 0 = EPP */
unsigned reserved2 : 1; /* Read as 1 */
unsigned reserved3 : 2; /* RAZ */
unsigned lock_crx: 1; /* Lock CR00 - CR18 */
} by_field;
} SMC37c669_CR01;
 
/*
** CR02 - default value 0x88
*/
typedef union _SMC37c669_CR02 {
unsigned char as_uchar;
struct {
unsigned reserved1 : 3; /* RAZ */
unsigned uart1_pwr : 1; /* 1 = supply power to UART1 */
unsigned reserved2 : 3; /* RAZ */
unsigned uart2_pwr : 1; /* 1 = supply power to UART2 */
} by_field;
} SMC37c669_CR02;
 
/*
** CR03 - default value 0x78
**
** CR03<7> CR03<2> Pin 94
** ------- ------- ------
** 0 X DRV2 (input)
** 1 0 ADRX
** 1 1 IRQ_B
**
** CR03<6> CR03<5> Op Mode
** ------- ------- -------
** 0 0 Model 30
** 0 1 PS/2
** 1 0 Reserved
** 1 1 AT Mode
*/
typedef union _SMC37c669_CR03 {
unsigned char as_uchar;
struct {
unsigned pwrgd_gamecs : 1; /* 1 = PWRGD, 0 = GAMECS */
unsigned fdc_mode2 : 1; /* 1 = Enhanced Mode 2 */
unsigned pin94_0 : 1; /* See note above */
unsigned reserved1 : 1; /* RAZ */
unsigned drvden : 1; /* 1 = high, 0 - output */
unsigned op_mode : 2; /* See note above */
unsigned pin94_1 : 1; /* See note above */
} by_field;
} SMC37c669_CR03;
 
/*
** CR04 - default value 0x00
**
** PP_EXT_MODE:
** If CR01<PP_MODE> = 0 and PP_EXT_MODE =
** 00 - Standard and Bidirectional
** 01 - EPP mode and SPP
** 10 - ECP mode
** In this mode, 2 drives can be supported
** directly, 3 or 4 drives must use external
** 4 drive support. SPP can be selected
** through the ECR register of ECP as mode 000.
** 11 - ECP mode and EPP mode
** In this mode, 2 drives can be supported
** directly, 3 or 4 drives must use external
** 4 drive support. SPP can be selected
** through the ECR register of ECP as mode 000.
** In this mode, EPP can be selected through
** the ECR register of ECP as mode 100.
**
** PP_FDC:
** 00 - Normal
** 01 - PPFD1
** 10 - PPFD2
** 11 - Reserved
**
** MIDI1:
** Serial Clock Select:
** A low level on this bit disables MIDI support,
** clock = divide by 13. A high level on this
** bit enables MIDI support, clock = divide by 12.
**
** MIDI operates at 31.25 Kbps which can be derived
** from 125 KHz (24 MHz / 12 = 2 MHz, 2 MHz / 16 = 125 KHz)
**
** ALT_IO:
** 0 - Use pins IRRX, IRTX
** 1 - Use pins IRRX2, IRTX2
**
** If this bit is set, the IR receive and transmit
** functions will not be available on pins 25 and 26
** unless CR00<IDE_EN> = 11.
*/
typedef union _SMC37c669_CR04 {
unsigned char as_uchar;
struct {
unsigned ppt_ext_mode : 2; /* See note above */
unsigned ppt_fdc : 2; /* See note above */
unsigned midi1 : 1; /* See note above */
unsigned midi2 : 1; /* See note above */
unsigned epp_type : 1; /* 0 = EPP 1.9, 1 = EPP 1.7 */
unsigned alt_io : 1; /* See note above */
} by_field;
} SMC37c669_CR04;
 
/*
** CR05 - default value 0x00
**
** DEN_SEL:
** 00 - Densel output normal
** 01 - Reserved
** 10 - Densel output 1
** 11 - Densel output 0
**
*/
typedef union _SMC37c669_CR05 {
unsigned char as_uchar;
struct {
unsigned reserved1 : 2; /* RAZ */
unsigned fdc_dma_mode : 1; /* 0 = burst, 1 = non-burst */
unsigned den_sel : 2; /* See note above */
unsigned swap_drv : 1; /* Swap the FDC motor selects */
unsigned extx4 : 1; /* 0 = 2 drive, 1 = external 4 drive decode */
unsigned reserved2 : 1; /* RAZ */
} by_field;
} SMC37c669_CR05;
 
/*
** CR06 - default value 0xFF
*/
typedef union _SMC37c669_CR06 {
unsigned char as_uchar;
struct {
unsigned floppy_a : 2; /* Type of floppy drive A */
unsigned floppy_b : 2; /* Type of floppy drive B */
unsigned floppy_c : 2; /* Type of floppy drive C */
unsigned floppy_d : 2; /* Type of floppy drive D */
} by_field;
} SMC37c669_CR06;
 
/*
** CR07 - default value 0x00
**
** Auto Power Management CR07<7:4>:
** 0 - Auto Powerdown disabled (default)
** 1 - Auto Powerdown enabled
**
** This bit is reset to the default state by POR or
** a hardware reset.
**
*/
typedef union _SMC37c669_CR07 {
unsigned char as_uchar;
struct {
unsigned floppy_boot : 2; /* 0 = A:, 1 = B: */
unsigned reserved1 : 2; /* RAZ */
unsigned ppt_en : 1; /* See note above */
unsigned uart1_en : 1; /* See note above */
unsigned uart2_en : 1; /* See note above */
unsigned fdc_en : 1; /* See note above */
} by_field;
} SMC37c669_CR07;
 
/*
** CR08 - default value 0x00
*/
typedef union _SMC37c669_CR08 {
unsigned char as_uchar;
struct {
unsigned zero : 4; /* 0 */
unsigned addrx7_4 : 4; /* ADR<7:3> for ADRx decode */
} by_field;
} SMC37c669_CR08;
 
/*
** CR09 - default value 0x00
**
** ADRx_CONFIG:
** 00 - ADRx disabled
** 01 - 1 byte decode A<3:0> = 0000b
** 10 - 8 byte block decode A<3:0> = 0XXXb
** 11 - 16 byte block decode A<3:0> = XXXXb
**
*/
typedef union _SMC37c669_CR09 {
unsigned char as_uchar;
struct {
unsigned adra8 : 3; /* ADR<10:8> for ADRx decode */
unsigned reserved1 : 3;
unsigned adrx_config : 2; /* See note above */
} by_field;
} SMC37c669_CR09;
 
/*
** CR0A - default value 0x00
*/
typedef union _SMC37c669_CR0A {
unsigned char as_uchar;
struct {
unsigned ecp_fifo_threshold : 4;
unsigned reserved1 : 4;
} by_field;
} SMC37c669_CR0A;
 
/*
** CR0B - default value 0x00
*/
typedef union _SMC37c669_CR0B {
unsigned char as_uchar;
struct {
unsigned fdd0_drtx : 2; /* FDD0 Data Rate Table */
unsigned fdd1_drtx : 2; /* FDD1 Data Rate Table */
unsigned fdd2_drtx : 2; /* FDD2 Data Rate Table */
unsigned fdd3_drtx : 2; /* FDD3 Data Rate Table */
} by_field;
} SMC37c669_CR0B;
 
/*
** CR0C - default value 0x00
**
** UART2_MODE:
** 000 - Standard (default)
** 001 - IrDA (HPSIR)
** 010 - Amplitude Shift Keyed IR @500 KHz
** 011 - Reserved
** 1xx - Reserved
**
*/
typedef union _SMC37c669_CR0C {
unsigned char as_uchar;
struct {
unsigned uart2_rcv_polarity : 1; /* 1 = invert RX */
unsigned uart2_xmit_polarity : 1; /* 1 = invert TX */
unsigned uart2_duplex : 1; /* 1 = full, 0 = half */
unsigned uart2_mode : 3; /* See note above */
unsigned uart1_speed : 1; /* 1 = high speed enabled */
unsigned uart2_speed : 1; /* 1 = high speed enabled */
} by_field;
} SMC37c669_CR0C;
 
/*
** CR0D - default value 0x03
**
** Device ID Register - read only
*/
typedef union _SMC37c669_CR0D {
unsigned char as_uchar;
struct {
unsigned device_id : 8; /* Returns 0x3 in this field */
} by_field;
} SMC37c669_CR0D;
 
/*
** CR0E - default value 0x02
**
** Device Revision Register - read only
*/
typedef union _SMC37c669_CR0E {
unsigned char as_uchar;
struct {
unsigned device_rev : 8; /* Returns 0x2 in this field */
} by_field;
} SMC37c669_CR0E;
 
/*
** CR0F - default value 0x00
*/
typedef union _SMC37c669_CR0F {
unsigned char as_uchar;
struct {
unsigned test0 : 1; /* Reserved - set to 0 */
unsigned test1 : 1; /* Reserved - set to 0 */
unsigned test2 : 1; /* Reserved - set to 0 */
unsigned test3 : 1; /* Reserved - set t0 0 */
unsigned test4 : 1; /* Reserved - set to 0 */
unsigned test5 : 1; /* Reserved - set t0 0 */
unsigned test6 : 1; /* Reserved - set t0 0 */
unsigned test7 : 1; /* Reserved - set to 0 */
} by_field;
} SMC37c669_CR0F;
 
/*
** CR10 - default value 0x00
*/
typedef union _SMC37c669_CR10 {
unsigned char as_uchar;
struct {
unsigned reserved1 : 3; /* RAZ */
unsigned pll_gain : 1; /* 1 = 3V, 2 = 5V operation */
unsigned pll_stop : 1; /* 1 = stop PLLs */
unsigned ace_stop : 1; /* 1 = stop UART clocks */
unsigned pll_clock_ctrl : 1; /* 0 = 14.318 MHz, 1 = 24 MHz */
unsigned ir_test : 1; /* Enable IR test mode */
} by_field;
} SMC37c669_CR10;
 
/*
** CR11 - default value 0x00
*/
typedef union _SMC37c669_CR11 {
unsigned char as_uchar;
struct {
unsigned ir_loopback : 1; /* Internal IR loop back */
unsigned test_10ms : 1; /* Test 10ms autopowerdown FDC timeout */
unsigned reserved1 : 6; /* RAZ */
} by_field;
} SMC37c669_CR11;
 
/*
** CR12 - CR1D are reserved registers
*/
 
/*
** CR1E - default value 0x80
**
** GAMECS:
** 00 - GAMECS disabled
** 01 - 1 byte decode ADR<3:0> = 0001b
** 10 - 8 byte block decode ADR<3:0> = 0XXXb
** 11 - 16 byte block decode ADR<3:0> = XXXXb
**
*/
typedef union _SMC37c66_CR1E {
unsigned char as_uchar;
struct {
unsigned gamecs_config: 2; /* See note above */
unsigned gamecs_addr9_4 : 6; /* GAMECS Addr<9:4> */
} by_field;
} SMC37c669_CR1E;
 
/*
** CR1F - default value 0x00
**
** DT0 DT1 DRVDEN0 DRVDEN1 Drive Type
** --- --- ------- ------- ----------
** 0 0 DENSEL DRATE0 4/2/1 MB 3.5"
** 2/1 MB 5.25"
** 2/1.6/1 MB 3.5" (3-mode)
** 0 1 DRATE1 DRATE0
** 1 0 nDENSEL DRATE0 PS/2
** 1 1 DRATE0 DRATE1
**
** Note: DENSEL, DRATE1, and DRATE0 map onto two output
** pins - DRVDEN0 and DRVDEN1.
**
*/
typedef union _SMC37c669_CR1F {
unsigned char as_uchar;
struct {
unsigned fdd0_drive_type : 2; /* FDD0 drive type */
unsigned fdd1_drive_type : 2; /* FDD1 drive type */
unsigned fdd2_drive_type : 2; /* FDD2 drive type */
unsigned fdd3_drive_type : 2; /* FDD3 drive type */
} by_field;
} SMC37c669_CR1F;
 
/*
** CR20 - default value 0x3C
**
** FDC Base Address Register
** - To disable this decode set Addr<9:8> = 0
** - A<10> = 0, A<3:0> = 0XXXb to access.
**
*/
typedef union _SMC37c669_CR20 {
unsigned char as_uchar;
struct {
unsigned zero : 2; /* 0 */
unsigned addr9_4 : 6; /* FDC Addr<9:4> */
} by_field;
} SMC37c669_CR20;
 
/*
** CR21 - default value 0x3C
**
** IDE Base Address Register
** - To disable this decode set Addr<9:8> = 0
** - A<10> = 0, A<3:0> = 0XXXb to access.
**
*/
typedef union _SMC37c669_CR21 {
unsigned char as_uchar;
struct {
unsigned zero : 2; /* 0 */
unsigned addr9_4 : 6; /* IDE Addr<9:4> */
} by_field;
} SMC37c669_CR21;
 
/*
** CR22 - default value 0x3D
**
** IDE Alternate Status Base Address Register
** - To disable this decode set Addr<9:8> = 0
** - A<10> = 0, A<3:0> = 0110b to access.
**
*/
typedef union _SMC37c669_CR22 {
unsigned char as_uchar;
struct {
unsigned zero : 2; /* 0 */
unsigned addr9_4 : 6; /* IDE Alt Status Addr<9:4> */
} by_field;
} SMC37c669_CR22;
 
/*
** CR23 - default value 0x00
**
** Parallel Port Base Address Register
** - To disable this decode set Addr<9:8> = 0
** - A<10> = 0 to access.
** - If EPP is enabled, A<2:0> = XXXb to access.
** If EPP is NOT enabled, A<1:0> = XXb to access
**
*/
typedef union _SMC37c669_CR23 {
unsigned char as_uchar;
struct {
unsigned addr9_2 : 8; /* Parallel Port Addr<9:2> */
} by_field;
} SMC37c669_CR23;
 
/*
** CR24 - default value 0x00
**
** UART1 Base Address Register
** - To disable this decode set Addr<9:8> = 0
** - A<10> = 0, A<2:0> = XXXb to access.
**
*/
typedef union _SMC37c669_CR24 {
unsigned char as_uchar;
struct {
unsigned zero : 1; /* 0 */
unsigned addr9_3 : 7; /* UART1 Addr<9:3> */
} by_field;
} SMC37c669_CR24;
 
/*
** CR25 - default value 0x00
**
** UART2 Base Address Register
** - To disable this decode set Addr<9:8> = 0
** - A<10> = 0, A<2:0> = XXXb to access.
**
*/
typedef union _SMC37c669_CR25 {
unsigned char as_uchar;
struct {
unsigned zero : 1; /* 0 */
unsigned addr9_3 : 7; /* UART2 Addr<9:3> */
} by_field;
} SMC37c669_CR25;
 
/*
** CR26 - default value 0x00
**
** Parallel Port / FDC DMA Select Register
**
** D3 - D0 DMA
** D7 - D4 Selected
** ------- --------
** 0000 None
** 0001 DMA_A
** 0010 DMA_B
** 0011 DMA_C
**
*/
typedef union _SMC37c669_CR26 {
unsigned char as_uchar;
struct {
unsigned ppt_drq : 4; /* See note above */
unsigned fdc_drq : 4; /* See note above */
} by_field;
} SMC37c669_CR26;
 
/*
** CR27 - default value 0x00
**
** Parallel Port / FDC IRQ Select Register
**
** D3 - D0 IRQ
** D7 - D4 Selected
** ------- --------
** 0000 None
** 0001 IRQ_A
** 0010 IRQ_B
** 0011 IRQ_C
** 0100 IRQ_D
** 0101 IRQ_E
** 0110 IRQ_F
** 0111 Reserved
** 1000 IRQ_H
**
** Any unselected IRQ REQ is in tristate
**
*/
typedef union _SMC37c669_CR27 {
unsigned char as_uchar;
struct {
unsigned ppt_irq : 4; /* See note above */
unsigned fdc_irq : 4; /* See note above */
} by_field;
} SMC37c669_CR27;
 
/*
** CR28 - default value 0x00
**
** UART IRQ Select Register
**
** D3 - D0 IRQ
** D7 - D4 Selected
** ------- --------
** 0000 None
** 0001 IRQ_A
** 0010 IRQ_B
** 0011 IRQ_C
** 0100 IRQ_D
** 0101 IRQ_E
** 0110 IRQ_F
** 0111 Reserved
** 1000 IRQ_H
** 1111 share with UART1 (only for UART2)
**
** Any unselected IRQ REQ is in tristate
**
** To share an IRQ between UART1 and UART2, set
** UART1 to use the desired IRQ and set UART2 to
** 0xF to enable sharing mechanism.
**
*/
typedef union _SMC37c669_CR28 {
unsigned char as_uchar;
struct {
unsigned uart2_irq : 4; /* See note above */
unsigned uart1_irq : 4; /* See note above */
} by_field;
} SMC37c669_CR28;
 
/*
** CR29 - default value 0x00
**
** IRQIN IRQ Select Register
**
** D3 - D0 IRQ
** D7 - D4 Selected
** ------- --------
** 0000 None
** 0001 IRQ_A
** 0010 IRQ_B
** 0011 IRQ_C
** 0100 IRQ_D
** 0101 IRQ_E
** 0110 IRQ_F
** 0111 Reserved
** 1000 IRQ_H
**
** Any unselected IRQ REQ is in tristate
**
*/
typedef union _SMC37c669_CR29 {
unsigned char as_uchar;
struct {
unsigned irqin_irq : 4; /* See note above */
unsigned reserved1 : 4; /* RAZ */
} by_field;
} SMC37c669_CR29;
 
/*
** Aliases of Configuration Register formats (should match
** the set of index aliases).
**
** Note that CR24 and CR25 have the same format and are the
** base address registers for UART1 and UART2. Because of
** this we only define 1 alias here - for CR24 - as the serial
** base address register.
**
** Note that CR21 and CR22 have the same format and are the
** base address and alternate status address registers for
** the IDE controller. Because of this we only define 1 alias
** here - for CR21 - as the IDE address register.
**
*/
typedef SMC37c669_CR0D SMC37c669_DEVICE_ID_REGISTER;
typedef SMC37c669_CR0E SMC37c669_DEVICE_REVISION_REGISTER;
typedef SMC37c669_CR20 SMC37c669_FDC_BASE_ADDRESS_REGISTER;
typedef SMC37c669_CR21 SMC37c669_IDE_ADDRESS_REGISTER;
typedef SMC37c669_CR23 SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER;
typedef SMC37c669_CR24 SMC37c669_SERIAL_BASE_ADDRESS_REGISTER;
typedef SMC37c669_CR26 SMC37c669_PARALLEL_FDC_DRQ_REGISTER;
typedef SMC37c669_CR27 SMC37c669_PARALLEL_FDC_IRQ_REGISTER;
typedef SMC37c669_CR28 SMC37c669_SERIAL_IRQ_REGISTER;
 
/*
** ISA/Device IRQ Translation Table Entry Definition
*/
typedef struct _SMC37c669_IRQ_TRANSLATION_ENTRY {
int device_irq;
int isa_irq;
} SMC37c669_IRQ_TRANSLATION_ENTRY;
 
/*
** ISA/Device DMA Translation Table Entry Definition
*/
typedef struct _SMC37c669_DRQ_TRANSLATION_ENTRY {
int device_drq;
int isa_drq;
} SMC37c669_DRQ_TRANSLATION_ENTRY;
 
/*
** External Interface Function Prototype Declarations
*/
 
SMC37c669_CONFIG_REGS *SMC37c669_detect(
void
);
 
unsigned int SMC37c669_enable_device(
unsigned int func
);
 
unsigned int SMC37c669_disable_device(
unsigned int func
);
 
unsigned int SMC37c669_configure_device(
unsigned int func,
int port,
int irq,
int drq
);
 
void SMC37c669_display_device_info(
void
);
 
#endif /* __SMC37c669_H */
 
/* file: smcc669.c
*
* Copyright (C) 1997 by
* Digital Equipment Corporation, Maynard, Massachusetts.
* All rights reserved.
*
* This software is furnished under a license and may be used and copied
* only in accordance of the terms of such license and with the
* inclusion of the above copyright notice. This software or any other
* copies thereof may not be provided or otherwise made available to any
* other person. No title to and ownership of the software is hereby
* transferred.
*
* The information in this software is subject to change without notice
* and should not be construed as a commitment by digital equipment
* corporation.
*
* Digital assumes no responsibility for the use or reliability of its
* software on equipment which is not supplied by digital.
*/
 
/*
*++
* FACILITY:
*
* Alpha SRM Console Firmware
*
* MODULE DESCRIPTION:
*
* SMC37c669 Super I/O controller configuration routines.
*
* AUTHORS:
*
* Eric Rasmussen
*
* CREATION DATE:
*
* 28-Jan-1997
*
* MODIFICATION HISTORY:
*
* er 01-May-1997 Fixed pointer conversion errors in
* SMC37c669_get_device_config().
* er 28-Jan-1997 Initial version.
*
*--
*/
#if 0
/* $INCLUDE_OPTIONS$ */
#include "cp$inc:platform_io.h"
/* $INCLUDE_OPTIONS_END$ */
#include "cp$src:common.h"
#include "cp$inc:prototypes.h"
#include "cp$src:kernel_def.h"
#include "cp$src:msg_def.h"
#include "cp$src:smcc669_def.h"
/* Platform-specific includes */
#include "cp$src:platform.h"
#endif
 
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
 
#define wb( _x_, _y_ ) outb( _y_, (unsigned int)((unsigned long)_x_) )
#define rb( _x_ ) inb( (unsigned int)((unsigned long)_x_) )
 
/*
** Local storage for device configuration information.
**
** Since the SMC37c669 does not provide an explicit
** mechanism for enabling/disabling individual device
** functions, other than unmapping the device, local
** storage for device configuration information is
** allocated here for use in implementing our own
** function enable/disable scheme.
*/
static struct DEVICE_CONFIG {
unsigned int port1;
unsigned int port2;
unsigned int irq;
unsigned int drq;
} local_config [NUM_FUNCS];
 
/*
** List of all possible addresses for the Super I/O chip
*/
static unsigned long SMC37c669_Addresses[] =
{
0x3F0UL, /* Primary address */
0x370UL, /* Secondary address */
0UL /* End of list */
};
 
/*
** Global Pointer to the Super I/O device
*/
static SMC37c669_CONFIG_REGS *SMC37c669 = NULL;
 
/*
** IRQ Translation Table
**
** The IRQ translation table is a list of SMC37c669 device
** and standard ISA IRQs.
**
*/
static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table;
 
/*
** The following definition is for the default IRQ
** translation table.
*/
static SMC37c669_IRQ_TRANSLATION_ENTRY SMC37c669_default_irq_table[ ] =
{
{ SMC37c669_DEVICE_IRQ_A, -1 },
{ SMC37c669_DEVICE_IRQ_B, -1 },
{ SMC37c669_DEVICE_IRQ_C, 7 },
{ SMC37c669_DEVICE_IRQ_D, 6 },
{ SMC37c669_DEVICE_IRQ_E, 4 },
{ SMC37c669_DEVICE_IRQ_F, 3 },
{ SMC37c669_DEVICE_IRQ_H, -1 },
{ -1, -1 } /* End of table */
};
 
/*
** DRQ Translation Table
**
** The DRQ translation table is a list of SMC37c669 device and
** ISA DMA channels.
**
*/
static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table;
 
/*
** The following definition is the default DRQ
** translation table.
*/
static SMC37c669_DRQ_TRANSLATION_ENTRY SMC37c669_default_drq_table[ ] =
{
{ SMC37c669_DEVICE_DRQ_A, 2 },
{ SMC37c669_DEVICE_DRQ_B, 3 },
{ SMC37c669_DEVICE_DRQ_C, -1 },
{ -1, -1 } /* End of table */
};
 
/*
** Local Function Prototype Declarations
*/
 
static unsigned int SMC37c669_is_device_enabled(
unsigned int func
);
 
#if 0
static unsigned int SMC37c669_get_device_config(
unsigned int func,
int *port,
int *irq,
int *drq
);
#endif
 
static void SMC37c669_config_mode(
unsigned int enable
);
 
static unsigned char SMC37c669_read_config(
unsigned char index
);
 
static void SMC37c669_write_config(
unsigned char index,
unsigned char data
);
 
static void SMC37c669_init_local_config( void );
 
static struct DEVICE_CONFIG *SMC37c669_get_config(
unsigned int func
);
 
static int SMC37c669_xlate_irq(
unsigned int irq
);
 
static int SMC37c669_xlate_drq(
unsigned int drq
);
 
#if 0
/*
** External Data Declarations
*/
 
extern struct LOCK spl_atomic;
 
/*
** External Function Prototype Declarations
*/
 
/* From kernel_alpha.mar */
extern spinlock(
struct LOCK *spl
);
 
extern spinunlock(
struct LOCK *spl
);
 
/* From filesys.c */
int allocinode(
char *name,
int can_create,
struct INODE **ipp
);
 
extern int null_procedure( void );
 
int smcc669_init( void );
int smcc669_open( struct FILE *fp, char *info, char *next, char *mode );
int smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf );
int smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf );
int smcc669_close( struct FILE *fp );
 
struct DDB smc_ddb = {
"smc", /* how this routine wants to be called */
smcc669_read, /* read routine */
smcc669_write, /* write routine */
smcc669_open, /* open routine */
smcc669_close, /* close routine */
null_procedure, /* name expansion routine */
null_procedure, /* delete routine */
null_procedure, /* create routine */
null_procedure, /* setmode */
null_procedure, /* validation routine */
0, /* class specific use */
1, /* allows information */
0, /* must be stacked */
0, /* is a flash update driver */
0, /* is a block device */
0, /* not seekable */
0, /* is an ethernet device */
0, /* is a filesystem driver */
};
#endif
 
#define spinlock(x)
#define spinunlock(x)
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function detects the presence of an SMC37c669 Super I/O
** controller.
**
** FORMAL PARAMETERS:
**
** None
**
** RETURN VALUE:
**
** Returns a pointer to the device if found, otherwise,
** the NULL pointer is returned.
**
** SIDE EFFECTS:
**
** None
**
**--
*/
SMC37c669_CONFIG_REGS *SMC37c669_detect( void )
{
int i;
SMC37c669_DEVICE_ID_REGISTER id;
 
for ( i = 0; SMC37c669_Addresses[i] != 0; i++ ) {
/*
** Initialize the device pointer even though we don't yet know if
** the controller is at this address. The support functions access
** the controller through this device pointer so we need to set it
** even when we are looking ...
*/
SMC37c669 = ( SMC37c669_CONFIG_REGS * )SMC37c669_Addresses[i];
/*
** Enter configuration mode
*/
SMC37c669_config_mode( TRUE );
/*
** Read the device id
*/
id.as_uchar = SMC37c669_read_config( SMC37c669_DEVICE_ID_INDEX );
/*
** Exit configuration mode
*/
SMC37c669_config_mode( FALSE );
/*
** Does the device id match? If so, assume we have found an
** SMC37c669 controller at this address.
*/
if ( id.by_field.device_id == SMC37c669_DEVICE_ID ) {
/*
** Initialize the IRQ and DRQ translation tables.
*/
SMC37c669_irq_table = SMC37c669_default_irq_table;
SMC37c669_drq_table = SMC37c669_default_drq_table;
/*
** erfix
**
** If the platform can't use the IRQ and DRQ defaults set up in this
** file, it should call a platform-specific external routine at this
** point to reset the IRQ and DRQ translation table pointers to point
** at the appropriate tables for the platform. If the defaults are
** acceptable, then the external routine should do nothing.
*/
 
/*
** Put the chip back into configuration mode
*/
SMC37c669_config_mode( TRUE );
/*
** Initialize local storage for configuration information
*/
SMC37c669_init_local_config( );
/*
** Exit configuration mode
*/
SMC37c669_config_mode( FALSE );
/*
** SMC37c669 controller found, break out of search loop
*/
break;
}
else {
/*
** Otherwise, we did not find an SMC37c669 controller at this
** address so set the device pointer to NULL.
*/
SMC37c669 = NULL;
}
}
return SMC37c669;
}
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function enables an SMC37c669 device function.
**
** FORMAL PARAMETERS:
**
** func:
** Which device function to enable
**
** RETURN VALUE:
**
** Returns TRUE is the device function was enabled, otherwise, FALSE
**
** SIDE EFFECTS:
**
** {@description or none@}
**
** DESIGN:
**
** Enabling a device function in the SMC37c669 controller involves
** setting all of its mappings (port, irq, drq ...). A local
** "shadow" copy of the device configuration is kept so we can
** just set each mapping to what the local copy says.
**
** This function ALWAYS updates the local shadow configuration of
** the device function being enabled, even if the device is always
** enabled. To avoid replication of code, functions such as
** configure_device set up the local copy and then call this
** function to the update the real device.
**
**--
*/
unsigned int SMC37c669_enable_device ( unsigned int func )
{
unsigned int ret_val = FALSE;
/*
** Put the device into configuration mode
*/
SMC37c669_config_mode( TRUE );
switch ( func ) {
case SERIAL_0:
{
SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
SMC37c669_SERIAL_IRQ_REGISTER irq;
/*
** Enable the serial 1 IRQ mapping
*/
irq.as_uchar =
SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
 
irq.by_field.uart1_irq =
SMC37c669_RAW_DEVICE_IRQ(
SMC37c669_xlate_irq( local_config[ func ].irq )
);
 
SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
/*
** Enable the serial 1 port base address mapping
*/
base_addr.as_uchar = 0;
base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3;
 
SMC37c669_write_config(
SMC37c669_SERIAL0_BASE_ADDRESS_INDEX,
base_addr.as_uchar
);
ret_val = TRUE;
break;
}
case SERIAL_1:
{
SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
SMC37c669_SERIAL_IRQ_REGISTER irq;
/*
** Enable the serial 2 IRQ mapping
*/
irq.as_uchar =
SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
 
irq.by_field.uart2_irq =
SMC37c669_RAW_DEVICE_IRQ(
SMC37c669_xlate_irq( local_config[ func ].irq )
);
 
SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
/*
** Enable the serial 2 port base address mapping
*/
base_addr.as_uchar = 0;
base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3;
 
SMC37c669_write_config(
SMC37c669_SERIAL1_BASE_ADDRESS_INDEX,
base_addr.as_uchar
);
ret_val = TRUE;
break;
}
case PARALLEL_0:
{
SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr;
SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
/*
** Enable the parallel port DMA channel mapping
*/
drq.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
 
drq.by_field.ppt_drq =
SMC37c669_RAW_DEVICE_DRQ(
SMC37c669_xlate_drq( local_config[ func ].drq )
);
 
SMC37c669_write_config(
SMC37c669_PARALLEL_FDC_DRQ_INDEX,
drq.as_uchar
);
/*
** Enable the parallel port IRQ mapping
*/
irq.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
 
irq.by_field.ppt_irq =
SMC37c669_RAW_DEVICE_IRQ(
SMC37c669_xlate_irq( local_config[ func ].irq )
);
 
SMC37c669_write_config(
SMC37c669_PARALLEL_FDC_IRQ_INDEX,
irq.as_uchar
);
/*
** Enable the parallel port base address mapping
*/
base_addr.as_uchar = 0;
base_addr.by_field.addr9_2 = local_config[ func ].port1 >> 2;
 
SMC37c669_write_config(
SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX,
base_addr.as_uchar
);
ret_val = TRUE;
break;
}
case FLOPPY_0:
{
SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr;
SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
/*
** Enable the floppy controller DMA channel mapping
*/
drq.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
drq.by_field.fdc_drq =
SMC37c669_RAW_DEVICE_DRQ(
SMC37c669_xlate_drq( local_config[ func ].drq )
);
SMC37c669_write_config(
SMC37c669_PARALLEL_FDC_DRQ_INDEX,
drq.as_uchar
);
/*
** Enable the floppy controller IRQ mapping
*/
irq.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
irq.by_field.fdc_irq =
SMC37c669_RAW_DEVICE_IRQ(
SMC37c669_xlate_irq( local_config[ func ].irq )
);
SMC37c669_write_config(
SMC37c669_PARALLEL_FDC_IRQ_INDEX,
irq.as_uchar
);
/*
** Enable the floppy controller base address mapping
*/
base_addr.as_uchar = 0;
base_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4;
SMC37c669_write_config(
SMC37c669_FDC_BASE_ADDRESS_INDEX,
base_addr.as_uchar
);
ret_val = TRUE;
break;
}
case IDE_0:
{
SMC37c669_IDE_ADDRESS_REGISTER ide_addr;
/*
** Enable the IDE alternate status base address mapping
*/
ide_addr.as_uchar = 0;
ide_addr.by_field.addr9_4 = local_config[ func ].port2 >> 4;
SMC37c669_write_config(
SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX,
ide_addr.as_uchar
);
/*
** Enable the IDE controller base address mapping
*/
ide_addr.as_uchar = 0;
ide_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4;
SMC37c669_write_config(
SMC37c669_IDE_BASE_ADDRESS_INDEX,
ide_addr.as_uchar
);
ret_val = TRUE;
break;
}
}
/*
** Exit configuration mode and return
*/
SMC37c669_config_mode( FALSE );
 
return ret_val;
}
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function disables a device function within the
** SMC37c669 Super I/O controller.
**
** FORMAL PARAMETERS:
**
** func:
** Which function to disable
**
** RETURN VALUE:
**
** Return TRUE if the device function was disabled, otherwise, FALSE
**
** SIDE EFFECTS:
**
** {@description or none@}
**
** DESIGN:
**
** Disabling a function in the SMC37c669 device involves
** disabling all the function's mappings (port, irq, drq ...).
** A shadow copy of the device configuration is maintained
** in local storage so we won't worry aboving saving the
** current configuration information.
**
**--
*/
unsigned int SMC37c669_disable_device ( unsigned int func )
{
unsigned int ret_val = FALSE;
 
/*
** Put the device into configuration mode
*/
SMC37c669_config_mode( TRUE );
switch ( func ) {
case SERIAL_0:
{
SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
SMC37c669_SERIAL_IRQ_REGISTER irq;
/*
** Disable the serial 1 IRQ mapping
*/
irq.as_uchar =
SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
 
irq.by_field.uart1_irq = 0;
 
SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
/*
** Disable the serial 1 port base address mapping
*/
base_addr.as_uchar = 0;
SMC37c669_write_config(
SMC37c669_SERIAL0_BASE_ADDRESS_INDEX,
base_addr.as_uchar
);
ret_val = TRUE;
break;
}
case SERIAL_1:
{
SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
SMC37c669_SERIAL_IRQ_REGISTER irq;
/*
** Disable the serial 2 IRQ mapping
*/
irq.as_uchar =
SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
 
irq.by_field.uart2_irq = 0;
 
SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
/*
** Disable the serial 2 port base address mapping
*/
base_addr.as_uchar = 0;
 
SMC37c669_write_config(
SMC37c669_SERIAL1_BASE_ADDRESS_INDEX,
base_addr.as_uchar
);
ret_val = TRUE;
break;
}
case PARALLEL_0:
{
SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr;
SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
/*
** Disable the parallel port DMA channel mapping
*/
drq.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
 
drq.by_field.ppt_drq = 0;
 
SMC37c669_write_config(
SMC37c669_PARALLEL_FDC_DRQ_INDEX,
drq.as_uchar
);
/*
** Disable the parallel port IRQ mapping
*/
irq.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
 
irq.by_field.ppt_irq = 0;
 
SMC37c669_write_config(
SMC37c669_PARALLEL_FDC_IRQ_INDEX,
irq.as_uchar
);
/*
** Disable the parallel port base address mapping
*/
base_addr.as_uchar = 0;
 
SMC37c669_write_config(
SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX,
base_addr.as_uchar
);
ret_val = TRUE;
break;
}
case FLOPPY_0:
{
SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr;
SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
/*
** Disable the floppy controller DMA channel mapping
*/
drq.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
drq.by_field.fdc_drq = 0;
SMC37c669_write_config(
SMC37c669_PARALLEL_FDC_DRQ_INDEX,
drq.as_uchar
);
/*
** Disable the floppy controller IRQ mapping
*/
irq.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
irq.by_field.fdc_irq = 0;
SMC37c669_write_config(
SMC37c669_PARALLEL_FDC_IRQ_INDEX,
irq.as_uchar
);
/*
** Disable the floppy controller base address mapping
*/
base_addr.as_uchar = 0;
SMC37c669_write_config(
SMC37c669_FDC_BASE_ADDRESS_INDEX,
base_addr.as_uchar
);
ret_val = TRUE;
break;
}
case IDE_0:
{
SMC37c669_IDE_ADDRESS_REGISTER ide_addr;
/*
** Disable the IDE alternate status base address mapping
*/
ide_addr.as_uchar = 0;
SMC37c669_write_config(
SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX,
ide_addr.as_uchar
);
/*
** Disable the IDE controller base address mapping
*/
ide_addr.as_uchar = 0;
SMC37c669_write_config(
SMC37c669_IDE_BASE_ADDRESS_INDEX,
ide_addr.as_uchar
);
ret_val = TRUE;
break;
}
}
/*
** Exit configuration mode and return
*/
SMC37c669_config_mode( FALSE );
 
return ret_val;
}
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function configures a device function within the
** SMC37c669 Super I/O controller.
**
** FORMAL PARAMETERS:
**
** func:
** Which device function
**
** port:
** I/O port for the function to use
**
** irq:
** IRQ for the device function to use
**
** drq:
** DMA channel for the device function to use
**
** RETURN VALUE:
**
** Returns TRUE if the device function was configured,
** otherwise, FALSE.
**
** SIDE EFFECTS:
**
** {@description or none@}
**
** DESIGN:
**
** If this function returns TRUE, the local shadow copy of
** the configuration is also updated. If the device function
** is currently disabled, only the local shadow copy is
** updated and the actual device function will be updated
** if/when it is enabled.
**
**--
*/
unsigned int SMC37c669_configure_device (
unsigned int func,
int port,
int irq,
int drq )
{
struct DEVICE_CONFIG *cp;
 
/*
** Check for a valid configuration
*/
if ( ( cp = SMC37c669_get_config ( func ) ) != NULL ) {
/*
** Configuration is valid, update the local shadow copy
*/
if ( ( drq & ~0xFF ) == 0 ) {
cp->drq = drq;
}
if ( ( irq & ~0xFF ) == 0 ) {
cp->irq = irq;
}
if ( ( port & ~0xFFFF ) == 0 ) {
cp->port1 = port;
}
/*
** If the device function is enabled, update the actual
** device configuration.
*/
if ( SMC37c669_is_device_enabled( func ) ) {
SMC37c669_enable_device( func );
}
return TRUE;
}
return FALSE;
}
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function determines whether a device function
** within the SMC37c669 controller is enabled.
**
** FORMAL PARAMETERS:
**
** func:
** Which device function
**
** RETURN VALUE:
**
** Returns TRUE if the device function is enabled, otherwise, FALSE
**
** SIDE EFFECTS:
**
** {@description or none@}
**
** DESIGN:
**
** To check whether a device is enabled we will only look at
** the port base address mapping. According to the SMC37c669
** specification, all of the port base address mappings are
** disabled if the addr<9:8> (bits <7:6> of the register) are
** zero.
**
**--
*/
static unsigned int SMC37c669_is_device_enabled ( unsigned int func )
{
unsigned char base_addr = 0;
unsigned int dev_ok = FALSE;
unsigned int ret_val = FALSE;
/*
** Enter configuration mode
*/
SMC37c669_config_mode( TRUE );
switch ( func ) {
case SERIAL_0:
base_addr =
SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX );
dev_ok = TRUE;
break;
case SERIAL_1:
base_addr =
SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX );
dev_ok = TRUE;
break;
case PARALLEL_0:
base_addr =
SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX );
dev_ok = TRUE;
break;
case FLOPPY_0:
base_addr =
SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX );
dev_ok = TRUE;
break;
case IDE_0:
base_addr =
SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX );
dev_ok = TRUE;
break;
}
/*
** If we have a valid device, check base_addr<7:6> to see if the
** device is enabled (mapped).
*/
if ( ( dev_ok ) && ( ( base_addr & 0xC0 ) != 0 ) ) {
/*
** The mapping is not disabled, so assume that the function is
** enabled.
*/
ret_val = TRUE;
}
/*
** Exit configuration mode
*/
SMC37c669_config_mode( FALSE );
 
return ret_val;
}
 
#if 0
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function retrieves the configuration information of a
** device function within the SMC37c699 Super I/O controller.
**
** FORMAL PARAMETERS:
**
** func:
** Which device function
**
** port:
** I/O port returned
**
** irq:
** IRQ returned
**
** drq:
** DMA channel returned
**
** RETURN VALUE:
**
** Returns TRUE if the device configuration was successfully
** retrieved, otherwise, FALSE.
**
** SIDE EFFECTS:
**
** The data pointed to by the port, irq, and drq parameters
** my be modified even if the configuration is not successfully
** retrieved.
**
** DESIGN:
**
** The device configuration is fetched from the local shadow
** copy. Any unused parameters will be set to -1. Any
** parameter which is not desired can specify the NULL
** pointer.
**
**--
*/
static unsigned int SMC37c669_get_device_config (
unsigned int func,
int *port,
int *irq,
int *drq )
{
struct DEVICE_CONFIG *cp;
unsigned int ret_val = FALSE;
/*
** Check for a valid device configuration
*/
if ( ( cp = SMC37c669_get_config( func ) ) != NULL ) {
if ( drq != NULL ) {
*drq = cp->drq;
ret_val = TRUE;
}
if ( irq != NULL ) {
*irq = cp->irq;
ret_val = TRUE;
}
if ( port != NULL ) {
*port = cp->port1;
ret_val = TRUE;
}
}
return ret_val;
}
#endif
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function displays the current state of the SMC37c699
** Super I/O controller's device functions.
**
** FORMAL PARAMETERS:
**
** None
**
** RETURN VALUE:
**
** None
**
** SIDE EFFECTS:
**
** None
**
**--
*/
void SMC37c669_display_device_info ( void )
{
if ( SMC37c669_is_device_enabled( SERIAL_0 ) ) {
printk( " Serial 0: Enabled [ Port 0x%x, IRQ %d ]\n",
local_config[ SERIAL_0 ].port1,
local_config[ SERIAL_0 ].irq
);
}
else {
printk( " Serial 0: Disabled\n" );
}
 
if ( SMC37c669_is_device_enabled( SERIAL_1 ) ) {
printk( " Serial 1: Enabled [ Port 0x%x, IRQ %d ]\n",
local_config[ SERIAL_1 ].port1,
local_config[ SERIAL_1 ].irq
);
}
else {
printk( " Serial 1: Disabled\n" );
}
 
if ( SMC37c669_is_device_enabled( PARALLEL_0 ) ) {
printk( " Parallel: Enabled [ Port 0x%x, IRQ %d/%d ]\n",
local_config[ PARALLEL_0 ].port1,
local_config[ PARALLEL_0 ].irq,
local_config[ PARALLEL_0 ].drq
);
}
else {
printk( " Parallel: Disabled\n" );
}
 
if ( SMC37c669_is_device_enabled( FLOPPY_0 ) ) {
printk( " Floppy Ctrl: Enabled [ Port 0x%x, IRQ %d/%d ]\n",
local_config[ FLOPPY_0 ].port1,
local_config[ FLOPPY_0 ].irq,
local_config[ FLOPPY_0 ].drq
);
}
else {
printk( " Floppy Ctrl: Disabled\n" );
}
 
if ( SMC37c669_is_device_enabled( IDE_0 ) ) {
printk( " IDE 0: Enabled [ Port 0x%x, IRQ %d ]\n",
local_config[ IDE_0 ].port1,
local_config[ IDE_0 ].irq
);
}
else {
printk( " IDE 0: Disabled\n" );
}
}
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function puts the SMC37c669 Super I/O controller into,
** and takes it out of, configuration mode.
**
** FORMAL PARAMETERS:
**
** enable:
** TRUE to enter configuration mode, FALSE to exit.
**
** RETURN VALUE:
**
** None
**
** SIDE EFFECTS:
**
** The SMC37c669 controller may be left in configuration mode.
**
**--
*/
static void SMC37c669_config_mode(
unsigned int enable )
{
if ( enable ) {
/*
** To enter configuration mode, two writes in succession to the index
** port are required. If a write to another address or port occurs
** between these two writes, the chip does not enter configuration
** mode. Therefore, a spinlock is placed around the two writes to
** guarantee that they complete uninterrupted.
*/
spinlock( &spl_atomic );
wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY );
wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY );
spinunlock( &spl_atomic );
}
else {
wb( &SMC37c669->index_port, SMC37c669_CONFIG_OFF_KEY );
}
}
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function reads an SMC37c669 Super I/O controller
** configuration register. This function assumes that the
** device is already in configuration mode.
**
** FORMAL PARAMETERS:
**
** index:
** Index value of configuration register to read
**
** RETURN VALUE:
**
** Data read from configuration register
**
** SIDE EFFECTS:
**
** None
**
**--
*/
static unsigned char SMC37c669_read_config(
unsigned char index )
{
unsigned char data;
 
wb( &SMC37c669->index_port, index );
data = rb( &SMC37c669->data_port );
return data;
}
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function writes an SMC37c669 Super I/O controller
** configuration register. This function assumes that the
** device is already in configuration mode.
**
** FORMAL PARAMETERS:
**
** index:
** Index of configuration register to write
**
** data:
** Data to be written
**
** RETURN VALUE:
**
** None
**
** SIDE EFFECTS:
**
** None
**
**--
*/
static void SMC37c669_write_config(
unsigned char index,
unsigned char data )
{
wb( &SMC37c669->index_port, index );
wb( &SMC37c669->data_port, data );
}
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function initializes the local device
** configuration storage. This function assumes
** that the device is already in configuration
** mode.
**
** FORMAL PARAMETERS:
**
** None
**
** RETURN VALUE:
**
** None
**
** SIDE EFFECTS:
**
** Local storage for device configuration information
** is initialized.
**
**--
*/
static void SMC37c669_init_local_config ( void )
{
SMC37c669_SERIAL_BASE_ADDRESS_REGISTER uart_base;
SMC37c669_SERIAL_IRQ_REGISTER uart_irqs;
SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER ppt_base;
SMC37c669_PARALLEL_FDC_IRQ_REGISTER ppt_fdc_irqs;
SMC37c669_PARALLEL_FDC_DRQ_REGISTER ppt_fdc_drqs;
SMC37c669_FDC_BASE_ADDRESS_REGISTER fdc_base;
SMC37c669_IDE_ADDRESS_REGISTER ide_base;
SMC37c669_IDE_ADDRESS_REGISTER ide_alt;
 
/*
** Get serial port 1 base address
*/
uart_base.as_uchar =
SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX );
/*
** Get IRQs for serial ports 1 & 2
*/
uart_irqs.as_uchar =
SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
/*
** Store local configuration information for serial port 1
*/
local_config[SERIAL_0].port1 = uart_base.by_field.addr9_3 << 3;
local_config[SERIAL_0].irq =
SMC37c669_xlate_irq(
SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart1_irq )
);
/*
** Get serial port 2 base address
*/
uart_base.as_uchar =
SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX );
/*
** Store local configuration information for serial port 2
*/
local_config[SERIAL_1].port1 = uart_base.by_field.addr9_3 << 3;
local_config[SERIAL_1].irq =
SMC37c669_xlate_irq(
SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart2_irq )
);
/*
** Get parallel port base address
*/
ppt_base.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX );
/*
** Get IRQs for parallel port and floppy controller
*/
ppt_fdc_irqs.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
/*
** Get DRQs for parallel port and floppy controller
*/
ppt_fdc_drqs.as_uchar =
SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
/*
** Store local configuration information for parallel port
*/
local_config[PARALLEL_0].port1 = ppt_base.by_field.addr9_2 << 2;
local_config[PARALLEL_0].irq =
SMC37c669_xlate_irq(
SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.ppt_irq )
);
local_config[PARALLEL_0].drq =
SMC37c669_xlate_drq(
SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.ppt_drq )
);
/*
** Get floppy controller base address
*/
fdc_base.as_uchar =
SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX );
/*
** Store local configuration information for floppy controller
*/
local_config[FLOPPY_0].port1 = fdc_base.by_field.addr9_4 << 4;
local_config[FLOPPY_0].irq =
SMC37c669_xlate_irq(
SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.fdc_irq )
);
local_config[FLOPPY_0].drq =
SMC37c669_xlate_drq(
SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.fdc_drq )
);
/*
** Get IDE controller base address
*/
ide_base.as_uchar =
SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX );
/*
** Get IDE alternate status base address
*/
ide_alt.as_uchar =
SMC37c669_read_config( SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX );
/*
** Store local configuration information for IDE controller
*/
local_config[IDE_0].port1 = ide_base.by_field.addr9_4 << 4;
local_config[IDE_0].port2 = ide_alt.by_field.addr9_4 << 4;
local_config[IDE_0].irq = 14;
}
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function returns a pointer to the local shadow
** configuration of the requested device function.
**
** FORMAL PARAMETERS:
**
** func:
** Which device function
**
** RETURN VALUE:
**
** Returns a pointer to the DEVICE_CONFIG structure for the
** requested function, otherwise, NULL.
**
** SIDE EFFECTS:
**
** {@description or none@}
**
**--
*/
static struct DEVICE_CONFIG *SMC37c669_get_config( unsigned int func )
{
struct DEVICE_CONFIG *cp = NULL;
 
switch ( func ) {
case SERIAL_0:
cp = &local_config[ SERIAL_0 ];
break;
case SERIAL_1:
cp = &local_config[ SERIAL_1 ];
break;
case PARALLEL_0:
cp = &local_config[ PARALLEL_0 ];
break;
case FLOPPY_0:
cp = &local_config[ FLOPPY_0 ];
break;
case IDE_0:
cp = &local_config[ IDE_0 ];
break;
}
return cp;
}
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function translates IRQs back and forth between ISA
** IRQs and SMC37c669 device IRQs.
**
** FORMAL PARAMETERS:
**
** irq:
** The IRQ to translate
**
** RETURN VALUE:
**
** Returns the translated IRQ, otherwise, returns -1.
**
** SIDE EFFECTS:
**
** {@description or none@}
**
**--
*/
static int SMC37c669_xlate_irq ( unsigned int irq )
{
int i, translated_irq = -1;
 
if ( SMC37c669_IS_DEVICE_IRQ( irq ) ) {
/*
** We are translating a device IRQ to an ISA IRQ
*/
for ( i = 0; ( SMC37c669_irq_table[i].device_irq != -1 ) || ( SMC37c669_irq_table[i].isa_irq != -1 ); i++ ) {
if ( irq == SMC37c669_irq_table[i].device_irq ) {
translated_irq = SMC37c669_irq_table[i].isa_irq;
break;
}
}
}
else {
/*
** We are translating an ISA IRQ to a device IRQ
*/
for ( i = 0; ( SMC37c669_irq_table[i].isa_irq != -1 ) || ( SMC37c669_irq_table[i].device_irq != -1 ); i++ ) {
if ( irq == SMC37c669_irq_table[i].isa_irq ) {
translated_irq = SMC37c669_irq_table[i].device_irq;
break;
}
}
}
return translated_irq;
}
 
/*
**++
** FUNCTIONAL DESCRIPTION:
**
** This function translates DMA channels back and forth between
** ISA DMA channels and SMC37c669 device DMA channels.
**
** FORMAL PARAMETERS:
**
** drq:
** The DMA channel to translate
**
** RETURN VALUE:
**
** Returns the translated DMA channel, otherwise, returns -1
**
** SIDE EFFECTS:
**
** {@description or none@}
**
**--
*/
static int SMC37c669_xlate_drq ( unsigned int drq )
{
int i, translated_drq = -1;
 
if ( SMC37c669_IS_DEVICE_DRQ( drq ) ) {
/*
** We are translating a device DMA channel to an ISA DMA channel
*/
for ( i = 0; ( SMC37c669_drq_table[i].device_drq != -1 ) || ( SMC37c669_drq_table[i].isa_drq != -1 ); i++ ) {
if ( drq == SMC37c669_drq_table[i].device_drq ) {
translated_drq = SMC37c669_drq_table[i].isa_drq;
break;
}
}
}
else {
/*
** We are translating an ISA DMA channel to a device DMA channel
*/
for ( i = 0; ( SMC37c669_drq_table[i].isa_drq != -1 ) || ( SMC37c669_drq_table[i].device_drq != -1 ); i++ ) {
if ( drq == SMC37c669_drq_table[i].isa_drq ) {
translated_drq = SMC37c669_drq_table[i].device_drq;
break;
}
}
}
return translated_drq;
}
#if 0
int smcc669_init ( void )
{
struct INODE *ip;
 
allocinode( smc_ddb.name, 1, &ip );
ip->dva = &smc_ddb;
ip->attr = ATTR$M_WRITE | ATTR$M_READ;
ip->len[0] = 0x30;
ip->misc = 0;
INODE_UNLOCK( ip );
 
return msg_success;
}
int smcc669_open( struct FILE *fp, char *info, char *next, char *mode )
{
struct INODE *ip;
/*
** Allow multiple readers but only one writer. ip->misc keeps track
** of the number of writers
*/
ip = fp->ip;
INODE_LOCK( ip );
if ( fp->mode & ATTR$M_WRITE ) {
if ( ip->misc ) {
INODE_UNLOCK( ip );
return msg_failure; /* too many writers */
}
ip->misc++;
}
/*
** Treat the information field as a byte offset
*/
*fp->offset = xtoi( info );
INODE_UNLOCK( ip );
 
return msg_success;
}
int smcc669_close( struct FILE *fp )
{
struct INODE *ip;
 
ip = fp->ip;
if ( fp->mode & ATTR$M_WRITE ) {
INODE_LOCK( ip );
ip->misc--;
INODE_UNLOCK( ip );
}
return msg_success;
}
int smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf )
{
int i;
int length;
int nbytes;
struct INODE *ip;
 
/*
** Always access a byte at a time
*/
ip = fp->ip;
length = size * number;
nbytes = 0;
 
SMC37c669_config_mode( TRUE );
for ( i = 0; i < length; i++ ) {
if ( !inrange( *fp->offset, 0, ip->len[0] ) )
break;
*buf++ = SMC37c669_read_config( *fp->offset );
*fp->offset += 1;
nbytes++;
}
SMC37c669_config_mode( FALSE );
return nbytes;
}
int smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf )
{
int i;
int length;
int nbytes;
struct INODE *ip;
/*
** Always access a byte at a time
*/
ip = fp->ip;
length = size * number;
nbytes = 0;
 
SMC37c669_config_mode( TRUE );
for ( i = 0; i < length; i++ ) {
if ( !inrange( *fp->offset, 0, ip->len[0] ) )
break;
SMC37c669_write_config( *fp->offset, *buf );
*fp->offset += 1;
buf++;
nbytes++;
}
SMC37c669_config_mode( FALSE );
return nbytes;
}
#endif
 
void
SMC37c669_dump_registers(void)
{
int i;
for (i = 0; i <= 0x29; i++)
printk("-- CR%02x : %02x\n", i, SMC37c669_read_config(i));
}
/*+
* ============================================================================
* = SMC_init - SMC37c669 Super I/O controller initialization =
* ============================================================================
*
* OVERVIEW:
*
* This routine configures and enables device functions on the
* SMC37c669 Super I/O controller.
*
* FORM OF CALL:
*
* SMC_init( );
*
* RETURNS:
*
* Nothing
*
* ARGUMENTS:
*
* None
*
* SIDE EFFECTS:
*
* None
*
-*/
void SMC669_Init ( void )
{
SMC37c669_CONFIG_REGS *SMC_base;
 
if ( ( SMC_base = SMC37c669_detect( ) ) != NULL ) {
printk( "SMC37c669 Super I/O Controller found @ 0x%lx\n",
(unsigned long) SMC_base );
#if SMC_DEBUG
SMC37c669_config_mode( TRUE );
SMC37c669_dump_registers( );
SMC37c669_config_mode( FALSE );
SMC37c669_display_device_info( );
#endif
SMC37c669_disable_device( SERIAL_0 );
SMC37c669_configure_device(
SERIAL_0,
COM1_BASE,
COM1_IRQ,
-1
);
SMC37c669_enable_device( SERIAL_0 );
 
SMC37c669_disable_device( SERIAL_1 );
SMC37c669_configure_device(
SERIAL_1,
COM2_BASE,
COM2_IRQ,
-1
);
SMC37c669_enable_device( SERIAL_1 );
 
SMC37c669_disable_device( PARALLEL_0 );
SMC37c669_configure_device(
PARALLEL_0,
PARP_BASE,
PARP_IRQ,
PARP_DRQ
);
SMC37c669_enable_device( PARALLEL_0 );
 
SMC37c669_disable_device( FLOPPY_0 );
SMC37c669_configure_device(
FLOPPY_0,
FDC_BASE,
FDC_IRQ,
FDC_DRQ
);
SMC37c669_enable_device( FLOPPY_0 );
SMC37c669_disable_device( IDE_0 );
 
#if SMC_DEBUG
SMC37c669_config_mode( TRUE );
SMC37c669_dump_registers( );
SMC37c669_config_mode( FALSE );
SMC37c669_display_device_info( );
#endif
}
else {
#if SMC_DEBUG
printk( "No SMC37c669 Super I/O Controller found\n" );
#endif
}
}
 
#endif /* SX164 */
/kernel/t2.c
0,0 → 1,654
/*
* Code common to all T2 chips.
*
* Written by Jay A Estabrook (jestabro@amt.tay1.dec.com).
* December 1996.
*
* based on CIA code by David A Rusling (david.rusling@reo.mts.dec.com)
*
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/sched.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/ptrace.h>
#include <asm/mmu_context.h>
 
extern struct hwrpb_struct *hwrpb;
extern asmlinkage void wrmces(unsigned long mces);
extern asmlinkage unsigned long whami(void);
 
#define CPUID whami()
 
/*
* Machine check reasons. Defined according to PALcode sources
* (osf.h and platform.h).
*/
#define MCHK_K_TPERR 0x0080
#define MCHK_K_TCPERR 0x0082
#define MCHK_K_HERR 0x0084
#define MCHK_K_ECC_C 0x0086
#define MCHK_K_ECC_NC 0x0088
#define MCHK_K_OS_BUGCHECK 0x008A
#define MCHK_K_PAL_BUGCHECK 0x0090
 
/*
* BIOS32-style PCI interface:
*/
 
#ifdef CONFIG_ALPHA_T2
 
#ifdef DEBUG_CONF
# define DBG(args) printk args
#else
# define DBG(args)
#endif
 
#ifdef DEBUG_MCHECK
# define DBGMC(args) printk args
#else
# define DBGMC(args)
#endif
 
#define vulp volatile unsigned long *
#define vuip volatile unsigned int *
 
static volatile unsigned int T2_mcheck_expected = 0;
static volatile unsigned int T2_mcheck_taken = 0;
static unsigned long T2_jd;
 
#ifdef CONFIG_ALPHA_SRM_SETUP
unsigned int T2_DMA_WIN_BASE = T2_DMA_WIN_BASE_DEFAULT;
unsigned int T2_DMA_WIN_SIZE = T2_DMA_WIN_SIZE_DEFAULT;
unsigned long t2_sm_base;
#endif /* SRM_SETUP */
 
/*
* Given a bus, device, and function number, compute resulting
* configuration space address and setup the T2_HAXR2 register
* accordingly. It is therefore not safe to have concurrent
* invocations to configuration space access routines, but there
* really shouldn't be any need for this.
*
* Type 0:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:11 Device select bit.
* 10:8 Function number
* 7:2 Register number
*
* Type 1:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:24 reserved
* 23:16 bus number (8 bits = 128 possible buses)
* 15:11 Device number (5 bits)
* 10:8 function number
* 7:2 register number
*
* Notes:
* The function number selects which function of a multi-function device
* (e.g., scsi and ethernet).
*
* The register selects a DWORD (32 bit) register offset. Hence it
* doesn't get shifted by 2 bits as we want to "drop" the bottom two
* bits.
*/
static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned long *pci_addr,
unsigned char *type1)
{
unsigned long addr;
 
DBG(("mk_conf_addr(bus=%d,dfn=0x%x,where=0x%x,addr=0x%lx,type1=0x%x)\n",
bus, device_fn, where, pci_addr, type1));
 
if (bus == 0) {
int device = device_fn >> 3;
 
/* type 0 configuration cycle: */
 
if (device > 8) {
DBG(("mk_conf_addr: device (%d)>20, returning -1\n",
device));
return -1;
}
 
*type1 = 0;
#if 0
addr = (device_fn << 8) | (where);
#else
addr = (0x0800L << device) | ((device_fn & 7) << 8) | (where);
#endif
} else {
/* type 1 configuration cycle: */
*type1 = 1;
addr = (bus << 16) | (device_fn << 8) | (where);
}
*pci_addr = addr;
DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
return 0;
}
 
 
static unsigned int conf_read(unsigned long addr, unsigned char type1)
{
unsigned long flags;
unsigned int stat0, value;
unsigned int t2_cfg = 0; /* to keep gcc quiet */
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
 
#if 0
/* reset status register to avoid losing errors: */
stat0 = *((volatile unsigned int *)T2_IOCSR);
*((volatile unsigned int *)T2_IOCSR) = stat0;
mb();
DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0));
/* if Type1 access, must set T2 CFG */
if (type1) {
t2_cfg = *((unsigned int *)T2_IOC_CFG);
mb();
*((unsigned int *)T2_IOC_CFG) = t2_cfg | 1;
DBG(("conf_read: TYPE1 access\n"));
}
mb();
draina();
#endif
T2_mcheck_expected = 1;
T2_mcheck_taken = 0;
mb();
/* access configuration space: */
value = *((volatile unsigned int *)addr);
mb();
mb();
if (T2_mcheck_taken) {
T2_mcheck_taken = 0;
value = 0xffffffffU;
mb();
}
T2_mcheck_expected = 0;
mb();
 
#if 0
/* if Type1 access, must reset IOC CFG so normal IO space ops work */
if (type1) {
*((unsigned int *)T2_IOC_CFG) = t2_cfg & ~1;
mb();
}
#endif
DBG(("conf_read(): finished\n"));
 
restore_flags(flags);
return value;
}
 
 
static void conf_write(unsigned long addr, unsigned int value,
unsigned char type1)
{
unsigned long flags;
unsigned int stat0;
unsigned int t2_cfg = 0; /* to keep gcc quiet */
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
#if 0
/* reset status register to avoid losing errors: */
stat0 = *((volatile unsigned int *)T2_IOCSR);
*((volatile unsigned int *)T2_IOCSR) = stat0;
mb();
DBG(("conf_write: T2 ERR was 0x%x\n", stat0));
/* if Type1 access, must set T2 CFG */
if (type1) {
t2_cfg = *((unsigned int *)T2_IOC_CFG);
mb();
*((unsigned int *)T2_IOC_CFG) = t2_cfg | 1;
DBG(("conf_write: TYPE1 access\n"));
}
draina();
#endif
T2_mcheck_expected = 1;
mb();
/* access configuration space: */
*((volatile unsigned int *)addr) = value;
mb();
mb();
T2_mcheck_expected = 0;
mb();
 
#if 0
/* if Type1 access, must reset IOC CFG so normal IO space ops work */
if (type1) {
*((unsigned int *)T2_IOC_CFG) = t2_cfg & ~1;
mb();
}
#endif
DBG(("conf_write(): finished\n"));
restore_flags(flags);
}
 
 
int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char *value)
{
unsigned long addr = T2_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xff;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
 
addr |= (pci_addr << 5) + 0x00;
 
*value = conf_read(addr, type1) >> ((where & 3) * 8);
 
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short *value)
{
unsigned long addr = T2_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xffff;
 
if (where & 0x1) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
return PCIBIOS_SUCCESSFUL;
}
 
addr |= (pci_addr << 5) + 0x08;
 
*value = conf_read(addr, type1) >> ((where & 3) * 8);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int *value)
{
unsigned long addr = T2_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xffffffff;
if (where & 0x3) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
*value = conf_read(addr, type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char value)
{
unsigned long addr = T2_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x00;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short value)
{
unsigned long addr = T2_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x08;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int value)
{
unsigned long addr = T2_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
unsigned long t2_init(unsigned long mem_start, unsigned long mem_end)
{
unsigned int t2_err;
struct percpu_struct *cpu;
int i;
 
#if 0
/*
* Set up error reporting.
*/
t2_err = *(vuip)T2_IOCSR ;
t2_err |= (0x1 << 7) ; /* master abort */
*(vuip)T2_IOC_T2_ERR = t2_err ;
mb() ;
#endif
 
printk("t2_init: HBASE was 0x%lx\n", *((unsigned long *)T2_HBASE));
#if 0
printk("t2_init: WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n",
*((vulp)T2_WBASE1),
*((vulp)T2_WMASK1),
*((vulp)T2_TBASE1));
printk("t2_init: WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n",
*((vulp)T2_WBASE2),
*((vulp)T2_WMASK2),
*((vulp)T2_TBASE2));
#endif
 
#ifdef CONFIG_ALPHA_SRM_SETUP
/* check window 1 for enabled and mapped to 0 */
if (((*(vulp)T2_WBASE1 & (3UL<<18)) == (2UL<<18)) &&
(*(vuip)T2_TBASE1 == 0))
{
T2_DMA_WIN_BASE = *(vulp)T2_WBASE1 & 0xfff00000UL;
T2_DMA_WIN_SIZE = *(vulp)T2_WMASK1 & 0xfff00000UL;
T2_DMA_WIN_SIZE += 0x00100000UL;
/* DISABLE window 2!! ?? */
#if 1
printk("t2_init: using Window 1 settings\n");
printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
*(vulp)T2_WBASE1,
*(vulp)T2_WMASK1,
*(vulp)T2_TBASE1);
#endif
}
else /* check window 2 for enabled and mapped to 0 */
if (((*(vulp)T2_WBASE2 & (3UL<<18)) == (2UL<<18)) &&
(*(vuip)T2_TBASE2 == 0))
{
T2_DMA_WIN_BASE = *(vulp)T2_WBASE2 & 0xfff00000UL;
T2_DMA_WIN_SIZE = *(vulp)T2_WMASK2 & 0xfff00000UL;
T2_DMA_WIN_SIZE += 0x00100000UL;
/* DISABLE window 1!! ?? */
#if 1
printk("t2_init: using Window 2 settings\n");
printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
*(vulp)T2_WBASE2,
*(vulp)T2_WMASK2,
*(vulp)T2_TBASE2);
#endif
}
else /* we must use our defaults... */
#endif /* SRM_SETUP */
{
/*
* Set up the PCI->physical memory translation windows.
* For now, window 2 is disabled. In the future, we may
* want to use it to do scatter/gather DMA. Window 1
* goes at 1 GB and is 1 GB large.
*/
 
/* WARNING!! must correspond to the DMA_WIN params!!! */
*(vuip)T2_WBASE1 = 0x400807ffU;
*(vuip)T2_WMASK1 = 0x3ff00000U;
*(vuip)T2_TBASE1 = 0;
 
*(vuip)T2_WBASE2 = 0x0;
*(vuip)T2_HBASE = 0x0;
}
 
 
/*
* check ASN in HWRPB for validity, report if bad
*/
if (hwrpb->max_asn != MAX_ASN) {
printk("T2_init: max ASN from HWRPB is bad (0x%lx)\n",
hwrpb->max_asn);
hwrpb->max_asn = MAX_ASN;
}
 
/*
* Finally, clear the T2_HAE_3 register, which gets used
* for PCI Config Space accesses. That is the way
* we want to use it, and we do not want to depend on
* what ARC or SRM might have left behind...
*/
{
unsigned long t2_hae_1 = *((unsigned long *)T2_HAE_1);
unsigned long t2_hae_2 = *((unsigned long *)T2_HAE_2);
unsigned long t2_hae_3 = *((unsigned long *)T2_HAE_3);
unsigned long t2_hae_4 = *((unsigned long *)T2_HAE_4);
#if 1
printk("T2_init: HAE1 was 0x%lx\n", t2_hae_1);
printk("T2_init: HAE2 was 0x%lx\n", t2_hae_2);
printk("T2_init: HAE3 was 0x%lx\n", t2_hae_3);
printk("T2_init: HAE4 was 0x%lx\n", t2_hae_4);
#endif
#ifdef CONFIG_ALPHA_SRM_SETUP
/*
sigh... For the SRM setup, unless we know apriori what the HAE
contents will be, we need to setup the arbitrary region bases
so we can test against the range of addresses and tailor the
region chosen for the SPARSE memory access.
 
see include/asm-alpha/t2.h for the SPARSE mem read/write
*/
t2_sm_base = (t2_hae_1 << 27) & 0xf8000000UL;
#else /* SRM_SETUP */
*((unsigned int *)T2_HAE_1) = 0; mb();
*((unsigned int *)T2_HAE_2) = 0; mb();
*((unsigned int *)T2_HAE_3) = 0; mb();
#if 0
*((unsigned int *)T2_HAE_4) = 0; mb();
#endif
#endif /* SRM_SETUP */
}
#if 1
if (hwrpb->nr_processors > 1) {
printk("T2_init: nr_processors 0x%lx\n",
hwrpb->nr_processors);
printk("T2_init: processor_size 0x%lx\n",
hwrpb->processor_size);
printk("T2_init: processor_offset 0x%lx\n",
hwrpb->processor_offset);
 
cpu = (struct percpu_struct *)
((char*)hwrpb + hwrpb->processor_offset);
 
for (i = 0; i < hwrpb->nr_processors; i++ ) {
printk("T2_init: CPU 0x%x: flags 0x%lx type 0x%lx\n",
i, cpu->flags, cpu->type);
cpu = (struct percpu_struct *)
((char *)cpu + hwrpb->processor_size);
}
}
#endif
 
return mem_start;
}
 
#define SIC_SEIC (1UL << 33) /* System Event Clear */
 
struct sable_cpu_csr *sable_cpu_regs[4] = {
(struct sable_cpu_csr *)CPU0_BASE,
(struct sable_cpu_csr *)CPU1_BASE,
(struct sable_cpu_csr *)CPU2_BASE,
(struct sable_cpu_csr *)CPU3_BASE,
};
int t2_clear_errors(void)
{
DBGMC(("???????? t2_clear_errors\n"));
 
sable_cpu_regs[CPUID]->sic &= ~SIC_SEIC;
 
/*
* clear cpu errors
*/
sable_cpu_regs[CPUID]->bcce |= sable_cpu_regs[CPUID]->bcce;
sable_cpu_regs[CPUID]->cbe |= sable_cpu_regs[CPUID]->cbe;
sable_cpu_regs[CPUID]->bcue |= sable_cpu_regs[CPUID]->bcue;
sable_cpu_regs[CPUID]->dter |= sable_cpu_regs[CPUID]->dter;
 
*(unsigned long *)T2_CERR1 |= *(unsigned long *)T2_CERR1;
*(unsigned long *)T2_PERR1 |= *(unsigned long *)T2_PERR1;
 
mb();
mb();
return 0;
}
 
void t2_machine_check(unsigned long vector, unsigned long la_ptr,
struct pt_regs * regs)
{
struct el_t2_logout_header *mchk_header;
struct el_t2_procdata_mcheck *mchk_procdata;
struct el_t2_sysdata_mcheck *mchk_sysdata;
unsigned long * ptr;
const char * reason;
char buf[128];
long i;
 
DBGMC(("t2_machine_check: vector=0x%lx la_ptr=0x%lx\n",
vector, la_ptr));
 
mchk_header = (struct el_t2_logout_header *)la_ptr;
 
DBGMC(("t2_machine_check: susoffset=0x%lx procoffset=0x%lx\n",
mchk_header->elfl_sysoffset, mchk_header->elfl_procoffset));
 
mchk_sysdata = (struct el_t2_sysdata_mcheck *)
(la_ptr + mchk_header->elfl_sysoffset);
mchk_procdata = (struct el_t2_procdata_mcheck *)
(la_ptr + mchk_header->elfl_procoffset - sizeof(unsigned long)*32);
 
DBGMC((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
regs->pc, mchk_header->elfl_size, mchk_header->elfl_procoffset,
mchk_header->elfl_sysoffset));
DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected));
#ifdef DEBUG_DUMP
{
unsigned long *ptr;
int i;
 
ptr = (unsigned long *)la_ptr;
for (i = 0; i < mchk_header->elfl_size / sizeof(long); i += 2) {
printk(" +%lx %lx %lx\n", i*sizeof(long),
ptr[i], ptr[i+1]);
}
}
#endif /* DEBUG_DUMP */
/*
* Check if machine check is due to a badaddr() and if so,
* ignore the machine check.
*/
mb();
mb();
if (T2_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
DBGMC(("T2 machine check expected\n"));
T2_mcheck_taken = 1;
t2_clear_errors();
T2_mcheck_expected = 0;
mb();
mb();
wrmces(rdmces()|1);/* ??? */
draina();
return;
}
 
switch ((unsigned int) mchk_header->elfl_error_type) {
case MCHK_K_TPERR: reason = "tag parity error"; break;
case MCHK_K_TCPERR: reason = "tag control parity error"; break;
case MCHK_K_HERR: reason = "generic hard error"; break;
case MCHK_K_ECC_C: reason = "correctable ECC error"; break;
case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break;
case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break;
case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break;
case 0x96: reason = "i-cache read retryable error"; break;
case 0x98: reason = "processor detected hard error"; break;
 
/* system specific (these are for Alcor, at least): */
case 0x203: reason = "system detected uncorrectable ECC error"; break;
case 0x205: reason = "parity error detected by T2"; break;
case 0x207: reason = "non-existent memory error"; break;
case 0x209: reason = "PCI SERR detected"; break;
case 0x20b: reason = "PCI data parity error detected"; break;
case 0x20d: reason = "PCI address parity error detected"; break;
case 0x20f: reason = "PCI master abort error"; break;
case 0x211: reason = "PCI target abort error"; break;
case 0x213: reason = "scatter/gather PTE invalid error"; break;
case 0x215: reason = "flash ROM write error"; break;
case 0x217: reason = "IOA timeout detected"; break;
case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break;
case 0x21b: reason = "EISA fail-safe timer timeout"; break;
case 0x21d: reason = "EISA bus time-out"; break;
case 0x21f: reason = "EISA software generated NMI"; break;
case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break;
default:
sprintf(buf, "reason for machine-check unknown (0x%x)",
(unsigned int) mchk_header->elfl_error_type);
reason = buf;
break;
}
wrmces(rdmces()|1); /* reset machine check pending flag */
mb();
 
printk(KERN_CRIT " T2 machine check: %s%s\n",
reason, mchk_header->elfl_retry ? " (retryable)" : "");
 
/* dump the logout area to give all info: */
 
ptr = (unsigned long *)la_ptr;
for (i = 0; i < mchk_header->elfl_size / sizeof(long); i += 2) {
printk(KERN_CRIT " +%8lx %016lx %016lx\n",
i*sizeof(long), ptr[i], ptr[i+1]);
}
}
 
#endif /* CONFIG_ALPHA_T2 */
/kernel/osf_sys.c
0,0 → 1,782
/*
* linux/arch/alpha/kernel/osf_sys.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
/*
* This file handles some of the stranger OSF/1 system call interfaces.
* Some of the system calls expect a non-C calling standard, others have
* special parameter blocks..
*/
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/ldt.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/utsname.h>
#include <linux/time.h>
#include <linux/major.h>
#include <linux/stat.h>
#include <linux/mman.h>
#include <linux/shm.h>
 
#include <asm/fpu.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
 
extern int do_mount(kdev_t, const char *, const char *, char *, int, void *);
extern int do_pipe(int *);
 
extern struct file_operations * get_blkfops(unsigned int);
extern struct file_operations * get_chrfops(unsigned int);
 
extern kdev_t get_unnamed_dev(void);
extern void put_unnamed_dev(kdev_t);
 
extern asmlinkage int sys_umount(char *);
extern asmlinkage int sys_swapon(const char *specialfile, int swap_flags);
 
/*
* OSF/1 directory handling functions...
*
* The "getdents()" interface is much more sane: the "basep" stuff is
* braindamage (it can't really handle filesystems where the directory
* offset differences aren't the same as "d_reclen").
*/
#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
#define ROUND_UP(x) (((x)+3) & ~3)
 
struct osf_dirent {
unsigned int d_ino;
unsigned short d_reclen;
unsigned short d_namlen;
char d_name[1];
};
 
struct osf_dirent_callback {
struct osf_dirent * dirent;
long *basep;
int count;
int error;
};
 
static int osf_filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
{
struct osf_dirent * dirent;
struct osf_dirent_callback * buf = (struct osf_dirent_callback *) __buf;
int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
 
buf->error = -EINVAL; /* only used if we fail */
if (reclen > buf->count)
return -EINVAL;
if (buf->basep) {
put_user(offset, buf->basep);
buf->basep = NULL;
}
dirent = buf->dirent;
put_user(ino, &dirent->d_ino);
put_user(namlen, &dirent->d_namlen);
put_user(reclen, &dirent->d_reclen);
memcpy_tofs(dirent->d_name, name, namlen);
put_fs_byte(0, dirent->d_name + namlen);
((char *) dirent) += reclen;
buf->dirent = dirent;
buf->count -= reclen;
return 0;
}
 
asmlinkage int osf_getdirentries(unsigned int fd, struct osf_dirent * dirent,
unsigned int count, long *basep)
{
int error;
struct file * file;
struct osf_dirent_callback buf;
 
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
return -EBADF;
if (!file->f_op || !file->f_op->readdir)
return -ENOTDIR;
error = verify_area(VERIFY_WRITE, dirent, count);
if (error)
return error;
if (basep) {
error = verify_area(VERIFY_WRITE, basep, sizeof(long));
if (error)
return error;
}
buf.dirent = dirent;
buf.basep = basep;
buf.count = count;
buf.error = 0;
error = file->f_op->readdir(file->f_inode, file, &buf, osf_filldir);
if (error < 0)
return error;
if (count == buf.count)
return buf.error;
return count - buf.count;
}
 
/*
* Alpha syscall convention has no problem returning negative
* values:
*/
asmlinkage int osf_getpriority(int which, int who, int a2, int a3, int a4,
int a5, struct pt_regs regs)
{
extern int sys_getpriority(int, int);
int prio;
 
prio = sys_getpriority(which, who);
if (prio < 0)
return prio;
 
regs.r0 = 0; /* special return: no errors */
return 20 - prio;
}
 
 
/*
* Heh. As documented by DEC..
*/
asmlinkage unsigned long sys_madvise(void)
{
return 0;
}
 
asmlinkage unsigned long sys_getxuid(int a0, int a1, int a2, int a3, int a4, int a5,
struct pt_regs regs)
{
(&regs)->r20 = current->euid;
return current->uid;
}
 
asmlinkage unsigned long sys_getxgid(int a0, int a1, int a2, int a3, int a4, int a5,
struct pt_regs regs)
{
(&regs)->r20 = current->egid;
return current->gid;
}
 
asmlinkage unsigned long sys_getxpid(int a0, int a1, int a2, int a3, int a4, int a5,
struct pt_regs regs)
{
(&regs)->r20 = current->p_opptr->pid;
return current->pid;
}
 
asmlinkage unsigned long osf_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long fd,
unsigned long off)
{
struct file * file = NULL;
 
if (flags & (MAP_HASSEMAPHORE | MAP_INHERIT | MAP_UNALIGNED))
printk("%s: unimplemented OSF mmap flags %04lx\n", current->comm, flags);
if (!(flags & MAP_ANONYMOUS)) {
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
return -EBADF;
}
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
return do_mmap(file, addr, len, prot, flags, off);
}
 
 
/*
* The OSF/1 statfs structure is much larger, but this should
* match the beginning, at least.
*/
struct osf_statfs {
short f_type;
short f_flags;
int f_fsize;
int f_bsize;
int f_blocks;
int f_bfree;
int f_bavail;
int f_files;
int f_ffree;
__kernel_fsid_t f_fsid;
} * osf_stat;
 
static void linux_to_osf_statfs (struct statfs * linux_stat, struct osf_statfs * osf_stat)
{
osf_stat->f_type = linux_stat->f_type;
osf_stat->f_flags = 0; /* mount flags */
/* Linux doesn't provide a "fundamental filesystem block size": */
osf_stat->f_fsize = linux_stat->f_bsize;
osf_stat->f_bsize = linux_stat->f_bsize;
osf_stat->f_blocks = linux_stat->f_blocks;
osf_stat->f_bfree = linux_stat->f_bfree;
osf_stat->f_bavail = linux_stat->f_bavail;
osf_stat->f_files = linux_stat->f_files;
osf_stat->f_ffree = linux_stat->f_ffree;
osf_stat->f_fsid = linux_stat->f_fsid;
}
 
 
asmlinkage int osf_statfs(char * path, struct osf_statfs * buffer, unsigned long bufsiz)
{
struct statfs linux_stat;
struct inode * inode;
int retval;
 
if (bufsiz > sizeof(struct osf_statfs))
bufsiz = sizeof(struct osf_statfs);
retval = verify_area(VERIFY_WRITE, buffer, bufsiz);
if (retval)
return retval;
retval = namei(path, &inode);
if (retval)
return retval;
if (!inode->i_sb->s_op->statfs) {
iput(inode);
return -ENOSYS;
}
inode->i_sb->s_op->statfs(inode->i_sb, &linux_stat, sizeof(linux_stat));
linux_to_osf_statfs(&linux_stat, buffer);
iput(inode);
return 0;
}
 
asmlinkage int osf_fstatfs(unsigned long fd, struct osf_statfs * buffer, unsigned long bufsiz)
{
struct statfs linux_stat;
struct file * file;
struct inode * inode;
int retval;
 
retval = verify_area(VERIFY_WRITE, buffer, bufsiz);
if (retval)
return retval;
if (bufsiz > sizeof(struct osf_statfs))
bufsiz = sizeof(struct osf_statfs);
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
return -EBADF;
if (!(inode = file->f_inode))
return -ENOENT;
if (!inode->i_sb->s_op->statfs)
return -ENOSYS;
inode->i_sb->s_op->statfs(inode->i_sb, &linux_stat, sizeof(linux_stat));
linux_to_osf_statfs(&linux_stat, buffer);
return 0;
}
 
/*
* Uhh.. OSF/1 mount parameters aren't exactly obvious..
*
* Although to be frank, neither are the native Linux/i386 ones..
*/
struct ufs_args {
char * devname;
int flags;
uid_t exroot;
};
 
struct cdfs_args {
char * devname;
int flags;
uid_t exroot;
/*
* this has lots more here, which linux handles with the option block
* but I'm too lazy to do the translation into ascii..
*/
};
 
struct procfs_args {
char * devname;
int flags;
uid_t exroot;
};
 
static int getdev(const char * name, int rdonly, struct inode ** ino)
{
kdev_t dev;
struct inode * inode;
struct file_operations * fops;
int retval;
 
retval = namei(name, &inode);
if (retval)
return retval;
if (!S_ISBLK(inode->i_mode)) {
iput(inode);
return -ENOTBLK;
}
if (IS_NODEV(inode)) {
iput(inode);
return -EACCES;
}
dev = inode->i_rdev;
if (MAJOR(dev) >= MAX_BLKDEV) {
iput(inode);
return -ENXIO;
}
fops = get_blkfops(MAJOR(dev));
if (!fops) {
iput(inode);
return -ENODEV;
}
if (fops->open) {
struct file dummy;
memset(&dummy, 0, sizeof(dummy));
dummy.f_inode = inode;
dummy.f_mode = rdonly ? 1 : 3;
retval = fops->open(inode, &dummy);
if (retval) {
iput(inode);
return retval;
}
}
*ino = inode;
return 0;
}
 
static void putdev(struct inode * inode)
{
struct file_operations * fops;
 
fops = get_blkfops(MAJOR(inode->i_rdev));
if (fops->release)
fops->release(inode, NULL);
}
 
/*
* We can't actually handle ufs yet, so we translate UFS mounts to
* ext2fs mounts... I wouldn't mind a UFS filesystem, but the UFS
* layout is so braindead it's a major headache doing it..
*/
static int osf_ufs_mount(char * dirname, struct ufs_args * args, int flags)
{
int retval;
struct inode * inode;
struct cdfs_args tmp;
 
retval = verify_area(VERIFY_READ, args, sizeof(*args));
if (retval)
return retval;
memcpy_fromfs(&tmp, args, sizeof(tmp));
retval = getdev(tmp.devname, 0, &inode);
if (retval)
return retval;
retval = do_mount(inode->i_rdev, tmp.devname, dirname, "ext2", flags, NULL);
if (retval)
putdev(inode);
iput(inode);
return retval;
}
 
static int osf_cdfs_mount(char * dirname, struct cdfs_args * args, int flags)
{
int retval;
struct inode * inode;
struct cdfs_args tmp;
 
retval = verify_area(VERIFY_READ, args, sizeof(*args));
if (retval)
return retval;
memcpy_fromfs(&tmp, args, sizeof(tmp));
retval = getdev(tmp.devname, 1, &inode);
if (retval)
return retval;
retval = do_mount(inode->i_rdev, tmp.devname, dirname, "iso9660", flags, NULL);
if (retval)
putdev(inode);
iput(inode);
return retval;
}
 
static int osf_procfs_mount(char * dirname, struct procfs_args * args, int flags)
{
kdev_t dev;
int retval;
struct procfs_args tmp;
 
retval = verify_area(VERIFY_READ, args, sizeof(*args));
if (retval)
return retval;
memcpy_fromfs(&tmp, args, sizeof(tmp));
dev = get_unnamed_dev();
if (!dev)
return -ENODEV;
retval = do_mount(dev, "", dirname, "proc", flags, NULL);
if (retval)
put_unnamed_dev(dev);
return retval;
}
 
asmlinkage int osf_mount(unsigned long typenr, char * path, int flag, void * data)
{
int retval;
 
retval = -EINVAL;
switch (typenr) {
case 1:
retval = osf_ufs_mount(path, (struct ufs_args *) data, flag);
break;
case 6:
retval = osf_cdfs_mount(path, (struct cdfs_args *) data, flag);
break;
case 9:
retval = osf_procfs_mount(path, (struct procfs_args *) data, flag);
break;
default:
printk("osf_mount(%ld, %x)\n", typenr, flag);
}
return retval;
}
 
asmlinkage int osf_umount(char * path, int flag)
{
return sys_umount(path);
}
 
/*
* I don't know what the parameters are: the first one
* seems to be a timeval pointer, and I suspect the second
* one is the time remaining.. Ho humm.. No documentation.
*/
asmlinkage int osf_usleep_thread(struct timeval * sleep, struct timeval * remain)
{
struct timeval tmp;
unsigned long ticks;
int retval;
 
retval = verify_area(VERIFY_READ, sleep, sizeof(*sleep));
if (retval)
return retval;
if (remain && (retval = verify_area(VERIFY_WRITE, remain, sizeof(*remain))))
return retval;
memcpy_fromfs(&tmp, sleep, sizeof(*sleep));
ticks = tmp.tv_usec;
ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ);
ticks += tmp.tv_sec * HZ;
current->timeout = ticks + jiffies;
current->state = TASK_INTERRUPTIBLE;
schedule();
if (!remain)
return 0;
ticks = jiffies;
if (ticks < current->timeout)
ticks = current->timeout - ticks;
else
ticks = 0;
current->timeout = 0;
tmp.tv_sec = ticks / HZ;
tmp.tv_usec = ticks % HZ;
memcpy_tofs(remain, &tmp, sizeof(*remain));
return 0;
}
 
asmlinkage int osf_utsname(char * name)
{
int error = verify_area(VERIFY_WRITE, name, 5*32);
if (error)
return error;
memcpy_tofs(name + 0, system_utsname.sysname, 32);
memcpy_tofs(name + 32, system_utsname.nodename, 32);
memcpy_tofs(name + 64, system_utsname.release, 32);
memcpy_tofs(name + 96, system_utsname.version, 32);
memcpy_tofs(name + 128, system_utsname.machine, 32);
return 0;
}
 
asmlinkage int osf_swapon(const char * path, int flags, int lowat, int hiwat)
{
/* for now, simply ignore lowat and hiwat... */
return sys_swapon(path, flags);
}
 
asmlinkage unsigned long sys_getpagesize(void)
{
return PAGE_SIZE;
}
 
asmlinkage unsigned long sys_getdtablesize(void)
{
return NR_OPEN;
}
 
asmlinkage int sys_pipe(int a0, int a1, int a2, int a3, int a4, int a5,
struct pt_regs regs)
{
int fd[2];
int error;
 
error = do_pipe(fd);
if (error)
return error;
(&regs)->r20 = fd[1];
return fd[0];
}
 
/*
* For compatibility with OSF/1 only. Use utsname(2) instead.
*/
asmlinkage int osf_getdomainname(char *name, int namelen)
{
unsigned len;
int i, error;
 
error = verify_area(VERIFY_WRITE, name, namelen);
if (error)
return error;
 
len = namelen;
if (namelen > 32)
len = 32;
 
for (i = 0; i < len; ++i) {
put_user(system_utsname.domainname[i], name + i);
if (system_utsname.domainname[i] == '\0')
break;
}
return 0;
}
 
 
asmlinkage long osf_shmat(int shmid, void *shmaddr, int shmflg)
{
unsigned long raddr;
int err;
 
err = sys_shmat(shmid, shmaddr, shmflg, &raddr);
if (err)
return err;
/*
* This works because all user-level addresses are
* non-negative longs!
*/
return raddr;
}
 
 
/*
* The following stuff should move into a header file should it ever
* be labeled "officially supported." Right now, there is just enough
* support to avoid applications (such as tar) printing error
* messages. The attributes are not really implemented.
*/
 
/*
* Values for Property list entry flag
*/
#define PLE_PROPAGATE_ON_COPY 0x1 /* cp(1) will copy entry
by default */
#define PLE_FLAG_MASK 0x1 /* Valid flag values */
#define PLE_FLAG_ALL -1 /* All flag value */
 
struct proplistname_args {
unsigned int pl_mask;
unsigned int pl_numnames;
char **pl_names;
};
 
union pl_args {
struct setargs {
char *path;
long follow;
long nbytes;
char *buf;
} set;
struct fsetargs {
long fd;
long nbytes;
char *buf;
} fset;
struct getargs {
char *path;
long follow;
struct proplistname_args *name_args;
long nbytes;
char *buf;
int *min_buf_size;
} get;
struct fgetargs {
long fd;
struct proplistname_args *name_args;
long nbytes;
char *buf;
int *min_buf_size;
} fget;
struct delargs {
char *path;
long follow;
struct proplistname_args *name_args;
} del;
struct fdelargs {
long fd;
struct proplistname_args *name_args;
} fdel;
};
 
enum pl_code {
PL_SET = 1, PL_FSET = 2,
PL_GET = 3, PL_FGET = 4,
PL_DEL = 5, PL_FDEL = 6
};
 
asmlinkage long osf_proplist_syscall (enum pl_code code, union pl_args *args)
{
long error;
int *min_buf_size_ptr;
 
switch (code) {
case PL_SET:
error = verify_area(VERIFY_READ, &args->set.nbytes,
sizeof(args->set.nbytes));
if (error)
return error;
return args->set.nbytes;
 
case PL_FSET:
error = verify_area(VERIFY_READ, &args->fset.nbytes,
sizeof(args->fset.nbytes));
if (error)
return error;
return args->fset.nbytes;
 
case PL_GET:
error = verify_area(VERIFY_READ, &args->get.min_buf_size,
sizeof(args->get.min_buf_size));
if (error)
return error;
min_buf_size_ptr = get_user(&args->get.min_buf_size);
error = verify_area(VERIFY_WRITE, min_buf_size_ptr,
sizeof(*min_buf_size_ptr));
if (error)
return error;
put_user(0, min_buf_size_ptr);
return 0;
 
case PL_FGET:
error = verify_area(VERIFY_READ, &args->fget.min_buf_size,
sizeof(args->fget.min_buf_size));
if (error)
return error;
min_buf_size_ptr = get_user(&args->fget.min_buf_size);
error = verify_area(VERIFY_WRITE, min_buf_size_ptr,
sizeof(*min_buf_size_ptr));
if (error)
return error;
put_user(0, min_buf_size_ptr);
return 0;
 
case PL_DEL:
case PL_FDEL:
return 0;
 
default:
return -EOPNOTSUPP;
}
}
 
/*
* The Linux kernel isn't good at returning values that look
* like negative longs (they are mistaken as error values).
* Until that is fixed, we need this little workaround for
* create_module() because it's one of the few system calls
* that return kernel addresses (which are negative).
*/
asmlinkage unsigned long
alpha_create_module (char * module_name, unsigned long size,
int a3, int a4, int a5, int a6,
struct pt_regs regs)
{
asmlinkage unsigned long sys_create_module (char *, unsigned long);
long retval;
 
retval = sys_create_module(module_name, size);
/*
* we get either a module address or an error number,
* and we know the error number is a small negative
* number, while the address is always negative but
* much larger.
*/
if (retval + 1000 > 0)
return retval;
 
/* tell entry.S:syscall_error that this is NOT an error: */
regs.r0 = 0;
return retval;
}
 
 
asmlinkage unsigned long
osf_getsysinfo (unsigned long op, void * buffer, unsigned long nbytes,
int * start, void *arg)
{
switch (op) {
case 45: /* GSI_IEEE_FP_CONTROL */
/* Return current sw control & status bits. */
put_user(current->tss.flags & IEEE_SW_MASK,
(unsigned long *)buffer);
return 0;
 
case 46: /* GSI_IEEE_STATE_AT_SIGNAL */
/*
* Not sure anybody will ever use this weird stuff. These
* ops can be used (under OSF/1) to set the fpcr that should
* be used when a signal handler starts executing.
*/
break;
 
default:
break;
}
return -EOPNOTSUPP;
}
 
 
asmlinkage unsigned long
osf_setsysinfo (unsigned long op, void * buffer, unsigned long nbytes,
int * start, void *arg)
{
switch (op) {
case 14: { /* SSI_IEEE_FP_CONTROL */
unsigned long sw, fpcw;
 
/*
* Alpha Architecture Handbook 4.7.7.3:
* To be fully IEEE compiant, we must track the current IEEE
* exception state in software, because spurrious bits can be
* set in the trap shadow of a software-complete insn.
*/
 
/* Update software trap enable bits. */
sw = get_user((unsigned long *) buffer) & IEEE_SW_MASK;
current->tss.flags &= ~IEEE_SW_MASK;
current->tss.flags |= sw & IEEE_SW_MASK;
 
/* Update the real fpcr. For exceptions that are disabled,
but that we have seen, turn off exceptions in h/w.
Otherwise leave them enabled so that we can update our
software status mask. */
fpcw = rdfpcr() & (~FPCR_MASK | FPCR_DYN_MASK);
fpcw |= ieee_sw_to_fpcr(sw | ((~sw & IEEE_STATUS_MASK) >> 16));
wrfpcr(fpcw);
return 0;
}
 
case 15: /* SSI_IEEE_STATE_AT_SIGNAL */
case 16: /* SSI_IEEE_IGNORE_STATE_AT_SIGNAL */
/*
* Not sure anybody will ever use this weird stuff. These
* ops can be used (under OSF/1) to set the fpcr that should
* be used when a signal handler starts executing.
*/
break;
 
default:
break;
}
return -EOPNOTSUPP;
}
/kernel/traps.c
0,0 → 1,451
/*
* kernel/traps.c
*
* (C) Copyright 1994 Linus Torvalds
*/
 
/*
* This file initializes the trap entry points
*/
 
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/tty.h>
 
#include <asm/gentrap.h>
#include <asm/segment.h>
#include <asm/unaligned.h>
 
void die_if_kernel(char * str, struct pt_regs * regs, long err)
{
long i;
unsigned long sp;
unsigned int * pc;
 
if (regs->ps & 8)
return;
printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err);
sp = (unsigned long) (regs+1);
printk("pc = [<%lx>] ps = %04lx\n", regs->pc, regs->ps);
printk("rp = [<%lx>] sp = %lx\n", regs->r26, sp);
printk("r0=%lx r1=%lx r2=%lx r3=%lx\n",
regs->r0, regs->r1, regs->r2, regs->r3);
printk("r8=%lx\n", regs->r8);
printk("r16=%lx r17=%lx r18=%lx r19=%lx\n",
regs->r16, regs->r17, regs->r18, regs->r19);
printk("r20=%lx r21=%lx r22=%lx r23=%lx\n",
regs->r20, regs->r21, regs->r22, regs->r23);
printk("r24=%lx r25=%lx r26=%lx r27=%lx\n",
regs->r24, regs->r25, regs->r26, regs->r27);
printk("r28=%lx r29=%lx r30=%lx\n",
regs->r28, regs->gp, sp);
printk("Code:");
pc = (unsigned int *) regs->pc;
for (i = -3; i < 6; i++)
printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>');
printk("\n");
do_exit(SIGSEGV);
}
 
asmlinkage void do_entArith(unsigned long summary, unsigned long write_mask,
unsigned long a2, unsigned long a3,
unsigned long a4, unsigned long a5,
struct pt_regs regs)
{
if ((summary & 1)) {
extern long alpha_fp_emul_imprecise (struct pt_regs * regs,
unsigned long write_mask);
/*
* Software-completion summary bit is set, so try to
* emulate the instruction.
*/
if (alpha_fp_emul_imprecise(&regs, write_mask)) {
return; /* emulation was successful */
}
}
printk("%s: arithmetic trap at %016lx: %02lx %016lx\n",
current->comm, regs.pc, summary, write_mask);
die_if_kernel("Arithmetic fault", &regs, 0);
force_sig(SIGFPE, current);
}
 
asmlinkage void do_entIF(unsigned long type, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
extern int ptrace_cancel_bpt (struct task_struct *who);
 
die_if_kernel("Instruction fault", &regs, type);
switch (type) {
case 0: /* breakpoint */
if (ptrace_cancel_bpt(current)) {
regs.pc -= 4; /* make pc point to former bpt */
}
force_sig(SIGTRAP, current);
break;
 
case 2: /* gentrap */
/*
* The exception code should be passed on to the signal
* handler as the second argument. Linux doesn't do that
* yet (also notice that Linux *always* behaves like
* DEC Unix with SA_SIGINFO off; see DEC Unix man page
* for sigaction(2)).
*/
switch ((long) regs.r16) {
case GEN_INTOVF: case GEN_INTDIV: case GEN_FLTOVF:
case GEN_FLTDIV: case GEN_FLTUND: case GEN_FLTINV:
case GEN_FLTINE:
force_sig(SIGFPE, current);
break;
 
case GEN_DECOVF:
case GEN_DECDIV:
case GEN_DECINV:
case GEN_ROPRAND:
case GEN_ASSERTERR:
case GEN_NULPTRERR:
case GEN_STKOVF:
case GEN_STRLENERR:
case GEN_SUBSTRERR:
case GEN_RANGERR:
case GEN_SUBRNG:
case GEN_SUBRNG1:
case GEN_SUBRNG2:
case GEN_SUBRNG3:
case GEN_SUBRNG4:
case GEN_SUBRNG5:
case GEN_SUBRNG6:
case GEN_SUBRNG7:
force_sig(SIGILL, current);
break;
}
break;
 
case 1: /* bugcheck */
case 3: /* FEN fault */
force_sig(SIGILL, current);
break;
 
case 4: /* opDEC */
#ifdef CONFIG_ALPHA_NEED_ROUNDING_EMULATION
{
extern long alpha_fp_emul (unsigned long pc);
unsigned int opcode;
 
/* get opcode of faulting instruction: */
opcode = get_user((__u32*)(regs.pc - 4)) >> 26;
if (opcode == 0x16) {
/*
* It's a FLTI instruction, emulate it
* (we don't do no stinkin' VAX fp...)
*/
if (!alpha_fp_emul(regs.pc - 4))
force_sig(SIGFPE, current);
break;
}
}
#endif
force_sig(SIGILL, current);
break;
 
default:
panic("do_entIF: unexpected instruction-fault type");
}
}
 
/*
* entUna has a different register layout to be reasonably simple. It
* needs access to all the integer registers (the kernel doesn't use
* fp-regs), and it needs to have them in order for simpler access.
*
* Due to the non-standard register layout (and because we don't want
* to handle floating-point regs), user-mode unaligned accesses are
* handled separately by do_entUnaUser below.
*
* Oh, btw, we don't handle the "gp" register correctly, but if we fault
* on a gp-register unaligned load/store, something is _very_ wrong
* in the kernel anyway..
*/
struct allregs {
unsigned long regs[32];
unsigned long ps, pc, gp, a0, a1, a2;
};
 
struct unaligned_stat {
unsigned long count, va, pc;
} unaligned[2];
 
asmlinkage void do_entUna(void * va, unsigned long opcode, unsigned long reg,
unsigned long a3, unsigned long a4, unsigned long a5,
struct allregs regs)
{
static int cnt = 0;
static long last_time = 0;
 
if (cnt >= 5 && jiffies - last_time > 5*HZ) {
cnt = 0;
}
if (++cnt < 5) {
printk("kernel: unaligned trap at %016lx: %p %lx %ld\n",
regs.pc - 4, va, opcode, reg);
}
last_time = jiffies;
 
++unaligned[0].count;
unaligned[0].va = (unsigned long) va - 4;
unaligned[0].pc = regs.pc;
 
/* $16-$18 are PAL-saved, and are offset by 19 entries */
if (reg >= 16 && reg <= 18)
reg += 19;
switch (opcode) {
case 0x28: /* ldl */
*(reg+regs.regs) = get_unaligned((int *)va);
return;
case 0x29: /* ldq */
*(reg+regs.regs) = get_unaligned((long *)va);
return;
case 0x2c: /* stl */
put_unaligned(*(reg+regs.regs), (int *)va);
return;
case 0x2d: /* stq */
put_unaligned(*(reg+regs.regs), (long *)va);
return;
}
printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n",
regs.pc, va, opcode, reg);
do_exit(SIGSEGV);
}
 
/*
* Convert an s-floating point value in memory format to the
* corresponding value in register format. The exponent
* needs to be remapped to preserve non-finite values
* (infinities, not-a-numbers, denormals).
*/
static inline unsigned long s_mem_to_reg (unsigned long s_mem)
{
unsigned long frac = (s_mem >> 0) & 0x7fffff;
unsigned long sign = (s_mem >> 31) & 0x1;
unsigned long exp_msb = (s_mem >> 30) & 0x1;
unsigned long exp_low = (s_mem >> 23) & 0x7f;
unsigned long exp;
 
exp = (exp_msb << 10) | exp_low; /* common case */
if (exp_msb) {
if (exp_low == 0x7f) {
exp = 0x7ff;
}
} else {
if (exp_low == 0x00) {
exp = 0x000;
} else {
exp |= (0x7 << 7);
}
}
return (sign << 63) | (exp << 52) | (frac << 29);
}
 
/*
* Convert an s-floating point value in register format to the
* corresponding value in memory format.
*/
static inline unsigned long s_reg_to_mem (unsigned long s_reg)
{
return ((s_reg >> 62) << 30) | ((s_reg << 5) >> 34);
}
 
/*
* Handle user-level unaligned fault. Handling user-level unaligned
* faults is *extremely* slow and produces nasty messages. A user
* program *should* fix unaligned faults ASAP.
*
* Notice that we have (almost) the regular kernel stack layout here,
* so finding the appropriate registers is a little more difficult
* than in the kernel case.
*
* Finally, we handle regular integer load/stores only. In
* particular, load-linked/store-conditionally and floating point
* load/stores are not supported. The former make no sense with
* unaligned faults (they are guaranteed to fail) and I don't think
* the latter will occur in any decent program.
*
* Sigh. We *do* have to handle some FP operations, because GCC will
* uses them as temporary storage for integer memory to memory copies.
* However, we need to deal with stt/ldt and sts/lds only.
*/
asmlinkage void do_entUnaUser(void * va, unsigned long opcode, unsigned long reg,
unsigned long * frame)
{
long dir, size;
unsigned long *reg_addr, *pc_addr, usp, zero = 0;
static int cnt = 0;
static long last_time = 0;
extern void alpha_write_fp_reg (unsigned long reg, unsigned long val);
extern unsigned long alpha_read_fp_reg (unsigned long reg);
 
pc_addr = frame + 7 + 20 + 3 /* em86 */ + 1; /* pc in PAL frame */
 
if (cnt >= 5 && jiffies - last_time > 5*HZ) {
cnt = 0;
}
if (++cnt < 5) {
printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n",
current->comm, current->pid,
*pc_addr - 4, va, opcode, reg);
}
last_time = jiffies;
 
++unaligned[1].count;
unaligned[1].va = (unsigned long) va - 4;
unaligned[1].pc = *pc_addr;
 
dir = VERIFY_READ;
if (opcode & 0x4) {
/* it's a stl, stq, stt, or sts */
dir = VERIFY_WRITE;
}
size = 4;
if (opcode & 0x1) {
/* it's a quadword op */
size = 8;
}
if (verify_area(dir, va, size)) {
*pc_addr -= 4; /* make pc point to faulting insn */
force_sig(SIGSEGV, current);
return;
}
 
reg_addr = frame;
if (opcode >= 0x28) {
/* it's an integer load/store */
switch (reg) {
case 0: case 1: case 2: case 3: case 4:
case 5: case 6: case 7: case 8:
/* v0-t7 in SAVE_ALL frame */
reg_addr += 7 + reg;
break;
 
case 9: case 10: case 11: case 12:
case 13: case 14: case 15:
/* s0-s6 in entUna frame */
reg_addr += (reg - 9);
break;
 
case 16: case 17: case 18:
/* a0-a2 in PAL frame */
reg_addr += 7 + 20 + 3 /* em86 */ + 3 + (reg - 16);
break;
 
case 19: case 20: case 21: case 22: case 23:
case 24: case 25: case 26: case 27: case 28:
/* a3-at in SAVE_ALL frame */
reg_addr += 7 + 9 + (reg - 19);
break;
 
case 29:
/* gp in PAL frame */
reg_addr += 7 + 20 + 3 /* em86 */ + 2;
break;
 
case 30:
/* usp in PAL regs */
usp = rdusp();
reg_addr = &usp;
break;
 
case 31:
/* zero "register" */
reg_addr = &zero;
break;
}
}
 
switch (opcode) {
case 0x22: /* lds */
alpha_write_fp_reg(reg, s_mem_to_reg(
get_unaligned((unsigned int *)va)));
break;
case 0x26: /* sts */
put_unaligned(s_reg_to_mem(alpha_read_fp_reg(reg)),
(unsigned int *)va);
break;
 
case 0x23: /* ldt */
alpha_write_fp_reg(reg, get_unaligned((unsigned long *)va));
break;
case 0x27: /* stt */
put_unaligned(alpha_read_fp_reg(reg), (unsigned long *)va);
break;
 
case 0x28: /* ldl */
*reg_addr = get_unaligned((int *)va);
break;
case 0x2c: /* stl */
put_unaligned(*reg_addr, (int *)va);
break;
 
case 0x29: /* ldq */
*reg_addr = get_unaligned((long *)va);
break;
case 0x2d: /* stq */
put_unaligned(*reg_addr, (long *)va);
break;
 
default:
*pc_addr -= 4; /* make pc point to faulting insn */
force_sig(SIGBUS, current);
return;
}
 
if (opcode >= 0x28 && reg == 30 && dir == VERIFY_WRITE) {
wrusp(usp);
}
}
 
/*
* DEC means people to use the "retsys" instruction for return from
* a system call, but they are clearly misguided about this. We use
* "rti" in all cases, and fill in the stack with the return values.
* That should make signal handling etc much cleaner.
*
* Even more horribly, DEC doesn't allow system calls from kernel mode.
* "Security" features letting the user do something the kernel can't
* are a thinko. DEC palcode is strange. The PAL-code designers probably
* got terminally tainted by VMS at some point.
*/
asmlinkage long do_entSys(unsigned long a0, unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4, unsigned long a5,
struct pt_regs regs)
{
if (regs.r0 != 112 && regs.r0 < 300)
printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2);
return -ENOSYS;
}
 
extern asmlinkage void entMM(void);
extern asmlinkage void entIF(void);
extern asmlinkage void entArith(void);
extern asmlinkage void entUna(void);
extern asmlinkage void entSys(void);
 
void trap_init(void)
{
unsigned long gptr;
 
/*
* Tell PAL-code what global pointer we want in the kernel..
*/
__asm__("br %0,___tmp\n"
"___tmp:\tldgp %0,0(%0)"
: "=r" (gptr));
wrkgp(gptr);
 
wrent(entArith, 1);
wrent(entMM, 2);
wrent(entIF, 3);
wrent(entUna, 4);
wrent(entSys, 5);
}
/kernel/irq.c
0,0 → 1,1290
/*
* linux/arch/alpha/kernel/irq.c
*
* Copyright (C) 1995 Linus Torvalds
*
* This file contains the code used by various IRQ handling routines:
* asking for different IRQ's should be done through these routines
* instead of just grabbing them. Thus setups with different IRQ numbers
* shouldn't result in any weird surprises, and installing new handlers
* should be easier.
*/
 
#include <linux/config.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/malloc.h>
#include <linux/random.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/dma.h>
 
extern void timer_interrupt(struct pt_regs * regs);
extern void cserve_update_hw(unsigned long, unsigned long);
 
#if NR_IRQS > 64
# error Unable to handle more than 64 irq levels.
#endif
 
/* Reserved interrupts. These must NEVER be requested by any driver!
*/
#define IS_RESERVED_IRQ(irq) ((irq)==2) /* IRQ 2 used by hw cascade */
 
/*
* Shadow-copy of masked interrupts.
* The bits are used as follows:
* 0.. 7 first (E)ISA PIC (irq level 0..7)
* 8..15 second (E)ISA PIC (irq level 8..15)
* Systems with PCI interrupt lines managed by GRU (e.g., Alcor, XLT):
* or PYXIS (e.g. Miata, PC164-LX)
* 16..47 PCI interrupts 0..31 (xxx_INT_MASK reg)
* Mikasa:
* 16..31 PCI interrupts 0..15 (short at I/O port 536)
* Other systems (not Mikasa) with 16 PCI interrupt lines:
* 16..23 PCI interrupts 0.. 7 (char at I/O port 26)
* 24..31 PCI interrupts 8..15 (char at I/O port 27)
* Systems with 17 PCI interrupt lines (e.g., Cabriolet and eb164):
* 16..32 PCI interrupts 0..31 (int at I/O port 804)
* Takara:
* 16..19 PCI interrupts A thru D
* For SABLE, which is really baroque, we manage 40 IRQ's, but the
* hardware really only supports 24, not via normal ISA PIC,
* but cascaded custom 8259's, etc.
* 0-7 (char at 536)
* 8-15 (char at 53a)
* 16-23 (char at 53c)
*/
static unsigned long irq_mask = ~0UL;
 
#ifdef CONFIG_ALPHA_SABLE
/* note that the vector reported by the SRM PALcode corresponds to the
interrupt mask bits, but we have to manage via more normal IRQs */
static char sable_irq_to_mask[NR_IRQS] = {
-1, 6, -1, 8, 15, 12, 7, 9, /* pseudo PIC 0-7 */
-1, 16, 17, 18, 3, -1, 21, 22, /* pseudo PIC 8-15 */
-1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 0-7 */
-1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 8-15 */
2, 1, 0, 4, 5, -1, -1, -1, /* pseudo PCI */
};
#define IRQ_TO_MASK(irq) (sable_irq_to_mask[(irq)])
static char sable_mask_to_irq[NR_IRQS] = {
34, 33, 32, 12, 35, 36, 1, 6, /* mask 0-7 */
3, 7, -1, -1, 5, -1, -1, 4, /* mask 8-15 */
9, 10, 11, -1, -1, 14, 15, -1, /* mask 16-23 */
};
#else /* CONFIG_ALPHA_SABLE */
#define IRQ_TO_MASK(irq) (irq)
#endif /* CONFIG_ALPHA_SABLE */
 
/*
* Update the hardware with the irq mask passed in MASK. The function
* exploits the fact that it is known that only bit IRQ has changed.
*
* There deliberately isn't a case in here for the Takara since it
* wouldn't work anyway because the interrupt controller is bizarre.
*/
static void update_hw(unsigned long irq, unsigned long mask)
{
#ifdef CONFIG_ALPHA_ALCOR
/* always mask out 20..30 (which are unused) */
mask |= 0x7ff00000UL << 16;
#endif
switch (irq) {
 
#ifdef CONFIG_ALPHA_SABLE
/* SABLE does everything different, so we manage it that way... :-( */
/* the "irq" argument is really the mask bit number */
case 0 ... 7:
outb(mask, 0x537);
break;
case 8 ... 15:
outb(mask >> 8, 0x53b);
break;
case 16 ... 23:
outb(mask >> 16, 0x53d);
break;
#else /* SABLE */
 
#if defined(CONFIG_ALPHA_NORITAKE)
/* note inverted sense of mask bits: */
case 16 ... 31:
outw(~(mask >> 16), 0x54a);
break;
case 32 ... 47:
outw(~(mask >> 32), 0x54c);
break;
#endif /* NORITAKE */
 
#if defined(CONFIG_ALPHA_MIATA)
case 16 ... 47:
{ unsigned long temp;
/* note inverted sense of mask bits: */
/* make CERTAIN none of the bogus ints get enabled */
*(unsigned long *)PYXIS_INT_MASK =
~((long)mask >> 16) & ~0x400000000000063bUL; mb();
temp = *(unsigned long *)PYXIS_INT_MASK;
break;
}
#endif /* MIATA */
 
#if defined(CONFIG_ALPHA_RUFFIAN)
case 16 ... 47:
{ unsigned long temp;
/* note inverted sense of mask bits: */
/* make CERTAIN none of the bogus ints get enabled */
*(unsigned long *)PYXIS_INT_MASK =
~((long)mask >> 16) & 0x00000000ffffffbfUL; mb();
temp = *(unsigned long *)PYXIS_INT_MASK;
break;
}
#endif /* RUFFIAN */
 
#if defined(CONFIG_ALPHA_SX164)
case 16 ... 39:
#if defined(CONFIG_ALPHA_SRM)
cserve_update_hw(irq, mask);
break;
#else
{ unsigned long temp;
/* make CERTAIN none of the bogus ints get enabled */
*(unsigned long *)PYXIS_INT_MASK =
~((long)mask >> 16) & ~0x000000000000003bUL; mb();
temp = *(unsigned long *)PYXIS_INT_MASK;
break;
}
#endif /* SRM */
#endif /* SX164 */
 
#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
case 16 ... 47:
/* note inverted sense of mask bits: */
*(unsigned int *)GRU_INT_MASK = ~(mask >> 16); mb();
break;
#endif /* ALCOR || XLT */
 
#if defined(CONFIG_ALPHA_CABRIOLET) || \
defined(CONFIG_ALPHA_EB66P) || \
defined(CONFIG_ALPHA_EB164) || \
defined(CONFIG_ALPHA_PC164) || \
defined(CONFIG_ALPHA_LX164)
#if defined(CONFIG_ALPHA_SRM)
case 16 ... 34:
cserve_update_hw(irq, mask);
break;
#else /* SRM */
case 16 ... 34:
outl(irq_mask >> 16, 0x804);
break;
#endif /* SRM */
#endif /* CABRIO || EB66P || EB164 || PC164 || LX164 */
 
#if defined(CONFIG_ALPHA_MIKASA)
case 16 ... 31:
outw(~(mask >> 16), 0x536); /* note invert */
break;
#endif /* MIKASA */
 
#if defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P)
case 16 ... 23:
outb(mask >> 16, 0x26);
break;
case 24 ... 31:
outb(mask >> 24, 0x27);
break;
#endif /* EB66 || EB64P */
 
/* handle ISA irqs last---fast devices belong on PCI... */
/* this is common for all except SABLE! */
 
case 0 ... 7: /* ISA PIC1 */
outb(mask, 0x21);
break;
 
case 8 ...15: /* ISA PIC2 */
outb(mask >> 8, 0xA1);
break;
 
#endif /* SABLE */
 
} /* end switch (irq) */
}
 
static inline void mask_irq(unsigned long irq)
{
irq_mask |= (1UL << irq);
update_hw(irq, irq_mask);
}
 
static inline void unmask_irq(unsigned long irq)
{
irq_mask &= ~(1UL << irq);
update_hw(irq, irq_mask);
}
 
void disable_irq(unsigned int irq_nr)
{
unsigned long flags;
 
save_flags(flags);
cli();
mask_irq(IRQ_TO_MASK(irq_nr));
restore_flags(flags);
}
 
void enable_irq(unsigned int irq_nr)
{
unsigned long flags;
 
save_flags(flags);
cli();
unmask_irq(IRQ_TO_MASK(irq_nr));
restore_flags(flags);
}
 
/*
* Initial irq handlers.
*/
static struct irqaction *irq_action[NR_IRQS];
 
int get_irq_list(char *buf)
{
int i, len = 0;
struct irqaction * action;
 
for (i = 0 ; i < NR_IRQS ; i++) {
action = irq_action[i];
if (!action)
continue;
len += sprintf(buf+len, "%2d: %8d %c %s",
i, kstat.interrupts[i],
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
for (action=action->next; action; action = action->next) {
len += sprintf(buf+len, ",%s %s",
(action->flags & SA_INTERRUPT) ? " +" : "",
action->name);
}
len += sprintf(buf+len, "\n");
}
return len;
}
 
static inline void ack_irq(int irq)
{
#ifdef CONFIG_ALPHA_SABLE
/* note that the "irq" here is really the mask bit number */
switch (irq) {
case 0 ... 7:
outb(0xE0 | (irq - 0), 0x536);
outb(0xE0 | 1, 0x534); /* slave 0 */
break;
case 8 ... 15:
outb(0xE0 | (irq - 8), 0x53a);
outb(0xE0 | 3, 0x534); /* slave 1 */
break;
case 16 ... 24:
outb(0xE0 | (irq - 16), 0x53c);
outb(0xE0 | 4, 0x534); /* slave 2 */
break;
}
#else /* CONFIG_ALPHA_SABLE */
if (irq < 16) {
#if defined(CONFIG_ALPHA_RUFFIAN)
/* ack pyxis ISA interrupt */
*(unsigned long *)PYXIS_INT_REQ = (0x01UL << 7);
if (irq > 7) {
outb(0x20,0xa0);
}
outb(0x20,0x20);
#else /* RUFFIAN */
/* ACK the interrupt making it the lowest priority */
/* First the slave .. */
if (irq > 7) {
outb(0xE0 | (irq - 8), 0xa0);
irq = 2;
}
/* .. then the master */
outb(0xE0 | irq, 0x20);
#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
/* on ALCOR/XLT, need to dismiss interrupt via GRU */
*(int *)GRU_INT_CLEAR = 0x80000000; mb();
*(int *)GRU_INT_CLEAR = 0x00000000; mb();
#endif /* ALCOR || XLT */
#endif /* RUFFIAN */
}
#if defined(CONFIG_ALPHA_RUFFIAN)
else {
/* ack pyxis int */
*(unsigned long *)PYXIS_INT_REQ = (1UL << (irq-16));
}
#endif /* RUFFIAN */
 
#endif /* CONFIG_ALPHA_SABLE */
}
 
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
const char * devname,
void *dev_id)
{
int shared = 0;
struct irqaction * action, **p;
unsigned long flags;
 
if (irq >= NR_IRQS)
return -EINVAL;
if (IS_RESERVED_IRQ(irq))
return -EINVAL;
if (!handler)
return -EINVAL;
p = irq_action + irq;
action = *p;
if (action) {
/* Can't share interrupts unless both agree to */
if (!(action->flags & irqflags & SA_SHIRQ))
return -EBUSY;
 
/* Can't share interrupts unless both are same type */
if ((action->flags ^ irqflags) & SA_INTERRUPT)
return -EBUSY;
 
/* add new interrupt at end of irq queue */
do {
p = &action->next;
action = *p;
} while (action);
shared = 1;
}
 
action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
GFP_KERNEL);
if (!action)
return -ENOMEM;
 
if (irqflags & SA_SAMPLE_RANDOM)
rand_initialize_irq(irq);
 
action->handler = handler;
action->flags = irqflags;
action->mask = 0;
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
 
save_flags(flags);
cli();
*p = action;
 
if (!shared)
unmask_irq(IRQ_TO_MASK(irq));
 
restore_flags(flags);
return 0;
}
void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction * action, **p;
unsigned long flags;
 
if (irq >= NR_IRQS) {
printk("Trying to free IRQ%d\n",irq);
return;
}
if (IS_RESERVED_IRQ(irq)) {
printk("Trying to free reserved IRQ %d\n", irq);
return;
}
for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
if (action->dev_id != dev_id)
continue;
 
/* Found it - now free it */
save_flags(flags);
cli();
*p = action->next;
if (!irq[irq_action])
mask_irq(IRQ_TO_MASK(irq));
restore_flags(flags);
kfree(action);
return;
}
printk("Trying to free free IRQ%d\n",irq);
}
 
static inline void handle_nmi(struct pt_regs * regs)
{
printk("Whee.. NMI received. Probable hardware error\n");
printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461));
}
 
static void unexpected_irq(int irq, struct pt_regs * regs)
{
struct irqaction *action;
int i;
 
printk("IO device interrupt, irq = %d\n", irq);
printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
printk("Expecting: ");
for (i = 0; i < 16; i++)
if ((action = irq_action[i]))
while (action->handler) {
printk("[%s:%d] ", action->name, i);
action = action->next;
}
printk("\n");
#if defined(CONFIG_ALPHA_JENSEN)
printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n",
inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa));
outb(0x0c, 0x3fc);
outb(0x0c, 0x2fc);
outb(0,0x61);
outb(0,0x461);
#endif
}
 
static inline void handle_irq(int irq, struct pt_regs * regs)
{
struct irqaction * action = irq_action[irq];
 
kstat.interrupts[irq]++;
if (!action) {
unexpected_irq(irq, regs);
return;
}
do {
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
}
 
static inline void device_interrupt(int irq, int ack, struct pt_regs * regs)
{
struct irqaction * action;
 
if ((unsigned) irq > NR_IRQS) {
printk("device_interrupt: illegal interrupt %d\n", irq);
return;
}
 
#if defined(CONFIG_ALPHA_RUFFIAN)
/* RUFFIAN uses ISA IRQ #0 to deliver clock ticks */
if (irq == 0) {
timer_interrupt(regs);
ack_irq(0);
return;
}
#endif /* RUFFIAN */
 
kstat.interrupts[irq]++;
action = irq_action[irq];
 
/*
* For normal interrupts, we mask it out, and then ACK it.
* This way another (more timing-critical) interrupt can
* come through while we're doing this one.
*
* Note! An irq without a handler gets masked and acked, but
* never unmasked. The autoirq stuff depends on this (it looks
* at the masks before and after doing the probing).
*/
mask_irq(ack);
ack_irq(ack);
if (!action) {
#if 1
printk("device_interrupt: unexpected interrupt %d\n", irq);
#endif
return;
}
if (action->flags & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
do {
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
unmask_irq(ack);
}
 
#ifdef CONFIG_PCI
 
/*
* Handle ISA interrupt via the PICs.
*/
static inline void isa_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
#if defined(CONFIG_ALPHA_APECS)
# define IACK_SC APECS_IACK_SC
#elif defined(CONFIG_ALPHA_LCA)
# define IACK_SC LCA_IACK_SC
#elif defined(CONFIG_ALPHA_CIA)
# define IACK_SC CIA_IACK_SC
#elif defined(CONFIG_ALPHA_PYXIS)
# define IACK_SC PYXIS_IACK_SC
#else
/*
* This is bogus but necessary to get it to compile
* on all platforms. If you try to use this on any
* other than the intended platforms, you'll notice
* real fast...
*/
# define IACK_SC 1L
#endif
int j;
 
#if 1
/*
* Generate a PCI interrupt acknowledge cycle. The PIC will
* respond with the interrupt vector of the highest priority
* interrupt that is pending. The PALcode sets up the
* interrupts vectors such that irq level L generates vector
* L.
*/
j = *(volatile int *) IACK_SC;
j &= 0xff;
if (j == 7) {
if (!(inb(0x20) & 0x80)) {
/* it's only a passive release... */
return;
}
}
device_interrupt(j, j, regs);
#else
unsigned long pic;
 
/*
* It seems to me that the probability of two or more *device*
* interrupts occurring at almost exactly the same time is
* pretty low. So why pay the price of checking for
* additional interrupts here if the common case can be
* handled so much easier?
*/
/*
* The first read of gives you *all* interrupting lines.
* Therefore, read the mask register and and out those lines
* not enabled. Note that some documentation has 21 and a1
* write only. This is not true.
*/
pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */
pic &= ~irq_mask; /* apply mask */
pic &= 0xFFFB; /* mask out cascade & hibits */
 
while (pic) {
j = ffz(~pic);
pic &= pic - 1;
device_interrupt(j, j, regs);
}
#endif
}
 
#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
/* we have to conditionally compile this because of GRU_xxx symbols */
static inline void alcor_and_xlt_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
 
save_flags(flags);
cli();
 
/* read the interrupt summary register of the GRU */
pld = (*(unsigned int *)GRU_INT_REQ) & GRU_INT_REQ_BITS;
 
#if 0
printk("[0x%08lx/0x%04x]", pld, inb(0x20) | (inb(0xA0) << 8));
#endif
 
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i == 31) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(16 + i, 16 + i, regs);
}
}
restore_flags(flags);
}
#endif /* ALCOR || XLT */
 
static inline void cabriolet_and_eb66p_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
 
save_flags(flags);
cli();
 
/* read the interrupt summary registers */
pld = inb(0x804) | (inb(0x805) << 8) | (inb(0x806) << 16);
 
#if 0
printk("[0x%04X/0x%04X]", pld, inb(0x20) | (inb(0xA0) << 8));
#endif
 
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i == 4) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(16 + i, 16 + i, regs);
}
}
restore_flags(flags);
}
 
static inline void mikasa_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
 
save_flags(flags);
cli();
 
/* read the interrupt summary registers */
pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) |
(((unsigned long) inb(0xa0)) << 8) |
((unsigned long) inb(0x20));
 
#if 0
printk("[0x%08lx]", pld);
#endif
 
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i < 16) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(i, i, regs);
}
}
restore_flags(flags);
}
 
static inline void eb66_and_eb64p_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
 
save_flags(flags);
cli();
 
/* read the interrupt summary registers */
pld = inb(0x26) | (inb(0x27) << 8);
/*
* Now, for every possible bit set, work through
* them and call the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
 
if (i == 5) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(16 + i, 16 + i, regs);
}
}
restore_flags(flags);
}
 
#if defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164)
/* we have to conditionally compile this because of PYXIS_xxx symbols */
static inline void miata_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld, tmp;
unsigned int i;
unsigned long flags;
 
save_flags(flags);
cli();
 
/* read the interrupt summary register of PYXIS */
pld = (*(volatile unsigned long *)PYXIS_INT_REQ);
 
#if 0
printk("[0x%08lx/0x%08lx/0x%04x]", pld,
*(volatile unsigned long *)PYXIS_INT_MASK,
inb(0x20) | (inb(0xA0) << 8));
#endif
 
#ifdef CONFIG_ALPHA_MIATA
/* for now, AND off any bits we are not interested in: */
/* HALT (2), timer (6), ISA Bridge (7), 21142 (8) */
/* then all the PCI slots/INTXs (12-31) */
/* maybe HALT should only be used for SRM console boots? */
pld &= 0x00000000fffff9c4UL;
#endif /* MIATA */
#ifdef CONFIG_ALPHA_SX164
/* for now, AND off any bits we are not interested in: */
/* HALT (2), timer (6), ISA Bridge (7) */
/* then all the PCI slots/INTXs (8-23) */
/* HALT should only be used for SRM console boots */
pld &= 0x0000000000ffffc0UL;
#endif /* SX164 */
 
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i == 7) {
isa_device_interrupt(vector, regs);
} else if (i == 6)
continue;
else { /* if not timer int */
device_interrupt(16 + i, 16 + i, regs);
}
*(unsigned long *)PYXIS_INT_REQ = 1UL << i; mb();
tmp = *(volatile unsigned long *)PYXIS_INT_REQ;
}
restore_flags(flags);
}
#endif /* MIATA || SX164 */
 
static inline void noritake_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
 
save_flags(flags);
cli();
 
/* read the interrupt summary registers */
/* read the interrupt summary registers of NORITAKE */
pld = ((unsigned long) inw(0x544) << 32) |
((unsigned long) inw(0x542) << 16) |
((unsigned long) inb(0xa0) << 8) |
((unsigned long) inb(0x20));
 
#if 0
printk("[0x%08lx]", pld);
#endif
 
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i < 16) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(i, i, regs);
}
}
restore_flags(flags);
}
 
#endif /* CONFIG_PCI */
 
#if defined(CONFIG_ALPHA_RUFFIAN)
static inline void ruffian_device_interrupt(unsigned long vector,
struct pt_regs * regs)
 
{
unsigned long pld, tmp;
unsigned int i;
unsigned long flags;
 
save_flags(flags);
cli();
 
/* read the interrupt summary register of PYXIS */
pld = (*(volatile unsigned long *)PYXIS_INT_REQ);
 
/* for now, AND off any bits we are not interested in:
* HALT (2), timer (6), ISA Bridge (7), 21142 (8)
* then all the PCI slots/INTXs (12-31)
* flash(5) :DWH:
*/
pld &= 0x00000000ffffff9fUL;/* was ffff7f */
 
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
 
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i == 7) {
isa_device_interrupt(vector, regs);
} else { /* if not timer int */
device_interrupt(16 + i,16 + i,regs);
}
*(unsigned long *)PYXIS_INT_REQ = 1UL << i; mb();
tmp = *(volatile unsigned long *)PYXIS_INT_REQ;
}
restore_flags(flags);
}
#endif /* RUFFIAN */
 
static inline void takara_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long flags;
unsigned intstatus;
 
save_flags(flags);
cli();
 
/*
* The PALcode will have passed us vectors 0x800 or 0x810,
* which are fairly arbitrary values and serve only to tell
* us whether an interrupt has come in on IRQ0 or IRQ1. If
* it's IRQ1 it's a PCI interrupt; if it's IRQ0, it's
* probably ISA, but PCI interrupts can come through IRQ0
* as well if the interrupt controller isn't in accelerated
* mode.
*
* OTOH, the accelerator thing doesn't seem to be working
* overly well, so what we'll do instead is try directly
* examining the Master Interrupt Register to see if it's a
* PCI interrupt, and if _not_ then we'll pass it on to the
* ISA handler.
*/
 
intstatus = inw(0x500) & 15;
if (intstatus) {
/*
* This is a PCI interrupt. Check each bit and
* despatch an interrupt if it's set.
*/
if (intstatus & 8) device_interrupt(16+3, 16+3, regs);
if (intstatus & 4) device_interrupt(16+2, 16+2, regs);
if (intstatus & 2) device_interrupt(16+1, 16+1, regs);
if (intstatus & 1) device_interrupt(16+0, 16+0, regs);
} else
isa_device_interrupt (vector, regs);
 
restore_flags(flags);
}
 
/*
* Jensen is special: the vector is 0x8X0 for EISA interrupt X, and
* 0x9X0 for the local motherboard interrupts..
*
* 0x660 - NMI
*
* 0x800 - IRQ0 interval timer (not used, as we use the RTC timer)
* 0x810 - IRQ1 line printer (duh..)
* 0x860 - IRQ6 floppy disk
* 0x8E0 - IRQ14 SCSI controller
*
* 0x900 - COM1
* 0x920 - COM2
* 0x980 - keyboard
* 0x990 - mouse
*
* PCI-based systems are more sane: they don't have the local
* interrupts at all, and have only normal PCI interrupts from
* devices. Happily it's easy enough to do a sane mapping from the
* Jensen.. Note that this means that we may have to do a hardware
* "ack" to a different interrupt than we report to the rest of the
* world.
*/
static inline void srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
{
int irq, ack;
unsigned long flags;
 
save_flags(flags);
cli();
 
#if 0
printk("srm_device_interrupt: vector 0x%lx\n", vector);
#endif
 
ack = irq = (vector - 0x800) >> 4;
 
#ifdef CONFIG_ALPHA_JENSEN
switch (vector) {
case 0x660: handle_nmi(regs); return;
/* local device interrupts: */
case 0x900: handle_irq(4, regs); return; /* com1 -> irq 4 */
case 0x920: handle_irq(3, regs); return; /* com2 -> irq 3 */
case 0x980: handle_irq(1, regs); return; /* kbd -> irq 1 */
case 0x990: handle_irq(9, regs); return; /* mouse -> irq 9 */
default:
if (vector > 0x900) {
printk("Unknown local interrupt %lx\n", vector);
}
}
/* irq1 is supposed to be the keyboard, silly Jensen (is this really needed??) */
if (irq == 1)
irq = 7;
#endif /* CONFIG_ALPHA_JENSEN */
 
#ifdef CONFIG_ALPHA_MIATA
/*
* I really hate to do this, but the MIATA SRM console ignores the
* low 8 bits in the interrupt summary register, and reports the
* vector 0x80 *lower* than I expected from the bit numbering in
* the documentation.
* This was done because the low 8 summary bits really aren't used
* for reporting any interrupts (the PCI-ISA bridge, bit 7, isn't
* used for this purpose, as PIC interrupts are delivered as the
* vectors 0x800-0x8f0).
* But I really don't want to change the fixup code for allocation
* of IRQs, nor the irq_mask maintenance stuff, both of which look
* nice and clean now.
* So, here's this grotty hack... :-(
*/
if (irq >= 16)
ack = irq = irq + 8;
#endif /* CONFIG_ALPHA_MIATA */
 
#ifdef CONFIG_ALPHA_NORITAKE
/*
* I really hate to do this, but the NORITAKE SRM console reports
* PCI vectors *lower* than I expected from the bit numbering in
* the documentation.
* But I really don't want to change the fixup code for allocation
* of IRQs, nor the irq_mask maintenance stuff, both of which look
* nice and clean now.
* So, here's this grotty hack... :-(
*/
if (irq >= 16)
ack = irq = irq + 1;
#endif /* CONFIG_ALPHA_NORITAKE */
 
#ifdef CONFIG_ALPHA_SABLE
irq = sable_mask_to_irq[(ack)];
#if 0
if (irq == 5 || irq == 9 || irq == 10 || irq == 11 ||
irq == 14 || irq == 15)
printk("srm_device_interrupt: vector=0x%lx ack=0x%x irq=0x%x\n",
vector, ack, irq);
#endif
#endif /* CONFIG_ALPHA_SABLE */
 
device_interrupt(irq, ack, regs);
 
restore_flags(flags) ;
}
 
/* PROBE_MASK is the bitset of irqs that we consider for autoprobing: */
#if defined(CONFIG_ALPHA_P2K)
/* always mask out unused timer irq 0 and RTC irq 8 */
# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0x101UL)
#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
/* always mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade: */
# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0xfff000000001UL)
#elif defined(CONFIG_ALPHA_RUFFIAN)
/* must leave timer irq 0 in the mask */
# define PROBE_MASK ((1UL << NR_IRQS) - 1)
#else
/* always mask out unused timer irq 0: */
# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~1UL)
#endif
 
/*
* Start listening for interrupts..
*/
unsigned long probe_irq_on(void)
{
struct irqaction * action;
unsigned long irqs = 0;
unsigned long delay;
unsigned int i;
 
for (i = NR_IRQS - 1; i > 0; i--) {
if (!(PROBE_MASK & (1UL << i))) {
continue;
}
action = irq_action[i];
if (!action) {
enable_irq(i);
irqs |= (1UL << i);
}
}
/*
* Wait about 100ms for spurious interrupts to mask themselves
* out again...
*/
for (delay = jiffies + HZ/10; delay > jiffies; )
barrier();
 
/* now filter out any obviously spurious interrupts */
return irqs & ~irq_mask;
}
 
/*
* Get the result of the IRQ probe.. A negative result means that
* we have several candidates (but we return the lowest-numbered
* one).
*/
int probe_irq_off(unsigned long irqs)
{
int i;
irqs &= irq_mask;
if (!irqs)
return 0;
i = ffz(~irqs);
if (irqs != (1UL << i))
i = -i;
return i;
}
 
static void machine_check(unsigned long vector, unsigned long la, struct pt_regs * regs)
{
#if defined(CONFIG_ALPHA_LCA)
extern void lca_machine_check (unsigned long vector, unsigned long la,
struct pt_regs *regs);
lca_machine_check(vector, la, regs);
#elif defined(CONFIG_ALPHA_APECS)
extern void apecs_machine_check(unsigned long vector, unsigned long la,
struct pt_regs * regs);
apecs_machine_check(vector, la, regs);
#elif defined(CONFIG_ALPHA_CIA)
extern void cia_machine_check(unsigned long vector, unsigned long la,
struct pt_regs * regs);
cia_machine_check(vector, la, regs);
#elif defined(CONFIG_ALPHA_PYXIS)
extern void pyxis_machine_check(unsigned long vector, unsigned long la,
struct pt_regs * regs);
pyxis_machine_check(vector, la, regs);
#elif defined(CONFIG_ALPHA_T2)
extern void t2_machine_check(unsigned long vector, unsigned long la,
struct pt_regs * regs);
t2_machine_check(vector, la, regs);
#else
printk("Machine check\n");
#endif
}
 
asmlinkage void do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
unsigned long a3, unsigned long a4, unsigned long a5,
struct pt_regs regs)
{
#if 0
printk("do_entInt: type 0x%lx\n", type);
#endif
switch (type) {
case 0:
printk("Interprocessor interrupt? You must be kidding\n");
break;
case 1:
timer_interrupt(&regs);
return;
case 2:
machine_check(vector, la_ptr, &regs);
return;
case 3:
#if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \
defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM)
srm_device_interrupt(vector, &regs);
#else /* everyone else */
 
#if defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164)
miata_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_NORITAKE)
noritake_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
alcor_and_xlt_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \
defined(CONFIG_ALPHA_EB164) || defined(CONFIG_ALPHA_PC164) || \
defined(CONFIG_ALPHA_LX164)
cabriolet_and_eb66p_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_MIKASA)
mikasa_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P)
eb66_and_eb64p_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_RUFFIAN)
ruffian_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_TAKARA)
takara_device_interrupt(vector, &regs);
#elif NR_IRQS == 16
isa_device_interrupt(vector, &regs);
#endif
#endif /* everyone else */
return;
case 4:
printk("Performance counter interrupt\n");
break;;
default:
printk("Hardware intr %ld %lx? Huh?\n", type, vector);
}
printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps);
}
 
extern asmlinkage void entInt(void);
 
void init_IRQ(void)
{
unsigned int temp;
 
wrent(entInt, 0);
 
outb(0, DMA1_RESET_REG);
outb(0, DMA2_RESET_REG);
 
/* FIXME FIXME FIXME FIXME FIXME */
#if !defined(CONFIG_ALPHA_SX164)
outb(0, DMA1_CLR_MASK_REG);
/* we need to figure out why this fails on the SX164 */
outb(0, DMA2_CLR_MASK_REG);
#endif /* !SX164 */
/* end FIXMEs */
 
#if defined(CONFIG_ALPHA_SABLE)
outb(irq_mask , 0x537); /* slave 0 */
outb(irq_mask >> 8, 0x53b); /* slave 1 */
outb(irq_mask >> 16, 0x53d); /* slave 2 */
outb(0x44, 0x535); /* enable cascades in master */
#else /* everybody but SABLE */
 
#if defined(CONFIG_ALPHA_MIATA)
/* note invert on MASK bits */
*(unsigned long *)PYXIS_INT_MASK =
~((long)irq_mask >> 16) & ~0x400000000000063bUL; mb();
#if 0
/* these break on MiataGL so we'll try not to do it at all */
*(unsigned long *)PYXIS_INT_HILO = 0x000000B2UL; mb();/* ISA/NMI HI */
*(unsigned long *)PYXIS_RT_COUNT = 0UL; mb();/* clear count */
#endif
/* clear upper timer */
*(unsigned long *)PYXIS_INT_REQ = 0x4000000000000180UL; mb();
 
/* Send -INTA pulses to clear any pending interrupts ...*/
temp = *(volatile unsigned int *) IACK_SC;
 
enable_irq(16 + 2); /* enable HALT switch - SRM only? */
enable_irq(16 + 6); /* enable timer */
enable_irq(16 + 7); /* enable ISA PIC cascade */
#endif /* MIATA */
 
#if defined(CONFIG_ALPHA_SX164)
#if !defined(CONFIG_ALPHA_SRM)
/* note invert on MASK bits */
*(unsigned long *)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb();
#if 0
*(unsigned long *)PYXIS_INT_HILO = 0x000000B2UL; mb();/* ISA/NMI HI */
*(unsigned long *)PYXIS_RT_COUNT = 0UL; mb();/* clear count */
#endif
#endif /* !SRM */
enable_irq(16 + 6); /* enable timer */
enable_irq(16 + 7); /* enable ISA PIC cascade */
#endif /* SX164 */
 
#if defined(CONFIG_ALPHA_NORITAKE)
outw(~(irq_mask >> 16), 0x54a); /* note invert */
outw(~(irq_mask >> 32), 0x54c); /* note invert */
#endif /* NORITAKE */
 
#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
*(unsigned int *)GRU_INT_MASK = ~(irq_mask >> 16); mb();/* invert */
*(unsigned int *)GRU_INT_EDGE = 0UL; mb();/* all are level */
*(unsigned int *)GRU_INT_HILO = 0x80000000UL; mb();/* ISA only HI */
*(unsigned int *)GRU_INT_CLEAR = 0UL; mb();/* all clear */
enable_irq(16 + 31); /* enable (E)ISA PIC cascade */
#endif /* ALCOR || XLT */
 
#if defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \
defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164) || \
defined(CONFIG_ALPHA_EB164)
#if !defined(CONFIG_ALPHA_SRM)
outl(irq_mask >> 16, 0x804);
#endif /* !SRM */
/* Send -INTA pulses to clear any pending interrupts ...*/
temp = *(volatile unsigned int *) IACK_SC;
enable_irq(16 + 4); /* enable SIO cascade */
#endif /* CABRIO || EB66P || PC164 || LX164 || EB164 */
 
#if defined(CONFIG_ALPHA_MIKASA)
outw(~(irq_mask >> 16), 0x536); /* note invert */
#endif /* MIKASA */
 
#if defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P)
outb(irq_mask >> 16, 0x26);
outb(irq_mask >> 24, 0x27);
enable_irq(16 + 5); /* enable SIO cascade */
#endif /* EB66 || EB64P */
 
#if defined(CONFIG_ALPHA_RUFFIAN)
/* invert 6&7 for i82371 */
*(unsigned long *)PYXIS_INT_HILO = 0x000000c0UL;mb();
*(unsigned long *)PYXIS_INT_CNFG = 0x00002064UL;mb(); /* all clear */
*(unsigned long *)PYXIS_INT_MASK = ((long)0x00000000UL);mb();
*(unsigned long *)PYXIS_INT_REQ = 0xffffffffUL;mb();
 
outb(0x11,0xA0);
outb(0x08,0xA1);
outb(0x02,0xA1);
outb(0x01,0xA1);
outb(0xFF,0xA1);
outb(0x11,0x20);
outb(0x00,0x21);
outb(0x04,0x21);
outb(0x01,0x21);
outb(0xFF,0x21);
/* Send -INTA pulses to clear any pending interrupts ...*/
temp = *(volatile unsigned int *) IACK_SC;
/* Finish writing the 82C59A PIC Operation Control Words */
outb(0x20,0xA0);
outb(0x20,0x20);
/* Turn on the interrupt controller, the timer interrupt */
enable_irq(16 + 7); /* enable ISA PIC cascade */
enable_irq(0); /* enable timer */
#endif /* RUFFIAN */
 
#ifdef CONFIG_ALPHA_TAKARA
{
unsigned int ctlreg = inl(0x500);
ctlreg &= ~0x8000; /* return to non-accelerated mode */
outw(ctlreg >> 16, 0x502);
outw(ctlreg & 0xFFFF, 0x500);
ctlreg = 0x05107c00; /* enable the PCI interrupt register */
printk("Setting to 0x%08x\n", ctlreg);
outw(ctlreg >> 16, 0x502);
outw(ctlreg & 0xFFFF, 0x500);
}
#endif /* TAKARA */
 
/* and finally, everyone but SABLE does this */
enable_irq(2); /* enable 2nd PIC cascade */
 
#endif /* SABLE */
}
/kernel/apecs.c
0,0 → 1,615
/*
* Code common to all APECS chips.
*
* Rewritten for Apecs from the lca.c from:
*
* Written by David Mosberger (davidm@cs.arizona.edu) with some code
* taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit
* bios code.
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/bios32.h>
#include <linux/pci.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/ptrace.h>
 
extern struct hwrpb_struct *hwrpb;
extern asmlinkage void wrmces(unsigned long mces);
 
/*
* BIOS32-style PCI interface:
*/
 
#ifdef CONFIG_ALPHA_APECS
 
#ifdef DEBUG
# define DBG(args) printk args
#else
# define DBG(args)
#endif
 
#define vuip volatile unsigned int *
 
static volatile unsigned int apecs_mcheck_expected = 0;
static volatile unsigned int apecs_mcheck_taken = 0;
static unsigned int apecs_jd, apecs_jd1, apecs_jd2;
 
#ifdef CONFIG_ALPHA_SRM_SETUP
unsigned int APECS_DMA_WIN_BASE = APECS_DMA_WIN_BASE_DEFAULT;
unsigned int APECS_DMA_WIN_SIZE = APECS_DMA_WIN_SIZE_DEFAULT;
#endif /* SRM_SETUP */
 
/*
* Given a bus, device, and function number, compute resulting
* configuration space address and setup the APECS_HAXR2 register
* accordingly. It is therefore not safe to have concurrent
* invocations to configuration space access routines, but there
* really shouldn't be any need for this.
*
* Type 0:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:11 Device select bit.
* 10:8 Function number
* 7:2 Register number
*
* Type 1:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:24 reserved
* 23:16 bus number (8 bits = 128 possible buses)
* 15:11 Device number (5 bits)
* 10:8 function number
* 7:2 register number
*
* Notes:
* The function number selects which function of a multi-function device
* (e.g., scsi and ethernet).
*
* The register selects a DWORD (32 bit) register offset. Hence it
* doesn't get shifted by 2 bits as we want to "drop" the bottom two
* bits.
*/
static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned long *pci_addr,
unsigned char *type1)
{
unsigned long addr;
 
DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n",
bus, device_fn, where, pci_addr, type1));
 
if (bus == 0) {
int device = device_fn >> 3;
 
/* type 0 configuration cycle: */
 
if (device > 20) {
DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", device));
return -1;
}
 
*type1 = 0;
addr = (device_fn << 8) | (where);
} else {
/* type 1 configuration cycle: */
*type1 = 1;
addr = (bus << 16) | (device_fn << 8) | (where);
}
*pci_addr = addr;
DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
return 0;
}
 
 
static unsigned int conf_read(unsigned long addr, unsigned char type1)
{
unsigned long flags;
unsigned int stat0, value;
unsigned int haxr2 = 0; /* to keep gcc quiet */
 
#ifdef CONFIG_ALPHA_SRM
/* some SRMs step on these registers during a machine check: */
register long s0 asm ("9");
register long s1 asm ("10");
register long s2 asm ("11");
register long s3 asm ("12");
register long s4 asm ("13");
register long s5 asm ("14");
asm volatile ("# %0" : "r="(s0));
asm volatile ("# %0" : "r="(s1));
asm volatile ("# %0" : "r="(s2));
asm volatile ("# %0" : "r="(s3));
asm volatile ("# %0" : "r="(s4));
asm volatile ("# %0" : "r="(s5));
#endif
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
 
/* reset status register to avoid losing errors: */
stat0 = *((vuip)APECS_IOC_DCSR);
*((vuip)APECS_IOC_DCSR) = stat0;
mb();
DBG(("conf_read: APECS DCSR was 0x%x\n", stat0));
/* if Type1 access, must set HAE #2 */
if (type1) {
haxr2 = *((vuip)APECS_IOC_HAXR2);
mb();
*((vuip)APECS_IOC_HAXR2) = haxr2 | 1;
DBG(("conf_read: TYPE1 access\n"));
}
 
draina();
apecs_mcheck_expected = 1;
apecs_mcheck_taken = 0;
mb();
/* access configuration space: */
value = *((vuip)addr);
mb();
mb();
if (apecs_mcheck_taken) {
apecs_mcheck_taken = 0;
value = 0xffffffffU;
mb();
}
apecs_mcheck_expected = 0;
mb();
/*
* david.rusling@reo.mts.dec.com. This code is needed for the
* EB64+ as it does not generate a machine check (why I don't
* know). When we build kernels for one particular platform
* then we can make this conditional on the type.
*/
#if 1
draina();
 
/* now look for any errors */
stat0 = *((vuip)APECS_IOC_DCSR);
DBG(("conf_read: APECS DCSR after read 0x%x\n", stat0));
if (stat0 & 0xffe0U) { /* is any error bit set? */
/* if not NDEV, print status */
if (!(stat0 & 0x0800)) {
printk("apecs.c:conf_read: got stat0=%x\n", stat0);
}
 
/* reset error status: */
*((vuip)APECS_IOC_DCSR) = stat0;
mb();
wrmces(0x7); /* reset machine check */
value = 0xffffffff;
}
#endif
 
/* if Type1 access, must reset HAE #2 so normal IO space ops work */
if (type1) {
*((vuip)APECS_IOC_HAXR2) = haxr2 & ~1;
mb();
}
restore_flags(flags);
#ifdef CONFIG_ALPHA_SRM
/* some SRMs step on these registers during a machine check: */
asm volatile ("# %0" :: "r"(s0));
asm volatile ("# %0" :: "r"(s1));
asm volatile ("# %0" :: "r"(s2));
asm volatile ("# %0" :: "r"(s3));
asm volatile ("# %0" :: "r"(s4));
asm volatile ("# %0" :: "r"(s5));
#endif
return value;
}
 
 
static void conf_write(unsigned long addr, unsigned int value, unsigned char type1)
{
unsigned long flags;
unsigned int stat0;
unsigned int haxr2 = 0; /* to keep gcc quiet */
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
/* reset status register to avoid losing errors: */
stat0 = *((vuip)APECS_IOC_DCSR);
*((vuip)APECS_IOC_DCSR) = stat0;
mb();
 
/* if Type1 access, must set HAE #2 */
if (type1) {
haxr2 = *((vuip)APECS_IOC_HAXR2);
mb();
*((vuip)APECS_IOC_HAXR2) = haxr2 | 1;
}
 
draina();
apecs_mcheck_expected = 1;
mb();
/* access configuration space: */
*((vuip)addr) = value;
mb();
mb();
apecs_mcheck_expected = 0;
mb();
/*
* david.rusling@reo.mts.dec.com. This code is needed for the
* EB64+ as it does not generate a machine check (why I don't
* know). When we build kernels for one particular platform
* then we can make this conditional on the type.
*/
#if 1
draina();
 
/* now look for any errors */
stat0 = *((vuip)APECS_IOC_DCSR);
if (stat0 & 0xffe0U) { /* is any error bit set? */
/* if not NDEV, print status */
if (!(stat0 & 0x0800)) {
printk("apecs.c:conf_write: got stat0=%x\n", stat0);
}
 
/* reset error status: */
*((vuip)APECS_IOC_DCSR) = stat0;
mb();
wrmces(0x7); /* reset machine check */
}
#endif
 
/* if Type1 access, must reset HAE #2 so normal IO space ops work */
if (type1) {
*((vuip)APECS_IOC_HAXR2) = haxr2 & ~1;
mb();
}
restore_flags(flags);
}
 
 
int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char *value)
{
unsigned long addr = APECS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xff;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
 
addr |= (pci_addr << 5) + 0x00;
 
*value = conf_read(addr, type1) >> ((where & 3) * 8);
 
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short *value)
{
unsigned long addr = APECS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xffff;
 
if (where & 0x1) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
return PCIBIOS_SUCCESSFUL;
}
 
addr |= (pci_addr << 5) + 0x08;
 
*value = conf_read(addr, type1) >> ((where & 3) * 8);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int *value)
{
unsigned long addr = APECS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xffffffff;
if (where & 0x3) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
*value = conf_read(addr, type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char value)
{
unsigned long addr = APECS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x00;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short value)
{
unsigned long addr = APECS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x08;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int value)
{
unsigned long addr = APECS_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end)
{
 
#ifdef CONFIG_ALPHA_XL
/*
* Set up the PCI->physical memory translation windows.
* For the XL we *must* use both windows, in order to
* maximize the amount of physical memory that can be used
* to DMA from the ISA bus, and still allow PCI bus devices
* access to all of host memory.
*
* see <asm/apecs.h> for window bases and sizes.
*
* this restriction due to the true XL motherboards' 82379AB SIO
* PCI<->ISA bridge chip which passes only 27 bits of address...
*/
 
*(vuip)APECS_IOC_PB1R = 1U<<19 | (APECS_XL_DMA_WIN1_BASE & 0xfff00000U);
*(vuip)APECS_IOC_PM1R = (APECS_XL_DMA_WIN1_SIZE - 1) & 0xfff00000U;
*(vuip)APECS_IOC_TB1R = 0;
 
*(vuip)APECS_IOC_PB2R = 1U<<19 | (APECS_XL_DMA_WIN2_BASE & 0xfff00000U);
*(vuip)APECS_IOC_PM2R = (APECS_XL_DMA_WIN2_SIZE - 1) & 0xfff00000U;
*(vuip)APECS_IOC_TB2R = 0;
 
#else /* CONFIG_ALPHA_XL */
#ifdef CONFIG_ALPHA_SRM_SETUP
/* check window 1 for enabled and mapped to 0 */
if ((*(vuip)APECS_IOC_PB1R & (1U<<19)) && (*(vuip)APECS_IOC_TB1R == 0))
{
APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB1R & 0xfff00000U;
APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM1R & 0xfff00000U;
APECS_DMA_WIN_SIZE += 0x00100000U;
#if 0
printk("apecs_init: using Window 1 settings\n");
printk("apecs_init: PB1R 0x%x PM1R 0x%x TB1R 0x%x\n",
*(vuip)APECS_IOC_PB1R,
*(vuip)APECS_IOC_PM1R,
*(vuip)APECS_IOC_TB1R);
#endif
}
else /* check window 2 for enabled and mapped to 0 */
if ((*(vuip)APECS_IOC_PB2R & (1U<<19)) && (*(vuip)APECS_IOC_TB2R == 0))
{
APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB2R & 0xfff00000U;
APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM2R & 0xfff00000U;
APECS_DMA_WIN_SIZE += 0x00100000U;
#if 0
printk("apecs_init: using Window 2 settings\n");
printk("apecs_init: PB2R 0x%x PM2R 0x%x TB2R 0x%x\n",
*(vuip)APECS_IOC_PB2R,
*(vuip)APECS_IOC_PM2R,
*(vuip)APECS_IOC_TB2R);
#endif
}
else /* we must use our defaults... */
#endif /* SRM_SETUP */
{
/*
* Set up the PCI->physical memory translation windows.
* For now, window 2 is disabled. In the future, we may
* want to use it to do scatter/gather DMA. Window 1
* goes at 1 GB and is 1 GB large.
*/
*(vuip)APECS_IOC_PB2R = 0U; /* disable window 2 */
 
*(vuip)APECS_IOC_PB1R = 1U<<19 | (APECS_DMA_WIN_BASE & 0xfff00000U);
*(vuip)APECS_IOC_PM1R = (APECS_DMA_WIN_SIZE - 1) & 0xfff00000U;
*(vuip)APECS_IOC_TB1R = 0;
}
#endif /* CONFIG_ALPHA_XL */
 
#ifdef CONFIG_ALPHA_CABRIOLET
#if 0
/*
* JAE: HACK!!! for now, hardwire if configured...
* davidm: Older miniloader versions don't set the clockfrequency
* right, so hardcode it for now.
*/
if (hwrpb->sys_type == ST_DEC_EB64P) {
hwrpb->sys_type = ST_DEC_EBPC64;
}
if (hwrpb->cycle_freq == 0) {
hwrpb->cycle_freq = 275000000;
}
 
/* update checksum: */
{
unsigned long *l, sum;
 
sum = 0;
for (l = (unsigned long *) hwrpb;
l < (unsigned long *) &hwrpb->chksum;
++l)
sum += *l;
hwrpb->chksum = sum;
}
#endif
#endif /* CONFIG_ALPHA_CABRIOLET */
 
/*
* Finally, clear the HAXR2 register, which gets used
* for PCI Config Space accesses. That is the way
* we want to use it, and we do not want to depend on
* what ARC or SRM might have left behind...
*/
{
#if 0
unsigned int haxr2 = *((vuip)APECS_IOC_HAXR2); mb();
if (haxr2) printk("apecs_init: HAXR2 was 0x%x\n", haxr2);
#endif
*((vuip)APECS_IOC_HAXR2) = 0; mb();
}
 
 
return mem_start;
}
 
int apecs_pci_clr_err(void)
{
apecs_jd = *((vuip)APECS_IOC_DCSR);
if (apecs_jd & 0xffe0L) {
apecs_jd1 = *((vuip)APECS_IOC_SEAR);
*((vuip)APECS_IOC_DCSR) = apecs_jd | 0xffe1L;
apecs_jd = *((vuip)APECS_IOC_DCSR);
mb();
}
*((vuip)APECS_IOC_TBIA) = APECS_IOC_TBIA;
apecs_jd2 = *((vuip)APECS_IOC_TBIA);
mb();
return 0;
}
 
void apecs_machine_check(unsigned long vector, unsigned long la_ptr,
struct pt_regs * regs)
{
struct el_common *mchk_header;
struct el_procdata *mchk_procdata;
struct el_apecs_sysdata_mcheck *mchk_sysdata;
unsigned long *ptr;
int i;
 
 
mchk_header = (struct el_common *)la_ptr;
mchk_procdata = (struct el_procdata *)
(la_ptr + mchk_header->proc_offset - sizeof(mchk_procdata->paltemp));
mchk_sysdata =
(struct el_apecs_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset);
 
#ifdef DEBUG
printk("apecs_machine_check: vector=0x%lx la_ptr=0x%lx\n",
vector, la_ptr);
printk(" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
regs->pc, mchk_header->size, mchk_header->proc_offset,
mchk_header->sys_offset);
printk("apecs_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n",
apecs_mcheck_expected, mchk_sysdata->epic_dcsr,
mchk_sysdata->epic_pear);
ptr = (unsigned long *)la_ptr;
for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]);
}
#endif /* DEBUG */
 
/*
* Check if machine check is due to a badaddr() and if so,
* ignore the machine check.
*/
#ifdef CONFIG_ALPHA_MIKASA
#define MCHK_NO_DEVSEL 0x205L
#define MCHK_NO_TABT 0x204L
if (apecs_mcheck_expected &&
(((unsigned int)mchk_header->code == MCHK_NO_DEVSEL) ||
((unsigned int)mchk_header->code == MCHK_NO_TABT))
)
{
#else
if (apecs_mcheck_expected && (mchk_sysdata->epic_dcsr && 0x0c00UL)) {
#endif
apecs_mcheck_expected = 0;
apecs_mcheck_taken = 1;
mb();
mb();
apecs_pci_clr_err();
wrmces(0x7);
mb();
draina();
DBG(("apecs_machine_check: EXPECTED\n"));
}
else if (vector == 0x620 || vector == 0x630) {
wrmces(0x1f); /* disable correctable from now on */
mb();
draina();
printk("apecs_machine_check: HW correctable (0x%lx)\n", vector);
}
else {
printk(KERN_CRIT "APECS machine check:\n");
printk(KERN_CRIT " vector=0x%lx la_ptr=0x%lx\n",
vector, la_ptr);
printk(KERN_CRIT
" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
regs->pc, mchk_header->size, mchk_header->proc_offset,
mchk_header->sys_offset);
printk(KERN_CRIT " expected %d DCSR 0x%lx PEAR 0x%lx\n",
apecs_mcheck_expected, mchk_sysdata->epic_dcsr,
mchk_sysdata->epic_pear);
 
ptr = (unsigned long *)la_ptr;
for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
printk(KERN_CRIT " +%lx %lx %lx\n",
i*sizeof(long), ptr[i], ptr[i+1]);
}
#if 0
/* doesn't work with MILO */
show_regs(regs);
#endif
}
}
#endif /* CONFIG_ALPHA_APECS */
/kernel/cia.c
0,0 → 1,671
/*
* Code common to all CIA chips.
*
* Written by David A Rusling (david.rusling@reo.mts.dec.com).
* December 1995.
*
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/sched.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/ptrace.h>
#include <asm/mmu_context.h>
 
extern struct hwrpb_struct *hwrpb;
extern asmlinkage void wrmces(unsigned long mces);
 
/*
* Machine check reasons. Defined according to PALcode sources
* (osf.h and platform.h).
*/
#define MCHK_K_TPERR 0x0080
#define MCHK_K_TCPERR 0x0082
#define MCHK_K_HERR 0x0084
#define MCHK_K_ECC_C 0x0086
#define MCHK_K_ECC_NC 0x0088
#define MCHK_K_OS_BUGCHECK 0x008A
#define MCHK_K_PAL_BUGCHECK 0x0090
 
/*
* BIOS32-style PCI interface:
*/
 
#ifdef CONFIG_ALPHA_CIA
 
/* #define DEBUG_MCHECK */
/* #define DEBUG_CONFIG */
/* #define DEBUG_DUMP_REGS */
 
#ifdef DEBUG_MCHECK
# define DBGM(args) printk args
#else
# define DBGM(args)
#endif
#ifdef DEBUG_CONFIG
# define DBGC(args) printk args
#else
# define DBGC(args)
#endif
 
#define vuip volatile unsigned int *
 
static volatile unsigned int CIA_mcheck_expected = 0;
static volatile unsigned int CIA_mcheck_taken = 0;
static unsigned int CIA_jd;
 
#ifdef CONFIG_ALPHA_SRM_SETUP
unsigned int CIA_DMA_WIN_BASE = CIA_DMA_WIN_BASE_DEFAULT;
unsigned int CIA_DMA_WIN_SIZE = CIA_DMA_WIN_SIZE_DEFAULT;
unsigned long cia_sm_base_r1, cia_sm_base_r2, cia_sm_base_r3;
#endif /* SRM_SETUP */
 
/*
* Given a bus, device, and function number, compute resulting
* configuration space address and setup the CIA_HAXR2 register
* accordingly. It is therefore not safe to have concurrent
* invocations to configuration space access routines, but there
* really shouldn't be any need for this.
*
* Type 0:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:11 Device select bit.
* 10:8 Function number
* 7:2 Register number
*
* Type 1:
*
* 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
* 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 31:24 reserved
* 23:16 bus number (8 bits = 128 possible buses)
* 15:11 Device number (5 bits)
* 10:8 function number
* 7:2 register number
*
* Notes:
* The function number selects which function of a multi-function device
* (e.g., scsi and ethernet).
*
* The register selects a DWORD (32 bit) register offset. Hence it
* doesn't get shifted by 2 bits as we want to "drop" the bottom two
* bits.
*/
static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned long *pci_addr,
unsigned char *type1)
{
unsigned long addr;
 
DBGC(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n",
bus, device_fn, where, pci_addr, type1));
 
if (bus == 0) {
int device = device_fn >> 3;
 
/* type 0 configuration cycle: */
 
if (device > 20) {
DBGC(("mk_conf_addr: device (%d) > 20, returning -1\n",
device));
return -1;
}
 
*type1 = 0;
addr = (device_fn << 8) | (where);
} else {
/* type 1 configuration cycle: */
*type1 = 1;
addr = (bus << 16) | (device_fn << 8) | (where);
}
*pci_addr = addr;
DBGC(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
return 0;
}
 
 
static unsigned int conf_read(unsigned long addr, unsigned char type1)
{
unsigned long flags;
unsigned int stat0, value;
unsigned int cia_cfg = 0; /* to keep gcc quiet */
 
value = 0xffffffffU;
mb();
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
DBGC(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
 
/* reset status register to avoid losing errors: */
stat0 = *((vuip)CIA_IOC_CIA_ERR);
*((vuip)CIA_IOC_CIA_ERR) = stat0;
mb();
DBGC(("conf_read: CIA ERR was 0x%x\n", stat0));
/* if Type1 access, must set CIA CFG */
if (type1) {
cia_cfg = *((vuip)CIA_IOC_CFG);
*((vuip)CIA_IOC_CFG) = cia_cfg | 1;
mb();
DBGC(("conf_read: TYPE1 access\n"));
}
 
mb();
draina();
CIA_mcheck_expected = 1;
CIA_mcheck_taken = 0;
mb();
/* access configuration space: */
value = *((vuip)addr);
mb();
mb();
if (CIA_mcheck_taken) {
CIA_mcheck_taken = 0;
value = 0xffffffffU;
mb();
}
CIA_mcheck_expected = 0;
mb();
 
/* if Type1 access, must reset IOC CFG so normal IO space ops work */
if (type1) {
*((vuip)CIA_IOC_CFG) = cia_cfg & ~1;
mb();
}
 
DBGC(("conf_read(): finished\n"));
 
restore_flags(flags);
return value;
}
 
 
static void conf_write(unsigned long addr, unsigned int value, unsigned char type1)
{
unsigned long flags;
unsigned int stat0;
unsigned int cia_cfg = 0; /* to keep gcc quiet */
 
save_flags(flags); /* avoid getting hit by machine check */
cli();
 
/* reset status register to avoid losing errors: */
stat0 = *((vuip)CIA_IOC_CIA_ERR);
*((vuip)CIA_IOC_CIA_ERR) = stat0;
mb();
DBGC(("conf_write: CIA ERR was 0x%x\n", stat0));
/* if Type1 access, must set CIA CFG */
if (type1) {
cia_cfg = *((vuip)CIA_IOC_CFG);
*((vuip)CIA_IOC_CFG) = cia_cfg | 1;
mb();
DBGC(("conf_write: TYPE1 access\n"));
}
 
draina();
CIA_mcheck_expected = 1;
mb();
/* access configuration space: */
*((vuip)addr) = value;
mb();
mb();
 
CIA_mcheck_expected = 0;
mb();
 
/* if Type1 access, must reset IOC CFG so normal IO space ops work */
if (type1) {
*((vuip)CIA_IOC_CFG) = cia_cfg & ~1;
mb();
}
 
DBGC(("conf_write(): finished\n"));
restore_flags(flags);
}
 
 
int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char *value)
{
unsigned long addr = CIA_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xff;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
 
addr |= (pci_addr << 5) + 0x00;
 
*value = conf_read(addr, type1) >> ((where & 3) * 8);
 
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short *value)
{
unsigned long addr = CIA_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xffff;
 
if (where & 0x1) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
return PCIBIOS_SUCCESSFUL;
}
 
addr |= (pci_addr << 5) + 0x08;
 
*value = conf_read(addr, type1) >> ((where & 3) * 8);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int *value)
{
unsigned long addr = CIA_CONF;
unsigned long pci_addr;
unsigned char type1;
 
*value = 0xffffffff;
if (where & 0x3) {
return PCIBIOS_BAD_REGISTER_NUMBER;
}
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
*value = conf_read(addr, type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char value)
{
unsigned long addr = CIA_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x00;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short value)
{
unsigned long addr = CIA_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x08;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int value)
{
unsigned long addr = CIA_CONF;
unsigned long pci_addr;
unsigned char type1;
 
if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) {
return PCIBIOS_SUCCESSFUL;
}
addr |= (pci_addr << 5) + 0x18;
conf_write(addr, value << ((where & 3) * 8), type1);
return PCIBIOS_SUCCESSFUL;
}
 
 
unsigned long cia_init(unsigned long mem_start, unsigned long mem_end)
{
unsigned int cia_tmp;
 
#ifdef DEBUG_DUMP_REGS
{
unsigned int temp;
#if 1
temp = *((vuip)CIA_IOC_CIA_REV); mb();
printk("CIA_init: CIA_REV was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_PCI_LAT); mb();
printk("CIA_init: CIA_PCI_LAT was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_CIA_CTRL); mb();
printk("CIA_init: CIA_CTRL was 0x%x\n", temp);
temp = *((vuip)0xfffffc8740000140UL); mb();
printk("CIA_init: CIA_CTRL1 was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_HAE_MEM); mb();
printk("CIA_init: CIA_HAE_MEM was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_HAE_IO); mb();
printk("CIA_init: CIA_HAE_IO was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_CFG); mb();
printk("CIA_init: CIA_CFG was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_CACK_EN); mb();
printk("CIA_init: CIA_CACK_EN was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_CFG); mb();
printk("CIA_init: CIA_CFG was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_CIA_DIAG); mb();
printk("CIA_init: CIA_DIAG was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_DIAG_CHECK); mb();
printk("CIA_init: CIA_DIAG_CHECK was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_PERF_MONITOR); mb();
printk("CIA_init: CIA_PERF_MONITOR was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_PERF_CONTROL); mb();
printk("CIA_init: CIA_PERF_CONTROL was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_CIA_ERR); mb();
printk("CIA_init: CIA_ERR was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_CIA_STAT); mb();
printk("CIA_init: CIA_STAT was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_MCR); mb();
printk("CIA_init: CIA_MCR was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_ERR_MASK); mb();
printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp);
#endif
temp = *((vuip)CIA_IOC_PCI_W0_BASE); mb();
printk("CIA_init: W0_BASE was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_PCI_W1_BASE); mb();
printk("CIA_init: W1_BASE was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_PCI_W2_BASE); mb();
printk("CIA_init: W2_BASE was 0x%x\n", temp);
temp = *((vuip)CIA_IOC_PCI_W3_BASE); mb();
printk("CIA_init: W3_BASE was 0x%x\n", temp);
}
#endif /* DEBUG_DUMP_REGS */
 
/*
* Set up error reporting.
*/
cia_tmp = *(vuip)CIA_IOC_CIA_ERR;
cia_tmp |= 0x180 ; /* master, target abort */
*(vuip)CIA_IOC_CIA_ERR = cia_tmp ;
mb() ;
 
cia_tmp = *(vuip)CIA_IOC_CIA_CTRL;
cia_tmp |= 0x400; /* turn on FILL_ERR to get mchecks */
*(vuip)CIA_IOC_CIA_CTRL = cia_tmp ;
mb() ;
 
#ifdef CONFIG_ALPHA_SRM_SETUP
/* check window 0 for enabled and mapped to 0 */
if (((*(vuip)CIA_IOC_PCI_W0_BASE & 3) == 1) &&
(*(vuip)CIA_IOC_PCI_T0_BASE == 0))
{
CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W0_BASE & 0xfff00000U;
CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W0_MASK & 0xfff00000U;
CIA_DMA_WIN_SIZE += 0x00100000U;
#if 1
printk("cia_init: using Window 0 settings\n");
printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)CIA_IOC_PCI_W0_BASE,
*(vuip)CIA_IOC_PCI_W0_MASK,
*(vuip)CIA_IOC_PCI_T0_BASE);
#endif
}
else /* check window 1 for enabled and mapped to 0 */
if (((*(vuip)CIA_IOC_PCI_W1_BASE & 3) == 1) &&
(*(vuip)CIA_IOC_PCI_T1_BASE == 0))
{
CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W1_BASE & 0xfff00000U;
CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W1_MASK & 0xfff00000U;
CIA_DMA_WIN_SIZE += 0x00100000U;
#if 1
printk("cia_init: using Window 1 settings\n");
printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)CIA_IOC_PCI_W1_BASE,
*(vuip)CIA_IOC_PCI_W1_MASK,
*(vuip)CIA_IOC_PCI_T1_BASE);
#endif
}
else /* check window 2 for enabled and mapped to 0 */
if (((*(vuip)CIA_IOC_PCI_W2_BASE & 3) == 1) &&
(*(vuip)CIA_IOC_PCI_T2_BASE == 0))
{
CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W2_BASE & 0xfff00000U;
CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W2_MASK & 0xfff00000U;
CIA_DMA_WIN_SIZE += 0x00100000U;
#if 1
printk("cia_init: using Window 2 settings\n");
printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)CIA_IOC_PCI_W2_BASE,
*(vuip)CIA_IOC_PCI_W2_MASK,
*(vuip)CIA_IOC_PCI_T2_BASE);
#endif
}
else /* check window 3 for enabled and mapped to 0 */
if (((*(vuip)CIA_IOC_PCI_W3_BASE & 3) == 1) &&
(*(vuip)CIA_IOC_PCI_T3_BASE == 0))
{
CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W3_BASE & 0xfff00000U;
CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W3_MASK & 0xfff00000U;
CIA_DMA_WIN_SIZE += 0x00100000U;
#if 1
printk("cia_init: using Window 3 settings\n");
printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
*(vuip)CIA_IOC_PCI_W3_BASE,
*(vuip)CIA_IOC_PCI_W3_MASK,
*(vuip)CIA_IOC_PCI_T3_BASE);
#endif
}
else /* we must use our defaults which were pre-initialized... */
#endif /* SRM_SETUP */
{
/*
* Set up the PCI->physical memory translation windows.
* For now, windows 1,2 and 3 are disabled. In the future, we may
* want to use them to do scatter/gather DMA. Window 0
* goes at 1 GB and is 1 GB large.
*/
 
*(vuip)CIA_IOC_PCI_W0_BASE = 1U | (CIA_DMA_WIN_BASE & 0xfff00000U);
*(vuip)CIA_IOC_PCI_W0_MASK = (CIA_DMA_WIN_SIZE - 1) & 0xfff00000U;
*(vuip)CIA_IOC_PCI_T0_BASE = 0;
 
*(vuip)CIA_IOC_PCI_W1_BASE = 0x0 ;
*(vuip)CIA_IOC_PCI_W2_BASE = 0x0 ;
*(vuip)CIA_IOC_PCI_W3_BASE = 0x0 ;
}
 
/*
* check ASN in HWRPB for validity, report if bad
*/
if (hwrpb->max_asn != MAX_ASN) {
printk("CIA_init: max ASN from HWRPB is bad (0x%lx)\n",
hwrpb->max_asn);
hwrpb->max_asn = MAX_ASN;
}
 
/*
* Next, clear the CIA_CFG register, which gets used
* for PCI Config Space accesses. That is the way
* we want to use it, and we do not want to depend on
* what ARC or SRM might have left behind...
*/
{
unsigned int cia_cfg = *((vuip)CIA_IOC_CFG); mb();
if (cia_cfg) {
printk("CIA_init: CFG was 0x%x\n", cia_cfg);
*((vuip)CIA_IOC_CFG) = 0; mb();
}
}
{
unsigned int cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM);
unsigned int cia_hae_io = *((vuip)CIA_IOC_HAE_IO);
#if 0
printk("CIA_init: HAE_MEM was 0x%x\n", cia_hae_mem);
printk("CIA_init: HAE_IO was 0x%x\n", cia_hae_io);
#endif
#ifdef CONFIG_ALPHA_SRM_SETUP
/*
sigh... For the SRM setup, unless we know apriori what the HAE
contents will be, we need to setup the arbitrary region bases
so we can test against the range of addresses and tailor the
region chosen for the SPARSE memory access.
 
see include/asm-alpha/cia.h for the SPARSE mem read/write
*/
cia_sm_base_r1 = (cia_hae_mem ) & 0xe0000000UL; /* region 1 */
cia_sm_base_r2 = (cia_hae_mem << 16) & 0xf8000000UL; /* region 2 */
cia_sm_base_r3 = (cia_hae_mem << 24) & 0xfc000000UL; /* region 3 */
#else /* SRM_SETUP */
*((vuip)CIA_IOC_HAE_MEM) = 0; mb();
cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM);
*((vuip)CIA_IOC_HAE_IO) = 0; mb();
cia_hae_io = *((vuip)CIA_IOC_HAE_IO);
#endif /* SRM_SETUP */
}
return mem_start;
}
 
int cia_pci_clr_err(void)
{
CIA_jd = *((vuip)CIA_IOC_CIA_ERR);
DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd));
*((vuip)CIA_IOC_CIA_ERR) = 0x0180;
mb();
return 0;
}
 
void cia_machine_check(unsigned long vector, unsigned long la_ptr,
struct pt_regs * regs)
{
struct el_common *mchk_header;
struct el_procdata *mchk_procdata;
struct el_CIA_sysdata_mcheck *mchk_sysdata;
unsigned long * ptr;
const char * reason;
char buf[128];
long i;
 
mchk_header = (struct el_common *)la_ptr;
mchk_procdata =
(struct el_procdata *)(la_ptr + mchk_header->proc_offset);
mchk_sysdata =
(struct el_CIA_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset);
 
DBGM(("cia_machine_check: vector=0x%lx la_ptr=0x%lx\n", vector, la_ptr));
DBGM((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
regs->pc, mchk_header->size, mchk_header->proc_offset, mchk_header->sys_offset));
DBGM(("cia_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n",
CIA_mcheck_expected, mchk_sysdata->epic_dcsr, mchk_sysdata->epic_pear));
#ifdef DEBUG_MCHECK
{
unsigned long *ptr;
int i;
 
ptr = (unsigned long *)la_ptr;
for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
printk(" +%lx %lx %lx\n", i*sizeof(long),
ptr[i], ptr[i+1]);
}
}
#endif /* DEBUG_MCHECK */
/*
* Check if machine check is due to a badaddr() and if so,
* ignore the machine check.
*/
mb();
mb();
if (CIA_mcheck_expected) {
DBGM(("CIA machine check expected\n"));
CIA_mcheck_expected = 0;
CIA_mcheck_taken = 1;
mb();
mb();
draina();
cia_pci_clr_err();
wrmces(0x7);
mb();
return;
}
 
switch ((unsigned int) mchk_header->code) {
case MCHK_K_TPERR: reason = "tag parity error"; break;
case MCHK_K_TCPERR: reason = "tag control parity error"; break;
case MCHK_K_HERR: reason = "generic hard error"; break;
case MCHK_K_ECC_C: reason = "correctable ECC error"; break;
case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break;
case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break;
case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break;
case 0x96: reason = "i-cache read retryable error"; break;
case 0x98: reason = "processor detected hard error"; break;
 
/* system specific (these are for Alcor, at least): */
case 0x203: reason = "system detected uncorrectable ECC error"; break;
case 0x205: reason = "parity error detected by CIA"; break;
case 0x207: reason = "non-existent memory error"; break;
case 0x209: reason = "PCI SERR detected"; break;
case 0x20b: reason = "PCI data parity error detected"; break;
case 0x20d: reason = "PCI address parity error detected"; break;
case 0x20f: reason = "PCI master abort error"; break;
case 0x211: reason = "PCI target abort error"; break;
case 0x213: reason = "scatter/gather PTE invalid error"; break;
case 0x215: reason = "flash ROM write error"; break;
case 0x217: reason = "IOA timeout detected"; break;
case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break;
case 0x21b: reason = "EISA fail-safe timer timeout"; break;
case 0x21d: reason = "EISA bus time-out"; break;
case 0x21f: reason = "EISA software generated NMI"; break;
case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break;
default:
sprintf(buf, "reason for machine-check unknown (0x%x)",
(unsigned int) mchk_header->code);
reason = buf;
break;
}
wrmces(rdmces()); /* reset machine check pending flag */
mb();
 
printk(KERN_CRIT " CIA machine check: %s%s\n",
reason, mchk_header->retry ? " (retryable)" : "");
printk(KERN_CRIT " vector=0x%lx la_ptr=0x%lx pc=0x%lx\n",
vector, la_ptr, regs->pc);
 
/* dump the logout area to give all info: */
 
ptr = (unsigned long *)la_ptr;
for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
printk(KERN_CRIT " +%8lx %016lx %016lx\n",
i*sizeof(long), ptr[i], ptr[i+1]);
}
}
 
#endif /* CONFIG_ALPHA_CIA */
/kernel/Makefile
0,0 → 1,33
#
# 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...
 
.S.s:
$(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
.S.o:
$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
 
all: kernel.o head.o
 
O_TARGET := kernel.o
O_OBJS := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \
bios32.o ptrace.o time.o apecs.o lca.o cia.o t2.o pyxis.o \
ksyms.o smc.o
 
ifdef CONFIG_KGDB
O_OBJS += kgdb.o
endif
 
all: kernel.o head.o
 
head.o: head.s
 
head.s: head.S $(TOPDIR)/include/asm-alpha/system.h
$(CPP) -traditional -o $*.s $<
 
include $(TOPDIR)/Rules.make
/boot/tools/mkbb.c
0,0 → 1,151
/* This utility makes a bootblock suitable for the SRM console/miniloader */
 
/* Usage:
* mkbb <device> <lxboot>
*
* Where <device> is the name of the device to install the bootblock on,
* and <lxboot> is the name of a bootblock to merge in. This bootblock
* contains the offset and size of the bootloader. It must be exactly
* 512 bytes long.
*/
 
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
 
/* Minimal definition of disklabel, so we don't have to include
* asm/disklabel.h (confuses make)
*/
#ifndef MAXPARTITIONS
#define MAXPARTITIONS 8 /* max. # of partitions */
#endif
 
#ifndef u8
#define u8 unsigned char
#endif
 
#ifndef u16
#define u16 unsigned short
#endif
 
#ifndef u32
#define u32 unsigned int
#endif
 
struct disklabel {
u32 d_magic; /* must be DISKLABELMAGIC */
u16 d_type, d_subtype;
u8 d_typename[16];
u8 d_packname[16];
u32 d_secsize;
u32 d_nsectors;
u32 d_ntracks;
u32 d_ncylinders;
u32 d_secpercyl;
u32 d_secprtunit;
u16 d_sparespertrack;
u16 d_sparespercyl;
u32 d_acylinders;
u16 d_rpm, d_interleave, d_trackskew, d_cylskew;
u32 d_headswitch, d_trkseek, d_flags;
u32 d_drivedata[5];
u32 d_spare[5];
u32 d_magic2; /* must be DISKLABELMAGIC */
u16 d_checksum;
u16 d_npartitions;
u32 d_bbsize, d_sbsize;
struct d_partition {
u32 p_size;
u32 p_offset;
u32 p_fsize;
u8 p_fstype;
u8 p_frag;
u16 p_cpg;
} d_partitions[MAXPARTITIONS];
};
 
 
typedef union __bootblock {
struct {
char __pad1[64];
struct disklabel __label;
} __u1;
struct {
unsigned long __pad2[63];
unsigned long __checksum;
} __u2;
char bootblock_bytes[512];
unsigned long bootblock_quadwords[64];
} bootblock;
 
#define bootblock_label __u1.__label
#define bootblock_checksum __u2.__checksum
 
main(int argc, char ** argv)
{
bootblock bootblock_from_disk;
bootblock bootloader_image;
int dev, fd;
int i;
int nread;
 
/* Make sure of the arg count */
if(argc != 3) {
fprintf(stderr, "Usage: %s device lxboot\n", argv[0]);
exit(0);
}
 
/* First, open the device and make sure it's accessible */
dev = open(argv[1], O_RDWR);
if(dev < 0) {
perror(argv[1]);
exit(0);
}
 
/* Now open the lxboot and make sure it's reasonable */
fd = open(argv[2], O_RDONLY);
if(fd < 0) {
perror(argv[2]);
close(dev);
exit(0);
}
 
/* Read in the lxboot */
nread = read(fd, &bootloader_image, sizeof(bootblock));
if(nread != sizeof(bootblock)) {
perror("lxboot read");
fprintf(stderr, "expected %d, got %d\n", sizeof(bootblock), nread);
exit(0);
}
 
/* Read in the bootblock from disk. */
nread = read(dev, &bootblock_from_disk, sizeof(bootblock));
if(nread != sizeof(bootblock)) {
perror("bootblock read");
fprintf(stderr, "expected %d, got %d\n", sizeof(bootblock), nread);
exit(0);
}
 
/* Swap the bootblock's disklabel into the bootloader */
bootloader_image.bootblock_label = bootblock_from_disk.bootblock_label;
 
/* Calculate the bootblock checksum */
bootloader_image.bootblock_checksum = 0;
for(i = 0; i < 63; i++) {
bootloader_image.bootblock_checksum +=
bootloader_image.bootblock_quadwords[i];
}
 
/* Write the whole thing out! */
lseek(dev, 0L, SEEK_SET);
if(write(dev, &bootloader_image, sizeof(bootblock)) != sizeof(bootblock)) {
perror("bootblock write");
exit(0);
}
 
close(fd);
close(dev);
exit(0);
}
 
 
/boot/tools/objstrip.c
0,0 → 1,280
/*
* arch/alpha/boot/tools/objstrip.c
*
* Strip the object file headers/trailers from an executable (ELF or ECOFF).
*
* Copyright (C) 1996 David Mosberger-Tang.
*/
/*
* Converts an ECOFF or ELF object file into a bootable file. The
* object file must be a OMAGIC file (i.e., data and bss follow immediatly
* behind the text). See DEC "Assembly Language Programmer's Guide"
* documentation for details. The SRM boot process is documented in
* the Alpha AXP Architecture Reference Manual, Second Edition by
* Richard L. Sites and Richard T. Witek.
*/
#include <stdio.h>
#include <unistd.h>
 
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
 
#include <linux/a.out.h>
#include <linux/coff.h>
#include <linux/param.h>
#include <linux/string.h>
#ifdef __ELF__
# include <asm/elf.h>
# include <linux/elf.h>
#endif
 
/* bootfile size must be multiple of BLOCK_SIZE: */
#define BLOCK_SIZE 512
 
const char * prog_name;
 
 
void
usage (void)
{
fprintf(stderr,
"usage: %s [-v] -p file primary\n"
" %s [-vb] file [secondary]\n", prog_name, prog_name);
exit(1);
}
 
 
int
main (int argc, char *argv[])
{
size_t nwritten, tocopy, n, mem_size, fil_size, pad = 0;
int fd, ofd, i, j, verbose = 0, primary = 0;
char buf[8192], *inname;
struct exec * aout; /* includes file & aout header */
long offset;
#ifdef __ELF__
struct elfhdr *elf;
struct elf_phdr *elf_phdr; /* program header */
unsigned long long e_entry;
#endif
 
prog_name = argv[0];
 
for (i = 1; i < argc && argv[i][0] == '-'; ++i) {
for (j = 1; argv[i][j]; ++j) {
switch (argv[i][j]) {
case 'v':
verbose = ~verbose;
break;
 
case 'b':
pad = BLOCK_SIZE;
break;
 
case 'p':
primary = 1; /* make primary bootblock */
break;
}
}
}
 
if (i >= argc) {
usage();
}
inname = argv[i++];
 
fd = open(inname, O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
 
ofd = 1;
if (i < argc) {
ofd = open(argv[i++], O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd == -1) {
perror("open");
exit(1);
}
}
 
if (primary) {
/* generate bootblock for primary loader */
unsigned long bb[64], sum = 0;
struct stat st;
off_t size;
int i;
 
if (ofd == 1) {
usage();
}
 
if (fstat(fd, &st) == -1) {
perror("fstat");
exit(1);
}
 
size = (st.st_size + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1);
memset(bb, 0, sizeof(bb));
strcpy((char *) bb, "Linux SRM bootblock");
bb[60] = size / BLOCK_SIZE; /* count */
bb[61] = 1; /* starting sector # */
bb[62] = 0; /* flags---must be 0 */
for (i = 0; i < 63; ++i) {
sum += bb[i];
}
bb[63] = sum;
if (write(ofd, bb, sizeof(bb)) != sizeof(bb)) {
perror("boot-block write");
exit(1);
}
printf("%lu\n", size);
return 0;
}
 
/* read and inspect exec header: */
 
if (read(fd, buf, sizeof(buf)) < 0) {
perror("read");
exit(1);
}
 
#ifdef __ELF__
elf = (struct elfhdr *) buf;
 
if (elf->e_ident[0] == 0x7f && strncmp(elf->e_ident + 1, "ELF", 3) == 0) {
if (elf->e_type != ET_EXEC) {
fprintf(stderr, "%s: %s is not an ELF executable\n",
prog_name, inname);
exit(1);
}
if (!elf_check_arch(elf->e_machine)) {
fprintf(stderr, "%s: is not for this processor (e_machine=%d)\n",
prog_name, elf->e_machine);
exit(1);
}
if (elf->e_phnum != 1) {
fprintf(stderr,
"%s: %d program headers (forgot to link with -N?)\n",
prog_name, elf->e_phnum);
}
 
e_entry = elf->e_entry;
 
lseek(fd, elf->e_phoff, SEEK_SET);
if (read(fd, buf, sizeof(*elf_phdr)) != sizeof(*elf_phdr)) {
perror("read");
exit(1);
}
 
elf_phdr = (struct elf_phdr *) buf;
offset = elf_phdr->p_offset;
mem_size = elf_phdr->p_memsz;
fil_size = elf_phdr->p_filesz;
 
/* work around ELF bug: */
if (elf_phdr->p_vaddr < e_entry) {
unsigned long delta = e_entry - elf_phdr->p_vaddr;
offset += delta;
mem_size -= delta;
fil_size -= delta;
elf_phdr->p_vaddr += delta;
}
 
if (verbose) {
fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n",
prog_name, (long) elf_phdr->p_vaddr,
elf_phdr->p_vaddr + fil_size, offset);
}
} else
#endif
{
aout = (struct exec *) buf;
 
if (!(aout->fh.f_flags & COFF_F_EXEC)) {
fprintf(stderr, "%s: %s is not in executable format\n",
prog_name, inname);
exit(1);
}
 
if (aout->fh.f_opthdr != sizeof(aout->ah)) {
fprintf(stderr, "%s: %s has unexpected optional header size\n",
prog_name, inname);
exit(1);
}
 
if (N_MAGIC(*aout) != OMAGIC) {
fprintf(stderr, "%s: %s is not an OMAGIC file\n",
prog_name, inname);
exit(1);
}
offset = N_TXTOFF(*aout);
fil_size = aout->ah.tsize + aout->ah.dsize;
mem_size = fil_size + aout->ah.bsize;
 
if (verbose) {
fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n",
prog_name, aout->ah.text_start,
aout->ah.text_start + fil_size, offset);
}
}
 
if (lseek(fd, offset, SEEK_SET) != offset) {
perror("lseek");
exit(1);
}
 
if (verbose) {
fprintf(stderr, "%s: copying %lu byte from %s\n",
prog_name, (unsigned long) fil_size, inname);
}
 
tocopy = fil_size;
while (tocopy > 0) {
n = tocopy;
if (n > sizeof(buf)) {
n = sizeof(buf);
}
tocopy -= n;
if ((size_t) read(fd, buf, n) != n) {
perror("read");
exit(1);
}
do {
nwritten = write(ofd, buf, n);
if ((ssize_t) nwritten == -1) {
perror("write");
exit(1);
}
n -= nwritten;
} while (n > 0);
}
 
if (pad) {
mem_size = ((mem_size + pad - 1) / pad) * pad;
}
 
tocopy = mem_size - fil_size;
if (tocopy > 0) {
fprintf(stderr,
"%s: zero-filling bss and aligning to %lu with %lu bytes\n",
prog_name, pad, (unsigned long) tocopy);
 
memset(buf, 0x00, sizeof(buf));
do {
n = tocopy;
if (n > sizeof(buf)) {
n = sizeof(buf);
}
nwritten = write(ofd, buf, n);
if ((ssize_t) nwritten == -1) {
perror("write");
exit(1);
}
tocopy -= nwritten;
} while (tocopy > 0);
}
return 0;
}
/boot/head.S
0,0 → 1,139
/*
* arch/alpha/boot/head.S
*
* initial bootloader stuff..
*/
 
#include <asm/system.h>
 
#define halt .long PAL_halt
 
.set noreorder
.globl __start
.ent __start
__start:
bis $31,$31,$31
br 1f
/* room for the initial PCB, which comes here */
.quad 0,0,0,0,0,0,0,0
1: br $27,2f
2: ldgp $29,0($27)
lda $27,start_kernel
jsr $26,($27),start_kernel
halt
.end __start
 
.align 5
.globl wrent
.ent wrent
wrent:
.long PAL_wrent
ret ($26)
.end wrent
 
.align 5
.globl wrkgp
.ent wrkgp
wrkgp:
.long PAL_wrkgp
ret ($26)
.end wrkgp
 
.align 5
.globl switch_to_osf_pal
.ent switch_to_osf_pal
switch_to_osf_pal:
subq $30,128,$30
stq $26,0($30)
stq $1,8($30)
stq $2,16($30)
stq $3,24($30)
stq $4,32($30)
stq $5,40($30)
stq $6,48($30)
stq $7,56($30)
stq $8,64($30)
stq $9,72($30)
stq $10,80($30)
stq $11,88($30)
stq $12,96($30)
stq $13,104($30)
stq $14,112($30)
stq $15,120($30)
 
stq $30,0($17) /* save KSP in PCB */
 
bis $30,$30,$20 /* a4 = KSP */
br $17,__do_swppal
 
ldq $26,0($30)
ldq $1,8($30)
ldq $2,16($30)
ldq $3,24($30)
ldq $4,32($30)
ldq $5,40($30)
ldq $6,48($30)
ldq $7,56($30)
ldq $8,64($30)
ldq $9,72($30)
ldq $10,80($30)
ldq $11,88($30)
ldq $12,96($30)
ldq $13,104($30)
ldq $14,112($30)
ldq $15,120($30)
addq $30,128,$30
ret ($26)
 
__do_swppal:
.long PAL_swppal
.end switch_to_osf_pal
 
.globl dispatch
.ent dispatch
dispatch:
subq $30,80,$30
stq $26,0($30)
stq $29,8($30)
 
stq $8,16($30)
stq $9,24($30)
stq $10,32($30)
stq $11,40($30)
stq $12,48($30)
stq $13,56($30)
stq $14,64($30)
stq $15,72($30)
 
lda $1,0x10000000 /* hwrpb */
ldq $2,0xc0($1) /* crb offset */
addq $2,$1,$2 /* crb */
ldq $27,0($2) /* dispatch procedure value */
 
ldq $2,8($27) /* dispatch call address */
jsr $26,($2) /* call it (weird VMS call seq) */
 
ldq $26,0($30)
ldq $29,8($30)
 
ldq $8,16($30)
ldq $9,24($30)
ldq $10,32($30)
ldq $11,40($30)
ldq $12,48($30)
ldq $13,56($30)
ldq $14,64($30)
ldq $15,72($30)
 
addq $30,80,$30
ret $31,($26)
.end dispatch
 
.align 3
.globl tbi
.ent tbi
tbi:
.long PAL_tbi
ret ($26)
.end tbi
 
/boot/bootp.c
0,0 → 1,238
/*
* arch/alpha/boot/bootp.c
*
* Copyright (C) 1997 Jay Estabrook
*
* This file is used for creating a bootp file for the Linux/AXP kernel
*
* based significantly on the arch/alpha/boot/main.c of Linus Torvalds
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/version.h>
#include <linux/mm.h>
 
#include <asm/system.h>
#include <asm/console.h>
#include <asm/hwrpb.h>
#include <asm/pgtable.h>
#include <asm/io.h>
 
#include <stdarg.h>
 
#include "ksize.h"
 
extern int vsprintf(char *, const char *, va_list);
extern unsigned long switch_to_osf_pal(unsigned long nr,
struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa,
unsigned long vptb, unsigned long *kstk);
 
int printk(const char * fmt, ...)
{
va_list args;
int i, j, written, remaining, num_nl;
static char buf[1024];
char * str;
 
va_start(args, fmt);
i = vsprintf(buf, fmt, args);
va_end(args);
 
/* expand \n into \r\n: */
 
num_nl = 0;
for (j = 0; j < i; ++j) {
if (buf[j] == '\n')
++num_nl;
}
remaining = i + num_nl;
for (j = i - 1; j >= 0; --j) {
buf[j + num_nl] = buf[j];
if (buf[j] == '\n') {
--num_nl;
buf[j + num_nl] = '\r';
}
}
 
str = buf;
do {
written = puts(str, remaining);
remaining -= written;
str += written;
} while (remaining > 0);
return i;
}
 
#define hwrpb (*INIT_HWRPB)
 
/*
* Find a physical address of a virtual object..
*
* This is easy using the virtual page table address.
*/
struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb)
{
unsigned long address = (unsigned long) pcb;
unsigned long result;
 
result = vptb[address >> 13];
result >>= 32;
result <<= 13;
result |= address & 0x1fff;
return (struct pcb_struct *) result;
}
 
/*
* This function moves into OSF/1 pal-code, and has a temporary
* PCB for that. The kernel proper should replace this PCB with
* the real one as soon as possible.
*
* The page table muckery in here depends on the fact that the boot
* code has the L1 page table identity-map itself in the second PTE
* in the L1 page table. Thus the L1-page is virtually addressable
* itself (through three levels) at virtual address 0x200802000.
*
* As we don't want it there anyway, we also move the L1 self-map
* up as high as we can, so that the last entry in the L1 page table
* maps the page tables.
*
* As a result, the OSF/1 pal-code will instead use a virtual page table
* map located at 0xffffffe00000000.
*/
#define pcb_va ((struct pcb_struct *) 0x20000000)
#define old_vptb (0x0000000200000000UL)
#define new_vptb (0xfffffffe00000000UL)
void pal_init(void)
{
unsigned long i, rev, sum;
unsigned long *L1, *l;
struct percpu_struct * percpu;
struct pcb_struct * pcb_pa;
 
/* Find the level 1 page table and duplicate it in high memory */
L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */
L1[1023] = L1[1];
 
percpu = (struct percpu_struct *)
(hwrpb.processor_offset + (unsigned long) &hwrpb),
pcb_va->ksp = 0;
pcb_va->usp = 0;
pcb_va->ptbr = L1[1] >> 32;
pcb_va->asn = 0;
pcb_va->pcc = 0;
pcb_va->unique = 0;
pcb_va->flags = 1;
pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va);
printk("Switching to OSF PAL-code .. ");
/*
* a0 = 2 (OSF)
* a1 = return address, but we give the asm the vaddr of the PCB
* a2 = physical addr of PCB
* a3 = new virtual page table pointer
* a4 = KSP (but we give it 0, asm sets it)
*/
i = switch_to_osf_pal(
2,
pcb_va,
pcb_pa,
new_vptb,
0);
if (i) {
printk("failed, code %ld\n", i);
halt();
}
rev = percpu->pal_revision = percpu->palcode_avail[2];
 
hwrpb.vptb = new_vptb;
 
/* update checksum: */
sum = 0;
for (l = (unsigned long *) &hwrpb;
l < (unsigned long *) &hwrpb.chksum;
++l)
sum += *l;
hwrpb.chksum = sum;
 
printk("Ok (rev %lx)\n", rev);
/* remove the old virtual page-table mapping */
L1[1] = 0;
flush_tlb_all();
}
 
static inline long load(unsigned long dst,
unsigned long src,
unsigned long count)
{
extern void * memcpy(void *, const void *, size_t);
 
memcpy((void *)dst, (void *)src, count);
return count;
}
 
/*
* Start the kernel.
*/
static void runkernel(void)
{
__asm__ __volatile__(
"bis %1,%1,$30\n\t"
"bis %0,%0,$26\n\t"
"ret ($26)"
: /* no outputs: it doesn't even return */
: "r" (START_ADDR),
"r" (PAGE_SIZE + INIT_STACK));
}
 
extern char _end;
#define KERNEL_ORIGIN \
((((unsigned long)&_end) + 511) & ~511)
 
void start_kernel(void)
{
static long i;
static int nbytes;
static char envval[256];
char envbuf[256];
 
printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n");
 
if (hwrpb.pagesize != 8192) {
printk("Expected 8kB pages, got %ldkB\n",
hwrpb.pagesize >> 10);
return;
}
pal_init();
 
nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS,
envbuf, sizeof(envbuf));
if (nbytes < 0 || nbytes >= sizeof(envbuf)) {
nbytes = 0;
}
 
envbuf[nbytes] = '\0';
memcpy(envval, envbuf, nbytes+1);
printk("Loading the kernel...'%s'\n", envval);
 
/* NOTE: *no* callbacks or printouts from here on out!!! */
 
/*
* HACK alert:
*
* assume direct copy will fail due to overlap of virtual source
* and physical destination, so move it way high physical first,
* then move it back to its final resting place...
*
* this way could fail, too, but it works on the platforms *I* have
*/
i = load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE);
i = load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE);
 
strcpy((char*)ZERO_PAGE, envval);
 
runkernel();
 
for (i = 0 ; i < 0x100000000 ; i++)
/* nothing */;
halt();
}
/boot/main.c
0,0 → 1,244
/*
* arch/alpha/boot/main.c
*
* Copyright (C) 1994, 1995 Linus Torvalds
*
* This file is the bootloader for the Linux/AXP kernel
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/version.h>
#include <linux/mm.h>
 
#include <asm/system.h>
#include <asm/console.h>
#include <asm/hwrpb.h>
#include <asm/pgtable.h>
 
#include <stdarg.h>
 
#include "ksize.h"
 
extern int vsprintf(char *, const char *, va_list);
extern unsigned long switch_to_osf_pal(unsigned long nr,
struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa,
unsigned long vptb, unsigned long *kstk);
 
int printk(const char * fmt, ...)
{
va_list args;
int i, j, written, remaining, num_nl;
static char buf[1024];
char * str;
 
va_start(args, fmt);
i = vsprintf(buf, fmt, args);
va_end(args);
 
/* expand \n into \r\n: */
 
num_nl = 0;
for (j = 0; j < i; ++j) {
if (buf[j] == '\n')
++num_nl;
}
remaining = i + num_nl;
for (j = i - 1; j >= 0; --j) {
buf[j + num_nl] = buf[j];
if (buf[j] == '\n') {
--num_nl;
buf[j + num_nl] = '\r';
}
}
 
str = buf;
do {
written = puts(str, remaining);
remaining -= written;
str += written;
} while (remaining > 0);
return i;
}
 
#define hwrpb (*INIT_HWRPB)
 
/*
* Find a physical address of a virtual object..
*
* This is easy using the virtual page table address.
*/
struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb)
{
unsigned long address = (unsigned long) pcb;
unsigned long result;
 
result = vptb[address >> 13];
result >>= 32;
result <<= 13;
result |= address & 0x1fff;
return (struct pcb_struct *) result;
}
 
/*
* This function moves into OSF/1 pal-code, and has a temporary
* PCB for that. The kernel proper should replace this PCB with
* the real one as soon as possible.
*
* The page table muckery in here depends on the fact that the boot
* code has the L1 page table identity-map itself in the second PTE
* in the L1 page table. Thus the L1-page is virtually addressable
* itself (through three levels) at virtual address 0x200802000.
*
* As we don't want it there anyway, we also move the L1 self-map
* up as high as we can, so that the last entry in the L1 page table
* maps the page tables.
*
* As a result, the OSF/1 pal-code will instead use a virtual page table
* map located at 0xffffffe00000000.
*/
#define pcb_va ((struct pcb_struct *) 0x20000000)
#define old_vptb (0x0000000200000000UL)
#define new_vptb (0xfffffffe00000000UL)
void pal_init(void)
{
unsigned long i, rev, sum;
unsigned long *L1, *l;
struct percpu_struct * percpu;
struct pcb_struct * pcb_pa;
 
/* Find the level 1 page table and duplicate it in high memory */
L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */
L1[1023] = L1[1];
 
percpu = (struct percpu_struct *) (hwrpb.processor_offset + (unsigned long) &hwrpb),
pcb_va->ksp = 0;
pcb_va->usp = 0;
pcb_va->ptbr = L1[1] >> 32;
pcb_va->asn = 0;
pcb_va->pcc = 0;
pcb_va->unique = 0;
pcb_va->flags = 1;
pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va);
printk("Switching to OSF PAL-code .. ");
/*
* a0 = 2 (OSF)
* a1 = return address, but we give the asm the virtual addr of the PCB
* a2 = physical addr of PCB
* a3 = new virtual page table pointer
* a4 = KSP (but we give it 0, asm sets it)
*/
i = switch_to_osf_pal(
2,
pcb_va,
pcb_pa,
new_vptb,
0);
if (i) {
printk("failed, code %ld\n", i);
halt();
}
rev = percpu->pal_revision = percpu->palcode_avail[2];
 
hwrpb.vptb = new_vptb;
 
/* update checksum: */
sum = 0;
for (l = (unsigned long *) &hwrpb; l < (unsigned long *) &hwrpb.chksum; ++l)
sum += *l;
hwrpb.chksum = sum;
 
printk("Ok (rev %lx)\n", rev);
/* remove the old virtual page-table mapping */
L1[1] = 0;
flush_tlb_all();
}
 
static inline long openboot(void)
{
char bootdev[256];
long result;
 
result = dispatch(CCB_GET_ENV, ENV_BOOTED_DEV, bootdev, 255);
if (result < 0)
return result;
return dispatch(CCB_OPEN, bootdev, result & 255);
}
 
static inline long close(long dev)
{
return dispatch(CCB_CLOSE, dev);
}
 
static inline long load(long dev, unsigned long addr, unsigned long count)
{
char bootfile[256];
extern char _end;
long result, boot_size = &_end - (char *) BOOT_ADDR;
 
result = dispatch(CCB_GET_ENV, ENV_BOOTED_FILE, bootfile, 255);
if (result < 0)
return result;
result &= 255;
bootfile[result] = '\0';
if (result)
printk("Boot file specification (%s) not implemented\n",
bootfile);
return dispatch(CCB_READ, dev, count, addr, boot_size/512 + 1);
}
 
/*
* Start the kernel.
*/
static void runkernel(void)
{
__asm__ __volatile__(
"bis %1,%1,$30\n\t"
"bis %0,%0,$26\n\t"
"ret ($26)"
: /* no outputs: it doesn't even return */
: "r" (START_ADDR),
"r" (PAGE_SIZE + INIT_STACK));
}
 
void start_kernel(void)
{
long i;
long dev;
int nbytes;
char envval[256];
 
printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n");
if (hwrpb.pagesize != 8192) {
printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10);
return;
}
pal_init();
dev = openboot();
if (dev < 0) {
printk("Unable to open boot device: %016lx\n", dev);
return;
}
dev &= 0xffffffff;
printk("Loading vmlinux ...");
i = load(dev, START_ADDR, KERNEL_SIZE);
close(dev);
if (i != KERNEL_SIZE) {
printk("Failed (%lx)\n", i);
return;
}
 
nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS,
envval, sizeof(envval));
if (nbytes < 0) {
nbytes = 0;
}
envval[nbytes] = '\0';
strcpy((char*)ZERO_PAGE, envval);
 
printk(" Ok\nNow booting the kernel\n");
runkernel();
for (i = 0 ; i < 0x100000000 ; i++)
/* nothing */;
halt();
}
/boot/bootloader.lds
0,0 → 1,50
OUTPUT_FORMAT("ecoff-littlealpha")
ENTRY(__start)
SECTIONS
{
.text 0x20000000: {
_ftext = . ;
__istart = . ;
eprol = .;
*(.text)
__fstart = . ;
_etext = .;
}
.rdata : {
*(.rdata)
}
.pdata : {
_fpdata = .;
*(.pdata)
}
.data : {
_fdata = .;
*(.data)
CONSTRUCTORS
}
.xdata : {
*(.xdata)
}
_gp = ALIGN (16) + 0x8000;
.lit8 : {
*(.lit8)
}
.lita : {
*(.lita)
}
.sdata : {
*(.sdata)
}
_EDATA = .;
_FBSS = .;
.sbss : {
*(.sbss)
*(.scommon)
. = ALIGN(16);
}
.bss : {
*(.bss)
*(COMMON)
}
_end = .;
}
/boot/Makefile
0,0 → 1,118
#
# arch/alpha/boot/Makefile
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 1994 by Linus Torvalds
#
 
ifdef CONFIG_CROSSCOMPILE
# enable this for linking under OSF/1:
LINKFLAGS = -non_shared -T 0x20000000 -N
else
elf=$(shell if $(LD) --help | grep elf64alpha >/dev/null; then echo yes; fi)
ifeq ($(elf),yes)
LINKFLAGS = -static -Ttext 0x20000000 -N
else
LINKFLAGS = -static -T bootloader.lds -N
endif
endif
 
.S.s:
$(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $<
.S.o:
$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
 
OBJECTS = head.o main.o
BPOBJECTS = head.o bootp.o
TARGETS = vmlinux.gz tools/objstrip # also needed by aboot & milo
VMLINUX = $(TOPDIR)/vmlinux
OBJSTRIP = tools/objstrip
 
all: $(TARGETS)
@echo Ready to install kernel in $(shell pwd)/vmlinux.gz
 
# normally no need to build these:
rawboot: vmlinux.nh tools/lxboot tools/bootlx
 
msb: tools/lxboot tools/bootlx vmlinux.nh
( cat tools/lxboot tools/bootlx vmlinux.nh ) > /dev/rz0a
disklabel -rw rz0 'linux' tools/lxboot tools/bootlx
 
bootimage: tools/mkbb tools/lxboot tools/bootlx vmlinux.nh
( cat tools/lxboot tools/bootlx vmlinux.nh ) > bootimage
tools/mkbb bootimage tools/lxboot
 
bootpfile: tools/bootph vmlinux.nh
( cat tools/bootph vmlinux.nh ) > bootpfile
 
srmboot: bootdevice bootimage
dd if=bootimage of=$(BOOTDEV) bs=512 seek=1 skip=1
tools/mkbb $(BOOTDEV) tools/lxboot
 
bootdevice:
@test "$(BOOTDEV)" != "" || (echo You must specify BOOTDEV ; exit -1)
 
vmlinux.gz: vmlinux
gzip -fv vmlinux
 
#
# A raw binary without header. Used by raw boot.
#
main.o: ksize.h
 
bootp.o: ksize.h
 
ksize.h: $(OBJSTRIP) vmlinux.nh
echo "#define KERNEL_SIZE `$(OBJSTRIP) -p vmlinux.nh /dev/null`" > $@
 
vmlinux.nh: $(VMLINUX) $(OBJSTRIP)
ifeq ($(elf),yes)
cp $(VMLINUX) vmlinux.stripped
strip vmlinux.stripped # work around ELF binutils bug...
$(OBJSTRIP) -v vmlinux.stripped vmlinux.nh
rm -f vmlinux.stripped
else
$(OBJSTRIP) -v $(VMLINUX) vmlinux.nh
endif
 
vmlinux: $(TOPDIR)/vmlinux
cp $(TOPDIR)/vmlinux vmlinux
strip vmlinux
 
tools/lxboot: $(OBJSTRIP) bootloader
$(OBJSTRIP) -p bootloader tools/lxboot
 
tools/bootlx: bootloader $(OBJSTRIP)
$(OBJSTRIP) -vb bootloader tools/bootlx
 
tools/bootph: bootpheader $(OBJSTRIP)
$(OBJSTRIP) -vb bootpheader tools/bootph
 
$(OBJSTRIP): $(OBJSTRIP).c
$(HOSTCC) $(OBJSTRIP).c -o $(OBJSTRIP)
 
tools/mkbb: tools/mkbb.c
$(HOSTCC) tools/mkbb.c -o tools/mkbb
 
bootloader: $(OBJECTS)
$(LD) $(LINKFLAGS) \
$(OBJECTS) \
$(LIBS) \
-o bootloader && strip bootloader || \
(rm -f bootloader && exit 1)
 
bootpheader: $(BPOBJECTS)
$(LD) $(LINKFLAGS) \
$(BPOBJECTS) \
$(LIBS) \
-o bootpheader && strip bootpheader || \
(rm -f bootpheader && exit 1)
 
clean:
rm -f $(TARGETS) bootloader bootimage vmlinux.nh \
tools/mkbb tools/bootlx tools/lxboot ksize.h
 
dep:
/vmlinux.lds
0,0 → 1,50
OUTPUT_FORMAT("ecoff-littlealpha")
ENTRY(__start)
SECTIONS
{
.text 0xfffffc0000310000: {
_ftext = . ;
__istart = . ;
eprol = .;
*(.text)
__fstart = . ;
_etext = .;
}
.rdata : {
*(.rdata)
}
.pdata : {
_fpdata = .;
*(.pdata)
}
.data : {
_fdata = .;
*(.data)
CONSTRUCTORS
}
.xdata : {
*(.xdata)
}
_gp = ALIGN (16) + 0x8000;
.lit8 : {
*(.lit8)
}
.lita : {
*(.lita)
}
.sdata : {
*(.sdata)
}
_EDATA = .;
_FBSS = .;
.sbss : {
*(.sbss)
*(.scommon)
. = ALIGN(16);
}
.bss : {
*(.bss)
*(COMMON)
}
_end = .;
}
/defconfig
0,0 → 1,234
#
# Automatically generated make config: don't edit
#
 
#
# Code maturity level options
#
# CONFIG_EXPERIMENTAL is not set
 
#
# Loadable module support
#
CONFIG_MODULES=y
# CONFIG_MODVERSIONS is not set
# CONFIG_KERNELD is not set
 
#
# General setup
#
CONFIG_NATIVE=y
# CONFIG_ALPHA_AVANTI is not set
# CONFIG_ALPHA_XL is not set
# CONFIG_ALPHA_XLT is not set
# CONFIG_ALPHA_CABRIOLET is not set
# CONFIG_ALPHA_EB66 is not set
# CONFIG_ALPHA_EB66P is not set
# CONFIG_ALPHA_EB64P is not set
# CONFIG_ALPHA_EB164 is not set
# CONFIG_ALPHA_PC164 is not set
# CONFIG_ALPHA_JENSEN is not set
# CONFIG_ALPHA_NONAME is not set
# CONFIG_ALPHA_MIKASA is not set
CONFIG_ALPHA_ALCOR=y
# CONFIG_ALPHA_P2K is not set
CONFIG_PCI=y
CONFIG_ALPHA_EV5=y
CONFIG_ALPHA_CIA=y
CONFIG_ALPHA_SRM=y
CONFIG_KGDB=y
CONFIG_KGDB_TRACING=n
# CONFIG_SERIAL_ECHO is not set
CONFIG_TGA_CONSOLE=y
CONFIG_VGA_CONSOLE=y
CONFIG_NET=y
CONFIG_SYSVIPC=y
CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_EM86=y
 
#
# Floppy, IDE, and other block devices
#
CONFIG_BLK_DEV_FD=y
# CONFIG_BLK_DEV_IDE is not set
 
#
# Please see Documentation/ide.txt for help/info on IDE drives
#
# CONFIG_BLK_DEV_HD_ONLY is not set
 
#
# Additional Block Devices
#
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_MD is not set
CONFIG_BLK_DEV_RAM=y
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_BLK_DEV_XD is not set
# CONFIG_BLK_DEV_HD is not set
 
#
# Networking options
#
# CONFIG_FIREWALL is not set
# CONFIG_NET_ALIAS is not set
CONFIG_INET=y
# CONFIG_IP_FORWARD is not set
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ACCT is not set
 
#
# (it is safe to leave these untouched)
#
# CONFIG_INET_PCTCP is not set
# CONFIG_INET_RARP is not set
# CONFIG_NO_PATH_MTU_DISCOVERY is not set
CONFIG_IP_NOSR=y
CONFIG_SKB_LARGE=y
 
#
#
#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_AX25 is not set
# CONFIG_NETLINK is not set
 
#
# SCSI support
#
CONFIG_SCSI=y
 
#
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=y
# CONFIG_CHR_DEV_ST is not set
CONFIG_BLK_DEV_SR=y
# CONFIG_CHR_DEV_SG is not set
 
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
# CONFIG_SCSI_MULTI_LUN is not set
CONFIG_SCSI_CONSTANTS=y
 
#
# SCSI low-level drivers
#
# CONFIG_SCSI_7000FASST is not set
# CONFIG_SCSI_AHA152X is not set
# CONFIG_SCSI_AHA1542 is not set
# CONFIG_SCSI_AHA1740 is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_IN2000 is not set
# CONFIG_SCSI_AM53C974 is not set
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DTC3280 is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_EATA_DMA is not set
# CONFIG_SCSI_EATA_PIO is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GENERIC_NCR5380 is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_NCR53C7xx is not set
# CONFIG_SCSI_NCR53C8XX is not set
# CONFIG_SCSI_PPA is not set
# CONFIG_SCSI_PAS16 is not set
# CONFIG_SCSI_QLOGIC_FAS is not set
CONFIG_SCSI_QLOGIC_ISP=y
# CONFIG_SCSI_SEAGATE is not set
# CONFIG_SCSI_T128 is not set
# CONFIG_SCSI_U14_34F is not set
# CONFIG_SCSI_ULTRASTOR is not set
 
#
# Network device support
#
CONFIG_NETDEVICES=y
CONFIG_DUMMY=m
# CONFIG_EQUALIZER is not set
# CONFIG_PLIP is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NET_RADIO is not set
CONFIG_NET_ETHERNET=y
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_LANCE is not set
# CONFIG_NET_VENDOR_SMC is not set
# CONFIG_NET_ISA is not set
CONFIG_NET_PCI=y
# CONFIG_APRICOT is not set
CONFIG_DE4X5=y
# CONFIG_DEC_ELCP is not set
# CONFIG_DGRS is not set
# CONFIG_NET_POCKET is not set
# CONFIG_TR is not set
# CONFIG_FDDI is not set
# CONFIG_ARCNET is not set
 
#
# ISDN subsystem
#
# CONFIG_ISDN is not set
CONFIG_HISAX_EURO=y
 
#
# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
#
# CONFIG_CD_NO_IDESCSI is not set
 
#
# Filesystems
#
# CONFIG_QUOTA is not set
# CONFIG_MINIX_FS is not set
# CONFIG_EXT_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_XIA_FS is not set
CONFIG_FAT_FS=y
CONFIG_MSDOS_FS=y
# CONFIG_VFAT_FS is not set
# CONFIG_UMSDOS_FS is not set
CONFIG_PROC_FS=y
CONFIG_NFS_FS=y
# CONFIG_ROOT_NFS is not set
# CONFIG_SMB_FS is not set
CONFIG_ISO9660_FS=y
# CONFIG_HPFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
# CONFIG_AUTOFS_FS is not set
 
#
# Character devices
#
CONFIG_SERIAL=y
# CONFIG_DIGI is not set
# CONFIG_CYCLADES is not set
# CONFIG_STALDRV is not set
# CONFIG_RISCOM8 is not set
# CONFIG_PRINTER is not set
CONFIG_MOUSE=y
# CONFIG_ATIXL_BUSMOUSE is not set
# CONFIG_BUSMOUSE is not set
# CONFIG_MS_BUSMOUSE is not set
CONFIG_PSMOUSE=y
# CONFIG_82C710_MOUSE is not set
# CONFIG_UMISC is not set
# CONFIG_QIC02_TAPE is not set
# CONFIG_FTAPE is not set
# CONFIG_WATCHDOG is not set
# CONFIG_RTC is not set
 
#
# Sound
#
# CONFIG_SOUND is not set
 
#
# Kernel hacking
#
# CONFIG_PROFILE is not set
/lib/checksum.c
0,0 → 1,148
/*
* arch/alpha/lib/checksum.c
*
* This file contains network checksum routines that are better done
* in an architecture-specific manner due to speed..
*/
#include <linux/string.h>
 
#include <asm/byteorder.h>
 
static inline unsigned short from64to16(unsigned long x)
{
/* add up 32-bit words for 33 bits */
x = (x & 0xffffffff) + (x >> 32);
/* add up 16-bit and 17-bit words for 17+c bits */
x = (x & 0xffff) + (x >> 16);
/* add up 16-bit and 2-bit for 16+c bit */
x = (x & 0xffff) + (x >> 16);
/* add up carry.. */
x = (x & 0xffff) + (x >> 16);
return x;
}
 
/*
* computes the checksum of the TCP/UDP pseudo-header
* returns a 16-bit checksum, already complemented.
*/
unsigned short int csum_tcpudp_magic(unsigned long saddr,
unsigned long daddr,
unsigned short len,
unsigned short proto,
unsigned int sum)
{
return ~from64to16(saddr + daddr + sum +
((unsigned long) ntohs(len) << 16) +
((unsigned long) proto << 8));
}
 
/*
* Do a 64-bit checksum on an arbitrary memory area..
*
* This isn't a great routine, but it's not _horrible_ either. The
* inner loop could be unrolled a bit further, and there are better
* ways to do the carry, but this is reasonable.
*/
static inline unsigned long do_csum(unsigned char * buff, int len)
{
int odd, count;
unsigned long result = 0;
 
if (len <= 0)
goto out;
odd = 1 & (unsigned long) buff;
if (odd) {
result = *buff << 8;
len--;
buff++;
}
count = len >> 1; /* nr of 16-bit words.. */
if (count) {
if (2 & (unsigned long) buff) {
result += *(unsigned short *) buff;
count--;
len -= 2;
buff += 2;
}
count >>= 1; /* nr of 32-bit words.. */
if (count) {
if (4 & (unsigned long) buff) {
result += *(unsigned int *) buff;
count--;
len -= 4;
buff += 4;
}
count >>= 1; /* nr of 64-bit words.. */
if (count) {
unsigned long carry = 0;
do {
unsigned long w = *(unsigned long *) buff;
count--;
buff += 8;
result += carry;
result += w;
carry = (w > result);
} while (count);
result += carry;
result = (result & 0xffffffff) + (result >> 32);
}
if (len & 4) {
result += *(unsigned int *) buff;
buff += 4;
}
}
if (len & 2) {
result += *(unsigned short *) buff;
buff += 2;
}
}
if (len & 1)
result += *buff;
result = from64to16(result);
if (odd)
result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
out:
return result;
}
 
/*
* This is a version of ip_compute_csum() optimized for IP headers,
* which always checksum on 4 octet boundaries.
*/
unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl)
{
return ~do_csum(iph,ihl*4);
}
 
/*
* computes the checksum of a memory block at buff, length len,
* and adds in "sum" (32-bit)
*
* returns a 32-bit number suitable for feeding into itself
* or csum_tcpudp_magic
*
* this function must be called with even lengths, except
* for the last fragment, which may be odd
*
* it's best to have buff aligned on a 32-bit boundary
*/
unsigned int csum_partial(unsigned char * buff, int len, unsigned int sum)
{
unsigned long result = do_csum(buff, len);
 
/* add in old sum, and carry.. */
result += sum;
/* 32+c bits -> 32 bits */
result = (result & 0xffffffff) + (result >> 32);
return result;
}
 
/*
* this routine is used for miscellaneous IP-like checksums, mainly
* in icmp.c
*/
unsigned short ip_compute_csum(unsigned char * buff, int len)
{
return ~from64to16(do_csum(buff,len));
}
/lib/divide.S
0,0 → 1,160
/*
* arch/alpha/lib/divide.S
*
* (C) 1995 Linus Torvalds
*
* Alpha division..
*/
 
/*
* The alpha chip doesn't provide hardware division, so we have to do it
* by hand. The compiler expects the functions
*
* __divqu: 64-bit unsigned long divide
* __remqu: 64-bit unsigned long remainder
* __divqs/__remqs: signed 64-bit
* __divlu/__remlu: unsigned 32-bit
* __divls/__remls: signed 32-bit
*
* These are not normal C functions: instead of the normal
* calling sequence, these expect their arguments in registers
* $24 and $25, and return the result in $27. Register $28 may
* be clobbered (assembly temporary), anything else must be saved.
*
* In short: painful.
*
* This is a rather simple bit-at-a-time algorithm: it's very good
* at dividing random 64-bit numbers, but the more usual case where
* the divisor is small is handled better by the DEC algorithm
* using lookup tables. This uses much less memory, though, and is
* nicer on the cache.. Besides, I don't know the copyright status
* of the DEC code.
*/
 
/*
* My temporaries:
* $0 - current bit
* $1 - shifted divisor
* $2 - modulus/quotient
*
* $23 - return address
* $24 - dividend
* $25 - divisor
*
* $27 - quotient/modulus
* $28 - compare status
*/
 
#define halt .long 0
 
/*
* Select function type and registers
*/
#define mask $0
#define divisor $1
#define compare $28
 
#ifdef DIV
#define func(x) __div##x
#define modulus $2
#define quotient $27
#define GETSIGN(x) xor $24,$25,x
#else
#define func(x) __rem##x
#define modulus $27
#define quotient $2
#define GETSIGN(x) bis $24,$24,x
#endif
 
/*
* For 32-bit operations, we need to extend to 64-bit
*/
#ifdef INTSIZE
#define ufunction func(lu)
#define sfunction func(l)
#define LONGIFY(x) zapnot x,15,x
#define SLONGIFY(x) addl x,0,x
#else
#define ufunction func(qu)
#define sfunction func(q)
#define LONGIFY(x)
#define SLONGIFY(x)
#endif
 
.set noat
.globl ufunction
.ent ufunction
ufunction:
subq $30,32,$30
stq $0, 0($30)
stq $1, 8($30)
stq $2,16($30)
 
bis $25,$25,divisor
bis $24,$24,modulus
bis $31,$31,quotient
LONGIFY(divisor)
LONGIFY(modulus)
beq divisor, 9f /* div by zero */
bis $31,1,mask
 
/* shift divisor left */
1: cmpult divisor,modulus,compare
blt divisor, 3f
addq divisor,divisor,divisor
addq mask,mask,mask
bne compare,1b
 
/* ok, start to go right again.. */
2: srl divisor,1,divisor
beq mask,9f
srl mask,1,mask
3: cmpule divisor,modulus,compare
beq compare,2b
addq quotient,mask,quotient
beq mask,9f
subq modulus,divisor,modulus
br 2b
 
9: ldq $0, 0($30)
ldq $1, 8($30)
ldq $2, 16($30)
addq $30,32,$30
ret $31,($23),1
.end ufunction
 
/*
* Uhh.. Ugly signed division. I'd rather not have it at all, but
* it's needed in some circumstances. There are different ways to
* handle this, really. This does:
* -a / b = a / -b = -(a / b)
* -a % b = -(a % b)
* a % -b = a % b
* which is probably not the best solution, but at least should
* have the property that (x/y)*y + (x%y) = x.
*/
.globl sfunction
.ent sfunction
sfunction:
bis $24,$25,$28
SLONGIFY($28)
bge $28,ufunction
subq $30,32,$30
stq $23,0($30)
stq $24,8($30)
stq $25,16($30)
subq $31,$24,$28
cmovlt $24,$28,$24 /* abs($24) */
subq $31,$25,$28
cmovlt $25,$28,$25 /* abs($25) */
bsr $23,ufunction
ldq $23,0($30)
ldq $24,8($30)
ldq $25,16($30)
addq $30,32,$30
GETSIGN($28)
SLONGIFY($28)
bge $28,1f
subq $31,$27,$27
1: ret $31,($23),1
.end sfunction
/lib/io.c
0,0 → 1,401
/*
* Alpha IO and memory functions.. Just expand the inlines in the header
* files..
*/
#include <linux/kernel.h>
 
#include <asm/io.h>
 
/*
* Jensen has a separate "local" and "bus" IO space for
* byte-wide IO.
*/
#ifdef __is_local
 
unsigned int _bus_inb(unsigned long addr)
{
return __bus_inb(addr);
}
 
void _bus_outb(unsigned char b, unsigned long addr)
{
__bus_outb(b, addr);
}
#endif
 
unsigned int _inb(unsigned long addr)
{
return __inb(addr);
}
 
unsigned int _inw(unsigned long addr)
{
return __inw(addr);
}
 
unsigned int _inl(unsigned long addr)
{
return __inl(addr);
}
 
 
void _outb(unsigned char b, unsigned long addr)
{
__outb(b, addr);
}
 
void _outw(unsigned short b, unsigned long addr)
{
__outw(b, addr);
}
 
void _outl(unsigned int b, unsigned long addr)
{
__outl(b, addr);
}
 
 
unsigned long _readb(unsigned long addr)
{
return __readb(addr);
}
 
unsigned long _readw(unsigned long addr)
{
return __readw(addr);
}
 
unsigned long _readl(unsigned long addr)
{
return __readl(addr);
}
 
 
void _writeb(unsigned char b, unsigned long addr)
{
__writeb(b, addr);
}
 
void _writew(unsigned short b, unsigned long addr)
{
__writew(b, addr);
}
 
void _writel(unsigned int b, unsigned long addr)
{
__writel(b, addr);
}
 
/*
* Read COUNT 8-bit bytes from port PORT into memory starting at
* SRC.
*/
void insb (unsigned long port, void *dst, unsigned long count)
{
while (((unsigned long)dst) & 0x3) {
if (!count)
return;
count--;
*(unsigned char *) dst = inb(port);
((unsigned char *) dst)++;
}
 
while (count >= 4) {
unsigned int w;
count -= 4;
w = inb(port);
w |= inb(port) << 8;
w |= inb(port) << 16;
w |= inb(port) << 24;
*(unsigned int *) dst = w;
((unsigned int *) dst)++;
}
 
while (count) {
--count;
*(unsigned char *) dst = inb(port);
((unsigned char *) dst)++;
}
}
 
 
/*
* Read COUNT 16-bit words from port PORT into memory starting at
* SRC. SRC must be at least short aligned. This is used by the
* IDE driver to read disk sectors. Performance is important, but
* the interfaces seems to be slow: just using the inlined version
* of the inw() breaks things.
*/
void insw (unsigned long port, void *dst, unsigned long count)
{
if (((unsigned long)dst) & 0x3) {
if (((unsigned long)dst) & 0x1) {
panic("insw: memory not short aligned");
}
if (!count)
return;
count--;
*(unsigned short* ) dst = inw(port);
((unsigned short *) dst)++;
}
 
while (count >= 2) {
unsigned int w;
count -= 2;
w = inw(port);
w |= inw(port) << 16;
*(unsigned int *) dst = w;
((unsigned int *) dst)++;
}
 
if (count) {
*(unsigned short*) dst = inw(port);
}
}
 
 
/*
* Read COUNT 32-bit words from port PORT into memory starting at
* SRC. Now works with any alignment in SRC. Performance is important,
* but the interfaces seems to be slow: just using the inlined version
* of the inl() breaks things.
*/
void insl (unsigned long port, void *dst, unsigned long count)
{
unsigned int l = 0, l2;
if (!count)
return;
switch (((unsigned long) dst) & 0x3)
{
case 0x00: /* Buffer 32-bit aligned */
while (count--)
{
*(unsigned int *) dst = inl(port);
((unsigned int *) dst)++;
}
break;
/* Assuming little endian Alphas in cases 0x01 -- 0x03 ... */
case 0x02: /* Buffer 16-bit aligned */
--count;
l = inl(port);
*(unsigned short *) dst = l;
((unsigned short *) dst)++;
while (count--)
{
l2 = inl(port);
*(unsigned int *) dst = l >> 16 | l2 << 16;
((unsigned int *) dst)++;
l = l2;
}
*(unsigned short *) dst = l >> 16;
break;
case 0x01: /* Buffer 8-bit aligned */
--count;
l = inl(port);
*(unsigned char *) dst = l;
((unsigned char *) dst)++;
*(unsigned short *) dst = l >> 8;
((unsigned short *) dst)++;
while (count--)
{
l2 = inl(port);
*(unsigned int *) dst = l >> 24 | l2 << 8;
((unsigned int *) dst)++;
l = l2;
}
*(unsigned char *) dst = l >> 24;
break;
case 0x03: /* Buffer 8-bit aligned */
--count;
l = inl(port);
*(unsigned char *) dst = l;
((unsigned char *) dst)++;
while (count--)
{
l2 = inl(port);
*(unsigned int *) dst = l << 24 | l2 >> 8;
((unsigned int *) dst)++;
l = l2;
}
*(unsigned short *) dst = l >> 8;
((unsigned short *) dst)++;
*(unsigned char *) dst = l >> 24;
break;
}
}
 
 
/*
* Like insb but in the opposite direction.
* Don't worry as much about doing aligned memory transfers:
* doing byte reads the "slow" way isn't nearly as slow as
* doing byte writes the slow way (no r-m-w cycle).
*/
void outsb(unsigned long port, const void * src, unsigned long count)
{
while (count) {
count--;
outb(*(char *)src, port);
((char *) src)++;
}
}
 
/*
* Like insw but in the opposite direction. This is used by the IDE
* driver to write disk sectors. Performance is important, but the
* interfaces seems to be slow: just using the inlined version of the
* outw() breaks things.
*/
void outsw (unsigned long port, const void *src, unsigned long count)
{
if (((unsigned long)src) & 0x3) {
if (((unsigned long)src) & 0x1) {
panic("outsw: memory not short aligned");
}
outw(*(unsigned short*)src, port);
((unsigned short *) src)++;
--count;
}
 
while (count >= 2) {
unsigned int w;
count -= 2;
w = *(unsigned int *) src;
((unsigned int *) src)++;
outw(w >> 0, port);
outw(w >> 16, port);
}
 
if (count) {
outw(*(unsigned short *) src, port);
}
}
 
 
/*
* Like insl but in the opposite direction. This is used by the IDE
* driver to write disk sectors. Works with any alignment in SRC.
* Performance is important, but the interfaces seems to be slow:
* just using the inlined version of the outl() breaks things.
*/
void outsl (unsigned long port, const void *src, unsigned long count)
{
unsigned int l = 0, l2;
if (!count)
return;
switch (((unsigned long) src) & 0x3)
{
case 0x00: /* Buffer 32-bit aligned */
while (count--)
{
outl(*(unsigned int *) src, port);
((unsigned int *) src)++;
}
break;
/* Assuming little endian Alphas in cases 0x01 -- 0x03 ... */
case 0x02: /* Buffer 16-bit aligned */
--count;
l = *(unsigned short *) src << 16;
((unsigned short *) src)++;
while (count--)
{
l2 = *(unsigned int *) src;
((unsigned int *) src)++;
outl (l >> 16 | l2 << 16, port);
l = l2;
}
l2 = *(unsigned short *) src;
outl (l >> 16 | l2 << 16, port);
break;
case 0x01: /* Buffer 8-bit aligned */
--count;
l = *(unsigned char *) src << 8;
((unsigned char *) src)++;
l |= *(unsigned short *) src << 16;
((unsigned short *) src)++;
while (count--)
{
l2 = *(unsigned int *) src;
((unsigned int *) src)++;
outl (l >> 8 | l2 << 24, port);
l = l2;
}
l2 = *(unsigned char *) src;
outl (l >> 8 | l2 << 24, port);
break;
case 0x03: /* Buffer 8-bit aligned */
--count;
l = *(unsigned char *) src << 24;
((unsigned char *) src)++;
while (count--)
{
l2 = *(unsigned int *) src;
((unsigned int *) src)++;
outl (l >> 24 | l2 << 8, port);
l = l2;
}
l2 = *(unsigned short *) src;
((unsigned short *) src)++;
l2 |= *(unsigned char *) src << 16;
outl (l >> 24 | l2 << 8, port);
break;
}
}
 
 
/*
* Copy data from IO memory space to "real" memory space.
* This needs to be optimized.
*/
void _memcpy_fromio(void * to, unsigned long from, unsigned long count)
{
while (count) {
count--;
*(char *) to = readb(from);
((char *) to)++;
from++;
}
}
 
/*
* Copy data from "real" memory space to IO memory space.
* This needs to be optimized.
*/
void _memcpy_toio(unsigned long to, void * from, unsigned long count)
{
while (count) {
count--;
writeb(*(char *) from, to);
((char *) from)++;
to++;
}
}
 
/*
* "memset" on IO memory space.
* This needs to be optimized.
*/
void _memset_io(unsigned long dst, int c, unsigned long count)
{
while (count) {
count--;
writeb(c, dst);
dst++;
}
}
/lib/strlen.S
0,0 → 1,57
/*
* strlen.S (c) 1995 David Mosberger (davidm@cs.arizona.edu)
*
* Finds length of a 0-terminated string. Optimized for the
* Alpha architecture:
*
* - memory accessed as aligned quadwords only
* - uses bcmpge to compare 8 bytes in parallel
* - does binary search to find 0 byte in last
* quadword (HAKMEM needed 12 instructions to
* do this instead of the 9 instructions that
* binary search needs).
*/
 
.set noreorder
.set noat
 
.align 3
 
.globl strlen
.ent strlen
 
strlen:
ldq_u $1, 0($16) # load first quadword ($16 may be misaligned)
lda $2, -1($31)
insqh $2, $16, $2
andnot $16, 7, $0
or $2, $1, $1
cmpbge $31, $1, $2 # $2 <- bitmask: bit i == 1 <==> i-th byte == 0
bne $2, found
 
loop: ldq $1, 8($0)
addq $0, 8, $0 # addr += 8
nop # helps dual issue last two insns
cmpbge $31, $1, $2
beq $2, loop
 
found: blbs $2, done # make aligned case fast
negq $2, $3
and $2, $3, $2
 
and $2, 0x0f, $1
addq $0, 4, $3
cmoveq $1, $3, $0
 
and $2, 0x33, $1
addq $0, 2, $3
cmoveq $1, $3, $0
 
and $2, 0x55, $1
addq $0, 1, $3
cmoveq $1, $3, $0
 
done: subq $0, $16, $0
ret $31, ($26)
 
.end strlen
/lib/memcpy.c
0,0 → 1,135
/*
* linux/arch/alpha/lib/memcpy.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
/*
* This is a reasonably optimized memcpy() routine.
*/
 
/*
* Note that the C code is written to be optimized into good assembly. However,
* at this point gcc is unable to sanely compile "if (n >= 0)", resulting in a
* explicit compare against 0 (instead of just using the proper "blt reg, xx" or
* "bge reg, xx"). I hope alpha-gcc will be fixed to notice this eventually..
*/
 
#include <linux/types.h>
 
/*
* This should be done in one go with ldq_u*2/mask/stq_u. Do it
* with a macro so that we can fix it up later..
*/
#define ALIGN_DEST_TO8(d,s,n) \
while (d & 7) { \
if (n <= 0) return; \
n--; \
*(char *) d = *(char *) s; \
d++; s++; \
}
 
/*
* This should similarly be done with ldq_u*2/mask/stq. The destination
* is aligned, but we don't fill in a full quad-word
*/
#define DO_REST(d,s,n) \
while (n > 0) { \
n--; \
*(char *) d = *(char *) s; \
d++; s++; \
}
 
/*
* This should be done with ldq/mask/stq. The source and destination are
* aligned, but we don't fill in a full quad-word
*/
#define DO_REST_ALIGNED(d,s,n) DO_REST(d,s,n)
 
/*
* This does unaligned memory copies. We want to avoid storing to
* an unaligned address, as that would do a read-modify-write cycle.
* We also want to avoid double-reading the unaligned reads.
*
* Note the ordering to try to avoid load (and address generation) latencies.
*/
static inline void __memcpy_unaligned(unsigned long d, unsigned long s, long n)
{
ALIGN_DEST_TO8(d,s,n);
n -= 8; /* to avoid compare against 8 in the loop */
if (n >= 0) {
unsigned long low_word, high_word;
__asm__("ldq_u %0,%1":"=r" (low_word):"m" (*(unsigned long *) s));
do {
unsigned long tmp;
__asm__("ldq_u %0,%1":"=r" (high_word):"m" (*(unsigned long *)(s+8)));
n -= 8;
__asm__("extql %1,%2,%0"
:"=r" (low_word)
:"r" (low_word), "r" (s));
__asm__("extqh %1,%2,%0"
:"=r" (tmp)
:"r" (high_word), "r" (s));
s += 8;
*(unsigned long *) d = low_word | tmp;
d += 8;
low_word = high_word;
} while (n >= 0);
}
n += 8;
DO_REST(d,s,n);
}
 
/*
* Hmm.. Strange. The __asm__ here is there to make gcc use a integer register
* for the load-store. I don't know why, but it would seem that using a floating
* point register for the move seems to slow things down (very small difference,
* though).
*
* Note the ordering to try to avoid load (and address generation) latencies.
*/
static inline void __memcpy_aligned(unsigned long d, unsigned long s, long n)
{
ALIGN_DEST_TO8(d,s,n);
n -= 8;
while (n >= 0) {
unsigned long tmp;
__asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s));
n -= 8;
s += 8;
*(unsigned long *) d = tmp;
d += 8;
}
n += 8;
DO_REST_ALIGNED(d,s,n);
}
 
void * __memcpy(void * dest, const void *src, size_t n)
{
if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) {
__memcpy_aligned((unsigned long) dest, (unsigned long) src, n);
return dest;
}
__memcpy_unaligned((unsigned long) dest, (unsigned long) src, n);
return dest;
}
 
/*
* Broken compiler uses "bcopy" to do internal
* assignments. Silly OSF/1 BSDism.
*/
char * bcopy(const char * src, char * dest, size_t n)
{
__memcpy(dest, src, n);
return dest;
}
 
/*
* gcc-2.7.1 and newer generate calls to memset and memcpy. So we
* need to define that here:
*/
#ifdef __ELF__
asm (".weak memcpy; memcpy = __memcpy");
#else
asm (".weakext memcpy, __memcpy");
#endif
/lib/memset.S
0,0 → 1,110
/*
* linux/arch/alpha/memset.S
*
* This is an efficient (and small) implementation of the C library "memset()"
* function for the alpha.
*
* (C) Copyright 1996 Linus Torvalds
*
* This routine is "moral-ware": you are free to use it any way you wish, and
* the only obligation I put on you is a moral one: if you make any improvements
* to the routine, please send me your improvements for me to use similarly.
*
* The scheduling comments are according to the EV5 documentation (and done by
* hand, so they might well be incorrect, please do tell me about it..)
*/
 
.set noat
.set noreorder
.text
.globl __memset
.globl __constant_c_memset
.ent __memset
.align 5
__memset:
.frame $30,0,$26,0
.prologue 0
 
zapnot $17,1,$17 /* E0 */
sll $17,8,$1 /* E1 (p-c latency, next cycle) */
bis $17,$1,$17 /* E0 (p-c latency, next cycle) */
sll $17,16,$1 /* E1 (p-c latency, next cycle) */
 
bis $17,$1,$17 /* E0 (p-c latency, next cycle) */
sll $17,32,$1 /* E1 (p-c latency, next cycle) */
bis $17,$1,$17 /* E0 (p-c latency, next cycle) */
ldq_u $31,0($30) /* .. E1 */
 
.align 5
__constant_c_memset:
addq $18,$16,$6 /* E0 */
bis $16,$16,$0 /* .. E1 */
xor $16,$6,$1 /* E0 */
ble $18,end /* .. E1 */
 
bic $1,7,$1 /* E0 */
beq $1,within_one_quad /* .. E1 (note EV5 zero-latency forwarding) */
and $16,7,$3 /* E0 */
beq $3,aligned /* .. E1 (note EV5 zero-latency forwarding) */
 
ldq_u $4,0($16) /* E0 */
bis $16,$16,$5 /* .. E1 */
insql $17,$16,$2 /* E0 */
subq $3,8,$3 /* .. E1 */
 
addq $18,$3,$18 /* E0 $18 is new count ($3 is negative) */
mskql $4,$16,$4 /* .. E1 (and possible load stall) */
subq $16,$3,$16 /* E0 $16 is new aligned destination */
bis $2,$4,$1 /* .. E1 */
 
bis $31,$31,$31 /* E0 */
ldq_u $31,0($30) /* .. E1 */
stq_u $1,0($5) /* E0 */
bis $31,$31,$31 /* .. E1 */
 
.align 4
aligned:
sra $18,3,$3 /* E0 */
and $18,7,$18 /* .. E1 */
bis $16,$16,$5 /* E0 */
beq $3,no_quad /* .. E1 */
 
.align 3
loop:
stq $17,0($5) /* E0 */
subq $3,1,$3 /* .. E1 */
addq $5,8,$5 /* E0 */
bne $3,loop /* .. E1 */
 
no_quad:
bis $31,$31,$31 /* E0 */
beq $18,end /* .. E1 */
ldq $7,0($5) /* E0 */
mskqh $7,$6,$2 /* .. E1 (and load stall) */
 
insqh $17,$6,$4 /* E0 */
bis $2,$4,$1 /* .. E1 */
stq $1,0($5) /* E0 */
ret $31,($26),1 /* .. E1 */
 
.align 3
within_one_quad:
ldq_u $1,0($16) /* E0 */
insql $17,$16,$2 /* E1 */
mskql $1,$16,$4 /* E0 (after load stall) */
bis $2,$4,$2 /* E0 */
 
mskql $2,$6,$4 /* E0 */
mskqh $1,$6,$2 /* .. E1 */
bis $2,$4,$1 /* E0 */
stq_u $1,0($16) /* E0 */
 
end:
ret $31,($26),1 /* E1 */
.end __memset
 
#ifdef __ELF__
.weak memset; memset = __memset
#else
.weakext memset, __memset
#endif
/lib/Makefile
0,0 → 1,28
#
# Makefile for alpha-specific library files..
#
 
OBJS = __divqu.o __remqu.o __divlu.o __remlu.o memset.o memcpy.o io.o \
checksum.o csum_partial_copy.o strlen.o
 
lib.a: $(OBJS)
$(AR) rcs lib.a $(OBJS)
 
memset.o: memset.S
$(CC) -c -o memset.o memset.S
 
__divqu.o: divide.S
$(CC) -DDIV -c -o __divqu.o divide.S
 
__remqu.o: divide.S
$(CC) -DREM -c -o __remqu.o divide.S
 
__divlu.o: divide.S
$(CC) -DDIV -DINTSIZE -c -o __divlu.o divide.S
 
__remlu.o: divide.S
$(CC) -DREM -DINTSIZE -c -o __remlu.o divide.S
 
dep:
 
include $(TOPDIR)/Rules.make
/lib/csum_partial_copy.c
0,0 → 1,300
/*
* csum_partial_copy - do IP checksumming and copy
*
* (C) Copyright 1996 Linus Torvalds
*
* Don't look at this too closely - you'll go mad. The things
* we do for performance..
*/
 
#define ldq_u(x,y) \
__asm__ __volatile__("ldq_u %0,%1":"=r" (x):"m" (*(unsigned long *)(y)))
 
#define stq_u(x,y) \
__asm__ __volatile__("stq_u %1,%0":"=m" (*(unsigned long *)(y)):"r" (x))
 
#define extql(x,y,z) \
__asm__ __volatile__("extql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
 
#define extqh(x,y,z) \
__asm__ __volatile__("extqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
 
#define mskql(x,y,z) \
__asm__ __volatile__("mskql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
 
#define mskqh(x,y,z) \
__asm__ __volatile__("mskqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
 
#define insql(x,y,z) \
__asm__ __volatile__("insql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
 
#define insqh(x,y,z) \
__asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
 
/*
* Ok. This isn't fun, but this is the EASY case.
*/
static inline unsigned long csum_partial_copy_aligned(
unsigned long *src, unsigned long *dst,
long len, unsigned long checksum)
{
unsigned long carry = 0;
 
while (len >= 0) {
unsigned long word = *src;
checksum += carry;
src++;
checksum += word;
len -= 8;
carry = checksum < word;
*dst = word;
dst++;
}
len += 8;
checksum += carry;
if (len) {
unsigned long word, tmp;
word = *src;
tmp = *dst;
mskql(word, len, word);
checksum += word;
mskqh(tmp, len, tmp);
carry = checksum < word;
*dst = word | tmp;
checksum += carry;
}
return checksum;
}
 
/*
* This is even less fun, but this is still reasonably
* easy.
*/
static inline unsigned long csum_partial_copy_dest_aligned(
unsigned long *src, unsigned long *dst,
unsigned long soff,
long len, unsigned long checksum)
{
unsigned long first;
unsigned long word, carry;
unsigned long lastsrc = 7+len+(unsigned long)src;
 
ldq_u(first,src);
carry = 0;
while (len >= 0) {
unsigned long second;
 
ldq_u(second, src+1);
extql(first, soff, word);
len -= 8;
src++;
extqh(second, soff, first);
checksum += carry;
word |= first;
first = second;
checksum += word;
*dst = word;
dst++;
carry = checksum < word;
}
len += 8;
checksum += carry;
if (len) {
unsigned long tmp;
unsigned long second;
ldq_u(second, lastsrc);
tmp = *dst;
extql(first, soff, word);
extqh(second, soff, first);
word |= first;
mskql(word, len, word);
checksum += word;
mskqh(tmp, len, tmp);
carry = checksum < word;
*dst = word | tmp;
checksum += carry;
}
return checksum;
}
 
/*
* This is slightly less fun than the above..
*/
static inline unsigned long csum_partial_copy_src_aligned(
unsigned long *src, unsigned long *dst,
unsigned long doff,
long len, unsigned long checksum,
unsigned long partial_dest)
{
unsigned long carry = 0;
unsigned long word;
 
mskql(partial_dest, doff, partial_dest);
while (len >= 0) {
unsigned long second_dest;
word = *src;
len -= 8;
insql(word, doff, second_dest);
checksum += carry;
stq_u(partial_dest | second_dest, dst);
src++;
checksum += word;
insqh(word, doff, partial_dest);
carry = checksum < word;
dst++;
}
len += doff;
checksum += carry;
if (len >= 0) {
unsigned long second_dest;
word = *src;
mskql(word, len-doff, word);
checksum += word;
insql(word, doff, second_dest);
stq_u(partial_dest | second_dest, dst);
carry = checksum < word;
if (len) {
ldq_u(second_dest, dst+1);
insqh(word, doff, partial_dest);
mskqh(second_dest, len, second_dest);
stq_u(partial_dest | second_dest, dst+1);
}
checksum += carry;
} else if (len & 7) {
unsigned long second_dest;
word = *src;
ldq_u(second_dest, dst);
mskql(word, len-doff, word);
checksum += word;
mskqh(second_dest, len, second_dest);
carry = checksum < word;
insql(word, doff, word);
stq_u(partial_dest | word | second_dest, dst);
checksum += carry;
}
return checksum;
}
 
/*
* This is so totally un-fun that it's frightening. Don't
* look at this too closely, you'll go blind.
*/
static inline unsigned long csum_partial_copy_unaligned(
unsigned long * src, unsigned long * dst,
unsigned long soff, unsigned long doff,
long len, unsigned long checksum,
unsigned long partial_dest)
{
unsigned long carry = 0;
unsigned long first;
unsigned long lastsrc;
 
ldq_u(first, src);
lastsrc = 7+len+(unsigned long)src;
mskql(partial_dest, doff, partial_dest);
while (len >= 0) {
unsigned long second, word;
unsigned long second_dest;
 
ldq_u(second, src+1);
extql(first, soff, word);
checksum += carry;
len -= 8;
extqh(second, soff, first);
src++;
word |= first;
first = second;
insql(word, doff, second_dest);
checksum += word;
stq_u(partial_dest | second_dest, dst);
carry = checksum < word;
insqh(word, doff, partial_dest);
dst++;
}
len += doff;
checksum += carry;
if (len >= 0) {
unsigned long second, word;
unsigned long second_dest;
ldq_u(second, lastsrc);
extql(first, soff, word);
extqh(second, soff, first);
word |= first;
first = second;
mskql(word, len-doff, word);
checksum += word;
insql(word, doff, second_dest);
carry = checksum < word;
stq_u(partial_dest | second_dest, dst);
if (len) {
ldq_u(second_dest, dst+1);
insqh(word, doff, partial_dest);
mskqh(second_dest, len, second_dest);
stq_u(partial_dest | second_dest, dst+1);
}
checksum += carry;
} else if (len & 7) {
unsigned long second, word;
unsigned long second_dest;
 
ldq_u(second, lastsrc);
extql(first, soff, word);
extqh(second, soff, first);
word |= first;
ldq_u(second_dest, dst);
mskql(word, len-doff, word);
checksum += word;
mskqh(second_dest, len, second_dest);
carry = checksum < word;
insql(word, doff, word);
stq_u(partial_dest | word | second_dest, dst);
checksum += carry;
}
return checksum;
}
 
unsigned int csum_partial_copy(char *src, char *dst, int len, int sum)
{
unsigned long checksum = (unsigned) sum;
unsigned long soff = 7 & (unsigned long) src;
unsigned long doff = 7 & (unsigned long) dst;
 
if (len) {
if (!doff) {
if (!soff)
checksum = csum_partial_copy_aligned(
(unsigned long *) src,
(unsigned long *) dst,
len-8, checksum);
else
checksum = csum_partial_copy_dest_aligned(
(unsigned long *) src,
(unsigned long *) dst,
soff, len-8, checksum);
} else {
unsigned long partial_dest;
ldq_u(partial_dest, dst);
if (!soff)
checksum = csum_partial_copy_src_aligned(
(unsigned long *) src,
(unsigned long *) dst,
doff, len-8, checksum,
partial_dest);
else
checksum = csum_partial_copy_unaligned(
(unsigned long *) src,
(unsigned long *) dst,
soff, doff, len-8, checksum,
partial_dest);
}
/* 64 -> 33 bits */
checksum = (checksum & 0xffffffff) + (checksum >> 32);
/* 33 -> < 32 bits */
checksum = (checksum & 0xffff) + (checksum >> 16);
/* 32 -> 16 bits */
checksum = (checksum & 0xffff) + (checksum >> 16);
checksum = (checksum & 0xffff) + (checksum >> 16);
}
return checksum;
}
/mm/init.c
0,0 → 1,204
/*
* linux/arch/alpha/mm/init.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
#include <linux/config.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/swap.h>
 
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/hwrpb.h>
#include <asm/dma.h>
 
extern void die_if_kernel(char *,struct pt_regs *,long);
extern void show_net_buffers(void);
 
struct thread_struct * original_pcb_ptr;
 
/*
* BAD_PAGE is the page that is used for page faults when linux
* is out-of-memory. Older versions of linux just did a
* do_exit(), but using this instead means there is less risk
* for a process dying in kernel mode, possibly leaving a inode
* unused etc..
*
* BAD_PAGETABLE is the accompanying page-table: it is initialized
* to point to BAD_PAGE entries.
*
* ZERO_PAGE is a special page that is used for zero-initialized
* data and COW.
*/
pmd_t * __bad_pagetable(void)
{
memset((void *) EMPTY_PGT, 0, PAGE_SIZE);
return (pmd_t *) EMPTY_PGT;
}
 
pte_t __bad_page(void)
{
memset((void *) EMPTY_PGE, 0, PAGE_SIZE);
return pte_mkdirty(mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED));
}
 
void show_mem(void)
{
int i,free = 0,total = 0,reserved = 0;
int shared = 0;
 
printk("\nMem-info:\n");
show_free_areas();
printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
i = MAP_NR(high_memory);
while (i-- > 0) {
total++;
if (PageReserved(mem_map+i))
reserved++;
else if (!mem_map[i].count)
free++;
else
shared += mem_map[i].count-1;
}
printk("%d pages of RAM\n",total);
printk("%d free pages\n",free);
printk("%d reserved pages\n",reserved);
printk("%d pages shared\n",shared);
show_buffers();
#ifdef CONFIG_NET
show_net_buffers();
#endif
}
 
extern unsigned long free_area_init(unsigned long, unsigned long);
 
static struct thread_struct * load_PCB(struct thread_struct * pcb)
{
struct thread_struct *old_pcb;
 
__asm__ __volatile__(
"stq $30,0(%1)\n\t"
"bis %1,%1,$16\n\t"
"call_pal %2\n\t"
"bis $0,$0,%0"
: "=r" (old_pcb)
: "r" (pcb), "i" (PAL_swpctx)
: "$0", "$1", "$16", "$22", "$23", "$24", "$25");
return old_pcb;
}
 
/*
* paging_init() sets up the page tables: in the alpha version this actually
* unmaps the bootup page table (as we're now in KSEG, so we don't need it).
*/
unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
{
int i;
unsigned long newptbr;
struct memclust_struct * cluster;
struct memdesc_struct * memdesc;
 
/* initialize mem_map[] */
start_mem = free_area_init(start_mem, end_mem);
 
/* find free clusters, update mem_map[] accordingly */
memdesc = (struct memdesc_struct *)
(INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB);
cluster = memdesc->cluster;
for (i = memdesc->numclusters ; i > 0; i--, cluster++) {
unsigned long pfn, nr;
#if 0
printk("paging_init: cluster %d usage %ld start %ld size %ld\n",
i, cluster->usage, cluster->start_pfn, cluster->numpages);
#endif
if (cluster->usage & 1)
continue;
pfn = cluster->start_pfn;
nr = cluster->numpages;
 
/* non-volatile memory. We might want to mark this for later */
if (cluster->usage & 2)
continue;
 
while (nr--)
clear_bit(PG_reserved, &mem_map[pfn++].flags);
}
 
/* unmap the console stuff: we don't need it, and we don't want it */
/* Also set up the real kernel PCB while we're at it.. */
memset((void *) ZERO_PAGE, 0, PAGE_SIZE);
memset(swapper_pg_dir, 0, PAGE_SIZE);
newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT;
pgd_val(swapper_pg_dir[1023]) = (newptbr << 32) | pgprot_val(PAGE_KERNEL);
init_task.tss.ptbr = newptbr;
init_task.tss.pal_flags = 1; /* set FEN, clear everything else */
init_task.tss.flags = 0;
init_task.kernel_stack_page = INIT_STACK;
original_pcb_ptr = load_PCB(&init_task.tss);
 
flush_tlb_all();
return start_mem;
}
 
void mem_init(unsigned long start_mem, unsigned long end_mem)
{
unsigned long tmp;
 
end_mem &= PAGE_MASK;
high_memory = end_mem;
start_mem = PAGE_ALIGN(start_mem);
 
/*
* Mark the pages used by the kernel as reserved..
*/
tmp = KERNEL_START;
while (tmp < start_mem) {
set_bit(PG_reserved, &mem_map[MAP_NR(tmp)].flags);
tmp += PAGE_SIZE;
}
 
for (tmp = PAGE_OFFSET ; tmp < high_memory ; tmp += PAGE_SIZE) {
if (tmp >= MAX_DMA_ADDRESS)
clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags);
if (PageReserved(mem_map+MAP_NR(tmp)))
continue;
mem_map[MAP_NR(tmp)].count = 1;
free_page(tmp);
}
tmp = nr_free_pages << PAGE_SHIFT;
printk("Memory: %luk available\n", tmp >> 10);
return;
}
 
void si_meminfo(struct sysinfo *val)
{
int i;
 
i = MAP_NR(high_memory);
val->totalram = 0;
val->sharedram = 0;
val->freeram = nr_free_pages << PAGE_SHIFT;
val->bufferram = buffermem;
while (i-- > 0) {
if (PageReserved(mem_map+i))
continue;
val->totalram++;
if (!mem_map[i].count)
continue;
val->sharedram += mem_map[i].count-1;
}
val->totalram <<= PAGE_SHIFT;
val->sharedram <<= PAGE_SHIFT;
return;
}
/mm/fault.c
0,0 → 1,115
/*
* linux/arch/alpha/mm/fault.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
 
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/mmu_context.h>
 
unsigned long asn_cache = ASN_FIRST_VERSION;
 
#ifndef BROKEN_ASN
/*
* Select a new ASN and reload the context. This is
* not inlined as this expands to a pretty large
* function.
*/
void get_new_asn_and_reload(struct task_struct *tsk, struct mm_struct *mm)
{
get_new_mmu_context(tsk, mm, asn_cache);
reload_context(tsk);
}
#endif
 
extern void die_if_kernel(char *,struct pt_regs *,long);
 
/*
* This routine handles page faults. It determines the address,
* and the problem, and then passes it off to handle_mm_fault().
*
* mmcsr:
* 0 = translation not valid
* 1 = access violation
* 2 = fault-on-read
* 3 = fault-on-execute
* 4 = fault-on-write
*
* cause:
* -1 = instruction fetch
* 0 = load
* 1 = store
*/
asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, long cause,
unsigned long a3, unsigned long a4, unsigned long a5,
struct pt_regs regs)
{
struct vm_area_struct * vma;
struct task_struct *tsk = current;
struct mm_struct *mm = tsk->mm;
 
down(&mm->mmap_sem);
vma = find_vma(mm, address);
if (!vma)
goto bad_area;
if (vma->vm_start <= address)
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
if (expand_stack(vma, address))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
* we can handle it..
*/
good_area:
if (cause < 0) {
if (!(vma->vm_flags & VM_EXEC))
goto bad_area;
} else if (!cause) {
/* Allow reads even for write-only mappings */
if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
goto bad_area;
} else {
if (!(vma->vm_flags & VM_WRITE))
goto bad_area;
}
handle_mm_fault(vma, address, cause > 0);
up(&mm->mmap_sem);
return;
 
/*
* Something tried to access memory that isn't in our memory map..
* Fix it, but check if it's kernel or user first..
*/
bad_area:
up(&mm->mmap_sem);
if (user_mode(&regs)) {
printk("%s: memory violation at pc=%08lx rp=%08lx (bad address = %08lx)\n",
tsk->comm, regs.pc, regs.r26, address);
die_if_kernel("oops", &regs, cause);
force_sig(SIGSEGV, tsk);
return;
}
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
*/
printk(KERN_ALERT
"Unable to handle kernel paging request at virtual address %016lx\n", address);
die_if_kernel("Oops", &regs, cause);
do_exit(SIGKILL);
}
/mm/Makefile
0,0 → 1,15
#
# Makefile for the linux alpha-specific parts of the memory manager.
#
# 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 definition is now in the main makefile...
 
OBJS = init.o fault.o
 
mm.o: $(OBJS)
$(LD) -r -o mm.o $(OBJS)
 
include $(TOPDIR)/Rules.make
/config.in
0,0 → 1,248
#
# For a description of the syntax of this configuration file,
# see the Configure script.
#
mainmenu_name "Kernel configuration of Linux for Alpha machines"
 
# clear all implied options (don't want default values for those):
unset CONFIG_CROSSCOMPILE CONFIG_NATIVE
unset CONFIG_PCI CONFIG_ALPHA_EISA
unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA CONFIG_ALPHA_T2
unset CONFIG_ALPHA_PYXIS
unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5
unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION
unset CONFIG_ALPHA_SRM CONFIG_ALPHA_SRM_SETUP
 
mainmenu_option next_comment
comment 'Code maturity level options'
bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
endmenu
 
mainmenu_option next_comment
comment 'Loadable module support'
bool 'Enable loadable module support' CONFIG_MODULES
if [ "$CONFIG_MODULES" = "y" ]; then
MODULES=y
bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
fi
endmenu
 
mainmenu_option next_comment
comment 'General setup'
 
if [ "`uname`" != "Linux" ]; then
define_bool CONFIG_CROSSCOMPILE y
else
define_bool CONFIG_NATIVE y
fi
 
choice 'Alpha system type' \
"Avanti CONFIG_ALPHA_AVANTI \
Alpha-XL CONFIG_ALPHA_XL \
Alpha-XLT CONFIG_ALPHA_XLT \
Cabriolet CONFIG_ALPHA_CABRIOLET \
EB66 CONFIG_ALPHA_EB66 \
EB66+ CONFIG_ALPHA_EB66P \
EB64+ CONFIG_ALPHA_EB64P \
EB164 CONFIG_ALPHA_EB164 \
PC164 CONFIG_ALPHA_PC164 \
LX164 CONFIG_ALPHA_LX164 \
SX164 CONFIG_ALPHA_SX164 \
Jensen CONFIG_ALPHA_JENSEN \
Noname CONFIG_ALPHA_NONAME \
Takara CONFIG_ALPHA_TAKARA \
Mikasa CONFIG_ALPHA_MIKASA \
Noritake CONFIG_ALPHA_NORITAKE \
Alcor CONFIG_ALPHA_ALCOR \
Miata CONFIG_ALPHA_MIATA \
Sable CONFIG_ALPHA_SABLE \
AlphaBook1 CONFIG_ALPHA_BOOK1 \
Ruffian CONFIG_ALPHA_RUFFIAN \
Platform2000 CONFIG_ALPHA_P2K" Cabriolet
 
if [ "$CONFIG_ALPHA_BOOK1" = "y" ]
then
define_bool CONFIG_ALPHA_NONAME y
fi
if [ "$CONFIG_ALPHA_NONAME" = "y" -o "$CONFIG_ALPHA_EB66" = "y" \
-o "$CONFIG_ALPHA_EB66P" = "y" -o "$CONFIG_ALPHA_P2K" = "y" ]
then
define_bool CONFIG_PCI y
define_bool CONFIG_ALPHA_EV4 y
define_bool CONFIG_ALPHA_LCA y
fi
if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \
-o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_XL" = "y" ]
then
define_bool CONFIG_PCI y
define_bool CONFIG_ALPHA_EV4 y
define_bool CONFIG_ALPHA_APECS y
fi
if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
-o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" \
-o "$CONFIG_ALPHA_TAKARA" = "y" ]
then
define_bool CONFIG_PCI y
define_bool CONFIG_ALPHA_EV5 y
define_bool CONFIG_ALPHA_CIA y
fi
if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ]
then
bool 'Pinnacle or Primo CPU daughtercard?' CONFIG_ALPHA_PRIMO
if [ "$CONFIG_ALPHA_PRIMO" = "y" ]
then
define_bool CONFIG_ALPHA_EV5 y
define_bool CONFIG_ALPHA_CIA y
else
define_bool CONFIG_ALPHA_EV4 y
define_bool CONFIG_ALPHA_APECS y
fi
define_bool CONFIG_PCI y
fi
if [ "$CONFIG_ALPHA_SABLE" = "y" ]
then
bool 'EV5 or EV56 CPU daughtercard?' CONFIG_ALPHA_GAMMA
if [ "$CONFIG_ALPHA_GAMMA" = "y" ]
then
define_bool CONFIG_ALPHA_EV5 y
else
define_bool CONFIG_ALPHA_EV4 y
fi
define_bool CONFIG_PCI y
define_bool CONFIG_ALPHA_T2 y
fi
if [ "$CONFIG_ALPHA_MIATA" = "y" -o "$CONFIG_ALPHA_LX164" = "y" \
-o "$CONFIG_ALPHA_SX164" = "y" -o "$CONFIG_ALPHA_RUFFIAN" = "y" ]
then
define_bool CONFIG_PCI y
define_bool CONFIG_ALPHA_EV5 y
define_bool CONFIG_ALPHA_PYXIS y
fi
if [ "$CONFIG_ALPHA_JENSEN" = "y" ]
then
define_bool CONFIG_ALPHA_EV4 y
fi
if [ "$CONFIG_ALPHA_EV4" = "y" ]
then
# EV45 and older do not support all rounding modes in hw:
define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y
fi
 
if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \
-o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \
-o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \
-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \
-o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
-o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" ]
then
bool 'Use SRM as bootloader' CONFIG_ALPHA_SRM
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
if [ "$CONFIG_ALPHA_SRM" = "y" ]; then
bool ' Use SRM PCI setup' CONFIG_ALPHA_SRM_SETUP
fi
fi
fi
if [ "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \
-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ]
then
define_bool CONFIG_ALPHA_EISA y
fi
if [ "$CONFIG_ALPHA_XL" = "y" ]
then
define_bool CONFIG_ALPHA_AVANTI y
fi
 
bool 'Kernel GDB support' CONFIG_KGDB
if [ "$CONFIG_KGDB" = "y" ]; then
bool 'Kernel tracing support?' CONFIG_KGDB_TRACING
fi
 
bool 'Echo console messages on /dev/ttyS0 (COM1)' CONFIG_SERIAL_ECHO
if [ "$CONFIG_PCI" = "y" ]; then
bool 'TGA Console Support' CONFIG_TGA_CONSOLE
if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then
bool 'VGA Console Support' CONFIG_VGA_CONSOLE
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
fi
fi
bool 'Networking support' CONFIG_NET
bool 'System V IPC' CONFIG_SYSVIPC
tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86
endmenu
 
source drivers/block/Config.in
 
if [ "$CONFIG_NET" = "y" ]; then
source net/Config.in
fi
 
mainmenu_option next_comment
comment 'SCSI support'
 
tristate 'SCSI support' CONFIG_SCSI
 
if [ "$CONFIG_SCSI" != "n" ]; then
source drivers/scsi/Config.in
fi
endmenu
 
if [ "$CONFIG_NET" = "y" ]; then
mainmenu_option next_comment
comment 'Network device support'
 
bool 'Network device support' CONFIG_NETDEVICES
if [ "$CONFIG_NETDEVICES" = "y" ]; then
source drivers/net/Config.in
fi
endmenu
fi
 
mainmenu_option next_comment
comment 'ISDN subsystem'
 
tristate 'ISDN support' CONFIG_ISDN
if [ "$CONFIG_ISDN" != "n" ]; then
source drivers/isdn/Config.in
fi
endmenu
 
mainmenu_option next_comment
comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI
if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
source drivers/cdrom/Config.in
fi
endmenu
 
source fs/Config.in
 
source drivers/char/Config.in
 
mainmenu_option next_comment
comment 'Sound'
 
tristate 'Sound card support' CONFIG_SOUND
if [ "$CONFIG_SOUND" != "n" ]; then
source drivers/sound/Config.in
fi
endmenu
 
mainmenu_option next_comment
comment 'Kernel hacking'
 
#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
bool 'Kernel profiling support' CONFIG_PROFILE
if [ "$CONFIG_PROFILE" = "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
fi
endmenu
 
if [ "$CONFIG_TGA_CONSOLE" = "n" ]; then
define_bool CONFIG_VGA_CONSOLE y
fi
/Makefile
0,0 → 1,77
#
# alpha/Makefile
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 1994 by Linus Torvalds
#
 
NM := nm -B
 
ifdef CONFIG_CROSSCOMPILE
# enable this for linking under OSF/1:
LINKFLAGS = -non_shared -T 0xfffffc0000310000 -N
else
elf=$(shell if $(LD) --help | grep elf64alpha >/dev/null; then echo yes; fi)
ifeq ($(elf),yes)
LINKFLAGS = -static -Ttext 0xfffffc0000310000 -N
else
LINKFLAGS = -static -T arch/alpha/vmlinux.lds -N
endif
# GNU gcc/cc1/as can use pipes instead of temporary files
CFLAGS := $(CFLAGS) -pipe
endif
 
CFLAGS := $(CFLAGS) -mno-fp-regs
 
# determine if we can use the BWX instructions with GAS
dummy:=$(shell rm -f /tmp/GAS_VER)
#$(shell $(AS) --version >& /tmp/GAS_VER)
dummy:=$(shell $(AS) --version > /tmp/GAS_VER 2>&1)
OLD_GAS := $(shell if cat /tmp/GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi)
 
ifneq ($(OLD_GAS),yes)
CFLAGS := $(CFLAGS) -Wa,-m21164a -DBWX_USABLE
 
# if PYXIS, then enable use of BWIO space
ifeq ($(CONFIG_ALPHA_PYXIS),y)
CFLAGS := $(CFLAGS) -DBWIO_ENABLED
endif
endif
 
HEAD := arch/alpha/kernel/head.o
 
SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib \
arch/alpha/math-emu
ARCHIVES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(ARCHIVES)
LIBS := $(TOPDIR)/arch/alpha/math-emu/math-emu.a \
$(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a
 
MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
 
rawboot:
@$(MAKEBOOT) rawboot
 
#
# my boot writes directly to a specific disk partition, I doubt most
# people will want to do that without changes..
#
msb my-special-boot:
@$(MAKEBOOT) msb
 
bootimage:
@$(MAKEBOOT) bootimage
 
srmboot:
@$(MAKEBOOT) srmboot
 
archclean:
@$(MAKEBOOT) clean
 
archdep:
@$(MAKEBOOT) dep
 
bootpfile:
@$(MAKEBOOT) bootpfile
/math-emu/ieee-math.c
0,0 → 1,1344
/*
* ieee-math.c - IEEE floating point emulation code
* Copyright (C) 1989,1990,1991,1995 by
* Digital Equipment Corporation, Maynard, Massachusetts.
*
* Heavily modified for Linux/Alpha. Changes are Copyright (c) 1995
* by David Mosberger (davidm@azstarnet.com).
*
* This file may be redistributed according to the terms of the
* GNU General Public License.
*/
/*
* The original code did not have any comments. I have created many
* comments as I fix the bugs in the code. My comments are based on
* my observation and interpretation of the code. If the original
* author would have spend a few minutes to comment the code, we would
* never had a problem of misinterpretation. -HA
*
* This code could probably be a lot more optimized (especially the
* division routine). However, my foremost concern was to get the
* IEEE behavior right. Performance is less critical as these
* functions are used on exceptional numbers only (well, assuming you
* don't turn on the "trap on inexact"...).
*/
#include "ieee-math.h"
 
#define STICKY_S 0x20000000 /* both in longword 0 of fraction */
#define STICKY_T 1
 
/*
* Careful: order matters here!
*/
enum {
NaN, QNaN, INFTY, ZERO, DENORM, NORMAL
};
 
enum {
SINGLE, DOUBLE
};
 
typedef unsigned long fpclass_t;
 
#define IEEE_TMAX 0x7fefffffffffffff
#define IEEE_SMAX 0x47efffffe0000000
#define IEEE_SNaN 0xfff00000000f0000
#define IEEE_QNaN 0xfff8000000000000
#define IEEE_PINF 0x7ff0000000000000
#define IEEE_NINF 0xfff0000000000000
 
 
/*
* The memory format of S floating point numbers differs from the
* register format. In the following, the bitnumbers above the
* diagram below give the memory format while the numbers below give
* the register format.
*
* 31 30 23 22 0
* +-----------------------------------------------+
* S | s | exp | fraction |
* +-----------------------------------------------+
* 63 62 52 51 29
*
* For T floating point numbers, the register and memory formats
* match:
*
* +-------------------------------------------------------------------+
* T | s | exp | frac | tion |
* +-------------------------------------------------------------------+
* 63 62 52 51 32 31 0
*/
typedef struct {
unsigned long f[2]; /* bit 55 in f[0] is the factor of 2^0*/
int s; /* 1 bit sign (0 for +, 1 for -) */
int e; /* 16 bit signed exponent */
} EXTENDED;
 
 
/*
* Return the sign of a Q integer, S or T fp number in the register
* format.
*/
static inline int
sign (unsigned long a)
{
if ((long) a < 0)
return -1;
else
return 1;
}
 
 
static inline long
cmp128 (const long a[2], const long b[2])
{
if (a[1] < b[1]) return -1;
if (a[1] > b[1]) return 1;
return a[0] - b[0];
}
 
 
static inline void
sll128 (unsigned long a[2])
{
a[1] = (a[1] << 1) | (a[0] >> 63);
a[0] <<= 1;
}
 
 
static inline void
srl128 (unsigned long a[2])
{
a[0] = (a[0] >> 1) | (a[1] << 63);
a[1] >>= 1;
}
 
 
static inline void
add128 (const unsigned long a[2], const unsigned long b[2], unsigned long c[2])
{
unsigned long carry = a[0] > (0xffffffffffffffff - b[0]);
 
c[0] = a[0] + b[0];
c[1] = a[1] + b[1] + carry;
}
 
 
static inline void
sub128 (const unsigned long a[2], const unsigned long b[2], unsigned long c[2])
{
unsigned long borrow = a[0] < b[0];
 
c[0] = a[0] - b[0];
c[1] = a[1] - b[1] - borrow;
}
 
 
static inline void
mul64 (const unsigned long a, const unsigned long b, unsigned long c[2])
{
c[0] = a * b;
asm ("umulh %1,%2,%0" : "=r"(c[1]) : "r"(a), "r"(b));
}
 
 
static void
div128 (unsigned long a[2], unsigned long b[2], unsigned long c[2])
{
unsigned long mask[2] = {1, 0};
 
/*
* Shift b until either the sign bit is set or until it is at
* least as big as the dividend:
*/
while (cmp128(b, a) < 0 && sign(b[1]) >= 0) {
sll128(b);
sll128(mask);
}
c[0] = c[1] = 0;
do {
if (cmp128(a, b) >= 0) {
sub128(a, b, a);
add128(mask, c, c);
}
srl128(mask);
srl128(b);
} while (mask[0] || mask[1]);
}
 
 
static void
normalize (EXTENDED *a)
{
if (!a->f[0] && !a->f[1])
return; /* zero fraction, unnormalizable... */
/*
* In "extended" format, the "1" in "1.f" is explicit; it is
* in bit 55 of f[0], and the decimal point is understood to
* be between bit 55 and bit 54. To normalize, shift the
* fraction until we have a "1" in bit 55.
*/
if ((a->f[0] & 0xff00000000000000) != 0 || a->f[1] != 0) {
/*
* Mantissa is greater than 1.0:
*/
while ((a->f[0] & 0xff80000000000000) != 0x0080000000000000 ||
a->f[1] != 0)
{
unsigned long sticky;
 
++a->e;
sticky = a->f[0] & 1;
srl128(a->f);
a->f[0] |= sticky;
}
return;
}
 
if (!(a->f[0] & 0x0080000000000000)) {
/*
* Mantissa is less than 1.0:
*/
while (!(a->f[0] & 0x0080000000000000)) {
--a->e;
a->f[0] <<= 1;
}
return;
}
}
 
 
static inline fpclass_t
ieee_fpclass (unsigned long a)
{
unsigned long exp, fract;
 
exp = (a >> 52) & 0x7ff; /* 11 bits of exponent */
fract = a & 0x000fffffffffffff; /* 52 bits of fraction */
if (exp == 0) {
if (fract == 0)
return ZERO;
return DENORM;
}
if (exp == 0x7ff) {
if (fract == 0)
return INFTY;
if (((fract >> 51) & 1) != 0)
return QNaN;
return NaN;
}
return NORMAL;
}
 
 
/*
* Translate S/T fp number in register format into extended format.
*/
static fpclass_t
extend_ieee (unsigned long a, EXTENDED *b, int prec)
{
fpclass_t result_kind;
 
b->s = a >> 63;
b->e = ((a >> 52) & 0x7ff) - 0x3ff; /* remove bias */
b->f[1] = 0;
/*
* We shift f[1] left three bits so that the higher order bits
* of the fraction will reside in bits 55 through 0 of f[0].
*/
b->f[0] = (a & 0x000fffffffffffff) << 3;
result_kind = ieee_fpclass(a);
if (result_kind == NORMAL) {
/* set implied 1. bit: */
b->f[0] |= 1UL << 55;
} else if (result_kind == DENORM) {
if (prec == SINGLE)
b->e = -126;
else
b->e = -1022;
}
return result_kind;
}
 
 
/*
* INPUT PARAMETERS:
* a a number in EXTENDED format to be converted to
* s-floating format.
* f rounding mode and exception enable bits.
* OUTPUT PARAMETERS:
* b will contain the s-floating number that "a" was
* converted to (in register format).
*/
static unsigned long
make_s_ieee (long f, EXTENDED *a, unsigned long *b)
{
unsigned long res, sticky;
 
if (!a->e && !a->f[0] && !a->f[1]) {
*b = (unsigned long) a->s << 63; /* return +/-0 */
return 0;
}
 
normalize(a);
res = 0;
 
if (a->e < -0x7e) {
res = FPCR_INE;
if (f & IEEE_TRAP_ENABLE_UNF) {
res |= FPCR_UNF;
a->e += 0xc0; /* scale up result by 2^alpha */
} else {
/* try making denormalized number: */
while (a->e < -0x7e) {
++a->e;
sticky = a->f[0] & 1;
srl128(a->f);
if (!a->f[0] && !a->f[0]) {
/* underflow: replace with exact 0 */
res |= FPCR_UNF;
break;
}
a->f[0] |= sticky;
}
a->e = -0x3ff;
}
}
if (a->e >= 0x80) {
res = FPCR_OVF | FPCR_INE;
if (f & IEEE_TRAP_ENABLE_OVF) {
a->e -= 0xc0; /* scale down result by 2^alpha */
} else {
/*
* Overflow without trap enabled, substitute
* result according to rounding mode:
*/
switch (RM(f)) {
case ROUND_NEAR:
*b = IEEE_PINF;
break;
 
case ROUND_CHOP:
*b = IEEE_SMAX;
break;
 
case ROUND_NINF:
if (a->s) {
*b = IEEE_PINF;
} else {
*b = IEEE_SMAX;
}
break;
 
case ROUND_PINF:
if (a->s) {
*b = IEEE_SMAX;
} else {
*b = IEEE_PINF;
}
break;
}
*b |= ((unsigned long) a->s << 63);
return res;
}
}
 
*b = (((unsigned long) a->s << 63) |
(((unsigned long) a->e + 0x3ff) << 52) |
((a->f[0] >> 3) & 0x000fffffe0000000));
return res;
}
 
 
static unsigned long
make_t_ieee (long f, EXTENDED *a, unsigned long *b)
{
unsigned long res, sticky;
 
if (!a->e && !a->f[0] && !a->f[1]) {
*b = (unsigned long) a->s << 63; /* return +/-0 */
return 0;
}
 
normalize(a);
res = 0;
if (a->e < -0x3fe) {
res = FPCR_INE;
if (f & IEEE_TRAP_ENABLE_UNF) {
res |= FPCR_UNF;
a->e += 0x600;
} else {
/* try making denormalized number: */
while (a->e < -0x3fe) {
++a->e;
sticky = a->f[0] & 1;
srl128(a->f);
if (!a->f[0] && !a->f[0]) {
/* underflow: replace with exact 0 */
res |= FPCR_UNF;
break;
}
a->f[0] |= sticky;
}
a->e = -0x3ff;
}
}
if (a->e >= 0x3ff) {
res = FPCR_OVF | FPCR_INE;
if (f & IEEE_TRAP_ENABLE_OVF) {
a->e -= 0x600; /* scale down result by 2^alpha */
} else {
/*
* Overflow without trap enabled, substitute
* result according to rounding mode:
*/
switch (RM(f)) {
case ROUND_NEAR:
*b = IEEE_PINF;
break;
 
case ROUND_CHOP:
*b = IEEE_TMAX;
break;
 
case ROUND_NINF:
if (a->s) {
*b = IEEE_PINF;
} else {
*b = IEEE_TMAX;
}
break;
 
case ROUND_PINF:
if (a->s) {
*b = IEEE_TMAX;
} else {
*b = IEEE_PINF;
}
break;
}
*b |= ((unsigned long) a->s << 63);
return res;
}
}
*b = (((unsigned long) a->s << 63) |
(((unsigned long) a->e + 0x3ff) << 52) |
((a->f[0] >> 3) & 0x000fffffffffffff));
return res;
}
 
 
/*
* INPUT PARAMETERS:
* a EXTENDED format number to be rounded.
* rm integer with value ROUND_NEAR, ROUND_CHOP, etc.
* indicates how "a" should be rounded to produce "b".
* OUTPUT PARAMETERS:
* b s-floating number produced by rounding "a".
* RETURN VALUE:
* if no errors occurred, will be zero. Else will contain flags
* like FPCR_INE_OP, etc.
*/
static unsigned long
round_s_ieee (int f, EXTENDED *a, unsigned long *b)
{
unsigned long diff1, diff2, res = 0;
EXTENDED z1, z2;
 
if (!(a->f[0] & 0xffffffff)) {
return make_s_ieee(f, a, b); /* no rounding error */
}
 
/*
* z1 and z2 are the S-floating numbers with the next smaller/greater
* magnitude than a, respectively.
*/
z1.s = z2.s = a->s;
z1.e = z2.e = a->e;
z1.f[0] = z2.f[0] = a->f[0] & 0xffffffff00000000;
z1.f[1] = z2.f[1] = 0;
z2.f[0] += 0x100000000; /* next bigger S float number */
 
switch (RM(f)) {
case ROUND_NEAR:
diff1 = a->f[0] - z1.f[0];
diff2 = z2.f[0] - a->f[0];
if (diff1 > diff2)
res = make_s_ieee(f, &z2, b);
else if (diff2 > diff1)
res = make_s_ieee(f, &z1, b);
else
/* equal distance: round towards even */
if (z1.f[0] & 0x100000000)
res = make_s_ieee(f, &z2, b);
else
res = make_s_ieee(f, &z1, b);
break;
 
case ROUND_CHOP:
res = make_s_ieee(f, &z1, b);
break;
 
case ROUND_PINF:
if (a->s) {
res = make_s_ieee(f, &z1, b);
} else {
res = make_s_ieee(f, &z2, b);
}
break;
 
case ROUND_NINF:
if (a->s) {
res = make_s_ieee(f, &z2, b);
} else {
res = make_s_ieee(f, &z1, b);
}
break;
}
return FPCR_INE | res;
}
 
 
static unsigned long
round_t_ieee (int f, EXTENDED *a, unsigned long *b)
{
unsigned long diff1, diff2, res;
EXTENDED z1, z2;
 
if (!(a->f[0] & 0x7)) {
/* no rounding error */
return make_t_ieee(f, a, b);
}
 
z1.s = z2.s = a->s;
z1.e = z2.e = a->e;
z1.f[0] = z2.f[0] = a->f[0] & ~0x7;
z1.f[1] = z2.f[1] = 0;
z2.f[0] += (1 << 3);
 
res = 0;
switch (RM(f)) {
case ROUND_NEAR:
diff1 = a->f[0] - z1.f[0];
diff2 = z2.f[0] - a->f[0];
if (diff1 > diff2)
res = make_t_ieee(f, &z2, b);
else if (diff2 > diff1)
res = make_t_ieee(f, &z1, b);
else
/* equal distance: round towards even */
if (z1.f[0] & (1 << 3))
res = make_t_ieee(f, &z2, b);
else
res = make_t_ieee(f, &z1, b);
break;
 
case ROUND_CHOP:
res = make_t_ieee(f, &z1, b);
break;
 
case ROUND_PINF:
if (a->s) {
res = make_t_ieee(f, &z1, b);
} else {
res = make_t_ieee(f, &z2, b);
}
break;
 
case ROUND_NINF:
if (a->s) {
res = make_t_ieee(f, &z2, b);
} else {
res = make_t_ieee(f, &z1, b);
}
break;
}
return FPCR_INE | res;
}
 
 
static fpclass_t
add_kernel_ieee (EXTENDED *op_a, EXTENDED *op_b, EXTENDED *op_c)
{
unsigned long mask, fa, fb, fc;
int diff;
 
diff = op_a->e - op_b->e;
fa = op_a->f[0];
fb = op_b->f[0];
if (diff < 0) {
diff = -diff;
op_c->e = op_b->e;
mask = (1UL << diff) - 1;
fa >>= diff;
if (op_a->f[0] & mask) {
fa |= 1; /* set sticky bit */
}
} else {
op_c->e = op_a->e;
mask = (1UL << diff) - 1;
fb >>= diff;
if (op_b->f[0] & mask) {
fb |= 1; /* set sticky bit */
}
}
if (op_a->s)
fa = -fa;
if (op_b->s)
fb = -fb;
fc = fa + fb;
op_c->f[1] = 0;
op_c->s = fc >> 63;
if (op_c->s) {
fc = -fc;
}
op_c->f[0] = fc;
normalize(op_c);
return 0;
}
 
 
/*
* converts s-floating "a" to t-floating "b".
*
* INPUT PARAMETERS:
* a a s-floating number to be converted
* f the rounding mode (ROUND_NEAR, etc. )
* OUTPUT PARAMETERS:
* b the t-floating number that "a" is converted to.
* RETURN VALUE:
* error flags - i.e., zero if no errors occurred,
* FPCR_INV if invalid operation occurred, etc.
*/
unsigned long
ieee_CVTST (int f, unsigned long a, unsigned long *b)
{
EXTENDED temp;
fpclass_t a_type;
 
a_type = extend_ieee(a, &temp, SINGLE);
if (a_type >= NaN && a_type <= INFTY) {
*b = a;
if (a_type == NaN) {
*b |= (1UL << 51); /* turn SNaN into QNaN */
return FPCR_INV;
}
return 0;
}
return round_t_ieee(f, &temp, b);
}
 
 
/*
* converts t-floating "a" to s-floating "b".
*
* INPUT PARAMETERS:
* a a t-floating number to be converted
* f the rounding mode (ROUND_NEAR, etc. )
* OUTPUT PARAMETERS:
* b the s-floating number that "a" is converted to.
* RETURN VALUE:
* error flags - i.e., zero if no errors occurred,
* FPCR_INV if invalid operation occurred, etc.
*/
unsigned long
ieee_CVTTS (int f, unsigned long a, unsigned long *b)
{
EXTENDED temp;
fpclass_t a_type;
 
a_type = extend_ieee(a, &temp, DOUBLE);
if (a_type >= NaN && a_type <= INFTY) {
*b = a;
if (a_type == NaN) {
*b |= (1UL << 51); /* turn SNaN into QNaN */
return FPCR_INV;
}
return 0;
}
return round_s_ieee(f, &temp, b);
}
 
 
/*
* converts q-format (64-bit integer) "a" to s-floating "b".
*
* INPUT PARAMETERS:
* a an 64-bit integer to be converted.
* f the rounding mode (ROUND_NEAR, etc. )
* OUTPUT PARAMETERS:
* b the s-floating number "a" is converted to.
* RETURN VALUE:
* error flags - i.e., zero if no errors occurred,
* FPCR_INV if invalid operation occurred, etc.
*/
unsigned long
ieee_CVTQS (int f, unsigned long a, unsigned long *b)
{
EXTENDED op_b;
 
op_b.s = 0;
op_b.f[0] = a;
op_b.f[1] = 0;
if (sign(a) < 0) {
op_b.s = 1;
op_b.f[0] = -a;
}
op_b.e = 55;
normalize(&op_b);
return round_s_ieee(f, &op_b, b);
}
 
 
/*
* converts 64-bit integer "a" to t-floating "b".
*
* INPUT PARAMETERS:
* a a 64-bit integer to be converted.
* f the rounding mode (ROUND_NEAR, etc.)
* OUTPUT PARAMETERS:
* b the t-floating number "a" is converted to.
* RETURN VALUE:
* error flags - i.e., zero if no errors occurred,
* FPCR_INV if invalid operation occurred, etc.
*/
unsigned long
ieee_CVTQT (int f, unsigned long a, unsigned long *b)
{
EXTENDED op_b;
 
op_b.s = 0;
op_b.f[0] = a;
op_b.f[1] = 0;
if (sign(a) < 0) {
op_b.s = 1;
op_b.f[0] = -a;
}
op_b.e = 55;
normalize(&op_b);
return round_t_ieee(f, &op_b, b);
}
 
 
/*
* converts t-floating "a" to 64-bit integer (q-format) "b".
*
* INPUT PARAMETERS:
* a a t-floating number to be converted.
* f the rounding mode (ROUND_NEAR, etc. )
* OUTPUT PARAMETERS:
* b the 64-bit integer "a" is converted to.
* RETURN VALUE:
* error flags - i.e., zero if no errors occurred,
* FPCR_INV if invalid operation occurred, etc.
*/
unsigned long
ieee_CVTTQ (int f, unsigned long a, unsigned long *pb)
{
unsigned int midway;
unsigned long ov, uv, res, b;
fpclass_t a_type;
EXTENDED temp;
 
a_type = extend_ieee(a, &temp, DOUBLE);
 
b = 0x7fffffffffffffff;
res = FPCR_INV;
if (a_type == NaN || a_type == INFTY)
goto out;
 
res = 0;
if (a_type == QNaN)
goto out;
 
if (temp.e > 0) {
ov = 0;
while (temp.e > 0) {
--temp.e;
ov |= temp.f[1] >> 63;
sll128(temp.f);
}
if (ov || (temp.f[1] & 0xffc0000000000000))
res |= FPCR_IOV | FPCR_INE;
}
else if (temp.e < 0) {
while (temp.e < 0) {
++temp.e;
uv = temp.f[0] & 1; /* save sticky bit */
srl128(temp.f);
temp.f[0] |= uv;
}
}
b = (temp.f[1] << 9) | (temp.f[0] >> 55);
 
/*
* Notice: the fraction is only 52 bits long. Thus, rounding
* cannot possibly result in an integer overflow.
*/
switch (RM(f)) {
case ROUND_NEAR:
if (temp.f[0] & 0x0040000000000000) {
midway = (temp.f[0] & 0x003fffffffffffff) == 0;
if ((midway && (temp.f[0] & 0x0080000000000000)) ||
!midway)
++b;
}
break;
 
case ROUND_PINF:
if ((temp.f[0] & 0x007fffffffffffff) != 0)
++b;
break;
 
case ROUND_NINF:
if ((temp.f[0] & 0x007fffffffffffff) != 0)
--b;
break;
 
case ROUND_CHOP:
/* no action needed */
break;
}
if ((temp.f[0] & 0x007fffffffffffff) != 0)
res |= FPCR_INE;
 
if (temp.s) {
b = -b;
}
 
out:
*pb = b;
return res;
}
 
 
unsigned long
ieee_CMPTEQ (unsigned long a, unsigned long b, unsigned long *c)
{
EXTENDED op_a, op_b;
fpclass_t a_type, b_type;
 
*c = 0;
a_type = extend_ieee(a, &op_a, DOUBLE);
b_type = extend_ieee(b, &op_b, DOUBLE);
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if ((op_a.e == op_b.e && op_a.s == op_b.s &&
op_a.f[0] == op_b.f[0] && op_a.f[1] == op_b.f[1]) ||
(a_type == ZERO && b_type == ZERO))
*c = 0x4000000000000000;
return 0;
}
 
 
unsigned long
ieee_CMPTLT (unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b;
 
*c = 0;
a_type = extend_ieee(a, &op_a, DOUBLE);
b_type = extend_ieee(b, &op_b, DOUBLE);
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if ((op_a.s == 1 && op_b.s == 0 &&
(a_type != ZERO || b_type != ZERO)) ||
(op_a.s == 1 && op_b.s == 1 &&
(op_a.e > op_b.e || (op_a.e == op_b.e &&
cmp128(op_a.f, op_b.f) > 0))) ||
(op_a.s == 0 && op_b.s == 0 &&
(op_a.e < op_b.e || (op_a.e == op_b.e &&
cmp128(op_a.f,op_b.f) < 0))))
*c = 0x4000000000000000;
return 0;
}
 
 
unsigned long
ieee_CMPTLE (unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b;
 
*c = 0;
a_type = extend_ieee(a, &op_a, DOUBLE);
b_type = extend_ieee(b, &op_b, DOUBLE);
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if ((a_type == ZERO && b_type == ZERO) ||
(op_a.s == 1 && op_b.s == 0) ||
(op_a.s == 1 && op_b.s == 1 &&
(op_a.e > op_b.e || (op_a.e == op_b.e &&
cmp128(op_a.f,op_b.f) >= 0))) ||
(op_a.s == 0 && op_b.s == 0 &&
(op_a.e < op_b.e || (op_a.e == op_b.e &&
cmp128(op_a.f,op_b.f) <= 0))))
*c = 0x4000000000000000;
return 0;
}
 
 
unsigned long
ieee_CMPTUN (unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b;
 
*c = 0x4000000000000000;
a_type = extend_ieee(a, &op_a, DOUBLE);
b_type = extend_ieee(b, &op_b, DOUBLE);
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
*c = 0;
return 0;
}
 
 
/*
* Add a + b = c, where a, b, and c are ieee s-floating numbers. "f"
* contains the rounding mode etc.
*/
unsigned long
ieee_ADDS (int f, unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b, op_c;
 
a_type = extend_ieee(a, &op_a, SINGLE);
b_type = extend_ieee(b, &op_b, SINGLE);
if ((a_type >= NaN && a_type <= INFTY) ||
(b_type >= NaN && b_type <= INFTY))
{
/* propagate NaNs according to arch. ref. handbook: */
if (b_type == QNaN)
*c = b;
else if (b_type == NaN)
*c = b | (1UL << 51);
else if (a_type == QNaN)
*c = a;
else if (a_type == NaN)
*c = a | (1UL << 51);
 
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if (a_type == INFTY && b_type == INFTY && sign(a) != sign(b)) {
*c = IEEE_QNaN;
return FPCR_INV;
}
if (a_type == INFTY)
*c = a;
else
*c = b;
return 0;
}
 
add_kernel_ieee(&op_a, &op_b, &op_c);
/* special case for -0 + -0 ==> -0 */
if (a_type == ZERO && b_type == ZERO)
op_c.s = op_a.s && op_b.s;
return round_s_ieee(f, &op_c, c);
}
 
 
/*
* Add a + b = c, where a, b, and c are ieee t-floating numbers. "f"
* contains the rounding mode etc.
*/
unsigned long
ieee_ADDT (int f, unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b, op_c;
 
a_type = extend_ieee(a, &op_a, DOUBLE);
b_type = extend_ieee(b, &op_b, DOUBLE);
if ((a_type >= NaN && a_type <= INFTY) ||
(b_type >= NaN && b_type <= INFTY))
{
/* propagate NaNs according to arch. ref. handbook: */
if (b_type == QNaN)
*c = b;
else if (b_type == NaN)
*c = b | (1UL << 51);
else if (a_type == QNaN)
*c = a;
else if (a_type == NaN)
*c = a | (1UL << 51);
 
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if (a_type == INFTY && b_type == INFTY && sign(a) != sign(b)) {
*c = IEEE_QNaN;
return FPCR_INV;
}
if (a_type == INFTY)
*c = a;
else
*c = b;
return 0;
}
add_kernel_ieee(&op_a, &op_b, &op_c);
/* special case for -0 + -0 ==> -0 */
if (a_type == ZERO && b_type == ZERO)
op_c.s = op_a.s && op_b.s;
 
return round_t_ieee(f, &op_c, c);
}
 
 
/*
* Subtract a - b = c, where a, b, and c are ieee s-floating numbers.
* "f" contains the rounding mode etc.
*/
unsigned long
ieee_SUBS (int f, unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b, op_c;
 
a_type = extend_ieee(a, &op_a, SINGLE);
b_type = extend_ieee(b, &op_b, SINGLE);
if ((a_type >= NaN && a_type <= INFTY) ||
(b_type >= NaN && b_type <= INFTY))
{
/* propagate NaNs according to arch. ref. handbook: */
if (b_type == QNaN)
*c = b;
else if (b_type == NaN)
*c = b | (1UL << 51);
else if (a_type == QNaN)
*c = a;
else if (a_type == NaN)
*c = a | (1UL << 51);
 
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if (a_type == INFTY && b_type == INFTY && sign(a) == sign(b)) {
*c = IEEE_QNaN;
return FPCR_INV;
}
if (a_type == INFTY)
*c = a;
else
*c = b ^ (1UL << 63);
return 0;
}
op_b.s = !op_b.s;
add_kernel_ieee(&op_a, &op_b, &op_c);
/* special case for -0 - +0 ==> -0 */
if (a_type == ZERO && b_type == ZERO)
op_c.s = op_a.s && op_b.s;
 
return round_s_ieee(f, &op_c, c);
}
 
 
/*
* Subtract a - b = c, where a, b, and c are ieee t-floating numbers.
* "f" contains the rounding mode etc.
*/
unsigned long
ieee_SUBT (int f, unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b, op_c;
 
a_type = extend_ieee(a, &op_a, DOUBLE);
b_type = extend_ieee(b, &op_b, DOUBLE);
if ((a_type >= NaN && a_type <= INFTY) ||
(b_type >= NaN && b_type <= INFTY))
{
/* propagate NaNs according to arch. ref. handbook: */
if (b_type == QNaN)
*c = b;
else if (b_type == NaN)
*c = b | (1UL << 51);
else if (a_type == QNaN)
*c = a;
else if (a_type == NaN)
*c = a | (1UL << 51);
 
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if (a_type == INFTY && b_type == INFTY && sign(a) == sign(b)) {
*c = IEEE_QNaN;
return FPCR_INV;
}
if (a_type == INFTY)
*c = a;
else
*c = b ^ (1UL << 63);
return 0;
}
op_b.s = !op_b.s;
add_kernel_ieee(&op_a, &op_b, &op_c);
/* special case for -0 - +0 ==> -0 */
if (a_type == ZERO && b_type == ZERO)
op_c.s = op_a.s && op_b.s;
 
return round_t_ieee(f, &op_c, c);
}
 
 
/*
* Multiply a x b = c, where a, b, and c are ieee s-floating numbers.
* "f" contains the rounding mode.
*/
unsigned long
ieee_MULS (int f, unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b, op_c;
 
a_type = extend_ieee(a, &op_a, SINGLE);
b_type = extend_ieee(b, &op_b, SINGLE);
if ((a_type >= NaN && a_type <= INFTY) ||
(b_type >= NaN && b_type <= INFTY))
{
/* propagate NaNs according to arch. ref. handbook: */
if (b_type == QNaN)
*c = b;
else if (b_type == NaN)
*c = b | (1UL << 51);
else if (a_type == QNaN)
*c = a;
else if (a_type == NaN)
*c = a | (1UL << 51);
 
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if ((a_type == INFTY && b_type == ZERO) ||
(b_type == INFTY && a_type == ZERO))
{
*c = IEEE_QNaN; /* return canonical QNaN */
return FPCR_INV;
}
if (a_type == INFTY)
*c = a ^ ((b >> 63) << 63);
else if (b_type == INFTY)
*c = b ^ ((a >> 63) << 63);
else
/* either of a and b are +/-0 */
*c = ((unsigned long) op_a.s ^ op_b.s) << 63;
return 0;
}
op_c.s = op_a.s ^ op_b.s;
op_c.e = op_a.e + op_b.e - 55;
mul64(op_a.f[0], op_b.f[0], op_c.f);
 
return round_s_ieee(f, &op_c, c);
}
 
 
/*
* Multiply a x b = c, where a, b, and c are ieee t-floating numbers.
* "f" contains the rounding mode.
*/
unsigned long
ieee_MULT (int f, unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b, op_c;
 
*c = IEEE_QNaN;
a_type = extend_ieee(a, &op_a, DOUBLE);
b_type = extend_ieee(b, &op_b, DOUBLE);
if ((a_type >= NaN && a_type <= ZERO) ||
(b_type >= NaN && b_type <= ZERO))
{
/* propagate NaNs according to arch. ref. handbook: */
if (b_type == QNaN)
*c = b;
else if (b_type == NaN)
*c = b | (1UL << 51);
else if (a_type == QNaN)
*c = a;
else if (a_type == NaN)
*c = a | (1UL << 51);
 
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
if ((a_type == INFTY && b_type == ZERO) ||
(b_type == INFTY && a_type == ZERO))
{
*c = IEEE_QNaN; /* return canonical QNaN */
return FPCR_INV;
}
if (a_type == INFTY)
*c = a ^ ((b >> 63) << 63);
else if (b_type == INFTY)
*c = b ^ ((a >> 63) << 63);
else
/* either of a and b are +/-0 */
*c = ((unsigned long) op_a.s ^ op_b.s) << 63;
return 0;
}
op_c.s = op_a.s ^ op_b.s;
op_c.e = op_a.e + op_b.e - 55;
mul64(op_a.f[0], op_b.f[0], op_c.f);
 
return round_t_ieee(f, &op_c, c);
}
 
 
/*
* Divide a / b = c, where a, b, and c are ieee s-floating numbers.
* "f" contains the rounding mode etc.
*/
unsigned long
ieee_DIVS (int f, unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b, op_c;
 
a_type = extend_ieee(a, &op_a, SINGLE);
b_type = extend_ieee(b, &op_b, SINGLE);
if ((a_type >= NaN && a_type <= ZERO) ||
(b_type >= NaN && b_type <= ZERO))
{
unsigned long res;
 
/* propagate NaNs according to arch. ref. handbook: */
if (b_type == QNaN)
*c = b;
else if (b_type == NaN)
*c = b | (1UL << 51);
else if (a_type == QNaN)
*c = a;
else if (a_type == NaN)
*c = a | (1UL << 51);
 
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
res = 0;
*c = IEEE_PINF;
if (a_type == INFTY) {
if (b_type == INFTY) {
*c = IEEE_QNaN;
return FPCR_INV;
}
} else if (b_type == ZERO) {
if (a_type == ZERO) {
*c = IEEE_QNaN;
return FPCR_INV;
}
res = FPCR_DZE;
} else
/* a_type == ZERO || b_type == INFTY */
*c = 0;
*c |= (unsigned long) (op_a.s ^ op_b.s) << 63;
return res;
}
op_c.s = op_a.s ^ op_b.s;
op_c.e = op_a.e - op_b.e;
 
op_a.f[1] = op_a.f[0];
op_a.f[0] = 0;
div128(op_a.f, op_b.f, op_c.f);
if (a_type != ZERO)
/* force a sticky bit because DIVs never hit exact .5: */
op_c.f[0] |= STICKY_S;
normalize(&op_c);
op_c.e -= 9; /* remove excess exp from original shift */
return round_s_ieee(f, &op_c, c);
}
 
 
/*
* Divide a/b = c, where a, b, and c are ieee t-floating numbers. "f"
* contains the rounding mode etc.
*/
unsigned long
ieee_DIVT (int f, unsigned long a, unsigned long b, unsigned long *c)
{
fpclass_t a_type, b_type;
EXTENDED op_a, op_b, op_c;
 
*c = IEEE_QNaN;
a_type = extend_ieee(a, &op_a, DOUBLE);
b_type = extend_ieee(b, &op_b, DOUBLE);
if ((a_type >= NaN && a_type <= ZERO) ||
(b_type >= NaN && b_type <= ZERO))
{
unsigned long res;
 
/* propagate NaNs according to arch. ref. handbook: */
if (b_type == QNaN)
*c = b;
else if (b_type == NaN)
*c = b | (1UL << 51);
else if (a_type == QNaN)
*c = a;
else if (a_type == NaN)
*c = a | (1UL << 51);
 
if (a_type == NaN || b_type == NaN)
return FPCR_INV;
if (a_type == QNaN || b_type == QNaN)
return 0;
 
res = 0;
*c = IEEE_PINF;
if (a_type == INFTY) {
if (b_type == INFTY) {
*c = IEEE_QNaN;
return FPCR_INV;
}
} else if (b_type == ZERO) {
if (a_type == ZERO) {
*c = IEEE_QNaN;
return FPCR_INV;
}
res = FPCR_DZE;
} else
/* a_type == ZERO || b_type == INFTY */
*c = 0;
*c |= (unsigned long) (op_a.s ^ op_b.s) << 63;
return res;
}
op_c.s = op_a.s ^ op_b.s;
op_c.e = op_a.e - op_b.e;
 
op_a.f[1] = op_a.f[0];
op_a.f[0] = 0;
div128(op_a.f, op_b.f, op_c.f);
if (a_type != ZERO)
/* force a sticky bit because DIVs never hit exact .5 */
op_c.f[0] |= STICKY_T;
normalize(&op_c);
op_c.e -= 9; /* remove excess exp from original shift */
return round_t_ieee(f, &op_c, c);
}
/math-emu/ieee-math.h
0,0 → 1,52
/*
* Copyright (C) 1992,1995 by
* Digital Equipment Corporation, Maynard, Massachusetts.
* This file may be redistributed according to the terms of the
* GNU General Public License.
*/
#ifndef __ieee_math_h__
#define __ieee_math_h__
 
#include <asm/fpu.h>
 
#define ROUND_SHIFT 6 /* make space for trap-enable bits */
#define RM(f) (((f) >> ROUND_SHIFT) & 0x3)
 
#define ROUND_CHOP (FPCR_DYN_CHOPPED >> FPCR_DYN_SHIFT)
#define ROUND_NINF (FPCR_DYN_MINUS >> FPCR_DYN_SHIFT)
#define ROUND_NEAR (FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT)
#define ROUND_PINF (FPCR_DYN_PLUS >> FPCR_DYN_SHIFT)
 
extern unsigned long ieee_CVTST (int rm, unsigned long a, unsigned long *b);
extern unsigned long ieee_CVTTS (int rm, unsigned long a, unsigned long *b);
extern unsigned long ieee_CVTQS (int rm, unsigned long a, unsigned long *b);
extern unsigned long ieee_CVTQT (int rm, unsigned long a, unsigned long *b);
extern unsigned long ieee_CVTTQ (int rm, unsigned long a, unsigned long *b);
 
extern unsigned long ieee_CMPTEQ (unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_CMPTLT (unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_CMPTLE (unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_CMPTUN (unsigned long a, unsigned long b,
unsigned long *c);
 
extern unsigned long ieee_ADDS (int rm, unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_ADDT (int rm, unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_SUBS (int rm, unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_SUBT (int rm, unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_MULS (int rm, unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_MULT (int rm, unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_DIVS (int rm, unsigned long a, unsigned long b,
unsigned long *c);
extern unsigned long ieee_DIVT (int rm, unsigned long a, unsigned long b,
unsigned long *c);
 
#endif /* __ieee_math_h__ */
/math-emu/fp-emul.c
0,0 → 1,398
#include <linux/types.h>
 
#include <linux/kernel.h>
#include <linux/sched.h>
 
#include <asm/segment.h>
 
#include "ieee-math.h"
 
#define OPC_PAL 0x00
 
#define OPC_INTA 0x10
#define OPC_INTL 0x11
#define OPC_INTS 0x12
#define OPC_INTM 0x13
#define OPC_FLTV 0x15
#define OPC_FLTI 0x16
#define OPC_FLTL 0x17
 
#define OPC_MISC 0x18
 
#define OPC_JSR 0x1a
 
/*
* "Base" function codes for the FLTI-class instructions. These
* instructions all have opcode 0x16. Note that in most cases these
* actually correspond to the "chopped" form of the instruction. Not
* to worry---we extract the qualifier bits separately and deal with
* them separately. Notice that base function code 0x2c is used for
* both CVTTS and CVTST. The other bits in the function code are used
* to distinguish the two.
*/
#define FLTI_FUNC_ADDS 0x000
#define FLTI_FUNC_ADDT 0x020
#define FLTI_FUNC_CMPTEQ 0x025
#define FLTI_FUNC_CMPTLT 0x026
#define FLTI_FUNC_CMPTLE 0x027
#define FLTI_FUNC_CMPTUN 0x024
#define FLTI_FUNC_CVTTS_or_CVTST 0x02c
#define FLTI_FUNC_CVTTQ 0x02f
#define FLTI_FUNC_CVTQS 0x03c
#define FLTI_FUNC_CVTQT 0x03e
#define FLTI_FUNC_DIVS 0x003
#define FLTI_FUNC_DIVT 0x023
#define FLTI_FUNC_MULS 0x002
#define FLTI_FUNC_MULT 0x022
#define FLTI_FUNC_SUBS 0x001
#define FLTI_FUNC_SUBT 0x021
 
#define FLTI_FUNC_CVTQL 0x030 /* opcode 0x17 */
 
#define MISC_TRAPB 0x0000
#define MISC_EXCB 0x0400
 
 
extern unsigned long rdfpcr (void);
extern void wrfpcr (unsigned long);
 
 
unsigned long
alpha_read_fp_reg (unsigned long reg)
{
unsigned long r;
 
switch (reg) {
case 0: asm ("stt $f0,%0" : "m="(r)); break;
case 1: asm ("stt $f1,%0" : "m="(r)); break;
case 2: asm ("stt $f2,%0" : "m="(r)); break;
case 3: asm ("stt $f3,%0" : "m="(r)); break;
case 4: asm ("stt $f4,%0" : "m="(r)); break;
case 5: asm ("stt $f5,%0" : "m="(r)); break;
case 6: asm ("stt $f6,%0" : "m="(r)); break;
case 7: asm ("stt $f7,%0" : "m="(r)); break;
case 8: asm ("stt $f8,%0" : "m="(r)); break;
case 9: asm ("stt $f9,%0" : "m="(r)); break;
case 10: asm ("stt $f10,%0" : "m="(r)); break;
case 11: asm ("stt $f11,%0" : "m="(r)); break;
case 12: asm ("stt $f12,%0" : "m="(r)); break;
case 13: asm ("stt $f13,%0" : "m="(r)); break;
case 14: asm ("stt $f14,%0" : "m="(r)); break;
case 15: asm ("stt $f15,%0" : "m="(r)); break;
case 16: asm ("stt $f16,%0" : "m="(r)); break;
case 17: asm ("stt $f17,%0" : "m="(r)); break;
case 18: asm ("stt $f18,%0" : "m="(r)); break;
case 19: asm ("stt $f19,%0" : "m="(r)); break;
case 20: asm ("stt $f20,%0" : "m="(r)); break;
case 21: asm ("stt $f21,%0" : "m="(r)); break;
case 22: asm ("stt $f22,%0" : "m="(r)); break;
case 23: asm ("stt $f23,%0" : "m="(r)); break;
case 24: asm ("stt $f24,%0" : "m="(r)); break;
case 25: asm ("stt $f25,%0" : "m="(r)); break;
case 26: asm ("stt $f26,%0" : "m="(r)); break;
case 27: asm ("stt $f27,%0" : "m="(r)); break;
case 28: asm ("stt $f28,%0" : "m="(r)); break;
case 29: asm ("stt $f29,%0" : "m="(r)); break;
case 30: asm ("stt $f30,%0" : "m="(r)); break;
case 31: asm ("stt $f31,%0" : "m="(r)); break;
default:
break;
}
return r;
}
 
 
#if 0
/*
* This is IMHO the better way of implementing LDT(). But it
* has the disadvantage that gcc 2.7.0 refuses to compile it
* (invalid operand constraints), so instead, we use the uglier
* macro below.
*/
# define LDT(reg,val) \
asm volatile ("ldt $f"#reg",%0" :: "m"(val));
#else
# define LDT(reg,val) \
asm volatile ("ldt $f"#reg",0(%0)" :: "r"(&val));
#endif
 
void
alpha_write_fp_reg (unsigned long reg, unsigned long val)
{
switch (reg) {
case 0: LDT( 0, val); break;
case 1: LDT( 1, val); break;
case 2: LDT( 2, val); break;
case 3: LDT( 3, val); break;
case 4: LDT( 4, val); break;
case 5: LDT( 5, val); break;
case 6: LDT( 6, val); break;
case 7: LDT( 7, val); break;
case 8: LDT( 8, val); break;
case 9: LDT( 9, val); break;
case 10: LDT(10, val); break;
case 11: LDT(11, val); break;
case 12: LDT(12, val); break;
case 13: LDT(13, val); break;
case 14: LDT(14, val); break;
case 15: LDT(15, val); break;
case 16: LDT(16, val); break;
case 17: LDT(17, val); break;
case 18: LDT(18, val); break;
case 19: LDT(19, val); break;
case 20: LDT(20, val); break;
case 21: LDT(21, val); break;
case 22: LDT(22, val); break;
case 23: LDT(23, val); break;
case 24: LDT(24, val); break;
case 25: LDT(25, val); break;
case 26: LDT(26, val); break;
case 27: LDT(27, val); break;
case 28: LDT(28, val); break;
case 29: LDT(29, val); break;
case 30: LDT(30, val); break;
case 31: LDT(31, val); break;
default:
break;
}
}
 
 
/*
* Emulate the floating point instruction at address PC. Returns 0 if
* emulation fails. Notice that the kernel does not and cannot use FP
* regs. This is good because it means that instead of
* saving/restoring all fp regs, we simply stick the result of the
* operation into the appropriate register.
*/
long
alpha_fp_emul (unsigned long pc)
{
unsigned long opcode, fa, fb, fc, func, mode;
unsigned long fpcw = current->tss.flags;
unsigned long va, vb, vc, res, fpcr;
__u32 insn;
 
insn = get_user((__u32*)pc);
fc = (insn >> 0) & 0x1f; /* destination register */
func = (insn >> 5) & 0x7ff;
fb = (insn >> 16) & 0x1f;
fa = (insn >> 21) & 0x1f;
opcode = insn >> 26;
va = alpha_read_fp_reg(fa);
vb = alpha_read_fp_reg(fb);
 
fpcr = rdfpcr();
/*
* Try the operation in software. First, obtain the rounding
* mode...
*/
mode = func & 0xc0;
if (mode == 0xc0) {
/* dynamic---get rounding mode from fpcr: */
mode = ((fpcr & FPCR_DYN_MASK) >> FPCR_DYN_SHIFT) << ROUND_SHIFT;
}
mode |= (fpcw & IEEE_TRAP_ENABLE_MASK);
 
if ((IEEE_TRAP_ENABLE_MASK & 0xc0)) {
extern int something_is_wrong (void);
something_is_wrong();
}
 
/* least 6 bits contain operation code: */
switch (func & 0x3f) {
case FLTI_FUNC_CMPTEQ:
res = ieee_CMPTEQ(va, vb, &vc);
break;
 
case FLTI_FUNC_CMPTLT:
res = ieee_CMPTLT(va, vb, &vc);
break;
 
case FLTI_FUNC_CMPTLE:
res = ieee_CMPTLE(va, vb, &vc);
break;
 
case FLTI_FUNC_CMPTUN:
res = ieee_CMPTUN(va, vb, &vc);
break;
 
case FLTI_FUNC_CVTQL:
/*
* Notice: We can get here only due to an integer
* overflow. Such overflows are reported as invalid
* ops. We return the result the hw would have
* computed.
*/
vc = ((vb & 0xc0000000) << 32 | /* sign and msb */
(vb & 0x3fffffff) << 29); /* rest of the integer */
res = FPCR_INV;
break;
 
case FLTI_FUNC_CVTQS:
res = ieee_CVTQS(mode, vb, &vc);
break;
 
case FLTI_FUNC_CVTQT:
res = ieee_CVTQT(mode, vb, &vc);
break;
 
case FLTI_FUNC_CVTTS_or_CVTST:
if (func == 0x6ac) {
/*
* 0x2ac is also CVTST, but if the /S
* qualifier isn't set, we wouldn't be here in
* the first place...
*/
res = ieee_CVTST(mode, vb, &vc);
} else {
res = ieee_CVTTS(mode, vb, &vc);
}
break;
 
case FLTI_FUNC_DIVS:
res = ieee_DIVS(mode, va, vb, &vc);
break;
 
case FLTI_FUNC_DIVT:
res = ieee_DIVT(mode, va, vb, &vc);
break;
 
case FLTI_FUNC_MULS:
res = ieee_MULS(mode, va, vb, &vc);
break;
 
case FLTI_FUNC_MULT:
res = ieee_MULT(mode, va, vb, &vc);
break;
 
case FLTI_FUNC_SUBS:
res = ieee_SUBS(mode, va, vb, &vc);
break;
 
case FLTI_FUNC_SUBT:
res = ieee_SUBT(mode, va, vb, &vc);
break;
 
case FLTI_FUNC_ADDS:
res = ieee_ADDS(mode, va, vb, &vc);
break;
 
case FLTI_FUNC_ADDT:
res = ieee_ADDT(mode, va, vb, &vc);
break;
 
case FLTI_FUNC_CVTTQ:
res = ieee_CVTTQ(mode, vb, &vc);
break;
 
default:
printk("alpha_fp_emul: unexpected function code %#lx at %#lx\n",
func & 0x3f, pc);
return 0;
}
/*
* Take the appropriate action for each possible
* floating-point result:
*
* - Set the appropriate bits in the FPCR
* - If the specified exception is enabled in the FPCR,
* return. The caller (entArith) will dispatch
* the appropriate signal to the translated program.
*
* In addition, properly track the exception state in software
* as described in Alpha Architecture Handbook 4.7.7.3.
*/
if (res) {
/* record exceptions in software control word. */
fpcw |= res >> 35;
current->tss.flags = fpcw;
 
/* update hardware control register, disabling hardware
exceptions for disabled software exceptions for which
we have a status. (no, really.) */
fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
fpcr |= ieee_sw_to_fpcr(fpcw | (~fpcw & IEEE_STATUS_MASK)>>16);
wrfpcr(fpcr);
 
/* Do we generate a signal? */
if (res >> 51 & fpcw & IEEE_TRAP_ENABLE_MASK)
return 0;
}
 
/*
* Whoo-kay... we got this far, and we're not generating a signal
* to the translated program. All that remains is to write the
* result:
*/
alpha_write_fp_reg(fc, vc);
return 1;
}
 
 
long
alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
{
unsigned long trigger_pc = regs->pc - 4;
unsigned long insn, opcode, rc;
/*
* Turn off the bits corresponding to registers that are the
* target of instructions that set bits in the exception
* summary register. We have some slack doing this because a
* register that is the target of a trapping instruction can
* be written at most once in the trap shadow.
*
* Branches, jumps, TRAPBs, EXCBs and calls to PALcode all
* bound the trap shadow, so we need not look any further than
* up to the first occurrence of such an instruction.
*/
while (write_mask) {
insn = get_user((__u32*)(trigger_pc));
opcode = insn >> 26;
rc = insn & 0x1f;
 
switch (opcode) {
case OPC_PAL:
case OPC_JSR:
case 0x30 ... 0x3f: /* branches */
return 0;
 
case OPC_MISC:
switch (insn & 0xffff) {
case MISC_TRAPB:
case MISC_EXCB:
return 0;
 
default:
break;
}
break;
 
case OPC_INTA:
case OPC_INTL:
case OPC_INTS:
case OPC_INTM:
write_mask &= ~(1UL << rc);
break;
 
case OPC_FLTV:
case OPC_FLTI:
case OPC_FLTL:
write_mask &= ~(1UL << (rc + 32));
break;
}
if (!write_mask) {
if ((opcode == OPC_FLTI || opcode == OPC_FLTL)
&& alpha_fp_emul(trigger_pc))
{
/* re-execute insns in trap-shadow: */
regs->pc = trigger_pc + 4;
return 1;
}
break;
}
trigger_pc -= 4;
}
return 0;
}
/math-emu/fp-emul.h
0,0 → 1,10
/*
* These defines correspond to the dynamic rounding mode bits in the
* Floating Point Control Register. They also happen to correspond to
* the instruction encodings except that 0x03 signifies dynamic
* rounding mode in that case.
*/
#define ROUND_CHOP 0x00 /* chopped (aka round towards zero) */
#define ROUND_NINF 0x01 /* round towards negative infinity */
#define ROUND_NEAR 0x02 /* round towards nearest number */
#define ROUND_PINF 0x03 /* round towards positive infinity */
/math-emu/Makefile
0,0 → 1,12
#
# Makefile for math-emulator files...
#
 
OBJS = fp-emul.o ieee-math.o
 
math-emu.a: $(OBJS)
$(AR) rcs $@ $(OBJS)
 
dep:
 
include $(TOPDIR)/Rules.make

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.