summaryrefslogtreecommitdiffstats
path: root/bootloader/mpio_hd200_hd300.c
diff options
context:
space:
mode:
Diffstat (limited to 'bootloader/mpio_hd200_hd300.c')
-rw-r--r--bootloader/mpio_hd200_hd300.c502
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)
+{
+}