diff --git a/src/superio/nuvoton/Makefile.mk b/src/superio/nuvoton/Makefile.mk index f56f5f4734..eab992a666 100644 --- a/src/superio/nuvoton/Makefile.mk +++ b/src/superio/nuvoton/Makefile.mk @@ -3,6 +3,8 @@ ## include generic nuvoton pre-ram stage driver bootblock-$(CONFIG_SUPERIO_NUVOTON_COMMON_PRE_RAM) += common/early_serial.c romstage-$(CONFIG_SUPERIO_NUVOTON_COMMON_PRE_RAM) += common/early_serial.c +## include generic nuvoton helper +ramstage-$(CONFIG_SUPERIO_NUVOTON_COMMON_PRE_RAM) += common/common.c subdirs-$(CONFIG_SUPERIO_NUVOTON_WPCM450) += wpcm450 subdirs-$(CONFIG_SUPERIO_NUVOTON_NCT5104D) += nct5104d diff --git a/src/superio/nuvoton/common/Kconfig b/src/superio/nuvoton/common/Kconfig index 2525c5d445..f3e8e4ae93 100644 --- a/src/superio/nuvoton/common/Kconfig +++ b/src/superio/nuvoton/common/Kconfig @@ -18,9 +18,8 @@ config HAVE_SHARED_PS2_PORT help Select this option if your mainboard has a single PS/2 port for both keyboard and mouse, and where a Y-cable cannot allow both keyboard and mouse to work off the same - port. This one port usually works for keyboards only. Add the nvram option - "swap_keyboard_and_mouse" to cmos.layout to allow changing this port into a - mouse port. + port. This one port usually works for keyboards only. Add the nvram/CFR option + "ps2_port_role" to allow changing this port into a mouse port. config SUPERIO_NUVOTON_PNP_BASE hex diff --git a/src/superio/nuvoton/common/common.c b/src/superio/nuvoton/common/common.c new file mode 100644 index 0000000000..e2948cfb2d --- /dev/null +++ b/src/superio/nuvoton/common/common.c @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Configuration routines common to Nuvoton SIO chips, + * except nct5104d, nct6687d, npcd378, wpcm450. + */ + +#include +#include +#include +#include +#include +#include +#include "nuvoton.h" + +#define MAINBOARD_POWER_OFF 0 +#define MAINBOARD_POWER_ON 1 +#define MAINBOARD_POWER_KEEP 2 + +#define POWER_LOSS_CONTROL_SHIFT 5 +#define POWER_LOSS_CONTROL_MASK (3 << POWER_LOSS_CONTROL_SHIFT) + +/* CR 0xe0 */ +#define KBD_WAKEUP_PSOUT BIT(6) +#define KBD_MS_SWAP BIT(2) +#define KBXKEY BIT(0) +/* CR 0xe4 */ +/* Power RAM in S3; for reference only; this needs to be enabled in bootblock */ +#define EN_3VSBSW BIT(4) +#define KBD_WAKEUP_ANYKEY BIT(3) + +static void enable_ps2_mouse(struct device *dev, bool en) +{ + if (!CONFIG(HAVE_SHARED_PS2_PORT)) + return; + + pnp_enter_conf_mode(dev); + u8 bit = KBD_MS_SWAP; + /* pnp_set_logical_device() is not used because the bit is in another LD. */ + pnp_write_config(dev, 0x7, NCT677X_ACPI); + pnp_unset_and_set_config(dev, 0xe0, bit, en ? bit : 0); + pnp_exit_conf_mode(dev); +} + +void nuvoton_common_init(struct device *dev) +{ + u8 byte, role, mask; + + if (!dev->enabled) + return; + + switch (dev->path.pnp.device) { + case NCT677X_KBC: + /* + * If mainboard has both PS/2 keyboard and mouse ports, bootblock code + * should enable both and leave them enabled. + */ + role = PS2_PORT_ROLE_KEYBOARD; + if (CONFIG(HAVE_SHARED_PS2_PORT)) { + role = get_uint_option("ps2_port_role", PS2_PORT_ROLE_KEYBOARD); + if (role > PS2_PORT_ROLE_AUTO) + break; /* Invalid setting; abort */ + } + if (role != PS2_PORT_ROLE_KEYBOARD) { + enable_ps2_mouse(dev, true); + if (role == PS2_PORT_ROLE_MOUSE) + break; /* Leave the port swapped; we're done */ + + /* This leaves PS2_PORT_ROLE_AUTO */ + if (pc_keyboard_init(PROBE_AUX_DEVICE)) { + break; /* Mouse found; leave the port swapped */ + } else { + printk(BIOS_INFO, "%s: Mouse not detected.\n", __func__); + } + } + /* Set port as keyboard, and initialize keyboard if enabled in Kconfig */ + enable_ps2_mouse(dev, false); + pc_keyboard_init(NO_AUX_DEVICE); + break; + case NCT677X_ACPI: + pnp_enter_conf_mode(dev); + pnp_set_logical_device(dev); + + /* + * Keyboard wakeup: any key + * NOTE: Bits related to keyboard wakeup are kept even in S5 (soft off), + * and will allow keyboard to wake system even from this state. + * Add SMM/ACPI code to turn it back off before entering S5. + */ + pnp_unset_and_set_config(dev, 0xe0, 0, KBD_WAKEUP_PSOUT | KBXKEY); + + byte = KBD_WAKEUP_ANYKEY; + mask = 0; + /* + * Set power state after power fail. + * + * Important: Make sure the definitions in mainboard/Kconfig around + * MAINBOARD_POWER_FAILURE_STATE and your board's cmos.layout match. + * And if MAINBOARD_POWER_FAILURE_STATE deviates from the chip's + * expectation (ie. 0=off, 1=on, 2=keep), this code must be adapted. + */ + if (CONFIG(HAVE_POWER_STATE_AFTER_FAILURE)) { + unsigned int power_status = get_uint_option("power_on_after_fail", + CONFIG_MAINBOARD_POWER_FAILURE_STATE); + + switch (power_status) { + case MAINBOARD_POWER_KEEP: + /* + * KEEP power state is software emulated using USER and + * a manually set power state. + */ + power_status = 3; + __fallthrough; + case MAINBOARD_POWER_OFF: + case MAINBOARD_POWER_ON: + byte |= (power_status << POWER_LOSS_CONTROL_SHIFT); + mask = POWER_LOSS_CONTROL_MASK; + break; + } + /* Log a "on" power state */ + pnp_unset_and_set_config(dev, 0xe6, BIT(4), 0); + } + + pnp_unset_and_set_config(dev, 0xe4, mask, byte); + pnp_exit_conf_mode(dev); + break; + } +} + +struct device_operations nuvoton_common_ops = { + .read_resources = noop_read_resources, + .set_resources = pnp_set_resources, + .enable_resources = pnp_enable_resources, + .enable = pnp_alt_enable, + .init = nuvoton_common_init, + .ops_pnp_mode = &pnp_conf_mode_8787_aa, +}; diff --git a/src/superio/nuvoton/common/nuvoton.h b/src/superio/nuvoton/common/nuvoton.h index 77eafeb240..1dd20923e0 100644 --- a/src/superio/nuvoton/common/nuvoton.h +++ b/src/superio/nuvoton/common/nuvoton.h @@ -4,6 +4,7 @@ #define SUPERIO_NUVOTON_COMMON_H #ifndef __ACPI__ +#include #include #include @@ -31,11 +32,27 @@ static __always_inline void nuvoton_pnp_exit_conf_state(pnp_devfn_t dev) outb(NUVOTON_EXIT_KEY, port); } +void nuvoton_enable_serial(pnp_devfn_t dev, u16 iobase); #endif /* SIMPLE_DEVICE */ -void nuvoton_enable_serial(pnp_devfn_t dev, u16 iobase); +#if ENV_RAMSTAGE +extern struct device_operations nuvoton_common_ops; + +void nuvoton_common_init(struct device *dev); #endif +#if ENV_SMM +void nuvoton_smi_sleep(u8 slp_typ); +#endif + +enum e_ps2role { + PS2_PORT_ROLE_KEYBOARD = 0, + PS2_PORT_ROLE_MOUSE = 1, + PS2_PORT_ROLE_AUTO = 2 +}; + +#endif /* __ACPI__ */ + /* * Logical Device Numbers (LDN) common to all Nuvoton SIOs. * Because they will be incorporated into ACPI device IDs, diff --git a/src/superio/nuvoton/common/smm.c b/src/superio/nuvoton/common/smm.c new file mode 100644 index 0000000000..9fdefc43b6 --- /dev/null +++ b/src/superio/nuvoton/common/smm.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * SMM routines common to Nuvoton NCTxxxx SIO chips, + * except 5104, 6687, npcd378, wpcm450. + */ + +/* SMM code needs to be kept simple */ +#define __SIMPLE_DEVICE__ + +#include +#include +#include +#include +#include "nuvoton.h" + +#define ACPI_DEV PNP_DEV(CONFIG_SUPERIO_NUVOTON_PNP_BASE, NCT677X_ACPI) + +void nuvoton_smi_sleep(u8 slp_typ) +{ + if (slp_typ != ACPI_S5) + return; + + _Static_assert(CONFIG_SUPERIO_NUVOTON_PNP_BASE != 0, + "Kconfig SUPERIO_NUVOTON_PNP_BASE must be set!"); + nuvoton_pnp_enter_conf_state(ACPI_DEV); + pnp_set_logical_device(ACPI_DEV); + pnp_unset_and_set_config(ACPI_DEV, 0xe0, BIT(6), 0); /* KB wakeup off */ + pnp_unset_and_set_config(ACPI_DEV, 0xe6, 0, BIT(4)); /* Power off */ + nuvoton_pnp_exit_conf_state(ACPI_DEV); +}