/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include 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); }