blob: 98c59e6a05e4af4071f05c5735704b4693290d1d [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>
Matt DeVillierc6361422022-03-28 22:35:42 -05006#include <device/i2c_bus.h>
Nico Huber0f2dd1e2017-08-01 14:02:40 +02007#include <device/i2c_simple.h>
Duncan Laurie21a097a2016-05-11 09:50:59 -07008#include <device/device.h>
Duncan Laurie21a097a2016-05-11 09:50:59 -07009#include <gpio.h>
Elyes Haouasbdd03c22024-05-27 11:20:07 +020010#include <stdio.h>
Elyes Haouas08375b52024-05-27 11:36:52 +020011
Duncan Laurie21a097a2016-05-11 09:50:59 -070012#include "chip.h"
13
Julius Wernercd49cce2019-03-05 16:53:33 -080014#if CONFIG(HAVE_ACPI_TABLES)
Furquan Shaikh626ad202016-10-20 16:01:04 -070015
Furquan Shaikh98915bb2016-12-12 09:23:01 -080016static bool i2c_generic_add_gpios_to_crs(struct drivers_i2c_generic_config *cfg)
17{
Furquan Shaikh71d830f2017-01-25 17:53:01 -080018 /*
19 * Return false if:
Matt DeVillier4902e9b2023-01-11 17:41:37 -060020 * 1. GPIOs are exported via a power resource, or
Furquan Shaikh71d830f2017-01-25 17:53:01 -080021 * 2. Both reset and enable GPIOs are not provided.
22 */
Matt DeVillier4902e9b2023-01-11 17:41:37 -060023 if (cfg->has_power_resource ||
Furquan Shaikh71d830f2017-01-25 17:53:01 -080024 ((cfg->reset_gpio.pin_count == 0) &&
25 (cfg->enable_gpio.pin_count == 0)))
26 return false;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080027
Furquan Shaikh71d830f2017-01-25 17:53:01 -080028 return true;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080029}
30
Furquan Shaikh7536a392020-04-24 21:59:21 -070031void i2c_generic_fill_ssdt(const struct device *dev,
Furquan Shaikh8220c4b2020-04-24 21:44:27 -070032 void (*callback)(const struct device *dev),
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -080033 struct drivers_i2c_generic_config *config)
Duncan Laurie21a097a2016-05-11 09:50:59 -070034{
Duncan Laurie21a097a2016-05-11 09:50:59 -070035 const char *scope = acpi_device_scope(dev);
36 struct acpi_i2c i2c = {
37 .address = dev->path.i2c.device,
38 .mode_10bit = dev->path.i2c.mode_10bit,
Duncan Laurie14b2a4d2016-06-08 13:49:40 -070039 .speed = config->speed ? : I2C_SPEED_FAST,
Duncan Laurie21a097a2016-05-11 09:50:59 -070040 .resource = scope,
41 };
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -070042 struct acpi_dp *dsd = NULL;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080043 int curr_index = 0;
Duncan Laurieb94e9352017-03-15 11:25:31 -070044 int reset_gpio_index = -1, enable_gpio_index = -1, irq_gpio_index = -1;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080045 const char *path = acpi_device_path(dev);
Duncan Laurie21a097a2016-05-11 09:50:59 -070046
Karthikeyan Ramasubramaniand1c0f952020-11-02 16:26:52 -070047 if (!scope)
Duncan Laurie21a097a2016-05-11 09:50:59 -070048 return;
49
50 if (!config->hid) {
Matt DeVillier2494e932022-10-31 14:59:31 -050051 printk(BIOS_ERR, "%s: HID required but not set\n", dev_path(dev));
Duncan Laurie21a097a2016-05-11 09:50:59 -070052 return;
53 }
54
Matt DeVillierc6361422022-03-28 22:35:42 -050055 if (config->detect) {
56 struct device *const busdev = i2c_busdev(dev);
57 if (!i2c_dev_detect(busdev, dev->path.i2c.device)) {
58 printk(BIOS_SPEW, "%s: %s at %s -- NOT FOUND, skipping\n",
59 path,
60 config->desc ? : dev->chip_ops->name,
61 dev_path(dev));
62 return;
63 }
64 }
65
Duncan Laurie21a097a2016-05-11 09:50:59 -070066 /* Device */
67 acpigen_write_scope(scope);
68 acpigen_write_device(acpi_device_name(dev));
69 acpigen_write_name_string("_HID", config->hid);
Furquan Shaikh1d334882016-10-21 16:24:07 -070070 if (config->cid)
71 acpigen_write_name_string("_CID", config->cid);
Matt DeVillier2ca55f22023-01-18 18:14:09 -060072 if (config->sub)
73 acpigen_write_name_string("_SUB", config->sub);
Duncan Laurie21a097a2016-05-11 09:50:59 -070074 acpigen_write_name_integer("_UID", config->uid);
Matt DeVillier86dce8f2020-11-19 18:29:44 -060075 if (config->desc)
76 acpigen_write_name_string("_DDN", config->desc);
Hung-Te Linb4be50c2018-09-10 10:55:49 +080077 acpigen_write_STA(acpi_device_status(dev));
Duncan Laurie21a097a2016-05-11 09:50:59 -070078
79 /* Resources */
80 acpigen_write_name("_CRS");
81 acpigen_write_resourcetemplate_header();
82 acpi_device_write_i2c(&i2c);
Duncan Laurieb94e9352017-03-15 11:25:31 -070083
84 /* Use either Interrupt() or GpioInt() */
85 if (config->irq_gpio.pin_count)
Jianeng Ceng01344bc2024-04-09 21:20:20 +080086 irq_gpio_index = acpi_device_write_dsd_gpio(&config->irq_gpio,
Duncan Laurieb94e9352017-03-15 11:25:31 -070087 &curr_index);
88 else
89 acpi_device_write_interrupt(&config->irq);
90
Furquan Shaikh98915bb2016-12-12 09:23:01 -080091 if (i2c_generic_add_gpios_to_crs(config) == true) {
Jianeng Ceng01344bc2024-04-09 21:20:20 +080092 reset_gpio_index = acpi_device_write_dsd_gpio(&config->reset_gpio,
Furquan Shaikh98915bb2016-12-12 09:23:01 -080093 &curr_index);
Jianeng Ceng01344bc2024-04-09 21:20:20 +080094 enable_gpio_index = acpi_device_write_dsd_gpio(&config->enable_gpio,
Furquan Shaikh98915bb2016-12-12 09:23:01 -080095 &curr_index);
96 }
Duncan Laurie21a097a2016-05-11 09:50:59 -070097 acpigen_write_resourcetemplate_footer();
98
99 /* Wake capabilities */
100 if (config->wake) {
Tony Huang1282b002021-05-03 09:35:56 +0800101 acpigen_write_name_integer("_S0W", ACPI_DEVICE_SLEEP_D3_HOT);
Duncan Laurie21a097a2016-05-11 09:50:59 -0700102 acpigen_write_PRW(config->wake, 3);
103 }
104
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800105 /* DSD */
Furquan Shaikh8ce36ac2019-08-09 07:59:06 -0700106 if (config->probed || config->property_count || config->compat_string ||
Duncan Laurie1533a3c2017-08-29 08:28:58 -0700107 (reset_gpio_index != -1) ||
Duncan Laurieb94e9352017-03-15 11:25:31 -0700108 (enable_gpio_index != -1) || (irq_gpio_index != -1)) {
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700109 dsd = acpi_dp_new_table("_DSD");
Furquan Shaikh8ce36ac2019-08-09 07:59:06 -0700110 if (config->compat_string)
111 acpi_dp_add_string(dsd, "compatible",
112 config->compat_string);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800113 if (config->probed)
114 acpi_dp_add_integer(dsd, "linux,probed", 1);
Duncan Laurieb94e9352017-03-15 11:25:31 -0700115 if (irq_gpio_index != -1)
116 acpi_dp_add_gpio(dsd, "irq-gpios", path,
117 irq_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700118 config->irq_gpio.active_low);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800119 if (reset_gpio_index != -1)
120 acpi_dp_add_gpio(dsd, "reset-gpios", path,
121 reset_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700122 config->reset_gpio.active_low);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800123 if (enable_gpio_index != -1)
124 acpi_dp_add_gpio(dsd, "enable-gpios", path,
125 enable_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700126 config->enable_gpio.active_low);
Duncan Laurie1533a3c2017-08-29 08:28:58 -0700127 /* Add generic property list */
128 acpi_dp_add_property_list(dsd, config->property_list,
129 config->property_count);
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700130 acpi_dp_write(dsd);
131 }
132
Furquan Shaikh626ad202016-10-20 16:01:04 -0700133 /* Power Resource */
Shelley Chena0603392018-04-26 13:52:30 -0700134 if (config->has_power_resource) {
135 const struct acpi_power_res_params power_res_params = {
136 &config->reset_gpio,
137 config->reset_delay_ms,
138 config->reset_off_delay_ms,
139 &config->enable_gpio,
140 config->enable_delay_ms,
141 config->enable_off_delay_ms,
142 &config->stop_gpio,
143 config->stop_delay_ms,
144 config->stop_off_delay_ms
145 };
146 acpi_device_add_power_res(&power_res_params);
147 }
Furquan Shaikh626ad202016-10-20 16:01:04 -0700148
Sean Rhodesbba6a212024-01-23 15:10:59 +0000149 /* Rotation Matrix */
150 if (config->has_rotation_matrix) {
151 acpigen_write_method("ROTM", 0);
152 acpigen_write_package(3);
153
154 for (int i = 0; i < 3; i++) {
155 char matrix_row[12];
156 snprintf(matrix_row, sizeof(matrix_row), "%d %d %d",
157 config->rotation_matrix[i * 3 + 0],
158 config->rotation_matrix[i * 3 + 1],
159 config->rotation_matrix[i * 3 + 2]);
160
161 acpigen_write_string(matrix_row);
162 }
163
164 acpigen_pop_len();
165 acpigen_pop_len();
166 }
167
Furquan Shaikh1d334882016-10-21 16:24:07 -0700168 /* Callback if any. */
169 if (callback)
170 callback(dev);
171
Duncan Laurie21a097a2016-05-11 09:50:59 -0700172 acpigen_pop_len(); /* Device */
173 acpigen_pop_len(); /* Scope */
174
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800175 printk(BIOS_INFO, "%s: %s at %s\n", path,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700176 config->desc ? : dev->chip_ops->name, dev_path(dev));
177}
178
Furquan Shaikh7536a392020-04-24 21:59:21 -0700179static void i2c_generic_fill_ssdt_generator(const struct device *dev)
Furquan Shaikh1d334882016-10-21 16:24:07 -0700180{
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -0800181 i2c_generic_fill_ssdt(dev, NULL, dev->chip_info);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700182}
183
Duncan Laurie21a097a2016-05-11 09:50:59 -0700184/* Use name specified in config or build one from I2C address */
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600185static const char *i2c_generic_acpi_name(const struct device *dev)
Duncan Laurie21a097a2016-05-11 09:50:59 -0700186{
187 struct drivers_i2c_generic_config *config = dev->chip_info;
188 static char name[5];
189
190 if (config->name)
Furquan Shaikh3f4e6272016-10-18 15:11:32 -0700191 return config->name;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700192
193 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
194 name[4] = '\0';
195 return name;
196}
197#endif
198
199static struct device_operations i2c_generic_ops = {
Nico Huber2f8ba692020-04-05 14:05:24 +0200200 .read_resources = noop_read_resources,
201 .set_resources = noop_set_resources,
Julius Wernercd49cce2019-03-05 16:53:33 -0800202#if CONFIG(HAVE_ACPI_TABLES)
Nico Huber68680dd2020-03-31 17:34:52 +0200203 .acpi_name = i2c_generic_acpi_name,
204 .acpi_fill_ssdt = i2c_generic_fill_ssdt_generator,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700205#endif
206};
207
208static void i2c_generic_enable(struct device *dev)
209{
210 struct drivers_i2c_generic_config *config = dev->chip_info;
211
Furquan Shaikh24681f12018-06-10 13:33:44 -0700212 if (!config)
213 return;
214
Duncan Laurie21a097a2016-05-11 09:50:59 -0700215 /* Check if device is present by reading GPIO */
216 if (config->device_present_gpio) {
217 int present = gpio_get(config->device_present_gpio);
218 present ^= config->device_present_gpio_invert;
219
220 printk(BIOS_INFO, "%s is %spresent\n",
221 dev->chip_ops->name, present ? "" : "not ");
222
223 if (!present) {
224 dev->enabled = 0;
225 return;
226 }
227 }
228
229 dev->ops = &i2c_generic_ops;
Naresh G Solanki69e9e712018-06-04 17:45:19 +0530230
231 /* Name the device as per description provided in devicetree */
Furquan Shaikh24681f12018-06-10 13:33:44 -0700232 if (config->desc)
Naresh G Solanki69e9e712018-06-04 17:45:19 +0530233 dev->name = config->desc;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700234}
235
236struct chip_operations drivers_i2c_generic_ops = {
Nicholas Sudsgaardbfb11be2024-01-30 09:53:46 +0900237 .name = "I2C Device",
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100238 .enable_dev = i2c_generic_enable
Duncan Laurie21a097a2016-05-11 09:50:59 -0700239};