|
1 <?xml version="1.0" encoding="utf-8"?> |
|
2 <!-- Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. --> |
|
3 <!-- This component and the accompanying materials are made available under the terms of the License |
|
4 "Eclipse Public License v1.0" which accompanies this distribution, |
|
5 and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". --> |
|
6 <!-- Initial Contributors: |
|
7 Nokia Corporation - initial contribution. |
|
8 Contributors: |
|
9 --> |
|
10 <!DOCTYPE concept |
|
11 PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd"> |
|
12 <concept id="GUID-2E54DA7D-1094-41C6-AFB0-9999471991F8" xml:lang="en"><title>Interrupt Implementation Guide</title><shortdesc>Describes how to implement the Interrupt class.</shortdesc><prolog><metadata><keywords/></metadata></prolog><conbody> |
|
13 <section id="GUID-514E76B0-009C-48D4-935C-FBE48FDD3631-GENID-1-2-1-10-1-5-1-7-1-1-9-1-4-1-3-1"><title>Introduction</title> <p>Interrupt handling is implemented by writing platform |
|
14 specific versions of the structures and functions of the Interrupt |
|
15 class. The details of the implementation depend on hardware and the |
|
16 architecture of the device. This document describes a simple implementation |
|
17 of the structures and functions and then discusses more elaborate |
|
18 strategies required with the use of device specific interrupts , chained |
|
19 interrupts and multiple interrupt sources. It also covers implementation |
|
20 on unicore platforms only. The SMP version of the kernel now implements |
|
21 support for the ARM Generic Interrupt Controller and relies on its |
|
22 use.</p><ul> |
|
23 <li><p>Chained interrupts are interrupts which are output by one controller |
|
24 and input to another. They need to be distinguished in the ISR table |
|
25 and require extensions to the handler functions.</p></li> |
|
26 <li><p>Multiple interrupt sources to the same ISR require the use |
|
27 of pseudo-interrupts.</p></li> |
|
28 <li><p>When a Symbian port is split into an ASSP and variant (common |
|
29 and device specific) layer, the variant may include additional interrupt |
|
30 sources. Their API is defined by the port. Device specific interrupts |
|
31 are sometimes used to handle interrupts from peripherals: another |
|
32 technique is to route peripheral interrupts to a GPIO pin. Peripheral |
|
33 interrupts cannot be specified as part of the ASSP layer.</p></li> |
|
34 </ul><p>The Template Baseport provides a skeleton implementation for |
|
35 developers to modify at <filepath>kernelhwsrv/bsptemplate/asspandvariant/template_assp/interrupts.cpp</filepath>.</p> </section> |
|
36 <section id="GUID-35383FC7-4099-4048-B4B8-F84D10259036"><title>The |
|
37 ISR table</title><p>The ISR table is a data structure which pairs |
|
38 each ISR with the interrupt source to which it will be bound. It must |
|
39 have enough space for each interrupt source on the device. It is implemented |
|
40 as an array of <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-B864263D-7682-3938-9180-030C3D123174"><apiname>Interrupt::SInterruptHander</apiname></xref> of size <xref href="GUID-2A04B9AF-92F3-3A5C-93AD-3DD52E20A6D3.dita"><apiname>KINterruptSourceCount</apiname></xref> in which the interrupt Id is used |
|
41 as an index to the table. This example code assumes a size of 32.</p><codeblock xml:space="preserve">... |
|
42 const TInt KInterruptSourceCount = 32; |
|
43 SInterruptHandler IsrHandlers[KInterruptSourceCount]; |
|
44 ...</codeblock></section> |
|
45 <section id="GUID-393C43D1-6A48-473F-AE6F-E3E6F474762B"><title>DisableAndClearAll()</title><p>Interrupts must be disabled and cleared with a call to <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-3486EC50-55C5-366B-8BE2-E1180F52AB05"><apiname>Interrupt::DisableAndClearAll()</apiname></xref> before the call to <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-41FEFEA2-27C8-33F3-8781-89C092AE41B6"><apiname>Interrupt::Init1()</apiname></xref> at the start of initialization. Implementation |
|
46 of this function is entirely hardware dependent.</p></section> |
|
47 <section id="GUID-7CD1BE2F-CDDC-46DC-95CE-4ADF909F0494"><title>Init1()</title><p>The kernel is initialized in phases, interrupts being involved |
|
48 in the first phase and sometimes the third. The Init1() function should |
|
49 be implemented to</p><ul> |
|
50 <li><p>Initialize the ISR table, binding all ISRs to the spurious |
|
51 interrupts handler.</p></li> |
|
52 <li><p>Register the dispatcher functions.</p></li> |
|
53 <li><p>Bind ISRs which handle chained or pseudo interrupts.</p></li> |
|
54 </ul><p>Interrupts must be disabled during first phase initialization.</p><p>This example code illustrates initialization of the ISR table.</p><codeblock xml:space="preserve">... |
|
55 const TInt KInterruptSourceCount = 32; |
|
56 SInterruptHandler IsrHandlers[KInterruptSourceCount]; |
|
57 ... |
|
58 |
|
59 for( TInt i = 0; i < KInterruptSourceCount; ++i ) |
|
60 { |
|
61 IsrHandlers[i].iIsr = SpuriousHandler; |
|
62 IsrHandlers[i].iPtr = (TAny*)i; |
|
63 }</codeblock><p>This example code illustrates an implementation |
|
64 of <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-41FEFEA2-27C8-33F3-8781-89C092AE41B6"><apiname>Interrupt::Init1()</apiname></xref> after the point at which |
|
65 the ISR table has been initialized.</p><codeblock xml:space="preserve">void TemplateInterrupt::Init1() |
|
66 { |
|
67 // |
|
68 // need to hook the ARM IRQ and FIQ handlers as early as possible |
|
69 // and disable and clear all interrupt sources |
|
70 // |
|
71 ... |
|
72 DisableAndClearAll(); |
|
73 Arm::SetIrqHandler((TLinAddr)TemplateInterrupt::IrqDispatch); |
|
74 Arm::SetFiqHandler((TLinAddr)TemplateInterrupt::FiqDispatch); |
|
75 } |
|
76 </codeblock></section> |
|
77 <section id="GUID-3FCBCFFC-C3E6-4439-86A4-4845B21CF3CF"><title>Init3()</title><p>Third phase initialization involves initializing various interrupt |
|
78 handlers and sources which can only be initialized when the kernel |
|
79 is fully functional. This is done with a call to <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-D77022E6-FA45-3C00-95A3-B62792359997"><apiname>Interrupt::Init3()</apiname></xref>.</p><p>It is important to remember that interrupts are enabled during |
|
80 third phase initialization.</p></section> |
|
81 <section id="GUID-C4CD4CA8-175B-4995-8DC3-7D7F7D62E3FB"><title>Spurious()</title><p>Interrupts not bound to a real ISR must be bound to a 'spurious' |
|
82 handler function <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-701CE497-EF6D-3557-8558-5C9354C1039B"><apiname>Interrupt::Spurious()</apiname></xref> which returns |
|
83 an error with the number of the interrupt.</p></section> |
|
84 <section id="GUID-D5367FEE-AC77-4317-B0CA-A8F0690AACD1"><title>Bind()</title><p>The <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-4E3CB472-3525-32F8-9BC4-8ECFEE931E7B"><apiname>Interrupt::Bind()</apiname></xref> function binds ISRs to |
|
85 interrupt sources.</p><p>The argument <codeph>aId</codeph> is the |
|
86 Id of an interrupt source and is used to index an entry in the ISR |
|
87 table. Set the <codeph>iPtr</codeph> and <codeph>iIsr</codeph> members |
|
88 of that entry (ISR parameter and ISR pointer) to the passed in values <codeph>aPtr</codeph> and <codeph>aIsr</codeph>.</p><p>The implementation |
|
89 should perform some preliminary checks.</p><ul> |
|
90 <li><p>The interrupt Id must be checked for validity</p></li> |
|
91 <li><p>The ISR must not already be bound to a real interrupt. It should |
|
92 have been bound to the spurious interrupt handler at initialization.</p></li> |
|
93 </ul><p>All interrupts must be disabled during binding.</p><p>This |
|
94 example code provides a basic implementation.</p><codeblock xml:space="preserve">EXPORT_C TInt Interrupt::Bind(TInt aId, TIsr aIsr, TAny* aPtr) |
|
95 { |
|
96 TInt r = KErrNone; |
|
97 If(TUint(aId)>=TUint(KInterruptSourceCount)) |
|
98 { |
|
99 r = KErrArgument; // Illegal interrupt number |
|
100 } |
|
101 else |
|
102 { |
|
103 SInterruptHandler& h = IsrHandlers[aId]; |
|
104 TInt irq = NKern::DisableAllInterrupts(); |
|
105 if (h.iIsr != &SpuriousHandler) |
|
106 { |
|
107 r = KErrInUse; // Already bound to an ISR |
|
108 } |
|
109 else |
|
110 { |
|
111 h.iPtr = aPtr; // The ISR parameter |
|
112 h.iIsr = aIsr; // Pointer to the ISR |
|
113 } |
|
114 NKern::RestoreInterrupts(irq); |
|
115 } |
|
116 return r; |
|
117 } |
|
118 </codeblock><p>The implementation of <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-4E3CB472-3525-32F8-9BC4-8ECFEE931E7B"><apiname>Interrupt::Bind()</apiname></xref> can be more complicated in the case of chained interrupts, multiple |
|
119 interrupt sources, pseudo interrupt sources and device interrupts: |
|
120 see the discussion of those topics.</p></section> |
|
121 <section id="GUID-BEB323CF-9DDC-4486-AD32-682B78AF2072"><title>Unbind()</title><p>The <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-CCC9A397-608C-3EAF-830F-A59800C2E8E5"><apiname>Interrupt::Unbind()</apiname></xref> function unbinds ISRs |
|
122 from interrupt sources.</p><p>The argument <codeph>aId</codeph> is |
|
123 the Id of an interrupt source and is used to index an entry in the |
|
124 ISR table. Reset the entry in the ISR table to reference the spurious |
|
125 handler function.</p><p>The implementation should perform some preliminary |
|
126 checks.</p><ul> |
|
127 <li><p>The interrupt Id must be checked for validity</p></li> |
|
128 <li><p>The ISR must not already be unbound (that is, bound to the |
|
129 spurious interrupt handler).</p></li> |
|
130 </ul><p>All interrupts must be disabled during unbinding.</p><p>This |
|
131 example code provides a basic implementation.</p><codeblock xml:space="preserve">EXPORT_C TInt Interrupt::Unbind(TInt aId) |
|
132 { |
|
133 TInt r = KErrNone; |
|
134 if (TUint(aId) >= TUint(KInterruptSourceCount)) |
|
135 { |
|
136 r = KErrArgument; // Illegal interrupt number |
|
137 } |
|
138 else |
|
139 { |
|
140 SInterruptHandler& h = IsrHandlers[aId]; |
|
141 TInt irq = NKern::DisableAllInterrupts(); |
|
142 if (h.iIsr == &SpuriousHandler) |
|
143 { |
|
144 r = KErrGeneral; // Already unbound |
|
145 } |
|
146 else |
|
147 { |
|
148 h.iPtr =(TAny*)aId; |
|
149 h.iIsr = SpuriousHandler; // Replace with spurious handler |
|
150 // NOTE: at this point it may be wise to |
|
151 // force the hardware interrupt source to disabled. |
|
152 } |
|
153 NKern::RestoreInterrupts(irq); |
|
154 } |
|
155 return r; |
|
156 } |
|
157 </codeblock><p>The implementation of <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-CCC9A397-608C-3EAF-830F-A59800C2E8E5"><apiname>Interrupt::Unbind()</apiname></xref> can be more complicated in the case of chained interrupts, multiple |
|
158 interrupt sources, pseudo interrupt sources and device interrupts: |
|
159 see the discussion of those topics below.</p></section> |
|
160 <section id="GUID-F8859FAE-9FA3-494E-A294-0ACF7158BD40"><title>Enable()</title><p>Device drivers call the <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-BB169E6E-D8F9-3762-899D-6DBA4B29CF87"><apiname>Interrupt::Enable()</apiname></xref> function to enable the interrupt source identified by the argument <codeph>anId</codeph> in the interrupt controller hardware. </p><p>The implementation |
|
161 is entirely hardware dependent.</p><p>This example involves a check |
|
162 for chained interrupts, which are discussed in their own section below.</p><codeblock xml:space="preserve">EXPORT_C TInt Interrupt::Enable(TInt anId) |
|
163 { |
|
164 TInt r=KErrNone; |
|
165 // if ID indicates a chained interrupt, call variant... |
|
166 if (anId<0 && ((((TUint)anId)>>16)&0x7fff)<(TUint)KNumTemplateInts) |
|
167 r=TemplateAssp::Variant->InterruptEnable(anId); |
|
168 else if ((TUint)anId>=(TUint)KNumTemplateInts) |
|
169 r=KErrArgument; |
|
170 else if (TemplateInterrupt::Handlers[anId].iIsr==TemplateInterrupt::Spurious) |
|
171 r=KErrNotReady; |
|
172 else |
|
173 { |
|
174 // |
|
175 // TO DO: (mandatory) |
|
176 // |
|
177 // Enable the corresponding Hardware Interrupt source |
|
178 // |
|
179 } |
|
180 return r; |
|
181 } |
|
182 </codeblock></section> |
|
183 <section id="GUID-A508AA5E-E81A-4FC0-97DD-41543FF458E6"><title>Disable()</title><p>Device drivers call the <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-2D14E023-E6ED-39BF-8B31-6FA510957A8A"><apiname>Interrupt::Disable()</apiname></xref> function to disable the interrupt source identified by the argument <codeph>anId</codeph> in the interrupt controller hardware. The implementation |
|
184 is entirely hardware dependent.</p></section> |
|
185 <section id="GUID-53F6AAE1-3AE7-49E6-845D-135E43449F9F"><title>Clear()</title><p>Device drivers call the <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-DB641A23-82B2-373E-A514-E11118CB6E69"><apiname>Interrupt::Clear()</apiname></xref> function |
|
186 to acknowledge that they have serviced the interrupt and cleared the |
|
187 pending flag in the interrupt controller hardware. The implementation |
|
188 is entirely hardware dependent.</p><p><xref href="GUID-5BCEAABF-D060-3F29-A8AE-0C14A8DFC1D2.dita"><apiname>Clear()</apiname></xref> is |
|
189 a useful function in cases where an interrupt must be cleared explicitly |
|
190 rather than as a side effect of I/O register access: for instance |
|
191 in PC card and MMC controller code.</p></section> |
|
192 <section id="GUID-3EB891CD-91C7-4B66-B41F-6F19CAF717CF"><title>SetPriority()</title><p>The <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-FA4CFED7-D694-399C-8F84-FA9FE3C3A171"><apiname>Interrupt::SetPriority()</apiname></xref> function associates |
|
193 a priority value (passed as a<codeph> TInt</codeph>) with an interrupt |
|
194 Id. The meaning of the priority value is entirely hardware dependent.</p><p>Priority is a property of interrupts on some hardware, an example |
|
195 being OMAP. Where the hardware is of this type, <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-FA4CFED7-D694-399C-8F84-FA9FE3C3A171"><apiname>Interrupt::SetPriority()</apiname></xref> can be used to modify priorities in hardware. A simple use is to |
|
196 determine whether an interrupt generates an IRQ or an FIQ. If priority |
|
197 adjustment is not supported or not specified, the function should |
|
198 simply return <codeph>KErrNotSupported</codeph>.</p><p>The implementation |
|
199 is entirely hardware dependent.</p></section> |
|
200 <section id="GUID-ABE454D5-E219-48D0-A38C-7A63FBB0C088"><title>IrqDispatch() |
|
201 and FiqDispatch()</title><p>The functions <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-9CD4CECF-9DF9-3E94-BF17-45F387228A76"><apiname>Interrupt::IrqDispatch()</apiname></xref> and <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-B12F9319-11D6-3E43-AE66-6654DF15D1E3"><apiname>Interrupt::FiqDispatch()</apiname></xref> dispatch an interrupt |
|
202 by calling the associated ISR. Interrupts are either IRQ or FIQ interrupts |
|
203 and separate dispatch functions must be provided for each type. What |
|
204 follows refers to IRQs but applies equally to FIQs as the distinction only operates at the level of hardware and the two |
|
205 dispatch functions look the same.</p><p>In the simplest implementation, |
|
206 the interrupt Id is used as an index into the ISR table. The <codeph>iIsr</codeph> member of the entry is called as a function with the <codeph>iPtr</codeph> member as its argument. The interrupt Id is taken from |
|
207 a list of pending IRQs as in this example code.</p><codeblock xml:space="preserve">void IrqDispatch() |
|
208 { |
|
209 TUint32 pendingIrqs = TheAssp::IrqPendingRegister(); |
|
210 // for the purposes of this example we assume that reading |
|
211 // this register also clears the pending flags |
|
212 |
|
213 TInt index = 0; |
|
214 while( pendingIrqs ) |
|
215 { |
|
216 // while there is at least one pending IRQ |
|
217 if( pendingIrqs & 1 ) |
|
218 { |
|
219 // the next interrupt is pending - dispatch it |
|
220 (IsrHandlers[index].iIsr)(IsrHandlers[index].iPtr); |
|
221 } |
|
222 ++index; |
|
223 pendingIrqs >>= 1; |
|
224 } |
|
225 } |
|
226 </codeblock><p>This code is a simplified example which assumes that</p><ul> |
|
227 <li><p>The interrupt controller provides 32 interrupt sources and |
|
228 has a 32 bit pending interrupt register where a 1 indicates a pending |
|
229 interrupt and all ones are cleared when the register is read.</p></li> |
|
230 <li><p>The interrupt source represented by the low order bit in the |
|
231 pending interrupt register is represented by interrupt Id 0 and so |
|
232 on.</p></li> |
|
233 </ul><p>Implementation will be more complex where chained interrupts |
|
234 and multiple interrupt sources are involved, as discussed below.</p><p>Dispatch functions are time critical. You will probably write |
|
235 an initial implementation in C++ to get them working and then rewrite |
|
236 in assembler for maximum efficiency.</p><codeblock xml:space="preserve"/></section> |
|
237 <section id="GUID-457E3092-8FE3-4CB4-8910-11E03DD8C82D"><title>Chained |
|
238 interrupts</title><p>A platform often has multiple interrupt controllers |
|
239 of higher and lower priority (higher and lower level controllers), |
|
240 organized so that the output of a lower level controller is one of |
|
241 the inputs to a higher level controller. Interrupt sources organized |
|
242 in this way are called chained interrupts.</p><p>In a system with |
|
243 chained interrupts, the ISR table must be structured so that interrupts |
|
244 from higher and lower level controllers can be distinguished by their |
|
245 Ids.</p><p>The <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-4E3CB472-3525-32F8-9BC4-8ECFEE931E7B"><apiname>Interrupt::Bind()</apiname></xref> and <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-CCC9A397-608C-3EAF-830F-A59800C2E8E5"><apiname>Interrupt::Unbind()</apiname></xref> functions are the same whether interrupts are chained or not.</p><p>In a system with chained interrupts it can be desirable to write |
|
246 the <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-BB169E6E-D8F9-3762-899D-6DBA4B29CF87"><apiname>Interrupt::Enable()</apiname></xref> and <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-2D14E023-E6ED-39BF-8B31-6FA510957A8A"><apiname>Interrupt::Disable()</apiname></xref> functions so as to disable not the interrupt itself but a the higher |
|
247 level interrupt on the controller to which it is the input.</p><p>The <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-9CD4CECF-9DF9-3E94-BF17-45F387228A76"><apiname>Interrupt::IrqDispatch()</apiname></xref> and <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-B12F9319-11D6-3E43-AE66-6654DF15D1E3"><apiname>Interrupt::FiqDispatch()</apiname></xref> functions need to be extended in a system with chained interrupts. |
|
248 There are two techniques for doing this, both of which involve a separate |
|
249 second level dispatch function, but which differ in the way it is |
|
250 called. </p><ul> |
|
251 <li><p>In one technique, the main interrupt dispatcher calls the second |
|
252 level dispatcher if the relevant condition is satisfied. </p></li> |
|
253 <li><p>In the other technique, the second level dispatcher is bound |
|
254 directly to an interrupt source as its ISR. </p></li> |
|
255 </ul><p>The first technique works well in cases where there is only |
|
256 a main and a secondary interrupt controller. It does not scale well |
|
257 in cases which make use of multiple controllers chained to substantial |
|
258 depth.</p><p>You need to allocate locations in your ISR table for |
|
259 the secondary controllers so that the interrupt Id identifies which |
|
260 hardware controller the input is on. For example, if each interrupt |
|
261 controller handles 32 interrupt sources, you could allocate the first |
|
262 32 Ids to the highest level controller, the next 32 to a second level |
|
263 controller and so on.</p><p>This example code illustrates a main dispatcher |
|
264 which calls a second level dispatcher.</p><codeblock xml:space="preserve">void IrqDispatch() |
|
265 { |
|
266 TUint32 pendingIrqs = TheAssp::IrqPendingRegister(); |
|
267 |
|
268 TInt index = 0; |
|
269 while( pendingIrqs ) |
|
270 { |
|
271 if( pendingIrqs & 1 ) |
|
272 { |
|
273 if( index == EMainIntChainIrq ) |
|
274 { |
|
275 // second-level controller is signalling |
|
276 SecondLevelIrqDispatch(); |
|
277 } |
|
278 else |
|
279 { |
|
280 // call ISR |
|
281 (IsrHandlers[index].iIsr)(IsrHandlers[index].iPtr); |
|
282 } |
|
283 } |
|
284 ++index; |
|
285 pendingIrqs >>= 1; |
|
286 } |
|
287 } |
|
288 </codeblock><p>This example code illustrates a second level dispatcher |
|
289 bound directly to an interrupt source.</p><codeblock xml:space="preserve">void IrqDispatch() |
|
290 // MAIN IRQ DISPATCHER, FIRST-LEVEL INTERRUPT |
|
291 { |
|
292 TUint32 pendingIrqs = TheAssp::IrqPendingRegister(); |
|
293 |
|
294 TInt index = 0; |
|
295 while( pendingIrqs ) |
|
296 { |
|
297 if( pendingIrqs & 1 ) |
|
298 { |
|
299 (IsrHandlers[index].iIsr)(IsrHandlers[index].iPtr); |
|
300 } |
|
301 ++index; |
|
302 pendingIrqs >>= 1; |
|
303 } |
|
304 } |
|
305 void SecondLevelIrqDispatch( TAny* /* aParam */ ) |
|
306 { |
|
307 TUint32 pendingIrqs = TheAssp::SecondLevelIrqPendingRegister(); |
|
308 |
|
309 TInt index = EStartOfSecondLevelIntId; |
|
310 while( pendingIrqs ) |
|
311 { |
|
312 if( pendingIrqs & 1 ) |
|
313 { |
|
314 (IsrHandlers[index].iIsr)(IsrHandlers[index].iPtr); |
|
315 } |
|
316 ++index; |
|
317 pendingIrqs >>= 1; |
|
318 } |
|
319 } |
|
320 void InitialiseSecondLevelDispatch() |
|
321 // Bind and enable the second-level dispatcher |
|
322 { |
|
323 Interrupt::Bind(EMainIntChainIrq,SecondLevelIrqDispatch,NULL); |
|
324 Interrupt::Enable( EMainIntChainIrq ); |
|
325 } |
|
326 </codeblock><p>This example assumes an ISR table in which the second |
|
327 level interrupt ISRs begin at location 32 (<codeph>EStartOfSecondLevelIntId</codeph>). <codeph>EMainIntChainIrq</codeph> is the interrupt Id of the chained |
|
328 interrupt source to the main interrupt controller. The second level |
|
329 dispatcher is itself an ISR with an argument <codeph>TAny</codeph>* which is not needed in this example (possible uses are to distinguish |
|
330 between core and device specific ISR tables or to point to I/O addresses).</p></section> |
|
331 <section id="GUID-8953F8D5-8EA7-46B2-9030-A2932F06032F"><title>Multiple |
|
332 interrupt sources</title><p>In cases where multiple peripherals are |
|
333 connected to the same interrupt source, multiple sources may generate |
|
334 the same interrupt which will then require a different ISR depending |
|
335 on the specific source. However, EKA2 does not allow binding of multiple |
|
336 ISRs to the same interrupt. There are two strategies for solving this |
|
337 problem, both of which involve assigning the multiple ISRs not to |
|
338 the real interrupt but to pseudo-interrupt Ids. In one strategy the |
|
339 dispatcher examines the hardware to determine where the interrupt |
|
340 originated and calls the appropriate ISR. In the other strategy, the |
|
341 ISRs are written so that they examine their peripheral hardware and |
|
342 only run if it is actually signalling an interrupt: in this strategy |
|
343 the dispatcher calls all the ISRs bound to the real interrupt but |
|
344 only one of them runs.</p><p>There is no requirement to extend the |
|
345 implementation of <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-4E3CB472-3525-32F8-9BC4-8ECFEE931E7B"><apiname>Interrupt::Bind()</apiname></xref> and <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-CCC9A397-608C-3EAF-830F-A59800C2E8E5"><apiname>Interrupt::Unbind()</apiname></xref> where multiple interrupt sources are |
|
346 involved.</p><p>Multiple interrupt sources require you to extend the |
|
347 implementation of <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-BB169E6E-D8F9-3762-899D-6DBA4B29CF87"><apiname>Interrupt::Enable()</apiname></xref> and <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-2D14E023-E6ED-39BF-8B31-6FA510957A8A"><apiname>Interrupt::Disable()</apiname></xref> to enable and disable the true interrupt |
|
348 source.</p><p>The dispatch functions should be extended in the same |
|
349 way as with chained interrupts, using one of the two techniques described |
|
350 for that case.</p><p>The ISR table should be structured so that the |
|
351 interrupt Id identifies the hardware controller the interrupt is on. |
|
352 For instance the first 32 Ids might refer to the highest level controller, |
|
353 the next 32 to a second level controller and so on.</p></section> |
|
354 <section id="GUID-26850469-13E9-4A50-B6B0-946E317085AD"><title>Device |
|
355 specific interrupts</title><p>Interrupts generated by peripherals |
|
356 are sometimes routed to a GPIO pin and sometimes included in a variant |
|
357 layer. Where they are part of the variant layer, they must be listed |
|
358 in a separate ISR table which is part of the device implementation. |
|
359 However, we want device drivers to be able to use the Interrupt class |
|
360 functions on interrupts of either type. The solution is to write separate |
|
361 device specific functions derived from those of the core class. Core |
|
362 class functions are then written in such a way as to identify device |
|
363 specific interrupts and pass them on to the derived functions.</p><p>A recommended way of labelling interrupts as being device specific |
|
364 is to assign negative numbers as their Ids. The core functions can |
|
365 then identify negative Ids as belonging to device specific interrupts |
|
366 and pass them to the device specific derived functions. The device |
|
367 specific functions can convert them to positive numbers which serve |
|
368 as indexes to the device specific ISR table.</p><p>This example code |
|
369 illustrates device specific interrupt handling.</p><codeblock xml:space="preserve">EXPORT_C TInt Interrupt::Bind(TInt aId, TIsr aIsr, TAny* aPtr) |
|
370 { |
|
371 TInt r = KErrNone; |
|
372 if(aId < 0 ) |
|
373 { |
|
374 return MyAsic->VariantBind( aId, aIsr, aPtr ); // Device specific ID, call variant |
|
375 } |
|
376 else if (aId >= KInterruptSourceCount) |
|
377 { |
|
378 r = KErrArgument; // Illegal interrupt number |
|
379 } |
|
380 else |
|
381 { |
|
382 SInterruptHandler& h = IsrHandlers[aId]; |
|
383 TInt irq = NKern::DisableAllInterrupts(); |
|
384 if (h.iIsr != SpuriousHandler) |
|
385 { |
|
386 r = KErrInUse; // Already bound to an ISR |
|
387 } |
|
388 else |
|
389 { |
|
390 h.iPtr = aPtr; |
|
391 h.iIsr = anIsr; |
|
392 } |
|
393 NKern::RestoreInterrupts(irq); |
|
394 } |
|
395 return r; |
|
396 } |
|
397 |
|
398 SInterruptHandler VariantHandlers[KNumVariantInts]; |
|
399 EXPORT_C TInt TMyVariant::VariantBind(TInt aId, TIsr aIsr, TAny* aPtr) |
|
400 { |
|
401 TInt r = KErrNone; |
|
402 aId = (-aId)-1; // convert to positive number >=0 |
|
403 If (aId >= KInterruptSourceCount || aId < 0) |
|
404 { |
|
405 r = KErrArgument; // Illegal interrupt number |
|
406 } |
|
407 else |
|
408 { |
|
409 SInterruptHandler& h = VariantHandlers[aId]; |
|
410 TInt irq = NKern::DisableAllInterrupts(); |
|
411 if (h.iIsr != VariantSpuriousHandler) |
|
412 { |
|
413 r = KErrInUse; // Already bound to an ISR |
|
414 } |
|
415 else |
|
416 { |
|
417 h.iPtr = aPtr; |
|
418 h.iIsr = anIsr; |
|
419 } |
|
420 NKern::RestoreInterrupts(irq); |
|
421 } |
|
422 return r; |
|
423 }</codeblock><p>The example provides a version of <xref href="GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3.dita#GUID-E7A7083C-97B9-39B9-A147-4A6E314EE3A3/GUID-4E3CB472-3525-32F8-9BC4-8ECFEE931E7B"><apiname>Interrupt::Bind()</apiname></xref> which calls a variant layer function<codeph> VariantBind()</codeph> to process device specific interrupts (here assigned negative Ids) |
|
424 to ISRs held in the variant specific table <codeph>VariantHandlers</codeph>.</p></section> |
|
425 </conbody><related-links> |
|
426 <link href="GUID-654A788A-526A-4C3F-838C-05B09F0D5445.dita"><linktext>Interrupt |
|
427 Technology Guide</linktext></link> |
|
428 <link href="GUID-D0F5D40A-28D2-4A2E-9B40-180537E60F56.dita"><linktext>Interrupt |
|
429 Client Interface Guide</linktext></link> |
|
430 </related-links></concept> |