summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c
blob: 4f45af45b888909201df687a38ebf14151ea3585 (plain) (tree)
1
2
3

                                                        
                                     
































                                                                           
                        





                    
     





































                                                
                  




















                                           
                                  
 










                                                                                



                          










                                                                                
                                                                                                







                                                                          




                                                                     
            
                                                                                            


     

                                  
                                
                                                                          


                                                                                

                                  
















                                                                                

                                                                   


                            

                                          


                                                                           
                                                                       



                                      








                                                                     






                                
 



















                                                                                
                                                                       










                                                                                    
 













                                                                                

                                                                   






                                                                           

                                                                       








                                    
 


















                                                                                
                                                                           
   
                                                                       










                                                                                       

 








                                                                                
                              


             

                              



                           
           


                                               


                                                         


























                                                                                                
                                 











                                                                           
                















                                                                      
                                          
















                                                                                    
                                         


                                                                                   
                                                                               






































                                                                       
                                                              








                                                           
 


























                                                                                
                              

                    

                  


















































                                                                                      
              
























                                                                   
/* rsPMCQ1.c - Radstone PMCQ1 Common Initialisation Code
 *
 * Copyright 2000 Radstone Technology
 *
 * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
 * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
 *
 * You are hereby granted permission to use, copy, modify, and distribute
 * this file, provided that this notice, plus the above copyright notice
 * and disclaimer, appears in all copies. Radstone Technology will provide
 * no support for this code.
 *
 *  COPYRIGHT (c) 2005.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 */

/*
DESCRIPTION
These functions are responsible for scanning for PMCQ1's and setting up
the Motorola MC68360's if present.

USAGE
call rsPMCQ1Init() to perform ba  sic initialisation of the PMCQ1's.
*/

/* includes */
#include <libcpu/io.h>
#include <bsp/irq.h>
#include <stdlib.h>
#include <rtems/bspIo.h>
#include <bsp/pci.h>
#include <bsp.h>
#include "rsPMCQ1.h"
#include "m68360.h"

/* defines */
#if 1
#define DEBUG_360
#endif

/* Local data */
PPMCQ1BoardData		pmcq1BoardData = NULL;

static unsigned char rsPMCQ1Initialized = FALSE;

/* forward declarations */

/* local Qspan II serial eeprom table */
static unsigned char rsPMCQ1eeprom[] =
    {
    0x00,	/* Byte 0 - PCI_SID */
    0x00,	/* Byte 1 - PCI_SID */
    0x00,	/* Byte 2 - PCI_SID */
    0x00,	/* Byte 3 - PCI_SID */
    0x00,	/* Byte 4 - PBROM_CTL */
    0x00,	/* Byte 5 - PBROM_CTL */
    0x00,	/* Byte 6 - PBROM_CTL */
    0x2C,	/* Byte 7 - PBTI0_CTL */
    0xB0,	/* Byte 8 - PBTI1_CTL */
    0x00,	/* Byte 9 - QBSI0_AT */
    0x00,	/* Byte 10 - QBSI0_AT */
    0x02,	/* Byte 11 - QBSI0_AT */
    0x00,	/* Byte 12 - PCI_ID */
    0x07,	/* Byte 13 - PCI_ID */
    0x11,	/* Byte 14 - PCI_ID */
    0xB5,	/* Byte 15 - PCI_ID */
    0x06,	/* Byte 16 - PCI_CLASS */
    0x80,	/* Byte 17 - PCI_CLASS */
    0x00,	/* Byte 18 - PCI_CLASS */
    0x00,	/* Byte 19 - PCI_MISC1 */
    0x00,	/* Byte 20 - PCI_MISC1 */
    0xC0,	/* Byte 21 - PCI_PMC */
    0x00	/* Byte 22 - PCI_BST */
};

void MsDelay(void)
{
  printk(".");
}

void write8( int addr, int data ){
  out_8((void *)addr, (unsigned char)data);
}

void write16( int addr, int data ) {
  out_be16((void *)addr, (short)data );
}

void write32( int addr, int data ) {
  out_be32((unsigned int *)addr, data );
}

int read32( int addr){
  return in_be32((unsigned int *)addr);
}


void rsPMCQ1_scc_nullFunc(void) {}

/*******************************************************************************
* rsPMCQ1Int - handle a PMCQ1 interrupt
*
* This routine gets called when the QUICC or MA causes
* an interrupt.
*
* RETURNS: NONE.
*/

