Quoting Chen Wang (2024-04-15 00:23:27) > diff --git a/drivers/clk/sophgo/clk-sophgo-sg2042.c b/drivers/clk/sophgo/clk-sophgo-sg2042.c > new file mode 100644 > index 000000000000..0bcfaab52f51 > --- /dev/null > +++ b/drivers/clk/sophgo/clk-sophgo-sg2042.c > @@ -0,0 +1,1645 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Sophgo SG2042 Clock Generator Driver > + * > + * Copyright (C) 2024 Sophgo Technology Inc. All rights reserved. > + */ > + > +#include <asm/div64.h> asm goes after linux includes... > +#include <linux/array_size.h> > +#include <linux/bits.h> > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > +#include <linux/io.h> > +#include <linux/iopoll.h> > +#include <linux/platform_device.h> here. > + > +/* > + * The clock of SG2042 is composed of three parts. > + * The registers of these three parts of the clock are scattered in three > + * different memory address spaces: > + * - pll clocks > + * - gate clocks for RP subsystem > + * - div/mux, and gate clocks working for other subsystem than RP subsystem > + */ > +#include <dt-bindings/clock/sophgo,sg2042-pll.h> > +#include <dt-bindings/clock/sophgo,sg2042-rpgate.h> > +#include <dt-bindings/clock/sophgo,sg2042-clkgen.h> > + > +/* Registers defined in SYS_CTRL */ > +#define R_PLL_BEGIN 0xC0 [...] > + > +#define SG2042_PLL(_id, _name, _parent_name, _r_stat, _r_enable, _r_ctrl, _shift) \ > + { \ > + .hw.init = CLK_HW_INIT( \ > + _name, \ > + _parent_name, \ This still uses a string. Please convert all parents described by strings to use clk_parent_data or clk_hw directly. > + &sg2042_clk_pll_ops, \ > + CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\ > + .id = _id, \ > + .offset_ctrl = _r_ctrl, \ > + .offset_status = _r_stat, \ > + .offset_enable = _r_enable, \ > + .shift_status_lock = 8 + (_shift), \ > + .shift_status_updating = _shift, \ [...] > + * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and > + * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1", > + * they are both controlled by register CLKDIVREG28; > + * While for register value of mux selection, use Clock Select for DDR01’s clock > + * as example, see CLKSELREG0, bit[2]. > + * 1: Select in_dpll0_clk as clock source, correspondng to the parent input > + * source from "clk_div_ddr01_0". > + * 0: Select in_fpll_clk as clock source, corresponding to the parent input > + * source from "clk_div_ddr01_1". > + * So we need a table to define the array of register values corresponding to > + * the parent index and tell CCF about this when registering mux clock. > + */ > +static const u32 sg2042_mux_table[] = {1, 0}; > + > +static const struct clk_parent_data clk_mux_ddr01_p[] = { > + { .hw = &sg2042_div_clks[0].hw }, > + { .hw = &sg2042_div_clks[1].hw }, Just use struct clk_init_data::parent_hws for this if you only have a clk_hw pointer for every element of the parent array. > +}; > + > +static const struct clk_parent_data clk_mux_ddr23_p[] = { > + { .hw = &sg2042_div_clks[2].hw }, > + { .hw = &sg2042_div_clks[3].hw }, > +}; > + [...] > + > +static int sg2042_pll_probe(struct platform_device *pdev) > +{ > + struct sg2042_clk_data *clk_data = NULL; > + int i, ret = 0; > + int num_clks = 0; > + > + num_clks = ARRAY_SIZE(sg2042_pll_clks); > + > + ret = sg2042_init_clkdata(pdev, num_clks, &clk_data); > + if (ret < 0) > + goto error_out; > + > + ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks, > + num_clks); > + if (ret) > + goto cleanup; > + > + return devm_of_clk_add_hw_provider(&pdev->dev, > + of_clk_hw_onecell_get, > + &clk_data->onecell_data); > + > +cleanup: > + for (i = 0; i < num_clks; i++) { > + if (clk_data->onecell_data.hws[i]) > + clk_hw_unregister(clk_data->onecell_data.hws[i]); This should be unnecessary if devm is used throughout. > + } > + > +error_out: > + pr_err("%s failed error number %d\n", __func__, ret); > + return ret; Just do this part in the one place the goto is. These two comments apply to all probes.