blob: 14f76354fb16baa183d30a4e1ce4a36392556bea [file] [log] [blame]
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 Marcin Bukat
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include "inttypes.h"
#include "string.h"
#include "cpu.h"
#include "system.h"
#include "lcd.h"
#include "kernel.h"
#include "thread.h"
#include "storage.h"
#include "usb.h"
#include "disk.h"
#include "font.h"
#include "adc.h"
#include "backlight.h"
#include "backlight-target.h"
#include "button.h"
#include "panic.h"
#include "power.h"
#include "powermgmt.h"
#include "file.h"
#include "common.h"
#include "version.h"
#include <stdarg.h>
/* Maximum allowed firmware image size. 10MB is more than enough */
#define MAX_LOADSIZE (10*1024*1024)
#define DRAM_START 0x31000000
#define BOOTMENU_TIMEOUT (10*HZ)
#define BOOTMENU_OPTIONS 3
#define EVENT_NONE 0x00
#define EVENT_ON 0x01
#define EVENT_AC 0x02
#define EVENT_USB 0x04
#define EVENT_RTC 0x08
/* From common.c */
extern int line;
static const char *bootmenu_options[] = {
"Boot rockbox",
"Boot MPIO firmware",
"Shutdown"
};
enum option_t {
rockbox,
mpio_firmware,
shutdown
};
int usb_screen(void)
{
return 0;
}
/* return true if charger is present */
static inline bool _charger_inserted(void)
{
return (GPIO1_READ & (1<<14)) ? false : true;
}
/* returns true if end of charge condition is reached */
static inline bool _battery_full(void)
{
return (GPIO_READ & (1<<30)) ? true : false;
}
#ifdef MPIO_HD300
/* returns true if startup is due to RTC alarm */
static inline bool _rtc_alarm(void)
{
if ( (GPIO1_READ & (1<<4)) && (GPIO1_READ & (1<<5)) )
return false;
return true;
}
#endif
/* Reset the cookie for the crt0 crash check */
static inline void __reset_cookie(void)
{
asm(" move.l #0,%d0");
asm(" move.l %d0,0x10017ffc");
}
static void start_rockbox(void)
{
adc_close();
asm(" move.w #0x2700,%sr");
__reset_cookie();
asm(" move.l %0,%%d0" :: "i"(DRAM_START));
asm(" movec.l %d0,%vbr");
asm(" move.l %0,%%sp" :: "m"(*(int *)DRAM_START));
asm(" move.l %0,%%a0" :: "m"(*(int *)(DRAM_START+4)));
asm(" jmp (%a0)");
}
static void start_mpio_firmware(void)
{
asm(" move.w #0x2700,%sr");
__reset_cookie();
asm(" movec.l %d0,%vbr");
asm(" move.l 0,%sp");
asm(" jmp 8");
}
static void __shutdown(void)
{
if (_charger_inserted())
/* if AC power do nothing */
return;
/* We need to gracefully spin down the disk to prevent clicks. */
if (ide_powered())
{
/* Make sure ATA has been initialized. */
storage_init();
/* And put the disk into sleep immediately. */
storage_sleepnow();
}
/* Backlight OFF */
_backlight_off();
__reset_cookie();
power_off();
}
/* Print the battery voltage (and a warning message). */
static void check_battery(void)
{
int battery_voltage, batt_int, batt_frac;
battery_voltage = battery_adc_voltage();
batt_int = battery_voltage / 1000;
batt_frac = (battery_voltage % 1000) / 10;
printf("Battery: %d.%02dV", batt_int, batt_frac);
if (battery_voltage <= 3500)
{
printf("WARNING! BATTERY LOW!!");
sleep(HZ*2);
}
}
static void lcd_putstring_centered(const char *string)
{
int w,h;
font_getstringsize(string, &w, &h, FONT_SYSFIXED);
lcd_putsxy((LCD_WIDTH-w)/2, (LCD_HEIGHT-h)/2, string);
}
/* This function initializes ATA driver, mounts partitions,
* loads rockbox image from disk to ram and finally
* jumps to entry point in ram
*/
static void rb_boot(void)
{
int rc;
/* boost to speedup rb image loading */
cpu_boost(true);
reset_screen();
printf("Rockbox boot loader");
printf("Version " RBVERSION);
rc = storage_init();
if(rc)
{
printf("ATA error: %d", rc);
sleep(HZ*5);
return;
}
disk_init();
rc = disk_mount_all();
if (rc<=0)
{
printf("No partition found");
sleep(HZ*5);
return;
}
printf("Loading firmware");
rc = load_firmware((unsigned char *)DRAM_START,
BOOTFILE, MAX_LOADSIZE);
if (rc < EOK)
{
printf("Error!");
printf("Can't load " BOOTFILE ": ");
printf("Result: %s", strerror(rc));
sleep(HZ*5);
return;
}
cpu_boost(false);
start_rockbox();
}
/* This function prints small bootmenu where
* you can choose to boot OF, rockbox or just shutdown
*/
static void bootmenu(void)
{
enum option_t i;
enum option_t option = rockbox;
int button;
const char select[] = "->";
long start_tick = current_tick;
/* backbone of menu */
/* run the loader */
printf("Rockbox boot loader");
printf("Ver: " RBVERSION);
check_battery();
printf("");
printf("=========================");
line += BOOTMENU_OPTIONS+2; /* skip lines */
printf("=========================");
printf("");
printf(" [FF] [REW] to move ");
printf(" [PLAY] to confirm ");
/* content of menu and keys handling */
while (TIME_BEFORE(current_tick,start_tick + BOOTMENU_TIMEOUT))
{
/* Draw the menu. */
line = 6; /* move below header */
for (i=0;i<BOOTMENU_OPTIONS;i++)
{
if (i != option)
printf(" %s",bootmenu_options[i]);
else
printf("%s %s",select,bootmenu_options[i]);
}
line = 15;
printf("Time left: %ds",(BOOTMENU_TIMEOUT -
(current_tick - start_tick))/HZ);
lcd_update();
button = BUTTON_NONE;
button = button_get_w_tmo(HZ);
switch (button)
{
case BUTTON_REW:
#ifdef MPIO_HD200
case BUTTON_RC_REW:
#endif
if (option > rockbox)
option--;
else
option = shutdown;
break;
case BUTTON_FF:
#ifdef MPIO_HD200
case BUTTON_RC_FF:
#endif
if (option < shutdown)
option++;
else
option = rockbox;
break;
case BUTTON_PLAY:
#ifdef MPIO_HD200
case BUTTON_RC_PLAY:
case (BUTTON_PLAY|BUTTON_REC):
#endif
reset_screen();
switch (option)
{
case rockbox:
rb_boot();
break;
case mpio_firmware:
start_mpio_firmware();
break;
default:
__shutdown();
break;
}
}
}
/* timeout */
}
void main(void)
{
/* messages */
const char usb_connect_msg[] = "Bootloader USB mode";
const char charging_msg[] = "Charging...";
const char complete_msg[] = "Charging complete";
/* helper variable for messages */
bool blink_toggle = false;
int button;
unsigned int event = EVENT_NONE;
unsigned int last_event = EVENT_NONE;
/* this is default mode after power_init() */
bool high_current_charging = true;
/* setup GPIOs related to power functions */
power_init();
system_init();
kernel_init();
/* run at 45MHz */
set_cpu_frequency(CPUFREQ_NORMAL);
/* IRQs are needed by button driver */
enable_irq();
lcd_init();
/* only lowlevel functions no queue init */
_backlight_init();
_backlight_hw_on();
/* setup font system*/
font_init();
lcd_setfont(FONT_SYSFIXED);
/* buttons reading init*/
adc_init();
button_init();
usb_init();
cpu_idle_mode(true);
/* Handle wakeup event. Possibilities are:
* RTC alarm (HD300)
* ON button (PLAY or RC_PLAY on HD200)
* USB insert
* AC charger plug
*/
while(1)
{
/* read buttons */
event = EVENT_NONE;
button = button_get_w_tmo(HZ);
if ( (button & BUTTON_PLAY)
#ifdef MPIO_HD200
|| (button & BUTTON_RC_PLAY)
#endif
)
event |= EVENT_ON;
if ( usb_detect() == USB_INSERTED )
event |= EVENT_USB;
if ( _charger_inserted() )
event |= EVENT_AC;
#ifdef MPIO_HD300
if ( _rtc_alarm() )
event |= EVENT_RTC;
#endif
reset_screen();
switch (event)
{
#ifdef MPIO_HD300
case EVENT_RTC:
case (EVENT_RTC | EVENT_ON):
/* start regardles of buttons state */
rb_boot();
break;
#endif
case EVENT_ON:
case (EVENT_ON | EVENT_AC):
/* hold is handled in button driver */
cpu_idle_mode(false);
ide_power_enable(true);
if (button & BUTTON_REC)
bootmenu();
else
rb_boot();
break;
case EVENT_AC:
/* AC plug in */
if (!(last_event & EVENT_AC))
{
/* reset charging circuit */
and_l(~(1<<23), &GPIO_ENABLE);
}
/* USB unplug */
if (last_event & EVENT_USB)
{
usb_enable(false);
sleep(HZ);
ide_power_enable(false);
sleep(HZ);
}
if(!_battery_full())
{
if (blink_toggle)
lcd_putstring_centered(charging_msg);
blink_toggle = !blink_toggle;
}
else /* end of charge condition */
{
/* put LTC1733 into shutdown mode */
or_l((1<<23), &GPIO_ENABLE);
if (high_current_charging)
{
/* switch to low current mode */
and_l(~(1<<15), &GPIO_OUT);
/* reset charging circuit */
and_l(~(1<<23), &GPIO_ENABLE);
high_current_charging = false;
}
else
{
lcd_putstring_centered(complete_msg);
}
}
check_battery();
break;
case EVENT_USB:
case (EVENT_USB | EVENT_AC):
/* AC plug in while in USB mode */
if (!(last_event & EVENT_AC))
{
/* reset charger circuit */
and_l(~(1<<23), &GPIO_ENABLE);
}
/* USB plug in */
if (!(last_event & EVENT_USB))
{
/* init USB */
ide_power_enable(true);
sleep(HZ/20);
ata_enable(false);
usb_enable(true);
}
/* display blinking USB indicator */
line = 0;
if (blink_toggle)
lcd_putstring_centered(usb_connect_msg);
check_battery();
blink_toggle = !blink_toggle;
storage_spin();
break;
default:
/* USB unplug */
if (last_event & EVENT_USB)
{
/* disable USB */
usb_enable(false);
ata_enable(true);
sleep(HZ);
ide_power_enable(false);
sleep(HZ);
}
/* spurious wakeup ?*/
__shutdown();
break;
}
lcd_update();
last_event = event;
}
}
/* These functions are present in the firmware library, but we reimplement
them here because the originals do a lot more than we want */
void screen_dump(void)
{
}