/**   pcie.c
 **
 **    This file contains a number of PCIE logic utilities to help discover and
 ** initialize devices on the PCIE bus on the Octeon QLM2 port on the
 ** TNPA_5651x board.
 **
 ** Author: Ted Schmitz
 ** Date originally created: 9-30-2009
 **
 **/
#include <common.h>
#include "cvmx.h"
#include "cvmx-csr-db.h"
#include "cvmx-pcie.h"
#include "pcie.h"

#if 0 /* code that is no longer used */

/**    This code was originally used to help write and debug a driver for
 **  the Intel 82571 Ethernet controller.  Once the driver was working this
 ** portion of this module was no longer used.  It has been commented out
 ** and kept in case it may be meeded at some future date.
 **/

/**   This table contains the steps taken to configure each 82571 on the
 ** PCIe bus port one.  These values were take from a trace of how the
 ** Linux code initializes these devices.
 **
 **/
static struct pcie_config_reg_entry pcie_config_table[] =
  {
    /* early init port zero in 82571 */
 /* dev  reg  size action data written
    --- ----- ---- -----  ------------ */
    {0, 0x000, B4, READ, 0x0},
    {0, 0x00E, B1, READ, 0x0},
    {0, 0x006, B2, READ, 0x0},
    {0, 0x034, B1, READ, 0x0},
    {0, 0x0C8, B1, READ, 0x0},
    {0, 0x0C9, B1, READ, 0x0},
    {0, 0x0D0, B1, READ, 0x0},
    {0, 0x0D1, B1, READ, 0x0},
    {0, 0x0E0, B1, READ, 0x0},
    {0, 0x100, B4, READ, 0x0},
    {0, 0x008, B4, READ, 0x0},
    {0, 0x03D, B1, READ, 0x0},
    {0, 0x03C, B1, READ, 0x0},
    {0, 0x010, B4, READ, 0x0},
    {0, 0x010, B4, WRITE, 0xFFFFFFFF},
    {0, 0x010, B4, READ, 0x0},
    {0, 0x010, B4, WRITE, 0x0},
    {0, 0x014, B4, READ, 0x0},
    {0, 0x014, B4, WRITE, 0xFFFFFFFF},
    {0, 0x014, B4, READ, 0x0},
    {0, 0x014, B4, WRITE, 0x0},
    {0, 0x018, B4, READ, 0x0},
    {0, 0x018, B4, WRITE, 0xFFFFFFFF},
    {0, 0x018, B4, READ, 0x0},
    {0, 0x018, B4, WRITE, 0x01},
    {0, 0x01C, B4, READ, 0x0},
    {0, 0x01C, B4, WRITE, 0xFFFFFFFF},
    {0, 0x01C, B4, READ, 0x0},
    {0, 0x01C, B4, WRITE, 0x0},
    {0, 0x020, B4, READ, 0x0},
    {0, 0x020, B4, WRITE, 0xFFFFFFFF},
    {0, 0x020, B4, READ, 0x0},
    {0, 0x020, B4, WRITE, 0x0},
    {0, 0x024, B4, READ, 0x0},
    {0, 0x024, B4, WRITE, 0xFFFFFFFF},
    {0, 0x024, B4, READ, 0x0},
    {0, 0x024, B4, WRITE, 0x0},
    {0, 0x030, B4, READ, 0x0},
    {0, 0x030, B4, WRITE, 0xFFFFFFFF},
    {0, 0x030, B4, READ, 0x0},
    {0, 0x030, B4, WRITE, 0x0},
    {0, 0x02C, B2, READ, 0x0},
    {0, 0x02E, B2, READ, 0x0},
    {0, 0x000, B4, READ, 0x0},

    /* early init port one in 82571 */
    {1, 0x000, B4, READ, 0x0},
    {1, 0x00E, B1, READ, 0x0},
    {1, 0x006, B2, READ, 0x0},
    {1, 0x034, B1, READ, 0x0},
    {1, 0x0C8, B1, READ, 0x0},
    {1, 0x0C9, B1, READ, 0x0},
    {1, 0x0D0, B1, READ, 0x0},
    {1, 0x0D1, B1, READ, 0x0},
    {1, 0x0E0, B1, READ, 0x0},
    {1, 0x100, B4, READ, 0x0},
    {1, 0x008, B4, READ, 0x0},
    {1, 0x03D, B1, READ, 0x0},
    {1, 0x03C, B1, READ, 0x0},
    {1, 0x010, B4, READ, 0x0},
    {1, 0x010, B4, WRITE, 0xFFFFFFFF},
    {1, 0x010, B4, READ, 0x0},
    {1, 0x010, B4, WRITE, 0x0},
    {1, 0x014, B4, READ, 0x0},
    {1, 0x014, B4, WRITE, 0xFFFFFFFF},
    {1, 0x014, B4, READ, 0x0},
    {1, 0x014, B4, WRITE, 0x0},
    {1, 0x018, B4, READ, 0x0},
    {1, 0x018, B4, WRITE, 0xFFFFFFFF},
    {1, 0x018, B4, READ, 0x0},
    {1, 0x018, B4, WRITE, 0x01},
    {1, 0x01C, B4, READ, 0x0},
    {1, 0x01C, B4, WRITE, 0xFFFFFFFF},
    {1, 0x01C, B4, READ, 0x0},
    {1, 0x01C, B4, WRITE, 0x0},
    {1, 0x020, B4, READ, 0x0},
    {1, 0x020, B4, WRITE, 0xFFFFFFFF},
    {1, 0x020, B4, READ, 0x0},
    {1, 0x020, B4, WRITE, 0x0},
    {1, 0x024, B4, READ, 0x0},
    {1, 0x024, B4, WRITE, 0xFFFFFFFF},
    {1, 0x024, B4, READ, 0x0},
    {1, 0x024, B4, WRITE, 0x0},
    {1, 0x030, B4, READ, 0x0},
    {1, 0x030, B4, WRITE, 0xFFFFFFFF},
    {1, 0x030, B4, READ, 0x0},
    {1, 0x030, B4, WRITE, 0x0},
    {1, 0x02C, B2, READ, 0x0},
    {1, 0x02E, B2, READ, 0x0},
    {1, 0x000, B4, READ, 0x0},

    /* init memory BAR, I/O BAR, etc., in both Ethernet ports */
    {0, 0x010, B4, WRITE, MEM_BAR_ETH_0},
    {0, 0x010, B4, READ, 0x0},
    {1, 0x010, B4, WRITE, MEM_BAR_ETH_1},
    {1, 0x010, B4, READ, 0x0},
    {0, 0x018, B4, WRITE, IO_BAR_ETH_0},
    {0, 0x018, B4, READ, 0x0},
    {1, 0x018, B4, WRITE, IO_BAR_ETH_1},
    {1, 0x018, B4, READ, 0x0},
    {0, 0x03D, B1, READ, 0x0},
    {0, 0x03C, B1, WRITE, REG_3C_ETH_0},
    {1, 0x03D, B1, READ, 0x0},
    {1, 0x03C, B1, WRITE, REG_3C_ETH_1},

    {TABLE_TERM,0x0000, B1, READ, 0x0ull} /* must be last entry in table */

  };

