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