blob: ca2dfafdc5561b6614913d020ad43970972b8773 [file] [log] [blame]
Duncan Laurie21a097a2016-05-11 09:50:59 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2016 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <arch/acpi_device.h>
17#include <arch/acpigen.h>
18#include <console/console.h>
Nico Huber0f2dd1e2017-08-01 14:02:40 +020019#include <device/i2c_simple.h>
Duncan Laurie21a097a2016-05-11 09:50:59 -070020#include <device/device.h>
21#include <device/path.h>
22#include <gpio.h>
23#include <stdint.h>
24#include <string.h>
25#include "chip.h"
26
27#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
Furquan Shaikh626ad202016-10-20 16:01:04 -070028
Furquan Shaikh98915bb2016-12-12 09:23:01 -080029static bool i2c_generic_add_gpios_to_crs(struct drivers_i2c_generic_config *cfg)
30{
Furquan Shaikh71d830f2017-01-25 17:53:01 -080031 /*
32 * Return false if:
33 * 1. Request to explicitly disable export of GPIOs in CRS, or
34 * 2. Both reset and enable GPIOs are not provided.
35 */
36 if (cfg->disable_gpio_export_in_crs ||
37 ((cfg->reset_gpio.pin_count == 0) &&
38 (cfg->enable_gpio.pin_count == 0)))
39 return false;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080040
Furquan Shaikh71d830f2017-01-25 17:53:01 -080041 return true;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080042}
43
44static int i2c_generic_write_gpio(struct acpi_gpio *gpio, int *curr_index)
45{
46 int ret = -1;
47
48 if (gpio->pin_count == 0)
49 return ret;
50
51 acpi_device_write_gpio(gpio);
52 ret = *curr_index;
53 (*curr_index)++;
54
55 return ret;
56}
57
Furquan Shaikh1d334882016-10-21 16:24:07 -070058void i2c_generic_fill_ssdt(struct device *dev,
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -080059 void (*callback)(struct device *dev),
60 struct drivers_i2c_generic_config *config)
Duncan Laurie21a097a2016-05-11 09:50:59 -070061{
Duncan Laurie21a097a2016-05-11 09:50:59 -070062 const char *scope = acpi_device_scope(dev);
63 struct acpi_i2c i2c = {
64 .address = dev->path.i2c.device,
65 .mode_10bit = dev->path.i2c.mode_10bit,
Duncan Laurie14b2a4d2016-06-08 13:49:40 -070066 .speed = config->speed ? : I2C_SPEED_FAST,
Duncan Laurie21a097a2016-05-11 09:50:59 -070067 .resource = scope,
68 };
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -070069 struct acpi_dp *dsd = NULL;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080070 int curr_index = 0;
Duncan Laurieb94e9352017-03-15 11:25:31 -070071 int reset_gpio_index = -1, enable_gpio_index = -1, irq_gpio_index = -1;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080072 const char *path = acpi_device_path(dev);
Duncan Laurie21a097a2016-05-11 09:50:59 -070073
74 if (!dev->enabled || !scope)
75 return;
76
77 if (!config->hid) {
78 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
79 return;
80 }
81
82 /* Device */
83 acpigen_write_scope(scope);
84 acpigen_write_device(acpi_device_name(dev));
85 acpigen_write_name_string("_HID", config->hid);
Furquan Shaikh1d334882016-10-21 16:24:07 -070086 if (config->cid)
87 acpigen_write_name_string("_CID", config->cid);
Duncan Laurie21a097a2016-05-11 09:50:59 -070088 acpigen_write_name_integer("_UID", config->uid);
89 acpigen_write_name_string("_DDN", config->desc);
Hung-Te Linb4be50c2018-09-10 10:55:49 +080090 acpigen_write_STA(acpi_device_status(dev));
Duncan Laurie21a097a2016-05-11 09:50:59 -070091
92 /* Resources */
93 acpigen_write_name("_CRS");
94 acpigen_write_resourcetemplate_header();
95 acpi_device_write_i2c(&i2c);
Duncan Laurieb94e9352017-03-15 11:25:31 -070096
97 /* Use either Interrupt() or GpioInt() */
98 if (config->irq_gpio.pin_count)
99 irq_gpio_index = i2c_generic_write_gpio(&config->irq_gpio,
100 &curr_index);
101 else
102 acpi_device_write_interrupt(&config->irq);
103
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800104 if (i2c_generic_add_gpios_to_crs(config) == true) {
105 reset_gpio_index = i2c_generic_write_gpio(&config->reset_gpio,
106 &curr_index);
107 enable_gpio_index = i2c_generic_write_gpio(&config->enable_gpio,
108 &curr_index);
109 }
Duncan Laurie21a097a2016-05-11 09:50:59 -0700110 acpigen_write_resourcetemplate_footer();
111
112 /* Wake capabilities */
113 if (config->wake) {
114 acpigen_write_name_integer("_S0W", 4);
115 acpigen_write_PRW(config->wake, 3);
116 }
117
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800118 /* DSD */
Duncan Laurie1533a3c2017-08-29 08:28:58 -0700119 if (config->probed || config->property_count ||
120 (reset_gpio_index != -1) ||
Duncan Laurieb94e9352017-03-15 11:25:31 -0700121 (enable_gpio_index != -1) || (irq_gpio_index != -1)) {
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700122 dsd = acpi_dp_new_table("_DSD");
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800123 if (config->probed)
124 acpi_dp_add_integer(dsd, "linux,probed", 1);
Duncan Laurieb94e9352017-03-15 11:25:31 -0700125 if (irq_gpio_index != -1)
126 acpi_dp_add_gpio(dsd, "irq-gpios", path,
127 irq_gpio_index, 0,
128 config->irq_gpio.polarity ==
129 ACPI_GPIO_ACTIVE_LOW);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800130 if (reset_gpio_index != -1)
131 acpi_dp_add_gpio(dsd, "reset-gpios", path,
132 reset_gpio_index, 0,
133 config->reset_gpio.polarity);
134 if (enable_gpio_index != -1)
135 acpi_dp_add_gpio(dsd, "enable-gpios", path,
136 enable_gpio_index, 0,
137 config->enable_gpio.polarity);
Duncan Laurie1533a3c2017-08-29 08:28:58 -0700138 /* Add generic property list */
139 acpi_dp_add_property_list(dsd, config->property_list,
140 config->property_count);
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700141 acpi_dp_write(dsd);
142 }
143
Furquan Shaikh626ad202016-10-20 16:01:04 -0700144 /* Power Resource */
Shelley Chena0603392018-04-26 13:52:30 -0700145 if (config->has_power_resource) {
146 const struct acpi_power_res_params power_res_params = {
147 &config->reset_gpio,
148 config->reset_delay_ms,
149 config->reset_off_delay_ms,
150 &config->enable_gpio,
151 config->enable_delay_ms,
152 config->enable_off_delay_ms,
153 &config->stop_gpio,
154 config->stop_delay_ms,
155 config->stop_off_delay_ms
156 };
157 acpi_device_add_power_res(&power_res_params);
158 }
Furquan Shaikh626ad202016-10-20 16:01:04 -0700159
Furquan Shaikh1d334882016-10-21 16:24:07 -0700160 /* Callback if any. */
161 if (callback)
162 callback(dev);
163
Duncan Laurie21a097a2016-05-11 09:50:59 -0700164 acpigen_pop_len(); /* Device */
165 acpigen_pop_len(); /* Scope */
166
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800167 printk(BIOS_INFO, "%s: %s at %s\n", path,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700168 config->desc ? : dev->chip_ops->name, dev_path(dev));
169}
170
Furquan Shaikh1d334882016-10-21 16:24:07 -0700171static void i2c_generic_fill_ssdt_generator(struct device *dev)
172{
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -0800173 i2c_generic_fill_ssdt(dev, NULL, dev->chip_info);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700174}
175
Duncan Laurie21a097a2016-05-11 09:50:59 -0700176/* Use name specified in config or build one from I2C address */
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600177static const char *i2c_generic_acpi_name(const struct device *dev)
Duncan Laurie21a097a2016-05-11 09:50:59 -0700178{
179 struct drivers_i2c_generic_config *config = dev->chip_info;
180 static char name[5];
181
182 if (config->name)
Furquan Shaikh3f4e6272016-10-18 15:11:32 -0700183 return config->name;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700184
185 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
186 name[4] = '\0';
187 return name;
188}
189#endif
190
191static struct device_operations i2c_generic_ops = {
192 .read_resources = DEVICE_NOOP,
193 .set_resources = DEVICE_NOOP,
194 .enable_resources = DEVICE_NOOP,
195#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100196 .acpi_name = i2c_generic_acpi_name,
197 .acpi_fill_ssdt_generator = i2c_generic_fill_ssdt_generator,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700198#endif
199};
200
201static void i2c_generic_enable(struct device *dev)
202{
203 struct drivers_i2c_generic_config *config = dev->chip_info;
204
Furquan Shaikh24681f12018-06-10 13:33:44 -0700205 if (!config)
206 return;
207
Duncan Laurie21a097a2016-05-11 09:50:59 -0700208 /* Check if device is present by reading GPIO */
209 if (config->device_present_gpio) {
210 int present = gpio_get(config->device_present_gpio);
211 present ^= config->device_present_gpio_invert;
212
213 printk(BIOS_INFO, "%s is %spresent\n",
214 dev->chip_ops->name, present ? "" : "not ");
215
216 if (!present) {
217 dev->enabled = 0;
218 return;
219 }
220 }
221
222 dev->ops = &i2c_generic_ops;
Naresh G Solanki69e9e712018-06-04 17:45:19 +0530223
224 /* Name the device as per description provided in devicetree */
Furquan Shaikh24681f12018-06-10 13:33:44 -0700225 if (config->desc)
Naresh G Solanki69e9e712018-06-04 17:45:19 +0530226 dev->name = config->desc;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700227}
228
229struct chip_operations drivers_i2c_generic_ops = {
230 CHIP_NAME("I2C Device")
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100231 .enable_dev = i2c_generic_enable
Duncan Laurie21a097a2016-05-11 09:50:59 -0700232};