/*
 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 2.0 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#ifndef _IOPCICONFIGURATOR_H
#define _IOPCICONFIGURATOR_H

#include <IOKit/IOLib.h>
#include <IOKit/pci/IOPCIBridge.h>


#define kPCIBridgeIOAlignment        (4096)
#define kPCIBridgeMemoryAlignment    (1024*1024)
#define kPCIBridgeBusNumberAlignment (1)

#define BRIDGE_RANGE_NUM(type)      ((type) + kIOPCIRangeBridgeMemory)

#define FOREACH_CHILD(bridge, child) \
    for(pci_dev_t (child) = (bridge)->child; (child); (child) = (child)->peer)

#define PCI_ADDRESS_TUPLE(device)   \
        device->space.s.busNum,     \
        device->space.s.deviceNum,  \
        device->space.s.functionNum

enum {
    kIOPCIConfiguratorEnable   = 0x00000001,
    kIOPCIConfiguratorAllocate = 0x00000002,
    kIOPCIConfiguratorIOLog    = 0x00000004,
    kIOPCIConfiguratorKPrintf  = 0x00000008
};


enum {
    kIOPCIRangeFlagMaximizeSize = 0x00000001,
};
enum {
    kIOPCIRangeBAR0               = 0,
    kIOPCIRangeBAR1               = 1,
    kIOPCIRangeBAR2               = 2,
    kIOPCIRangeBAR3               = 3,
    kIOPCIRangeBAR4               = 4,
    kIOPCIRangeBAR5               = 5,
    kIOPCIRangeExpansionROM       = 6,

    // order matches kIOPCIResourceType*
    kIOPCIRangeBridgeMemory       = 7,
    kIOPCIRangeBridgePFMemory     = 8,
    kIOPCIRangeBridgeIO           = 9,
    kIOPCIRangeBridgeBusNumber    = 10,

    kIOPCIRangeCount
};

enum {
    kPCIDeviceStateBIOSConfig = 0,
    kPCIDeviceStateResourceWait,
    kPCIDeviceStateResourceAssigned,
    kPCIDeviceStateConfigurationDone
};

/*
 * PCI device struct (including bridges)
 */
struct pci_dev
{
    struct pci_dev *    child;
    struct pci_dev *    peer;
    UInt32              classCode;
    IOPCIAddressSpace   space;
    IOPCIRange		ranges[kIOPCIRangeCount];
    UInt8               headerType;
    UInt8		isBridge;
    UInt8		isHostBridge;
    UInt8		supportsHotPlug;
    UInt8               deviceState;
    UInt8               secBusNum;  // bridge only
    UInt8               subBusNum;  // bridge only
    IORegistryEntry *   dtNub;
    IORegistryEntry *   acpiDevice;
};

typedef struct pci_dev * pci_dev_t;

enum {
    kPCIHeaderType0 = 0,
    kPCIHeaderType1 = 1,
    kPCIHeaderType2 = 2
};

enum {
    kPCI2PCIPrimaryBus          = 0x18,
    kPCI2PCISecondaryBus        = 0x19,
    kPCI2PCISubordinateBus      = 0x1a,
    kPCI2PCISecondaryLT         = 0x1b,
    kPCI2PCIIORange             = 0x1c,
    kPCI2PCIMemoryRange         = 0x20,
    kPCI2PCIPrefetchMemoryRange = 0x24,
    kPCI2PCIPrefetchUpperBase   = 0x28,
    kPCI2PCIPrefetchUpperLimit  = 0x2c,
    kPCI2PCIUpperIORange        = 0x30,
    kPCI2PCIBridgeControl       = 0x3e
};

#define kPCIBridgeMaxCount  256


class IOPCIConfigurator : public IOService
{
    OSDeclareDefaultStructors( IOPCIConfigurator );

    IOOptionBits	    fFlags;
    IOPCIBridge *	    fRootBridge;
    UInt8                   fCacheLineSize;
    pci_dev_t               fPCIBridgeList[kPCIBridgeMaxCount];
    int                     fPCIBridgeIndex;
    int                     fPCIBridgeTailIndex;
    int                     fBridgeConfigCount;
    int                     fDeviceConfigCount;
    int                     fYentaConfigCount;

protected:

