summaryrefslogtreecommitdiff
path: root/src/soc/intel/broadwell/pch/acpi.c
blob: 8c435d6f473a096edd04a0309f48cee65d48022f (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
110
111
112
113
114
115
/* 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);
}