/*
 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
 * Andreas Heppel <aheppel@sysgo.de>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

/*
 * PCI routines for the MPC106 CPU.
 */

#include <ppcboot.h>
#include <command.h>
#include <cmd_boot.h>
#include <mpc106.h>
#include <asm/processor.h>

#ifdef CONFIG_PCI

/*
 * These are the lowest addresses allowed for PCI configuration.
 * They correspond to lowest available I/O and Memory addresses.
 * In the case where where multiple PMM regs are being used to map
 * different PLB to PCI regions, each region should have it's own
 * minimum address.
 */
unsigned long    LowestMemAddr1 = CFG_MIN_PCI_MEMADDR1;
unsigned long    LowestMemAddr2 = CFG_MIN_PCI_MEMADDR2;
unsigned long    LowestIOAddr   = CFG_PCI_PCI_IOADDR;

unsigned long    MaxBusNum = 0;

unsigned char		ShortPCIListing = 0;

/*
 * Subroutine:  pci_init
 *
 * Description: Initialises the MPC106 PCI Configuration regs.
 *
 * Inputs:      bd  board info structure
 *
 * Return:		None
 *
 */
void pci_init(bd_t *bd)
{
	unsigned int pcicmd;
	unsigned int pcistat;
	unsigned int picr1;
	unsigned int picr2;
	unsigned int pvr = get_pvr();

	puts("PCI:   ");

	picr2  = PCI_Read_CFG_Reg(PCIDEVID_MPC106, PCI_PICR2, 4);
	picr2 |= PICR2_CF_SNOOP_WS(3) |
		 PICR2_CF_FLUSH_L2 |
		 PICR2_CF_L2_HIT_DELAY(3) |
		 PICR2_CF_APHASE_WS(3);
	picr2 &= ~(PICR2_L2_EN | PICR2_L2_UPDATE_EN);
	PCI_Write_CFG_Reg(PCIDEVID_MPC106, PCI_PICR2, picr2, 4);

	pcicmd = PCI_Read_CFG_Reg(PCIDEVID_MPC106, PCI_CFG_COMMAND, 2);
	pcicmd |= (CMD_SERR | PCI_CMD_MASTER | PCI_CMD_MEMEN);
	PCI_Write_CFG_Reg(PCIDEVID_MPC106, PCI_CFG_COMMAND, pcicmd, 2);

	/* clear non-reserved bits in status register */
	pcistat = PCI_Read_CFG_Reg(PCIDEVID_MPC106, PCI_CFG_STATUS, 2);
	pcistat |= PCI_STAT_NO_RSV_BITS;
	PCI_Write_CFG_Reg(PCIDEVID_MPC106, PCI_CFG_STATUS, pcistat, 2);

	picr1 = PCI_Read_CFG_Reg(PCIDEVID_MPC106, PCI_PICR1, 4);
	if (PVR_VER(pvr) == 0x0008)
		picr1 |= PICR1_PROC_TYPE_603;
	else
		picr1 |= PICR1_PROC_TYPE_604;

	picr1 |= PICR1_CF_CBA(63) | PICR1_CF_BREAD_WS(2);
	picr1 |= PICR1_MCP_EN | PICR1_CF_DPARK |
		 PICR1_CF_LOOP_SNOOP | PICR1_CF_APARK;
	PCI_Write_CFG_Reg(PCIDEVID_MPC106, PCI_PICR1, picr1, 4);

#ifdef CONFIG_PCI_PNP
	/*
	 * Scan the PCI bus and configure devices found.
	 */
	puts("scanning bus for devices ...");

    PCI_Scan(0);
#endif  /* CONFIG_PCI_PNP */
	puts("OK\n");
}

/*
 * Subroutine:  PCI_Scan
 *
 * Description: Scan through all allowable PCI IDs and configure
 *              those for which the vendor ID indicates there is a
 *              device present. Routine scans only function 0.
 *
 * Inputs:      BusNum  Bus number where scanning begins
 *
 * Return:      Number of devices found on the bus.
 *
 */