/*****************************************************************************
 **        Force Configuration Of PCIE Devices On QLM2 Interface
 **
 **    This function sets up all the configuration registers to a specific
 ** pre-calculated value to debug the operation of the 82571 on the PCIE
 ** interface (QLM2 interface).  
 **
 **    This code should be replaced by the proper discovery and dynamic
 ** initialization functions at some future date.
 **
 ** INPUT - "devno" = PCIE device number to be set up.
 **
 ** OUTPUT - The configuration registers on the selected port on the 
 **          82571 will be initialized.
 **
 ** RETURN - Nothing
 **
 ****************************************************************************/
void force_init_of_82571_config_regs(pci_dev_t devno)
{
  uint32_t lwork;
  int i;

#if 0
  printf("\n\n - - In force_init_of_82571_config_regs() for device %0d - -\n\n",
         devno);
#endif
  /* make sure the device number requested is supported by this function */
  if (devno > MAX_PCIE_DEV_NO)
    {
    printf("Device number %0d not supported!\n",devno);
    return;
    }

  /* step through table of operatiouns and set up all registers for the device
     number selected by the calling code */
  i = 0;
  do
    {
    if ((pcie_config_table[i].access == READ) && 
        (pcie_config_table[i].func == devno))
      {
      read_pcie_config_reg(pcie_config_table[i].func,
                           pcie_config_table[i].reg,
                           pcie_config_table[i].size,
                           &lwork);
      printf("Value: %08lx read from reg %04x\n",lwork, 
             pcie_config_table[i].reg);
      }
    else if ((pcie_config_table[i].access == WRITE) &&
             (pcie_config_table[i].func == devno))
      {
      write_pcie_config_reg(pcie_config_table[i].func,
                            pcie_config_table[i].reg,
                            pcie_config_table[i].size,
                            pcie_config_table[i].val);
      printf("Value: %08lx written to reg %04x\n",
             pcie_config_table[i].val,
             pcie_config_table[i].reg);
      }
#if 0 /* not sure we need this now */
    else
      printf("ERROR! Unknown PCIE operation!\n");
#endif

    i++;
    } while (pcie_config_table[i].func != TABLE_TERM);

}
#endif  /* code that is no longer used */

