blob: a245f2cf0e8d9484623a2428f8807ff6a1f0a4fe [file] [log] [blame]
Duncan Lauriee530d812020-05-27 12:32:30 -07001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3#include <acpi/acpi_device.h>
4#include <acpi/acpigen.h>
5#include <console/console.h>
Duncan Lauriee530d812020-05-27 12:32:30 -07006#include <string.h>
7#include "chip.h"
8
9static bool uart_acpi_add_gpios_to_crs(struct drivers_uart_acpi_config *config)
10{
11 /*
12 * Return false if:
Matt DeVilliercf440b62023-01-11 18:02:31 -060013 * 1. GPIOs are exported via a power resource, or
Duncan Lauriee530d812020-05-27 12:32:30 -070014 * 2. Both reset and enable GPIOs are not provided.
15 */
Matt DeVilliercf440b62023-01-11 18:02:31 -060016 if (config->has_power_resource ||
Duncan Lauriee530d812020-05-27 12:32:30 -070017 ((config->reset_gpio.pin_count == 0) &&
18 (config->enable_gpio.pin_count == 0)))
19 return false;
20
21 return true;
22}
23
24static int uart_acpi_write_gpio(struct acpi_gpio *gpio, int *curr_index)
25{
26 int ret = -1;
27
28 if (gpio->pin_count == 0)
29 return ret;
30
31 acpi_device_write_gpio(gpio);
32 ret = *curr_index;
33 (*curr_index)++;
34
35 return ret;
36}
37
38static void uart_acpi_fill_ssdt(const struct device *dev)
39{
40 const char *scope = acpi_device_scope(dev);
41 const char *path = acpi_device_path(dev);
42 struct drivers_uart_acpi_config *config = dev->chip_info;
43 int curr_index = 0;
44 int irq_gpio_index = -1;
45 int reset_gpio_index = -1;
46 int enable_gpio_index = -1;
47
Karthikeyan Ramasubramaniand1c0f952020-11-02 16:26:52 -070048 if (!scope)
Duncan Lauriee530d812020-05-27 12:32:30 -070049 return;
50
51 if (!config->hid) {
52 printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
53 return;
54 }
55
56 acpigen_write_scope(scope);
57 acpigen_write_device(acpi_device_name(dev));
58
59 acpigen_write_name_string("_HID", config->hid);
60 if (config->cid)
61 acpigen_write_name_string("_CID", config->cid);
62 acpigen_write_name_integer("_UID", config->uid);
63 acpigen_write_name_string("_DDN", config->desc);
64 acpigen_write_STA(acpi_device_status(dev));
65
66 /* Resources */
67 acpigen_write_name("_CRS");
68 acpigen_write_resourcetemplate_header();
69
70 /* Fix up resource pointer to this scope */
71 config->uart.resource = scope;
72 acpi_device_write_uart(&config->uart);
73
74 /* Use either Interrupt() or GpioInt() */
75 if (config->irq_gpio.pin_count)
76 acpi_device_write_gpio(&config->irq_gpio);
77 else
78 acpi_device_write_interrupt(&config->irq);
79
80 /* Add enable/reset GPIOs if needed */
81 if (uart_acpi_add_gpios_to_crs(config)) {
82 reset_gpio_index = uart_acpi_write_gpio(&config->reset_gpio,
83 &curr_index);
84 enable_gpio_index = uart_acpi_write_gpio(&config->enable_gpio,
85 &curr_index);
86 }
87 acpigen_write_resourcetemplate_footer();
88
89 /* Wake capabilities */
90 if (config->wake) {
Tim Wawrzynczak29f3a882021-08-05 09:37:52 -060091 acpigen_write_name_integer("_S0W", ACPI_DEVICE_SLEEP_D3_HOT);
Duncan Lauriee530d812020-05-27 12:32:30 -070092 acpigen_write_PRW(config->wake, SLP_TYP_S3);
93 };
94
95 /* Write device properties if needed */
96 if (config->compat_string || irq_gpio_index >= 0 ||
97 reset_gpio_index >= 0 || enable_gpio_index >= 0) {
98 struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
99 if (config->compat_string)
100 acpi_dp_add_string(dsd, "compatible",
101 config->compat_string);
102 if (irq_gpio_index >= 0)
103 acpi_dp_add_gpio(dsd, "irq-gpios", path,
104 irq_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700105 config->irq_gpio.active_low);
Duncan Lauriee530d812020-05-27 12:32:30 -0700106 if (reset_gpio_index >= 0)
107 acpi_dp_add_gpio(dsd, "reset-gpios", path,
108 reset_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700109 config->reset_gpio.active_low);
Duncan Lauriee530d812020-05-27 12:32:30 -0700110 if (enable_gpio_index >= 0)
111 acpi_dp_add_gpio(dsd, "enable-gpios", path,
112 enable_gpio_index, 0,
Furquan Shaikhd2d5e442020-07-01 01:37:13 -0700113 config->enable_gpio.active_low);
Duncan Lauriee530d812020-05-27 12:32:30 -0700114 acpi_dp_write(dsd);
115 }
116
117 /* Power Resource */
118 if (config->has_power_resource) {
119 const struct acpi_power_res_params power_res_params = {
120 &config->reset_gpio,
121 config->reset_delay_ms,
122 config->reset_off_delay_ms,
123 &config->enable_gpio,
124 config->enable_delay_ms,
125 config->enable_off_delay_ms,
126 &config->stop_gpio,
127 config->stop_delay_ms,
128 config->stop_off_delay_ms
129 };
130 acpi_device_add_power_res(&power_res_params);
131 }
132
133 acpigen_pop_len(); /* Device */
134 acpigen_pop_len(); /* Scope */
135
136 printk(BIOS_INFO, "%s: %s at %s\n", path, config->desc, dev_path(dev));
137}
138
139static const char *uart_acpi_name(const struct device *dev)
140{
141 struct drivers_uart_acpi_config *config = dev->chip_info;
142 static char name[5];
143
144 if (config->name)
145 return config->name;
146
147 snprintf(name, sizeof(name), "D%03.3X", dev->path.generic.id);
148 return name;
149}
150
151static struct device_operations uart_acpi_dev_ops = {
152 .read_resources = noop_read_resources,
153 .set_resources = noop_set_resources,
154 .acpi_fill_ssdt = uart_acpi_fill_ssdt,
155 .acpi_name = uart_acpi_name,
156};
157
158static void uart_acpi_enable(struct device *dev)
159{
160 struct drivers_uart_acpi_config *config = dev->chip_info;
161
162 if (config->desc)
163 dev->name = config->desc;
164 else
165 config->desc = dev->chip_ops->name;
166
167 dev->ops = &uart_acpi_dev_ops;
168}
169
170struct chip_operations drivers_uart_acpi_ops = {
Nicholas Sudsgaardbfb11be2024-01-30 09:53:46 +0900171 .name = "ACPI UART Device",
Duncan Lauriee530d812020-05-27 12:32:30 -0700172 .enable_dev = uart_acpi_enable
173};