diff options
Diffstat (limited to 'src/ec/clevo')
24 files changed, 2012 insertions, 0 deletions
diff --git a/src/ec/clevo/it5570e/Kconfig b/src/ec/clevo/it5570e/Kconfig new file mode 100644 index 0000000000..8620eb1233 --- /dev/null +++ b/src/ec/clevo/it5570e/Kconfig @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config EC_CLEVO_IT5570E + bool + select EC_ACPI + help + IT5570E embedded controller in Clevo notebooks + +if EC_CLEVO_IT5570E + +config EC_CLEVO_IT5570E_MEM_BASE + hex + help + Memory address for the EC memory region mapped via LGMR + +config EC_CLEVO_IT5570E_AC_FAN_ALWAYS_ON + bool "Fan always on with AC attached" + default n + help + Never turn the fan fully off when AC is attached. + + This setting can be overridden by cmos option `ac_fan_always_on`. + +config EC_CLEVO_IT5570E_KBLED_BOOTEFFECT + bool "Keyboard boot effect" + default n + help + Enable the "breathing" boot effect of the LED keyboard. + + This setting can be overridden by cmos option `kbled_booteffect`. + +config EC_CLEVO_IT5570E_KBLED_TIMEOUT + int "Keyboard backlight timeout" + default 15 + help + Keyboard backlight timeout in seconds. 0 keeps the backlight + always on. + + This setting can be overridden by cmos option `kbled_timeout`. + +config EC_CLEVO_IT5570E_FN_WIN_SWAP + bool "Swap Fn/Windows keys" + default n + help + Swap the Fn and Windows key. + + This setting can be overridden by cmos option `fn_win_swap`. + +config EC_CLEVO_IT5570E_FLEXICHARGER + bool "Flexicharger" + default n + help + Enable the Flexicharger functionality. + + This setting can be overridden by cmos option `flexicharger`. + +if EC_CLEVO_IT5570E_FLEXICHARGER + +config EC_CLEVO_IT5570E_FLEXICHG_START + int "Start charge threshold" + default 95 + help + Start charge threshold in percent. + + This setting can be overridden by cmos option `flexicharger_start`. + +config EC_CLEVO_IT5570E_FLEXICHG_STOP + int "Stop charge threshold" + default 100 + help + Stop charge threshold in percent. + + This setting can be overridden by cmos option `flexicharger_stop`. + +endif + +choice + prompt "Camera default state" + default EC_CLEVO_IT5570E_CAM_BOOT_STATE_KEEP + help + Camera default state. + + This setting can be overridden by cmos option `camera_boot_state`. + +config EC_CLEVO_IT5570E_CAM_BOOT_STATE_KEEP + bool "Keep previous state" + +config EC_CLEVO_IT5570E_CAM_BOOT_STATE_DISABLE + bool "Disable" + +config EC_CLEVO_IT5570E_CAM_BOOT_STATE_ENABLE + bool "Enable" + +endchoice + +config EC_CLEVO_IT5570E_CAM_BOOT_STATE + int + default 0 if EC_CLEVO_IT5570E_CAM_BOOT_STATE_DISABLE + default 1 if EC_CLEVO_IT5570E_CAM_BOOT_STATE_ENABLE + default 2 + +choice + prompt "Touchpad toggle mode" + default EC_CLEVO_IT5570E_TP_TOGGLE_MODE_CTRLALTF9 + help + There are two modes for the touchpad toggle (Fn-F1): + - Ctrl-Alt-F9 mode sends the windows-native touchpad toggle keyboard shortcut. + - Keycode mode sends special key codes f7/f8 which can be configured in udev + to be handled as touchpad toggle. + + This setting can be overridden by cmos option `tp_toggle_mode`. + +config EC_CLEVO_IT5570E_TP_TOGGLE_MODE_CTRLALTF9 + bool "Ctrl-Alt-F9" + +config EC_CLEVO_IT5570E_TP_TOGGLE_MODE_KEYOCDE_F7F8 + bool "Keycode f7/f8" + +endchoice + +config EC_CLEVO_IT5570E_TP_TOGGLE_MODE + int + default 0 if EC_CLEVO_IT5570E_TP_TOGGLE_MODE_CTRLALTF9 + default 1 if EC_CLEVO_IT5570E_TP_TOGGLE_MODE_KEYOCDE_F7F8 + +endif diff --git a/src/ec/clevo/it5570e/Makefile.inc b/src/ec/clevo/it5570e/Makefile.inc new file mode 100644 index 0000000000..46d97d2dc6 --- /dev/null +++ b/src/ec/clevo/it5570e/Makefile.inc @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +ifeq ($(CONFIG_EC_CLEVO_IT5570E),y) + +bootblock-y += early_init.c +bootblock-y += i2ec.c + +ramstage-y += ec.c +ramstage-y += commands.c +ramstage-y += smbios.c +ramstage-y += ssdt.c + +smm-y += commands.c +smm-y += smihandler.c + +endif diff --git a/src/ec/clevo/it5570e/acpi/ac.asl b/src/ec/clevo/it5570e/acpi/ac.asl new file mode 100644 index 0000000000..49f4c26f83 --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/ac.asl @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (AC) +{ + Name (_HID, "ACPI0003") + Name (_PCL, Package () { \_SB }) + + Method (_PSR) + { + Return (\_SB.PCI0.LPCB.EC0.ADP) + } +} diff --git a/src/ec/clevo/it5570e/acpi/battery.asl b/src/ec/clevo/it5570e/acpi/battery.asl new file mode 100644 index 0000000000..7b29a2ef9b --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/battery.asl @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define PBST_STATE 0 +#define PBST_PRESENT_RATE 1 +#define PBST_REMAINING_CAP 2 +#define PBST_PRESENT_VOLT 3 + +#define PBIX_DESIGN_CAP 2 +#define PBIX_LAST_FULL_CHG_CAP 3 +#define PBIX_DESIGN_VOLT 5 +#define PBIX_DESIGN_CAP_WARN 6 +#define PBIX_DESIGN_CAP_LOW 7 +#define PBIX_CYCLE_COUNT 8 +#define PBIX_MODEL 16 +#define PBIX_SERIAL 17 +#define PBIX_TYPE 18 +#define PBIX_OEM_INFO 19 + +Device (BAT0) +{ + Name (_HID, EisaId ("PNP0C0A")) + Name (_UID, 0) + Name (_PCL, Package () { \_SB }) + + Name (BFCC, 0) /* Full Charge Capacity */ + + Name (PBST, Package () { + 0, // Battery State + // [0] : Discharging + // [1] : Charging + // [2] : Critical Low + 0, // Battery Present Rate + 0, // Battery Remaining Capacity + 0, // Battery Present Voltage + }) + + Name (PBIX, Package () { + 1, // Revision + 1, // Power Unit (1 = mA(h)) + 0, // Design Capacity + 0, // Last Full Charge Capacity + 1, // Battery Technology (1 = rechargeable) + 0, // Design Voltage + 0, // Design Capacity of Warning + 0, // Design Capacity of Low + 0, // Cycle Count + 95000, // Measurement Accuracy (95 %) + 0, // Max Sampling Time + 0, // Min Sampling Time + 0, // Max Averaging Interval + 0, // Min Averaging Interval + 1, // Battery Capacity Granularity 1 (low < warning) + 1, // Battery Capacity Granularity 2 (warning < full) + " ", // Model Number + " ", // Serial Number + " ", // Battery Type + " ", // OEM Information + 0, // Battery Swapping Capability (0 = not swappable) + }) + + Method (_STA) + { + Local0 = 0x0f + + If (\_SB.PCI0.LPCB.EC0.BAT0) + { + Local0 |= 0x10 /* battery present */ + } + + Return (Local0) + } + + Method (_BST) + { + /* + * Trigger update of static info update when + * the last full charge capacity changes. + * (This is what the vendor does.) + */ + If (BFCC != ToInteger (\_SB.PCI0.LPCB.EC0.BFC0)) + { + Notify (BAT0, 0x81) /* information change */ + } + + /* Convert signed current to absolute value */ + Local0 = ToInteger (\_SB.PCI0.LPCB.EC0.BPR0) + If (Local0 & 0x8000) + { + Local0 = (~Local0 & 0xffff) + 1 + } + + PBST [PBST_STATE] = ToInteger (\_SB.PCI0.LPCB.EC0.BST0) + PBST [PBST_PRESENT_RATE] = Local0 + PBST [PBST_REMAINING_CAP] = ToInteger (\_SB.PCI0.LPCB.EC0.BRC0) + PBST [PBST_PRESENT_VOLT] = ToInteger (\_SB.PCI0.LPCB.EC0.BPV0) + + Return (PBST) + } + + Method (_BIX) + { + BFCC = ToInteger (\_SB.PCI0.LPCB.EC0.BFC0) + PBIX [PBIX_DESIGN_CAP] = ToInteger (\_SB.PCI0.LPCB.EC0.BDC0) + PBIX [PBIX_LAST_FULL_CHG_CAP] = ToInteger (\_SB.PCI0.LPCB.EC0.BFC0) + PBIX [PBIX_DESIGN_VOLT] = ToInteger (\_SB.PCI0.LPCB.EC0.BDV0) + PBIX [PBIX_DESIGN_CAP_WARN] = ToInteger (\_SB.PCI0.LPCB.EC0.BCW0) + PBIX [PBIX_DESIGN_CAP_LOW] = ToInteger (\_SB.PCI0.LPCB.EC0.BCL0) + PBIX [PBIX_CYCLE_COUNT] = ToInteger (\_SB.PCI0.LPCB.EC0.CYC0) + PBIX [PBIX_MODEL] = ToBuffer (\_SB.PCI0.LPCB.EC0.BMO0) + PBIX [PBIX_SERIAL] = ToHexString (\_SB.PCI0.LPCB.EC0.BSN0) + PBIX [PBIX_TYPE] = ToBuffer (\_SB.PCI0.LPCB.EC0.BTY0) + PBIX [PBIX_OEM_INFO] = ToBuffer (\_SB.PCI0.LPCB.EC0.BIF0) + + Return (PBIX) + } +} diff --git a/src/ec/clevo/it5570e/acpi/buttons.asl b/src/ec/clevo/it5570e/acpi/buttons.asl new file mode 100644 index 0000000000..3f5dd12973 --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/buttons.asl @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (PWRB) +{ + Name (_HID, "PNP0C0C") + Name (_PRW, Package () { EC_GPE_PWRB, 4 }) +} + +Device (SLPB) +{ + Name (_HID, "PNP0C0E") + Name (_PRW, Package () { EC_GPE_SLPB, 3 }) +} diff --git a/src/ec/clevo/it5570e/acpi/common.asl b/src/ec/clevo/it5570e/acpi/common.asl new file mode 100644 index 0000000000..21ddbd9816 --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/common.asl @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef EC_GPE_SCI +#error EC_GPE_SCI must be defined by mainboard. +#endif +#ifndef EC_GPE_PWRB +#error EC_GPE_PWRB must be defined by mainboard. +#endif +#ifndef EC_GPE_SLPB +#error EC_GPE_PWRB must be defined by mainboard. +#endif +#ifndef EC_GPE_LID +#error EC_GPE_LID must be defined by mainboard. +#endif + +Scope (\_SB) +{ + #include "ac.asl" + #include "battery.asl" + #include "buttons.asl" + #include "lid.asl" + #include "hid.asl" + + Scope (PCI0.LPCB) + { + #include "ec.asl" + } +} diff --git a/src/ec/clevo/it5570e/acpi/ec.asl b/src/ec/clevo/it5570e/acpi/ec.asl new file mode 100644 index 0000000000..a4b282c4cc --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/ec.asl @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <ec/clevo/it5570e/commands.h> + +#define EC_DATA_IO 0x62 +#define EC_SC_IO 0x66 + +Device (EC0) +{ + Name (_HID, "PNP0C09") + Name (_GPE, EC_GPE_SCI) + + Name (_CRS, ResourceTemplate () + { + IO (Decode16, 0x62, 0x62, 0, 1) + IO (Decode16, 0x66, 0x66, 0, 1) + }) + + #include <ec/acpi/ec.asl> + + OperationRegion (PMS0, SystemMemory, 0xfe001b1c, 4) + Field (PMS0, DWordAcc, NoLock, Preserve) + { + , 15, + GS0E, 1, /* Global SLP_S0# enable */ + } + + #include "ec_ram.asl" + + External (SFCV, MethodObj) /* Generated in SSDT */ + + Method (INIT) + { + Printf ("EC: INIT") + + ECOS = 2 /* ACPI with driver */ + BLCT = 1 /* Enable ACPI brightness control */ + CAMK = 1 /* Enable camera hotkey */ + + SFCV () /* Apply custom fan curve */ + + PNOT () + } + + Method (_INI) + { + Printf ("EC: _INI") + + INIT () + } + + /* Send FCMD */ + Method (SFCC, 1, Serialized) + { + FCMD = Arg0 + SEND_EC_COMMAND(0) + + /* EC sets FCMD = 0x00 on completion (FCMD = 0xfa on some commands) */ + Local0 = 50 + While (Local0--) + { + Stall (1) + If (FCMD == 0x00 || FCMD == 0xfa) + { + Printf("EC: FCMD 0x%o completed after %o ms", + ToHexString(Arg0), ToDecimalString(50 - Local0)) + Return (1) + } + } + Printf("EC: FCMD 0x%o timed out", ToHexString(Arg0)) + Return (0) + } + + /* + * Method called from _PTS prior to system sleep state entry + */ + Method (PTS, 1, Serialized) + { + Printf ("EC: PTS: Arg0=%o", ToDecimalString(Arg0)) + + WFNO = 0 /* Clear wake cause */ + } + + /* + * Method called from _WAK prior to system sleep state wakeup + */ + Method (WAK, 1, Serialized) + { + Printf ("EC: WAK: Arg0=%o, WFNO=%o", ToDecimalString(Arg0), ToHexString (WFNO)) + + INIT () + + /* update battery */ + Notify (\_SB.BAT0, 0x00) /* bus check */ + Notify (\_SB.BAT0, 0x80) /* state change */ + Notify (\_SB.BAT0, 0x81) /* information change */ + + /* update AC */ + Notify (\_SB.AC, 0x00) /* bus check */ + Notify (\_SB.AC, 0x80) /* state change */ + + If (Arg0 == 0x03 || Arg0 == 0x04) { + Notify (\_SB.PWRB, 0x02) /* Wake */ + } + } + + /* + * Display On/Off Notifications + * Called from \_SB.PEPD._DSM + */ + Name (KBLV, 0) + Method (EDSX, 1, Serialized) + { + Printf ("EC: PEP display hook, state=%o", ToDecimalString (Arg0)) + + If (S5FG) + { + Return () + } + + /* Display off */ + If (!Arg0) + { + /* Store current keyboard backlight level */ + FDAT = FDAT_KBLED_WHITE_GET_LEVEL + SFCC (FCMD_KLED) + KBLV = FBUF + + /* Turn off keyboard backlight */ + FDAT = FDAT_KBLED_WHITE_SET_LEVEL + FBUF = 0x00 + SFCC (FCMD_KLED) + } + + /* Display on */ + Else + { + /* Restore keyboard backlight level */ + FDAT = FDAT_KBLED_WHITE_SET_LEVEL + FBUF = KBLV + SFCC (FCMD_KLED) + } + } + + /* + * S0ix Entry/Exit Notifications + * Called from \_SB.PEPD._DSM + */ + Method (S0IX, 1, Serialized) + { + If (S5FG) + { + Return () + } + + Printf ("EC: S0ix change, state=%o", ToDecimalString (Arg0)) + + /* S0ix entry */ + If (Arg0) + { + MSFG = 1 /* Notify EC */ + } + + /* S0ix exit */ + Else + { + GS0E = 0 /* Block SLP_S0# assertion during wakeup */ + MSFG = 0 /* Notfiy EC */ + Sleep (150) /* wait for EC to become ready */ + SFCV () /* Apply custom fan curve */ + GS0E = 1 /* Unblock SLP_S0# */ + } + } + + #include "ec_queries.asl" +} diff --git a/src/ec/clevo/it5570e/acpi/ec_queries.asl b/src/ec/clevo/it5570e/acpi/ec_queries.asl new file mode 100644 index 0000000000..ff9cd06470 --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/ec_queries.asl @@ -0,0 +1,387 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Note: + * The code contains all queries/events that are known to be implemented by the EC firmware. + * Not all events are known yet, though. + */ + +Method (_Q0A) +{ + Printf ("EC: _Q0A: Toggle touchpad, SCIE=0x%o, state=%o", + ToHexString (SCIE), ToDecimalString(SCIE & 1)) +} + +Method (_Q0B) +{ + Printf ("EC: _Q0B: LCD off, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q0C) +{ + Printf ("EC: _Q0C: Toggle mute, SCIE=0x%o, state=%o", + ToHexString (SCIE), ToDecimalString(SCIE & 1)) +} + +Method (_Q0E) +{ + Printf ("EC: _Q0E: Decrease volume") +} + +Method (_Q0F) +{ + Printf ("EC: _Q0F: Increase volume") +} + +Method (_Q10) +{ + Printf ("EC: _Q10, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q11) +{ + Printf ("EC: _Q11: Decrease brightness") + + If (CondRefOf (\_SB.PCI0.GFX0.LCD0)) { + Notify (\_SB.PCI0.GFX0.LCD0, 0x87) + } +} + +Method (_Q12) +{ + Printf ("EC: _Q12: Increase brightness") + + If (CondRefOf (\_SB.PCI0.GFX0.LCD0)) { + Notify (\_SB.PCI0.GFX0.LCD0, 0x86) + } +} + +Method (_Q13) +{ + Printf ("EC: _Q13: Toggle camera, SCIE=0x%o, state=%o", + ToHexString (SCIE), ToDecimalString(SCIE & 1)) +} + +Method (_Q14) +{ + Printf ("EC: _Q14: Toggle airplane mode, SCIE=0x%o", ToHexString (SCIE)) + + \_SB.HIDD.HPEM (8) +} + +Method (_Q15) +{ + Printf ("EC: _Q15: Sleep button") + + Notify (\_SB.SLPB, 0x80) +} + +Method (_Q16) +{ + Printf ("EC: _Q16: Power event (AC/BAT0)") + + Notify (AC, 0x80) /* status change */ + If (BAT0) + { + Notify (\_SB.BAT0, 0x80) /* status change */ + Notify (\_SB.BAT0, 0x81) /* information change */ + } +} + +Method (_Q17) +{ + Printf ("EC: _Q17: Battery presence change, state=%o", ToDecimalString (BAT0)) + + Notify (\_SB.BAT0, 0x81) /* information change */ +} + +Method (_Q19) +{ + Printf ("EC: _Q19: Battery critical") + + Notify (\_SB.BAT0, 0x80) /* status change */ +} + +Method (_Q1A) +{ + Printf ("EC: _Q1A: Wake event, WFNO=0x%o", ToHexString (WFNO)) + + Switch (ToInteger (WFNO)) + { + Case (0x01) + { + Printf ("EC: Wake reason: Lid") + Notify (\_SB.LID, 0x02) /* wake */ + } + + Case (0x04) + { + Printf ("EC: Wake reason: Sleep button") + Notify (\_SB.SLPB, 0x02) /* wake */ + } + + Case (0x05) + { + Printf ("EC: Wake reason: Timer") + Notify (\_SB.PWRB, 0x02) /* wake */ + } + + Case (0x10) + { + Printf ("EC: Wake reason: Battery low") + Notify (\_SB.BAT0, 0x02) /* wake */ + } + + Default + { + Printf ("EC: Wake reason: other") + Notify (\_SB.PWRB, 0x02) /* wake */ + } + } +} + +Method (_Q1B) +{ + Printf ("EC: _Q1B: Lid state change, state=%o", ToDecimalString (LSTE)) + + Notify (\_SB.LID, 0x80) +} + +Method (_Q1D) +{ + Printf ("EC: _Q1D: Power button") + + Notify (\_SB.PWRB, 0x80) +} + +Method (_Q1E) +{ + Printf ("EC: _Q1E: Battery low") +} + +Method (_Q32) +{ + Printf ("EC: _Q32: Battery thermal trip") +} + +Method (_Q35) +{ + Printf ("EC: _Q35: Silent fan mode change, state=%o", ToDecimalString (SLFG)) +} + +Method (_Q37) +{ + Printf ("EC: _Q37: B15C flag change, B15C=%o", ToHexString (B15C)) +} + +Method (_Q42) +{ + Printf ("EC: _Q42, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q46) +{ + Printf ("EC: _Q46, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q4A) +{ + Printf ("EC: _Q4A: KBC beep on/off, SCIE=0x%o, state=%o", + ToHexString (SCIE), ToDecimalString(SCIE & 1)) +} + +Method (_Q50) +{ + Printf ("EC: _Q50: SCI event, SCIE=0x%o", ToHexString (SCIE)) + + Switch (ToInteger (SCIE)) + { + Case (0x68) // L140MU only + { + } + + Case (0x69) // L140MU only + { + } + + Case (0x6a) + { + Printf ("EC: Fan mode: MaxQ") + } + + Case (0x6c) + { + Printf ("EC: Fan mode: custom") + } + + Case (0x7a) + { + } + + Case (0x7b) + { + Printf ("EC: Fn + Backspace pressed") + } + + Case (0x7c) + { + Printf ("EC: Screen rotate (Fn + R)") + } + + Case (0x80) + { + Printf ("EC: Color keyboard color change") + } + + Case (0x81) + { + Printf ("EC: Color keyboard brightness down") + } + + Case (0x82) + { + Printf ("EC: Color keyboard brightness up") + } + + Case (0x8a) + { + Printf ("EC: White keyboard backlight toggle") + } + + Case (0x9f) + { + Printf ("EC: Color keyboard backlight toggle") + } + + Case (0xa0) + { + } + + Case (0xa8) + { + Printf ("EC: Fn + ESC pressed") + } + + Case (0xae) + { + Printf ("EC: airplane mode LED off") + } + + Case (0xaf) + { + Printf ("EC: airplane mode LED on") + } + + Case (0xb0) + { + } + + Case (0xc7) + { + Printf ("EC: NumLock off") + } + + Case (0xc8) + { + Printf ("EC: NumLock on") + } + + Case (0xc9) + { + Printf ("EC: CapsLock off") + } + + Case (0xca) + { + Printf ("EC: CapsLock on") + } + + Case (0xcf) + { + Printf ("EC: ScrollLock off") + } + + Case (0xd0) + { + Printf ("EC: ScrollLock on") + } + + Case (0xf0) + { + } + + Case (0xf1) + { + } + + Case (0xf2) + { + Printf ("EC: Fan mode: auto") + } + + Case (0xf3) + { + Printf ("EC: Fan mode: turbo") + } + } +} + +Method (_Q51) +{ + Printf ("EC: _Q51, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q52) +{ + Printf ("EC: _Q52, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q53) +{ + Printf ("EC: _Q53, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q56) +{ + Printf ("EC: _Q56, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q57) +{ + Printf ("EC: _Q57, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q58) +{ + Printf ("EC: _Q58, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q59) +{ + Printf ("EC: _Q59, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q5A) +{ + Printf ("EC: _Q5A, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q5D) +{ + Printf ("EC: _Q5D: Performance profile hotkey pressed (Fn + 3)") +} + +Method (_Q5E) +{ + Printf ("EC: _Q5E, SCIE=0x%o", ToHexString (SCIE)) +} + +Method (_Q61) +{ + Printf ("EC: _Q61: Board thermal trip") +} + +Method (_Q62) +{ + Printf ("EC: _Q62: UCSI event") +} diff --git a/src/ec/clevo/it5570e/acpi/ec_ram.asl b/src/ec/clevo/it5570e/acpi/ec_ram.asl new file mode 100644 index 0000000000..0f4b39baa4 --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/ec_ram.asl @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Labeling: + * - just offset bits + * ? unknown / not fully understood, used by vendor fw / ec fw + */ + +/* + * Note: not all fields are used by all mainboards. Also some bits/fields are + * still unknown and will be amended as soon as more details are known. + * Naming differs from vendor firmware, since there were many completely + * wrong/misleading names. Fields unused by the vendor EC fw have been + * dropped, since they just were present due to vendor doing copy-pasting. + */ + +OperationRegion (RAM1, SystemMemory, CONFIG_EC_CLEVO_IT5570E_MEM_BASE + 0x100, 0x400) +Field (RAM1, ByteAcc, Lock, Preserve) +{ + Offset (0x03), + LSTE, 1, // Lid open 3h.0 + , 1, // - 3h.1 + LWKE, 1, // Lid wake enable 3h.2 + + Offset (0x04), + AC0, 8, // Active cooling temp 0 4h + PSV, 8, // Passive cooling temp 5h + CRT, 8, // Critical temp 6h + TMP, 8, // CPU temp read from PECI 7h + AC1, 8, // Active cooling temp 1 8h + BBST, 8, // ? dGPU related (BatteryBooST?) 9h + BTMP, 8, // Board temperature ah + + Offset (0x10), + ADP, 1, // AC connected 10h.0 + , 1, // - 10h.1 + BAT0, 1, // BAT0 connected 10h.2 + + Offset (0x11), + WFNO, 8, // Wake cause 11h + // 0x01 = lid + // 0x04 = sleep button + // 0x05 = timer + // 0x10 = battery low + + Offset (0x16), + BDC0, 32, // BAT0 design capacity 16h-19h + BFC0, 32, // BAT0 last full charge capacity 1ah-1dh + + Offset (0x22), + BDV0, 32, // BAT0 design voltage 22h-25h + BST0, 8, // BAT0 status 26h + // BST0[0] : discharging + // BST0[1] : charging + // BST0[2] : critical low + + Offset (0x2a), + BPR0, 32, // BAT0 present current 2ah-2dh + BRC0, 32, // BAT0 remaining capacity 2eh-31h + BPV0, 32, // BAT0 present voltage 32h-35h + + Offset (0x38), + BRS0, 8, // BAT0 relative charge 38h + + Offset (0x3a), + BCW0, 32, // Design capacity of warning 3ah-3dh + BCL0, 32, // Design capacity of low 3eh-41h + + Offset (0x4a), + BMO0, 64, // Model 4ah-51h + BIF0, 64, // Vendor 52h-59h + BSN0, 32, // Serial number 5ah-5dh + BTY0, 64, // Type 5eh-65h + + Offset (0x68), + ECOS, 8, // ACPI OS support 68h + // 0 = no ACPI + // 1 = ACPI w/o driver + // 2 = ACPI w/ driver + + Offset (0x78), + /* + * PECI + * Maybe usable for debugging. Must never be written directly! + */ + PCAD, 8, // PECI address 78h + PEWL, 8, // PECI write length 79h + PWRL, 8, // PECI read length 7ah + PECD, 8, // PECI command 7bh + PEHI, 8, // PECI host ID 7ch + PECI, 8, // PECI index 7dh + PEPL, 8, // PECI LSB 7eh + PEPM, 8, // PECI MSB 7fh + PWFC, 8, // ? 80h + PECC, 8, // PECI completion code 81h + PDT0, 8, // PECI data 82h + PDT1, 8, // PECI data 83h + PDT2, 8, // PECI data 84h + PDT3, 8, // PECI data 85h + + Offset (0x92), + BMD0, 16, // BAT0 manufacturing date 92h-93h + // BMD0[4:0] : day + // BMD0[8:5] : month + // BMD0[15:9] : year - 1980 + CYC0, 16, // BAT0 cycle count 94h-95h + + Offset (0xc7), + VOFF, 8, // VGA fan base offset c7h + FANC, 8, // FAN count (FANC == FANQ) c8h + BLVL, 8, // Legacy display brightness level (unused) c9h + + Offset (0xca), + , 1, // - cah.0 + , 1, // ? cah.1 + CAMK, 1, // Enable webcam hotkey cah.2 + , 2, // - cah.3-4 + WWAN, 1, // WWAN/3G/LTE present (enables WWAN) cah.5 + + Offset (0xcb), + , 5, // - cbh.0-4 + B15C, 1, // ? cbh.5 + , 1, // - cbh.6 + SLFG, 1, // silent fan mode flag cbh.7 + + Offset (0xcc), + SCIE, 8, // SCI extra value cch + + Offset (0xce), + DUT1, 8, // Fan 1 duty ceh + DUT2, 8, // Fan 2 duty cfh + + Offset (0xd0), + RPM1, 16, // Fan 1 RPM d0h-d1h + RPM2, 16, // Fan 2 RPM d2h-d3h + RPM4, 16, // Fan 4 RPM d4h-d5h + + Offset (0xd7), + DTHL, 8, // ? d7h + DTBP, 8, // ? d8h + , 1, // - d9h.0 + WOLD, 1, // ? Disable Wake-on-LAN d9h.1 + PWRM, 2, // current performance profile d9h.2-3 + // 0 = entertainment + // 1 = performance + // 2 = quiet + // 3 = powersave + FN3E, 1, // Fn + 3 enable (power profile toggle) d9h.4 + , 1, // ? d9h.5 + AIRP, 1, // airplane mode status (in non-ACPI mode) d9h.6 + GPUP, 1, // dGPU power status d9h.7 + + Offset (0xda), + BLCT, 1, // ACPI backlight control dah.0 + DBGP, 1, // 3IN1 debug card present flag dah.1 + , 1, // WINF[2] ? dah.2 + MEUL, 1, // ME/IFD unlock (ACPI usage unclear) dah.3 + + Offset (0xdb), + RINF, 8, // dbh + // RINF[0] : set when EC cmd A8 was called + // RINF[1] : - + // RINF[2] : ? TP + // RINF[3] : ? + // RINF[4] : I2C TP SupportSandTPScanCode + // RINF[5] : ? + // RINF[6] : set on first airplane mode activation + // RINF[7] : ? + DBG, 8, // P80 + 3in1 debug dch + + Offset (0xdd), + , 1, // ddh.0 + , 1, // INF2[1] : ? ddh.1 + , 4, // - ddh.2-5 + BWKE, 1, // S3 wake on low battery ddh.6 + FF2D, 1, // Fn + F2 (LCD off) disable ddh.7 + EID2, 8, // EC CHIPID LSB deh + BWKT, 8, // threshold for S3 wake on low battery dfh + + Offset (0xe0), + RPM3, 16, // Fan 3 RPM e0h-e1h + + Offset (0xe2), + , 3, // - e2h.0-2 + SWFN, 1, // swap Fn and left Win key e2h.3 + LWIN, 1, // enable left Win key e2h.4 + , 2, // - e2h.5-6 + AIRK, 1, // enable airplane hotkey support e2h.7 + + Offset (0xe4), + , 1, // ? e4h.0 + , 2, // - e4h.1-2 + , 1, // ? e4h.3 + , 1, // - e4h.4 + EP12, 1, // ? (gpu related) e4h.5 + FN_G, 1, // Fn + G pressed (GPU reset on vendor fw) e4h.6 + , 1, // ? e4h.7 + + Offset (0xe5), + ECSZ, 8, // EC eFlash size e5h + + Offset (0xe6), + , 2, // - e6h.0-1 + G3FG, 1, // Enter G3 (all power off) in S4/S5 e6h.2 + , 3, // - e6h.3-5 + FOAC, 1, // Fan always on when AC connected e6h.6 + + Offset (0xe7), + FOFF, 8, // Fan base offset e7h + + Offset (0xe8), + , 1, // ? static 1; vendor: if 1: eccmd c6, val 0 e8h.0 + CNVI, 1, // CNVI card present e8h.1 + , 3, // - e8h.2-4 + FN_D, 1, // Fn + D pressed (CMOS reset on vendor fw) e8h.5 + , 1, // ? fan related e8h.6 + + Offset (0xe9), + KBBO, 1, // KB LED supports boot effect override e9h.0 + + Offset (0xea), + , 3, // - eah.0-2 + PDFG, 1, // Power supplied via USB-C eah.3 + MSFG, 1, // Modern standby flag eah.4 + RCHG, 1, // ? eah.5 + ACOT, 1, // ? eah.6 + S5FG, 1, // ? eah.7 + + Offset (0xeb), + , 1, // ? (unknown keypress status, NV4x only) ebh.0 + , 1, // ? (unknown keypress status, NV4x only) ebh.1 + , 1, // - ebh.2 + DGPT, 1, // ? ebh.3 + TOPN, 1, // ? ebh.4 + , 1, // ? (kbc beep related?) ebh.5 + , 1, // ? (kbc beep related?) ebh.6 + APRD, 1, // AP ready ebh.7 + + Offset (0xf0), + PL2B, 16, // Power Limit 2 set when on battery f0h-f1h + PL2T, 16, // Power Limit 2 (note: never enabled by EC!) f2h-f3h + TAUT, 8, // Tau (for PL1, not effective) f4h + + /* FCMD interface */ + Offset (0xf8), + FCMD, 8, // Command f8h + FDAT, 8, // Data f9h + FBUF, 8, // Buffer[0] fah + FBF1, 8, // Buffer[1] fbh + FBF2, 8, // Buffer[2] fch + FBF3, 8, // Buffer[3] fdh + + Offset (0xff), + , 8, // ? static, l14xcu/mu: 0xe0, nv4x 0x22 ffh + + Offset (0x28a), + FANQ, 8, // FAN count (FANC == FANQ) 28ah + KBTP, 8, // Keyboard backlight type 28bh + // 0x00 = none + // 0x01 = white + // 0x02 = RGB + // 0x*3 = per-key RGB + // 0x06 = RGB15Color + // 0x16 = RGB15ColorCustom +} diff --git a/src/ec/clevo/it5570e/acpi/hid.asl b/src/ec/clevo/it5570e/acpi/hid.asl new file mode 100644 index 0000000000..10d23f5330 --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/hid.asl @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* Minimal implementation of HID event filter device for airplane hotkey support */ + +Device (HIDD) +{ + Name (_HID, "INTC1051") + + Name (HRDY, 0) + Name (HBSY, 0) + Name (HIDX, 0) + + /* + * Workaround: + * There was a bug in Linux' HID driver, making evaluation of function 2 fail. + * The driver falls back to legacy mode and evaluates integers instead of _DSM. + * A bugfix for this was merged to mainline and stable but not LTS-branches. Thus, + * let's keep this for some time. + * https://lore.kernel.org/r/66f813f5bcc724a0f6dd5adefe6a9728dbe509e3.camel@mniewoehner.de + */ + Name (HDMM, 0) + + Method (_STA) + { + Return (0x0f) + } + + Method (_DSM, 4, Serialized) + { + If (Arg0 == ToUUID ("eeec56b3-4442-408f-a792-4edd4d758054")) + { + If (Arg1 == 1) /* Revision 1 */ + { + Printf ("HIDD: _DSM called, fn=%o", ToDecimalString(Arg2)) + + Switch (ToInteger (Arg2)) + { + Case (0) + { + /* Supported functions: 0, 2, 3, 4, 7 */ + Return (Buffer () {0x9d, 0x00}) + } + Case (2) + { + /* Simple mode */ + Return (0) + } + Case (3) + { + /* Driver status */ + HRDY = DeRefOf (Index (Arg3, 0)) + } + Case (4) + { + /* HID driver calls this to get event */ + HBSY = 0 + Return (HIDX) + } + Case (7) + { + /* Only airplane mode button implemented */ + Return (1 << 1) + } + } + } + } + + Return (Buffer () {0}) + } + + /* + * HID Platform Event Method + * Called to trigger HID event. + */ + Method (HPEM, 1, Serialized) + { + Printf ("HIDD: HPEM called, event=%o", ToHexString (Arg0)) + + If (!HRDY) + { + Printf ("HIDD: HID driver not ready. Ignoring event.") + } + + HBSY = 1 + HIDX = Arg0 + + Notify (HIDD, 0xc0) + + /* Wait max. 1 second for HID driver */ + Local0 = 0 + While ((Local0 < 250) && HBSY) + { + Sleep (4) + Local0++ + } + + If (HBSY) + { + Printf ("HIDD: HPEM timeout") + + HBSY = 0 + HIDX = 0 + + Return (1) /* Timeout */ + } + + Return (0) + } +} diff --git a/src/ec/clevo/it5570e/acpi/lid.asl b/src/ec/clevo/it5570e/acpi/lid.asl new file mode 100644 index 0000000000..3a1ab1750e --- /dev/null +++ b/src/ec/clevo/it5570e/acpi/lid.asl @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (LID) +{ + Name (_HID, "PNP0C0D") + Name (_PRW, Package () { EC_GPE_LID, 3 }) + + Method (_LID) + { + Printf ("LID: _LID: state=%o", ToDecimalString (\_SB.PCI0.LPCB.EC0.LSTE)) + + Return (\_SB.PCI0.LPCB.EC0.LSTE) + } + + Method (_PSW, 1) + { + Printf ("LID: _PSW: set lid wake enable=%o", ToDecimalString (Arg0)) + + \_SB.PCI0.LPCB.EC0.LWKE = Arg0 + } +} diff --git a/src/ec/clevo/it5570e/chip.h b/src/ec/clevo/it5570e/chip.h new file mode 100644 index 0000000000..a9ff3b1644 --- /dev/null +++ b/src/ec/clevo/it5570e/chip.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef EC_CLEVO_IT5570E_CHIP_H +#define EC_CLEVO_IT5570E_CHIP_H + +#define IT5570E_FAN_CURVE_LEN 4 /* Number of fan curve points */ +#define IT5570E_MAX_FAN_CNT 4 /* Maximum number of configurable fans */ + +enum ec_clevo_it5570e_fan_mode { + FAN_MODE_AUTO = 0, + FAN_MODE_CUSTOM, +}; + +struct ec_clevo_it5570e_fan_curve { + uint8_t temperature[IT5570E_FAN_CURVE_LEN]; + uint8_t speed[IT5570E_FAN_CURVE_LEN]; +}; + +struct ec_clevo_it5570e_config { + uint8_t pl2_on_battery; + enum ec_clevo_it5570e_fan_mode fan_mode; + struct ec_clevo_it5570e_fan_curve fan_curves[IT5570E_MAX_FAN_CNT]; +}; + +typedef struct ec_clevo_it5570e_config ec_config_t; + +#endif /* EC_CLEVO_IT5570E_CHIP_H */ diff --git a/src/ec/clevo/it5570e/commands.c b/src/ec/clevo/it5570e/commands.c new file mode 100644 index 0000000000..7a85b8f769 --- /dev/null +++ b/src/ec/clevo/it5570e/commands.c @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <delay.h> +#include <device/mmio.h> +#include <ec/acpi/ec.h> +#include <swab.h> +#include <timer.h> +#include <types.h> + +#include "chip.h" +#include "commands.h" +#include "ec.h" + +#define ec_cmd send_ec_command +#define ec_dat send_ec_data + +static void ec_fcmd(uint8_t fcmd) +{ + write8p(ECRAM + FCMD, fcmd); + ec_cmd(ECCMD_NOP); + + /* EC sets FCMD = 0x00 on completion (FCMD = 0xfa on some commands) */ + int time = wait_us(50000, read8p(ECRAM + FCMD) == 0x00 || read8p(ECRAM + FCMD) == 0xfa); + if (time) + printk(BIOS_DEBUG, "EC: FCMD 0x%02x completed after %d us\n", fcmd, time); + else + printk(BIOS_ERR, "EC: FCMD 0x%02x timed out\n", fcmd); +} + +static void ec_recv_str(char *buf, size_t size) +{ + while (size--) { + *buf = recv_ec_data(); + if (*buf == '$') { /* end mark */ + *buf = '\0'; + return; + } + buf++; + } + + /* Truncate and discard the rest */ + *--buf = '\0'; + do {} while (recv_ec_data() != '$'); + printk(BIOS_ERR, "EC: Received string longer than buffer. Data truncated.\n"); +} + +char *ec_read_model(void) +{ + static char model[10]; + + ec_cmd(ECCMD_READ_MODEL); + ec_recv_str(model, sizeof(model)); + + return model; +} + +char *ec_read_fw_version(void) +{ + static char version[10] = "1."; + + ec_cmd(ECCMD_READ_FW_VER); + ec_recv_str(version + 2, sizeof(version) - 2); + + return version; +} + +void ec_set_acpi_mode(bool state) +{ + ec_cmd(state ? ECCMD_ENABLE_ACPI_MODE : ECCMD_DISABLE_ACPI_MODE); + if (state) + ec_cmd(ECCMD_ENABLE_HOTKEYS); +} + +void ec_set_enter_g3_in_s4s5(bool state) +{ + clrsetbits8p(ECRAM + 0x1e6, 1 << G3FG, state << G3FG); +} + +void ec_set_aprd(void) +{ + setbits8p(ECRAM + 0x1eb, 1 << APRD); +} + +/* To be called by a graphics driver, when detecting a dGPU */ +void ec_set_dgpu_present(bool state) +{ + clrsetbits8p(ECRAM + 0x1eb, 1 << DGPT, state << DGPT); +} + +void ec_set_fn_win_swap(bool state) +{ + clrsetbits8p(ECRAM + ECKS, 1 << SWFN, state << SWFN); +} + +void ec_set_ac_fan_always_on(bool state) +{ + clrsetbits8p(ECRAM + 0x1e6, 1 << FOAC, state << FOAC); +} + +void ec_set_kbled_timeout(uint16_t timeout) +{ + printk(BIOS_DEBUG, "EC: set keyboard backlight timeout to %us\n", timeout); + + write8p(ECRAM + FDAT, timeout ? 0xff : 0x00); + write16p(ECRAM + FBUF, swab16(timeout)); + ec_fcmd(FCMD_SET_KBLED_TIMEOUT); +} + +void ec_set_flexicharger(bool state, uint8_t start, uint8_t stop) +{ + printk(BIOS_DEBUG, "EC: set flexicharger: enabled=%d, start=%u%%, stop=%u%%\n", + state, start, stop); + + if (!state) { + start = 0xff; + stop = 0xff; + + } else if (start > 100 || stop > 100) { + printk(BIOS_ERR, "EC: invalid flexicharger settings: start/stop > 100%%\n"); + return; + + } else if (start >= stop) { + printk(BIOS_ERR, "EC: invalid flexicharger settings: start >= stop\n"); + return; + } + + write8p(ECRAM + FBF1, state << 1); + write8p(ECRAM + FBUF, start); + write8p(ECRAM + FDAT, stop); + ec_fcmd(FCMD_FLEXICHARGER); +} + +void ec_set_camera_boot_state(enum camera_state state) +{ + if (state > CAMERA_STATE_KEEP) { + printk(BIOS_ERR, + "EC: invalid camera boot state %u. Keeping previous state.\n", state); + state = CAMERA_STATE_KEEP; + } + + if (state == CAMERA_STATE_KEEP) { + /* + * The EC maintains the camera's state in RAM. However, it doesn't sync the GPIO + * on a concurrent boot. Thus, read the previous state from the EC and set the + * state and the GPIO by sending the state command even in the keep-case. + */ + ec_cmd(ECCMD_GET_DEVICES_STATE); + state = recv_ec_data() & 1; + } + + printk(BIOS_DEBUG, "EC: set camera: enabled=%u\n", state); + + ec_dat(DEVICE_CAMERA | DEVICE_STATE(state)); + ec_cmd(ECCMD_SET_INV_DEVICE_STATE); +} + +void ec_set_tp_toggle_mode(uint8_t mode) +{ + switch (mode) { + case 0: /* CtrlAltF9 */ + setbits8p(ECRAM + RINF, TP_TOGGLE_CTRLALTF9); + break; + case 1: /* KeycodeF7F8*/ + clrbits8p(ECRAM + RINF, TP_TOGGLE_CTRLALTF9); + break; + } +} diff --git a/src/ec/clevo/it5570e/commands.h b/src/ec/clevo/it5570e/commands.h new file mode 100644 index 0000000000..7593117b1b --- /dev/null +++ b/src/ec/clevo/it5570e/commands.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef EC_CLEVO_IT5570E_COMMANDS_H +#define EC_CLEVO_IT5570E_COMMANDS_H + +#ifndef __ACPI__ +#define ECRAM CONFIG_EC_CLEVO_IT5570E_MEM_BASE + +/* EC RAM fields and bits */ +#define FANC 0x1c8 +#define RINF 0x1db +#define TP_TOGGLE_CTRLALTF9 (BIT(4) | BIT(2)) +#define ECKS 0x1e2 +#define SWFN 3 +// #### 0x1e6 +#define G3FG 2 +#define FOAC 6 +#define KBBO 0x1e9 +// #### 0x1eb +#define DGPT 3 +#define APRD 7 +#define PL2B 0x1f0 +#define PL2T 0x1f2 +#define TAUT 0x1f4 +#define FCMD 0x1f8 +#define FDAT 0x1f9 +#define FBUF 0x1fa +#define FBF1 0x1fb +#define FBF2 0x1fc +#define FBF3 0x1fd +#endif // __ACPI__ + +/* EC commands */ +#define ECCMD_NOP 0x00 /* dummy, triggers FCMDs */ +#define ECCMD_ENABLE_ACPI_MODE 0x90 +#define ECCMD_DISABLE_ACPI_MODE 0x91 +#define ECCMD_READ_MODEL 0x92 +#define ECCMD_READ_FW_VER 0x93 +#define ECCMD_ENABLE_HOTKEYS 0x98 +#define ECCMD_GET_DEVICES_STATE 0x9a +#define ECCMD_SET_INV_DEVICE_STATE 0x9c +#define DEVICE_CAMERA 2 +#define DEVICE_STATE(state) (!(state) << 7) +#define ECCMD_SET_BATLOW_ALARM 0x9d +#define ECCMD_SETUP_DEVICES 0xa8 + +/* FCMD commands */ +#define FCMD_DEVICES 0xb8 +#define FDAT_DEVICE_SET_INV_STATE 0xc2 /* inverted! en=0xc2|0, dis=0xc2|1 */ +#define FCMD_KLED 0xca +#define FDAT_KBLED_WHITE_SET_LEVEL 0x00 +#define FDAT_KBLED_WHITE_GET_LEVEL 0x01 +#define FCMD_FLEXICHARGER 0xcb +#define FCMD_SET_KBLED_TIMEOUT 0xd4 + +#ifndef __ACPI__ +enum camera_state { + CAMERA_STATE_DISABLE, + CAMERA_STATE_ENABLE, + CAMERA_STATE_KEEP, +}; + +char *ec_read_model(void); +char *ec_read_fw_version(void); +void ec_set_acpi_mode(bool state); +void ec_set_aprd(void); +void ec_set_enter_g3_in_s4s5(bool state); +void ec_set_dgpu_present(bool state); +void ec_set_fn_win_swap(bool state); +void ec_set_ac_fan_always_on(bool state); +void ec_set_kbled_timeout(uint16_t timeout); +void ec_set_flexicharger(bool state, uint8_t start, uint8_t stop); +void ec_set_camera_boot_state(enum camera_state state); +void ec_set_tp_toggle_mode(uint8_t mode); +#endif // __ACPI__ + +#endif /* EC_CLEVO_IT5570E_COMMANDS_H */ diff --git a/src/ec/clevo/it5570e/early_init.c b/src/ec/clevo/it5570e/early_init.c new file mode 100644 index 0000000000..864d6ecaa4 --- /dev/null +++ b/src/ec/clevo/it5570e/early_init.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <option.h> + +#include "early_init.h" +#include "i2ec.h" + +#define XRAM_BOOTEFFECT_DISABLE 0x47a +#define XRAM_BOOTEFFECT_SUPPORT 0x1e9 + +void ec_configure_kbled_booteffect(void) +{ + if (!ec_d2i2ec_read(XRAM_BOOTEFFECT_SUPPORT)) { + printk(BIOS_INFO, "EC: boot effect override not supported by ec firmware\n"); + return; + } + + bool enable = get_uint_option("kbled_booteffect", + CONFIG(EC_CLEVO_IT5570E_KBLED_BOOTEFFECT)); + + printk(BIOS_DEBUG, "EC: set booteffect enable=%i\n", enable); + ec_d2i2ec_write(XRAM_BOOTEFFECT_DISABLE, !enable); +} diff --git a/src/ec/clevo/it5570e/early_init.h b/src/ec/clevo/it5570e/early_init.h new file mode 100644 index 0000000000..7822d0d768 --- /dev/null +++ b/src/ec/clevo/it5570e/early_init.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef EC_CLEVO_IT5570E_EARLY_INIT_H +#define EC_CLEVO_IT5570E_EARLY_INIT_H + +void ec_configure_kbled_booteffect(void); + +#endif /* EC_CLEVO_IT5570E_EARLY_INIT_H */ diff --git a/src/ec/clevo/it5570e/ec.c b/src/ec/clevo/it5570e/ec.c new file mode 100644 index 0000000000..4f10750b51 --- /dev/null +++ b/src/ec/clevo/it5570e/ec.c @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <cpu/x86/msr.h> +#include <device/device.h> +#include <device/mmio.h> +#include <device/pnp.h> +#include <ec/acpi/ec.h> +#include <option.h> +#include <pc80/keyboard.h> +#include <soc/msr.h> +#include <superio/conf_mode.h> + +#include "chip.h" +#include "commands.h" +#include "ec.h" + +static void pnp_configure_smfi(void) +{ + if (!CONFIG_EC_CLEVO_IT5570E_MEM_BASE) { + printk(BIOS_ERR, "EC: no LGMR base address configured. Check your config!\n"); + return; + } + + /* Check for valid address (0xfeXXX000/0xffXXX000) */ + if ((CONFIG_EC_CLEVO_IT5570E_MEM_BASE & 0xfe000fff) != 0xfe000000) { + printk(BIOS_ERR, "EC: LGMR base address 0x%08x invalid. Check your config!\n", + CONFIG_EC_CLEVO_IT5570E_MEM_BASE); + return; + } + + struct device dev = { + .path.type = DEVICE_PATH_PNP, + .path.pnp.port = 0x2e, + .path.pnp.device = IT5570E_SMFI, + }; + dev.ops->ops_pnp_mode = &pnp_conf_mode_870155_aa; + + /* Configure SMFI for LGMR */ + pnp_enter_conf_mode(&dev); + pnp_set_logical_device(&dev); + pnp_set_enable(&dev, 1); + pnp_write_config(&dev, HLPCRAMBA_24, CONFIG_EC_CLEVO_IT5570E_MEM_BASE >> 24 & 0x01); + pnp_write_config(&dev, HLPCRAMBA_23_16, CONFIG_EC_CLEVO_IT5570E_MEM_BASE >> 16 & 0xff); + pnp_write_config(&dev, HLPCRAMBA_15_12, CONFIG_EC_CLEVO_IT5570E_MEM_BASE >> 8 & 0xf0); + pnp_exit_conf_mode(&dev); +} + +static void ec_init(struct device *dev) +{ + if (!dev->enabled) + return; + + const ec_config_t *config = config_of(dev); + printk(BIOS_DEBUG, "%s init.\n", dev->chip_ops->name); + + const char *const model = ec_read_model(); + const char *const version = ec_read_fw_version(); + printk(BIOS_DEBUG, "EC FW: model %s, version %s\n", model, version); + + pnp_configure_smfi(); + + ec_set_ac_fan_always_on( + get_uint_option("ac_fan_always_on", CONFIG(EC_CLEVO_IT5570E_AC_FAN_ALWAYS_ON))); + + ec_set_kbled_timeout( + get_uint_option("kbled_timeout", CONFIG_EC_CLEVO_IT5570E_KBLED_TIMEOUT)); + + ec_set_fn_win_swap( + get_uint_option("fn_win_swap", CONFIG(EC_CLEVO_IT5570E_FN_WIN_SWAP))); + + ec_set_flexicharger( + get_uint_option("flexicharger", CONFIG(EC_CLEVO_IT5570E_FLEXICHARGER)), + get_uint_option("flexicharger_start", CONFIG_EC_CLEVO_IT5570E_FLEXICHG_START), + get_uint_option("flexicharger_stop", CONFIG_EC_CLEVO_IT5570E_FLEXICHG_STOP)); + + ec_set_camera_boot_state( + get_uint_option("camera_boot_state", CONFIG_EC_CLEVO_IT5570E_CAM_BOOT_STATE)); + + ec_set_tp_toggle_mode( + get_uint_option("tp_toggle_mode", CONFIG_EC_CLEVO_IT5570E_TP_TOGGLE_MODE)); + + /* + * The vendor abuses the field PL2B (originally named PL1T) to set PL2 via PECI on + * battery-only. With AC attached, PL2B (PL1T) gets set as PL1 and PL2T as PL2, but + * both are never enabled (bit 15). Since PL1 is never enabled, Tau isn't either. + * Thus, set PL2T, TAUT to zero, so the EC doesn't write these non-effective values. + */ + const uint16_t power_unit = 1 << (msr_read(MSR_PKG_POWER_SKU_UNIT) & 0xf); + write16p(ECRAM + PL2B, config->pl2_on_battery * power_unit); + write16p(ECRAM + PL2T, 0); + write16p(ECRAM + TAUT, 0); + + ec_set_aprd(); + + pc_keyboard_init(NO_AUX_DEVICE); +} + +static const char *ec_acpi_name(const struct device *dev) +{ + return "EC0"; +} + +static void ec_fill_ssdt_generator(const struct device *dev) +{ + ec_fan_curve_fill_ssdt(dev); +} + +static struct device_operations ec_ops = { + .init = ec_init, + .read_resources = noop_read_resources, + .set_resources = noop_set_resources, + .acpi_fill_ssdt = ec_fill_ssdt_generator, + .acpi_name = ec_acpi_name, +}; + +static void enable_dev(struct device *dev) +{ + if (dev->path.type == DEVICE_PATH_GENERIC && dev->path.generic.id == 0) + dev->ops = &ec_ops; + else + printk(BIOS_ERR, "EC: Unknown device. Check your devicetree!\n"); +} + +struct chip_operations ec_clevo_it5570e_ops = { + CHIP_NAME("Clevo IT5570E EC") + .enable_dev = enable_dev, +}; diff --git a/src/ec/clevo/it5570e/ec.h b/src/ec/clevo/it5570e/ec.h new file mode 100644 index 0000000000..fee976a781 --- /dev/null +++ b/src/ec/clevo/it5570e/ec.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef EC_CLEVO_IT5570E_EC_H +#define EC_CLEVO_IT5570E_EC_H + +#include <device/device.h> + +/* PNP devices */ +#define IT5570E_UART1 0x01 /* UART1 */ +#define IT5570E_UART2 0x02 /* UART2 */ +#define IT5570E_SWUC 0x04 /* System Wake-Up Control */ +#define IT5570E_KBCM 0x05 /* PS/2 KBC Mouse */ +#define IT5570E_KBCK 0x06 /* PS/2 KBC Keyboard */ +#define IT5570E_CIR 0x0a /* Consumer IR */ +#define IT5570E_SMFI 0x0f /* Shared Memory/Flash Interface */ +#define IT5570E_RTCT 0x10 /* RTC-like Timer */ +#define IT5570E_PM1 0x11 /* Power Management Channel 1 */ +#define IT5570E_PM2 0x12 /* Power Management Channel 2 */ +#define IT5570E_SSPI 0x13 /* Serial Peripheral Interface */ +#define IT5570E_PECI 0x14 /* Platform Environment Control Interface */ +#define IT5570E_PM3 0x17 /* Power Management Channel 3 */ +#define IT5570E_PM4 0x18 /* Power Management Channel 4 */ +#define IT5570E_PM5 0x19 /* Power Management Channel 5 */ + +/* SMFI registers */ +#define HLPCRAMBA_15_12 0xf5 +#define HLPCRAMBA_23_16 0xf6 +#define HLPCRAMBA_24 0xfc + +void ec_fan_curve_fill_ssdt(const struct device *dev); + +#endif /* EC_CLEVO_IT5570E_EC_H */ diff --git a/src/ec/clevo/it5570e/i2ec.c b/src/ec/clevo/it5570e/i2ec.c new file mode 100644 index 0000000000..2d7dd6c37c --- /dev/null +++ b/src/ec/clevo/it5570e/i2ec.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <device/pnp_ops.h> + +#include "i2ec.h" + +#define SIO_DEV PNP_DEV(0x2e, 0) + +/* SIO depth 2 index/data pair */ +#define D2ADR 0x2e +#define D2DAT 0x2f + +/* SIO depth 2 address space */ +#define I2EC_ADDR_L 0x10 +#define I2EC_ADDR_H 0x11 +#define I2EC_DATA 0x12 + +/* + * Read/write SIO "depth 2" registers + */ + +static uint8_t sio_d2_read(uint8_t addr) +{ + pnp_write_config(SIO_DEV, D2ADR, addr); + return pnp_read_config(SIO_DEV, D2DAT); +} + +static void sio_d2_write(uint8_t addr, uint8_t val) +{ + pnp_write_config(SIO_DEV, D2ADR, addr); + pnp_write_config(SIO_DEV, D2DAT, val); +} + +/* + * Read/write I2EC registers through SIO "depth 2" address space + */ + +uint8_t ec_d2i2ec_read(uint16_t addr) +{ + sio_d2_write(I2EC_ADDR_H, addr >> 8 & 0xff); + sio_d2_write(I2EC_ADDR_L, addr & 0xff); + return sio_d2_read(I2EC_DATA); +} + +void ec_d2i2ec_write(uint16_t addr, uint8_t val) +{ + sio_d2_write(I2EC_ADDR_H, addr >> 8 & 0xff); + sio_d2_write(I2EC_ADDR_L, addr & 0xff); + sio_d2_write(I2EC_DATA, val); +} diff --git a/src/ec/clevo/it5570e/i2ec.h b/src/ec/clevo/it5570e/i2ec.h new file mode 100644 index 0000000000..8b8b0f62d7 --- /dev/null +++ b/src/ec/clevo/it5570e/i2ec.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef EC_CLEVO_IT5570E_I2EC_H +#define EC_CLEVO_IT5570E_I2EC_H + +/* + * Read/write I2EC registers through SIO "depth 2" address space + */ + +uint8_t ec_d2i2ec_read(uint16_t addr); +void ec_d2i2ec_write(uint16_t addr, uint8_t val); + +#endif /* EC_CLEVO_IT5570E_I2EC_H */ diff --git a/src/ec/clevo/it5570e/smbios.c b/src/ec/clevo/it5570e/smbios.c new file mode 100644 index 0000000000..20fa2ce897 --- /dev/null +++ b/src/ec/clevo/it5570e/smbios.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <string.h> +#include <smbios.h> +#include "commands.h" + +void smbios_ec_revision(uint8_t *ec_major_revision, uint8_t *ec_minor_revision) +{ + char *version, *major, *minor; + + version = ec_read_fw_version(); /* 1.XX.YY */ + major = &version[2]; + minor = &version[5]; + + *ec_major_revision = skip_atoi(&major); + *ec_minor_revision = skip_atoi(&minor); +} diff --git a/src/ec/clevo/it5570e/smihandler.c b/src/ec/clevo/it5570e/smihandler.c new file mode 100644 index 0000000000..fedac06e34 --- /dev/null +++ b/src/ec/clevo/it5570e/smihandler.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpi.h> +#include <cpu/x86/smm.h> + +#include "commands.h" +#include "smm.h" + +void ec_smi_apmc(uint8_t apmc) +{ + printk(BIOS_DEBUG, "EC SMI APMC handler\n"); + + switch (apmc) { + case APM_CNT_ACPI_ENABLE: + ec_set_acpi_mode(true); + break; + case APM_CNT_ACPI_DISABLE: + ec_set_acpi_mode(false); + break; + default: + break; + } +} + +void ec_smi_sleep(uint8_t slp_type) +{ + printk(BIOS_DEBUG, "EC SMI sleep handler\n"); + + switch (slp_type) { + case ACPI_S4: + case ACPI_S5: + ec_set_enter_g3_in_s4s5(true); + __fallthrough; + default: + break; + } +} diff --git a/src/ec/clevo/it5570e/smm.h b/src/ec/clevo/it5570e/smm.h new file mode 100644 index 0000000000..fc05c32f19 --- /dev/null +++ b/src/ec/clevo/it5570e/smm.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef EC_CLEVO_IT5570E_SMM_H +#define EC_CLEVO_IT5570E_SMM_H + +/* SMI handler */ +void ec_smi_apmc(uint8_t apmc); +void ec_smi_sleep(uint8_t slp_type); + +#endif /* EC_CLEVO_IT5570E_SMM_H */ diff --git a/src/ec/clevo/it5570e/ssdt.c b/src/ec/clevo/it5570e/ssdt.c new file mode 100644 index 0000000000..a0d746ac00 --- /dev/null +++ b/src/ec/clevo/it5570e/ssdt.c @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpigen.h> +#include <console/console.h> +#include <device/device.h> +#include <device/mmio.h> + +#include "chip.h" +#include "ec.h" +#include "commands.h" + +static bool is_curve_valid(struct ec_clevo_it5570e_fan_curve curve) +{ + /* + * Fan curve speeds have to be non-decreasing. + * Fan curve temperatures have to be increasing (to avoid division by 0). + * This also covers the case when the curve is all zeroes (i.e. not configured). + */ + + for (int i = 1; i < IT5570E_FAN_CURVE_LEN; i++) { + if (curve.speed[i] < curve.speed[i - 1] || + curve.temperature[i] <= curve.temperature[i - 1]) + return false; + } + + return true; +} + +static void write_fan_curve(struct ec_clevo_it5570e_fan_curve curve, int fan) +{ + uint16_t ramp; + char fieldname[5]; + + /* Curve points */ + for (int i = 0; i < IT5570E_FAN_CURVE_LEN; i++) { + snprintf(fieldname, 5, "F%dT%d", fan + 1, i + 1); + acpigen_write_store_int_to_namestr(curve.temperature[i], fieldname); + snprintf(fieldname, 5, "F%dD%d", fan + 1, i + 1); + acpigen_write_store_int_to_namestr(curve.speed[i] * 255 / 100, fieldname); + } + + /* Ramps */ + for (int i = 0; i < (IT5570E_FAN_CURVE_LEN - 1); i++) { + ramp = 255 * 16 * + (curve.speed[i + 1] - curve.speed[i]) / + (curve.temperature[i + 1] - curve.temperature[i]) / + 100; + + snprintf(fieldname, 5, "F%dR%d", fan + 1, i + 1); + acpigen_write_store_int_to_namestr(ramp, fieldname); + } +} + +static void write_fan_opregion(int fan_cnt) +{ + char fieldname[5]; + uint8_t flags = FIELD_ANYACC | FIELD_LOCK | FIELD_PRESERVE; + struct opregion opreg = { + .name = "FNCV", + .regionspace = SYSTEMMEMORY, + .regionoffset = CONFIG_EC_CLEVO_IT5570E_MEM_BASE + 0x38c, + .regionlen = fan_cnt * 14, + }; + + acpigen_write_opregion(&opreg); + acpigen_emit_ext_op(FIELD_OP); + acpigen_write_len_f(); + acpigen_emit_namestring(opreg.name); + acpigen_emit_byte(flags); + + for (int fan = 1; fan <= fan_cnt; fan++) { + /* temps */ + for (int i = 1; i <= IT5570E_FAN_CURVE_LEN; i++) { + snprintf(fieldname, 5, "F%dT%d", fan, i); + acpigen_write_field_name(fieldname, 8); + } + + /* duties */ + for (int i = 1; i <= IT5570E_FAN_CURVE_LEN; i++) { + snprintf(fieldname, 5, "F%dD%d", fan, i); + acpigen_write_field_name(fieldname, 8); + } + + /* ramps */ + for (int i = 1; i < IT5570E_FAN_CURVE_LEN; i++) { + snprintf(fieldname, 5, "F%dR%d", fan, i); + acpigen_write_field_name(fieldname, 16); + } + } + + acpigen_pop_len(); /* Field */ +} + +/* + * Set Fan curve + * The function must exist even if the fan curve isn't enabled in devicetree. + */ +void ec_fan_curve_fill_ssdt(const struct device *dev) +{ + const ec_config_t *config = config_of(dev); + const int fan_cnt = read8p(ECRAM + FANC); + + acpigen_write_scope(acpi_device_path(dev)); + write_fan_opregion(fan_cnt); + acpigen_write_method("SFCV", 0); + + if (config->fan_mode == FAN_MODE_CUSTOM) { + int curve_cnt = 0; + + /* Check curve count against fan count from EC */ + for (int i = 0; i < IT5570E_MAX_FAN_CNT; i++) + if (*config->fan_curves[i].speed && *config->fan_curves[i].temperature) + curve_cnt++; + + if (curve_cnt != fan_cnt) { + printk(BIOS_WARNING, + "EC: Fan curve count (%d) does not match fan count (%d). " + "Check your devicetree!\n", curve_cnt, fan_cnt); + goto pop; + } + + /* + * Check all curves. + * Custom mode can only be enabled for all fans or none. Thus, all + * custom curves must be valid before custom mode can be enabled. + */ + bool error = false; + for (int i = 0; i < fan_cnt; i++) { + if (!is_curve_valid(config->fan_curves[i])) { + printk(BIOS_ERR, + "EC: Fan %d curve invalid. Check your devicetree!\n", i); + error = true; + } + } + if (error) + goto pop; + + acpigen_write_debug_string("EC: Apply custom fan curve"); + + for (int i = 0; i < fan_cnt; i++) + write_fan_curve(config->fan_curves[i], i); + + /* Enable custom fan mode */ + acpigen_write_store_int_to_namestr(0x04, "FDAT"); + acpigen_emit_namestring("SFCC"); + acpigen_write_integer(0xd7); + } + +pop: + acpigen_pop_len(); /* Method */ + acpigen_pop_len(); /* Scope */ +} |