From patchwork Tue Sep 22 00:06:30 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3404 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sin.lore.kernel.org (sin.lore.kernel.org [104.64.211.4]) by mxe881.netcup.net (Postfix) with ESMTPS id 9B1691C36E3 for ; Tue, 22 Sep 2026 02:06:55 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 104.64.211.4) smtp.mailfrom=linux-sunxi+bounces-26102-noreply=patchwork.local@lists.linux.dev smtp.helo=sin.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 104.64.211.4 as permitted sender) client-ip=104.64.211.4; envelope-from=linux-sunxi+bounces-26102-noreply=patchwork.local@lists.linux.dev; helo=sin.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sin.lore.kernel.org (Postfix) with ESMTP id 091252D752 for ; Tue, 22 Sep 2026 00:06:49 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id F1BC01D798E; Tue, 22 Sep 2026 00:06:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aRJxexjS" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo1-f45.google.com (mail-oo1-f45.google.com [209.85.161.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7CD9415853B for ; Tue, 22 Sep 2026 00:06:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035606; cv=none; b=WIRRcDG+6C0yML5kO1hh19ZxWFH4ehGzkR8k+8OKcPZWvpTOGi2ttPjxyByuE40om4prqRx76PhtMe4V50u86OfFZAIgEf4GWEPXM1OgeVtc5XbwYPYcLGTZuw/SD/nwMuZX+57J01Rjp7HYXtY3HVHr7SJwDkfZ7X5biZu5qsA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035606; c=relaxed/simple; bh=5k9VLU6OvtrGZi0gO1yZy16OfwqfrZmQwujeqIifWv0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WRujug1a+uvjnFPS2hWDvjARcagKnnBLPJIwTDQ4XtPy8mh+n1159HNlPWiGZFKbKajOApODQV/MN1tbMgmLDszbJsRZW4XbuLkf1WalJxQMNNEBrd4BSmym1kBDyyAqivBOwKYyZu1ZcJ00i8ty2up1wCYLPXYrKf/gWoOuv9E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=aRJxexjS; arc=none smtp.client-ip=209.85.161.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo1-f45.google.com with SMTP id 006d021491bc7-6c8f6a53fabso390255eaf.1 for ; Mon, 21 Sep 2026 17:06:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790035603; x=1790640403; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=M4cUR/bgVx/Ef4Tc/Iqux+0dgmZ5BenjLrR4X+eBZEU=; b=aRJxexjSbanxazuGRzL/CzQYT59tPMQsCxUdm77+eX0OmplksCsHWcJWWwpf7zZY4q GfD4v2LBsPtY9u4mpx8NjVglejA8/wjLGG22pZqvUmtG/8AwuK6q+svGwXP+XeV8EDTU sWZJY5XEHIiZekp1oyE0olA57IrkToh8ANcU8Rm0Y7fR/1Dr8FQa05d92I34whCF/GB2 rBT2KON/JaKaZef6I9tkFDS5L279DVoX/vFLFHnDWC4sFB3Nl9CWcPGR/LgFF21XvfPA TnfBgrFztqlkbPz5A3KZ6TRm90p28c6Q277emSDKzgK01pBJ+yKk5jyRcDPlCF9kiLjC C9mQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790035603; x=1790640403; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=M4cUR/bgVx/Ef4Tc/Iqux+0dgmZ5BenjLrR4X+eBZEU=; b=PhLqR1Q1MfXeRDno8MNbzDZfgL34Xu4U+/sAxxtcKv1dKX2sTO4JViftNKFEYiSGR1 GQpAnJ8DSjrwdYGwscn6P3KAMk071WxW9DAoOG4gphlZWVo1Moq7MEb+F9gir50eHgtU 2fYwQyfK9iTTynnhJIf1fkbJ66FEWQAmSYmvlAcFHsfTsV4pKS35xnu2yDjxA3e1VaCX QRmVnsWo83TiMtb92n0fEWWefGx97jfLrnjrCdWFAQIBvh/RTB26D3TnSykC28GjOcoJ AtS3bglyl+HoiO8rd9x6ZguuJD5yGaW0VAocnl8xqnFYApGmJO37ub2le2mu+dC+JyhG gewg== X-Forwarded-Encrypted: i=1; AKwUvBxdK9//S7ujNqxzXlNJkdVMm4+47I6zq3neNfExJKPYB5m+ZZBoVBPjcgTRl+pD1Xk93T9aWyhIHrhzEA==@lists.linux.dev X-Gm-Message-State: AFuF++mL3Z/FxqRmvrmNPqAA47hRUpJQcZWf9rfcd7TPGzFi2fqyXZKR rs1yLptOdY7j4qCEPVBawlAJwsvYek5AKXe4NhoipE82dffQratJsY2K X-Gm-Gg: AYBFou0ZIuv1T1dUNZwZN3kCudp/7kgH/WuSaNFh7xSoJztxc/lyrvJ7pUyBjzRd6vV kp9hjg6//MCDIc/3pTCL9ugJumFB7uBIrC7ulOMlSmzytfzRwk1SVSyYZ77w6ocfxAy+0VInTTD LCRkNVALKC0CDl5vDaaQPyzik1mo/4bFoek8uyHwRmGqGKF6cUlAX60kbhxNq15IRpdbTXvxkfT thHN25VuO5xQ8FPT/9gzRrunI3g4BkjavUc3ydWFiKqdMiXN1TE6ZXrFzd5IdMDZhXnmJEZ9GtU EjR/+It9uBTGFeFLkOe3MEpkyPT3KDmYDuL+8b+oqgA0FSGJuo+/TVxOmXFSzDM46cgrbETyFKY tbJWYKhDWOsSwZxZiftEQZw1paSh/GIj52ttSd1sTktWG3gwjRiRYLFY2+MCYlLgVe0S/87AN7J bdxDMMtzCkzZwy0qqJF/U6L03wpc8Fe5qh0yd2aqF77sVo125VsC4cMKWgSSUhd46ORWTYrHEOP cVUytf0WWM40z/6w9Zn0UThh/UcTAqCIkr/sRwsM4Fq42WtIWOuCEFPVtmIX4G1Nwv4hsAGBKRl A0mjIvBbd3NR6hRhZIRvgre7PBmAwzCFSuKl2VmKJkVqa79pLBT/8UYfMp3kQlFttHTZQm3NExB zVJfprcHRUSJHIjxmJwn1 X-Received: by 2002:a05:6820:179b:b0:6b1:a146:1297 with SMTP id 006d021491bc7-6d157d893ddmr1041226eaf.1.1790035603341; Mon, 21 Sep 2026 17:06:43 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-48f7280cf14sm801683fac.13.2026.09.21.17.06.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 17:06:42 -0700 (PDT) From: James Hilliard Date: Mon, 21 Sep 2026 18:06:30 -0600 Subject: [PATCH v9 1/5] dt-bindings: pwm: allwinner: add h616 pwm compatible Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260921-h616-pwm-v8-v9-1-37bec6201ff5@gmail.com> References: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> In-Reply-To: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Maxime Ripard , Richard Genoud , Philipp Zabel , Michael Turquette , Stephen Boyd , Brian Masney Cc: linux-pwm@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, Paul Kocialkowski , Thomas Petazzoni , John Stultz , Joao Schim , bigunclemax@gmail.com, linux-clk@vger.kernel.org, James Hilliard , Conor Dooley X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= From: Richard Genoud The Allwinner H616 PWM block differs from the A10 and H6 blocks, but it uses the same input clocks as H6, so extend the existing Allwinner PWM binding. The block has six channels that can generate PWM waveforms. Each channel also has a bypass that turns its output into a clock suitable for consumers such as the integrated X-Powers AC200 or AC300 Ethernet PHY. Require #clock-cells for H616 because the bypass clocks are part of its provider interface, while continuing to disallow the property for the older variants. Acked-by: Conor Dooley Signed-off-by: Richard Genoud Signed-off-by: James Hilliard --- .../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 33 +++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml index 1197858e431f..3b1819c8630c 100644 --- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml @@ -14,6 +14,13 @@ properties: "#pwm-cells": const: 3 + "#clock-cells": + const: 1 + description: + The cell selects the PWM channel whose hardware bypass path is exported + as a clock. Its rate follows the selected shared pair source and divider; + the PWM period, duty-cycle and polarity logic is bypassed. + compatible: oneOf: - const: allwinner,sun4i-a10-pwm @@ -36,6 +43,7 @@ properties: - const: allwinner,sun50i-h5-pwm - const: allwinner,sun5i-a13-pwm - const: allwinner,sun50i-h6-pwm + - const: allwinner,sun50i-h616-pwm reg: maxItems: 1 @@ -62,7 +70,9 @@ allOf: properties: compatible: contains: - const: allwinner,sun50i-h6-pwm + enum: + - allwinner,sun50i-h6-pwm + - allwinner,sun50i-h616-pwm then: properties: @@ -83,6 +93,27 @@ allOf: clocks: maxItems: 1 + - if: + properties: + compatible: + contains: + const: allwinner,sun50i-h616-pwm + + then: + properties: + clocks: + minItems: 2 + + clock-names: + minItems: 2 + + required: + - "#clock-cells" + + else: + properties: + "#clock-cells": false + required: - compatible - reg From patchwork Tue Sep 22 00:06:31 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3405 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sin.lore.kernel.org (sin.lore.kernel.org [104.64.211.4]) by mxe881.netcup.net (Postfix) with ESMTPS id 38D051C36E3 for ; Tue, 22 Sep 2026 02:07:03 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 104.64.211.4) smtp.mailfrom=linux-sunxi+bounces-26103-noreply=patchwork.local@lists.linux.dev smtp.helo=sin.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 104.64.211.4 as permitted sender) client-ip=104.64.211.4; envelope-from=linux-sunxi+bounces-26103-noreply=patchwork.local@lists.linux.dev; helo=sin.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sin.lore.kernel.org (Postfix) with ESMTP id EC7E52D66B for ; Tue, 22 Sep 2026 00:06:51 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 86F581386C9; Tue, 22 Sep 2026 00:06:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WqZj0atr" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oa2-f12.google.com (mail-oa2-f12.google.com [74.125.231.76]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3AF5D2264A6 for ; Tue, 22 Sep 2026 00:06:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.76 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035611; cv=none; b=ozsdj607ohuFgp4lNgtPW2OHZXoCdueCkYvWCtee1ixy5oPhfPfdKGwi2eL2rxEK+eUWtObqbOug0UZ6fMndvx1hRKgU6nY78cg/2hKR9JlWkGiMqKAUshe5K/lMe3zZLyUaoIpVwpHx/z1wxVtJaY+biJTp+gXu4TzQz7WXNK0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035611; c=relaxed/simple; bh=NaP5go48DIg7ZP2vsh8v9L0+32c55LUSiW/3zQ6evCw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=psRB81IujV1o5rwLi2jGdG90II00u6kLj46u/5JbvDHcmJC6DS/4FA3rh/ItlD7qSPDV5xXSy7r/Y6MBpSX9fKZ0Ec/spWlzdVv0VF5hfkyij4wBOEMoe87A9cfLVex0MMidsqyI46lDqkMpKv+LUcheDpKV/sq4HAddnuqSaU0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=WqZj0atr; arc=none smtp.client-ip=74.125.231.76 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oa2-f12.google.com with SMTP id 586e51a60fabf-46accbdfb21so3740987fac.1 for ; Mon, 21 Sep 2026 17:06:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790035606; x=1790640406; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=TmixJAuwq8ENbvDysNd8oI5Iik3FUsXX4Zkq+YJZJ3g=; b=WqZj0atrcbmULcazKIQbVBBnadgUTz1RxzbPfGlxIZG2EbWJpT1jeQqcBdYTPY81yW boB7qMk4Ovx8uDmVA4rRSS0w6hMf6jhfW+smRcsfeGZxfielArVs0Tt6QKtlVBtHixDe ZRV2Guq9GXpNXbKm/gr0q0kwGtaJUZv3F1SPo2w8ic6/K3UV1He9v2uFwqZE7Oi8h0Ah ii2zNlUJRzuHOtj/X46oicO3QmAjnzDfrzoUrM70yhlYwswVacv2dvdADL15IZ0XTcux NBzCAXLlqEYU9TVxvHQgGnjRi5YcoIqhfwjM7b9cwx+jKRLYVogJSvu7gkdudqzMimNl tbNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790035606; x=1790640406; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=TmixJAuwq8ENbvDysNd8oI5Iik3FUsXX4Zkq+YJZJ3g=; b=F8cx/RXS8zC85ZnKkBnJKEo0AG+7hhezx2m/lT7dVMWxZK/4eDkCDELulXh/XwC7Wd PrMnvcYY7cxEu0c7tsUO+aHdpiIMccj+4HtNochyRLF73HtB7JJQfrloxZFMAkX2MEiX 0L2RPV3oMZxzR8AZq7g0hW4E9Pc85JwoVg7hOrCYrPR3RIkzm25BZfPl99b94jv9hYCC Vzog4QRc/Ihrn14DPKwk5DlS8f468IHpY5RKYK9WB67rnc0Omfa0WOI8F1yhBk1RJW08 RDCL8ut2eZQQQtfxaU90G7Qd/30PUOqQxvIyFmV8pcEsZOdp7ZGpN/gq5AS2Ay7hHJqN 5RvQ== X-Forwarded-Encrypted: i=1; AKwUvBzwlRtcpKoNccTcVnnNh7lFs/6qWimLbYeAn9LC8HvTMDC2TZ82d0qrl2zoBc8OyeSvQGH4PqV94q0ArQ==@lists.linux.dev X-Gm-Message-State: AFuF++not+mTkXR5A9m3ErBlcczw4Um259xoU7gwYPknS46Gns13jaDb YHNKhhpS/HMLeq2eXWlkiGLEbsyC6iCzvS/hl43wcgiz4C3WT7aD5jJ+ X-Gm-Gg: AYBFou0TIHB9AegTVxJYx7gxV2eBgHRz4DAHI7yxTsOiJtBym0w7Q3jAVTB+VLCHKFQ gwYZCwB/gff19jDdvYzGC+m2Zw/CAyFEs8/wSof+lxB4nqoTPJefW3jeJf/wuJ6BYRWrmdeZoc2 0WsNJ1XvaM/ILyeoJzGNawg/R1tQqS6CM/Oztx6+KY4rek8AZg+nMxuZIVcWwLyjv+g3Gltpu7m 9bohKKXvzu1I+LS9IQBcQ4QsjdCZJypvFH6nW9Ba1kTfO6vj6CwbpJGrM8hqXMNeVuZmtSFDWgp uj3GIuOnL1EgLxrXtF+K8jMnzabectFe595GHrsF9Cp+wXcAP//6unbkjNQPIQDh53THq7oz0/R OUO38BtHkzegFzMmFIbWIbq5HdjKZ+BEWjbX8wSO2/JMzCgn7saJ4inVOtixYNzbYCBgmJhwm// pe1W9v0UWGuHeM4HYc6t1yBStJulmOi5vTvlqZGfmmszxQXDhthfrKrz+DeitInbF7y6/X4ZEol E2fN1pHNG4OkK1H2QWh6G/NLKrtuH6iShwKN/muzC/JDnGU4nA8EuH0qvm8jJ/wLW55gA6yV2Nu vF3CkcEMSPvbtse8Mr+VNsIvbSeUhY6VUhWx2T9HHIZXoHPB474H4hCkCpAVNw5HbTE8mdrnDEj +yUS2ag5n6J8+uZ2a3/vU X-Received: by 2002:a05:6820:f029:b0:6c9:80d8:a1f4 with SMTP id 006d021491bc7-6ca9cb5b1ffmr11821779eaf.49.1790035605893; Mon, 21 Sep 2026 17:06:45 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-48f7280cf14sm801683fac.13.2026.09.21.17.06.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 17:06:44 -0700 (PDT) From: James Hilliard Date: Mon, 21 Sep 2026 18:06:31 -0600 Subject: [PATCH v9 2/5] pwm: sun8i: Add H616 PWM support Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260921-h616-pwm-v8-v9-2-37bec6201ff5@gmail.com> References: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> In-Reply-To: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Maxime Ripard , Richard Genoud , Philipp Zabel , Michael Turquette , Stephen Boyd , Brian Masney Cc: linux-pwm@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, Paul Kocialkowski , Thomas Petazzoni , John Stultz , Joao Schim , bigunclemax@gmail.com, linux-clk@vger.kernel.org, James Hilliard X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-1.16 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[104.64.211.4:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; BAD_REP_POLICIES(0.10)[]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; RCVD_COUNT_FIVE(0.00)[6]; FROM_HAS_DN(0.00)[]; FORGED_SENDER_MAILLIST(0.00)[]; RCPT_COUNT_TWELVE(0.00)[25]; PRECEDENCE_BULK(0.00)[]; FREEMAIL_CC(0.00)[vger.kernel.org,lists.infradead.org,lists.linux.dev,sys-base.io,bootlin.com,google.com,schimsalabim.eu,gmail.com]; TAGGED_RCPT(0.00)[dt]; DBL_BLOCKED_OPENRESOLVER(0.00)[bootlin.com:email,sin.lore.kernel.org:rdns,sin.lore.kernel.org:helo]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; TAGGED_FROM(0.00)[bounces-26103-noreply=patchwork.local]; FREEMAIL_FROM(0.00)[gmail.com]; R_SPF_ALLOW(0.00)[+ip4:104.64.211.4]; TO_DN_SOME(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[104.64.211.4:from]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[100.90.174.1:received,174.29.1.49:received]; ASN(0.00)[asn:63949, ipnet:104.64.192.0/19, country:SG]; FREEMAIL_TO(0.00)[kernel.org,gmail.com,sholland.org,bootlin.com,pengutronix.de,baylibre.com,redhat.com]; MIME_TRACE(0.00)[0:+]; MID_RHS_MATCH_FROM(0.00)[]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; RCVD_TLS_LAST(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: 38D051C36E3 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= From: Richard Genoud Add waveform support for the six-channel Allwinner H616 PWM controller. Each pair of outputs shares a source mux, gate and power-of-two divider; each channel has its own prescaler and period/duty registers. Model the shared pair clocks in CCF and program the channel-local dividers directly. Protect the pair rate while a PWM is active. Also check the hardware enable bits before retuning or gating a pair, since firmware may have left an output running without a Linux consumer. Preserve such outputs until a consumer explicitly changes them; do not implement an unused-PWM sweep. Round period, duty and offset in the order required by the waveform API, including the hardware bypass path and the 65536-tick period boundary. Wait for pending period updates and quiesce the channel when changing its clock, prescaler, polarity or bypass mode. Live period updates additionally require PCLK to be faster than the PWM clock, as specified by the manual. Encode the channel register values during waveform conversion. Keep rate protection, safe quiescing and clock rollback in one preparation helper, leaving the write callback to apply the prepared values. Select candidates using one ordered comparison while retaining the bounded divider search. Pulse and coupled dead-zone modes cannot be represented by this API. Reject active unsupported modes, but clear dormant dead-zone configuration before programming an ordinary waveform. Allocate the fixed six-channel topology with the pwm_chip. Register the per-device clocks with managed helpers after deasserting reset. Acquire separate pair-clock consumer handles only while PWMs are requested, so internal clock references cannot prevent module removal. Use CCF's provider-owned handles for notifiers and rate readback, including readback of channels which have not been requested. Signed-off-by: Richard Genoud Co-developed-by: James Hilliard Signed-off-by: James Hilliard --- drivers/pwm/Kconfig | 11 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-sun8i.c | 1080 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1092 insertions(+) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 729776086879..d87d166e7836 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -748,6 +748,17 @@ config PWM_SUN4I To compile this driver as a module, choose M here: the module will be called pwm-sun4i. +config PWM_SUN8I + tristate "Allwinner sun8i/sun50i PWM support" + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM && COMMON_CLK + help + PWM framework driver for Allwinner controllers whose channels share + paired source clocks. + + To compile this driver as a module, choose M here: the module + will be called pwm-sun8i. + config PWM_SUNPLUS tristate "Sunplus PWM support" depends on ARCH_SUNPLUS || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 5630a521a7cf..9c922b1fd0b4 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o +obj-$(CONFIG_PWM_SUN8I) += pwm-sun8i.o obj-$(CONFIG_PWM_SUNPLUS) += pwm-sunplus.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TH1520) += pwm_th1520.o diff --git a/drivers/pwm/pwm-sun8i.c b/drivers/pwm/pwm-sun8i.c new file mode 100644 index 000000000000..7e6c39815c18 --- /dev/null +++ b/drivers/pwm/pwm-sun8i.c @@ -0,0 +1,1080 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Allwinner sun8i Pulse Width Modulation Controller + * + * (C) Copyright 2025 Richard Genoud, Bootlin + * (C) Copyright 2026 James Hilliard + * + * Based on drivers/pwm/pwm-sun4i.c with Copyright: + * + * Copyright (C) 2014 Alexandre Belloni + * + * Channels are paired (0/1, 2/3, 4/5). Each pair shares a clock source and + * first prescaler (div_m), while each channel has its own second prescaler + * (div_k) and bypass path. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUN8I_PWM_NPWM 6 +#define SUN8I_PWM_NPAIRS (SUN8I_PWM_NPWM / 2) + +/* PWMCC Pairs Clock Configuration Registers */ +#define SUN8I_PWM_PCCR(pair) (0x20 + ((pair) * 0x4)) +#define SUN8I_PWM_PCCR_SRC_SHIFT 7 +#define SUN8I_PWM_PCCR_SRC_MASK GENMASK(1, 0) +#define SUN8I_PWM_PCCR_GATE_BIT 4 +#define SUN8I_PWM_PCCR_GATE BIT(SUN8I_PWM_PCCR_GATE_BIT) +#define SUN8I_PWM_PCCR_BYPASS_BIT(chan) ((chan) % 2 + 5) +#define SUN8I_PWM_PCCR_DIV_M_SHIFT 0 +#define SUN8I_PWM_PCCR_DIV_M_WIDTH 4 + +/* PWM Enable Register */ +#define SUN8I_PWM_PER 0x40 +#define SUN8I_PWM_ENABLE(chan) BIT(chan) + +/* PWM Control Register */ +#define SUN8I_PWM_PCR(chan) (0x60 + (chan) * 0x20) +#define SUN8I_PWM_PCR_PRESCAL_K_MASK GENMASK(7, 0) +#define SUN8I_PWM_PCR_ACTIVE_STATE BIT(8) +#define SUN8I_PWM_PCR_MODE BIT(9) +#define SUN8I_PWM_PCR_PULSE_START BIT(10) +#define SUN8I_PWM_PCR_PERIOD_READY BIT(11) /* 0: ready, 1: busy */ +#define SUN8I_PWM_PCR_WAVEFORM_MASK (SUN8I_PWM_PCR_PRESCAL_K_MASK | \ + SUN8I_PWM_PCR_ACTIVE_STATE) + +/* PWM Period Register */ +#define SUN8I_PWM_PPR(chan) (0x64 + (chan) * 0x20) +#define SUN8I_PWM_PPR_PERIOD_MASK GENMASK(31, 16) +#define SUN8I_PWM_PPR_DUTY_MASK GENMASK(15, 0) +#define SUN8I_PWM_PPR_PERIOD_VALUE(reg) (FIELD_GET(SUN8I_PWM_PPR_PERIOD_MASK, reg) + 1) +#define SUN8I_PWM_PPR_DUTY_VALUE(reg) FIELD_GET(SUN8I_PWM_PPR_DUTY_MASK, reg) +#define SUN8I_PWM_PPR_PERIOD(prd) FIELD_PREP(SUN8I_PWM_PPR_PERIOD_MASK, (prd) - 1) +#define SUN8I_PWM_PPR_DUTY(dty) FIELD_PREP(SUN8I_PWM_PPR_DUTY_MASK, dty) +#define SUN8I_PWM_PPR_PERIOD_MAX (FIELD_MAX(SUN8I_PWM_PPR_PERIOD_MASK) + 1) + +/* PWM pair dead-zone control registers. */ +#define SUN8I_PWM_PDZCR(pair) (0x30 + ((pair) * 0x4)) +#define SUN8I_PWM_PDZCR_ENABLE BIT(0) + +#define SUN8I_PWM_PERIOD_READY_MARGIN_US 50 +#define SUN8I_PWM_PERIOD_READY_MIN_POLL_US 10 +#define SUN8I_PWM_PERIOD_READY_MAX_POLL_US 10000 +#define SUN8I_PWM_PERIOD_READY_POLL_COUNT 1024 + +#define SUN8I_PWM_PAIR_IDX(chan) ((chan) >> 1) + +/* + * Block diagram of the PWM clock controller: + * + * _____ ______ ________ + * OSC24M --->| | | | | | + * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> SUN8I_PWM_clock_src_xy + * |_____| |______| |________| + * ________ + * | | + * +->| /div_k |---> SUN8I_PWM_clock_x + * | |________| + * | ______ + * | | | + * +-->| Gate |----> SUN8I_PWM_bypass_clock_x + * | |______| + * SUN8I_PWM_clock_src_xy ---+ ________ + * | | | + * +->| /div_k |---> SUN8I_PWM_clock_y + * | |________| + * | ______ + * | | | + * +-->| Gate |----> SUN8I_PWM_bypass_clock_y + * |______| + * + * NB: when the bypass is set, all the PWM logic is bypassed. + * So, the duty cycle and polarity can't be modified (we just have a clock). + * The bypass in PWM mode is used to achieve a 1/2 relative duty cycle with the + * fastest clock. + * + */ + +/* /div_m is a power-of-two divider limited to /256. */ +static const struct clk_div_table sun8i_pwm_div_m_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 16 }, + { .val = 5, .div = 32 }, + { .val = 6, .div = 64 }, + { .val = 7, .div = 128 }, + { .val = 8, .div = 256 }, + { /* sentinel */ } +}; + +struct sun8i_pwm_chip; + +struct sun8i_pwm_pair { + struct clk_mux mux; + struct clk_hw gate_hw; + struct clk_divider divider; + struct clk_hw *hw; + struct notifier_block rate_nb; + struct sun8i_pwm_chip *chip; + unsigned int index; +}; + +struct sun8i_pwm_channel { + /* Separate CCF consumer, held only from PWM request to free. */ + struct clk *pair_clk; + u64 pending_period_ns; + bool rate_exclusive; +}; + +struct sun8i_pwm_chip { + struct sun8i_pwm_pair pairs[SUN8I_PWM_NPAIRS]; + struct sun8i_pwm_channel channels[SUN8I_PWM_NPWM]; + struct clk *bus_clk; + void __iomem *base; + /* Protects shared registers and rate_exclusive. */ + spinlock_t lock; +}; + +struct sun8i_pwm_waveform { + u32 pcr; + u32 ppr; + u32 pair_rate; + bool enabled; + bool bypass_en; +}; + +static inline struct sun8i_pwm_chip *sun8i_pwm_from_chip(const struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +static unsigned long sun8i_pwm_get_pair_rate(struct sun8i_pwm_chip *sun8i_chip, + unsigned int idx) +{ + struct sun8i_pwm_pair *pair = &sun8i_chip->pairs[SUN8I_PWM_PAIR_IDX(idx)]; + + /* Readback also runs on channels without a PWM request. */ + return clk_get_rate(pair->hw->clk); +} + +static inline u32 sun8i_pwm_readl(struct sun8i_pwm_chip *sun8i_chip, + unsigned long offset) +{ + return readl(sun8i_chip->base + offset); +} + +static inline void sun8i_pwm_writel(struct sun8i_pwm_chip *sun8i_chip, + u32 val, unsigned long offset) +{ + writel(val, sun8i_chip->base + offset); +} + +static void sun8i_pwm_set_bypass_locked(struct sun8i_pwm_chip *sun8i_chip, + unsigned int idx, bool enable) +{ + unsigned long reg_offset; + u32 val; + + lockdep_assert_held(&sun8i_chip->lock); + + reg_offset = SUN8I_PWM_PCCR(SUN8I_PWM_PAIR_IDX(idx)); + val = sun8i_pwm_readl(sun8i_chip, reg_offset); + if (enable) + val |= BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx)); + else + val &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx)); + + sun8i_pwm_writel(sun8i_chip, val, reg_offset); +} + +static void sun8i_pwm_set_enabled_locked(struct sun8i_pwm_chip *sun8i_chip, + unsigned int idx, bool enable) +{ + u32 val; + + lockdep_assert_held(&sun8i_chip->lock); + + val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); + if (enable) + val |= SUN8I_PWM_ENABLE(idx); + else + val &= ~SUN8I_PWM_ENABLE(idx); + sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PER); +} + +static bool +sun8i_pwm_channel_is_enabled_locked(struct sun8i_pwm_chip *sun8i_chip, + unsigned int idx) +{ + unsigned int pair = SUN8I_PWM_PAIR_IDX(idx); + u32 pccr, per; + + lockdep_assert_held(&sun8i_chip->lock); + + pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); + per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); + + return (pccr & SUN8I_PWM_PCCR_GATE) && + (per & SUN8I_PWM_ENABLE(idx)); +} + +static u32 sun8i_pwm_pair_enable_mask(unsigned int pair) +{ + return GENMASK(pair * 2 + 1, pair * 2); +} + +static inline struct sun8i_pwm_pair * +sun8i_pwm_pair_from_gate_hw(struct clk_hw *hw) +{ + return container_of(hw, struct sun8i_pwm_pair, gate_hw); +} + +static int sun8i_pwm_pair_gate_enable(struct clk_hw *hw) +{ + struct sun8i_pwm_pair *pair = sun8i_pwm_pair_from_gate_hw(hw); + struct sun8i_pwm_chip *sun8i_chip = pair->chip; + u32 pccr; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair->index)); + pccr |= SUN8I_PWM_PCCR_GATE; + sun8i_pwm_writel(sun8i_chip, pccr, SUN8I_PWM_PCCR(pair->index)); + + return 0; +} + +static void sun8i_pwm_pair_gate_disable(struct clk_hw *hw) +{ + struct sun8i_pwm_pair *pair = sun8i_pwm_pair_from_gate_hw(hw); + struct sun8i_pwm_chip *sun8i_chip = pair->chip; + unsigned long reg = SUN8I_PWM_PCCR(pair->index); + u32 pccr, per; + + /* CCF counts do not include outputs awaiting firmware-state handoff. */ + guard(spinlock_irqsave)(&sun8i_chip->lock); + per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); + if (!(per & sun8i_pwm_pair_enable_mask(pair->index))) { + pccr = sun8i_pwm_readl(sun8i_chip, reg); + pccr &= ~SUN8I_PWM_PCCR_GATE; + sun8i_pwm_writel(sun8i_chip, pccr, reg); + } +} + +static int sun8i_pwm_pair_gate_is_enabled(struct clk_hw *hw) +{ + struct sun8i_pwm_pair *pair = sun8i_pwm_pair_from_gate_hw(hw); + struct sun8i_pwm_chip *sun8i_chip = pair->chip; + unsigned long reg = SUN8I_PWM_PCCR(pair->index); + + guard(spinlock_irqsave)(&sun8i_chip->lock); + + return !!(sun8i_pwm_readl(sun8i_chip, reg) & SUN8I_PWM_PCCR_GATE); +} + +static const struct clk_ops sun8i_pwm_pair_gate_ops = { + .enable = sun8i_pwm_pair_gate_enable, + .disable = sun8i_pwm_pair_gate_disable, + .is_enabled = sun8i_pwm_pair_gate_is_enabled, +}; + +static int sun8i_pwm_pair_rate_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct sun8i_pwm_pair *pair = + container_of(nb, struct sun8i_pwm_pair, rate_nb); + struct sun8i_pwm_chip *sun8i_chip = pair->chip; + bool active; + + if (event != PRE_RATE_CHANGE) + return NOTIFY_DONE; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + active = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER) & + sun8i_pwm_pair_enable_mask(pair->index); + + return active ? NOTIFY_BAD : NOTIFY_OK; +} + +static void sun8i_pwm_put_rate(struct sun8i_pwm_chip *sun8i_chip, + unsigned int idx) +{ + struct sun8i_pwm_channel *chan = &sun8i_chip->channels[idx]; + bool exclusive; + + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + exclusive = chan->rate_exclusive; + chan->rate_exclusive = false; + } + /* CCF takes the prepare mutex and may call back into this driver. */ + if (exclusive) + clk_rate_exclusive_put(chan->pair_clk); +} + +static int sun8i_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); + struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; + unsigned int idx = pwm->hwpwm; + struct clk_hw *parent = sun8i_chip->pairs[SUN8I_PWM_PAIR_IDX(idx)].hw; + bool was_enabled; + int ret; + + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + was_enabled = + sun8i_pwm_channel_is_enabled_locked(sun8i_chip, idx); + } + + chan->pair_clk = clk_hw_get_clk(parent, NULL); + if (IS_ERR(chan->pair_clk)) { + ret = PTR_ERR(chan->pair_clk); + goto err_clear_clock; + } + + if (was_enabled) { + ret = clk_rate_exclusive_get(chan->pair_clk); + if (ret) + goto err_put_clock; + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) + chan->rate_exclusive = true; + } + + ret = clk_prepare_enable(chan->pair_clk); + if (ret) { + sun8i_pwm_put_rate(sun8i_chip, idx); + goto err_put_clock; + } + + return 0; + +err_put_clock: + clk_put(chan->pair_clk); +err_clear_clock: + chan->pair_clk = NULL; + + return ret; +} + +static void sun8i_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); + struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; + + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, false); + sun8i_pwm_set_bypass_locked(sun8i_chip, pwm->hwpwm, false); + } + + sun8i_pwm_put_rate(sun8i_chip, pwm->hwpwm); + clk_disable_unprepare(chan->pair_clk); + clk_put(chan->pair_clk); + chan->pair_clk = NULL; +} + +static int sun8i_pwm_read_waveform(struct pwm_chip *chip, + struct pwm_device *pwm, + void *_wfhw) +{ + struct sun8i_pwm_waveform *wfhw = _wfhw; + struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); + unsigned int pair = SUN8I_PWM_PAIR_IDX(pwm->hwpwm); + unsigned long pair_rate; + u32 pccr, pcr, pdzcr, per, ppr; + + pair_rate = sun8i_pwm_get_pair_rate(sun8i_chip, pwm->hwpwm); + if (pair_rate > U32_MAX) + return -ERANGE; + + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); + per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); + pcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(pwm->hwpwm)); + ppr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PPR(pwm->hwpwm)); + pdzcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PDZCR(pair)); + } + + *wfhw = (struct sun8i_pwm_waveform) { + .enabled = !!(pccr & SUN8I_PWM_PCCR_GATE) && + !!(per & SUN8I_PWM_ENABLE(pwm->hwpwm)), + .bypass_en = !!(pccr & + BIT(SUN8I_PWM_PCCR_BYPASS_BIT(pwm->hwpwm))), + .pcr = pcr & SUN8I_PWM_PCR_WAVEFORM_MASK, + .ppr = ppr, + .pair_rate = pair_rate, + }; + + /* The waveform API cannot describe coupled dead-zone or pulse mode. */ + if (wfhw->enabled && !wfhw->bypass_en && + ((pdzcr & SUN8I_PWM_PDZCR_ENABLE) || (pcr & SUN8I_PWM_PCR_MODE))) + return -EOPNOTSUPP; + + return 0; +} + +static u64 sun8i_pwm_ticks_to_ns(u32 ticks, u16 div_k, u32 pair_rate) +{ + return DIV_ROUND_UP_ULL(NSEC_PER_SEC * (u64)ticks * div_k, + pair_rate); +} + +static u64 sun8i_pwm_period_ns(const struct sun8i_pwm_waveform *wfhw) +{ + if (wfhw->bypass_en) + return DIV_ROUND_UP_ULL(NSEC_PER_SEC, wfhw->pair_rate); + + return sun8i_pwm_ticks_to_ns(SUN8I_PWM_PPR_PERIOD_VALUE(wfhw->ppr), + FIELD_GET(SUN8I_PWM_PCR_PRESCAL_K_MASK, wfhw->pcr) + 1, + wfhw->pair_rate); +} + +static void +__sun8i_pwm_round_waveform_fromhw(const struct sun8i_pwm_waveform *wfhw, + struct pwm_waveform *wf) +{ + u32 pair_rate = wfhw->pair_rate; + u32 period_ticks = SUN8I_PWM_PPR_PERIOD_VALUE(wfhw->ppr); + u32 duty_ticks = SUN8I_PWM_PPR_DUTY_VALUE(wfhw->ppr); + u16 div_k = FIELD_GET(SUN8I_PWM_PCR_PRESCAL_K_MASK, wfhw->pcr) + 1; + + wf->duty_offset_ns = 0; + + if (!wfhw->enabled || !pair_rate) { + wf->period_length_ns = 0; + wf->duty_length_ns = 0; + return; + } + + wf->period_length_ns = sun8i_pwm_period_ns(wfhw); + if (wfhw->bypass_en) { + wf->duty_length_ns = + DIV_ROUND_UP_ULL(NSEC_PER_SEC, + 2ULL * pair_rate); + return; + } + + duty_ticks = min(duty_ticks, period_ticks); + if (!(wfhw->pcr & SUN8I_PWM_PCR_ACTIVE_STATE)) { + /* Constant outputs have no edge and therefore no offset. */ + if (duty_ticks && duty_ticks < period_ticks) + wf->duty_offset_ns = + sun8i_pwm_ticks_to_ns(duty_ticks, div_k, pair_rate); + duty_ticks = period_ticks - duty_ticks; + } + wf->duty_length_ns = sun8i_pwm_ticks_to_ns(duty_ticks, div_k, pair_rate); +} + +static int sun8i_pwm_round_waveform_fromhw(struct pwm_chip *chip, + struct pwm_device *pwm, + const void *_wfhw, + struct pwm_waveform *wf) +{ + const struct sun8i_pwm_waveform *wfhw = _wfhw; + + __sun8i_pwm_round_waveform_fromhw(wfhw, wf); + + dev_dbg(pwmchip_parent(chip), + "pwm#%u: pair-rate=%u, pcr=%#x, ppr=%#x, bypass=%u -> %llu/%llu [+%llu]\n", + pwm->hwpwm, wfhw->pair_rate, wfhw->pcr, wfhw->ppr, + wfhw->bypass_en, + wf->duty_length_ns, wf->period_length_ns, + wf->duty_offset_ns); + + return 0; +} + +static bool +sun8i_pwm_pair_rate_constrained(struct sun8i_pwm_chip *sun8i_chip, + unsigned int idx) +{ + unsigned int sibling = idx ^ 1; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + return sun8i_chip->channels[sibling].rate_exclusive || + sun8i_pwm_channel_is_enabled_locked(sun8i_chip, sibling); +} + +static int sun8i_pwm_clear_idle_deadzone(struct sun8i_pwm_chip *sun8i_chip, + unsigned int idx) +{ + unsigned int pair = SUN8I_PWM_PAIR_IDX(idx); + u32 pdzcr, per; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + pdzcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PDZCR(pair)); + if (!(pdzcr & SUN8I_PWM_PDZCR_ENABLE)) + return 0; + + per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); + if (per & sun8i_pwm_pair_enable_mask(pair)) + return -EOPNOTSUPP; + + pdzcr &= ~SUN8I_PWM_PDZCR_ENABLE; + sun8i_pwm_writel(sun8i_chip, pdzcr, SUN8I_PWM_PDZCR(pair)); + + return 0; +} + +static u64 sun8i_pwm_max_period_ns(struct sun8i_pwm_chip *sun8i_chip, + unsigned int idx) +{ + unsigned long pair_rate; + u32 pcr; + u16 div_k; + + pair_rate = sun8i_pwm_get_pair_rate(sun8i_chip, idx); + if (!pair_rate || pair_rate > U32_MAX) + return 0; + + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + pcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(idx)); + } + div_k = FIELD_GET(SUN8I_PWM_PCR_PRESCAL_K_MASK, pcr) + 1; + + return sun8i_pwm_ticks_to_ns(SUN8I_PWM_PPR_PERIOD_MAX, div_k, + pair_rate); +} + +static int sun8i_pwm_wait_period_ready(struct pwm_chip *chip, unsigned int idx) +{ + struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); + struct sun8i_pwm_channel *chan = &sun8i_chip->channels[idx]; + unsigned long poll_us; + u64 period_ns, timeout_us; + u32 val; + int ret; + + /* PPR contains the pending value, not necessarily the active period. */ + period_ns = chan->pending_period_ns; + if (!period_ns) + period_ns = sun8i_pwm_max_period_ns(sun8i_chip, idx); + timeout_us = DIV_ROUND_UP_ULL(period_ns, NSEC_PER_USEC) + + SUN8I_PWM_PERIOD_READY_MARGIN_US; + poll_us = clamp_t(u64, + DIV_ROUND_UP_ULL(timeout_us, + SUN8I_PWM_PERIOD_READY_POLL_COUNT), + SUN8I_PWM_PERIOD_READY_MIN_POLL_US, + SUN8I_PWM_PERIOD_READY_MAX_POLL_US); + + ret = readl_poll_timeout(sun8i_chip->base + SUN8I_PWM_PCR(idx), val, + !(val & SUN8I_PWM_PCR_PERIOD_READY), poll_us, + timeout_us); + if (!ret) + chan->pending_period_ns = 0; + else + dev_err_ratelimited(pwmchip_parent(chip), + "pwm#%u: update timeout after %llu us\n", + idx, timeout_us); + + return ret; +} + +/* + * Protect the rate and quiesce the output if a live update is unsafe. On + * failure restore the old clock before restarting an output we stopped. + * CCF operations must stay outside the register lock. + */ +static int sun8i_pwm_prepare_update(struct pwm_chip *chip, unsigned int idx, + const struct sun8i_pwm_waveform *wfhw, + struct sun8i_pwm_waveform *old) +{ + struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); + struct sun8i_pwm_channel *chan = &sun8i_chip->channels[idx]; + unsigned long rate, bus_rate; + bool had_exclusive, stop; + u32 pccr; + int ret, restore_ret; + + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) + had_exclusive = chan->rate_exclusive; + + if (!had_exclusive) { + ret = clk_rate_exclusive_get(chan->pair_clk); + if (ret) + return ret; + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) + chan->rate_exclusive = true; + } + + /* Sample the old rate only after excluding other rate changes. */ + rate = sun8i_pwm_get_pair_rate(sun8i_chip, idx); + if (!rate || rate > U32_MAX) { + ret = -ERANGE; + goto put_rate; + } + old->pair_rate = rate; + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + old->enabled = sun8i_pwm_channel_is_enabled_locked(sun8i_chip, idx); + pccr = sun8i_pwm_readl(sun8i_chip, + SUN8I_PWM_PCCR(SUN8I_PWM_PAIR_IDX(idx))); + old->bypass_en = !!(pccr & BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx))); + old->pcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(idx)); + old->ppr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PPR(idx)); + } + + if (rate != wfhw->pair_rate && + sun8i_pwm_pair_rate_constrained(sun8i_chip, idx)) { + ret = -EBUSY; + goto put_rate; + } + + /* Live PPR updates require unchanged configuration and a faster PCLK. */ + bus_rate = clk_get_rate(sun8i_chip->bus_clk); + stop = old->enabled && + (old->bypass_en != wfhw->bypass_en || rate != wfhw->pair_rate || + (!wfhw->bypass_en && + (((old->pcr ^ wfhw->pcr) & + (SUN8I_PWM_PCR_WAVEFORM_MASK | SUN8I_PWM_PCR_MODE)) || + bus_rate <= DIV_ROUND_UP_ULL(wfhw->pair_rate, + FIELD_GET(SUN8I_PWM_PCR_PRESCAL_K_MASK, wfhw->pcr) + 1)))); + if (stop) { + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) + sun8i_pwm_set_enabled_locked(sun8i_chip, idx, false); + } + + if (rate == wfhw->pair_rate) + return 0; + + ret = clk_set_rate(chan->pair_clk, wfhw->pair_rate); + if (!ret && sun8i_pwm_get_pair_rate(sun8i_chip, idx) == wfhw->pair_rate) + return 0; + if (!ret) + ret = -EINVAL; + + if (sun8i_pwm_get_pair_rate(sun8i_chip, idx) != rate) { + restore_ret = clk_set_rate(chan->pair_clk, rate); + if (restore_ret || sun8i_pwm_get_pair_rate(sun8i_chip, idx) != rate) { + dev_err(pwmchip_parent(chip), + "pwm#%u: failed to restore pair clock rate %lu\n", + idx, rate); + goto put_rate; + } + } + if (stop) { + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) + sun8i_pwm_set_enabled_locked(sun8i_chip, idx, true); + } +put_rate: + if (!had_exclusive) + sun8i_pwm_put_rate(sun8i_chip, idx); + + return ret; +} + +static int sun8i_pwm_write_waveform(struct pwm_chip *chip, + struct pwm_device *pwm, const void *_wfhw) +{ + const struct sun8i_pwm_waveform *wfhw = _wfhw; + struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); + struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; + struct sun8i_pwm_waveform old; + u64 old_ns = 0, new_ns; + u32 pcr; + int ret; + + if (!wfhw->enabled) { + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, false); + sun8i_pwm_set_bypass_locked(sun8i_chip, pwm->hwpwm, false); + } + sun8i_pwm_put_rate(sun8i_chip, pwm->hwpwm); + return 0; + } + + if (!wfhw->bypass_en) { + ret = sun8i_pwm_clear_idle_deadzone(sun8i_chip, pwm->hwpwm); + if (ret) + return ret; + ret = sun8i_pwm_wait_period_ready(chip, pwm->hwpwm); + if (ret) + return ret; + } + + ret = sun8i_pwm_prepare_update(chip, pwm->hwpwm, wfhw, &old); + if (ret) + return ret; + + if (!wfhw->bypass_en) { + /* Apply only waveform fields; never replay status or W1S bits. */ + pcr = old.pcr & ~(SUN8I_PWM_PCR_WAVEFORM_MASK | + SUN8I_PWM_PCR_MODE | SUN8I_PWM_PCR_PULSE_START | + SUN8I_PWM_PCR_PERIOD_READY); + sun8i_pwm_writel(sun8i_chip, pcr | wfhw->pcr, + SUN8I_PWM_PCR(pwm->hwpwm)); + + if (old.enabled && !old.bypass_en) + old_ns = sun8i_pwm_period_ns(&old); + new_ns = sun8i_pwm_period_ns(wfhw); + chan->pending_period_ns = max(old_ns, new_ns); + sun8i_pwm_writel(sun8i_chip, wfhw->ppr, SUN8I_PWM_PPR(pwm->hwpwm)); + } + + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + sun8i_pwm_set_bypass_locked(sun8i_chip, pwm->hwpwm, wfhw->bypass_en); + sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, true); + } + + return 0; +} + +struct sun8i_pwm_rounding { + const struct pwm_waveform *requested; + struct sun8i_pwm_waveform best; + struct pwm_waveform best_wf; + u32 current_pair_rate; + bool have_best; +}; + +static void +sun8i_pwm_choose_waveform(struct sun8i_pwm_rounding *rounding, + const struct sun8i_pwm_waveform *candidate) +{ + const struct pwm_waveform *requested = rounding->requested; + const struct pwm_waveform *best = &rounding->best_wf; + struct pwm_waveform wf; + bool better; + + __sun8i_pwm_round_waveform_fromhw(candidate, &wf); + /* A zero-duty, zero-offset cycle-mode candidate always exists. */ + if (wf.duty_length_ns > requested->duty_length_ns || + wf.duty_offset_ns > requested->duty_offset_ns) + return; + + if (!rounding->have_best) { + better = true; + } else if (wf.period_length_ns != best->period_length_ns) { + /* Prefer the longest period below the request, else the shortest. */ + if (wf.period_length_ns > requested->period_length_ns) + better = wf.period_length_ns < best->period_length_ns; + else + better = best->period_length_ns > requested->period_length_ns || + wf.period_length_ns > best->period_length_ns; + } else if (wf.duty_length_ns != best->duty_length_ns) { + better = wf.duty_length_ns > best->duty_length_ns; + } else if (wf.duty_offset_ns != best->duty_offset_ns) { + better = wf.duty_offset_ns > best->duty_offset_ns; + } else { + /* Prefer the current pair rate when waveforms are equivalent. */ + better = candidate->pair_rate == rounding->current_pair_rate && + rounding->best.pair_rate != rounding->current_pair_rate; + } + if (!better) + return; + + rounding->best = *candidate; + rounding->best_wf = wf; + rounding->have_best = true; +} + +static struct sun8i_pwm_waveform +sun8i_pwm_round_cycle(const struct pwm_waveform *requested, u32 pair_rate, + u16 div_k) +{ + struct sun8i_pwm_waveform candidate = { + .enabled = true, + .pair_rate = pair_rate, + .pcr = FIELD_PREP(SUN8I_PWM_PCR_PRESCAL_K_MASK, div_k - 1), + }; + u64 denominator = NSEC_PER_SEC * (u64)div_k; + u64 duty_ticks, period_ticks; + u32 inactive_ticks; + + /* Floor division rounds down, except for the minimum one-tick period. */ + period_ticks = mul_u64_u64_div_u64(requested->period_length_ns, + pair_rate, denominator); + period_ticks = clamp_t(u64, period_ticks, 1, SUN8I_PWM_PPR_PERIOD_MAX); + duty_ticks = mul_u64_u64_div_u64(requested->duty_length_ns, + pair_rate, denominator); + duty_ticks = min(duty_ticks, period_ticks); + + /* + * Zero active ticks encode either constant level. For a toggling + * waveform, active-low mode places the rising edge after the inactive + * part, which is the only non-zero offset supported by the hardware. + */ + if (!duty_ticks) { + candidate.pcr |= SUN8I_PWM_PCR_ACTIVE_STATE; + } else if (duty_ticks == period_ticks) { + duty_ticks = 0; + } else { + inactive_ticks = period_ticks - duty_ticks; + if (sun8i_pwm_ticks_to_ns(inactive_ticks, div_k, pair_rate) <= + requested->duty_offset_ns) + duty_ticks = inactive_ticks; + else + candidate.pcr |= SUN8I_PWM_PCR_ACTIVE_STATE; + } + + candidate.ppr = SUN8I_PWM_PPR_PERIOD(period_ticks) | + SUN8I_PWM_PPR_DUTY(duty_ticks); + + return candidate; +} + +static void +sun8i_pwm_consider_pair_rate(struct sun8i_pwm_rounding *rounding, u32 pair_rate) +{ + struct sun8i_pwm_waveform candidate; + + for (unsigned int div_k = 1; div_k <= 256; div_k++) { + candidate = sun8i_pwm_round_cycle(rounding->requested, pair_rate, div_k); + sun8i_pwm_choose_waveform(rounding, &candidate); + } + + candidate = (struct sun8i_pwm_waveform) { + .pair_rate = pair_rate, + .enabled = true, + .bypass_en = true, + }; + sun8i_pwm_choose_waveform(rounding, &candidate); +} + +static int sun8i_pwm_round_waveform_tohw(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_waveform *wf, + void *_wfhw) +{ + struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); + struct sun8i_pwm_pair *pair = + &sun8i_chip->pairs[SUN8I_PWM_PAIR_IDX(pwm->hwpwm)]; + struct sun8i_pwm_waveform *wfhw = _wfhw; + struct sun8i_pwm_rounding rounding = { + .requested = wf, + }; + unsigned long current_rate; + + if (!wf->period_length_ns) { + *wfhw = (struct sun8i_pwm_waveform) { + .enabled = false, + }; + return 0; + } + + current_rate = sun8i_pwm_get_pair_rate(sun8i_chip, pwm->hwpwm); + if (!current_rate || current_rate > U32_MAX) + return -ERANGE; + rounding.current_pair_rate = current_rate; + + if (sun8i_pwm_pair_rate_constrained(sun8i_chip, pwm->hwpwm)) { + sun8i_pwm_consider_pair_rate(&rounding, current_rate); + } else { + for (unsigned int parent_idx = 0; + parent_idx < clk_hw_get_num_parents(pair->hw); + parent_idx++) { + struct clk_hw *parent; + unsigned long parent_rate; + + parent = clk_hw_get_parent_by_index(pair->hw, parent_idx); + if (!parent) + continue; + parent_rate = clk_hw_get_rate(parent); + if (!parent_rate) + continue; + + for (unsigned int i = 0; sun8i_pwm_div_m_table[i].div; + i++) { + u16 div_m = sun8i_pwm_div_m_table[i].div; + u64 pair_rate; + + pair_rate = DIV_ROUND_UP_ULL(parent_rate, div_m); + if (!pair_rate || pair_rate > U32_MAX) + continue; + sun8i_pwm_consider_pair_rate(&rounding, pair_rate); + } + } + } + + if (!rounding.have_best) + return -EINVAL; + *wfhw = rounding.best; + + dev_dbg(pwmchip_parent(chip), + "pwm#%u: %llu/%llu [+%llu] -> pair-rate=%u, pcr=%#x, ppr=%#x, bypass=%u\n", + pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, + wf->duty_offset_ns, wfhw->pair_rate, wfhw->pcr, wfhw->ppr, + wfhw->bypass_en); + + return rounding.best_wf.period_length_ns > wf->period_length_ns; +} + +static const struct pwm_ops sun8i_pwm_ops = { + .request = sun8i_pwm_request, + .free = sun8i_pwm_free, + .sizeof_wfhw = sizeof(struct sun8i_pwm_waveform), + .round_waveform_tohw = sun8i_pwm_round_waveform_tohw, + .round_waveform_fromhw = sun8i_pwm_round_waveform_fromhw, + .read_waveform = sun8i_pwm_read_waveform, + .write_waveform = sun8i_pwm_write_waveform, +}; + +/* Register the shared mux, gate and /div_m clock for each channel pair. */ +static int sun8i_pwm_register_pair_clocks(struct device *dev, + struct sun8i_pwm_chip *sun8i_chip) +{ + static const struct clk_parent_data parent_data[] = { + { .index = 0 }, + { .index = 1 }, + }; + + for (unsigned int i = 0; i < SUN8I_PWM_NPAIRS; i++) { + struct sun8i_pwm_pair *pair = &sun8i_chip->pairs[i]; + void __iomem *reg = sun8i_chip->base + SUN8I_PWM_PCCR(i); + const char *name; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, + "%s#pwm-clk-src%u%u", dev_name(dev), + i * 2, i * 2 + 1); + if (!name) + return -ENOMEM; + + pair->mux.reg = reg; + pair->mux.shift = SUN8I_PWM_PCCR_SRC_SHIFT; + pair->mux.mask = SUN8I_PWM_PCCR_SRC_MASK; + pair->mux.flags = CLK_MUX_ROUND_CLOSEST; + pair->mux.lock = &sun8i_chip->lock; + + pair->chip = sun8i_chip; + pair->index = i; + + pair->divider.reg = reg; + pair->divider.shift = SUN8I_PWM_PCCR_DIV_M_SHIFT; + pair->divider.width = SUN8I_PWM_PCCR_DIV_M_WIDTH; + pair->divider.table = sun8i_pwm_div_m_table; + pair->divider.lock = &sun8i_chip->lock; + + pair->hw = devm_clk_hw_register_composite_pdata(dev, name, + parent_data, + ARRAY_SIZE(parent_data), + &pair->mux.hw, &clk_mux_ops, + &pair->divider.hw, &clk_divider_ops, + &pair->gate_hw, &sun8i_pwm_pair_gate_ops, 0); + if (IS_ERR(pair->hw)) + return dev_err_probe(dev, PTR_ERR(pair->hw), + "Failed to register pair %u clock\n", i); + + /* Borrow CCF's handle so registration cannot pin this module. */ + pair->rate_nb.notifier_call = sun8i_pwm_pair_rate_notifier; + ret = devm_clk_notifier_register(dev, pair->hw->clk, &pair->rate_nb); + if (ret) + return dev_err_probe(dev, ret, + "Failed to protect pair %u clock rate\n", i); + } + + return 0; +} + +/* + * A disabled pair gate makes any set PER bits ineffective. Clear those stale + * enables before a clock consumer can turn the shared gate on and expose an + * unrequested output. + */ +static void +sun8i_pwm_sanitize_disabled_pairs(struct sun8i_pwm_chip *sun8i_chip) +{ + u32 per; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); + for (unsigned int pair = 0; pair < SUN8I_PWM_NPAIRS; pair++) { + unsigned int first = pair * 2; + u32 pccr; + + pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); + if (pccr & SUN8I_PWM_PCCR_GATE) + continue; + + per &= ~sun8i_pwm_pair_enable_mask(pair); + pccr &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(first)); + pccr &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(first + 1)); + sun8i_pwm_writel(sun8i_chip, pccr, SUN8I_PWM_PCCR(pair)); + } + sun8i_pwm_writel(sun8i_chip, per, SUN8I_PWM_PER); +} + +static int sun8i_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sun8i_pwm_chip *sun8i_chip; + struct reset_control *rst; + struct pwm_chip *chip; + int ret; + + chip = devm_pwmchip_alloc(dev, SUN8I_PWM_NPWM, sizeof(*sun8i_chip)); + if (IS_ERR(chip)) + return dev_err_probe(dev, PTR_ERR(chip), + "Failed to allocate pwmchip\n"); + + sun8i_chip = sun8i_pwm_from_chip(chip); + spin_lock_init(&sun8i_chip->lock); + sun8i_chip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(sun8i_chip->base)) + return dev_err_probe(dev, PTR_ERR(sun8i_chip->base), + "Failed to get PWM base address\n"); + + sun8i_chip->bus_clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(sun8i_chip->bus_clk)) + return dev_err_probe(dev, PTR_ERR(sun8i_chip->bus_clk), + "Failed to get bus clock\n"); + + rst = devm_reset_control_get_shared_deasserted(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), + "Failed to get reset control\n"); + + chip->ops = &sun8i_pwm_ops; + + sun8i_pwm_sanitize_disabled_pairs(sun8i_chip); + + ret = sun8i_pwm_register_pair_clocks(dev, sun8i_chip); + if (ret) + return ret; + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + + return 0; +} + +static const struct of_device_id sun8i_pwm_dt_ids[] = { + { + .compatible = "allwinner,sun50i-h616-pwm", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, sun8i_pwm_dt_ids); + +static struct platform_driver sun8i_pwm_driver = { + .driver = { + .name = "sun8i-pwm", + .of_match_table = sun8i_pwm_dt_ids, + }, + .probe = sun8i_pwm_probe, +}; +module_platform_driver(sun8i_pwm_driver); + +MODULE_AUTHOR("Richard Genoud "); +MODULE_DESCRIPTION("Allwinner sun8i PWM driver"); +MODULE_LICENSE("GPL"); From patchwork Tue Sep 22 00:06:32 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3407 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 6379E1C36E5 for ; Tue, 22 Sep 2026 02:08:23 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26105-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26105-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 2EC41628EE for ; Tue, 22 Sep 2026 00:06:53 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DBF8F211466; Tue, 22 Sep 2026 00:06:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="j4Fgy7eA" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oa2-f12.google.com (mail-oa2-f12.google.com [74.125.231.76]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B13F91E1DF0 for ; Tue, 22 Sep 2026 00:06:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.76 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035612; cv=none; b=VaDOPW4AjvNiFb8GIetYb5MCnZpr6AyQ2O9pZpGl2zX7W3MvYBkNcR/mj1kZLcsI0RJa96e46+q5XUwm/uJ2GUpCUXlm+Z1fic6noomfYBfeRXh8U4RzprCnoDg57V7rmLjWpZF4m0fxmKbJXn4ZcNxDrvTxr8QzDZYPc/xdYnI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035612; c=relaxed/simple; bh=AwEdnSqwNldsU46K3L8s8kozlkZ4yxHPkhkqHOBcOAk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=donoPdW1W43XjxwOM1haMZ7QKAZfJ9uRengFwmnDWW271m4wHnELgAhFlgUHqml77EM5/HbWgOHLdTw894qhIOnXlbabHHJ/4bb2C9X5lBPBU6xaJol/ion1r3y2dsRJVBHvCtfSrNtEmgygXytyRbZ9nNIedVny86bSGl4nUls= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=j4Fgy7eA; arc=none smtp.client-ip=74.125.231.76 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oa2-f12.google.com with SMTP id 586e51a60fabf-466ccdd7695so1244114fac.1 for ; Mon, 21 Sep 2026 17:06:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790035607; x=1790640407; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=1mzlKbz8Phj+ip4hm3yiFbRMiYi293gbL25g7vO3mK8=; b=j4Fgy7eAx7P7y8ZaZbNwUoO0cG9Je8JqE82B/yCzryxa1z2WKy4kHjNbIAk7YxvO22 N4iLw7+gWntYcU9mjO29MeIKOpLeZH7CGD5jzoOmFS6c66G3+E4mREvQ1cBNUyVhKgeO +HIZ7WMwbIUkYzH1SUtQUVt9xNM+nCWT+tFk4o5Li3+TI4dU5WVyyiZGePSnf06CX85Y 7uzDMdlxEzA6GxUS23507lt+la+pCqgjgWNu+fMFzhUQnGaXaYTFRMH2d196tSLn0Th1 nMnyhP2aDfpiLUB8Q4KG1ItO5tQ5ozFw6SgLm0HPKInVkWYCt5CFsKxhPIHbSRf7SacQ 2ZCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790035607; x=1790640407; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=1mzlKbz8Phj+ip4hm3yiFbRMiYi293gbL25g7vO3mK8=; b=Rg4P3wSVogQx+aLnfSsyM56jgMNClrjTSSqGo9Q8WX6UA1+6W6OxrxOBVKmQ1jq4gm UM81mUPf7g4pnknXCdh1iahUp0k3aXgXlxuejCNGXqn0kn8HzE8pbgFBIxvaL9wLB2wd 2q6cfuqw+R1kK+ioOJsZXF++YMLPudyywg4ByAGkFTOGqgRkFhpdM4gG1XD7vqsms5zE kcsgGEzgBfV+dIbGIhkXIBfIT/ak+2ikn/PtF/uWS4lFYqzgfjfA4B4eXVZnqLZCTPsw qqOAFhH7jWHaqoEIz4747jH32RmeJJN6YHwdhUVUmhTI8mriOp5rWVwsktFaPJWDnD/v tgDA== X-Forwarded-Encrypted: i=1; AKwUvBzci3uP8mk90ZOL0L9jCcHp9pqpahmBTZSZUmX7YIC0Gluszc44dQefrE4qW1L9sGCcqkxwrGOgEjNISw==@lists.linux.dev X-Gm-Message-State: AFuF++mrDH5IrKHrqMM/bpOJe1kobKTgR0coRkv1HzrugfXvB0XiZiaw a9ZZf4BNqdQxWyEXEPt1P0KykZd8xlw57PA9du1b0ohK8hUppt8znIZ2 X-Gm-Gg: AYBFou2QsXPeZop1HLARbVVUoa8kKcMkf1eaQKMiBXUR6+BiUE7TO3PUSbocsJ8aejT EHSjp56LJUI6Uvqi/CzWr39ZSIoacBOpFzkiJTF+QVzYJLzQSXA78TAZR+oBHz30x5WTZqo8Fim RNG2fLEq4DrVVg7oODHh1yUXA9weIgp2f2fbIvNhvVSbkcW/EOOuc+2yvYWW4Q32KiHdmdd8jZK rVxjaGM+vEN2uK8Q9xQz1WZR+8ItaWhpD/u2+GyA7F8GCgXPMN47nBAWQLnsYtJ0BfLXaYF3iqE PiWCV2qDe7r/B2suiybGY4GuOlQ4sLW0RgUzcNk+iAy4Xd0hBvghCc4SGjlR+UHeRD63AR4NypU EGUh3YrnH22ccqKHy1V39rncuiw6zRsKnosgIPwc/jRj6vdXVBt46sC7wxjS9/VBdQcJ5+A/oKb CmlyGD3ZGs9esNt4Bog2u6MMPKfx2SGdauvrNOrlo69YLB3sm/tUUD8hKwG8rCSCk8BVY+HnQdm PjsUR7+9eekTYAv6iNgB2bKAs45EoD83biBBLDlVi6e87xe0MxQ3M+9oNd1fBuc7Xczdoe8OFhR KvblcOsfktiHPX49fPwAbbWIvPakHT17Yf6/NTH7ON7AGgOHLJAx5iMer0FRX9zVkVRZXlRQ2YU H4Knuf4cGiY5IOQ58tmi1trNkr+/3+G0= X-Received: by 2002:a05:6870:14d4:b0:47f:b0ff:8383 with SMTP id 586e51a60fabf-486e4df7aa7mr12381394fac.1.1790035607564; Mon, 21 Sep 2026 17:06:47 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-48f7280cf14sm801683fac.13.2026.09.21.17.06.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 17:06:46 -0700 (PDT) From: James Hilliard Date: Mon, 21 Sep 2026 18:06:32 -0600 Subject: [PATCH v9 3/5] pwm: sun8i: export the bypass outputs as clocks Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260921-h616-pwm-v8-v9-3-37bec6201ff5@gmail.com> References: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> In-Reply-To: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Maxime Ripard , Richard Genoud , Philipp Zabel , Michael Turquette , Stephen Boyd , Brian Masney Cc: linux-pwm@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, Paul Kocialkowski , Thomas Petazzoni , John Stultz , Joao Schim , bigunclemax@gmail.com, linux-clk@vger.kernel.org, James Hilliard X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-1.16 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; BAD_REP_POLICIES(0.10)[]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; FORGED_SENDER_MAILLIST(0.00)[]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; FROM_HAS_DN(0.00)[]; RCPT_COUNT_TWELVE(0.00)[25]; PRECEDENCE_BULK(0.00)[]; FREEMAIL_CC(0.00)[vger.kernel.org,lists.infradead.org,lists.linux.dev,sys-base.io,bootlin.com,google.com,schimsalabim.eu,gmail.com]; TAGGED_RCPT(0.00)[dt]; DBL_BLOCKED_OPENRESOLVER(0.00)[bootlin.com:email,sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo]; RCVD_COUNT_FIVE(0.00)[6]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10:c]; FREEMAIL_FROM(0.00)[gmail.com]; TO_DN_SOME(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[172.234.253.10:from]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[174.29.1.49:received,74.125.231.76:received,100.90.174.1:received]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; FREEMAIL_TO(0.00)[kernel.org,gmail.com,sholland.org,bootlin.com,pengutronix.de,baylibre.com,redhat.com]; MIME_TRACE(0.00)[0:+]; TAGGED_FROM(0.00)[bounces-26105-noreply=patchwork.local]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; MID_RHS_MATCH_FROM(0.00)[]; RCVD_TLS_LAST(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: 6379E1C36E5 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= From: Richard Genoud Expose each H616 PWM channel's hardware bypass as a CCF clock. Consumers such as the co-packaged AC200/AC300 PHY need a clock rather than PWM duty or polarity control. Keep the pair source protected for the complete prepared lifetime, and arbitrate ownership between PWM requests and clock prepare so both interfaces cannot drive the same output. drivers/clk/clk-pwm.c advertises a fixed frequency without reading back the rounded PWM rate. A 42 ns, 21 ns request does select 24 MHz bypass when it is available. However, with the pair constrained to 100 MHz by its sibling, the PWM rounding rules select a 40 ns, 20 ns waveform (25 MHz), while a pwm-clock configured for 24 MHz still reports 24 MHz. The direct provider instead reports the actual pair rate and makes its shared-rate protection visible to CCF. Consumers must still check the rate they receive. Use CLK_IGNORE_UNUSED to preserve unclaimed firmware bypass outputs, just as the PWM path preserves inherited waveforms. Normal consumer disable still turns the output off and releases its shared gate reference. Signed-off-by: Richard Genoud Co-developed-by: James Hilliard Signed-off-by: James Hilliard --- drivers/pwm/Kconfig | 3 +- drivers/pwm/pwm-sun8i.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 180 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index d87d166e7836..d3c35a11859f 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -754,7 +754,8 @@ config PWM_SUN8I depends on HAS_IOMEM && COMMON_CLK help PWM framework driver for Allwinner controllers whose channels share - paired source clocks. + paired source clocks. In addition to PWM operation, each channel's + hardware bypass path can be exported as a clock output. To compile this driver as a module, choose M here: the module will be called pwm-sun8i. diff --git a/drivers/pwm/pwm-sun8i.c b/drivers/pwm/pwm-sun8i.c index 7e6c39815c18..fd81c5f5e326 100644 --- a/drivers/pwm/pwm-sun8i.c +++ b/drivers/pwm/pwm-sun8i.c @@ -113,6 +113,9 @@ * The bypass in PWM mode is used to achieve a 1/2 relative duty cycle with the * fastest clock. * + * SUN8I_PWM_clock_x/y serve for the PWM purpose. + * SUN8I_PWM_bypass_clock_x/y serve for the clock-provider purpose. + * */ /* /div_m is a power-of-two divider limited to /256. */ @@ -129,6 +132,12 @@ static const struct clk_div_table sun8i_pwm_div_m_table[] = { { /* sentinel */ } }; +enum sun8i_pwm_mode { + SUN8I_PWM_MODE_NONE, + SUN8I_PWM_MODE_PWM, + SUN8I_PWM_MODE_CLK, +}; + struct sun8i_pwm_chip; struct sun8i_pwm_pair { @@ -142,8 +151,12 @@ struct sun8i_pwm_pair { }; struct sun8i_pwm_channel { + struct clk_hw bypass_hw; + struct sun8i_pwm_chip *chip; + unsigned int index; /* Separate CCF consumer, held only from PWM request to free. */ struct clk *pair_clk; + enum sun8i_pwm_mode mode; u64 pending_period_ns; bool rate_exclusive; }; @@ -153,7 +166,7 @@ struct sun8i_pwm_chip { struct sun8i_pwm_channel channels[SUN8I_PWM_NPWM]; struct clk *bus_clk; void __iomem *base; - /* Protects shared registers and rate_exclusive. */ + /* Protects shared registers, channel ownership and rate_exclusive. */ spinlock_t lock; }; @@ -317,6 +330,101 @@ static int sun8i_pwm_pair_rate_notifier(struct notifier_block *nb, return active ? NOTIFY_BAD : NOTIFY_OK; } +static inline struct sun8i_pwm_channel * +sun8i_pwm_channel_from_hw(struct clk_hw *hw) +{ + return container_of(hw, struct sun8i_pwm_channel, bypass_hw); +} + +static int sun8i_pwm_bypass_prepare(struct clk_hw *hw) +{ + struct sun8i_pwm_channel *chan = sun8i_pwm_channel_from_hw(hw); + struct sun8i_pwm_chip *sun8i_chip = chan->chip; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + if (chan->mode != SUN8I_PWM_MODE_NONE) + return -EBUSY; + + chan->mode = SUN8I_PWM_MODE_CLK; + return 0; +} + +static void sun8i_pwm_bypass_unprepare(struct clk_hw *hw) +{ + struct sun8i_pwm_channel *chan = sun8i_pwm_channel_from_hw(hw); + struct sun8i_pwm_chip *sun8i_chip = chan->chip; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + if (chan->mode != SUN8I_PWM_MODE_CLK) + return; + + chan->mode = SUN8I_PWM_MODE_NONE; +} + +static int sun8i_pwm_bypass_enable(struct clk_hw *hw) +{ + struct sun8i_pwm_channel *chan = sun8i_pwm_channel_from_hw(hw); + struct sun8i_pwm_chip *sun8i_chip = chan->chip; + unsigned int pair = SUN8I_PWM_PAIR_IDX(chan->index); + u32 pccr, per; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + if (chan->mode != SUN8I_PWM_MODE_CLK) + return -EBUSY; + + pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); + per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); + if (!(pccr & BIT(SUN8I_PWM_PCCR_BYPASS_BIT(chan->index))) || + !(per & SUN8I_PWM_ENABLE(chan->index))) { + sun8i_pwm_set_enabled_locked(sun8i_chip, chan->index, false); + sun8i_pwm_set_bypass_locked(sun8i_chip, chan->index, true); + sun8i_pwm_set_enabled_locked(sun8i_chip, chan->index, true); + } + + return 0; +} + +static void sun8i_pwm_bypass_disable(struct clk_hw *hw) +{ + struct sun8i_pwm_channel *chan = sun8i_pwm_channel_from_hw(hw); + struct sun8i_pwm_chip *sun8i_chip = chan->chip; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + if (chan->mode != SUN8I_PWM_MODE_CLK) + return; + + sun8i_pwm_set_enabled_locked(sun8i_chip, chan->index, false); + sun8i_pwm_set_bypass_locked(sun8i_chip, chan->index, false); +} + +static int sun8i_pwm_bypass_is_enabled(struct clk_hw *hw) +{ + struct sun8i_pwm_channel *chan = sun8i_pwm_channel_from_hw(hw); + struct sun8i_pwm_chip *sun8i_chip = chan->chip; + unsigned int pair = SUN8I_PWM_PAIR_IDX(chan->index); + bool enabled; + u32 val; + + guard(spinlock_irqsave)(&sun8i_chip->lock); + + val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); + enabled = (val & SUN8I_PWM_PCCR_GATE) && + (val & BIT(SUN8I_PWM_PCCR_BYPASS_BIT(chan->index))); + + val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); + enabled = enabled && (val & SUN8I_PWM_ENABLE(chan->index)); + + return enabled; +} + +static const struct clk_ops sun8i_pwm_bypass_ops = { + .prepare = sun8i_pwm_bypass_prepare, + .unprepare = sun8i_pwm_bypass_unprepare, + .enable = sun8i_pwm_bypass_enable, + .disable = sun8i_pwm_bypass_disable, + .is_enabled = sun8i_pwm_bypass_is_enabled, +}; + static void sun8i_pwm_put_rate(struct sun8i_pwm_chip *sun8i_chip, unsigned int idx) { @@ -342,8 +450,12 @@ static int sun8i_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) int ret; scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + if (chan->mode != SUN8I_PWM_MODE_NONE) + return -EBUSY; + was_enabled = sun8i_pwm_channel_is_enabled_locked(sun8i_chip, idx); + chan->mode = SUN8I_PWM_MODE_PWM; } chan->pair_clk = clk_hw_get_clk(parent, NULL); @@ -372,6 +484,8 @@ static int sun8i_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) clk_put(chan->pair_clk); err_clear_clock: chan->pair_clk = NULL; + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) + chan->mode = SUN8I_PWM_MODE_NONE; return ret; } @@ -382,6 +496,9 @@ static void sun8i_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; scoped_guard(spinlock_irqsave, &sun8i_chip->lock) { + if (chan->mode != SUN8I_PWM_MODE_PWM) + return; + sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, false); sun8i_pwm_set_bypass_locked(sun8i_chip, pwm->hwpwm, false); } @@ -390,6 +507,9 @@ static void sun8i_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable_unprepare(chan->pair_clk); clk_put(chan->pair_clk); chan->pair_clk = NULL; + + scoped_guard(spinlock_irqsave, &sun8i_chip->lock) + chan->mode = SUN8I_PWM_MODE_NONE; } static int sun8i_pwm_read_waveform(struct pwm_chip *chip, @@ -510,7 +630,8 @@ sun8i_pwm_pair_rate_constrained(struct sun8i_pwm_chip *sun8i_chip, unsigned int sibling = idx ^ 1; guard(spinlock_irqsave)(&sun8i_chip->lock); - return sun8i_chip->channels[sibling].rate_exclusive || + return sun8i_chip->channels[sibling].mode == SUN8I_PWM_MODE_CLK || + sun8i_chip->channels[sibling].rate_exclusive || sun8i_pwm_channel_is_enabled_locked(sun8i_chip, sibling); } @@ -984,6 +1105,42 @@ static int sun8i_pwm_register_pair_clocks(struct device *dev, return 0; } +/* Register the bypass clock for each channel. */ +static int sun8i_pwm_register_bypass_clocks(struct device *dev, + struct sun8i_pwm_chip *sun8i_chip) +{ + for (unsigned int i = 0; i < SUN8I_PWM_NPWM; i++) { + struct sun8i_pwm_channel *chan = &sun8i_chip->channels[i]; + struct clk_hw *parent = sun8i_chip->pairs[SUN8I_PWM_PAIR_IDX(i)].hw; + const char *name; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s#pwm-bypass%u", + dev_name(dev), i); + if (!name) + return -ENOMEM; + + chan->chip = sun8i_chip; + chan->index = i; + /* + * Protect the shared rate while prepared. Preserve unclaimed + * firmware outputs through CCF's unused-clock cleanup, as for + * PWM waveforms; only a consumer may turn them off. + */ + chan->bypass_hw.init = + CLK_HW_INIT_HW(name, parent, &sun8i_pwm_bypass_ops, + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE | + CLK_IGNORE_UNUSED); + + ret = devm_clk_hw_register(dev, &chan->bypass_hw); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register bypass clock %u\n", i); + } + + return 0; +} + /* * A disabled pair gate makes any set PER bits ineffective. Clear those stale * enables before a clock consumer can turn the shared gate on and expose an @@ -1012,6 +1169,17 @@ sun8i_pwm_sanitize_disabled_pairs(struct sun8i_pwm_chip *sun8i_chip) sun8i_pwm_writel(sun8i_chip, per, SUN8I_PWM_PER); } +static struct clk_hw *sun8i_pwm_get_clk_hw(struct of_phandle_args *clkspec, + void *data) +{ + struct sun8i_pwm_chip *sun8i_chip = data; + + if (clkspec->args[0] >= SUN8I_PWM_NPWM) + return ERR_PTR(-EINVAL); + + return &sun8i_chip->channels[clkspec->args[0]].bypass_hw; +} + static int sun8i_pwm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1050,6 +1218,14 @@ static int sun8i_pwm_probe(struct platform_device *pdev) if (ret) return ret; + ret = sun8i_pwm_register_bypass_clocks(dev, sun8i_chip); + if (ret) + return ret; + + ret = devm_of_clk_add_hw_provider(dev, sun8i_pwm_get_clk_hw, sun8i_chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to add HW clock provider\n"); + ret = devm_pwmchip_add(dev, chip); if (ret < 0) return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); From patchwork Tue Sep 22 00:06:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3406 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 6F4F81C1FE8 for ; Tue, 22 Sep 2026 02:08:21 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26104-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26104-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id F1B4B627B7 for ; Tue, 22 Sep 2026 00:06:52 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CCC591C5D72; Tue, 22 Sep 2026 00:06:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="MZbMAFjG" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oa2-f12.google.com (mail-oa2-f12.google.com [74.125.231.76]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6C9E02192F9 for ; Tue, 22 Sep 2026 00:06:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.76 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035612; cv=none; b=D4djGkHACkXDGBniW5pWyI6l74d/iKrqtSu8cyXQEcJUkdzh1FSKekPiB0Sf1ZR2oJ07yxTPjOoWCswGiq34vo5/Q9PjTznNnOTpstswoGNpZFvZBtR+LbANvmklW58jaM0sZRSw9x5aDtAhLldjo6ltjl/XBoIhce9R/3D6Qrg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035612; c=relaxed/simple; bh=cxjDKZ0MA9VUms6JcpknLJlKdT7ZoJ5aaylgo7tCtYM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CBykBcYe0Xo1Fjam25wx8NAObOMyT/LkpZyuKeEXuNwAi0UY6xZETinUlaqnD/adcq5ZWa/SssDBidjusjZJhAXKAePtUYMNoP/Spm1KqpibXnKlFHzzY8cMgz+mt6DhQdY2rWqcU8Zq3YbBHX/MzqyrQ+cxJQCWcVLE8GrvWI4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=MZbMAFjG; arc=none smtp.client-ip=74.125.231.76 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oa2-f12.google.com with SMTP id 586e51a60fabf-482620dc91bso2566540fac.2 for ; Mon, 21 Sep 2026 17:06:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790035609; x=1790640409; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=FHj476tA2byKc8arhZaw8Ci81fHtdMiGqz0hVZIDFng=; b=MZbMAFjG/wTH5OInIuARwFZGpfFozn0ZDaUl+YzeJm5pBFJJ37MisO2WTqmpETqCDf HX9MchjSSeXQc2/qt19Nm3gjtiZxlyBuGva3GTZLK26oSD+hsPMLi4Kq/ZOdNQnyHHPM VomZX5GrqV5qTSzwSSTRXu2eYWaQyWLxj65cbtlGSNzAtuXYYVvbE1I69Rz655lB9Yqd EboutFx8IOScM+cZDVgMACMAKT4Xby1PaQ4XZb0UjMGA5leveKN5LR/k1aluVUM2uxUh 6BF8N6TN45MMBo4iiTPahyoN52Rjj81D8sVKJ9C8UVtMBKK+EZsgc3gg/DLR+Fl+i8fA DM4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790035609; x=1790640409; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=FHj476tA2byKc8arhZaw8Ci81fHtdMiGqz0hVZIDFng=; b=iCt2MUCItvWl5x4IMDV5uZuwgGU87qWmqhD80WM8IG/6GLm7Bq2MPxhv69QzKQ2WBA rEuGFgIJUjvFaxA5PKxOQOSivA/WEEnLYCMpXtECBhNIdpe0+F81jpQpZnidjracOJBU 6GWJN0naW2OsPDdbVV7YdulNMV3hCvjrJiHmlqUKHKrlyhuykc0Lea1HItLjdREXt8sE tdLHmO6rp7kuuCAXOdx24U7wTEnT3Mteeg9KWQt14ppQMvsBUJ7QTcj/jF/rxONw/t+G Lw/PA7/qcmjnTyKoGauKPlwV+GYhhMFFf9bNLWLGAUi6/7IKKy/iO9mgH2xinEwnSr/r FC2g== X-Forwarded-Encrypted: i=1; AKwUvBwXa80xjB57RE2k7jm6kL+DiFHYwtvi9tPD+wyXz18RsQeHSnAaehmLmNfQsZmtnlXPjHVt+VfjKHUn5g==@lists.linux.dev X-Gm-Message-State: AFuF++lIWxJLnQTkVT9D3QHYoSABIb9heVsgep5aZ4CUCiq1myvqrLpV OBrIaavfHEBNc0AgXrpJqvxQkMSzUM40KktwXxAxIkP6GyeCfKynfMF9 X-Gm-Gg: AYBFou0xWS9QB4/7wuWAGcRJJb4/SPg96IRZj5XW2DGlBLEHBHJ4h4yvergNXUR9FDh lquZDnixAOJwjUEbmFQYKdy8D7V5iPA1fhPbGytwSSgs313WeevmjiCzF4tAe4Co5wKFsFKGZuC KrS8/1wAez9AexiWu4O27tjA6G0xSH+OYPNkTQ5DPwRfZloY3B1u6ZoecoWh1Fz/vGNo3bkobyR oeBVPLxGvMTfPaAbEO5cp3jlrww5tmGkWWbjZN+B5YLqgCplL8DOKicylG/teDZDtEy/uzxCeMN 7L/IOAix0JBSrMjsSzcTo7d48QHSA1fDWzGMJahmY9AvUsfji5HCcOhPNvbMLyQvj8/yHH7voBD TjQWjaGjhJHtCmuAqJgcfCG9wvJFhPPSlgMsLfXbP25oIl5V/GaCKZewrknk6xNRi67IKmAkzGw IbJpN09CCk9AHlO+Y2iuuaRaHbd0WwQinOkfEdY0gp3IrtfoglKczdprP75JhMn7BWsRIP3NjT3 MB8/2ODlImmAFGm7xuosDTFAM2hXpWOulER9yHW+fybBaF+RL7IPAkj6C+utwXk8iyab/ICOTyg D5k+ydv6qtRa9BWUijZFj9Y9EKsbA/jkfyQJM8f9vEXtvh+a6p+Xiudgf7zHClQ1WbK4GiAn5aG 5YIUJ+nCzvbYW9b0gxgN1 X-Received: by 2002:a05:6870:21e4:b0:464:c314:ddc2 with SMTP id 586e51a60fabf-486e4bbce38mr12349868fac.8.1790035609185; Mon, 21 Sep 2026 17:06:49 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-48f7280cf14sm801683fac.13.2026.09.21.17.06.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 17:06:48 -0700 (PDT) From: James Hilliard Date: Mon, 21 Sep 2026 18:06:33 -0600 Subject: [PATCH v9 4/5] arm64: dts: allwinner: h616: add PWM controller Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260921-h616-pwm-v8-v9-4-37bec6201ff5@gmail.com> References: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> In-Reply-To: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Maxime Ripard , Richard Genoud , Philipp Zabel , Michael Turquette , Stephen Boyd , Brian Masney Cc: linux-pwm@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, Paul Kocialkowski , Thomas Petazzoni , John Stultz , Joao Schim , bigunclemax@gmail.com, linux-clk@vger.kernel.org, James Hilliard X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [4.34 / 15.00]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; BAD_REP_POLICIES(0.10)[]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; TAGGED_RCPT(0.00)[dt]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; PRECEDENCE_BULK(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; RCPT_COUNT_TWELVE(0.00)[25]; FREEMAIL_CC(0.00)[vger.kernel.org,lists.infradead.org,lists.linux.dev,sys-base.io,bootlin.com,google.com,schimsalabim.eu,gmail.com]; DBL_BLOCKED_OPENRESOLVER(0.00)[bootlin.com:email,sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo]; FORGED_SENDER_MAILLIST(0.00)[]; FROM_HAS_DN(0.00)[]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; FREEMAIL_FROM(0.00)[gmail.com]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10]; TO_DN_SOME(0.00)[]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[174.29.1.49:received,74.125.231.76:received,100.90.174.1:received]; RCVD_COUNT_FIVE(0.00)[6]; FREEMAIL_TO(0.00)[kernel.org,gmail.com,sholland.org,bootlin.com,pengutronix.de,baylibre.com,redhat.com]; MIME_TRACE(0.00)[0:+]; TAGGED_FROM(0.00)[bounces-26104-noreply=patchwork.local]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; MID_RHS_MATCH_FROM(0.00)[]; RCVD_TLS_LAST(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[172.234.253.10:from] X-Rspamd-Queue-Id: 6F4F81C1FE8 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= From: Richard Genoud The H616 has a PWM controller that can provide PWM signals, but also plain clocks. Add the PWM controller node and pins in the device tree. Signed-off-by: Richard Genoud Signed-off-by: James Hilliard --- arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi index 1598e86259ab..90fe56a1d3cd 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi @@ -281,6 +281,17 @@ watchdog: watchdog@30090a0 { clocks = <&osc24M>; }; + pwm: pwm@300a000 { + compatible = "allwinner,sun50i-h616-pwm"; + reg = <0x0300a000 0x400>; + clocks = <&osc24M>, <&ccu CLK_BUS_PWM>; + clock-names = "mod", "bus"; + resets = <&ccu RST_BUS_PWM>; + #pwm-cells = <3>; + #clock-cells = <1>; + status = "disabled"; + }; + pio: pinctrl@300b000 { compatible = "allwinner,sun50i-h616-pinctrl"; reg = <0x0300b000 0x400>; @@ -385,6 +396,42 @@ nand_rb1_pin: nand-rb1-pin { bias-pull-up; }; + /omit-if-no-ref/ + pwm0_pin: pwm0-pin { + pins = "PD28"; + function = "pwm0"; + }; + + /omit-if-no-ref/ + pwm1_pin: pwm1-pin { + pins = "PG19"; + function = "pwm1"; + }; + + /omit-if-no-ref/ + pwm2_pin: pwm2-pin { + pins = "PH2"; + function = "pwm2"; + }; + + /omit-if-no-ref/ + pwm3_pin: pwm3-pin { + pins = "PH0"; + function = "pwm3"; + }; + + /omit-if-no-ref/ + pwm4_pin: pwm4-pin { + pins = "PI14"; + function = "pwm4"; + }; + + /omit-if-no-ref/ + pwm5_pin: pwm5-pin { + pins = "PA12"; + function = "pwm5"; + }; + /omit-if-no-ref/ spi0_pins: spi0-pins { pins = "PC0", "PC2", "PC4"; From patchwork Tue Sep 22 00:06:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3408 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id EED6D1C1FE8 for ; Tue, 22 Sep 2026 02:09:03 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26106-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26106-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 236A26362A for ; Tue, 22 Sep 2026 00:06:55 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7E6EC221FD4; Tue, 22 Sep 2026 00:06:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sOjOhvaT" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oa2-f12.google.com (mail-oa2-f12.google.com [74.125.231.76]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1EDA2223328 for ; Tue, 22 Sep 2026 00:06:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.76 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035614; cv=none; b=A4qrcLzHt+Kqa1paulYbwIeMEVa88BtejCboRUUZp35c4da9YapEZDNxzI20o/OB2e2zChD12c5fOzw0GdFw3UwyRQUwHHRnRtdsdI66JR5x5m1oZ7wXX1BagrJqnrt24yBQ1JP8pmP+nslUN0m49l7mU48G31cM8m5P5OLpTj0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790035614; c=relaxed/simple; bh=OJS6mQlRWmTTimOyj6kC0TvQSBszoDm45iW9XPqV2AQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=H8vR0l9LQniyc8GajDfR9CkzOS9/uh6Y2K601f0qVqmcuw0PPfbLSezOON3A57c7K9sz7JxfnwfOVX8daqQ0r5AEw2wFVliDh1d26T8NnhcU1C5Fg9qMyo/I/VYRCa+/IXrcGUpGXQZj6DrjPE8p1YP16/AK9TJmUZA76PV5L+I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=sOjOhvaT; arc=none smtp.client-ip=74.125.231.76 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oa2-f12.google.com with SMTP id 586e51a60fabf-466ccdd7695so1244124fac.1 for ; Mon, 21 Sep 2026 17:06:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790035611; x=1790640411; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=xhBI/4MYKZ9FiatfUTHQXDlddZz4ARJ+4t2N8WN0Mwk=; b=sOjOhvaTxiGEBKVvSJxcZikHVm2YvMItUrcTLs69AHz/9V/N9NUfxPfqKhYm0R7he2 D5MpaFqZYDRC40611IwDWf+A+eO3+pMO519lPvd5zr2fOgWe60BAq4ilUZfavSbU8aUq SWEX67OBlffhuSyqifl3ujjYLFcJd6byyWclzjmOBTr6iFR92MkFPPeb/sCsUrwuaTcs 5M23jF3xK5zYnHrzQ2d5fthHv/9DYBhQTsR21rgpqU3oPLQG5GE14pAJerz3y9uKGWB+ JxO127Ovu/Os5Y4o1yCu4Y5577R24EsOw46xonKgta8Ujy17FGWCFwWj58oXhof6t3Vy Rvzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790035611; x=1790640411; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=xhBI/4MYKZ9FiatfUTHQXDlddZz4ARJ+4t2N8WN0Mwk=; b=ZdW3stOFBcTbDzVIfg4oCLcA02HuvFQbtSVy6R9gvXHc1e9duNRgaMoMC6auU7Kacc JfPCbVxbSWvNzaZldKTGCmeuZdOUSkjJncqNilDEOFDS9/O2EwCtAlo81epeP4L5egSG jekd5SHcoCTRrZqGWmOsSulkvSgwkVo47bsC2qW1nEznFU1+xwglOsqrvtgtnjtINOln sOAfUuDW+zecwlk7bzn55bsEjgFaAfp3UJocClCCrHDKsKSn2EadrvuLX3v5J+iO4imH sF9vnjJQEKjD3aFo3H8/Gxzw2iMfAIKLo76Nf9183wcKD0Ud7XkENT/yelZQVznAyjnM uk4Q== X-Forwarded-Encrypted: i=1; AKwUvBwelqpPejilXsGjTA0IZNz1lPsnmNbFxye+WFTdxT4+eaOix8wk32MwVXkgmIPOz+TjdpcZjKIsmJvqmg==@lists.linux.dev X-Gm-Message-State: AFuF++nX/2nFiiv284/GZvTFbVYTbfGKmouZsswWC4Y1XdR1JKDQX9nH HeWBE/GSSNgxLhlxr1VwPz8jiHNnC6p0P2AI+5sGO05TnEJwm5nS7A8B X-Gm-Gg: AYBFou0PTk7jMFU2mMcyqOwIiBqIUhdtbMYVgLlvmh9wPDrLH33/TrtQMmAsMFIKftp BCjsGfcy2S+CtO1ryKp9uHNr6p8JRmn7AltkPHrWai70k8RIgauvSyr7YLxdQpSkigvAoH9UNvf nZ+h6W2auYRoUa3u6JmQ6MZqWFYrpYmR9FrVq3VlZ2ggJpci/TZVtLBTa4W5TH5otAGx5KH2XOR VrxwbZqaknTE60m1iK/xnXqorzuU0K9Mw/3IvxwtAM5ACSYDYJ2pHt2a4611uoL49zuy8zEtkzv CFMNxNMzZw6zoidXY38jUTuNuHDCvfDbVV/AmidJFkAl7XC0wNq31PWSxtLhFtJMT24GpNKTNR4 oqa7QrXPVqyaHLLWXIntCKmtIBcjnIJfbzhyrOixpkILoiJ7SXYGkNdIr/EuXza3A276znVYt24 jb1at2hAeOmwEVExTyCfei8IeBcGyFS5TYMzesC9fnC9NjTmM88nwOLCOAXwMOpTVeeEe9FIOKZ JE+gntsoOUwlIQAEaGbZ7UPi/jkUoyzoyHLIIh/jGb5lxGL3vJHJ0u+eDH4252v/bbGkkaTJUu1 Fxz6LD/DmAomnzjSprS4zLPHoqAENEGT7M3mhXa2m+s4Io/Hw1ezLrny+DXs5AfYRDZqMRaxDWG HuE53+zQS/yD2On0BJIZj X-Received: by 2002:a05:6870:e24a:b0:483:d19f:334b with SMTP id 586e51a60fabf-486e6eeddb5mr12235098fac.24.1790035610897; Mon, 21 Sep 2026 17:06:50 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-48f7280cf14sm801683fac.13.2026.09.21.17.06.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 17:06:50 -0700 (PDT) From: James Hilliard Date: Mon, 21 Sep 2026 18:06:34 -0600 Subject: [PATCH v9 5/5] MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260921-h616-pwm-v8-v9-5-37bec6201ff5@gmail.com> References: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> In-Reply-To: <20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Maxime Ripard , Richard Genoud , Philipp Zabel , Michael Turquette , Stephen Boyd , Brian Masney Cc: linux-pwm@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, Paul Kocialkowski , Thomas Petazzoni , John Stultz , Joao Schim , bigunclemax@gmail.com, linux-clk@vger.kernel.org, James Hilliard X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= From: Richard Genoud Add myself as maintainer of Allwinner SUN8I PWM driver. Signed-off-by: Richard Genoud Signed-off-by: James Hilliard --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index cc3cae2e378b..d9713b1044c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -918,6 +918,11 @@ S: Maintained F: Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml F: drivers/hwspinlock/sun6i_hwspinlock.c +ALLWINNER SUN8I PWM DRIVER +M: Richard Genoud +S: Maintained +F: drivers/pwm/pwm-sun8i.c + ALLWINNER THERMAL DRIVER M: Vasily Khoruzhick M: Yangtao Li