ec/purism/librem-ec: Add support for Purism Librem EC

Initial commit is a clone of ec/system76/ec with string changes;
Purism-specific functionality will be added in subsequent commits.

Change-Id: I8c51724e6dbfe1bc09496537f9e031643f95c755
Signed-off-by: Matt DeVillier <matt.devillier@puri.sm>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/52390
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
This commit is contained in:
Matt DeVillier 2021-04-14 13:15:43 -05:00 committed by Patrick Georgi
parent 5f20e85ff3
commit 7d57d561b1
12 changed files with 870 additions and 0 deletions

View File

@ -0,0 +1,19 @@
config EC_LIBREM_EC
bool
help
Purism Librem EC
config EC_LIBREM_EC_BAT_THRESHOLDS
depends on EC_LIBREM_EC
bool
default n
config EC_LIBREM_EC_COLOR_KEYBOARD
depends on EC_LIBREM_EC
bool
default n
config EC_LIBREM_EC_OLED
depends on EC_LIBREM_EC
bool
default n

View File

@ -0,0 +1,6 @@
ifeq ($(CONFIG_EC_LIBREM_EC),y)
all-y += librem_ec.c
smm-$(CONFIG_DEBUG_SMI) += librem_ec.c
endif

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
Device (AC)
{
Name (_HID, "ACPI0003" /* Power Source Device */) // _HID: Hardware ID
Name (_PCL, Package (0x01) // _PCL: Power Consumer List
{
_SB
})
Name (ACFG, One)
Method (_PSR, 0, NotSerialized) // _PSR: Power Source
{
Return (ACFG)
}
Method (_STA, 0, NotSerialized) // _STA: Status
{
Return (0x0F)
}
}

View File

