]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
platform: arm64: thinkpad-t14s-ec: add system PM hooks
authorSebastian Reichel <sre@kernel.org>
Wed, 19 Nov 2025 00:41:42 +0000 (01:41 +0100)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Mon, 24 Nov 2025 17:05:54 +0000 (19:05 +0200)
Improve support for system suspend. The register information has been
extracted from the ACPI DSDT code handling Windows Modern Standby. I
took over the weird multi-write function from the ACPI DSDT code where
it is called ECWS.

In addition to writing to the 0xE0 register, the ACPI Windows Modern
Standby code also does some changes to the thermal configuration. This
part is not implemented.

After this patch the laptop's power and LID LEDs will switch into the
typical breathing animation when the system is suspended and enabled
normally again after resuming.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
Link: https://patch.msgid.link/20251119-thinkpad-t14s-ec-improvements-v2-3-441219857c02@kernel.org
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
drivers/platform/arm64/lenovo-thinkpad-t14s.c

index 1d5d11adaf32066b2f7a8913bbed131c57aa6194..4bebe7b3b2a8cebd44e0b92d573e2101a7b25006 100644 (file)
 #include <linux/module.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
+#include <linux/pm.h>
 
 #define T14S_EC_CMD_ECRD       0x02
 #define T14S_EC_CMD_ECWR       0x03
 #define T14S_EC_CMD_EVT                0xf0
 
-#define T14S_EC_REG_LED                0x0c
-#define T14S_EC_REG_KBD_BL1    0x0d
-#define T14S_EC_REG_KBD_BL2    0xe1
-#define T14S_EC_KBD_BL1_MASK   GENMASK_U8(7, 6)
-#define T14S_EC_KBD_BL2_MASK   GENMASK_U8(3, 2)
-#define T14S_EC_REG_AUD                0x30
-#define T14S_EC_MIC_MUTE_LED   BIT(5)
-#define T14S_EC_SPK_MUTE_LED   BIT(6)
+#define T14S_EC_REG_LED                                0x0c
+#define T14S_EC_REG_KBD_BL1                    0x0d
+#define T14S_EC_REG_MODERN_STANDBY             0xe0
+#define T14S_EC_MODERN_STANDBY_ENTRY           BIT(1)
+#define T14S_EC_MODERN_STANDBY_EXIT            BIT(0)
+#define T14S_EC_REG_KBD_BL2                    0xe1
+#define T14S_EC_KBD_BL1_MASK                   GENMASK_U8(7, 6)
+#define T14S_EC_KBD_BL2_MASK                   GENMASK_U8(3, 2)
+#define T14S_EC_REG_AUD                                0x30
+#define T14S_EC_MIC_MUTE_LED                   BIT(5)
+#define T14S_EC_SPK_MUTE_LED                   BIT(6)
 
 #define T14S_EC_EVT_NONE                       0x00
 #define T14S_EC_EVT_KEY_FN_4                   0x13
@@ -198,6 +202,14 @@ out:
        return ret;
 }
 
+static void t14s_ec_write_sequence(struct t14s_ec *ec, u8 reg, u8 val, u8 cnt)
+{
+       int i;
+
+       for (i = 0; i < cnt; i++)
+               regmap_write(ec->regmap, reg, val);
+}
+
 static int t14s_led_set_status(struct t14s_ec *ec,
                               struct t14s_ec_led_classdev *led,
                               const enum t14s_ec_led_status_t ledstatus)
@@ -550,6 +562,7 @@ static int t14s_ec_probe(struct i2c_client *client)
                return -ENOMEM;
 
        ec->dev = dev;
+       i2c_set_clientdata(client, ec);
 
        ec->regmap = devm_regmap_init(dev, &t14s_ec_regmap_bus,
                                      ec, &t14s_ec_regmap_config);
@@ -589,6 +602,26 @@ static int t14s_ec_probe(struct i2c_client *client)
        return 0;
 }
 
+static int t14s_ec_suspend(struct device *dev)
+{
+       struct t14s_ec *ec = dev_get_drvdata(dev);
+
+       t14s_ec_write_sequence(ec, T14S_EC_REG_MODERN_STANDBY,
+                              T14S_EC_MODERN_STANDBY_ENTRY, 3);
+
+       return 0;
+}
+
+static int t14s_ec_resume(struct device *dev)
+{
+       struct t14s_ec *ec = dev_get_drvdata(dev);
+
+       t14s_ec_write_sequence(ec, T14S_EC_REG_MODERN_STANDBY,
+                              T14S_EC_MODERN_STANDBY_EXIT, 3);
+
+       return 0;
+}
+
 static const struct of_device_id t14s_ec_of_match[] = {
        { .compatible = "lenovo,thinkpad-t14s-ec" },
        {}
@@ -601,10 +634,15 @@ static const struct i2c_device_id t14s_ec_i2c_id_table[] = {
 };
 MODULE_DEVICE_TABLE(i2c, t14s_ec_i2c_id_table);
 
+static const struct dev_pm_ops t14s_ec_pm_ops = {
+       SYSTEM_SLEEP_PM_OPS(t14s_ec_suspend, t14s_ec_resume)
+};
+
 static struct i2c_driver t14s_ec_i2c_driver = {
        .driver = {
                .name = "thinkpad-t14s-ec",
                .of_match_table = t14s_ec_of_match,
+               .pm = &t14s_ec_pm_ops,
        },
        .probe = t14s_ec_probe,
        .id_table = t14s_ec_i2c_id_table,