int PCI_Scan(int BusNum)
{
	int	Device;
	int Function;
	int	BusDevFunc;
	int Found = 0;
	unsigned int HeaderType = 0;
	unsigned int VendorID = 0;

#ifdef DEBUG
	printf("Scanning PCI bus %d\n", BusNum);
#endif

	/*
	 * Start with device 0, the MPC106 is device 0.  sr: 09-07-2001
	 */
	for (Device = 0; Device < CFG_MAX_PCI_DEVICES; Device++) {
		HeaderType = 0;
		VendorID = 0;
		for (Function = 0; Function < CFG_MAX_PCI_FUNCTIONS; Function++) {
			/*
			 * If this is not a multi-function device, we skip the rest
			 */
			if (Function && !(HeaderType & 0x80))
				break;

			BusDevFunc = (BusNum << 16) |
				     (Device << 11) |
				     (Function << 8);

			VendorID = PCI_Read_CFG_Reg (BusDevFunc,
						     PCI_CFG_VENDOR_ID, 2);
			if ((VendorID == 0xFFFF) || (VendorID == 0x0000))
				continue;

			HeaderType = PCI_Read_CFG_Reg(BusDevFunc,
						      PCI_CFG_HEADER_TYPE, 1);
#ifdef DEBUG
			printf("\nPCI Device %d Funtion %d is present\n",
				Device, Function);
#endif
			if (HeaderType & 0x01) {
				/*  PCI-PCI Bridge */
				PCI_Config_Bridge(BusDevFunc);
			} else {
				PCI_Config_Device(BusDevFunc, 6);
			}
			Found++;
		}
	}
	return Found;
}

/*
 * Subroutine:  PCI_Read_CFG_Reg
 *
 * Description: Read a PCI configuration register
 *
 * Inputs:      BusDevFunc      PCI Bus+Device+Function number
 *              Reg             Configuration register number
 *              Width           Number of bytes to read (1, 2, or 4)
 *
 * Return:      Value of the configuration register read.
 *              For reads shorter than 4 bytes, return value
 *              is LSB-justified
 */
unsigned int PCI_Read_CFG_Reg(int BusDevFunc, int Reg, int Width)
{
	unsigned int RegAddr;

	/*
	 * bit 31 must be 1 and bits 1:0 must be 0 (note Little Endian bit notation)
	 */
	RegAddr = MPC106_REG | ((Reg|BusDevFunc) & 0xFFFFFFFC);

	/*
	 * Write reg to PCI Config Address
	 */
	out32r(MPC106_REG_ADDR, RegAddr);

	/*
	 * Read reg value from PCI Config Data
	*/
	switch (Width) {
	case 1:
		return ((unsigned int) in8(MPC106_REG_DATA | (Reg & 0x3)));
    	case 2:
		return ((unsigned int) in16r(MPC106_REG_DATA | (Reg & 0x3)));
    	case 4:
    		return (in32r(MPC106_REG_DATA | (Reg & 0x3)));
	}

	return 0; /* not reached: just to satisfy the compiler */
}

/*
 * Subroutine:  PCI_Write_CFG_Reg
 *
 * Description: Write a PCI configuration register.
 *
 * Inputs:      BusDevFunc      PCI Bus+Device+Function number
 *              Reg             Configuration register number
 *              Value           Configuration register value
 *              Width           Number of bytes to write (1, 2, or 4)
 *
 * Return:      0       Successful
 *
 */
int PCI_Write_CFG_Reg(int BusDevFunc, int Reg, unsigned int Value, int Width)
{
	unsigned int    RegAddr;

	/*
	 * bit 31 must be 1 and bits 1:0 must be 0 (note Little Endian bit notation)
	 * (DWORD alignment)
	 */
	RegAddr = MPC106_REG | ((Reg|BusDevFunc) & 0xFFFFFFFC);

	/*
	 * Write reg to PCI Config Address
	 */
	out32r(MPC106_REG_ADDR, RegAddr);

	/*
	 * Perform a read to update the data register.
	 */
	in32r(MPC106_REG_DATA | (Reg & 0x3));

	/*
	 * Write reg value to PCI Config Data
	 */
	switch (Width) {
	case 1: out8   (MPC106_REG_DATA | (Reg & 0x3),
			(unsigned char)(Value & 0xFF));
    		break;
	case 2: out16r (MPC106_REG_DATA | (Reg & 0x3),
			(unsigned short)(Value & 0xFFFF));
    		break;
	case 4: out32r (MPC106_REG_DATA | (Reg & 0x3), Value);
		break;
	}

	return 0;
}

