Blame view

arch/arm/mach-omap2/clkt2xxx_dpllcore.c 4.44 KB
b1823d861   Paul Walmsley   OMAP2xxx clock: m...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  /*
   * DPLL + CORE_CLK composite clock functions
   *
   * Copyright (C) 2005-2008 Texas Instruments, Inc.
   * Copyright (C) 2004-2010 Nokia Corporation
   *
   * Contacts:
   * Richard Woodruff <r-woodruff2@ti.com>
   * Paul Walmsley
   *
   * Based on earlier work by Tuukka Tikkanen, Tony Lindgren,
   * Gordon McNutt and RidgeRun, Inc.
   *
   * 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.
   *
   * XXX The DPLL and CORE clocks should be split into two separate clock
   * types.
   */
  #undef DEBUG
  
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/clk.h>
  #include <linux/io.h>
  
  #include <plat/clock.h>
  #include <plat/sram.h>
  #include <plat/sdrc.h>
  
  #include "clock.h"
  #include "clock2xxx.h"
  #include "opp2xxx.h"
59fb659b0   Paul Walmsley   OMAP2/3: PRCM: sp...
35
  #include "cm2xxx_3xxx.h"
b1823d861   Paul Walmsley   OMAP2xxx clock: m...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  #include "cm-regbits-24xx.h"
  
  /* #define DOWN_VARIABLE_DPLL 1 */		/* Experimental */
  
  /**
   * omap2xxx_clk_get_core_rate - return the CORE_CLK rate
   * @clk: pointer to the combined dpll_ck + core_ck (currently "dpll_ck")
   *
   * Returns the CORE_CLK rate.  CORE_CLK can have one of three rate
   * sources on OMAP2xxx: the DPLL CLKOUT rate, DPLL CLKOUTX2, or 32KHz
   * (the latter is unusual).  This currently should be called with
   * struct clk *dpll_ck, which is a composite clock of dpll_ck and
   * core_ck.
   */
  unsigned long omap2xxx_clk_get_core_rate(struct clk *clk)
  {
  	long long core_clk;
  	u32 v;
  
  	core_clk = omap2_get_dpll_rate(clk);
c4d7e58fb   Paul Walmsley   OMAP2/3: PRM/CM: ...
56
  	v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
b1823d861   Paul Walmsley   OMAP2xxx clock: m...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  	v &= OMAP24XX_CORE_CLK_SRC_MASK;
  
  	if (v == CORE_CLK_SRC_32K)
  		core_clk = 32768;
  	else
  		core_clk *= v;
  
  	return core_clk;
  }
  
  /*
   * Uses the current prcm set to tell if a rate is valid.
   * You can go slower, but not faster within a given rate set.
   */
  static long omap2_dpllcore_round_rate(unsigned long target_rate)
  {
  	u32 high, low, core_clk_src;
c4d7e58fb   Paul Walmsley   OMAP2/3: PRM/CM: ...
74
  	core_clk_src = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
b1823d861   Paul Walmsley   OMAP2xxx clock: m...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  	core_clk_src &= OMAP24XX_CORE_CLK_SRC_MASK;
  
  	if (core_clk_src == CORE_CLK_SRC_DPLL) {	/* DPLL clockout */
  		high = curr_prcm_set->dpll_speed * 2;
  		low = curr_prcm_set->dpll_speed;
  	} else {				/* DPLL clockout x 2 */
  		high = curr_prcm_set->dpll_speed;
  		low = curr_prcm_set->dpll_speed / 2;
  	}
  
  #ifdef DOWN_VARIABLE_DPLL
  	if (target_rate > high)
  		return high;
  	else
  		return target_rate;
  #else
  	if (target_rate > low)
  		return high;
  	else
  		return low;
  #endif
  
  }
  
  unsigned long omap2_dpllcore_recalc(struct clk *clk)
  {
  	return omap2xxx_clk_get_core_rate(clk);
  }
  
  int omap2_reprogram_dpllcore(struct clk *clk, unsigned long rate)
  {
  	u32 cur_rate, low, mult, div, valid_rate, done_rate;
  	u32 bypass = 0;
  	struct prcm_config tmpset;
  	const struct dpll_data *dd;
  
  	cur_rate = omap2xxx_clk_get_core_rate(dclk);
c4d7e58fb   Paul Walmsley   OMAP2/3: PRM/CM: ...
112
  	mult = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
b1823d861   Paul Walmsley   OMAP2xxx clock: m...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  	mult &= OMAP24XX_CORE_CLK_SRC_MASK;
  
  	if ((rate == (cur_rate / 2)) && (mult == 2)) {
  		omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1);
  	} else if ((rate == (cur_rate * 2)) && (mult == 1)) {
  		omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
  	} else if (rate != cur_rate) {
  		valid_rate = omap2_dpllcore_round_rate(rate);
  		if (valid_rate != rate)
  			return -EINVAL;
  
  		if (mult == 1)
  			low = curr_prcm_set->dpll_speed;
  		else
  			low = curr_prcm_set->dpll_speed / 2;
  
  		dd = clk->dpll_data;
  		if (!dd)
  			return -EINVAL;
  
  		tmpset.cm_clksel1_pll = __raw_readl(dd->mult_div1_reg);
  		tmpset.cm_clksel1_pll &= ~(dd->mult_mask |
  					   dd->div1_mask);
  		div = ((curr_prcm_set->xtal_speed / 1000000) - 1);
c4d7e58fb   Paul Walmsley   OMAP2/3: PRM/CM: ...
137
  		tmpset.cm_clksel2_pll = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
b1823d861   Paul Walmsley   OMAP2xxx clock: m...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  		tmpset.cm_clksel2_pll &= ~OMAP24XX_CORE_CLK_SRC_MASK;
  		if (rate > low) {
  			tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL_X2;
  			mult = ((rate / 2) / 1000000);
  			done_rate = CORE_CLK_SRC_DPLL_X2;
  		} else {
  			tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL;
  			mult = (rate / 1000000);
  			done_rate = CORE_CLK_SRC_DPLL;
  		}
  		tmpset.cm_clksel1_pll |= (div << __ffs(dd->mult_mask));
  		tmpset.cm_clksel1_pll |= (mult << __ffs(dd->div1_mask));
  
  		/* Worst case */
  		tmpset.base_sdrc_rfr = SDRC_RFR_CTRL_BYPASS;
  
  		if (rate == curr_prcm_set->xtal_speed)	/* If asking for 1-1 */
  			bypass = 1;
  
  		/* For omap2xxx_sdrc_init_params() */
  		omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
  
  		/* Force dll lock mode */
  		omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr,
  			       bypass);
  
  		/* Errata: ret dll entry state */
  		omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked());
  		omap2xxx_sdrc_reprogram(done_rate, 0);
  	}
  
  	return 0;
  }