@ -0,0 +1,170 @@
/* SPDX-License-Identifier: GPL-2.0-only */
Device (BAT0)
{
Name (_HID, EisaId ("PNP0C0A") /* Control Method Battery */) // _HID: Hardware ID
Name (_UID, Zero) // _UID: Unique ID
Name (_PCL, Package (0x01) // _PCL: Power Consumer List
{
_SB
})
Name (BFCC, Zero)
Method (_STA, 0, NotSerialized) // _STA: Status
{
If (^^PCI0.LPCB.EC0.ECOK)
{
If (^^PCI0.LPCB.EC0.BAT0)
{
Return (0x1F)
}
Else
{
Return (0x0F)
}
}
Else
{
Return (0x0F)
}
}
Name (PBIF, Package (0x0D)
{
One, // 0 - Power Unit
0xFFFFFFFF, // 1 - Design Capacity
0xFFFFFFFF, // 2 - Last Full Charge Capacity
One, // 3 - Battery Technology
0xFFFFFFFF, // 4 - Design Voltage
Zero, // 5 - Design Capacity of Warning
Zero, // 6 - Design Capacity of Low
0x40, // 7 - Battery Capacity Granularity 1
0x40, // 8 - Battery Capacity Granularity 2
" ", // 9 - Model Number
" ", // 10 - Serial Number
" ", // 11 - Battery Type
" " // 12 - OEM Information
})
Method (IVBI, 0, NotSerialized)
{
PBIF [1] = 0xFFFFFFFF
PBIF [2] = 0xFFFFFFFF
PBIF [4] = 0xFFFFFFFF
PBIF [9] = " "
PBIF [10] = " "
PBIF [11] = " "
PBIF [12] = " "
BFCC = Zero
}
Method (UPBI, 0, NotSerialized)
{
If (^^PCI0.LPCB.EC0.BAT0)
{
Local0 = (^^PCI0.LPCB.EC0.BDC0 & 0xFFFF)
PBIF [1] = Local0
Local0 = (^^PCI0.LPCB.EC0.BFC0 & 0xFFFF)
PBIF [2] = Local0
BFCC = Local0
Local0 = (^^PCI0.LPCB.EC0.BDV0 & 0xFFFF)
PBIF [4] = Local0
Local0 = (^^PCI0.LPCB.EC0.BCW0 & 0xFFFF)
PBIF [5] = Local0
Local0 = (^^PCI0.LPCB.EC0.BCL0 & 0xFFFF)
PBIF [6] = Local0
PBIF [9] = "BAT"
PBIF [10] = "0001"
PBIF [11] = "LION"
PBIF [12] = "Notebook"
}
Else
{
IVBI ()
}
}
Method (_BIF, 0, NotSerialized) // _BIF: Battery Information
{
If (^^PCI0.LPCB.EC0.ECOK)
{
UPBI ()
}
Else
{
IVBI ()
}
Return (PBIF) /* \_SB_.BAT0.PBIF */
}
Name (PBST, Package (0x04)
{
Zero, // 0 - Battery state
0xFFFFFFFF, // 1 - Battery present rate
0xFFFFFFFF, // 2 - Battery remaining capacity
0xFFFFFFFF // 3 - Battery present voltage
})
Method (IVBS, 0, NotSerialized)
{
PBST [0] = Zero
PBST [1] = 0xFFFFFFFF
PBST [2] = 0xFFFFFFFF
PBST [3] = 0xFFFFFFFF
}
Method (UPBS, 0, NotSerialized)
{
If (^^PCI0.LPCB.EC0.BAT0)
{
Local0 = Zero
Local1 = Zero
If (^^AC.ACFG)
{
If (((^^PCI0.LPCB.EC0.BST0 & 0x02) == 0x02))
{
Local0 |= 0x02
Local1 = (^^PCI0.LPCB.EC0.BPR0 & 0xFFFF)
}
}
Else
{
Local0 |= One
Local1 = (^^PCI0.LPCB.EC0.BPR0 & 0xFFFF)
}
Local7 = (Local1 & 0x8000)
If ((Local7 == 0x8000))
{
Local1 ^= 0xFFFF
}
Local2 = (^^PCI0.LPCB.EC0.BRC0 & 0xFFFF)
Local3 = (^^PCI0.LPCB.EC0.BPV0 & 0xFFFF)
PBST [0] = Local0
PBST [1] = Local1
PBST [2] = Local2
PBST [3] = Local3
If ((BFCC != ^^PCI0.LPCB.EC0.BFC0))
{
Notify (BAT0, 0x81) // Information Change
}
}
Else
{
IVBS ()
}
}
Method (_BST, 0, NotSerialized) // _BST: Battery Status
{
If (^^PCI0.LPCB.EC0.ECOK)
{
UPBS ()
}
Else
{
IVBS ()
}
Return (PBST) /* \_SB_.BAT0.PBST */
}
}

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-only */
Field (ERAM, ByteAcc, Lock, Preserve)
{
Offset (0xBC),
BTL0, 8, /* BAT0 charging start threshold */
BTH0, 8, /* BAT0 charging end threshold */
}
/*
* Get battery charging threshold
*
* Arg0: 0: Start threshold
* 1: Stop threshold
*/
Method (GBCT, 1, NotSerialized)
{
If (Arg0 == 0) {
Return (BTL0)
}
If (Arg0 == 1) {
Return (BTH0)
}
Return (0xFF)
}
/*
* Set battery charging threshold
*
* Arg0: 0: Start threshold
* 1: Stop threshold
* Arg1: Percentage
*/
Method (SBCT, 2, NotSerialized)
{
If (Arg1 <= 100) {
If (Arg0 == 0) {
BTL0 = Arg1
}
If (Arg0 == 1) {
BTH0 = Arg1
}
}
}

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-only */
Device (PWRB)
{
Name (_HID, EisaId ("PNP0C0C"))
Name (_PRW, Package () { EC_GPE_SWI, 3 })
}
Device (SLPB)
{
Name (_HID, EisaId ("PNP0C0E"))
Name (_PRW, Package () { EC_GPE_SWI, 3 })
}

View File

