omap3530/omap3530_drivers/gpio/gpio_interrupts.cpp
author Pat Downey <patd@symbian.org>
Thu, 15 Oct 2009 12:59:54 +0100
changeset 0 6663340f3fc9
permissions -rwxr-xr-x
Add EPL'd beagleboard code

// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// omap3530/omap3530_drivers/gpio/gpio_interrupts.cpp
//

#include <e32cmn.h>
#include <nk_priv.h>
#include <assp/omap3530_assp/omap3530_gpio.h>
#include <assp/omap3530_assp/omap3530_irqmap.h>
#include <assp/omap3530_assp/omap3530_ktrace.h>
#include <assp/omap3530_assp/omap3530_assp_priv.h>
#include <assp/omap3530_assp/locks.h>

NONSHARABLE_CLASS( TGpioDispatcher ) : public MInterruptDispatcher
	{
	public:
		TGpioDispatcher();

		TInt Init();

		virtual TInt Bind(TInt aId, TIsr aIsr, TAny* aPtr);
		virtual TInt Unbind(TInt aId);
		virtual TInt Enable(TInt aId);
		virtual TInt Disable(TInt aId);
		virtual TInt Clear(TInt aId);
		virtual TInt SetPriority(TInt aId, TInt aPriority);

	private:
		static void Spurious( TAny* aParam );
		static void DispatchIsr( TAny* aParam );

		static TInt GetGPIOPin( TInt aId,GpioPin *aPin );
		static TInt SetGPIOPin(TInt aId,GpioPin *aPin);

		static FORCE_INLINE TBool IsValidId( TInt aId );
	};

TSpinLock GPIOpinsDescLock(/*TSpinLock::EOrderGenericIrqLow0*/);
static GpioPin	GpioPins[KHwGpioPinMax];


TGpioDispatcher::TGpioDispatcher()
	{
	for (TInt32 i = 0; i < KHwGpioPinMax; i++)
		{
		GpioPins[i].iMode = GPIO::EIdle;
		GpioPins[i].irq.iIsr = Spurious;
		GpioPins[i].irq.iPtr = &GpioPins[i];	
		GpioPins[i].iBankAddr = GPIO_BASE_ADDRESS(i);
		GpioPins[i].iBank = i / KHwGpioPinsPerBank;
		GpioPins[i].iIrqVector =  EOmap3530_IRQ29_GPIO1_MPU_IRQ +GpioPins[i].iBank;
		}
	}


TInt TGpioDispatcher::Init()
	{
	for(TInt i=0; i < KHwGpioBanks; i++)
		{		
		TInt r = Interrupt::Bind(EOmap3530_IRQ29_GPIO1_MPU_IRQ+i,DispatchIsr,(TAny*) i); 
		// Pass in a pointer to the first pin in the bank - shouldn't use addition on the constant here	
		__ASSERT_ALWAYS(r==KErrNone,Kern::Fault("ExternalInterrupt::%s Cant Bind to %d",EOmap3530_IRQ29_GPIO1_MPU_IRQ+i));	
		}

	Register( EIrqRangeBaseGpio );

	return KErrNone;
	}



TInt TGpioDispatcher::GetGPIOPin(TInt aId,GpioPin *aPin)
	{
	
	if(! ( aId >= 0 && aId <  KHwGpioPinMax)  )
			return KErrArgument;
	
	TInt irq = /*NKern::DisableAllInterrupts();*/__SPIN_LOCK_IRQSAVE_R(GPIOpinsDescLock);
	memcpy(aPin,&GpioPins[aId],sizeof(GpioPin));
	/*NKern::RestoreInterrupts(irq);*/__SPIN_UNLOCK_IRQRESTORE_R(GPIOpinsDescLock,irq);	
	
	return KErrNone;	
	}

TInt TGpioDispatcher::SetGPIOPin(TInt aId,GpioPin *aPin)
	{
		
	if(! ( aId >= 0 && aId <  KHwGpioPinMax)  )
			return KErrArgument;
	
	TInt irq = /*NKern::DisableAllInterrupts();*/__SPIN_LOCK_IRQSAVE_W(GPIOpinsDescLock);				
	memcpy(&GpioPins[aId],aPin,sizeof(GpioPin ));
	/*NKern::RestoreInterrupts(irq);*/__SPIN_UNLOCK_IRQRESTORE_W(GPIOpinsDescLock,irq);
	
	return KErrNone;
	}

FORCE_INLINE TBool TGpioDispatcher::IsValidId( TInt aId )
	{
	return ((TUint)aId < EGPIOIRQ_END && (TUint)aId>=EGPIOIRQ_FIRST);
	}


void TGpioDispatcher::Spurious(TAny* aId)
	{
	Kern::Fault("SpuriousExtInt",(TInt)aId);
	}


void TGpioDispatcher::DispatchIsr(TAny *aPtr)
	{
	Interrupt::Disable(EOmap3530_IRQ29_GPIO1_MPU_IRQ + (TInt) aPtr);
	
	//need to spinlock the gpio here.....
	TUint32	highVectors = AsspRegister::Read32(KGPIO_BASE_ADDRESSES[(TInt) aPtr] + KGPIO_IRQSTATUS1);
  	AsspRegister::Write32(KGPIO_BASE_ADDRESSES[(TInt) aPtr] + KGPIO_IRQSTATUS1, highVectors);
	
	GpioPin pin;
	for (TInt i =  0; i < KHwGpioPinsPerBank ; i++,highVectors >>=1)
		{
		if(highVectors & 0x1)
			{		
			GetGPIOPin(i+(TInt)aPtr*KHwGpioPinsPerBank, &pin);
			(*pin.irq.iIsr)(pin.irq.iPtr); // dispatch this pin's ISR
			}
		}
	Interrupt::Enable(EOmap3530_IRQ29_GPIO1_MPU_IRQ + (TInt)aPtr);
	}


