linux/drivers/clk/sophgo/clk-sg2042-clkgen.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Sophgo SG2042 Clock Generator Driver
 *
 * Copyright (C) 2024 Sophgo Technology Inc.
 * Copyright (C) 2024 Chen Wang <[email protected]>
 */

#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <asm/div64.h>

#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>

#include "clk-sg2042.h"

/* Registers defined in SYS_CTRL */
#define R_PLL_BEGIN
#define R_PLL_STAT
#define R_PLL_CLKEN_CONTROL
#define R_MPLL_CONTROL
#define R_FPLL_CONTROL
#define R_DPLL0_CONTROL
#define R_DPLL1_CONTROL

/* Registers defined in CLOCK */
#define R_CLKENREG0
#define R_CLKENREG1
#define R_CLKSELREG0
#define R_CLKDIVREG0
#define R_CLKDIVREG1
#define R_CLKDIVREG2
#define R_CLKDIVREG3
#define R_CLKDIVREG4
#define R_CLKDIVREG5
#define R_CLKDIVREG6
#define R_CLKDIVREG7
#define R_CLKDIVREG8
#define R_CLKDIVREG9
#define R_CLKDIVREG10
#define R_CLKDIVREG11
#define R_CLKDIVREG12
#define R_CLKDIVREG13
#define R_CLKDIVREG14
#define R_CLKDIVREG15
#define R_CLKDIVREG16
#define R_CLKDIVREG17
#define R_CLKDIVREG18
#define R_CLKDIVREG19
#define R_CLKDIVREG20
#define R_CLKDIVREG21
#define R_CLKDIVREG22
#define R_CLKDIVREG23
#define R_CLKDIVREG24
#define R_CLKDIVREG25
#define R_CLKDIVREG26
#define R_CLKDIVREG27
#define R_CLKDIVREG28
#define R_CLKDIVREG29
#define R_CLKDIVREG30

/* All following shift value are the same for all DIV registers */
#define SHIFT_DIV_RESET_CTRL
#define SHIFT_DIV_FACTOR_SEL
#define SHIFT_DIV_FACTOR

/**
 * struct sg2042_divider_clock - Divider clock
 * @hw:			clk_hw for initialization
 * @id:			used to map clk_onecell_data
 * @reg:		used for readl/writel.
 *			**NOTE**: DIV registers are ALL in CLOCK!
 * @lock:		spinlock to protect register access, modification of
 *			frequency can only be served one at the time
 * @offset_ctrl:	offset of divider control registers
 * @shift:		shift of "Clock Divider Factor" in divider control register
 * @width:		width of "Clock Divider Factor" in divider control register
 * @div_flags:		private flags for this clock, not for framework-specific
 * @initval:		In the divider control register, we can configure whether
 *			to use the value of "Clock Divider Factor" or just use
 *			the initial value pre-configured by IC. BIT[3] controls
 *			this and by default (value is 0), means initial value
 *			is used.
 *			**NOTE** that we cannot read the initial value (default
 *			value when poweron) and default value of "Clock Divider
 *			Factor" is zero, which I think is a hardware design flaw
 *			and should be sync-ed with the initial value. So in
 *			software we have to add a configuration item (initval)
 *			to manually configure this value and use it when BIT[3]
 *			is zero.
 */
struct sg2042_divider_clock {};

#define to_sg2042_clk_divider(_hw)

/**
 * struct sg2042_gate_clock - Gate clock
 * @hw:			clk_hw for initialization
 * @id:			used to map clk_onecell_data
 * @offset_enable:	offset of gate enable registers
 * @bit_idx:		which bit in the register controls gating of this clock
 */
struct sg2042_gate_clock {};

/**
 * struct sg2042_mux_clock - Mux clock
 * @hw:			clk_hw for initialization
 * @id:			used to map clk_onecell_data
 * @offset_select:	offset of mux selection registers
 *			**NOTE**: MUX registers are ALL in CLOCK!
 * @shift:		shift of "Clock Select" in mux selection register
 * @width:		width of "Clock Select" in mux selection register
 * @clk_nb:		used for notification
 * @original_index:	set by notifier callback
 */
struct sg2042_mux_clock {};

#define to_sg2042_mux_nb(_nb)

static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw,
						    unsigned long parent_rate)
{}

static long sg2042_clk_divider_round_rate(struct clk_hw *hw,
					  unsigned long rate,
					  unsigned long *prate)
{}

static int sg2042_clk_divider_set_rate(struct clk_hw *hw,
				       unsigned long rate,
				       unsigned long parent_rate)
{}

static const struct clk_ops sg2042_clk_divider_ops =;

static const struct clk_ops sg2042_clk_divider_ro_ops =;

/*
 * Clock initialization macro naming rules:
 * FW: use CLK_HW_INIT_FW_NAME
 * HW: use CLK_HW_INIT_HW
 * HWS: use CLK_HW_INIT_HWS
 * RO: means Read-Only
 */
#define SG2042_DIV_FW(_id, _name, _parent,				\
		      _r_ctrl, _shift, _width,				\
		      _div_flag, _initval)

#define SG2042_DIV_FW_RO(_id, _name, _parent,				\
		  _r_ctrl, _shift, _width,				\
		  _div_flag, _initval)

#define SG2042_DIV_HW(_id, _name, _parent,				\
		      _r_ctrl, _shift, _width,				\
		      _div_flag, _initval)

