ubl/davinci.c

529 lines
13 KiB
C
Raw Normal View History

2012-04-25 07:20:50 +00:00
/*
* davinci.c - common DaVinci platform initialization
*
* Copyright (C) 2008 Hugo Villeneuve <hugo@hugovil.com>
*
* Based on TI DaVinci Flash and Boot Utilities, original copyright follows:
* Copyright 2008 Texas Instruments, Inc. <www.ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "common.h"
#include "davinci.h"
#include "ddr.h"
#include "util.h"
#include "uart.h"
#include "gpio.h"
extern enum bootmode_t bootmode;
extern const int8_t lpsc_en_list[];
extern const int8_t lpsc_emurstie_list[];
extern const size_t lpsc_en_list_len;
extern const size_t lpsc_emurstie_list_len;
/* Symbol from linker script */
extern uint32_t __DDR_START;
static void
pinmuxControl(uint32_t regOffset, uint32_t mask, uint32_t value)
{
SYSTEM->PINMUX[regOffset] &= ~mask;
SYSTEM->PINMUX[regOffset] |= (mask & value);
}
static void
lpsc_tansition(uint8_t module, uint8_t domain, uint8_t state)
{
/* Wait for any outstanding transition to complete */
while ((PSC->PTSTAT) & (0x00000001 << domain))
;
/* If we are already in that state, just return */
if (((PSC->MDSTAT[module]) & 0x1F) == state)
return;
/* Perform transition */
PSC->MDCTL[module] = ((PSC->MDCTL[module]) & (0xFFFFFFE0)) | (state);
PSC->PTCMD |= (0x00000001 << domain);
/* Wait for transition to complete */
while ((PSC->PTSTAT) & (0x00000001 << domain))
;
/* Wait and verify the state */
while (((PSC->MDSTAT[module]) & 0x1F) != state)
;
}
static void
ivt_init(void)
{
volatile uint32_t *ivect;
extern uint32_t __IVT;
if (bootmode == NON_SECURE_NOR) {
ivect = &(__IVT);
*ivect++ = 0xEAFFFFFE; /* Reset @ 0x00*/
} else
ivect = &(__IVT) + 4;
*ivect++ = 0xEAFFFFFE; /* Undefined Address @ 0x04 */
*ivect++ = 0xEAFFFFFE; /* Software Interrupt @0x08 */
*ivect++ = 0xEAFFFFFE; /* Pre-Fetch Abort @ 0x0C */
*ivect++ = 0xEAFFFFFE; /* Data Abort @ 0x10 */
*ivect++ = 0xEAFFFFFE; /* Reserved @ 0x14 */
*ivect++ = 0xEAFFFFFE; /* IRQ @ 0x18 */
*ivect = 0xEAFFFFFE; /* FIQ @ 0x1C */
}
static int
timer0_init(void)
{
TIMER0->TGCR = 0x00000000; /* Reset timer */
TIMER0->TCR = 0x00000000; /* Disable timer */
TIMER0->TIM12 = 0x00000000; /* Reset timer count to zero */
/* Set timer period (5 seconds timeout) */
TIMER0->PRD12 = SYSTEM_CLK_HZ * 5;
return E_PASS;
}
void
timer0_start(void)
{
AINTC->IRQ1 |= 0x00000001; /* Clear interrupt */
TIMER0->TGCR = 0x00000000; /* Reset timer */
TIMER0->TIM12 = 0x00000000; /* Reset timer count to zero */
TIMER0->TCR = 0x00000040; /* Setup for one-shot mode */
TIMER0->TGCR = 0x00000005; /* Start TIMER12 in 32-bits mode. */
}
uint32_t
timer0_status(void)
{
return AINTC->IRQ1 & 0x1;
}
static int
uart0_init(void)
{
UART0->PWREMU_MGNT = 0; /* Reset UART TX & RX components */
waitloop(100);
/* Set DLAB bit - allows setting of clock divisors */
UART0->LCR |= 0x80;
/*
* Compute divisor value. Normally, we should simply return:
* SYSTEM_CLK_HZ / (16 * baudrate)
* but we need to round that value by adding 0.5.
* Rounding is especially important at high baud rates.
*/
UART0->DLL = (SYSTEM_CLK_HZ + (UART_BAUDRATE * (UART_BCLK_RATIO / 2))) /
(UART_BCLK_RATIO * UART_BAUDRATE);
UART0->DLH = 0x00;
UART0->FCR = 0x0007; /* Clear UART TX & RX FIFOs */
UART0->MCR = 0x0000; /* RTS & CTS disabled,
* Loopback mode disabled,
* Autoflow disabled
*/
UART0->LCR = 0x0003; /* Clear DLAB bit
* 8-bit words,
* 1 STOP bit generated,
* No Parity, No Stick paritiy,
* No Break control
*/
/* Enable receiver, transmitter, set to run. */
UART0->PWREMU_MGNT |= 0x6001;
return E_PASS;
}
static int
pll_init(volatile struct pll_regs_t *pll, int pll_mult, int plldiv_ratio[5])
{
int k;
volatile uint32_t *plldiv_reg[5];
int pll_is_powered_up =
(pll->PLLCTL & DEVICE_PLLCTL_PLLPWRDN_MASK) >> 1;
plldiv_reg[0] = &pll->PLLDIV1;
plldiv_reg[1] = &pll->PLLDIV2;
plldiv_reg[2] = &pll->PLLDIV3;
plldiv_reg[3] = &pll->PLLDIV4;
plldiv_reg[4] = &pll->PLLDIV5;
/* Set PLL clock input to internal osc. */
pll->PLLCTL &= ~(DEVICE_PLLCTL_CLKMODE_MASK);
/* Set PLL to bypass, then wait for PLL to stabilize */
pll->PLLCTL &= ~(DEVICE_PLLCTL_PLLENSRC_MASK |
DEVICE_PLLCTL_PLLEN_MASK);
waitloop(150);
/* Reset PLL: Warning, bit state is inverted for DM644x vs DM35x. */
#if defined(DM644x)
pll->PLLCTL &= ~DEVICE_PLLCTL_PLLRST_MASK;
#elif defined(DM35x)
pll->PLLCTL |= DEVICE_PLLCTL_PLLRST_MASK;
#endif
if (pll_is_powered_up) {
/* Disable PLL */
pll->PLLCTL |= DEVICE_PLLCTL_PLLDIS_MASK;
/* Powerup PLL */
pll->PLLCTL &= ~(DEVICE_PLLCTL_PLLPWRDN_MASK);
}
/* Enable PLL */
pll->PLLCTL &= ~(DEVICE_PLLCTL_PLLDIS_MASK);
/* Wait for PLL to stabilize */
waitloop(150);
/* Load PLL multiplier. */
pll->PLLM = (pll_mult - 1) & 0xff;
/* Set and enable dividers as needed. */
for (k = 0; k < 5; k++) {
if (plldiv_ratio[k] > 0)
*(plldiv_reg[k]) |= DEVICE_PLLDIV_EN_MASK |
(plldiv_ratio[k] - 1);
}
#if defined(DM35x)
/* Set the processor AIM wait state and PLL1 post-divider to to 1 */
SYSTEM->MISC &= ~(DEVICE_MISC_PLL1POSTDIV_MASK |
DEVICE_MISC_AIMWAITST_MASK);
#endif
/* Initiate a new divider transition. */
pll->PLLCMD |= DEVICE_PLLCMD_GOSET_MASK;
/* Wait for completion of phase alignment. */
while ((pll->PLLSTAT & DEVICE_PLLSTAT_GOSTAT_MASK))
;
/* Wait for PLL to reset ( ~5 usec ) */
waitloop(5000);
/* Release PLL from reset */
/* Reset PLL: Warning, bit state is inverted for DM644x vs DM35x. */
#if defined(DM644x)
pll->PLLCTL |= DEVICE_PLLCTL_PLLRST_MASK;
#elif defined(DM35x)
pll->PLLCTL &= ~DEVICE_PLLCTL_PLLRST_MASK;
#endif
/* Wait for PLL to re-lock:
* DM644z: 2000P
* DM35x: 8000P
*/
waitloop(8000);
/* Switch out of BYPASS mode */
pll->PLLCTL |= DEVICE_PLLCTL_PLLEN_MASK;
return E_PASS;
}
static int
pll1_init(void)
{
int plldiv_ratio[5];
#if defined(DM644x)
plldiv_ratio[0] = 1; /* PLLDIV1 fixed */
plldiv_ratio[1] = 2; /* PLLDIV2 fixed */
plldiv_ratio[2] = 3; /* PLLDIV3 fixed */
plldiv_ratio[3] = -1; /* PLLDIV4 not used */
plldiv_ratio[4] = 6; /* PLLDIV5 fixed */
#elif defined(DM35x)
plldiv_ratio[0] = 2; /* PLLDIV1 fixed */
plldiv_ratio[1] = 4; /* PLLDIV2 fixed */
/* Calculate PLL divider ratio for divider 3 (feeds VPBE) */
plldiv_ratio[2] = 0;
while ((plldiv_ratio[2] * VPBE_CLK_HZ) <
(SYSTEM_CLK_HZ * (PLL1_Mult >> 3)))
plldiv_ratio[2]++;
/* Check to make sure we can supply accurate VPBE clock */
if ((plldiv_ratio[2] * VPBE_CLK_HZ) !=
(SYSTEM_CLK_HZ * (PLL1_Mult >> 3)))
return E_FAIL;
/* See the device datasheet for more info (must be 2 or 4) */
plldiv_ratio[3] = 4;
plldiv_ratio[4] = -1; /* PLLDIV5 not used */
#endif
return pll_init(PLL1, PLL1_Mult, plldiv_ratio);
}
static int
pll2_init(void)
{
int plldiv_ratio[5];
plldiv_ratio[0] = PLL2_Div1;
plldiv_ratio[1] = PLL2_Div2;
plldiv_ratio[2] = -1; /* PLLDIV3 not used */
plldiv_ratio[3] = -1; /* PLLDIV4 not used */
plldiv_ratio[4] = -1; /* PLLDIV5 not used */
return pll_init(PLL2, PLL2_Mult, plldiv_ratio);
}
static void
ddr_timing_setup(void)
{
/* The configuration of DDRPHYCR is not dependent on the DDR2 device
* specification but rather on the board layout.
* Setup the read latency and clear DLLPWRDN */
DDR->DDRPHYCR = DDRPHYCR_DEFAULT |
(DDR_READ_Latency & DDRPHYCR_READLAT_MASK);
/*
* Set the PR_OLD_COUNT bits in the Bus Burst Priority Register (PBBPR)
* as suggested in TMS320DM6446 errata 2.1.2:
*
* On DM6446 Silicon Revision 2.1 and earlier, under certain conditions
* low priority modules can occupy the bus and prevent high priority
* modules like the VPSS from getting the required DDR2 throughput.
*/
DDR->PBBPR = DDR_PBBPR_PR_OLD_COUNT;
/* TIMUNLOCK (unlocked), CAS Latency, number of banks and page size */
DDR->SDBCR = SDBCR_DEFAULT |
SDBCR_TIMUNLOCK |
(DDR_NM << 14) |
(DDR_CL << 9) |
(DDR_IBANK << 4) |
(DDR_PAGESIZE << 0);
/* Program timing registers */
DDR->SDTIMR = (DDR_T_RFC << 25) |
(DDR_T_RP << 22) |
(DDR_T_RCD << 19) |
(DDR_T_WR << 16) |
(DDR_T_RAS << 11) |
(DDR_T_RC << 6) |
(DDR_T_RRD << 3) |
(DDR_T_WTR << 0);
DDR->SDTIMR2 = (DDR_T_XSNR << 16) |
(DDR_T_XSRD << 8) |
(DDR_T_RTP << 5) |
(DDR_T_CKE << 0);
#if defined(DM35x)
DDR->SDTIMR2 |= (DDR_T_RASMAX << 27) |
(DDR_T_XP << 25);
#endif
/* Clear the TIMUNLOCK bit (locked) */
DDR->SDBCR &= ~SDBCR_TIMUNLOCK;
/* Set the refresh rate */
DDR->SDRCR = DDR_RR;
}
static void
ddr_reset(void)
{
/* Perform a soft reset to the DDR2 memory controller:
* Put in SYNCRESET and enable it again. */
lpsc_tansition(LPSC_DDR2, PD0, PSC_SYNCRESET);
lpsc_tansition(LPSC_DDR2, PD0, PSC_ENABLE);
}
static int
ddr_init(void)
{
volatile uint32_t *ddr_start = &__DDR_START;
/* For reading/writing dummy value in order to apply timing settings */
volatile uint32_t ddr_dummy_read;
/* Enable DDR2 module. */
lpsc_tansition(LPSC_DDR2, PD0, PSC_ENABLE);
#if defined(DM35x)
ddr_vtp_calibration();
ddr_reset();
#endif
ddr_timing_setup();
/* Dummy read to apply timing settings */
ddr_dummy_read = ddr_start[0];
#if defined(DM644x)
ddr_reset();
ddr_vtp_calibration();
#endif
/* Verify correct initialization. */
ddr_start[0] = DDR_TEST_PATTERN;
if (ddr_start[0] != DDR_TEST_PATTERN) {
log_fail("DDR init failed");
return E_FAIL;
}
return E_PASS;
}
static void
psc_init(void)
{
uint32_t i;
#if defined(DM35x)
/* Do always on power domain transitions */
while ((PSC->PTSTAT) & 0x00000001);
#elif defined(DM644x)
/*
* Workaround for TMS320DM6446 errata 1.3.22
* (Revision(s) Affected: 1.3 and earlier):
* PSC: PTSTAT Register Does Not Clear After Warm/Maximum Reset.
* Clear the reserved location at address 0x01C41A20
*/
PSC_PTSTAT_WORKAROUND_REG = 0;
/* Put the C64x+ Core into reset (if it's on) */
PSC->MDCTL[LPSC_DSP] &= (~0x00000100);
PSC->PTCMD |= 0x00000002;
while ((PSC->PTSTAT) & (0x00000002));
while ((PSC->MDSTAT[LPSC_DSP]) & (0x00000100));
#endif
/* Enable selected modules */
for (i = 0; i < lpsc_en_list_len; i++) {
int8_t k = lpsc_en_list[i];
PSC->MDCTL[k] = (PSC->MDCTL[k] & 0xFFFFFFE0) | PSC_ENABLE;
}
/* Set EMURSTIE on selected modules */
for (i = 0; i < lpsc_emurstie_list_len; i++) {
int8_t k = lpsc_emurstie_list[i];
PSC->MDCTL[k] |= EMURSTIE_MASK;
}
/* Do Always-On Power Domain Transitions */
PSC->PTCMD |= 0x00000001;
while ((PSC->PTSTAT) & 0x00000001);
#if defined(DM644x)
/* DO DSP Power Domain Transitions */
PSC->PTCMD |= 0x00000002;
while ((PSC->PTSTAT) & (0x00000002));
#endif
/* Clear EMURSTIE on selected modules */
for (i = 0; i < lpsc_emurstie_list_len; i++) {
int8_t k = lpsc_emurstie_list[i];
PSC->MDCTL[k] &= (~EMURSTIE_MASK);
}
}
int
davinci_platform_init(char *version)
{
int status = E_PASS;
psc_init();
/* Disable ARM interrupts */
AINTC->INTCTL = 0x4;
AINTC->EABASE = 0x0;
AINTC->EINT0 = 0x0;
AINTC->EINT1 = 0x0;
AINTC->FIQ0 = 0xFFFFFFFF;
AINTC->FIQ1 = 0xFFFFFFFF;
AINTC->IRQ0 = 0xFFFFFFFF;
AINTC->IRQ1 = 0xFFFFFFFF;
#ifdef PINMUX0_DEFAULT
pinmuxControl(0, 0xFFFFFFFF, PINMUX0_DEFAULT);
#endif
#ifdef PINMUX1_DEFAULT
pinmuxControl(1, 0xFFFFFFFF, PINMUX1_DEFAULT);
#endif
/* The folowing are only available on DM35x */
#ifdef PINMUX2_DEFAULT
pinmuxControl(2, 0xFFFFFFFF, PINMUX2_DEFAULT);
#endif
#ifdef PINMUX3_DEFAULT
pinmuxControl(3, 0xFFFFFFFF, PINMUX3_DEFAULT);
#endif
#ifdef PINMUX4_DEFAULT
pinmuxControl(4, 0xFFFFFFFF, PINMUX4_DEFAULT);
#endif
if (status == E_PASS)
status |= pll1_init();
if (status == E_PASS)
status |= uart0_init();
if (status == E_PASS)
status |= timer0_init();
uart_send_lf();
log_info(version);
if (status == E_PASS)
status |= pll2_init();
if (status == E_PASS)
status |= ddr_init();
#ifdef STATUS_LED
gpio_direction_out(STATUS_LED, 1);
#endif /* STATUS_LED */
#ifdef board_minidas
gpio_direction_out(FAN, 0);
gpio_direction_out(BUZZER, 0);
/* Put all peripherals in RESET state */
gpio_direction_out(DSP1_PWR_ENA, 0);
gpio_direction_out(DSP2_PWR_ENA, 0);
gpio_direction_out(WIFI_RESETn, 0);
gpio_direction_out(GPS_RESETn, 0);
gpio_direction_out(CAN_RESETn, 0);
gpio_direction_out(ATA_RESETn, 0);
gpio_direction_out(CAMERA_RESETn, 0);
/* Enable power for hard disk */
gpio_direction_out(HDD_ENA, 1);
#endif
/* IRQ Vector Table Setup */
ivt_init();
return status;
}