navienginebsp/naviengine_assp/pci/pci.cpp
changeset 0 5de814552237
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/navienginebsp/naviengine_assp/pci/pci.cpp	Tue Sep 28 18:00:05 2010 +0100
@@ -0,0 +1,519 @@
+/*
+* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  
+*
+*/
+
+
+
+#include <pci.h>
+#include <naviengine.h>
+#include <naviengine_priv.h>
+
+#include "pci_priv.h"
+
+//
+// TAddrSpace class
+//
+
+TAddrSpace::TAddrSpace(TUint aSize, const TDesC& aName)
+	:iSize(aSize), iName(aName)
+	{
+	}
+
+EXPORT_C TUint TAddrSpace::Size()
+	{
+	return iSize;
+	}
+
+/**
+Check access is aligned and in range
+*/
+void TAddrSpace::CheckAccess(TNumberOfBytes aNumberOfBytes, TUint aByteOffset)
+	{
+	if(aByteOffset > iSize-aNumberOfBytes)
+		{
+		Kern::Printf("PCI %S: Access out of range", &iName);
+		FAULT();
+		}
+	if(aByteOffset%aNumberOfBytes)
+		{
+		Kern::Printf("PCI %S: Access missaligned", &iName);
+		FAULT();
+		}
+	}
+
+//
+// DPciBridge class
+//
+DPciBridge::DPciBridge()
+	{
+	}
+
+DPciBridge::~DPciBridge()
+	{
+	}
+
+
+/**
+Register this bridge with the system wide Pci manager.
+*/
+TInt DPciBridge::Register()
+	{
+	return Pci::AddBridge(this);
+	}
+
+
+//
+// Pci class
+//
+
+TBool Pci::iEnumerated = EFalse;
+
+RPointerArray<DPciBridge> Pci::iPciBridges(2);
+
+/**
+Search all PCI busses for PCI functions matching the specified vendor and
+device ids.
+
+@param aVid Vendor Id
+@param aDid Device Id
+@param aListOfFunctions On KErrNone will be filled with indices of matching PCI functions
+@return
+	- KErrNone - Matching functions were found
+	- KErrNotFound - No matches were found.
+	- KErrArgument - aListOfFunctions was not empty at start.
+*/
+EXPORT_C TInt Pci::Probe(RArray<TInt>& aListOfFunctions, TPciVendorId aVid, TDeviceId aDid)
+	{
+	if(aListOfFunctions.Count()>0)
+		return KErrArgument;
+
+	const TInt functionCount=iFunctions.Count();
+	TInt r=KErrNotFound;
+	for(TInt i=0; i<functionCount;++i)
+		{
+		TPciFunction* func=iFunctions[i];
+		if(func->VendorId()==aVid && func->DeviceId()==aDid)
+			{
+			r = aListOfFunctions.Append(i);
+			if(r!=KErrNone)
+				return r; //some error when appending
+			}
+		}
+
+	if(aListOfFunctions.Count()>0)
+		r=KErrNone;
+
+	return r;
+	}
+
+/**
+Gets a pointer to one of the specified function's memory
+spaces. A function may have a memory space for each of its
+6 BARS. If either of the supplied indices are invalid NULL
+will be returned.
+
+@return NULL if error, otherwise a pointer the requested memory space.
+@param aFunction Index of the PCI function.
+@param aBarIndex Index from 0 to 5
+*/
+EXPORT_C TAddrSpace* Pci::GetMemorySpace(TInt aFunction, TInt aBarIndex)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return NULL;
+	return iFunctions[aFunction]->GetMemorySpace(aBarIndex);
+	}
+
+/**
+Get a pointer to the specified function's configuration space.
+
+@return NULL if aFunction is invalid or some other error
+@param aFunction Index of the PCI function.
+*/
+EXPORT_C TAddrSpace* Pci::GetConfigSpace(TInt aFunction)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return NULL;
+
+	return iFunctions[aFunction]->GetConfigSpace();
+	}
+
+/**
+Create a DMA'able DPlatChunkHw chunk with memory allocated which will be accessable by the PCI function aFunction (and other PCI devices
+on the same bridge). The allocated chunk must only be destroyed with Pci::RemoveChunk. For simplicity
+it is recommended to use the alternate CreateChunk to create a DChunk, since this will be a able to clean itself up automatically on
+closure.
+@return
+	- KErrNone on success
+	- KErrInUse - aChunk was not NULL
+	- KErrNotFound - All of Bridge's BARS have been used up. Free one with RemoveChunk
+	- KErrArgument - aSize was less than 1.
+@param aFunction Index of the PCI function.
+@param aChunk Must be NULL, on success will point to the created chunk.
+@param aSize of chunk required.
+This will be rounded up to the next power of 2 in kilobytes
+.eg 1K, 2K, 4K etc. So requesting a 600Kb buffer will result in 1Mb being allocated.
+@param aAttributes Attribute mask made up of OR'd values in TMappingAttributes
+@param aPciAddress On success will be set to the PCI address with which devices may access the allocated memory.
+*/
+EXPORT_C TInt Pci::CreateChunk(TInt aFunction, DPlatChunkHw*& aChunk, TInt aSize, TUint aAttributes, TUint32& aPciAddress)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return KErrArgument;
+
+	return iFunctions[aFunction]->GetBridge().CreateChunk(aChunk, aSize, aAttributes, aPciAddress);
+	}
+
+/**
+Create a DMA'able chunk with memory allocated which will be accessable by the PCI function aFunction (and other PCI devices
+on the same bridge). When the final reference to the chunk is closed it will be unmapped from PCI memory and free its physical ram.
+@return
+	- KErrNone on success
+	- KErrInUse - aChunk was not NULL
+	- KErrNotFound - All of Bridge's BARS have been used up. Free one by calling RemoveMapping, RemoveChunk or closing all
+	  references to a DChunk previously allocated with this function.
+	- KErrArgument - aSize was less than 1.
+@param aFunction Index of the PCI function.
+@param aChunk Must be NULL, on success will point to the created chunk.
+@param aAttributes Contains creation parameters for DChunk. These may be adjusted by the driver and can be viewed on return
+@param aOffset The number of bytes into the chunk's virtual address range at which memory should be commited.
+@param aSize of chunk required.
+This will be rounded up to the next power of 2 in kilobytes
+.eg 1K, 2K, 4K etc. So requesting a 600Kb buffer will result in 1Mb being allocated. 
+@param aPciAddress On success will be set to the PCI address with which devices may access the allocated memory.
+*/
+EXPORT_C TInt Pci::CreateChunk(TInt aFunction, DChunk*& aChunk, TChunkCreateInfo &aAttributes, TUint aOffset, TUint aSize, TUint32& aPciAddress)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return KErrArgument;
+
+	return iFunctions[aFunction]->GetBridge().CreateChunk(aChunk, aAttributes, aOffset, aSize, aPciAddress);
+	}
+
+RPointerArray<TPciFunction> Pci::iFunctions;
+
+
+/**
+Remove mapping from PCI memory space to chunk.
+Free the memory chunk's memory and chunk object itself.
+
+@param aFunction The PCI function wth which the chunk was associated.
+@param aChunk Pointer to the chunk to be deleted
+@return
+	- KErrNone on success
+	- KErrArgument aFunction was invalid
+	- KErrNotFound aChunk was not found
+*/
+EXPORT_C TInt Pci::RemoveChunk(TInt aFunction, DPlatChunkHw* aChunk)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return KErrArgument;
+
+	return iFunctions[aFunction]->GetBridge().RemoveChunk(aChunk);
+	}
+
+EXPORT_C void Pci::ChunkCleanupCallback(TChunkCleanup* aCleanup)
+	{
+	__KTRACE_OPT(KPCI, Kern::Printf("Pci::ChunkCleanupCallback aCleanup = 0x%08x", aCleanup));
+	aCleanup->Destroy();
+	delete aCleanup;	
+	}
+
+EXPORT_C TInt Pci::CreateMapping(TInt aFunction, TUint32 aPhysicalAddress, TInt aSize, TUint32& aPciAddress)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return KErrArgument;
+	__KTRACE_OPT(KPCI, Kern::Printf("Pci::CreateMapping() requested 0x%X bytes", aSize));
+	return iFunctions[aFunction]->GetBridge().CreateMapping(aPhysicalAddress, aSize, aPciAddress);
+	}
+
+EXPORT_C TInt Pci::RemoveMapping(TInt aFunction, TUint32 aPhysicalAddress)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return KErrArgument;
+
+	return iFunctions[aFunction]->GetBridge().RemoveMapping(aPhysicalAddress);
+	}
+
+/**
+Returns the PCI address corresponding to a given physical address.
+@param aFunction
+@param aAddress The physical address, on success, will have been converted to the
+equivilant PCI address.
+@return
+	- KErrNone
+	- KErrNotFound The physical address has no corresponding PCI address
+*/
+EXPORT_C TInt Pci::GetPciAddress(TInt aFunction, TUint32& aAddress)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return KErrArgument;
+
+	return iFunctions[aFunction]->GetBridge().GetPciAddress(aAddress, aAddress);
+	}
+
+/**
+Returns the physical address corresponding to a given PCI address.
+@param aFunction
+@param aAddress The PCI address, on success, will have been converted to the
+equivilant physical address.
+@return
+	- KErrNone
+	- KErrNotFound The PCI address has no corresponding physical address
+*/
+EXPORT_C TInt Pci::GetPhysicalAddress(TInt aFunction, TUint32& aAddress)
+	{
+	if(!Rng(0, aFunction, iFunctions.Count()-1 ))
+		return KErrArgument;
+
+	return iFunctions[aFunction]->GetBridge().GetPhysicalAddress(aAddress,aAddress);
+	}
+
+
+/**
+Add bridges before calling Enumerate.
+
+@param aBridge A constructed bridge object.
+@return An error value
+*/
+TInt Pci::AddBridge(DPciBridge* aBridge)
+	{
+	return iPciBridges.Append(aBridge);
+	}
+
+/**
+Query all host bridges on the system for their PCI functions and
+populate global function list.
+
+@return
+	- KErrNone - Success
+	- KErrNotSupported - No bridges have been registered
+	- KErrAccessDenied - Enumerate has already been called
+*/
+TInt Pci::Enumerate()
+	{
+	__KTRACE_OPT(KPCI, Kern::Printf("Pci::Enumerate"));
+
+	//only call once, from the extension
+	if(iEnumerated)
+		return KErrAccessDenied;
+	iEnumerated = ETrue;
+
+	const TInt numberOfPciBridges= iPciBridges.Count();
+	if(0 == numberOfPciBridges)
+		return KErrNotSupported;
+
+	TInt r=KErrNone;
+	for(TInt bridge=0; bridge<numberOfPciBridges; ++bridge)
+		{
+		for(TInt bus=0; bus<KPciMaxBusses; ++bus)
+			{
+			for(TInt device=0; device<KPciMaxDevices; ++device)
+				{
+				for(TInt function=0; function<KPciMaxFunctions; ++function)
+					{
+					TPciFunction* func = iPciBridges[bridge]->Function(bus, device, function);
+					if(func==NULL)
+						continue;
+					if(func->IsBridge())
+						{
+						//will call bridge fix up code here when supported.
+						//don't add a bridge into global list as we do not want to
+						//grant access to it to the client programmer.
+						__KTRACE_OPT(KPCI, Kern::Printf(  "PCI-PCI bridge unsupported"));
+						delete func;
+						func = NULL;
+						continue;
+						}
+					else
+						{
+						r=iFunctions.Append(func);
+						__KTRACE_OPT(KPCI, Kern::Printf("  Adding function %d:%d:%d:%d, vid=0x%04x, did=0x%04x"
+									, bridge, bus, device, function, func->VendorId(), func->DeviceId()));
+						}
+					if(r!=KErrNone)
+						return r;
+
+					//a multifunction device can indicate the last function
+					//implemented by setting its multi function bit to 0
+					//so this is checked for all function numbers
+					if(!func->IsMultiFunc())
+						{
+						break;
+						}
+					}
+				}
+			}
+		iPciBridges[bridge]->ConfigurationComplete();
+		}
+	return r;
+	}
+
+TPciFunction::TPciFunction(TInt aBus, TInt aDevice, TInt aFunction,
+		TPciVendorId aVid, TDeviceId aDid, DPciBridge& aBridge)
+	:iBus(aBus), iDevice(aDevice), iFunction(aFunction), iVid(aVid), iDid(aDid),
+		iBridge(aBridge), iConfigSpace(NULL), iMemorySpaces(KPciNumberOfBars),
+		iIsBridge(EFalse), iIsMultiFunc(EFalse)
+	{
+	__ASSERT_DEBUG(iBus<256, Kern::Printf("TConfigSpace: Bus id too big"));
+	__ASSERT_DEBUG(iDevice<32, Kern::Printf("TConfigSpace: Device id too big"));
+	__ASSERT_DEBUG(iFunction<8, Kern::Printf("TConfigSpace: Function id too big"));
+
+	//Array has an element for each of this PCI function's BARs
+	//but an unused one will be NULL
+	for(TInt i=0; i<KPciNumberOfBars;++i)
+		{
+		TInt r=iMemorySpaces.Append(NULL);
+		__NK_ASSERT_ALWAYS(KErrNone==r);
+		}
+
+	iConfigSpace = new TConfigSpace(*this, iBridge);
+	__NK_ASSERT_ALWAYS(iConfigSpace!=NULL);
+
+	const TUint8 headerTypeByte = iConfigSpace->Read8(KPciHeaderType);
+	iIsMultiFunc = (headerTypeByte & HeaderType::KHtMultiFunction);
+
+	const TInt headerType = headerTypeByte & (~HeaderType::KHtMultiFunction);
+	_LIT(KHeaderError, "Unknown PCI header type");
+	switch(headerType)
+		{
+	case 0:
+		iIsBridge=EFalse; break;
+	case 1:
+		iIsBridge=ETrue; break;
+	default:
+		Kern::PanicCurrentThread(KHeaderError, 0);
+		}
+	}
+
+TPciFunction::~TPciFunction()
+	{
+	iMemorySpaces.ResetAndDestroy();
+
+	delete iConfigSpace;
+	}
+
+/**
+@param aBarIndex The Bar which this memory space is asssociated with
+*/
+TInt TPciFunction::AddMemorySpace(TUint32 aSize, TUint32 aAddress, TInt aBarIndex)
+	{
+	TMemorySpace* space=new TMemorySpace(aAddress, aSize);
+	if(NULL==space)
+		{
+		return KErrNoMemory;
+		}
+	else
+		{
+		iMemorySpaces[aBarIndex]=space;
+		return KErrNone;
+		}
+	}
+
+EXPORT_C TAddrSpace* TPciFunction::GetConfigSpace()
+	{
+	return iConfigSpace;
+	}
+
+EXPORT_C TAddrSpace* TPciFunction::GetMemorySpace(TInt aBar)
+	{
+	if(!Rng(0,aBar, iMemorySpaces.Count()-1))
+		return NULL;
+
+	return iMemorySpaces[aBar];
+	}
+//
+// TMemory Space
+//
+
+#define READ_TRACE(N) __KTRACE_OPT(KPCI, Kern::Printf( "TMemorySpace::READ" #N " Offset=0x%x, Absolute=0x%x (Base=0x%x), Value=0x%x",\
+			aOffset, iBaseAddress+aOffset, iBaseAddress, value ))
+
+#define WRITE_TRACE(N) __KTRACE_OPT(KPCI, Kern::Printf( "TMemorySpace::Write" #N " Offset=0x%x, Absolute=0x%x (Base=0x%x), Value=0x%x",\
+			aOffset, iBaseAddress+aOffset, iBaseAddress, aValue ))
+
+#define MOD_TRACE(N) __KTRACE_OPT(KPCI, Kern::Printf( "TMemorySpace::Modify" #N " Offset=0x%x, Absolute=0x%x (Base=0x%x), ClearMask=0x%x, SetMask=0x%x",\
+			aOffset, iBaseAddress+aOffset, iBaseAddress, aClearMask, aSetMask ))
+
+_LIT(KTMemorySpace, "TMemorySpace");
+TMemorySpace::TMemorySpace(TUint32 aBaseAddress, TUint aSize)
+	:TAddrSpace(aSize, KTMemorySpace), iBaseAddress(aBaseAddress)
+	{
+	}
+
+TUint32 TMemorySpace::Read32(TUint32 aOffset)
+	{
+	CheckAccess(E4Byte, aOffset);
+	return AsspRegister::Read32(iBaseAddress+aOffset);
+	}
+
+void TMemorySpace::Write32(TUint32 aOffset, TUint32 aValue)
+	{
+	WRITE_TRACE(32);
+	CheckAccess(E4Byte, aOffset);
+	AsspRegister::Write32(iBaseAddress+aOffset, aValue);
+	}
+
+void TMemorySpace::Modify32(TUint32 aOffset, TUint32 aClearMask, TUint32 aSetMask)
+	{
+	MOD_TRACE(32);
+	CheckAccess(E4Byte, aOffset);
+	AsspRegister::Modify32(iBaseAddress+aOffset, aClearMask, aSetMask);
+	}
+
+TUint16 TMemorySpace::Read16(TUint32 aOffset)
+	{
+	CheckAccess(E2Byte, aOffset);
+	return AsspRegister::Read16(iBaseAddress+aOffset);
+	}
+
+void TMemorySpace::Write16(TUint32 aOffset, TUint16 aValue)
+	{
+	WRITE_TRACE(16);
+	CheckAccess(E2Byte, aOffset);
+	AsspRegister::Write16(iBaseAddress+aOffset, aValue);
+	}
+
+void TMemorySpace::Modify16(TUint32 aOffset, TUint16 aClearMask, TUint16 aSetMask)
+	{
+	MOD_TRACE(16);
+	CheckAccess(E2Byte, aOffset);
+	AsspRegister::Modify16(iBaseAddress+aOffset, aClearMask, aSetMask);
+	}
+
+TUint8 TMemorySpace::Read8(TUint32 aOffset)
+	{
+	CheckAccess(E1Byte, aOffset);
+	return AsspRegister::Read8(iBaseAddress+aOffset);
+	}
+
+void TMemorySpace::Write8(TUint32 aOffset, TUint8 aValue)
+	{
+	WRITE_TRACE(8);
+	CheckAccess(E1Byte, aOffset);
+	AsspRegister::Write8(iBaseAddress+aOffset, aValue);
+	}
+
+void TMemorySpace::Modify8(TUint32 aOffset, TUint8 aClearMask, TUint8 aSetMask)
+	{
+	MOD_TRACE(8);
+	CheckAccess(E1Byte, aOffset);
+	AsspRegister::Modify8(iBaseAddress+aOffset, aClearMask, aSetMask);
+	}
+
+//
+// TChunkCleanup
+//
+