/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #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]; } void ec_mirror_flag(void) { /* * For the mirror flag to work, the status of the EC pin must be known * at all times, which means external power. This can be either a DC * charger, or PD with CCG6. PD with an ANX7447 requires configuration * from the EC, so the update will interrupt this. * * This means we can unconditionally apply the mirror flag to devices * that have CCG6, present on devices with TBT, but have a manual * flag for devices without it. */ uint16_t ec_version = ec_get_version(); /* Full mirror support was added in EC 1.18 (0x0112) */ if (ec_version < 0x0112) return; if (CONFIG(EC_STARLABS_MIRROR_SUPPORT) && (CONFIG(DRIVERS_INTEL_USB4_RETIMER) || get_uint_option("mirror_flag", 0)) && (ec_version != CONFIG_EC_STARLABS_MIRROR_VERSION)) { printk(BIOS_ERR, "ITE: EC version 0x%x doesn't match coreboot version 0x%x.\n", ec_version, CONFIG_EC_STARLABS_MIRROR_VERSION); ec_write(ECRAM_MIRROR_FLAG, MIRROR_ENABLED); } } static uint16_t ec_get_chip_id(unsigned int port) { return (pnp_read_index(port, ITE_CHIPID1) << 8) | pnp_read_index(port, ITE_CHIPID2); } static void merlin_init(struct device *dev) { if (!dev->enabled) return; /* * The address/data IO port pair for the ite 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 != ITE_FIXED_ADDR) { printk(BIOS_ERR, "ITE: Incorrect ports defined in devicetree.cb.\n"); printk(BIOS_ERR, "ITE: Serious operational issues will arise.\n"); return; } const uint16_t chip_id = ec_get_chip_id(dev->path.pnp.port); if (chip_id != ITE_CHIPID_VAL) { printk(BIOS_ERR, "ITE: Expected chip ID 0x%04x, but got 0x%04x instead.\n", ITE_CHIPID_VAL, chip_id); return; } ec_mirror_flag(); /* * 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", 0, 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 * * Note: Always enable, as the brightness level of `off` disables it. * */ ec_write(ECRAM_KBL_STATE, KBL_ENABLED); } 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[] = { /* Serial Port 1 (UART1) */ { NULL, ITE_SP1, PNP_IO0 | PNP_IRQ0, 0x0ff8, }, /* Serial Port 2 (UART2) */ { NULL, ITE_SP2, PNP_IO0 | PNP_IRQ0, 0x0ff8, }, /* System Wake-Up Control (SWUC) */ { NULL, ITE_SWUC, PNP_IO0 | PNP_IRQ0, 0xfff0, }, /* KBC / Mouse Interface */ { NULL, ITE_SWUC, PNP_IRQ0, }, /* KBC / Keyboard Interface */ { NULL, ITE_KBCK, PNP_IO0 | PNP_IO1 | PNP_IRQ0, 0x07ff, 0x07ff, }, /* Consumer IR (CIR) */ { NULL, ITE_IR, PNP_IO0 | PNP_IRQ0, 0xfff8, }, /* Shared Memory / Flash Interface (SMFI) */ { NULL, ITE_SMFI, PNP_IO0 | PNP_IRQ0, 0xfff0, }, /* RTC-like Timer (RCTC) */ { NULL, ITE_RTCT, PNP_IO0 | PNP_IO1 | PNP_IO2 | PNP_IO3 | PNP_IRQ0, 0xfffe, 0xfffe, 0xfffe, 0xfffe, }, /* Power Management I/F Channel 1 (PMC1) */ { NULL, ITE_PMC1, PNP_IO0 | PNP_IO1 | PNP_IRQ0, 0x07ff, 0x07ff, }, /* Power Management I/F Channel 2 (PMC2) */ { NULL, ITE_PMC2, PNP_IO0 | PNP_IO1 | PNP_IO2 | PNP_IRQ0, 0x07fc, 0x07fc, 0xfff0, }, /* Serial Peripheral Interface (SSPI) */ { NULL, ITE_SSPI, PNP_IO0 | PNP_IRQ0, 0xfff8, }, /* Platform Environment Control Interface (PECI) */ { NULL, ITE_PECI, PNP_IRQ0, 0xfff8, }, /* Power Management I/F Channel 3 (PMC3) */ { NULL, ITE_PMC3, PNP_IO0 | PNP_IO1 | PNP_IRQ0, 0x07ff, 0x07ff, }, /* Power Management I/F Channel 4 (PMC4) */ { NULL, ITE_PMC4, PNP_IO0 | PNP_IO1 | PNP_IRQ0, 0x07ff, 0x07ff, }, /* Power Management I/F Channel 5 (PMC5) */ { NULL, ITE_PMC5, 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 = { .name = "ITE EC", .enable_dev = enable_dev };