Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2217134/?format=api
{ "id": 2217134, "url": "http://patchwork.ozlabs.org/api/patches/2217134/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260327-ltc4283-support-v8-2-471de255d728@analog.com/", "project": { "id": 42, "url": "http://patchwork.ozlabs.org/api/projects/42/?format=api", "name": "Linux GPIO development", "link_name": "linux-gpio", "list_id": "linux-gpio.vger.kernel.org", "list_email": "linux-gpio@vger.kernel.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260327-ltc4283-support-v8-2-471de255d728@analog.com>", "list_archive_url": null, "date": "2026-03-27T17:26:15", "name": "[v8,2/3] hwmon: ltc4283: Add support for the LTC4283 Swap Controller", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "8ec540f7f3d57503ca6a817ad628d179c662113d", "submitter": { "id": 87565, "url": "http://patchwork.ozlabs.org/api/people/87565/?format=api", "name": "Nuno Sá via B4 Relay", "email": "devnull+nuno.sa.analog.com@kernel.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260327-ltc4283-support-v8-2-471de255d728@analog.com/mbox/", "series": [ { "id": 497805, "url": "http://patchwork.ozlabs.org/api/series/497805/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/list/?series=497805", "date": "2026-03-27T17:26:14", "name": "hwmon: Add support for the LTC4283 Hot Swap Controller", "version": 8, "mbox": "http://patchwork.ozlabs.org/series/497805/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2217134/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2217134/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-gpio+bounces-34291-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-gpio@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=Zr4ObiJn;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.232.135.74; helo=sto.lore.kernel.org;\n envelope-from=linux-gpio+bounces-34291-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=\"Zr4ObiJn\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=10.30.226.201" ], "Received": [ "from sto.lore.kernel.org (sto.lore.kernel.org [172.232.135.74])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fj6xN508hz1y1x\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 28 Mar 2026 04:26:00 +1100 (AEDT)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id 5DF7030557BD\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 27 Mar 2026 17:25:38 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id AF3F63F0740;\n\tFri, 27 Mar 2026 17:25:29 +0000 (UTC)", "from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org\n [10.30.226.201])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 42BA13BF69C;\n\tFri, 27 Mar 2026 17:25:29 +0000 (UTC)", "by smtp.kernel.org (Postfix) with ESMTPS id 1A7C3C2BCB1;\n\tFri, 27 Mar 2026 17:25:29 +0000 (UTC)", "from aws-us-west-2-korg-lkml-1.web.codeaurora.org\n (localhost.localdomain [127.0.0.1])\n\tby smtp.lore.kernel.org (Postfix) with ESMTP id 0E2FC10F285F;\n\tFri, 27 Mar 2026 17:25:29 +0000 (UTC)" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1774632329; cv=none;\n b=gluZYlzqtsj/+HgLuVSDXtPPZLfAcyuZlnOabWHmUf+sIR54awxNx4SDBq3p8iNRHxw9Vlj807SMQbvtRn3yD5xFLilXpIYtZCOb6rpy88Fu7DivU3vuTcEkHSRhZqVi10VBDE8hw9kw0N0upIZJgWiJjiHV7B/BHAjQCAbZT8k=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1774632329; c=relaxed/simple;\n\tbh=cj+7lknzEchN1qpXJaCz2YQxiijKLO+gQqM3OYAdJJE=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References:\n\t In-Reply-To:To:Cc;\n b=kZaIhIsIdqF+0CJKpQpBj1GTONjbI3luzpipN9UiXa+quAhR/8hvOTGGwgDeKIjwOxCqvsN2aUTNZtakgsNHTu45mDZd4xKQ88bYTaZngDGRzh012iF8YtdEu3sRA+lbHLU4EaZ5GZafsCEE4x7igVIJaYT+U9Mjv90i9ZzSIuI=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=Zr4ObiJn; arc=none smtp.client-ip=10.30.226.201", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;\n\ts=k20201202; t=1774632329;\n\tbh=cj+7lknzEchN1qpXJaCz2YQxiijKLO+gQqM3OYAdJJE=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From;\n\tb=Zr4ObiJnOw6m19ej75swzhv6kVZV/TF9I50dx4z5FPymke/yWdoU7NeBu1jzMj1Ww\n\t brmCSM/Yqspe/gOyf0YIjdkSxQJDbGSaq0srwk/IUlxK77z/0GnzaGVo+fu0Tp0YAc\n\t m6ZcaZKnSgRrj967o0T4m+cpqG7G9mnNzJIvBRYS0I9I+MKB10EWIq/Bf4ZI6IGfxr\n\t AdCUzlJmyWAM4JFxf7SxWz+E3lDpB8oQUchKNjmYrNWxPy+tNoHfOmpkPhWlZDlUN4\n\t AQEbrzvNHFvdX8zOsuwL1vwaktXObOfZs1ItR4EZOo+hVibp4cZ1XZKkQKfSzO2gU9\n\t XB5xhYbchXtUQ==", "From": "=?utf-8?q?Nuno_S=C3=A1_via_B4_Relay?=\n <devnull+nuno.sa.analog.com@kernel.org>", "Date": "Fri, 27 Mar 2026 17:26:15 +0000", "Subject": "[PATCH v8 2/3] hwmon: ltc4283: Add support for the LTC4283 Swap\n Controller", "Precedence": "bulk", "X-Mailing-List": "linux-gpio@vger.kernel.org", "List-Id": "<linux-gpio.vger.kernel.org>", "List-Subscribe": "<mailto:linux-gpio+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-gpio+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "8bit", "Message-Id": "<20260327-ltc4283-support-v8-2-471de255d728@analog.com>", "References": "<20260327-ltc4283-support-v8-0-471de255d728@analog.com>", "In-Reply-To": "<20260327-ltc4283-support-v8-0-471de255d728@analog.com>", "To": "linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org,\n devicetree@vger.kernel.org, linux-doc@vger.kernel.org", "Cc": "Guenter Roeck <linux@roeck-us.net>, Rob Herring <robh@kernel.org>,\n Krzysztof Kozlowski <krzk+dt@kernel.org>,\n Conor Dooley <conor+dt@kernel.org>, Jonathan Corbet <corbet@lwn.net>,\n Linus Walleij <linusw@kernel.org>, Bartosz Golaszewski <brgl@kernel.org>", "X-Mailer": "b4 0.15.0", "X-Developer-Signature": "v=1; a=ed25519-sha256; t=1774632374; l=67656;\n i=nuno.sa@analog.com; s=20231116; h=from:subject:message-id;\n bh=oJFVi/TQG7ik16oYD2OzsEP8Sfki49YAVR0yFKE89Yg=;\n b=REwY88DbEkEF24vUzxbAsyoILDBw5N8WXEKe77Da+t0X6oFSGvq/d9ND2x3+ADs+0xyuDbv6a\n X7ywgv+T9nTDZ4GdJOCSG4BcufoBlWGfk+lR8BJi9enbvrGbWWpEZ3N", "X-Developer-Key": "i=nuno.sa@analog.com; a=ed25519;\n pk=3NQwYA013OUYZsmDFBf8rmyyr5iQlxV/9H4/Df83o1E=", "X-Endpoint-Received": "by B4 Relay for nuno.sa@analog.com/20231116 with\n auth_id=100", "X-Original-From": "=?utf-8?q?Nuno_S=C3=A1?= <nuno.sa@analog.com>", "Reply-To": "nuno.sa@analog.com" }, "content": "From: Nuno Sá <nuno.sa@analog.com>\n\nSupport the LTC4283 Hot Swap Controller. The device features programmable\ncurrent limit with foldback and independently adjustable inrush current to\noptimize the MOSFET safe operating area (SOA). The SOA timer limits MOSFET\ntemperature rise for reliable protection against overstresses.\n\nAn I2C interface and onboard ADC allow monitoring of board current,\nvoltage, power, energy, and fault status.\n\nSigned-off-by: Nuno Sá <nuno.sa@analog.com>\n---\n Documentation/hwmon/index.rst | 1 +\n Documentation/hwmon/ltc4283.rst | 266 ++++++\n MAINTAINERS | 1 +\n drivers/hwmon/Kconfig | 12 +\n drivers/hwmon/Makefile | 1 +\n drivers/hwmon/ltc4283.c | 1796 +++++++++++++++++++++++++++++++++++++++\n 6 files changed, 2077 insertions(+)", "diff": "diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst\nindex 559c32344cd3..eab95152abee 100644\n--- a/Documentation/hwmon/index.rst\n+++ b/Documentation/hwmon/index.rst\n@@ -144,6 +144,7 @@ Hardware Monitoring Kernel Drivers\n ltc4260\n ltc4261\n ltc4282\n+ ltc4283\n ltc4286\n macsmc-hwmon\n max127\ndiff --git a/Documentation/hwmon/ltc4283.rst b/Documentation/hwmon/ltc4283.rst\nnew file mode 100644\nindex 000000000000..ba88445e45f4\n--- /dev/null\n+++ b/Documentation/hwmon/ltc4283.rst\n@@ -0,0 +1,266 @@\n+.. SPDX-License-Identifier: GPL-2.0-only\n+\n+Kernel drivers ltc4283\n+==========================================\n+\n+Supported chips:\n+\n+ * Analog Devices LTC4283\n+\n+ Prefix: 'ltc4283'\n+\n+ Addresses scanned: -\n+\n+ Datasheet:\n+\n+ https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4283.pdf\n+\n+Author: Nuno Sá <nuno.sa@analog.com>\n+\n+Description\n+___________\n+\n+The LTC4283 negative voltage hot swap controller drives an external N-channel\n+MOSFET to allow a board to be safely inserted and removed from a live backplane.\n+The device features programmable current limit with foldback and independently\n+adjustable inrush current to optimize the MOSFET safe operating area (SOA). The\n+SOA timer limits MOSFET temperature rise for reliable protection against\n+overstresses. An I2C interface and onboard gear-shift ADC allow monitoring of\n+board current, voltage, power, energy, and fault status. Additional features\n+respond to input UV/OV, interrupt the host when a fault has occurred, notify\n+when output power is good, detect insertion of a board, turn off the MOSFET\n+if an external supply monitor fails to indicate power good within a timeout\n+period, and auto-reboot after a programmable delay following a host commanded\n+turn-off.\n+\n+Sysfs entries\n+_____________\n+\n+The following attributes are supported. Limits are read-write and all the other\n+attributes are read-only. Note that the VADIOx channels might not be available\n+if the ADIO pins are used as GPIOs (naturally also affects the respective\n+differential channels).\n+\n+======================= ==========================================\n+in0_lcrit_alarm Critical Undervoltage alarm\n+in0_crit_alarm Critical Overvoltage alarm\n+in0_label\t\tChannel label (VIN)\n+\n+in1_input\t\tOutput voltage (mV).\n+in1_min\t\t\tUndervoltage threshold\n+in1_max\t\t\tOvervoltage threshold\n+in1_lowest\t\tLowest measured voltage\n+in1_highest\t\tHighest measured voltage\n+in1_reset_history\tWrite 1 to reset history.\n+in1_min_alarm\t\tUndervoltage alarm\n+in1_max_alarm\t\tOvervoltage alarm\n+in1_label\t\tChannel label (VPWR)\n+\n+in2_input\t\tOutput voltage (mV).\n+in2_min\t\t\tUndervoltage threshold\n+in2_max\t\t\tOvervoltage threshold\n+in2_lowest\t\tLowest measured voltage\n+in2_highest\t\tHighest measured voltage\n+in2_reset_history\tWrite 1 to reset history.\n+in2_min_alarm\t\tUndervoltage alarm\n+in2_max_alarm\t\tOvervoltage alarm\n+in2_enable\t\tEnable/Disable monitoring.\n+in2_label\t\tChannel label (VADI1)\n+\n+in3_input\t\tOutput voltage (mV).\n+in3_min\t\t\tUndervoltage threshold\n+in3_max\t\t\tOvervoltage threshold\n+in3_lowest\t\tLowest measured voltage\n+in3_highest\t\tHighest measured voltage\n+in3_reset_history\tWrite 1 to reset history.\n+in3_min_alarm\t\tUndervoltage alarm\n+in3_max_alarm\t\tOvervoltage alarm\n+in3_enable\t\tEnable/Disable monitoring.\n+in3_label\t\tChannel label (VADI2)\n+\n+in4_input\t\tOutput voltage (mV).\n+in4_min\t\t\tUndervoltage threshold\n+in4_max\t\t\tOvervoltage threshold\n+in4_lowest\t\tLowest measured voltage\n+in4_highest\t\tHighest measured voltage\n+in4_reset_history\tWrite 1 to reset history.\n+in4_min_alarm\t\tUndervoltage alarm\n+in4_max_alarm\t\tOvervoltage alarm\n+in4_enable\t\tEnable/Disable monitoring.\n+in4_label\t\tChannel label (VADI3)\n+\n+in5_input\t\tOutput voltage (mV).\n+in5_min\t\t\tUndervoltage threshold\n+in5_max\t\t\tOvervoltage threshold\n+in5_lowest\t\tLowest measured voltage\n+in5_highest\t\tHighest measured voltage\n+in5_reset_history\tWrite 1 to reset history.\n+in5_min_alarm\t\tUndervoltage alarm\n+in5_max_alarm\t\tOvervoltage alarm\n+in5_enable\t\tEnable/Disable monitoring.\n+in5_label\t\tChannel label (VADI4)\n+\n+in6_input\t\tOutput voltage (mV).\n+in6_min\t\t\tUndervoltage threshold\n+in6_max\t\t\tOvervoltage threshold\n+in6_lowest\t\tLowest measured voltage\n+in6_highest\t\tHighest measured voltage\n+in6_reset_history\tWrite 1 to reset history.\n+in6_min_alarm\t\tUndervoltage alarm\n+in6_max_alarm\t\tOvervoltage alarm\n+in6_enable\t\tEnable/Disable monitoring.\n+in6_label\t\tChannel label (VADIO1)\n+\n+in7_input\t\tOutput voltage (mV).\n+in7_min\t\t\tUndervoltage threshold\n+in7_max\t\t\tOvervoltage threshold\n+in7_lowest\t\tLowest measured voltage\n+in7_highest\t\tHighest measured voltage\n+in7_reset_history\tWrite 1 to reset history.\n+in7_min_alarm\t\tUndervoltage alarm\n+in7_max_alarm\t\tOvervoltage alarm\n+in7_enable\t\tEnable/Disable monitoring.\n+in7_label\t\tChannel label (VADIO2)\n+\n+in8_input\t\tOutput voltage (mV).\n+in8_min\t\t\tUndervoltage threshold\n+in8_max\t\t\tOvervoltage threshold\n+in8_lowest\t\tLowest measured voltage\n+in8_highest\t\tHighest measured voltage\n+in8_reset_history\tWrite 1 to reset history.\n+in8_min_alarm\t\tUndervoltage alarm\n+in8_max_alarm\t\tOvervoltage alarm\n+in8_enable\t\tEnable/Disable monitoring.\n+in8_label\t\tChannel label (VADIO3)\n+\n+in9_input\t\tOutput voltage (mV).\n+in9_min\t\t\tUndervoltage threshold\n+in9_max\t\t\tOvervoltage threshold\n+in9_lowest\t\tLowest measured voltage\n+in9_highest\t\tHighest measured voltage\n+in9_reset_history\tWrite 1 to reset history.\n+in9_min_alarm\t\tUndervoltage alarm\n+in9_max_alarm\t\tOvervoltage alarm\n+in9_enable\t\tEnable/Disable monitoring.\n+in9_label\t\tChannel label (VADIO4)\n+\n+in10_input\t\tOutput voltage (mV).\n+in10_min\t\tUndervoltage threshold\n+in10_max\t\tOvervoltage threshold\n+in10_lowest\t\tLowest measured voltage\n+in10_highest\t\tHighest measured voltage\n+in10_reset_history\tWrite 1 to reset history.\n+in10_min_alarm\t\tUndervoltage alarm\n+in10_max_alarm\t\tOvervoltage alarm\n+in10_enable\t\tEnable/Disable monitoring.\n+in10_label\t\tChannel label (DRNS)\n+\n+in11_input\t\tOutput voltage (mV).\n+in11_min\t\tUndervoltage threshold\n+in11_max\t\tOvervoltage threshold\n+in11_lowest\t\tLowest measured voltage\n+in11_highest\t\tHighest measured voltage\n+in11_reset_history\tWrite 1 to reset history.\n+\t\t\tAlso clears fet bad and short fault logs.\n+in11_min_alarm\t\tUndervoltage alarm\n+in11_max_alarm\t\tOvervoltage alarm\n+in11_enable\t\tEnable/Disable monitoring\n+in11_fault\t\tFailure in the MOSFET. Either bad or shorted FET.\n+in11_label\t\tChannel label (DRAIN)\n+\n+in12_input\t\tOutput voltage (mV).\n+in12_min\t\tUndervoltage threshold\n+in12_max\t\tOvervoltage threshold\n+in12_lowest\t\tLowest measured voltage\n+in12_highest\t\tHighest measured voltage\n+in12_reset_history\tWrite 1 to reset history.\n+in12_min_alarm\t\tUndervoltage alarm\n+in12_max_alarm\t\tOvervoltage alarm\n+in12_enable\t\tEnable/Disable monitoring.\n+in12_label\t\tChannel label (ADIN2-ADIN1)\n+\n+in13_input\t\tOutput voltage (mV).\n+in13_min\t\tUndervoltage threshold\n+in13_max\t\tOvervoltage threshold\n+in13_lowest\t\tLowest measured voltage\n+in13_highest\t\tHighest measured voltage\n+in13_reset_history\tWrite 1 to reset history.\n+in13_min_alarm\t\tUndervoltage alarm\n+in13_max_alarm\t\tOvervoltage alarm\n+in13_enable\t\tEnable/Disable monitoring.\n+in13_label\t\tChannel label (ADIN4-ADIN3)\n+\n+in14_input\t\tOutput voltage (mV).\n+in14_min\t\tUndervoltage threshold\n+in14_max\t\tOvervoltage threshold\n+in14_lowest\t\tLowest measured voltage\n+in14_highest\t\tHighest measured voltage\n+in14_reset_history\tWrite 1 to reset history.\n+in14_min_alarm\t\tUndervoltage alarm\n+in14_max_alarm\t\tOvervoltage alarm\n+in14_enable\t\tEnable/Disable monitoring.\n+in14_label\t\tChannel label (ADIO2-ADIO1)\n+\n+in15_input\t\tOutput voltage (mV).\n+in15_min\t\tUndervoltage threshold\n+in15_max\t\tOvervoltage threshold\n+in15_lowest\t\tLowest measured voltage\n+in15_highest\t\tHighest measured voltage\n+in15_reset_history\tWrite 1 to reset history.\n+in15_min_alarm\t\tUndervoltage alarm\n+in15_max_alarm\t\tOvervoltage alarm\n+in15_enable\t\tEnable/Disable monitoring.\n+in15_label\t\tChannel label (ADIO4-ADIO3)\n+\n+curr1_input\t\tSense current (mA)\n+curr1_min\t\tUndercurrent threshold\n+curr1_max\t\tOvercurrent threshold\n+curr1_lowest\t\tLowest measured current\n+curr1_highest\t\tHighest measured current\n+curr1_reset_history\tWrite 1 to reset curr1 history.\n+\t\t\tAlso clears overcurrent fault logs.\n+curr1_min_alarm\t\tUndercurrent alarm\n+curr1_max_alarm\t\tOvercurrent alarm\n+curr1_crit_alarm Critical Overcurrent alarm\n+curr1_label\t\tChannel label (ISENSE)\n+\n+power1_input\t\tPower (in uW)\n+power1_min\t\tLow power threshold\n+power1_max\t\tHigh power threshold\n+power1_input_lowest\tHistorical minimum power use\n+power1_input_highest\tHistorical maximum power use\n+power1_reset_history\tWrite 1 to reset power1 history.\n+\t\t\tAlso clears power fault logs.\n+power1_min_alarm\tLow power alarm\n+power1_max_alarm\tHigh power alarm\n+power1_label\t\tChannel label (Power)\n+\n+energy1_input\t\tMeasured energy over time (in microJoule)\n+energy1_enable\t\tEnable/Disable Energy accumulation\n+======================= ==========================================\n+\n+DebugFs entries\n+_______________\n+\n+The chip also has a fault log register where failures can be logged. Hence,\n+as these are logging events, we give access to them in debugfs. Note that\n+even if some failure is detected in these logs, it does necessarily mean\n+that the failure is still present. As mentioned in the proper Sysfs entries,\n+these logs can be cleared by writing in the proper reset_history attribute.\n+\n+.. warning:: The debugfs interface is subject to change without notice\n+ and is only available when the kernel is compiled with\n+ ``CONFIG_DEBUG_FS`` defined.\n+\n+``/sys/kernel/debug/i2c/i2c-[X]/[X]-addr/``\n+contains the following attributes:\n+\n+=======================\t\t==========================================\n+power1_failed_fault_log\t\tSet to 1 by a power1 fault occurring.\n+power1_good_input_fault_log\tSet to 1 by a power1 good input fault occurring at PGIO3.\n+in11_fet_short_fault_log\tSet to 1 when a FET-short fault occurs.\n+in11_fet_bad_fault_log\t\tSet to 1 when a FET-BAD fault occurs.\n+in0_lcrit_fault_log\t\tSet to 1 by a VIN undervoltage fault occurring.\n+in0_crit_fault_log\t\tSet to 1 by a VIN overvoltage fault occurring.\n+curr1_crit_fault_log\t\tSet to 1 by an overcurrent fault occurring.\n+======================= \t==========================================\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 13ae2f3db449..38d22cf622b7 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -15146,6 +15146,7 @@ M:\tNuno Sá <nuno.sa@analog.com>\n L:\tlinux-hwmon@vger.kernel.org\n S:\tSupported\n F:\tDocumentation/devicetree/bindings/hwmon/adi,ltc4283.yaml\n+F:\tdrivers/hwmon/ltc4283.c\n \n LTC4286 HARDWARE MONITOR DRIVER\n M:\tDelphine CC Chiu <Delphine_CC_Chiu@Wiwynn.com>\ndiff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig\nindex 7dd8381ba0d0..d8334edf5514 100644\n--- a/drivers/hwmon/Kconfig\n+++ b/drivers/hwmon/Kconfig\n@@ -1157,6 +1157,18 @@ config SENSORS_LTC4282\n \t This driver can also be built as a module. If so, the module will\n \t be called ltc4282.\n \n+config SENSORS_LTC4283\n+\ttristate \"Analog Devices LTC4283\"\n+\tdepends on I2C\n+\tselect REGMAP_I2C\n+\tselect AUXILIARY_BUS\n+\thelp\n+\t If you say yes here you get support for Analog Devices LTC4283\n+\t Negative Voltage Hot Swap Controller I2C interface.\n+\n+\t This driver can also be built as a module. If so, the module will\n+\t be called ltc4283.\n+\n config SENSORS_LTQ_CPUTEMP\n \tbool \"Lantiq cpu temperature sensor driver\"\n \tdepends on SOC_XWAY\ndiff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile\nindex 556e86d277b1..cb77938dbe07 100644\n--- a/drivers/hwmon/Makefile\n+++ b/drivers/hwmon/Makefile\n@@ -147,6 +147,7 @@ obj-$(CONFIG_SENSORS_LTC4245)\t+= ltc4245.o\n obj-$(CONFIG_SENSORS_LTC4260)\t+= ltc4260.o\n obj-$(CONFIG_SENSORS_LTC4261)\t+= ltc4261.o\n obj-$(CONFIG_SENSORS_LTC4282)\t+= ltc4282.o\n+obj-$(CONFIG_SENSORS_LTC4283)\t+= ltc4283.o\n obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o\n obj-$(CONFIG_SENSORS_MACSMC_HWMON)\t+= macsmc-hwmon.o\n obj-$(CONFIG_SENSORS_MAX1111)\t+= max1111.o\ndiff --git a/drivers/hwmon/ltc4283.c b/drivers/hwmon/ltc4283.c\nnew file mode 100644\nindex 000000000000..f5320f34eb45\n--- /dev/null\n+++ b/drivers/hwmon/ltc4283.c\n@@ -0,0 +1,1796 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Analog Devices LTC4283 I2C Negative Voltage Hot Swap Controller (HWMON)\n+ *\n+ * Copyright 2025 Analog Devices Inc.\n+ */\n+#include <linux/auxiliary_bus.h>\n+#include <linux/bitfield.h>\n+#include <linux/bitmap.h>\n+#include <linux/bitops.h>\n+#include <linux/bits.h>\n+\n+#include <linux/debugfs.h>\n+#include <linux/device.h>\n+#include <linux/device/devres.h>\n+#include <linux/hwmon.h>\n+#include <linux/i2c.h>\n+#include <linux/math.h>\n+#include <linux/math64.h>\n+#include <linux/minmax.h>\n+#include <linux/module.h>\n+\n+#include <linux/mod_devicetable.h>\n+#include <linux/overflow.h>\n+#include <linux/property.h>\n+#include <linux/regmap.h>\n+#include <linux/unaligned.h>\n+#include <linux/units.h>\n+\n+#define LTC4283_SYSTEM_STATUS\t\t0x00\n+#define LTC4283_FAULT_STATUS\t\t0x03\n+#define LTC4283_OV_MASK\t\tBIT(0)\n+#define LTC4283_UV_MASK\t\tBIT(1)\n+#define LTC4283_OC_MASK\t\tBIT(2)\n+#define LTC4283_FET_BAD_MASK\t\tBIT(3)\n+#define LTC4283_FET_SHORT_MASK\tBIT(6)\n+#define LTC4283_FAULT_LOG\t\t0x04\n+#define LTC4283_OV_FAULT_MASK\t\tBIT(0)\n+#define LTC4283_UV_FAULT_MASK\t\tBIT(1)\n+#define LTC4283_OC_FAULT_MASK\t\tBIT(2)\n+#define LTC4283_FET_BAD_FAULT_MASK\tBIT(3)\n+#define LTC4283_PGI_FAULT_MASK\tBIT(4)\n+#define LTC4283_PWR_FAIL_FAULT_MASK\tBIT(5)\n+#define LTC4283_FET_SHORT_FAULT_MASK\tBIT(6)\n+#define LTC4283_ADC_ALM_LOG_1\t\t0x05\n+#define LTC4283_POWER_LOW_ALM\t\tBIT(0)\n+#define LTC4283_POWER_HIGH_ALM\tBIT(1)\n+#define LTC4283_SENSE_LOW_ALM\t\tBIT(4)\n+#define LTC4283_SENSE_HIGH_ALM\tBIT(5)\n+#define LTC4283_ADC_ALM_LOG_2\t\t0x06\n+#define LTC4283_ADC_ALM_LOG_3\t\t0x07\n+#define LTC4283_ADC_ALM_LOG_4\t\t0x08\n+#define LTC4283_ADC_ALM_LOG_5\t\t0x09\n+#define LTC4283_CONTROL_1\t\t0x0a\n+#define LTC4283_RW_PAGE_MASK\t\tBIT(0)\n+#define LTC4283_PIGIO2_ACLB_MASK\tBIT(2)\n+#define LTC4283_PWRGD_RST_CTRL_MASK\tBIT(3)\n+#define LTC4283_FET_BAD_OFF_MASK\tBIT(4)\n+#define LTC4283_THERM_TMR_MASK\tBIT(5)\n+#define LTC4283_DVDT_MASK\t\tBIT(6)\n+#define LTC4283_CONTROL_2\t\t0x0b\n+#define LTC4283_OV_RETRY_MASK\t\tBIT(0)\n+#define LTC4283_UV_RETRY_MASK\t\tBIT(1)\n+#define LTC4283_OC_RETRY_MASK\t\tGENMASK(3, 2)\n+#define LTC4283_FET_BAD_RETRY_MASK\tGENMASK(5, 4)\n+#define LTC4283_EXT_FAULT_RETRY_MASK\tBIT(7)\n+#define LTC4283_RESERVED_OC\t\t0x0c\n+#define LTC4283_CONFIG_1\t\t0x0d\n+#define LTC4283_FB_MASK\t\tGENMASK(3, 2)\n+#define LTC4283_ILIM_MASK\t\tGENMASK(7, 4)\n+#define LTC4283_CONFIG_2\t\t0x0e\n+#define LTC4283_COOLING_DL_MASK\tGENMASK(3, 1)\n+#define LTC4283_FTBD_DL_MASK\t\tGENMASK(5, 4)\n+#define LTC4283_CONFIG_3\t\t0x0f\n+#define LTC4283_VPWR_DRNS_MASK\tBIT(6)\n+#define LTC4283_EXTFLT_TURN_OFF_MASK\tBIT(7)\n+#define LTC4283_PGIO_CONFIG\t\t0x10\n+#define LTC4283_PGIO1_CFG_MASK\tGENMASK(1, 0)\n+#define LTC4283_PGIO2_CFG_MASK\tGENMASK(3, 2)\n+#define LTC4283_PGIO3_CFG_MASK\tGENMASK(5, 4)\n+#define LTC4283_PGIO4_CFG_MASK\tGENMASK(7, 6)\n+#define LTC4283_PGIO_CONFIG_2\t\t0x11\n+#define LTC4283_ADC_MASK\t\tGENMASK(2, 0)\n+#define LTC4283_ADC_SELECT(c)\t\t(0x13 + (c) / 8)\n+#define LTC4283_ADC_SELECT_MASK(c)\tBIT((c) % 8)\n+#define LTC4283_SENSE_MIN_TH\t\t0x1b\n+#define LTC4283_SENSE_MAX_TH\t\t0x1c\n+#define LTC4283_VPWR_MIN_TH\t\t0x1d\n+#define LTC4283_VPWR_MAX_TH\t\t0x1e\n+#define LTC4283_POWER_MIN_TH\t\t0x1f\n+#define LTC4283_POWER_MAX_TH\t\t0x20\n+#define LTC4283_ADC_2_MIN_TH(c)\t\t(0x21 + (c) * 2)\n+#define LTC4283_ADC_2_MAX_TH(c)\t\t(0x22 + (c) * 2)\n+#define LTC4283_ADC_2_MIN_TH_DIFF(c)\t(0x39 + (c) * 2)\n+#define LTC4283_ADC_2_MAX_TH_DIFF(c)\t(0x3a + (c) * 2)\n+#define LTC4283_SENSE\t\t\t0x41\n+#define LTC4283_SENSE_MIN\t\t0x42\n+#define LTC4283_SENSE_MAX\t\t0x43\n+#define LTC4283_VPWR\t\t\t0x44\n+#define LTC4283_VPWR_MIN\t\t0x45\n+#define LTC4283_VPWR_MAX\t\t0x46\n+#define LTC4283_POWER\t\t\t0x47\n+#define LTC4283_POWER_MIN\t\t0x48\n+#define LTC4283_POWER_MAX\t\t0x49\n+#define LTC4283_RESERVED_68\t\t0x68\n+#define LTC4283_RESERVED_6D\t\t0x6D\n+/* get channels from ADC 2 */\n+#define LTC4283_ADC_2(c)\t\t(0x4a + (c) * 3)\n+#define LTC4283_ADC_2_MIN(c)\t\t(0x4b + (c) * 3)\n+#define LTC4283_ADC_2_MAX(c)\t\t(0x4c + (c) * 3)\n+#define LTC4283_ADC_2_DIFF(c)\t\t(0x6e + (c) * 3)\n+#define LTC4283_ADC_2_MIN_DIFF(c)\t(0x6f + (c) * 3)\n+#define LTC4283_ADC_2_MAX_DIFF(c)\t(0x70 + (c) * 3)\n+#define LTC4283_ENERGY\t\t\t0x7a\n+#define LTC4283_METER_CONTROL\t\t0x84\n+#define LTC4283_INTEGRATE_I_MASK\tBIT(0)\n+#define LTC4283_METER_HALT_MASK\tBIT(6)\n+#define LTC4283_RESERVED_86\t\t0x86\n+#define LTC4283_RESERVED_8F\t\t0x8F\n+#define LTC4283_FAULT_LOG_CTRL\t\t0x90\n+#define LTC4283_FAULT_LOG_EN_MASK\tBIT(7)\n+#define LTC4283_RESERVED_91\t\t0x91\n+#define LTC4283_RESERVED_A1\t\t0xA1\n+#define LTC4283_RESERVED_A3\t\t0xA3\n+#define LTC4283_RESERVED_AC\t\t0xAC\n+#define LTC4283_POWER_PLAY_MSB\t\t0xE7\n+#define LTC4283_POWER_PLAY_LSB\t\t0xE8\n+#define LTC4283_RESERVED_F1\t\t0xF1\n+#define LTC4283_RESERVED_FF\t\t0xFF\n+\n+/* also applies for differential channels */\n+#define LTC4283_ADC1_FS_uV\t\t32768\n+#define LTC4283_ADC2_FS_mV\t\t2048\n+#define LTC4283_TCONV_uS\t\t64103\n+#define LTC4283_VILIM_MIN_uV\t\t15000\n+#define LTC4283_VILIM_MAX_uV\t\t30000\n+#define LTC4283_VILIM_RANGE\t\\\n+\t(LTC4283_VILIM_MAX_uV - LTC4283_VILIM_MIN_uV + 1)\n+\n+#define LTC4283_PGIO_FUNC_GPIO\t\t2\n+#define LTC4283_PGIO2_FUNC_ACLB\t\t3\n+\n+/* voltage channels */\n+enum {\n+\tLTC4283_CHAN_VIN,\n+\tLTC4283_CHAN_VPWR,\n+\tLTC4283_CHAN_ADI_1,\n+\tLTC4283_CHAN_ADI_2,\n+\tLTC4283_CHAN_ADI_3,\n+\tLTC4283_CHAN_ADI_4,\n+\tLTC4283_CHAN_ADIO_1,\n+\tLTC4283_CHAN_ADIO_2,\n+\tLTC4283_CHAN_ADIO_3,\n+\tLTC4283_CHAN_ADIO_4,\n+\tLTC4283_CHAN_DRNS,\n+\tLTC4283_CHAN_DRAIN,\n+\t/* differential channels */\n+\tLTC4283_CHAN_ADIN12,\n+\tLTC4283_CHAN_ADIN34,\n+\tLTC4283_CHAN_ADIO12,\n+\tLTC4283_CHAN_ADIO34,\n+\tLTC4283_CHAN_MAX\n+};\n+\n+/* Just for ease of use on the regmap */\n+#define LTC4283_ADIO34_MAX \\\n+\tLTC4283_ADC_2_MAX_DIFF(LTC4283_CHAN_ADIO34 - LTC4283_CHAN_ADIN12)\n+\n+struct ltc4283_hwmon {\n+\tstruct regmap *map;\n+\tstruct i2c_client *client;\n+\tunsigned long gpio_mask;\n+\tunsigned long ch_enable_mask;\n+\t/* in microwatt */\n+\tlong power_max;\n+\t/* in millivolt */\n+\tu32 vsense_max;\n+\t/* in tenths of microohm*/\n+\tu32 rsense;\n+\tbool energy_en;\n+\tbool ext_fault;\n+};\n+\n+static int ltc4283_read_voltage_word(const struct ltc4283_hwmon *st,\n+\t\t\t\t u32 reg, u32 fs, long *val)\n+{\n+\tunsigned int __raw;\n+\tint ret;\n+\n+\tret = regmap_read(st->map, reg, &__raw);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = DIV_ROUND_CLOSEST(__raw * fs, BIT(16));\n+\treturn 0;\n+}\n+\n+static int ltc4283_read_voltage_byte(const struct ltc4283_hwmon *st,\n+\t\t\t\t u32 reg, u32 fs, long *val)\n+{\n+\tint ret;\n+\tu32 in;\n+\n+\tret = regmap_read(st->map, reg, &in);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = DIV_ROUND_CLOSEST(in * fs, BIT(8));\n+\treturn 0;\n+}\n+\n+static u32 ltc4283_in_reg(u32 attr, u32 channel)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_in_input:\n+\t\tif (channel == LTC4283_CHAN_VPWR)\n+\t\t\treturn LTC4283_VPWR;\n+\t\tif (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN)\n+\t\t\treturn LTC4283_ADC_2(channel - LTC4283_CHAN_ADI_1);\n+\t\treturn LTC4283_ADC_2_DIFF(channel - LTC4283_CHAN_ADIN12);\n+\tcase hwmon_in_highest:\n+\t\tif (channel == LTC4283_CHAN_VPWR)\n+\t\t\treturn LTC4283_VPWR_MAX;\n+\t\tif (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN)\n+\t\t\treturn LTC4283_ADC_2_MAX(channel - LTC4283_CHAN_ADI_1);\n+\t\treturn LTC4283_ADC_2_MAX_DIFF(channel - LTC4283_CHAN_ADIN12);\n+\tcase hwmon_in_lowest:\n+\t\tif (channel == LTC4283_CHAN_VPWR)\n+\t\t\treturn LTC4283_VPWR_MIN;\n+\t\tif (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN)\n+\t\t\treturn LTC4283_ADC_2_MIN(channel - LTC4283_CHAN_ADI_1);\n+\t\treturn LTC4283_ADC_2_MIN_DIFF(channel - LTC4283_CHAN_ADIN12);\n+\tcase hwmon_in_max:\n+\t\tif (channel == LTC4283_CHAN_VPWR)\n+\t\t\treturn LTC4283_VPWR_MAX_TH;\n+\t\tif (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN)\n+\t\t\treturn LTC4283_ADC_2_MAX_TH(channel - LTC4283_CHAN_ADI_1);\n+\t\treturn LTC4283_ADC_2_MAX_TH_DIFF(channel - LTC4283_CHAN_ADIN12);\n+\tdefault:\n+\t\tif (channel == LTC4283_CHAN_VPWR)\n+\t\t\treturn LTC4283_VPWR_MIN_TH;\n+\t\tif (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN)\n+\t\t\treturn LTC4283_ADC_2_MIN_TH(channel - LTC4283_CHAN_ADI_1);\n+\t\treturn LTC4283_ADC_2_MIN_TH_DIFF(channel - LTC4283_CHAN_ADIN12);\n+\t}\n+}\n+\n+static int ltc4283_read_in_vals(const struct ltc4283_hwmon *st,\n+\t\t\t\tu32 attr, u32 channel, long *val)\n+{\n+\tu32 reg = ltc4283_in_reg(attr, channel);\n+\tint ret;\n+\n+\tif (channel < LTC4283_CHAN_ADIN12) {\n+\t\tif (attr != hwmon_in_max && attr != hwmon_in_min)\n+\t\t\treturn ltc4283_read_voltage_word(st, reg,\n+\t\t\t\t\t\t\t LTC4283_ADC2_FS_mV,\n+\t\t\t\t\t\t\t val);\n+\n+\t\treturn ltc4283_read_voltage_byte(st, reg,\n+\t\t\t\t\t\t LTC4283_ADC2_FS_mV, val);\n+\t}\n+\n+\tif (attr != hwmon_in_max && attr != hwmon_in_min)\n+\t\tret = ltc4283_read_voltage_word(st, reg,\n+\t\t\t\t\t\tLTC4283_ADC1_FS_uV, val);\n+\telse\n+\t\tret = ltc4283_read_voltage_byte(st, reg,\n+\t\t\t\t\t\tLTC4283_ADC1_FS_uV, val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = DIV_ROUND_CLOSEST(*val, MILLI);\n+\treturn 0;\n+}\n+\n+static int ltc4283_read_alarm(struct ltc4283_hwmon *st, u32 reg,\n+\t\t\t u32 mask, long *val)\n+{\n+\tu32 alarm;\n+\tint ret;\n+\n+\tret = regmap_read(st->map, reg, &alarm);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = !!(alarm & mask);\n+\n+\t/* If not status/fault logs, clear the alarm after reading it. */\n+\tif (reg != LTC4283_FAULT_STATUS && reg != LTC4283_FAULT_LOG)\n+\t\treturn regmap_clear_bits(st->map, reg, mask);\n+\n+\treturn 0;\n+}\n+\n+static int ltc4283_read_in_alarm(struct ltc4283_hwmon *st, u32 channel,\n+\t\t\t\t bool max_alm, long *val)\n+{\n+\tif (channel == LTC4283_VPWR)\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1,\n+\t\t\t\t\t BIT(2 + max_alm), val);\n+\n+\tif (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_ADI_4) {\n+\t\tu32 bit = (channel - LTC4283_CHAN_ADI_1) * 2;\n+\t\t/*\n+\t\t * Lower channels go to higher bits. We also want to go +1 down\n+\t\t * in the min_alarm case.\n+\t\t */\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_2,\n+\t\t\t\t\t BIT(7 - bit - !max_alm), val);\n+\t}\n+\n+\tif (channel >= LTC4283_CHAN_ADIO_1 && channel <= LTC4283_CHAN_ADIO_4) {\n+\t\tu32 bit = (channel - LTC4283_CHAN_ADIO_1) * 2;\n+\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_3,\n+\t\t\t\t\t BIT(7 - bit - !max_alm), val);\n+\t}\n+\n+\tif (channel >= LTC4283_CHAN_ADIN12 && channel <= LTC4283_CHAN_ADIN34) {\n+\t\tu32 bit = (channel - LTC4283_CHAN_ADIN12) * 2;\n+\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_5,\n+\t\t\t\t\t BIT(7 - bit - !max_alm), val);\n+\t}\n+\n+\tif (channel == LTC4283_CHAN_DRNS)\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_4,\n+\t\t\t\t\t BIT(6 + max_alm), val);\n+\n+\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_4, BIT(4 + max_alm),\n+\t\t\t\t val);\n+}\n+\n+static int ltc4283_read_in(struct ltc4283_hwmon *st, u32 attr, u32 channel,\n+\t\t\t long *val)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_in_input:\n+\t\tif (!test_bit(channel, &st->ch_enable_mask))\n+\t\t\treturn -ENODATA;\n+\n+\t\treturn ltc4283_read_in_vals(st, attr, channel, val);\n+\tcase hwmon_in_highest:\n+\tcase hwmon_in_lowest:\n+\tcase hwmon_in_max:\n+\tcase hwmon_in_min:\n+\t\treturn ltc4283_read_in_vals(st, attr, channel, val);\n+\tcase hwmon_in_max_alarm:\n+\t\treturn ltc4283_read_in_alarm(st, channel, true, val);\n+\tcase hwmon_in_min_alarm:\n+\t\treturn ltc4283_read_in_alarm(st, channel, false, val);\n+\tcase hwmon_in_crit_alarm:\n+\t\treturn ltc4283_read_alarm(st, LTC4283_FAULT_STATUS,\n+\t\t\t\t\t LTC4283_OV_MASK, val);\n+\tcase hwmon_in_lcrit_alarm:\n+\t\treturn ltc4283_read_alarm(st, LTC4283_FAULT_STATUS,\n+\t\t\t\t\t LTC4283_UV_MASK, val);\n+\tcase hwmon_in_fault:\n+\t\t/*\n+\t\t * We report failure if we detect either a fer_bad or a\n+\t\t * fet_short in the status register.\n+\t\t */\n+\t\treturn ltc4283_read_alarm(st, LTC4283_FAULT_STATUS,\n+\t\t\t\t\t LTC4283_FET_BAD_MASK | LTC4283_FET_SHORT_MASK, val);\n+\tcase hwmon_in_enable:\n+\t\t*val = test_bit(channel, &st->ch_enable_mask);\n+\t\treturn 0;\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\treturn 0;\n+}\n+\n+static int ltc4283_read_current_word(const struct ltc4283_hwmon *st, u32 reg,\n+\t\t\t\t long *val)\n+{\n+\tu64 temp = (u64)LTC4283_ADC1_FS_uV * DECA * MILLI;\n+\tunsigned int __raw;\n+\tint ret;\n+\n+\tret = regmap_read(st->map, reg, &__raw);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = DIV64_U64_ROUND_CLOSEST(__raw * temp,\n+\t\t\t\t BIT_ULL(16) * st->rsense);\n+\n+\treturn 0;\n+}\n+\n+static int ltc4283_read_current_byte(const struct ltc4283_hwmon *st, u32 reg,\n+\t\t\t\t long *val)\n+{\n+\tu64 temp = (u64)LTC4283_ADC1_FS_uV * DECA * MILLI;\n+\tu32 curr;\n+\tint ret;\n+\n+\tret = regmap_read(st->map, reg, &curr);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = DIV_ROUND_CLOSEST_ULL(curr * temp, BIT(8) * st->rsense);\n+\treturn 0;\n+}\n+\n+static int ltc4283_read_curr(struct ltc4283_hwmon *st, u32 attr, long *val)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_curr_input:\n+\t\treturn ltc4283_read_current_word(st, LTC4283_SENSE, val);\n+\tcase hwmon_curr_highest:\n+\t\treturn ltc4283_read_current_word(st, LTC4283_SENSE_MAX, val);\n+\tcase hwmon_curr_lowest:\n+\t\treturn ltc4283_read_current_word(st, LTC4283_SENSE_MIN, val);\n+\tcase hwmon_curr_max:\n+\t\treturn ltc4283_read_current_byte(st, LTC4283_SENSE_MAX_TH, val);\n+\tcase hwmon_curr_min:\n+\t\treturn ltc4283_read_current_byte(st, LTC4283_SENSE_MIN_TH, val);\n+\tcase hwmon_curr_max_alarm:\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1,\n+\t\t\t\t\t LTC4283_SENSE_HIGH_ALM, val);\n+\tcase hwmon_curr_min_alarm:\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1,\n+\t\t\t\t\t LTC4283_SENSE_LOW_ALM, val);\n+\tcase hwmon_curr_crit_alarm:\n+\t\treturn ltc4283_read_alarm(st, LTC4283_FAULT_STATUS,\n+\t\t\t\t\t LTC4283_OC_MASK, val);\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static int ltc4283_read_power_word(const struct ltc4283_hwmon *st,\n+\t\t\t\t u32 reg, long *val)\n+{\n+\tu64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI;\n+\tunsigned int __raw;\n+\tint ret;\n+\n+\tret = regmap_read(st->map, reg, &__raw);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * Power is given by:\n+\t * P = CODE(16b) * 32.768mV * 2.048V / (2^16 * Rsense)\n+\t */\n+\t*val = DIV64_U64_ROUND_CLOSEST(temp * __raw, BIT_ULL(16) * st->rsense);\n+\n+\treturn 0;\n+}\n+\n+static int ltc4283_read_power_byte(const struct ltc4283_hwmon *st,\n+\t\t\t\t u32 reg, long *val)\n+{\n+\tu64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI;\n+\tu32 power;\n+\tint ret;\n+\n+\tret = regmap_read(st->map, reg, &power);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = DIV_ROUND_CLOSEST_ULL(power * temp, BIT(8) * st->rsense);\n+\n+\treturn 0;\n+}\n+\n+static int ltc4283_read_power(struct ltc4283_hwmon *st, u32 attr, long *val)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_power_input:\n+\t\treturn ltc4283_read_power_word(st, LTC4283_POWER, val);\n+\tcase hwmon_power_input_highest:\n+\t\treturn ltc4283_read_power_word(st, LTC4283_POWER_MAX, val);\n+\tcase hwmon_power_input_lowest:\n+\t\treturn ltc4283_read_power_word(st, LTC4283_POWER_MIN, val);\n+\tcase hwmon_power_max_alarm:\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1,\n+\t\t\t\t\t LTC4283_POWER_HIGH_ALM, val);\n+\tcase hwmon_power_min_alarm:\n+\t\treturn ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1,\n+\t\t\t\t\t LTC4283_POWER_LOW_ALM, val);\n+\tcase hwmon_power_max:\n+\t\treturn ltc4283_read_power_byte(st, LTC4283_POWER_MAX_TH, val);\n+\tcase hwmon_power_min:\n+\t\treturn ltc4283_read_power_byte(st, LTC4283_POWER_MIN_TH, val);\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static int ltc4283_read_energy(struct ltc4283_hwmon *st, u32 attr, s64 *val)\n+{\n+\tu64 temp = LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV, energy, temp_2;\n+\tu8 raw[8] = {};\n+\tint ret;\n+\n+\tif (!st->energy_en)\n+\t\treturn -ENODATA;\n+\n+\tret = i2c_smbus_read_i2c_block_data(st->client, LTC4283_ENERGY, 6, raw);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tif (ret != 6)\n+\t\treturn -EIO;\n+\n+\tenergy = get_unaligned_be64(raw) >> 16;\n+\n+\t/*\n+\t * The formula for energy is given by:\n+\t *\tE = CODE(48b) * 32.768mV * 2.048V * Tconv / 2^24 * Rsense\n+\t *\n+\t * As Rsense can have tenths of micro-ohm resolution, we need to\n+\t * multiply by DECA to get microjoule.\n+\t */\n+\tif (check_mul_overflow(temp * LTC4283_TCONV_uS, energy, &temp_2)) {\n+\t\t/*\n+\t\t * We multiply again by 1000 to make sure that we don't get 0\n+\t\t * in the following division which could happen for big rsense\n+\t\t * values. OTOH, we then divide energy first by 1000 so that\n+\t\t * we do not overflow u64 again for very small rsense values.\n+\t\t * We add 100 factor for proper conversion to microjoule.\n+\t\t */\n+\t\ttemp_2 = DIV64_U64_ROUND_CLOSEST(temp * LTC4283_TCONV_uS * MILLI,\n+\t\t\t\t\t\t BIT_ULL(24) * st->rsense);\n+\t\tenergy = DIV_ROUND_CLOSEST_ULL(energy, MILLI * CENTI) * temp_2;\n+\t} else {\n+\t\t/* Put rsense back into nanoohm so we get microjoule. */\n+\t\tenergy = DIV64_U64_ROUND_CLOSEST(temp_2, BIT_ULL(24) * st->rsense * CENTI);\n+\t}\n+\n+\t*val = energy;\n+\treturn 0;\n+}\n+\n+static int ltc4283_read(struct device *dev, enum hwmon_sensor_types type,\n+\t\t\tu32 attr, int channel, long *val)\n+{\n+\tstruct ltc4283_hwmon *st = dev_get_drvdata(dev);\n+\n+\tswitch (type) {\n+\tcase hwmon_in:\n+\t\treturn ltc4283_read_in(st, attr, channel, val);\n+\tcase hwmon_curr:\n+\t\treturn ltc4283_read_curr(st, attr, val);\n+\tcase hwmon_power:\n+\t\treturn ltc4283_read_power(st, attr, val);\n+\tcase hwmon_energy:\n+\t\t*val = st->energy_en;\n+\t\treturn 0;\n+\tcase hwmon_energy64:\n+\t\treturn ltc4283_read_energy(st, attr, (s64 *)val);\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static int ltc4283_write_power_byte(const struct ltc4283_hwmon *st, u32 reg,\n+\t\t\t\t long val)\n+{\n+\tu64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI;\n+\tu32 __raw;\n+\n+\tval = clamp_val(val, 0, st->power_max);\n+\t__raw = DIV64_U64_ROUND_CLOSEST(val * BIT_ULL(8) * st->rsense, temp);\n+\n+\treturn regmap_write(st->map, reg, __raw);\n+}\n+\n+static int ltc4283_write_power_word(const struct ltc4283_hwmon *st,\n+\t\t\t\t u32 reg, long val)\n+{\n+\tu64 temp = st->rsense * BIT_ULL(16), temp_2;\n+\tu16 __raw;\n+\n+\tif (check_mul_overflow(val, temp, &temp_2)) {\n+\t\ttemp = DIV_ROUND_CLOSEST_ULL(temp, DECA * MILLI);\n+\t\t__raw = DIV_ROUND_CLOSEST_ULL(temp * val, LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV);\n+\t} else {\n+\t\ttemp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI;\n+\t\t__raw = DIV64_U64_ROUND_CLOSEST(temp_2, temp);\n+\t}\n+\n+\treturn regmap_write(st->map, reg, __raw);\n+}\n+\n+static int ltc4283_reset_power_hist(struct ltc4283_hwmon *st)\n+{\n+\tint ret;\n+\n+\tret = ltc4283_write_power_word(st, LTC4283_POWER_MIN, st->power_max);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ltc4283_write_power_word(st, LTC4283_POWER_MAX, 0);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Clear possible power faults. */\n+\treturn regmap_clear_bits(st->map, LTC4283_FAULT_LOG,\n+\t\t\t\t LTC4283_PWR_FAIL_FAULT_MASK | LTC4283_PGI_FAULT_MASK);\n+}\n+\n+static int ltc4283_write_power(struct ltc4283_hwmon *st, u32 attr, long val)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_power_max:\n+\t\treturn ltc4283_write_power_byte(st, LTC4283_POWER_MAX_TH, val);\n+\tcase hwmon_power_min:\n+\t\treturn ltc4283_write_power_byte(st, LTC4283_POWER_MIN_TH, val);\n+\tcase hwmon_power_reset_history:\n+\t\treturn ltc4283_reset_power_hist(st);\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static int ltc4283_write_in_history(struct ltc4283_hwmon *st, u32 reg,\n+\t\t\t\t long lowest, u32 fs)\n+{\n+\tu32 __raw;\n+\tint ret;\n+\n+\t__raw = DIV_ROUND_CLOSEST(BIT(16) * lowest, fs);\n+\tif (__raw == BIT(16))\n+\t\t__raw = U16_MAX;\n+\n+\tret = regmap_write(st->map, reg, __raw);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn regmap_write(st->map, reg + 1, 0);\n+}\n+\n+static int ltc4283_write_in_byte(const struct ltc4283_hwmon *st,\n+\t\t\t\t u32 reg, u32 fs, long val)\n+{\n+\tu32 __raw;\n+\n+\tval = clamp_val(val, 0, fs);\n+\t__raw = DIV_ROUND_CLOSEST(val * BIT(8), fs);\n+\tif (__raw == BIT(8))\n+\t\t__raw = U8_MAX;\n+\n+\treturn regmap_write(st->map, reg, __raw);\n+}\n+\n+static int ltc4283_reset_in_hist(struct ltc4283_hwmon *st, u32 channel)\n+{\n+\tu32 reg, fs;\n+\tint ret;\n+\n+\t/*\n+\t * Make sure to clear possible under/over voltage faults. Otherwise the\n+\t * chip won't latch on again.\n+\t */\n+\tif (channel == LTC4283_CHAN_VIN)\n+\t\treturn regmap_clear_bits(st->map, LTC4283_FAULT_LOG,\n+\t\t\t\t\t LTC4283_OV_FAULT_MASK | LTC4283_UV_FAULT_MASK);\n+\n+\tif (channel == LTC4283_CHAN_VPWR)\n+\t\treturn ltc4283_write_in_history(st, LTC4283_VPWR_MIN,\n+\t\t\t\t\t\tLTC4283_ADC2_FS_mV,\n+\t\t\t\t\t\tLTC4283_ADC2_FS_mV);\n+\n+\tif (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) {\n+\t\tfs = LTC4283_ADC2_FS_mV;\n+\t\treg = LTC4283_ADC_2_MIN(channel - LTC4283_CHAN_ADI_1);\n+\t} else {\n+\t\tfs = LTC4283_ADC1_FS_uV;\n+\t\treg = LTC4283_ADC_2_MIN_DIFF(channel - LTC4283_CHAN_ADIN12);\n+\t}\n+\n+\tret = ltc4283_write_in_history(st, reg, fs, fs);\n+\tif (ret)\n+\t\treturn ret;\n+\tif (channel != LTC4283_CHAN_DRAIN)\n+\t\treturn 0;\n+\n+\t/* Then, let's also clear possible fet faults. Same as above. */\n+\treturn regmap_clear_bits(st->map, LTC4283_FAULT_LOG,\n+\t\t\t\t LTC4283_FET_BAD_FAULT_MASK | LTC4283_FET_SHORT_FAULT_MASK);\n+}\n+\n+static int ltc4283_write_in_en(struct ltc4283_hwmon *st, u32 channel, bool en)\n+{\n+\tunsigned int bit, adc_idx = channel - LTC4283_CHAN_ADI_1;\n+\tunsigned int reg = LTC4283_ADC_SELECT(adc_idx);\n+\tint ret;\n+\n+\tbit = LTC4283_ADC_SELECT_MASK(adc_idx);\n+\tif (channel > LTC4283_CHAN_DRAIN)\n+\t\t/* Account for two reserved fields after DRAIN. */\n+\t\tbit <<= 2;\n+\n+\tif (en)\n+\t\tret = regmap_set_bits(st->map, reg, bit);\n+\telse\n+\t\tret = regmap_clear_bits(st->map, reg, bit);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t__assign_bit(channel, &st->ch_enable_mask, en);\n+\treturn 0;\n+}\n+\n+static int ltc4283_write_minmax(struct ltc4283_hwmon *st, long val,\n+\t\t\t\tu32 channel, bool is_max)\n+{\n+\tu32 reg;\n+\n+\tif (channel == LTC4283_CHAN_VPWR) {\n+\t\tif (is_max)\n+\t\t\treturn ltc4283_write_in_byte(st, LTC4283_VPWR_MAX_TH,\n+\t\t\t\t\t\t LTC4283_ADC2_FS_mV, val);\n+\n+\t\treturn ltc4283_write_in_byte(st, LTC4283_VPWR_MIN_TH,\n+\t\t\t\t\t LTC4283_ADC2_FS_mV, val);\n+\t}\n+\n+\tif (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) {\n+\t\tif (is_max) {\n+\t\t\treg = LTC4283_ADC_2_MAX_TH(channel - LTC4283_CHAN_ADI_1);\n+\t\t\treturn ltc4283_write_in_byte(st, reg,\n+\t\t\t\t\t\t LTC4283_ADC2_FS_mV, val);\n+\t\t}\n+\n+\t\treg = LTC4283_ADC_2_MIN_TH(channel - LTC4283_CHAN_ADI_1);\n+\t\treturn ltc4283_write_in_byte(st, reg, LTC4283_ADC2_FS_mV, val);\n+\t}\n+\n+\tif (is_max) {\n+\t\treg = LTC4283_ADC_2_MAX_TH_DIFF(channel - LTC4283_CHAN_ADIN12);\n+\t\treturn ltc4283_write_in_byte(st, reg, LTC4283_ADC1_FS_uV,\n+\t\t\t\t\t val * MILLI);\n+\t}\n+\n+\treg = LTC4283_ADC_2_MIN_TH_DIFF(channel - LTC4283_CHAN_ADIN12);\n+\treturn ltc4283_write_in_byte(st, reg, LTC4283_ADC1_FS_uV, val * MILLI);\n+}\n+\n+static int ltc4283_write_in(struct ltc4283_hwmon *st, u32 attr, long val,\n+\t\t\t int channel)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_in_max:\n+\t\treturn ltc4283_write_minmax(st, val, channel, true);\n+\tcase hwmon_in_min:\n+\t\treturn ltc4283_write_minmax(st, val, channel, false);\n+\tcase hwmon_in_reset_history:\n+\t\treturn ltc4283_reset_in_hist(st, channel);\n+\tcase hwmon_in_enable:\n+\t\treturn ltc4283_write_in_en(st, channel, !!val);\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static int ltc4283_write_curr_byte(const struct ltc4283_hwmon *st,\n+\t\t\t\t u32 reg, long val)\n+{\n+\tu32 temp = LTC4283_ADC1_FS_uV * DECA * MILLI;\n+\tu32 reg_val, isense_max;\n+\n+\tisense_max = DIV_ROUND_CLOSEST(st->vsense_max * MICRO * DECA, st->rsense);\n+\tval = clamp_val(val, 0, isense_max);\n+\treg_val = DIV_ROUND_CLOSEST_ULL(val * BIT_ULL(8) * st->rsense, temp);\n+\n+\treturn regmap_write(st->map, reg, reg_val);\n+}\n+\n+static int ltc4283_write_curr_history(struct ltc4283_hwmon *st)\n+{\n+\tint ret;\n+\n+\tret = ltc4283_write_in_history(st, LTC4283_SENSE_MIN,\n+\t\t\t\t st->vsense_max * MILLI,\n+\t\t\t\t LTC4283_ADC1_FS_uV);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Now, let's also clear possible overcurrent logs. */\n+\treturn regmap_clear_bits(st->map, LTC4283_FAULT_LOG,\n+\t\t\t\t LTC4283_OC_FAULT_MASK);\n+}\n+\n+static int ltc4283_write_curr(struct ltc4283_hwmon *st, u32 attr, long val)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_curr_max:\n+\t\treturn ltc4283_write_curr_byte(st, LTC4283_SENSE_MAX_TH, val);\n+\tcase hwmon_curr_min:\n+\t\treturn ltc4283_write_curr_byte(st, LTC4283_SENSE_MIN_TH, val);\n+\tcase hwmon_curr_reset_history:\n+\t\treturn ltc4283_write_curr_history(st);\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static int ltc4283_energy_enable_set(struct ltc4283_hwmon *st, long val)\n+{\n+\tint ret;\n+\n+\t/* Setting the bit halts the meter. */\n+\tval = !!val;\n+\tret = regmap_update_bits(st->map, LTC4283_METER_CONTROL,\n+\t\t\t\t LTC4283_METER_HALT_MASK,\n+\t\t\t\t FIELD_PREP(LTC4283_METER_HALT_MASK, !val));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tst->energy_en = val;\n+\n+\treturn 0;\n+}\n+\n+static int ltc4283_write(struct device *dev, enum hwmon_sensor_types type,\n+\t\t\t u32 attr, int channel, long val)\n+{\n+\tstruct ltc4283_hwmon *st = dev_get_drvdata(dev);\n+\n+\tswitch (type) {\n+\tcase hwmon_power:\n+\t\treturn ltc4283_write_power(st, attr, val);\n+\tcase hwmon_in:\n+\t\treturn ltc4283_write_in(st, attr, val, channel);\n+\tcase hwmon_curr:\n+\t\treturn ltc4283_write_curr(st, attr, val);\n+\tcase hwmon_energy:\n+\t\treturn ltc4283_energy_enable_set(st, val);\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static umode_t ltc4283_in_is_visible(const struct ltc4283_hwmon *st,\n+\t\t\t\t u32 attr, int channel)\n+{\n+\t/* If ADIO is set as a GPIO, don´t make it visible. */\n+\tif (channel >= LTC4283_CHAN_ADIO_1 && channel <= LTC4283_CHAN_ADIO_4) {\n+\t\t/* ADIOX pins come at index 0 in the gpio mask. */\n+\t\tchannel -= LTC4283_CHAN_ADIO_1;\n+\t\tif (test_bit(channel, &st->gpio_mask))\n+\t\t\treturn 0;\n+\t}\n+\n+\t/* Also take care of differential channels. */\n+\tif (channel >= LTC4283_CHAN_ADIO12 && channel <= LTC4283_CHAN_ADIO34) {\n+\t\tchannel -= LTC4283_CHAN_ADIO12;\n+\t\t/* If one channel in the pair is used, make it invisible. */\n+\t\tif (test_bit(channel * 2, &st->gpio_mask) ||\n+\t\t test_bit(channel * 2 + 1, &st->gpio_mask))\n+\t\t\treturn 0;\n+\t}\n+\n+\tswitch (attr) {\n+\tcase hwmon_in_input:\n+\tcase hwmon_in_highest:\n+\tcase hwmon_in_lowest:\n+\tcase hwmon_in_max_alarm:\n+\tcase hwmon_in_min_alarm:\n+\tcase hwmon_in_label:\n+\tcase hwmon_in_lcrit_alarm:\n+\tcase hwmon_in_crit_alarm:\n+\tcase hwmon_in_fault:\n+\t\treturn 0444;\n+\tcase hwmon_in_max:\n+\tcase hwmon_in_min:\n+\tcase hwmon_in_enable:\n+\t\treturn 0644;\n+\tcase hwmon_in_reset_history:\n+\t\treturn 0200;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n+static umode_t ltc4283_curr_is_visible(u32 attr)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_curr_input:\n+\tcase hwmon_curr_highest:\n+\tcase hwmon_curr_lowest:\n+\tcase hwmon_curr_max_alarm:\n+\tcase hwmon_curr_min_alarm:\n+\tcase hwmon_curr_crit_alarm:\n+\tcase hwmon_curr_label:\n+\t\treturn 0444;\n+\tcase hwmon_curr_max:\n+\tcase hwmon_curr_min:\n+\t\treturn 0644;\n+\tcase hwmon_curr_reset_history:\n+\t\treturn 0200;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n+static umode_t ltc4283_power_is_visible(u32 attr)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_power_input:\n+\tcase hwmon_power_input_highest:\n+\tcase hwmon_power_input_lowest:\n+\tcase hwmon_power_label:\n+\tcase hwmon_power_max_alarm:\n+\tcase hwmon_power_min_alarm:\n+\t\treturn 0444;\n+\tcase hwmon_power_max:\n+\tcase hwmon_power_min:\n+\t\treturn 0644;\n+\tcase hwmon_power_reset_history:\n+\t\treturn 0200;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n+static umode_t ltc4283_is_visible(const void *data,\n+\t\t\t\t enum hwmon_sensor_types type,\n+\t\t\t\t u32 attr, int channel)\n+{\n+\tswitch (type) {\n+\tcase hwmon_in:\n+\t\treturn ltc4283_in_is_visible(data, attr, channel);\n+\tcase hwmon_curr:\n+\t\treturn ltc4283_curr_is_visible(attr);\n+\tcase hwmon_power:\n+\t\treturn ltc4283_power_is_visible(attr);\n+\tcase hwmon_energy:\n+\t\t/* hwmon_energy_enable */\n+\t\treturn 0644;\n+\tcase hwmon_energy64:\n+\t\t/* hwmon_energy_input */\n+\t\treturn 0444;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n+static const char * const ltc4283_in_strs[] = {\n+\t\"VIN\", \"VPWR\", \"VADI1\", \"VADI2\", \"VADI3\", \"VADI4\", \"VADIO1\", \"VADIO2\",\n+\t\"VADIO3\", \"VADIO4\", \"DRNS\", \"DRAIN\", \"ADIN2-ADIN1\", \"ADIN4-ADIN3\",\n+\t\"ADIO2-ADIO1\", \"ADIO4-ADIO3\"\n+};\n+\n+static int ltc4283_read_labels(struct device *dev,\n+\t\t\t enum hwmon_sensor_types type,\n+\t\t\t u32 attr, int channel, const char **str)\n+{\n+\tswitch (type) {\n+\tcase hwmon_in:\n+\t\t*str = ltc4283_in_strs[channel];\n+\t\treturn 0;\n+\tcase hwmon_curr:\n+\t\t*str = \"ISENSE\";\n+\t\treturn 0;\n+\tcase hwmon_power:\n+\t\t*str = \"Power\";\n+\t\treturn 0;\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+/*\n+ * Set max limits for ISENSE and Power as that depends on the max voltage on\n+ * rsense that is defined in ILIM_ADJUST. This is specially important for power\n+ * because for some rsense and vfsout values, if we allow the default raw 255\n+ * value, that would overflow long in 32bit archs when reading back the max\n+ * power limit.\n+ */\n+static int ltc4283_set_max_limits(struct ltc4283_hwmon *st, struct device *dev)\n+{\n+\tu32 temp = st->vsense_max * DECA * MICRO;\n+\tint ret;\n+\n+\tret = ltc4283_write_in_byte(st, LTC4283_SENSE_MAX_TH, LTC4283_ADC1_FS_uV,\n+\t\t\t\t st->vsense_max * MILLI);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Power is given by ISENSE * Vout. */\n+\tst->power_max = DIV_ROUND_CLOSEST(temp, st->rsense) * LTC4283_ADC2_FS_mV;\n+\treturn ltc4283_write_power_byte(st, LTC4283_POWER_MAX_TH, st->power_max);\n+}\n+\n+static int ltc4283_parse_array_prop(const struct ltc4283_hwmon *st,\n+\t\t\t\t struct device *dev, const char *prop,\n+\t\t\t\t const u32 *vals, u32 n_vals)\n+{\n+\tu32 prop_val;\n+\tint ret;\n+\tu32 i;\n+\n+\tret = device_property_read_u32(dev, prop, &prop_val);\n+\tif (ret)\n+\t\treturn n_vals;\n+\n+\tfor (i = 0; i < n_vals; i++) {\n+\t\tif (prop_val != vals[i])\n+\t\t\tcontinue;\n+\n+\t\treturn i;\n+\t}\n+\n+\treturn dev_err_probe(dev, -EINVAL,\n+\t\t\t \"Invalid %s property value %u, expected one of: %*ph\\n\",\n+\t\t\t prop, prop_val, n_vals, vals);\n+}\n+\n+static int ltc4283_get_defaults(struct ltc4283_hwmon *st)\n+{\n+\tu32 reg_val, ilm_adjust, c;\n+\tint ret;\n+\n+\tret = regmap_read(st->map, LTC4283_METER_CONTROL, ®_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tst->energy_en = !FIELD_GET(LTC4283_METER_HALT_MASK, reg_val);\n+\n+\tret = regmap_read(st->map, LTC4283_CONFIG_1, ®_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tilm_adjust = FIELD_GET(LTC4283_ILIM_MASK, reg_val);\n+\tst->vsense_max = LTC4283_VILIM_MIN_uV / MILLI + ilm_adjust;\n+\n+\tret = regmap_read(st->map, LTC4283_PGIO_CONFIG, ®_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Can be latter overwritten in ltc4283_pgio_config() */\n+\tif (FIELD_GET(LTC4283_PGIO4_CFG_MASK, reg_val) < LTC4283_PGIO_FUNC_GPIO)\n+\t\tst->ext_fault = true;\n+\n+\t/* VPWR and VIN are always enabled */\n+\t__set_bit(LTC4283_CHAN_VIN, &st->ch_enable_mask);\n+\t__set_bit(LTC4283_CHAN_VPWR, &st->ch_enable_mask);\n+\tfor (c = LTC4283_CHAN_ADI_1; c < LTC4283_CHAN_MAX; c++) {\n+\t\tu32 chan = c - LTC4283_CHAN_ADI_1, bit;\n+\n+\t\tret = regmap_read(st->map, LTC4283_ADC_SELECT(chan), ®_val);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tbit = LTC4283_ADC_SELECT_MASK(chan);\n+\t\tif (c > LTC4283_CHAN_DRAIN)\n+\t\t\t/* account for two reserved fields after DRAIN */\n+\t\t\tbit <<= 2;\n+\n+\t\tif (!(bit & reg_val))\n+\t\t\tcontinue;\n+\n+\t\t__set_bit(c, &st->ch_enable_mask);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const char * const ltc4283_pgio1_funcs[] = {\n+\t\"inverted_power_good\", \"power_good\", \"gpio\"\n+};\n+\n+static const char * const ltc4283_pgio2_funcs[] = {\n+\t \"inverted_power_good\", \"power_good\", \"gpio\", \"active_current_limiting\"\n+};\n+\n+static const char * const ltc4283_pgio3_funcs[] = {\n+\t\"inverted_power_good_input\", \"power_good_input\", \"gpio\"\n+};\n+\n+static const char * const ltc4283_pgio4_funcs[] = {\n+\t\"inverted_external_fault\", \"external_fault\", \"gpio\"\n+};\n+\n+enum {\n+\tLTC4283_PIN_ADIO1,\n+\tLTC4283_PIN_ADIO2,\n+\tLTC4283_PIN_ADIO3,\n+\tLTC4283_PIN_ADIO4,\n+\tLTC4283_PIN_PGIO1,\n+\tLTC4283_PIN_PGIO2,\n+\tLTC4283_PIN_PGIO3,\n+\tLTC4283_PIN_PGIO4,\n+};\n+\n+static int ltc4283_pgio_config(struct ltc4283_hwmon *st, struct device *dev)\n+{\n+\tint ret, func;\n+\n+\tfunc = device_property_match_property_string(dev, \"adi,pgio1-func\",\n+\t\t\t\t\t\t ltc4283_pgio1_funcs,\n+\t\t\t\t\t\t ARRAY_SIZE(ltc4283_pgio1_funcs));\n+\tif (func < 0 && func != -EINVAL)\n+\t\treturn dev_err_probe(dev, func,\n+\t\t\t\t \"Invalid adi,pgio1-func property\\n\");\n+\tif (func >= 0) {\n+\t\tif (func == LTC4283_PGIO_FUNC_GPIO) {\n+\t\t\t__set_bit(LTC4283_PIN_PGIO1, &st->gpio_mask);\n+\t\t\t/* If GPIO, default to an input pin. */\n+\t\t\tfunc++;\n+\t\t}\n+\n+\t\tret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG,\n+\t\t\t\t\t LTC4283_PGIO1_CFG_MASK,\n+\t\t\t\t\t FIELD_PREP(LTC4283_PGIO1_CFG_MASK, func));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tfunc = device_property_match_property_string(dev, \"adi,pgio2-func\",\n+\t\t\t\t\t\t ltc4283_pgio2_funcs,\n+\t\t\t\t\t\t ARRAY_SIZE(ltc4283_pgio2_funcs));\n+\n+\tif (func < 0 && func != -EINVAL)\n+\t\treturn dev_err_probe(dev, func,\n+\t\t\t\t \"Invalid adi,pgio2-func property\\n\");\n+\tif (func >= 0) {\n+\t\tif (func != LTC4283_PGIO2_FUNC_ACLB) {\n+\t\t\tif (func == LTC4283_PGIO_FUNC_GPIO) {\n+\t\t\t\t__set_bit(LTC4283_PIN_PGIO2, &st->gpio_mask);\n+\t\t\t\tfunc++;\n+\t\t\t}\n+\n+\t\t\tret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG,\n+\t\t\t\t\t\t LTC4283_PGIO2_CFG_MASK,\n+\t\t\t\t\t\t FIELD_PREP(LTC4283_PGIO2_CFG_MASK, func));\n+\t\t} else {\n+\t\t\tret = regmap_set_bits(st->map, LTC4283_CONTROL_1,\n+\t\t\t\t\t LTC4283_PIGIO2_ACLB_MASK);\n+\t\t}\n+\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tfunc = device_property_match_property_string(dev, \"adi,pgio3-func\",\n+\t\t\t\t\t\t ltc4283_pgio3_funcs,\n+\t\t\t\t\t\t ARRAY_SIZE(ltc4283_pgio3_funcs));\n+\n+\tif (func < 0 && func != -EINVAL)\n+\t\treturn dev_err_probe(dev, func,\n+\t\t\t\t \"Invalid adi,pgio3-func property\\n\");\n+\tif (func >= 0) {\n+\t\tif (func == LTC4283_PGIO_FUNC_GPIO) {\n+\t\t\t__set_bit(LTC4283_PIN_PGIO3, &st->gpio_mask);\n+\t\t\tfunc++;\n+\t\t}\n+\n+\t\tret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG,\n+\t\t\t\t\t LTC4283_PGIO3_CFG_MASK,\n+\t\t\t\t\t FIELD_PREP(LTC4283_PGIO3_CFG_MASK, func));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tfunc = device_property_match_property_string(dev, \"adi,pgio4-func\",\n+\t\t\t\t\t\t ltc4283_pgio4_funcs,\n+\t\t\t\t\t\t ARRAY_SIZE(ltc4283_pgio4_funcs));\n+\n+\tif (func < 0 && func != -EINVAL)\n+\t\treturn dev_err_probe(dev, func,\n+\t\t\t\t \"Invalid adi,pgio4-func property\\n\");\n+\tif (func >= 0) {\n+\t\tif (func == LTC4283_PGIO_FUNC_GPIO) {\n+\t\t\t__set_bit(LTC4283_PIN_PGIO4, &st->gpio_mask);\n+\t\t\tfunc++;\n+\t\t\tst->ext_fault = false;\n+\t\t} else {\n+\t\t\tst->ext_fault = true;\n+\t\t}\n+\n+\t\tret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG,\n+\t\t\t\t\t LTC4283_PGIO4_CFG_MASK,\n+\t\t\t\t\t FIELD_PREP(LTC4283_PGIO4_CFG_MASK, func));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int ltc4283_adio_config(struct ltc4283_hwmon *st, struct device *dev,\n+\t\t\t const char *prop, u32 pin)\n+{\n+\tu32 adc_idx;\n+\tint ret;\n+\n+\tif (!device_property_read_bool(dev, prop))\n+\t\treturn 0;\n+\n+\tadc_idx = LTC4283_CHAN_ADIO_1 - LTC4283_CHAN_ADI_1 + pin;\n+\tret = regmap_clear_bits(st->map, LTC4283_ADC_SELECT(adc_idx),\n+\t\t\t\tLTC4283_ADC_SELECT_MASK(adc_idx));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t__set_bit(pin, &st->gpio_mask);\n+\treturn 0;\n+}\n+\n+static int ltc4283_pin_config(struct ltc4283_hwmon *st, struct device *dev)\n+{\n+\tint ret;\n+\n+\tret = ltc4283_pgio_config(st, dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ltc4283_adio_config(st, dev, \"adi,gpio-on-adio1\", LTC4283_PIN_ADIO1);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ltc4283_adio_config(st, dev, \"adi,gpio-on-adio2\", LTC4283_PIN_ADIO2);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ltc4283_adio_config(st, dev, \"adi,gpio-on-adio3\", LTC4283_PIN_ADIO3);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn ltc4283_adio_config(st, dev, \"adi,gpio-on-adio4\", LTC4283_PIN_ADIO4);\n+}\n+\n+static const char * const ltc4283_oc_fet_retry[] = {\n+\t\"latch-off\", \"1\", \"7\", \"unlimited\"\n+};\n+\n+static const u32 ltc4283_fb_factor[] = {\n+\t100, 50, 20, 10\n+};\n+\n+static const u32 ltc4283_cooling_dl[] = {\n+\t512, 1002, 2005, 4100, 8190, 16400, 32800, 65600\n+};\n+\n+static const u32 ltc4283_fet_bad_delay[] = {\n+\t256, 512, 1002, 2005\n+};\n+\n+static int ltc4283_setup(struct ltc4283_hwmon *st, struct device *dev)\n+{\n+\tu32 val;\n+\tint ret;\n+\n+\t/* The part has an eeprom so let's get the needed defaults from it */\n+\tret = ltc4283_get_defaults(st);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * Default to 1 micro ohm so we can probe without FW properties. Note\n+\t * the below division expects rsense in nano ohms.\n+\t */\n+\tst->rsense = 1 * MILLI;\n+\tret = device_property_read_u32(dev, \"adi,rsense-nano-ohms\",\n+\t\t\t\t &st->rsense);\n+\tif (!ret) {\n+\t\tif (st->rsense < CENTI)\n+\t\t\treturn dev_err_probe(dev, -EINVAL,\n+\t\t\t\t\t \"adi,rsense-nano-ohms too small (< %lu)\\n\",\n+\t\t\t\t\t CENTI);\n+\t}\n+\n+\t/*\n+\t * The resolution for rsense is tenths of micro (eg: 62.5 uOhm) which\n+\t * means we need nano in the bindings. However, to make things easier to\n+\t * handle (with respect to overflows) we divide it by 100 as we don't\n+\t * really need the last two digits.\n+\t */\n+\tst->rsense /= CENTI;\n+\n+\tret = device_property_read_u32(dev, \"adi,current-limit-sense-microvolt\",\n+\t\t\t\t &st->vsense_max);\n+\tif (!ret) {\n+\t\tu32 reg_val;\n+\n+\t\tif (!in_range(st->vsense_max, LTC4283_VILIM_MIN_uV,\n+\t\t\t LTC4283_VILIM_RANGE)) {\n+\t\t\treturn dev_err_probe(dev, -EINVAL,\n+\t\t\t\t\t \"adi,current-limit-sense-microvolt (%u) out of range [%u %u]\\n\",\n+\t\t\t\t\t st->vsense_max, LTC4283_VILIM_MIN_uV,\n+\t\t\t\t\t LTC4283_VILIM_MAX_uV);\n+\t\t}\n+\n+\t\tst->vsense_max /= MILLI;\n+\t\treg_val = FIELD_PREP(LTC4283_ILIM_MASK,\n+\t\t\t\t st->vsense_max - LTC4283_VILIM_MIN_uV / MILLI);\n+\t\tret = regmap_update_bits(st->map, LTC4283_CONFIG_1,\n+\t\t\t\t\t LTC4283_ILIM_MASK, reg_val);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = ltc4283_parse_array_prop(st, dev, \"adi,current-limit-foldback-factor\",\n+\t\t\t\t ltc4283_fb_factor, ARRAY_SIZE(ltc4283_fb_factor));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tif (ret < ARRAY_SIZE(ltc4283_fb_factor)) {\n+\t\tret = regmap_update_bits(st->map, LTC4283_CONFIG_1, LTC4283_FB_MASK,\n+\t\t\t\t\t FIELD_PREP(LTC4283_FB_MASK, ret));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = ltc4283_parse_array_prop(st, dev, \"adi,cooling-delay-ms\",\n+\t\t\t\t ltc4283_cooling_dl, ARRAY_SIZE(ltc4283_cooling_dl));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tif (ret < ARRAY_SIZE(ltc4283_cooling_dl)) {\n+\t\tret = regmap_update_bits(st->map, LTC4283_CONFIG_2, LTC4283_COOLING_DL_MASK,\n+\t\t\t\t\t FIELD_PREP(LTC4283_COOLING_DL_MASK, ret));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = ltc4283_parse_array_prop(st, dev, \"adi,fet-bad-timer-delay-ms\",\n+\t\t\t\t ltc4283_fet_bad_delay, ARRAY_SIZE(ltc4283_fet_bad_delay));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tif (ret < ARRAY_SIZE(ltc4283_fet_bad_delay)) {\n+\t\tret = regmap_update_bits(st->map, LTC4283_CONFIG_2, LTC4283_FTBD_DL_MASK,\n+\t\t\t\t\t FIELD_PREP(LTC4283_FTBD_DL_MASK, ret));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = ltc4283_set_max_limits(st, dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ltc4283_pin_config(st, dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (device_property_read_bool(dev, \"adi,power-good-reset-on-fet\")) {\n+\t\tret = regmap_clear_bits(st->map, LTC4283_CONTROL_1,\n+\t\t\t\t\tLTC4283_PWRGD_RST_CTRL_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,fet-turn-off-disable\")) {\n+\t\tret = regmap_clear_bits(st->map, LTC4283_CONTROL_1,\n+\t\t\t\t\tLTC4283_FET_BAD_OFF_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,tmr-pull-down-disable\")) {\n+\t\tret = regmap_set_bits(st->map, LTC4283_CONTROL_1,\n+\t\t\t\t LTC4283_THERM_TMR_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,dvdt-inrush-control-disable\")) {\n+\t\tret = regmap_clear_bits(st->map, LTC4283_CONTROL_1,\n+\t\t\t\t\tLTC4283_DVDT_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,undervoltage-retry-disable\")) {\n+\t\tret = regmap_clear_bits(st->map, LTC4283_CONTROL_2,\n+\t\t\t\t\tLTC4283_UV_RETRY_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,overvoltage-retry-disable\")) {\n+\t\tret = regmap_clear_bits(st->map, LTC4283_CONTROL_2,\n+\t\t\t\t\tLTC4283_OV_RETRY_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,external-fault-retry-enable\")) {\n+\t\tif (!st->ext_fault)\n+\t\t\treturn dev_err_probe(dev, -EINVAL,\n+\t\t\t\t\t \"adi,external-fault-retry-enable set but PGIO4 not configured\\n\");\n+\t\tret = regmap_set_bits(st->map, LTC4283_CONTROL_2,\n+\t\t\t\t LTC4283_EXT_FAULT_RETRY_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,fault-log-enable\")) {\n+\t\tret = regmap_set_bits(st->map, LTC4283_FAULT_LOG_CTRL,\n+\t\t\t\t LTC4283_FAULT_LOG_EN_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = device_property_match_property_string(dev, \"adi,overcurrent-retries\",\n+\t\t\t\t\t\t ltc4283_oc_fet_retry,\n+\t\t\t\t\t\t ARRAY_SIZE(ltc4283_oc_fet_retry));\n+\t/* We still want to catch when an invalid string is given. */\n+\tif (ret < 0 && ret != -EINVAL)\n+\t\treturn dev_err_probe(dev, ret,\n+\t\t\t\t \"adi,overcurrent-retries invalid value\\n\");\n+\tif (ret >= 0) {\n+\t\tret = regmap_update_bits(st->map, LTC4283_CONTROL_2,\n+\t\t\t\t\t LTC4283_OC_RETRY_MASK,\n+\t\t\t\t\t FIELD_PREP(LTC4283_OC_RETRY_MASK, ret));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = device_property_match_property_string(dev, \"adi,fet-bad-retries\",\n+\t\t\t\t\t\t ltc4283_oc_fet_retry,\n+\t\t\t\t\t\t ARRAY_SIZE(ltc4283_oc_fet_retry));\n+\tif (ret < 0 && ret != -EINVAL)\n+\t\treturn dev_err_probe(dev, ret,\n+\t\t\t\t \"adi,fet-bad-retries invalid value\\n\");\n+\tif (ret >= 0) {\n+\t\tret = regmap_update_bits(st->map, LTC4283_CONTROL_2,\n+\t\t\t\t\t LTC4283_FET_BAD_RETRY_MASK,\n+\t\t\t\t\t FIELD_PREP(LTC4283_FET_BAD_RETRY_MASK, ret));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,external-fault-fet-off-enable\")) {\n+\t\tif (!st->ext_fault)\n+\t\t\treturn dev_err_probe(dev, -EINVAL,\n+\t\t\t\t\t \"adi,external-fault-fet-off-enable set but PGIO4 not configured\\n\");\n+\t\tret = regmap_set_bits(st->map, LTC4283_CONFIG_3,\n+\t\t\t\t LTC4283_EXTFLT_TURN_OFF_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (device_property_read_bool(dev, \"adi,vpower-drns-enable\")) {\n+\t\tu32 chan = LTC4283_CHAN_DRNS - LTC4283_CHAN_ADI_1;\n+\n+\t\t__clear_bit(LTC4283_CHAN_DRNS, &st->ch_enable_mask);\n+\t\t/*\n+\t\t * Then, let's by default disable DRNS from ADC2 given that it\n+\t\t * is already being monitored by the VPWR channel. One can still\n+\t\t * enable it later on if needed.\n+\t\t */\n+\t\tret = regmap_clear_bits(st->map, LTC4283_ADC_SELECT(chan),\n+\t\t\t\t\tLTC4283_ADC_SELECT_MASK(chan));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tval = 1;\n+\t} else {\n+\t\tval = 0;\n+\t}\n+\n+\tret = regmap_update_bits(st->map, LTC4283_CONFIG_3,\n+\t\t\t\t LTC4283_VPWR_DRNS_MASK,\n+\t\t\t\t FIELD_PREP(LTC4283_VPWR_DRNS_MASK, val));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Make sure the ADC has 12bit resolution since we're assuming that. */\n+\tret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG_2,\n+\t\t\t\t LTC4283_ADC_MASK,\n+\t\t\t\t FIELD_PREP(LTC4283_ADC_MASK, 3));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Energy reads (which are 6 byte block reads) rely on page access */\n+\tret = regmap_set_bits(st->map, LTC4283_CONTROL_1, LTC4283_RW_PAGE_MASK);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * Make sure we are integrating power as we only support reporting\n+\t * consumed energy.\n+\t */\n+\treturn regmap_clear_bits(st->map, LTC4283_METER_CONTROL,\n+\t\t\t\t LTC4283_INTEGRATE_I_MASK);\n+}\n+\n+static const struct hwmon_channel_info * const ltc4283_info[] = {\n+\tHWMON_CHANNEL_INFO(in,\n+\t\t\t HWMON_I_LCRIT_ALARM | HWMON_I_CRIT_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_MAX_ALARM | HWMON_I_RESET_HISTORY |\n+\t\t\t HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_FAULT | HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL,\n+\t\t\t HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |\n+\t\t\t HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM |\n+\t\t\t HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM |\n+\t\t\t HWMON_I_ENABLE | HWMON_I_LABEL),\n+\tHWMON_CHANNEL_INFO(curr,\n+\t\t\t HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST |\n+\t\t\t HWMON_C_MAX | HWMON_C_MIN | HWMON_C_MIN_ALARM |\n+\t\t\t HWMON_C_MAX_ALARM | HWMON_C_CRIT_ALARM |\n+\t\t\t HWMON_C_RESET_HISTORY | HWMON_C_LABEL),\n+\tHWMON_CHANNEL_INFO(power,\n+\t\t\t HWMON_P_INPUT | HWMON_P_INPUT_LOWEST |\n+\t\t\t HWMON_P_INPUT_HIGHEST | HWMON_P_MAX | HWMON_P_MIN |\n+\t\t\t HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM |\n+\t\t\t HWMON_P_RESET_HISTORY | HWMON_P_LABEL),\n+\tHWMON_CHANNEL_INFO(energy,\n+\t\t\t HWMON_E_ENABLE),\n+\tHWMON_CHANNEL_INFO(energy64,\n+\t\t\t HWMON_E_INPUT),\n+\tNULL\n+};\n+\n+static const struct hwmon_ops ltc4283_ops = {\n+\t.read = ltc4283_read,\n+\t.write = ltc4283_write,\n+\t.is_visible = ltc4283_is_visible,\n+\t.read_string = ltc4283_read_labels,\n+};\n+\n+static const struct hwmon_chip_info ltc4283_chip_info = {\n+\t.ops = <c4283_ops,\n+\t.info = ltc4283_info,\n+};\n+\n+static int ltc4283_show_fault_log(void *arg, u64 *val, u32 mask)\n+{\n+\tstruct ltc4283_hwmon *st = arg;\n+\tlong alarm;\n+\tint ret;\n+\n+\tret = ltc4283_read_alarm(st, LTC4283_FAULT_LOG, mask, &alarm);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = alarm;\n+\n+\treturn 0;\n+}\n+\n+static int ltc4283_show_in0_lcrit_fault_log(void *arg, u64 *val)\n+{\n+\treturn ltc4283_show_fault_log(arg, val, LTC4283_UV_FAULT_MASK);\n+}\n+DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_in0_lcrit_fault_log,\n+\t\t\t ltc4283_show_in0_lcrit_fault_log, NULL, \"%llu\\n\");\n+\n+static int ltc4283_show_in0_crit_fault_log(void *arg, u64 *val)\n+{\n+\treturn ltc4283_show_fault_log(arg, val, LTC4283_OV_FAULT_MASK);\n+}\n+DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_in0_crit_fault_log,\n+\t\t\t ltc4283_show_in0_crit_fault_log, NULL, \"%llu\\n\");\n+\n+static int ltc4283_show_fet_bad_fault_log(void *arg, u64 *val)\n+{\n+\treturn ltc4283_show_fault_log(arg, val, LTC4283_FET_BAD_FAULT_MASK);\n+}\n+DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_fet_bad_fault_log,\n+\t\t\t ltc4283_show_fet_bad_fault_log, NULL, \"%llu\\n\");\n+\n+static int ltc4283_show_fet_short_fault_log(void *arg, u64 *val)\n+{\n+\treturn ltc4283_show_fault_log(arg, val, LTC4283_FET_SHORT_FAULT_MASK);\n+}\n+DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_fet_short_fault_log,\n+\t\t\t ltc4283_show_fet_short_fault_log, NULL, \"%llu\\n\");\n+\n+static int ltc4283_show_curr1_crit_fault_log(void *arg, u64 *val)\n+{\n+\treturn ltc4283_show_fault_log(arg, val, LTC4283_OC_FAULT_MASK);\n+}\n+DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_curr1_crit_fault_log,\n+\t\t\t ltc4283_show_curr1_crit_fault_log, NULL, \"%llu\\n\");\n+\n+static int ltc4283_show_power1_failed_fault_log(void *arg, u64 *val)\n+{\n+\treturn ltc4283_show_fault_log(arg, val, LTC4283_PWR_FAIL_FAULT_MASK);\n+}\n+DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_power1_failed_fault_log,\n+\t\t\t ltc4283_show_power1_failed_fault_log, NULL, \"%llu\\n\");\n+\n+static int ltc4283_show_power1_good_input_fault_log(void *arg, u64 *val)\n+{\n+\treturn ltc4283_show_fault_log(arg, val, LTC4283_PGI_FAULT_MASK);\n+}\n+DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_power1_good_input_fault_log,\n+\t\t\t ltc4283_show_power1_good_input_fault_log, NULL, \"%llu\\n\");\n+\n+static void ltc4283_debugfs_init(struct ltc4283_hwmon *st, struct i2c_client *i2c)\n+{\n+\tdebugfs_create_file_unsafe(\"in0_crit_fault_log\", 0400, i2c->debugfs, st,\n+\t\t\t\t <c4283_in0_crit_fault_log);\n+\tdebugfs_create_file_unsafe(\"in0_lcrit_fault_log\", 0400, i2c->debugfs, st,\n+\t\t\t\t <c4283_in0_lcrit_fault_log);\n+\tdebugfs_create_file_unsafe(\"in0_fet_bad_fault_log\", 0400, i2c->debugfs, st,\n+\t\t\t\t <c4283_fet_bad_fault_log);\n+\tdebugfs_create_file_unsafe(\"in0_fet_short_fault_log\", 0400, i2c->debugfs, st,\n+\t\t\t\t <c4283_fet_short_fault_log);\n+\tdebugfs_create_file_unsafe(\"curr1_crit_fault_log\", 0400, i2c->debugfs, st,\n+\t\t\t\t <c4283_curr1_crit_fault_log);\n+\tdebugfs_create_file_unsafe(\"power1_failed_fault_log\", 0400, i2c->debugfs, st,\n+\t\t\t\t <c4283_power1_failed_fault_log);\n+\tdebugfs_create_file_unsafe(\"power1_good_input_fault_log\", 0400, i2c->debugfs,\n+\t\t\t\t st, <c4283_power1_good_input_fault_log);\n+}\n+\n+static bool ltc4283_is_word_reg(unsigned int reg)\n+{\n+\treturn reg >= LTC4283_SENSE && reg <= LTC4283_ADIO34_MAX;\n+}\n+\n+static int ltc4283_reg_read(void *context, unsigned int reg, unsigned int *val)\n+{\n+\tstruct i2c_client *client = context;\n+\tint ret;\n+\n+\tif (ltc4283_is_word_reg(reg))\n+\t\tret = i2c_smbus_read_word_swapped(client, reg);\n+\telse\n+\t\tret = i2c_smbus_read_byte_data(client, reg);\n+\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\t*val = ret;\n+\treturn 0;\n+}\n+\n+static int ltc4283_reg_write(void *context, unsigned int reg, unsigned int val)\n+{\n+\tstruct i2c_client *client = context;\n+\n+\tif (ltc4283_is_word_reg(reg))\n+\t\treturn i2c_smbus_write_word_swapped(client, reg, val);\n+\n+\treturn i2c_smbus_write_byte_data(client, reg, val);\n+}\n+\n+static const struct regmap_bus ltc4283_regmap_bus = {\n+\t.reg_read = ltc4283_reg_read,\n+\t.reg_write = ltc4283_reg_write,\n+};\n+\n+static bool ltc4283_writable_reg(struct device *dev, unsigned int reg)\n+{\n+\tswitch (reg) {\n+\tcase LTC4283_SYSTEM_STATUS ... LTC4283_FAULT_STATUS:\n+\t\treturn false;\n+\tcase LTC4283_RESERVED_OC:\n+\t\treturn false;\n+\tcase LTC4283_RESERVED_86 ... LTC4283_RESERVED_8F:\n+\t\treturn false;\n+\tcase LTC4283_RESERVED_91 ... LTC4283_RESERVED_A1:\n+\t\treturn false;\n+\tcase LTC4283_RESERVED_A3:\n+\t\treturn false;\n+\tcase LTC4283_RESERVED_AC:\n+\t\treturn false;\n+\tcase LTC4283_POWER_PLAY_MSB ... LTC4283_POWER_PLAY_LSB:\n+\t\treturn false;\n+\tcase LTC4283_RESERVED_F1 ... LTC4283_RESERVED_FF:\n+\t\treturn false;\n+\tdefault:\n+\t\treturn true;\n+\t}\n+}\n+\n+static const struct regmap_config ltc4283_regmap_config = {\n+\t.reg_bits = 8,\n+\t.val_bits = 16,\n+\t.max_register = 0xFF,\n+\t.writeable_reg = ltc4283_writable_reg,\n+};\n+\n+static int ltc4283_probe(struct i2c_client *client)\n+{\n+\tstruct device *dev = &client->dev, *hwmon;\n+\tstruct auxiliary_device *adev;\n+\tstruct ltc4283_hwmon *st;\n+\tint ret;\n+\n+\tst = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);\n+\tif (!st)\n+\t\treturn -ENOMEM;\n+\n+\tif (!i2c_check_functionality(client->adapter,\n+\t\t\t\t I2C_FUNC_SMBUS_BYTE_DATA |\n+\t\t\t\t I2C_FUNC_SMBUS_WORD_DATA |\n+\t\t\t\t I2C_FUNC_SMBUS_READ_I2C_BLOCK))\n+\t\treturn -EOPNOTSUPP;\n+\n+\tst->client = client;\n+\tst->map = devm_regmap_init(dev, <c4283_regmap_bus, client,\n+\t\t\t\t <c4283_regmap_config);\n+\tif (IS_ERR(st->map))\n+\t\treturn dev_err_probe(dev, PTR_ERR(st->map),\n+\t\t\t\t \"Failed to create regmap\\n\");\n+\n+\tret = ltc4283_setup(st, dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\thwmon = devm_hwmon_device_register_with_info(dev, \"ltc4283\", st,\n+\t\t\t\t\t\t <c4283_chip_info, NULL);\n+\n+\tif (IS_ERR(hwmon))\n+\t\treturn PTR_ERR(hwmon);\n+\n+\tltc4283_debugfs_init(st, client);\n+\n+\tif (!st->gpio_mask)\n+\t\treturn 0;\n+\n+\tadev = devm_auxiliary_device_create(dev, \"gpio\", &st->gpio_mask);\n+\tif (!adev)\n+\t\treturn dev_err_probe(dev, -ENODEV, \"Failed to add GPIO device\\n\");\n+\n+\treturn 0;\n+}\n+\n+static const struct of_device_id ltc4283_of_match[] = {\n+\t{ .compatible = \"adi,ltc4283\" },\n+\t{ }\n+};\n+\n+static const struct i2c_device_id ltc4283_i2c_id[] = {\n+\t{ \"ltc4283\" },\n+\t{ }\n+};\n+MODULE_DEVICE_TABLE(i2c, ltc4283_i2c_id);\n+\n+static struct i2c_driver ltc4283_driver = {\n+\t.driver\t= {\n+\t\t.name = \"ltc4283\",\n+\t\t.of_match_table = ltc4283_of_match,\n+\t},\n+\t.probe = ltc4283_probe,\n+\t.id_table = ltc4283_i2c_id,\n+};\n+module_i2c_driver(ltc4283_driver);\n+\n+MODULE_AUTHOR(\"Nuno Sá <nuno.sa@analog.com>\");\n+MODULE_DESCRIPTION(\"LTC4283 Hot Swap Controller driver\");\n+MODULE_LICENSE(\"GPL\");\n", "prefixes": [ "v8", "2/3" ] }