[PATCH v4 3/4] clk: spacemit: Add clock support for Spacemit K1 SoC
Haylen Chu
heylenay at 4d2.org
Sat Feb 22 01:57:12 PST 2025
On Fri, Feb 21, 2025 at 03:10:46PM -0600, Alex Elder wrote:
> On 2/16/25 5:34 AM, Haylen Chu wrote:
> > On Thu, Feb 13, 2025 at 10:04:10PM -0600, Alex Elder wrote:
> > > On 1/3/25 3:56 PM, Haylen Chu wrote:
> > > > The clock tree of K1 SoC contains three main types of clock hardware
> > > > (PLL/DDN/MIX) and is managed by several independent controllers in
> > > > different SoC parts (APBC, APBS and etc.), thus different compatible
> > > > strings are added to distinguish them.
> > > >
> > > > Some controllers may share IO region with reset controller and other low
> > > > speed peripherals like watchdog, so all register operations are done
> > > > through regmap to avoid competition.
> > > >
> > > > Signed-off-by: Haylen Chu <heylenay at 4d2.org>
> > >
> > > This is a really big patch (over 3000 lines), and a fairly large
> > > amount of code to review. But I've given it a really thorough
> > > read and I have a *lot* of review comments for you to consider.
> >
> > Thanks for the detailed review!
> >
> > > First, a few top-level comments.
> > > - This driver is very comprehensive. It represents essentially
> > > *all* of the clocks in the tree diagram shown here:
> > > https://developer.spacemit.com/resource/file/images?fileName=DkWGb4ed7oAziVxE6PIcbjTLnpd.png
> > > (I can tell you what's missing but I don't think it matters.)
> > > - In almost all cases, the names of the clocks match the names
> > > shown in that diagram, which is very helpful.
> > > - All of the clocks are implemented using "custom" clock
> > > implementations. I'm fairly certain that almost all of
> > > them can use standard clock framework types instead
> > > (fixed-rate, fixed-factor, fractional-divider, mux, and
> > > composite). But for now I think there are other things
> > > more important to improve.
> >
> > These three types of clocks are originally adapted from the vendor
> > driver, although the mix type has been largely rewritten during the
> > revisions.
> >
> > If the custom types don't cause big problems, I'd like to see the driver
> > merged first and we can gradually convert them to the more general ones,
> > which avoids blocking other SoC drivers.
>
> Understood. Actually I have some other input that suggests
> repreenting things differently, but let me respond to your
> message (this one) first.
>
> . . .
>
> > > What do APBS, MPMU, APBC, and APMU stand for and represent?
> >
> > APMU and MPMU are abbreviated from Application/Main Power Management
> > Unit, as mentioned earlier in my reply to the dt-binding things. APBC
> > means APB Bus Clock Unit. APBS stands for APB SPARE and I'm not sure
> > about the precise meaning.
> >
> > These information seems important but I'm not sure about where they
> > should be documented. I could mention it in the cover letter or commit
> > message in next version.
>
> I guess knowing what they stand for is something, but knowing
> why they're separated and what function each plays is more
> important. Don't worry about this for now.
>
> > > It seems each has its own clock controller, but can you
> > > offer any explanation about why they're divided that way?
> >
> > Sadly no, I have no idea why the clock bits are divided into several
> > MMIO regions, which really complicates binding.
>
> . . .
>
> > > > +/* APMU register offset */
> > > > +#define APMU_CCI550_CLK_CTRL 0x300
> > > > +#define APMU_CPU_C0_CLK_CTRL 0x38C
> > > > +#define APMU_CPU_C1_CLK_CTRL 0x390
>
> I didn't notice previously, but the above three are also
> duplicated a little later.
>
> > > > +#define APMU_JPG_CLK_RES_CTRL 0x20
> > > > +#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x24
> > > > +#define APMU_ISP_CLK_RES_CTRL 0x38
> > > > +#define APMU_LCD_CLK_RES_CTRL1 0x44
> > > > +#define APMU_LCD_SPI_CLK_RES_CTRL 0x48
> > > > +#define APMU_LCD_CLK_RES_CTRL2 0x4c
> > > > +#define APMU_CCIC_CLK_RES_CTRL 0x50
> > > > +#define APMU_SDH0_CLK_RES_CTRL 0x54
> > > > +#define APMU_SDH1_CLK_RES_CTRL 0x58
> > > > +#define APMU_USB_CLK_RES_CTRL 0x5c
> > > > +#define APMU_QSPI_CLK_RES_CTRL 0x60
> > >
> > > APMU_USB_CLK_RES_CTRL is duplicated below.
> >
> > Thanks, will remove one along with other duplication.
> >
> > For the unused offsets, I prefer to keep the documented ones for future
> > completion of the driver. I don't think it's a large burden.
>
> That's fine. Almost everything is used, so it does no real
> harm to include the few that aren't (yet).
>
> > > > +#define APMU_USB_CLK_RES_CTRL 0x5c
> > > > +#define APMU_DMA_CLK_RES_CTRL 0x64
> > > > +#define APMU_AES_CLK_RES_CTRL 0x68
> > > > +#define APMU_VPU_CLK_RES_CTRL 0xa4
> > > > +#define APMU_GPU_CLK_RES_CTRL 0xcc
> > > > +#define APMU_SDH2_CLK_RES_CTRL 0xe0
>
> . . .
>
> > > I'm going to say more about this below, but these definitions can
> > > be simplified a lot. For example, the first one could be:
> > > CCU_PLL_RATE(2457600000UL, 0x0050dd64, 0x330ccccd),
> > >
> > > > +static const struct ccu_pll_rate_tbl pll1_rate_tbl[] = {
> > > > + CCU_PLL_RATE(2457600000UL, 0x64, 0xdd, 0x50, 0x00, 0x33, 0x0ccccd),
> > > > +};
> > > > +
> > > > +static const struct ccu_pll_rate_tbl pll2_rate_tbl[] = {
> > > > + CCU_PLL_RATE(3000000000UL, 0x66, 0xdd, 0x50, 0x00, 0x3f, 0xe00000),
> > > > +};
> > > > +
> > > > +static const struct ccu_pll_rate_tbl pll3_rate_tbl[] = {
> > > > + CCU_PLL_RATE(3000000000UL, 0x66, 0xdd, 0x50, 0x00, 0x3f, 0xe00000),
> > > > + CCU_PLL_RATE(3200000000UL, 0x67, 0xdd, 0x50, 0x00, 0x43, 0xeaaaab),
> > > > + CCU_PLL_RATE(2457600000UL, 0x64, 0xdd, 0x50, 0x00, 0x33, 0x0ccccd),
> > > > +};
> > > > +
> > >
> > > Therre are only three PLL clocks, defined next. All share the same
> > > lock register offset (MPMU_POSR), so that could be factored out.
> >
> > I'm not sure how to factor the offset out, could you please explain more
> > about it? By coding the offset in the macro or something else?
> >
> > I'd like to keep it as is, since PLL status in future SpacemiT SoCs are
> > likely to locate in different registers. Factoring the offset out won't
> > save a lot of characters, either.
>
> Keep it the way you have it. Factoring it out doesn't help much.
>
> > > > +static CCU_PLL_DEFINE(pll1, "pll1", pll1_rate_tbl,
> > > > + APB_SPARE1_REG, APB_SPARE2_REG, APB_SPARE3_REG,
> > > > + MPMU_POSR, POSR_PLL1_LOCK, CLK_SET_RATE_GATE);
>
> . . .
>
> > > The next clock is weird, and it's the only one of its kind. It is not
> > > represented in the clock tree diagram. It is a "factor 1" clock (so it
> > > just passes the parent's rate through), and has no gate. Do you know
> > > why it's defined? It is used only as one of the MPMU parent clocks.
> > > Why isn't just the pll1_d7 clock used in its place?
> >
> > It is represented in the diagram. The photo version of the diagram seems
> > hard to search so I will ask the vendor to publish a PDF version if
> > possible.
> >
> > As the definition involves no hardware bits, I guess it's actually an
> > alias listed to keep the tree structure in similar form. Will confirm
> > this with the vendor.
>
> This is what I meant by "not represented in the diagram." It seems
> like it's just a place holder, more or less. I think this is the
> only one defined here that's like that.
Yes, it's really a placeholder, as confirmed in another reply sent
earlier. Will remove it in the next version.
> > > > +static CCU_FACTOR_DEFINE(pll1_d7_351p08, "pll1_d7_351p08", CCU_PARENT_HW(pll1_d7),
> > > > + 1, 1);
>
> . . .
>
> > > > +static struct ccu_ddn_tbl slow_uart1_tbl[] = {
> > > > + { .num = 125, .den = 24 },
> > > > +};
> > > > +static struct ccu_ddn_tbl slow_uart2_tbl[] = {
> > > > + { .num = 6144, .den = 960 },
> > > > +};
> > >
> > > I'll note here that this "slow_uart" gate is defined separately
> > > from the two slow UART DDN clocks below. That's different from
> > > some other gated clocks.
> >
> > "slow_uart" is a gate that controlls both slow_uart1_14p7 and
> > slow_uart2_48. Enabling any one of these two clocks requires slow_uart
> > to ungate.
> >
> > I didn't find a good method to describe the structure under CCF (I'm
> > really new to it), thus listed it out and applied the CLK_IGNORE_UNUSED
> > trick. Any suggesions on this will be appreciated, thanks.
>
> Right now I have no suggestions. Let's see the next version
> of the patches and we can maybe revisit this.
Okay.
> > > > +static CCU_GATE_DEFINE(slow_uart, "slow_uart", CCU_PARENT_NAME(osc),
> > > > + MPMU_ACGR,
> > > > + BIT(1), BIT(1), 0, CLK_IGNORE_UNUSED);
> > >
> > > What follows are the only two DDN clocks defined. Both define
> > > a "table" of numerator/denominator pairs, but in both cases,
> > > the table has only one entry.
> > >
> > > Given that, why not simply encode the numerator and denominator
> > > in the ccu_info structure for each of these DDN clock instances?
> >
> > DDN types are M/N clocks, just like clk-fractional-divider. Hardcoded
> > tables (even with only one entry) work because their only consumers are
> > UARTs, which require a relatively fixed frequency. This will prevent us
> > from generating precise clock if we want the UART to operate under a
> > different baudrate.
> >
> > I'll cover more about this later.
>
> My point was that there's no need to define a "table" if there's
> only one entry.
You're right. I've decided to rewrite the DDN part in next version,
eliminating these tables or any type of hard-coded configuration.
> > > > +static CCU_DDN_DEFINE(slow_uart1_14p74, "slow_uart1_14p74", pll1_d16_153p6,
> > > > + &uart_ddn_mask_info, slow_uart1_tbl,
> > > > + MPMU_SUCCR, 0);
> > > > +static CCU_DDN_DEFINE(slow_uart2_48, "slow_uart2_48", pll1_d4_614p4,
> > > > + &uart_ddn_mask_info, slow_uart2_tbl,
> > > > + MPMU_SUCCR_1, 0);
> > > > +
> > > > +static CCU_GATE_DEFINE(wdt_clk, "wdt_clk", CCU_PARENT_HW(pll1_d96_25p6),
> > > > + MPMU_WDTPCR,
> > > > + BIT(1), BIT(1), 0x0,
> > > > + 0);
> > > > +
> > >
> > > I couldn't find the "ripc_clk" on the clock tree diagram. It is
> > > never used elsewhere, so I think this definition can go away.
> >
> > I'm not sure whether the ripc_clk doesn't exist or it's just missing in
> > both datasheet and clock tree diagram. Will confirm with the vendor.
> >
> > > > +static CCU_GATE_DEFINE(ripc_clk, "ripc_clk", CCU_PARENT_NAME(vctcxo_24m),
> > > > + MPMU_RIPCCR,
> > > > + 0x3, 0x3, 0x0,
> > > > + 0);
> > > > +
> > > > +static CCU_GATE_FACTOR_DEFINE(i2s_sysclk, "i2s_sysclk", CCU_PARENT_HW(pll1_d16_153p6),
> > > > + MPMU_ISCCR,
> > > > + BIT(31), BIT(31), 0x0, 50, 1,
> > > > + 0);
> > > > +static CCU_GATE_FACTOR_DEFINE(i2s_bclk, "i2s_bclk", CCU_PARENT_HW(i2s_sysclk),
> > > > + MPMU_ISCCR,
> > > > + BIT(29), BIT(29), 0x0, 1, 1,
> > > > + 0);
> > > > +
> > > > +static const struct clk_parent_data apb_parents[] = {
> > > > + CCU_PARENT_HW(pll1_d96_25p6),
> > > > + CCU_PARENT_HW(pll1_d48_51p2),
> > > > + CCU_PARENT_HW(pll1_d96_25p6),
> > > > + CCU_PARENT_HW(pll1_d24_102p4),
> > > > +};
> > > > +static CCU_MUX_DEFINE(apb_clk, "apb_clk", apb_parents,
> > > > + MPMU_APBCSCR,
> > > > + 0, 2,
> > > > + 0);
> > > > +
> > >
> > > The following clock is just called "wdt_clk" on the clock tree diagram.
> >
> > They're different things. The wdt_clk is defined a few lines earlier and
> > acts as the function clock of watchdog. Instead, wdt_bus_clk is its bus
> > clock.
> >
> > Most bus clocks in the clock tree diagram aren't listed explicitly, but
> > represented as a gate besides the function clock.
>
> OK, this is good to know. Thank you.
>
> >
> > > > +static CCU_GATE_DEFINE(wdt_bus_clk, "wdt_bus_clk", CCU_PARENT_HW(apb_clk),
> > > > + MPMU_WDTPCR,
> > > > + BIT(2), BIT(2), 0x0,
> > > > + 0);
> >
> > I do find a typo here. BIT(0) should be used here instead of BIT(2),
> > which is the reset bit for watchdog.
> >
> > > > +/* MPMU clocks end */
> > > > +
> > > > +/* APBC clocks start */
> > > > +static const struct clk_parent_data uart_clk_parents[] = {
> > > > + CCU_PARENT_HW(pll1_m3d128_57p6),
> > > > + CCU_PARENT_HW(slow_uart1_14p74),
> > > > + CCU_PARENT_HW(slow_uart2_48),
> > > > +};
> > >
> > > I might be misunderstanding this, but all of the 9 UART clocks below
> > > share a common gate.
> >
> > AFAIK they don't share the common gate. Each uart comes with its own
> > functional gate, bus gate and multiplexer. The diagram seems a little
> > confusing: only one gate is drawn to avoid duplication.
>
> OK this is also good to know, thanks.
>
> > This applies for PWM as well. There're 10 different UART clock/reset
> > registers, each contains a gate bit for functional clock and one for
> > bus clock, confirming the idea[1].
> >
> > > I *think* that gate should be represented as a distinct clock so that
> > > it can be properly reference-counted. There are numerous cases of this.
>
> After I sent this I concluded the above statement was wrong.
>
> > > Also note there is no uart1 clock; I think the clock tree diagram
> > > erroneously calls the first clock "uart1" (not uart0).
> >
> > The clocks are reordered to match our current devicetree, where the
> > first uart is uart0 and uart1 is assigned to the one in RCPU (real-time
> > CPU) region.
>
> I guess what I care about is that it is done in a way that avoids
> any confusion. I believe what you describe makes sense.
>
> > > > +static CCU_MUX_GATE_DEFINE(uart0_clk, "uart0_clk", uart_clk_parents,
> > > > + APBC_UART1_CLK_RST,
> > > > + 4, 3, BIT(1), BIT(1), 0x0,
> > > > + CLK_IS_CRITICAL);
>
> . . .
>
> > > Here we begin a bunch of definitions of "bus clocks". They are all
> > > simply gates, and the clock tree diagram shows these paired with
> > > another gated parent.
> >
> > Please note in the diagram, the clock beside the bus clock isn't the
> > parent of the bus clock. Without special notes, all bus clocks in the
> > APBC region take apb_clk as parent. All bus clocks in the APMU region
> > take pmua_aclk as parent.
>
> OK, I think there might be a note at the top of the diagram
> that suggests this, but I'm very happy to have you explain it
> to me.
>
> > > This says to me that in order to use a "child clock" in this
> > > situation, both the "regular gate" and the "bus gate" clock must be
> > > defined, and a reference to it taken (with clk_get() or similar).
> >
> > No, functional clock and bus clock are completely irrelevant. They
> > control different parts of a peripheral. Taking UART as example, you do
> > need to take both function and bus clock for normal operation; but if
> > the bus clock is disabled, the UART could continue to send data. Only
> > the bus component won't work and the registers couldn't be accessed.
>
> The bus clocks sound like interconnects. But I'm not going to
> even go there right now...
>
> > > Can you confirm this?
> > > > +
>
> . . .
>
> > > The next two parent clocks are duplicates. It looks this way on the
> > > clock tree diagram as well. Is this correct? Can you find out from
> > > SpacemiT whether one of them is actually a different clock (like
> > > pll2_d6 or something)? It makes no sense to have two multiplexed
> > > parent clocks with the same source.
> >
> > Yes, will confirm it later. The register description[2] suggests it's
> > wrong (there aren't two configuration for MIPI_BIT_CLK_SEL resulting in
> > the same frequency).
> >
> > > > + CCU_PARENT_HW(pll2_d8),
> > > > + CCU_PARENT_HW(pll2_d8),
> > > > +};
>
> OK thank you.
This has been confirmed in the earlier message as well, the duplicated
parents ARE wrong. Thanks for pointing this out.
> > > > +static CCU_DIV_FC_MUX_GATE_DEFINE(dpu_bit_clk, "dpu_bit_clk", dpubit_parents,
> > > > + APMU_LCD_CLK_RES_CTRL1,
> > > > + 17, 3, BIT(31),
> > > > + 20, 3, BIT(16), BIT(16), 0x0,
> > > > + 0);
>
> . . .
>
> > > > +static CCU_DIV_FC_MUX_GATE_DEFINE(sdh1_clk, "sdh1_clk", sdh01_parents,
> > > > + APMU_SDH1_CLK_RES_CTRL,
> > > > + 8, 3, BIT(11),
> > > > + 5, 3, BIT(4), BIT(4), 0x0,
> > > > + 0);
> > >
> > > This is strange too. sdh2_parents is identical to sdh01_parents.
> >
> > No, it isn't. pll2_d5 is a parent for sdh0/1; for sdh2, pll1_d3_819p2
> > replaces it.
>
> I see that now in the code, but not in the diagram.
> I guess I'll assume the code is right.
>
> > > All of the bits used are identical for the three control registers.
> > > The clock tree diagram shows all three of these sdhx clocks sharing
> > > a single parent mux. Why is sdh2_parents defined separately?
> >
> > I don't think the mux is shared. It should be the same case for
> > UART/PWM clocks, as described earlier.
>
> I'm sure you're right.
>
> > > > +static const struct clk_parent_data sdh2_parents[] = {
> > > > + CCU_PARENT_HW(pll1_d6_409p6),
> > > > + CCU_PARENT_HW(pll1_d4_614p4),
> > > > + CCU_PARENT_HW(pll2_d8),
> > > > + CCU_PARENT_HW(pll1_d3_819p2),
> > > > + CCU_PARENT_HW(pll1_d11_223p4),
> > > > + CCU_PARENT_HW(pll1_d13_189),
> > > > + CCU_PARENT_HW(pll1_d23_106p8),
> > > > +};
>
> . . .
>
> > > You could reduce some duplication here using a simple
> > > macro for defining the address of the clk_hw structure:
> > >
> > > #define CLK_HW(x) &x.common.hw
> > >
> > > (Add parentheses if you like.) It saves 3 characters for
> > > each clock...
> >
> > Looks good to me, but
>
> Don't bother for now. It can be fixed easily later if
> it's important.
Thanks.
> > > > +static struct clk_hw_onecell_data k1_ccu_apbs_clks = {
> > > > + .hws = {
> > > > + [CLK_PLL1] = &pll1.common.hw,
> > > > + [CLK_PLL2] = &pll2.common.hw,
> > > > + [CLK_PLL3] = &pll3.common.hw,
> > > > + [CLK_PLL1_D2] = &pll1_d2.common.hw,
> > > > + [CLK_PLL1_D3] = &pll1_d3.common.hw,
> > > > + [CLK_PLL1_D4] = &pll1_d4.common.hw,
> > > > + [CLK_PLL1_D5] = &pll1_d5.common.hw,
> > > > + [CLK_PLL1_D6] = &pll1_d6.common.hw,
> > > > + [CLK_PLL1_D7] = &pll1_d7.common.hw,
> > > > + [CLK_PLL1_D8] = &pll1_d8.common.hw,
> > > > + [CLK_PLL1_D11] = &pll1_d11_223p4.common.hw,
> > > > + [CLK_PLL1_D13] = &pll1_d13_189.common.hw,
> > > > + [CLK_PLL1_D23] = &pll1_d23_106p8.common.hw,
> > > > + [CLK_PLL1_D64] = &pll1_d64_38p4.common.hw,
> > > > + [CLK_PLL1_D10_AUD] = &pll1_aud_245p7.common.hw,
> > > > + [CLK_PLL1_D100_AUD] = &pll1_aud_24p5.common.hw,
> > > > + [CLK_PLL2_D1] = &pll2_d1.common.hw,
> > > > + [CLK_PLL2_D2] = &pll2_d2.common.hw,
> > > > + [CLK_PLL2_D3] = &pll2_d3.common.hw,
> > > > + [CLK_PLL2_D4] = &pll2_d4.common.hw,
> > > > + [CLK_PLL2_D5] = &pll2_d5.common.hw,
> > > > + [CLK_PLL2_D6] = &pll2_d6.common.hw,
> > > > + [CLK_PLL2_D7] = &pll2_d7.common.hw,
> > > > + [CLK_PLL2_D8] = &pll2_d8.common.hw,
> > > > + [CLK_PLL3_D1] = &pll3_d1.common.hw,
> > > > + [CLK_PLL3_D2] = &pll3_d2.common.hw,
> > > > + [CLK_PLL3_D3] = &pll3_d3.common.hw,
> > > > + [CLK_PLL3_D4] = &pll3_d4.common.hw,
> > > > + [CLK_PLL3_D5] = &pll3_d5.common.hw,
> > > > + [CLK_PLL3_D6] = &pll3_d6.common.hw,
> > > > + [CLK_PLL3_D7] = &pll3_d7.common.hw,
> > > > + [CLK_PLL3_D8] = &pll3_d8.common.hw,
> > > > + [CLK_PLL3_80] = &pll3_80.common.hw,
> > > > + [CLK_PLL3_40] = &pll3_40.common.hw,
> > > > + [CLK_PLL3_20] = &pll3_20.common.hw,
> > > > +
> > > > + },
> > > > + .num = CLK_APBS_NUM,
> > >
> > > It sure would be nice to be able to use ARRAY_SIZE() here but
> > > alas I don't think it's possible here.
> >
> > CLK_*_NUM will be removed in the next revision, as it's actually not a
> > binding. I'm looking for a better way to represent these clocks.
> >
> > Maybe represent the clocks with (ID, struct clk_hw *) pairs and
> > generate struct clk_hw_onecell_data dynamically during probe.
>
> Yes I think I have a suggestion but I'll hold off until I send
> out another message in which I suggest something different.
>
> > > > +};
>
> . . .
>
> > > The only time the "PLL lock" is needed is with PLL clocks. Can you
> > > encode this flag, or perhaps the address of the lock register,
> > > into the ccu_pll structure instead somehow?
> > >
> > > If so you wouldn't need this spacemit_ccu_data structure, and could
> > > simply use the clk_hw_onecell_data structure in its place.
> >
> > We could scan the clk_hw_onecell_data array before the probing, but I
> > don't think it's worth.
> >
> > As the raw clk_hw_onecell_data is likely to be removed in the next
> > revision, I'll try to avoid the flag during refactoring.
>
> I think you'll be able to.
>
> > > > +struct spacemit_ccu_data {
> > > > + struct clk_hw_onecell_data *hw_clks;
> > > > + bool need_pll_lock;
> > > > +};
>
> . . .
>
> > > The next two pointers are regmap pointers. Could the
> > > name of the fields suggest that? (They sound like raw
> > > memory addresses to me.)
> >
> > Do "common.regmap" and "common.lock_regmap" sound better for you?
>
> Yes I think that's an improvement.
>
> > > > + common->base = priv->base;
> > > > + common->lock_base = priv->lock_base;
>
> . . .
>
> > > > + ret = devm_clk_hw_register(dev, hw);
> > > > + if (ret) {
> > > > + dev_err(dev, "Cannot register clock %d - %s\n",
> > > > + i, name);
> > >
> > > Just use hw->init->name here instead of name.
> >
> > Will change in the next version.
>
> . . .
>
> > > You allocate the priv structure, but never free it (a bug).
> > > It only holds 3 values; just pass them as arguments when
> > > registering the CCU, as suggested above.
> >
> > The allocation is done through a devres variant, it should be released
> > automatically. Please correct me if I'm wrong.
>
> You're right. But my other point about priv being unnecessary
> (just pass the three values as arguments) remains.
Makes sense to me, I'll drop the structure.
> > > > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > > > + if (!priv)
> > > > + return -ENOMEM;
>
> . . .
>
> > > End of "ccu-k1.c"!!!
> >
> > It's really a lot of lines lol
> >
> > > > diff --git a/drivers/clk/spacemit/ccu_common.h b/drivers/clk/spacemit/ccu_common.h
> > > > new file mode 100644
> > > > index 000000000000..242461ee592f
> > > > --- /dev/null
> > > > +++ b/drivers/clk/spacemit/ccu_common.h
> > > > @@ -0,0 +1,51 @@
>
> . . .
>
> > > Although I think the following macros are nice and concise, they
> > > obscure the fact that they're essentially renaming existing
> > > regmap operations. They don't really add much value; the
> > > regmap functions are pretty clear.
> >
> > They're just for saving some characters,
> >
> > regmap_read(common->base, common->reg_ctrl, &tmp)
> >
> > is much longer than
> >
> > ccu_read(common, ctrl, &tmp)
> >
> > Of course it could be changed, if you're still strongly against it,
>
> It's OK for now, but I might make the same suggestion in a future
> version of the series.
Okay, then will keep it as it, at least for now.
> > > Although regmap_read() returns a value, you never check for an
> > > error when calling ccu_read().
> >
> > I don't expect that access to a MMIO regmap could fail. There shouldn't
> > be any cases of failure, so I don't check it.
> >
> > > > +#define ccu_read(reg, c, val) regmap_read((c)->base, (c)->reg_##reg, val)
>
> It should never return an error. But what if it did? I'm
> mainly thinking about the very first attempt to read from
> the I/O space. It's probably OK; lots of existing code
> ignores any return value from regmap_read.
>
> . . .
>
> > > > diff --git a/drivers/clk/spacemit/ccu_ddn.c b/drivers/clk/spacemit/ccu_ddn.c
> > > > new file mode 100644
> > > > index 000000000000..1df555888ecb
> > > > --- /dev/null
> > > > +++ b/drivers/clk/spacemit/ccu_ddn.c
>
> . . .
>
> > > The next three functions are never used and can be removed.
> > >
> > > The reason is that the only time they'd be used is when
> > > CCU_DDN_GATE_DEFINE() (defined in "ccu_ddn.h") is called,
> > > but it never is. So you can get rid of CCU_DDN_GATE_DEFINE(),
> > > and once you do that there's no need for ccu_ddn_disable(),
> > > ccu_ddn_enable(), and ccu_ddn_is_enabled(). Furthermore,
> > > these three functions are the only place the "reg_sel"
> > > field in the ccu_common structure is used, and without
> > > these functions, that field serves no purpose. So that
> > > field can go away, and there's no need to provide a value
> > > for initializing it in all those clock definition macros.
> >
> > When cleaning up the driver from the vendor source, I tried to keep most
> > clock types included since future SpacemiT SoCs made may make use of
> > them. It seems too farsighted as this patch is already quite large.
> >
> > I'll remove the unused clock types as well as the reg_sel field, thanks.
>
> This is good practice. Don't add code that you expect
> to use at a future date. Keep that out, and add it in
> *when* you need it. Otherwise it's basically dead code.
>
> . . .
>
> > > I'm going to say more about this below, but I think the
> > > factor can be removed and hard-coded here.
> >
> > Alright, we could hardcode it for now to make the patch as simple as
> > possible. If different factors are used in future SoCs, it isn't hard
> > to add it back later.
>
> Exactly.
>
> > > > + rate = (*prate * params->tbl[i].den) /
> > > > + (params->tbl[i].num * params->info->factor);
> > >
>
> . . .
>
> > > Here is a general comment that applies to a bunch of places in
> > > this code. You can concisely define a field with a single
> > > mask value. That means there is no need to define both a mask
> > > (or a field width) and a shift. What you need is defined in
> > > <linux/bitfield.h>. There you will find a set of macros
> > > like FIELD_GET() and FIELD_PREP(), or alternatively a set
> > > of inline functions like u32_get_bits() and u32_encode_bits().
> >
> > I think both macros and inline functions in bitfield.h work with only
> > constant masks. With non-constant masks, some checks cannot be optimized
> > out, resulting in compilation errors.
>
> Yes, this was true some time back and I suppose it still is.
> For some reason I thought there might have been a way to use
> non-constant masks. Anyway, I still think something similar
> to u32_get_bits() or FIELD_GET() is nicely encapsulated and
> less error-prone than "manually" shifting and masking.
>
> Don't worry about it for now, but again I might suggest it
> again.
>
> > > > + unsigned int num_mask;
> > > > + unsigned int den_mask;
> > > > + unsigned int num_shift;
> > > > + unsigned int den_shift;
> > > > +};
>
> . . .
>
> > > > diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c
> > > > new file mode 100644
> > > > index 000000000000..b46eeb59faea
> > > > --- /dev/null
> > > > +++ b/drivers/clk/spacemit/ccu_mix.c
>
> . . .
>
> > > > +static int ccu_mux_set_parent(struct clk_hw *hw, u8 index)
> > > > +{
> > > > + struct ccu_mix *mix = hw_to_ccu_mix(hw);
> > > > + struct ccu_common *common = &mix->common;
> > > > + struct ccu_mux_config *mux = mix->mux;
> > > > + int ret = 0;
> > > > + u32 mask;
> > > > +
> > > > + if (mux->table)
> > >
> > > You should verify that the index is less than the
> > > number of entries in the table before you blindly
> > > dereference it here.
> >
> > The desired index is found by searching in clk_fetch_parent_index(),
> > thus is always be valid, isn't it?
> >
> > However, this is a dead codepath: no clock hardware comes with a valid
> > table. The whole if could be removed for simplication.
>
> That's even better.
>
> > > > + index = mux->table[index];
>
> . . .
>
> > > > + u32 val_enable;
> > > > + u32 val_disable;
> > > > + u32 flags;
> > >
> > > When you have a flags field like this, it's helpful to add a comment
> > > that indicates what values it can take on. I believe in all cases
> > > like this, the flags values are standard clock framework flags.
> >
> > Yes.
> >
> > > So what I mean is do something like this:
> > >
> > > u32 flags; /* E.g., CLK_IGNORE_UNUSED */
> >
> > Do you think
> >
> > u32 flags; /* standard CCF flags */
> >
> > ls more descriptive?
>
> No, but your way is OK.
Thanks.
> >
> > > > +};
>
> . . .
>
> > > > +#define CCU_GATE_DEFINE(_struct, _name, _parent, _reg, _gate_mask, \
> > > > + _val_enable, _val_disable, _flags) \
> > >
> > > There is one case where the _flags value is CLK_IGNORE_UNUSED, and
> > > one case where it is CLK_IS_CRITICAL.
> >
> > The UART0 (booting UART) and all core clocks are marked as
> > CLK_IS_CRTICAL, not just one case.
> >
> > > All others pass 0. Given
> > > that, you could define two macros and remove the 0 argument from
> > > most of the callers:
> >
> > I'd like to always keep the flags field, since keeping the clock
> > definitions intact and in the same format helps reading. There're a
> > bunch of clocks.
>
> OK.
>
> . . .
>
> > > I'd put the "2" somewhere else in the name of this. It suggests
> > > "divide by two" but it's really a "second DIV_FC_MUX_GATE" macro.
> >
> > Is DIV_SPLIT_FC_MUX_GATE a better name? The difference between this and
> > DIV_FC_MUX_GATE is that the fc bit locates in a different register.
>
> Either is fine. My point was about "DIV2" suggesting "divide
> by two".
>
> . . .
>
> > > > diff --git a/drivers/clk/spacemit/ccu_pll.c b/drivers/clk/spacemit/ccu_pll.c
> > > > new file mode 100644
> > > > index 000000000000..81b929ca1c5c
> > > > --- /dev/null
> > > > +++ b/drivers/clk/spacemit/ccu_pll.c
>
> . . .
>
> > > > + unsigned int i;
> > > > +
> > > > + for (i = 0; i < params->tbl_size; i++) {
> > > > + if (params->rate_tbl[i].rate <= rate) {
> > > > + if (max_rate < params->rate_tbl[i].rate)
> > > > + max_rate = params->rate_tbl[i].rate;
> > > > + }
> > > > + }
> > > > +
> > >
> > > I don't like that you're passing a compile-time defined
> > > frequency (which might not match reality).
> >
> > We could make the rate_tbl sorted, which should improve both set_rate
> > and round_rate a lot.
>
> That might be nice, but it's not strictly necessary. But if you
> don't do that, fix the loop above.
>
> > > > + return MAX(max_rate, PLL_MIN_FREQ);
> > > > +}
>
> . . .
>
> That's the end of my response.
>
> Now on to my bigger proposal. Do *not* delay posting the
> next version of your series based on my next message.
Which one does "next message" refer to? Is it the one about dt-binding
designing, or should I wait for another message?
> Just send out an update that addresses all of the feedback
> you have received so far. If my proposal makes sense we can
> talk about how to modify that next version to incorporate it.
Okay.
> Thanks.
>
> -Alex
Best regards,
Haylen Chu
More information about the linux-riscv
mailing list