diff options
Diffstat (limited to 'bootloader/mpio_hd200_hd300.c')
-rw-r--r-- | bootloader/mpio_hd200_hd300.c | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/bootloader/mpio_hd200_hd300.c b/bootloader/mpio_hd200_hd300.c new file mode 100644 index 0000000000..7d6c60885d --- /dev/null +++ b/bootloader/mpio_hd200_hd300.c @@ -0,0 +1,502 @@ +/*************************************************************************** + * __________ __ ___. + * 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 + +/* 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; +} + +static inline bool _charger_inserted(void) +{ + return (GPIO1_READ & (1<<14)) ? false : true; +} + +static inline bool _battery_full(void) +{ + return (GPIO_READ & (1<<30)) ? true : false; +} + +/* 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); +} + +static void rb_boot(void) +{ + int rc; + + /* boost to speedup rb image loading */ + cpu_boost(true); + + 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(); +} + +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: + return; + 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: + * ON button (PLAY) + * 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; + + reset_screen(); + switch (event) + { + 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_PLAY|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); + 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); + 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) +{ +} |