| /* SPDX-License-Identifier: GPL-2.0 */ |
| #ifndef __MACH_MMP_CLK_H |
| #define __MACH_MMP_CLK_H |
| |
| #include <linux/clk-provider.h> |
| #include <linux/clkdev.h> |
| #include <soc/asr/asrdcstat.h> |
| #include <linux/pm_qos.h> |
| #include <linux/clk/dvfs-dvc.h> |
| |
| #define APBC_NO_BUS_CTRL BIT(0) |
| #define APBC_POWER_CTRL BIT(1) |
| #define APBC_FN_CTRL BIT(2) |
| #define APBC_APB_CTRL BIT(3) |
| #define APBC_RST_CTRL BIT(4) |
| |
| /* for APB pwm clock */ |
| #define APBC_PWM BIT(5) |
| #define APBC_PWM_APB_CTRL BIT(6) |
| |
| /* for two bit control like TSEN in 1802s */ |
| #define APBC_TWO_CTRL BIT(7) |
| /* for APB clock whose parent is mux */ |
| #define APBC_MUX BIT(8) |
| |
| #define MAX_OP_NUM (10) |
| #define CP_REAL_DCLK_SHIFT (1) |
| #define CP_REAL_DCLK_MASK (0x3FF << CP_REAL_DCLK_SHIFT) |
| |
| /* supported DDR chip type */ |
| enum ddr_type { |
| DDR_400M = 0, |
| DDR_533M, |
| DDR_416M, |
| DDR_667M, |
| DDR_800M, |
| DDR_TYPE_MAX, |
| }; |
| |
| #define KHZ (1000UL) |
| #define MHZ (1000000UL) |
| |
| #define MHZ_TO_HZ (1000000) |
| #define MHZ_TO_KHZ (1000) |
| |
| #define PLL_MASK(n) ((1 << (n)) - 1) |
| #define MASK(n) ((1 << (n)) - 1) |
| |
| #define MMP_AXI_SEPERATED_SRC_SEL BIT(0) |
| |
| /* TODO clk-provider.h */ |
| #define CLK_SET_RATE_ENABLED BIT(15) /* enable clk before changing clk rate */ |
| |
| extern enum ddr_type ddr_mode; |
| extern unsigned int uisvtdro; |
| extern unsigned long (*freqs_cmb)[MAX_PMIC_LEVEL]; |
| |
| |
| struct mmp_clk_factor_masks { |
| unsigned int factor; |
| unsigned int num_mask; |
| unsigned int den_mask; |
| unsigned int num_shift; |
| unsigned int den_shift; |
| }; |
| |
| struct mmp_clk_factor_tbl { |
| unsigned int num; |
| unsigned int den; |
| }; |
| |
| struct mmp_clk_factor { |
| struct clk_hw hw; |
| void __iomem *base; |
| struct mmp_clk_factor_masks *masks; |
| struct mmp_clk_factor_tbl *ftbl; |
| unsigned int ftbl_cnt; |
| spinlock_t *lock; |
| }; |
| |
| extern struct clk *mmp_clk_register_factor(const char *name, |
| const char *parent_name, unsigned long flags, |
| void __iomem *base, struct mmp_clk_factor_masks *masks, |
| struct mmp_clk_factor_tbl *ftbl, unsigned int ftbl_cnt, |
| spinlock_t *lock); |
| |
| /* Clock type "mix" */ |
| #define MMP_CLK_BITS_MASK(width, shift) \ |
| (((1 << (width)) - 1) << (shift)) |
| #define MMP_CLK_BITS_GET_VAL(data, width, shift) \ |
| ((data & MMP_CLK_BITS_MASK(width, shift)) >> (shift)) |
| #define MMP_CLK_BITS_SET_VAL(val, width, shift) \ |
| (((val) << (shift)) & MMP_CLK_BITS_MASK(width, shift)) |
| |
| enum { |
| MMP_CLK_MIX_TYPE_V1, |
| MMP_CLK_MIX_TYPE_V2, |
| MMP_CLK_MIX_TYPE_V3, |
| }; |
| |
| /* The register layout */ |
| struct mmp_clk_mix_reg_info { |
| void __iomem *reg_clk_ctrl; |
| void __iomem *reg_clk_sel; |
| u8 width_div; |
| u8 shift_div; |
| u8 width_mux; |
| u8 shift_mux; |
| u32 bit_fc; |
| }; |
| |
| /* The suggested clock table from user. */ |
| struct mmp_clk_mix_clk_table { |
| unsigned long rate; |
| u8 parent_index; |
| unsigned int divisor; |
| unsigned int valid; |
| }; |
| |
| struct mmp_clk_mix_config { |
| struct mmp_clk_mix_reg_info reg_info; |
| struct mmp_clk_mix_clk_table *table; |
| unsigned int table_size; |
| u32 *mux_table; |
| struct clk_div_table *div_table; |
| u8 div_flags; |
| u8 mux_flags; |
| }; |
| |
| struct mmp_clk_mix { |
| struct clk_hw hw; |
| struct mmp_clk_mix_reg_info reg_info; |
| struct mmp_clk_mix_clk_table *table; |
| u32 *mux_table; |
| struct clk_div_table *div_table; |
| unsigned int table_size; |
| u8 div_flags; |
| u8 mux_flags; |
| unsigned int type; |
| spinlock_t *lock; |
| }; |
| |
| extern const struct clk_ops mmp_clk_mix_ops; |
| extern struct clk *mmp_clk_register_mix(struct device *dev, |
| const char *name, |
| const char **parent_names, |
| u8 num_parents, |
| unsigned long flags, |
| struct mmp_clk_mix_config *config, |
| spinlock_t *lock); |
| |
| |
| /* Clock type "gate". MMP private gate */ |
| #define MMP_CLK_GATE_NEED_DELAY BIT(0) |
| |
| struct mmp_clk_gate { |
| struct clk_hw hw; |
| void __iomem *reg; |
| u32 mask; |
| u32 val_enable; |
| u32 val_disable; |
| unsigned int flags; |
| spinlock_t *lock; |
| /* FIXME: for PWM clock, two PWMs share one APB clk */ |
| struct clk *clk_share; |
| }; |
| |
| extern const struct clk_ops mmp_clk_gate_ops; |
| extern struct clk *mmp_clk_register_gate(struct device *dev, const char *name, |
| const char *parent_name, unsigned long flags, |
| void __iomem *reg, u32 mask, u32 val_enable, |
| u32 val_disable, unsigned int gate_flags, |
| spinlock_t *lock); |
| |
| |
| extern struct clk *mmp_clk_register_pll2(const char *name, |
| const char *parent_name, unsigned long flags); |
| extern struct clk *mmp_clk_register_apbc(const char *name, |
| const char *parent_name, void __iomem *base, |
| unsigned int delay, unsigned int apbc_flags, spinlock_t *lock); |
| extern struct clk *mmp_clk_register_apmu(const char *name, |
| const char *parent_name, void __iomem *base, u32 enable_mask, |
| spinlock_t *lock); |
| extern struct clk *mmp_clk_register_dvfs_dummy(const char *name, |
| const char *parent_name, unsigned long flags, |
| unsigned long init_rate); |
| |
| struct mmp_clk_unit { |
| unsigned int nr_clks; |
| struct clk **clk_table; |
| struct clk_onecell_data clk_data; |
| }; |
| |
| struct mmp_param_fixed_rate_clk { |
| unsigned int id; |
| char *name; |
| const char *parent_name; |
| unsigned long flags; |
| unsigned long fixed_rate; |
| }; |
| void mmp_register_fixed_rate_clks(struct mmp_clk_unit *unit, |
| struct mmp_param_fixed_rate_clk *clks, |
| int size); |
| |
| struct mmp_param_fixed_factor_clk { |
| unsigned int id; |
| char *name; |
| const char *parent_name; |
| unsigned long mult; |
| unsigned long div; |
| unsigned long flags; |
| }; |
| void mmp_register_fixed_factor_clks(struct mmp_clk_unit *unit, |
| struct mmp_param_fixed_factor_clk *clks, |
| int size); |
| |
| struct mmp_param_general_gate_clk { |
| unsigned int id; |
| const char *name; |
| const char *parent_name; |
| unsigned long flags; |
| unsigned long offset; |
| u8 bit_idx; |
| u8 gate_flags; |
| spinlock_t *lock; |
| }; |
| void mmp_register_general_gate_clks(struct mmp_clk_unit *unit, |
| struct mmp_param_general_gate_clk *clks, |
| void __iomem *base, int size); |
| |
| struct mmp_param_gate_clk { |
| unsigned int id; |
| char *name; |
| const char *parent_name; |
| unsigned long flags; |
| unsigned long offset; |
| u32 mask; |
| u32 val_enable; |
| u32 val_disable; |
| unsigned int gate_flags; |
| spinlock_t *lock; |
| }; |
| void mmp_register_gate_clks(struct mmp_clk_unit *unit, |
| struct mmp_param_gate_clk *clks, |
| void __iomem *base, int size); |
| |
| struct mmp_param_mux_clk { |
| unsigned int id; |
| char *name; |
| const char **parent_name; |
| u8 num_parents; |
| unsigned long flags; |
| unsigned long offset; |
| u8 shift; |
| u8 width; |
| u8 mux_flags; |
| spinlock_t *lock; |
| }; |
| void mmp_register_mux_clks(struct mmp_clk_unit *unit, |
| struct mmp_param_mux_clk *clks, |
| void __iomem *base, int size); |
| |
| struct mmp_param_div_clk { |
| unsigned int id; |
| char *name; |
| const char *parent_name; |
| unsigned long flags; |
| unsigned long offset; |
| u8 shift; |
| u8 width; |
| u8 div_flags; |
| spinlock_t *lock; |
| }; |
| void mmp_register_div_clks(struct mmp_clk_unit *unit, |
| struct mmp_param_div_clk *clks, |
| void __iomem *base, int size); |
| |
| #define DEFINE_MIX_REG_INFO(w_d, s_d, w_m, s_m, fc) \ |
| { \ |
| .width_div = (w_d), \ |
| .shift_div = (s_d), \ |
| .width_mux = (w_m), \ |
| .shift_mux = (s_m), \ |
| .bit_fc = (fc), \ |
| } |
| |
| struct mmp_clk_list { |
| const char *dev_id; |
| const char *con_id; |
| unsigned long initrate; |
| }; |
| |
| struct parents_table { |
| char *parent_name; |
| struct clk *parent; |
| u32 hw_sel_val; |
| }; |
| |
| struct ddr_opt { |
| unsigned int dclk; /* ddr clock */ |
| unsigned int ddr_tbl_index; /* ddr FC table index */ |
| unsigned int ddr_freq_level; /* ddr freq level(0~7) */ |
| unsigned int ddr_volt_level; /* ddr voltage level (0~7) */ |
| u32 ddr_clk_sel; /* ddr src sel val */ |
| unsigned int ddr_clk_src; /* ddr src rate */ |
| struct clk *ddr_parent; /* ddr clk parent node */ |
| unsigned int dclk_div; /* ddr clk divider */ |
| }; |
| |
| struct ddr_reg_offset { |
| /* ddr clk src sel set register */ |
| u32 fcdclk_off; |
| u32 tbl_ctrl_off; |
| u32 clk_sel_shift; |
| u32 clk_sel_width; |
| u32 tbl_enable_shift; |
| u32 tbl_index_shift; |
| u32 tbl_index_width; |
| }; |
| |
| struct dfc_level_reg_offset { |
| u32 dclksel_shift; |
| u32 dclksel_width; |
| u32 ddr_clk_div_shift; |
| u32 ddr_clk_div_width; |
| u32 mc_table_num_shift; |
| u32 mc_table_num_width; |
| u32 volt_level_shift; |
| u32 volt_level_width; |
| }; |
| |
| struct dfc_ap_reg_offset { |
| u32 dfc_req_shift; |
| u32 freq_level_shift; |
| u32 freq_level_width; |
| }; |
| |
| struct ddr_params { |
| void __iomem *apmu_base; |
| void __iomem *mpmu_base; |
| void __iomem *dmcu_base; |
| struct ddr_reg_offset *ddr_offset; |
| struct dfc_level_reg_offset *dfc_level_reg_offset; |
| struct dfc_ap_reg_offset *dfc_ap_reg_offset; |
| struct parents_table *parent_table; |
| int parent_table_size; |
| struct ddr_opt *ddr_opt; |
| int ddr_opt_size; |
| unsigned long *hwdfc_freq_table; |
| int hwdfc_table_size; |
| /* dynamic dc stat support? */ |
| bool dcstat_support; |
| }; |
| |
| struct clk_ddr { |
| struct clk_hw hw; |
| struct ddr_params *params; |
| u32 flags; |
| spinlock_t *lock; |
| }; |
| |
| struct ddr_combclk_relation { |
| unsigned long dclk_rate; |
| unsigned long combclk_rate; |
| }; |
| |
| struct ddr_combined_clk { |
| struct clk *clk; |
| unsigned long maxrate; |
| struct list_head node; |
| /* Describe the relationship with Dclk */ |
| struct ddr_combclk_relation *relationtbl; |
| unsigned int num_relationtbl; |
| }; |
| |
| /* FLAGS */ |
| /* indicate if hwdfc or sw lagency dfc is used */ |
| #define MMP_DDR_HWDFC_FEAT BIT(0) |
| #define MMP_DDR_FC_HARDWARE_ENABLE BIT(1) |
| #define MMP_DDR_HAS_HW_VBLANK_DFC BIT(2) |
| /* used for hwdfc WR, add more flag in case need to separate |
| diff issue on diff plat */ |
| #define MMP_DDR_HWDFC_WR BIT(3) |
| #define MMP_DDR_PLLSEL_3BIT BIT(4) |
| #define MMP_DDR_DLL_BYPASS BIT(5) |
| /* only show ddr rate for AP part */ |
| #define MMP_DDR_RATE_AP_ONLY BIT(6) |
| #define MMP_DDR_FC_BY_CP BIT(7) |
| #define MMP_DDR_RIPC_LOCK_FC BIT(8) |
| |
| /* RTC/WTC table used for solution change rtc/wtc on the fly */ |
| struct cpu_rtcwtc { |
| unsigned int max_pclk; /* max rate could be used by this rtc/wtc */ |
| unsigned int l1_rtc; |
| unsigned int l2_rtc; |
| unsigned int svtdro; |
| }; |
| |
| struct cpu_opt { |
| unsigned int pclk; /* core clock */ |
| unsigned int l2clk; /* L2 cache interface clock */ |
| unsigned int pdclk; /* DDR interface clock */ |
| unsigned int baclk; /* bus interface clock */ |
| unsigned int periphclk; /* PERIPHCLK */ |
| unsigned int ap_clk_sel; /* core src sel val */ |
| struct clk *parent; /* core clk parent node */ |
| unsigned int ap_clk_src; /* core src rate */ |
| unsigned int pclk_div; /* core clk divider*/ |
| unsigned int l2clk_div; /* L2 clock divider */ |
| unsigned int pdclk_div; /* DDR interface clock divider */ |
| unsigned int baclk_div; /* bus interface clock divider */ |
| unsigned int periphclk_div; /* PERIPHCLK divider */ |
| unsigned int l1_rtc; /* L1 cache RTC/WTC */ |
| unsigned int l2_rtc; /* L2 cache RTC/WTC */ |
| struct list_head node; |
| }; |
| |
| struct core_reg_offset { |
| /* core clk src sel set register */ |
| u32 fcap_off; |
| u32 clk_sel_shift; |
| u32 clk_sel_width; |
| void __iomem *pll1_pll3_swreg; |
| u32 pll1_pll3_swbit; |
| }; |
| |
| struct core_params { |
| void __iomem *apmu_base; |
| void __iomem *mpmu_base; |
| void __iomem *ciu_base; |
| struct core_reg_offset *core_offset; |
| struct parents_table *parent_table; |
| int parent_table_size; |
| struct cpu_opt *cpu_opt; |
| int cpu_opt_size; |
| #ifdef UPDATE_CPUDFC_XTC |
| struct cpu_rtcwtc *cpu_rtcwtc_table; |
| int cpu_rtcwtc_table_size; |
| #endif |
| unsigned int max_cpurate; |
| unsigned int bridge_cpurate; |
| unsigned int *pp_disable; |
| unsigned int pp_discnt; |
| /* dynamic dc stat support? */ |
| bool dcstat_support; |
| powermode pxa_powermode; |
| spinlock_t *shared_lock; |
| }; |
| |
| struct axi_opt { |
| unsigned int aclk; /* axi clock */ |
| u32 axi_clk_sel; /* axi src sel val */ |
| unsigned int axi_clk_src; /* axi src rate */ |
| struct clk *axi_parent; /* axi clk parent node */ |
| unsigned int aclk_div; /* axi clk divider */ |
| u32 combined_apb_clk; |
| u32 combined_apb_clk_sel; |
| }; |
| |
| struct axi_reg_offset { |
| /* axi clk src sel set register */ |
| u32 fcaclk_off; |
| u32 clk_sel0_shift; |
| u32 clk_sel0_width; |
| u32 clk_sel1_shift; |
| u32 clk_sel1_width; |
| }; |
| |
| struct axi_params { |
| void __iomem *apmu_base; |
| void __iomem *mpmu_base; |
| void __iomem *apbs_base; |
| struct axi_reg_offset *axi_offset; |
| struct parents_table *parent_table; |
| int parent_table_size; |
| struct axi_opt *axi_opt; |
| int axi_opt_size; |
| /* dynamic dc stat support? */ |
| bool dcstat_support; |
| }; |
| |
| struct clk_cpu { |
| struct clk_hw hw; |
| struct core_params *params; |
| u32 flags; |
| spinlock_t *lock; |
| }; |
| |
| struct clk_axi { |
| struct clk_hw hw; |
| struct axi_params *params; |
| u32 flags; |
| spinlock_t *lock; |
| }; |
| |
| #define to_clk_cpu(core) container_of(core, struct clk_cpu, hw) |
| #define to_clk_axi(axi) container_of(axi, struct clk_axi, hw) |
| #define to_clk_ddr(ddr) container_of(ddr, struct clk_ddr, hw) |
| |
| void mmp_clk_init(struct device_node *np, struct mmp_clk_unit *unit, |
| int nr_clks); |
| void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id, |
| struct clk *clk); |
| extern int get_voltage_table_for_cp(unsigned int *dvc_voltage_tbl, unsigned int *level_num); |
| extern int ddr_get_dvc_level(int rate); |
| extern int __init setup_asr18xx_dvfs_platinfo(void); |
| extern int __init setup_asr1901_dvfs_platinfo(void); |
| #endif |