From patchwork Thu Feb 17 10:44:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zev Weiss X-Patchwork-Id: 12749751 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 80A4AC43217 for ; Thu, 17 Feb 2022 10:46:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239284AbiBQKq1 (ORCPT ); Thu, 17 Feb 2022 05:46:27 -0500 Received: from mxb-00190b01.gslb.pphosted.com ([23.128.96.19]:60382 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239271AbiBQKqZ (ORCPT ); Thu, 17 Feb 2022 05:46:25 -0500 Received: from thorn.bewilderbeest.net (thorn.bewilderbeest.net [IPv6:2605:2700:0:5::4713:9cab]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B08A2294114 for ; Thu, 17 Feb 2022 02:46:11 -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 E2F3FB10; Thu, 17 Feb 2022 02:46:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bewilderbeest.net; s=thorn; t=1645094770; bh=k2uD7BYduSjipdEcL2Koh9IwXWlXmu34APxpIgOo/M0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ETB8cvRO/ymbjLAvw0i90ILe/BawTknLjnIdlNuS+l0LHklhq+EER5ANvTDfokZBP DJmFLVZTD+CnF1S4y7NOr01Oitch4Ho5x3sFJOM4Vp8sKh5BHxo7+IpRKPwoexW9Sz f4PXv2y5yy/WzxvILlg3isIO2A3PurYgpD73mFto= From: Zev Weiss To: Guenter Roeck , Jean Delvare , linux-hwmon@vger.kernel.org Cc: Zev Weiss , openbmc@lists.ozlabs.org, Greg Kroah-Hartman , linux-kernel@vger.kernel.org, Mark Brown , Liam Girdwood Subject: [PATCH 1/4] hwmon: (pmbus) Add get_error_flags support to regulator ops Date: Thu, 17 Feb 2022 02:44:41 -0800 Message-Id: <20220217104444.7695-2-zev@bewilderbeest.net> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220217104444.7695-1-zev@bewilderbeest.net> References: <20220217104444.7695-1-zev@bewilderbeest.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-hwmon@vger.kernel.org The various PMBus status bits don't all map perfectly to the more limited set of REGULATOR_ERROR_* flags, but there's a reasonable number where they correspond well enough. Signed-off-by: Zev Weiss --- drivers/hwmon/pmbus/pmbus_core.c | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 776ee2237be2..a274e8e524a5 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2417,10 +2417,107 @@ static int pmbus_regulator_disable(struct regulator_dev *rdev) return _pmbus_regulator_on_off(rdev, 0); } +/* A PMBus status flag and the corresponding REGULATOR_ERROR_* flag */ +struct pmbus_regulator_status_assoc { + int pflag, rflag; +}; + +/* PMBus->regulator bit mappings for a PMBus status register */ +struct pmbus_regulator_status_category { + int func; + int reg; + const struct pmbus_regulator_status_assoc *bits; /* zero-terminated */ +}; + +static const struct pmbus_regulator_status_category pmbus_regulator_flag_map[] = { + { + .func = PMBUS_HAVE_STATUS_VOUT, + .reg = PMBUS_STATUS_VOUT, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_VOLTAGE_UV_WARNING, REGULATOR_ERROR_UNDER_VOLTAGE_WARN }, + { PB_VOLTAGE_UV_FAULT, REGULATOR_ERROR_UNDER_VOLTAGE }, + { PB_VOLTAGE_OV_WARNING, REGULATOR_ERROR_OVER_VOLTAGE_WARN }, + { PB_VOLTAGE_OV_FAULT, REGULATOR_ERROR_REGULATION_OUT }, + { }, + }, + }, { + .func = PMBUS_HAVE_STATUS_IOUT, + .reg = PMBUS_STATUS_IOUT, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_IOUT_OC_WARNING, REGULATOR_ERROR_OVER_CURRENT_WARN }, + { PB_IOUT_OC_FAULT, REGULATOR_ERROR_OVER_CURRENT }, + { PB_IOUT_OC_LV_FAULT, REGULATOR_ERROR_OVER_CURRENT }, + { }, + }, + }, { + .func = PMBUS_HAVE_STATUS_TEMP, + .reg = PMBUS_STATUS_TEMPERATURE, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_TEMP_OT_WARNING, REGULATOR_ERROR_OVER_TEMP_WARN }, + { PB_TEMP_OT_FAULT, REGULATOR_ERROR_OVER_TEMP }, + { }, + }, + }, +}; + +static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) +{ + int i, status, statusreg; + const struct pmbus_regulator_status_category *cat; + const struct pmbus_regulator_status_assoc *bit; + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + u8 page = rdev_get_id(rdev); + int func = data->info->func[page]; + + *flags = 0; + + for (i = 0; i < ARRAY_SIZE(pmbus_regulator_flag_map); i++) { + cat = &pmbus_regulator_flag_map[i]; + if (!(func & cat->func)) + continue; + + status = pmbus_read_byte_data(client, page, cat->reg); + if (status < 0) + return status; + + for (bit = cat->bits; bit->pflag; bit++) { + if (status & bit->pflag) + *flags |= bit->rflag; + } + } + + /* + * Map what bits of STATUS_{WORD,BYTE} we can to REGULATOR_ERROR_* + * bits. Some of the other bits are tempting (especially for cases + * where we don't have the relevant PMBUS_HAVE_STATUS_* + * functionality), but there's an unfortunate ambiguity in that + * they're defined as indicating a fault *or* a warning, so we can't + * easily determine whether to report REGULATOR_ERROR_ or + * REGULATOR_ERROR__WARN. + */ + statusreg = data->has_status_word ? PMBUS_STATUS_WORD : PMBUS_STATUS_BYTE; + status = pmbus_get_status(client, page, statusreg); + + if (status < 0) + return status; + + if (pmbus_regulator_is_enabled(rdev) && (status & PB_STATUS_OFF)) + *flags |= REGULATOR_ERROR_FAIL; + if (status & PB_STATUS_IOUT_OC) + *flags |= REGULATOR_ERROR_OVER_CURRENT; + if (status & PB_STATUS_VOUT_OV) + *flags |= REGULATOR_ERROR_REGULATION_OUT; + + return 0; +} + const struct regulator_ops pmbus_regulator_ops = { .enable = pmbus_regulator_enable, .disable = pmbus_regulator_disable, .is_enabled = pmbus_regulator_is_enabled, + .get_error_flags = pmbus_regulator_get_error_flags, }; EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS); From patchwork Thu Feb 17 10:44:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zev Weiss X-Patchwork-Id: 12749749 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 DBB3BC433EF for ; Thu, 17 Feb 2022 10:46:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239282AbiBQKq0 (ORCPT ); Thu, 17 Feb 2022 05:46:26 -0500 Received: from mxb-00190b01.gslb.pphosted.com ([23.128.96.19]:60386 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239272AbiBQKqZ (ORCPT ); Thu, 17 Feb 2022 05:46:25 -0500 Received: from thorn.bewilderbeest.net (thorn.bewilderbeest.net [IPv6:2605:2700:0:5::4713:9cab]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BE014294117 for ; Thu, 17 Feb 2022 02:46:11 -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 3C96CB98; Thu, 17 Feb 2022 02:46:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bewilderbeest.net; s=thorn; t=1645094770; bh=SQvjKarSQnVSfGUf/SQbGhZom4+CH6pepOFEZVx59+0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jciqh0otGJbTdn+SOnWBhWfeeNq0M5QiC59ykWAB5/I4N9BHwU3qvZE+ICRe/6NtL F267ucv4WMc0vCp9PeTavnmTGzo0j0nRi1buS1qFw/vIhAtbRm6/egmp79dtr4aN5v 3XLOOP1UKHTYX8t/hIGm50smYUaB9fwWLYgxJA/0= From: Zev Weiss To: Guenter Roeck , Jean Delvare , linux-hwmon@vger.kernel.org Cc: Zev Weiss , openbmc@lists.ozlabs.org, Greg Kroah-Hartman , linux-kernel@vger.kernel.org, Mark Brown , Liam Girdwood Subject: [PATCH 2/4] hwmon: (pmbus) lm25066: Add regulator support Date: Thu, 17 Feb 2022 02:44:42 -0800 Message-Id: <20220217104444.7695-3-zev@bewilderbeest.net> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220217104444.7695-1-zev@bewilderbeest.net> References: <20220217104444.7695-1-zev@bewilderbeest.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-hwmon@vger.kernel.org While these chips aren't strictly advertised as voltage regulators per se, they (aside from the lm25056) support the PMBus OPERATION command to enable and disable their outputs and have status bits for reporting various warnings and faults, and can hence usefully support all the pmbus_regulator_ops operations. Signed-off-by: Zev Weiss --- drivers/hwmon/pmbus/Kconfig | 7 +++++++ drivers/hwmon/pmbus/lm25066.c | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 41f6cbf96d3b..4acf63fd69b2 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -174,6 +174,13 @@ config SENSORS_LM25066 This driver can also be built as a module. If so, the module will be called lm25066. +config SENSORS_LM25066_REGULATOR + bool "Regulator support for LM25066 and compatibles" + depends on SENSORS_LM25066 && REGULATOR + help + If you say yes here you get regulator support for National + Semiconductor LM25066, LM5064, and LM5066. + config SENSORS_LTC2978 tristate "Linear Technologies LTC2978 and compatibles" help diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 8402b41520eb..09792cd03d9f 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -435,6 +435,12 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, return ret; } +#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR) +static const struct regulator_desc lm25066_reg_desc[] = { + PMBUS_REGULATOR("vout", 0), +}; +#endif + static const struct i2c_device_id lm25066_id[] = { {"lm25056", lm25056}, {"lm25066", lm25066}, @@ -545,6 +551,14 @@ static int lm25066_probe(struct i2c_client *client) info->m[PSC_CURRENT_IN] = info->m[PSC_CURRENT_IN] * shunt / 1000; info->m[PSC_POWER] = info->m[PSC_POWER] * shunt / 1000; +#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR) + /* LM25056 doesn't support OPERATION */ + if (data->id != lm25056) { + info->num_regulators = ARRAY_SIZE(lm25066_reg_desc); + info->reg_desc = lm25066_reg_desc; + } +#endif + return pmbus_do_probe(client, info); } From patchwork Thu Feb 17 10:44:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zev Weiss X-Patchwork-Id: 12749750 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 DAA21C43219 for ; Thu, 17 Feb 2022 10:46:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239271AbiBQKq1 (ORCPT ); Thu, 17 Feb 2022 05:46:27 -0500 Received: from mxb-00190b01.gslb.pphosted.com ([23.128.96.19]:60384 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239274AbiBQKqZ (ORCPT ); Thu, 17 Feb 2022 05:46:25 -0500 Received: from thorn.bewilderbeest.net (thorn.bewilderbeest.net [IPv6:2605:2700:0:5::4713:9cab]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A971294112; Thu, 17 Feb 2022 02:46:11 -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 888F2C55; Thu, 17 Feb 2022 02:46:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bewilderbeest.net; s=thorn; t=1645094770; bh=uUlF8FcwaYf9aGYW+5rRDb+F4fC71b6gQUNd/l6QrtE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Xr18n/QleCAZEULPoAqyKVz/yD9EN5HfdEQ1N3l777roRbcdi6qlN8d9skbhQvcAd /zbvzR0CJgzqAX8xDj7YynY2uZeYgne2OPQ5srXGItCmyJlMKftXlzLY0cpbxAEwLe zR/SQCw4hHw18kfO/AxnVWL4/TUh5NffUZkb1kR0= 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, Guenter Roeck , Jean Delvare , Arnd Bergmann , linux-hwmon@vger.kernel.org, Mark Brown , Liam Girdwood Subject: [PATCH 3/4] dt-bindings: Add power-efuse binding Date: Thu, 17 Feb 2022 02:44:43 -0800 Message-Id: <20220217104444.7695-4-zev@bewilderbeest.net> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220217104444.7695-1-zev@bewilderbeest.net> References: <20220217104444.7695-1-zev@bewilderbeest.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-hwmon@vger.kernel.org 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 | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/power-efuse.yaml diff --git a/Documentation/devicetree/bindings/misc/power-efuse.yaml b/Documentation/devicetree/bindings/misc/power-efuse.yaml new file mode 100644 index 000000000000..cadce15d2ce7 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/power-efuse.yaml @@ -0,0 +1,37 @@ +# 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 + +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 = "power-efuse"; + vout-supply = <&efuse_reg>; + error-flags-cache-ttl-ms = <500>; + }; From patchwork Thu Feb 17 10:44:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zev Weiss X-Patchwork-Id: 12749752 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 3CCE2C433F5 for ; Thu, 17 Feb 2022 10:46:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239320AbiBQKqf (ORCPT ); Thu, 17 Feb 2022 05:46:35 -0500 Received: from mxb-00190b01.gslb.pphosted.com ([23.128.96.19]:60418 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239291AbiBQKq2 (ORCPT ); Thu, 17 Feb 2022 05:46:28 -0500 Received: from thorn.bewilderbeest.net (thorn.bewilderbeest.net [IPv6:2605:2700:0:5::4713:9cab]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5168929410F for ; Thu, 17 Feb 2022 02:46:14 -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 E4264C68; Thu, 17 Feb 2022 02:46:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bewilderbeest.net; s=thorn; t=1645094771; bh=g0HlU7fIoDEJJj/W+s6LzkNdT7A+UPzuBZZId3sSDXI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=L9fQKgQXdpvd4Ip9j+0C0LhtLbwg8gSPdLtL1ZarGAKMGk/gf5w5iQifLnDGXbE1L KZNAJSrg6c77OItmmqajNc3gWgePE6N5dSJxAtGW0sCVlZmNeH8vgqAOm+zcPRBJCd 2K0uwKyT1si3rQ2y4YzP1I7C71BLDZzHu7Ems50U= From: Zev Weiss To: Greg Kroah-Hartman , Arnd Bergmann Cc: Zev Weiss , openbmc@lists.ozlabs.org, linux-kernel@vger.kernel.org, Guenter Roeck , Jean Delvare , linux-hwmon@vger.kernel.org, Mark Brown , Liam Girdwood Subject: [PATCH 4/4] misc: Add power-efuse driver Date: Thu, 17 Feb 2022 02:44:44 -0800 Message-Id: <20220217104444.7695-5-zev@bewilderbeest.net> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220217104444.7695-1-zev@bewilderbeest.net> References: <20220217104444.7695-1-zev@bewilderbeest.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-hwmon@vger.kernel.org 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 --- MAINTAINERS | 5 + drivers/misc/Kconfig | 15 +++ drivers/misc/Makefile | 1 + drivers/misc/power-efuse.c | 221 +++++++++++++++++++++++++++++++++++++ 4 files changed, 242 insertions(+) create mode 100644 drivers/misc/power-efuse.c diff --git a/MAINTAINERS b/MAINTAINERS index fca970a46e77..d1153a0389d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7064,6 +7064,11 @@ S: Orphan W: http://aeschi.ch.eu.org/efs/ F: fs/efs/ +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. +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) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o +obj-$(CONFIG_POWER_EFUSE) += power-efuse.o diff --git a/drivers/misc/power-efuse.c b/drivers/misc/power-efuse.c new file mode 100644 index 000000000000..e974dde57615 --- /dev/null +++ b/drivers/misc/power-efuse.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This module provides a thin wrapper around a regulator device that exposes + * 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 = 0; + mutex_unlock(&efuse->error_flags.lock); +} + +static ssize_t efuse_show_operstate(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct efuse *efuse = dev_get_drvdata(dev); + int status = 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_attribute *attr, + const char *buf, size_t count) +{ + int status, wantstate; + struct efuse *efuse = dev_get_drvdata(dev); + struct regulator *reg = efuse->reg; + + if (sysfs_streq(buf, "on")) + wantstate = 1; + else if (sysfs_streq(buf, "off")) + wantstate = 0; + else + return -EINVAL; + + status = 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 == wantstate) + return count; + + if (wantstate) + status = regulator_enable(reg); + else + status = 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) != 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 = -EIO; + } + + return status ? : count; +} + +static int efuse_update_error_flags(struct efuse *efuse) +{ + int status = 0; + unsigned long cache_expiry; + + mutex_lock(&efuse->error_flags.lock); + + cache_expiry = 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 = regulator_get_error_flags(efuse->reg, &efuse->error_flags.cache); + if (!status) + efuse->error_flags.fetch_time = jiffies; + } + + mutex_unlock(&efuse->error_flags.lock); + + return status; +} + +static DEVICE_ATTR(operstate, 0644, efuse_show_operstate, efuse_set_operstate); + +#define EFUSE_ERROR_ATTR(name, bit) \ + static ssize_t efuse_show_##name(struct device *dev, struct device_attribute *attr, \ + char *buf) \ + { \ + struct efuse *efuse = dev_get_drvdata(dev); \ + int status = 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 *attributes[] = { + &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, +}; + +static const struct attribute_group attr_group = { + .attrs = attributes, +}; + +static int efuse_probe(struct platform_device *pdev) +{ + int status; + struct regulator *reg; + struct efuse *efuse; + u32 cache_ttl_ms; + + reg = devm_regulator_get(&pdev->dev, "vout"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + status = regulator_enable(reg); + if (status) { + dev_err(&pdev->dev, "failed to enable regulator\n"); + return status; + } + + efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); + if (!efuse) + return -ENOMEM; + + efuse->reg = 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 = msecs_to_jiffies(cache_ttl_ms); + + platform_set_drvdata(pdev, efuse); + + return sysfs_create_group(&pdev->dev.kobj, &attr_group); +} + +static int efuse_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &attr_group); + return 0; +} + +static const struct of_device_id efuse_of_match_table[] = { + { .compatible = "power-efuse" }, + { }, +}; + +static struct platform_driver efuse_driver = { + .driver = { + .name = "power-efuse", + .of_match_table = efuse_of_match_table, + }, + .probe = efuse_probe, + .remove = efuse_remove, +}; +module_platform_driver(efuse_driver); + +MODULE_AUTHOR("Zev Weiss "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Power efuse driver");