/*
 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
 * Andreas Heppel <aheppel@sysgo.de>
 *
 * (C) Copyright 2002
 * Wolfgang Denk, DENX Software Engineering, wd@denx.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
 */

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

#include "pci.h"
#include "hardware.h"

/*
 * 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    LowestMemAddr = PCI_LOWEST_MEMADDR;
unsigned long    LowestIOAddr  = PCI_LOWEST_IOADDR;

unsigned long    MaxBusNum = 0;

unsigned char	ShortPCIListing = 0;

/*
 * 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
	 */
	for (Device = 0; Device < PCI_MAX_PCI_DEVICES; Device++) {
		HeaderType = 0;
		VendorID = 0;
		for (Function = 0; Function < PCI_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 Function %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_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 = PCI_LOWEST_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 = PCI_LOWEST_MEMADDR;
			     AddrProg < LowestMemAddr;
			     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
			LowestMemAddr = 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 = PCI_LOWEST_MEMADDR;
		     AddrProg < LowestMemAddr;
		     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
		LowestMemAddr = 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 = LowestMemAddr;

	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 LowestMemAddr, 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 ( LowestMemAddr > 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 ((LowestMemAddr % 0x100000) != 0)
    	LowestMemAddr += 0x100000 - (LowestMemAddr % 0x100000);

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

	/*
	 * 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 = LowestMemAddr;
	if ( LowestMemAddr > 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 )
		LowestMemAddr = (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 < PCI_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.
 */

void pciinfo(int BusNum);

/*
 * 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 < PCI_MAX_PCI_DEVICES; Device++) {
		HeaderType = 0;
		VendorID = 0;
		for (Function = 0; Function < PCI_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 */