/*****************************************************************************
 **                  Read PCIE Configuration Register
 **
 **   This function reads a PCIe configuration register of any size from a 
 ** device on the QLM2 PCIe interface.
 **
 ** INPUT - "func" function number which is 0 or 1 for read Ethernet Port 0
 **         or Port 1 configuration registers respectively.
 **       - "reg" - configuration reg number to be read
 **       - "size" - size of the access (1, 2, or 4 bytes)
 **       - "*val" - pointer to 32 bit value returned to the calling code.
 **         It is up to the calling code to retrieve the appropriate bytes
 **         from the 32-bit data returned to the calling code.
 **
 ** OUTPUT - Nothing
 **
 ** RETURN - Nothing
 **
 ****************************************************************************/
void read_pcie_config_reg(int func, int reg, int size, uint32_t * val)
{

  switch(size)
    {
    case B1: /* read an 8-bit register */
      *val = cvmx_pcie_config_read8(PCIE_PORT, PCIE_BUS, PCIE_DEV, func, reg);
      break;
    case B2: /* read a 16-bit register */
      *val = cvmx_pcie_config_read16(PCIE_PORT, PCIE_BUS, PCIE_DEV, func, reg);
      break;
    case B4: /* read a 32-bit register */
      *val = cvmx_pcie_config_read32(PCIE_PORT, PCIE_BUS, PCIE_DEV, func, reg);
      break;
    default:
      break;
    }

}

/*****************************************************************************
 **                Write PCIE Configuration Register
 **
 **   This function writes a PCIe configuration register of any size to a 
 ** device on the QLM2 PCIe interface.
 **
 ** INPUT - "func" function number which is 0 or 1 for write Ethernet Port 0
 **         or Port 1 configuration registers respectively.
 **       - "reg" - configuratoin reg number to be written.
 **       - "size" - size of the access (1, 2, or 4 bytes)
 **       - "val" - 32 bit value to be written to the config reg.
 **         The calling code must place the data to be written in the
 **         appropriate bytes of this parameter for the size of the data
 **         being written.
 **
 ** OUTPUT - The selected PCIe configuration registers is written with the
 **          data supplied by the calling code.
 **
 ** RETURN - Nothing
 **
 ****************************************************************************/
