get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2225194,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2225194/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/linux-pwm/patch/20260420-rk3576-pwm-v4-2-421738c7bf28@collabora.com/",
    "project": {
        "id": 38,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/38/?format=api",
        "name": "Linux PWM development",
        "link_name": "linux-pwm",
        "list_id": "linux-pwm.vger.kernel.org",
        "list_email": "linux-pwm@vger.kernel.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260420-rk3576-pwm-v4-2-421738c7bf28@collabora.com>",
    "date": "2026-04-20T13:35:20",
    "name": "[v4,2/5] mfd: Add Rockchip mfpwm driver",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "6f3c5a39e3e674e55605d160c0406eeeabc9eb27",
    "submitter": {
        "id": 90188,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/90188/?format=api",
        "name": "Nicolas Frattaroli",
        "email": "nicolas.frattaroli@collabora.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/linux-pwm/patch/20260420-rk3576-pwm-v4-2-421738c7bf28@collabora.com/mbox/",
    "series": [
        {
            "id": 500618,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/500618/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/linux-pwm/list/?series=500618",
            "date": "2026-04-20T13:35:22",
            "name": "Add Rockchip RK3576 PWM Support Through MFPWM",
            "version": 4,
            "mbox": "http://patchwork.ozlabs.org/series/500618/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2225194/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2225194/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "\n <linux-pwm+bounces-8650-incoming=patchwork.ozlabs.org@vger.kernel.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "linux-pwm@vger.kernel.org"
        ],
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=collabora.com header.i=nicolas.frattaroli@collabora.com\n header.a=rsa-sha256 header.s=zohomail header.b=i8fzysou;\n\tdkim-atps=neutral",
            "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-pwm+bounces-8650-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)",
            "smtp.subspace.kernel.org;\n\tdkim=pass (1024-bit key) header.d=collabora.com\n header.i=nicolas.frattaroli@collabora.com header.b=\"i8fzysou\"",
            "smtp.subspace.kernel.org;\n arc=pass smtp.client-ip=136.143.188.112",
            "smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=collabora.com",
            "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=collabora.com"
        ],
        "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 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fzq25010pz1yD4\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 01:21:00 +1000 (AEST)",
            "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id B788E310F301\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 20 Apr 2026 14:42:14 +0000 (UTC)",
            "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 082C13D34A8;\n\tMon, 20 Apr 2026 13:36:50 +0000 (UTC)",
            "from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com\n [136.143.188.112])\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 15C5A3D330C;\n\tMon, 20 Apr 2026 13:36:47 +0000 (UTC)",
            "by mx.zohomail.com with SMTPS id 1776692168272656.361573259086;\n\tMon, 20 Apr 2026 06:36:08 -0700 (PDT)"
        ],
        "ARC-Seal": [
            "i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1776692209; cv=pass;\n b=qnKmSlvZomlxFAbh6iYPQ55/T4R1qxWXP1lAWfUlr/YiuzBi632FsKNZbgJuVB6s8FQBDPYgUXG5fuM88kE65Kd/uVQjPhPHN4l3wiOvhvTiQFUmVsedL4HazS5pITgkdESK+IplhS1cCQgIvgJYL3BuZVlXuYbL2kdq8/zsDIE=",
            "i=1; a=rsa-sha256; t=1776692170; cv=none;\n\td=zohomail.com; s=zohoarc;\n\tb=nXhrsH51VAFBAhM9IQH1yqxwlTzZT0rupwRXGJRR2Ans2Gd2zfBKnpJSfEJMUdJFoGqB37E0ECmjN4bhUzhE7sqaxtujD0bphMA4zLwUa8+v/ISQ1C7wKKQtjYsjsO8Xwnxo+Q+v0Rep6Fk23/Go0CEfLcNr6OVMtToU0DkOJqY="
        ],
        "ARC-Message-Signature": [
            "i=2; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1776692209; c=relaxed/simple;\n\tbh=j8ObxGxHJoGllcbbMiki/8nCk4s/82qozHaJLCpkO7s=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References:\n\t In-Reply-To:To:Cc;\n b=qMpsr2ASyCDOf69j8BRm7HCSIIFHJJQs+me0tUBp7NvjNsDB0c4c/7DfGEMA+WAXz3rtfruw0SJoQWalnVnggb2HxfatXEImH6T/ETF1xikz2/X0pCJ1wE1VUFxsmG/TWOI4WABE/NsKO1vWw3CE/cyFVtmN5eeLrBqpiqaNLm8=",
            "i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com;\n s=zohoarc;\n\tt=1776692170;\n h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To;\n\tbh=t4A5u4kOHHeDSeaKScYKtoN1NxYeqY2oq/9BksWd0Og=;\n\tb=LYYk3jYa+T0VwRNbOvTBShOAwYpJWVH9lbBrnAu0gmf5r3SLDXliAN53ctH0IiLRThY1hdQUECCsK1ccGJ7N343/WnSo1gmuhGYGwCUcudUTAxe5uNxYDdREWUr2OIVrJ+LJou9o0TOQF5D70P5k14Mqk5K3yTC0cMQmA1Ulb2s="
        ],
        "ARC-Authentication-Results": [
            "i=2; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=collabora.com;\n spf=pass smtp.mailfrom=collabora.com;\n dkim=pass (1024-bit key) header.d=collabora.com\n header.i=nicolas.frattaroli@collabora.com header.b=i8fzysou;\n arc=pass smtp.client-ip=136.143.188.112",
            "i=1; mx.zohomail.com;\n\tdkim=pass  header.i=collabora.com;\n\tspf=pass  smtp.mailfrom=nicolas.frattaroli@collabora.com;\n\tdmarc=pass header.from=<nicolas.frattaroli@collabora.com>"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776692170;\n\ts=zohomail; d=collabora.com; i=nicolas.frattaroli@collabora.com;\n\th=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To;\n\tbh=t4A5u4kOHHeDSeaKScYKtoN1NxYeqY2oq/9BksWd0Og=;\n\tb=i8fzysouQjE39RaAuleGouM0PawR06St95Bp+A0DsYf0FETk/8DolHW2o2DLG428\n\tZnOAvOKJVFxI2aLhqc0e1RPAeoxialBM31K+8M6q9hQtp1VVpqlZrgdlz0Y3nPapC8W\n\tNQT2JXitrZo669GMkwOgpKS1GEofWOv8lDgqNG6g=",
        "From": "Nicolas Frattaroli <nicolas.frattaroli@collabora.com>",
        "Date": "Mon, 20 Apr 2026 15:35:20 +0200",
        "Subject": "[PATCH v4 2/5] mfd: Add Rockchip mfpwm driver",
        "Precedence": "bulk",
        "X-Mailing-List": "linux-pwm@vger.kernel.org",
        "List-Id": "<linux-pwm.vger.kernel.org>",
        "List-Subscribe": "<mailto:linux-pwm+subscribe@vger.kernel.org>",
        "List-Unsubscribe": "<mailto:linux-pwm+unsubscribe@vger.kernel.org>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "8bit",
        "Message-Id": "<20260420-rk3576-pwm-v4-2-421738c7bf28@collabora.com>",
        "References": "<20260420-rk3576-pwm-v4-0-421738c7bf28@collabora.com>",
        "In-Reply-To": "<20260420-rk3576-pwm-v4-0-421738c7bf28@collabora.com>",
        "To": "=?utf-8?q?Uwe_Kleine-K=C3=B6nig?= <ukleinek@kernel.org>,\n  Rob Herring <robh@kernel.org>, Krzysztof Kozlowski <krzk+dt@kernel.org>,\n  Conor Dooley <conor+dt@kernel.org>, Heiko Stuebner <heiko@sntech.de>,\n  Lee Jones <lee@kernel.org>, William Breathitt Gray <wbg@kernel.org>,\n  Damon Ding <damon.ding@rock-chips.com>",
        "Cc": "Nicolas Frattaroli <nicolas.frattaroli@collabora.com>,\n kernel@collabora.com, Jonas Karlman <jonas@kwiboo.se>,\n Alexey Charkov <alchark@gmail.com>, linux-rockchip@lists.infradead.org,\n linux-pwm@vger.kernel.org, devicetree@vger.kernel.org,\n linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,\n linux-iio@vger.kernel.org",
        "X-Mailer": "b4 0.15.2"
    },
    "content": "With the Rockchip RK3576, the PWM IP used by Rockchip has changed\nsubstantially. Looking at both the downstream pwm-rockchip driver as\nwell as the mainline pwm-rockchip driver made it clear that with all its\nadditional features and its differences from previous IP revisions, it\nis best supported in a new driver.\n\nThis brings us to the question as to what such a new driver should be.\nTo me, it soon became clear that it should actually be several new\ndrivers, most prominently when Uwe Kleine-König let me know that I\nshould not implement the pwm subsystem's capture callback, but instead\nwrite a counter driver for this functionality.\n\nCombined with the other as-of-yet unimplemented functionality of this\nnew IP, it became apparent that it needs to be spread across several\nsubsystems.\n\nFor this reason, we add a new MFD core driver, called mfpwm (short for\n\"Multi-function PWM\"). This \"parent\" driver makes sure that only one\ndevice function driver is using the device at a time, and is in charge\nof registering the MFD cell devices for the individual device functions\noffered by the device.\n\nAn acquire/release pattern is used to guarantee that device function\ndrivers don't step on each other's toes.\n\nSigned-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\n---\n MAINTAINERS                        |   2 +\n drivers/mfd/Kconfig                |  16 ++\n drivers/mfd/Makefile               |   1 +\n drivers/mfd/rockchip-mfpwm.c       | 357 ++++++++++++++++++++++++++++\n include/linux/mfd/rockchip-mfpwm.h | 470 +++++++++++++++++++++++++++++++++++++\n 5 files changed, 846 insertions(+)",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex 86f20cb563c6..d52731242a33 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -23178,6 +23178,8 @@ L:\tlinux-rockchip@lists.infradead.org\n L:\tlinux-pwm@vger.kernel.org\n S:\tMaintained\n F:\tDocumentation/devicetree/bindings/pwm/rockchip,rk3576-pwm.yaml\n+F:\tdrivers/mfd/rockchip-mfpwm.c\n+F:\tinclude/linux/mfd/rockchip-mfpwm.h\n \n ROCKCHIP RK3568 RANDOM NUMBER GENERATOR SUPPORT\n M:\tDaniel Golle <daniel@makrotopia.org>\ndiff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig\nindex 7192c9d1d268..80b4e82c4937 100644\n--- a/drivers/mfd/Kconfig\n+++ b/drivers/mfd/Kconfig\n@@ -1378,6 +1378,22 @@ config MFD_RC5T583\n \t  Additional drivers must be enabled in order to use the\n \t  different functionality of the device.\n \n+config MFD_ROCKCHIP_MFPWM\n+\ttristate \"Rockchip multi-function PWM controller\"\n+\tdepends on ARCH_ROCKCHIP || COMPILE_TEST\n+\tdepends on OF\n+\tdepends on HAS_IOMEM\n+\tdepends on COMMON_CLK\n+\tselect MFD_CORE\n+\thelp\n+\t  Some Rockchip SoCs, such as the RK3576, use a PWM controller that has\n+\t  several different functions, such as generating PWM waveforms but also\n+\t  counting waveforms.\n+\n+\t  This driver manages the overall device, and selects between different\n+\t  functionalities at runtime as needed. Drivers for them are implemented\n+\t  in their respective subsystems.\n+\n config MFD_RK8XX\n \ttristate\n \tselect MFD_CORE\ndiff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile\nindex e75e8045c28a..ebadbaea9e4a 100644\n--- a/drivers/mfd/Makefile\n+++ b/drivers/mfd/Makefile\n@@ -231,6 +231,7 @@ obj-$(CONFIG_MFD_PALMAS)\t+= palmas.o\n obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o\n obj-$(CONFIG_MFD_NTXEC)\t\t+= ntxec.o\n obj-$(CONFIG_MFD_RC5T583)\t+= rc5t583.o rc5t583-irq.o\n+obj-$(CONFIG_MFD_ROCKCHIP_MFPWM)\t+= rockchip-mfpwm.o\n obj-$(CONFIG_MFD_RK8XX)\t\t+= rk8xx-core.o\n obj-$(CONFIG_MFD_RK8XX_I2C)\t+= rk8xx-i2c.o\n obj-$(CONFIG_MFD_RK8XX_SPI)\t+= rk8xx-spi.o\ndiff --git a/drivers/mfd/rockchip-mfpwm.c b/drivers/mfd/rockchip-mfpwm.c\nnew file mode 100644\nindex 000000000000..72d04982b961\n--- /dev/null\n+++ b/drivers/mfd/rockchip-mfpwm.c\n@@ -0,0 +1,357 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Copyright (c) 2025 Collabora Ltd.\n+ *\n+ * A driver to manage all the different functionalities exposed by Rockchip's\n+ * PWMv4 hardware.\n+ *\n+ * This driver is chiefly focused on guaranteeing non-concurrent operation\n+ * between the different device functions, as well as setting the clocks.\n+ * It registers the device function platform devices, e.g. PWM output or\n+ * PWM capture.\n+ *\n+ * Authors:\n+ *     Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\n+ */\n+\n+#include <linux/array_size.h>\n+#include <linux/clk.h>\n+#include <linux/clk-provider.h>\n+#include <linux/mfd/core.h>\n+#include <linux/mfd/rockchip-mfpwm.h>\n+#include <linux/module.h>\n+#include <linux/of.h>\n+#include <linux/overflow.h>\n+#include <linux/platform_device.h>\n+#include <linux/spinlock.h>\n+\n+/**\n+ * struct rockchip_mfpwm - private mfpwm driver instance state struct\n+ * @pdev: pointer to this instance's &struct platform_device\n+ * @base: pointer to the memory mapped registers of this device\n+ * @pwm_clk: pointer to the PLL clock the PWM signal may be derived from\n+ * @osc_clk: pointer to the fixed crystal the PWM signal may be derived from\n+ * @rc_clk: pointer to the RC oscillator the PWM signal may be derived from\n+ * @chosen_clk: a clk-mux of pwm_clk, osc_clk and rc_clk\n+ * @pclk: pointer to the APB bus clock needed for mmio register access\n+ * @active_func: pointer to the currently active device function, or %NULL if no\n+ *               device function is currently actively using any of the shared\n+ *               resources. May only be checked/modified with @state_lock held.\n+ * @acquire_cnt: number of times @active_func has currently mfpwm_acquire()'d\n+ *               it. Must only be checked or modified while holding @state_lock.\n+ * @state_lock: this lock is held while either the active device function, the\n+ *              enable register, or the chosen clock is being changed.\n+ * @irq: the IRQ number of this device\n+ */\n+struct rockchip_mfpwm {\n+\tstruct platform_device *pdev;\n+\tvoid __iomem *base;\n+\tstruct clk *pwm_clk;\n+\tstruct clk *osc_clk;\n+\tstruct clk *rc_clk;\n+\tstruct clk *chosen_clk;\n+\tstruct clk *pclk;\n+\tstruct rockchip_mfpwm_func *active_func;\n+\tunsigned int acquire_cnt;\n+\tspinlock_t state_lock;\n+\tint irq;\n+};\n+\n+static atomic_t subdev_id = ATOMIC_INIT(0);\n+\n+static inline struct rockchip_mfpwm *to_rockchip_mfpwm(struct platform_device *pdev)\n+{\n+\treturn platform_get_drvdata(pdev);\n+}\n+\n+static int mfpwm_check_pwmf(const struct rockchip_mfpwm_func *pwmf,\n+\t\t\t    const char *fname)\n+{\n+\tstruct device *dev = &pwmf->parent->pdev->dev;\n+\n+\tif (IS_ERR_OR_NULL(pwmf)) {\n+\t\tdev_warn(dev, \"called %s with an erroneous handle, no effect\\n\",\n+\t\t\t fname);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (IS_ERR_OR_NULL(pwmf->parent)) {\n+\t\tdev_warn(dev, \"called %s with an erroneous mfpwm_func parent, no effect\\n\",\n+\t\t\t fname);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+__attribute__((nonnull))\n+static int mfpwm_do_acquire(struct rockchip_mfpwm_func *pwmf)\n+{\n+\tstruct rockchip_mfpwm *mfpwm = pwmf->parent;\n+\tunsigned int cnt;\n+\n+\tif (mfpwm->active_func && pwmf->id != mfpwm->active_func->id)\n+\t\treturn -EBUSY;\n+\n+\tif (!mfpwm->active_func)\n+\t\tmfpwm->active_func = pwmf;\n+\n+\tif (!check_add_overflow(mfpwm->acquire_cnt, 1, &cnt)) {\n+\t\tmfpwm->acquire_cnt = cnt;\n+\t} else {\n+\t\tdev_warn(&mfpwm->pdev->dev, \"prevented acquire counter overflow in %s\\n\",\n+\t\t\t __func__);\n+\t\treturn -EOVERFLOW;\n+\t}\n+\n+\tdev_dbg(&mfpwm->pdev->dev, \"%d acquired mfpwm, acquires now at %u\\n\",\n+\t\tpwmf->id, mfpwm->acquire_cnt);\n+\n+\treturn clk_enable(mfpwm->pclk);\n+}\n+\n+int mfpwm_acquire(struct rockchip_mfpwm_func *pwmf)\n+{\n+\tstruct rockchip_mfpwm *mfpwm;\n+\tunsigned long flags;\n+\tint ret = 0;\n+\n+\tret = mfpwm_check_pwmf(pwmf, \"mfpwm_acquire\");\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tmfpwm = pwmf->parent;\n+\tdev_dbg(&mfpwm->pdev->dev, \"%d is attempting to acquire\\n\", pwmf->id);\n+\n+\tif (!spin_trylock_irqsave(&mfpwm->state_lock, flags))\n+\t\treturn -EBUSY;\n+\n+\tret = mfpwm_do_acquire(pwmf);\n+\n+\tspin_unlock_irqrestore(&mfpwm->state_lock, flags);\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_NS_GPL(mfpwm_acquire, \"ROCKCHIP_MFPWM\");\n+\n+__attribute__((nonnull))\n+static void mfpwm_do_release(const struct rockchip_mfpwm_func *pwmf)\n+{\n+\tstruct rockchip_mfpwm *mfpwm = pwmf->parent;\n+\n+\tif (!mfpwm->active_func)\n+\t\treturn;\n+\n+\tif (mfpwm->active_func->id != pwmf->id)\n+\t\treturn;\n+\n+\t/*\n+\t * No need to check_sub_overflow here, !mfpwm->active_func above catches\n+\t * this type of problem already.\n+\t */\n+\tmfpwm->acquire_cnt--;\n+\n+\tif (!mfpwm->acquire_cnt)\n+\t\tmfpwm->active_func = NULL;\n+\n+\tclk_disable(mfpwm->pclk);\n+}\n+\n+void mfpwm_release(const struct rockchip_mfpwm_func *pwmf)\n+{\n+\tstruct rockchip_mfpwm *mfpwm;\n+\tunsigned long flags;\n+\n+\tif (mfpwm_check_pwmf(pwmf, \"mfpwm_release\"))\n+\t\treturn;\n+\n+\tmfpwm = pwmf->parent;\n+\n+\tspin_lock_irqsave(&mfpwm->state_lock, flags);\n+\tmfpwm_do_release(pwmf);\n+\tdev_dbg(&mfpwm->pdev->dev, \"%d released mfpwm, acquires now at %u\\n\",\n+\t\tpwmf->id, mfpwm->acquire_cnt);\n+\tspin_unlock_irqrestore(&mfpwm->state_lock, flags);\n+}\n+EXPORT_SYMBOL_NS_GPL(mfpwm_release, \"ROCKCHIP_MFPWM\");\n+\n+int mfpwm_get_mode(const struct rockchip_mfpwm_func *pwmf)\n+{\n+\tstruct rockchip_mfpwm *mfpwm;\n+\tint ret;\n+\n+\tret = mfpwm_check_pwmf(pwmf, \"mfpwm_acquire\");\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tmfpwm = pwmf->parent;\n+\n+\tguard(spinlock_irqsave)(&mfpwm->state_lock);\n+\n+\tif (!rockchip_pwm_v4_is_enabled(mfpwm_reg_read(mfpwm->base, PWMV4_REG_ENABLE)))\n+\t\treturn -1;\n+\n+\treturn mfpwm_reg_read(mfpwm->base, PWMV4_REG_CTRL) & PWMV4_MODE_MASK;\n+}\n+EXPORT_SYMBOL_NS_GPL(mfpwm_get_mode, \"ROCKCHIP_MFPWM\");\n+\n+/**\n+ * mfpwm_register_subdev - register a single mfpwm_func\n+ * @mfpwm: pointer to the parent &struct rockchip_mfpwm\n+ * @name: sub-device name string\n+ *\n+ * Allocate a single &struct mfpwm_func, fill its members with appropriate data,\n+ * and register a new mfd cell.\n+ *\n+ * Returns: 0 on success, negative errno on error\n+ */\n+static int mfpwm_register_subdev(struct rockchip_mfpwm *mfpwm,\n+\t\t\t\t const char *name)\n+{\n+\tstruct rockchip_mfpwm_func *func;\n+\tstruct mfd_cell cell = {};\n+\n+\tfunc = devm_kzalloc(&mfpwm->pdev->dev, sizeof(*func), GFP_KERNEL);\n+\tif (IS_ERR(func))\n+\t\treturn PTR_ERR(func);\n+\tfunc->irq = mfpwm->irq;\n+\tfunc->parent = mfpwm;\n+\tfunc->id = atomic_inc_return(&subdev_id);\n+\tfunc->base = mfpwm->base;\n+\tfunc->core = mfpwm->chosen_clk;\n+\tcell.name = name;\n+\tcell.platform_data = func;\n+\tcell.pdata_size = sizeof(*func);\n+\n+\treturn devm_mfd_add_devices(&mfpwm->pdev->dev, func->id, &cell, 1, NULL,\n+\t\t\t\t    0, NULL);\n+}\n+\n+static int mfpwm_register_subdevs(struct rockchip_mfpwm *mfpwm)\n+{\n+\tint ret;\n+\n+\tret = mfpwm_register_subdev(mfpwm, \"rockchip-pwm-v4\");\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = mfpwm_register_subdev(mfpwm, \"rockchip-pwm-capture\");\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+static int rockchip_mfpwm_probe(struct platform_device *pdev)\n+{\n+\tstruct device *dev = &pdev->dev;\n+\tstruct rockchip_mfpwm *mfpwm;\n+\tchar *clk_mux_name;\n+\tconst char *mux_p_names[3];\n+\tint ret = 0;\n+\n+\tmfpwm = devm_kzalloc(&pdev->dev, sizeof(*mfpwm), GFP_KERNEL);\n+\tif (IS_ERR(mfpwm))\n+\t\treturn PTR_ERR(mfpwm);\n+\n+\tmfpwm->pdev = pdev;\n+\n+\tspin_lock_init(&mfpwm->state_lock);\n+\n+\tmfpwm->base = devm_platform_ioremap_resource(pdev, 0);\n+\tif (IS_ERR(mfpwm->base))\n+\t\treturn dev_err_probe(dev, PTR_ERR(mfpwm->base),\n+\t\t\t\t     \"failed to ioremap address\\n\");\n+\n+\tmfpwm->pclk = devm_clk_get_prepared(dev, \"pclk\");\n+\tif (IS_ERR(mfpwm->pclk))\n+\t\treturn dev_err_probe(dev, PTR_ERR(mfpwm->pclk),\n+\t\t\t\t     \"couldn't get and prepare 'pclk' clock\\n\");\n+\n+\tmfpwm->irq = platform_get_irq(pdev, 0);\n+\tif (mfpwm->irq < 0)\n+\t\treturn dev_err_probe(dev, mfpwm->irq, \"couldn't get irq 0\\n\");\n+\n+\tmfpwm->pwm_clk = devm_clk_get_prepared(dev, \"pwm\");\n+\tif (IS_ERR(mfpwm->pwm_clk))\n+\t\treturn dev_err_probe(dev, PTR_ERR(mfpwm->pwm_clk),\n+\t\t\t\t     \"couldn't get and prepare 'pwm' clock\\n\");\n+\n+\tmfpwm->osc_clk = devm_clk_get_prepared(dev, \"osc\");\n+\tif (IS_ERR(mfpwm->osc_clk))\n+\t\treturn dev_err_probe(dev, PTR_ERR(mfpwm->osc_clk),\n+\t\t\t\t     \"couldn't get and prepare 'osc' clock\\n\");\n+\n+\tmfpwm->rc_clk = devm_clk_get_prepared(dev, \"rc\");\n+\tif (IS_ERR(mfpwm->rc_clk))\n+\t\treturn dev_err_probe(dev, PTR_ERR(mfpwm->rc_clk),\n+\t\t\t\t     \"couldn't get and prepare 'rc' clock\\n\");\n+\n+\tclk_mux_name = devm_kasprintf(dev, GFP_KERNEL, \"%s_chosen\", dev_name(dev));\n+\tif (!clk_mux_name)\n+\t\treturn -ENOMEM;\n+\n+\tmux_p_names[0] = __clk_get_name(mfpwm->pwm_clk);\n+\tmux_p_names[1] = __clk_get_name(mfpwm->osc_clk);\n+\tmux_p_names[2] = __clk_get_name(mfpwm->rc_clk);\n+\tmfpwm->chosen_clk = clk_register_mux(dev, clk_mux_name, mux_p_names,\n+\t\t\t\t\t     ARRAY_SIZE(mux_p_names),\n+\t\t\t\t\t     CLK_SET_RATE_PARENT,\n+\t\t\t\t\t     mfpwm->base + PWMV4_REG_CLK_CTRL,\n+\t\t\t\t\t     PWMV4_CLK_SRC_SHIFT, PWMV4_CLK_SRC_WIDTH,\n+\t\t\t\t\t     CLK_MUX_HIWORD_MASK, NULL);\n+\tret = clk_prepare(mfpwm->chosen_clk);\n+\tif (ret) {\n+\t\tdev_err(dev, \"failed to prepare PWM clock mux: %pe\\n\",\n+\t\t\tERR_PTR(ret));\n+\t\treturn ret;\n+\t}\n+\n+\tplatform_set_drvdata(pdev, mfpwm);\n+\n+\tret = mfpwm_register_subdevs(mfpwm);\n+\tif (ret) {\n+\t\tdev_err(dev, \"failed to register sub-devices: %pe\\n\",\n+\t\t\tERR_PTR(ret));\n+\t\treturn ret;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static void rockchip_mfpwm_remove(struct platform_device *pdev)\n+{\n+\tstruct rockchip_mfpwm *mfpwm = to_rockchip_mfpwm(pdev);\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(&mfpwm->state_lock, flags);\n+\n+\tif (mfpwm->chosen_clk) {\n+\t\tclk_unprepare(mfpwm->chosen_clk);\n+\t\tclk_unregister_mux(mfpwm->chosen_clk);\n+\t}\n+\n+\tspin_unlock_irqrestore(&mfpwm->state_lock, flags);\n+}\n+\n+static const struct of_device_id rockchip_mfpwm_of_match[] = {\n+\t{\n+\t\t.compatible = \"rockchip,rk3576-pwm\",\n+\t},\n+\t{ /* sentinel */ }\n+};\n+MODULE_DEVICE_TABLE(of, rockchip_mfpwm_of_match);\n+\n+static struct platform_driver rockchip_mfpwm_driver = {\n+\t.driver = {\n+\t\t.name = KBUILD_MODNAME,\n+\t\t.of_match_table = rockchip_mfpwm_of_match,\n+\t},\n+\t.probe = rockchip_mfpwm_probe,\n+\t.remove = rockchip_mfpwm_remove,\n+};\n+module_platform_driver(rockchip_mfpwm_driver);\n+\n+MODULE_AUTHOR(\"Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\");\n+MODULE_DESCRIPTION(\"Rockchip MFPWM Driver\");\n+MODULE_LICENSE(\"GPL\");\ndiff --git a/include/linux/mfd/rockchip-mfpwm.h b/include/linux/mfd/rockchip-mfpwm.h\nnew file mode 100644\nindex 000000000000..dbf1588a4382\n--- /dev/null\n+++ b/include/linux/mfd/rockchip-mfpwm.h\n@@ -0,0 +1,470 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (c) 2025 Collabora Ltd.\n+ *\n+ * Common header file for all the Rockchip Multi-function PWM controller\n+ * drivers that are spread across subsystems.\n+ *\n+ * Authors:\n+ *     Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\n+ */\n+\n+#ifndef __SOC_ROCKCHIP_MFPWM_H__\n+#define __SOC_ROCKCHIP_MFPWM_H__\n+\n+#include <linux/bits.h>\n+#include <linux/clk.h>\n+#include <linux/hw_bitfield.h>\n+#include <linux/io.h>\n+#include <linux/spinlock.h>\n+\n+struct rockchip_mfpwm;\n+\n+/**\n+ * struct rockchip_mfpwm_func - struct representing a single function driver\n+ *\n+ * @id: unique id for this function driver instance\n+ * @base: pointer to start of MMIO registers\n+ * @parent: a pointer to the parent mfpwm struct\n+ * @irq: the shared IRQ gotten from the parent mfpwm device\n+ * @core: a pointer to the clk mux that drives this channel's PWM\n+ */\n+struct rockchip_mfpwm_func {\n+\tint id;\n+\tvoid __iomem *base;\n+\tstruct rockchip_mfpwm *parent;\n+\tint irq;\n+\tstruct clk *core;\n+};\n+\n+/*\n+ * PWMV4 Register Definitions\n+ * --------------------------\n+ *\n+ * Attributes:\n+ *  RW  - Read-Write\n+ *  RO  - Read-Only\n+ *  WO  - Write-Only\n+ *  W1T - Write high, Self-clearing\n+ *  W1C - Write high to clear interrupt\n+ *\n+ * Bit ranges to be understood with Verilog-like semantics,\n+ * e.g. [03:00] is 4 bits: 0, 1, 2 and 3.\n+ *\n+ * All registers must be accessed with 32-bit width accesses only\n+ */\n+\n+#define PWMV4_REG_VERSION\t\t0x000\n+/*\n+ * VERSION Register Description\n+ * [31:24] RO  | Hardware Major Version\n+ * [23:16] RO  | Hardware Minor Version\n+ * [15:15] RO  | Reserved\n+ * [14:14] RO  | Hardware supports biphasic counters\n+ * [13:13] RO  | Hardware supports filters\n+ * [12:12] RO  | Hardware supports waveform generation\n+ * [11:11] RO  | Hardware supports counter\n+ * [10:10] RO  | Hardware supports frequency metering\n+ * [09:09] RO  | Hardware supports power key functionality\n+ * [08:08] RO  | Hardware supports infrared transmissions\n+ * [07:04] RO  | Channel index of this instance\n+ * [03:00] RO  | Number of channels the base instance supports\n+ */\n+\n+#define PWMV4_REG_ENABLE\t\t0x004\n+/*\n+ * ENABLE Register Description\n+ * [31:16] WO  | Write Enable Mask for the lower half of the register\n+ *               Set bit `n` here to 1 if you wish to modify bit `n >> 16` in\n+ *               the same write operation\n+ * [15:06] RO  | Reserved\n+ * [05:05] RW  | PWM Channel Counter Read Enable, 1 = enabled\n+ */\n+#define PWMV4_CHN_CNT_RD_EN(v)\t\tFIELD_PREP_WM16(BIT(5), (v))\n+/*\n+ * [04:04] W1T | PWM Globally Joined Control Enable\n+ *               1 = this PWM channel will be enabled by a global pwm enable\n+ *               bit instead of the PWM Enable bit.\n+ */\n+#define PWMV4_GLOBAL_CTRL_EN(v)\t\tFIELD_PREP_WM16(BIT(4), (v))\n+/*\n+ * [03:03] RW  | Force Clock Enable\n+ *               0 = disabled, if the PWM channel is inactive then so is the\n+ *               clock prescale module\n+ */\n+#define PWMV4_FORCE_CLK_EN(v)\t\tFIELD_PREP_WM16(BIT(3), (v))\n+/*\n+ * [02:02] W1T | PWM Control Update Enable\n+ *               1 = enabled, commits modifications of _CTRL, _PERIOD, _DUTY and\n+ *               _OFFSET registers once 1 is written to it\n+ */\n+#define PWMV4_CTRL_UPDATE_EN\t\tFIELD_PREP_WM16_CONST(BIT(2), 1)\n+/*\n+ * [01:01] RW  | PWM Enable, 1 = enabled\n+ *               If in one-shot mode, clears after end of operation\n+ */\n+#define PWMV4_EN_MASK\t\t\tBIT(1)\n+#define PWMV4_EN(v)\t\t\tFIELD_PREP_WM16(PWMV4_EN_MASK, \\\n+\t\t\t\t\t\t\t((v) ? 1 : 0))\n+/*\n+ * [00:00] RW  | PWM Clock Enable, 1 = enabled\n+ *               If in one-shot mode, clears after end of operation\n+ */\n+#define PWMV4_CLK_EN_MASK\t\tBIT(0)\n+#define PWMV4_CLK_EN(v)\t\t\tFIELD_PREP_WM16(PWMV4_CLK_EN_MASK, \\\n+\t\t\t\t\t\t\t((v) ? 1 : 0))\n+#define PWMV4_EN_BOTH_MASK\t\t(PWMV4_EN_MASK | PWMV4_CLK_EN_MASK)\n+static inline __pure bool rockchip_pwm_v4_is_enabled(unsigned int val)\n+{\n+\treturn (val & PWMV4_EN_BOTH_MASK);\n+}\n+\n+#define PWMV4_REG_CLK_CTRL\t\t0x008\n+/*\n+ * CLK_CTRL Register Description\n+ * [31:16] WO  | Write Enable Mask for the lower half of the register\n+ *               Set bit `n` here to 1 if you wish to modify bit `n >> 16` in\n+ *               the same write operation\n+ * [15:15] RW  | Clock Global Selection\n+ *               0 = current channel scale clock\n+ *               1 = global channel scale clock\n+ */\n+#define PWMV4_CLK_GLOBAL(v)\t\tFIELD_PREP_WM16(BIT(15), (v))\n+/*\n+ * [14:13] RW  | Clock Source Selection\n+ *               0 = Clock from PLL, frequency can be configured\n+ *               1 = Clock from crystal oscillator, frequency is fixed\n+ *               2 = Clock from RC oscillator, frequency is fixed\n+ *               3 = Reserved\n+ *               NOTE: The purpose for this clock-mux-outside-CRU construct is\n+ *                     to let the SoC go into a sleep state with the PWM\n+ *                     hardware still having a clock signal for IR input, which\n+ *                     can then wake up the SoC.\n+ */\n+#define PWMV4_CLK_SRC_PLL\t\t0x0U\n+#define PWMV4_CLK_SRC_CRYSTAL\t\t0x1U\n+#define PWMV4_CLK_SRC_RC\t\t0x2U\n+#define PWMV4_CLK_SRC_SHIFT\t\t13\n+#define PWMV4_CLK_SRC_WIDTH\t\t2\n+/*\n+ * [12:04] RW  | Scale Factor to apply to pre-scaled clock\n+ *               1 <= v <= 256, v means clock divided by 2*v\n+ */\n+#define PWMV4_CLK_SCALE_F(v)\t\tFIELD_PREP_WM16(GENMASK(12, 4), (v))\n+/*\n+ * [03:03] RO  | Reserved\n+ * [02:00] RW  | Prescale Factor\n+ *               v here means the input clock is divided by pow(2, v)\n+ */\n+#define PWMV4_CLK_PRESCALE_F(v)\t\tFIELD_PREP_WM16(GENMASK(2, 0), (v))\n+\n+#define PWMV4_REG_CTRL\t\t\t0x00C\n+/*\n+ * CTRL Register Description\n+ * [31:16] WO  | Write Enable Mask for the lower half of the register\n+ *               Set bit `n` here to 1 if you wish to modify bit `n >> 16` in\n+ *               the same write operation\n+ * [15:09] RO  | Reserved\n+ * [08:06] RW  | PWM Input Channel Selection\n+ *               By default, the channel selects its own input, but writing v\n+ *               here selects PWM input from channel v instead.\n+ */\n+#define PWMV4_CTRL_IN_SEL(v)\t\tFIELD_PREP_WM16(GENMASK(8, 6), (v))\n+/* [05:05] RW  | Aligned Mode, 0 = Valid, 1 = Invalid */\n+#define PWMV4_CTRL_UNALIGNED(v)\t\tFIELD_PREP_WM16(BIT(5), (v))\n+/* [04:04] RW  | Output Mode, 0 = Left Aligned, 1 = Centre Aligned */\n+#define PWMV4_LEFT_ALIGNED\t\t0x0U\n+#define PWMV4_CENTRE_ALIGNED\t\t0x1U\n+#define PWMV4_CTRL_OUT_MODE(v)\t\tFIELD_PREP_WM16(BIT(4), (v))\n+/*\n+ * [03:03] RW  | Inactive Polarity for when the channel is either disabled or\n+ *               has completed outputting the entire waveform in one-shot mode.\n+ *               0 = Negative, 1 = Positive\n+ */\n+#define PWMV4_POLARITY_N\t\t0x0U\n+#define PWMV4_POLARITY_P\t\t0x1U\n+#define PWMV4_INACTIVE_POL(v)\t\tFIELD_PREP_WM16(BIT(3), (v))\n+/*\n+ * [02:02] RW  | Duty Cycle Polarity to use at the start of the waveform.\n+ *               0 = Negative, 1 = Positive\n+ */\n+#define PWMV4_DUTY_POL_SHIFT\t\t2\n+#define PWMV4_DUTY_POL_MASK\t\tBIT(PWMV4_DUTY_POL_SHIFT)\n+#define PWMV4_DUTY_POL(v)\t\tFIELD_PREP_WM16(PWMV4_DUTY_POL_MASK, \\\n+\t\t\t\t\t\t\t(v))\n+/*\n+ * [01:00] RW  | PWM Mode\n+ *               0 = One-shot mode, PWM generates waveform RPT times\n+ *               1 = Continuous mode\n+ *               2 = Capture mode, PWM measures cycles of input waveform\n+ *               3 = Reserved\n+ */\n+#define PWMV4_MODE_ONESHOT\t\t0x0U\n+#define PWMV4_MODE_CONT\t\t\t0x1U\n+#define PWMV4_MODE_CAPTURE\t\t0x2U\n+#define PWMV4_MODE_MASK\t\t\tGENMASK(1, 0)\n+#define PWMV4_MODE(v)\t\t\tFIELD_PREP_WM16(PWMV4_MODE_MASK, (v))\n+#define PWMV4_CTRL_COM_FLAGS\t(PWMV4_INACTIVE_POL(PWMV4_POLARITY_N) | \\\n+\t\t\t\t PWMV4_DUTY_POL(PWMV4_POLARITY_P) | \\\n+\t\t\t\t PWMV4_CTRL_OUT_MODE(PWMV4_LEFT_ALIGNED) | \\\n+\t\t\t\t PWMV4_CTRL_UNALIGNED(true))\n+#define PWMV4_CTRL_CONT_FLAGS\t(PWMV4_MODE(PWMV4_MODE_CONT) | \\\n+\t\t\t\t PWMV4_CTRL_COM_FLAGS)\n+#define PWMV4_CTRL_CAP_FLAGS\t(PWMV4_MODE(PWMV4_MODE_CAPTURE) | \\\n+\t\t\t\t PWMV4_CTRL_COM_FLAGS)\n+\n+#define PWMV4_REG_PERIOD\t\t0x010\n+/*\n+ * PERIOD Register Description\n+ * [31:00] RW  | Period of the output waveform\n+ *               Constraints: should be even if CTRL_OUT_MODE is CENTRE_ALIGNED\n+ */\n+\n+#define PWMV4_REG_DUTY\t\t\t0x014\n+/*\n+ * DUTY Register Description\n+ * [31:00] RW  | Duty cycle of the output waveform\n+ *               Constraints: should be even if CTRL_OUT_MODE is CENTRE_ALIGNED\n+ */\n+\n+#define PWMV4_REG_OFFSET\t\t0x018\n+/*\n+ * OFFSET Register Description\n+ * [31:00] RW  | Offset of the output waveform, based on the PWM clock\n+ *               Constraints: 0 <= v <= (PERIOD - DUTY)\n+ */\n+\n+#define PWMV4_REG_RPT\t\t\t0x01C\n+/*\n+ * RPT Register Description\n+ * [31:16] RW  | Second dimensional of the effective number of waveform\n+ *               repetitions. Increases by one every first dimensional times.\n+ *               Value `n` means `n + 1` repetitions. The final number of\n+ *               repetitions of the waveform in one-shot mode is:\n+ *               `(first_dimensional + 1) * (second_dimensional + 1)`\n+ * [15:00] RW  | First dimensional of the effective number of waveform\n+ *               repetitions. Value `n` means `n + 1` repetitions.\n+ */\n+\n+#define PWMV4_REG_FILTER_CTRL\t\t0x020\n+/*\n+ * FILTER_CTRL Register Description\n+ * [31:16] WO  | Write Enable Mask for the lower half of the register\n+ *               Set bit `n` here to 1 if you wish to modify bit `n >> 16` in\n+ *               the same write operation\n+ * [15:10] RO  | Reserved\n+ * [09:04] RW  | Filter window number\n+ * [03:01] RO  | Reserved\n+ * [00:00] RW  | Filter Enable, 0 = disabled, 1 = enabled\n+ */\n+\n+#define PWMV4_REG_CNT\t\t\t0x024\n+/*\n+ * CNT Register Description\n+ * [31:00] RO  | Current value of the PWM Channel 0 counter in pwm clock cycles,\n+ *               0 <= v <= 2^32-1\n+ */\n+\n+#define PWMV4_REG_ENABLE_DELAY\t\t0x028\n+/*\n+ * ENABLE_DELAY Register Description\n+ * [31:16] RO  | Reserved\n+ * [15:00] RW  | PWM enable delay, in an unknown unit but probably cycles\n+ */\n+\n+#define PWMV4_REG_HPC\t\t\t0x02C\n+/*\n+ * HPC Register Description\n+ * [31:00] RW  | Number of effective high polarity cycles of the input waveform\n+ *               in capture mode. Based on the PWM clock. 0 <= v <= 2^32-1\n+ */\n+\n+#define PWMV4_REG_LPC\t\t\t0x030\n+/*\n+ * LPC Register Description\n+ * [31:00] RW  | Number of effective low polarity cycles of the input waveform\n+ *               in capture mode. Based on the PWM clock. 0 <= v <= 2^32-1\n+ */\n+\n+#define PWMV4_REG_BIPHASIC_CNT_CTRL0\t0x040\n+/*\n+ * BIPHASIC_CNT_CTRL0 Register Description\n+ * [31:16] WO  | Write Enable Mask for the lower half of the register\n+ *               Set bit `n` here to 1 if you wish to modify bit `n >> 16` in\n+ *               the same write operation\n+ * [15:10] RO  | Reserved\n+ * [09:09] RW  | Biphasic Counter Phase Edge Selection for mode 0,\n+ *               0 = rising edge (posedge), 1 = falling edge (negedge)\n+ * [08:08] RW  | Biphasic Counter Clock force enable, 1 = force enable\n+ * [07:07] W1T | Synchronous Enable\n+ * [06:06] W1T | Mode Switch\n+ *               0 = Normal Mode, 1 = Switch timer clock and measured clock\n+ *               Constraints: \"Biphasic Counter Mode\" must be 0 if this is 1\n+ * [05:03] RW  | Biphasic Counter Mode\n+ *               0x0 = Mode 0, 0x1 = Mode 1, 0x2 = Mode 2, 0x3 = Mode 3,\n+ *               0x4 = Mode 4, 0x5 = Reserved\n+ * [02:02] RW  | Biphasic Counter Clock Selection\n+ *               0 = clock is from PLL and frequency can be configured\n+ *               1 = clock is from crystal oscillator and frequency is fixed\n+ * [01:01] RW  | Biphasic Counter Continuous Mode\n+ * [00:00] W1T | Biphasic Counter Enable\n+ */\n+\n+#define PWMV4_REG_BIPHASIC_CNT_CTRL1\t0x044\n+/*\n+ * BIPHASIC_CNT_CTRL1 Register Description\n+ * [31:16] WO  | Write Enable Mask for the lower half of the register\n+ *               Set bit `n` here to 1 if you wish to modify bit `n >> 16` in\n+ *               the same write operation\n+ * [15:11] RO  | Reserved\n+ * [10:04] RW  | Biphasic Counter Filter Window Number\n+ * [03:01] RO  | Reserved\n+ * [00:00] RW  | Biphasic Counter Filter Enable\n+ */\n+\n+#define PWMV4_REG_BIPHASIC_CNT_TIMER\t0x048\n+/*\n+ * BIPHASIC_CNT_TIMER Register Description\n+ * [31:00] RW  | Biphasic Counter Timer Value, in number of biphasic counter\n+ *               timer clock cycles\n+ */\n+\n+#define PWMV4_REG_BIPHASIC_CNT_RES\t0x04C\n+/*\n+ * BIPHASIC_CNT_RES Register Description\n+ * [31:00] RO  | Biphasic Counter Result Value\n+ *               Constraints: Can only be read after INTSTS[9] is asserted\n+ */\n+\n+#define PWMV4_REG_BIPHASIC_CNT_RES_S\t0x050\n+/*\n+ * BIPHASIC_CNT_RES_S Register Description\n+ * [31:00] RO  | Biphasic Counter Result Value with synchronised processing\n+ *               Can be read in real-time if BIPHASIC_CNT_CTRL0[7] was set to 1\n+ */\n+\n+#define PWMV4_REG_INTSTS\t\t0x070\n+/*\n+ * INTSTS Register Description\n+ * [31:10] RO  | Reserved\n+ * [09:09] W1C | Biphasic Counter Interrupt Status, 1 = interrupt asserted\n+ * [08:08] W1C | Waveform Middle Interrupt Status, 1 = interrupt asserted\n+ * [07:07] W1C | Waveform Max Interrupt Status, 1 = interrupt asserted\n+ * [06:06] W1C | IR Transmission End Interrupt Status, 1 = interrupt asserted\n+ * [05:05] W1C | Power Key Match Interrupt Status, 1 = interrupt asserted\n+ * [04:04] W1C | Frequency Meter Interrupt Status, 1 = interrupt asserted\n+ * [03:03] W1C | Reload Interrupt Status, 1 = interrupt asserted\n+ * [02:02] W1C | Oneshot End Interrupt Status, 1 = interrupt asserted\n+ * [01:01] W1C | HPC Capture Interrupt Status, 1 = interrupt asserted\n+ * [00:00] W1C | LPC Capture Interrupt Status, 1 = interrupt asserted\n+ */\n+#define PWMV4_INT_LPC\t\t\tBIT(0)\n+#define PWMV4_INT_HPC\t\t\tBIT(1)\n+#define PWMV4_INT_LPC_W(v)\t\tFIELD_PREP_WM16(PWMV4_INT_LPC, \\\n+\t\t\t\t\t\t\t((v) ? 1 : 0))\n+#define PWMV4_INT_HPC_W(v)\t\tFIELD_PREP_WM16(PWMV4_INT_HPC, \\\n+\t\t\t\t\t\t\t((v) ? 1 : 0))\n+\n+#define PWMV4_REG_INT_EN\t\t0x074\n+/*\n+ * INT_EN Register Description\n+ * [31:16] WO  | Write Enable Mask for the lower half of the register\n+ *               Set bit `n` here to 1 if you wish to modify bit `n >> 16` in\n+ *               the same write operation\n+ * [15:10] RO  | Reserved\n+ * [09:09] RW  | Biphasic Counter Interrupt Enable, 1 = enabled\n+ * [08:08] W1C | Waveform Middle Interrupt Enable, 1 = enabled\n+ * [07:07] W1C | Waveform Max Interrupt Enable, 1 = enabled\n+ * [06:06] W1C | IR Transmission End Interrupt Enable, 1 = enabled\n+ * [05:05] W1C | Power Key Match Interrupt Enable, 1 = enabled\n+ * [04:04] W1C | Frequency Meter Interrupt Enable, 1 = enabled\n+ * [03:03] W1C | Reload Interrupt Enable, 1 = enabled\n+ * [02:02] W1C | Oneshot End Interrupt Enable, 1 = enabled\n+ * [01:01] W1C | HPC Capture Interrupt Enable, 1 = enabled\n+ * [00:00] W1C | LPC Capture Interrupt Enable, 1 = enabled\n+ */\n+\n+#define PWMV4_REG_INT_MASK\t\t0x078\n+/*\n+ * INT_MASK Register Description\n+ * [31:16] WO  | Write Enable Mask for the lower half of the register\n+ *               Set bit `n` here to 1 if you wish to modify bit `n >> 16` in\n+ *               the same write operation\n+ * [15:10] RO  | Reserved\n+ * [09:09] RW  | Biphasic Counter Interrupt Masked, 1 = masked\n+ * [08:08] W1C | Waveform Middle Interrupt Masked, 1 = masked\n+ * [07:07] W1C | Waveform Max Interrupt Masked, 1 = masked\n+ * [06:06] W1C | IR Transmission End Interrupt Masked, 1 = masked\n+ * [05:05] W1C | Power Key Match Interrupt Masked, 1 = masked\n+ * [04:04] W1C | Frequency Meter Interrupt Masked, 1 = masked\n+ * [03:03] W1C | Reload Interrupt Masked, 1 = masked\n+ * [02:02] W1C | Oneshot End Interrupt Masked, 1 = masked\n+ * [01:01] W1C | HPC Capture Interrupt Masked, 1 = masked\n+ * [00:00] W1C | LPC Capture Interrupt Masked, 1 = masked\n+ */\n+\n+static inline u32 mfpwm_reg_read(void __iomem *base, u32 reg)\n+{\n+\treturn readl(base + reg);\n+}\n+\n+static inline void mfpwm_reg_write(void __iomem *base, u32 reg, u32 val)\n+{\n+\twritel(val, base + reg);\n+}\n+\n+/**\n+ * mfpwm_acquire - try becoming the active mfpwm function device\n+ * @pwmf: pointer to the calling driver instance's &struct rockchip_mfpwm_func\n+ *\n+ * mfpwm device \"function\" drivers must call this function before doing anything\n+ * that either modifies or relies on the parent device's state, such as clocks,\n+ * enabling/disabling outputs, modifying shared regs etc.\n+ *\n+ * The return statues should always be checked.\n+ *\n+ * All mfpwm_acquire() calls must be balanced with corresponding mfpwm_release()\n+ * calls once the device is no longer making changes that affect other devices,\n+ * or stops producing user-visible effects that depend on the current device\n+ * state being kept as-is. (e.g. after the PWM output signal is stopped)\n+ *\n+ * The same device function may mfpwm_acquire() multiple times while it already\n+ * is active, i.e. it is re-entrant, though it needs to balance this with the\n+ * same number of mfpwm_release() calls.\n+ *\n+ * Context: This function does not sleep.\n+ *\n+ * Return:\n+ * * %0                 - success\n+ * * %-EBUSY            - a different device function is active\n+ * * %-EOVERFLOW        - the acquire counter is at its maximum\n+ */\n+extern int __must_check mfpwm_acquire(struct rockchip_mfpwm_func *pwmf);\n+\n+/**\n+ * mfpwm_release - drop usage of active mfpwm device function by 1\n+ * @pwmf: pointer to the calling driver instance's &struct rockchip_mfpwm_func\n+ *\n+ * This is the balancing call to mfpwm_acquire(). If no users of the device\n+ * function remain, set the mfpwm device to have no active device function,\n+ * allowing other device functions to claim it.\n+ */\n+extern void mfpwm_release(const struct rockchip_mfpwm_func *pwmf);\n+\n+/**\n+ * mfpwm_get_mode - get the current mode the hardware is in\n+ * @pwmf: pointer to a &struct rockchip_mfpwm_func\n+ *\n+ * Check the hardware registers of the PWM hardware to determine which mode it\n+ * is currently operating in, if any.\n+ *\n+ * Returns:\n+ *   - %-EINVAL if @pwmf is %NULL or an error pointer\n+ *   - %-1 if the PWM hardware is off, regardless of operating mode\n+ *   - %PWMV4_MODE_ONESHOT if PWM hardware is in one-shot output mode\n+ *   - %PWMV4_MODE_CONT if PWM hardware is in continuous output mode\n+ *   - %PWMV4_MODE_CAPTURE if PWM hardware is in capture mode\n+ */\n+extern int mfpwm_get_mode(const struct rockchip_mfpwm_func *pwmf);\n+\n+#endif /* __SOC_ROCKCHIP_MFPWM_H__ */\n",
    "prefixes": [
        "v4",
        "2/5"
    ]
}