diff options
author | Jörg Hohensohn <hohensoh@rockbox.org> | 2003-11-30 11:37:43 +0000 |
---|---|---|
committer | Jörg Hohensohn <hohensoh@rockbox.org> | 2003-11-30 11:37:43 +0000 |
commit | 6a4e4c87c24455e18bbd77565cb3e993ee350618 (patch) | |
tree | 6f2ceac4da97aa63ff8deef939bd3cc2d56d3466 /flash/bootloader/bootloader.c | |
parent | 014c4fa1f8d56850cfd504a90221c293e4a158f6 (diff) | |
download | rockbox-6a4e4c87c24455e18bbd77565cb3e993ee350618.tar.gz rockbox-6a4e4c87c24455e18bbd77565cb3e993ee350618.zip |
source code for all my flash stuff, now finally in cvs
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4083 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'flash/bootloader/bootloader.c')
-rw-r--r-- | flash/bootloader/bootloader.c | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/flash/bootloader/bootloader.c b/flash/bootloader/bootloader.c new file mode 100644 index 0000000000..7179bbef95 --- /dev/null +++ b/flash/bootloader/bootloader.c @@ -0,0 +1,480 @@ +/*************************************************************************** + * __________ __ ___. + * 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 + * + * 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? + { + // how do we switch this off? + } +#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) +} |