|
@@ -0,0 +1,776 @@
|
|
|
+/*****************************************************************************
|
|
|
+* 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;
|
|
|
+}
|