7b9067f89ad7d3603705d57955279f9f7cea7d03
[mirror_edk2.git] / PcAtChipsetPkg / 8259InterruptControllerDxe / 8259.c
1 /**@file
2 This contains the installation function for the driver.
3
4 Copyright (c) 2005 - 2009, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "8259.h"
16
17 //
18 // Global for the Legacy 8259 Protocol that is produced by this driver
19 //
20 EFI_LEGACY_8259_PROTOCOL m8259 = {
21 Interrupt8259SetVectorBase,
22 Interrupt8259GetMask,
23 Interrupt8259SetMask,
24 Interrupt8259SetMode,
25 Interrupt8259GetVector,
26 Interrupt8259EnableIrq,
27 Interrupt8259DisableIrq,
28 Interrupt8259GetInterruptLine,
29 Interrupt8259EndOfInterrupt
30 };
31
32 //
33 // Global for the handle that the Legacy 8259 Protocol is installed
34 //
35 EFI_HANDLE m8259Handle = NULL;
36
37 UINT8 mMasterBase = 0xff;
38 UINT8 mSlaveBase = 0xff;
39 EFI_8259_MODE mMode = Efi8259ProtectedMode;
40 UINT16 mProtectedModeMask = 0xffff;
41 UINT16 mLegacyModeMask = FixedPcdGet16(Pcd8259LegacyModeMask);
42 UINT16 mProtectedModeEdgeLevel = 0x0000;
43 UINT16 mLegacyModeEdgeLevel = FixedPcdGet16(Pcd8259LegacyModeEdgeLevel);
44
45 //
46 // Worker Functions
47 //
48
49 /**
50 Write to mask and edge/level triggered registers of master and slave PICs.
51
52 @param[in] Mask low byte for master PIC mask register,
53 high byte for slave PIC mask register.
54 @param[in] EdgeLevel low byte for master PIC edge/level triggered register,
55 high byte for slave PIC edge/level triggered register.
56
57 **/
58 VOID
59 Interrupt8259WriteMask (
60 IN UINT16 Mask,
61 IN UINT16 EdgeLevel
62 )
63 {
64 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, (UINT8) Mask);
65 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, (UINT8) (Mask >> 8));
66 IoWrite8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_MASTER, (UINT8) EdgeLevel);
67 IoWrite8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_SLAVE, (UINT8) (EdgeLevel >> 8));
68 }
69
70 /**
71 Read from mask and edge/level triggered registers of master and slave PICs.
72
73 @param[out] Mask low byte for master PIC mask register,
74 high byte for slave PIC mask register.
75 @param[out] EdgeLevel low byte for master PIC edge/level triggered register,
76 high byte for slave PIC edge/level triggered register.
77
78 **/
79 VOID
80 Interrupt8259ReadMask (
81 OUT UINT16 *Mask,
82 OUT UINT16 *EdgeLevel
83 )
84 {
85 UINT16 MasterValue;
86 UINT16 SlaveValue;
87
88 if (Mask != NULL) {
89 MasterValue = IoRead8 (LEGACY_8259_MASK_REGISTER_MASTER);
90 SlaveValue = IoRead8 (LEGACY_8259_MASK_REGISTER_SLAVE);
91
92 *Mask = (UINT16) (MasterValue | (SlaveValue << 8));
93 }
94
95 if (EdgeLevel != NULL) {
96 MasterValue = IoRead8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_MASTER);
97 SlaveValue = IoRead8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_SLAVE);
98
99 *EdgeLevel = (UINT16) (MasterValue | (SlaveValue << 8));
100 }
101 }
102
103 //
104 // Legacy 8259 Protocol Interface Functions
105 //
106
107 /**
108 Sets the base address for the 8259 master and slave PICs.
109
110 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
111 @param[in] MasterBase Interrupt vectors for IRQ0-IRQ7.
112 @param[in] SlaveBase Interrupt vectors for IRQ8-IRQ15.
113
114 @retval EFI_SUCCESS The 8259 PIC was programmed successfully.
115 @retval EFI_DEVICE_ERROR There was an error while writing to the 8259 PIC.
116
117 **/
118 EFI_STATUS
119 EFIAPI
120 Interrupt8259SetVectorBase (
121 IN EFI_LEGACY_8259_PROTOCOL *This,
122 IN UINT8 MasterBase,
123 IN UINT8 SlaveBase
124 )
125 {
126 UINT8 Mask;
127
128 //
129 // Set vector base for slave PIC
130 //
131 if (SlaveBase != mSlaveBase) {
132 mSlaveBase = SlaveBase;
133
134 //
135 // Initialization sequence is needed for setting vector base.
136 //
137
138 //
139 // Preserve interrtup mask register before initialization sequence
140 // because it will be cleared during intialization
141 //
142 Mask = IoRead8 (LEGACY_8259_MASK_REGISTER_SLAVE);
143
144 //
145 // ICW1: cascade mode, ICW4 write required
146 //
147 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, 0x11);
148
149 //
150 // ICW2: new vector base (must be multiple of 8)
151 //
152 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, mSlaveBase);
153
154 //
155 // ICW3: slave indentification code must be 2
156 //
157 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0x02);
158
159 //
160 // ICW4: fully nested mode, non-buffered mode, normal EOI, IA processor
161 //
162 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0x01);
163
164 //
165 // Restore interrupt mask register
166 //
167 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, Mask);
168 }
169
170 //
171 // Set vector base for master PIC
172 //
173 if (MasterBase != mMasterBase) {
174 mMasterBase = MasterBase;
175
176 //
177 // Initialization sequence is needed for setting vector base.
178 //
179
180 //
181 // Preserve interrtup mask register before initialization sequence
182 // because it will be cleared during intialization
183 //
184 Mask = IoRead8 (LEGACY_8259_MASK_REGISTER_MASTER);
185
186 //
187 // ICW1: cascade mode, ICW4 write required
188 //
189 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, 0x11);
190
191 //
192 // ICW2: new vector base (must be multiple of 8)
193 //
194 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, mMasterBase);
195
196 //
197 // ICW3: slave PIC is cascaded on IRQ2
198 //
199 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0x04);
200
201 //
202 // ICW4: fully nested mode, non-buffered mode, normal EOI, IA processor
203 //
204 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0x01);
205
206 //
207 // Restore interrupt mask register
208 //
209 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, Mask);
210 }
211
212 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI);
213 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI);
214
215 return EFI_SUCCESS;
216 }
217
218 /**
219 Gets the current 16-bit real mode and 32-bit protected-mode IRQ masks.
220
221 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
222 @param[out] LegacyMask 16-bit mode interrupt mask for IRQ0-IRQ15.
223 @param[out] LegacyEdgeLevel 16-bit mode edge/level mask for IRQ-IRQ15.
224 @param[out] ProtectedMask 32-bit mode interrupt mask for IRQ0-IRQ15.
225 @param[out] ProtectedEdgeLevel 32-bit mode edge/level mask for IRQ0-IRQ15.
226
227 @retval EFI_SUCCESS The 8259 PIC was programmed successfully.
228 @retval EFI_DEVICE_ERROR There was an error while reading the 8259 PIC.
229
230 **/
231 EFI_STATUS
232 EFIAPI
233 Interrupt8259GetMask (
234 IN EFI_LEGACY_8259_PROTOCOL *This,
235 OUT UINT16 *LegacyMask, OPTIONAL
236 OUT UINT16 *LegacyEdgeLevel, OPTIONAL
237 OUT UINT16 *ProtectedMask, OPTIONAL
238 OUT UINT16 *ProtectedEdgeLevel OPTIONAL
239 )
240 {
241 if (LegacyMask != NULL) {
242 *LegacyMask = mLegacyModeMask;
243 }
244
245 if (LegacyEdgeLevel != NULL) {
246 *LegacyEdgeLevel = mLegacyModeEdgeLevel;
247 }
248
249 if (ProtectedMask != NULL) {
250 *ProtectedMask = mProtectedModeMask;
251 }
252
253 if (ProtectedEdgeLevel != NULL) {
254 *ProtectedEdgeLevel = mProtectedModeEdgeLevel;
255 }
256
257 return EFI_SUCCESS;
258 }
259
260 /**
261 Sets the current 16-bit real mode and 32-bit protected-mode IRQ masks.
262
263 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
264 @param[in] LegacyMask 16-bit mode interrupt mask for IRQ0-IRQ15.
265 @param[in] LegacyEdgeLevel 16-bit mode edge/level mask for IRQ-IRQ15.
266 @param[in] ProtectedMask 32-bit mode interrupt mask for IRQ0-IRQ15.
267 @param[in] ProtectedEdgeLevel 32-bit mode edge/level mask for IRQ0-IRQ15.
268
269 @retval EFI_SUCCESS The 8259 PIC was programmed successfully.
270 @retval EFI_DEVICE_ERROR There was an error while writing the 8259 PIC.
271
272 **/
273 EFI_STATUS
274 EFIAPI
275 Interrupt8259SetMask (
276 IN EFI_LEGACY_8259_PROTOCOL *This,
277 IN UINT16 *LegacyMask, OPTIONAL
278 IN UINT16 *LegacyEdgeLevel, OPTIONAL
279 IN UINT16 *ProtectedMask, OPTIONAL
280 IN UINT16 *ProtectedEdgeLevel OPTIONAL
281 )
282 {
283 if (LegacyMask != NULL) {
284 mLegacyModeMask = *LegacyMask;
285 }
286
287 if (LegacyEdgeLevel != NULL) {
288 mLegacyModeEdgeLevel = *LegacyEdgeLevel;
289 }
290
291 if (ProtectedMask != NULL) {
292 mProtectedModeMask = *ProtectedMask;
293 }
294
295 if (ProtectedEdgeLevel != NULL) {
296 mProtectedModeEdgeLevel = *ProtectedEdgeLevel;
297 }
298
299 return EFI_SUCCESS;
300 }
301
302 /**
303 Sets the mode of the PICs.
304
305 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
306 @param[in] Mode 16-bit real or 32-bit protected mode.
307 @param[in] Mask The value with which to set the interrupt mask.
308 @param[in] EdgeLevel The value with which to set the edge/level mask.
309
310 @retval EFI_SUCCESS The mode was set successfully.
311 @retval EFI_INVALID_PARAMETER The mode was not set.
312
313 **/
314 EFI_STATUS
315 EFIAPI
316 Interrupt8259SetMode (
317 IN EFI_LEGACY_8259_PROTOCOL *This,
318 IN EFI_8259_MODE Mode,
319 IN UINT16 *Mask, OPTIONAL
320 IN UINT16 *EdgeLevel OPTIONAL
321 )
322 {
323 if (Mode == mMode) {
324 return EFI_SUCCESS;
325 }
326
327 if (Mode == Efi8259LegacyMode) {
328 //
329 // In Efi8259ProtectedMode, mask and edge/level trigger registers should
330 // be changed through this protocol, so we can track them in the
331 // corresponding module variables.
332 //
333 Interrupt8259ReadMask (&mProtectedModeMask, &mProtectedModeEdgeLevel);
334
335 if (Mask != NULL) {
336 //
337 // Update the Mask for the new mode
338 //
339 mLegacyModeMask = *Mask;
340 }
341
342 if (EdgeLevel != NULL) {
343 //
344 // Update the Edge/Level triggered mask for the new mode
345 //
346 mLegacyModeEdgeLevel = *EdgeLevel;
347 }
348
349 mMode = Mode;
350
351 //
352 // Write new legacy mode mask/trigger level
353 //
354 Interrupt8259WriteMask (mLegacyModeMask, mLegacyModeEdgeLevel);
355
356 return EFI_SUCCESS;
357 }
358
359 if (Mode == Efi8259ProtectedMode) {
360 //
361 // Save the legacy mode mask/trigger level
362 //
363 Interrupt8259ReadMask (&mLegacyModeMask, &mLegacyModeEdgeLevel);
364 //
365 // Always force Timer to be enabled after return from 16-bit code.
366 // This always insures that on next entry, timer is counting.
367 //
368 mLegacyModeMask &= 0xFFFE;
369
370 if (Mask != NULL) {
371 //
372 // Update the Mask for the new mode
373 //
374 mProtectedModeMask = *Mask;
375 }
376
377 if (EdgeLevel != NULL) {
378 //
379 // Update the Edge/Level triggered mask for the new mode
380 //
381 mProtectedModeEdgeLevel = *EdgeLevel;
382 }
383
384 mMode = Mode;
385
386 //
387 // Write new protected mode mask/trigger level
388 //
389 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
390
391 return EFI_SUCCESS;
392 }
393
394 return EFI_INVALID_PARAMETER;
395 }
396
397 /**
398 Translates the IRQ into a vector.
399
400 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
401 @param[in] Irq IRQ0-IRQ15.
402 @param[out] Vector The vector that is assigned to the IRQ.
403
404 @retval EFI_SUCCESS The Vector that matches Irq was returned.
405 @retval EFI_INVALID_PARAMETER Irq is not valid.
406
407 **/
408 EFI_STATUS
409 EFIAPI
410 Interrupt8259GetVector (
411 IN EFI_LEGACY_8259_PROTOCOL *This,
412 IN EFI_8259_IRQ Irq,
413 OUT UINT8 *Vector
414 )
415 {
416 if (Irq < Efi8259Irq0 || Irq > Efi8259Irq15) {
417 return EFI_INVALID_PARAMETER;
418 }
419
420 if (Irq <= Efi8259Irq7) {
421 *Vector = (UINT8) (mMasterBase + Irq);
422 } else {
423 *Vector = (UINT8) (mSlaveBase + (Irq - Efi8259Irq8));
424 }
425
426 return EFI_SUCCESS;
427 }
428
429 /**
430 Enables the specified IRQ.
431
432 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
433 @param[in] Irq IRQ0-IRQ15.
434 @param[in] LevelTriggered 0 = Edge triggered; 1 = Level triggered.
435
436 @retval EFI_SUCCESS The Irq was enabled on the 8259 PIC.
437 @retval EFI_INVALID_PARAMETER The Irq is not valid.
438
439 **/
440 EFI_STATUS
441 EFIAPI
442 Interrupt8259EnableIrq (
443 IN EFI_LEGACY_8259_PROTOCOL *This,
444 IN EFI_8259_IRQ Irq,
445 IN BOOLEAN LevelTriggered
446 )
447 {
448 if (Irq < Efi8259Irq0 || Irq > Efi8259Irq15) {
449 return EFI_INVALID_PARAMETER;
450 }
451
452 mProtectedModeMask = (UINT16) (mProtectedModeMask & ~(1 << Irq));
453 if (LevelTriggered) {
454 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel | (1 << Irq));
455 } else {
456 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq));
457 }
458
459 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
460
461 return EFI_SUCCESS;
462 }
463
464 /**
465 Disables the specified IRQ.
466
467 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
468 @param[in] Irq IRQ0-IRQ15.
469
470 @retval EFI_SUCCESS The Irq was disabled on the 8259 PIC.
471 @retval EFI_INVALID_PARAMETER The Irq is not valid.
472
473 **/
474 EFI_STATUS
475 EFIAPI
476 Interrupt8259DisableIrq (
477 IN EFI_LEGACY_8259_PROTOCOL *This,
478 IN EFI_8259_IRQ Irq
479 )
480 {
481 if (Irq < Efi8259Irq0 || Irq > Efi8259Irq15) {
482 return EFI_INVALID_PARAMETER;
483 }
484
485 mProtectedModeMask = (UINT16) (mProtectedModeMask | (1 << Irq));
486
487 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq));
488
489 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
490
491 return EFI_SUCCESS;
492 }
493
494 /**
495 Reads the PCI configuration space to get the interrupt number that is assigned to the card.
496
497 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
498 @param[in] PciHandle PCI function for which to return the vector.
499 @param[out] Vector IRQ number that corresponds to the interrupt line.
500
501 @retval EFI_SUCCESS The interrupt line value was read successfully.
502
503 **/
504 EFI_STATUS
505 EFIAPI
506 Interrupt8259GetInterruptLine (
507 IN EFI_LEGACY_8259_PROTOCOL *This,
508 IN EFI_HANDLE PciHandle,
509 OUT UINT8 *Vector
510 )
511 {
512 EFI_PCI_IO_PROTOCOL *PciIo;
513 UINT8 InterruptLine;
514 EFI_STATUS Status;
515
516 Status = gBS->HandleProtocol (
517 PciHandle,
518 &gEfiPciIoProtocolGuid,
519 (VOID **) &PciIo
520 );
521 if (EFI_ERROR (Status)) {
522 return EFI_INVALID_PARAMETER;
523 }
524
525 PciIo->Pci.Read (
526 PciIo,
527 EfiPciIoWidthUint8,
528 PCI_INT_LINE_OFFSET,
529 1,
530 &InterruptLine
531 );
532 //
533 // Interrupt line is same location for standard PCI cards, standard
534 // bridge and CardBus bridge.
535 //
536 *Vector = InterruptLine;
537
538 return EFI_SUCCESS;
539 }
540
541 /**
542 Issues the End of Interrupt (EOI) commands to PICs.
543
544 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
545 @param[in] Irq The interrupt for which to issue the EOI command.
546
547 @retval EFI_SUCCESS The EOI command was issued.
548 @retval EFI_INVALID_PARAMETER The Irq is not valid.
549
550 **/
551 EFI_STATUS
552 EFIAPI
553 Interrupt8259EndOfInterrupt (
554 IN EFI_LEGACY_8259_PROTOCOL *This,
555 IN EFI_8259_IRQ Irq
556 )
557 {
558 if (Irq < Efi8259Irq0 || Irq > Efi8259Irq15) {
559 return EFI_INVALID_PARAMETER;
560 }
561
562 if (Irq >= Efi8259Irq8) {
563 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI);
564 }
565
566 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI);
567
568 return EFI_SUCCESS;
569 }
570
571 /**
572 Driver Entry point.
573
574 @param[in] ImageHandle ImageHandle of the loaded driver.
575 @param[in] SystemTable Pointer to the EFI System Table.
576
577 @retval EFI_SUCCESS One or more of the drivers returned a success code.
578 @retval !EFI_SUCCESS Error installing Legacy 8259 Protocol.
579
580 **/
581 EFI_STATUS
582 EFIAPI
583 Install8259 (
584 IN EFI_HANDLE ImageHandle,
585 IN EFI_SYSTEM_TABLE *SystemTable
586 )
587 {
588 EFI_STATUS Status;
589 EFI_8259_IRQ Irq;
590
591 //
592 // Clear all pending interrupt
593 //
594 for (Irq = Efi8259Irq0; Irq <= Efi8259Irq15; Irq++) {
595 Interrupt8259EndOfInterrupt (&m8259, Irq);
596 }
597
598 //
599 // Set the 8259 Master base to 0x68 and the 8259 Slave base to 0x70
600 //
601 Status = Interrupt8259SetVectorBase (&m8259, PROTECTED_MODE_BASE_VECTOR_MASTER, PROTECTED_MODE_BASE_VECTOR_SLAVE);
602
603 //
604 // Set all 8259 interrupts to edge triggered and disabled
605 //
606 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
607
608 //
609 // Install 8259 Protocol onto a new handle
610 //
611 Status = gBS->InstallProtocolInterface (
612 &m8259Handle,
613 &gEfiLegacy8259ProtocolGuid,
614 EFI_NATIVE_INTERFACE,
615 &m8259
616 );
617 return Status;
618 }