rockbox/flash/bootloader/bootloader.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

518 lines
15 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2003 by Jörg Hohensohn
*
* Second-level bootloader, with dual-boot feature by holding F1/Menu
* This is the image being descrambled and executed by the boot ROM.
* It's task is to copy Rockbox from Flash to DRAM.
* The image(s) in flash may optionally be compressed with UCL 2e
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "sh7034.h"
#include "bootloader.h"
#ifdef NO_ROM
/* start with the vector table */
UINT32 vectors[] __attribute__ ((section (".vectors"))) =
{
(UINT32)_main, /* entry point, the copy routine */
(UINT32)(end_stack - 1), /* initial stack pointer */
FLASH_BASE + 0x200, /* source of image in flash */
(UINT32)total_size, /* size of image */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0x03020080 /* mask and version (just as a suggestion) */
};
#else
/* our binary has to start with a vector to the entry point */
tpMain start_vector[] __attribute__ ((section (".startvector"))) = {main};
#endif
#ifdef NO_ROM /* some code which is only needed for the romless variant */
void _main(void)
{
UINT32* pSrc;
UINT32* pDest;
UINT32* pEnd;
/*
asm volatile ("ldc %0,sr" : : "r"(0xF0)); // disable interrupts
asm volatile ("mov.l @%0,r15" : : "r"(4)); // load stack
asm volatile ("ldc %0,vbr" : : "r"(0)); // load vector base
*/
/* copy everything to IRAM and continue there */
pSrc = begin_iramcopy;
pDest = begin_text;
pEnd = pDest + (begin_stack - begin_text);
do
{
*pDest++ = *pSrc++;
}
while (pDest < pEnd);
main(); /* jump to the real main() */
}
void BootInit(void)
{
/* inits from the boot ROM, whether they make sense or not */
PBDR &= 0xFFBF; /* LED off (0x131E) */
PBCR2 = 0; /* all GPIO */
PBIOR |= 0x0040; /* LED output */
PBIOR &= 0xFFF1; /* LCD lines input */
/* init DRAM like the boot ROM does */
PACR2 &= 0xFFFB;
PACR2 |= 0x0008;
CASCR = 0xAF;
BCR |= 0x8000;
WCR1 &= 0xFDFD;
DCR = 0x0E00;
RCR = 0x5AB0;
RTCOR = 0x9605;
RTCSR = 0xA518;
}
#endif /* #ifdef NO_ROM */
int main(void)
{
int nButton;
PlatformInit(); /* model-specific inits */
nButton = ButtonPressed();
if (nButton == 3)
{ /* F3 means start monitor */
MiniMon();
}
else
{
tImage* pImage;
pImage = GetStartImage(nButton); /* which image */
DecompressStart(pImage); /* move into place and start it */
}
return 0; /* I guess we won't return ;-) */
}
/* init code that is specific to certain platform */
void PlatformInit(void)
{
#ifdef NO_ROM
BootInit(); /* if not started by boot ROM, we need to init what it did */
#endif
#if defined PLATFORM_PLAYER
BRR1 = 0x19; /* 14400 Baud for monitor */
PBDRL |= 0x10; /* set PB4 to 1 to power the hd early (and prepare for
* probing in case the charger is connected) */
PBIORL |= 0x10; /* make PB4 an output */
PACR2 &= 0xFFFC; /* GPIO for PA0 (charger detection, input by default) */
if (!(PADRL & 0x01)) /* charger plugged? */
{ /* we need to probe whether the box is able to control hd power */
int i;
PBIORL &= ~0x10; /* set PB4 to input */
/* wait whether it goes low, max. ~1 ms */
for (i = 0; (PBDRL & 0x10) && i < 1000; i++);
if (~(PBDRL & 0x10)) /* pulled low -> power controllable */
PBDRL &= 0x10; /* set PB4 low */
else /* still floating high -> not controllable */
PBDRL |= 0x10; /* set PB4 high */
PBIORL |= 0x10; /* ..and output again */
}
#elif defined PLATFORM_RECORDER
BRR1 = 0x02; /* 115200 Baud for monitor */
if (ReadADC(7) > 0x100) /* charger plugged? */
{ /* switch off the HD, else a flat battery may not start */
PACR2 &= 0xFBFF; /* GPIO for PA5 */
PAIOR |= 0x0020; /* make PA5 an output (low by default) */
}
#elif defined PLATFORM_FM
BRR1 = 0x02; /* 115200 Baud for monitor */
PBDR |= 0x0020; /* set PB5 to keep power (fixes the ON-holding problem) */
PBIOR |= 0x0020; /* make PB5 an output */
if (ReadADC(0) < 0x1FF) /* charger plugged? */
{ /* switch off the HD, else a flat battery may not start */
PACR2 &= 0xFBFF; /* GPIO for PA5 */
PAIOR |= 0x0020; /* make PA5 an output (low by default) */
}
#elif defined PLATFORM_ONDIO
BRR1 = 0x19; /* 14400 Baud for monitor */
PBDR |= 0x0020; /* set PB5 to keep power (fixes the ON-holding problem) */
PBIOR |= 0x0020; /* make PB5 an output */
#endif
/* platform-independent inits */
DCR |= 0x1000; /* enable burst mode on DRAM */
BCR |= 0x2000; /* activate Warp mode (simultaneous internal and external
* mem access) */
}
/* Thinned out version of the UCL 2e decompression sourcecode
* Original (C) Markus F.X.J Oberhumer under GNU GPL license */
#define GETBIT(bb, src, ilen) \
(((bb = bb & 0x7f ? bb*2 : ((unsigned)src[ilen++]*2+1)) >> 8) & 1)
int ucl_nrv2e_decompress_8(
const UINT8 *src, UINT8 *dst, UINT32* dst_len)
{
UINT32 bb = 0;
unsigned ilen = 0, olen = 0, last_m_off = 1;
for (;;)
{
unsigned m_off, m_len;
while (GETBIT(bb,src,ilen))
{
dst[olen++] = src[ilen++];
}
m_off = 1;
for (;;)
{
m_off = m_off*2 + GETBIT(bb,src,ilen);
if (GETBIT(bb,src,ilen)) break;
m_off = (m_off-1)*2 + GETBIT(bb,src,ilen);
}
if (m_off == 2)
{
m_off = last_m_off;
m_len = GETBIT(bb,src,ilen);
}
else
{
m_off = (m_off-3)*256 + src[ilen++];
if (m_off == 0xffffffff)
break;
m_len = (m_off ^ 0xffffffff) & 1;
m_off >>= 1;
last_m_off = ++m_off;
}
if (m_len)
m_len = 1 + GETBIT(bb,src,ilen);
else if (GETBIT(bb,src,ilen))
m_len = 3 + GETBIT(bb,src,ilen);
else
{
m_len++;
do {
m_len = m_len*2 + GETBIT(bb,src,ilen);
} while (!GETBIT(bb,src,ilen));
m_len += 3;
}
m_len += (m_off > 0x500);
{
const UINT8 *m_pos;
m_pos = dst + olen - m_off;
dst[olen++] = *m_pos++;
do dst[olen++] = *m_pos++; while (--m_len > 0);
}
}
*dst_len = olen;
return ilen;
}
/* move the image into place and start it */
void DecompressStart(tImage* pImage)
{
UINT32* pSrc;
UINT32* pDest;
pSrc = pImage->image;
pDest = pImage->pDestination;
if (pSrc != pDest) /* if not linked to that flash address */
{
if (pImage->flags & IF_UCL_2E)
{ /* UCL compressed, algorithm 2e */
UINT32 dst_len; /* dummy */
ucl_nrv2e_decompress_8((UINT8*)pSrc, (UINT8*)pDest, &dst_len);
}
else
{ /* uncompressed, copy it */
UINT32 size = pImage->size;
UINT32* pEnd;
size = (size + 3) / 4; /* round up to 32bit-words */
pEnd = pDest + size;
do
{
*pDest++ = *pSrc++;
}
while (pDest < pEnd);
}
}
pImage->pExecute();
}
#ifdef USE_ADC
int ReadADC(int channel)
{
/* after channel 3, the ports wrap and get re-used */
volatile UINT16* pResult = (UINT16*)(ADDRAH_ADDR + 2 * (channel & 0x03));
int timeout = 266; /* conversion takes 266 clock cycles */
ADCSR = 0x20 | channel; /* start single conversion */
while (((ADCSR & 0x80) == 0) && (--timeout)); /* 6 instructions per round*/
return (timeout == 0) ? -1 : *pResult>>6;
}
#endif
/* This function is platform-dependent,
* until I figure out how to distinguish at runtime. */
int ButtonPressed(void) /* return 1,2,3 for F1,F2,F3, 0 if none pressed */
{
#ifdef USE_ADC
int value = ReadADC(CHANNEL);
if (value >= F1_LOWER && value <= F1_UPPER) /* in range */
return 1;
else if (value >= F2_LOWER && value <= F2_UPPER) /* in range */
return 2;
else if (value >= F3_LOWER && value <= F3_UPPER) /* in range */
return 3;
#else
int value = PCDR;
if (!(value & F1_MASK))
return 1;
else if (!(value & F2_MASK))
return 2;
else if (!(value & F3_MASK))
return 3;
#endif
return 0;
}
/* Determine the image to be started */
tImage* GetStartImage(int nPreferred)
{
tImage* pImage1;
tImage* pImage2 = NULL; /* default to not present */
UINT32 pos;
UINT32* pFlash = (UINT32*)FLASH_BASE;
/* determine the first image position */
pos = pFlash[2] + pFlash[3]; /* position + size of the bootloader
* = after it */
pos = (pos + 3) & ~3; /* be sure it's 32 bit aligned */
pImage1 = (tImage*)pos;
if (pImage1->size != 0)
{ /* check for second image */
pos = (UINT32)(&pImage1->image) + pImage1->size;
pImage2 = (tImage*)pos;
/* does it make sense? (not in FF or 00 erazed space) */
if (pImage2->pDestination == (void*)0xFFFFFFFF
|| pImage2->size == 0xFFFFFFFF
|| pImage2->pExecute == (void*)0xFFFFFFFF
|| pImage2->flags == 0xFFFFFFFF
|| pImage2->pDestination == NULL)
/* size, execute and flags can legally be 0 */
{
pImage2 = NULL; /* invalidate */
}
}
if (pImage2 == NULL || nPreferred == 1)
{ /* no second image or overridden: return the first */
return pImage1;
}
return pImage2; /* return second image */
}
/* diagnostic functions */
void SetLed(BOOL bOn)
{
if (bOn)
PBDR |= 0x0040;
else
PBDR &= ~0x0040;
}
void UartInit(void)
{
PBIOR &= 0xFBFF; /* input: RXD1 remote pin */
PBCR1 |= 0x00A0; /* set PB11+PB10 to UART */
PBCR1 &= 0xFFAF; /* clear bits 6, 4 -> UART */
SMR1 = 0x00; /* async format 8N1, baud generator input is CPU clock */
SCR1 = 0x30; /* transmit+receive enable */
PBCR1 &= 0x00FF; /* set bit 12...15 as GPIO */
SSR1 &= 0xBF; /* clear bit 6 (RDRF, receive data register full) */
}
UINT8 UartRead(void)
{
UINT8 byte;
while (!(SSR1 & SCI_RDRF)); /* wait for char to be available */
byte = RDR1;
SSR1 &= ~SCI_RDRF;
return byte;
}
void UartWrite(UINT8 byte)
{
while (!(SSR1 & SCI_TDRE)); /* wait for transmit buffer empty */
TDR1 = byte;
SSR1 &= ~SCI_TDRE;
}
/* include the mini monitor as a rescue feature, started with F3 */
void MiniMon(void)
{
UINT8 cmd;
UINT32 addr;
UINT32 size;
UINT32 content;
volatile UINT8* paddr = NULL;
volatile UINT8* pflash = NULL; /* flash base address */
UartInit();
while (1)
{
cmd = UartRead();
switch (cmd)
{
case BAUDRATE:
content = UartRead();
UartWrite(cmd); /* acknowledge by returning the command value */
while (!(SSR1 & SCI_TEND)); /* wait for empty shift register,
* before changing baudrate */
BRR1 = content;
break;
case ADDRESS:
addr = (UartRead() << 24) | (UartRead() << 16)
| (UartRead() << 8) | UartRead();
paddr = (UINT8*)addr;
pflash = (UINT8*)(addr & 0xFFF80000); /* round down to 512k align*/
UartWrite(cmd); /* acknowledge by returning the command value */
break;
case BYTE_READ:
content = *paddr++;
UartWrite(content); /* the content is the ack */
break;
case BYTE_WRITE:
content = UartRead();
*paddr++ = content;
UartWrite(cmd); /* acknowledge by returning the command value */
break;
case BYTE_READ16:
size = 16;
while (size--)
{
content = *paddr++;
UartWrite(content); /* the content is the ack */
}
break;
case BYTE_WRITE16:
size = 16;
while (size--)
{
content = UartRead();
*paddr++ = content;
}
UartWrite(cmd); /* acknowledge by returning the command value */
break;
case BYTE_FLASH:
content = UartRead();
pflash[0x5555] = 0xAA; /* set flash to command mode */
pflash[0x2AAA] = 0x55;
pflash[0x5555] = 0xA0; /* byte program command */
*paddr++ = content;
UartWrite(cmd); /* acknowledge by returning the command value */
break;
case BYTE_FLASH16:
size = 16;
while (size--)
{
content = UartRead();
pflash[0x5555] = 0xAA; /* set flash to command mode */
pflash[0x2AAA] = 0x55;
pflash[0x5555] = 0xA0; /* byte program command */
*paddr++ = content;
}
UartWrite(cmd); /* acknowledge by returning the command value */
break;
case HALFWORD_READ:
content = *(UINT16*)paddr;
paddr += 2;
UartWrite(content >> 8); /* highbyte */
UartWrite(content & 0xFF); /* lowbyte */
break;
case HALFWORD_WRITE:
content = UartRead() << 8 | UartRead();
*(UINT16*)paddr = content;
paddr += 2;
UartWrite(cmd); /* acknowledge by returning the command value */
break;
case EXECUTE:
{
tpFunc pFunc = (tpFunc)paddr;
pFunc();
UartWrite(cmd); /* acknowledge by returning the command value*/
}
break;
case VERSION:
UartWrite(1); /* return our version number */
break;
default:
{
SetLed(TRUE);
UartWrite(~cmd); /* error acknowledge */
}
} /* case */
} /* while (1) */
}