blob: f5ad765aa17ea7179d8cc0cf45e3b835513ba4f9 [file] [log] [blame]
Felix Held3f3eca92020-01-23 17:12:32 +01001/* SPDX-License-Identifier: GPL-2.0-or-later */
2/* This file is part of the coreboot project. */
Patrick Rudolphc1621312019-05-28 11:29:29 +02003
4#include <superio/common/ssdt.h>
5
6#include <device/device.h>
7#include <device/pnp.h>
8#include <arch/acpigen.h>
9#include <arch/acpi.h>
10#include <device/pnp_def.h>
11#include <console/console.h>
12#include <types.h>
Joel Kitchinga1b15172020-03-12 18:15:34 +080013#include <string.h>
Patrick Rudolphc1621312019-05-28 11:29:29 +020014
15struct superio_dev {
16 const char *acpi_hid;
17 u16 io_base[4];
18 u8 irq[2];
19};
20
21static const struct superio_dev superio_devs[] = {
22 {ACPI_HID_FDC, {0x3f0, 0x3f2, 0x3f7}, {6, } },
23 {ACPI_HID_KEYBOARD, {60, 64, }, {1, } },
24 {ACPI_HID_MOUSE, {60, 64, }, {12, } },
25 {ACPI_HID_COM, {0x3f8, 0x2f8, 0x3e8, 0x2e8}, {4, 3} },
26 {ACPI_HID_LPT, {0x378, }, {7, } },
27};
28
29static const u8 io_idx[] = {PNP_IDX_IO0, PNP_IDX_IO1, PNP_IDX_IO2, PNP_IDX_IO3};
30static const u8 irq_idx[] = {PNP_IDX_IRQ0, PNP_IDX_IRQ1};
31
32static const struct superio_dev *superio_guess_function(struct device *dev)
33{
34 for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
35 struct resource *res = probe_resource(dev, io_idx[i]);
36 if (!res || !res->base)
37 continue;
38
39 for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
40 for (size_t k = 0; k < 4; k++) {
41 if (!superio_devs[j].io_base[k])
42 continue;
43 if (superio_devs[j].io_base[k] == res->base)
44 return &superio_devs[j];
45 }
46 }
47 }
48 for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
49 struct resource *res = probe_resource(dev, irq_idx[i]);
50 if (!res || !res->size)
51 continue;
52 for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
53 for (size_t k = 0; k < 2; k++) {
54 if (!superio_devs[j].irq[k])
55 continue;
56 if (superio_devs[j].irq[k] == res->base)
57 return &superio_devs[j];
58 }
59 }
60 }
61 return NULL;
62}
63
64/* Return true if there are resources to report */
65static bool has_resources(struct device *dev)
66{
67 for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
68 struct resource *res = probe_resource(dev, io_idx[i]);
69 if (!res || !res->base || !res->size)
70 continue;
71 return 1;
72 }
73 for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
74 struct resource *res = probe_resource(dev, irq_idx[i]);
75 if (!res || !res->size || res->base > 16)
76 continue;
77 return 1;
78 }
79 return 0;
80}
81
82/* Add IO and IRQ resources for _CRS or _PRS */
83static void ldn_gen_resources(struct device *dev)
84{
85 uint16_t irq = 0;
86 for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
87 struct resource *res = probe_resource(dev, io_idx[i]);
88 if (!res || !res->base)
89 continue;
90 resource_t base = res->base;
91 resource_t size = res->size;
92 while (size > 0) {
93 resource_t sz = size > 255 ? 255 : size;
94 /* TODO: Needs test with regions >= 256 bytes */
95 acpigen_write_io16(base, base, 1, sz, 1);
96 size -= sz;
97 base += sz;
98 }
99 }
100 for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
101 struct resource *res = probe_resource(dev, irq_idx[i]);
102 if (!res || !res->size || res->base >= 16)
103 continue;
104 irq |= 1 << res->base;
105 }
106 if (irq)
107 acpigen_write_irq(irq);
108
109}
110
111/* Add resource base and size for additional SuperIO code */
112static void ldn_gen_resources_use(struct device *dev)
113{
114 char name[5];
115 for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
116 struct resource *res = probe_resource(dev, io_idx[i]);
117 if (!res || !res->base || !res->size)
118 continue;
119
Patrick Georgi28cbab32019-09-16 12:17:59 +0200120 snprintf(name, sizeof(name), "IO%zXB", i);
Patrick Rudolphc1621312019-05-28 11:29:29 +0200121 name[4] = '\0';
122 acpigen_write_name_integer(name, res->base);
123
Patrick Georgi28cbab32019-09-16 12:17:59 +0200124 snprintf(name, sizeof(name), "IO%zXS", i);
Patrick Rudolphc1621312019-05-28 11:29:29 +0200125 name[4] = '\0';
126 acpigen_write_name_integer(name, res->size);
127 }
128}
129
130const char *superio_common_ldn_acpi_name(const struct device *dev)
131{
132 u8 ldn = dev->path.pnp.device & 0xff;
133 u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
134 static char name[5];
135
136 snprintf(name, sizeof(name), "L%02X%01X", ldn, vldn);
137
138 name[4] = '\0';
139
140 return name;
141}
142
143static const char *name_from_hid(const char *hid)
144{
145 static const struct {
146 const char *hid;
147 const char *name;
148 } lookup[] = {
149 {ACPI_HID_FDC, "FDC" },
150 {ACPI_HID_KEYBOARD, "PS2 Keyboard" },
151 {ACPI_HID_MOUSE, "PS2 Mouse"},
152 {ACPI_HID_COM, "COM port" },
153 {ACPI_HID_LPT, "LPT" },
154 {ACPI_HID_PNP, "Generic PNP device" },
155 };
156
157 for (size_t i = 0; hid && i < ARRAY_SIZE(lookup); i++) {
158 if (strcmp(hid, lookup[i].hid) == 0)
159 return lookup[i].name;
160 }
161 return "Generic device";
162}
163
164void superio_common_fill_ssdt_generator(struct device *dev)
165{
Patrick Rudolph1ac2cc22020-02-13 13:00:41 +0100166 if (!dev || !dev->bus || !dev->bus->dev) {
167 printk(BIOS_CRIT, "BUG: Invalid argument in %s!\n", __func__);
168 return;
169 }
170
Patrick Rudolphc1621312019-05-28 11:29:29 +0200171 const char *scope = acpi_device_scope(dev);
172 const char *name = acpi_device_name(dev);
173 const u8 ldn = dev->path.pnp.device & 0xff;
174 const u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
175 const char *hid;
176
Patrick Rudolph1ac2cc22020-02-13 13:00:41 +0100177 /* Validate devicetree settings */
178 bool bug = false;
179 if (dev->bus->dev->path.type != DEVICE_PATH_PNP) {
180 bug = true;
181 printk(BIOS_CRIT, "BUG: Parent of device %s is not a PNP device\n",
182 dev_path(dev));
183 } else if (dev->bus->dev->path.pnp.port != dev->path.pnp.port) {
184 bug = true;
185 printk(BIOS_CRIT, "BUG: Parent of device %s has wrong I/O port\n",
186 dev_path(dev));
187 }
188 if (bug) {
189 printk(BIOS_CRIT, "BUG: Check your devicetree!\n");
190 return;
191 }
192
Patrick Rudolphc1621312019-05-28 11:29:29 +0200193 if (!scope || !name) {
194 printk(BIOS_ERR, "%s: Missing ACPI path/scope\n", dev_path(dev));
195 return;
196 }
197 if (vldn) {
198 printk(BIOS_DEBUG, "%s: Ignoring virtual LDN\n", dev_path(dev));
199 return;
200 }
201
202 printk(BIOS_DEBUG, "%s.%s: %s\n", scope, name, dev_path(dev));
203
204 /* Scope */
205 acpigen_write_scope(scope);
206
207 /* Device */
208 acpigen_write_device(name);
209
Patrick Rudolphc83bab62019-12-13 12:16:06 +0100210 acpi_device_write_uid(dev);
211
Patrick Rudolphc1621312019-05-28 11:29:29 +0200212 acpigen_write_name_byte("LDN", ldn);
213 acpigen_write_name_byte("VLDN", vldn);
214
Patrick Rudolph95bff2e2019-12-10 14:12:03 +0100215 acpigen_write_method("_STA", 0);
216 {
217 acpigen_write_store();
218 acpigen_emit_namestring("^^QLDN");
219 acpigen_write_integer(ldn);
220 acpigen_emit_byte(LOCAL0_OP);
221
222 /* Multiply (Local0, 0xf, Local0) */
223 acpigen_emit_byte(MULTIPLY_OP);
224 acpigen_emit_byte(LOCAL0_OP);
225 acpigen_write_integer(0xf);
226 acpigen_emit_byte(LOCAL0_OP);
227
228 acpigen_emit_byte(RETURN_OP);
229 acpigen_emit_byte(LOCAL0_OP);
230
231 }
232 acpigen_pop_len(); /* Method */
Patrick Rudolphc1621312019-05-28 11:29:29 +0200233
Patrick Rudolph5c8ff792019-12-13 11:12:33 +0100234 /*
235 * The ACPI6.2 spec Chapter 6.1.5 requires to set a _HID if no _ADR
236 * is present. Tests on Windows 10 showed that this is also true for
237 * disabled (_STA = 0) devices, otherwise it BSODs.
238 */
Patrick Rudolphc1621312019-05-28 11:29:29 +0200239
240 hid = acpi_device_hid(dev);
241 if (!hid) {
242 printk(BIOS_ERR, "%s: SuperIO driver doesn't provide a _HID\n", dev_path(dev));
243 /* Try to guess it... */
244 const struct superio_dev *sdev = superio_guess_function(dev);
245 if (sdev && sdev->acpi_hid) {
246 hid = sdev->acpi_hid;
247 printk(BIOS_WARNING, "%s: Guessed _HID is '%s'\n", dev_path(dev), hid);
248 } else {
249 hid = ACPI_HID_PNP;
250 printk(BIOS_ERR, "%s: Failed to guessed _HID\n", dev_path(dev));
251 }
252 }
253
254 acpigen_write_name_string("_HID", hid);
255 acpigen_write_name_string("_DDN", name_from_hid(hid));
256
Patrick Rudolph95bff2e2019-12-10 14:12:03 +0100257 acpigen_write_method("_DIS", 0);
258 {
259 acpigen_emit_namestring("^^DLDN");
260 acpigen_write_integer(ldn);
261 }
262 acpigen_pop_len(); /* Method */
263
Patrick Rudolph5c8ff792019-12-13 11:12:33 +0100264 if (dev->enabled && has_resources(dev)) {
265 /* Resources - _CRS */
266 acpigen_write_name("_CRS");
267 acpigen_write_resourcetemplate_header();
268 ldn_gen_resources(dev);
269 acpigen_write_resourcetemplate_footer();
270
271 /* Resources - _PRS */
272 acpigen_write_name("_PRS");
273 acpigen_write_resourcetemplate_header();
274 ldn_gen_resources(dev);
275 acpigen_write_resourcetemplate_footer();
276
277 /* Resources base and size for 3rd party ACPI code */
278 ldn_gen_resources_use(dev);
279 }
280
Patrick Rudolphc1621312019-05-28 11:29:29 +0200281 acpigen_pop_len(); /* Device */
282 acpigen_pop_len(); /* Scope */
283}