📄 clock.c
字号:
/*! * \file clock.c * \brief Driver for the on-chip Clock Module * \version $Revision: 1.4 $ * \author Michael Norman * * Driver for the on-chip clock module which includes a PLL, * crystal oscillator, and low-power divider. * * \todo Add section assignment to allow linker files to * force all code and data into non-sdram address */#include "common.h"#include "clock.h"#include "sdramc.h"/********************************************************************//*! * The input reference frequency (in Hz). This variable should be placed in * stable RAM that will be accessible at all possible clock frequencies. */static int clock_fref = FREF;/********************************************************************//*! * \brief Initialize the on-chip PLL * \param fref Input reference frequency (in Hz) * \param fsys Desired system frequency (in Hz) * \param flags Optional parameter bit field * \return The resulting output system frequency (in Hz) * * The PLL has three modes of operation: normal mode with crystal reference, * normal mode with external reference, and limp mode. In normal mode with * a crystal reference, the PLL receives an input clock frequency from the * crystal oscillator circuit and multiplies the frequency to create the PLL * output clock. It can synthesize frequencies ranging from 4x to 34x the * input frequency. Normal mode with external reference is the same except * EXTAL is driven by an external clock. * * * In limp mode the PLL is bypassed and the device runs from a factor of the * input clock (EXTAL). In this mode, EXTAL feeds a 5-bit programmable * counter that divides the input clock by 2^n, where n is the value of the * programmable counter field, MISCCR[LPDIV]. * * \todo Add flags for * 1) CLOCK_PLL_FORCE_60 - try to force fvco to be a valid multiple * of 60 MHz so the USB clock can be derived from the PLL */intclock_pll_init (int fref, int fsys, int flags, void (*isr)(void)){ int p, pn, px, o, on, ox, fvco, pfd, outdiv1, outdiv3, diff; ASSERT ((fref >= CLOCK_PLL_FREF_MIN) && (fref <= CLOCK_PLL_FREF_MAX)); ASSERT ((fsys >= CLOCK_PLL_FSYS_MIN) && (fsys <= CLOCK_PLL_FSYS_MAX)); /* Save off fref value */ clock_fref = fref; if (!(flags & CLOCK_PLL_DEFAULT)) { /* * Calculate the PFD and OUTDIV1: * Fsys = Fvco / OUTDIV1 * Fvco = Fref * PFD */ /* minimum possible pfd */ pn = (CLOCK_PLL_FVCO_MIN / fref) + ((CLOCK_PLL_FVCO_MIN % fref) ? 1 : 0); /* maximum possible pfd */ px = CLOCK_PLL_FVCO_MAX / fref; /* minimum possible outdiv1 */ on = ((pn * fref) / fsys) + (((pn * fref) % fsys) ? 1 : 0); /* maximum possible outdiv1 */ ox = ((px * fref) / fsys) + (((px * fref) % fsys) ? 1 : 0); diff = fsys; for (p = pn; p <= px; p++) { fvco = fref * p; for (o = on; o <= ox; o++) { /* exact match */ if ((fvco / o) == fsys) { pfd = p; outdiv1 = o; p = px; break; } /* We've gone below the desired fsys * Check diff and move on to next iteration of pfd */ else if ((fvco / o) < fsys) { if ((fsys - (fvco / o)) < diff) { diff = fsys - (fvco / o); pfd = p; outdiv1 = o; } break; } /* We are still above the desired fsys. * Check diff and move on to next iteration of outdiv1 * within this pfd value. Notice this will allow a value * larger than the desired fsys to be the outcome if it * results in the closest valid setting. */ else /* ((fvco / o) > fsys) */ { /* Make sure these settings result in a valid fsys. * It is possible to step off the upper edge here... */ if ((fvco / o) > CLOCK_PLL_FSYS_MAX) break; if (((fvco / o) - fsys) < diff) { diff = (fvco / o) - fsys; pfd = p; outdiv1 = o; } } } } // sdram_enter_self_refresh(); clock_enter_limp(1); /* Determine FBCLK output divider */ if (flags & CLOCK_PLL_FBCLK_NONE) outdiv3 = 0; else { if (flags & CLOCK_PLL_FBCLK_DIV8) outdiv3 = outdiv1*8 - 1; else outdiv3 = outdiv1*4 - 1; } /* Apply new PLL settings */ MCF_PLL_PCR = 0 | MCF_PLL_PCR_OUTDIV1(outdiv1 - 1) | MCF_PLL_PCR_OUTDIV2(outdiv1*2 - 1) | MCF_PLL_PCR_OUTDIV3(outdiv3) | MCF_PLL_PCR_OUTDIV4(pfd-1) | MCF_PLL_PCR_OUTDIV5(0) | MCF_PLL_PCR_PFDR(pfd); clock_exit_limp();// sdram_exit_self_refresh(); } if (flags & CLOCK_PLL_LOLIRQ) { if (isr == NULL) isr = clock_irq_handler; MCF_INTC1_ICR57 = MCF_INTC_ICR_IL(7); MCF_INTC1_CIMR = MCF_INTC_CIMR_CIMR(57); mcf5xxx_set_handler (128 + 57, (ADDRESS) isr); MCF_PLL_PSR |= MCF_PLL_PSR_LOLIRQ; } if (flags & CLOCK_PLL_LOLRE) { MCF_PLL_PSR |= MCF_PLL_PSR_LOLRE; } if (flags & CLOCK_PLL_LOLDIS) { MCF_PLL_PSR |= MCF_PLL_PSR_LOLDIS; } return clock_get_fsys();}/********************************************************************/voidclock_irq_handler (void){ if (MCF_PLL_PSR & MCF_PLL_PSR_LOCKS) { MCF_PLL_PSR &= ~MCF_PLL_PSR_LOCKS; #ifdef DEBUG_PRINT printf("Loss of Lock!\n"); #endif /* Add additional code here if desired */ }}/********************************************************************//* * \brief Enter Limp mode * * Place the device into low-frequency limp mode, in which the PLL is * bypassed and the device runs from a factor of the input clock (EXTAL). * In this mode, EXTAL feeds a 5-bit programmable counter that divides * the input clock by 2^n, where n is the value of the programmable * counter field, MISCCR[LPDIV]. * * \param lpdiv decoded divider (2^n (not just n)) */voidclock_enter_limp (int lpdiv){ int i, j, retval; /* Check bounds of divider */ if (lpdiv < CLOCK_LPD_MIN) lpdiv = CLOCK_LPD_MIN; if (lpdiv > CLOCK_LPD_MAX) lpdiv = CLOCK_LPD_MAX; /* Round divider down to nearest power of two */ for (i = 0, j = lpdiv; j != 1; j >>= 1, i++) {}; /* Apply the divider to the system clock */ MCF_CCM_CDR = (MCF_CCM_CDR & 0x00FF) | MCF_CCM_CDR_LPDIV(i); /* Enable Limp Mode */ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_LIMP;}/********************************************************************//* * \brief Exit Limp mode * \warning The PLL should be set and locked prior to exiting Limp mode */voidclock_exit_limp (void){ int pfd, outdiv1; /* Exit Limp mode */ MCF_CCM_MISCCR &= ~MCF_CCM_MISCCR_LIMP;}/********************************************************************//* * \brief Get the value of the current system clock * \return current system clock frequency (in Hz) */intclock_get_fsys (void){ int fsys; if (MCF_CCM_MISCCR & MCF_CCM_MISCCR_LIMP) { /* Calculate Limp mode low-power divider setting */ int lpdiv = (MCF_CCM_CDR & MCF_CCM_CDR_LPDIV_MASK) >> 8; fsys = clock_fref / (1 << lpdiv); } else { /* Calculate PLL settings */ int pfd, outdiv1; pfd = (MCF_PLL_PCR & MCF_PLL_PCR_PFDR_MASK) >> 24; outdiv1 = MCF_PLL_PCR & MCF_PLL_PCR_OUTDIV1_MASK; fsys = (clock_fref * pfd) / (outdiv1 + 1); } return fsys;}/********************************************************************//* * \brief Get the value of the current (internal) bus clock * \return current bus clock frequency (in Hz) */intclock_get_fbus (void){ int pfd, outdiv2; /* Calculate PLL settings */ pfd = (MCF_PLL_PCR & MCF_PLL_PCR_PFDR_MASK) >> 24; outdiv2 = (MCF_PLL_PCR & MCF_PLL_PCR_OUTDIV2_MASK) >> 4; return ((clock_fref * pfd) / (outdiv2 + 1));}/********************************************************************//* * \brief Get the value of the current FlexBus clock * \return current FlexBus clock frequency (in Hz) */intclock_get_ffb (void){ int pfd, outdiv3; /* Calculate PLL settings */ pfd = (MCF_PLL_PCR & MCF_PLL_PCR_PFDR_MASK) >> 24; outdiv3 = (MCF_PLL_PCR & MCF_PLL_PCR_OUTDIV3_MASK) >> 8; if (outdiv3 == 0) return 0; else return ((clock_fref * pfd) / (outdiv3 + 1));}/********************************************************************//* * \brief Get the value of the current PCI clock * \return current PCI clock frequency (in Hz) */intclock_get_fpci (void){ int pfd, outdiv4; /* Calculate PLL settings */ pfd = (MCF_PLL_PCR & MCF_PLL_PCR_PFDR_MASK) >> 24; outdiv4 = (MCF_PLL_PCR & MCF_PLL_PCR_OUTDIV4_MASK) >> 12; if (outdiv4 == 0) return 0; else return ((clock_fref * pfd) / (outdiv4 + 1));}/********************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -