blob: 41cafdfb0eef5115f1a506f4449d75b557fc0365 [file] [log] [blame]
Patrick Rudolphc1621312019-05-28 11:29:29 +02001/*
2 * This file is part of the coreboot project.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <superio/common/ssdt.h>
16
17#include <device/device.h>
18#include <device/pnp.h>
19#include <arch/acpigen.h>
20#include <arch/acpi.h>
21#include <device/pnp_def.h>
22#include <console/console.h>
23#include <types.h>
24
25struct superio_dev {
26 const char *acpi_hid;
27 u16 io_base[4];
28 u8 irq[2];
29};
30
31static const struct superio_dev superio_devs[] = {
32 {ACPI_HID_FDC, {0x3f0, 0x3f2, 0x3f7}, {6, } },
33 {ACPI_HID_KEYBOARD, {60, 64, }, {1, } },
34 {ACPI_HID_MOUSE, {60, 64, }, {12, } },
35 {ACPI_HID_COM, {0x3f8, 0x2f8, 0x3e8, 0x2e8}, {4, 3} },
36 {ACPI_HID_LPT, {0x378, }, {7, } },
37};
38
39static const u8 io_idx[] = {PNP_IDX_IO0, PNP_IDX_IO1, PNP_IDX_IO2, PNP_IDX_IO3};
40static const u8 irq_idx[] = {PNP_IDX_IRQ0, PNP_IDX_IRQ1};
41
42static const struct superio_dev *superio_guess_function(struct device *dev)
43{
44 for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
45 struct resource *res = probe_resource(dev, io_idx[i]);
46 if (!res || !res->base)
47 continue;
48
49 for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
50 for (size_t k = 0; k < 4; k++) {
51 if (!superio_devs[j].io_base[k])
52 continue;
53 if (superio_devs[j].io_base[k] == res->base)
54 return &superio_devs[j];
55 }
56 }
57 }
58 for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
59 struct resource *res = probe_resource(dev, irq_idx[i]);
60 if (!res || !res->size)
61 continue;
62 for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
63 for (size_t k = 0; k < 2; k++) {
64 if (!superio_devs[j].irq[k])
65 continue;
66 if (superio_devs[j].irq[k] == res->base)
67 return &superio_devs[j];
68 }
69 }
70 }
71 return NULL;
72}
73
74/* Return true if there are resources to report */
75static bool has_resources(struct device *dev)
76{
77 for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
78 struct resource *res = probe_resource(dev, io_idx[i]);
79 if (!res || !res->base || !res->size)
80 continue;
81 return 1;
82 }
83 for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
84 struct resource *res = probe_resource(dev, irq_idx[i]);
85 if (!res || !res->size || res->base > 16)
86 continue;
87 return 1;
88 }
89 return 0;
90}
91
92/* Add IO and IRQ resources for _CRS or _PRS */
93static void ldn_gen_resources(struct device *dev)
94{
95 uint16_t irq = 0;
96 for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
97 struct resource *res = probe_resource(dev, io_idx[i]);
98 if (!res || !res->base)
99 continue;
100 resource_t base = res->base;
101 resource_t size = res->size;
102 while (size > 0) {
103 resource_t sz = size > 255 ? 255 : size;
104 /* TODO: Needs test with regions >= 256 bytes */
105 acpigen_write_io16(base, base, 1, sz, 1);
106 size -= sz;
107 base += sz;
108 }
109 }
110 for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
111 struct resource *res = probe_resource(dev, irq_idx[i]);
112 if (!res || !res->size || res->base >= 16)
113 continue;
114 irq |= 1 << res->base;
115 }
116 if (irq)
117 acpigen_write_irq(irq);
118
119}
120
121/* Add resource base and size for additional SuperIO code */
122static void ldn_gen_resources_use(struct device *dev)
123{
124 char name[5];
125 for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
126 struct resource *res = probe_resource(dev, io_idx[i]);
127 if (!res || !res->base || !res->size)
128 continue;
129
Patrick Georgi28cbab32019-09-16 12:17:59 +0200130 snprintf(name, sizeof(name), "IO%zXB", i);
Patrick Rudolphc1621312019-05-28 11:29:29 +0200131 name[4] = '\0';
132 acpigen_write_name_integer(name, res->base);
133
Patrick Georgi28cbab32019-09-16 12:17:59 +0200134 snprintf(name, sizeof(name), "IO%zXS", i);
Patrick Rudolphc1621312019-05-28 11:29:29 +0200135 name[4] = '\0';
136 acpigen_write_name_integer(name, res->size);
137 }
138}
139
140const char *superio_common_ldn_acpi_name(const struct device *dev)
141{
142 u8 ldn = dev->path.pnp.device & 0xff;
143 u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
144 static char name[5];
145
146 snprintf(name, sizeof(name), "L%02X%01X", ldn, vldn);
147
148 name[4] = '\0';
149
150 return name;
151}
152
153static const char *name_from_hid(const char *hid)
154{
155 static const struct {
156 const char *hid;
157 const char *name;
158 } lookup[] = {
159 {ACPI_HID_FDC, "FDC" },
160 {ACPI_HID_KEYBOARD, "PS2 Keyboard" },
161 {ACPI_HID_MOUSE, "PS2 Mouse"},
162 {ACPI_HID_COM, "COM port" },
163 {ACPI_HID_LPT, "LPT" },
164 {ACPI_HID_PNP, "Generic PNP device" },
165 };
166
167 for (size_t i = 0; hid && i < ARRAY_SIZE(lookup); i++) {
168 if (strcmp(hid, lookup[i].hid) == 0)
169 return lookup[i].name;
170 }
171 return "Generic device";
172}
173
174void superio_common_fill_ssdt_generator(struct device *dev)
175{
176 const char *scope = acpi_device_scope(dev);
177 const char *name = acpi_device_name(dev);
178 const u8 ldn = dev->path.pnp.device & 0xff;
179 const u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
180 const char *hid;
181
182 if (!scope || !name) {
183 printk(BIOS_ERR, "%s: Missing ACPI path/scope\n", dev_path(dev));
184 return;
185 }
186 if (vldn) {
187 printk(BIOS_DEBUG, "%s: Ignoring virtual LDN\n", dev_path(dev));
188 return;
189 }
190
191 printk(BIOS_DEBUG, "%s.%s: %s\n", scope, name, dev_path(dev));
192
193 /* Scope */
194 acpigen_write_scope(scope);
195
196 /* Device */
197 acpigen_write_device(name);
198
199 acpigen_write_name_byte("_UID", 0);
200 acpigen_write_name_byte("LDN", ldn);
201 acpigen_write_name_byte("VLDN", vldn);
202
Patrick Rudolph95bff2e2019-12-10 14:12:03 +0100203 acpigen_write_method("_STA", 0);
204 {
205 acpigen_write_store();
206 acpigen_emit_namestring("^^QLDN");
207 acpigen_write_integer(ldn);
208 acpigen_emit_byte(LOCAL0_OP);
209
210 /* Multiply (Local0, 0xf, Local0) */
211 acpigen_emit_byte(MULTIPLY_OP);
212 acpigen_emit_byte(LOCAL0_OP);
213 acpigen_write_integer(0xf);
214 acpigen_emit_byte(LOCAL0_OP);
215
216 acpigen_emit_byte(RETURN_OP);
217 acpigen_emit_byte(LOCAL0_OP);
218
219 }
220 acpigen_pop_len(); /* Method */
Patrick Rudolphc1621312019-05-28 11:29:29 +0200221
Patrick Rudolph5c8ff792019-12-13 11:12:33 +0100222 /*
223 * The ACPI6.2 spec Chapter 6.1.5 requires to set a _HID if no _ADR
224 * is present. Tests on Windows 10 showed that this is also true for
225 * disabled (_STA = 0) devices, otherwise it BSODs.
226 */
Patrick Rudolphc1621312019-05-28 11:29:29 +0200227
228 hid = acpi_device_hid(dev);
229 if (!hid) {
230 printk(BIOS_ERR, "%s: SuperIO driver doesn't provide a _HID\n", dev_path(dev));
231 /* Try to guess it... */
232 const struct superio_dev *sdev = superio_guess_function(dev);
233 if (sdev && sdev->acpi_hid) {
234 hid = sdev->acpi_hid;
235 printk(BIOS_WARNING, "%s: Guessed _HID is '%s'\n", dev_path(dev), hid);
236 } else {
237 hid = ACPI_HID_PNP;
238 printk(BIOS_ERR, "%s: Failed to guessed _HID\n", dev_path(dev));
239 }
240 }
241
242 acpigen_write_name_string("_HID", hid);
243 acpigen_write_name_string("_DDN", name_from_hid(hid));
244
Patrick Rudolph95bff2e2019-12-10 14:12:03 +0100245 acpigen_write_method("_DIS", 0);
246 {
247 acpigen_emit_namestring("^^DLDN");
248 acpigen_write_integer(ldn);
249 }
250 acpigen_pop_len(); /* Method */
251
Patrick Rudolph5c8ff792019-12-13 11:12:33 +0100252 if (dev->enabled && has_resources(dev)) {
253 /* Resources - _CRS */
254 acpigen_write_name("_CRS");
255 acpigen_write_resourcetemplate_header();
256 ldn_gen_resources(dev);
257 acpigen_write_resourcetemplate_footer();
258
259 /* Resources - _PRS */
260 acpigen_write_name("_PRS");
261 acpigen_write_resourcetemplate_header();
262 ldn_gen_resources(dev);
263 acpigen_write_resourcetemplate_footer();
264
265 /* Resources base and size for 3rd party ACPI code */
266 ldn_gen_resources_use(dev);
267 }
268
Patrick Rudolphc1621312019-05-28 11:29:29 +0200269 acpigen_pop_len(); /* Device */
270 acpigen_pop_len(); /* Scope */
271}