/*
 * Subroutine:  PCI_Config_Device
 *
 * Description: Configure a PCI device by examining its I/O and memory
 *              address space needs and allocating address space to it by
 *              programming the address decoders in the Base Address Registers.
 *
 * Inputs:		BusDevFunc    Bus+Device+Function number
 *              NumBaseAddr   Number of base address registers to
 *                            configure
 *
 * Return:      None
 *
 */
void PCI_Config_Device(int BusDevFunc, int NumBaseAddr)
{
	int     AddrSlot, i;
	unsigned long  AddrDesc, AddrProg, Min_Gnt_Val;

	unsigned long pcicmd;
	pcicmd = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_COMMAND, 2);
	PCI_Write_CFG_Reg (BusDevFunc, PCI_CFG_COMMAND,
			   pcicmd & ~(PCI_CMD_MEMEN|PCI_CMD_IOEN), 2);

	for (AddrSlot = 0; AddrSlot < NumBaseAddr; AddrSlot++) {
		/*
		 * Write virtual address into register to check LSB.
		 * LSB == 1 => IO space, else memory space.
		 */
		PCI_Write_CFG_Reg (BusDevFunc,
				   PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot),
				   0xFFFFFFFF, 4);

		AddrDesc = PCI_Read_CFG_Reg (BusDevFunc,
					     PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot),
					     4);

		if (AddrDesc == 0)		/* unimplemented, stop looking */
			continue;		/* 01/04/99 MCG */

#ifdef DEBUG
		printf ("Read Base Addr Reg %d = 0x%08lx\n",
			AddrSlot, AddrDesc);
#endif

		if ((AddrDesc & 1) == 1) {        /* I/O space */
			AddrDesc &= 0xFFFFFFFC;

			for (i = 0; (AddrDesc & 1) != 1; i++)
				AddrDesc = AddrDesc >> 1;

			AddrDesc = 1 << i;
#ifdef DEBUG
			printf("  PCI I/O space = 0x%lx bytes\n", AddrDesc);
#endif
			for (AddrProg = CFG_PCI_PCI_IOADDR;
			     AddrProg < LowestIOAddr;
			     AddrProg += AddrDesc) {
				;	/* empty */
			}
			PCI_Write_CFG_Reg (BusDevFunc,
					   PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot),
					   AddrProg, 4);
#ifdef DEBUG
			printf("  PCI I/O addr = 0x%lx\n", AddrProg);
#endif
			LowestIOAddr = AddrProg + AddrDesc;
		} else {                          /* memory space */
			AddrDesc &= 0xFFFFFFF0;

			for (i = 0; (AddrDesc & 1) != 1; i++)
				AddrDesc = AddrDesc >> 1;

			AddrDesc = 1 << i;

			if ((unsigned long)AddrDesc < 4096)
				AddrDesc = 4096;
#ifdef DEBUG
			printf("  PCI memory space = 0x%lx bytes \n",AddrDesc);
#endif
			for (AddrProg = CFG_MIN_PCI_MEMADDR1;
			     AddrProg < LowestMemAddr1;
			     AddrProg += AddrDesc) {
			     	;	/* empty */
			}

			PCI_Write_CFG_Reg (BusDevFunc,
					   PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot),
					   AddrProg, 4);
#ifdef DEBUG
			printf("  PCI memory addr = 0x%lx\n", AddrProg);
