Commit | Line | Data |
---|---|---|
efdf72ad CC |
1 | /* |
2 | * Copyright (C) 2011 Google, Inc. | |
3 | * | |
4 | * Author: | |
5 | * Colin Cross <ccross@android.com> | |
6 | * | |
7 | * This software is licensed under the terms of the GNU General Public | |
8 | * License version 2, as published by the Free Software Foundation, and | |
9 | * may be copied, distributed, and modified under those terms. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/kernel.h> | |
17711dbf | 19 | #include <linux/device.h> |
efdf72ad CC |
20 | #include <linux/clk.h> |
21 | #include <linux/err.h> | |
22 | #include <linux/io.h> | |
23 | #include <linux/module.h> | |
941b8db1 | 24 | #include <linux/of.h> |
17711dbf OJ |
25 | #include <linux/platform_device.h> |
26 | #include <linux/platform_data/tegra_emc.h> | |
efdf72ad | 27 | |
efdf72ad | 28 | #include "tegra2_emc.h" |
941b8db1 | 29 | #include "fuse.h" |
efdf72ad CC |
30 | |
31 | #ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE | |
32 | static bool emc_enable = true; | |
33 | #else | |
34 | static bool emc_enable; | |
35 | #endif | |
36 | module_param(emc_enable, bool, 0644); | |
37 | ||
17711dbf OJ |
38 | static struct platform_device *emc_pdev; |
39 | static void __iomem *emc_regbase; | |
efdf72ad CC |
40 | |
41 | static inline void emc_writel(u32 val, unsigned long addr) | |
42 | { | |
17711dbf | 43 | writel(val, emc_regbase + addr); |
efdf72ad CC |
44 | } |
45 | ||
46 | static inline u32 emc_readl(unsigned long addr) | |
47 | { | |
17711dbf | 48 | return readl(emc_regbase + addr); |
efdf72ad CC |
49 | } |
50 | ||
51 | static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { | |
52 | 0x2c, /* RC */ | |
53 | 0x30, /* RFC */ | |
54 | 0x34, /* RAS */ | |
55 | 0x38, /* RP */ | |
56 | 0x3c, /* R2W */ | |
57 | 0x40, /* W2R */ | |
58 | 0x44, /* R2P */ | |
59 | 0x48, /* W2P */ | |
60 | 0x4c, /* RD_RCD */ | |
61 | 0x50, /* WR_RCD */ | |
62 | 0x54, /* RRD */ | |
63 | 0x58, /* REXT */ | |
64 | 0x5c, /* WDV */ | |
65 | 0x60, /* QUSE */ | |
66 | 0x64, /* QRST */ | |
67 | 0x68, /* QSAFE */ | |
68 | 0x6c, /* RDV */ | |
69 | 0x70, /* REFRESH */ | |
70 | 0x74, /* BURST_REFRESH_NUM */ | |
71 | 0x78, /* PDEX2WR */ | |
72 | 0x7c, /* PDEX2RD */ | |
73 | 0x80, /* PCHG2PDEN */ | |
74 | 0x84, /* ACT2PDEN */ | |
75 | 0x88, /* AR2PDEN */ | |
76 | 0x8c, /* RW2PDEN */ | |
77 | 0x90, /* TXSR */ | |
78 | 0x94, /* TCKE */ | |
79 | 0x98, /* TFAW */ | |
80 | 0x9c, /* TRPAB */ | |
81 | 0xa0, /* TCLKSTABLE */ | |
82 | 0xa4, /* TCLKSTOP */ | |
83 | 0xa8, /* TREFBW */ | |
84 | 0xac, /* QUSE_EXTRA */ | |
85 | 0x114, /* FBIO_CFG6 */ | |
86 | 0xb0, /* ODT_WRITE */ | |
87 | 0xb4, /* ODT_READ */ | |
88 | 0x104, /* FBIO_CFG5 */ | |
89 | 0x2bc, /* CFG_DIG_DLL */ | |
90 | 0x2c0, /* DLL_XFORM_DQS */ | |
91 | 0x2c4, /* DLL_XFORM_QUSE */ | |
92 | 0x2e0, /* ZCAL_REF_CNT */ | |
93 | 0x2e4, /* ZCAL_WAIT_CNT */ | |
94 | 0x2a8, /* AUTO_CAL_INTERVAL */ | |
95 | 0x2d0, /* CFG_CLKTRIM_0 */ | |
96 | 0x2d4, /* CFG_CLKTRIM_1 */ | |
97 | 0x2d8, /* CFG_CLKTRIM_2 */ | |
98 | }; | |
99 | ||
100 | /* Select the closest EMC rate that is higher than the requested rate */ | |
101 | long tegra_emc_round_rate(unsigned long rate) | |
102 | { | |
17711dbf | 103 | struct tegra_emc_pdata *pdata; |
efdf72ad CC |
104 | int i; |
105 | int best = -1; | |
106 | unsigned long distance = ULONG_MAX; | |
107 | ||
17711dbf | 108 | if (!emc_pdev) |
efdf72ad CC |
109 | return -EINVAL; |
110 | ||
17711dbf | 111 | pdata = emc_pdev->dev.platform_data; |
efdf72ad CC |
112 | |
113 | pr_debug("%s: %lu\n", __func__, rate); | |
114 | ||
115 | /* | |
116 | * The EMC clock rate is twice the bus rate, and the bus rate is | |
117 | * measured in kHz | |
118 | */ | |
119 | rate = rate / 2 / 1000; | |
120 | ||
17711dbf OJ |
121 | for (i = 0; i < pdata->num_tables; i++) { |
122 | if (pdata->tables[i].rate >= rate && | |
123 | (pdata->tables[i].rate - rate) < distance) { | |
124 | distance = pdata->tables[i].rate - rate; | |
efdf72ad CC |
125 | best = i; |
126 | } | |
127 | } | |
128 | ||
129 | if (best < 0) | |
130 | return -EINVAL; | |
131 | ||
17711dbf | 132 | pr_debug("%s: using %lu\n", __func__, pdata->tables[best].rate); |
efdf72ad | 133 | |
17711dbf | 134 | return pdata->tables[best].rate * 2 * 1000; |
efdf72ad CC |
135 | } |
136 | ||
137 | /* | |
138 | * The EMC registers have shadow registers. When the EMC clock is updated | |
139 | * in the clock controller, the shadow registers are copied to the active | |
140 | * registers, allowing glitchless memory bus frequency changes. | |
141 | * This function updates the shadow registers for a new clock frequency, | |
142 | * and relies on the clock lock on the emc clock to avoid races between | |
143 | * multiple frequency changes | |
144 | */ | |
145 | int tegra_emc_set_rate(unsigned long rate) | |
146 | { | |
17711dbf | 147 | struct tegra_emc_pdata *pdata; |
efdf72ad CC |
148 | int i; |
149 | int j; | |
150 | ||
17711dbf | 151 | if (!emc_pdev) |
efdf72ad CC |
152 | return -EINVAL; |
153 | ||
17711dbf OJ |
154 | pdata = emc_pdev->dev.platform_data; |
155 | ||
efdf72ad CC |
156 | /* |
157 | * The EMC clock rate is twice the bus rate, and the bus rate is | |
158 | * measured in kHz | |
159 | */ | |
160 | rate = rate / 2 / 1000; | |
161 | ||
17711dbf OJ |
162 | for (i = 0; i < pdata->num_tables; i++) |
163 | if (pdata->tables[i].rate == rate) | |
efdf72ad CC |
164 | break; |
165 | ||
17711dbf | 166 | if (i >= pdata->num_tables) |
efdf72ad CC |
167 | return -EINVAL; |
168 | ||
169 | pr_debug("%s: setting to %lu\n", __func__, rate); | |
170 | ||
171 | for (j = 0; j < TEGRA_EMC_NUM_REGS; j++) | |
17711dbf OJ |
172 | emc_writel(pdata->tables[i].regs[j], emc_reg_addr[j]); |
173 | ||
174 | emc_readl(pdata->tables[i].regs[TEGRA_EMC_NUM_REGS - 1]); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
941b8db1 OJ |
179 | #ifdef CONFIG_OF |
180 | static struct device_node *tegra_emc_ramcode_devnode(struct device_node *np) | |
181 | { | |
182 | struct device_node *iter; | |
183 | u32 reg; | |
184 | ||
185 | for_each_child_of_node(np, iter) { | |
186 | if (of_property_read_u32(np, "nvidia,ram-code", ®)) | |
187 | continue; | |
188 | if (reg == tegra_bct_strapping) | |
189 | return of_node_get(iter); | |
190 | } | |
191 | ||
192 | return NULL; | |
193 | } | |
194 | ||
195 | static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( | |
196 | struct platform_device *pdev) | |
197 | { | |
198 | struct device_node *np = pdev->dev.of_node; | |
199 | struct device_node *tnp, *iter; | |
200 | struct tegra_emc_pdata *pdata; | |
201 | int ret, i, num_tables; | |
202 | ||
203 | if (!np) | |
204 | return NULL; | |
205 | ||
206 | if (of_find_property(np, "nvidia,use-ram-code", NULL)) { | |
207 | tnp = tegra_emc_ramcode_devnode(np); | |
208 | if (!tnp) | |
209 | dev_warn(&pdev->dev, | |
210 | "can't find emc table for ram-code 0x%02x\n", | |
211 | tegra_bct_strapping); | |
212 | } else | |
213 | tnp = of_node_get(np); | |
214 | ||
215 | if (!tnp) | |
216 | return NULL; | |
217 | ||
218 | num_tables = 0; | |
219 | for_each_child_of_node(tnp, iter) | |
220 | if (of_device_is_compatible(iter, "nvidia,tegra20-emc-table")) | |
221 | num_tables++; | |
222 | ||
223 | if (!num_tables) { | |
224 | pdata = NULL; | |
225 | goto out; | |
226 | } | |
227 | ||
228 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | |
229 | pdata->tables = devm_kzalloc(&pdev->dev, | |
230 | sizeof(*pdata->tables) * num_tables, | |
231 | GFP_KERNEL); | |
232 | ||
233 | i = 0; | |
234 | for_each_child_of_node(tnp, iter) { | |
235 | u32 prop; | |
236 | ||
237 | ret = of_property_read_u32(iter, "clock-frequency", &prop); | |
238 | if (ret) { | |
239 | dev_err(&pdev->dev, "no clock-frequency in %s\n", | |
240 | iter->full_name); | |
241 | continue; | |
242 | } | |
243 | pdata->tables[i].rate = prop; | |
244 | ||
245 | ret = of_property_read_u32_array(iter, "nvidia,emc-registers", | |
246 | pdata->tables[i].regs, | |
247 | TEGRA_EMC_NUM_REGS); | |
248 | if (ret) { | |
249 | dev_err(&pdev->dev, | |
250 | "malformed emc-registers property in %s\n", | |
251 | iter->full_name); | |
252 | continue; | |
253 | } | |
254 | ||
255 | i++; | |
256 | } | |
257 | pdata->num_tables = i; | |
258 | ||
259 | out: | |
260 | of_node_put(tnp); | |
261 | return pdata; | |
262 | } | |
263 | #else | |
264 | static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( | |
265 | struct platform_device *pdev) | |
266 | { | |
267 | return NULL; | |
268 | } | |
269 | #endif | |
270 | ||
351a102d | 271 | static struct tegra_emc_pdata *tegra_emc_fill_pdata(struct platform_device *pdev) |
941b8db1 OJ |
272 | { |
273 | struct clk *c = clk_get_sys(NULL, "emc"); | |
274 | struct tegra_emc_pdata *pdata; | |
275 | unsigned long khz; | |
276 | int i; | |
277 | ||
278 | WARN_ON(pdev->dev.platform_data); | |
23cbd4e8 | 279 | BUG_ON(IS_ERR(c)); |
941b8db1 OJ |
280 | |
281 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | |
282 | pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables), | |
283 | GFP_KERNEL); | |
284 | ||
76c2f6e5 | 285 | pdata->tables[0].rate = clk_get_rate(c) / 2 / 1000; |
941b8db1 OJ |
286 | |
287 | for (i = 0; i < TEGRA_EMC_NUM_REGS; i++) | |
288 | pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]); | |
289 | ||
290 | pdata->num_tables = 1; | |
291 | ||
76c2f6e5 | 292 | khz = pdata->tables[0].rate; |
941b8db1 | 293 | dev_info(&pdev->dev, "no tables provided, using %ld kHz emc, " |
76c2f6e5 | 294 | "%ld kHz mem\n", khz * 2, khz); |
941b8db1 OJ |
295 | |
296 | return pdata; | |
297 | } | |
298 | ||
351a102d | 299 | static int tegra_emc_probe(struct platform_device *pdev) |
17711dbf OJ |
300 | { |
301 | struct tegra_emc_pdata *pdata; | |
302 | struct resource *res; | |
303 | ||
304 | if (!emc_enable) { | |
305 | dev_err(&pdev->dev, "disabled per module parameter\n"); | |
306 | return -ENODEV; | |
307 | } | |
308 | ||
17711dbf | 309 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
5857bd98 TR |
310 | emc_regbase = devm_ioremap_resource(&pdev->dev, res); |
311 | if (IS_ERR(emc_regbase)) | |
312 | return PTR_ERR(emc_regbase); | |
941b8db1 OJ |
313 | |
314 | pdata = pdev->dev.platform_data; | |
315 | ||
316 | if (!pdata) | |
317 | pdata = tegra_emc_dt_parse_pdata(pdev); | |
318 | ||
319 | if (!pdata) | |
320 | pdata = tegra_emc_fill_pdata(pdev); | |
321 | ||
322 | pdev->dev.platform_data = pdata; | |
323 | ||
17711dbf | 324 | emc_pdev = pdev; |
efdf72ad CC |
325 | |
326 | return 0; | |
327 | } | |
328 | ||
351a102d | 329 | static struct of_device_id tegra_emc_of_match[] = { |
941b8db1 OJ |
330 | { .compatible = "nvidia,tegra20-emc", }, |
331 | { }, | |
332 | }; | |
333 | ||
17711dbf OJ |
334 | static struct platform_driver tegra_emc_driver = { |
335 | .driver = { | |
336 | .name = "tegra-emc", | |
337 | .owner = THIS_MODULE, | |
941b8db1 | 338 | .of_match_table = tegra_emc_of_match, |
17711dbf OJ |
339 | }, |
340 | .probe = tegra_emc_probe, | |
341 | }; | |
342 | ||
343 | static int __init tegra_emc_init(void) | |
efdf72ad | 344 | { |
17711dbf | 345 | return platform_driver_register(&tegra_emc_driver); |
efdf72ad | 346 | } |
17711dbf | 347 | device_initcall(tegra_emc_init); |