aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/broadwell/pch/acpi.c
blob: 85726b06e47e3e83f75a0a9046b30211b186db2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* 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);
}