superio/nuvoton/npcd378: Add ACPI code for S3 resume

Configure SuperIO on shutdown to keep devices enabled, set green LED
to fading on sleep and normal on wake.
Add SSDT to write LDN4 IOBASE addresses stored in devicetree.cb.

Tested on HP8200:
* Wakes from power button or USB keyboard.
* LED is fading

Change-Id: I2035249a39616aa2d87bd93f9e49c70d231546cc
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/27510
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
This commit is contained in:
Patrick Rudolph 2018-07-17 11:41:10 +02:00 committed by Patrick Georgi
parent 9990943782
commit 9ae150a591
2 changed files with 229 additions and 8 deletions

View File

@ -78,14 +78,6 @@ Device(SUPERIO_DEV) {
PNP_IRQ1, 8, /* Second IRQ */
}
Method (_CRS)
{
/* Announce the used I/O ports to the OS */
Return (ResourceTemplate () {
IO (Decode16, SUPERIO_PNP_BASE, SUPERIO_PNP_BASE, 0x01, 0x02)
})
}
#undef PNP_ENTER_MAGIC_1ST
#undef PNP_ENTER_MAGIC_2ND
#undef PNP_ENTER_MAGIC_3RD
@ -165,4 +157,180 @@ Device(SUPERIO_DEV) {
#define SUPERIO_PNP_IO0 0x08, 0x08
#include <superio/acpi/pnp_generic.asl>
#endif
// generated by SSDT
External(SWB, IntObj)
External(SWL, IntObj)
OperationRegion (SWCR, SystemIO, SWB, SWL)
Field (SWCR, ByteAcc, NoLock, Preserve)
{
LEDC, 8,
SWCC, 8
}
// generated by SSDT
External(RNB, IntObj)
External(RNL, IntObj)
OperationRegion (RNTR, SystemIO, RNB, RNL)
Field (RNTR, ByteAcc, NoLock, Preserve)
{
GPES, 8,
GPEE, 8,
Offset (0x08),
GPS0, 8,
GPS1, 8,
GPS2, 8,
GPS3, 8,
GPE0, 8,
GPE1, 8,
GPE2, 8,
GPE3, 8
}
Name (MSFG, One)
Name (KBFG, One)
Name (PMFG, Zero) // Wake event backup
Method (_CRS, 0, Serialized)
{
Name (CRS, ResourceTemplate ()
{
FixedIO (SUPERIO_PNP_BASE, 0x02)
// filled below
FixedIO (0, 0, CRS1)
FixedIO (0, 0, CRS2)
})
CreateWordField (CRS, CRS1._BAS, TMP1)
Store(SWB, TMP1)
CreateByteField (CRS, CRS1._LEN, TMP2)
Store(SWL, TMP2)
CreateWordField (CRS, CRS2._BAS, TMP3)
Store(RNB, TMP3)
CreateByteField (CRS, CRS2._LEN, TMP4)
Store(RNL, TMP4)
/* Announce the used I/O ports to the OS */
Return (CRS)
}
#ifdef SUPERIO_SHOW_KBC
#if defined(SUPERIO_KBC_LDN)
#define _PS2_KB SUPERIO_ID(KBD, SUPERIO_KBC_LDN)
#else
#define _PS2_KB PS2K
#endif
Scope (_PS2_KB)
{
Method (_PSW, 1, NotSerialized)
{
KBFG = Arg0
}
Method (_PRW, 0, NotSerialized)
{
Return (Package (0x02) {0x08, 0x03})
}
}
#if defined(SUPERIO_KBC_PS2M)
#define _PS2_M SUPERIO_ID(PS2, SUPERIO_KBC_PS2M)
#elif defined(SUPERIO_KBC_PS2LDN)
#define _PS2_M SUPERIO_ID(PS2, SUPERIO_KBC_PS2LDN)
#else
#define _PS2_M PS2M
#endif
Scope (_PS2_M)
{
Method (_PSW, 1, NotSerialized)
{
MSFG = Arg0
}
Method (_PRW, 0, NotSerialized)
{
Return (Package (0x02) {0x08, 0x03})
}
}
Method (SIOH, 0, NotSerialized)
{
If ((PMFG & 0xE8))
{
Notify (_PS2_KB, 0x02)
}
If ((PMFG & 0x10))
{
Notify (_PS2_M, 0x02)
}
}
#else
Method (SIOH, 0, NotSerialized)
{
}
#endif
/* SuperIO sleep method */
Method (SIOS, 1, NotSerialized)
{
If ((0x05 != Arg0))
{
/* Set PS/2 powerstate in S3 */
If (KBFG)
{
GPE2 |= 0xE8
}
Else
{
GPE2 &= 0x17
}
If (MSFG)
{
GPE2 |= 0x10
}
Else
{
GPE2 &= 0xEF
}
/* Enable wake on GPE */
GPEE = One
If ((0x03 == Arg0))
{
/* green LED fading */
Local1 = LEDC
Local1 &= 0xE0
LEDC = (Local1 | 0x1C)
Local1 = SWCC
Local1 &= 0xBF
SWCC = (Local1 | 0x40)
}
}
GPE0 = 0x10
GPE1 = 0x20
}
/* SuperIO wake method */
Method (SIOW, 1, NotSerialized)
{
/* Store wake status */
PMFG = GPS2
/* Disable wake on GPE */
GPEE = Zero
GPE0 = Zero
GPE1 = Zero
/* green LED normal */
Local1 = LEDC
Local1 &= 0xE0
LEDC = (Local1 | 0x1E)
Local1 = SWCC
SWCC = (Local1 & 0xBF)
}
}