@ -0,0 +1,244 @@
/* SPDX-License-Identifier: GPL-2.0-only */
Scope (\_SB) {
#include "ac.asl"
#include "battery.asl"
#include "buttons.asl"
#include "hid.asl"
#include "lid.asl"
#include "librem-ec.asl"
}
Device (\_SB.PCI0.LPCB.EC0)
{
Name (_HID, EisaId ("PNP0C09") /* Embedded Controller Device */) // _HID: Hardware ID
Name (_GPE, EC_GPE_SCI) // _GPE: General Purpose Events
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
IO (Decode16,
0x0062, // Range Minimum
0x0062, // Range Maximum
0x00, // Alignment
0x01, // Length
)
IO (Decode16,
0x0066, // Range Minimum
0x0066, // Range Maximum
0x00, // Alignment
0x01, // Length
)
})
#include "ec_ram.asl"
Name (ECOK, Zero)
Method (_REG, 2, Serialized) // _REG: Region Availability
{
Debug = Concatenate("EC: _REG", Concatenate(ToHexString(Arg0), Concatenate(" ", ToHexString(Arg1))))
If ((Arg0 == 0x03) && (Arg1 == One)) {
// Enable hardware touchpad lock, airplane mode, and keyboard backlight keys
ECOS = 1
// Enable software display brightness keys
WINF = 1
// Set current AC state
^^^^AC.ACFG = ADP
// Update battery information and status
^^^^BAT0.UPBI()
^^^^BAT0.UPBS()
// Notify of changes
Notify(^^^^AC, Zero)
Notify(^^^^BAT0, Zero)
PNOT ()
// EC is now available
ECOK = Arg1
// Reset System76 Device
^^^^S76D.RSET()
}
}
Name (S3OS, Zero)
Method (PTS, 1, Serialized) {
Debug = Concatenate("EC: PTS: ", ToHexString(Arg0))
If (ECOK) {
// Save ECOS during sleep
S3OS = ECOS
// Clear wake cause
WFNO = Zero
}
}
Method (WAK, 1, Serialized) {
Debug = Concatenate("EC: WAK: ", ToHexString(Arg0))
If (ECOK) {
// Restore ECOS after sleep
ECOS = S3OS
// Set current AC state
^^^^AC.ACFG = ADP
// Update battery information and status
^^^^BAT0.UPBI()
^^^^BAT0.UPBS()
// Notify of changes
Notify(^^^^AC, Zero)
Notify(^^^^BAT0, Zero)
// Reset System76 Device
^^^^S76D.RSET()
}
}
Method (_Q0A, 0, NotSerialized) // Touchpad Toggle
{
Debug = "EC: Touchpad Toggle"
}
Method (_Q0B, 0, NotSerialized) // Screen Toggle
{
Debug = "EC: Screen Toggle"
#if CONFIG(EC_SYSTEM76_EC_OLED)
Notify (^^^^S76D, 0x85)
#endif // CONFIG(EC_SYSTEM76_EC_OLED)
}
Method (_Q0C, 0, NotSerialized) // Mute
{
Debug = "EC: Mute"
}
Method (_Q0D, 0, NotSerialized) // Keyboard Backlight
{
Debug = "EC: Keyboard Backlight"
}
Method (_Q0E, 0, NotSerialized) // Volume Down
{
Debug = "EC: Volume Down"
}
Method (_Q0F, 0, NotSerialized) // Volume Up
{
Debug = "EC: Volume Up"
}
Method (_Q10, 0, NotSerialized) // Switch Video Mode
{
Debug = "EC: Switch Video Mode"
}
Method (_Q11, 0, NotSerialized) // Brightness Down
{
Debug = "EC: Brightness Down"
if (^^^^HIDD.HRDY) {
^^^^HIDD.HPEM (20)
}
}
Method (_Q12, 0, NotSerialized) // Brightness Up
{
Debug = "EC: Brightness Up"
if (^^^^HIDD.HRDY) {
^^^^HIDD.HPEM (19)
}
}
Method (_Q13, 0, NotSerialized) // Camera Toggle
{
Debug = "EC: Camera Toggle"
}
Method (_Q14, 0, NotSerialized) // Airplane Mode
{
Debug = "EC: Airplane Mode"
if (^^^^HIDD.HRDY) {
^^^^HIDD.HPEM (8)
}
// TODO: hardware airplane mode
}
Method (_Q15, 0, NotSerialized) // Suspend Button
{
Debug = "EC: Suspend Button"
Notify (SLPB, 0x80)
}
Method (_Q16, 0, NotSerialized) // AC Detect
{
Debug = "EC: AC Detect"
^^^^AC.ACFG = ADP
Notify (AC, 0x80) // Status Change
If (BAT0)
{
Notify (^^^^BAT0, 0x81) // Information Change
Notify (^^^^BAT0, 0x80) // Status Change
}
}
Method (_Q17, 0, NotSerialized) // BAT0 Update
{
Debug = "EC: BAT0 Update (17)"
Notify (^^^^BAT0, 0x81) // Information Change
}
Method (_Q19, 0, NotSerialized) // BAT0 Update
{
Debug = "EC: BAT0 Update (19)"
Notify (^^^^BAT0, 0x81) // Information Change
}
Method (_Q1B, 0, NotSerialized) // Lid Close
{
Debug = "EC: Lid Close"
Notify (LID0, 0x80)
}
Method (_Q1C, 0, NotSerialized) // Thermal Trip
{
Debug = "EC: Thermal Trip"
/* TODO
Notify (\_TZ.TZ0, 0x81) // Thermal Trip Point Change
Notify (\_TZ.TZ0, 0x80) // Thermal Status Change
*/
}
Method (_Q1D, 0, NotSerialized) // Power Button
{
Debug = "EC: Power Button"
Notify (PWRB, 0x80)
}
Method (_Q50, 0, NotSerialized) // Other Events
{
Local0 = OEM4
If (Local0 == 0x8A) {
Debug = "EC: White Keyboard Backlight"
Notify (^^^^S76D, 0x80)
} ElseIf (Local0 == 0x9F) {
Debug = "EC: Color Keyboard Toggle"
Notify (^^^^S76D, 0x81)
} ElseIf (Local0 == 0x81) {
Debug = "EC: Color Keyboard Down"
Notify (^^^^S76D, 0x82)
} ElseIf (Local0 == 0x82) {
Debug = "EC: Color Keyboard Up"
Notify (^^^^S76D, 0x83)
} ElseIf (Local0 == 0x80) {
Debug = "EC: Color Keyboard Color Change"
Notify (^^^^S76D, 0x84)
} Else {
Debug = Concatenate("EC: Other: ", ToHexString(Local0))
}
}
#if CONFIG(EC_SYSTEM76_EC_BAT_THRESHOLDS)
#include "battery_thresholds.asl"
#endif
}

