0
|
1 |
/*
|
|
2 |
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
3 |
* All rights reserved.
|
|
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".
|
|
8 |
*
|
|
9 |
* Initial Contributors:
|
|
10 |
* Nokia Corporation - initial contribution.
|
|
11 |
*
|
|
12 |
* Contributors:
|
|
13 |
*
|
|
14 |
* Description:
|
|
15 |
*
|
|
16 |
*/
|
|
17 |
|
|
18 |
|
|
19 |
#include <drivers/iic.h>
|
|
20 |
#include <drivers/iic_channel.h>
|
|
21 |
// #include <gpio.h> // Include if using GPIO functionality
|
|
22 |
#include "iic_psl.h"
|
|
23 |
#include "iic_slave.h"
|
|
24 |
|
|
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
|
|
29 |
|
|
30 |
|
|
31 |
// In an SMP system, use a spin lock to guard access to member variables iTrigger and iInProgress
|
|
32 |
#ifdef __SMP__
|
|
33 |
static TSpinLock IicPslSpinLock = TSpinLock(TSpinLock::EOrderGenericIrqLow3);
|
|
34 |
#endif
|
|
35 |
|
|
36 |
// Callback function for the iHwGuardTimer timer.
|
|
37 |
//
|
|
38 |
// Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut
|
|
39 |
//
|
|
40 |
void DIicBusChannelSlavePsl::TimeoutCallback(TAny* aPtr)
|
|
41 |
{
|
|
42 |
__KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::TimeoutCallback"));
|
|
43 |
DIicBusChannelSlavePsl *a = (DIicBusChannelSlavePsl*) aPtr;
|
|
44 |
a->iTransactionStatus = KErrTimedOut;
|
|
45 |
}
|
|
46 |
|
|
47 |
|
|
48 |
// Static method called by the ISR when the Master has ended a transfer
|
|
49 |
//
|
|
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
|
|
55 |
//
|
|
56 |
void DIicBusChannelSlavePsl::NotifyClientEnd(DIicBusChannelSlavePsl* aPtr)
|
|
57 |
{
|
|
58 |
__KTRACE_OPT(KIIC, Kern::Printf("NotifyClientEnd, iTrigger %x", aPtr->iTrigger));
|
|
59 |
|
|
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);
|
|
65 |
|
|
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
|
|
68 |
TInt flag = 0;
|
|
69 |
if(aPtr->iTrigger & EReceive)
|
|
70 |
{
|
|
71 |
// Requested Rx operation has ended - check for RxUnderrun
|
|
72 |
flag = ERxAllBytes;
|
|
73 |
if(aPtr->iRxDataEnd != aPtr->iRxData)
|
|
74 |
{
|
|
75 |
flag |= ERxUnderrun;
|
|
76 |
}
|
|
77 |
}
|
|
78 |
if(aPtr->iTrigger & ETransmit)
|
|
79 |
{
|
|
80 |
// Requested Tx operation has ended - check for RxOverrun
|
|
81 |
flag |= ETxAllBytes;
|
|
82 |
if(aPtr->iTxDataEnd != aPtr->iTxData)
|
|
83 |
{
|
|
84 |
flag |= ETxOverrun;
|
|
85 |
}
|
|
86 |
}
|
|
87 |
aPtr->NotifyClient(flag);
|
|
88 |
}
|
|
89 |
|
|
90 |
|
|
91 |
// ISR Handler
|
|
92 |
//
|
|
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
|
|
96 |
//
|
|
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.
|
|
100 |
//
|
|
101 |
void DIicBusChannelSlavePsl::IicPslIsr(TAny* /*aPtr*/)
|
|
102 |
{
|
|
103 |
// DIicBusChannelSlavePsl *a = (DIicBusChannelSlavePsl*) aPtr;
|
|
104 |
|
|
105 |
// TInt intState = 0; // Variable to support use of spin lock
|
|
106 |
|
|
107 |
// TInt trigger = 0; // Record the Rx and Tx transfers
|
|
108 |
|
|
109 |
// TUint32 intStatus = 0; // Record of the interrupts that are being reported
|
|
110 |
|
|
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);
|
|
114 |
|
|
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.
|
|
119 |
//
|
|
120 |
// if(iAsyncConfig == 1) // Replace with a check of the indicator that the interrupt was due to asynchrous channel capture
|
|
121 |
// {
|
|
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.
|
|
125 |
// //
|
|
126 |
// a->ChanCaptureCallback(KErrNone);
|
|
127 |
// return;
|
|
128 |
// }
|
|
129 |
|
|
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);
|
|
137 |
//
|
|
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.
|
|
142 |
|
|
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);
|
|
149 |
|
|
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)
|
|
156 |
// {
|
|
157 |
// if(trigger & ETransmit)
|
|
158 |
// {
|
|
159 |
// // Interrupt was not spurious
|
|
160 |
// if(a->iTxData == a->iTxDataEnd)
|
|
161 |
// {
|
|
162 |
// // All the data to be transmitted has been sent, so call the PIL method NotifyClient
|
|
163 |
// a->NotifyClient(ETxAllBytes | ETxUnderrun);
|
|
164 |
// }
|
|
165 |
// else
|
|
166 |
// {
|
|
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
|
|
170 |
// // endianness
|
|
171 |
//
|
|
172 |
// // Write to the Tx register with something similar to the following:
|
|
173 |
// // AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue);
|
|
174 |
//
|
|
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
|
|
178 |
// }
|
|
179 |
// }
|
|
180 |
// }
|
|
181 |
|
|
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)
|
|
189 |
// {
|
|
190 |
// if(trigger & EReceive)
|
|
191 |
// {
|
|
192 |
// // Interrupt was not spurious
|
|
193 |
// while(AsspRegister::Read32(a->iChannelBase + KRxFifoLevelOffset))
|
|
194 |
// {
|
|
195 |
// if((a->iRxData - a->iRxDataEnd) >= a->iWordSize)
|
|
196 |
// {
|
|
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
|
|
201 |
// // endianness
|
|
202 |
//
|
|
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
|
|
206 |
// }
|
|
207 |
// else
|
|
208 |
// {
|
|
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);
|
|
214 |
// break;
|
|
215 |
// }
|
|
216 |
// }
|
|
217 |
// }
|
|
218 |
// else
|
|
219 |
// {
|
|
220 |
// // If the interrupt was spurious, ignore the data, and reset the FIFO
|
|
221 |
// AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoClearBitMask);
|
|
222 |
// }
|
|
223 |
|
|
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);
|
|
227 |
|
|
228 |
}
|
|
229 |
|
|
230 |
|
|
231 |
// Constructor, first stage
|
|
232 |
//
|
|
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
|
|
235 |
//
|
|
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
|
|
239 |
{
|
|
240 |
iChannelNumber = aChannelNumber;
|
|
241 |
__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::DIicBusChannelSlavePsl, iChannelNumber = %d\n", iChannelNumber));
|
|
242 |
}
|
|
243 |
|
|
244 |
|
|
245 |
// Second stage construction
|
|
246 |
//
|
|
247 |
// Allocate and initialise objects required by the PSL channel implementation
|
|
248 |
//
|
|
249 |
TInt DIicBusChannelSlavePsl::DoCreate()
|
|
250 |
{
|
|
251 |
__KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelSlavePsl::DoCreate, ch: %d \n", iChannelNumber));
|
|
252 |
|
|
253 |
TInt r = KErrNone;
|
|
254 |
|
|
255 |
// PIL Base class initialization.
|
|
256 |
r = Init();
|
|
257 |
if(r == KErrNone)
|
|
258 |
{
|
|
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.
|
|
263 |
//
|
|
264 |
// There is no set format for the ID, it just needs to be unique.
|
|
265 |
// Un-comment and complete the following line:
|
|
266 |
// iChannelId =
|
|
267 |
|
|
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.
|
|
270 |
//
|
|
271 |
// Un-comment and complete the following line:
|
|
272 |
// iChannelBase =
|
|
273 |
}
|
|
274 |
return r;
|
|
275 |
}
|
|
276 |
|
|
277 |
// static method used to construct the DIicBusChannelSlavePsl object.
|
|
278 |
DIicBusChannelSlavePsl* DIicBusChannelSlavePsl::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex)
|
|
279 |
{
|
|
280 |
__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::NewL(): aChannelNumber = %d, BusType =%d", aChannelNumber, aBusType));
|
|
281 |
DIicBusChannelSlavePsl *pChan = new DIicBusChannelSlavePsl(aChannelNumber, aBusType, aChanDuplex);
|
|
282 |
|
|
283 |
TInt r = KErrNoMemory;
|
|
284 |
if (pChan)
|
|
285 |
{
|
|
286 |
r = pChan->DoCreate();
|
|
287 |
}
|
|
288 |
if (r != KErrNone)
|
|
289 |
{
|
|
290 |
delete pChan;
|
|
291 |
pChan = NULL;
|
|
292 |
}
|
|
293 |
|
|
294 |
return pChan;
|
|
295 |
}
|
|
296 |
|
|
297 |
|
|
298 |
// Validates the configuration information specified by the client when capturing the channel
|
|
299 |
//
|
|
300 |
// Called by the PIL as part of the Slave CaptureChannel processing
|
|
301 |
//
|
|
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.
|
|
304 |
//
|
|
305 |
TInt DIicBusChannelSlavePsl::CheckHdr(TDes8* aHdrBuff)
|
|
306 |
{
|
|
307 |
TInt r = KErrNone;
|
|
308 |
|
|
309 |
if(!aHdrBuff)
|
|
310 |
{
|
|
311 |
r = KErrArgument;
|
|
312 |
}
|
|
313 |
else
|
|
314 |
{
|
|
315 |
// Check that the contents of the header are valid
|
|
316 |
//
|
|
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:
|
|
320 |
//
|
|
321 |
// TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff;
|
|
322 |
// TConfigAbcV01 &abcHeader = (*headerBuf)();
|
|
323 |
// if( (abcHeader.iHeaderMember < ESomeMinValue) ||
|
|
324 |
// (abcHeader.iHeaderMember > ESomeMaxValue))
|
|
325 |
// {
|
|
326 |
// __KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember));
|
|
327 |
// r = KErrNotSupported;
|
|
328 |
// }
|
|
329 |
|
|
330 |
}
|
|
331 |
__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::CheckHdr() r %d", r));
|
|
332 |
|
|
333 |
return r;
|
|
334 |
}
|
|
335 |
|
|
336 |
|
|
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.
|
|
339 |
//
|
|
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
|
|
342 |
// is cleared.
|
|
343 |
//
|
|
344 |
void DIicBusChannelSlavePsl::ProcessData(TInt aTrigger, TIicBusSlaveCallback* aCb)
|
|
345 |
{
|
|
346 |
__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::ProcessData(), trigger: %x\n", aTrigger));
|
|
347 |
|
|
348 |
TInt intState;
|
|
349 |
|
|
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.
|
|
352 |
//
|
|
353 |
// Must use spin lock to guard access since iInProgress is accessed by the ISR
|
|
354 |
//
|
|
355 |
TInt inProgress;
|
|
356 |
intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
|
|
357 |
inProgress = iInProgress;
|
|
358 |
__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
|
|
359 |
//
|
|
360 |
if(!inProgress && // Transfer has now ended
|
|
361 |
(aTrigger & (ERxAllBytes | ETxAllBytes))) // Master has not yet finished transferring data
|
|
362 |
{
|
|
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
|
|
365 |
//
|
|
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)
|
|
369 |
//
|
|
370 |
TInt8 transferring = 1;
|
|
371 |
iTransactionStatus = KErrNone;
|
|
372 |
iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
|
|
373 |
|
|
374 |
while((iTransactionStatus == KErrNone) &&
|
|
375 |
transferring); // Replace transferring with a register read, as described above
|
|
376 |
|
|
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);
|
|
380 |
|
|
381 |
// Check for guard timer expiry
|
|
382 |
if(iTransactionStatus != KErrNone)
|
|
383 |
{
|
|
384 |
__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::ProcessData - Transaction timed-out"));
|
|
385 |
return;
|
|
386 |
}
|
|
387 |
else
|
|
388 |
{
|
|
389 |
iHwGuardTimer.Cancel();
|
|
390 |
}
|
|
391 |
|
|
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
|
|
394 |
//
|
|
395 |
intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
|
|
396 |
iTrigger = 0;
|
|
397 |
__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
|
|
398 |
}
|
|
399 |
|
|
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
|
|
405 |
//
|
|
406 |
if(aTrigger & ERxAllBytes)
|
|
407 |
{
|
|
408 |
__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Buf: %x\n", iRxData));
|
|
409 |
__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Bufend: %x\n", iRxDataEnd));
|
|
410 |
|
|
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);
|
|
416 |
|
|
417 |
// Update the PIL count of Rx data (in the Callback object)
|
|
418 |
aCb->SetRxWords(iNumRxWords - ((iRxDataEnd - iRxData) / iWordSize));
|
|
419 |
}
|
|
420 |
//
|
|
421 |
if(aTrigger & ETxAllBytes)
|
|
422 |
{
|
|
423 |
__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Buf: %x\n", iTxData));
|
|
424 |
__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Bufend: %x\n", iTxDataEnd));
|
|
425 |
|
|
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);
|
|
431 |
|
|
432 |
// Update the PIL count of Tx data (in the Callback object)
|
|
433 |
aCb->SetTxWords(iNumTxWords - ((iTxDataEnd - iTxData) / iWordSize));
|
|
434 |
}
|
|
435 |
//
|
|
436 |
if(aTrigger & EGeneralBusError)
|
|
437 |
{
|
|
438 |
__KTRACE_OPT(KIIC, Kern::Printf("BusError.."));
|
|
439 |
|
|
440 |
// Clear and disable relevant interrupts, possibly using code similar to the following:
|
|
441 |
// AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask);
|
|
442 |
|
|
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);
|
|
446 |
iTrigger = 0;
|
|
447 |
__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
|
|
448 |
}
|
|
449 |
|
|
450 |
// Set the callback's trigger, for use by the PIL
|
|
451 |
aCb->SetTrigger(aTrigger | aCb->GetTrigger());
|
|
452 |
}
|
|
453 |
|
|
454 |
|
|
455 |
|
|
456 |
// Method to initialise the hardware in accordance with the data provided by the Client
|
|
457 |
// in the configuration header when capturing the channel
|
|
458 |
//
|
|
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
|
|
461 |
//
|
|
462 |
TInt DIicBusChannelSlavePsl::ConfigureInterface()
|
|
463 |
{
|
|
464 |
__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
|
|
465 |
|
|
466 |
TInt r = KErrNone;
|
|
467 |
|
|
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:
|
|
471 |
//
|
|
472 |
// TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) iConfigHeader;
|
|
473 |
// TConfigAbcV01 &abcHeader = (*headerBuf)();
|
|
474 |
// TInt value = abcHeader.iTintMember;
|
|
475 |
|
|
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);
|
|
479 |
|
|
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);
|
|
486 |
|
|
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)
|
|
496 |
// {
|
|
497 |
// r = KErrGeneral;
|
|
498 |
// }
|
|
499 |
// else
|
|
500 |
// {
|
|
501 |
// iHwGuardTimer.Cancel();
|
|
502 |
// }
|
|
503 |
|
|
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,
|
|
506 |
// r = KErrGeneral;
|
|
507 |
// __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface failed with error %d\n",r));
|
|
508 |
return r;
|
|
509 |
}
|
|
510 |
|
|
511 |
|
|
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;
|
|
515 |
//
|
|
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
|
|
520 |
//
|
|
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
|
|
523 |
//
|
|
524 |
TInt DIicBusChannelSlavePsl::AsynchConfigureInterface()
|
|
525 |
{
|
|
526 |
__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
|
|
527 |
|
|
528 |
// TInt r = KErrNone; // A real implementation would use this as the return value to indicate success / failure
|
|
529 |
|
|
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.
|
|
533 |
//
|
|
534 |
// Whatever the operation, there must be some means of the ISR recognising that an asynchronous initialisation has
|
|
535 |
// been performed
|
|
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
|
|
539 |
TInt intState;
|
|
540 |
intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
|
|
541 |
iAsyncConfig = 1;
|
|
542 |
__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
|
|
543 |
|
|
544 |
return KErrNone; // A real implementation would return an indication of success / failure
|
|
545 |
}
|
|
546 |
|
|
547 |
// Method called from DoRequest to start Tx and-or Rx transfer.
|
|
548 |
//
|
|
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.
|
|
551 |
//
|
|
552 |
TInt DIicBusChannelSlavePsl::InitTransfer()
|
|
553 |
{
|
|
554 |
__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::InitTransfer()"));
|
|
555 |
|
|
556 |
TInt r = KErrNone;
|
|
557 |
|
|
558 |
// Local copies of member variables that must be accessed in a synchronised manner
|
|
559 |
TInt inProgress;
|
|
560 |
TInt trigger;
|
|
561 |
|
|
562 |
TInt intState;
|
|
563 |
|
|
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
|
|
567 |
// progress
|
|
568 |
intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
|
|
569 |
inProgress = iInProgress;
|
|
570 |
__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
|
|
571 |
|
|
572 |
if(!inProgress)
|
|
573 |
{
|
|
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,
|
|
576 |
//
|
|
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);
|
|
580 |
}
|
|
581 |
|
|
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);
|
|
585 |
trigger = iTrigger;
|
|
586 |
__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
|
|
587 |
|
|
588 |
if(trigger & ETransmit)
|
|
589 |
{
|
|
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);
|
|
592 |
|
|
593 |
// Initialise the Tx pointers
|
|
594 |
iTxData = iTxBuf + (iWordSize * iTxOffset);
|
|
595 |
iTxDataEnd = iTxData + (iWordSize * iNumTxWords);
|
|
596 |
|
|
597 |
__KTRACE_OPT(KIIC, Kern::Printf("Tx Buf: %x", iTxData));
|
|
598 |
__KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd));
|
|
599 |
|
|
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))
|
|
608 |
{
|
|
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
|
|
611 |
// endianness
|
|
612 |
|
|
613 |
// Write to the Tx register with something similar to the following:
|
|
614 |
// AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue);
|
|
615 |
|
|
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
|
|
619 |
}
|
|
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
|
|
621 |
|
|
622 |
__KTRACE_OPT(KIIC, Kern::Printf("After adding:\n\rTx Buf: %x", iTxData));
|
|
623 |
__KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd));
|
|
624 |
}
|
|
625 |
|
|
626 |
if(trigger & EReceive)
|
|
627 |
{
|
|
628 |
// Initialise the Rx pointers
|
|
629 |
iRxData = iRxBuf + (iWordSize * iRxOffset);
|
|
630 |
iRxDataEnd = iRxData + (iWordSize * iNumRxWords);
|
|
631 |
|
|
632 |
__KTRACE_OPT(KIIC, Kern::Printf("Rx Buffer: %x", iRxData));
|
|
633 |
__KTRACE_OPT(KIIC, Kern::Printf("Rx Bufend: %x", iRxDataEnd));
|
|
634 |
|
|
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);
|
|
637 |
}
|
|
638 |
|
|
639 |
// If there is some common configuration required to support Rx, Tx transfers, may do it here
|
|
640 |
|
|
641 |
return r;
|
|
642 |
}
|
|
643 |
|
|
644 |
|
|
645 |
// The gateway function for PSL implementation
|
|
646 |
//
|
|
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.
|
|
649 |
//
|
|
650 |
TInt DIicBusChannelSlavePsl::DoRequest(TInt aOperation)
|
|
651 |
{
|
|
652 |
__KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelSlavePsl::DoRequest, Operation 0x%x\n", aOperation));
|
|
653 |
|
|
654 |
TInt r = KErrNone;
|
|
655 |
TInt intState;
|
|
656 |
|
|
657 |
if (aOperation & EAsyncConfigPwrUp)
|
|
658 |
{
|
|
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
|
|
667 |
//
|
|
668 |
__KTRACE_OPT(KIIC, Kern::Printf("EAsyncConfigPwrUp"));
|
|
669 |
r = AsynchConfigureInterface();
|
|
670 |
if (r != KErrNone)
|
|
671 |
{
|
|
672 |
__KTRACE_OPT(KIIC, Kern::Printf("AsynchConfigureInterface returned %d\n", r));
|
|
673 |
}
|
|
674 |
return r;
|
|
675 |
}
|
|
676 |
|
|
677 |
if (aOperation & ESyncConfigPwrUp)
|
|
678 |
{
|
|
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.
|
|
685 |
//
|
|
686 |
__KTRACE_OPT(KIIC, Kern::Printf("ESyncConfigPwrUp"));
|
|
687 |
r = ConfigureInterface();
|
|
688 |
if (r != KErrNone)
|
|
689 |
{
|
|
690 |
__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface returned %d\n", r));
|
|
691 |
return r;
|
|
692 |
}
|
|
693 |
}
|
|
694 |
|
|
695 |
if (aOperation & ETransmit)
|
|
696 |
{
|
|
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.
|
|
705 |
//
|
|
706 |
__KTRACE_OPT(KIIC, Kern::Printf("ETransmit"));
|
|
707 |
intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
|
|
708 |
iTrigger |= ETransmit;
|
|
709 |
__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
|
|
710 |
}
|
|
711 |
|
|
712 |
if (aOperation & EReceive)
|
|
713 |
{
|
|
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.
|
|
722 |
//
|
|
723 |
__KTRACE_OPT(KIIC, Kern::Printf("EReceive"));
|
|
724 |
intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
|
|
725 |
iTrigger |= EReceive;
|
|
726 |
__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
|
|
727 |
}
|
|
728 |
|
|
729 |
if (aOperation & (EReceive | ETransmit))
|
|
730 |
{
|
|
731 |
// This code should only be executed once it has been checked whether Rx and Tx operations are required.
|
|
732 |
r = InitTransfer();
|
|
733 |
}
|
|
734 |
|
|
735 |
if (aOperation & EAbort)
|
|
736 |
{
|
|
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.
|
|
749 |
//
|
|
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);
|
|
753 |
if(dummyCanAbort)
|
|
754 |
{
|
|
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;
|
|
762 |
iTrigger = 0;
|
|
763 |
}
|
|
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);
|
|
769 |
}
|
|
770 |
|
|
771 |
if (aOperation & EPowerDown)
|
|
772 |
{
|
|
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).
|
|
780 |
//
|
|
781 |
__KTRACE_OPT(KIIC, Kern::Printf("EPowerDown"));
|
|
782 |
|
|
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);
|
|
786 |
|
|
787 |
// Disable interrupts may be achieved with calls similar to the following:
|
|
788 |
// Interrupt::Disable(iRxInterruptId);
|
|
789 |
// Interrupt::Disable(iTxInterruptId);
|
|
790 |
|
|
791 |
// Unbinding an ISR may be achieved with calls similar to the following:
|
|
792 |
// Interrupt::Unbind(iRxInterruptId);
|
|
793 |
// Interrupt::Unbind(iTxInterruptId);
|
|
794 |
|
|
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,
|
|
797 |
// r = KErrGeneral;
|
|
798 |
// __KTRACE_OPT(KIIC, Kern::Printf("EPowerDown failed with error %d\n",r));
|
|
799 |
|
|
800 |
}
|
|
801 |
return r;
|
|
802 |
}
|
|
803 |
|