superio/common: Add more ACPI methods

* Make use of introduced SSDT config mode access
* Make use of introduced SSDT mutex
* Provide ACPI functions to safely access SIO config space
* Implement method to query LDN enable state
* Implement method to set LDN enable state
* Use introduced functions to implement _DIS and _STA in the device
* Update documentation

Tested on Aspeed AST2500 and Linux 5.2.
Manually verified ACPI code that generates no errors in Linux.

Change-Id: I520b29de925f368cd71ff8f1f58d2d57d72eff8d
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/37640
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
diff --git a/src/superio/common/generic.c b/src/superio/common/generic.c
index 7ac1f83..85b70df 100644
--- a/src/superio/common/generic.c
+++ b/src/superio/common/generic.c
@@ -156,6 +156,146 @@
 	acpigen_write_indexfield("INDX", "DATA", i, ARRAY_SIZE(i), FIELD_BYTEACC |
 				 FIELD_NOLOCK | FIELD_PRESERVE);
 
+	const char *mutex = "MTX0";
+
+	acpigen_write_mutex(mutex, 0);
+	/* Backup LDN */
+	acpigen_write_name_integer("BLDN", 0);
+
+	/* Acquire mutex - Enter config mode */
+	acpigen_write_method("AMTX", 0);
+	{
+		acpigen_write_acquire(mutex, 0xffff);
+
+		/* Pick one of the children as the generic SIO doesn't have config mode */
+		if (dev->link_list && dev->link_list->children)
+			pnp_ssdt_enter_conf_mode(dev->link_list->children, "^INDX", "^DATA");
+
+		/* Backup LDN */
+		acpigen_write_store();
+		acpigen_emit_namestring("^LDN");
+		acpigen_emit_namestring("^BLDN");
+	}
+	acpigen_pop_len(); /* Method */
+
+	/* Release mutex - Exit config mode */
+	acpigen_write_method("RMTX", 0);
+	{
+		/* Restore LDN */
+		acpigen_write_store();
+		acpigen_emit_namestring("^BLDN");
+		acpigen_emit_namestring("^LDN");
+
+		/* Pick one of the children as the generic SIO doesn't have config mode */
+		if (dev->link_list && dev->link_list->children)
+			pnp_ssdt_exit_conf_mode(dev->link_list->children, "^INDX", "^DATA");
+
+		acpigen_write_release(mutex);
+	}
+	acpigen_pop_len(); /* Method */
+
+	/* Select a LDN */
+	acpigen_write_method("SLDN", 1);
+	{
+		/* Local0 = Arg0 & 0xff */
+		acpigen_emit_byte(AND_OP);
+		acpigen_write_integer(0xff);
+		acpigen_emit_byte(ARG0_OP);
+		acpigen_emit_byte(LOCAL0_OP);
+
+		/* LDN = LOCAL0_OP */
+		acpigen_write_store();
+		acpigen_emit_byte(LOCAL0_OP);
+		acpigen_emit_namestring("^LDN");
+	}
+	acpigen_pop_len(); /* Method */
+
+	/* Disable a LDN/VLDN */
+	acpigen_write_method("DLDN", 1);
+	{
+		/* AMTX() */
+		acpigen_emit_namestring("AMTX");
+
+		/* SLDN (Local0) */
+		acpigen_emit_namestring("SLDN");
+		acpigen_emit_byte(ARG0_OP);
+
+		/* Local0 = Arg0 >> 8 */
+		acpigen_emit_byte(SHIFT_RIGHT_OP);
+		acpigen_emit_byte(ARG0_OP);
+		acpigen_write_integer(8);
+		acpigen_emit_byte(LOCAL0_OP);
+
+		/* Local0 = Local0 & 0x7 */
+		acpigen_emit_byte(AND_OP);
+		acpigen_write_integer(0x7);
+		acpigen_emit_byte(LOCAL0_OP);
+		acpigen_emit_byte(LOCAL0_OP);
+
+		for (int j = 0; j < 8; j++) {
+			char act[6] = "^ACT0";
+			act[4] += j;
+
+			/* If (Local0 == j) { */
+			acpigen_write_if_lequal_op_int(LOCAL0_OP, j);
+
+			/* ACT[j] = 0 */
+			acpigen_write_store();
+			acpigen_emit_byte(ZERO_OP);
+			acpigen_emit_namestring(act);
+
+			acpigen_pop_len(); /* } */
+		}
+
+		/* RMTX() */
+		acpigen_emit_namestring("RMTX");
+	}
+	acpigen_pop_len(); /* Method */
+
+	/* Query LDN enable state. Returns 1 if LDN/VLDN is enabled. */
+	acpigen_write_method("QLDN", 1);
+	{
+		acpigen_emit_namestring("AMTX");
+
+		/* SLDN (Local0) */
+		acpigen_emit_namestring("SLDN");
+		acpigen_emit_byte(ARG0_OP);
+
+		/* Local0 = Arg0 >> 8 */
+		acpigen_emit_byte(SHIFT_RIGHT_OP);
+		acpigen_emit_byte(ARG0_OP);
+		acpigen_write_integer(8);
+		acpigen_emit_byte(LOCAL0_OP);
+
+		/* Local0 = Local0 & 0x7 */
+		acpigen_emit_byte(AND_OP);
+		acpigen_write_integer(0x7);
+		acpigen_emit_byte(LOCAL0_OP);
+		acpigen_emit_byte(LOCAL0_OP);
+
+		for (int j = 0; j < 8; j++) {
+			char act[6] = "^ACT0";
+			act[4] += j;
+			/* If (Local0 == j) { */
+			acpigen_write_if_lequal_op_int(LOCAL0_OP, j);
+
+			/* Local1 = ACT[j] */
+			acpigen_write_store();
+			acpigen_emit_namestring(act);
+			acpigen_emit_byte(LOCAL1_OP);
+
+			acpigen_pop_len(); /* } */
+		}
+
+		/* RMTX() */
+		acpigen_emit_namestring("RMTX");
+
+		/* Return (Local1) */
+		acpigen_emit_byte(RETURN_OP);
+		acpigen_emit_byte(LOCAL1_OP);
+	}
+	acpigen_pop_len(); /* Method */
+
 	acpigen_pop_len(); /* Device */
 	acpigen_pop_len(); /* Scope */
 }