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