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