blob: 13f83935f9c3509e2a3cc008cd8a55b6fc9e142d [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);
90 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
91
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 */
Duncan Lauriebd73dbb2017-02-17 17:05:03 -0800145 if (config->has_power_resource)
146 acpi_device_add_power_res(
147 &config->reset_gpio, config->reset_delay_ms,
Furquan Shaikhedf459f2017-08-28 17:20:49 -0700148 &config->enable_gpio, config->enable_delay_ms,
149 &config->stop_gpio, config->stop_delay_ms);
Furquan Shaikh626ad202016-10-20 16:01:04 -0700150
Furquan Shaikh1d334882016-10-21 16:24:07 -0700151 /* Callback if any. */
152 if (callback)
153 callback(dev);
154
Duncan Laurie21a097a2016-05-11 09:50:59 -0700155 acpigen_pop_len(); /* Device */
156 acpigen_pop_len(); /* Scope */
157
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800158 printk(BIOS_INFO, "%s: %s at %s\n", path,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700159 config->desc ? : dev->chip_ops->name, dev_path(dev));
160}
161
Furquan Shaikh1d334882016-10-21 16:24:07 -0700162static void i2c_generic_fill_ssdt_generator(struct device *dev)
163{
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -0800164 i2c_generic_fill_ssdt(dev, NULL, dev->chip_info);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700165}
166
Duncan Laurie21a097a2016-05-11 09:50:59 -0700167/* Use name specified in config or build one from I2C address */
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600168static const char *i2c_generic_acpi_name(const struct device *dev)
Duncan Laurie21a097a2016-05-11 09:50:59 -0700169{
170 struct drivers_i2c_generic_config *config = dev->chip_info;
171 static char name[5];
172
173 if (config->name)
Furquan Shaikh3f4e6272016-10-18 15:11:32 -0700174 return config->name;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700175
176 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
177 name[4] = '\0';
178 return name;
179}
180#endif
181
182static struct device_operations i2c_generic_ops = {
183 .read_resources = DEVICE_NOOP,
184 .set_resources = DEVICE_NOOP,
185 .enable_resources = DEVICE_NOOP,
186#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
187 .acpi_name = &i2c_generic_acpi_name,
Furquan Shaikh1d334882016-10-21 16:24:07 -0700188 .acpi_fill_ssdt_generator = &i2c_generic_fill_ssdt_generator,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700189#endif
190};
191
192static void i2c_generic_enable(struct device *dev)
193{
194 struct drivers_i2c_generic_config *config = dev->chip_info;
195
196 /* Check if device is present by reading GPIO */
197 if (config->device_present_gpio) {
198 int present = gpio_get(config->device_present_gpio);
199 present ^= config->device_present_gpio_invert;
200
201 printk(BIOS_INFO, "%s is %spresent\n",
202 dev->chip_ops->name, present ? "" : "not ");
203
204 if (!present) {
205 dev->enabled = 0;
206 return;
207 }
208 }
209
210 dev->ops = &i2c_generic_ops;
211}
212
213struct chip_operations drivers_i2c_generic_ops = {
214 CHIP_NAME("I2C Device")
215 .enable_dev = &i2c_generic_enable
216};