#endif
			LowestMemAddr1 = AddrProg + AddrDesc;
		}
	}

	/*
	 * Assign expansion ROM address
	 */
	PCI_Write_CFG_Reg(BusDevFunc, PCI_CFG_EXPANSION_ROM, 0xFFFFFFFE, 4);

	AddrDesc = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_EXPANSION_ROM, 4);

	if (AddrDesc != 0) {
#ifdef DEBUG
		printf("Read Expansion ROM Addr Reg = 0x%08lx\n" , AddrDesc);
#endif
		for (i = 0; (AddrDesc & 1) != 1; i++)
			AddrDesc = AddrDesc >> 1;

		AddrDesc = 1 << i;
#ifdef DEBUG
		printf("  PCI Expansion ROM space = 0x%lx bytes\n", AddrDesc);
#endif
		for (AddrProg = CFG_MIN_PCI_MEMADDR1;
		     AddrProg < LowestMemAddr1;
		     AddrProg += AddrDesc) {
			;	/* empty */
		}
		PCI_Write_CFG_Reg(BusDevFunc, PCI_CFG_EXPANSION_ROM, AddrProg, 4);
#ifdef DEBUG
		printf("  PCI Expansion ROM addr = 0x%lx\n", AddrProg);
#endif
		LowestMemAddr1 = AddrProg + AddrDesc;
	}

	Min_Gnt_Val = 0x80;
	PCI_Write_CFG_Reg(BusDevFunc, PCI_CFG_LATENCY_TIMER, Min_Gnt_Val, 1);

	/*
	 * Disable interrupt line, if device says it wants to use interrupts
	 */
	if (PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_DEV_INT_PIN, 1) != 0)
		PCI_Write_CFG_Reg(BusDevFunc, PCI_CFG_DEV_INT_LINE, 0xFF, 1);
	/*
	 * Enable i/o space and memory space on this device
	 */
	PCI_Write_CFG_Reg (BusDevFunc, PCI_CFG_COMMAND,
			   pcicmd | PCI_CMD_MEMEN | PCI_CMD_IOEN, 2);
}

/*
 * Subroutine:  PCI_Config_Bridge
 *
 * Description: Configure a PCI-PCI bridge
 *
 * Inputs:		BusDevFunc      Bus+Device+Function number
 *
 * Return:      None
 *
 */
void PCI_Config_Bridge(int BusDevFunc)
{
	int     SecondaryBus;
	int     PrimaryBus;
	int     CommandReg_Val;
	int     InitialLowestIOAddr, InitialLowestMemAddr;
	int     IOBase, MemBase;
	int     IOLimit, MemLimit;

	InitialLowestIOAddr = LowestIOAddr;
	InitialLowestMemAddr = LowestMemAddr1;

	CommandReg_Val = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_COMMAND, 2);

	/* Configure bridge's base address registers */
	PCI_Config_Device(BusDevFunc, 2);

	/*
	 * PCI_config_device() for the bridge could have changed the values in
	 * LowestIOAddr and LowestMemAddr1, if the bridge itself uses
	 * I/O and/or memory space.
	 */
	if ( LowestIOAddr > InitialLowestIOAddr )	/* bridge uses IO space? */
		CommandReg_Val |= 0x01;			/* enable I/O Space */

	if ( LowestMemAddr1 > InitialLowestMemAddr ) /* bridge uses memory space? */
    	CommandReg_Val |= 0x02;				/* enable Memory Space */

	PrimaryBus = (BusDevFunc >> 16) & 0xFF;
	PCI_Write_CFG_Reg(BusDevFunc, PCIPCI_PRIMARYBUS, PrimaryBus, 1);

	SecondaryBus = ++MaxBusNum;
	PCI_Write_CFG_Reg(BusDevFunc, PCIPCI_SECONDARYBUS, SecondaryBus, 1);

	/*
	 * Start with max. possible value for subordinate bus number
	 * Later, after any additional child busses are found, we'll update this
	 */
	PCI_Write_CFG_Reg(BusDevFunc, PCIPCI_SUBORDINATEBUS, 0xFF, 1);

	/* IO Base must be on 4Kb boundary.  Adjust if needed */
	if ((LowestIOAddr % 4096) != 0)
    	LowestIOAddr += 4096 - (LowestIOAddr % 4096);

	PCI_Write_CFG_Reg (BusDevFunc, PCIPCI_IOBASE,
			   (LowestIOAddr>>8) & 0xF0, 1);
	PCI_Write_CFG_Reg (BusDevFunc, PCIPCI_IOBASEUPPER16,
			   (LowestIOAddr>>16) & 0xFFFF, 2);

	IOBase = LowestIOAddr;

	/* Mem Base must be on 1 MB boundary.  adjust if needed */
	if ((LowestMemAddr1 % 0x100000) != 0)
    	LowestMemAddr1 += 0x100000 - (LowestMemAddr1 % 0x100000);

	PCI_Write_CFG_Reg (BusDevFunc, PCIPCI_MEMBASE,
			   (LowestMemAddr1>>16) & 0xFFF0, 2);
	MemBase = LowestMemAddr1;

	/*
	 * secondary bus on this bridge
	 */
	PCI_Scan(SecondaryBus);

	PCI_Write_CFG_Reg(BusDevFunc, PCIPCI_SUBORDINATEBUS, MaxBusNum, 1);

	IOLimit = LowestIOAddr;
	if (LowestIOAddr > IOBase) {	/* IO space used on secondary bus?   */
		CommandReg_Val |= 0x01; /*   enable IO Space                 */
		IOLimit--;              /*   IOLimit is highest used address */
	}

	PCI_Write_CFG_Reg (BusDevFunc, PCIPCI_IOLIMIT,
			   ((IOLimit)>>8) & 0xF0, 1);
	PCI_Write_CFG_Reg (BusDevFunc, PCIPCI_IOLIMITUPPER16,
			   ((IOLimit)>>16) & 0xFFFF, 2);

	/*
	 * IOLIMIT is the starting address of a 4K block forwarded by the bridge.
	 * Round LowestIOAddr up to the next 4K boundary if IO space is enabled.
	 */
	if ((CommandReg_Val & 0x01) == 0x01)
    	LowestIOAddr = (IOLimit | 0xFFF) + 1;

	MemLimit = LowestMemAddr1;
	if ( LowestMemAddr1 > MemBase ) { /* mem. space is used on secondary bus? */
		CommandReg_Val |= 0x02;	/*   enable Memory Space                */
		MemLimit--;             /*   MemLimit is highest used address   */
	}

	PCI_Write_CFG_Reg (BusDevFunc, PCIPCI_MEMLIMIT,
			   ((MemLimit)>>16) & 0xFFF0, 2);

	/*
	 * MEMLIMIT is the starting address of a 1M block forwarded by the bridge.
	 * Round LowestMemAddr up to the next 1M boundary
	 * if Memory space is enabled.
	 */

	if ( (CommandReg_Val & 0x02) == 0x02 )
		LowestMemAddr1 = (MemLimit | 0xFFFFF) + 1;

	/* Enable Bus Master on secondary bus */
	CommandReg_Val |= 0x04;

	PCI_Write_CFG_Reg(BusDevFunc, PCI_CFG_COMMAND, CommandReg_Val, 2);
}