#define SG2042_DIV_HW_RO(_id, _name, _parent,				\
			 _r_ctrl, _shift, _width,			\
			 _div_flag, _initval)

#define SG2042_DIV_HWS(_id, _name, _parent,				\
		       _r_ctrl, _shift, _width,				\
		       _div_flag, _initval)

#define SG2042_DIV_HWS_RO(_id, _name, _parent,				\
			  _r_ctrl, _shift, _width,			\
			  _div_flag, _initval)

#define SG2042_GATE_HWS(_id, _name, _parent, _flags,	\
			_r_enable, _bit_idx)

#define SG2042_GATE_HW(_id, _name, _parent, _flags,	\
		       _r_enable, _bit_idx)

#define SG2042_GATE_FW(_id, _name, _parent, _flags,	\
		       _r_enable, _bit_idx)

#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width)

/*
 * Clock items in the array are sorted according to the clock-tree diagram,
 * from top to bottom, from upstream to downstream. Read TRM for details.
 */

/* updated during probe/registration */
static const struct clk_hw *clk_gate_ddr01_div0[] =;
static const struct clk_hw *clk_gate_ddr01_div1[] =;
static const struct clk_hw *clk_gate_ddr23_div0[] =;
static const struct clk_hw *clk_gate_ddr23_div1[] =;
static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] =;
static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] =;
static const struct clk_hw *clk_gate_axi_ddr_div0[] =;
static const struct clk_hw *clk_gate_axi_ddr_div1[] =;

static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] =;

#define DEF_DIVFLAG

static struct sg2042_divider_clock sg2042_div_clks_level_1[] =;

/*
 * Note: regarding names for mux clock, "0/1" or "div0/div1" means the
 * first/second parent input source, not the register value.
 * For example:
 * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and
 * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0",
 * they are both controlled by register CLKDIVREG27;
 * "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[] =;

/* Aliases just for easy reading */
#define clk_div_ddr01_0
#define clk_div_ddr01_1
#define clk_div_ddr23_0
#define clk_div_ddr23_1
#define clk_div_rp_cpu_normal_0
#define clk_div_rp_cpu_normal_1
#define clk_div_axi_ddr_0
#define clk_div_axi_ddr_1

static const struct clk_hw *clk_mux_ddr01_p[] =;

static const struct clk_hw *clk_mux_ddr23_p[] =;

static const struct clk_hw *clk_mux_rp_cpu_normal_p[] =;

static const struct clk_hw *clk_mux_axi_ddr_p[] =;

/* Mux clocks to be updated during probe/registration */
static const struct clk_hw *clk_mux_ddr01[] =;
static const struct clk_hw *clk_mux_ddr23[] =;
static const struct clk_hw *clk_mux_rp_cpu_normal[] =;
static const struct clk_hw *clk_mux_axi_ddr[] =;

static struct sg2042_mux_clock sg2042_mux_clks[] =;

/* Aliases just for easy reading */
#define clk_div_top_rp_cmn_div2
#define clk_div_50m_a53
#define clk_div_timer1
#define clk_div_timer2
#define clk_div_timer3
#define clk_div_timer4
#define clk_div_timer5
#define clk_div_timer6
#define clk_div_timer7
#define clk_div_timer8
#define clk_div_uart_500m
#define clk_div_ahb_lpc
#define clk_div_efuse
#define clk_div_tx_eth0
#define clk_div_ptp_ref_i_eth0
#define clk_div_ref_eth0
#define clk_div_emmc
#define clk_div_sd
#define clk_div_top_axi0
#define clk_div_100k_emmc
#define clk_div_100k_sd
#define clk_div_gpio_db
#define clk_div_top_axi_hsperi

static struct sg2042_divider_clock sg2042_div_clks_level_2[] =;

/* Gate clocks to be updated during probe/registration */
static const struct clk_hw *clk_gate_rp_cpu_normal[] =;
static const struct clk_hw *clk_gate_top_rp_cmn_div2[] =;

static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] =;

static DEFINE_SPINLOCK(sg2042_clk_lock);

static int sg2042_clk_register_divs(struct device *dev,
				    struct sg2042_clk_data *clk_data,
				    struct sg2042_divider_clock div_clks[],
				    int num_div_clks)
{}

static int sg2042_clk_register_gates(struct device *dev,
				     struct sg2042_clk_data *clk_data,
				     const struct sg2042_gate_clock gate_clks[],
				     int num_gate_clks)
{}

static int sg2042_clk_register_gates_fw(struct device *dev,
					struct sg2042_clk_data *clk_data,
					const struct sg2042_gate_clock gate_clks[],
					int num_gate_clks)
{}

static int sg2042_mux_notifier_cb(struct notifier_block *nb,
				  unsigned long event,
				  void *data)
{}

static int sg2042_clk_register_muxs(struct device *dev,
				    struct sg2042_clk_data *clk_data,
				    struct sg2042_mux_clock mux_clks[],
				    int num_mux_clks)
{}

static int sg2042_init_clkdata(struct platform_device *pdev,
			       int num_clks,
			       struct sg2042_clk_data **pp_clk_data)
{}

static int sg2042_clkgen_probe(struct platform_device *pdev)
{}

static const struct of_device_id sg2042_clkgen_match[] =;
MODULE_DEVICE_TABLE(of, sg2042_clkgen_match);

static struct platform_driver sg2042_clkgen_driver =;
module_platform_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();