1 /* SPDX-License-Identifier: GPL-2.0-or-later */
4 #include <console/console.h>
5 #include <device/device.h>
6 #include <device/pnp.h>
8 #include <pc80/keyboard.h>
9 #include <superio/conf_mode.h>
10 #include <acpi/acpi.h>
11 #include <acpi/acpigen.h>
12 #include <superio/common/ssdt.h>
16 uint8_t npcd378_hwm_read(const uint16_t iobase
, const uint16_t reg
)
18 outb((reg
>> 8) & 0xf, iobase
+ 0xff);
19 uint8_t reg8
= inb(iobase
+ (reg
& 0xff));
21 reg8
= inb(iobase
+ (reg
& 0xff));
23 outb(0, iobase
+ 0xff);
27 void npcd378_hwm_write(const uint16_t iobase
, const uint16_t reg
, const uint8_t val
)
29 outb((reg
>> 8) & 0xf, iobase
+ 0xff);
30 outb(val
, iobase
+ (reg
& 0xff));
32 outb(0, iobase
+ 0xff);
35 void npcd378_hwm_write_start(const uint16_t iobase
)
37 u8 reg8
= npcd378_hwm_read(iobase
, NPCD837_HWM_WRITE_LOCK_CTRL
);
38 reg8
&= ~NPCD837_HWM_WRITE_LOCK_BIT
;
39 npcd378_hwm_write(iobase
, NPCD837_HWM_WRITE_LOCK_CTRL
, reg8
);
42 void npcd378_hwm_write_finished(const uint16_t iobase
)
44 u8 reg8
= npcd378_hwm_read(iobase
, NPCD837_HWM_WRITE_LOCK_CTRL
);
45 reg8
|= NPCD837_HWM_WRITE_LOCK_BIT
;
46 npcd378_hwm_write(iobase
, NPCD837_HWM_WRITE_LOCK_CTRL
, reg8
);
49 static void npcd378_init(struct device
*dev
)
56 switch (dev
->path
.pnp
.device
) {
57 /* TODO: Might potentially need code for FDC etc. */
59 pc_keyboard_init(PROBE_AUX_DEVICE
);
62 res
= probe_resource(dev
, PNP_IDX_IO0
);
63 if (!res
|| !res
->base
) {
64 printk(BIOS_ERR
, "NPCD378: LDN%u IOBASE not set.\n", NPCD378_HWM
);
68 npcd378_hwm_write_start(res
->base
);
70 unsigned int fan_lvl
= get_uint_option("psu_fan_lvl", 3);
74 uint8_t pwm
= NPCD378_HWM_PSU_FAN_MIN
+
75 (NPCD378_HWM_PSU_FAN_MAX
- NPCD378_HWM_PSU_FAN_MIN
) *
78 /* Set PSU fan PWM lvl */
79 npcd378_hwm_write(res
->base
, NPCD378_HWM_PSU_FAN_PWM_CTRL
, pwm
);
80 printk(BIOS_INFO
, "NPCD378: PSU fan PWM 0x%02x\n", pwm
);
82 npcd378_hwm_write_finished(res
->base
);
87 #if CONFIG(HAVE_ACPI_TABLES)
88 /* Provide ACPI HIDs for generic Super I/O SSDT */
89 static const char *npcd378_acpi_hid(const struct device
*dev
)
92 if (dev
->path
.type
!= DEVICE_PATH_PNP
)
94 if (dev
->path
.pnp
.port
== 0)
96 if ((dev
->path
.pnp
.device
& 0xff) > NPCD378_GPIOA
)
99 switch (dev
->path
.pnp
.device
& 0xff) {
104 case NPCD378_SP1
: /* fallthrough */
108 return ACPI_HID_MOUSE
;
110 return ACPI_HID_KEYBOARD
;
116 static void npcd378_ssdt_aux(const struct device
*dev
)
119 acpigen_write_scope(acpi_device_path(dev
));
121 acpigen_write_method("_PSW", 1);
122 acpigen_write_store();
123 acpigen_emit_byte(ARG0_OP
);
124 acpigen_emit_namestring("^^MSFG");
125 acpigen_pop_len(); /* Pop Method */
127 acpigen_write_PRW(8, 3);
129 acpigen_pop_len(); /* Pop Scope */
132 static void npcd378_ssdt_kbc(const struct device
*dev
)
135 acpigen_write_scope(acpi_device_path(dev
));
137 acpigen_write_method("_PSW", 1);
138 acpigen_write_store();
139 acpigen_emit_byte(ARG0_OP
);
140 acpigen_emit_namestring("^^KBFG");
141 acpigen_pop_len(); /* Pop Method */
143 acpigen_write_PRW(8, 3);
145 acpigen_pop_len(); /* Pop Scope */
148 static void npcd378_ssdt_pwr(const struct device
*dev
)
150 const char *name
= acpi_device_path(dev
);
151 const char *scope
= acpi_device_scope(dev
);
154 acpigen_write_scope(name
);
156 acpigen_emit_ext_op(OPREGION_OP
);
157 acpigen_emit_namestring("SWCR");
158 acpigen_emit_byte(SYSTEMIO
);
159 acpigen_emit_namestring("IO0B");
160 acpigen_emit_namestring("IO0S");
162 struct fieldlist l1
[] = {
164 FIELDLIST_NAMESTR("LEDC", 8),
165 FIELDLIST_NAMESTR("SWCC", 8),
168 acpigen_write_field("SWCR", l1
, ARRAY_SIZE(l1
), FIELD_BYTEACC
|
169 FIELD_NOLOCK
| FIELD_PRESERVE
);
171 acpigen_emit_ext_op(OPREGION_OP
);
172 acpigen_emit_namestring("RNTR");
173 acpigen_emit_byte(SYSTEMIO
);
174 acpigen_emit_namestring("IO1B");
175 acpigen_emit_namestring("IO1S");
177 struct fieldlist l2
[] = {
179 FIELDLIST_NAMESTR("GPES", 8),
180 FIELDLIST_NAMESTR("GPEE", 8),
182 FIELDLIST_NAMESTR("GPS0", 8),
183 FIELDLIST_NAMESTR("GPS1", 8),
184 FIELDLIST_NAMESTR("GPS2", 8),
185 FIELDLIST_NAMESTR("GPS3", 8),
186 FIELDLIST_NAMESTR("GPE0", 8),
187 FIELDLIST_NAMESTR("GPE1", 8),
188 FIELDLIST_NAMESTR("GPE2", 8),
189 FIELDLIST_NAMESTR("GPE3", 8),
192 acpigen_write_field("RNTR", l2
, ARRAY_SIZE(l2
), FIELD_BYTEACC
|
193 FIELD_NOLOCK
| FIELD_PRESERVE
);
195 /* Method (SIOW, 1, NotSerialized) */
196 acpigen_write_method("SIOW", 1);
197 acpigen_write_store();
198 acpigen_emit_namestring("^GPS2");
199 acpigen_emit_namestring("^^PMFG");
201 acpigen_write_store();
202 acpigen_emit_byte(ZERO_OP
);
203 acpigen_emit_namestring("^GPEE");
205 acpigen_write_store();
206 acpigen_emit_byte(ZERO_OP
);
207 acpigen_emit_namestring("^GPE0");
209 acpigen_write_store();
210 acpigen_emit_byte(ZERO_OP
);
211 acpigen_emit_namestring("^GPE1");
213 acpigen_emit_byte(AND_OP
);
214 acpigen_emit_namestring("^LEDC");
215 acpigen_write_integer(0xE0);
216 acpigen_emit_byte(LOCAL0_OP
);
218 acpigen_emit_byte(OR_OP
);
219 acpigen_emit_byte(LOCAL0_OP
);
220 acpigen_write_integer(0x1E);
221 acpigen_emit_namestring("^LEDC");
223 acpigen_emit_byte(AND_OP
);
224 acpigen_emit_namestring("^SWCC");
225 acpigen_write_integer(0xBF);
226 acpigen_emit_namestring("^SWCC");
228 acpigen_pop_len(); /* SIOW method */
230 /* Method (SIOS, 1, NotSerialized) */
231 acpigen_write_method("SIOS", 1);
234 acpigen_emit_byte(LNOT_OP
);
235 acpigen_emit_byte(LEQUAL_OP
);
236 acpigen_emit_byte(ARG0_OP
);
237 acpigen_write_integer(5);
240 acpigen_emit_byte(LEQUAL_OP
);
241 acpigen_emit_namestring("^^KBFG");
242 acpigen_emit_byte(ONE_OP
);
244 acpigen_emit_byte(OR_OP
);
245 acpigen_emit_namestring("^GPE2");
246 acpigen_write_integer(0xE8);
247 acpigen_emit_namestring("^GPE2");
249 acpigen_write_else();
251 acpigen_emit_byte(AND_OP
);
252 acpigen_emit_namestring("^GPE2");
253 acpigen_write_integer(0x17);
254 acpigen_emit_namestring("^GPE2");
256 acpigen_pop_len(); /* Pop Else */
259 acpigen_emit_byte(LEQUAL_OP
);
260 acpigen_emit_namestring("^^MSFG");
261 acpigen_emit_byte(ONE_OP
);
263 acpigen_emit_byte(OR_OP
);
264 acpigen_emit_namestring("^GPE2");
265 acpigen_write_integer(0x10);
266 acpigen_emit_namestring("^GPE2");
268 acpigen_write_else();
270 acpigen_emit_byte(AND_OP
);
271 acpigen_emit_namestring("^GPE2");
272 acpigen_write_integer(0xEF);
273 acpigen_emit_namestring("^GPE2");
275 acpigen_pop_len(); /* Pop Else */
277 /* Enable wake on GPE */
278 acpigen_write_store();
279 acpigen_emit_byte(ONE_OP
);
280 acpigen_emit_namestring("^GPEE");
283 acpigen_emit_byte(LEQUAL_OP
);
284 acpigen_emit_byte(ARG0_OP
);
285 acpigen_write_integer(3);
287 acpigen_emit_byte(AND_OP
);
288 acpigen_emit_namestring("^LEDC");
289 acpigen_write_integer(0xE0);
290 acpigen_emit_byte(LOCAL0_OP
);
292 acpigen_emit_byte(OR_OP
);
293 acpigen_emit_byte(LOCAL0_OP
);
294 acpigen_write_integer(0x1C);
295 acpigen_emit_namestring("^LEDC");
297 acpigen_emit_byte(AND_OP
);
298 acpigen_emit_namestring("^SWCC");
299 acpigen_write_integer(0xBF);
300 acpigen_emit_byte(LOCAL0_OP
);
302 acpigen_emit_byte(OR_OP
);
303 acpigen_emit_byte(LOCAL0_OP
);
304 acpigen_write_integer(0x40);
305 acpigen_emit_namestring("^SWCC");
307 acpigen_pop_len(); /* Pop If */
309 acpigen_pop_len(); /* Pop If */
311 acpigen_write_store();
312 acpigen_write_integer(0x10);
313 acpigen_emit_namestring("^GPE0");
315 acpigen_write_store();
316 acpigen_write_integer(0x20);
317 acpigen_emit_namestring("^GPE1");
319 acpigen_pop_len(); /* Pop SIOS method */
321 acpigen_pop_len(); /* Pop Scope */
323 /* Inject into parent: */
325 printk(BIOS_ERR
, "%s: Missing ACPI path/scope\n", dev_path(dev
));
328 acpigen_write_scope(scope
);
330 acpigen_write_name_integer("MSFG", 1);
331 acpigen_write_name_integer("KBFG", 1);
332 acpigen_write_name_integer("PMFG", 0);
334 /* DSDT must call SIOW on _WAK */
335 /* Method (SIOW, 1, NotSerialized) */
336 acpigen_write_method("SIOW", 1);
337 acpigen_emit_byte(RETURN_OP
);
338 acpigen_emit_namestring(acpi_device_path_join(dev
, "SIOW"));
340 acpigen_emit_byte(ARG0_OP
);
343 /* DSDT must call SIOS on _PTS */
344 /* Method (SIOS, 1, NotSerialized) */
345 acpigen_write_method("SIOS", 1);
346 acpigen_emit_byte(RETURN_OP
);
347 acpigen_emit_namestring(acpi_device_path_join(dev
, "SIOS"));
348 acpigen_emit_byte(ARG0_OP
);
349 acpigen_pop_len(); /* Pop Method */
351 acpigen_pop_len(); /* Scope */
353 acpigen_write_scope("\\_GPE");
355 /* Method (SIOH, 0, NotSerialized) */
356 acpigen_write_method("_L08", 0);
357 acpigen_emit_byte(AND_OP
);
358 acpigen_emit_namestring(acpi_device_path_join(dev
->upstream
->dev
, "PMFG"));
359 acpigen_write_integer(0xE8);
360 acpigen_emit_byte(LOCAL0_OP
);
363 acpigen_emit_byte(LGREATER_OP
);
364 acpigen_emit_byte(LOCAL0_OP
);
365 acpigen_emit_byte(ZERO_OP
);
367 acpigen_emit_byte(NOTIFY_OP
);
368 acpigen_emit_namestring(acpi_device_path_join(dev
->upstream
->dev
, "L060"));
369 acpigen_write_integer(2);
371 acpigen_pop_len(); /* Pop If */
373 acpigen_emit_byte(AND_OP
);
374 acpigen_emit_namestring(acpi_device_path_join(dev
->upstream
->dev
, "PMFG"));
375 acpigen_write_integer(0x10);
376 acpigen_emit_byte(LOCAL0_OP
);
379 acpigen_emit_byte(LGREATER_OP
);
380 acpigen_emit_byte(LOCAL0_OP
);
381 acpigen_emit_byte(ZERO_OP
);
383 acpigen_emit_byte(NOTIFY_OP
);
384 acpigen_emit_namestring(acpi_device_path_join(dev
->upstream
->dev
, "L050"));
385 acpigen_write_integer(2);
386 acpigen_pop_len(); /* Pop If */
388 acpigen_pop_len(); /* Pop Method */
390 acpigen_pop_len(); /* Scope */
393 static void npcd378_fill_ssdt_generator(const struct device
*dev
)
398 superio_common_fill_ssdt_generator(dev
);
400 switch (dev
->path
.pnp
.device
) {
402 npcd378_ssdt_pwr(dev
);
405 npcd378_ssdt_aux(dev
);
408 npcd378_ssdt_kbc(dev
);
414 static struct device_operations ops
= {
415 .read_resources
= pnp_read_resources
,
416 .set_resources
= pnp_set_resources
,
417 .enable_resources
= pnp_enable_resources
,
418 .enable
= pnp_alt_enable
,
419 .init
= npcd378_init
,
420 .ops_pnp_mode
= &pnp_conf_mode_8787_aa
,
421 #if CONFIG(HAVE_ACPI_TABLES)
422 .acpi_fill_ssdt
= npcd378_fill_ssdt_generator
,
423 .acpi_name
= superio_common_ldn_acpi_name
,
424 .acpi_hid
= npcd378_acpi_hid
,
428 static struct pnp_info pnp_dev_info
[] = {
429 { NULL
, NPCD378_FDC
, PNP_IO0
|PNP_IRQ0
|PNP_DRQ0
, 0x0ff8, },
430 { NULL
, NPCD378_PP
, PNP_IO0
|PNP_IRQ0
|PNP_DRQ0
, 0x0ff8, },
431 { NULL
, NPCD378_SP1
, PNP_IO0
|PNP_IRQ0
, 0x0ff8, },
432 { NULL
, NPCD378_SP2
, PNP_IO0
|PNP_IRQ0
, 0x0ff8, },
433 { NULL
, NPCD378_PWR
, PNP_IO0
|PNP_IO1
|PNP_IRQ0
|PNP_MSC0
|
434 PNP_MSC1
|PNP_MSC2
|PNP_MSC3
|PNP_MSC4
|PNP_MSC5
|PNP_MSC6
|PNP_MSC7
|
435 PNP_MSC8
|PNP_MSC9
|PNP_MSCA
|PNP_MSCB
|PNP_MSCC
|PNP_MSCD
|PNP_MSCE
,
437 { NULL
, NPCD378_AUX
, PNP_IRQ0
, 0x0fff, 0x0fff, },
438 { NULL
, NPCD378_KBC
, PNP_IO0
|PNP_IO1
|PNP_IRQ0
,
440 { NULL
, NPCD378_WDT1
, PNP_IO0
|PNP_MSC8
|PNP_MSC9
|
441 PNP_MSCA
|PNP_MSCB
|PNP_MSCC
|PNP_MSCD
|PNP_MSCE
, 0x0fe0},
442 { NULL
, NPCD378_HWM
, PNP_IO0
|PNP_MSC0
|PNP_MSC1
|PNP_MSC2
|PNP_MSC3
|
443 PNP_MSC4
|PNP_MSC5
|PNP_MSC6
|PNP_MSC7
|PNP_IRQ0
, 0x0f00},
444 { NULL
, NPCD378_GPIO_PP_OD
, PNP_MSC0
|PNP_MSC1
|PNP_MSC2
|PNP_MSC3
|
445 PNP_MSC4
|PNP_MSC5
|PNP_MSC6
|PNP_MSC7
|PNP_MSC8
|PNP_MSC9
|PNP_MSCA
|
446 PNP_MSCB
|PNP_MSCC
|PNP_MSCD
|PNP_MSCE
},
447 { NULL
, NPCD378_I2C
, PNP_IO0
|PNP_IO1
|PNP_IRQ0
|PNP_MSC0
|
448 PNP_MSC1
|PNP_MSC2
|PNP_MSC3
|PNP_MSC4
|PNP_MSC5
|PNP_MSC6
|PNP_MSC7
|
449 PNP_MSC8
|PNP_MSC9
|PNP_MSCA
|PNP_MSCB
|PNP_MSCC
|PNP_MSCD
|PNP_MSCE
,
451 { NULL
, NPCD378_SUSPEND
, PNP_IO0
, 0x0fe0 },
452 { NULL
, NPCD378_GPIOA
, PNP_IO0
|PNP_MSC0
|PNP_MSC1
|PNP_MSC2
|PNP_MSC3
|
456 static void enable_dev(struct device
*dev
)
458 pnp_enable_devices(dev
, &ops
, ARRAY_SIZE(pnp_dev_info
), pnp_dev_info
);
461 struct chip_operations superio_nuvoton_npcd378_ops
= {
462 .name
= "NUVOTON NPCD378 Super I/O",
463 .enable_dev
= enable_dev
,