From nobody Fri Jun 26 16:21:54 2026 Received: from azure-sdnproxy.icoremail.net (azure-sdnproxy.icoremail.net [52.175.55.52]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1B36E3C4554; Tue, 23 Jun 2026 07:14:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=52.175.55.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782198878; cv=none; b=lh3Ujsj5aLxW2yBUPYll7sZA15Y/BRuxBN3QeHg+LrENDXgBmH/tKszFhLmR4TCDtrQrVPNYHn8nU+yQXzxXYLc3c3JbrfoOaz1LFaRo8I/MuFmNcmI+BJ5B2DLieFXYmbfJJMzGUgM5LVSi32KXOSwdGy6yY4nK6PUrcObP2bY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782198878; c=relaxed/simple; bh=wJjtLJIfjiBKML90sObcewLCRTHHWeMmI5DG8JStaJY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=q0PCM4LrS/2OZEX44G3BKO1ReMgVYVXcDmJH1njS5neAoY7wocj6t8mRtZ8og2O71bh8whO9QjBRxbEkIS11DK/veDB1xexPG/nrrkJEePX/wThILFWAjl/obQvKL8HII1gGBn9yjfX2E/fkBa8CIvVzTjbxJIdHWLhJNhgdsDs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com; spf=pass smtp.mailfrom=eswincomputing.com; arc=none smtp.client-ip=52.175.55.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=eswincomputing.com Received: from E0005152DT.eswin.cn (unknown [10.12.96.41]) by app2 (Coremail) with SMTP id TQJkCgD3DKBOMjpqT3otAA--.26036S2; Tue, 23 Jun 2026 15:14:23 +0800 (CST) From: dongxuyang@eswincomputing.com To: ukleinek@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, ben-linux@fluff.org, ben.dooks@codethink.co.uk, p.zabel@pengutronix.de, linux-pwm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: ningyu@eswincomputing.com, linmin@eswincomputing.com, xuxiang@eswincomputing.com, wangguosheng@eswincomputing.com, pinkesh.vaghela@einfochips.com, Xuyang Dong Subject: [PATCH v8 1/3] dt-bindings: pwm: dwc: Document optional resets property Date: Tue, 23 Jun 2026 15:14:16 +0800 Message-Id: <20260623071416.2092-1-dongxuyang@eswincomputing.com> X-Mailer: git-send-email 2.31.1.windows.1 In-Reply-To: <20260623071329.2034-1-dongxuyang@eswincomputing.com> References: <20260623071329.2034-1-dongxuyang@eswincomputing.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: TQJkCgD3DKBOMjpqT3otAA--.26036S2 X-Coremail-Antispam: 1UD129KBjvJXoW7Ww4DAry8GFy7Cw1kKFyfWFg_yoW8Gry7pa yxur92qryfXF13Wws5XF1kCr13XFn0yr43Kr1UXr42ywsrta1jqFWakw15JFWUArWIvrWa gFZ3uw13ZFyjyr7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUBv14x267AKxVW8JVW5JwAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK02 1l84ACjcxK6xIIjxv20xvE14v26w1j6s0DM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4U JVWxJr1l84ACjcxK6I8E87Iv67AKxVW0oVCq3wA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_Gc CE3s1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E 2Ix0cI8IcVAFwI0_JrI_JrylYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJV W8JwACjcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lFIxGxcIEc7CjxVA2 Y2ka0xkIwI1lw4CEc2x0rVAKj4xxMxkF7I0En4kS14v26r1q6r43MxkIecxEwVCm-wCF04 k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18 MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr4 1lIxAIcVC0I7IYx2IY67AKxVWUJVWUCwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1l IxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4 A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x0JUXJ5wUUUUU= X-CM-SenderInfo: pgrqw5xx1d0w46hv4xpqfrz1xxwl0woofrz/ Content-Type: text/plain; charset="utf-8" From: Xuyang Dong The DesignWare PWM IP has two active-low reset inputs: presetn resets the register interface logic in the pclk (bus) domain, and timer_N_resetn resets the counter/timer logic in the timer_N_clk domain. The existing snps,dw-apb-timers-pwm2 binding does not describe either of these lines. Add the resets property and describe the function of each reset to support future use of resets. Signed-off-by: Xuyang Dong Acked-by: Conor Dooley --- .../devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.= yaml b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml index 7523a89a1773..213fdaef25d9 100644 --- a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml +++ b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml @@ -43,6 +43,11 @@ properties: - const: bus - const: timer =20 + resets: + items: + - description: Interface bus (presetn) reset + - description: PWM timer logic (timer_N_resetn) reset + snps,pwm-number: $ref: /schemas/types.yaml#/definitions/uint32 description: The number of PWM channels configured for this instance --=20 2.34.1 From nobody Fri Jun 26 16:21:54 2026 Received: from zg8tmtyylji0my4xnjqumte4.icoremail.net (zg8tmtyylji0my4xnjqumte4.icoremail.net [162.243.164.118]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 42A9C340286; Tue, 23 Jun 2026 07:14:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.243.164.118 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782198902; cv=none; b=ccTQArLsT+SIFdTTfgL2gfCTRhnVlTbO7Jz4jVM5K9jaEYMDVk9nawBZyIzlkAG8LB1V9Mr2Tn7PS9ZKhWkkcpW3DpzEjI1wVBZi/rPPqP+TLuQFlKZjZDlJg9THL/EWVmXhHk52/2MpxQ7yU/TRrSfkVu5Lnq1RcwOoZ8DafUc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782198902; c=relaxed/simple; bh=BG7aJT75SeSudrAVtENnb67kpwtBrgsQnPh0cT54ldg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=d8KPyNbJ1mKXjLWpMhVIdRGIW6CEmD7vvFial8cZwsVF9OceD4/BJnvfyLZaXf+eG6SKmDYgWjhrEcUnJtTwJtcw79q+XETMBRxdOy5glFzpv3teHMESp1rz9kxrZxB3Xk5BLAxkxzVUQ+uLEyz/TaoDCX1qJsf5bLg8laIG0Zc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com; spf=pass smtp.mailfrom=eswincomputing.com; arc=none smtp.client-ip=162.243.164.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=eswincomputing.com Received: from E0005152DT.eswin.cn (unknown [10.12.96.41]) by app1 (Coremail) with SMTP id TAJkCgA3y29gMjpqAoctAA--.48226S2; Tue, 23 Jun 2026 15:14:41 +0800 (CST) From: dongxuyang@eswincomputing.com To: ukleinek@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, ben-linux@fluff.org, ben.dooks@codethink.co.uk, p.zabel@pengutronix.de, linux-pwm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: ningyu@eswincomputing.com, linmin@eswincomputing.com, xuxiang@eswincomputing.com, wangguosheng@eswincomputing.com, pinkesh.vaghela@einfochips.com, Xuyang Dong Subject: [PATCH v8 2/3] dt-bindings: pwm: dwc: Add eswin compatible Date: Tue, 23 Jun 2026 15:14:38 +0800 Message-Id: <20260623071438.2147-1-dongxuyang@eswincomputing.com> X-Mailer: git-send-email 2.31.1.windows.1 In-Reply-To: <20260623071329.2034-1-dongxuyang@eswincomputing.com> References: <20260623071329.2034-1-dongxuyang@eswincomputing.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: TAJkCgA3y29gMjpqAoctAA--.48226S2 X-Coremail-Antispam: 1UD129KBjvJXoW7Ww43uw4fXrW5XF1xJr1kKrg_yoW8uF48pF 4furWvqw1fXr17Zws5XF10k3W3Xas5JFW3Kr1xtFWUCa90qF4jqFW3Kr15AFWUZr4xurW3 Wa93ur15A3Wj9r7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUBv14x267AKxVW8JVW5JwAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK02 1l84ACjcxK6xIIjxv20xvE14v26w1j6s0DM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4U JVWxJr1l84ACjcxK6I8E87Iv67AKxVW0oVCq3wA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_Gc CE3s1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E 2Ix0cI8IcVAFwI0_Jrv_JF1lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJV W8JwACjcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lFIxGxcIEc7CjxVA2 Y2ka0xkIwI1lw4CEc2x0rVAKj4xxMxkF7I0En4kS14v26r1q6r43MxkIecxEwVCm-wCF04 k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18 MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr4 1lIxAIcVC0I7IYx2IY67AKxVWUJVWUCwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1l IxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4 A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x0JUHCJQUUUUU= X-CM-SenderInfo: pgrqw5xx1d0w46hv4xpqfrz1xxwl0woofrz/ Content-Type: text/plain; charset="utf-8" From: Xuyang Dong EIC7700 integrates the DesignWare PWM IP described by the generic snps,dw-apb-timers-pwm2 binding. On this SoC, the presetn and timer_N_resetn inputs are physically tied together to a single reset line, so exactly one reset is both required and sufficient, unlike the generic binding where up to two independent lines are optional. Add the eswin,eic7700-pwm compatible string and constrain its resets property to exactly one entry. Signed-off-by: Xuyang Dong Acked-by: Conor Dooley --- .../bindings/pwm/snps,dw-apb-timers-pwm2.yaml | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.= yaml b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml index 213fdaef25d9..9a9a24ddc61b 100644 --- a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml +++ b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml @@ -20,12 +20,11 @@ description: instead of having to encode the IP version number in the device tree compatible. =20 -allOf: - - $ref: pwm.yaml# - properties: compatible: - const: snps,dw-apb-timers-pwm2 + enum: + - snps,dw-apb-timers-pwm2 + - eswin,eic7700-pwm =20 reg: maxItems: 1 @@ -44,6 +43,7 @@ properties: - const: timer =20 resets: + minItems: 1 items: - description: Interface bus (presetn) reset - description: PWM timer logic (timer_N_resetn) reset @@ -59,6 +59,21 @@ required: - clocks - clock-names =20 +allOf: + - $ref: pwm.yaml# + + - if: + properties: + compatible: + contains: + const: eswin,eic7700-pwm + then: + properties: + resets: + maxItems: 1 + required: + - resets + additionalProperties: false =20 examples: @@ -70,3 +85,12 @@ examples: clocks =3D <&bus>, <&timer>; clock-names =3D "bus", "timer"; }; + - | + pwm@50818000 { + compatible =3D "eswin,eic7700-pwm"; + reg =3D <0x50818000 0x4000>; + #pwm-cells =3D <3>; + clocks =3D <&bus>, <&timer>; + clock-names =3D "bus", "timer"; + resets =3D <&reset>; + }; --=20 2.34.1 From nobody Fri Jun 26 16:21:54 2026 Received: from azure-sdnproxy.icoremail.net (azure-sdnproxy.icoremail.net [207.46.229.174]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C114225B0BD; Tue, 23 Jun 2026 07:15:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=207.46.229.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782198931; cv=none; b=daMWTT2J7cGGkLqjKkk1S0lj7UQvsxmoTYcJVoeX4l74amBcbFpSyw6khE5bbD6BUMCotoLJ9xF65n0nzrkGKgBBgLaLVHSPCLMh2nFBFsnWbxo/RKEuagYYo6T8zMoGwx/ZKvwiPQyqVG4Zp7+91zaN8zj5bLjZyLMJHDRLYfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782198931; c=relaxed/simple; bh=G2q5aoQV6D5YHwmShuDNGpcyUJv5lDnehiqg4L/7RL4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HJIvmGIjLaJUorvEjQnnOP70LTQrD0ozWCS9HcAZetA8961yBpCin4PpAVyORmvosEKIsM/0e7HvDtha8kMlyKs1zi6oU5vG02xCsrnLOzetRZxPJ8n+WpmiPLCTh01dy0sabEMoWz5hj4C2gjPMmk6JfwcMMMN9aWmG5qaLhls= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com; spf=pass smtp.mailfrom=eswincomputing.com; arc=none smtp.client-ip=207.46.229.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=eswincomputing.com Received: from E0005152DT.eswin.cn (unknown [10.12.96.41]) by app1 (Coremail) with SMTP id TAJkCgA32XKIMjpqG4ctAA--.22627S2; Tue, 23 Jun 2026 15:15:21 +0800 (CST) From: dongxuyang@eswincomputing.com To: ukleinek@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, ben-linux@fluff.org, ben.dooks@codethink.co.uk, p.zabel@pengutronix.de, linux-pwm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: ningyu@eswincomputing.com, linmin@eswincomputing.com, xuxiang@eswincomputing.com, wangguosheng@eswincomputing.com, pinkesh.vaghela@einfochips.com, Xuyang Dong Subject: [PATCH v8 3/3] pwm: dwc: add of/platform support Date: Tue, 23 Jun 2026 15:15:16 +0800 Message-Id: <20260623071516.2251-1-dongxuyang@eswincomputing.com> X-Mailer: git-send-email 2.31.1.windows.1 In-Reply-To: <20260623071329.2034-1-dongxuyang@eswincomputing.com> References: <20260623071329.2034-1-dongxuyang@eswincomputing.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: TAJkCgA32XKIMjpqG4ctAA--.22627S2 X-Coremail-Antispam: 1UD129KBjvAXoW3ur1rKryfJrW8Zw1DGF43GFg_yoW8AF1xuo WSkr1fXw1rK3Z5J397Ca42kayjvw4kt34fur1rWF4DCFn8Zw45AayUK3yY934Iqw1YyFWx Ar4xXr1fAF4fJw18n29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UjIYCTnIWjp_UUUYN7AC8VAFwI0_Gr0_Xr1l1xkIjI8I6I8E6xAIw20EY4v20xva j40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l8cAvFVAK0II2c7xJM28CjxkF64kEwVA0rcxSw2 x7M28EF7xvwVC0I7IYx2IY67AKxVWDJVCq3wA2z4x0Y4vE2Ix0cI8IcVCY1x0267AKxVW8 Jr0_Cr1UM28EF7xvwVC2z280aVAFwI0_GcCE3s1l84ACjcxK6I8E87Iv6xkF7I0E14v26r xl6s0DM2AIxVAIcxkEcVAq07x20xvEncxIr21l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj 6xIIjxv20xvE14v26r126r1DMcIj6I8E87Iv67AKxVWUJVW8JwAm72CE4IkC6x0Yz7v_Jr 0_Gr1lF7xvr2IYc2Ij64vIr41lF7I21c0EjII2zVCS5cI20VAGYxC7M4IIrI8v6xkF7I0E 8cxan2IY04v7M4kE6xkIj40Ew7xC0wCY1x0262kKe7AKxVWUtVW8ZwCY02Avz4vE-syl42 xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWU GwC20s026x8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r4a6rW5MIIYrxkI7VAKI4 8JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY6xkF7I0E14v26F4j6r4U JwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Jr0_Gr1lIxAIcV C2z280aVCY1x0267AKxVW8JVW8JrUvcSsGvfC2KfnxnUUI43ZEXa7VUbQJ57UUUUU== X-CM-SenderInfo: pgrqw5xx1d0w46hv4xpqfrz1xxwl0woofrz/ Content-Type: text/plain; charset="utf-8" From: Xuyang Dong The dwc pwm controller can be used in non-PCI systems, so allow either platform or OF based probing. The controller is reset only when no PWM channel is enabled. Otherwise, clocks are enabled and the runtime PM state is updated to reflect the active hardware configuration. Co-developed-by: Ben Dooks Signed-off-by: Ben Dooks Signed-off-by: Xiang Xu Signed-off-by: Guosheng Wang Signed-off-by: Xuyang Dong --- drivers/pwm/Kconfig | 10 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-dwc-core.c | 103 ++++++++--- drivers/pwm/pwm-dwc-of.c | 346 +++++++++++++++++++++++++++++++++++++ drivers/pwm/pwm-dwc.h | 25 ++- 5 files changed, 455 insertions(+), 30 deletions(-) create mode 100644 drivers/pwm/pwm-dwc-of.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e8886a9b64d9..fd1d68beab67 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -249,6 +249,16 @@ config PWM_DWC To compile this driver as a module, choose M here: the module will be called pwm-dwc. =20 +config PWM_DWC_OF + tristate "DesignWare PWM Controller (OF bus)" + depends on HAS_IOMEM && (OF || COMPILE_TEST) + select PWM_DWC_CORE + help + PWM driver for Synopsys DWC PWM Controller on an OF bus or + a platform bus. + To compile this driver as a module, choose M here: the module + will be called pwm-dwc-of. + config PWM_EP93XX tristate "Cirrus Logic EP93xx PWM support" depends on ARCH_EP93XX || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 5630a521a7cf..acd7dfe98dff 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PWM_CRC) +=3D pwm-crc.o obj-$(CONFIG_PWM_CROS_EC) +=3D pwm-cros-ec.o obj-$(CONFIG_PWM_DWC_CORE) +=3D pwm-dwc-core.o obj-$(CONFIG_PWM_DWC) +=3D pwm-dwc.o +obj-$(CONFIG_PWM_DWC_OF) +=3D pwm-dwc-of.o obj-$(CONFIG_PWM_EP93XX) +=3D pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) +=3D pwm-fsl-ftm.o obj-$(CONFIG_PWM_GPIO) +=3D pwm-gpio.o diff --git a/drivers/pwm/pwm-dwc-core.c b/drivers/pwm/pwm-dwc-core.c index 6dabec93a3c6..b9a9d6ae0b45 100644 --- a/drivers/pwm/pwm-dwc-core.c +++ b/drivers/pwm/pwm-dwc-core.c @@ -12,8 +12,10 @@ #define DEFAULT_SYMBOL_NAMESPACE "dwc_pwm" =20 #include +#include #include #include +#include #include #include #include @@ -44,21 +46,55 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dw= c, u32 high; u32 low; =20 - /* - * Calculate width of low and high period in terms of input clock - * periods and check are the result within HW limits between 1 and - * 2^32 periods. - */ - tmp =3D DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns); - if (tmp < 1 || tmp > (1ULL << 32)) - return -ERANGE; - low =3D tmp - 1; - - tmp =3D DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle, - dwc->clk_ns); - if (tmp < 1 || tmp > (1ULL << 32)) - return -ERANGE; - high =3D tmp - 1; + if (dwc->clk) + dwc->clk_rate =3D clk_get_rate(dwc->clk); + + if (dwc->features & DWC_TIM_CTRL_0N100PWM_EN) { + /* + * Calculate width of low and high period in terms of input + * clock periods and check are the result within HW limits + * between 0 and 2^32 periods. + * Use mul_u64_u64_div_u64() to avoid overflowing the 64-bit + * intermediate result and to round down to the nearest + * achievable hardware value, as required by the PWM core. + */ + tmp =3D mul_u64_u64_div_u64(state->duty_cycle, dwc->clk_rate, + NSEC_PER_SEC); + if (tmp >=3D (1ULL << 32)) + return -ERANGE; + + if (pwm->args.polarity =3D=3D PWM_POLARITY_INVERSED) + high =3D tmp; + else + low =3D tmp; + + tmp =3D mul_u64_u64_div_u64(state->period - state->duty_cycle, + dwc->clk_rate, NSEC_PER_SEC); + if (tmp >=3D (1ULL << 32)) + return -ERANGE; + + if (pwm->args.polarity =3D=3D PWM_POLARITY_INVERSED) + low =3D tmp; + else + high =3D tmp; + } else { + /* + * Calculate width of low and high period in terms of input + * clock periods and check are the result within HW limits + * between 1 and 2^32 periods. + */ + tmp =3D mul_u64_u64_div_u64(state->duty_cycle, dwc->clk_rate, + NSEC_PER_SEC); + if (tmp < 1 || tmp > (1ULL << 32)) + return -ERANGE; + low =3D tmp - 1; + + tmp =3D mul_u64_u64_div_u64(state->period - state->duty_cycle, + dwc->clk_rate, NSEC_PER_SEC); + if (tmp < 1 || tmp > (1ULL << 32)) + return -ERANGE; + high =3D tmp - 1; + } =20 /* * Specification says timer usage flow is to disable timer, then @@ -74,6 +110,7 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc, * width of low period and latter the width of high period in terms * multiple of input clock periods: * Width =3D ((Count + 1) * input clock period). + * Width =3D (Count * input clock period) : supported 0% and 100%. */ dwc_pwm_writel(dwc, low, DWC_TIM_LD_CNT(pwm->hwpwm)); dwc_pwm_writel(dwc, high, DWC_TIM_LD_CNT2(pwm->hwpwm)); @@ -85,6 +122,9 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc, * periods are set by Load Count registers. */ ctrl =3D DWC_TIM_CTRL_MODE_USER | DWC_TIM_CTRL_PWM; + if (dwc->features & DWC_TIM_CTRL_0N100PWM_EN) + ctrl |=3D DWC_TIM_CTRL_0N100PWM_EN; + dwc_pwm_writel(dwc, ctrl, DWC_TIM_CTRL(pwm->hwpwm)); =20 /* @@ -121,10 +161,19 @@ static int dwc_pwm_get_state(struct pwm_chip *chip, s= truct pwm_device *pwm, struct pwm_state *state) { struct dwc_pwm *dwc =3D to_dwc_pwm(chip); + unsigned long clk_rate; u64 duty, period; u32 ctrl, ld, ld2; + int ret; + + ret =3D pm_runtime_resume_and_get(pwmchip_parent(chip)); + if (ret) + return ret; =20 - pm_runtime_get_sync(pwmchip_parent(chip)); + if (dwc->clk) + dwc->clk_rate =3D clk_get_rate(dwc->clk); + + clk_rate =3D dwc->clk_rate; =20 ctrl =3D dwc_pwm_readl(dwc, DWC_TIM_CTRL(pwm->hwpwm)); ld =3D dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(pwm->hwpwm)); @@ -137,17 +186,25 @@ static int dwc_pwm_get_state(struct pwm_chip *chip, s= truct pwm_device *pwm, * based on the timer load-count only. */ if (ctrl & DWC_TIM_CTRL_PWM) { - duty =3D (ld + 1) * dwc->clk_ns; - period =3D (ld2 + 1) * dwc->clk_ns; - period +=3D duty; + if (dwc->features & DWC_TIM_CTRL_0N100PWM_EN) { + if (pwm->args.polarity =3D=3D PWM_POLARITY_INVERSED) + duty =3D ld2; + else + duty =3D ld; + period =3D (u64)ld + ld2; + } else { + duty =3D ld + 1; + period =3D ld2 + 1; + period +=3D duty; + } } else { - duty =3D (ld + 1) * dwc->clk_ns; + duty =3D ld + 1; period =3D duty * 2; } =20 state->polarity =3D PWM_POLARITY_INVERSED; - state->period =3D period; - state->duty_cycle =3D duty; + state->period =3D mul_u64_u64_div_u64(period, NSEC_PER_SEC, clk_rate); + state->duty_cycle =3D mul_u64_u64_div_u64(duty, NSEC_PER_SEC, clk_rate); =20 pm_runtime_put_sync(pwmchip_parent(chip)); =20 @@ -169,7 +226,7 @@ struct pwm_chip *dwc_pwm_alloc(struct device *dev) return chip; dwc =3D to_dwc_pwm(chip); =20 - dwc->clk_ns =3D 10; + dwc->clk_rate =3D NSEC_PER_SEC / 10; chip->ops =3D &dwc_pwm_ops; =20 return chip; diff --git a/drivers/pwm/pwm-dwc-of.c b/drivers/pwm/pwm-dwc-of.c new file mode 100644 index 000000000000..34713cb7299e --- /dev/null +++ b/drivers/pwm/pwm-dwc-of.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DesignWare PWM Controller driver OF + * + * Copyright (C) 2026 SiFive, Inc. + */ + +#define DEFAULT_SYMBOL_NAMESPACE "dwc_pwm_of" + +#include +#include +#include +#include +#include + +#include "pwm-dwc.h" + +struct dwc_pwm_plat_data { + bool reset_required; +}; + +static int dwc_pwm_plat_probe(struct platform_device *pdev) +{ + const struct dwc_pwm_plat_data *pdata; + struct device *dev =3D &pdev->dev; + struct dwc_pwm_drvdata *data; + u32 ctrl[DWC_TIMERS_TOTAL]; + struct pwm_chip *chip; + struct dwc_pwm *dwc; + bool pwm_en =3D false; + u32 nr_pwm, tim_id; + unsigned int i; + int ret; + + data =3D devm_kzalloc(dev, struct_size(data, chips, 1), GFP_KERNEL); + if (!data) + return -ENOMEM; + + chip =3D dwc_pwm_alloc(dev); + if (IS_ERR(chip)) + return dev_err_probe(dev, PTR_ERR(chip), + "failed to alloc pwm\n"); + + dwc =3D to_dwc_pwm(chip); + + dwc->base =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dwc->base)) + return PTR_ERR(dwc->base); + + if (!device_property_read_u32(dev, "snps,pwm-number", &nr_pwm)) { + if (nr_pwm > DWC_TIMERS_TOTAL) + dev_warn(dev, "too many PWMs (%d), capping at %d\n", + nr_pwm, chip->npwm); + else + chip->npwm =3D nr_pwm; + } + + dwc->bus_clk =3D devm_clk_get(dev, "bus"); + if (IS_ERR(dwc->bus_clk)) + return dev_err_probe(dev, PTR_ERR(dwc->bus_clk), + "failed to get bus clock\n"); + + dwc->clk =3D devm_clk_get(dev, "timer"); + if (IS_ERR(dwc->clk)) + return dev_err_probe(dev, PTR_ERR(dwc->clk), + "failed to get timer clock\n"); + + ret =3D devm_clk_rate_exclusive_get(dev, dwc->clk); + if (ret) + return dev_err_probe(dev, ret, + "failed to get exclusive rate\n"); + + dwc->clk_rate =3D clk_get_rate(dwc->clk); + + pdata =3D device_get_match_data(dev); + if (pdata && pdata->reset_required) + dwc->rst =3D devm_reset_control_get_exclusive(dev, NULL); + else + dwc->rst =3D devm_reset_control_array_get_optional_exclusive(dev); + + if (IS_ERR(dwc->rst)) + return dev_err_probe(dev, PTR_ERR(dwc->rst), + "failed to get reset control\n"); + + ret =3D clk_prepare_enable(dwc->bus_clk); + if (ret) + return dev_err_probe(dev, ret, + "failed to enable bus clock\n"); + + ret =3D clk_prepare_enable(dwc->clk); + if (ret) { + dev_err(dev, "failed to enable timer clock\n"); + goto disable_busclk; + } + + /* + * Check all channels to see if any channel is enabled. + * Read the control register of each channel and extract the enable bit + */ + for (i =3D 0; i < chip->npwm; i++) { + ctrl[i] =3D dwc_pwm_readl(dwc, DWC_TIM_CTRL(i)) & DWC_TIM_CTRL_EN; + if (ctrl[i]) + pwm_en =3D true; + } + + /* + * Only issue a reset pulse when all channels are disabled, so a PWM + * channel already running (e.g. configured by firmware before Linux + * took over) is left undisturbed. + */ + if (!pwm_en) { + ret =3D reset_control_reset(dwc->rst); + if (ret) { + dev_err(dev, "failed to reset\n"); + goto disable_clk; + } + } + + /* init PWM feature */ + dwc->features =3D 0; + /* + * Support for 0% and 100% duty cycle mode was added in version 2.11a + * and later. + */ + tim_id =3D dwc_pwm_readl(dwc, DWC_TIMERS_COMP_VERSION); + if (tim_id >=3D DWC_TIM_VERSION_ID_2_11A) + dwc->features |=3D DWC_TIM_CTRL_0N100PWM_EN; + + data->chips[0] =3D chip; + dev_set_drvdata(dev, data); + + /* + * If any PWM channel is enabled, mark device active and hold runtime PM + * references for each enabled channel. Otherwise, gate the clocks. + */ + if (pwm_en) { + pm_runtime_set_active(dev); + for (i =3D 0; i < chip->npwm; i++) { + if (ctrl[i]) + pm_runtime_get_noresume(dev); + } + } else { + clk_disable_unprepare(dwc->clk); + clk_disable_unprepare(dwc->bus_clk); + } + + pm_runtime_enable(dev); + + ret =3D pwmchip_add(chip); + if (ret) { + dev_err(dev, "failed to add pwm chip\n"); + goto pm_disable; + } + + return 0; + +pm_disable: + pm_runtime_disable(dev); + if (pwm_en) { + for (i =3D 0; i < chip->npwm; i++) { + if (ctrl[i]) + pm_runtime_put_noidle(dev); + } + } +disable_clk: + clk_disable_unprepare(dwc->clk); +disable_busclk: + clk_disable_unprepare(dwc->bus_clk); + + return ret; +} + +static void dwc_pwm_plat_remove(struct platform_device *pdev) +{ + struct dwc_pwm_drvdata *data =3D platform_get_drvdata(pdev); + struct pwm_chip *chip =3D data->chips[0]; + struct dwc_pwm *dwc =3D to_dwc_pwm(chip); + unsigned int idx; + int ret; + + pm_runtime_get_sync(&pdev->dev); + pwmchip_remove(chip); + + for (idx =3D 0; idx < chip->npwm; idx++) { + if (dwc_pwm_readl(dwc, DWC_TIM_CTRL(idx)) & DWC_TIM_CTRL_EN) + pm_runtime_put_noidle(&pdev->dev); + } + pm_runtime_put_sync(&pdev->dev); + + if (!pm_runtime_status_suspended(&pdev->dev)) { + clk_disable_unprepare(dwc->clk); + clk_disable_unprepare(dwc->bus_clk); + } + pm_runtime_disable(&pdev->dev); + + if (dwc->rst) { + ret =3D reset_control_assert(dwc->rst); + if (ret) + dev_warn(&pdev->dev, "failed to assert reset: %d\n", + ret); + } +} + +static int dwc_pwm_runtime_suspend(struct device *dev) +{ + struct dwc_pwm_drvdata *data =3D dev_get_drvdata(dev); + struct pwm_chip *chip =3D data->chips[0]; + struct dwc_pwm *dwc =3D to_dwc_pwm(chip); + + clk_disable_unprepare(dwc->clk); + clk_disable_unprepare(dwc->bus_clk); + + return 0; +} + +static int dwc_pwm_runtime_resume(struct device *dev) +{ + struct dwc_pwm_drvdata *data =3D dev_get_drvdata(dev); + struct pwm_chip *chip =3D data->chips[0]; + struct dwc_pwm *dwc =3D to_dwc_pwm(chip); + int ret; + + ret =3D clk_prepare_enable(dwc->bus_clk); + if (ret) { + dev_err(dev, "failed to enable bus clock: %d\n", ret); + return ret; + } + + ret =3D clk_prepare_enable(dwc->clk); + if (ret) { + dev_err(dev, "failed to enable timer clock: %d\n", ret); + clk_disable_unprepare(dwc->bus_clk); + return ret; + } + + return 0; +} + +static int dwc_pwm_suspend(struct device *dev) +{ + struct dwc_pwm_drvdata *data =3D dev_get_drvdata(dev); + struct pwm_chip *chip =3D data->chips[0]; + struct dwc_pwm *dwc =3D to_dwc_pwm(chip); + unsigned int idx; + int ret; + + if (pm_runtime_status_suspended(dev)) { + ret =3D dwc_pwm_runtime_resume(dev); + if (ret) + return ret; + } + + for (idx =3D 0; idx < chip->npwm; idx++) { + if (chip->pwms[idx].state.enabled) + return -EBUSY; + + dwc->ctx[idx].cnt =3D dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(idx)); + dwc->ctx[idx].cnt2 =3D dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(idx)); + dwc->ctx[idx].ctrl =3D dwc_pwm_readl(dwc, DWC_TIM_CTRL(idx)); + } + + ret =3D dwc_pwm_runtime_suspend(dev); + if (ret) + return ret; + + return 0; +} + +static int dwc_pwm_resume(struct device *dev) +{ + struct dwc_pwm_drvdata *data =3D dev_get_drvdata(dev); + struct pwm_chip *chip =3D data->chips[0]; + struct dwc_pwm *dwc =3D to_dwc_pwm(chip); + unsigned int idx; + bool pm_flags; + int ret; + + /* Check if device was runtime suspended before system resume */ + pm_flags =3D pm_runtime_status_suspended(dev); + if (pm_flags) { + /* + * Use PM framework to resume device + * (calls dwc_pwm_runtime_resume) + */ + ret =3D pm_runtime_get_sync(dev); + if (ret < 0) + return ret; + } else { + /* + * Device was active, but clocks might be off after system sleep + * Call runtime_resume directly to restore hardware state + */ + ret =3D dwc_pwm_runtime_resume(dev); + if (ret) + return ret; + } + + for (idx =3D 0; idx < chip->npwm; idx++) { + dwc_pwm_writel(dwc, dwc->ctx[idx].cnt, DWC_TIM_LD_CNT(idx)); + dwc_pwm_writel(dwc, dwc->ctx[idx].cnt2, DWC_TIM_LD_CNT2(idx)); + dwc_pwm_writel(dwc, dwc->ctx[idx].ctrl, DWC_TIM_CTRL(idx)); + } + + if (pm_flags) { + /* + * Balance the refcount taken by pm_runtime_get_sync + * if it was used + */ + pm_runtime_put_sync(dev); + } + + return 0; +} + +static const struct dev_pm_ops dwc_pwm_pm_ops =3D { + RUNTIME_PM_OPS(dwc_pwm_runtime_suspend, dwc_pwm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(dwc_pwm_suspend, dwc_pwm_resume) +}; + +static const struct dwc_pwm_plat_data pwm_eic7700_pdata =3D { + .reset_required =3D true, +}; + +static const struct of_device_id dwc_pwm_dt_ids[] =3D { + { .compatible =3D "snps,dw-apb-timers-pwm2" }, + { .compatible =3D "eswin,eic7700-pwm", .data =3D &pwm_eic7700_pdata }, + { } +}; +MODULE_DEVICE_TABLE(of, dwc_pwm_dt_ids); + +static struct platform_driver dwc_pwm_plat_driver =3D { + .driver =3D { + .name =3D "dwc-pwm", + .pm =3D pm_ptr(&dwc_pwm_pm_ops), + .of_match_table =3D dwc_pwm_dt_ids, + }, + .probe =3D dwc_pwm_plat_probe, + .remove =3D dwc_pwm_plat_remove, +}; + +module_platform_driver(dwc_pwm_plat_driver); + +MODULE_ALIAS("platform:dwc-pwm-of"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("DesignWare PWM Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-dwc.h b/drivers/pwm/pwm-dwc.h index 1562594e7f85..75f7c2d031c4 100644 --- a/drivers/pwm/pwm-dwc.h +++ b/drivers/pwm/pwm-dwc.h @@ -26,12 +26,19 @@ MODULE_IMPORT_NS("dwc_pwm"); #define DWC_TIMERS_TOTAL 8 =20 /* Timer Control Register */ -#define DWC_TIM_CTRL_EN BIT(0) -#define DWC_TIM_CTRL_MODE BIT(1) -#define DWC_TIM_CTRL_MODE_FREE (0 << 1) -#define DWC_TIM_CTRL_MODE_USER (1 << 1) -#define DWC_TIM_CTRL_INT_MASK BIT(2) -#define DWC_TIM_CTRL_PWM BIT(3) +#define DWC_TIM_CTRL_EN BIT(0) +#define DWC_TIM_CTRL_MODE BIT(1) +#define DWC_TIM_CTRL_MODE_FREE (0 << 1) +#define DWC_TIM_CTRL_MODE_USER BIT(1) +#define DWC_TIM_CTRL_INT_MASK BIT(2) +#define DWC_TIM_CTRL_PWM BIT(3) +#define DWC_TIM_CTRL_0N100PWM_EN BIT(4) + +/* + * The version 2.11a and later add "Pulse Width Modulation with + * 0% and 100% Duty Cycle". + */ +#define DWC_TIM_VERSION_ID_2_11A 0x3231312a =20 struct dwc_pwm_info { unsigned int nr; @@ -52,8 +59,12 @@ struct dwc_pwm_ctx { =20 struct dwc_pwm { void __iomem *base; - unsigned int clk_ns; + struct clk *bus_clk; + struct clk *clk; + unsigned long clk_rate; + struct reset_control *rst; struct dwc_pwm_ctx ctx[DWC_TIMERS_TOTAL]; + u32 features; }; =20 static inline struct dwc_pwm *to_dwc_pwm(struct pwm_chip *chip) --=20 2.34.1