/*
 * Subroutine:  PCI_Find_Device
 *
 * Description: Locate a PCI device by vendor and device number on any bus.
 *
 * Inputs:		VendorID        Value of the device's Vendor ID field
 *              DeviceID        Value of the device's Device ID field
 *
 * Return:      < 0     Device not found
 *              (int)   PCI Bus+Device+Function number
 *
 */
int PCI_Find_Device(unsigned short VendorID, unsigned short DeviceID)
{
	int     Device;
	int     BusDevFunc;
	int     BusNum;

#if CFG_SCSI_SCAN_BUS_REVERSE
	for (BusNum = MaxBusNum; BusNum >= 0; BusNum--) {
#else
	for (BusNum = 0; BusNum <= MaxBusNum; BusNum++) {
#endif
		for (Device = 0; Device < CFG_MAX_PCI_DEVICES; Device++) {
	        BusDevFunc = (BusNum << 16) | (Device << 11);

			if (PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_VENDOR_ID, 2) == VendorID
			 && PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_DEVICE_ID, 2) == DeviceID) {
				return (BusDevFunc);
			}
		}
	}
	return (-1);
}


#if (CONFIG_COMMANDS & CFG_CMD_PCI)
/*
 * Follows routines for the output of infos about devices on PCI bus.
 */

/*
 * Subroutine:  do_pciinfo
 *
 * Description: Handler for 'pciinfo' command..
 *
 * Inputs:		argv[1] may contain the number of the bus to be scanned.
 *				Default is bus 0.
 *
 * Return:      None
 *
 */
void do_pciinfo(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[])
{
	char *s;
	int	bus_no = 0;

	if (argc == 2)
	{
		bus_no = (int)simple_strtoul(argv[1], NULL, 10);
	}

	if ((s = getenv("pci_listing")) != NULL)
		ShortPCIListing = (strcmp(s, "short") == 0) ? 1 : 0;
	else
		ShortPCIListing = 0;

	pciinfo(bus_no);
}


/*
 * Subroutine:  pciinfo
 *
 * Description: Show information about devices on PCI bus.
 *				Depending on the define CFG_SHORT_PCI_LISTING
 *				the output will be more or less exhaustive.
 *
 * Inputs:		bus_no		the number of the bus to be scanned.
 *
 * Return:      None
 *
 */
