aboutsummaryrefslogtreecommitdiff
path: root/src/superio/nuvoton/npcd378/superio.c
diff options
context:
space:
mode:
authorPatrick Rudolph <patrick.rudolph@9elements.com>2020-02-12 15:23:05 +0100
committerFelix Held <felix-coreboot@felixheld.de>2020-03-02 10:19:44 +0000
commite1498ce6da91c3e6bc61c67213f10ab927704510 (patch)
treec50a0b3d68878f0efd657dd0a503c36a2fc27e63 /src/superio/nuvoton/npcd378/superio.c
parent56626cf5d88523ed274c27e9624de7a358efe835 (diff)
superio/nuvoton/npcd378: Switch to superio/common
Replace DSDT ACPI code and DSDT injection with a SSDT only solution. The current implementation shows some issues on current Linux, which might be due to external ACPI objects, which are then injected into DSDT or the fact that those objects only use 3 characters. Replace all the DSDT code with an SSDT generator. Tested on HP Z220: Boots into Linux with no ACPI errors. The SSDT can be disassembled. Change-Id: I41616d9bf320fd2b4d8495892b8190cd2a2d057f Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/38862 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
Diffstat (limited to 'src/superio/nuvoton/npcd378/superio.c')
-rw-r--r--src/superio/nuvoton/npcd378/superio.c373
1 files changed, 332 insertions, 41 deletions
diff --git a/src/superio/nuvoton/npcd378/superio.c b/src/superio/nuvoton/npcd378/superio.c
index 624c1f0882..a07afdc79c 100644
--- a/src/superio/nuvoton/npcd378/superio.c
+++ b/src/superio/nuvoton/npcd378/superio.c
@@ -10,6 +10,8 @@
#include <superio/conf_mode.h>
#include <arch/acpi.h>
#include <arch/acpigen.h>
+#include <superio/common/ssdt.h>
+#include <stdlib.h>
#include "npcd378.h"
@@ -87,62 +89,351 @@ static void npcd378_init(struct device *dev)
}
#if CONFIG(HAVE_ACPI_TABLES)
-static void npcd378_ssdt(struct device *dev)
+/* Provide ACPI HIDs for generic Super I/O SSDT */
+static const char *npcd378_acpi_hid(const struct device *dev)
{
- struct resource *res;
+ /* Sanity checks */
+ if (dev->path.type != DEVICE_PATH_PNP)
+ return NULL;
+ if (dev->path.pnp.port == 0)
+ return NULL;
+ if ((dev->path.pnp.device & 0xff) > NPCD378_GPIOA)
+ return NULL;
- const char *scope = acpi_device_path(dev);
- if (!scope) {
- printk(BIOS_ERR, "%s: Missing ACPI scope\n", dev_path(dev));
- return;
+ switch (dev->path.pnp.device & 0xff) {
+ case NPCD378_FDC:
+ return ACPI_HID_FDC;
+ case NPCD378_PP:
+ return ACPI_HID_LPT;
+ case NPCD378_SP1: /* fallthrough */
+ case NPCD378_SP2:
+ return ACPI_HID_COM;
+ case NPCD378_AUX:
+ return ACPI_HID_MOUSE;
+ case NPCD378_KBC:
+ return ACPI_HID_KEYBOARD;
+ default:
+ return ACPI_HID_PNP;
}
+}
- switch (dev->path.pnp.device) {
- case NPCD378_PWR: {
- res = find_resource(dev, PNP_IDX_IO0);
- if (!res || !res->base) {
- printk(BIOS_ERR, "NPCD378: LDN%u IOBASE not set.\n",
- NPCD378_PWR);
- break;
- }
+static void npcd378_ssdt_aux(struct device *dev)
+{
+ /* Scope */
+ acpigen_write_scope(acpi_device_path(dev));
- acpigen_write_scope(scope);
- acpigen_write_name_integer("SWB", res->base);
- acpigen_write_name_integer("SWL", res->size);
- acpigen_pop_len(); /* pop scope */
+ acpigen_write_method("_PSW", 1);
+ acpigen_write_store();
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_emit_namestring("^^MSFG");
+ acpigen_pop_len(); /* Pop Method */
- res = find_resource(dev, PNP_IDX_IO1);
- if (!res || !res->base) {
- printk(BIOS_ERR, "NPCD378: LDN%u IOBASE2 not set.\n",
- NPCD378_PWR);
- break;
- }
+ acpigen_write_PRW(8, 3);
- acpigen_write_scope(scope);
- acpigen_write_name_integer("RNB", res->base);
- acpigen_write_name_integer("RNL", res->size);
- acpigen_pop_len(); /* pop scope */
- break;
- }
- }
+ acpigen_pop_len(); /* Pop Scope */
+}
+
+static void npcd378_ssdt_kbc(struct device *dev)
+{
+ /* Scope */
+ acpigen_write_scope(acpi_device_path(dev));
+
+ acpigen_write_method("_PSW", 1);
+ acpigen_write_store();
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_emit_namestring("^^KBFG");
+ acpigen_pop_len(); /* Pop Method */
+
+ acpigen_write_PRW(8, 3);
+
+ acpigen_pop_len(); /* Pop Scope */
+}
+
+static void npcd378_ssdt_pwr(struct device *dev)
+{
+ const char *name = acpi_device_path(dev);
+ const char *scope = acpi_device_scope(dev);
+ char *tmp_name;
+
+ /* Scope */
+ acpigen_write_scope(name);
+
+ acpigen_emit_ext_op(OPREGION_OP);
+ acpigen_emit_namestring("SWCR");
+ acpigen_emit_byte(SYSTEMIO);
+ acpigen_emit_namestring("IO0B");
+ acpigen_emit_namestring("IO0S");
+
+ struct fieldlist l1[] = {
+ FIELDLIST_OFFSET(0),
+ FIELDLIST_NAMESTR("LEDC", 8),
+ FIELDLIST_NAMESTR("SWCC", 8),
+ };
+
+ acpigen_write_field("SWCR", l1, ARRAY_SIZE(l1), FIELD_BYTEACC |
+ FIELD_NOLOCK | FIELD_PRESERVE);
+
+ acpigen_emit_ext_op(OPREGION_OP);
+ acpigen_emit_namestring("RNTR");
+ acpigen_emit_byte(SYSTEMIO);
+ acpigen_emit_namestring("IO1B");
+ acpigen_emit_namestring("IO1S");
+
+ struct fieldlist l2[] = {
+ FIELDLIST_OFFSET(0),
+ FIELDLIST_NAMESTR("GPES", 8),
+ FIELDLIST_NAMESTR("GPEE", 8),
+ FIELDLIST_OFFSET(8),
+ FIELDLIST_NAMESTR("GPS0", 8),
+ FIELDLIST_NAMESTR("GPS1", 8),
+ FIELDLIST_NAMESTR("GPS2", 8),
+ FIELDLIST_NAMESTR("GPS3", 8),
+ FIELDLIST_NAMESTR("GPE0", 8),
+ FIELDLIST_NAMESTR("GPE1", 8),
+ FIELDLIST_NAMESTR("GPE2", 8),
+ FIELDLIST_NAMESTR("GPE3", 8),
+ };
+
+ acpigen_write_field("RNTR", l2, ARRAY_SIZE(l2), FIELD_BYTEACC |
+ FIELD_NOLOCK | FIELD_PRESERVE);
+
+ /* Method (SIOW, 1, NotSerialized) */
+ acpigen_write_method("SIOW", 1);
+ acpigen_write_store();
+ acpigen_emit_namestring("^GPS2");
+ acpigen_emit_namestring("^^PMFG");
+
+ acpigen_write_store();
+ acpigen_emit_byte(ZERO_OP);
+ acpigen_emit_namestring("^GPEE");
+
+ acpigen_write_store();
+ acpigen_emit_byte(ZERO_OP);
+ acpigen_emit_namestring("^GPE0");
+
+ acpigen_write_store();
+ acpigen_emit_byte(ZERO_OP);
+ acpigen_emit_namestring("^GPE1");
+
+ acpigen_emit_byte(AND_OP);
+ acpigen_emit_namestring("^LEDC");
+ acpigen_write_integer(0xE0);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ acpigen_emit_byte(OR_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_integer(0x1E);
+ acpigen_emit_namestring("^LEDC");
+
+ acpigen_emit_byte(AND_OP);
+ acpigen_emit_namestring("^SWCC");
+ acpigen_write_integer(0xBF);
+ acpigen_emit_namestring("^SWCC");
+
+ acpigen_pop_len(); /* SIOW method */
+
+ /* Method (SIOS, 1, NotSerialized) */
+ acpigen_write_method("SIOS", 1);
+
+ acpigen_write_if();
+ acpigen_emit_byte(LNOT_OP);
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_write_integer(5);
+
+ acpigen_write_if();
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_namestring("^^KBFG");
+ acpigen_emit_byte(ONE_OP);
+
+ acpigen_emit_byte(OR_OP);
+ acpigen_emit_namestring("^GPE2");
+ acpigen_write_integer(0xE8);
+ acpigen_emit_namestring("^GPE2");
+
+ acpigen_pop_len(); /* Pop If */
+ acpigen_write_else();
+
+ acpigen_emit_byte(AND_OP);
+ acpigen_emit_namestring("^GPE2");
+ acpigen_write_integer(0x17);
+ acpigen_emit_namestring("^GPE2");
+
+ acpigen_pop_len(); /* Pop Else */
+
+ acpigen_write_if();
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_namestring("^^MSFG");
+ acpigen_emit_byte(ONE_OP);
+
+ acpigen_emit_byte(OR_OP);
+ acpigen_emit_namestring("^GPE2");
+ acpigen_write_integer(0x10);
+ acpigen_emit_namestring("^GPE2");
+
+ acpigen_pop_len(); /* Pop If */
+ acpigen_write_else();
+
+ acpigen_emit_byte(AND_OP);
+ acpigen_emit_namestring("^GPE2");
+ acpigen_write_integer(0xEF);
+ acpigen_emit_namestring("^GPE2");
+
+ acpigen_pop_len(); /* Pop Else */
+
+ /* Enable wake on GPE */
+ acpigen_write_store();
+ acpigen_emit_byte(ONE_OP);
+ acpigen_emit_namestring("^GPEE");
+
+ acpigen_write_if();
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_write_integer(3);
+
+ acpigen_emit_byte(AND_OP);
+ acpigen_emit_namestring("^LEDC");
+ acpigen_write_integer(0xE0);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ acpigen_emit_byte(OR_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_integer(0x1C);
+ acpigen_emit_namestring("^LEDC");
+
+ acpigen_emit_byte(AND_OP);
+ acpigen_emit_namestring("^SWCC");
+ acpigen_write_integer(0xBF);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ acpigen_emit_byte(OR_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_integer(0x40);
+ acpigen_emit_namestring("^SWCC");
+
+ acpigen_pop_len(); /* Pop If */
+
+ acpigen_pop_len(); /* Pop If */
+
+ acpigen_write_store();
+ acpigen_write_integer(0x10);
+ acpigen_emit_namestring("^GPE0");
+
+ acpigen_write_store();
+ acpigen_write_integer(0x20);
+ acpigen_emit_namestring("^GPE1");
+
+ acpigen_pop_len(); /* Pop SIOS method */
+
+ acpigen_pop_len(); /* Pop Scope */
+
+ /* Inject into parent: */
+ acpigen_write_scope(acpi_device_scope(dev));
+
+ acpigen_write_name_integer("MSFG", 1);
+ acpigen_write_name_integer("KBFG", 1);
+ acpigen_write_name_integer("PMFG", 0);
+
+ /* DSDT must call SIOW on _WAK */
+ /* Method (SIOW, 1, NotSerialized) */
+ acpigen_write_method("SIOW", 1);
+ acpigen_emit_byte(RETURN_OP);
+ tmp_name = strconcat(name, ".SIOW");
+ acpigen_emit_namestring(tmp_name);
+ free(tmp_name);
+
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_pop_len();
+
+ /* DSDT must call SIOS on _PTS */
+ /* Method (SIOS, 1, NotSerialized) */
+ acpigen_write_method("SIOS", 1);
+ acpigen_emit_byte(RETURN_OP);
+ tmp_name = strconcat(name, ".SIOS");
+ acpigen_emit_namestring(tmp_name);
+ free(tmp_name);
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_pop_len(); /* Pop Method */
+
+ acpigen_pop_len(); /* Scope */
+
+ acpigen_write_scope("\\_GPE");
+
+ /* Method (SIOH, 0, NotSerialized) */
+ acpigen_write_method("_L08", 0);
+ acpigen_emit_byte(AND_OP);
+ tmp_name = strconcat(scope, ".PMFG");
+ acpigen_emit_namestring(tmp_name);
+ free(tmp_name);
+ acpigen_write_integer(0xE8);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ acpigen_write_if();
+ acpigen_emit_byte(LGREATER_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_emit_byte(ZERO_OP);
+
+ acpigen_emit_byte(NOTIFY_OP);
+ tmp_name = strconcat(scope, ".L060");
+ acpigen_emit_namestring(tmp_name);
+ free(tmp_name);
+ acpigen_write_integer(2);
+
+ acpigen_pop_len(); /* Pop If */
+
+ acpigen_emit_byte(AND_OP);
+ tmp_name = strconcat(scope, ".PMFG");
+ acpigen_emit_namestring(tmp_name);
+ free(tmp_name);
+ acpigen_write_integer(0x10);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ acpigen_write_if();
+ acpigen_emit_byte(LGREATER_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_emit_byte(ZERO_OP);
+
+ acpigen_emit_byte(NOTIFY_OP);
+ tmp_name = strconcat(scope, ".L050");
+ acpigen_emit_namestring(tmp_name);
+ free(tmp_name);
+ acpigen_write_integer(2);
+ acpigen_pop_len(); /* Pop If */
+
+ acpigen_pop_len(); /* Pop Method */
+
+ acpigen_pop_len(); /* Scope */
}
-static const char *npcd378_acpi_name(const struct device *dev)
+static void npcd378_fill_ssdt_generator(struct device *dev)
{
- return "SIO0";
+ superio_common_fill_ssdt_generator(dev);
+
+ switch (dev->path.pnp.device) {
+ case NPCD378_PWR:
+ npcd378_ssdt_pwr(dev);
+ break;
+ case NPCD378_AUX:
+ npcd378_ssdt_aux(dev);
+ break;
+ case NPCD378_KBC:
+ npcd378_ssdt_kbc(dev);
+ break;
+ }
}
#endif
static struct device_operations ops = {
- .read_resources = pnp_read_resources,
- .set_resources = pnp_set_resources,
- .enable_resources = pnp_enable_resources,
- .enable = pnp_alt_enable,
- .init = npcd378_init,
- .ops_pnp_mode = &pnp_conf_mode_8787_aa,
+ .read_resources = pnp_read_resources,
+ .set_resources = pnp_set_resources,
+ .enable_resources = pnp_enable_resources,
+ .enable = pnp_alt_enable,
+ .init = npcd378_init,
+ .ops_pnp_mode = &pnp_conf_mode_8787_aa,
#if CONFIG(HAVE_ACPI_TABLES)
- .acpi_fill_ssdt_generator = npcd378_ssdt,
- .acpi_name = npcd378_acpi_name,
+ .acpi_fill_ssdt_generator = npcd378_fill_ssdt_generator,
+ .acpi_name = superio_common_ldn_acpi_name,
+ .acpi_hid = npcd378_acpi_hid,
#endif
};