get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2218219/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2218219,
    "url": "http://patchwork.ozlabs.org/api/patches/2218219/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260331171612.102018-6-o.rempel@pengutronix.de/",
    "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": "<20260331171612.102018-6-o.rempel@pengutronix.de>",
    "list_archive_url": null,
    "date": "2026-03-31T17:16:11",
    "name": "[v9,5/6] hwmon: add NXP MC33978/MC34978 driver",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "2b430c4dd4587291323dfae0da2a926e41a712e1",
    "submitter": {
        "id": 71360,
        "url": "http://patchwork.ozlabs.org/api/people/71360/?format=api",
        "name": "Oleksij Rempel",
        "email": "o.rempel@pengutronix.de"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260331171612.102018-6-o.rempel@pengutronix.de/mbox/",
    "series": [
        {
            "id": 498233,
            "url": "http://patchwork.ozlabs.org/api/series/498233/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/list/?series=498233",
            "date": "2026-03-31T17:16:09",
            "name": "mfd: Add support for NXP MC33978/MC34978 MSDI",
            "version": 9,
            "mbox": "http://patchwork.ozlabs.org/series/498233/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2218219/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2218219/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "\n <linux-gpio+bounces-34492-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 spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c09:e001:a7::12fc:5321; helo=sto.lore.kernel.org;\n envelope-from=linux-gpio+bounces-34492-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)",
            "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=185.203.201.7",
            "smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=pengutronix.de",
            "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=pengutronix.de"
        ],
        "Received": [
            "from sto.lore.kernel.org (sto.lore.kernel.org\n [IPv6:2600:3c09:e001:a7::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4flZkC2jsBz1y1q\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 01 Apr 2026 04:24:51 +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 A87BE30233D7\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 31 Mar 2026 17:17:19 +0000 (UTC)",
            "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id D4CAE3644CC;\n\tTue, 31 Mar 2026 17:16:36 +0000 (UTC)",
            "from metis.whiteo.stw.pengutronix.de\n (metis.whiteo.stw.pengutronix.de [185.203.201.7])\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 87521346A1E\n\tfor <linux-gpio@vger.kernel.org>; Tue, 31 Mar 2026 17:16:34 +0000 (UTC)",
            "from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2])\n\tby metis.whiteo.stw.pengutronix.de with esmtps\n (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)\n\t(Exim 4.92)\n\t(envelope-from <ore@pengutronix.de>)\n\tid 1w7chK-0004mP-Dv; Tue, 31 Mar 2026 19:16:14 +0200",
            "from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac]\n helo=dude04)\n\tby drehscheibe.grey.stw.pengutronix.de with esmtps  (TLS1.3) tls\n TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n\t(Exim 4.96)\n\t(envelope-from <ore@pengutronix.de>)\n\tid 1w7chJ-0034jT-23;\n\tTue, 31 Mar 2026 19:16:13 +0200",
            "from ore by dude04 with local (Exim 4.98.2)\n\t(envelope-from <ore@pengutronix.de>)\n\tid 1w7chJ-00000000QZP-2ERw;\n\tTue, 31 Mar 2026 19:16:13 +0200"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1774977396; cv=none;\n b=MP/Ccujukufv7+Ozyt+xbVMnJQJi+VKLTOnpkIzhJNUS4SgmWFonbJAQdbgjUL/d3iyqYUOtAauCzFV4x3u8ovQR7dq7izBxiKY9JtbecObDmkQazdvG+v2X5PAY7EanNoQyHPPEBlxJQTT2lgsMDzdJvByp7mV6xhtq10uGnWE=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1774977396; c=relaxed/simple;\n\tbh=zRaj8lz0aiSaJ9ViZNjk+vm8IaFsxsOe5Vi3He5z+Rc=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=d04uJBMOdZwL17ee2X8eVKz80UdkGMYQztNjhOix6ugzG1o/AB4zQRof/1/rLUU5LE9vof7ffYWh5T6k1c5hoVYSembZu3+SUjoL0+OUxmMbzyUsDpAaDfigIDh9A/ROYGm3COEL5uZuiMfi9ROeiYCUg+i0L07lMtth7/KKwRA=",
        "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=pengutronix.de;\n spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7",
        "From": "Oleksij Rempel <o.rempel@pengutronix.de>",
        "To": "Guenter Roeck <linux@roeck-us.net>,\n\tRob Herring <robh@kernel.org>,\n\tKrzysztof Kozlowski <krzk+dt@kernel.org>,\n\tConor Dooley <conor+dt@kernel.org>,\n\tLee Jones <lee@kernel.org>,\n\tPeter Rosin <peda@axentia.se>,\n\tLinus Walleij <linusw@kernel.org>",
        "Cc": "Oleksij Rempel <o.rempel@pengutronix.de>,\n\tkernel@pengutronix.de,\n\tlinux-kernel@vger.kernel.org,\n\tdevicetree@vger.kernel.org,\n\tlinux-hwmon@vger.kernel.org,\n\tlinux-gpio@vger.kernel.org,\n\tDavid Jander <david@protonic.nl>",
        "Subject": "[PATCH v9 5/6] hwmon: add NXP MC33978/MC34978 driver",
        "Date": "Tue, 31 Mar 2026 19:16:11 +0200",
        "Message-ID": "<20260331171612.102018-6-o.rempel@pengutronix.de>",
        "X-Mailer": "git-send-email 2.47.3",
        "In-Reply-To": "<20260331171612.102018-1-o.rempel@pengutronix.de>",
        "References": "<20260331171612.102018-1-o.rempel@pengutronix.de>",
        "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-Transfer-Encoding": "8bit",
        "X-SA-Exim-Connect-IP": "2a0a:edc0:0:c01:1d::a2",
        "X-SA-Exim-Mail-From": "ore@pengutronix.de",
        "X-SA-Exim-Scanned": "No (on metis.whiteo.stw.pengutronix.de);\n SAEximRunCond expanded to false",
        "X-PTX-Original-Recipient": "linux-gpio@vger.kernel.org"
    },
    "content": "Add hardware monitoring support for the NXP MC33978/MC34978 Multiple\nSwitch Detection Interface (MSDI).\n\nThe hardware utilizes a clear-on-read FAULT register, but physical\nfaults remain asserted as long as the underlying condition exists. This\nasserts a global FAULT_STAT bit on the SPI bus. To handle this without\ntrapping the CPU in an interrupt storm, this driver implements the\nfollowing architecture:\n- Requests a rising-edge nested IRQ (IRQF_TRIGGER_RISING) from the MFD\n  core to catch the initial 0 -> 1 transition of the global fault state.\n- Caches hwmon-specific alarm bits and calculates state edges (XOR) to\n  isolate alarm transitions from system integrity faults.\n- Implements a 1Hz delayed workqueue that polls the hardware as long as\n  any alarm is active. This compensates for the edge-triggered IRQ by\n  discovering secondary faults that occur without a rising edge, and\n  detecting when the hardware clears.\n\nSigned-off-by: Oleksij Rempel <o.rempel@pengutronix.de>\nAcked-by: Guenter Roeck <linux@roeck-us.net>\n---\nchanges v9:\n- add Acked-by: Guenter Roeck <linux@roeck-us.net>\nchanges v8:\n- no changes\nchanges v7:\n- Fix fault monitoring stall by unconditionally rearming on SPI read\n  errors.\n- Fix use-after-free race during unbind by correcting devm registration\n  order.\nchanges v6:\n- Protect clear-on-read FAULT register and state updates with hwmon_lock().\n- Isolate hwmon alarm bits from system integrity bits to fix edge detection.\n- Log system faults (SPI/HASH) as level-triggered and add temperature warning\n  logs.\n- Refactor sysfs read callback into smaller subsystem-specific helpers.\n- Fix probe race condition by calling mc33978_hwmon_update_faults() at the end\n  of probe instead of reading raw faults early.\n- Expose static datasheet temperature limits via temp1_rated_min and\n  temp1_rated_max\n- Introduce variant-specific hw_info data to correctly report the max\n  temperature\n- Add a 1Hz delayed workqueue that polls the SPI bus while any alarm is active.\nchanges v5:\n- no changes\nchanges v4:\n- no changes\nchanges v3:\n- no changes\nchanges v2:\n- Switch from OF match table to platform_device_id\n---\n drivers/hwmon/Kconfig         |  10 +\n drivers/hwmon/Makefile        |   1 +\n drivers/hwmon/mc33978-hwmon.c | 548 ++++++++++++++++++++++++++++++++++\n 3 files changed, 559 insertions(+)\n create mode 100644 drivers/hwmon/mc33978-hwmon.c",
    "diff": "diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig\nindex 328867242cb3..0c52e8268a20 100644\n--- a/drivers/hwmon/Kconfig\n+++ b/drivers/hwmon/Kconfig\n@@ -700,6 +700,16 @@ config SENSORS_MC13783_ADC\n         help\n           Support for the A/D converter on MC13783 and MC13892 PMIC.\n \n+config SENSORS_MC33978\n+\ttristate \"NXP MC33978/MC34978 fault monitoring\"\n+\tdepends on MFD_MC33978\n+\thelp\n+\t  If you say yes here you get fault monitoring support for the\n+\t  NXP MC33978/MC34978 Multiple Switch Detection Interface (MSDI).\n+\n+\t  This driver can also be built as a module. If so, the module\n+\t  will be called mc33978-hwmon.\n+\n config SENSORS_MC33XS2410\n \ttristate \"MC33XS2410 HWMON support\"\n \tdepends on PWM_MC33XS2410\ndiff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile\nindex 5833c807c688..4c3db5433a10 100644\n--- a/drivers/hwmon/Makefile\n+++ b/drivers/hwmon/Makefile\n@@ -167,6 +167,7 @@ obj-$(CONFIG_SENSORS_MAX31790)\t+= max31790.o\n obj-$(CONFIG_MAX31827) += max31827.o\n obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o\n obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o\n+obj-$(CONFIG_SENSORS_MC33978)\t+= mc33978-hwmon.o\n obj-$(CONFIG_SENSORS_MC33XS2410) += mc33xs2410_hwmon.o\n obj-$(CONFIG_SENSORS_MC34VR500)\t+= mc34vr500.o\n obj-$(CONFIG_SENSORS_MCP3021)\t+= mcp3021.o\ndiff --git a/drivers/hwmon/mc33978-hwmon.c b/drivers/hwmon/mc33978-hwmon.c\nnew file mode 100644\nindex 000000000000..0333c0315e06\n--- /dev/null\n+++ b/drivers/hwmon/mc33978-hwmon.c\n@@ -0,0 +1,548 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+// Copyright (c) 2026 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>\n+/*\n+ * MC33978/MC34978 Hardware Monitor Driver\n+ *\n+ * Fault handling model:\n+ *\n+ * The FAULT register is clear-on-read for most bits, but persistent fault\n+ * conditions remain asserted. The MFD core only harvests the aggregate\n+ * FAULT_STAT indication from SPI responses and dispatches the hwmon child\n+ * IRQ on that basis. Because a persistent fault can keep FAULT_STAT asserted,\n+ * secondary fault assertions and fault clear events may not generate a fresh\n+ * interrupt edge visible to the hwmon child.\n+ *\n+ * To provide stable hwmon alarm state, this driver:\n+ * - caches only hwmon-relevant alarm bits\n+ * - serializes FAULT register reads with cache updates\n+ * - polls while any alarm remains active to detect secondary alarms and\n+ *   clearing edges\n+ *\n+ * Raw integrity bits such as SPI_ERROR and HASH are logged, but are not\n+ * exported through hwmon alarm attributes.\n+ */\n+\n+#include <linux/device.h>\n+#include <linux/err.h>\n+#include <linux/hwmon.h>\n+#include <linux/interrupt.h>\n+#include <linux/mod_devicetable.h>\n+#include <linux/module.h>\n+#include <linux/platform_device.h>\n+#include <linux/regmap.h>\n+\n+#include <linux/mfd/mc33978.h>\n+\n+/* Operating Temperature Ranges (Datasheet Rated) */\n+#define MC33978_TEMP_MIN_MC\t\t(-40000)\n+#define MC33978_TEMP_MAX_MC\t\t125000\n+#define MC34978_TEMP_MAX_MC\t\t105000\n+\n+/* Thermal Warning threshold (~120C) */\n+#define MC33978_TEMP_WARN_MC\t\t120000\n+\n+/* Thermal Limit / tLIM (>155C) - Hardware enters CWET throttling */\n+#define MC33978_TEMP_CRIT_MC\t\t155000\n+\n+/* Hysteresis for tLIM recovery (Silicon must cool to <140C) */\n+#define MC33978_TEMP_HYST_MC\t\t15000\n+\n+/* VBATP (in0) IC Level thresholds */\n+#define MC33978_VBATP_OV_MV\t\t36000 /* Overvoltage limit */\n+#define MC33978_VBATP_FUNC_MV\t\t28000 /* Functional/Normal boundary */\n+#define MC33978_VBATP_DEGRADED_MV\t6000 /* Degraded parametrics start */\n+#define MC33978_VBATP_UVLO_MV\t\t4500 /* UV Rising Threshold max */\n+\n+/* VDDQ (in1) Logic Supply thresholds */\n+#define MC33978_VDDQ_MAX_MV\t\t5250 /* Operating Condition max */\n+#define MC33978_VDDQ_MIN_MV\t\t3000 /* Operating Condition min */\n+#define MC33978_VDDQ_UV_MV\t\t2800 /* UV Falling Threshold max */\n+\n+#define MC33978_FAULT_POLL_INTERVAL_MS\t1000\n+\n+enum mc33978_hwmon_in_channels {\n+\tMC33978_IN_VBATP,\n+\tMC33978_IN_VDDQ,\n+};\n+\n+struct mc33978_hwmon_priv {\n+\tstruct device *dev;\n+\tstruct device *hwmon_dev;\n+\tstruct regmap *map;\n+\n+\tconst struct mc33978_hwmon_hw_info *hw_info;\n+\n+\tint fault_irq;\n+\n+\t/* Cached hwmon alarm bits, serialized by hwmon_lock(). */\n+\tu32 last_faults;\n+\n+\t/*\n+\t * Background polling worker. Active only when faults are present\n+\t * to compensate for the lack of clearing/secondary edge interrupts.\n+\t */\n+\tstruct delayed_work poll_work;\n+};\n+\n+struct mc33978_hwmon_hw_info {\n+\tlong rated_max_temp;\n+};\n+\n+static const struct mc33978_hwmon_hw_info hwmon_hwinfo_mc33978 = {\n+\t.rated_max_temp = MC33978_TEMP_MAX_MC,\n+};\n+\n+static const struct mc33978_hwmon_hw_info hwmon_hwinfo_mc34978 = {\n+\t.rated_max_temp = MC34978_TEMP_MAX_MC,\n+};\n+\n+static int mc33978_hwmon_read_fault(struct mc33978_hwmon_priv *priv,\n+\t\t\t\t    u32 *faults)\n+{\n+\tunsigned int val;\n+\tint ret;\n+\n+\tret = regmap_read(priv->map, MC33978_REG_FAULT, &val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*faults = val;\n+\n+\treturn 0;\n+}\n+\n+static void mc33978_hwmon_report_faults(struct mc33978_hwmon_priv *priv,\n+\t\t\t\t\tu32 new_faults)\n+{\n+\tif (!new_faults)\n+\t\treturn;\n+\n+\tif (new_faults & MC33978_FAULT_TEMP_WARN)\n+\t\tdev_warn_ratelimited(priv->dev, \"Temperature warning threshold reached\\n\");\n+\n+\tif (new_faults & MC33978_FAULT_OT)\n+\t\tdev_crit_ratelimited(priv->dev, \"Over-temperature fault detected!\\n\");\n+\n+\tif (new_faults & MC33978_FAULT_OV)\n+\t\tdev_crit_ratelimited(priv->dev, \"Over-voltage fault detected!\\n\");\n+\n+\tif (new_faults & MC33978_FAULT_UV)\n+\t\tdev_err_ratelimited(priv->dev, \"Under-voltage fault detected!\\n\");\n+}\n+\n+static int mc33978_hwmon_update_faults(struct mc33978_hwmon_priv *priv)\n+{\n+\tu32 old_faults, new_faults, changed_faults;\n+\tu32 alarm_faults = 0;\n+\tu32 faults = 0;\n+\tbool rearm;\n+\tint ret;\n+\n+\t/*\n+\t * Serialize clear-on-read FAULT register access with cached alarm state\n+\t * updates and hwmon sysfs readers.\n+\t */\n+\thwmon_lock(priv->hwmon_dev);\n+\told_faults = priv->last_faults;\n+\n+\tret = mc33978_hwmon_read_fault(priv, &faults);\n+\tif (ret) {\n+\t\thwmon_unlock(priv->hwmon_dev);\n+\t\tdev_err_ratelimited(priv->dev,\n+\t\t\t\t    \"failed to read fault register: %pe\\n\",\n+\t\t\t\t    ERR_PTR(ret));\n+\t\t/*\n+\t\t * Always retry on read failure. If we drop the heartbeat during\n+\t\t * the initial fault before caching it, the edge-triggered IRQ\n+\t\t * will never fire again and permanently stall fault monitoring.\n+\t\t */\n+\t\trearm = true;\n+\t\tgoto out_poll;\n+\t}\n+\n+\t/* Isolate hwmon alarm bits from system integrity bits */\n+\talarm_faults = faults & MC33978_FAULT_ALARM_MASK;\n+\tchanged_faults = alarm_faults ^ old_faults;\n+\tnew_faults = alarm_faults & ~old_faults;\n+\tpriv->last_faults = alarm_faults;\n+\n+\thwmon_unlock(priv->hwmon_dev);\n+\n+\tif (faults & MC33978_FAULT_SPI_ERROR)\n+\t\tdev_err_ratelimited(priv->dev, \"SPI communication error detected\\n\");\n+\tif (faults & MC33978_FAULT_HASH)\n+\t\tdev_err_ratelimited(priv->dev, \"SPI register hash mismatch detected\\n\");\n+\n+\tif (new_faults)\n+\t\tmc33978_hwmon_report_faults(priv, new_faults);\n+\n+\tif (changed_faults & MC33978_FAULT_UV)\n+\t\thwmon_notify_event(priv->hwmon_dev, hwmon_in,\n+\t\t\t\t   hwmon_in_lcrit_alarm, MC33978_IN_VBATP);\n+\n+\tif (changed_faults & MC33978_FAULT_OV)\n+\t\thwmon_notify_event(priv->hwmon_dev, hwmon_in,\n+\t\t\t\t   hwmon_in_crit_alarm, MC33978_IN_VBATP);\n+\n+\tif (changed_faults & MC33978_FAULT_TEMP_WARN)\n+\t\thwmon_notify_event(priv->hwmon_dev, hwmon_temp,\n+\t\t\t\t   hwmon_temp_max_alarm, 0);\n+\n+\tif (changed_faults & MC33978_FAULT_OT)\n+\t\thwmon_notify_event(priv->hwmon_dev, hwmon_temp,\n+\t\t\t\t   hwmon_temp_crit_alarm, 0);\n+\n+\tif (changed_faults)\n+\t\thwmon_notify_event(priv->hwmon_dev, hwmon_chip,\n+\t\t\t\t   hwmon_chip_alarms, 0);\n+\n+\trearm = !!alarm_faults;\n+\n+out_poll:\n+\t/*\n+\t * If any alarms are currently active, the global FAULT_STAT bit remains\n+\t * asserted. The hardware will not generate a new rising edge interrupt\n+\t * if a secondary fault occurs, nor will it interrupt when faults clear.\n+\t * Schedule a poll to detect both clearing edges and secondary alarms.\n+\t */\n+\tif (rearm)\n+\t\tmod_delayed_work(system_wq, &priv->poll_work,\n+\t\t\t\t msecs_to_jiffies(MC33978_FAULT_POLL_INTERVAL_MS));\n+\n+\treturn ret;\n+}\n+\n+static irqreturn_t mc33978_hwmon_fault_irq(int irq, void *data)\n+{\n+\tstruct mc33978_hwmon_priv *priv = data;\n+\n+\tmc33978_hwmon_update_faults(priv);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static void mc33978_hwmon_poll_work(struct work_struct *work)\n+{\n+\tstruct mc33978_hwmon_priv *priv =\n+\t\tcontainer_of(work, struct mc33978_hwmon_priv, poll_work.work);\n+\n+\tmc33978_hwmon_update_faults(priv);\n+}\n+\n+static umode_t mc33978_hwmon_is_visible(const void *data,\n+\t\t\t\t\tenum hwmon_sensor_types type,\n+\t\t\t\t\tu32 attr, int channel)\n+{\n+\tswitch (type) {\n+\tcase hwmon_chip:\n+\t\tif (attr == hwmon_chip_alarms)\n+\t\t\treturn 0444;\n+\t\tbreak;\n+\n+\tcase hwmon_temp:\n+\t\tswitch (attr) {\n+\t\tcase hwmon_temp_max:\n+\t\tcase hwmon_temp_crit:\n+\t\tcase hwmon_temp_crit_hyst:\n+\t\tcase hwmon_temp_max_alarm:\n+\t\tcase hwmon_temp_crit_alarm:\n+\t\tcase hwmon_temp_rated_min:\n+\t\tcase hwmon_temp_rated_max:\n+\t\t\treturn 0444;\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\t\tbreak;\n+\n+\tcase hwmon_in:\n+\t\tswitch (attr) {\n+\t\tcase hwmon_in_label:\n+\t\tcase hwmon_in_max:\n+\t\tcase hwmon_in_min:\n+\t\tcase hwmon_in_lcrit:\n+\t\t\treturn 0444;\n+\t\tcase hwmon_in_crit:\n+\t\t\tif (channel == MC33978_IN_VBATP)\n+\t\t\t\treturn 0444;\n+\t\t\tbreak;\n+\t\tcase hwmon_in_crit_alarm:\n+\t\tcase hwmon_in_lcrit_alarm:\n+\t\t\tif (channel == MC33978_IN_VBATP)\n+\t\t\t\treturn 0444;\n+\t\t\tbreak;\n+\t\t}\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int mc33978_hwmon_read_chip(struct mc33978_hwmon_priv *priv, u32 attr,\n+\t\t\t\t   long *val)\n+{\n+\tif (attr == hwmon_chip_alarms) {\n+\t\t*val = priv->last_faults;\n+\t\treturn 0;\n+\t}\n+\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int mc33978_hwmon_read_in_vbatp(struct mc33978_hwmon_priv *priv,\n+\t\t\t\t       u32 attr, long *val)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_in_crit:\n+\t\t*val = MC33978_VBATP_OV_MV;\n+\t\treturn 0;\n+\tcase hwmon_in_max:\n+\t\t*val = MC33978_VBATP_FUNC_MV;\n+\t\treturn 0;\n+\tcase hwmon_in_min:\n+\t\t*val = MC33978_VBATP_DEGRADED_MV;\n+\t\treturn 0;\n+\tcase hwmon_in_lcrit:\n+\t\t*val = MC33978_VBATP_UVLO_MV;\n+\t\treturn 0;\n+\tcase hwmon_in_crit_alarm:\n+\t\t*val = !!(priv->last_faults & MC33978_FAULT_OV);\n+\t\treturn 0;\n+\tcase hwmon_in_lcrit_alarm:\n+\t\t*val = !!(priv->last_faults & MC33978_FAULT_UV);\n+\t\treturn 0;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int mc33978_hwmon_read_in_vddq(u32 attr, long *val)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_in_max:\n+\t\t*val = MC33978_VDDQ_MAX_MV;\n+\t\treturn 0;\n+\tcase hwmon_in_min:\n+\t\t*val = MC33978_VDDQ_MIN_MV;\n+\t\treturn 0;\n+\tcase hwmon_in_lcrit:\n+\t\t*val = MC33978_VDDQ_UV_MV;\n+\t\treturn 0;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int mc33978_hwmon_read_in(struct mc33978_hwmon_priv *priv, u32 attr,\n+\t\t\t\t int channel, long *val)\n+{\n+\tswitch (channel) {\n+\tcase MC33978_IN_VBATP:\n+\t\treturn mc33978_hwmon_read_in_vbatp(priv, attr, val);\n+\tcase MC33978_IN_VDDQ:\n+\t\treturn mc33978_hwmon_read_in_vddq(attr, val);\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int mc33978_hwmon_read_temp(struct mc33978_hwmon_priv *priv, u32 attr,\n+\t\t\t\t   long *val)\n+{\n+\tswitch (attr) {\n+\tcase hwmon_temp_max:\n+\t\t*val = MC33978_TEMP_WARN_MC;\n+\t\treturn 0;\n+\tcase hwmon_temp_crit:\n+\t\t*val = MC33978_TEMP_CRIT_MC;\n+\t\treturn 0;\n+\tcase hwmon_temp_crit_hyst:\n+\t\t*val = MC33978_TEMP_CRIT_MC - MC33978_TEMP_HYST_MC;\n+\t\treturn 0;\n+\tcase hwmon_temp_max_alarm:\n+\t\t*val = !!(priv->last_faults & MC33978_FAULT_TEMP_WARN);\n+\t\treturn 0;\n+\tcase hwmon_temp_crit_alarm:\n+\t\t*val = !!(priv->last_faults & MC33978_FAULT_OT);\n+\t\treturn 0;\n+\tcase hwmon_temp_rated_min:\n+\t\t*val = MC33978_TEMP_MIN_MC;\n+\t\treturn 0;\n+\tcase hwmon_temp_rated_max:\n+\t\t*val = priv->hw_info->rated_max_temp;\n+\t\treturn 0;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int mc33978_hwmon_read(struct device *dev,\n+\t\t\t      enum hwmon_sensor_types type,\n+\t\t\t      u32 attr, int channel, long *val)\n+{\n+\tstruct mc33978_hwmon_priv *priv = dev_get_drvdata(dev);\n+\n+\tswitch (type) {\n+\tcase hwmon_chip:\n+\t\treturn mc33978_hwmon_read_chip(priv, attr, val);\n+\tcase hwmon_in:\n+\t\treturn mc33978_hwmon_read_in(priv, attr, channel, val);\n+\tcase hwmon_temp:\n+\t\treturn mc33978_hwmon_read_temp(priv, attr, val);\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int mc33978_hwmon_read_string(struct device *dev,\n+\t\t\t\t     enum hwmon_sensor_types type,\n+\t\t\t\t     u32 attr, int channel, const char **str)\n+{\n+\t/* Only in_label is supported for string reads */\n+\tif (type != hwmon_in || attr != hwmon_in_label)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tswitch (channel) {\n+\tcase MC33978_IN_VBATP:\n+\t\t*str = \"VBATP\";\n+\t\treturn 0;\n+\tcase MC33978_IN_VDDQ:\n+\t\t*str = \"VDDQ\";\n+\t\treturn 0;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+}\n+\n+static const struct hwmon_channel_info * const mc33978_hwmon_info[] = {\n+\tHWMON_CHANNEL_INFO(chip,\n+\t\t\t   HWMON_C_ALARMS),\n+\tHWMON_CHANNEL_INFO(temp,\n+\t\t\t   HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST |\n+\t\t\t   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM |\n+\t\t\t   HWMON_T_RATED_MIN | HWMON_T_RATED_MAX),\n+\tHWMON_CHANNEL_INFO(in,\n+\t\t\t   /* Index 0: MC33978_IN_VBATP */\n+\t\t\t   HWMON_I_LABEL | HWMON_I_CRIT | HWMON_I_MAX |\n+\t\t\t   HWMON_I_MIN | HWMON_I_LCRIT |\n+\t\t\t   HWMON_I_CRIT_ALARM | HWMON_I_LCRIT_ALARM,\n+\n+\t\t\t   /* Index 1: MC33978_IN_VDDQ */\n+\t\t\t   HWMON_I_LABEL | HWMON_I_MAX | HWMON_I_MIN |\n+\t\t\t   HWMON_I_LCRIT),\n+\tNULL\n+};\n+\n+static const struct hwmon_ops mc33978_hwmon_ops = {\n+\t.is_visible = mc33978_hwmon_is_visible,\n+\t.read_string = mc33978_hwmon_read_string,\n+\t.read = mc33978_hwmon_read,\n+};\n+\n+static const struct hwmon_chip_info mc33978_hwmon_chip_info = {\n+\t.ops = &mc33978_hwmon_ops,\n+\t.info = mc33978_hwmon_info,\n+};\n+\n+static void mc33978_hwmon_action_cancel_work(void *data)\n+{\n+\tstruct mc33978_hwmon_priv *priv = data;\n+\n+\tcancel_delayed_work_sync(&priv->poll_work);\n+}\n+\n+static int mc33978_hwmon_probe(struct platform_device *pdev)\n+{\n+\tconst struct platform_device_id *id;\n+\tstruct device *dev = &pdev->dev;\n+\tstruct mc33978_hwmon_priv *priv;\n+\tstruct device *hwmon_dev;\n+\tint ret;\n+\n+\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n+\tif (!priv)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->dev = dev;\n+\n+\tid = platform_get_device_id(pdev);\n+\tif (!id || !id->driver_data)\n+\t\treturn dev_err_probe(dev, -EINVAL, \"missing device match data\\n\");\n+\n+\tpriv->hw_info = (const struct mc33978_hwmon_hw_info *)id->driver_data;\n+\n+\tpriv->map = dev_get_regmap(dev->parent, NULL);\n+\tif (!priv->map)\n+\t\treturn dev_err_probe(dev, -ENODEV, \"failed to get regmap\\n\");\n+\n+\tplatform_set_drvdata(pdev, priv);\n+\n+\tINIT_DELAYED_WORK(&priv->poll_work, mc33978_hwmon_poll_work);\n+\n+\tpriv->fault_irq = platform_get_irq(pdev, 0);\n+\tif (priv->fault_irq < 0)\n+\t\treturn priv->fault_irq;\n+\n+\thwmon_dev = devm_hwmon_device_register_with_info(dev, \"mc33978\", priv,\n+\t\t\t\t\t\t\t &mc33978_hwmon_chip_info,\n+\t\t\t\t\t\t\t NULL);\n+\tif (IS_ERR(hwmon_dev))\n+\t\treturn dev_err_probe(dev, PTR_ERR(hwmon_dev),\n+\t\t\t\t     \"failed to register hwmon device\\n\");\n+\n+\tpriv->hwmon_dev = hwmon_dev;\n+\n+\tret = devm_add_action_or_reset(dev, mc33978_hwmon_action_cancel_work,\n+\t\t\t\t       priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * The FAULT child IRQ is generated by the MFD core from transitions of\n+\t * the aggregated FAULT_STAT bus state. Request a rising-edge nested\n+\t * IRQ so the core dispatches the hwmon fault handler when faults become\n+\t * active.\n+\t *\n+\t * Fault clearing and secondary faults while FAULT_STAT remains asserted\n+\t * are handled by the hwmon polling path.\n+\t */\n+\tret = devm_request_threaded_irq(dev, priv->fault_irq, NULL,\n+\t\t\t\t\tmc33978_hwmon_fault_irq,\n+\t\t\t\t\tIRQF_ONESHOT | IRQF_TRIGGER_RISING,\n+\t\t\t\t\tdev_name(dev), priv);\n+\tif (ret)\n+\t\treturn dev_err_probe(dev, ret, \"failed to request fault IRQ\\n\");\n+\n+\treturn mc33978_hwmon_update_faults(priv);\n+}\n+\n+static const struct platform_device_id mc33978_hwmon_id[] = {\n+\t{ \"mc33978-hwmon\", (kernel_ulong_t)&hwmon_hwinfo_mc33978 },\n+\t{ \"mc34978-hwmon\", (kernel_ulong_t)&hwmon_hwinfo_mc34978 },\n+\t{ }\n+};\n+MODULE_DEVICE_TABLE(platform, mc33978_hwmon_id);\n+\n+static struct platform_driver mc33978_hwmon_driver = {\n+\t.driver = {\n+\t\t.name = \"mc33978-hwmon\",\n+\t},\n+\t.probe = mc33978_hwmon_probe,\n+\t.id_table = mc33978_hwmon_id,\n+};\n+module_platform_driver(mc33978_hwmon_driver);\n+\n+MODULE_AUTHOR(\"Oleksij Rempel <kernel@pengutronix.de>\");\n+MODULE_DESCRIPTION(\"NXP MC33978/MC34978 Hardware Monitor Driver\");\n+MODULE_LICENSE(\"GPL\");\n",
    "prefixes": [
        "v9",
        "5/6"
    ]
}