omap3530/omap3530_drivers/spi/slave.cpp
branchBeagle_BSP_dev
changeset 77 e5fd00cbb70a
equal deleted inserted replaced
76:29b14275133a 77:e5fd00cbb70a
       
     1 // Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of the License "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 // lukasz.forynski@gmail.com
       
    13 //
       
    14 // Description:
       
    15 // omap3530/omap3530_drivers/spi/slave.cpp
       
    16 //
       
    17 
       
    18 
       
    19 #include <drivers/iic.h>
       
    20 #include <drivers/iic_channel.h>
       
    21 #include "psl_init.h"
       
    22 #include "slave.h"
       
    23 
       
    24 #error "Trying to use the SPI slave, but it's not implemented yet!"
       
    25 
       
    26 // The timeout period to wait for a response from the client, expressed in milliseconds
       
    27 // This is converted to timer ticks by the PIL, so the maximum value is 2147483.
       
    28 // The value should be selected to allow for the longest, slowest transfer
       
    29 // const TInt KClientWaitTime = 2; // 2mS, when debugging might set up to KMaxWaitTime
       
    30 
       
    31 
       
    32 // In an SMP system, use a spin lock to guard access to member variables iTrigger and iInProgress
       
    33 #ifdef __SMP__
       
    34 static TSpinLock IicPslSpinLock = TSpinLock(TSpinLock::EOrderGenericIrqLow3);
       
    35 #endif
       
    36 
       
    37 // Callback function for the iHwGuardTimer timer.
       
    38 //
       
    39 // Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut
       
    40 //
       
    41 void DSpiSlaveBeagle::TimeoutCallback(TAny* aPtr)
       
    42 	{
       
    43 	__KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::TimeoutCallback"));
       
    44 	DSpiSlaveBeagle *a = (DSpiSlaveBeagle*) aPtr;
       
    45 	a->iTransactionStatus = KErrTimedOut;
       
    46 	}
       
    47 
       
    48 
       
    49 // Static method called by the ISR when the Master has ended a transfer
       
    50 //
       
    51 // The method checks and reports the Rx and Tx status to the PIL by calling NotifyClient with a bitmask described as follows:.
       
    52 // - If a Tx transfer has ended before all the data was transmitted, bitmask = (ETxAllBytes | ETxOverrun)
       
    53 // - If a Tx transfer has ended and all the data was transmitted, bitmask = ETxAllBytes
       
    54 // - If a Rx transfer has ended before the expected amount of data was received, bitmask = (ERxAllBytes | ERxUnderrun)
       
    55 // - If a Rx transfer has ended and the expected amount of data was received, bitmask = ERxAllBytes
       
    56 //
       
    57 void DSpiSlaveBeagle::NotifyClientEnd(DSpiSlaveBeagle* aPtr)
       
    58 	{
       
    59 	__KTRACE_OPT(KIIC, Kern::Printf("NotifyClientEnd, iTrigger %x", aPtr->iTrigger));
       
    60 
       
    61 	// Since a transfer has ended, may wish to disable interrupts at this point
       
    62 	// This will likely be supported with calls similar to the following:
       
    63 	//		AsspRegister::Write32(aPtr->iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
       
    64 	//		Interrupt::Disable(aPtr->iRxInterruptId);
       
    65 	//		Interrupt::Disable(aPtr->iTxInterruptId);
       
    66 
       
    67 	// iTrigger will have bits ETransmit and EReceive set according to the operation requested in the call to DoRequest
       
    68 	// Use variable flag for the bitmask to pass into the PIL method NotifyClient
       
    69 	TInt flag = 0;
       
    70 	if(aPtr->iTrigger & EReceive)
       
    71 		{
       
    72 		// Requested Rx operation has ended - check for RxUnderrun
       
    73 		flag = ERxAllBytes;
       
    74 		if(aPtr->iRxDataEnd != aPtr->iRxData)
       
    75 			{
       
    76 			flag |= ERxUnderrun;
       
    77 			}
       
    78 		}
       
    79 	if(aPtr->iTrigger & ETransmit)
       
    80 		{
       
    81 		// Requested Tx operation has ended - check for RxOverrun
       
    82 		flag |= ETxAllBytes;
       
    83 		if(aPtr->iTxDataEnd != aPtr->iTxData)
       
    84 			{
       
    85 			flag |= ETxOverrun;
       
    86 			}
       
    87 		}
       
    88 	aPtr->NotifyClient(flag);
       
    89 	}
       
    90 
       
    91 
       
    92 // ISR Handler
       
    93 //
       
    94 // The ISR handler identifies the cause of the interrupt that lead to its invocation:
       
    95 // if the cause was transfer-related, it calls the PIL function NotifyClient to report a summary of the transfer status;
       
    96 // if the cause was completion of asynchronous channel capture, PIL function ChanCaptureCallback is called
       
    97 //
       
    98 // The ISR also clears the source of the interrupt, and (for transfer-related interrupts) transfers the next data
       
    99 // between buffers and the hardware and updates the member variable iInProgress to indicate if a transfer has started or
       
   100 // ended. If a transfer has ended before the expected amount of data has been transfered it calls function NotifyClientEnd.
       
   101 //
       
   102 void DSpiSlaveBeagle::IicPslIsr(TAny* /*aPtr*/)
       
   103 	{
       
   104 	//		DSpiSlaveBeagle *a = (DSpiSlaveBeagle*) aPtr;
       
   105 
       
   106 	//		TInt intState = 0;	// Variable to support use of spin lock
       
   107 
       
   108 	//		TInt trigger = 0; // Record the Rx and Tx transfers
       
   109 
       
   110 	//		TUint32 intStatus = 0; // Record of the interrupts that are being reported
       
   111 
       
   112 	// Identify the cause of the interrupt. If this can be achieved by reading a single register,
       
   113 	// code similar to the following could be used:
       
   114 	//		intStatus = AsspRegister::Read32(a->iChannelBase + KIntStatusOffset);
       
   115 
       
   116 	// Optional (not required if asynchronous channel capture is not supported)
       
   117 	// If the cause of the interrupt is completion of asynchronous channel capture, the ISR will check the appropriate
       
   118 	// indicator for confirmation of success - for a real PSL, this may be by querying a bitmask in a register. For the template PSL,
       
   119 	// however, a dummy member variable (iAsyncConfig) has been used to represent the asynchronous operation instead.
       
   120 	//
       
   121 	//		if(iAsyncConfig == 1)	// Replace with a check of the indicator that the interrupt was due to asynchrous channel capture
       
   122 	//			{
       
   123 	//			// The PIL function ChanCaptureCallback is now to be invoked. It takes as an argument either KErrNone or a
       
   124 	//			// system-wide error code to indicate the cause of failure. For a real PSL, the argument would likely be determined
       
   125 	//			// by reading a bitmask in a status register - but for the template port, just use KErrNone.
       
   126 	//			//
       
   127 	//			a->ChanCaptureCallback(KErrNone);
       
   128 	//			return;
       
   129 	//			}
       
   130 
       
   131 	// If an interrupt indicates that a transfer has started, or that it has now ended, (such as a chip select
       
   132 	// line transition for a SPI bus the member variable iInProgress should be modified accordingly. This should
       
   133 	// be done under the guard of spin lock macros since iInProgress can be accessed in the context of the Client
       
   134 	// thread (in DoRequest, ProcessData and InitTransfer). The following structure should be adopted:
       
   135 	//		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   136 	//		<access a->iInProgress>
       
   137 	//		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   138 	//
       
   139 	// If a transfer has ended before the expected amount of data has been transfered, function NotifyClientEnd
       
   140 	// should be called, as follows:
       
   141 	//		a->NotifyClientEnd(a);
       
   142 	//		return;	// Return now - the interrupt indicated transfer end, not receipt or transmission of data.
       
   143 
       
   144 	// The transfers that had been started are indicated by the bitmask held in member variable iTrigger.
       
   145 	// This must be accessed under the guard of a spin lock since it can be accessed in the context of the
       
   146 	// Client thread (in DoRequest, ProcessData and InitTransfer). The following structure should be adopted:
       
   147 	//		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   148 	//		trigger = a->iTrigger;
       
   149 	//		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   150 
       
   151 	// If the interrupt was raised for a Tx event, and a Tx transfer had been started (so the interrupt was not spurious)
       
   152 	// then either prepare the next data to send, or, if all the data has been sent, call the PIL function NotifyClient
       
   153 	// with bitmask (ETxAllBytes | ETxUnderrun) so that, if the Client specified a ETxUnderrun notification, it will be alerted
       
   154 	// and can determine whether another buffer of data should be provide for transmission.
       
   155 	// Code similar to the following could be used:
       
   156 	//		if(intStatus & KTxInterruptBitMask)
       
   157 	//			{
       
   158 	//			if(trigger & ETransmit)
       
   159 	//				{
       
   160 	//				// Interrupt was not spurious
       
   161 	//				if(a->iTxData == a->iTxDataEnd)
       
   162 	//					{
       
   163 	//					// All the data to be transmitted has been sent, so call the PIL method NotifyClient
       
   164 	//					a->NotifyClient(ETxAllBytes | ETxUnderrun);
       
   165 	//					}
       
   166 	//				else
       
   167 	//					{
       
   168 	//					// There is more data to be sent
       
   169 	//					// TUint8 nextTxValue = *iTxData;	// For this example, assumes one byte of data is to be transmitted
       
   170 	//														// but if operating in 16-bit mode, bytes may need arranging for
       
   171 	//														// endianness
       
   172 	//
       
   173 	//					// Write to the Tx register with something similar to the following:
       
   174 	//					//		AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue);
       
   175 	//
       
   176 	//					iTxData += iWordSize;	// Then increment the pointer to the data. In this example, 8-bit mode is assumed
       
   177 	//											// (iWordSize=1), but if operating in 16-bit mode iTxData would be incremented
       
   178 	//											// by the number of bytes specified in iWordSize
       
   179 	//					}
       
   180 	//				}
       
   181 	//			}
       
   182 
       
   183 	// If the interrupt was raised for a Rx event, and a Rx transfer had been started (so the interrupt was not spurious)
       
   184 	// read the received data from the hardware to the buffer. If a Rx FIFO is being used, use a loop to drain it - until
       
   185 	// the FIFO is empty or the buffer is full. If data remains after the buffer is full, an RxOverrun condition has occurred
       
   186 	// - so the PIL function NotifyClient should be called with bitmask (ERxAllBytes | ERxOverrun) so that, if the Client specified
       
   187 	// a ERxOverrun notification, it will be alerted and can determine whether another buffer should be provided to continue reception.
       
   188 	// Code similar to the following could be used:
       
   189 	//		if(intStatus & KRxInterruptBitMask)
       
   190 	//			{
       
   191 	//			if(trigger & EReceive)
       
   192 	//				{
       
   193 	//				// Interrupt was not spurious
       
   194 	//				while(AsspRegister::Read32(a->iChannelBase + KRxFifoLevelOffset))
       
   195 	//					{
       
   196 	//					if((a->iRxData - a->iRxDataEnd) >= a->iWordSize)
       
   197 	//						{
       
   198 	//						// Space remains in the buffer, so copy the received data to it
       
   199 	//						TUint8 nextRxValue = AsspRegister::Read32(a->iChannelBase + KRxFifoOffset);
       
   200 	//						*a->iRxData = nextRxValue;	// For this example, assumes one byte of data is to be transmitted
       
   201 	//													// but if operating in 16-bit mode, bytes may need arranging for
       
   202 	//													// endianness
       
   203 	//
       
   204 	//						a->iRxData += a->iWordSize;	// Then increment the pointer to the data. In this example, 8-bit mode is assumed
       
   205 	//													// (iWordSize=1), but if operating in 16-bit mode iRxData would be incremented
       
   206 	//													// by the number of bytes specified in iWordSize
       
   207 	//						}
       
   208 	//					else
       
   209 	//						{
       
   210 	//						// The buffer is full but more data has been received - so there is an RxOverrun condition
       
   211 	//						// Disable the hardware from receiving any more data and call the PIL function NotifyClient
       
   212 	//						// with bitmask (ERxAllBytes | ERxOverrun).
       
   213 	//						AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoDisableBitMask);
       
   214 	//						a->NotifyClient(ERxAllBytes | ERxOverrun);
       
   215 	//						break;
       
   216 	//						}
       
   217 	//					}
       
   218 	//				}
       
   219 	//			else
       
   220 	//				{
       
   221 	//				// If the interrupt was spurious, ignore the data, and reset the FIFO
       
   222 	//				AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoClearBitMask);
       
   223 	//				}
       
   224 
       
   225 	// Once the interrupts have been processed, clear the source. If this can be achieve by writing to
       
   226 	// a single register, code similar to the following could be used:
       
   227 	//		AsspRegister::Write32(a->iChannelBase + KIntStatusOffset, KAIntBitMask);
       
   228 
       
   229 	}
       
   230 
       
   231 
       
   232 // Constructor, first stage
       
   233 //
       
   234 // The PSL is responsible for setting the channel number - this is passed as the first parameter to
       
   235 // this overload of the base class constructor
       
   236 //
       
   237 DSpiSlaveBeagle::DSpiSlaveBeagle(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) :
       
   238 	DIicBusChannelSlave(aBusType, aChanDuplex, 0),	// Base class constructor. Initalise channel ID to zero.
       
   239 	iHwGuardTimer(TimeoutCallback, this)			// Timer to guard against hardware timeout
       
   240 	{
       
   241 	iChannelNumber = aChannelNumber;
       
   242 	__KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::DSpiSlaveBeagle, iChannelNumber = %d\n", iChannelNumber));
       
   243 	}
       
   244 
       
   245 
       
   246 // Second stage construction
       
   247 //
       
   248 // Allocate and initialise objects required by the PSL channel implementation
       
   249 //
       
   250 TInt DSpiSlaveBeagle::DoCreate()
       
   251 	{
       
   252 	__KTRACE_OPT(KIIC, Kern::Printf("\nDSpiSlaveBeagle::DoCreate, ch: %d \n", iChannelNumber));
       
   253 
       
   254 	TInt r = KErrNone;
       
   255 
       
   256 	// PIL Base class initialization.
       
   257 	r = Init();
       
   258 	if(r == KErrNone)
       
   259 		{
       
   260 		// At a minimum, this function must set the channel's unique channel ID.
       
   261 		// When the channel is captured, this value will be combined with an instance count
       
   262 		// provided by the PIL to generate a value that will be used by a client as a unique
       
   263 		// identifer in subsequent calls to the Slave API.
       
   264 		//
       
   265 		// There is no set format for the ID, it just needs to be unique.
       
   266 		// Un-comment and complete the following line:
       
   267 //		iChannelId =
       
   268 
       
   269 		// This method may also be concerned with setting the base register address iChannelBase), and allocating
       
   270 		// any objects that will be required to support operaton until the channel is deleted.
       
   271 		//
       
   272 		// Un-comment and complete the following line:
       
   273 //		iChannelBase =
       
   274 		}
       
   275 	return r;
       
   276 	}
       
   277 
       
   278 // static method used to construct the DSpiSlaveBeagle object.
       
   279 DSpiSlaveBeagle* DSpiSlaveBeagle::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex)
       
   280 	{
       
   281 	__KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::NewL(): aChannelNumber = %d, BusType =%d", aChannelNumber, aBusType));
       
   282 	DSpiSlaveBeagle *pChan = new DSpiSlaveBeagle(aChannelNumber, aBusType, aChanDuplex);
       
   283 
       
   284 	TInt r = KErrNoMemory;
       
   285 	if (pChan)
       
   286 		{
       
   287 		r = pChan->DoCreate();
       
   288 		}
       
   289 	if (r != KErrNone)
       
   290 		{
       
   291 		delete pChan;
       
   292 		pChan = NULL;
       
   293 		}
       
   294 
       
   295 	return pChan;
       
   296 	}
       
   297 
       
   298 
       
   299 // Validates the configuration information specified by the client when capturing the channel
       
   300 //
       
   301 // Called by the PIL as part of the Slave CaptureChannel processing
       
   302 //
       
   303 // If the pointer to the header is NULL, return KErrArgument.
       
   304 // If the content of the header is not valid for this channel, return KErrNotSupported.
       
   305 //
       
   306 TInt DSpiSlaveBeagle::CheckHdr(TDes8* aHdrBuff)
       
   307 	{
       
   308 	TInt r = KErrNone;
       
   309 
       
   310 	if(!aHdrBuff)
       
   311 		{
       
   312 		r = KErrArgument;
       
   313 		}
       
   314 	else
       
   315 		{
       
   316 		// Check that the contents of the header are valid
       
   317 		//
       
   318 		// The header will be specific to a particular bus type. Using a fictional
       
   319 		// bus type Abc,code similar to the following could be used to validate each
       
   320 		// member of the header:
       
   321 		//
       
   322 		//		TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff;
       
   323 		//		TConfigAbcV01 &abcHeader = (*headerBuf)();
       
   324 		//		if(	(abcHeader.iHeaderMember < ESomeMinValue)	||
       
   325 		//			(abcHeader.iHeaderMember > ESomeMaxValue))
       
   326 		//			{
       
   327 		//			__KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember));
       
   328 		//			r = KErrNotSupported;
       
   329 		//			}
       
   330 
       
   331 		}
       
   332 	__KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::CheckHdr() r %d", r));
       
   333 
       
   334 	return r;
       
   335 	}
       
   336 
       
   337 
       
   338 // Method called in the context of the client thread, as a consequence of the PSL invocation of the
       
   339 // PIL method NotifyClient when a bus event occurs.
       
   340 //
       
   341 // This method updates the bitmask of requested operations (held in member variable iTrigger) and the
       
   342 // PIL counts of data received and transmitted. If the event was a bus error, the bitmask of requested operations
       
   343 // is cleared.
       
   344 //
       
   345 void DSpiSlaveBeagle::ProcessData(TInt aTrigger, TIicBusSlaveCallback* aCb)
       
   346 	{
       
   347 	__KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::ProcessData(), trigger: %x\n", aTrigger));
       
   348 
       
   349 	TInt intState;
       
   350 
       
   351 	// If using the iInProgress member variable to indicate transitions on a chip-select line, and an interrupt
       
   352 	// occurred as a transfer was to end, then must ensure the transmission of data has ceased.
       
   353 	//
       
   354 	// Must use spin lock to guard access since iInProgress is accessed by the ISR
       
   355 	//
       
   356 	TInt inProgress;
       
   357 	intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   358 	inProgress = iInProgress;
       
   359 	__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   360 	//
       
   361 	if(!inProgress &&								// Transfer has now ended
       
   362 	   (aTrigger & (ERxAllBytes | ETxAllBytes)))	// Master has not yet finished transferring data
       
   363 		{
       
   364 		// Use the guard timer to make sure that transfer ends with an expected time - if this does not cease
       
   365 		// before the timer expires, iTransactionStatus will be set to KErrTimedOut by the callback function TimeoutCallback
       
   366 		//
       
   367 		// Poll the relevant register to check for transfer activity, using code similar to the following:
       
   368 		//		TInt8 transferring = AsspRegister::Read32(iChannelBase + KStatusRegisterOffset) & KTransferringBitMask);
       
   369 		// For the template port, use a dummy variable instead of the register access (transferring = 1)
       
   370 		//
       
   371 		TInt8 transferring = 1;
       
   372 		iTransactionStatus = KErrNone;
       
   373 		iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
       
   374 
       
   375 		while((iTransactionStatus == KErrNone) &&
       
   376 		      transferring);		// Replace transferring with a register read, as described above
       
   377 
       
   378 		// At this point, either the transfer has ceased, or the timer expired - in either case, may disable the interrupt
       
   379 		// for the transfer now, using code similar to the following:
       
   380 		//		AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask);
       
   381 
       
   382 		// Check for guard timer expiry
       
   383 		if(iTransactionStatus != KErrNone)
       
   384 			{
       
   385 			__KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::ProcessData - Transaction timed-out"));
       
   386 			return;
       
   387 			}
       
   388 		else
       
   389 			{
       
   390 			iHwGuardTimer.Cancel();
       
   391 			}
       
   392 
       
   393 		// If all transfer activity has now ceased, clear iTrigger
       
   394 		// Must use spin lock to guard access since iInProgress is accessed by the ISR
       
   395 		//
       
   396 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   397 		iTrigger = 0;
       
   398 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   399 		}
       
   400 
       
   401 	// If the PSL called the PIL function NotifyClient to indicate transfer activity (or error), the reason
       
   402 	// will be specified as a bitmask in aTrigger
       
   403 	//  - if a Rx event occurred, the ERxAllBytes flag will be set
       
   404 	//  - if a Tx event occurred, the ETxAllBytes flag will be set
       
   405 	//  - if a bus error occurred, the EGeneralBusError flag will be set
       
   406 	//
       
   407 	if(aTrigger & ERxAllBytes)
       
   408 		{
       
   409 		__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Buf:    %x\n", iRxData));
       
   410 		__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Bufend: %x\n", iRxDataEnd));
       
   411 
       
   412 		// Clear the internal EReceive flag
       
   413 		// This must be done under guard of a spin lock since iTrigger is accessed by the ISR
       
   414 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   415 		iTrigger &= ~EReceive;
       
   416 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   417 
       
   418 		// Update the PIL count of Rx data (in the Callback object)
       
   419 		aCb->SetRxWords(iNumRxWords - ((iRxDataEnd - iRxData) / iWordSize));
       
   420 		}
       
   421 	//
       
   422 	if(aTrigger & ETxAllBytes)
       
   423 		{
       
   424 		__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Buf:    %x\n", iTxData));
       
   425 		__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Bufend: %x\n", iTxDataEnd));
       
   426 
       
   427 		// Clear the internal ETransmit flag..
       
   428 		// This must be done under guard of a spin lock since iTrigger is accessed by the ISR
       
   429 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   430 		iTrigger &= ~ETransmit;
       
   431 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   432 
       
   433 		// Update the PIL count of Tx data (in the Callback object)
       
   434 		aCb->SetTxWords(iNumTxWords - ((iTxDataEnd - iTxData) / iWordSize));
       
   435 		}
       
   436 	//
       
   437 	if(aTrigger & EGeneralBusError)
       
   438 		{
       
   439 		__KTRACE_OPT(KIIC, Kern::Printf("BusError.."));
       
   440 
       
   441 		// Clear and disable relevant interrupts, possibly using code similar to the following:
       
   442 		//		AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask);
       
   443 
       
   444 		// Clear internal flags
       
   445 		// This must be done under guard of a spin lock since iTrigger is accessed by the ISR
       
   446 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   447 		iTrigger = 0;
       
   448 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   449 		}
       
   450 
       
   451 	// Set the callback's trigger, for use by the PIL
       
   452 	aCb->SetTrigger(aTrigger | aCb->GetTrigger());
       
   453 	}
       
   454 
       
   455 
       
   456 
       
   457 // Method to initialise the hardware in accordance with the data provided by the Client
       
   458 // in the configuration header when capturing the channel
       
   459 //
       
   460 // This method is called from DoRequest and is expected to return a value to indicate success
       
   461 // or a system wide error code to inform of the failure
       
   462 //
       
   463 TInt DSpiSlaveBeagle::ConfigureInterface()
       
   464 	{
       
   465 	__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
       
   466 
       
   467 	TInt r = KErrNone;
       
   468 
       
   469 	// The header is stored in member variable iConfigHeader, and will be specific to a particular bus type.
       
   470 	// Using a fictional bus type Abc, code similar to the following could be used to access each
       
   471 	// member of the header:
       
   472 	//
       
   473 	//		TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) iConfigHeader;
       
   474 	//		TConfigAbcV01 &abcHeader = (*headerBuf)();
       
   475 	//		TInt value = abcHeader.iTintMember;
       
   476 
       
   477 	// Initialising the hardware may be achieved with calls similar to the following:
       
   478 	//		AsspRegister::Write32(a->iChannelBase + KBusModeControlOffset, KIicPslModeControlBitMask);
       
   479 	//		GPIO::SetPinMode(aPinId, GPIO::EEnabled);
       
   480 
       
   481 	// Binding an ISR may be achieved with calls similar to the following:
       
   482 	//		r = Interrupt::Bind(iRxInterruptId, DSpiSlaveBeagle::IicPslIsr, this);
       
   483 	//		r = Interrupt::Bind(iTxInterruptId, DSpiSlaveBeagle::IicPslIsr, this);
       
   484 	// Enabling interrupts may be achieved with calls similar to the following:
       
   485 	//		r = Interrupt::Enable(iRxInterruptId);
       
   486 	//		r = Interrupt::Enable(iTxInterruptId);
       
   487 
       
   488 	// Modifying a hardware register may not be a zero-delay operation. The member variable iHwGuardTimer could be used to guard a
       
   489 	// continuous poll of the hardware register that checks for the required change in the setting; TimeoutCallback is already
       
   490 	// assigned as the callback function for iHwGaurdTimer, and it modifies member variable iTransactionStatus to indicate a timeout
       
   491 	// - so the two could be used together as follows:
       
   492 	//		iTransactionStatus = KErrNone;
       
   493 	//		iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
       
   494 	//		while((iTransactionStatus == KErrNone) &&
       
   495 	//		       AsspRegister::Read32(iChannelBase + KRegisterOffset) & KRegisterFlagBitMask);
       
   496 	//		if(iTransactionStatus != KErrNone)
       
   497 	//			{
       
   498 	//			r = KErrGeneral;
       
   499 	//			}
       
   500 	//		else
       
   501 	//			{
       
   502 	//			iHwGuardTimer.Cancel();
       
   503 	//			}
       
   504 
       
   505 	// DoRequest checks the return value so the variable r should be modified in the event of failure with a system-wide error code
       
   506 	// for example, if a register could not be modified,
       
   507 	//			r = KErrGeneral;
       
   508 	//			__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface failed with error %d\n",r));
       
   509 	return r;
       
   510 	}
       
   511 
       
   512 
       
   513 // Method to start asynchronous initialisation of the hardware, in accordance with the data provided by the Client
       
   514 // in the configuration header when capturing the channel. This differs from ConfigureInterface in that it
       
   515 // merely starts the initialisation, then returns immediately;
       
   516 //
       
   517 // The PSL is expected to be implemented as an asynchronous state machine, where events (for example hardware
       
   518 // interrupts, or timer expiry) invoke callback functions that advance the state machine to the next state. Once
       
   519 // all the required states have been transitioned, so that the PSL part of the CaptureChannel processing is
       
   520 // complete, the ISR should be invoked, which will then call PIL method ChanCaptureCallback
       
   521 //
       
   522 // This method is called from DoRequest and is expected to return a value to indicate success
       
   523 // or a system wide error code to inform of the failure
       
   524 //
       
   525 TInt DSpiSlaveBeagle::AsynchConfigureInterface()
       
   526 	{
       
   527 	__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
       
   528 
       
   529 //	TInt r = KErrNone;	// A real implementation would use this as the return value to indicate success / failure
       
   530 
       
   531 	// Precisely what processing is done to 'start' the asynchronous processing is entirely platform-specific;
       
   532 	// it may be the set-up and activation of a long-running operation that completes asynchronously. Regardless of what
       
   533 	// is done, its completion is expected to result in the ISR being run.
       
   534 	//
       
   535 	// Whatever the operation, there must be some means of the ISR recognising that an asynchronous initialisation has
       
   536 	// been performed
       
   537 	// In a real PSL, this may be be checking a bitmask in a status register. For the template PSL, however,
       
   538 	// a dummy class member will be used (iAsyncConfig)
       
   539 	// Since this member will be accessed by the ISR, it should, strictly speaking, be accessed under the guard of a spin lock
       
   540 	TInt intState;
       
   541 	intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   542 	iAsyncConfig = 1;
       
   543 	__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   544 
       
   545 	return KErrNone;	// A real implementation would return an indication of success / failure
       
   546 	}
       
   547 
       
   548 // Method called from DoRequest to start Tx and-or Rx transfer.
       
   549 //
       
   550 // The method will initialise the hardware and pointers used to manage transfers, before returning a value to report success
       
   551 // (KErrNone) or a system-wide error code that indicates the cause of failure.
       
   552 //
       
   553 TInt DSpiSlaveBeagle::InitTransfer()
       
   554 	{
       
   555 	__KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::InitTransfer()"));
       
   556 
       
   557 	TInt r = KErrNone;
       
   558 
       
   559 	// Local copies of member variables that must be accessed in a synchronised manner
       
   560 	TInt inProgress;
       
   561 	TInt trigger;
       
   562 
       
   563 	TInt intState;
       
   564 
       
   565 	// Check if a transfer is already in progress.
       
   566 	// If variable iInProgress is being used, this must be determined in a synchronised manner because the ISR modifies it.
       
   567 	// Bus types that do not rely on chip-select transitions may use an alternative method to indicate if a transfer is in
       
   568 	// progress
       
   569 	intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   570 	inProgress = iInProgress;
       
   571 	__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   572 
       
   573 	if(!inProgress)
       
   574 		{
       
   575 		// If no transfers are in progress, it may be necessary to initialise the hardware to support those that
       
   576 		// are being requested. This may include FIFO and interrupt initialisation,
       
   577 		//
       
   578 		// Initialising the hardware may be achieved with calls similar to the following:
       
   579 		//		AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslModeControlBitMask);
       
   580 		//		GPIO::SetPinMode(aPinId, GPIO::EEnabled);
       
   581 		}
       
   582 
       
   583 	// Check the current operations. This must be determined in a synchronised manner because ProcessData
       
   584 	// runs in the context of the Client thread and it modifies the value of iTrigger
       
   585 	intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   586 	trigger = iTrigger;
       
   587 	__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   588 
       
   589 	if(trigger & ETransmit)
       
   590 		{
       
   591 		// If Tx transfers were not previously active, it may be necessary to initialise the Tx hardware here, e.g.
       
   592 		//		AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslTxModeBitMask);
       
   593 
       
   594 		// Initialise the Tx pointers
       
   595 		iTxData = iTxBuf + (iWordSize * iTxOffset);
       
   596 		iTxDataEnd = iTxData + (iWordSize * iNumTxWords);
       
   597 
       
   598 		__KTRACE_OPT(KIIC, Kern::Printf("Tx Buf:    %x", iTxData));
       
   599 		__KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd));
       
   600 
       
   601 		// If using a FIFO, copy the data to it until either the FIFO is full or all the data has been copied
       
   602 		// This could be achieved with something similar to the following lines:
       
   603 		//		while(AsspRegister::Read32(iChannelBase + KFifoLevelOffset) <= (KFifoMaxLevel - iWordSize) &&
       
   604 		//		      iTxData != iTxDataEnd)
       
   605 		// For the template port, will just use a dummy variable (dummyFifoLvlChk )in place of the register read
       
   606 		TInt dummyFifoLvlChk = 0;
       
   607 		while((dummyFifoLvlChk)	&&	// Replace this dummy variable with a read of the hardware
       
   608 			(iTxData != iTxDataEnd))
       
   609 			{
       
   610 			// TUint8 nextTxValue = *iTxData;	// For this example, assumes one byte of data is to be transmitted
       
   611 												// but if operating in 16-bit mode, bytes may need arranging for
       
   612 												// endianness
       
   613 
       
   614 			// Write to the Tx register with something similar to the following:
       
   615 			//		AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue);
       
   616 
       
   617 			iTxData += iWordSize;	// Then increment the pointer to the data. In this example, 8-bit mode is assumed
       
   618 									// (iWordSize=1), but if operating in 16-bit mode iTxData would be incremented
       
   619 									// by the number of bytes specified in iWordSize
       
   620 			}
       
   621 		// If a Tx FIFO is not being used, a single Tx value would be written - in which case the above loop would be replaced
       
   622 
       
   623 		__KTRACE_OPT(KIIC, Kern::Printf("After adding:\n\rTx Buf:    %x", iTxData));
       
   624 		__KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd));
       
   625 		}
       
   626 
       
   627 	if(trigger & EReceive)
       
   628 		{
       
   629 		// Initialise the Rx pointers
       
   630 		iRxData = iRxBuf + (iWordSize * iRxOffset);
       
   631 		iRxDataEnd = iRxData + (iWordSize * iNumRxWords);
       
   632 
       
   633 		__KTRACE_OPT(KIIC, Kern::Printf("Rx Buffer:  %x", iRxData));
       
   634 		__KTRACE_OPT(KIIC, Kern::Printf("Rx Bufend: %x", iRxDataEnd));
       
   635 
       
   636 		// If Rx transfers were not previously active, it may be necessary to initialise the Rx hardware here, e.g.
       
   637 		//		AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslRxModeBitMask);
       
   638 		}
       
   639 
       
   640 	// If there is some common configuration required to support Rx, Tx transfers, may do it here
       
   641 
       
   642 	return r;
       
   643 	}
       
   644 
       
   645 
       
   646 // The gateway function for PSL implementation
       
   647 //
       
   648 // This method is called by the PIL to perform one or more operations indicated in the bitmask aOperation,
       
   649 // which corresponds to members of the TPslOperation enumeration.
       
   650 //
       
   651 TInt DSpiSlaveBeagle::DoRequest(TInt aOperation)
       
   652 	{
       
   653 	__KTRACE_OPT(KIIC, Kern::Printf("\nDSpiSlaveBeagle::DoRequest, Operation 0x%x\n", aOperation));
       
   654 
       
   655 	TInt r = KErrNone;
       
   656 	TInt intState;
       
   657 
       
   658 	if (aOperation & EAsyncConfigPwrUp)
       
   659 		{
       
   660 		// The PIL has requested asynchronous operation of CaptureChannel.
       
   661 		// The PSL should start the processing required for a channel to be captured, and then return immediately with
       
   662 		// error code KErrNone (if the processing was started without error), so that the client thread will be unblocked.
       
   663 		// The PSL is expected to be implemented as an asynchronous state machine, where events (for example hardware
       
   664 		// interrupts, or timer expiry) invoke callback functions that advance the state machine to the next state. Once
       
   665 		// all the required states have been transitioned, so that the PSL part of the CaptureChannel processing is
       
   666 		// complete, the PSL should call the PIL function ChanCaptureCallback - this will lead to the Client-provided
       
   667 		// callback being executed in the context of the client thread
       
   668 		//
       
   669 		__KTRACE_OPT(KIIC, Kern::Printf("EAsyncConfigPwrUp"));
       
   670 		r = AsynchConfigureInterface();
       
   671 		if (r != KErrNone)
       
   672 			{
       
   673 			__KTRACE_OPT(KIIC, Kern::Printf("AsynchConfigureInterface returned %d\n", r));
       
   674 			}
       
   675 		return r;
       
   676 		}
       
   677 
       
   678 	if (aOperation & ESyncConfigPwrUp)
       
   679 		{
       
   680 		// The PIL has requested synchronous operation of CaptureChannel.
       
   681 		// The PSL should perform the processing required for a channel to be captured, and return a system-wide error
       
   682 		// code when this is complete to indicate the status of the capture.
       
   683 		// Capturing a channel is expected to include initialisation of the hardware to enable operation in accordance
       
   684 		// with the configuration specified in the PIL member variable iConfigHeader, which holds the configuration
       
   685 		// specified by the Client.
       
   686 		//
       
   687 		__KTRACE_OPT(KIIC, Kern::Printf("ESyncConfigPwrUp"));
       
   688 		r = ConfigureInterface();
       
   689 		if (r != KErrNone)
       
   690 			{
       
   691 			__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface returned %d\n", r));
       
   692 			return r;
       
   693 			}
       
   694 		}
       
   695 
       
   696 	if (aOperation & ETransmit)
       
   697 		{
       
   698 		// The PIL has requested that a Tx operation be started.
       
   699 		// Since the SPL may support simultaneous Rx and Tx operations, just set the flag in the iTrigger bitmask to
       
   700 		// indicate what has been requested. If both Rx and Tx operations are requested, and one completes ahead of the other,
       
   701 		// requiring the Client to provide a new buffer and associated call to DoRequest (as is the case if the Master wishes
       
   702 		// to transfer more data than the Slave buffer supported), it is possible that the other transfer could complete while
       
   703 		// this function is running; consequently, it may attempt to access iTrigger, and so cause data corruption. To cater for
       
   704 		// such situations, use a spin lock to guard access to iTrigger.
       
   705 		// When the same check has been performed for Rx, call the InitTransfer function to start the required transfers.
       
   706 		//
       
   707 		__KTRACE_OPT(KIIC, Kern::Printf("ETransmit"));
       
   708 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   709 		iTrigger |= ETransmit;
       
   710 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   711 		}
       
   712 
       
   713 	if (aOperation & EReceive)
       
   714 		{
       
   715 		// The PIL has requested that a Rx operation be started.
       
   716 		// Since the SPL may support simultaneous Rx and Tx operations, just set the flag in the iTrigger bitmask to
       
   717 		// indicate what has been requested. If both Rx and Tx operations are requested, and one completes ahead of the other,
       
   718 		// requiring the Client to provide a new buffer and associated call to DoRequest (as is the case if the Master wishes
       
   719 		// to transfer more data than the Slave buffer supported), it is possible that the other transfer could complete while
       
   720 		// this function is running; consequently, it may attempt to access iTrigger, and so cause data corruption. To cater for
       
   721 		// such situations, use a spin lock to guard access to iTrigger.
       
   722 		// When the same check has been performed for Tx, call the InitTransfer function to start the required transfers.
       
   723 		//
       
   724 		__KTRACE_OPT(KIIC, Kern::Printf("EReceive"));
       
   725 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   726 		iTrigger |= EReceive;
       
   727 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   728 		}
       
   729 
       
   730 	if (aOperation & (EReceive | ETransmit))
       
   731 		{
       
   732 		// This code should only be executed once it has been checked whether Rx and Tx operations are required.
       
   733 		r = InitTransfer();
       
   734 		}
       
   735 
       
   736 	if (aOperation & EAbort)
       
   737 		{
       
   738 		// The PIL has requested that the current transaction be aborted.
       
   739 		// This is the case if the Client has not responded within an expected time to specify the next steps in
       
   740 		// the transaction processing. The time allowed is specified by calling PIL function SetClientWaitTime, otherwise
       
   741 		// the time defaults to KSlaveDefCWaitTime.
       
   742 		// If the PSL is able to satisfy this request it should, at a minimum, disable interrupts and update the member
       
   743 		// variables that indicate a transaction is in progress. If the PSL is unable to satisfy the request then the same
       
   744 		// behaviour will follow as if this request had not been made, so there is no point in modifying the state variables.
       
   745 		// If both Rx and Tx operations had been requested, and one completes ahead of the other, it is possible that the other
       
   746 		// transfer could complete while this function is running; consequently, it may attempt to access iTrigger and iInProgress,
       
   747 		// and so cause data corruption. To cater for such situations, use a spin lock to guard access to iTrigger.
       
   748 		// The PIL makes no assumptions of whether the PSL can support this request or not, and does not check the return
       
   749 		// value - so there is no need to set one.
       
   750 		//
       
   751 		TUint8 dummyCanAbort = 1;	// Dummy variable to represent a check of if it is possible to abort the current transaction
       
   752 		__KTRACE_OPT(KIIC, Kern::Printf("EAbort"));
       
   753 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
       
   754 		if(dummyCanAbort)
       
   755 			{
       
   756 			// The spin lock has been acquired, so it is safe to modify data and hardware registers that may be accessed as part of
       
   757 			// interrupt processing performed by an ISR - this is assuming that the ISR has been written to acquire the same spin lock.
       
   758 			// Limit the processing to only that which is necessary to be processed under spin lock control, so as to not delay other
       
   759 			// threads of execution that are waiting for the spin lock to be freed.
       
   760 			// Hardware may be configured using code similar to the following:
       
   761 			//		AsspRegister::Write32(iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
       
   762 			iInProgress = EFalse;
       
   763 			iTrigger = 0;
       
   764 			}
       
   765 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
       
   766 		// Having released the spin lock, now perform any actions that are not affected by pre-emption by an ISR, this may include code
       
   767 		// such as the following
       
   768 		//		Interrupt::Disable(iRxInterruptId);
       
   769 		//		Interrupt::Disable(iTxInterruptId);
       
   770 		}
       
   771 
       
   772 	if (aOperation & EPowerDown)
       
   773 		{
       
   774 		// The PIL has requested that the channel be released.
       
   775 		// If this channel is not part of a MasterSlave channel, the next Client will operate in Slave mode. In this case, it may only
       
   776 		// be necessary to disable interrupts, and reset the channel hardware.
       
   777 		// If this channel represents the Slave of a MasterSlave channel, it is possible that some of the hardware is shared between the
       
   778 		// Master and Slave sub-channels. Since it may not be known whether the next Client of the parent channel will require operation
       
   779 		// in either Master or Slave mode, some additional processing may be required to allow for subsequent Master operation (for example.
       
   780 		// unbinding an interrupt).
       
   781 		//
       
   782 		__KTRACE_OPT(KIIC, Kern::Printf("EPowerDown"));
       
   783 
       
   784 		// Resetting the hardware may be achieved with calls similar to the following:
       
   785 		//		AsspRegister::Write32(iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
       
   786 		//		GPIO::SetPinMode(aPinId, GPIO::EDisabled);
       
   787 
       
   788 		// Disable interrupts may be achieved with calls similar to the following:
       
   789 		//		Interrupt::Disable(iRxInterruptId);
       
   790 		//		Interrupt::Disable(iTxInterruptId);
       
   791 
       
   792 		// Unbinding an ISR may be achieved with calls similar to the following:
       
   793 		//		Interrupt::Unbind(iRxInterruptId);
       
   794 		//		Interrupt::Unbind(iTxInterruptId);
       
   795 
       
   796 		// The PIL checks the return value so the variable r should be modified in the event of failure with a system-wide error code
       
   797 		// for example, if a register could not be modified,
       
   798 		//		r = KErrGeneral;
       
   799 		//		__KTRACE_OPT(KIIC, Kern::Printf("EPowerDown failed with error %d\n",r));
       
   800 
       
   801 		}
       
   802 	return r;
       
   803 	}
       
   804