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 <buurin@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/82632
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
This commit is contained in:
Keith Hui 2024-04-16 14:36:01 -04:00 committed by Matt DeVillier
commit 7876bcaa86
5 changed files with 190 additions and 4 deletions

View file

@ -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

View file

@ -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

View file

@ -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 <console/console.h>
#include <device/device.h>
#include <device/pnp.h>
#include <option.h>
#include <pc80/keyboard.h>
#include <superio/conf_mode.h>
#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,
};

View file

@ -4,6 +4,7 @@
#define SUPERIO_NUVOTON_COMMON_H
#ifndef __ACPI__
#include <device/device.h>
#include <device/pnp_type.h>
#include <stdint.h>
@ -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,

View file

@ -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 <assert.h>
#include <acpi/acpi.h>
#include <arch/io.h>
#include <device/pnp_ops.h>
#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);
}