void pciinfo(int BusNum)
{
	int Device;
	int Function;
	unsigned int HeaderType;
	unsigned int VendorID;
	int BusDevFunc;

	printf("Scanning PCI devices on bus %d\n", BusNum);

	if (ShortPCIListing) {
		printf("No.   VendorId   DeviceId   Device Class       Sub-Class\n");
		printf("________________________________________________________\n");
	}

	for (Device = 0; Device < CFG_MAX_PCI_DEVICES; Device++) {
		HeaderType = 0;
		VendorID = 0;
		for (Function = 0; Function < CFG_MAX_PCI_FUNCTIONS; Function++) {
			/*
			 * If this is not a multi-function device, we skip the rest.
			 */
			if (Function && !(HeaderType & 0x80))
				break;

			BusDevFunc = (BusNum << 16) |
				     (Device << 11) |
				     (Function << 8);

			VendorID = PCI_Read_CFG_Reg (BusDevFunc, PCI_CFG_VENDOR_ID, 2);
			if ((VendorID == 0xFFFF) || (VendorID == 0x0000))
				continue;

			HeaderType = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_HEADER_TYPE, 1);

			if (ShortPCIListing)
				printf("%02d.%d  ", Device, Function);
			else
   	    		printf("\nFound PCI device %d, function %d:\n",
				Device, Function);

			PCI_Header_Show(BusDevFunc);
	    }
    }
}


/*
 * Subroutine:  PCI_Header_Show
 *
 * Description: Reads the header of the specified PCI device.
 *
 * Inputs:		BusDevFunc      Bus+Device+Function number
 *
 * Return:      None
 *
 */
void PCI_Header_Show(int BusDevFunc)
{
    PCI_HEADER_DEVICE headerDevice;
    PCI_HEADER_BRIDGE headerBridge;
    PCI_HEADER_DEVICE * pD = &headerDevice;
    PCI_HEADER_BRIDGE * pB = &headerBridge;

    pD->headerType = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_HEADER_TYPE, 1);

    if (pD->headerType & 0x01) {		/* PCI-to-PCI bridge */
	pB->vendorId      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_VENDOR_ID, 2);
    	pB->deviceId      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_DEVICE_ID, 2);
    	pB->command       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_COMMAND, 2);
    	pB->status        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_STATUS, 2);
    	pB->revisionId    = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_REVISION, 1);
    	pB->progIf        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_PROGRAMMING_IF, 1);
    	pB->subClass      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_SUBCLASS, 1);
    	pB->classCode     = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_CLASS, 1);
    	pB->cacheLine     = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_CACHE_LINE_SIZE, 1);
    	pB->latency       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_LATENCY_TIMER, 1);
    	pB->headerType    = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_HEADER_TYPE, 1);
    	pB->bist          = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BIST, 1);
    	pB->base0         = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BASE_ADDRESS_0, 4);
    	pB->base1         = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BASE_ADDRESS_1, 4);
    	pB->priBus        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_PRIMARY_BUS, 1);
    	pB->secBus        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_SECONDARY_BUS, 1);
    	pB->subBus        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_SUBORDINATE_BUS, 1);
    	pB->secLatency    = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_SEC_LATENCY, 1);
    	pB->ioBase        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_IO_BASE, 1);
    	pB->ioLimit       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_IO_LIMIT, 1);
    	pB->secStatus     = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_SEC_STATUS, 2);
    	pB->memBase       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_MEM_BASE, 2);
    	pB->memLimit      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_MEM_LIMIT, 2);
    	pB->preBase       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_PRE_MEM_BASE, 2);
    	pB->preLimit      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_PRE_MEM_LIMIT, 2);
    	pB->preBaseUpper  = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_PRE_MEM_BASE_U, 4);
    	pB->preLimitUpper = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_PRE_MEM_LIMIT_U, 4);
    	pB->ioBaseUpper   = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_IO_BASE_U, 2);
    	pB->ioLimitUpper  = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_IO_LIMIT_U, 2);
    	pB->romBase       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_ROM_BASE, 4);
    	pB->intLine       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BRG_INT_LINE, 1);
    	pB->intPin        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BRG_INT_PIN, 1);
    	pB->control       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BRIDGE_CONTROL, 2);
    	PCI_Bheader_Print(pB);
    } else {					/* PCI device */
    	pD->vendorId      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_VENDOR_ID, 2);
    	pD->deviceId      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_DEVICE_ID, 2);
		pD->command       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_COMMAND, 2);
    	pD->status        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_STATUS, 1);
    	pD->revisionId    = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_REVISION, 1);
    	pD->progIf        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_PROGRAMMING_IF, 1);
    	pD->subClass      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_SUBCLASS, 1);
    	pD->classCode     = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_CLASS, 1);
    	pD->cacheLine     = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_CACHE_LINE_SIZE, 1);
    	pD->latency       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_LATENCY_TIMER, 1);
    	pD->headerType    = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_HEADER_TYPE, 1);
    	pD->bist          = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BIST, 1);
    	pD->base0         = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BASE_ADDRESS_0, 4);
    	pD->base1         = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BASE_ADDRESS_1, 4);
    	pD->base2         = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BASE_ADDRESS_2, 4);
    	pD->base3         = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BASE_ADDRESS_3, 4);
	pD->base4         = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BASE_ADDRESS_4, 4);
	pD->base5         = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_BASE_ADDRESS_5, 4);
	pD->cis           = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_CIS, 4);
	pD->subVendorId   = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_SUB_VENDER_ID, 2);
	pD->subSystemId   = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_SUB_SYSTEM_ID, 2);
	pD->romBase       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_EXPANSION_ROM, 4);
	pD->intLine       = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_DEV_INT_LINE, 1);
	pD->intPin        = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_DEV_INT_PIN, 1);
	pD->minGrant      = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_MIN_GRANT, 1);
	pD->maxLatency    = PCI_Read_CFG_Reg(BusDevFunc, PCI_CFG_MAX_LATENCY, 1);
	PCI_Dheader_Print(pD);
    }
}

