The Pedigree Project 0.1
IntelPirq.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_CHIPSET_INTEL_PIRQ_H
3#define PEDIGREE_CHIPSET_INTEL_PIRQ_H
4#include "pedigree/kernel/processor/types.h"
5
6// Intel 6 Series datasheet 324645, sections 10.1.20-34 and 13.1.16/18.
7// Preserve firmware pin selection and device-to-PIRQ routing.
8namespace IntelPirq {
9inline uint16_t routeOffset(uint8_t device) {
10 switch (device) {
11 case 31:
12 return 0x3140;
13 case 29:
14 return 0x3144;
15 case 27:
16 return 0x3148;
17 case 26:
18 return 0x314c;
19 case 25:
20 return 0x3150;
21 case 22:
22 return 0x315c;
23 default:
24 return 0;
25 }
26}
27inline bool decode(uint16_t routes, uint8_t pin, uint8_t& pirq) {
28 if (pin < 1 || pin > 4 || routes == 0xffff)
29 return false;
30 const uint8_t selector = (routes >> (4 * (pin - 1))) & 0xf;
31 if (selector > 7)
32 return false;
33 pirq = selector;
34 return true;
35}
36inline uint16_t pinOffset(uint8_t device) {
37 switch (device) {
38 case 31:
39 return 0x3100;
40 case 29:
41 return 0x3108;
42 case 27:
43 return 0x3110;
44 case 26:
45 return 0x3114;
46 case 25:
47 return 0x3118;
48 case 22:
49 return 0x3124;
50 default:
51 return 0;
52 }
53}
54inline bool nativePinMatches(uint32_t pins, uint8_t function, uint8_t pin) {
55 return pins != 0xffffffff && function < 8 && pin >= 1 && pin <= 4 &&
56 ((pins >> (4 * function)) & 0xf) == pin;
57}
58inline bool chipsetExclusions(uint8_t acpiControl, uint8_t tcoControl, uint16_t& excluded) {
59 if (acpiControl == 0xff || tcoControl == 0xff || (acpiControl & 7) == 3 ||
60 ((tcoControl & 0x80) && (tcoControl & 7) == 3))
61 return false;
62 excluded = 0;
63 if ((acpiControl & 7) < 3)
64 excluded |= uint16_t{1} << (9 + (acpiControl & 7));
65 if ((tcoControl & 0x80) && (tcoControl & 7) < 3)
66 excluded |= uint16_t{1} << (9 + (tcoControl & 7));
67 return true;
68}
69inline uint16_t hpetExclusion(uint32_t config, uint32_t pending, unsigned timer,
70 bool legacyReplacement) {
71 if (config == 0xffffffff || pending == 0xffffffff)
72 return (1U << 10) | (1U << 11);
73 if ((config & (1U << 14)) || (legacyReplacement && timer < 2))
74 return 0;
75 const unsigned irq = (config >> 9) & 31;
76 const bool active = (config & 4) || ((config & 2) && (pending & (1U << timer)));
77 return active && irq < 16 ? uint16_t{1} << irq : 0;
78}
79inline uint16_t configOffset(uint8_t pirq) {
80 return pirq < 4 ? 0x60 + pirq : 0x68 + pirq - 4;
81}
82inline bool validIrq(uint8_t irq) {
83 return irq < 16 && (0xdef8U & (1U << irq));
84}
85
86enum class Result { Ready, ReadFailed, Unsupported, Conflict, WriteFailed, RestoreFailed };
87
88// Reserve PIC ownership before connecting a previously disabled PIRQ. An enabled
89// firmware route is preserved unless the caller explicitly identifies its line
90// as unavailable to PCI (for example, the fixed PS/2 mouse line).
91template <class Config, class Reserve>
92Result establish(Config& config, Reserve reserve, uint8_t pirq, uint16_t excluded, uint8_t& irq,
93 bool rerouteExcludedEnabled = false) {
94 if (pirq >= 8)
95 return Result::Unsupported;
96 const uint16_t offset = configOffset(pirq);
97 uint8_t original = 0;
98 if (!config.read8(offset, original) || original == 0xff)
99 return Result::ReadFailed;
100 if (!(original & 0x80)) {
101 const uint8_t selected = original & 0xf;
102 if (validIrq(selected) && !(excluded & (1U << selected))) {
103 if (!reserve(selected))
104 return Result::Conflict;
105 irq = selected;
106 return Result::Ready;
107 }
108 if (!rerouteExcludedEnabled)
109 return Result::Unsupported;
110 }
111 // Keep legacy timer/keyboard/serial/storage lines and the SCI out of new
112 // assignments. Sharing a reserved PCI level line is supported by the PIC.
113 const uint8_t choices[] = {static_cast<uint8_t>(10 + (pirq & 1)),
114 static_cast<uint8_t>(11 - (pirq & 1))};
115 for (uint8_t selected : choices) {
116 if ((excluded & (1U << selected)) || !reserve(selected))
117 continue;
118 const uint8_t desired = (original & 0x70) | selected;
119 uint8_t actual = 0;
120 if (config.write8(offset, desired) && config.read8(offset, actual) && actual == desired) {
121 irq = selected;
122 return Result::Ready;
123 }
124 // Keep the reservation even on failure: a posted/partial write must never
125 // leave an electrically connected PIRQ on a line another driver can claim.
126 if (!config.write8(offset, original) || !config.read8(offset, actual) || actual != original)
127 return Result::RestoreFailed;
128 return Result::WriteFailed;
129 }
130 return Result::Conflict;
131}
132} // namespace IntelPirq
133#endif