    static void safeProbeCallback( void * refcon );
    static void configProbeCallback( void * refcon );

    static void matchDTEntry( IORegistryEntry * dtEntry, void * _context );
    static void matchACPIEntry( IORegistryEntry * dtEntry, void * _context );

    void    checkPCIConfiguration( void );
    void    pciBridgeScanBus( pci_dev_t bridge, 
			                  UInt8 busNum, UInt8 * nextBusNum, UInt8 lastBusNum );
    void    pciRangeAppendSubRange( IOPCIRange * headRange, IOPCIRange * newRange );
    void    pciBridgeCheckConfiguration( pci_dev_t bridge );
    void    pciBridgeClipRanges( IOPCIRange * rangeList, 
                                 IOPCIScalar start, IOPCIScalar size );
    void    pciBridgeAllocateResource( pci_dev_t bridge );
    void    pciBridgeDistributeResource( pci_dev_t bridge );
    void    pciBridgeDistributeResourceType( pci_dev_t bridge, UInt32 type );
    void    pciApplyConfiguration( pci_dev_t device );
    void    pciDeviceApplyConfiguration( pci_dev_t device );
    void    pciBridgeApplyConfiguration( pci_dev_t bridge );
    UInt16  pciDisableAccess( pci_dev_t device );
    void    pciRestoreAccess( pci_dev_t device, UInt16 command );
    void    pciBridgeAddChild( pci_dev_t bridge, pci_dev_t child );
    void    pciBridgeProbeChild( pci_dev_t bridge, IOPCIAddressSpace space );
    void    pciProbeBaseAddressRegister( pci_dev_t device, UInt32 lastBarNum );
    void    pciSafeProbeBaseAddressRegister( pci_dev_t device, UInt32 lastBarNum );
    void    pciDeviceProbeRanges( pci_dev_t device );
    void    pciBridgeProbeRanges( pci_dev_t bridge );
    void    pciYentaProbeRanges( pci_dev_t yenta );
    void    pciBridgeProbeBusRange( pci_dev_t bridge );
    void    pciCheckCacheLineSize( pci_dev_t device );
    void    pciWriteLatencyTimer( pci_dev_t device );
    void    pciBridgeConnectDeviceTree( pci_dev_t bridge );
    void    pciBridgeConstructDeviceTree( pci_dev_t bridge );
    OSDictionary * constructProperties( pci_dev_t device );
    void           constructAddressingProperties( pci_dev_t device, OSDictionary * propTable );

public:

    virtual bool    start( IOService * provider );
    virtual void    free( void );

    virtual bool    createRoot( IOService * provider );

    virtual UInt32  configRead32( IOPCIAddressSpace space, UInt32 offset );
    virtual UInt16  configRead16( IOPCIAddressSpace space, UInt32 offset );
    virtual UInt8   configRead8(  IOPCIAddressSpace space, UInt32 offset );
    virtual void    configWrite32( IOPCIAddressSpace space,
                           UInt32 offset, UInt32 data );
    virtual void    configWrite16( IOPCIAddressSpace space,
                           UInt32 offset, UInt16 data );
    virtual void    configWrite8(  IOPCIAddressSpace space,
                           UInt32 offset, UInt8 data );
};

#if 0
class IOPCI2PCIConfigurator : public IOPCIConfigurator
{
    OSDeclareDefaultStructors( IOPCI2PCIConfigurator );

    IOPCIBridge *  fRootPCIBridge;

public:

    virtual bool    start( IOService * provider );
    virtual void    free( void );

    virtual bool    createRoot( IOService * provider );

    virtual UInt32  configRead32( IOPCIAddressSpace space, UInt32 offset );
    virtual UInt16  configRead16( IOPCIAddressSpace space, UInt32 offset );
    virtual UInt8   configRead8(  IOPCIAddressSpace space, UInt32 offset );
    virtual void    configWrite32( IOPCIAddressSpace space,
                           UInt32 offset, UInt32 data );
    virtual void    configWrite16( IOPCIAddressSpace space,
                           UInt32 offset, UInt16 data );
    virtual void    configWrite8(  IOPCIAddressSpace space,
                           UInt32 offset, UInt8 data );
};
#endif

#endif /* !_IOPCICONFIGURATOR_H */