static char *PCI_classes[] = {
	"Build before PCI Rev2.0",
	"Mass storage controller",
	"Network controller     ",
	"Display controller     ",
	"Multimedia device      ",
	"Memory controller      ",
	"Bridge device          ",
	"Simple comm. controller",
	"Base system peripheral ",
	"Input device           ",
	"Docking station        ",
	"Processor              ",
	"Serial bus controller  ",
	"Reserved entry         ",
	"Does not fit any class "
};

/*
 * Subroutine:  PCI_Dheader_Print
 *
 * Description: Prints the header of the specified PCI device.
 *
 * Inputs:		pD      pointer to device header info
 *
 * Return:      None
 *
 */
void PCI_Dheader_Print(PCI_HEADER_DEVICE * pD)
{
    if (ShortPCIListing) {
	printf("0x%.4x     0x%.4x     %s 0x%.2x\n",
		(ushort)pD->vendorId,
		(ushort)pD->deviceId,
		PCI_classes[(uchar)pD->classCode],
		(uchar)pD->subClass);
    } else {
	printf("  vendor ID =                   0x%.4x\n", (ushort)pD->vendorId);
	printf("  device ID =                   0x%.4x\n", (ushort)pD->deviceId);
	printf("  command register =            0x%.4x\n", (ushort)pD->command);
	printf("  status register =             0x%.4x\n", (ushort)pD->status);
	printf("  revision ID =                 0x%.2x\n", (uchar)pD->revisionId);
	printf("  class code =                  0x%.2x (%s)\n",
		(uchar)pD->classCode,
		PCI_classes[(int)pD->classCode]);
	printf("  sub class code =              0x%.2x\n", (uchar)pD->subClass);
	printf("  programming interface =       0x%.2x\n", (uchar)pD->progIf);
	printf("  cache line =                  0x%.2x\n", (uchar)pD->cacheLine);
	printf("  latency time =                0x%.2x\n", (uchar)pD->latency);
	printf("  header type =                 0x%.2x\n", (uchar)pD->headerType);
	printf("  BIST =                        0x%.2x\n", (uchar)pD->bist);
	printf("  base address 0 =              0x%.8x\n", pD->base0);
	printf("  base address 1 =              0x%.8x\n", pD->base1);
	printf("  base address 2 =              0x%.8x\n", pD->base2);
	printf("  base address 3 =              0x%.8x\n", pD->base3);
	printf("  base address 4 =              0x%.8x\n", pD->base4);
	printf("  base address 5 =              0x%.8x\n", pD->base5);
	printf("  cardBus CIS pointer =         0x%.8x\n", pD->cis);
	printf("  sub system vendor ID =        0x%.4x\n", (ushort)pD->subVendorId);
	printf("  sub system ID =               0x%.4x\n", (ushort)pD->subSystemId);
	printf("  expansion ROM base address =  0x%.8x\n", pD->romBase);
	printf("  interrupt line =              0x%.2x\n", (uchar)pD->intLine);
	printf("  interrupt pin =               0x%.2x\n", (uchar)pD->intPin);
	printf("  min Grant =                   0x%.2x\n", (uchar)pD->minGrant);
	printf("  max Latency =                 0x%.2x\n", (uchar)pD->maxLatency);
    }
}

