From nobody Fri Dec 19 15:59:40 2025 Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 30B57328B5B; Wed, 17 Dec 2025 08:25:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.171.202.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765959928; cv=none; b=htJrBf1yl9DTeng/PWflisxlyVScb5vOC9GYD9lTIwbgEnQWSaUxPUSbzaRa2b9501GHkXAclaEejB54hX1ktl4/1dNeepJRScQ+gO+Nzel/1XjQzU/wAS4DKOSqmzDb4LsbDsDIpLXBkOuhsPnYh0OVniG8Oy06jjHEkWo8/7E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765959928; c=relaxed/simple; bh=AOtlkmYcFf4N1NoTUtXQQ8B8TY+AgJ+8WpzwyZyEyzs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FqoKU6ER23JQ3JfWwFrzioSgdr3qx1/mkUsfJKZdoiNGCk8nse1sSPAzrAsRmcP894Z/FDMYABnLAZm1yCisaO1E6+IyeTyJ0WP1wBYfBRCQ8zwWzrJuWUB8O/x+gVE+mRxQ/EWXT4SBGQtryOX8VCdwEwXfg83thxDhnI5CJqU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=h+gdosi9; arc=none smtp.client-ip=185.171.202.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="h+gdosi9" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id 14DA1C1A595; Wed, 17 Dec 2025 08:24:58 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 9698E6072F; Wed, 17 Dec 2025 08:25:22 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id BEF33119502E7; Wed, 17 Dec 2025 09:25:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1765959918; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=x3b+xMraFqoaM2Sw2nhC5gdOxqmlP8rsrzs6PW+Y/W8=; b=h+gdosi9m1kVDsbEQmxRUF/LDE/F1BxtfsDtAw1/OhIJh3SNytZDMXwZhUuV4VrQpuQ8li V2PrC75hkRITRlQIcC/e+iP+tvtHvuW+keg/hwxf+qZKAgbr6cRfmb49D15Et07m3b74N2 c5gfjDE5Z/VPi1L2bRhalQyzh0WcL0qQmBH7Dy3XftiTr+B+TUc0r9t73V2uJug/EMrPOS 82Ej4aJtQ5qBjfse/gBxo5YHMla0qYrf5Hz1rBe++zH/XV38A/rug/JYn4kYPZW1dzAw9f BzD28FjX7Mn0LMRRXjwtUN5Znpw15I+xWgLcpaaOLYjt+KUXROrwJfB2Iq3qYQ== From: Richard Genoud To: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Philipp Zabel Cc: Thomas Petazzoni , 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, Richard Genoud , Krzysztof Kozlowski Subject: [PATCH v2 1/4] dt-bindings: pwm: allwinner: add h616 pwm compatible Date: Wed, 17 Dec 2025 09:25:01 +0100 Message-ID: <20251217082504.80226-2-richard.genoud@bootlin.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251217082504.80226-1-richard.genoud@bootlin.com> References: <20251217082504.80226-1-richard.genoud@bootlin.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-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Allwinner H616 PWM block is quite different from the A10 or H6, but at the end, it uses the same clocks as the H6; so the sun4i pwm binding can be used. It has 6 channels than can generate PWM waveforms or clocks if bypass is enabled. Suggested-by: Krzysztof Kozlowski Signed-off-by: Richard Genoud --- .../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 19 ++++++++++++++++++- 1 file changed, 18 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..4f58110ec98f 100644 --- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml @@ -14,6 +14,9 @@ properties: "#pwm-cells": const: 3 =20 + "#clock-cells": + const: 1 + compatible: oneOf: - const: allwinner,sun4i-a10-pwm @@ -36,6 +39,7 @@ properties: - const: allwinner,sun50i-h5-pwm - const: allwinner,sun5i-a13-pwm - const: allwinner,sun50i-h6-pwm + - const: allwinner,sun50i-h616-pwm =20 reg: maxItems: 1 @@ -62,7 +66,9 @@ allOf: properties: compatible: contains: - const: allwinner,sun50i-h6-pwm + enum: + - allwinner,sun50i-h6-pwm + - allwinner,sun50i-h616-pwm =20 then: properties: @@ -83,6 +89,17 @@ allOf: clocks: maxItems: 1 =20 + - if: + not: + properties: + compatible: + contains: + const: allwinner,sun50i-h616-pwm + + then: + properties: + "#clock-cells": false + required: - compatible - reg --=20 2.47.3 From nobody Fri Dec 19 15:59:40 2025 Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 200F933AD9D; Wed, 17 Dec 2025 08:25:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.171.202.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765959933; cv=none; b=PFk/8UJG1pkxbqKmIP236elTx5Ns4gOOQcyVJaU1kK9OBFGGMyi5T+IXDysk+jM+js8VGDdYa6ENBkUMnvbbNVI971STqJqi8AMFsOermvDzqps0ZwdoxRk4HUx0nBAUzw/j2QMDvPPUlOxU69PMq8Mmck/E+cqVNJdTYHcx4oY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765959933; c=relaxed/simple; bh=kxYGY2wt0f/EzL9aOmg+KuduxMHhPfqqrzpHBkzANjU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MsLw1ln+lIdR99N7qxvYVV47CtyETpDDgSVe8Pis9Kyc5aYVvsVl92XHbubAj7+hXCl8Q84NP2B52t0Ls32jTOs/n7pHy+XxZacU7nTqWtf7kQbBwiToQtDGcxykVatvt1f0xEX2AOOeIBLIxNIj5tFwyqELkVXeV6ueDUsIdmI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=hWw4oPMw; arc=none smtp.client-ip=185.171.202.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="hWw4oPMw" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id EACAAC1A594; Wed, 17 Dec 2025 08:25:03 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 7307E6072F; Wed, 17 Dec 2025 08:25:28 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 49AA3119502E4; Wed, 17 Dec 2025 09:25:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1765959923; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=dhe8nxJCKy9f9jwOQBwc2zcplRwYegvYT7YHn73Ieis=; b=hWw4oPMwKFFLiTAt559Zs+x0WimV4LRBDHVXX3OjaHiERG2/qA2FOVQdl86XG7OFcYFKrd OiIj55vMjHdVpfyMhNbHvGvBXanoFtEQP7iFdYBqqrAGuN1q+JyFT2wjZlnxFbGycQlN/W yxopBSQnTmfYsVpPoUFs6tz/YRFKwUFHVk8jiRl17bR1BeOjhvrfY73xXo6ZkdMWoQ+85n 6JjtAF2lc59qZVuJ1bO1tc83xBLDqVfmUkY+C6Oh7qHOBKTYncjLRoCUoVGOupXbAGj/9A /I/825TeSdnpFJg8z9v9pmRzE1sVyOG3sPeZNRkcIccB6wLMKK14iQz7Sl052g== From: Richard Genoud To: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Philipp Zabel Cc: Thomas Petazzoni , 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, Richard Genoud , Joao Schim Subject: [PATCH v2 2/4] pwm: sun50i: Add H616 PWM support Date: Wed, 17 Dec 2025 09:25:02 +0100 Message-ID: <20251217082504.80226-3-richard.genoud@bootlin.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251217082504.80226-1-richard.genoud@bootlin.com> References: <20251217082504.80226-1-richard.genoud@bootlin.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-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Add driver for Allwinner H616 PWM controller, supporting up to 6 channels. Those channels output can be either a PWM signal output or a clock output, thanks to the bypass. The channels are paired (0/1, 2/3 and 4/5) and each pair has a prescaler/mux/gate. Moreover, each channel has its own prescaler and bypass. Tested-by: Joao Schim Signed-off-by: Richard Genoud --- drivers/pwm/Kconfig | 12 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-sun50i-h616.c | 892 ++++++++++++++++++++++++++++++++++ 3 files changed, 905 insertions(+) create mode 100644 drivers/pwm/pwm-sun50i-h616.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6f3147518376..66534e033761 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -736,6 +736,18 @@ config PWM_SUN4I To compile this driver as a module, choose M here: the module will be called pwm-sun4i. =20 +config PWM_SUN50I_H616 + tristate "Allwinner H616 PWM support" + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM && COMMON_CLK + help + Generic PWM framework driver for Allwinner H616 SoCs. + It supports generic PWM, but can also provides a plain clock. + The AC300 PHY integrated in H616 SoC needs such a clock. + + To compile this driver as a module, choose M here: the module + will be called pwm-h616. + config PWM_SUNPLUS tristate "Sunplus PWM support" depends on ARCH_SUNPLUS || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 0dc0d2b69025..a16ae9eef9e5 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_PWM_STM32) +=3D pwm-stm32.o obj-$(CONFIG_PWM_STM32_LP) +=3D pwm-stm32-lp.o obj-$(CONFIG_PWM_STMPE) +=3D pwm-stmpe.o obj-$(CONFIG_PWM_SUN4I) +=3D pwm-sun4i.o +obj-$(CONFIG_PWM_SUN50I_H616) +=3D pwm-sun50i-h616.o obj-$(CONFIG_PWM_SUNPLUS) +=3D pwm-sunplus.o obj-$(CONFIG_PWM_TEGRA) +=3D pwm-tegra.o obj-$(CONFIG_PWM_TH1520) +=3D pwm_th1520.o diff --git a/drivers/pwm/pwm-sun50i-h616.c b/drivers/pwm/pwm-sun50i-h616.c new file mode 100644 index 000000000000..b002ea5935e4 --- /dev/null +++ b/drivers/pwm/pwm-sun50i-h616.c @@ -0,0 +1,892 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Allwinner H616 Pulse Width Modulation Controller + * + * (C) Copyright 2025 Richard Genoud, Bootlin + * + * Based on drivers/pwm/pwm-sun4i.c with Copyright: + * + * Copyright (C) 2014 Alexandre Belloni + * + * Limitations: + * - When outputing the source clock directly, the PWM logic is bypassed a= nd the + * currently running period is not guaranteed to be completed. + * - As the channels are paired (0/1, 2/3, 4/5), they share the same clock + * source and prescaler(div_m), but they also have their own prescaler(d= iv_k) + * and bypass. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef UINT32_MAX +#define UINT32_MAX 0xffffffffU +#endif + +/* PWM IRQ Enable Register */ +#define PWM_IER 0x0 + +/* PWM IRQ Status Register */ +#define PWM_ISR 0x4 + +/* PWM Capture IRQ Enable Register */ +#define PWM_CIER 0x10 + +/* PWM Capture IRQ Status Register */ +#define PWM_CISR 0x14 + +/* PWMCC Pairs Clock Configuration Registers */ +#define PWM_XY_CLK_CR(pair) (0x20 + ((pair) * 0x4)) +#define PWM_XY_CLK_CR_SRC_SHIFT 7 +#define PWM_XY_CLK_CR_SRC_MASK 1 +#define PWM_XY_CLK_CR_GATE_BIT 4 +#define PWM_XY_CLK_CR_BYPASS_BIT(chan) ((chan) % 2 + 5) +#define PWM_XY_CLK_CR_DIV_M_SHIFT 0 + +/* PWMCC Pairs Dead Zone Control Registers */ +#define PWM_XY_DZ(pair) (0x30 + ((pair) * 0x4)) + +/* PWM Enable Register */ +#define PWM_ENR 0x40 +#define PWM_ENABLE(x) BIT(x) + +/* PWM Capture Enable Register */ +#define PWM_CER 0x44 + +/* PWM Control Register */ +#define PWM_CTRL_REG(chan) (0x60 + (chan) * 0x20) +#define PWM_CTRL_PRESCAL_K_SHIFT 0 +#define PWM_CTRL_PRESCAL_K_WIDTH 8 +#define PWM_CTRL_ACTIVE_STATE BIT(8) + +/* PWM Period Register */ +#define PWM_PERIOD_REG(ch) (0x64 + (ch) * 0x20) +#define PWM_PERIOD_MASK GENMASK(31, 16) +#define PWM_DUTY_MASK GENMASK(15, 0) +#define PWM_REG_PERIOD(reg) (FIELD_GET(PWM_PERIOD_MASK, reg) + 1) +#define PWM_REG_DUTY(reg) FIELD_GET(PWM_DUTY_MASK, reg) +#define PWM_PERIOD(prd) FIELD_PREP(PWM_PERIOD_MASK, (prd) - 1) +#define PWM_DUTY(dty) FIELD_PREP(PWM_DUTY_MASK, dty) +#define PWM_PERIOD_MAX FIELD_MAX(PWM_PERIOD_MASK) + + +/* PWM Count Register */ +#define PWM_CNT_REG(x) (0x68 + (x) * 0x20) + +/* PWM Capture Control Register */ +#define PWM_CCR(x) (0x6c + (x) * 0x20) + +/* PWM Capture Rise Lock Register */ +#define PWM_CRLR(x) (0x70 + (x) * 0x20) + +/* PWM Capture Fall Lock Register */ +#define PWM_CFLR(x) (0x74 + (x) * 0x20) + +#define PWM_PAIR_IDX(chan) ((chan) >> 2) + +/* + * Block diagram of the PWM clock controller: + * + * _____ ______ ________ + * OSC24M --->| | | | | | + * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> PWM_clock_src_xy + * |_____| |______| |________| + * ________ + * | | + * +->| /div_k |---> PWM_clock_x + * | |________| + * | ______ + * | | | + * +-->| Gate |----> PWM_bypass_clock_x + * | |______| + * PWM_clock_src_xy -----+ ________ + * | | | + * +->| /div_k |---> PWM_clock_y + * | |________| + * | ______ + * | | | + * +-->| Gate |----> 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 duty cycle with the fas= test + * clock. + * + * PWM_clock_x/y serve for the PWM purpose. + * PWM_bypass_clock_x/y serve for the clock-provider purpose. + * + */ + +/* + * Table used for /div_m (diviser before obtaining PWM_clock_src_xy) + * It's actually CLK_DIVIDER_POWER_OF_TWO, but limited to /256 + */ +static const struct clk_div_table clk_table_div_m[] =3D { + { .val =3D 0, .div =3D 1, }, + { .val =3D 1, .div =3D 2, }, + { .val =3D 2, .div =3D 4, }, + { .val =3D 3, .div =3D 8, }, + { .val =3D 4, .div =3D 16, }, + { .val =3D 5, .div =3D 32, }, + { .val =3D 6, .div =3D 64, }, + { .val =3D 7, .div =3D 128, }, + { .val =3D 8, .div =3D 256, }, + { .val =3D 0, .div =3D 0, }, /* last entry */ +}; + +#define PWM_XY_SRC_GATE(_pair, _reg) \ +struct clk_gate gate_xy_##_pair =3D { \ + .reg =3D (void *)_reg, \ + .bit_idx =3D PWM_XY_CLK_CR_GATE_BIT, \ + .hw.init =3D &(struct clk_init_data){ \ + .ops =3D &clk_gate_ops, \ + } \ +} + +#define PWM_XY_SRC_MUX(_pair, _reg) \ +struct clk_mux mux_xy_##_pair =3D { \ + .reg =3D (void *)_reg, \ + .shift =3D PWM_XY_CLK_CR_SRC_SHIFT, \ + .mask =3D PWM_XY_CLK_CR_SRC_MASK, \ + .flags =3D CLK_MUX_ROUND_CLOSEST, \ + .hw.init =3D &(struct clk_init_data){ \ + .ops =3D &clk_mux_ops, \ + } \ +} + +#define PWM_XY_SRC_DIV(_pair, _reg) \ +struct clk_divider rate_xy_##_pair =3D { \ + .reg =3D (void *)_reg, \ + .shift =3D PWM_XY_CLK_CR_DIV_M_SHIFT, \ + .table =3D clk_table_div_m, \ + .hw.init =3D &(struct clk_init_data){ \ + .ops =3D &clk_divider_ops, \ + } \ +} + +#define PWM_X_DIV(_idx, _reg) \ +struct clk_divider rate_x_##_idx =3D { \ + .reg =3D (void *)_reg, \ + .shift =3D PWM_CTRL_PRESCAL_K_SHIFT, \ + .width =3D PWM_CTRL_PRESCAL_K_WIDTH, \ + .hw.init =3D &(struct clk_init_data){ \ + .ops =3D &clk_divider_ops, \ + } \ +} + +#define PWM_X_BYPASS_GATE(_idx) \ +struct clk_gate gate_x_bypass_##_idx =3D { \ + .reg =3D (void *)PWM_ENR, \ + .bit_idx =3D _idx, \ + .hw.init =3D &(struct clk_init_data){ \ + .ops =3D &clk_gate_ops, \ + } \ +} + +#define PWM_XY_CLK_SRC(_pair, _reg) \ + static PWM_XY_SRC_MUX(_pair, _reg); \ + static PWM_XY_SRC_GATE(_pair, _reg); \ + static PWM_XY_SRC_DIV(_pair, _reg) + +#define PWM_X_CLK(_idx) \ + static PWM_X_DIV(_idx, PWM_CTRL_REG(_idx)) + +#define PWM_X_BYPASS_CLK(_idx) \ + PWM_X_BYPASS_GATE(_idx) + +#define REF_CLK_XY_SRC(_pair) \ + { \ + .name =3D "pwm-clk-src" #_pair, \ + .mux_hw =3D &mux_xy_##_pair.hw, \ + .gate_hw =3D &gate_xy_##_pair.hw, \ + .rate_hw =3D &rate_xy_##_pair.hw, \ + } + +#define REF_CLK_X(_idx, _pair) \ + { \ + .name =3D "pwm-clk" #_idx, \ + .parent_names =3D (const char *[]){ "pwm-clk-src" #_pair }, \ + .num_parents =3D 1, \ + .rate_hw =3D &rate_x_##_idx.hw, \ + .flags =3D CLK_SET_RATE_PARENT, \ + } + +#define REF_CLK_BYPASS(_idx, _pair) \ + { \ + .name =3D "pwm-clk-bypass" #_idx, \ + .parent_names =3D (const char *[]){ "pwm-clk-src" #_pair }, \ + .num_parents =3D 1, \ + .gate_hw =3D &gate_x_bypass_##_idx.hw, \ + .flags =3D CLK_SET_RATE_PARENT, \ + } + +/* + * PWM_clock_src_xy generation: + * _____ ______ ________ + * OSC24M --->| | | | | | + * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> PWM_clock_src_xy + * |_____| |______| |________| + */ +PWM_XY_CLK_SRC(01, PWM_XY_CLK_CR(0)); +PWM_XY_CLK_SRC(23, PWM_XY_CLK_CR(1)); +PWM_XY_CLK_SRC(45, PWM_XY_CLK_CR(2)); + +/* + * PWM_clock_x_div generation: + * ________ + * | | PWM_clock_x/y + * PWM_clock_src_xy --->| /div_k |---------------> + * |________| + */ +PWM_X_CLK(0); +PWM_X_CLK(1); +PWM_X_CLK(2); +PWM_X_CLK(3); +PWM_X_CLK(4); +PWM_X_CLK(5); + +/* + * PWM_bypass_clock_xy generation: + * ______ + * | | + * PWM_clock_src_xy ---->| Gate |-------> PWM_bypass_clock_x + * |______| + * + * The gate is actually PWM_ENR register. + */ +PWM_X_BYPASS_CLK(0); +PWM_X_BYPASS_CLK(1); +PWM_X_BYPASS_CLK(2); +PWM_X_BYPASS_CLK(3); +PWM_X_BYPASS_CLK(4); +PWM_X_BYPASS_CLK(5); + +struct clk_pwm_data { + const char *name; + const char **parent_names; + unsigned int num_parents; + struct clk_hw *mux_hw; + struct clk_hw *rate_hw; + struct clk_hw *gate_hw; + unsigned long flags; +}; + +#define CLK_BYPASS(h616chip, ch) ((h616chip)->data->npwm + (ch)) +#define CLK_XY_SRC_IDX(h616chip, ch) ((h616chip)->data->npwm * 2 + ((ch) >= > 1)) +static struct clk_pwm_data pwmcc_data[] =3D { + REF_CLK_X(0, 01), + REF_CLK_X(1, 01), + REF_CLK_X(2, 23), + REF_CLK_X(3, 23), + REF_CLK_X(4, 45), + REF_CLK_X(5, 45), + REF_CLK_BYPASS(0, 01), + REF_CLK_BYPASS(1, 01), + REF_CLK_BYPASS(2, 23), + REF_CLK_BYPASS(3, 23), + REF_CLK_BYPASS(4, 45), + REF_CLK_BYPASS(5, 45), + REF_CLK_XY_SRC(01), + REF_CLK_XY_SRC(23), + REF_CLK_XY_SRC(45), + { /* sentinel */ }, +}; + +enum h616_pwm_mode { + H616_PWM_MODE_NONE, + H616_PWM_MODE_PWM, + H616_PWM_MODE_CLK, +}; + +struct h616_pwm_data { + unsigned int npwm; +}; + +struct h616_pwm_channel { + struct clk *pwm_clk; + unsigned long rate; + unsigned int entire_cycles; + unsigned int active_cycles; + enum h616_pwm_mode mode; + bool bypass; +}; + +struct clk_pwm_pdata { + struct clk_hw_onecell_data *hw_data; + spinlock_t lock; + void __iomem *reg; +}; + +struct h616_pwm_chip { + struct clk_pwm_pdata *clk_pdata; + struct h616_pwm_channel *channels; + struct clk *bus_clk; + struct reset_control *rst; + void __iomem *base; + const struct h616_pwm_data *data; +}; + +static inline struct h616_pwm_chip *to_h616_pwm_chip(struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +static inline u32 h616_pwm_readl(struct h616_pwm_chip *h616chip, + unsigned long offset) +{ + return readl(h616chip->base + offset); +} + +static inline void h616_pwm_writel(struct h616_pwm_chip *h616chip, + u32 val, unsigned long offset) +{ + writel(val, h616chip->base + offset); +} + +static void h616_pwm_set_bypass(struct h616_pwm_chip *h616chip, unsigned i= nt idx, + bool en_bypass) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&h616chip->clk_pdata->lock, flags); + + val =3D h616_pwm_readl(h616chip, PWM_XY_CLK_CR(PWM_PAIR_IDX(idx))); + if (en_bypass) + val |=3D BIT(PWM_XY_CLK_CR_BYPASS_BIT(idx)); + else + val &=3D ~BIT(PWM_XY_CLK_CR_BYPASS_BIT(idx)); + + h616_pwm_writel(h616chip, val, PWM_XY_CLK_CR(PWM_PAIR_IDX(idx))); + + spin_unlock_irqrestore(&h616chip->clk_pdata->lock, flags); +} + +static int h616_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct h616_pwm_chip *h616chip =3D to_h616_pwm_chip(chip); + struct h616_pwm_channel *chan =3D &h616chip->channels[pwm->hwpwm]; + struct device *dev =3D pwmchip_parent(chip); + unsigned long flags; + int ret =3D 0; + + spin_lock_irqsave(&h616chip->clk_pdata->lock, flags); + + if (chan->mode =3D=3D H616_PWM_MODE_CLK) + ret =3D -EBUSY; + else + chan->mode =3D H616_PWM_MODE_PWM; + + spin_unlock_irqrestore(&h616chip->clk_pdata->lock, flags); + if (ret) + goto out; + + ret =3D clk_prepare_enable(chan->pwm_clk); + if (ret < 0) { + dev_err(dev, "Failed to enable clock %s: %d\n", + __clk_get_name(chan->pwm_clk), ret); + } +out: + return ret; +} + +static void h616_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct h616_pwm_chip *h616chip =3D to_h616_pwm_chip(chip); + struct h616_pwm_channel *chan =3D &h616chip->channels[pwm->hwpwm]; + + clk_disable_unprepare(chan->pwm_clk); + chan->mode =3D H616_PWM_MODE_NONE; +} + +static int h616_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) +{ + struct h616_pwm_chip *h616chip =3D to_h616_pwm_chip(chip); + struct h616_pwm_channel *chan =3D &h616chip->channels[pwm->hwpwm]; + u64 clk_rate, tmp; + u32 val; + + clk_rate =3D clk_get_rate(chan->pwm_clk); + if (!clk_rate) + return -EINVAL; + + val =3D h616_pwm_readl(h616chip, PWM_ENR); + state->enabled =3D !!(PWM_ENABLE(pwm->hwpwm) & val); + + val =3D h616_pwm_readl(h616chip, PWM_XY_CLK_CR(PWM_PAIR_IDX(pwm->hwpwm))); + if (val & BIT(PWM_XY_CLK_CR_BYPASS_BIT(pwm->hwpwm))) { + /* + * When bypass is enabled, the PWM logic is inactive. + * The PWM_clock_src_xy is directly routed to PWM_clock_x + */ + state->period =3D DIV_ROUND_UP_ULL(NSEC_PER_SEC, clk_rate); + state->duty_cycle =3D DIV_ROUND_UP_ULL(state->period, 2); + state->polarity =3D PWM_POLARITY_NORMAL; + return 0; + } + + state->enabled &=3D !!(BIT(PWM_XY_CLK_CR_GATE_BIT) & val); + + val =3D h616_pwm_readl(h616chip, PWM_CTRL_REG(pwm->hwpwm)); + if (val & PWM_CTRL_ACTIVE_STATE) + state->polarity =3D PWM_POLARITY_NORMAL; + else + state->polarity =3D PWM_POLARITY_INVERSED; + + val =3D h616_pwm_readl(h616chip, PWM_PERIOD_REG(pwm->hwpwm)); + + tmp =3D NSEC_PER_SEC * PWM_REG_DUTY(val); + state->duty_cycle =3D DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + + tmp =3D NSEC_PER_SEC * PWM_REG_PERIOD(val); + state->period =3D DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + + return 0; +} + +static int h616_pwm_calc(struct pwm_chip *chip, unsigned int idx, + const struct pwm_state *state) +{ + struct h616_pwm_chip *h616chip =3D to_h616_pwm_chip(chip); + struct h616_pwm_channel *chan =3D &h616chip->channels[idx]; + unsigned int cnt, duty_cnt; + unsigned long max_rate; + long calc_rate; + u64 duty, period, freq; + + duty =3D state->duty_cycle; + period =3D state->period; + + max_rate =3D clk_round_rate(chan->pwm_clk, UINT32_MAX); + + dev_dbg(pwmchip_parent(chip), "max_rate: %ld Hz\n", max_rate); + + if ((period * max_rate >=3D NSEC_PER_SEC) && + (period * max_rate < 2 * NSEC_PER_SEC) && + (duty * max_rate * 2 >=3D NSEC_PER_SEC)) { + /* + * If the requested period is to small to be generated by the + * PWM, we can just select the highest clock and bypass the + * PWM logic + */ + dev_dbg(pwmchip_parent(chip), "Setting bypass (period=3D%lld)\n", + period); + freq =3D div64_u64(NSEC_PER_SEC, period); + chan->bypass =3D true; + duty =3D period / 2; + } else { + chan->bypass =3D false; + freq =3D div64_u64(NSEC_PER_SEC * (u64)PWM_PERIOD_MAX, period); + if (freq > UINT32_MAX) + freq =3D UINT32_MAX; + } + + calc_rate =3D clk_round_rate(chan->pwm_clk, freq); + if (calc_rate <=3D 0) { + dev_err(pwmchip_parent(chip), + "Invalid source clock frequency %llu\n", freq); + return calc_rate ? calc_rate : -EINVAL; + } + + dev_dbg(pwmchip_parent(chip), "calc_rate: %ld Hz\n", calc_rate); + + cnt =3D mul_u64_u64_div_u64(calc_rate, period, NSEC_PER_SEC); + if ((cnt =3D=3D 0) || (cnt > PWM_PERIOD_MAX)) { + dev_err(pwmchip_parent(chip), "Period out of range\n"); + return -EINVAL; + } + + duty_cnt =3D mul_u64_u64_div_u64(calc_rate, duty, NSEC_PER_SEC); + + if (duty_cnt >=3D cnt) + duty_cnt =3D cnt - 1; + + dev_dbg(pwmchip_parent(chip), "period=3D%llu cnt=3D%u duty=3D%llu duty_cn= t=3D%u\n", + period, cnt, duty, duty_cnt); + + chan->active_cycles =3D duty_cnt; + chan->entire_cycles =3D cnt; + + chan->rate =3D calc_rate; + + return 0; +} + +static int h616_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct h616_pwm_chip *h616chip =3D to_h616_pwm_chip(chip); + struct h616_pwm_channel *chan =3D &h616chip->channels[pwm->hwpwm]; + struct pwm_state cstate; + unsigned long flags; + u32 val; + int ret; + + ret =3D h616_pwm_calc(chip, pwm->hwpwm, state); + if (ret) { + dev_err(pwmchip_parent(chip), "period exceeds the maximum value\n"); + return ret; + } + + pwm_get_state(pwm, &cstate); + + ret =3D clk_set_rate(chan->pwm_clk, chan->rate); + if (ret) { + dev_err(pwmchip_parent(chip), "failed to set PWM %d clock rate to %lu\n", + pwm->hwpwm, chan->rate); + return ret; + } + + h616_pwm_set_bypass(h616chip, pwm->hwpwm, chan->bypass); + + /* + * If bypass is set, the PWM logic (polarity, duty) can't be applied + */ + + if (chan->bypass && (state->polarity =3D=3D PWM_POLARITY_INVERSED)) { + dev_warn(pwmchip_parent(chip), + "Can't set inversed polarity with bypass enabled\n"); + } else { + val =3D h616_pwm_readl(h616chip, PWM_CTRL_REG(pwm->hwpwm)); + val &=3D ~PWM_CTRL_ACTIVE_STATE; + if (state->polarity =3D=3D PWM_POLARITY_NORMAL) + val |=3D PWM_CTRL_ACTIVE_STATE; + h616_pwm_writel(h616chip, val, PWM_CTRL_REG(pwm->hwpwm)); + } + + if (chan->bypass && (state->duty_cycle * 2 !=3D state->period)) { + dev_warn(pwmchip_parent(chip), + "Can't set a duty cycle with bypass enabled\n"); + } + + if (!chan->bypass) { + val =3D PWM_DUTY(chan->active_cycles); + val |=3D PWM_PERIOD(chan->entire_cycles); + h616_pwm_writel(h616chip, val, PWM_PERIOD_REG(pwm->hwpwm)); + } + + if (state->enabled =3D=3D cstate.enabled) + return 0; + + if (cstate.enabled) { + unsigned long delay_us; + + /* + * We need a full period to elapse before + * disabling the channel. + */ + delay_us =3D DIV_ROUND_UP_ULL(cstate.period, NSEC_PER_USEC); + fsleep(delay_us); + } + + spin_lock_irqsave(&h616chip->clk_pdata->lock, flags); + + val =3D h616_pwm_readl(h616chip, PWM_ENR); + if (state->enabled) + val |=3D PWM_ENABLE(pwm->hwpwm); + else + val &=3D ~PWM_ENABLE(pwm->hwpwm); + h616_pwm_writel(h616chip, val, PWM_ENR); + + spin_unlock_irqrestore(&h616chip->clk_pdata->lock, flags); + + return 0; +} + +static const struct pwm_ops h616_pwm_ops =3D { + .apply =3D h616_pwm_apply, + .get_state =3D h616_pwm_get_state, + .request =3D h616_pwm_request, + .free =3D h616_pwm_free, +}; + +static struct clk_hw *h616_pwm_of_clk_get(struct of_phandle_args *clkspec, + void *data) +{ + struct h616_pwm_chip *h616chip =3D data; + struct clk_hw_onecell_data *hw_data =3D h616chip->clk_pdata->hw_data; + unsigned int idx =3D clkspec->args[0]; + struct h616_pwm_channel *chan; + struct clk_hw *ret_clk =3D NULL; + unsigned long flags; + + if (idx >=3D h616chip->data->npwm) + return ERR_PTR(-EINVAL); + + chan =3D &h616chip->channels[idx]; + + spin_lock_irqsave(&h616chip->clk_pdata->lock, flags); + + if (chan->mode =3D=3D H616_PWM_MODE_PWM) { + ret_clk =3D ERR_PTR(-EBUSY); + } else { + chan->mode =3D H616_PWM_MODE_CLK; + ret_clk =3D hw_data->hws[CLK_BYPASS(h616chip, idx)]; + } + spin_unlock_irqrestore(&h616chip->clk_pdata->lock, flags); + + if (IS_ERR(ret_clk)) + goto out; + + chan->bypass =3D true; + h616_pwm_set_bypass(h616chip, idx, chan->bypass); +out: + return ret_clk; +} + +static int h616_add_composite_clk(struct clk_pwm_data *data, + void __iomem *reg, spinlock_t *lock, + struct device *dev, struct clk_hw **hw) +{ + const struct clk_ops *mux_ops =3D NULL, *gate_ops =3D NULL, *rate_ops =3D= NULL; + struct clk_hw *mux_hw =3D NULL, *gate_hw =3D NULL, *rate_hw =3D NULL; + struct device_node *node =3D dev->of_node; + + if (data->mux_hw) { + struct clk_mux *mux; + + mux_hw =3D data->mux_hw; + mux =3D to_clk_mux(mux_hw); + mux->lock =3D lock; + mux_ops =3D mux_hw->init->ops; + mux->reg =3D (u64)mux->reg + reg; + } + + if (data->gate_hw) { + struct clk_gate *gate; + + gate_hw =3D data->gate_hw; + gate =3D to_clk_gate(gate_hw); + gate->lock =3D lock; + gate_ops =3D gate_hw->init->ops; + gate->reg =3D (u64)gate->reg + reg; + } + + if (data->rate_hw) { + struct clk_divider *rate; + + rate_hw =3D data->rate_hw; + rate =3D to_clk_divider(rate_hw); + rate_ops =3D rate_hw->init->ops; + rate->lock =3D lock; + rate->reg =3D (u64)rate->reg + reg; + + if (rate->table) { + const struct clk_div_table *clkt; + int table_size =3D 0; + + for (clkt =3D rate->table; clkt->div; clkt++) + table_size++; + rate->width =3D order_base_2(table_size); + } + } + + /* + * Retrieve the parent clock names from DTS for pwm-clk-srcxy + */ + if (!data->parent_names) { + data->num_parents =3D of_clk_get_parent_count(node); + if (data->num_parents =3D=3D 0) + return -ENOENT; + + data->parent_names =3D devm_kzalloc(dev, + sizeof(*data->parent_names), + GFP_KERNEL); + for (unsigned int i =3D 0; i < data->num_parents; i++) + data->parent_names[i] =3D of_clk_get_parent_name(node, i); + } + + *hw =3D clk_hw_register_composite(dev, data->name, data->parent_names, + data->num_parents, mux_hw, + mux_ops, rate_hw, rate_ops, + gate_hw, gate_ops, data->flags); + + return PTR_ERR_OR_ZERO(*hw); +} + +static int h616_pwm_init_clocks(struct platform_device *pdev, + struct h616_pwm_chip *h616chip) +{ + struct clk_pwm_pdata *pdata; + struct device *dev =3D &pdev->dev; + int num_clocks =3D 0; + int ret; + + pdata =3D devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + while (pwmcc_data[num_clocks].name) + num_clocks++; + + pdata->hw_data =3D devm_kzalloc(dev, struct_size(pdata->hw_data, hws, num= _clocks), + GFP_KERNEL); + if (!pdata->hw_data) + return -ENOMEM; + + pdata->hw_data->num =3D num_clocks; + pdata->reg =3D h616chip->base; + + spin_lock_init(&pdata->lock); + + for (int i =3D 0; i < num_clocks; i++) { + struct clk_hw **hw =3D &pdata->hw_data->hws[i]; + + ret =3D h616_add_composite_clk(&pwmcc_data[i], pdata->reg, + &pdata->lock, dev, hw); + if (ret) { + dev_err_probe(dev, ret, + "Failed to register hw clock %s\n", + pwmcc_data[i].name); + for (i--; i >=3D 0; i--) + clk_hw_unregister_composite(pdata->hw_data->hws[i]); + return ret; + } + } + + h616chip->clk_pdata =3D pdata; + + return 0; +} + +static int h616_pwm_probe(struct platform_device *pdev) +{ + const struct h616_pwm_data *data; + struct device *dev =3D &pdev->dev; + struct h616_pwm_chip *h616chip; + struct pwm_chip *chip; + int ret; + + data =3D of_device_get_match_data(dev); + if (!data) + return -ENODEV; + + chip =3D devm_pwmchip_alloc(dev, data->npwm, sizeof(*h616chip)); + if (IS_ERR(chip)) + return dev_err_probe(dev, PTR_ERR(chip), + "Failed to allocate pwmchip\n"); + + h616chip =3D to_h616_pwm_chip(chip); + h616chip->data =3D data; + h616chip->base =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(h616chip->base)) + return dev_err_probe(dev, PTR_ERR(h616chip->base), + "Failed to get PWM base address\n"); + + h616chip->bus_clk =3D devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(h616chip->bus_clk)) + return dev_err_probe(dev, PTR_ERR(h616chip->bus_clk), + "Failed to get bus clock\n"); + + h616chip->channels =3D devm_kmalloc_array(dev, data->npwm, + sizeof(*(h616chip->channels)), + GFP_KERNEL); + if (!h616chip->channels) + return dev_err_probe(dev, -ENOMEM, + "Failed to allocate %d channels array\n", + data->npwm); + + h616chip->rst =3D devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(h616chip->rst)) + return dev_err_probe(dev, PTR_ERR(h616chip->rst), + "Failed to get reset control\n"); + + chip->ops =3D &h616_pwm_ops; + + ret =3D h616_pwm_init_clocks(pdev, h616chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize clocks\n"); + + for (unsigned int i =3D 0; i < data->npwm; i++) { + struct h616_pwm_channel *chan =3D &h616chip->channels[i]; + struct clk_hw **hw =3D &h616chip->clk_pdata->hw_data->hws[i]; + + chan->pwm_clk =3D devm_clk_hw_get_clk(dev, *hw, NULL); + if (IS_ERR(chan->pwm_clk)) { + ret =3D dev_err_probe(dev, PTR_ERR(chan->pwm_clk), + "Failed to register PWM clock %d\n", i); + goto err_get_clk; + } + chan->mode =3D H616_PWM_MODE_NONE; + } + + ret =3D devm_of_clk_add_hw_provider(dev, h616_pwm_of_clk_get, h616chip); + if (ret) { + dev_err_probe(dev, ret, "Failed to add HW clock provider\n"); + goto err_add_clk_provider; + } + + /* Deassert reset */ + ret =3D reset_control_deassert(h616chip->rst); + if (ret) { + dev_err_probe(dev, ret, "Cannot deassert reset control\n"); + goto err_ctrl_deassert; + } + + ret =3D pwmchip_add(chip); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + goto err_pwm_add; + } + + platform_set_drvdata(pdev, chip); + + return 0; + +err_pwm_add: + reset_control_assert(h616chip->rst); + +err_ctrl_deassert: +err_add_clk_provider: +err_get_clk: + for (int i =3D 0; i < h616chip->clk_pdata->hw_data->num; i++) + clk_hw_unregister_composite(h616chip->clk_pdata->hw_data->hws[i]); + + return ret; +} + +static const struct h616_pwm_data sun50i_h616_pwm_data =3D { + .npwm =3D 6, +}; + +static const struct of_device_id h616_pwm_dt_ids[] =3D { + { + .compatible =3D "allwinner,sun50i-h616-pwm", + .data =3D &sun50i_h616_pwm_data, + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, h616_pwm_dt_ids); + + +static struct platform_driver h616_pwm_driver =3D { + .driver =3D { + .name =3D "h616-pwm", + .of_match_table =3D h616_pwm_dt_ids, + }, + .probe =3D h616_pwm_probe, +}; +module_platform_driver(h616_pwm_driver); + +MODULE_AUTHOR("Richard Genoud "); +MODULE_DESCRIPTION("Allwinner H616 PWM driver"); +MODULE_LICENSE("GPL"); --=20 2.47.3 From nobody Fri Dec 19 15:59:40 2025 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 43D0333EB04 for ; Wed, 17 Dec 2025 08:25:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765959937; cv=none; b=Elvv56ybWgLkpF+L3zO1HlM2b79OJuKxkrEVlw89zu53vU+QaTITsjS4cbwxgWPyaqP0DpySOzgn9FMiEQwoW+NG/57P1ws+1olN9dmpO0c7x32FyUPUVZbOjfJ92a7jpD8NiKedbRV8xLegXYdalU5N52CAJhcdvCNQmYvqdgc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765959937; c=relaxed/simple; bh=evFZwjlzvXHjkqK0R2AFe3sUyEqQ9n7YUfDCJh3pUhI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Bjji38wq9xan2492fxjrUhnsP4RNw4ZdbegwHlztHM7h6W37/nOwdw91+SejTf6vuDxv1M0FVQ4sD8saoy2X1KnRCuGTLFpXKJ97ZB8+kuk92WQBwr//fPRtb4qLD9VgBrZWuMkOVokwTR/YLG96GtwBjUsqguxBgNd/6CWOo0A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=U8AhfwTm; arc=none smtp.client-ip=185.246.84.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="U8AhfwTm" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 83FF21A2278; Wed, 17 Dec 2025 08:25:32 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 5B16D6072F; Wed, 17 Dec 2025 08:25:32 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 0079F119502E3; Wed, 17 Dec 2025 09:25:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1765959929; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=aEM5QbdUXJsRLpURVvs7QvZzTARrGZdkj7npX4h2AHg=; b=U8AhfwTmv42+iyY4wmGOZfLDUHZG27Q5ff7hdfcZu88RprrIP8d0vS1cIF8wM+Oz1sOOGS YmTiHbZx+VgpQnIUhOYQ5hDOUMKZYtq7kzVCcuF8mqw42D/QQgbLzqQDs0vwEDPrlkjLxm jFDNKEZcCg4qucRvrT4fPox5YjszphmoKOzzl0X+iLBg3N5JSuYEnc0k1uKYi5SC25GxE6 NmfRcCJY+dc+eiIaQnFRHnA9SddSCHY01n02PV/eA6XIAMnlwS73SqWqywgFGTp1zzGnJS JJqj+Gir9Aja9kpo2rKbMwKLQkRQu6bhVAVkNp/xwcuAk2UQz24qcEq0HDaO0A== From: Richard Genoud To: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Philipp Zabel Cc: Thomas Petazzoni , 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, Richard Genoud , Joao Schim Subject: [PATCH v2 3/4] arm64: dts: allwinner: h616: add PWM controller Date: Wed, 17 Dec 2025 09:25:03 +0100 Message-ID: <20251217082504.80226-4-richard.genoud@bootlin.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251217082504.80226-1-richard.genoud@bootlin.com> References: <20251217082504.80226-1-richard.genoud@bootlin.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-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" 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. Tested-by: Joao Schim Signed-off-by: Richard Genoud --- .../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/bo= ot/dts/allwinner/sun50i-h616.dtsi index 8d1110c14bad..1c7628a6e4bb 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi @@ -236,6 +236,17 @@ watchdog: watchdog@30090a0 { clocks =3D <&osc24M>; }; =20 + pwm: pwm@300a000 { + compatible =3D "allwinner,sun50i-h616-pwm"; + reg =3D <0x0300a000 0x400>; + clocks =3D <&osc24M>, <&ccu CLK_BUS_PWM>; + clock-names =3D "mod", "bus"; + resets =3D <&ccu RST_BUS_PWM>; + #pwm-cells =3D <3>; + #clock-cells =3D <1>; + status =3D "disabled"; + }; + pio: pinctrl@300b000 { compatible =3D "allwinner,sun50i-h616-pinctrl"; reg =3D <0x0300b000 0x400>; @@ -340,6 +351,42 @@ nand_rb1_pin: nand-rb1-pin { bias-pull-up; }; =20 + /omit-if-no-ref/ + pwm0_pin: pwm0-pin { + pins =3D "PD28"; + function =3D "pwm0"; + }; + + /omit-if-no-ref/ + pwm1_pin: pwm1-pin { + pins =3D "PG19"; + function =3D "pwm1"; + }; + + /omit-if-no-ref/ + pwm2_pin: pwm2-pin { + pins =3D "PH2"; + function =3D "pwm2"; + }; + + /omit-if-no-ref/ + pwm3_pin: pwm3-pin { + pins =3D "PH0"; + function =3D "pwm3"; + }; + + /omit-if-no-ref/ + pwm4_pin: pwm4-pin { + pins =3D "PI14"; + function =3D "pwm4"; + }; + + /omit-if-no-ref/ + pwm5_pin: pwm5-pin { + pins =3D "PA12"; + function =3D "pwm5"; + }; + /omit-if-no-ref/ spi0_pins: spi0-pins { pins =3D "PC0", "PC2", "PC4"; --=20 2.47.3 From nobody Fri Dec 19 15:59:40 2025 Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C917E33B6F8 for ; Wed, 17 Dec 2025 08:25:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.171.202.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765959939; cv=none; b=OCoq7nDLkfRlhTsyRm4tCESisZVgvimB+9Ae/6UfHLMtZm3KP0egbGJb8wS7+dzARp3eVieON+hVuUMEdEVTUXCohPgw/lWz05W9foZuNwIB3J1vDraVCMWlyDOIs0sS370UKwGjMjpuVBQSIzKn1x8rJZrGhg4c7fa7Fu7tG8o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765959939; c=relaxed/simple; bh=8rK8UObtNlXunH3ZCqgjs2acF9kkhu7gtkhWT9H0ndc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Rrl6jNI9o5yyK+nk+xddpCRxTLUV9SF8I9E9jKQYF+p6IkEw5eXlcyzpIirZU4q3SdiySKaPlrGtP3SHm30beZRpbsoQRkZeeKJQZSjCvxhdFbPa92O7v2rVsY8RABQeaD+hPSRfl+blQBfOP5kSPEHeQ1RSB1dE0soPvO9caRQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=tgJq2G6n; arc=none smtp.client-ip=185.171.202.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="tgJq2G6n" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id AF128C1A594; Wed, 17 Dec 2025 08:25:09 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 3CC1C6072F; Wed, 17 Dec 2025 08:25:34 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id DE0C7119502EA; Wed, 17 Dec 2025 09:25:31 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1765959933; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=NBiL1YfDIGET34Id9XIjZ47HfM8LVPK1or9ae7Uci6c=; b=tgJq2G6nPfnc4g0pNVcNkONaQEneuCLW6L6pcrABcxqM37l3tmbyw7q3OWFCPFQljO0HA9 uYwsGyrB+fDFIg9Kvd0CMGwlwqGXzqTBeiN3sZejKHErsttrrBTqc1JdWIRcQll3CJE4LJ X5u3U2/IegyB0rcK+ytLAc6mGfJqqVepWOtSMsHrt+VQYiFaleHZdPWn5AN/JPH+P2fCcv 4O6cUjosS8+ZJwZSELEMF1D7FeDDbAEFaiJaxQJxbSON28z3e591nDBQfl/H4/t22mV5Tq g+8iPup5cv2C5bkj3+fekFM1iepqkCtVLuTM2Jk/QovawtRVl7gdOn6HsC6PZg== From: Richard Genoud To: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Philipp Zabel Cc: Thomas Petazzoni , 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, Richard Genoud Subject: [PATCH v2 4/4] MAINTAINERS: Add entry on Allwinner H616 PWM driver Date: Wed, 17 Dec 2025 09:25:04 +0100 Message-ID: <20251217082504.80226-5-richard.genoud@bootlin.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251217082504.80226-1-richard.genoud@bootlin.com> References: <20251217082504.80226-1-richard.genoud@bootlin.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-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Add myself as maintainer of Allwinner H616 PWM driver and device-tree bindings. Signed-off-by: Richard Genoud --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5b11839cba9d..df2de03e6e38 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -903,6 +903,11 @@ S: Maintained F: Documentation/devicetree/bindings/sound/allwinner,sun50i-h6-dmic.yaml F: sound/soc/sunxi/sun50i-dmic.c =20 +ALLWINNER H616 PWM DRIVER +M: Richard Genoud +S: Maintained +F: drivers/pwm/pwm-sun50i-h616.c + ALLWINNER HARDWARE SPINLOCK SUPPORT M: Wilken Gottwalt S: Maintained --=20 2.47.3