]>
Commit | Line | Data |
---|---|---|
38a41fdf SH |
1 | /* |
2 | * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
17 | */ | |
18 | ||
19 | #include <linux/kernel.h> | |
20 | #include <linux/device.h> | |
21 | #include <linux/list.h> | |
22 | #include <linux/math64.h> | |
23 | #include <linux/err.h> | |
fced80c7 | 24 | #include <linux/io.h> |
38a41fdf | 25 | |
a09e64fb | 26 | #include <mach/imx-regs.h> |
38a41fdf SH |
27 | |
28 | /* | |
29 | * Very simple approach: We can't disable clocks, so we do | |
30 | * not need refcounting | |
31 | */ | |
32 | ||
33 | struct clk { | |
34 | struct list_head node; | |
35 | const char *name; | |
36 | unsigned long (*get_rate)(void); | |
37 | }; | |
38 | ||
39 | /* | |
40 | * get the system pll clock in Hz | |
41 | * | |
42 | * mfi + mfn / (mfd +1) | |
43 | * f = 2 * f_ref * -------------------- | |
44 | * pd + 1 | |
45 | */ | |
46 | static unsigned long imx_decode_pll(unsigned int pll, u32 f_ref) | |
47 | { | |
48 | unsigned long long ll; | |
49 | unsigned long quot; | |
50 | ||
51 | u32 mfi = (pll >> 10) & 0xf; | |
52 | u32 mfn = pll & 0x3ff; | |
53 | u32 mfd = (pll >> 16) & 0x3ff; | |
54 | u32 pd = (pll >> 26) & 0xf; | |
55 | ||
56 | mfi = mfi <= 5 ? 5 : mfi; | |
57 | ||
58 | ll = 2 * (unsigned long long)f_ref * | |
59 | ((mfi << 16) + (mfn << 16) / (mfd + 1)); | |
60 | quot = (pd + 1) * (1 << 16); | |
61 | ll += quot / 2; | |
62 | do_div(ll, quot); | |
63 | return (unsigned long)ll; | |
64 | } | |
65 | ||
66 | static unsigned long imx_get_system_clk(void) | |
67 | { | |
68 | u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512); | |
69 | ||
70 | return imx_decode_pll(SPCTL0, f_ref); | |
71 | } | |
72 | ||
73 | static unsigned long imx_get_mcu_clk(void) | |
74 | { | |
75 | return imx_decode_pll(MPCTL0, CLK32 * 512); | |
76 | } | |
77 | ||
78 | /* | |
79 | * get peripheral clock 1 ( UART[12], Timer[12], PWM ) | |
80 | */ | |
81 | static unsigned long imx_get_perclk1(void) | |
82 | { | |
83 | return imx_get_system_clk() / (((PCDR) & 0xf)+1); | |
84 | } | |
85 | ||
86 | /* | |
87 | * get peripheral clock 2 ( LCD, SD, SPI[12] ) | |
88 | */ | |
89 | static unsigned long imx_get_perclk2(void) | |
90 | { | |
91 | return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1); | |
92 | } | |
93 | ||
94 | /* | |
95 | * get peripheral clock 3 ( SSI ) | |
96 | */ | |
97 | static unsigned long imx_get_perclk3(void) | |
98 | { | |
99 | return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1); | |
100 | } | |
101 | ||
102 | /* | |
103 | * get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA ) | |
104 | */ | |
105 | static unsigned long imx_get_hclk(void) | |
106 | { | |
107 | return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1); | |
108 | } | |
109 | ||
110 | static struct clk clk_system_clk = { | |
111 | .name = "system_clk", | |
112 | .get_rate = imx_get_system_clk, | |
113 | }; | |
114 | ||
115 | static struct clk clk_hclk = { | |
116 | .name = "hclk", | |
117 | .get_rate = imx_get_hclk, | |
118 | }; | |
119 | ||
120 | static struct clk clk_mcu_clk = { | |
121 | .name = "mcu_clk", | |
122 | .get_rate = imx_get_mcu_clk, | |
123 | }; | |
124 | ||
125 | static struct clk clk_perclk1 = { | |
126 | .name = "perclk1", | |
127 | .get_rate = imx_get_perclk1, | |
128 | }; | |
129 | ||
130 | static struct clk clk_uart_clk = { | |
131 | .name = "uart_clk", | |
132 | .get_rate = imx_get_perclk1, | |
133 | }; | |
134 | ||
135 | static struct clk clk_perclk2 = { | |
136 | .name = "perclk2", | |
137 | .get_rate = imx_get_perclk2, | |
138 | }; | |
139 | ||
140 | static struct clk clk_perclk3 = { | |
141 | .name = "perclk3", | |
142 | .get_rate = imx_get_perclk3, | |
143 | }; | |
144 | ||
145 | static struct clk *clks[] = { | |
146 | &clk_perclk1, | |
147 | &clk_perclk2, | |
148 | &clk_perclk3, | |
149 | &clk_system_clk, | |
150 | &clk_hclk, | |
151 | &clk_mcu_clk, | |
152 | &clk_uart_clk, | |
153 | }; | |
154 | ||
155 | static LIST_HEAD(clocks); | |
156 | static DEFINE_MUTEX(clocks_mutex); | |
157 | ||
158 | struct clk *clk_get(struct device *dev, const char *id) | |
159 | { | |
160 | struct clk *p, *clk = ERR_PTR(-ENOENT); | |
161 | ||
162 | mutex_lock(&clocks_mutex); | |
163 | list_for_each_entry(p, &clocks, node) { | |
164 | if (!strcmp(p->name, id)) { | |
165 | clk = p; | |
166 | goto found; | |
167 | } | |
168 | } | |
169 | ||
170 | found: | |
171 | mutex_unlock(&clocks_mutex); | |
172 | ||
173 | return clk; | |
174 | } | |
79a13b29 | 175 | EXPORT_SYMBOL(clk_get); |
38a41fdf SH |
176 | |
177 | void clk_put(struct clk *clk) | |
178 | { | |
179 | } | |
79a13b29 | 180 | EXPORT_SYMBOL(clk_put); |
38a41fdf SH |
181 | |
182 | int clk_enable(struct clk *clk) | |
183 | { | |
184 | return 0; | |
185 | } | |
79a13b29 | 186 | EXPORT_SYMBOL(clk_enable); |
38a41fdf SH |
187 | |
188 | void clk_disable(struct clk *clk) | |
189 | { | |
190 | } | |
79a13b29 | 191 | EXPORT_SYMBOL(clk_disable); |
38a41fdf SH |
192 | |
193 | unsigned long clk_get_rate(struct clk *clk) | |
194 | { | |
195 | return clk->get_rate(); | |
196 | } | |
79a13b29 | 197 | EXPORT_SYMBOL(clk_get_rate); |
38a41fdf SH |
198 | |
199 | int imx_clocks_init(void) | |
200 | { | |
201 | int i; | |
202 | ||
203 | mutex_lock(&clocks_mutex); | |
204 | for (i = 0; i < ARRAY_SIZE(clks); i++) | |
205 | list_add(&clks[i]->node, &clocks); | |
206 | mutex_unlock(&clocks_mutex); | |
207 | ||
208 | return 0; | |
209 | } | |
210 |