Logo Search packages:      
Sourcecode: linux version File versions  Download package

chipcHw.c

Go to the documentation of this file.
/*****************************************************************************
* 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
*
*/
/****************************************************************************/
00062 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
*
*/
/****************************************************************************/
00261 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
*
*/
/****************************************************************************/
00508 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
*
*/
/****************************************************************************/
00689 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
*
*/
/****************************************************************************/
00748 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;
      }

      /* Initialize 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;
}

Generated by  Doxygen 1.6.0   Back to index