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