get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2217452,
    "url": "http://patchwork.ozlabs.org/api/patches/2217452/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260329090601.532477-5-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": "<20260329090601.532477-5-o.rempel@pengutronix.de>",
    "list_archive_url": null,
    "date": "2026-03-29T09:05:59",
    "name": "[v8,4/6] pinctrl: add NXP MC33978/MC34978 pinctrl driver",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "5f3f8c85b2d858e4dfc8f5656c10b2601cff3ab1",
    "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/20260329090601.532477-5-o.rempel@pengutronix.de/mbox/",
    "series": [
        {
            "id": 497911,
            "url": "http://patchwork.ozlabs.org/api/series/497911/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/list/?series=497911",
            "date": "2026-03-29T09:05:56",
            "name": "mfd: Add support for NXP MC33978/MC34978 MSDI",
            "version": 8,
            "mbox": "http://patchwork.ozlabs.org/series/497911/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2217452/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2217452/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "\n <linux-gpio+bounces-34363-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=172.105.105.114; helo=tor.lore.kernel.org;\n envelope-from=linux-gpio+bounces-34363-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 tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114])\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 4fk7nL5xZpz1xtJ\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 29 Mar 2026 20:07:34 +1100 (AEDT)",
            "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 3AE86301A431\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 29 Mar 2026 09:06:43 +0000 (UTC)",
            "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 864732EB859;\n\tSun, 29 Mar 2026 09:06:42 +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 7953A2ED848\n\tfor <linux-gpio@vger.kernel.org>; Sun, 29 Mar 2026 09:06:39 +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 1w6m5s-00018u-11; Sun, 29 Mar 2026 11:06:04 +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 1w6m5r-002gZU-06;\n\tSun, 29 Mar 2026 11:06:03 +0200",
            "from ore by dude04 with local (Exim 4.98.2)\n\t(envelope-from <ore@pengutronix.de>)\n\tid 1w6m5q-00000002EYC-3vmY;\n\tSun, 29 Mar 2026 11:06:02 +0200"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1774775202; cv=none;\n b=DYcKVP+7SJ/FPOrx283gIKcr2ewF2agisV+PlEsxFxmQUy7BYeO2Uxe83lmtCnaWqEINiYYwIdQFqvITRtSzk5tppl7W874ZrcP8GfCEpknEo0t948PmuR02ZAWt+vHtL43EfFyfGbVXffD78+BgmPW8+/wrXFG/kZ0Wz3JfMFQ=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1774775202; c=relaxed/simple;\n\tbh=0JTMX73T/exfAmdWX5diBYB2hYKUf2y4QkClHfjHNcw=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=LyYWWKAeAzmr1DlLw84hHn4R6b/9VgUweQnGXjCFVghN1umvIZeMZV26H2sxLxZWczs81LvznuEHZYUptw9JgpYzSUobtfqODLEYFPT1C43EvAMgvdPd1ZRrQWU4p0lCZ2l5G+N9z0mGjzOKbkXUVwgWlj22jOs/Rcp8xolv/Pw=",
        "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": "David Jander <david@protonic.nl>,\n\tOleksij 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",
        "Subject": "[PATCH v8 4/6] pinctrl: add NXP MC33978/MC34978 pinctrl driver",
        "Date": "Sun, 29 Mar 2026 11:05:59 +0200",
        "Message-ID": "<20260329090601.532477-5-o.rempel@pengutronix.de>",
        "X-Mailer": "git-send-email 2.47.3",
        "In-Reply-To": "<20260329090601.532477-1-o.rempel@pengutronix.de>",
        "References": "<20260329090601.532477-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": "From: David Jander <david@protonic.nl>\n\nAdd pin control and GPIO driver for the NXP MC33978/MC34978 Multiple\nSwitch Detection Interface (MSDI) devices.\n\nThis driver exposes the 22 mechanical switch detection inputs (14\nSwitch-to-Ground, 8 Programmable) as standard GPIOs.\n\nKey features implemented:\n- GPIO read/write: Translates physical switch states (open/closed)\n  to logical GPIO levels based on the configured switch topology\n  (Switch-to-Ground vs. Switch-to-Battery).\n- Emulated Output: Allows setting pins \"high\" or \"low\" by manipulating\n  the tri-state registers and hardware pull topologies.\n- Interrupt routing: Proxies GPIO interrupt requests to the irq_domain\n  managed by the parent MFD core driver via a hierarchical irq_chip.\n\nSigned-off-by: David Jander <david@protonic.nl>\nCo-developed-by: Oleksij Rempel <o.rempel@pengutronix.de>\nSigned-off-by: Oleksij Rempel <o.rempel@pengutronix.de>\nReviewed-by: Linus Walleij <linusw@kernel.org>\n---\nchanges v8:\n- Fix comment documentation to state the driver implements a hierarchical\n  irq_chip instead of proxying .to_irq().\n- Add missing <linux/irqdomain.h> include.\n- Add .irq_set_wake = irq_chip_set_wake_parent to the gpio_irq_chip to\n  properly proxy wake-up configuration to the parent domain.\n- Replace irq_find_host() with irq_find_matching_fwnode() during probe\n  to support parent domain lookup on non-OF platforms.\nchanges v7:\n- Refactor I/O state reading and tri-state updates for SG/SB topologies\n- Fix open-drain and open-source pinconf emulation\n- Make direction_input a no-op to prevent overriding pinctrl bias\n- Add defensive wrappers for IRQ proxying to prevent NULL pointer panics\n- Add missing mutex guards to pinconf and get operations\n- Convert generic internal variables to u32 and add lockdep assertions\nchanges v6:\n- no changes\nchanges v5:\n- no changes\nchanges v4:\n- add Reviewed-by: Linus Walleij ...\n- Replace the of_device_id match table with a platform_device_id table\n- Add device_set_node(dev, dev_fwnode(dev->parent)) during probe\n- Remove the check for a missing dev->of_node\nchanges v3:\n- replace manual mutex_lock()/mutex_unlock() paths with guard(mutex)\n- Unify error checking style by replacing if (ret < 0) with if (ret)\n- Migrate from a custom .to_irq callback to a hierarchical gpio_irq_chip\n- Implement .irq_bus_lock and .irq_bus_sync_unlock proxies to properly\n  cascade SPI bus lock operations to the parent MFD domain\n- Set girq->handler to handle_simple_irq\nchanges v2:\n- Translate all remaining German comments to English.\n- Remove unnecessary #ifdef CONFIG_OF wrappers around dt_node_to_map.\n- Add detailed comments to mc33978_get() and mc33978_get_multiple() explaining\n  the hardware comparator logic (1 = closed, 0 = open) and justifying the\n  bitwise inversion required to report actual physical voltage levels.\n- Add comments to the .set() and .set_config() callbacks explaining why\n  gpiolib's standard open-drain emulation (switching to input mode) fails on\n  this hardware due to active wetting currents, and why tri-state isolation is\n  mandatory.\n- Add a comment to mc33978_gpio_to_irq() explaining why it must act as a\n  proxy to the parent MFD's irq_domain (shared physical INT_B line with hwmon).\n- Drop dummy pin group callbacks (get_groups_count, etc.). This relies on a\n  preparatory patch in this series making these callbacks optional in the core.\n- Fix debugfs 'pinconf-pins' read errors by correctly returning -ENOTSUPP\n  instead of -EOPNOTSUPP for unsupported generic configurations.\n- Fix empty 'gpio-ranges' and missing debugfs labels by explicitly calling\n  gpiochip_add_pin_range() during probe.\n- Eliminate \"magic\" bitwise math in the wetting current configuration by\n  introducing a static lookup array (mc33978_wet_mA).\n- Resolve checkpatch.pl strict warnings regarding macro argument reuse by\n  converting MC33978_SPSG, MC33978_PINSHIFT, MC33978_WREG, and MC33978_WSHIFT\n  to static inline functions.\n- Remove artifacts from previous interrupt handling implementations.\n- Address minor formatting and whitespace nits.\n---\n drivers/pinctrl/Kconfig           |  14 +\n drivers/pinctrl/Makefile          |   1 +\n drivers/pinctrl/pinctrl-mc33978.c | 839 ++++++++++++++++++++++++++++++\n 3 files changed, 854 insertions(+)\n create mode 100644 drivers/pinctrl/pinctrl-mc33978.c",
    "diff": "diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig\nindex afecd9407f53..c315656c0fe5 100644\n--- a/drivers/pinctrl/Kconfig\n+++ b/drivers/pinctrl/Kconfig\n@@ -388,6 +388,20 @@ config PINCTRL_MAX77620\n \t  function in alternate mode. This driver also configure push-pull,\n \t  open drain, FPS slots etc.\n \n+config PINCTRL_MC33978\n+\ttristate \"MC33978/MC34978 industrial input controller support\"\n+\tdepends on MFD_MC33978\n+\tselect GPIOLIB\n+\tselect GENERIC_PINCONF\n+\thelp\n+\t  Say Y here to enable support for NXP MC33978/MC34978 Multiple\n+\t  Switch Detection Interface (MSDI) devices. This driver provides\n+\t  pinctrl and GPIO interfaces for the 22 mechanical switch inputs\n+\t  (14 Switch-to-Ground, 8 Programmable).\n+\n+\t  It allows reading switch states, configuring hardware pull\n+\t  topologies, and handling interrupts for state changes.\n+\n config PINCTRL_MCP23S08_I2C\n \ttristate\n \tselect REGMAP_I2C\ndiff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile\nindex f7d5d5f76d0c..afb58fb5a197 100644\n--- a/drivers/pinctrl/Makefile\n+++ b/drivers/pinctrl/Makefile\n@@ -40,6 +40,7 @@ obj-$(CONFIG_PINCTRL_XWAY)\t+= pinctrl-xway.o\n obj-$(CONFIG_PINCTRL_LPC18XX)\t+= pinctrl-lpc18xx.o\n obj-$(CONFIG_PINCTRL_MAX7360)\t+= pinctrl-max7360.o\n obj-$(CONFIG_PINCTRL_MAX77620)\t+= pinctrl-max77620.o\n+obj-$(CONFIG_PINCTRL_MC33978)\t+= pinctrl-mc33978.o\n obj-$(CONFIG_PINCTRL_MCP23S08_I2C)\t+= pinctrl-mcp23s08_i2c.o\n obj-$(CONFIG_PINCTRL_MCP23S08_SPI)\t+= pinctrl-mcp23s08_spi.o\n obj-$(CONFIG_PINCTRL_MCP23S08)\t+= pinctrl-mcp23s08.o\ndiff --git a/drivers/pinctrl/pinctrl-mc33978.c b/drivers/pinctrl/pinctrl-mc33978.c\nnew file mode 100644\nindex 000000000000..b53718e98ef2\n--- /dev/null\n+++ b/drivers/pinctrl/pinctrl-mc33978.c\n@@ -0,0 +1,839 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Copyright (C) 2024 David Jander <david@protonic.nl>, Protonic Holland\n+ * Copyright (C) 2026 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix\n+ *\n+ * MC33978/MC34978 Multiple Switch Detection Interface - Pinctrl/GPIO Driver\n+ *\n+ * Provides GPIO and pinctrl interfaces for the 22 switch detection inputs.\n+ * Handles digital input reading and wetting current configuration. Analog AMUX\n+ * functionality is handled by a separate mux driver.\n+ *\n+ * GPIO Mapping:\n+ * - GPIO 0-13:  SG0-SG13 (Switch-to-Ground inputs)\n+ * - GPIO 14-21: SP0-SP7 (Programmable: Switch-to-Ground or Switch-to-Battery)\n+ * This is dictated by the READ_IN register where bits [21:14] = SP[7:0]\n+ * and bits [13:0] = SG[13:0].\n+ *\n+ * Register Organization:\n+ * Configuration registers are generally paired. The _SP register at offset N\n+ * controls SP0-SP7, and the _SG register at offset N+2 controls SG0-SG13.\n+ *\n+ * Wetting Currents vs. Pull Resistors:\n+ * The hardware physically lacks traditional passive pull-up or pull-down\n+ * resistors. Instead, it uses active, controllable current regulators\n+ * (wetting currents) to detect switch states and clean mechanical contacts.\n+ * - Because these are active current sources, specifying an ohmic value for\n+ * pull-up/down biases is physically invalid. The driver ignores ohm arguments.\n+ * - 8 selectable current values: 2, 6, 8, 10, 12, 14, 16, 20 mA.\n+ * - Exposed via the pinconf PIN_CONFIG_DRIVE_STRENGTH parameter (in mA).\n+ *\n+ * Emulated Outputs:\n+ * The hardware lacks traditional push-pull output drivers; it is strictly an\n+ * input device. \"Outputs\" are simulated by toggling the wetting currents and\n+ * physically isolating the pins via hardware tri-state registers. Consequently,\n+ * consumers MUST flag outputs with GPIO_OPEN_DRAIN or GPIO_OPEN_SOURCE in\n+ * the Device Tree.\n+ *\n+ * Input Detection Mechanics:\n+ * This input mechanism relies on the active current regulators rather than\n+ * passive hard resistors. For a Switch-to-Ground (SG) pin, the chip sources\n+ * a constant current. When the switch is open, the pin voltage floats up to\n+ * the battery voltage. When the switch closes, it creates a path to ground;\n+ * because the current is strictly regulated, the pin voltage drops sharply\n+ * below the internal 4.0V comparator threshold.\n+ * * The hardware evaluates this and reports an abstract \"contact status\"\n+ * (1 = closed, 0 = open). For SG pins, a closed switch (~0V) reports as '1'.\n+ * To align with gpiolib expectations where ~0V equals a physical logical '0',\n+ * this driver explicitly inverts the hardware status for all SG-configured\n+ * pins before reporting them.\n+ *\n+ * Interrupts:\n+ * The physical INT_B line and threaded IRQ domain are managed centrally by\n+ * the parent MFD core. This driver implements a hierarchical irq_chip\n+ * to proxy masking/unmasking and configuration to the parent domain.\n+ *\n+ * Written by David Jander <david@protonic.nl>\n+ *\n+ * Datasheet:\n+ * https://www.nxp.com/docs/en/data-sheet/MC33978.pdf\n+ */\n+\n+#include <linux/cleanup.h>\n+#include <linux/device.h>\n+#include <linux/gpio/driver.h>\n+#include <linux/interrupt.h>\n+#include <linux/irq.h>\n+#include <linux/irqdomain.h>\n+#include <linux/kernel.h>\n+#include <linux/module.h>\n+#include <linux/mutex.h>\n+#include <linux/pinctrl/pinconf-generic.h>\n+#include <linux/pinctrl/pinconf.h>\n+#include <linux/pinctrl/pinctrl.h>\n+#include <linux/platform_device.h>\n+#include <linux/regmap.h>\n+#include <linux/seq_file.h>\n+\n+#include <linux/mfd/mc33978.h>\n+\n+#define MC33978_NGPIO\t\t22\n+\n+/*\n+ * Input numbering is dictated by bit-order of the input register:\n+ * Inputs 0-13 -> SG0-SG13\n+ * Inputs 14-21 -> SP0-SP7\n+ */\n+#define MC33978_NUM_SG\t\t14\n+#define MC33978_SP_MASK\t\tGENMASK(MC33978_NGPIO - 1, MC33978_NUM_SG)\n+#define MC33978_SG_MASK\t\tGENMASK(MC33978_NUM_SG - 1, 0)\n+#define MC33978_SG_SHIFT\t0\n+#define MC33978_SP_SHIFT\tMC33978_NUM_SG\n+\n+#define MC33978_TRISTATE\t0\n+#define MC33978_PU\t\t1\n+#define MC33978_PD\t\t2\n+\n+struct mc33978_pinctrl {\n+\tstruct device *dev;\n+\tstruct regmap *regmap;\n+\tint irq;\n+\n+\tstruct irq_domain *domain;\n+\n+\tstruct gpio_chip chip;\n+\tstruct pinctrl_dev *pctldev;\n+\tstruct pinctrl_desc pinctrl_desc;\n+\n+\t/*\n+\t * Protects multi-register hardware sequences in .set() and atomic\n+\t * READ_IN + CONFIG reads in .get()\n+\t */\n+\tstruct mutex lock;\n+};\n+\n+static const struct pinctrl_pin_desc mc33978_pins[] = {\n+\tPINCTRL_PIN(0, \"sg0\"),\n+\tPINCTRL_PIN(1, \"sg1\"),\n+\tPINCTRL_PIN(2, \"sg2\"),\n+\tPINCTRL_PIN(3, \"sg3\"),\n+\tPINCTRL_PIN(4, \"sg4\"),\n+\tPINCTRL_PIN(5, \"sg5\"),\n+\tPINCTRL_PIN(6, \"sg6\"),\n+\tPINCTRL_PIN(7, \"sg7\"),\n+\tPINCTRL_PIN(8, \"sg8\"),\n+\tPINCTRL_PIN(9, \"sg9\"),\n+\tPINCTRL_PIN(10, \"sg10\"),\n+\tPINCTRL_PIN(11, \"sg11\"),\n+\tPINCTRL_PIN(12, \"sg12\"),\n+\tPINCTRL_PIN(13, \"sg13\"),\n+\tPINCTRL_PIN(14, \"sp0\"),\n+\tPINCTRL_PIN(15, \"sp1\"),\n+\tPINCTRL_PIN(16, \"sp2\"),\n+\tPINCTRL_PIN(17, \"sp3\"),\n+\tPINCTRL_PIN(18, \"sp4\"),\n+\tPINCTRL_PIN(19, \"sp5\"),\n+\tPINCTRL_PIN(20, \"sp6\"),\n+\tPINCTRL_PIN(21, \"sp7\"),\n+};\n+\n+static inline bool mc33978_is_sp(unsigned int pin)\n+{\n+\treturn pin >= MC33978_NUM_SG;\n+}\n+\n+/* Choose register offset for _SG/_SP registers. reg is always the _SP addr. */\n+static inline u8 mc33978_spsg(u8 reg, unsigned int pin)\n+{\n+\treturn mc33978_is_sp(pin) ? reg : reg + 2;\n+}\n+\n+/* Get the bit index into the corresponding register */\n+static inline unsigned int mc33978_pinshift(unsigned int pin)\n+{\n+\treturn mc33978_is_sp(pin) ? pin - MC33978_NUM_SG : pin;\n+}\n+\n+#define MC33978_PINMASK(pin)\tBIT(mc33978_pinshift(pin))\n+\n+/*\n+ * Wetting current registers: 3 in total, each pin uses a 3-bit field,\n+ * 8 pins per register, except for the last one.\n+ */\n+static inline u8 mc33978_wreg(u8 reg, unsigned int pin)\n+{\n+\treturn reg + (mc33978_is_sp(pin) ? 0 : 2 + 2 * (pin / 8));\n+}\n+\n+static inline unsigned int mc33978_wshift(unsigned int pin)\n+{\n+\treturn mc33978_is_sp(pin) ? 3 * (pin - MC33978_NUM_SG) : 3 * (pin % 8);\n+}\n+\n+#define MC33978_WMASK(pin)\t(7 << mc33978_wshift(pin))\n+\n+static int mc33978_read(struct mc33978_pinctrl *mpc, u8 reg, u32 *val)\n+{\n+\tint ret;\n+\n+\tret = regmap_read(mpc->regmap, reg, val);\n+\tif (ret)\n+\t\tdev_err_ratelimited(mpc->dev, \"Regmap read error %d at reg: %02x.\\n\",\n+\t\t\t\t    ret, reg);\n+\treturn ret;\n+}\n+\n+static int mc33978_update_bits(struct mc33978_pinctrl *mpc, u8 reg, u32 mask,\n+\t\t\t       u32 val)\n+{\n+\tint ret;\n+\n+\tret = regmap_update_bits(mpc->regmap, reg, mask, val);\n+\tif (ret)\n+\t\tdev_err_ratelimited(mpc->dev, \"Regmap update bits error %d at reg: %02x.\\n\",\n+\t\t\t\t    ret, reg);\n+\treturn ret;\n+}\n+\n+static const struct pinctrl_ops mc33978_pinctrl_ops = {\n+\t.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,\n+\t.dt_free_map = pinconf_generic_dt_free_map,\n+};\n+\n+static int mc33978_get_pull(struct mc33978_pinctrl *mpc, unsigned int pin, u32 *val)\n+{\n+\tu32 data;\n+\tint ret;\n+\n+\tlockdep_assert_held(&mpc->lock);\n+\n+\tret = mc33978_read(mpc, mc33978_spsg(MC33978_REG_TRI_SP, pin), &data);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Is the pin tri-stated? */\n+\tif (data & MC33978_PINMASK(pin)) {\n+\t\t*val = MC33978_TRISTATE;\n+\t\treturn 0;\n+\t}\n+\n+\t/* Pins 0..13 only support pull-up */\n+\tif (!mc33978_is_sp(pin)) {\n+\t\t*val = MC33978_PU;\n+\t\treturn 0;\n+\t}\n+\n+\t/* Check pin pull direction for pins 14..21 */\n+\tret = mc33978_read(mpc, MC33978_REG_CONFIG, &data);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (data & MC33978_PINMASK(pin))\n+\t\t*val = MC33978_PD;\n+\telse\n+\t\t*val = MC33978_PU;\n+\n+\treturn 0;\n+}\n+\n+static int mc33978_set_pull(struct mc33978_pinctrl *mpc, unsigned int pin, int val)\n+{\n+\tu32 mask = MC33978_PINMASK(pin);\n+\tint ret;\n+\n+\tlockdep_assert_held(&mpc->lock);\n+\n+\t/* SG pins physically lack pull-downs current sources */\n+\tif (val == MC33978_PD && !mc33978_is_sp(pin))\n+\t\treturn -EINVAL;\n+\n+\t/* Configure direction (Exclusively for SP pins) */\n+\tif (mc33978_is_sp(pin) && val != MC33978_TRISTATE) {\n+\t\tret = mc33978_update_bits(mpc, MC33978_REG_CONFIG, mask,\n+\t\t\t\t\t  (val == MC33978_PD) ? mask : 0);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\t/* Enable current source or set to tri-state  */\n+\treturn mc33978_update_bits(mpc, mc33978_spsg(MC33978_REG_TRI_SP, pin),\n+\t\t\t\t   mask,\n+\t\t\t\t   (val == MC33978_TRISTATE) ? mask : 0);\n+}\n+\n+static const unsigned int mc33978_wet_mA[] = { 2, 6, 8, 10, 12, 14, 16, 20 };\n+\n+static int mc33978_set_ds(struct mc33978_pinctrl *mpc, unsigned int pin,\n+\t\t\t  u32 val)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(mc33978_wet_mA); i++) {\n+\t\tif (val == mc33978_wet_mA[i]) {\n+\t\t\treturn mc33978_update_bits(mpc,\n+\t\t\t\t\tmc33978_wreg(MC33978_REG_WET_SP, pin),\n+\t\t\t\t\tMC33978_WMASK(pin),\n+\t\t\t\t\ti << mc33978_wshift(pin));\n+\t\t}\n+\t}\n+\n+\treturn -EINVAL;\n+}\n+\n+static int mc33978_get_ds(struct mc33978_pinctrl *mpc, unsigned int pin,\n+\t\t\t  u32 *val)\n+{\n+\tu32 data;\n+\tint ret;\n+\n+\tret = mc33978_read(mpc, mc33978_wreg(MC33978_REG_WET_SP, pin), &data);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tdata &= MC33978_WMASK(pin);\n+\tdata >>= mc33978_wshift(pin);\n+\n+\tif (data >= ARRAY_SIZE(mc33978_wet_mA))\n+\t\treturn -EINVAL;\n+\n+\t*val = mc33978_wet_mA[data];\n+\n+\treturn 0;\n+}\n+\n+static int mc33978_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,\n+\t\t\t       unsigned long *config)\n+{\n+\tstruct mc33978_pinctrl *mpc = pinctrl_dev_get_drvdata(pctldev);\n+\tenum pin_config_param param = pinconf_to_config_param(*config);\n+\tu32 arg;\n+\tu32 data;\n+\tint ret;\n+\n+\tguard(mutex)(&mpc->lock);\n+\n+\tswitch (param) {\n+\tcase PIN_CONFIG_BIAS_PULL_UP:\n+\t\tret = mc33978_get_pull(mpc, pin, &data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tif (data != MC33978_PU)\n+\t\t\treturn -EINVAL;\n+\t\targ = 1;\n+\t\tbreak;\n+\tcase PIN_CONFIG_BIAS_PULL_DOWN:\n+\t\tret = mc33978_get_pull(mpc, pin, &data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tif (data != MC33978_PD)\n+\t\t\treturn -EINVAL;\n+\t\targ = 1;\n+\t\tbreak;\n+\tcase PIN_CONFIG_DRIVE_OPEN_DRAIN:\n+\t\tif (!mc33978_is_sp(pin))\n+\t\t\treturn -EINVAL;\n+\n+\t\tret = mc33978_read(mpc, MC33978_REG_CONFIG, &data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!(data & MC33978_PINMASK(pin)))\n+\t\t\treturn -EINVAL;\n+\t\targ = 1;\n+\t\tbreak;\n+\tcase PIN_CONFIG_DRIVE_OPEN_SOURCE:\n+\t\tif (mc33978_is_sp(pin)) {\n+\t\t\tret = mc33978_read(mpc, MC33978_REG_CONFIG, &data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\n+\t\t\tif (data & MC33978_PINMASK(pin))\n+\t\t\t\treturn -EINVAL;\n+\t\t}\n+\t\targ = 1;\n+\t\tbreak;\n+\tcase PIN_CONFIG_BIAS_DISABLE:\n+\tcase PIN_CONFIG_BIAS_HIGH_IMPEDANCE:\n+\t\tret = mc33978_get_pull(mpc, pin, &data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tif (data != MC33978_TRISTATE)\n+\t\t\treturn -EINVAL;\n+\t\targ = 1;\n+\t\tbreak;\n+\tcase PIN_CONFIG_DRIVE_STRENGTH:\n+\t\tret = mc33978_get_ds(mpc, pin, &data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\targ = data;\n+\t\tbreak;\n+\tdefault:\n+\t\t/*\n+\t\t * Ignore checkpatch warning: the pinctrl core specifically\n+\t\t * expects -ENOTSUPP to silently skip unsupported generic\n+\t\t * parameters. Using -EOPNOTSUPP causes debugfs read failures.\n+\t\t */\n+\t\treturn -ENOTSUPP;\n+\t}\n+\n+\t*config = pinconf_to_config_packed(param, arg);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Hardware constraint regarding PIN_CONFIG_BIAS_PULL_UP/DOWN:\n+ * The MC33978 utilizes active constant current sources (wetting currents)\n+ * rather than passive pull-resistors. Since the equivalent ohmic resistance\n+ * scales dynamically with the fluctuating board voltage (VBATP), computing\n+ * a static ohm value is physically invalid.\n+ * The driver intentionally ignores resistance arguments during configuration\n+ * and continuously reports 0 ohms to the pinctrl framework.\n+ */\n+static int mc33978_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,\n+\t\t\t       unsigned long *configs, unsigned int num_configs)\n+{\n+\tstruct mc33978_pinctrl *mpc = pinctrl_dev_get_drvdata(pctldev);\n+\tenum pin_config_param param;\n+\tint ret = 0;\n+\tu32 arg;\n+\tint i;\n+\n+\tguard(mutex)(&mpc->lock);\n+\n+\tfor (i = 0; i < num_configs; i++) {\n+\t\tparam = pinconf_to_config_param(configs[i]);\n+\t\targ = pinconf_to_config_argument(configs[i]);\n+\n+\t\t/*\n+\t\t * The hardware physically lacks push-pull output drivers.\n+\t\t * By explicitly handling OPEN_DRAIN and OPEN_SOURCE here, we\n+\t\t * signal to gpiolib that we support these modes \"natively\".\n+\t\t * This crucially prevents gpiolib from falling back to its\n+\t\t * software emulation (which sets the pin to input mode to\n+\t\t * achieve High-Z). On the MC33978, input mode is NOT High-Z;\n+\t\t * it actively drives the line with a wetting current!\n+\t\t */\n+\t\tswitch (param) {\n+\t\tcase PIN_CONFIG_DRIVE_OPEN_SOURCE:\n+\t\t\t/* Setup topology only; do not turn on current yet */\n+\t\t\tif (mc33978_is_sp(pin))\n+\t\t\t\tret = mc33978_update_bits(mpc, MC33978_REG_CONFIG,\n+\t\t\t\t\t\t\t  MC33978_PINMASK(pin), 0);\n+\t\t\tbreak;\n+\t\tcase PIN_CONFIG_BIAS_PULL_UP:\n+\t\t\tret = mc33978_set_pull(mpc, pin, MC33978_PU);\n+\t\t\tbreak;\n+\t\tcase PIN_CONFIG_DRIVE_OPEN_DRAIN:\n+\t\t\tif (!mc33978_is_sp(pin)) {\n+\t\t\t\tdev_err(mpc->dev, \"Pin %u is SG and does not support open-drain\\n\",\n+\t\t\t\t\tpin);\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t\t/* Setup topology only; do not turn on current yet */\n+\t\t\tret = mc33978_update_bits(mpc, MC33978_REG_CONFIG,\n+\t\t\t\t\t\t  MC33978_PINMASK(pin),\n+\t\t\t\t\t\t  MC33978_PINMASK(pin));\n+\t\t\tbreak;\n+\t\tcase PIN_CONFIG_BIAS_PULL_DOWN:\n+\t\t\tif (!mc33978_is_sp(pin)) {\n+\t\t\t\tdev_err(mpc->dev, \"Pin %u is SG and does not support pull-down\\n\",\n+\t\t\t\t\tpin);\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t\tret = mc33978_set_pull(mpc, pin, MC33978_PD);\n+\t\t\tbreak;\n+\t\t/*\n+\t\t * The MC33978 uses active wetting currents rather than passive\n+\t\t * pull-resistors. Disabling the bias (pull-up/down) is\n+\t\t * physically equivalent to putting the pin into a\n+\t\t * high-impedance state. Both actions are achieved by isolating\n+\t\t * the pin via the hardware tri-state registers.\n+\t\t */\n+\t\tcase PIN_CONFIG_BIAS_DISABLE:\n+\t\tcase PIN_CONFIG_BIAS_HIGH_IMPEDANCE:\n+\t\t\tret = mc33978_set_pull(mpc, pin, MC33978_TRISTATE);\n+\t\t\tbreak;\n+\t\tcase PIN_CONFIG_DRIVE_STRENGTH_UA:\n+\t\t\targ /= 1000;\n+\t\t\tfallthrough;\n+\t\tcase PIN_CONFIG_DRIVE_STRENGTH:\n+\t\t\tret = mc33978_set_ds(mpc, pin, arg);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t/*\n+\t\t\t * Required by the pinctrl core to safely fall back or\n+\t\t\t * skip unsupported configs. Do not use -EOPNOTSUPP.\n+\t\t\t */\n+\t\t\treturn -ENOTSUPP;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\tdev_err(mpc->dev, \"Failed to set config param %04x for pin %u: %d\\n\",\n+\t\t\t\tparam, pin, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct pinconf_ops mc33978_pinconf_ops = {\n+\t.pin_config_get = mc33978_pinconf_get,\n+\t.pin_config_set = mc33978_pinconf_set,\n+\t.is_generic = true,\n+};\n+\n+static int mc33978_direction_input(struct gpio_chip *chip, unsigned int offset)\n+{\n+\t/* This chip is strictly an input device (comparators always active) */\n+\treturn 0;\n+}\n+\n+/*\n+ * The hardware evaluates pin voltage against a threshold (default 4.0V)\n+ * and reports an abstract contact status (1 = closed, 0 = open):\n+ *\n+ * SG (Switch-to-Ground) topology (pull-up current source):\n+ * - Voltage > Threshold: Switch Open   (HW reports 0) -> Physical High\n+ * - Voltage < Threshold: Switch Closed (HW reports 1) -> Physical Low\n+ *\n+ * SB (Switch-to-Battery) topology (pull-down current source):\n+ * - Voltage > Threshold: Switch Closed (HW reports 1) -> Physical High\n+ * - Voltage < Threshold: Switch Open   (HW reports 0) -> Physical Low\n+ *\n+ * We translate this contact status back into physical voltage levels by\n+ * inverting the hardware status for all pins operating in SG topology.\n+ */\n+static int mc33978_read_in_state(struct mc33978_pinctrl *mpc,\n+\t\t\t\t unsigned long mask, unsigned long *state)\n+{\n+\tu32 status, inv_mask;\n+\tu32 config_reg = 0;\n+\tint ret;\n+\n+\tret = mc33978_read(mpc, MC33978_REG_READ_IN, &status);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Read CONFIG register only if the requested mask involves SP pins */\n+\tif (mask & MC33978_SP_MASK) {\n+\t\tret = mc33978_read(mpc, MC33978_REG_CONFIG, &config_reg);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\t/*\n+\t * Create an inversion mask for all pins currently operating in\n+\t * Switch-to-Ground (SG) topology. SG pins always have pull-ups.\n+\t * For SP pins, CONFIG bit 0 = Switch-to-Ground (PU),\n+\t * CONFIG bit 1 = Switch-to-Battery (PD).\n+\t */\n+\tinv_mask = MC33978_SG_MASK |\n+\t\t   (~(config_reg << MC33978_NUM_SG) & MC33978_SP_MASK);\n+\n+\t*state = (status ^ inv_mask) & mask;\n+\n+\treturn 0;\n+}\n+\n+static int mc33978_get(struct gpio_chip *chip, unsigned int offset)\n+{\n+\tstruct mc33978_pinctrl *mpc = gpiochip_get_data(chip);\n+\tunsigned long state;\n+\tint ret;\n+\n+\tguard(mutex)(&mpc->lock);\n+\n+\tret = mc33978_read_in_state(mpc, BIT(offset), &state);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn !!(state & BIT(offset));\n+}\n+\n+static int mc33978_get_multiple(struct gpio_chip *chip,\n+\t\t\t\tunsigned long *mask, unsigned long *bits)\n+{\n+\tstruct mc33978_pinctrl *mpc = gpiochip_get_data(chip);\n+\tunsigned long state;\n+\tint ret;\n+\n+\tguard(mutex)(&mpc->lock);\n+\n+\tret = mc33978_read_in_state(mpc, *mask, &state);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*bits = (*bits & ~*mask) | state;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Emulate output states by routing or isolating active wetting currents.\n+ * To turn the line ON, we disable the hardware tri-state (write 0).\n+ * To turn the line OFF (High-Z), we enable tri-state (write 1).\n+ *\n+ * For Open-Source (Pull-Up): value=1 turns it ON, value=0 is High-Z.\n+ * For Open-Drain (Pull-Down): value=0 turns it ON, value=1 is High-Z.\n+ * We dynamically read the CONFIG register to determine the topology\n+ * and invert the bits accordingly for Open-Drain pins.\n+ *\n+ * Note: The hardware physically lacks push-pull drivers. Toggling outputs\n+ * via tri-state isolation may cause transient spikes.\n+ */\n+static int mc33978_update_tri_state(struct mc33978_pinctrl *mpc, u32 mask,\n+\t\t\t\t    u32 bits)\n+{\n+\tu32 sgmask = (mask & MC33978_SG_MASK) >> MC33978_SG_SHIFT;\n+\tu32 sgbits = (bits & MC33978_SG_MASK) >> MC33978_SG_SHIFT;\n+\tu32 spmask = (mask & MC33978_SP_MASK) >> MC33978_SP_SHIFT;\n+\tu32 spbits = (bits & MC33978_SP_MASK) >> MC33978_SP_SHIFT;\n+\tu32 config_reg = 0;\n+\tint ret = 0;\n+\n+\tif (spmask) {\n+\t\t/* Read topology: 1 = PD (Open-Drain), 0 = PU (Open-Source) */\n+\t\tret = mc33978_read(mpc, MC33978_REG_CONFIG, &config_reg);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t/*\n+\t\t * Invert bits for Open-Drain (PD) pins.\n+\t\t * The Open-Drain API contract expects value=1 to be High-Z.\n+\t\t */\n+\t\tspbits ^= (config_reg & spmask);\n+\n+\t\tret = mc33978_update_bits(mpc, MC33978_REG_TRI_SP, spmask,\n+\t\t\t\t\t  ~spbits);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\t/* SG pins are always Pull-Up (Open-Source), no inversion needed */\n+\tif (sgmask)\n+\t\tret = mc33978_update_bits(mpc, MC33978_REG_TRI_SG, sgmask,\n+\t\t\t\t\t  ~sgbits);\n+\n+\treturn ret;\n+}\n+\n+static int mc33978_set(struct gpio_chip *chip, unsigned int offset, int value)\n+{\n+\tstruct mc33978_pinctrl *mpc = gpiochip_get_data(chip);\n+\tu32 mask = BIT(offset);\n+\tu32 bits = value ? mask : 0;\n+\n+\tguard(mutex)(&mpc->lock);\n+\n+\treturn mc33978_update_tri_state(mpc, mask, bits);\n+}\n+\n+static int mc33978_set_multiple(struct gpio_chip *chip,\n+\t\t\t\tunsigned long *mask, unsigned long *bits)\n+{\n+\tstruct mc33978_pinctrl *mpc = gpiochip_get_data(chip);\n+\n+\tguard(mutex)(&mpc->lock);\n+\n+\treturn mc33978_update_tri_state(mpc, *mask, *bits);\n+}\n+\n+static int mc33978_direction_output(struct gpio_chip *chip, unsigned int offset,\n+\t\t\t\t    int value)\n+{\n+\treturn mc33978_set(chip, offset, value);\n+}\n+\n+static int mc33978_gpio_child_to_parent_hwirq(struct gpio_chip *gc,\n+\t\t\t\t\t      unsigned int child,\n+\t\t\t\t\t      unsigned int child_type,\n+\t\t\t\t\t      unsigned int *parent,\n+\t\t\t\t\t      unsigned int *parent_type)\n+{\n+\t*parent_type = child_type;\n+\t*parent = child;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Defensive wrappers for hierarchical IRQ proxying.\n+ *\n+ * gpiolib's hierarchical allocation exposes a lifecycle gap: the child\n+ * descriptor is registered before irq_domain_alloc_irqs_parent() fully\n+ * instantiates the parent chip.\n+ *\n+ * During consumer probe (e.g., gpiod_to_irq()), irq_create_fwspec_mapping()\n+ * allocates the hierarchy. As part of this, irq_domain_set_info() initializes\n+ * the top-level irq_desc and calls __irq_set_handler(). If the irq_desc\n+ * requires locking, __irq_get_desc_lock() will invoke the child's\n+ * .irq_bus_lock before the parent allocation is complete.\n+ *\n+ * Upstream generic helpers (e.g., irq_chip_mask_parent) blindly dereference\n+ * data->parent_data->chip, causing an immediate NULL pointer panic during\n+ * this gap. These wrappers check for a valid parent chip to safely drop\n+ * premature locking or masking events while the legacy subsystem hierarchy\n+ * is still assembling itself.\n+ */\n+static void mc33978_gpio_irq_mask(struct irq_data *data)\n+{\n+\tstruct irq_data *parent = data->parent_data;\n+\n+\tif (parent && parent->chip && parent->chip->irq_mask)\n+\t\tparent->chip->irq_mask(parent);\n+}\n+\n+static void mc33978_gpio_irq_unmask(struct irq_data *data)\n+{\n+\tstruct irq_data *parent = data->parent_data;\n+\n+\tif (parent && parent->chip && parent->chip->irq_unmask)\n+\t\tparent->chip->irq_unmask(parent);\n+}\n+\n+static int mc33978_gpio_irq_set_type(struct irq_data *data, unsigned int type)\n+{\n+\tstruct irq_data *parent = data->parent_data;\n+\n+\tif (parent && parent->chip && parent->chip->irq_set_type)\n+\t\treturn parent->chip->irq_set_type(parent, type);\n+\n+\treturn -EINVAL;\n+}\n+\n+static void mc33978_gpio_irq_bus_lock(struct irq_data *data)\n+{\n+\tstruct irq_data *parent = data->parent_data;\n+\n+\tif (parent && parent->chip && parent->chip->irq_bus_lock)\n+\t\tparent->chip->irq_bus_lock(parent);\n+}\n+\n+static void mc33978_gpio_irq_bus_sync_unlock(struct irq_data *data)\n+{\n+\tstruct irq_data *parent = data->parent_data;\n+\n+\tif (parent && parent->chip && parent->chip->irq_bus_sync_unlock)\n+\t\tparent->chip->irq_bus_sync_unlock(parent);\n+}\n+\n+static const struct irq_chip mc33978_gpio_irqchip = {\n+\t.name = \"mc33978-gpio\",\n+\t.irq_mask = mc33978_gpio_irq_mask,\n+\t.irq_unmask = mc33978_gpio_irq_unmask,\n+\t.irq_set_type = mc33978_gpio_irq_set_type,\n+\t.irq_bus_lock = mc33978_gpio_irq_bus_lock,\n+\t.irq_bus_sync_unlock = mc33978_gpio_irq_bus_sync_unlock,\n+\t.irq_set_wake = irq_chip_set_wake_parent,\n+\t.flags = IRQCHIP_IMMUTABLE,\n+\tGPIOCHIP_IRQ_RESOURCE_HELPERS,\n+};\n+\n+static void mc33978_init_gpio_chip(struct mc33978_pinctrl *mpc,\n+\t\t\t\t   struct device *dev)\n+{\n+\tstruct gpio_irq_chip *girq;\n+\n+\tmpc->chip.label = dev_name(dev);\n+\tmpc->chip.direction_input = mc33978_direction_input;\n+\tmpc->chip.get = mc33978_get;\n+\tmpc->chip.get_multiple = mc33978_get_multiple;\n+\tmpc->chip.direction_output = mc33978_direction_output;\n+\tmpc->chip.set = mc33978_set;\n+\tmpc->chip.set_multiple = mc33978_set_multiple;\n+\tmpc->chip.set_config = gpiochip_generic_config;\n+\n+\tmpc->chip.base = -1;\n+\tmpc->chip.ngpio = MC33978_NGPIO;\n+\tmpc->chip.can_sleep = true;\n+\tmpc->chip.parent = dev;\n+\tmpc->chip.owner = THIS_MODULE;\n+\n+\tgirq = &mpc->chip.irq;\n+\tgpio_irq_chip_set_chip(girq, &mc33978_gpio_irqchip);\n+\tgirq->fwnode = dev_fwnode(dev);\n+\tgirq->parent_domain = mpc->domain;\n+\tgirq->child_to_parent_hwirq = mc33978_gpio_child_to_parent_hwirq;\n+\tgirq->handler = handle_simple_irq;\n+\tgirq->default_type = IRQ_TYPE_NONE;\n+}\n+\n+static void mc33978_init_pinctrl_desc(struct mc33978_pinctrl *mpc,\n+\t\t\t\t      struct device *dev)\n+{\n+\tmpc->pinctrl_desc.name = dev_name(dev);\n+\n+\tmpc->pinctrl_desc.pctlops = &mc33978_pinctrl_ops;\n+\tmpc->pinctrl_desc.confops = &mc33978_pinconf_ops;\n+\tmpc->pinctrl_desc.pins = mc33978_pins;\n+\tmpc->pinctrl_desc.npins = MC33978_NGPIO;\n+\tmpc->pinctrl_desc.owner = THIS_MODULE;\n+}\n+\n+static int mc33978_pinctrl_probe(struct platform_device *pdev)\n+{\n+\tstruct device *dev = &pdev->dev;\n+\tstruct mc33978_pinctrl *mpc;\n+\tint ret;\n+\n+\tdevice_set_node(dev, dev_fwnode(dev->parent));\n+\n+\tmpc = devm_kzalloc(dev, sizeof(*mpc), GFP_KERNEL);\n+\tif (!mpc)\n+\t\treturn -ENOMEM;\n+\n+\tmpc->dev = dev;\n+\n+\tmpc->regmap = dev_get_regmap(dev->parent, NULL);\n+\tif (!mpc->regmap)\n+\t\treturn dev_err_probe(dev, -ENODEV, \"Failed to get parent regmap\\n\");\n+\n+\tmpc->domain = irq_find_matching_fwnode(dev_fwnode(dev->parent), DOMAIN_BUS_ANY);\n+\tif (!mpc->domain)\n+\t\treturn dev_err_probe(dev, -ENODEV, \"Failed to find parent IRQ domain\\n\");\n+\n+\tmutex_init(&mpc->lock);\n+\n+\tmc33978_init_gpio_chip(mpc, dev);\n+\tmc33978_init_pinctrl_desc(mpc, dev);\n+\n+\tmpc->pctldev = devm_pinctrl_register(dev, &mpc->pinctrl_desc, mpc);\n+\tif (IS_ERR(mpc->pctldev))\n+\t\treturn dev_err_probe(dev, PTR_ERR(mpc->pctldev),\n+\t\t\t\t     \"can't register pinctrl\\n\");\n+\n+\tret = devm_gpiochip_add_data(dev, &mpc->chip, mpc);\n+\tif (ret)\n+\t\treturn dev_err_probe(dev, ret, \"can't add GPIO chip\\n\");\n+\n+\tret = gpiochip_add_pin_range(&mpc->chip, dev_name(dev), 0, 0,\n+\t\t\t\t     MC33978_NGPIO);\n+\tif (ret)\n+\t\treturn dev_err_probe(dev, ret, \"failed to add pin range\\n\");\n+\n+\tplatform_set_drvdata(pdev, mpc);\n+\n+\treturn 0;\n+}\n+\n+static const struct platform_device_id mc33978_pinctrl_id[] = {\n+\t{ \"mc33978-pinctrl\", },\n+\t{ \"mc34978-pinctrl\", },\n+\t{ }\n+};\n+MODULE_DEVICE_TABLE(platform, mc33978_pinctrl_id);\n+\n+static struct platform_driver mc33978_pinctrl_driver = {\n+\t.driver = {\n+\t\t.name = \"mc33978-pinctrl\",\n+\t},\n+\t.probe = mc33978_pinctrl_probe,\n+\t.id_table = mc33978_pinctrl_id,\n+};\n+module_platform_driver(mc33978_pinctrl_driver);\n+\n+MODULE_AUTHOR(\"David Jander <david@protonic.nl>\");\n+MODULE_DESCRIPTION(\"NXP MC33978/MC34978 pinctrl driver\");\n+MODULE_LICENSE(\"GPL\");\n",
    "prefixes": [
        "v8",
        "4/6"
    ]
}