View File

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0-only */
OperationRegion (ERAM, EmbeddedControl, Zero, 0xFF)
Field (ERAM, ByteAcc, Lock, Preserve)
{
Offset (0x03),
LSTE, 1, // Lid is open
, 1,
LWKE, 1, // Lid wake
, 5,
Offset (0x07),
TMP1, 8, // CPU temperature
Offset (0x10),
ADP, 1, // AC adapter connected
, 1,
BAT0, 1, // Battery connected
, 5,
WFNO, 8, // Wake cause (not implemented)
Offset (0x16),
BDC0, 32, // Battery design capacity
BFC0, 32, // Battery full capacity
Offset (0x22),
BDV0, 32, // Battery design voltage
BST0, 32, // Battery status
BPR0, 32, // Battery current
BRC0, 32, // Battery remaining capacity
BPV0, 32, // Battery voltage
Offset (0x3A),
BCW0, 32,
BCL0, 32,
Offset (0x68),
ECOS, 8, // Detected OS, 0 = no ACPI, 1 = ACPI but no driver, 2 = ACPI with driver
Offset (0xC8),
OEM1, 8,
OEM2, 8,
OEM3, 16,
OEM4, 8, // Extra SCI data
Offset (0xCD),
TMP2, 8, // GPU temperature
DUT1, 8, // Fan 1 duty
DUT2, 8, // Fan 2 duty
RPM1, 16, // Fan 1 RPM
RPM2, 16, // Fan 2 RPM
Offset (0xD9),
AIRP, 8, // Airplane mode LED
WINF, 8, // Enable ACPI brightness controls
Offset (0xF8),
FCMD, 8,
FDAT, 8,
FBUF, 8,
FBF1, 8,
FBF2, 8,
FBF3, 8,
}

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-only */
Device (HIDD)
{
Name (_HID, "INT33D5")
Name (HBSY, Zero)
Name (HIDX, Zero)
Name (HRDY, Zero)
Method (HDEM, 0, Serialized)
{
HBSY = Zero
Return (HIDX)
}
Method (HDMM, 0, Serialized)
{
Return (Zero)
}
Method (HDSM, 1, Serialized)
{
HRDY = Arg0
}
Method (HPEM, 1, Serialized)
{
HBSY = One
HIDX = Arg0
Notify (HIDD, 0xC0)
Local0 = Zero
While ((Local0 < 0xFA) && HBSY)
{
Sleep (0x04)
Local0++
}
If (HBSY == One)
{
HBSY = Zero
HIDX = Zero
Return (One)
}
Else
{
Return (Zero)
}
}
}

