Patrick Rudolph has uploaded this change for review.

View Change

[WIP] ssdtgen for PNP devices

Automatically create ACPI code for PNP devices.

TODO: Add HID to devicetree.

Change-Id: I2716ae0580d68e5d4fcc484cb1648a2cdc1f4ca0
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
---
A src/superio/common/chip.h
A src/superio/common/ssdt.c
A src/superio/common/ssdt.h
3 files changed, 386 insertions(+), 0 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/33/33033/1
diff --git a/src/superio/common/chip.h b/src/superio/common/chip.h
new file mode 100644
index 0000000..2d75c0f
--- /dev/null
+++ b/src/superio/common/chip.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2019 9elements Agency GmbH <patrick.rudolph@9elements.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SUPERIO_COMMON_CHIP_H__
+#define __SUPERIO_COMMON_CHIP_H__
+
+struct common_superio_ldn_config {
+ const char *acpi_name;
+ const char *dos_device_name;
+ const char *acpi_hid;
+};
+
+#endif /* __SUPERIO_COMMON_CHIP_H__ */
diff --git a/src/superio/common/ssdt.c b/src/superio/common/ssdt.c
new file mode 100644
index 0000000..0e2c59f
--- /dev/null
+++ b/src/superio/common/ssdt.c
@@ -0,0 +1,334 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2019 9elements Agency GmbH <patrick.rudolph@9elements.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <superio/common/chip.h>
+#include <superio/common/ssdt.h>
+
+#include <device/device.h>
+#include <device/pnp.h>
+#include <arch/acpigen.h>
+#include <device/pnp_def.h>
+#include <console/console.h>
+
+#define NUM_IO 4
+#define NUM_IRQ 2
+
+struct superio_dev {
+ const char *acpi_hid;
+ u16 io_base[NUM_IO];
+ u8 irq[NUM_IRQ];
+};
+
+static const struct superio_dev superio_devs[] = {
+ {"PNP0700", {0x3f0, 0x3f2, 0x3f7}, {6, }},
+ {"PNP0303", {60, 64, }, {1, }},
+ {"PNP0F03", {60, 64, }, {12, }},
+ {"PNP0501", {0x3f8, 0x2f8, 0x3e8, 0x2e8}, {4, 3}},
+ {"PNP0400", {0x378, }, {7, }},
+};
+
+static const u8 io_idx[NUM_IO] = {PNP_IDX_IO0, PNP_IDX_IO1, PNP_IDX_IO2,
+ PNP_IDX_IO3};
+static const u8 irq_idx[NUM_IRQ] = {PNP_IDX_IRQ0, PNP_IDX_IRQ1};
+
+
+static void superio_base_fill_ssdt_generator(struct device *dev)
+{
+ const char *scope = acpi_device_scope(dev);
+ const char *name = acpi_device_name(dev);
+
+ if (!scope || !name) {
+ printk(BIOS_ERR, "%s: Missing ACPI path/scope\n", dev_path(dev));
+ return;
+ }
+
+ /* Device */
+ acpigen_write_scope(scope);
+ acpigen_write_device(name);
+
+ printk(BIOS_DEBUG, "%s.%s: %s\n", scope, name, dev_path(dev));
+
+ acpigen_write_name_string("_HID", "PNP0C02");
+ acpigen_write_name_string("_DDN", dev_name(dev));
+
+ /* OperationRegion("IOID", SYSTEMIO, port, 2) */
+ struct opregion opreg = OPREGION("IOID", SYSTEMIO,
+ dev->path.pnp.port, 2);
+ acpigen_write_opregion(&opreg);
+
+ struct fieldlist l[] = {
+ FIELDLIST_OFFSET(0),
+ FIELDLIST_NAMESTR("INDX", 8),
+ FIELDLIST_NAMESTR("DATA", 8),
+ };
+
+ /* Field (IOID, AnyAcc, NoLock, Preserve)
+ * {
+ * Offset (0),
+ * INDX, 8,
+ * DATA, 8,
+ * } */
+ acpigen_write_field(opreg.name, l, ARRAY_SIZE(l), FIELD_BYTEACC |
+ FIELD_NOLOCK | FIELD_PRESERVE);
+
+ struct fieldlist i[] = {
+ FIELDLIST_OFFSET(0x07),
+ FIELDLIST_NAMESTR("LDN", 8),
+ FIELDLIST_OFFSET(0x21),
+ FIELDLIST_NAMESTR("SCF1", 8),
+ FIELDLIST_NAMESTR("SCF2", 8),
+ FIELDLIST_NAMESTR("SCF3", 8),
+ FIELDLIST_NAMESTR("SCF4", 8),
+ FIELDLIST_NAMESTR("SCF5", 8),
+ FIELDLIST_NAMESTR("SCF6", 8),
+ FIELDLIST_NAMESTR("SCF7", 8),
+ FIELDLIST_OFFSET(0x29),
+ FIELDLIST_NAMESTR("CKCF", 8),
+ FIELDLIST_OFFSET(0x2F),
+ FIELDLIST_NAMESTR("SCFF", 8),
+ FIELDLIST_NAMESTR("ACTR", 8),
+ FIELDLIST_OFFSET(0x60),
+ FIELDLIST_NAMESTR("IOAH", 8),
+ FIELDLIST_NAMESTR("IOAL", 8),
+ FIELDLIST_NAMESTR("IOH2", 8),
+ FIELDLIST_NAMESTR("IOL2", 8),
+ FIELDLIST_OFFSET(0x70),
+ FIELDLIST_NAMESTR("INTR", 4),
+ FIELDLIST_NAMESTR("INTT", 4),
+ FIELDLIST_OFFSET (0x74),
+ FIELDLIST_NAMESTR("DMCH", 8),
+ FIELDLIST_OFFSET(0xE0),
+ FIELDLIST_NAMESTR("RGE0", 8),
+ FIELDLIST_NAMESTR("RGE1", 8),
+ FIELDLIST_NAMESTR("RGE2", 8),
+ FIELDLIST_NAMESTR("RGE3", 8),
+ FIELDLIST_NAMESTR("RGE4", 8),
+ FIELDLIST_NAMESTR("RGE5", 8),
+ FIELDLIST_NAMESTR("RGE6", 8),
+ FIELDLIST_NAMESTR("RGE7", 8),
+ FIELDLIST_NAMESTR("RGE8", 8),
+ FIELDLIST_NAMESTR("RGE9", 8),
+ FIELDLIST_NAMESTR("RGEA", 8),
+ FIELDLIST_OFFSET(0xF0),
+ FIELDLIST_NAMESTR("OPT0", 8),
+ FIELDLIST_NAMESTR("OPT1", 8),
+ FIELDLIST_NAMESTR("OPT2", 8),
+ FIELDLIST_NAMESTR("OPT3", 8),
+ FIELDLIST_NAMESTR("OPT4", 8),
+ FIELDLIST_NAMESTR("OPT5", 8),
+ FIELDLIST_NAMESTR("OPT6", 8),
+ FIELDLIST_NAMESTR("OPT7", 8),
+ FIELDLIST_NAMESTR("OPT8", 8),
+ FIELDLIST_NAMESTR("OPT9", 8),
+ };
+
+ acpigen_write_indexfield("INDX", "DATA", i, ARRAY_SIZE(i),
+ FIELD_BYTEACC | FIELD_NOLOCK | FIELD_PRESERVE);
+
+ acpigen_pop_len(); /* Device */
+ acpigen_pop_len(); /* Scope */
+}
+
+static const char *superio_guess_hid(struct device *dev)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
+ struct resource *res = probe_resource(dev, io_idx[i]);
+ if (!res || !res->base)
+ continue;
+
+ for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
+ for (size_t k = 0; k < NUM_IO; k++) {
+ if (!superio_devs[j].io_base[k])
+ continue;
+ if (superio_devs[j].io_base[k] == res->base)
+ return superio_devs[j].acpi_hid;
+ }
+ }
+ }
+ for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
+ struct resource *res = probe_resource(dev, irq_idx[i]);
+ if (!res || !res->size)
+ continue;
+ for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
+ for (size_t k = 0; k < NUM_IRQ; k++) {
+ if (!superio_devs[j].irq[k])
+ continue;
+ if (superio_devs[j].irq[k] == res->base)
+ return superio_devs[j].acpi_hid;
+ }
+ }
+ }
+ return NULL;
+}
+
+static int ldn_resource_cnt(struct device *dev)
+{
+ int cnt = 0;
+ for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
+ struct resource *res = probe_resource(dev, io_idx[i]);
+ if (!res || !res->base)
+ continue;
+ cnt++;
+ }
+ for (size_t i = 0; i < NUM_IRQ; i++) {
+ struct resource *res = probe_resource(dev, irq_idx[i]);
+ if (!res || !res->size)
+ continue;
+ cnt++;
+ }
+ return cnt;
+}
+
+/* Add IO and IRQ resources for _CRS or _PRS */
+static void ldn_gen_resources(struct device *dev)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
+ struct resource *res = probe_resource(dev, io_idx[i]);
+ if (!res || !res->base)
+ continue;
+ resource_t base = res->base;
+ resource_t size = res->size;
+ while (size > 0) {
+ resource_t sz = size > 255 ? 255 : size;
+ acpigen_write_io16(base, base, 0, sz, 1);
+ size -= sz;
+ base += sz;
+ }
+ }
+ for (size_t i = 0; i < NUM_IRQ; i++) {
+ struct resource *res = probe_resource(dev, irq_idx[i]);
+ if (!res || !res->size)
+ continue;
+ acpigen_write_irq(res->base);
+ }
+}
+
+/* Add resource base and size for additional SuperIO code */
+static void ldn_gen_resources_use(struct device *dev)
+{
+ char name[5];
+ for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
+ struct resource *res = probe_resource(dev, io_idx[i]);
+ if (!res || !res->base || !res->size)
+ continue;
+
+ snprintf(name, sizeof(name), "IO%XB", i);
+ name[4] = '\0';
+ acpigen_write_name_integer(name, res->base);
+
+ snprintf(name, sizeof(name), "IO%XS", i);
+ name[4] = '\0';
+ acpigen_write_name_integer(name, res->size);
+ }
+}
+
+static const char *ldn_acpi_name(const struct device *dev)
+{
+ u8 ldn = dev->path.pnp.device & 0xff;
+ u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
+ static char name[5];
+
+ if (!vldn)
+ snprintf(name, sizeof(name), "LD%02X", ldn);
+ else
+ snprintf(name, sizeof(name), "VLD%X", vldn);
+
+ name[4] = '\0';
+
+ return name;
+}
+
+
+void superio_common_fill_ssdt_generator(struct device *dev)
+{
+ struct common_superio_ldn_config *config = dev->chip_info;
+ const char *scope = acpi_device_path(dev);
+ const char *name = ldn_acpi_name(dev);
+ const char *hid = NULL;
+ const u8 ldn = dev->path.pnp.device & 0xff;
+ const u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
+ bool has_resources;
+
+ if (!scope || !name) {
+ printk(BIOS_ERR, "%s: Missing ACPI path/scope\n", dev_path(dev));
+ return;
+ }
+ if (vldn) {
+ printk(BIOS_DEBUG, "%s: Ignoring virtual LDN\n", dev_path(dev));
+ return;
+ }
+
+ if (ldn == 0)
+ superio_base_fill_ssdt_generator(dev);
+
+ printk(BIOS_DEBUG, "%s.%s: %s\n", scope, name, dev_path(dev));
+
+ /* Scope */
+ acpigen_write_scope(scope);
+
+ /* Device */
+ acpigen_write_device(name);
+
+ if (config && config->dos_device_name)
+ acpigen_write_name_string("_DDN", config->dos_device_name);
+
+ acpigen_write_name_byte("_UID", 0);
+ acpigen_write_name_byte("LDN", ldn);
+
+ acpigen_write_STA(dev->enabled ? 0xf : 0);
+
+ if (!dev->enabled) {
+ acpigen_pop_len(); /* Device */
+ acpigen_pop_len(); /* Scope */
+ return;
+ }
+
+ has_resources = ldn_resource_cnt(dev);
+
+ /* Resources - _CRS */
+ if (has_resources) {
+ acpigen_write_name("_CRS");
+ acpigen_write_resourcetemplate_header();
+ ldn_gen_resources(dev);
+ acpigen_write_resourcetemplate_footer();
+
+ /* Resources - _PRS */
+ acpigen_write_name("_PRS");
+ acpigen_write_resourcetemplate_header();
+ ldn_gen_resources(dev);
+ acpigen_write_resourcetemplate_footer();
+
+ /* Resources base and size for 3rd party ACPI code */
+ ldn_gen_resources_use(dev);
+ }
+
+ if (config && config->acpi_hid) {
+ hid = config->acpi_hid;
+ } else {
+ printk(BIOS_WARNING, "%s: No HID set in devicetree\n",
+ dev_path(dev));
+
+ hid = superio_guess_hid(dev);
+ }
+
+ if (hid)
+ acpigen_write_name_string("_HID", hid);
+ else
+ acpigen_write_name_string("_HID", "PNP0C02");
+
+ acpigen_pop_len(); /* Device */
+ acpigen_pop_len(); /* Scope */
+}
diff --git a/src/superio/common/ssdt.h b/src/superio/common/ssdt.h
new file mode 100644
index 0000000..4d4e4b1
--- /dev/null
+++ b/src/superio/common/ssdt.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2019 9elements Agency GmbH <patrick.rudolph@9elements.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SUPERIO_COMMON_SSDT_H__
+#define __SUPERIO_COMMON_SSDT_H__
+
+#include <device/device.h>
+
+void superio_common_fill_ssdt_generator(struct device *dev);
+const char *superio_common_acpi_name(const struct device *dev);
+void superio_set_base_dev_ops(struct device *dev);
+
+#endif /* __SUPERIO_COMMON_SSDT_H__ */

To view, visit change 33033. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I2716ae0580d68e5d4fcc484cb1648a2cdc1f4ca0
Gerrit-Change-Number: 33033
Gerrit-PatchSet: 1
Gerrit-Owner: Patrick Rudolph <patrick.rudolph@9elements.com>
Gerrit-MessageType: newchange