void rsPMCQ1Int( void *ptr )
{
  unsigned long   status;
  unsigned long   status1;
  unsigned long   mask;
  uint32_t        data;
  PPMCQ1BoardData boardData = ptr;

  status = PMCQ1_Read_EPLD(boardData->baseaddr, PMCQ1_INT_STATUS );
  mask   = PMCQ1_Read_EPLD(boardData->baseaddr, PMCQ1_INT_MASK );

  if (((mask & PMCQ1_INT_MASK_QUICC) == 0) && (status & PMCQ1_INT_STATUS_QUICC))
  {
    /* If there is a handler call it otherwise mask the interrupt */
    if (boardData->quiccInt) {
      boardData->quiccInt(boardData->quiccArg);
    } else {
      *(volatile unsigned long *)(boardData->baseaddr + PMCQ1_INT_MASK) |= PMCQ1_INT_MASK_QUICC;
    }
  }

  if (((mask & PMCQ1_INT_MASK_MA) == 0) && (status & PMCQ1_INT_STATUS_MA))
  {
    /* If there is a handler call it otherwise mask the interrupt */
    if (boardData->maInt) {
      boardData->maInt(boardData->maArg);

      data = PMCQ1_Read_EPLD(boardData->baseaddr, PMCQ1_INT_STATUS );
      data &= (~PMCQ1_INT_STATUS_MA);
      PMCQ1_Write_EPLD(boardData->baseaddr, PMCQ1_INT_STATUS, data );

    } else {
     *(volatile unsigned long *)(boardData->baseaddr + PMCQ1_INT_MASK) |= PMCQ1_INT_MASK_MA;
    }
  }

  RTEMS_COMPILER_MEMORY_BARRIER();

  /* Clear Interrupt on QSPAN */
  *(volatile unsigned long *)(boardData->bridgeaddr + 0x600) = 0x00001000;

  /* read back the status register to ensure that the pci write has completed */
  status1 = *(volatile unsigned long *)(boardData->bridgeaddr + 0x600);
  RTEMS_COMPILER_MEMORY_BARRIER();

}


/*******************************************************************************
*
* rsPMCQ1MaIntConnect - connect a MiniAce interrupt routine
*
* This routine is called to connect a MiniAce interrupt handler
* upto a PMCQ1.
*
* RETURNS: OK if PMCQ1 found, ERROR if not.
*/

unsigned int rsPMCQ1MaIntConnect (
    unsigned long	busNo,	/* Pci Bus number of PMCQ1 */
    unsigned long	slotNo,	/* Pci Slot number of PMCQ1 */
    unsigned long	funcNo,	/* Pci Function number of PMCQ1 */
    FUNCION_PTR	routine,/* interrupt routine */
    int		arg	/* argument to pass to interrupt routine */
)
{
  PPMCQ1BoardData boardData;
  uint32_t        data;
  unsigned int    status = RTEMS_IO_ERROR;

  for (boardData = pmcq1BoardData; boardData; boardData = boardData->pNext)
  {
    if ((boardData->busNo == busNo) && (boardData->slotNo == slotNo) &&
        (boardData->funcNo == funcNo))
    {
      boardData->maInt = routine;
      boardData->maArg = arg;

      data  = PMCQ1_Read_EPLD(boardData->baseaddr, PMCQ1_INT_MASK );
      data &= (~PMCQ1_INT_MASK_MA);
      PMCQ1_Write_EPLD(boardData->baseaddr, PMCQ1_INT_MASK, data );

      data = PMCQ1_Read_EPLD(boardData->baseaddr, PMCQ1_INT_STATUS );
      data &= (~PMCQ1_INT_STATUS_MA);
      PMCQ1_Write_EPLD(boardData->baseaddr, PMCQ1_INT_STATUS, data );

      status = RTEMS_SUCCESSFUL;
      break;
    }
  }

  return (status);
}

/*******************************************************************************
*
* rsPMCQ1MaIntDisconnect - disconnect a MiniAce interrupt routine
*
* This routine is called to disconnect a MiniAce interrupt handler
* from a PMCQ1. It also masks the interrupt source on the PMCQ1.
*
* RETURNS: OK if PMCQ1 found, ERROR if not.
*/

