|
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 <drivers/gpio.h> |
|
24 #include "csi_psl.h" |
|
25 #include "csi_master.h" |
|
26 |
|
27 #include "hcrconfig.h" |
|
28 #include "hcrconfig_csi.h" |
|
29 |
|
30 // This method ensures, that all Timers have been canceled.. |
|
31 // interrupts disabled prior to completing the request. |
|
32 // This method is used to complete request and notify PIL in various situations. |
|
33 void DCsiChannelMaster::ExitComplete(TInt aErr, TBool aComplete /*= ETrue*/) |
|
34 { |
|
35 __KTRACE_OPT(KIIC, Kern::Printf("ExitComplete, r %d, complete %d", aErr, aComplete)); |
|
36 |
|
37 // make sure we've disabled ints and canceled dfcs/timers.. |
|
38 // Disable interrupts for CSI |
|
39 Interrupt::Disable(iInterruptId); |
|
40 |
|
41 // cancel timers and DFCs.. |
|
42 CancelTimeOut(); |
|
43 iHwGuardTimer.Cancel(); |
|
44 iTransferEndDfc.Cancel(); |
|
45 |
|
46 // change state to EIdle.. |
|
47 iState = EIdle; |
|
48 |
|
49 // complete the request..calling the PIL method |
|
50 if(aComplete) |
|
51 { |
|
52 CompleteRequest(aErr); |
|
53 } |
|
54 } |
|
55 |
|
56 // this is call-back for iHwGuard timer. It is called in the ISR context |
|
57 // if the iHwGuardTimer expires. |
|
58 // It will change the iTransactionStatus to KErrTimedOut to allow exiting from the while-loop.. |
|
59 void DCsiChannelMaster::TimeoutCallback(TAny* aPtr) |
|
60 { |
|
61 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::TimeoutCallback")); |
|
62 DCsiChannelMaster *a = (DCsiChannelMaster*) aPtr; |
|
63 a->iTransactionStatus = KErrTimedOut; |
|
64 } |
|
65 |
|
66 // This method is called by the PIL in the case of timeout for the transaction expiration. |
|
67 // The PIL will call CompleteRequest() after this function returns, so we need only to clean-up |
|
68 // and de-assert the SS line. |
|
69 TInt DCsiChannelMaster::HandleSlaveTimeout() |
|
70 { |
|
71 __KTRACE_OPT(KIIC, Kern::Printf("HandleSlaveTimeout")); |
|
72 |
|
73 // make sure, that CSIE bit is cleared (disable the interface) |
|
74 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, 0); |
|
75 |
|
76 // stop the driver's operation.. |
|
77 ExitComplete(KErrTimedOut, EFalse); |
|
78 |
|
79 // bring the CS signal pin back to inactive state.. |
|
80 GPIO::SetOutputState(iSSPin, iSSPinActiveStateOff); |
|
81 |
|
82 return KErrTimedOut; |
|
83 } |
|
84 |
|
85 //DFC for TransferComplete |
|
86 void DCsiChannelMaster::TransferEndDfc(TAny* aPtr) |
|
87 { |
|
88 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::TransferEndDfc")); |
|
89 DCsiChannelMaster *a = (DCsiChannelMaster*) aPtr; |
|
90 |
|
91 // we are still receiving the data, so must wait until the end of the transmission |
|
92 // start the guard timer, which - in case of the following while got stucked - will |
|
93 // unblock this dfc by changing iTransactionStatus |
|
94 a->iTransactionStatus = KErrNone; |
|
95 |
|
96 a->iHwGuardTimer.OneShot(NKern::TimerTicks(a->iHwTimeoutValue)); |
|
97 |
|
98 // active wait until the transfer has finished |
|
99 while((AsspRegister::Read32(a->iChannelBase + KHoCSIModeControl) & KHtCSIModeTransferState) && |
|
100 (a->iTransactionStatus == KErrNone)); |
|
101 |
|
102 // bring the CS signal pin back to inactive state, but only if this is not an extended transaction, |
|
103 // in which case we want to leave the bus alone so that the multiple transactions making up the |
|
104 // extended transaction become one big transaction as far as the bus is concerned |
|
105 if (!(a->iCurrTransaction->Flags() & KTransactionWithMultiTransc)) |
|
106 { |
|
107 GPIO::SetOutputState(a->iSSPin, a->iSSPinActiveStateOff); |
|
108 } |
|
109 |
|
110 // clear CSIE bit.. |
|
111 AsspRegister::Modify32(a->iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, 0); |
|
112 |
|
113 // check if the the guard timer or transaction timer hasn't expired.. |
|
114 if(a->iTransactionStatus != KErrNone) |
|
115 { |
|
116 __KTRACE_OPT(KIIC, Kern::Printf("CsiChannelMaster::TransferEndDfc(): Transaction timed-out")); |
|
117 a->ExitComplete(a->iTransactionStatus); // report the error situation.. |
|
118 return; |
|
119 } |
|
120 else |
|
121 { |
|
122 a->iHwGuardTimer.Cancel(); |
|
123 } |
|
124 |
|
125 // drain the Rx FIFO buffer (there might be one item left) |
|
126 if(a->iOperation.iOp.iIsReceiving) |
|
127 { |
|
128 if(AsspRegister::Read32(a->iChannelBase + KHoCSIIFifoL) && |
|
129 (a->iRxDataEnd - a->iRxData >= a->iWordSize)) |
|
130 { |
|
131 TUint16 val = AsspRegister::Read32(a->iChannelBase + KHoCSIIFifo); |
|
132 |
|
133 // we're big-endian.. (AB).. |
|
134 // so in 16bit mode we need to read MSB first.. |
|
135 if(a->iWordSize > 1) |
|
136 { |
|
137 *a->iRxData = val >> 8; // MSB shifted down.. |
|
138 *(a->iRxData + 1) = val & 0xff; // LSB.. |
|
139 } |
|
140 else |
|
141 { |
|
142 *a->iRxData = val; |
|
143 } |
|
144 |
|
145 // increment the pointer.. |
|
146 a->iRxData += a->iWordSize; |
|
147 } |
|
148 } |
|
149 // else - we don't care about the read data, it will be flushed before the next transfer.. |
|
150 |
|
151 // check, if there are more transfers in this transaction |
|
152 if(a->iState == EBusy) |
|
153 { |
|
154 TInt err = a->ProcessNextTransfers(); |
|
155 |
|
156 // if for any reason coudln't start next transfer-complete the transaction with err.. |
|
157 if(err != KErrNone) |
|
158 { |
|
159 a->ExitComplete(err); |
|
160 } |
|
161 } |
|
162 } |
|
163 |
|
164 // ISR Handler |
|
165 void DCsiChannelMaster::CsiIsr(TAny* aPtr) |
|
166 { |
|
167 DCsiChannelMaster *a = (DCsiChannelMaster*) aPtr; |
|
168 |
|
169 // read the interrupt flags - to see, what was causing the interrupt.. |
|
170 TUint32 status = AsspRegister::Read32(a->iChannelBase + KHoCSIIntStatus); |
|
171 |
|
172 // process TEND end interrupts |
|
173 // this ISR happens every time ONE unit has been transfered.. |
|
174 if(status & KHtCSIIntStatusTEnd) |
|
175 { |
|
176 // clear TxEnd interrupt.. |
|
177 AsspRegister::Write32(a->iChannelBase + KHoCSIIntStatus, KHtCSIIntStatusTEnd); |
|
178 |
|
179 if(a->iTxData == a->iTxDataEnd) |
|
180 { |
|
181 // if the Tx FIFO is empty (this was the last item transferred) |
|
182 // confirm that the transfer was completed successfully: |
|
183 // - set the transfer status as KErrNone |
|
184 // - disable interrupts.. |
|
185 // - queue a DFC, which will finish the Tx-asserting/de-asserting CS line |
|
186 // and start another transfer in the transaction or complete transaction |
|
187 if(!AsspRegister::Read32(a->iChannelBase + KHoCSIOFifoL)) |
|
188 { |
|
189 Interrupt::Disable(a->iInterruptId); |
|
190 a->iTransferEndDfc.Add(); |
|
191 } |
|
192 } |
|
193 else |
|
194 { |
|
195 // if tere's more data to be sent - copy next item to the FIFO window register. |
|
196 // if we are in 'receive-only' mode - (e.g. simple read request) we're only |
|
197 // sending '0' (or other value defined as KValueSentOnRead) - to generate a clock for the slave. |
|
198 if(a->iOperation.iOp.iIsTransmitting) |
|
199 { |
|
200 // copy data to the FIFO.. |
|
201 TUint16 val; |
|
202 |
|
203 // in 16bit mode we need to write two bytes as once MSB first.. |
|
204 if(a->iWordSize > 1) |
|
205 { |
|
206 val = (*a->iTxData) << 8; // MSB shifted up.. |
|
207 val |= *(a->iTxData + 1) & 0xff; // LSB.. |
|
208 } |
|
209 else |
|
210 { |
|
211 val = *a->iTxData; |
|
212 } |
|
213 |
|
214 // write this value to the register.. |
|
215 AsspRegister::Write32(a->iChannelBase + KHoCSIOFifo, val); |
|
216 |
|
217 // increment the pointer.. |
|
218 a->iTxData += a->iWordSize; |
|
219 } |
|
220 } |
|
221 } //end of TXEnd processing |
|
222 |
|
223 // process receive threshold interrupt |
|
224 if(status & KHtCSIIntStatusRxTrgIE) |
|
225 { |
|
226 // read data from the FIFO .. |
|
227 if(a->iOperation.iOp.iIsReceiving) |
|
228 { |
|
229 while(AsspRegister::Read32(a->iChannelBase + KHoCSIIFifoL)) |
|
230 { |
|
231 // if there's still some place in the buffer - put it into buffer.. |
|
232 if((a->iRxDataEnd - a->iRxData) >= a->iWordSize) |
|
233 { |
|
234 // copy data from the FIFO if tere's more space in the buffer |
|
235 TUint16 val = AsspRegister::Read32(a->iChannelBase + KHoCSIIFifo); |
|
236 |
|
237 // we're big-endian.. (AB).. |
|
238 // so in 16bit mode we need to read MSB first.. |
|
239 if(a->iWordSize > 1) |
|
240 { |
|
241 *a->iRxData = val >> 8; // MSB shifted down.. |
|
242 *(a->iRxData + 1) = val & 0xff; // LSB.. |
|
243 } |
|
244 else |
|
245 { |
|
246 *a->iRxData = val; |
|
247 } |
|
248 } |
|
249 else |
|
250 { |
|
251 // overrun, i.e Slave has sent more data than expected by the client |
|
252 // (e.g. too small buffer size for the transmission) |
|
253 a->iTransactionStatus = KErrOverflow; |
|
254 break; |
|
255 } |
|
256 |
|
257 // increment the pointer.. |
|
258 a->iRxData += a->iWordSize; |
|
259 } |
|
260 } |
|
261 else |
|
262 { |
|
263 // or drop the data, writing 0 to the FIFOL register |
|
264 AsspRegister::Write32(a->iChannelBase + KHoCSIIFifoL, 0); |
|
265 } |
|
266 |
|
267 // if we are in the 'half-duplex' 'receive only' mode, |
|
268 // once the receive buffer is full we are to finish the transmission.. |
|
269 if((a->iOperation.iValue == TCsiOperationType::EReceiveOnly) && |
|
270 (a->iRxDataEnd == a->iRxData)) |
|
271 { |
|
272 Interrupt::Disable(a->iInterruptId); |
|
273 a->iTransferEndDfc.Add(); |
|
274 } |
|
275 |
|
276 // Clear the RxThreshold interrupt |
|
277 AsspRegister::Write32(a->iChannelBase + KHoCSIIntStatus, KHtCSIIntStatusRxTrgIE); |
|
278 |
|
279 } // end of reception processing.. |
|
280 } |
|
281 |
|
282 // constructor 1-st stage |
|
283 DCsiChannelMaster::DCsiChannelMaster(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) : |
|
284 DIicBusChannelMaster(aBusType, aChanDuplex), // !!call overloaded base class constructor.. |
|
285 iTransferEndDfc(TransferEndDfc, this, KCsiDfcPriority), |
|
286 iHwGuardTimer(TimeoutCallback, this) |
|
287 { |
|
288 iHwTimeoutValue = -1; |
|
289 iSSPin = 0; |
|
290 iChannelNumber = aChannelNumber; //Set the iChannelNumber of the Base Class |
|
291 iState = EIdle; |
|
292 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::DCsiChannelMaster: iChannelNumber = %d", iChannelNumber)); |
|
293 } |
|
294 |
|
295 // 2nd stage construction.. |
|
296 TInt DCsiChannelMaster::DoCreate() |
|
297 { |
|
298 __KTRACE_OPT(KIIC, Kern::Printf("\nDCsiChannelMaster::DoCreate() ch: %d \n", iChannelNumber)); //__THREAD_AND_CPU; |
|
299 |
|
300 TInt32 interruptId; |
|
301 TUint32 channelBase; |
|
302 |
|
303 HCR::TSettingId settingId; |
|
304 settingId.iCat = KHcrCat_MHA_Interrupt; |
|
305 |
|
306 HCR::TSettingId channelBaseId; |
|
307 channelBaseId.iCat = KHcrCat_MHA_HWBASE; |
|
308 |
|
309 TInt r = KErrNone; |
|
310 switch(iChannelNumber) |
|
311 { |
|
312 case 0: |
|
313 settingId.iKey = KHcrKey_Interrupt_CSI0; |
|
314 channelBaseId.iKey = KHcrKey_HwBase_CSI0; |
|
315 break; |
|
316 case 1: |
|
317 settingId.iKey = KHcrKey_Interrupt_CSI1; |
|
318 channelBaseId.iKey = KHcrKey_HwBase_CSI1; |
|
319 break; |
|
320 default: |
|
321 __KTRACE_OPT(KIIC, Kern::Printf("Wrong ChannelNumber specified (%d)", iChannelNumber)); |
|
322 return KErrArgument; |
|
323 } |
|
324 |
|
325 r = HCR::GetUInt(channelBaseId, channelBase); |
|
326 iChannelBase = channelBase; |
|
327 |
|
328 if(r != KErrNone) |
|
329 { |
|
330 __KTRACE_OPT(KIIC, Kern::Printf("Failed to read HCR setting for category = %d and key = %d\n", channelBaseId.iCat, channelBaseId.iKey)); |
|
331 return r; |
|
332 } |
|
333 |
|
334 r = HCR::GetInt(settingId, interruptId); |
|
335 if(r != KErrNone) |
|
336 { |
|
337 __KTRACE_OPT(KIIC, Kern::Printf("Failed to read HCR setting for category = %d and key = %d\n", settingId.iCat, settingId.iKey)); |
|
338 return r; |
|
339 } |
|
340 |
|
341 //Read the timeout value from HCR |
|
342 //The value will not be changed during transaction, so it needs to be read only once from HCR |
|
343 if(iHwTimeoutValue == -1) |
|
344 { |
|
345 // csiTimeout values was not yet read from HCR; read it |
|
346 HCR::TSettingId settingId; |
|
347 settingId.iCat = KHcrCat_HWServ_CSI; |
|
348 settingId.iKey = KHcrKey_CSI_Timeout; |
|
349 r = HCR::GetInt(settingId, iHwTimeoutValue); |
|
350 if(r != KErrNone) |
|
351 { |
|
352 __KTRACE_OPT(KIIC, Kern::Printf("Failed to read HCR setting for category = %d and key = %d\n", settingId.iCat, settingId.iKey)); |
|
353 return r; |
|
354 } |
|
355 } |
|
356 |
|
357 |
|
358 // Create kernel DFCQ (thread) for the driver.. |
|
359 if(!iDfcQ) |
|
360 { |
|
361 TBuf8<KMaxName> threadName (KSpiThreadName); |
|
362 threadName.AppendNum(iChannelNumber); |
|
363 TDynamicDfcQue* dynamicDfcQ; |
|
364 r = Kern::DynamicDfcQCreate(dynamicDfcQ, KSpiThreadPriority, threadName); |
|
365 if(r != KErrNone) |
|
366 { |
|
367 __KTRACE_OPT(KIIC, Kern::Printf("DFC Queue creation failed, ch: %d, r = %d\n", iChannelNumber, r)); |
|
368 return r; |
|
369 } |
|
370 iDfcQ = dynamicDfcQ; |
|
371 } |
|
372 |
|
373 // PIL Base class initialization. This must!! be called prior to SetDfcQ(iDfcQ) .. |
|
374 r = Init(); |
|
375 if(r == KErrNone) |
|
376 { |
|
377 // set iDFCQ |
|
378 SetDfcQ(iDfcQ); // base class.. |
|
379 iTransferEndDfc.SetDfcQ(iDfcQ); // transfer-end DFC |
|
380 |
|
381 #ifdef CPU_AFFINITY_ANY |
|
382 NKern::ThreadSetCpuAffinity((NThread*)(iDfcQ->iThread), KCpuAffinityAny); |
|
383 #endif |
|
384 |
|
385 // Bind the Interrupt. |
|
386 |
|
387 iInterruptId = Interrupt::Bind(interruptId, CsiIsr, this); |
|
388 |
|
389 // this returns interruptId or error code(err < 0) |
|
390 if(iInterruptId < KErrNone) |
|
391 { |
|
392 __KTRACE_OPT(KIIC, Kern::Printf("ERROR: InterruptBind error.. %d", r)); |
|
393 r = iInterruptId; |
|
394 iInterruptId = 0; |
|
395 } |
|
396 } |
|
397 |
|
398 return r; |
|
399 } |
|
400 |
|
401 // static method used to construct the DCsiChannelMaster object. |
|
402 // Export the channel creating function for client use in controller-less mode |
|
403 #ifdef STANDALONE_CHANNEL |
|
404 EXPORT_C |
|
405 #endif |
|
406 DCsiChannelMaster* DCsiChannelMaster::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex) |
|
407 { |
|
408 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::NewL(): aChannelNumber = %d, BusType =%d", aChannelNumber, aBusType)); |
|
409 DCsiChannelMaster *pChan = new DCsiChannelMaster(aChannelNumber, aBusType, aChanDuplex); |
|
410 |
|
411 TInt r = KErrNoMemory; |
|
412 |
|
413 if(pChan) |
|
414 { |
|
415 r = pChan->DoCreate(); |
|
416 } |
|
417 if(r != KErrNone) |
|
418 { |
|
419 delete pChan; |
|
420 pChan = NULL; |
|
421 } |
|
422 return pChan; |
|
423 } |
|
424 |
|
425 #ifdef STANDALONE_CHANNEL |
|
426 DCsiChannelMaster::~DCsiChannelMaster() |
|
427 { |
|
428 // This destructor will only be called in controller-less mode, when iDfcQ is a TDynamicDfcQ, |
|
429 // In here, detroy the dfc queue and unbind the interrupt for next channel creation |
|
430 |
|
431 // stop the Hardware |
|
432 // clear CISE bit..(disables CSI) |
|
433 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, 0); |
|
434 |
|
435 // set CSIRST bit.. |
|
436 AsspRegister::Modify32(iChannelBase + KHoCSIControl, 0, KHtCSIControlCSIRst); |
|
437 |
|
438 if(iInterruptId) |
|
439 { |
|
440 Interrupt::Unbind(iInterruptId); |
|
441 } |
|
442 if(iDfcQ) |
|
443 { |
|
444 ((TDynamicDfcQue*)iDfcQ)->Destroy(); |
|
445 } |
|
446 } |
|
447 #endif |
|
448 |
|
449 // this method compares if the previous transaction header and slave-select(chip-select) pin are different |
|
450 // If configuration is the same - HW will not be re-initialized. It also makes a copy of the new header |
|
451 // and new pin number used to configure the interface. |
|
452 TBool DCsiChannelMaster::TransConfigDiffersFromPrev() |
|
453 { |
|
454 TConfigSpiBufV01* headerBuf = (TConfigSpiBufV01*) (GetTransactionHeader(iCurrTransaction)); |
|
455 TConfigSpiV01 &spiHeader = (*headerBuf)(); |
|
456 |
|
457 // the busId - has a SlaveAddress which in the case of SPI is a SlaveSelect pin (GPIO pin number) |
|
458 TUint32 busId = ((TIicBusTransaction*)iCurrTransaction)->GetBusId(); |
|
459 |
|
460 // get the slave address |
|
461 TUint16 csPin; |
|
462 csPin = GET_SLAVE_ADDR(busId); |
|
463 |
|
464 // compare it to the previous configuration.. |
|
465 if(csPin != iSSPin || |
|
466 spiHeader.iWordWidth != iSpiHeader.iWordWidth || |
|
467 spiHeader.iClkSpeedHz != iSpiHeader.iClkSpeedHz || |
|
468 spiHeader.iClkMode != iSpiHeader.iClkMode || |
|
469 spiHeader.iTimeoutPeriod != iSpiHeader.iTimeoutPeriod || |
|
470 spiHeader.iBitOrder != iSpiHeader.iBitOrder || |
|
471 spiHeader.iTransactionWaitCycles != iSpiHeader.iTransactionWaitCycles || |
|
472 spiHeader.iSSPinActiveMode != iSpiHeader.iSSPinActiveMode) |
|
473 { |
|
474 iSSPin = csPin; // copy CsPin number. |
|
475 iSpiHeader = spiHeader; // copy the new config params |
|
476 #ifdef _DEBUG |
|
477 DumpConfiguration(spiHeader, iSSPin); |
|
478 #endif |
|
479 return ETrue; |
|
480 } |
|
481 |
|
482 return EFalse; |
|
483 } |
|
484 |
|
485 // Validates various fields in the transaction header |
|
486 // this pure-virtual method is called by the PIL in the context of the |
|
487 // client's thread - whenever he makes a call to QueueTransaction(). |
|
488 TInt DCsiChannelMaster::CheckHdr(TDes8* aHdrBuff) |
|
489 { |
|
490 TInt r = KErrNone; |
|
491 |
|
492 if(!aHdrBuff) |
|
493 { |
|
494 r = KErrArgument; |
|
495 } |
|
496 else |
|
497 { |
|
498 TConfigSpiBufV01* headerBuf = (TConfigSpiBufV01*) aHdrBuff; |
|
499 TConfigSpiV01 &spiHeader = (*headerBuf)(); |
|
500 |
|
501 if(spiHeader.iTransactionWaitCycles > 15) // (can be 0 - 15) |
|
502 { |
|
503 __KTRACE_OPT(KIIC, Kern::Printf("iTransactionWaitCycles not supported")); |
|
504 r = KErrNotSupported; |
|
505 } |
|
506 else |
|
507 { |
|
508 if(spiHeader.iWordWidth != ESpiWordWidth_8 && |
|
509 spiHeader.iWordWidth != ESpiWordWidth_16) |
|
510 { |
|
511 __KTRACE_OPT(KIIC, Kern::Printf("iWordWidth not supported")); |
|
512 r = KErrNotSupported; |
|
513 } |
|
514 else |
|
515 { |
|
516 if(spiHeader.iClkSpeedHz != 130000 && |
|
517 spiHeader.iClkSpeedHz != 260000 && |
|
518 spiHeader.iClkSpeedHz != 521000 && |
|
519 spiHeader.iClkSpeedHz != 1040000 && |
|
520 spiHeader.iClkSpeedHz != 2080000 && |
|
521 spiHeader.iClkSpeedHz != 4170000 && |
|
522 spiHeader.iClkSpeedHz != 16670000) |
|
523 { |
|
524 __KTRACE_OPT(KIIC, Kern::Printf("iClock not supported")); |
|
525 r = KErrNotSupported; |
|
526 } |
|
527 } |
|
528 } |
|
529 } |
|
530 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::CheckHdr() r %d", r)); |
|
531 return r; |
|
532 } |
|
533 |
|
534 // Initializes the hardware with the data provided in the transaction and slave-address field |
|
535 TInt DCsiChannelMaster::ConfigureInterface() |
|
536 { |
|
537 __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()")); |
|
538 |
|
539 HCR::TSettingId settingId; |
|
540 |
|
541 // CSI initialization procedure: |
|
542 // 1. clear CISE bit.. |
|
543 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, 0); |
|
544 |
|
545 // 2. wait until CIS_MODE.bit0 (CSOT) changes to 0 |
|
546 // start the GuardTimer before while-loop.. |
|
547 iTransactionStatus = KErrNone; |
|
548 |
|
549 iHwGuardTimer.OneShot(NKern::TimerTicks(iHwTimeoutValue)); |
|
550 |
|
551 while((iTransactionStatus == KErrNone) && |
|
552 AsspRegister::Read32(iChannelBase + KHoCSIModeControl) & KHtCSIModeTransferState); |
|
553 |
|
554 // check if the the guard timer or transaction timer hasn't expired.. |
|
555 if(iTransactionStatus != KErrNone) |
|
556 { |
|
557 return KErrGeneral; |
|
558 } |
|
559 else |
|
560 { |
|
561 iHwGuardTimer.Cancel(); |
|
562 } |
|
563 |
|
564 // 3. set CSIRST bit.. |
|
565 AsspRegister::Modify32(iChannelBase + KHoCSIControl, 0, KHtCSIControlCSIRst); |
|
566 |
|
567 // 4. set KHoCSIClockSelect register.. |
|
568 TUint32 val = 0; |
|
569 |
|
570 // CKP - ClocK Polarity (bit 4) 0: initial value is high, 1: initial val is low |
|
571 // DAP - Clock phase select (bit 3) 0: 180 degree delay, 1: 0 degree delay |
|
572 // @** don't use DAP=1 when iClock set to KHCSIClockValPCLKdiv4 (HW bug..see SoC errata) |
|
573 switch(iSpiHeader.iClkMode) |
|
574 { |
|
575 case ESpiPolarityLowRisingEdge: // Active high, odd edges |
|
576 val = KHtCSIClockSelectCKP; // CKP 1, DAP 0 |
|
577 break; |
|
578 case ESpiPolarityLowFallingEdge: // Active high, even edges |
|
579 val = KHtCSIClockSelectCKP | // CKP 1, DAP 1 |
|
580 KHtCSIClockSelectDAP; |
|
581 break; |
|
582 |
|
583 case ESpiPolarityHighFallingEdge: // Active low, odd edges |
|
584 val = 0; // CKP 0, DAP 0 |
|
585 break; |
|
586 |
|
587 case ESpiPolarityHighRisingEdge: // Active low, even edges |
|
588 val = KHtCSIClockSelectDAP; // CKP 0, DAP 1 |
|
589 break; |
|
590 default: |
|
591 break; // there's no default..no other value can be specified as it's an enum ;) |
|
592 } |
|
593 |
|
594 // set the clock.. |
|
595 switch(iSpiHeader.iClkSpeedHz) |
|
596 { |
|
597 case 130000: |
|
598 val |= KHCSIClockValPCLKdiv512; // 1/512 PCLK (master mode) 130 kHz |
|
599 break; |
|
600 case 260000: |
|
601 val |= KHCSIClockValPCLKdiv256; // 1/256 PCLK (master mode) 260 kHz |
|
602 break; |
|
603 case 521000: |
|
604 val |= KHCSIClockValPCLKdiv128; // 1/128 PCLK (master mode) 521 kHz |
|
605 break; |
|
606 case 1040000: |
|
607 val |= KHCSIClockValPCLKdiv64; // 1/64 PCLK (master mode) 1.04 MHz |
|
608 break; |
|
609 case 2080000: |
|
610 val |= KHCSIClockValPCLKdiv32; // 1/32 PCLK (master mode) 2.08 MHz |
|
611 break; |
|
612 case 4170000: |
|
613 val |= KHCSIClockValPCLKdiv16; // 1/16 PCLK (master mode) 4.17 MHz |
|
614 break; |
|
615 case 16670000: |
|
616 if(val & KHtCSIClockSelectDAP) // see @** |
|
617 { |
|
618 __KTRACE_OPT(KIIC, Kern::Printf("Unsupported CLK/Clock mode")); |
|
619 return KErrArgument; |
|
620 } |
|
621 val |= KHCSIClockValPCLKdiv4; // 1/4 PCLK (master mode) 16.67 MHz |
|
622 break; |
|
623 default: |
|
624 return KErrNotSupported; |
|
625 } |
|
626 |
|
627 // set transaction wait time.. |
|
628 val |= (0xf & iSpiHeader.iTransactionWaitCycles) << KHsCSIModeTWait; |
|
629 |
|
630 // and finally update the register |
|
631 AsspRegister::Write32(iChannelBase + KHoCSIClockSelect, val); |
|
632 |
|
633 // 5. clear KHtCSIControlCSIRst bit.. |
|
634 AsspRegister::Modify32(iChannelBase + KHoCSIControl, KHtCSIControlCSIRst, 0); |
|
635 |
|
636 // 6. Set Mode Control register: |
|
637 // Transmission and reception mode |
|
638 val = KHtCSIModeTrEnable; |
|
639 |
|
640 // Select transmit data length (8/16 bits) |
|
641 if(iSpiHeader.iWordWidth == ESpiWordWidth_16) |
|
642 { |
|
643 iWordSize = 2; |
|
644 val |= KHtCSIModeDataLen; |
|
645 } |
|
646 else |
|
647 { |
|
648 iWordSize = 1; |
|
649 } |
|
650 |
|
651 // Select Transfer direction (if set-LSB first) |
|
652 if(iSpiHeader.iBitOrder == ELsbFirst) |
|
653 { |
|
654 val |= KHtCSIModeTransferDir; |
|
655 } |
|
656 |
|
657 // update the register |
|
658 AsspRegister::Write32(iChannelBase + KHoCSIModeControl, val); |
|
659 |
|
660 // 7. Set FIFO trigger levels |
|
661 TUint8 csiFifoRxTrigerLvl; |
|
662 |
|
663 settingId.iCat = KHcrCat_HWServ_CSI; |
|
664 settingId.iKey = KHcrKey_CSI_FifoRxTrigerLvl; |
|
665 |
|
666 TInt r = HCR::GetUInt(settingId, csiFifoRxTrigerLvl); |
|
667 if(r != KErrNone) |
|
668 { |
|
669 __KTRACE_OPT(KIIC, Kern::Printf("Failed to read HCR setting for category = %d and key = %d\n", settingId.iCat, settingId.iKey)); |
|
670 return r; |
|
671 } |
|
672 AsspRegister::Write32(iChannelBase + KHoCSIFifoTrgLvl, (csiFifoRxTrigerLvl << KHsCSIRxFifoTrgLvl)); |
|
673 |
|
674 // 8. Clear all interrupts |
|
675 AsspRegister::Write32(iChannelBase + KHoCSIIntStatus, KInterruptsAll); |
|
676 |
|
677 // 9. Set RxTrig permission and enable TEnd and RxTrg interrupts |
|
678 AsspRegister::Write32(iChannelBase + KHoCSIControl, KHtCSIControlRxTrgEn | |
|
679 KHtCSIControlTEndIE | |
|
680 KHtCSIControlRxTrgIE); |
|
681 |
|
682 // 10. finally set CSIE bit |
|
683 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, 0, KHtCSIModeEnable); |
|
684 |
|
685 // enable and configure GPIO pin - it is CS Pin used for the transmission.. |
|
686 // if specified different (e.g. 0) - iSSPin will be ignored during the transmission.. |
|
687 if(iSSPin < 1 || iSSPin > 32) // can be 1 - 32 |
|
688 { |
|
689 __KTRACE_OPT(KIIC, Kern::Printf("Wrong pin number specified()")); |
|
690 return KErrArgument; |
|
691 } |
|
692 else |
|
693 { |
|
694 GPIO::SetPinMode(iSSPin, GPIO::EEnabled); |
|
695 GPIO::SetPinDirection(iSSPin, GPIO::EOutput); |
|
696 GPIO::SetDebounceTime(iSSPin, 0); |
|
697 |
|
698 // and set active high bit (KtCSPinHigh) |
|
699 if(iSpiHeader.iSSPinActiveMode == ESpiCSPinActiveHigh) |
|
700 { |
|
701 iSSPinActiveStateOn = GPIO::EHigh; |
|
702 iSSPinActiveStateOff = GPIO::ELow; |
|
703 } |
|
704 else |
|
705 { |
|
706 iSSPinActiveStateOn = GPIO::ELow; |
|
707 iSSPinActiveStateOff = GPIO::EHigh; |
|
708 } |
|
709 } |
|
710 |
|
711 // clear KHtCSIModeEnable.. it will be set to trigger the transmission in DoTransfer() |
|
712 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, 0); |
|
713 |
|
714 return KErrNone; |
|
715 } |
|
716 |
|
717 // This method starts data transfer - filling the Transmit FIFO and enabling the device (CSIE bit) |
|
718 TInt DCsiChannelMaster::DoTransfer(TInt8 *aBuff, TUint aNumOfBytes) |
|
719 { |
|
720 __KTRACE_OPT(KIIC, Kern::Printf("\nDCsiChannelMaster::DoTransfer()")); |
|
721 __KTRACE_OPT(KIIC, Kern::Printf("Receiving %d, Transmitting %d", iOperation.iOp.iIsReceiving, iOperation.iOp.iIsTransmitting)); |
|
722 |
|
723 if(aNumOfBytes == 0) // wanted to transfer an empty buffer?? |
|
724 { |
|
725 return KErrArgument; |
|
726 } |
|
727 |
|
728 // store current Tx buffer addresses - to use them later in the ISR, |
|
729 // when re-filling the buffer if its level drops down to the threshold value.. |
|
730 iTxData = aBuff; |
|
731 iTxDataEnd = (TInt8*) (aBuff + aNumOfBytes); |
|
732 __KTRACE_OPT(KIIC, Kern::Printf("Tx: Start: %x, End %x, bytes %d\n\n", iTxData, iTxDataEnd, aNumOfBytes)); |
|
733 |
|
734 // ensure, we won't be sending until we've filled up the FIFO.. |
|
735 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeEnable, 0); |
|
736 |
|
737 // clean the FIFO.. |
|
738 AsspRegister::Write32(iChannelBase + KHoCSIOFifoL, 0); |
|
739 |
|
740 // if we are transmitting - Add data to the FIFO.. |
|
741 if(iOperation.iOp.iIsTransmitting) |
|
742 { |
|
743 // Set mode to transmission and reception (Set TRMD) |
|
744 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, 0, KHtCSIModeTrEnable); |
|
745 |
|
746 while(AsspRegister::Read32(iChannelBase + KHoCSIOFifoL) < KHwCSIFifoLMax && // until FIFO not full.. |
|
747 iTxData != iTxDataEnd) // or whole data has been copied. |
|
748 { |
|
749 // copy data to the FIFO.. |
|
750 TUint16 val; |
|
751 |
|
752 // in 16bit mode we need to write two bytes as once MSB first.. |
|
753 if(iWordSize > 1) |
|
754 { |
|
755 val = (*iTxData) << 8; // MSB shifted up.. |
|
756 val |= *(iTxData + 1) & 0xff; // LSB.. |
|
757 } |
|
758 else |
|
759 { |
|
760 val = *iTxData; |
|
761 } |
|
762 |
|
763 // write this value to the register.. |
|
764 AsspRegister::Write32(iChannelBase + KHoCSIOFifo, val); |
|
765 |
|
766 // increment the pointer. |
|
767 iTxData += iWordSize; |
|
768 } |
|
769 } |
|
770 else // we are starting receive transfer only.. |
|
771 { |
|
772 // Set mode to reception only (clear TRMD) |
|
773 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, KHtCSIModeTrEnable, 0); |
|
774 } |
|
775 |
|
776 // change the SS line status - start of the transmission. This should be set back to high - after |
|
777 // the transmission has been finished (iTransferEndDfc) |
|
778 GPIO::SetOutputState(iSSPin, iSSPinActiveStateOn); |
|
779 |
|
780 // enable interrupts.. |
|
781 Interrupt::Enable(iInterruptId); |
|
782 |
|
783 // enable transmission..this will trigger the HW to start the transmission.. |
|
784 AsspRegister::Modify32(iChannelBase + KHoCSIModeControl, 0, KHtCSIModeEnable); |
|
785 |
|
786 return KErrNone; |
|
787 } |
|
788 |
|
789 // this method starts the given transfer.. |
|
790 TInt DCsiChannelMaster::StartTransfer(TIicBusTransfer* aTransferPtr, TUint8 aType) |
|
791 { |
|
792 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::StartTransfer()")); |
|
793 |
|
794 TInt r = KErrNone; |
|
795 |
|
796 switch(aType) |
|
797 { |
|
798 case TIicBusTransfer::EMasterWrite: |
|
799 { |
|
800 __KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterWrite, duplex=%d", iFullDTransfer)); |
|
801 |
|
802 const TDes8* aBufPtr = GetTferBuffer(aTransferPtr); |
|
803 |
|
804 __KTRACE_OPT(KIIC, Kern::Printf("Length %d, iWordSize %d", aBufPtr->Length(), iWordSize)); |
|
805 |
|
806 // set this flag - to indicate, that we'll be transmitting the data. |
|
807 // if this is set to 0 prior to calling to DoTransfer() - only '0'(or KValueSentOnRead) values will be |
|
808 // sent out of the interface (e.g. if only receiving from the slave - to generate the clock) |
|
809 iOperation.iOp.iIsTransmitting = ETrue; |
|
810 |
|
811 // initiate the transmission.. |
|
812 r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length()); |
|
813 |
|
814 if(r != KErrNone) |
|
815 { |
|
816 __KTRACE_OPT(KIIC, Kern::Printf("Starting Write filed, r = %d", r)); |
|
817 } |
|
818 break; |
|
819 } |
|
820 case TIicBusTransfer::EMasterRead: |
|
821 { |
|
822 __KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterRead, duplex=%x", iFullDTransfer)); |
|
823 |
|
824 // store the current address and ending address for Reception- to use them later in the ISR and DFC |
|
825 const TDes8* aBufPtr = GetTferBuffer(aTransferPtr); |
|
826 |
|
827 iRxData = (TInt8*) aBufPtr->Ptr(); |
|
828 iRxDataEnd = (TInt8*) (iRxData + aBufPtr->Length()); |
|
829 |
|
830 __KTRACE_OPT(KIIC, Kern::Printf("Rx: Start: %x, End %x, bytes %d", iRxData, iRxDataEnd, aBufPtr->Length())); |
|
831 |
|
832 // set the flag - that we're going to receive the data.. |
|
833 iOperation.iOp.iIsReceiving = ETrue; |
|
834 |
|
835 // if this is half-duplex transfer.. (read-only) |
|
836 // we still need to transmit '0' to generate CLK and SS signals for the Slave |
|
837 if(!iFullDTransfer) |
|
838 { |
|
839 // make sure transmitting flag is cleared |
|
840 iOperation.iOp.iIsTransmitting = EFalse; |
|
841 |
|
842 // set pointers - as if we're transmitting the same amount of data.. |
|
843 r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length()); |
|
844 } |
|
845 } |
|
846 break; |
|
847 default: |
|
848 { |
|
849 __KTRACE_OPT(KIIC, Kern::Printf("Unsupported TrasactionType %x", aType)); |
|
850 r = KErrArgument; |
|
851 break; |
|
852 } |
|
853 } |
|
854 |
|
855 return r; |
|
856 } |
|
857 |
|
858 // calling this method whenever a transfer has been finished - will process all transfers in the transaction |
|
859 TInt DCsiChannelMaster::ProcessNextTransfers() |
|
860 { |
|
861 // transfers are queued in linked list.. so just go through that list and start them |
|
862 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::ProcessNextTransfers(),BUSY=%d", iState)); |
|
863 |
|
864 // clear flags.. |
|
865 iOperation.iValue = TCsiOperationType::ENop; |
|
866 |
|
867 // in both cases - full-duplex - or simple half-duplex |
|
868 // we need to flush the buffers for the transmission: |
|
869 AsspRegister::Write32(iChannelBase + KHoCSIIFifoL, 0); // write 0 to FIFOL will drop data |
|
870 AsspRegister::Write32(iChannelBase + KHoCSIOFifoL, 0); // write 0 to FIFOL will drop data |
|
871 |
|
872 // if this is the first transfer in the transaction..(called from DoCreate) |
|
873 if(iState == EIdle) |
|
874 { |
|
875 // Get the pointer to half-duplex transfer object.. |
|
876 iHalfDTransfer = GetTransHalfDuplexTferPtr(iCurrTransaction); |
|
877 |
|
878 // Get the pointer to full-duplex transfer object.. |
|
879 iFullDTransfer = GetTransFullDuplexTferPtr(iCurrTransaction); |
|
880 |
|
881 // from now on - our state is EBusy. |
|
882 iState = EBusy; |
|
883 |
|
884 // kick-off the transaction timer.. |
|
885 // it's default handler, run in ISR context will change the iTransactionStatus |
|
886 // and queue TranferEndDfc (in the case if it wasn't queued) - this DFC |
|
887 // will then finish transfer and report this error to the PIL. |
|
888 __ASSERT_DEBUG(iSpiHeader.iTimeoutPeriod > 0, Kern::Fault("NE1_TB SPI: timeout value not set,line: %d", __LINE__)); |
|
889 __KTRACE_OPT(KIIC, Kern::Printf("Timeout for transaction %d", iSpiHeader.iTimeoutPeriod)); |
|
890 iTransactionStatus = KErrNone; |
|
891 // Initiate the timer for the transaction. When it expires - PIL will call |
|
892 // HandleSlaveTimeout() - which will stop the HW operations.. |
|
893 StartSlaveTimeOutTimer(iSpiHeader.iTimeoutPeriod); |
|
894 // When the KIIC trace flag is enabled, we need cancel the transaction timeout, |
|
895 // so that debug traces don't cause slave timer expiration. |
|
896 __KTRACE_OPT(KIIC, CancelTimeOut()); |
|
897 |
|
898 } |
|
899 else |
|
900 // We continue with next transfer in the transaction..(Called from TransferEndDfc) |
|
901 { |
|
902 // Get the pointer the next half-duplex transfer object.. |
|
903 iHalfDTransfer = GetTferNextTfer(iHalfDTransfer); |
|
904 |
|
905 // Get the pointer to the next half-duplex transfer object.. |
|
906 if(iFullDTransfer) |
|
907 { |
|
908 iFullDTransfer = GetTferNextTfer(iFullDTransfer); |
|
909 } |
|
910 } |
|
911 |
|
912 // all of the transfers were completed, just notify the PIL and return. |
|
913 // (if either Rx or Tx has not finished properly ExitComplete() would have been called |
|
914 // from TransferEndDfc if there was an error during the transfer) |
|
915 TInt r = KErrNone; |
|
916 if(!iFullDTransfer && !iHalfDTransfer) |
|
917 { |
|
918 __KTRACE_OPT(KIIC, Kern::Printf("All transfers completed successfully")); |
|
919 // complete the request.. |
|
920 ExitComplete(KErrNone); |
|
921 } |
|
922 else |
|
923 { |
|
924 // start transfers.. |
|
925 // if this is full-duplex transfer - we need to Start EMasterRead transfer first |
|
926 // this is because buffer pointers and flags for this transfer type must be initialized |
|
927 // prior to starting EMasterWrite (which always triggers the transmission start) |
|
928 TInt8 hDTrType = (TInt8) GetTferType(iHalfDTransfer); |
|
929 |
|
930 if(iFullDTransfer) |
|
931 { |
|
932 // if iHalfDTransfer is EMasterRead - start it first.. |
|
933 if(hDTrType == TIicBusTransfer::EMasterRead) |
|
934 { |
|
935 r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterRead); |
|
936 if(r != KErrNone) |
|
937 { |
|
938 return r; |
|
939 } |
|
940 r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterWrite); |
|
941 } |
|
942 else // hDTrType == TIicBusTransfer::EMasterWrite) |
|
943 { |
|
944 r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterRead); |
|
945 if(r != KErrNone) |
|
946 { |
|
947 return r; |
|
948 } |
|
949 r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterWrite); |
|
950 } |
|
951 } |
|
952 else |
|
953 // this is normal halfDuplex transfer - so just start it |
|
954 { |
|
955 r = StartTransfer(iHalfDTransfer, hDTrType); |
|
956 } |
|
957 } |
|
958 return r; |
|
959 } |
|
960 |
|
961 // Gateway function for PSL implementation - this method is an entry point - it is called by the PIL |
|
962 // to initiate the transaction. After finishing it's processing PSL calls CompleteRequest(error_status) |
|
963 TInt DCsiChannelMaster::DoRequest(TIicBusTransaction* aTransaction) |
|
964 { |
|
965 __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::DoRequest (aTransaction=0x%x)\n", aTransaction)); |
|
966 |
|
967 // Check, if pointers are not NULL.. |
|
968 if(!aTransaction || !GetTransactionHeader(aTransaction)) |
|
969 { |
|
970 return KErrArgument; |
|
971 } |
|
972 |
|
973 // Make sure if the PIL doesn't try to start another one- before we've confirmed the previous one.. |
|
974 if(iState != EIdle) |
|
975 { |
|
976 return KErrInUse; |
|
977 } |
|
978 |
|
979 // copy pointer to the transaction.. |
|
980 iCurrTransaction = aTransaction; |
|
981 |
|
982 // check, if Hardware needs re-configuration |
|
983 // (i.e. this transaction and SlaveAddress (iSSPin) are the same as for the previous one) |
|
984 TInt r = KErrNone; |
|
985 if(TransConfigDiffersFromPrev()) |
|
986 { |
|
987 r = ConfigureInterface(); |
|
988 if(r != KErrNone) |
|
989 { |
|
990 iSSPin = 0; |
|
991 return r; |
|
992 } |
|
993 } |
|
994 |
|
995 // start processing transfers of this transaction. |
|
996 r = ProcessNextTransfers(); |
|
997 |
|
998 return r; |
|
999 } |
|
1000 |