ec/dasharo/ec: Add initial copy of ec/system76/ec

Initial commit is a copy of ec/system76/ec from tag v24.02.1 (commit
0a280ff7) with string changes. Dasharo-specific features will be added
in subsequent commits, similar to how Librem EC support was added in
changes 52390 and 52391.

Change-Id: Ic7c3d9413488026548514963eb78accc28e41e06
Signed-off-by: Michał Kopeć <michal.kopec@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/82671
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Singer <service+coreboot-gerrit@felixsinger.de>
diff --git a/src/ec/dasharo/ec/Kconfig b/src/ec/dasharo/ec/Kconfig
new file mode 100644
index 0000000..1a007fa
--- /dev/null
+++ b/src/ec/dasharo/ec/Kconfig
@@ -0,0 +1,21 @@
+## SPDX-License-Identifier: GPL-2.0-only
+
+config EC_DASHARO_EC
+	bool
+	help
+	  Dasharo EC
+
+config EC_DASHARO_EC_BAT_THRESHOLDS
+	depends on EC_DASHARO_EC
+	bool
+	default y
+
+config EC_DASHARO_EC_DGPU
+	depends on EC_DASHARO_EC
+	bool
+	default n
+
+config EC_DASHARO_EC_OLED
+	depends on EC_DASHARO_EC
+	bool
+	default n
diff --git a/src/ec/dasharo/ec/Makefile.mk b/src/ec/dasharo/ec/Makefile.mk
new file mode 100644
index 0000000..247bcd4
--- /dev/null
+++ b/src/ec/dasharo/ec/Makefile.mk
@@ -0,0 +1,10 @@
+## SPDX-License-Identifier: GPL-2.0-only
+ifeq ($(CONFIG_EC_DASHARO_EC),y)
+
+all-y += dasharo_ec.c
+
+ramstage-y += smbios.c
+
+smm-$(CONFIG_DEBUG_SMI) += dasharo_ec.c
+
+endif
diff --git a/src/ec/dasharo/ec/acpi/ac.asl b/src/ec/dasharo/ec/acpi/ac.asl
new file mode 100644
index 0000000..66cf742
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/ac.asl
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+Device (AC)
+{
+	Name (_HID, "ACPI0003" /* Power Source Device */)
+	Name (_PCL, Package (0x01)  // _PCL: Power Consumer List
+	{
+		_SB
+	})
+
+	Name (ACFG, 1)
+
+	Method (_PSR, 0, NotSerialized)  // _PSR: Power Source
+	{
+		Return (ACFG)
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		Return (0x0F)
+	}
+}
diff --git a/src/ec/dasharo/ec/acpi/battery.asl b/src/ec/dasharo/ec/acpi/battery.asl
new file mode 100644
index 0000000..106f3d1
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/battery.asl
@@ -0,0 +1,248 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+Device (BAT0)
+{
+	Name (_HID, EisaId ("PNP0C0A") /* Control Method Battery */)
+	Name (_UID, 0)
+	Name (_PCL, Package (0x01)  // _PCL: Power Consumer List
+	{
+		_SB
+	})
+	Name (BFCC, 0)
+	Method (_STA, 0, NotSerialized)
+	{
+		If (^^PCI0.LPCB.EC0.ECOK)
+		{
+			If (^^PCI0.LPCB.EC0.BAT0)
+			{
+				Return (0x1F)
+			}
+			Else
+			{
+				Return (0x0F)
+			}
+		}
+		Else
+		{
+			Return (0x0F)
+		}
+	}
+
+	Name (PBIF, Package (0x0D)
+	{
+		1, // 0 - Power Unit
+		0xFFFFFFFF, // 1 - Design Capacity
+		0xFFFFFFFF, // 2 - Last Full Charge Capacity
+		1, // 3 - Battery Technology
+		0xFFFFFFFF, // 4 - Design Voltage
+		0, // 5 - Design Capacity of Warning
+		0, // 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 = 0
+	}
+
+	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 (PBIX, Package ()
+	{
+		0, // 0 - Revision
+		1, // 1 - Power Unit: mAh
+		0xFFFFFFFF, // 2 - Design Capacity
+		0xFFFFFFFF, // 3 - Last Full Charge Capacity
+		1, // 4 - Battery Technology: Rechargeable
+		0xFFFFFFFF, // 5 - Design Voltage
+		0, // 6 - Design Capacity of Warning
+		0, // 7 - Design Capacity of Low
+		0, // 8 - Cycle Count
+		98000, // 9 - Measurement Accuracy
+		0xFFFFFFFF, // 10 - Max Sampling Time
+		0xFFFFFFFF, // 11 - Min Sampling Time
+		0xFFFFFFFF, // 12 - Max Averaging Interval
+		0xFFFFFFFF, // 13 - Min Averaging Interval
+		0x40, // 14 - Battery Capacity Granularity 1
+		0x40, // 15 - Battery Capacity Granularity 2
+		" ", // 16 - Model Number
+		" ", // 17 - Serial Number
+		" ", // 18 - Battery Type
+		" " // 19 - OEM Information
+	})
+
+	Method (IVBX, 0, NotSerialized)
+	{
+		PBIX [2] = 0xFFFFFFFF
+		PBIX [3] = 0xFFFFFFFF
+		PBIX [5] = 0xFFFFFFFF
+		PBIX [16] = " "
+		PBIX [17] = " "
+		PBIX [18] = " "
+		PBIX [19] = " "
+		BFCC = 0
+	}
+
+	Method (UPBX, 0, NotSerialized)
+	{
+		If (^^PCI0.LPCB.EC0.BAT0)
+		{
+			Local0 = (^^PCI0.LPCB.EC0.BDC0 & 0xFFFF)
+			PBIX [2] = Local0
+			Local0 = (^^PCI0.LPCB.EC0.BFC0 & 0xFFFF)
+			PBIX [3] = Local0
+			BFCC = Local0
+			Local0 = (^^PCI0.LPCB.EC0.BDV0 & 0xFFFF)
+			PBIX [5] = Local0
+			Local0 = (^^PCI0.LPCB.EC0.BCW0 & 0xFFFF)
+			PBIX [6] = Local0
+			Local0 = (^^PCI0.LPCB.EC0.BCL0 & 0xFFFF)
+			PBIX [7] = Local0
+			LOCAL0 = ^^PCI0.LPCB.EC0.CYC0
+			PBIX [8] = LOCAL0
+			PBIX [16] = "BAT"
+			PBIX [17] = "0001"
+			PBIX [18] = "LION"
+			PBIX [19] = "Notebook"
+		}
+		Else
+		{
+			IVBX ()
+		}
+	}
+
+	// _BIX: Battery Information Extended
+	Method (_BIX, 0, NotSerialized)
+	{
+		If (^^PCI0.LPCB.EC0.ECOK)
+		{
+			UPBX ()
+		}
+		Else
+		{
+			IVBX ()
+		}
+		Return (PBIX) /* \_SB_.BAT0.PBIX */
+	}
+
+	Name (PBST, Package (0x04)
+	{
+		0, // 0 - Battery state
+		0xFFFFFFFF, // 1 - Battery present rate
+		0xFFFFFFFF, // 2 - Battery remaining capacity
+		0xFFFFFFFF // 3 - Battery present voltage
+	})
+	Method (IVBS, 0, NotSerialized)
+	{
+		PBST [0] = 0
+		PBST [1] = 0xFFFFFFFF
+		PBST [2] = 0xFFFFFFFF
+		PBST [3] = 0xFFFFFFFF
+	}
+
+	Method (UPBS, 0, NotSerialized)
+	{
+		If (^^PCI0.LPCB.EC0.BAT0)
+		{
+			Local0 = 0
+			Local1 = 0
+			If (^^AC.ACFG)
+			{
+				If (((^^PCI0.LPCB.EC0.BST0 & 0x02) == 0x02))
+				{
+					Local0 |= 0x02
+					Local1 = (^^PCI0.LPCB.EC0.BPR0 & 0xFFFF)
+				}
+			}
+			Else
+			{
+				Local0 |= 1
+				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 */
+	}
+}
diff --git a/src/ec/dasharo/ec/acpi/battery_thresholds.asl b/src/ec/dasharo/ec/acpi/battery_thresholds.asl
new file mode 100644
index 0000000..3bb1330
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/battery_thresholds.asl
@@ -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
+		}
+	}
+}
diff --git a/src/ec/dasharo/ec/acpi/buttons.asl b/src/ec/dasharo/ec/acpi/buttons.asl
new file mode 100644
index 0000000..ae85c67
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/buttons.asl
@@ -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 })
+}
diff --git a/src/ec/dasharo/ec/acpi/ec.asl b/src/ec/dasharo/ec/acpi/ec.asl
new file mode 100644
index 0000000..e4fcdeb
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/ec.asl
@@ -0,0 +1,241 @@
+/* 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 "s76.asl"
+}
+
+Device (\_SB.PCI0.LPCB.EC0)
+{
+	Name (_HID, EisaId ("PNP0C09") /* Embedded Controller Device */)
+	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, 0)
+	Method (_REG, 2, Serialized)  // _REG: Region Availability
+	{
+		Printf ("EC: _REG %o %o", ToHexString(Arg0), ToHexString(Arg1))
+		If ((Arg0 == 0x03) && (Arg1 == 1)) {
+			// 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, 0)
+			Notify(^^^^BAT0, 0)
+
+			PNOT ()
+
+			// EC is now available
+			ECOK = Arg1
+
+			// Reset Dasharo Device
+			^^^^S76D.RSET()
+		}
+	}
+
+	Name (S3OS, 0)
+	Method (PTS, 1, Serialized) {
+		Printf ("EC: PTS: %o", ToHexString(Arg0))
+		If (ECOK) {
+			// Save ECOS during sleep
+			S3OS = ECOS
+
+			// Clear wake cause
+			WFNO = 0
+		}
+	}
+
+	Method (WAK, 1, Serialized) {
+		Printf ("EC: WAK: %o", 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, 0)
+			Notify(^^^^BAT0, 0)
+		}
+	}
+
+	Method (_Q0A, 0, NotSerialized) // Touchpad Toggle
+	{
+		Printf ("EC: Touchpad Toggle")
+	}
+
+	Method (_Q0B, 0, NotSerialized) // Screen Toggle
+	{
+		Printf ("EC: Screen Toggle")
+#if CONFIG(EC_DASHARO_EC_OLED)
+		Notify (^^^^S76D, 0x85)
+#endif // CONFIG(EC_DASHARO_EC_OLED)
+	}
+
+	Method (_Q0C, 0, NotSerialized)  // Mute
+	{
+		Printf ("EC: Mute")
+	}
+
+	Method (_Q0D, 0, NotSerialized) // Keyboard Backlight
+	{
+		Printf ("EC: Keyboard Backlight")
+	}
+
+	Method (_Q0E, 0, NotSerialized) // Volume Down
+	{
+		Printf ("EC: Volume Down")
+	}
+
+	Method (_Q0F, 0, NotSerialized) // Volume Up
+	{
+		Printf ("EC: Volume Up")
+	}
+
+	Method (_Q10, 0, NotSerialized) // Switch Video Mode
+	{
+		Printf ("EC: Switch Video Mode")
+	}
+
+	Method (_Q11, 0, NotSerialized) // Brightness Down
+	{
+		Printf ("EC: Brightness Down")
+		if (^^^^HIDD.HRDY) {
+			^^^^HIDD.HPEM (20)
+		}
+	}
+
+	Method (_Q12, 0, NotSerialized) // Brightness Up
+	{
+		Printf ("EC: Brightness Up")
+		if (^^^^HIDD.HRDY) {
+			^^^^HIDD.HPEM (19)
+		}
+	}
+
+	Method (_Q13, 0, NotSerialized) // Camera Toggle
+	{
+		Printf ("EC: Camera Toggle")
+	}
+
+	Method (_Q14, 0, NotSerialized) // Airplane Mode
+	{
+		Printf ("EC: Airplane Mode")
+		if (^^^^HIDD.HRDY) {
+			^^^^HIDD.HPEM (8)
+		}
+		// TODO: hardware airplane mode
+	}
+
+	Method (_Q15, 0, NotSerialized) // Suspend Button
+	{
+		Printf ("EC: Suspend Button")
+		Notify (SLPB, 0x80)
+	}
+
+	Method (_Q16, 0, NotSerialized) // AC Detect
+	{
+		Printf ("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
+	{
+		Printf ("EC: BAT0 Update (17)")
+		Notify (^^^^BAT0, 0x81) // Information Change
+	}
+
+	Method (_Q19, 0, NotSerialized)  // BAT0 Update
+	{
+		Printf ("EC: BAT0 Update (19)")
+		Notify (^^^^BAT0, 0x81) // Information Change
+	}
+
+	Method (_Q1B, 0, NotSerialized) // Lid Close
+	{
+		Printf ("EC: Lid Close")
+		Notify (LID0, 0x80)
+	}
+
+	Method (_Q1C, 0, NotSerialized) // Thermal Trip
+	{
+		Printf ("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
+	{
+		Printf ("EC: Power Button")
+		Notify (PWRB, 0x80)
+	}
+
+	Method (_Q50, 0, NotSerialized) // Other Events
+	{
+		Local0 = OEM4
+		If (Local0 == 0x8A) {
+			Printf ("EC: White Keyboard Backlight")
+			Notify (^^^^S76D, 0x80)
+		} ElseIf (Local0 == 0x9F) {
+			Printf ("EC: Color Keyboard Toggle")
+			Notify (^^^^S76D, 0x81)
+		} ElseIf (Local0 == 0x81) {
+			Printf ("EC: Color Keyboard Down")
+			Notify (^^^^S76D, 0x82)
+		} ElseIf (Local0 == 0x82) {
+			Printf ("EC: Color Keyboard Up")
+			Notify (^^^^S76D, 0x83)
+		} ElseIf (Local0 == 0x80) {
+			Printf ("EC: Color Keyboard Color Change")
+			Notify (^^^^S76D, 0x84)
+		} Else {
+			Printf ("EC: Other: %o", ToHexString(Local0))
+		}
+	}
+
+	#if CONFIG(EC_DASHARO_EC_BAT_THRESHOLDS)
+	#include "battery_thresholds.asl"
+	#endif
+}
diff --git a/src/ec/dasharo/ec/acpi/ec_ram.asl b/src/ec/dasharo/ec/acpi/ec_ram.asl
new file mode 100644
index 0000000..cb5bcf1
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/ec_ram.asl
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+OperationRegion (ERAM, EmbeddedControl, 0, 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,
+	CYC0, 16,	// Battery cycle count
+	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,
+}
diff --git a/src/ec/dasharo/ec/acpi/hid.asl b/src/ec/dasharo/ec/acpi/hid.asl
new file mode 100644
index 0000000..eb5f420
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/hid.asl
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+Device (HIDD)
+{
+	Name (_HID, "INT33D5")
+	Name (HBSY, 0)
+	Name (HIDX, 0)
+	Name (HRDY, 0)
+
+	Method (HDEM, 0, Serialized)
+	{
+		HBSY = 0
+		Return (HIDX)
+	}
+
+	Method (HDMM, 0, Serialized)
+	{
+		Return (0)
+	}
+
+	Method (HDSM, 1, Serialized)
+	{
+		HRDY = Arg0
+	}
+
+	Method (HPEM, 1, Serialized)
+	{
+		HBSY = 1
+		HIDX = Arg0
+
+		Notify (HIDD, 0xC0)
+		Local0 = 0
+		While ((Local0 < 0xFA) && HBSY)
+		{
+			Sleep (0x04)
+			Local0++
+		}
+
+		If (HBSY == 1)
+		{
+			HBSY = 0
+			HIDX = 0
+			Return (1)
+		}
+		Else
+		{
+			Return (0)
+		}
+	}
+}
diff --git a/src/ec/dasharo/ec/acpi/lid.asl b/src/ec/dasharo/ec/acpi/lid.asl
new file mode 100644
index 0000000..0a64341
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/lid.asl
@@ -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) {
+		Printf ("LID: _LID")
+		If (^^PCI0.LPCB.EC0.ECOK) {
+			Return (^^PCI0.LPCB.EC0.LSTE)
+		} Else {
+			Return (1)
+		}
+	}
+
+	Method (_PSW, 1, NotSerialized) {
+		Printf ("LID: _PSW: %o", ToHexString(Arg0))
+		If (^^PCI0.LPCB.EC0.ECOK) {
+			^^PCI0.LPCB.EC0.LWKE = Arg0
+		}
+	}
+}
diff --git a/src/ec/dasharo/ec/acpi/s76.asl b/src/ec/dasharo/ec/acpi/s76.asl
new file mode 100644
index 0000000..f23f857
--- /dev/null
+++ b/src/ec/dasharo/ec/acpi/s76.asl
@@ -0,0 +1,178 @@
+/* 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)
+	// Hide the device so that Windows does not warn about a missing driver.
+	Name (_STA, 0xB)
+
+	Method (RSET, 0, Serialized) {
+		Printf ("S76D: RSET")
+		SAPL(0)
+		SKBB(0)
+		SKBC(0xFFFFFF)
+	}
+
+	Method (INIT, 0, Serialized) {
+		Printf ("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) {
+		Printf ("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
+			}
+		}
+	}
+
+	// Get Keyboard Backlight Kind
+	// 0 - No backlight
+	// 1 - White backlight
+	// 2 - RGB backlight
+	Method (GKBK, 0, Serialized) {
+		Local0 = 0
+		If (^^PCI0.LPCB.EC0.ECOK) {
+			^^PCI0.LPCB.EC0.FDAT = 2
+			^^PCI0.LPCB.EC0.FCMD = 0xCA
+			Local0 = ^^PCI0.LPCB.EC0.FBUF
+		}
+		Return (Local0)
+	}
+
+	// Get Keyboard Brightness
+	Method (GKBB, 0, Serialized) {
+		Local0 = 0
+		If (^^PCI0.LPCB.EC0.ECOK) {
+			^^PCI0.LPCB.EC0.FDAT = 1
+			^^PCI0.LPCB.EC0.FCMD = 0xCA
+			Local0 = ^^PCI0.LPCB.EC0.FBUF
+		}
+		Return (Local0)
+	}
+
+	// Set Keyboard Brightness
+	Method (SKBB, 1, Serialized) {
+		If (^^PCI0.LPCB.EC0.ECOK) {
+			^^PCI0.LPCB.EC0.FDAT = 0
+			^^PCI0.LPCB.EC0.FBUF = Arg0
+			^^PCI0.LPCB.EC0.FCMD = 0xCA
+		}
+	}
+
+	// Get Keyboard Color
+	Method (GKBC, 0, Serialized) {
+		Local0 = 0
+		If (^^PCI0.LPCB.EC0.ECOK) {
+			^^PCI0.LPCB.EC0.FDAT = 4
+			^^PCI0.LPCB.EC0.FCMD = 0xCA
+			Local0 = ^^PCI0.LPCB.EC0.FBUF
+			Local0 |= (^^PCI0.LPCB.EC0.FBF1) << 16
+			Local0 |= (^^PCI0.LPCB.EC0.FBF2) << 8
+		}
+		Return (Local0)
+	}
+
+	// Set Keyboard Color
+	Method (SKBC, 1, Serialized) {
+		If (^^PCI0.LPCB.EC0.ECOK) {
+			^^PCI0.LPCB.EC0.FDAT = 3
+			^^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
+		}
+	}
+
+	// Fan names
+	Method (NFAN, 0, Serialized) {
+		Return (Package() {
+			"CPU fan",
+#if CONFIG(EC_DASHARO_EC_DGPU)
+			"GPU fan",
+#endif
+		})
+	}
+
+	// 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",
+#if CONFIG(EC_DASHARO_EC_DGPU)
+			"GPU temp",
+#endif
+		})
+	}
+
+	// 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)
+	}
+}
diff --git a/src/ec/dasharo/ec/dasharo_ec.c b/src/ec/dasharo/ec/dasharo_ec.c
new file mode 100644
index 0000000..97c9613
--- /dev/null
+++ b/src/ec/dasharo/ec/dasharo_ec.c
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include "dasharo_ec.h"
+#include <arch/io.h>
+#include <console/dasharo_ec.h>
+#include <console/console.h>
+#include <timer.h>
+
+// This is the command region for Dasharo EC firmware. It must be
+// enabled for LPC in the mainboard.
+#define DASHARO_EC_BASE 0x0E00
+#define DASHARO_EC_SIZE 256
+
+#define REG_CMD 0
+#define REG_RESULT 1
+#define REG_DATA 2	// Start of command data
+
+// When command register is 0, command is complete
+#define CMD_FINISHED 0
+
+#define RESULT_OK 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 dasharo_ec_read(uint8_t addr)
+{
+	return inb(DASHARO_EC_BASE + (uint16_t)addr);
+}
+
+static inline void dasharo_ec_write(uint8_t addr, uint8_t data)
+{
+	outb(data, DASHARO_EC_BASE + (uint16_t)addr);
+}
+
+void dasharo_ec_init(void)
+{
+	// Clear entire command region
+	for (int i = 0; i < DASHARO_EC_SIZE; i++)
+		dasharo_ec_write((uint8_t)i, 0);
+}
+
+void dasharo_ec_flush(void)
+{
+	dasharo_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, dasharo_ec_read(REG_CMD) == CMD_FINISHED);
+
+	dasharo_ec_write(CMD_PRINT_REG_LEN, 0);
+}
+
+void dasharo_ec_print(uint8_t byte)
+{
+	uint8_t len = dasharo_ec_read(CMD_PRINT_REG_LEN);
+	dasharo_ec_write(CMD_PRINT_REG_DATA + len, byte);
+	dasharo_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 >= (DASHARO_EC_SIZE - CMD_PRINT_REG_DATA))
+		dasharo_ec_flush();
+}
+
+bool dasharo_ec_cmd(uint8_t cmd, const uint8_t *request_data,
+	uint8_t request_size, uint8_t *reply_data, uint8_t reply_size)
+{
+	if (request_size > DASHARO_EC_SIZE - REG_DATA ||
+		reply_size > DASHARO_EC_SIZE - REG_DATA) {
+		printk(BIOS_ERR, "EC command %d too long - request size %u, reply size %u\n",
+			cmd, request_size, reply_size);
+		return false;
+	}
+
+	/* If any data were buffered by dasharo_ec_print(), flush it first */
+	uint8_t buffered_len = dasharo_ec_read(CMD_PRINT_REG_LEN);
+	if (buffered_len > 0)
+		dasharo_ec_flush();
+
+	/* Write the data */
+	uint8_t i;
+	for (i = 0; i < request_size; ++i)
+		dasharo_ec_write(REG_DATA + i, request_data[i]);
+
+	/* Write the command */
+	dasharo_ec_write(REG_CMD, cmd);
+
+	/* Wait for the command to complete */
+	bool ret = true;
+	int elapsed = wait_ms(1000, dasharo_ec_read(REG_CMD) == CMD_FINISHED);
+	if (elapsed == 0) {
+		/* Timed out: fail the command, don't attempt to read a reply. */
+		printk(BIOS_WARNING, "EC command %d timed out - request size %d, reply size %d\n",
+			cmd, request_size, reply_size);
+		ret = false;
+	} else {
+		/* Read the reply */
+		for (i = 0; i < reply_size; ++i)
+			reply_data[i] = dasharo_ec_read(REG_DATA+i);
+		/* Check the reply status */
+		ret = (dasharo_ec_read(REG_RESULT) == RESULT_OK);
+	}
+
+	/* Reset the flags and length so we can buffer console prints again */
+	dasharo_ec_write(CMD_PRINT_REG_FLAGS, 0);
+	dasharo_ec_write(CMD_PRINT_REG_LEN, 0);
+
+	return ret;
+}
diff --git a/src/ec/dasharo/ec/dasharo_ec.h b/src/ec/dasharo/ec/dasharo_ec.h
new file mode 100644
index 0000000..f313b0d
--- /dev/null
+++ b/src/ec/dasharo/ec/dasharo_ec.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef DASHARO_EC_H
+#define DASHARO_EC_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/*
+ * Send a command to the EC.  request_data/request_size are the request payload,
+ * request_data can be NULL if request_size is 0.  reply_data/reply_size are
+ * the reply payload, reply_data can be NULL if reply_size is 0.
+ */
+bool dasharo_ec_cmd(uint8_t cmd, const uint8_t *request_data,
+	uint8_t request_size, uint8_t *reply_data, uint8_t reply_size);
+
+#endif
diff --git a/src/ec/dasharo/ec/smbios.c b/src/ec/dasharo/ec/smbios.c
new file mode 100644
index 0000000..e55d5f0
--- /dev/null
+++ b/src/ec/dasharo/ec/smbios.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <smbios.h>
+
+smbios_wakeup_type smbios_system_wakeup_type(void)
+{
+	// TODO: Read wake source from EC.
+	return SMBIOS_WAKEUP_TYPE_POWER_SWITCH;
+}