blob: 85726b06e47e3e83f75a0a9046b30211b186db2c [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpi.h>
#include <acpi/acpi_gnvs.h>
#include <acpi/acpigen.h>
#include <soc/device_nvs.h>
#include <soc/pch.h>
#include <types.h>
#include <version.h>
static void acpi_write_serialio_psx_methods(const char *const name, const uint32_t bar1)
{
const char *const spcs = "SPCS";
const unsigned int spcs_bits = 32;
const unsigned long offset = bar1 + 0x84;
const uint8_t flags = FIELD_DWORDACC | FIELD_NOLOCK | FIELD_PRESERVE;
const struct opregion op_reg = OPREGION("SPRT", SYSTEMMEMORY, offset, spcs_bits / 8);
const struct fieldlist field = FIELDLIST_NAMESTR(spcs, spcs_bits);
acpigen_write_scope(name);
{
acpigen_write_opregion(&op_reg);
acpigen_write_field(op_reg.name, &field, 1, flags);
acpigen_write_method_serialized("_PS0", 0);
{
/* SPCS &= 0xfffffffc */
acpigen_emit_byte(AND_OP);
acpigen_emit_namestring(spcs);
acpigen_write_dword(0xfffffffc);
acpigen_emit_namestring(spcs);
/* Do a posting read after writing */
acpigen_write_store();
acpigen_emit_namestring(spcs);
acpigen_emit_byte(LOCAL0_OP);
}
acpigen_pop_len();
acpigen_write_method_serialized("_PS3", 0);
{
/* SPCS |= 3 */
acpigen_emit_byte(OR_OP);
acpigen_emit_namestring(spcs);
acpigen_write_byte(3);
acpigen_emit_namestring(spcs);
/* Do a posting read after writing */
acpigen_write_store();
acpigen_emit_namestring(spcs);
acpigen_emit_byte(LOCAL0_OP);
}
acpigen_pop_len();
}
acpigen_pop_len();
}
static void acpi_create_serialio_ssdt_entry(int sio_index, struct device_nvs *dev_nvs)
{
const char idx = '0' + sio_index;
const char sxen[5] = { 'S', idx, 'E', 'N', '\0' };
acpigen_write_name_byte(sxen, dev_nvs->enable[sio_index]);
const char sxb0[5] = { 'S', idx, 'B', '0', '\0' };
acpigen_write_name_dword(sxb0, dev_nvs->bar0[sio_index]);
const char sxb1[5] = { 'S', idx, 'B', '1', '\0' };
acpigen_write_name_dword(sxb1, dev_nvs->bar1[sio_index]);
}
void acpi_create_serialio_ssdt(acpi_header_t *ssdt)
{
unsigned long current = (unsigned long)ssdt + sizeof(acpi_header_t);
struct device_nvs *dev_nvs = acpi_get_device_nvs();
if (!dev_nvs)
return;
/* Fill the SSDT header */
memset((void *)ssdt, 0, sizeof(acpi_header_t));
memcpy(&ssdt->signature, "SSDT", 4);
ssdt->revision = get_acpi_table_revision(SSDT);
memcpy(&ssdt->oem_id, OEM_ID, 6);
memcpy(&ssdt->oem_table_id, "SERIALIO", 8);
ssdt->oem_revision = 43;
memcpy(&ssdt->asl_compiler_id, ASLC, 4);
ssdt->asl_compiler_revision = asl_revision;
ssdt->length = sizeof(acpi_header_t);
acpigen_set_current((char *)current);
/* Fill the SSDT with an entry for each SerialIO device */
for (int id = 0; id < 9; id++)
acpi_create_serialio_ssdt_entry(id, dev_nvs);
acpigen_write_scope("\\_SB.PCI0");
{
acpi_write_serialio_psx_methods("I2C0", dev_nvs->bar1[SIO_NVS_I2C0]);
acpi_write_serialio_psx_methods("I2C1", dev_nvs->bar1[SIO_NVS_I2C1]);
acpi_write_serialio_psx_methods("SPI0", dev_nvs->bar1[SIO_NVS_SPI0]);
acpi_write_serialio_psx_methods("SPI1", dev_nvs->bar1[SIO_NVS_SPI1]);
acpi_write_serialio_psx_methods("UAR0", dev_nvs->bar1[SIO_NVS_UART0]);
acpi_write_serialio_psx_methods("UAR1", dev_nvs->bar1[SIO_NVS_UART1]);
}
acpigen_pop_len();
/* (Re)calculate length and checksum. */
current = (unsigned long)acpigen_get_current();
ssdt->length = current - (unsigned long)ssdt;
ssdt->checksum = acpi_checksum((void *)ssdt, ssdt->length);
}