777 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			777 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*****************************************************************************
 | |
| * Copyright 2003 - 2008 Broadcom Corporation.  All rights reserved.
 | |
| *
 | |
| * Unless you and Broadcom execute a separate written software license
 | |
| * agreement governing use of this software, this software is licensed to you
 | |
| * under the terms of the GNU General Public License version 2, available at
 | |
| * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
 | |
| *
 | |
| * Notwithstanding the above, under no circumstances may you combine this
 | |
| * software in any way with any other Broadcom software provided under a
 | |
| * license other than the GPL, without Broadcom's express prior written
 | |
| * consent.
 | |
| *****************************************************************************/
 | |
| 
 | |
| /****************************************************************************/
 | |
| /**
 | |
| *  @file    chipcHw.c
 | |
| *
 | |
| *  @brief   Low level Various CHIP clock controlling routines
 | |
| *
 | |
| *  @note
 | |
| *
 | |
| *   These routines provide basic clock controlling functionality only.
 | |
| */
 | |
| /****************************************************************************/
 | |
| 
 | |
| /* ---- Include Files ---------------------------------------------------- */
 | |
| 
 | |
| #include <csp/errno.h>
 | |
| #include <csp/stdint.h>
 | |
| #include <csp/module.h>
 | |
| 
 | |
| #include <mach/csp/chipcHw_def.h>
 | |
| #include <mach/csp/chipcHw_inline.h>
 | |
| 
 | |
| #include <csp/reg.h>
 | |
| #include <csp/delay.h>
 | |
| 
 | |
| /* ---- Private Constants and Types --------------------------------------- */
 | |
| 
 | |
| /* VPM alignment algorithm uses this */
 | |
| #define MAX_PHASE_ADJUST_COUNT         0xFFFF	/* Max number of times allowed to adjust the phase */
 | |
| #define MAX_PHASE_ALIGN_ATTEMPTS       10	/* Max number of attempt to align the phase */
 | |
| 
 | |
| /* Local definition of clock type */
 | |
| #define PLL_CLOCK                      1	/* PLL Clock */
 | |
| #define NON_PLL_CLOCK                  2	/* Divider clock */
 | |
| 
 | |
| static int chipcHw_divide(int num, int denom)
 | |
|     __attribute__ ((section(".aramtext")));
 | |
| 
 | |
| /****************************************************************************/
 | |
| /**
 | |
| *  @brief   Set clock fequency for miscellaneous configurable clocks
 | |
| *
 | |
| *  This function sets clock frequency
 | |
| *
 | |
| *  @return  Configured clock frequency in hertz
 | |
| *
 | |
| */
 | |
| /****************************************************************************/
 | |
| chipcHw_freq chipcHw_getClockFrequency(chipcHw_CLOCK_e clock	/*  [ IN ] Configurable clock */
 | |
|     ) {
 | |
| 	volatile uint32_t *pPLLReg = (uint32_t *) 0x0;
 | |
| 	volatile uint32_t *pClockCtrl = (uint32_t *) 0x0;
 | |
| 	volatile uint32_t *pDependentClock = (uint32_t *) 0x0;
 | |
| 	uint32_t vcoFreqPll1Hz = 0;	/* Effective VCO frequency for PLL1 in Hz */
 | |
| 	uint32_t vcoFreqPll2Hz = 0;	/* Effective VCO frequency for PLL2 in Hz */
 | |
| 	uint32_t dependentClockType = 0;
 | |
| 	uint32_t vcoHz = 0;
 | |
| 
 | |
| 	/* Get VCO frequencies */
 | |
| 	if ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASK) != chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER) {
 | |
| 		uint64_t adjustFreq = 0;
 | |
| 
 | |
| 		vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
 | |
| 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
 | |
| 		    ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
 | |
| 		     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
 | |
| 
 | |
| 		/* Adjusted frequency due to chipcHw_REG_PLL_DIVIDER_NDIV_f_SS */
 | |
| 		adjustFreq = (uint64_t) chipcHw_XTAL_FREQ_Hz *
 | |
| 			(uint64_t) chipcHw_REG_PLL_DIVIDER_NDIV_f_SS *
 | |
| 			chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, (chipcHw_REG_PLL_PREDIVIDER_P2 * (uint64_t) chipcHw_REG_PLL_DIVIDER_FRAC));
 | |
| 		vcoFreqPll1Hz += (uint32_t) adjustFreq;
 | |
