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