]> git.proxmox.com Git - mirror_edk2.git/blob - PcAtChipsetPkg/8259InterruptControllerDxe/8259.c
Fix the bug that necessary call of SetVectorBase() is missing in the SetMode() function.
[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
5 All rights reserved. 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 = 0x06b8;
42 UINT16 mProtectedModeEdgeLevel = 0x0000;
43 UINT16 mLegacyModeEdgeLevel = 0x0000;
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 Interrupt8259SetVectorBase (This, LEGACY_MODE_BASE_VECTOR_MASTER, LEGACY_MODE_BASE_VECTOR_SLAVE);
355 Interrupt8259WriteMask (mLegacyModeMask, mLegacyModeEdgeLevel);
356
357 return EFI_SUCCESS;
358 }
359
360 if (Mode == Efi8259ProtectedMode) {
361 //
362 // Save the legacy mode mask/trigger level
363 //
364 Interrupt8259ReadMask (&mLegacyModeMask, &mLegacyModeEdgeLevel);
365 //
366 // Always force Timer to be enabled after return from 16-bit code.
367 // This always insures that on next entry, timer is counting.
368 //
369 mLegacyModeMask &= 0xFFFE;
370
371 if (Mask != NULL) {
372 //
373 // Update the Mask for the new mode
374 //
375 mProtectedModeMask = *Mask;
376 }
377
378 if (EdgeLevel != NULL) {
379 //
380 // Update the Edge/Level triggered mask for the new mode
381 //
382 mProtectedModeEdgeLevel = *EdgeLevel;
383 }
384
385 mMode = Mode;
386
387 //
388 // Write new protected mode mask/trigger level
389 //
390 Interrupt8259SetVectorBase (This, PROTECTED_MODE_BASE_VECTOR_MASTER, PROTECTED_MODE_BASE_VECTOR_SLAVE);
391 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
392
393 return EFI_SUCCESS;
394 }
395
396 return EFI_INVALID_PARAMETER;
397 }
398
399 /**
400 Translates the IRQ into a vector.
401
402 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
403 @param[in] Irq IRQ0-IRQ15.
404 @param[out] Vector The vector that is assigned to the IRQ.
405
406 @retval EFI_SUCCESS The Vector that matches Irq was returned.
407 @retval EFI_INVALID_PARAMETER Irq is not valid.
408
409 **/
410 EFI_STATUS
411 EFIAPI
412 Interrupt8259GetVector (
413 IN EFI_LEGACY_8259_PROTOCOL *This,
414 IN EFI_8259_IRQ Irq,
415 OUT UINT8 *Vector
416 )
417 {
418 if (Irq < Efi8259Irq0 || Irq > Efi8259Irq15) {
419 return EFI_INVALID_PARAMETER;
420 }
421
422 if (Irq <= Efi8259Irq7) {
423 *Vector = (UINT8) (mMasterBase + Irq);
424 } else {
425 *Vector = (UINT8) (mSlaveBase + (Irq - Efi8259Irq8));
426 }
427
428 return EFI_SUCCESS;
429 }
430
431 /**
432 Enables the specified IRQ.
433
434 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
435 @param[in] Irq IRQ0-IRQ15.
436 @param[in] LevelTriggered 0 = Edge triggered; 1 = Level triggered.
437
438 @retval EFI_SUCCESS The Irq was enabled on the 8259 PIC.
439 @retval EFI_INVALID_PARAMETER The Irq is not valid.
440
441 **/
442 EFI_STATUS
443 EFIAPI
444 Interrupt8259EnableIrq (
445 IN EFI_LEGACY_8259_PROTOCOL *This,
446 IN EFI_8259_IRQ Irq,
447 IN BOOLEAN LevelTriggered
448 )
449 {
450 if (Irq < Efi8259Irq0 || Irq > Efi8259Irq15) {
451 return EFI_INVALID_PARAMETER;
452 }
453
454 mProtectedModeMask = (UINT16) (mProtectedModeMask & ~(1 << Irq));
455 if (LevelTriggered) {
456 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel | (1 << Irq));
457 } else {
458 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq));
459 }
460
461 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
462
463 return EFI_SUCCESS;
464 }
465
466 /**
467 Disables the specified IRQ.
468
469 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
470 @param[in] Irq IRQ0-IRQ15.
471
472 @retval EFI_SUCCESS The Irq was disabled on the 8259 PIC.
473 @retval EFI_INVALID_PARAMETER The Irq is not valid.
474
475 **/
476 EFI_STATUS
477 EFIAPI
478 Interrupt8259DisableIrq (
479 IN EFI_LEGACY_8259_PROTOCOL *This,
480 IN EFI_8259_IRQ Irq
481 )
482 {
483 if (Irq < Efi8259Irq0 || Irq > Efi8259Irq15) {
484 return EFI_INVALID_PARAMETER;
485 }
486
487 mProtectedModeMask = (UINT16) (mProtectedModeMask | (1 << Irq));
488
489 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq));
490
491 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
492
493 return EFI_SUCCESS;
494 }
495
496 /**
497 Reads the PCI configuration space to get the interrupt number that is assigned to the card.
498
499 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
500 @param[in] PciHandle PCI function for which to return the vector.
501 @param[out] Vector IRQ number that corresponds to the interrupt line.
502
503 @retval EFI_SUCCESS The interrupt line value was read successfully.
504
505 **/
506 EFI_STATUS
507 EFIAPI
508 Interrupt8259GetInterruptLine (
509 IN EFI_LEGACY_8259_PROTOCOL *This,
510 IN EFI_HANDLE PciHandle,
511 OUT UINT8 *Vector
512 )
513 {
514 EFI_PCI_IO_PROTOCOL *PciIo;
515 UINT8 InterruptLine;
516 EFI_STATUS Status;
517
518 Status = gBS->HandleProtocol (
519 PciHandle,
520 &gEfiPciIoProtocolGuid,
521 (VOID **) &PciIo
522 );
523 if (EFI_ERROR (Status)) {
524 return EFI_INVALID_PARAMETER;
525 }
526
527 PciIo->Pci.Read (
528 PciIo,
529 EfiPciIoWidthUint8,
530 PCI_INT_LINE_OFFSET,
531 1,
532 &InterruptLine
533 );
534 //
535 // Interrupt line is same location for standard PCI cards, standard
536 // bridge and CardBus bridge.
537 //
538 *Vector = InterruptLine;
539
540 return EFI_SUCCESS;
541 }
542
543 /**
544 Issues the End of Interrupt (EOI) commands to PICs.
545
546 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
547 @param[in] Irq The interrupt for which to issue the EOI command.
548
549 @retval EFI_SUCCESS The EOI command was issued.
550 @retval EFI_INVALID_PARAMETER The Irq is not valid.
551
552 **/
553 EFI_STATUS
554 EFIAPI
555 Interrupt8259EndOfInterrupt (
556 IN EFI_LEGACY_8259_PROTOCOL *This,
557 IN EFI_8259_IRQ Irq
558 )
559 {
560 if (Irq < Efi8259Irq0 || Irq > Efi8259Irq15) {
561 return EFI_INVALID_PARAMETER;
562 }
563
564 if (Irq >= Efi8259Irq8) {
565 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI);
566 }
567
568 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI);
569
570 return EFI_SUCCESS;
571 }
572
573 /**
574 Driver Entry point.
575
576 @param[in] ImageHandle ImageHandle of the loaded driver.
577 @param[in] SystemTable Pointer to the EFI System Table.
578
579 @retval EFI_SUCCESS One or more of the drivers returned a success code.
580 @retval !EFI_SUCCESS Error installing Legacy 8259 Protocol.
581
582 **/
583 EFI_STATUS
584 EFIAPI
585 Install8259 (
586 IN EFI_HANDLE ImageHandle,
587 IN EFI_SYSTEM_TABLE *SystemTable
588 )
589 {
590 EFI_STATUS Status;
591 EFI_8259_IRQ Irq;
592
593 //
594 // Clear all pending interrupt
595 //
596 for (Irq = Efi8259Irq0; Irq <= Efi8259Irq15; Irq++) {
597 Interrupt8259EndOfInterrupt (&m8259, Irq);
598 }
599
600 //
601 // Set the 8259 Master base to 0x68 and the 8259 Slave base to 0x70
602 //
603 Status = Interrupt8259SetVectorBase (&m8259, PROTECTED_MODE_BASE_VECTOR_MASTER, PROTECTED_MODE_BASE_VECTOR_SLAVE);
604
605 //
606 // Set all 8259 interrupts to edge triggered and disabled
607 //
608 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
609
610 //
611 // Install 8259 Protocol onto a new handle
612 //
613 Status = gBS->InstallProtocolInterface (
614 &m8259Handle,
615 &gEfiLegacy8259ProtocolGuid,
616 EFI_NATIVE_INTERFACE,
617 &m8259
618 );
619 return Status;
620 }
621