diff options
Diffstat (limited to 'drivers/i2c')
-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-cdns.c | 2 | ||||
-rw-r--r-- | drivers/i2c/i2c-uclass.c | 17 | ||||
-rw-r--r-- | drivers/i2c/mxc_i2c.c | 7 |
6 files changed, 267 insertions, 3 deletions
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-cdns.c b/drivers/i2c/i2c-cdns.c index 024c63c4ea..7144d39984 100644 --- a/drivers/i2c/i2c-cdns.c +++ b/drivers/i2c/i2c-cdns.c @@ -500,7 +500,7 @@ static const struct udevice_id cdns_i2c_of_match[] = { }; U_BOOT_DRIVER(cdns_i2c) = { - .name = "i2c-cdns", + .name = "i2c_cdns", .id = UCLASS_I2C, .of_match = cdns_i2c_of_match, .ofdata_to_platdata = cdns_i2c_ofdata_to_platdata, 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/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index e3d980a9df..7609594bd0 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -941,7 +941,8 @@ static int mxc_i2c_probe(struct udevice *bus) */ ret = fdt_stringlist_search(fdt, node, "pinctrl-names", "gpio"); if (ret < 0) { - debug("i2c bus %d at 0x%2lx, no gpio pinctrl state.\n", bus->seq, i2c_bus->base); + debug("i2c bus %d at 0x%2lx, no gpio pinctrl state.\n", + bus->seq, i2c_bus->base); } else { ret = gpio_request_by_name_nodev(offset_to_ofnode(node), "scl-gpios", 0, &i2c_bus->scl_gpio, @@ -952,7 +953,9 @@ static int mxc_i2c_probe(struct udevice *bus) if (!dm_gpio_is_valid(&i2c_bus->sda_gpio) || !dm_gpio_is_valid(&i2c_bus->scl_gpio) || ret || ret2) { - dev_err(dev, "i2c bus %d at %lu, fail to request scl/sda gpio\n", bus->seq, i2c_bus->base); + dev_err(bus, + "i2c bus %d at %lu, fail to request scl/sda gpio\n", + bus->seq, i2c_bus->base); return -EINVAL; } } |