xref: /aosp_15_r20/external/coreboot/src/superio/common/generic.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <device/device.h>
4 #include <device/pnp.h>
5 #include <acpi/acpigen.h>
6 #include <console/console.h>
7 #include "chip.h"
8 
generic_set_resources(struct device * dev)9 static void generic_set_resources(struct device *dev)
10 {
11 	struct resource *res;
12 
13 	if (!dev)
14 		return;
15 
16 	if (dev->downstream)
17 		assign_resources(dev->downstream);
18 
19 	for (res = dev->resource_list; res; res = res->next) {
20 		if (!(res->flags & IORESOURCE_ASSIGNED))
21 			continue;
22 
23 		res->flags |= IORESOURCE_STORED;
24 		report_resource_stored(dev, res, "");
25 	}
26 }
27 
generic_read_resources(struct device * dev)28 static void generic_read_resources(struct device *dev)
29 {
30 	struct resource *res = new_resource(dev, 0);
31 	res->base = dev->path.pnp.port;
32 	res->size = 2;
33 	res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
34 }
35 
36 #if CONFIG(HAVE_ACPI_TABLES)
generic_ssdt(const struct device * dev)37 static void generic_ssdt(const struct device *dev)
38 {
39 	const char *scope = acpi_device_scope(dev);
40 	const char *name = acpi_device_name(dev);
41 
42 	if (!scope || !name) {
43 		printk(BIOS_ERR, "%s: Missing ACPI path/scope\n", dev_path(dev));
44 		return;
45 	}
46 
47 	/* Device */
48 	acpigen_write_scope(scope);
49 	acpigen_write_device(name);
50 
51 	printk(BIOS_DEBUG, "%s.%s: %s\n", scope, name, dev_path(dev));
52 
53 	acpigen_write_name_string("_HID", "PNP0C02");
54 	acpigen_write_name_string("_DDN", dev_name(dev));
55 
56 	/* OperationRegion("IOID", SYSTEMIO, port, 2) */
57 	struct opregion opreg = OPREGION("IOID", SYSTEMIO, dev->path.pnp.port, 2);
58 	acpigen_write_opregion(&opreg);
59 
60 	struct fieldlist l[] = {
61 		FIELDLIST_OFFSET(0),
62 		FIELDLIST_NAMESTR("INDX", 8),
63 		FIELDLIST_NAMESTR("DATA", 8),
64 	};
65 
66 	/* Field (IOID, AnyAcc, NoLock, Preserve)
67 	 * {
68 	 *  Offset (0),
69 	 *  INDX,   8,
70 	 *  DATA,   8,
71 	 * } */
72 	acpigen_write_field(opreg.name, l, ARRAY_SIZE(l), FIELD_BYTEACC | FIELD_NOLOCK |
73 			    FIELD_PRESERVE);
74 
75 	struct fieldlist i[] = {
76 		FIELDLIST_OFFSET(0x07),
77 		FIELDLIST_NAMESTR("LDN", 8),
78 		FIELDLIST_OFFSET(0x21),
79 		FIELDLIST_NAMESTR("SCF1", 8),
80 		FIELDLIST_NAMESTR("SCF2", 8),
81 		FIELDLIST_NAMESTR("SCF3", 8),
82 		FIELDLIST_NAMESTR("SCF4", 8),
83 		FIELDLIST_NAMESTR("SCF5", 8),
84 		FIELDLIST_NAMESTR("SCF6", 8),
85 		FIELDLIST_NAMESTR("SCF7", 8),
86 		FIELDLIST_OFFSET(0x29),
87 		FIELDLIST_NAMESTR("CKCF", 8),
88 		FIELDLIST_OFFSET(0x2F),
89 		FIELDLIST_NAMESTR("SCFF", 8),
90 		FIELDLIST_OFFSET(0x30),
91 		FIELDLIST_NAMESTR("ACT0", 1),
92 		FIELDLIST_NAMESTR("ACT1", 1),
93 		FIELDLIST_NAMESTR("ACT2", 1),
94 		FIELDLIST_NAMESTR("ACT3", 1),
95 		FIELDLIST_NAMESTR("ACT4", 1),
96 		FIELDLIST_NAMESTR("ACT5", 1),
97 		FIELDLIST_NAMESTR("ACT6", 1),
98 		FIELDLIST_NAMESTR("ACT7", 1),
99 		FIELDLIST_OFFSET(0x60),
100 		FIELDLIST_NAMESTR("IOH0", 8),
101 		FIELDLIST_NAMESTR("IOL0", 8),
102 		FIELDLIST_NAMESTR("IOH1", 8),
103 		FIELDLIST_NAMESTR("IOL1", 8),
104 		FIELDLIST_NAMESTR("IOH2", 8),
105 		FIELDLIST_NAMESTR("IOL2", 8),
106 		FIELDLIST_NAMESTR("IOH3", 8),
107 		FIELDLIST_NAMESTR("IOL3", 8),
108 		FIELDLIST_OFFSET(0x70),
109 		/* Interrupt level 0 (IRQ number) */
110 		FIELDLIST_NAMESTR("ITL0", 4),
111 		FIELDLIST_OFFSET(0x71),
112 		/* Interrupt type 0 */
113 		FIELDLIST_NAMESTR("ITT0", 2),
114 		FIELDLIST_OFFSET(0x72),
115 		/* Interrupt level 1 (IRQ number) */
116 		FIELDLIST_NAMESTR("ITL1", 4),
117 		FIELDLIST_OFFSET(0x73),
118 		/* Interrupt type 1 */
119 		FIELDLIST_NAMESTR("ITT1", 2),
120 		FIELDLIST_OFFSET(0x74),
121 		FIELDLIST_NAMESTR("DMCH", 8),
122 		FIELDLIST_OFFSET(0xE0),
123 		FIELDLIST_NAMESTR("RGE0", 8),
124 		FIELDLIST_NAMESTR("RGE1", 8),
125 		FIELDLIST_NAMESTR("RGE2", 8),
126 		FIELDLIST_NAMESTR("RGE3", 8),
127 		FIELDLIST_NAMESTR("RGE4", 8),
128 		FIELDLIST_NAMESTR("RGE5", 8),
129 		FIELDLIST_NAMESTR("RGE6", 8),
130 		FIELDLIST_NAMESTR("RGE7", 8),
131 		FIELDLIST_NAMESTR("RGE8", 8),
132 		FIELDLIST_NAMESTR("RGE9", 8),
133 		FIELDLIST_NAMESTR("RGEA", 8),
134 		FIELDLIST_OFFSET(0xF0),
135 		FIELDLIST_NAMESTR("OPT0", 8),
136 		FIELDLIST_NAMESTR("OPT1", 8),
137 		FIELDLIST_NAMESTR("OPT2", 8),
138 		FIELDLIST_NAMESTR("OPT3", 8),
139 		FIELDLIST_NAMESTR("OPT4", 8),
140 		FIELDLIST_NAMESTR("OPT5", 8),
141 		FIELDLIST_NAMESTR("OPT6", 8),
142 		FIELDLIST_NAMESTR("OPT7", 8),
143 		FIELDLIST_NAMESTR("OPT8", 8),
144 		FIELDLIST_NAMESTR("OPT9", 8),
145 	};
146 
147 	acpigen_write_indexfield("INDX", "DATA", i, ARRAY_SIZE(i), FIELD_BYTEACC |
148 				 FIELD_NOLOCK | FIELD_PRESERVE);
149 
150 	const char *mutex = "MTX0";
151 
152 	acpigen_write_mutex(mutex, 0);
153 	/* Backup LDN */
154 	acpigen_write_name_integer("BLDN", 0);
155 
156 	/* Acquire mutex - Enter config mode */
157 	acpigen_write_method("AMTX", 0);
158 	{
159 		acpigen_write_acquire(mutex, 0xffff);
160 
161 		/* Pick one of the children as the generic SIO doesn't have config mode */
162 		if (dev->downstream && dev->downstream->children)
163 			pnp_ssdt_enter_conf_mode(dev->downstream->children, "^INDX", "^DATA");
164 
165 		/* Backup LDN */
166 		acpigen_write_store();
167 		acpigen_emit_namestring("^LDN");
168 		acpigen_emit_namestring("^BLDN");
169 	}
170 	acpigen_pop_len(); /* Method */
171 
172 	/* Release mutex - Exit config mode */
173 	acpigen_write_method("RMTX", 0);
174 	{
175 		/* Restore LDN */
176 		acpigen_write_store();
177 		acpigen_emit_namestring("^BLDN");
178 		acpigen_emit_namestring("^LDN");
179 
180 		/* Pick one of the children as the generic SIO doesn't have config mode */
181 		if (dev->downstream && dev->downstream->children)
182 			pnp_ssdt_exit_conf_mode(dev->downstream->children, "^INDX", "^DATA");
183 
184 		acpigen_write_release(mutex);
185 	}
186 	acpigen_pop_len(); /* Method */
187 
188 	/* Select a LDN */
189 	acpigen_write_method("SLDN", 1);
190 	{
191 		/* Local0 = Arg0 & 0xff */
192 		acpigen_emit_byte(AND_OP);
193 		acpigen_write_integer(0xff);
194 		acpigen_emit_byte(ARG0_OP);
195 		acpigen_emit_byte(LOCAL0_OP);
196 
197 		/* LDN = LOCAL0_OP */
198 		acpigen_write_store();
199 		acpigen_emit_byte(LOCAL0_OP);
200 		acpigen_emit_namestring("^LDN");
201 	}
202 	acpigen_pop_len(); /* Method */
203 
204 	/* Disable a LDN/VLDN */
205 	acpigen_write_method("DLDN", 1);
206 	{
207 		/* AMTX() */
208 		acpigen_emit_namestring("AMTX");
209 
210 		/* SLDN (Local0) */
211 		acpigen_emit_namestring("SLDN");
212 		acpigen_emit_byte(ARG0_OP);
213 
214 		/* Local0 = Arg0 >> 8 */
215 		acpigen_emit_byte(SHIFT_RIGHT_OP);
216 		acpigen_emit_byte(ARG0_OP);
217 		acpigen_write_integer(8);
218 		acpigen_emit_byte(LOCAL0_OP);
219 
220 		/* Local0 = Local0 & 0x7 */
221 		acpigen_emit_byte(AND_OP);
222 		acpigen_write_integer(0x7);
223 		acpigen_emit_byte(LOCAL0_OP);
224 		acpigen_emit_byte(LOCAL0_OP);
225 
226 		for (int j = 0; j < 8; j++) {
227 			char act[6] = "^ACT0";
228 			act[4] += j;
229 
230 			/* If (Local0 == j) { */
231 			acpigen_write_if_lequal_op_int(LOCAL0_OP, j);
232 
233 			/* ACT[j] = 0 */
234 			acpigen_write_store();
235 			acpigen_emit_byte(ZERO_OP);
236 			acpigen_emit_namestring(act);
237 
238 			acpigen_pop_len(); /* } */
239 		}
240 
241 		/* RMTX() */
242 		acpigen_emit_namestring("RMTX");
243 	}
244 	acpigen_pop_len(); /* Method */
245 
246 	/* Query LDN enable state. Returns 1 if LDN/VLDN is enabled. */
247 	acpigen_write_method("QLDN", 1);
248 	{
249 		acpigen_emit_namestring("AMTX");
250 
251 		/* SLDN (Local0) */
252 		acpigen_emit_namestring("SLDN");
253 		acpigen_emit_byte(ARG0_OP);
254 
255 		/* Local0 = Arg0 >> 8 */
256 		acpigen_emit_byte(SHIFT_RIGHT_OP);
257 		acpigen_emit_byte(ARG0_OP);
258 		acpigen_write_integer(8);
259 		acpigen_emit_byte(LOCAL0_OP);
260 
261 		/* Local0 = Local0 & 0x7 */
262 		acpigen_emit_byte(AND_OP);
263 		acpigen_write_integer(0x7);
264 		acpigen_emit_byte(LOCAL0_OP);
265 		acpigen_emit_byte(LOCAL0_OP);
266 
267 		for (int j = 0; j < 8; j++) {
268 			char act[6] = "^ACT0";
269 			act[4] += j;
270 			/* If (Local0 == j) { */
271 			acpigen_write_if_lequal_op_int(LOCAL0_OP, j);
272 
273 			/* Local1 = ACT[j] */
274 			acpigen_write_store();
275 			acpigen_emit_namestring(act);
276 			acpigen_emit_byte(LOCAL1_OP);
277 
278 			acpigen_pop_len(); /* } */
279 		}
280 
281 		/* RMTX() */
282 		acpigen_emit_namestring("RMTX");
283 
284 		/* Return (Local1) */
285 		acpigen_emit_byte(RETURN_OP);
286 		acpigen_emit_byte(LOCAL1_OP);
287 	}
288 	acpigen_pop_len(); /* Method */
289 
290 	acpigen_pop_len(); /* Device */
291 	acpigen_pop_len(); /* Scope */
292 }
293 
generic_acpi_name(const struct device * dev)294 static const char *generic_acpi_name(const struct device *dev)
295 {
296 	const struct superio_common_config *cfg = dev->chip_info;
297 	if (cfg && cfg->acpi_name[sizeof(cfg->acpi_name)-1] == '\0' &&
298 		strlen(cfg->acpi_name) == 4) {
299 		return cfg->acpi_name;
300 	}
301 	return "SIO0";
302 }
303 #endif
304 
305 static struct device_operations ops = {
306 	.read_resources   = generic_read_resources,
307 	.set_resources    = generic_set_resources,
308 	.scan_bus	  = scan_static_bus,
309 #if CONFIG(HAVE_ACPI_TABLES)
310 	.acpi_fill_ssdt	  = generic_ssdt,
311 	.acpi_name	  = generic_acpi_name,
312 #endif
313 };
314 
enable_dev(struct device * dev)315 static void enable_dev(struct device *dev)
316 {
317 	if (dev->path.type != DEVICE_PATH_PNP)
318 		printk(BIOS_ERR, "%s: Unsupported device type\n", dev_path(dev));
319 	else if (!dev->path.pnp.port)
320 		printk(BIOS_ERR, "%s: Base address not set\n", dev_path(dev));
321 	else
322 		dev->ops = &ops;
323 }
324 
325 struct chip_operations superio_common_ops = {
326 	.name = "Generic Super I/O",
327 	.enable_dev = enable_dev,
328 };
329