diff options
Diffstat (limited to '')
-rw-r--r-- | drivers/clk/x86/clk-cgu.h | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/drivers/clk/x86/clk-cgu.h b/drivers/clk/x86/clk-cgu.h new file mode 100644 index 000000000..bcaf8aec9 --- /dev/null +++ b/drivers/clk/x86/clk-cgu.h @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 MaxLinear, Inc. + * Copyright (C) 2020 Intel Corporation. + * Zhu Yixin <yzhu@maxlinear.com> + * Rahul Tanwar <rtanwar@maxlinear.com> + */ + +#ifndef __CLK_CGU_H +#define __CLK_CGU_H + +#include <linux/regmap.h> + +struct lgm_clk_mux { + struct clk_hw hw; + struct regmap *membase; + unsigned int reg; + u8 shift; + u8 width; + unsigned long flags; +}; + +struct lgm_clk_divider { + struct clk_hw hw; + struct regmap *membase; + unsigned int reg; + u8 shift; + u8 width; + u8 shift_gate; + u8 width_gate; + unsigned long flags; + const struct clk_div_table *table; +}; + +struct lgm_clk_ddiv { + struct clk_hw hw; + struct regmap *membase; + unsigned int reg; + u8 shift0; + u8 width0; + u8 shift1; + u8 width1; + u8 shift2; + u8 width2; + u8 shift_gate; + u8 width_gate; + unsigned int mult; + unsigned int div; + unsigned long flags; +}; + +struct lgm_clk_gate { + struct clk_hw hw; + struct regmap *membase; + unsigned int reg; + u8 shift; + unsigned long flags; +}; + +enum lgm_clk_type { + CLK_TYPE_FIXED, + CLK_TYPE_MUX, + CLK_TYPE_DIVIDER, + CLK_TYPE_FIXED_FACTOR, + CLK_TYPE_GATE, + CLK_TYPE_NONE, +}; + +/** + * struct lgm_clk_provider + * @membase: IO mem base address for CGU. + * @np: device node + * @dev: device + * @clk_data: array of hw clocks and clk number. + */ +struct lgm_clk_provider { + struct regmap *membase; + struct device_node *np; + struct device *dev; + struct clk_hw_onecell_data clk_data; +}; + +enum pll_type { + TYPE_ROPLL, + TYPE_LJPLL, + TYPE_NONE, +}; + +struct lgm_clk_pll { + struct clk_hw hw; + struct regmap *membase; + unsigned int reg; + unsigned long flags; + enum pll_type type; +}; + +/** + * struct lgm_pll_clk_data + * @id: platform specific id of the clock. + * @name: name of this pll clock. + * @parent_data: parent clock data. + * @num_parents: number of parents. + * @flags: optional flags for basic clock. + * @type: platform type of pll. + * @reg: offset of the register. + */ +struct lgm_pll_clk_data { + unsigned int id; + const char *name; + const struct clk_parent_data *parent_data; + u8 num_parents; + unsigned long flags; + enum pll_type type; + int reg; +}; + +#define LGM_PLL(_id, _name, _pdata, _flags, \ + _reg, _type) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .num_parents = ARRAY_SIZE(_pdata), \ + .flags = _flags, \ + .reg = _reg, \ + .type = _type, \ + } + +struct lgm_clk_ddiv_data { + unsigned int id; + const char *name; + const struct clk_parent_data *parent_data; + u8 flags; + unsigned long div_flags; + unsigned int reg; + u8 shift0; + u8 width0; + u8 shift1; + u8 width1; + u8 shift_gate; + u8 width_gate; + u8 ex_shift; + u8 ex_width; +}; + +#define LGM_DDIV(_id, _name, _pname, _flags, _reg, \ + _shft0, _wdth0, _shft1, _wdth1, \ + _shft_gate, _wdth_gate, _xshft, _df) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = &(const struct clk_parent_data){ \ + .fw_name = _pname, \ + .name = _pname, \ + }, \ + .flags = _flags, \ + .reg = _reg, \ + .shift0 = _shft0, \ + .width0 = _wdth0, \ + .shift1 = _shft1, \ + .width1 = _wdth1, \ + .shift_gate = _shft_gate, \ + .width_gate = _wdth_gate, \ + .ex_shift = _xshft, \ + .ex_width = 1, \ + .div_flags = _df, \ + } + +struct lgm_clk_branch { + unsigned int id; + enum lgm_clk_type type; + const char *name; + const struct clk_parent_data *parent_data; + u8 num_parents; + unsigned long flags; + unsigned int mux_off; + u8 mux_shift; + u8 mux_width; + unsigned long mux_flags; + unsigned int mux_val; + unsigned int div_off; + u8 div_shift; + u8 div_width; + u8 div_shift_gate; + u8 div_width_gate; + unsigned long div_flags; + unsigned int div_val; + const struct clk_div_table *div_table; + unsigned int gate_off; + u8 gate_shift; + unsigned long gate_flags; + unsigned int gate_val; + unsigned int mult; + unsigned int div; +}; + +/* clock flags definition */ +#define CLOCK_FLAG_VAL_INIT BIT(16) +#define MUX_CLK_SW BIT(17) +#define GATE_CLK_HW BIT(18) +#define DIV_CLK_NO_MASK BIT(19) + +#define LGM_MUX(_id, _name, _pdata, _f, _reg, \ + _shift, _width, _cf, _v) \ + { \ + .id = _id, \ + .type = CLK_TYPE_MUX, \ + .name = _name, \ + .parent_data = _pdata, \ + .num_parents = ARRAY_SIZE(_pdata), \ + .flags = _f, \ + .mux_off = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .mux_flags = _cf, \ + .mux_val = _v, \ + } + +#define LGM_DIV(_id, _name, _pname, _f, _reg, _shift, _width, \ + _shift_gate, _width_gate, _cf, _v, _dtable) \ + { \ + .id = _id, \ + .type = CLK_TYPE_DIVIDER, \ + .name = _name, \ + .parent_data = &(const struct clk_parent_data){ \ + .fw_name = _pname, \ + .name = _pname, \ + }, \ + .num_parents = 1, \ + .flags = _f, \ + .div_off = _reg, \ + .div_shift = _shift, \ + .div_width = _width, \ + .div_shift_gate = _shift_gate, \ + .div_width_gate = _width_gate, \ + .div_flags = _cf, \ + .div_val = _v, \ + .div_table = _dtable, \ + } + +#define LGM_GATE(_id, _name, _pname, _f, _reg, \ + _shift, _cf, _v) \ + { \ + .id = _id, \ + .type = CLK_TYPE_GATE, \ + .name = _name, \ + .parent_data = &(const struct clk_parent_data){ \ + .fw_name = _pname, \ + .name = _pname, \ + }, \ + .num_parents = !_pname ? 0 : 1, \ + .flags = _f, \ + .gate_off = _reg, \ + .gate_shift = _shift, \ + .gate_flags = _cf, \ + .gate_val = _v, \ + } + +#define LGM_FIXED(_id, _name, _pname, _f, _reg, \ + _shift, _width, _cf, _freq, _v) \ + { \ + .id = _id, \ + .type = CLK_TYPE_FIXED, \ + .name = _name, \ + .parent_data = &(const struct clk_parent_data){ \ + .fw_name = _pname, \ + .name = _pname, \ + }, \ + .num_parents = !_pname ? 0 : 1, \ + .flags = _f, \ + .div_off = _reg, \ + .div_shift = _shift, \ + .div_width = _width, \ + .div_flags = _cf, \ + .div_val = _v, \ + .mux_flags = _freq, \ + } + +#define LGM_FIXED_FACTOR(_id, _name, _pname, _f, _reg, \ + _shift, _width, _cf, _v, _m, _d) \ + { \ + .id = _id, \ + .type = CLK_TYPE_FIXED_FACTOR, \ + .name = _name, \ + .parent_data = &(const struct clk_parent_data){ \ + .fw_name = _pname, \ + .name = _pname, \ + }, \ + .num_parents = 1, \ + .flags = _f, \ + .div_off = _reg, \ + .div_shift = _shift, \ + .div_width = _width, \ + .div_flags = _cf, \ + .div_val = _v, \ + .mult = _m, \ + .div = _d, \ + } + +static inline void lgm_set_clk_val(struct regmap *membase, u32 reg, + u8 shift, u8 width, u32 set_val) +{ + u32 mask = (GENMASK(width - 1, 0) << shift); + + regmap_update_bits(membase, reg, mask, set_val << shift); +} + +static inline u32 lgm_get_clk_val(struct regmap *membase, u32 reg, + u8 shift, u8 width) +{ + u32 mask = (GENMASK(width - 1, 0) << shift); + u32 val; + + if (regmap_read(membase, reg, &val)) { + WARN_ONCE(1, "Failed to read clk reg: 0x%x\n", reg); + return 0; + } + + val = (val & mask) >> shift; + + return val; +} + + + +int lgm_clk_register_branches(struct lgm_clk_provider *ctx, + const struct lgm_clk_branch *list, + unsigned int nr_clk); +int lgm_clk_register_plls(struct lgm_clk_provider *ctx, + const struct lgm_pll_clk_data *list, + unsigned int nr_clk); +int lgm_clk_register_ddiv(struct lgm_clk_provider *ctx, + const struct lgm_clk_ddiv_data *list, + unsigned int nr_clk); +#endif /* __CLK_CGU_H */ |