View File

@ -0,0 +1,162 @@
/* SPDX-License-Identifier: GPL-2.0-only */
// Notifications:
// 0x80 - hardware backlight toggle
// 0x81 - backlight toggle
// 0x82 - backlight down
// 0x83 - backlight up
// 0x84 - backlight color change
// 0x85 - OLED screen toggle
Device (S76D) {
Name (_HID, "17761776")
Name (_UID, 0)
Method (RSET, 0, Serialized) {
Debug = "S76D: RSET"
SAPL(0)
SKBL(0)
#if CONFIG(EC_SYSTEM76_EC_COLOR_KEYBOARD)
SKBC(0xFFFFFF)
#endif // CONFIG(EC_SYSTEM76_EC_COLOR_KEYBOARD)
}
Method (INIT, 0, Serialized) {
Debug = "S76D: INIT"
RSET()
If (^^PCI0.LPCB.EC0.ECOK) {
// Set flags to use software control
^^PCI0.LPCB.EC0.ECOS = 2
Return (0)
} Else {
Return (1)
}
}
Method (FINI, 0, Serialized) {
Debug = "S76D: FINI"
RSET()
If (^^PCI0.LPCB.EC0.ECOK) {
// Set flags to use hardware control
^^PCI0.LPCB.EC0.ECOS = 1
Return (0)
} Else {
Return (1)
}
}
// Get Airplane LED
Method (GAPL, 0, Serialized) {
If (^^PCI0.LPCB.EC0.ECOK) {
If (^^PCI0.LPCB.EC0.AIRP & 0x40) {
Return (1)
}
}
Return (0)
}
// Set Airplane LED
Method (SAPL, 1, Serialized) {
If (^^PCI0.LPCB.EC0.ECOK) {
If (Arg0) {
^^PCI0.LPCB.EC0.AIRP |= 0x40
} Else {
^^PCI0.LPCB.EC0.AIRP &= 0xBF
}
}
}
#if CONFIG(EC_SYSTEM76_EC_COLOR_KEYBOARD)
// Set KB LED Brightness
Method (SKBL, 1, Serialized) {
If (^^PCI0.LPCB.EC0.ECOK) {
^^PCI0.LPCB.EC0.FDAT = 6
^^PCI0.LPCB.EC0.FBUF = Arg0
^^PCI0.LPCB.EC0.FBF1 = 0
^^PCI0.LPCB.EC0.FBF2 = Arg0
^^PCI0.LPCB.EC0.FCMD = 0xCA
}
}
// Set Keyboard Color
Method (SKBC, 1, Serialized) {
If (^^PCI0.LPCB.EC0.ECOK) {
^^PCI0.LPCB.EC0.FDAT = 0x3
^^PCI0.LPCB.EC0.FBUF = (Arg0 & 0xFF)
^^PCI0.LPCB.EC0.FBF1 = ((Arg0 >> 16) & 0xFF)
^^PCI0.LPCB.EC0.FBF2 = ((Arg0 >> 8) & 0xFF)
^^PCI0.LPCB.EC0.FCMD = 0xCA
Return (Arg0)
} Else {
Return (0)
}
}
#else // CONFIG(EC_SYSTEM76_EC_COLOR_KEYBOARD)
// Get KB LED
Method (GKBL, 0, Serialized) {
Local0 = 0
If (^^PCI0.LPCB.EC0.ECOK) {
^^PCI0.LPCB.EC0.FDAT = One
^^PCI0.LPCB.EC0.FCMD = 0xCA
Local0 = ^^PCI0.LPCB.EC0.FBUF
^^PCI0.LPCB.EC0.FCMD = Zero
}
Return (Local0)
}
// Set KB Led
Method (SKBL, 1, Serialized) {
If (^^PCI0.LPCB.EC0.ECOK) {
^^PCI0.LPCB.EC0.FDAT = Zero
^^PCI0.LPCB.EC0.FBUF = Arg0
^^PCI0.LPCB.EC0.FCMD = 0xCA
}
}
#endif // CONFIG(EC_SYSTEM76_EC_COLOR_KEYBOARD)
// Fan names
Method (NFAN, 0, Serialized) {
Return (Package() {
"CPU fan",
})
}
// Get fan duty cycle and RPM as a single value
Method (GFAN, 1, Serialized) {
Local0 = 0
Local1 = 0
If (^^PCI0.LPCB.EC0.ECOK) {
If (Arg0 == 0) {
Local0 = ^^PCI0.LPCB.EC0.DUT1
Local1 = ^^PCI0.LPCB.EC0.RPM1
} ElseIf (Arg0 == 1) {
Local0 = ^^PCI0.LPCB.EC0.DUT2
Local1 = ^^PCI0.LPCB.EC0.RPM2
}
}
If (Local1 != 0) {
// 60 * (EC frequency / 120) / 2
Local1 = 2156250 / Local1
}
Return ((Local1 << 8) | Local0)
}
// Temperature names
Method (NTMP, 0, Serialized) {
Return (Package() {
"CPU temp",
})
}
// Get temperature
Method (GTMP, 1, Serialized) {
Local0 = 0;
If (^^PCI0.LPCB.EC0.ECOK) {
If (Arg0 == 0) {
Local0 = ^^PCI0.LPCB.EC0.TMP1
} ElseIf (Arg0 == 1) {
Local0 = ^^PCI0.LPCB.EC0.TMP2
}
}
Return (Local0)
}
}

