diff options
Diffstat (limited to 'c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c')
-rw-r--r-- | c/src/lib/libbsp/powerpc/ep1a/console/rsPMCQ1.c | 558 |
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 +} + |