// SPDX-License-Identifier: GPL-2.0 #include "pr.h" #include #include #include #include #include #include #include "ec.h" #include "wmi.h" /* ========================================================================== */ static DECLARE_RWSEM(ec_lock); /* ========================================================================== */ int __must_check ac71_ec_lock(void) { return down_write_killable(&ec_lock); } void ac71_ec_unlock(void) { up_write(&ec_lock); } int __must_check ac71_ec_transaction(uint16_t addr, uint16_t data, union ac71_ec_result *result, bool read) { uint8_t buf[] = { addr & 0xFF, addr >> 8, data & 0xFF, data >> 8, 0, read ? 1 : 0, 0, 0, }; static_assert(ARRAY_SIZE(buf) == 8); /* the returned ACPI_TYPE_BUFFER is 40 bytes long for some reason ... */ uint8_t output_buf[sizeof(union acpi_object) + 40]; struct acpi_buffer input = { sizeof(buf), buf }, output = { sizeof(output_buf), output_buf }; union acpi_object *obj; acpi_status status = AE_OK; int err; if (read) err = down_read_killable(&ec_lock); else err = down_write_killable(&ec_lock); if (err) goto out; memset(output_buf, 0, sizeof(output_buf)); status = wmi_evaluate_method(AC71_WMI_WMBC_GUID, 0, AC71_WMBC_GETSETULONG_ID, &input, &output); if (read) up_read(&ec_lock); else up_write(&ec_lock); if (ACPI_FAILURE(status)) { err = -EIO; goto out; } obj = output.pointer; if (result) { if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= sizeof(*result)) { memcpy(result, obj->buffer.pointer, sizeof(*result)); } else { err = -ENODATA; goto out; } } out: pr_debug( "%s(addr=%#06x, data=%#06x, result=%c, read=%c)" ": (%d) [%#010lx] %s" ": [%*ph]\n", __func__, (unsigned int) addr, (unsigned int) data, result ? 'y' : 'n', read ? 'y' : 'n', err, (unsigned long) status, acpi_format_exception(status), (obj && obj->type == ACPI_TYPE_BUFFER) ? (int) min(sizeof(*result), (size_t) obj->buffer.length) : 0, (obj && obj->type == ACPI_TYPE_BUFFER) ? obj->buffer.pointer : NULL ); return err; } ALLOW_ERROR_INJECTION(ac71_ec_transaction, ERRNO);