|
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 "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 |
|
20 #include <drivers/iic.h> |
|
21 #include <drivers/iic_channel.h> |
|
22 #include <assp/naviengine/naviengine_priv.h> |
|
23 #include "csi_psl.h" |
|
24 #include "csi_slave.h" |
|
25 |
|
26 #include "hcrconfig.h" |
|
27 #include "hcrconfig_csi.h" |
|
28 |
|
29 // the timeout period to wait for a response from the client. |
|
30 // We can't predict the frequency, at which the Master will be sending us data, |
|
31 // it can be from 130kHz (which is 16,25bytes/ms) up to 16,67MHz(2083 bytes/ms). |
|
32 // Timeout is set for the slowest transfer - for the time that the 32 bytes Tx FIFO would be emptied |
|
33 // (Rx FIFO filled), so 2ms. |
|
34 const TInt KClientWaitTime = 2; // when debugging might set up to KMaxWaitTime |
|
35 |
|
36 // set of interrupt flags used by the driver |
|
37 const TUint32 KSlaveInterruptFlags = |
|
38 KHtCSIControlSSDnIE | // SS signal negative-edge interrupt enable |
|
39 KHtCSIControlSSUpIE | // SS signal positive-edge interrupt enable |
|
40 KHtCSIControlTEndIE | |
|
41 KHtCSIControlRxTrgEn | |
|
42 KHtCSIControlRxTrgIE; |
|
43 |
|
44 |
|
45 #ifdef __SMP__ |
|
46 static TSpinLock CsiSpinLock = TSpinLock(TSpinLock::EOrderGenericIrqLow2); |
|
47 #endif |
|
48 |
|
49 // this is call-back for iHwGuard timer. It is called in the ISR context |
|
50 // if the iHwGuardTimer expires. |
|
51 // It will change the iTransactionStatus to KErrTimedOut to allow exiting from the while-loop.. |
|
52 void DCsiChannelSlave::TimeoutCallback(TAny* aPtr) |
|
53 { |
|
54 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::TimeoutCallback")); |
|
55 DCsiChannelSlave *a = (DCsiChannelSlave*) aPtr; |
|
56 a->iTransactionStatus = KErrTimedOut; |
|
57 } |
|
58 |
|
59 |
|
60 // this is a static method to be called - when SS line is de-asserted. |
|
61 // it disabled the interface and interrupts, and then it checks which flags |
|
62 // need to be specified and then calls NotifyClient using it |
|
63 void DCsiChannelSlave::NotifyClientEnd(DCsiChannelSlave* aPtr) |
|
64 { |
|
65 __KTRACE_OPT(KIIC, Kern::Printf("NotifyClientEnd, iTrigger %x", aPtr->iTrigger)); |
|
66 |
|
67 // disable interrupts and the interface.. |
|
68 AsspRegister::Modify32(aPtr->iChannelBase + KHoCSIModeControl, |
|
69 KHtCSIModeEnable | KSlaveInterruptFlags, 0); |
|
70 |
|
71 // call NotifyClient with xAllBytes (as specified by the Operation) |
|
72 TInt flag = 0; |
|
73 if(aPtr->iTrigger & EReceive) |
|
74 { |
|
75 flag = ERxAllBytes; |
|
76 // if received less data, than the buffer size (i.e the Master de-asserted SS line |
|
77 // before we could fill the whole buffer) - this is the underrun situation |
|
78 if(aPtr->iRxDataEnd != aPtr->iRxData) |
|
79 { |
|
80 __KTRACE_OPT(KIIC, Kern::Printf("Rx Underrun")); |
|
81 flag |= ERxUnderrun; |
|
82 } |
|
83 } |
|
84 |
|
85 if(aPtr->iTrigger & ETransmit) |
|
86 { |
|
87 flag |= ETxAllBytes; |
|
88 // if not everything was transferred, i.e. Master de-asserted SS line |
|
89 // before we could transmit all the data - this is the overrun error.. |
|
90 if(aPtr->iTxDataEnd != aPtr->iTxData) |
|
91 { |
|
92 __KTRACE_OPT(KIIC, Kern::Printf("Tx Overrun")); |
|
93 flag |= ETxOverrun; |
|
94 } |
|
95 } |
|
96 |
|
97 aPtr->NotifyClient(flag); |
|
98 } |
|
99 |
|
100 // ISR Handler |
|
101 void DCsiChannelSlave::CsiIsr(TAny* aPtr) |
|
102 { |
|
103 DCsiChannelSlave *a = (DCsiChannelSlave*) aPtr; |
|
104 TInt intState = 0; |
|
105 |
|
106 // read the interrupt flags - to see, what was causing the interrupt.. |
|
107 TUint32 status = AsspRegister::Read32(a->iChannelBase + KHoCSIIntStatus); |
|
108 |
|
109 // SS signal negative-edge interrupt |
|
110 if (status & KHtCSIIntStatusSSDn) |
|
111 { |
|
112 // falling edge.. |
|
113 TInt pin = AsspRegister::Read32(a->iChannelBase + KHoCSIControl) & KHtCSIControlSSMon; |
|
114 |
|
115 // falling edge.. and if pin active at LOW state - this is the beginning |
|
116 // of the transmission from the Master.. |
|
117 if (a->iSSPinActiveMode == ESpiCSPinActiveLow) |
|
118 { |
|
119 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
120 if (!pin && !a->iInProgress) |
|
121 { |
|
122 a->iInProgress = ETrue; |
|
123 } |
|
124 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
125 } |
|
126 else |
|
127 // falling edge.. and if pin active at HIGH state - this is the end |
|
128 // of the transmission from the Master.. |
|
129 { |
|
130 TInt8 notify = EFalse; |
|
131 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
132 if (pin && a->iInProgress) |
|
133 { |
|
134 a->iInProgress = EFalse; |
|
135 notify = ETrue; |
|
136 } |
|
137 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
138 |
|
139 if(notify) |
|
140 { |
|
141 a->NotifyClientEnd(a); |
|
142 return; |
|
143 } |
|
144 } |
|
145 |
|
146 |
|
147 // clear KHtCSIIntStatusSSDn interrupt.. |
|
148 AsspRegister::Write32(a->iChannelBase + KHoCSIIntStatus, KHtCSIIntStatusSSDn); |
|
149 } |
|
150 |
|
151 // SS signal positive-edge interrupt |
|
152 if (status & KHtCSIIntStatusSSUp) |
|
153 { |
|
154 // rising edge.. |
|
155 TInt pin = AsspRegister::Read32(a->iChannelBase + KHoCSIControl) & KHtCSIControlSSMon; |
|
156 |
|
157 // rising edge.. and if pin active at HIGH state - this is the beginning |
|
158 // of the transmission from the Master.. |
|
159 if (a->iSSPinActiveMode == ESpiCSPinActiveHigh) |
|
160 { |
|
161 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
162 if (pin && !a->iInProgress) |
|
163 { |
|
164 a->iInProgress = ETrue; |
|
165 } |
|
166 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
167 } |
|
168 else |
|
169 // rising edge.. and if pin active at LOW state - this is the END |
|
170 // of the transmission from the Master.. |
|
171 { |
|
172 TInt8 notify = EFalse; |
|
173 |
|
174 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
175 if(pin && a->iInProgress) |
|
176 { |
|
177 a->iInProgress = EFalse; |
|
178 notify = ETrue; |
|
179 } |
|
180 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
181 |
|
182 if(notify) |
|
183 { |
|
184 a->NotifyClientEnd(a); |
|
185 return; |
|
186 } |
|
187 } |
|
188 |
|
189 // clear KHtCSIIntStatusSSUp interrupt.. |
|
190 AsspRegister::Write32(a->iChannelBase + KHoCSIIntStatus, KHtCSIIntStatusSSUp); |
|
191 } |
|
192 |
|
193 TInt trigger = 0; |
|
194 if (status & (KHtCSIIntStatusTEnd | KHtCSIIntStatusRxTrgIE)) |
|
195 { |
|
196 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
197 trigger = a->iTrigger; |
|
198 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
199 } |
|
200 |
|
201 // process TEND end interrupts |
|
202 // this ISR happens every time ONE unit has been transfered.. |
|
203 if (status & KHtCSIIntStatusTEnd) |
|
204 { |
|
205 // clear TxEnd interrupt.. |
|
206 AsspRegister::Write32(a->iChannelBase + KHoCSIIntStatus, KHtCSIIntStatusTEnd); |
|
207 |
|
208 if(trigger & ETransmit) |
|
209 { |
|
210 if(a->iTxData == a->iTxDataEnd) |
|
211 { |
|
212 // if we've moved all the data to the FIFO.. |
|
213 // notify the PIL with ETxAllBytes | ETxUnderrun; |
|
214 // (so that - if the Client set ERxUnderrun - he could provide more data to be sent.. |
|
215 AsspRegister::Modify32(a->iChannelBase + KHoCSIControl, KHtCSIControlTEndIE, 0); |
|
216 a->NotifyClient(ETxAllBytes | ETxUnderrun); |
|
217 } |
|
218 else |
|
219 { |
|
220 // if tere's more data to be sent - copy next item to the FIFO window register. |
|
221 TUint16 val; |
|
222 |
|
223 // in 16bit mode we need to read MSB first.. |
|
224 if(a->iWordSize > 1) |
|
225 { |
|
226 val = (*a->iTxData) >> 8; // MSB shifted down.. |
|
227 val |= *(a->iTxData + 1) & 0xff; // LSB.. |
|
228 } |
|
229 else |
|
230 { |
|
231 val = *a->iTxData; |
|
232 } |
|
233 |
|
234 // copy the data item to the FIFO window register |
|
235 AsspRegister::Write32(a->iChannelBase + KHoCSIOFifo, val); |
|
236 |
|
237 // increment the pointer.. |
|
238 a->iTxData += a->iWordSize; |
|
239 } |
|
240 } |
|
241 } //end of TXEnd processing |
|
242 |
|
243 // process receive threshold interrupt |
|
244 if (status & KHtCSIIntStatusRxTrgIE) |
|
245 { |
|
246 // read data from the FIFO .. |
|
247 if(trigger & EReceive) |
|
248 { |
|
249 while(AsspRegister::Read32(a->iChannelBase + KHoCSIIFifoL)) |
|
250 { |
|
251 // if there's still some place in the buffer - put it into buffer.. |
|
252 if((a->iRxDataEnd - a->iRxData) >= a->iWordSize) |
|
253 { |
|
254 // copy data from the FIFO if tere's more space in the buffer |
|
255 TUint16 val = AsspRegister::Read32(a->iChannelBase + KHoCSIIFifo); |
|
256 |
|
257 // we're big-endian.. (AB).. |
|
258 // so in 16bit mode we need to read MSB first.. |
|
259 if(a->iWordSize > 1) |
|
260 { |
|
261 *a->iRxData = val >> 8; // MSB shifted down.. |
|
262 *(a->iRxData + 1) = val & 0xff; // LSB.. |
|
263 } |
|
264 else |
|
265 { |
|
266 *a->iRxData = val; |
|
267 } |
|
268 |
|
269 // increment the pointer.. |
|
270 a->iRxData += a->iWordSize; |
|
271 } |
|
272 else |
|
273 { |
|
274 // overrun, i.e Slave has sent more data than expected by the client |
|
275 //__KTRACE_OPT(KIIC, Kern::Printf("REND, ERxOverrun")); |
|
276 AsspRegister::Modify32(a->iChannelBase + KHoCSIControl, KHtCSIControlRxTrgIE, 0); |
|
277 a->NotifyClient(ERxAllBytes | ERxOverrun); |
|
278 break; |
|
279 } |
|
280 } |
|
281 } |
|
282 else |
|
283 { |
|
284 // or drop the data, writing 0 to the FIFOL register |
|
285 AsspRegister::Write32(a->iChannelBase + KHoCSIIFifoL, 0); |
|
286 } |
|
287 |
|
288 // Clear the RxThreshold interrupt |
|
289 AsspRegister::Write32(a->iChannelBase + KHoCSIIntStatus, KHtCSIIntStatusRxTrgIE); |
|
290 |
|
291 } // end of reception processing.. |
|
292 } |
|
293 |
|
294 // overloaded constructor |
|
295 DCsiChannelSlave::DCsiChannelSlave(TInt aChannelNumber, |
|
296 const DIicBusChannel::TBusType aBusType, |
|
297 const DIicBusChannel::TChannelDuplex aChanDuplex) : |
|
298 DIicBusChannelSlave(aBusType, aChanDuplex, 0), |
|
299 iHwGuardTimer(TimeoutCallback, this) |
|
300 { |
|
301 iHwTimeoutValue = -1; |
|
302 iChannelNumber = aChannelNumber; |
|
303 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelSlave::DCsiChannelSlave, iChannelNumber = %d\n", iChannelNumber)); |
|
304 } |
|
305 |
|
306 // 2nd stage object construction |
|
307 TInt DCsiChannelSlave::DoCreate() |
|
308 { |
|
309 __KTRACE_OPT(KIIC, Kern::Printf("\nDCsiChannelSlave::DoCreate, ch: %d \n", iChannelNumber)); |
|
310 |
|
311 TUint32 channelBase; |
|
312 |
|
313 HCR::TSettingId channelBaseId; |
|
314 channelBaseId.iCat = KHcrCat_MHA_HWBASE; |
|
315 |
|
316 TInt r = KErrNone; |
|
317 switch (iChannelNumber) |
|
318 { |
|
319 case 0: |
|
320 channelBaseId.iKey = KHcrKey_HwBase_CSI0; |
|
321 break; |
|
322 case 1: |
|
323 channelBaseId.iKey = KHcrKey_HwBase_CSI1; |
|
324 break; |
|
325 default: |
|
326 __KTRACE_OPT(KIIC, Kern::Printf("Wrong ChannelNumber specified (%d)", iChannelNumber)); |
|
327 return KErrArgument; |
|
328 } |
|
329 |
|
330 r = HCR::GetUInt(channelBaseId, channelBase); |
|
331 iChannelBase = channelBase; |
|
332 |
|
333 if(r != KErrNone) |
|
334 { |
|
335 __KTRACE_OPT(KIIC, Kern::Printf("Failed to read HCR setting for category = %d and key = %d\n", channelBaseId.iCat, channelBaseId.iKey)); |
|
336 return r; |
|
337 } |
|
338 |
|
339 //Read the timeout value from HCR |
|
340 if(iHwTimeoutValue == -1) |
|
341 { |
|
342 HCR::TSettingId settingId; |
|
343 // csiTimeout values was not yet read from HCR; read it |
|
344 settingId.iCat = KHcrCat_HWServ_CSI; |
|
345 settingId.iKey = KHcrKey_CSI_Timeout; |
|
346 |
|
347 TInt r = HCR::GetInt(settingId, iHwTimeoutValue); |
|
348 if(r != KErrNone) |
|
349 { |
|
350 __KTRACE_OPT(KIIC, Kern::Printf("Failed to read HCR setting for category = %d and key = %d\n", settingId.iCat, settingId.iKey)); |
|
351 return r; |
|
352 } |
|
353 } |
|
354 |
|
355 // PIL Base class initialization. |
|
356 r = Init(); |
|
357 |
|
358 // set the unique Slave's iChannelId |
|
359 // THis, along with the instance count of opened Slave channels - will be returned to the client |
|
360 // as he captures the Channel (in the referenced aChannelId making an unique ID) - and if the capture is |
|
361 // successful he refers to this channel using the returned ChannelId. |
|
362 |
|
363 // For now, let's just use the combination of iChannelNumber/iChannelBase(register address)) |
|
364 // This might be later replaced by the call into ConfRep to obtain the iChannelId for the PSL. |
|
365 iChannelId = 0xffff & (iChannelNumber + iChannelBase); |
|
366 |
|
367 return r; |
|
368 } |
|
369 |
|
370 // static method used to construct the DCsiChannelSlave object. |
|
371 #ifdef STANDALONE_CHANNEL |
|
372 EXPORT_C |
|
373 #endif |
|
374 DCsiChannelSlave* DCsiChannelSlave::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex) |
|
375 { |
|
376 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelSlave::NewL(): aChannelNumber = %d, BusType =%d", aChannelNumber, aBusType)); |
|
377 DCsiChannelSlave *pChan = new DCsiChannelSlave(aChannelNumber, aBusType, aChanDuplex); |
|
378 |
|
379 TInt r = KErrNoMemory; |
|
380 if (pChan) |
|
381 { |
|
382 r = pChan->DoCreate(); |
|
383 } |
|
384 if (r != KErrNone) |
|
385 { |
|
386 delete pChan; |
|
387 pChan = NULL; |
|
388 } |
|
389 |
|
390 return pChan; |
|
391 } |
|
392 |
|
393 // Validates the various Fields in the transaction header |
|
394 // THis is a pure virtual.. which.. is never called by the PIL?.. |
|
395 TInt DCsiChannelSlave::CheckHdr(TDes8* aHdrBuff) |
|
396 { |
|
397 TInt r = KErrNone; |
|
398 |
|
399 if(!aHdrBuff) |
|
400 { |
|
401 r = KErrArgument; |
|
402 } |
|
403 else |
|
404 { |
|
405 TConfigSpiBufV01* headerBuf = (TConfigSpiBufV01*) aHdrBuff; |
|
406 TConfigSpiV01 &spiHeader = (*headerBuf)(); |
|
407 |
|
408 if(spiHeader.iTransactionWaitCycles > 15) // (can be 0 - 15) |
|
409 { |
|
410 __KTRACE_OPT(KIIC, Kern::Printf("iTransactionWaitCycles not supported")); |
|
411 r = KErrNotSupported; |
|
412 } |
|
413 else |
|
414 { |
|
415 if(spiHeader.iWordWidth != ESpiWordWidth_8 && |
|
416 spiHeader.iWordWidth != ESpiWordWidth_16) |
|
417 { |
|
418 __KTRACE_OPT(KIIC, Kern::Printf("iWordWidth not supported")); |
|
419 r = KErrNotSupported; |
|
420 } |
|
421 } |
|
422 } |
|
423 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelSlave::CheckHdr() r %d", r)); |
|
424 |
|
425 return r; |
|
426 } |
|
427 |
|
428 void DCsiChannelSlave::ProcessData(TInt aTrigger, TIicBusSlaveCallback* aCb) |
|
429 { |
|
430 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelSlave::ProcessData(), trigger: %x, in progress %d", aTrigger, iInProgress)); |
|
431 TInt intState; |
|
432 |
|
433 // if NotifyClient was called due to SS line de-assertion.. |
|
434 TInt inProgress; |
|
435 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
436 inProgress = iInProgress; |
|
437 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
438 |
|
439 if(!inProgress && |
|
440 (aTrigger & (ERxAllBytes | ETxAllBytes))) |
|
441 { |
|
442 __KTRACE_OPT(KIIC, Kern::Printf("Finished, cleaning..")); |
|
443 // we migh be still receiving or transmitting data, so must wait until the end of the transmission |
|
444 // start the guard timer, which - in case of the following while got stucked - will |
|
445 // unblock this dfc by changing iTransactionStatus |
|
446 iTransactionStatus = KErrNone; |
|
447 |
|
448 iHwGuardTimer.OneShot(NKern::TimerTicks(iHwTimeoutValue)); |
|
449 |
|
450 // active wait until the transfer has finished |
|
451 while((iTransactionStatus == KErrNone) && |
|
452 (AsspRegister::Read32(iChannelBase + KHoCSIModeControl) & KHtCSIModeTransferState)); |
|
453 |
|
454 // clear CSIE bit.. |
|
455 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, 0); |
|
456 |
|
457 // check if the the guard timer or transaction timer hasn't expired.. |
|
458 if(iTransactionStatus != KErrNone) |
|
459 { |
|
460 __KTRACE_OPT(KIIC, Kern::Printf("CsiChannelMaster::TransferEndDfc(): Transaction timed-out")); |
|
461 return; |
|
462 } |
|
463 else |
|
464 { |
|
465 iHwGuardTimer.Cancel(); |
|
466 } |
|
467 |
|
468 // clear internal flags |
|
469 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
470 iTrigger = 0; |
|
471 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
472 } |
|
473 |
|
474 // if the transaction was finished |
|
475 // NotifyClient() with ERxAllBytes (and)or ETxAllBytes called, when SS pin is de-asserted |
|
476 // this indicated end of the transmission. Check buffer boundaries, as if Rx or Tx buffers |
|
477 // provided by the client were bigger - this indicates ERxUnderrun or ETxOverrun - accordingly |
|
478 // Rx.. |
|
479 if(aTrigger & ERxAllBytes) |
|
480 { |
|
481 __KTRACE_OPT(KIIC, Kern::Printf("Rx Buf: %x", iRxData)); |
|
482 __KTRACE_OPT(KIIC, Kern::Printf("Rx BufeND: %x", iRxDataEnd)); |
|
483 |
|
484 __KTRACE_OPT(KIIC, Kern::Printf("\n\nTxFifo level %d", AsspRegister::Read32(iChannelBase + KHoCSIOFifoL))); |
|
485 __KTRACE_OPT(KIIC, Kern::Printf("RxFifo level %d\n\n", AsspRegister::Read32(iChannelBase + KHoCSIIFifoL))); |
|
486 |
|
487 // clear the internal EReceive flag.. |
|
488 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
489 iTrigger &= ~EReceive; |
|
490 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
491 |
|
492 // update the buffer information in the Callback object.. |
|
493 aCb->SetRxWords(iNumRxWords - ((iRxDataEnd - iRxData) / iWordSize)); |
|
494 } |
|
495 |
|
496 // Tx... |
|
497 if(aTrigger & ETxAllBytes) |
|
498 { |
|
499 __KTRACE_OPT(KIIC, Kern::Printf("Tx Buf: %x", iTxData)); |
|
500 __KTRACE_OPT(KIIC, Kern::Printf("Tx BufeND: %x", iTxDataEnd)); |
|
501 |
|
502 // set the callback's TxWords value |
|
503 __KTRACE_OPT(KIIC, Kern::Printf("aCb->SetTxWords %d", iNumTxWords - ((iTxDataEnd - iTxData) / iWordSize))); |
|
504 |
|
505 // clear the internal ETransmit flag.. |
|
506 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
507 iTrigger &= ~ETransmit; |
|
508 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
509 |
|
510 // update the buffer information in the Callback object.. |
|
511 aCb->SetTxWords(iNumTxWords - ((iTxDataEnd - iTxData) / iWordSize)); |
|
512 } |
|
513 |
|
514 if(aTrigger & EGeneralBusError) |
|
515 { |
|
516 __KTRACE_OPT(KIIC, Kern::Printf("BusError..")); |
|
517 // clear CSIE bit..and disable HW interrupts.. |
|
518 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable | KSlaveInterruptFlags, 0); |
|
519 |
|
520 // clear internal flags |
|
521 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
522 iTrigger = 0; |
|
523 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
524 } |
|
525 |
|
526 // set the callback's trigger.. |
|
527 aCb->SetTrigger(aTrigger | aCb->GetTrigger()); |
|
528 } |
|
529 |
|
530 // Initializes the hardware with the data provided in the transaction and slave-address field |
|
531 TInt DCsiChannelSlave::ConfigureInterface() |
|
532 { |
|
533 __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()")); |
|
534 |
|
535 HCR::TSettingId settingId; |
|
536 TConfigSpiBufV01* aBuf = (TConfigSpiBufV01*) iConfigHeader; |
|
537 TConfigSpiV01 &spiHeader = (*aBuf)(); |
|
538 |
|
539 // CSI initialization procedure: |
|
540 // 1. clear CISE bit.. |
|
541 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, 0); |
|
542 |
|
543 // 2. wait until CIS_MODE.bit0 (CSOT) changes to 0 |
|
544 // start the GuardTimer before while-loop.. |
|
545 iTransactionStatus = KErrNone; |
|
546 |
|
547 iHwGuardTimer.OneShot(NKern::TimerTicks(iHwTimeoutValue)); |
|
548 |
|
549 while((iTransactionStatus == KErrNone) && |
|
550 AsspRegister::Read32(iChannelBase + KHoCSIModeControl) & KHtCSIModeTransferState); |
|
551 |
|
552 // check if the the guard timer or transaction timer hasn't expired.. |
|
553 if(iTransactionStatus != KErrNone) |
|
554 { |
|
555 return KErrGeneral; |
|
556 } |
|
557 else |
|
558 { |
|
559 iHwGuardTimer.Cancel(); |
|
560 } |
|
561 |
|
562 // 3. set CSIRST bit.. |
|
563 AsspRegister::Modify32(iChannelBase + KHoCSIControl, 0, KHtCSIControlCSIRst); |
|
564 |
|
565 // 4. set KHoCSIClockSelect register.. |
|
566 TUint32 val = 0; |
|
567 |
|
568 // CKP - ClocK Polarity (bit 4) 0: initial value is high, 1: initial val is low |
|
569 // DAP - Clock phase select (bit 3) 0: 180 degree delay, 1: 0 degree delay |
|
570 // @** don't use DAP=1 when iClock set to KHCSIClockValPCLKdiv4 (HW bug..see SoC errata) |
|
571 switch (spiHeader.iClkMode) |
|
572 { |
|
573 case ESpiPolarityLowRisingEdge: // Active high, odd edges |
|
574 val = KHtCSIClockSelectCKP; // CKP 1, DAP 0 |
|
575 break; |
|
576 case ESpiPolarityLowFallingEdge: // Active high, even edges |
|
577 val = KHtCSIClockSelectCKP | // CKP 1, DAP 1 |
|
578 KHtCSIClockSelectDAP; |
|
579 break; |
|
580 |
|
581 case ESpiPolarityHighFallingEdge: // Active low, odd edges |
|
582 val = 0; // CKP 0, DAP 0 |
|
583 break; |
|
584 |
|
585 case ESpiPolarityHighRisingEdge: // Active low, even edges |
|
586 val = KHtCSIClockSelectDAP; // CKP 0, DAP 1 |
|
587 break; |
|
588 default: |
|
589 break; // there's no default..no other value can be specified as it's an enum ;) |
|
590 } |
|
591 |
|
592 // we are a Slave - so we use only one setting for the clk speed, ignoring the header |
|
593 val |= KHCSIClockValSlave; |
|
594 |
|
595 // Set the SS pin configuration.. |
|
596 val |= KHtCSIClockSelectSSE; // SS pin enable |
|
597 |
|
598 if (spiHeader.iSSPinActiveMode == ESpiCSPinActiveHigh) |
|
599 { |
|
600 val |= KHtCSIClockSelectSSPol; // 1: SS pin high active |
|
601 } |
|
602 |
|
603 // store iSSPinActiveMode internaly - it will be used in interrupts. |
|
604 iSSPinActiveMode = spiHeader.iSSPinActiveMode; |
|
605 |
|
606 // set transaction wait time.. |
|
607 val |= (0xf & spiHeader.iTransactionWaitCycles) << KHsCSIModeTWait; |
|
608 |
|
609 // and finally update the register |
|
610 AsspRegister::Write32(iChannelBase + KHoCSIClockSelect, val); |
|
611 |
|
612 // 5. clear KHtCSIControlCSIRst bit.. |
|
613 AsspRegister::Modify32(iChannelBase + KHoCSIControl, KHtCSIControlCSIRst, 0); |
|
614 |
|
615 // 6. Set Mode Control register: |
|
616 // Transmission and reception mode |
|
617 val = KHtCSIModeTrEnable; |
|
618 |
|
619 // Select transmit data length (8/16 bits) |
|
620 if (spiHeader.iWordWidth == ESpiWordWidth_16) |
|
621 { |
|
622 iWordSize = 2; |
|
623 val |= KHtCSIModeDataLen; |
|
624 } |
|
625 else |
|
626 { |
|
627 iWordSize = 1; |
|
628 } |
|
629 |
|
630 // Select Transfer direction (if set-LSB first) |
|
631 if (spiHeader.iBitOrder == ELsbFirst) |
|
632 { |
|
633 val |= KHtCSIModeTransferDir; |
|
634 } |
|
635 |
|
636 // update the register |
|
637 AsspRegister::Write32(iChannelBase + KHoCSIModeControl, val); |
|
638 |
|
639 // 7. Set FIFO trigger levels |
|
640 TUint8 csiFifoRxTrigerLvl; // set RxTrigger level |
|
641 |
|
642 settingId.iCat = KHcrCat_HWServ_CSI; |
|
643 settingId.iKey = KHcrKey_CSI_FifoRxTrigerLvl; |
|
644 TInt r = HCR::GetUInt(settingId, csiFifoRxTrigerLvl); |
|
645 if(r != KErrNone) |
|
646 { |
|
647 __KTRACE_OPT(KIIC, Kern::Printf("Failed to read HCR setting for category = %d and key = %d\n", settingId.iCat, settingId.iKey)); |
|
648 return r; |
|
649 } |
|
650 AsspRegister::Write32(iChannelBase + KHoCSIFifoTrgLvl, |
|
651 (csiFifoRxTrigerLvl << KHsCSIRxFifoTrgLvl)); |
|
652 |
|
653 // 8. Clear all interrupts |
|
654 AsspRegister::Write32(iChannelBase + KHoCSIIntStatus, KInterruptsAll); |
|
655 |
|
656 // 9. Set RxTrig permission and enable TEnd and RxTrg interrupts |
|
657 AsspRegister::Write32(iChannelBase + KHoCSIControl, KSlaveInterruptFlags); |
|
658 |
|
659 // the timeout period to wait for a response from the client. |
|
660 SetClientWaitTime(KClientWaitTime); |
|
661 // if the KIIC debug trace flag is set, we need to increase the transaction timeout, |
|
662 // so that debug traces don't cause timer expiration. This call will overwrite |
|
663 // previously set value (SetClientWaitTime) and default value set by the PIL (SetMasterWaitTime) |
|
664 __KTRACE_OPT(KIIC, SetClientWaitTime(KMaxWaitTime)); |
|
665 __KTRACE_OPT(KIIC, SetMasterWaitTime(KMaxWaitTime)); |
|
666 |
|
667 |
|
668 settingId.iCat = KHcrCat_MHA_Interrupt; |
|
669 settingId.iKey = iChannelNumber == 0 ? KHcrKey_Interrupt_CSI0 : KHcrKey_Interrupt_CSI1; |
|
670 |
|
671 TInt32 interruptId; |
|
672 r = HCR::GetInt(settingId, interruptId); |
|
673 if(r != KErrNone) |
|
674 { |
|
675 __KTRACE_OPT(KIIC, Kern::Printf("Failed to read HCR setting for category = %d and key = %d\n", settingId.iCat, settingId.iKey)); |
|
676 return r; |
|
677 } |
|
678 |
|
679 // Bind the ISR for the Slave |
|
680 iInterruptId = Interrupt::Bind(interruptId, DCsiChannelSlave::CsiIsr, this); |
|
681 |
|
682 // this returns interruptId or error code(err < 0) |
|
683 if (iInterruptId < KErrNone) |
|
684 { |
|
685 __KTRACE_OPT(KIIC, Kern::Printf("ERROR: InterruptBind error.. %d", r)); |
|
686 Kern::Printf("ERROR: InterruptBind error.. %d", r); |
|
687 r = iInterruptId; |
|
688 } |
|
689 |
|
690 return r; |
|
691 } |
|
692 |
|
693 // This method starts data transfer - filling the Transmit FIFO and enabling the device (CSIE bit) |
|
694 TInt DCsiChannelSlave::InitTransfer() |
|
695 { |
|
696 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelSlave::InitTransfer()")); |
|
697 //__KTRACE_OPT(KIIC, Kern::Printf("Receiving %d, Transmitting %d", (iTrigger & EReceive)>>4, (iTrigger & ETransmit)>>3)); |
|
698 |
|
699 TUint r = KErrNone; |
|
700 TInt intState; |
|
701 TInt trigger; |
|
702 |
|
703 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
704 if(!iInProgress) |
|
705 { |
|
706 // clean the FIFOs.. |
|
707 AsspRegister::Write32(iChannelBase + KHoCSIOFifoL, 0); |
|
708 AsspRegister::Write32(iChannelBase + KHoCSIOFifoL, 0); |
|
709 |
|
710 // clear CISE bit..re-enable all HW interrupts.. |
|
711 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, KSlaveInterruptFlags); |
|
712 |
|
713 // Clear all interrupts |
|
714 AsspRegister::Write32(iChannelBase + KHoCSIIntStatus, KInterruptsAll); |
|
715 } |
|
716 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
717 |
|
718 |
|
719 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
720 trigger = iTrigger; |
|
721 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
722 |
|
723 // if we are transmitting - Add data to the FIFO.. |
|
724 if(trigger & ETransmit) |
|
725 { |
|
726 // Set mode to transmission and reception (Set TRMD) |
|
727 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, 0, KHtCSIModeTrEnable); |
|
728 |
|
729 // enable TEND interrupt (e.g. if this is called during the transmission as the result of |
|
730 // TxUnderrun event. |
|
731 AsspRegister::Modify32(iChannelBase + KHoCSIControl, 0, KHtCSIControlTEndIE); |
|
732 |
|
733 iWordSize = iTxGranularity >> 3; |
|
734 iTxData = iTxBuf + (iWordSize * iTxOffset); |
|
735 iTxDataEnd = iTxData + (iWordSize * iNumTxWords); |
|
736 |
|
737 __KTRACE_OPT(KIIC, Kern::Printf("Tx Buf: %x", iTxData)); |
|
738 __KTRACE_OPT(KIIC, Kern::Printf("Tx BufeND: %x", iTxDataEnd)); |
|
739 |
|
740 // copy data to the FIFO.. |
|
741 while(AsspRegister::Read32(iChannelBase + KHoCSIOFifoL) <= (KHwCSIFifoLMax - iWordSize) && // until FIFO not full.. |
|
742 iTxData != iTxDataEnd) // or whole data has been copied. |
|
743 { |
|
744 TUint16 val; |
|
745 |
|
746 // in 16bit mode we need to read MSB first.. |
|
747 if(iWordSize > 1) |
|
748 { |
|
749 val = (*iTxData) << 8; // MSB shifted up.. |
|
750 val |= *(iTxData + 1) & 0xff; // LSB.. |
|
751 } |
|
752 else |
|
753 { |
|
754 val = *iTxData; |
|
755 } |
|
756 |
|
757 // write this value to the FIFO window register.. |
|
758 AsspRegister::Write32(iChannelBase + KHoCSIOFifo, val); |
|
759 |
|
760 // increment the pointer. |
|
761 iTxData += iWordSize; |
|
762 } |
|
763 |
|
764 __KTRACE_OPT(KIIC, Kern::Printf("After adding:\n\rTx Buf: %x", iTxData)); |
|
765 __KTRACE_OPT(KIIC, Kern::Printf("Tx BufeND: %x", iTxDataEnd)); |
|
766 } |
|
767 |
|
768 if(trigger & EReceive) // we are starting receive transfer only.. |
|
769 { |
|
770 if(!(trigger & ETransmit)) |
|
771 { |
|
772 // if only receiving - set it to reveive-only |
|
773 // Set mode to reception only (clear TRMD) |
|
774 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeTrEnable, 0); |
|
775 } |
|
776 |
|
777 // enable RxTrg interrupt (e.g. if this is called during the transmission as the result |
|
778 // of RxOverrun event. |
|
779 AsspRegister::Modify32(iChannelBase + KHoCSIControl, 0, KHtCSIControlRxTrgIE); |
|
780 |
|
781 iWordSize = iRxGranularity >> 3; |
|
782 iRxData = iRxBuf + (iWordSize * iRxOffset); |
|
783 iRxDataEnd = iRxData + (iWordSize * iNumRxWords); |
|
784 |
|
785 __KTRACE_OPT(KIIC, Kern::Printf("Rx Buffer: %x", iRxData)); |
|
786 __KTRACE_OPT(KIIC, Kern::Printf("Rx BufreND: %x", iRxDataEnd)); |
|
787 } |
|
788 |
|
789 // enable interrupts.. |
|
790 Interrupt::Enable(iInterruptId); |
|
791 |
|
792 // enable transmission..this will trigger the HW to start the transmission.. |
|
793 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, 0, KHtCSIModeEnable); |
|
794 |
|
795 return r; |
|
796 } |
|
797 |
|
798 // aOperation is a bit-mask made of TPslOperation |
|
799 TInt DCsiChannelSlave::DoRequest(TInt aOperation) |
|
800 { |
|
801 // __KTRACE_OPT(KIIC, Kern::Printf("\n===>DCsiChannelSlave::DoRequest, Operation %x", aOperation)); |
|
802 |
|
803 TInt r = KErrNone; |
|
804 TInt intState; |
|
805 |
|
806 if (aOperation & EAsyncConfigPwrUp) |
|
807 { |
|
808 __KTRACE_OPT(KIIC, Kern::Printf("EAsyncConfigPwrUp")); |
|
809 // this is called when the client calls IicBus::CaptureChannel() asynchronously |
|
810 r = ConfigureInterface(); |
|
811 ChanCaptureCallback(r); |
|
812 return r; |
|
813 } |
|
814 |
|
815 // PowerUp |
|
816 if (aOperation & ESyncConfigPwrUp) |
|
817 { |
|
818 __KTRACE_OPT(KIIC, Kern::Printf("ESyncConfigPwrUp")); |
|
819 // this is called when the client calls IicBus::CaptureChannel() |
|
820 // - configure the channel with the configuration provided in the header. |
|
821 r = ConfigureInterface(); |
|
822 // we can't continue in this case.. |
|
823 if (r != KErrNone) |
|
824 { |
|
825 __KTRACE_OPT(KIIC, Kern::Printf("Coudln't configure the interface..r %d", r)); |
|
826 return r; |
|
827 } |
|
828 } |
|
829 |
|
830 if (aOperation & ETransmit) |
|
831 { |
|
832 __KTRACE_OPT(KIIC, Kern::Printf("ETransmit")); |
|
833 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
834 iTrigger |= ETransmit; |
|
835 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
836 } |
|
837 |
|
838 if (aOperation & EReceive) |
|
839 { |
|
840 __KTRACE_OPT(KIIC, Kern::Printf("EReceive")); |
|
841 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
842 iTrigger |= EReceive; |
|
843 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
844 } |
|
845 |
|
846 if (aOperation & (EReceive | ETransmit)) |
|
847 { |
|
848 r = InitTransfer(); |
|
849 } |
|
850 |
|
851 if (aOperation & EAbort) |
|
852 { |
|
853 __KTRACE_OPT(KIIC, Kern::Printf("EAbort")); |
|
854 intState = __SPIN_LOCK_IRQSAVE(CsiSpinLock); |
|
855 // disable CSI (clear CSIE bit), and interrupts |
|
856 if(!iInProgress) |
|
857 { |
|
858 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable | KSlaveInterruptFlags, 0); |
|
859 iInProgress = EFalse; |
|
860 iTrigger = 0; |
|
861 } |
|
862 __SPIN_UNLOCK_IRQRESTORE(CsiSpinLock, intState); |
|
863 Interrupt::Disable(iInterruptId); |
|
864 } |
|
865 |
|
866 if (aOperation & EPowerDown) |
|
867 { |
|
868 __KTRACE_OPT(KIIC, Kern::Printf("EPowerDown")); |
|
869 |
|
870 // set CSI Rst bit |
|
871 AsspRegister::Write32(iChannelBase + KHoCSIModeControl, KHtCSIControlCSIRst); |
|
872 |
|
873 // disable and unbind the ISR, |
|
874 Interrupt::Disable(iInterruptId); |
|
875 Interrupt::Unbind(iInterruptId); |
|
876 } |
|
877 return r; |
|
878 } |
|
879 |