blob: cd7406893bd2f486da1d9c7f417a602ef3e849d6 [file] [log] [blame]
Angel Pons8a3453f2020-04-02 23:48:19 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Duncan Laurie21a097a2016-05-11 09:50:59 -07002
Furquan Shaikh76cedd22020-05-02 10:24:23 -07003#include <acpi/acpi_device.h>
4#include <acpi/acpigen.h>
Duncan Laurie21a097a2016-05-11 09:50:59 -07005#include <console/console.h>
Nico Huber0f2dd1e2017-08-01 14:02:40 +02006#include <device/i2c_simple.h>
Duncan Laurie21a097a2016-05-11 09:50:59 -07007#include <device/device.h>
8#include <device/path.h>
9#include <gpio.h>
Duncan Laurie21a097a2016-05-11 09:50:59 -070010#include <string.h>
11#include "chip.h"
12
Julius Wernercd49cce2019-03-05 16:53:33 -080013#if CONFIG(HAVE_ACPI_TABLES)
Furquan Shaikh626ad202016-10-20 16:01:04 -070014
Furquan Shaikh98915bb2016-12-12 09:23:01 -080015static bool i2c_generic_add_gpios_to_crs(struct drivers_i2c_generic_config *cfg)
16{
Furquan Shaikh71d830f2017-01-25 17:53:01 -080017 /*
18 * Return false if:
19 * 1. Request to explicitly disable export of GPIOs in CRS, or
20 * 2. Both reset and enable GPIOs are not provided.
21 */
22 if (cfg->disable_gpio_export_in_crs ||
23 ((cfg->reset_gpio.pin_count == 0) &&
24 (cfg->enable_gpio.pin_count == 0)))
25 return false;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080026
Furquan Shaikh71d830f2017-01-25 17:53:01 -080027 return true;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080028}
29
30static int i2c_generic_write_gpio(struct acpi_gpio *gpio, int *curr_index)
31{
32 int ret = -1;
33
34 if (gpio->pin_count == 0)
35 return ret;
36
37 acpi_device_write_gpio(gpio);
38 ret = *curr_index;
39 (*curr_index)++;
40
41 return ret;
42}
43
Furquan Shaikh7536a392020-04-24 21:59:21 -070044void i2c_generic_fill_ssdt(const struct device *dev,
Furquan Shaikh8220c4b2020-04-24 21:44:27 -070045 void (*callback)(const struct device *dev),
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -080046 struct drivers_i2c_generic_config *config)
Duncan Laurie21a097a2016-05-11 09:50:59 -070047{
Duncan Laurie21a097a2016-05-11 09:50:59 -070048 const char *scope = acpi_device_scope(dev);
49 struct acpi_i2c i2c = {
50 .address = dev->path.i2c.device,
51 .mode_10bit = dev->path.i2c.mode_10bit,
Duncan Laurie14b2a4d2016-06-08 13:49:40 -070052 .speed = config->speed ? : I2C_SPEED_FAST,
Duncan Laurie21a097a2016-05-11 09:50:59 -070053 .resource = scope,
54 };
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -070055 struct acpi_dp *dsd = NULL;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080056 int curr_index = 0;
Duncan Laurieb94e9352017-03-15 11:25:31 -070057 int reset_gpio_index = -1, enable_gpio_index = -1, irq_gpio_index = -1;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080058 const char *path = acpi_device_path(dev);
Duncan Laurie21a097a2016-05-11 09:50:59 -070059
Karthikeyan Ramasubramaniand1c0f952020-11-02 16:26:52 -070060 if (!scope)
Duncan Laurie21a097a2016-05-11 09:50:59 -070061 return;
62
63 if (!config->hid) {
64 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
65 return;
66 }
67
68 /* Device */
69 acpigen_write_scope(scope);
70 acpigen_write_device(acpi_device_name(dev));
71 acpigen_write_name_string("_HID", config->hid);
Furquan Shaikh1d334882016-10-21 16:24:07 -070072 if (config->cid)
73 acpigen_write_name_string("_CID", config->cid);
Duncan Laurie21a097a2016-05-11 09:50:59 -070074 acpigen_write_name_integer("_UID", config->uid);
75 acpigen_write_name_string("_DDN", config->desc);
Hung-Te Linb4be50c2018-09-10 10:55:49 +080076 acpigen_write_STA(acpi_device_status(dev));
Duncan Laurie21a097a2016-05-11 09:50:59 -070077
78 /* Resources */
79 acpigen_write_name("_CRS");
80 acpigen_write_resourcetemplate_header();
81 acpi_device_write_i2c(&i2c);
Duncan Laurieb94e9352017-03-15 11:25:31 -070082
83 /* Use either Interrupt() or GpioInt() */
84 if (config->irq_gpio.pin_count)
85 irq_gpio_index = i2c_generic_write_gpio(&config->irq_gpio,
86 &curr_index);
87 else
88 acpi_device_write_interrupt(&config->irq);
89
Furquan Shaikh98915bb2016-12-12 09:23:01 -080090 if (i2c_generic_add_gpios_to_crs(config) == true) {
91 reset_gpio_index = i2c_generic_write_gpio(&config->reset_gpio,
92 &curr_index);
93 enable_gpio_index = i2c_generic_write_gpio(&config->enable_gpio,
94 &curr_index);
95 }
Duncan Laurie21a097a2016-05-11 09:50:59 -070096 acpigen_write_resourcetemplate_footer();
97
98 /* Wake capabilities */
99 if (config->wake) {
100 acpigen_write_name_integer("_S0W", 4);
101 acpigen_write_PRW(config->wake, 3);
102 }
103
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800104 /* DSD */
Furquan Shaikh8ce36ac2019-08-09 07:59:06 -0700105 if (config->probed || config->property_count || config->compat_string ||
Duncan Laurie1533a3c2017-08-29 08:28:58 -0700106 (reset_gpio_index != -1) ||
Duncan Laurieb94e9352017-03-15 11:25:31 -0700107 (enable_gpio_index != -1) || (irq_gpio_index != -1)) {
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700108 dsd = acpi_dp_new_table("_DSD");
Furquan Shaikh8ce36ac2019-08-09 07:59:06 -0700109 if (config->compat_string)
110 acpi_dp_add_string(dsd, "compatible",
111 config->compat_string);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800112 if (config->probed)
113 acpi_dp_add_integer(dsd, "linux,probed", 1);
Duncan Laurieb94e9352017-03-15 11:25:31 -0700114 if (irq_gpio_index != -1)
115 acpi_dp_add_gpio(dsd, "irq-gpios", path,
116 irq_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700117 config->irq_gpio.active_low);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800118 if (reset_gpio_index != -1)
119 acpi_dp_add_gpio(dsd, "reset-gpios", path,
120 reset_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700121 config->reset_gpio.active_low);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800122 if (enable_gpio_index != -1)
123 acpi_dp_add_gpio(dsd, "enable-gpios", path,
124 enable_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700125 config->enable_gpio.active_low);
Duncan Laurie1533a3c2017-08-29 08:28:58 -0700126 /* Add generic property list */
127 acpi_dp_add_property_list(dsd, config->property_list,
128 config->property_count);
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700129 acpi_dp_write(dsd);
130 }
131
Furquan Shaikh626ad202016-10-20 16:01:04 -0700132 /* Power Resource */
Shelley Chena0603392018-04-26 13:52:30 -0700133 if (config->has_power_resource) {
134 const struct acpi_power_res_params power_res_params = {
135 &config->reset_gpio,
136 config->reset_delay_ms,
137 config->reset_off_delay_ms,
138 &config->enable_gpio,
139 config->enable_delay_ms,
140 config->enable_off_delay_ms,
141 &config->stop_gpio,
142 config->stop_delay_ms,
143 config->stop_off_delay_ms
144 };
145 acpi_device_add_power_res(&power_res_params);
146 }
Furquan Shaikh626ad202016-10-20 16:01:04 -0700147
Furquan Shaikh1d334882016-10-21 16:24:07 -0700148 /* Callback if any. */
149 if (callback)
150 callback(dev);
151
Duncan Laurie21a097a2016-05-11 09:50:59 -0700152 acpigen_pop_len(); /* Device */
153 acpigen_pop_len(); /* Scope */
154
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800155 printk(BIOS_INFO, "%s: %s at %s\n", path,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700156 config->desc ? : dev->chip_ops->name, dev_path(dev));
157}
158
Furquan Shaikh7536a392020-04-24 21:59:21 -0700159static void i2c_generic_fill_ssdt_generator(const struct device *dev)
Furquan Shaikh1d334882016-10-21 16:24:07 -0700160{
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -0800161 i2c_generic_fill_ssdt(dev, NULL, dev->chip_info);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700162}
163
Duncan Laurie21a097a2016-05-11 09:50:59 -0700164/* Use name specified in config or build one from I2C address */
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600165static const char *i2c_generic_acpi_name(const struct device *dev)
Duncan Laurie21a097a2016-05-11 09:50:59 -0700166{
167 struct drivers_i2c_generic_config *config = dev->chip_info;
168 static char name[5];
169
170 if (config->name)
Furquan Shaikh3f4e6272016-10-18 15:11:32 -0700171 return config->name;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700172
173 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
174 name[4] = '\0';
175 return name;
176}
177#endif
178
179static struct device_operations i2c_generic_ops = {
Nico Huber2f8ba692020-04-05 14:05:24 +0200180 .read_resources = noop_read_resources,
181 .set_resources = noop_set_resources,
Julius Wernercd49cce2019-03-05 16:53:33 -0800182#if CONFIG(HAVE_ACPI_TABLES)
Nico Huber68680dd2020-03-31 17:34:52 +0200183 .acpi_name = i2c_generic_acpi_name,
184 .acpi_fill_ssdt = i2c_generic_fill_ssdt_generator,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700185#endif
186};
187
188static void i2c_generic_enable(struct device *dev)
189{
190 struct drivers_i2c_generic_config *config = dev->chip_info;
191
Furquan Shaikh24681f12018-06-10 13:33:44 -0700192 if (!config)
193 return;
194
Duncan Laurie21a097a2016-05-11 09:50:59 -0700195 /* Check if device is present by reading GPIO */
196 if (config->device_present_gpio) {
197 int present = gpio_get(config->device_present_gpio);
198 present ^= config->device_present_gpio_invert;
199
200 printk(BIOS_INFO, "%s is %spresent\n",
201 dev->chip_ops->name, present ? "" : "not ");
202
203 if (!present) {
204 dev->enabled = 0;
205 return;
206 }
207 }
208
209 dev->ops = &i2c_generic_ops;
Naresh G Solanki69e9e712018-06-04 17:45:19 +0530210
211 /* Name the device as per description provided in devicetree */
Furquan Shaikh24681f12018-06-10 13:33:44 -0700212 if (config->desc)
Naresh G Solanki69e9e712018-06-04 17:45:19 +0530213 dev->name = config->desc;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700214}
215
216struct chip_operations drivers_i2c_generic_ops = {
217 CHIP_NAME("I2C Device")
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100218 .enable_dev = i2c_generic_enable
Duncan Laurie21a097a2016-05-11 09:50:59 -0700219};