View File

@ -25,6 +25,8 @@
#include <pc80/keyboard.h>
#include <stdlib.h>
#include <superio/conf_mode.h>
#include <arch/acpi.h>
#include <arch/acpigen.h>
#include "npcd378.h"
@ -101,6 +103,53 @@ static void npcd378_init(struct device *dev)
}
}
#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
static void npcd378_ssdt(struct device *dev)
{
struct resource *res;
const char *scope = acpi_device_path(dev);
if (!scope) {
printk(BIOS_ERR, "%s: Missing ACPI scope\n", dev_path(dev));
return;
}
switch (dev->path.pnp.device) {
case NPCD378_PWR: {
res = find_resource(dev, PNP_IDX_IO0);
if (!res || !res->base) {
printk(BIOS_ERR, "NPCD378: LDN%u IOBASE not set.\n",
NPCD378_PWR);
break;
}
acpigen_write_scope(scope);
acpigen_write_name_integer("SWB", res->base);
acpigen_write_name_integer("SWL", res->size);
acpigen_pop_len(); /* pop scope */
res = find_resource(dev, PNP_IDX_IO1);
if (!res || !res->base) {
printk(BIOS_ERR, "NPCD378: LDN%u IOBASE2 not set.\n",
NPCD378_PWR);
break;
}
acpigen_write_scope(scope);
acpigen_write_name_integer("RNB", res->base);
acpigen_write_name_integer("RNL", res->size);
acpigen_pop_len(); /* pop scope */
break;
}
}
}
static const char *npcd378_acpi_name(const struct device *dev)
{
return "SIO0";
}
#endif
static struct device_operations ops = {
.read_resources = pnp_read_resources,
.set_resources = pnp_set_resources,
@ -108,6 +157,10 @@ static struct device_operations ops = {
.enable = pnp_alt_enable,
.init = npcd378_init,
.ops_pnp_mode = &pnp_conf_mode_8787_aa,
#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
.acpi_fill_ssdt_generator = npcd378_ssdt,
.acpi_name = npcd378_acpi_name,
#endif
};
static struct pnp_info pnp_dev_info[] = {