blob: 4cf3e9e419402010b5efc0eabba7e5e42153af45 [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>
19#include <device/i2c.h>
20#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
29static void i2c_generic_add_power_res(struct drivers_i2c_generic_config *config)
30{
31 const char *power_res_dev_states[] = { "_PR0", "_PR3" };
Furquan Shaikh98915bb2016-12-12 09:23:01 -080032 unsigned reset_gpio = config->reset_gpio.pins[0];
33 unsigned enable_gpio = config->enable_gpio.pins[0];
Furquan Shaikh626ad202016-10-20 16:01:04 -070034
Furquan Shaikh98915bb2016-12-12 09:23:01 -080035 if (config->pwr_mgmt_type != POWER_RESOURCE)
36 return;
37
38 if (!reset_gpio && !enable_gpio)
Furquan Shaikh626ad202016-10-20 16:01:04 -070039 return;
40
41 /* PowerResource (PRIC, 0, 0) */
42 acpigen_write_power_res("PRIC", 0, 0, power_res_dev_states,
43 ARRAY_SIZE(power_res_dev_states));
44
45 /* Method (_STA, 0, NotSerialized) { Return (0x1) } */
46 acpigen_write_STA(0x1);
47
48 /* Method (_ON, 0, Serialized) */
49 acpigen_write_method_serialized("_ON", 0);
Furquan Shaikh98915bb2016-12-12 09:23:01 -080050 if (reset_gpio)
51 acpigen_soc_set_tx_gpio(reset_gpio);
52 if (enable_gpio) {
53 acpigen_soc_set_tx_gpio(enable_gpio);
Furquan Shaikh626ad202016-10-20 16:01:04 -070054 acpigen_write_sleep(config->enable_delay_ms);
55 }
Furquan Shaikh98915bb2016-12-12 09:23:01 -080056 if (reset_gpio) {
57 acpigen_soc_clear_tx_gpio(reset_gpio);
Furquan Shaikh626ad202016-10-20 16:01:04 -070058 acpigen_write_sleep(config->reset_delay_ms);
59 }
60 acpigen_pop_len(); /* _ON method */
61
62 /* Method (_OFF, 0, Serialized) */
63 acpigen_write_method_serialized("_OFF", 0);
Furquan Shaikh98915bb2016-12-12 09:23:01 -080064 if (reset_gpio)
65 acpigen_soc_set_tx_gpio(reset_gpio);
66 if (enable_gpio)
67 acpigen_soc_clear_tx_gpio(enable_gpio);
Furquan Shaikh626ad202016-10-20 16:01:04 -070068 acpigen_pop_len(); /* _OFF method */
69
70 acpigen_pop_len(); /* PowerResource PRIC */
71}
72
Furquan Shaikh98915bb2016-12-12 09:23:01 -080073static bool i2c_generic_add_gpios_to_crs(struct drivers_i2c_generic_config *cfg)
74{
75 if (cfg->pwr_mgmt_type == GPIO_EXPORT)
76 return true;
77
78 return false;
79}
80
81static int i2c_generic_write_gpio(struct acpi_gpio *gpio, int *curr_index)
82{
83 int ret = -1;
84
85 if (gpio->pin_count == 0)
86 return ret;
87
88 acpi_device_write_gpio(gpio);
89 ret = *curr_index;
90 (*curr_index)++;
91
92 return ret;
93}
94
Furquan Shaikh1d334882016-10-21 16:24:07 -070095void i2c_generic_fill_ssdt(struct device *dev,
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -080096 void (*callback)(struct device *dev),
97 struct drivers_i2c_generic_config *config)
Duncan Laurie21a097a2016-05-11 09:50:59 -070098{
Duncan Laurie21a097a2016-05-11 09:50:59 -070099 const char *scope = acpi_device_scope(dev);
100 struct acpi_i2c i2c = {
101 .address = dev->path.i2c.device,
102 .mode_10bit = dev->path.i2c.mode_10bit,
Duncan Laurie14b2a4d2016-06-08 13:49:40 -0700103 .speed = config->speed ? : I2C_SPEED_FAST,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700104 .resource = scope,
105 };
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700106 struct acpi_dp *dsd = NULL;
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800107 int curr_index = 0;
108 int reset_gpio_index = -1, enable_gpio_index = -1;
109 const char *path = acpi_device_path(dev);
Duncan Laurie21a097a2016-05-11 09:50:59 -0700110
111 if (!dev->enabled || !scope)
112 return;
113
114 if (!config->hid) {
115 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
116 return;
117 }
118
119 /* Device */
120 acpigen_write_scope(scope);
121 acpigen_write_device(acpi_device_name(dev));
122 acpigen_write_name_string("_HID", config->hid);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700123 if (config->cid)
124 acpigen_write_name_string("_CID", config->cid);
Duncan Laurie21a097a2016-05-11 09:50:59 -0700125 acpigen_write_name_integer("_UID", config->uid);
126 acpigen_write_name_string("_DDN", config->desc);
127 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
128
129 /* Resources */
130 acpigen_write_name("_CRS");
131 acpigen_write_resourcetemplate_header();
132 acpi_device_write_i2c(&i2c);
133 acpi_device_write_interrupt(&config->irq);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800134 if (i2c_generic_add_gpios_to_crs(config) == true) {
135 reset_gpio_index = i2c_generic_write_gpio(&config->reset_gpio,
136 &curr_index);
137 enable_gpio_index = i2c_generic_write_gpio(&config->enable_gpio,
138 &curr_index);
139 }
Duncan Laurie21a097a2016-05-11 09:50:59 -0700140 acpigen_write_resourcetemplate_footer();
141
142 /* Wake capabilities */
143 if (config->wake) {
144 acpigen_write_name_integer("_S0W", 4);
145 acpigen_write_PRW(config->wake, 3);
146 }
147
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800148 /* DSD */
149 if (config->probed || (reset_gpio_index != -1) ||
150 (enable_gpio_index != -1)) {
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700151 dsd = acpi_dp_new_table("_DSD");
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800152 if (config->probed)
153 acpi_dp_add_integer(dsd, "linux,probed", 1);
154 if (reset_gpio_index != -1)
155 acpi_dp_add_gpio(dsd, "reset-gpios", path,
156 reset_gpio_index, 0,
157 config->reset_gpio.polarity);
158 if (enable_gpio_index != -1)
159 acpi_dp_add_gpio(dsd, "enable-gpios", path,
160 enable_gpio_index, 0,
161 config->enable_gpio.polarity);
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700162 acpi_dp_write(dsd);
163 }
164
Furquan Shaikh626ad202016-10-20 16:01:04 -0700165 /* Power Resource */
166 i2c_generic_add_power_res(config);
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 Shaikh1d334882016-10-21 16:24:07 -0700179static void i2c_generic_fill_ssdt_generator(struct device *dev)
180{
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 */
185static const char *i2c_generic_acpi_name(struct device *dev)
186{
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 = {
200 .read_resources = DEVICE_NOOP,
201 .set_resources = DEVICE_NOOP,
202 .enable_resources = DEVICE_NOOP,
203#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
204 .acpi_name = &i2c_generic_acpi_name,
Furquan Shaikh1d334882016-10-21 16:24:07 -0700205 .acpi_fill_ssdt_generator = &i2c_generic_fill_ssdt_generator,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700206#endif
207};
208
209static void i2c_generic_enable(struct device *dev)
210{
211 struct drivers_i2c_generic_config *config = dev->chip_info;
212
213 /* Check if device is present by reading GPIO */
214 if (config->device_present_gpio) {
215 int present = gpio_get(config->device_present_gpio);
216 present ^= config->device_present_gpio_invert;
217
218 printk(BIOS_INFO, "%s is %spresent\n",
219 dev->chip_ops->name, present ? "" : "not ");
220
221 if (!present) {
222 dev->enabled = 0;
223 return;
224 }
225 }
226
227 dev->ops = &i2c_generic_ops;
228}
229
230struct chip_operations drivers_i2c_generic_ops = {
231 CHIP_NAME("I2C Device")
232 .enable_dev = &i2c_generic_enable
233};