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

1537 lines
38 KiB
C

static char sccsid[] = "@(#)78 1.56 src/bos/kernel/ios/POWER/initiocc.c, sysios, bos41J 5/2/95 16:53:43";
/*
* COMPONENT_NAME: (SYSIOS) IO subsystem
*
* FUNCTIONS: initiocc
*
* 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/iocc.h>
#include <sys/iplcb.h>
#include <sys/nio.h>
#include <sys/machine.h>
#include <sys/except.h>
#include <sys/syspest.h>
#include <sys/vmker.h>
#include <sys/sysdma.h>
#include <sys/ppda.h>
#include <sys/systemcfg.h>
#include <sys/sys_resource.h>
#include <sys/adspace.h>
#include <sys/ioacc.h>
#include <sys/inline.h>
#include "intr_hw.h"
#include "dma_hw.h"
#include "interrupt.h"
#ifdef _RS6K_SMP_MCA
#include "pegasus.h"
#endif /* _RS6K_SMP_MCA */
#ifdef _RSPC
#include <sys/system_rspc.h>
#include <sys/residual.h>
#include <sys/genadpnp.h>
#include "mpic.h"
#endif
#ifdef _POWER_MP
#include <sys/lock_alloc.h>
#include <sys/lockname.h>
#endif /* _POWER_MP */
#ifdef _POWER
extern int num_ioccs;
#endif /* _POWER */
extern uint nio_buid, nio_slot;
extern uint nio_posdata;
extern struct io_map nio_map;
#ifndef _POWER_MP
extern struct ppda ppda[]; /* Need this for PPDA macro */
#endif /* _POWER_MP */
#ifdef _RS6K_SMP_MCA
#define APR_BIT_INDEX(intr) \
((struct sys_interrupt_regs *)intr - \
&sys_resource_ptr->sys_interrupt_space.sys_intr_regs[0])
#endif /* _RS6K_SMP_MCA */
#ifdef _POWER_RS
/*
* NAME: init_iocc_pwr
*
* FUNCTION: Initialize a POWER IOCC if it is present
*
* EXECUTION ENVIORMENT:
* This is called after VMSI from ios_init. This is still early in
* system initialization, but we are able to take IO exceptions
*
* RETURNS: None
*/
static void
init_iocc_pwr(
ulong bid, /* iocc bid */
int mlvl) /* level to map IOCC to */
{
int num_slave_tcws; /* tcws for slave channels */
int num_master_tcws; /* tcws for master channels */
int num_slave_chan; /* number of slave channels */
int first_slv_tcw; /* number of TCW used for slaves */
volatile struct iocc *i; /* IOCC */
ulong value; /* interrupt level mask */
int level; /* interrupt level */
int channel; /* DMA channel number */
struct slave_regs *d; /* DMA registers */
ulong tag_num; /* tag table index */
struct tag *t; /* tag entry */
uint num_tcws; /* number of TCWs */
uint tcw_num; /* TCW index */
uint ioccaddr; /* Effective address of IOCC */
void init_nio();
/* Setup addressibility to the IOCC.
*/
ioccaddr = (uint) io_att(bid, 0);
i = (volatile struct iocc *)(ioccaddr + IO_IOCC);
/*
* Initialize the IOCC interrupt control registers. All interrupts
* are disabled. Any pending interrupts are cleared. The bus
* interrupt levels are mapped to the most significant bits in EIS0.
*
* The IOCC is now in a state so that interrupt requests
* will not be processed. The i_init service must be
* called before taking an interrupt.
*/
i->int_enable = 0; /* disable all IOCC interrupts */
i->int_misc = 0; /* turn off miscellaneous interrupts */
i->rfi = 0; /* clear all pending interrupts */
/* Initialize the interrupt vector table, unless we are on a RSC
* machine, where the stores will except. This is useless for RS2
* but it won't hurt anything.
*/
if (!__power_rsc())
{
value = (mlvl << 24) | ((mlvl+1) << 16) | ((mlvl+2) << 8) |
(mlvl+3);
for ( level = 0; level < 4; level++ )
{
i->vector[level] = value;
value += 0x04040404;
}
}
/*
* Initialize the IOCC DMA control registers. All DMA
* channels are disabled. All buffers are marked as invalid.
*
* The IOCC is now in a state so that DMA requests
* will not be processed. The d_init service must be
* called before performing DMA operations.
*/
for ( channel = 0; channel < 16; channel++ )
{
d = SLAVE_EFF_ADDR(channel);
d->control = 0;
d->raddr = -1;
d->buf_ctl = BUF_INVALID;
}
num_tcws = ICF_TCWS(i->config);
num_slave_chan = ICF_NUM_SLVCHN(i->config);
/*
* Check if DMA slave opperations will be
* done with TCWs or TAGs
*/
if (i->config & ICF_SLAVE_TCW)
{
/*
* allocate:
* TCWs 16 -- (first_slave_tcw-1) for bus master
* opperations
*
* TCW fist_slave_tcw -- (num_tcws-1) are for
* slave operations, with TAGS_PER_CHANNEL TCWs
* allocated for each channel
*/
num_slave_tcws = ICF_SLV_TCWS(i->config);
num_master_tcws = ICF_NUM_TCWS(i->config);
first_slv_tcw = num_master_tcws;
}
else
{
/*
* initialize TAG table
*/
for ( tag_num = 0; tag_num < (TAGS_PER_CHANNEL * 16);
tag_num++ )
{
t = TAG_EFF_ADDR(tag_num);
t->control = 0;
t->raddr = -1;
}
/*
* allocate all TCWs for use by DMA master opperations
*/
num_slave_tcws = 0;
num_master_tcws = num_tcws;
first_slv_tcw = -1;
}
/*
* Initialize the TCWs allocated for DMA masters.
*/
for (tcw_num = 0; tcw_num < num_master_tcws; tcw_num++)
{
iocc_rw(IOCC_WRITE, TCW_EFF_ADDR(tcw_num),
MASTER_TCW(-1, 0xF, 0, TCW_NO_ACCESS));
tlbi(tcw_num << DMA_L2PSIZE);
}
/*
* initialize TCWs allocated for DMA slave
*/
for (tcw_num = num_master_tcws; tcw_num < num_tcws; tcw_num++)
{
iocc_rw(IOCC_WRITE, TCW_EFF_ADDR(tcw_num),
SLAVE_TCW(-1));
tlbi(tcw_num << DMA_L2PSIZE);
}
/* If IOCC 0 is being initialized set global vaiables that
* are tested in IOS services
*/
if (bid == IOCC0_BID)
{
iocc_config = i->config;
first_slave_tcw = first_slv_tcw;
}
/*
* Call the Native I/O Initialization to setup any NIO that
* may be on this IOCC
*/
init_nio(ioccaddr, bid);
/*
* Clean up after IOCC initialization.
*/
io_det( ioccaddr );
}
#endif /* _POWER_RS */
#ifdef _RS6K
/*
* NAME: init_iocc_ppc
*
* FUNCTION: Initialize a PowerPC IOCC if it is present
*
* EXECUTION ENVIORMENT:
* This is called after VMSI from ios_init. This is still early in
* system initialization, but we are able to take IO exceptions
*
* RETURNS: None
*/
static void
init_iocc_ppc(
int i_cnt, /* iocc_info index */
ulong bid) /* iocc bid */
{
int num_master_tcws; /* tcws for master channels */
int num_slave_chan; /* number of slave channels */
volatile struct iocc_ppc *i; /* IOCC */
ulong value; /* interrupt level mask */
int level; /* interrupt level */
uint tcw_num; /* TCW index */
uint ioccaddr; /* Effective address of IOCC */
struct iocc_info *dp; /* Pointer to IOCC DMA info struct */
uint cfg_reg; /* contents of IOCC config register */
struct ipl_cb *iplcb_ptr; /* ipl cb pointer */
void init_nio();
/* Setup addressibility to the IOCC.
*/
ioccaddr = (uint) io_att(bid, 0);
i = (volatile struct iocc_ppc *)(ioccaddr + IO_IOCC_PPC);
/*
* Initialize the IOCC interrupt control registers. All interrupts
* are disabled. Any pending interrupts are cleared.
*
* The IOCC is now in a state so that interrupt requests
* will not be processed. The i_init service must be
* called before taking an interrupt.
*/
i->int_enable = 0; /* disable all IOCC interrupts */
i->int_misc = 0; /* turn off miscellaneous interrupts */
/*
* Initialize the interrupt vector table(XIVR) and
* issue an EOI for each level
*/
value = 0xffff; /* 0xffXX = Any processor */
/* 0xXXff = least favored prority */
for ( level = 0; level < NUM_BUS_SOURCE; level++ )
{
i->xivr[level] = value;
i->eoi[level] = 0;
}
/*
* Read IOCC configuration Register
*/
cfg_reg = i->config;
/*
* Set up a pointer to this IOCC's info structure
*/
dp = (struct iocc_info *)&iocc_info[i_cnt];
/*
* Initialize DMA channel allocation bitmask (1 = free)
*/
dp->dma_chans = INIT_DMA_CHAN_TAB();
#ifdef _POWER_MP
lock_alloc( &dp->iocc_lock, LOCK_ALLOC_PIN, DMA_LOCK_CLASS, i_cnt );
simple_lock_init( &dp->iocc_lock );
#endif
/*
* Compute Number of Slave Channels, and initialize
* the control register allocation bitmask
*/
dp->num_slave_chans = NUM_SLVCHN_PPC(cfg_reg);
dp->slave_ctrl_regs = INIT_SLV_CTRL_TAB(dp->num_slave_chans);
/*
* Initialize TCE Information
*/
dp->num_tces = NUM_TCES_PPC(i->tce_addr_low);
dp->first_slave_tce = num_master_tcws
= dp->num_tces - SLV_TCES_PPC(cfg_reg);
/*
* Initialize the TCEs allocated for DMA masters.
*/
for (tcw_num = 0; tcw_num < num_master_tcws ; tcw_num++) {
/*
* for each master TCE, set it up for page fault
*/
*(uint *)(TCE_EFF_ADDR_PPC(dp->tce_offset, tcw_num)) =
(uint)MASTER_TCE_PPC(-1, 0, TCE_PAGE_FAULT);
}
/*
* Initialize TCEs allocated for DMA slave
*/
for (tcw_num = num_master_tcws; tcw_num < dp->num_tces;
tcw_num++) {
*(uint *)(TCE_EFF_ADDR_PPC(dp->tce_offset, tcw_num)) =
(uint)SLAVE_TCE_PPC(-1);
}
/*
* Perform SYNC so everything is globally seen
*/
__iospace_sync();
/*
* Call the Native I/O Initialization to setup any NIO that
* may be on this IOCC
*/
init_nio(ioccaddr, bid);
/*
* Clean up after IOCC initialization.
*/
io_det( ioccaddr );
}
#endif /* _POWER_PC */
/*
* NAME: init_nio
*
* FUNCTION: This routine is called to initialize a NIO card and
* component reset register.
*
* EXECUTION ENVIRONMENT:
*
* This service executes in the early part of system initialization,
* prior to fully establishing a process or interrupt handler
* execution environment.
* Called by iocc_init().
*
* EXTERNAL PROCEDURES CALLED:
*
*/
void
init_nio(ulong ioccaddr, /* ioccaddr attached by initiocc() */
ulong bid) /* BUSID of current IOCC */
{
ulong ioaddr; /* io_att return value */
int posdata; /* hold pos info */
int slot,i;
struct ipl_cb *iplcb_ptr; /* ipl cb pointer */
struct ipl_directory *dir_ptr; /* iplcb directeory */
char *vpd_ptr; /* iplcb VPD pointer */
char NIO_level=0; /* level of NIO chip */
/* If this is a Rainbow box, Look in the iplcb to determine the
* NIO chip level.
* NOTE: The VPD fields of the IPLCB are not present on all
* implementations.
*/
if (__rs6k_up_mca()) {
iplcb_ptr = (struct ipl_cb *) vmker.iplcbptr;
dir_ptr = &(iplcb_ptr->s0);
vpd_ptr = (char *) ((char *) iplcb_ptr +
dir_ptr->system_vpd_offset);
NIO_level = vpd_ptr[dir_ptr->system_vpd_size - 1];
}
/*
* Remove the hard reset to the NIO card via the component
* reset register. Also, remove the slot resets.
*/
#if defined(_KDB)
if (!__kdb()) /* reset done by kdb */
#endif /* _KDB */
if (__rs6k()) {
if ( __rs6k_up_mca() && (NIO_level == 0x01)) {
/* this is a 601 box with DD1 NIO, we can't read the
* Component Reset Reg...
*/
*(volatile uint *)(ioccaddr + COMP_RESET_REG_PPC) = 0;
for (i=0;i<1000000;i++)
*(volatile uint *)(ioccaddr + IOCC_DELAY) = 0;
*(volatile uint *)(ioccaddr + COMP_RESET_REG_PPC) =
0xff000007;
} else if (__rs6k_smp_mca()) {
/* With IONIAN, we can't read the Component
* Reset Register
*/
*(volatile uint *)(ioccaddr + COMP_RESET_REG_PPC) =
0xffff0007;
} else {
/*
* This is the right way to remove the reset
*/
*(volatile uint *)(ioccaddr + COMP_RESET_REG_PPC) |=
0xff000007;
}
} else
*(volatile uint *)(ioccaddr + COMP_RESET_REG) |= 0xff000007;
/*
* Determine if NIO is present.
*/
for (slot=0;slot < MAX_SLOTS;slot++) {
posdata = (*(volatile uchar *)
(ioccaddr + POS_ADDR(slot, 0))) << 8;
posdata |= *(volatile uchar *)(ioccaddr + POS_ADDR(slot, 1));
posdata &= 0xffff;
switch (posdata) {
/* RS1 planars
*/
case NIO_POS0_REL1:
case NIO_POS0_REL2:
#if defined(_KDB)
#ifdef _POWER_RS
if (__kdb() && __power_rs()) {
if (bid == IOCC0_BID) {
nio_buid = bid;
nio_posdata = posdata;
nio_slot = slot;
break; /* reset already done by kdb */
}
}
#endif /* _POWER_RS */
#ifdef _RS6K
if (__kdb() && __rs6k()) {
if (bid == iocc_info[0].bid) {
nio_buid = bid;
nio_posdata = posdata;
nio_slot = slot;
break; /* reset already done by kdb */
}
}
#endif /* _RS6K */
#endif /* _KDB */
/*
* Reset the Reset/VPD POS register.
*/
*(volatile unsigned char *)
(ioccaddr + POS_ADDR(slot,2)) = 0xbd;
*(volatile unsigned char *)
(ioccaddr + POS_ADDR(slot,2)) = 0xbf;
/*
* Setup addressibility to the IO space.
*/
ioaddr = (ulong) io_att((bid | IO_SEG_REG),0);
/*
* Initialize the keyboard with interrupts disabled.
*/
*(volatile unsigned char *)
(ioaddr + KBD_CONFIG_REG) = 0xc3;
*(volatile unsigned char *)
(ioaddr + KBD_CONFIG_REG) = 0x08;
/*
* Clean up after IO initialization.
*/
io_det( ioaddr );
/*
* Remove reset mode.
*/
*(volatile unsigned char *)
(ioccaddr + POS_ADDR(slot,2)) = 0x03;
/*
* set up the global NIO Bus Id field
*/
nio_buid = bid;
nio_posdata = posdata;
nio_slot = slot;
break;
/* RSC and 601 planars
*/
case NIO_POS0_RSC1:
#if defined(_KDB)
#ifdef _POWER_RS
if (__kdb() && __power_rs()) {
if (bid == IOCC0_BID) {
nio_buid = bid;
nio_posdata = posdata;
nio_slot = slot;
break; /* reset already done by kdb */
}
}
#endif /* _POWER_RS */
#ifdef _RS6K
if (__kdb() && __rs6k()) {
if (bid == iocc_info[0].bid) {
nio_buid = bid;
nio_posdata = posdata;
nio_slot = slot;
break; /* reset already done by kdb */
}
}
#endif /* _RS6K */
#endif /* _KDB */
/* Reset the standard IO devices
*/
*(volatile unsigned char *)
(ioccaddr + POS_ADDR(slot,2)) = 0xff;
if (__rs6k_up_mca() && (NIO_level == 0x01)) {
/*
* if this is a Rainbow box with a DD1 NIO chip
* then we must disable data parity checking
*/
*(volatile unsigned char *)(ioccaddr +
POS_ADDR(slot,2)) = 0x01;
} else {
/*
* set up the normal value
*/
*(volatile unsigned char *)(ioccaddr +
POS_ADDR(slot,2)) = 0x03;
}
/*
* set up the global NIO Bus Id field
*/
nio_buid = bid;
nio_posdata = posdata;
nio_slot = slot;
break;
#ifdef _RS6K_SMP_MCA
case NIO_POS0_PEGASUS:
#if defined(_KDB)
if (__kdb() && __rs6k_smp_mca()) {
if (bid == iocc_info[0].bid) {
nio_buid = bid;
nio_posdata = posdata;
nio_slot = slot;
break; /* reset already done by kdb */
}
}
#endif /* _KDB */
/* Reset the 3rd serial line, enable Native IO
*/
*(volatile unsigned char *)
(ioccaddr + POS_ADDR(slot,2)) = 0x05;
*(volatile unsigned char *)
(ioccaddr + POS_ADDR(slot,2)) = 0x01;
/*
* set up the global NIO Bus Id field
*/
nio_buid = bid;
nio_posdata = posdata;
nio_slot = slot;
break;
#endif /* _RS6K_SMP_MCA */
default:
break;
}
}
return ;
}
#ifdef _POWER_RS
/*
* NAME: init_ios_pwr
*
* FUNCTION: This routine is called by ios_init() to perform POWER platform
* specific initialization. It initializes the hardware for the
* I/O Subsystem.
* This includes the following:
*
* Initialize the decrementer's EIS level.
* Initialize the EPOW's EIS level.
* Initialize the PC AT to Micro Channel converter.
* Initialize the IOCC(s).
* Initialize the Native I/O.
* Initialize the EIMs and EISs - if RS1/RSC.
* Initialize the PEISs and CIL - if RS2.
*
* EXECUTION ENVIRONMENT:
*
* This service executes in the early part of system initialization,
* prior to fully establishing a process or interrupt handler
* execution environment.
*
* NOTES:
* Check the IPL contol block before touching buid 21. This is
* because some Lamp. machines will check stop instead of DSI on
* a buid 21 access.
*
* EXTERNAL PROCEDURES CALLED:
* io_att, io_det
*
*/
void
ios_init_pwr()
{
volatile ulong *dec; /* EICR */
volatile ulong *exch; /* EICR */
volatile ulong *epow; /* EICR */
#if defined( _POWER_RS1 ) || defined( _POWER_RSC )
volatile ulong *eim; /* EIM */
volatile ulong *eis; /* EIS */
ulong trash; /* return data ignored */
#endif /* _POWER_RS1 || _POWER_RSC */
int i; /* Loop counter */
ulong sreset_ptr; /* system reset count ptr */
ulong ioccaddr; /* io_att return value */
struct ipl_cb *iplcb_ptr; /* ipl cb pointer */
struct ipl_info *info_ptr; /* ipl info pointer */
struct iocc_post_results *post_ptr; /* ipl iocc posts */
struct ipl_directory *dir_ptr; /* iplcb directeory */
int i_cnt; /* count of IOCCs */
struct ppda *ppda_ptr; /* Processor info */
/*
* Tell the decrementer to use INT_TIMLVL. The decrementer can
* not be turned off and defaults to using the most significant
* EIS bit (bit 0 of EIS0). This must be done before the EIS
* and EIM are initialized so that an interrupt will not be
* left pending on interrupt level 0.
*
* PowerPC decrementer has its own low memory vector and is
* enabled/disabled by the MSR(EE) bit.
*/
ioccaddr = (ulong) io_att( EICRBID, 0 );
dec = (volatile ulong *)(ioccaddr + DEC_EISBID);
*dec = INT_TIMLVL;
io_det( ioccaddr );
/*
* Set the decrementer to max time and hope this is enough time
* to get the interrupt subsystem initialized. If the debugger
* is invoked then this won't happen but only side effect of
* this is phantom interrupts detected in the flih.
*
* Any pending bogus decrementer interrupts will be discarded
* when the EIS/PEIS is initialized(cleared) later.
*/
mtdec( -1 );
/*
* Tell the EPOW handler to use INT_EPOW
*/
ioccaddr = (ulong) io_att( EICRBID, 0 );
epow = (volatile ulong *)(ioccaddr + EPOW_EISBID);
#if defined( _POWER_RS1 ) || defined( _POWER_RSC )
if( __power_set( POWER_RS1 | POWER_RSC )) {
*epow = INT_EPOW_RS1;
}
#endif /* _POWER_RS1 || _POWER_RSC */
#ifdef _POWER_RS2
if( __power_rs2() ) {
*epow = INT_EPOW_RS2;
}
#endif /* _POWER_RS2 */
io_det( ioccaddr );
/*
* Tell the external check handler to use INT_SCUB
*/
ioccaddr = (ulong) io_att( EICRBID, 0 );
exch = (volatile ulong *)(ioccaddr + MEEB_EISBID);
#if defined( _POWER_RS1 ) || defined( _POWER_RSC )
if( __power_set( POWER_RS1 | POWER_RSC )) {
*exch = INT_SCUB_RS1;
}
#endif /* _POWER_RS1 || _POWER_RSC */
#ifdef _POWER_RS2
if( __power_rs2() ) {
*exch = INT_SCUB_RS2;
}
#endif /* _POWER_RS2 */
io_det( ioccaddr );
/*
* Initialize IOCCs on the POWER platforms
*/
/* Look in the iplcb to determine the number of IOCCs present.
*
* When the dust settles down around the iplcb then the method
* of locating and identifying an IOCC should differ between
* the POWER and PPC boxes. PPC boxes should pick up the
* assigned BUIDs from there. For the time being PPC IOCCs
* will use BUID 20-23 just like POWER.
*/
iplcb_ptr = (struct ipl_cb *) vmker.iplcbptr;
dir_ptr = &(iplcb_ptr->s0);
post_ptr = (struct iocc_post_results *) ((char *) iplcb_ptr +
dir_ptr->iocc_post_results_offset);
init_iocc_pwr(IOCC0_BID, 0);
/* Initialize IOCCs */
if (post_ptr->length == 2) {
init_iocc_pwr(IOCC1_BID, LVL_PER_WORD);
num_ioccs = 2;
}
else {
num_ioccs = 1;
}
/*
* Initialize the system reset counter to enable warm IPL
* For POWER platforms
*/
sreset_ptr = io_att(IOCC0_BID, 0x004000E0);
if (__power_rsc()) {
*(volatile ulong *)sreset_ptr = 0;
} else {
*(volatile uchar *)sreset_ptr = 0;
}
io_det(sreset_ptr);
#if defined( _POWER_RS1 ) || defined( _POWER_RSC )
/*
* Clear the EIM and EIS. The i_init service will initialize
* an array of EIM values with one entry for each priority.
* This array is defined in interrupt.h. Code in low.s uses
* this array to set the EIM as per the desired priority.
*/
if( __power_set( POWER_RS1 | POWER_RSC ))
{
ioccaddr = (ulong) io_att( EICRBID, 0 );
eim = (volatile ulong *)(ioccaddr + EIM0);
*eim = 0;
eim = (volatile ulong *)(ioccaddr + EIM1);
*eim = 0;
eis = (volatile ulong *)(ioccaddr + EIS0);
trash = *eis;
eis = (volatile ulong *)(ioccaddr + EIS1);
trash = *eis;
io_det( ioccaddr );
}
#endif /* _POWER_RS1 || _POWER_RSC */
#ifdef _POWER_RS2
/*
* Clear any bogus pending interrupts and set CIL to most
* favored interrupt level.
*/
if( __power_rs2() )
{
ulong peis0, peis1;
int plvl;
mfpeis( &peis0, &peis1 );
while( peis0 ) {
plvl = bitindex( &peis0 );
peis0 &= ~((ulong)(0x80000000) >> plvl);
i_peis_reset( plvl );
}
while( peis1 ) {
plvl = bitindex( &peis1 );
peis1 &= ~((ulong)(0x80000000) >> plvl);
plvl += LVL_PER_WORD;
i_peis_reset( plvl );
}
/*
* Now make current interrupt level 0
*/
i_cil_set( 0 );
}
#endif /* _POWER_RS2 */
}
#endif /* _POWER_RS */
#ifdef _RS6K
/*
* NAME: init_ios_ppc
*
* FUNCTION: This routine is called by ios_init() to perform PowerPC platform
* specific initialization. It initializes the hardware for the I/O
* Subsystem.
* This includes the following:
*
* Initialize the decrementer's EIS level.
* Initialize the IOCC(s).
* Initialize the Native I/O.
* Initialize the CPPR - if PPC.
*
* EXECUTION ENVIRONMENT:
*
* This service executes in the early part of system initialization,
* prior to fully establishing a process or interrupt handler
* execution environment.
*
* EXTERNAL PROCEDURES CALLED:
* io_att, io_det
*
*/
void
ios_init_ppc()
{
int i_cnt; /* count of IOCCs */
struct ppda *ppda_ptr; /* Processor info */
volatile struct ppcint *intr; /* PPC interrupt registers */
#ifdef _POWER_MP
extern Simple_lock int_hw_lock; /* XIVR/IER/IRR global lock */
extern uint cppr_random;
#endif
/*
* Set the decrementer to max time and hope this is enough time
* to get the interrupt subsystem initialized. If the debugger
* is invoked then this won't happen but only side effect of
* this is phantom interrupts detected in the flih.
*
* Any pending bogus decrementer interrupts will be discarded
* when the EIS/PEIS is initialized(cleared) later.
*/
mtdec( -1 );
/*
* NOTE: No setup necessory for EPOW, as
* _POWER_PC machines are hardwired to use BUID 0 src 1.
*/
/*
* Initialize all IOCCs that were defined by hardinit()
* for the PowerPC platform
*/
#ifdef _POWER_MP
lock_alloc(&int_hw_lock, LOCK_ALLOC_PIN, IOS_LOCK_CLASS, 0);
simple_lock_init(&int_hw_lock);
#endif
for(i_cnt=0;i_cnt < MAX_NUM_IOCCS;i_cnt++) {
if (iocc_info[i_cnt].bid != 0xFFFFFFFF) {
/*
* if a valid IOCC
*/
init_iocc_ppc(i_cnt, iocc_info[i_cnt].bid);
}
}
/*
* Initialize the system reset counter to enable warm IPL
* For PowerPC platforms
*/
#ifdef _RS6K_SMP_MCA
*rsr_addr = 0; /* works on all PowerPC platforms */
#else
sys_resource_ptr->sys_regs.reset_status = 0;
#endif
ppda_ptr = PPDA;
/*
* Set the MFRR to no interrupt
* Set the CPPR to most favored interrupt priority
*/
intr = (volatile struct ppcint *)(ppda_ptr->intr);
intr->mfrr = 0xff;
intr->i_cppr = INTMAX;
__iospace_eieio(); /* 601/Ithaca workaround */
#ifdef _POWER_MP
ppda_ptr->mpc_pend = 0;
ppda_ptr->mfrr_pend = 0;
ppda_ptr->mfrr_lock = 0;
#endif /* _POWER_MP */
#ifdef _RS6K_SMP_MCA
if (__rs6k_smp_mca()) {
/*
* Set the corresponding APR bit to allow interrupts,
* reset other bits (safety).
*/
((struct pgs_apr*)(sys_resource_ptr->reserved3))->apr
= (1 << (7 - APR_BIT_INDEX(intr)));
/*
* Even balancing for interrupts within the processor complex
*/
cppr_random = 1;
}
#endif /* _RS6K_SMP_MCA */
}
#endif /* _RS6K */
#ifdef _POWER_MP
/*
* NAME: ios_bs_proc
*
* INPUT:
* _rs6k:
* ppda address (in SPRG0)
* ppda->intr
* _rspc:
* MPIC I/O Map
* Physical CPU ID
*
* FUNCTION: This routine is called by main_bs_proc() to perform PowerPC
* platform specific initialization for boot slave processors:
* _rs6k:
* Initialize the CPPR MFRR - if PPC.
* Initialize the APR - if PEGASUS.
* _rspc:
* Enable interrupts to the local processor by writing down the
* Current Task Priority Register from 0xF (disabled).
*
* RETURNS: None.
*/
void
ios_bs_proc()
{
#ifdef _RS6K
if (__rs6k())
{
struct ppda *ppda_ptr; /* Processor info */
volatile struct ppcint *intr; /* PPC interrupt regs */
ppda_ptr = PPDA;
/*
* Set the MFRR to no interrupt
* Set the CPPR to most favored interrupt priority
*/
intr = (volatile struct ppcint *)(ppda_ptr->intr);
intr->mfrr = 0xFF;
intr->i_cppr = INTBASE;
ppda_ptr->mpc_pend = 0;
ppda_ptr->mfrr_pend = 0;
__iospace_sync();
#ifdef _RS6K_SMP_MCA
/*
* Set the corresponding APR bit to allow interrupts.
*/
if (__rs6k_smp_mca())
{
((struct pgs_apr*)(sys_resource_ptr->reserved3))->apr
|= (1 << (7 - APR_BIT_INDEX(intr)));
}
}
#endif /* _RS6K_SMP_MCA */
#endif /* _RS6K */
#if defined( _RSPC ) && defined( _RSPC_MP_PCI )
if (__rspc() && __rspc_mp_pci())
{
int phys_cpu;
volatile struct s_mpic *Mpic;
union { struct taskpri s;
unsigned int i; } tp;
extern struct io_map mpic_map;
Mpic = (volatile struct s_mpic *)iomem_att( &mpic_map );
/*
* Enable routing of interrupts to the current processor
*/
phys_cpu = my_phys_id();
tp.i = MpicRead( &Mpic->procreg[phys_cpu].taskpri );
tp.s.priority = MPIC_PRI(INTBASE);
MpicWrite( &Mpic->procreg[phys_cpu].taskpri, tp.i );
/*
* Set platform-specific APR.
* Victory: N/A
*/
sync();
iomem_det( (void *)Mpic );
}
#endif /* _RSPC && _RSPC_MP_PCI */
}
#endif /* _POWER_MP */
#ifdef _RSPC
/*
* NAME: ios_init_rspc
*
* FUNCTION: This routine is called by ios_init() to perform platform
* specific initialization. It initializes the hardware for the I/O
* Subsystem. The 8259s are part of the E/ISA Bridge chip.
* This includes the following:
*
* Reinitializing the 8259s and/or MPIC
* When applicable:
* Setting up the 8259 ELCR registers for edge trigger mode
* except IRQ 15 which is level sensitive.
* Setting up the MPIC Interrupt Source Configuration and
* Destination registers.
*
* Initialize NMI
*
* NOTE: The 8259s should have been initialized before the kernel gets
* control, to the point of being quiet. They are reinitialized
* here so AIX can set them up differently if needed.
*
* EXECUTION ENVIRONMENT:
* This service executes in the early part of system initialization,
* prior to fully establishing a process or interrupt handler
* execution environment. You need not acquire any locks because only
* one processor is running the operating system yet.
*
* EXTERNAL PROCEDURES CALLED:
*/
void
ios_init_rspc()
{
char reg;
int i;
int numIRQ;
unsigned int cpumask;
unsigned int master; /* Master proc. Phys ID */
volatile unsigned char *ioseg; /* Pointer to IO segment */
extern struct io_map mpic_map;
extern void setup_intctl();
#ifdef _POWER_MP
extern Simple_lock int_hw_lock; /* MPIC global lock */
#endif
#ifdef _POWER_MP
lock_alloc(&int_hw_lock, LOCK_ALLOC_PIN, IOS_LOCK_CLASS, 0);
simple_lock_init(&int_hw_lock);
#endif /* _POWER_MP */
/* get addressability to IO space */
ioseg = (char *)iomem_att( &nio_map );
/* Determine to interrupt architecture. The global int (machine.h)
* mach_model says what platform we're on. That gets set in
* hardinit.c, before this code runs.
*/
setup_intctl(); /* Query resid data */
if ( ( intctl_pri == INT_CTL_8259 ) || ( intctl_sec == INT_CTL_8259 ) )
{
/* Initialize the master 8259 */
*(ioseg + INTA00) = ICW1;
eieio();
*(ioseg + INTA01) = ICW2A;
eieio();
*(ioseg + INTA01) = ICW3A;
eieio();
*(ioseg + INTA01) = ICW4;
eieio();
*(ioseg + INTA01) = 0xFF; /* Mask all interrupts lines */
eieio();
/* Initialize the slave 8259 */
*(ioseg + INTB00) = ICW1;
eieio();
*(ioseg + INTB01) = ICW2B;
eieio();
*(ioseg + INTB01) = ICW3B;
eieio();
*(ioseg + INTB01) = ICW4;
eieio();
*(ioseg + INTB01) = 0xFF; /* Mask all interrupts lines */
eieio();
*(ioseg + ELCR0) = 0; /* everybody on edge */
*(ioseg + ELCR1) = 0;
__iospace_sync();
}
if ( intctl_pri == INT_CTL_MPIC )
{
union { struct feature0 s;
unsigned int i; } feat0;
union { struct globalcfg0 s;
unsigned int i; } gcfg0;
union { struct ipivecpri s;
unsigned int i; } ivp;
union { struct timer_vecpri s;
unsigned int i; } tvp;
union { struct vecpri s;
unsigned int i; } vp;
union { struct taskpri s;
unsigned int i; } tp;
volatile struct s_mpic *Mpic;
/* Generate the MPIC destination bitmask for the master CPU.
*/
#ifdef _POWER_MP
master = my_phys_id();
#else /* _POWER_MP */
master = 0;
#endif /* _POWER_MP */
master = 1 << master;
mpic_map.key = IO_MEM_MAP;
mpic_map.flags = 0;
mpic_map.size = 0x40000;
mpic_map.bid = REALMEM_BID;
mpic_map.busaddr = mpic_base;
/* get addressability to the MPIC
*/
Mpic = (volatile struct s_mpic *)iomem_att( &mpic_map );
/* Initialize the MPIC controller */
feat0.i = MpicRead( &Mpic->global.feature0 );
numIRQ = feat0.s.numIRQ;
if ( numIRQ >= 0xF0 )
numIRQ = 0xEF;
/* Generate the MPIC destination bitmask for all possible CPUs
* in the system. This works because every CPU's Current Task
* Priority register is currently 0xF, i.e. disabled. Only
* CPUs that start will lower that and enable interrupts to
* themselves. MPIC numIRQ reports 3 for a 4 CPU system.
*/
i = feat0.s.numCPU;
cpumask = ( 1 << ( i + 1 ) ) - 1;
/* Set the global operating mode to indicate cascaded 8259s
*/
gcfg0.i = MpicRead( &Mpic->global.globalcfg0 );
gcfg0.s.mode = MPIC_GMODE_MIXED;
MpicWrite( &Mpic->global.globalcfg0, gcfg0.i );
/* Initialize Per-Processor Registers
*/
while( i >= 0 )
{
/* There are currently no items of this type */
i -= 1;
}
/* Initialize Global Per-Timer Registers
*/
for( i = 0; i <= 3; i += 1 )
{
tvp.i = MpicRead( &Mpic->timer[i].timer_vecpri );
tvp.s.mask = MPIC_MASK_DISABLED;
MpicWrite( &Mpic->timer[i].timer_vecpri, tvp.i );
}
/* Initialize Global Per-IPI Registers
*/
for( i = 0; i <= 3; i += 1 )
{
ivp.i = MpicRead( &Mpic->global.ivp[i].ipivecpri );
ivp.s.mask = MPIC_MASK_DISABLED;
MpicWrite( &Mpic->global.ivp[i].ipivecpri, ivp.i );
}
/* Initialize Per-Interrupt Source Registers
*/
for( i = 0; i < numIRQ; i += 1 )
{
vp.i = MpicRead( &Mpic->intsource[i].vecpri );
/* Disable the interrupt source */
vp.s.mask = MPIC_MASK_DISABLED;
MpicWrite( &Mpic->intsource[i].vecpri, vp.i );
eieio();
/* Its permanent vector value */
vp.s.vector = MPIC_VECT(i);
/* Make it level sensitive */
vp.s.sense = MPIC_SENSE_LEVEL;
/* Make it active low, the PCI Default */
vp.s.polarity = MPIC_POLARITY_LOW;
MpicWrite( &Mpic->intsource[i].vecpri, vp.i );
/* Steer to any processor */
MpicWrite( &Mpic->intsource[i].destination, cpumask );
}
if ( intctl_sec == INT_CTL_8259 )
{
/* The 8259s are physically wired to MPIC interrupt
* zero (0). The 8259 input to MPIC must be
* programmed Active High. Set the cascaded 8259
* source (ISA) to priority INTMAX. Steer ISA
* interrupts to the master processor (Funnelled).
* Turn on the MPIC source for the cascaded 8259s (ISA)
*/
vp.i = MpicRead( &Mpic->intsource[0].vecpri );
vp.s.polarity = MPIC_POLARITY_HIGH;
vp.s.priority = MPIC_PRI(INTMAX);
MpicWrite( &Mpic->intsource[0].vecpri, vp.i );
MpicWrite( &Mpic->intsource[0].destination, master );
eieio();
vp.s.mask = MPIC_MASK_ENABLED;
MpicWrite( &Mpic->intsource[0].vecpri, vp.i );
}
__iospace_sync();
/* Enable routing interrupts to this (the master) processor
*/
tp.i = MpicRead( &Mpic->procreg[0].taskpri );
tp.s.priority = MPIC_PRI(INTBASE);
MpicWrite( &Mpic->procreg[0].taskpri, tp.i );
eieio();
/* get addressability back to IO space
*/
iomem_det( (void *)Mpic );
ioseg = (char *)iomem_att( &nio_map );
}
/* Enable NMI to permit machine check notifications */
reg = *(ioseg + NMISC);
reg &= ~NMISC_INIT;
*(ioseg + NMISC) = reg;
eieio();
*(ioseg + NMIREG) = NMIREG_INIT;
__iospace_sync();
iomem_det( (void *)ioseg );
/* set decrementer to max */
mtdec( -1 );
}
/*
* NAME: setup_intctl
*
* FUNCTION: This routine is called during system initialization on all
* RSPC systems whose machine model byte reads 0x4c. Such systems
* indicate their interrupt control architecture through residual
* data. Only one processor on an MP system will ever run this code.
* Once the two system global variables intctl_pri and intctl_sec
* are written to indicate the appropriate controllers, the system
* should be capable of servicing calls to i_init() and physical
* interrupts.
*
* EXECUTION ENVIRONMENT:
*
* This service executes in the early part of system initialization,
* prior to fully establishing a process or interrupt handler
* execution environment.
* This is initialization code, so it has many assertions.
*
* EXTERNAL PROCEDURES CALLED:
* query_parms_mpic() - later in this file
* query_parms_8259() - later in this file
*/
void
setup_intctl()
{
int i;
int actual;
int i8259 = -1;
int iMPIC = -1;
int npics = 0;
extern void query_parms_mpic( struct _GenericAddr * );
extern void query_parms_8259( struct _S8_Pack * );
struct ipl_cb *iplcb;
struct ipl_directory *idir_ptr;
RESIDUAL *rp;
/* Get pointer to RESIDUAL structure at residual_offset in the ipl_cb.
*/
iplcb = (struct ipl_cb *)vmker.iplcbptr;
idir_ptr = (struct ipl_directory *)(&iplcb->s0);
rp = (RESIDUAL *)((char *)iplcb + idir_ptr->residual_offset);
for ( i = 0, actual = rp->ActualNumDevices; i < actual; i++ )
{
if ((rp->Devices[i].DeviceId.BaseType == SystemPeripheral) &&
(rp->Devices[i].DeviceId.SubType == ProgrammableInterruptController))
{
switch ( rp->Devices[i].DeviceId.Interface )
{
case ISA_PIC:
case EISA_PIC:
assert( i8259 == -1 ); /* Only 1 allowed */
i8259 = i;
npics += 1;
break;
case MPIC:
assert( iMPIC == -1 ); /* Only 1 allowed */
iMPIC = i;
npics += 1;
break;
default:
assert(0); /* Unknown int. ctlr */
}
}
}
ASSERT(npics > 0 && npics <= 2);
intctl_pri = INT_CTL_NONE;
intctl_sec = INT_CTL_NONE;
if ( iMPIC != -1 )
{
intctl_pri = INT_CTL_MPIC;
query_parms_mpic( (struct _GenericAddr *)
&rp->DevicePnPHeap[rp->Devices[iMPIC].AllocatedOffset] );
}
if ( i8259 != -1 )
{
if ( intctl_pri == INT_CTL_NONE )
intctl_pri = INT_CTL_8259;
else
intctl_sec = INT_CTL_8259;
query_parms_8259( (struct _S8_Pack *)
&rp->DevicePnPHeap[rp->Devices[i8259].AllocatedOffset] );
}
return;
}
/*
* NAME: query_parms_mpic
*
* FUNCTION: This routine queries the residual data heap for the MPIC
* interrupt controller and extracts its base address. It also sets
* the global variable mpic_base, which is used in the global io_map
* structure passed to iomem_att when doing I/O to the MPIC.
* Only one processor on an MP system will ever run this code.
*
* NOTES:
* Passed a pointer to the first object in the MPIC's residual data heap,
* the routine scans for the GenericAddr packet which defines the MPIC
* itself. That detection is made because the length is 0x40000 bytes.
* All other packets are ignored up to the end packet.
*
* EXECUTION ENVIRONMENT:
*
* This service executes in the early part of system initialization,
* prior to fully establishing a process or interrupt handler
* execution environment.
*
* EXTERNAL PROCEDURES CALLED:
*/
void
query_parms_mpic( struct _GenericAddr *gen )
{
unsigned long i;
struct _GenericAddr *mpic = (struct _GenericAddr *)0;
while ( ( gen->Tag & 0xF8 ) != 0x78 ) /* End */
{
if ( gen->Tag == 0x84 ) /* GenAddr */
{
i = (gen->Length[3] << 24) + (gen->Length[2] << 16)
+ (gen->Length[1] << 8) + (gen->Length[0]);
if ( i == 0x40000 )
mpic = gen;
}
if ( gen->Tag & 0x80 ) /* Large */
i = ( gen->Count1 << 8 ) + gen->Count0 + 3;
else /* Small */
i = ( gen->Tag & 0x07 ) + 1;
gen = (struct _GenericAddr *)( (void *)gen + i );
}
assert( mpic != (struct _GenericAddr *)0 );
i = (mpic->Address[3] << 24) + (mpic->Address[2] << 16)
+ (mpic->Address[1] << 8) + (mpic->Address[0]);
switch( mpic->AddrType )
{
case ISAIOAddr: i += 0x80000000; /* PCI I/O base */
break;
case IOMemAddr: i += 0xC0000000; /* PCI Memory base */
break;
case SysMemAddr: break; /* Address OK */
default: assert(0);
}
mpic_base = i; /* Global mpic_base address */
return;
}
/*
* NAME: query_parms_8259
*
* FUNCTION: This routine is a stub. Eventually, it will scan the residual
* data heap for the 8259 interrupt controller and extract pertinent
* information.
* Only one processor on an MP system will ever run this code.
*
* EXECUTION ENVIRONMENT:
*
* This service executes in the early part of system initialization,
* prior to fully establishing a process or interrupt handler
* execution environment.
*
* EXTERNAL PROCEDURES CALLED:
*/
void
query_parms_8259( struct _S8_Pack *s8 )
{
return;
}
#endif /* _RSPC */
/*
* NAME: init_ios
*
* FUNCTION: This routine is called early in system initialization by
* main.c. It initializes the hardware for the I/O Subsystem.
* This routine determines the platform/machine type and calls the
* appropriate hardware specific routine.
*
* EXECUTION ENVIRONMENT:
*
* This service executes in the early part of system initialization,
* prior to fully establishing a process or interrupt handler
* execution environment.
*
* EXTERNAL PROCEDURES CALLED:
*/
void
ios_init()
{
#ifdef _POWER_PC
/*
* Register REALMEM_BID. This is used by base kernel
* to access memory mapped system resources on Power
* PC platforms.
*/
if (__power_pc()) {
struct businfo bi;
int rc;
bi.bid = REALMEM_BID;
bi.d_map_init = NULL;
bi.num_regions = 1;
bi.ioaddr[0] = 0;
rc = bus_register(&bi);
assert(rc == 0);
}
#endif /* _POWER_PC */
#ifdef _RS6K
if (__rs6k()) {
/*
* This is a PowerPC machine, so call PPC version
*/
ios_init_ppc();
}
#endif /* _RS6K */
#ifdef _POWER_RS
if (__power_rs()) {
/*
* This is a POWER machine, so call POWER version
*/
ios_init_pwr();
}
#endif /* _POWER_RS */
#ifdef _RSPC
if (__rspc()) {
/*
* This is a RSPC base Power PC
*/
ios_init_rspc();
}
#endif /* _RSPC */
}