From patchwork Tue May 28 20:04:10 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: 800822 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 4589C178398; Tue, 28 May 2024 20:05:15 +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=1716926719; cv=none; b=stAvsiwMeMQ4SrKaEztJux32OhZDn+SlgHkrcGHYFwl5xoN1ExttL5HDNPHGYSN1JqWA5KJzPNu1yvwkHtlCMrpYPsOgztAHD2XVoX4axrwoXbUuz569N65Rp9BhuHmGI2SrT8zARbc0lWqXSMxGUPd3qx4XzAKQaq7CcM4FI4A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716926719; c=relaxed/simple; bh=JbYQHnPT7R8bbx48TFbQXDrU5PRM/0uyA9vkGt576is=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=C9l7g8ScWBOraRJXd3UMj5TvTlNaN0X+TJQiMYOwXhxp6k3xh3T1KCUa+aLN0+bWtB1KdpFWRwO2qrX2xXcrkWct8psbXJ4daoC0eaTfipCoscrIxzajh5S+PRal9bwTy59vKDlyZ6LPFcKggG1Tb+bEKeSBoY6Mmv4SidRf+kQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine 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=bLejnAhw; arc=none smtp.client-ip=159.69.126.157 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine 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="bLejnAhw" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1716926707; bh=JbYQHnPT7R8bbx48TFbQXDrU5PRM/0uyA9vkGt576is=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=bLejnAhw/GmdlkCX4xAaQFhl8q971F5D9s5s2sJfaHP2LYW4EVIsqkszh41oCkAI6 HwRsFZ048LqZmDKk9TdRdG6hjuisKnAV+5Ue/QT0lmqZYDjVku8SgDi2PK3mCV+WJK xpNH0+PMQq7pe18XRGPrATOtH0MAOzr3US3noDlQ= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Tue, 28 May 2024 22:04:10 +0200 Subject: [PATCH v2 1/3] platform/chrome: Update binary interface for EC-based charge control Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240528-cros_ec-charge-control-v2-1-81fb27e1cff4@weissschuh.net> References: <20240528-cros_ec-charge-control-v2-0-81fb27e1cff4@weissschuh.net> In-Reply-To: <20240528-cros_ec-charge-control-v2-0-81fb27e1cff4@weissschuh.net> To: Benson Leung , Guenter Roeck , Sebastian Reichel , Lee Jones , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Cc: chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Mario Limonciello , Dustin Howett , Stephen Horvath , Rajas Paranjpe , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1716926706; l=2691; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=JbYQHnPT7R8bbx48TFbQXDrU5PRM/0uyA9vkGt576is=; b=LjM+FLqiJVMvC8n7q9IfHO2F4e28kIeTc3GEYi4cjWje8j1BOWTHVBIUy8s+td/vLjUkwO/wf RTidBOH6MsWDNekA/woovjb05qm2EtQ4GHn0mnMoK/llBztyUDRsXSM X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= The charge-control command v2/v3 is more featureful than v1, it additionally supports charge thresholds. The definitions were imported from ChromeOS EC commit 32870d602317 ("squirtle: modify motionsense rotation matrix") Signed-off-by: Thomas Weißschuh --- include/linux/platform_data/cros_ec_commands.h | 49 ++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index ecc47d5fe239..c35e534e3c3d 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -3809,16 +3809,61 @@ struct ec_params_i2c_write { * discharge the battery. */ #define EC_CMD_CHARGE_CONTROL 0x0096 -#define EC_VER_CHARGE_CONTROL 1 +#define EC_VER_CHARGE_CONTROL 3 enum ec_charge_control_mode { CHARGE_CONTROL_NORMAL = 0, CHARGE_CONTROL_IDLE, CHARGE_CONTROL_DISCHARGE, + /* Add no more entry below. */ + CHARGE_CONTROL_COUNT, +}; + +#define EC_CHARGE_MODE_TEXT \ + { \ + [CHARGE_CONTROL_NORMAL] = "NORMAL", \ + [CHARGE_CONTROL_IDLE] = "IDLE", \ + [CHARGE_CONTROL_DISCHARGE] = "DISCHARGE", \ + } + +enum ec_charge_control_cmd { + EC_CHARGE_CONTROL_CMD_SET = 0, + EC_CHARGE_CONTROL_CMD_GET, +}; + +enum ec_charge_control_flag { + EC_CHARGE_CONTROL_FLAG_NO_IDLE = BIT(0), }; struct ec_params_charge_control { - uint32_t mode; /* enum charge_control_mode */ + uint32_t mode; /* enum charge_control_mode */ + + /* Below are the fields added in V2. */ + uint8_t cmd; /* enum ec_charge_control_cmd. */ + uint8_t flags; /* enum ec_charge_control_flag (v3+) */ + /* + * Lower and upper thresholds for battery sustainer. This struct isn't + * named to avoid tainting foreign projects' name spaces. + * + * If charge mode is explicitly set (e.g. DISCHARGE), battery sustainer + * will be disabled. To disable battery sustainer, set mode=NORMAL, + * lower=-1, upper=-1. + */ + struct { + int8_t lower; /* Display SoC in percentage. */ + int8_t upper; /* Display SoC in percentage. */ + } sustain_soc; +} __ec_align4; + +/* Added in v2 */ +struct ec_response_charge_control { + uint32_t mode; /* enum charge_control_mode */ + struct { /* Battery sustainer thresholds */ + int8_t lower; + int8_t upper; + } sustain_soc; + uint8_t flags; /* enum ec_charge_control_flag (v3+) */ + uint8_t reserved; } __ec_align4; /*****************************************************************************/ From patchwork Tue May 28 20:04:11 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: 800823 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 443F6178395; Tue, 28 May 2024 20:05:15 +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=1716926718; cv=none; b=BdlDVwHhiYms20iMfSz5wLno6IOwMaVCYpChv4SBWHfQAbJryHHtNBb8VBRUm5ICUy5Qm8z1Yt65dWiPyb+PTHmqtc/ws+2RQV3DYetOEjinuBxklyPJM+5dRVd0YT6I02spJAYU0hAa6jdJjcqHJk8HQo6zlJpIydVV4Ls042U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716926718; c=relaxed/simple; bh=uQv4uZGdRG0J9Ve71RXN8pv6C1y9fhqunXEmY39VaT8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rb2+W457dHL5CB17bv9kHxxHJR1/WmVHge+A9OJRR9DGwi1aYuEYgLmZaSEWZviVMuHeA/txb5RTkBL+BO0VShd+U4kN4h1YQPEZOJtpg7GBw0KOMUPmSZpV/BhdlTMMVbVHCXR29KeRd6+NhhBDYgH9Cet3txAcJl0yT1fA0sE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine 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=uFkSg8vD; arc=none smtp.client-ip=159.69.126.157 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine 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="uFkSg8vD" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1716926707; bh=uQv4uZGdRG0J9Ve71RXN8pv6C1y9fhqunXEmY39VaT8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=uFkSg8vDU6eG0QCZ+VX3EW59jz+YmxmHdOW9MorigfQGurwjbQIJca7VwYPDK/OW3 DKMXV57NnDbXpIvj99g7beqTWMzPiyBZGPpbW1hkpDc6zhp/55RnsxYvESt9u/6I6o 62ePlpB/chr+ge6PfqJIAT2sPn2nKR7fp2hoWfys= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Tue, 28 May 2024 22:04:11 +0200 Subject: [PATCH v2 2/3] power: supply: add ChromeOS EC based charge control driver Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240528-cros_ec-charge-control-v2-2-81fb27e1cff4@weissschuh.net> References: <20240528-cros_ec-charge-control-v2-0-81fb27e1cff4@weissschuh.net> In-Reply-To: <20240528-cros_ec-charge-control-v2-0-81fb27e1cff4@weissschuh.net> To: Benson Leung , Guenter Roeck , Sebastian Reichel , Lee Jones , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Cc: chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Mario Limonciello , Dustin Howett , Stephen Horvath , Rajas Paranjpe , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1716926707; l=14306; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=uQv4uZGdRG0J9Ve71RXN8pv6C1y9fhqunXEmY39VaT8=; b=FsfmsAtmvnDcvvOImW8OxcR3+dJCo4/X2E4CTS2Wc1hpvv0WDPiqUOTHeGid657PltZsvcLCL Z9RhN4t1EV0DtJAtMNAOjE3jilOZ8JKFwkaxByMzzU2q+fThqQTyWMQ X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= The ChromeOS Embedded Controller implements a command to control charge thresholds and behaviour. Use it to implement the standard Linux charge_control_start_threshold, charge_control_end_threshold and charge_behaviour sysfs UAPIs. The driver is designed to be probed via the cros_ec mfd device. Signed-off-by: Thomas Weißschuh --- MAINTAINERS | 6 + drivers/power/supply/Kconfig | 12 + drivers/power/supply/Makefile | 1 + drivers/power/supply/cros_charge-control.c | 353 +++++++++++++++++++++++++++++ 4 files changed, 372 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d6c90161c7bf..fc0fb3827163 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5135,11 +5135,17 @@ S: Maintained F: Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml F: sound/soc/codecs/cros_ec_codec.* +CHROMEOS EC CHARGE CONTROL +M: Thomas Weißschuh +S: Maintained +F: drivers/power/supply/cros_charge-control.c + CHROMEOS EC SUBDRIVERS M: Benson Leung R: Guenter Roeck L: chrome-platform@lists.linux.dev S: Maintained +F: drivers/power/supply/cros_charge-control.c F: drivers/power/supply/cros_usbpd-charger.c N: cros_ec N: cros-ec diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 3e31375491d5..f6321a42aa53 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -860,6 +860,18 @@ config CHARGER_CROS_PCHG the peripheral charge ports from the EC and converts that into power_supply properties. +config CHARGER_CROS_CONTROL + tristate "ChromeOS EC based charge control" + depends on MFD_CROS_EC_DEV + depends on ACPI_BATTERY + default MFD_CROS_EC_DEV + help + Say Y here to enable ChromeOS EC based battery charge control. + This driver can manage charge thresholds and behaviour. + + This driver can also be built as a module. If so, the module will be + called cros_charge-control. + config CHARGER_SC2731 tristate "Spreadtrum SC2731 charger driver" depends on MFD_SC27XX_PMIC || COMPILE_TEST diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 58b567278034..31ca6653a564 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o +obj-$(CONFIG_CHARGER_CROS_CONTROL) += cros_charge-control.o obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o obj-$(CONFIG_CHARGER_CROS_PCHG) += cros_peripheral_charger.o obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c new file mode 100644 index 000000000000..a2bed984163a --- /dev/null +++ b/drivers/power/supply/cros_charge-control.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ChromeOS EC driver for charge control + * + * Copyright (C) 2024 Thomas Weißschuh + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cros-charge-control" + +#define EC_CHARGE_CONTROL_BEHAVIOURS (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \ + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) | \ + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) + +enum CROS_CHCTL_ATTR { + CROS_CHCTL_ATTR_START_THRESHOLD, + CROS_CHCTL_ATTR_END_THRESHOLD, + CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR, + + _CROS_CHCTL_ATTR_COUNT, +}; + +/* + * Semantics of data *returned* from the EC API and Linux sysfs differ + * slightly, also the v1 API can not return any data. + * To match the expected sysfs API, data is never read back from the EC but + * cached in the driver. + * + * Changes to the EC bypassing the driver will not be reflected in sysfs. + * Any change to "charge_behaviour" will synchronize the EC with the driver state. + */ + +struct cros_chctl_priv { + struct cros_ec_device *cros_ec; + struct acpi_battery_hook battery_hook; + struct power_supply *hooked_battery; + u8 cmd_version; + + /* The callbacks need to access this priv structure. + * As neither the struct device nor power_supply are under the drivers + * control, embed the attributes within priv to use with container_of(). + */ + struct device_attribute device_attrs[_CROS_CHCTL_ATTR_COUNT]; + struct attribute *attributes[_CROS_CHCTL_ATTR_COUNT]; + struct attribute_group group; + + enum power_supply_charge_behaviour current_behaviour; + u8 current_start_threshold, current_end_threshold; +}; + +static int cros_chctl_send_charge_control_cmd(struct cros_ec_device *cros_ec, + u8 cmd_version, struct ec_params_charge_control *req) +{ + static const u8 outsizes[] = { + [1] = offsetof(struct ec_params_charge_control, cmd), + [2] = sizeof(struct ec_params_charge_control), + [3] = sizeof(struct ec_params_charge_control), + }; + + struct { + struct cros_ec_command msg; + union { + struct ec_params_charge_control req; + struct ec_response_charge_control resp; + } __packed data; + } __packed buf = { + .msg = { + .command = EC_CMD_CHARGE_CONTROL, + .version = cmd_version, + .insize = 0, + .outsize = outsizes[cmd_version], + }, + .data.req = *req, + }; + + return cros_ec_cmd_xfer_status(cros_ec, &buf.msg); +} + +static int cros_chctl_configure_ec(struct device *dev, struct cros_chctl_priv *priv) +{ + struct ec_params_charge_control req = { }; + + req.cmd = EC_CHARGE_CONTROL_CMD_SET; + + switch (priv->current_behaviour) { + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: + req.mode = CHARGE_CONTROL_NORMAL; + break; + case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: + req.mode = CHARGE_CONTROL_IDLE; + break; + case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: + req.mode = CHARGE_CONTROL_DISCHARGE; + break; + default: + dev_warn_ratelimited(dev, "Unexpected behaviour %d", priv->current_behaviour); + return -EINVAL; + } + + if (priv->current_behaviour == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO && + !(priv->current_start_threshold == 0 && priv->current_end_threshold == 100)) { + req.sustain_soc.lower = priv->current_start_threshold; + req.sustain_soc.upper = priv->current_end_threshold; + } else { + /* Disable charging limits */ + req.sustain_soc.lower = -1; + req.sustain_soc.upper = -1; + } + + return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version, &req); +} + +static struct cros_chctl_priv *chctl_attr_to_priv(struct attribute *attr, enum CROS_CHCTL_ATTR idx) +{ + struct device_attribute *dev_attr = container_of(attr, struct device_attribute, attr); + + return container_of(dev_attr, struct cros_chctl_priv, device_attrs[idx]); +} + +static ssize_t cros_chctl_store_threshold(struct device *dev, struct cros_chctl_priv *priv, + int is_end_threshold, const char *buf, size_t count) +{ + int ret, val; + + ret = kstrtoint(buf, 10, &val); + if (ret < 0) + return ret; + if (val < 0 || val > 100) + return -EINVAL; + + if (is_end_threshold) { + if (val <= priv->current_start_threshold) + return -EINVAL; + priv->current_end_threshold = val; + } else { + if (val >= priv->current_end_threshold) + return -EINVAL; + priv->current_start_threshold = val; + } + + if (priv->current_behaviour == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) { + ret = cros_chctl_configure_ec(dev, priv); + if (ret < 0) + return ret; + } + + return count; +} + +static ssize_t charge_control_start_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cros_chctl_priv *priv = chctl_attr_to_priv(&attr->attr, + CROS_CHCTL_ATTR_START_THRESHOLD); + + return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_start_threshold); +} + +static ssize_t charge_control_start_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cros_chctl_priv *priv = chctl_attr_to_priv(&attr->attr, + CROS_CHCTL_ATTR_START_THRESHOLD); + + return cros_chctl_store_threshold(dev, priv, 0, buf, count); +} + +static ssize_t charge_control_end_threshold_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cros_chctl_priv *priv = chctl_attr_to_priv(&attr->attr, + CROS_CHCTL_ATTR_END_THRESHOLD); + + return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_end_threshold); +} + +static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cros_chctl_priv *priv = chctl_attr_to_priv(&attr->attr, + CROS_CHCTL_ATTR_END_THRESHOLD); + + return cros_chctl_store_threshold(dev, priv, 1, buf, count); +} + +static ssize_t charge_behaviour_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct cros_chctl_priv *priv = chctl_attr_to_priv(&attr->attr, + CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR); + + return power_supply_charge_behaviour_show(dev, EC_CHARGE_CONTROL_BEHAVIOURS, + priv->current_behaviour, buf); +} + +static ssize_t charge_behaviour_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cros_chctl_priv *priv = chctl_attr_to_priv(&attr->attr, + CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR); + enum power_supply_charge_behaviour behaviour; + int ret; + + behaviour = power_supply_charge_behaviour_parse(EC_CHARGE_CONTROL_BEHAVIOURS, buf); + if (behaviour < 0) + return behaviour; + + priv->current_behaviour = behaviour; + + ret = cros_chctl_configure_ec(dev, priv); + if (ret < 0) + return ret; + + return count; +} + +static umode_t cros_chtl_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct cros_chctl_priv *priv = chctl_attr_to_priv(attr, n); + + if (priv->cmd_version < 2) { + if (n == CROS_CHCTL_ATTR_START_THRESHOLD) + return 0; + if (n == CROS_CHCTL_ATTR_END_THRESHOLD) + return 0; + } + + return attr->mode; +} + +static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct cros_chctl_priv *priv = container_of(hook, struct cros_chctl_priv, battery_hook); + + if (priv->hooked_battery) + return 0; + + priv->hooked_battery = battery; + return device_add_group(&battery->dev, &priv->group); +} + +static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct cros_chctl_priv *priv = container_of(hook, struct cros_chctl_priv, battery_hook); + + if (priv->hooked_battery == battery) { + device_remove_group(&battery->dev, &priv->group); + priv->hooked_battery = NULL; + } + + return 0; +} + +static int cros_chctl_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 ec_params_get_cmd_versions req = {}; + struct ec_response_get_cmd_versions resp; + struct cros_chctl_priv *priv; + size_t i; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + req.cmd = EC_CMD_CHARGE_CONTROL; + ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GET_CMD_VERSIONS, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret < 0) + return ret; + else if (resp.version_mask & EC_VER_MASK(3)) + priv->cmd_version = 3; + else if (resp.version_mask & EC_VER_MASK(2)) + priv->cmd_version = 2; + else if (resp.version_mask & EC_VER_MASK(1)) + priv->cmd_version = 1; + else + return -ENODEV; + + dev_dbg(dev, "Command version: %u\n", (unsigned int)priv->cmd_version); + + priv->cros_ec = cros_ec; + priv->device_attrs[CROS_CHCTL_ATTR_START_THRESHOLD] = + (struct device_attribute)__ATTR_RW(charge_control_start_threshold); + priv->device_attrs[CROS_CHCTL_ATTR_END_THRESHOLD] = + (struct device_attribute)__ATTR_RW(charge_control_end_threshold); + priv->device_attrs[CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR] = + (struct device_attribute)__ATTR_RW(charge_behaviour); + for (i = 0; i < _CROS_CHCTL_ATTR_COUNT; i++) + priv->attributes[i] = &priv->device_attrs[i].attr; + priv->attributes[_CROS_CHCTL_ATTR_COUNT] = NULL; + priv->group.is_visible = cros_chtl_attr_is_visible; + priv->group.attrs = priv->attributes; + + priv->battery_hook.name = dev_name(dev), + priv->battery_hook.add_battery = cros_chctl_add_battery, + priv->battery_hook.remove_battery = cros_chctl_remove_battery, + + priv->current_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; + priv->current_start_threshold = 0; + priv->current_end_threshold = 100; + + /* Bring EC into well-known state */ + ret = cros_chctl_configure_ec(dev, priv); + if (ret == -EOPNOTSUPP) + return -ENODEV; + else if (ret < 0) + return ret; + + battery_hook_register(&priv->battery_hook); + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static int cros_chctl_remove(struct platform_device *pdev) +{ + struct cros_chctl_priv *priv = platform_get_drvdata(pdev); + + battery_hook_unregister(&priv->battery_hook); + + return 0; +} + +static const struct platform_device_id cros_chctl_id[] = { + { DRV_NAME, 0 }, + { } +}; + +static struct platform_driver cros_chctl_driver = { + .driver.name = DRV_NAME, + .probe = cros_chctl_probe, + .remove = cros_chctl_remove, + .id_table = cros_chctl_id, +}; +module_platform_driver(cros_chctl_driver); + +MODULE_DEVICE_TABLE(platform, cros_chctl_id); +MODULE_DESCRIPTION("ChromeOS EC charge control"); +MODULE_AUTHOR("Thomas Weißschuh "); +MODULE_LICENSE("GPL"); From patchwork Tue May 28 20:04:12 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: 799718 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 40E2B178392; Tue, 28 May 2024 20:05:15 +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=1716926719; cv=none; b=nIWJjrDgNAnZ5pjL7Vcyq4c8XCk/jVyAWx04StsejM8KuAfiSeNyV6eYK8rU87678BeSa6lprRwhsC4GjDJ1Wku3orrLxNjVta15vX+FltsF61o+DhjvUoKGZ/6fz6yw7LXXh5CowKl3/ADfEbfowQJ0rAfgANuTqFQFTGBQUts= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716926719; c=relaxed/simple; bh=QGClvnWyRt6Z5l3HwVZJ+J+CE2jOyvBkkV1qVUSgp/w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CBc3u5HpfeYe58cfEdT05ipKIB6WCmXzGjtLhsCDnbeW3x7PZrIDqf+Ic+a712woJPFfd5i87ksLmwlCt5iyTfd6HCOb6NUaX/BodM5rSijB3/oiur0DDFGIRo3HJpz45uUr/8BTzFQhd7yWF4Yym/V0cmCb6BFQ00TI+EiCpRU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine 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=DTRI5pjO; arc=none smtp.client-ip=159.69.126.157 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine 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="DTRI5pjO" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1716926707; bh=QGClvnWyRt6Z5l3HwVZJ+J+CE2jOyvBkkV1qVUSgp/w=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=DTRI5pjO5zRfFd/DBEiue5VFoOGeyoTFdJ6NqG89d4d54qp2TCGvB1V++ZdgEsh7l WZLNymt5hhb0ZC+tVXKE9p97zrW0o66yVQkE+7kR1pdgBOQUKeTVqwAfyfB3Kj2wV7 5jxyNy/N3suBab1v6ipNwb0RvzxoWPIY4rUT3pLo= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Tue, 28 May 2024 22:04:12 +0200 Subject: [PATCH v2 3/3] mfd: cros_ec: Register charge control subdevice Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240528-cros_ec-charge-control-v2-3-81fb27e1cff4@weissschuh.net> References: <20240528-cros_ec-charge-control-v2-0-81fb27e1cff4@weissschuh.net> In-Reply-To: <20240528-cros_ec-charge-control-v2-0-81fb27e1cff4@weissschuh.net> To: Benson Leung , Guenter Roeck , Sebastian Reichel , Lee Jones , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Cc: chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Mario Limonciello , Dustin Howett , Stephen Horvath , Rajas Paranjpe , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1716926707; l=645; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=QGClvnWyRt6Z5l3HwVZJ+J+CE2jOyvBkkV1qVUSgp/w=; b=cMubD7FX6aKWHXlIcGRZlCmI3fX7A4Y3afA3lFL79I6hTx78IjO9QMEz4XaTL9FOgg2tRwC7N LUnVCS4e7LoAkUlTkRbgHz1XgEhRInOroBaz6qf7753Q1v8bWqDapCZ X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= Add ChromeOS EC-based charge control as EC subdevice. Signed-off-by: Thomas Weißschuh --- drivers/mfd/cros_ec_dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index a52d59cc2b1e..dab2bec39dcd 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -87,6 +87,7 @@ static const struct mfd_cell cros_ec_sensorhub_cells[] = { }; static const struct mfd_cell cros_usbpd_charger_cells[] = { + { .name = "cros-charge-control", }, { .name = "cros-usbpd-charger", }, { .name = "cros-usbpd-logger", }, };