From patchwork Sun May 5 20:56:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= X-Patchwork-Id: 794883 Received: from todd.t-8ch.de (todd.t-8ch.de [159.69.126.157]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B682A757E1; Sun, 5 May 2024 20:57:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=159.69.126.157 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714942631; cv=none; b=MA2kVbEBoSXSHXzSfpOlC9UG4OwHmNqbk68Gh9enoMiVteSUvzPntxBny5/+CBFFwcNDiNFUavNO0rXc+q9LkZk3z0g+Q0OpZl9B7jAZhczrfmd2IbNHOhdhkt/Vv6llIMtHlNnDYEX4/U24fsEdmIY/kcYKad7S5NBUn0t+10M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714942631; c=relaxed/simple; bh=gA7s7UCZEe5kKL1FVTHg7aew7OnKObi9bZPJ9EmFYE4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YWUwhWL0hCPkPZjoP/rSwM9hXc1AadIqd/cEc1UgAZ5Iu04D/6BULXsly9vNQQHZ/aQOcvb9ORgkt9v8LSrpN6w93YWnNbmt+dv1BDOWeh1t8DWnAhx9i+2d63HcZ7dkSUBwDy0Q7sW4RQ0aKjzvFjgs4k+QtW8Ju4qZjkC4CzQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=weissschuh.net; spf=pass smtp.mailfrom=weissschuh.net; dkim=pass (1024-bit key) header.d=weissschuh.net header.i=@weissschuh.net header.b=rz/9LoT8; arc=none smtp.client-ip=159.69.126.157 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=weissschuh.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=weissschuh.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=weissschuh.net header.i=@weissschuh.net header.b="rz/9LoT8" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1714942625; bh=gA7s7UCZEe5kKL1FVTHg7aew7OnKObi9bZPJ9EmFYE4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=rz/9LoT8OW6q0Bm+Qz8blYkgtL+NdbmB6pPFVSazyyg+uN0Rb2WXGpRz6NM3A7At/ dCrqDaeJIOdz9vKf58h07++jflzrQTgsFnrtaKmTtYXfboMeQBcO7trtIfmNFYIw+n DxLfhKYEsUMnVw2Dp0eWPmy1UheuZG1YpauSz+00= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Sun, 05 May 2024 22:56:34 +0200 Subject: [PATCH 1/2] platform/chrome: cros_ec_framework_laptop: introduce driver Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240505-cros_ec-framework-v1-1-402662d6276b@weissschuh.net> References: <20240505-cros_ec-framework-v1-0-402662d6276b@weissschuh.net> In-Reply-To: <20240505-cros_ec-framework-v1-0-402662d6276b@weissschuh.net> To: Lee Jones , Benson Leung , Guenter Roeck , Tzung-Bi Shih Cc: linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, Mario Limonciello , "Dustin L. Howett" , Sebastian Reichel , linux-pm@vger.kernel.org, =?utf-8?q?Thomas_Wei=C3=9Fschuh?= X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1714942624; l=5459; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=gA7s7UCZEe5kKL1FVTHg7aew7OnKObi9bZPJ9EmFYE4=; b=fzjK631GremBbM3R6RGdZBVXEfXJ2AnE7f8LuuDx7pEOVcD3zYDZwWfqfTe+u7DEFuOTpBCFv R6hVlNV14iuDJYCUC81hwSjuCHosWue/snCEppYnx8KGd3inIfkzRax X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= Framework Laptops are using embedded controller firmware based on the ChromeOS EC project. In addition to the standard upstream commands, some vendor-specific ones are implemented. Add a driver for those custom EC commands. At first, provide an empty driver that only takes care of scaffolding and device binding. Further patches will add functionality to the driver. Signed-off-by: Thomas Weißschuh --- MAINTAINERS | 5 ++ drivers/mfd/cros_ec_dev.c | 13 ++++++ drivers/platform/chrome/Kconfig | 11 +++++ drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_ec_framework_laptop.c | 53 ++++++++++++++++++++++ 5 files changed, 83 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c23fda1aa1f0..60699c289757 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4988,6 +4988,11 @@ S: Maintained F: Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml F: sound/soc/codecs/cros_ec_codec.* +CHROMEOS EC FRAMEWORK LAPTOP EXTENSIONS +M: Thomas Weißschuh +S: Maintained +F: drivers/platform/chrome/cros_ec_framework_laptop.c + CHROMEOS EC SUBDRIVERS M: Benson Leung R: Guenter Roeck diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index a52d59cc2b1e..0a36e77e5039 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -145,6 +145,10 @@ static const struct mfd_cell cros_ec_vbc_cells[] = { { .name = "cros-ec-vbc", } }; +static const struct mfd_cell cros_ec_framework_cells[] = { + { .name = "cros-ec-framework", } +}; + static void cros_ec_class_release(struct device *dev) { kfree(to_cros_ec_dev(dev)); @@ -299,6 +303,15 @@ static int ec_device_probe(struct platform_device *pdev) retval); } + /* The EC on Framework laptops implements some nonstandard features */ + if (dmi_match(DMI_SYS_VENDOR, "Framework")) { + retval = mfd_add_hotplug_devices(ec->dev, cros_ec_framework_cells, + ARRAY_SIZE(cros_ec_framework_cells)); + if (retval) + dev_warn(ec->dev, "failed to add framework laptop devices: %d\n", + retval); + } + return 0; failed: diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 073616b5b5a0..ff69ee226606 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -239,6 +239,17 @@ config CROS_EC_TYPEC To compile this driver as a module, choose M here: the module will be called cros-ec-typec. +config CROS_EC_FRAMEWORK_LAPTOP + tristate "ChromeOS EC Framework Laptop extensions" + depends on MFD_CROS_EC_DEV + default MFD_CROS_EC_DEV + help + If you say Y here, you get support for using Framework Laptop-specific extensions + of the Chrome OS EC. + + To compile this driver as a module, choose M here: the module will be + called cros_ec_framework_laptop. + config CROS_HPS_I2C tristate "ChromeOS HPS device" depends on HID && I2C && PM diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 2dcc6ccc2302..ce6aac620086 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_CHROMEOS_PRIVACY_SCREEN) += chromeos_privacy_screen.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o obj-$(CONFIG_CROS_EC) += cros_ec.o +obj-$(CONFIG_CROS_EC_FRAMEWORK_LAPTOP) += cros_ec_framework_laptop.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_ISHTP) += cros_ec_ishtp.o obj-$(CONFIG_CROS_TYPEC_SWITCH) += cros_typec_switch.o diff --git a/drivers/platform/chrome/cros_ec_framework_laptop.c b/drivers/platform/chrome/cros_ec_framework_laptop.c new file mode 100644 index 000000000000..8a8bf039fa9c --- /dev/null +++ b/drivers/platform/chrome/cros_ec_framework_laptop.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ChromesOS EC driver for Framework laptop + * + * Copyright (C) 2024 Thomas Weißschuh + */ +#include +#include +#include +#include +#include + +#define DRV_NAME "cros-ec-framework" + +struct cros_fwk_priv { + struct cros_ec_device *cros_ec; +}; + +static int cros_fwk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct cros_fwk_priv *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->cros_ec = cros_ec; + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static const struct platform_device_id cros_fwk_id[] = { + { DRV_NAME, 0 }, + { } +}; + +static struct platform_driver cros_fwk_driver = { + .driver.name = DRV_NAME, + .probe = cros_fwk_probe, + .id_table = cros_fwk_id, +}; + +module_platform_driver(cros_fwk_driver); + +MODULE_DEVICE_TABLE(platform, cros_fwk_id); +MODULE_DESCRIPTION("ChromeOS EC Framework Laptop extensions"); +MODULE_AUTHOR("Thomas Weißschuh X-Patchwork-Id: 795138 Received: from todd.t-8ch.de (todd.t-8ch.de [159.69.126.157]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B43CF6DCF5; Sun, 5 May 2024 20:57:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=159.69.126.157 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714942631; cv=none; b=GIMyGHITKA0hIMdICA/3Y8LxNp2iRII0gVs8slli6YN4+f0qJJdJ/yEvAiCNUHlWM3ktO/Lvpo+B/4ZqoJ9dpHXWlNAvEtCsuHqu0HHF5uy9+INCgb69PpBREVTX5PeK3SxlR9CLm4QzjHf5kc6aooRpABqti0pGwFCe0TYyAp4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714942631; c=relaxed/simple; bh=K/HTrL3dabNlERTmoHaPw5pHxjJqcQ+Gsw+F4RjFMP0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tJGwgBd0sYHu4WFT/fLl7Udx25RfulzNn10C0o1NPZXZd0rY2aNXSy6n9e51Gb1sgW2TNOn7QBWyuO4Bk42SjXLg8Q9KhDvEye7l3YLRVQCXQdd5NLjyQCxvE5UgxVHDiPs0jworj1Qd1GDV5QTLq0wTaRxPaI9cpXGxTledh+8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=weissschuh.net; spf=pass smtp.mailfrom=weissschuh.net; dkim=pass (1024-bit key) header.d=weissschuh.net header.i=@weissschuh.net header.b=DlRuY0kr; arc=none smtp.client-ip=159.69.126.157 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=weissschuh.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=weissschuh.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=weissschuh.net header.i=@weissschuh.net header.b="DlRuY0kr" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1714942625; bh=K/HTrL3dabNlERTmoHaPw5pHxjJqcQ+Gsw+F4RjFMP0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=DlRuY0kruFOFHwKvP0N13UyDTwS3AxmEGenfQA7/GcAiNW89WUOVe9y4kf7KGai9y 69enMtp0qr9LDruumOdm93IYltTXiHit3ydLwQ5hutz976WgppFbwJ6wz84Nd403yI xIaD7mVR7Hh4+/ozxkbQi4WvGXrwTLsv7vjVDA4E= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Sun, 05 May 2024 22:56:35 +0200 Subject: [PATCH 2/2] platform/chrome: cros_ec_framework_laptop: implement battery charge thresholds Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240505-cros_ec-framework-v1-2-402662d6276b@weissschuh.net> References: <20240505-cros_ec-framework-v1-0-402662d6276b@weissschuh.net> In-Reply-To: <20240505-cros_ec-framework-v1-0-402662d6276b@weissschuh.net> To: Lee Jones , Benson Leung , Guenter Roeck , Tzung-Bi Shih Cc: linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, Mario Limonciello , "Dustin L. Howett" , Sebastian Reichel , linux-pm@vger.kernel.org, =?utf-8?q?Thomas_Wei=C3=9Fschuh?= X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1714942624; l=4885; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=K/HTrL3dabNlERTmoHaPw5pHxjJqcQ+Gsw+F4RjFMP0=; b=qclKK7GMQPcv8He6mcRlgvXNLOvtIzmaV2pNR/4ps1X7RD1Hy7U0/BTNHLezsHDjaCFfk2tMd Mg7ujhtt64/Ae0d6OGF/53YdoO/4E8BBylcqgUQ9bQZn2a2989pm/pJ X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= Make use of the non-standard EC command FW_EC_CMD_CHARGE_LIMIT_CONTROL to implement the standard sysfs attribute "charge_control_end_threshold". Tested on a Framework 13 AMD with firmware version 3.05. Signed-off-by: Thomas Weißschuh --- drivers/platform/chrome/cros_ec_framework_laptop.c | 120 +++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/drivers/platform/chrome/cros_ec_framework_laptop.c b/drivers/platform/chrome/cros_ec_framework_laptop.c index 8a8bf039fa9c..2693a80df38f 100644 --- a/drivers/platform/chrome/cros_ec_framework_laptop.c +++ b/drivers/platform/chrome/cros_ec_framework_laptop.c @@ -4,18 +4,119 @@ * * Copyright (C) 2024 Thomas Weißschuh */ +#include #include #include #include +#include #include #include #define DRV_NAME "cros-ec-framework" +#define FW_EC_CMD_CHARGE_LIMIT_CONTROL 0x3E03 + +#define FW_EC_CHG_LIMIT_DISABLE BIT(0) +#define FW_EC_CHG_LIMIT_SET BIT(1) +#define FW_EC_CHG_LIMIT_GET BIT(3) +#define FW_EC_CHG_LIMIT_OVERRIDE BIT(7) + struct cros_fwk_priv { struct cros_ec_device *cros_ec; + struct acpi_battery_hook battery_hook; + struct device_attribute end_threshold; +}; + +union cros_fwk_data { + struct { + u8 modes; + u8 max_percentage; + u8 min_percentage; /* not implemented in the EC */ + } __packed req; + struct { + u8 max_percentage; + u8 min_percentage; + } __packed resp; }; +static int cros_fwk_send_cmd(struct cros_ec_device *cros_ec, union cros_fwk_data *arg) +{ + int ret; + struct { + struct cros_ec_command msg; + union cros_fwk_data data; + } __packed buf = { + .msg = { + .version = 0, + .command = FW_EC_CMD_CHARGE_LIMIT_CONTROL, + .insize = sizeof(arg->resp), + .outsize = sizeof(arg->req), + }, + .data.req = arg->req + }; + + ret = cros_ec_cmd_xfer_status(cros_ec, &buf.msg); + if (ret < 0) + return ret; + + arg->resp = buf.data.resp; + + return 0; +} + +static ssize_t charge_control_end_threshold_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cros_fwk_priv *priv = container_of(attr, struct cros_fwk_priv, end_threshold); + union cros_fwk_data arg = { }; + int ret; + + arg.req.modes = FW_EC_CHG_LIMIT_GET; + ret = cros_fwk_send_cmd(priv->cros_ec, &arg); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%u\n", (unsigned int)arg.resp.max_percentage); +} + +static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cros_fwk_priv *priv = container_of(attr, struct cros_fwk_priv, end_threshold); + union cros_fwk_data arg = { }; + int ret, val; + + ret = kstrtoint(buf, 10, &val); + if (ret < 0) + return ret; + if (val < 1 || val > 100) + return -EINVAL; + + arg.req.modes = FW_EC_CHG_LIMIT_SET; + arg.req.max_percentage = val; + ret = cros_fwk_send_cmd(priv->cros_ec, &arg); + if (ret < 0) + return ret; + + return count; +} + +static int cros_fwk_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct cros_fwk_priv *priv = container_of(hook, struct cros_fwk_priv, battery_hook); + + return device_create_file(&battery->dev, &priv->end_threshold); +} + +static int cros_fwk_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct cros_fwk_priv *priv = container_of(hook, struct cros_fwk_priv, battery_hook); + + device_remove_file(&battery->dev, &priv->end_threshold); + + return 0; +} + static int cros_fwk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -28,6 +129,24 @@ static int cros_fwk_probe(struct platform_device *pdev) return -ENOMEM; priv->cros_ec = cros_ec; + priv->end_threshold = (struct device_attribute)__ATTR_RW(charge_control_end_threshold); + + priv->battery_hook.name = dev_name(dev), + priv->battery_hook.add_battery = cros_fwk_add_battery, + priv->battery_hook.remove_battery = cros_fwk_remove_battery, + + battery_hook_register(&priv->battery_hook); + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static int cros_fwk_remove(struct platform_device *pdev) +{ + struct cros_fwk_priv *priv = platform_get_drvdata(pdev); + + battery_hook_unregister(&priv->battery_hook); platform_set_drvdata(pdev, priv); @@ -42,6 +161,7 @@ static const struct platform_device_id cros_fwk_id[] = { static struct platform_driver cros_fwk_driver = { .driver.name = DRV_NAME, .probe = cros_fwk_probe, + .remove = cros_fwk_remove, .id_table = cros_fwk_id, };