forked from len0rd/rockbox
		
	git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5486 a1c6a512-1295-4272-9138-f99709370657
		
			
				
	
	
		
			516 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			516 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
 | |
|  *
 | |
|  * 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) */
 | |
| }
 |