|
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 |