void write_pcie_config_reg(int func, int reg, int size, uint32_t val)
{

  switch(size)
    {
    case B1: /* write an 8-bit register */
      cvmx_pcie_config_write8(PCIE_PORT, PCIE_BUS, PCIE_DEV, func, reg,
                              (uint8_t) val);
      break;
    case B2: /* write a 16-bit register */
      cvmx_pcie_config_write16(PCIE_PORT, PCIE_BUS, PCIE_DEV, func, reg,
                               (uint16_t) val);
      break;
    case B4: /* write a 32-bit register */
      cvmx_pcie_config_write32(PCIE_PORT, PCIE_BUS, PCIE_DEV, func, reg,
                               (uint32_t) val);
      break;
    default:
      break;
    }

}

/*****************************************************************************
 **                  Find Next Device On PCIe Bus
 **
 **    This function scans for the next available & supported device on the 
 ** Port 1, PCIe bus on the Octeon chip.  In this case all PCIe devices are to
 ** be found only on PCIe port 1 (QLM 2).  No need to look on other PCIe 
 ** busses.
 **
 ** INPUT - "ids" = Pointer to a list of device identifiers support by this
 **         function.
 **       - "start_device" = PCIe device id with which to start the scan.
 **
 ** OUTPUT - Nothing
 **
 ** RETURN - The first valid PCIe bus device id starting from "index" is 
 **          returned to the calling code.  A negative value is return if no
 **          supported devices aare found on the PCIe bus.
 **
 ****************************************************************************/
pci_dev_t pcie_find_next_device(struct pci_device_id *ids, int start_device)
{
  int cur_pcie_id;                /* current PCIe device id under scan */
  u16 vendor;                     /* vendor id read from current device */
  u16 device;                     /* device id read from current device */
  u8 header_type;                 /* header type field read from device */
  uint32_t lwork;                 /* 32-bit working register */
  int i;

  //printf("In debug pcie_find_next_device: start device = %04X\n",start_device);

#if 0 /* orig logic */
  for (cur_pcie_id = start_device; cur_pcie_id < PCI_MAX_PCI_DEVICES; 
       cur_pcie_id++)
#else /* debug logic */
  for (cur_pcie_id = start_device; cur_pcie_id < 2; 
       cur_pcie_id++)
#endif
    {
    read_pcie_config_reg(cur_pcie_id, PCI_HEADER_TYPE, B1, &lwork);
    //printf("Raw header type = %08lx\n",lwork);

    header_type = (u8) lwork;
    //printf("header_type = %02x\n",header_type);

    /* if no device present at this device number skip to next device number */
    if ((header_type & 0xFF) != 0x80)
      continue;

    /* read vendor ID and device ID from next device on bus */
    read_pcie_config_reg(cur_pcie_id, PCI_VENDOR_ID, B2, &lwork);
    //printf("A:read %08lx from reg %04x\n",lwork, PCI_VENDOR_ID);
    vendor = (lwork & 0xFFFF);

    read_pcie_config_reg(cur_pcie_id, PCI_DEVICE_ID, B2, &lwork);
    //printf("B:read %08lx from reg %04x\n",lwork, PCI_DEVICE_ID);
    device = (lwork & 0xFFFF);

   if ((vendor != 0xFFFF) || (device != 0xFFFF))
     {
#if 0
     printf("TED_debug pcie_find_next_device: vendor = %04X, device = %04X\n",
            vendor, device);
#endif
     for (i = 0; ids[i].vendor != 0; i++)
       {
       if ((vendor == ids[i].vendor) && (device == ids[i].device))
         {
         return cur_pcie_id;
         }
       }
     }/* end 'if ((vendor != 0xFFFF)...' */

    } /* end 'for (cur_pcie_id...' */

  return (-1); /* return "no device found" status */
}


