/*
	 Copyright 2001 (c) by Authors,
	 Swiss Federal Institute of Technology,
	 Computer Engineering and Networks Laboratory.
 
	 TOPSY -- A Teachable Operating System.
	 Implementation of a tiny and simple
	 micro kernel for teaching purposes.
 
	 For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
	 This software is provided under the terms of the GNU General Public 
	 Licence. A full copy of the GNU GPL is provided in the file COPYING 
	 found in the development root of Topsy.
 */
/*
 
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/IO/sa1100/Drivers/Attic/IXPuEng.c,v $
	Affiliation:           ETH Zuerich, TIK
	Version:               $Revision: 2936 $
	Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $  by: $Author: baderma $
 
*/

// this code is based upon parts of vera from Princeton

//
// Utilities for the Intel IXP1200
// Princeton University
// Scott Karlin and Tammo Spalink
//
// Copyright Princeton University Board of Trustees
// All rights reserved
//
//  $Id: IXPuEng.c 2936 2007-03-21 14:23:42Z baderma $
//

#include "IXPuEng.h"
#include "types.h"
#include "Support.h"
#include "IXP_Defines.h"
#include "IXPMemAccess.h"
#include "IXPUtil.h"
#include "Syscall.h"
#include "kalloc_api.h"


#define THREADS_PER_UENGINE     4

#define DEBUG_UENG_PREPARE_CTX  0
#define DEBUG_UENG_ENABLE_CTX   1
//
// ---------- Private Hardware Access Definitions ----------
//

// Microengine Control Store Registers (base addresses)
static uint32 uEng_CSR_Base[] = {
                                  0xB0000000,
                                  0xB0000800,
                                  0xB0001000,
                                  0xB0001800,
                                  0xB0002000,
                                  0xB0002800
                                };

// Microengine Control Store Registers (register offsets)
typedef enum {
  USTORE_ADDRESS = 0x0,
  USTORE_DATA    = 0x4,
  ALU_OUTPUT     = 0x08,
  ACTIVE_CTX_STS = 0x10,
  CTX_ARB_CTL    = 0x24,
  CTX_ENABLES    = 0x28,
  CC_ENABLE      = 0x2C,
} csrOffsets;

static uint32 CTX_STS[] = {
                            0x34, 0x38, 0x3C, 0x40
                          };

static uint32 CTX_SIG_EVENTS[] = {
                                   0x44, 0x48, 0x4C, 0x50
                                 };

static uint32 CTX_WAKEUP_EVENTS[] = {
                                      0x54, 0x58, 0x5C, 0x60
                                    };

typedef enum {
  USTORE_ADDRESS_ECS = 1 << 10          // Allow uStore to be acessed
} USTORE_ADDRESS_fields;

typedef enum {
  CTX_WAKEUP_EVENTS_VS = 1 << 0,        // Wakes at next opportunity
  CTX_WAKEUP_EVENTS_TD = 1 << 4         // Waiting on INTER_THREAD signal
} CTX_WAKEUP_EVENTS_fields;


/*****************************************************************************/
/*                                                                           */
/* Function Name: csr                                                        */
/* Parameters:                                                               */
/*      uint8              uEng    The microengine to query.                 */
/*      csrOffsets      address The offset to find.                          */
/* Returns: uint32 the StrongARM address of the CSR.                         */
/* Effects: <none>                                                           */
/*                                                                           */
/*****************************************************************************/

