blob: 62e2e9a813cf01cdbf89767583017286ff21efef [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 };
38
39 if (!dev->enabled || !scope)
40 return;
41
42 if (!config->hid) {
43 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
44 return;
45 }
46
47 /* Device */
48 acpigen_write_scope(scope);
49 acpigen_write_device(acpi_device_name(dev));
50 acpigen_write_name_string("_HID", config->hid);
51 acpigen_write_name_integer("_UID", config->uid);
52 acpigen_write_name_string("_DDN", config->desc);
53 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
54
55 /* Resources */
56 acpigen_write_name("_CRS");
57 acpigen_write_resourcetemplate_header();
58 acpi_device_write_i2c(&i2c);
59 acpi_device_write_interrupt(&config->irq);
60 acpigen_write_resourcetemplate_footer();
61
62 /* Wake capabilities */
63 if (config->wake) {
64 acpigen_write_name_integer("_S0W", 4);
65 acpigen_write_PRW(config->wake, 3);
66 }
67
68 acpigen_pop_len(); /* Device */
69 acpigen_pop_len(); /* Scope */
70
71 printk(BIOS_INFO, "%s: %s at %s\n", acpi_device_path(dev),
72 config->desc ? : dev->chip_ops->name, dev_path(dev));
73}
74
75/* Use name specified in config or build one from I2C address */
76static const char *i2c_generic_acpi_name(struct device *dev)
77{
78 struct drivers_i2c_generic_config *config = dev->chip_info;
79 static char name[5];
80
81 if (config->name)
82 return name;
83
84 snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
85 name[4] = '\0';
86 return name;
87}
88#endif
89
90static struct device_operations i2c_generic_ops = {
91 .read_resources = DEVICE_NOOP,
92 .set_resources = DEVICE_NOOP,
93 .enable_resources = DEVICE_NOOP,
94#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
95 .acpi_name = &i2c_generic_acpi_name,
96 .acpi_fill_ssdt_generator = &i2c_generic_fill_ssdt,
97#endif
98};
99
100static void i2c_generic_enable(struct device *dev)
101{
102 struct drivers_i2c_generic_config *config = dev->chip_info;
103
104 /* Check if device is present by reading GPIO */
105 if (config->device_present_gpio) {
106 int present = gpio_get(config->device_present_gpio);
107 present ^= config->device_present_gpio_invert;
108
109 printk(BIOS_INFO, "%s is %spresent\n",
110 dev->chip_ops->name, present ? "" : "not ");
111
112 if (!present) {
113 dev->enabled = 0;
114 return;
115 }
116 }
117
118 dev->ops = &i2c_generic_ops;
119}
120
121struct chip_operations drivers_i2c_generic_ops = {
122 CHIP_NAME("I2C Device")
123 .enable_dev = &i2c_generic_enable
124};