First public contribution.
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 * This component and the accompanying materials are made available
5 * under the terms of the License "Eclipse Public License v1.0"
6 * which accompanies this distribution, and is available
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
19 #include <drivers/iic.h>
20 #include <drivers/iic_channel.h>
21 // #include <gpio.h> // Include if using GPIO functionality
23 #include "iic_slave.h"
25 // The timeout period to wait for a response from the client, expressed in milliseconds
26 // This is converted to timer ticks by the PIL, so the maximum value is 2147483.
27 // The value should be selected to allow for the longest, slowest transfer
28 // const TInt KClientWaitTime = 2; // 2mS, when debugging might set up to KMaxWaitTime
31 // In an SMP system, use a spin lock to guard access to member variables iTrigger and iInProgress
33 static TSpinLock IicPslSpinLock = TSpinLock(TSpinLock::EOrderGenericIrqLow3);
36 // Callback function for the iHwGuardTimer timer.
38 // Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut
40 void DIicBusChannelSlavePsl::TimeoutCallback(TAny* aPtr)
42 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::TimeoutCallback"));
43 DIicBusChannelSlavePsl *a = (DIicBusChannelSlavePsl*) aPtr;
44 a->iTransactionStatus = KErrTimedOut;
48 // Static method called by the ISR when the Master has ended a transfer
50 // The method checks and reports the Rx and Tx status to the PIL by calling NotifyClient with a bitmask described as follows:.
51 // - If a Tx transfer has ended before all the data was transmitted, bitmask = (ETxAllBytes | ETxOverrun)
52 // - If a Tx transfer has ended and all the data was transmitted, bitmask = ETxAllBytes
53 // - If a Rx transfer has ended before the expected amount of data was received, bitmask = (ERxAllBytes | ERxUnderrun)
54 // - If a Rx transfer has ended and the expected amount of data was received, bitmask = ERxAllBytes
56 void DIicBusChannelSlavePsl::NotifyClientEnd(DIicBusChannelSlavePsl* aPtr)
58 __KTRACE_OPT(KIIC, Kern::Printf("NotifyClientEnd, iTrigger %x", aPtr->iTrigger));
60 // Since a transfer has ended, may wish to disable interrupts at this point
61 // This will likely be supported with calls similar to the following:
62 // AsspRegister::Write32(aPtr->iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
63 // Interrupt::Disable(aPtr->iRxInterruptId);
64 // Interrupt::Disable(aPtr->iTxInterruptId);
66 // iTrigger will have bits ETransmit and EReceive set according to the operation requested in the call to DoRequest
67 // Use variable flag for the bitmask to pass into the PIL method NotifyClient
69 if(aPtr->iTrigger & EReceive)
71 // Requested Rx operation has ended - check for RxUnderrun
73 if(aPtr->iRxDataEnd != aPtr->iRxData)
78 if(aPtr->iTrigger & ETransmit)
80 // Requested Tx operation has ended - check for RxOverrun
82 if(aPtr->iTxDataEnd != aPtr->iTxData)
87 aPtr->NotifyClient(flag);
93 // The ISR handler identifies the cause of the interrupt that lead to its invocation:
94 // if the cause was transfer-related, it calls the PIL function NotifyClient to report a summary of the transfer status;
95 // if the cause was completion of asynchronous channel capture, PIL function ChanCaptureCallback is called
97 // The ISR also clears the source of the interrupt, and (for transfer-related interrupts) transfers the next data
98 // between buffers and the hardware and updates the member variable iInProgress to indicate if a transfer has started or
99 // ended. If a transfer has ended before the expected amount of data has been transfered it calls function NotifyClientEnd.
101 void DIicBusChannelSlavePsl::IicPslIsr(TAny* /*aPtr*/)
103 // DIicBusChannelSlavePsl *a = (DIicBusChannelSlavePsl*) aPtr;
105 // TInt intState = 0; // Variable to support use of spin lock
107 // TInt trigger = 0; // Record the Rx and Tx transfers
109 // TUint32 intStatus = 0; // Record of the interrupts that are being reported
111 // Identify the cause of the interrupt. If this can be achieved by reading a single register,
112 // code similar to the following could be used:
113 // intStatus = AsspRegister::Read32(a->iChannelBase + KIntStatusOffset);
115 // Optional (not required if asynchronous channel capture is not supported)
116 // If the cause of the interrupt is completion of asynchronous channel capture, the ISR will check the appropriate
117 // indicator for confirmation of success - for a real PSL, this may be by querying a bitmask in a register. For the template PSL,
118 // however, a dummy member variable (iAsyncConfig) has been used to represent the asynchronous operation instead.
120 // if(iAsyncConfig == 1) // Replace with a check of the indicator that the interrupt was due to asynchrous channel capture
122 // // The PIL function ChanCaptureCallback is now to be invoked. It takes as an argument either KErrNone or a
123 // // system-wide error code to indicate the cause of failure. For a real PSL, the argument would likely be determined
124 // // by reading a bitmask in a status register - but for the template port, just use KErrNone.
126 // a->ChanCaptureCallback(KErrNone);
130 // If an interrupt indicates that a transfer has started, or that it has now ended, (such as a chip select
131 // line transition for a SPI bus the member variable iInProgress should be modified accordingly. This should
132 // be done under the guard of spin lock macros since iInProgress can be accessed in the context of the Client
133 // thread (in DoRequest, ProcessData and InitTransfer). The following structure should be adopted:
134 // intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
135 // <access a->iInProgress>
136 // __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
138 // If a transfer has ended before the expected amount of data has been transfered, function NotifyClientEnd
139 // should be called, as follows:
140 // a->NotifyClientEnd(a);
141 // return; // Return now - the interrupt indicated transfer end, not receipt or transmission of data.
143 // The transfers that had been started are indicated by the bitmask held in member variable iTrigger.
144 // This must be accessed under the guard of a spin lock since it can be accessed in the context of the
145 // Client thread (in DoRequest, ProcessData and InitTransfer). The following structure should be adopted:
146 // intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
147 // trigger = a->iTrigger;
148 // __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
150 // If the interrupt was raised for a Tx event, and a Tx transfer had been started (so the interrupt was not spurious)
151 // then either prepare the next data to send, or, if all the data has been sent, call the PIL function NotifyClient
152 // with bitmask (ETxAllBytes | ETxUnderrun) so that, if the Client specified a ETxUnderrun notification, it will be alerted
153 // and can determine whether another buffer of data should be provide for transmission.
154 // Code similar to the following could be used:
155 // if(intStatus & KTxInterruptBitMask)
157 // if(trigger & ETransmit)
159 // // Interrupt was not spurious
160 // if(a->iTxData == a->iTxDataEnd)
162 // // All the data to be transmitted has been sent, so call the PIL method NotifyClient
163 // a->NotifyClient(ETxAllBytes | ETxUnderrun);
167 // // There is more data to be sent
168 // // TUint8 nextTxValue = *iTxData; // For this example, assumes one byte of data is to be transmitted
169 // // but if operating in 16-bit mode, bytes may need arranging for
172 // // Write to the Tx register with something similar to the following:
173 // // AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue);
175 // iTxData += iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed
176 // // (iWordSize=1), but if operating in 16-bit mode iTxData would be incremented
177 // // by the number of bytes specified in iWordSize
182 // If the interrupt was raised for a Rx event, and a Rx transfer had been started (so the interrupt was not spurious)
183 // read the received data from the hardware to the buffer. If a Rx FIFO is being used, use a loop to drain it - until
184 // the FIFO is empty or the buffer is full. If data remains after the buffer is full, an RxOverrun condition has occurred
185 // - so the PIL function NotifyClient should be called with bitmask (ERxAllBytes | ERxOverrun) so that, if the Client specified
186 // a ERxOverrun notification, it will be alerted and can determine whether another buffer should be provided to continue reception.
187 // Code similar to the following could be used:
188 // if(intStatus & KRxInterruptBitMask)
190 // if(trigger & EReceive)
192 // // Interrupt was not spurious
193 // while(AsspRegister::Read32(a->iChannelBase + KRxFifoLevelOffset))
195 // if((a->iRxData - a->iRxDataEnd) >= a->iWordSize)
197 // // Space remains in the buffer, so copy the received data to it
198 // TUint8 nextRxValue = AsspRegister::Read32(a->iChannelBase + KRxFifoOffset);
199 // *a->iRxData = nextRxValue; // For this example, assumes one byte of data is to be transmitted
200 // // but if operating in 16-bit mode, bytes may need arranging for
203 // a->iRxData += a->iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed
204 // // (iWordSize=1), but if operating in 16-bit mode iRxData would be incremented
205 // // by the number of bytes specified in iWordSize
209 // // The buffer is full but more data has been received - so there is an RxOverrun condition
210 // // Disable the hardware from receiving any more data and call the PIL function NotifyClient
211 // // with bitmask (ERxAllBytes | ERxOverrun).
212 // AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoDisableBitMask);
213 // a->NotifyClient(ERxAllBytes | ERxOverrun);
220 // // If the interrupt was spurious, ignore the data, and reset the FIFO
221 // AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoClearBitMask);
224 // Once the interrupts have been processed, clear the source. If this can be achieve by writing to
225 // a single register, code similar to the following could be used:
226 // AsspRegister::Write32(a->iChannelBase + KIntStatusOffset, KAIntBitMask);
231 // Constructor, first stage
233 // The PSL is responsible for setting the channel number - this is passed as the first parameter to
234 // this overload of the base class constructor
236 DIicBusChannelSlavePsl::DIicBusChannelSlavePsl(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) :
237 DIicBusChannelSlave(aBusType, aChanDuplex, 0), // Base class constructor. Initalise channel ID to zero.
238 iHwGuardTimer(TimeoutCallback, this) // Timer to guard against hardware timeout
240 iChannelNumber = aChannelNumber;
241 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::DIicBusChannelSlavePsl, iChannelNumber = %d\n", iChannelNumber));
245 // Second stage construction
247 // Allocate and initialise objects required by the PSL channel implementation
249 TInt DIicBusChannelSlavePsl::DoCreate()
251 __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelSlavePsl::DoCreate, ch: %d \n", iChannelNumber));
255 // PIL Base class initialization.
259 // At a minimum, this function must set the channel's unique channel ID.
260 // When the channel is captured, this value will be combined with an instance count
261 // provided by the PIL to generate a value that will be used by a client as a unique
262 // identifer in subsequent calls to the Slave API.
264 // There is no set format for the ID, it just needs to be unique.
265 // Un-comment and complete the following line:
268 // This method may also be concerned with setting the base register address iChannelBase), and allocating
269 // any objects that will be required to support operaton until the channel is deleted.
271 // Un-comment and complete the following line:
277 // static method used to construct the DIicBusChannelSlavePsl object.
278 DIicBusChannelSlavePsl* DIicBusChannelSlavePsl::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex)
280 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::NewL(): aChannelNumber = %d, BusType =%d", aChannelNumber, aBusType));
281 DIicBusChannelSlavePsl *pChan = new DIicBusChannelSlavePsl(aChannelNumber, aBusType, aChanDuplex);
283 TInt r = KErrNoMemory;
286 r = pChan->DoCreate();
298 // Validates the configuration information specified by the client when capturing the channel
300 // Called by the PIL as part of the Slave CaptureChannel processing
302 // If the pointer to the header is NULL, return KErrArgument.
303 // If the content of the header is not valid for this channel, return KErrNotSupported.
305 TInt DIicBusChannelSlavePsl::CheckHdr(TDes8* aHdrBuff)
315 // Check that the contents of the header are valid
317 // The header will be specific to a particular bus type. Using a fictional
318 // bus type Abc,code similar to the following could be used to validate each
319 // member of the header:
321 // TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff;
322 // TConfigAbcV01 &abcHeader = (*headerBuf)();
323 // if( (abcHeader.iHeaderMember < ESomeMinValue) ||
324 // (abcHeader.iHeaderMember > ESomeMaxValue))
326 // __KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember));
327 // r = KErrNotSupported;
331 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::CheckHdr() r %d", r));
337 // Method called in the context of the client thread, as a consequence of the PSL invocation of the
338 // PIL method NotifyClient when a bus event occurs.
340 // This method updates the bitmask of requested operations (held in member variable iTrigger) and the
341 // PIL counts of data received and transmitted. If the event was a bus error, the bitmask of requested operations
344 void DIicBusChannelSlavePsl::ProcessData(TInt aTrigger, TIicBusSlaveCallback* aCb)
346 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::ProcessData(), trigger: %x\n", aTrigger));
350 // If using the iInProgress member variable to indicate transitions on a chip-select line, and an interrupt
351 // occurred as a transfer was to end, then must ensure the transmission of data has ceased.
353 // Must use spin lock to guard access since iInProgress is accessed by the ISR
356 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
357 inProgress = iInProgress;
358 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
360 if(!inProgress && // Transfer has now ended
361 (aTrigger & (ERxAllBytes | ETxAllBytes))) // Master has not yet finished transferring data
363 // Use the guard timer to make sure that transfer ends with an expected time - if this does not cease
364 // before the timer expires, iTransactionStatus will be set to KErrTimedOut by the callback function TimeoutCallback
366 // Poll the relevant register to check for transfer activity, using code similar to the following:
367 // TInt8 transferring = AsspRegister::Read32(iChannelBase + KStatusRegisterOffset) & KTransferringBitMask);
368 // For the template port, use a dummy variable instead of the register access (transferring = 1)
370 TInt8 transferring = 1;
371 iTransactionStatus = KErrNone;
372 iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
374 while((iTransactionStatus == KErrNone) &&
375 transferring); // Replace transferring with a register read, as described above
377 // At this point, either the transfer has ceased, or the timer expired - in either case, may disable the interrupt
378 // for the transfer now, using code similar to the following:
379 // AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask);
381 // Check for guard timer expiry
382 if(iTransactionStatus != KErrNone)
384 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::ProcessData - Transaction timed-out"));
389 iHwGuardTimer.Cancel();
392 // If all transfer activity has now ceased, clear iTrigger
393 // Must use spin lock to guard access since iInProgress is accessed by the ISR
395 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
397 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
400 // If the PSL called the PIL function NotifyClient to indicate transfer activity (or error), the reason
401 // will be specified as a bitmask in aTrigger
402 // - if a Rx event occurred, the ERxAllBytes flag will be set
403 // - if a Tx event occurred, the ETxAllBytes flag will be set
404 // - if a bus error occurred, the EGeneralBusError flag will be set
406 if(aTrigger & ERxAllBytes)
408 __KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Buf: %x\n", iRxData));
409 __KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Bufend: %x\n", iRxDataEnd));
411 // Clear the internal EReceive flag
412 // This must be done under guard of a spin lock since iTrigger is accessed by the ISR
413 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
414 iTrigger &= ~EReceive;
415 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
417 // Update the PIL count of Rx data (in the Callback object)
418 aCb->SetRxWords(iNumRxWords - ((iRxDataEnd - iRxData) / iWordSize));
421 if(aTrigger & ETxAllBytes)
423 __KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Buf: %x\n", iTxData));
424 __KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Bufend: %x\n", iTxDataEnd));
426 // Clear the internal ETransmit flag..
427 // This must be done under guard of a spin lock since iTrigger is accessed by the ISR
428 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
429 iTrigger &= ~ETransmit;
430 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
432 // Update the PIL count of Tx data (in the Callback object)
433 aCb->SetTxWords(iNumTxWords - ((iTxDataEnd - iTxData) / iWordSize));
436 if(aTrigger & EGeneralBusError)
438 __KTRACE_OPT(KIIC, Kern::Printf("BusError.."));
440 // Clear and disable relevant interrupts, possibly using code similar to the following:
441 // AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask);
443 // Clear internal flags
444 // This must be done under guard of a spin lock since iTrigger is accessed by the ISR
445 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
447 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
450 // Set the callback's trigger, for use by the PIL
451 aCb->SetTrigger(aTrigger | aCb->GetTrigger());
456 // Method to initialise the hardware in accordance with the data provided by the Client
457 // in the configuration header when capturing the channel
459 // This method is called from DoRequest and is expected to return a value to indicate success
460 // or a system wide error code to inform of the failure
462 TInt DIicBusChannelSlavePsl::ConfigureInterface()
464 __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
468 // The header is stored in member variable iConfigHeader, and will be specific to a particular bus type.
469 // Using a fictional bus type Abc, code similar to the following could be used to access each
470 // member of the header:
472 // TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) iConfigHeader;
473 // TConfigAbcV01 &abcHeader = (*headerBuf)();
474 // TInt value = abcHeader.iTintMember;
476 // Initialising the hardware may be achieved with calls similar to the following:
477 // AsspRegister::Write32(a->iChannelBase + KBusModeControlOffset, KIicPslModeControlBitMask);
478 // GPIO::SetPinMode(aPinId, GPIO::EEnabled);
480 // Binding an ISR may be achieved with calls similar to the following:
481 // r = Interrupt::Bind(iRxInterruptId, DIicBusChannelSlavePsl::IicPslIsr, this);
482 // r = Interrupt::Bind(iTxInterruptId, DIicBusChannelSlavePsl::IicPslIsr, this);
483 // Enabling interrupts may be achieved with calls similar to the following:
484 // r = Interrupt::Enable(iRxInterruptId);
485 // r = Interrupt::Enable(iTxInterruptId);
487 // Modifying a hardware register may not be a zero-delay operation. The member variable iHwGuardTimer could be used to guard a
488 // continuous poll of the hardware register that checks for the required change in the setting; TimeoutCallback is already
489 // assigned as the callback function for iHwGaurdTimer, and it modifies member variable iTransactionStatus to indicate a timeout
490 // - so the two could be used together as follows:
491 // iTransactionStatus = KErrNone;
492 // iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
493 // while((iTransactionStatus == KErrNone) &&
494 // AsspRegister::Read32(iChannelBase + KRegisterOffset) & KRegisterFlagBitMask);
495 // if(iTransactionStatus != KErrNone)
501 // iHwGuardTimer.Cancel();
504 // DoRequest checks the return value so the variable r should be modified in the event of failure with a system-wide error code
505 // for example, if a register could not be modified,
507 // __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface failed with error %d\n",r));
512 // Method to start asynchronous initialisation of the hardware, in accordance with the data provided by the Client
513 // in the configuration header when capturing the channel. This differs from ConfigureInterface in that it
514 // merely starts the initialisation, then returns immediately;
516 // The PSL is expected to be implemented as an asynchronous state machine, where events (for example hardware
517 // interrupts, or timer expiry) invoke callback functions that advance the state machine to the next state. Once
518 // all the required states have been transitioned, so that the PSL part of the CaptureChannel processing is
519 // complete, the ISR should be invoked, which will then call PIL method ChanCaptureCallback
521 // This method is called from DoRequest and is expected to return a value to indicate success
522 // or a system wide error code to inform of the failure
524 TInt DIicBusChannelSlavePsl::AsynchConfigureInterface()
526 __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
528 // TInt r = KErrNone; // A real implementation would use this as the return value to indicate success / failure
530 // Precisely what processing is done to 'start' the asynchronous processing is entirely platform-specific;
531 // it may be the set-up and activation of a long-running operation that completes asynchronously. Regardless of what
532 // is done, its completion is expected to result in the ISR being run.
534 // Whatever the operation, there must be some means of the ISR recognising that an asynchronous initialisation has
536 // In a real PSL, this may be be checking a bitmask in a status register. For the template PSL, however,
537 // a dummy class member will be used (iAsyncConfig)
538 // Since this member will be accessed by the ISR, it should, strictly speaking, be accessed under the guard of a spin lock
540 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
542 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
544 return KErrNone; // A real implementation would return an indication of success / failure
547 // Method called from DoRequest to start Tx and-or Rx transfer.
549 // The method will initialise the hardware and pointers used to manage transfers, before returning a value to report success
550 // (KErrNone) or a system-wide error code that indicates the cause of failure.
552 TInt DIicBusChannelSlavePsl::InitTransfer()
554 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::InitTransfer()"));
558 // Local copies of member variables that must be accessed in a synchronised manner
564 // Check if a transfer is already in progress.
565 // If variable iInProgress is being used, this must be determined in a synchronised manner because the ISR modifies it.
566 // Bus types that do not rely on chip-select transitions may use an alternative method to indicate if a transfer is in
568 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
569 inProgress = iInProgress;
570 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
574 // If no transfers are in progress, it may be necessary to initialise the hardware to support those that
575 // are being requested. This may include FIFO and interrupt initialisation,
577 // Initialising the hardware may be achieved with calls similar to the following:
578 // AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslModeControlBitMask);
579 // GPIO::SetPinMode(aPinId, GPIO::EEnabled);
582 // Check the current operations. This must be determined in a synchronised manner because ProcessData
583 // runs in the context of the Client thread and it modifies the value of iTrigger
584 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
586 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
588 if(trigger & ETransmit)
590 // If Tx transfers were not previously active, it may be necessary to initialise the Tx hardware here, e.g.
591 // AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslTxModeBitMask);
593 // Initialise the Tx pointers
594 iTxData = iTxBuf + (iWordSize * iTxOffset);
595 iTxDataEnd = iTxData + (iWordSize * iNumTxWords);
597 __KTRACE_OPT(KIIC, Kern::Printf("Tx Buf: %x", iTxData));
598 __KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd));
600 // If using a FIFO, copy the data to it until either the FIFO is full or all the data has been copied
601 // This could be achieved with something similar to the following lines:
602 // while(AsspRegister::Read32(iChannelBase + KFifoLevelOffset) <= (KFifoMaxLevel - iWordSize) &&
603 // iTxData != iTxDataEnd)
604 // For the template port, will just use a dummy variable (dummyFifoLvlChk )in place of the register read
605 TInt dummyFifoLvlChk = 0;
606 while((dummyFifoLvlChk) && // Replace this dummy variable with a read of the hardware
607 (iTxData != iTxDataEnd))
609 // TUint8 nextTxValue = *iTxData; // For this example, assumes one byte of data is to be transmitted
610 // but if operating in 16-bit mode, bytes may need arranging for
613 // Write to the Tx register with something similar to the following:
614 // AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue);
616 iTxData += iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed
617 // (iWordSize=1), but if operating in 16-bit mode iTxData would be incremented
618 // by the number of bytes specified in iWordSize
620 // 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 __KTRACE_OPT(KIIC, Kern::Printf("After adding:\n\rTx Buf: %x", iTxData));
623 __KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd));
626 if(trigger & EReceive)
628 // Initialise the Rx pointers
629 iRxData = iRxBuf + (iWordSize * iRxOffset);
630 iRxDataEnd = iRxData + (iWordSize * iNumRxWords);
632 __KTRACE_OPT(KIIC, Kern::Printf("Rx Buffer: %x", iRxData));
633 __KTRACE_OPT(KIIC, Kern::Printf("Rx Bufend: %x", iRxDataEnd));
635 // If Rx transfers were not previously active, it may be necessary to initialise the Rx hardware here, e.g.
636 // AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslRxModeBitMask);
639 // If there is some common configuration required to support Rx, Tx transfers, may do it here
645 // The gateway function for PSL implementation
647 // This method is called by the PIL to perform one or more operations indicated in the bitmask aOperation,
648 // which corresponds to members of the TPslOperation enumeration.
650 TInt DIicBusChannelSlavePsl::DoRequest(TInt aOperation)
652 __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelSlavePsl::DoRequest, Operation 0x%x\n", aOperation));
657 if (aOperation & EAsyncConfigPwrUp)
659 // The PIL has requested asynchronous operation of CaptureChannel.
660 // The PSL should start the processing required for a channel to be captured, and then return immediately with
661 // error code KErrNone (if the processing was started without error), so that the client thread will be unblocked.
662 // The PSL is expected to be implemented as an asynchronous state machine, where events (for example hardware
663 // interrupts, or timer expiry) invoke callback functions that advance the state machine to the next state. Once
664 // all the required states have been transitioned, so that the PSL part of the CaptureChannel processing is
665 // complete, the PSL should call the PIL function ChanCaptureCallback - this will lead to the Client-provided
666 // callback being executed in the context of the client thread
668 __KTRACE_OPT(KIIC, Kern::Printf("EAsyncConfigPwrUp"));
669 r = AsynchConfigureInterface();
672 __KTRACE_OPT(KIIC, Kern::Printf("AsynchConfigureInterface returned %d\n", r));
677 if (aOperation & ESyncConfigPwrUp)
679 // The PIL has requested synchronous operation of CaptureChannel.
680 // The PSL should perform the processing required for a channel to be captured, and return a system-wide error
681 // code when this is complete to indicate the status of the capture.
682 // Capturing a channel is expected to include initialisation of the hardware to enable operation in accordance
683 // with the configuration specified in the PIL member variable iConfigHeader, which holds the configuration
684 // specified by the Client.
686 __KTRACE_OPT(KIIC, Kern::Printf("ESyncConfigPwrUp"));
687 r = ConfigureInterface();
690 __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface returned %d\n", r));
695 if (aOperation & ETransmit)
697 // The PIL has requested that a Tx operation be started.
698 // Since the SPL may support simultaneous Rx and Tx operations, just set the flag in the iTrigger bitmask to
699 // indicate what has been requested. If both Rx and Tx operations are requested, and one completes ahead of the other,
700 // requiring the Client to provide a new buffer and associated call to DoRequest (as is the case if the Master wishes
701 // to transfer more data than the Slave buffer supported), it is possible that the other transfer could complete while
702 // this function is running; consequently, it may attempt to access iTrigger, and so cause data corruption. To cater for
703 // such situations, use a spin lock to guard access to iTrigger.
704 // When the same check has been performed for Rx, call the InitTransfer function to start the required transfers.
706 __KTRACE_OPT(KIIC, Kern::Printf("ETransmit"));
707 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
708 iTrigger |= ETransmit;
709 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
712 if (aOperation & EReceive)
714 // The PIL has requested that a Rx operation be started.
715 // Since the SPL may support simultaneous Rx and Tx operations, just set the flag in the iTrigger bitmask to
716 // indicate what has been requested. If both Rx and Tx operations are requested, and one completes ahead of the other,
717 // requiring the Client to provide a new buffer and associated call to DoRequest (as is the case if the Master wishes
718 // to transfer more data than the Slave buffer supported), it is possible that the other transfer could complete while
719 // this function is running; consequently, it may attempt to access iTrigger, and so cause data corruption. To cater for
720 // such situations, use a spin lock to guard access to iTrigger.
721 // When the same check has been performed for Tx, call the InitTransfer function to start the required transfers.
723 __KTRACE_OPT(KIIC, Kern::Printf("EReceive"));
724 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
725 iTrigger |= EReceive;
726 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
729 if (aOperation & (EReceive | ETransmit))
731 // This code should only be executed once it has been checked whether Rx and Tx operations are required.
735 if (aOperation & EAbort)
737 // The PIL has requested that the current transaction be aborted.
738 // This is the case if the Client has not responded within an expected time to specify the next steps in
739 // the transaction processing. The time allowed is specified by calling PIL function SetClientWaitTime, otherwise
740 // the time defaults to KSlaveDefCWaitTime.
741 // If the PSL is able to satisfy this request it should, at a minimum, disable interrupts and update the member
742 // variables that indicate a transaction is in progress. If the PSL is unable to satisfy the request then the same
743 // behaviour will follow as if this request had not been made, so there is no point in modifying the state variables.
744 // If both Rx and Tx operations had been requested, and one completes ahead of the other, it is possible that the other
745 // transfer could complete while this function is running; consequently, it may attempt to access iTrigger and iInProgress,
746 // and so cause data corruption. To cater for such situations, use a spin lock to guard access to iTrigger.
747 // The PIL makes no assumptions of whether the PSL can support this request or not, and does not check the return
748 // value - so there is no need to set one.
750 TUint8 dummyCanAbort = 1; // Dummy variable to represent a check of if it is possible to abort the current transaction
751 __KTRACE_OPT(KIIC, Kern::Printf("EAbort"));
752 intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
755 // The spin lock has been acquired, so it is safe to modify data and hardware registers that may be accessed as part of
756 // interrupt processing performed by an ISR - this is assuming that the ISR has been written to acquire the same spin lock.
757 // Limit the processing to only that which is necessary to be processed under spin lock control, so as to not delay other
758 // threads of execution that are waiting for the spin lock to be freed.
759 // Hardware may be configured using code similar to the following:
760 // AsspRegister::Write32(iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
761 iInProgress = EFalse;
764 __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
765 // Having released the spin lock, now perform any actions that are not affected by pre-emption by an ISR, this may include code
766 // such as the following
767 // Interrupt::Disable(iRxInterruptId);
768 // Interrupt::Disable(iTxInterruptId);
771 if (aOperation & EPowerDown)
773 // The PIL has requested that the channel be released.
774 // If this channel is not part of a MasterSlave channel, the next Client will operate in Slave mode. In this case, it may only
775 // be necessary to disable interrupts, and reset the channel hardware.
776 // If this channel represents the Slave of a MasterSlave channel, it is possible that some of the hardware is shared between the
777 // Master and Slave sub-channels. Since it may not be known whether the next Client of the parent channel will require operation
778 // in either Master or Slave mode, some additional processing may be required to allow for subsequent Master operation (for example.
779 // unbinding an interrupt).
781 __KTRACE_OPT(KIIC, Kern::Printf("EPowerDown"));
783 // Resetting the hardware may be achieved with calls similar to the following:
784 // AsspRegister::Write32(iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
785 // GPIO::SetPinMode(aPinId, GPIO::EDisabled);
787 // Disable interrupts may be achieved with calls similar to the following:
788 // Interrupt::Disable(iRxInterruptId);
789 // Interrupt::Disable(iTxInterruptId);
791 // Unbinding an ISR may be achieved with calls similar to the following:
792 // Interrupt::Unbind(iRxInterruptId);
793 // Interrupt::Unbind(iTxInterruptId);
795 // The PIL checks the return value so the variable r should be modified in the event of failure with a system-wide error code
796 // for example, if a register could not be modified,
798 // __KTRACE_OPT(KIIC, Kern::Printf("EPowerDown failed with error %d\n",r));