unsigned int rsPMCQ1MaIntDisconnect(
    unsigned long	busNo,	/* Pci Bus number of PMCQ1 */
    unsigned long	slotNo,	/* Pci Slot number of PMCQ1 */
    unsigned long	funcNo	/* Pci Function number of PMCQ1 */
)
{
  PPMCQ1BoardData boardData;
  unsigned int status = RTEMS_IO_ERROR;

  for (boardData = pmcq1BoardData; boardData; boardData = boardData->pNext) {
    if ((boardData->busNo == busNo) && (boardData->slotNo == slotNo) &&
        (boardData->funcNo == funcNo))
    {
      boardData->maInt = NULL;
      *(unsigned long *)(boardData->baseaddr + PMCQ1_INT_MASK) |= PMCQ1_INT_MASK_MA;
      status = RTEMS_SUCCESSFUL;
      break;
    }
  }

  return (status);
}

/*******************************************************************************
*
* rsPMCQ1QuiccIntConnect - connect a Quicc interrupt routine
*
* This routine is called to connect a Quicc interrupt handler
* upto a PMCQ1.
*
* RETURNS: OK if PMCQ1 found, ERROR if not.
*/

unsigned int rsPMCQ1QuiccIntConnect(
    unsigned long	busNo,	/* Pci Bus number of PMCQ1 */
    unsigned long	slotNo,	/* Pci Slot number of PMCQ1 */
    unsigned long	funcNo,	/* Pci Function number of PMCQ1 */
    FUNCION_PTR	routine,/* interrupt routine */
    int		arg	/* argument to pass to interrupt routine */
)
{
  PPMCQ1BoardData boardData;
  unsigned int status = RTEMS_IO_ERROR;

  for (boardData = pmcq1BoardData; boardData; boardData = boardData->pNext)
  {
    if ((boardData->busNo == busNo) && (boardData->slotNo == slotNo) &&
        (boardData->funcNo == funcNo))
    {
      boardData->quiccInt = routine;
      boardData->quiccArg = arg;
      status = RTEMS_SUCCESSFUL;
      break;
    }
  }
  return (status);
}

/*******************************************************************************
*
* rsPMCQ1QuiccIntDisconnect - disconnect a Quicc interrupt routine
*
* This routine is called to disconnect a Quicc interrupt handler
* from a PMCQ1. It also masks the interrupt source on the PMCQ1.
*
* RETURNS: OK if PMCQ1 found, ERROR if not.
*/

unsigned int rsPMCQ1QuiccIntDisconnect(
    unsigned long	busNo,	/* Pci Bus number of PMCQ1 */
    unsigned long	slotNo,	/* Pci Slot number of PMCQ1 */
    unsigned long	funcNo	/* Pci Function number of PMCQ1 */
)
{
  PPMCQ1BoardData boardData;
  unsigned int status = RTEMS_IO_ERROR;

  for (boardData = pmcq1BoardData; boardData; boardData = boardData->pNext)
  {
    if ((boardData->busNo == busNo) && (boardData->slotNo == slotNo) &&
        (boardData->funcNo == funcNo))
    {
      boardData->quiccInt = NULL;
      *(unsigned long *)(boardData->baseaddr + PMCQ1_INT_MASK) |= PMCQ1_INT_MASK_QUICC;
      status = RTEMS_SUCCESSFUL;
      break;
    }
  }

  return (status);
}


/*******************************************************************************
*
* rsPMCQ1Init - initialize the PMCQ1's
*
* This routine is called to initialize the PCI card to a quiescent state.
*
* RETURNS: OK if PMCQ1 found, ERROR if not.
*/

