rockbox/flash/bootloader/bootloader.c

486 lines
12 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2003 by J<EFBFBD>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
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* 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 |= 0x40; // 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 = 0x0019; // 14400 Baud for monitor
if (FW_VERSION > 451) // "new" Player?
{
PBDR &= ~0x10; // set PB4 to 0 to power-up the harddisk early
PBIOR |= 0x10; // make PB4 an output
}
#elif defined PLATFORM_RECORDER
BRR1 = 0x0002; // 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 |= 0x20; // make PA5 an output (low by default)
}
#elif defined PLATFORM_FM
BRR1 = 0x0002; // 115200 Baud for monitor
PBDR |= 0x20; // set PB5 to keep power (fixes the ON-holding problem)
PBIOR |= 0x20; // 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 |= 0x20; // make PA5 an output (low by default)
}
#elif defined PLATFORM_ONDIO
BRR1 = 0x0019; // 14400 Baud for monitor
PBDR |= 0x20; // set PB5 to keep power (fixes the ON-holding problem)
PBIOR |= 0x20; // 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();
}
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;
}
// 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
{
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;
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 shure 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 |= 0x40;
else
PBDR &= ~0x40;
}
void UartInit(void)
{
PBIOR &= 0xFBFF; // input: RXD1 remote pin
PBCR1 |= 0x00A0; // set PB3+PB2 to UART
PBCR1 &= 0xFFAF; // clear bits 6, 4 -> UART
SMR1 = 0x0000; // async format 8N1, baud generator input is CPU clock
SCR1 = 0x0030; // transmit+receive enable
PBCR1 &= 0x00FF; // set bit 12...15 as GPIO
SSR1 &= 0x00BF; // 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)
}