drivers/i2c/generic: Ensure config is not NULL before accessing it
[coreboot.git] / src / drivers / i2c / generic / generic.c
blob1b12df57d31953a4535ae55a053b58634aa31fe5
1 /*
2 * This file is part of the coreboot project.
4 * Copyright 2016 Google Inc.
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.
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.
16 #include <arch/acpi_device.h>
17 #include <arch/acpigen.h>
18 #include <console/console.h>
19 #include <device/i2c_simple.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"
27 #if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
29 static bool i2c_generic_add_gpios_to_crs(struct drivers_i2c_generic_config *cfg)
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.
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;
41 return true;
44 static int i2c_generic_write_gpio(struct acpi_gpio *gpio, int *curr_index)
46 int ret = -1;
48 if (gpio->pin_count == 0)
49 return ret;
51 acpi_device_write_gpio(gpio);
52 ret = *curr_index;
53 (*curr_index)++;
55 return ret;
58 void i2c_generic_fill_ssdt(struct device *dev,
59 void (*callback)(struct device *dev),
60 struct drivers_i2c_generic_config *config)
62 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,
66 .speed = config->speed ? : I2C_SPEED_FAST,
67 .resource = scope,
69 struct acpi_dp *dsd = NULL;
70 int curr_index = 0;
71 int reset_gpio_index = -1, enable_gpio_index = -1, irq_gpio_index = -1;
72 const char *path = acpi_device_path(dev);
74 if (!dev->enabled || !scope)
75 return;
77 if (!config->hid) {
78 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
79 return;
82 /* Device */
83 acpigen_write_scope(scope);
84 acpigen_write_device(acpi_device_name(dev));
85 acpigen_write_name_string("_HID", config->hid);
86 if (config->cid)
87 acpigen_write_name_string("_CID", config->cid);
88 acpigen_write_name_integer("_UID", config->uid);
89 acpigen_write_name_string("_DDN", config->desc);
90 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
92 /* Resources */
93 acpigen_write_name("_CRS");
94 acpigen_write_resourcetemplate_header();
95 acpi_device_write_i2c(&i2c);
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);
104 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);
110 acpigen_write_resourcetemplate_footer();
112 /* Wake capabilities */
113 if (config->wake) {
114 acpigen_write_name_integer("_S0W", 4);
115 acpigen_write_PRW(config->wake, 3);
118 /* DSD */
119 if (config->probed || config->property_count ||
120 (reset_gpio_index != -1) ||
121 (enable_gpio_index != -1) || (irq_gpio_index != -1)) {
122 dsd = acpi_dp_new_table("_DSD");
123 if (config->probed)
124 acpi_dp_add_integer(dsd, "linux,probed", 1);
125 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);
130 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);
138 /* Add generic property list */
139 acpi_dp_add_property_list(dsd, config->property_list,
140 config->property_count);
141 acpi_dp_write(dsd);
144 /* Power Resource */
145 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
157 acpi_device_add_power_res(&power_res_params);
160 /* Callback if any. */
161 if (callback)
162 callback(dev);
164 acpigen_pop_len(); /* Device */
165 acpigen_pop_len(); /* Scope */
167 printk(BIOS_INFO, "%s: %s at %s\n", path,
168 config->desc ? : dev->chip_ops->name, dev_path(dev));
171 static void i2c_generic_fill_ssdt_generator(struct device *dev)
173 i2c_generic_fill_ssdt(dev, NULL, dev->chip_info);
176 /* Use name specified in config or build one from I2C address */
177 static const char *i2c_generic_acpi_name(const struct device *dev)
179 struct drivers_i2c_generic_config *config = dev->chip_info;
180 static char name[5];
182 if (config->name)
183 return config->name;
185 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
186 name[4] = '\0';
187 return name;
189 #endif
191 static 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)
196 .acpi_name = &i2c_generic_acpi_name,
197 .acpi_fill_ssdt_generator = &i2c_generic_fill_ssdt_generator,
198 #endif
201 static void i2c_generic_enable(struct device *dev)
203 struct drivers_i2c_generic_config *config = dev->chip_info;
205 if (!config)
206 return;
208 /* 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;
213 printk(BIOS_INFO, "%s is %spresent\n",
214 dev->chip_ops->name, present ? "" : "not ");
216 if (!present) {
217 dev->enabled = 0;
218 return;
222 dev->ops = &i2c_generic_ops;
224 /* Name the device as per description provided in devicetree */
225 if (config->desc)
226 dev->name = config->desc;
229 struct chip_operations drivers_i2c_generic_ops = {
230 CHIP_NAME("I2C Device")
231 .enable_dev = &i2c_generic_enable