• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*****************************************************************************
2 * Copyright 2003 - 2008 Broadcom Corporation.  All rights reserved.
3 *
4 * Unless you and Broadcom execute a separate written software license
5 * agreement governing use of this software, this software is licensed to you
6 * under the terms of the GNU General Public License version 2, available at
7 * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8 *
9 * Notwithstanding the above, under no circumstances may you combine this
10 * software in any way with any other Broadcom software provided under a
11 * license other than the GPL, without Broadcom's express prior written
12 * consent.
13 *****************************************************************************/
14 
15 /****************************************************************************/
16 /**
17 *  @file    chipcHw.c
18 *
19 *  @brief   Low level Various CHIP clock controlling routines
20 *
21 *  @note
22 *
23 *   These routines provide basic clock controlling functionality only.
24 */
25 /****************************************************************************/
26 
27 /* ---- Include Files ---------------------------------------------------- */
28 
29 #include <csp/errno.h>
30 #include <csp/stdint.h>
31 #include <csp/module.h>
32 
33 #include <mach/csp/chipcHw_def.h>
34 #include <mach/csp/chipcHw_inline.h>
35 
36 #include <csp/reg.h>
37 #include <csp/delay.h>
38 
39 /* ---- Private Constants and Types --------------------------------------- */
40 
41 /* VPM alignment algorithm uses this */
42 #define MAX_PHASE_ADJUST_COUNT         0xFFFF	/* Max number of times allowed to adjust the phase */
43 #define MAX_PHASE_ALIGN_ATTEMPTS       10	/* Max number of attempt to align the phase */
44 
45 /* Local definition of clock type */
46 #define PLL_CLOCK                      1	/* PLL Clock */
47 #define NON_PLL_CLOCK                  2	/* Divider clock */
48 
49 static int chipcHw_divide(int num, int denom)
50     __attribute__ ((section(".aramtext")));
51 
52 /****************************************************************************/
53 /**
54 *  @brief   Set clock fequency for miscellaneous configurable clocks
55 *
56 *  This function sets clock frequency
57 *
58 *  @return  Configured clock frequency in hertz
59 *
60 */
61 /****************************************************************************/
chipcHw_getClockFrequency(chipcHw_CLOCK_e clock)62 chipcHw_freq chipcHw_getClockFrequency(chipcHw_CLOCK_e clock	/*  [ IN ] Configurable clock */
63     ) {
64 	volatile uint32_t *pPLLReg = (uint32_t *) 0x0;
65 	volatile uint32_t *pClockCtrl = (uint32_t *) 0x0;
66 	volatile uint32_t *pDependentClock = (uint32_t *) 0x0;
67 	uint32_t vcoFreqPll1Hz = 0;	/* Effective VCO frequency for PLL1 in Hz */
68 	uint32_t vcoFreqPll2Hz = 0;	/* Effective VCO frequency for PLL2 in Hz */
69 	uint32_t dependentClockType = 0;
70 	uint32_t vcoHz = 0;
71 
72 	/* Get VCO frequencies */
73 	if ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASK) != chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER) {
74 		uint64_t adjustFreq = 0;
75 
76 		vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
77 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
78 		    ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
79 		     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
80 
81 		/* Adjusted frequency due to chipcHw_REG_PLL_DIVIDER_NDIV_f_SS */
82 		adjustFreq = (uint64_t) chipcHw_XTAL_FREQ_Hz *
83 			(uint64_t) chipcHw_REG_PLL_DIVIDER_NDIV_f_SS *
84 			chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, (chipcHw_REG_PLL_PREDIVIDER_P2 * (uint64_t) chipcHw_REG_PLL_DIVIDER_FRAC));
85 		vcoFreqPll1Hz += (uint32_t) adjustFreq;
86 	} else {
87 		vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
88 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
89 		    ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
90 		     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
91 	}
92 	vcoFreqPll2Hz =
93 	    chipcHw_XTAL_FREQ_Hz *
94 		 chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
95 	    ((pChipcHw->PLLPreDivider2 & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
96 	     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
97 
98 	switch (clock) {
99 	case chipcHw_CLOCK_DDR:
100 		pPLLReg = &pChipcHw->DDRClock;
101 		vcoHz = vcoFreqPll1Hz;
102 		break;
103 	case chipcHw_CLOCK_ARM:
104 		pPLLReg = &pChipcHw->ARMClock;
105 		vcoHz = vcoFreqPll1Hz;
106 		break;
107 	case chipcHw_CLOCK_ESW:
108 		pPLLReg = &pChipcHw->ESWClock;
109 		vcoHz = vcoFreqPll1Hz;
110 		break;
111 	case chipcHw_CLOCK_VPM:
112 		pPLLReg = &pChipcHw->VPMClock;
113 		vcoHz = vcoFreqPll1Hz;
114 		break;
115 	case chipcHw_CLOCK_ESW125:
116 		pPLLReg = &pChipcHw->ESW125Clock;
117 		vcoHz = vcoFreqPll1Hz;
118 		break;
119 	case chipcHw_CLOCK_UART:
120 		pPLLReg = &pChipcHw->UARTClock;
121 		vcoHz = vcoFreqPll1Hz;
122 		break;
123 	case chipcHw_CLOCK_SDIO0:
124 		pPLLReg = &pChipcHw->SDIO0Clock;
125 		vcoHz = vcoFreqPll1Hz;
126 		break;
127 	case chipcHw_CLOCK_SDIO1:
128 		pPLLReg = &pChipcHw->SDIO1Clock;
129 		vcoHz = vcoFreqPll1Hz;
130 		break;
131 	case chipcHw_CLOCK_SPI:
132 		pPLLReg = &pChipcHw->SPIClock;
133 		vcoHz = vcoFreqPll1Hz;
134 		break;
135 	case chipcHw_CLOCK_ETM:
136 		pPLLReg = &pChipcHw->ETMClock;
137 		vcoHz = vcoFreqPll1Hz;
138 		break;
139 	case chipcHw_CLOCK_USB:
140 		pPLLReg = &pChipcHw->USBClock;
141 		vcoHz = vcoFreqPll2Hz;
142 		break;
143 	case chipcHw_CLOCK_LCD:
144 		pPLLReg = &pChipcHw->LCDClock;
145 		vcoHz = vcoFreqPll2Hz;
146 		break;
147 	case chipcHw_CLOCK_APM:
148 		pPLLReg = &pChipcHw->APMClock;
149 		vcoHz = vcoFreqPll2Hz;
150 		break;
151 	case chipcHw_CLOCK_BUS:
152 		pClockCtrl = &pChipcHw->ACLKClock;
153 		pDependentClock = &pChipcHw->ARMClock;
154 		vcoHz = vcoFreqPll1Hz;
155 		dependentClockType = PLL_CLOCK;
156 		break;
157 	case chipcHw_CLOCK_OTP:
158 		pClockCtrl = &pChipcHw->OTPClock;
159 		break;
160 	case chipcHw_CLOCK_I2C:
161 		pClockCtrl = &pChipcHw->I2CClock;
162 		break;
163 	case chipcHw_CLOCK_I2S0:
164 		pClockCtrl = &pChipcHw->I2S0Clock;
165 		break;
166 	case chipcHw_CLOCK_RTBUS:
167 		pClockCtrl = &pChipcHw->RTBUSClock;
168 		pDependentClock = &pChipcHw->ACLKClock;
169 		dependentClockType = NON_PLL_CLOCK;
170 		break;
171 	case chipcHw_CLOCK_APM100:
172 		pClockCtrl = &pChipcHw->APM100Clock;
173 		pDependentClock = &pChipcHw->APMClock;
174 		vcoHz = vcoFreqPll2Hz;
175 		dependentClockType = PLL_CLOCK;
176 		break;
177 	case chipcHw_CLOCK_TSC:
178 		pClockCtrl = &pChipcHw->TSCClock;
179 		break;
180 	case chipcHw_CLOCK_LED:
181 		pClockCtrl = &pChipcHw->LEDClock;
182 		break;
183 	case chipcHw_CLOCK_I2S1:
184 		pClockCtrl = &pChipcHw->I2S1Clock;
185 		break;
186 	}
187 
188 	if (pPLLReg) {
189 		/* Obtain PLL clock frequency */
190 		if (*pPLLReg & chipcHw_REG_PLL_CLOCK_BYPASS_SELECT) {
191 			/* Return crystal clock frequency when bypassed */
192 			return chipcHw_XTAL_FREQ_Hz;
193 		} else if (clock == chipcHw_CLOCK_DDR) {
194 			/* DDR frequency is configured in PLLDivider register */
195 			return chipcHw_divide (vcoHz, (((pChipcHw->PLLDivider & 0xFF000000) >> 24) ? ((pChipcHw->PLLDivider & 0xFF000000) >> 24) : 256));
196 		} else {
197 			/* From chip revision number B0, LCD clock is internally divided by 2 */
198 			if ((pPLLReg == &pChipcHw->LCDClock) && (chipcHw_getChipRevisionNumber() != chipcHw_REV_NUMBER_A0)) {
199 				vcoHz >>= 1;
200 			}
201 			/* Obtain PLL clock frequency using VCO dividers */
202 			return chipcHw_divide(vcoHz, ((*pPLLReg & chipcHw_REG_PLL_CLOCK_MDIV_MASK) ? (*pPLLReg & chipcHw_REG_PLL_CLOCK_MDIV_MASK) : 256));
203 		}
204 	} else if (pClockCtrl) {
205 		/* Obtain divider clock frequency */
206 		uint32_t div;
207 		uint32_t freq = 0;
208 
209 		if (*pClockCtrl & chipcHw_REG_DIV_CLOCK_BYPASS_SELECT) {
210 			/* Return crystal clock frequency when bypassed */
211 			return chipcHw_XTAL_FREQ_Hz;
212 		} else if (pDependentClock) {
213 			/* Identify the dependent clock frequency */
214 			switch (dependentClockType) {
215 			case PLL_CLOCK:
216 				if (*pDependentClock & chipcHw_REG_PLL_CLOCK_BYPASS_SELECT) {
217 					/* Use crystal clock frequency when dependent PLL clock is bypassed */
218 					freq = chipcHw_XTAL_FREQ_Hz;
219 				} else {
220 					/* Obtain PLL clock frequency using VCO dividers */
221 					div = *pDependentClock & chipcHw_REG_PLL_CLOCK_MDIV_MASK;
222 					freq = div ? chipcHw_divide(vcoHz, div) : 0;
223 				}
224 				break;
225 			case NON_PLL_CLOCK:
226 				if (pDependentClock == (uint32_t *) &pChipcHw->ACLKClock) {
227 					freq = chipcHw_getClockFrequency (chipcHw_CLOCK_BUS);
228 				} else {
229 					if (*pDependentClock & chipcHw_REG_DIV_CLOCK_BYPASS_SELECT) {
230 						/* Use crystal clock frequency when dependent divider clock is bypassed */
231 						freq = chipcHw_XTAL_FREQ_Hz;
232 					} else {
233 						/* Obtain divider clock frequency using XTAL dividers */
234 						div = *pDependentClock & chipcHw_REG_DIV_CLOCK_DIV_MASK;
235 						freq = chipcHw_divide (chipcHw_XTAL_FREQ_Hz, (div ? div : 256));
236 					}
237 				}
238 				break;
239 			}
240 		} else {
241 			/* Dependent on crystal clock */
242 			freq = chipcHw_XTAL_FREQ_Hz;
243 		}
244 
245 		div = *pClockCtrl & chipcHw_REG_DIV_CLOCK_DIV_MASK;
246 		return chipcHw_divide(freq, (div ? div : 256));
247 	}
248 	return 0;
249 }
250 
251 /****************************************************************************/
252 /**
253 *  @brief   Set clock fequency for miscellaneous configurable clocks
254 *
255 *  This function sets clock frequency
256 *
257 *  @return  Configured clock frequency in Hz
258 *
259 */
260 /****************************************************************************/
chipcHw_setClockFrequency(chipcHw_CLOCK_e clock,uint32_t freq)261 chipcHw_freq chipcHw_setClockFrequency(chipcHw_CLOCK_e clock,	/*  [ IN ] Configurable clock */
262 				       uint32_t freq	/*  [ IN ] Clock frequency in Hz */
263     ) {
264 	volatile uint32_t *pPLLReg = (uint32_t *) 0x0;
265 	volatile uint32_t *pClockCtrl = (uint32_t *) 0x0;
266 	volatile uint32_t *pDependentClock = (uint32_t *) 0x0;
267 	uint32_t vcoFreqPll1Hz = 0;	/* Effective VCO frequency for PLL1 in Hz */
268 	uint32_t desVcoFreqPll1Hz = 0;	/* Desired VCO frequency for PLL1 in Hz */
269 	uint32_t vcoFreqPll2Hz = 0;	/* Effective VCO frequency for PLL2 in Hz */
270 	uint32_t dependentClockType = 0;
271 	uint32_t vcoHz = 0;
272 	uint32_t desVcoHz = 0;
273 
274 	/* Get VCO frequencies */
275 	if ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASK) != chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER) {
276 		uint64_t adjustFreq = 0;
277 
278 		vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
279 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
280 		    ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
281 		     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
282 
283 		/* Adjusted frequency due to chipcHw_REG_PLL_DIVIDER_NDIV_f_SS */
284 		adjustFreq = (uint64_t) chipcHw_XTAL_FREQ_Hz *
285 			(uint64_t) chipcHw_REG_PLL_DIVIDER_NDIV_f_SS *
286 			chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, (chipcHw_REG_PLL_PREDIVIDER_P2 * (uint64_t) chipcHw_REG_PLL_DIVIDER_FRAC));
287 		vcoFreqPll1Hz += (uint32_t) adjustFreq;
288 
289 		/* Desired VCO frequency */
290 		desVcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
291 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
292 		    (((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
293 		      chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) + 1);
294 	} else {
295 		vcoFreqPll1Hz = desVcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
296 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
297 		    ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
298 		     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
299 	}
300 	vcoFreqPll2Hz = chipcHw_XTAL_FREQ_Hz * chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
301 	    ((pChipcHw->PLLPreDivider2 & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
302 	     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
303 
304 	switch (clock) {
305 	case chipcHw_CLOCK_DDR:
306 		/* Configure the DDR_ctrl:BUS ratio settings */
307 		{
308 			REG_LOCAL_IRQ_SAVE;
309 			/* Dvide DDR_phy by two to obtain DDR_ctrl clock */
310 			pChipcHw->DDRClock = (pChipcHw->DDRClock & ~chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_MASK) | ((((freq / 2) / chipcHw_getClockFrequency(chipcHw_CLOCK_BUS)) - 1)
311 				<< chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_SHIFT);
312 			REG_LOCAL_IRQ_RESTORE;
313 		}
314 		pPLLReg = &pChipcHw->DDRClock;
315 		vcoHz = vcoFreqPll1Hz;
316 		desVcoHz = desVcoFreqPll1Hz;
317 		break;
318 	case chipcHw_CLOCK_ARM:
319 		pPLLReg = &pChipcHw->ARMClock;
320 		vcoHz = vcoFreqPll1Hz;
321 		desVcoHz = desVcoFreqPll1Hz;
322 		break;
323 	case chipcHw_CLOCK_ESW:
324 		pPLLReg = &pChipcHw->ESWClock;
325 		vcoHz = vcoFreqPll1Hz;
326 		desVcoHz = desVcoFreqPll1Hz;
327 		break;
328 	case chipcHw_CLOCK_VPM:
329 		/* Configure the VPM:BUS ratio settings */
330 		{
331 			REG_LOCAL_IRQ_SAVE;
332 			pChipcHw->VPMClock = (pChipcHw->VPMClock & ~chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_MASK) | ((chipcHw_divide (freq, chipcHw_getClockFrequency(chipcHw_CLOCK_BUS)) - 1)
333 				<< chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_SHIFT);
334 			REG_LOCAL_IRQ_RESTORE;
335 		}
336 		pPLLReg = &pChipcHw->VPMClock;
337 		vcoHz = vcoFreqPll1Hz;
338 		desVcoHz = desVcoFreqPll1Hz;
339 		break;
340 	case chipcHw_CLOCK_ESW125:
341 		pPLLReg = &pChipcHw->ESW125Clock;
342 		vcoHz = vcoFreqPll1Hz;
343 		desVcoHz = desVcoFreqPll1Hz;
344 		break;
345 	case chipcHw_CLOCK_UART:
346 		pPLLReg = &pChipcHw->UARTClock;
347 		vcoHz = vcoFreqPll1Hz;
348 		desVcoHz = desVcoFreqPll1Hz;
349 		break;
350 	case chipcHw_CLOCK_SDIO0:
351 		pPLLReg = &pChipcHw->SDIO0Clock;
352 		vcoHz = vcoFreqPll1Hz;
353 		desVcoHz = desVcoFreqPll1Hz;
354 		break;
355 	case chipcHw_CLOCK_SDIO1:
356 		pPLLReg = &pChipcHw->SDIO1Clock;
357 		vcoHz = vcoFreqPll1Hz;
358 		desVcoHz = desVcoFreqPll1Hz;
359 		break;
360 	case chipcHw_CLOCK_SPI:
361 		pPLLReg = &pChipcHw->SPIClock;
362 		vcoHz = vcoFreqPll1Hz;
363 		desVcoHz = desVcoFreqPll1Hz;
364 		break;
365 	case chipcHw_CLOCK_ETM:
366 		pPLLReg = &pChipcHw->ETMClock;
367 		vcoHz = vcoFreqPll1Hz;
368 		desVcoHz = desVcoFreqPll1Hz;
369 		break;
370 	case chipcHw_CLOCK_USB:
371 		pPLLReg = &pChipcHw->USBClock;
372 		vcoHz = vcoFreqPll2Hz;
373 		desVcoHz = vcoFreqPll2Hz;
374 		break;
375 	case chipcHw_CLOCK_LCD:
376 		pPLLReg = &pChipcHw->LCDClock;
377 		vcoHz = vcoFreqPll2Hz;
378 		desVcoHz = vcoFreqPll2Hz;
379 		break;
380 	case chipcHw_CLOCK_APM:
381 		pPLLReg = &pChipcHw->APMClock;
382 		vcoHz = vcoFreqPll2Hz;
383 		desVcoHz = vcoFreqPll2Hz;
384 		break;
385 	case chipcHw_CLOCK_BUS:
386 		pClockCtrl = &pChipcHw->ACLKClock;
387 		pDependentClock = &pChipcHw->ARMClock;
388 		vcoHz = vcoFreqPll1Hz;
389 		desVcoHz = desVcoFreqPll1Hz;
390 		dependentClockType = PLL_CLOCK;
391 		break;
392 	case chipcHw_CLOCK_OTP:
393 		pClockCtrl = &pChipcHw->OTPClock;
394 		break;
395 	case chipcHw_CLOCK_I2C:
396 		pClockCtrl = &pChipcHw->I2CClock;
397 		break;
398 	case chipcHw_CLOCK_I2S0:
399 		pClockCtrl = &pChipcHw->I2S0Clock;
400 		break;
401 	case chipcHw_CLOCK_RTBUS:
402 		pClockCtrl = &pChipcHw->RTBUSClock;
403 		pDependentClock = &pChipcHw->ACLKClock;
404 		dependentClockType = NON_PLL_CLOCK;
405 		break;
406 	case chipcHw_CLOCK_APM100:
407 		pClockCtrl = &pChipcHw->APM100Clock;
408 		pDependentClock = &pChipcHw->APMClock;
409 		vcoHz = vcoFreqPll2Hz;
410 		desVcoHz = vcoFreqPll2Hz;
411 		dependentClockType = PLL_CLOCK;
412 		break;
413 	case chipcHw_CLOCK_TSC:
414 		pClockCtrl = &pChipcHw->TSCClock;
415 		break;
416 	case chipcHw_CLOCK_LED:
417 		pClockCtrl = &pChipcHw->LEDClock;
418 		break;
419 	case chipcHw_CLOCK_I2S1:
420 		pClockCtrl = &pChipcHw->I2S1Clock;
421 		break;
422 	}
423 
424 	if (pPLLReg) {
425 		/* Select XTAL as bypass source */
426 		reg32_modify_and(pPLLReg, ~chipcHw_REG_PLL_CLOCK_SOURCE_GPIO);
427 		reg32_modify_or(pPLLReg, chipcHw_REG_PLL_CLOCK_BYPASS_SELECT);
428 		/* For DDR settings use only the PLL divider clock */
429 		if (pPLLReg == &pChipcHw->DDRClock) {
430 			/* Set M1DIV for PLL1, which controls the DDR clock */
431 			reg32_write(&pChipcHw->PLLDivider, (pChipcHw->PLLDivider & 0x00FFFFFF) | ((chipcHw_REG_PLL_DIVIDER_MDIV (desVcoHz, freq)) << 24));
432 			/* Calculate expected frequency */
433 			freq = chipcHw_divide(vcoHz, (((pChipcHw->PLLDivider & 0xFF000000) >> 24) ? ((pChipcHw->PLLDivider & 0xFF000000) >> 24) : 256));
434 		} else {
435 			/* From chip revision number B0, LCD clock is internally divided by 2 */
436 			if ((pPLLReg == &pChipcHw->LCDClock) && (chipcHw_getChipRevisionNumber() != chipcHw_REV_NUMBER_A0)) {
437 				desVcoHz >>= 1;
438 				vcoHz >>= 1;
439 			}
440 			/* Set MDIV to change the frequency */
441 			reg32_modify_and(pPLLReg, ~(chipcHw_REG_PLL_CLOCK_MDIV_MASK));
442 			reg32_modify_or(pPLLReg, chipcHw_REG_PLL_DIVIDER_MDIV(desVcoHz, freq));
443 			/* Calculate expected frequency */
444 			freq = chipcHw_divide(vcoHz, ((*(pPLLReg) & chipcHw_REG_PLL_CLOCK_MDIV_MASK) ? (*(pPLLReg) & chipcHw_REG_PLL_CLOCK_MDIV_MASK) : 256));
445 		}
446 		/* Wait for for atleast 200ns as per the protocol to change frequency */
447 		udelay(1);
448 		/* Do not bypass */
449 		reg32_modify_and(pPLLReg, ~chipcHw_REG_PLL_CLOCK_BYPASS_SELECT);
450 		/* Return the configured frequency */
451 		return freq;
452 	} else if (pClockCtrl) {
453 		uint32_t divider = 0;
454 
455 		/* Divider clock should not be bypassed  */
456 		reg32_modify_and(pClockCtrl,
457 				 ~chipcHw_REG_DIV_CLOCK_BYPASS_SELECT);
458 
459 		/* Identify the clock source */
460 		if (pDependentClock) {
461 			switch (dependentClockType) {
462 			case PLL_CLOCK:
463 				divider = chipcHw_divide(chipcHw_divide (desVcoHz, (*pDependentClock & chipcHw_REG_PLL_CLOCK_MDIV_MASK)), freq);
464 				break;
465 			case NON_PLL_CLOCK:
466 				{
467 					uint32_t sourceClock = 0;
468 
469 					if (pDependentClock == (uint32_t *) &pChipcHw->ACLKClock) {
470 						sourceClock = chipcHw_getClockFrequency (chipcHw_CLOCK_BUS);
471 					} else {
472 						uint32_t div = *pDependentClock & chipcHw_REG_DIV_CLOCK_DIV_MASK;
473 						sourceClock = chipcHw_divide (chipcHw_XTAL_FREQ_Hz, ((div) ? div : 256));
474 					}
475 					divider = chipcHw_divide(sourceClock, freq);
476 				}
477 				break;
478 			}
479 		} else {
480 			divider = chipcHw_divide(chipcHw_XTAL_FREQ_Hz, freq);
481 		}
482 
483 		if (divider) {
484 			REG_LOCAL_IRQ_SAVE;
485 			/* Set the divider to obtain the required frequency */
486 			*pClockCtrl = (*pClockCtrl & (~chipcHw_REG_DIV_CLOCK_DIV_MASK)) | (((divider > 256) ? chipcHw_REG_DIV_CLOCK_DIV_256 : divider) & chipcHw_REG_DIV_CLOCK_DIV_MASK);
487 			REG_LOCAL_IRQ_RESTORE;
488 			return freq;
489 		}
490 	}
491 
492 	return 0;
493 }
494 
495 EXPORT_SYMBOL(chipcHw_setClockFrequency);
496 
497 /****************************************************************************/
498 /**
499 *  @brief   Set VPM clock in sync with BUS clock for Chip Rev #A0
500 *
501 *  This function does the phase adjustment between VPM and BUS clock
502 *
503 *  @return >= 0 : On success (# of adjustment required)
504 *            -1 : On failure
505 *
506 */
507 /****************************************************************************/
vpmPhaseAlignA0(void)508 static int vpmPhaseAlignA0(void)
509 {
510 	uint32_t phaseControl;
511 	uint32_t phaseValue;
512 	uint32_t prevPhaseComp;
513 	int iter = 0;
514 	int adjustCount = 0;
515 	int count = 0;
516 
517 	for (iter = 0; (iter < MAX_PHASE_ALIGN_ATTEMPTS) && (adjustCount < MAX_PHASE_ADJUST_COUNT); iter++) {
518 		phaseControl = (pChipcHw->VPMClock & chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK) >> chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT;
519 		phaseValue = 0;
520 		prevPhaseComp = 0;
521 
522 		/* Step 1: Look for falling PH_COMP transition */
523 
524 		/* Read the contents of VPM Clock resgister */
525 		phaseValue = pChipcHw->VPMClock;
526 		do {
527 			/* Store previous value of phase comparator */
528 			prevPhaseComp = phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP;
529 			/* Change the value of PH_CTRL. */
530 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
531 			/* Wait atleast 20 ns */
532 			udelay(1);
533 			/* Toggle the LOAD_CH after phase control is written. */
534 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
535 			/* Read the contents of  VPM Clock resgister. */
536 			phaseValue = pChipcHw->VPMClock;
537 
538 			if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0x0) {
539 				phaseControl = (0x3F & (phaseControl - 1));
540 			} else {
541 				/* Increment to the Phase count value for next write, if Phase is not stable. */
542 				phaseControl = (0x3F & (phaseControl + 1));
543 			}
544 			/* Count number of adjustment made */
545 			adjustCount++;
546 		} while (((prevPhaseComp == (phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP)) ||	/* Look for a transition */
547 			  ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) != 0x0)) &&	/* Look for a falling edge */
548 			 (adjustCount < MAX_PHASE_ADJUST_COUNT)	/* Do not exceed the limit while trying */
549 		    );
550 
551 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
552 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
553 			return -1;
554 		}
555 
556 		/* Step 2: Keep moving forward to make sure falling PH_COMP transition was valid */
557 
558 		for (count = 0; (count < 5) && ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0); count++) {
559 			phaseControl = (0x3F & (phaseControl + 1));
560 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
561 			/* Wait atleast 20 ns */
562 			udelay(1);
563 			/* Toggle the LOAD_CH after phase control is written. */
564 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
565 			phaseValue = pChipcHw->VPMClock;
566 			/* Count number of adjustment made */
567 			adjustCount++;
568 		}
569 
570 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
571 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
572 			return -1;
573 		}
574 
575 		if (count != 5) {
576 			/* Detected false transition */
577 			continue;
578 		}
579 
580 		/* Step 3: Keep moving backward to make sure falling PH_COMP transition was stable */
581 
582 		for (count = 0; (count < 3) && ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0); count++) {
583 			phaseControl = (0x3F & (phaseControl - 1));
584 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
585 			/* Wait atleast 20 ns */
586 			udelay(1);
587 			/* Toggle the LOAD_CH after phase control is written. */
588 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
589 			phaseValue = pChipcHw->VPMClock;
590 			/* Count number of adjustment made */
591 			adjustCount++;
592 		}
593 
594 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
595 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
596 			return -1;
597 		}
598 
599 		if (count != 3) {
600 			/* Detected noisy transition */
601 			continue;
602 		}
603 
604 		/* Step 4: Keep moving backward before the original transition took place. */
605 
606 		for (count = 0; (count < 5); count++) {
607 			phaseControl = (0x3F & (phaseControl - 1));
608 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
609 			/* Wait atleast 20 ns */
610 			udelay(1);
611 			/* Toggle the LOAD_CH after phase control is written. */
612 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
613 			phaseValue = pChipcHw->VPMClock;
614 			/* Count number of adjustment made */
615 			adjustCount++;
616 		}
617 
618 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
619 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
620 			return -1;
621 		}
622 
623 		if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0) {
624 			/* Detected false transition */
625 			continue;
626 		}
627 
628 		/* Step 5: Re discover the valid transition */
629 
630 		do {
631 			/* Store previous value of phase comparator */
632 			prevPhaseComp = phaseValue;
633 			/* Change the value of PH_CTRL. */
634 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
635 			/* Wait atleast 20 ns */
636 			udelay(1);
637 			/* Toggle the LOAD_CH after phase control is written. */
638 			pChipcHw->VPMClock ^=
639 			    chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
640 			/* Read the contents of  VPM Clock resgister. */
641 			phaseValue = pChipcHw->VPMClock;
642 
643 			if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0x0) {
644 				phaseControl = (0x3F & (phaseControl - 1));
645 			} else {
646 				/* Increment to the Phase count value for next write, if Phase is not stable. */
647 				phaseControl = (0x3F & (phaseControl + 1));
648 			}
649 
650 			/* Count number of adjustment made */
651 			adjustCount++;
652 		} while (((prevPhaseComp == (phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP)) || ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) != 0x0)) && (adjustCount < MAX_PHASE_ADJUST_COUNT));
653 
654 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
655 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries  */
656 			return -1;
657 		} else {
658 			/* Valid phase must have detected */
659 			break;
660 		}
661 	}
662 
663 	/* For VPM Phase should be perfectly aligned. */
664 	phaseControl = (((pChipcHw->VPMClock >> chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT) - 1) & 0x3F);
665 	{
666 		REG_LOCAL_IRQ_SAVE;
667 
668 		pChipcHw->VPMClock = (pChipcHw->VPMClock & ~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT);
669 		/* Load new phase value */
670 		pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
671 
672 		REG_LOCAL_IRQ_RESTORE;
673 	}
674 	/* Return the status */
675 	return (int)adjustCount;
676 }
677 
678 /****************************************************************************/
679 /**
680 *  @brief   Set VPM clock in sync with BUS clock
681 *
682 *  This function does the phase adjustment between VPM and BUS clock
683 *
684 *  @return >= 0 : On success (# of adjustment required)
685 *            -1 : On failure
686 *
687 */
688 /****************************************************************************/
chipcHw_vpmPhaseAlign(void)689 int chipcHw_vpmPhaseAlign(void)
690 {
691 
692 	if (chipcHw_getChipRevisionNumber() == chipcHw_REV_NUMBER_A0) {
693 		return vpmPhaseAlignA0();
694 	} else {
695 		uint32_t phaseControl = chipcHw_getVpmPhaseControl();
696 		uint32_t phaseValue = 0;
697 		int adjustCount = 0;
698 
699 		/* Disable VPM access */
700 		pChipcHw->Spare1 &= ~chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
701 		/* Disable HW VPM phase alignment  */
702 		chipcHw_vpmHwPhaseAlignDisable();
703 		/* Enable SW VPM phase alignment  */
704 		chipcHw_vpmSwPhaseAlignEnable();
705 		/* Adjust VPM phase */
706 		while (adjustCount < MAX_PHASE_ADJUST_COUNT) {
707 			phaseValue = chipcHw_getVpmHwPhaseAlignStatus();
708 
709 			/* Adjust phase control value */
710 			if (phaseValue > 0xF) {
711 				/* Increment phase control value */
712 				phaseControl++;
713 			} else if (phaseValue < 0xF) {
714 				/* Decrement phase control value */
715 				phaseControl--;
716 			} else {
717 				/* Enable VPM access */
718 				pChipcHw->Spare1 |= chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
719 				/* Return adjust count */
720 				return adjustCount;
721 			}
722 			/* Change the value of PH_CTRL. */
723 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
724 			/* Wait atleast 20 ns */
725 			udelay(1);
726 			/* Toggle the LOAD_CH after phase control is written. */
727 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
728 			/* Count adjustment */
729 			adjustCount++;
730 		}
731 	}
732 
733 	/* Disable VPM access */
734 	pChipcHw->Spare1 &= ~chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
735 	return -1;
736 }
737 
738 /****************************************************************************/
739 /**
740 *  @brief   Local Divide function
741 *
742 *  This function does the divide
743 *
744 *  @return divide value
745 *
746 */
747 /****************************************************************************/
chipcHw_divide(int num,int denom)748 static int chipcHw_divide(int num, int denom)
749 {
750 	int r;
751 	int t = 1;
752 
753 	/* Shift denom and t up to the largest value to optimize algorithm */
754 	/* t contains the units of each divide */
755 	while ((denom & 0x40000000) == 0) {	/* fails if denom=0 */
756 		denom = denom << 1;
757 		t = t << 1;
758 	}
759 
760 	/* Initialize the result */
761 	r = 0;
762 
763 	do {
764 		/* Determine if there exists a positive remainder */
765 		if ((num - denom) >= 0) {
766 			/* Accumlate t to the result and calculate a new remainder */
767 			num = num - denom;
768 			r = r + t;
769 		}
770 		/* Continue to shift denom and shift t down to 0 */
771 		denom = denom >> 1;
772 		t = t >> 1;
773 	} while (t != 0);
774 
775 	return r;
776 }
777