diff options
author | Leo Chen <leochen@broadcom.com> | 2009-08-07 21:06:47 +0200 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-08-15 17:01:46 +0200 |
commit | 638ee2db1916de4da144f0ed6d156181ede20a41 (patch) | |
tree | 12b87e82578c55422b0c4b96e1e5708ee80cd070 /arch/arm/mach-bcmring/csp | |
parent | ARM: 5656/1: bcmring: add csp chipc inline functions (diff) | |
download | linux-638ee2db1916de4da144f0ed6d156181ede20a41.tar.xz linux-638ee2db1916de4da144f0ed6d156181ede20a41.zip |
ARM: 5657/1: bcmring: add csp chipc block source code
add csp chipc block source code
Signed-off-by: Leo Chen <leochen@broadcom.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-bcmring/csp')
-rw-r--r-- | arch/arm/mach-bcmring/csp/chipc/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-bcmring/csp/chipc/chipcHw.c | 776 | ||||
-rw-r--r-- | arch/arm/mach-bcmring/csp/chipc/chipcHw_init.c | 293 | ||||
-rw-r--r-- | arch/arm/mach-bcmring/csp/chipc/chipcHw_reset.c | 124 | ||||
-rw-r--r-- | arch/arm/mach-bcmring/csp/chipc/chipcHw_str.c | 64 |
5 files changed, 1258 insertions, 0 deletions
diff --git a/arch/arm/mach-bcmring/csp/chipc/Makefile b/arch/arm/mach-bcmring/csp/chipc/Makefile new file mode 100644 index 000000000000..673952768ee5 --- /dev/null +++ b/arch/arm/mach-bcmring/csp/chipc/Makefile @@ -0,0 +1 @@ +obj-y += chipcHw.o chipcHw_str.o chipcHw_reset.o chipcHw_init.o diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw.c new file mode 100644 index 000000000000..b3a61d860c65 --- /dev/null +++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw.c @@ -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; +} diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw_init.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw_init.c new file mode 100644 index 000000000000..367df75d4bb3 --- /dev/null +++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw_init.c @@ -0,0 +1,293 @@ +/***************************************************************************** +* 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_init.c +* +* @brief Low level CHIPC PLL configuration functions +* +* @note +* +* These routines provide basic PLL 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 --------------------------------------- */ + +/* + Calculation for NDIV_i to obtain VCO frequency + ----------------------------------------------- + + Freq_vco = Freq_ref * (P2 / P1) * (PLL_NDIV_i + PLL_NDIV_f) + for Freq_vco = VCO_FREQ_MHz + Freq_ref = chipcHw_XTAL_FREQ_Hz + PLL_P1 = PLL_P2 = 1 + and + PLL_NDIV_f = 0 + + We get: + PLL_NDIV_i = Freq_vco / Freq_ref = VCO_FREQ_MHz / chipcHw_XTAL_FREQ_Hz + + Calculation for PLL MDIV to obtain frequency Freq_x for channel x + ----------------------------------------------------------------- + Freq_x = chipcHw_XTAL_FREQ_Hz * PLL_NDIV_i / PLL_MDIV_x = VCO_FREQ_MHz / PLL_MDIV_x + + PLL_MDIV_x = VCO_FREQ_MHz / Freq_x +*/ + +/* ---- Private Variables ------------------------------------------------- */ +/****************************************************************************/ +/** +* @brief Initializes the PLL2 +* +* This function initializes the PLL2 +* +*/ +/****************************************************************************/ +void chipcHw_pll2Enable(uint32_t vcoFreqHz) +{ + uint32_t pllPreDivider2 = 0; + + { + REG_LOCAL_IRQ_SAVE; + pChipcHw->PLLConfig2 = + chipcHw_REG_PLL_CONFIG_D_RESET | + chipcHw_REG_PLL_CONFIG_A_RESET; + + pllPreDivider2 = chipcHw_REG_PLL_PREDIVIDER_POWER_DOWN | + chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER | + (chipcHw_REG_PLL_PREDIVIDER_NDIV_i(vcoFreqHz) << + chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) | + (chipcHw_REG_PLL_PREDIVIDER_P1 << + chipcHw_REG_PLL_PREDIVIDER_P1_SHIFT) | + (chipcHw_REG_PLL_PREDIVIDER_P2 << + chipcHw_REG_PLL_PREDIVIDER_P2_SHIFT); + + /* Enable CHIPC registers to control the PLL */ + pChipcHw->PLLStatus |= chipcHw_REG_PLL_STATUS_CONTROL_ENABLE; + + /* Set pre divider to get desired VCO frequency */ + pChipcHw->PLLPreDivider2 = pllPreDivider2; + /* Set NDIV Frac */ + pChipcHw->PLLDivider2 = chipcHw_REG_PLL_DIVIDER_NDIV_f; + + /* This has to be removed once the default values are fixed for PLL2. */ + pChipcHw->PLLControl12 = 0x38000700; + pChipcHw->PLLControl22 = 0x00000015; + + /* Reset PLL2 */ + if (vcoFreqHz > chipcHw_REG_PLL_CONFIG_VCO_SPLIT_FREQ) { + pChipcHw->PLLConfig2 = chipcHw_REG_PLL_CONFIG_D_RESET | + chipcHw_REG_PLL_CONFIG_A_RESET | + chipcHw_REG_PLL_CONFIG_VCO_1601_3200 | + chipcHw_REG_PLL_CONFIG_POWER_DOWN; + } else { + pChipcHw->PLLConfig2 = chipcHw_REG_PLL_CONFIG_D_RESET | + chipcHw_REG_PLL_CONFIG_A_RESET | + chipcHw_REG_PLL_CONFIG_VCO_800_1600 | + chipcHw_REG_PLL_CONFIG_POWER_DOWN; + } + REG_LOCAL_IRQ_RESTORE; + } + + /* Insert certain amount of delay before deasserting ARESET. */ + udelay(1); + + { + REG_LOCAL_IRQ_SAVE; + /* Remove analog reset and Power on the PLL */ + pChipcHw->PLLConfig2 &= + ~(chipcHw_REG_PLL_CONFIG_A_RESET | + chipcHw_REG_PLL_CONFIG_POWER_DOWN); + + REG_LOCAL_IRQ_RESTORE; + + } + + /* Wait until PLL is locked */ + while (!(pChipcHw->PLLStatus2 & chipcHw_REG_PLL_STATUS_LOCKED)) + ; + + { + REG_LOCAL_IRQ_SAVE; + /* Remove digital reset */ + pChipcHw->PLLConfig2 &= ~chipcHw_REG_PLL_CONFIG_D_RESET; + + REG_LOCAL_IRQ_RESTORE; + } +} + +EXPORT_SYMBOL(chipcHw_pll2Enable); + +/****************************************************************************/ +/** +* @brief Initializes the PLL1 +* +* This function initializes the PLL1 +* +*/ +/****************************************************************************/ +void chipcHw_pll1Enable(uint32_t vcoFreqHz, chipcHw_SPREAD_SPECTRUM_e ssSupport) +{ + uint32_t pllPreDivider = 0; + + { + REG_LOCAL_IRQ_SAVE; + + pChipcHw->PLLConfig = + chipcHw_REG_PLL_CONFIG_D_RESET | + chipcHw_REG_PLL_CONFIG_A_RESET; + /* Setting VCO frequency */ + if (ssSupport == chipcHw_SPREAD_SPECTRUM_ALLOW) { + pllPreDivider = + chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASH_1_8 | + ((chipcHw_REG_PLL_PREDIVIDER_NDIV_i(vcoFreqHz) - + 1) << chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) | + (chipcHw_REG_PLL_PREDIVIDER_P1 << + chipcHw_REG_PLL_PREDIVIDER_P1_SHIFT) | + (chipcHw_REG_PLL_PREDIVIDER_P2 << + chipcHw_REG_PLL_PREDIVIDER_P2_SHIFT); + } else { + pllPreDivider = chipcHw_REG_PLL_PREDIVIDER_POWER_DOWN | + chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER | + (chipcHw_REG_PLL_PREDIVIDER_NDIV_i(vcoFreqHz) << + chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) | + (chipcHw_REG_PLL_PREDIVIDER_P1 << + chipcHw_REG_PLL_PREDIVIDER_P1_SHIFT) | + (chipcHw_REG_PLL_PREDIVIDER_P2 << + chipcHw_REG_PLL_PREDIVIDER_P2_SHIFT); + } + + /* Enable CHIPC registers to control the PLL */ + pChipcHw->PLLStatus |= chipcHw_REG_PLL_STATUS_CONTROL_ENABLE; + + /* Set pre divider to get desired VCO frequency */ + pChipcHw->PLLPreDivider = pllPreDivider; + /* Set NDIV Frac */ + if (ssSupport == chipcHw_SPREAD_SPECTRUM_ALLOW) { + pChipcHw->PLLDivider = chipcHw_REG_PLL_DIVIDER_M1DIV | + chipcHw_REG_PLL_DIVIDER_NDIV_f_SS; + } else { + pChipcHw->PLLDivider = chipcHw_REG_PLL_DIVIDER_M1DIV | + chipcHw_REG_PLL_DIVIDER_NDIV_f; + } + + /* Reset PLL1 */ + if (vcoFreqHz > chipcHw_REG_PLL_CONFIG_VCO_SPLIT_FREQ) { + pChipcHw->PLLConfig = chipcHw_REG_PLL_CONFIG_D_RESET | + chipcHw_REG_PLL_CONFIG_A_RESET | + chipcHw_REG_PLL_CONFIG_VCO_1601_3200 | + chipcHw_REG_PLL_CONFIG_POWER_DOWN; + } else { + pChipcHw->PLLConfig = chipcHw_REG_PLL_CONFIG_D_RESET | + chipcHw_REG_PLL_CONFIG_A_RESET | + chipcHw_REG_PLL_CONFIG_VCO_800_1600 | + chipcHw_REG_PLL_CONFIG_POWER_DOWN; + } + + REG_LOCAL_IRQ_RESTORE; + + /* Insert certain amount of delay before deasserting ARESET. */ + udelay(1); + + { + REG_LOCAL_IRQ_SAVE; + /* Remove analog reset and Power on the PLL */ + pChipcHw->PLLConfig &= + ~(chipcHw_REG_PLL_CONFIG_A_RESET | + chipcHw_REG_PLL_CONFIG_POWER_DOWN); + REG_LOCAL_IRQ_RESTORE; + } + + /* Wait until PLL is locked */ + while (!(pChipcHw->PLLStatus & chipcHw_REG_PLL_STATUS_LOCKED) + || !(pChipcHw-> + PLLStatus2 & chipcHw_REG_PLL_STATUS_LOCKED)) + ; + + /* Remove digital reset */ + { + REG_LOCAL_IRQ_SAVE; + pChipcHw->PLLConfig &= ~chipcHw_REG_PLL_CONFIG_D_RESET; + REG_LOCAL_IRQ_RESTORE; + } + } +} + +EXPORT_SYMBOL(chipcHw_pll1Enable); + +/****************************************************************************/ +/** +* @brief Initializes the chipc module +* +* This function initializes the PLLs and core system clocks +* +*/ +/****************************************************************************/ + +void chipcHw_Init(chipcHw_INIT_PARAM_t *initParam /* [ IN ] Misc chip initialization parameter */ + ) { +#if !(defined(__KERNEL__) && !defined(STANDALONE)) + delay_init(); +#endif + + /* Do not program PLL, when warm reset */ + if (!(chipcHw_getStickyBits() & chipcHw_REG_STICKY_CHIP_WARM_RESET)) { + chipcHw_pll1Enable(initParam->pllVcoFreqHz, + initParam->ssSupport); + chipcHw_pll2Enable(initParam->pll2VcoFreqHz); + } else { + /* Clear sticky bits */ + chipcHw_clearStickyBits(chipcHw_REG_STICKY_CHIP_WARM_RESET); + } + /* Clear sticky bits */ + chipcHw_clearStickyBits(chipcHw_REG_STICKY_CHIP_SOFT_RESET); + + /* Before configuring the ARM clock, atleast we need to make sure BUS clock maintains the proper ratio with ARM clock */ + pChipcHw->ACLKClock = + (pChipcHw-> + ACLKClock & ~chipcHw_REG_ACLKClock_CLK_DIV_MASK) | (initParam-> + armBusRatio & + chipcHw_REG_ACLKClock_CLK_DIV_MASK); + + /* Set various core component frequencies. The order in which this is done is important for some. */ + /* The RTBUS (DDR PHY) is derived from the BUS, and the BUS from the ARM, and VPM needs to know BUS */ + /* frequency to find its ratio with the BUS. Hence we must set the ARM first, followed by the BUS, */ + /* then VPM and RTBUS. */ + + chipcHw_setClockFrequency(chipcHw_CLOCK_ARM, + initParam->busClockFreqHz * + initParam->armBusRatio); + chipcHw_setClockFrequency(chipcHw_CLOCK_BUS, initParam->busClockFreqHz); + chipcHw_setClockFrequency(chipcHw_CLOCK_VPM, + initParam->busClockFreqHz * + initParam->vpmBusRatio); + chipcHw_setClockFrequency(chipcHw_CLOCK_DDR, + initParam->busClockFreqHz * + initParam->ddrBusRatio); + chipcHw_setClockFrequency(chipcHw_CLOCK_RTBUS, + initParam->busClockFreqHz / 2); +} diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw_reset.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw_reset.c new file mode 100644 index 000000000000..2671d8896bbb --- /dev/null +++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw_reset.c @@ -0,0 +1,124 @@ +/***************************************************************************** +* 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. +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ +#include <csp/stdint.h> +#include <mach/csp/chipcHw_def.h> +#include <mach/csp/chipcHw_inline.h> +#include <csp/intcHw.h> +#include <csp/cache.h> + +/* ---- Private Constants and Types --------------------------------------- */ +/* ---- Private Variables ------------------------------------------------- */ +void chipcHw_reset_run_from_aram(void); + +typedef void (*RUNFUNC) (void); + +/****************************************************************************/ +/** +* @brief warmReset +* +* @note warmReset configures the clocks which are not reset back to the state +* required to execute on reset. To do so we need to copy the code into internal +* memory to change the ARM clock while we are not executing from DDR. +*/ +/****************************************************************************/ +void chipcHw_reset(uint32_t mask) +{ + int i = 0; + RUNFUNC runFunc = (RUNFUNC) (unsigned long)MM_ADDR_IO_ARAM; + + /* Disable all interrupts */ + intcHw_irq_disable(INTCHW_INTC0, 0xffffffff); + intcHw_irq_disable(INTCHW_INTC1, 0xffffffff); + intcHw_irq_disable(INTCHW_SINTC, 0xffffffff); + + { + REG_LOCAL_IRQ_SAVE; + if (mask & chipcHw_REG_SOFT_RESET_CHIP_SOFT) { + chipcHw_softReset(chipcHw_REG_SOFT_RESET_CHIP_SOFT); + } + /* Bypass the PLL clocks before reboot */ + pChipcHw->UARTClock |= chipcHw_REG_PLL_CLOCK_BYPASS_SELECT; + pChipcHw->SPIClock |= chipcHw_REG_PLL_CLOCK_BYPASS_SELECT; + + /* Copy the chipcHw_warmReset_run_from_aram function into ARAM */ + do { + ((uint32_t *) MM_IO_BASE_ARAM)[i] = + ((uint32_t *) &chipcHw_reset_run_from_aram)[i]; + i++; + } while (((uint32_t *) MM_IO_BASE_ARAM)[i - 1] != 0xe1a0f00f); /* 0xe1a0f00f == asm ("mov r15, r15"); */ + + CSP_CACHE_FLUSH_ALL; + + /* run the function from ARAM */ + runFunc(); + + /* Code will never get here, but include it to balance REG_LOCAL_IRQ_SAVE above */ + REG_LOCAL_IRQ_RESTORE; + } +} + +/* This function must run from internal memory */ +void chipcHw_reset_run_from_aram(void) +{ +/* Make sure, pipeline is filled with instructions coming from ARAM */ +__asm (" nop \n\t" + " nop \n\t" +#if defined(__KERNEL__) && !defined(STANDALONE) + " MRC p15,#0x0,r0,c1,c0,#0 \n\t" + " BIC r0,r0,#0xd \n\t" + " MCR p15,#0x0,r0,c1,c0,#0 \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" +#endif + " nop \n\t" + " nop \n\t" +/* Bypass the ARM clock and switch to XTAL clock */ + " MOV r2,#0x80000000 \n\t" + " LDR r3,[r2,#8] \n\t" + " ORR r3,r3,#0x20000 \n\t" + " STR r3,[r2,#8] \n\t" + + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" +/* Issue reset */ + " MOV r3,#0x2 \n\t" + " STR r3,[r2,#0x80] \n\t" +/* End here */ + " MOV pc,pc \n\t"); +/* 0xe1a0f00f == asm ("mov r15, r15"); */ +} diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw_str.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw_str.c new file mode 100644 index 000000000000..54ad964fe94c --- /dev/null +++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw_str.c @@ -0,0 +1,64 @@ +/***************************************************************************** +* Copyright 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_str.c +* +* @brief Contains strings which are useful to linux and csp +* +* @note +*/ +/****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ + +#include <mach/csp/chipcHw_inline.h> + +/* ---- Private Constants and Types --------------------------------------- */ + +static const char *gMuxStr[] = { + "GPIO", /* 0 */ + "KeyPad", /* 1 */ + "I2C-Host", /* 2 */ + "SPI", /* 3 */ + "Uart", /* 4 */ + "LED-Mtx-P", /* 5 */ + "LED-Mtx-S", /* 6 */ + "SDIO-0", /* 7 */ + "SDIO-1", /* 8 */ + "PCM", /* 9 */ + "I2S", /* 10 */ + "ETM", /* 11 */ + "Debug", /* 12 */ + "Misc", /* 13 */ + "0xE", /* 14 */ + "0xF", /* 15 */ +}; + +/****************************************************************************/ +/** +* @brief Retrieves a string representation of the mux setting for a pin. +* +* @return Pointer to a character string. +*/ +/****************************************************************************/ + +const char *chipcHw_getGpioPinFunctionStr(int pin) +{ + if ((pin < 0) || (pin >= chipcHw_GPIO_COUNT)) { + return ""; + } + + return gMuxStr[chipcHw_getGpioPinFunction(pin)]; +} |