ec/starlabs/merlin: Add support for Nuvoton EC's
Support was created for the NPCE9m5x series, using version 1.1
of the datasheet. The specific model tested was the NPCE985P/G,
on the StarLite Mk IV with version 1.00 of the EC firmware.
Signed-off-by: Sean Rhodes <sean@starlabs.systems>
Change-Id: Ib66baf1e88f5d548ce955dffa00c9b88255b2f95
Reviewed-on: https://review.coreboot.org/c/coreboot/+/62702
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: Paul Menzel <paulepanter@mailbox.org>
diff --git a/src/ec/starlabs/merlin/Kconfig b/src/ec/starlabs/merlin/Kconfig
index 93101b7..ff6077a 100644
--- a/src/ec/starlabs/merlin/Kconfig
+++ b/src/ec/starlabs/merlin/Kconfig
@@ -1,5 +1,11 @@
## SPDX-License-Identifier: GPL-2.0-only
+config EC_STARLABS_NUVOTON
+ bool
+ select EC_ACPI
+ help
+ Interface to Nuvoton embedded controller principally in Star Labs notebooks.
+
config EC_STARLABS_ITE
bool
select EC_ACPI
@@ -33,21 +39,21 @@
config EC_STARLABS_KBL_LEVELS
bool
default n
- depends on EC_STARLABS_ITE
+ depends on EC_STARLABS_ITE || EC_STARLABS_NUVOTON
help
Select if the mainboard supports multiple levels of brightness for the keyboard.
config EC_STARLABS_FAN
bool
default n
- depends on EC_STARLABS_ITE
+ depends on EC_STARLABS_ITE || EC_STARLABS_NUVOTON
help
Select if the mainboard has a fan.
config EC_STARLABS_MAX_CHARGE
bool
default n
- depends on EC_STARLABS_ITE
+ depends on EC_STARLABS_ITE || EC_STARLABS_NUVOTON
help
Select if the mainboard supports limiting the maximum charge of the battery.
diff --git a/src/ec/starlabs/merlin/Makefile.inc b/src/ec/starlabs/merlin/Makefile.inc
index 3445ef3..ac0d5e3 100644
--- a/src/ec/starlabs/merlin/Makefile.inc
+++ b/src/ec/starlabs/merlin/Makefile.inc
@@ -26,3 +26,12 @@
endif
endif
endif
+
+ifeq ($(CONFIG_EC_STARLABS_NUVOTON),y)
+
+EC_VARIANT_DIR := $(call strip_quotes, $(CONFIG_EC_VARIANT_DIR))
+CPPFLAGS_common += -I$(src)/ec/starlabs/merlin/variants/$(EC_VARIANT_DIR)
+
+all-y += nuvoton.c
+
+endif
diff --git a/src/ec/starlabs/merlin/ec.h b/src/ec/starlabs/merlin/ec.h
index 1e7607d..6a6cda5 100644
--- a/src/ec/starlabs/merlin/ec.h
+++ b/src/ec/starlabs/merlin/ec.h
@@ -1,19 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * EC communication interface for ITE Embedded Controller.
+ * EC communication interface for Embedded Controller.
*/
-#ifndef _EC_STARLABS_ITE_H
-#define _EC_STARLABS_ITE_H
+#ifndef _EC_STARLABS_EC_H
+#define _EC_STARLABS_EC_H
/*
* Define the expected value of the PNP base address that is fixed through
* the BADRSEL register controlled within the EC domain by the EC Firmware.
*/
#define ITE_FIXED_ADDR 0x4e
+#define NUVOTON_FIXED_ADDR 0x4e
-/* Logical device number (LDN) assignments. */
+/* Logical device number (LDN) assignments for ITE. */
#define ITE_SP1 0x01 /* Serial Port 1 (UART) */
#define ITE_SP2 0x02 /* Serial Port 2 (UART) */
#define ITE_SWUC 0x04 /* System Wake-Up Control (SWUC) */
@@ -30,9 +31,21 @@
#define ITE_PMC4 0x18 /* Power Management I/F Channel 4 (PMC4) */
#define ITE_PMC5 0x19 /* Power Management I/F Channel 5 (PMC5) */
+/* Logical device number (LDN) assignments for Nuvoton. */
+#define NUVOTON_MSWC 0x04 /* Mobile System Wake-Up Control (MSWC) */
+#define NUVOTON_KBCM 0x05 /* KBC / Mouse Interface */
+#define NUVOTON_KBCK 0x06 /* KBC / Keyboard Interface */
+#define NUVOTON_SHM 0x0f /* Shared Memory (SHM) */
+#define NUVOTON_PM1 0x11 /* Power Management I/F Channel 1 (PM1) */
+#define NUVOTON_PM2 0x12 /* Power Management I/F Channel 2 (PM2) */
+#define NUVOTON_PM3 0x17 /* Power Management I/F Channel 3 (PM3) */
+#define NUVOTON_ESHM 0x1d /* Extended Shared Memory (ESHM) */
+#define NUVOTON_PM4 0x1e /* Power Management I/F Channel 3 (PM4) */
+
/* Host domain registers. */
#define ITE_CHIPID1 0x20 /* Device ID register 1 */
#define ITE_CHIPID2 0x21 /* Device ID register 2 */
+#define NUVOTON_CHIPID 0x27 /* Device ID register */
/* EC RAM common offsets */
#define ECRAM_MAJOR_VERSION 0x00
diff --git a/src/ec/starlabs/merlin/nuvoton.c b/src/ec/starlabs/merlin/nuvoton.c
new file mode 100644
index 0000000..ee8a661
--- /dev/null
+++ b/src/ec/starlabs/merlin/nuvoton.c
@@ -0,0 +1,287 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pnp.h>
+#include <ec/acpi/ec.h>
+#include <option.h>
+#include <pc80/keyboard.h>
+
+#include "ec.h"
+#include "ecdefs.h"
+
+uint16_t ec_get_version(void)
+{
+ return (ec_read(ECRAM_MAJOR_VERSION) << 8) | ec_read(ECRAM_MINOR_VERSION);
+}
+
+static uint8_t get_ec_value_from_option(const char *name,
+ unsigned int fallback,
+ const uint8_t *lut,
+ size_t lut_size)
+{
+ unsigned int index = get_uint_option(name, fallback);
+ if (index >= lut_size)
+ index = fallback;
+ return lut[index];
+}
+
+static uint16_t ec_get_chip_id(unsigned int port)
+{
+ return pnp_read_index(port, NUVOTON_CHIPID);
+}
+
+static void merlin_init(struct device *dev)
+{
+ if (!dev->enabled)
+ return;
+
+ /*
+ * The address/data IO port pair for the Nuvoton EC are configurable
+ * through the EC domain and are fixed by the EC's firmware blob. If
+ * the value(s) passed through the "dev" structure don't match the
+ * expected values then output severe warnings.
+ */
+ if (dev->path.pnp.port != NUVOTON_FIXED_ADDR) {
+ printk(BIOS_ERR, "NUVOTON: Incorrect ports defined in devicetree.cb.\n");
+ printk(BIOS_ERR, "NUVOTON: Serious operational issues will arise.\n");
+ return;
+ }
+
+ const uint16_t chip_id = ec_get_chip_id(dev->path.pnp.port);
+
+ if (chip_id != NUVOTON_CHIPID_VAL) {
+ printk(BIOS_ERR, "NUVOTON: Expected chip ID 0x%04x, but got 0x%04x instead.\n",
+ NUVOTON_CHIPID_VAL, chip_id);
+ return;
+ }
+
+ pc_keyboard_init(NO_AUX_DEVICE);
+
+ /*
+ * Restore settings from CMOS into EC RAM:
+ *
+ * kbl_timeout
+ * fn_ctrl_swap
+ * max_charge
+ * fan_mode
+ * fn_lock_state
+ * trackpad_state
+ * kbl_brightness
+ * kbl_state
+ */
+
+ /*
+ * Keyboard Backlight Timeout
+ *
+ * Setting: kbl_timeout
+ *
+ * Values: 30 Seconds, 1 Minute, 3 Minutes, 5 Minutes, Never
+ * Default: 30 Seconds
+ *
+ */
+ const uint8_t kbl_timeout[] = {
+ SEC_30,
+ MIN_1,
+ MIN_3,
+ MIN_5,
+ NEVER
+ };
+
+ ec_write(ECRAM_KBL_TIMEOUT,
+ get_ec_value_from_option("kbl_timeout",
+ 0,
+ kbl_timeout,
+ ARRAY_SIZE(kbl_timeout)));
+
+ /*
+ * Fn Ctrl Reverse
+ *
+ * Setting: fn_ctrl_swap
+ *
+ * Values: Enabled, Disabled
+ * Default: Disabled
+ *
+ */
+ const uint8_t fn_ctrl_swap[] = {
+ FN_CTRL,
+ CTRL_FN
+ };
+
+ ec_write(ECRAM_FN_CTRL_REVERSE,
+ get_ec_value_from_option("fn_ctrl_swap",
+ 1,
+ fn_ctrl_swap,
+ ARRAY_SIZE(fn_ctrl_swap)));
+
+ /*
+ * Maximum Charge Level
+ *
+ * Setting: max_charge
+ *
+ * Values: 60%, 80%, 100%
+ * Default: 100%
+ *
+ */
+ const uint8_t max_charge[] = {
+ CHARGE_100,
+ CHARGE_80,
+ CHARGE_60
+ };
+
+ if (CONFIG(EC_STARLABS_MAX_CHARGE))
+ ec_write(ECRAM_MAX_CHARGE,
+ get_ec_value_from_option("max_charge",
+ 0,
+ max_charge,
+ ARRAY_SIZE(max_charge)));
+
+ /*
+ * Fan Mode
+ *
+ * Setting: fan_mode
+ *
+ * Values: Quiet, Normal, Aggressive
+ * Default: Normal
+ *
+ */
+ const uint8_t fan_mode[] = {
+ FAN_NORMAL,
+ FAN_AGGRESSIVE,
+ FAN_QUIET
+ };
+
+ if (CONFIG(EC_STARLABS_FAN))
+ ec_write(ECRAM_FAN_MODE,
+ get_ec_value_from_option("fan_mode",
+ 0,
+ fan_mode,
+ ARRAY_SIZE(fan_mode)));
+
+ /*
+ * Function Lock
+ *
+ * Setting: fn_lock_state
+ *
+ * Values: Locked, Unlocked
+ * Default: Locked
+ *
+ */
+ const uint8_t fn_lock_state[] = {
+ UNLOCKED,
+ LOCKED
+ };
+
+ ec_write(ECRAM_FN_LOCK_STATE,
+ get_ec_value_from_option("fn_lock_state",
+ 1,
+ fn_lock_state,
+ ARRAY_SIZE(fn_lock_state)));
+
+ /*
+ * Trackpad State
+ *
+ * Setting: trackpad_state
+ *
+ * Values: Enabled, Disabled
+ * Default: Enabled
+ *
+ */
+ const uint8_t trackpad_state[] = {
+ TRACKPAD_ENABLED,
+ TRACKPAD_DISABLED
+ };
+
+ ec_write(ECRAM_TRACKPAD_STATE,
+ get_ec_value_from_option("trackpad_state",
+ 0,
+ trackpad_state,
+ ARRAY_SIZE(trackpad_state)));
+
+ /*
+ * Keyboard Backlight Brightness
+ *
+ * Setting: kbl_brightness
+ *
+ * Values: Off, Low, High / Off, On
+ * Default: Low
+ *
+ */
+ const uint8_t kbl_brightness[] = {
+ KBL_ON,
+ KBL_OFF,
+ KBL_LOW,
+ KBL_HIGH
+ };
+
+ if (CONFIG(EC_STARLABS_KBL_LEVELS))
+ ec_write(ECRAM_KBL_BRIGHTNESS,
+ get_ec_value_from_option("kbl_brightness",
+ 2,
+ kbl_brightness,
+ ARRAY_SIZE(kbl_brightness)));
+ else
+ ec_write(ECRAM_KBL_BRIGHTNESS,
+ get_ec_value_from_option("kbl_brightness",
+ 0,
+ kbl_brightness,
+ ARRAY_SIZE(kbl_brightness)));
+
+ /*
+ * Keyboard Backlight State
+ *
+ * Setting: kbl_state
+ *
+ * Values: Off, On
+ * Default: On
+ *
+ */
+ const uint8_t kbl_state[] = {
+ KBL_DISABLED,
+ KBL_ENABLED
+ };
+
+ ec_write(ECRAM_KBL_STATE,
+ get_ec_value_from_option("kbl_state",
+ 1,
+ kbl_state,
+ ARRAY_SIZE(kbl_state)));
+}
+
+static struct device_operations ops = {
+ .init = merlin_init,
+ .read_resources = noop_read_resources,
+ .set_resources = noop_set_resources,
+};
+
+static struct pnp_info pnp_dev_info[] = {
+ /* System Wake-Up Control (SWUC) */
+ { NULL, NUVOTON_MSWC, PNP_IO0 | PNP_IRQ0, 0xfff0, },
+ /* KBC / Mouse Interface */
+ { NULL, NUVOTON_KBCM, PNP_IRQ0, },
+ /* KBC / Keyboard Interface */
+ { NULL, NUVOTON_KBCK, PNP_IO0 | PNP_IO1 | PNP_IRQ0, 0x07ff, 0x07ff, },
+ /* Shared Memory / Flash Interface (SMFI) */
+ { NULL, NUVOTON_SHM, PNP_IO0 | PNP_IRQ0, 0xfff0, },
+ /* Power Management I/F Channel 1 (PMC1) */
+ { NULL, NUVOTON_PM1, PNP_IO0 | PNP_IO1 | PNP_IRQ0, 0x07ff, 0x07ff, },
+ /* Power Management I/F Channel 2 (PMC2) */
+ { NULL, NUVOTON_PM2, PNP_IO0 | PNP_IO1 | PNP_IO2 | PNP_IRQ0, 0x07fc,
+ 0x07fc, 0xfff0, },
+ /* Power Management I/F Channel 3 (PMC3) */
+ { NULL, NUVOTON_PM3, PNP_IO0 | PNP_IO1 | PNP_IRQ0, 0x07ff, 0x07ff, },
+ /* Extended Shared Memory (ESHM) */
+ { NULL, NUVOTON_ESHM },
+ /* Power Management I/F Channel 4 (PMC4) */
+ { NULL, NUVOTON_PM4, PNP_IO0 | PNP_IO1 | PNP_IRQ0, 0x07ff, 0x07ff, },
+};
+
+static void enable_dev(struct device *dev)
+{
+ pnp_enable_devices(dev, &ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
+}
+
+struct chip_operations ec_starlabs_merlin_ops = {
+ CHIP_NAME("NUVOTON EC")
+ .enable_dev = enable_dev
+};