| 	} else {
 | |
| 		vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
 | |
| 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
 | |
| 		    ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
 | |
| 		     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
 | |
| 	}
 | |
| 	vcoFreqPll2Hz =
 | |
| 	    chipcHw_XTAL_FREQ_Hz *
 | |
| 		 chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
 | |
| 	    ((pChipcHw->PLLPreDivider2 & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
 | |
| 	     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
 | |
| 
 | |
| 	switch (clock) {
 | |
| 	case chipcHw_CLOCK_DDR:
 | |
| 		pPLLReg = &pChipcHw->DDRClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_ARM:
 | |
| 		pPLLReg = &pChipcHw->ARMClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_ESW:
 | |
| 		pPLLReg = &pChipcHw->ESWClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_VPM:
 | |
| 		pPLLReg = &pChipcHw->VPMClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_ESW125:
 | |
| 		pPLLReg = &pChipcHw->ESW125Clock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_UART:
 | |
| 		pPLLReg = &pChipcHw->UARTClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_SDIO0:
 | |
| 		pPLLReg = &pChipcHw->SDIO0Clock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_SDIO1:
 | |
| 		pPLLReg = &pChipcHw->SDIO1Clock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_SPI:
 | |
| 		pPLLReg = &pChipcHw->SPIClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_ETM:
 | |
| 		pPLLReg = &pChipcHw->ETMClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_USB:
 | |
| 		pPLLReg = &pChipcHw->USBClock;
 | |
| 		vcoHz = vcoFreqPll2Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_LCD:
 | |
| 		pPLLReg = &pChipcHw->LCDClock;
 | |
| 		vcoHz = vcoFreqPll2Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_APM:
 | |
| 		pPLLReg = &pChipcHw->APMClock;
 | |
| 		vcoHz = vcoFreqPll2Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_BUS:
 | |
| 		pClockCtrl = &pChipcHw->ACLKClock;
 | |
| 		pDependentClock = &pChipcHw->ARMClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		dependentClockType = PLL_CLOCK;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_OTP:
 | |
| 		pClockCtrl = &pChipcHw->OTPClock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_I2C:
 | |
| 		pClockCtrl = &pChipcHw->I2CClock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_I2S0:
 | |
| 		pClockCtrl = &pChipcHw->I2S0Clock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_RTBUS:
 | |
| 		pClockCtrl = &pChipcHw->RTBUSClock;
 | |
| 		pDependentClock = &pChipcHw->ACLKClock;
 | |
| 		dependentClockType = NON_PLL_CLOCK;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_APM100:
 | |
| 		pClockCtrl = &pChipcHw->APM100Clock;
 | |
| 		pDependentClock = &pChipcHw->APMClock;
 | |
| 		vcoHz = vcoFreqPll2Hz;
 | |
| 		dependentClockType = PLL_CLOCK;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_TSC:
 | |
| 		pClockCtrl = &pChipcHw->TSCClock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_LED:
 | |
| 		pClockCtrl = &pChipcHw->LEDClock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_I2S1:
 | |
| 		pClockCtrl = &pChipcHw->I2S1Clock;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (pPLLReg) {
 | |
| 		/* Obtain PLL clock frequency */
 | |
| 		if (*pPLLReg & chipcHw_REG_PLL_CLOCK_BYPASS_SELECT) {
 | |
| 			/* Return crystal clock frequency when bypassed */
 | |
| 			return chipcHw_XTAL_FREQ_Hz;
 | |
| 		} else if (clock == chipcHw_CLOCK_DDR) {
 | |
| 			/* DDR frequency is configured in PLLDivider register */
 | |
| 			return chipcHw_divide (vcoHz, (((pChipcHw->PLLDivider & 0xFF000000) >> 24) ? ((pChipcHw->PLLDivider & 0xFF000000) >> 24) : 256));
 | |
| 		} else {
 | |
| 			/* From chip revision number B0, LCD clock is internally divided by 2 */
 | |
| 			if ((pPLLReg == &pChipcHw->LCDClock) && (chipcHw_getChipRevisionNumber() != chipcHw_REV_NUMBER_A0)) {
 | |
| 				vcoHz >>= 1;
 | |
| 			}
 | |
| 			/* Obtain PLL clock frequency using VCO dividers */
 | |
| 			return chipcHw_divide(vcoHz, ((*pPLLReg & chipcHw_REG_PLL_CLOCK_MDIV_MASK) ? (*pPLLReg & chipcHw_REG_PLL_CLOCK_MDIV_MASK) : 256));
 | |
| 		}
 | |
| 	} else if (pClockCtrl) {
 | |
| 		/* Obtain divider clock frequency */
 | |
| 		uint32_t div;
 | |
| 		uint32_t freq = 0;
 | |
| 
 | |
| 		if (*pClockCtrl & chipcHw_REG_DIV_CLOCK_BYPASS_SELECT) {
 | |
| 			/* Return crystal clock frequency when bypassed */
 | |
| 			return chipcHw_XTAL_FREQ_Hz;
 | |
| 		} else if (pDependentClock) {
 | |
| 			/* Identify the dependent clock frequency */
 | |
| 			switch (dependentClockType) {
 | |
| 			case PLL_CLOCK:
 | |
| 				if (*pDependentClock & chipcHw_REG_PLL_CLOCK_BYPASS_SELECT) {
 | |
| 					/* Use crystal clock frequency when dependent PLL clock is bypassed */
 | |
| 					freq = chipcHw_XTAL_FREQ_Hz;
 | |
| 				} else {
 | |
| 					/* Obtain PLL clock frequency using VCO dividers */
 | |
| 					div = *pDependentClock & chipcHw_REG_PLL_CLOCK_MDIV_MASK;
 | |
| 					freq = div ? chipcHw_divide(vcoHz, div) : 0;
 | |
| 				}
 | |
| 				break;
 | |
| 			case NON_PLL_CLOCK:
 | |
| 				if (pDependentClock == (uint32_t *) &pChipcHw->ACLKClock) {
 | |
| 					freq = chipcHw_getClockFrequency (chipcHw_CLOCK_BUS);
 | |
| 				} else {
 | |
| 					if (*pDependentClock & chipcHw_REG_DIV_CLOCK_BYPASS_SELECT) {
 | |
| 						/* Use crystal clock frequency when dependent divider clock is bypassed */
 | |
| 						freq = chipcHw_XTAL_FREQ_Hz;
 | |
| 					} else {
 | |
| 						/* Obtain divider clock frequency using XTAL dividers */
 | |
| 						div = *pDependentClock & chipcHw_REG_DIV_CLOCK_DIV_MASK;
 | |
| 						freq = chipcHw_divide (chipcHw_XTAL_FREQ_Hz, (div ? div : 256));
 | |
| 					}
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 		} else {
 | |
| 			/* Dependent on crystal clock */
 | |
| 			freq = chipcHw_XTAL_FREQ_Hz;
 | |
| 		}
 | |
| 
 | |
| 		div = *pClockCtrl & chipcHw_REG_DIV_CLOCK_DIV_MASK;
 | |
| 		return chipcHw_divide(freq, (div ? div : 256));
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /****************************************************************************/
 | |
| /**
 | |
| *  @brief   Set clock fequency for miscellaneous configurable clocks
 | |
| *
 | |
| *  This function sets clock frequency
 | |
| *
 | |
| *  @return  Configured clock frequency in Hz
 | |
| *
 | |
| */
 | |
| /****************************************************************************/
 | |
| chipcHw_freq chipcHw_setClockFrequency(chipcHw_CLOCK_e clock,	/*  [ IN ] Configurable clock */
 | |
| 				       uint32_t freq	/*  [ IN ] Clock frequency in Hz */
 | |
|     ) {
 | |
| 	volatile uint32_t *pPLLReg = (uint32_t *) 0x0;
 | |
| 	volatile uint32_t *pClockCtrl = (uint32_t *) 0x0;
 | |
| 	volatile uint32_t *pDependentClock = (uint32_t *) 0x0;
 | |
| 	uint32_t vcoFreqPll1Hz = 0;	/* Effective VCO frequency for PLL1 in Hz */
 | |
| 	uint32_t desVcoFreqPll1Hz = 0;	/* Desired VCO frequency for PLL1 in Hz */
 | |
| 	uint32_t vcoFreqPll2Hz = 0;	/* Effective VCO frequency for PLL2 in Hz */
 | |
| 	uint32_t dependentClockType = 0;
 | |
| 	uint32_t vcoHz = 0;
 | |
| 	uint32_t desVcoHz = 0;
 | |
| 
 | |
| 	/* Get VCO frequencies */
 | |
| 	if ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASK) != chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER) {
 | |
| 		uint64_t adjustFreq = 0;
 | |
| 
 | |
| 		vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
 | |
| 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
 | |
| 		    ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
 | |
| 		     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
 | |
| 
 | |
| 		/* Adjusted frequency due to chipcHw_REG_PLL_DIVIDER_NDIV_f_SS */
 | |
| 		adjustFreq = (uint64_t) chipcHw_XTAL_FREQ_Hz *
 | |
| 			(uint64_t) chipcHw_REG_PLL_DIVIDER_NDIV_f_SS *
 | |
| 			chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, (chipcHw_REG_PLL_PREDIVIDER_P2 * (uint64_t) chipcHw_REG_PLL_DIVIDER_FRAC));
 | |
| 		vcoFreqPll1Hz += (uint32_t) adjustFreq;
 | |
| 
 | |
| 		/* Desired VCO frequency */
 | |
| 		desVcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
 | |
| 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
 | |
| 		    (((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
 | |
| 		      chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) + 1);
 | |
| 	} else {
 | |
| 		vcoFreqPll1Hz = desVcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
 | |
| 		    chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
 | |
| 		    ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
 | |
| 		     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
 | |
| 	}
 | |
| 	vcoFreqPll2Hz = chipcHw_XTAL_FREQ_Hz * chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
 | |
| 	    ((pChipcHw->PLLPreDivider2 & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
 | |
| 	     chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
 | |
| 
 | |
| 	switch (clock) {
 | |
| 	case chipcHw_CLOCK_DDR:
 | |
| 		/* Configure the DDR_ctrl:BUS ratio settings */
 | |
| 		{
 | |
| 			REG_LOCAL_IRQ_SAVE;
 | |
| 			/* Dvide DDR_phy by two to obtain DDR_ctrl clock */
 | |
| 			pChipcHw->DDRClock = (pChipcHw->DDRClock & ~chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_MASK) | ((((freq / 2) / chipcHw_getClockFrequency(chipcHw_CLOCK_BUS)) - 1)
 | |
| 				<< chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_SHIFT);
 | |
| 			REG_LOCAL_IRQ_RESTORE;
 | |
| 		}
 | |
| 		pPLLReg = &pChipcHw->DDRClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_ARM:
 | |
| 		pPLLReg = &pChipcHw->ARMClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_ESW:
 | |
| 		pPLLReg = &pChipcHw->ESWClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_VPM:
 | |
| 		/* Configure the VPM:BUS ratio settings */
 | |
| 		{
 | |
| 			REG_LOCAL_IRQ_SAVE;
 | |
| 			pChipcHw->VPMClock = (pChipcHw->VPMClock & ~chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_MASK) | ((chipcHw_divide (freq, chipcHw_getClockFrequency(chipcHw_CLOCK_BUS)) - 1)
 | |
| 				<< chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_SHIFT);
 | |
| 			REG_LOCAL_IRQ_RESTORE;
 | |
| 		}
 | |
| 		pPLLReg = &pChipcHw->VPMClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_ESW125:
 | |
| 		pPLLReg = &pChipcHw->ESW125Clock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_UART:
 | |
| 		pPLLReg = &pChipcHw->UARTClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_SDIO0:
 | |
| 		pPLLReg = &pChipcHw->SDIO0Clock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_SDIO1:
 | |
| 		pPLLReg = &pChipcHw->SDIO1Clock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_SPI:
 | |
| 		pPLLReg = &pChipcHw->SPIClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_ETM:
 | |
| 		pPLLReg = &pChipcHw->ETMClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_USB:
 | |
| 		pPLLReg = &pChipcHw->USBClock;
 | |
| 		vcoHz = vcoFreqPll2Hz;
 | |
| 		desVcoHz = vcoFreqPll2Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_LCD:
 | |
| 		pPLLReg = &pChipcHw->LCDClock;
 | |
| 		vcoHz = vcoFreqPll2Hz;
 | |
| 		desVcoHz = vcoFreqPll2Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_APM:
 | |
| 		pPLLReg = &pChipcHw->APMClock;
 | |
| 		vcoHz = vcoFreqPll2Hz;
 | |
| 		desVcoHz = vcoFreqPll2Hz;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_BUS:
 | |
| 		pClockCtrl = &pChipcHw->ACLKClock;
 | |
| 		pDependentClock = &pChipcHw->ARMClock;
 | |
| 		vcoHz = vcoFreqPll1Hz;
 | |
| 		desVcoHz = desVcoFreqPll1Hz;
 | |
| 		dependentClockType = PLL_CLOCK;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_OTP:
 | |
| 		pClockCtrl = &pChipcHw->OTPClock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_I2C:
 | |
| 		pClockCtrl = &pChipcHw->I2CClock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_I2S0:
 | |
| 		pClockCtrl = &pChipcHw->I2S0Clock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_RTBUS:
 | |
| 		pClockCtrl = &pChipcHw->RTBUSClock;
 | |
| 		pDependentClock = &pChipcHw->ACLKClock;
 | |
| 		dependentClockType = NON_PLL_CLOCK;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_APM100:
 | |
| 		pClockCtrl = &pChipcHw->APM100Clock;
 | |
| 		pDependentClock = &pChipcHw->APMClock;
 | |
| 		vcoHz = vcoFreqPll2Hz;
 | |
| 		desVcoHz = vcoFreqPll2Hz;
 | |
| 		dependentClockType = PLL_CLOCK;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_TSC:
 | |
| 		pClockCtrl = &pChipcHw->TSCClock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_LED:
 | |
| 		pClockCtrl = &pChipcHw->LEDClock;
 | |
| 		break;
 | |
| 	case chipcHw_CLOCK_I2S1:
 | |
| 		pClockCtrl = &pChipcHw->I2S1Clock;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (pPLLReg) {
 | |
| 		/* Select XTAL as bypass source */
 | |
| 		reg32_modify_and(pPLLReg, ~chipcHw_REG_PLL_CLOCK_SOURCE_GPIO);
 | |
| 		reg32_modify_or(pPLLReg, chipcHw_REG_PLL_CLOCK_BYPASS_SELECT);
 | |
| 		/* For DDR settings use only the PLL divider clock */
 | |
| 		if (pPLLReg == &pChipcHw->DDRClock) {
 | |
| 			/* Set M1DIV for PLL1, which controls the DDR clock */
 | |
| 			reg32_write(&pChipcHw->PLLDivider, (pChipcHw->PLLDivider & 0x00FFFFFF) | ((chipcHw_REG_PLL_DIVIDER_MDIV (desVcoHz, freq)) << 24));
 | |
| 			/* Calculate expected frequency */
 | |
| 			freq = chipcHw_divide(vcoHz, (((pChipcHw->PLLDivider & 0xFF000000) >> 24) ? ((pChipcHw->PLLDivider & 0xFF000000) >> 24) : 256));
 | |
| 		} else {
 | |
| 			/* From chip revision number B0, LCD clock is internally divided by 2 */
 | |
| 			if ((pPLLReg == &pChipcHw->LCDClock) && (chipcHw_getChipRevisionNumber() != chipcHw_REV_NUMBER_A0)) {
 | |
| 				desVcoHz >>= 1;
 | |
| 				vcoHz >>= 1;
 | |
| 			}
 | |
| 			/* Set MDIV to change the frequency */
 | |
| 			reg32_modify_and(pPLLReg, ~(chipcHw_REG_PLL_CLOCK_MDIV_MASK));
 | |
| 			reg32_modify_or(pPLLReg, chipcHw_REG_PLL_DIVIDER_MDIV(desVcoHz, freq));
 | |
| 			/* Calculate expected frequency */
 | |
| 			freq = chipcHw_divide(vcoHz, ((*(pPLLReg) & chipcHw_REG_PLL_CLOCK_MDIV_MASK) ? (*(pPLLReg) & chipcHw_REG_PLL_CLOCK_MDIV_MASK) : 256));
 | |
| 		}
 | |
| 		/* Wait for for atleast 200ns as per the protocol to change frequency */
 | |
| 		udelay(1);
 | |
| 		/* Do not bypass */
 | |
| 		reg32_modify_and(pPLLReg, ~chipcHw_REG_PLL_CLOCK_BYPASS_SELECT);
 | |
| 		/* Return the configured frequency */
 | |
| 		return freq;
 | |
| 	} else if (pClockCtrl) {
 | |
| 		uint32_t divider = 0;
 | |
| 
 | |
| 		/* Divider clock should not be bypassed  */
 | |
| 		reg32_modify_and(pClockCtrl,
 | |
| 				 ~chipcHw_REG_DIV_CLOCK_BYPASS_SELECT);
 | |
| 
 | |
| 		/* Identify the clock source */
 | |
| 		if (pDependentClock) {
 | |
| 			switch (dependentClockType) {
 | |
| 			case PLL_CLOCK:
 | |
| 				divider = chipcHw_divide(chipcHw_divide (desVcoHz, (*pDependentClock & chipcHw_REG_PLL_CLOCK_MDIV_MASK)), freq);
 | |
| 				break;
 | |
| 			case NON_PLL_CLOCK:
 | |
| 				{
 | |
| 					uint32_t sourceClock = 0;
 | |
| 
 | |
| 					if (pDependentClock == (uint32_t *) &pChipcHw->ACLKClock) {
 | |
| 						sourceClock = chipcHw_getClockFrequency (chipcHw_CLOCK_BUS);
 | |
| 					} else {
 | |
| 						uint32_t div = *pDependentClock & chipcHw_REG_DIV_CLOCK_DIV_MASK;
 | |
| 						sourceClock = chipcHw_divide (chipcHw_XTAL_FREQ_Hz, ((div) ? div : 256));
 | |
| 					}
 | |
| 					divider = chipcHw_divide(sourceClock, freq);
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 		} else {
 | |
| 			divider = chipcHw_divide(chipcHw_XTAL_FREQ_Hz, freq);
 | |
| 		}
 | |
| 
 | |
| 		if (divider) {
 | |
| 			REG_LOCAL_IRQ_SAVE;
 | |
| 			/* Set the divider to obtain the required frequency */
 | |
| 			*pClockCtrl = (*pClockCtrl & (~chipcHw_REG_DIV_CLOCK_DIV_MASK)) | (((divider > 256) ? chipcHw_REG_DIV_CLOCK_DIV_256 : divider) & chipcHw_REG_DIV_CLOCK_DIV_MASK);
 | |
| 			REG_LOCAL_IRQ_RESTORE;
 | |
| 			return freq;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| EXPORT_SYMBOL(chipcHw_setClockFrequency);
 | |
| 
 | |
| /****************************************************************************/
 | |
| /**
 | |
| *  @brief   Set VPM clock in sync with BUS clock for Chip Rev #A0
 | |
| *
 | |
| *  This function does the phase adjustment between VPM and BUS clock
 | |
| *
 | |
| *  @return >= 0 : On success (# of adjustment required)
 | |
| *            -1 : On failure
 | |
| *
 | |
| */
 | |
| /****************************************************************************/
 | |
| static int vpmPhaseAlignA0(void)
 | |
| {
 | |
| 	uint32_t phaseControl;
 | |
| 	uint32_t phaseValue;
 | |
| 	uint32_t prevPhaseComp;
 | |
| 	int iter = 0;
 | |
| 	int adjustCount = 0;
 | |
| 	int count = 0;
 | |
| 
 | |
| 	for (iter = 0; (iter < MAX_PHASE_ALIGN_ATTEMPTS) && (adjustCount < MAX_PHASE_ADJUST_COUNT); iter++) {
 | |
| 		phaseControl = (pChipcHw->VPMClock & chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK) >> chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT;
 | |
| 		phaseValue = 0;
 | |
| 		prevPhaseComp = 0;
 | |
| 
 | |
| 		/* Step 1: Look for falling PH_COMP transition */
 | |
| 
 | |
| 		/* Read the contents of VPM Clock resgister */
 | |
| 		phaseValue = pChipcHw->VPMClock;
 | |
| 		do {
 | |
| 			/* Store previous value of phase comparator */
 | |
| 			prevPhaseComp = phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP;
 | |
| 			/* Change the value of PH_CTRL. */
 | |
| 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
 | |
| 			/* Wait atleast 20 ns */
 | |
| 			udelay(1);
 | |
| 			/* Toggle the LOAD_CH after phase control is written. */
 | |
| 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
 | |
| 			/* Read the contents of  VPM Clock resgister. */
 | |
| 			phaseValue = pChipcHw->VPMClock;
 | |
| 
 | |
| 			if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0x0) {
 | |
| 				phaseControl = (0x3F & (phaseControl - 1));
 | |
| 			} else {
 | |
| 				/* Increment to the Phase count value for next write, if Phase is not stable. */
 | |
| 				phaseControl = (0x3F & (phaseControl + 1));
 | |
| 			}
 | |
| 			/* Count number of adjustment made */
 | |
| 			adjustCount++;
 | |
| 		} while (((prevPhaseComp == (phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP)) ||	/* Look for a transition */
 | |
| 			  ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) != 0x0)) &&	/* Look for a falling edge */
 | |
| 			 (adjustCount < MAX_PHASE_ADJUST_COUNT)	/* Do not exceed the limit while trying */
 | |
| 		    );
 | |
| 
 | |
| 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
 | |
| 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		/* Step 2: Keep moving forward to make sure falling PH_COMP transition was valid */
 | |
| 
 | |
| 		for (count = 0; (count < 5) && ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0); count++) {
 | |
| 			phaseControl = (0x3F & (phaseControl + 1));
 | |
| 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
 | |
| 			/* Wait atleast 20 ns */
 | |
| 			udelay(1);
 | |
| 			/* Toggle the LOAD_CH after phase control is written. */
 | |
| 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
 | |
| 			phaseValue = pChipcHw->VPMClock;
 | |
| 			/* Count number of adjustment made */
 | |
| 			adjustCount++;
 | |
| 		}
 | |
| 
 | |
| 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
 | |
| 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		if (count != 5) {
 | |
| 			/* Detected false transition */
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Step 3: Keep moving backward to make sure falling PH_COMP transition was stable */
 | |
| 
 | |
| 		for (count = 0; (count < 3) && ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0); count++) {
 | |
| 			phaseControl = (0x3F & (phaseControl - 1));
 | |
| 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
 | |
| 			/* Wait atleast 20 ns */
 | |
| 			udelay(1);
 | |
| 			/* Toggle the LOAD_CH after phase control is written. */
 | |
| 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
 | |
| 			phaseValue = pChipcHw->VPMClock;
 | |
| 			/* Count number of adjustment made */
 | |
| 			adjustCount++;
 | |
| 		}
 | |
| 
 | |
| 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
 | |
| 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		if (count != 3) {
 | |
| 			/* Detected noisy transition */
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Step 4: Keep moving backward before the original transition took place. */
 | |
| 
 | |
| 		for (count = 0; (count < 5); count++) {
 | |
| 			phaseControl = (0x3F & (phaseControl - 1));
 | |
| 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
 | |
| 			/* Wait atleast 20 ns */
 | |
| 			udelay(1);
 | |
| 			/* Toggle the LOAD_CH after phase control is written. */
 | |
| 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
 | |
| 			phaseValue = pChipcHw->VPMClock;
 | |
| 			/* Count number of adjustment made */
 | |
| 			adjustCount++;
 | |
| 		}
 | |
| 
 | |
| 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
 | |
| 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0) {
 | |
| 			/* Detected false transition */
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Step 5: Re discover the valid transition */
 | |
| 
 | |
| 		do {
 | |
| 			/* Store previous value of phase comparator */
 | |
| 			prevPhaseComp = phaseValue;
 | |
| 			/* Change the value of PH_CTRL. */
 | |
| 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
 | |
| 			/* Wait atleast 20 ns */
 | |
| 			udelay(1);
 | |
| 			/* Toggle the LOAD_CH after phase control is written. */
 | |
| 			pChipcHw->VPMClock ^=
 | |
| 			    chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
 | |
| 			/* Read the contents of  VPM Clock resgister. */
 | |
| 			phaseValue = pChipcHw->VPMClock;
 | |
| 
 | |
| 			if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0x0) {
 | |
| 				phaseControl = (0x3F & (phaseControl - 1));
 | |
| 			} else {
 | |
| 				/* Increment to the Phase count value for next write, if Phase is not stable. */
 | |
| 				phaseControl = (0x3F & (phaseControl + 1));
 | |
| 			}
 | |
| 
 | |
| 			/* Count number of adjustment made */
 | |
| 			adjustCount++;
 | |
| 		} while (((prevPhaseComp == (phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP)) || ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) != 0x0)) && (adjustCount < MAX_PHASE_ADJUST_COUNT));
 | |
| 
 | |
| 		if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
 | |
| 			/* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries  */
 | |
| 			return -1;
 | |
| 		} else {
 | |
| 			/* Valid phase must have detected */
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* For VPM Phase should be perfectly aligned. */
 | |
| 	phaseControl = (((pChipcHw->VPMClock >> chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT) - 1) & 0x3F);
 | |
| 	{
 | |
| 		REG_LOCAL_IRQ_SAVE;
 | |
| 
 | |
| 		pChipcHw->VPMClock = (pChipcHw->VPMClock & ~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT);
 | |
| 		/* Load new phase value */
 | |
| 		pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
 | |
| 
 | |
| 		REG_LOCAL_IRQ_RESTORE;
 | |
| 	}
 | |
| 	/* Return the status */
 | |
| 	return (int)adjustCount;
 | |
| }
 | |
| 
 | |
| /****************************************************************************/
 | |
| /**
 | |
| *  @brief   Set VPM clock in sync with BUS clock
 | |
| *
 | |
| *  This function does the phase adjustment between VPM and BUS clock
 | |
| *
 | |
| *  @return >= 0 : On success (# of adjustment required)
 | |
| *            -1 : On failure
 | |
| *
 | |
| */
 | |
| /****************************************************************************/
 | |
| int chipcHw_vpmPhaseAlign(void)
 | |
| {
 | |
| 
 | |
| 	if (chipcHw_getChipRevisionNumber() == chipcHw_REV_NUMBER_A0) {
 | |
| 		return vpmPhaseAlignA0();
 | |
| 	} else {
 | |
| 		uint32_t phaseControl = chipcHw_getVpmPhaseControl();
 | |
| 		uint32_t phaseValue = 0;
 | |
| 		int adjustCount = 0;
 | |
| 
 | |
| 		/* Disable VPM access */
 | |
| 		pChipcHw->Spare1 &= ~chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
 | |
| 		/* Disable HW VPM phase alignment  */
 | |
| 		chipcHw_vpmHwPhaseAlignDisable();
 | |
| 		/* Enable SW VPM phase alignment  */
 | |
| 		chipcHw_vpmSwPhaseAlignEnable();
 | |
| 		/* Adjust VPM phase */
 | |
| 		while (adjustCount < MAX_PHASE_ADJUST_COUNT) {
 | |
| 			phaseValue = chipcHw_getVpmHwPhaseAlignStatus();
 | |
| 
 | |
| 			/* Adjust phase control value */
 | |
| 			if (phaseValue > 0xF) {
 | |
| 				/* Increment phase control value */
 | |
| 				phaseControl++;
 | |
| 			} else if (phaseValue < 0xF) {
 | |
| 				/* Decrement phase control value */
 | |
| 				phaseControl--;
 | |
| 			} else {
 | |
| 				/* Enable VPM access */
 | |
| 				pChipcHw->Spare1 |= chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
 | |
| 				/* Return adjust count */
 | |
| 				return adjustCount;
 | |
| 			}
 | |
| 			/* Change the value of PH_CTRL. */
 | |
| 			reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
 | |
| 			/* Wait atleast 20 ns */
 | |
| 			udelay(1);
 | |
| 			/* Toggle the LOAD_CH after phase control is written. */
 | |
| 			pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
 | |
| 			/* Count adjustment */
 | |
| 			adjustCount++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Disable VPM access */
 | |
| 	pChipcHw->Spare1 &= ~chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| /****************************************************************************/
 | |
| /**
 | |
| *  @brief   Local Divide function
 | |
| *
 | |
| *  This function does the divide
 | |
| *
 | |
| *  @return divide value
 | |
| *
 | |
| */
 | |
| /****************************************************************************/
 | |
| static int chipcHw_divide(int num, int denom)
 | |
| {
 | |
| 	int r;
 | |
| 	int t = 1;
 | |
| 
 | |
| 	/* Shift denom and t up to the largest value to optimize algorithm */
 | |
| 	/* t contains the units of each divide */
 | |
| 	while ((denom & 0x40000000) == 0) {	/* fails if denom=0 */
 | |
| 		denom = denom << 1;
 | |
| 		t = t << 1;
 | |
| 	}
 | |
| 
 | |
| 	/* Intialize the result */
 | |
| 	r = 0;
 | |
| 
 | |
| 	do {
 | |
| 		/* Determine if there exists a positive remainder */
 | |
| 		if ((num - denom) >= 0) {
 | |
| 			/* Accumlate t to the result and calculate a new remainder */
 | |
| 			num = num - denom;
 | |
| 			r = r + t;
 | |
| 		}
 | |
| 		/* Continue to shift denom and shift t down to 0 */
 | |
| 		denom = denom >> 1;
 | |
| 		t = t >> 1;
 | |
| 	} while (t != 0);
 | |
| 
 | |
| 	return r;
 | |
| }
 |