/* * Copyright (C) 2013 Allwinnertech, kevin.z.m * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Adjustable factor-based clock implementation */ #ifndef __MACH_SUNXI_CLK_FACTORS_H #define __MACH_SUNXI_CLK_FACTORS_H #include #include #include #include "clk-sunxi.h" typedef enum pll_lock_mode { PLL_LOCK_NEW_MODE = 0x0, PLL_LOCK_OLD_MODE, PLL_LOCK_NONE_MODE, PLL_LOCK_MODE_MAX, } pll_lock_mode_e; /** * struct clk_factors_value - factor value * * @factorn: factor-n value * @factork: factor-k value * @factorm: factor-m value * @factorp: factor-p value * @factord1: factor-d1 value * @factord2: factor-d2 value * @frac_mode: fraction mode value * @frac_freq: fraction frequnecy value */ struct clk_factors_value { u16 factorn; u16 factork; u16 factorm; u16 factorp; u16 factord1; u16 factord2; u16 frac_mode; u16 frac_freq; }; /** * struct sunxi_clk_factors_config - factor config * * @nshift: shift to factor-n bit field * @nwidth: width of factor-n bit field * @kshift: shift to factor-k bit field * @kwidth: width of factor-k bit field * @mshift: shift to factor-m bit field * @mwidth: width of factor-m bit field * @pshift: shift to factor-p bit field * @pwidth: width of factor-p bit field * @d1shift: shift to factor-d1 bit field * @d1width: width of factor-d1 bit field * @d2shift: shift to factor-d2 bit field * @d2width: width of factor-d2 bit field * @frac: flag of fraction * @outshift: shift to frequency select bit field * @modeshift: shift to fraction/integer mode select * @enshift: shift to factor enable bit field * @lockshift: shift to factor lock status bit filed * @sdmshift: shift to factor sdm enable bit filed * @sdmwidth shift to factor sdm width bit filed * @sdmpat sdmpat reg address offset * @sdmval sdm default value * @updshift shift to update bit (especial for ddr/ddr0/ddr1) * @delay for flat factors delay. * @mux_inshift shift to multiplexer(multiple 24M source clocks) bit field * @out_enshift shift to enable pll clock output bit field */ struct sunxi_clk_factors_config { u8 nshift; u8 nwidth; u8 kshift; u8 kwidth; u8 mshift; u8 mwidth; u8 pshift; u8 pwidth; u8 d1shift; u8 d1width; u8 d2shift; u8 d2width; u8 frac; u8 outshift; u8 modeshift; u8 enshift; u8 lockshift; u8 sdmshift; u8 sdmwidth; unsigned long sdmpat; u32 sdmval; u32 updshift; u32 delay; u32 mux_inshift; u32 out_enshift; }; struct sunxi_clk_factor_freq { u32 factor; u32 freq; }; /** * struct factor_init_data - factor init data * * @name: name of the clock * @parent_name:name of the parent * @num_parents:counter of the parents * @flags: factor optimal configurations * @reg: register address for the factor * @lock_reg: register address for check if the pll has locked * @lock_bit: bit offset of the lock_reg, to check if the the pll has locked * @pll_lock_ctrl_reg: pll lock control register, this function is first used on * the sun50i, to enable the function of pll hardlock * @lock_en_bit:bit offset of the pll_lock_ctrl_reg, to enable the function * @config: configuration of the factor * @get_factors:function for get factors parameter under a given frequency * @calc_rate: function for calculate the factor frequency * @priv_ops: private operations hook for the special factor * @priv_regops:register operation hook for read/write the register * */ struct factor_init_data { const char *name; const char **parent_names; int num_parents; unsigned long flags; u64 reg; u64 lock_reg; unsigned char lock_bit; u64 pll_lock_ctrl_reg; unsigned char lock_en_bit; pll_lock_mode_e lock_mode; struct sunxi_clk_factors_config *config; int (*get_factors)(u32 rate, u32 parent_rate, struct clk_factors_value *factor); unsigned long (*calc_rate)(u32 parent_rate, struct clk_factors_value *factor); struct clk_ops *priv_ops; struct sunxi_reg_ops *priv_regops; }; /** * struct sunxi_clk_factors - factor clock * * @hw: handle between common and hardware-specific interfaces * @dev: device handle who register this clock * @flags: factor optimal configurations * @reg: register address for the factor * @lock_reg: register address for check if the pll has locked * @lock_bit: bit offset of the lock_reg, to check if the the pll has locked * @pll_lock_ctrl_reg: pll lock control register, this function is first used on * the sun50i, to enable the function of pll hardlock * @lock_en_bit:bit offset of the pll_lock_ctrl_reg, to enable the function * @get_factor: function for get factors parameter under a given frequency * @calc_rate: function for calculate the factor frequency * @lock: lock for protecting the factors operations * @priv_ops: private operations hook for the special factor * */ struct sunxi_clk_factors { struct clk_hw hw; struct device *dev; unsigned long flags; void __iomem *reg; void __iomem *lock_reg; unsigned char lock_bit; void __iomem *pll_lock_ctrl_reg; unsigned char lock_en_bit; pll_lock_mode_e lock_mode; struct sunxi_clk_factors_config *config; int (*get_factors)(u32 rate, u32 parent_rate, struct clk_factors_value *factor); unsigned long (*calc_rate)(u32 parent_rate, struct clk_factors_value *factor); spinlock_t *lock; struct sunxi_reg_ops *priv_regops; }; struct sunxi_clk_pat_item { char *name; char *patname; }; static inline u32 factor_readl(struct sunxi_clk_factors *factor, void __iomem *reg) { return ((unsigned long *)factor->priv_regops) ? factor->priv_regops->reg_readl(reg) : readl(reg); } static inline void factor_writel(struct sunxi_clk_factors *factor, unsigned int val, void __iomem *reg) { (((unsigned long *)factor->priv_regops) ? factor->priv_regops->reg_writel(val, reg) : writel(val, reg)); } void sunxi_clk_get_factors_ops(struct clk_ops *ops); struct clk *sunxi_clk_register_factors(struct device *dev, void __iomem *base, spinlock_t *lock, struct factor_init_data *init_data); #define SUNXI_CLK_FACTORS(name, _nshift, _nwidth, _kshift, _kwidth, \ _mshift, _mwidth, _pshift, _pwidth, _d1shift, _d1width, \ _d2shift, _d2width, _frac, _outshift, _modeshift, \ _enshift, _sdmshift, _sdmwidth, _sdmpat, _sdmval) \ static struct sunxi_clk_factors_config sunxi_clk_factor_##name = { \ .nshift = _nshift, \ .nwidth = _nwidth, \ .kshift = _kshift, \ .kwidth = _kwidth, \ .mshift = _mshift, \ .mwidth = _mwidth, \ .pshift = _pshift, \ .pwidth = _pwidth, \ .d1shift = _d1shift, \ .d1width = _d1width, \ .d2shift = _d2shift, \ .d2width = _d2width, \ .frac = _frac, \ .outshift = _outshift, \ .modeshift = _modeshift,\ .enshift = _enshift, \ .sdmshift = _sdmshift, \ .sdmwidth = _sdmwidth, \ .sdmpat = _sdmpat, \ .sdmval = _sdmval, \ .updshift = 0, \ .mux_inshift = 0, \ .out_enshift = 0, \ } #define FACTOR_ALL(nv, ns, nw, kv, ks, kw, mv, ms, mw, \ pv, ps, pw, d1v, d1s, d1w, d2v, d2s, d2w) \ ((((nv & ((1 << nw) - 1)) << ns) | \ ((kv & ((1 << kw) - 1)) << ks) | \ ((mv & ((1 << mw) - 1)) << ms) | \ ((pv & ((1 << pw) - 1)) << ps) | \ ((d1v & ((1 << d1w) - 1)) << d1s) | \ ((d2v & ((1 << d2w) - 1)) << d2s))) #define SUNXI_CLK_FACTORS_UPDATE(name, _nshift, _nwidth, _kshift, _kwidth, \ _mshift, _mwidth, _pshift, _pwidth, _d1shift, _d1width, \ _d2shift, _d2width, _frac, _outshift, _modeshift, \ _enshift, _sdmshift, _sdmwidth, _sdmpat, _sdmval, _updshift) \ static struct sunxi_clk_factors_config sunxi_clk_factor_##name = { \ .nshift = _nshift, \ .nwidth = _nwidth, \ .kshift = _kshift, \ .kwidth = _kwidth, \ .mshift = _mshift, \ .mwidth = _mwidth, \ .pshift = _pshift, \ .pwidth = _pwidth, \ .d1shift = _d1shift, \ .d1width = _d1width, \ .d2shift = _d2shift, \ .d2width = _d2width, \ .frac = _frac, \ .outshift = _outshift, \ .modeshift = _modeshift,\ .enshift = _enshift, \ .sdmshift = _sdmshift, \ .sdmwidth = _sdmwidth, \ .sdmpat = _sdmpat, \ .sdmval = _sdmval, \ .updshift = _updshift, \ .mux_inshift = 0, \ .out_enshift = 0, \ } #define SUNXI_CLK_FACTORS_DELAY(name, _nshift, _nwidth, _kshift, _kwidth, \ _mshift, _mwidth, _pshift, _pwidth, _d1shift, _d1width, \ _d2shift, _d2width, _frac, _outshift, _modeshift, \ _enshift, _sdmshift, _sdmwidth, _sdmpat, _sdmval, _delay) \ static struct sunxi_clk_factors_config sunxi_clk_factor_##name = { \ .nshift = _nshift, \ .nwidth = _nwidth, \ .kshift = _kshift, \ .kwidth = _kwidth, \ .mshift = _mshift, \ .mwidth = _mwidth, \ .pshift = _pshift, \ .pwidth = _pwidth, \ .d1shift = _d1shift, \ .d1width = _d1width, \ .d2shift = _d2shift, \ .d2width = _d2width, \ .frac = _frac, \ .outshift = _outshift, \ .modeshift = _modeshift,\ .enshift = _enshift, \ .sdmshift = _sdmshift, \ .sdmwidth = _sdmwidth, \ .sdmpat = _sdmpat, \ .sdmval = _sdmval, \ .delay = _delay \ } #define SUNXI_CLK_FACTORS1(name, _nshift, _nwidth, _kshift, _kwidth, \ _mshift, _mwidth, _pshift, _pwidth, _d1shift, _d1width, \ _d2shift, _d2width, _frac, _outshift, _modeshift, \ _enshift, _sdmshift, _sdmwidth, _sdmpat, _sdmval, \ _mux_inshift, _out_enshift) \ static struct sunxi_clk_factors_config sunxi_clk_factor_##name = { \ .nshift = _nshift, \ .nwidth = _nwidth, \ .kshift = _kshift, \ .kwidth = _kwidth, \ .mshift = _mshift, \ .mwidth = _mwidth, \ .pshift = _pshift, \ .pwidth = _pwidth, \ .d1shift = _d1shift, \ .d1width = _d1width, \ .d2shift = _d2shift, \ .d2width = _d2width, \ .frac = _frac, \ .outshift = _outshift, \ .modeshift = _modeshift, \ .enshift = _enshift, \ .sdmshift = _sdmshift, \ .sdmwidth = _sdmwidth, \ .sdmpat = _sdmpat, \ .sdmval = _sdmval, \ .updshift = 0, \ .mux_inshift = _mux_inshift, \ .out_enshift = _out_enshift, \ } #define FACTOR_SIZEOF(name) (sizeof(factor_pll##name##_tbl)/ \ sizeof(struct sunxi_clk_factor_freq)) #define FACTOR_SEARCH(name) (sunxi_clk_com_ftr_sr( \ &sunxi_clk_factor_pll_##name, factor, \ factor_pll##name##_tbl, index, \ FACTOR_SIZEOF(name))) #define F_N8X7_M0X4(nv, mv) \ FACTOR_ALL(nv, 8, 7, 0, 0, 0, mv, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0) #define F_N8X5_K4X2(nv, kv) \ FACTOR_ALL(nv, 8, 5, kv, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) #define F_N8X7_M0X2(nv, mv) \ FACTOR_ALL(nv, 8, 7, 0, 0, 0, mv, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0) #define F_N8X5_K4X2_M0X2(nv, kv, mv) \ FACTOR_ALL(nv, 8, 5, kv, 4, 2, mv, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0) #define F_N8X5_K4X2_M0X2_P16x2(nv, kv, mv, pv) \ FACTOR_ALL(nv, 8, 5, kv, 4, 2, mv, 0, 2, pv, 16, 2, 0, 0, 0, 0, 0, 0) int sunxi_clk_get_common_factors(struct sunxi_clk_factors_config *f_config, struct clk_factors_value *factor, struct sunxi_clk_factor_freq table[], unsigned long index, unsigned long tbl_size); int sunxi_clk_com_ftr_sr(struct sunxi_clk_factors_config *f_config, struct clk_factors_value *factor, struct sunxi_clk_factor_freq table[], unsigned long index, unsigned long tbl_count); void sunxi_clk_set_factor_lock_mode(struct factor_init_data *factor, const char *lock_mode); #endif