/*
 * pci.c :  this file contains basic PCI Io functions.
 *
 *  CopyRight (C) 1999 valette@crf.canon.fr
 *
 *  This code is heavilly inspired by the public specification of STREAM V2
 *  that can be found at :
 *
 *      <http://www.chorus.com/Documentation/index.html> by following
 *  the STREAM API Specification Document link.
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/rtems/license.html.
 *
 *  Copyright 2004, 2008 Brookhaven National Laboratory and
 *                  Shuchen K. Feng, <feng1@bnl.gov>
 *
 *   - to be consistent with the original pci.c written by Eric Valette
 *   - added 2nd PCI support for discovery based PCI bridge (e.g. mvme5500/mvme6100)
 *   - added bus support for the expansion of PMCSpan as per request by Peter
 */
#define PCI_MAIN

#include <libcpu/io.h>
#include <rtems/bspIo.h>	    /* printk */

#include <bsp/irq.h>
#include <bsp/pci.h>
#include <bsp/gtreg.h>
#include <bsp/gtpcireg.h>
#include <bsp.h>

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#define PCI_DEBUG 0
#define PCI_PRINT 1

/* allow for overriding these definitions */
#ifndef PCI_CONFIG_ADDR
#define PCI_CONFIG_ADDR			0xcf8
#endif
#ifndef PCI_CONFIG_DATA
#define PCI_CONFIG_DATA			0xcfc
#endif

#ifndef PCI1_CONFIG_ADDR
#define PCI1_CONFIG_ADDR		0xc78
#endif
#ifndef PCI1_CONFIG_DATA
#define PCI1_CONFIG_DATA	        0xc7c
#endif

#define HOSTBRIDGET_ERROR               0xf0000000

#define GT64x60_PCI_CONFIG_ADDR         GT64x60_REG_BASE + PCI_CONFIG_ADDR
#define GT64x60_PCI_CONFIG_DATA         GT64x60_REG_BASE + PCI_CONFIG_DATA

#define GT64x60_PCI1_CONFIG_ADDR        GT64x60_REG_BASE + PCI1_CONFIG_ADDR
#define GT64x60_PCI1_CONFIG_DATA        GT64x60_REG_BASE + PCI1_CONFIG_DATA

static int      numPCIDevs=0;
static DiscoveryChipVersion BSP_sysControllerVersion = 0;
static BSP_VMEchipTypes BSP_VMEinterface = 0;
static rtems_pci_config_t BSP_pci[2]={
  {(volatile unsigned char*) (GT64x60_PCI_CONFIG_ADDR),
   (volatile unsigned char*) (GT64x60_PCI_CONFIG_DATA),
   0 /* defined at BSP_pci_configuration */},
  {(volatile unsigned char*) (GT64x60_PCI1_CONFIG_ADDR),
   (volatile unsigned char*) (GT64x60_PCI1_CONFIG_DATA),
   0 /* defined at BSP_pci_configuration */}
};

/* Pack RegNum,FuncNum,DevNum,BusNum,and ConfigEnable for
 * PCI Configuration Address Register
 */
#define pciConfigPack(bus,dev,func,offset)\
((offset&~3)<<24)|(PCI_DEVFN(dev,func)<<16)|(bus<<8)|0x80

/*
 * Bit encode for PCI_CONFIG_HEADER_TYPE register
 */
static unsigned char ucMaxPCIBus=0;

/* Please note that PCI0 and PCI1 does not correlate with the busNum 0 and 1.
 */
static int indirect_pci_read_config_byte(unsigned char bus,unsigned char dev,unsigned char func,
unsigned char offset, uint8_t *val)
{
  int n=0;

  if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) {
     bus-=BSP_MAX_PCI_BUS_ON_PCI0;
     n=1;
  }

  *val = 0xff;
  if (offset & ~0xff) return PCIBIOS_BAD_REGISTER_NUMBER;
#if 0
  printk("addr %x, data %x, pack %x \n", BSP_pci[n].pci_config_addr),
    BSP_pci[n].config_data,pciConfigPack(bus,dev,func,offset));
#endif

  out_be32((volatile uint32_t *) BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset));
  *val = in_8(BSP_pci[n].pci_config_data + (offset&3));
  return PCIBIOS_SUCCESSFUL;
}

static int indirect_pci_read_config_word(unsigned char bus, unsigned char dev,
unsigned char func, unsigned char offset, uint16_t *val)
{
  int n=0;

  if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) {
     bus-=BSP_MAX_PCI_BUS_ON_PCI0;
     n=1;
  }

  *val = 0xffff;
  if ((offset&1)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER;
#if 0
  printk("addr %x, data %x, pack %x \n", config_addr,
    config_data,pciConfigPack(bus,dev,func,offset));
#endif
  out_be32((volatile uint32_t *) BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset));
  *val = in_le16((volatile uint16_t *) (BSP_pci[n].pci_config_data + (offset&2)));
  return PCIBIOS_SUCCESSFUL;
}

