/* 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 #include #include #include #include #include #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; unsigned 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_number)((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; unsigned int venId1; unsigned 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 }