blob: a725c1d45fb0565d3533001fc65422669e3007d4 [file] [log] [blame]
/* 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