From f0ea699b1816ebed6de9aa013387622b0f73f31b Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 18 Feb 2025 14:41:10 -0500 Subject: [PATCH 01/16] platform/x86: hp: Use *-y instead of *-objs in Makefile MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The `objs` suffix is reserved for user-space tools. Use the `y` suffix instead, which is usually used for kernel drivers. Suggested-by: Andy Shevchenko Signed-off-by: Kurt Borja Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250218194113.26589-4-kuurtb@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-bioscfg/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/hp/hp-bioscfg/Makefile b/drivers/platform/x86/hp/hp-bioscfg/Makefile index 67be0d917753..7d23649b34dc 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/Makefile +++ b/drivers/platform/x86/hp/hp-bioscfg/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_HP_BIOSCFG) := hp-bioscfg.o -hp-bioscfg-objs := bioscfg.o \ +hp-bioscfg-y := bioscfg.o \ biosattr-interface.o \ enum-attributes.o \ int-attributes.o \ -- 2.51.0 From 677fb7b0699dde2609d93eb57863c8d493e673ee Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 18 Feb 2025 14:41:11 -0500 Subject: [PATCH 02/16] platform/x86: intel: Use *-y instead of *-objs in Makefile MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The `objs` suffix is reserved for user-space tools. Use the `y` suffix instead, which is usually used for kernel drivers. Suggested-by: Andy Shevchenko Signed-off-by: Kurt Borja Reviewed-by: Jithu Joseph Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250218194113.26589-5-kuurtb@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/ifs/Makefile b/drivers/platform/x86/intel/ifs/Makefile index 30f035ef5581..c3e417bce9b6 100644 --- a/drivers/platform/x86/intel/ifs/Makefile +++ b/drivers/platform/x86/intel/ifs/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_INTEL_IFS) += intel_ifs.o -intel_ifs-objs := core.o load.o runtest.o sysfs.o +intel_ifs-y := core.o load.o runtest.o sysfs.o -- 2.51.0 From 5ad6d62c9b183314ec1c64a95a26636e973e736a Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Mon, 10 Feb 2025 22:34:45 +0200 Subject: [PATCH 03/16] MAINTAINERS: Add documentation reference for Mellanox platform MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add reference for Documentation/ABI/stable/sysfs-driver-mlxreg-io under "MELLANOX HARDWARE PLATFORM SUPPORT". Signed-off-by: Vadim Pasternak Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202502032336.lauIV68J-lkp@intel.com/ Link: https://lore.kernel.org/r/20250210203451.29712-1-vadimp@nvidia.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6f78d6bcbc7b..f71fec78e1cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15030,6 +15030,7 @@ M: Ilpo Järvinen M: Vadim Pasternak L: platform-driver-x86@vger.kernel.org S: Supported +F: Documentation/ABI/stable/sysfs-driver-mlxreg-io F: Documentation/ABI/testing/sysfs-platform-mellanox-bootctl F: drivers/platform/mellanox/ F: include/linux/platform_data/mlxreg.h -- 2.51.0 From defcf2fb30f7bf128c0be5e571f4db2b7fff66cc Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sat, 15 Feb 2025 02:06:54 +0800 Subject: [PATCH 04/16] dt-bindings: platform: Add Huawei Matebook E Go EC MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add binding for the EC found in the Huawei Matebook E Go and Huawei Matebook E Go LTE 2-in-1 tablets, the former one is a QS sc8280xp based tablet, the latter one is a QS sc8180x based tablet. This series has a codename, gaokun. More information about gaokun, please check https://bugzilla.kernel.org/show_bug.cgi?id=219645 Signed-off-by: Pengyu Luo Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250214180656.28599-2-mitltlatltl@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../bindings/platform/huawei,gaokun-ec.yaml | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/platform/huawei,gaokun-ec.yaml diff --git a/Documentation/devicetree/bindings/platform/huawei,gaokun-ec.yaml b/Documentation/devicetree/bindings/platform/huawei,gaokun-ec.yaml new file mode 100644 index 000000000000..4a03b0ee3149 --- /dev/null +++ b/Documentation/devicetree/bindings/platform/huawei,gaokun-ec.yaml @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/platform/huawei,gaokun-ec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Huawei Matebook E Go Embedded Controller + +maintainers: + - Pengyu Luo + +description: + Different from other Qualcomm Snapdragon sc8180x and sc8280xp-based + machines, the Huawei Matebook E Go tablets use embedded controllers + while others use a system called PMIC GLink which handles battery, + UCSI, USB Type-C DP Alt Mode. In addition, Huawei's implementation + also handles additional features, such as charging thresholds, FN + lock, smart charging, tablet lid status, thermal sensors, and more. + +properties: + compatible: + enum: + - huawei,gaokun3-ec + + reg: + const: 0x38 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + interrupts: + maxItems: 1 + +patternProperties: + '^connector@[01]$': + $ref: /schemas/connector/usb-connector.yaml# + + properties: + reg: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + embedded-controller@38 { + compatible = "huawei,gaokun3-ec"; + reg = <0x38>; + + interrupts-extended = <&tlmm 107 IRQ_TYPE_LEVEL_LOW>; + + #address-cells = <1>; + #size-cells = <0>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + ucsi0_ss_in: endpoint { + remote-endpoint = <&usb_0_qmpphy_out>; + }; + }; + + port@1 { + reg = <1>; + + ucsi0_sbu: endpoint { + remote-endpoint = <&usb0_sbu_mux>; + }; + }; + }; + }; + + connector@1 { + compatible = "usb-c-connector"; + reg = <1>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + ucsi1_ss_in: endpoint { + remote-endpoint = <&usb_1_qmpphy_out>; + }; + }; + + port@1 { + reg = <1>; + + ucsi1_sbu: endpoint { + remote-endpoint = <&usb1_sbu_mux>; + }; + }; + }; + }; + }; + }; -- 2.51.0 From 7636f090d02e791918bb3c924e695880123d0c59 Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sat, 15 Feb 2025 02:06:55 +0800 Subject: [PATCH 05/16] platform: arm64: add Huawei Matebook E Go EC driver MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit There are three variants of which Huawei released the first two simultaneously. Huawei Matebook E Go LTE(sc8180x), codename seems to be gaokun2. Huawei Matebook E Go(sc8280xp@3.0GHz), codename must be gaokun3. (see [1]) Huawei Matebook E Go 2023(sc8280xp@2.69GHz), codename should be also gaokun3. Adding support for the latter two variants for now, this driver should also work for the sc8180x variant according to acpi table files, but I don't have the device to test yet. Different from other Qualcomm Snapdragon sc8280xp based machines, the Huawei Matebook E Go uses an embedded controller while others use a system called PMIC GLink. This embedded controller can be used to perform a set of various functions, including, but not limited to: - Battery and charger monitoring; - Charge control and smart charge; - Fn_lock settings; - Tablet lid status; - Temperature sensors; - USB Type-C notifications (ports orientation, DP alt mode HPD); - USB Type-C PD (according to observation, up to 48w). Add a driver for the EC which creates devices for UCSI and power supply devices. This driver is inspired by the following drivers: drivers/platform/arm64/acer-aspire1-ec.c drivers/platform/arm64/lenovo-yoga-c630.c drivers/platform/x86/huawei-wmi.c Also thanks for reviewers' working. They have made this patch improve a lot. [1] https://bugzilla.kernel.org/show_bug.cgi?id=219645 Signed-off-by: Pengyu Luo Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20250214180656.28599-3-mitltlatltl@gmail.com Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 7 + drivers/platform/arm64/Kconfig | 21 + drivers/platform/arm64/Makefile | 1 + drivers/platform/arm64/huawei-gaokun-ec.c | 825 ++++++++++++++++++ .../linux/platform_data/huawei-gaokun-ec.h | 79 ++ 5 files changed, 933 insertions(+) create mode 100644 drivers/platform/arm64/huawei-gaokun-ec.c create mode 100644 include/linux/platform_data/huawei-gaokun-ec.h diff --git a/MAINTAINERS b/MAINTAINERS index f71fec78e1cf..0669b6433b1a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10698,6 +10698,13 @@ S: Maintained F: Documentation/networking/device_drivers/ethernet/huawei/hinic.rst F: drivers/net/ethernet/huawei/hinic/ +HUAWEI MATEBOOK E GO EMBEDDED CONTROLLER DRIVER +M: Pengyu Luo +S: Maintained +F: Documentation/devicetree/bindings/platform/huawei,gaokun-ec.yaml +F: drivers/platform/arm64/huawei-gaokun-ec.c +F: include/linux/platform_data/huawei-gaokun-ec.h + HUGETLB SUBSYSTEM M: Muchun Song L: linux-mm@kvack.org diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig index f88395ea3376..0abe5377891b 100644 --- a/drivers/platform/arm64/Kconfig +++ b/drivers/platform/arm64/Kconfig @@ -33,6 +33,27 @@ config EC_ACER_ASPIRE1 laptop where this information is not properly exposed via the standard ACPI devices. +config EC_HUAWEI_GAOKUN + tristate "Huawei Matebook E Go Embedded Controller driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on I2C + depends on INPUT + depends on HWMON + select AUXILIARY_BUS + + help + Say Y here to enable the EC driver for the Huawei Matebook E Go + which is a sc8280xp-based 2-in-1 tablet. The driver handles battery + (information, charge control) and USB Type-C DP HPD events as well + as some misc functions like the lid sensor and temperature sensors, + etc. + + This driver provides battery and AC status support for the mentioned + laptop where this information is not properly exposed via the + standard ACPI devices. + + Say M or Y here to include this support. + config EC_LENOVO_YOGA_C630 tristate "Lenovo Yoga C630 Embedded Controller driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile index b2ae9114fdd8..46a99eba3264 100644 --- a/drivers/platform/arm64/Makefile +++ b/drivers/platform/arm64/Makefile @@ -6,4 +6,5 @@ # obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o +obj-$(CONFIG_EC_HUAWEI_GAOKUN) += huawei-gaokun-ec.o obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o diff --git a/drivers/platform/arm64/huawei-gaokun-ec.c b/drivers/platform/arm64/huawei-gaokun-ec.c new file mode 100644 index 000000000000..97c2607f8d9f --- /dev/null +++ b/drivers/platform/arm64/huawei-gaokun-ec.c @@ -0,0 +1,825 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * huawei-gaokun-ec - An EC driver for HUAWEI Matebook E Go + * + * Copyright (C) 2024-2025 Pengyu Luo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EC_EVENT 0x06 + +/* Also can be found in ACPI specification 12.3 */ +#define EC_READ 0x80 +#define EC_WRITE 0x81 +#define EC_BURST 0x82 +#define EC_QUERY 0x84 + +#define EC_FN_LOCK_ON 0x5A +#define EC_FN_LOCK_OFF 0x55 +#define EC_FN_LOCK_READ 0x6B +#define EC_FN_LOCK_WRITE 0x6C + +#define EC_EVENT_LID 0x81 + +#define EC_LID_STATE 0x80 +#define EC_LID_OPEN BIT(1) + +#define EC_TEMP_REG 0x61 + +#define EC_STANDBY_REG 0xB2 +#define EC_STANDBY_ENTER 0xDB +#define EC_STANDBY_EXIT 0xEB + +enum gaokun_ec_smart_charge_cmd { + SMART_CHARGE_DATA_WRITE = 0xE3, + SMART_CHARGE_DATA_READ, + SMART_CHARGE_ENABLE_WRITE, + SMART_CHARGE_ENABLE_READ, +}; + +enum gaokun_ec_ucsi_cmd { + UCSI_REG_WRITE = 0xD2, + UCSI_REG_READ, + UCSI_DATA_WRITE, + UCSI_DATA_READ, +}; + +#define UCSI_REG_SIZE 7 + +/* + * For tx, command sequences are arranged as + * {master_cmd, slave_cmd, data_len, data_seq} + */ +#define REQ_HDR_SIZE 3 +#define INPUT_SIZE_OFFSET 2 +#define REQ_LEN(req) (REQ_HDR_SIZE + (req)[INPUT_SIZE_OFFSET]) + +/* + * For rx, data sequences are arranged as + * {status, data_len(unreliable), data_seq} + */ +#define RESP_HDR_SIZE 2 + +#define MKREQ(REG0, REG1, SIZE, ...) \ +{ \ + REG0, REG1, SIZE, \ + /* ## will remove comma when SIZE is 0 */ \ + ## __VA_ARGS__, \ + /* make sure len(pkt[3:]) >= SIZE */ \ + [3 + (SIZE)] = 0, \ +} + +#define MKRESP(SIZE) \ +{ \ + [RESP_HDR_SIZE + (SIZE) - 1] = 0, \ +} + +/* Possible size 1, 4, 20, 24. Most of the time, the size is 1. */ +static inline void refill_req(u8 *dest, const u8 *src, size_t size) +{ + memcpy(dest + REQ_HDR_SIZE, src, size); +} + +static inline void refill_req_byte(u8 *dest, const u8 *src) +{ + dest[REQ_HDR_SIZE] = *src; +} + +/* Possible size 1, 2, 4, 7, 20. Most of the time, the size is 1. */ +static inline void extr_resp(u8 *dest, const u8 *src, size_t size) +{ + memcpy(dest, src + RESP_HDR_SIZE, size); +} + +static inline void extr_resp_byte(u8 *dest, const u8 *src) +{ + *dest = src[RESP_HDR_SIZE]; +} + +static inline void *extr_resp_shallow(const u8 *src) +{ + return (void *)(src + RESP_HDR_SIZE); +} + +struct gaokun_ec { + struct i2c_client *client; + struct mutex lock; /* EC transaction lock */ + struct blocking_notifier_head notifier_list; + struct device *hwmon_dev; + struct input_dev *idev; + bool suspended; +}; + +static int gaokun_ec_request(struct gaokun_ec *ec, const u8 *req, + size_t resp_len, u8 *resp) +{ + struct i2c_client *client = ec->client; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = client->flags, + .len = REQ_LEN(req), + .buf = (void *)req, + }, { + .addr = client->addr, + .flags = client->flags | I2C_M_RD, + .len = resp_len, + .buf = resp, + }, + }; + int ret; + + guard(mutex)(&ec->lock); + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "I2C transfer error %d\n", ret); + goto out_after_break; + } + + ret = *resp; + if (ret) + dev_err(&client->dev, "EC transaction error %d\n", ret); + +out_after_break: + usleep_range(2000, 2500); /* have a break, ACPI did this */ + + return ret; +} + +/* -------------------------------------------------------------------------- */ +/* Common API */ + +/** + * gaokun_ec_read - Read from EC + * @ec: The gaokun_ec structure + * @req: The sequence to request + * @resp_len: The size to read + * @resp: The buffer to store response sequence + * + * This function is used to read data after writing a magic sequence to EC. + * All EC operations depend on this function. + * + * Huawei uses magic sequences everywhere to complete various functions, all + * these sequences are passed to ECCD(a ACPI method which is quiet similar + * to gaokun_ec_request), there is no good abstraction to generalize these + * sequences, so just wrap it for now. Almost all magic sequences are kept + * in this file. + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_read(struct gaokun_ec *ec, const u8 *req, + size_t resp_len, u8 *resp) +{ + return gaokun_ec_request(ec, req, resp_len, resp); +} +EXPORT_SYMBOL_GPL(gaokun_ec_read); + +/** + * gaokun_ec_write - Write to EC + * @ec: The gaokun_ec structure + * @req: The sequence to request + * + * This function has no big difference from gaokun_ec_read. When caller care + * only write status and no actual data are returned, then use it. + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_write(struct gaokun_ec *ec, const u8 *req) +{ + u8 ec_resp[] = MKRESP(0); + + return gaokun_ec_request(ec, req, sizeof(ec_resp), ec_resp); +} +EXPORT_SYMBOL_GPL(gaokun_ec_write); + +int gaokun_ec_read_byte(struct gaokun_ec *ec, const u8 *req, u8 *byte) +{ + int ret; + u8 ec_resp[] = MKRESP(sizeof(*byte)); + + ret = gaokun_ec_read(ec, req, sizeof(ec_resp), ec_resp); + extr_resp_byte(byte, ec_resp); + + return ret; +} +EXPORT_SYMBOL_GPL(gaokun_ec_read_byte); + +/** + * gaokun_ec_register_notify - Register a notifier callback for EC events. + * @ec: The gaokun_ec structure + * @nb: Notifier block pointer to register + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_register_notify(struct gaokun_ec *ec, struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&ec->notifier_list, nb); +} +EXPORT_SYMBOL_GPL(gaokun_ec_register_notify); + +/** + * gaokun_ec_unregister_notify - Unregister notifier callback for EC events. + * @ec: The gaokun_ec structure + * @nb: Notifier block pointer to unregister + * + * Unregister a notifier callback that was previously registered with + * gaokun_ec_register_notify(). + */ +void gaokun_ec_unregister_notify(struct gaokun_ec *ec, struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&ec->notifier_list, nb); +} +EXPORT_SYMBOL_GPL(gaokun_ec_unregister_notify); + +/* -------------------------------------------------------------------------- */ +/* API for PSY */ + +/** + * gaokun_ec_psy_multi_read - Read contiguous registers + * @ec: The gaokun_ec structure + * @reg: The start register + * @resp_len: The number of registers to be read + * @resp: The buffer to store response sequence + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_psy_multi_read(struct gaokun_ec *ec, u8 reg, + size_t resp_len, u8 *resp) +{ + u8 ec_req[] = MKREQ(0x02, EC_READ, 1, 0); + u8 ec_resp[] = MKRESP(1); + int i, ret; + + for (i = 0; i < resp_len; ++i, reg++) { + refill_req_byte(ec_req, ®); + ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp); + if (ret) + return ret; + extr_resp_byte(&resp[i], ec_resp); + } + + return 0; +} +EXPORT_SYMBOL_GPL(gaokun_ec_psy_multi_read); + +/* Smart charge */ + +/** + * gaokun_ec_psy_get_smart_charge - Get smart charge data from EC + * @ec: The gaokun_ec structure + * @resp: The buffer to store response sequence (mode, delay, start, end) + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_psy_get_smart_charge(struct gaokun_ec *ec, + u8 resp[GAOKUN_SMART_CHARGE_DATA_SIZE]) +{ + /* GBCM */ + u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_DATA_READ, 0); + u8 ec_resp[] = MKRESP(GAOKUN_SMART_CHARGE_DATA_SIZE); + int ret; + + ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp); + if (ret) + return ret; + + extr_resp(resp, ec_resp, GAOKUN_SMART_CHARGE_DATA_SIZE); + + return 0; +} +EXPORT_SYMBOL_GPL(gaokun_ec_psy_get_smart_charge); + +static inline bool validate_battery_threshold_range(u8 start, u8 end) +{ + return end != 0 && start <= end && end <= 100; +} + +/** + * gaokun_ec_psy_set_smart_charge - Set smart charge data + * @ec: The gaokun_ec structure + * @req: The sequence to request (mode, delay, start, end) + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_psy_set_smart_charge(struct gaokun_ec *ec, + const u8 req[GAOKUN_SMART_CHARGE_DATA_SIZE]) +{ + /* SBCM */ + u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_DATA_WRITE, + GAOKUN_SMART_CHARGE_DATA_SIZE); + + if (!validate_battery_threshold_range(req[2], req[3])) + return -EINVAL; + + refill_req(ec_req, req, GAOKUN_SMART_CHARGE_DATA_SIZE); + + return gaokun_ec_write(ec, ec_req); +} +EXPORT_SYMBOL_GPL(gaokun_ec_psy_set_smart_charge); + +/* Smart charge enable */ + +/** + * gaokun_ec_psy_get_smart_charge_enable - Get smart charge state + * @ec: The gaokun_ec structure + * @on: The state + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_psy_get_smart_charge_enable(struct gaokun_ec *ec, bool *on) +{ + /* GBAC */ + u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_ENABLE_READ, 0); + u8 state; + int ret; + + ret = gaokun_ec_read_byte(ec, ec_req, &state); + if (ret) + return ret; + + *on = !!state; + + return 0; +} +EXPORT_SYMBOL_GPL(gaokun_ec_psy_get_smart_charge_enable); + +/** + * gaokun_ec_psy_set_smart_charge_enable - Set smart charge state + * @ec: The gaokun_ec structure + * @on: The state + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_psy_set_smart_charge_enable(struct gaokun_ec *ec, bool on) +{ + /* SBAC */ + u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_ENABLE_WRITE, 1, on); + + return gaokun_ec_write(ec, ec_req); +} +EXPORT_SYMBOL_GPL(gaokun_ec_psy_set_smart_charge_enable); + +/* -------------------------------------------------------------------------- */ +/* API for UCSI */ + +/** + * gaokun_ec_ucsi_read - Read UCSI data from EC + * @ec: The gaokun_ec structure + * @resp: The buffer to store response sequence + * + * Read CCI and MSGI (used by UCSI subdriver). + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_ucsi_read(struct gaokun_ec *ec, + u8 resp[GAOKUN_UCSI_READ_SIZE]) +{ + u8 ec_req[] = MKREQ(0x03, UCSI_DATA_READ, 0); + u8 ec_resp[] = MKRESP(GAOKUN_UCSI_READ_SIZE); + int ret; + + ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp); + if (ret) + return ret; + + extr_resp(resp, ec_resp, GAOKUN_UCSI_READ_SIZE); + return 0; +} +EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_read); + +/** + * gaokun_ec_ucsi_write - Write UCSI data to EC + * @ec: The gaokun_ec structure + * @req: The sequence to request + * + * Write CTRL and MSGO (used by UCSI subdriver). + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_ucsi_write(struct gaokun_ec *ec, + const u8 req[GAOKUN_UCSI_WRITE_SIZE]) +{ + u8 ec_req[] = MKREQ(0x03, UCSI_DATA_WRITE, GAOKUN_UCSI_WRITE_SIZE); + + refill_req(ec_req, req, GAOKUN_UCSI_WRITE_SIZE); + + return gaokun_ec_write(ec, ec_req); +} +EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_write); + +/** + * gaokun_ec_ucsi_get_reg - Get UCSI register from EC + * @ec: The gaokun_ec structure + * @ureg: The gaokun ucsi register + * + * Get UCSI register data (used by UCSI subdriver). + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_ucsi_get_reg(struct gaokun_ec *ec, struct gaokun_ucsi_reg *ureg) +{ + u8 ec_req[] = MKREQ(0x03, UCSI_REG_READ, 0); + u8 ec_resp[] = MKRESP(UCSI_REG_SIZE); + int ret; + + ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp); + if (ret) + return ret; + + extr_resp((u8 *)ureg, ec_resp, UCSI_REG_SIZE); + + return 0; +} +EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_get_reg); + +/** + * gaokun_ec_ucsi_pan_ack - Ack pin assignment notifications from EC + * @ec: The gaokun_ec structure + * @port_id: The port id receiving and handling the notifications + * + * Ack pin assignment notifications (used by UCSI subdriver). + * + * Return: 0 on success or negative error code. + */ +int gaokun_ec_ucsi_pan_ack(struct gaokun_ec *ec, int port_id) +{ + u8 ec_req[] = MKREQ(0x03, UCSI_REG_WRITE, 1); + u8 data = 1 << port_id; + + if (port_id == GAOKUN_UCSI_NO_PORT_UPDATE) + data = 0; + + refill_req_byte(ec_req, &data); + + return gaokun_ec_write(ec, ec_req); +} +EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_pan_ack); + +/* -------------------------------------------------------------------------- */ +/* EC Sysfs */ + +/* Fn lock */ +static int gaokun_ec_get_fn_lock(struct gaokun_ec *ec, bool *on) +{ + /* GFRS */ + u8 ec_req[] = MKREQ(0x02, EC_FN_LOCK_READ, 0); + int ret; + u8 state; + + ret = gaokun_ec_read_byte(ec, ec_req, &state); + if (ret) + return ret; + + if (state == EC_FN_LOCK_ON) + *on = true; + else if (state == EC_FN_LOCK_OFF) + *on = false; + else + return -EIO; + + return 0; +} + +static int gaokun_ec_set_fn_lock(struct gaokun_ec *ec, bool on) +{ + /* SFRS */ + u8 ec_req[] = MKREQ(0x02, EC_FN_LOCK_WRITE, 1, + on ? EC_FN_LOCK_ON : EC_FN_LOCK_OFF); + + return gaokun_ec_write(ec, ec_req); +} + +static ssize_t fn_lock_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct gaokun_ec *ec = dev_get_drvdata(dev); + bool on; + int ret; + + ret = gaokun_ec_get_fn_lock(ec, &on); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", on); +} + +static ssize_t fn_lock_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct gaokun_ec *ec = dev_get_drvdata(dev); + bool on; + int ret; + + if (kstrtobool(buf, &on)) + return -EINVAL; + + ret = gaokun_ec_set_fn_lock(ec, on); + if (ret) + return ret; + + return size; +} + +static DEVICE_ATTR_RW(fn_lock); + +static struct attribute *gaokun_ec_attrs[] = { + &dev_attr_fn_lock.attr, + NULL, +}; +ATTRIBUTE_GROUPS(gaokun_ec); + +/* -------------------------------------------------------------------------- */ +/* Thermal Zone HwMon */ + +/* Range from 0 to 0x2C, partially valid */ +static const u8 temp_reg[] = { + 0x05, 0x07, 0x08, 0x0E, 0x0F, 0x12, 0x15, 0x1E, + 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2A +}; + +static int gaokun_ec_get_temp(struct gaokun_ec *ec, u8 idx, long *temp) +{ + /* GTMP */ + u8 ec_req[] = MKREQ(0x02, EC_TEMP_REG, 1, temp_reg[idx]); + u8 ec_resp[] = MKRESP(sizeof(__le16)); + __le16 *tmp; + int ret; + + ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp); + if (ret) + return ret; + + tmp = (__le16 *)extr_resp_shallow(ec_resp); + *temp = le16_to_cpu(*tmp) * 100; /* convert to HwMon's unit */ + + return 0; +} + +static umode_t +gaokun_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + return type == hwmon_temp ? 0444 : 0; +} + +static int +gaokun_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct gaokun_ec *ec = dev_get_drvdata(dev); + + if (type == hwmon_temp) + return gaokun_ec_get_temp(ec, channel, val); + + return -EINVAL; +} + +static const struct hwmon_ops gaokun_ec_hwmon_ops = { + .is_visible = gaokun_ec_hwmon_is_visible, + .read = gaokun_ec_hwmon_read, +}; + +static u32 gaokun_ec_temp_config[] = { + [0 ... ARRAY_SIZE(temp_reg) - 1] = HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info gaokun_ec_temp = { + .type = hwmon_temp, + .config = gaokun_ec_temp_config, +}; + +static const struct hwmon_channel_info * const gaokun_ec_hwmon_info[] = { + &gaokun_ec_temp, + NULL +}; + +static const struct hwmon_chip_info gaokun_ec_hwmon_chip_info = { + .ops = &gaokun_ec_hwmon_ops, + .info = gaokun_ec_hwmon_info, +}; + +/* -------------------------------------------------------------------------- */ +/* Modern Standby */ + +static int gaokun_ec_suspend(struct device *dev) +{ + struct gaokun_ec *ec = dev_get_drvdata(dev); + u8 ec_req[] = MKREQ(0x02, EC_STANDBY_REG, 1, EC_STANDBY_ENTER); + int ret; + + if (ec->suspended) + return 0; + + ret = gaokun_ec_write(ec, ec_req); + if (ret) + return ret; + + ec->suspended = true; + + return 0; +} + +static int gaokun_ec_resume(struct device *dev) +{ + struct gaokun_ec *ec = dev_get_drvdata(dev); + u8 ec_req[] = MKREQ(0x02, EC_STANDBY_REG, 1, EC_STANDBY_EXIT); + int ret; + int i; + + if (!ec->suspended) + return 0; + + for (i = 0; i < 3; ++i) { + ret = gaokun_ec_write(ec, ec_req); + if (ret == 0) + break; + + msleep(100); /* EC need time to resume */ + }; + + ec->suspended = false; + + return 0; +} + +static void gaokun_aux_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + kfree(adev); +} + +static void gaokun_aux_remove(void *data) +{ + struct auxiliary_device *adev = data; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +static int gaokun_aux_init(struct device *parent, const char *name, + struct gaokun_ec *ec) +{ + struct auxiliary_device *adev; + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + + adev->name = name; + adev->id = 0; + adev->dev.parent = parent; + adev->dev.release = gaokun_aux_release; + adev->dev.platform_data = ec; + /* Allow aux devices to access parent's DT nodes directly */ + device_set_of_node_from_dev(&adev->dev, parent); + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ret; + } + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(parent, gaokun_aux_remove, adev); +} + +/* -------------------------------------------------------------------------- */ +/* EC */ + +static irqreturn_t gaokun_ec_irq_handler(int irq, void *data) +{ + struct gaokun_ec *ec = data; + u8 ec_req[] = MKREQ(EC_EVENT, EC_QUERY, 0); + u8 status, id; + int ret; + + ret = gaokun_ec_read_byte(ec, ec_req, &id); + if (ret) + return IRQ_HANDLED; + + switch (id) { + case 0x0: /* No event */ + break; + + case EC_EVENT_LID: + gaokun_ec_psy_read_byte(ec, EC_LID_STATE, &status); + status &= EC_LID_OPEN; + input_report_switch(ec->idev, SW_LID, !status); + input_sync(ec->idev); + break; + + default: + blocking_notifier_call_chain(&ec->notifier_list, id, ec); + } + + return IRQ_HANDLED; +} + +static int gaokun_ec_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gaokun_ec *ec; + int ret; + + ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL); + if (!ec) + return -ENOMEM; + + ret = devm_mutex_init(dev, &ec->lock); + if (ret) + return ret; + + ec->client = client; + i2c_set_clientdata(client, ec); + BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list); + + /* Lid switch */ + ec->idev = devm_input_allocate_device(dev); + if (!ec->idev) + return -ENOMEM; + + ec->idev->name = "LID"; + ec->idev->phys = "gaokun-ec/input0"; + input_set_capability(ec->idev, EV_SW, SW_LID); + + ret = input_register_device(ec->idev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register input device\n"); + + ret = gaokun_aux_init(dev, GAOKUN_DEV_PSY, ec); + if (ret) + return ret; + + ret = gaokun_aux_init(dev, GAOKUN_DEV_UCSI, ec); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + gaokun_ec_irq_handler, IRQF_ONESHOT, + dev_name(dev), ec); + if (ret) + return dev_err_probe(dev, ret, "Failed to request IRQ\n"); + + ec->hwmon_dev = devm_hwmon_device_register_with_info(dev, "gaokun_ec_hwmon", + ec, &gaokun_ec_hwmon_chip_info, NULL); + if (IS_ERR(ec->hwmon_dev)) + return dev_err_probe(dev, PTR_ERR(ec->hwmon_dev), + "Failed to register hwmon device\n"); + + return 0; +} + +static const struct i2c_device_id gaokun_ec_id[] = { + { "gaokun-ec", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, gaokun_ec_id); + +static const struct of_device_id gaokun_ec_of_match[] = { + { .compatible = "huawei,gaokun3-ec", }, + { } +}; +MODULE_DEVICE_TABLE(of, gaokun_ec_of_match); + +static const struct dev_pm_ops gaokun_ec_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(gaokun_ec_suspend, gaokun_ec_resume) +}; + +static struct i2c_driver gaokun_ec_driver = { + .driver = { + .name = "gaokun-ec", + .of_match_table = gaokun_ec_of_match, + .pm = &gaokun_ec_pm_ops, + .dev_groups = gaokun_ec_groups, + }, + .probe = gaokun_ec_probe, + .id_table = gaokun_ec_id, +}; +module_i2c_driver(gaokun_ec_driver); + +MODULE_DESCRIPTION("HUAWEI Matebook E Go EC driver"); +MODULE_AUTHOR("Pengyu Luo "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/huawei-gaokun-ec.h b/include/linux/platform_data/huawei-gaokun-ec.h new file mode 100644 index 000000000000..faa15d315128 --- /dev/null +++ b/include/linux/platform_data/huawei-gaokun-ec.h @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Huawei Matebook E Go Embedded Controller + * + * Copyright (C) 2024-2025 Pengyu Luo + */ + +#ifndef __HUAWEI_GAOKUN_EC_H__ +#define __HUAWEI_GAOKUN_EC_H__ + +#define GAOKUN_UCSI_CCI_SIZE 4 +#define GAOKUN_UCSI_MSGI_SIZE 16 +#define GAOKUN_UCSI_READ_SIZE (GAOKUN_UCSI_CCI_SIZE + GAOKUN_UCSI_MSGI_SIZE) +#define GAOKUN_UCSI_WRITE_SIZE 24 /* 8B CTRL, 16B MSGO */ + +#define GAOKUN_UCSI_NO_PORT_UPDATE (-1) + +#define GAOKUN_SMART_CHARGE_DATA_SIZE 4 /* mode, delay, start, end */ + +/* -------------------------------------------------------------------------- */ + +struct gaokun_ec; +struct gaokun_ucsi_reg; +struct notifier_block; + +#define GAOKUN_MOD_NAME "huawei_gaokun_ec" +#define GAOKUN_DEV_PSY "psy" +#define GAOKUN_DEV_UCSI "ucsi" + +/* -------------------------------------------------------------------------- */ +/* Common API */ + +int gaokun_ec_register_notify(struct gaokun_ec *ec, + struct notifier_block *nb); +void gaokun_ec_unregister_notify(struct gaokun_ec *ec, + struct notifier_block *nb); + +int gaokun_ec_read(struct gaokun_ec *ec, const u8 *req, + size_t resp_len, u8 *resp); +int gaokun_ec_write(struct gaokun_ec *ec, const u8 *req); +int gaokun_ec_read_byte(struct gaokun_ec *ec, const u8 *req, u8 *byte); + +/* -------------------------------------------------------------------------- */ +/* API for PSY */ + +int gaokun_ec_psy_multi_read(struct gaokun_ec *ec, u8 reg, + size_t resp_len, u8 *resp); + +static inline int gaokun_ec_psy_read_byte(struct gaokun_ec *ec, + u8 reg, u8 *byte) +{ + return gaokun_ec_psy_multi_read(ec, reg, sizeof(*byte), byte); +} + +static inline int gaokun_ec_psy_read_word(struct gaokun_ec *ec, + u8 reg, u16 *word) +{ + return gaokun_ec_psy_multi_read(ec, reg, sizeof(*word), (u8 *)word); +} + +int gaokun_ec_psy_get_smart_charge(struct gaokun_ec *ec, + u8 resp[GAOKUN_SMART_CHARGE_DATA_SIZE]); +int gaokun_ec_psy_set_smart_charge(struct gaokun_ec *ec, + const u8 req[GAOKUN_SMART_CHARGE_DATA_SIZE]); + +int gaokun_ec_psy_get_smart_charge_enable(struct gaokun_ec *ec, bool *on); +int gaokun_ec_psy_set_smart_charge_enable(struct gaokun_ec *ec, bool on); + +/* -------------------------------------------------------------------------- */ +/* API for UCSI */ + +int gaokun_ec_ucsi_read(struct gaokun_ec *ec, u8 resp[GAOKUN_UCSI_READ_SIZE]); +int gaokun_ec_ucsi_write(struct gaokun_ec *ec, + const u8 req[GAOKUN_UCSI_WRITE_SIZE]); + +int gaokun_ec_ucsi_get_reg(struct gaokun_ec *ec, struct gaokun_ucsi_reg *ureg); +int gaokun_ec_ucsi_pan_ack(struct gaokun_ec *ec, int port_id); + +#endif /* __HUAWEI_GAOKUN_EC_H__ */ -- 2.51.0 From 0b6d8f9d2df78116afb159df05bbccf13a51b758 Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sat, 15 Feb 2025 02:06:56 +0800 Subject: [PATCH 06/16] arm64: dts: qcom: gaokun3: Add Embedded Controller node MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The Embedded Controller in the Huawei Matebook E Go is accessible on &i2c15 and provides battery and adapter status, port orientation status, as well as HPD event notifications for two USB Type-C port, etc. Add the EC to the device tree and describe the relationship among the type-c connectors, role switches, orientation switches and the QMP combo PHY. Signed-off-by: Pengyu Luo Reviewed-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20250214180656.28599-4-mitltlatltl@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../boot/dts/qcom/sc8280xp-huawei-gaokun3.dts | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts index 09b95f89ee58..1667c7157057 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts @@ -28,6 +28,7 @@ aliases { i2c4 = &i2c4; + i2c15 = &i2c15; serial1 = &uart2; }; @@ -216,6 +217,40 @@ }; }; + usb0-sbu-mux { + compatible = "pericom,pi3usb102", "gpio-sbu-mux"; + + select-gpios = <&tlmm 164 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&usb0_sbu_default>; + pinctrl-names = "default"; + + orientation-switch; + + port { + usb0_sbu_mux: endpoint { + remote-endpoint = <&ucsi0_sbu>; + }; + }; + }; + + usb1-sbu-mux { + compatible = "pericom,pi3usb102", "gpio-sbu-mux"; + + select-gpios = <&tlmm 47 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&usb1_sbu_default>; + pinctrl-names = "default"; + + orientation-switch; + + port { + usb1_sbu_mux: endpoint { + remote-endpoint = <&ucsi1_sbu>; + }; + }; + }; + wcn6855-pmu { compatible = "qcom,wcn6855-pmu"; @@ -584,6 +619,97 @@ }; +&i2c15 { + clock-frequency = <400000>; + + pinctrl-0 = <&i2c15_default>; + pinctrl-names = "default"; + + status = "okay"; + + embedded-controller@38 { + compatible = "huawei,gaokun3-ec"; + reg = <0x38>; + + interrupts-extended = <&tlmm 107 IRQ_TYPE_LEVEL_LOW>; + + #address-cells = <1>; + #size-cells = <0>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + ucsi0_hs_in: endpoint { + remote-endpoint = <&usb_0_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + ucsi0_ss_in: endpoint { + remote-endpoint = <&usb_0_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + ucsi0_sbu: endpoint { + remote-endpoint = <&usb0_sbu_mux>; + }; + }; + }; + }; + + connector@1 { + compatible = "usb-c-connector"; + reg = <1>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + ucsi1_hs_in: endpoint { + remote-endpoint = <&usb_1_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + ucsi1_ss_in: endpoint { + remote-endpoint = <&usb_1_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + ucsi1_sbu: endpoint { + remote-endpoint = <&usb1_sbu_mux>; + }; + }; + }; + }; + }; +}; + &mdss0 { status = "okay"; }; @@ -1004,6 +1130,10 @@ dr_mode = "host"; }; +&usb_0_dwc3_hs { + remote-endpoint = <&ucsi0_hs_in>; +}; + &usb_0_hsphy { vdda-pll-supply = <&vreg_l9d>; vdda18-supply = <&vreg_l1c>; @@ -1025,6 +1155,10 @@ remote-endpoint = <&mdss0_dp0_out>; }; +&usb_0_qmpphy_out { + remote-endpoint = <&ucsi0_ss_in>; +}; + &usb_1 { status = "okay"; }; @@ -1033,6 +1167,10 @@ dr_mode = "host"; }; +&usb_1_dwc3_hs { + remote-endpoint = <&ucsi1_hs_in>; +}; + &usb_1_hsphy { vdda-pll-supply = <&vreg_l4b>; vdda18-supply = <&vreg_l1c>; @@ -1054,6 +1192,10 @@ remote-endpoint = <&mdss0_dp1_out>; }; +&usb_1_qmpphy_out { + remote-endpoint = <&ucsi1_ss_in>; +}; + &usb_2 { status = "okay"; }; @@ -1177,6 +1319,13 @@ bias-disable; }; + i2c15_default: i2c15-default-state { + pins = "gpio36", "gpio37"; + function = "qup15"; + drive-strength = <2>; + bias-pull-up; + }; + mode_pin_active: mode-pin-state { pins = "gpio26"; function = "gpio"; @@ -1301,6 +1450,20 @@ }; }; + usb0_sbu_default: usb0-sbu-state { + pins = "gpio164"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + usb1_sbu_default: usb1-sbu-state { + pins = "gpio47"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + wcd_default: wcd-default-state { reset-pins { pins = "gpio106"; -- 2.51.0 From baa18b6ed4200aff869e78f2650123572b6a9ad6 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 26 Feb 2025 00:21:27 +0100 Subject: [PATCH 07/16] platform/x86: hp-bioscfg: Replace deprecated strncpy() with strscpy() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Since kstrtol() requires a NUL-terminated string as input and strncpy() is deprecated for NUL-terminated destination buffers, use strscpy() instead. Compile-tested only. Link: https://github.com/KSPP/linux/issues/90 Cc: linux-hardening@vger.kernel.org Signed-off-by: Thorsten Blum Link: https://lore.kernel.org/r/20250225232126.356274-2-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c index 63c78b4d8258..13237890fc92 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c @@ -445,7 +445,7 @@ int hp_convert_hexstr_to_str(const char *input, u32 input_len, char **str, int * return -ENOMEM; for (i = 0; i < input_len; i += 5) { - strncpy(tmp, input + i, strlen(tmp)); + strscpy(tmp, input + i); if (kstrtol(tmp, 16, &ch) == 0) { // escape char if (ch == '\\' || -- 2.51.0 From 61250669eaa9fd480e0f1c3cec107924b6a06b46 Mon Sep 17 00:00:00 2001 From: Jackie Dong Date: Sat, 22 Feb 2025 19:45:31 +0800 Subject: [PATCH 08/16] platform/x86:lenovo-wmi-hotkey-utilities.c: Support for mic and audio mute LEDs MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Implement Lenovo utility data WMI calls needed to make LEDs work on Ideapads that support this GUID. This enables the mic and audio LEDs to be updated correctly. Tested on below samples. ThinkBook 13X Gen4 IMH ThinkBook 14 G6 ABP ThinkBook 16p Gen4-21J8 ThinkBook 16 G8 IRL ThinkBook 16 G7+ ASP Signed-off-by: Jackie Dong Suggested-by: Mark Pearson Reviewed-by: Kurt Borja Reviewed-by: Ilpo Järvinen Reviewed-by: Armin Wolf Link: https://lore.kernel.org/r/20250222114532.4105-1-xy-jackie@139.com Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 6 + drivers/platform/x86/Kconfig | 11 + drivers/platform/x86/Makefile | 1 + .../x86/lenovo-wmi-hotkey-utilities.c | 212 ++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 drivers/platform/x86/lenovo-wmi-hotkey-utilities.c diff --git a/MAINTAINERS b/MAINTAINERS index 0669b6433b1a..1afd30d00aec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13158,6 +13158,12 @@ S: Maintained W: http://legousb.sourceforge.net/ F: drivers/usb/misc/legousbtower.c +LENOVO WMI HOTKEY UTILITIES DRIVER +M: Jackie Dong +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/lenovo-wmi-hotkey-utilities.c + LETSKETCH HID TABLET DRIVER M: Hans de Goede L: linux-input@vger.kernel.org diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 7e20a58861eb..43407e76476b 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -475,6 +475,17 @@ config IDEAPAD_LAPTOP This is a driver for Lenovo IdeaPad netbooks contains drivers for rfkill switch, hotkey, fan control and backlight control. +config LENOVO_WMI_HOTKEY_UTILITIES + tristate "Lenovo Hotkey Utility WMI extras driver" + depends on ACPI_WMI + select NEW_LEDS + select LEDS_CLASS + imply IDEAPAD_LAPTOP + help + This driver provides WMI support for Lenovo customized hotkeys function, + such as LED control for audio/mic mute event for Ideapad, YOGA, XiaoXin, + Gaming, ThinkBook and so on. + config LENOVO_YMC tristate "Lenovo Yoga Tablet Mode Control" depends on ACPI_WMI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 5f6307246e69..650dfbebb6c8 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o # IBM Thinkpad and Lenovo obj-$(CONFIG_IBM_RTL) += ibm_rtl.o obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o +obj-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += lenovo-wmi-hotkey-utilities.o obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o diff --git a/drivers/platform/x86/lenovo-wmi-hotkey-utilities.c b/drivers/platform/x86/lenovo-wmi-hotkey-utilities.c new file mode 100644 index 000000000000..89153afd7015 --- /dev/null +++ b/drivers/platform/x86/lenovo-wmi-hotkey-utilities.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Lenovo Super Hotkey Utility WMI extras driver for Ideapad laptop + * + * Copyright (C) 2025 Lenovo + */ + +#include +#include +#include +#include +#include +#include + +/* Lenovo Super Hotkey WMI GUIDs */ +#define LUD_WMI_METHOD_GUID "CE6C0974-0407-4F50-88BA-4FC3B6559AD8" + +/* Lenovo Utility Data WMI method_id */ +#define WMI_LUD_GET_SUPPORT 1 +#define WMI_LUD_SET_FEATURE 2 + +#define WMI_LUD_GET_MICMUTE_LED_VER 20 +#define WMI_LUD_GET_AUDIOMUTE_LED_VER 26 + +#define WMI_LUD_SUPPORT_MICMUTE_LED_VER 25 +#define WMI_LUD_SUPPORT_AUDIOMUTE_LED_VER 27 + +/* Input parameters to mute/unmute audio LED and Mic LED */ +struct wmi_led_args { + u8 id; + u8 subid; + u16 value; +}; + +/* Values of input parameters to SetFeature of audio LED and Mic LED */ +enum hotkey_set_feature { + MIC_MUTE_LED_ON = 1, + MIC_MUTE_LED_OFF = 2, + AUDIO_MUTE_LED_ON = 4, + AUDIO_MUTE_LED_OFF = 5, +}; + +#define LSH_ACPI_LED_MAX 2 + +struct lenovo_super_hotkey_wmi_private { + struct led_classdev cdev[LSH_ACPI_LED_MAX]; + struct wmi_device *led_wdev; +}; + +enum mute_led_type { + MIC_MUTE, + AUDIO_MUTE, +}; + +static int lsh_wmi_mute_led_set(enum mute_led_type led_type, struct led_classdev *led_cdev, + enum led_brightness brightness) + +{ + struct lenovo_super_hotkey_wmi_private *wpriv = container_of(led_cdev, + struct lenovo_super_hotkey_wmi_private, cdev[led_type]); + struct wmi_led_args led_arg = {0, 0, 0}; + struct acpi_buffer input; + acpi_status status; + + switch (led_type) { + case MIC_MUTE: + led_arg.id = brightness == LED_ON ? MIC_MUTE_LED_ON : MIC_MUTE_LED_OFF; + break; + case AUDIO_MUTE: + led_arg.id = brightness == LED_ON ? AUDIO_MUTE_LED_ON : AUDIO_MUTE_LED_OFF; + break; + default: + return -EINVAL; + } + + input.length = sizeof(led_arg); + input.pointer = &led_arg; + status = wmidev_evaluate_method(wpriv->led_wdev, 0, WMI_LUD_SET_FEATURE, &input, NULL); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} + +static int lsh_wmi_audiomute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) + +{ + return lsh_wmi_mute_led_set(AUDIO_MUTE, led_cdev, brightness); +} + +static int lsh_wmi_micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + return lsh_wmi_mute_led_set(MIC_MUTE, led_cdev, brightness); +} + +static int lenovo_super_hotkey_wmi_led_init(enum mute_led_type led_type, struct device *dev) +{ + struct lenovo_super_hotkey_wmi_private *wpriv = dev_get_drvdata(dev); + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer input; + int led_version, err = 0; + unsigned int wmiarg; + acpi_status status; + + switch (led_type) { + case MIC_MUTE: + wmiarg = WMI_LUD_GET_MICMUTE_LED_VER; + break; + case AUDIO_MUTE: + wmiarg = WMI_LUD_GET_AUDIOMUTE_LED_VER; + break; + default: + return -EINVAL; + } + + input.length = sizeof(wmiarg); + input.pointer = &wmiarg; + status = wmidev_evaluate_method(wpriv->led_wdev, 0, WMI_LUD_GET_SUPPORT, &input, &output); + if (ACPI_FAILURE(status)) + return -EIO; + + union acpi_object *obj __free(kfree) = output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + led_version = obj->integer.value; + else + return -EIO; + + wpriv->cdev[led_type].max_brightness = LED_ON; + wpriv->cdev[led_type].flags = LED_CORE_SUSPENDRESUME; + + switch (led_type) { + case MIC_MUTE: + if (led_version != WMI_LUD_SUPPORT_MICMUTE_LED_VER) + return -EIO; + + wpriv->cdev[led_type].name = "platform::micmute"; + wpriv->cdev[led_type].brightness_set_blocking = &lsh_wmi_micmute_led_set; + wpriv->cdev[led_type].default_trigger = "audio-micmute"; + break; + case AUDIO_MUTE: + if (led_version != WMI_LUD_SUPPORT_AUDIOMUTE_LED_VER) + return -EIO; + + wpriv->cdev[led_type].name = "platform::mute"; + wpriv->cdev[led_type].brightness_set_blocking = &lsh_wmi_audiomute_led_set; + wpriv->cdev[led_type].default_trigger = "audio-mute"; + break; + default: + dev_err(dev, "Unknown LED type %d\n", led_type); + return -EINVAL; + } + + err = devm_led_classdev_register(dev, &wpriv->cdev[led_type]); + if (err < 0) { + dev_err(dev, "Could not register mute LED %d : %d\n", led_type, err); + return err; + } + return 0; +} + +static int lenovo_super_hotkey_wmi_leds_setup(struct device *dev) +{ + int err; + + err = lenovo_super_hotkey_wmi_led_init(MIC_MUTE, dev); + if (err) + return err; + + err = lenovo_super_hotkey_wmi_led_init(AUDIO_MUTE, dev); + if (err) + return err; + + return 0; +} + +static int lenovo_super_hotkey_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct lenovo_super_hotkey_wmi_private *wpriv; + + wpriv = devm_kzalloc(&wdev->dev, sizeof(*wpriv), GFP_KERNEL); + if (!wpriv) + return -ENOMEM; + + dev_set_drvdata(&wdev->dev, wpriv); + wpriv->led_wdev = wdev; + return lenovo_super_hotkey_wmi_leds_setup(&wdev->dev); +} + +static const struct wmi_device_id lenovo_super_hotkey_wmi_id_table[] = { + { LUD_WMI_METHOD_GUID, NULL }, /* Utility data */ + { } +}; + +MODULE_DEVICE_TABLE(wmi, lenovo_super_hotkey_wmi_id_table); + +static struct wmi_driver lenovo_wmi_hotkey_utilities_driver = { + .driver = { + .name = "lenovo_wmi_hotkey_utilities", + .probe_type = PROBE_PREFER_ASYNCHRONOUS + }, + .id_table = lenovo_super_hotkey_wmi_id_table, + .probe = lenovo_super_hotkey_wmi_probe, + .no_singleton = true, +}; + +module_wmi_driver(lenovo_wmi_hotkey_utilities_driver); + +MODULE_AUTHOR("Jackie Dong "); +MODULE_DESCRIPTION("Lenovo Super Hotkey Utility WMI extras driver"); +MODULE_LICENSE("GPL"); -- 2.51.0 From 886ca11a0c70efe5627a18557062e8a44370d78f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 4 Mar 2025 18:06:38 +0200 Subject: [PATCH 09/16] platform/x86: lenovo-yoga-tab2-pro-1380-fastcharger: Make symbol static MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Sparse reports: lenovo-yoga-tab2-pro-1380-fastcharger.c:222:29: warning: symbol 'yt2_1380_fc_serdev_driver' was not declared. Should it be static? Fix that by making the symbol static. Fixes: b2ed33e8d486a ("platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver") Reviewed-by: Mario Limonciello Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20250304160639.4295-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Reviewed-by: Ilpo Järvinen --- drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c index a96b215cd2c5..25933cd018d1 100644 --- a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c +++ b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c @@ -219,7 +219,7 @@ static int yt2_1380_fc_serdev_probe(struct serdev_device *serdev) return 0; } -struct serdev_device_driver yt2_1380_fc_serdev_driver = { +static struct serdev_device_driver yt2_1380_fc_serdev_driver = { .probe = yt2_1380_fc_serdev_probe, .driver = { .name = KBUILD_MODNAME, -- 2.51.0 From 4878e0b14c3e31a87ab147bd2dae443394cb5a2c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 4 Mar 2025 18:06:39 +0200 Subject: [PATCH 10/16] platform/x86: dell-uart-backlight: Make dell_uart_bl_serdev_driver static MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Sparse reports: dell-uart-backlight.c:328:29: warning: symbol 'dell_uart_bl_serdev_driver' was not declared. Should it be static? Fix it by making the symbol static. Fixes: 484bae9e4d6ac ("platform/x86: Add new Dell UART backlight driver") Reviewed-by: Mario Limonciello Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20250304160639.4295-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Reviewed-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-uart-backlight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c index 50002ef13d5d..8f868f845350 100644 --- a/drivers/platform/x86/dell/dell-uart-backlight.c +++ b/drivers/platform/x86/dell/dell-uart-backlight.c @@ -325,7 +325,7 @@ static int dell_uart_bl_serdev_probe(struct serdev_device *serdev) return PTR_ERR_OR_ZERO(dell_bl->bl); } -struct serdev_device_driver dell_uart_bl_serdev_driver = { +static struct serdev_device_driver dell_uart_bl_serdev_driver = { .probe = dell_uart_bl_serdev_probe, .driver = { .name = KBUILD_MODNAME, -- 2.51.0 From 7bb84ca9b8d0cfff76a6108636aec1a5a8cdcd49 Mon Sep 17 00:00:00 2001 From: Joshua Grisham Date: Mon, 3 Mar 2025 14:24:32 +0100 Subject: [PATCH 11/16] platform/x86: samsung-galaxybook: Fix block_recording not supported logic MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Fixes logic error when block_recording is not supported but the fw attr was being added anyway (reported by GitHub user bbregeault). Tested myself on a Samsung Galaxy Book2 Pro (has block_recording) and by bbregeault on a Galaxy Book2 Business (does not have block_recording). Fixes: 56f529ce4370 ("platform/x86: samsung-galaxybook: Add samsung-galaxybook driver") Signed-off-by: Joshua Grisham Reviewed-by: Armin Wolf Link: https://lore.kernel.org/r/20250303132433.348653-1-josh@joshuagrisham.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/samsung-galaxybook.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/samsung-galaxybook.c b/drivers/platform/x86/samsung-galaxybook.c index de1ed2dc6cca..5878a351993e 100644 --- a/drivers/platform/x86/samsung-galaxybook.c +++ b/drivers/platform/x86/samsung-galaxybook.c @@ -1100,11 +1100,13 @@ static int galaxybook_fw_attrs_init(struct samsung_galaxybook *galaxybook) } err = galaxybook_block_recording_init(galaxybook); - if (!err) - galaxybook->has_block_recording = true; - else if (err != GB_NOT_SUPPORTED) + if (err == GB_NOT_SUPPORTED) + return 0; + else if (err) return err; + galaxybook->has_block_recording = true; + return galaxybook_fw_attr_init(galaxybook, GB_ATTR_BLOCK_RECORDING, &block_recording_acpi_get, -- 2.51.0 From e57eabe2fb044950e6ffdfe01803895043dec0b7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 5 Mar 2025 11:31:46 +0100 Subject: [PATCH 12/16] platform/x86: thinkpad_acpi: check the return value of devm_mutex_init() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit devm_mutex_init() can fail so check its return value. Fixes: 38b9ab80db31 ("platform/x86: thinkpad_acpi: Move subdriver initialization to tpacpi_pdriver's probe.") Signed-off-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20250305103146.53221-1-brgl@bgdev.pl Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/thinkpad_acpi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a7e82157bd67..4a581a6d6677 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -11849,7 +11849,9 @@ static int __init tpacpi_pdriver_probe(struct platform_device *pdev) { int ret; - devm_mutex_init(&pdev->dev, &tpacpi_inputdev_send_mutex); + ret = devm_mutex_init(&pdev->dev, &tpacpi_inputdev_send_mutex); + if (ret) + return ret; tpacpi_inputdev = devm_input_allocate_device(&pdev->dev); if (!tpacpi_inputdev) -- 2.51.0 From 7a248294a3145bc65eb0d8980a0a8edbb1b92db4 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 5 Mar 2025 06:30:07 +0100 Subject: [PATCH 13/16] platform/x86: dell-ddv: Fix temperature calculation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit On the Dell Inspiron 3505 the battery temperature is always 0.1 degrees larger than the temperature show inside the OEM application. Emulate this behaviour to avoid showing strange looking values like 29.1 degrees. Fixes: 0331b1b0ba653 ("platform/x86: dell-ddv: Fix temperature scaling") Signed-off-by: Armin Wolf Reviewed-by: Sebastian Reichel Link: https://lore.kernel.org/r/20250305053009.378609-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-ddv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index e75cd6e1efe6..ab5f7d3ab824 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -665,8 +665,10 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char if (ret < 0) return ret; - /* Use 2731 instead of 2731.5 to avoid unnecessary rounding */ - return sysfs_emit(buf, "%d\n", value - 2731); + /* Use 2732 instead of 2731.5 to avoid unnecessary rounding and to emulate + * the behaviour of the OEM application which seems to round down the result. + */ + return sysfs_emit(buf, "%d\n", value - 2732); } static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf) -- 2.51.0 From 8dc3f0161e35d6ceb12de4a70cbed593e5b0583f Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 5 Mar 2025 06:30:08 +0100 Subject: [PATCH 14/16] platform/x86: dell-ddv: Use devm_battery_hook_register MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Use devm_battery_hook_register() instead of manually calling devm_add_action_or_reset() to simplify the code. Signed-off-by: Armin Wolf Reviewed-by: Sebastian Reichel Link: https://lore.kernel.org/r/20250305053009.378609-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-ddv.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index ab5f7d3ab824..811cddab57fc 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -732,13 +732,6 @@ static int dell_wmi_ddv_remove_battery(struct power_supply *battery, struct acpi return 0; } -static void dell_wmi_ddv_battery_remove(void *data) -{ - struct acpi_battery_hook *hook = data; - - battery_hook_unregister(hook); -} - static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data) { data->hook.name = "Dell DDV Battery Extension"; @@ -755,9 +748,7 @@ static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data) data->eppid_attr.attr.mode = 0444; data->eppid_attr.show = eppid_show; - battery_hook_register(&data->hook); - - return devm_add_action_or_reset(&data->wdev->dev, dell_wmi_ddv_battery_remove, &data->hook); + return devm_battery_hook_register(&data->wdev->dev, &data->hook); } static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method method) -- 2.51.0 From 99923a0df7852311fa3d01eaddb430c958780143 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 5 Mar 2025 06:30:09 +0100 Subject: [PATCH 15/16] platform/x86: dell-ddv: Use the power supply extension mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Use the power supply extension mechanism for registering the battery temperature properties so that they can show up in the hwmon device associated with the ACPI battery. Signed-off-by: Armin Wolf Reviewed-by: Sebastian Reichel Link: https://lore.kernel.org/r/20250305053009.378609-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-ddv.c | 75 ++++++++++++++---------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index 811cddab57fc..f27739da380f 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -104,7 +104,6 @@ struct dell_wmi_ddv_sensors { struct dell_wmi_ddv_data { struct acpi_battery_hook hook; - struct device_attribute temp_attr; struct device_attribute eppid_attr; struct dell_wmi_ddv_sensors fans; struct dell_wmi_ddv_sensors temps; @@ -651,26 +650,6 @@ static int dell_wmi_ddv_battery_index(struct acpi_device *acpi_dev, u32 *index) return kstrtou32(uid_str, 10, index); } -static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, temp_attr); - u32 index, value; - int ret; - - ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index); - if (ret < 0) - return ret; - - ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_TEMPERATURE, index, &value); - if (ret < 0) - return ret; - - /* Use 2732 instead of 2731.5 to avoid unnecessary rounding and to emulate - * the behaviour of the OEM application which seems to round down the result. - */ - return sysfs_emit(buf, "%d\n", value - 2732); -} - static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr); @@ -697,6 +676,46 @@ static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, cha return ret; } +static int dell_wmi_ddv_get_property(struct power_supply *psy, const struct power_supply_ext *ext, + void *drvdata, enum power_supply_property psp, + union power_supply_propval *val) +{ + struct dell_wmi_ddv_data *data = drvdata; + u32 index, value; + int ret; + + ret = dell_wmi_ddv_battery_index(to_acpi_device(psy->dev.parent), &index); + if (ret < 0) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_TEMP: + ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_TEMPERATURE, index, + &value); + if (ret < 0) + return ret; + + /* Use 2732 instead of 2731.5 to avoid unnecessary rounding and to emulate + * the behaviour of the OEM application which seems to round down the result. + */ + val->intval = value - 2732; + return 0; + default: + return -EINVAL; + } +} + +static const enum power_supply_property dell_wmi_ddv_properties[] = { + POWER_SUPPLY_PROP_TEMP, +}; + +static const struct power_supply_ext dell_wmi_ddv_extension = { + .name = DRIVER_NAME, + .properties = dell_wmi_ddv_properties, + .num_properties = ARRAY_SIZE(dell_wmi_ddv_properties), + .get_property = dell_wmi_ddv_get_property, +}; + static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) { struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook); @@ -708,13 +727,14 @@ static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_ba if (ret < 0) return 0; - ret = device_create_file(&battery->dev, &data->temp_attr); + ret = device_create_file(&battery->dev, &data->eppid_attr); if (ret < 0) return ret; - ret = device_create_file(&battery->dev, &data->eppid_attr); + ret = power_supply_register_extension(battery, &dell_wmi_ddv_extension, &data->wdev->dev, + data); if (ret < 0) { - device_remove_file(&battery->dev, &data->temp_attr); + device_remove_file(&battery->dev, &data->eppid_attr); return ret; } @@ -726,8 +746,8 @@ static int dell_wmi_ddv_remove_battery(struct power_supply *battery, struct acpi { struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook); - device_remove_file(&battery->dev, &data->temp_attr); device_remove_file(&battery->dev, &data->eppid_attr); + power_supply_unregister_extension(battery, &dell_wmi_ddv_extension); return 0; } @@ -738,11 +758,6 @@ static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data) data->hook.add_battery = dell_wmi_ddv_add_battery; data->hook.remove_battery = dell_wmi_ddv_remove_battery; - sysfs_attr_init(&data->temp_attr.attr); - data->temp_attr.attr.name = "temp"; - data->temp_attr.attr.mode = 0444; - data->temp_attr.show = temp_show; - sysfs_attr_init(&data->eppid_attr.attr); data->eppid_attr.attr.name = "eppid"; data->eppid_attr.attr.mode = 0444; -- 2.51.0 From 351f3d9a05504a5c6d6be39eee8e17fbafa89df7 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 5 Mar 2025 19:56:12 +0530 Subject: [PATCH 16/16] platform/x86/amd/pmc: Notify user when platform does not support s0ix transition MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Some of the AMD platforms do not support modern standby, so when such CPU ID is detected, a warning message will be displayed to the user. Reviewed-by: Mario Limonciello Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20250305142615.410178-1-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 3 ++- drivers/platform/x86/amd/pmc/pmc.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index e6124498b195..c7c7afb8a431 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -808,6 +808,7 @@ static const struct pci_device_id pmc_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SHP) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M60H_ROOT) }, { } @@ -832,7 +833,7 @@ static int amd_pmc_probe(struct platform_device *pdev) dev->cpu_id = rdev->device; - if (dev->cpu_id == AMD_CPU_ID_SP) { + if (dev->cpu_id == AMD_CPU_ID_SP || dev->cpu_id == AMD_CPU_ID_SHP) { dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n"); err = -ENODEV; goto err_pci_dev_put; diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index f43f0253b0f5..8f39988ce7a3 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -79,6 +79,7 @@ void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); #define AMD_CPU_ID_CB 0x14D8 #define AMD_CPU_ID_PS 0x14E8 #define AMD_CPU_ID_SP 0x14A4 +#define AMD_CPU_ID_SHP 0x153A #define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 #define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 #define PCI_DEVICE_ID_AMD_MP2_STB 0x172c -- 2.51.0