From 7876bcaa86c61ea8809f0f6d4aa38455c025a8eb Mon Sep 17 00:00:00 2001 From: Keith Hui Date: Tue, 16 Apr 2024 14:36:01 -0400 Subject: [PATCH] sio/nuvoton: Implement common ramstage keyboard/ACPI init routines With few exceptions, Nuvoton super I/O chips have the same configuration registers. This patch provides a single set of initialization routines to set up power loss resume, initialize keyboard, and handle switching PS/2 port role for mainboards with only one PS/2 port. Power loss resume handling and PS/2 port role switching can be controlled using nvram or CFR options. Mainboards lacking such options will need to add them separately. The common routines support all currently in-tree Nuvoton SIO chips except nct5104d, nct6687d, npcd378, wpcm450. They will be hooked up in follow-up patches. There is also SMM code to disable PS/2 keyboard wakeup before going to S5, otherwise the system will also power up with any key on the keyboard, which is most likely undesirable. To use this, mainboards must set Kconfig SUPERIO_NUVOTON_PNP_BASE, add smm.c to Makefile.mk and call it from mainboard_smi_sleep(). Update the help text of HAVE_SHARED_PS2_PORT Kconfig with the final option name used. TEST=Both power loss resume and PS/2 port role control work on asus/p8z77-m with entire train of supporting patches applied. Change-Id: Ibaddcaa2d77c5b06ddfbb6dbaac00df5e72dd4bd Signed-off-by: Keith Hui Reviewed-on: https://review.coreboot.org/c/coreboot/+/82632 Tested-by: build bot (Jenkins) Reviewed-by: Matt DeVillier Reviewed-by: Angel Pons --- src/superio/nuvoton/Makefile.mk | 2 + src/superio/nuvoton/common/Kconfig | 5 +- src/superio/nuvoton/common/common.c | 137 +++++++++++++++++++++++++++ src/superio/nuvoton/common/nuvoton.h | 19 +++- src/superio/nuvoton/common/smm.c | 31 ++++++ 5 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 src/superio/nuvoton/common/common.c create mode 100644 src/superio/nuvoton/common/smm.c 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); +}