PcAtChipsetPkg: Clean up source files
[mirror_edk2.git] / PcAtChipsetPkg / 8259InterruptControllerDxe / 8259.c
1 /** @file
2 This contains the installation function for the driver.
3
4 Copyright (c) 2005 - 2018, 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 mInterrupt8259 = {
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;
42 UINT16 mProtectedModeEdgeLevel = 0x0000;
43 UINT16 mLegacyModeEdgeLevel;
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 EFI_TPL OriginalTpl;
128
129 OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
130 //
131 // Set vector base for slave PIC
132 //
133 if (SlaveBase != mSlaveBase) {
134 mSlaveBase = SlaveBase;
135
136 //
137 // Initialization sequence is needed for setting vector base.
138 //
139
140 //
141 // Preserve interrtup mask register before initialization sequence
142 // because it will be cleared during initialization
143 //
144 Mask = IoRead8 (LEGACY_8259_MASK_REGISTER_SLAVE);
145
146 //
147 // ICW1: cascade mode, ICW4 write required
148 //
149 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, 0x11);
150
151 //
152 // ICW2: new vector base (must be multiple of 8)
153 //
154 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, mSlaveBase);
155
156 //
157 // ICW3: slave indentification code must be 2
158 //
159 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0x02);
160
161 //
162 // ICW4: fully nested mode, non-buffered mode, normal EOI, IA processor
163 //
164 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0x01);
165
166 //
167 // Restore interrupt mask register
168 //
169 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, Mask);
170 }
171
172 //
173 // Set vector base for master PIC
174 //
175 if (MasterBase != mMasterBase) {
176 mMasterBase = MasterBase;
177
178 //
179 // Initialization sequence is needed for setting vector base.
180 //
181
182 //
183 // Preserve interrtup mask register before initialization sequence
184 // because it will be cleared during initialization
185 //
186 Mask = IoRead8 (LEGACY_8259_MASK_REGISTER_MASTER);
187
188 //
189 // ICW1: cascade mode, ICW4 write required
190 //
191 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, 0x11);
192
193 //
194 // ICW2: new vector base (must be multiple of 8)
195 //
196 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, mMasterBase);
197
198 //
199 // ICW3: slave PIC is cascaded on IRQ2
200 //
201 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0x04);
202
203 //
204 // ICW4: fully nested mode, non-buffered mode, normal EOI, IA processor
205 //
206 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0x01);
207
208 //
209 // Restore interrupt mask register
210 //
211 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, Mask);
212 }
213
214 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI);
215 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI);
216
217 gBS->RestoreTPL (OriginalTpl);
218
219 return EFI_SUCCESS;
220 }
221
222 /**
223 Gets the current 16-bit real mode and 32-bit protected-mode IRQ masks.
224
225 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
226 @param[out] LegacyMask 16-bit mode interrupt mask for IRQ0-IRQ15.
227 @param[out] LegacyEdgeLevel 16-bit mode edge/level mask for IRQ-IRQ15.
228 @param[out] ProtectedMask 32-bit mode interrupt mask for IRQ0-IRQ15.
229 @param[out] ProtectedEdgeLevel 32-bit mode edge/level mask for IRQ0-IRQ15.
230
231 @retval EFI_SUCCESS The 8259 PIC was programmed successfully.
232 @retval EFI_DEVICE_ERROR There was an error while reading the 8259 PIC.
233
234 **/
235 EFI_STATUS
236 EFIAPI
237 Interrupt8259GetMask (
238 IN EFI_LEGACY_8259_PROTOCOL *This,
239 OUT UINT16 *LegacyMask, OPTIONAL
240 OUT UINT16 *LegacyEdgeLevel, OPTIONAL
241 OUT UINT16 *ProtectedMask, OPTIONAL
242 OUT UINT16 *ProtectedEdgeLevel OPTIONAL
243 )
244 {
245 if (LegacyMask != NULL) {
246 *LegacyMask = mLegacyModeMask;
247 }
248
249 if (LegacyEdgeLevel != NULL) {
250 *LegacyEdgeLevel = mLegacyModeEdgeLevel;
251 }
252
253 if (ProtectedMask != NULL) {
254 *ProtectedMask = mProtectedModeMask;
255 }
256
257 if (ProtectedEdgeLevel != NULL) {
258 *ProtectedEdgeLevel = mProtectedModeEdgeLevel;
259 }
260
261 return EFI_SUCCESS;
262 }
263
264 /**
265 Sets the current 16-bit real mode and 32-bit protected-mode IRQ masks.
266
267 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
268 @param[in] LegacyMask 16-bit mode interrupt mask for IRQ0-IRQ15.
269 @param[in] LegacyEdgeLevel 16-bit mode edge/level mask for IRQ-IRQ15.
270 @param[in] ProtectedMask 32-bit mode interrupt mask for IRQ0-IRQ15.
271 @param[in] ProtectedEdgeLevel 32-bit mode edge/level mask for IRQ0-IRQ15.
272
273 @retval EFI_SUCCESS The 8259 PIC was programmed successfully.
274 @retval EFI_DEVICE_ERROR There was an error while writing the 8259 PIC.
275
276 **/
277 EFI_STATUS
278 EFIAPI
279 Interrupt8259SetMask (
280 IN EFI_LEGACY_8259_PROTOCOL *This,
281 IN UINT16 *LegacyMask, OPTIONAL
282 IN UINT16 *LegacyEdgeLevel, OPTIONAL
283 IN UINT16 *ProtectedMask, OPTIONAL
284 IN UINT16 *ProtectedEdgeLevel OPTIONAL
285 )
286 {
287 if (LegacyMask != NULL) {
288 mLegacyModeMask = *LegacyMask;
289 }
290
291 if (LegacyEdgeLevel != NULL) {
292 mLegacyModeEdgeLevel = *LegacyEdgeLevel;
293 }
294
295 if (ProtectedMask != NULL) {
296 mProtectedModeMask = *ProtectedMask;
297 }
298
299 if (ProtectedEdgeLevel != NULL) {
300 mProtectedModeEdgeLevel = *ProtectedEdgeLevel;
301 }
302
303 return EFI_SUCCESS;
304 }
305
306 /**
307 Sets the mode of the PICs.
308
309 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
310 @param[in] Mode 16-bit real or 32-bit protected mode.
311 @param[in] Mask The value with which to set the interrupt mask.
312 @param[in] EdgeLevel The value with which to set the edge/level mask.
313
314 @retval EFI_SUCCESS The mode was set successfully.
315 @retval EFI_INVALID_PARAMETER The mode was not set.
316
317 **/
318 EFI_STATUS
319 EFIAPI
320 Interrupt8259SetMode (
321 IN EFI_LEGACY_8259_PROTOCOL *This,
322 IN EFI_8259_MODE Mode,
323 IN UINT16 *Mask, OPTIONAL
324 IN UINT16 *EdgeLevel OPTIONAL
325 )
326 {
327 if (Mode == mMode) {
328 return EFI_SUCCESS;
329 }
330
331 if (Mode == Efi8259LegacyMode) {
332 //
333 // In Efi8259ProtectedMode, mask and edge/level trigger registers should
334 // be changed through this protocol, so we can track them in the
335 // corresponding module variables.
336 //
337 Interrupt8259ReadMask (&mProtectedModeMask, &mProtectedModeEdgeLevel);
338
339 if (Mask != NULL) {
340 //
341 // Update the Mask for the new mode
342 //
343 mLegacyModeMask = *Mask;
344 }
345
346 if (EdgeLevel != NULL) {
347 //
348 // Update the Edge/Level triggered mask for the new mode
349 //
350 mLegacyModeEdgeLevel = *EdgeLevel;
351 }
352
353 mMode = Mode;
354
355 //
356 // Write new legacy mode mask/trigger level
357 //
358 Interrupt8259WriteMask (mLegacyModeMask, mLegacyModeEdgeLevel);
359
360 return EFI_SUCCESS;
361 }
362
363 if (Mode == Efi8259ProtectedMode) {
364 //
365 // Save the legacy mode mask/trigger level
366 //
367 Interrupt8259ReadMask (&mLegacyModeMask, &mLegacyModeEdgeLevel);
368 //
369 // Always force Timer to be enabled after return from 16-bit code.
370 // This always insures that on next entry, timer is counting.
371 //
372 mLegacyModeMask &= 0xFFFE;
373
374 if (Mask != NULL) {
375 //
376 // Update the Mask for the new mode
377 //
378 mProtectedModeMask = *Mask;
379 }
380
381 if (EdgeLevel != NULL) {
382 //
383 // Update the Edge/Level triggered mask for the new mode
384 //
385 mProtectedModeEdgeLevel = *EdgeLevel;
386 }
387
388 mMode = Mode;
389
390 //
391 // Write new protected mode mask/trigger level
392 //
393 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
394
395 return EFI_SUCCESS;
396 }
397
398 return EFI_INVALID_PARAMETER;
399 }
400
401 /**
402 Translates the IRQ into a vector.
403
404 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
405 @param[in] Irq IRQ0-IRQ15.
406 @param[out] Vector The vector that is assigned to the IRQ.
407
408 @retval EFI_SUCCESS The Vector that matches Irq was returned.
409 @retval EFI_INVALID_PARAMETER Irq is not valid.
410
411 **/
412 EFI_STATUS
413 EFIAPI
414 Interrupt8259GetVector (
415 IN EFI_LEGACY_8259_PROTOCOL *This,
416 IN EFI_8259_IRQ Irq,
417 OUT UINT8 *Vector
418 )
419 {
420 if ((UINT32)Irq > Efi8259Irq15) {
421 return EFI_INVALID_PARAMETER;
422 }
423
424 if (Irq <= Efi8259Irq7) {
425 *Vector = (UINT8) (mMasterBase + Irq);
426 } else {
427 *Vector = (UINT8) (mSlaveBase + (Irq - Efi8259Irq8));
428 }
429
430 return EFI_SUCCESS;
431 }
432
433 /**
434 Enables the specified IRQ.
435
436 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
437 @param[in] Irq IRQ0-IRQ15.
438 @param[in] LevelTriggered 0 = Edge triggered; 1 = Level triggered.
439
440 @retval EFI_SUCCESS The Irq was enabled on the 8259 PIC.
441 @retval EFI_INVALID_PARAMETER The Irq is not valid.
442
443 **/
444 EFI_STATUS
445 EFIAPI
446 Interrupt8259EnableIrq (
447 IN EFI_LEGACY_8259_PROTOCOL *This,
448 IN EFI_8259_IRQ Irq,
449 IN BOOLEAN LevelTriggered
450 )
451 {
452 if ((UINT32)Irq > Efi8259Irq15) {
453 return EFI_INVALID_PARAMETER;
454 }
455
456 mProtectedModeMask = (UINT16) (mProtectedModeMask & ~(1 << Irq));
457 if (LevelTriggered) {
458 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel | (1 << Irq));
459 } else {
460 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq));
461 }
462
463 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
464
465 return EFI_SUCCESS;
466 }
467
468 /**
469 Disables the specified IRQ.
470
471 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
472 @param[in] Irq IRQ0-IRQ15.
473
474 @retval EFI_SUCCESS The Irq was disabled on the 8259 PIC.
475 @retval EFI_INVALID_PARAMETER The Irq is not valid.
476
477 **/
478 EFI_STATUS
479 EFIAPI
480 Interrupt8259DisableIrq (
481 IN EFI_LEGACY_8259_PROTOCOL *This,
482 IN EFI_8259_IRQ Irq
483 )
484 {
485 if ((UINT32)Irq > Efi8259Irq15) {
486 return EFI_INVALID_PARAMETER;
487 }
488
489 mProtectedModeMask = (UINT16) (mProtectedModeMask | (1 << Irq));
490
491 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq));
492
493 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
494
495 return EFI_SUCCESS;
496 }
497
498 /**
499 Reads the PCI configuration space to get the interrupt number that is assigned to the card.
500
501 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
502 @param[in] PciHandle PCI function for which to return the vector.
503 @param[out] Vector IRQ number that corresponds to the interrupt line.
504
505 @retval EFI_SUCCESS The interrupt line value was read successfully.
506
507 **/
508 EFI_STATUS
509 EFIAPI
510 Interrupt8259GetInterruptLine (
511 IN EFI_LEGACY_8259_PROTOCOL *This,
512 IN EFI_HANDLE PciHandle,
513 OUT UINT8 *Vector
514 )
515 {
516 EFI_PCI_IO_PROTOCOL *PciIo;
517 UINT8 InterruptLine;
518 EFI_STATUS Status;
519
520 Status = gBS->HandleProtocol (
521 PciHandle,
522 &gEfiPciIoProtocolGuid,
523 (VOID **) &PciIo
524 );
525 if (EFI_ERROR (Status)) {
526 return EFI_INVALID_PARAMETER;
527 }
528
529 PciIo->Pci.Read (
530 PciIo,
531 EfiPciIoWidthUint8,
532 PCI_INT_LINE_OFFSET,
533 1,
534 &InterruptLine
535 );
536 //
537 // Interrupt line is same location for standard PCI cards, standard
538 // bridge and CardBus bridge.
539 //
540 *Vector = InterruptLine;
541
542 return EFI_SUCCESS;
543 }
544
545 /**
546 Issues the End of Interrupt (EOI) commands to PICs.
547
548 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance.
549 @param[in] Irq The interrupt for which to issue the EOI command.
550
551 @retval EFI_SUCCESS The EOI command was issued.
552 @retval EFI_INVALID_PARAMETER The Irq is not valid.
553
554 **/
555 EFI_STATUS
556 EFIAPI
557 Interrupt8259EndOfInterrupt (
558 IN EFI_LEGACY_8259_PROTOCOL *This,
559 IN EFI_8259_IRQ Irq
560 )
561 {
562 if ((UINT32)Irq > Efi8259Irq15) {
563 return EFI_INVALID_PARAMETER;
564 }
565
566 if (Irq >= Efi8259Irq8) {
567 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI);
568 }
569
570 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI);
571
572 return EFI_SUCCESS;
573 }
574
575 /**
576 Driver Entry point.
577
578 @param[in] ImageHandle ImageHandle of the loaded driver.
579 @param[in] SystemTable Pointer to the EFI System Table.
580
581 @retval EFI_SUCCESS One or more of the drivers returned a success code.
582 @retval !EFI_SUCCESS Error installing Legacy 8259 Protocol.
583
584 **/
585 EFI_STATUS
586 EFIAPI
587 Install8259 (
588 IN EFI_HANDLE ImageHandle,
589 IN EFI_SYSTEM_TABLE *SystemTable
590 )
591 {
592 EFI_STATUS Status;
593 EFI_8259_IRQ Irq;
594
595 //
596 // Initialze mask values from PCDs
597 //
598 mLegacyModeMask = PcdGet16 (Pcd8259LegacyModeMask);
599 mLegacyModeEdgeLevel = PcdGet16 (Pcd8259LegacyModeEdgeLevel);
600
601 //
602 // Clear all pending interrupt
603 //
604 for (Irq = Efi8259Irq0; Irq <= Efi8259Irq15; Irq++) {
605 Interrupt8259EndOfInterrupt (&mInterrupt8259, Irq);
606 }
607
608 //
609 // Set the 8259 Master base to 0x68 and the 8259 Slave base to 0x70
610 //
611 Status = Interrupt8259SetVectorBase (&mInterrupt8259, PROTECTED_MODE_BASE_VECTOR_MASTER, PROTECTED_MODE_BASE_VECTOR_SLAVE);
612
613 //
614 // Set all 8259 interrupts to edge triggered and disabled
615 //
616 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
617
618 //
619 // Install 8259 Protocol onto a new handle
620 //
621 Status = gBS->InstallProtocolInterface (
622 &m8259Handle,
623 &gEfiLegacy8259ProtocolGuid,
624 EFI_NATIVE_INTERFACE,
625 &mInterrupt8259
626 );
627 return Status;
628 }