blob: dea78d5e892e1e68e09f06659873de1b692666bd [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
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;
71 int reset_gpio_index = -1, enable_gpio_index = -1;
72 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);
96 acpi_device_write_interrupt(&config->irq);
Furquan Shaikh98915bb2016-12-12 09:23:01 -080097 if (i2c_generic_add_gpios_to_crs(config) == true) {
98 reset_gpio_index = i2c_generic_write_gpio(&config->reset_gpio,
99 &curr_index);
100 enable_gpio_index = i2c_generic_write_gpio(&config->enable_gpio,
101 &curr_index);
102 }
Duncan Laurie21a097a2016-05-11 09:50:59 -0700103 acpigen_write_resourcetemplate_footer();
104
105 /* Wake capabilities */
106 if (config->wake) {
107 acpigen_write_name_integer("_S0W", 4);
108 acpigen_write_PRW(config->wake, 3);
109 }
110
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800111 /* DSD */
112 if (config->probed || (reset_gpio_index != -1) ||
113 (enable_gpio_index != -1)) {
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700114 dsd = acpi_dp_new_table("_DSD");
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800115 if (config->probed)
116 acpi_dp_add_integer(dsd, "linux,probed", 1);
117 if (reset_gpio_index != -1)
118 acpi_dp_add_gpio(dsd, "reset-gpios", path,
119 reset_gpio_index, 0,
120 config->reset_gpio.polarity);
121 if (enable_gpio_index != -1)
122 acpi_dp_add_gpio(dsd, "enable-gpios", path,
123 enable_gpio_index, 0,
124 config->enable_gpio.polarity);
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -0700125 acpi_dp_write(dsd);
126 }
127
Furquan Shaikh626ad202016-10-20 16:01:04 -0700128 /* Power Resource */
Duncan Lauriebd73dbb2017-02-17 17:05:03 -0800129 if (config->has_power_resource)
130 acpi_device_add_power_res(
131 &config->reset_gpio, config->reset_delay_ms,
132 &config->enable_gpio, config->enable_delay_ms);
Furquan Shaikh626ad202016-10-20 16:01:04 -0700133
Furquan Shaikh1d334882016-10-21 16:24:07 -0700134 /* Callback if any. */
135 if (callback)
136 callback(dev);
137
Duncan Laurie21a097a2016-05-11 09:50:59 -0700138 acpigen_pop_len(); /* Device */
139 acpigen_pop_len(); /* Scope */
140
Furquan Shaikh98915bb2016-12-12 09:23:01 -0800141 printk(BIOS_INFO, "%s: %s at %s\n", path,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700142 config->desc ? : dev->chip_ops->name, dev_path(dev));
143}
144
Furquan Shaikh1d334882016-10-21 16:24:07 -0700145static void i2c_generic_fill_ssdt_generator(struct device *dev)
146{
Furquan Shaikhfdc1b2e2016-12-13 21:50:32 -0800147 i2c_generic_fill_ssdt(dev, NULL, dev->chip_info);
Furquan Shaikh1d334882016-10-21 16:24:07 -0700148}
149
Duncan Laurie21a097a2016-05-11 09:50:59 -0700150/* Use name specified in config or build one from I2C address */
151static const char *i2c_generic_acpi_name(struct device *dev)
152{
153 struct drivers_i2c_generic_config *config = dev->chip_info;
154 static char name[5];
155
156 if (config->name)
Furquan Shaikh3f4e6272016-10-18 15:11:32 -0700157 return config->name;
Duncan Laurie21a097a2016-05-11 09:50:59 -0700158
159 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
160 name[4] = '\0';
161 return name;
162}
163#endif
164
165static struct device_operations i2c_generic_ops = {
166 .read_resources = DEVICE_NOOP,
167 .set_resources = DEVICE_NOOP,
168 .enable_resources = DEVICE_NOOP,
169#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
170 .acpi_name = &i2c_generic_acpi_name,
Furquan Shaikh1d334882016-10-21 16:24:07 -0700171 .acpi_fill_ssdt_generator = &i2c_generic_fill_ssdt_generator,
Duncan Laurie21a097a2016-05-11 09:50:59 -0700172#endif
173};
174
175static void i2c_generic_enable(struct device *dev)
176{
177 struct drivers_i2c_generic_config *config = dev->chip_info;
178
179 /* Check if device is present by reading GPIO */
180 if (config->device_present_gpio) {
181 int present = gpio_get(config->device_present_gpio);
182 present ^= config->device_present_gpio_invert;
183
184 printk(BIOS_INFO, "%s is %spresent\n",
185 dev->chip_ops->name, present ? "" : "not ");
186
187 if (!present) {
188 dev->enabled = 0;
189 return;
190 }
191 }
192
193 dev->ops = &i2c_generic_ops;
194}
195
196struct chip_operations drivers_i2c_generic_ops = {
197 CHIP_NAME("I2C Device")
198 .enable_dev = &i2c_generic_enable
199};