diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/core/Kconfig | 9 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 3 | ||||
-rw-r--r-- | drivers/i2c/acpi_i2c.c | 226 | ||||
-rw-r--r-- | drivers/i2c/acpi_i2c.h | 15 | ||||
-rw-r--r-- | drivers/i2c/i2c-uclass.c | 17 | ||||
-rw-r--r-- | drivers/sound/max98357a.c | 2 | ||||
-rw-r--r-- | drivers/tpm/cr50_i2c.c | 55 |
7 files changed, 326 insertions, 1 deletions
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 00d1d80dc3..1ca5d66141 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 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f7b2786448..bd248cbf52 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 0000000000..57d29683cb --- /dev/null +++ b/drivers/i2c/acpi_i2c.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <acpi/acpi_device.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_dp.h> +#ifdef CONFIG_X86 +#include <asm/intel_pinctrl_defs.h> +#endif +#include <asm-generic/gpio.h> +#include <dm/acpi.h> + +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 0000000000..1f4be29601 --- /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 <dm/acpi.h> + +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 2373aa2ea4..5c4626b044 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -9,6 +9,8 @@ #include <i2c.h> #include <log.h> #include <malloc.h> +#include <acpi/acpi_device.h> +#include <dm/acpi.h> #include <dm/device-internal.h> #include <dm/lists.h> #include <dm/pinctrl.h> @@ -16,6 +18,7 @@ #include <asm/gpio.h> #endif #include <linux/delay.h> +#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/drivers/sound/max98357a.c b/drivers/sound/max98357a.c index 841bc6ef68..827262d235 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); diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index 1942c07c60..64831a4223 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -14,11 +14,14 @@ #include <log.h> #include <spl.h> #include <tpm-v2.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_device.h> #include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/iomap.h> #include <asm/arch/pm.h> #include <linux/delay.h> +#include <dm/acpi.h> 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, }; |