From: dkl Date: Sun, 20 Apr 2014 08:55:24 +0000 (+0800) Subject: clk: rockchip: add clk_pll_ops_3188plus_auto X-Git-Tag: firefly_0821_release~5416 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=91019ca759b259519a96d3916a441bdf2004cf6f;p=firefly-linux-kernel-4.4.55.git clk: rockchip: add clk_pll_ops_3188plus_auto --- diff --git a/arch/arm/boot/dts/rk3288-clocks.dtsi b/arch/arm/boot/dts/rk3288-clocks.dtsi index 38fad817c1ca..92d69a422742 100755 --- a/arch/arm/boot/dts/rk3288-clocks.dtsi +++ b/arch/arm/boot/dts/rk3288-clocks.dtsi @@ -357,7 +357,7 @@ status-reg = <0x0284 7>; clocks = <&xin24m>; clock-output-names = "clk_cpll"; - rockchip,pll-type = ; + rockchip,pll-type = ; #clock-cells = <0>; #clock-init-cells = <1>; }; diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index cceafebaa888..e3cd58124954 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -697,6 +697,181 @@ static const struct clk_ops clk_pll_ops_3188plus = { .set_rate = clk_pll_set_rate_3188plus, }; +/* CLK_PLL_3188PLUS_AUTO type ops */ +#define PLL_FREF_MIN (269*KHZ) +#define PLL_FREF_MAX (2200*MHZ) + +#define PLL_FVCO_MIN (440*MHZ) +#define PLL_FVCO_MAX (2200*MHZ) + +#define PLL_FOUT_MIN (27500*KHZ) +#define PLL_FOUT_MAX (2200*MHZ) + +#define PLL_NF_MAX (4096) +#define PLL_NR_MAX (64) +#define PLL_NO_MAX (16) + +static u32 clk_gcd(u32 numerator, u32 denominator) +{ + u32 a, b; + + if (!numerator || !denominator) + return 0; + if (numerator > denominator) { + a = numerator; + b = denominator; + } else { + a = denominator; + b = numerator; + } + while (b != 0) { + int r = b; + b = a % b; + a = r; + } + + return a; +} + +/* FIXME: calc using u64 */ +static int pll_clk_get_best_set(unsigned long fin_hz, unsigned long fout_hz, + u32 *best_nr, u32 *best_nf, u32 *best_no) +{ + u32 nr, nf, no, nonr; + u32 nr_out, nf_out, no_out; + u32 n; + u32 YFfenzi; + u32 YFfenmu; + u64 fref, fvco, fout; + u32 gcd_val = 0; + + + nr_out = PLL_NR_MAX + 1; + no_out = 0; + +// printk("pll_clk_get_set fin=%lu,fout=%lu\n", fin_hz, fout_hz); + if(!fin_hz || !fout_hz || fout_hz == fin_hz) + return -EINVAL; + gcd_val = clk_gcd(fin_hz, fout_hz); + +// printk("gcd_val = %d\n",gcd_val); + + YFfenzi = fout_hz / gcd_val; + YFfenmu = fin_hz / gcd_val; + +// printk("YFfenzi = %d, YFfenmu = %d\n",YFfenzi,YFfenmu); + + for(n = 1;; n++) { + nf = YFfenzi * n; + nonr = YFfenmu * n; + if(nf > PLL_NF_MAX || nonr > (PLL_NO_MAX * PLL_NR_MAX)) + break; + for(no = 1; no <= PLL_NO_MAX; no++) { + if(!(no == 1 || !(no % 2))) + continue; + + if(nonr % no) + continue; + nr = nonr / no; + + if(nr > PLL_NR_MAX) //PLL_NR_MAX + continue; + + fref = fin_hz / nr; + if(fref < PLL_FREF_MIN || fref > PLL_FREF_MAX) + continue; + + fvco = fref * nf; + if(fvco < PLL_FVCO_MIN || fvco > PLL_FVCO_MAX) + continue; + fout = fvco / no; + if(fout < PLL_FOUT_MIN || fout > PLL_FOUT_MAX) + continue; + + /* output all available PLL settings */ + //printk("nr=%d,\tnf=%d,\tno=%d\n",nr,nf,no); + //printk("_PLL_SET_CLKS(%lu,\t%d,\t%d,\t%d),\n",fout_hz/KHZ,nr,nf,no); + + /* select the best from all available PLL settings */ + if((nr < nr_out) || ((nr == nr_out)&&(no > no_out))) + { + nr_out = nr; + nf_out = nf; + no_out = no; + } + } + + } + + /* output the best PLL setting */ + if((nr_out <= PLL_NR_MAX) && (no_out > 0)){ + //printk("_PLL_SET_CLKS(%lu,\t%d,\t%d,\t%d),\n",fout_hz/KHZ,nr_out,nf_out,no_out); + if(best_nr && best_nf && best_no){ + *best_nr = nr_out; + *best_nf = nf_out; + *best_no = no_out; + } + return 0; + } else { + return -EINVAL; + } +} + +static unsigned long clk_pll_recalc_rate_3188plus_auto(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_pll_recalc_rate_3188plus(hw, parent_rate); +} + +static long clk_pll_round_rate_3188plus_auto(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long best; + + for(best=rate; best>0; best--){ + if(!pll_clk_get_best_set(*prate, best, NULL, NULL, NULL)) + return best; + } + + return 0; +} + +static int clk_pll_set_rate_3188plus_auto(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + unsigned long best; + u32 nr,nf,no; + struct pll_clk_set clk_set; + int ret; + + + best = clk_pll_round_rate_3188plus_auto(hw, rate, &parent_rate); + + if(!best) + return -EINVAL; + + pll_clk_get_best_set(parent_rate, best, &nr, &nf, &no); + + /* prepare clk_set */ + clk_set.rate = best; + clk_set.pllcon0 = RK3188_PLL_CLKR_SET(nr)|RK3188_PLL_CLKOD_SET(no), \ + clk_set.pllcon1 = RK3188_PLL_CLKF_SET(nf),\ + clk_set.pllcon2 = RK3188_PLL_CLK_BWADJ_SET(nf >> 1),\ + clk_set.rst_dly = ((nr*500)/24+1),\ + + ret = _pll_clk_set_rate_3188plus(&clk_set, hw); + clk_debug("pll %s set rate=%lu OK!\n", __clk_get_name(hw->clk), best); + + return ret; +} + + +static const struct clk_ops clk_pll_ops_3188plus_auto = { + .recalc_rate = clk_pll_recalc_rate_3188plus_auto, + .round_rate = clk_pll_round_rate_3188plus_auto, + .set_rate = clk_pll_set_rate_3188plus_auto, +}; + /* CLK_PLL_3188PLUS_APLL type ops */ static unsigned long clk_pll_recalc_rate_3188plus_apll(struct clk_hw *hw, @@ -1105,6 +1280,9 @@ const struct clk_ops *rk_get_pll_ops(u32 pll_flags) case CLK_PLL_3288_APLL: return &clk_pll_ops_3288_apll; + case CLK_PLL_3188PLUS_AUTO: + return &clk_pll_ops_3188plus_auto; + default: clk_err("%s: unknown pll_flags!\n", __func__); return NULL; diff --git a/include/dt-bindings/clock/rockchip.h b/include/dt-bindings/clock/rockchip.h index c482a54b2e93..927a51d0a621 100644 --- a/include/dt-bindings/clock/rockchip.h +++ b/include/dt-bindings/clock/rockchip.h @@ -35,6 +35,8 @@ #define CLK_PLL_3188PLUS BIT(2) #define CLK_PLL_3188PLUS_APLL BIT(3) #define CLK_PLL_3288_APLL BIT(4) +#define CLK_PLL_3188PLUS_AUTO BIT(5) + /* rate_ops index */ #define CLKOPS_RATE_MUX_DIV 1