blob: 592e791ba9e1ef90d0ddd8d56a7c3ceccfd12073 [file] [log] [blame]
Duncan Laurie21a097a2016-05-11 09:50:59 -07001/*
2 * This file is part of the coreboot project.
3 *
Duncan Laurie21a097a2016-05-11 09:50:59 -07004 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <arch/acpi_device.h>
15#include <arch/acpigen.h>
16#include <console/console.h>
Nico Huber0f2dd1e2017-08-01 14:02:40 +020017#include <device/i2c_simple.h>
Duncan Laurie21a097a2016-05-11 09:50:59 -070018#include <device/device.h>
19#include <device/path.h>
20#include <gpio.h>
21#include <stdint.h>
22#include <string.h>
23#include "chip.h"
24
Julius Wernercd49cce2019-03-05 16:53:33 -080025#if CONFIG(HAVE_ACPI_TABLES)
Furquan Shaikh626ad202016-10-20 16:01:04 -070026
Furquan Shaikh98915bb2016-12-12 09:23:01 -080027static bool i2c_generic_add_gpios_to_crs(struct drivers_i2c_generic_config *cfg)
28{
Furquan Shaikh71d830f2017-01-25 17:53:01 -080029 /*
30 * Return false if:
31 * 1. Request to explicitly disable export of GPIOs in CRS, or
32 * 2. Both reset and enable GPIOs are not provided.
33 */
34 if (cfg->disable_gpio_export_in_crs ||
35 ((cfg->reset_gpio.pin_count == 0) &&
36 (cfg->enable_gpio.pin_count == 0)))
37 return false;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080038
Furquan Shaikh71d830f2017-01-25 17:53:01 -080039 return true;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080040}
41
42static int i2c_generic_write_gpio(struct acpi_gpio *gpio, int *curr_index)
43{
44 int ret = -1;
45
46 if (gpio->pin_count == 0)
47 return ret;
48
49 acpi_device_write_gpio(gpio);
50 ret = *curr_index;
51 (*curr_index)++;
52
53 return ret;
54}
55
Furquan Shaikh1d334882016-10-21 16:24:07 -070056void i2c_generic_fill_ssdt(struct device *dev,
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -080057 void (*callback)(struct device *dev),
58 struct drivers_i2c_generic_config *config)
Duncan Laurie21a097a2016-05-11 09:50:59 -070059{
Duncan Laurie21a097a2016-05-11 09:50:59 -070060 const char *scope = acpi_device_scope(dev);
61 struct acpi_i2c i2c = {
62 .address = dev->path.i2c.device,
63 .mode_10bit = dev->path.i2c.mode_10bit,
Duncan Laurie14b2a4d2016-06-08 13:49:40 -070064 .speed = config->speed ? : I2C_SPEED_FAST,
Duncan Laurie21a097a2016-05-11 09:50:59 -070065 .resource = scope,
66 };
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -070067 struct acpi_dp *dsd = NULL;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080068 int curr_index = 0;
Duncan Laurieb94e9352017-03-15 11:25:31 -070069 int reset_gpio_index = -1, enable_gpio_index = -1, irq_gpio_index = -1;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080070 const char *path = acpi_device_path(dev);
Duncan Laurie21a097a2016-05-11 09:50:59 -070071
72 if (!dev->enabled || !scope)
73 return;
74
75 if (!config->hid) {
76 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
77 return;
78 }
79
80 /* Device */
81 acpigen_write_scope(scope);
82 acpigen_write_device(acpi_device_name(dev));
83 acpigen_write_name_string("_HID", config->hid);
Furquan Shaikh1d334882016-10-21 16:24:07 -070084 if (config->cid)
85 acpigen_write_name_string("_CID", config->cid);
Duncan Laurie21a097a2016-05-11 09:50:59 -070086 acpigen_write_name_integer("_UID", config->uid);
87 acpigen_write_name_string("_DDN", config->desc);
Hung-Te Linb4be50c2018-09-10 10:55:49 +080088 acpigen_write_STA(acpi_device_status(dev));
Duncan Laurie21a097a2016-05-11 09:50:59 -070089
90 /* Resources */
91 acpigen_write_name("_CRS");
92 acpigen_write_resourcetemplate_header();
93 acpi_device_write_i2c(&i2c);
Duncan Laurieb94e9352017-03-15 11:25:31 -070094
95 /* Use either Interrupt() or GpioInt() */
96 if (config->irq_gpio.pin_count)
97 irq_gpio_index = i2c_generic_write_gpio(&config->irq_gpio,
98 &curr_index);
99 else
100 acpi_device_write_interrupt(&config->irq);
101
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800102 if (i2c_generic_add_gpios_to_crs(config) == true) {
103 reset_gpio_index = i2c_generic_write_gpio(&config->reset_gpio,
104 &curr_index);
105 enable_gpio_index = i2c_generic_write_gpio(&config->enable_gpio,
106 &curr_index);
107 }
Duncan Laurie21a097a2016-05-11 09:50:59 -0700108 acpigen_write_resourcetemplate_footer();
109
110 /* Wake capabilities */
111 if (config->wake) {
112 acpigen_write_name_integer("_S0W", 4);
113 acpigen_write_PRW(config->wake, 3);
114 }
115
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800116 /* DSD */
Furquan Shaikh8ce36ac2019-08-09 07:59:06 -0700117 if (config->probed || config->property_count || config->compat_string ||
Duncan Laurie1533a3c2017-08-29 08:28:58 -0700118 (reset_gpio_index != -1) ||
Duncan Laurieb94e9352017-03-15 11:25:31 -0700119 (enable_gpio_index != -1) || (irq_gpio_index != -1)) {
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700120 dsd = acpi_dp_new_table("_DSD");
Furquan Shaikh8ce36ac2019-08-09 07:59:06 -0700121 if (config->compat_string)
122 acpi_dp_add_string(dsd, "compatible",
123 config->compat_string);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800124 if (config->probed)
125 acpi_dp_add_integer(dsd, "linux,probed", 1);
Duncan Laurieb94e9352017-03-15 11:25:31 -0700126 if (irq_gpio_index != -1)
127 acpi_dp_add_gpio(dsd, "irq-gpios", path,
128 irq_gpio_index, 0,
129 config->irq_gpio.polarity ==
130 ACPI_GPIO_ACTIVE_LOW);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800131 if (reset_gpio_index != -1)
132 acpi_dp_add_gpio(dsd, "reset-gpios", path,
133 reset_gpio_index, 0,
134 config->reset_gpio.polarity);
135 if (enable_gpio_index != -1)
136 acpi_dp_add_gpio(dsd, "enable-gpios", path,
137 enable_gpio_index, 0,
138 config->enable_gpio.polarity);
Duncan Laurie1533a3c2017-08-29 08:28:58 -0700139 /* Add generic property list */
140 acpi_dp_add_property_list(dsd, config->property_list,
141 config->property_count);
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700142 acpi_dp_write(dsd);
143 }
144
Furquan Shaikh626ad202016-10-20 16:01:04 -0700145 /* Power Resource */
Shelley Chena0603392018-04-26 13:52:30 -0700146 if (config->has_power_resource) {
147 const struct acpi_power_res_params power_res_params = {
148 &config->reset_gpio,
149 config->reset_delay_ms,
150 config->reset_off_delay_ms,
151 &config->enable_gpio,
152 config->enable_delay_ms,
153 config->enable_off_delay_ms,
154 &config->stop_gpio,
155 config->stop_delay_ms,
156 config->stop_off_delay_ms
157 };
158 acpi_device_add_power_res(&power_res_params);
159 }
Furquan Shaikh626ad202016-10-20 16:01:04 -0700160
Furquan Shaikh1d334882016-10-21 16:24:07 -0700161 /* Callback if any. */
162 if (callback)
163 callback(dev);
164
Duncan Laurie21a097a2016-05-11 09:50:59 -0700165 acpigen_pop_len(); /* Device */
166 acpigen_pop_len(); /* Scope */
167
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800168 printk(BIOS_INFO, "%s: %s at %s\n", path,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700169 config->desc ? : dev->chip_ops->name, dev_path(dev));
170}
171
Furquan Shaikh1d334882016-10-21 16:24:07 -0700172static void i2c_generic_fill_ssdt_generator(struct device *dev)
173{
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -0800174 i2c_generic_fill_ssdt(dev, NULL, dev->chip_info);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700175}
176
Duncan Laurie21a097a2016-05-11 09:50:59 -0700177/* Use name specified in config or build one from I2C address */
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600178static const char *i2c_generic_acpi_name(const struct device *dev)
Duncan Laurie21a097a2016-05-11 09:50:59 -0700179{
180 struct drivers_i2c_generic_config *config = dev->chip_info;
181 static char name[5];
182
183 if (config->name)
Furquan Shaikh3f4e6272016-10-18 15:11:32 -0700184 return config->name;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700185
186 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
187 name[4] = '\0';
188 return name;
189}
190#endif
191
192static struct device_operations i2c_generic_ops = {
Nico Huber68680dd2020-03-31 17:34:52 +0200193 .read_resources = DEVICE_NOOP,
194 .set_resources = DEVICE_NOOP,
195 .enable_resources = DEVICE_NOOP,
Julius Wernercd49cce2019-03-05 16:53:33 -0800196#if CONFIG(HAVE_ACPI_TABLES)
Nico Huber68680dd2020-03-31 17:34:52 +0200197 .acpi_name = i2c_generic_acpi_name,
198 .acpi_fill_ssdt = i2c_generic_fill_ssdt_generator,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700199#endif
200};
201
202static void i2c_generic_enable(struct device *dev)
203{
204 struct drivers_i2c_generic_config *config = dev->chip_info;
205
Furquan Shaikh24681f12018-06-10 13:33:44 -0700206 if (!config)
207 return;
208
Duncan Laurie21a097a2016-05-11 09:50:59 -0700209 /* Check if device is present by reading GPIO */
210 if (config->device_present_gpio) {
211 int present = gpio_get(config->device_present_gpio);
212 present ^= config->device_present_gpio_invert;
213
214 printk(BIOS_INFO, "%s is %spresent\n",
215 dev->chip_ops->name, present ? "" : "not ");
216
217 if (!present) {
218 dev->enabled = 0;
219 return;
220 }
221 }
222
223 dev->ops = &i2c_generic_ops;
Naresh G Solanki69e9e712018-06-04 17:45:19 +0530224
225 /* Name the device as per description provided in devicetree */
Furquan Shaikh24681f12018-06-10 13:33:44 -0700226 if (config->desc)
Naresh G Solanki69e9e712018-06-04 17:45:19 +0530227 dev->name = config->desc;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700228}
229
230struct chip_operations drivers_i2c_generic_ops = {
231 CHIP_NAME("I2C Device")
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100232 .enable_dev = i2c_generic_enable
Duncan Laurie21a097a2016-05-11 09:50:59 -0700233};