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