From patchwork Thu Dec 5 20:46:35 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: 13896020 X-Patchwork-Delegate: rjw@sisk.pl 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 4EAB81B87D0; Thu, 5 Dec 2024 20:47:28 +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=1733431651; cv=none; b=CzFdGKbPuVXbWrtz6OVnHhS/11QYy8AlPk1gxaf0pr7S28qjN6o47EcAZkzEHxKjpFWp/MyEKaHOefWd5ACoxZGen92CAdHSrFSnKrmBV05idPowA/G8HXImUNcYShKrxShda9fotJfOFVANe+VLRQoImSqQUTNOIVChETDq89o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733431651; c=relaxed/simple; bh=Dnz8VEbwpgl6LUJweSEcqDxhkrrzgpj9dI0EqHUFdyQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NJ2vVmGzeu+iwyc5dBHyrv6Jr+/+EOXrsRPKBR23061wetxuXWtfto5he+Gg1kqKzmgGEKZwIhXhw+4UQddWH7Al+nu2vtew/+y+z36G8lPja6IJFPpH4vmu4jAXnOoPEcE1jtCg+bxcYdGi2EoCAkw7ySlddlwr8RtTlPv/w8s= 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=MjJeSWjs; 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="MjJeSWjs" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1733431639; bh=Dnz8VEbwpgl6LUJweSEcqDxhkrrzgpj9dI0EqHUFdyQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=MjJeSWjsp35ZfbJtViZG7/UpRC75ebrAkTTtSX896Qzl52//tN+sYoctpgfkb+zol IksIUhMuaufvYQmDUb6kSJ0AFLiNm8yYWn8EArifNYQvjHm26h7eUIHbg8dRcVKYyz Fks+IEXwaMQlQG9G1+uOLua4DxtVNTlGTsA7poro= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Thu, 05 Dec 2024 21:46:35 +0100 Subject: [PATCH v5 1/4] ACPI: battery: Rename extensions to hook in messages Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241205-power-supply-extensions-v5-1-f0f996db4347@weissschuh.net> References: <20241205-power-supply-extensions-v5-0-f0f996db4347@weissschuh.net> In-Reply-To: <20241205-power-supply-extensions-v5-0-f0f996db4347@weissschuh.net> To: Sebastian Reichel , Armin Wolf , Hans de Goede , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , Benson Leung , Guenter Roeck , "Rafael J. Wysocki" , Len Brown Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-acpi@vger.kernel.org, =?utf-8?q?Tho?= =?utf-8?q?mas_Wei=C3=9Fschuh?= X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1733431638; l=2452; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=Dnz8VEbwpgl6LUJweSEcqDxhkrrzgpj9dI0EqHUFdyQ=; b=iQBWjUiU4xN/ovhrbPPILoVWmCfgac4Z1Yi8MSoBTKcha2/BdkoR+aXQ4LkJwKzWlqRnVBcV4 xQojx8BbXY1C75zIygXMhqmWObAAQqIGduGEY2eBmCqJppn4r7oqhuN X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= This functionality is called "hook" everywhere in the code. For consistency call it the same in the log messages. The power supply subsystem is about to get its own extension functionality. While the two are closely related and will be used together, the current wording leaves room for misinterpretation. Signed-off-by: Thomas Weißschuh Reviewed-by: Sebastian Reichel --- This patch can also be applied independently through the ACPI tree. --- drivers/acpi/battery.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 3d5342f8d7b3ae4e259131f9c7b7144a6206cfdb..6760330a8af55d51c82a0447623c2040ffdaab10 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -717,7 +717,7 @@ static void battery_hook_unregister_unlocked(struct acpi_battery_hook *hook) } list_del_init(&hook->list); - pr_info("extension unregistered: %s\n", hook->name); + pr_info("hook unregistered: %s\n", hook->name); } void battery_hook_unregister(struct acpi_battery_hook *hook) @@ -751,18 +751,18 @@ void battery_hook_register(struct acpi_battery_hook *hook) if (hook->add_battery(battery->bat, hook)) { /* * If a add-battery returns non-zero, - * the registration of the extension has failed, + * the registration of the hook has failed, * and we will not add it to the list of loaded * hooks. */ - pr_err("extension failed to load: %s", hook->name); + pr_err("hook failed to load: %s", hook->name); battery_hook_unregister_unlocked(hook); goto end; } power_supply_changed(battery->bat); } - pr_info("new extension: %s\n", hook->name); + pr_info("new hook: %s\n", hook->name); end: mutex_unlock(&hook_mutex); } @@ -805,10 +805,10 @@ static void battery_hook_add_battery(struct acpi_battery *battery) list_for_each_entry_safe(hook_node, tmp, &battery_hook_list, list) { if (hook_node->add_battery(battery->bat, hook_node)) { /* - * The notification of the extensions has failed, to - * prevent further errors we will unload the extension. + * The notification of the hook has failed, to + * prevent further errors we will unload the hook. */ - pr_err("error in extension, unloading: %s", + pr_err("error in hook, unloading: %s", hook_node->name); battery_hook_unregister_unlocked(hook_node); } From patchwork Thu Dec 5 20:46:36 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: 13896017 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 4EA571B6D02; Thu, 5 Dec 2024 20:47:28 +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=1733431650; cv=none; b=gYkuR57eRMk8Wc4VClUdw1j5oNi0Rp3auifcVrM8PhwrUO61WDbG4Cn5PtaSn1kxmRcwGaQzsXsigKdCtbXleAKKUmQKxrt1u4lvYTfgDVGeq2iizh4YGzKzatBddt713jCSi7hLDG869SCw41LRhWouRM29P8+AJqmg7G26Hrk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733431650; c=relaxed/simple; bh=C6VN2SfSZtFRpEOtEz6Y8lMjuWe2npGhF4x2o/bo8Ac=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=B4mlCPlhbMzWKa3YaPJ95wdTJHXuTXbQgsPg6UUq5QMxKJhA7DCntlU3sETbNVxeiC95kOc5ZOwMIAuB8KLIbktSGf0GcxX/DAp3pav/q0Z2SEnKtbuS225yrmcL3/z8jrYgnWRymgpG4xVHVlEgkw3gp6jyv0MDYNGYmTMfOgw= 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=UNiG5ZmY; 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="UNiG5ZmY" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1733431639; bh=C6VN2SfSZtFRpEOtEz6Y8lMjuWe2npGhF4x2o/bo8Ac=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=UNiG5ZmYonZ85SJ5v1gnES55wHPemHWxBYvQCE4+7Qeohb/mrgB6EnOVYEAA1jyVc w/hVevZtmuVkM0iEMu8ZAEIvAbDXgLNarXnwAm5/Vja5DYvHWmKOcGMxR7dEEIJ9bs B17/22wy8yJxNrgGvljwB53RuhMRWAbEPqK18t7k= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Thu, 05 Dec 2024 21:46:36 +0100 Subject: [PATCH v5 2/4] power: supply: core: implement extension API Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241205-power-supply-extensions-v5-2-f0f996db4347@weissschuh.net> References: <20241205-power-supply-extensions-v5-0-f0f996db4347@weissschuh.net> In-Reply-To: <20241205-power-supply-extensions-v5-0-f0f996db4347@weissschuh.net> To: Sebastian Reichel , Armin Wolf , Hans de Goede , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , Benson Leung , Guenter Roeck , "Rafael J. Wysocki" , Len Brown Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-acpi@vger.kernel.org, =?utf-8?q?Tho?= =?utf-8?q?mas_Wei=C3=9Fschuh?= X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1733431638; l=14289; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=C6VN2SfSZtFRpEOtEz6Y8lMjuWe2npGhF4x2o/bo8Ac=; b=8yDDctZB0Zv4cYQm5C1J3kGSjNX/cTFBFdPdQHUJAnrfwOdgk1y/DpIHG+DO8/Zrp1tGu6ZTn eCgeIMbSJyOCur5nyUltJxDnoGxRdRiF1JDkXd791ayHd3T6HmKXiv+ X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= Various drivers, mostly in platform/x86 extend the ACPI battery driver with additional sysfs attributes to implement more UAPIs than are exposed through ACPI by using various side-channels, like WMI, nonstandard ACPI or EC communication. While the created sysfs attributes look similar to the attributes provided by the powersupply core, there are various deficiencies: * They don't show up in uevent payload. * They can't be queried with the standard in-kernel APIs. * They don't work with triggers. * The extending driver has to reimplement all of the parsing, formatting and sysfs display logic. * Writing a extension driver is completely different from writing a normal power supply driver. This extension API avoids all of these issues. An extension is just a "struct power_supply_ext" with the same kind of callbacks as in a normal "struct power_supply_desc". The API is meant to be used via battery_hook_register(), the same way as the current extensions. Signed-off-by: Thomas Weißschuh --- drivers/power/supply/power_supply.h | 14 +++ drivers/power/supply/power_supply_core.c | 159 ++++++++++++++++++++++++++++-- drivers/power/supply/power_supply_sysfs.c | 26 ++++- include/linux/power_supply.h | 32 ++++++ 4 files changed, 221 insertions(+), 10 deletions(-) diff --git a/drivers/power/supply/power_supply.h b/drivers/power/supply/power_supply.h index 5dabbd895538003096b62d03fdd0201b82b090e6..4c3e602c416cec556173a8eb1a3114c13ded71b7 100644 --- a/drivers/power/supply/power_supply.h +++ b/drivers/power/supply/power_supply.h @@ -9,6 +9,8 @@ * Modified: 2004, Oct Szabolcs Gyurko */ +#include + struct device; struct device_type; struct power_supply; @@ -17,6 +19,18 @@ extern int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp); extern bool power_supply_has_property(struct power_supply *psy, enum power_supply_property psp); +extern bool power_supply_ext_has_property(const struct power_supply_ext *ext, + enum power_supply_property psp); + +struct power_supply_ext_registration { + struct list_head list_head; + const struct power_supply_ext *ext; + void *data; +}; + +#define power_supply_for_each_extension(pos, psy) \ + lockdep_assert_held(&(psy)->extensions_sem); \ + list_for_each_entry(pos, &(psy)->extensions, list_head) #ifdef CONFIG_SYSFS diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 502b07468b93dfb7f5a6c2092588d931a7d015f2..72d9205130d2ec9b7b401eddb7461a58e0be49e5 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -81,6 +81,7 @@ static int __power_supply_changed_work(struct device *dev, void *data) static void power_supply_changed_work(struct work_struct *work) { + int ret; unsigned long flags; struct power_supply *psy = container_of(work, struct power_supply, changed_work); @@ -88,6 +89,16 @@ static void power_supply_changed_work(struct work_struct *work) dev_dbg(&psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags); + + if (unlikely(psy->update_groups)) { + psy->update_groups = false; + spin_unlock_irqrestore(&psy->changed_lock, flags); + ret = sysfs_update_groups(&psy->dev.kobj, power_supply_dev_type.groups); + if (ret) + dev_warn(&psy->dev, "failed to update sysfs groups: %pe\n", ERR_PTR(ret)); + spin_lock_irqsave(&psy->changed_lock, flags); + } + /* * Check 'changed' here to avoid issues due to race between * power_supply_changed() and this routine. In worst case @@ -1196,15 +1207,34 @@ static bool psy_desc_has_property(const struct power_supply_desc *psy_desc, return found; } +bool power_supply_ext_has_property(const struct power_supply_ext *psy_ext, + enum power_supply_property psp) +{ + int i; + + for (i = 0; i < psy_ext->num_properties; i++) + if (psy_ext->properties[i] == psp) + return true; + + return false; +} + bool power_supply_has_property(struct power_supply *psy, enum power_supply_property psp) { + struct power_supply_ext_registration *reg; + if (psy_desc_has_property(psy->desc, psp)) return true; if (power_supply_battery_info_has_prop(psy->battery_info, psp)) return true; + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, psp)) + return true; + } + return false; } @@ -1212,12 +1242,21 @@ int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { + struct power_supply_ext_registration *reg; + if (atomic_read(&psy->use_cnt) <= 0) { if (!psy->initialized) return -EAGAIN; return -ENODEV; } + scoped_guard(rwsem_read, &psy->extensions_sem) { + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, psp)) + return reg->ext->get_property(psy, reg->ext, reg->data, psp, val); + } + } + if (psy_desc_has_property(psy->desc, psp)) return psy->desc->get_property(psy, psp, val); else if (power_supply_battery_info_has_prop(psy->battery_info, psp)) @@ -1231,7 +1270,24 @@ int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { - if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property) + struct power_supply_ext_registration *reg; + + if (atomic_read(&psy->use_cnt) <= 0) + return -ENODEV; + + scoped_guard(rwsem_read, &psy->extensions_sem) { + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, psp)) { + if (reg->ext->set_property) + return reg->ext->set_property(psy, reg->ext, reg->data, + psp, val); + else + return -ENODEV; + } + } + } + + if (!psy->desc->set_property) return -ENODEV; return psy->desc->set_property(psy, psp, val); @@ -1241,7 +1297,22 @@ EXPORT_SYMBOL_GPL(power_supply_set_property); int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { - return psy->desc->property_is_writeable && psy->desc->property_is_writeable(psy, psp); + struct power_supply_ext_registration *reg; + + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, psp)) { + if (reg->ext->property_is_writeable) + return reg->ext->property_is_writeable(psy, reg->ext, + reg->data, psp); + else + return -ENODEV; + } + } + + if (!psy->desc->property_is_writeable) + return -ENODEV; + + return psy->desc->property_is_writeable(psy, psp); } void power_supply_external_power_changed(struct power_supply *psy) @@ -1260,6 +1331,73 @@ int power_supply_powers(struct power_supply *psy, struct device *dev) } EXPORT_SYMBOL_GPL(power_supply_powers); +static int power_supply_update_sysfs_and_hwmon(struct power_supply *psy) +{ + unsigned long flags; + + spin_lock_irqsave(&psy->changed_lock, flags); + psy->update_groups = true; + spin_unlock_irqrestore(&psy->changed_lock, flags); + + power_supply_changed(psy); + + power_supply_remove_hwmon_sysfs(psy); + return power_supply_add_hwmon_sysfs(psy); +} + +int power_supply_register_extension(struct power_supply *psy, const struct power_supply_ext *ext, + void *data) +{ + struct power_supply_ext_registration *reg; + size_t i; + int ret; + + if (!psy || !ext || !ext->properties || !ext->num_properties) + return -EINVAL; + + guard(rwsem_write)(&psy->extensions_sem); + + for (i = 0; i < ext->num_properties; i++) + if (power_supply_has_property(psy, ext->properties[i])) + return -EEXIST; + + reg = kmalloc(sizeof(*reg), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + reg->ext = ext; + reg->data = data; + list_add(®->list_head, &psy->extensions); + + ret = power_supply_update_sysfs_and_hwmon(psy); + if (ret) { + list_del(®->list_head); + kfree(reg); + } + + return ret; +} +EXPORT_SYMBOL_GPL(power_supply_register_extension); + +void power_supply_unregister_extension(struct power_supply *psy, const struct power_supply_ext *ext) +{ + struct power_supply_ext_registration *reg; + + guard(rwsem_write)(&psy->extensions_sem); + + power_supply_for_each_extension(reg, psy) { + if (reg->ext == ext) { + list_del(®->list_head); + kfree(reg); + power_supply_update_sysfs_and_hwmon(psy); + return; + } + } + + dev_warn(&psy->dev, "Trying to unregister invalid extension"); +} +EXPORT_SYMBOL_GPL(power_supply_unregister_extension); + static void power_supply_dev_release(struct device *dev) { struct power_supply *psy = to_power_supply(dev); @@ -1414,6 +1552,9 @@ __power_supply_register(struct device *parent, } spin_lock_init(&psy->changed_lock); + init_rwsem(&psy->extensions_sem); + INIT_LIST_HEAD(&psy->extensions); + rc = device_add(dev); if (rc) goto device_add_failed; @@ -1426,13 +1567,15 @@ __power_supply_register(struct device *parent, if (rc) goto register_thermal_failed; - rc = power_supply_create_triggers(psy); - if (rc) - goto create_triggers_failed; + scoped_guard(rwsem_read, &psy->extensions_sem) { + rc = power_supply_create_triggers(psy); + if (rc) + goto create_triggers_failed; - rc = power_supply_add_hwmon_sysfs(psy); - if (rc) - goto add_hwmon_sysfs_failed; + rc = power_supply_add_hwmon_sysfs(psy); + if (rc) + goto add_hwmon_sysfs_failed; + } /* * Update use_cnt after any uevents (most notably from device_add()). diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 99bfe1f03eb8326d38c4e2831c9670313b42e425..927ddb9d83bb7259809ba695cb9398d1ad654b46 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -268,6 +268,27 @@ static ssize_t power_supply_show_enum_with_available( return count; } +static ssize_t power_supply_show_charge_behaviour(struct device *dev, + struct power_supply *psy, + union power_supply_propval *value, + char *buf) +{ + struct power_supply_ext_registration *reg; + + scoped_guard(rwsem_read, &psy->extensions_sem) { + power_supply_for_each_extension(reg, psy) { + if (power_supply_ext_has_property(reg->ext, + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR)) + return power_supply_charge_behaviour_show(dev, + reg->ext->charge_behaviours, + value->intval, buf); + } + } + + return power_supply_charge_behaviour_show(dev, psy->desc->charge_behaviours, + value->intval, buf); +} + static ssize_t power_supply_format_property(struct device *dev, bool uevent, struct device_attribute *attr, @@ -307,8 +328,7 @@ static ssize_t power_supply_format_property(struct device *dev, case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: if (uevent) /* no possible values in uevents */ goto default_format; - ret = power_supply_charge_behaviour_show(dev, psy->desc->charge_behaviours, - value.intval, buf); + ret = power_supply_show_charge_behaviour(dev, psy, &value, buf); break; case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = sysfs_emit(buf, "%s\n", value.strval); @@ -385,6 +405,8 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj, if (attrno == POWER_SUPPLY_PROP_TYPE) return mode; + guard(rwsem_read)(&psy->extensions_sem); + if (power_supply_has_property(psy, attrno)) { if (power_supply_property_is_writeable(psy, attrno) > 0) mode |= S_IWUSR; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index b98106e1a90f34bce5129317a099f363248342b9..016e44cb3eb5eb7ace01a032661f65a5d81a522f 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include @@ -281,6 +283,27 @@ struct power_supply_desc { int use_for_apm; }; +struct power_supply_ext { + u8 charge_behaviours; + const enum power_supply_property *properties; + size_t num_properties; + + int (*get_property)(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp, + union power_supply_propval *val); + int (*set_property)(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp, + const union power_supply_propval *val); + int (*property_is_writeable)(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp); +}; + struct power_supply { const struct power_supply_desc *desc; @@ -300,10 +323,13 @@ struct power_supply { struct delayed_work deferred_register_work; spinlock_t changed_lock; bool changed; + bool update_groups; bool initialized; bool removing; atomic_t use_cnt; struct power_supply_battery_info *battery_info; + struct rw_semaphore extensions_sem; /* protects "extensions" */ + struct list_head extensions; #ifdef CONFIG_THERMAL struct thermal_zone_device *tzd; struct thermal_cooling_device *tcd; @@ -878,6 +904,12 @@ devm_power_supply_register(struct device *parent, extern void power_supply_unregister(struct power_supply *psy); extern int power_supply_powers(struct power_supply *psy, struct device *dev); +extern int power_supply_register_extension(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data); +extern void power_supply_unregister_extension(struct power_supply *psy, + const struct power_supply_ext *ext); + #define to_power_supply(device) container_of(device, struct power_supply, dev) extern void *power_supply_get_drvdata(struct power_supply *psy); From patchwork Thu Dec 5 20:46:37 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: 13896018 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 AE0EE1B4123; Thu, 5 Dec 2024 20:47:27 +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=1733431650; cv=none; b=ZnmqWh4g9kuu0BA6vkzN5JXbTxLNuj+YTHwaFfiMA5sVqW85sbjCnQYK3/ZrDrGpU7ZBcP6Pl9Z/PEf120/dqQjTe2VFkycGh6STihUMh32mvutF5eYD91xbUsCBZQ9r57b0DlkZXm6HzTxl925MSSt2He1OsBYmy6yG753e2BY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733431650; c=relaxed/simple; bh=u5aND4+bmYLeegxlwVhwulMeUQLI6UQhFWuN0Ipz++Y=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oJCWdo03V99ia6dAatNcPQUTLuHmfrNhU+qvbKBQqOI+QyIsoINSPIHID2QBV8dqMbJFyT48Md+r5HXMl9+OY+rdRwA5+XpPCKNSIPkj4wal2ZU+XPbmizHbR4dGtY8CaL2uGgUz9vLy0TGZMNhL1/7JmYHJ0Zepa+Np9oTM82Y= 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=NzWJ7uIz; 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="NzWJ7uIz" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1733431639; bh=u5aND4+bmYLeegxlwVhwulMeUQLI6UQhFWuN0Ipz++Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=NzWJ7uIzGl5K2d6FT2DHBLQKbZl9u9l9/4jYl6KbxEvyhtaODD8jQW1NueEG2jzPM yKdB8PmobykPcbez6qx16V+ybB9elHqvhfFLoEYyZfQpXP3i4I9zPW7M8JwM4xjEFj 0tkZe8sAfFU5wQ6ASXzhGWMNoKsrSwjOJzm8JOTA= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Thu, 05 Dec 2024 21:46:37 +0100 Subject: [PATCH v5 3/4] power: supply: test-power: implement a power supply extension Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241205-power-supply-extensions-v5-3-f0f996db4347@weissschuh.net> References: <20241205-power-supply-extensions-v5-0-f0f996db4347@weissschuh.net> In-Reply-To: <20241205-power-supply-extensions-v5-0-f0f996db4347@weissschuh.net> To: Sebastian Reichel , Armin Wolf , Hans de Goede , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , Benson Leung , Guenter Roeck , "Rafael J. Wysocki" , Len Brown Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-acpi@vger.kernel.org, =?utf-8?q?Tho?= =?utf-8?q?mas_Wei=C3=9Fschuh?= X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1733431638; l=5423; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=u5aND4+bmYLeegxlwVhwulMeUQLI6UQhFWuN0Ipz++Y=; b=3d1a0ClYPsrF756j0asmMRqi8KoJBQQntAPdwQzs27RZBeAjlwqC7wINhPqW7YosTcw0pQKYl raB87fC3MqQB9cmlfLGlvpZuudOBLdaQGBVdKArZ7LJWLytKI3oRNWa X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= Allow easy testing of the new power supply extension functionality. Signed-off-by: Thomas Weißschuh --- drivers/power/supply/test_power.c | 111 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c index 442ceb7795e1d84e34da2801d228d53fb67e08d9..1e15d3d192ef4040839aa9740b06f5ab78b6b6d3 100644 --- a/drivers/power/supply/test_power.c +++ b/drivers/power/supply/test_power.c @@ -37,6 +37,7 @@ static int battery_charge_counter = -1000; static int battery_current = -1600; static enum power_supply_charge_behaviour battery_charge_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; +static bool battery_hook; static bool module_initialized; @@ -238,6 +239,85 @@ static const struct power_supply_config test_power_configs[] = { }, }; +static int power_supply_ext_manufacture_year = 1234; +static int power_supply_ext_temp_max = 1000; +static const enum power_supply_property power_supply_ext_props[] = { + POWER_SUPPLY_PROP_MANUFACTURE_YEAR, + POWER_SUPPLY_PROP_TEMP_MAX, +}; + +static int power_supply_ext_get_property(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: + val->intval = power_supply_ext_manufacture_year; + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + val->intval = power_supply_ext_temp_max; + break; + default: + return -EINVAL; + } + return 0; +} + +static int power_supply_ext_set_property(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: + power_supply_ext_manufacture_year = val->intval; + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + power_supply_ext_temp_max = val->intval; + break; + default: + return -EINVAL; + } + return 0; +} + +static int power_supply_ext_property_is_writeable(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp) +{ + return true; +} + +static const struct power_supply_ext power_supply_ext = { + .properties = power_supply_ext_props, + .num_properties = ARRAY_SIZE(power_supply_ext_props), + .get_property = power_supply_ext_get_property, + .set_property = power_supply_ext_set_property, + .property_is_writeable = power_supply_ext_property_is_writeable, +}; + +static void test_battery_configure_battery_hook(bool enable) +{ + struct power_supply *psy; + + psy = test_power_supplies[TEST_BATTERY]; + + if (enable) { + if (power_supply_register_extension(psy, &power_supply_ext, NULL)) { + pr_err("registering battery extension failed\n"); + return; + } + } else { + power_supply_unregister_extension(psy, &power_supply_ext); + } + + battery_hook = enable; +} + static int __init test_power_init(void) { int i; @@ -258,6 +338,8 @@ static int __init test_power_init(void) } } + test_battery_configure_battery_hook(true); + module_initialized = true; return 0; failed: @@ -524,6 +606,26 @@ static int param_set_battery_current(const char *key, #define param_get_battery_current param_get_int +static int param_set_battery_hook(const char *key, + const struct kernel_param *kp) +{ + bool old_battery_hook; + int ret; + + old_battery_hook = battery_hook; + + ret = param_set_bool(key, kp); + if (ret) + return ret; + + if (old_battery_hook != battery_hook) + test_battery_configure_battery_hook(battery_hook); + + return 0; +} + +#define param_get_battery_hook param_get_bool + static const struct kernel_param_ops param_ops_ac_online = { .set = param_set_ac_online, .get = param_get_ac_online, @@ -574,6 +676,11 @@ static const struct kernel_param_ops param_ops_battery_current = { .get = param_get_battery_current, }; +static const struct kernel_param_ops param_ops_battery_hook = { + .set = param_set_battery_hook, + .get = param_get_battery_hook, +}; + #define param_check_ac_online(name, p) __param_check(name, p, void); #define param_check_usb_online(name, p) __param_check(name, p, void); #define param_check_battery_status(name, p) __param_check(name, p, void); @@ -584,6 +691,7 @@ static const struct kernel_param_ops param_ops_battery_current = { #define param_check_battery_voltage(name, p) __param_check(name, p, void); #define param_check_battery_charge_counter(name, p) __param_check(name, p, void); #define param_check_battery_current(name, p) __param_check(name, p, void); +#define param_check_battery_hook(name, p) __param_check(name, p, void); module_param(ac_online, ac_online, 0644); @@ -621,6 +729,9 @@ MODULE_PARM_DESC(battery_charge_counter, module_param(battery_current, battery_current, 0644); MODULE_PARM_DESC(battery_current, "battery current (milliampere)"); +module_param(battery_hook, battery_hook, 0644); +MODULE_PARM_DESC(battery_hook, "battery hook"); + MODULE_DESCRIPTION("Power supply driver for testing"); MODULE_AUTHOR("Anton Vorontsov "); MODULE_LICENSE("GPL"); From patchwork Thu Dec 5 20:46:38 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: 13896016 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 4EB0A1B87ED; Thu, 5 Dec 2024 20:47:28 +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=1733431650; cv=none; b=HEisXthfj1ZKUxTHJPirY1ikNAh7C5fQPblOijSGi6gZ7ZeuQXHl46NnSKPGlOqDC6bt+3spbW1DR+EWWUDbzDUPhCFf8TeeJLb4T7nl09wQdeWXM+GDAZOsxmDYZSViNyeuM4yHuA4V/HyrU2ePvanBjceeNwTgxb4acZ8dIGQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733431650; c=relaxed/simple; bh=/qZLsNYmSP0RbFSEcx3FSjSAyL2XYa3JBbP1lSu7fxE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nBSgU+dy712Q0EcBctqBmFsUOk13L6QqS8S9yMKbIcnK60QVWBtn9L09i1At/8vrXD5yXyD2lDct0xioXvAPkkVPHnhQhu27vX++NMzc/BfCvOZKxQ2537+e8upxy83xl0jXj7gXnoZyj/2Fn0oXeBRAl6cWJs+4PAxQkcnrURY= 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=DdzmSGGk; 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="DdzmSGGk" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=weissschuh.net; s=mail; t=1733431639; bh=/qZLsNYmSP0RbFSEcx3FSjSAyL2XYa3JBbP1lSu7fxE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=DdzmSGGkilqJqEtbyjAqprtq6ZUifm+ks0KTuIrRJpUyteZMD/ynT8G5kXFlEX8fb TaEeX/7+/ELwVGN3aTKZZWwYceP34kvXhysxnbvO893Op8Nj06H3z6oztHTEZyWYIx /DFonlNr1YS8ioMlDbOCki1hy8gjFfwoz/BFP6Ow= From: =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Date: Thu, 05 Dec 2024 21:46:38 +0100 Subject: [PATCH v5 4/4] power: supply: cros_charge-control: implement a power supply extension Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241205-power-supply-extensions-v5-4-f0f996db4347@weissschuh.net> References: <20241205-power-supply-extensions-v5-0-f0f996db4347@weissschuh.net> In-Reply-To: <20241205-power-supply-extensions-v5-0-f0f996db4347@weissschuh.net> To: Sebastian Reichel , Armin Wolf , Hans de Goede , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , Benson Leung , Guenter Roeck , "Rafael J. Wysocki" , Len Brown Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-acpi@vger.kernel.org, =?utf-8?q?Tho?= =?utf-8?q?mas_Wei=C3=9Fschuh?= X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1733431638; l=10924; i=linux@weissschuh.net; s=20221212; h=from:subject:message-id; bh=/qZLsNYmSP0RbFSEcx3FSjSAyL2XYa3JBbP1lSu7fxE=; b=Q+SB1KiYOfK5fMUzp22GDlS0+2NwLllyAJmejPsxpFuziv3ry8qMJT5JkfgDG1rL+iSVAUDKZ pZPDF/cLRMVB5OcmVwG5WGyR86AuBvUL7sr48GDXemvtLjrJXZK/St1 X-Developer-Key: i=linux@weissschuh.net; a=ed25519; pk=KcycQgFPX2wGR5azS7RhpBqedglOZVgRPfdFSPB1LNw= Power supply extensions provide an easier mechanism to implement additional properties for existing power supplies. Use that instead of reimplementing the sysfs attributes manually. Signed-off-by: Thomas Weißschuh Reviewed-by: Sebastian Reichel --- drivers/power/supply/cros_charge-control.c | 217 ++++++++++++----------------- 1 file changed, 88 insertions(+), 129 deletions(-) diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c index 17c53591ce197d08d97c94d3d4359a282026dd7d..7f914a65f88f8ea57bf01c334d25d9b8ebaa2de7 100644 --- a/drivers/power/supply/cros_charge-control.c +++ b/drivers/power/supply/cros_charge-control.c @@ -18,13 +18,6 @@ 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. @@ -41,13 +34,7 @@ struct cros_chctl_priv { 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; + const struct power_supply_ext *psy_ext; enum power_supply_charge_behaviour current_behaviour; u8 current_start_threshold, current_end_threshold; @@ -114,124 +101,104 @@ static int cros_chctl_configure_ec(struct cros_chctl_priv *priv) return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version, &req); } -static struct cros_chctl_priv *cros_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 const enum power_supply_property cros_chctl_psy_ext_props[] = { + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, /* has to be first for cros_chctl_psy_ext_v1 */ + POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, + POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, +}; -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) +static int cros_chctl_psy_ext_get_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp, + union power_supply_propval *val) { - int ret, val; + struct cros_chctl_priv *priv = data; - ret = kstrtoint(buf, 10, &val); - if (ret < 0) - return ret; - if (val < 0 || val > 100) + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: + val->intval = priv->current_start_threshold; + return 0; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + val->intval = priv->current_end_threshold; + return 0; + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + val->intval = priv->current_behaviour; + return 0; + default: 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(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 = cros_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 = cros_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 = cros_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 = cros_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 = cros_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) +static int cros_chctl_psy_ext_set_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp, + const union power_supply_propval *val) { - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, - CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR); + struct cros_chctl_priv *priv = data; int ret; - ret = power_supply_charge_behaviour_parse(EC_CHARGE_CONTROL_BEHAVIOURS, buf); - if (ret < 0) - return ret; - - priv->current_behaviour = ret; + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + if (val->intval < 0 || val->intval > 100) + return -EINVAL; - ret = cros_chctl_configure_ec(priv); - if (ret < 0) - return ret; + if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) { + if (val->intval <= priv->current_start_threshold) + return -EINVAL; + priv->current_end_threshold = val->intval; + } else { + if (val->intval >= priv->current_end_threshold) + return -EINVAL; + priv->current_start_threshold = val->intval; + } + + if (priv->current_behaviour == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) { + ret = cros_chctl_configure_ec(priv); + if (ret < 0) + return ret; + } - return count; + return 0; + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + priv->current_behaviour = val->intval; + ret = cros_chctl_configure_ec(priv); + if (ret < 0) + return ret; + return 0; + default: + return -EINVAL; + } } -static umode_t cros_chtl_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +static int cros_chctl_psy_prop_is_writeable(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp) { - struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(attr, n); + return true; +} - if (priv->cmd_version < 2) { - if (n == CROS_CHCTL_ATTR_START_THRESHOLD) - return 0; - if (n == CROS_CHCTL_ATTR_END_THRESHOLD) - return 0; - } +static const struct power_supply_ext cros_chctl_psy_ext = { + .properties = cros_chctl_psy_ext_props, + .num_properties = ARRAY_SIZE(cros_chctl_psy_ext_props), + .charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, + .get_property = cros_chctl_psy_ext_get_prop, + .set_property = cros_chctl_psy_ext_set_prop, + .property_is_writeable = cros_chctl_psy_prop_is_writeable, +}; - return attr->mode; -} +static const struct power_supply_ext cros_chctl_psy_ext_v1 = { + .properties = cros_chctl_psy_ext_props, + .num_properties = 1, + .charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, + .get_property = cros_chctl_psy_ext_get_prop, + .set_property = cros_chctl_psy_ext_set_prop, + .property_is_writeable = cros_chctl_psy_prop_is_writeable, +}; static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) { @@ -241,7 +208,7 @@ static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_batt return 0; priv->hooked_battery = battery; - return device_add_group(&battery->dev, &priv->group); + return power_supply_register_extension(battery, priv->psy_ext, priv); } static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) @@ -249,7 +216,7 @@ static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_b 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); + power_supply_unregister_extension(battery, priv->psy_ext); priv->hooked_battery = NULL; } @@ -275,7 +242,6 @@ static int cros_chctl_probe(struct platform_device *pdev) struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); struct cros_ec_device *cros_ec = ec_dev->ec_dev; struct cros_chctl_priv *priv; - size_t i; int ret; ret = cros_chctl_fwk_charge_control_versions(cros_ec); @@ -305,18 +271,11 @@ static int cros_chctl_probe(struct platform_device *pdev) 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++) { - sysfs_attr_init(&priv->device_attrs[i].attr); - priv->attributes[i] = &priv->device_attrs[i].attr; - } - priv->group.is_visible = cros_chtl_attr_is_visible; - priv->group.attrs = priv->attributes; + + if (priv->cmd_version == 1) + priv->psy_ext = &cros_chctl_psy_ext_v1; + else + priv->psy_ext = &cros_chctl_psy_ext; priv->battery_hook.name = dev_name(dev); priv->battery_hook.add_battery = cros_chctl_add_battery;