static int indirect_pci_read_config_dword(unsigned char bus, unsigned char dev,
unsigned char func, unsigned char offset, uint32_t *val)
{
  int n=0;

  if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) {
     bus-=BSP_MAX_PCI_BUS_ON_PCI0;
     n=1;
  }

  *val = 0xffffffff;
  if ((offset&3)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER;

  out_be32((volatile uint32_t *)BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset));
  *val = in_le32((volatile uint32_t *)BSP_pci[n].pci_config_data);
  return PCIBIOS_SUCCESSFUL;
}

static int indirect_pci_write_config_byte(unsigned char bus, unsigned char dev,unsigned char func, unsigned char offset, uint8_t val)
{
  int n=0;

  if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) {
     bus-=BSP_MAX_PCI_BUS_ON_PCI0;
     n=1;
  }

  if (offset & ~0xff) return PCIBIOS_BAD_REGISTER_NUMBER;

  out_be32((volatile uint32_t *)BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset));
  out_8((volatile uint8_t *) (BSP_pci[n].pci_config_data + (offset&3)), val);
  return PCIBIOS_SUCCESSFUL;
}

static int indirect_pci_write_config_word(unsigned char bus, unsigned char dev,unsigned char func, unsigned char offset, uint16_t val)
{
  int n=0;

  if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) {
     bus-=BSP_MAX_PCI_BUS_ON_PCI0;
     n=1;
  }

  if ((offset&1)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER;

  out_be32((volatile uint32_t *)BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset));
  out_le16((volatile uint16_t *)(BSP_pci[n].pci_config_data + (offset&3)), val);
  return PCIBIOS_SUCCESSFUL;
}

static int indirect_pci_write_config_dword(unsigned char bus,unsigned char dev,unsigned char func, unsigned char offset, uint32_t val)
{
  int n=0;

  if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) {
     bus-=BSP_MAX_PCI_BUS_ON_PCI0;
     n=1;
  }

  if ((offset&3)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER;

  out_be32((volatile uint32_t *)BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset));
  out_le32((volatile uint32_t *)BSP_pci[n].pci_config_data, val);
  return PCIBIOS_SUCCESSFUL;
}

const pci_config_access_functions pci_indirect_functions = {
  	indirect_pci_read_config_byte,
  	indirect_pci_read_config_word,
  	indirect_pci_read_config_dword,
  	indirect_pci_write_config_byte,
  	indirect_pci_write_config_word,
  	indirect_pci_write_config_dword
};


rtems_pci_config_t BSP_pci_configuration = {
           (volatile unsigned char*) (GT64x60_PCI_CONFIG_ADDR),
	   (volatile unsigned char*) (GT64x60_PCI_CONFIG_DATA),
	   &pci_indirect_functions};

DiscoveryChipVersion BSP_getDiscoveryChipVersion(void)
{
  return(BSP_sysControllerVersion);
}

BSP_VMEchipTypes BSP_getVMEchipType(void)
{
  return(BSP_VMEinterface);
}

/*
 * This routine determines the maximum bus number in the system.
 * The PCI_SUBORDINATE_BUS is not supported in GT6426xAB. Thus,
 * it's not used.
 *
 */
