Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

722 lines
17 KiB
C

static char sccsid[] = "@(#)33 1.23.3.21 src/bos/kernel/ios/POWER/i_misc.c, sysios, bos41J, 9516B_all 4/21/95 11:20:26";
/*
* COMPONENT_NAME: (SYSIOS) IO subsystem
*
* FUNCTIONS: i_epow, epow_timer, io_exception
*
* ORIGINS: 27, 83
*
* IBM CONFIDENTIAL -- (IBM Confidential Restricted when
* combined with the aggregated modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1988, 1995
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
/*
* LEVEL 1, 5 Years Bull Confidential Information
*/
#include <sys/types.h>
#include <sys/buid.h>
#include <sys/low.h>
#include <sys/signal.h>
#include <sys/except.h>
#include <sys/intr.h>
#include <sys/mstsave.h>
#include <sys/iplcb.h>
#include <sys/timer.h>
#include <sys/vmker.h>
#include <sys/ppda.h>
#include <sys/syspest.h>
#include <sys/systemcfg.h>
#include <sys/sys_resource.h>
#include "dma_hw.h"
#include "intr_hw.h"
#include "interrupt.h"
#ifdef _RS6K_SMP_MCA
#include "pegasus.h"
#include <sys/processor.h>
#endif
#ifdef _RS6K_SMP_MCA
/*
* Pegasus NVRAM WA - See dma_ppc.c for details.
*/
#define DISKETTE_INT_LVL 4
extern int fd_mutex;
extern int pgs_SSGA_lvl;
extern void d_abort_fd();
#endif /* _RS6K_SMP_MCA */
extern struct ppda ppda[];
#ifdef _POWER_MP
extern struct i_data i_data; /* interrupt handler data struct*/
#endif /* _POWER_MP */
struct intr scuhand; /* keep in pinned part of kernel */
struct intr epowhand; /* keep in pinned part of kernel */
#ifdef _RS6K_SMP_MCA
struct intr epowhand_dev; /* keep in pinned part of kernel */
#endif
struct intr *epowhandler; /* keep in pinned part of kernel */
uint get_pksr();
#ifdef _RSPC
int fw_keycase = KEY_POS_NORMAL;
#endif /* _RSPC */
/*
* allocated, in pinned memory, and initializes various error
* logging structures
*/
struct miscerr misc_log = { ERRID_MISC_ERR,"SYSIOS ", 0, 0, 0 };
struct scuerr exdma_log = { ERRID_EXCHECK_DMA,"SYSIOS ", 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
struct scuerr scrub_log = { ERRID_EXCHECK_SCRUB,"SYSIOS ", 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
struct epowerr suspend_log = { ERRID_EPOW_SUS,"SYSIOS ", 0 };
struct epowerr resume_log = { ERRID_EPOW_RES,"SYSIOS ", 0 };
int epow_status = RESUME; /* power status flag, initialize to normal */
struct trb *epow_trb; /* pointer to timer block for epow */
/*
* NAME: i_epow
*
* FUNCTION:
* This routine calls all of the registered EPOW interrupt handlers.
*
* EXECUTION ENVIRONMENT:
* This routine can only be called by the interrupt handler
* that catches the EPOW interrupt on interrupt priority INTEPOW.
*
* EPOW interrupt handlers are registered via i_init. They are
* not called by i_slih though. This is because each of them
* must be called, not just the first that returns INTR_SUCC.
*
* This routine does not page fault.
*
* RETURN VALUE DESCRIPTION:
* INTR_SUCC
*
* EXTERNAL PROCEDURES CALLED:
* i_disable, i_enable, io_att, io_det
* All registered EPOW interrupt handlers.
*/
int
i_epow(
struct intr *handler) /* EPOW interrupt handler */
{
register ulong power_status; /* resume power status */
register ulong behavior; /* expected behavior */
register int ipri; /* interrupt priority */
register int i_flag; /* flag for intr struct */
/*
* We're running at INTEPOW
*/
/*
* Read the Power Status Register
*/
power_status = get_pksr();
behavior = (power_status >> BEHAVIOR_SHIFT) & BEHAVIOR_MASK;
power_status &= EPOW_MASK;
/*
* Don't act on warnings which are sure to be followed by more
* serious errors if real, so that a transient failure does not
* disrupt SCSI activity. Likewise don't act on "everything's fine".
*/
if ((behavior == USE_EPOW_BITS) &&
((power_status == PWR_OVERLOAD) ||
((power_status == NORMAL_STATE) && (epow_status == RESUME))))
return INTR_SUCC;
/* log errors */
suspend_log.psr = power_status;
if ( epow_status != SUSPEND ) {
if ( power_status == BATTERY ) {
/* running on battery */
i_flag = EPOW_BATTERY;
epow_status = SUSPEND_BAT;
pidsig(1, SIGPWR);
} else if ( (power_status == NORMAL_STATE) &&
(epow_status == SUSPEND_BAT) ) {
/*
* This implies that we have just gone from
* battery power back to main power. We will
* resume via the timer since it runs at INTTIMER.
* Start Epow timer for immediate interrupt.
*/
while(tstop(epow_trb));
epow_trb->func_data = (ulong)FROM_FLIH;
epow_trb->timeout.it_value.tv_sec = 0;
epow_trb->timeout.it_value.tv_nsec = 0;
tstart(epow_trb);
return( INTR_SUCC );
}
else {
/* loss of power */
i_flag = EPOW_SUSPEND;
epow_status = SUSPEND;
}
/*
* Run the list of interrupt handlers that have
* requested notification of EPOW. Call each of them.
*/
handler = handler->next;
while ( handler != (struct intr *)NULL ) {
handler->flags |= i_flag;
(void)(*handler->handler) (handler);
handler = handler->next;
}
}
errsave(&suspend_log, sizeof(suspend_log));
/*
* Start Epow timer for reasonable delay (allow for false EPOW)
* to pop if we're still alive; set func data to reflect
* we started the timer from the flih
*/
while(tstop(epow_trb));
epow_trb->func_data = (ulong)FROM_FLIH;
epow_trb->timeout.it_value.tv_sec = 0;
epow_trb->timeout.it_value.tv_nsec = 50000000;
tstart(epow_trb);
return( INTR_SUCC );
}
/*
* NAME: epow_timer
*
* FUNCTION:
* This routine is the timer handler for EPOW. It resumes normal system
* operation if power is resumed, and detects and reports fan faults
*
* EXECUTION ENVIRONMENT:
* This routine can only be called by the system timer service as a
* result of the timer expiring at INTMAX/INTEPOW.
*
* This routine does not page fault.
*
* RETURN VALUE DESCRIPTION:
* None
*
* EXTERNAL PROCEDURES CALLED:
*/
void
epow_timer(
struct trb *t) /* trb structure pointer */
{
register ulong power_status; /* resume power status */
register ulong behavior; /* expected behavior */
register int ipri; /* interrupt priority */
register int plvl=0; /* Processor level */
struct intr *handler; /* interrupt handler pointer*/
/*
* Read Power Status Register
*/
power_status = get_pksr();
behavior = (power_status >> BEHAVIOR_SHIFT) & BEHAVIOR_MASK;
power_status &= EPOW_MASK;
/*
* Fake behavior for machines not setting it
*/
if (behavior == USE_EPOW_BITS) {
switch (power_status) {
case THERMAL_WARNING :
case PWR_FAN_FAULT :
behavior = FAST_SHUTDOWN;
break;
case BATTERY :
behavior = IMMED_SHUTDOWN;
break;
}
}
#ifdef _RS6K_SMP_MCA
if ((__rs6k_smp_mca()) && (power_status & LOPP)) {
/* wait for the end of the EPOW transient */
epow_trb->func_data = (ulong)FROM_FLIH;
epow_trb->timeout.it_value.tv_sec = 0;
epow_trb->timeout.it_value.tv_nsec = 50000000;
tstart(epow_trb);
return;
}
#endif
if (t->func_data == FROM_FLIH) {
/*
* We must still be alive
*/
if ( epow_status != RESUME ) {
/*
* If an immediate power down condition is in effect,
* then we'd better not resume.
*/
if ((behavior == IMMED_SHUTDOWN) ||
(behavior == IMMEDX_SHUTDOWN))
return;
#ifdef _POWER_MP
/* get the process level of EPOW
* and lock the poll array
*/
plvl = i_genplvl( &epowhand );
#endif /* _POWER_MP */
/*
* We were suspended, we can resume.
* Traverse list of EPOW handlers.
*/
handler = epowhandler->next;
while (handler != (struct intr *)NULL) {
handler->flags |= EPOW_RESUME;
handler->flags &= ~(EPOW_BATTERY |EPOW_SUSPEND);
handler = handler->next;
}
epow_status = RESUME;
handler = epowhandler->next;
while (handler != (struct intr *)NULL) {
(void)(*handler->handler) (handler);
/*
* This allows any pending EPOW interrupts
* in.
*/
i_enable(INTCLASS0);
ipri = i_disable(INTEPOW);
if ( epow_status != RESUME )
/* We have had another EPOW so
* stop resuming
*/
return;
handler->flags &= ~( EPOW_RESUME |
EPOW_BATTERY | EPOW_SUSPEND);
handler = handler->next;
}
switch (behavior) {
case FAST_SHUTDOWN:
/*
* this was a Power Supply Fan Fault or
* a Thermal warning, start
* a timer for 20 seconds to allow the system
* a little more time to get things to disk.
*/
/*
* Make sure Epow timer isn't already running
* (shouldn't be possible).
*/
if (!(epow_trb->flags & T_ACTIVE)) {
/*
* Start Epow timer for 20 seconds
* set func data to reflect
* we started the timer from the timer
*/
epow_trb->func_data = (ulong)FROM_TRB;
epow_trb->timeout.it_value.tv_sec = 20;
epow_trb->timeout.it_value.tv_nsec = 0;
tstart(epow_trb);
}
/*
* signal init to take action
*/
pidsig(1, SIGPWR);
break;
case SLOW_SHUTDOWN:
/*
* we still have about 10 minutes before loss
* of power, start a timer for 10 minutes to
* allow for a clean shutdown of the system.
*/
/*
* Make sure Epow timer isn't already running
* (shouldn't be possible).
*/
if (!(epow_trb->flags & T_ACTIVE)) {
/*
* Start Epow timer for 10 minutes
* set func data to reflect
* we started the timer from the timer
*/
epow_trb->func_data = (ulong)FROM_TRB;
epow_trb->timeout.it_value.tv_sec = 600;
epow_trb->timeout.it_value.tv_nsec = 0;
tstart(epow_trb);
}
/*
* signal init to take action
*/
pidsig(1, SIGPWR);
break;
case USE_EPOW_BITS:
if (power_status == NORMAL_STATE) {
/*
* Must have been a false EPOW
* return to normal state
*/
resume_log.psr = power_status;
errsave(&resume_log,sizeof(resume_log));
break;
}
default:
/*
* Must have been a secondary fan fault,
* switch to battery backup, or an undefined
* power status. signal init to take action.
*/
pidsig(1, SIGPWR);
break;
}
} /* if (epow_suspend) ....else we were already resumed */
} else {
/*
* We were started from the Timer, which means we
* previously detected a power supply fan fault.
* See if the condition still exists, and if so
* suspend all registered handlers.
*/
if ((behavior == FAST_SHUTDOWN) ||
(behavior == SLOW_SHUTDOWN)) {
/*
* Yip, still a problem.
* Make sure we weren't already suspended
*/
if (epow_status != SUSPEND) {
/*
* Suspend handlers
*/
handler = epowhandler->next;
while ( handler != (struct intr *)NULL ) {
handler->flags |= EPOW_SUSPEND;
(void)(*handler->handler) (handler);
handler = handler->next;
}
epow_status = SUSPEND;
}
}
}
}
/*
* NAME: io_exception
*
* FUNCTION: called by ds_flih when a DSI occurs in iospace.
*
* EXECUTION ENVIORMENT:
* Called from ds_flih with interrupt disabled
*
* RETURNS: None
*/
void
io_exception(
ulong dar, /* faulting address */
ulong srval, /* faulting segment reg. value */
struct mstsave *mst, /* faulting mst */
ulong dsisr) /* machine dsisr register value */
{
volatile ulong *csr15;
volatile ulong *dsirr_ptr;
struct ipl_cb *iplcb_ptr; /* ros ipl cb pointer */
struct buc_info *buc_ptr; /* BUC info pointer */
struct b_protect *b_prt;
ulong ioccaddr;
int buid;
int num_bucs,buc_cnt;
int dsirr;
ulong csr_val;
int valid_buid;
int iocc_dev=FALSE;
int except;
struct ppda *ppda_ptr; /* PPC interrupt registers */
volatile struct ppcint *intr;
extern void check_bus_timeout();
assert(!__rspc());
buid = BID_TO_BUID(srval);
#ifdef _RS6K
if (__rs6k()) {
/*
* For PowerPC there isn't a Data Storage Interrupt Reason
* Register....
*/
dsirr = 0;
/* get addressability to iplinfo structure
*/
iplcb_ptr = (struct ipl_cb *) vmker.iplcbptr;
buc_ptr = (struct buc_info *)((uint)iplcb_ptr +
iplcb_ptr->s0.buc_info_offset);
num_bucs = buc_ptr->num_of_structs;
for(buc_cnt=0;buc_cnt < num_bucs;buc_cnt++) {
/*
* Find the BUC that had the exception
*/
if (buc_ptr->buid_data[0].buid_value == buid) {
/*
* Make sure this is an IOCC
*/
if (buc_ptr->IOCC_flag)
iocc_dev = TRUE;
break;
}
/*
* Point to the next BUC
*/
buc_ptr = (struct buc_info *)((uint)buc_ptr +
buc_ptr->struct_size);
}
}
#endif /* _RS6K */
#ifdef _POWER_RS
if (__power_rs()) {
if (__power_rsc()) {
/* the data storage interrupt reason
* register can't be read on RSC.
*/
dsirr = 0;
valid_buid = (buid == IOCC0_BUID || buid == SGA_BUID ||
buid == SCU_BUID);
check_bus_timeout(dar, srval, mst);
} else {
/* get data storage interrupt reason register
*/
dsirr_ptr = (ulong *)io_att(SCU_BID, DSIRR);
dsirr = *dsirr_ptr;
io_det(dsirr_ptr);
valid_buid = !(dsirr & DSIRR_IB);
}
}
#endif /* _POWER_RS */
/* store off error logging information:
*/
mst->except[0] = 0;
mst->except[1] = dsisr;
mst->except[2] = srval;
mst->except[3] = dar;
mst->except[4] = dsirr;
mst->o_vaddr = dar;
#ifdef _POWER_RS
if (__power_rs()) {
/*
* For the POWER platform, we know the hardcoded IOCC
* Bus IDs, and that exception status is available in
* channel status register 15
*/
if (valid_buid && buid >= IOCC0_BUID && buid <= IOCC3_BUID) {
/* check that the exception was in iocc space
* Setup access to the IOCC.
*/
ioccaddr = (ulong) io_att(IOCC_HANDLE(srval), 0);
/* Get value of channel status register 15
*/
csr15 = CSR_EFF_ADDR(15);
csr_val = *csr15;
RESET_STATUS(csr15);
mst->except[0] = csr_val;
io_det(ioccaddr);
/* Re-establish access to displays controlled by CSR15
* for a graphics thread.
* Make sure csr15 is set up correctly (even when this
* thread does not currently own any display on the bus)
*/
RESTORE_CSR15(buid);
}
/* get exception code to pas to p_slih
*/
switch(buid) {
case IOCC0_BUID:
case IOCC1_BUID:
case IOCC2_BUID:
case IOCC3_BUID:
except = EXCEPT_IO;
break;
case MSLA0_BUID:
case MSLA1_BUID:
case MSLA2_BUID:
case MSLA3_BUID:
case MSLA4_BUID:
case MSLA5_BUID:
case MSLA6_BUID:
case MSLA7_BUID:
except = EXCEPT_IO_SLA;
break;
case SGA_BUID:
except = EXCEPT_IO_SGA;
break;
default:
except = EXCEPT_IO_SCU;
}
}
#endif /* _POWER_RS */
#ifdef _RS6K
if (__rs6k()) {
/*
* For PowerPC, we know that only T=1 BUCs will cause
* direct store error interrupts, which are only IOCC type
* devices. We also know that the exception status is
* available in the DSEIR for this processor.
*/
if (iocc_dev) {
/*
* If a valid IOCC....
*/
/*
* !!!Determine which processor we're on and
* read the appropriate DSIER
*/
ppda_ptr = PPDA;
intr = (volatile struct ppcint *)(ppda_ptr->intr);
/*
* note that we make the exception status look
* like the old CSR 15 for device driver compatibility
*/
mst->except[0] = ((intr->dsier << 12) & 0xF0000000);
except = EXCEPT_IO;
} else {
except = EXCEPT_IO_SCU;
}
}
#endif /* _RS6K */
ASSERT(CSA != mst);
if (mst->intpri == INTBASE) {
/*
* if the exception was originated at INTBASE (graphics app),
* then run the exception handling at INTPAGER. Otherwise,
* allow the exception handling to run at INTMAX. NOTE: This
* will require the acquisition of a spin-lock for MP systems.
*/
i_enable(INTPAGER);
}
/* Call p_slih to handle the exception
*/
#ifndef _THREADS
p_slih(mst, dar, except, curproc);
#else /* _THREADS */
p_slih(mst, dar, except, curthread);
#endif
}
/*
* NAME: get_pksr()
*
* FUNCTION:
* gets power status / keylock register value.
*
* EXECUTION ENVIRONMENT:
* This routine can be called in an interrupt context.
*
* NOTES:
*
* RETURN VALUE DESCRIPTION:
* uint
*/
uint
get_pksr()
{
uint pksr;
volatile ulong *pow_stat_addr;
#ifdef _RS6K_SMP_MCA
int intpri = INTMAX;
cpu_t save_cpu;
#endif /* _RS6K_SMP_MCA */
#ifdef _POWER_RS
if (__power_rs()) {
/* Address of Power/Status register. */
pow_stat_addr = (ulong *)io_att(IOCC_HANDLE(IOCC_BID),
PSR_ADDRESS);
pksr = *pow_stat_addr;
io_det(pow_stat_addr);
}
#endif /* _POWER_RS */
#ifdef _RS6K
if (__rs6k())
{
#ifdef _RS6K_SMP_MCA
pksr = *pksr_addr;
if (pgs_SSGA_lvl == 2) {
if (CSA->intpri == INTBASE) {
save_cpu = CURTHREAD->t_cpuid;
switch_cpu (MP_MASTER, SET_PROCESSOR_ID);
intpri = i_disable (DISKETTE_INT_LVL);
}
if (fd_mutex)
d_abort_fd();
}
if ( (__rs6k_smp_mca()) &&
(~(((struct pgs_sys_spec*)
(sys_resource_ptr->sys_specific_regs))->iod_hw_sts) &
(EPOW_MCA_CAB | EPOW_MAIN_CAB)) )
pksr |= LOPP;
if (intpri == INTBASE) {
i_enable(intpri);
switch_cpu (save_cpu,RESET_PROCESSOR_ID);
}
#else
pksr = sys_resource_ptr->sys_regs.pwr_key_status;
#endif /* _RS6K_SMP_MCA */
}
#endif /* _RS6K */
#ifdef _RSPC
if (__rspc())
{
pksr = fw_keycase;
}
#endif
return(pksr);
} /* get_pksr() */