brintos

brintos / linux-shallow public Read only

0
0
Text · 36.5 KiB · a334963 Raw
1153 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Sophgo SG2042 Clock Generator Driver4 *5 * Copyright (C) 2024 Sophgo Technology Inc.6 * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com>7 */8 9#include <linux/array_size.h>10#include <linux/bits.h>11#include <linux/clk.h>12#include <linux/clk-provider.h>13#include <linux/io.h>14#include <linux/platform_device.h>15#include <asm/div64.h>16 17#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>18 19#include "clk-sg2042.h"20 21/* Registers defined in SYS_CTRL */22#define R_PLL_BEGIN		0xC023#define R_PLL_STAT		(0xC0 - R_PLL_BEGIN)24#define R_PLL_CLKEN_CONTROL	(0xC4 - R_PLL_BEGIN)25#define R_MPLL_CONTROL		(0xE8 - R_PLL_BEGIN)26#define R_FPLL_CONTROL		(0xF4 - R_PLL_BEGIN)27#define R_DPLL0_CONTROL		(0xF8 - R_PLL_BEGIN)28#define R_DPLL1_CONTROL		(0xFC - R_PLL_BEGIN)29 30/* Registers defined in CLOCK */31#define R_CLKENREG0		0x0032#define R_CLKENREG1		0x0433#define R_CLKSELREG0		0x2034#define R_CLKDIVREG0		0x4035#define R_CLKDIVREG1		0x4436#define R_CLKDIVREG2		0x4837#define R_CLKDIVREG3		0x4C38#define R_CLKDIVREG4		0x5039#define R_CLKDIVREG5		0x5440#define R_CLKDIVREG6		0x5841#define R_CLKDIVREG7		0x5C42#define R_CLKDIVREG8		0x6043#define R_CLKDIVREG9		0x6444#define R_CLKDIVREG10		0x6845#define R_CLKDIVREG11		0x6C46#define R_CLKDIVREG12		0x7047#define R_CLKDIVREG13		0x7448#define R_CLKDIVREG14		0x7849#define R_CLKDIVREG15		0x7C50#define R_CLKDIVREG16		0x8051#define R_CLKDIVREG17		0x8452#define R_CLKDIVREG18		0x8853#define R_CLKDIVREG19		0x8C54#define R_CLKDIVREG20		0x9055#define R_CLKDIVREG21		0x9456#define R_CLKDIVREG22		0x9857#define R_CLKDIVREG23		0x9C58#define R_CLKDIVREG24		0xA059#define R_CLKDIVREG25		0xA460#define R_CLKDIVREG26		0xA861#define R_CLKDIVREG27		0xAC62#define R_CLKDIVREG28		0xB063#define R_CLKDIVREG29		0xB464#define R_CLKDIVREG30		0xB865 66/* All following shift value are the same for all DIV registers */67#define SHIFT_DIV_RESET_CTRL	068#define SHIFT_DIV_FACTOR_SEL	369#define SHIFT_DIV_FACTOR	1670 71/**72 * struct sg2042_divider_clock - Divider clock73 * @hw:			clk_hw for initialization74 * @id:			used to map clk_onecell_data75 * @reg:		used for readl/writel.76 *			**NOTE**: DIV registers are ALL in CLOCK!77 * @lock:		spinlock to protect register access, modification of78 *			frequency can only be served one at the time79 * @offset_ctrl:	offset of divider control registers80 * @shift:		shift of "Clock Divider Factor" in divider control register81 * @width:		width of "Clock Divider Factor" in divider control register82 * @div_flags:		private flags for this clock, not for framework-specific83 * @initval:		In the divider control register, we can configure whether84 *			to use the value of "Clock Divider Factor" or just use85 *			the initial value pre-configured by IC. BIT[3] controls86 *			this and by default (value is 0), means initial value87 *			is used.88 *			**NOTE** that we cannot read the initial value (default89 *			value when poweron) and default value of "Clock Divider90 *			Factor" is zero, which I think is a hardware design flaw91 *			and should be sync-ed with the initial value. So in92 *			software we have to add a configuration item (initval)93 *			to manually configure this value and use it when BIT[3]94 *			is zero.95 */96struct sg2042_divider_clock {97	struct clk_hw hw;98 99	unsigned int id;100 101	void __iomem *reg;102	/* protect register access */103	spinlock_t *lock;104 105	u32 offset_ctrl;106	u8 shift;107	u8 width;108	u8 div_flags;109	u32 initval;110};111 112#define to_sg2042_clk_divider(_hw)	\113	container_of(_hw, struct sg2042_divider_clock, hw)114 115/**116 * struct sg2042_gate_clock - Gate clock117 * @hw:			clk_hw for initialization118 * @id:			used to map clk_onecell_data119 * @offset_enable:	offset of gate enable registers120 * @bit_idx:		which bit in the register controls gating of this clock121 */122struct sg2042_gate_clock {123	struct clk_hw hw;124 125	unsigned int id;126 127	u32 offset_enable;128	u8 bit_idx;129};130 131/**132 * struct sg2042_mux_clock - Mux clock133 * @hw:			clk_hw for initialization134 * @id:			used to map clk_onecell_data135 * @offset_select:	offset of mux selection registers136 *			**NOTE**: MUX registers are ALL in CLOCK!137 * @shift:		shift of "Clock Select" in mux selection register138 * @width:		width of "Clock Select" in mux selection register139 * @clk_nb:		used for notification140 * @original_index:	set by notifier callback141 */142struct sg2042_mux_clock {143	struct clk_hw hw;144 145	unsigned int id;146 147	u32 offset_select;148	u8 shift;149	u8 width;150 151	struct notifier_block clk_nb;152	u8 original_index;153};154 155#define to_sg2042_mux_nb(_nb) container_of(_nb, struct sg2042_mux_clock, clk_nb)156 157static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw,158						    unsigned long parent_rate)159{160	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);161	unsigned long ret_rate;162	u32 val;163 164	if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {165		val = divider->initval;166	} else {167		val = readl(divider->reg) >> divider->shift;168		val &= clk_div_mask(divider->width);169	}170 171	ret_rate = divider_recalc_rate(hw, parent_rate, val, NULL,172				       divider->div_flags, divider->width);173 174	pr_debug("--> %s: divider_recalc_rate: ret_rate = %ld\n",175		 clk_hw_get_name(hw), ret_rate);176	return ret_rate;177}178 179static long sg2042_clk_divider_round_rate(struct clk_hw *hw,180					  unsigned long rate,181					  unsigned long *prate)182{183	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);184	unsigned long ret_rate;185	u32 bestdiv;186 187	/* if read only, just return current value */188	if (divider->div_flags & CLK_DIVIDER_READ_ONLY) {189		if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {190			bestdiv = divider->initval;191		} else {192			bestdiv = readl(divider->reg) >> divider->shift;193			bestdiv &= clk_div_mask(divider->width);194		}195		ret_rate = DIV_ROUND_UP_ULL((u64)*prate, bestdiv);196	} else {197		ret_rate = divider_round_rate(hw, rate, prate, NULL,198					      divider->width, divider->div_flags);199	}200 201	pr_debug("--> %s: divider_round_rate: val = %ld\n",202		 clk_hw_get_name(hw), ret_rate);203	return ret_rate;204}205 206static int sg2042_clk_divider_set_rate(struct clk_hw *hw,207				       unsigned long rate,208				       unsigned long parent_rate)209{210	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);211	unsigned long flags = 0;212	u32 val, val2, value;213 214	value = divider_get_val(rate, parent_rate, NULL,215				divider->width, divider->div_flags);216 217	if (divider->lock)218		spin_lock_irqsave(divider->lock, flags);219	else220		__acquire(divider->lock);221 222	/*223	 * The sequence of clock frequency modification is:224	 * Assert to reset divider.225	 * Modify the value of Clock Divide Factor (and High Wide if needed).226	 * De-assert to restore divided clock with new frequency.227	 */228	val = readl(divider->reg);229 230	/* assert */231	val &= ~BIT(SHIFT_DIV_RESET_CTRL);232	writel(val, divider->reg);233 234	if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) {235		val = clk_div_mask(divider->width) << (divider->shift + 16);236	} else {237		val = readl(divider->reg);238		val &= ~(clk_div_mask(divider->width) << divider->shift);239	}240	val |= value << divider->shift;241	val |= BIT(SHIFT_DIV_FACTOR_SEL);242	writel(val, divider->reg);243	val2 = val;244 245	/* de-assert */246	val |= BIT(SHIFT_DIV_RESET_CTRL);247	writel(val, divider->reg);248 249	if (divider->lock)250		spin_unlock_irqrestore(divider->lock, flags);251	else252		__release(divider->lock);253 254	pr_debug("--> %s: divider_set_rate: register val = 0x%x\n",255		 clk_hw_get_name(hw), val2);256	return 0;257}258 259static const struct clk_ops sg2042_clk_divider_ops = {260	.recalc_rate = sg2042_clk_divider_recalc_rate,261	.round_rate = sg2042_clk_divider_round_rate,262	.set_rate = sg2042_clk_divider_set_rate,263};264 265static const struct clk_ops sg2042_clk_divider_ro_ops = {266	.recalc_rate = sg2042_clk_divider_recalc_rate,267	.round_rate = sg2042_clk_divider_round_rate,268};269 270/*271 * Clock initialization macro naming rules:272 * FW: use CLK_HW_INIT_FW_NAME273 * HW: use CLK_HW_INIT_HW274 * HWS: use CLK_HW_INIT_HWS275 * RO: means Read-Only276 */277#define SG2042_DIV_FW(_id, _name, _parent,				\278		      _r_ctrl, _shift, _width,				\279		      _div_flag, _initval) {				\280		.id = _id,						\281		.hw.init = CLK_HW_INIT_FW_NAME(				\282				_name,					\283				_parent,				\284				&sg2042_clk_divider_ops,		\285				0),					\286		.offset_ctrl = _r_ctrl,					\287		.shift = _shift,					\288		.width = _width,					\289		.div_flags = _div_flag,					\290		.initval = _initval,					\291	}292 293#define SG2042_DIV_FW_RO(_id, _name, _parent,				\294		  _r_ctrl, _shift, _width,				\295		  _div_flag, _initval) {				\296		.id = _id,						\297		.hw.init = CLK_HW_INIT_FW_NAME(				\298				_name,					\299				_parent,				\300				&sg2042_clk_divider_ro_ops,		\301				0),					\302		.offset_ctrl = _r_ctrl,					\303		.shift = _shift,					\304		.width = _width,					\305		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\306		.initval = _initval,					\307	}308 309#define SG2042_DIV_HW(_id, _name, _parent,				\310		      _r_ctrl, _shift, _width,				\311		      _div_flag, _initval) {				\312		.id = _id,						\313		.hw.init = CLK_HW_INIT_HW(				\314				_name,					\315				_parent,				\316				&sg2042_clk_divider_ops,		\317				0),					\318		.offset_ctrl = _r_ctrl,					\319		.shift = _shift,					\320		.width = _width,					\321		.div_flags = _div_flag,					\322		.initval = _initval,					\323	}324 325#define SG2042_DIV_HW_RO(_id, _name, _parent,				\326			 _r_ctrl, _shift, _width,			\327			 _div_flag, _initval) {				\328		.id = _id,						\329		.hw.init = CLK_HW_INIT_HW(				\330				_name,					\331				_parent,				\332				&sg2042_clk_divider_ro_ops,		\333				0),					\334		.offset_ctrl = _r_ctrl,					\335		.shift = _shift,					\336		.width = _width,					\337		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\338		.initval = _initval,					\339	}340 341#define SG2042_DIV_HWS(_id, _name, _parent,				\342		       _r_ctrl, _shift, _width,				\343		       _div_flag, _initval) {				\344		.id = _id,						\345		.hw.init = CLK_HW_INIT_HWS(				\346				_name,					\347				_parent,				\348				&sg2042_clk_divider_ops,		\349				0),					\350		.offset_ctrl = _r_ctrl,					\351		.shift = _shift,					\352		.width = _width,					\353		.div_flags = _div_flag,					\354		.initval = _initval,					\355	}356 357#define SG2042_DIV_HWS_RO(_id, _name, _parent,				\358			  _r_ctrl, _shift, _width,			\359			  _div_flag, _initval) {			\360		.id = _id,						\361		.hw.init = CLK_HW_INIT_HWS(				\362				_name,					\363				_parent,				\364				&sg2042_clk_divider_ro_ops,		\365				0),					\366		.offset_ctrl = _r_ctrl,					\367		.shift = _shift,					\368		.width = _width,					\369		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\370		.initval = _initval,					\371	}372 373#define SG2042_GATE_HWS(_id, _name, _parent, _flags,	\374			_r_enable, _bit_idx) {		\375		.id = _id,				\376		.hw.init = CLK_HW_INIT_HWS(		\377				_name,			\378				_parent,		\379				NULL,			\380				_flags),		\381		.offset_enable = _r_enable,		\382		.bit_idx = _bit_idx,			\383	}384 385#define SG2042_GATE_HW(_id, _name, _parent, _flags,	\386		       _r_enable, _bit_idx) {		\387		.id = _id,				\388		.hw.init = CLK_HW_INIT_HW(		\389				_name,			\390				_parent,		\391				NULL,			\392				_flags),		\393		.offset_enable = _r_enable,		\394		.bit_idx = _bit_idx,			\395	}396 397#define SG2042_GATE_FW(_id, _name, _parent, _flags,	\398		       _r_enable, _bit_idx) {		\399		.id = _id,				\400		.hw.init = CLK_HW_INIT_FW_NAME(		\401				_name,			\402				_parent,		\403				NULL,			\404				_flags),		\405		.offset_enable = _r_enable,		\406		.bit_idx = _bit_idx,			\407	}408 409#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width) { \410		.id = _id,					\411		.hw.init = CLK_HW_INIT_PARENTS_HW(		\412				_name,				\413				_parents,			\414				NULL,				\415				_flags),			\416		.offset_select = _r_select,			\417		.shift = _shift,				\418		.width = _width,				\419	}420 421/*422 * Clock items in the array are sorted according to the clock-tree diagram,423 * from top to bottom, from upstream to downstream. Read TRM for details.424 */425 426/* updated during probe/registration */427static const struct clk_hw *clk_gate_ddr01_div0[] = { NULL };428static const struct clk_hw *clk_gate_ddr01_div1[] = { NULL };429static const struct clk_hw *clk_gate_ddr23_div0[] = { NULL };430static const struct clk_hw *clk_gate_ddr23_div1[] = { NULL };431static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] = { NULL };432static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] = { NULL };433static const struct clk_hw *clk_gate_axi_ddr_div0[] = { NULL };434static const struct clk_hw *clk_gate_axi_ddr_div1[] = { NULL };435 436static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] = {437	SG2042_GATE_FW(GATE_CLK_DDR01_DIV0, "clk_gate_ddr01_div0", "dpll0",438		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,439		       R_CLKDIVREG27, 4),440	SG2042_GATE_FW(GATE_CLK_DDR01_DIV1, "clk_gate_ddr01_div1", "fpll",441		       CLK_IS_CRITICAL,442		       R_CLKDIVREG28, 4),443 444	SG2042_GATE_FW(GATE_CLK_DDR23_DIV0, "clk_gate_ddr23_div0", "dpll1",445		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,446		       R_CLKDIVREG29, 4),447	SG2042_GATE_FW(GATE_CLK_DDR23_DIV1, "clk_gate_ddr23_div1", "fpll",448		       CLK_IS_CRITICAL,449		       R_CLKDIVREG30, 4),450 451	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV0,452		       "clk_gate_rp_cpu_normal_div0", "mpll",453		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,454		       R_CLKDIVREG0, 4),455	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV1,456		       "clk_gate_rp_cpu_normal_div1", "fpll",457		       CLK_IS_CRITICAL,458		       R_CLKDIVREG1, 4),459 460	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV0, "clk_gate_axi_ddr_div0", "mpll",461		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,462		       R_CLKDIVREG25, 4),463	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV1, "clk_gate_axi_ddr_div1", "fpll",464		       CLK_IS_CRITICAL,465		       R_CLKDIVREG26, 4),466};467 468#define DEF_DIVFLAG (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO)469 470static struct sg2042_divider_clock sg2042_div_clks_level_1[] = {471	SG2042_DIV_HWS_RO(DIV_CLK_DPLL0_DDR01_0,472			  "clk_div_ddr01_0", clk_gate_ddr01_div0,473			  R_CLKDIVREG27, 16, 5, DEF_DIVFLAG, 1),474	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR01_1,475			  "clk_div_ddr01_1", clk_gate_ddr01_div1,476			  R_CLKDIVREG28, 16, 5, DEF_DIVFLAG, 1),477 478	SG2042_DIV_HWS_RO(DIV_CLK_DPLL1_DDR23_0,479			  "clk_div_ddr23_0", clk_gate_ddr23_div0,480			  R_CLKDIVREG29, 16, 5, DEF_DIVFLAG, 1),481	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR23_1,482			  "clk_div_ddr23_1", clk_gate_ddr23_div1,483			  R_CLKDIVREG30, 16, 5, DEF_DIVFLAG, 1),484 485	SG2042_DIV_HWS(DIV_CLK_MPLL_RP_CPU_NORMAL_0,486		       "clk_div_rp_cpu_normal_0", clk_gate_rp_cpu_normal_div0,487		       R_CLKDIVREG0, 16, 5, DEF_DIVFLAG, 1),488	SG2042_DIV_HWS(DIV_CLK_FPLL_RP_CPU_NORMAL_1,489		       "clk_div_rp_cpu_normal_1", clk_gate_rp_cpu_normal_div1,490		       R_CLKDIVREG1, 16, 5, DEF_DIVFLAG, 1),491 492	SG2042_DIV_HWS(DIV_CLK_MPLL_AXI_DDR_0,493		       "clk_div_axi_ddr_0", clk_gate_axi_ddr_div0,494		       R_CLKDIVREG25, 16, 5, DEF_DIVFLAG, 2),495	SG2042_DIV_HWS(DIV_CLK_FPLL_AXI_DDR_1,496		       "clk_div_axi_ddr_1", clk_gate_axi_ddr_div1,497		       R_CLKDIVREG26, 16, 5, DEF_DIVFLAG, 1),498};499 500/*501 * Note: regarding names for mux clock, "0/1" or "div0/div1" means the502 * first/second parent input source, not the register value.503 * For example:504 * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and505 * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0",506 * they are both controlled by register CLKDIVREG27;507 * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and508 * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1",509 * they are both controlled by register CLKDIVREG28;510 * While for register value of mux selection, use Clock Select for DDR01’s clock511 * as example, see CLKSELREG0, bit[2].512 * 1: Select in_dpll0_clk as clock source, correspondng to the parent input513 *    source from "clk_div_ddr01_0".514 * 0: Select in_fpll_clk as clock source, corresponding to the parent input515 *    source from "clk_div_ddr01_1".516 * So we need a table to define the array of register values corresponding to517 * the parent index and tell CCF about this when registering mux clock.518 */519static const u32 sg2042_mux_table[] = {1, 0};520 521/* Aliases just for easy reading */522#define clk_div_ddr01_0		(&sg2042_div_clks_level_1[0].hw)523#define clk_div_ddr01_1		(&sg2042_div_clks_level_1[1].hw)524#define clk_div_ddr23_0		(&sg2042_div_clks_level_1[2].hw)525#define clk_div_ddr23_1		(&sg2042_div_clks_level_1[3].hw)526#define clk_div_rp_cpu_normal_0	(&sg2042_div_clks_level_1[4].hw)527#define clk_div_rp_cpu_normal_1	(&sg2042_div_clks_level_1[5].hw)528#define clk_div_axi_ddr_0	(&sg2042_div_clks_level_1[6].hw)529#define clk_div_axi_ddr_1	(&sg2042_div_clks_level_1[7].hw)530 531static const struct clk_hw *clk_mux_ddr01_p[] = {532	clk_div_ddr01_0,533	clk_div_ddr01_1,534};535 536static const struct clk_hw *clk_mux_ddr23_p[] = {537	clk_div_ddr23_0,538	clk_div_ddr23_1,539};540 541static const struct clk_hw *clk_mux_rp_cpu_normal_p[] = {542	clk_div_rp_cpu_normal_0,543	clk_div_rp_cpu_normal_1,544};545 546static const struct clk_hw *clk_mux_axi_ddr_p[] = {547	clk_div_axi_ddr_0,548	clk_div_axi_ddr_1,549};550 551/* Mux clocks to be updated during probe/registration */552static const struct clk_hw *clk_mux_ddr01[] = { NULL };553static const struct clk_hw *clk_mux_ddr23[] = { NULL };554static const struct clk_hw *clk_mux_rp_cpu_normal[] = { NULL };555static const struct clk_hw *clk_mux_axi_ddr[] = { NULL };556 557static struct sg2042_mux_clock sg2042_mux_clks[] = {558	SG2042_MUX(MUX_CLK_DDR01, "clk_mux_ddr01", clk_mux_ddr01_p,559		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,560		   R_CLKSELREG0, 2, 1),561	SG2042_MUX(MUX_CLK_DDR23, "clk_mux_ddr23", clk_mux_ddr23_p,562		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,563		   R_CLKSELREG0, 3, 1),564	SG2042_MUX(MUX_CLK_RP_CPU_NORMAL, "clk_mux_rp_cpu_normal", clk_mux_rp_cpu_normal_p,565		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,566		   R_CLKSELREG0, 0, 1),567	SG2042_MUX(MUX_CLK_AXI_DDR, "clk_mux_axi_ddr", clk_mux_axi_ddr_p,568		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,569		   R_CLKSELREG0, 1, 1),570};571 572/* Aliases just for easy reading */573#define clk_div_top_rp_cmn_div2	(&sg2042_div_clks_level_2[0].hw)574#define clk_div_50m_a53		(&sg2042_div_clks_level_2[1].hw)575#define clk_div_timer1		(&sg2042_div_clks_level_2[2].hw)576#define clk_div_timer2		(&sg2042_div_clks_level_2[3].hw)577#define clk_div_timer3		(&sg2042_div_clks_level_2[4].hw)578#define clk_div_timer4		(&sg2042_div_clks_level_2[5].hw)579#define clk_div_timer5		(&sg2042_div_clks_level_2[6].hw)580#define clk_div_timer6		(&sg2042_div_clks_level_2[7].hw)581#define clk_div_timer7		(&sg2042_div_clks_level_2[8].hw)582#define clk_div_timer8		(&sg2042_div_clks_level_2[9].hw)583#define clk_div_uart_500m	(&sg2042_div_clks_level_2[10].hw)584#define clk_div_ahb_lpc		(&sg2042_div_clks_level_2[11].hw)585#define clk_div_efuse		(&sg2042_div_clks_level_2[12].hw)586#define clk_div_tx_eth0		(&sg2042_div_clks_level_2[13].hw)587#define clk_div_ptp_ref_i_eth0	(&sg2042_div_clks_level_2[14].hw)588#define clk_div_ref_eth0	(&sg2042_div_clks_level_2[15].hw)589#define clk_div_emmc		(&sg2042_div_clks_level_2[16].hw)590#define clk_div_sd		(&sg2042_div_clks_level_2[17].hw)591#define clk_div_top_axi0	(&sg2042_div_clks_level_2[18].hw)592#define clk_div_100k_emmc	(&sg2042_div_clks_level_2[19].hw)593#define clk_div_100k_sd		(&sg2042_div_clks_level_2[20].hw)594#define clk_div_gpio_db		(&sg2042_div_clks_level_2[21].hw)595#define clk_div_top_axi_hsperi	(&sg2042_div_clks_level_2[22].hw)596 597static struct sg2042_divider_clock sg2042_div_clks_level_2[] = {598	SG2042_DIV_HWS(DIV_CLK_FPLL_TOP_RP_CMN_DIV2,599		       "clk_div_top_rp_cmn_div2", clk_mux_rp_cpu_normal,600		       R_CLKDIVREG3, 16, 16, DEF_DIVFLAG, 2),601 602	SG2042_DIV_FW(DIV_CLK_FPLL_50M_A53, "clk_div_50m_a53", "fpll",603		      R_CLKDIVREG2, 16, 8, DEF_DIVFLAG, 20),604	/* downstream of div_50m_a53 */605	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER1, "clk_div_timer1", clk_div_50m_a53,606		      R_CLKDIVREG6, 16, 16, DEF_DIVFLAG, 1),607	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER2, "clk_div_timer2", clk_div_50m_a53,608		      R_CLKDIVREG7, 16, 16, DEF_DIVFLAG, 1),609	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER3, "clk_div_timer3", clk_div_50m_a53,610		      R_CLKDIVREG8, 16, 16, DEF_DIVFLAG, 1),611	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER4, "clk_div_timer4", clk_div_50m_a53,612		      R_CLKDIVREG9, 16, 16, DEF_DIVFLAG, 1),613	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER5, "clk_div_timer5", clk_div_50m_a53,614		      R_CLKDIVREG10, 16, 16, DEF_DIVFLAG, 1),615	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER6, "clk_div_timer6", clk_div_50m_a53,616		      R_CLKDIVREG11, 16, 16, DEF_DIVFLAG, 1),617	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER7, "clk_div_timer7", clk_div_50m_a53,618		      R_CLKDIVREG12, 16, 16, DEF_DIVFLAG, 1),619	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER8, "clk_div_timer8", clk_div_50m_a53,620		      R_CLKDIVREG13, 16, 16, DEF_DIVFLAG, 1),621 622	/*623	 * Set clk_div_uart_500m as RO, because the width of CLKDIVREG4 is too624	 * narrow for us to produce 115200. Use UART internal divider directly.625	 */626	SG2042_DIV_FW_RO(DIV_CLK_FPLL_UART_500M, "clk_div_uart_500m", "fpll",627			 R_CLKDIVREG4, 16, 7, DEF_DIVFLAG, 2),628	SG2042_DIV_FW(DIV_CLK_FPLL_AHB_LPC, "clk_div_ahb_lpc", "fpll",629		      R_CLKDIVREG5, 16, 16, DEF_DIVFLAG, 5),630	SG2042_DIV_FW(DIV_CLK_FPLL_EFUSE, "clk_div_efuse", "fpll",631		      R_CLKDIVREG14, 16, 7, DEF_DIVFLAG, 40),632	SG2042_DIV_FW(DIV_CLK_FPLL_TX_ETH0, "clk_div_tx_eth0", "fpll",633		      R_CLKDIVREG16, 16, 11, DEF_DIVFLAG, 8),634	SG2042_DIV_FW(DIV_CLK_FPLL_PTP_REF_I_ETH0,635		      "clk_div_ptp_ref_i_eth0", "fpll",636		      R_CLKDIVREG17, 16, 8, DEF_DIVFLAG, 20),637	SG2042_DIV_FW(DIV_CLK_FPLL_REF_ETH0, "clk_div_ref_eth0", "fpll",638		      R_CLKDIVREG18, 16, 8, DEF_DIVFLAG, 40),639	SG2042_DIV_FW(DIV_CLK_FPLL_EMMC, "clk_div_emmc", "fpll",640		      R_CLKDIVREG19, 16, 5, DEF_DIVFLAG, 10),641	SG2042_DIV_FW(DIV_CLK_FPLL_SD, "clk_div_sd", "fpll",642		      R_CLKDIVREG21, 16, 5, DEF_DIVFLAG, 10),643 644	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI0, "clk_div_top_axi0", "fpll",645		      R_CLKDIVREG23, 16, 5, DEF_DIVFLAG, 10),646	/* downstream of div_top_axi0 */647	SG2042_DIV_HW(DIV_CLK_FPLL_100K_EMMC, "clk_div_100k_emmc", clk_div_top_axi0,648		      R_CLKDIVREG20, 16, 16, DEF_DIVFLAG, 1000),649	SG2042_DIV_HW(DIV_CLK_FPLL_100K_SD, "clk_div_100k_sd", clk_div_top_axi0,650		      R_CLKDIVREG22, 16, 16, DEF_DIVFLAG, 1000),651	SG2042_DIV_HW(DIV_CLK_FPLL_GPIO_DB, "clk_div_gpio_db", clk_div_top_axi0,652		      R_CLKDIVREG15, 16, 16, DEF_DIVFLAG, 1000),653 654	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI_HSPERI,655		      "clk_div_top_axi_hsperi", "fpll",656		      R_CLKDIVREG24, 16, 5, DEF_DIVFLAG, 4),657};658 659/* Gate clocks to be updated during probe/registration */660static const struct clk_hw *clk_gate_rp_cpu_normal[] = { NULL };661static const struct clk_hw *clk_gate_top_rp_cmn_div2[] = { NULL };662 663static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] = {664	SG2042_GATE_HWS(GATE_CLK_DDR01, "clk_gate_ddr01", clk_mux_ddr01,665			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,666			R_CLKENREG1, 14),667 668	SG2042_GATE_HWS(GATE_CLK_DDR23, "clk_gate_ddr23", clk_mux_ddr23,669			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,670			R_CLKENREG1, 15),671 672	SG2042_GATE_HWS(GATE_CLK_RP_CPU_NORMAL,673			"clk_gate_rp_cpu_normal", clk_mux_rp_cpu_normal,674			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,675			R_CLKENREG0, 0),676 677	SG2042_GATE_HWS(GATE_CLK_AXI_DDR, "clk_gate_axi_ddr", clk_mux_axi_ddr,678			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,679			R_CLKENREG1, 13),680 681	/* upon are gate clocks directly downstream of muxes */682 683	/* downstream of clk_div_top_rp_cmn_div2 */684	SG2042_GATE_HW(GATE_CLK_TOP_RP_CMN_DIV2,685		       "clk_gate_top_rp_cmn_div2", clk_div_top_rp_cmn_div2,686		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 2),687	SG2042_GATE_HWS(GATE_CLK_HSDMA, "clk_gate_hsdma", clk_gate_top_rp_cmn_div2,688			CLK_SET_RATE_PARENT, R_CLKENREG1, 10),689 690	/*691	 * downstream of clk_gate_rp_cpu_normal692	 *693	 * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal694	 * and clk_gate_axi_pcie0/clk_gate_axi_pcie1.695	 * But the 1/2 DIV is fixed and no configurable register exported, so696	 * when reading from these two clocks, the rate value are still the697	 * same as that of clk_gate_rp_cpu_normal, it's not correct.698	 * This just affects the value read.699	 */700	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0,701			"clk_gate_axi_pcie0", clk_gate_rp_cpu_normal,702			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8),703	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1,704			"clk_gate_axi_pcie1", clk_gate_rp_cpu_normal,705			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9),706 707	/* downstream of div_50m_a53 */708	SG2042_GATE_HW(GATE_CLK_A53_50M, "clk_gate_a53_50m", clk_div_50m_a53,709		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 1),710	SG2042_GATE_HW(GATE_CLK_TIMER1, "clk_gate_timer1", clk_div_timer1,711		       CLK_SET_RATE_PARENT, R_CLKENREG0, 12),712	SG2042_GATE_HW(GATE_CLK_TIMER2, "clk_gate_timer2", clk_div_timer2,713		       CLK_SET_RATE_PARENT, R_CLKENREG0, 13),714	SG2042_GATE_HW(GATE_CLK_TIMER3, "clk_gate_timer3", clk_div_timer3,715		       CLK_SET_RATE_PARENT, R_CLKENREG0, 14),716	SG2042_GATE_HW(GATE_CLK_TIMER4, "clk_gate_timer4", clk_div_timer4,717		       CLK_SET_RATE_PARENT, R_CLKENREG0, 15),718	SG2042_GATE_HW(GATE_CLK_TIMER5, "clk_gate_timer5", clk_div_timer5,719		       CLK_SET_RATE_PARENT, R_CLKENREG0, 16),720	SG2042_GATE_HW(GATE_CLK_TIMER6, "clk_gate_timer6", clk_div_timer6,721		       CLK_SET_RATE_PARENT, R_CLKENREG0, 17),722	SG2042_GATE_HW(GATE_CLK_TIMER7, "clk_gate_timer7", clk_div_timer7,723		       CLK_SET_RATE_PARENT, R_CLKENREG0, 18),724	SG2042_GATE_HW(GATE_CLK_TIMER8, "clk_gate_timer8", clk_div_timer8,725		       CLK_SET_RATE_PARENT, R_CLKENREG0, 19),726 727	/* gate clocks downstream from div clocks one-to-one */728	SG2042_GATE_HW(GATE_CLK_UART_500M, "clk_gate_uart_500m", clk_div_uart_500m,729		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 4),730	SG2042_GATE_HW(GATE_CLK_AHB_LPC, "clk_gate_ahb_lpc", clk_div_ahb_lpc,731		       CLK_SET_RATE_PARENT, R_CLKENREG0, 7),732	SG2042_GATE_HW(GATE_CLK_EFUSE, "clk_gate_efuse", clk_div_efuse,733		       CLK_SET_RATE_PARENT, R_CLKENREG0, 20),734	SG2042_GATE_HW(GATE_CLK_TX_ETH0, "clk_gate_tx_eth0", clk_div_tx_eth0,735		       CLK_SET_RATE_PARENT, R_CLKENREG0, 30),736	SG2042_GATE_HW(GATE_CLK_PTP_REF_I_ETH0,737		       "clk_gate_ptp_ref_i_eth0", clk_div_ptp_ref_i_eth0,738		       CLK_SET_RATE_PARENT, R_CLKENREG1, 0),739	SG2042_GATE_HW(GATE_CLK_REF_ETH0, "clk_gate_ref_eth0", clk_div_ref_eth0,740		       CLK_SET_RATE_PARENT, R_CLKENREG1, 1),741	SG2042_GATE_HW(GATE_CLK_EMMC_100M, "clk_gate_emmc", clk_div_emmc,742		       CLK_SET_RATE_PARENT, R_CLKENREG1, 3),743	SG2042_GATE_HW(GATE_CLK_SD_100M, "clk_gate_sd", clk_div_sd,744		       CLK_SET_RATE_PARENT, R_CLKENREG1, 6),745 746	/* downstream of clk_div_top_axi0 */747	SG2042_GATE_HW(GATE_CLK_AHB_ROM, "clk_gate_ahb_rom", clk_div_top_axi0,748		       0, R_CLKENREG0, 8),749	SG2042_GATE_HW(GATE_CLK_AHB_SF, "clk_gate_ahb_sf", clk_div_top_axi0,750		       0, R_CLKENREG0, 9),751	SG2042_GATE_HW(GATE_CLK_AXI_SRAM, "clk_gate_axi_sram", clk_div_top_axi0,752		       CLK_IGNORE_UNUSED, R_CLKENREG0, 10),753	SG2042_GATE_HW(GATE_CLK_APB_TIMER, "clk_gate_apb_timer", clk_div_top_axi0,754		       CLK_IGNORE_UNUSED, R_CLKENREG0, 11),755	SG2042_GATE_HW(GATE_CLK_APB_EFUSE, "clk_gate_apb_efuse", clk_div_top_axi0,756		       0, R_CLKENREG0, 21),757	SG2042_GATE_HW(GATE_CLK_APB_GPIO, "clk_gate_apb_gpio", clk_div_top_axi0,758		       0, R_CLKENREG0, 22),759	SG2042_GATE_HW(GATE_CLK_APB_GPIO_INTR,760		       "clk_gate_apb_gpio_intr", clk_div_top_axi0,761		       CLK_IS_CRITICAL, R_CLKENREG0, 23),762	SG2042_GATE_HW(GATE_CLK_APB_I2C, "clk_gate_apb_i2c", clk_div_top_axi0,763		       0, R_CLKENREG0, 26),764	SG2042_GATE_HW(GATE_CLK_APB_WDT, "clk_gate_apb_wdt", clk_div_top_axi0,765		       0, R_CLKENREG0, 27),766	SG2042_GATE_HW(GATE_CLK_APB_PWM, "clk_gate_apb_pwm", clk_div_top_axi0,767		       0, R_CLKENREG0, 28),768	SG2042_GATE_HW(GATE_CLK_APB_RTC, "clk_gate_apb_rtc", clk_div_top_axi0,769		       0, R_CLKENREG0, 29),770	SG2042_GATE_HW(GATE_CLK_TOP_AXI0, "clk_gate_top_axi0", clk_div_top_axi0,771		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,772		       R_CLKENREG1, 11),773	/* downstream of DIV clocks which are sourced from clk_div_top_axi0 */774	SG2042_GATE_HW(GATE_CLK_GPIO_DB, "clk_gate_gpio_db", clk_div_gpio_db,775		       CLK_SET_RATE_PARENT, R_CLKENREG0, 24),776	SG2042_GATE_HW(GATE_CLK_100K_EMMC, "clk_gate_100k_emmc", clk_div_100k_emmc,777		       CLK_SET_RATE_PARENT, R_CLKENREG1, 4),778	SG2042_GATE_HW(GATE_CLK_100K_SD, "clk_gate_100k_sd", clk_div_100k_sd,779		       CLK_SET_RATE_PARENT, R_CLKENREG1, 7),780 781	/* downstream of clk_div_top_axi_hsperi */782	SG2042_GATE_HW(GATE_CLK_SYSDMA_AXI,783		       "clk_gate_sysdma_axi", clk_div_top_axi_hsperi,784		       CLK_SET_RATE_PARENT, R_CLKENREG0, 3),785	SG2042_GATE_HW(GATE_CLK_APB_UART,786		       "clk_gate_apb_uart", clk_div_top_axi_hsperi,787		       CLK_SET_RATE_PARENT, R_CLKENREG0, 5),788	SG2042_GATE_HW(GATE_CLK_AXI_DBG_I2C,789		       "clk_gate_axi_dbg_i2c", clk_div_top_axi_hsperi,790		       CLK_SET_RATE_PARENT, R_CLKENREG0, 6),791	SG2042_GATE_HW(GATE_CLK_APB_SPI,792		       "clk_gate_apb_spi", clk_div_top_axi_hsperi,793		       CLK_SET_RATE_PARENT, R_CLKENREG0, 25),794	SG2042_GATE_HW(GATE_CLK_AXI_ETH0,795		       "clk_gate_axi_eth0", clk_div_top_axi_hsperi,796		       CLK_SET_RATE_PARENT, R_CLKENREG0, 31),797	SG2042_GATE_HW(GATE_CLK_AXI_EMMC,798		       "clk_gate_axi_emmc", clk_div_top_axi_hsperi,799		       CLK_SET_RATE_PARENT, R_CLKENREG1, 2),800	SG2042_GATE_HW(GATE_CLK_AXI_SD,801		       "clk_gate_axi_sd", clk_div_top_axi_hsperi,802		       CLK_SET_RATE_PARENT, R_CLKENREG1, 5),803	SG2042_GATE_HW(GATE_CLK_TOP_AXI_HSPERI,804		       "clk_gate_top_axi_hsperi", clk_div_top_axi_hsperi,805		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,806		       R_CLKENREG1, 12),807};808 809static DEFINE_SPINLOCK(sg2042_clk_lock);810 811static int sg2042_clk_register_divs(struct device *dev,812				    struct sg2042_clk_data *clk_data,813				    struct sg2042_divider_clock div_clks[],814				    int num_div_clks)815{816	struct sg2042_divider_clock *div;817	struct clk_hw *hw;818	int i, ret = 0;819 820	for (i = 0; i < num_div_clks; i++) {821		div = &div_clks[i];822 823		if (div->div_flags & CLK_DIVIDER_HIWORD_MASK) {824			if (div->width + div->shift > 16) {825				pr_warn("divider value exceeds LOWORD field\n");826				ret = -EINVAL;827				break;828			}829		}830 831		div->reg = clk_data->iobase + div->offset_ctrl;832		div->lock = &sg2042_clk_lock;833 834		hw = &div->hw;835		ret = devm_clk_hw_register(dev, hw);836		if (ret) {837			pr_err("failed to register clock %s\n", div->hw.init->name);838			break;839		}840 841		clk_data->onecell_data.hws[div->id] = hw;842	}843 844	return ret;845}846 847static int sg2042_clk_register_gates(struct device *dev,848				     struct sg2042_clk_data *clk_data,849				     const struct sg2042_gate_clock gate_clks[],850				     int num_gate_clks)851{852	const struct sg2042_gate_clock *gate;853	struct clk_hw *hw;854	int i, ret = 0;855 856	for (i = 0; i < num_gate_clks; i++) {857		gate = &gate_clks[i];858		hw = __devm_clk_hw_register_gate859			(dev,860			 NULL,861			 gate->hw.init->name,862			 NULL,863			 gate->hw.init->parent_hws[0],864			 NULL,865			 gate->hw.init->flags,866			 clk_data->iobase + gate->offset_enable,867			 gate->bit_idx,868			 0,869			 &sg2042_clk_lock);870		if (IS_ERR(hw)) {871			pr_err("failed to register clock %s\n", gate->hw.init->name);872			ret = PTR_ERR(hw);873			break;874		}875 876		clk_data->onecell_data.hws[gate->id] = hw;877 878		/* Updated some clocks which take the role of parent */879		switch (gate->id) {880		case GATE_CLK_RP_CPU_NORMAL:881			*clk_gate_rp_cpu_normal = hw;882			break;883		case GATE_CLK_TOP_RP_CMN_DIV2:884			*clk_gate_top_rp_cmn_div2 = hw;885			break;886		}887	}888 889	return ret;890}891 892static int sg2042_clk_register_gates_fw(struct device *dev,893					struct sg2042_clk_data *clk_data,894					const struct sg2042_gate_clock gate_clks[],895					int num_gate_clks)896{897	const struct sg2042_gate_clock *gate;898	struct clk_hw *hw;899	int i, ret = 0;900 901	for (i = 0; i < num_gate_clks; i++) {902		gate = &gate_clks[i];903		hw = devm_clk_hw_register_gate_parent_data904			(dev,905			 gate->hw.init->name,906			 gate->hw.init->parent_data,907			 gate->hw.init->flags,908			 clk_data->iobase + gate->offset_enable,909			 gate->bit_idx,910			 0,911			 &sg2042_clk_lock);912		if (IS_ERR(hw)) {913			pr_err("failed to register clock %s\n", gate->hw.init->name);914			ret = PTR_ERR(hw);915			break;916		}917 918		clk_data->onecell_data.hws[gate->id] = hw;919 920		/* Updated some clocks which take the role of parent */921		switch (gate->id) {922		case GATE_CLK_DDR01_DIV0:923			*clk_gate_ddr01_div0 = hw;924			break;925		case GATE_CLK_DDR01_DIV1:926			*clk_gate_ddr01_div1 = hw;927			break;928		case GATE_CLK_DDR23_DIV0:929			*clk_gate_ddr23_div0 = hw;930			break;931		case GATE_CLK_DDR23_DIV1:932			*clk_gate_ddr23_div1 = hw;933			break;934		case GATE_CLK_RP_CPU_NORMAL_DIV0:935			*clk_gate_rp_cpu_normal_div0 = hw;936			break;937		case GATE_CLK_RP_CPU_NORMAL_DIV1:938			*clk_gate_rp_cpu_normal_div1 = hw;939			break;940		case GATE_CLK_AXI_DDR_DIV0:941			*clk_gate_axi_ddr_div0 = hw;942			break;943		case GATE_CLK_AXI_DDR_DIV1:944			*clk_gate_axi_ddr_div1 = hw;945			break;946		}947	}948 949	return ret;950}951 952static int sg2042_mux_notifier_cb(struct notifier_block *nb,953				  unsigned long event,954				  void *data)955{956	struct sg2042_mux_clock *mux = to_sg2042_mux_nb(nb);957	const struct clk_ops *ops = &clk_mux_ops;958	struct clk_notifier_data *ndata = data;959	struct clk_hw *hw;960	int ret = 0;961 962	hw = __clk_get_hw(ndata->clk);963 964	/* To switch to fpll before changing rate and restore after that */965	if (event == PRE_RATE_CHANGE) {966		mux->original_index = ops->get_parent(hw);967 968		/*969		 * "1" is the array index of the second parent input source of970		 * mux. For SG2042, it's fpll for all mux clocks.971		 * "0" is the array index of the frist parent input source of972		 * mux, For SG2042, it's mpll.973		 * FIXME, any good idea to avoid magic number?974		 */975		if (mux->original_index == 0)976			ret = ops->set_parent(hw, 1);977	} else if (event == POST_RATE_CHANGE) {978		ret = ops->set_parent(hw, mux->original_index);979	}980 981	return notifier_from_errno(ret);982}983 984static int sg2042_clk_register_muxs(struct device *dev,985				    struct sg2042_clk_data *clk_data,986				    struct sg2042_mux_clock mux_clks[],987				    int num_mux_clks)988{989	struct sg2042_mux_clock *mux;990	struct clk_hw *hw;991	int i, ret = 0;992 993	for (i = 0; i < num_mux_clks; i++) {994		mux = &mux_clks[i];995 996		hw = __devm_clk_hw_register_mux997			(dev,998			 NULL,999			 mux->hw.init->name,1000			 mux->hw.init->num_parents,1001			 NULL,1002			 mux->hw.init->parent_hws,1003			 NULL,1004			 mux->hw.init->flags,1005			 clk_data->iobase + mux->offset_select,1006			 mux->shift,1007			 BIT(mux->width) - 1,1008			 0,1009			 sg2042_mux_table,1010			 &sg2042_clk_lock);1011		if (IS_ERR(hw)) {1012			pr_err("failed to register clock %s\n", mux->hw.init->name);1013			ret = PTR_ERR(hw);1014			break;1015		}1016 1017		clk_data->onecell_data.hws[mux->id] = hw;1018 1019		/* Updated some clocks which takes the role of parent */1020		switch (mux->id) {1021		case MUX_CLK_DDR01:1022			*clk_mux_ddr01 = hw;1023			break;1024		case MUX_CLK_DDR23:1025			*clk_mux_ddr23 = hw;1026			break;1027		case MUX_CLK_RP_CPU_NORMAL:1028			*clk_mux_rp_cpu_normal = hw;1029			break;1030		case MUX_CLK_AXI_DDR:1031			*clk_mux_axi_ddr = hw;1032			break;1033		}1034 1035		/*1036		 * FIXME: Theoretically, we should set parent for the1037		 * mux, but seems hardware has done this for us with1038		 * default value, so we don't set parent again here.1039		 */1040 1041		if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) {1042			mux->clk_nb.notifier_call = sg2042_mux_notifier_cb;1043			ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb);1044			if (ret) {1045				pr_err("failed to register clock notifier for %s\n",1046				       mux->hw.init->name);1047				break;1048			}1049		}1050	}1051 1052	return ret;1053}1054 1055static int sg2042_init_clkdata(struct platform_device *pdev,1056			       int num_clks,1057			       struct sg2042_clk_data **pp_clk_data)1058{1059	struct sg2042_clk_data *clk_data = NULL;1060 1061	clk_data = devm_kzalloc(&pdev->dev,1062				struct_size(clk_data, onecell_data.hws, num_clks),1063				GFP_KERNEL);1064	if (!clk_data)1065		return -ENOMEM;1066 1067	clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);1068	if (WARN_ON(IS_ERR(clk_data->iobase)))1069		return PTR_ERR(clk_data->iobase);1070 1071	clk_data->onecell_data.num = num_clks;1072 1073	*pp_clk_data = clk_data;1074 1075	return 0;1076}1077 1078static int sg2042_clkgen_probe(struct platform_device *pdev)1079{1080	struct sg2042_clk_data *clk_data = NULL;1081	int num_clks;1082	int ret;1083 1084	num_clks = ARRAY_SIZE(sg2042_div_clks_level_1) +1085		   ARRAY_SIZE(sg2042_div_clks_level_2) +1086		   ARRAY_SIZE(sg2042_gate_clks_level_1) +1087		   ARRAY_SIZE(sg2042_gate_clks_level_2) +1088		   ARRAY_SIZE(sg2042_mux_clks);1089 1090	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);1091	if (ret)1092		goto error_out;1093 1094	/* level-1 gates */1095	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data,1096					   sg2042_gate_clks_level_1,1097					   ARRAY_SIZE(sg2042_gate_clks_level_1));1098	if (ret)1099		goto error_out;1100 1101	/* level-1 div */1102	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1,1103				       ARRAY_SIZE(sg2042_div_clks_level_1));1104	if (ret)1105		goto error_out;1106 1107	/* mux */1108	ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks,1109				       ARRAY_SIZE(sg2042_mux_clks));1110	if (ret)1111		goto error_out;1112 1113	/* level 2 div */1114	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2,1115				       ARRAY_SIZE(sg2042_div_clks_level_2));1116	if (ret)1117		goto error_out;1118 1119	/* level 2 gate */1120	ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2,1121					ARRAY_SIZE(sg2042_gate_clks_level_2));1122	if (ret)1123		goto error_out;1124 1125	return devm_of_clk_add_hw_provider(&pdev->dev,1126					   of_clk_hw_onecell_get,1127					   &clk_data->onecell_data);1128 1129error_out:1130	pr_err("%s failed error number %d\n", __func__, ret);1131	return ret;1132}1133 1134static const struct of_device_id sg2042_clkgen_match[] = {1135	{ .compatible = "sophgo,sg2042-clkgen" },1136	{ /* sentinel */ }1137};1138MODULE_DEVICE_TABLE(of, sg2042_clkgen_match);1139 1140static struct platform_driver sg2042_clkgen_driver = {1141	.probe = sg2042_clkgen_probe,1142	.driver = {1143		.name = "clk-sophgo-sg2042-clkgen",1144		.of_match_table = sg2042_clkgen_match,1145		.suppress_bind_attrs = true,1146	},1147};1148module_platform_driver(sg2042_clkgen_driver);1149 1150MODULE_AUTHOR("Chen Wang");1151MODULE_DESCRIPTION("Sophgo SG2042 clock generator driver");1152MODULE_LICENSE("GPL");1153