#ident "kit/P4000B/sbdrtpx.c: Copyright (c) MIPS Technologies, Inc. All rights reserved."

/*
 * Copyright (c) 1996-2003 MIPS Technologies, Inc.
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above
 *      copyright
 *        notice, this list of conditions and the following disclaimer
 *        in the documentation and/or other materials provided with
 *        the distribution.
 *      * Neither the name of MIPS Technologies Inc. nor the names of its
 *        contributors may be used to endorse or promote products derived
 *        from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * sbdrtpx.c: RTPX board-specific support for P4000
 */


#include "pthread_impl.h"
#include <limits.h>
#include <mips/cpu.h>
#include <mips/xcpt.h>
#include "sbd.h"

static signed char	crToEvn[256];
static unsigned short	evnToSr[SBD_EVN_MAX];


unsigned int _sbd_tickspersec (void)
{
    /* timer ticks at half pipeline frequency */
    return _sbd_pipefreq () / 2;
}


unsigned int (_sbd_gettickval)(void)
{
  return mips_getcount();
}


void (_sbd_settickcmp)(unsigned int v)
{
  mips_setcompare(v);
}


int 
_sbd_intr_howmany ()
{
    return INTR_MAX + 1;
}


/* for ALGOR PMON */
#ifdef HANDLEDEBUG
/* handle high priority debug event used on CR_HINT4 */
unsigned _intrmask = SR_IMASK;
static int debugHandler (int,struct xcptcontext *);
#else
unsigned _intrmask = SR_IMASK & ~SR_HINT4;
#endif

/* used to poll ipl0 interupts 
 * returns 0 if event env is not polling
 * called at ipl1.
 */
int _sbd_evpoll(int evn)
{
  return (mips_getcr() & evnToSr[evn]);
}

/* enable ipl0 interupts which have been blocked
 * called at ipl1.
 */
void _sbd_evenable(int evn)
{
  _rtpx_mips_ipl0_sr |= evnToSr[evn];
}


/* change local icu mask when returning to user mode */
void _sbd_evleave (void)
{
}


/* called from scheduler (at ipl1) to return next pending event on
 * this cpu.
 */
int _sbd_evnext(int idle)
{
  int evn;

  /* take advantage of this call to poll available interupts 
   */
  mips_setsr(_RTPX_MIPS_IPL2_SR);
  
  do {
    unsigned pend = mips_getcr() & _rtpx_mips_ipl0_sr & SR_IMASK;
    
    evn = crToEvn[pend >> 8];

    if (evn == EV_DEBUG) {
#ifdef HANDLEDEBUG
	debugHandler(0,NULL);
	continue;
#else
	/* don't mask it - let monitor catch it when we exit */
	evn = 0;
	break;
#endif
    }

  } while (evn == 0 && idle);

  if (evn > 0)
      _rtpx_mips_ipl0_sr &= ~evnToSr[evn];

  mips_setsr(_RTPX_MIPS_IPL1_SR);
  return evn;
}


/* board-specific interrupt handler, return 1 if interrupt handled */
int _sbd_intrhandler (unsigned int pend, struct xcptcontext *xcp)
{
#ifdef HANDLEDEBUG
    if(pend & CR_HINT4) {
      debugHandler(xcpt,xcp);
      return 1;
    }
#endif
    return 0;
}


/* initialise event handling for this board */
void _sbd_evinit(void)
{
  int i;

  /* set up interrupt config
   */

  /* sonic on ip4 CR_HINT3 */
  *(volatile unsigned char *)PA_TO_KVA1(INT_ICG0) = ICG_SET;
  *(volatile unsigned char *)PA_TO_KVA1(INT_ICG1) = ICG_SET;
  
  /* msc on ip3 CR_HINT2 */
  *(volatile unsigned char *)PA_TO_KVA1(INT_ICG2) = ICG_SET;
  *(volatile unsigned char *)PA_TO_KVA1(INT_ICG3) = ICG_CLR;
  
  /* ioirq3 on ip5 CR_HINT3 (not available) 
   */
  *(volatile unsigned char *)PA_TO_KVA1(INT_IMR3) = IMR_DISABLE;
  
  /* ioirq2 on ip4 CR_HINT2 (not available) 
   */
  *(volatile unsigned char *)PA_TO_KVA1(INT_IMR2) = IMR_DISABLE;
  
  /* ioirq1 on ip3 CR_HINT1 */
  *(volatile unsigned char *)PA_TO_KVA1(INT_IMR1) = IMR_ENABLE;
  
  /* ioirq0 on ip2 CR_HINT0 */
  *(volatile unsigned char *)PA_TO_KVA1(INT_IMR0) = IMR_ENABLE;
  
  /* build interrupt pending to event number mapping table */
  for (i = 0; i < 256; i++) {
      unsigned int cr = i << 8;
      int evn;
      if (cr & CR_HINT4)
	  evn = EV_DEBUG;
      else if (cr & CR_HINT5)
	  evn = EV_CLOCK;
      else if (cr & CR_HINT3)
	  evn = EV_SONIC;
      else if (cr & CR_HINT2)
	  evn = EV_DUART;
      else if (cr & CR_HINT1)
	  evn = EV_IO1;
      else if (cr & CR_HINT0)
	  evn = EV_IO0;
      else if (cr & CR_SINT1)
	  evn = EV_SINT1;
      else if (cr & CR_SINT0)
	  evn = EV_SINT0;
      else
	  evn = 0;
      crToEvn[i] = evn;
  }

  evnToSr[EV_DEBUG]	= SR_HINT4;
  evnToSr[EV_SONIC]	= SR_HINT3;
  evnToSr[EV_DUART]	= SR_HINT2;
  evnToSr[EV_IO1]	= SR_HINT1;
  evnToSr[EV_IO0]	= SR_HINT0;
  evnToSr[EV_SINT1]	= SR_SINT1;
  evnToSr[EV_SINT0]	= SR_SINT0;

  /* set timer a long way into the future */
  mips_setcompare (mips_getcount () + INT_MAX);
}


#ifdef HANDLEDEBUG
/* NOTE xcp may be NULL ! */
static int debugHandler(int foo, struct xcptcontext *xcp)
{
    unsigned int irr0 = *(volatile unsigned int *)PA_TO_KVA1(INT_IRR0);
    (void) *(volatile unsigned int *)PA_TO_KVA1(INT_ACKPANIC);

    if (irr0 & IRR0_DEBUG) {
	/* software debounce debug button
	 * (hope no other panics arrive in the meantime)
	 */
	do {
	    int i;
	    (void) *(volatile unsigned int *) PA_TO_KVA1 (INT_ACKPANIC);
	    for (i = 100000; i > 0; i--)
		irr0 = *(volatile unsigned int *) PA_TO_KVA1 (INT_IRR0);
	} while (irr0 & IRR0_DEBUG);
	rtpx_log (LOG_PANIC, "debug button");
    }
    else if (irr0 & IRR0_ACFAIL) {
	rtpx_log (LOG_PANIC, "power fail");
    }
    else if (irr0 & IRR0_BUS) {
	rtpx_log (LOG_PANIC, "bus error");
    }

    abort();
      
    return 0;
}
#endif
