From nobody Tue Jun 23 09:12:03 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A290BC433F5 for ; Tue, 8 Mar 2022 01:18:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245179AbiCHBTa (ORCPT ); Mon, 7 Mar 2022 20:19:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52360 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234265AbiCHBT3 (ORCPT ); Mon, 7 Mar 2022 20:19:29 -0500 Received: from thorn.bewilderbeest.net (thorn.bewilderbeest.net [IPv6:2605:2700:0:5::4713:9cab]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D60B936E33; Mon, 7 Mar 2022 17:18:31 -0800 (PST) Received: from hatter.bewilderbeest.net (174-21-187-98.tukw.qwest.net [174.21.187.98]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: zev) by thorn.bewilderbeest.net (Postfix) with ESMTPSA id D33867C8; Mon, 7 Mar 2022 17:18:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bewilderbeest.net; s=thorn; t=1646702311; bh=gr2x0McqUm6AttCXHBXmPoHrzYua62hvh5ryRZTRS7Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XAER39zXNAdf6FGdGARa83+dH6IQkjhJlJA5pDlCxAqtAkj3qyC9htgGBj9YRAmXd q0G9hjEiCrEnPGCNW+LeA+RMLUZ3olqIArXDr/pYly7TNTpAbQNqw4oI9ysSmZPgvb jU2bPjXkIenCCshN6p1FHfYKEh2g5LIL0biM3SFM= From: Zev Weiss To: Rob Herring , devicetree@vger.kernel.org Cc: Zev Weiss , openbmc@lists.ozlabs.org, Greg Kroah-Hartman , linux-kernel@vger.kernel.org, Arnd Bergmann , Mark Brown , Liam Girdwood Subject: [PATCH v2 1/2] dt-bindings: Add power-efuse binding Date: Mon, 7 Mar 2022 17:18:09 -0800 Message-Id: <20220308011811.10353-2-zev@bewilderbeest.net> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220308011811.10353-1-zev@bewilderbeest.net> References: <20220308011811.10353-1-zev@bewilderbeest.net> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This can be used to describe a power output supplied by a regulator device that the system controls. Signed-off-by: Zev Weiss --- .../devicetree/bindings/misc/power-efuse.yaml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/power-efuse.yaml diff --git a/Documentation/devicetree/bindings/misc/power-efuse.yaml b/Docu= mentation/devicetree/bindings/misc/power-efuse.yaml new file mode 100644 index 000000000000..5f8f0b21af0e --- /dev/null +++ b/Documentation/devicetree/bindings/misc/power-efuse.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/power-efuse.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic power efuse device + +maintainers: + - Zev Weiss + +description: | + This binding describes a physical power output supplied by a + regulator providing efuse functionality (manual on/off control, and + auto-shutoff if current, voltage, or thermal limits are exceeded). + + These may be found on systems such as "smart" network PDUs, and + typically supply power to devices entirely separate from the system + described by the device-tree by way of an external connector such as + an Open19 power cable: + + https://www.open19.org/marketplace/coolpower-cable-assembly-8ru/ + +properties: + compatible: + const: power-efuse + + vout-supply: + description: + phandle to the regulator providing power for the efuse + + error-flags-cache-ttl-ms: + description: + The number of milliseconds the vout-supply regulator's error + flags should be cached before re-fetching them. + +required: + - compatible + - vout-supply + +additionalProperties: false + +examples: + - | + efuse { + compatible =3D "power-efuse"; + vout-supply =3D <&efuse_reg>; + error-flags-cache-ttl-ms =3D <500>; + }; --=20 2.35.1 From nobody Tue Jun 23 09:12:03 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1C0EAC433EF for ; Tue, 8 Mar 2022 01:18:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344208AbiCHBTd (ORCPT ); Mon, 7 Mar 2022 20:19:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231255AbiCHBT3 (ORCPT ); Mon, 7 Mar 2022 20:19:29 -0500 Received: from thorn.bewilderbeest.net (thorn.bewilderbeest.net [71.19.156.171]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D5E8B36E30 for ; Mon, 7 Mar 2022 17:18:31 -0800 (PST) Received: from hatter.bewilderbeest.net (174-21-187-98.tukw.qwest.net [174.21.187.98]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: zev) by thorn.bewilderbeest.net (Postfix) with ESMTPSA id 2979C7CE; Mon, 7 Mar 2022 17:18:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bewilderbeest.net; s=thorn; t=1646702311; bh=mvigKCjGcfZTQW2nvczMW71EedJFVH4oYc+lexFa/F0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UJ6FQJAywnFNp2a71ajia4P/v1tLBtQi6OVhPf88DGh+fWabZ69Bgx9nY7hjU5f/V vszqJ8m4BTkNjzLeO2SBVU8jJxcxzEEE9qrBvmgVdntk8sWxq4togyi/ISvnR+z/kq a+BzW/oDhZrpBDeTLMsELp4zXZpA0RVO3vS8KA4M= From: Zev Weiss To: Greg Kroah-Hartman , Arnd Bergmann Cc: Zev Weiss , openbmc@lists.ozlabs.org, linux-kernel@vger.kernel.org, Mark Brown , Liam Girdwood Subject: [PATCH v2 2/2] misc: Add power-efuse driver Date: Mon, 7 Mar 2022 17:18:10 -0800 Message-Id: <20220308011811.10353-3-zev@bewilderbeest.net> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220308011811.10353-1-zev@bewilderbeest.net> References: <20220308011811.10353-1-zev@bewilderbeest.net> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This driver provides a sysfs interface to access the on/off state and error flags of a regulator supplying a power output controlled by the system. Signed-off-by: Zev Weiss --- .../ABI/testing/sysfs-driver-power-efuse | 32 +++ MAINTAINERS | 5 + drivers/misc/Kconfig | 15 ++ drivers/misc/Makefile | 1 + drivers/misc/power-efuse.c | 212 ++++++++++++++++++ 5 files changed, 265 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-power-efuse create mode 100644 drivers/misc/power-efuse.c diff --git a/Documentation/ABI/testing/sysfs-driver-power-efuse b/Documenta= tion/ABI/testing/sysfs-driver-power-efuse new file mode 100644 index 000000000000..ff9bb53e012a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-power-efuse @@ -0,0 +1,32 @@ +What: /sys/bus/platform/drivers/power-efuse/\*/operstate +Date: February 2022 +KernelVersion: 5.18 +Contact: Zev Weiss + +What: /sys/bus/platform/drivers/power-efuse/\*/fail +Date: February 2022 +KernelVersion: 5.18 +Contact: Zev Weiss + +What: /sys/bus/platform/drivers/power-efuse/\*/{under_voltage,over_curren= t,regulation_out,over_temp} +Date: February 2022 +KernelVersion: 5.18 +Contact: Zev Weiss + +What: /sys/bus/platform/drivers/power-efuse/\*/{under_voltage,over_curren= t,over_voltage,over_temp}_warn +Date: February 2022 +KernelVersion: 5.18 +Contact: Zev Weiss diff --git a/MAINTAINERS b/MAINTAINERS index 05fd080b82f3..6490b09b43d4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7058,6 +7058,11 @@ S: Orphan W: http://aeschi.ch.eu.org/efs/ F: fs/efs/ =20 +POWER EFUSE DRIVER +M: Zev Weiss +S: Maintained +F: drivers/misc/power-efuse.c + EHEA (IBM pSeries eHEA 10Gb ethernet adapter) DRIVER M: Douglas Miller L: netdev@vger.kernel.org diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0f5a49fc7c9e..45fc3e8ad35d 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -470,6 +470,21 @@ config HISI_HIKEY_USB switching between the dual-role USB-C port and the USB-A host ports using only one USB controller. =20 +config POWER_EFUSE + tristate "Power efuse driver support" + depends on OF && REGULATOR + help + This driver supports a regulator device functioning as a + power efuse, with status bits and an on/off switch available + via sysfs. + + A typical use for this would be for an efuse controlling a + generic power output for supplying power to devices external + to the system running this driver (such as in the management + controller of a "smart" PDU or similar), allowing the + operator to manually turn the output on and off, check if + the efuse has tripped due to overload, etc. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a086197af544..7bd784b89ef8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE) +=3D uacce/ obj-$(CONFIG_XILINX_SDFEC) +=3D xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) +=3D hisi_hikey_usb.o obj-$(CONFIG_HI6421V600_IRQ) +=3D hi6421v600-irq.o +obj-$(CONFIG_POWER_EFUSE) +=3D power-efuse.o diff --git a/drivers/misc/power-efuse.c b/drivers/misc/power-efuse.c new file mode 100644 index 000000000000..e9d2feb668f2 --- /dev/null +++ b/drivers/misc/power-efuse.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This module provides a thin wrapper around a regulator device that expo= ses + * status bits and on/off state via sysfs. + * + * Copyright (C) 2022 Zev Weiss + */ + +#include +#include +#include +#include +#include + +struct efuse { + struct regulator *reg; + struct { + unsigned int cache; + unsigned long ttl; + unsigned long fetch_time; + struct mutex lock; + } error_flags; +}; + +/* Ensure that the next error_flags access fetches them from the device */ +static void efuse_invalidate_error_flags(struct efuse *efuse) +{ + mutex_lock(&efuse->error_flags.lock); + efuse->error_flags.fetch_time =3D 0; + mutex_unlock(&efuse->error_flags.lock); +} + +static ssize_t efuse_show_operstate(struct device *dev, struct device_attr= ibute *attr, + char *buf) +{ + struct efuse *efuse =3D dev_get_drvdata(dev); + int status =3D regulator_is_enabled(efuse->reg); + + if (status < 0) + return status; + + return sysfs_emit(buf, "%s\n", status ? "on" : "off"); +} + +static ssize_t efuse_set_operstate(struct device *dev, struct device_attri= bute *attr, + const char *buf, size_t count) +{ + int status, wantstate; + struct efuse *efuse =3D dev_get_drvdata(dev); + struct regulator *reg =3D efuse->reg; + + if (sysfs_streq(buf, "on")) + wantstate =3D 1; + else if (sysfs_streq(buf, "off")) + wantstate =3D 0; + else + return -EINVAL; + + status =3D regulator_is_enabled(reg); + + /* + * We need to ensure our enable/disable calls don't get imbalanced, so + * bail if we can't determine the current state. + */ + if (status < 0) + return status; + + /* Return early if we're already in the desired state */ + if (!!status =3D=3D wantstate) + return count; + + if (wantstate) + status =3D regulator_enable(reg); + else + status =3D regulator_disable(reg); + + /* + * Toggling operstate can reset latched status flags, so invalidate + * the cached value. + */ + efuse_invalidate_error_flags(efuse); + + if (!status && regulator_is_enabled(reg) !=3D wantstate) { + /* + * We could do + * + * if (!wantstate) + * regulator_force_disable(reg); + * + * here, but it's likely to leave it such that it can't then + * be re-enabled, so we'll just report the error and leave it + * as it is (and hopefully as long as our enable/disable calls + * remain balanced and nobody registers another consumer for + * the same supply we won't end up in this situation anyway). + */ + dev_err(dev, "regulator_%sable() didn't take effect\n", wantstate ? "en"= : "dis"); + status =3D -EIO; + } + + return status ? : count; +} + +static int efuse_update_error_flags(struct efuse *efuse) +{ + int status =3D 0; + unsigned long cache_expiry; + + mutex_lock(&efuse->error_flags.lock); + + cache_expiry =3D efuse->error_flags.fetch_time + efuse->error_flags.ttl; + + if (!efuse->error_flags.ttl || !efuse->error_flags.fetch_time || + time_after(jiffies, cache_expiry)) { + status =3D regulator_get_error_flags(efuse->reg, &efuse->error_flags.cac= he); + if (!status) + efuse->error_flags.fetch_time =3D jiffies; + } + + mutex_unlock(&efuse->error_flags.lock); + + return status; +} + +static DEVICE_ATTR(operstate, 0644, efuse_show_operstate, efuse_set_operst= ate); + +#define EFUSE_ERROR_ATTR(name, bit) \ + static ssize_t efuse_show_##name(struct device *dev, struct device_attrib= ute *attr, \ + char *buf) \ + { = \ + struct efuse *efuse =3D dev_get_drvdata(dev); = \ + int status =3D efuse_update_error_flags(efuse); = \ + if (status) = \ + return status; \ + return sysfs_emit(buf, "%d\n", !!(efuse->error_flags.cache & bit)); = \ + } = \ + static DEVICE_ATTR(name, 0444, efuse_show_##name, NULL) + +EFUSE_ERROR_ATTR(under_voltage, REGULATOR_ERROR_UNDER_VOLTAGE); +EFUSE_ERROR_ATTR(over_current, REGULATOR_ERROR_OVER_CURRENT); +EFUSE_ERROR_ATTR(regulation_out, REGULATOR_ERROR_REGULATION_OUT); +EFUSE_ERROR_ATTR(fail, REGULATOR_ERROR_FAIL); +EFUSE_ERROR_ATTR(over_temp, REGULATOR_ERROR_OVER_TEMP); +EFUSE_ERROR_ATTR(under_voltage_warn, REGULATOR_ERROR_UNDER_VOLTAGE_WARN); +EFUSE_ERROR_ATTR(over_current_warn, REGULATOR_ERROR_OVER_CURRENT_WARN); +EFUSE_ERROR_ATTR(over_voltage_warn, REGULATOR_ERROR_OVER_VOLTAGE_WARN); +EFUSE_ERROR_ATTR(over_temp_warn, REGULATOR_ERROR_OVER_TEMP_WARN); + +static struct attribute *efuse_attrs[] =3D { + &dev_attr_operstate.attr, + &dev_attr_under_voltage.attr, + &dev_attr_over_current.attr, + &dev_attr_regulation_out.attr, + &dev_attr_fail.attr, + &dev_attr_over_temp.attr, + &dev_attr_under_voltage_warn.attr, + &dev_attr_over_current_warn.attr, + &dev_attr_over_voltage_warn.attr, + &dev_attr_over_temp_warn.attr, + NULL, +}; +ATTRIBUTE_GROUPS(efuse); + +static int efuse_probe(struct platform_device *pdev) +{ + int status; + struct regulator *reg; + struct efuse *efuse; + u32 cache_ttl_ms; + + reg =3D devm_regulator_get(&pdev->dev, "vout"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + status =3D regulator_enable(reg); + if (status) { + dev_err(&pdev->dev, "failed to enable regulator\n"); + return status; + } + + efuse =3D devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); + if (!efuse) + return -ENOMEM; + + efuse->reg =3D reg; + mutex_init(&efuse->error_flags.lock); + + if (!of_property_read_u32(pdev->dev.of_node, "error-flags-cache-ttl-ms", = &cache_ttl_ms)) + efuse->error_flags.ttl =3D msecs_to_jiffies(cache_ttl_ms); + + platform_set_drvdata(pdev, efuse); + + return 0; +} + +static const struct of_device_id efuse_of_match_table[] =3D { + { .compatible =3D "power-efuse" }, + { }, +}; + +static struct platform_driver efuse_driver =3D { + .driver =3D { + .name =3D "power-efuse", + .of_match_table =3D efuse_of_match_table, + .dev_groups =3D efuse_groups, + }, + .probe =3D efuse_probe, +}; +module_platform_driver(efuse_driver); + +MODULE_AUTHOR("Zev Weiss "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Power efuse driver"); --=20 2.35.1