unsigned int rsPMCQ1Init(void)
{
  int busNo;
  int slotNo;
  unsigned int baseaddr = 0;
  unsigned int bridgeaddr = 0;
  unsigned long pbti0_ctl;
  int i;
  unsigned char int_vector;
  int fun;
  int temp;
  PPMCQ1BoardData       boardData;
  rtems_irq_connect_data IrqData = {0,
                                    rsPMCQ1Int,
                                    rsPMCQ1_scc_nullFunc,
                                    rsPMCQ1_scc_nullFunc,
                                    rsPMCQ1_scc_nullFunc,
                                    NULL};

  if (rsPMCQ1Initialized)
  {
    return RTEMS_SUCCESSFUL;
  }
  for (i=0;;i++){
    if ( pci_find_device(PCI_VEN_ID_RADSTONE, PCI_DEV_ID_PMCQ1, i, &busNo, &slotNo, &fun) != 0 )
      break;

    pci_read_config_dword(busNo, slotNo, 0, PCI_BASE_ADDRESS_2, &baseaddr);
    pci_read_config_dword(busNo, slotNo, 0, PCI_BASE_ADDRESS_0, &bridgeaddr);
#ifdef DEBUG_360
  printk("PMCQ1 baseaddr 0x%08x bridgeaddr 0x%08x\n", baseaddr, bridgeaddr );
#endif

    /* Set function code to normal mode and enable window */
    pbti0_ctl = *(unsigned long *)(bridgeaddr + 0x100) & 0xff0fffff;
    eieio();
    *(unsigned long *)(bridgeaddr + 0x100) = pbti0_ctl | 0x00500080;
    eieio();

    /* Assert QBUS reset */
    *(unsigned long *)(bridgeaddr + 0x800) |= 0x00000080;
    eieio();

    /*
     * Hold QBus in reset for 1ms
     */
    MsDelay();

    /* Take QBUS out of reset */
    *(unsigned long *)(bridgeaddr + 0x800) &= ~0x00000080;
    eieio();

    MsDelay();

    /* If a QUICC is fitted initialise it */
    if (PMCQ1_Read_EPLD(baseaddr, PMCQ1_BUILD_OPTION) & PMCQ1_QUICC_FITTED)
    {
#ifdef DEBUG_360
  printk(" Found QUICC busNo %d slotNo %d\n", busNo, slotNo);
#endif

      /* Initialise MBAR (must use function code of 7) */
      *(unsigned long *)(bridgeaddr + 0x100) = pbti0_ctl | 0x00700080;
      eieio();

      /* place internal 8K SRAM and registers at address 0x0 */
      *(unsigned long *)(baseaddr + Q1_360_MBAR) = 0x1;
      eieio();

      /* Set function code to normal mode */
      *(unsigned long *)(bridgeaddr + 0x100) = pbti0_ctl | 0x00500080;
      eieio();

      /* Disable the SWT and perform basic initialisation */
      write8(baseaddr+Q1_360_SIM_SYPCR,0);
      eieio();

      write32(baseaddr+Q1_360_SIM_MCR,0xa0001029);
      write16(baseaddr+Q1_360_SIM_PICR,0);
      write16(baseaddr+Q1_360_SIM_PITR,0);

      write16(baseaddr+Q1_360_CPM_ICCR,0x770);
      write16(baseaddr+Q1_360_CPM_SDCR,0x770);
      write32(baseaddr+Q1_360_CPM_CICR,0x00e49f00);
      write16(baseaddr+Q1_360_SIM_PEPAR,0x2080);
      eieio();

      /* Enable SRAM */
      write32(baseaddr+Q1_360_SIM_GMR,0x00001000);  /* external master wait state */
      eieio();
      write32(baseaddr+Q1_360_SIM_OR0,0x1ff00000);  /*| MEMC_OR_FC*/
      eieio();
      write32(baseaddr+Q1_360_SIM_BR0,0);
      eieio();
      write32(baseaddr+Q1_360_SIM_OR1,(0x5ff00000 | 0x00000780));  /*| MEMC_OR_FC*/
      eieio();
      write32(baseaddr+Q1_360_SIM_BR1,(0x00000040 | 0x00000001 | 0x00200280) );
      eieio();
    }

    /*
     * If a second PCI window is present then make it opposite
     * endian to simplify 1553 integration.
     */
    pci_read_config_dword(busNo, slotNo, 0, PCI_BASE_ADDRESS_3, &temp);
    if (temp) {
      *(unsigned long *)(bridgeaddr + 0x110) |= 0x00500880;
    }

    /*
     * Create descriptor structure for this card
     */
    if ((boardData = malloc(sizeof(struct _PMCQ1BoardData))) == NULL)
    {
      printk("Error Unable to allocate memory for _PMCQ1BoardData\n");
      return(RTEMS_IO_ERROR);
    }

    boardData->pNext = pmcq1BoardData;
    boardData->busNo = busNo;
    boardData->slotNo = slotNo;
    boardData->funcNo = 0;
    boardData->baseaddr = baseaddr;
    boardData->bridgeaddr = bridgeaddr;
    boardData->quiccInt = NULL;
    boardData->maInt = NULL;
    pmcq1BoardData = boardData;
    mc68360_scc_create_chip( boardData, int_vector );

    /*
     * Connect PMCQ1 interrupt handler.
     */
    pci_read_config_byte(busNo, slotNo, 0, 0x3c, &int_vector);
#ifdef DEBUG_360
    printk("PMCQ1 int_vector %d\n", int_vector);
#endif
    IrqData.name  = ((unsigned int)BSP_PCI_IRQ0 + int_vector);
    IrqData.handle = boardData;
    if (!BSP_install_rtems_shared_irq_handler (&IrqData)) {
        printk("Error installing interrupt handler!\n");
        rtems_fatal_error_occurred(1);
    }

    /*
     * Enable PMCQ1 Interrupts from QSPAN-II
     */

    *(unsigned long *)(bridgeaddr + 0x600) = 0x00001000;
    eieio();
    *(unsigned long *)(bridgeaddr + 0x604) |= 0x00001000;

    eieio();
  }

  if (i > 0)
  {
    rsPMCQ1Initialized = TRUE;
  }
  return((i > 0) ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR);
}

