/*************************************************************************** * __________ __ ___. * 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? { // 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) }