5#include "pedigree/kernel/LockGuard.h"
6#include "pedigree/kernel/Log.h"
7#include "pedigree/kernel/process/Mutex.h"
8#include "pedigree/kernel/processor/Processor.h"
9#include "pedigree/kernel/time/Time.h"
11#include <uacpi/namespace.h>
12#include <uacpi/opregion.h>
13#include <uacpi/resources.h>
14#include <uacpi/uacpi.h>
15#include <uacpi/utilities.h>
18constexpr size_t MaxControllers = 4;
19constexpr uint8_t OutputFull = 1;
20constexpr uint8_t InputFull = 2;
21constexpr uint8_t ReadCommand = 0x80;
22constexpr uint8_t WriteCommand = 0x81;
23constexpr auto EcTimeout = 100 * Time::Multiplier::Millisecond;
24constexpr const char* EcIds[] = {
"PNP0C09",
nullptr};
27 uacpi_namespace_node* node =
nullptr;
28 uint16_t dataPort = 0;
29 uint16_t commandPort = 0;
33Ec controllers[MaxControllers];
34size_t controllerCount = 0;
35bool installed =
false;
37uint8_t readPort(uint16_t port) {
39 asm volatile(
"inb %1, %0" :
"=a"(value) :
"Nd"(port));
43void writePort(uint16_t port, uint8_t value) {
44 asm volatile(
"outb %0, %1" : :
"a"(value),
"Nd"(port));
47bool waitStatus(
const Ec& ec, uint8_t mask, uint8_t expected) {
48 const auto deadline = Time::getTicks() + EcTimeout;
50 if ((readPort(ec.commandPort) & mask) == expected) {
54 }
while (Time::getTicks() < deadline);
55 return (readPort(ec.commandPort) & mask) == expected;
58bool readByte(
const Ec& ec, uint8_t offset, uint8_t& value) {
59 if (!waitStatus(ec, InputFull | OutputFull, 0)) {
62 writePort(ec.commandPort, ReadCommand);
63 if (!waitStatus(ec, InputFull, 0)) {
66 writePort(ec.dataPort, offset);
67 if (!waitStatus(ec, OutputFull, OutputFull)) {
70 value = readPort(ec.dataPort);
74bool writeByte(
const Ec& ec, uint8_t offset, uint8_t value) {
75 if (!waitStatus(ec, InputFull | OutputFull, 0)) {
78 writePort(ec.commandPort, WriteCommand);
79 if (!waitStatus(ec, InputFull, 0)) {
82 writePort(ec.dataPort, offset);
83 if (!waitStatus(ec, InputFull, 0)) {
86 writePort(ec.dataPort, value);
87 return waitStatus(ec, InputFull, 0);
90uacpi_status regionOperation(uacpi_region_op operation, uacpi_handle opaque) {
91 if (operation == UACPI_REGION_OP_ATTACH) {
92 const auto* data =
static_cast<uacpi_region_attach_data*
>(opaque);
93 return data->generic_info.base < 256 &&
94 data->generic_info.length <= 256 - data->generic_info.base
96 : UACPI_STATUS_INVALID_ARGUMENT;
98 if (operation == UACPI_REGION_OP_DETACH) {
99 return UACPI_STATUS_OK;
101 if (operation != UACPI_REGION_OP_READ && operation != UACPI_REGION_OP_WRITE) {
102 return UACPI_STATUS_INVALID_ARGUMENT;
105 auto* data =
static_cast<uacpi_region_rw_data*
>(opaque);
106 auto* ec =
static_cast<Ec*
>(data->handler_context);
107 if (!ec || !data->byte_width || data->byte_width > 8 || data->offset > 255 ||
108 data->byte_width > 256 - data->offset) {
109 return UACPI_STATUS_INVALID_ARGUMENT;
113 if (operation == UACPI_REGION_OP_READ) {
116 for (uint8_t i = 0; i < data->byte_width; ++i) {
117 const uint8_t address =
static_cast<uint8_t
>(data->offset + i);
119 const bool ok = operation == UACPI_REGION_OP_READ
120 ? readByte(*ec, address, value)
121 : writeByte(*ec, address, static_cast<uint8_t>(data->value >> (i * 8)));
123 ERROR(
"ACPI EC: timed out at offset " <<
Hex <<
static_cast<unsigned>(address));
124 return UACPI_STATUS_HARDWARE_TIMEOUT;
126 if (operation == UACPI_REGION_OP_READ) {
127 data->value |=
static_cast<uint64_t
>(value) << (i * 8);
130 return UACPI_STATUS_OK;
133uacpi_iteration_decision findController(
void*, uacpi_namespace_node* node, uacpi_u32) {
134 if (controllerCount < MaxControllers && uacpi_device_matches_pnp_id(node, EcIds)) {
135 controllers[controllerCount++].node = node;
137 return UACPI_ITERATION_DECISION_CONTINUE;
140uacpi_iteration_decision findPort(
void* opaque, uacpi_resource* resource) {
141 auto* ec =
static_cast<Ec*
>(opaque);
143 if (resource->type == UACPI_RESOURCE_TYPE_IO) {
144 if (resource->io.minimum != resource->io.maximum || resource->io.length != 1) {
145 return UACPI_ITERATION_DECISION_CONTINUE;
147 port = resource->io.minimum;
148 }
else if (resource->type == UACPI_RESOURCE_TYPE_FIXED_IO) {
149 if (resource->fixed_io.length != 1) {
150 return UACPI_ITERATION_DECISION_CONTINUE;
152 port = resource->fixed_io.address;
157 }
else if (!ec->commandPort) {
158 ec->commandPort = port;
161 return UACPI_ITERATION_DECISION_CONTINUE;
165void installAcpiEcHandlersForShutdown() {
171 uacpi_namespace_for_each_child(uacpi_namespace_root(), findController,
nullptr,
172 UACPI_OBJECT_DEVICE_BIT, UACPI_MAX_DEPTH_ANY,
nullptr);
173 if (status != UACPI_STATUS_OK) {
174 WARNING(
"ACPI EC: device discovery failed: " << uacpi_status_to_string(status));
177 if (!controllerCount) {
178 WARNING(
"ACPI EC: no PNP0C09 controller found");
181 for (
size_t i = 0; i < controllerCount; ++i) {
182 Ec& ec = controllers[i];
183 uacpi_resources* resources =
nullptr;
184 const auto resourceStatus = uacpi_get_current_resources(ec.node, &resources);
185 if (resourceStatus != UACPI_STATUS_OK || !resources) {
187 uacpi_free_resources(resources);
189 WARNING(
"ACPI EC: current resources unavailable: " << uacpi_status_to_string(resourceStatus));
192 const auto parseStatus = uacpi_for_each_resource(resources, findPort, &ec);
193 uacpi_free_resources(resources);
194 if (parseStatus != UACPI_STATUS_OK || !ec.dataPort || !ec.commandPort ||
195 ec.dataPort == ec.commandPort) {
196 WARNING(
"ACPI EC: invalid data and command ports");
200 uint64_t globalLock = 0;
201 const auto lockStatus = uacpi_eval_simple_integer(ec.node,
"_GLK", &globalLock);
202 if ((lockStatus != UACPI_STATUS_OK && lockStatus != UACPI_STATUS_NOT_FOUND) || globalLock) {
203 WARNING(
"ACPI EC: shared controller needs global-lock support");
210 const auto installStatus = uacpi_install_address_space_handler(
211 i ? ec.node : uacpi_namespace_root(), UACPI_ADDRESS_SPACE_EMBEDDED_CONTROLLER,
212 regionOperation, &ec);
213 if (installStatus == UACPI_STATUS_OK) {
214 NOTICE(
"ACPI EC: shutdown handler on data port " <<
Hex << ec.dataPort <<
", command port "
217 WARNING(
"ACPI EC: handler installation failed: " << uacpi_status_to_string(installStatus));
222void installAcpiEcHandlersForShutdown() {}