static inline uint32
csr(uint8 uEng, csrOffsets address)
{
  return address + uEng_CSR_Base[uEng];
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_Init                                                  */
/* Parameters: uint8   uEng        The microengine to initialize.            */
/* Returns: <none>                                                           */
/* Effects: The specified uEng will be removed from the reset state, and all */
/*          its state cleared.                                               */
/*                                                                           */
/*****************************************************************************/

void
uEng_Init(uint8 uEng)
{
  int i = 0;

  // Take the uEng out of reset
  uEng_Enable(uEng);
  led(0x60);

  // Disable all threads
  memWrite32(csr(uEng, CTX_ENABLES), 0x0);
  led(0x61);

  // Clear each context's state
  for (i = 0; i < THREADS_PER_UENGINE; i++)
  {
    memWrite32(csr(uEng, CTX_STS[i]), 0x0);
    memWrite32(csr(uEng, CTX_SIG_EVENTS[i]), 0x0);
    memWrite32(csr(uEng, CTX_WAKEUP_EVENTS[i]), 0x0);
  }
  led(0x62);

  // Clear the uStore
  for (i = 0; i < 1024; i++)
  {
    uint32 addr;

    // Set the address enable (ECS) bit in the address written to
    // USTORE_ADDRESS to make USTORE_DATA read/writable
    addr = i | USTORE_ADDRESS_ECS;

    memWrite32(csr(uEng, USTORE_ADDRESS), addr); // where to write
    memWrite32(csr(uEng, USTORE_DATA), 0x0);     // what to write
  }
  led(0x63);

  memWrite32(csr(uEng, USTORE_ADDRESS), 0x0);      // Clear ECS bit
  led(0x64);
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_Enable                                                */
/* Parameters: uint8   uEng    The microengine to enable.                    */
/* Returns: <none>                                                           */
/* Effects: Brings the microengine out of reset mode.                        */
/*                                                                           */
/*****************************************************************************/

void
uEng_Enable(uint8 uEng)
{
  uint32 IXP1200_RESET_value;

  IXP1200_RESET_value = memRead32(IXP1200_RESET);
  IXP1200_RESET_value &= putBits(0, uEng, 1);
  memWrite32(IXP1200_RESET, IXP1200_RESET_value);
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_ResetAll                                              */
/* Parameters: <none>                                                        */
/* Returns: <none>                                                           */
/* Effects: Resets all the microengines.                                     */
/*                                                                           */
/*****************************************************************************/

void
uEng_ResetAll(void)
{
  memWrite32(IXP1200_RESET, 0x3F);
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_PrepareCtx                                            */
/* Parameters:                                                               */
/*      uint8      uEng    Which microengine                                 */
/*      int     ctx     Which thread                                         */
/* Returns: <none>                                                           */
/* Effects: Prepares the thread ctx of uEng to run.                          */
/*                                                                           */
/*****************************************************************************/

void
uEng_PrepareCtx(uint8 uEng, int ctx)
{
  uint32 addr;

  /* To facilitate the stdlib.uc startup. Each thread starts on its own
   * address. */
  addr = ((uEng * THREADS_PER_UENGINE) + ctx) * 2;

#if DEBUG_UENG_PREPARE_CTX

  printf("%s(uEng = %d, ctx = %d): addr = 0x%08x\n", __FUNCTION__,
         uEng, ctx, addr);
#endif

  memWrite32(csr(uEng, CTX_STS[ctx]), putBits(addr, 0, 10));
  memWrite32(csr(uEng, CTX_WAKEUP_EVENTS[ctx]), CTX_WAKEUP_EVENTS_VS);

#if DEBUG_UENG_PREPARE_CTX

  uEng_PrintCSRs(uEng);
#endif
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_EnableCtx                                             */
/* Parameters:                                                               */
/*      uint8      uEng    Which microengine                                 */
/*      int     ctx     Which thread                                         */
/* Returns: <none>                                                           */
/* Effects: Enables thread ctx of uEng to run at the next opportunity.       */
/*                                                                           */
/*****************************************************************************/

void
uEng_EnableCtx(uint8 uEng, int ctx)
{
  uint32 CTX_ENABLES_value;

  /* Set this context as the active context. */
  memWrite32(csr(uEng, ACTIVE_CTX_STS), putBits(ctx, 0, 2));

  /* Enable the thread for the future. */
  CTX_ENABLES_value = getBits(memRead32(csr(uEng, CTX_ENABLES)), 8, 4);
  CTX_ENABLES_value |= 1 << ctx;
  memWrite32(csr(uEng, CTX_ENABLES), putBits(CTX_ENABLES_value, 8, 4));
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_Stop                                                  */
/* Parameters:                                                               */
/*      int     uEng            The microengine to stop                      */
/*      uint32     threaddata[]    An array to hold the status of all the    */
/*                              threads.                                     */
/* Returns: <none>                                                           */
/* Effects: Stops all of the uEng's threads. Stores their state in           */
/*          threaddata, so they can be restarted.                            */
/*****************************************************************************/

uint32 *
uEng_Stop(uint8 uEng)
{
  uint32* retval;
  int i = 0;

  // XXX maybe I should clear the memory which i return?
  retval = km_alloc(4 * sizeof(*retval), KM_MEMORY);

  memWrite32(csr(uEng, CTX_ENABLES), putBits(0x0, 8, 4));

  while (getBits(memRead32(csr(uEng, ACTIVE_CTX_STS)), 2, 1))
  {
    // Wait for halt.
  }

  for (i = 0; i < THREADS_PER_UENGINE; i++)
  {
    retval[i] = memRead32(csr(uEng, CTX_STS[i]));
  }

  return retval;
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_Restart                                               */
/* Parameters:                                                               */
/*      int     uEng            Microengine whose threads to restart.        */
/*      uint32 *   threaddata      The data associated with the uEngines.       */
/* Returns: <none>                                                           */
/* Effects:                                                                  */
/*                                                                           */
/*****************************************************************************/

void
uEng_Restart(uint8 uEng, uint32 *threaddata)
{
  int i = 0;

  if (threaddata != NULL)
  {
    for (i = 0; i < THREADS_PER_UENGINE; i++)
    {
      memWrite32(csr(uEng, CTX_STS[i]), threaddata[i]);
    }
  }
  memWrite32(csr(uEng, CTX_ENABLES), putBits(0xF, 8, 4));
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_LoadAtAddress                                         */
/* Parameters:                                                               */
/*      int     uEng    The microengine to modify                            */
/*      uint32 *   code    The array with the instructions                   */
/*      int     length  The length of the array                              */
/*      uint32     addr    The starting address of the the code block        */
/* Returns: <none>                                                           */
/* Effects: Loads the first length instructions of code to the ISTORE of     */
/*          microengine uEng beginning at addr.                              */
/*****************************************************************************/

uint32
uEng_LoadAtAddress(uint8 uEng, uint32 *code, int length, uint32 addr)
{
  uint32 retval = addr + length;
  int i = 0;


  led(0x40);
  addr &= 0x000003FF;         // There are only 10 bits of valid addresses.

  led(0x41);
  /* Set the address enable (ECS) bit in the address written to
  	 USTORE_ADDRESS to make USTORE_DATA read/writable. */
  addr |= USTORE_ADDRESS_ECS;

  led(0x42);
  /* Copy the uCode into the I-Store. */
  for (i = 0; i < length; i++)
  {
    memWrite32(csr(uEng, USTORE_ADDRESS), addr + i); // Where to write
    memWrite32(csr(uEng, USTORE_DATA), code[i]);     // What to write
  }
  led(0x43);

  /* Turn off writing. */
  memWrite32(csr(uEng, USTORE_ADDRESS), addr & ~USTORE_ADDRESS_ECS);
  led(0x44);

  return retval;
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_ThreadIsWaiting                                       */
/* Parameters:                                                               */
/*      uint8      uEng    Which microengine                                    */
/*      int     ctx     Which thread                                         */
/* Returns: TRUE if thread is waiting on the INTER_THREAD condition.         */
/* Effects: <none>                                                           */
/*                                                                           */
/*****************************************************************************/

Boolean
uEng_ThreadIsWaiting(uint8 uEng, int ctx)
{
  uint32 CTX_WAKEUP_EVENTS_value;

  CTX_WAKEUP_EVENTS_value = memRead32(csr(uEng, CTX_WAKEUP_EVENTS[ctx]));
  return CTX_WAKEUP_EVENTS_value & CTX_WAKEUP_EVENTS_TD;
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_PrintCSRs                                             */
/* Parameters: uint8   uEng    The microengine to query                      */
/* Returns: <none>                                                           */
/* Effects: Prints out CSRs related to uEng to standard output               */
/*                                                                           */
/*****************************************************************************/

void
uEng_PrintCSRs(
  uint8 uEng)                    // Which microengine
{
  uint32 ACTIVE_CTX_STS_value;
  uint32 CTX_ENABLES_value;
  int i = 0;

  ACTIVE_CTX_STS_value = memRead32(csr(uEng, ACTIVE_CTX_STS));
  CTX_ENABLES_value = memRead32(csr(uEng, CTX_ENABLES));

  printf("\nMicroEngine %d\n  %-13s : %x\n  %-13s : %d\n",
         uEng,
         "active",         getBits(ACTIVE_CTX_STS_value, 2, 1),
         "current thread", getBits(ACTIVE_CTX_STS_value, 0, 2));

  for (i = 0; i < THREADS_PER_UENGINE; i++)
  {
    uint32 CTX_STS_value;

    CTX_STS_value = memRead32(csr(uEng, CTX_STS[i]));


    printf("  thread %d\n    %s : %x\n    %s : %x\n    %s : %03x"
           "    %s : %x\n    %s : %x\n",
           i,
           "enabled",    getBits(CTX_ENABLES_value, 8 + i, 1),
           "ready",      getBits(CTX_STS_value, 10, 1),
           "PC",         getBits(CTX_STS_value, 0, 10),
           "signals",    getBits(memRead32(csr(uEng,
                                               CTX_SIG_EVENTS[i])),
                                 0, 9),
           "wakeup",     getBits(memRead32(csr(uEng,
                                               CTX_WAKEUP_EVENTS[i])),
                                 0, 10));
  }
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uEng_PrintStore                                            */
/* Parameters:                                                               */
/*      uint8      uEng    Which microengine                                    */
/*      int     lines   How many lines of data to print.                     */
/* Returns: <none>                                                           */
/* Effects: Prints the contents of the I-Store to standard output. 8 values  */
/*          are printed per line.                                            */
/*****************************************************************************/

void
uEng_PrintStore(uint8 uEng, int lines)
{
  int i = 0;
  int j = 0;

  uint32 iaddr = memRead32(csr(uEng, USTORE_ADDRESS));   // Store the address

  printf("\nMicroEngine Store %d\n", uEng);

  for (i = 0; i < lines; i++)
  {

    for (j = 0; j < 8; j++)
    {
      uint32 addr;

      addr = ((i * 8) + j) | USTORE_ADDRESS_ECS;

      memWrite32(csr(uEng, USTORE_ADDRESS), addr);
      printf("%08x\t",  memRead32(csr(uEng, USTORE_DATA)));
    }

    printf("\n");
  }

  /* Restore the address. */
  memWrite32(csr(uEng, USTORE_ADDRESS), iaddr & ~USTORE_ADDRESS_ECS);
}

