From 23dd0ea4c7edbde5bc05a1567ba0bf41734ea524 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:44:59 -0600 Subject: dm: acpi: Use correct GPIO polarity type in acpi_dp_add_gpio() This function currently accepts the IRQ-polarity type. Fix it to use the GPIO type instead. Signed-off-by: Simon Glass --- drivers/sound/max98357a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/sound/max98357a.c b/drivers/sound/max98357a.c index 841bc6ef682..827262d235c 100644 --- a/drivers/sound/max98357a.c +++ b/drivers/sound/max98357a.c @@ -81,7 +81,7 @@ static int max98357a_acpi_fill_ssdt(const struct udevice *dev, dp = acpi_dp_new_table("_DSD"); acpi_dp_add_gpio(dp, "sdmode-gpio", path, 0, 0, priv->sdmode_gpio.flags & GPIOD_ACTIVE_LOW ? - ACPI_IRQ_ACTIVE_LOW : ACPI_IRQ_ACTIVE_HIGH); + ACPI_GPIO_ACTIVE_LOW : ACPI_GPIO_ACTIVE_HIGH); acpi_dp_add_integer(dp, "sdmode-delay", dev_read_u32_default(dev, "sdmode-delay", 0)); acpi_dp_write(ctx, dp); -- cgit v1.3.1 From fd42f263cef9fd6bba9ded76a82d4ac66450e156 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:45:01 -0600 Subject: i2c: Add a generic driver to generate ACPI info Many I2C devices produce roughly the same ACPI data with just things like the GPIO/interrupt information being different. This can be handled by a generic driver along with some information in the device tree. Add a generic i2c driver for this purpose. Signed-off-by: Simon Glass Reviewed-by: Heiko Schocher --- doc/device-tree-bindings/i2c/generic-acpi.txt | 42 +++++ drivers/i2c/Makefile | 3 + drivers/i2c/acpi_i2c.c | 226 ++++++++++++++++++++++++++ drivers/i2c/acpi_i2c.h | 15 ++ drivers/i2c/i2c-uclass.c | 17 ++ include/acpi/acpi_device.h | 55 +++++++ include/i2c.h | 23 +++ 7 files changed, 381 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/generic-acpi.txt create mode 100644 drivers/i2c/acpi_i2c.c create mode 100644 drivers/i2c/acpi_i2c.h (limited to 'drivers') diff --git a/doc/device-tree-bindings/i2c/generic-acpi.txt b/doc/device-tree-bindings/i2c/generic-acpi.txt new file mode 100644 index 00000000000..3510a71b570 --- /dev/null +++ b/doc/device-tree-bindings/i2c/generic-acpi.txt @@ -0,0 +1,42 @@ +I2C generic device +================== + +This is used only to generate ACPI tables for an I2C device. + +Required properties : + + - compatible : "i2c-chip"; + - reg : I2C chip address + - acpi,hid : HID name for the device + +Optional properies in addition to device.txt: + + - reset-gpios : GPIO used to assert reset to the device + - irq-gpios : GPIO used for interrupt (if Interrupt is not used) + - stop-gpios : GPIO used to stop the device + - interrupts-extended : Interrupt to use for the device + - reset-delay-ms : Delay after de-asserting reset, in ms + - reset-off-delay-ms : Delay after asserting reset (during power off) + - enable-delay-ms : Delay after asserting enable + - enable-off-delay-ms : Delay after de-asserting enable (during power off) + - stop-delay-ms : Delay after de-aserting stop + - stop-off-delay-ms : Delay after asserting stop (during power off) + - hid-descr-addr : HID register offset (for Human Interface Devices) + +Example +------- + + elan-touchscreen@10 { + compatible = "i2c-chip"; + reg = <0x10>; + acpi,hid = "ELAN0001"; + acpi,ddn = "ELAN Touchscreen"; + interrupts-extended = <&acpi_gpe GPIO_21_IRQ + IRQ_TYPE_EDGE_FALLING>; + linux,probed; + reset-gpios = <&gpio_n GPIO_36 GPIO_ACTIVE_HIGH>; + reset-delay-ms = <20>; + enable-gpios = <&gpio_n GPIO_152 GPIO_ACTIVE_HIGH>; + enable-delay-ms = <1>; + acpi,has-power-resource; + }; diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f7b27864488..bd248cbf52b 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -3,6 +3,9 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. obj-$(CONFIG_DM_I2C) += i2c-uclass.o +ifdef CONFIG_ACPIGEN +obj-$(CONFIG_DM_I2C) += acpi_i2c.o +endif obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_$(SPL_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o obj-$(CONFIG_$(SPL_)I2C_CROS_EC_LDO) += cros_ec_ldo.o diff --git a/drivers/i2c/acpi_i2c.c b/drivers/i2c/acpi_i2c.c new file mode 100644 index 00000000000..57d29683cbf --- /dev/null +++ b/drivers/i2c/acpi_i2c.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_X86 +#include +#endif +#include +#include + +static bool acpi_i2c_add_gpios_to_crs(struct acpi_i2c_priv *priv) +{ + /* + * Return false if: + * 1. Request to explicitly disable export of GPIOs in CRS, or + * 2. Both reset and enable GPIOs are not provided. + */ + if (priv->disable_gpio_export_in_crs || + (!dm_gpio_is_valid(&priv->reset_gpio) && + !dm_gpio_is_valid(&priv->enable_gpio))) + return false; + + return true; +} + +static int acpi_i2c_write_gpio(struct acpi_ctx *ctx, struct gpio_desc *gpio, + int *curindex) +{ + int ret; + + if (!dm_gpio_is_valid(gpio)) + return -ENOENT; + + acpi_device_write_gpio_desc(ctx, gpio); + ret = *curindex; + (*curindex)++; + + return ret; +} + +int acpi_i2c_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + int reset_gpio_index = -1, enable_gpio_index = -1, irq_gpio_index = -1; + enum i2c_device_t type = dev_get_driver_data(dev); + struct acpi_i2c_priv *priv = dev_get_priv(dev); + struct acpi_dp *dsd = NULL; + char scope[ACPI_PATH_MAX]; + char name[ACPI_NAME_MAX]; + int tx_state_val; + int curindex = 0; + int ret; + +#ifdef CONFIG_X86 + tx_state_val = PAD_CFG0_TX_STATE; +#elif defined(CONFIG_SANDBOX) + tx_state_val = BIT(7); /* test value */ +#else +#error "Not supported on this architecture" +#endif + ret = acpi_get_name(dev, name); + if (ret) + return log_msg_ret("name", ret); + ret = acpi_device_scope(dev, scope, sizeof(scope)); + if (ret) + return log_msg_ret("scope", ret); + + /* Device */ + acpigen_write_scope(ctx, scope); + acpigen_write_device(ctx, name); + acpigen_write_name_string(ctx, "_HID", priv->hid); + if (type == I2C_DEVICE_HID_OVER_I2C) + acpigen_write_name_string(ctx, "_CID", "PNP0C50"); + acpigen_write_name_integer(ctx, "_UID", priv->uid); + acpigen_write_name_string(ctx, "_DDN", priv->desc); + acpigen_write_sta(ctx, acpi_device_status(dev)); + + /* Resources */ + acpigen_write_name(ctx, "_CRS"); + acpigen_write_resourcetemplate_header(ctx); + acpi_device_write_i2c_dev(ctx, dev); + + /* Use either Interrupt() or GpioInt() */ + if (dm_gpio_is_valid(&priv->irq_gpio)) { + irq_gpio_index = acpi_i2c_write_gpio(ctx, &priv->irq_gpio, + &curindex); + } else { + ret = acpi_device_write_interrupt_irq(ctx, &priv->irq); + if (ret < 0) + return log_msg_ret("irq", ret); + } + + if (acpi_i2c_add_gpios_to_crs(priv)) { + reset_gpio_index = acpi_i2c_write_gpio(ctx, &priv->reset_gpio, + &curindex); + enable_gpio_index = acpi_i2c_write_gpio(ctx, &priv->enable_gpio, + &curindex); + } + acpigen_write_resourcetemplate_footer(ctx); + + /* Wake capabilities */ + if (priv->wake) { + acpigen_write_name_integer(ctx, "_S0W", 4); + acpigen_write_prw(ctx, priv->wake, 3); + } + + /* DSD */ + if (priv->probed || priv->property_count || priv->compat_string || + reset_gpio_index >= 0 || enable_gpio_index >= 0 || + irq_gpio_index >= 0) { + char path[ACPI_PATH_MAX]; + + ret = acpi_device_path(dev, path, sizeof(path)); + if (ret) + return log_msg_ret("path", ret); + + dsd = acpi_dp_new_table("_DSD"); + if (priv->compat_string) + acpi_dp_add_string(dsd, "compatible", + priv->compat_string); + if (priv->probed) + acpi_dp_add_integer(dsd, "linux,probed", 1); + if (irq_gpio_index >= 0) + acpi_dp_add_gpio(dsd, "irq-gpios", path, + irq_gpio_index, 0, + priv->irq_gpio.flags & + GPIOD_ACTIVE_LOW ? + ACPI_GPIO_ACTIVE_LOW : 0); + if (reset_gpio_index >= 0) + acpi_dp_add_gpio(dsd, "reset-gpios", path, + reset_gpio_index, 0, + priv->reset_gpio.flags & + GPIOD_ACTIVE_LOW ? + ACPI_GPIO_ACTIVE_LOW : 0); + if (enable_gpio_index >= 0) + acpi_dp_add_gpio(dsd, "enable-gpios", path, + enable_gpio_index, 0, + priv->enable_gpio.flags & + GPIOD_ACTIVE_LOW ? + ACPI_GPIO_ACTIVE_LOW : 0); + /* Generic property list is not supported */ + acpi_dp_write(ctx, dsd); + } + + /* Power Resource */ + if (priv->has_power_resource) { + ret = acpi_device_add_power_res(ctx, tx_state_val, + "\\_SB.GPC0", "\\_SB.SPC0", + &priv->reset_gpio, priv->reset_delay_ms, + priv->reset_off_delay_ms, &priv->enable_gpio, + priv->enable_delay_ms, priv->enable_off_delay_ms, + &priv->stop_gpio, priv->stop_delay_ms, + priv->stop_off_delay_ms); + if (ret) + return log_msg_ret("power", ret); + } + if (priv->hid_desc_reg_offset) { + ret = acpi_device_write_dsm_i2c_hid(ctx, + priv->hid_desc_reg_offset); + if (ret) + return log_msg_ret("dsm", ret); + } + + acpigen_pop_len(ctx); /* Device */ + acpigen_pop_len(ctx); /* Scope */ + + return 0; +} + +int acpi_i2c_ofdata_to_platdata(struct udevice *dev) +{ + struct acpi_i2c_priv *priv = dev_get_priv(dev); + + gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset_gpio, + GPIOD_IS_OUT); + gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable_gpio, + GPIOD_IS_OUT); + gpio_request_by_name(dev, "irq-gpios", 0, &priv->irq_gpio, GPIOD_IS_IN); + gpio_request_by_name(dev, "stop-gpios", 0, &priv->stop_gpio, + GPIOD_IS_OUT); + irq_get_by_index(dev, 0, &priv->irq); + priv->hid = dev_read_string(dev, "acpi,hid"); + if (!priv->hid) + return log_msg_ret("hid", -EINVAL); + dev_read_u32(dev, "acpi,uid", &priv->uid); + priv->desc = dev_read_string(dev, "acpi,ddn"); + dev_read_u32(dev, "acpi,wake", &priv->wake); + priv->probed = dev_read_bool(dev, "linux,probed"); + priv->compat_string = dev_read_string(dev, "acpi,compatible"); + priv->has_power_resource = dev_read_bool(dev, + "acpi,has-power-resource"); + dev_read_u32(dev, "hid-descr-addr", &priv->hid_desc_reg_offset); + dev_read_u32(dev, "reset-delay-ms", &priv->reset_delay_ms); + dev_read_u32(dev, "reset-off-delay-ms", &priv->reset_off_delay_ms); + dev_read_u32(dev, "enable-delay-ms", &priv->enable_delay_ms); + dev_read_u32(dev, "enable-off-delay-ms", &priv->enable_off_delay_ms); + dev_read_u32(dev, "stop-delay-ms", &priv->stop_delay_ms); + dev_read_u32(dev, "stop-off-delay-ms", &priv->stop_off_delay_ms); + + return 0; +} + +/* Use name specified in priv or build one from I2C address */ +static int acpi_i2c_get_name(const struct udevice *dev, char *out_name) +{ + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); + struct acpi_i2c_priv *priv = dev_get_priv(dev); + + snprintf(out_name, ACPI_NAME_MAX, + priv->hid_desc_reg_offset ? "H%03X" : "D%03X", + chip->chip_addr); + + return 0; +} + +struct acpi_ops acpi_i2c_ops = { + .fill_ssdt = acpi_i2c_fill_ssdt, + .get_name = acpi_i2c_get_name, +}; diff --git a/drivers/i2c/acpi_i2c.h b/drivers/i2c/acpi_i2c.h new file mode 100644 index 00000000000..1f4be296016 --- /dev/null +++ b/drivers/i2c/acpi_i2c.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 Google LLC + */ + +#ifndef __ACPI_I2C_H +#define __ACPI_I2C_H + +#include + +extern struct acpi_ops acpi_i2c_ops; + +int acpi_i2c_ofdata_to_platdata(struct udevice *dev); + +#endif diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 2373aa2ea4c..5c4626b0442 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,6 +18,7 @@ #include #endif #include +#include "acpi_i2c.h" #define I2C_MAX_OFFSET_LEN 4 @@ -749,7 +752,21 @@ UCLASS_DRIVER(i2c_generic) = { .name = "i2c_generic", }; +static const struct udevice_id generic_chip_i2c_ids[] = { + { .compatible = "i2c-chip", .data = I2C_DEVICE_GENERIC }, +#if CONFIG_IS_ENABLED(ACPIGEN) + { .compatible = "hid-over-i2c", .data = I2C_DEVICE_HID_OVER_I2C }, +#endif + { } +}; + U_BOOT_DRIVER(i2c_generic_chip_drv) = { .name = "i2c_generic_chip_drv", .id = UCLASS_I2C_GENERIC, + .of_match = generic_chip_i2c_ids, +#if CONFIG_IS_ENABLED(ACPIGEN) + .ofdata_to_platdata = acpi_i2c_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct acpi_i2c_priv), +#endif + ACPI_OPS_PTR(&acpi_i2c_ops) }; diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index a5b12217820..1b838fcb857 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -10,7 +10,9 @@ #define __ACPI_DEVICE_H #include +#include #include +#include #include struct acpi_ctx; @@ -235,6 +237,59 @@ struct acpi_spi { const char *resource; }; +/** + * struct acpi_i2c_priv - Information read from device tree + * + * This is used by devices which want to specify various pieces of ACPI + * information, including power control. It allows a generic function to + * generate the information for ACPI, based on device-tree properties. + * + * @disable_gpio_export_in_crs: Don't export GPIOs in the CRS + * @reset_gpio: GPIO used to assert reset to the device + * @enable_gpio: GPIO used to enable the device + * @stop_gpio: GPIO used to stop the device + * @irq_gpio: GPIO used for interrupt (if @irq is not used) + * @irq: IRQ used for interrupt (if @irq_gpio is not used) + * @hid: _HID value for device (required) + * @uid: _UID value for device + * @desc: _DDN value for device + * @wake: Wake event, e.g. GPE0_DW1_15; 0 if none + * @property_count: Number of other DSD properties (currently always 0) + * @probed: true set set 'linux,probed' property + * @compat_string: Device tree compatible string to report through ACPI + * @has_power_resource: true if this device has a power resource + * @reset_delay_ms: Delay after de-asserting reset, in ms + * @reset_off_delay_ms: Delay after asserting reset (during power off) + * @enable_delay_ms: Delay after asserting enable + * @enable_off_delay_ms: Delay after de-asserting enable (during power off) + * @stop_delay_ms: Delay after de-aserting stop + * @stop_off_delay_ms: Delay after asserting stop (during power off) + * @hid_desc_reg_offset: HID register offset (for Human Interface Devices) + */ +struct acpi_i2c_priv { + bool disable_gpio_export_in_crs; + struct gpio_desc reset_gpio; + struct gpio_desc enable_gpio; + struct gpio_desc irq_gpio; + struct gpio_desc stop_gpio; + struct irq irq; + const char *hid; + u32 uid; + const char *desc; + u32 wake; + u32 property_count; + bool probed; + const char *compat_string; + bool has_power_resource; + u32 reset_delay_ms; + u32 reset_off_delay_ms; + u32 enable_delay_ms; + u32 enable_off_delay_ms; + u32 stop_delay_ms; + u32 stop_off_delay_ms; + u32 hid_desc_reg_offset; +}; + /** * acpi_device_path() - Get the full path to an ACPI device * diff --git a/include/i2c.h b/include/i2c.h index 1d792db454a..880aa8032b7 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -58,6 +58,12 @@ enum i2c_address_mode { I2C_MODE_10_BIT }; +/** enum i2c_device_t - Types of I2C devices, used for compatible strings */ +enum i2c_device_t { + I2C_DEVICE_GENERIC, + I2C_DEVICE_HID_OVER_I2C, +}; + struct udevice; /** * struct dm_i2c_chip - information about an i2c chip @@ -558,6 +564,23 @@ int i2c_emul_find(struct udevice *dev, struct udevice **emulp); */ struct udevice *i2c_emul_get_device(struct udevice *emul); +/* ACPI operations for generic I2C devices */ +extern struct acpi_ops i2c_acpi_ops; + +/** + * acpi_i2c_ofdata_to_platdata() - Read properties intended for ACPI + * + * This reads the generic I2C properties from the device tree, so that these + * can be used to create ACPI information for the device. + * + * See the i2c/generic-acpi.txt binding file for information about the + * properties. + * + * @dev: I2C device to process + * @return 0 if OK, -EINVAL if acpi,hid is not present + */ +int acpi_i2c_ofdata_to_platdata(struct udevice *dev); + #ifndef CONFIG_DM_I2C /* -- cgit v1.3.1 From 9b3e6d4c1f86b27f320ef9c5857a9ef223b2b356 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:45:14 -0600 Subject: x86: acpi: Add common Intel ACPI tables Add various tables that are common to Intel CPUs. These functions can be used by arch-specific CPU code. Signed-off-by: Simon Glass --- arch/x86/cpu/intel_common/Makefile | 2 + arch/x86/cpu/intel_common/acpi.c | 377 +++++++++++++++++++++++++++++++++++++ arch/x86/cpu/intel_common/cpu.c | 14 ++ arch/x86/include/asm/acpi_table.h | 22 +++ arch/x86/include/asm/cpu_common.h | 7 + arch/x86/include/asm/intel_acpi.h | 52 +++++ drivers/core/Kconfig | 9 + 7 files changed, 483 insertions(+) create mode 100644 arch/x86/cpu/intel_common/acpi.c create mode 100644 arch/x86/include/asm/intel_acpi.h (limited to 'drivers') diff --git a/arch/x86/cpu/intel_common/Makefile b/arch/x86/cpu/intel_common/Makefile index f1d1513a981..4a5cf17e41d 100644 --- a/arch/x86/cpu/intel_common/Makefile +++ b/arch/x86/cpu/intel_common/Makefile @@ -2,6 +2,8 @@ # # Copyright (c) 2016 Google, Inc +obj-$(CONFIG_INTEL_ACPIGEN) += acpi.o + ifdef CONFIG_HAVE_MRC obj-$(CONFIG_$(SPL_TPL_)X86_16BIT_INIT) += car.o obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += me_status.o diff --git a/arch/x86/cpu/intel_common/acpi.c b/arch/x86/cpu/intel_common/acpi.c new file mode 100644 index 00000000000..a4d5fbd38a7 --- /dev/null +++ b/arch/x86/cpu/intel_common/acpi.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generic Intel ACPI table generation + * + * Copyright (C) 2017 Intel Corp. + * Copyright 2019 Google LLC + * + * Modified from coreboot src/soc/intel/common/block/acpi.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +u32 acpi_fill_mcfg(u32 current) +{ + /* PCI Segment Group 0, Start Bus Number 0, End Bus Number is 255 */ + current += acpi_create_mcfg_mmconfig((void *)current, + CONFIG_MMCONF_BASE_ADDRESS, 0, 0, + (CONFIG_SA_PCIEX_LENGTH >> 20) + - 1); + return current; +} + +static int acpi_sci_irq(void) +{ + int sci_irq = 9; + uint scis; + int ret; + + ret = arch_read_sci_irq_select(); + if (IS_ERR_VALUE(ret)) + return log_msg_ret("sci_irq", ret); + scis = ret; + scis &= SCI_IRQ_MASK; + scis >>= SCI_IRQ_SHIFT; + + /* Determine how SCI is routed. */ + switch (scis) { + case SCIS_IRQ9: + case SCIS_IRQ10: + case SCIS_IRQ11: + sci_irq = scis - SCIS_IRQ9 + 9; + break; + case SCIS_IRQ20: + case SCIS_IRQ21: + case SCIS_IRQ22: + case SCIS_IRQ23: + sci_irq = scis - SCIS_IRQ20 + 20; + break; + default: + log_warning("Invalid SCI route! Defaulting to IRQ9\n"); + sci_irq = 9; + break; + } + + log_debug("SCI is IRQ%d\n", sci_irq); + + return sci_irq; +} + +static unsigned long acpi_madt_irq_overrides(unsigned long current) +{ + int sci = acpi_sci_irq(); + u16 flags = MP_IRQ_TRIGGER_LEVEL; + + if (sci < 0) + return log_msg_ret("sci irq", sci); + + /* INT_SRC_OVR */ + current += acpi_create_madt_irqoverride((void *)current, 0, 0, 2, 0); + + flags |= arch_madt_sci_irq_polarity(sci); + + /* SCI */ + current += + acpi_create_madt_irqoverride((void *)current, 0, sci, sci, flags); + + return current; +} + +u32 acpi_fill_madt(u32 current) +{ + /* Local APICs */ + current += acpi_create_madt_lapics(current); + + /* IOAPIC */ + current += acpi_create_madt_ioapic((void *)current, 2, IO_APIC_ADDR, 0); + + return acpi_madt_irq_overrides(current); +} + +void intel_acpi_fill_fadt(struct acpi_fadt *fadt) +{ + const u16 pmbase = IOMAP_ACPI_BASE; + + /* Use ACPI 3.0 revision. */ + fadt->header.revision = acpi_get_table_revision(ACPITAB_FADT); + + fadt->sci_int = acpi_sci_irq(); + fadt->smi_cmd = APM_CNT; + fadt->acpi_enable = APM_CNT_ACPI_ENABLE; + fadt->acpi_disable = APM_CNT_ACPI_DISABLE; + fadt->s4bios_req = 0x0; + fadt->pstate_cnt = 0; + + fadt->pm1a_evt_blk = pmbase + PM1_STS; + fadt->pm1b_evt_blk = 0x0; + fadt->pm1a_cnt_blk = pmbase + PM1_CNT; + fadt->pm1b_cnt_blk = 0x0; + + fadt->gpe0_blk = pmbase + GPE0_STS; + + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + + /* GPE0 STS/EN pairs each 32 bits wide. */ + fadt->gpe0_blk_len = 2 * GPE0_REG_MAX * sizeof(uint32_t); + + fadt->flush_size = 0x400; /* twice of cache size */ + fadt->flush_stride = 0x10; /* Cache line width */ + fadt->duty_offset = 1; + fadt->day_alrm = 0xd; + + fadt->flags = ACPI_FADT_WBINVD | ACPI_FADT_C1_SUPPORTED | + ACPI_FADT_C2_MP_SUPPORTED | ACPI_FADT_SLEEP_BUTTON | + ACPI_FADT_RESET_REGISTER | ACPI_FADT_SEALED_CASE | + ACPI_FADT_S4_RTC_WAKE | ACPI_FADT_PLATFORM_CLOCK; + + fadt->reset_reg.space_id = 1; + fadt->reset_reg.bit_width = 8; + fadt->reset_reg.addrl = IO_PORT_RESET; + fadt->reset_value = RST_CPU | SYS_RST; + + fadt->x_pm1a_evt_blk.space_id = 1; + fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; + fadt->x_pm1a_evt_blk.addrl = pmbase + PM1_STS; + + fadt->x_pm1b_evt_blk.space_id = 1; + + fadt->x_pm1a_cnt_blk.space_id = 1; + fadt->x_pm1a_cnt_blk.bit_width = fadt->pm1_cnt_len * 8; + fadt->x_pm1a_cnt_blk.addrl = pmbase + PM1_CNT; + + fadt->x_pm1b_cnt_blk.space_id = 1; + + fadt->x_gpe1_blk.space_id = 1; +} + +int intel_southbridge_write_acpi_tables(const struct udevice *dev, + struct acpi_ctx *ctx) +{ + int ret; + + ret = acpi_write_dbg2_pci_uart(ctx, gd->cur_serial_dev, + ACPI_ACCESS_SIZE_DWORD_ACCESS); + if (ret) + return log_msg_ret("dbg2", ret); + + ret = acpi_write_hpet(ctx); + if (ret) + return log_msg_ret("hpet", ret); + + return 0; +} + +__weak u32 acpi_fill_soc_wake(u32 generic_pm1_en, + const struct chipset_power_state *ps) +{ + return generic_pm1_en; +} + +__weak int acpi_create_gnvs(struct acpi_global_nvs *gnvs) +{ + return 0; +} + +int southbridge_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + struct acpi_global_nvs *gnvs; + int ret; + + ret = bloblist_ensure_size(BLOBLISTT_ACPI_GNVS, sizeof(*gnvs), + (void **)&gnvs); + if (ret) + return log_msg_ret("bloblist", ret); + memset(gnvs, '\0', sizeof(*gnvs)); + + ret = acpi_create_gnvs(gnvs); + if (ret) + return log_msg_ret("gnvs", ret); + + /* + * TODO(sjg@chromum.org): tell SMI about it + * smm_setup_structures(gnvs, NULL, NULL); + */ + + /* Add it to DSDT */ + acpigen_write_scope(ctx, "\\"); + acpigen_write_name_dword(ctx, "NVSA", (uintptr_t)gnvs); + acpigen_pop_len(ctx); + + return 0; +} + +static int calculate_power(int tdp, int p1_ratio, int ratio) +{ + u32 m; + u32 power; + + /* + * M = ((1.1 - ((p1_ratio - ratio) * 0.00625)) / 1.1) ^ 2 + * + * Power = (ratio / p1_ratio) * m * tdp + */ + + m = (110000 - ((p1_ratio - ratio) * 625)) / 11; + m = (m * m) / 1000; + + power = ((ratio * 100000 / p1_ratio) / 100); + power *= (m / 100) * (tdp / 1000); + power /= 1000; + + return power; +} + +void generate_p_state_entries(struct acpi_ctx *ctx, int core, + int cores_per_package) +{ + int ratio_min, ratio_max, ratio_turbo, ratio_step; + int coord_type, power_max, num_entries; + int ratio, power, clock, clock_max; + bool turbo; + + coord_type = cpu_get_coord_type(); + ratio_min = cpu_get_min_ratio(); + ratio_max = cpu_get_max_ratio(); + clock_max = (ratio_max * cpu_get_bus_clock_khz()) / 1000; + turbo = (turbo_get_state() == TURBO_ENABLED); + + /* Calculate CPU TDP in mW */ + power_max = cpu_get_power_max(); + + /* Write _PCT indicating use of FFixedHW */ + acpigen_write_empty_pct(ctx); + + /* Write _PPC with no limit on supported P-state */ + acpigen_write_ppc_nvs(ctx); + /* Write PSD indicating configured coordination type */ + acpigen_write_psd_package(ctx, core, 1, coord_type); + + /* Add P-state entries in _PSS table */ + acpigen_write_name(ctx, "_PSS"); + + /* Determine ratio points */ + ratio_step = PSS_RATIO_STEP; + do { + num_entries = ((ratio_max - ratio_min) / ratio_step) + 1; + if (((ratio_max - ratio_min) % ratio_step) > 0) + num_entries += 1; + if (turbo) + num_entries += 1; + if (num_entries > PSS_MAX_ENTRIES) + ratio_step += 1; + } while (num_entries > PSS_MAX_ENTRIES); + + /* _PSS package count depends on Turbo */ + acpigen_write_package(ctx, num_entries); + + /* P[T] is Turbo state if enabled */ + if (turbo) { + ratio_turbo = cpu_get_max_turbo_ratio(); + + /* Add entry for Turbo ratio */ + acpigen_write_pss_package(ctx, clock_max + 1, /* MHz */ + power_max, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio_turbo << 8, /* control */ + ratio_turbo << 8); /* status */ + num_entries -= 1; + } + + /* First regular entry is max non-turbo ratio */ + acpigen_write_pss_package(ctx, clock_max, /* MHz */ + power_max, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio_max << 8, /* control */ + ratio_max << 8); /* status */ + num_entries -= 1; + + /* Generate the remaining entries */ + for (ratio = ratio_min + ((num_entries - 1) * ratio_step); + ratio >= ratio_min; ratio -= ratio_step) { + /* Calculate power at this ratio */ + power = calculate_power(power_max, ratio_max, ratio); + clock = (ratio * cpu_get_bus_clock_khz()) / 1000; + + acpigen_write_pss_package(ctx, clock, /* MHz */ + power, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio << 8, /* control */ + ratio << 8); /* status */ + } + /* Fix package length */ + acpigen_pop_len(ctx); +} + +void generate_t_state_entries(struct acpi_ctx *ctx, int core, + int cores_per_package, struct acpi_tstate *entry, + int nentries) +{ + if (!nentries) + return; + + /* Indicate SW_ALL coordination for T-states */ + acpigen_write_tsd_package(ctx, core, cores_per_package, SW_ALL); + + /* Indicate FixedHW so OS will use MSR */ + acpigen_write_empty_ptc(ctx); + + /* Set NVS controlled T-state limit */ + acpigen_write_tpc(ctx, "\\TLVL"); + + /* Write TSS table for MSR access */ + acpigen_write_tss_package(ctx, entry, nentries); +} + +int acpi_generate_cpu_header(struct acpi_ctx *ctx, int core_id, + const struct acpi_cstate *c_state_map, + int num_cstates) +{ + bool is_first = !core_id; + + /* Generate processor \_PR.CPUx */ + acpigen_write_processor(ctx, core_id, is_first ? ACPI_BASE_ADDRESS : 0, + is_first ? 6 : 0); + + /* Generate C-state tables */ + acpigen_write_cst_package(ctx, c_state_map, num_cstates); + + return 0; +} + +int acpi_generate_cpu_package_final(struct acpi_ctx *ctx, int cores_per_package) +{ + /* + * PPKG is usually used for thermal management of the first and only + * package + */ + acpigen_write_processor_package(ctx, "PPKG", 0, cores_per_package); + + /* Add a method to notify processor nodes */ + acpigen_write_processor_cnot(ctx, cores_per_package); + + return 0; +} diff --git a/arch/x86/cpu/intel_common/cpu.c b/arch/x86/cpu/intel_common/cpu.c index cb4ef84013a..d8a3d60ae72 100644 --- a/arch/x86/cpu/intel_common/cpu.c +++ b/arch/x86/cpu/intel_common/cpu.c @@ -291,3 +291,17 @@ int cpu_get_max_turbo_ratio(void) return msr.lo & 0xff; } + +int cpu_get_cores_per_package(void) +{ + struct cpuid_result result; + int cores = 1; + + if (gd->arch.x86_vendor != X86_VENDOR_INTEL) + return 1; + + result = cpuid_ext(0xb, 1); + cores = result.ebx & 0xff; + + return cores; +} diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index 1b7ff509516..3245e447813 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -76,4 +76,26 @@ ulong write_acpi_tables(ulong start); */ ulong acpi_get_rsdp_addr(void); +/** + * arch_read_sci_irq_select() - Read the system-control interrupt number + * + * @returns value of IRQ register in the PMC + */ +int arch_read_sci_irq_select(void); + +/** + * arch_write_sci_irq_select() - Set the system-control interrupt number + * + * @scis: New value for IRQ register in the PMC + */ +int arch_write_sci_irq_select(uint scis); + +/** + * arch_madt_sci_irq_polarity() - Return the priority to use for the MADT + * + * @sci: System-control interrupt number + * @return priority to use (MP_IRQ_POLARITY_...) + */ +int arch_madt_sci_irq_polarity(int sci); + #endif /* __ASM_ACPI_TABLE_H__ */ diff --git a/arch/x86/include/asm/cpu_common.h b/arch/x86/include/asm/cpu_common.h index a7b7112d417..48f56c2aad9 100644 --- a/arch/x86/include/asm/cpu_common.h +++ b/arch/x86/include/asm/cpu_common.h @@ -177,4 +177,11 @@ int cpu_get_power_max(void); */ int cpu_get_max_turbo_ratio(void); +/** + * cpu_get_cores_per_package() - Get the number of CPU cores in each package + * + * @return number of cores + */ +int cpu_get_cores_per_package(void); + #endif diff --git a/arch/x86/include/asm/intel_acpi.h b/arch/x86/include/asm/intel_acpi.h new file mode 100644 index 00000000000..a5781f1af45 --- /dev/null +++ b/arch/x86/include/asm/intel_acpi.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018, Bin Meng + */ + +#ifndef __ASM_INTEL_ACPI_H__ +#define __ASM_INTEL_ACPI_H__ + +struct acpi_cstate; +struct acpi_ctx; +struct acpi_tstate; +struct udevice; + +/** + * acpi_generate_cpu_header() - Start generating an ACPI CPU entry + * + * Generates the ACPI information for a CPU. After this, the caller should + * generate_p_state_entries(), generate_t_state_entries and then + * acpigen_pop_len() to close off this package. + * + * @ctx: ACPI context pointer + * @core_id: CPU core number, as numbered by the SoC + * @c_state_map: Information about each C state + * @num_cstates: Number of entries in @c_state_map + * @return 0 if OK, -ve on error + */ +int acpi_generate_cpu_header(struct acpi_ctx *ctx, int core_id, + const struct acpi_cstate *c_state_map, + int num_cstates); + +/** + * acpi_generate_cpu_package_final() - Write out the CPU PPKG entry + * + * This writes information about the CPUs in the package + * + * @ctx: ACPI context pointer + * @cores_per_package: Number of CPU cores in each package in the SoC + */ +int acpi_generate_cpu_package_final(struct acpi_ctx *ctx, + int cores_per_package); + +void generate_p_state_entries(struct acpi_ctx *ctx, int core, + int cores_per_package); +void generate_t_state_entries(struct acpi_ctx *ctx, int core, + int cores_per_package, struct acpi_tstate *entry, + int nentries); +int southbridge_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx); + +int intel_southbridge_write_acpi_tables(const struct udevice *dev, + struct acpi_ctx *ctx); + +#endif /* __ASM_INTEL_ACPI_H__ */ diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 00d1d80dc38..1ca5d66141b 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -277,4 +277,13 @@ config ACPIGEN things like generating device-specific tables and returning the ACPI name of a device. +config INTEL_ACPIGEN + bool "Support ACPI table generation for Intel SoCs" + depends on ACPIGEN + help + This option adds some functions used for programatic generation of + ACPI tables on Intel SoCs. This provides features for writing CPU + information such as P states and T stages. Also included is a way + to create a GNVS table and set it up. + endmenu -- cgit v1.3.1 From eaac9717369c6ed9e6c5d9c7ed5effb420cb4840 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:45:24 -0600 Subject: tpm: cr50: Add ACPI support Generate ACPI information for this device so that Linux can use it correctly. Signed-off-by: Simon Glass --- drivers/tpm/cr50_i2c.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'drivers') diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index 1942c07c605..64831a42232 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -14,11 +14,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include enum { TIMEOUT_INIT_MS = 30000, /* Very long timeout for TPM init */ @@ -581,6 +584,53 @@ static int cr50_i2c_cleanup(struct udevice *dev) return 0; } +static int cr50_acpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + char scope[ACPI_PATH_MAX]; + char name[ACPI_NAME_MAX]; + const char *hid; + int ret; + + ret = acpi_device_scope(dev, scope, sizeof(scope)); + if (ret) + return log_msg_ret("scope", ret); + ret = acpi_get_name(dev, name); + if (ret) + return log_msg_ret("name", ret); + + hid = dev_read_string(dev, "acpi,hid"); + if (!hid) + return log_msg_ret("hid", ret); + + /* Device */ + acpigen_write_scope(ctx, scope); + acpigen_write_device(ctx, name); + acpigen_write_name_string(ctx, "_HID", hid); + acpigen_write_name_integer(ctx, "_UID", + dev_read_u32_default(dev, "acpi,uid", 0)); + acpigen_write_name_string(ctx, "_DDN", + dev_read_string(dev, "acpi,ddn")); + acpigen_write_sta(ctx, acpi_device_status(dev)); + + /* Resources */ + acpigen_write_name(ctx, "_CRS"); + acpigen_write_resourcetemplate_header(ctx); + ret = acpi_device_write_i2c_dev(ctx, dev); + if (ret < 0) + return log_msg_ret("i2c", ret); + ret = acpi_device_write_interrupt_or_gpio(ctx, (struct udevice *)dev, + "ready-gpios"); + if (ret < 0) + return log_msg_ret("irq_gpio", ret); + + acpigen_write_resourcetemplate_footer(ctx); + + acpigen_pop_len(ctx); /* Device */ + acpigen_pop_len(ctx); /* Scope */ + + return 0; +} + enum { TPM_TIMEOUT_MS = 5, SHORT_TIMEOUT_MS = 750, @@ -653,6 +703,10 @@ static int cr50_i2c_probe(struct udevice *dev) return 0; } +struct acpi_ops cr50_acpi_ops = { + .fill_ssdt = cr50_acpi_fill_ssdt, +}; + static const struct tpm_ops cr50_i2c_ops = { .open = cr50_i2c_open, .get_desc = cr50_i2c_get_desc, @@ -675,5 +729,6 @@ U_BOOT_DRIVER(cr50_i2c) = { .probe = cr50_i2c_probe, .remove = cr50_i2c_cleanup, .priv_auto_alloc_size = sizeof(struct cr50_priv), + ACPI_OPS_PTR(&cr50_acpi_ops) .flags = DM_FLAG_OS_PREPARE, }; -- cgit v1.3.1