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, ¤t->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) |
{ |
(®s)->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, ®s); |
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) |
{ |
(®s)->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) |
{ |
(®s)->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) |
{ |
(®s)->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; |
(®s)->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(®s, 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", ®s, 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", ®s, 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(®s); |
return; |
case 2: |
machine_check(vector, la_ptr, ®s); |
return; |
case 3: |
#if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \ |
defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM) |
srm_device_interrupt(vector, ®s); |
#else /* everyone else */ |
|
#if defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164) |
miata_device_interrupt(vector, ®s); |
#elif defined(CONFIG_ALPHA_NORITAKE) |
noritake_device_interrupt(vector, ®s); |
#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) |
alcor_and_xlt_device_interrupt(vector, ®s); |
#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, ®s); |
#elif defined(CONFIG_ALPHA_MIKASA) |
mikasa_device_interrupt(vector, ®s); |
#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) |
eb66_and_eb64p_device_interrupt(vector, ®s); |
#elif defined(CONFIG_ALPHA_RUFFIAN) |
ruffian_device_interrupt(vector, ®s); |
#elif defined(CONFIG_ALPHA_TAKARA) |
takara_device_interrupt(vector, ®s); |
#elif NR_IRQS == 16 |
isa_device_interrupt(vector, ®s); |
#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(®s)) { |
printk("%s: memory violation at pc=%08lx rp=%08lx (bad address = %08lx)\n", |
tsk->comm, regs.pc, regs.r26, address); |
die_if_kernel("oops", ®s, 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", ®s, 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 |