/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: slic.c 1.10 90/04/20 $"

/*
 * slic.c
 *	Slic functions.
 *
 * wrslave/rdslave/wrSubslave/rdSubslave are mutexed by a global lock.
 * All other routines assume caller arranged mutex of SLIC usage (splhi()
 * or disable processor interrupts) -- SLIC registers are not saved or
 * restored across interrupts.
 *
 * Conditionals:
 *	-DCHECKSLIC	check slic results (parity, etc) for sanity
 *
 * Gates are handled in gate.c; interrupt acceptence/etc in locore.s
 */

/* $Log:	slic.c,v $
 */

#include "sys/types.h"
#include "sys/mutex.h"
#include "sys/arch.h"

#include "sys/cfg.h"
#include "sys/slic.h"
#include "sys/slicreg.h"

#include "sys/gate.h"
#include "sys/mc_vmparam.h"
#include "sys/intctl.h"
#include "sys/hwparam.h"

#ifdef	i386
/*
 * In order to use the performance counters on the CMCs and BICs, it
 * is necessary to access the SLICs of other processors. Since multiple
 * SLIC commands are required for wrslave/rdslave/wrSubslave/rdSubslave,
 * we need to ensure their atomicity by mutexing on the same lock.
 *
 * Other uses of slic slave addressing is mutually excluded at higher levels;
 * upper level code only online/offline's one processor at a time, memory
 * error polling guaranteed single-thread, etc.
 */
#endif	/* i386 */

/* Rely on implicit initialization of the lock */

static	lock_t	slic_mutex;

/*
 * wrslave()
 *	Write to a slave port.
 *
 */

wrslave(destination, reg, data)
	unsigned char destination, reg, data;
{
	spl_t s = p_lock(&slic_mutex, SPLHI);

	wrAddr(destination, reg);
	wrData(destination, data);

	v_lock(&slic_mutex, s);
}

/*
 * lwrslave()
 *	Write to a slave port. Don't acquire slic_mutex.
 *
 * On panics, engines pausing themselves call lwrslave to hold
 * themselves without acquiring slic_mutex. Otherwise the lock would
 * never get released (wrslave doesn't return) and all other engines
 * would block behind the lock.
 */

lwrslave(destination, reg, data)
	unsigned char destination, reg, data;
{
	wrAddr(destination, reg);
	wrData(destination, data);
}

/*
 * rdslave()
 *	Read a slave port.
 *
 */

rdslave(destination, reg)
	unsigned char destination, reg;
{
	int val;
	spl_t s = p_lock(&slic_mutex, SPLHI);

	wrAddr(destination, reg);
	val = rdData(destination);

	v_lock(&slic_mutex, s);
	return(val);
}

/*
 * lrdslave()
 *	Read a slave port. Don't acquire slic_mutex.
 *
 */

lrdslave(destination, reg)
	unsigned char destination, reg;
{
	wrAddr(destination, reg);
	return(rdData(destination));
}

/*
 * wrSubslave()
 *	Write a register that responds to SLIC slave sub-register addressing.
 *
 * For compatibility with diagnostic usage, if slave==0 don't do the wrAddr().
 */

wrSubslave(slic, slave, subreg, val)
	unchar slic, slave, subreg, val;
{
	spl_t s = p_lock(&slic_mutex, SPLHI);

	if (slave != 0)
		wrAddr(slic, slave);
	wrData(slic, subreg);
	wrData(slic, val);

	v_lock(&slic_mutex, s);
}

/*
 * rdSubslave()
 *	Read a register that responds to SLIC slave sub-register addressing.
 *
 * For compatibility with diagnostic usage, if slave==0 don't do the wrAddr().
 */

rdSubslave(slic, slave, subreg)
	unchar slic, slave, subreg;
{
	int	val;
	spl_t s = p_lock(&slic_mutex, SPLHI);

	if (slave != 0)
		wrAddr(slic, slave);
	wrData(slic, subreg);
	val = rdData(slic);

	v_lock(&slic_mutex, s);
	return (val);
}

/*
 * wrAddr()
 *	Write address to slave.
 *
 */

wrAddr(destination, address)
	unsigned char destination, address;
{
	register volatile struct cpuslic *sl = (struct cpuslic *)va_slic;

	sl->sl_dest = destination;
	sl->sl_smessage = address;
	sl->sl_cmd_stat = SL_WRADDR;
	while (sl->sl_cmd_stat & SL_BUSY)
		continue;
#ifdef	CHECKSLIC
	check_slic("wrAddr");
#endif	/* CHECKSLIC  */
}

/*
 * wrData()
 *	Write data to previously addressed slave register.
 *
 * Internal interface.
 *
 * Caller assures mutex of SLIC usage (splhi() or holding gate is sufficient).
 */

static
wrData(destination, data)
	unsigned char destination, data;
{
	register volatile struct cpuslic *sl = (struct cpuslic *)va_slic;

	sl->sl_dest = destination;
	sl->sl_smessage = data;
	sl->sl_cmd_stat = SL_WRDATA;
	while (sl->sl_cmd_stat & SL_BUSY)
		continue;
#ifdef	CHECKSLIC
	check_slic("wrData");
#endif	/* CHECKSLIC  */
}

/*
 * rdData()
 *	Read data from previously addressed slave register.
 *
 * Internal interface.
 *
 * Caller assures mutex of SLIC usage (splhi() or holding gate is sufficient).
 */

static
rdData(destination)
	unsigned char destination;
{
	register volatile struct cpuslic *sl = (struct cpuslic *)va_slic;

	sl->sl_dest = destination;
	sl->sl_cmd_stat = SL_RDDATA;
	while (sl->sl_cmd_stat & SL_BUSY)
		continue;
#ifdef	CHECKSLIC
	check_slic("rdData");
#endif	/* CHECKSLIC  */
	return(sl->sl_sdr & 0xff);
}

