blob: 8c435d6f473a096edd04a0309f48cee65d48022f [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpi.h>
#include <acpi/acpigen.h>
#include <assert.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 struct pch_acpi_device_state device_state[NUM_PCH_ACPI_DEVICES] = { 0 };
struct pch_acpi_device_state *get_acpi_device_state(enum pch_acpi_device dev_index)
{
assert(dev_index < ARRAY_SIZE(device_state));
return &device_state[dev_index];
}
static void acpi_create_serialio_ssdt_entry(enum pch_acpi_device dev_index)
{
const struct pch_acpi_device_state *state = get_acpi_device_state(dev_index);
const char idx = '0' + dev_index;
const char sxen[5] = { 'S', idx, 'E', 'N', '\0' };
acpigen_write_name_byte(sxen, state->enable);
const char sxb0[5] = { 'S', idx, 'B', '0', '\0' };
acpigen_write_name_dword(sxb0, state->bar0);
const char sxb1[5] = { 'S', idx, 'B', '1', '\0' };
acpigen_write_name_dword(sxb1, state->bar1);
}
void acpi_create_serialio_ssdt(acpi_header_t *ssdt)
{
unsigned long current = (unsigned long)ssdt + sizeof(acpi_header_t);
/* 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 (enum pch_acpi_device dev_index = 0; dev_index < NUM_PCH_ACPI_DEVICES; dev_index++)
acpi_create_serialio_ssdt_entry(dev_index);
acpigen_write_scope("\\_SB.PCI0");
{
acpi_write_serialio_psx_methods("I2C0", device_state[PCH_ACPI_I2C0].bar1);
acpi_write_serialio_psx_methods("I2C1", device_state[PCH_ACPI_I2C1].bar1);
acpi_write_serialio_psx_methods("SPI0", device_state[PCH_ACPI_GSPI0].bar1);
acpi_write_serialio_psx_methods("SPI1", device_state[PCH_ACPI_GSPI1].bar1);
acpi_write_serialio_psx_methods("UAR0", device_state[PCH_ACPI_UART0].bar1);
acpi_write_serialio_psx_methods("UAR1", device_state[PCH_ACPI_UART1].bar1);
}
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);
}