blob: a90947f1b4a9507cb255da1ea0354038b7943948 [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 Shaikh71d830f2017-01-25 17:53:01 -080035 if (!config->has_power_resource)
Furquan Shaikh98915bb2016-12-12 09:23:01 -080036 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{
Furquan Shaikh71d830f2017-01-25 17:53:01 -080075 /*
76 * Return false if:
77 * 1. Request to explicitly disable export of GPIOs in CRS, or
78 * 2. Both reset and enable GPIOs are not provided.
79 */
80 if (cfg->disable_gpio_export_in_crs ||
81 ((cfg->reset_gpio.pin_count == 0) &&
82 (cfg->enable_gpio.pin_count == 0)))
83 return false;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080084
Furquan Shaikh71d830f2017-01-25 17:53:01 -080085 return true;
Furquan Shaikh98915bb2016-12-12 09:23:01 -080086}
87
88static int i2c_generic_write_gpio(struct acpi_gpio *gpio, int *curr_index)
89{
90 int ret = -1;
91
92 if (gpio->pin_count == 0)
93 return ret;
94
95 acpi_device_write_gpio(gpio);
96 ret = *curr_index;
97 (*curr_index)++;
98
99 return ret;
100}
101
Furquan Shaikh1d334882016-10-21 16:24:07 -0700102void i2c_generic_fill_ssdt(struct device *dev,
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -0800103 void (*callback)(struct device *dev),
104 struct drivers_i2c_generic_config *config)
Duncan Laurie21a097a2016-05-11 09:50:59 -0700105{
Duncan Laurie21a097a2016-05-11 09:50:59 -0700106 const char *scope = acpi_device_scope(dev);
107 struct acpi_i2c i2c = {
108 .address = dev->path.i2c.device,
109 .mode_10bit = dev->path.i2c.mode_10bit,
Duncan Laurie14b2a4d2016-06-08 13:49:40 -0700110 .speed = config->speed ? : I2C_SPEED_FAST,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700111 .resource = scope,
112 };
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700113 struct acpi_dp *dsd = NULL;
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800114 int curr_index = 0;
115 int reset_gpio_index = -1, enable_gpio_index = -1;
116 const char *path = acpi_device_path(dev);
Duncan Laurie21a097a2016-05-11 09:50:59 -0700117
118 if (!dev->enabled || !scope)
119 return;
120
121 if (!config->hid) {
122 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
123 return;
124 }
125
126 /* Device */
127 acpigen_write_scope(scope);
128 acpigen_write_device(acpi_device_name(dev));
129 acpigen_write_name_string("_HID", config->hid);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700130 if (config->cid)
131 acpigen_write_name_string("_CID", config->cid);
Duncan Laurie21a097a2016-05-11 09:50:59 -0700132 acpigen_write_name_integer("_UID", config->uid);
133 acpigen_write_name_string("_DDN", config->desc);
134 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
135
136 /* Resources */
137 acpigen_write_name("_CRS");
138 acpigen_write_resourcetemplate_header();
139 acpi_device_write_i2c(&i2c);
140 acpi_device_write_interrupt(&config->irq);
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800141 if (i2c_generic_add_gpios_to_crs(config) == true) {
142 reset_gpio_index = i2c_generic_write_gpio(&config->reset_gpio,
143 &curr_index);
144 enable_gpio_index = i2c_generic_write_gpio(&config->enable_gpio,
145 &curr_index);
146 }
Duncan Laurie21a097a2016-05-11 09:50:59 -0700147 acpigen_write_resourcetemplate_footer();
148
149 /* Wake capabilities */
150 if (config->wake) {
151 acpigen_write_name_integer("_S0W", 4);
152 acpigen_write_PRW(config->wake, 3);
153 }
154
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800155 /* DSD */
156 if (config->probed || (reset_gpio_index != -1) ||
157 (enable_gpio_index != -1)) {
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700158 dsd = acpi_dp_new_table("_DSD");
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800159 if (config->probed)
160 acpi_dp_add_integer(dsd, "linux,probed", 1);
161 if (reset_gpio_index != -1)
162 acpi_dp_add_gpio(dsd, "reset-gpios", path,
163 reset_gpio_index, 0,
164 config->reset_gpio.polarity);
165 if (enable_gpio_index != -1)
166 acpi_dp_add_gpio(dsd, "enable-gpios", path,
167 enable_gpio_index, 0,
168 config->enable_gpio.polarity);
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700169 acpi_dp_write(dsd);
170 }
171
Furquan Shaikh626ad202016-10-20 16:01:04 -0700172 /* Power Resource */
173 i2c_generic_add_power_res(config);
174
Furquan Shaikh1d334882016-10-21 16:24:07 -0700175 /* Callback if any. */
176 if (callback)
177 callback(dev);
178
Duncan Laurie21a097a2016-05-11 09:50:59 -0700179 acpigen_pop_len(); /* Device */
180 acpigen_pop_len(); /* Scope */
181
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800182 printk(BIOS_INFO, "%s: %s at %s\n", path,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700183 config->desc ? : dev->chip_ops->name, dev_path(dev));
184}
185
Furquan Shaikh1d334882016-10-21 16:24:07 -0700186static void i2c_generic_fill_ssdt_generator(struct device *dev)
187{
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -0800188 i2c_generic_fill_ssdt(dev, NULL, dev->chip_info);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700189}
190
Duncan Laurie21a097a2016-05-11 09:50:59 -0700191/* Use name specified in config or build one from I2C address */
192static const char *i2c_generic_acpi_name(struct device *dev)
193{
194 struct drivers_i2c_generic_config *config = dev->chip_info;
195 static char name[5];
196
197 if (config->name)
Furquan Shaikh3f4e6272016-10-18 15:11:32 -0700198 return config->name;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700199
200 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
201 name[4] = '\0';
202 return name;
203}
204#endif
205
206static struct device_operations i2c_generic_ops = {
207 .read_resources = DEVICE_NOOP,
208 .set_resources = DEVICE_NOOP,
209 .enable_resources = DEVICE_NOOP,
210#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
211 .acpi_name = &i2c_generic_acpi_name,
Furquan Shaikh1d334882016-10-21 16:24:07 -0700212 .acpi_fill_ssdt_generator = &i2c_generic_fill_ssdt_generator,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700213#endif
214};
215
216static void i2c_generic_enable(struct device *dev)
217{
218 struct drivers_i2c_generic_config *config = dev->chip_info;
219
220 /* Check if device is present by reading GPIO */
221 if (config->device_present_gpio) {
222 int present = gpio_get(config->device_present_gpio);
223 present ^= config->device_present_gpio_invert;
224
225 printk(BIOS_INFO, "%s is %spresent\n",
226 dev->chip_ops->name, present ? "" : "not ");
227
228 if (!present) {
229 dev->enabled = 0;
230 return;
231 }
232 }
233
234 dev->ops = &i2c_generic_ops;
235}
236
237struct chip_operations drivers_i2c_generic_ops = {
238 CHIP_NAME("I2C Device")
239 .enable_dev = &i2c_generic_enable
240};