/*
 * sendsoft()
 *	Post SW interrupt to somebody.
 *
 * Used to post resched "nudge", pff calc, softclock.
 *
 * Caller assures mutex of SLIC usage (splhi() or holding gate is sufficient).
 */

sendsoft(dest, bitmask)
	unsigned char dest;
	unsigned char bitmask;
{
	register volatile struct cpuslic *sl;

#ifdef	i386
	extern	unchar bin0mask;

	/*
	 * Fake software interrupts on PC/3AT
	 * by setting bits in memory that are
	 * checked on interrupt exit by locore.
	 */

	if (arch_type == SYSTYP_S1) {
		bin0mask |= bitmask;
		return;
	}
#endif	/* i386 */

	sl = (struct cpuslic *)va_slic;
	sl->sl_dest = dest;
	sl->sl_smessage = bitmask;

	sl->sl_cmd_stat = SL_MINTR | 0;		/* 0 ==> bin 0 */
	while (sl->sl_cmd_stat & SL_BUSY)
		continue;

#ifdef	CHECKSLIC
	check_slic("sendsoft");
#endif	/* CHECKSLIC  */
}

/*
 * nmIntr()
 *	Post NMI interrupt to somebody.
 *
 * Used to post send NMI to a processor to have it shut down.
 *
 * Caller assures mutex of SLIC usage (splhi() or holding gate is sufficient).
 */

nmIntr(dest, message)
	unsigned char dest;
	unsigned char message;
{
	register volatile struct cpuslic *sl = (struct cpuslic *)va_slic;

	sl->sl_dest = dest;
	sl->sl_smessage = message;

	do {
		sl->sl_cmd_stat = SL_NMINTR;
		while (sl->sl_cmd_stat & SL_BUSY)
			continue;
	} while ((sl->sl_cmd_stat & SL_OK) == 0);
#ifdef	CHECKSLIC
	check_slic("nmIntr");
#endif	/* CHECKSLIC  */
}

/*
 * mIntr()
 *	Post HW interrupt to somebody.
 *
 * Used to send commands to various peripheral controllers on the Sequent bus.
 *
 * Caller assures mutex of SLIC usage (splhi() or holding gate is sufficient).
 *
 * Implementation acquires gate before sending message, to avoid SLIC bus
 * saturation.
 */

static	gate_t	mIntr_mutex = G_UNLOCKED;

mIntr(dest, bin, data)
	unsigned char dest;
	unsigned char bin;
	unsigned char data;
{
	register volatile struct cpuslic *sl = (struct cpuslic *)va_slic;
	register volatile unsigned stat;

	/*
	 * Get mIntr "gate" -- serializes mIntr requests, avoiding
	 * SLIC bus saturation.
	 *
	 * Semantics insist caller is at splhi(); thus no need to re-do this.
	 */

	p_gate(&mIntr_mutex);			/* single-thread mIntr's */

	/*
	 * Send message.  Spin forever until sent.
	 */

	sl->sl_dest = dest;
	sl->sl_smessage = data;

	do {
		sl->sl_cmd_stat = SL_MINTR | bin;
		while ((stat = sl->sl_cmd_stat) & SL_BUSY)
			continue;
	} while ((stat & SL_OK) == 0);

#ifdef	CHECKSLIC
	check_slic("mIntr");
#endif	/* CHECKSLIC  */

	/*
	 * Release mIntr gate.
	 */

	v_gate(&mIntr_mutex);
}

/*
 * setgm()
 *	Set group mask in destination slic.
 *
 * Caller assures mutex of SLIC usage (splhi() or holding gate is sufficient).
 */

setgm(dest, mask)
	unsigned char dest;
	unsigned char mask;
{
	register volatile struct cpuslic *sl = va_slic;

	sl->sl_dest = dest;			/* set this guy's... */
	sl->sl_smessage = mask;			/* group mask to "mask" */
	sl->sl_cmd_stat = SL_SETGM;		/* set the group-mask */
	while (sl->sl_cmd_stat & SL_BUSY)	/* and wait for cmd done */
		continue;

#ifdef	CHECKSLIC
	check_slic("setgm");
#endif	/* CHECKSLIC  */
}

#ifdef	CHECKSLIC
/* 
 * check_slic()
 *	Check status from SLIC for parity, exists, and ok bits.
 */

check_slic(procname)
	char	*procname;
{
	register unsigned stat;

	stat = va_slic->sl_cmd_stat;

	if ((stat & SL_PARITY) == 0) {
		cmn_err(CE_CONT, "%s: slic parity error\n", procname);
		cmn_err(CE_PANIC, "slic");
		/*
		 *+ A parity error was detected while doing a slic
		 *+ operation.  This indicates a probable hardware
		 *+ problem.
		 */
		/*NOTREACHED*/
	}
	if ((stat & SL_EXISTS) == 0) {
		cmn_err(CE_CONT, "%s: slic(s) don't exist\n", procname);
		cmn_err(CE_PANIC, "slic");
		/*
		 *+ An erro was encountered while doing a slic
		 *+ operation.  This indicates a probable hardware
		 *+ problem.
		 */
		/*NOTREACHED*/
	}
	if ((stat & SL_OK) == 0) {
		cmn_err(CE_CONT, "%s: slic not ok\n", procname);
		cmn_err(CE_PANIC, "slic");
		/*
		 *+ An erro was encountered while doing a slic
		 *+ operation.  This indicates a probable hardware
		 *+ problem.
		 */
		/*NOTREACHED*/
	}
}
#endif	/* CHECKSLIC  */
