blob: 92d835731eee0054fae5243876ac090541fecf22 [file] [log] [blame]
Felix Held3f3eca92020-01-23 17:12:32 +01001/* SPDX-License-Identifier: GPL-2.0-or-later */
Patrick Rudolphc1621312019-05-28 11:29:29 +02002
3#include <device/device.h>
4#include <device/pnp.h>
Furquan Shaikh76cedd22020-05-02 10:24:23 -07005#include <acpi/acpigen.h>
Patrick Rudolphc1621312019-05-28 11:29:29 +02006#include <console/console.h>
Jonathon Hall247ec332023-04-07 14:43:25 -04007#include "chip.h"
Patrick Rudolphc1621312019-05-28 11:29:29 +02008
9static void generic_set_resources(struct device *dev)
10{
11 struct resource *res;
12
John Zhao9857c902020-07-17 09:09:56 -070013 if (!dev)
14 return;
15
Arthur Heymans7fcd4d52023-08-24 15:12:19 +020016 if (dev->downstream)
17 assign_resources(dev->downstream);
Christian Walter199f98b2019-12-09 13:27:10 +010018
Patrick Rudolphc1621312019-05-28 11:29:29 +020019 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
28static 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)
Furquan Shaikh7536a392020-04-24 21:59:21 -070037static void generic_ssdt(const struct device *dev)
Patrick Rudolphc1621312019-05-28 11:29:29 +020038{
39 const char *scope = acpi_device_scope(dev);
40 const char *name = acpi_device_name(dev);
41
42 if (!scope || !name) {
Elyes HAOUAS45808392020-03-30 16:43:34 +020043 printk(BIOS_ERR, "%s: Missing ACPI path/scope\n", dev_path(dev));
Patrick Rudolphc1621312019-05-28 11:29:29 +020044 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),
Michael Niewöhner5403a8e2019-09-08 18:03:53 +0200109 /* Interrupt level 0 (IRQ number) */
110 FIELDLIST_NAMESTR("ITL0", 4),
Patrick Rudolphc1621312019-05-28 11:29:29 +0200111 FIELDLIST_OFFSET(0x71),
Michael Niewöhner5403a8e2019-09-08 18:03:53 +0200112 /* Interrupt type 0 */
113 FIELDLIST_NAMESTR("ITT0", 2),
Patrick Rudolphc1621312019-05-28 11:29:29 +0200114 FIELDLIST_OFFSET(0x72),
Michael Niewöhner5403a8e2019-09-08 18:03:53 +0200115 /* Interrupt level 1 (IRQ number) */
116 FIELDLIST_NAMESTR("ITL1", 4),
Patrick Rudolphc1621312019-05-28 11:29:29 +0200117 FIELDLIST_OFFSET(0x73),
Michael Niewöhner5403a8e2019-09-08 18:03:53 +0200118 /* Interrupt type 1 */
119 FIELDLIST_NAMESTR("ITT1", 2),
Patrick Rudolphc1621312019-05-28 11:29:29 +0200120 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
Patrick Rudolph95bff2e2019-12-10 14:12:03 +0100150 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 */
Arthur Heymans7fcd4d52023-08-24 15:12:19 +0200162 if (dev->downstream && dev->downstream->children)
163 pnp_ssdt_enter_conf_mode(dev->downstream->children, "^INDX", "^DATA");
Patrick Rudolph95bff2e2019-12-10 14:12:03 +0100164
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 */
Arthur Heymans7fcd4d52023-08-24 15:12:19 +0200181 if (dev->downstream && dev->downstream->children)
182 pnp_ssdt_exit_conf_mode(dev->downstream->children, "^INDX", "^DATA");
Patrick Rudolph95bff2e2019-12-10 14:12:03 +0100183
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
Patrick Rudolphc1621312019-05-28 11:29:29 +0200290 acpigen_pop_len(); /* Device */
291 acpigen_pop_len(); /* Scope */
292}
293
294static const char *generic_acpi_name(const struct device *dev)
295{
Jonathon Hall247ec332023-04-07 14:43:25 -0400296 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 }
Patrick Rudolphc1621312019-05-28 11:29:29 +0200301 return "SIO0";
302}
303#endif
304
305static struct device_operations ops = {
306 .read_resources = generic_read_resources,
307 .set_resources = generic_set_resources,
Christian Walter199f98b2019-12-09 13:27:10 +0100308 .scan_bus = scan_static_bus,
Patrick Rudolphc1621312019-05-28 11:29:29 +0200309#if CONFIG(HAVE_ACPI_TABLES)
Nico Huber68680dd2020-03-31 17:34:52 +0200310 .acpi_fill_ssdt = generic_ssdt,
311 .acpi_name = generic_acpi_name,
Patrick Rudolphc1621312019-05-28 11:29:29 +0200312#endif
313};
314
315static 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
Patrick Rudolphc1621312019-05-28 11:29:29 +0200324}
325
326struct chip_operations superio_common_ops = {
Nicholas Sudsgaardbfb11be2024-01-30 09:53:46 +0900327 .name = "Generic Super I/O",
Patrick Rudolphc1621312019-05-28 11:29:29 +0200328 .enable_dev = enable_dev,
329};