blob: ef23127bb6f77636416510e800283bcf41bcbe10 [file] [log] [blame]
Aneesh V0d2628b2011-07-21 09:10:07 -04001/*
2 *
3 * Clock initialization for OMAP4
4 *
5 * (C) Copyright 2010
6 * Texas Instruments, <www.ti.com>
7 *
8 * Aneesh V <aneesh@ti.com>
9 *
10 * Based on previous work by:
11 * Santosh Shilimkar <santosh.shilimkar@ti.com>
12 * Rajendra Nayak <rnayak@ti.com>
13 *
14 * See file CREDITS for list of people who contributed to this
15 * project.
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of
20 * the License, or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30 * MA 02111-1307 USA
31 */
32#include <common.h>
Lokesh Vutla36852972013-05-30 03:19:29 +000033#include <i2c.h>
Aneesh V0d2628b2011-07-21 09:10:07 -040034#include <asm/omap_common.h>
Sanjeev Premi0c2c8ac2011-09-08 10:48:39 -040035#include <asm/gpio.h>
Lokesh Vutla61c517f2013-05-30 02:54:32 +000036#include <asm/arch/clock.h>
Aneesh V0d2628b2011-07-21 09:10:07 -040037#include <asm/arch/sys_proto.h>
38#include <asm/utils.h>
Aneesh V0fa1d1b2011-07-21 09:29:32 -040039#include <asm/omap_gpio.h>
Lokesh Vutlafef54c32013-02-04 04:21:59 +000040#include <asm/emif.h>
Aneesh V0d2628b2011-07-21 09:10:07 -040041
42#ifndef CONFIG_SPL_BUILD
43/*
44 * printing to console doesn't work unless
45 * this code is executed from SPL
46 */
47#define printf(fmt, args...)
48#define puts(s)
49#endif
50
SRICHARAN R1a79cab2013-02-04 04:22:01 +000051const u32 sys_clk_array[8] = {
52 12000000, /* 12 MHz */
Lokesh Vutla16523262013-05-30 03:19:38 +000053 20000000, /* 20 MHz */
SRICHARAN R1a79cab2013-02-04 04:22:01 +000054 16800000, /* 16.8 MHz */
55 19200000, /* 19.2 MHz */
56 26000000, /* 26 MHz */
57 27000000, /* 27 MHz */
58 38400000, /* 38.4 MHz */
59};
60
Aneesh V0d2628b2011-07-21 09:10:07 -040061static inline u32 __get_sys_clk_index(void)
62{
Lokesh Vutla5e70e292013-02-12 21:29:05 +000063 s8 ind;
Aneesh V0d2628b2011-07-21 09:10:07 -040064 /*
65 * For ES1 the ROM code calibration of sys clock is not reliable
66 * due to hw issue. So, use hard-coded value. If this value is not
67 * correct for any board over-ride this function in board file
68 * From ES2.0 onwards you will get this information from
69 * CM_SYS_CLKSEL
70 */
71 if (omap_revision() == OMAP4430_ES1_0)
72 ind = OMAP_SYS_CLK_IND_38_4_MHZ;
73 else {
74 /* SYS_CLKSEL - 1 to match the dpll param array indices */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +000075 ind = (readl((*prcm)->cm_sys_clksel) &
Aneesh V0d2628b2011-07-21 09:10:07 -040076 CM_SYS_CLKSEL_SYS_CLKSEL_MASK) - 1;
77 }
78 return ind;
79}
80
81u32 get_sys_clk_index(void)
82 __attribute__ ((weak, alias("__get_sys_clk_index")));
83
84u32 get_sys_clk_freq(void)
85{
86 u8 index = get_sys_clk_index();
87 return sys_clk_array[index];
88}
89
SRICHARAN R1a79cab2013-02-04 04:22:01 +000090void setup_post_dividers(u32 const base, const struct dpll_params *params)
91{
92 struct dpll_regs *const dpll_regs = (struct dpll_regs *)base;
93
94 /* Setup post-dividers */
95 if (params->m2 >= 0)
96 writel(params->m2, &dpll_regs->cm_div_m2_dpll);
97 if (params->m3 >= 0)
98 writel(params->m3, &dpll_regs->cm_div_m3_dpll);
99 if (params->m4_h11 >= 0)
100 writel(params->m4_h11, &dpll_regs->cm_div_m4_h11_dpll);
101 if (params->m5_h12 >= 0)
102 writel(params->m5_h12, &dpll_regs->cm_div_m5_h12_dpll);
103 if (params->m6_h13 >= 0)
104 writel(params->m6_h13, &dpll_regs->cm_div_m6_h13_dpll);
105 if (params->m7_h14 >= 0)
106 writel(params->m7_h14, &dpll_regs->cm_div_m7_h14_dpll);
SRICHARAN Ra04ed142013-02-12 01:33:43 +0000107 if (params->h21 >= 0)
108 writel(params->h21, &dpll_regs->cm_div_h21_dpll);
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000109 if (params->h22 >= 0)
110 writel(params->h22, &dpll_regs->cm_div_h22_dpll);
111 if (params->h23 >= 0)
112 writel(params->h23, &dpll_regs->cm_div_h23_dpll);
SRICHARAN Ra04ed142013-02-12 01:33:43 +0000113 if (params->h24 >= 0)
114 writel(params->h24, &dpll_regs->cm_div_h24_dpll);
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000115}
116
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000117static inline void do_bypass_dpll(u32 const base)
Aneesh V0d2628b2011-07-21 09:10:07 -0400118{
119 struct dpll_regs *dpll_regs = (struct dpll_regs *)base;
120
121 clrsetbits_le32(&dpll_regs->cm_clkmode_dpll,
122 CM_CLKMODE_DPLL_DPLL_EN_MASK,
123 DPLL_EN_FAST_RELOCK_BYPASS <<
124 CM_CLKMODE_DPLL_EN_SHIFT);
125}
126
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000127static inline void wait_for_bypass(u32 const base)
Aneesh V0d2628b2011-07-21 09:10:07 -0400128{
129 struct dpll_regs *const dpll_regs = (struct dpll_regs *)base;
130
131 if (!wait_on_value(ST_DPLL_CLK_MASK, 0, &dpll_regs->cm_idlest_dpll,
132 LDELAY)) {
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000133 printf("Bypassing DPLL failed %x\n", base);
Aneesh V0d2628b2011-07-21 09:10:07 -0400134 }
135}
136
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000137static inline void do_lock_dpll(u32 const base)
Aneesh V0d2628b2011-07-21 09:10:07 -0400138{
139 struct dpll_regs *const dpll_regs = (struct dpll_regs *)base;
140
141 clrsetbits_le32(&dpll_regs->cm_clkmode_dpll,
142 CM_CLKMODE_DPLL_DPLL_EN_MASK,
143 DPLL_EN_LOCK << CM_CLKMODE_DPLL_EN_SHIFT);
144}
145
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000146static inline void wait_for_lock(u32 const base)
Aneesh V0d2628b2011-07-21 09:10:07 -0400147{
148 struct dpll_regs *const dpll_regs = (struct dpll_regs *)base;
149
150 if (!wait_on_value(ST_DPLL_CLK_MASK, ST_DPLL_CLK_MASK,
151 &dpll_regs->cm_idlest_dpll, LDELAY)) {
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000152 printf("DPLL locking failed for %x\n", base);
Aneesh V0d2628b2011-07-21 09:10:07 -0400153 hang();
154 }
155}
156
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000157inline u32 check_for_lock(u32 const base)
Sricharan308fe922011-11-15 09:50:03 -0500158{
159 struct dpll_regs *const dpll_regs = (struct dpll_regs *)base;
160 u32 lock = readl(&dpll_regs->cm_idlest_dpll) & ST_DPLL_CLK_MASK;
161
162 return lock;
163}
164
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000165const struct dpll_params *get_mpu_dpll_params(struct dplls const *dpll_data)
166{
167 u32 sysclk_ind = get_sys_clk_index();
168 return &dpll_data->mpu[sysclk_ind];
169}
170
171const struct dpll_params *get_core_dpll_params(struct dplls const *dpll_data)
172{
173 u32 sysclk_ind = get_sys_clk_index();
174 return &dpll_data->core[sysclk_ind];
175}
176
177const struct dpll_params *get_per_dpll_params(struct dplls const *dpll_data)
178{
179 u32 sysclk_ind = get_sys_clk_index();
180 return &dpll_data->per[sysclk_ind];
181}
182
183const struct dpll_params *get_iva_dpll_params(struct dplls const *dpll_data)
184{
185 u32 sysclk_ind = get_sys_clk_index();
186 return &dpll_data->iva[sysclk_ind];
187}
188
189const struct dpll_params *get_usb_dpll_params(struct dplls const *dpll_data)
190{
191 u32 sysclk_ind = get_sys_clk_index();
192 return &dpll_data->usb[sysclk_ind];
193}
194
195const struct dpll_params *get_abe_dpll_params(struct dplls const *dpll_data)
196{
197#ifdef CONFIG_SYS_OMAP_ABE_SYSCK
198 u32 sysclk_ind = get_sys_clk_index();
199 return &dpll_data->abe[sysclk_ind];
200#else
201 return dpll_data->abe;
202#endif
203}
204
Lokesh Vutla5e70e292013-02-12 21:29:05 +0000205static const struct dpll_params *get_ddr_dpll_params
206 (struct dplls const *dpll_data)
207{
208 u32 sysclk_ind = get_sys_clk_index();
209
210 if (!dpll_data->ddr)
211 return NULL;
212 return &dpll_data->ddr[sysclk_ind];
213}
214
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000215static void do_setup_dpll(u32 const base, const struct dpll_params *params,
Sricharan308fe922011-11-15 09:50:03 -0500216 u8 lock, char *dpll)
Aneesh V0d2628b2011-07-21 09:10:07 -0400217{
Sricharan308fe922011-11-15 09:50:03 -0500218 u32 temp, M, N;
Aneesh V0d2628b2011-07-21 09:10:07 -0400219 struct dpll_regs *const dpll_regs = (struct dpll_regs *)base;
220
Lokesh Vutla5e70e292013-02-12 21:29:05 +0000221 if (!params)
222 return;
223
Sricharan308fe922011-11-15 09:50:03 -0500224 temp = readl(&dpll_regs->cm_clksel_dpll);
225
226 if (check_for_lock(base)) {
227 /*
228 * The Dpll has already been locked by rom code using CH.
229 * Check if M,N are matching with Ideal nominal opp values.
230 * If matches, skip the rest otherwise relock.
231 */
232 M = (temp & CM_CLKSEL_DPLL_M_MASK) >> CM_CLKSEL_DPLL_M_SHIFT;
233 N = (temp & CM_CLKSEL_DPLL_N_MASK) >> CM_CLKSEL_DPLL_N_SHIFT;
234 if ((M != (params->m)) || (N != (params->n))) {
235 debug("\n %s Dpll locked, but not for ideal M = %d,"
236 "N = %d values, current values are M = %d,"
237 "N= %d" , dpll, params->m, params->n,
238 M, N);
239 } else {
240 /* Dpll locked with ideal values for nominal opps. */
241 debug("\n %s Dpll already locked with ideal"
242 "nominal opp values", dpll);
243 goto setup_post_dividers;
244 }
245 }
246
Aneesh V0d2628b2011-07-21 09:10:07 -0400247 bypass_dpll(base);
248
249 /* Set M & N */
Aneesh V0d2628b2011-07-21 09:10:07 -0400250 temp &= ~CM_CLKSEL_DPLL_M_MASK;
251 temp |= (params->m << CM_CLKSEL_DPLL_M_SHIFT) & CM_CLKSEL_DPLL_M_MASK;
252
253 temp &= ~CM_CLKSEL_DPLL_N_MASK;
254 temp |= (params->n << CM_CLKSEL_DPLL_N_SHIFT) & CM_CLKSEL_DPLL_N_MASK;
255
256 writel(temp, &dpll_regs->cm_clksel_dpll);
257
258 /* Lock */
259 if (lock)
260 do_lock_dpll(base);
261
Sricharan308fe922011-11-15 09:50:03 -0500262setup_post_dividers:
Sricharan9784f1f2011-11-15 09:49:58 -0500263 setup_post_dividers(base, params);
Aneesh V0d2628b2011-07-21 09:10:07 -0400264
265 /* Wait till the DPLL locks */
266 if (lock)
267 wait_for_lock(base);
268}
269
Sricharan9784f1f2011-11-15 09:49:58 -0500270u32 omap_ddr_clk(void)
Aneesh V0d2628b2011-07-21 09:10:07 -0400271{
Sricharan9784f1f2011-11-15 09:49:58 -0500272 u32 ddr_clk, sys_clk_khz, omap_rev, divider;
Aneesh V0d2628b2011-07-21 09:10:07 -0400273 const struct dpll_params *core_dpll_params;
274
Sricharan9784f1f2011-11-15 09:49:58 -0500275 omap_rev = omap_revision();
Aneesh V0d2628b2011-07-21 09:10:07 -0400276 sys_clk_khz = get_sys_clk_freq() / 1000;
277
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000278 core_dpll_params = get_core_dpll_params(*dplls_data);
Aneesh V0d2628b2011-07-21 09:10:07 -0400279
280 debug("sys_clk %d\n ", sys_clk_khz * 1000);
281
282 /* Find Core DPLL locked frequency first */
283 ddr_clk = sys_clk_khz * 2 * core_dpll_params->m /
284 (core_dpll_params->n + 1);
Sricharan9784f1f2011-11-15 09:49:58 -0500285
286 if (omap_rev < OMAP5430_ES1_0) {
287 /*
288 * DDR frequency is PHY_ROOT_CLK/2
289 * PHY_ROOT_CLK = Fdpll/2/M2
290 */
291 divider = 4;
292 } else {
293 /*
294 * DDR frequency is PHY_ROOT_CLK
295 * PHY_ROOT_CLK = Fdpll/2/M2
296 */
297 divider = 2;
298 }
Aneesh V0d2628b2011-07-21 09:10:07 -0400299
Sricharan9784f1f2011-11-15 09:49:58 -0500300 ddr_clk = ddr_clk / divider / core_dpll_params->m2;
Aneesh V0d2628b2011-07-21 09:10:07 -0400301 ddr_clk *= 1000; /* convert to Hz */
302 debug("ddr_clk %d\n ", ddr_clk);
303
304 return ddr_clk;
305}
306
Aneesh Va47a79f2011-07-21 09:29:36 -0400307/*
308 * Lock MPU dpll
309 *
310 * Resulting MPU frequencies:
311 * 4430 ES1.0 : 600 MHz
312 * 4430 ES2.x : 792 MHz (OPP Turbo)
313 * 4460 : 920 MHz (OPP Turbo) - DCC disabled
314 */
315void configure_mpu_dpll(void)
316{
317 const struct dpll_params *params;
318 struct dpll_regs *mpu_dpll_regs;
Sricharan9784f1f2011-11-15 09:49:58 -0500319 u32 omap_rev;
320 omap_rev = omap_revision();
Aneesh Va47a79f2011-07-21 09:29:36 -0400321
Sricharan9784f1f2011-11-15 09:49:58 -0500322 /*
323 * DCC and clock divider settings for 4460.
324 * DCC is required, if more than a certain frequency is required.
325 * For, 4460 > 1GHZ.
326 * 5430 > 1.4GHZ.
327 */
328 if ((omap_rev >= OMAP4460_ES1_0) && (omap_rev < OMAP5430_ES1_0)) {
Aneesh Va47a79f2011-07-21 09:29:36 -0400329 mpu_dpll_regs =
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000330 (struct dpll_regs *)((*prcm)->cm_clkmode_dpll_mpu);
331 bypass_dpll((*prcm)->cm_clkmode_dpll_mpu);
332 clrbits_le32((*prcm)->cm_mpu_mpu_clkctrl,
Aneesh Va47a79f2011-07-21 09:29:36 -0400333 MPU_CLKCTRL_CLKSEL_EMIF_DIV_MODE_MASK);
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000334 setbits_le32((*prcm)->cm_mpu_mpu_clkctrl,
Aneesh Va47a79f2011-07-21 09:29:36 -0400335 MPU_CLKCTRL_CLKSEL_ABE_DIV_MODE_MASK);
336 clrbits_le32(&mpu_dpll_regs->cm_clksel_dpll,
337 CM_CLKSEL_DCC_EN_MASK);
338 }
339
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000340 params = get_mpu_dpll_params(*dplls_data);
Sricharan308fe922011-11-15 09:50:03 -0500341
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000342 do_setup_dpll((*prcm)->cm_clkmode_dpll_mpu, params, DPLL_LOCK, "mpu");
Aneesh Va47a79f2011-07-21 09:29:36 -0400343 debug("MPU DPLL locked\n");
344}
345
Govindraj.Rad4426b2012-02-06 03:55:36 +0000346#ifdef CONFIG_USB_EHCI_OMAP
347static void setup_usb_dpll(void)
348{
349 const struct dpll_params *params;
350 u32 sys_clk_khz, sd_div, num, den;
351
352 sys_clk_khz = get_sys_clk_freq() / 1000;
353 /*
354 * USB:
355 * USB dpll is J-type. Need to set DPLL_SD_DIV for jitter correction
356 * DPLL_SD_DIV = CEILING ([DPLL_MULT/(DPLL_DIV+1)]* CLKINP / 250)
357 * - where CLKINP is sys_clk in MHz
358 * Use CLKINP in KHz and adjust the denominator accordingly so
359 * that we have enough accuracy and at the same time no overflow
360 */
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000361 params = get_usb_dpll_params(*dplls_data);
Govindraj.Rad4426b2012-02-06 03:55:36 +0000362 num = params->m * sys_clk_khz;
363 den = (params->n + 1) * 250 * 1000;
364 num += den - 1;
365 sd_div = num / den;
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000366 clrsetbits_le32((*prcm)->cm_clksel_dpll_usb,
Govindraj.Rad4426b2012-02-06 03:55:36 +0000367 CM_CLKSEL_DPLL_DPLL_SD_DIV_MASK,
368 sd_div << CM_CLKSEL_DPLL_DPLL_SD_DIV_SHIFT);
369
370 /* Now setup the dpll with the regular function */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000371 do_setup_dpll((*prcm)->cm_clkmode_dpll_usb, params, DPLL_LOCK, "usb");
Govindraj.Rad4426b2012-02-06 03:55:36 +0000372}
373#endif
374
Aneesh V0d2628b2011-07-21 09:10:07 -0400375static void setup_dplls(void)
376{
Anatolij Gustschin20f23512011-12-03 06:46:14 +0000377 u32 temp;
Aneesh V0d2628b2011-07-21 09:10:07 -0400378 const struct dpll_params *params;
Aneesh V0d2628b2011-07-21 09:10:07 -0400379
Anatolij Gustschin20f23512011-12-03 06:46:14 +0000380 debug("setup_dplls\n");
Aneesh V0d2628b2011-07-21 09:10:07 -0400381
382 /* CORE dpll */
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000383 params = get_core_dpll_params(*dplls_data); /* default - safest */
Aneesh V0d2628b2011-07-21 09:10:07 -0400384 /*
385 * Do not lock the core DPLL now. Just set it up.
386 * Core DPLL will be locked after setting up EMIF
387 * using the FREQ_UPDATE method(freq_update_core())
388 */
Lokesh Vutlafef54c32013-02-04 04:21:59 +0000389 if (emif_sdram_type() == EMIF_SDRAM_TYPE_LPDDR2)
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000390 do_setup_dpll((*prcm)->cm_clkmode_dpll_core, params,
Lokesh Vutlacdfc4ea2012-05-22 00:03:26 +0000391 DPLL_NO_LOCK, "core");
392 else
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000393 do_setup_dpll((*prcm)->cm_clkmode_dpll_core, params,
Lokesh Vutlacdfc4ea2012-05-22 00:03:26 +0000394 DPLL_LOCK, "core");
Aneesh V0d2628b2011-07-21 09:10:07 -0400395 /* Set the ratios for CORE_CLK, L3_CLK, L4_CLK */
396 temp = (CLKSEL_CORE_X2_DIV_1 << CLKSEL_CORE_SHIFT) |
397 (CLKSEL_L3_CORE_DIV_2 << CLKSEL_L3_SHIFT) |
398 (CLKSEL_L4_L3_DIV_2 << CLKSEL_L4_SHIFT);
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000399 writel(temp, (*prcm)->cm_clksel_core);
Aneesh V0d2628b2011-07-21 09:10:07 -0400400 debug("Core DPLL configured\n");
401
402 /* lock PER dpll */
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000403 params = get_per_dpll_params(*dplls_data);
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000404 do_setup_dpll((*prcm)->cm_clkmode_dpll_per,
Sricharan308fe922011-11-15 09:50:03 -0500405 params, DPLL_LOCK, "per");
Aneesh V0d2628b2011-07-21 09:10:07 -0400406 debug("PER DPLL locked\n");
407
408 /* MPU dpll */
Aneesh Va47a79f2011-07-21 09:29:36 -0400409 configure_mpu_dpll();
Govindraj.Rad4426b2012-02-06 03:55:36 +0000410
411#ifdef CONFIG_USB_EHCI_OMAP
412 setup_usb_dpll();
413#endif
Lokesh Vutla5e70e292013-02-12 21:29:05 +0000414 params = get_ddr_dpll_params(*dplls_data);
415 do_setup_dpll((*prcm)->cm_clkmode_dpll_ddrphy,
416 params, DPLL_LOCK, "ddr");
Aneesh V0d2628b2011-07-21 09:10:07 -0400417}
418
Sricharan308fe922011-11-15 09:50:03 -0500419#ifdef CONFIG_SYS_CLOCKS_ENABLE_ALL
Aneesh V0d2628b2011-07-21 09:10:07 -0400420static void setup_non_essential_dplls(void)
421{
Anatolij Gustschind75ffd42012-03-27 23:13:43 +0000422 u32 abe_ref_clk;
Aneesh V0d2628b2011-07-21 09:10:07 -0400423 const struct dpll_params *params;
424
Aneesh V0d2628b2011-07-21 09:10:07 -0400425 /* IVA */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000426 clrsetbits_le32((*prcm)->cm_bypclk_dpll_iva,
Aneesh V0d2628b2011-07-21 09:10:07 -0400427 CM_BYPCLK_DPLL_IVA_CLKSEL_MASK, DPLL_IVA_CLKSEL_CORE_X2_DIV_2);
428
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000429 params = get_iva_dpll_params(*dplls_data);
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000430 do_setup_dpll((*prcm)->cm_clkmode_dpll_iva, params, DPLL_LOCK, "iva");
Aneesh V0d2628b2011-07-21 09:10:07 -0400431
Sricharan9784f1f2011-11-15 09:49:58 -0500432 /* Configure ABE dpll */
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000433 params = get_abe_dpll_params(*dplls_data);
Sricharan9784f1f2011-11-15 09:49:58 -0500434#ifdef CONFIG_SYS_OMAP_ABE_SYSCK
Aneesh V0d2628b2011-07-21 09:10:07 -0400435 abe_ref_clk = CM_ABE_PLL_REF_CLKSEL_CLKSEL_SYSCLK;
Lokesh Vutla16523262013-05-30 03:19:38 +0000436
437 if (omap_revision() == DRA752_ES1_0)
438 /* Select the sys clk for dpll_abe */
439 clrsetbits_le32((*prcm)->cm_abe_pll_sys_clksel,
440 CM_CLKSEL_ABE_PLL_SYS_CLKSEL_MASK,
441 CM_ABE_PLL_SYS_CLKSEL_SYSCLK2);
Aneesh V0d2628b2011-07-21 09:10:07 -0400442#else
Aneesh V0d2628b2011-07-21 09:10:07 -0400443 abe_ref_clk = CM_ABE_PLL_REF_CLKSEL_CLKSEL_32KCLK;
444 /*
445 * We need to enable some additional options to achieve
446 * 196.608MHz from 32768 Hz
447 */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000448 setbits_le32((*prcm)->cm_clkmode_dpll_abe,
Aneesh V0d2628b2011-07-21 09:10:07 -0400449 CM_CLKMODE_DPLL_DRIFTGUARD_EN_MASK|
450 CM_CLKMODE_DPLL_RELOCK_RAMP_EN_MASK|
451 CM_CLKMODE_DPLL_LPMODE_EN_MASK|
452 CM_CLKMODE_DPLL_REGM4XEN_MASK);
453 /* Spend 4 REFCLK cycles at each stage */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000454 clrsetbits_le32((*prcm)->cm_clkmode_dpll_abe,
Aneesh V0d2628b2011-07-21 09:10:07 -0400455 CM_CLKMODE_DPLL_RAMP_RATE_MASK,
456 1 << CM_CLKMODE_DPLL_RAMP_RATE_SHIFT);
457#endif
458
459 /* Select the right reference clk */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000460 clrsetbits_le32((*prcm)->cm_abe_pll_ref_clksel,
Aneesh V0d2628b2011-07-21 09:10:07 -0400461 CM_ABE_PLL_REF_CLKSEL_CLKSEL_MASK,
462 abe_ref_clk << CM_ABE_PLL_REF_CLKSEL_CLKSEL_SHIFT);
463 /* Lock the dpll */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000464 do_setup_dpll((*prcm)->cm_clkmode_dpll_abe, params, DPLL_LOCK, "abe");
Aneesh V0d2628b2011-07-21 09:10:07 -0400465}
Sricharan308fe922011-11-15 09:50:03 -0500466#endif
Aneesh V0d2628b2011-07-21 09:10:07 -0400467
SRICHARAN R00d328c2013-02-04 04:22:02 +0000468u32 get_offset_code(u32 volt_offset, struct pmic_data *pmic)
Aneesh V0fa1d1b2011-07-21 09:29:32 -0400469{
SRICHARAN R00d328c2013-02-04 04:22:02 +0000470 u32 offset_code;
Aneesh V0fa1d1b2011-07-21 09:29:32 -0400471
SRICHARAN R00d328c2013-02-04 04:22:02 +0000472 volt_offset -= pmic->base_offset;
Aneesh V0fa1d1b2011-07-21 09:29:32 -0400473
SRICHARAN R00d328c2013-02-04 04:22:02 +0000474 offset_code = (volt_offset + pmic->step - 1) / pmic->step;
Nishanth Menona0f45c12012-03-01 14:17:38 +0000475
SRICHARAN R00d328c2013-02-04 04:22:02 +0000476 /*
477 * Offset codes 1-6 all give the base voltage in Palmas
478 * Offset code 0 switches OFF the SMPS
479 */
480 return offset_code + pmic->start_code;
Aneesh V0fa1d1b2011-07-21 09:29:32 -0400481}
482
SRICHARAN R00d328c2013-02-04 04:22:02 +0000483void do_scale_vcore(u32 vcore_reg, u32 volt_mv, struct pmic_data *pmic)
Aneesh V0d2628b2011-07-21 09:10:07 -0400484{
Nishanth Menon41d7ab12012-03-01 14:17:37 +0000485 u32 offset_code;
Aneesh V0d2628b2011-07-21 09:10:07 -0400486 u32 offset = volt_mv;
SRICHARAN R00d328c2013-02-04 04:22:02 +0000487 int ret = 0;
488
Lokesh Vutla36852972013-05-30 03:19:29 +0000489 if (!volt_mv)
490 return;
491
Lokesh Vutlaae49f6d2013-05-30 02:54:33 +0000492 pmic->pmic_bus_init();
SRICHARAN R00d328c2013-02-04 04:22:02 +0000493 /* See if we can first get the GPIO if needed */
494 if (pmic->gpio_en)
495 ret = gpio_request(pmic->gpio, "PMIC_GPIO");
496
497 if (ret < 0) {
498 printf("%s: gpio %d request failed %d\n", __func__,
499 pmic->gpio, ret);
500 return;
501 }
502
503 /* Pull the GPIO low to select SET0 register, while we program SET1 */
504 if (pmic->gpio_en)
505 gpio_direction_output(pmic->gpio, 0);
Aneesh V0d2628b2011-07-21 09:10:07 -0400506
507 /* convert to uV for better accuracy in the calculations */
508 offset *= 1000;
509
SRICHARAN R00d328c2013-02-04 04:22:02 +0000510 offset_code = get_offset_code(offset, pmic);
Aneesh V0d2628b2011-07-21 09:10:07 -0400511
512 debug("do_scale_vcore: volt - %d offset_code - 0x%x\n", volt_mv,
513 offset_code);
SRICHARAN R698a1f22012-03-12 02:25:38 +0000514
Lokesh Vutlaae49f6d2013-05-30 02:54:33 +0000515 if (pmic->pmic_write(pmic->i2c_slave_addr, vcore_reg, offset_code))
Aneesh V0d2628b2011-07-21 09:10:07 -0400516 printf("Scaling voltage failed for 0x%x\n", vcore_reg);
SRICHARAN R00d328c2013-02-04 04:22:02 +0000517
518 if (pmic->gpio_en)
519 gpio_direction_output(pmic->gpio, 1);
520}
521
Nishanth Menon93cdb282013-05-30 03:19:31 +0000522static u32 optimize_vcore_voltage(struct volts const *v)
523{
524 u32 val;
525 if (!v->value)
526 return 0;
527 if (!v->efuse.reg)
528 return v->value;
529
530 switch (v->efuse.reg_bits) {
531 case 16:
532 val = readw(v->efuse.reg);
533 break;
534 case 32:
535 val = readl(v->efuse.reg);
536 break;
537 default:
538 printf("Error: efuse 0x%08x bits=%d unknown\n",
539 v->efuse.reg, v->efuse.reg_bits);
540 return v->value;
541 }
542
543 if (!val) {
544 printf("Error: efuse 0x%08x bits=%d val=0, using %d\n",
545 v->efuse.reg, v->efuse.reg_bits, v->value);
546 return v->value;
547 }
548
549 debug("%s:efuse 0x%08x bits=%d Vnom=%d, using efuse value %d\n",
550 __func__, v->efuse.reg, v->efuse.reg_bits, v->value, val);
551 return val;
552}
553
SRICHARAN R00d328c2013-02-04 04:22:02 +0000554/*
555 * Setup the voltages for vdd_mpu, vdd_core, and vdd_iva
556 * We set the maximum voltages allowed here because Smart-Reflex is not
557 * enabled in bootloader. Voltage initialization in the kernel will set
558 * these to the nominal values after enabling Smart-Reflex
559 */
560void scale_vcores(struct vcores_data const *vcores)
561{
Nishanth Menon93cdb282013-05-30 03:19:31 +0000562 u32 val;
SRICHARAN R00d328c2013-02-04 04:22:02 +0000563
Nishanth Menon93cdb282013-05-30 03:19:31 +0000564 val = optimize_vcore_voltage(&vcores->core);
565 do_scale_vcore(vcores->core.addr, val, vcores->core.pmic);
566
567 val = optimize_vcore_voltage(&vcores->mpu);
568 do_scale_vcore(vcores->mpu.addr, val, vcores->mpu.pmic);
SRICHARAN R00d328c2013-02-04 04:22:02 +0000569
Andrii Tseglytskyi84bd3252013-05-20 22:42:09 +0000570 /* Configure MPU ABB LDO after scale */
571 abb_setup((*ctrl)->control_std_fuse_opp_vdd_mpu_2,
572 (*ctrl)->control_wkup_ldovbb_mpu_voltage_ctrl,
573 (*prcm)->prm_abbldo_mpu_setup,
574 (*prcm)->prm_abbldo_mpu_ctrl,
575 (*prcm)->prm_irqstatus_mpu_2,
576 OMAP_ABB_MPU_TXDONE_MASK,
577 OMAP_ABB_FAST_OPP);
578
Nishanth Menon93cdb282013-05-30 03:19:31 +0000579 val = optimize_vcore_voltage(&vcores->mm);
580 do_scale_vcore(vcores->mm.addr, val, vcores->mm.pmic);
SRICHARAN R00d328c2013-02-04 04:22:02 +0000581
Nishanth Menon93cdb282013-05-30 03:19:31 +0000582 val = optimize_vcore_voltage(&vcores->gpu);
583 do_scale_vcore(vcores->gpu.addr, val, vcores->gpu.pmic);
Lokesh Vutla36852972013-05-30 03:19:29 +0000584
Nishanth Menon93cdb282013-05-30 03:19:31 +0000585 val = optimize_vcore_voltage(&vcores->eve);
586 do_scale_vcore(vcores->eve.addr, val, vcores->eve.pmic);
Lokesh Vutla36852972013-05-30 03:19:29 +0000587
Nishanth Menon93cdb282013-05-30 03:19:31 +0000588 val = optimize_vcore_voltage(&vcores->iva);
589 do_scale_vcore(vcores->iva.addr, val, vcores->iva.pmic);
Lokesh Vutla36852972013-05-30 03:19:29 +0000590
SRICHARAN R00d328c2013-02-04 04:22:02 +0000591 if (emif_sdram_type() == EMIF_SDRAM_TYPE_DDR3) {
592 /* Configure LDO SRAM "magic" bits */
593 writel(2, (*prcm)->prm_sldo_core_setup);
594 writel(2, (*prcm)->prm_sldo_mpu_setup);
595 writel(2, (*prcm)->prm_sldo_mm_setup);
596 }
Aneesh V0d2628b2011-07-21 09:10:07 -0400597}
598
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000599static inline void enable_clock_domain(u32 const clkctrl_reg, u32 enable_mode)
Aneesh V0d2628b2011-07-21 09:10:07 -0400600{
601 clrsetbits_le32(clkctrl_reg, CD_CLKCTRL_CLKTRCTRL_MASK,
602 enable_mode << CD_CLKCTRL_CLKTRCTRL_SHIFT);
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000603 debug("Enable clock domain - %x\n", clkctrl_reg);
Aneesh V0d2628b2011-07-21 09:10:07 -0400604}
605
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000606static inline void wait_for_clk_enable(u32 clkctrl_addr)
Aneesh V0d2628b2011-07-21 09:10:07 -0400607{
608 u32 clkctrl, idlest = MODULE_CLKCTRL_IDLEST_DISABLED;
609 u32 bound = LDELAY;
610
611 while ((idlest == MODULE_CLKCTRL_IDLEST_DISABLED) ||
612 (idlest == MODULE_CLKCTRL_IDLEST_TRANSITIONING)) {
613
614 clkctrl = readl(clkctrl_addr);
615 idlest = (clkctrl & MODULE_CLKCTRL_IDLEST_MASK) >>
616 MODULE_CLKCTRL_IDLEST_SHIFT;
617 if (--bound == 0) {
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000618 printf("Clock enable failed for 0x%x idlest 0x%x\n",
Aneesh V0d2628b2011-07-21 09:10:07 -0400619 clkctrl_addr, clkctrl);
620 return;
621 }
622 }
623}
624
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000625static inline void enable_clock_module(u32 const clkctrl_addr, u32 enable_mode,
Aneesh V0d2628b2011-07-21 09:10:07 -0400626 u32 wait_for_enable)
627{
628 clrsetbits_le32(clkctrl_addr, MODULE_CLKCTRL_MODULEMODE_MASK,
629 enable_mode << MODULE_CLKCTRL_MODULEMODE_SHIFT);
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000630 debug("Enable clock module - %x\n", clkctrl_addr);
Aneesh V0d2628b2011-07-21 09:10:07 -0400631 if (wait_for_enable)
632 wait_for_clk_enable(clkctrl_addr);
633}
634
Aneesh V0d2628b2011-07-21 09:10:07 -0400635void freq_update_core(void)
636{
637 u32 freq_config1 = 0;
638 const struct dpll_params *core_dpll_params;
SRICHARAN R3d534962012-03-12 02:25:37 +0000639 u32 omap_rev = omap_revision();
Aneesh V0d2628b2011-07-21 09:10:07 -0400640
SRICHARAN R1a79cab2013-02-04 04:22:01 +0000641 core_dpll_params = get_core_dpll_params(*dplls_data);
Aneesh V0d2628b2011-07-21 09:10:07 -0400642 /* Put EMIF clock domain in sw wakeup mode */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000643 enable_clock_domain((*prcm)->cm_memif_clkstctrl,
Aneesh V0d2628b2011-07-21 09:10:07 -0400644 CD_CLKCTRL_CLKTRCTRL_SW_WKUP);
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000645 wait_for_clk_enable((*prcm)->cm_memif_emif_1_clkctrl);
646 wait_for_clk_enable((*prcm)->cm_memif_emif_2_clkctrl);
Aneesh V0d2628b2011-07-21 09:10:07 -0400647
648 freq_config1 = SHADOW_FREQ_CONFIG1_FREQ_UPDATE_MASK |
649 SHADOW_FREQ_CONFIG1_DLL_RESET_MASK;
650
651 freq_config1 |= (DPLL_EN_LOCK << SHADOW_FREQ_CONFIG1_DPLL_EN_SHIFT) &
652 SHADOW_FREQ_CONFIG1_DPLL_EN_MASK;
653
654 freq_config1 |= (core_dpll_params->m2 <<
655 SHADOW_FREQ_CONFIG1_M2_DIV_SHIFT) &
656 SHADOW_FREQ_CONFIG1_M2_DIV_MASK;
657
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000658 writel(freq_config1, (*prcm)->cm_shadow_freq_config1);
Aneesh V0d2628b2011-07-21 09:10:07 -0400659 if (!wait_on_value(SHADOW_FREQ_CONFIG1_FREQ_UPDATE_MASK, 0,
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000660 (u32 *) (*prcm)->cm_shadow_freq_config1, LDELAY)) {
Aneesh V0d2628b2011-07-21 09:10:07 -0400661 puts("FREQ UPDATE procedure failed!!");
662 hang();
663 }
664
SRICHARAN R3d534962012-03-12 02:25:37 +0000665 /*
666 * Putting EMIF in HW_AUTO is seen to be causing issues with
Lubomir Popova01f0b02013-04-04 05:51:45 +0000667 * EMIF clocks and the master DLL. Keep EMIF in SW_WKUP
SRICHARAN R3d534962012-03-12 02:25:37 +0000668 * in OMAP5430 ES1.0 silicon
669 */
670 if (omap_rev != OMAP5430_ES1_0) {
671 /* Put EMIF clock domain back in hw auto mode */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000672 enable_clock_domain((*prcm)->cm_memif_clkstctrl,
SRICHARAN R3d534962012-03-12 02:25:37 +0000673 CD_CLKCTRL_CLKTRCTRL_HW_AUTO);
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000674 wait_for_clk_enable((*prcm)->cm_memif_emif_1_clkctrl);
675 wait_for_clk_enable((*prcm)->cm_memif_emif_2_clkctrl);
SRICHARAN R3d534962012-03-12 02:25:37 +0000676 }
Aneesh V0d2628b2011-07-21 09:10:07 -0400677}
678
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000679void bypass_dpll(u32 const base)
Aneesh V0d2628b2011-07-21 09:10:07 -0400680{
681 do_bypass_dpll(base);
682 wait_for_bypass(base);
683}
684
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000685void lock_dpll(u32 const base)
Aneesh V0d2628b2011-07-21 09:10:07 -0400686{
687 do_lock_dpll(base);
688 wait_for_lock(base);
689}
690
Aneesh Vb8e60b92011-07-21 09:10:21 -0400691void setup_clocks_for_console(void)
692{
693 /* Do not add any spl_debug prints in this function */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000694 clrsetbits_le32((*prcm)->cm_l4per_clkstctrl, CD_CLKCTRL_CLKTRCTRL_MASK,
Aneesh Vb8e60b92011-07-21 09:10:21 -0400695 CD_CLKCTRL_CLKTRCTRL_SW_WKUP <<
696 CD_CLKCTRL_CLKTRCTRL_SHIFT);
697
698 /* Enable all UARTs - console will be on one of them */
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000699 clrsetbits_le32((*prcm)->cm_l4per_uart1_clkctrl,
Aneesh Vb8e60b92011-07-21 09:10:21 -0400700 MODULE_CLKCTRL_MODULEMODE_MASK,
701 MODULE_CLKCTRL_MODULEMODE_SW_EXPLICIT_EN <<
702 MODULE_CLKCTRL_MODULEMODE_SHIFT);
703
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000704 clrsetbits_le32((*prcm)->cm_l4per_uart2_clkctrl,
Aneesh Vb8e60b92011-07-21 09:10:21 -0400705 MODULE_CLKCTRL_MODULEMODE_MASK,
706 MODULE_CLKCTRL_MODULEMODE_SW_EXPLICIT_EN <<
707 MODULE_CLKCTRL_MODULEMODE_SHIFT);
708
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000709 clrsetbits_le32((*prcm)->cm_l4per_uart3_clkctrl,
Aneesh Vb8e60b92011-07-21 09:10:21 -0400710 MODULE_CLKCTRL_MODULEMODE_MASK,
711 MODULE_CLKCTRL_MODULEMODE_SW_EXPLICIT_EN <<
712 MODULE_CLKCTRL_MODULEMODE_SHIFT);
713
Lubomir Popova01f0b02013-04-04 05:51:45 +0000714 clrsetbits_le32((*prcm)->cm_l4per_uart4_clkctrl,
Aneesh Vb8e60b92011-07-21 09:10:21 -0400715 MODULE_CLKCTRL_MODULEMODE_MASK,
716 MODULE_CLKCTRL_MODULEMODE_SW_EXPLICIT_EN <<
717 MODULE_CLKCTRL_MODULEMODE_SHIFT);
718
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000719 clrsetbits_le32((*prcm)->cm_l4per_clkstctrl, CD_CLKCTRL_CLKTRCTRL_MASK,
Aneesh Vb8e60b92011-07-21 09:10:21 -0400720 CD_CLKCTRL_CLKTRCTRL_HW_AUTO <<
721 CD_CLKCTRL_CLKTRCTRL_SHIFT);
722}
723
SRICHARAN Rfb6aa1f2013-02-04 04:22:00 +0000724void do_enable_clocks(u32 const *clk_domains,
725 u32 const *clk_modules_hw_auto,
726 u32 const *clk_modules_explicit_en,
Sricharan9784f1f2011-11-15 09:49:58 -0500727 u8 wait_for_enable)
728{
729 u32 i, max = 100;
730
731 /* Put the clock domains in SW_WKUP mode */
732 for (i = 0; (i < max) && clk_domains[i]; i++) {
733 enable_clock_domain(clk_domains[i],
734 CD_CLKCTRL_CLKTRCTRL_SW_WKUP);
735 }
736
737 /* Clock modules that need to be put in HW_AUTO */
738 for (i = 0; (i < max) && clk_modules_hw_auto[i]; i++) {
739 enable_clock_module(clk_modules_hw_auto[i],
740 MODULE_CLKCTRL_MODULEMODE_HW_AUTO,
741 wait_for_enable);
742 };
743
744 /* Clock modules that need to be put in SW_EXPLICIT_EN mode */
745 for (i = 0; (i < max) && clk_modules_explicit_en[i]; i++) {
746 enable_clock_module(clk_modules_explicit_en[i],
747 MODULE_CLKCTRL_MODULEMODE_SW_EXPLICIT_EN,
748 wait_for_enable);
749 };
750
751 /* Put the clock domains in HW_AUTO mode now */
752 for (i = 0; (i < max) && clk_domains[i]; i++) {
753 enable_clock_domain(clk_domains[i],
754 CD_CLKCTRL_CLKTRCTRL_HW_AUTO);
755 }
756}
757
Aneesh V0d2628b2011-07-21 09:10:07 -0400758void prcm_init(void)
759{
Sricharan9310ff72011-11-15 09:49:55 -0500760 switch (omap_hw_init_context()) {
Aneesh V0d2628b2011-07-21 09:10:07 -0400761 case OMAP_INIT_CONTEXT_SPL:
762 case OMAP_INIT_CONTEXT_UBOOT_FROM_NOR:
763 case OMAP_INIT_CONTEXT_UBOOT_AFTER_CH:
Aneesh V9a390882011-07-21 09:29:29 -0400764 enable_basic_clocks();
Lokesh Vutlad9c839a2013-05-30 03:19:30 +0000765 timer_init();
SRICHARAN R00d328c2013-02-04 04:22:02 +0000766 scale_vcores(*omap_vcores);
Aneesh V0d2628b2011-07-21 09:10:07 -0400767 setup_dplls();
Sricharan308fe922011-11-15 09:50:03 -0500768#ifdef CONFIG_SYS_CLOCKS_ENABLE_ALL
Aneesh V0d2628b2011-07-21 09:10:07 -0400769 setup_non_essential_dplls();
770 enable_non_essential_clocks();
Sricharan308fe922011-11-15 09:50:03 -0500771#endif
Lokesh Vutla100c2d82013-04-17 20:49:40 +0000772 setup_warmreset_time();
Aneesh V0d2628b2011-07-21 09:10:07 -0400773 break;
774 default:
775 break;
776 }
Sricharan308fe922011-11-15 09:50:03 -0500777
778 if (OMAP_INIT_CONTEXT_SPL != omap_hw_init_context())
779 enable_basic_uboot_clocks();
Aneesh V0d2628b2011-07-21 09:10:07 -0400780}
Lokesh Vutla36852972013-05-30 03:19:29 +0000781
782void gpi2c_init(void)
783{
784 static int gpi2c = 1;
785
786 if (gpi2c) {
787 i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
788 gpi2c = 0;
789 }
790}