Simon Glass | 31cc5b4 | 2020-09-22 12:45:01 -0600 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright 2019 Google LLC |
| 4 | */ |
| 5 | |
| 6 | #include <common.h> |
| 7 | #include <dm.h> |
| 8 | #include <i2c.h> |
| 9 | #include <log.h> |
| 10 | #include <acpi/acpi_device.h> |
| 11 | #include <acpi/acpigen.h> |
| 12 | #include <acpi/acpi_dp.h> |
| 13 | #ifdef CONFIG_X86 |
| 14 | #include <asm/intel_pinctrl_defs.h> |
| 15 | #endif |
| 16 | #include <asm-generic/gpio.h> |
| 17 | #include <dm/acpi.h> |
| 18 | |
| 19 | static bool acpi_i2c_add_gpios_to_crs(struct acpi_i2c_priv *priv) |
| 20 | { |
| 21 | /* |
| 22 | * Return false if: |
| 23 | * 1. Request to explicitly disable export of GPIOs in CRS, or |
| 24 | * 2. Both reset and enable GPIOs are not provided. |
| 25 | */ |
| 26 | if (priv->disable_gpio_export_in_crs || |
| 27 | (!dm_gpio_is_valid(&priv->reset_gpio) && |
| 28 | !dm_gpio_is_valid(&priv->enable_gpio))) |
| 29 | return false; |
| 30 | |
| 31 | return true; |
| 32 | } |
| 33 | |
| 34 | static int acpi_i2c_write_gpio(struct acpi_ctx *ctx, struct gpio_desc *gpio, |
| 35 | int *curindex) |
| 36 | { |
| 37 | int ret; |
| 38 | |
| 39 | if (!dm_gpio_is_valid(gpio)) |
| 40 | return -ENOENT; |
| 41 | |
| 42 | acpi_device_write_gpio_desc(ctx, gpio); |
| 43 | ret = *curindex; |
| 44 | (*curindex)++; |
| 45 | |
| 46 | return ret; |
| 47 | } |
| 48 | |
| 49 | int acpi_i2c_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) |
| 50 | { |
| 51 | int reset_gpio_index = -1, enable_gpio_index = -1, irq_gpio_index = -1; |
| 52 | enum i2c_device_t type = dev_get_driver_data(dev); |
| 53 | struct acpi_i2c_priv *priv = dev_get_priv(dev); |
| 54 | struct acpi_dp *dsd = NULL; |
| 55 | char scope[ACPI_PATH_MAX]; |
| 56 | char name[ACPI_NAME_MAX]; |
| 57 | int tx_state_val; |
| 58 | int curindex = 0; |
| 59 | int ret; |
| 60 | |
| 61 | #ifdef CONFIG_X86 |
| 62 | tx_state_val = PAD_CFG0_TX_STATE; |
| 63 | #elif defined(CONFIG_SANDBOX) |
| 64 | tx_state_val = BIT(7); /* test value */ |
| 65 | #else |
| 66 | #error "Not supported on this architecture" |
| 67 | #endif |
| 68 | ret = acpi_get_name(dev, name); |
| 69 | if (ret) |
| 70 | return log_msg_ret("name", ret); |
| 71 | ret = acpi_device_scope(dev, scope, sizeof(scope)); |
| 72 | if (ret) |
| 73 | return log_msg_ret("scope", ret); |
| 74 | |
| 75 | /* Device */ |
| 76 | acpigen_write_scope(ctx, scope); |
| 77 | acpigen_write_device(ctx, name); |
| 78 | acpigen_write_name_string(ctx, "_HID", priv->hid); |
| 79 | if (type == I2C_DEVICE_HID_OVER_I2C) |
| 80 | acpigen_write_name_string(ctx, "_CID", "PNP0C50"); |
| 81 | acpigen_write_name_integer(ctx, "_UID", priv->uid); |
| 82 | acpigen_write_name_string(ctx, "_DDN", priv->desc); |
| 83 | acpigen_write_sta(ctx, acpi_device_status(dev)); |
| 84 | |
| 85 | /* Resources */ |
| 86 | acpigen_write_name(ctx, "_CRS"); |
| 87 | acpigen_write_resourcetemplate_header(ctx); |
| 88 | acpi_device_write_i2c_dev(ctx, dev); |
| 89 | |
| 90 | /* Use either Interrupt() or GpioInt() */ |
| 91 | if (dm_gpio_is_valid(&priv->irq_gpio)) { |
| 92 | irq_gpio_index = acpi_i2c_write_gpio(ctx, &priv->irq_gpio, |
| 93 | &curindex); |
| 94 | } else { |
| 95 | ret = acpi_device_write_interrupt_irq(ctx, &priv->irq); |
| 96 | if (ret < 0) |
| 97 | return log_msg_ret("irq", ret); |
| 98 | } |
| 99 | |
| 100 | if (acpi_i2c_add_gpios_to_crs(priv)) { |
| 101 | reset_gpio_index = acpi_i2c_write_gpio(ctx, &priv->reset_gpio, |
| 102 | &curindex); |
| 103 | enable_gpio_index = acpi_i2c_write_gpio(ctx, &priv->enable_gpio, |
| 104 | &curindex); |
| 105 | } |
| 106 | acpigen_write_resourcetemplate_footer(ctx); |
| 107 | |
| 108 | /* Wake capabilities */ |
| 109 | if (priv->wake) { |
| 110 | acpigen_write_name_integer(ctx, "_S0W", 4); |
| 111 | acpigen_write_prw(ctx, priv->wake, 3); |
| 112 | } |
| 113 | |
| 114 | /* DSD */ |
| 115 | if (priv->probed || priv->property_count || priv->compat_string || |
| 116 | reset_gpio_index >= 0 || enable_gpio_index >= 0 || |
| 117 | irq_gpio_index >= 0) { |
| 118 | char path[ACPI_PATH_MAX]; |
| 119 | |
| 120 | ret = acpi_device_path(dev, path, sizeof(path)); |
| 121 | if (ret) |
| 122 | return log_msg_ret("path", ret); |
| 123 | |
| 124 | dsd = acpi_dp_new_table("_DSD"); |
| 125 | if (priv->compat_string) |
| 126 | acpi_dp_add_string(dsd, "compatible", |
| 127 | priv->compat_string); |
| 128 | if (priv->probed) |
| 129 | acpi_dp_add_integer(dsd, "linux,probed", 1); |
| 130 | if (irq_gpio_index >= 0) |
| 131 | acpi_dp_add_gpio(dsd, "irq-gpios", path, |
| 132 | irq_gpio_index, 0, |
| 133 | priv->irq_gpio.flags & |
| 134 | GPIOD_ACTIVE_LOW ? |
| 135 | ACPI_GPIO_ACTIVE_LOW : 0); |
| 136 | if (reset_gpio_index >= 0) |
| 137 | acpi_dp_add_gpio(dsd, "reset-gpios", path, |
| 138 | reset_gpio_index, 0, |
| 139 | priv->reset_gpio.flags & |
| 140 | GPIOD_ACTIVE_LOW ? |
| 141 | ACPI_GPIO_ACTIVE_LOW : 0); |
| 142 | if (enable_gpio_index >= 0) |
| 143 | acpi_dp_add_gpio(dsd, "enable-gpios", path, |
| 144 | enable_gpio_index, 0, |
| 145 | priv->enable_gpio.flags & |
| 146 | GPIOD_ACTIVE_LOW ? |
| 147 | ACPI_GPIO_ACTIVE_LOW : 0); |
| 148 | /* Generic property list is not supported */ |
| 149 | acpi_dp_write(ctx, dsd); |
| 150 | } |
| 151 | |
| 152 | /* Power Resource */ |
| 153 | if (priv->has_power_resource) { |
| 154 | ret = acpi_device_add_power_res(ctx, tx_state_val, |
| 155 | "\\_SB.GPC0", "\\_SB.SPC0", |
| 156 | &priv->reset_gpio, priv->reset_delay_ms, |
| 157 | priv->reset_off_delay_ms, &priv->enable_gpio, |
| 158 | priv->enable_delay_ms, priv->enable_off_delay_ms, |
| 159 | &priv->stop_gpio, priv->stop_delay_ms, |
| 160 | priv->stop_off_delay_ms); |
| 161 | if (ret) |
| 162 | return log_msg_ret("power", ret); |
| 163 | } |
| 164 | if (priv->hid_desc_reg_offset) { |
| 165 | ret = acpi_device_write_dsm_i2c_hid(ctx, |
| 166 | priv->hid_desc_reg_offset); |
| 167 | if (ret) |
| 168 | return log_msg_ret("dsm", ret); |
| 169 | } |
| 170 | |
| 171 | acpigen_pop_len(ctx); /* Device */ |
| 172 | acpigen_pop_len(ctx); /* Scope */ |
| 173 | |
| 174 | return 0; |
| 175 | } |
| 176 | |
| 177 | int acpi_i2c_ofdata_to_platdata(struct udevice *dev) |
| 178 | { |
| 179 | struct acpi_i2c_priv *priv = dev_get_priv(dev); |
| 180 | |
| 181 | gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset_gpio, |
| 182 | GPIOD_IS_OUT); |
| 183 | gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable_gpio, |
| 184 | GPIOD_IS_OUT); |
| 185 | gpio_request_by_name(dev, "irq-gpios", 0, &priv->irq_gpio, GPIOD_IS_IN); |
| 186 | gpio_request_by_name(dev, "stop-gpios", 0, &priv->stop_gpio, |
| 187 | GPIOD_IS_OUT); |
| 188 | irq_get_by_index(dev, 0, &priv->irq); |
| 189 | priv->hid = dev_read_string(dev, "acpi,hid"); |
| 190 | if (!priv->hid) |
| 191 | return log_msg_ret("hid", -EINVAL); |
| 192 | dev_read_u32(dev, "acpi,uid", &priv->uid); |
| 193 | priv->desc = dev_read_string(dev, "acpi,ddn"); |
| 194 | dev_read_u32(dev, "acpi,wake", &priv->wake); |
| 195 | priv->probed = dev_read_bool(dev, "linux,probed"); |
| 196 | priv->compat_string = dev_read_string(dev, "acpi,compatible"); |
| 197 | priv->has_power_resource = dev_read_bool(dev, |
| 198 | "acpi,has-power-resource"); |
| 199 | dev_read_u32(dev, "hid-descr-addr", &priv->hid_desc_reg_offset); |
| 200 | dev_read_u32(dev, "reset-delay-ms", &priv->reset_delay_ms); |
| 201 | dev_read_u32(dev, "reset-off-delay-ms", &priv->reset_off_delay_ms); |
| 202 | dev_read_u32(dev, "enable-delay-ms", &priv->enable_delay_ms); |
| 203 | dev_read_u32(dev, "enable-off-delay-ms", &priv->enable_off_delay_ms); |
| 204 | dev_read_u32(dev, "stop-delay-ms", &priv->stop_delay_ms); |
| 205 | dev_read_u32(dev, "stop-off-delay-ms", &priv->stop_off_delay_ms); |
| 206 | |
| 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | /* Use name specified in priv or build one from I2C address */ |
| 211 | static int acpi_i2c_get_name(const struct udevice *dev, char *out_name) |
| 212 | { |
| 213 | struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); |
| 214 | struct acpi_i2c_priv *priv = dev_get_priv(dev); |
| 215 | |
| 216 | snprintf(out_name, ACPI_NAME_MAX, |
| 217 | priv->hid_desc_reg_offset ? "H%03X" : "D%03X", |
| 218 | chip->chip_addr); |
| 219 | |
| 220 | return 0; |
| 221 | } |
| 222 | |
| 223 | struct acpi_ops acpi_i2c_ops = { |
| 224 | .fill_ssdt = acpi_i2c_fill_ssdt, |
| 225 | .get_name = acpi_i2c_get_name, |
| 226 | }; |