|
1 // Copyright (c) 2004-2009 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 // |
|
13 // Description: |
|
14 // template\template_assp\dmapsl.cpp |
|
15 // Template DMA Platform Specific Layer (PSL). |
|
16 // |
|
17 // |
|
18 |
|
19 |
|
20 #include <kernel/kern_priv.h> |
|
21 #include <template_assp.h> // /assp/template_assp/ |
|
22 #include <drivers/dma.h> |
|
23 |
|
24 // Debug support |
|
25 static const char KDmaPanicCat[] = "DMA PSL"; |
|
26 |
|
27 static const TInt KMaxTransferLen = 0x1FE0; // max transfer length for this DMAC |
|
28 static const TInt KMemAlignMask = 7; // memory addresses passed to DMAC must be multiple of 8 |
|
29 static const TInt KChannelCount = 16; // we got 16 channels |
|
30 static const TInt KDesCount = 1024; // DMA descriptor count |
|
31 |
|
32 |
|
33 class TDmaDesc |
|
34 // |
|
35 // Hardware DMA descriptor |
|
36 // |
|
37 { |
|
38 public: |
|
39 enum {KStopBitMask = 1}; |
|
40 public: |
|
41 TPhysAddr iDescAddr; |
|
42 TPhysAddr iSrcAddr; |
|
43 TPhysAddr iDestAddr; |
|
44 TUint32 iCmd; |
|
45 }; |
|
46 |
|
47 |
|
48 ////////////////////////////////////////////////////////////////////////////// |
|
49 // Test Support |
|
50 ////////////////////////////////////////////////////////////////////////////// |
|
51 |
|
52 TDmaTestInfo TestInfo = |
|
53 { |
|
54 0, |
|
55 0, |
|
56 0, |
|
57 0, |
|
58 NULL, |
|
59 0, |
|
60 NULL, |
|
61 0, |
|
62 NULL |
|
63 }; |
|
64 |
|
65 |
|
66 EXPORT_C const TDmaTestInfo& DmaTestInfo() |
|
67 // |
|
68 // |
|
69 // |
|
70 { |
|
71 return TestInfo; |
|
72 } |
|
73 |
|
74 |
|
75 ////////////////////////////////////////////////////////////////////////////// |
|
76 // Helper Functions |
|
77 ////////////////////////////////////////////////////////////////////////////// |
|
78 |
|
79 inline TBool IsHwDesAligned(TAny* aDes) |
|
80 // |
|
81 // Checks whether given hardware descriptor is 16-bytes aligned. |
|
82 // |
|
83 { |
|
84 return ((TLinAddr)aDes & 0xF) == 0; |
|
85 } |
|
86 |
|
87 |
|
88 static TUint32 DcmdReg(TInt aCount, TUint aFlags, TUint32 aPslInfo) |
|
89 // |
|
90 // Returns value to set in DMA command register or in descriptor command field. |
|
91 // |
|
92 { |
|
93 // TO DO: Construct CMD word from input values. |
|
94 // The return value should reflect the actual control word. |
|
95 return (aCount | aFlags | aPslInfo); |
|
96 } |
|
97 |
|
98 |
|
99 ////////////////////////////////////////////////////////////////////////////// |
|
100 // Derived Channel (Scatter/Gather) |
|
101 ////////////////////////////////////////////////////////////////////////////// |
|
102 |
|
103 class TTemplateSgChannel : public TDmaSgChannel |
|
104 { |
|
105 public: |
|
106 TDmaDesc* iTmpDes; |
|
107 TPhysAddr iTmpDesPhysAddr; |
|
108 }; |
|
109 |
|
110 |
|
111 ////////////////////////////////////////////////////////////////////////////// |
|
112 // Derived Controller Class |
|
113 ////////////////////////////////////////////////////////////////////////////// |
|
114 |
|
115 class TTemplateDmac : public TDmac |
|
116 { |
|
117 public: |
|
118 TTemplateDmac(); |
|
119 TInt Create(); |
|
120 private: |
|
121 // from TDmac (PIL pure virtual) |
|
122 virtual void Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr); |
|
123 virtual void StopTransfer(const TDmaChannel& aChannel); |
|
124 virtual TBool IsIdle(const TDmaChannel& aChannel); |
|
125 virtual TInt MaxTransferSize(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo); |
|
126 virtual TUint MemAlignMask(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo); |
|
127 // from TDmac (PIL virtual) |
|
128 virtual void InitHwDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount, |
|
129 TUint aFlags, TUint32 aPslInfo, TUint32 aCookie); |
|
130 virtual void ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr); |
|
131 virtual void AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr, |
|
132 const SDmaDesHdr& aNewHdr); |
|
133 virtual void UnlinkHwDes(const TDmaChannel& aChannel, SDmaDesHdr& aHdr); |
|
134 // other |
|
135 static void Isr(TAny* aThis); |
|
136 inline TDmaDesc* HdrToHwDes(const SDmaDesHdr& aHdr); |
|
137 private: |
|
138 static const SCreateInfo KInfo; |
|
139 public: |
|
140 TTemplateSgChannel iChannels[KChannelCount]; |
|
141 }; |
|
142 |
|
143 static TTemplateDmac Controller; |
|
144 |
|
145 const TDmac::SCreateInfo TTemplateDmac::KInfo = |
|
146 { |
|
147 KChannelCount, |
|
148 KDesCount, |
|
149 TDmac::KCapsBitHwDes, |
|
150 sizeof(TDmaDesc), |
|
151 EMapAttrSupRw | EMapAttrFullyBlocking |
|
152 }; |
|
153 |
|
154 |
|
155 TTemplateDmac::TTemplateDmac() |
|
156 // |
|
157 // Constructor. |
|
158 // |
|
159 : TDmac(KInfo) |
|
160 {} |
|
161 |
|
162 |
|
163 TInt TTemplateDmac::Create() |
|
164 // |
|
165 // Second phase construction. |
|
166 // |
|
167 { |
|
168 TInt r = TDmac::Create(KInfo); // Base class Create() |
|
169 if (r == KErrNone) |
|
170 { |
|
171 __DMA_ASSERTA(ReserveSetOfDes(KChannelCount) == KErrNone); |
|
172 for (TInt i=0; i < KChannelCount; ++i) |
|
173 { |
|
174 TDmaDesc* pD = HdrToHwDes(*iFreeHdr); |
|
175 iChannels[i].iTmpDes = pD; |
|
176 iChannels[i].iTmpDesPhysAddr = DesLinToPhys(pD); |
|
177 iFreeHdr = iFreeHdr->iNext; |
|
178 } |
|
179 r = Interrupt::Bind(EAsspIntIdDma, Isr, this); |
|
180 if (r == KErrNone) |
|
181 { |
|
182 // TO DO: Map DMA clients (requests) to DMA channels here. |
|
183 |
|
184 r = Interrupt::Enable(EAsspIntIdDma); |
|
185 } |
|
186 } |
|
187 return r; |
|
188 } |
|
189 |
|
190 |
|
191 void TTemplateDmac::Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr) |
|
192 // |
|
193 // Initiates a (previously constructed) request on a specific channel. |
|
194 // |
|
195 { |
|
196 const TUint8 i = static_cast<TUint8>(aChannel.PslId()); |
|
197 TDmaDesc* pD = HdrToHwDes(aHdr); |
|
198 |
|
199 __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::Transfer channel=%d des=0x%08X", i, pD)); |
|
200 |
|
201 // TO DO (for instance): Load the first descriptor address into the DMAC and start it |
|
202 // by setting the RUN bit. |
|
203 (void) *pD, (void) i; |
|
204 |
|
205 } |
|
206 |
|
207 |
|
208 void TTemplateDmac::StopTransfer(const TDmaChannel& aChannel) |
|
209 // |
|
210 // Stops a running channel. |
|
211 // |
|
212 { |
|
213 const TUint8 i = static_cast<TUint8>(aChannel.PslId()); |
|
214 |
|
215 __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::StopTransfer channel=%d", i)); |
|
216 |
|
217 // TO DO (for instance): Clear the RUN bit of the channel. |
|
218 (void) i; |
|
219 |
|
220 } |
|
221 |
|
222 |
|
223 TBool TTemplateDmac::IsIdle(const TDmaChannel& aChannel) |
|
224 // |
|
225 // Returns the state of a given channel. |
|
226 // |
|
227 { |
|
228 const TUint8 i = static_cast<TUint8>(aChannel.PslId()); |
|
229 |
|
230 __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::IsIdle channel=%d", i)); |
|
231 |
|
232 // TO DO (for instance): Return the state of the RUN bit of the channel. |
|
233 // The return value should reflect the actual state. |
|
234 (void) i; |
|
235 |
|
236 return ETrue; |
|
237 } |
|
238 |
|
239 |
|
240 TInt TTemplateDmac::MaxTransferSize(TDmaChannel& /*aChannel*/, TUint /*aFlags*/, TUint32 /*aPslInfo*/) |
|
241 // |
|
242 // Returns the maximum transfer size for a given transfer. |
|
243 // |
|
244 { |
|
245 // TO DO: Determine the proper return value, based on the arguments. |
|
246 |
|
247 // For instance: |
|
248 return KMaxTransferLen; |
|
249 } |
|
250 |
|
251 |
|
252 TUint TTemplateDmac::MemAlignMask(TDmaChannel& /*aChannel*/, TUint /*aFlags*/, TUint32 /*aPslInfo*/) |
|
253 // |
|
254 // Returns the memory buffer alignment restrictions mask for a given transfer. |
|
255 // |
|
256 { |
|
257 // TO DO: Determine the proper return value, based on the arguments. |
|
258 |
|
259 // For instance: |
|
260 return KMemAlignMask; |
|
261 } |
|
262 |
|
263 |
|
264 void TTemplateDmac::InitHwDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount, |
|
265 TUint aFlags, TUint32 aPslInfo, TUint32 /*aCookie*/) |
|
266 // |
|
267 // Sets up (from a passed in request) the descriptor with that fragment's source and destination address, |
|
268 // the fragment size, and the (driver/DMA controller) specific transfer parameters (mem/peripheral, |
|
269 // burst size, transfer width). |
|
270 // |
|
271 { |
|
272 TDmaDesc* pD = HdrToHwDes(aHdr); |
|
273 |
|
274 __KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::InitHwDes 0x%08X", pD)); |
|
275 |
|
276 // Unaligned descriptor? Bug in generic layer! |
|
277 __DMA_ASSERTD(IsHwDesAligned(pD)); |
|
278 |
|
279 pD->iSrcAddr = (aFlags & KDmaPhysAddrSrc) ? aSrc : Epoc::LinearToPhysical(aSrc); |
|
280 pD->iDestAddr = (aFlags & KDmaPhysAddrDest) ? aDest : Epoc::LinearToPhysical(aDest); |
|
281 pD->iCmd = DcmdReg(aCount, aFlags, aPslInfo); |
|
282 pD->iDescAddr = TDmaDesc::KStopBitMask; |
|
283 } |
|
284 |
|
285 |
|
286 void TTemplateDmac::ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr) |
|
287 // |
|
288 // Chains hardware descriptors together by setting the next pointer of the original descriptor |
|
289 // to the physical address of the descriptor to be chained. |
|
290 // |
|
291 { |
|
292 TDmaDesc* pD = HdrToHwDes(aHdr); |
|
293 TDmaDesc* pN = HdrToHwDes(aNextHdr); |
|
294 |
|
295 __KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::ChainHwDes des=0x%08X next des=0x%08X", pD, pN)); |
|
296 |
|
297 // Unaligned descriptor? Bug in generic layer! |
|
298 __DMA_ASSERTD(IsHwDesAligned(pD) && IsHwDesAligned(pN)); |
|
299 |
|
300 // TO DO: Modify pD->iCmd so that no end-of-transfer interrupt gets raised any longer. |
|
301 |
|
302 pD->iDescAddr = DesLinToPhys(pN); |
|
303 } |
|
304 |
|
305 |
|
306 void TTemplateDmac::AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr, |
|
307 const SDmaDesHdr& aNewHdr) |
|
308 // |
|
309 // Appends a descriptor to the chain while the channel is running. |
|
310 // |
|
311 { |
|
312 const TUint8 i = static_cast<TUint8>(aChannel.PslId()); |
|
313 |
|
314 TDmaDesc* pL = HdrToHwDes(aLastHdr); |
|
315 TDmaDesc* pN = HdrToHwDes(aNewHdr); |
|
316 |
|
317 __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::AppendHwDes channel=%d last des=0x%08X new des=0x%08X", |
|
318 i, pL, pN)); |
|
319 // Unaligned descriptor? Bug in generic layer! |
|
320 __DMA_ASSERTD(IsHwDesAligned(pL) && IsHwDesAligned(pN)); |
|
321 |
|
322 TPhysAddr newPhys = DesLinToPhys(pN); |
|
323 |
|
324 const TInt irq = NKern::DisableAllInterrupts(); |
|
325 StopTransfer(aChannel); |
|
326 |
|
327 pL->iDescAddr = newPhys; |
|
328 const TTemplateSgChannel& channel = static_cast<const TTemplateSgChannel&>(aChannel); |
|
329 TDmaDesc* pD = channel.iTmpDes; |
|
330 |
|
331 // TO DO: Implement the appropriate algorithm for appending a descriptor here. |
|
332 (void) *pD, (void) i; |
|
333 |
|
334 NKern::RestoreInterrupts(irq); |
|
335 |
|
336 __KTRACE_OPT(KDMA, Kern::Printf("<TTemplateDmac::AppendHwDes")); |
|
337 } |
|
338 |
|
339 |
|
340 void TTemplateDmac::UnlinkHwDes(const TDmaChannel& /*aChannel*/, SDmaDesHdr& aHdr) |
|
341 // |
|
342 // Unlink the last item in the h/w descriptor chain from a subsequent chain that it was |
|
343 // possibly linked to. |
|
344 // |
|
345 { |
|
346 __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::UnlinkHwDes")); |
|
347 TDmaDesc* pD = HdrToHwDes(aHdr); |
|
348 pD->iDescAddr = TDmaDesc::KStopBitMask; |
|
349 |
|
350 // TO DO: Modify pD->iCmd so that an end-of-transfer interrupt will get raised. |
|
351 |
|
352 } |
|
353 |
|
354 |
|
355 void TTemplateDmac::Isr(TAny* aThis) |
|
356 // |
|
357 // This ISR reads the interrupt identification and calls back into the base class |
|
358 // interrupt service handler with the channel identifier and an indication whether the |
|
359 // transfer completed correctly or with an error. |
|
360 // |
|
361 { |
|
362 TTemplateDmac& me = *static_cast<TTemplateDmac*>(aThis); |
|
363 |
|
364 // TO DO: Implement the behaviour described above, call HandleIsr(). |
|
365 |
|
366 HandleIsr(me.iChannels[5], 0); // Example |
|
367 |
|
368 } |
|
369 |
|
370 |
|
371 inline TDmaDesc* TTemplateDmac::HdrToHwDes(const SDmaDesHdr& aHdr) |
|
372 // |
|
373 // Changes return type of base class call. |
|
374 // |
|
375 { |
|
376 return static_cast<TDmaDesc*>(TDmac::HdrToHwDes(aHdr)); |
|
377 } |
|
378 |
|
379 |
|
380 ////////////////////////////////////////////////////////////////////////////// |
|
381 // Channel Opening/Closing (Channel Allocator) |
|
382 ////////////////////////////////////////////////////////////////////////////// |
|
383 |
|
384 TDmaChannel* DmaChannelMgr::Open(TUint32 aOpenId) |
|
385 // |
|
386 // |
|
387 // |
|
388 { |
|
389 __KTRACE_OPT(KDMA, Kern::Printf(">DmaChannelMgr::Open aOpenId=%d", aOpenId)); |
|
390 |
|
391 __DMA_ASSERTA(aOpenId < static_cast<TUint32>(KChannelCount)); |
|
392 |
|
393 TDmaChannel* pC = Controller.iChannels + aOpenId; |
|
394 if (pC->IsOpened()) |
|
395 pC = NULL; |
|
396 else |
|
397 { |
|
398 pC->iController = &Controller; |
|
399 pC->iPslId = aOpenId; |
|
400 } |
|
401 |
|
402 return pC; |
|
403 } |
|
404 |
|
405 |
|
406 void DmaChannelMgr::Close(TDmaChannel* /* aChannel */) |
|
407 // |
|
408 // |
|
409 // |
|
410 { |
|
411 // NOP |
|
412 } |
|
413 |
|
414 |
|
415 TInt DmaChannelMgr::StaticExtension(TInt /* aCmd */, TAny* /* aArg */) |
|
416 // |
|
417 // |
|
418 // |
|
419 { |
|
420 return KErrNotSupported; |
|
421 } |
|
422 |
|
423 |
|
424 ////////////////////////////////////////////////////////////////////////////// |
|
425 // DLL Exported Function |
|
426 ////////////////////////////////////////////////////////////////////////////// |
|
427 |
|
428 DECLARE_STANDARD_EXTENSION() |
|
429 // |
|
430 // Creates and initializes a new DMA controller object on the kernel heap. |
|
431 // |
|
432 { |
|
433 __KTRACE_OPT2(KBOOT, KDMA, Kern::Printf("Starting DMA Extension")); |
|
434 |
|
435 return Controller.Create(); |
|
436 } |