int pci_initialize(void)
{
  int deviceFound;
  unsigned char ucBusNumber, ucSlotNumber, ucFnNumber, ucNumFuncs, data8;
  uint32_t ulHeader, ulClass, ulDeviceID;
#if PCI_DEBUG
  uint32_t pcidata;
#endif

  /*
   * Scan PCI0 and PCI1 buses
   */
  for (ucBusNumber=0; ucBusNumber<BSP_MAX_PCI_BUS; ucBusNumber++) {
    deviceFound=0;
    for (ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) {
      ucFnNumber = 0;
      pci_read_config_dword(ucBusNumber,
				ucSlotNumber,
				0,
				PCI_VENDOR_ID,
				&ulDeviceID);

      if( ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
        /* This slot is empty */
        continue;
      }

      if (++numPCIDevs > PCI_MAX_DEVICES) {
	 rtems_panic("Too many PCI devices found; increase PCI_MAX_DEVICES in pci.h\n");
      }

      if (!deviceFound) deviceFound=1;
      switch(ulDeviceID) {
        case (PCI_VENDOR_ID_MARVELL+(PCI_DEVICE_ID_MARVELL_GT6426xAB<<16)):
          pci_read_config_byte(0,0,0,PCI_REVISION_ID, &data8);
          switch(data8) {
	  case 0x10:
	    BSP_sysControllerVersion = GT64260A;
#if PCI_PRINT
	    printk("Marvell GT64260A (Discovery I) hostbridge detected at bus%d slot%d\n",
                 ucBusNumber,ucSlotNumber);
#endif
	    break;
          case 0x20:
	    BSP_sysControllerVersion = GT64260B;
#if PCI_PRINT
	    printk("Marvell GT64260B (Discovery I) hostbridge detected at bus%d slot%d\n",
                 ucBusNumber,ucSlotNumber);
#endif
	    break;
	  default:
	    printk("Undefined revsion of GT64260 chip\n");
            break;
          }
	  break;
        case PCI_VENDOR_ID_TUNDRA:
#if PCI_PRINT
          printk("TUNDRA PCI-VME bridge detected at bus%d slot%d\n",
                 ucBusNumber,ucSlotNumber);
#endif
          break;
      case (PCI_VENDOR_ID_DEC+(PCI_DEVICE_ID_DEC_21150<<16)):
#if PCI_PRINT
          printk("DEC21150 PCI-PCI bridge detected at bus%d slot%d\n",
                 ucBusNumber,ucSlotNumber);
#endif
	  break;
       default :
#if PCI_PRINT
          printk("BSP unlisted vendor, Bus%d Slot%d DeviceID 0x%" PRIx32 "\n",
             ucBusNumber,ucSlotNumber, ulDeviceID);
#endif
	  /* Kate Feng : device not supported by BSP needs to remap the IRQ line on mvme5500/mvme6100 */
          pci_read_config_byte(ucBusNumber,ucSlotNumber,0,PCI_INTERRUPT_LINE,&data8);
	  if (data8 < BSP_GPP_IRQ_LOWEST_OFFSET)  pci_write_config_byte(ucBusNumber,
 	     ucSlotNumber,0,PCI_INTERRUPT_LINE,BSP_GPP_IRQ_LOWEST_OFFSET+data8);

          break;
      }

#if PCI_DEBUG
      pci_read_config_dword(ucBusNumber,
			  ucSlotNumber,
			  0,
			  PCI_BASE_ADDRESS_0,
                          &data);
      printk("Bus%d BASE_ADDRESS_0 0x%x \n",ucBusNumber, data);
      pci_read_config_dword(ucBusNumber,
			  ucSlotNumber,
			  0,
			  PCI_BASE_ADDRESS_1,
                          &data);
      printk("Bus%d BASE_ADDRESS_1 0x%x \n",ucBusNumber, data);
      pci_read_config_dword(ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI_BASE_ADDRESS_2,
                          &data);
      printk("Bus%d BASE_ADDRESS_2 0x%x \n", ucBusNumber, data);

      pci_read_config_dword(ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI_BASE_ADDRESS_3,
                          &data);
      printk("Bus%d BASE_ADDRESS_3 0x%x \n", ucBusNumber, data);

      pci_read_config_word(ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI_INTERRUPT_LINE,
                          &sdata);
      printk("Bus%d INTERRUPT_LINE 0x%x \n", ucBusNumber, sdata);

      /* We always enable internal memory. */
      pci_read_config_dword(ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI_MEM_BASE_ADDR,
			  &pcidata);
      printk("Bus%d MEM_BASE_ADDR 0x%x \n", ucBusNumber,pcidata);

      /* We always enable internal IO. */
      pci_read_config_dword(ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI_IO_BASE_ADDR,
			  &pcidata);
      printk("Bus%d IO_BASE_ADDR 0x%x \n", ucBusNumber,pcidata);
#endif

      pci_read_config_dword(ucBusNumber,
			  ucSlotNumber,
			  0,
			  PCI_CACHE_LINE_SIZE,
			  &ulHeader);
      if ((ulHeader>>16)&PCI_HEADER_TYPE_MULTI_FUNCTION)
         ucNumFuncs=PCI_MAX_FUNCTIONS;
      else
         ucNumFuncs=1;

#if PCI_DEBUG
      printk("Bus%d Slot 0x%x HEADER/LAT/CACHE 0x%x \n",
             ucBusNumber, ucSlotNumber, ulHeader);
#endif

      for (ucFnNumber=1;ucFnNumber<ucNumFuncs;ucFnNumber++) {
          pci_read_config_dword(ucBusNumber,
				  ucSlotNumber,
				  ucFnNumber,
				  PCI_VENDOR_ID,
				  &ulDeviceID);
          if (ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
	     /* This slot/function is empty */
	     continue;
          }

         /* This slot/function has a device fitted.*/
         pci_read_config_dword(ucBusNumber,
				  ucSlotNumber,
				  ucFnNumber,
				  PCI_CLASS_REVISION,
				  &ulClass);
#if PCI_DEBUG
         printk("Bus%d Slot 0x%x Func %d classID 0x%x \n",ucBusNumber,ucSlotNumber,
             ucFnNumber, ulClass);
#endif

      }
    }
    if (deviceFound) ucMaxPCIBus++;
  } /* for (ucBusNumber=0; ucBusNumber<BSP_MAX_PCI_BUS; ... */
#if PCI_DEBUG
  printk("number of PCI buses: %d, numPCIDevs %d\n",
	 pci_bus_count(), numPCIDevs);
#endif
  pci_interface();
  return(0);
}

void FixupPCI( const struct _int_map *bspmap, int (*swizzler)(int,int) )
{
}

/*
 * Return the number of PCI buses in the system
 */
unsigned char pci_bus_count(void)
{
  return(ucMaxPCIBus);
}

