From: Richard GENOUD <richard.genoud@bootlin.com>
To: Paul Kocialkowski <paulk@sys-base.io>
Cc: "Uwe Kleine-König" <u.kleine-koenig@baylibre.com>,
"Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Chen-Yu Tsai" <wens@csie.org>,
"Jernej Skrabec" <jernej.skrabec@gmail.com>,
"Samuel Holland" <samuel@sholland.org>,
"Philipp Zabel" <p.zabel@pengutronix.de>,
"Thomas Petazzoni" <thomas.petazzoni@bootlin.com>,
"John Stultz" <jstultz@google.com>,
"Joao Schim" <joao@schimsalabim.eu>,
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
Subject: Re: [PATCH v4 2/4] pwm: sun50i: Add H616 PWM support
Date: Thu, 16 Apr 2026 09:53:27 +0200 [thread overview]
Message-ID: <26f86593-a561-497e-bd47-b9cbda67bbf4@bootlin.com> (raw)
In-Reply-To: <adfiPo4Jq1IRMM0h@shepard>
Hi Paul,
Le 09/04/2026 à 19:30, Paul Kocialkowski a écrit :
> Hi Richard,
>
> On Thu 05 Mar 26, 10:19, Richard Genoud wrote:
>> +/* PWM IRQ Enable Register */
>> +#define H616_PWM_IER 0x0
>
> I think it would make more sense to keep the full register names from the
> manual after the suffix and stick to them. It makes things easier when
> comparing the code with documentation or the reference implementation.
>
> So something like SUN8I_PWM_PIER here.
Ok, that make sense.
>
>> +
>> +/* PWM IRQ Status Register */
>> +#define H616_PWM_ISR 0x4
>> +
>> +/* PWM Capture IRQ Enable Register */
>> +#define H616_PWM_CIER 0x10
>> +
>> +/* PWM Capture IRQ Status Register */
>> +#define H616_PWM_CISR 0x14
>> +
>> +/* PWMCC Pairs Clock Configuration Registers */
>> +#define H616_PWM_XY_CLK_CR(pair) (0x20 + ((pair) * 0x4))
>> +#define H616_PWM_XY_CLK_CR_SRC_SHIFT 7
>> +#define H616_PWM_XY_CLK_CR_SRC_MASK 1
>> +#define H616_PWM_XY_CLK_CR_GATE_BIT 4
>> +#define H616_PWM_XY_CLK_CR_BYPASS_BIT(chan) ((chan) % 2 + 5)
>> +#define H616_PWM_XY_CLK_CR_DIV_M_SHIFT 0
>> +
>> +/* PWMCC Pairs Dead Zone Control Registers */
>> +#define H616_PWM_XY_DZ(pair) (0x30 + ((pair) * 0x4))
>> +
>> +/* PWM Enable Register */
>> +#define H616_PWM_ENR 0x40
>> +#define H616_PWM_ENABLE(x) BIT(x)
>> +
>> +/* PWM Capture Enable Register */
>> +#define H616_PWM_CER 0x44
>> +
>> +/* PWM Control Register */
>> +#define H616_PWM_CTRL_REG(chan) (0x60 + (chan) * 0x20)
>
> You're sometimes calling the register offset _REG and sometimes not.
> Both options are fine but you need to keep it consistent across the whole
> definitions. I would be enclined to not use it after using the register names
> coming from the manual as suggested above.
I see what you mean, so H616_PWM_CTRL_REG() would become SUN8I_PWM_PCR()
>
> Also you're sometimes using "chan", sometimes "ch" for the argument to the
> register macros. This is inconsistent and you might as well just use "c"
> everywhere so it doesn't take too much space.
Indeed.
>
>> +#define H616_PWM_CTRL_PRESCAL_K_SHIFT 0
>> +#define H616_PWM_CTRL_PRESCAL_K_WIDTH 8
>> +#define H616_PWM_CTRL_ACTIVE_STATE BIT(8)
>> +
>> +/* PWM Period Register */
>> +#define H616_PWM_PERIOD_REG(ch) (0x64 + (ch) * 0x20)
>> +#define H616_PWM_PERIOD_MASK GENMASK(31, 16)
>> +#define H616_PWM_DUTY_MASK GENMASK(15, 0)
>> +#define H616_PWM_REG_PERIOD(reg) (FIELD_GET(H616_PWM_PERIOD_MASK, reg) + 1)
>> +#define H616_PWM_REG_DUTY(reg) FIELD_GET(H616_PWM_DUTY_MASK, reg)
>> +#define H616_PWM_PERIOD(prd) FIELD_PREP(H616_PWM_PERIOD_MASK, (prd) - 1)
>> +#define H616_PWM_DUTY(dty) FIELD_PREP(H616_PWM_DUTY_MASK, dty)
>> +#define H616_PWM_PERIOD_MAX (FIELD_MAX(H616_PWM_PERIOD_MASK) + 1)
>
> Using REG as a prefix feels a bit confusing here. I would rather see:
> #define SUN8I_PWM_PPR(c) (0x64 + (c) * 0x20)
> #define SUN8I_PWM_PPR_PERIOD(p) FIELD_PREP(...)
> #define SUN8I_PWM_PPR_PERIOD_VALUE(r) FIELD_GET(...)
> #define SUN8I_PWN_PPR_PERIOD_MAX FIELD_MAX(...)
> #define SUN8I_PWM_PPR_DUTY(d) FIELD_PREP(...)
> #define SUN8I_PWM_PPR_DUTY_VALUE(r) FIELD_GET(...)
That's right, that would be less confusing.
>
>> +
>> +/* PWM Count Register */
>> +#define H616_PWM_CNT_REG(x) (0x68 + (x) * 0x20)
>> +
>> +/* PWM Capture Control Register */
>> +#define H616_PWM_CCR(x) (0x6c + (x) * 0x20)
>> +
>> +/* PWM Capture Rise Lock Register */
>> +#define H616_PWM_CRLR(x) (0x70 + (x) * 0x20)
>> +
>> +/* PWM Capture Fall Lock Register */
>> +#define H616_PWM_CFLR(x) (0x74 + (x) * 0x20)
>> +
>> +#define H616_PWM_PAIR_IDX(chan) ((chan) >> 2)
>> +
>> +/*
>> + * Block diagram of the PWM clock controller:
>> + *
>> + * _____ ______ ________
>> + * OSC24M --->| | | | | |
>> + * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> H616_PWM_clock_src_xy
>> + * |_____| |______| |________|
>> + * ________
>> + * | |
>> + * +->| /div_k |---> H616_PWM_clock_x
>> + * | |________|
>> + * | ______
>> + * | | |
>> + * +-->| Gate |----> H616_PWM_bypass_clock_x
>> + * | |______|
>> + * H616_PWM_clock_src_xy ----+ ________
>> + * | | |
>> + * +->| /div_k |---> H616_PWM_clock_y
>> + * | |________|
>> + * | ______
>> + * | | |
>> + * +-->| Gate |----> H616_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.
>> + *
>> + * H616_PWM_clock_x/y serve for the PWM purpose.
>> + * H616_PWM_bypass_clock_x/y serve for the clock-provider purpose.
>> + *
>> + */
>> +
>> +/*
>> + * Table used for /div_m (diviser before obtaining H616_PWM_clock_src_xy)
>> + * It's actually CLK_DIVIDER_POWER_OF_TWO, but limited to /256
>> + */
>> +#define CLK_TABLE_DIV_M_ENTRY(i) { \
>> + .val = (i), .div = 1 << (i) \
>> +}
>> +
>> +static const struct clk_div_table clk_table_div_m[] = {
>> + CLK_TABLE_DIV_M_ENTRY(0),
>> + CLK_TABLE_DIV_M_ENTRY(1),
>> + CLK_TABLE_DIV_M_ENTRY(2),
>> + CLK_TABLE_DIV_M_ENTRY(3),
>> + CLK_TABLE_DIV_M_ENTRY(4),
>> + CLK_TABLE_DIV_M_ENTRY(5),
>> + CLK_TABLE_DIV_M_ENTRY(6),
>> + CLK_TABLE_DIV_M_ENTRY(7),
>> + CLK_TABLE_DIV_M_ENTRY(8),
>> + { /* sentinel */ }
>> +};
>> +
>> +#define H616_PWM_XY_SRC_GATE(_pair, _reg) \
>> +struct clk_gate gate_xy_##_pair = { \
>> + .reg = (void *)(_reg), \
>> + .bit_idx = H616_PWM_XY_CLK_CR_GATE_BIT, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_gate_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_XY_SRC_MUX(_pair, _reg) \
>> +struct clk_mux mux_xy_##_pair = { \
>> + .reg = (void *)(_reg), \
>> + .shift = H616_PWM_XY_CLK_CR_SRC_SHIFT, \
>> + .mask = H616_PWM_XY_CLK_CR_SRC_MASK, \
>> + .flags = CLK_MUX_ROUND_CLOSEST, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_mux_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_XY_SRC_DIV(_pair, _reg) \
>> +struct clk_divider rate_xy_##_pair = { \
>> + .reg = (void *)(_reg), \
>> + .shift = H616_PWM_XY_CLK_CR_DIV_M_SHIFT, \
>> + .table = clk_table_div_m, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_divider_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_X_DIV(_idx, _reg) \
>> +struct clk_divider rate_x_##_idx = { \
>> + .reg = (void *)(_reg), \
>> + .shift = H616_PWM_CTRL_PRESCAL_K_SHIFT, \
>> + .width = H616_PWM_CTRL_PRESCAL_K_WIDTH, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_divider_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_X_BYPASS_GATE(_idx) \
>> +struct clk_gate gate_x_bypass_##_idx = { \
>> + .reg = (void *)H616_PWM_ENR, \
>> + .bit_idx = _idx, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_gate_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_XY_CLK_SRC(_pair, _reg) \
>> + static H616_PWM_XY_SRC_MUX(_pair, _reg); \
>> + static H616_PWM_XY_SRC_GATE(_pair, _reg); \
>> + static H616_PWM_XY_SRC_DIV(_pair, _reg)
>> +
>> +#define H616_PWM_X_CLK(_idx) \
>> + static H616_PWM_X_DIV(_idx, H616_PWM_CTRL_REG(_idx))
>> +
>> +#define H616_PWM_X_BYPASS_CLK(_idx) \
>> + H616_PWM_X_BYPASS_GATE(_idx)
>> +
>> +#define REF_CLK_XY_SRC(_pair) \
>> + { \
>> + .name = "pwm-clk-src" #_pair, \
>> + .mux_hw = &mux_xy_##_pair.hw, \
>> + .gate_hw = &gate_xy_##_pair.hw, \
>> + .rate_hw = &rate_xy_##_pair.hw, \
>> + }
>> +
>> +#define REF_CLK_X(_idx, _pair) \
>> + { \
>> + .name = "pwm-clk" #_idx, \
>> + .parent_names = (const char *[]){ "pwm-clk-src" #_pair }, \
>> + .num_parents = 1, \
>> + .rate_hw = &rate_x_##_idx.hw, \
>> + .flags = CLK_SET_RATE_PARENT, \
>> + }
>> +
>> +#define REF_CLK_BYPASS(_idx, _pair) \
>> + { \
>> + .name = "pwm-clk-bypass" #_idx, \
>> + .parent_names = (const char *[]){ "pwm-clk-src" #_pair }, \
>> + .num_parents = 1, \
>> + .gate_hw = &gate_x_bypass_##_idx.hw, \
>> + .flags = CLK_SET_RATE_PARENT, \
>> + }
>> +
>> +/*
>> + * H616_PWM_clock_src_xy generation:
>> + * _____ ______ ________
>> + * OSC24M --->| | | | | |
>> + * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> H616_PWM_clock_src_xy
>> + * |_____| |______| |________|
>> + */
>> +H616_PWM_XY_CLK_SRC(01, H616_PWM_XY_CLK_CR(0));
>> +H616_PWM_XY_CLK_SRC(23, H616_PWM_XY_CLK_CR(1));
>> +H616_PWM_XY_CLK_SRC(45, H616_PWM_XY_CLK_CR(2));
>> +
>> +/*
>> + * H616_PWM_clock_x_div generation:
>> + * ________
>> + * | | H616_PWM_clock_x/y
>> + * H616_PWM_clock_src_xy --->| /div_k |--------------->
>> + * |________|
>> + */
>> +H616_PWM_X_CLK(0);
>> +H616_PWM_X_CLK(1);
>> +H616_PWM_X_CLK(2);
>> +H616_PWM_X_CLK(3);
>> +H616_PWM_X_CLK(4);
>> +H616_PWM_X_CLK(5);
>> +
>> +/*
>> + * H616_PWM_bypass_clock_xy generation:
>> + * ______
>> + * | |
>> + * H616_PWM_clock_src_xy ---->| Gate |-------> H616_PWM_bypass_clock_x
>> + * |______|
>> + *
>> + * The gate is actually H616_PWM_ENR register.
>> + */
>> +H616_PWM_X_BYPASS_CLK(0);
>> +H616_PWM_X_BYPASS_CLK(1);
>> +H616_PWM_X_BYPASS_CLK(2);
>> +H616_PWM_X_BYPASS_CLK(3);
>> +H616_PWM_X_BYPASS_CLK(4);
>> +H616_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[] = {
>> + 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 */ }
>> +};
>
> We'll probably need a way to tie these static definitions to a particular
> instance of the unit for a given chip. But I guess that can be done later
> when adding more chips to the driver.
>
> I'm not too versed in the clk and pwm APIs but the rest generally looks good
> to me.
Thanks!
>
> All the best,
>
> Paul
>
next prev parent reply other threads:[~2026-04-16 7:53 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-05 9:19 [PATCH v4 0/4] Introduce Allwinner H616 PWM controller Richard Genoud
2026-03-05 9:19 ` [PATCH v4 1/4] dt-bindings: pwm: allwinner: add h616 pwm compatible Richard Genoud
2026-03-05 9:19 ` [PATCH v4 2/4] pwm: sun50i: Add H616 PWM support Richard Genoud
2026-04-09 17:30 ` Paul Kocialkowski
2026-04-16 7:53 ` Richard GENOUD [this message]
2026-04-13 12:39 ` bigunclemax
2026-04-13 15:58 ` Paul Kocialkowski
2026-04-16 7:57 ` Richard GENOUD
2026-03-05 9:19 ` [PATCH v4 3/4] arm64: dts: allwinner: h616: add PWM controller Richard Genoud
2026-03-05 9:19 ` [PATCH v4 4/4] MAINTAINERS: Add entry on Allwinner H616 PWM driver Richard Genoud
2026-03-23 16:27 ` [PATCH v4 0/4] Introduce Allwinner H616 PWM controller Richard GENOUD
2026-03-25 7:14 ` Uwe Kleine-König
2026-04-09 17:16 ` Paul Kocialkowski
2026-04-16 8:22 ` Richard GENOUD
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=26f86593-a561-497e-bd47-b9cbda67bbf4@bootlin.com \
--to=richard.genoud@bootlin.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=jernej.skrabec@gmail.com \
--cc=joao@schimsalabim.eu \
--cc=jstultz@google.com \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=linux-sunxi@lists.linux.dev \
--cc=p.zabel@pengutronix.de \
--cc=paulk@sys-base.io \
--cc=robh@kernel.org \
--cc=samuel@sholland.org \
--cc=thomas.petazzoni@bootlin.com \
--cc=u.kleine-koenig@baylibre.com \
--cc=wens@csie.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox