summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c')
-rw-r--r--c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c558
1 files changed, 558 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c b/c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c
new file mode 100644
index 0000000000..088502e3b2
--- /dev/null
+++ b/c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c
@@ -0,0 +1,558 @@
+/* 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 0
+#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()
+{
+ 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() {}
+
+/*******************************************************************************
+* 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;
+ 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 {
+ *(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);
+ } else {
+ *(unsigned long *)(boardData->baseaddr + PMCQ1_INT_MASK) |= PMCQ1_INT_MASK_MA;
+ }
+ }
+
+ /* Clear Interrupt on QSPAN */
+ *(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);
+}
+
+
+/*******************************************************************************
+*
+* 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 */
+ rtems_irq_hdl routine,/* interrupt routine */
+ rtems_irq_hdl_param 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->maInt = routine;
+ boardData->maArg = arg;
+ 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 */
+ rtems_irq_hdl routine,/* interrupt routine */
+ rtems_irq_hdl_param 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()
+{
+ 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,
+ NULL,
+ (rtems_irq_enable)rsPMCQ1_scc_nullFunc,
+ (rtems_irq_disable)rsPMCQ1_scc_nullFunc,
+ (rtems_irq_is_enabled)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 = (rtems_irq_symbolic_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;
+ unsigned int bridgeaddr = 0;
+ unsigned long val;
+ int i;
+ int venId1;
+ int 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
+}
+