/*
 * Subroutine:  PCI_Bheader_Print
 *
 * Description: Prints the header of the specified PCI-to-PCI bridge.
 *
 * Inputs:		pB      pointer to bridge header info
 *
 * Return:      None
 *
 */
void PCI_Bheader_Print (PCI_HEADER_BRIDGE * pB)
{
     if (ShortPCIListing) {
	printf("0x%.4x     0x%.4x     %s 0x%.2x\n",
		(ushort)pB->vendorId,
		(ushort)pB->deviceId,
		PCI_classes[(uchar)pB->classCode],
		(uchar)pB->subClass);
     } else {
	printf ("  vendor ID =                   0x%.4x\n", (ushort)pB->vendorId);
	printf ("  device ID =                   0x%.4x\n", (ushort)pB->deviceId);
	printf ("  command register =            0x%.4x\n", (ushort)pB->command);
	printf ("  status register =             0x%.4x\n", (ushort)pB->status);
	printf ("  revision ID =                 0x%.2x\n", (uchar)pB->revisionId);
	printf ("  class code =                  0x%.2x (%s)\n",
		(uchar)pB->classCode,
		PCI_classes[(int)pB->classCode]);
	printf ("  sub class code =              0x%.2x\n", (uchar)pB->subClass);
	printf ("  programming interface =       0x%.2x\n", (uchar)pB->progIf);
	printf ("  cache line =                  0x%.2x\n", (uchar)pB->cacheLine);
	printf ("  latency time =                0x%.2x\n", (uchar)pB->latency);
	printf ("  header type =                 0x%.2x\n", (uchar)pB->headerType);
	printf ("  BIST =                        0x%.2x\n", (uchar)pB->bist);
	printf ("  base address 0 =              0x%.8x\n", pB->base0);
	printf ("  base address 1 =              0x%.8x\n", pB->base1);
	printf ("  primary bus number =          0x%.2x\n", (uchar)pB->priBus);
	printf ("  secondary bus number =        0x%.2x\n", (uchar)pB->secBus);
	printf ("  subordinate bus number =      0x%.2x\n", (uchar)pB->subBus);
	printf ("  secondary latency timer =     0x%.2x\n", (uchar)pB->secLatency);
	printf ("  IO base =                     0x%.2x\n", (uchar)pB->ioBase);
	printf ("  IO limit =                    0x%.2x\n", (uchar)pB->ioLimit);
	printf ("  secondary status =            0x%.4x\n", (ushort)pB->secStatus);
	printf ("  memory base =                 0x%.4x\n", (ushort)pB->memBase);
	printf ("  memory limit =                0x%.4x\n", (ushort)pB->memLimit);
	printf ("  prefetch memory base =        0x%.4x\n", (ushort)pB->preBase);
	printf ("  prefetch memory limit =       0x%.4x\n", (ushort)pB->preLimit);
	printf ("  prefetch memory base upper =  0x%.8x\n", pB->preBaseUpper);
	printf ("  prefetch memory limit upper = 0x%.8x\n", pB->preLimitUpper);
	printf ("  IO base upper 16 bits =       0x%.4x\n", (ushort)pB->ioBaseUpper);
	printf ("  IO limit upper 16 bits =      0x%.4x\n", (ushort)pB->ioLimitUpper);
	printf ("  expansion ROM base address =  0x%.8x\n", pB->romBase);
	printf ("  interrupt line =              0x%.2x\n", (uchar)pB->intLine);
	printf ("  interrupt pin =               0x%.2x\n", (uchar)pB->intPin);
	printf ("  bridge control =              0x%.4x\n", (ushort)pB->control);
     }
}

#endif /* CONFIG_COMMANDS & CFG_CMD_PCI */

#endif /* CONFIG_PCI */
