1
0
Files
irix-657m-src/stand/arcs/IO6prom/ip27lib.c
2022-09-29 17:59:04 +03:00

399 lines
8.2 KiB
C

#ifdef SABLE
/***********************************************************************\
* File: hub.c *
* *
* This file contains the IP27 PROM Utility Library routines for *
* manipulating miscellaneous features of the hub. *
* *
\***********************************************************************/
#ident "$Revision: 1.13 $"
#include <sys/types.h>
#include <sys/SN/agent.h>
#include <sys/SN/SN0/IP27.h>
#include <sys/SN/klconfig.h>
#include "sys/nic.h"
#include "ip27prom.h"
#include "libsk.h"
ulong get_BSR(void);
void set_BSR(ulong);
#define hub_cpu_get() \
((int) (get_BSR() & BSR_CPUNUM))
#define BSR_GMASTER 0x00008000 /* am global master */
int ip27prom_epilogue(void) ;
void putchar(char) ;
extern int cpuid(void) ;
/*
* The hub NIC is read once and kept cached since it's expensive to read.
*/
static nic_t hub_nic;
#define ARB_ALIVE_A PI_RT_COMPARE_A
#define ARB_ALIVE_B PI_RT_COMPARE_B
#ifdef SABLE
#define LOCAL_ARB_TIMEOUT 50
#else
#define LOCAL_ARB_TIMEOUT 5000000
#endif
#define SINGLE() ((get_BSR() & BSR_SINGLE ) != 0)
#define MASTER() ((get_BSR() & BSR_LMASTER) != 0)
/*
* During boot, several scratch registers are needed for mutual exclusion
* between the local CPUs. They must be read/write, cleared to zero on
* reset, not cause funny things to happen, and not get twiddled by the
* chip, and not get used during initialization. It's very difficult to
* find registers fitting the bill. We'll use the MD_PERF_CNTx registers,
* which are not zero on reset, but are zeroed at the beginning of the
* PROM before the local arbitration.
*/
#define CPU_A_WANT MD_PERF_CNT0
#define CPU_B_WANT MD_PERF_CNT1
#define CPU_B_FAVOR MD_PERF_CNT2
#define BARRIER_A MD_PERF_CNT3
#define BARRIER_B MD_PERF_CNT4
#define IO6_HUB_LAUNCH MD_PERF_CNT5
#define TWO_CPUS() ((get_BSR() & BSR_SINGLE) == 0)
/*
* WASTE_TIME is the delay to avoid swamping the hub in polling loops.
* In practice, both writing and reading a hub register appears to take
* from 80 to 200 cycles!
*/
#define WASTE_TIME 100
void ip27_delay(int) ;
#define CTRL(x) ((x) & 0x1f)
#ifdef SABLE
#define DELAY_COUNTER 100
#else
#define DELAY_COUNTER 0x30000
#endif
/*
* hub_barrier
*
* Closely synchronizes the CPUs on the local node at a barrier point.
* The algorithm follows. The BARRIER_A and BARRIER_B registers
* are used for synchronization variables A and B. They are both assumed
* to be 0 before using barriers. Does nothing if only one CPU is active.
*
* A = 1; B = 1;
* while (B == 0); while (A == 0);
* B = 0; A = 0;
* while (A == 1); while (B == 1);
*/
void
hub_barrier(void)
{
/*
hub_led_set(PLED_BARRIER);
*/
if (TWO_CPUS()) {
if (hub_cpu_get() == 0) {
SD(LOCAL_HUB(BARRIER_A), 1);
while (LD(LOCAL_HUB(BARRIER_B)) == 0)
ip27_delay(WASTE_TIME);
SD(LOCAL_HUB(BARRIER_B), 0);
while (LD(LOCAL_HUB(BARRIER_A)) != 0)
ip27_delay(WASTE_TIME);
} else {
SD(LOCAL_HUB(BARRIER_B), 1);
while (LD(LOCAL_HUB(BARRIER_A)) == 0)
ip27_delay(WASTE_TIME);
SD(LOCAL_HUB(BARRIER_A), 0);
while (LD(LOCAL_HUB(BARRIER_B)) != 0)
ip27_delay(WASTE_TIME);
}
}
/*
hub_led_set(PLED_BARRIEROK);
*/
}
/*
* hub_lock
*
* Allows CPU A and CPU B to mutually exclude the hub from one another by
* obtaining a blocking lock. Does nothing if only one CPU is active.
* Implements Peterson's Algorithm for two-process exclusion. Utilizes
* three hub registers as communication scratch registers.
*/
void
hub_lock(void)
{
if (TWO_CPUS()) {
if (hub_cpu_get() == 0) {
SD(LOCAL_HUB(CPU_A_WANT ), 1);
SD(LOCAL_HUB(CPU_B_FAVOR), 1);
while (LD(LOCAL_HUB(CPU_B_WANT)) && LD(LOCAL_HUB(CPU_B_FAVOR)))
ip27_delay(WASTE_TIME);
} else {
SD(LOCAL_HUB(CPU_B_WANT ), 1);
SD(LOCAL_HUB(CPU_B_FAVOR), 0);
while (LD(LOCAL_HUB(CPU_A_WANT)) && ! LD(LOCAL_HUB(CPU_B_FAVOR)))
ip27_delay(WASTE_TIME);
}
}
}
/*
* hub_unlock
*
* Counterpart to hub_lock
*/
void
hub_unlock(void)
{
if (TWO_CPUS()) {
if (hub_cpu_get() == 0)
SD(LOCAL_HUB(CPU_A_WANT), 0);
else
SD(LOCAL_HUB(CPU_B_WANT), 0);
}
}
/*
* hub_nic_get
*
* XXX - CLOCK_PHASE_BITS should depend on MHz
*/
#define CLOCK_PHASE_BITS (0x31UL << 27 | 0x31UL << 20)
/*ARGSUSED*/
static uint
read_nic_ulan(nic_data_t data)
{
return (uint) LD(LOCAL_HUB(MD_MLAN_CTL)) & 0xfffff;
}
/*ARGSUSED*/
static void
write_nic_ulan(nic_data_t data, uint value)
{
SD(LOCAL_HUB(MD_MLAN_CTL),
CLOCK_PHASE_BITS | (long) value);
}
int
hub_nic_get(nic_t *nic)
{
nic_state_t ns;
if (hub_nic == 0) {
hub_lock();
nic_setup(&ns,
read_nic_ulan,
write_nic_ulan,
(nic_data_t) 0);
hub_nic = 0; /* Clear two MS bytes */
if (nic_next(&ns, (char *) &hub_nic + 2, (char *) 0, (char *) 0) < 0) {
hub_unlock();
return -1;
}
hub_unlock();
}
*nic = hub_nic;
return 0;
}
/*
* arb_local_master
*
* BSR_LMASTER will be set in the BSR for only one of the CPUs.
* If only one CPU is present or responding, then BSR_SINGLE will
* also be set in that CPU's BSR.
*/
void arb_local_master(void)
{
int cpu_num;
ulong bsr;
bsr = get_BSR() & ~(BSR_LMASTER | BSR_SINGLE);
cpu_num = hub_cpu_get();
/*
* If only one CPU is present, it becomes the master.
*/
if (! LD(LOCAL_HUB(PI_CPU_PRESENT_A)) ||
! LD(LOCAL_HUB(PI_CPU_PRESENT_B))) {
bsr |= (BSR_LMASTER | BSR_SINGLE);
} else {
int i, my_alive, their_alive;
/*
* Each CPU sets its own alive indicator.
*/
if (cpu_num == 0) {
my_alive = ARB_ALIVE_A;
their_alive = ARB_ALIVE_B;
} else {
my_alive = ARB_ALIVE_B;
their_alive = ARB_ALIVE_A;
}
SD(LOCAL_HUB(my_alive), 0xbeef);
/*
* If our own alive indicator has not turned on, flash LEDs and
* allow the other CPU to time out and become the master.
if (LD(LOCAL_HUB(my_alive)) != 0xbeef)
hub_led_die(FLED_HUBLOCAL);
*/
/*
* Wait in a timeout loop for the other processor's indicator.
* We would like the timeout to be around 0.25 sec.
*/
for (i = 0; i < LOCAL_ARB_TIMEOUT; i++)
if (LD(LOCAL_HUB(their_alive)) == 0xbeef)
break;
if (i == LOCAL_ARB_TIMEOUT) {
/*
* Timed out waiting for other CPU. Become single master.
*/
bsr |= (BSR_LMASTER | BSR_SINGLE);
} else {
/*
* Both CPUs okay; CPU A becomes master.
*/
if (cpu_num == 0)
bsr |= BSR_LMASTER;
}
SD(LOCAL_HUB(my_alive), 0); /* Clean up */
}
set_BSR(bsr);
#if 0
/* IN sable IO6prom I have not yet set up stacks for each cpu.
So, assume that there are 2 cpus and make cpu a the master. */
if (! LD(LOCAL_HUB(PI_CPU_PRESENT_A)) ||
! LD(LOCAL_HUB(PI_CPU_PRESENT_B)))
set_BSR(get_BSR() | (BSR_LMASTER | BSR_SINGLE));
else {
if (hub_cpu_get() == 0)
set_BSR((get_BSR() & ~(BSR_LMASTER | BSR_SINGLE)) | BSR_LMASTER) ;
else
set_BSR(get_BSR() & ~(BSR_LMASTER | BSR_SINGLE)) ;
}
/*
* If only one CPU is present, it becomes the master.
*/
#endif
}
#if 0
/*
* Return the cpuid of the Global Master CPU.
* Info obtained from ip27prom. XXX from where?? IP27PROM_CFG.
*/
int
global_master(void)
{
if (get_BSR() & BSR_GMASTER)
return(nasid()) ;
else
return -1 ; /* XXX - make sure this is not used as an index */
}
#endif
int
ip27prom_simulate(void)
{
ulong bsr ;
/*
* Arbitrate the boot master CPU. After this, we can use hub locks,
* which are required before local resources shared between CPU A
* and CPU B (RTC, UARTs, IIC, etc.) can be used.
*/
arb_local_master();
hub_barrier(); /* Both CPUs are in sync */
if (MASTER())
putchar('M') ;
hub_barrier() ;
if (cpuid() == 0) { /* Make cpu 0 the global master */
bsr = get_BSR() ;
bsr |= BSR_GMASTER ;
set_BSR(bsr) ;
/* XXX also need to set MD_PERF_CNT3 */
}
return 1 ;
}
int
ip27prom_epilogue(void)
{
/* The code upto this point is to bring the IO6prom environment
to the 'end of IP27prom' environment. What follows is the
actual epilogue code. */
if (MASTER()) {
SD(LOCAL_HUB(IO6_HUB_LAUNCH), 0);
}
hub_barrier() ;
return 1 ;
}
#endif /* SABLE */