ec/clevo/it5570e: add driver for EC used on various Clevo laptops

This adds a driver for the ITE IT5570E EC in combination with Clevo
vendor EC firmware. The interface is mostly identical on various laptop
models. Thus, we have implemented one common driver to support them all.

The following features were implemented:
 - Basics like battery, ac, etc.
 - Suspend/hibernate support: S0ix, S3*, S4/S5
 - Save/restore of keyboard backlight level during S0ix without the need
   for Clevo vendor software (ControlCenter)
 - Flexicharger
 - Fn keys (backlight, volume, airplane etc.)
 - Various configuration options via Kconfig / CMOS options

* Note: S3 support works at least on L140CU (Cometlake), but it's not
        enabled for this board because S0ix is used.

Not implemented, yet:
 - Type-C UCSI: the EC firmware seems to be buggy (with vendor fw, too)
 - dGPU support is WIP

An example of how this driver can be hooked up by a board can be seen in
in change CB:59850, where support for the L140MU is added.

Known issues:

 - Touchpad toggle:
   The touchpad toggle (Fn-F1) has two modes, Ctrl-Alt-F9 mode and
   keycodes 0xf7/0xf8 mode. Ctrl-Alt-F9 is the native touchpad toggle
   shortcut on Windows. On Linux this would switch to virtual console 9,
   if enabled.  Thus, one should use the keycodes mode and add udev
   rules as specified in [1]. If VT9 is disabled, Ctrl-Alt-F9 mode could
   be used to set up a keyboard shortcut command toggling the touchpad.

 - Multi-fan systems
   The Clevo NV41MZ (w/o dGPU) has two fans that should be in-sync.
   However, the second fan does not spin. This needs further
   investigation.

[1] https://docs.dasharo.com/variants/clevo_nv41/post_install/

Testing the various functionalities of this EC driver was done in the
changes hooking up this driver for the boards.

Change-Id: Ic8c0bee9002ad9edcd10c83b775fc723744caaa0
Co-authored-by: Michał Kopeć <michal.kopec@3mdeb.com>
Co-authored-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Co-authored-by: Michael Niewöhner <foss@mniewoehner.de>
Signed-off-by: Michał Kopeć <michal.kopec@3mdeb.com>
Signed-off-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Signed-off-by: Michael Niewöhner <foss@mniewoehner.de>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/68791
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
This commit is contained in:
Michael Niewöhner 2021-12-01 19:09:13 +01:00
parent 064c6ced40
commit e1e65cb0f1
24 changed files with 2012 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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")
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

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

128
src/ec/clevo/it5570e/ec.c Normal file
View File

@ -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,
};

32
src/ec/clevo/it5570e/ec.h Normal file
View File

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

View File

@ -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);
}

View File

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

View File

@ -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);
}

View File

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

View File

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

152
src/ec/clevo/it5570e/ssdt.c Normal file
View File

@ -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 */
}