Tim Wawrzynczak | d40a4c2 | 2021-02-25 13:14:49 -0700 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | |
| 3 | #include <acpi/acpigen.h> |
| 4 | #include <acpi/acpigen_pci.h> |
| 5 | #include <assert.h> |
| 6 | #include <device/device.h> |
| 7 | #include <device/pci_def.h> |
| 8 | #include <device/pci_type.h> |
| 9 | #include <types.h> |
| 10 | |
| 11 | void acpigen_write_ADR_pci_devfn(pci_devfn_t devfn) |
| 12 | { |
| 13 | /* |
| 14 | * _ADR for PCI Bus is encoded as follows: |
| 15 | * [63:32] - unused |
| 16 | * [31:16] - device # |
| 17 | * [15:0] - function # |
| 18 | */ |
| 19 | acpigen_write_ADR(PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn)); |
| 20 | } |
| 21 | |
| 22 | void acpigen_write_ADR_pci_device(const struct device *dev) |
| 23 | { |
| 24 | assert(dev->path.type == DEVICE_PATH_PCI); |
| 25 | acpigen_write_ADR_pci_devfn(dev->path.pci.devfn); |
| 26 | } |
Tim Wawrzynczak | 290979f | 2021-02-26 10:54:15 -0700 | [diff] [blame] | 27 | |
| 28 | void acpigen_write_PRT_GSI_entry(unsigned int pci_dev, unsigned int acpi_pin, unsigned int gsi) |
| 29 | { |
| 30 | acpigen_write_package(4); |
| 31 | acpigen_write_dword((pci_dev << 16) | 0xffff); |
| 32 | acpigen_write_byte(acpi_pin); |
| 33 | |
| 34 | /* Source */ |
| 35 | acpigen_write_byte(0); |
| 36 | |
| 37 | /* Source Index */ |
| 38 | acpigen_write_dword(gsi); |
| 39 | |
| 40 | acpigen_pop_len(); /* Package */ |
| 41 | } |
| 42 | |
| 43 | void acpigen_write_PRT_source_entry(unsigned int pci_dev, unsigned int acpi_pin, |
| 44 | const char *source_path, unsigned int index) |
| 45 | { |
| 46 | acpigen_write_package(4); |
| 47 | acpigen_write_dword((pci_dev << 16) | 0xffff); |
| 48 | acpigen_write_byte(acpi_pin); |
| 49 | |
| 50 | /* Source */ |
| 51 | acpigen_emit_namestring(source_path); |
| 52 | |
| 53 | /* Source Index */ |
| 54 | acpigen_write_dword(index); |
| 55 | |
| 56 | acpigen_pop_len(); /* Package */ |
| 57 | } |
Shuo Liu | f4a12e1 | 2024-03-21 21:22:03 +0800 | [diff] [blame] | 58 | |
| 59 | #define PCI_HOST_BRIDGE_OSC_UUID "33db4d5b-1ff7-401c-9657-7441c03dd766" |
| 60 | #define CXL_HOST_BRIDGE_OSC_UUID "68f2d50b-c469-4d8a-bd3d-941a103fd3fc" |
| 61 | |
| 62 | #define OSC_RET_FAILURE 0x02 |
| 63 | #define OSC_RET_UNRECOGNIZED_UUID 0x04 |
| 64 | #define OSC_RET_UNRECOGNIZED_REV 0x08 |
| 65 | #define OSC_RET_CAPABILITIES_MASKED 0x10 |
| 66 | |
| 67 | #define OSC_QUERY_SUPPORT_SET 0x01 |
| 68 | |
| 69 | #define ASL_UUID_UNHANDLED 0x00 |
| 70 | #define ASL_UUID_HANDLED 0x01 |
| 71 | |
| 72 | static void acpigen_OSC_handle_pcie_request(const struct device *domain); |
| 73 | static void acpigen_OSC_handle_cxl_request(const struct device *domain); |
| 74 | |
| 75 | /* |
| 76 | * acpigen_write_OSC_pci_domain |
| 77 | * |
| 78 | * Reference: |
| 79 | * 6.2.11 in https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html |
| 80 | * |
| 81 | * _OSC ASL Arguments: (4) |
| 82 | * Arg0 - A Buffer containing a UUID |
| 83 | * Arg1 - An Integer containing a Revision ID of the buffer format |
| 84 | * Arg2 - An Integer containing a count of entries in Arg3 |
| 85 | * Arg3 - A Buffer containing a list of DWORD capabilities |
| 86 | * |
| 87 | * _OSC ASL Return Value: |
| 88 | * A Buffer containing a list of capabilities |
| 89 | * |
| 90 | * Local Variables Assignment: |
| 91 | * Local0 - Temp assigned |
| 92 | * Local1 - Temp assigned |
| 93 | * Local2 - Not used |
| 94 | * Local3 - Not used |
| 95 | * Local4 - Not used |
| 96 | * Local5 - Not used |
| 97 | * Local6 - Record whether the UUID is handled |
| 98 | * Local7 - Backs up the input value of Arg3 |
| 99 | * |
| 100 | * Field Definitions: |
| 101 | * Name - Width Source Offset Description |
| 102 | * -------------------------------- |
| 103 | * QSUP - DWord Local7 0x00 Query support |
| 104 | * RETE - DWord Arg3 0x00 Returned errors |
| 105 | * SUPP - Dword Arg3 0x04 PCIe Features that OS supported |
| 106 | * CTRL - Dword Arg3 0x08 PCIe Features that firmware grant control to OS |
| 107 | * OTRL - Dword Local7 0x08 PCIe Features that OS requests for control |
| 108 | * SUPC - Dword Arg3 0x0C CXL Features that OS supported |
| 109 | * CTRC - Dword Arg3 0x10 CXL Features that firmware grant control to OS |
| 110 | * OTRC - Dword Local7 0x10 CXL Features that OS requests for control |
| 111 | */ |
| 112 | void acpigen_write_OSC_pci_domain(const struct device *domain, const bool is_cxl_domain) |
| 113 | { |
| 114 | /* |
| 115 | * Method (_OSC, 4, NotSerialized) |
| 116 | * { |
| 117 | */ |
| 118 | acpigen_write_method("_OSC", 4); |
| 119 | |
| 120 | /* |
| 121 | * // |
| 122 | * // Check revision ID |
| 123 | * // |
| 124 | * If (Arg1 != 1) |
| 125 | * { |
| 126 | * RETE = OSC_RET_UNRECOGNIZED_REV |
| 127 | * Return (Arg3) |
| 128 | * } |
| 129 | */ |
| 130 | acpigen_write_if(); |
| 131 | acpigen_emit_byte(LNOT_OP); |
| 132 | acpigen_emit_byte(LEQUAL_OP); |
| 133 | acpigen_emit_byte(ARG1_OP); |
| 134 | acpigen_write_integer(0x1); |
| 135 | |
| 136 | acpigen_write_store_int_to_namestr(OSC_RET_UNRECOGNIZED_REV, "RETE"); |
| 137 | acpigen_write_return_op(ARG3_OP); |
| 138 | |
| 139 | acpigen_write_if_end(); |
| 140 | |
| 141 | /* |
| 142 | * // |
| 143 | * // Setup up local variables |
| 144 | * // |
| 145 | * Local7 = Arg3 |
| 146 | * CreateDwordField (Local7, 0x00, QSUP) |
| 147 | * CreateDWordField (Arg3, 0x00, RETE) |
| 148 | * RETE = 0x0 |
| 149 | * Local6 = ASL_UUID_UNHANDLED |
| 150 | */ |
| 151 | acpigen_write_store_ops(ARG3_OP, LOCAL7_OP); |
| 152 | acpigen_write_create_dword_field(LOCAL7_OP, 0x00, "QSUP"); |
| 153 | acpigen_write_create_dword_field(ARG3_OP, 0x00, "RETE"); |
| 154 | acpigen_write_store_int_to_namestr(0x0, "RETE"); |
| 155 | acpigen_write_store_int_to_op(ASL_UUID_UNHANDLED, LOCAL6_OP); |
| 156 | |
| 157 | /* |
| 158 | * // |
| 159 | * // Refer to CXL-3.1-Specification, 9.18.2 |
| 160 | * // A CXL Host Bridge also originates a PCIe hierarchy and will have a |
| 161 | * // _CID of EISAID("PNP0A08"). As such, a CXL Host Bridge device may expose |
| 162 | * // both CXL _OSC and PCIe _OSC. |
| 163 | * // |
| 164 | * |
| 165 | * If (Arg0 == ToUUID (PCI_HOST_BRIDGE_OSC_UUID)) |
| 166 | * { |
| 167 | * // |
| 168 | * // Handle PCIe _OSC request |
| 169 | * // Mark UUID handled |
| 170 | * // |
| 171 | * } |
| 172 | */ |
| 173 | acpigen_write_if(); |
| 174 | acpigen_emit_byte(LEQUAL_OP); |
| 175 | acpigen_emit_byte(ARG0_OP); |
| 176 | acpigen_write_uuid(PCI_HOST_BRIDGE_OSC_UUID); |
| 177 | |
| 178 | acpigen_OSC_handle_pcie_request(domain); |
| 179 | acpigen_write_store_int_to_op(ASL_UUID_HANDLED, LOCAL6_OP); |
| 180 | |
| 181 | acpigen_write_if_end(); |
| 182 | |
| 183 | if (is_cxl_domain) { |
| 184 | /* |
| 185 | * If (Arg0 == ToUUID (CXL_HOST_BRIDGE_OSC_UUID)) |
| 186 | * { |
| 187 | * // |
| 188 | * // Handle CXL _OSC request |
| 189 | * // Mark UUID handled |
| 190 | * // |
| 191 | * } |
| 192 | */ |
| 193 | acpigen_write_if(); |
| 194 | acpigen_emit_byte(LEQUAL_OP); |
| 195 | acpigen_emit_byte(ARG0_OP); |
| 196 | acpigen_write_uuid(CXL_HOST_BRIDGE_OSC_UUID); |
| 197 | |
| 198 | acpigen_OSC_handle_cxl_request(domain); |
| 199 | acpigen_write_store_int_to_op(ASL_UUID_HANDLED, LOCAL6_OP); |
| 200 | |
| 201 | acpigen_write_if_end(); |
| 202 | } |
| 203 | |
| 204 | /* |
| 205 | * // |
| 206 | * // Handle unrecognized UUID |
| 207 | * // |
| 208 | * If (Local6 == ASL_UUID_UNHANDLED) |
| 209 | * { |
| 210 | * RETE = OSC_RET_UNRECOGNIZED_UUID |
| 211 | * } |
| 212 | */ |
| 213 | acpigen_write_if_lequal_op_int(LOCAL6_OP, ASL_UUID_UNHANDLED); |
| 214 | acpigen_write_store_int_to_namestr(OSC_RET_UNRECOGNIZED_UUID, "RETE"); |
| 215 | acpigen_write_if_end(); |
| 216 | |
| 217 | /* |
| 218 | * // |
| 219 | * // All done, return |
| 220 | * // |
| 221 | * Return (Arg3) |
| 222 | */ |
| 223 | acpigen_write_return_op(ARG3_OP); |
| 224 | |
| 225 | /* |
| 226 | * } // Method (_OSC, 4, NotSerialized) |
| 227 | */ |
| 228 | acpigen_pop_len(); |
| 229 | |
| 230 | } |
| 231 | |
| 232 | void acpigen_OSC_handle_pcie_request(const struct device *domain) |
| 233 | { |
| 234 | uint32_t osc_features = soc_get_granted_pci_features(domain); |
| 235 | |
| 236 | /* |
| 237 | * If (Arg2 < 2)) |
| 238 | * { |
| 239 | * RETE = OSC_RET_FAILURE |
| 240 | * Return (Arg3) |
| 241 | * } |
| 242 | */ |
| 243 | acpigen_write_if(); |
| 244 | acpigen_emit_byte(LLESS_OP); |
| 245 | acpigen_emit_byte(ARG2_OP); |
| 246 | acpigen_write_integer(0x2); |
| 247 | |
| 248 | acpigen_write_store_int_to_namestr(OSC_RET_FAILURE, "RETE"); |
| 249 | acpigen_write_return_op(ARG3_OP); |
| 250 | |
| 251 | acpigen_write_if_end(); |
| 252 | |
| 253 | /* |
| 254 | * CreateDWordField (Arg3, 0x04, SUPP) |
| 255 | * CreateDWordField (Arg3, 0x08, CTRL) |
| 256 | * CreateDWordField (Local7, 0x08, OTRL) |
| 257 | */ |
| 258 | acpigen_write_create_dword_field(ARG3_OP, 0x04, "SUPP"); |
| 259 | acpigen_write_create_dword_field(ARG3_OP, 0x08, "CTRL"); |
| 260 | acpigen_write_create_dword_field(LOCAL7_OP, 0x08, "OTRL"); |
| 261 | |
| 262 | /* |
| 263 | * // Grant PCIe feature controls to OS |
| 264 | * CTRL &= osc_features |
| 265 | */ |
| 266 | acpigen_write_to_integer_from_namestring("CTRL", LOCAL0_OP); |
| 267 | acpigen_write_store_int_to_op(osc_features, LOCAL1_OP); |
| 268 | acpigen_write_and(LOCAL0_OP, LOCAL1_OP, LOCAL0_OP); |
| 269 | acpigen_write_store_op_to_namestr(LOCAL0_OP, "CTRL"); |
| 270 | |
| 271 | /* |
| 272 | * If (CTRL != OTRL) |
| 273 | * { |
| 274 | * RETE = OSC_RET_CAPABILITIES_MASKED |
| 275 | * } |
| 276 | */ |
| 277 | acpigen_write_if(); |
| 278 | acpigen_emit_byte(LNOT_OP); |
| 279 | acpigen_emit_byte(LEQUAL_OP); |
| 280 | acpigen_emit_namestring("CTRL"); |
| 281 | acpigen_emit_namestring("OTRL"); |
| 282 | acpigen_write_store_int_to_namestr(OSC_RET_CAPABILITIES_MASKED, "RETE"); |
| 283 | acpigen_write_if_end(); |
| 284 | }; |
| 285 | |
| 286 | void acpigen_OSC_handle_cxl_request(const struct device *domain) |
| 287 | { |
| 288 | uint32_t osc_features = soc_get_granted_cxl_features(domain); |
| 289 | |
| 290 | /* |
| 291 | * If (Arg2 < 4)) |
| 292 | * { |
| 293 | * RETE = OSC_RET_FAILURE |
| 294 | * Return (Arg3) |
| 295 | * } |
| 296 | */ |
| 297 | acpigen_write_if(); |
| 298 | acpigen_emit_byte(LLESS_OP); |
| 299 | acpigen_emit_byte(ARG2_OP); |
| 300 | acpigen_write_integer(0x4); |
| 301 | |
| 302 | acpigen_write_store_int_to_namestr(OSC_RET_FAILURE, "RETE"); |
| 303 | acpigen_write_return_op(ARG3_OP); |
| 304 | |
| 305 | acpigen_write_if_end(); |
| 306 | |
| 307 | /* |
| 308 | * CreateDWordField (Arg3, 0x0C, SUPC) |
| 309 | * CreateDWordField (Arg3, 0x10, CTRC) |
| 310 | * CreateDWordField (Local7, 0x10, OTRC) |
| 311 | */ |
| 312 | acpigen_write_create_dword_field(ARG3_OP, 0x0C, "SUPC"); |
| 313 | acpigen_write_create_dword_field(ARG3_OP, 0x10, "CTRC"); |
| 314 | acpigen_write_create_dword_field(LOCAL7_OP, 0x10, "OTRC"); |
| 315 | |
| 316 | /* |
| 317 | * // Grant CXL feature controls to OS |
| 318 | * CTRC &= osc_features |
| 319 | */ |
| 320 | acpigen_write_to_integer_from_namestring("CTRC", LOCAL0_OP); |
| 321 | acpigen_write_store_int_to_op(osc_features, LOCAL1_OP); |
| 322 | acpigen_write_and(LOCAL0_OP, LOCAL1_OP, LOCAL0_OP); |
| 323 | acpigen_write_store_op_to_namestr(LOCAL0_OP, "CTRC"); |
| 324 | |
| 325 | /* |
| 326 | * If (CTRC != OTRC) |
| 327 | * { |
| 328 | * RETE = OSC_RET_CAPABILITIES_MASKED |
| 329 | * } |
| 330 | */ |
| 331 | acpigen_write_if(); |
| 332 | acpigen_emit_byte(LNOT_OP); |
| 333 | acpigen_emit_byte(LEQUAL_OP); |
| 334 | acpigen_emit_namestring("CTRC"); |
| 335 | acpigen_emit_namestring("OTRC"); |
| 336 | acpigen_write_store_int_to_namestr(OSC_RET_CAPABILITIES_MASKED, "RETE"); |
| 337 | acpigen_write_if_end(); |
| 338 | }; |
| 339 | |
| 340 | __weak uint32_t soc_get_granted_pci_features(const struct device *domain) |
| 341 | { |
| 342 | /* |
| 343 | * By default grant no features to OS, which equals to the case where _OSC |
| 344 | * is absent. |
| 345 | * |
| 346 | * Refer to PCI firmware specification, revision 3.1. |
| 347 | * If the _OSC control method is absent from the scope of a host bridge device, then |
| 348 | * the operating system must not enable or attempt to use any features defined in this |
| 349 | * section for the hierarchy originated by the host bridge. Doing so could contend with |
| 350 | * platform firmware operations or produce undesired results. |
| 351 | */ |
| 352 | return 0; |
| 353 | } |
| 354 | |
| 355 | __weak uint32_t soc_get_granted_cxl_features(const struct device *domain) |
| 356 | { |
| 357 | /* |
| 358 | * By default grant no features to OS, which equals to the case where _OSC |
| 359 | * is absent. |
| 360 | */ |
| 361 | return 0; |
| 362 | } |