[PATCH v11 0/5] Introduce Allwinner H616 PWM controller
James Hilliard
james.hilliard1 at gmail.com
Tue Nov 3 00:40:25 PST 2026
The H616 controller has six PWM outputs, paired as 0/1, 2/3 and 4/5.
Each pair shares a source mux, gate and power-of-two divider. Each channel
adds a prescaler and waveform generator, with a bypass for clock output.
The bypass can supply the input clock for the co-packaged AC200/AC300 PHY.
The series separates PWM waveform support and exported bypass clocks into
distinct patches. Internal CCF pair clocks model
the shared hardware, while PWM request/free and clock prepare/unprepare
arbitrate ownership of each output. Active PWM outputs and prepared bypass
clocks protect their shared rate against incompatible sibling requests.
Firmware-active outputs are preserved until a consumer changes them.
Hardware-enable checks protect their shared gate and rate even before
Linux has an owner for them; there is no driver-local unused-output sweep.
A conflicting shared-rate request is rejected while an inherited output
remains enabled. In particular, assigned-clock-rates cannot override an
incompatible firmware-active output. Firmware must leave a compatible
configuration or disable the conflicting outputs for that request to work.
The clock-provider interface reports the actual pair rate and makes rate
protection visible to CCF. In contrast, drivers/clk/clk-pwm.c advertises a
fixed frequency without checking the rounded PWM result: a 42 ns, 21 ns
request selects 24 MHz bypass when available, but can become 25 MHz when
a sibling holds the pair at 100 MHz. The direct provider still permits
CCF rounding; it does not guarantee that every requested rate is available.
The bounded TLA+ models and accompanying implementation checks are
available with their assumptions and limitations at:
https://github.com/jameshilliard/linux-h616-pwm-tla
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
Assisted-by: Codex:gpt-5
---
Changes v10 -> v11:
- rebase onto pwm/for-next at 9ad5602d4943
- compare candidate waveforms before nanosecond rounding, retaining
period-first ordering across pair rates and bypass half-cycles
- protect the pair rate across waveform readback so an exported-clock
consumer cannot mix a new register configuration with a stale rate
- use the provider-owned handle for the temporary hold, separately from
the PWM handles used to change the rate
- Link to v10:
https://patch.msgid.link/20260922-h616-pwm-v8-v10-0-dccebbe1aabf@gmail.com
Changes v9 -> v10:
- validate the single clock-specifier argument before reading its channel
index (reported by Sashiko)
- Link to v9:
https://patch.msgid.link/20260921-h616-pwm-v8-v9-0-37bec6201ff5@gmail.com
Changes v8 -> v9:
- split PWM waveform support and exported bypass-clock support into
separate patches, retaining internal CCF pair clocks in the PWM patch
- preserve unclaimed firmware outputs instead of stopping them at
sync_state(); replace the empty disable_unused callback with
CLK_IGNORE_UNUSED
- embed the fixed six-channel topology in the pwm_chip allocation, remove
single-variant match data, and use a channel-indexed clock lookup
- store bypass clock state directly in each channel and recover the channel
from its clk_hw without a separate wrapper
- avoid module self-pinning by acquiring internal clock consumer handles
only while a PWM is requested; use provider-owned handles for notifiers
and rate readback
- use scoped locking and protect rate_exclusive with the register lock,
keeping CCF operations outside it; remove ownership WARN_ON_ONCE calls
- explain unsupported pulse/dead-zone reads and rename the waveform
conversion helper to match its callback
- pre-encode waveform registers, isolate update preparation and rollback,
combine candidate comparison/selection, and remove redundant rounding
corrections and private waveform-field checks
- normalize polarity readback, use ordinary boolean waveform flags, and
remove the redundant bypass recalc_rate callback
- clarify the clk-pwm rounding limitation and correct the v7 topology
description
- Link to v8:
https://patch.msgid.link/20260804-h616-pwm-v8-v8-0-db37ab8624ae@gmail.com
Changes v7 -> v8:
- require both the module and bus input clocks for H616
- clear the complete two-bit clock-source selector field
- leave rate and period registers untouched when disabling a channel
- distinguish PWM-owned bypass signals from clock-owned bypass outputs
- round shortest-period requests using the fastest achievable clock
- encode constant levels without overflowing the 16-bit active-cycle field
- require #clock-cells for the H616 clock provider (reported by Sashiko)
- allocate clock topology state per device, use the fixed two-parent map,
and use managed clock registration
(reported by Sashiko and suggested by Philipp)
- deassert reset before registering clocks and keep the reset handle local
(reported by Sashiko and suggested by Philipp)
- arbitrate channel ownership in clock prepare/unprepare and select bypass
only while the clock output is enabled (reported by Sashiko)
- roll back failed PWM requests and serialize channel release
(reported by Sashiko)
- protect the shared pair source while either sibling channel is active
(reported by Sashiko)
- preserve the 65536-tick duty intermediate before polarity conversion
(reported by Sashiko)
- keep the hardware-waveform state within the PWM core storage limit
- clarify that either co-packaged AC200 or AC300 PHY uses the bypass clock
- drop driver Tested-by tags after the clock implementation was reworked
- quantize inverted waveforms in hardware ticks and report their
physical offset
- force released channels off so stale waveforms cannot restart with a
sibling
- track the active and pending cycle for the period-update handshake,
adapt the sleep interval to bound MMIO polling on long periods, and
honor the PCLK rate constraint
- quiesce rate, polarity and bypass transitions before reprogramming the
output
- clear unsupported pulse mode when programming periodic waveforms
- describe the bypass clock cells and simplify the fixed parent topology
- avoid resetting active pair registers during probe
- model only shared pair clocks in CCF and program per-channel dividers
directly
- make pair-rate changes transactional and hold exclusivity only while
an output is active
- implement formal waveform rounding across all parent, divider and
bypass choices
- preserve firmware-active outputs through generic unused-clock cleanup,
stop any still-unclaimed channels at sync_state(), reconcile empty pair
gates through CCF, and reject pair retuning while either output is
active
- clear dormant dead-zone state when both pair outputs are disabled,
while rejecting active dead-zone and pulse modes which the PWM API
cannot represent
- correct the hardware topology to show the pair gate before div_m
- Link to v7:
https://patch.msgid.link/20260703152215.192859-1-richard.genoud@bootlin.com
Changes v6 -> v7:
- Cc Common Clock Framework maintainers (suggested by Uwe)
- add missing static before SUN8I_PWM_X_BYPASS_GATE
- reorder code in probe to fix potential lifecycle issues
- set pwmcc_data[i].parent_names to NULL in sun8i_pwm_unregister_clk
Changes v5 -> v6:
- remove trailing junk after the patch 4 commit message
- remove Tested-by tags where they do not apply
Changes v4 -> v5:
- fix bypass handling for channels greater than 1
- add colons to clarify two debug messages
- switch from H616 to sun8i prefixes in code, filenames and module names
- fix consistency issues in macro parameters
- rename confusing macros
Changes v3 -> v4:
- gather Acked-by and Tested-by tags
- fix a pointer-to-integer cast size warning on ARC
- add a managed action for clk_hw_unregister_composite
(suggested by Philipp)
- remove the unused pwm_remove function (suggested by Philipp)
Changes v2 -> v3:
- use U32_MAX instead of defining UINT32_MAX
- document U32_MAX usage in clk_round_rate()
- define clk_table_div_m using macros
- fix formatting
- correct the parent clock order
- simplify code using scoped_guard()
- add a missing const qualifier and rename to_h616_pwm_chip() to
h616_pwm_from_chip()
- add missing error messages and remove redundant ones
- rename cnt to period_ticks and duty_cnt to duty_ticks
- fix PWM_PERIOD_MAX
- add the remove callback
- replace DIV_ROUND_CLOSEST_ULL with DIV_ROUND_UP_ULL
- add H616 prefixes
- protect _reg in macros
- switch from apply/get_state to waveforms
- shrink struct h616_pwm_channel
Changes v1 -> v2:
- add missing headers
- remove MODULE_ALIAS (suggested by Krzysztof)
- use the sun4i-pwm binding instead of adding a new one
(suggested by Krzysztof)
- retrieve parent clocks from the device tree
- change num_parents to unsigned int
To: Uwe Kleine-König <ukleinek at kernel.org>
To: Rob Herring <robh at kernel.org>
To: Krzysztof Kozlowski <krzk+dt at kernel.org>
To: Conor Dooley <conor+dt at kernel.org>
To: Chen-Yu Tsai <wens at kernel.org>
To: Jernej Skrabec <jernej.skrabec at gmail.com>
To: Samuel Holland <samuel at sholland.org>
To: Maxime Ripard <mripard at kernel.org>
To: Richard Genoud <richard.genoud at bootlin.com>
To: Philipp Zabel <p.zabel at pengutronix.de>
Cc: linux-pwm at vger.kernel.org
Cc: devicetree at vger.kernel.org
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-sunxi at lists.linux.dev
Cc: linux-kernel at vger.kernel.org
---
Richard Genoud (5):
dt-bindings: pwm: allwinner: add h616 pwm compatible
pwm: sun8i: Add H616 PWM support
pwm: sun8i: export the bypass outputs as clocks
arm64: dts: allwinner: h616: add PWM controller
MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver
.../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 33 +-
MAINTAINERS | 5 +
arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi | 47 +
drivers/pwm/Kconfig | 12 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-sun8i.c | 1317 ++++++++++++++++++++
6 files changed, 1414 insertions(+), 1 deletion(-)
---
base-commit: 9ad5602d4943635ea2b3b73d1a720161f88da1a1
change-id: 20260803-h616-pwm-v8-e21a92470923
Best regards,
--
James Hilliard <james.hilliard1 at gmail.com>
More information about the linux-arm-kernel
mailing list