/*******************************************************************************
*
* rsPMCQ1Commission - initialize the serial EEPROM on the QSPAN
*
* This routine is called to initialize the EEPROM attached to the QSPAN
* on the PMCQ1 module. It will load standard settings into any QSPAN's
* found with apparently uninitialised EEPROM's or PMCQ1's (to allow
* EEPROM modifications to be performed).
*/

unsigned int rsPMCQ1Commission( unsigned long busNo, unsigned long slotNo )
{
  unsigned int status = RTEMS_IO_ERROR;
  uint32_t     bridgeaddr = 0;
  unsigned long val;
  int i;
  uint32_t venId1;
  uint32_t venId2;

  pci_read_config_dword(busNo, slotNo, 0, PCI_VENDOR_ID, &venId1);
  pci_read_config_dword(busNo, slotNo, 0, PCI_VENDOR_ID, &venId2);
  if ((venId1 == 0x086210e3) ||
      (venId2 == PCI_ID(PCI_VEN_ID_RADSTONE, PCI_DEV_ID_PMCQ1)))
  {
    pci_read_config_dword(busNo, slotNo, 0, PCI_BASE_ADDRESS_0, &bridgeaddr);
    status = RTEMS_SUCCESSFUL;

    /*
     * The On board PMCQ1 on an EP1A has a subVendor ID of 0.
     * A real PMCQ1 also has the sub vendor ID set up.
     */
    if ((busNo == 0) && (slotNo == 1)) {
      *(unsigned long *)rsPMCQ1eeprom = 0;
    } else {
      *(unsigned long *)rsPMCQ1eeprom = PCI_ID(PCI_VEN_ID_RADSTONE, PCI_DEV_ID_PMCQ1);
    }

    for (i = 0; i < 23; i++) {
      /* Wait until interface not active */
      while(read32(bridgeaddr + 0x804) & 0x80000000) {
        rtems_bsp_delay(1);
      }

      /* Write value */
      write32(bridgeaddr + 0x804, (rsPMCQ1eeprom[i] << 8) | i);

      /* delay for > 31 usec to allow active bit to become set */
      rtems_bsp_delay(100);

      /* Wait until interface not active */
      while(read32(bridgeaddr + 0x804) & 0x80000000) {
        rtems_bsp_delay(1);
      }

      /* Re-read value */
      write32(bridgeaddr + 0x804, 0x40000000 | i);

      /* delay for > 31 usec to allow active bit to become set */
      rtems_bsp_delay(100);

      /* Wait until interface not active */
      while((val = read32(bridgeaddr + 0x804)) & 0x80000000) {
        rtems_bsp_delay(1);
      }

      if (((val >> 8) & 0xff) != rsPMCQ1eeprom[i]) {
        printk("Error writing byte %d expected 0x%02x got 0x%02x\n",
               i, rsPMCQ1eeprom[i], (unsigned char)(val >> 8));
        status = RTEMS_IO_ERROR;
        break;
      }
    }
  }
  return(status);
}

uint32_t PMCQ1_Read_EPLD( uint32_t base, uint32_t reg )
{
  uint32_t data;

  data = ( *((unsigned long *) (base + reg)) );
#ifdef DEBUG_360
  printk("EPLD Read 0x%x: 0x%08x\n", reg + base, data );
#endif
  return data;
}

void PMCQ1_Write_EPLD( uint32_t base, uint32_t reg, uint32_t data )
{
  *((unsigned long *) (base + reg)) = data;
#ifdef DEBUG_360
  printk("EPLD Write 0x%x: 0x%08x\n", reg+base, data );
#endif
}