aboutsummaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-09-25 09:04:01 -0400
committerTom Rini <trini@konsulko.com>2020-09-25 09:04:01 -0400
commit0ac83d080a0044cd0d8f782ba12f02cf969d3004 (patch)
treeca5c2351113ba9b56d59e241a8857c7e6e8f5604 /drivers/i2c
parent67ece26d8b5d4bfa4fda8c456261c465d0815d7d (diff)
parent8c180d669a0f4a8eb70bde8c74c73cef45993f67 (diff)
downloadu-boot-0ac83d080a0044cd0d8f782ba12f02cf969d3004.zip
u-boot-0ac83d080a0044cd0d8f782ba12f02cf969d3004.tar.gz
u-boot-0ac83d080a0044cd0d8f782ba12f02cf969d3004.tar.bz2
Merge branch 'next' of https://gitlab.denx.de/u-boot/custodians/u-boot-x86 into next
- Enhance the 'zboot' command to be more like 'bootm' with sub-commands - The last series of ACPI core changes for programmatic generation of ACPI tables - Add all required ACPI tables for ApolloLake and enable ACPIGEN on Chromebook Coral - A feature minor enhancements to the 'hob' command - Intel edison: Support for writing an xFSTK image via binman
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/Makefile3
-rw-r--r--drivers/i2c/acpi_i2c.c226
-rw-r--r--drivers/i2c/acpi_i2c.h15
-rw-r--r--drivers/i2c/i2c-uclass.c17
4 files changed, 261 insertions, 0 deletions
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index f7b2786..bd248cb 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 0000000..57d2968
--- /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 0000000..1f4be29
--- /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 2373aa2..5c4626b 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)
};