add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
obj-y += dmac/
obj-y += tmr/
obj-y += chipc/

View File

@@ -0,0 +1 @@
obj-y += chipcHw.o chipcHw_str.o chipcHw_reset.o chipcHw_init.o

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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"); */
}

View File

@@ -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)];
}

View File

@@ -0,0 +1 @@
obj-y += dmacHw.o dmacHw_extra.o

View File

@@ -0,0 +1,917 @@
/*****************************************************************************
* 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 dmacHw.c
*
* @brief Low level DMA controller driver routines
*
* @note
*
* These routines provide basic DMA functionality only.
*/
/****************************************************************************/
/* ---- Include Files ---------------------------------------------------- */
#include <csp/stdint.h>
#include <csp/string.h>
#include <stddef.h>
#include <csp/dmacHw.h>
#include <mach/csp/dmacHw_reg.h>
#include <mach/csp/dmacHw_priv.h>
#include <mach/csp/chipcHw_inline.h>
/* ---- External Function Prototypes ------------------------------------- */
/* Allocate DMA control blocks */
dmacHw_CBLK_t dmacHw_gCblk[dmacHw_MAX_CHANNEL_COUNT];
uint32_t dmaChannelCount_0 = dmacHw_MAX_CHANNEL_COUNT / 2;
uint32_t dmaChannelCount_1 = dmacHw_MAX_CHANNEL_COUNT / 2;
/****************************************************************************/
/**
* @brief Get maximum FIFO for a DMA channel
*
* @return Maximum allowable FIFO size
*
*
*/
/****************************************************************************/
static uint32_t GetFifoSize(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
) {
uint32_t val = 0;
dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
dmacHw_MISC_t *pMiscReg =
(dmacHw_MISC_t *) dmacHw_REG_MISC_BASE(pCblk->module);
switch (pCblk->channel) {
case 0:
val = (pMiscReg->CompParm2.lo & 0x70000000) >> 28;
break;
case 1:
val = (pMiscReg->CompParm3.hi & 0x70000000) >> 28;
break;
case 2:
val = (pMiscReg->CompParm3.lo & 0x70000000) >> 28;
break;
case 3:
val = (pMiscReg->CompParm4.hi & 0x70000000) >> 28;
break;
case 4:
val = (pMiscReg->CompParm4.lo & 0x70000000) >> 28;
break;
case 5:
val = (pMiscReg->CompParm5.hi & 0x70000000) >> 28;
break;
case 6:
val = (pMiscReg->CompParm5.lo & 0x70000000) >> 28;
break;
case 7:
val = (pMiscReg->CompParm6.hi & 0x70000000) >> 28;
break;
}
if (val <= 0x4) {
return 8 << val;
} else {
dmacHw_ASSERT(0);
}
return 0;
}
/****************************************************************************/
/**
* @brief Program channel register to initiate transfer
*
* @return void
*
*
* @note
* - Descriptor buffer MUST ALWAYS be flushed before calling this function
* - This function should also be called from ISR to program the channel with
* pending descriptors
*/
/****************************************************************************/
void dmacHw_initiateTransfer(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
void *pDescriptor /* [ IN ] Descriptor buffer */
) {
dmacHw_DESC_RING_t *pRing;
dmacHw_DESC_t *pProg;
dmacHw_CBLK_t *pCblk;
pCblk = dmacHw_HANDLE_TO_CBLK(handle);
pRing = dmacHw_GET_DESC_RING(pDescriptor);
if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) {
/* Not safe yet to program the channel */
return;
}
if (pCblk->varDataStarted) {
if (pCblk->descUpdated) {
pCblk->descUpdated = 0;
pProg =
(dmacHw_DESC_t *) ((uint32_t)
dmacHw_REG_LLP(pCblk->module,
pCblk->channel) +
pRing->virt2PhyOffset);
/* Load descriptor if not loaded */
if (!(pProg->ctl.hi & dmacHw_REG_CTL_DONE)) {
dmacHw_SET_SAR(pCblk->module, pCblk->channel,
pProg->sar);
dmacHw_SET_DAR(pCblk->module, pCblk->channel,
pProg->dar);
dmacHw_REG_CTL_LO(pCblk->module,
pCblk->channel) =
pProg->ctl.lo;
dmacHw_REG_CTL_HI(pCblk->module,
pCblk->channel) =
pProg->ctl.hi;
} else if (pProg == (dmacHw_DESC_t *) pRing->pEnd->llp) {
/* Return as end descriptor is processed */
return;
} else {
dmacHw_ASSERT(0);
}
} else {
return;
}
} else {
if (pConfig->transferMode == dmacHw_TRANSFER_MODE_PERIODIC) {
/* Do not make a single chain, rather process one descriptor at a time */
pProg = pRing->pHead;
/* Point to the next descriptor for next iteration */
dmacHw_NEXT_DESC(pRing, pHead);
} else {
/* Return if no more pending descriptor */
if (pRing->pEnd == NULL) {
return;
}
pProg = pRing->pProg;
if (pConfig->transferMode ==
dmacHw_TRANSFER_MODE_CONTINUOUS) {
/* Make sure a complete ring can be formed */
dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pEnd->
llp == pRing->pProg);
/* Make sure pProg pointing to the pHead */
dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pProg ==
pRing->pHead);
/* Make a complete ring */
do {
pRing->pProg->ctl.lo |=
(dmacHw_REG_CTL_LLP_DST_EN |
dmacHw_REG_CTL_LLP_SRC_EN);
pRing->pProg =
(dmacHw_DESC_t *) pRing->pProg->llp;
} while (pRing->pProg != pRing->pHead);
} else {
/* Make a single long chain */
while (pRing->pProg != pRing->pEnd) {
pRing->pProg->ctl.lo |=
(dmacHw_REG_CTL_LLP_DST_EN |
dmacHw_REG_CTL_LLP_SRC_EN);
pRing->pProg =
(dmacHw_DESC_t *) pRing->pProg->llp;
}
}
}
/* Program the channel registers */
dmacHw_SET_SAR(pCblk->module, pCblk->channel, pProg->sar);
dmacHw_SET_DAR(pCblk->module, pCblk->channel, pProg->dar);
dmacHw_SET_LLP(pCblk->module, pCblk->channel,
(uint32_t) pProg - pRing->virt2PhyOffset);
dmacHw_REG_CTL_LO(pCblk->module, pCblk->channel) =
pProg->ctl.lo;
dmacHw_REG_CTL_HI(pCblk->module, pCblk->channel) =
pProg->ctl.hi;
if (pRing->pEnd) {
/* Remember the descriptor to use next */
pRing->pProg = (dmacHw_DESC_t *) pRing->pEnd->llp;
}
/* Indicate no more pending descriptor */
pRing->pEnd = (dmacHw_DESC_t *) NULL;
}
/* Start DMA operation */
dmacHw_DMA_START(pCblk->module, pCblk->channel);
}
/****************************************************************************/
/**
* @brief Initializes DMA
*
* This function initializes DMA CSP driver
*
* @note
* Must be called before using any DMA channel
*/
/****************************************************************************/
void dmacHw_initDma(void)
{
uint32_t i = 0;
dmaChannelCount_0 = dmacHw_GET_NUM_CHANNEL(0);
dmaChannelCount_1 = dmacHw_GET_NUM_CHANNEL(1);
/* Enable access to the DMA block */
chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC0);
chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC1);
if ((dmaChannelCount_0 + dmaChannelCount_1) > dmacHw_MAX_CHANNEL_COUNT) {
dmacHw_ASSERT(0);
}
memset((void *)dmacHw_gCblk, 0,
sizeof(dmacHw_CBLK_t) * (dmaChannelCount_0 + dmaChannelCount_1));
for (i = 0; i < dmaChannelCount_0; i++) {
dmacHw_gCblk[i].module = 0;
dmacHw_gCblk[i].channel = i;
}
for (i = 0; i < dmaChannelCount_1; i++) {
dmacHw_gCblk[i + dmaChannelCount_0].module = 1;
dmacHw_gCblk[i + dmaChannelCount_0].channel = i;
}
}
/****************************************************************************/
/**
* @brief Exit function for DMA
*
* This function isolates DMA from the system
*
*/
/****************************************************************************/
void dmacHw_exitDma(void)
{
/* Disable access to the DMA block */
chipcHw_busInterfaceClockDisable(chipcHw_REG_BUS_CLOCK_DMAC0);
chipcHw_busInterfaceClockDisable(chipcHw_REG_BUS_CLOCK_DMAC1);
}
/****************************************************************************/
/**
* @brief Gets a handle to a DMA channel
*
* This function returns a handle, representing a control block of a particular DMA channel
*
* @return -1 - On Failure
* handle - On Success, representing a channel control block
*
* @note
* None Channel ID must be created using "dmacHw_MAKE_CHANNEL_ID" macro
*/
/****************************************************************************/
dmacHw_HANDLE_t dmacHw_getChannelHandle(dmacHw_ID_t channelId /* [ IN ] DMA Channel Id */
) {
int idx;
switch ((channelId >> 8)) {
case 0:
dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_0);
idx = (channelId & 0xff);
break;
case 1:
dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_1);
idx = dmaChannelCount_0 + (channelId & 0xff);
break;
default:
dmacHw_ASSERT(0);
return (dmacHw_HANDLE_t) -1;
}
return dmacHw_CBLK_TO_HANDLE(&dmacHw_gCblk[idx]);
}
/****************************************************************************/
/**
* @brief Initializes a DMA channel for use
*
* This function initializes and resets a DMA channel for use
*
* @return -1 - On Failure
* 0 - On Success
*
* @note
* None
*/
/****************************************************************************/
int dmacHw_initChannel(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
) {
dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
int module = pCblk->module;
int channel = pCblk->channel;
/* Reinitialize the control block */
memset((void *)pCblk, 0, sizeof(dmacHw_CBLK_t));
pCblk->module = module;
pCblk->channel = channel;
/* Enable DMA controller */
dmacHw_DMA_ENABLE(pCblk->module);
/* Reset DMA channel */
dmacHw_RESET_CONTROL_LO(pCblk->module, pCblk->channel);
dmacHw_RESET_CONTROL_HI(pCblk->module, pCblk->channel);
dmacHw_RESET_CONFIG_LO(pCblk->module, pCblk->channel);
dmacHw_RESET_CONFIG_HI(pCblk->module, pCblk->channel);
/* Clear all raw interrupt status */
dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);
dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);
dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);
/* Mask event specific interrupts */
dmacHw_TRAN_INT_DISABLE(pCblk->module, pCblk->channel);
dmacHw_BLOCK_INT_DISABLE(pCblk->module, pCblk->channel);
dmacHw_STRAN_INT_DISABLE(pCblk->module, pCblk->channel);
dmacHw_DTRAN_INT_DISABLE(pCblk->module, pCblk->channel);
dmacHw_ERROR_INT_DISABLE(pCblk->module, pCblk->channel);
return 0;
}
/****************************************************************************/
/**
* @brief Finds amount of memory required to form a descriptor ring
*
*
* @return Number of bytes required to form a descriptor ring
*
*
*/
/****************************************************************************/
uint32_t dmacHw_descriptorLen(uint32_t descCnt /* [ IN ] Number of descriptor in the ring */
) {
/* Need extra 4 byte to ensure 32 bit alignment */
return (descCnt * sizeof(dmacHw_DESC_t)) + sizeof(dmacHw_DESC_RING_t) +
sizeof(uint32_t);
}
/****************************************************************************/
/**
* @brief Initializes descriptor ring
*
* This function will initializes the descriptor ring of a DMA channel
*
*
* @return -1 - On failure
* 0 - On success
* @note
* - "len" parameter should be obtained from "dmacHw_descriptorLen"
* - Descriptor buffer MUST be 32 bit aligned and uncached as it is
* accessed by ARM and DMA
*/
/****************************************************************************/
int dmacHw_initDescriptor(void *pDescriptorVirt, /* [ IN ] Virtual address of uncahced buffer allocated to form descriptor ring */
uint32_t descriptorPhyAddr, /* [ IN ] Physical address of pDescriptorVirt (descriptor buffer) */
uint32_t len, /* [ IN ] Size of the pBuf */
uint32_t num /* [ IN ] Number of descriptor in the ring */
) {
uint32_t i;
dmacHw_DESC_RING_t *pRing;
dmacHw_DESC_t *pDesc;
/* Check the alignment of the descriptor */
if ((uint32_t) pDescriptorVirt & 0x00000003) {
dmacHw_ASSERT(0);
return -1;
}
/* Check if enough space has been allocated for descriptor ring */
if (len < dmacHw_descriptorLen(num)) {
return -1;
}
pRing = dmacHw_GET_DESC_RING(pDescriptorVirt);
pRing->pHead =
(dmacHw_DESC_t *) ((uint32_t) pRing + sizeof(dmacHw_DESC_RING_t));
pRing->pFree = pRing->pTail = pRing->pEnd = pRing->pHead;
pRing->pProg = dmacHw_DESC_INIT;
/* Initialize link item chain, starting from the head */
pDesc = pRing->pHead;
/* Find the offset between virtual to physical address */
pRing->virt2PhyOffset = (uint32_t) pDescriptorVirt - descriptorPhyAddr;
/* Form the descriptor ring */
for (i = 0; i < num - 1; i++) {
/* Clear link list item */
memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t));
/* Point to the next item in the physical address */
pDesc->llpPhy = (uint32_t) (pDesc + 1) - pRing->virt2PhyOffset;
/* Point to the next item in the virtual address */
pDesc->llp = (uint32_t) (pDesc + 1);
/* Mark descriptor is ready to use */
pDesc->ctl.hi = dmacHw_DESC_FREE;
/* Look into next link list item */
pDesc++;
}
/* Clear last link list item */
memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t));
/* Last item pointing to the first item in the
physical address to complete the ring */
pDesc->llpPhy = (uint32_t) pRing->pHead - pRing->virt2PhyOffset;
/* Last item pointing to the first item in the
virtual address to complete the ring
*/
pDesc->llp = (uint32_t) pRing->pHead;
/* Mark descriptor is ready to use */
pDesc->ctl.hi = dmacHw_DESC_FREE;
/* Set the number of descriptors in the ring */
pRing->num = num;
return 0;
}
/****************************************************************************/
/**
* @brief Configure DMA channel
*
* @return 0 : On success
* -1 : On failure
*/
/****************************************************************************/
int dmacHw_configChannel(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
dmacHw_CONFIG_t *pConfig /* [ IN ] Configuration settings */
) {
dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
uint32_t cfgHigh = 0;
int srcTrSize;
int dstTrSize;
pCblk->varDataStarted = 0;
pCblk->userData = NULL;
/* Configure
- Burst transaction when enough data in available in FIFO
- AHB Access protection 1
- Source and destination peripheral ports
*/
cfgHigh =
dmacHw_REG_CFG_HI_FIFO_ENOUGH | dmacHw_REG_CFG_HI_AHB_HPROT_1 |
dmacHw_SRC_PERI_INTF(pConfig->
srcPeripheralPort) |
dmacHw_DST_PERI_INTF(pConfig->dstPeripheralPort);
/* Set priority */
dmacHw_SET_CHANNEL_PRIORITY(pCblk->module, pCblk->channel,
pConfig->channelPriority);
if (pConfig->dstStatusRegisterAddress != 0) {
/* Destination status update enable */
cfgHigh |= dmacHw_REG_CFG_HI_UPDATE_DST_STAT;
/* Configure status registers */
dmacHw_SET_DSTATAR(pCblk->module, pCblk->channel,
pConfig->dstStatusRegisterAddress);
}
if (pConfig->srcStatusRegisterAddress != 0) {
/* Source status update enable */
cfgHigh |= dmacHw_REG_CFG_HI_UPDATE_SRC_STAT;
/* Source status update enable */
dmacHw_SET_SSTATAR(pCblk->module, pCblk->channel,
pConfig->srcStatusRegisterAddress);
}
/* Configure the config high register */
dmacHw_GET_CONFIG_HI(pCblk->module, pCblk->channel) = cfgHigh;
/* Clear all raw interrupt status */
dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);
dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);
dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);
/* Configure block interrupt */
if (pConfig->blockTransferInterrupt == dmacHw_INTERRUPT_ENABLE) {
dmacHw_BLOCK_INT_ENABLE(pCblk->module, pCblk->channel);
} else {
dmacHw_BLOCK_INT_DISABLE(pCblk->module, pCblk->channel);
}
/* Configure complete transfer interrupt */
if (pConfig->completeTransferInterrupt == dmacHw_INTERRUPT_ENABLE) {
dmacHw_TRAN_INT_ENABLE(pCblk->module, pCblk->channel);
} else {
dmacHw_TRAN_INT_DISABLE(pCblk->module, pCblk->channel);
}
/* Configure error interrupt */
if (pConfig->errorInterrupt == dmacHw_INTERRUPT_ENABLE) {
dmacHw_ERROR_INT_ENABLE(pCblk->module, pCblk->channel);
} else {
dmacHw_ERROR_INT_DISABLE(pCblk->module, pCblk->channel);
}
/* Configure gather register */
if (pConfig->srcGatherWidth) {
srcTrSize =
dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);
if (!
((pConfig->srcGatherWidth % srcTrSize)
&& (pConfig->srcGatherJump % srcTrSize))) {
dmacHw_REG_SGR_LO(pCblk->module, pCblk->channel) =
((pConfig->srcGatherWidth /
srcTrSize) << 20) | (pConfig->srcGatherJump /
srcTrSize);
} else {
return -1;
}
}
/* Configure scatter register */
if (pConfig->dstScatterWidth) {
dstTrSize =
dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);
if (!
((pConfig->dstScatterWidth % dstTrSize)
&& (pConfig->dstScatterJump % dstTrSize))) {
dmacHw_REG_DSR_LO(pCblk->module, pCblk->channel) =
((pConfig->dstScatterWidth /
dstTrSize) << 20) | (pConfig->dstScatterJump /
dstTrSize);
} else {
return -1;
}
}
return 0;
}
/****************************************************************************/
/**
* @brief Indicates whether DMA transfer is in progress or completed
*
* @return DMA transfer status
* dmacHw_TRANSFER_STATUS_BUSY: DMA Transfer ongoing
* dmacHw_TRANSFER_STATUS_DONE: DMA Transfer completed
* dmacHw_TRANSFER_STATUS_ERROR: DMA Transfer error
*
*/
/****************************************************************************/
dmacHw_TRANSFER_STATUS_e dmacHw_transferCompleted(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
) {
dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) {
return dmacHw_TRANSFER_STATUS_BUSY;
} else if (dmacHw_REG_INT_RAW_ERROR(pCblk->module) &
(0x00000001 << pCblk->channel)) {
return dmacHw_TRANSFER_STATUS_ERROR;
}
return dmacHw_TRANSFER_STATUS_DONE;
}
/****************************************************************************/
/**
* @brief Set descriptors for known data length
*
* When DMA has to work as a flow controller, this function prepares the
* descriptor chain to transfer data
*
* from:
* - Memory to memory
* - Peripheral to memory
* - Memory to Peripheral
* - Peripheral to Peripheral
*
* @return -1 - On failure
* 0 - On success
*
*/
/****************************************************************************/
int dmacHw_setDataDescriptor(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
void *pDescriptor, /* [ IN ] Descriptor buffer */
void *pSrcAddr, /* [ IN ] Source (Peripheral/Memory) address */
void *pDstAddr, /* [ IN ] Destination (Peripheral/Memory) address */
size_t dataLen /* [ IN ] Data length in bytes */
) {
dmacHw_TRANSACTION_WIDTH_e dstTrWidth;
dmacHw_TRANSACTION_WIDTH_e srcTrWidth;
dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
dmacHw_DESC_t *pStart;
dmacHw_DESC_t *pProg;
int srcTs = 0;
int blkTs = 0;
int oddSize = 0;
int descCount = 0;
int count = 0;
int dstTrSize = 0;
int srcTrSize = 0;
uint32_t maxBlockSize = dmacHw_MAX_BLOCKSIZE;
dstTrSize = dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);
srcTrSize = dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);
/* Skip Tx if buffer is NULL or length is unknown */
if ((pSrcAddr == NULL) || (pDstAddr == NULL) || (dataLen == 0)) {
/* Do not initiate transfer */
return -1;
}
/* Ensure scatter and gather are transaction aligned */
if ((pConfig->srcGatherWidth % srcTrSize)
|| (pConfig->dstScatterWidth % dstTrSize)) {
return -2;
}
/*
Background 1: DMAC can not perform DMA if source and destination addresses are
not properly aligned with the channel's transaction width. So, for successful
DMA transfer, transaction width must be set according to the alignment of the
source and destination address.
*/
/* Adjust destination transaction width if destination address is not aligned properly */
dstTrWidth = pConfig->dstMaxTransactionWidth;
while (dmacHw_ADDRESS_MASK(dstTrSize) & (uint32_t) pDstAddr) {
dstTrWidth = dmacHw_GetNextTrWidth(dstTrWidth);
dstTrSize = dmacHw_GetTrWidthInBytes(dstTrWidth);
}
/* Adjust source transaction width if source address is not aligned properly */
srcTrWidth = pConfig->srcMaxTransactionWidth;
while (dmacHw_ADDRESS_MASK(srcTrSize) & (uint32_t) pSrcAddr) {
srcTrWidth = dmacHw_GetNextTrWidth(srcTrWidth);
srcTrSize = dmacHw_GetTrWidthInBytes(srcTrWidth);
}
/* Find the maximum transaction per descriptor */
if (pConfig->maxDataPerBlock
&& ((pConfig->maxDataPerBlock / srcTrSize) <
dmacHw_MAX_BLOCKSIZE)) {
maxBlockSize = pConfig->maxDataPerBlock / srcTrSize;
}
/* Find number of source transactions needed to complete the DMA transfer */
srcTs = dataLen / srcTrSize;
/* Find the odd number of bytes that need to be transferred as single byte transaction width */
if (srcTs && (dstTrSize > srcTrSize)) {
oddSize = dataLen % dstTrSize;
/* Adjust source transaction count due to "oddSize" */
srcTs = srcTs - (oddSize / srcTrSize);
} else {
oddSize = dataLen % srcTrSize;
}
/* Adjust "descCount" due to "oddSize" */
if (oddSize) {
descCount++;
}
/* Find the number of descriptor needed for total "srcTs" */
if (srcTs) {
descCount += ((srcTs - 1) / maxBlockSize) + 1;
}
/* Check the availability of "descCount" discriptors in the ring */
pProg = pRing->pHead;
for (count = 0; (descCount <= pRing->num) && (count < descCount);
count++) {
if ((pProg->ctl.hi & dmacHw_DESC_FREE) == 0) {
/* Sufficient descriptors are not available */
return -3;
}
pProg = (dmacHw_DESC_t *) pProg->llp;
}
/* Remember the link list item to program the channel registers */
pStart = pProg = pRing->pHead;
/* Make a link list with "descCount(=count)" number of descriptors */
while (count) {
/* Reset channel control information */
pProg->ctl.lo = 0;
/* Enable source gather if configured */
if (pConfig->srcGatherWidth) {
pProg->ctl.lo |= dmacHw_REG_CTL_SG_ENABLE;
}
/* Enable destination scatter if configured */
if (pConfig->dstScatterWidth) {
pProg->ctl.lo |= dmacHw_REG_CTL_DS_ENABLE;
}
/* Set source and destination address */
pProg->sar = (uint32_t) pSrcAddr;
pProg->dar = (uint32_t) pDstAddr;
/* Use "devCtl" to mark that user memory need to be freed later if needed */
if (pProg == pRing->pHead) {
pProg->devCtl = dmacHw_FREE_USER_MEMORY;
} else {
pProg->devCtl = 0;
}
blkTs = srcTs;
/* Special treatmeant for last descriptor */
if (count == 1) {
/* Mark the last descriptor */
pProg->ctl.lo &=
~(dmacHw_REG_CTL_LLP_DST_EN |
dmacHw_REG_CTL_LLP_SRC_EN);
/* Treatment for odd data bytes */
if (oddSize) {
/* Adjust for single byte transaction width */
switch (pConfig->transferType) {
case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
dstTrWidth =
dmacHw_DST_TRANSACTION_WIDTH_8;
blkTs =
(oddSize / srcTrSize) +
((oddSize % srcTrSize) ? 1 : 0);
break;
case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
srcTrWidth =
dmacHw_SRC_TRANSACTION_WIDTH_8;
blkTs = oddSize;
break;
case dmacHw_TRANSFER_TYPE_MEM_TO_MEM:
srcTrWidth =
dmacHw_SRC_TRANSACTION_WIDTH_8;
dstTrWidth =
dmacHw_DST_TRANSACTION_WIDTH_8;
blkTs = oddSize;
break;
case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL:
/* Do not adjust the transaction width */
break;
}
} else {
srcTs -= blkTs;
}
} else {
if (srcTs / maxBlockSize) {
blkTs = maxBlockSize;
}
/* Remaining source transactions for next iteration */
srcTs -= blkTs;
}
/* Must have a valid source transactions */
dmacHw_ASSERT(blkTs > 0);
/* Set control information */
if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) {
pProg->ctl.lo |= pConfig->transferType |
pConfig->srcUpdate |
pConfig->dstUpdate |
srcTrWidth |
dstTrWidth |
pConfig->srcMaxBurstWidth |
pConfig->dstMaxBurstWidth |
pConfig->srcMasterInterface |
pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;
} else {
uint32_t transferType = 0;
switch (pConfig->transferType) {
case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
transferType = dmacHw_REG_CTL_TTFC_PM_PERI;
break;
case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
transferType = dmacHw_REG_CTL_TTFC_MP_PERI;
break;
default:
dmacHw_ASSERT(0);
}
pProg->ctl.lo |= transferType |
pConfig->srcUpdate |
pConfig->dstUpdate |
srcTrWidth |
dstTrWidth |
pConfig->srcMaxBurstWidth |
pConfig->dstMaxBurstWidth |
pConfig->srcMasterInterface |
pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;
}
/* Set block transaction size */
pProg->ctl.hi = blkTs & dmacHw_REG_CTL_BLOCK_TS_MASK;
/* Look for next descriptor */
if (count > 1) {
/* Point to the next descriptor */
pProg = (dmacHw_DESC_t *) pProg->llp;
/* Update source and destination address for next iteration */
switch (pConfig->transferType) {
case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
if (pConfig->dstScatterWidth) {
pDstAddr =
(char *)pDstAddr +
blkTs * srcTrSize +
(((blkTs * srcTrSize) /
pConfig->dstScatterWidth) *
pConfig->dstScatterJump);
} else {
pDstAddr =
(char *)pDstAddr +
blkTs * srcTrSize;
}
break;
case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
if (pConfig->srcGatherWidth) {
pSrcAddr =
(char *)pDstAddr +
blkTs * srcTrSize +
(((blkTs * srcTrSize) /
pConfig->srcGatherWidth) *
pConfig->srcGatherJump);
} else {
pSrcAddr =
(char *)pSrcAddr +
blkTs * srcTrSize;
}
break;
case dmacHw_TRANSFER_TYPE_MEM_TO_MEM:
if (pConfig->dstScatterWidth) {
pDstAddr =
(char *)pDstAddr +
blkTs * srcTrSize +
(((blkTs * srcTrSize) /
pConfig->dstScatterWidth) *
pConfig->dstScatterJump);
} else {
pDstAddr =
(char *)pDstAddr +
blkTs * srcTrSize;
}
if (pConfig->srcGatherWidth) {
pSrcAddr =
(char *)pDstAddr +
blkTs * srcTrSize +
(((blkTs * srcTrSize) /
pConfig->srcGatherWidth) *
pConfig->srcGatherJump);
} else {
pSrcAddr =
(char *)pSrcAddr +
blkTs * srcTrSize;
}
break;
case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL:
/* Do not adjust the address */
break;
default:
dmacHw_ASSERT(0);
}
} else {
/* At the end of transfer "srcTs" must be zero */
dmacHw_ASSERT(srcTs == 0);
}
count--;
}
/* Remember the descriptor to initialize the registers */
if (pRing->pProg == dmacHw_DESC_INIT) {
pRing->pProg = pStart;
}
/* Indicate that the descriptor is updated */
pRing->pEnd = pProg;
/* Head pointing to the next descriptor */
pRing->pHead = (dmacHw_DESC_t *) pProg->llp;
/* Update Tail pointer if destination is a peripheral,
because no one is going to read from the pTail
*/
if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) {
pRing->pTail = pRing->pHead;
}
return 0;
}
/****************************************************************************/
/**
* @brief Provides DMA controller attributes
*
*
* @return DMA controller attributes
*
* @note
* None
*/
/****************************************************************************/
uint32_t dmacHw_getDmaControllerAttribute(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
dmacHw_CONTROLLER_ATTRIB_e attr /* [ IN ] DMA Controler attribute of type dmacHw_CONTROLLER_ATTRIB_e */
) {
dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
switch (attr) {
case dmacHw_CONTROLLER_ATTRIB_CHANNEL_NUM:
return dmacHw_GET_NUM_CHANNEL(pCblk->module);
case dmacHw_CONTROLLER_ATTRIB_CHANNEL_MAX_BLOCK_SIZE:
return (1 <<
(dmacHw_GET_MAX_BLOCK_SIZE
(pCblk->module, pCblk->module) + 2)) - 8;
case dmacHw_CONTROLLER_ATTRIB_MASTER_INTF_NUM:
return dmacHw_GET_NUM_INTERFACE(pCblk->module);
case dmacHw_CONTROLLER_ATTRIB_CHANNEL_BUS_WIDTH:
return 32 << dmacHw_GET_CHANNEL_DATA_WIDTH(pCblk->module,
pCblk->channel);
case dmacHw_CONTROLLER_ATTRIB_CHANNEL_FIFO_SIZE:
return GetFifoSize(handle);
}
dmacHw_ASSERT(0);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
obj-y += tmrHw.o

View File

@@ -0,0 +1,576 @@
/*****************************************************************************
* 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 tmrHw.c
*
* @brief Low level Timer driver routines
*
* @note
*
* These routines provide basic timer functionality only.
*/
/****************************************************************************/
/* ---- Include Files ---------------------------------------------------- */
#include <csp/errno.h>
#include <csp/stdint.h>
#include <csp/tmrHw.h>
#include <mach/csp/tmrHw_reg.h>
#define tmrHw_ASSERT(a) if (!(a)) *(char *)0 = 0
#define tmrHw_MILLISEC_PER_SEC (1000)
#define tmrHw_LOW_1_RESOLUTION_COUNT (tmrHw_LOW_RESOLUTION_CLOCK / tmrHw_MILLISEC_PER_SEC)
#define tmrHw_LOW_1_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_1_RESOLUTION_COUNT)
#define tmrHw_LOW_16_RESOLUTION_COUNT (tmrHw_LOW_1_RESOLUTION_COUNT / 16)
#define tmrHw_LOW_16_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_16_RESOLUTION_COUNT)
#define tmrHw_LOW_256_RESOLUTION_COUNT (tmrHw_LOW_1_RESOLUTION_COUNT / 256)
#define tmrHw_LOW_256_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_256_RESOLUTION_COUNT)
#define tmrHw_HIGH_1_RESOLUTION_COUNT (tmrHw_HIGH_RESOLUTION_CLOCK / tmrHw_MILLISEC_PER_SEC)
#define tmrHw_HIGH_1_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_1_RESOLUTION_COUNT)
#define tmrHw_HIGH_16_RESOLUTION_COUNT (tmrHw_HIGH_1_RESOLUTION_COUNT / 16)
#define tmrHw_HIGH_16_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_16_RESOLUTION_COUNT)
#define tmrHw_HIGH_256_RESOLUTION_COUNT (tmrHw_HIGH_1_RESOLUTION_COUNT / 256)
#define tmrHw_HIGH_256_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_256_RESOLUTION_COUNT)
static void ResetTimer(tmrHw_ID_t timerId)
__attribute__ ((section(".aramtext")));
static int tmrHw_divide(int num, int denom)
__attribute__ ((section(".aramtext")));
/****************************************************************************/
/**
* @brief Get timer capability
*
* This function returns various capabilities/attributes of a timer
*
* @return Capability
*
*/
/****************************************************************************/
uint32_t tmrHw_getTimerCapability(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
tmrHw_CAPABILITY_e capability /* [ IN ] Timer capability */
) {
switch (capability) {
case tmrHw_CAPABILITY_CLOCK:
return (timerId <=
1) ? tmrHw_LOW_RESOLUTION_CLOCK :
tmrHw_HIGH_RESOLUTION_CLOCK;
case tmrHw_CAPABILITY_RESOLUTION:
return 32;
default:
return 0;
}
return 0;
}
/****************************************************************************/
/**
* @brief Resets a timer
*
* This function initializes timer
*
* @return void
*
*/
/****************************************************************************/
static void ResetTimer(tmrHw_ID_t timerId /* [ IN ] Timer Id */
) {
/* Reset timer */
pTmrHw[timerId].LoadValue = 0;
pTmrHw[timerId].CurrentValue = 0xFFFFFFFF;
pTmrHw[timerId].Control = 0;
pTmrHw[timerId].BackgroundLoad = 0;
/* Always configure as a 32 bit timer */
pTmrHw[timerId].Control |= tmrHw_CONTROL_32BIT;
/* Clear interrupt only if raw status interrupt is set */
if (pTmrHw[timerId].RawInterruptStatus) {
pTmrHw[timerId].InterruptClear = 0xFFFFFFFF;
}
}
/****************************************************************************/
/**
* @brief Sets counter value for an interval in ms
*
* @return On success: Effective counter value set
* On failure: 0
*
*/
/****************************************************************************/
static tmrHw_INTERVAL_t SetTimerPeriod(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */
) {
uint32_t scale = 0;
uint32_t count = 0;
if (timerId == 0 || timerId == 1) {
if (msec <= tmrHw_LOW_1_MAX_MILLISEC) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;
scale = tmrHw_LOW_1_RESOLUTION_COUNT;
} else if (msec <= tmrHw_LOW_16_MAX_MILLISEC) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;
scale = tmrHw_LOW_16_RESOLUTION_COUNT;
} else if (msec <= tmrHw_LOW_256_MAX_MILLISEC) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;
scale = tmrHw_LOW_256_RESOLUTION_COUNT;
} else {
return 0;
}
count = msec * scale;
/* Set counter value */
pTmrHw[timerId].LoadValue = count;
pTmrHw[timerId].BackgroundLoad = count;
} else if (timerId == 2 || timerId == 3) {
if (msec <= tmrHw_HIGH_1_MAX_MILLISEC) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;
scale = tmrHw_HIGH_1_RESOLUTION_COUNT;
} else if (msec <= tmrHw_HIGH_16_MAX_MILLISEC) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;
scale = tmrHw_HIGH_16_RESOLUTION_COUNT;
} else if (msec <= tmrHw_HIGH_256_MAX_MILLISEC) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;
scale = tmrHw_HIGH_256_RESOLUTION_COUNT;
} else {
return 0;
}
count = msec * scale;
/* Set counter value */
pTmrHw[timerId].LoadValue = count;
pTmrHw[timerId].BackgroundLoad = count;
}
return count / scale;
}
/****************************************************************************/
/**
* @brief Configures a periodic timer in terms of timer interrupt rate
*
* This function initializes a periodic timer to generate specific number of
* timer interrupt per second
*
* @return On success: Effective timer frequency
* On failure: 0
*
*/
/****************************************************************************/
tmrHw_RATE_t tmrHw_setPeriodicTimerRate(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
tmrHw_RATE_t rate /* [ IN ] Number of timer interrupt per second */
) {
uint32_t resolution = 0;
uint32_t count = 0;
ResetTimer(timerId);
/* Set timer mode periodic */
pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;
pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;
/* Set timer in highest resolution */
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;
if (rate && (timerId == 0 || timerId == 1)) {
if (rate > tmrHw_LOW_RESOLUTION_CLOCK) {
return 0;
}
resolution = tmrHw_LOW_RESOLUTION_CLOCK;
} else if (rate && (timerId == 2 || timerId == 3)) {
if (rate > tmrHw_HIGH_RESOLUTION_CLOCK) {
return 0;
} else {
resolution = tmrHw_HIGH_RESOLUTION_CLOCK;
}
} else {
return 0;
}
/* Find the counter value */
count = resolution / rate;
/* Set counter value */
pTmrHw[timerId].LoadValue = count;
pTmrHw[timerId].BackgroundLoad = count;
return resolution / count;
}
/****************************************************************************/
/**
* @brief Configures a periodic timer to generate timer interrupt after
* certain time interval
*
* This function initializes a periodic timer to generate timer interrupt
* after every time interval in millisecond
*
* @return On success: Effective interval set in milli-second
* On failure: 0
*
*/
/****************************************************************************/
tmrHw_INTERVAL_t tmrHw_setPeriodicTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */
) {
ResetTimer(timerId);
/* Set timer mode periodic */
pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;
pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;
return SetTimerPeriod(timerId, msec);
}
/****************************************************************************/
/**
* @brief Configures a periodic timer to generate timer interrupt just once
* after certain time interval
*
* This function initializes a periodic timer to generate a single ticks after
* certain time interval in millisecond
*
* @return On success: Effective interval set in milli-second
* On failure: 0
*
*/
/****************************************************************************/
tmrHw_INTERVAL_t tmrHw_setOneshotTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */
) {
ResetTimer(timerId);
/* Set timer mode oneshot */
pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;
pTmrHw[timerId].Control |= tmrHw_CONTROL_ONESHOT;
return SetTimerPeriod(timerId, msec);
}
/****************************************************************************/
/**
* @brief Configures a timer to run as a free running timer
*
* This function initializes a timer to run as a free running timer
*
* @return Timer resolution (count / sec)
*
*/
/****************************************************************************/
tmrHw_RATE_t tmrHw_setFreeRunningTimer(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
uint32_t divider /* [ IN ] Dividing the clock frequency */
) {
uint32_t scale = 0;
ResetTimer(timerId);
/* Set timer as free running mode */
pTmrHw[timerId].Control &= ~tmrHw_CONTROL_PERIODIC;
pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;
if (divider >= 64) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;
scale = 256;
} else if (divider >= 8) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;
scale = 16;
} else {
pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;
scale = 1;
}
if (timerId == 0 || timerId == 1) {
return tmrHw_divide(tmrHw_LOW_RESOLUTION_CLOCK, scale);
} else if (timerId == 2 || timerId == 3) {
return tmrHw_divide(tmrHw_HIGH_RESOLUTION_CLOCK, scale);
}
return 0;
}
/****************************************************************************/
/**
* @brief Starts a timer
*
* This function starts a preconfigured timer
*
* @return -1 - On Failure
* 0 - On Success
*
*/
/****************************************************************************/
int tmrHw_startTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */
) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_TIMER_ENABLE;
return 0;
}
/****************************************************************************/
/**
* @brief Stops a timer
*
* This function stops a running timer
*
* @return -1 - On Failure
* 0 - On Success
*
*/
/****************************************************************************/
int tmrHw_stopTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */
) {
pTmrHw[timerId].Control &= ~tmrHw_CONTROL_TIMER_ENABLE;
return 0;
}
/****************************************************************************/
/**
* @brief Gets current timer count
*
* This function returns the current timer value
*
* @return Current downcounting timer value
*
*/
/****************************************************************************/
uint32_t tmrHw_GetCurrentCount(tmrHw_ID_t timerId /* [ IN ] Timer id */
) {
/* return 32 bit timer value */
switch (pTmrHw[timerId].Control & tmrHw_CONTROL_MODE_MASK) {
case tmrHw_CONTROL_FREE_RUNNING:
if (pTmrHw[timerId].CurrentValue) {
return tmrHw_MAX_COUNT - pTmrHw[timerId].CurrentValue;
}
break;
case tmrHw_CONTROL_PERIODIC:
case tmrHw_CONTROL_ONESHOT:
return pTmrHw[timerId].BackgroundLoad -
pTmrHw[timerId].CurrentValue;
}
return 0;
}
/****************************************************************************/
/**
* @brief Gets timer count rate
*
* This function returns the number of counts per second
*
* @return Count rate
*
*/
/****************************************************************************/
tmrHw_RATE_t tmrHw_getCountRate(tmrHw_ID_t timerId /* [ IN ] Timer id */
) {
uint32_t divider = 0;
switch (pTmrHw[timerId].Control & tmrHw_CONTROL_PRESCALE_MASK) {
case tmrHw_CONTROL_PRESCALE_1:
divider = 1;
break;
case tmrHw_CONTROL_PRESCALE_16:
divider = 16;
break;
case tmrHw_CONTROL_PRESCALE_256:
divider = 256;
break;
default:
tmrHw_ASSERT(0);
}
if (timerId == 0 || timerId == 1) {
return tmrHw_divide(tmrHw_LOW_RESOLUTION_CLOCK, divider);
} else {
return tmrHw_divide(tmrHw_HIGH_RESOLUTION_CLOCK, divider);
}
return 0;
}
/****************************************************************************/
/**
* @brief Enables timer interrupt
*
* This function enables the timer interrupt
*
* @return N/A
*
*/
/****************************************************************************/
void tmrHw_enableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
) {
pTmrHw[timerId].Control |= tmrHw_CONTROL_INTERRUPT_ENABLE;
}
/****************************************************************************/
/**
* @brief Disables timer interrupt
*
* This function disable the timer interrupt
*
* @return N/A
*
*/
/****************************************************************************/
void tmrHw_disableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
) {
pTmrHw[timerId].Control &= ~tmrHw_CONTROL_INTERRUPT_ENABLE;
}
/****************************************************************************/
/**
* @brief Clears the interrupt
*
* This function clears the timer interrupt
*
* @return N/A
*
* @note
* Must be called under the context of ISR
*/
/****************************************************************************/
void tmrHw_clearInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
) {
pTmrHw[timerId].InterruptClear = 0x1;
}
/****************************************************************************/
/**
* @brief Gets the interrupt status
*
* This function returns timer interrupt status
*
* @return Interrupt status
*/
/****************************************************************************/
tmrHw_INTERRUPT_STATUS_e tmrHw_getInterruptStatus(tmrHw_ID_t timerId /* [ IN ] Timer id */
) {
if (pTmrHw[timerId].InterruptStatus) {
return tmrHw_INTERRUPT_STATUS_SET;
} else {
return tmrHw_INTERRUPT_STATUS_UNSET;
}
}
/****************************************************************************/
/**
* @brief Indentifies a timer causing interrupt
*
* This functions returns a timer causing interrupt
*
* @return 0xFFFFFFFF : No timer causing an interrupt
* ! 0xFFFFFFFF : timer causing an interrupt
* @note
* tmrHw_clearIntrrupt() must be called with a valid timer id after calling this function
*/
/****************************************************************************/
tmrHw_ID_t tmrHw_getInterruptSource(void /* void */
) {
int i;
for (i = 0; i < tmrHw_TIMER_NUM_COUNT; i++) {
if (pTmrHw[i].InterruptStatus) {
return i;
}
}
return 0xFFFFFFFF;
}
/****************************************************************************/
/**
* @brief Displays specific timer registers
*
*
* @return void
*
*/
/****************************************************************************/
void tmrHw_printDebugInfo(tmrHw_ID_t timerId, /* [ IN ] Timer id */
int (*fpPrint) (const char *, ...) /* [ IN ] Print callback function */
) {
(*fpPrint) ("Displaying register contents \n\n");
(*fpPrint) ("Timer %d: Load value 0x%X\n", timerId,
pTmrHw[timerId].LoadValue);
(*fpPrint) ("Timer %d: Background load value 0x%X\n", timerId,
pTmrHw[timerId].BackgroundLoad);
(*fpPrint) ("Timer %d: Control 0x%X\n", timerId,
pTmrHw[timerId].Control);
(*fpPrint) ("Timer %d: Interrupt clear 0x%X\n", timerId,
pTmrHw[timerId].InterruptClear);
(*fpPrint) ("Timer %d: Interrupt raw interrupt 0x%X\n", timerId,
pTmrHw[timerId].RawInterruptStatus);
(*fpPrint) ("Timer %d: Interrupt status 0x%X\n", timerId,
pTmrHw[timerId].InterruptStatus);
}
/****************************************************************************/
/**
* @brief Use a timer to perform a busy wait delay for a number of usecs.
*
* @return N/A
*/
/****************************************************************************/
void tmrHw_udelay(tmrHw_ID_t timerId, /* [ IN ] Timer id */
unsigned long usecs /* [ IN ] usec to delay */
) {
tmrHw_RATE_t usec_tick_rate;
tmrHw_COUNT_t start_time;
tmrHw_COUNT_t delta_time;
start_time = tmrHw_GetCurrentCount(timerId);
usec_tick_rate = tmrHw_divide(tmrHw_getCountRate(timerId), 1000000);
delta_time = usecs * usec_tick_rate;
/* Busy wait */
while (delta_time > (tmrHw_GetCurrentCount(timerId) - start_time))
;
}
/****************************************************************************/
/**
* @brief Local Divide function
*
* This function does the divide
*
* @return divide value
*
*/
/****************************************************************************/
static int tmrHw_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;
}