From 08c44f33ae41fbef2c31e82563a65c64c9d962f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kope=C4=87?= Date: Sun, 8 Mar 2026 00:59:35 +0100 Subject: [PATCH] [WIP] ec/dasharo/ec: UCSI-ACPI support --- src/ec/dasharo/ec/acpi/ec.asl | 6 +- src/ec/dasharo/ec/acpi/smfi.asl | 37 ++++ src/ec/dasharo/ec/acpi/ucsi.asl | 297 ++++++++++++++++++++++++++++++++ src/ec/dasharo/ec/dasharo_ec.c | 94 ++++++++++ 4 files changed, 432 insertions(+), 2 deletions(-) create mode 100644 src/ec/dasharo/ec/acpi/smfi.asl create mode 100644 src/ec/dasharo/ec/acpi/ucsi.asl diff --git a/src/ec/dasharo/ec/acpi/ec.asl b/src/ec/dasharo/ec/acpi/ec.asl index 51221ee8628..ec705a8f6ee 100644 --- a/src/ec/dasharo/ec/acpi/ec.asl +++ b/src/ec/dasharo/ec/acpi/ec.asl @@ -4,10 +4,12 @@ Scope (\_SB) { #include "ac.asl" #include "battery.asl" #include "buttons.asl" - #include "hid.asl" - #include "lid.asl" #include "dshr.asl" #include "dtt.asl" + #include "hid.asl" + #include "lid.asl" + #include "smfi.asl" + #include "ucsi.asl" } Device (\_SB.PCI0.LPCB.EC0) diff --git a/src/ec/dasharo/ec/acpi/smfi.asl b/src/ec/dasharo/ec/acpi/smfi.asl new file mode 100644 index 00000000000..22c34a593f9 --- /dev/null +++ b/src/ec/dasharo/ec/acpi/smfi.asl @@ -0,0 +1,37 @@ +OperationRegion (SMFI, SystemIO, 0xE00, 0x100) +Field (SMFI, ByteAcc, Lock, Preserve) +{ + SMCR, 8, // REG_CMD + SMRR, 8, // REG_RESULT + SMDR, 2032, // REG_DATA +} + +Method (SMFC, 2, Serialized) { // SMFI Command + Local0 = 10 // Command completion timeout + // Wait for previous command completion + While (SMCR != 0) + { + Sleep (1) + Local0-- + If (Local0 == 0) + { + Return (1) + } + } + + SMDR = Arg1 + SMCR = Arg0 + + // Wait for command completion + While (SMCR != 0) + { + Sleep (1) + Local0-- + If (Local0 == 0) + { + Return (1) + } + } + + Return (SMRR) +} diff --git a/src/ec/dasharo/ec/acpi/ucsi.asl b/src/ec/dasharo/ec/acpi/ucsi.asl new file mode 100644 index 00000000000..c1bb4842dca --- /dev/null +++ b/src/ec/dasharo/ec/acpi/ucsi.asl @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Scope (\_SB) +{ + /* + * UCSI ACPI interface for Dasharo EC USB-PD (TPS65987). + * + * Transport: SMFI CMD_UCSI (0x1B) proxies UCSI CONTROL bytes to the TPS65987 + * via its 4CC 'UCSI' command interface. The TPS65987 DataX response is mapped + * to UCSI CCI + MESSAGE_IN in the shared buffer. + * + * The UCSI shared buffer (OperationRegion UCSM, _CRS, and all field + * declarations) is generated at runtime by dasharo_ec_fill_ssdt() in + * dasharo_ec.c, which allocates a CBMEM region and emits the SSDT scope + * "\\_SB.UCSI" with the actual physical address. The External() declarations + * below reference those dynamically-generated fields. + * + * UCSI v1.0 data structure layout (48 bytes, per CBMEM allocation): + * Offset 0 ( 2B): VERSION = 0x0100 + * Offset 2 ( 2B): RESERVED + * Offset 4 ( 4B): CCI (PPM writes, OPM reads) + * Offset 8 ( 8B): CONTROL (OPM writes, PPM reads) + * Offset 16 (16B): MESSAGE_IN (PPM writes, OPM reads) + * Offset 32 (16B): MESSAGE_OUT (OPM writes, PPM reads -- future use) + * + * SMFI CMD_UCSI data layout (matches cmd_ucsi() in smfi.c): + * Input data[0..7] = UCSI CONTROL (8 bytes) + * Output data[0] = response_len (N bytes in DataX) + * Output data[1] = TPS65987 task return code (0 = success) + * Output data[2..N] = TPS65987 DataX[2..N] -> UCSI MESSAGE_IN[0..] + * + * _DSM UUID: 6f8398c2-7ca4-11e4-ad36-631042b5008f (UCSI ACPI transport) + * Function 0: return supported bitmask (0x07 = functions 0, 1, 2) + * Function 1: OS has written CONTROL -> EC executes, populates CCI+MESSAGE_IN, notifies + * Function 2: OS acknowledges CCI read (no-op; data already in shared buffer) + */ + + /* Byte-level access to SMFI DATA region for UCSI command exchange. + * SMFI OperationRegion is defined in smfi.asl; additional Field overlays + * on the same region are permitted by the AML spec. */ + Field (SMFI, ByteAcc, Lock, Preserve) + { + Offset (0x02), /* Skip CMD (0x00) and RESULT (0x01) */ + SD00, 8, /* data[0]: CONTROL[0] in / response_len out */ + SD01, 8, /* data[1]: CONTROL[1] in / task return code out */ + SD02, 8, /* data[2]: CONTROL[2] in / MESSAGE_IN[0] out */ + SD03, 8, /* data[3]: CONTROL[3] in / MESSAGE_IN[1] out */ + SD04, 8, /* data[4]: CONTROL[4] in / MESSAGE_IN[2] out */ + SD05, 8, /* data[5]: CONTROL[5] in / MESSAGE_IN[3] out */ + SD06, 8, /* data[6]: CONTROL[6] in / MESSAGE_IN[4] out */ + SD07, 8, /* data[7]: CONTROL[7] in / MESSAGE_IN[5] out */ + SD08, 8, /* data[8]: MESSAGE_IN[6] out */ + SD09, 8, /* data[9]: MESSAGE_IN[7] out */ + SD0A, 8, /* data[10]: MESSAGE_IN[8] out */ + SD0B, 8, /* data[11]: MESSAGE_IN[9] out */ + SD0C, 8, /* data[12]: MESSAGE_IN[10] out */ + SD0D, 8, /* data[13]: MESSAGE_IN[11] out */ + SD0E, 8, /* data[14]: MESSAGE_IN[12] out */ + SD0F, 8, /* data[15]: MESSAGE_IN[13] out */ + SD10, 8, /* data[16]: MESSAGE_IN[14] out */ + } + + Device (UCSI) + { + Name (_HID, EisaId ("PNP0CA0")) + Name (_DDN, "Dasharo EC USB-C UCSI") + Name (_UID, 1) + Name (_STA, 0xF) + + /* + * _CRS, OperationRegion (UCSM), and field declarations are generated + * at runtime by dasharo_ec_fill_ssdt() into the SSDT under this scope. + */ + External (VER0, FieldUnitObj) + External (VER1, FieldUnitObj) + External (CCI0, FieldUnitObj) + External (CCI1, FieldUnitObj) + External (CCI2, FieldUnitObj) + External (CCI3, FieldUnitObj) + External (CTL0, FieldUnitObj) + External (CTL1, FieldUnitObj) + External (CTL2, FieldUnitObj) + External (CTL3, FieldUnitObj) + External (CTL4, FieldUnitObj) + External (CTL5, FieldUnitObj) + External (CTL6, FieldUnitObj) + External (CTL7, FieldUnitObj) + External (MGI0, FieldUnitObj) + External (MGI1, FieldUnitObj) + External (MGI2, FieldUnitObj) + External (MGI3, FieldUnitObj) + External (MGI4, FieldUnitObj) + External (MGI5, FieldUnitObj) + External (MGI6, FieldUnitObj) + External (MGI7, FieldUnitObj) + External (MGI8, FieldUnitObj) + External (MGI9, FieldUnitObj) + External (MGIA, FieldUnitObj) + External (MGIB, FieldUnitObj) + External (MGIC, FieldUnitObj) + External (MGID, FieldUnitObj) + External (MGIE, FieldUnitObj) + External (MGIF, FieldUnitObj) + External (MGO0, FieldUnitObj) + External (MGO1, FieldUnitObj) + External (MGO2, FieldUnitObj) + External (MGO3, FieldUnitObj) + External (MGO4, FieldUnitObj) + External (MGO5, FieldUnitObj) + External (MGO6, FieldUnitObj) + External (MGO7, FieldUnitObj) + External (MGO8, FieldUnitObj) + External (MGO9, FieldUnitObj) + External (MGOA, FieldUnitObj) + External (MGOB, FieldUnitObj) + External (MGOC, FieldUnitObj) + External (MGOD, FieldUnitObj) + External (MGOE, FieldUnitObj) + External (MGOF, FieldUnitObj) + + /* Write UCSI version into shared buffer on device init */ + Method (_INI, 0, Serialized) + { + VER0 = 0x00 + VER1 = 0x01 /* Version 1.0 (little-endian 0x0100) */ + } + + /* + * DSND: Execute a UCSI command via SMFI CMD_UCSI. + * + * Reads CONTROL from the shared buffer (CTL0..CTL7), writes it byte-by-byte + * to the SMFI DATA region (bypassing SMFC which would wipe all DATA bytes), + * issues CMD_UCSI = 0x1B, then maps the TPS65987 DataX response to + * UCSI CCI + MESSAGE_IN in the shared buffer. + * + * CCI byte mapping: + * CCI[2] bit 7 = CommandCompleted, bit 6 = Error, bit 5 = ResetCompleted + * CCI[1] = DataLength = number of valid MESSAGE_IN bytes + * = response_len - 1 (subtract the task return code byte) + */ + Method (DSND, 0, Serialized) + { + Local0 = 10 /* Pre-command timeout counter (ms) */ + + /* Wait for any previous SMFI command to finish */ + While (SMCR != 0) + { + Sleep (1) + Local0-- + If (Local0 == 0) + { + CCI0 = 0 + CCI1 = 0 + CCI2 = 0x40 /* ErrorIndicator = bit 22 of CCI = CCI2[6] */ + CCI3 = 0 + Notify (\_SB.UCSI, 0x80) + Return + } + } + + /* Write UCSI CONTROL bytes to SMFI DATA[0..7] */ + SD00 = CTL0 + SD01 = CTL1 + SD02 = CTL2 + SD03 = CTL3 + SD04 = CTL4 + SD05 = CTL5 + SD06 = CTL6 + SD07 = CTL7 + + /* Issue CMD_UCSI (27 = 0x1B) */ + SMCR = 0x1B + + /* Poll for command completion (~200 ms timeout) */ + Local0 = 200 + While (SMCR != 0) + { + Sleep (1) + Local0-- + If (Local0 == 0) + { + CCI0 = 0 + CCI1 = 0 + CCI2 = 0x40 /* ErrorIndicator = bit 22 of CCI = CCI2[6] */ + CCI3 = 0 + Notify (\_SB.UCSI, 0x80) + Return + } + } + + /* + * Map TPS65987 response to UCSI CCI + MESSAGE_IN. + * SMRR = SMFI result (0 = RES_OK), SD01 = TPS65987 task return code. + */ + If (LAnd (SMRR == 0, SD01 == 0)) + { + /* Success */ + Local1 = SD00 /* response_len */ + If (Local1 > 0) { Local1-- } /* subtract task return code byte */ + + CCI0 = 0 + CCI1 = Local1 /* DataLength: valid MESSAGE_IN bytes */ + /* CommandCompleted = bit 23 of CCI = CCI2[7] = 0x80 */ + /* ResetCompleted = bit 21 of CCI = CCI2[5] = 0x20 */ + If (CTL0 == 0x01) /* PPM Reset command */ + { + CCI2 = 0xA0 /* CommandCompleted | ResetCompleted */ + } + Else + { + CCI2 = 0x80 /* CommandCompleted */ + } + CCI3 = 0 + + MGI0 = SD02 + MGI1 = SD03 + MGI2 = SD04 + MGI3 = SD05 + MGI4 = SD06 + MGI5 = SD07 + MGI6 = SD08 + MGI7 = SD09 + MGI8 = SD0A + MGI9 = SD0B + MGIA = SD0C + MGIB = SD0D + MGIC = SD0E + MGID = SD0F + MGIE = SD10 + MGIF = 0 + } + Else + { + /* Error: SMFI failed or TPS65987 returned non-zero task code */ + CCI0 = 0 + CCI1 = 0 + CCI2 = 0x40 /* ErrorIndicator = bit 22 of CCI = CCI2[6] */ + CCI3 = 0 + + MGI0 = 0 + MGI1 = 0 + MGI2 = 0 + MGI3 = 0 + MGI4 = 0 + MGI5 = 0 + MGI6 = 0 + MGI7 = 0 + MGI8 = 0 + MGI9 = 0 + MGIA = 0 + MGIB = 0 + MGIC = 0 + MGID = 0 + MGIE = 0 + MGIF = 0 + } + + /* Signal OS that PPM has completed the command */ + Notify (\_SB.UCSI, 0x80) + } + + Method (_DSM, 4, Serialized) + { + If (Arg0 != ToUUID ("6f8398c2-7ca4-11e4-ad36-631042b5008f")) { + Return (Buffer (1) { 0 }) + } + + Switch (ToInteger (Arg2)) + { + Case (0) + { + /* Query supported functions: 0, 1, 2 */ + Return (Buffer (1) { 0x07 }) + } + Case (1) + { + /* + * Send UCSI Data to PPM. + * OS driver has written CONTROL to CTL0..CTL7 in the + * shared buffer. Execute via SMFI and update CCI + MESSAGE_IN. + */ + DSND () + } + Case (2) + { + /* + * Get UCSI Data from PPM (OPM acknowledge). + * CCI and MESSAGE_IN are already in the shared buffer + * from the completed DSND() call. Nothing to do. + */ + } + } + + Return (Buffer (1) { 0 }) + } + } +} \ No newline at end of file diff --git a/src/ec/dasharo/ec/dasharo_ec.c b/src/ec/dasharo/ec/dasharo_ec.c index 3e9c7d1eb43..8ea68a8d222 100644 --- a/src/ec/dasharo/ec/dasharo_ec.c +++ b/src/ec/dasharo/ec/dasharo_ec.c @@ -1,8 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +#include #include #include #include +#include +#include #include #include #include @@ -984,10 +987,101 @@ static void dasharo_ec_init(struct device *dev) pc_keyboard_init(PROBE_AUX_DEVICE); } +/* + * Setting minimum length of UCSI_ACPI will ensure this region is placed out of + * IMD Small, preventing memory mapping conflicts. + */ +#define UCSI_MIN_ALLOC_REGION_LEN CBMEM_SM_ROOT_SIZE + +static struct fieldlist ucsi_region_fields[] = { + FIELDLIST_NAMESTR("VER0", 8), + FIELDLIST_NAMESTR("VER1", 8), + FIELDLIST_NAMESTR("RSV0", 8), + FIELDLIST_NAMESTR("RSV1", 8), + FIELDLIST_NAMESTR("CCI0", 8), + FIELDLIST_NAMESTR("CCI1", 8), + FIELDLIST_NAMESTR("CCI2", 8), + FIELDLIST_NAMESTR("CCI3", 8), + FIELDLIST_NAMESTR("CTL0", 8), + FIELDLIST_NAMESTR("CTL1", 8), + FIELDLIST_NAMESTR("CTL2", 8), + FIELDLIST_NAMESTR("CTL3", 8), + FIELDLIST_NAMESTR("CTL4", 8), + FIELDLIST_NAMESTR("CTL5", 8), + FIELDLIST_NAMESTR("CTL6", 8), + FIELDLIST_NAMESTR("CTL7", 8), + FIELDLIST_NAMESTR("MGI0", 8), + FIELDLIST_NAMESTR("MGI1", 8), + FIELDLIST_NAMESTR("MGI2", 8), + FIELDLIST_NAMESTR("MGI3", 8), + FIELDLIST_NAMESTR("MGI4", 8), + FIELDLIST_NAMESTR("MGI5", 8), + FIELDLIST_NAMESTR("MGI6", 8), + FIELDLIST_NAMESTR("MGI7", 8), + FIELDLIST_NAMESTR("MGI8", 8), + FIELDLIST_NAMESTR("MGI9", 8), + FIELDLIST_NAMESTR("MGIA", 8), + FIELDLIST_NAMESTR("MGIB", 8), + FIELDLIST_NAMESTR("MGIC", 8), + FIELDLIST_NAMESTR("MGID", 8), + FIELDLIST_NAMESTR("MGIE", 8), + FIELDLIST_NAMESTR("MGIF", 8), + FIELDLIST_NAMESTR("MGO0", 8), + FIELDLIST_NAMESTR("MGO1", 8), + FIELDLIST_NAMESTR("MGO2", 8), + FIELDLIST_NAMESTR("MGO3", 8), + FIELDLIST_NAMESTR("MGO4", 8), + FIELDLIST_NAMESTR("MGO5", 8), + FIELDLIST_NAMESTR("MGO6", 8), + FIELDLIST_NAMESTR("MGO7", 8), + FIELDLIST_NAMESTR("MGO8", 8), + FIELDLIST_NAMESTR("MGO9", 8), + FIELDLIST_NAMESTR("MGOA", 8), + FIELDLIST_NAMESTR("MGOB", 8), + FIELDLIST_NAMESTR("MGOC", 8), + FIELDLIST_NAMESTR("MGOD", 8), + FIELDLIST_NAMESTR("MGOE", 8), + FIELDLIST_NAMESTR("MGOF", 8), +}; +static const size_t ucsi_region_fields_len = ARRAY_SIZE(ucsi_region_fields); + +static void dasharo_ec_fill_ssdt(const struct device *dev) +{ + struct opregion opreg; + void *region_ptr; + size_t alloc_len; + + alloc_len = ucsi_region_fields_len < UCSI_MIN_ALLOC_REGION_LEN ? + UCSI_MIN_ALLOC_REGION_LEN : ucsi_region_fields_len; + + region_ptr = cbmem_add(CBMEM_ID_ACPI_UCSI, alloc_len); + if (!region_ptr) { + printk(BIOS_ERR, "Dasharo EC: Failed to allocate UCSI CBMEM region\n"); + return; + } + memset(region_ptr, 0, alloc_len); + + opreg.name = "UCSM"; + opreg.regionspace = SYSTEMMEMORY; + opreg.regionoffset = (uintptr_t)region_ptr; + opreg.regionlen = alloc_len; + + acpigen_write_scope("\\_SB.UCSI"); + acpigen_write_name("_CRS"); + acpigen_write_resourcetemplate_header(); + acpigen_write_mem32fixed(1, (uintptr_t)region_ptr, ucsi_region_fields_len); + acpigen_write_resourcetemplate_footer(); + acpigen_write_opregion(&opreg); + acpigen_write_field(opreg.name, ucsi_region_fields, ucsi_region_fields_len, + FIELD_ANYACC | FIELD_LOCK | FIELD_PRESERVE); + acpigen_pop_len(); /* Scope */ +} + static struct device_operations ops = { .init = dasharo_ec_init, .read_resources = noop_read_resources, .set_resources = noop_set_resources, + .acpi_fill_ssdt = dasharo_ec_fill_ssdt, }; static struct pnp_info pnp_dev_info[] = {