Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/33033
Change subject: [WIP] ssdtgen for PNP devices ......................................................................
[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__ */