Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2225189/?format=api
{ "id": 2225189, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2225189/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pwm/patch/20260420-rk3576-pwm-v4-3-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-3-421738c7bf28@collabora.com>", "date": "2026-04-20T13:35:21", "name": "[v4,3/5] pwm: Add rockchip PWMv4 driver", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "72f62472e2a56abc7474bfe9a5c90878da5e23ca", "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-3-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/2225189/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2225189/checks/", "tags": {}, "headers": { "Return-Path": "\n <linux-pwm+bounces-8649-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=cVSN9QHy;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c0a:e001:db::12fc:5321; helo=sea.lore.kernel.org;\n envelope-from=linux-pwm+bounces-8649-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=\"cVSN9QHy\"", "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 sea.lore.kernel.org (sea.lore.kernel.org\n [IPv6:2600:3c0a:e001:db::12fc:5321])\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 4fzpnZ0xN5z1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 01:10:10 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id 503EB37FE280\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 20 Apr 2026 14:42:12 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id C02E23D3494;\n\tMon, 20 Apr 2026 13:36:49 +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 2DC5B3D3305;\n\tMon, 20 Apr 2026 13:36:47 +0000 (UTC)", "by mx.zohomail.com with SMTPS id 1776692172247435.7285667818933;\n\tMon, 20 Apr 2026 06:36:12 -0700 (PDT)" ], "ARC-Seal": [ "i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1776692209; cv=pass;\n b=udE9qnOrDTZt/mtnBbS3qcxTow4mcNWW8gDbXeduDsKByCchwKxf7Ge/xIE8lC/kKpwJHrMBeom/7h6RI2c0pjsNGQ/jIfi8B9VQrQXuE+vkBHFeIv/WOHR7ZmB798GpR7FbTNkotjuVkWuE73lhRXup3xLGh+GdGszy7LrZ9CU=", "i=1; a=rsa-sha256; t=1776692174; cv=none;\n\td=zohomail.com; s=zohoarc;\n\tb=d99VOWcJzTktq2yr1f1JVn929dugXet/qcoUCx1tIiMo9LpC7xgJy9ibWmlD1HIPaDV2a14kLpMLa3KFh6DXegqdk88K4G4pIdkvmezf+p5PflyF4npKrCkKvNKnuu7sVwf99QzD0ks7ulh7u/VLFQIvw7rPN2PkutjQs4B73ZY=" ], "ARC-Message-Signature": [ "i=2; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1776692209; c=relaxed/simple;\n\tbh=2rHgoiYTnNrmBACuB/plpJJ3e3OvRFpmVwn1atGzMjA=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References:\n\t In-Reply-To:To:Cc;\n b=IddSkGas8W1QTwLQQc+55aN7bQNm7QkfLAY6RsgEauOvO7HmXQLDAysppsBNyXDAo5k4SF3WLcLR9A6wUvB85n7JibNtkFiaIRwmvY3aoe9XN3SpSG3medxF9OmGmrEeg0LfScave1twf5BmGR2G2I6ELrvF6MoJ4mqi9L+b/BA=", "i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com;\n s=zohoarc;\n\tt=1776692174;\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=978hIvIDO1JQcPsSm9qwEOC+l0xe4bOMy67Ouu23DCU=;\n\tb=Hpx/mIvCo6XVR/e9/ugaQ4s2EiZWiK9yByKj2CKcd31n1wnNpZxvkIdxE6TbDucb3LdiRE1+XGq0FOdTRYP8zq1ZVkI0BE50S0Fl8vbqm6PcSpMlYA7s2r9NH9B6dLf//ZQuSRaOHsK6HfJJSi9B0soB9ieXshjPjcrQcWsVWwQ=" ], "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=cVSN9QHy;\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=1776692174;\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=978hIvIDO1JQcPsSm9qwEOC+l0xe4bOMy67Ouu23DCU=;\n\tb=cVSN9QHy2FLNUnE1Csd/PWmjLlGgE+J+3t4vDNU6AsFSuk+muWW0VBLfhiU4phcf\n\tKPwxj59eGPhyt9BhJdzp668EVz9rXGx9KRIXtWFTPxDGk+E/GKUfjI0n3AWcFNmUzDy\n\t5Bm1pJXS/qFRfsaZca03yYOXR6JiTwg1FsE0ETSI=", "From": "Nicolas Frattaroli <nicolas.frattaroli@collabora.com>", "Date": "Mon, 20 Apr 2026 15:35:21 +0200", "Subject": "[PATCH v4 3/5] pwm: Add rockchip PWMv4 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": "7bit", "Message-Id": "<20260420-rk3576-pwm-v4-3-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": "The Rockchip RK3576 brings with it a new PWM IP, in downstream code\nreferred to as \"v4\". This new IP is different enough from the previous\nRockchip IP that I felt it necessary to add a new driver for it, instead\nof shoehorning it in the old one.\n\nAdd this new driver, based on the PWM core's waveform APIs. Its platform\ndevice is registered by the parent mfpwm driver, from which it also\nreceives a little platform data struct, so that mfpwm can guarantee that\nall the platform device drivers spread across different subsystems for\nthis specific hardware IP do not interfere with each other.\n\nSigned-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\n---\n MAINTAINERS | 2 +\n drivers/counter/Kconfig | 11 +\n drivers/counter/Makefile | 1 +\n drivers/counter/rockchip-pwm-capture.c | 307 ++++++++++++++++++++++++++\n drivers/pwm/Kconfig | 11 +\n drivers/pwm/Makefile | 1 +\n drivers/pwm/pwm-rockchip-v4.c | 383 +++++++++++++++++++++++++++++++++\n 7 files changed, 716 insertions(+)", "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex d52731242a33..3f72784dd5bc 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -23178,7 +23178,9 @@ 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/counter/rockchip-pwm-capture.c\n F:\tdrivers/mfd/rockchip-mfpwm.c\n+F:\tdrivers/pwm/pwm-rockchip-v4.c\n F:\tinclude/linux/mfd/rockchip-mfpwm.h\n \n ROCKCHIP RK3568 RANDOM NUMBER GENERATOR SUPPORT\ndiff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig\nindex d30d22dfe577..85adeb41aeed 100644\n--- a/drivers/counter/Kconfig\n+++ b/drivers/counter/Kconfig\n@@ -90,6 +90,17 @@ config MICROCHIP_TCB_CAPTURE\n \t To compile this driver as a module, choose M here: the\n \t module will be called microchip-tcb-capture.\n \n+config ROCKCHIP_PWM_CAPTURE\n+\ttristate \"Rockchip PWM Counter Capture driver\"\n+\tdepends on MFD_ROCKCHIP_MFPWM\n+\thelp\n+\t Generic counter framework driver for the multi-function PWM on\n+\t Rockchip SoCs such as the RK3576.\n+\n+\t Uses the Rockchip Multi-function PWM controller driver infrastructure\n+\t to guarantee exclusive operation with other functions of the same\n+\t device implemented by drivers in other subsystems.\n+\n config RZ_MTU3_CNT\n \ttristate \"Renesas RZ/G2L MTU3a counter driver\"\n \tdepends on RZ_MTU3\ndiff --git a/drivers/counter/Makefile b/drivers/counter/Makefile\nindex fa3c1d08f706..2bfcfc2c584b 100644\n--- a/drivers/counter/Makefile\n+++ b/drivers/counter/Makefile\n@@ -17,3 +17,4 @@ obj-$(CONFIG_FTM_QUADDEC)\t+= ftm-quaddec.o\n obj-$(CONFIG_MICROCHIP_TCB_CAPTURE)\t+= microchip-tcb-capture.o\n obj-$(CONFIG_INTEL_QEP)\t\t+= intel-qep.o\n obj-$(CONFIG_TI_ECAP_CAPTURE)\t+= ti-ecap-capture.o\n+obj-$(CONFIG_ROCKCHIP_PWM_CAPTURE)\t+= rockchip-pwm-capture.o\ndiff --git a/drivers/counter/rockchip-pwm-capture.c b/drivers/counter/rockchip-pwm-capture.c\nnew file mode 100644\nindex 000000000000..09a92f2bc409\n--- /dev/null\n+++ b/drivers/counter/rockchip-pwm-capture.c\n@@ -0,0 +1,307 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Copyright (c) 2025 Collabora Ltd.\n+ *\n+ * A counter driver for the Pulse-Width-Modulation (PWM) hardware found on\n+ * Rockchip SoCs such as the RK3576, internally referred to as \"PWM v4\". It\n+ * allows for measuring the high cycles and low cycles of a PWM signal through\n+ * the generic counter framework, while guaranteeing exclusive use over the\n+ * MFPWM device while the counter is enabled.\n+ *\n+ * Authors:\n+ * Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\n+ */\n+\n+#include <linux/cleanup.h>\n+#include <linux/counter.h>\n+#include <linux/devm-helpers.h>\n+#include <linux/interrupt.h>\n+#include <linux/mfd/rockchip-mfpwm.h>\n+#include <linux/mod_devicetable.h>\n+#include <linux/of.h>\n+#include <linux/platform_device.h>\n+#include <linux/spinlock.h>\n+\n+#define RKPWMC_INT_MASK\t\t\t(PWMV4_INT_LPC | PWMV4_INT_HPC)\n+\n+struct rockchip_pwm_capture {\n+\tstruct rockchip_mfpwm_func *pwmf;\n+\tstruct counter_device *counter;\n+};\n+\n+static struct counter_signal rkpwmc_signals[] = {\n+\t{\n+\t\t.id = 0,\n+\t\t.name = \"PWM Clock\"\n+\t},\n+};\n+\n+static const enum counter_synapse_action rkpwmc_hpc_lpc_actions[] = {\n+\tCOUNTER_SYNAPSE_ACTION_BOTH_EDGES,\n+\tCOUNTER_SYNAPSE_ACTION_NONE,\n+};\n+\n+static struct counter_synapse rkpwmc_pwm_synapses[] = {\n+\t{\n+\t\t.actions_list = rkpwmc_hpc_lpc_actions,\n+\t\t.num_actions = ARRAY_SIZE(rkpwmc_hpc_lpc_actions),\n+\t\t.signal = &rkpwmc_signals[0]\n+\t},\n+};\n+\n+static const enum counter_function rkpwmc_functions[] = {\n+\tCOUNTER_FUNCTION_INCREASE,\n+};\n+\n+static inline bool rkpwmc_is_enabled(struct rockchip_mfpwm_func *pwmf)\n+{\n+\treturn mfpwm_get_mode(pwmf) == PWMV4_MODE_CAPTURE;\n+}\n+\n+static bool rkpwmc_acquire_if_enabled(struct rockchip_pwm_capture *pc)\n+{\n+\tint ret;\n+\n+\tret = mfpwm_acquire(pc->pwmf);\n+\tif (ret < 0)\n+\t\treturn false;\n+\n+\tif (rkpwmc_is_enabled(pc->pwmf))\n+\t\treturn true;\n+\n+\tmfpwm_release(pc->pwmf);\n+\n+\treturn false;\n+}\n+\n+static int rkpwmc_enable_read(struct counter_device *counter,\n+\t\t\t struct counter_count *count,\n+\t\t\t u8 *enable)\n+{\n+\tstruct rockchip_pwm_capture *pc = counter_priv(counter);\n+\n+\t*enable = rkpwmc_is_enabled(pc->pwmf);\n+\n+\treturn 0;\n+}\n+\n+static int rkpwmc_enable_write(struct counter_device *counter,\n+\t\t\t struct counter_count *count,\n+\t\t\t u8 enable)\n+{\n+\tstruct rockchip_pwm_capture *pc = counter_priv(counter);\n+\tint ret;\n+\n+\tret = mfpwm_acquire(pc->pwmf);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (!!enable != rkpwmc_is_enabled(pc->pwmf)) {\n+\t\tif (enable) {\n+\t\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,\n+\t\t\t\t\t PWMV4_EN(false));\n+\t\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_CTRL,\n+\t\t\t\t\t PWMV4_CTRL_CAP_FLAGS);\n+\t\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INT_EN,\n+\t\t\t\t\t PWMV4_INT_LPC_W(true) |\n+\t\t\t\t\t PWMV4_INT_HPC_W(true));\n+\t\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,\n+\t\t\t\t\t PWMV4_EN(true) | PWMV4_CLK_EN(true));\n+\n+\t\t\tret = clk_enable(pc->pwmf->core);\n+\t\t\tif (ret)\n+\t\t\t\tgoto err_release;\n+\n+\t\t\tret = clk_rate_exclusive_get(pc->pwmf->core);\n+\t\t\tif (ret)\n+\t\t\t\tgoto err_disable_pwm_clk;\n+\n+\t\t\tret = mfpwm_acquire(pc->pwmf);\n+\t\t\tif (ret)\n+\t\t\t\tgoto err_unprotect_pwm_clk;\n+\t\t} else {\n+\t\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INT_EN,\n+\t\t\t\t\t PWMV4_INT_LPC_W(false) |\n+\t\t\t\t\t PWMV4_INT_HPC_W(false));\n+\t\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,\n+\t\t\t\t\t PWMV4_EN(false) | PWMV4_CLK_EN(false));\n+\t\t\tclk_rate_exclusive_put(pc->pwmf->core);\n+\t\t\tclk_disable(pc->pwmf->core);\n+\t\t\tmfpwm_release(pc->pwmf);\n+\t\t}\n+\t}\n+\n+\tmfpwm_release(pc->pwmf);\n+\n+\treturn 0;\n+\n+err_unprotect_pwm_clk:\n+\tclk_rate_exclusive_put(pc->pwmf->core);\n+err_disable_pwm_clk:\n+\tclk_disable(pc->pwmf->core);\n+err_release:\n+\tmfpwm_release(pc->pwmf);\n+\n+\treturn ret;\n+}\n+\n+static struct counter_comp rkpwmc_ext[] = {\n+\tCOUNTER_COMP_ENABLE(rkpwmc_enable_read, rkpwmc_enable_write),\n+};\n+\n+enum rkpwmc_count_id {\n+\tCOUNT_LPC = 0,\n+\tCOUNT_HPC = 1,\n+};\n+\n+static struct counter_count rkpwmc_counts[] = {\n+\t{\n+\t\t.id = COUNT_LPC,\n+\t\t.name = \"Low Polarity Capture\",\n+\t\t.functions_list = rkpwmc_functions,\n+\t\t.num_functions = ARRAY_SIZE(rkpwmc_functions),\n+\t\t.synapses = rkpwmc_pwm_synapses,\n+\t\t.num_synapses = ARRAY_SIZE(rkpwmc_pwm_synapses),\n+\t\t.ext = rkpwmc_ext,\n+\t\t.num_ext = ARRAY_SIZE(rkpwmc_ext),\n+\t},\n+\t{\n+\t\t.id = COUNT_HPC,\n+\t\t.name = \"High Polarity Capture\",\n+\t\t.functions_list = rkpwmc_functions,\n+\t\t.num_functions = ARRAY_SIZE(rkpwmc_functions),\n+\t\t.synapses = rkpwmc_pwm_synapses,\n+\t\t.num_synapses = ARRAY_SIZE(rkpwmc_pwm_synapses),\n+\t\t.ext = rkpwmc_ext,\n+\t\t.num_ext = ARRAY_SIZE(rkpwmc_ext),\n+\t},\n+};\n+\n+static int rkpwmc_count_read(struct counter_device *counter,\n+\t\t\t struct counter_count *count, u64 *value)\n+{\n+\tstruct rockchip_pwm_capture *pc = counter_priv(counter);\n+\n+\tswitch (count->id) {\n+\tcase COUNT_LPC:\n+\t\tif (rkpwmc_acquire_if_enabled(pc)) {\n+\t\t\t*value = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_LPC);\n+\t\t\tmfpwm_release(pc->pwmf);\n+\t\t} else {\n+\t\t\t*value = 0;\n+\t\t}\n+\t\treturn 0;\n+\tcase COUNT_HPC:\n+\t\tif (rkpwmc_acquire_if_enabled(pc)) {\n+\t\t\t*value = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_HPC);\n+\t\t\tmfpwm_release(pc->pwmf);\n+\t\t} else {\n+\t\t\t*value = 0;\n+\t\t}\n+\t\treturn 0;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+}\n+\n+static const struct counter_ops rkpwmc_ops = {\n+\t.count_read = rkpwmc_count_read,\n+};\n+\n+static irqreturn_t rkpwmc_irq_handler(int irq, void *data)\n+{\n+\tstruct rockchip_pwm_capture *pc = data;\n+\tu32 intsts;\n+\tu32 clr = 0;\n+\n+\tintsts = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_INTSTS);\n+\n+\tif (!(intsts & RKPWMC_INT_MASK))\n+\t\treturn IRQ_NONE;\n+\n+\tif (intsts & PWMV4_INT_LPC) {\n+\t\tclr |= PWMV4_INT_LPC;\n+\t\tcounter_push_event(pc->counter, COUNTER_EVENT_CHANGE_OF_STATE, 0);\n+\t}\n+\n+\tif (intsts & PWMV4_INT_HPC) {\n+\t\tclr |= PWMV4_INT_HPC;\n+\t\tcounter_push_event(pc->counter, COUNTER_EVENT_CHANGE_OF_STATE, 1);\n+\t}\n+\n+\tif (clr)\n+\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INTSTS, clr);\n+\n+\t/* If other interrupt status bits are set, they're not for this driver */\n+\tif (intsts != clr)\n+\t\treturn IRQ_NONE;\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static int rockchip_pwm_capture_probe(struct platform_device *pdev)\n+{\n+\tstruct rockchip_mfpwm_func *pwmf = dev_get_platdata(&pdev->dev);\n+\tstruct rockchip_pwm_capture *pc;\n+\tstruct counter_device *counter;\n+\tint ret;\n+\n+\t/* Set our (still unset) OF node to the parent MFD device's OF node */\n+\tpdev->dev.parent->of_node_reused = true;\n+\tdevice_set_node(&pdev->dev,\n+\t\t\tof_fwnode_handle(no_free_ptr(pdev->dev.parent->of_node)));\n+\n+\tcounter = devm_counter_alloc(&pdev->dev, sizeof(*pc));\n+\tif (IS_ERR(counter))\n+\t\treturn PTR_ERR(counter);\n+\n+\tpc = counter_priv(counter);\n+\tpc->pwmf = pwmf;\n+\n+\tplatform_set_drvdata(pdev, pc);\n+\n+\t/* If the counter is on at module probe, acquire it */\n+\trkpwmc_acquire_if_enabled(pc);\n+\n+\tcounter->name = pdev->name;\n+\tcounter->signals = rkpwmc_signals;\n+\tcounter->num_signals = ARRAY_SIZE(rkpwmc_signals);\n+\tcounter->ops = &rkpwmc_ops;\n+\tcounter->counts = rkpwmc_counts;\n+\tcounter->num_counts = ARRAY_SIZE(rkpwmc_counts);\n+\n+\tpc->counter = counter;\n+\n+\tret = devm_counter_add(&pdev->dev, counter);\n+\tif (ret < 0)\n+\t\treturn dev_err_probe(&pdev->dev, ret, \"Failed to add counter\\n\");\n+\n+\tret = devm_request_irq(&pdev->dev, pwmf->irq, rkpwmc_irq_handler,\n+\t\t\t IRQF_SHARED, pdev->name, pc);\n+\tif (ret)\n+\t\treturn dev_err_probe(&pdev->dev, ret, \"Failed requesting IRQ\\n\");\n+\n+\treturn 0;\n+}\n+\n+static const struct platform_device_id rockchip_pwm_capture_id_table[] = {\n+\t{ .name = \"rockchip-pwm-capture\", },\n+\t{ /* sentinel */ },\n+};\n+MODULE_DEVICE_TABLE(platform, rockchip_pwm_capture_id_table);\n+\n+static struct platform_driver rockchip_pwm_capture_driver = {\n+\t.probe = rockchip_pwm_capture_probe,\n+\t.id_table = rockchip_pwm_capture_id_table,\n+\t.driver = {\n+\t\t.name = \"rockchip-pwm-capture\",\n+\t},\n+};\n+module_platform_driver(rockchip_pwm_capture_driver);\n+\n+MODULE_AUTHOR(\"Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\");\n+MODULE_DESCRIPTION(\"Rockchip PWM Counter Capture Driver\");\n+MODULE_LICENSE(\"GPL\");\n+MODULE_IMPORT_NS(\"ROCKCHIP_MFPWM\");\n+MODULE_IMPORT_NS(\"COUNTER\");\n+MODULE_ALIAS(\"platform:rockchip-pwm-capture\");\ndiff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig\nindex 6f3147518376..3fe7993bf12b 100644\n--- a/drivers/pwm/Kconfig\n+++ b/drivers/pwm/Kconfig\n@@ -625,6 +625,17 @@ config PWM_ROCKCHIP\n \t Generic PWM framework driver for the PWM controller found on\n \t Rockchip SoCs.\n \n+config PWM_ROCKCHIP_V4\n+\ttristate \"Rockchip PWM v4 support\"\n+\tdepends on MFD_ROCKCHIP_MFPWM\n+\thelp\n+\t Generic PWM framework driver for the PWM controller found on\n+\t later Rockchip SoCs such as the RK3576.\n+\n+\t Uses the Rockchip Multi-function PWM controller driver infrastructure\n+\t to guarantee fearlessly concurrent operation with other functions of\n+\t the same device implemented by drivers in other subsystems.\n+\n config PWM_SAMSUNG\n \ttristate \"Samsung PWM support\"\n \tdepends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST\ndiff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile\nindex 0dc0d2b69025..a234027dbbc6 100644\n--- a/drivers/pwm/Makefile\n+++ b/drivers/pwm/Makefile\n@@ -56,6 +56,7 @@ obj-$(CONFIG_PWM_RENESAS_RZG2L_GPT)\t+= pwm-rzg2l-gpt.o\n obj-$(CONFIG_PWM_RENESAS_RZ_MTU3)\t+= pwm-rz-mtu3.o\n obj-$(CONFIG_PWM_RENESAS_TPU)\t+= pwm-renesas-tpu.o\n obj-$(CONFIG_PWM_ROCKCHIP)\t+= pwm-rockchip.o\n+obj-$(CONFIG_PWM_ROCKCHIP_V4)\t+= pwm-rockchip-v4.o\n obj-$(CONFIG_PWM_SAMSUNG)\t+= pwm-samsung.o\n obj-$(CONFIG_PWM_SIFIVE)\t+= pwm-sifive.o\n obj-$(CONFIG_PWM_SL28CPLD)\t+= pwm-sl28cpld.o\ndiff --git a/drivers/pwm/pwm-rockchip-v4.c b/drivers/pwm/pwm-rockchip-v4.c\nnew file mode 100644\nindex 000000000000..b7de72c433c5\n--- /dev/null\n+++ b/drivers/pwm/pwm-rockchip-v4.c\n@@ -0,0 +1,383 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Copyright (c) 2025 Collabora Ltd.\n+ *\n+ * A Pulse-Width-Modulation (PWM) generator driver for the generators found in\n+ * Rockchip SoCs such as the RK3576, internally referred to as \"PWM v4\". Uses\n+ * the MFPWM infrastructure to guarantee exclusive use over the device without\n+ * other functions of the device from different drivers interfering with its\n+ * operation while it's active.\n+ *\n+ * Technical Reference Manual: Chapter 31 of the RK3506 TRM Part 1, a SoC which\n+ * uses the same PWM hardware and has a publicly available TRM.\n+ * https://opensource.rock-chips.com/images/3/36/Rockchip_RK3506_TRM_Part_1_V1.2-20250811.pdf\n+ *\n+ * Authors:\n+ * Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\n+ *\n+ * Limitations:\n+ * - The hardware supports both completing the currently running period\n+ * on disable (by switching to oneshot mode with a single repetition and\n+ * only disable when the complete irq fires), and abrupt disable (freeze).\n+ * Only the latter is implemented in the driver.\n+ * - When the output is disabled, the pin will remain driven to whatever state\n+ * it last had.\n+ * - Adjustments to the duty cycle will only take effect during the next period.\n+ * - Adjustments to the period length will only take effect during the next\n+ * period.\n+ * - The hardware only supports offsets in [0, period - duty_cycle]\n+ */\n+\n+#include <linux/math64.h>\n+#include <linux/mfd/rockchip-mfpwm.h>\n+#include <linux/platform_device.h>\n+#include <linux/pwm.h>\n+\n+struct rockchip_pwm_v4 {\n+\tstruct rockchip_mfpwm_func *pwmf;\n+\tstruct pwm_chip chip;\n+};\n+\n+struct __packed rockchip_pwm_v4_wf {\n+\tu32 period;\n+\tu32 duty;\n+\tu32 offset;\n+\tunsigned long rate;\n+};\n+\n+static inline struct rockchip_pwm_v4 *to_rockchip_pwm_v4(struct pwm_chip *chip)\n+{\n+\treturn pwmchip_get_drvdata(chip);\n+}\n+\n+/**\n+ * rockchip_pwm_v4_round_single - convert a PWM parameter to hardware\n+ * @rate: clock rate of the PWM clock, as per clk_get_rate\n+ * Assumed to be <= 1GHz for overflow considerations\n+ * @in_val: parameter in nanoseconds to convert\n+ *\n+ * Returns the rounded value, saturating at U32_MAX if too large\n+ */\n+static u32 rockchip_pwm_v4_round_single(unsigned long rate, u64 in_val)\n+{\n+\tu64 tmp;\n+\n+\ttmp = mul_u64_u64_div_u64(rate, in_val, NSEC_PER_SEC);\n+\tif (tmp > U32_MAX)\n+\t\ttmp = U32_MAX;\n+\n+\treturn tmp;\n+}\n+\n+/**\n+ * rockchip_pwm_v4_round_params - convert PWM parameters to hardware\n+ * @rate: PWM clock rate to do the calculations at\n+ * @wf: pointer to the generic &struct pwm_waveform input parameters\n+ * @wfhw: pointer to the hardware-specific &struct rockchip_pwm_v4_wf output\n+ * parameters that the results will be stored in\n+ *\n+ * Convert nanosecond-based duty/period/offset parameters to the PWM hardware's\n+ * native rounded representation in number of cycles at clock rate @rate. Should\n+ * any of the input parameters be out of range for the hardware, the\n+ * corresponding output parameter is the maximum permissible value for said\n+ * parameter with considerations to the others.\n+ */\n+static void rockchip_pwm_v4_round_params(unsigned long rate,\n+\t\t\t\t\t const struct pwm_waveform *wf,\n+\t\t\t\t\t struct rockchip_pwm_v4_wf *wfhw)\n+{\n+\twfhw->period = rockchip_pwm_v4_round_single(rate, wf->period_length_ns);\n+\n+\twfhw->duty = rockchip_pwm_v4_round_single(rate, wf->duty_length_ns);\n+\n+\t/* As per TRM, PWM_OFFSET: \"The value ranges from 0 to (period-duty)\" */\n+\twfhw->offset = rockchip_pwm_v4_round_single(rate, wf->duty_offset_ns);\n+\tif (!wfhw->period) /* Don't underflow when pwm disabled */\n+\t\twfhw->offset = 0;\n+\telse if (wfhw->offset > wfhw->period - wfhw->duty)\n+\t\twfhw->offset = wfhw->period - wfhw->duty;\n+}\n+\n+static int rockchip_pwm_v4_round_wf_tohw(struct pwm_chip *chip,\n+\t\t\t\t\t struct pwm_device *pwm,\n+\t\t\t\t\t const struct pwm_waveform *wf,\n+\t\t\t\t\t void *_wfhw)\n+{\n+\tstruct rockchip_pwm_v4 *pc = to_rockchip_pwm_v4(chip);\n+\tstruct rockchip_pwm_v4_wf *wfhw = _wfhw;\n+\tunsigned long rate;\n+\n+\trate = clk_get_rate(pc->pwmf->core);\n+\n+\t/*\n+\t * It's unlikely this code path is ever taken, as current hardware does\n+\t * not expose a clock that comes anywhere close to 1GHz. However, in\n+\t * order to avoid even a theoretical overflow in parameter rounding,\n+\t * error out if this ever happens to be the case.\n+\t */\n+\tif (rate > NSEC_PER_SEC)\n+\t\treturn -ERANGE;\n+\n+\trockchip_pwm_v4_round_params(rate, wf, wfhw);\n+\n+\tif (wf->period_length_ns > 0)\n+\t\twfhw->rate = rate;\n+\telse\n+\t\twfhw->rate = 0;\n+\n+\tdev_dbg(&chip->dev,\n+\t\t\"tohw: pwm#%u: %lld/%lld [+%lld] @%lu -> DUTY: %08x, PERIOD: %08x, OFFSET: %08x\\n\",\n+\t\tpwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,\n+\t\trate, wfhw->duty, wfhw->period, wfhw->offset);\n+\n+\treturn 0;\n+}\n+\n+static int rockchip_pwm_v4_round_wf_fromhw(struct pwm_chip *chip,\n+\t\t\t\t\t struct pwm_device *pwm,\n+\t\t\t\t\t const void *_wfhw,\n+\t\t\t\t\t struct pwm_waveform *wf)\n+{\n+\tconst struct rockchip_pwm_v4_wf *wfhw = _wfhw;\n+\tunsigned long rate = wfhw->rate;\n+\n+\tif (rate) {\n+\t\twf->period_length_ns = DIV_ROUND_UP((u64)wfhw->period * NSEC_PER_SEC, rate);\n+\t\twf->duty_length_ns = DIV_ROUND_UP((u64)wfhw->duty * NSEC_PER_SEC, rate);\n+\t\twf->duty_offset_ns = DIV_ROUND_UP((u64)wfhw->offset * NSEC_PER_SEC, rate);\n+\t} else {\n+\t\twf->period_length_ns = 0;\n+\t\twf->duty_length_ns = 0;\n+\t\twf->duty_offset_ns = 0;\n+\t}\n+\n+\tdev_dbg(&chip->dev,\n+\t\t\"fromhw: pwm#%u: DUTY: %08x, PERIOD: %08x, OFFSET: %08x @%lu -> %lld/%lld [+%lld]\\n\",\n+\t\tpwm->hwpwm, wfhw->duty, wfhw->period, wfhw->offset, rate,\n+\t\twf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns);\n+\n+\treturn 0;\n+}\n+\n+static int rockchip_pwm_v4_read_wf(struct pwm_chip *chip, struct pwm_device *pwm,\n+\t\t\t\t void *_wfhw)\n+{\n+\tstruct rockchip_pwm_v4 *pc = to_rockchip_pwm_v4(chip);\n+\tstruct rockchip_pwm_v4_wf *wfhw = _wfhw;\n+\tunsigned long rate;\n+\tint ret;\n+\n+\tret = mfpwm_acquire(pc->pwmf);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\trate = clk_get_rate(pc->pwmf->core);\n+\n+\twfhw->period = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_PERIOD);\n+\twfhw->duty = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_DUTY);\n+\twfhw->offset = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_OFFSET);\n+\tif (rockchip_pwm_v4_is_enabled(mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_ENABLE)))\n+\t\twfhw->rate = rate;\n+\telse\n+\t\twfhw->rate = 0;\n+\n+\tmfpwm_release(pc->pwmf);\n+\n+\treturn 0;\n+}\n+\n+static int rockchip_pwm_v4_write_wf(struct pwm_chip *chip, struct pwm_device *pwm,\n+\t\t\t\t const void *_wfhw)\n+{\n+\tstruct rockchip_pwm_v4 *pc = to_rockchip_pwm_v4(chip);\n+\tconst struct rockchip_pwm_v4_wf *wfhw = _wfhw;\n+\tbool was_enabled;\n+\tint ret;\n+\n+\tret = mfpwm_acquire(pc->pwmf);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\twas_enabled = rockchip_pwm_v4_is_enabled(mfpwm_reg_read(pc->pwmf->base,\n+\t\t\t\t\t\t\t\tPWMV4_REG_ENABLE));\n+\n+\t/*\n+\t * \"But Nicolas\", you ask with valid concerns, \"why would you enable the\n+\t * PWM before setting all the parameter registers?\"\n+\t *\n+\t * Excellent question, Mr. Reader M. Strawman! The RK3576 TRM Part 1\n+\t * Section 34.6.3 specifies that this is the intended order of writes.\n+\t * Doing the PWM_EN and PWM_CLK_EN writes after the params but before\n+\t * the CTRL_UPDATE_EN, or even after the CTRL_UPDATE_EN, results in\n+\t * erratic behaviour where repeated turning on and off of the PWM may\n+\t * not turn it off under all circumstances. This is also why we don't\n+\t * use relaxed writes; it's not worth the footgun.\n+\t */\n+\tif (wfhw->rate)\n+\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,\n+\t\t\t\tFIELD_PREP_WM16(PWMV4_EN_BOTH_MASK,\n+\t\t\t\t\t\tPWMV4_EN_BOTH_MASK));\n+\telse\n+\t\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,\n+\t\t\t\tFIELD_PREP_WM16(PWMV4_EN_BOTH_MASK, 0));\n+\n+\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_PERIOD, wfhw->period);\n+\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_DUTY, wfhw->duty);\n+\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_OFFSET, wfhw->offset);\n+\n+\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_CTRL, PWMV4_CTRL_CONT_FLAGS);\n+\n+\t/* Commit new configuration to hardware output. */\n+\tmfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,\n+\t\t\tPWMV4_CTRL_UPDATE_EN);\n+\n+\tif (wfhw->rate) {\n+\t\tif (!was_enabled) {\n+\t\t\tdev_dbg(&chip->dev, \"Enabling PWM output\\n\");\n+\t\t\tret = clk_enable(pc->pwmf->core);\n+\t\t\tif (ret)\n+\t\t\t\tgoto err_mfpwm_release;\n+\t\t\tret = clk_set_rate_exclusive(pc->pwmf->core, wfhw->rate);\n+\t\t\tif (ret) {\n+\t\t\t\tclk_disable(pc->pwmf->core);\n+\t\t\t\tgoto err_mfpwm_release;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * Output should be on now, acquire device to guarantee\n+\t\t\t * exclusion with other device functions while it's on.\n+\t\t\t *\n+\t\t\t * It's highly unlikely that this fails, as mfpwm has\n+\t\t\t * already been acquired before, and this is just a\n+\t\t\t * usage counter increase. Not worth the added\n+\t\t\t * complexity of clearing the PWMV4_REG_ENABLE again,\n+\t\t\t * especially considering the CTRL_UPDATE_EN behaviour.\n+\t\t\t */\n+\t\t\tret = mfpwm_acquire(pc->pwmf);\n+\t\t\tif (ret) {\n+\t\t\t\tclk_rate_exclusive_put(pc->pwmf->core);\n+\t\t\t\tclk_disable(pc->pwmf->core);\n+\t\t\t\tgoto err_mfpwm_release;\n+\t\t\t}\n+\t\t}\n+\t} else if (was_enabled) {\n+\t\tdev_dbg(&chip->dev, \"Disabling PWM output\\n\");\n+\t\tclk_rate_exclusive_put(pc->pwmf->core);\n+\t\tclk_disable(pc->pwmf->core);\n+\t\t/* Output is off now, extra release to balance extra acquire */\n+\t\tmfpwm_release(pc->pwmf);\n+\t}\n+\n+err_mfpwm_release:\n+\tmfpwm_release(pc->pwmf);\n+\n+\treturn ret;\n+}\n+\n+static const struct pwm_ops rockchip_pwm_v4_ops = {\n+\t.sizeof_wfhw = sizeof(struct rockchip_pwm_v4_wf),\n+\t.round_waveform_tohw = rockchip_pwm_v4_round_wf_tohw,\n+\t.round_waveform_fromhw = rockchip_pwm_v4_round_wf_fromhw,\n+\t.read_waveform = rockchip_pwm_v4_read_wf,\n+\t.write_waveform = rockchip_pwm_v4_write_wf,\n+};\n+\n+static bool rockchip_pwm_v4_on_and_continuous(struct rockchip_pwm_v4 *pc)\n+{\n+\tbool en;\n+\tu32 val;\n+\n+\ten = rockchip_pwm_v4_is_enabled(mfpwm_reg_read(pc->pwmf->base,\n+\t\t\t\t\t\t PWMV4_REG_ENABLE));\n+\tval = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_CTRL);\n+\n+\treturn en && ((val & PWMV4_MODE_MASK) == PWMV4_MODE_CONT);\n+}\n+\n+static int rockchip_pwm_v4_probe(struct platform_device *pdev)\n+{\n+\tstruct rockchip_mfpwm_func *pwmf = dev_get_platdata(&pdev->dev);\n+\tstruct rockchip_pwm_v4 *pc;\n+\tstruct pwm_chip *chip;\n+\tstruct device *dev = &pdev->dev;\n+\tint ret;\n+\n+\t/*\n+\t * For referencing the PWM in the DT to work, we need the parent MFD\n+\t * device's OF node.\n+\t */\n+\tdev->of_node_reused = true;\n+\tdevice_set_node(dev, of_fwnode_handle(dev->parent->of_node));\n+\n+\tchip = devm_pwmchip_alloc(dev, 1, sizeof(*pc));\n+\tif (IS_ERR(chip))\n+\t\treturn PTR_ERR(chip);\n+\n+\tpc = to_rockchip_pwm_v4(chip);\n+\tpc->pwmf = pwmf;\n+\n+\tret = mfpwm_acquire(pwmf);\n+\tif (ret)\n+\t\treturn dev_err_probe(dev, ret, \"Couldn't acquire mfpwm in probe\\n\");\n+\n+\tif (!rockchip_pwm_v4_on_and_continuous(pc))\n+\t\tmfpwm_release(pwmf);\n+\telse {\n+\t\tdev_dbg(dev, \"PWM was already on at probe time\\n\");\n+\t\tret = clk_enable(pwmf->core);\n+\t\tif (ret) {\n+\t\t\tdev_err_probe(dev, ret, \"Enabling pwm clock failed\\n\");\n+\t\t\tgoto err_mfpwm_release;\n+\t\t}\n+\t\tret = clk_rate_exclusive_get(pc->pwmf->core);\n+\t\tif (ret) {\n+\t\t\tdev_err_probe(dev, ret, \"Protecting pwm clock failed\\n\");\n+\t\t\tgoto err_clk_disable;\n+\t\t}\n+\t}\n+\n+\tplatform_set_drvdata(pdev, chip);\n+\n+\tchip->ops = &rockchip_pwm_v4_ops;\n+\n+\tret = devm_pwmchip_add(dev, chip);\n+\tif (ret) {\n+\t\tdev_err_probe(dev, ret, \"Failed to add PWM chip\\n\");\n+\t\tif (rockchip_pwm_v4_on_and_continuous(pc))\n+\t\t\tgoto err_rate_put;\n+\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+\n+err_rate_put:\n+\tclk_rate_exclusive_put(pwmf->core);\n+err_clk_disable:\n+\tclk_disable(pwmf->core);\n+err_mfpwm_release:\n+\tmfpwm_release(pwmf);\n+\n+\treturn ret;\n+}\n+\n+static const struct platform_device_id rockchip_pwm_v4_ids[] = {\n+\t{ .name = \"rockchip-pwm-v4\", },\n+\t{ /* sentinel */ }\n+};\n+MODULE_DEVICE_TABLE(platform, rockchip_pwm_v4_ids);\n+\n+static struct platform_driver rockchip_pwm_v4_driver = {\n+\t.probe = rockchip_pwm_v4_probe,\n+\t.driver = {\n+\t\t.name = \"rockchip-pwm-v4\",\n+\t},\n+\t.id_table = rockchip_pwm_v4_ids,\n+};\n+module_platform_driver(rockchip_pwm_v4_driver);\n+\n+MODULE_AUTHOR(\"Nicolas Frattaroli <nicolas.frattaroli@collabora.com>\");\n+MODULE_DESCRIPTION(\"Rockchip PWMv4 Driver\");\n+MODULE_LICENSE(\"GPL\");\n+MODULE_IMPORT_NS(\"ROCKCHIP_MFPWM\");\n+MODULE_ALIAS(\"platform:pwm-rockchip-v4\");\n", "prefixes": [ "v4", "3/5" ] }