123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- // 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,
- };
|