/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2025 Zhihe Computing Limited. */ #ifndef __MACH_ZHIHE_CLK_H #define __MACH_ZHIHE_CLK_H #include #include #include #include extern spinlock_t zhihe_clk_lock; #define ZHIHE_CLK_NAME_SIZE 40 #define PLL_RATE(_vco, _rate, _r, _b, _f, _p, _k) \ { \ .vco_rate = (_vco), \ .rate = (_rate), \ .refdiv = (_r), \ .fbdiv = (_b), \ .frac = (_f), \ .postdiv1 = (_p), \ .postdiv2 = (_k), \ } #define REG(n) [(n)] = {.name = #n} #define CLK(_type, _id, _name, _parent) \ .type = (_type), \ .id = (_id), \ .name = (_name), \ .parent = (_parent) #define FIXED(_id, _name, _parent, _freq) { \ CLK(CLK_TYPE_FIXED, (_id), (_name), (_parent)), \ .fixed = {.freq = (_freq),}, \ } #define PLL(_id, _name, _parent, _reg, _shift, _pll) { \ CLK(CLK_TYPE_PLL, (_id), (_name), (_parent)), \ .reg = (_reg), \ .shift = (_shift), \ .pll = (_pll), \ } #define FIXED_FACTOR(_id, _name, _parent, _mult, _div) { \ CLK(CLK_TYPE_FIXED_FACTOR, (_id), (_name), (_parent)), \ .fixed_factor = {.mult = (_mult), .div = (_div),}, \ } #define DIV(_id, _name, _parent, _reg, _shift, _bit_idx, _width, _sync, _div_type, _min, _max) { \ CLK(CLK_TYPE_DIVIDER, (_id), (_name), (_parent)), \ .reg = (_reg), \ .shift = (_shift), \ .bit_idx = (_bit_idx), \ .width = (_width), \ .divider = {.sync = (_sync), .div_type = (_div_type), .min = (_min), .max = (_max)} \ } #define DIV_CLOSEST(_id, _name, _parent, _reg, _shift, _bit_idx, _width, _sync, _div_type, _min, _max) { \ CLK(CLK_TYPE_DIVIDER_CLOSEST, (_id), (_name), (_parent)), \ .reg = (_reg), \ .shift = (_shift), \ .bit_idx = (_bit_idx), \ .width = (_width), \ .divider = {.sync = (_sync), .div_type = (_div_type), .min = (_min), .max = (_max)} \ } #define GATE(_id, _name, _parent, _reg, _shift, _bit_idx) { \ CLK(CLK_TYPE_GATE, (_id), (_name), (_parent)), \ .reg = (_reg), \ .shift = (_shift), \ .bit_idx = (_bit_idx), \ } #define GATE_SHARED(_id, _name, _parent, _reg, _shift, _bit_idx, _count) { \ CLK(CLK_TYPE_GATE_SHARED, (_id), (_name), (_parent)), \ .reg = (_reg), \ .shift = (_shift), \ .bit_idx = (_bit_idx), \ .gate_shared = {.share_count = (_count)}, \ } #define MUX(_id, _name, _reg, _shift, _bit_idx, _width, _parents, _num, _flags) { \ CLK(CLK_TYPE_MUX, (_id), (_name), ""), \ .reg = (_reg), \ .shift = (_shift), \ .bit_idx = (_bit_idx), \ .width = (_width), \ .mux = {.parents = (_parents), .num_parents = (_num), .flags = (_flags)}, \ } #define PLL_PARAM(_pll, _out, _table, _count, _cfg0, _sts, _lock, _bypass, _rst, _mode, _name) \ [(_pll)] = { \ .out_type = (_out), \ .clk_type = (_pll), \ .rate_table = (_table), \ .rate_count = (_count), \ .cfg0_reg_off = (_cfg0), \ .pll_sts_off = (_sts), \ .pll_lock_bit = (_lock), \ .pll_bypass_bit = (_bypass), \ .pll_rst_bit = (_rst), \ .pll_mode = (_mode), \ .name = (_name), \ } #define CLK_SUBSYS(_name, _regs, _num_reg, _info, _num_info, _pll, _num_pll, _is_fpga, _die_num) { \ .name = (_name), \ .regs = (_regs), \ .num_regs = (_num_reg), \ .info = (_info), \ .num_info = (_num_info), \ .plls = (_pll), \ .num_plls = (_num_pll), \ .is_fpga = (_is_fpga), \ .die_num = (_die_num), \ } enum zhihe_pll_outtype { ZHIHE_PLL_VCO, ZHIHE_PLL_DIV, }; enum zhihe_div_type { MUX_TYPE_DIV, MUX_TYPE_CDE, }; enum zhihe_div_sync_type { NO_DIV_EN = 255, }; enum zhihe_clk_types { CLK_TYPE_FIXED, CLK_TYPE_PLL, CLK_TYPE_FIXED_FACTOR, CLK_TYPE_DIVIDER, CLK_TYPE_DIVIDER_CLOSEST, CLK_TYPE_GATE, CLK_TYPE_GATE_SHARED, CLK_TYPE_MUX, }; enum zhihe_pll_mode { PLL_MODE_FRAC, PLL_MODE_INT, }; struct zhihe_clk_reg { void __iomem *base; char name[ZHIHE_CLK_NAME_SIZE]; }; /* clk summary info at subsys level */ struct zhihe_clk_subsys { char name[ZHIHE_CLK_NAME_SIZE]; struct zhihe_clk_reg *regs; u32 num_regs; struct zhihe_clk_info *info; u32 num_info; struct zhihe_clk_info_pll *plls; u32 num_plls; bool is_fpga; struct clk_onecell_data *clk_data; char die_num; }; struct clk_zhihepll { struct clk_hw hw; void __iomem *base; unsigned int clk_type; enum zhihe_pll_outtype out_type; enum zhihe_pll_mode pll_mode; const struct zhihe_pll_rate_table *rate_table; int rate_count; u32 cfg0_reg_off; u32 pll_sts_off; int pll_lock_bit; int pll_rst_bit; int pll_bypass_bit; }; struct clk_zhihediv { struct clk_divider divider; enum zhihe_div_type div_type; u16 min_div; u16 max_div; u8 sync_en; const struct clk_ops *ops; }; struct clk_zhihegate { struct clk_gate gate; unsigned int *share_count; const struct clk_ops *ops; }; struct zhihe_pll_rate_table { unsigned long vco_rate; unsigned long rate; unsigned int refdiv; unsigned int fbdiv; unsigned int frac; unsigned int postdiv1; unsigned int postdiv2; }; /* detailed clk info for each clk */ struct zhihe_clk_info_pll { enum zhihe_pll_outtype out_type; unsigned int clk_type; struct zhihe_pll_rate_table *rate_table; int rate_count; int flags; char name[ZHIHE_CLK_NAME_SIZE]; u32 cfg0_reg_off; u32 pll_sts_off; int pll_lock_bit; int pll_rst_bit; int pll_bypass_bit; enum zhihe_pll_mode pll_mode; }; struct zhihe_clk_info_fixed { unsigned int freq; }; struct zhihe_clk_info_fixed_factor { unsigned int mult; unsigned int div; }; struct zhihe_clk_info_divider { u8 sync; enum zhihe_div_type div_type; u16 min; u16 max; }; struct zhihe_clk_info_gate_shared { unsigned int *share_count; }; struct zhihe_clk_info_mux { const char * const *parents; int num_parents; unsigned long flags; }; struct zhihe_clk_info { enum zhihe_clk_types type; u32 id; char name[ZHIHE_CLK_NAME_SIZE]; char parent[ZHIHE_CLK_NAME_SIZE]; u8 reg; u32 shift; u8 width; u8 bit_idx; union { struct zhihe_clk_info_fixed fixed; struct zhihe_clk_info_fixed_factor fixed_factor; struct zhihe_clk_info_pll *pll; struct zhihe_clk_info_divider divider; struct zhihe_clk_info_gate_shared gate_shared; struct zhihe_clk_info_mux mux; }; }; static inline struct clk *zhihe_clk_fixed_factor(const char *name, const char *parent, unsigned int mult, unsigned int div) { return clk_register_fixed_factor(NULL, name, parent, CLK_SET_RATE_PARENT, mult, div); } struct clk *zhihe_pll(const char *name, const char *parent_name, void __iomem *base, const struct zhihe_clk_info_pll *pll_clk); static inline struct clk *zhihe_clk_gate(const char *name, const char *parent, void __iomem *reg, u8 shift) { return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, shift, 0, &zhihe_clk_lock); } struct clk *zhihe_clk_register_gate_shared(const char *name, const char *parent, unsigned long flags, void __iomem *reg, u8 shift, spinlock_t *lock, unsigned int *share_count); struct clk *zhihe_clk_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width, u8 sync, enum zhihe_div_type div_type, u16 min, u16 max); /** * By default, the clk framework calculates frequency by rounding downwards. * This function is to achieve closest frequency. */ struct clk *zhihe_clk_divider_closest(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width, u8 sync, enum zhihe_div_type div_type, u16 min, u16 max); static inline struct clk *zhihe_clk_fixed(const char *name, const char *parent, unsigned long rate) { return clk_register_fixed_rate(NULL, name, parent, 0, rate); } static inline struct clk *zhihe_clk_gate_shared(const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned int *share_count) { return zhihe_clk_register_gate_shared(name, parent, CLK_SET_RATE_PARENT, reg, shift, &zhihe_clk_lock, share_count); } static inline struct clk *zhihe_clk_mux_flags(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents, unsigned long flags) { return clk_register_mux(NULL, name, parents, num_parents, flags , reg, shift, width, 0, &zhihe_clk_lock); } void zhihe_clk_fake_pll_fixed_ops(void); int zhihe_clk_set_round_rate(struct device *dev, struct clk *clk, unsigned int freq); int zhihe_clk_of_bulk_init(struct device *dev, struct clk **clks); int add_die_suffix(struct platform_device *pdev); void zhihe_register_clock(struct platform_device *pdev); void zhihe_unregister_clocks(struct clk *clks[], unsigned int count); int zhihe_parse_regbase(struct platform_device *pdev); #endif