TInt TGpioDispatcher::Bind(TInt anId, TIsr anIsr, TAny* aPtr)
	{
	if(IsValidId(anId))
		{
		//we want to bind the callers isrfunc to the pin dispatch here
		GpioPin pin;
		GetGPIOPin(anId- EGPIOIRQ_FIRST,&pin);
		pin.irq.iIsr = anIsr;
		pin.irq.iPtr = aPtr;
		SetGPIOPin(anId- EGPIOIRQ_FIRST,&pin);
		return KErrNone;
		} 
	else
		{
		return KErrArgument;
		}
	}

TInt TGpioDispatcher::Unbind(TInt anId)
	{
	__KTRACE_OPT(KGPIO,Kern::Printf("GPIO:%s id=%d",__FUNCTION__,anId));

	if(IsValidId(anId))
		{
		GpioPin pin;
		TInt pinNr = anId - EGPIOIRQ_FIRST;
		GetGPIOPin(pinNr,&pin);
		pin.irq.iIsr=Spurious;
		pin.irq.iPtr=NULL;
		SetGPIOPin(pinNr,&pin);
		return KErrNone;
		}
	else
		{
		return KErrArgument;
		}
	}
	

TInt TGpioDispatcher::Enable(TInt anId)
	{
	__KTRACE_OPT(KGPIO,Kern::Printf("GPIO:%s id=%d +",__FUNCTION__,anId));
	CHECK_PRECONDITIONS(MASK_NO_FAST_MUTEX,"GPIO::InterruptEnable Cant Hold Mutex in Blocking function");			
	
	if(IsValidId(anId))
		{
		GpioPin pin;
		TInt pinNr = anId - EGPIOIRQ_FIRST;	
		GetGPIOPin(pinNr,&pin);
		
		if(Spurious == pin.irq.iIsr)
			{
			
			__KTRACE_OPT(KGPIO,Kern::Printf("GPIO:%s id=%d NOT BOUND",__FUNCTION__,anId));
			return KErrNotReady;
			}
		AsspRegister::Write32(pin.iBankAddr+KGPIO_SETIRQENABLE1,GPIO_PIN_OFFSET( pinNr));
		
		if(!Omap3530Interrupt::IsInterruptEnabled(pin.iIrqVector))
			Interrupt::Enable(pin.iIrqVector);
		
		return KErrNone;
		}
	else
		{
		return KErrArgument; 
		}
	}

TInt TGpioDispatcher::Disable(TInt anId)
	{
	
	__KTRACE_OPT(KGPIO,Kern::Printf("GPIO:%s id=%d",__FUNCTION__,anId));
	CHECK_PRECONDITIONS(MASK_NO_FAST_MUTEX,"GPIO::InterruptDisable Cant Hold Mutex in Blocking function");
	if(IsValidId(anId))
		{		
		TInt pinNr = anId- EGPIOIRQ_FIRST;
		GpioPin pin;
		GetGPIOPin(pinNr, &pin);
		
		AsspRegister::Write32(pin.iBankAddr+KGPIO_CLEARIRQENABLE1, GPIO_PIN_OFFSET(pinNr));
		
		//is this the last one for this bank ? then unmap
		if(0x00 == AsspRegister::Read32(pin.iBankAddr+KGPIO_IRQENABLE1))
			{
			Interrupt::Disable(pin.iIrqVector);
			}
		return KErrNone;
		}
	else
		{
		return KErrArgument; 
		}
	}

TInt TGpioDispatcher::Clear(TInt anId)
	{
	
	__KTRACE_OPT(KGPIO,Kern::Printf("GPIO:%s id=%d",__FUNCTION__,anId));
	CHECK_PRECONDITIONS(MASK_NO_FAST_MUTEX,"GPIO::InterruptDisable Cant Hold Mutex in Blocking function");
		
	if(IsValidId(anId))
		{
		TInt pinNr = anId- EGPIOIRQ_FIRST;
		GpioPin myPin;
		GetGPIOPin(pinNr, &myPin);
		
		AsspRegister::Write32((myPin.iBankAddr+KGPIO_IRQSTATUS1),  GPIO_PIN_OFFSET(pinNr));
		//if that was the only high bit clear the mainline as well
		if(0 == AsspRegister::Read32(myPin.iBankAddr+KGPIO_IRQSTATUS1))
			{
			Interrupt::Clear(myPin.iIrqVector);
			}
		}
	return KErrNone;
	}

TInt TGpioDispatcher::SetPriority(TInt aId, TInt aPriority)
	{
	return KErrNotSupported;
	}


GLDEF_C TInt InitGpioInterrupts()
	{
	TInt r = KErrNoMemory;

	TGpioDispatcher* dispatcher = new TGpioDispatcher;
	if( dispatcher )
		{
		r = dispatcher->Init();
		}
	return r;
	}