blob: 30a280bcff8dbf0c5a9fb9e14a450e6f9cff026e [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)
28static void i2c_generic_fill_ssdt(struct device *dev)
29{
30 struct drivers_i2c_generic_config *config = dev->chip_info;
31 const char *scope = acpi_device_scope(dev);
32 struct acpi_i2c i2c = {
33 .address = dev->path.i2c.device,
34 .mode_10bit = dev->path.i2c.mode_10bit,
Duncan Laurie14b2a4d2016-06-08 13:49:40 -070035 .speed = config->speed ? : I2C_SPEED_FAST,
Duncan Laurie21a097a2016-05-11 09:50:59 -070036 .resource = scope,
37 };
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -070038 struct acpi_dp *dsd = NULL;
Duncan Laurie21a097a2016-05-11 09:50:59 -070039
40 if (!dev->enabled || !scope)
41 return;
42
43 if (!config->hid) {
44 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
45 return;
46 }
47
48 /* Device */
49 acpigen_write_scope(scope);
50 acpigen_write_device(acpi_device_name(dev));
51 acpigen_write_name_string("_HID", config->hid);
52 acpigen_write_name_integer("_UID", config->uid);
53 acpigen_write_name_string("_DDN", config->desc);
54 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
55
56 /* Resources */
57 acpigen_write_name("_CRS");
58 acpigen_write_resourcetemplate_header();
59 acpi_device_write_i2c(&i2c);
60 acpi_device_write_interrupt(&config->irq);
61 acpigen_write_resourcetemplate_footer();
62
63 /* Wake capabilities */
64 if (config->wake) {
65 acpigen_write_name_integer("_S0W", 4);
66 acpigen_write_PRW(config->wake, 3);
67 }
68
Duncan Lauriefbf2c79b2016-09-26 10:31:22 -070069 if (config->probed) {
70 dsd = acpi_dp_new_table("_DSD");
71 acpi_dp_add_integer(dsd, "linux,probed", 1);
72 acpi_dp_write(dsd);
73 }
74
Duncan Laurie21a097a2016-05-11 09:50:59 -070075 acpigen_pop_len(); /* Device */
76 acpigen_pop_len(); /* Scope */
77
78 printk(BIOS_INFO, "%s: %s at %s\n", acpi_device_path(dev),
79 config->desc ? : dev->chip_ops->name, dev_path(dev));
80}
81
82/* Use name specified in config or build one from I2C address */
83static const char *i2c_generic_acpi_name(struct device *dev)
84{
85 struct drivers_i2c_generic_config *config = dev->chip_info;
86 static char name[5];
87
88 if (config->name)
89 return name;
90
91 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
92 name[4] = '\0';
93 return name;
94}
95#endif
96
97static struct device_operations i2c_generic_ops = {
98 .read_resources = DEVICE_NOOP,
99 .set_resources = DEVICE_NOOP,
100 .enable_resources = DEVICE_NOOP,
101#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
102 .acpi_name = &i2c_generic_acpi_name,
103 .acpi_fill_ssdt_generator = &i2c_generic_fill_ssdt,
104#endif
105};
106
107static void i2c_generic_enable(struct device *dev)
108{
109 struct drivers_i2c_generic_config *config = dev->chip_info;
110
111 /* Check if device is present by reading GPIO */
112 if (config->device_present_gpio) {
113 int present = gpio_get(config->device_present_gpio);
114 present ^= config->device_present_gpio_invert;
115
116 printk(BIOS_INFO, "%s is %spresent\n",
117 dev->chip_ops->name, present ? "" : "not ");
118
119 if (!present) {
120 dev->enabled = 0;
121 return;
122 }
123 }
124
125 dev->ops = &i2c_generic_ops;
126}
127
128struct chip_operations drivers_i2c_generic_ops = {
129 CHIP_NAME("I2C Device")
130 .enable_dev = &i2c_generic_enable
131};