View File

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0-only */
Device (LID0)
{
Name (_HID, EisaId ("PNP0C0D"))
Name (_PRW, Package () { EC_GPE_SWI, 3 })
Method (_LID, 0, NotSerialized) {
DEBUG = "LID: _LID"
If (^^PCI0.LPCB.EC0.ECOK) {
Return (^^PCI0.LPCB.EC0.LSTE)
} Else {
Return (One)
}
}
Method (_PSW, 1, NotSerialized) {
DEBUG = Concatenate("LID: _PSW: ", ToHexString(Arg0))
If (^^PCI0.LPCB.EC0.ECOK) {
^^PCI0.LPCB.EC0.LWKE = Arg0
}
}
}

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <arch/io.h>
#include <console/system76_ec.h>
#include <timer.h>
// This is the command region for Librem EC firmware. It must be
// enabled for LPC in the mainboard.
#define LIBREM_EC_BASE 0x0E00
#define LIBREM_EC_SIZE 256
#define REG_CMD 0
#define REG_RESULT 1
// When command register is 0, command is complete
#define CMD_FINISHED 0
// Print command. Registers are unique for each command
#define CMD_PRINT 4
#define CMD_PRINT_REG_FLAGS 2
#define CMD_PRINT_REG_LEN 3
#define CMD_PRINT_REG_DATA 4
static inline uint8_t system76_ec_read(uint8_t addr)
{
return inb(LIBREM_EC_BASE + (uint16_t)addr);
}
static inline void system76_ec_write(uint8_t addr, uint8_t data)
{
outb(data, LIBREM_EC_BASE + (uint16_t)addr);
}
void system76_ec_init(void)
{
// Clear entire command region
for (int i = 0; i < LIBREM_EC_SIZE; i++)
system76_ec_write((uint8_t)i, 0);
}
void system76_ec_flush(void)
{
system76_ec_write(REG_CMD, CMD_PRINT);
// Wait for command completion, for up to 10 milliseconds, with a
// test period of 1 microsecond
wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED);
system76_ec_write(CMD_PRINT_REG_LEN, 0);
}
void system76_ec_print(uint8_t byte)
{
uint8_t len = system76_ec_read(CMD_PRINT_REG_LEN);
system76_ec_write(CMD_PRINT_REG_DATA + len, byte);
system76_ec_write(CMD_PRINT_REG_LEN, len + 1);
// If we hit the end of the buffer, or were given a newline, flush
if (byte == '\n' || len >= (LIBREM_EC_SIZE - CMD_PRINT_REG_DATA))
system76_ec_flush();
}